No OneTemporary

File Metadata

Created
Sun, May 5, 1:33 PM
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt
index dc0f00c43c..a7b99bb7a1 100644
--- a/3rdparty/ext_qt/CMakeLists.txt
+++ b/3rdparty/ext_qt/CMakeLists.txt
@@ -1,186 +1,186 @@
SET(EXTPREFIX_qt "${EXTPREFIX}")
if (WIN32)
ExternalProject_Add(
ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.zip
URL_MD5 9d7ea0cadcec7b5a63e8e83686756978
PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/disable-wintab.diff
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qtgui-private-headers.diff
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Hack-always-return-we-support-DIBV5.patch
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-for-fullscreen-workaround.patch
INSTALL_DIR ${EXTPREFIX_qt}
CONFIGURE_COMMAND <SOURCE_DIR>/configure.bat -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++
# use this line for building Qt with debugging info enabled
#CONFIGURE_COMMAND <SOURCE_DIR>/configure.bat -release -force-debug-info -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++
BUILD_COMMAND mingw32-make
INSTALL_COMMAND mingw32-make install
UPDATE_COMMAND ""
BUILD_IN_SOURCE 1
ALWAYS 0
DEPENDS ext_patch
)
elseif (NOT APPLE)
ExternalProject_Add(
ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.tar.gz
URL_MD5 8fdec6d657bc370bd3183d8fe8e9c47a
PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qt-no-motion-compression.diff
INSTALL_DIR ${EXTPREFIX_qt}
CONFIGURE_COMMAND <SOURCE_DIR>/configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -nomake examples -no-sql-sqlite -no-openssl -no-qml-debug -no-mtdev -no-journald -no-syslog -no-nis -no-cups -no-tslib -no-directfb -no-linuxfb -no-libproxy -no-pch -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -qt-harfbuzz -qt-freetype -qt-xcb -qt-xkbcommon-x11 -optimized-qmake -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport
- BUILD_COMMAND make
- INSTALL_COMMAND make install
+ BUILD_COMMAND $(MAKE)
+ INSTALL_COMMAND $(MAKE) install
UPDATE_COMMAND ""
BUILD_IN_SOURCE 1
ALWAYS 0
)
else( APPLE )
# XCODE_VERSION is set by CMake when using the Xcode generator, otherwise we need
# to detect it manually here.
if (NOT XCODE_VERSION)
execute_process(
COMMAND xcodebuild -version
OUTPUT_VARIABLE xcodebuild_version
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_FILE /dev/null
)
string(REGEX MATCH "Xcode ([0-9]([.][0-9])+)" version_match ${xcodebuild_version})
if (version_match)
message(STATUS "${EXTPREFIX_qt}:Identified Xcode Version: ${CMAKE_MATCH_1}")
set(XCODE_VERSION ${CMAKE_MATCH_1})
else()
# If detecting Xcode version failed, set a crazy high version so we default
# to the newest.
set(XCODE_VERSION 99)
message(WARNING "${EXTPREFIX_qt}:Failed to detect the version of an installed copy of Xcode, falling back to highest supported version. Set XCODE_VERSION to override.")
endif(version_match)
endif(NOT XCODE_VERSION)
# -------------------------------------------------------------------------------
# Verify the Xcode installation on Mac OS like Qt5.7 does/will
# If not stop now, the system isn't configured correctly for Qt.
# No reason to even proceed.
# -------------------------------------------------------------------------------
set(XCSELECT_OUTPUT)
find_program(XCSELECT_PROGRAM "xcode-select")
if(XCSELECT_PROGRAM)
message(STATUS "${EXTPREFIX_qt}:Found XCSELECT_PROGRAM as ${XCSELECT_PROGRAM}")
set(XCSELECT_COMMAND ${XCSELECT_PROGRAM}
"--print-path")
execute_process(
COMMAND ${XCSELECT_COMMAND}
RESULT_VARIABLE XCSELECT_COMMAND_RESULT
OUTPUT_VARIABLE XCSELECT_COMMAND_OUTPUT
ERROR_FILE /dev/null
)
if(NOT XCSELECT_COMMAND_RESULT)
# returned 0, we're ok.
string(REGEX REPLACE
"[ \t]*[\r\n]+[ \t]*" ";"
XCSELECT_COMMAND_OUTPUT ${XCSELECT_COMMAND_OUTPUT})
else()
string(REPLACE ";" " " XCSELECT_COMMAND_STR "${XCSELECT_COMMAND}")
# message(STATUS "${XCSELECT_COMMAND_STR}")
message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} test failed with status ${XCSELECT_COMMAND_RESULT}")
endif()
else()
message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} not found. No Xcode is selected. Use xcode-select -switch to choose an Xcode version")
endif()
# Belts and suspenders
# Beyond all the Xcode and Qt version checking, the proof of the pudding
# lies in the success/failure of this command: xcrun --find xcrun.
# On failure a patch is necessary, otherwise we're ok
# So hard check xcrun now...
set(XCRUN_OUTPUT)
find_program(XCRUN_PROGRAM "xcrun")
if(XCRUN_PROGRAM)
message(STATUS "${EXTPREFIX_qt}:Found XCRUN_PROGRAM as ${XCRUN_PROGRAM}")
set(XCRUN_COMMAND ${XCRUN_PROGRAM}
"--find xcrun")
execute_process(
COMMAND ${XCRUN_COMMAND}
RESULT_VARIABLE XCRUN_COMMAND_RESULT
OUTPUT_VARIABLE XCRUN_COMMAND_OUTPUT
ERROR_FILE /dev/null
)
if(NOT XCRUN_COMMAND_RESULT)
# returned 0, we're ok.
string(REGEX REPLACE
"[ \t]*[\r\n]+[ \t]*" ";"
XCRUN_COMMAND_OUTPUT ${XCRUN_COMMAND_OUTPUT})
else()
string(REPLACE ";" " " XCRUN_COMMAND_STR "${XCRUN_COMMAND}")
# message(STATUS "${XCRUN_COMMAND_STR}")
message(STATUS "${EXTPREFIX_qt}:xcrun test failed with status ${XCRUN_COMMAND_RESULT}")
endif()
else()
message(STATUS "${EXTPREFIX_qt}:xcrun not found -- ${XCRUN_PROGRAM}")
endif()
#
# Now configure ext_qt accordingly
#
if ((XCRUN_COMMAND_RESULT) AND (NOT (XCODE_VERSION VERSION_LESS 8.0.0)))
# Fix Xcode xcrun related issue.
# NOTE: This should be fixed by Qt 5.7.1 see here: http://code.qt.io/cgit/qt/qtbase.git/commit/?h=dev&id=77a71c32c9d19b87f79b208929e71282e8d8b5d9
# NOTE: but no one's holding their breath.
set(ext_qt_PATCH_COMMAND $${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-166202.diff
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff
COMMAND ${PATCH_COMMAND} -p1 -b -d <SOURCE_DIR>/qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/qtbase-configure.patch
COMMAND ${PATCH_COMMAND} -p1 -b -d <SOURCE_DIR>/qtbase/mkspecs/features/mac -i ${CMAKE_CURRENT_SOURCE_DIR}/mac-default.patch)
message(STATUS "${EXTPREFIX_qt}:Additional patches injected.")
else()
# No extra patches will be applied
# NOTE: defaults for some untested scenarios like xcrun fails and xcode_version < 8.
# NOTE: that is uncharted territory and (hopefully) a very unlikely scenario...
set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-166202.diff
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff)
endif()
# Qt is big - try and parallelize if at all possible
include(ProcessorCount)
ProcessorCount(NUM_CORES)
if(NOT NUM_CORES EQUAL 0)
if (NUM_CORES GREATER 2)
# be nice...
MATH( EXPR NUM_CORES "${NUM_CORES} - 2" )
endif()
set(PARALLEL_MAKE "make;-j${NUM_CORES}")
message(STATUS "${EXTPREFIX_qt}:Parallelized make: ${PARALLEL_MAKE}")
else()
set(PARALLEL_MAKE "make")
endif()
ExternalProject_Add(ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
LOG_DOWNLOAD ON
LOG_UPDATE ON
LOG_CONFIGURE ON
LOG_BUILD ON
LOG_TEST ON
LOG_INSTALL ON
BUILD_IN_SOURCE ON
URL https://download.qt.io/official_releases/qt/5.7/5.7.0/single/qt-everywhere-opensource-src-5.7.0.tar.gz
URL_MD5 9a46cce61fc64c20c3ac0a0e0fa41b42
PATCH_COMMAND ${ext_qt_PATCH_COMMAND}
INSTALL_DIR ${EXTPREFIX_qt}
CONFIGURE_COMMAND <SOURCE_DIR>/configure -confirm-license -opensource -nomake examples -no-openssl -no-compile-examples -qt-freetype -qt-harfbuzz -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -prefix ${EXTPREFIX_qt}
BUILD_COMMAND ${PARALLEL_MAKE}
INSTALL_COMMAND make install
UPDATE_COMMAND ""
BUILD_IN_SOURCE 1
ALWAYS 0
)
endif()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3a26d22aa2..c05e003aa4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,654 +1,655 @@
project(krita)
message(STATUS "Using CMake version: ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
set(MIN_QT_VERSION 5.6.0)
option(OVERRIDE_QT_VERSION "Use this to make it possible to build with Qt < 5.6.0. There will be bugs." OFF)
if (OVERRIDE_QT_VERSION)
set(MIN_QT_VERSION 5.4.0)
endif()
set(MIN_FRAMEWORKS_VERSION 5.7.0)
if (POLICY CMP0002)
cmake_policy(SET CMP0002 OLD)
endif()
if (POLICY CMP0017)
cmake_policy(SET CMP0017 NEW)
endif ()
if (POLICY CMP0022)
cmake_policy(SET CMP0022 OLD)
endif ()
if (POLICY CMP0026)
cmake_policy(SET CMP0026 OLD)
endif()
if (POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
if (POLICY CMP0046)
cmake_policy(SET CMP0046 OLD)
endif ()
if (POLICY CMP0059)
cmake_policy(SET CMP0059 OLD)
endif()
if (POLICY CMP0063)
cmake_policy(SET CMP0063 OLD)
endif()
if (POLICY CMP0054)
cmake_policy(SET CMP0054 OLD)
endif()
if (POLICY CMP0064)
cmake_policy(SET CMP0064 OLD)
endif()
if (APPLE)
set(APPLE_SUPPRESS_X11_WARNING TRUE)
set(KDE_SKIP_RPATH_SETTINGS TRUE)
set(CMAKE_MACOSX_RPATH 1)
set(BUILD_WITH_INSTALL_RPATH 1)
- add_definitions(-mmacosx-version-min=10.9 -Wno-deprecated-register)
+ add_definitions(-mmacosx-version-min=10.9 -Wno-macro-redefined -Wno-deprecated-register)
endif()
######################
#######################
## Constants defines ##
#######################
######################
# define common versions of Krita applications, used to generate kritaversion.h
# update these version for every release:
set(KRITA_VERSION_STRING "3.0.92")
set(KRITA_STABLE_VERSION_MAJOR 3) # 3 for 3.x, 4 for 4.x, etc.
set(KRITA_STABLE_VERSION_MINOR 0) # 0 for 3.0, 1 for 3.1, etc.
set(KRITA_VERSION_RELEASE 91) # 89 for Alpha, increase for next test releases, set 0 for first Stable, etc.
#set(KRITA_ALPHA 1) # uncomment only for Alpha
set(KRITA_BETA 1) # uncomment only for Beta
#set(KRITA_RC 1) # uncomment only for RC
set(KRITA_YEAR 2016) # update every year
if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC)
set(KRITA_STABLE 1) # do not edit
endif()
message(STATUS "Krita version: ${KRITA_VERSION_STRING}")
# Define the generic version of the Krita libraries here
# This makes it easy to advance it when the next Krita release comes.
# 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series
# (2.x) so we're starting with 15 in 3.x series.
if(KRITA_STABLE_VERSION_MAJOR EQUAL 3)
math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_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_KRITA_LIB_VERSION_MAJOR to something bigger")
endif()
set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0")
set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}")
LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro")
# fetch git revision for the current build
set(KRITA_GIT_SHA1_STRING "")
set(KRITA_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(KRITA_GIT_SHA1_STRING ${GIT_SHA1})
set(KRITA_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}")
# create test make targets
enable_testing()
# collect list of broken tests, empty here to start fresh with each cmake run
set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS")
############
#############
## Options ##
#############
############
include(FeatureSummary)
option(PACKAGERS_BUILD "Build support of multiple CPU architectures in one binary. Should be used by packagers only or Krita developers. Only switch off when you're an artist optimizing a build for your very own machine." ON)
add_feature_info("Packagers' Build" PACKAGERS_BUILD "Support several CPU arch in one binary. Recommended for packages. Switch this off to make a build for only your machine.")
if (WIN32)
option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON)
add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler")
if (MINGW)
option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON)
add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags")
if (USE_MINGW_HARDENING_LINKER)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
# Enable high-entropy ASLR for 64-bit
# The image base has to be >4GB for HEASLR to be enabled.
# The values used here are kind of arbitrary.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000")
endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
else (USE_MINGW_HARDENING_LINKER)
message(WARNING "Linker Security Flags not enabled!")
endif (USE_MINGW_HARDENING_LINKER)
endif (MINGW)
endif ()
option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON)
configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h)
add_feature_info("Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.")
option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF)
add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.")
option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF)
add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).")
include(MacroJPEG)
########################
#########################
## 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(ECMAddAppIcon)
include(ECMSetupVersion)
include(ECMMarkNonGuiExecutable)
include(ECMGenerateHeaders)
include(GenerateExportHeader)
include(ECMMarkAsTest)
include(ECMInstallIcons)
include(CMakePackageConfigHelpers)
include(WriteBasicConfigVersionFile)
include(CheckFunctionExists)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings)
# do not reorder to be alphabetical: this is the order in which the frameworks
# depend on each other.
find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS
Archive
Config
WidgetsAddons
Completion
CoreAddons
GuiAddons
I18n
ItemModels
ItemViews
WindowSystem
)
# KConfig deprecated authorizeKAction. In order to be warning free,
# compile with the updated function when the dependency is new enough.
# Remove this (and the uses of the define) when the minimum KF5
# version is >= 5.24.0.
if (${KF5Config_VERSION} VERSION_LESS "5.24.0" )
message("Old KConfig (< 5.24.0) found.")
add_definitions(-DKCONFIG_BEFORE_5_24)
endif()
find_package(Qt5 ${MIN_QT_VERSION}
REQUIRED COMPONENTS
Core
Gui
Widgets
Xml
Network
PrintSupport
Svg
Test
Concurrent
)
include (MacroAddFileDependencies)
include (MacroBoolTo01)
include (MacroEnsureOutOfSourceBuild)
macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions")
# Note: OPTIONAL_COMPONENTS does not seem to be reliable
# (as of ECM 5.15.0, CMake 3.2)
if (NOT WIN32 AND NOT APPLE)
find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras)
find_package(Qt5DBus ${MIN_QT_VERSION} QUIET)
set(HAVE_DBUS ${Qt5DBus_FOUND})
set_package_properties(Qt5DBus PROPERTIES
DESCRIPTION "Qt DBUS integration"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used to provide a dbus api on Linux")
find_package(KF5KIO ${MIN_FRAMEWORKS_VERSION} QUIET)
macro_bool_to_01(KF5KIO_FOUND HAVE_KIO)
set_package_properties(KF5KIO PROPERTIES
DESCRIPTION "KDE's KIO Framework"
URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kio/html/index.html"
TYPE OPTIONAL
PURPOSE "Optionally used for recent document handling")
find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION} QUIET)
macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH)
set_package_properties(KF5Crash PROPERTIES
DESCRIPTION "KDE's Crash Handler"
URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html"
TYPE OPTIONAL
PURPOSE "Optionally used to provide crash reporting on Linux")
find_package(X11 REQUIRED COMPONENTS Xinput)
set(HAVE_X11 TRUE)
add_definitions(-DHAVE_X11)
find_package(XCB COMPONENTS XCB ATOM)
set(HAVE_XCB ${XCB_FOUND})
else()
set(HAVE_DBUS FALSE)
set(HAVE_X11 FALSE)
set(HAVE_XCB FALSE)
endif()
add_definitions(
-DQT_USE_QSTRINGBUILDER
-DQT_STRICT_ITERATORS
-DQT_NO_SIGNALS_SLOTS_KEYWORDS
-DQT_USE_FAST_OPERATOR_PLUS
-DQT_USE_FAST_CONCATENATION
-DQT_NO_URL_CAST_FROM_STRING
-DQT_DISABLE_DEPRECATED_BEFORE=0
)
add_definitions(-DTRANSLATION_DOMAIN=\"krita\")
#
# The reason for this mode is that the Debug mode disable inlining
#
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS_KRITADEVS "-O3 -g" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals")
endif()
if(UNIX)
set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m")
endif()
if(WIN32)
if(MSVC)
# C4522: 'class' : multiple assignment operators specified
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522")
endif()
endif()
# enable exceptions globally
kde_enable_exceptions()
# 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()
set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/)
macro(macro_add_unittest_definitions)
add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/")
add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}")
add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}")
+ add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/")
endmacro()
# 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()
# set custom krita plugin installdir
set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins)
###########################
############################
## Required dependencies ##
############################
###########################
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(SYSTEM ${PNG_INCLUDE_DIR})
endif()
add_definitions(-DBOOST_ALL_NO_LIB)
find_package(Boost REQUIRED COMPONENTS system) # for pigment and stage
include_directories(${Boost_INCLUDE_DIRS})
##
## Test for GNU Scientific Library
##
find_package(GSL)
set_package_properties(GSL PROPERTIES
URL "http://www.gnu.org/software/gsl"
TYPE RECOMMENDED
PURPOSE "Required by Krita's Transform tool.")
macro_bool_to_01(GSL_FOUND HAVE_GSL)
configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h )
###########################
############################
## Optional dependencies ##
############################
###########################
##
## Check for OpenEXR
##
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES
DESCRIPTION "Compression library"
URL "http://www.zlib.net/"
TYPE OPTIONAL
PURPOSE "Optionally used by the G'Mic and the PSD plugins")
macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB)
find_package(OpenEXR)
set_package_properties(OpenEXR PROPERTIES
DESCRIPTION "High dynamic-range (HDR) image file format"
URL "http://www.openexr.com"
TYPE OPTIONAL
PURPOSE "Required by the Krita OpenEXR filter")
macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR)
set(LINK_OPENEXR_LIB)
if(OPENEXR_FOUND)
include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR})
set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES})
add_definitions(${OPENEXR_DEFINITIONS})
endif()
find_package(TIFF)
set_package_properties(TIFF PROPERTIES
DESCRIPTION "TIFF Library and Utilities"
URL "http://www.remotesensing.org/libtiff"
TYPE OPTIONAL
PURPOSE "Required by the Krita TIFF filter")
find_package(JPEG)
set_package_properties(JPEG PROPERTIES
DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported."
URL "http://www.libjpeg-turbo.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita JPEG filter")
set(LIBRAW_MIN_VERSION "0.16")
find_package(LibRaw ${LIBRAW_MIN_VERSION})
set_package_properties(LibRaw PROPERTIES
DESCRIPTION "Library to decode RAW images"
URL "http://www.libraw.org"
TYPE OPTIONAL
PURPOSE "Required to build the raw import plugin")
find_package(FFTW3)
set_package_properties(FFTW3 PROPERTIES
DESCRIPTION "A fast, free C FFT library"
URL "http://www.fftw.org/"
TYPE OPTIONAL
PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features")
macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3)
find_package(OCIO)
set_package_properties(OCIO PROPERTIES
DESCRIPTION "The OpenColorIO Library"
URL "http://www.opencolorio.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita LUT docker")
macro_bool_to_01(OCIO_FOUND HAVE_OCIO)
##
## Look for OpenGL
##
# TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes)
if(Qt5Gui_OPENGL_IMPLEMENTATION)
message(STATUS "Found QtGui OpenGL support")
else()
message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.")
endif()
##
## Test for eigen3
##
find_package(Eigen3 REQUIRED "3.0")
set_package_properties(Eigen3 PROPERTIES
DESCRIPTION "C++ template library for linear algebra"
URL "http://eigen.tuxfamily.org"
TYPE REQUIRED)
##
## Test for exiv2
##
set(EXIV2_MIN_VERSION "0.16")
find_package(Exiv2 REQUIRED)
set_package_properties(Exiv2 PROPERTIES
DESCRIPTION "Image metadata library and tools"
URL "http://www.exiv2.org"
PURPOSE "Required by Krita")
##
## Test for lcms
##
find_package(LCMS2 REQUIRED "2.4")
set_package_properties(LCMS2 PROPERTIES
DESCRIPTION "LittleCMS Color management engine"
URL "http://www.littlecms.com"
TYPE REQUIRED
PURPOSE "Will be used for color management and is necessary 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 )
set(HAVE_VC FALSE)
if( NOT MSVC)
find_package(Vc 1.1.0)
set_package_properties(Vc PROPERTIES
DESCRIPTION "Portable, zero-overhead SIMD library for C++"
URL "https://github.com/VcDevel/Vc"
TYPE OPTIONAL
PURPOSE "Required by the Krita for vectorization")
macro_bool_to_01(Vc_FOUND HAVE_VC)
macro_bool_to_01(PACKAGERS_BUILD DO_PACKAGERS_BUILD)
endif()
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)
if(Vc_COMPILER_IS_CLANG)
set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast -fPIC")
elseif (NOT MSVC)
set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast -fPIC")
endif()
#Handle Vc master
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} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2)
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} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2)
else()
set(${_objs} ${_src})
endif()
endmacro()
if (NOT PACKAGERS_BUILD)
# Optimize everything for the current architecture
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}")
endif ()
endif()
set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} )
add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS})
if(WIN32)
set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS}
ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} )
endif()
##
## Test endianess
##
include (TestBigEndian)
test_big_endian(CMAKE_WORDS_BIGENDIAN)
##
## Test for qt-poppler
##
find_package(Poppler)
set_package_properties(Poppler PROPERTIES
DESCRIPTION "A PDF rendering library"
URL "http://poppler.freedesktop.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita PDF filter.")
##
## Test for pthreads (for G'Mic)
##
find_package(Threads)
set_package_properties(Threads PROPERTIES
DESCRIPTION "PThreads - A low-level threading library"
TYPE OPTIONAL
PURPOSE "Optionally used by the G'Mic plugin")
##
## Test for OpenMP (for G'Mic)
##
find_package(OpenMP)
set_package_properties(OpenMP PROPERTIES
DESCRIPTION "A low-level parallel execution library"
URL "http://openmp.org/wp/"
TYPE OPTIONAL
PURPOSE "Optionally used by the G'Mic plugin")
##
## Test for Curl (for G'Mic)
##
find_package(CURL)
set_package_properties(CURL PROPERTIES
DESCRIPTION "A tool to fetch remote data"
URL "http://curl.haxx.se/"
TYPE OPTIONAL
PURPOSE "Optionally used by the G'Mic plugin")
############################
#############################
## Add Krita helper macros ##
#############################
############################
include(MacroKritaAddBenchmark)
####################
#####################
## Define includes ##
#####################
####################
# 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
)
add_subdirectory(libs)
add_subdirectory(plugins)
add_subdirectory(benchmarks)
add_subdirectory(krita)
configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h )
configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h)
configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h )
check_function_exists(powf HAVE_POWF)
configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h)
message("\nBroken tests:")
foreach(tst ${KRITA_BROKEN_TESTS})
message(" * ${tst}")
endforeach()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/benchmarks/kis_projection_benchmark.cpp b/benchmarks/kis_projection_benchmark.cpp
index c7fff291d2..3a1ff0b043 100644
--- a/benchmarks/kis_projection_benchmark.cpp
+++ b/benchmarks/kis_projection_benchmark.cpp
@@ -1,62 +1,62 @@
/*
* 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 <QTest>
#include "kis_projection_benchmark.h"
#include "kis_benchmark_values.h"
#include <KoColor.h>
#include <kis_group_layer.h>
#include <kis_paint_device.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <KisPart.h>
void KisProjectionBenchmark::initTestCase()
{
}
void KisProjectionBenchmark::cleanupTestCase()
{
}
void KisProjectionBenchmark::benchmarkProjection()
{
QBENCHMARK{
KisDocument *doc = KisPart::instance()->createDocument();
doc->loadNativeFormat(QString(FILES_DATA_DIR) + QDir::separator() + "load_test.kra");
doc->image()->refreshGraph();
- doc->saveNativeFormat(QString(FILES_OUTPUT_DIR) + QDir::separator() + "save_test.kra");
+ doc->exportDocument(QUrl::fromLocalFile(QString(FILES_OUTPUT_DIR) + QDir::separator() + "save_test.kra"));
delete doc;
}
}
void KisProjectionBenchmark::benchmarkLoading()
{
QBENCHMARK{
KisDocument *doc2 = KisPart::instance()->createDocument();
doc2->loadNativeFormat(QString(FILES_DATA_DIR) + QDir::separator() + "load_test.kra");
delete doc2;
}
}
QTEST_MAIN(KisProjectionBenchmark)
diff --git a/benchmarks/kis_thumbnail_benchmark.cpp b/benchmarks/kis_thumbnail_benchmark.cpp
index 99a080584f..b692a67fa4 100644
--- a/benchmarks/kis_thumbnail_benchmark.cpp
+++ b/benchmarks/kis_thumbnail_benchmark.cpp
@@ -1,161 +1,161 @@
/*
* Copyright (c) 2016 Eugene Ingerman geneing at gmail dot 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_thumbnail_benchmark.h"
#include "kis_benchmark_values.h"
#include <QTest>
#include <QImage>
#include "kis_iterator_ng.h"
#include "kis_paint_device.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"
#include "kis_sequential_iterator.h"
#include "kis_transform_worker.h"
#include <cmath>
#define SAVE_OUTPUT
const int THUMBNAIL_WIDTH = 64;
const int THUMBNAIL_HEIGHT = 64;
const int IMAGE_WIDTH = 8000;
const int IMAGE_HEIGHT = 6000;
const int OVERSAMPLE = 4;
void KisThumbnailBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_dev = new KisPaintDevice(m_colorSpace);
KoColor color(m_colorSpace);
color.fromQColor(Qt::white);
m_dev->clear();
m_dev->fill(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, color.data());
color.fromQColor(Qt::black);
KisPainter painter(m_dev);
painter.setPaintColor(color);
float radius = std::min(IMAGE_WIDTH, IMAGE_HEIGHT);
const float angle = 2 * 3.1415926 / 360.;
const float endWidth = 30;
- for (float i = 0; i < 90; i += 5) {
+ for (int i = 0; i < 90; i += 5) {
painter.drawThickLine(QPointF(0, 0), QPointF(radius * std::sin(angle * i), radius * std::cos(angle * i)), 1, endWidth);
painter.drawThickLine(QPointF(IMAGE_WIDTH, IMAGE_HEIGHT),
QPointF(IMAGE_WIDTH - radius * std::sin(angle * i), IMAGE_HEIGHT - radius * std::cos(angle * i)), 1, endWidth);
}
#ifdef SAVE_OUTPUT
m_dev->convertToQImage(m_colorSpace->profile()).save("ThumbFullImage.png");
#endif
}
void KisThumbnailBenchmark::cleanupTestCase()
{
}
void KisThumbnailBenchmark::benchmarkCreateThumbnail()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect() );
//m_dev->setDirty();
}
image.save("createThumbnail.png");
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailCached()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, 2. );
}
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQ()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(OVERSAMPLE * THUMBNAIL_WIDTH, OVERSAMPLE * THUMBNAIL_HEIGHT);
image = image.scaled(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_dev->setDirty();
}
image.save("createThumbnailHiQ.png");
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample2x()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 2,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
m_dev->setDirty();
}
image.save("createThumbnailHiQcreateThumbOversample2x.png");
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample3x()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 3,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
m_dev->setDirty();
}
image.save("createThumbnailHiQcreateThumbOversample3x.png");
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample4x()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 4,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
m_dev->setDirty();
}
image.save("createThumbnailHiQcreateThumbOversample4x.png");
}
QTEST_MAIN(KisThumbnailBenchmark)
diff --git a/cmake/modules/MacroKritaAddBenchmark.cmake b/cmake/modules/MacroKritaAddBenchmark.cmake
index 84669a0f43..c0da4b652b 100644
--- a/cmake/modules/MacroKritaAddBenchmark.cmake
+++ b/cmake/modules/MacroKritaAddBenchmark.cmake
@@ -1,83 +1,83 @@
# Copyright (c) 2010, Cyrille Berger, <cberger@cberger.net>
# Copyright (c) 2006-2009 Alexander Neundorf, <neundorf@kde.org>
# Copyright (c) 2006, 2007, Laurent Montel, <montel@kde.org>
# Copyright (c) 2007 Matthias Kretz <kretz@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
add_custom_target(benchmark)
macro (KRITA_ADD_BENCHMARK _test_NAME)
set(_srcList ${ARGN})
set(_targetName ${_test_NAME})
if( ${ARGV1} STREQUAL "TESTNAME" )
set(_targetName ${ARGV2})
list(REMOVE_AT _srcList 0 1)
endif()
set(_nogui)
list(GET ${_srcList} 0 first_PARAM)
if( ${first_PARAM} STREQUAL "NOGUI" )
set(_nogui "NOGUI")
endif()
add_executable( ${_test_NAME} ${_srcList} )
ecm_mark_as_test(${_test_NAME})
if(NOT KDE4_TEST_OUTPUT)
set(KDE4_TEST_OUTPUT plaintext)
endif()
set(KDE4_TEST_OUTPUT ${KDE4_TEST_OUTPUT} CACHE STRING "The output to generate when running the QTest unit tests")
set(using_qtest "")
foreach(_filename ${_srcList})
if(NOT using_qtest)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_filename}")
file(READ ${_filename} file_CONTENT)
string(REGEX MATCH "QTEST_(KDE)?MAIN" using_qtest "${file_CONTENT}")
endif()
endif()
endforeach(_filename)
get_target_property( loc ${_test_NAME} LOCATION )
if(WIN32)
if(MSVC_IDE)
string(REGEX REPLACE "\\$\\(.*\\)" "\${CTEST_CONFIGURATION_TYPE}" loc "${loc}")
endif()
# .bat because of rpath handling
set(_executable "${loc}.bat")
else()
- if (Q_WS_MAC AND NOT _nogui)
+ if (Q_OS_OSX AND NOT _nogui)
set(_executable ${EXECUTABLE_OUTPUT_PATH}/${_test_NAME}.app/Contents/MacOS/${_test_NAME})
else ()
# .shell because of rpath handling
set(_executable "${loc}.shell")
endif ()
endif()
if (using_qtest AND KDE4_TEST_OUTPUT STREQUAL "xml")
#MESSAGE(STATUS "${_targetName} : Using QTestLib, can produce XML report.")
add_custom_target( ${_targetName} COMMAND ${_executable} -xml -o ${_targetName}.tml )
else ()
#MESSAGE(STATUS "${_targetName} : NOT using QTestLib, can't produce XML report, please use QTestLib to write your unit tests.")
add_custom_target( ${_targetName} COMMAND ${_executable} )
endif ()
add_dependencies(benchmark ${_targetName} )
# add_test( ${_targetName} ${EXECUTABLE_OUTPUT_PATH}/${_test_NAME} -xml -o ${_test_NAME}.tml )
if (NOT MSVC_IDE) #not needed for the ide
# if the tests are EXCLUDE_FROM_ALL, add a target "buildtests" to build all tests
if (NOT BUILD_TESTING)
get_directory_property(_buildtestsAdded BUILDTESTS_ADDED)
if(NOT _buildtestsAdded)
add_custom_target(buildtests)
set_directory_properties(PROPERTIES BUILDTESTS_ADDED TRUE)
endif()
add_dependencies(buildtests ${_test_NAME})
endif ()
endif ()
endmacro (KRITA_ADD_BENCHMARK)
diff --git a/krita/data/splash/splash_screen.xpm b/krita/data/splash/splash_screen.xpm
index 330c24a5ba..33217cd910 100644
--- a/krita/data/splash/splash_screen.xpm
+++ b/krita/data/splash/splash_screen.xpm
@@ -1,15913 +1,18345 @@
/* XPM */
static const char *splash_screen_xpm[]={
-"440 286 15624 3",
-".pE c #000000",
-".q0 c #020305",
-".qk c #030608",
-".5R c #05141c",
-".Au c #060d13",
-".A3 c #060f13",
-".Cm c #061015",
-".9v c #06121a",
-".yZ c #070b0e",
-".zp c #070c0e",
-"aBD c #070c10",
-".CT c #071116",
-".Ev c #071118",
-".tT c #077b7b",
-"aCc c #080e14",
-".BR c #081317",
-".D0 c #081319",
-".Gr c #08141b",
-".It c #08151d",
-".Sn c #08171e",
-".ql c #090807",
-".yU c #090e10",
-".yY c #090e11",
-".CR c #091217",
-".Fm c #09161c",
-".Rt c #09171f",
-".Tg c #09181f",
-".5W c #091820",
-"aDC c #091d26",
-".ur c #09a0a0",
-".oU c #0a0806",
-".q1 c #0a0d11",
-".yV c #0a0e10",
-".yX c #0a0e11",
-".zW c #0a0f11",
-".A1 c #0a1217",
-".Al c #0a1317",
-".Am c #0a1318",
-".As c #0a1418",
-".Ex c #0a151b",
-".Ds c #0a161b",
-".Gm c #0a161d",
-".PD c #0a1820",
-".5S c #0a1821",
-"ahy c #0a1823",
-"a#a c #0a1922",
-".yR c #0b0f11",
-".yW c #0b0f12",
-".yN c #0b1013",
-"aAZ c #0b1014",
-".Ak c #0b1418",
-".Ch c #0b141a",
-".Ck c #0b151a",
-".Ci c #0b151b",
-".Cj c #0b161b",
-".Ey c #0b161c",
-".Fn c #0b171e",
-".E3 c #0b181f",
-".Hg c #0b1820",
-".M# c #0b1921",
-".5X c #0b1922",
-"#5A c #0b1923",
-"anY c #0b1924",
-".Kn c #0b1a21",
-".N4 c #0b1a22",
-"#75 c #0b1a24",
-".TY c #0b1d25",
-"aDB c #0b1d26",
-"aGC c #0b1d27",
-"aDD c #0b1e27",
-".pF c #0c0c0b",
-".yQ c #0c1012",
-".yP c #0c1013",
-".yO c #0c1114",
-".A2 c #0c1318",
-"aIZ c #0c1319",
-".At c #0c1418",
-".An c #0c1419",
-".BQ c #0c151a",
-".CS c #0c161a",
-".Cl c #0c161b",
-".Dm c #0c161c",
-".DU c #0c171d",
-".Fd c #0c181e",
-".UB c #0c1920",
-".RL c #0c1921",
-".3I c #0c1a21",
-".PE c #0c1a22",
-"#YE c #0c1a23",
-"#0y c #0c1a24",
-"a.K c #0c1b24",
-"#3B c #0c1c25",
-".SZ c #0c1e26",
-"aDF c #0c1e27",
-"aHZ c #0c1e28",
-"bmi c #0c1f28",
-"#32 c #0c355a",
-".yS c #0d1113",
-".zS c #0d1114",
-"aEe c #0d141b",
-".BH c #0d1519",
-".Ar c #0d151a",
-".Ao c #0d151b",
-".BJ c #0d161a",
-".Ag c #0d161b",
-".BA c #0d161c",
-".Ab c #0d171c",
-".DB c #0d181d",
-".DV c #0d181e",
-".Gq c #0d181f",
-".FH c #0d1920",
-".FJ c #0d1a21",
-".Hf c #0d1a22",
-".G5 c #0d1b22",
-".5T c #0d1b23",
-"#9i c #0d1b24",
-"ap4 c #0d1b25",
-"#74 c #0d1b26",
-".Vs c #0d1c23",
-"#3e c #0d1c25",
-"aeX c #0d1c26",
-".I5 c #0d1d23",
-"#lx c #0d1d26",
-"#ng c #0d1d27",
-".OG c #0d1e26",
-"acE c #0d1e27",
-"#9T c #0d1e28",
-"aH0 c #0d1f28",
-"atS c #0d2028",
-"awb c #0d2029",
-"aEm c #0d202c",
-"#31 c #0d365b",
-"aDs c #0e1113",
-".zT c #0e1214",
-".yT c #0e1215",
-".BI c #0e151a",
-"aCP c #0e151b",
-".Aq c #0e161b",
-".BC c #0e161c",
-".BP c #0e171c",
-".Dq c #0e181c",
-".Dp c #0e181d",
-".Ca c #0e181e",
-".Ew c #0e191e",
-".E9 c #0e191f",
-".FP c #0e1920",
-".DL c #0e1a20",
-".FI c #0e1a21",
-".HN c #0e1a22",
-"a7j c #0e1a23",
-".Fy c #0e1b21",
-".He c #0e1b22",
-".SF c #0e1b23",
-".Is c #0e1c23",
-".7i c #0e1c24",
-".5V c #0e1c25",
-"#9s c #0e1c26",
-".Jf c #0e1d24",
-"a9c c #0e1d25",
-"a2N c #0e1d26",
-"a0V c #0e1d27",
-"aSc c #0e1e27",
-".UY c #0e1f27",
-"aaT c #0e1f28",
-"#pN c #0e1f29",
-"#oY c #0e2028",
-"#Ar c #0e2029",
-"aDf c #0e2129",
-"#ni c #0e212a",
-"aZD c #0e222a",
-"aK5 c #0e232b",
-"#7r c #0e375a",
-"#35 c #0e375e",
-"#47 c #0e385c",
-"#33 c #0e3961",
-".zX c #0f1114",
-".zV c #0f1215",
-".zU c #0f1315",
-".Ap c #0f161c",
-".Af c #0f171b",
-".Ae c #0f171c",
-".BB c #0f171d",
-".Ad c #0f181d",
-".Ac c #0f181e",
-".B0 c #0f191e",
-"#1k c #0f1a1f",
-".C# c #0f1a20",
-".FN c #0f1a21",
-".DM c #0f1b21",
-".GX c #0f1b22",
-"auA c #0f1b24",
-".Io c #0f1c22",
-".HF c #0f1c23",
-".7j c #0f1c24",
-".Kk c #0f1d23",
-".N1 c #0f1d25",
-"#UQ c #0f1d26",
-"#4r c #0f1d27",
-".JI c #0f1e25",
-".2J c #0f1e27",
-"#i0 c #0f1e28",
-"#o2 c #0f1e29",
-".6a c #0f1f27",
-"a#k c #0f1f28",
-".Oy c #0f1f29",
-".7F c #0f2028",
-"#As c #0f2029",
-".8Z c #0f2129",
-"#oc c #0f212a",
-"#wO c #0f222a",
-"aHK c #0f232b",
-"aLM c #0f3155",
-"bwJ c #0f375b",
-"aIF c #0f385c",
-"bT6 c #0f395d",
-".uW c #0f4e4e",
-".yM c #101418",
-".BD c #10181d",
-".Ce c #10181e",
-".Cd c #10191e",
-".Dn c #10191f",
-".DW c #101a20",
-".DT c #101a21",
-".F. c #101b1f",
-".F# c #101b21",
-".FO c #101b22",
-".Go c #101c21",
-".Gn c #101c22",
-".Gp c #101c23",
-".HM c #101d23",
-".GT c #101d24",
-".1j c #101d25",
-"au6 c #101d26",
-"aWV c #101d27",
-".JJ c #101e25",
-".QK c #101e26",
-".5U c #101e27",
-".3Y c #101e28",
-".rI c #101e2f",
-".K2 c #101f26",
-".MR c #101f27",
-"#kE c #101f28",
-".MG c #101f29",
-"aUQ c #102027",
-"#8r c #102028",
-".P0 c #102029",
-"#re c #10202a",
-".R7 c #102129",
-".6k c #10212a",
-"aZ6 c #102229",
-"#.x c #10222a",
-"a2c c #10222b",
-".Zb c #10232a",
-"aJ4 c #10232b",
-"bpO c #10232c",
-"aFg c #10242b",
-"aTv c #10242c",
-"aSB c #10252c",
-"bnH c #10252e",
-"aNn c #102c3e",
-"bRi c #103154",
-"bSD c #103155",
-"aNW c #103255",
-"bIs c #103256",
-"bHm c #103358",
-"bfk c #10355a",
-"#7q c #10375b",
-"#6j c #10385c",
-"#Zm c #10395d",
-".vw c #105757",
-".zA c #111214",
-".BK c #11181d",
-".Aj c #11181e",
-".BG c #11181f",
-".B1 c #11191f",
-".Cc c #111a1f",
-".Cb c #111a20",
-".Fa c #111b20",
-".DP c #111b21",
-".DX c #111b22",
-".DZ c #111c22",
-".FM c #111c23",
-".G4 c #111d23",
-".Fo c #111d24",
-".Ly c #111d25",
-"#Hz c #111d26",
-".Iq c #111e23",
-".Ip c #111e24",
-".Ir c #111e25",
-".JH c #111e26",
-"avS c #111f25",
-"auN c #111f26",
-".Lu c #111f27",
-"#PW c #111f28",
-".W1 c #112027",
-".1A c #112028",
-".41 c #112029",
-"#GL c #11202a",
-".Y3 c #112127",
-".31 c #112128",
-".Q3 c #112129",
-"#h2 c #11212a",
-"aDE c #11212b",
-"#hW c #112229",
-".0G c #11222a",
-"#wN c #11222b",
-"aH1 c #11222c",
-".5b c #11232a",
-".Zc c #11232b",
-"#GE c #11232c",
-".20 c #11242b",
-"aIO c #11242c",
-"a1. c #11252c",
-"blc c #11252d",
-"#w1 c #113356",
-"#s8 c #113357",
-"#s7 c #113457",
-"#tW c #113458",
-"bXn c #113459",
-"a7O c #113559",
-"aJW c #11365a",
-"bhP c #11365b",
-"#s3 c #11375b",
-"#30 c #11385c",
-"#3a c #11395d",
-"#1I c #113c61",
-".Ah c #121a20",
-".DC c #121b21",
-".B3 c #121c20",
-".DY c #121c21",
-".Aa c #121c22",
-".DO c #121c23",
-"#Ym c #121d24",
-"#Jk c #121d25",
-".GY c #121e23",
-".Fz c #121e24",
-"#Xn c #121e25",
-".IY c #121e26",
-".5q c #121e27",
-".Kl c #121f25",
-".Ji c #121f26",
-".Km c #121f27",
-".TM c #121f28",
-".9W c #122026",
-".Sm c #122027",
-".N2 c #122028",
-".KT c #122029",
-"#ut c #12202a",
-".2K c #122127",
-".TL c #122128",
-".Qd c #122129",
-".30 c #12212a",
-"#sN c #12212b",
-".1z c #122228",
-"#yR c #122229",
-".9F c #12222a",
-"#B6 c #12222b",
-"bhq c #12222c",
-"aRK c #12222e",
-"bpA c #122329",
-".6l c #12232a",
-".Ya c #12232b",
-"#CL c #12232c",
-"#DO c #12232d",
-"aGx c #12242b",
-"aRQ c #12242c",
-"aPe c #12242d",
-"aDR c #12242e",
-"bcF c #12252b",
-"aRP c #12252c",
-"#r6 c #12252d",
-"bog c #12262e",
-"aOG c #122a39",
-"a9h c #123356",
-"#sr c #123458",
-"#w2 c #123558",
-"#s6 c #123559",
-"aYu c #123659",
-"biD c #12365a",
-"bg2 c #12365b",
-"#6i c #12375b",
-"#0t c #12395c",
-"#0s c #12395d",
-"#3# c #123d64",
-"aRJ c #123f68",
-"#34 c #123f6b",
-".uX c #12b5b5",
-".z5 c #131216",
-".Ai c #131a20",
-".BL c #131b20",
-".B2 c #131c20",
-".Cg c #131c22",
-".DN c #131d23",
-".E4 c #131d24",
-".GZ c #131e23",
-".FD c #131e24",
-".FC c #131e25",
-".FG c #131e26",
-"#UA c #131f25",
-".HL c #131f26",
-".JL c #131f27",
-".KV c #131f28",
-"#pF c #131f29",
-".I1 c #132026",
-".I0 c #132027",
-".K1 c #132028",
-".P1 c #132029",
-"#xY c #13202a",
-".9V c #132127",
-".Tf c #132128",
-".N3 c #132129",
-".3Z c #13212a",
-"#qr c #13212b",
-"#Er c #13212c",
-".Te c #132228",
-".W0 c #132229",
-".SX c #13222a",
-".OW c #13222b",
-"ai2 c #13222c",
-".Yb c #13232a",
-".Y. c #13232b",
-"#xl c #13232c",
-".Y# c #13242b",
-"#aP c #13242c",
-"aR1 c #13242d",
-"ax7 c #13242e",
-"bdm c #13252c",
-"aSC c #13252d",
-"##2 c #13252e",
-"aCZ c #13252f",
-"#uu c #13262e",
-"#uY c #133559",
-"#48 c #13355a",
-"#wq c #133659",
-"#kM c #13365a",
-"bKT c #13365b",
-"#mH c #13375a",
-"#vN c #13375b",
-"aE7 c #13375c",
-"#3Z c #13385b",
-"#rt c #13385c",
-"#rs c #13385d",
-"bXc c #13395c",
-"#si c #13395d",
-"#sj c #13395e",
-"bwM c #13395f",
-"#0r c #133a5f",
-"bew c #133b63",
-"aC7 c #133f66",
-"aC6 c #133f67",
-"bUm c #134271",
-"#8Y c #13497c",
-".Bz c #141a1f",
-".BN c #141b21",
-".BE c #141b22",
-".BO c #141c21",
-".BF c #141c22",
-".DQ c #141d24",
-".DD c #141e24",
-".Fb c #141e25",
-".Fj c #141e26",
-".FK c #141f25",
-".FB c #141f26",
-".FL c #141f27",
-"#RI c #142026",
-".Hz c #142027",
-".Lx c #142028",
-"#gI c #142029",
-"#oR c #14202a",
-".IZ c #142127",
-".I2 c #142128",
-".IW c #142129",
-".RZ c #14212a",
-"#ra c #14212b",
-"aq5 c #14212d",
-".7Z c #142228",
-".OA c #142229",
-".Oz c #14222a",
-".UN c #14222b",
-"#Eq c #14222c",
-"#Vl c #14222d",
-".5r c #142329",
-".OC c #14232a",
-".R6 c #14232b",
-"#HK c #14232c",
-"#wP c #14232d",
-".W7 c #14242b",
-".9L c #14242c",
-".5a c #14242d",
-".QL c #14242e",
-"aw# c #14242f",
-"bie c #14252c",
-"aVM c #14252d",
-"ayU c #14252e",
-"azL c #14252f",
-"aAw c #142531",
-"#Es c #142532",
-"#pG c #14262e",
-"#ql c #14262f",
-"#n1 c #14272f",
-"aS0 c #142836",
-"#Kp c #142c40",
-"a69 c #142c41",
-"a7N c #142d42",
-"#Kq c #143350",
-"aPE c #14354d",
-"bn5 c #143555",
-"bqU c #143656",
-"bl7 c #143657",
-"bsT c #143659",
-"#ig c #14375a",
-"#lK c #14375b",
-"#ih c #14385b",
-"#if c #14385c",
-"#RM c #14385d",
-"#1J c #14395c",
-"#sk c #14395d",
-"#HQ c #14395e",
-"#qF c #14395f",
-"aEl c #143a5b",
-"br4 c #143a5f",
-"biI c #143a60",
-"aSx c #143b61",
-"bg1 c #143b62",
-"aO4 c #143c65",
-"#Yw c #143d63",
-"#7p c #143d65",
-"bGU c #143e66",
-"bE1 c #143f67",
-"aTf c #144068",
-"bhU c #144372",
-"bJL c #144674",
-"bHz c #144775",
-"aMy c #144b79",
-".v5 c #145757",
-"aAm c #15191c",
-".BM c #151c22",
-".Cf c #151d24",
-".E5 c #151e25",
-".E8 c #151e26",
-"aMm c #151f2a",
-".FA c #152027",
-".GU c #152028",
-".HO c #152127",
-".HB c #152128",
-".HJ c #152129",
-".HI c #15212a",
-"#Oa c #15212b",
-".KW c #152229",
-".HK c #15222a",
-".Sb c #15222b",
-"#nh c #15222c",
-"aSK c #15222d",
-"##s c #152329",
-".Ha c #15232a",
-".No c #15232b",
-".UM c #15232c",
-"#Du c #15232d",
-"#Dt c #15232e",
-".7G c #15242b",
-".SY c #15242c",
-".Q0 c #15242d",
-"#ID c #15242e",
-"#N2 c #15242f",
-"#Fe c #15252b",
-"#Dy c #15252c",
-"aQ8 c #15252d",
-".Ty c #15252e",
-"#SN c #15252f",
-"#Jy c #152530",
-"#Ko c #152531",
-"#ks c #15262e",
-"#ln c #15262f",
-"blJ c #152630",
-"#UF c #152632",
-"aHL c #15272f",
-"avy c #152730",
-"#RJ c #152732",
-"#Kn c #152733",
-"#Jz c #152734",
-"a99 c #15282f",
-"aTO c #152835",
-"#GT c #152836",
-"#MZ c #152a3c",
-"aUE c #152b3a",
-"aSl c #152b3b",
-"bhZ c #152b3d",
-"a8x c #152d41",
-"aRt c #152f42",
-"bsq c #152f47",
-"boG c #153047",
-"bm. c #153048",
-"bh0 c #153049",
-"bl8 c #15324c",
-"#Ld c #153350",
-"bmH c #153453",
-"bnm c #153553",
-"bhO c #153759",
-"bm# c #15385a",
-"#sq c #15385c",
-"#ry c #15385d",
-"#rq c #153959",
-"#xw c #15395c",
-"#j# c #15395d",
-"#rr c #15395e",
-"#ww c #153a5e",
-"#yQ c #153a5f",
-"#sp c #153a60",
-"bV9 c #153b5f",
-"#AF c #153b60",
-"#ie c #153b61",
-"bvA c #153b62",
-"#0q c #153c62",
-"#O# c #153c63",
-"buF c #153d64",
-"bAj c #153f65",
-"bB6 c #153f66",
-"bA8 c #154067",
-"bfs c #154370",
-"bOI c #154371",
-"bXv c #154372",
-"#80 c #154674",
-"#81 c #154775",
-"aRE c #154a78",
-"aLG c #154b78",
-".tg c #155152",
-".DA c #161d24",
-".Dr c #161e24",
-".Do c #161e25",
-".Fk c #162128",
-".KX c #162129",
-".VS c #16212a",
-".HG c #162228",
-".HA c #162229",
-".HH c #16222a",
-".Hp c #16222b",
-"#do c #16222c",
-"#ob c #16222d",
-"brJ c #162329",
-".MI c #16232a",
-".Je c #16232b",
-".Nm c #16232c",
-"#oa c #16232d",
-"#X7 c #16232e",
-"btr c #16242a",
-".OB c #16242b",
-".P2 c #16242c",
-".RY c #16242d",
-"#Sv c #16242e",
-"#Tp c #16242f",
-"#D1 c #16252b",
-".0p c #16252c",
-".UW c #16252d",
-"#CK c #16252e",
-"#Ir c #16252f",
-"awL c #162530",
-"b#d c #16262d",
-"aYl c #16262e",
-"#oS c #16262f",
-"awa c #162630",
-"#PH c #162631",
-"a4z c #16272f",
-"aRR c #162730",
-"aFo c #162731",
-"a2Q c #162734",
-"bDo c #16282f",
-"at5 c #162830",
-"aQ7 c #162831",
-"aZ7 c #162832",
-"bmI c #162833",
-"aTh c #162835",
-"bCs c #162930",
-"a7i c #162931",
-"aEM c #162932",
-"#Zd c #162937",
-"#M0 c #162938",
-"aDL c #162a32",
-"#SO c #162a38",
-"#HJ c #162a39",
-"#FY c #162a3a",
-"#L1 c #162a3b",
-"#TH c #162b3a",
-"bpt c #162b3c",
-"a9g c #162c3d",
-"bl9 c #162c3f",
-"#DZ c #162d40",
-"#JA c #162e43",
-"a6C c #163046",
-"aQs c #16334a",
-"bFz c #16344f",
-"#RR c #163655",
-"bxW c #163755",
-"#RQ c #163757",
-"#QS c #163758",
-"bXh c #16375b",
-"#HP c #16385a",
-"#L9 c #16385b",
-"#0u c #16385c",
-"#JC c #16395c",
-"bl6 c #16395d",
-"brG c #16395e",
-"boF c #163a5d",
-"blv c #163a5e",
-"bg0 c #163b5e",
-"a9Y c #163b5f",
-"#GZ c #163b60",
-"#AE c #163b61",
-"aKT c #163c60",
-"#zZ c #163c61",
-"#G0 c #163c62",
-"#Ba c #163c63",
-"bxV c #163d62",
-"#Cu c #163d63",
-"#rx c #163d64",
-"#Yv c #163d65",
-"#QM c #163e65",
-"aZl c #163e66",
-"bQ5 c #163e67",
-"aSZ c #163f60",
-"bex c #163f68",
-".uq c #164040",
-"aC5 c #164068",
-"bwK c #16416a",
-"bP9 c #164472",
-"aT1 c #16456f",
-"aUD c #16466f",
-"#8Z c #164673",
-"bC7 c #164772",
-"bP0 c #164774",
-"#9. c #164775",
-"aMG c #164976",
-"aBV c #164b79",
-".DS c #172028",
-".G3 c #172228",
-".HE c #17222b",
-"#kv c #17222c",
-".MJ c #17232a",
-".JC c #17232b",
-".JK c #17232c",
-"#rb c #17232d",
-".JB c #17242b",
-".MK c #17242c",
-".Nn c #17242d",
-"#wQ c #17242e",
-"an7 c #17242f",
-"#9E c #172430",
-".Vr c #17252b",
-".VU c #17252c",
-".Q1 c #17252d",
-".O4 c #17252e",
-"#pJ c #17252f",
-".P5 c #17262d",
-".4. c #17262e",
-".Z5 c #17262f",
-".TZ c #172630",
-"#bE c #17272e",
-"#jF c #17272f",
-"#aU c #172730",
-"bgx c #172731",
-"a5d c #172732",
-"#Lc c #172734",
-"bqy c #17282f",
-"a9w c #172830",
-"#C# c #172831",
-"b#F c #172832",
-"a53 c #172833",
-"#SJ c #172834",
-"bti c #172835",
-"bzQ c #172931",
-"aDM c #172933",
-"#2G c #172934",
-"baR c #172935",
-"bqa c #172936",
-"bnn c #172938",
-"bvK c #172a31",
-"aDS c #172a32",
-"aDH c #172a33",
-"blC c #172a36",
-"btn c #172a37",
-"btm c #172a38",
-"#IC c #172b3c",
-"#Yr c #172d3d",
-"#Jx c #172d40",
-"#K9 c #172e40",
-"#L2 c #172e41",
-"bFx c #173147",
-"bFy c #17324a",
-"bxX c #17324b",
-"bXN c #17324c",
-"bXM c #17334f",
-"bhY c #17354c",
-"bFu c #173551",
-"#JB c #173552",
-"bu6 c #173653",
-"bt4 c #173654",
-"#QT c #173655",
-"#8L c #173659",
-"bql c #17374f",
-"bj5 c #173757",
-"#qE c #173857",
-"#L8 c #173858",
-"#IG c #173859",
-"bRb c #17385b",
-"bHl c #17385c",
-"a11 c #173a5b",
-"bmG c #173a5e",
-"bps c #173b5e",
-"bth c #173b5f",
-"blu c #173b60",
-"aW2 c #173c60",
-"br3 c #173c61",
-"bxJ c #173d62",
-"#Ct c #173d65",
-"aOF c #173e5f",
-"#Cv c #173e65",
-"#zv c #173e66",
-"#JD c #173f66",
-"#F8 c #173f67",
-"#Dx c #173f68",
-"#BQ c #174068",
-"#yc c #174069",
-"aTN c #174163",
-"aDY c #17416b",
-"aSk c #174266",
-"aYz c #17426f",
-"bhT c #174472",
-"aQM c #174671",
-"bey c #174675",
-"#Yx c #174773",
-"bK9 c #174774",
-"bJM c #174775",
-"aUN c #174875",
-"bHA c #17497a",
-"aDW c #174a77",
-"bMK c #175085",
-"bx7 c #175a96",
-"aHO c #182026",
-".DR c #182128",
-".E6 c #182129",
-".E7 c #18212a",
-".Fl c #182228",
-".Fc c #182229",
-".G0 c #18222a",
-"#oz c #18222b",
-"bhh c #18232a",
-".GV c #18232b",
-".JD c #18232c",
-".KY c #18232d",
-".Lv c #18242c",
-".KU c #18242d",
-".Lz c #18242e",
-"asM c #182431",
-".Nl c #18252c",
-".MM c #18252d",
-".J# c #18252e",
-".VV c #18252f",
-"#X9 c #182530",
-"arX c #182531",
-".WA c #18262d",
-".P3 c #18262e",
-".R0 c #18262f",
-".Lq c #182630",
-"arW c #182631",
-".Q2 c #18272e",
-".5i c #18272f",
-".UO c #182730",
-".Rl c #182731",
-".TV c #182732",
-"bjF c #182830",
-"a9v c #182831",
-"bCQ c #182832",
-"blK c #182834",
-"a9x c #182931",
-".0n c #182932",
-"a1a c #182933",
-"#2w c #182934",
-"bsv c #182935",
-"aDK c #182a32",
-"#QJ c #182a36",
-"aAv c #182a39",
-"aDI c #182b33",
-"#0a c #182b37",
-"bu. c #182b38",
-"bvb c #182b39",
-"bu9 c #182c3b",
-"bt5 c #182d3d",
-"a9X c #182e3f",
-"btl c #182e40",
-"bnr c #182f3d",
-"bVG c #182f46",
-"bkS c #183044",
-"bFw c #183047",
-"bno c #183145",
-"#E9 c #183146",
-"aDA c #183147",
-"bFv c #183148",
-"bWH c #183149",
-"bWG c #18314a",
-"bXO c #18314b",
-"bqf c #183242",
-"bhi c #183243",
-"#L. c #183247",
-"#Km c #183248",
-"bwN c #183249",
-"#D0 c #18324b",
-"bWF c #18324c",
-"btj c #183349",
-"bkV c #183448",
-"#Ep c #18344d",
-"#RS c #183450",
-"#pV c #183552",
-"bho c #18364a",
-"#GY c #183756",
-"#1K c #18375b",
-"bn8 c #18384c",
-"#RP c #183857",
-"bwb c #183858",
-"#L7 c #183a5a",
-"bn4 c #183b5f",
-"bl5 c #183c5f",
-"bt3 c #183c60",
-"#pW c #183e63",
-"#M7 c #183e64",
-"bh1 c #184063",
-"#yd c #184068",
-"#z0 c #184069",
-"#F9 c #184169",
-"#om c #18416a",
-"#j. c #18416b",
-"#s4 c #18426b",
-"#pb c #18426c",
-"#nu c #18426d",
-"b.9 c #18436f",
-"bAw c #18446a",
-"a0f c #18446f",
-"aPZ c #184471",
-"#s2 c #18456b",
-"aSw c #184570",
-"bRA c #184573",
-"bVi c #184673",
-"bdR c #184675",
-"aRs c #18476e",
-"bVa c #184775",
-"#7F c #184875",
-"a0d c #184976",
-"aCu c #184977",
-"aCw c #184a77",
-"aKA c #184a78",
-"bC1 c #184d79",
-"bdX c #18598d",
-".Fp c #19222a",
-".FF c #19232c",
-"bIS c #19242a",
-"bM9 c #19242b",
-"byZ c #19242c",
-".HC c #19242d",
-".ML c #19242e",
-".IX c #19252c",
-".Qp c #19252d",
-".MH c #19252e",
-".OE c #19252f",
-"#CJ c #192530",
-"aqc c #192532",
-".Un c #19262d",
-".OD c #19262e",
-".Jt c #19262f",
-".W9 c #192630",
-"#X8 c #192631",
-"aq6 c #192632",
-".P4 c #19272e",
-".OF c #19272f",
-".K7 c #192730",
-"#t# c #192731",
-"#L5 c #192732",
-"bq7 c #19282f",
-".1J c #192830",
-"#w. c #192831",
-".OU c #192832",
-"bwR c #192931",
-"aw. c #192932",
-".SR c #192933",
-".rJ c #19293e",
-"azI c #192a32",
-"#TG c #192a33",
-"aDJ c #192b32",
-"##l c #192b34",
-"btF c #192b35",
-"#1l c #192b36",
-"aDG c #192c33",
-".5l c #192c35",
-"bsO c #192c38",
-"bsu c #192c3c",
-"avi c #192d38",
-"#PK c #192d3a",
-"brK c #192d3c",
-"bvc c #192d3d",
-"awr c #192e3a",
-".y5 c #192f3e",
-"bAf c #192f41",
-"bUz c #192f45",
-"bUy c #193048",
-"azE c #193245",
-"bkR c #193248",
-"bVF c #19324c",
-"bkX c #193345",
-"b.w c #193348",
-"bXP c #19334d",
-"#QK c #19344b",
-"#QU c #19344f",
-"#Xs c #19354d",
-"#HI c #19354f",
-"brH c #193650",
-"bVE c #193651",
-"bWE c #193653",
-"#HO c #193654",
-"bgp c #19384c",
-"bpw c #19384d",
-"bkT c #193854",
-"bkQ c #193855",
-"#L0 c #193957",
-"bXL c #193958",
-"bVb c #193a59",
-"bzm c #193b5a",
-"bqV c #193b5b",
-"bxi c #193b5e",
-"bmg c #193c53",
-"bmF c #193c5f",
-"bpr c #193c60",
-"boJ c #193d55",
-"bu5 c #193d61",
-"bia c #193e57",
-"bmJ c #193e61",
-"bjk c #19405a",
-"#vU c #194064",
-"aGj c #194066",
-"#O. c #19416a",
-"#G. c #19426c",
-"#AB c #19436c",
-"#s5 c #19436d",
-"#F7 c #19436e",
-"#M. c #19446e",
-"#so c #19446f",
-"#mG c #194470",
-"aYq c #19456e",
-"#O3 c #194570",
-"#jY c #194571",
-"aO0 c #194572",
-"aOZ c #194673",
-"bSV c #194674",
-"bhV c #194774",
-"bfl c #194775",
-"a1Y c #194873",
-"#7E c #194875",
-"#9# c #194876",
-"aC4 c #194976",
-"aLB c #194977",
-"#1B c #194a77",
-"#1C c #194a78",
-"bzv c #194c7b",
-"#8K c #194d7d",
-".wz c #194e4e",
-"bcT c #195d98",
-"bGf c #1a2329",
-"aDt c #1a232b",
-"bD7 c #1a242a",
-"bD6 c #1a242b",
-".4# c #1a242c",
-".FE c #1a242d",
-".GW c #1a242e",
-"bD3 c #1a252b",
-"bD2 c #1a252c",
-".G1 c #1a252d",
-"aU2 c #1a252e",
-"a.J c #1a2531",
-".Hd c #1a262d",
-".Hb c #1a262e",
-".JG c #1a262f",
-".Ql c #1a2630",
-"#zP c #1a2631",
-"aph c #1a2633",
-".Ro c #1a272e",
-".O5 c #1a272f",
-".O3 c #1a2730",
-".S4 c #1a2731",
-"#g. c #1a2732",
-"#YO c #1a2733",
-".Vq c #1a282e",
-".Um c #1a282f",
-".XA c #1a2830",
-".XY c #1a2831",
-".Nf c #1a2832",
-".Rm c #1a2833",
-"#vV c #1a2930",
-"#qq c #1a2931",
-"ajs c #1a2932",
-".Q7 c #1a2933",
-"bd5 c #1a2a32",
-".39 c #1a2a33",
-"#bM c #1a2a34",
-"bd6 c #1a2b32",
-".32 c #1a2b33",
-"#jG c #1a2b34",
-"#h9 c #1a2b35",
-"bam c #1a2b36",
-"bxY c #1a2b38",
-"aAD c #1a2c34",
-"aHq c #1a2c36",
-"bFB c #1a2c3a",
-"au1 c #1a2d35",
-"#fD c #1a2d36",
-"aC0 c #1a2d37",
-"br2 c #1a2d3c",
-"#L6 c #1a2e3f",
-".rH c #1a2e49",
-"#Z6 c #1a2f3c",
-"#Is c #1a2f3e",
-"#NV c #1a303e",
-"brL c #1a3144",
-"bva c #1a3245",
-"brP c #1a3347",
-"#QV c #1a334c",
-"bUx c #1a334d",
-"brO c #1a3449",
-"bby c #1a344a",
-"#qD c #1a354e",
-"bxE c #1a3559",
-"bHB c #1a364f",
-"#Ds c #1a3650",
-"#Wz c #1a3751",
-"azD c #1a3852",
-"#IB c #1a3853",
-"bXm c #1a385b",
-"#RK c #1a3954",
-"#Ze c #1a3955",
-"bu# c #1a3a51",
-"b.8 c #1a3a57",
-"#FX c #1a3a58",
-"bkJ c #1a3a5a",
-"bkU c #1a3c5d",
-"bsS c #1a3c5e",
-"#K8 c #1a3d5d",
-"bn3 c #1a3d60",
-"bl4 c #1a3d61",
-"#1H c #1a3d64",
-"bAx c #1a3e5d",
-"boH c #1a3e61",
-"btK c #1a3e62",
-"bu8 c #1a3f63",
-"bkW c #1a405a",
-"a5e c #1a4063",
-"bkP c #1a4065",
-"aAK c #1a4162",
-"#u7 c #1a4266",
-"a9i c #1a4267",
-"bt6 c #1a4269",
-"bzl c #1a426b",
-"bgw c #1a435f",
-"brI c #1a4360",
-"bu7 c #1a436a",
-"bqb c #1a436b",
-"#G# c #1a446f",
-"bwc c #1a456e",
-"#id c #1a4570",
-"#SR c #1a4571",
-"bgk c #1a4572",
-"#Ga c #1a4671",
-"#zw c #1a4672",
-"#Cs c #1a4673",
-"bxI c #1a4674",
-"blB c #1a4767",
-"#SS c #1a4773",
-"#TM c #1a4774",
-"bma c #1a4775",
-"#21 c #1a4872",
-"aBh c #1a4874",
-"#QR c #1a4875",
-"aZk c #1a4876",
-"a4r c #1a4877",
-"aCv c #1a4976",
-"aUK c #1a4977",
-"#36 c #1a4978",
-"bhj c #1a4b6c",
-"aQr c #1a4c77",
-"aPT c #1a4d7a",
-"aKy c #1a4e7b",
-"beD c #1a5384",
-"bA9 c #1a5786",
-"bAk c #1a5888",
-"biH c #1a5889",
-"bB7 c #1a5989",
-"bej c #1a5c97",
-".qm c #1b1a18",
-".zq c #1b1e20",
-".Fq c #1b212a",
-"bGi c #1b2328",
-"#jK c #1b232d",
-"bGg c #1b2429",
-"bD8 c #1b242a",
-".Zd c #1b242d",
-"bmq c #1b242e",
-"bD5 c #1b252b",
-"bD4 c #1b252c",
-"bdl c #1b252e",
-"aTu c #1b252f",
-"bD1 c #1b262d",
-".G2 c #1b262e",
-".Rn c #1b262f",
-"#FQ c #1b2630",
-"#Ap c #1b2632",
-"bD0 c #1b272e",
-".Qm c #1b272f",
-".Lp c #1b2730",
-"#p9 c #1b2731",
-"#Aq c #1b2732",
-"a.6 c #1b2733",
-"bwO c #1b282e",
-".Qn c #1b282f",
-".Qo c #1b2830",
-".R1 c #1b2831",
-".T0 c #1b2832",
-"#Ab c #1b2833",
-"a.2 c #1b2834",
-".Ul c #1b292f",
-"bdk c #1b2930",
-".Xz c #1b2931",
-".0f c #1b2932",
-".M0 c #1b2933",
-".W6 c #1b2934",
-"beU c #1b2a31",
-"ayL c #1b2a32",
-"aoR c #1b2a33",
-".R2 c #1b2a34",
-"#cA c #1b2a35",
-"bif c #1b2b32",
-"aXb c #1b2b33",
-".X3 c #1b2b34",
-".1C c #1b2b35",
-"ayi c #1b2b36",
-".9G c #1b2c35",
-".9R c #1b2c36",
-"bvJ c #1b2c38",
-"bDj c #1b2d35",
-".4f c #1b2d36",
-"aHs c #1b2d37",
-"bwQ c #1b2d39",
-"aHr c #1b2e38",
-"#56 c #1b2e39",
-"a3I c #1b2f3a",
-"aAz c #1b2f3e",
-"aw6 c #1b303b",
-"bvF c #1b3041",
-"#MV c #1b313f",
-"btE c #1b3245",
-"#RD c #1b3343",
-"#GX c #1b334b",
-"brM c #1b3449",
-"brN c #1b354b",
-"bUA c #1b354e",
-"azW c #1b364d",
-"bWI c #1b3650",
-"#0b c #1b374d",
-"bvE c #1b3752",
-"bUw c #1b3753",
-"bID c #1b3954",
-"azV c #1b3955",
-"baf c #1b3a56",
-"#Vz c #1b3a57",
-"#Jw c #1b3b58",
-"bVD c #1b3b5b",
-"#N3 c #1b3c5b",
-"bt2 c #1b3d61",
-"az6 c #1b3e5d",
-"#M6 c #1b3e61",
-"bxh c #1b3e62",
-"az5 c #1b4165",
-"bto c #1b4269",
-"bvi c #1b4361",
-"#tV c #1b4369",
-"aYv c #1b4469",
-"btk c #1b446c",
-"aSy c #1b446d",
-"bsr c #1b456e",
-"bkO c #1b456f",
-"aTg c #1b4570",
-"aE6 c #1b4670",
-"#ST c #1b4671",
-"#pZ c #1b4673",
-"#Gb c #1b4773",
-"#z1 c #1b4774",
-"aYt c #1b4873",
-"#JE c #1b4874",
-"#z5 c #1b4875",
-"#TL c #1b4876",
-"#qG c #1b4877",
-"bgq c #1b4969",
-"#2N c #1b4975",
-"#Fc c #1b4976",
-"#lJ c #1b4977",
-"#Zh c #1b4978",
-"aQN c #1b4a72",
-"biE c #1b4a77",
-"ay1 c #1b4a78",
-"#yb c #1b4a79",
-"bvz c #1b4b7b",
-"bgl c #1b4c7a",
-"bk. c #1b4d70",
-"bj8 c #1b4d7c",
-"bj7 c #1b4f7d",
-"bjg c #1b4f7e",
-"bgu c #1b5074",
-"bgr c #1b5176",
-"bgt c #1b5277",
-"bgv c #1b5278",
-"biN c #1b547c",
-"bkN c #1b5583",
-"bnq c #1b567d",
-"aFv c #1b5894",
-"b.X c #1b5994",
-"bc# c #1b5c96",
-".th c #1bc6c7",
-".tU c #1be8e8",
-".q2 c #1c2025",
-"aGB c #1c2226",
-"bEb c #1c2327",
-"bEa c #1c2328",
-".Zg c #1c232d",
-"ba6 c #1c232e",
-"bGh c #1c2428",
-"bE# c #1c2429",
-"bE. c #1c242a",
-"aR0 c #1c242e",
-"bD9 c #1c252a",
-"bHP c #1c252b",
-"bGe c #1c252c",
-"aU1 c #1c252f",
-"brm c #1c2530",
-".Hq c #1c262e",
-".4d c #1c2630",
-".Hc c #1c272e",
-"bQg c #1c272f",
-".HD c #1c2730",
-"#po c #1c2731",
-"aoe c #1c2733",
-".Re c #1c282f",
-".I3 c #1c2830",
-".Jd c #1c2831",
-".La c #1c2832",
-".UZ c #1c2833",
-"aFk c #1c2834",
-"bWT c #1c2836",
-".Sc c #1c2930",
-".Ja c #1c2931",
-".MN c #1c2932",
-".MV c #1c2933",
-".Rc c #1c2934",
-"arV c #1c2935",
-"bVN c #1c2937",
-".Vp c #1c2a31",
-".X1 c #1c2a32",
-".21 c #1c2a33",
-"#ta c #1c2a34",
-".Z# c #1c2a35",
-"bUK c #1c2a37",
-"bWS c #1c2a38",
-"bXZ c #1c2a39",
-"b.B c #1c2b33",
-".Q6 c #1c2b34",
-".P8 c #1c2b35",
-"ayV c #1c2b36",
-"#IE c #1c2b39",
-"bXY c #1c2b3a",
-"bWR c #1c2b3b",
-"aAC c #1c2c34",
-".6c c #1c2c35",
-".86 c #1c2c36",
-"#gQ c #1c2d35",
-".23 c #1c2d36",
-"#h6 c #1c2d37",
-"aAE c #1c2d38",
-"btL c #1c2d39",
-"bXX c #1c2d3d",
-"a18 c #1c2e37",
-"bvh c #1c2e3b",
-"bwP c #1c2e3c",
-"ax8 c #1c2e3d",
-"azP c #1c303f",
-"#OX c #1c3242",
-"bvG c #1c3244",
-"#QW c #1c3248",
-"azY c #1c3345",
-"bJO c #1c3346",
-"aye c #1c3347",
-"azX c #1c3348",
-"avT c #1c3443",
-"ayW c #1c3448",
-"#pU c #1c344a",
-"#HN c #1c344b",
-"azQ c #1c354c",
-"ayd c #1c364e",
-"#Zn c #1c365a",
-"bq6 c #1c374e",
-"bJx c #1c375b",
-"ayc c #1c3852",
-"bXQ c #1c3956",
-"az7 c #1c3a52",
-"aya c #1c3a55",
-"#Dv c #1c3a56",
-"ayb c #1c3a57",
-"bvD c #1c3a58",
-"bts c #1c3b55",
-"bvd c #1c3b57",
-"ay# c #1c3b58",
-"bvC c #1c3b59",
-"bUv c #1c3b5b",
-"a02 c #1c3d58",
-"azR c #1c3d5d",
-"bWD c #1c3d5e",
-"azU c #1c3e5e",
-"#L# c #1c3e5f",
-"brF c #1c3e61",
-"#Ys c #1c3f5f",
-"baS c #1c3f60",
-"boE c #1c3f62",
-"azC c #1c4061",
-"#Kl c #1c4062",
-"azT c #1c4063",
-"ayX c #1c4164",
-"#PR c #1c4166",
-"#g4 c #1c4262",
-"az4 c #1c4266",
-"ay4 c #1c4369",
-"bvB c #1c436a",
-"aWl c #1c4468",
-"#PQ c #1c446c",
-"bJN c #1c446d",
-"azS c #1c456d",
-"ay3 c #1c456e",
-"bxj c #1c466f",
-"ayY c #1c4670",
-"#PP c #1c4671",
-"ay2 c #1c4772",
-"#2O c #1c4773",
-"#O2 c #1c4873",
-"#PO c #1c4874",
-"#PN c #1c4875",
-"aBW c #1c4876",
-"#QQ c #1c4975",
-"#Fd c #1c4976",
-"#h. c #1c4977",
-"#Fb c #1c4a77",
-"#h# c #1c4a78",
-"#rw c #1c4a79",
-"ay0 c #1c4a7a",
-"#QP c #1c4b79",
-"#qJ c #1c4b7a",
-"ayZ c #1c4b7b",
-"bje c #1c4c7a",
-"buG c #1c4c7c",
-"b#H c #1c4c7d",
-"biJ c #1c4f7e",
-"bft c #1c5181",
-"bMZ c #1c5187",
-"bgs c #1c5277",
-"bj6 c #1c5282",
-"bjf c #1c5586",
-"bvj c #1c5680",
-"bj9 c #1c5687",
-"biK c #1c5688",
-"bhd c #1c5788",
-"bh2 c #1c5888",
-"bOs c #1c5890",
-"#7D c #1c5892",
-"blw c #1c598a",
-"a7y c #1c5994",
-"bmK c #1c5a8c",
-"a35 c #1c5a95",
-"bhn c #1c5b86",
-"bqW c #1c5d8a",
-".z4 c #1d191d",
-".2S c #1d232d",
-"a8U c #1d232e",
-".Ze c #1d242e",
-"bOY c #1d252e",
-"#qs c #1d2530",
-"#mt c #1d262f",
-"#dp c #1d2630",
-"aG. c #1d2730",
-"bQo c #1d2731",
-"bTi c #1d2732",
-"bLn c #1d282f",
-"aHY c #1d2830",
-".Jv c #1d2831",
-".JE c #1d2832",
-".V4 c #1d2833",
-"#GC c #1d2834",
-"bQh c #1d2930",
-".I4 c #1d2931",
-".KZ c #1d2932",
-".Lo c #1d2933",
-".W8 c #1d2934",
-"aq4 c #1d2935",
-"aqd c #1d2936",
-".Jc c #1d2a31",
-".U0 c #1d2a32",
-".Rd c #1d2a33",
-".Nc c #1d2a34",
-"#D4 c #1d2a35",
-"aay c #1d2a36",
-".25 c #1d2b32",
-"#n7 c #1d2b33",
-".2R c #1d2b34",
-"#fU c #1d2b35",
-"#g# c #1d2b36",
-"bao c #1d2c34",
-".SQ c #1d2c35",
-"#aa c #1d2c36",
-"aGa c #1d2c37",
-"bTh c #1d2c3a",
-".8U c #1d2d35",
-".1S c #1d2d36",
-"#fC c #1d2d37",
-"bgF c #1d2d38",
-"bUJ c #1d2d3c",
-"aG# c #1d2e36",
-".5h c #1d2e37",
-"#cv c #1d2e38",
-"blH c #1d2e39",
-"ayh c #1d2e3b",
-"bVM c #1d2e3e",
-".7M c #1d2f37",
-"b.J c #1d2f39",
-"aFf c #1d2f3a",
-"ayg c #1d2f3e",
-"aAA c #1d2f3f",
-"bWQ c #1d2f40",
-"#yB c #1d3039",
-"b.C c #1d303a",
-"bgZ c #1d3042",
-"bXW c #1d3044",
-"ayf c #1d3142",
-"#GW c #1d3145",
-"a6F c #1d323d",
-"#3I c #1d333f",
-"a3H c #1d3340",
-"azZ c #1d3346",
-"aYD c #1d3442",
-"az0 c #1d3447",
-"ay7 c #1d3448",
-"a.A c #1d3548",
-"ax9 c #1d354a",
-"az1 c #1d364c",
-"bKS c #1d365a",
-"bur c #1d374e",
-"bNc c #1d374f",
-"bLs c #1d3750",
-"bVH c #1d3751",
-"bT. c #1d3853",
-"bN# c #1d3854",
-"bwI c #1d385c",
-"#3Q c #1d3949",
-"buq c #1d3951",
-"ay6 c #1d3952",
-"bOR c #1d3954",
-"bS9 c #1d3956",
-"bwm c #1d3a54",
-"bQk c #1d3a55",
-"bQj c #1d3a57",
-"ay. c #1d3b56",
-"bXR c #1d3b57",
-"bRO c #1d3b58",
-"bS8 c #1d3b59",
-"az2 c #1d3c58",
-"#PT c #1d3c5a",
-"bS7 c #1d3c5b",
-"aC8 c #1d3c64",
-"bsN c #1d3d59",
-"ay5 c #1d3d5a",
-"a4s c #1d3d5d",
-"bVC c #1d3e5f",
-"#PS c #1d3e60",
-"az3 c #1d3f61",
-"bt9 c #1d4060",
-"bv# c #1d4061",
-"#SP c #1d4062",
-"bwa c #1d4063",
-"bXK c #1d4064",
-"#GS c #1d4163",
-"bSP c #1d4267",
-"#HH c #1d4368",
-"bzk c #1d4369",
-"aYp c #1d436a",
-"#TI c #1d4469",
-"#37 c #1d446a",
-"#N9 c #1d446b",
-"aQR c #1d456b",
-"#0d c #1d456d",
-"#s9 c #1d466b",
-"bxK c #1d466c",
-"bhQ c #1d466d",
-"#Zi c #1d466e",
-"#1p c #1d466f",
-"buE c #1d4771",
-"aTc c #1d4774",
-"aKS c #1d4870",
-"#O1 c #1d4873",
-"aKB c #1d4875",
-"aMJ c #1d4972",
-"#89 c #1d4976",
-"bck c #1d4a73",
-"aNS c #1d4a77",
-"aVB c #1d4a78",
-"aDX c #1d4b78",
-"#WB c #1d4b79",
-"#Fa c #1d4b7a",
-"#2M c #1d4b7b",
-"bua c #1d4c71",
-"aPD c #1d4c78",
-"aLA c #1d4c7a",
-"#QN c #1d4c7b",
-"#g9 c #1d4c7c",
-"#PM c #1d4d7c",
-"#sl c #1d4d7d",
-"#Xu c #1d4d7e",
-"bB5 c #1d4e7e",
-"bmN c #1d4f80",
-"bag c #1d4f82",
-"bvy c #1d5083",
-"bzq c #1d5486",
-"bmf c #1d5681",
-"aOU c #1d5885",
-"bvt c #1d588f",
-"bII c #1d5895",
-"bvu c #1d5991",
-"a8g c #1d5994",
-"a3h c #1d5a95",
-"#dM c #1d5b96",
-"bgm c #1d5c8e",
-"aWa c #1d5c97",
-"bmP c #1d5d8b",
-"bkK c #1d5e91",
-"bmM c #1d5f93",
-"bh3 c #1d6197",
-"biL c #1d6297",
-"bhk c #1d6393",
-"bh4 c #1d659b",
-"bn7 c #1d6698",
-"bjh c #1d679d",
-"bkM c #1d689e",
-"bhW c #1d69a0",
-"bhX c #1d6a9f",
-"biF c #1d6aa1",
-"bB. c #1d6ca3",
-"bd. c #1d6fa9",
-"bqd c #1d70a9",
-"bss c #1d71aa",
-"bvm c #1d72ac",
-".vx c #1dbdbd",
-".zB c #1e1a1c",
-".yy c #1e1e1e",
-".7H c #1e232d",
-"bHX c #1e2429",
-"bKh c #1e242a",
-"bLz c #1e242b",
-".Zf c #1e242e",
-"bKg c #1e252b",
-"bKf c #1e252c",
-"bNm c #1e252d",
-"bHQ c #1e262b",
-"bLy c #1e262d",
-"bNl c #1e262e",
-"bOX c #1e262f",
-"aZB c #1e2630",
-"bGc c #1e272e",
-"bQp c #1e272f",
-"bNk c #1e2730",
-"atT c #1e2731",
-"bIT c #1e282f",
-"aHp c #1e2831",
-"a9u c #1e2832",
-"bDZ c #1e2930",
-".Ju c #1e2932",
-".JF c #1e2933",
-"#2c c #1e2934",
-".Jb c #1e2a32",
-".L# c #1e2a33",
-".Lw c #1e2a34",
-".V6 c #1e2a35",
-"apa c #1e2a36",
-"aqX c #1e2a37",
-".Rp c #1e2b32",
-".K0 c #1e2b33",
-".MQ c #1e2b34",
-".OO c #1e2b35",
-"#e5 c #1e2b36",
-"#nD c #1e2b37",
-".K9 c #1e2c33",
-".MP c #1e2c34",
-".UP c #1e2c35",
-".OK c #1e2c36",
-"#gc c #1e2c37",
-"#M3 c #1e2c38",
-"bWU c #1e2c39",
-"bX0 c #1e2c3a",
-"aAB c #1e2d35",
-".43 c #1e2d36",
-".22 c #1e2d37",
-"aoQ c #1e2d38",
-".9O c #1e2e38",
-"boK c #1e2e39",
-"bFV c #1e2e3c",
-"#bO c #1e2f38",
-"#uJ c #1e2f39",
-"aMZ c #1e2f3a",
-"bTg c #1e2f3f",
-"#.D c #1e3038",
-"#zj c #1e3039",
-"aLb c #1e303a",
-"aL5 c #1e303b",
-"ay8 c #1e303f",
-"#qC c #1e313c",
-"azF c #1e313f",
-"bUF c #1e3144",
-"bxF c #1e3153",
-"#p. c #1e3244",
-"bVK c #1e3245",
-"bWP c #1e3246",
-"#IF c #1e3347",
-"bWN c #1e3348",
-"bFA c #1e334f",
-"#4V c #1e3440",
-"bvg c #1e3446",
-"bWO c #1e3449",
-"bXV c #1e344a",
-"bXU c #1e354b",
-"aT3 c #1e3646",
-"az8 c #1e3648",
-"bRS c #1e364e",
-"bTa c #1e364f",
-"#Yq c #1e3747",
-"#GM c #1e3749",
-"bQm c #1e374f",
-"bNb c #1e3750",
-"bLq c #1e3751",
-"bRR c #1e3850",
-"bLr c #1e3851",
-"bNa c #1e3852",
-"bT# c #1e3853",
-"bQl c #1e3953",
-"#PU c #1e3954",
-"bRQ c #1e3a55",
-"bRP c #1e3a56",
-"bRN c #1e3a57",
-"bsR c #1e3b58",
-"btD c #1e3f5d",
-"bvf c #1e3f5e",
-"a06 c #1e3f5f",
-"#N8 c #1e4061",
-"bq# c #1e4063",
-"bt1 c #1e4163",
-"bsw c #1e4264",
-"a3F c #1e4265",
-"bve c #1e4365",
-"btt c #1e4366",
-"#9a c #1e4367",
-"#Zk c #1e4469",
-"#5f c #1e446a",
-"#Zj c #1e446b",
-"#IA c #1e466d",
-"bOB c #1e466e",
-"#Zl c #1e466f",
-"#L3 c #1e476f",
-"bBd c #1e4773",
-"aMx c #1e4774",
-"#wZ c #1e486e",
-"#PL c #1e4870",
-"axQ c #1e4971",
-"#K7 c #1e4c7a",
-"aKz c #1e4d7a",
-"#RN c #1e4d7d",
-"#QO c #1e4d7e",
-"aWo c #1e4e7c",
-"#AC c #1e4e7e",
-"#ha c #1e4e7f",
-"#zx c #1e4e80",
-"#z2 c #1e4f80",
-"#6h c #1e4f81",
-"#UG c #1e5081",
-"#kL c #1e5082",
-"aLz c #1e517f",
-"bgj c #1e5482",
-"#Xv c #1e5488",
-"aEk c #1e558c",
-"b#4 c #1e5994",
-"a9O c #1e5a94",
-"#6t c #1e5a95",
-"beH c #1e5b8a",
-"a0L c #1e5b95",
-"#cP c #1e5b96",
-"bgo c #1e5c8a",
-"aAp c #1e5c96",
-"#b0 c #1e5c97",
-"aYg c #1e5d97",
-"bMQ c #1e5e97",
-"bsL c #1e5f9b",
-"bfw c #1e6093",
-"bst c #1e6193",
-"brZ c #1e619a",
-"bvk c #1e679b",
-"bd# c #1e679d",
-"blA c #1e689e",
-"bmL c #1e699f",
-"bqe c #1e6a9f",
-"bh5 c #1e6aa1",
-"boI c #1e6ba1",
-"bd0 c #1e6ba2",
-"bfu c #1e6ca3",
-"beE c #1e6da5",
-"bB8 c #1e6ea6",
-"bgn c #1e6fa7",
-"bh6 c #1e6fa8",
-"bkL c #1e70a8",
-"bhe c #1e70a9",
-"bi# c #1e70aa",
-"bdY c #1e71aa",
-"bji c #1e71ab",
-"beG c #1e71ac",
-"bmb c #1e72ab",
-"bpv c #1e72ac",
-"bhf c #1e72ad",
-"bfv c #1e73ad",
-"bdZ c #1e73ae",
-"bwq c #1e74af",
-"aCO c #1f1e1f",
-".xv c #1f1f1f",
-".BZ c #1f1f26",
-"bEC c #1f2327",
-"bEB c #1f2428",
-"bEA c #1f2429",
-"#i1 c #1f242e",
-"#pO c #1f242f",
-"bGj c #1f252a",
-"bGw c #1f252b",
-"bI3 c #1f252c",
-"bEc c #1f262a",
-"bHW c #1f262c",
-"aVN c #1f2630",
-"a71 c #1f2631",
-"bKe c #1f272e",
-".IV c #1f272f",
-"ath c #1f2732",
-"bKd c #1f2831",
-"#wg c #1f2832",
-"bLx c #1f2932",
-"bNj c #1f2933",
-"#OJ c #1f2a31",
-"ayt c #1f2a32",
-"aD9 c #1f2a33",
-"#qX c #1f2a34",
-"#qW c #1f2a35",
-".V5 c #1f2a36",
-".S5 c #1f2b32",
-".Rq c #1f2b33",
-".Ln c #1f2b34",
-".Ne c #1f2b35",
-".T1 c #1f2b36",
-"aqe c #1f2b37",
-"arM c #1f2b38",
-".Uk c #1f2c32",
-".U1 c #1f2c33",
-".K8 c #1f2c34",
-".MO c #1f2c35",
-".ON c #1f2c36",
-"#f. c #1f2c37",
-"#e9 c #1f2c38",
-"bKc c #1f2c39",
-"bOW c #1f2c3a",
-".L. c #1f2d34",
-".MW c #1f2d35",
-".7w c #1f2d36",
-".4e c #1f2d37",
-"#hu c #1f2d38",
-"bLw c #1f2d3a",
-"bNg c #1f2d3b",
-"bNf c #1f2d3c",
-"aCm c #1f2e36",
-"#kx c #1f2e37",
-"#fJ c #1f2e38",
-"aRA c #1f2e39",
-"#CZ c #1f2e3a",
-"bRU c #1f2e3b",
-"#SV c #1f2e3d",
-"#SW c #1f2e3e",
-"anC c #1f2f38",
-"#ex c #1f2f39",
-"#Jj c #1f2f3a",
-"bP3 c #1f2f3b",
-"a0U c #1f2f3c",
-"a5h c #1f2f3e",
-"#GV c #1f2f3f",
-"bUI c #1f2f40",
-"#np c #1f3038",
-".85 c #1f3039",
-"aLr c #1f303a",
-"amt c #1f303b",
-"bzn c #1f303e",
-"bRW c #1f3040",
-"bRV c #1f3041",
-"#RT c #1f3042",
-"auw c #1f313a",
-"aUF c #1f313b",
-"#pT c #1f3141",
-"bTf c #1f3142",
-"bVL c #1f3143",
-"bUH c #1f3144",
-"bUG c #1f3145",
-"aPd c #1f323e",
-"bXT c #1f3245",
-"bOS c #1f3347",
-"bUB c #1f3447",
-"bLt c #1f3448",
-"aPL c #1f3541",
-"#SU c #1f354b",
-"bPL c #1f365a",
-"#Xr c #1f3745",
-"#PV c #1f3750",
-"a3C c #1f3850",
-"#2H c #1f3a4b",
-"#M5 c #1f3c56",
-"bVB c #1f3c58",
-"aAL c #1f3d55",
-"bg. c #1f3d59",
-"bup c #1f3e5b",
-"aYo c #1f3e5c",
-"a0e c #1f3f5e",
-".w4 c #1f4040",
-"#0e c #1f4060",
-"#7G c #1f4163",
-"bpq c #1f4164",
-"axO c #1f4264",
-"bmE c #1f4265",
-"bP1 c #1f4367",
-"bXJ c #1f4368",
-"bWC c #1f4469",
-"axP c #1f466c",
-"#0p c #1f466e",
-"#0c c #1f486f",
-"bwn c #1f4870",
-"#Jv c #1f4973",
-"bhg c #1f4a6a",
-"#wr c #1f4a6e",
-"#OY c #1f4a74",
-"#E8 c #1f4a75",
-"#O0 c #1f4a76",
-"#QL c #1f4b76",
-"aNL c #1f4b77",
-"aOT c #1f4b78",
-"bd1 c #1f4e74",
-"aLH c #1f4e7b",
-"#Kk c #1f4e7d",
-"aQQ c #1f4f7c",
-"bwd c #1f4f80",
-"#Dw c #1f5082",
-"bOH c #1f5182",
-"#Ex c #1f5183",
-"#sn c #1f5184",
-"aLy c #1f5280",
-"#6u c #1f5284",
-"#g8 c #1f5285",
-"bda c #1f537c",
-"bUl c #1f5890",
-"a5J c #1f5893",
-"a36 c #1f5994",
-"#Xw c #1f5a91",
-"bvv c #1f5a92",
-"a#r c #1f5a94",
-"a5I c #1f5a95",
-"bub c #1f5b8c",
-"aA1 c #1f5b95",
-"bAO c #1f5b96",
-"bca c #1f5c96",
-"#8I c #1f5d97",
-"bzr c #1f5e93",
-"bUb c #1f5e97",
-"abf c #1f5e98",
-"afJ c #1f5f99",
-"bws c #1f6ba4",
-"bwp c #1f6ca4",
-"bt8 c #1f6ca5",
-"bB9 c #1f6ca6",
-"bqc c #1f6da6",
-"bwr c #1f6ea7",
-"bwe c #1f6fa8",
-"bzt c #1f6fa9",
-"bh9 c #1f70a9",
-"bvl c #1f70aa",
-"bB# c #1f71a9",
-"bh8 c #1f71aa",
-"bjj c #1f71ab",
-"bh7 c #1f72ab",
-"beF c #1f72ac",
-"biG c #1f73ac",
-"bi. c #1f73ad",
-"bhm c #1f73ae",
-"biM c #1f73af",
-"bhl c #1f74af",
-".v4 c #202020",
-"bET c #202122",
-"bGS c #202222",
-"bES c #202223",
-".Hr c #20232e",
-"bKA c #202426",
-"bED c #202428",
-"bGO c #202429",
-"bJi c #202528",
-"bGN c #202529",
-"bEz c #20252a",
-"aU0 c #20252f",
-"aRO c #202530",
-"bEL c #20262b",
-"bEy c #20262c",
-"bGv c #20262d",
-"aF9 c #202630",
-"bEK c #20272c",
-"bEx c #20272d",
-"bW6 c #20272e",
-"bGu c #20272f",
-"a3Q c #202732",
-"bEI c #20282d",
-"bEE c #20282e",
-"bYd c #20282f",
-"bYc c #202830",
-"bYb c #202831",
-"bYa c #202832",
-"aYK c #202833",
-"bEH c #20292e",
-"bEF c #20292f",
-"bHO c #202930",
-"bEs c #202932",
-"bEr c #202933",
-"aHt c #202934",
-"bGr c #202a33",
-"bGq c #202a34",
-"aZC c #202a35",
-".UX c #202b32",
-"aui c #202b33",
-"abT c #202b36",
-"afo c #202b37",
-".Rr c #202c33",
-".Rs c #202c34",
-".VW c #202c35",
-".Nd c #202c36",
-".S3 c #202c37",
-"alM c #202c38",
-"bNi c #202c39",
-".JM c #202d33",
-".Vo c #202d34",
-".70 c #202d35",
-".MX c #202d36",
-".0H c #202d37",
-"#e4 c #202d38",
-"#0Y c #202d39",
-"bNh c #202d3a",
-"bQn c #202d3b",
-"#p0 c #202e35",
-".MY c #202e36",
-".0o c #202e37",
-".6q c #202e38",
-"#lh c #202e39",
-"ajv c #202e3a",
-"bTe c #202e3c",
-"bUE c #202e3d",
-"bcs c #202f38",
-"#wS c #202f39",
-"#Ji c #202f3a",
-"bF0 c #202f3b",
-"#o9 c #202f3c",
-"bVJ c #202f3d",
-"#RU c #202f3f",
-"#a0 c #203039",
-"#vE c #20303a",
-"azb c #20303b",
-"bFW c #20303c",
-"bWM c #203040",
-"#zk c #203139",
-"#zV c #20313a",
-"afu c #20313b",
-"at6 c #20313c",
-"byV c #20313f",
-"bUC c #203142",
-"ata c #20323b",
-"aRS c #20323c",
-"aqK c #20323e",
-"#HM c #203242",
-"bhN c #203345",
-"#M4 c #203446",
-"biC c #203548",
-"a0. c #203749",
-"bQi c #20374f",
-"aDZ c #20375b",
-"a2I c #203848",
-"bSC c #20385c",
-"a1X c #20394f",
-"bS6 c #203952",
-"#CY c #203a51",
-"#0f c #203a54",
-"#Jl c #203b4d",
-"bA6 c #203b52",
-"a10 c #203c57",
-"a3E c #203c58",
-"#1q c #203e5a",
-"bRt c #204161",
-"bn2 c #204264",
-"bnl c #204265",
-"btg c #204365",
-"#FZ c #204366",
-"#1A c #204467",
-"bXI c #204569",
-"aTe c #204572",
-"buo c #20466a",
-"#w0 c #204a6f",
-"#La c #204c78",
-"#Zf c #204d78",
-"aE2 c #204d7a",
-"#F6 c #204d7b",
-"byj c #204e7c",
-"#TN c #204f7f",
-"b#G c #20507f",
-"#pX c #205284",
-"#z3 c #205285",
-"#ic c #205286",
-"#Ew c #205386",
-"#Ey c #205387",
-"#zu c #205488",
-"#i9 c #205489",
-"#7e c #205690",
-"bvw c #20588f",
-"bJU c #205993",
-"a3i c #205994",
-"#g5 c #205a92",
-"#ML c #205a94",
-"a5H c #205a95",
-"bwo c #205b8e",
-"bdC c #205b95",
-"bFO c #205b96",
-"azx c #205c96",
-"aDy c #205c97",
-"by7 c #205d96",
-"a.b c #205d97",
-"aa1 c #205e98",
-"acX c #205f99",
-"buc c #20659c",
-"bzs c #20679f",
-"bme c #2068a1",
-"bmc c #206aa3",
-"byk c #206ba4",
-"bC2 c #206ca2",
-"blz c #206ca5",
-"btq c #206ca7",
-".v3 c #212121",
-"bGT c #212223",
-"bER c #212324",
-".80 c #21232e",
-".y0 c #212426",
-"bGM c #21252a",
-".Zj c #212530",
-"bEO c #212629",
-"bIc c #21262b",
-"bYe c #21262c",
-"bGx c #21272b",
-"bEJ c #21272c",
-"bGP c #21272d",
-"bW7 c #21272e",
-"a2b c #212732",
-"bGQ c #21282c",
-"bId c #21282d",
-"bEM c #21282e",
-".Jg c #21282f",
-"bW5 c #212830",
-"aCV c #212834",
-"bEN c #21292e",
-"bEG c #21292f",
-"bNn c #212930",
-"bEw c #212931",
-"bGt c #212932",
-"bAC c #212933",
-"aVT c #212934",
-"bTd c #212a31",
-"bRT c #212a32",
-"bEv c #212a33",
-"#DP c #212a34",
-"#kI c #212b33",
-"#i. c #212b34",
-"bGs c #212b35",
-".Iu c #212c33",
-"acF c #212c34",
-"#jV c #212c35",
-".Qc c #212c36",
-".Sa c #212c37",
-"ato c #212c38",
-".Sd c #212d34",
-".Sl c #212d35",
-".TN c #212d36",
-".OV c #212d37",
-".1B c #212d38",
-"amM c #212d39",
-"aqP c #212d3a",
-".Uj c #212e34",
-".Xy c #212e35",
-".Yc c #212e36",
-".MZ c #212e37",
-".VX c #212e38",
-"#e6 c #212e39",
-"#WK c #212e3a",
-"bGp c #212e3c",
-".OM c #212f37",
-"##k c #212f38",
-"#ew c #212f39",
-"#e8 c #212f3a",
-"az. c #212f3b",
-"buJ c #213038",
-".9P c #213039",
-"#At c #21303a",
-"#Ij c #21303b",
-"asp c #21313a",
-"aAQ c #21313b",
-"aKY c #21313c",
-"atQ c #21323b",
-"aTV c #21323c",
-"#bD c #21323d",
-"aIK c #21333e",
-"#kJ c #213343",
-"#i# c #213344",
-"aQB c #213440",
-"aZf c #213442",
-"aP8 c #213540",
-"brQ c #213649",
-"bwl c #21364a",
-"bIr c #21365a",
-"a3D c #21384d",
-"aYn c #21384e",
-"a0i c #213a4a",
-"bVA c #213a51",
-"#2P c #213d58",
-"bev c #214365",
-"#N7 c #214367",
-"aAJ c #21436b",
-"#MY c #214563",
-"#p# c #214567",
-"#3R c #21465c",
-"bWB c #21466b",
-"aVv c #214672",
-"#1D c #214673",
-"aCx c #214773",
-"buI c #21486e",
-"bzj c #214870",
-"aT2 c #214a6d",
-"br1 c #214a72",
-"buD c #214a73",
-"aJR c #214a77",
-"bg8 c #214b73",
-"bT2 c #214d74",
-"bsx c #214d79",
-"bun c #214e7a",
-"aQP c #214f79",
-"bbz c #214f7c",
-"#GR c #214f7d",
-"aLL c #21507b",
-"#Xt c #21507e",
-"#RL c #21507f",
-"#HG c #215080",
-"aDz c #215181",
-"#Yt c #215282",
-"#Et c #215284",
-".xy c #215352",
-"#WA c #215383",
-"buH c #215385",
-"bxk c #215387",
-"#Ez c #215488",
-"#AD c #215489",
-"#Ev c #215589",
-"#ru c #21558a",
-"#BP c #21558b",
-"bq5 c #215687",
-"bzp c #21568a",
-"#z4 c #21568b",
-"#TK c #21568c",
-"#8y c #215892",
-"bwL c #215991",
-"axg c #215993",
-"aVl c #215995",
-"a6n c #215a94",
-"a4T c #215a95",
-"aXt c #215a96",
-"aW# c #215b95",
-"a2v c #215b96",
-"#eR c #215c95",
-"avo c #215c96",
-"#f0 c #215c97",
-"#7m c #215d96",
-"#bb c #215d97",
-"#8J c #215e98",
-"afK c #215f99",
-"bul c #215f9a",
-"bBa c #21659e",
-"bv. c #21669f",
-"bzo c #2167a0",
-"btp c #2168a1",
-"bxo c #216ba6",
-".sC c #218c8f",
-".pG c #22201e",
-".vt c #222222",
-"bEQ c #222324",
-"bEP c #222325",
-".Zh c #22232f",
-"bEY c #222425",
-"bGR c #222426",
-"bIe c #222528",
-"aGb c #222531",
-"bKa c #222629",
-"bK# c #22262a",
-"bm6 c #222631",
-"bGL c #22272b",
-"bKb c #22272c",
-"bW8 c #22272d",
-"aHo c #222731",
-"bmr c #222732",
-"bNe c #22282c",
-"bGK c #22282d",
-"bOU c #22282e",
-"a0t c #222833",
-"bKv c #22292e",
-"bGk c #22292f",
-"atb c #222933",
-"a6B c #222a31",
-"bUD c #222a32",
-"bTI c #222b30",
-"bGd c #222b32",
-"bVI c #222b33",
-"asX c #222b34",
-"bEu c #222b35",
-"atC c #222c34",
-".UL c #222c35",
-"#TT c #222c36",
-"bEt c #222c37",
-"bQf c #222d35",
-"#nq c #222d36",
-".TP c #222d37",
-".Za c #222d38",
-"anW c #222d39",
-"anX c #222d3a",
-".Se c #222e35",
-".Sk c #222e36",
-".6D c #222e37",
-".P9 c #222e38",
-".0g c #222e39",
-"#SX c #222e3a",
-"#RX c #222e3b",
-".Ui c #222f36",
-"#rz c #222f37",
-".OL c #222f38",
-".X2 c #222f39",
-"#e7 c #222f3a",
-"#RW c #222f3b",
-"#RV c #222f3c",
-"bEq c #222f3d",
-"#Su c #223038",
-"#.E c #223039",
-"#fI c #22303a",
-"#iu c #22303b",
-"#3g c #22303c",
-"bEp c #22303f",
-"aAF c #223040",
-"a0W c #223139",
-"#vD c #22313a",
-"anD c #22313b",
-"aLq c #22313c",
-"a4b c #22313d",
-"#TS c #22313f",
-"bEo c #223140",
-"bGo c #223141",
-"at# c #22323b",
-"#Hs c #22323c",
-"a01 c #22323d",
-"#nr c #223240",
-"bNd c #223241",
-"bEn c #223242",
-"bGV c #223256",
-"a1J c #22333b",
-"a#d c #22333c",
-"aQA c #223340",
-"bEm c #223343",
-"bEl c #223344",
-"bsW c #22343d",
-"bsY c #22343e",
-"bkc c #223440",
-"bEk c #223445",
-"bEj c #223446",
-"bGn c #223447",
-"ba4 c #223542",
-"bEi c #223548",
-"#3b c #223559",
-"a1Z c #223648",
-"#0g c #22364a",
-"beI c #22374a",
-"#9b c #22384c",
-"a2P c #22384d",
-"#5g c #223950",
-"bWh c #223951",
-"bdb c #223a4f",
-"#lH c #223a50",
-"#i7 c #223d56",
-"#UB c #223e50",
-"#QE c #223f54",
-"blt c #223f5b",
-"#0o c #224161",
-"btJ c #224364",
-"bq. c #224466",
-"aky c #224871",
-"#82 c #224a76",
-"bdQ c #224b73",
-"#CX c #224c77",
-"bSH c #224d72",
-"btC c #224d79",
-"bPM c #224e73",
-"bAe c #224e7b",
-"aWn c #22527f",
-"bXa c #225280",
-"#Iz c #225284",
-"bQ4 c #22537e",
-"a5f c #225381",
-"azB c #225385",
-"aVC c #225482",
-"#OZ c #225486",
-"#DY c #225487",
-"#Dr c #225587",
-"#Lb c #225588",
-"#VA c #225589",
-"bsM c #225688",
-"#SQ c #22568a",
-"#Bb c #22568b",
-"#jX c #22568c",
-"#F. c #22578b",
-"#Eu c #22578c",
-"#yP c #22578d",
-"#VB c #22578e",
-"#rv c #22588e",
-"#sm c #22588f",
-"#Kj c #225890",
-"aos c #225892",
-"#7o c #22598f",
-"aun c #225992",
-"#90 c #225993",
-"#5e c #225a92",
-"#Cf c #225a93",
-"atH c #225a94",
-"ayF c #225a95",
-"aa2 c #225b94",
-"#7n c #225b95",
-"aYh c #225b96",
-"br0 c #225c93",
-"aBG c #225c95",
-"ajB c #225c96",
-"bcz c #225c97",
-"bak c #225c98",
-"a#E c #225d96",
-"#g6 c #225d97",
-"aps c #225d98",
-"a#F c #225e97",
-"aBH c #225e98",
-"aqq c #225e99",
-"acM c #226099",
-"bBb c #22609a",
-"aqp c #22609c",
-"byq c #226199",
-"bym c #22629b",
-"byr c #22639a",
-"bwf c #22639c",
-"bys c #22649c",
-"blx c #22649d",
-"bnp c #22649f",
-"bn6 c #22659e",
-"bly c #22669f",
-"bc9 c #2270a7",
-".uT c #232323",
-"##f c #23232e",
-"bEX c #232425",
-"bEZ c #232426",
-"bEU c #232526",
-"aTx c #232631",
-"a5r c #232632",
-"bOV c #23272a",
-"bK. c #23272b",
-"bJ9 c #23282c",
-"bI2 c #23282d",
-"bTc c #23282e",
-"#nb c #232833",
-"a74 c #232834",
-"bGy c #23292d",
-"bGJ c #23292e",
-"bOT c #23292f",
-"bWL c #232930",
-"bAz c #232934",
-"bEd c #232a2f",
-"bHR c #232a30",
-"bWJ c #232a31",
-"bWK c #232b30",
-"bXS c #232b31",
-"bS5 c #232c34",
-"aCn c #232c37",
-"beb c #232c39",
-"#A5 c #232d35",
-"#BB c #232d36",
-"agQ c #232d37",
-"alO c #232d38",
-"#oi c #232e36",
-"#Au c #232e37",
-".42 c #232e38",
-".0h c #232e39",
-"#RY c #232e3a",
-"aD8 c #232e3b",
-".Sf c #232f36",
-".Sg c #232f37",
-".TK c #232f38",
-".TO c #232f39",
-"#hY c #232f3a",
-"#TU c #232f3b",
-".Vn c #233036",
-".Xx c #233037",
-".ZU c #233038",
-"#kw c #233039",
-".9Q c #23303a",
-"#gd c #23303b",
-"#TV c #23303c",
-"#HL c #23303d",
-".5Q c #233139",
-"#mp c #23313a",
-"#jM c #23313b",
-"#Jh c #23313c",
-"#HV c #23313d",
-"bTb c #23313f",
-"a4A c #23323b",
-"#.t c #23323c",
-"#Kw c #23323d",
-"a3G c #23323e",
-"aVt c #23323f",
-"bJ5 c #233240",
-"akw c #233242",
-"aso c #23333c",
-"amu c #23333e",
-"bVO c #233340",
-"#0h c #233342",
-"bN. c #233344",
-"amv c #233440",
-"#38 c #233445",
-".tS c #233535",
-"bsX c #23353e",
-"bqr c #23353f",
-"aXC c #233546",
-"bRM c #233547",
-"#sh c #233559",
-"anL c #233641",
-"#1r c #233647",
-"aZg c #233648",
-"aHH c #233742",
-"bB3 c #233746",
-"#8M c #23385b",
-"bVz c #23394c",
-"#TR c #233950",
-"akx c #233952",
-"#7s c #23395d",
-"bRy c #233a4e",
-"#oj c #233a4f",
-"bus c #233a50",
-"bsQ c #233a51",
-"#20 c #233d57",
-"#TQ c #233f5a",
-"axN c #23405a",
-"#N1 c #234156",
-"#Wv c #234257",
-"#TO c #234363",
-"bt0 c #234466",
-"bcl c #234563",
-"bpp c #234566",
-"a03 c #234570",
-"aRD c #234571",
-"aKC c #234572",
-"#jW c #23476a",
-"#UJ c #23476b",
-"bXH c #23486c",
-"#UI c #23486e",
-"bRs c #234a76",
-"br# c #234e7a",
-"aIE c #234f77",
-"a8y c #235075",
-".rY c #235154",
-"#WD c #23517d",
-"#M2 c #23517f",
-"axR c #23527e",
-"bvH c #235282",
-"#N6 c #235383",
-"aUO c #235481",
-"bsy c #235587",
-"#lI c #235588",
-"aSj c #23558a",
-"#Ju c #235689",
-"bwh c #23568a",
-"byw c #23568b",
-"#L4 c #23578c",
-"#M1 c #23578d",
-"#WC c #23588b",
-"bwg c #23588d",
-"#TJ c #23588e",
-"#VC c #23588f",
-"#mF c #23598f",
-"#g7 c #235990",
-"#RO c #235991",
-"#BR c #235a91",
-"#qH c #235a92",
-"#Az c #235a93",
-"#E7 c #235a94",
-"#6g c #235b93",
-"#yK c #235b94",
-"#ol c #235b95",
-"aY9 c #235b96",
-"#VE c #235c95",
-"#b1 c #235c96",
-"#Jt c #235c97",
-"atI c #235d96",
-"abg c #235d97",
-"azw c #235d98",
-"bt7 c #235e97",
-"arg c #235e99",
-"bbA c #235e9a",
-"byp c #235f97",
-"bxl c #236099",
-"bmd c #23609a",
-"bpu c #23619a",
-"bxp c #23629b",
-"apr c #23649f",
-".w5 c #237d7c",
-".Dt c #242128",
-".sA c #242424",
-"#fH c #242430",
-"bE0 c #242526",
-"bEW c #242527",
-"bEV c #242627",
-"aEZ c #242734",
-"bI1 c #24282c",
-"bJ8 c #24282d",
-"bHU c #24292d",
-"bEh c #24292e",
-"bLv c #24292f",
-"aBN c #242931",
-"bld c #242933",
-"bGz c #242a2f",
-"azq c #242a30",
-"#CM c #242a34",
-"bGA c #242b30",
-"bHY c #242b31",
-"bHV c #242b32",
-"bVZ c #242c32",
-"bIW c #242c33",
-"bVY c #242d33",
-"#NH c #242d34",
-".8Y c #242d35",
-"#GU c #242d36",
-"aw5 c #242e36",
-"#Cm c #242e37",
-".Rk c #242e38",
-"ah7 c #242e39",
-".6b c #242f38",
-".7v c #242f39",
-"#iU c #242f3a",
-"#2R c #242f3b",
-"#Yl c #242f3c",
-".Sh c #243037",
-".Si c #243038",
-".2Q c #243039",
-"#hX c #24303a",
-"#n8 c #24303b",
-"#FR c #24303c",
-"#TW c #24303d",
-".0I c #243137",
-".YC c #243138",
-"#t4 c #243139",
-"#lr c #24313a",
-"#jL c #24313b",
-"#gb c #24313c",
-"#Uz c #24313d",
-"#TX c #24313e",
-".xx c #243232",
-"#qK c #243239",
-"#vW c #24323a",
-"auM c #24323b",
-"#uI c #24323c",
-"#Nc c #24323d",
-"azs c #24323e",
-"#0i c #24323f",
-"#5h c #243240",
-"#2Q c #243241",
-"a6G c #24333e",
-"bY# c #24333f",
-"a07 c #243340",
-"#Cn c #243341",
-"aVL c #24343d",
-"bL. c #243442",
-"a94 c #24353f",
-"bre c #243540",
-"az9 c #243541",
-"#7H c #243546",
-"alP c #243547",
-"bU0 c #243559",
-".xY c #243635",
-"bsZ c #24363f",
-".1i c #243640",
-"aoW c #243641",
-"apU c #243642",
-"ban c #243643",
-"bar c #243842",
-"axM c #243a4d",
-"byU c #243a4f",
-"#Co c #243b51",
-"#F3 c #243e58",
-"bzP c #243f58",
-"#F4 c #243f5a",
-"aXL c #244052",
-"#F5 c #24405c",
-"a15 c #244153",
-"#F2 c #24415e",
-"#TP c #24415f",
-"#Xo c #244358",
-"#mE c #244362",
-"br. c #244363",
-"bxg c #244566",
-"bnk c #244567",
-"#Cp c #244769",
-"#F1 c #24476a",
-"bWA c #24486c",
-"aCe c #24496d",
-"buC c #24496f",
-"bwk c #244970",
-"azu c #244a70",
-"b#K c #244a71",
-"#BL c #244b71",
-"#ns c #244d76",
-"#ia c #244f7b",
-"#kK c #24507d",
-"bSE c #245176",
-"#BM c #24517d",
-"aQO c #24527a",
-"bah c #24537c",
-"#LZ c #24537f",
-"aBF c #245382",
-"bC3 c #245477",
-"bra c #245485",
-"bfN c #245585",
-"#UH c #245689",
-"bcA c #24568a",
-"bum c #245789",
-"bvI c #24578a",
-"#i8 c #24588d",
-"btB c #24598d",
-"bsz c #24598f",
-"#HF c #245990",
-"byn c #245991",
-"ayG c #245a8f",
-"bwi c #245a90",
-"#Zg c #245a91",
-"#VD c #245a92",
-"bdi c #245a93",
-"#nt c #245b93",
-"#yO c #245b94",
-"#zs c #245b95",
-"#Cw c #245c94",
-"#ba c #245c95",
-"#cQ c #245c96",
-"#91 c #245d95",
-"#AA c #245d96",
-"#ib c #245d97",
-"#B. c #245d98",
-"byo c #245e95",
-"bwt c #245e97",
-"#VF c #245e98",
-"#pY c #245e99",
-"#6f c #245f98",
-"bmO c #245f99",
-"bAi c #246099",
-"bud c #24619b",
-"aor c #24649d",
-".v6 c #24b6b5",
-".B4 c #251a24",
-".re c #252525",
-"bcE c #252530",
-"aF8 c #252531",
-"au5 c #252532",
-"bPp c #252627",
-"bTT c #252628",
-"bIf c #252728",
-"bX# c #252729",
-"bGm c #25292e",
-"blb c #252934",
-"a8S c #252935",
-"bEg c #252a2f",
-"bJ7 c #252a30",
-"#sP c #252a34",
-"bEf c #252b30",
-"bEe c #252b31",
-"a51 c #252b35",
-"bNv c #252c30",
-"bGl c #252c31",
-"bHS c #252c32",
-"bIX c #252c33",
-"bLD c #252d31",
-"bGI c #252d32",
-"bI4 c #252d33",
-"bGH c #252e33",
-"#MI c #252e35",
-"bLu c #252e36",
-"aol c #252e37",
-".84 c #252e38",
-".6r c #252e39",
-"ayK c #252f36",
-"#Kb c #252f37",
-"an. c #252f38",
-"#1s c #252f39",
-"#gX c #252f3a",
-"axj c #253038",
-"ax2 c #253039",
-".Q8 c #25303a",
-"#4. c #25303b",
-"#Vu c #25303c",
-"#Vt c #25303d",
-".Sj c #253138",
-".T2 c #253139",
-".6E c #25313a",
-"#wh c #25313b",
-"#E2 c #25313c",
-"#Uy c #25313d",
-"#4# c #25313e",
-".Wz c #253239",
-"#ja c #25323a",
-"#tG c #25323b",
-"#sM c #25323c",
-"#ge c #25323d",
-"#TC c #25323e",
-"#0j c #25323f",
-"#yS c #25333b",
-"#EU c #25333c",
-"#Ef c #25333d",
-"#Cl c #25333e",
-"aBs c #25333f",
-"aCd c #253340",
-"bks c #25343e",
-"#G4 c #25343f",
-"beJ c #253440",
-"abE c #253442",
-"#rp c #253452",
-"#Ao c #25353e",
-"bjr c #25353f",
-"#aO c #253540",
-"amw c #253541",
-"#kr c #253640",
-"aMR c #253641",
-"apT c #253642",
-"aGq c #253643",
-"brR c #253646",
-"bmX c #253740",
-"#n0 c #253741",
-"aD4 c #253743",
-"aD6 c #253744",
-"bWV c #253745",
-"azG c #253747",
-"#UK c #253749",
-".sB c #253839",
-"amA c #253843",
-"aMS c #253844",
-"bX1 c #253848",
-"#1z c #253b51",
-"aVu c #253b59",
-"ayJ c #253c51",
-"aBE c #253c52",
-"azt c #253c53",
-"#0n c #253d56",
-"ax1 c #253e54",
-"bq9 c #253f58",
-"#1d c #254253",
-"a12 c #254254",
-"aAn c #254361",
-"aYr c #25436b",
-"#Vv c #254457",
-"#Cj c #254565",
-"aNK c #254570",
-"aLC c #254571",
-"btf c #254667",
-"bu4 c #254668",
-"bg3 c #254767",
-"axi c #25486c",
-"#WE c #25496b",
-"aqo c #25496d",
-"ax0 c #254a6e",
-"bbN c #254d75",
-"ajA c #254d78",
-"bA5 c #254f78",
-"#F0 c #254f79",
-"bAd c #25517d",
-"bbM c #25517e",
-"aVz c #255278",
-"bfO c #25527e",
-"#VH c #25527f",
-"awC c #255280",
-"#Cq c #255381",
-"bbB c #255382",
-"aAo c #255383",
-"bV5 c #25547c",
-"#Ci c #255482",
-"ayI c #255484",
-"#ok c #255687",
-"bsB c #255688",
-"aA0 c #255689",
-"axh c #255788",
-"baT c #255789",
-"bwj c #25578a",
-"aPX c #255886",
-"#BN c #25588b",
-"#N5 c #25588c",
-"#5d c #25588e",
-"brb c #25598d",
-"bC0 c #25598e",
-"azA c #25598f",
-"axZ c #255a8e",
-"#VG c #255a91",
-"bxr c #255a92",
-"ayH c #255b91",
-"bxn c #255b92",
-"bsA c #255b93",
-"#Xx c #255b94",
-"byl c #255b95",
-"#pa c #255c93",
-"bxq c #255c94",
-"#BS c #255c95",
-"#Yu c #255c96",
-"#C0 c #255d94",
-"#b2 c #255d95",
-"#cR c #255d96",
-"#zt c #255d97",
-"bxm c #255d98",
-"akz c #255e96",
-"#f1 c #255e97",
-"#f2 c #255e98",
-"#B# c #255e99",
-"bsU c #255e9a",
-"#qI c #255f99",
-"#F# c #255f9a",
-"#FW c #255f9b",
-".us c #25f4f4",
-"a6# c #262331",
-"aSI c #262431",
-"aUZ c #262531",
-"#o3 c #262532",
-".pW c #262626",
-"bJo c #262728",
-"aWt c #262732",
-"bpN c #262733",
-"bGC c #262c30",
-"bGB c #262c31",
-"bHT c #262c32",
-"bI0 c #262c33",
-"bGD c #262d31",
-"bIb c #262d32",
-"bIZ c #262d33",
-"bIY c #262d34",
-"bLE c #262e32",
-"bGE c #262e33",
-"bNu c #262e34",
-"bIV c #262e35",
-"bJ6 c #262e36",
-"aMf c #262e37",
-".4g c #262e39",
-"bGF c #262f34",
-"bO3 c #262f35",
-"bNt c #262f36",
-"bJ2 c #262f37",
-"#39 c #262f39",
-".OJ c #262f3a",
-".vv c #263030",
-"bQt c #263036",
-"bR4 c #263037",
-"bVX c #263038",
-"a.B c #263039",
-"#ey c #26303a",
-"avc c #26303b",
-"aHM c #26303c",
-"bNo c #263138",
-"#Kc c #263139",
-"#z6 c #26313a",
-"aok c #26313b",
-"aig c #26313c",
-"#K0 c #26313d",
-"bQe c #263238",
-".Td c #263239",
-".S6 c #26323a",
-"#bc c #26323b",
-"#DN c #26323c",
-"#wR c #26323d",
-"#K1 c #26323e",
-"#UL c #26323f",
-".YB c #26333a",
-"#dN c #26333b",
-"#IH c #26333c",
-"#xm c #26333d",
-"#f# c #26333e",
-"#2S c #26333f",
-"#7I c #263340",
-"#tb c #26343c",
-"#Hr c #26343d",
-"#Hq c #26343e",
-"agR c #26343f",
-"anE c #263440",
-"ayj c #263441",
-"aMe c #26353e",
-"bCr c #26353f",
-"aAa c #263540",
-"bhp c #263541",
-"#0k c #263542",
-"bVc c #263543",
-"bjd c #263544",
-"biY c #263640",
-"bpC c #263641",
-"amx c #263642",
-"#A6 c #263643",
-"blL c #263740",
-"bDi c #263741",
-"anK c #263742",
-"bcy c #263743",
-"#5m c #263744",
-"azH c #263748",
-"#0l c #263749",
-"bXd c #26375b",
-"amz c #263843",
-"aD7 c #263844",
-"aPc c #263845",
-"bcB c #263848",
-"awg c #263943",
-"bVd c #263a47",
-"bOC c #263a4d",
-"aqn c #263a4e",
-"#Ck c #263b4c",
-"bcn c #263b4e",
-"#0m c #263b50",
-"apn c #263c52",
-"bc2 c #263e56",
-"b#b c #26405a",
-"aRI c #26416c",
-"btI c #264360",
-"aBi c #26436c",
-"bxG c #264370",
-"aBU c #264471",
-"bmD c #264666",
-"aws c #26475b",
-"#OT c #26475e",
-"bqT c #264768",
-"#HA c #264860",
-"#RH c #264960",
-"byi c #26496c",
-"bXG c #264a6e",
-"#VI c #264a6f",
-"#BK c #264d72",
-"bue c #264e75",
-"bzO c #264f72",
-"bcp c #264f77",
-"bsC c #26517c",
-"bA7 c #26517d",
-"bd4 c #26517e",
-"btu c #26527f",
-"bvn c #265280",
-".xZ c #265351",
-"#tX c #265378",
-"bAh c #265380",
-"bsV c #265382",
-"#wv c #265479",
-"bxL c #26557d",
-"aot c #26558e",
-"#wp c #26567e",
-"bLc c #265686",
-"bMJ c #265782",
-"azv c #265789",
-"aRr c #26578d",
-"aO1 c #265885",
-"bJT c #26588a",
-"#NK c #26588c",
-"ayE c #26598c",
-"bIH c #26598d",
-"a34 c #265b94",
-"brc c #265c92",
-"#N4 c #265c93",
-"a.a c #265c94",
-"bU6 c #265c95",
-"bvx c #265d94",
-"#Ch c #265d95",
-"aWS c #265e95",
-"#Cr c #265e96",
-"#BO c #265e97",
-"#yJ c #265f97",
-"#46 c #265f98",
-"#Cg c #265f99",
-"#8x c #26609a",
-"as1 c #26619b",
-"aa3 c #26639b",
-".wA c #26a09f",
-".oV c #272522",
-"aU3 c #272531",
-".yK c #272626",
-".yL c #272627",
-".mY c #272727",
-"bL5 c #272829",
-".yz c #272929",
-"bUX c #27292c",
-"aHn c #272934",
-"a4B c #272936",
-"bL1 c #272a2d",
-"bvO c #272a35",
-"#qp c #272b35",
-".yA c #272c2b",
-"bV4 c #272c2f",
-"bYf c #272e31",
-"bKu c #272e33",
-"aih c #272e3b",
-".wy c #272f2f",
-"bIa c #272f33",
-"bGG c #272f34",
-"bJ4 c #272f36",
-"aI0 c #272f37",
-"bLp c #272f38",
-"aLs c #272f3b",
-"bLC c #273035",
-"bIU c #273038",
-"#FK c #273039",
-"aBr c #27303c",
-".yB c #273130",
-"bUU c #273137",
-"bO2 c #273138",
-"##e c #273139",
-"bRL c #27313a",
-"auc c #27313c",
-"#KX c #273239",
-"bUT c #27323a",
-"bVW c #27323b",
-"aCU c #27323c",
-"#FN c #27323d",
-"#Jg c #27323e",
-"#5i c #27323f",
-"bOP c #273339",
-".Tc c #27333a",
-".Xw c #27333b",
-"#ak c #27333c",
-"#ET c #27333d",
-"#GD c #27333e",
-"#NU c #27333f",
-"#1t c #273340",
-"bE2 c #273357",
-".Vm c #27343a",
-".1a c #27343b",
-"#jZ c #27343c",
-"#t5 c #27343d",
-"#Ee c #27343e",
-"#ga c #27343f",
-"akv c #273440",
-"#2T c #273441",
-"brV c #27353e",
-"#3l c #27353f",
-"#9I c #273540",
-"alN c #273541",
-"ay9 c #273542",
-"bae c #273543",
-"bHk c #273558",
-"a0n c #27363f",
-"bih c #273640",
-"bk4 c #273641",
-"aTl c #273642",
-"biS c #273740",
-"aoV c #273742",
-"#4b c #273743",
-"aD5 c #273744",
-"bP2 c #273746",
-"aAM c #273748",
-".rK c #27374b",
-"#6y c #273844",
-"bY. c #273845",
-"aPb c #273945",
-"#A7 c #273946",
-"#DV c #273947",
-"bVy c #27394a",
-"bsP c #273a4b",
-"aWi c #273a56",
-"bq8 c #273b4e",
-"bcm c #273c4f",
-"#1G c #273c64",
-".yi c #27403e",
-"bfP c #274159",
-"aik c #274356",
-"arf c #27435d",
-"#VJ c #274360",
-"#Yy c #27436e",
-"aC3 c #27436f",
-"aW1 c #274470",
-"bJS c #274564",
-"aw7 c #274759",
-"a2R c #27475a",
-"#Yn c #27485e",
-"byT c #274865",
-"bpo c #274869",
-"bsD c #27486a",
-"#Z# c #27495e",
-"bnj c #27496a",
-"#Kd c #274a62",
-"apo c #274a6c",
-"bB4 c #274a6d",
-"bdj c #274a6f",
-"#88 c #274b77",
-"#2F c #274c64",
-"bal c #274c71",
-"bco c #274c72",
-"bwu c #274d73",
-"bRz c #274e76",
-"bP8 c #274e77",
-"bzi c #274f77",
-"bOG c #274f79",
-"brd c #27507a",
-"aOE c #275081",
-"bMY c #27527f",
-"#vO c #27557a",
-"br5 c #275585",
-"aTM c #27568b",
-"a5g c #275781",
-"aYf c #275892",
-"aO3 c #275986",
-"aYy c #275a87",
-"aKP c #275a88",
-"byt c #275a8f",
-"auo c #275a93",
-"bxH c #275b94",
-"#1o c #275d90",
-"#BI c #275d91",
-"a37 c #275e96",
-"bCh c #275f98",
-"bCg c #276098",
-"#yN c #276099",
-"#7d c #27609a",
-"aPU c #27618f",
-"#3Y c #27619a",
-"bE6 c #27639b",
-"aRL c #282432",
-"#n6 c #282531",
-".yg c #282727",
-"aLY c #282733",
-".mf c #282828",
-".xV c #282829",
-"aWv c #282834",
-".yx c #282928",
-".xW c #282929",
-".yh c #282a2a",
-"bMo c #282a2b",
-"aC1 c #282a37",
-".xX c #282b2b",
-"avz c #282c36",
-".5j c #282e3a",
-"azc c #282f3b",
-"bV0 c #283034",
-"bI# c #283035",
-"bOQ c #283038",
-"aGp c #28303c",
-"bW9 c #283135",
-"bHZ c #283137",
-"bJ3 c #283139",
-"bLo c #28313a",
-"bIG c #28313b",
-"awn c #28313c",
-"aDl c #28313d",
-"bLB c #283237",
-"bKi c #283239",
-"bF1 c #28323b",
-"axF c #28323c",
-"bDY c #283339",
-"bLm c #28333a",
-"bS3 c #28333b",
-"bJR c #28333c",
-"#Jf c #28333d",
-"#7T c #28333e",
-"a#5 c #283341",
-"bHN c #28343a",
-".S7 c #28343b",
-".6F c #28343c",
-".71 c #28343d",
-"#qV c #28343e",
-"#4U c #28343f",
-"#UM c #283440",
-"a.C c #283442",
-".Wy c #28353b",
-".ZT c #28353c",
-"#u8 c #28353d",
-"#MT c #28353e",
-"#tk c #28353f",
-"#2v c #283540",
-"#UN c #283541",
-"#UO c #283542",
-"#UP c #283543",
-"bFt c #283554",
-".yC c #283634",
-"aHP c #28363f",
-"big c #283640",
-"#q. c #283641",
-"aDe c #283642",
-"aB6 c #283643",
-"b.A c #283645",
-"biZ c #283740",
-"afA c #283742",
-"a7S c #283743",
-"#5l c #283744",
-"#VL c #283746",
-"amy c #283843",
-"#9d c #283844",
-"bj4 c #283848",
-"bCq c #283943",
-"### c #283944",
-"aMQ c #283945",
-"an# c #283947",
-"#qk c #283a45",
-"#E3 c #283a49",
-"auB c #283c47",
-"bP7 c #283c50",
-"#VK c #283c51",
-"bOF c #283c52",
-"bMX c #283e54",
-"#3. c #283e66",
-"btH c #284055",
-"bLb c #28415c",
-"bBB c #284250",
-"beT c #28445f",
-"aMw c #28446f",
-"aT0 c #284470",
-"aVE c #284658",
-"bfj c #284867",
-"bn1 c #28496a",
-"bWz c #284b6f",
-"aUP c #284e6b",
-"aNM c #28507c",
-"bRh c #28567c",
-"apt c #28568d",
-"bAv c #28577f",
-"bh# c #285781",
-"aUC c #285992",
-"acW c #285a92",
-"aE5 c #285b89",
-"aW4 c #285c8a",
-"ail c #286092",
-"#Ay c #286199",
-"axY c #28619a",
-"apq c #28659c",
-"##8 c #292330",
-"b.a c #292331",
-"bke c #292431",
-"aZA c #292632",
-"aXU c #292633",
-".xw c #292828",
-".xt c #292829",
-".jk c #292929",
-".w2 c #292a2a",
-"aA8 c #292a33",
-".yJ c #292b2a",
-".Q4 c #292b35",
-"ayk c #292c36",
-"aEN c #292c38",
-".w3 c #292d2d",
-"#x2 c #292e3a",
-"aAP c #292f3a",
-"bX. c #293034",
-"#bF c #29303b",
-"aJi c #29303c",
-"bKt c #293135",
-"bI. c #293136",
-".Nb c #29313b",
-".2L c #29313c",
-"bH9 c #293236",
-"bH8 c #293237",
-"aBx c #29323d",
-"aE# c #29323e",
-"bNs c #293339",
-"bJ1 c #29333b",
-"aGY c #29333e",
-"bGb c #29343b",
-"bS4 c #29343c",
-"abV c #29343e",
-"#VN c #29343f",
-"aRT c #293440",
-".S8 c #29353c",
-"#b3 c #29353d",
-"#eS c #29353e",
-"#MU c #29353f",
-"#Zw c #293540",
-"#VM c #293541",
-"anF c #293542",
-"aAG c #29354d",
-".Vl c #29363c",
-".Xv c #29363d",
-"#wx c #29363e",
-"#sz c #29363f",
-"#hv c #293640",
-"#qR c #293641",
-"#1u c #293642",
-"#1v c #293643",
-"#2Z c #293644",
-"baV c #293740",
-"a92 c #293741",
-"ats c #293742",
-"aoX c #293743",
-"anG c #293744",
-"#7L c #293745",
-"bsb c #293842",
-"aPa c #293843",
-"#6x c #293844",
-"aN5 c #293845",
-"bAg c #293846",
-"brS c #293847",
-"aN4 c #293944",
-"aUR c #293945",
-"aSD c #293946",
-"bW4 c #293a45",
-"btG c #293b4b",
-"bFl c #293b51",
-"bVP c #293c4a",
-"bC4 c #293c4d",
-"br6 c #293d50",
-"bcq c #293e52",
-"bbO c #293e53",
-".qA c #293f41",
-"bsE c #293f54",
-"btv c #29425a",
-"aXD c #294370",
-"aBX c #294471",
-"alQ c #294764",
-".sp c #29476e",
-"#Wy c #294858",
-"avj c #29495b",
-"brE c #29496a",
-".so c #294a74",
-"bOA c #294b76",
-"bUf c #294c77",
-"bAc c #29547e",
-"aCR c #295584",
-"byL c #295885",
-"afL c #295990",
-"bOo c #295a83",
-"app c #295a89",
-"asa c #295b8b",
-"bDz c #295b93",
-"#ya c #296195",
-"#K6 c #296197",
-"bIi c #29629a",
-"aVm c #296397",
-"ah. c #29669d",
-".zR c #2a2324",
-"b#f c #2a2331",
-"#.y c #2a2430",
-".aO c #2a2525",
-"a1# c #2a2532",
-".ai c #2a2626",
-".wx c #2a2929",
-".xu c #2a292a",
-".hV c #2a2a2a",
-".vs c #2a2a2b",
-"#lo c #2a2a35",
-".uV c #2a2e2e",
-"bnI c #2a2e3b",
-"aRB c #2a2f3c",
-".yI c #2a302d",
-"aJj c #2a303c",
-"aZ8 c #2a303d",
-".CQ c #2a3135",
-"aLa c #2a313d",
-"bH7 c #2a3237",
-"#jN c #2a323d",
-"aKq c #2a323e",
-"bH6 c #2a3337",
-"bH5 c #2a3338",
-".Z. c #2a333c",
-"ayy c #2a333e",
-"adg c #2a3342",
-"bH4 c #2a3438",
-"bI8 c #2a3439",
-"bH0 c #2a343a",
-"bUu c #2a343c",
-"#tF c #2a343e",
-".S9 c #2a353d",
-"bqq c #2a353e",
-"#Ii c #2a353f",
-"bfQ c #2a3540",
-".26 c #2a363c",
-".T. c #2a363d",
-"#jb c #2a363e",
-"#bd c #2a363f",
-"#OS c #2a3640",
-"#vc c #2a3641",
-"ba5 c #2a3642",
-"#4a c #2a3643",
-"#5k c #2a3644",
-"#6v c #2a3645",
-".1# c #2a373e",
-"#tc c #2a373f",
-"#MS c #2a3740",
-"aad c #2a3741",
-"#pj c #2a3742",
-"#2U c #2a3743",
-"#2V c #2a3744",
-"#1y c #2a3745",
-"b#c c #2a3841",
-"bC6 c #2a3842",
-"#7Y c #2a3843",
-"#5j c #2a3844",
-"#6w c #2a3845",
-"a4c c #2a3846",
-"a4a c #2a3941",
-"aoU c #2a3943",
-"apS c #2a3944",
-"aqJ c #2a3945",
-"aTm c #2a3946",
-"baU c #2a3947",
-"b#L c #2a3948",
-"boW c #2a3a44",
-"buf c #2a3a49",
-"bqn c #2a3b4a",
-"bqp c #2a3b4b",
-"bqo c #2a3c4c",
-"a7k c #2a3d49",
-"a0# c #2a3d60",
-"axx c #2a3e48",
-"bUg c #2a3e4b",
-"#CS c #2a4254",
-"aWj c #2a426e",
-"aRF c #2a436e",
-"a05 c #2a436f",
-"bqS c #2a4a6b",
-"buB c #2a4b6c",
-"bXF c #2a4d71",
-"a09 c #2a4e65",
-"a1P c #2a4e66",
-"avU c #2a4f66",
-"#QI c #2a4f69",
-"#1m c #2a516c",
-"bVj c #2a547e",
-"bA4 c #2a5580",
-"#uZ c #2a5a7f",
-"#BJ c #2a5a84",
-"bBM c #2a5b93",
-"bxU c #2a5d87",
-"bAP c #2a5d95",
-"baF c #2a6198",
-"bCf c #2a649b",
-"aum c #2a649d",
-"#zr c #2a659c",
-"bSx c #2a659d",
-"a#s c #2a669d",
-"bKZ c #2a6b9d",
-".z6 c #2b101a",
-".#p c #2b1f1f",
-".bm c #2b211f",
-".cw c #2b2220",
-"#cB c #2b2231",
-"a0j c #2b2432",
-".bl c #2b2526",
-"aT4 c #2b2532",
-"aQZ c #2b2533",
-".#X c #2b2626",
-"aUU c #2b2834",
-".vu c #2b2a2a",
-"#oT c #2b2a36",
-".gq c #2b2b2b",
-"a98 c #2b2b36",
-".xs c #2b2c2b",
-"bTP c #2b2c2d",
-"aHm c #2b2c3a",
-"bOf c #2b2d2e",
-"aB7 c #2b2d37",
-".yw c #2b2e2b",
-"#hP c #2b2e3a",
-"aJh c #2b303d",
-"bqH c #2b313a",
-"bwW c #2b313b",
-"#fT c #2b313c",
-"aLZ c #2b313d",
-"bS. c #2b3237",
-"aI3 c #2b323a",
-"aMP c #2b333f",
-".rg c #2b3435",
-"bH3 c #2b3439",
-"auJ c #2b343e",
-"azj c #2b343f",
-"aAX c #2b3440",
-".yH c #2b3531",
-"bKl c #2b3539",
-"bH2 c #2b353a",
-"#ye c #2b353f",
-"anB c #2b3540",
-"bH1 c #2b363b",
-"bI7 c #2b363c",
-".Ta c #2b363d",
-".Tb c #2b363e",
-"#yT c #2b363f",
-"#Hp c #2b3640",
-"bC5 c #2b3641",
-"#1x c #2b3642",
-".Wx c #2b373d",
-".T# c #2b373e",
-".6G c #2b373f",
-"#NT c #2b3740",
-"#Z4 c #2b3741",
-"#1w c #2b3742",
-"#2Y c #2b3743",
-"#2W c #2b3744",
-"#7J c #2b3745",
-".YA c #2b383f",
-"#t6 c #2b3840",
-"#u9 c #2b3841",
-"#yY c #2b3842",
-"#HZ c #2b3843",
-"#2X c #2b3844",
-"#7K c #2b3845",
-"aqI c #2b3846",
-"bNp c #2b3941",
-"#4A c #2b3943",
-"aoT c #2b3944",
-"anJ c #2b3945",
-"anH c #2b3946",
-"a7J c #2b3947",
-".y6 c #2b3a45",
-"amB c #2b3a46",
-"axq c #2b3a47",
-".yD c #2b3b38",
-"au. c #2b3b46",
-"baQ c #2b3b47",
-"bW3 c #2b3c48",
-"bFk c #2b3d55",
-"bVQ c #2b404e",
-"bWW c #2b4050",
-"aPS c #2b406b",
-".SG c #2b414c",
-"#1E c #2b416b",
-"aTb c #2b4370",
-"bXq c #2b4871",
-"bp9 c #2b4b6c",
-"a7a c #2b4c5c",
-"bB2 c #2b4e71",
-"a7I c #2b4f67",
-"aou c #2b528a",
-"ayD c #2b5681",
-"bzu c #2b5987",
-"#vT c #2b5b81",
-"#1n c #2b5b83",
-"bMP c #2b5b93",
-"bw4 c #2b5d95",
-"bC. c #2b5f88",
-"#uX c #2b5f89",
-"aFw c #2b6098",
-"a4U c #2b6197",
-"bG0 c #2b659d",
-"#yL c #2b669d",
-".z7 c #2c0e19",
-"Qt0 c #2c2020",
-".aj c #2c2121",
-".c9 c #2c2321",
-".6m c #2c2331",
-".0q c #2c2332",
-"bpz c #2c2432",
-".Zi c #2c2734",
-"aHu c #2c2735",
-"blO c #2c2936",
-".uU c #2c2a2a",
-"#ml c #2c2a36",
-".uo c #2c2b2b",
-".dz c #2c2c2c",
-".tR c #2c2c2d",
-"aCT c #2c2c35",
-"#gP c #2c2c37",
-".up c #2c2d2d",
-"bQM c #2c2d2e",
-"aEL c #2c2d39",
-"aDg c #2c2e37",
-"aJ1 c #2c2f3c",
-"aHX c #2c3138",
-"#vF c #2c313c",
-"aL4 c #2c313e",
-".7E c #2c333b",
-"aOL c #2c333f",
-"bPg c #2c3439",
-"azh c #2c343f",
-"aGZ c #2c3440",
-"avM c #2c353f",
-"aC. c #2c3540",
-"bpy c #2c3641",
-"#4c c #2c3642",
-"bKk c #2c373d",
-".U2 c #2c373e",
-".T3 c #2c373f",
-"##t c #2c3740",
-"#GB c #2c3741",
-"aA. c #2c3743",
-"adl c #2c3744",
-"adk c #2c3745",
-"#9c c #2c3746",
-"#49 c #2c375b",
-"bI6 c #2c383d",
-".Vk c #2c383e",
-".LA c #2c383f",
-".9# c #2c3840",
-"#vX c #2c3841",
-"#TB c #2c3842",
-"#1c c #2c3843",
-"aA# c #2c3844",
-"axr c #2c3845",
-"a.D c #2c3846",
-".Ww c #2c393f",
-"#pc c #2c3940",
-"#B5 c #2c3941",
-"byv c #2c3942",
-"#AI c #2c3943",
-"#R6 c #2c3944",
-"aoS c #2c3945",
-"aqH c #2c3946",
-"bcd c #2c3947",
-"buY c #2c3a43",
-"aVW c #2c3a44",
-"#q# c #2c3a45",
-"anI c #2c3a46",
-"atR c #2c3a47",
-"bUS c #2c3d47",
-"a7b c #2c3d48",
-"#Q5 c #2c3d4a",
-".yE c #2c3e39",
-"byu c #2c3e4e",
-"bUL c #2c404d",
-".VI c #2c424d",
-"bWY c #2c4251",
-"aWZ c #2c426d",
-"#22 c #2c426e",
-"bX2 c #2c4354",
-"aKx c #2c436e",
-"#BC c #2c4456",
-"bpn c #2c4c6c",
-"bn0 c #2c4c6d",
-"#0# c #2c4d5d",
-"bWy c #2c4f73",
-"bHE c #2c5176",
-"#2I c #2c536d",
-"#PJ c #2c536f",
-"aa0 c #2c568d",
-".AA c #2c5774",
-"acY c #2c5890",
-"bzZ c #2c5a92",
-"bOm c #2c5c81",
-"bPQ c #2c5e83",
-"aHA c #2c5e88",
-"bxM c #2c608a",
-"aNT c #2c608e",
-"#vM c #2c618b",
-"#Eo c #2c679e",
-"aoq c #2c6ea7",
-"QtH c #2d2222",
-"#bN c #2d2331",
-"aF7 c #2d2334",
-".dO c #2d2421",
-"a16 c #2d2533",
-"aZz c #2d2633",
-".#o c #2d2828",
-"bqx c #2d2835",
-"a1g c #2d2937",
-"#ty c #2d2a36",
-"aQ0 c #2d2a38",
-".te c #2d2c2c",
-".w1 c #2d2d2b",
-".vr c #2d2d2c",
-".cW c #2d2d2d",
-".td c #2d2d2e",
-"bPq c #2d2e2f",
-"bTY c #2d2e30",
-".Hh c #2d2e34",
-"aAO c #2d2e3a",
-".87 c #2d2e3b",
-".xU c #2d2f2a",
-".tf c #2d2f30",
-"a0Q c #2d2f39",
-"aCF c #2d2f3c",
-"aS7 c #2d2f3d",
-"aJg c #2d303d",
-"a52 c #2d303e",
-"#zi c #2d313d",
-"bfR c #2d313e",
-"aAN c #2d313f",
-".UQ c #2d323d",
-"aR# c #2d323e",
-"aGk c #2d3458",
-"bNI c #2d3539",
-".UV c #2d353e",
-"avO c #2d3640",
-"aCK c #2d3641",
-"#6z c #2d3644",
-"awX c #2d3741",
-"bpD c #2d3743",
-"adj c #2d3744",
-"aFe c #2d3745",
-"bDX c #2d383e",
-".T4 c #2d383f",
-".V7 c #2d3840",
-"#b4 c #2d3841",
-"#55 c #2d3842",
-"bFm c #2d384d",
-".Zk c #2d393f",
-".Uh c #2d3940",
-"#eT c #2d3941",
-"#td c #2d3942",
-"#NS c #2d3943",
-"#e3 c #2d3944",
-".yG c #2d3a36",
-"bI5 c #2d3a40",
-"#p1 c #2d3a41",
-"#CI c #2d3a42",
-"#4s c #2d3a43",
-"am5 c #2d3a44",
-"#ht c #2d3a45",
-"a6H c #2d3a46",
-"a9W c #2d3a47",
-"bcZ c #2d3a48",
-"aNe c #2d3b43",
-"bqm c #2d3b44",
-"bAm c #2d3b45",
-"a8t c #2d3b47",
-"a6v c #2d3b48",
-"aAH c #2d3b5c",
-"aLg c #2d3c45",
-"axL c #2d3c49",
-"bkI c #2d3c4c",
-"bFj c #2d3c54",
-".yF c #2d3d39",
-"a9b c #2d3d46",
-"bQq c #2d3d47",
-"#P# c #2d3f4c",
-"atD c #2d404d",
-"aAI c #2d4069",
-"a8V c #2d414c",
-"bX9 c #2d414e",
-"as. c #2d424f",
-"bX5 c #2d4352",
-"bWX c #2d4453",
-"bl3 c #2d445a",
-"awD c #2d4460",
-"#El c #2d4559",
-"aij c #2d485b",
-"bAl c #2d4a67",
-"btZ c #2d4d6d",
-"#K2 c #2d5672",
-"bzh c #2d567f",
-"bq4 c #2d588f",
-"#Xy c #2d5890",
-"bOw c #2d5a92",
-"bOn c #2d5e83",
-"aWm c #2d6089",
-"b.o c #2d6096",
-"aVA c #2d618c",
-"aGi c #2d618e",
-"#xv c #2d628c",
-"a5K c #2d6298",
-"bhc c #2d6492",
-"#yM c #2d689f",
-"bOg c #2d699f",
-"awB c #2d69a0",
-"#2L c #2d69a2",
-".bV c #2e2322",
-".eu c #2e2423",
-"a5i c #2e2433",
-"aUY c #2e2633",
-"aVO c #2e2635",
-"bvL c #2e2936",
-"aXT c #2e2b38",
-"a5m c #2e2c3b",
-".rW c #2e2d2d",
-".v2 c #2e2e2b",
-".tQ c #2e2e2d",
-".aE c #2e2e2e",
-"#lG c #2e2e3b",
-"a8R c #2e2e3c",
-".rd c #2e2f2f",
-"bTL c #2e2f30",
-".6s c #2e2f3b",
-"aHG c #2e2f3c",
-"aS8 c #2e2f3d",
-".rX c #2e3030",
-".zo c #2e3031",
-"#Df c #2e303b",
-"aJk c #2e303d",
-"atP c #2e333f",
-"aHl c #2e3343",
-"bgy c #2e353d",
-"ams c #2e3540",
-"aQC c #2e3543",
-"##7 c #2e363f",
-"boO c #2e3741",
-"aEa c #2e3742",
-"a.E c #2e3744",
-".Gs c #2e383e",
-"bUt c #2e383f",
-"bGa c #2e393f",
-".T5 c #2e3940",
-"#hb c #2e3942",
-"bgE c #2e3947",
-".Vj c #2e3a40",
-".T6 c #2e3a41",
-".6H c #2e3a42",
-"#t7 c #2e3a43",
-"#Xm c #2e3a44",
-"#1R c #2e3a45",
-".Wv c #2e3b41",
-"#qL c #2e3b42",
-"#De c #2e3b43",
-"#2b c #2e3b44",
-"#DC c #2e3b45",
-"aBu c #2e3b46",
-"bcY c #2e3b47",
-"bFi c #2e3b52",
-"bKj c #2e3c42",
-"bNr c #2e3c43",
-"#9p c #2e3c47",
-"a7M c #2e3c48",
-"bdK c #2e3c49",
-"bLA c #2e3d43",
-"bTq c #2e3d44",
-"by0 c #2e3d48",
-"#1F c #2e3d66",
-"bTp c #2e3e46",
-"bRX c #2e3f49",
-"bf9 c #2e3f4d",
-"bVV c #2e404b",
-"#S2 c #2e404d",
-"bTj c #2e414c",
-"bW2 c #2e414e",
-"bX4 c #2e4555",
-"bX3 c #2e4657",
-"a.z c #2e4768",
-"#Ca c #2e4b60",
-"bzR c #2e4c5b",
-"#83 c #2e4d78",
-"brD c #2e4e6e",
-"bxs c #2e558c",
-"asb c #2e568c",
-"bRB c #2e5780",
-"a3g c #2e5890",
-".yj c #2e5956",
-"aQq c #2e5990",
-"#3S c #2e5a77",
-"bU7 c #2e5e95",
-"azy c #2e6098",
-"bRc c #2e6186",
-"bfZ c #2e6294",
-"bzN c #2e638c",
-"bSy c #2e638e",
-"b#5 c #2e6399",
-".EM c #2e658c",
-"#GQ c #2e69a0",
-"bPG c #2e6a99",
-"#yI c #2e6aa0",
-"axX c #2e6aa1",
-"#Ki c #2e6ba4",
-"Qtx c #2f2424",
-"a5w c #2f2432",
-"aZr c #2f2434",
-".fc c #2f2524",
-"asy c #2f2636",
-"a1h c #2f2735",
-"aZu c #2f2835",
-"a72 c #2f2837",
-".G6 c #2f2a33",
-"QtZ c #2f2b2b",
-"a7f c #2f2c3b",
-".rf c #2f2e2e",
-"aKr c #2f2e3c",
-".#O c #2f2f2f",
-"aZb c #2f2f3d",
-"bMc c #2f3031",
-"bbD c #2f303a",
-"#mw c #2f303c",
-"bKE c #2f3132",
-".pe c #2f3233",
-"b.I c #2f323f",
-".yf c #2f332c",
-".ou c #2f3536",
-"awG c #2f3640",
-"#Ht c #2f3641",
-"aPM c #2f3644",
-"aEt c #2f3740",
-"avb c #2f3742",
-"bTF c #2f383c",
-"aDn c #2f3842",
-"aDm c #2f3843",
-"awW c #2f3942",
-"avN c #2f3943",
-".Ug c #2f3a41",
-"#al c #2f3a43",
-"aod c #2f3a45",
-"apg c #2f3a46",
-"ba3 c #2f3a47",
-"bRK c #2f3b41",
-".T7 c #2f3b42",
-".72 c #2f3b43",
-"#Px c #2f3b44",
-"#Z5 c #2f3b45",
-"#4T c #2f3b46",
-".Xu c #2f3c42",
-"#on c #2f3c43",
-"#rA c #2f3c44",
-"aZP c #2f3c45",
-"#x. c #2f3c46",
-"#qa c #2f3c47",
-"bmR c #2f3c48",
-"bdL c #2f3c49",
-"a6a c #2f3d44",
-"aB9 c #2f3d47",
-"aM7 c #2f3d48",
-"bmh c #2f3d49",
-"a8u c #2f3d4a",
-"bNq c #2f3e45",
-"a93 c #2f3e48",
-"ah6 c #2f3e4b",
-"bO1 c #2f3f46",
-"bTo c #2f3f47",
-"adA c #2f3f4b",
-"bVx c #2f3f4d",
-"bUR c #2f414b",
-"aKD c #2f416c",
-"bBc c #2f4254",
-"aUM c #2f426d",
-"aBg c #2f426e",
-"bC# c #2f4355",
-".1k c #2f444f",
-"bVR c #2f4452",
-"aCS c #2f4c71",
-"bOi c #2f4f7c",
-".pX c #2f5157",
-"bXE c #2f5275",
-"byh c #2f5479",
-"aW6 c #2f5770",
-"#XA c #2f578f",
-"bAb c #2f5881",
-"#OW c #2f5a78",
-"#Xq c #2f5c7a",
-"aO5 c #2f628a",
-"bT7 c #2f6389",
-"aYA c #2f6694",
-".EL c #2f678d",
-"aXJ c #2f6795",
-"axS c #2f689a",
-"#Iy c #2f6ca3",
-"aU4 c #302634",
-"a0s c #302635",
-"bDk c #302736",
-"aPf c #302835",
-"bof c #302936",
-"aBQ c #302938",
-"#a# c #302d3a",
-"#du c #302e3c",
-".pd c #302f2f",
-".5k c #302f3c",
-"aBq c #302f3d",
-"QtR c #303030",
-".pV c #303031",
-".or c #303032",
-"bL9 c #303131",
-"bQQ c #303132",
-"bJl c #303233",
-"#zQ c #30323e",
-"asn c #30323f",
-"at. c #30333f",
-".Js c #30343c",
-"biT c #303441",
-"bjE c #303542",
-"#8m c #30363f",
-"#Iq c #303641",
-"adi c #303645",
-".nL c #30383b",
-"aSq c #303842",
-"aCJ c #303844",
-"a9l c #303845",
-"bPa c #30393c",
-"aAk c #303942",
-"ayx c #303943",
-"aBz c #303944",
-"aN6 c #303947",
-".U3 c #303b42",
-"#f3 c #303b43",
-"#PG c #303b44",
-"#Je c #303b45",
-"adX c #303b46",
-".Vi c #303c42",
-".T8 c #303c43",
-"#cS c #303c44",
-"#NR c #303c45",
-"#Yk c #303c46",
-"#1b c #303c47",
-".Wu c #303d43",
-".Y2 c #303d44",
-"#NQ c #303d45",
-"#Wn c #303d46",
-"#Fs c #303d47",
-"acA c #303d48",
-"a7L c #303d49",
-"bdJ c #303d4a",
-"aaQ c #303e47",
-"bpx c #303e4a",
-"bes c #303e4b",
-"aQL c #303e68",
-"a3q c #303f47",
-"bii c #303f49",
-"#LR c #304048",
-"bOZ c #304049",
-"bR3 c #304148",
-"bTn c #30414a",
-"aKO c #30416c",
-"bWZ c #304654",
-"bAy c #304658",
-"asY c #304755",
-".RU c #304a59",
-".sq c #304b6d",
-"bqR c #304f6f",
-"bp8 c #305070",
-"bxf c #305273",
-"aqr c #30548a",
-"bA3 c #305982",
-"bRj c #305a91",
-"#PI c #305c7b",
-"#NW c #305d7b",
-"byS c #305f85",
-"bK5 c #305f95",
-"azz c #305f96",
-"aA2 c #306098",
-"aXu c #306295",
-"bKU c #306389",
-".EN c #30678d",
-"bxT c #306792",
-"aOV c #306896",
-"#6. c #306ca2",
-"#FV c #306da3",
-"#Js c #306da6",
-".rh c #307a81",
-".uY c #30f8f8",
-"aZy c #312534",
-".f0 c #312625",
-".2Z c #312736",
-"#dr c #312837",
-"a9r c #312b38",
-".ot c #313030",
-"asq c #31303b",
-".nK c #313130",
-"Qtn c #313131",
-".nH c #313132",
-"aS1 c #31313e",
-"aJf c #31313f",
-"bPw c #313233",
-"bJn c #313234",
-"bMl c #313334",
-"bMD c #313356",
-"bjs c #313542",
-"adh c #313544",
-"#9e c #313644",
-".S# c #313740",
-"aPK c #313745",
-"bNE c #31393d",
-"arw c #313943",
-"#6k c #31395d",
-"bI9 c #313a3e",
-"azi c #313a42",
-"bUs c #313a43",
-"aBy c #313a44",
-"bQd c #313c42",
-".T9 c #313c43",
-".73 c #313c44",
-"#Q4 c #313c45",
-"#Ih c #313c46",
-".U. c #313d44",
-"#b5 c #313d45",
-"#ss c #313d46",
-"#6X c #313d47",
-"#w6 c #313d48",
-"#pd c #313e45",
-"aeY c #313e47",
-"b.d c #313e48",
-"#ZF c #313e49",
-"bdc c #313e4a",
-"beq c #313e4b",
-"ae4 c #313f49",
-"aq. c #313f4a",
-"ber c #313f4b",
-"a04 c #31406c",
-"bdu c #314149",
-"bO0 c #31424a",
-"bR2 c #31434b",
-"bX6 c #314957",
-"bCZ c #314f6a",
-"aov c #314f85",
-"bnZ c #315070",
-"boD c #315170",
-"#FS c #31526b",
-"bDp c #315465",
-"bWx c #315478",
-"#Xz c #31568e",
-"#2x c #31586d",
-"bcS c #315890",
-"a2V c #315c77",
-"bWc c #315e95",
-"#t. c #31648a",
-"aW3 c #31668e",
-"aJV c #316792",
-"aPY c #316796",
-"bxN c #316993",
-"bE5 c #316da3",
-"#Dq c #316ea4",
-"bww c #316fa5",
-"aMt c #3171a1",
-"a2W c #322534",
-"Qtr c #322727",
-"#lD c #322d3b",
-"a5c c #322d3c",
-"aTW c #322e3c",
-"aF# c #322e3d",
-"QtG c #322f2f",
-"a5a c #322f3e",
-"#fV c #32303d",
-".un c #323129",
-".rV c #32312d",
-"baW c #32313b",
-"#i4 c #32313d",
-"aSm c #32313f",
-".m0 c #323231",
-".ej c #323232",
-".me c #323233",
-"bTW c #323334",
-".Jh c #323339",
-"ag4 c #32333c",
-"av9 c #32333e",
-"aE8 c #323357",
-"#bP c #323540",
-"aFn c #323639",
-"bo8 c #32363f",
-".Qk c #323740",
-"anA c #323742",
-"bFh c #32374d",
-".1I c #323841",
-"#Ik c #323843",
-".m1 c #323a3e",
-"bP6 c #323a43",
-"bUW c #323b3e",
-"biR c #323b48",
-"aHk c #323b4f",
-".yv c #323c33",
-".Uf c #323d44",
-"#am c #323d45",
-"#w3 c #323d46",
-"#Ho c #323d47",
-"aq7 c #323d49",
-".U# c #323e44",
-".Xt c #323e45",
-"##u c #323e46",
-"#PF c #323e47",
-"#Zx c #323e48",
-"#kV c #323e49",
-".1T c #323f45",
-".3y c #323f46",
-"bkY c #323f48",
-"#km c #323f49",
-"a.P c #323f4a",
-"b#M c #323f4b",
-"bwv c #323f4c",
-"an4 c #32404a",
-"brT c #32404b",
+"440 286 18056 3",
+".6w c #000000",
+".8o c #010000",
+".#X c #02141c",
+"#gd c #050709",
+"#jr c #05070a",
+"#ge c #050809",
+"#gc c #05080a",
+".7m c #05080b",
+"#gb c #05080c",
+"#js c #05090a",
+"#ga c #05090b",
+".7l c #05090c",
+"#kX c #06080b",
+"#kY c #06090b",
+"#jq c #06090c",
+"#mn c #060a0b",
+"#kW c #060a0c",
+"#kV c #060a0d",
+"#nW c #060b0d",
+"#pJ c #060b0e",
+"aEa c #060b10",
+".Qo c #06131b",
+"#nX c #070a0d",
+"#pI c #070b0d",
+"#mm c #070b0e",
+"#q4 c #070b0f",
+"#nV c #070c0e",
+"#jp c #070c0f",
+"#q5 c #070c10",
+"#se c #070d10",
+"aDw c #070d11",
+"aGl c #070d12",
+"#t# c #070f12",
+"#s. c #070f13",
+"#uc c #071014",
+"baj c #07161f",
+"aFT c #080b0e",
+".8n c #080b12",
+"#q3 c #080d0f",
+"#pH c #080d10",
+"#sd c #080d11",
+"#sc c #080e11",
+"#q0 c #080f12",
+"aEO c #081015",
+"#we c #081317",
+".VA c #08141c",
+".lz c #08151e",
+".c1 c #08161e",
+".fh c #08161f",
+"#q6 c #090c0f",
+"#q2 c #090d10",
+"#sb c #090e11",
+"#q1 c #090e12",
+"#t. c #090f12",
+"#sa c #090f13",
+"#s9 c #091013",
+"#s8 c #091014",
+"#ub c #091114",
+"#vf c #091216",
+".gd c #09161f",
+".dA c #09171f",
+"Qtu c #091a21",
+".5s c #0a0d0f",
+"#s# c #0a1014",
+"#a1 c #0a1015",
+"#s7 c #0a1115",
+"#d1 c #0a1216",
+"#cS c #0a1217",
+"#ua c #0a1317",
+"#u. c #0a1418",
+"#vc c #0a151a",
+"#wc c #0a151c",
+"#hM c #0a171c",
+"#jf c #0a171d",
+".gb c #0a171f",
+".tn c #0a1720",
+"#md c #0a181e",
+"#u9 c #0a181f",
+"QtQ c #0a1820",
+".dC c #0a1921",
+".fo c #0a1a23",
+"bhR c #0a1e26",
+"#JC c #0a1f27",
+"#qZ c #0b1115",
+"#cl c #0b1216",
+".7u c #0b1317",
+".5F c #0b1318",
+"#u# c #0b1418",
+".7v c #0b151a",
+"#vd c #0b151b",
+".3K c #0b161b",
+"#.k c #0b1623",
+"#t5 c #0b181e",
+"#v. c #0b181f",
+".di c #0b1820",
+".af c #0b1821",
+".qt c #0b1822",
+"#w. c #0b191f",
+".#U c #0b1921",
+"#41 c #0b1922",
+"#w6 c #0b1a21",
+"#9B c #0b1a22",
+".gm c #0b1b24",
+".hm c #0b1c24",
+".nh c #0b1c25",
+"aGw c #0b1d26",
+"bt0 c #0b1d27",
+".9q c #0c1216",
+"#ax c #0c1318",
+".3J c #0c1419",
+"#wd c #0c141a",
+"#.G c #0c1519",
+"#ve c #0c151a",
+".5G c #0c151b",
+"#kw c #0c161b",
+".9r c #0c171c",
+".HQ c #0c171f",
+".5e c #0c181e",
+".vp c #0c181f",
+".JR c #0c1820",
+"#qT c #0c191f",
+"#s3 c #0c1920",
+".cG c #0c1921",
+".mj c #0c1922",
+".ef c #0c1a22",
+".tA c #0c1a23",
+"atV c #0c1b23",
+"avY c #0c1b24",
+"#61 c #0c1b25",
+".hn c #0c1c24",
+".il c #0c1c25",
+".ng c #0c1d25",
+"a.1 c #0c1d26",
+".k7 c #0c1e25",
+"#N6 c #0c1e26",
+"aGx c #0c1e27",
+"a8P c #0c1f28",
+"br# c #0c2229",
+"c#J c #0c355a",
+"bYZ c #0c385c",
+"#fk c #0c5f61",
+".7k c #0d0e10",
+"#a0 c #0d0f13",
+"##b c #0d1014",
+".9p c #0d1015",
+"#cR c #0d1016",
+".7t c #0d1115",
+".5E c #0d1116",
+".3I c #0d1217",
+"#d6 c #0d1317",
+".1V c #0d1318",
+"#d2 c #0d1319",
+"#fL c #0d1418",
+".0g c #0d1419",
+"#hv c #0d1519",
+".YA c #0d151a",
+"#.F c #0d161a",
+".1W c #0d161b",
+"##c c #0d161c",
+".0h c #0d171c",
+"#ee c #0d171d",
+".1X c #0d181e",
+".Qp c #0d181f",
+"#w9 c #0d191e",
+"##d c #0d191f",
+".yr c #0d1920",
+".BR c #0d1921",
+"a6O c #0d1a21",
+".a8 c #0d1a22",
+"asi c #0d1a25",
+".co c #0d1b23",
+"abf c #0d1b24",
+".w7 c #0d1c25",
+"a4u c #0d1c26",
+"#90 c #0d1d25",
+"aUR c #0d1d26",
+".d1 c #0d1e25",
+".n9 c #0d1e26",
+"agd c #0d1e27",
+"aS9 c #0d1e28",
+"#C. c #0d1f27",
+"aLd c #0d1f28",
+"#Hs c #0d2028",
+"axg c #0d2029",
+"btb c #0d2129",
+"a6e c #0d2229",
+"b5L c #0d3659",
+"a#o c #0d365a",
+"cdD c #0d365b",
+"#eu c #0e1012",
+"#eq c #0e1215",
+"#d7 c #0e1418",
+"#cs c #0e1419",
+"#d0 c #0e141a",
+"#cm c #0e1518",
+"#ct c #0e1519",
+".40 c #0e151a",
+"#fK c #0e151b",
+".WP c #0e161b",
+"#.E c #0e171c",
+"#a2 c #0e171d",
+".YB c #0e181d",
+".y4 c #0e181e",
+".vQ c #0e181f",
+".PA c #0e191f",
+"#hN c #0e1920",
+"#jg c #0e1a1f",
+"#kL c #0e1a20",
+"azf c #0e1a22",
+".tB c #0e1b22",
+".dB c #0e1b23",
+".gc c #0e1b24",
+"#x2 c #0e1c23",
+"Qtr c #0e1c24",
+".ex c #0e1d25",
+"#7V c #0e1d26",
+"#1t c #0e1d27",
+".gn c #0e1e25",
+".n. c #0e1e26",
+".rL c #0e1f26",
+"#C# c #0e1f27",
+"acX c #0e1f28",
+"aLe c #0e1f29",
+"aaf c #0e2028",
+"axL c #0e2029",
+"aQ. c #0e2128",
+"#HA c #0e2129",
+"#CJ c #0e222a",
+"cxE c #0e3453",
+"bx3 c #0e3659",
+"ccp c #0e365a",
+"b8V c #0e365b",
+"a#p c #0e375b",
+"#9. c #0e385c",
+".Yt c #0f0a0f",
+"#cQ c #0f0f13",
+"#cr c #0f1418",
+"#.L c #0f1419",
+".WO c #0f141a",
+"#.M c #0f1519",
+"#fM c #0f151b",
+"#ck c #0f161b",
+".PV c #0f161d",
+"#i1 c #0f171b",
+".V# c #0f171c",
+".A0 c #0f171d",
+".Mq c #0f181e",
+".PE c #0f181f",
+".S0 c #0f191f",
+".NU c #0f1920",
+"aJN c #0f1922",
+".Va c #0f1a20",
+".IB c #0f1a21",
+".Mk c #0f1b21",
+".xg c #0f1b22",
+".wk c #0f1c23",
+".b6 c #0f1c24",
+"acR c #0f1c25",
+".0i c #0f1d23",
+"Qti c #0f1d25",
+".uu c #0f1d26",
+".YC c #0f1e24",
+"aGv c #0f1e25",
+"av. c #0f1e27",
+".tY c #0f1f26",
+".cw c #0f1f27",
+"aLc c #0f1f29",
+"#MA c #0f2027",
+"af2 c #0f2028",
+"#Ne c #0f2029",
+"axf c #0f2129",
+"#IX c #0f212a",
+"bWP c #0f212e",
+".MK c #0f222a",
+"aXA c #0f222b",
+"#CK c #0f232b",
+"cx8 c #0f2841",
+"cye c #0f2942",
+"cvB c #0f2943",
+"cyf c #0f2944",
+"c## c #0f2a44",
+"b9F c #0f2a45",
+"caw c #0f2b44",
+"b7S c #0f2b45",
+"b7T c #0f2b46",
+"bO4 c #0f3153",
+"bGn c #0f3155",
+"bMl c #0f3254",
+"bbw c #0f3255",
+"b.8 c #0f3256",
+"bi7 c #0f355a",
+"co5 c #0f365b",
+"#yn c #0f375b",
+"#8f c #0f375c",
+"a#n c #0f385c",
+"#7q c #0f385f",
+"bRe c #0f395d",
+"#cP c #100d12",
+".3H c #101016",
+".1U c #101116",
+".0f c #101217",
+".Yz c #101318",
+".8R c #10151a",
+".V. c #10151b",
+".Tl c #10151c",
+".60 c #10161a",
+".8S c #10161b",
+".RC c #10161c",
+".61 c #10171b",
+".3c c #10181d",
+".CO c #10181f",
+".IO c #10191e",
+".vP c #101a20",
+".Mj c #101a21",
+".PW c #101a22",
+".WQ c #101b21",
+".AL c #101b22",
+"#nL c #101b23",
+".zT c #101b24",
+".5. c #101c22",
+".Gn c #101c23",
+".zU c #101c24",
+".vo c #101c25",
+"#me c #101d23",
+".Of c #101d24",
+".hb c #101d25",
+".nj c #101d26",
+"amz c #101d28",
+".aO c #101e25",
+"Qtd c #101e26",
+".bt c #101e27",
+".aJ c #101f26",
+".aN c #101f27",
+"a.2 c #101f28",
+".fp c #102027",
+"#B9 c #102028",
+"aby c #102029",
+"bWT c #10202e",
+"#J9 c #102129",
+"#GR c #10212a",
+"bTI c #10212d",
+"bWQ c #10212e",
+"bWR c #10212f",
+".Og c #10222a",
+"aV8 c #10222b",
+".RD c #10232a",
+".EW c #10232b",
+"b9T c #10283e",
+"cx7 c #102942",
+"cxp c #102943",
+"cxo c #102944",
+"cx6 c #102a42",
+"cx4 c #102a43",
+"b9G c #102a44",
+"cav c #102a45",
+"cwu c #102a46",
+"cxn c #102b44",
+"b9E c #102b45",
+"b7R c #102b46",
+"b6c c #102b47",
+"b7Q c #102c46",
+"b6b c #102c47",
+"b4H c #102c48",
+"ck5 c #102c49",
+"b6e c #102d48",
+"b4I c #102d49",
+"b4G c #102d4a",
+"ceU c #102e49",
+"b4B c #102e4b",
+"aHp c #10314b",
+"bHC c #103254",
+"bL# c #103255",
+"aON c #103256",
+"bNG c #103354",
+"bTZ c #103356",
+"bPY c #103357",
+"b7a c #103358",
+"cse c #103454",
+"bVm c #103458",
+"cwP c #103556",
+"b0K c #103559",
+"bkt c #10355a",
+"#7p c #10365b",
+"bhD c #10365c",
+"#89 c #10385c",
+"bN. c #10395c",
+"aIi c #10395d",
+"aF9 c #103a5f",
+"#dr c #10b2b2",
+".0a c #110b10",
+".9o c #110f13",
+".5D c #110f14",
+"#et c #111315",
+"#es c #111416",
+"#er c #111417",
+".PU c #11151d",
+"#hU c #111619",
+".62 c #11161b",
+".41 c #11171c",
+".8T c #11171d",
+".42 c #11181d",
+".KK c #11181e",
+".xO c #11191e",
+".wH c #111a20",
+".S4 c #111b21",
+"#zX c #111b22",
+".X7 c #111c22",
+".yU c #111c23",
+"#kM c #111c24",
+".7# c #111d23",
+".Oe c #111d24",
+".ha c #111d25",
+".xL c #111e25",
+"QtT c #111e26",
+".ag c #111e27",
+"#WZ c #111e28",
+"QtU c #111f26",
+"Qtj c #111f27",
+".ee c #111f28",
+"a52 c #111f29",
+".cF c #112028",
+"axk c #112029",
+"aVh c #112128",
+"akm c #112129",
+"#Ca c #11212a",
+"aIx c #11212b",
+"brm c #11212c",
+"bVb c #11212d",
+"bTJ c #11212e",
+"bWS c #11212f",
+"#GQ c #11222a",
+"#GS c #11222b",
+".aR c #11222c",
+"bTN c #11222e",
+"bVa c #11222f",
+"bYE c #112230",
+".EV c #11232b",
+".EU c #11232c",
+"bcR c #11242c",
+"b6y c #112436",
+"b6z c #112437",
+"bbe c #11252c",
+"b8c c #112538",
+"b8b c #112539",
+"b9V c #11253a",
+"b8a c #112639",
+"b9U c #11263a",
+"chj c #11263b",
+"cqY c #11273c",
+"b6x c #11283d",
+"cx5 c #112a44",
+"cwv c #112b45",
+"cvu c #112b46",
+"cwF c #112b47",
+"cwt c #112c46",
+"cpZ c #112c47",
+"b6d c #112c48",
+"ctG c #112d47",
+"b7P c #112d48",
+"b4E c #112d49",
+"b4C c #112d4a",
+"b4F c #112e49",
+"b4D c #112e4a",
+"b2Y c #112e4b",
+"b20 c #112f4b",
+"b2Z c #112f4c",
+"ceV c #112f4d",
+"b21 c #11304d",
+"b1u c #11304e",
+"b1s c #11304f",
+"b1t c #11314f",
+"b1r c #113150",
+"b0E c #113256",
+"cs6 c #11334f",
+"bMk c #113355",
+"bW3 c #113356",
+"bJD c #113357",
+"bRJ c #113456",
+"#DK c #113457",
+"aO. c #113458",
+"crr c #113555",
+"aRY c #113559",
+"bcK c #11355a",
+"bio c #11365b",
+"c.s c #11375b",
+"ccq c #11375c",
+"c.r c #11385b",
+"b6T c #11385c",
+"#8e c #11385d",
+"aGZ c #11395d",
+"aF8 c #11395e",
+"aL3 c #113b60",
+"aaN c #11497b",
+"#g2 c #117274",
+".5y c #120a10",
+"##a c #120e14",
+".U9 c #12121a",
+"aFn c #121415",
+".RB c #12141a",
+".43 c #12181d",
+".Ey c #12181e",
+"#cu c #12191d",
+".3d c #12191e",
+".ZQ c #12191f",
+".Ex c #121920",
+".1r c #121a1f",
+".NX c #121b21",
+".Wm c #121b22",
+".8Z c #121c22",
+".UI c #121c23",
+".uX c #121d23",
+".IC c #121d24",
+".xE c #121d25",
+"aHd c #121d26",
+"#as c #121e24",
+".IA c #121e25",
+".ni c #121e26",
+".Fy c #121e27",
+".wC c #121f25",
+".Mm c #121f26",
+"QtR c #121f27",
+".fZ c #121f28",
+"#Zh c #121f29",
+".vJ c #122027",
+"Qth c #122028",
+".bu c #122029",
+".d0 c #122128",
+".as c #122129",
+".Tm c #12212a",
+"#H3 c #12212b",
+"bPK c #12212c",
+"bVc c #12212e",
+"agc c #12222a",
+"#Hz c #12222b",
+"bLJ c #12222c",
+"aCr c #12222d",
+"bTH c #12222e",
+"bTK c #12222f",
+"bWU c #122230",
+"cvo c #122232",
+"cuo c #122233",
+".I9 c #12232b",
+".ET c #12232c",
+"aCq c #12232d",
+"bTM c #12232e",
+"bTL c #12232f",
+"bYF c #122331",
+"cvn c #122332",
+"cvm c #122333",
+"cup c #122334",
+"cur c #122335",
+"#.j c #122337",
+"bra c #12242c",
+"bta c #12242d",
+"bYD c #122431",
+"cuq c #122435",
+"ctD c #122436",
+"a3. c #12252d",
+"ctC c #122535",
+"crS c #122536",
+"cqX c #122537",
+"cpM c #122538",
+"ces c #122539",
+"b6A c #122638",
+"b8. c #122639",
+"b8# c #12263a",
+"b8d c #12273a",
+"cet c #12273b",
+"b9W c #12283d",
+"c#e c #12283e",
+"b79 c #122940",
+"bHo c #122c3e",
+"cxq c #122c46",
+"cau c #122d47",
+"csJ c #122d48",
+"cpW c #122d49",
+"cww c #122e47",
+"c#. c #122e48",
+"b9D c #122e49",
+"b6a c #122e4a",
+"b78 c #122e4b",
+"cbS c #122f4a",
+"cat c #122f4b",
+"b2W c #122f4c",
+"b2V c #122f4d",
+"b4A c #122f4e",
+"b7O c #12304b",
+"b2X c #12304c",
+"b2T c #12304d",
+"b2U c #12304e",
+"b48 c #12304f",
+"b6. c #12314d",
+"b2S c #12314e",
+"b1v c #12314f",
+"b1q c #123150",
+"b1p c #123250",
+"b1o c #123251",
+"b1n c #123252",
+"b1w c #123352",
+"b58 c #123353",
+"b2j c #123456",
+"bx6 c #123457",
+"bJ5 c #123458",
+"bOt c #123459",
+"bO3 c #123557",
+"aJE c #123558",
+"#zr c #123559",
+"bQm c #12355a",
+"cvM c #123656",
+"bMj c #123658",
+"#B. c #123659",
+"#Gy c #12365a",
+"bSs c #12365b",
+"bGm c #12365c",
+"bNF c #123759",
+"#7o c #12375b",
+"cmH c #12375c",
+"#7n c #12385c",
+"cln c #12385f",
+"bIh c #12395c",
+"#xp c #12395d",
+"aVe c #123c63",
+"cmI c #12426e",
+"bVA c #124674",
+".1T c #131016",
+".WN c #131218",
+"#d8 c #13141a",
+".Tk c #13141b",
+".3e c #13191f",
+".GE c #131a20",
+".y3 c #131a21",
+".4Z c #131b1f",
+".1s c #131b20",
+".xN c #131b21",
+".wG c #131b22",
+".vO c #131c22",
+".Yg c #131c23",
+".SX c #131d23",
+".SW c #131d24",
+"#y4 c #131e24",
+".Cz c #131e25",
+".KC c #131e26",
+"aK3 c #131e27",
+"#YB c #131f25",
+".AK c #131f26",
+".e4 c #131f27",
+"aPc c #131f2a",
+"#PA c #132026",
+"#ci c #132027",
+"QtS c #132028",
+"Qtc c #132029",
+"#ar c #132127",
+".br c #132128",
+".#y c #132129",
+".ah c #13212a",
+"azr c #13212b",
+"bQ4 c #13212c",
+".qh c #132229",
+".dg c #13222a",
+".r. c #13222b",
+"#XD c #13222c",
+"bMX c #13222d",
+".G1 c #13232b",
+".G0 c #13232c",
+"#LA c #13232d",
+"aBS c #13232e",
+"#yM c #13242c",
+"baA c #13242d",
+"aBh c #13242e",
+"bPJ c #132431",
+"ctz c #132434",
+"ctB c #132435",
+"baB c #13252d",
+"baC c #13252e",
+"ctA c #132535",
+"csH c #132536",
+"cvp c #132537",
+"cpL c #132539",
+"ayL c #13262f",
+"crR c #132637",
+"crQ c #132638",
+"cer c #132639",
+"b6B c #13263a",
+"aHV c #132730",
+"cqW c #132739",
+"b6C c #13273a",
+"clY c #13273b",
+"cfU c #13283b",
+"b8e c #13283c",
+"aSs c #132936",
+"aP9 c #132a39",
+"b9X c #132a40",
+"ceu c #132b41",
+"aRu c #132e41",
+"ctF c #132e49",
+"cq# c #132e4a",
+"ceT c #132f4a",
+"cf1 c #132f4b",
+"cuy c #13304a",
+"c.9 c #13304b",
+"b9H c #13304c",
+"b59 c #13304d",
+"cbT c #13314c",
+"b6# c #13314d",
+"b4J c #133150",
+"b49 c #133250",
+"ceW c #133251",
+"b22 c #133252",
+"cxu c #133351",
+"b1m c #133352",
+"b1l c #133353",
+"bZL c #133354",
+"b25 c #133355",
+"b2R c #133453",
+"bZM c #133454",
+"bZK c #133455",
+"bDt c #133457",
+"cqs c #133554",
+"b4z c #133555",
+"bZJ c #133556",
+"b57 c #133557",
+"bCD c #133558",
+"ban c #133559",
+"b1# c #13355a",
+"bZI c #133657",
+"bXz c #133658",
+"#Ct c #133659",
+"#Zp c #13365a",
+"b2I c #13365b",
+"bx2 c #133759",
+"#yt c #13375a",
+"#wx c #13375b",
+"aOM c #13375c",
+"cwd c #13375d",
+"bZs c #13385a",
+"#2# c #13385b",
+"#zt c #13385c",
+"#vx c #13385d",
+"cgr c #13385e",
+"bRf c #13395c",
+"#yo c #13395d",
+"#UI c #13395e",
+"bva c #133a5f",
+"b5M c #133b61",
+"bi8 c #133d67",
+"b35 c #133f69",
+"aUo c #13416a",
+"bUc c #134271",
+"b2o c #13436f",
+"b0Q c #134472",
+"aaO c #134572",
+"bKS c #134674",
+"bJQ c #134675",
+"aPr c #134b79",
+".3E c #140b11",
+".WI c #140d12",
+"#pK c #141015",
+".Yy c #141117",
+".RA c #14111a",
+".8p c #141413",
+".1Y c #141a21",
+".3f c #141b20",
+".Y. c #141b21",
+".CN c #141b22",
+".Ew c #141c21",
+".KJ c #141c22",
+".xM c #141c23",
+".UR c #141d23",
+"#dX c #141d25",
+".Ra c #141e25",
+"#v9 c #141e26",
+".UH c #141f25",
+".NR c #141f26",
+".R# c #141f27",
+"#yL c #141f29",
+".Px c #142026",
+".Em c #142027",
+".yT c #142028",
+".rK c #142029",
+".o1 c #142128",
+".#z c #142129",
+".#S c #14212a",
+"#2H c #14212b",
+"awc c #14212d",
+".av c #142229",
+".#A c #14222a",
+".au c #14222b",
+"#EU c #14222c",
+"aYp c #14222d",
+".7E c #14232a",
+".k6 c #14232b",
+".v4 c #14232c",
+".GZ c #14232d",
+"#PB c #14232e",
+"#Do c #14242c",
+".x9 c #14242d",
+"#O7 c #14242e",
+"#Rc c #14242f",
+"a4K c #14252d",
+"avT c #14252e",
+"azJ c #14252f",
+"#PQ c #142530",
+"#SP c #142531",
+"bbf c #14262e",
+"#Qp c #142631",
+"b0q c #142635",
+"csG c #142637",
+"b5h c #142639",
+"aFZ c #142730",
+"aVg c #142737",
+"b5c c #142739",
+"b5d c #14273a",
+"b6D c #14283a",
+"b6E c #14283b",
+"cfT c #14283c",
+"ckA c #14293c",
+"chi c #14293d",
+"cwq c #142a3d",
+"ckB c #142a3e",
+"ccY c #142b41",
+"c#f c #142c43",
+"cnr c #142f4b",
+"cyd c #143049",
+"ckM c #14304b",
+"cgl c #14304c",
+"cq2 c #14314b",
+"cc6 c #14314c",
+"b7N c #14314d",
+"b9C c #14324d",
+"cbZ c #14324e",
+"cd. c #14324f",
+"b4K c #143453",
+"cuC c #143454",
+"b4L c #143455",
+"b23 c #143555",
+"bZN c #143556",
+"b24 c #143557",
+"bZO c #143558",
+"#8g c #14355a",
+"b1k c #143656",
+"ch6 c #143657",
+"bG8 c #143658",
+"bFe c #14365a",
+"bUr c #14365b",
+"cq9 c #143758",
+"bm. c #143759",
+"#vw c #14375a",
+"#yu c #14375b",
+"a#q c #14375c",
+"bVW c #14385a",
+"#HK c #14385b",
+"#xr c #14385c",
+"#zu c #14385d",
+"bZt c #14385e",
+"b1j c #14395b",
+"#Vu c #14395c",
+"#ww c #14395d",
+"#xq c #14395e",
+"#R9 c #14395f",
+"crL c #143960",
+"#S. c #143a5e",
+"#xs c #143a5f",
+"#Vt c #143a60",
+"bx5 c #143a61",
+"b9c c #143b5f",
+"#yp c #143b60",
+"#TP c #143b61",
+"bFf c #143b62",
+"aWL c #143c62",
+"aRX c #143c64",
+"bHn c #144068",
+"a#m c #14416a",
+"aV1 c #14436d",
+"bhK c #144371",
+"bkz c #144472",
+"bP9 c #144674",
+"a#H c #144775",
+"aOG c #144a78",
+"aPq c #144b78",
+".1Q c #150c12",
+".5C c #150e15",
+".3G c #150f15",
+"#hV c #151819",
+"#t9 c #15191f",
+"#aD c #151a1f",
+"#.N c #151a20",
+".8U c #151b20",
+".ZR c #151c23",
+".Y# c #151d23",
+".vN c #151d24",
+".y2 c #151d25",
+".wF c #151e25",
+".uW c #151e26",
+".82 c #151f26",
+".49 c #151f27",
+".Cy c #152027",
+".xD c #152028",
+".uQ c #152029",
+"#IW c #15202a",
+".AJ c #152128",
+".uO c #152129",
+".vH c #15212a",
+"#JB c #15212b",
+"#ch c #152228",
+".eU c #152229",
+".#x c #15222a",
+".#D c #15222b",
+".aM c #15222c",
+".dD c #15232a",
+".aE c #15232b",
+".dh c #15232c",
+"#P# c #15232d",
+"#No c #15232e",
+".tm c #15242c",
+".w2 c #15242d",
+"#5u c #15242e",
+"#Yv c #15242f",
+"ayY c #152430",
+"a5z c #15252d",
+"aBK c #15252e",
+"#PP c #15252f",
+"#MH c #152531",
+"baD c #15262e",
+"azK c #15262f",
+"#KJ c #152631",
+"#YC c #152632",
+"bWO c #152633",
+"bYG c #152635",
+"b0p c #152636",
+"b#j c #15272f",
+"aI# c #152730",
+"aXf c #152733",
+"#RZ c #152735",
+"b3y c #152739",
+"b5e c #15273a",
+"aGF c #152831",
+"#R5 c #152836",
+"b3B c #152839",
+"b5f c #15283a",
+"b5g c #15283b",
+"cpN c #15283c",
+"aWM c #152937",
+"#SQ c #152938",
+"cqV c #15293b",
+"cn# c #15293c",
+"clX c #15293d",
+"ckz c #15293e",
+"#Rd c #152a3a",
+"b6F c #152a3d",
+"ceq c #152a3e",
+"#Qq c #152b3c",
+"bOm c #152b3e",
+"b8f c #152b40",
+"ccZ c #152c42",
+"b.s c #152d43",
+"b9Y c #152d44",
+"cc0 c #152e45",
+"#Re c #15304a",
+"cqb c #15304b",
+"cxm c #15314a",
+"cf2 c #15314c",
+"#R8 c #15324c",
+"c.8 c #15324d",
+"b7M c #15324e",
+"cq1 c #15334c",
+"cnl c #15334d",
+"cjh c #15334e",
+"b7U c #15334f",
+"b0D c #153356",
+"aTi c #15344a",
+"cnu c #15344e",
+"cc9 c #15344f",
+"bqV c #153451",
+"#wu c #153551",
+"bo# c #153553",
+"ceX c #153555",
+"b3n c #153556",
+"by4 c #153656",
+"#Rf c #153657",
+"bqd c #153658",
+"boK c #153757",
+"#wv c #153758",
+"#Ol c #153759",
+"bmM c #15375a",
+"bSX c #15375b",
+"com c #15375c",
+"cmy c #153859",
+"#P. c #15385a",
+"bvy c #15385b",
+"#HL c #15385c",
+"#4K c #15385d",
+"b4n c #15395b",
+"#Ag c #15395c",
+"#zs c #15395d",
+"#SX c #15395e",
+"bEq c #15395f",
+"b4y c #153a5d",
+"#uw c #153a5e",
+"#3r c #153a5f",
+"#Jq c #153a60",
+"aKR c #153b5f",
+"#Rg c #153b60",
+"#7r c #153b61",
+"#40 c #153b62",
+"b2H c #153c60",
+"#1r c #153c61",
+"#W# c #153c62",
+"#Rh c #153c63",
+"#SY c #153d63",
+"#UH c #153d64",
+"bOE c #153d67",
+"#G4 c #153e65",
+"cf. c #153e66",
+"aGY c #153f67",
+"coX c #154168",
+"cbo c #15416c",
+"bZf c #154372",
+"bOT c #154471",
+"b2t c #154472",
+"bNq c #154774",
+"a#G c #154775",
+"aGU c #154b78",
+"#iw c #15787a",
+"### c #160b11",
+".0b c #160c12",
+".Yu c #160c13",
+".WJ c #160c14",
+".9n c #160d14",
+".7s c #160e14",
+".5t c #16191b",
+"#nU c #161b1e",
+".NY c #161b22",
+".63 c #161c22",
+".44 c #161d22",
+".3g c #161d23",
+".ZS c #161e24",
+".CM c #161e25",
+".vM c #161e26",
+"#eE c #161f25",
+".uV c #161f26",
+".uU c #161f27",
+"#kP c #162027",
+"#hu c #162028",
+"#lW c #162029",
+"#pv c #162128",
+".Cx c #162129",
+".Vb c #16212a",
+"#dW c #162229",
+".bh c #16222a",
+".o. c #16222b",
+"#Hr c #16222c",
+"#F8 c #16222d",
+".dY c #16232a",
+".bi c #16232b",
+".#R c #16232c",
+"Qtq c #16232d",
+"#Yl c #16232e",
+".dE c #16242b",
+".#w c #16242c",
+"Qt. c #16242d",
+"aze c #16242e",
+"avm c #16242f",
+".WZ c #16252d",
+"#OW c #16252e",
+"aEZ c #16252f",
+"aFx c #162530",
+"baF c #16262e",
+"aJt c #16262f",
+"baE c #16272f",
+"awB c #162730",
+"#6a c #162732",
+"#Vm c #162733",
+"b3L c #162738",
+"a82 c #16282f",
+"aGG c #162831",
+"#MI c #162835",
+"#R6 c #162836",
+"b3M c #162838",
+"b3K c #162839",
+"b3A c #16283a",
+"b.z c #162931",
+"#Vn c #162937",
+"#Q9 c #162938",
+"#31 c #162939",
+"b3z c #16293a",
+"b3D c #16293b",
+"#Rb c #162a38",
+"#R7 c #162a39",
+"#Og c #162a3a",
+"cqU c #162a3d",
+"cfS c #162a3f",
+"#R0 c #162b3b",
+"cpK c #162b3e",
+"chh c #162b3f",
+"#O6 c #162c3c",
+"#R. c #162c3d",
+"#PR c #162c3e",
+"cwp c #162c3f",
+"#UB c #162d3e",
+"#3l c #162d3f",
+"#PO c #162e40",
+"b.7 c #162e44",
+"boa c #162f44",
+"boe c #162f45",
+"#SO c #162f46",
+"bu. c #163048",
+"bG9 c #16304a",
+"c#d c #163148",
+"cc1 c #16314a",
+"cms c #16314d",
+"ctT c #163248",
+"aT9 c #163249",
+"cev c #16324b",
+"#Qr c #16324c",
+"ceB c #16324e",
+"boL c #16334d",
+"ck6 c #16334e",
+"csI c #16344d",
+"cq0 c #16344e",
+"c.7 c #16344f",
+"bpK c #163450",
+"#uv c #163451",
+"cvt c #16354d",
+"cqZ c #16354f",
+"b9S c #163550",
+"c.6 c #163551",
+"cd# c #163552",
+"b6w c #163652",
+"b47 c #163653",
+"bnL c #163656",
+"#vv c #163756",
+"bY5 c #163759",
+"#PS c #163857",
+"b1x c #16385a",
+"aIj c #16385b",
+"coW c #16395c",
+"cnM c #16395d",
+"cm7 c #16395e",
+"bnk c #163a5d",
+"bjQ c #163a5e",
+"bZP c #163a5f",
+"ciV c #163a60",
+"aSr c #163b59",
+"bO5 c #163b5e",
+"aZx c #163b5f",
+"#Af c #163b60",
+"cuK c #163b61",
+"crX c #163c60",
+"#SW c #163c61",
+"byy c #163c62",
+"#Iu c #163c63",
+"b4x c #163d62",
+"#YE c #163d63",
+"#S# c #163d64",
+"cxO c #163d65",
+"aXe c #163e5f",
+"bv# c #163e64",
+"#wy c #163e65",
+"#UJ c #163e66",
+"bZ# c #163e67",
+"#Ew c #163f66",
+"#0J c #163f67",
+"#7s c #163f68",
+"aNb c #164064",
+"b7y c #164067",
+"aV2 c #164068",
+"bsZ c #164069",
+"aXr c #16416a",
+"#Vs c #16416b",
+"bQ9 c #16436d",
+"aXY c #16446b",
+"bip c #164472",
+"bky c #164572",
+"bQd c #164573",
+"aFG c #164773",
+"bZ. c #164774",
+"a#I c #164775",
+"aVW c #164977",
+"aVa c #164a77",
+"aEv c #164a78",
+"bSk c #165a8b",
+".9l c #170b11",
+".5z c #170b12",
+"#gh c #170d13",
+".0e c #170f17",
+".Ti c #171017",
+".WM c #171118",
+".Tj c #17121a",
+".7w c #171820",
+"#nO c #171e25",
+".Wr c #171f26",
+".y1 c #171f27",
+".Yf c #172026",
+".wE c #172027",
+".PD c #172028",
+".uT c #172128",
+".t6 c #172129",
+"#iY c #17212a",
+"#pu c #172228",
+"bnS c #172229",
+".bU c #17222a",
+".1w c #17222b",
+"#uT c #17222c",
+"#.u c #172328",
+"blI c #17232a",
+".bV c #17232b",
+".c2 c #17232c",
+"#sP c #17232d",
+"#fI c #172428",
+".dX c #17242b",
+".#v c #17242c",
+"Qtb c #17242d",
+"Qt# c #17242e",
+".yS c #17252c",
+".ai c #17252d",
+".bO c #17252e",
+"aBw c #17252f",
+"auF c #172530",
+".cp c #17262e",
+".sq c #17262f",
+".ed c #172630",
+"#7d c #172631",
+"bcI c #172632",
+"bBd c #17272f",
+"ayi c #172730",
+".j0 c #172731",
+"bEc c #172732",
+"buL c #172733",
+"aF6 c #172831",
+"aGE c #172832",
+"a8f c #172834",
+"bdO c #172835",
+"bkE c #172836",
+"b3N c #172839",
+"b3O c #17283a",
+"#qv c #17292f",
+"a9v c #172931",
+"bod c #172936",
+"bpL c #172938",
+"b3P c #172939",
+"b3J c #17293a",
+"b3C c #17293b",
+"bsR c #172a37",
+"bob c #172a38",
+"bpM c #172a39",
+"#4F c #172b39",
+"#L5 c #172b3b",
+"b5i c #172b3e",
+"cpJ c #172b3f",
+"boM c #172c3c",
+"bam c #172c3d",
+"chg c #172c3f",
+"b5b c #172c40",
+"bqe c #172d3f",
+"bkG c #172d41",
+"ciZ c #172d42",
+"#K9 c #172e42",
+"ba6 c #172f43",
+"#Qo c #172f44",
+"a92 c #172f45",
+"cwr c #173047",
+"aV4 c #173148",
+"byz c #17314a",
+"cgw c #173247",
+"#XE c #173249",
+"cus c #17324b",
+"brB c #17324c",
+"cx9 c #17334b",
+"coz c #17334d",
+"cjj c #17334f",
+"bLI c #17344a",
+"#SS c #17344c",
+"cuv c #17344e",
+"cgk c #17344f",
+"ctE c #17354e",
+"cc2 c #17354f",
+"b7L c #173550",
+"b9B c #173551",
+"cqt c #173552",
+"cuu c #17364f",
+"cq8 c #173650",
+"c.5 c #173651",
+"cas c #173652",
+"#Ok c #173653",
+"cjf c #173751",
+"b77 c #173752",
+"cb0 c #173753",
+"b3o c #173858",
+"bTU c #17385b",
+"bYT c #17385c",
+"cnT c #17395d",
+"buq c #173a5c",
+"cw9 c #173a5d",
+"b1y c #173b5c",
+"bt9 c #173b5f",
+"b7u c #173b60",
+"aVf c #173c5f",
+"bqc c #173c60",
+"bpJ c #173c61",
+"bZH c #173c62",
+"aWv c #173d5d",
+"bwu c #173d61",
+"#Cs c #173d62",
+"byT c #173e63",
+"#TO c #173e64",
+"bsY c #173e65",
+"aVM c #173f60",
+"b4w c #173f64",
+"aJD c #173f65",
+"bAD c #173f66",
+"#xx c #173f67",
+"#XH c #173f68",
+"#ym c #174065",
+"ciW c #174067",
+"#Jp c #174068",
+"#Z7 c #174069",
+"byS c #17406a",
+"#TQ c #174169",
+"#4. c #17416a",
+"#HJ c #17416b",
+"cdF c #17416d",
+"bB6 c #174268",
+"bMi c #17426a",
+"#SZ c #17426b",
+"bl# c #17426c",
+"b4s c #17426d",
+"bH1 c #17426e",
+"#dq c #174348",
+"crM c #17436c",
+"cmD c #17436e",
+"blD c #17446d",
+"bkx c #174471",
+"aX8 c #174571",
+"bgU c #174572",
+"b7n c #174573",
+"aWK c #174672",
+"bgT c #174674",
+"aFF c #174773",
+"aa0 c #174774",
+"a#F c #174775",
+"bJR c #174875",
+"#6D c #174976",
+"aE8 c #174977",
+"aYP c #174978",
+"a0a c #174a76",
+"aE9 c #174a77",
+"#5M c #174a78",
+"bTE c #174e7d",
+"aID c #175793",
+"bTD c #175887",
+"bSl c #175c8d",
+".1R c #180c13",
+".5A c #180d13",
+".5B c #180d14",
+".U6 c #180e16",
+".1S c #180f16",
+".7r c #181016",
+".U8 c #181219",
+".6x c #181815",
+"#kx c #181820",
+".9s c #181a22",
+".UQ c #181f26",
+"#y5 c #181f27",
+".1t c #182025",
+".Ws c #182028",
+".IN c #182128",
+".uS c #182129",
+".t5 c #182229",
+".t4 c #18222a",
+".CA c #18222b",
+"#WH c #18222c",
+".NV c #18232a",
+".yV c #18232b",
+".td c #18232c",
+"bcj c #18232d",
+".4K c #18242a",
+"biD c #18242b",
+".ct c #18242c",
+".mi c #18242d",
+"Qtg c #18242e",
+".Om c #18242f",
+".hL c #18252c",
+".#u c #18252d",
+"Qta c #18252e",
+".cu c #18252f",
+"bpP c #182530",
+"aOi c #182531",
+".Kw c #18262d",
+".#t c #18262e",
+"QtV c #18262f",
+".qZ c #182630",
+"bns c #182631",
+"cxU c #182635",
+".fM c #18272f",
+"#I7 c #182730",
+".df c #182731",
+"boc c #182732",
+"bqj c #182733",
+".st c #182831",
+".bk c #182832",
+"#18 c #182833",
+"bjm c #182834",
+"bVd c #182835",
+"bYC c #182836",
+"a30 c #182932",
+"#6k c #182935",
+"bts c #182936",
+"b0o c #182938",
+"b1Z c #182939",
+"b12 c #18293a",
+"bqu c #182a31",
+"aFy c #182a34",
+"bAg c #182a35",
+"#Uu c #182a38",
+"buK c #182a39",
+"#4z c #182b39",
+"a5v c #182b3a",
+"bkF c #182b3b",
+"bwy c #182b3c",
+"crP c #182b3d",
+"aAu c #182c38",
+"#Qf c #182c39",
+"aDd c #182c3a",
+"bFN c #182c3b",
+"bzx c #182c3c",
+"btt c #182c3d",
+"bnr c #182d3e",
+"by5 c #182d3f",
+"b5j c #182d40",
+"cpI c #182d41",
+"coq c #182d42",
+"aCk c #182e40",
+"bsN c #182e41",
+"ciY c #182e42",
+"cky c #182e43",
+"bw8 c #182f41",
+"#WR c #182f42",
+"bw7 c #183045",
+"boN c #183046",
+"cep c #183148",
+"#L4 c #183248",
+"#19 c #18334a",
+"#RY c #18334b",
+"br8 c #18334c",
+"#Nz c #18334e",
+"coG c #18334f",
+"#2I c #18344c",
+"bwx c #18344d",
+"#vu c #18344e",
+"cjH c #18344f",
+"a9r c #183550",
+"aCj c #18364f",
+"cut c #183650",
+"cc3 c #183652",
+"cws c #18374f",
+"cvs c #183750",
+"cf3 c #183751",
+"b71 c #183752",
+"bqf c #183753",
+"bu# c #183754",
+"bSt c #18375a",
+"b9A c #183852",
+"b7K c #183853",
+"cnH c #183854",
+"#9# c #18385b",
+"bKt c #183953",
+"chW c #183955",
+"cda c #183957",
+"bi6 c #183959",
+"crg c #18395c",
+"aUs c #183a5c",
+"ceY c #183b5b",
+"#SV c #183b5e",
+"bzw c #183c61",
+"brA c #183d61",
+"bsX c #183d62",
+"b4m c #183d63",
+"aDj c #183e5e",
+"bQn c #183e62",
+"bPX c #183e63",
+"cr. c #183e64",
+"clT c #183f64",
+"bZG c #183f66",
+"b4v c #183f67",
+"bPR c #184064",
+"bLR c #184066",
+"bAC c #184067",
+"#Ri c #184069",
+"bdi c #184168",
+"b2Q c #184169",
+"bGl c #18416a",
+"#ys c #18416b",
+"col c #18416c",
+"ckw c #184269",
+"con c #18426a",
+"#34 c #18426b",
+"#Vv c #18426c",
+"#5A c #18426d",
+"#WV c #18436c",
+"aUr c #18436d",
+"#3q c #18436e",
+"#2L c #18436f",
+"cva c #184370",
+"aUZ c #18446a",
+"#UK c #18446e",
+"#vy c #18446f",
+"bw5 c #184470",
+"ca. c #184471",
+"ce# c #184472",
+"cx. c #184570",
+"bs0 c #184571",
+"aSK c #184572",
+"bjf c #184573",
+"cwe c #184670",
+"aQH c #184673",
+"bkA c #184674",
+"aDX c #184772",
+"bT7 c #184774",
+"cim c #184874",
+"a#J c #184875",
+"aWG c #184876",
+"bGH c #184878",
+"aFD c #184975",
+"aN5 c #184976",
+"bIw c #184978",
+"aT8 c #184a75",
+"aF. c #184a76",
+"aOz c #184a77",
+"aNO c #184a78",
+"bFg c #184b78",
+"aNN c #184b79",
+"bix c #184c79",
+"bGG c #184c7d",
+"bCE c #184f7c",
+"#fl c #18d1d1",
+".9m c #190d13",
+".3F c #190d14",
+".0c c #190d15",
+".Yv c #190e15",
+".0d c #190f16",
+".Yx c #191017",
+".U7 c #191118",
+".ZT c #192027",
+".uY c #192029",
+".3b c #192125",
+".UP c #192229",
+".Mp c #19222a",
+"#AM c #19222b",
+".UJ c #192328",
+".1v c #19232a",
+".t3 c #19232b",
+".vL c #19232c",
+"a1F c #19232d",
+".Rd c #19242b",
+".wD c #19242c",
+".o9 c #19242d",
+"#vQ c #19242e",
+".Wg c #19252c",
+".dW c #19252d",
+".dZ c #19252e",
+".aK c #19252f",
+"#p6 c #192530",
+".mb c #19262d",
+".c3 c #19262e",
+"Qtk c #19262f",
+".aF c #192630",
+"#Ll c #192631",
+"atg c #192632",
+".NP c #19272e",
+".#s c #19272f",
+".#Q c #192730",
+".fX c #192731",
+".qg c #192732",
+".p5 c #192831",
+".o7 c #192832",
+"a8U c #192833",
+"#E4 c #192931",
+"a0p c #192932",
+"#gu c #192933",
+"bw9 c #192935",
+"bwv c #192936",
+"#Pz c #192a33",
+"aDc c #192a34",
+"aKF c #192a35",
+"aZC c #192a36",
+"buS c #192a38",
+"b0r c #192a39",
+"b10 c #192a3b",
+"aKG c #192b35",
+"aYa c #192b37",
+"#5v c #192b38",
+"bu2 c #192b39",
+"b0w c #192b3b",
+"b17 c #192b3c",
+"aTM c #192c35",
+"bwL c #192c3c",
+"csD c #192c3e",
+"azW c #192d3a",
+"bnR c #192d3c",
+"boV c #192d3d",
+"aDa c #192d3e",
+"cpP c #192d41",
+"#Xy c #192e3d",
+"aD. c #192e3f",
+"cqT c #192e41",
+"cop c #192e42",
+"ciX c #192e44",
+"bIx c #192f40",
+"aC5 c #192f41",
+"cn. c #192f43",
+"ckx c #192f44",
+"bAE c #193044",
+"bnt c #193045",
+"#iv c #193138",
+"bka c #193144",
+"bom c #193145",
+"bw6 c #193147",
+"b8g c #193249",
+"cun c #193349",
+"#uu c #19334a",
+"bua c #19334c",
+"aBq c #19344c",
+"ckV c #193450",
+"bj5 c #193549",
+"#R# c #19354d",
+"#1p c #19354e",
+"#KK c #19354f",
+"chC c #193550",
+"cx3 c #19364d",
+"c#g c #19364e",
+"#Q8 c #19364f",
+"cmT c #193651",
+"buN c #193652",
+"cxl c #19374f",
+"ctI c #193750",
+"aCE c #193751",
+"#O5 c #193752",
+"bub c #193753",
+"bjn c #193850",
+"bkD c #193851",
+"coA c #193852",
+"bBH c #193853",
+"ci5 c #193854",
+"bJC c #19385a",
+"c.4 c #193953",
+"b9z c #193954",
+"ceL c #193955",
+"#0H c #193956",
+"bQ3 c #193957",
+"clU c #193958",
+"aG. c #19395e",
+"cgc c #193a54",
+"cbY c #193a55",
+"cb1 c #193a56",
+"bvz c #193a5a",
+"b6f c #193b57",
+"aBm c #193b5b",
+"aBj c #193b5c",
+"aBk c #193c5d",
+"cpD c #193c5e",
+"bmU c #193d5f",
+"bmV c #193d61",
+"a8R c #193e62",
+"bZQ c #193f62",
+"bmT c #193f63",
+"cr# c #193f64",
+"b#F c #194065",
+"bww c #194066",
+"cfK c #194167",
+"buc c #194168",
+"bxF c #194169",
+"bW4 c #194267",
+"buM c #19426a",
+"bof c #19426b",
+"b4u c #19436c",
+"b56 c #19436d",
+"#Z6 c #19436e",
+"b7x c #19446d",
+"#6s c #19446e",
+"#4Z c #19446f",
+"bFd c #194470",
+"cqP c #194471",
+"aRt c #19456c",
+"b4t c #19456e",
+"bNE c #19456f",
+"#Zq c #194570",
+"#Sa c #194571",
+"#Ev c #194572",
+"cfE c #194573",
+"bO2 c #194670",
+"#4L c #194671",
+"#S0 c #194672",
+"aN6 c #194673",
+"aGX c #194674",
+"cw# c #194675",
+"#W. c #194773",
+"#XI c #194774",
+"aRU c #194775",
+"a5s c #194874",
+"a#E c #194875",
+"aFE c #194876",
+"bRE c #194877",
+"aNP c #194976",
+"aNL c #194977",
+"cdR c #194978",
+"b8W c #194979",
+"aQy c #194a76",
+"aNM c #194a78",
+"bNf c #194c7d",
+"bEr c #194e7c",
+"aHo c #194e81",
+"bBQ c #194f7d",
+"bDu c #19507f",
+"bGF c #195285",
+"bFh c #195384",
+"cfp c #19558a",
+"bhq c #195b97",
+"bSm c #195d8e",
+"bQZ c #196193",
+"bLz c #19699f",
+"#j8 c #196d6e",
+".WK c #1a0f16",
+".Yw c #1a0f17",
+".4x c #1a1714",
+".Ya c #1a2229",
+".S1 c #1a232b",
+".uR c #1a232c",
+"#CI c #1a232d",
+"aUz c #1a232e",
+".ZK c #1a2429",
+".KI c #1a242c",
+".jS c #1a242d",
+"#sB c #1a242e",
+".tc c #1a252d",
+".vK c #1a252e",
+"#rK c #1a252f",
+"#a3 c #1a262d",
+".dV c #1a262e",
+".s7 c #1a262f",
+".aI c #1a2630",
+".Jj c #1a2631",
+"#lU c #1a272a",
+".m9 c #1a272e",
+".bP c #1a272f",
+"QtW c #1a2730",
+".c8 c #1a2731",
+"#FD c #1a2732",
+"bSo c #1a2733",
+".G# c #1a282f",
+".#r c #1a2830",
+".t1 c #1a2831",
+".gV c #1a2832",
+".fY c #1a2833",
+"aC9 c #1a2834",
+"a4n c #1a2931",
+".ey c #1a2932",
+"##j c #1a2933",
+"aDb c #1a2934",
+"#Km c #1a2935",
+"#Go c #1a2a33",
+"azg c #1a2a34",
+"aLf c #1a2a35",
+"aBu c #1a2a37",
+"cxT c #1a2a38",
+"#lA c #1a2b32",
+"#GV c #1a2b34",
+"#GT c #1a2b35",
+"a6X c #1a2b37",
+"cwg c #1a2b3a",
+"b11 c #1a2b3b",
+"a0Y c #1a2c35",
+"aye c #1a2c36",
+"#TB c #1a2c3b",
+"b3I c #1a2c3d",
+"cuf c #1a2c3e",
+"aBt c #1a2d3c",
+"aBT c #1a2d3d",
+"aCw c #1a2d3e",
+"cts c #1a2d3f",
+"csC c #1a2d40",
+"ctr c #1a2e40",
+"azs c #1a2f3e",
+"crO c #1a2f41",
+"btx c #1a2f42",
+"cpH c #1a2f43",
+"b5k c #1a2f44",
+"b3x c #1a3044",
+"clW c #1a3045",
+"cil c #1a3142",
+"aDe c #1a3144",
+"aBs c #1a3145",
+"aD# c #1a3146",
+"aBU c #1a3248",
+"chf c #1a3249",
+"bul c #1a3348",
+"csF c #1a334a",
+"#O9 c #1a334b",
+"b6G c #1a344a",
+"aCx c #1a344b",
+"#Oj c #1a344c",
+"cfR c #1a354d",
+"bk7 c #1a354e",
+"#SU c #1a354f",
+"b8h c #1a364e",
+"cx2 c #1a364f",
+"aBi c #1a3651",
+"cmx c #1a3652",
+"cxr c #1a374f",
+"#Of c #1a3750",
+"aCJ c #1a3751",
+"cx1 c #1a3850",
+"cvv c #1a3851",
+"cnk c #1a3852",
+"#Ra c #1a3853",
+"aBV c #1a3855",
+"bT8 c #1a3856",
+"bYV c #1a385a",
+"cux c #1a3951",
+"csK c #1a3952",
+"cvr c #1a3953",
+"c.3 c #1a3954",
+"bKT c #1a3956",
+"bdg c #1a3957",
+"bkQ c #1a3a53",
+"cbU c #1a3a54",
+"b70 c #1a3a55",
+"b6q c #1a3a56",
+"#PN c #1a3a58",
+"aBo c #1a3a59",
+"crW c #1a3b54",
+"c.2 c #1a3b55",
+"b6p c #1a3b56",
+"b9y c #1a3b57",
+"aCD c #1a3b59",
+"aBl c #1a3b5a",
+"aCy c #1a3b5b",
+"bs. c #1a3c56",
+"cbR c #1a3c58",
+"cdb c #1a3c59",
+"aBn c #1a3c5c",
+"a4z c #1a3d5a",
+"b1U c #1a3d5d",
+"bhC c #1a3d60",
+"bju c #1a3e5a",
+"bpj c #1a3e5b",
+"aCC c #1a3e62",
+"b9b c #1a3f61",
+"bpI c #1a3f62",
+"aBW c #1a3f63",
+"aCO c #1a4064",
+"c#K c #1a4065",
+"aCB c #1a4066",
+"aB3 c #1a4166",
+"cfC c #1a4167",
+"bmW c #1a4267",
+"#DJ c #1a4268",
+"aCN c #1a4269",
+"aCz c #1a426a",
+"b0F c #1a4368",
+"cwQ c #1a4369",
+"aB2 c #1a436b",
+"aUq c #1a436d",
+"btw c #1a4465",
+"aCA c #1a446d",
+"bmS c #1a446e",
+"aBX c #1a456e",
+"bpf c #1a456f",
+"aB1 c #1a4570",
+"bRI c #1a4571",
+"bmd c #1a4572",
+"cdQ c #1a4670",
+"#It c #1a4671",
+"#2K c #1a4672",
+"#6r c #1a4673",
+"cbu c #1a4674",
+"bmZ c #1a4768",
+"b9f c #1a4771",
+"#Vr c #1a4772",
+"#Vw c #1a4773",
+"#WW c #1a4774",
+"bEp c #1a4775",
+"bUq c #1a4776",
+"#Sb c #1a4874",
+"#F1 c #1a4875",
+"#TR c #1a4876",
+"ca# c #1a4877",
+"a25 c #1a4974",
+"aOA c #1a4975",
+"#WY c #1a4976",
+"#3p c #1a4977",
+"#4Y c #1a4978",
+"cub c #1a497a",
+"btu c #1a4a78",
+"aB0 c #1a4a79",
+"#8m c #1a4a7a",
+"bjo c #1a4b70",
+"bk8 c #1a4b79",
+"aBY c #1a4b7a",
+"aTh c #1a4e7c",
+"aSG c #1a5383",
+"bSj c #1a5585",
+"bzf c #1a5a96",
+"bf0 c #1a5c97",
+"bQ0 c #1a6294",
+"bQ1 c #1a6295",
+"bPF c #1a6498",
+"bPG c #1a6599",
+"bgh c #1a679f",
+"bLA c #1a69a0",
+".WL c #1b1017",
+".CP c #1b1c22",
+"#rL c #1b1f27",
+"#nY c #1b2023",
+"#aC c #1b2025",
+"#tO c #1b202b",
+".G2 c #1b232e",
+".ZL c #1b242a",
+".Wt c #1b242b",
+".Re c #1b242c",
+"aiT c #1b242d",
+".tb c #1b252d",
+".ta c #1b252e",
+"#zH c #1b252f",
+".22 c #1b262a",
+".X2 c #1b262c",
+".t2 c #1b262e",
+".t# c #1b262f",
+".KE c #1b2630",
+"#Kt c #1b2631",
+".iI c #1b272f",
+".wB c #1b2730",
+".#B c #1b2731",
+"#vH c #1b2732",
+".n3 c #1b282f",
+"QtX c #1b2830",
+".cq c #1b2831",
+".cv c #1b2832",
+".wZ c #1b2833",
+"#Ow c #1b2834",
+".Ma c #1b2930",
+".bT c #1b2931",
+"#m8 c #1b2932",
+".aw c #1b2933",
+"avl c #1b2934",
+"bTG c #1b2936",
+"#qD c #1b2a2e",
+"#oR c #1b2a31",
+"#FN c #1b2a32",
+".9C c #1b2a33",
+"#gt c #1b2a34",
+"aFz c #1b2a35",
+"acQ c #1b2b34",
+"#GU c #1b2b35",
+"au6 c #1b2b36",
+"atM c #1b2b37",
+"#GW c #1b2c34",
+"b0y c #1b2c3b",
+"bYP c #1b2c3c",
+"b0x c #1b2c3d",
+"b16 c #1b2d3d",
+"#30 c #1b2e3d",
+"#ST c #1b2e3e",
+"cxV c #1b2e3f",
+"#66 c #1b2f3e",
+"#TJ c #1b2f40",
+"bx. c #1b2f41",
+"#7e c #1b303f",
+"#N7 c #1b3040",
+"#Ny c #1b3043",
+"cm9 c #1b3044",
+"#g1 c #1b3138",
+"#YA c #1b3142",
+"cpG c #1b3144",
+"clV c #1b3145",
+"aCF c #1b3246",
+"aCG c #1b3247",
+"cm8 c #1b3249",
+"aBr c #1b334a",
+"bks c #1b344a",
+"aCI c #1b344b",
+"cpd c #1b354c",
+"aB5 c #1b354d",
+"#m9 c #1b363c",
+"csE c #1b364d",
+"bwH c #1b364e",
+"byO c #1b365a",
+"#TG c #1b374e",
+"cty c #1b3750",
+"#4G c #1b3751",
+"cfh c #1b3752",
+"bW2 c #1b3759",
+"#Kl c #1b3852",
+"bvD c #1b3853",
+"aBp c #1b3854",
+"b8z c #1b3859",
+"bYU c #1b385b",
+"bG7 c #1b385d",
+"cwo c #1b3952",
+"cvd c #1b3954",
+"bty c #1b3955",
+"#Nt c #1b3956",
+"cwx c #1b3a52",
+"c#h c #1b3a53",
+"cx0 c #1b3a54",
+"#R1 c #1b3a56",
+"#32 c #1b3a57",
+"cxZ c #1b3b54",
+"cbV c #1b3b55",
+"cc7 c #1b3b56",
+"cc8 c #1b3b57",
+"cbW c #1b3c55",
+"cbX c #1b3c56",
+"b7J c #1b3c57",
+"b9x c #1b3c58",
+"aCP c #1b3c59",
+"btM c #1b3c5c",
+"cvq c #1b3d57",
+"b6o c #1b3d58",
+"cbQ c #1b3d59",
+"bB7 c #1b3d5b",
+"aB4 c #1b3d5e",
+"aCL c #1b3d5f",
+"a2d c #1b3e5c",
+"#TN c #1b3f61",
+"bo. c #1b3f62",
+"bvE c #1b405c",
+"#4M c #1b4063",
+"cr4 c #1b4065",
+"aCM c #1b4165",
+"#RX c #1b4167",
+"#7w c #1b4267",
+"bg# c #1b4269",
+"bXy c #1b426a",
+"aYS c #1b4369",
+"#BR c #1b436a",
+"a#K c #1b436b",
+"bUp c #1b436d",
+"bIT c #1b4469",
+"#38 c #1b446b",
+"#UG c #1b446c",
+"a1t c #1b446d",
+"bkH c #1b456c",
+"#36 c #1b456e",
+"#5D c #1b4670",
+"#39 c #1b4671",
+"blH c #1b4768",
+"#Vq c #1b4772",
+"#35 c #1b4773",
+"a0d c #1b4774",
+"c#6 c #1b4775",
+"a1w c #1b4873",
+"#yq c #1b4874",
+"#Wa c #1b4875",
+"#JX c #1b4876",
+"csz c #1b4877",
+"c.C c #1b4974",
+"#5C c #1b4975",
+"#Jo c #1b4976",
+"#JW c #1b4977",
+"aV3 c #1b4978",
+"#WX c #1b4a77",
+"#8n c #1b4a78",
+"#xt c #1b4a79",
+"bpO c #1b4b71",
+"#XM c #1b4b78",
+"#5B c #1b4b79",
+"#9k c #1b4b7a",
+"aBZ c #1b4b7b",
+"bnq c #1b4c77",
+"bjg c #1b4c7b",
+"bj6 c #1b4d72",
+"blE c #1b4d7d",
+"bma c #1b4e7e",
+"#lB c #1b5153",
+"bla c #1b5182",
+"bw1 c #1b5184",
+"bqW c #1b527c",
+"#Tv c #1b528c",
+"bk# c #1b537d",
+"ccz c #1b5387",
+"bOl c #1b5889",
+"bQY c #1b588a",
+"baX c #1b5994",
+"bGE c #1b5a8e",
+"bdD c #1b5a94",
+"bQ2 c #1b5d90",
+"bl. c #1b5e92",
+"bPH c #1b659b",
+"bOi c #1b689d",
+"bOk c #1b6a9f",
+"bLB c #1b6ba1",
+"bKp c #1b6ba3",
+"bJk c #1b6fa6",
+"#ez c #1c0f16",
+".U5 c #1c1017",
+"#d9 c #1c161d",
+".wI c #1c1e25",
+"#w5 c #1c242d",
+"aWc c #1c242f",
+".IM c #1c252d",
+"aKE c #1c252f",
+".Wv c #1c262e",
+".xF c #1c262f",
+"bbd c #1c2630",
+".sw c #1c272f",
+".sv c #1c2730",
+".vI c #1c2731",
+"#cb c #1c2825",
+".gM c #1c2830",
+"QtY c #1c2831",
+".aL c #1c2832",
+"#Fm c #1c2833",
+"#.l c #1c2837",
+".ar c #1c2930",
+".fL c #1c2931",
+"QtZ c #1c2932",
+".bW c #1c2933",
+"#c2 c #1c2934",
+"#yz c #1c2935",
+".bf c #1c2a31",
+".bg c #1c2a32",
+"#a4 c #1c2a33",
+".#E c #1c2a34",
+"a.R c #1c2a35",
+"aEo c #1c2a36",
+"#lz c #1c2b33",
+"a4o c #1c2b34",
+".DH c #1c2b35",
+"#TK c #1c2b36",
+"arK c #1c2b37",
+"#GX c #1c2c34",
+"#Bw c #1c2c35",
+"asU c #1c2c37",
+"bYH c #1c2c3a",
+"b0z c #1c2c3d",
+".WY c #1c2d35",
+".P2 c #1c2d36",
+"avR c #1c2d38",
+"aB6 c #1c2d3b",
+"bYQ c #1c2d3c",
+"b3H c #1c2d3e",
+"b3E c #1c2d3f",
+"a6V c #1c2e3b",
+"bCA c #1c2e3e",
+"b3F c #1c2e3f",
+"ctq c #1c3042",
+"cqR c #1c3043",
+"cqS c #1c3044",
+"cpF c #1c3045",
+"#vt c #1c3143",
+"coo c #1c3144",
+"cpE c #1c3145",
+"cnc c #1c3146",
+"#oS c #1c3239",
+"#UA c #1c3243",
+"crs c #1c3246",
+"bU9 c #1c3248",
+"aCH c #1c3348",
+"bpV c #1c343f",
+"cqQ c #1c354c",
+"b5l c #1c364b",
+"b5. c #1c364c",
+"b5a c #1c374d",
+"b6H c #1c374f",
+"coS c #1c3751",
+"cjE c #1c3753",
+"b9Z c #1c3952",
+"bwG c #1c3954",
+"b8i c #1c3a52",
+"bee c #1c3a55",
+"aCK c #1c3a57",
+"ca0 c #1c3a5e",
+"cwy c #1c3b53",
+"cxY c #1c3b54",
+"cay c #1c3b55",
+"cp4 c #1c3b56",
+"bup c #1c3b59",
+"ctH c #1c3c55",
+"b9P c #1c3c56",
+"b9O c #1c3c57",
+"chV c #1c3c58",
+"bsW c #1c3c5c",
+"ccr c #1c3c5d",
+"b9I c #1c3d57",
+"b9w c #1c3d58",
+"b40 c #1c3d59",
+"bu1 c #1c3d5c",
+"b7I c #1c3e59",
+"b4X c #1c3e5a",
+"b4Z c #1c3e5b",
+"b4Y c #1c3f5b",
+"cbP c #1c3f5c",
+"b3p c #1c3f5d",
+"#Qn c #1c3f5f",
+"#3m c #1c3f60",
+"#Zn c #1c3f61",
+"boJ c #1c3f62",
+"#K8 c #1c4061",
+"bqb c #1c4062",
+"bn9 c #1c4063",
+"bO1 c #1c4064",
+"buT c #1c4162",
+"bND c #1c4166",
+"b08 c #1c4168",
+"bZR c #1c4265",
+"bVV c #1c4266",
+"#KI c #1c4267",
+"aDi c #1c4268",
+"b5N c #1c4269",
+"a3M c #1c4368",
+"a3Q c #1c4369",
+"bOY c #1c436b",
+"a4C c #1c446a",
+"bMh c #1c446b",
+"caO c #1c456b",
+"cbp c #1c456c",
+"a6U c #1c456d",
+"bUl c #1c456e",
+"bKD c #1c466b",
+"#A9 c #1c466d",
+"aP8 c #1c4670",
+"aN9 c #1c476f",
+"b1i c #1c4771",
+"aSF c #1c4773",
+"aX7 c #1c4775",
+"#Vp c #1c4873",
+"a4B c #1c4874",
+"aSN c #1c4974",
+"bPI c #1c4a75",
+"#JY c #1c4a78",
+"#8d c #1c4a79",
+"bFb c #1c4a7a",
+"a0g c #1c4b78",
+"#yr c #1c4b79",
+"#V9 c #1c4b7a",
+"#3o c #1c4b7b",
+"#1s c #1c4c7b",
+"#xw c #1c4c7c",
+"cw5 c #1c4c7e",
+"bol c #1c4d75",
+"bmN c #1c4d7c",
+"#WU c #1c4d7d",
+"bs1 c #1c4d7e",
+"bJg c #1c4d80",
+"#XK c #1c4e7e",
+"bAL c #1c4e7f",
+"boU c #1c4f77",
+"bSn c #1c4f7f",
+"bw2 c #1c4f80",
+"bNw c #1c5085",
+"aRN c #1c517e",
+"aNJ c #1c517f",
+"boO c #1c5283",
+"bOF c #1c5287",
+"bvF c #1c537d",
+"bw0 c #1c5385",
+"bjt c #1c547f",
+"blA c #1c5486",
+"blf c #1c557f",
+"bnl c #1c5586",
+"biy c #1c5588",
+"bjs c #1c5681",
+"boQ c #1c5688",
+"bog c #1c5789",
+"c#R c #1c5791",
+"bjr c #1c5883",
+"bhL c #1c588b",
+"a7g c #1c5994",
+"a9L c #1c5995",
+"bmY c #1c5a88",
+"blb c #1c5a8d",
+"bj3 c #1c5a8e",
+"bmg c #1c5b8a",
+"byg c #1c5b94",
+"bwN c #1c5d8d",
+"bmR c #1c5d91",
+"bg0 c #1c5d92",
+"bfy c #1c5e92",
+"bMW c #1c6295",
+"bnp c #1c6296",
+"bKo c #1c6299",
+"bj7 c #1c6397",
+"bGD c #1c6399",
+"bOh c #1c6499",
+"bLH c #1c659a",
+"bCF c #1c669b",
+"bOj c #1c689c",
+"bKs c #1c69a1",
+"bMT c #1c6aa0",
+"bMU c #1c6ba1",
+"bLF c #1c6ca3",
+"bFi c #1c6ca4",
+"bKr c #1c6da4",
+"bLG c #1c6da5",
+"bJn c #1c6ea5",
+"bFk c #1c6ea6",
+"bFj c #1c6ea7",
+"bJi c #1c6fa6",
+"bFl c #1c6fa7",
+"buP c #1c70a8",
+"bJp c #1c70a9",
+"bwB c #1c71a9",
+"bqh c #1c71aa",
+"bFr c #1c71ab",
+"bwS c #1c72aa",
+"bg2 c #1c73ad",
+"#bK c #1c797c",
+".Wx c #1d1f27",
+".YD c #1d1f2a",
+"#hw c #1d2327",
+".jr c #1d232c",
+"a8j c #1d2530",
+"aCa c #1d262d",
+".S2 c #1d262e",
+".IJ c #1d262f",
+"aJs c #1d2630",
+"bCc c #1d2631",
+"aHq c #1d272e",
+"#Kn c #1d272f",
+".sx c #1d2730",
+"aKH c #1d2732",
+".NO c #1d282f",
+".AI c #1d2830",
+".su c #1d2831",
+".rP c #1d2832",
+"#r4 c #1d2833",
+".ma c #1d2931",
+".dF c #1d2932",
+"Qtf c #1d2933",
+".gW c #1d2934",
+".cs c #1d2a31",
+".fi c #1d2a32",
+"Qt0 c #1d2a33",
+".#P c #1d2a34",
+"#id c #1d2a35",
+"#bd c #1d2a36",
+".be c #1d2b32",
+".5Q c #1d2b34",
+".w0 c #1d2b35",
+"#qd c #1d2b36",
+"bTO c #1d2b37",
+".YJ c #1d2c35",
+"ayf c #1d2c36",
+"atL c #1d2c37",
+"auu c #1d2c38",
+"bV# c #1d2c3a",
+"cxS c #1d2c3b",
+".9B c #1d2d35",
+".0r c #1d2d36",
+"#Ke c #1d2d37",
+"#Py c #1d2d38",
+"bJU c #1d2d39",
+"bwK c #1d2d3b",
+"bYR c #1d2d3d",
+"aqQ c #1d2e38",
+"bAF c #1d2e3b",
+"b0n c #1d2e3d",
+"bYS c #1d2e3e",
+"a1B c #1d2f38",
+"arN c #1d2f39",
+"axd c #1d2f3a",
+"cxa c #1d2f3e",
+"b3G c #1d2f3f",
+"#ut c #1d2f40",
+"cue c #1d2f41",
+"bbD c #1d3039",
+"aVN c #1d303a",
+"cvf c #1d3041",
+"ctp c #1d3043",
+"cud c #1d3142",
+"crN c #1d3143",
+"csB c #1d3144",
+"#8W c #1d3240",
+"aCQ c #1d3243",
+"#Oi c #1d3245",
+"csA c #1d3348",
+"#XC c #1d3446",
+"bXd c #1d344b",
+"cug c #1d364b",
+"clH c #1d364c",
+"a8h c #1d364e",
+"bx1 c #1d3659",
+"#qw c #1d373a",
+"a3G c #1d374a",
+"aDf c #1d3751",
+"crV c #1d3853",
+"a.g c #1d385c",
+"aDk c #1d3952",
+"c.t c #1d3953",
+"clb c #1d3954",
+"cp9 c #1d3955",
+"cl8 c #1d3b55",
+"beQ c #1d3b58",
+"b8U c #1d3b5d",
+"#8o c #1d3c59",
+"b90 c #1d3d56",
+"c#i c #1d3d57",
+"cax c #1d3d58",
+"b2G c #1d3d5b",
+"b9Q c #1d3e57",
+"b7Z c #1d3e58",
+"caz c #1d3e59",
+"bRH c #1d3e5c",
+"#5E c #1d3e5d",
+"bQl c #1d3e5f",
+"c#b c #1d3f58",
+"c#c c #1d3f59",
+"b9v c #1d3f5a",
+"b9u c #1d3f5b",
+"a6S c #1d3f5f",
+"#MJ c #1d3f60",
+"b51 c #1d3f61",
+"cxk c #1d405b",
+"b4W c #1d405c",
+"b7H c #1d405d",
+"boI c #1d4062",
+"a.r c #1d4063",
+"b1. c #1d4064",
+"cdc c #1d415f",
+"#V7 c #1d4163",
+"#Vo c #1d4265",
+"#37 c #1d4267",
+"bOZ c #1d4268",
+"bQj c #1d4368",
+"bRD c #1d4369",
+"bUm c #1d436a",
+"bZS c #1d4467",
+"#SN c #1d4469",
+"#WS c #1d446a",
+"bjW c #1d446b",
+"aFH c #1d446e",
+"b7z c #1d4569",
+"b36 c #1d456d",
+"bGo c #1d466b",
+"bXZ c #1d466c",
+"aUp c #1d466d",
+"bXv c #1d466e",
+"#Q7 c #1d466f",
+"aPp c #1d4773",
+"c#w c #1d4870",
+"#SR c #1d4871",
+"cg7 c #1d4874",
+"aRM c #1d4975",
+"bwM c #1d4a70",
+"cds c #1d4b77",
+"bv6 c #1d4b78",
+"bH0 c #1d4b79",
+"b07 c #1d4c79",
+"cgH c #1d4c7a",
+"bGk c #1d4c7b",
+"#XG c #1d4c7c",
+"cua c #1d4c7d",
+"cmz c #1d4d7b",
+"bSN c #1d4d7c",
+"#Fl c #1d4d7d",
+"#1q c #1d4d7e",
+"#6v c #1d4e7e",
+"bvb c #1d4e7f",
+"bAK c #1d4f7f",
+"#0K c #1d4f80",
+"cwa c #1d4f81",
+"bm# c #1d5082",
+"bmO c #1d5182",
+"bwZ c #1d5284",
+"cfq c #1d5285",
+"bmX c #1d537c",
+"bjp c #1d557f",
+"bjq c #1d5782",
+"bJX c #1d5894",
+"bMP c #1d598e",
+"bb2 c #1d5994",
+"a9e c #1d5995",
+"bnM c #1d5a8d",
+"aGt c #1d5a95",
+"bkI c #1d5b8f",
+"bBo c #1d5b95",
+"aYG c #1d5b96",
+"bjh c #1d5d92",
+"bfj c #1d5d97",
+"bPE c #1d5e90",
+"bme c #1d5e94",
+"boP c #1d6096",
+"bhP c #1d6195",
+"blF c #1d6298",
+"bvG c #1d6397",
+"bmP c #1d6399",
+"blc c #1d6499",
+"bsQ c #1d659a",
+"bpi c #1d669b",
+"bFn c #1d679d",
+"bkK c #1d679f",
+"bDv c #1d689f",
+"bmb c #1d69a1",
+"bMV c #1d6aa0",
+"bFm c #1d6aa1",
+"bnm c #1d6aa3",
+"bwO c #1d6ba2",
+"bBR c #1d6ca3",
+"bkL c #1d6ca4",
+"bkC c #1d6da5",
+"bmQ c #1d6da6",
+"bJo c #1d6ea5",
+"bzz c #1d6ea6",
+"bld c #1d6ea7",
+"bgj c #1d6ea8",
+"bg3 c #1d6ea9",
+"bJj c #1d6fa6",
+"bwC c #1d6fa7",
+"bhM c #1d6fa8",
+"bpN c #1d6faa",
+"bvB c #1d70a8",
+"bji c #1d70a9",
+"bj8 c #1d70aa",
+"blC c #1d71a9",
+"bg1 c #1d71aa",
+"bk. c #1d71ab",
+"bqi c #1d71ac",
+"bwR c #1d72aa",
+"bhN c #1d72ab",
+"bhO c #1d72ac",
+"bjj c #1d72ad",
+"bwQ c #1d73ab",
+"biA c #1d73ad",
+"bgi c #1d73ae",
+"bmf c #1d74af",
+"#gi c #1e0e16",
+"#ud c #1e131b",
+".IP c #1e1e24",
+".Rj c #1e1f25",
+"#t4 c #1e2127",
+".RE c #1e232e",
+"aYf c #1e242f",
+"aJr c #1e2630",
+".1k c #1e272b",
+".ZG c #1e272c",
+".Rf c #1e272f",
+"alu c #1e2732",
+".IL c #1e282f",
+".IK c #1e2830",
+".CH c #1e2831",
+".KD c #1e2832",
+"aGH c #1e2833",
+".CG c #1e2931",
+".fq c #1e2932",
+".aH c #1e2933",
+"Qtl c #1e2934",
+"#hp c #1e2a26",
+".Mf c #1e2a31",
+".c7 c #1e2a32",
+".aG c #1e2a33",
+"Qte c #1e2a34",
+"#r3 c #1e2a35",
+"aGh c #1e2a36",
+".gL c #1e2b32",
+".fK c #1e2b33",
+"Qt1 c #1e2b34",
+".#F c #1e2b35",
+"#bj c #1e2b36",
+"#c3 c #1e2b37",
+"bV. c #1e2b39",
+".eT c #1e2c33",
+"a.F c #1e2c34",
+".RI c #1e2c35",
+".3U c #1e2c36",
+"aiv c #1e2c37",
+"#mz c #1e2d35",
+".RJ c #1e2d36",
+"#Nk c #1e2d37",
+"anI c #1e2d38",
+"bWZ c #1e2d3e",
+"aGD c #1e2e37",
+"#us c #1e2e38",
+"#NX c #1e2e39",
+"bWY c #1e2e3d",
+"bYO c #1e2e3e",
+"aUL c #1e2f38",
+"adO c #1e2f39",
+"bcg c #1e2f3a",
+"#Nx c #1e2f3e",
+"cx# c #1e2f40",
+"bhg c #1e3039",
+"awA c #1e303a",
+"bHs c #1e3040",
+"cuc c #1e3042",
+"cve c #1e3141",
+"cto c #1e3143",
+"a9u c #1e3240",
+"cwf c #1e3244",
+"byP c #1e3254",
+"#TL c #1e3346",
+"#70 c #1e3443",
+"blz c #1e3447",
+"ckE c #1e3448",
+"#QY c #1e3646",
+"c.w c #1e364d",
+"#xo c #1e3759",
+"b5m c #1e384e",
+"b0A c #1e385a",
+"cjD c #1e3954",
+"b5# c #1e3a51",
+"ckO c #1e3a55",
+"#TM c #1e3a56",
+"bUo c #1e3b55",
+"#4N c #1e3b56",
+"#9l c #1e3c58",
+"a4E c #1e3c59",
+"cga c #1e3d58",
+"#6w c #1e3d5a",
+"b2F c #1e3d5b",
+"cvA c #1e3e57",
+"chP c #1e3e59",
+"csL c #1e3f58",
+"c#a c #1e3f59",
+"caA c #1e3f5a",
+"ceo c #1e3f5c",
+"b7i c #1e3f5f",
+"c#j c #1e4059",
+"b72 c #1e405a",
+"c#k c #1e405b",
+"b9t c #1e405c",
+"ccX c #1e405d",
+"byx c #1e4062",
+"#UF c #1e4063",
+"caB c #1e415b",
+"cxj c #1e415c",
+"b7G c #1e415e",
+"btK c #1e4162",
+"#6C c #1e4163",
+"car c #1e425f",
+"c.1 c #1e4360",
+"cbO c #1e4361",
+"b7E c #1e4362",
+"btz c #1e4366",
+"bvV c #1e4367",
+"#4X c #1e4368",
+"ceZ c #1e4464",
+"bwF c #1e4468",
+"#Oe c #1e4469",
+"b7A c #1e4569",
+"buR c #1e456a",
+"#O4 c #1e456b",
+"#UE c #1e456c",
+"bXY c #1e466c",
+"aCi c #1e466d",
+"bdP c #1e466e",
+"bXX c #1e476d",
+"b2J c #1e476e",
+"#R2 c #1e476f",
+"a2h c #1e4772",
+"aQq c #1e4773",
+"bSY c #1e486e",
+"cbd c #1e4870",
+"aA3 c #1e4872",
+"aYR c #1e4873",
+"bXc c #1e4874",
+"bGp c #1e496e",
+"bY0 c #1e496f",
+"aGu c #1e4974",
+"bZF c #1e4a72",
+"#Lz c #1e4b77",
+"buU c #1e4c74",
+"b9g c #1e4c78",
+"cfF c #1e4c7a",
+"aPz c #1e4d7b",
+"ccA c #1e4e7e",
+"bQX c #1e4e7f",
+"bUb c #1e4f7f",
+"boR c #1e4f80",
+"#9j c #1e4f81",
+"bBP c #1e5080",
+"#XN c #1e5081",
+"#Zr c #1e5082",
+"#XL c #1e5083",
+"bPB c #1e5084",
+"bBM c #1e5182",
+"#7v c #1e5183",
+"cdP c #1e5285",
+"cxL c #1e5286",
+"bx4 c #1e5386",
+"bw4 c #1e548a",
+"bg4 c #1e5583",
+"aWu c #1e5587",
+"aVL c #1e5689",
+"cgG c #1e568b",
+"bAM c #1e588b",
+"b83 c #1e5893",
+"b.Q c #1e5994",
+"aHl c #1e5995",
+"bZe c #1e5a94",
+"ac6 c #1e5a95",
+"beE c #1e5b95",
+"aZ0 c #1e5b96",
+"a2T c #1e5c96",
+"aZk c #1e5c97",
+"aax c #1e5d97",
+"a#j c #1e5e97",
+"bvN c #1e5e98",
+"aeP c #1e5e99",
+"bjk c #1e6093",
+"bMS c #1e6097",
+"bFo c #1e6297",
+"biB c #1e6399",
+"bkJ c #1e639a",
+"bLC c #1e659c",
+"bFp c #1e669c",
+"bJh c #1e679e",
+"boT c #1e679f",
+"bFq c #1e699f",
+"bGJ c #1e6aa1",
+"bzC c #1e6ba2",
+"bBS c #1e6ba3",
+"bkB c #1e6ba4",
+"bk9 c #1e6ca4",
+"bnn c #1e6ca6",
+"biz c #1e6da5",
+"bkP c #1e6da6",
+"byE c #1e6ea6",
+"bxK c #1e6fa6",
+"bxQ c #1e6fa7",
+"bDw c #1e6fa8",
+"bph c #1e6faa",
+"bAQ c #1e70a8",
+"bkO c #1e70a9",
+"bvH c #1e70aa",
+"bud c #1e71a8",
+"blB c #1e71a9",
+"bkN c #1e71aa",
+"bmc c #1e71ab",
+"bkM c #1e72ab",
+"bwP c #1e72ac",
+"brD c #1e72ad",
+"bj9 c #1e73ae",
+"blG c #1e73af",
+"ble c #1e74af",
+"#qS c #1f1a23",
+"##e c #1f222c",
+"aVk c #1f2430",
+"#pY c #1f252e",
+"baw c #1f2530",
+"#Cb c #1f262f",
+"bx7 c #1f2631",
+"bxb c #1f2731",
+"bmv c #1f2732",
+"#p# c #1f2830",
+"#Nf c #1f2832",
+"aHU c #1f2833",
+"#HB c #1f2932",
+"Qto c #1f2933",
+"Qtn c #1f2934",
+".uP c #1f2a33",
+"Qtm c #1f2a34",
+".b5 c #1f2a35",
+".o0 c #1f2b33",
+".o8 c #1f2b34",
+"Qtp c #1f2b35",
+".bj c #1f2b36",
+"#G5 c #1f2b37",
+".bv c #1f2c33",
+".dU c #1f2c34",
+"Qt2 c #1f2c35",
+".#T c #1f2c36",
+"#be c #1f2c37",
+"#zw c #1f2c38",
+".Eb c #1f2d34",
+"#bG c #1f2d35",
+".Vh c #1f2d36",
+"#NW c #1f2d37",
+"ajP c #1f2d38",
+"#Nw c #1f2d39",
+"#tm c #1f2e38",
+"ali c #1f2e39",
+"#tn c #1f2e3b",
+"#fj c #1f2f37",
+"aPM c #1f2f38",
+"arM c #1f2f39",
+"arL c #1f2f3a",
+"cxR c #1f2f3f",
+"awz c #1f303a",
+"baH c #1f303b",
+".BQ c #1f303c",
+"bWN c #1f303d",
+"bYB c #1f303e",
+"cxQ c #1f303f",
+"b15 c #1f3040",
+"bTF c #1f3041",
+"aZq c #1f313a",
+"aZ3 c #1f313b",
+"cjU c #1f313e",
+"#O8 c #1f3141",
+"cxP c #1f3143",
+"cgI c #1f3344",
+"aCl c #1f3345",
+"a6W c #1f3545",
+"ca1 c #1f354a",
+"#1k c #1f3748",
+"#WN c #1f3749",
+"aG0 c #1f3759",
+"aXt c #1f384a",
+"a#L c #1f384f",
+"a0P c #1f3850",
+"cwh c #1f3950",
+"c.v c #1f3951",
+"aa1 c #1f3952",
+"a3P c #1f3a53",
+"bL. c #1f3a54",
+"bVU c #1f3a55",
+"bXx c #1f3b54",
+"a2c c #1f3b55",
+"cc4 c #1f3b56",
+"bBb c #1f3c56",
+"a58 c #1f3c58",
+"b6I c #1f3d55",
+"cpR c #1f3d58",
+"a5t c #1f3d59",
+"cft c #1f3d5a",
+"aDg c #1f3d5f",
+"b8j c #1f3e58",
+"b91 c #1f3f59",
+"aX5 c #1f3f5e",
+"cuw c #1f4058",
+"ctJ c #1f4059",
+"b9N c #1f405a",
+"aFY c #1f405f",
+"cuB c #1f4159",
+"b9R c #1f415a",
+"b9K c #1f415b",
+"b7V c #1f415c",
+"cwn c #1f415d",
+"brz c #1f4163",
+"c#l c #1f425c",
+"b9s c #1f425e",
+"cum c #1f425f",
+"b3g c #1f4260",
+"bqU c #1f4264",
+"caC c #1f435d",
+"b3d c #1f4360",
+"b3e c #1f4361",
+"b7F c #1f4362",
+"b3f c #1f4461",
+"b9r c #1f4462",
+"bvW c #1f4467",
+"aA2 c #1f4468",
+"b7D c #1f4564",
+"b7B c #1f4566",
+"buf c #1f456a",
+"aQG c #1f4570",
+"b7C c #1f4666",
+"bvU c #1f466c",
+"aNQ c #1f4672",
+"b85 c #1f476e",
+"bGr c #1f486c",
+"bX0 c #1f486e",
+"#PM c #1f4970",
+"#4H c #1f4972",
+"bgS c #1f4973",
+"bGq c #1f4a6f",
+"byU c #1f4a70",
+"bXA c #1f4a71",
+"#XF c #1f4a74",
+"bXW c #1f4b71",
+"cwG c #1f4b72",
+"#2J c #1f4c78",
+"bv7 c #1f4d7a",
+"#33 c #1f4d7b",
+"clS c #1f4d7c",
+"aPC c #1f4e7a",
+"aPo c #1f4e7b",
+"ckt c #1f4e7c",
+"bj4 c #1f4f76",
+"cvb c #1f4f7e",
+"bcJ c #1f4f7f",
+"a2l c #1f507e",
+"ccL c #1f507f",
+"bIS c #1f5081",
+"#6t c #1f5082",
+"cqO c #1f5180",
+"bxG c #1f5181",
+"bHB c #1f5182",
+"#wz c #1f5183",
+"#XJ c #1f5184",
+"bPC c #1f5185",
+"bxH c #1f5283",
+"b4# c #1f5284",
+"#Is c #1f5285",
+"#Q6 c #1f5286",
+"bOd c #1f5287",
+"byA c #1f5385",
+"#JZ c #1f5386",
+"bs2 c #1f5387",
+"bMQ c #1f5389",
+"bfz c #1f5480",
+"aM9 c #1f5482",
+"bGj c #1f5487",
+"bKn c #1f558d",
+"#95 c #1f5690",
+"aUY c #1f578c",
+"bKY c #1f5993",
+"a7V c #1f5994",
+"aIE c #1f5a94",
+"bb3 c #1f5a95",
+"ast c #1f5b94",
+"aDB c #1f5b95",
+"#9i c #1f5b96",
+"bVu c #1f5c95",
+"aDC c #1f5c96",
+"bfk c #1f5d96",
+"a1m c #1f5d97",
+"bH4 c #1f5e8f",
+"ahL c #1f5e98",
+"bgk c #1f5f91",
+"bu0 c #1f5f99",
+"bAN c #1f6299",
+"boh c #1f649c",
+"bJl c #1f669e",
+"bAJ c #1f68a0",
+"bH3 c #1f69a0",
+"bAO c #1f69a1",
+"bAR c #1f6aa1",
+"brC c #1f6aa2",
+"bnQ c #1f6ba3",
+"bGC c #1f6ca3",
+"bwA c #1f6ca4",
+"bAP c #1f6da5",
+"bno c #1f6da6",
+"bEs c #1f6ea6",
+"#f4 c #202229",
+"aXv c #202430",
+"aKD c #202530",
+"#B3 c #202531",
+".MJ c #20262e",
+"baz c #202631",
+".s8 c #20272f",
+"aGK c #202733",
+"awC c #202831",
+"#Hy c #202832",
+"aZK c #202833",
+"b.C c #202834",
+"aGC c #202a32",
+"bar c #202a33",
+"#D5 c #202a34",
+".7. c #202b32",
+".wA c #202b34",
+".bs c #202b35",
+".ld c #202b36",
+"bFO c #202b3a",
+".jR c #202c34",
+".bS c #202c35",
+".#C c #202c36",
+".w1 c #202c37",
+".k5 c #202d34",
+"Qt3 c #202d35",
+".dH c #202d36",
+".vc c #202d37",
+"#bi c #202d38",
+"#bf c #202d39",
+".Cm c #202e35",
+".dG c #202e36",
+"#fi c #202e37",
+".dI c #202e38",
+"#xK c #202e39",
+"#Oq c #202e3a",
+".dJ c #202f37",
+"apv c #202f39",
+"#NY c #202f3a",
+"acm c #202f3b",
+"aGe c #20303a",
+"apw c #20303b",
+"#Oh c #20303e",
+"cqu c #20303f",
+"a3F c #20313a",
+"aDp c #20313b",
+".wj c #20313c",
+"aUf c #20313d",
+"aoO c #203142",
+"bAc c #203240",
+"bBc c #203243",
+"bsj c #20333c",
+"bs5 c #20333d",
+"bVB c #203445",
+"aTm c #203541",
+"bXw c #203547",
+"#4O c #20364b",
+"a3A c #203746",
+"a3U c #203748",
+"#Ko c #20374c",
+"a1s c #20374e",
+"bVX c #20384e",
+"#14 c #20394c",
+"a0# c #203950",
+"a3N c #203951",
+"biC c #203952",
+"a2b c #203a52",
+"a5r c #203a53",
+"a6T c #203a54",
+"b5n c #203b52",
+"aoP c #203b56",
+"#6l c #203c50",
+"cl. c #203c57",
+"beS c #203d58",
+"cp0 c #203e58",
+"bSV c #203f5e",
+"cwz c #204057",
+"aA1 c #20405f",
+"#MR c #204060",
+"cuz c #20415a",
+"b9M c #20415b",
+"ciw c #20415d",
+"#MO c #204161",
+"b7t c #204164",
+"cuA c #20425a",
+"b9L c #20425b",
+"b9J c #20425c",
+"b6n c #20425d",
+"cix c #204262",
+"b50 c #204263",
+"boH c #204264",
+"c#m c #20435d",
+"caD c #20435e",
+"b92 c #20445e",
+"cb2 c #20445f",
+"b4M c #204460",
+"cwm c #204462",
+"b3c c #204562",
+"cvl c #204564",
+"b1V c #204665",
+"aYO c #20466c",
+"#6E c #204672",
+"bv. c #20476e",
+"aQr c #204771",
+"bZT c #20486a",
+"b4l c #20486e",
+"bvT c #20486f",
+"bxa c #204971",
+"bX1 c #204a6f",
+"bOu c #204b71",
+"cfJ c #204b72",
+"bOD c #204c72",
+"b1a c #204c73",
+"b2E c #204c77",
+"#LB c #204c78",
+"by3 c #204d76",
+"bxO c #204d7b",
+"bZE c #204e78",
+"#2. c #204f7d",
+"#Kk c #204f7e",
+"aGW c #20507d",
+"aX9 c #20507e",
+"aSq c #205080",
+"b4r c #20517c",
+"ce. c #20517f",
+"a8g c #205180",
+"#YD c #205181",
+"aNK c #205280",
+"aYT c #205281",
+"c#S c #205284",
+"#MG c #205285",
+"byB c #205385",
+"#Eu c #205386",
+"bEn c #205387",
+"buV c #205480",
+"b84 c #205486",
+"bsO c #205487",
+"#Z8 c #205488",
+"#6u c #205489",
+"bBN c #205588",
+"a.q c #205589",
+"#V8 c #20558a",
+"cdt c #20558b",
+"aXd c #20568a",
+"asu c #205790",
+"cxy c #205791",
+"anM c #205891",
+"a6x c #205893",
+"bOg c #20598e",
+"a#D c #205990",
+"ayB c #205992",
+"a7h c #205994",
+"cbc c #205a93",
+"a6y c #205a94",
+"aEd c #205a95",
+"bcw c #205b94",
+"bD7 c #205b95",
+"aHn c #205b96",
+"bVH c #205b97",
+"agk c #205c96",
+"adj c #205d96",
+"abV c #205d97",
+"aaz c #205d98",
+"aeS c #205e98",
+"aey c #205e99",
+"bLE c #205f95",
+"bJm c #206199",
+"bzI c #206299",
+"bue c #20649b",
+"bAH c #20659d",
+"bBT c #20669e",
+"bqg c #20669f",
+"btv c #20679f",
+"bBK c #2067a0",
+"byD c #2068a1",
+"bxR c #206aa2",
+"bMO c #206da2",
+".3y c #211a1c",
+".3o c #212027",
+".US c #212128",
+"#pz c #21222a",
+"bbN c #21232f",
+"aWN c #212430",
+".8Q c #21252a",
+"aJq c #212530",
+"bCZ c #212631",
+"#HC c #212730",
+"aGJ c #212833",
+".Ir c #212a33",
+"#GY c #212b35",
+"#Nv c #212c34",
+".GY c #212c35",
+".5P c #212c36",
+"#st c #212c37",
+".iH c #212d35",
+".0s c #212d36",
+".at c #212d37",
+".w3 c #212d38",
+"aGg c #212d39",
+"#kr c #212e29",
+".k4 c #212e35",
+"Qt4 c #212e36",
+".Hc c #212e37",
+".v5 c #212e38",
+"#bg c #212e39",
+"#bh c #212e3a",
+"#do c #212f37",
+".dK c #212f38",
+"#Iv c #212f39",
+"#J6 c #212f3a",
+"#4R c #212f3b",
+"aNB c #21303a",
+"anH c #21303b",
+"#Ie c #21303c",
+"#9m c #21303e",
+"#o4 c #213130",
+"a6R c #21313a",
+"aDq c #21313b",
+"axe c #21313c",
+"#4Q c #21313e",
+"bWX c #213140",
+"aVl c #21323c",
+"apx c #21323d",
+"bVY c #213241",
+"#bJ c #21333a",
+"brF c #21333c",
+"brE c #21333d",
+"bFI c #21333e",
+"bBe c #213340",
+"cj5 c #213342",
+"#4P c #213343",
+"cfr c #213344",
+"#Jj c #213345",
+"bFH c #21343f",
+"amJ c #213440",
+"a.s c #213444",
+"#7x c #213445",
+"#1o c #213543",
+"bQk c #213546",
+"bl9 c #213548",
+"bO0 c #213549",
+"bg5 c #213649",
+"#Jk c #21364a",
+"b0B c #21375a",
+"aUu c #213846",
+"a#r c #21385c",
+"ayZ c #21394a",
+"a5u c #213951",
+"#5n c #213a4d",
+"#WQ c #213a4e",
+"bhQ c #213a53",
+"buo c #213b54",
+"#Jl c #213c57",
+"bsV c #213c58",
+"#3h c #213d52",
+"#Vh c #213d53",
+"cmv c #213d58",
+"bOO c #213d59",
+"#V6 c #213e54",
+"b5o c #213e56",
+"#4W c #213e5c",
+"#Zi c #213f55",
+"bQi c #213f5c",
+"cgb c #21405a",
+"cxb c #21405b",
+"bnK c #21405e",
+"bwJ c #21405f",
+"bK7 c #214060",
+"a3H c #214063",
+"ck0 c #21415c",
+"cwD c #214259",
+"cvy c #21425a",
+"b8k c #21425b",
+"ctt c #21425d",
+"bpH c #214264",
+"cwC c #21435a",
+"cvz c #21435b",
+"b7Y c #21435d",
+"b76 c #21435e",
+"bgl c #214361",
+"bxE c #214365",
+"aXn c #214369",
+"b75 c #21445d",
+"b74 c #21445e",
+"b94 c #21445f",
+"bCz c #214466",
+"aZu c #21446b",
+"##U c #21454c",
+"b93 c #21455f",
+"caE c #214560",
+"caF c #214561",
+"ctx c #214563",
+"bXu c #21456a",
+"c#n c #214661",
+"cxi c #214662",
+"cxX c #214663",
+"b1N c #214664",
+"cvk c #214764",
+"cdd c #214765",
+"b1K c #214866",
+"cdS c #21486f",
+"aoQ c #214870",
+"aXs c #21496f",
+"amK c #214a72",
+"bX2 c #214b70",
+"bSx c #214b71",
+"cd7 c #214b73",
+"bXV c #214c73",
+"bNH c #214d72",
+"a0S c #214d73",
+"aL2 c #214d74",
+"crY c #214d75",
+"bvS c #214d78",
+"bwI c #214d79",
+"aO# c #214e74",
+"#zq c #214e76",
+"bZu c #214e77",
+"cee c #214f77",
+"bx# c #214f7c",
+"btA c #214f7d",
+"#Od c #215080",
+"cks c #21517e",
+"aA4 c #215180",
+"#3n c #215182",
+"b2P c #21527d",
+"#O3 c #215281",
+"bwE c #215282",
+"#L6 c #215283",
+"#R3 c #215284",
+"bPD c #215286",
+"btB c #215383",
+"#RW c #215385",
+"#R4 c #215485",
+"#PL c #215486",
+"#0I c #215487",
+"bOc c #215488",
+"bB5 c #21557e",
+"#4J c #215589",
+"#7t c #21558a",
+"#HI c #21558b",
+"#Gv c #21558d",
+"bzy c #21568a",
+"#XO c #21568b",
+"bGh c #21568c",
+"#vz c #21568d",
+"#Jd c #21568e",
+"bxI c #21578b",
+"#0L c #21578c",
+"#7u c #21578d",
+"bw3 c #21578e",
+"aXX c #21578f",
+"cf# c #215790",
+"ayz c #215791",
+"bFs c #215883",
+"a#l c #21588e",
+"#Fd c #215890",
+"cls c #215891",
+"a.8 c #215892",
+"bMR c #21598e",
+"bGI c #21598f",
+"co1 c #215992",
+"#Qm c #215993",
+"cuF c #215a93",
+"a9M c #215a94",
+"#PI c #215a95",
+"bT3 c #215b94",
+"alA c #215b95",
+"bKN c #215c95",
+"ayA c #215c96",
+"bxP c #215d92",
+"abU c #215d96",
+"bBn c #215d97",
+"bok c #215e94",
+"aeR c #215e97",
+"abG c #215e98",
+"bKq c #215f96",
+"agu c #215f99",
+"bzH c #216097",
+"bAI c #216199",
+"buO c #21639a",
+"bzA c #21639b",
+"bvI c #21639c",
+"bzJ c #21649a",
+"bAS c #21649c",
+"bDx c #21659d",
+"br9 c #21659e",
+"buQ c #21669e",
+"bnP c #2166a0",
+"#ta c #22121a",
+"#i2 c #221820",
+".5r c #221b1d",
+"#r9 c #222124",
+"##f c #22242f",
+"bpp c #222531",
+"bC. c #222631",
+"bAf c #222632",
+"azL c #222b34",
+"b8X c #222b35",
+"#Nu c #222c35",
+".t. c #222c36",
+"a3z c #222c37",
+".Cl c #222d36",
+".ez c #222d37",
+"bej c #222d38",
+".a7 c #222e36",
+"Qt5 c #222e37",
+".cE c #222e38",
+"#bc c #222e39",
+"aoz c #222e3a",
+".n2 c #222f36",
+".h9 c #222f37",
+"#dp c #222f38",
+".vb c #222f39",
+".w4 c #222f3a",
+"#JR c #222f3b",
+"#bH c #223038",
+"bXi c #223039",
+"#rj c #22303a",
+"#F2 c #22303b",
+"azN c #22303c",
+"aa2 c #22303d",
+"bda c #223139",
+"avQ c #22313b",
+"#OM c #22313c",
+"a4y c #22313d",
+"cpe c #22313e",
+"#5F c #22313f",
+".fC c #22323a",
+"aZD c #22323c",
+"atm c #22323e",
+"bV0 c #22323f",
+"a7C c #223240",
+"#6x c #223241",
+"b13 c #223242",
+".zS c #22333e",
+"bFG c #22333f",
+"asW c #223340",
+"bSW c #223341",
+"b14 c #223342",
+"bRG c #223343",
+"btR c #22343e",
+".Lw c #22343f",
+"apy c #223440",
+"bjl c #223444",
+"bUk c #223445",
+"a2a c #223446",
+"ap9 c #223545",
+"#JS c #223546",
+"aAK c #223547",
+"#JQ c #223648",
+"bVT c #223649",
+"cfs c #22364a",
+"bSu c #223659",
+"clZ c #22374a",
+"a3O c #22374c",
+"aj8 c #223846",
+"aYN c #22394f",
+"c.u c #223950",
+"aA0 c #223a51",
+"#5w c #223c50",
+"cq5 c #223c57",
+"cqa c #223d57",
+"bRF c #223e58",
+"cc5 c #223e59",
+"#3W c #223f54",
+"#RQ c #224056",
+"#MP c #22405b",
+"#MQ c #22405d",
+"b6J c #22415a",
+"cl4 c #22415b",
+"#JT c #224160",
+"aDh c #224168",
+"cmk c #22425d",
+"#MS c #224262",
+"bpe c #224264",
+"aTu c #22426c",
+"cxs c #224359",
+"cwB c #22435a",
+"cvx c #22435b",
+"#JP c #224364",
+"bt8 c #224365",
+"cy. c #224459",
+"cwA c #22445b",
+"cvw c #22445c",
+"b8l c #22445e",
+"aUt c #224460",
+"b73 c #22455e",
+"b7X c #22455f",
+"b95 c #224560",
+"anK c #224568",
+"b6v c #22465f",
+"b7W c #224660",
+"cb3 c #224662",
+"#Jm c #22466a",
+"a4A c #224671",
+"b4V c #224762",
+"cxh c #224763",
+"b3h c #224764",
+"#MN c #22476b",
+"caG c #224863",
+"b1M c #224866",
+"b1L c #224867",
+"bSM c #22486c",
+"cul c #224968",
+"cvj c #224a67",
+"cbN c #224a69",
+"ccW c #224a6a",
+"bGg c #224b75",
+"az7 c #224d77",
+"bRg c #224e74",
+"c.h c #224e75",
+"bU8 c #224f79",
+"b06 c #224f7a",
+"#MK c #224f7c",
+"b2e c #225076",
+"bIE c #22507f",
+"aOL c #22517c",
+"btE c #225281",
+"bJf c #22537f",
+"aTA c #225381",
+"bvC c #225383",
+"btD c #225384",
+"aTy c #225483",
+"btC c #225485",
+"buk c #225486",
+"clo c #225586",
+"bPA c #225587",
+"#Zo c #225588",
+"#UC c #225589",
+"bOe c #22558a",
+"ac5 c #22558e",
+"bQW c #225687",
+"bDs c #22568c",
+"#YG c #22578c",
+"#JV c #22578d",
+"#FV c #22578e",
+"bzG c #22588d",
+"#WT c #22588e",
+"#xv c #22588f",
+"bdQ c #225890",
+"#Fe c #225891",
+"bzF c #22598f",
+"#FW c #225990",
+"#Kj c #225991",
+"aam c #225992",
+"a.9 c #225993",
+"cdu c #225994",
+"bGi c #225a91",
+"#K5 c #225a92",
+"#L2 c #225a93",
+"#7m c #225a94",
+"bb4 c #225a95",
+"bH2 c #225b93",
+"a.f c #225b94",
+"aHm c #225b95",
+"anL c #225b96",
+"bSD c #225c94",
+"agj c #225c95",
+"aCZ c #225c96",
+"abT c #225d95",
+"a#k c #225d96",
+"a2U c #225d97",
+"aFX c #225d98",
+"aeQ c #225e97",
+"buj c #225e98",
+"bwD c #225f96",
+"bAT c #225f97",
+"bpg c #225f98",
+"bvO c #225f99",
+"bBU c #226098",
+"ass c #226099",
+"bvA c #226198",
+"bzD c #226199",
+"bnN c #22619a",
+"bTC c #22628e",
+"byF c #22629a",
+"boS c #22629c",
+"bzN c #22639b",
+"bzK c #22649b",
+"bxS c #22649c",
+"bzL c #22659d",
+"bsP c #22659e",
+"bzM c #22669d",
+"boj c #2266a0",
+"bfx c #22679c",
+"bLy c #226da3",
+".Th c #23151d",
+"#eD c #231821",
+".vR c #23232a",
+"aV7 c #232531",
+"b#V c #232632",
+"#Ee c #232733",
+"aHc c #23282d",
+".SS c #232931",
+"bfG c #232933",
+"aDN c #232935",
+"bwj c #232b35",
+"#zW c #232c35",
+".IG c #232d36",
+"aFw c #232d37",
+"#iT c #232e28",
+".m# c #232e36",
+".xK c #232e37",
+".og c #232e38",
+"b09 c #232e39",
+".oZ c #232f36",
+".k3 c #232f37",
+"Qt6 c #232f38",
+".v. c #232f39",
+".L# c #232f3a",
+"#Zw c #232f3b",
+".m8 c #233037",
+"Qtt c #233038",
+".v# c #233039",
+".Du c #23303a",
+"#c4 c #23303b",
+"#Lm c #23303c",
+"a7p c #23303d",
+"#dS c #233138",
+"#bI c #233139",
+"aen c #23313a",
+"avc c #23313b",
+"#ic c #23313c",
+"asl c #23313d",
+"a5V c #23313e",
+"a5e c #23323a",
+"#IV c #23323e",
+"a4D c #23323f",
+"cnS c #233240",
+"bgy c #23333e",
+"arR c #233340",
+"bYI c #233341",
+"bll c #23343e",
+".yq c #23343f",
+"atP c #233440",
+"afp c #233441",
+"b0m c #233442",
+"aCb c #233443",
+"aUg c #233540",
+"b#I c #233543",
+"aBJ c #233546",
+"apz c #233642",
+"aCn c #233646",
+"bDU c #233745",
+"bRw c #233748",
+"aB# c #23374b",
+"aCm c #23384c",
+"aDy c #23394d",
+"aub c #23394e",
+"bQ. c #23394f",
+"a2o c #233a4c",
+"atn c #233a51",
+"bAb c #233b51",
+"cj4 c #233d55",
+"bUn c #233d56",
+"bK8 c #233e57",
+"bK6 c #233e58",
+"#2G c #233f55",
+"aEQ c #233f59",
+"azX c #234055",
+"aCc c #23405b",
+"bJV c #23405d",
+"b5p c #234159",
+"aUn c #23416c",
+"bID c #23425f",
+"bxN c #234261",
+"bfA c #234360",
+"aAJ c #234362",
+"#MT c #234363",
+"cy# c #23445a",
+"cwE c #23445b",
+"b6K c #23445e",
+"aVV c #234470",
+"cya c #23455a",
+"cyc c #23455b",
+"cxt c #23455c",
+"bu9 c #234566",
+"b6r c #234661",
+"b8m c #234761",
+"b96 c #234762",
+"c#o c #234763",
+"auc c #23476c",
+"aNI c #234773",
+"b97 c #234863",
+"cb4 c #234864",
+"che c #234868",
+"cxg c #234966",
+"b1T c #234967",
+"aEc c #23496e",
+"b3q c #234a67",
+"b1J c #234a68",
+"b9q c #234a69",
+"caq c #234a6a",
+"clv c #234a75",
+"cwl c #234b68",
+"cuk c #234b6a",
+"b1z c #234b6b",
+"bZU c #234b6d",
+"bJP c #234b76",
+"c.0 c #234c6c",
+"aDz c #234c76",
+"bCB c #234c77",
+"bX3 c #234d72",
+"btG c #234d76",
+"#MM c #234d77",
+"bAG c #234d78",
+"bIQ c #234d79",
+"bPN c #234d7a",
+"bBJ c #234e7a",
+"bGK c #234f73",
+"bMm c #234f74",
+"bXU c #234f75",
+"b.9 c #235076",
+"cef c #235077",
+"cuL c #235078",
+"aBI c #23507b",
+"bIR c #23507e",
+"a0e c #235178",
+"ctK c #23517a",
+"aAI c #23517f",
+"#ML c #235180",
+"chb c #23527b",
+"b9e c #23527f",
+"#Jn c #235281",
+"bN# c #23537d",
+"b9h c #23537e",
+"#JO c #235383",
+"cok c #235482",
+"bvR c #235485",
+"bHA c #235486",
+"ccM c #235582",
+"a0U c #235583",
+"aud c #235587",
+"#UD c #235589",
+"bOf c #23558a",
+"cfD c #235685",
+"aGV c #235786",
+"beU c #23578c",
+"#Z5 c #23578d",
+"#4I c #23578e",
+"#TI c #23588c",
+"#Jf c #23588d",
+"aCh c #23588e",
+"bEo c #23588f",
+"#Ns c #23598f",
+"#Gw c #235990",
+"#Il c #235991",
+"bBO c #235a90",
+"#Je c #235a91",
+"#xu c #235a92",
+"#KG c #235a93",
+"#Fj c #235a94",
+"#Fc c #235b91",
+"#0M c #235b92",
+"#K6 c #235b93",
+"#Fi c #235b94",
+"aDA c #235b95",
+"#Oc c #235c94",
+"#K7 c #235c95",
+"amL c #235c96",
+"aBG c #235c97",
+"agv c #235d95",
+"aay c #235d96",
+"ahM c #235d97",
+"aCe c #235d98",
+"bxL c #235e96",
+"bef c #235e99",
+"byG c #235f97",
+"bzB c #236097",
+"bxJ c #236098",
+"bnO c #236099",
+"byC c #236199",
+"#h0 c #24111a",
+"#wf c #241720",
+"#wb c #24222a",
+"bdo c #242330",
+"aZH c #242531",
+"##g c #242632",
+"b#g c #242733",
+"aE2 c #242834",
+"axM c #242a34",
+".G. c #242a35",
+".ik c #242b33",
+"beh c #242b34",
+"brl c #242c35",
+"#Gg c #242c37",
+"#QV c #242d35",
+"aF5 c #242d37",
+".5d c #242e34",
+".CI c #242e37",
+"#QU c #242e38",
+"aDo c #242e39",
+"#at c #242f34",
+".uN c #242f37",
+"a0. c #242f38",
+"anJ c #242f39",
+"a02 c #242f3a",
+"awy c #242f3b",
+".n1 c #243038",
+".aj c #243039",
+".va c #24303a",
+".Fr c #24303b",
+"#I8 c #24303c",
+".n0 c #243138",
+"Qt7 c #243139",
+".MO c #24313a",
+".Jk c #24313b",
+"#c5 c #24313c",
+"#Xx c #24313d",
+"bVZ c #24313e",
+"aIw c #24323b",
+"clG c #24323c",
+"aka c #24323d",
+"aX4 c #24323e",
+"#Lt c #24323f",
+"bfB c #243241",
+"bEE c #24333d",
+"auP c #24333f",
+"#4S c #243340",
+"blV c #24343e",
+"aPJ c #24343f",
+"arS c #243441",
+"b0l c #243444",
+"a20 c #243445",
+"blj c #24353f",
+"aqV c #243542",
+"b0s c #243544",
+"bck c #243641",
+"apA c #243642",
+"#JH c #243645",
+"b0k c #243646",
+"aDl c #243647",
+"bM9 c #243656",
+"bG6 c #243658",
+"btL c #243749",
+"bmL c #24394d",
+"bsU c #243a4f",
+"bM# c #243a50",
+"byQ c #243a63",
+"#4V c #243b53",
+"#0G c #243c4c",
+"bDr c #243e57",
+"bKX c #243f59",
+"auQ c #24405c",
+"bu8 c #24415b",
+"cf4 c #24425d",
+"#7f c #244359",
+"#7c c #24435a",
+"cjA c #24445f",
+"bry c #244466",
+"aOB c #244570",
+"cyb c #24465c",
+"b6t c #244862",
+"bRx c #24486b",
+"bQc c #24486c",
+"bFa c #24486d",
+"b6m c #244963",
+"b46 c #244964",
+"caH c #244965",
+"cxW c #244967",
+"bOS c #24496e",
+"bsd c #24496f",
+"b8n c #244a64",
+"b8o c #244a65",
+"c#p c #244a66",
+"aCX c #244a70",
+"c#q c #244b66",
+"cxf c #244b69",
+"bKR c #244b76",
+"aaP c #244b77",
+"cvi c #244c6b",
+"cbM c #244c6d",
+"aFo c #244c75",
+"bIP c #244c77",
+"b9p c #244d6d",
+"cfQ c #244d6f",
+"bBa c #244d71",
+"ctw c #244e6e",
+"cbL c #244e70",
+"bsh c #244e78",
+"ccV c #244f71",
+"#Jg c #244f7a",
+"bMa c #244f7b",
+"bOx c #245075",
+"bZr c #24507c",
+"auR c #24507e",
+"aRZ c #245178",
+"clQ c #245179",
+"btF c #24517e",
+"bwY c #245182",
+"byH c #24527f",
+"cqd c #24537b",
+"aB. c #245484",
+"bs3 c #245486",
+"bZD c #24557f",
+"bHZ c #245581",
+"bJW c #245586",
+"bSi c #245684",
+"bxM c #245687",
+"aER c #245689",
+"cbt c #245783",
+"aOH c #245785",
+"cll c #245786",
+"buX c #245789",
+"aCY c #24578c",
+"a40 c #245791",
+"aPn c #245886",
+"bLD c #24588e",
+"aTv c #245986",
+"aBF c #24598e",
+"az6 c #24598f",
+"#Iq c #245990",
+"aBH c #245a90",
+"#G3 c #245a91",
+"#FZ c #245a92",
+"bEm c #245a93",
+"#FU c #245b91",
+"#Gx c #245b92",
+"#FX c #245b93",
+"#PK c #245b94",
+"aez c #245b95",
+"bwz c #245c93",
+"#HG c #245c94",
+"#wB c #245c95",
+"#JN c #245c96",
+"bCC c #245d94",
+"#8c c #245d95",
+"#HH c #245d96",
+"#In c #245d97",
+"#PJ c #245d98",
+"bBL c #245e95",
+"bzE c #245e96",
+"buW c #245e97",
+"#L8 c #245e98",
+"#L7 c #245e99",
+"bdh c #245f9a",
+"bvP c #24609b",
+"atr c #24629d",
+"bGB c #2473a9",
+"#cO c #251a20",
+".7j c #251d1f",
+"#Bx c #25242f",
+"b#W c #252632",
+"aEN c #252729",
+"b#X c #252734",
+"a3Z c #252833",
+"#Ed c #252934",
+"axh c #252b35",
+"akn c #252c35",
+".Ea c #252c37",
+"bz. c #252d36",
+"ahD c #252e36",
+"#QW c #252e37",
+".b4 c #252e38",
+"#eF c #252e39",
+".8I c #252f35",
+"#SD c #252f37",
+".Es c #253039",
+"bST c #25303a",
+"#Kz c #25303b",
+"axc c #25303c",
+".qU c #253138",
+".rJ c #253139",
+".aq c #25313a",
+".w5 c #25313b",
+"#Us c #25313c",
+"aDx c #25313d",
+"bkR c #25313e",
+".jQ c #253239",
+"Qt8 c #25323a",
+".Z. c #25323b",
+".16 c #25323c",
+"#uH c #25323d",
+"#ET c #25323e",
+"bnT c #25323f",
+"agV c #253240",
+"aZp c #25333b",
+".90 c #25333c",
+"af9 c #25333d",
+"aqR c #25333e",
+"#2b c #25333f",
+"aGf c #253340",
+"cn4 c #253341",
+"blm c #25343e",
+"aHe c #25343f",
+"aSx c #253440",
+"bIC c #253441",
+"bli c #25353f",
+"aa3 c #253542",
+"bVS c #253543",
+"bNr c #253544",
+"a.u c #253642",
+".xf c #253643",
+"ari c #253644",
+"#4T c #253646",
+"b18 c #253657",
+"aTn c #253743",
+"bRt c #253747",
+"bQb c #253748",
+"bOR c #253849",
+"bi5 c #25384a",
+"bNv c #25384b",
+"#4U c #25394e",
+"#qC c #253a38",
+".wl c #253a46",
+"#Z4 c #253c4b",
+"#Zm c #253d4c",
+"az8 c #253f56",
+"bsb c #253f57",
+"coT c #25405a",
+"cg# c #25405c",
+"cp1 c #25415b",
+"cpQ c #25425b",
+"cnj c #25425c",
+"aAv c #254357",
+"b5q c #25435c",
+"btN c #254360",
+"bsc c #254463",
+"btr c #254466",
+"coK c #25455f",
+"bqT c #254567",
+"aQx c #25456f",
+"aNR c #254570",
+"bHz c #254668",
+"b6L c #254862",
+"bAa c #254866",
+"ato c #25486b",
+"b6u c #254963",
+"bIO c #25496e",
+"b6s c #254a63",
+"b6g c #254a64",
+"b6k c #254b65",
+"b8p c #254b66",
+"caI c #254b67",
+"b3m c #254b68",
+"b1O c #254c69",
+"bgV c #254c72",
+"bdR c #254c74",
+"cde c #254d6b",
+"bZ8 c #254d6d",
+"bz1 c #254d75",
+"cxe c #254e6c",
+"cwk c #254e6d",
+"bZ6 c #254e6e",
+"cap c #254e6f",
+"bAB c #254e76",
+"bse c #254e77",
+"bvJ c #254e78",
+"beg c #254e79",
+"cuj c #254f6f",
+"b9o c #254f70",
+"b2D c #254f7a",
+"a3R c #255079",
+"b05 c #25507a",
+"bBG c #25517b",
+"bCy c #25517c",
+"bwU c #25517d",
+"bVs c #255278",
+"bXT c #255279",
+"bUs c #25527b",
+"bsf c #25527f",
+"beT c #255281",
+"bug c #255380",
+"cdi c #25547d",
+"bsg c #255584",
+"bku c #255681",
+"b1h c #255683",
+"a3t c #255791",
+"clk c #255988",
+"#5z c #255a90",
+"bwT c #255b93",
+"boi c #255b94",
+"#Gu c #255c92",
+"bFc c #255c93",
+"#FY c #255c94",
+"#Im c #255c95",
+"#L3 c #255c96",
+"#YF c #255c97",
+"a9d c #255d94",
+"#KH c #255d95",
+"#F0 c #255d96",
+"#wA c #255d97",
+"bGs c #255e8c",
+"#96 c #255e96",
+"#JU c #255e97",
+"#Io c #255e98",
+"#LC c #255e99",
+"crc c #255f97",
+"#7l c #255f98",
+"#Fk c #255f99",
+"#Ir c #255f9a",
+"awl c #25609a",
+"bvQ c #25609b",
+"#TH c #25609c",
+"agl c #25649c",
+"#g3 c #25dede",
+"#eA c #26111a",
+"#lZ c #261a24",
+"#my c #26222d",
+".G3 c #262431",
+"#x3 c #26252e",
+"a31 c #262632",
+"aKC c #262733",
+"a8Z c #262835",
+"#Gn c #262b36",
+".8y c #262e29",
+"aOs c #262e3a",
+"#.S c #262f36",
+"#JG c #262f39",
+"aGd c #262f3b",
+".6P c #263037",
+"azo c #26303a",
+"aD6 c #26303b",
+".rI c #263139",
+"aTO c #26313a",
+".BF c #26313b",
+"aiu c #26313c",
+".pZ c #263239",
+".oY c #26323a",
+".dT c #26323b",
+".Dt c #26323c",
+"#13 c #26323d",
+"azM c #26323e",
+".pY c #26333a",
+"Qt9 c #26333b",
+".eS c #26333c",
+"#vE c #26333d",
+"#Ut c #26333e",
+"#Sf c #26333f",
+"aG6 c #263340",
+".6a c #26343c",
+"a5k c #26343d",
+"blJ c #26343e",
+"#Kq c #26343f",
+"arO c #263440",
+"atO c #263441",
+"#nn c #263530",
+"bai c #26353e",
+"blk c #26353f",
+"#UP c #263541",
+"arQ c #263542",
+"cmG c #263543",
+"bWM c #263544",
+"a5o c #263641",
+"#7y c #263642",
+"adP c #263643",
+"bYN c #263644",
+"b0u c #263645",
+".6v c #26364e",
+"bW0 c #263657",
+"amI c #263743",
+"#VV c #263745",
+"b0t c #263746",
+"bce c #263748",
+".zV c #263843",
+".tC c #263844",
+"#5L c #263849",
+"beR c #26384a",
+".75 c #263941",
+"#I9 c #263949",
+"bMg c #263a4d",
+"bRC c #263a4e",
+"bOX c #263a4f",
+"bsa c #263b4d",
+"#Ji c #263b4f",
+"bu7 c #263c52",
+"bH5 c #263d52",
+"b2n c #263d60",
+"bsi c #263e56",
+"beV c #263f57",
+"bcP c #263f58",
+"bEu c #264058",
+"bs4 c #26415b",
+"bIN c #26415e",
+"btI c #26425f",
+"aV# c #26436d",
+"a4G c #26445b",
+"buY c #264463",
+"bpd c #264567",
+"#Vl c #26465f",
+"buh c #264665",
+"bxT c #264667",
+"azt c #264760",
+"#qx c #264847",
+"c.M c #264868",
+"bzv c #26486c",
+"bHm c #264870",
+"#SK c #264962",
+"bJq c #264965",
+"btH c #26496c",
+"aQz c #264974",
+"b6j c #264b65",
+"b6l c #264c65",
+"b6i c #264c66",
+"b98 c #264c67",
+"b8q c #264c68",
+"b3b c #264d69",
+"caJ c #264d6a",
+"cb5 c #264d6b",
+"c#r c #264e6a",
+"#oT c #264f51",
+"bZ7 c #264f6e",
+"bZ5 c #264f6f",
+"bZV c #265070",
+"cao c #265173",
+"bX4 c #265174",
+"ctv c #265274",
+"cbK c #265275",
+"bZq c #26527d",
+"asv c #26548c",
+"caT c #26557b",
+"cfI c #265680",
+"atp c #265686",
+"bNg c #26568c",
+"aoR c #265787",
+"aY. c #265885",
+"bLY c #26588c",
+"aRT c #265988",
+"a2k c #265a88",
+"aCd c #265a8e",
+"aue c #265a92",
+"b7w c #265b88",
+"aTz c #265b8a",
+"bT2 c #265b93",
+"cbv c #265c8f",
+"bKm c #265c92",
+"#Ip c #265d94",
+"b#u c #265d95",
+"cia c #265e94",
+"aIF c #265e96",
+"bEt c #265f8b",
+"#MF c #265f96",
+"cvP c #265f97",
+"#88 c #265f98",
+"atq c #266098",
+"#Q5 c #266099",
+"#Um c #26609a",
+"#n. c #266466",
+"#e. c #271821",
+"a81 c #272431",
+"aUv c #272533",
+"#CF c #272734",
+"aXE c #272834",
+"#oa c #272934",
+"#I6 c #272b35",
+".0q c #272b37",
+".4w c #272c33",
+".go c #272e37",
+"aE1 c #272e38",
+"aDn c #272e39",
+"aSY c #272f3b",
+".C8 c #27303a",
+"aEF c #27303b",
+".3n c #273139",
+"#RK c #27313a",
+"aEJ c #27313b",
+"asT c #27313c",
+".P3 c #27323b",
+".zG c #27323c",
+"aqP c #27323d",
+"aDm c #27323e",
+".iG c #27333b",
+".#. c #27333c",
+".fJ c #27333d",
+".bd c #27333e",
+"aOU c #27333f",
+".qT c #27343b",
+".hK c #27343c",
+".dL c #27343d",
+"#uD c #27343e",
+"#3V c #27343f",
+"#5G c #273440",
+"#9n c #273441",
+"awI c #27353c",
+".6b c #27353d",
+"awb c #27353f",
+"aoy c #273540",
+"avS c #273541",
+"aB7 c #273542",
+"bBI c #273543",
+"#rJ c #273641",
+"a.t c #273642",
+"aqU c #273643",
+"bYA c #273644",
+"bSU c #273645",
+"b0v c #273646",
+"bOs c #273658",
+".hG c #27373f",
+"apB c #273743",
+"afr c #273744",
+"bs# c #273745",
+"bsT c #273746",
+"#6B c #273748",
+"aV9 c #273844",
+"aWO c #273845",
+"bu6 c #273849",
+"b#k c #273945",
+"bNC c #27394b",
+".VB c #273a45",
+"cn5 c #273a49",
+"#MB c #273a4a",
+"cpf c #273c4d",
+"btJ c #273c51",
+"bCd c #273d4d",
+"bEl c #273e54",
+"#If c #273f52",
+"bFt c #273f54",
+"bdm c #273f56",
+"bWL c #274258",
+"cnL c #27425d",
+"ckT c #27425e",
+"aUk c #27436e",
+"boG c #274667",
+"br7 c #274668",
+"b5r c #27475f",
+"#ae c #274770",
+"b#C c #274961",
+"bvX c #27496b",
+"b6h c #274d67",
+"b8r c #274d69",
+"b4U c #274e68",
+"b8s c #274e69",
+"b4k c #274e75",
+"caK c #274f6c",
+"cb6 c #274f6d",
+"bZ4 c #275171",
+"bhE c #275178",
+"bv5 c #27517a",
+"b9n c #275272",
+"ctu c #275275",
+"b2C c #27527c",
+"c.Z c #275375",
+"bX5 c #275376",
+"chd c #275379",
+"can c #275477",
+"cen c #27557a",
+"bCG c #275582",
+"aBE c #275583",
+"#C6 c #27567f",
+"ckv c #275680",
+"b7# c #27577e",
+"a1x c #27577f",
+"aZy c #275881",
+"a26 c #275884",
+"avA c #27598a",
+"c#9 c #275a88",
+"alB c #275a90",
+"aIh c #275b89",
+"aSL c #275c8b",
+"abW c #275c94",
+"b55 c #275d8a",
+"bP4 c #275d94",
+"adk c #275d95",
+"bOb c #275e92",
+"a7i c #275e95",
+"aCf c #275e97",
+"clf c #276097",
+"abH c #276098",
+"cle c #276198",
+"cxG c #276199",
+"#94 c #27619a",
+"art c #27679f",
+"#go c #281d27",
+".Z7 c #281e20",
+"#x5 c #281e29",
+"#jF c #28202a",
+".J. c #282430",
+"bnw c #282633",
+"bax c #282734",
+"##i c #282936",
+".Ts c #282b37",
+"bhh c #282c3a",
+".im c #282e36",
+"aJ8 c #282e37",
+"aNC c #282e3a",
+"b#Z c #282f3a",
+"aWA c #282f3b",
+"aND c #282f3c",
+"#lX c #283037",
+"abz c #283038",
+"aAL c #283039",
+".q9 c #28303a",
+"aFi c #28303b",
+"bbE c #28303c",
+".ZM c #283136",
+".4S c #283239",
+"#aM c #28323a",
+"#eh c #28323b",
+"#RP c #28323c",
+"aDs c #28323d",
+"#QX c #28333b",
+".w. c #28333c",
+"#a9 c #28333d",
+"bRu c #28333f",
+"bbB c #283340",
+".v7 c #28343c",
+".## c #28343d",
+".#q c #28343e",
+"#65 c #28343f",
+"ayV c #283440",
+".tX c #28353c",
+".gK c #28353d",
+".4b c #28353e",
+".#p c #28353f",
+"#tt c #283540",
+"#5H c #283541",
+"#5K c #283542",
+"cvL c #283545",
+"bKC c #283556",
+".VJ c #28363e",
+"a6E c #28363f",
+"#MX c #283641",
+"a#N c #283642",
+"#8p c #283643",
+"biE c #283644",
+"bkb c #283743",
+"afq c #283744",
+"bYJ c #283746",
+"bGf c #283748",
+"#bF c #283843",
+"aWP c #283844",
+"aR7 c #283845",
+"bun c #283846",
+"bHy c #283847",
+"bK5 c #283848",
+"bK9 c #283849",
+"bIM c #28394c",
+"bfE c #283a4c",
+"bEk c #283c4e",
+"#KA c #283e4e",
+"bfD c #283e54",
+"asm c #284055",
+"cnx c #28435d",
+"aN4 c #28436e",
+"aEw c #28446f",
+"bXt c #284766",
+"byw c #284768",
+"brx c #284769",
+"bpc c #28476a",
+"aYV c #28485e",
+"cp8 c #284863",
+"bqS c #284869",
+"b5s c #284a63",
+"#3k c #284a65",
+"b6M c #284d68",
+"b41 c #284e68",
+"b44 c #284e69",
+"b43 c #284f69",
+"b42 c #28506a",
+"b8t c #28506b",
+"c#s c #28506d",
+"bga c #285076",
+"b3r c #28516c",
+"caL c #28516f",
+"cb7 c #285170",
+"cdf c #285271",
+"bBF c #28527d",
+"cxd c #285373",
+"b7o c #28537e",
+"cvh c #285475",
+"bYn c #285476",
+"c.Y c #285679",
+"cbJ c #28567a",
+"bRl c #28577e",
+"bXS c #28577f",
+"bd5 c #28578f",
+"bIi c #285880",
+"b52 c #285881",
+"cg8 c #285882",
+"byV c #285982",
+"aRO c #285986",
+"agt c #28598f",
+"auS c #285990",
+"a0G c #285991",
+"bNe c #285a83",
+"bBV c #285a8c",
+"bcO c #285b8d",
+"bze c #285b93",
+"aZz c #285c89",
+"bOo c #285c8a",
+"bj0 c #285e8b",
+"ch. c #285e8d",
+"caa c #285e8f",
+"a8A c #285e94",
+"cxH c #286198",
+"bM0 c #286199",
+"cnQ c #28619a",
+"#Fh c #286299",
+"a#. c #28629a",
+"cvE c #28629b",
+"#Ik c #286398",
+"crZ c #28639c",
+"#Ql c #28649d",
+"asr c #2868a1",
+"bGA c #2877ad",
+"bGu c #2879af",
+"#sf c #29131c",
+".1K c #291e20",
+".Tb c #292121",
+".K4 c #292331",
+"aJp c #292633",
+"by9 c #292734",
+".hM c #292a34",
+"auv c #292b36",
+"#p5 c #292b37",
+"a4p c #292c36",
+".ho c #292e36",
+"abu c #292e38",
+"aNg c #292f3c",
+"bjv c #293039",
+"aMy c #29303b",
+"avP c #29303c",
+"#kt c #293137",
+"aDK c #29313b",
+"#ji c #29323b",
+"aqJ c #29323d",
+"#f3 c #29333b",
+"aAn c #29333d",
+"btQ c #293340",
+".Iw c #29343a",
+".xG c #29343c",
+".v6 c #29343d",
+"#L. c #29343e",
+"abo c #29343f",
+"#6A c #293440",
+"aZt c #293442",
+".#g c #29353d",
+".#a c #29353e",
+".#o c #29353f",
+"#bk c #293540",
+"#5J c #293541",
+"#5I c #293542",
+"cqr c #293544",
+".nv c #29363d",
+".vG c #29363e",
+".2u c #29363f",
+"#c1 c #293640",
+"#m5 c #293641",
+"#6z c #293642",
+"#6y c #293643",
+"atN c #293644",
+"bGL c #293646",
+"acz c #29373e",
+".4c c #29373f",
+"#9x c #293741",
+"bdJ c #293742",
+"aBv c #293743",
+"a#M c #293744",
+"a5d c #293745",
+"bQh c #293746",
+"aqT c #293844",
+"aQT c #293845",
+"bfC c #293846",
+"bYK c #293847",
+"bED c #293943",
+"#uS c #293946",
+"bYL c #293947",
+"bvK c #293949",
+"aAZ c #293a49",
+"bui c #293a4a",
+"bYz c #293b4d",
+"cn6 c #293f52",
+"aPy c #29426c",
+"aPs c #29436d",
+"cp3 c #29445e",
+"cnh c #29465f",
+"cnC c #294761",
+"bqa c #294769",
+"#Jh c #294865",
+"bpG c #294869",
+"aq. c #294965",
+"buJ c #29496a",
+"bUa c #294a6f",
+"bSI c #294b77",
+"#Yw c #294d6a",
+"#5x c #294f6d",
+"b2i c #294f72",
+"b45 c #29506a",
+"b4R c #29506b",
+"aY# c #295070",
+"b4T c #29516b",
+"b8u c #29516d",
+"b26 c #29526e",
+"c#t c #295370",
+"b04 c #29537d",
+"bYj c #295476",
+"cxc c #295477",
+"bzO c #29547e",
+"bYk c #295576",
+"bYl c #295577",
+"bYm c #295677",
+"bYi c #295678",
+"cdO c #295687",
+"cui c #29577a",
+"cam c #29577c",
+"bOw c #29577e",
+"byR c #29578e",
+"a5P c #29578f",
+"cem c #29587d",
+"#5y c #295882",
+"cfP c #29597e",
+"chc c #295980",
+"aKQ c #295a84",
+"bNl c #295a92",
+"cha c #295b85",
+"bIq c #295b92",
+"bZC c #295c86",
+"cku c #295c8b",
+"a9K c #295c93",
+"bzg c #295c94",
+"bEX c #295d94",
+"bQ8 c #295e8b",
+"c.P c #295e8c",
+"aA5 c #295e91",
+"aDD c #295e96",
+"aCg c #295f97",
+"aPm c #29608f",
+"ce9 c #296193",
+"#HF c #296297",
+"cc# c #29639a",
+"bDD c #29639b",
+"cvW c #29649b",
+"cmC c #29659d",
+"bGv c #297aaf",
+"bGt c #297bb0",
+".3x c #2a2021",
+".1J c #2a2022",
+".A1 c #2a2329",
+"a3a c #2a2532",
+"#xL c #2a2633",
+"aIa c #2a2634",
+"bpU c #2a2734",
+"bm7 c #2a2936",
+".P1 c #2a2b37",
+".mc c #2a2c35",
+"#Ht c #2a2c37",
+"a91 c #2a2e37",
+"aMw c #2a2e3a",
+"aMx c #2a2e3b",
+".aD c #2a2f3a",
+"aMz c #2a2f3b",
+"aWz c #2a2f3c",
+".p. c #2a3038",
+".sy c #2a3039",
+"aSt c #2a303c",
+".ec c #2a313b",
+"acY c #2a323a",
+"#ns c #2a3339",
+"#nN c #2a333c",
+"aqI c #2a333e",
+"bmh c #2a333f",
+".27 c #2a343a",
+".v9 c #2a343e",
+"aCT c #2a343f",
+"#o8 c #2a353d",
+".v8 c #2a353e",
+"#TA c #2a353f",
+"#8J c #2a3540",
+"agZ c #2a3542",
+".#b c #2a363e",
+".ap c #2a363f",
+"#KL c #2a3640",
+"#2R c #2a3641",
+"bcf c #2a3642",
+"ag0 c #2a3643",
+"#Ur c #2a373f",
+".sp c #2a3740",
+".#m c #2a3741",
+"a#V c #2a3742",
+"a6F c #2a3743",
+"asV c #2a3744",
+"arP c #2a3745",
+".2v c #2a3840",
+"aO5 c #2a3841",
+"cgJ c #2a3842",
+"bJ4 c #2a3843",
+"b.V c #2a3844",
+"aqS c #2a3845",
+"b.2 c #2a3846",
+".k0 c #2a3942",
+"#PY c #2a3945",
+"bfs c #2a3946",
+"bfr c #2a3947",
+"bYM c #2a3a47",
+"axz c #2a3d4b",
+"aV0 c #2a436d",
+"cp2 c #2a455f",
+"cjo c #2a4660",
+"ayu c #2a485a",
+"bwt c #2a496a",
+"bsM c #2a4a6b",
+"b4S c #2a516c",
+"b4N c #2a526c",
+"b4Q c #2a526d",
+"b8v c #2a526e",
+"b99 c #2a526f",
+"b0J c #2a5274",
+"b1I c #2a5370",
+"c.. c #2a5470",
+"caM c #2a5472",
+"bDq c #2a547d",
+"bCx c #2a547e",
+"cdg c #2a5574",
+"bZp c #2a5680",
+"bY. c #2a5778",
+"bX6 c #2a577a",
+"bX9 c #2a5878",
+"cuh c #2a587c",
+"b9m c #2a597d",
+"awm c #2a598e",
+"a#i c #2a5991",
+"ccU c #2a5a7f",
+"bfZ c #2a5a92",
+"bTY c #2a5b82",
+"cvN c #2a5b85",
+"bVG c #2a5b8f",
+"bvM c #2a5b92",
+"c#7 c #2a5c84",
+"cvC c #2a5c86",
+"cgm c #2a5d87",
+"bHh c #2a5d94",
+"bVn c #2a5e87",
+"csM c #2a5e8a",
+"a7U c #2a5e95",
+"aES c #2a6199",
+"#Fb c #2a6297",
+"cpC c #2a6391",
+"#Ff c #2a6399",
+"aA9 c #2a649b",
+"co2 c #2a649c",
+"bEv c #2a659c",
+"#PH c #2a659d",
+"#6q c #2a669f",
+"alz c #2a689e",
+"aeA c #2a69a0",
+"ars c #2a6aa2",
+"bGz c #2a7cb1",
+".Rz c #2b1c25",
+".U0 c #2b2021",
+".B2 c #2b2628",
+"aGL c #2b2635",
+".DS c #2b2728",
+".z9 c #2b272a",
+".1P c #2b272b",
+"#of c #2b2b38",
+"aEY c #2b2c35",
+"aYg c #2b2c39",
+"#K. c #2b2d38",
+"af1 c #2b2e35",
+"bfF c #2b2e38",
+"aMv c #2b2e3b",
+"aKW c #2b2f3b",
+"aPI c #2b2f3c",
+".p6 c #2b3039",
+".7D c #2b313c",
+"aGR c #2b323a",
+"aBa c #2b323b",
+"apu c #2b333e",
+".1y c #2b343b",
+"#kO c #2b343c",
+"agY c #2b3442",
+"axX c #2b353f",
+"acn c #2b3542",
+".KB c #2b363c",
+".ZI c #2b363d",
+".Cs c #2b363e",
+".y. c #2b363f",
+"#Xw c #2b3640",
+"agb c #2b3641",
+"bie c #2b3642",
+"aQS c #2b3644",
+".#c c #2b373f",
+".ao c #2b3740",
+".#n c #2b3741",
+".#l c #2b3742",
+"#8h c #2b375a",
+".Ge c #2b383e",
+".yR c #2b3840",
+".0N c #2b3841",
+"#7G c #2b3842",
+"#44 c #2b3843",
+"bfq c #2b3844",
+"apC c #2b3845",
+"bVh c #2b3846",
+".iF c #2b3941",
+"bon c #2b3945",
+"b#D c #2b3946",
+"bqk c #2b3947",
+"bf8 c #2b3a47",
+".nY c #2b3b43",
+"#D4 c #2b3c48",
+"#ly c #2b3d4a",
+"bnj c #2b3e4e",
+"aWH c #2b426a",
+"aXo c #2b426b",
+"crt c #2b4356",
+"cqv c #2b4357",
+"cpg c #2b4459",
+"cj7 c #2b4559",
+"#LY c #2b455c",
+"cnq c #2b4660",
+"cfX c #2b4662",
+"cpT c #2b4861",
+"cnD c #2b4a64",
+"b5Z c #2b4a6c",
+"cp7 c #2b4b65",
+"bXs c #2b4c6a",
+"#V1 c #2b516e",
+"b4O c #2b526c",
+"bcd c #2b5276",
+"bzu c #2b5277",
+"b4P c #2b536e",
+"c#u c #2b5472",
+"b8w c #2b5571",
+"bVI c #2b557e",
+"bAA c #2b567f",
+"a2S c #2b568e",
+"caN c #2b5775",
+"bX7 c #2b5879",
+"bX8 c #2b587b",
+"cwj c #2b597c",
+"c.X c #2b597e",
+"cbI c #2b5a80",
+"bOJ c #2b5a92",
+"bJE c #2b5b82",
+"bW7 c #2b5b83",
+"cfO c #2b5c82",
+"aeT c #2b5c94",
+"b9i c #2b5d86",
+"ctU c #2b5d87",
+"bOK c #2b5d93",
+"aJC c #2b5f8c",
+"cxv c #2b608c",
+"csy c #2b618e",
+"aRV c #2b618f",
+"a8B c #2b6197",
+"#K4 c #2b659c",
+"cjQ c #2b669c",
+"aoS c #2b669d",
+"aSH c #2b6797",
+".Yo c #2c2022",
+".5q c #2c2223",
+".Yh c #2c252d",
+"a4J c #2c2634",
+"aKB c #2c2a37",
+".YI c #2c2b38",
+"b#Y c #2c2c39",
+"a0l c #2c2d3a",
+"#IY c #2c2e38",
+"a.3 c #2c2f37",
+"a4w c #2c2f3c",
+"#Mu c #2c313a",
+"#gs c #2c313c",
+"aQ# c #2c313d",
+"aA. c #2c323c",
+"aqK c #2c333e",
+"#Ls c #2c333f",
+"btP c #2c3440",
+"aHf c #2c3540",
+"ag1 c #2c3543",
+".y0 c #2c363f",
+".W0 c #2c3640",
+"aUe c #2c3642",
+"aXw c #2c3644",
+".X5 c #2c373d",
+".Wl c #2c373e",
+"#iV c #2c373f",
+".w6 c #2c3740",
+".P4 c #2c3741",
+"bii c #2c3742",
+".SV c #2c383e",
+".Iu c #2c383f",
+".#d c #2c3840",
+".an c #2c3841",
+"#V0 c #2c3842",
+".#k c #2c3843",
+".#e c #2c3941",
+".0O c #2c3942",
+"#rE c #2c3943",
+"aix c #2c3944",
+"biF c #2c3945",
+"bl8 c #2c3946",
+".jO c #2c3a43",
+"bCY c #2c3a44",
+"bMY c #2c3a45",
+"b#J c #2c3a46",
+"bf5 c #2c3a47",
+"bf7 c #2c3a48",
+"#uI c #2c3b47",
+"bu5 c #2c3c49",
+"ayM c #2c404d",
+"c#N c #2c404f",
+"avy c #2c4050",
+"ak# c #2c4150",
+"aVX c #2c426b",
+"aDW c #2c426c",
+".6c c #2c444d",
+"bn8 c #2c455c",
+"bDV c #2c4659",
+"cjk c #2c4662",
+"cdG c #2c475f",
+"coV c #2c4761",
+"cmU c #2c485f",
+"cpU c #2c4861",
+"#KZ c #2c495e",
+"#af c #2c496f",
+"c#T c #2c4b67",
+"br6 c #2c4b6b",
+"bL8 c #2c4b75",
+"bt7 c #2c4c6c",
+"a6a c #2c4e66",
+"a1z c #2c4e68",
+"#Np c #2c4f6a",
+"b3w c #2c546e",
+"b6N c #2c5471",
+"b3i c #2c5570",
+"b3s c #2c5671",
+"b6O c #2c5672",
+"c.# c #2c5673",
+"bRy c #2c567f",
+"b8x c #2c5773",
+"cdh c #2c5777",
+"b2B c #2c5781",
+"c#v c #2c5875",
+"bZ3 c #2c5877",
+"bYh c #2c597a",
+"bjZ c #2c5982",
+"bWx c #2c5b7e",
+"b9l c #2c5b80",
+"bWu c #2c5c7f",
+"cal c #2c5c81",
+"cel c #2c5c82",
+"ccT c #2c5c83",
+"cvg c #2c5d81",
+"cwi c #2c5d82",
+"c.W c #2c5d83",
+"bXR c #2c5d84",
+"bz2 c #2c5d8e",
+"cs7 c #2c5e87",
+"cs# c #2c5e93",
+"ciU c #2c5f8a",
+"b9d c #2c608b",
+"bB# c #2c608c",
+"aZ1 c #2c6094",
+"bHY c #2c618d",
+"b4q c #2c618f",
+"biX c #2c6293",
+"bPz c #2c6393",
+"crK c #2c6595",
+"aku c #2c659b",
+"csN c #2c679d",
+"bDE c #2c679e",
+"#87 c #2c689e",
+"bFu c #2c689f",
+"bGw c #2c7eb3",
+".U4 c #2d1d21",
+".Z6 c #2d2223",
+".Mr c #2d252c",
+"#vR c #2d2632",
+"a2s c #2d2634",
+"bou c #2d2734",
+"aE3 c #2d2736",
+"#.T c #2d2830",
+"##h c #2d2835",
+"aHW c #2d2937",
+".yB c #2d2b2d",
+"#EV c #2d2b38",
+"aL7 c #2d2d3b",
+"#Nn c #2d2e38",
+"aMA c #2d2e3b",
+"aMu c #2d2e3c",
+"#KU c #2d2f39",
+"aU8 c #2d2f3c",
+".te c #2d3039",
+"awx c #2d303d",
+"aXZ c #2d313e",
+"aAQ c #2d323c",
+"aqH c #2d343f",
+"agX c #2d3442",
+"agW c #2d3443",
+".eA c #2d353e",
+"aKa c #2d3540",
+"bml c #2d3541",
+"aRA c #2d3542",
+"bJB c #2d3556",
+"#ed c #2d363a",
+".KH c #2d363e",
+".Ri c #2d363f",
+"az9 c #2d3641",
+".PC c #2d373f",
+"#eL c #2d3740",
+"##l c #2d3741",
+".yf c #2d3841",
+".bc c #2d3842",
+".am c #2d3843",
+".z4 c #2d3940",
+".dM c #2d3941",
+".#f c #2d3942",
+"#MW c #2d3943",
+"ap7 c #2d3944",
+"clI c #2d3945",
+".uM c #2d3a42",
+".al c #2d3a43",
+"#8z c #2d3a44",
+"aj6 c #2d3a45",
+"bdn c #2d3a46",
+"bqX c #2d3a47",
+"bf6 c #2d3a48",
+".FM c #2d3b43",
+"##u c #2d3b45",
+"#3y c #2d3b46",
+"bas c #2d3b47",
+"aaA c #2d3b60",
+".s6 c #2d3c44",
+"#Gf c #2d3c47",
+"bWW c #2d3c49",
+"bWV c #2d3c4a",
+"cdC c #2d405e",
+"aAR c #2d414d",
+"bBW c #2d4154",
+"aUj c #2d416b",
+"aEu c #2d426c",
+"aId c #2d436d",
+"cnR c #2d4559",
+"#8i c #2d4a71",
+"ceE c #2d4b64",
+"b7s c #2d4c6c",
+"cn7 c #2d4d66",
+"cph c #2d4d68",
+"brw c #2d4d6c",
+"bpF c #2d4d6d",
+"asw c #2d5287",
+"#4A c #2d536d",
+"b.1 c #2d5572",
+"#6m c #2d5573",
+"#Uz c #2d5574",
+"b4j c #2d557c",
+"b3k c #2d5671",
+"b3a c #2d5672",
+"#7g c #2d5675",
+"b0W c #2d567f",
+"b3l c #2d5772",
+"bBE c #2d5780",
+"b6P c #2d5874",
+"bZ9 c #2d5877",
+"c.a c #2d5976",
+"aaw c #2d5a91",
+"bWp c #2d5c80",
+"bWv c #2d5c81",
+"bWw c #2d5d80",
+"bWo c #2d5d81",
+"b9k c #2d5d84",
+"cbH c #2d5e84",
+"cak c #2d5f85",
+"b9j c #2d5f86",
+"bIF c #2d5f91",
+"cek c #2d6087",
+"bXQ c #2d6088",
+"ckq c #2d6089",
+"bOv c #2d6188",
+"ced c #2d618b",
+"bad c #2d6194",
+"bZB c #2d628e",
+"ch# c #2d6392",
+"ccg c #2d6491",
+"cm6 c #2d6493",
+"ciS c #2d6494",
+"#SM c #2d6594",
+"#Fg c #2d679d",
+"#Et c #2d689d",
+"cvV c #2d689e",
+"bDF c #2d699f",
+"co0 c #2d69a0",
+"cnX c #2d71a8",
+"bGy c #2d7eb3",
+".3w c #2e2324",
+".7i c #2e2425",
+"boq c #2e2433",
+"a8i c #2e2534",
+"bbL c #2e2736",
+"aGI c #2e2a38",
+"aHT c #2e2b38",
+"#tl c #2e2c3a",
+".tZ c #2e2d36",
+"aD5 c #2e2e3c",
+".Aq c #2e2e3f",
+"aZ9 c #2e2f3c",
+"aNA c #2e313e",
+"aLb c #2e3239",
+"aNr c #2e323b",
+"#a5 c #2e323d",
+"#ay c #2e3337",
+"bDf c #2e333c",
+"bmm c #2e3440",
+"#8q c #2e3442",
+"bLQ c #2e3454",
+"#KY c #2e3540",
+"cn3 c #2e3642",
+"aW. c #2e3644",
+".Mo c #2e373f",
+"azn c #2e3741",
+"bIy c #2e3742",
+"aCU c #2e3841",
+".W1 c #2e3842",
+".k2 c #2e3942",
+".#j c #2e3943",
+"bAV c #2e3944",
+".ak c #2e3a42",
+".gE c #2e3a43",
+".Tt c #2e3a44",
+"#ol c #2e3a45",
+"bDz c #2e3a46",
+"a2e c #2e3a59",
+".s. c #2e3b42",
+".dR c #2e3b43",
+".Sk c #2e3b44",
+"#xz c #2e3b45",
+"##v c #2e3b46",
+"bgm c #2e3b47",
+"boW c #2e3b48",
+".m4 c #2e3c44",
+"bm0 c #2e3c48",
+".sn c #2e3d45",
+"aeo c #2e3d47",
+"#uJ c #2e3d49",
+"ant c #2e3e4a",
+"##T c #2e3f4c",
+"c#M c #2e3f4d",
+"a3L c #2e406a",
+"#H2 c #2e414e",
+"bbA c #2e4558",
+"cnf c #2e4a62",
+"crT c #2e4a63",
+"cj# c #2e4a64",
+"bEJ c #2e4b5e",
+"ckI c #2e4b64",
+"ci4 c #2e4b66",
+"cp5 c #2e4d66",
+"bqR c #2e4d6d",
+"aaZ c #2e4d77",
+"buI c #2e4e6d",
+"#Ig c #2e506a",
+"b5Y c #2e5070",
+"bXr c #2e5173",
+"avz c #2e5272",
+"ats c #2e5287",
+"aRs c #2e5385",
+"#qy c #2e5450",
+"csf c #2e5474",
+"#Uv c #2e5574",
+"bv4 c #2e567d",
+"b3j c #2e5872",
+"b3t c #2e5873",
+"b3. c #2e5973",
+"b03 c #2e5982",
+"b6Q c #2e5a76",
+"b8y c #2e5a77",
+"#TC c #2e5a7b",
+"bWn c #2e5e81",
+"bWm c #2e5e82",
+"biW c #2e5e94",
+"bWl c #2e5f83",
+"bO6 c #2e6086",
+"bV1 c #2e608a",
+"c.V c #2e6187",
+"bXP c #2e618a",
+"ccb c #2e618e",
+"cbG c #2e6289",
+"by2 c #2e628d",
+"cwR c #2e628e",
+"cfL c #2e638d",
+"cb8 c #2e638e",
+"cra c #2e6491",
+"aN7 c #2e6694",
+"ctL c #2e6698",
+"#JM c #2e679c",
+".91 c #2e6970",
+"ctW c #2e699f",
+"cjP c #2e69a0",
+"cdk c #2e6a9f",
+"cuP c #2e6aa0",
+"cuW c #2e6aa1",
+"ch7 c #2e6ba0",
+"#8b c #2e6ba1",
+"aw3 c #2e6ca5",
+"bGx c #2e80b5",
+".Te c #2f2023",
+".1I c #2f2425",
+"bdS c #2f2433",
+"aYh c #2f2534",
+"b#0 c #2f2736",
+"#ei c #2f2831",
+"#ss c #2f2b39",
+".n4 c #2f2e39",
+"aWB c #2f2e3c",
+".t0 c #2f2f3a",
+"aVx c #2f2f3c",
+"aPd c #2f303e",
+"aOg c #2f313d",
+"aYJ c #2f313e",
+"aGy c #2f333c",
+"bmu c #2f3340",
+"bmn c #2f3341",
+"aBg c #2f343e",
+"btO c #2f3441",
+"aag c #2f353d",
+"aVm c #2f3643",
+".Wq c #2f373b",
+".Wn c #2f373c",
+".eG c #2f3941",
+".gD c #2f3a42",
+".bb c #2f3a43",
+".#i c #2f3a44",
+".ae c #2f3b42",
+".a9 c #2f3b43",
+".#h c #2f3b44",
+".YK c #2f3b45",
+"aoL c #2f3b46",
+"bgN c #2f3b47",
+".dQ c #2f3c44",
+".T7 c #2f3c45",
+"#eY c #2f3c46",
+"bgO c #2f3c47",
+"beW c #2f3c48",
+".eu c #2f3d44",
+"a5c c #2f3d45",
+".tT c #2f3d46",
+"auA c #2f3d47",
+"bqY c #2f3d49",
+"bF# c #2f3e49",
+"#tx c #2f3e4b",
+"bDR c #2f3f48",
+"#RO c #2f3f4b",
+"ait c #2f404d",
+"#Dn c #2f404e",
+"bbh c #2f424e",
+"bB8 c #2f4355",
+"aw0 c #2f4657",
+"#qB c #2f4944",
+"ckQ c #2f4a64",
+"bIv c #2f4c76",
+"alx c #2f4d67",
+"bws c #2f4e6d",
+"cbn c #2f4f6c",
+"bsL c #2f4f6e",
+"a94 c #2f5165",
+"#6b c #2f5268",
+"#Kf c #2f5370",
+"bEj c #2f5478",
+"aex c #2f5488",
+"bCw c #2f5881",
+"b3# c #2f5974",
+"b3u c #2f5975",
+"bZo c #2f5983",
+"bD6 c #2f5990",
+"b3v c #2f5a75",
+"b29 c #2f5a76",
+"aly c #2f5a78",
+"bDp c #2f5a84",
+"bVt c #2f5a91",
+"aTg c #2f5a92",
+"b6R c #2f5b78",
+"bJu c #2f5c90",
+"bA# c #2f5f86",
+"bWh c #2f6084",
+"aC0 c #2f6096",
+"bf1 c #2f6195",
+"bKx c #2f6196",
+"bPW c #2f6289",
+"caj c #2f628a",
+"cfN c #2f628b",
+"cej c #2f638b",
+"b4o c #2f6390",
+"aNa c #2f648f",
+"bMb c #2f649a",
+"bOy c #2f6590",
+"b1g c #2f6592",
+"bU7 c #2f668d",
+"aRW c #2f6695",
+"bkw c #2f6896",
+"#G2 c #2f6a9c",
+"bz3 c #2f6b9f",
+"cuV c #2f6ba0",
+"crl c #2f6ba1",
+"caU c #2f6ba2",
+"bLX c #2f6c9b",
+"aan c #2f6ca1",
+"cvT c #2f6ca2",
+"ac7 c #2f6da3",
+"bgg c #2f6fa0",
+"#qE c #30202c",
+"aUy c #302434",
+".wJ c #30262e",
+"bEF c #302635",
+".Rr c #302729",
+"aXz c #302735",
+"#E5 c #302835",
+"#qU c #302931",
+".9A c #302938",
+"aEk c #302e37",
+"aFO c #302e3c",
+"aMB c #302e3d",
+"aOT c #302f3c",
+"aKA c #302f40",
+"bg6 c #30303a",
+"avO c #30313f",
+".jZ c #30323e",
+"aRk c #30333c",
+"aIy c #30353e",
+"aqO c #303540",
+"aR6 c #303544",
+".8M c #30363a",
+"aBR c #30363e",
+"#cx c #30373c",
+"bF9 c #303944",
+"bVR c #303a43",
+".eR c #303b44",
+"#l# c #303b45",
+".R9 c #303c43",
+".b. c #303c44",
+".ba c #303c45",
+".W2 c #303c46",
+"ahj c #303c47",
+"bgL c #303c48",
+"bgM c #303c49",
+".VK c #303d44",
+".dP c #303d45",
+".Sl c #303d46",
+"#YM c #303d47",
+"aG7 c #303d48",
+"bxU c #303d49",
+"Qts c #303e46",
+"b.3 c #303e4a",
+"bVe c #303e4b",
+"b.X c #303f48",
+"bVf c #303f4b",
+"aRL c #303f68",
+"bkS c #304049",
+"aQw c #304069",
+"aNS c #30416a",
+"bcT c #30424e",
+"azO c #304451",
+"cr5 c #304466",
+"baq c #304758",
+"ccB c #304960",
+"cnt c #304a65",
+"cqc c #304b64",
+"ch2 c #304b65",
+"cez c #304b66",
+"chA c #304c66",
+"bpb c #304e6c",
+"arj c #304f66",
+"cml c #304f69",
+"cqw c #30506b",
+"bpE c #30506f",
+"bqQ c #30516f",
+"byv c #305172",
+"#Lu c #305371",
+"aj. c #305387",
+"biq c #30577b",
+"b2A c #305a83",
+"bP3 c #305a8f",
+"b28 c #305b76",
+"b1H c #305b77",
+"b1P c #305c78",
+"b1S c #305d79",
+"#PC c #305d7e",
+"#Xz c #305d80",
+"b0d c #305e7c",
+"#XB c #305e80",
+"bWi c #306084",
+"bWg c #306085",
+"bWk c #306185",
+"bWf c #306186",
+"bUS c #306287",
+"aQL c #30628a",
+"bUT c #306388",
+"ccS c #30638c",
+"aYU c #30638e",
+"a2V c #306396",
+"bUW c #306488",
+"c.U c #30648b",
+"cbF c #30648c",
+"bjH c #306495",
+"bdE c #306498",
+"b2f c #30658c",
+"cai c #30658d",
+"cei c #30668f",
+"cfM c #306690",
+"ceg c #306691",
+"cuD c #306694",
+"aYH c #306798",
+"a1y c #306897",
+"bQV c #306997",
+"cab c #306998",
+"cbw c #30699b",
+"cuO c #306da2",
+"#7k c #306ea5",
+"bje c #306f9e",
+"bJt c #3075aa",
+"#lC c #30a19f",
+"#q7 c #31131d",
+"#AN c #312430",
+".G4 c #312433",
+".Z5 c #312526",
+"aJu c #312534",
+"bnW c #312836",
+"bay c #312937",
+".xr c #312d31",
+".M# c #312d3c",
+"aZ4 c #312e3c",
+"a3E c #312f3c",
+"#gf c #313234",
+"bmk c #31323f",
+"bym c #31343e",
+"a#O c #313442",
+".yQ c #313443",
+".Bm c #31353f",
+"aG# c #313558",
+"#f0 c #31363c",
+"aEn c #31363f",
+"bDg c #313640",
+"#ht c #313a42",
+"ag8 c #313b45",
+".eH c #313c44",
+".m7 c #313c45",
+".b# c #313c46",
+".bQ c #313d45",
+".dS c #313d46",
+".Vi c #313d47",
+"axy c #313d48",
+"bb. c #313d49",
+".eP c #313e46",
+"aGn c #313e47",
+"##t c #313e48",
+"#4f c #313e49",
+"bak c #313e4a",
+"axV c #313f49",
+"bVg c #313f4c",
+"cnz c #314c66",
+"bP8 c #314d77",
+"cdE c #314f79",
+"bvx c #31516f",
+"bAU c #315374",
+"cru c #315774",
+"bv2 c #31577b",
+"aZZ c #31578e",
+"#J. c #315876",
+"bv3 c #31597e",
+"bBD c #315a84",
+"bAz c #315b84",
+"b27 c #315c77",
+"b6S c #315e7b",
+"byf c #315f93",
+"#Un c #316090",
+"bWj c #316186",
+"aIG c #316197",
+"bWt c #316285",
+"bUR c #316388",
+"a18 c #316395",
+"bUU c #316488",
+"bUQ c #316489",
+"bUV c #31648a",
+"ccR c #31668f",
+"bXO c #316790",
+"cbE c #316791",
+"bXB c #316793",
+"aRS c #316794",
+"c.Q c #316894",
+"a0f c #316996",
+"aQI c #316a9a",
+"ckr c #316b9a",
+"ccK c #316b9c",
+"cqf c #316ba0",
+"#FT c #316c9e",
+"bB4 c #316d98",
+"aPl c #316d9c",
+"cdj c #316ea3",
+"cvH c #316fa3",
+"c#B c #316fa4",
+"cxw c #316fa5",
+"cib c #316fa6",
+"arr c #3172a8",
+"asq c #3173aa",
+"bMN c #317dae",
+"#ds c #31f9f9",
+"#fQ c #321f28",
+".FO c #32232d",
+"aVv c #322434",
+"aJo c #322437",
+"aY0 c #322535",
+"a3Y c #322635",
+"a3b c #322735",
+"bnx c #322937",
+"#E3 c #322a37",
+"aEE c #322d3c",
+"awD c #32303b",
+".K3 c #32323a",
+"bpk c #323240",
+"afN c #32333d",
+"aWQ c #323544",
+"#g# c #323638",
+"bv8 c #323640",
+"aKz c #32374b",
+".eB c #323941",
+".xC c #323945",
+"aM# c #323a3f",
+".II c #323b43",
+"aFI c #323c63",
+".ge c #323d44",
+".zn c #323d46",
+".bR c #323d47",
+".cr c #323e46",
+".gI c #323e47",
+"#ts c #323e48",
+"##w c #323e49",
+"#cd c #323f46",
+".eO c #323f47",
+"ar9 c #323f48",
+"ajQ c #323f49",
+"#VC c #323f4a",
+"bdf c #323f4b",
+".qQ c #324048",
"bbC c #32404c",
-"bls c #32404d",
-"aMz c #32406b",
-"aXA c #324149",
-"ajz c #32414c",
-"ba8 c #32424e",
-"bPH c #32426e",
-"bQs c #32434b",
-".y4 c #32434c",
-"bR1 c #32444d",
-"#JH c #324451",
-"#UX c #324452",
-"boh c #324455",
-".YZ c #32454e",
-"#Bu c #324752",
-"but c #32475c",
-"a91 c #324a5b",
-"bni c #324e6a",
-"#87 c #324e78",
-"bsp c #325271",
-"ana c #32536c",
-"as# c #325674",
-"baE c #32588f",
-"aIG c #325980",
-"bB1 c #325b85",
-"#Z7 c #325c76",
-"bx8 c #325e94",
-"bcU c #325e95",
-"alR c #325f88",
-"#LW c #326182",
-"aZ. c #326292",
-"#tY c #32678d",
-"byM c #32689b",
-"bIg c #326993",
-"aMH c #326a98",
-"aJS c #326b9a",
-"#HE c #326fa5",
-"bKI c #3270a5",
-"bIl c #3272a7",
-".xz c #32928f",
-".ti c #32fdfe",
-".z8 c #331821",
-"bjq c #332433",
-"aZx c #332635",
-"#mo c #332735",
-"#dq c #332737",
-"a0l c #332936",
-"aB. c #332939",
-".5g c #332b3a",
-".7L c #332c3b",
-"aJ0 c #332e3d",
-"aZe c #332f3e",
-"aAR c #33303b",
-".p# c #333230",
-"#zc c #33323f",
-".lx c #333332",
-".fH c #333333",
-".kP c #333334",
-"bTK c #333434",
-"biU c #333441",
-"bMp c #333536",
-".QW c #33363e",
-".Lr c #33363f",
-"#Eg c #333641",
-"brf c #333643",
-"biX c #333743",
-"#CR c #333843",
-"aLS c #333845",
-"aMT c #333847",
-"bTz c #33393d",
-"aqG c #333a44",
-".mg c #333b40",
-"#2k c #333d44",
-"#XV c #333d48",
-"bM8 c #333e44",
-".Ua c #333e45",
-".9a c #333e46",
-"#yf c #333e47",
-"#5t c #333e48",
-"#WW c #333e49",
-".Ub c #333f45",
-".Ue c #333f46",
-"#jc c #333f47",
-"#AK c #333f48",
-"#54 c #333f49",
-"arO c #333f4a",
-"aMv c #333f69",
-"aCt c #333f6a",
-"#St c #334047",
-"#Z3 c #334048",
-"aYX c #334049",
-"#sy c #33404a",
-"#jx c #33404b",
-"a7K c #33404c",
-"bff c #33404d",
-"aVy c #33406a",
-"aaI c #33414a",
-"auL c #33414b",
-"b#B c #33414c",
-"aM8 c #33414d",
-"ba# c #33424a",
-"a8n c #33424b",
-"beV c #33424d",
-"#9O c #33444c",
-"asD c #334451",
-"bVw c #334454",
-"bQr c #33454e",
-"bR0 c #33464f",
-"#WL c #334653",
-"bUQ c #334751",
-"bVU c #334752",
-"btY c #335271",
-"ayC c #335372",
-"afI c #335389",
-"bJs c #33568c",
-"aMu c #335784",
-"#SM c #335a70",
-"bSK c #335e94",
-"aw8 c #335f7a",
-"bDA c #335f94",
-"a7x c #336297",
-"aFx c #336399",
-"a6o c #33669a",
-"aVD c #33678f",
-"bW. c #33698f",
-"a7P c #336990",
-"aZo c #336c9a",
-"bhR c #336f9e",
-"acN c #3370a5",
-"#Ce c #3371a6",
-"#NJ c #3374ad",
-"bap c #342334",
-"a5s c #342636",
-".ht c #342829",
-"azJ c #342b39",
-"bi0 c #342c3a",
-"aDd c #342e3d",
-".Dl c #342f34",
-"aS9 c #342f3e",
-"#sO c #34313c",
-"aIV c #343240",
-"bJw c #343256",
-".tc c #343329",
-".j9 c #343433",
-".eg c #343434",
-".Y4 c #343641",
-"aQ6 c #343644",
-"a7T c #343645",
-".Rb c #343841",
-".zg c #34393b",
-"#aT c #343a42",
-"ace c #343c40",
-"bJb c #343c41",
-"bG# c #343e43",
-"au# c #343e46",
-"adB c #343e47",
-"#GA c #343e48",
-"aOS c #343e68",
-".Uc c #343f45",
-".Ud c #343f46",
-"#dO c #343f47",
-"#z7 c #343f48",
-"a.5 c #343f49",
-"aac c #343f4a",
-".oT c #343f4d",
-".Xs c #344046",
-".VT c #344047",
-"#zy c #344048",
-"#FJ c #344049",
-"#Sw c #34404a",
-"#1a c #34404b",
-"#CH c #344148",
-"#t8 c #344149",
-"a2Z c #34414b",
-"#T5 c #34414c",
-"a9d c #34414d",
-"bfe c #34414e",
-"aub c #34424c",
-"bfd c #34424e",
-"bhu c #34434d",
-"bTm c #344751",
-"#KZ c #344752",
-"bRZ c #344851",
-"bRY c #344852",
-"bUM c #344a56",
-"aNR c #344c78",
-"brC c #345372",
-"bXD c #345779",
-"bPV c #345990",
-"bx6 c #345a91",
-"#Vy c #345b70",
-"bzg c #345c84",
-"#Zc c #345f78",
-"aWq c #34607b",
-"b#u c #34679a",
-"bha c #346895",
-"bLd c #34689d",
-"#u6 c #34698f",
-"aZm c #346d99",
-"bMu c #346f9e",
-"bhS c #34709f",
-"byN c #3470a2",
-".GD c #34729c",
-"bCe c #3472a7",
-"atG c #3474ac",
-".tV c #34fffe",
-"aT5 c #352535",
-"b#e c #352738",
-".ic c #35292a",
-"aJ5 c #352a37",
-"blN c #352a38",
-"a4u c #352c3c",
-"aJl c #352e3e",
-"#GJ c #35313d",
-"aTP c #353240",
-".o# c #353330",
-"a4o c #35333e",
-"aKp c #353340",
-"bC8 c #353459",
-".c2 c #353535",
-".jo c #353538",
-"adm c #353545",
-".hU c #353636",
-"bSp c #353637",
-".k. c #35363c",
-"#g3 c #353642",
-".zr c #353738",
-"#iV c #353742",
-"biW c #353743",
-".kQ c #35383e",
-"a#4 c #353843",
-".zh c #35393b",
-".zf c #35393c",
-".xr c #353a2c",
-".ze c #353a3c",
-".ly c #353a40",
-"#8N c #353a5d",
-"bRx c #353c45",
-"agn c #353f47",
-"aNJ c #353f69",
-"bIR c #354046",
-".U4 c #354047",
-"#cT c #354048",
-"#Qx c #354049",
-"#9H c #35404a",
-"#Pt c #35404b",
-".Vh c #354147",
-".Yz c #354148",
-"#C1 c #354149",
-"#Rn c #35414a",
-"#kW c #35414b",
-"arb c #35414c",
-"bAV c #35424c",
-"#IQ c #35424d",
-"biO c #35424e",
-"a9V c #35434b",
-"a1I c #35444c",
-"awU c #35444f",
-"bTl c #354953",
-"bUP c #354a54",
-"#AW c #354a56",
-"bDW c #354a5c",
-"bTk c #354b55",
-"apu c #354d82",
-"bIC c #354f78",
-"buA c #355370",
-"a0K c #35558d",
-"auO c #355668",
-"bAD c #35586a",
-"bAa c #355d85",
-"bRo c #355f94",
-"bHu c #355f95",
-"a8s c #356483",
-"#N0 c #35688b",
-"bXi c #356a90",
-"a0g c #356a92",
-"bxO c #356e9a",
-"aXI c #35709e",
-"bQ2 c #3573a8",
-"#CW c #3574a9",
-"#3X c #3575ab",
-"aa4 c #357baf",
-".x0 c #358380",
-"aQY c #362435",
-"a4y c #362637",
-"#ly c #362836",
-"aGw c #362937",
-"#yC c #362e3d",
-"ayn c #362e3e",
-"aXQ c #36313f",
-"#A4 c #36323d",
-"aOM c #363242",
-"Qtw c #363333",
-".oq c #363430",
-"bk5 c #363442",
-"#5n c #363444",
-".j5 c #363535",
-".iE c #363537",
-"aT6 c #363545",
-"aTX c #36354f",
-".jj c #363635",
-".cl c #363636",
-"biV c #363643",
-"bKB c #363737",
-"aEu c #363844",
-"bsg c #363940",
-".zd c #363a3d",
-".zc c #363b3d",
-"aRz c #363b45",
-"bDh c #363b48",
-"aND c #363f69",
-"awY c #364048",
-".U5 c #364148",
-"#be c #364149",
-"#Ka c #36414a",
-"bAW c #36414b",
-"#7U c #36414c",
-".Vg c #364248",
-".3x c #364249",
-"#v. c #36424a",
-"#2u c #36424c",
-"asQ c #36424d",
-"a#b c #36434b",
-"#d0 c #36434d",
-"arL c #36434e",
-"a8v c #36434f",
-"#XP c #36444e",
-"bf6 c #364450",
-"bc1 c #364758",
-"aHj c #36475f",
-"bUO c #364c57",
-"bVS c #364e5a",
-"bX8 c #364e5b",
-"bJK c #364f78",
-"bqQ c #365573",
-"bp7 c #365574",
-"bCw c #36596c",
-"#TF c #365f74",
-"#cO c #365f94",
-"#BD c #36607d",
-"aAq c #366198",
-"b.W c #366397",
-"#Xp c #36698c",
-"aE4 c #3674a4",
-"#x9 c #3675a9",
-"bJr c #3679ae",
-"aop c #367ab2",
-"a5q c #372636",
-"bmp c #372837",
-"a8N c #372838",
-"Qtj c #372e2e",
-"aD3 c #372e3d",
-"aYm c #372e3e",
-"#gJ c #37303e",
-"##m c #37323f",
-"arx c #37333d",
-"atc c #37333e",
-"aC9 c #37365c",
-".aF c #373737",
-"#fS c #373742",
-".e3 c #373837",
-".gp c #373838",
-"bQX c #373939",
-".zb c #373b3e",
-"bw9 c #373b43",
-".zi c #373c3e",
-"bLX c #373c42",
-"bPi c #373e42",
-"bUr c #374047",
-"#2l c #374049",
-"aCL c #37404a",
-".y2 c #374146",
-"bRJ c #374148",
-"aae c #37414a",
-".Zl c #374248",
-".2l c #374249",
-".9X c #37424a",
-"#5O c #37424b",
-"alw c #37424c",
-"agH c #37424d",
-"a3M c #37424e",
-".1U c #374348",
-".Vf c #374349",
-".74 c #37434a",
-"#Qr c #37434b",
-"aaP c #37434c",
-"#V1 c #37434d",
-"asG c #37434e",
-"biw c #37444d",
-"#d1 c #37444e",
-"#YM c #37444f",
-"bf5 c #374451",
-"aag c #374550",
-".SN c #374854",
-"#v9 c #374a55",
-"#YJ c #374a57",
-"bVv c #374c61",
-"#jE c #374d5a",
-"bUN c #374e58",
-"b.L c #374f5b",
-".pD c #37506e",
-"boC c #375574",
-"bpm c #375674",
-"bWw c #375a7b",
-".qZ c #375b87",
-"bfY c #375d93",
-"bA2 c #375e86",
-"#Dl c #37617f",
-"aom c #376385",
-"#6e c #376399",
-"aZ0 c #376695",
-"aQS c #376b8e",
-"#Yp c #376b8f",
-"#RE c #376c90",
-"aNX c #376d93",
-"aXH c #37709b",
-"bxS c #37729e",
-"bxP c #37739f",
-"#E6 c #3776aa",
-"axV c #3777ad",
-"aRZ c #382536",
-"bCt c #382838",
-"#ds c #38293a",
-"aJ3 c #382a38",
-".iW c #382b2d",
-"aL. c #383241",
-"au2 c #38333e",
-"aX. c #383341",
-"boQ c #383342",
-".nG c #383531",
-".kO c #383534",
-"aSE c #383545",
-".hb c #383738",
-".gz c #38373a",
-".Ho c #38373e",
-".44 c #383742",
-".uS c #383825",
-".#T c #383838",
-"#EV c #383844",
-".dF c #383938",
-"aAu c #38394a",
-"bMk c #383a3b",
-".q3 c #383a3d",
-"aLp c #383b42",
-"bBR c #383b43",
-".y9 c #383c3e",
-"bLT c #383d42",
-"bDH c #383d44",
-"aoP c #383d46",
-"aoY c #383e4a",
-"aKK c #383e68",
-"aCy c #383e69",
-"bTy c #383f43",
-"amC c #383f4b",
-".Bd c #384148",
-"an2 c #38414a",
-".V8 c #384249",
-"#0D c #38424b",
-"aS. c #38424c",
-".U6 c #384349",
-".Yd c #38434a",
-"#.H c #38434b",
-"#QA c #38434c",
-"#U5 c #38434d",
-"ah3 c #38434e",
-".Wt c #384449",
-".Ve c #38444a",
-".8J c #38444b",
-"#Qz c #38444c",
-"#Wm c #38444d",
-"#j9 c #38444e",
-"#d2 c #38444f",
-"bfy c #38454e",
-"#A. c #38454f",
-"a6P c #384550",
-"bf3 c #384551",
-"#I4 c #384650",
-"#J2 c #384651",
-"bSU c #38475a",
-".y3 c #384950",
-"aow c #384b7f",
-"#zb c #384d59",
-"#Nd c #384d5c",
-"bVT c #384f5b",
-".XP c #38505c",
-"#84 c #385079",
-"aim c #385084",
-"bW1 c #38515c",
-"bW0 c #38515d",
-"bso c #385775",
-"buk c #38588e",
-"byg c #385e85",
-"bXl c #386083",
-"bB0 c #386087",
-"#UE c #386277",
-"#E4 c #386484",
-"a2w c #386591",
-"b.Y c #386a9b",
-"aBI c #386aa0",
-"#2J c #386c90",
-"#MW c #386d92",
-"bMx c #386fa2",
-"byO c #3873a0",
-"aOY c #3875a4",
-"#LY c #3876a9",
-"aJx c #3878a7",
-"bMv c #3878ab",
-"axU c #3879af",
-"bG1 c #387aae",
-"bOh c #387bab",
-"bE7 c #387fb3",
-"aUX c #392636",
-"a8O c #392738",
-"a7h c #392739",
-"byY c #392838",
-"a9q c #392c3a",
-"##j c #392c3c",
-"#qu c #39323e",
-"#Bv c #39333e",
-"aEB c #393442",
-".lv c #393534",
-"aN3 c #393544",
-"aHB c #39365b",
-".rc c #39372b",
-"QtV c #393939",
-"bJj c #39393a",
-".ci c #393a39",
-"bSw c #393a3b",
-"bPA c #393a3c",
-"bPF c #393b3c",
-"bNU c #393b3d",
-".z. c #393d3f",
-".z# c #393e40",
-"aTU c #393e48",
-"bPd c #394145",
-"#qb c #39434c",
-"#6F c #39434d",
-"asH c #39434e",
-".U7 c #39444a",
-".Np c #39444b",
-"#dP c #39444c",
-"#Pw c #39444d",
-"#Y. c #39444e",
-"agq c #39444f",
-".27 c #39454a",
-".5s c #39454b",
-".ZS c #39454c",
-"#RB c #39454d",
-"#Wo c #39454e",
-"#lT c #39454f",
-"bib c #394550",
-"#1Y c #39464e",
-"arH c #394650",
-"bmQ c #394651",
-"bf4 c #394652",
-"ae5 c #39474f",
-"#Gp c #394752",
-"aMa c #394853",
-"#us c #394c57",
-"#pp c #394c58",
-"arh c #394f83",
-"#86 c #395079",
-"aNm c #395083",
-"abe c #395287",
-"bX7 c #39535f",
-"a.l c #39567e",
-"bIy c #39588e",
-"aPC c #395890",
-"agP c #39596c",
-"aFy c #395980",
-"bg9 c #395b7d",
-"bCY c #396189",
-"#1j c #39667e",
-".Cr c #396785",
-"a0M c #396795",
-"aZq c #396d8f",
-"#3T c #396f93",
-"#MX c #39719b",
-"bcj c #3974a2",
-"bxR c #3975a2",
-"bzM c #3977a5",
-"bIh c #3979ad",
-"axT c #3979ae",
-"bBi c #397aad",
-"#45 c #397aae",
-"a3J c #3a2536",
-"a8P c #3a2738",
-"#od c #3a2839",
-"aK4 c #3a2a39",
-"a17 c #3a2b3a",
-"#pS c #3a2c3d",
-"aEK c #3a2f3d",
-"#1L c #3a3154",
-"bOl c #3a3155",
-"aMn c #3a3241",
-"asm c #3a3341",
-"a#6 c #3a3445",
-".mX c #3a3632",
-".md c #3a3633",
-".e4 c #3a373c",
-".qn c #3a3834",
-"Qtq c #3a3939",
-".el c #3a393a",
-".KS c #3a3941",
-".#P c #3a3a3a",
-".b. c #3a3b3a",
-"bQS c #3a3b3c",
-".za c #3a3e40",
-"btM c #3a3e45",
-"aGU c #3a3e46",
-"adf c #3a3e4c",
-"bNL c #3a4044",
-"bLQ c #3a4146",
-"alr c #3a434c",
-"bM7 c #3a444b",
-"#Ig c #3a444e",
-".rL c #3a4451",
-".U8 c #3a454b",
-"#B4 c #3a454c",
-"#yU c #3a454d",
-"#Qy c #3a454e",
-"adG c #3a454f",
-"#WJ c #3a4550",
-".Ws c #3a464b",
-".Xr c #3a464c",
-".Yy c #3a464d",
-"#RC c #3a464e",
-"#Yj c #3a464f",
-"#4m c #3a4650",
-"#Go c #3a4651",
-"bns c #3a4652",
-"alK c #3a474e",
-"a41 c #3a4751",
-"#6V c #3a4752",
-"bgY c #3a4753",
-".PF c #3a484f",
-"aqk c #3a4851",
-"#I# c #3a4852",
-"aM# c #3a4953",
-"bmC c #3a4a59",
-"auG c #3a4e5b",
-"aej c #3a4e82",
-"ayB c #3a4f63",
-"aNt c #3a5069",
-"avE c #3a525f",
-"#KY c #3a5260",
-"buz c #3a566f",
-"btX c #3a5876",
-"brB c #3a5877",
-"bsK c #3a588f",
-"avp c #3a5c93",
-"bzf c #3a6188",
-"bdW c #3a668d",
-"bgO c #3a6797",
-"awt c #3a6c89",
-"aLI c #3a74a3",
-"aNV c #3a75a5",
-"bxQ c #3a76a3",
-"bwx c #3a76a9",
-"#wY c #3a77a4",
-"aZn c #3a78a6",
-"#y# c #3a7aad",
-"aE3 c #3a7bab",
-"#zq c #3a7bae",
-"aJB c #3a7cac",
-"#A9 c #3a7db1",
-"#Ax c #3a82b5",
-".vy c #3af9f8",
-".DE c #3b1a2b",
-"b#O c #3b2336",
-"#mD c #3b2c3c",
-"bjG c #3b2e3d",
-"bIq c #3b3155",
-"#x1 c #3b3341",
-"auv c #3b3342",
-".hT c #3b3638",
-"#DQ c #3b3641",
-".he c #3b3740",
-".fD c #3b383a",
-".dG c #3b383c",
-".ee c #3b3a3b",
-"Qtg c #3b3b3b",
-"bPx c #3b3c3d",
-"bjm c #3b3c44",
-"aKJ c #3b3c66",
-"#7# c #3b3d44",
-"#bL c #3b3e46",
-"apR c #3b3f48",
-"bDK c #3b404a",
-"bQF c #3b4346",
-"#YU c #3b444e",
-".V9 c #3b454c",
-".QV c #3b454e",
-"#1S c #3b454f",
-".Vd c #3b464c",
-".U9 c #3b464d",
-"#b6 c #3b464e",
-"#Hn c #3b464f",
-"adH c #3b4650",
-"adI c #3b4651",
-".3w c #3b474d",
-"#KW c #3b474e",
-"#OI c #3b474f",
-"abZ c #3b4750",
-"#dZ c #3b4751",
-"bcr c #3b4752",
-"bl2 c #3b4753",
-"bjl c #3b4851",
-"#Ha c #3b4852",
-"bd2 c #3b4853",
-"bgX c #3b4854",
-"#Hc c #3b4952",
-"#H9 c #3b4953",
-"buZ c #3b4a53",
-"aVK c #3b4a54",
-"a4k c #3b4d56",
-"aif c #3b4e5b",
-"ahC c #3b4e5c",
-"#85 c #3b5179",
-"a.m c #3b517a",
-"aNN c #3b517b",
-"bVu c #3b556f",
-"a1v c #3b558b",
-"bWb c #3b5b7c",
-"bXC c #3b5d7d",
-"bxe c #3b5f83",
-"aCf c #3b6297",
-".pf c #3b6771",
-"a7R c #3b677c",
-"bcb c #3b6797",
-".s5 c #3b68a3",
-"#GN c #3b6f93",
-".yk c #3b7670",
-"as0 c #3b77a7",
-"byR c #3b78a3",
-"aPW c #3b79a8",
-"#x8 c #3b7bae",
-"bKJ c #3b7baf",
-"bIj c #3b7cae",
-"#yH c #3b7caf",
-"aJC c #3b7eae",
-"aJy c #3b7faf",
-"anj c #3b81b6",
-"a#t c #3b82b4",
-"b#N c #3c2436",
-"aTi c #3c2537",
-"aCo c #3c2a3c",
-"#lF c #3c2c3d",
-"aBp c #3c2d3e",
-".9S c #3c303f",
-"aVp c #3c3040",
-"bKR c #3c3154",
-"aCj c #3c333c",
-"#wf c #3c3440",
-"##a c #3c3843",
-"#fK c #3c3a44",
-".ch c #3c3b3c",
-"#Hy c #3c3b47",
-"Qtv c #3c3c3c",
-"aLF c #3c3c66",
-".#N c #3c3d3c",
-".#s c #3c3d3d",
-"bTO c #3c3d3e",
-".6j c #3c3d44",
-".M1 c #3c3d46",
-"bAn c #3c3d67",
-"bPB c #3c3e3f",
-"aaU c #3c3e44",
-".zm c #3c4042",
-"bJe c #3c4146",
-"bDI c #3c414b",
-"bJh c #3c4247",
-"bS2 c #3c464e",
-"axJ c #3c464f",
-".V. c #3c474e",
-"#eU c #3c474f",
-"#Gz c #3c4750",
-"#1# c #3c4751",
-"acd c #3c4752",
-".Wr c #3c484d",
-".ZR c #3c484f",
-"ao6 c #3c4850",
-"#4S c #3c4852",
-"a9f c #3c4853",
-"#1. c #3c4950",
-"a1Q c #3c4951",
-"bqX c #3c4952",
-"#Gq c #3c4953",
-"ap# c #3c4954",
-"bgW c #3c4955",
-"awl c #3c4a54",
-"bmV c #3c4b55",
-"aM9 c #3c4c57",
-"buy c #3c556b",
-"aZZ c #3c558b",
-"bU# c #3c578d",
-"bqP c #3c5977",
-"bpk c #3c5a78",
-"av0 c #3c6092",
-"bA# c #3c6389",
-"a1w c #3c6995",
-".s4 c #3c6aa6",
-"aQT c #3c6d85",
-"#Yo c #3c769d",
-"alS c #3c78a7",
-"#2K c #3c79a5",
-"aPV c #3c7ba9",
-"aW5 c #3c7cab",
-"#8w c #3c7cb2",
-"bBj c #3c7db0",
-"aJD c #3c7fb0",
-"aoo c #3c81b7",
-".ut c #3cfffe",
-"bny c #3d2537",
-".zY c #3d262b",
-"a59 c #3d273a",
-"bBx c #3d2838",
-"aW8 c #3d2b3a",
-"#dt c #3d2b3c",
-"#w# c #3d2c3b",
-"aYE c #3d2d3f",
-"aWh c #3d2e3f",
-".5m c #3d303f",
-"#0v c #3d3154",
-"a8A c #3d3244",
-"bFg c #3d3246",
-"b.H c #3d3342",
-"aJe c #3d3442",
-"aUG c #3d3443",
-"#BA c #3d3540",
-"a5# c #3d3641",
-".P7 c #3d3944",
-"bsa c #3d3946",
-".bS c #3d3b3d",
-"afB c #3d3b43",
-"#ky c #3d3b46",
-".a9 c #3d3c3d",
-"a0c c #3d3c65",
-"Qto c #3d3d3d",
-"Qt3 c #3d3e3d",
-"QtA c #3d3e3e",
-"bL8 c #3d3e3f",
-"ak7 c #3d3f4d",
-".A4 c #3d4245",
-"ajw c #3d434d",
-"bUq c #3d454d",
-"ai6 c #3d464e",
-".V# c #3d484e",
-"#bf c #3d4850",
-"#SI c #3d4851",
-"#Z. c #3d4852",
-"ae6 c #3d4853",
-".28 c #3d494e",
-".Xq c #3d494f",
-"#t9 c #3d4950",
-"#MR c #3d4951",
-"bix c #3d4952",
-"#d3 c #3d4953",
-"a9e c #3d4954",
-"bgV c #3d4955",
-"a3y c #3d4a52",
-"brU c #3d4a53",
-"#9n c #3d4a54",
-"awq c #3d4a55",
-"bgU c #3d4a56",
-"#XM c #3d4b55",
-"#0E c #3d4c57",
-"a1b c #3d4d57",
-"aM6 c #3d4d58",
-"aii c #3d516c",
-"acZ c #3d5185",
-"aHi c #3d526f",
-"aWR c #3d5389",
-"#aj c #3d578d",
-"boB c #3d5a77",
-"bpl c #3d5a78",
-"bDV c #3d5d7d",
-"bWv c #3d5f80",
-"a6W c #3d6d9c",
-"a55 c #3d6f8b",
-"#CT c #3d7196",
-"#Ww c #3d7292",
-"#QF c #3d79a2",
-"bQ3 c #3d7cab",
-"axW c #3d7fb3",
-"bIk c #3d83b5",
-".sD c #3deff3",
-"b.K c #3e2336",
-"aTk c #3e2537",
-"aU5 c #3e2638",
-"#aZ c #3e2b3d",
-"a8Q c #3e2c3e",
-"aWY c #3e2d3f",
-".zC c #3e2e2f",
-"aQ1 c #3e2e40",
-"bFs c #3e2e4d",
-"bnz c #3e3243",
-"#uK c #3e3341",
-"aMX c #3e3443",
-"az# c #3e3541",
-".c3 c #3e3640",
-".dy c #3e383d",
-"bqs c #3e3846",
-"aBj c #3e395f",
-"#9U c #3e3a43",
-"awF c #3e3a44",
-"Qtd c #3e3b3b",
-"#ls c #3e3b47",
-".ae c #3e3c3e",
-"#iZ c #3e3d44",
-".#M c #3e3e3d",
-"Qti c #3e3e3e",
-"QtB c #3e3f3e",
-"Qtm c #3e3f3f",
-".TU c #3e3f48",
-"QtD c #3e403e",
-".zj c #3e4143",
-"ak6 c #3e414e",
-"bml c #3e424d",
-".w0 c #3e442d",
-"bPm c #3e4449",
-"bNR c #3e454a",
-"bSb c #3e464a",
-"aox c #3e4679",
-"adQ c #3e474e",
-"bHM c #3e484d",
-".Va c #3e484f",
-"ae3 c #3e4851",
-"#3i c #3e4852",
-".Vb c #3e494f",
-".1. c #3e4950",
-"##v c #3e4951",
-"#LH c #3e4952",
-"ahG c #3e4953",
-"adP c #3e4954",
-".0J c #3e4a4f",
-".Yx c #3e4a50",
-"#qM c #3e4a51",
-"#Dd c #3e4a52",
-"#KP c #3e4a53",
-"#LI c #3e4a54",
-"apm c #3e4a55",
-"bnt c #3e4b55",
-"bhM c #3e4b56",
-".ye c #3e4c3a",
-"#JM c #3e4c55",
-"abX c #3e4c56",
-"#VX c #3e4c57",
-"bd7 c #3e505d",
-"bG. c #3e5061",
-"#Ge c #3e515f",
-"buu c #3e5263",
-"bux c #3e5468",
-"aek c #3e568c",
-"a.c c #3e588d",
-"#MB c #3e5f76",
-"bw# c #3e6082",
-"#bZ c #3e6194",
-"agN c #3e6377",
-"bA1 c #3e648a",
-"b.p c #3e6d9c",
-"#57 c #3e6e88",
-".sn c #3e6eac",
-"#3J c #3e6f89",
-"#DW c #3e7399",
-"#Cb c #3e749a",
-"#BE c #3e769c",
-"#QH c #3e79a3",
-"aID c #3e7aa8",
-"aWp c #3e7ba7",
-"aon c #3e7ba9",
-"aJE c #3e7dae",
-"bTZ c #3e7fb0",
-"#7f c #3e7fb1",
-"aGh c #3e80b0",
-"#En c #3e80b2",
-"#BH c #3e81b3",
-"#K5 c #3e81b6",
-"bKL c #3e84b6",
-".1s c #3e87b8",
-".0. c #3e87b9",
-"#Oy c #3e8bbc",
-"bCv c #3f2839",
-"bvM c #3f283a",
-"boe c #3f2b3b",
-"#lE c #3f2b3d",
-"aCE c #3f2c3f",
-"#qm c #3f2d3c",
-"aB5 c #3f2d3f",
-"aP# c #3f3244",
-"aTn c #3f3345",
-"bwH c #3f3358",
-"as9 c #3f3443",
-"#GK c #3f3541",
-"#kD c #3f3641",
-".#0 c #3f3b3f",
-"aKE c #3f3c65",
-"Qt4 c #3f3d3f",
-".#g c #3f3e3f",
-"aEn c #3f3e46",
-"awE c #3f3e4d",
-"Qtb c #3f3f3f",
-"QtC c #3f403f",
-"bPt c #3f4041",
-"brn c #3f4047",
-"bOa c #3f4142",
-"acf c #3f4242",
-"bV1 c #3f4549",
-"aC# c #3f4751",
-"#I2 c #3f4851",
-"#OC c #3f4852",
-".Vc c #3f4950",
-"#J1 c #3f4951",
-"#Rt c #3f4952",
-".xT c #3f4a35",
-".Wq c #3f4a4f",
-".Ma c #3f4a50",
-".3v c #3f4a51",
-"#Ed c #3f4a52",
-"adO c #3f4a53",
-"#Qm c #3f4a54",
-"ake c #3f4a55",
-".4F c #3f4b51",
-"#DM c #3f4b52",
-"#Wu c #3f4b53",
-"#oA c #3f4b54",
-"#nS c #3f4b55",
-"a8w c #3f4b56",
-"bhL c #3f4b57",
-"as2 c #3f4b7e",
-"bBw c #3f4c55",
-"#D3 c #3f4c56",
-"avh c #3f4d57",
-"aw4 c #3f4d58",
-"a20 c #3f4e57",
-"#6J c #3f4f59",
-"buw c #3f5366",
-".yu c #3f5445",
-"a2u c #3f548a",
-"bvs c #3f5589",
-"#7l c #3f568b",
-"bp6 c #3f5c79",
-"bsn c #3f5c7a",
-"bUk c #3f5c83",
-"bKM c #3f5d8f",
-"#fZ c #3f6195",
-"byf c #3f658b",
-"aYi c #3f6b94",
-"aJF c #3f74a3",
-"aw9 c #3f799e",
-"#tZ c #3f7aa0",
-"#RG c #3f7ba4",
-"bU3 c #3f7da7",
-"#It c #3f7da8",
-"bJp c #3f81b0",
-"bD# c #3f81b3",
-"#7c c #3f83b9",
-"ani c #3f85b9",
-".YX c #3f87b9",
-".TG c #3f8abe",
-"bk3 c #402437",
-"aU. c #40293a",
-"avD c #402a3d",
-"#.C c #402b3d",
-"aXV c #402d3d",
-"aMO c #402d3f",
-".6t c #403140",
-"aVF c #403144",
-"bFn c #403246",
-"aPj c #403443",
-".am c #403541",
-".#f c #403640",
-".aD c #403840",
-"QtL c #403940",
-"aMF c #403962",
-"QtP c #403a40",
-"aEA c #403a46",
-"#7t c #403a5e",
-".#L c #403b40",
-"aBY c #403b66",
-"QtK c #403c40",
-"QtQ c #403d40",
-"Qtc c #404040",
-"ak5 c #40414e",
-".vq c #404224",
-"bQY c #404243",
-"bSQ c #40444d",
-"bV3 c #40474b",
-"bFU c #404763",
-"bLH c #40484b",
-".X. c #404a50",
-".W. c #404a51",
-"#kN c #404a52",
-"#KO c #404a53",
-"ah0 c #404a54",
-"aq3 c #404a55",
-".4j c #404b51",
-".9Y c #404b52",
-"#w4 c #404b53",
-"#I3 c #404b54",
-"#Hb c #404b55",
-"abU c #404b56",
-".Wp c #404c51",
-".2k c #404c52",
-"#pe c #404c53",
-"#0X c #404c54",
-"#I. c #404c55",
-"#3h c #404c56",
-"aYH c #404c57",
-"a0m c #404c58",
-"aXO c #404d56",
-"ajt c #404d57",
-"aZv c #404d58",
-"a0Y c #404e56",
-"#H# c #404e57",
-"aWD c #404e58",
-"a2B c #404f56",
-"#J0 c #404f5b",
-"buv c #405364",
-"#JI c #405664",
-"bPR c #40578d",
-".Z4 c #405a67",
-"bqO c #405d7a",
-"brA c #405d7b",
-"bu3 c #405f7e",
-"bek c #406194",
-"bBZ c #40658b",
-"b#t c #406798",
-"a3j c #406b95",
-"aWb c #406c95",
-".rG c #406faa",
-"bOr c #407da5",
-"aMr c #407daa",
-"#Jm c #407faa",
-"byP c #4081af",
-"aZp c #4082b1",
-"#8z c #4082b4",
-"bCd c #4083b5",
-"#59 c #4084b9",
-"#s1 c #4086b5",
-".XV c #4088ba",
-".rZ c #40c6cd",
-".w6 c #40cac8",
-".v7 c #40f4f3",
-"aMl c #41273a",
-"#mx c #412b3d",
-"a9t c #412e3d",
-"#wd c #41303e",
-".7N c #413140",
-"aD0 c #413155",
-"bkr c #413243",
-"bV8 c #413256",
-"QtN c #413441",
-"be1 c #413444",
-"QtM c #413541",
-"QtO c #413641",
-"aSv c #413b65",
-"ak8 c #413c4c",
-"aI4 c #413d48",
-"aHW c #413e44",
-"byK c #413e67",
-"Qt# c #414141",
-"bPs c #414242",
-"bMg c #414243",
-"bJd c #41474b",
-"bQI c #41474c",
-".Ez c #41484c",
-"#cU c #414b53",
-"#K# c #414b54",
-"#7V c #414b55",
-"azr c #414b56",
-".Xp c #414c52",
-"#tn c #414c54",
-"#U4 c #414c55",
-"#H8 c #414c56",
-"aW9 c #414c57",
-"aZw c #414c58",
-"#9j c #414d54",
-"anZ c #414d55",
-"aoh c #414d56",
-"aWu c #414d57",
-"aXP c #414d58",
-"bhK c #414d59",
-"a3N c #414e57",
-"bfx c #414e58",
-"bhJ c #414e59",
-"aTA c #414f58",
-"a7p c #414f59",
-"a5T c #415059",
-"bK8 c #415179",
-"be0 c #41535f",
-"beW c #415562",
-"ayr c #415b68",
-"bsm c #415e7b",
-"bVt c #415f7d",
-"bPW c #416194",
-"bze c #41678c",
-".C2 c #416c88",
-"a7z c #416f9d",
-"#4W c #41748e",
-"#Em c #417ba4",
-"a2S c #417da5",
-"aLN c #417ea5",
-"aKQ c #4180b0",
-"aYx c #4182b1",
-"bUZ c #4183b2",
-"bT1 c #4184b2",
-"bE4 c #4184b5",
-"baj c #4185b9",
-".VP c #4188b9",
-".3S c #4188ba",
-".WV c #4189bb",
-"#Nv c #418cbc",
-"a4t c #422538",
-"a22 c #42263a",
-"#hV c #422b3a",
-"aIN c #422b3c",
-"aKs c #422c3f",
-"a9s c #422e3e",
-"blM c #423143",
-"a.F c #423245",
-".a8 c #423442",
-"#zU c #423443",
-".cV c #423540",
-".eV c #42363f",
-"aLT c #423a4a",
-"aDV c #423a63",
-"Qte c #423b3b",
-".7x c #423c47",
-"#o1 c #423d46",
-"#cw c #423d48",
-"#uH c #423d49",
-"aNs c #423e54",
-".Lb c #423f46",
-"aGT c #424048",
-"Qta c #424141",
-"ak4 c #42414d",
-"Qt. c #424242",
-"bn9 c #42434d",
-"bMU c #42444e",
-"bO4 c #42484c",
-"axK c #424b53",
-".W# c #424c52",
-".X# c #424c53",
-"#Zy c #424c56",
-".0K c #424d52",
-".Wo c #424d53",
-".09 c #424d54",
-"#KV c #424d55",
-"#2t c #424d57",
-"bDN c #424d58",
-"alo c #424e56",
-"biy c #424e57",
-"#P8 c #424e58",
-"#Wt c #424f57",
-"a4v c #424f58",
-"aRd c #424f59",
-"a0X c #425058",
-"bp0 c #425059",
-"#WQ c #42505a",
-"#I1 c #42505c",
-"bWg c #425179",
-"bRn c #42568b",
-"a#G c #42588d",
-"#IM c #425967",
-"#na c #425b69",
-"brz c #425f7c",
-"bBN c #426194",
-"bXB c #426283",
-"a9P c #42709e",
-"#Py c #42738c",
-"bzS c #42758d",
-"anb c #42789e",
-"#Vw c #427999",
-"avV c #427a99",
-"#FT c #4280a9",
-"#3U c #4280ab",
-"a8r c #4281a8",
-"#OV c #4281ae",
-"aMs c #4281b1",
-"aHz c #4282b2",
-"#NX c #4283b0",
-"bXb c #4284b4",
-"aJz c #4285b5",
-"aJA c #4286b6",
-"#x7 c #4286b7",
-"#44 c #4287b9",
-"#Qf c #4288b9",
-"#Ox c #4289ba",
-"bMw c #428bbb",
-"#6# c #428ebe",
-"#Nw c #428ebf",
-"bvo c #4291c2",
-"a2a c #432639",
-"bAB c #43283a",
-"bwS c #43283b",
-"aYL c #432b3b",
-"aGo c #432c3f",
-"aXa c #432e3e",
-"bHj c #432f53",
-"bkf c #433042",
-"bhv c #433344",
-"aKo c #433443",
-"atO c #433444",
-".go c #43373e",
-"aQG c #433a63",
-"aFG c #433e4a",
-".pH c #43403c",
-"Qtf c #434242",
-".cr c #434343",
-"bTS c #434446",
-"bLP c #43494e",
-"bNF c #434a4e",
-".Wa c #434d53",
-"#b7 c #434d55",
-"#Jd c #434d56",
-"ahF c #434d57",
-".Xo c #434e54",
-".5K c #434e55",
-"#te c #434e56",
-"#53 c #434e57",
-"#Gn c #434e58",
-"#09 c #434f56",
-"#wA c #434f58",
-"#d4 c #434f59",
-"bv3 c #434f5a",
-"aVo c #435058",
-"aDi c #435059",
-"#f9 c #43505a",
-"#2j c #435159",
-"a0B c #43515b",
-"a6u c #43515d",
-"aN. c #43535f",
-"#8H c #435589",
-"bCJ c #43568b",
-"awQ c #435d6a",
-"beu c #435e75",
-"bte c #43607c",
-"btW c #43607d",
-"bXA c #436484",
-"aJG c #436592",
-"bQ. c #43688c",
-"#JZ c #436984",
-"#MM c #436c92",
-"#Wx c #437895",
-"#BF c #4381ad",
-"aOX c #4382b1",
-"aLK c #4382b2",
-"#Ug c #4383b2",
-"#NZ c #4385b3",
-"bBk c #4386b7",
-"bzL c #4387b6",
-"#y. c #4387b8",
-"#3W c #4388bb",
-".4V c #4389ba",
-".UH c #438abb",
-"bJq c #438bbc",
-"#8A c #438dbd",
-"bDa c #438dbe",
-"bzC c #438ebe",
-"#40 c #4390c1",
-"bsF c #4391c2",
-".wB c #43e7e5",
-".0r c #442438",
-"aVP c #442539",
-"a0r c #442639",
-"bDn c #442839",
-"bnG c #442c3c",
-"aKX c #442c3f",
-"aXM c #442c40",
-"aK6 c #442d3d",
-"#r0 c #442d3e",
-".#K c #442f44",
-"#sg c #442f52",
-".z3 c #443133",
-".24 c #443141",
-".ad c #443143",
-"adn c #443245",
-".cg c #443342",
-".bI c #443343",
-".cs c #443346",
-"aL# c #443444",
-".em c #443649",
-"#AX c #443844",
-"atU c #443944",
-".fO c #44394b",
-"#fR c #443c48",
-".hY c #443f50",
-"aQU c #444056",
-"#8s c #444248",
-".S2 c #444249",
-".tP c #44441c",
-"QtE c #444444",
-"#cz c #44444c",
-"bL2 c #444546",
-".y1 c #444647",
-"bFX c #44464f",
-"aCQ c #444958",
-"a5n c #444b56",
-"a56 c #444b57",
-"bNz c #444c4f",
-"#B3 c #444d54",
-"bS1 c #444d55",
-"#Hm c #444d56",
-".Xa c #444e54",
-".Wb c #444e55",
-".WY c #444e56",
-"#Gy c #444e57",
-"acc c #444e58",
-".Wn c #444f55",
-"#NP c #444f56",
-"#u. c #444f57",
-"#Vs c #444f58",
-"#4R c #444f59",
-".9b c #445056",
-"#Ux c #445058",
-"#yj c #445059",
-"#mP c #44505a",
-"biB c #44505c",
-"#fa c #44515b",
-"bnh c #44515d",
-"aYk c #44525b",
-"adE c #44525c",
-"bc. c #445288",
-"be7 c #44558b",
-"bnY c #445667",
-"aHh c #445b7d",
-"#rZ c #445e6c",
-"bWu c #446484",
-"bA. c #44698d",
-"bxd c #44698e",
-"#OK c #446f84",
-"bAu c #446f9b",
-"a8z c #44768f",
-"aOW c #4479a7",
-"#KM c #4481ab",
-"aO2 c #4483b3",
-"aYC c #4484af",
-"a2U c #4485b2",
-"aJX c #4487ae",
-"#HB c #4487b5",
-"#tU c #4487b7",
-"bGZ c #4487b8",
-"#Ke c #4488b6",
-"#wo c #4488b8",
-"#DX c #4488b9",
-"#92 c #448aba",
-"#Nu c #448bba",
-"#Mx c #448bbb",
-"#7g c #448cbc",
-"#LD c #448dbd",
-"#93 c #448ebe",
-"#zp c #448fbf",
-"#vL c #448fc0",
-"#tQ c #4490c0",
-".uZ c #44fffd",
-"at4 c #45263a",
-"aT. c #452c40",
-"#3c c #452f52",
-"aZh c #453250",
-"aNo c #453344",
-".iB c #45393c",
-"aNC c #453962",
-"aLx c #453b63",
-"btU c #454249",
-".ck c #454545",
-"bN5 c #454647",
-".zv c #454748",
-".ww c #454a2b",
-"bTr c #454a4e",
-"bLK c #454c4f",
-"bLG c #454c50",
-".Wc c #454f56",
-"ak# c #454f58",
-"adN c #454f59",
-"#hc c #455057",
-"#Rs c #455058",
-"#0J c #455059",
-"bpB c #45505a",
-"biz c #45505b",
-"#v# c #455158",
-"bta c #45515a",
-"#dY c #45515b",
-"biA c #45515c",
-"an6 c #45525b",
-"#gf c #45525c",
-"aBv c #45525d",
-"aah c #45535d",
-"a1B c #45545c",
-"an1 c #455c6b",
-"bdS c #45627c",
-"bJH c #456295",
-"bDU c #456483",
-".C3 c #45718c",
-"aMq c #45729b",
-"a8h c #45729f",
-"bCx c #45768f",
-"bFC c #45778f",
-"#UC c #4580a4",
-"#vS c #4583aa",
-"bhb c #4585b3",
-"#Td c #4585b4",
-"a54 c #4588b6",
-"#3P c #4588b8",
-"#Sg c #4589b8",
-"#Qd c #4589b9",
-"#yG c #458aba",
-"#GP c #458abb",
-"#Ow c #458bbb",
-"#Kh c #458bbf",
-"#Ov c #458cbb",
-"#Pm c #458cbc",
-"bKK c #458cbd",
-"anh c #458cbf",
-"#Qe c #458dbc",
-"btw c #458dbd",
-"#uS c #458ebe",
-"#uT c #458ebf",
-"#tR c #458fbf",
-"bAq c #4590c1",
-"#yF c #4591c1",
-"aIh c #4593c5",
-".tj c #45fdfe",
-".1K c #462338",
-"aUW c #462439",
-".zH c #46292f",
-".zO c #462930",
-".zG c #462a30",
-".zI c #462b31",
-"aLR c #462b40",
-".#e c #462b46",
-"#qB c #462c3f",
-"aXB c #462c40",
-".#t c #462d47",
-"aU8 c #462e3f",
-".#1 c #462e46",
-"bkt c #463041",
-"aEJ c #463141",
-"#jH c #463242",
-"aP7 c #463245",
-".zz c #463333",
-"bki c #463345",
-"aFd c #46394a",
-"bbE c #463a46",
-"ak3 c #46414d",
-".sz c #464424",
-"Qtl c #464444",
-"apv c #464576",
-".fL c #464646",
-"bMh c #464747",
-".y8 c #464849",
-".v1 c #464a28",
-"bSl c #464b50",
-".TI c #464b54",
-".CU c #464c4f",
-"awm c #464f59",
-".Xb c #465056",
-".Wd c #465057",
-"#dQ c #465058",
-"#Ru c #465059",
-".Wm c #465156",
-".ZQ c #465157",
-"#ES c #465158",
-"#DL c #465159",
-"#6W c #46515a",
-"aPo c #46515b",
-"#RZ c #465259",
-"#fr c #46525b",
-"bF2 c #46525c",
-"#qe c #46535c",
-"acv c #46535d",
-"aY8 c #465387",
-"a8o c #46545b",
-"aPn c #46545e",
-"bHy c #46547b",
-"a1A c #46555d",
-"#WP c #46555f",
-"bFN c #46558a",
-"abh c #46578b",
-"bbx c #46586a",
-"bu2 c #46627f",
-"bF9 c #466483",
-"bVs c #466686",
-"bA0 c #466a8f",
-"bye c #466b8f",
-"bM0 c #466f98",
-"auj c #46738b",
-"a87 c #46739f",
-".GE c #467da3",
-"aIB c #467ead",
-"bzH c #4683af",
-"a08 c #4687b3",
-"#BG c #4687b4",
-"#Dm c #4688b7",
-"a2T c #4689b7",
-"#Se c #4689b8",
-"a13 c #468ab7",
-"#Sf c #468ab9",
-"#Iu c #468aba",
-"#Jn c #468abb",
-"alT c #468abd",
-"aYw c #468bb9",
-"#Nt c #468bba",
-".56 c #468bbb",
-"aGg c #468bbc",
-"#Jo c #468cbb",
-"#uU c #468cbc",
-"#LX c #468cbe",
-"#tS c #468dbd",
-"bGY c #468dbe",
-"bD. c #468ebe",
-"bzD c #468ebf",
-"bzK c #468fbf",
-"bzE c #468fc0",
-"a#u c #4690bf",
-"bBh c #4690c0",
-"a2H c #4691c0",
-"bAr c #4691c1",
-"bc8 c #4691c3",
-"a6L c #472439",
-"Qt5 c #472948",
-".zZ c #472a31",
-".zP c #472b31",
-".zJ c #472e33",
-"brl c #472e3e",
-"aFY c #472f40",
-"bQ8 c #472f52",
-"#iP c #473142",
-"#9f c #473145",
-"#zR c #473444",
-"aEC c #473646",
-"aNI c #473861",
-"#r5 c #473945",
-"#sL c #473f4b",
-"au0 c #47404a",
-".qz c #47432c",
-".aL c #474747",
-"bNW c #474848",
-"bSv c #474849",
-".zl c #47494a",
-"aEf c #474952",
-"bKq c #474e51",
-"bLM c #474e52",
-"aBA c #474f59",
-"bIQ c #475056",
-".We c #475157",
-"#jd c #475158",
-"#Y9 c #47515a",
-"#52 c #47515b",
-".Wl c #475257",
-".Yw c #475258",
-"#rB c #475259",
-"#2s c #47525b",
-"bdt c #47525c",
-".66 c #47535a",
-"#em c #47535c",
-"#zC c #47535d",
-"#Ro c #47545c",
-"ar7 c #47545e",
-"#9r c #47555e",
-"bf8 c #475560",
-"an5 c #47565f",
-"by8 c #476193",
-"bc3 c #47637d",
-"bWt c #476786",
-"bVh c #476a98",
-"bBY c #476b90",
-"b.z c #47738e",
-"#1e c #477f9e",
-"b#a c #4784ae",
-"#wu c #4786ad",
-"#Pl c #4787b6",
-"aNU c #4788b8",
-"aa5 c #4789b9",
-"#8B c #478ab9",
-"#xt c #478aba",
-"#Ns c #478bba",
-"#uV c #478bbb",
-"#Do c #478bbc",
-"#3V c #478cbb",
-".7r c #478cbc",
-"#tT c #478cbd",
-"#Dp c #478dbc",
-"#s0 c #478dbd",
-"#Cd c #478dbe",
-"bAs c #478ebd",
-"#x6 c #478ebe",
-"#E5 c #478ebf",
-"#FU c #478ec0",
-".tW c #47fefe",
-"bvN c #48283c",
-"aze c #482a3f",
-"aE1 c #48375f",
-"aKN c #483861",
-"#we c #483a46",
-"#C. c #483b47",
-"aEv c #48404d",
-".R3 c #48454d",
-"acL c #484677",
-".gx c #484848",
-"bSu c #484949",
-"bMf c #48494a",
-".o. c #484a4d",
-"bNG c #484f53",
-"bBv c #48505b",
-"aW. c #485186",
-".Xc c #485258",
-"##w c #485259",
-"#D2 c #48525a",
-"afb c #48525b",
-"#R9 c #48525c",
-"bML c #485285",
-"aOD c #485287",
-".Wk c #485358",
-".Wf c #485359",
-"#f4 c #48535a",
-"#Sp c #48535c",
-"#2n c #48535d",
-"aUn c #48545d",
-"#zB c #48545e",
-"aWe c #48565f",
-"bRD c #485761",
-"aN# c #485964",
-"boA c #485c6f",
-"#Yi c #485d69",
-"ao9 c #48606e",
-"aHg c #486287",
-"bez c #486783",
-"bw. c #486c90",
-"bBl c #4888b7",
-"#tP c #4888b8",
-"bMI c #4889b3",
-"bqY c #4889b9",
-"bg7 c #488ab7",
-"aJT c #488aba",
-"aJU c #488abb",
-"aXK c #488cba",
-"bzI c #488cbb",
-"#GO c #488cbc",
-"#CU c #488cbd",
-"#Uh c #488dbc",
-"#xu c #488dbd",
-"aHy c #488dbe",
-"bzJ c #488ebd",
-"#wn c #488ebe",
-"#uW c #488ebf",
-"#CV c #488ec0",
-"#Jr c #488ec1",
-"byQ c #488fbf",
-"#Dn c #488fc0",
-"#Cc c #488fc1",
-"bbP c #492439",
-"aRN c #49253a",
-"a9y c #49273c",
-".9N c #492a3f",
-"#wM c #492c3d",
-".zF c #492d32",
-"bQ6 c #493054",
-".zK c #493136",
-"abF c #493145",
-".bT c #49324c",
-".zL c #493439",
-".zN c #493539",
-"aK. c #493546",
-"aJd c #493645",
-"bpE c #493949",
-"Qtk c #493f3f",
-"bvP c #493f4a",
-"aSd c #49444c",
-"a9m c #494452",
-"bz# c #49454d",
-".oW c #494641",
-".0m c #49464e",
-"aoy c #494777",
-"a#q c #494878",
-".#n c #494949",
-"bMt c #494a4b",
-".zu c #494b4b",
-".Av c #494d50",
-"bNN c #494d52",
-"bTG c #495054",
-"bM6 c #495258",
-"#K. c #49525b",
-".O6 c #495359",
-".5J c #49535a",
-"#2m c #49535c",
-".Wg c #495459",
-".Yv c #49545a",
-"#b8 c #49545b",
-"#2q c #49545c",
-"#3H c #49545d",
-"bjc c #49545f",
-".N5 c #49555c",
-"bja c #49555e",
-"ap. c #49555f",
-"bAN c #495589",
-"#9M c #49565d",
-"#V2 c #49565f",
-"#uc c #495660",
-"aWU c #495760",
-"awV c #495761",
-"a3A c #49585f",
-"aXz c #495860",
-".3H c #495d67",
-"#KN c #495e73",
-"aNu c #496083",
-"#Kx c #496373",
-"a9z c #496473",
-"bu1 c #496580",
-"bCX c #496d90",
-"bzd c #496d91",
-"bhD c #497094",
-"asZ c #497c99",
-"bG2 c #497ead",
-"bME c #498bb6",
-"aMI c #498bbc",
-".8P c #498ebd",
-"a0h c #498fbf",
-"a14 c #498fc0",
-"bT0 c #4990c0",
-"aYB c #4990c1",
-"#NY c #4990c2",
-"#QG c #4990c3",
-"#OU c #4991c3",
-"baq c #4a233a",
-"aHv c #4a243a",
-"bod c #4a2a3d",
-"aAe c #4a2a3f",
-"aBo c #4a2b3f",
-"#zl c #4a2c41",
-"aLt c #4a3345",
-"asl c #4a3546",
-"aKL c #4a3760",
-".zM c #4a383c",
-".Q5 c #4a3d48",
-"av8 c #4a3e4a",
-"avx c #4a3f4a",
-"bjo c #4a4351",
-"aqs c #4a4474",
-"aOw c #4a454c",
-"axp c #4a464d",
-"arv c #4a4650",
-".e0 c #4a4a4a",
-"bL6 c #4a4a4b",
-"bL3 c #4a4b4c",
-"bK0 c #4a5185",
-"#Z2 c #4a535c",
-".AR c #4a5459",
-".Xd c #4a545a",
-"#4Q c #4a545e",
-".Wj c #4a555a",
-".5t c #4a555b",
-"#ii c #4a555c",
-"blr c #4a555e",
-"#j8 c #4a555f",
-"bjb c #4a5560",
-"ach c #4a565f",
-"bbZ c #4a5660",
-"bOt c #4a568a",
-"asU c #4a5760",
-"bqK c #4a5761",
-"#VY c #4a5862",
-"ayv c #4a5863",
-"ap9 c #4a5963",
-"bHL c #4a5a6a",
-"#G3 c #4a5b66",
-"bUp c #4a5c6d",
-"#7a c #4a697e",
-"bzG c #4a7097",
-"byG c #4a719d",
-".EO c #4a7695",
-"acO c #4a79a8",
-"bqZ c #4a84b3",
-"#uR c #4a85b4",
-"#Te c #4a86b5",
-"#6a c #4a89b8",
-"aIj c #4a89b9",
-"aLJ c #4a8dbd",
-"aIC c #4a8ebe",
-"aIi c #4a8ebf",
-"avZ c #4a90c2",
-".yl c #4a9187",
-"#2E c #4a91c2",
-".UI c #4a92c2",
-".qB c #4aa6b2",
-".vz c #4afffd",
-"aIt c #4b283d",
-"aAd c #4b2a3f",
-"#r7 c #4b3041",
-".z0 c #4b3235",
-"#hQ c #4b3243",
-"#fW c #4b3343",
-"aQt c #4b3445",
-"#Yz c #4b3860",
-".TQ c #4b3e4b",
-"auS c #4b4070",
-".zy c #4b4345",
-"aRk c #4b454c",
-".Bg c #4b4550",
-".SW c #4b464f",
-"Qtt c #4b4848",
-"bBT c #4b4a53",
-".bQ c #4b4b4b",
-"#MC c #4b515a",
-"aDu c #4b515b",
-"bQy c #4b5255",
-".AQ c #4b545a",
-".Wh c #4b555a",
-".AP c #4b555b",
-"#nv c #4b555c",
-"#2r c #4b555e",
-"bSO c #4b557b",
-".Wi c #4b565b",
-".08 c #4b565c",
-"#j0 c #4b565d",
-"#Qq c #4b565e",
-"bj3 c #4b565f",
-"bHG c #4b5660",
-"aoj c #4b5761",
-"#d5 c #4b5861",
-"#XL c #4b5963",
-"b.u c #4b5964",
-"asT c #4b5a64",
-"aqU c #4b6372",
-"bHK c #4b6987",
-"bF8 c #4b6a88",
-"a8f c #4b6b9a",
-"bOJ c #4b6e91",
-"bxc c #4b6f92",
-"bbp c #4b749e",
-"a0T c #4b7698",
-"ag9 c #4b7aa7",
-"bDb c #4b80af",
-"aIk c #4b82b0",
-"bCc c #4b82b1",
-"#Sd c #4b83b2",
-"bAt c #4b84b3",
-"#Rd c #4b85b4",
-"#Vx c #4b87a8",
-"#KG c #4b8ab7",
-"aKR c #4b8ebe",
-".XW c #4b91c1",
-"axf c #4b92c4",
-"ang c #4b94c6",
-"ank c #4b99ca",
-"aTt c #4c243b",
-".1R c #4c253b",
-"boc c #4c273c",
-"aIs c #4c283d",
-".6p c #4c2a3f",
-"a1f c #4c2b41",
-".zE c #4c3134",
-"aKZ c #4c3748",
-"bkj c #4c3949",
-"aN7 c #4c394c",
-"aA7 c #4c3b47",
-"ak9 c #4c3b4c",
-"amr c #4c424e",
-"ak2 c #4c424f",
-"aQk c #4c474d",
-".fF c #4c4c4c",
-".zs c #4c4d4d",
-".zw c #4c4d4e",
-"aFz c #4c4d5c",
-"bP# c #4c5356",
-"aAl c #4c545d",
-"bPZ c #4c547a",
-"by6 c #4c5488",
-"bRI c #4c555c",
-"azk c #4c555f",
-".AO c #4c565b",
-".Xe c #4c565c",
-"#eV c #4c565d",
-"#Xl c #4c565e",
-"#Rl c #4c565f",
-"azg c #4c5660",
-".2j c #4c575d",
-"#NG c #4c575e",
-"aWF c #4c5760",
-"azf c #4c5861",
-"asW c #4c5862",
-"a.Q c #4c5a64",
-"aaC c #4c5b65",
-"#dL c #4c6293",
-"bOx c #4c6394",
-"#iO c #4c6777",
-"bdP c #4c6a84",
-"bDT c #4c6a87",
-"bv9 c #4c6e90",
-"bz9 c #4c6f92",
-".ov c #4c7b8b",
-"btx c #4c80ae",
-"bE3 c #4c81af",
-"#sZ c #4c81b0",
-"#JY c #4c83ad",
-"#Ly c #4c8cb9",
-"#KK c #4c8dbb",
-"#43 c #4c8dbe",
-"#TD c #4c8eb7",
-"#Uf c #4c92c0",
-"#3O c #4c93c2",
-".sE c #4cfaff",
-".0F c #4d243a",
-"bx1 c #4d283d",
-"aIu c #4d283e",
-"aVs c #4d2940",
-"aDc c #4d2b40",
-"a0u c #4d2c3f",
-"aQD c #4d2c42",
-"aIP c #4d2e3f",
-"aSJ c #4d2e40",
-"#6A c #4d3046",
-"#oX c #4d3141",
-"#Zo c #4d3156",
-".zD c #4d3537",
-"aOd c #4d3547",
-"aLU c #4d3a4c",
-"aBM c #4d3b45",
-"bDG c #4d454d",
-"brY c #4d4575",
-"bbL c #4d4654",
-"aPw c #4d474e",
-".aN c #4d4d4d",
-"bN8 c #4d4d4e",
-"bBu c #4d4d59",
-"aVk c #4d4f84",
-"agM c #4d565b",
-"#Jc c #4d565f",
-".Ye c #4d575c",
-".Xn c #4d575d",
-".ZP c #4d575e",
-"#zF c #4d575f",
-"#4P c #4d5760",
-"bQ# c #4d5761",
-".6I c #4d585e",
-"#u# c #4d585f",
-"#3G c #4d5861",
-"bXx c #4d5862",
-"axB c #4d5863",
-"#A8 c #4d5879",
-"aVU c #4d5960",
-"bsi c #4d5962",
-"#BY c #4d5963",
-"#xA c #4d5a63",
-"atA c #4d5a64",
-"aZN c #4d5c66",
-"aM5 c #4d5e69",
-"bdn c #4d6573",
-".2v c #4d6a79",
-"a5G c #4d6c9a",
-"#LV c #4d7388",
-"#MJ c #4d7a91",
-".tH c #4d87d1",
-"#Mu c #4d8bb8",
-"#Ou c #4d8bb9",
-"#KL c #4d8dbc",
-"awu c #4d8eb4",
-"aP0 c #4d91bd",
-"#Ix c #4d95c6",
-".uu c #4dfffd",
-"bmW c #4e253b",
-"a8M c #4e283d",
-"aRC c #4e2a41",
-"aWW c #4e2b41",
-"aFZ c #4e2e42",
-"aN8 c #4e3143",
-"aUL c #4e365e",
-"a0a c #4e365f",
-"a1c c #4e3749",
-"asr c #4e3d49",
-"#r# c #4e3f4b",
-"ah# c #4e4271",
-"Qts c #4e4343",
-"abD c #4e4453",
-"anM c #4e4553",
-".ah c #4e4e4e",
-".zt c #4e4f4f",
-"bOd c #4e4f50",
-".zx c #4e5050",
-".UK c #4e535c",
-"bTB c #4e5456",
-"bNK c #4e5457",
-"bS0 c #4e565d",
-"aAY c #4e565f",
-".AN c #4e575c",
-".Xf c #4e585d",
-".0e c #4e585e",
-"#dR c #4e585f",
-"#08 c #4e5860",
-"asN c #4e5861",
-".Zm c #4e595e",
-"#va c #4e5960",
-"bkH c #4e5961",
-"#4q c #4e5962",
-"#ve c #4e5a61",
-"#sx c #4e5a63",
-"bb0 c #4e5a64",
-"amS c #4e5b63",
-"aR8 c #4e5b64",
-"#8d c #4e5b65",
-"b#T c #4e5c66",
-"bnW c #4e5d6b",
-"aNa c #4e5f6a",
-"aOo c #4e5f6b",
-"aA3 c #4e6597",
-"a6m c #4e6695",
-"amQ c #4e6878",
-"#HW c #4e6979",
-"btd c #4e6984",
-"#pE c #4e6a7a",
-"b#g c #4e6b7a",
-"bWl c #4e6d9a",
-"bAZ c #4e7193",
-"bxb c #4e7194",
-".C4 c #4e7791",
-"#wX c #4e7dab",
-".D7 c #4e7e9b",
-"#94 c #4e88b7",
-"avk c #4e8aa8",
-"#Os c #4e8ab8",
-".tG c #4e8ad7",
-"#Ot c #4e8bb8",
-"#Sh c #4e90be",
-"b#. c #4e91b9",
-"#7b c #4e91bb",
-"b.x c #4e92ba",
-"a7. c #4e93be",
-".0# c #4e94c2",
-"#Kf c #4e96c4",
-"#K3 c #4e96c5",
-"a7H c #4e9acc",
-".x1 c #4eb4ae",
-".xA c #4ecfca",
-".B5 c #4f152b",
-"#aV c #4f243a",
-"aU6 c #4f253c",
-"aB# c #4f273e",
-"aF. c #4f2a41",
-"a1W c #4f2b41",
-"awf c #4f2c42",
-"bFf c #4f2d43",
-"bmY c #4f2e44",
-"#wK c #4f2f41",
-"aPN c #4f2f46",
-"aEI c #4f3244",
-"aED c #4f3648",
-".z9 c #4f3839",
-".z1 c #4f3a3b",
-"aLV c #4f3a4c",
-"#8O c #4f3b5f",
-"aKj c #4f424f",
-"aau c #4f4949",
-"aCY c #4f4951",
-".5# c #4f4a51",
-".e2 c #4f4f4f",
-".zk c #4f5050",
-"bO# c #4f5051",
-"aaG c #4f5056",
-"aJH c #4f537f",
-".Yu c #4f595e",
-".8j c #4f595f",
-"#je c #4f5960",
-".Yf c #4f5a5f",
-"#rC c #4f5a61",
-"bnX c #4f5a63",
-"agY c #4f5b63",
-"#p8 c #4f5b64",
-"ad5 c #4f5c65",
-"aZa c #4f5d66",
-"aDj c #4f5d67",
-"aw3 c #4f5e68",
-"aSb c #4f6169",
-"bIP c #4f6174",
-"beZ c #4f6675",
-"#wJ c #4f6a79",
-"bqN c #4f6a85",
-"#nI c #4f6b7b",
-"bBX c #4f7294",
-"aIl c #4f79a4",
-"#vK c #4f7aa8",
-"bq0 c #4f7ba9",
-"#Sc c #4f82b1",
-"#Lx c #4f87b4",
-"a#v c #4f88b7",
-"#LC c #4f8ab7",
-"#Nr c #4f8bb8",
-"#Mv c #4f8bb9",
-"bXr c #4f8dac",
-".VO c #4f91bf",
-"#t0 c #4f92ba",
-".9C c #4f92c0",
-"anc c #4f93c2",
-"#SK c #4f96c4",
-"#Jp c #4f97c6",
-"#Iv c #4f98c6",
-"bai c #4f98c8",
-"#K4 c #4f99ca",
-"alU c #4f99cc",
-"#MK c #4f9ace",
-"a5v c #50233b",
-"aIr c #50273d",
-"a70 c #502940",
-"aHF c #502a41",
-"aW7 c #502b41",
-"#gT c #502c3f",
-".BS c #50333d",
-"#29 c #50345b",
-"aNE c #50355d",
-".zQ c #503a3a",
-"#cG c #50434f",
-"afM c #504473",
-".RV c #50474d",
-".OX c #504a51",
-".Rj c #504a52",
-"Qtz c #504c4c",
-"Qth c #505050",
-"bSt c #505051",
-"bTJ c #505052",
-"#LG c #50515d",
-".AS c #50595e",
-"#cV c #505960",
-".AM c #505a5f",
-".ZO c #505a60",
-"#kO c #505a61",
-"aVb c #505a63",
-".1V c #505b60",
-".7h c #505b61",
-"#XG c #505b64",
-"#3F c #505c64",
-"#me c #505c65",
-"#EC c #505c66",
-"#e2 c #505d66",
-"aLf c #505e67",
-"#UZ c #505e68",
-"a0P c #506067",
-"beX c #506877",
-"#An c #506b7a",
-".9w c #507080",
-".yt c #50715d",
-"byd c #507295",
-"bzc c #507395",
-"bCi c #5078a6",
-"bug c #507aa8",
-"#zo c #507ba9",
-"#Rc c #5084b3",
-"bAE c #508aa7",
-"#LB c #508bb9",
-"#Mw c #508cb8",
-".s3 c #508ddc",
-"bRg c #5093bb",
-"bSG c #5095bc",
-"a9Z c #5096be",
-"#2D c #5099c7",
-"#RF c #5099c9",
-"awA c #5099ca",
-".v8 c #50fffc",
-".B6 c #51192e",
-"blI c #51243b",
-"aSH c #51243c",
-"bob c #51253c",
-"bDl c #51273d",
-"aIv c #51283f",
-"bT5 c #512d51",
-"aRU c #512f46",
-"bm7 c #513043",
-"bjD c #513247",
-"#gK c #513345",
-"aPR c #51335a",
-"#yA c #513546",
-"b.G c #513547",
-"aKF c #51355d",
-".z2 c #513b3c",
-"#Il c #514451",
-"#Ip c #514552",
-"Qty c #514646",
-".cZ c #515151",
-".zn c #515152",
-"aSi c #515187",
-"bN1 c #515252",
-"bUa c #515386",
-"bnw c #51565f",
-"bKz c #51575b",
-"#ZQ c #515a63",
-".29 c #515b60",
-".2i c #515b61",
-"#f5 c #515b62",
-".Zn c #515c61",
-".CH c #515c62",
-"#hm c #515c66",
-"#it c #515d66",
-"alx c #515d67",
-"#3j c #515e67",
-"#DD c #515e68",
-"bcK c #515f68",
-"aAg c #515f69",
-"#Ps c #516069",
-"ar6 c #51606a",
-"aIm c #516b92",
-"bz8 c #517395",
-"bgP c #517696",
-"#tN c #5177a4",
-"#yE c #5177a5",
-"bzF c #517daa",
-"bzB c #517eac",
-"#41 c #5184b2",
-"atE c #5186a1",
-"#Nq c #518bb8",
-"bc7 c #5194bc",
-"bKY c #5197c2",
-".WW c #5197c5",
-"#HC c #519ac9",
-".#d c #522152",
-".4c c #52293f",
-".aC c #522b51",
-"aOK c #522c42",
-".bU c #522c51",
-"#4d c #52304d",
-"aOR c #52335a",
-"bgG c #52354a",
-"aKM c #52355c",
-"aKn c #523748",
-"bkl c #523749",
-"bkk c #52394b",
-"aMA c #523a61",
-"bDg c #523b4e",
-".Bh c #52404b",
-"#i5 c #524350",
-"amD c #524956",
-"awK c #524a52",
-"anz c #524b55",
-"bHC c #524e57",
-".dE c #525252",
-"bTV c #525253",
-"bQK c #525557",
-"bQv c #525759",
-"bR6 c #52575a",
-"bSh c #52595c",
-"#ZP c #525962",
-"bRH c #525b62",
-".Xg c #525c61",
-".CF c #525c62",
-"#st c #525c63",
-"#Wl c #525c64",
-"#hl c #525c65",
-"aAj c #525c66",
-".CG c #525d63",
-"aSL c #525d64",
-"#yk c #525e67",
-"#yZ c #525e68",
-"bWo c #525f68",
-"aZ5 c #526069",
-"agp c #52606a",
-"#YL c #52616a",
-"a9a c #52616b",
-"#7S c #526470",
-"byH c #526792",
-"bg4 c #526b80",
-"bcG c #526c7b",
-"ap8 c #526c7c",
-"#IL c #526f80",
-"bBW c #527496",
-"bGX c #5278a6",
-"a38 c #527ba3",
-".EK c #5282a0",
-"a3x c #5283a8",
-".ue c #5291e1",
-"#Za c #5294b8",
-"aMK c #5296be",
-"#SL c #5297bd",
-"#u5 c #5297bf",
-"#Kg c #529ccb",
-"a1O c #529ccd",
-"anf c #529dce",
-"bJA c #52a5d5",
-".u0 c #52fffc",
-"a5l c #53263e",
-"bAA c #53283e",
-"aB4 c #532a41",
-"a5b c #532a42",
-"aSF c #532e46",
-"aWw c #533144",
-"aQH c #53345b",
-"bkm c #533548",
-"al. c #533c4e",
-"bJt c #533c6a",
-".A. c #533d3d",
-"aeu c #533f6d",
-"bfz c #53434e",
-"aDQ c #534c53",
-"aqF c #534d56",
-".iF c #534f69",
-"aYe c #535083",
-"aRq c #535085",
-"aSY c #535086",
-".aM c #535353",
-"aEd c #535455",
-".Be c #53565d",
-"bJf c #53575b",
-"bO8 c #53585c",
-"bJg c #53585d",
-"aIn c #53597e",
-"bNC c #535a5c",
-"#Uw c #535c64",
-"avd c #535c65",
-"arN c #535c66",
-".Xh c #535d62",
-".ZN c #535d63",
-"#tf c #535d64",
-"#8q c #535d65",
-"#51 c #535d66",
-".CE c #535e64",
-"#Tq c #535e66",
-"#ZH c #535f67",
-"#jm c #535f68",
-"#nE c #536069",
-"#Ln c #53606a",
-"#73 c #53616a",
-"#9q c #53616b",
-"amR c #53626c",
-"a7D c #53636c",
-"aAr c #536493",
-"aJw c #536893",
-"a4S c #536e9a",
-"bfm c #536f87",
-"#zO c #53707f",
-"aGf c #5376a3",
-"#zY c #5378a5",
-".qj c #5383bc",
-"#MA c #5384ad",
-"a6E c #5391af",
-".uJ c #5392e3",
-"aIg c #5393c0",
-".sm c #5393e4",
-"#UD c #5396bc",
-"#Po c #5397c3",
-".1t c #5397c4",
-"#TE c #5398be",
-"ax. c #539bc5",
-"a6D c #539dcd",
-".tk c #53fdfe",
-".Fr c #541831",
-"aSz c #54253c",
-"aSA c #54253d",
-"aIq c #542840",
-"aIJ c #542a41",
-"aJm c #542a42",
-"bQ7 c #542c4f",
-"bBr c #542d4f",
-"#7M c #542e46",
-"a9p c #543043",
-"aBk c #543052",
-"bwG c #543257",
-"aBf c #54345d",
-"aEE c #543648",
-".ed c #54374e",
-"aIp c #543854",
-"br7 c #543a4c",
-"aLW c #543a4d",
-"ak1 c #544552",
-"aIo c #544767",
-"QtI c #544848",
-".Jj c #54484e",
-"bij c #544857",
-"ah8 c #544b54",
-"aNO c #544d75",
-".jH c #544e50",
-"QtJ c #545050",
-"aFp c #545057",
-"ac0 c #545180",
-"#5U c #54525b",
-".y7 c #545453",
-".jm c #545454",
-".Bf c #54545c",
-"bMb c #545555",
-"akA c #545987",
-"bNA c #545b5e",
-"bQc c #545c64",
-"#ij c #545d64",
-".AT c #545e62",
-".3u c #545e64",
-"#eW c #545e65",
-"apb c #545e67",
-".1W c #545f63",
-".9Z c #545f64",
-".CI c #545f65",
-"#pA c #546069",
-"ar5 c #54626b",
-"avL c #54626c",
-"a68 c #546279",
-"aHf c #54688f",
-"atn c #546f7f",
-"#yx c #54707f",
-"bsG c #54719e",
-"#tO c #54729f",
-"#9Z c #547caa",
-"#7h c #5481b0",
-"#Mt c #5489b5",
-".ud c #5495e8",
-".ub c #5495e9",
-".tE c #5496e9",
-"#Z8 c #5498be",
-"bPP c #549ac2",
-"#58 c #549bc5",
-"avn c #549ece",
-"auR c #54a0d0",
-"Qt6 c #551f55",
-"aSr c #552a41",
-"aZE c #552e41",
-"bjt c #552e45",
-"aEH c #553345",
-"aLD c #55335a",
-"#23 c #55335b",
-"bx2 c #553c4c",
-"apw c #553d6b",
-"bBt c #554756",
-"bfM c #554857",
-"Qt1 c #554949",
-".Ng c #554c53",
-"aCl c #554d54",
-"aUB c #554d81",
-"bRu c #555059",
-"#H7 c #55525d",
-".fG c #555555",
-"bMq c #555657",
-"bO7 c #555a5d",
-"bJc c #555b5e",
-"#lL c #555e65",
-"adM c #555e67",
-".Xi c #555f64",
-"#.I c #555f65",
-"#TA c #555f66",
-"aGz c #555f67",
-"#da c #555f68",
-".QJ c #556067",
-"bOK c #55606a",
-"bqJ c #55616a",
-"bv4 c #55636d",
-"aNb c #556773",
-"aNv c #556f9b",
-"agm c #557080",
-"bBm c #55709d",
-"avH c #557181",
-"#Bt c #557282",
-"bDS c #55728e",
-"bVr c #55728f",
-"a75 c #557383",
-"aHx c #5573a0",
-"au7 c #557484",
-"aWT c #557694",
-".C5 c #557990",
-"#NI c #558eab",
-"a9k c #558fac",
-"#T# c #5591c0",
-".s0 c #5595e9",
-".ua c #5595ea",
-".tD c #5596e9",
-".uc c #5596ea",
-".s1 c #5596eb",
-"bU. c #559cc4",
-"bOp c #559cc8",
-"#HD c #55a0cf",
-".wC c #55fefa",
-".tX c #55fefd",
-".DF c #56162f",
-"aQX c #56243d",
-"bU2 c #562b4f",
-"aXN c #562e42",
-"aVJ c #562f42",
-"#4h c #56335a",
-"aEF c #563446",
-"#zT c #563649",
-"as8 c #56364a",
-"bs# c #563a4d",
-"aux c #56414e",
-".ji c #564446",
-".1D c #564451",
-"bs0 c #564b53",
-"bSJ c #565284",
-".bj c #565656",
-"bPl c #565b5f",
-"bLI c #565c5f",
-"bOO c #565e65",
-"#If c #565e67",
-".AL c #565f64",
-".5u c #565f65",
-"#dS c #565f66",
-".Xj c #566064",
-".Xm c #566065",
-".ZM c #566066",
-"#9G c #566068",
-"#S6 c #566069",
-"#VO c #566168",
-"#iv c #56616b",
-"#p5 c #56626b",
-"a7E c #566369",
-"aFi c #56646e",
-"#ZE c #56656f",
-"aDx c #566f9a",
-"#sY c #566f9b",
-"bXu c #56709b",
-".UC c #567788",
-"bCW c #567798",
-"a4V c #567ea4",
-".sr c #5683b6",
-"#LA c #568ab6",
-"bBC c #5693b3",
-".tC c #5696e9",
-".sZ c #5697e9",
-"#2y c #5698bb",
-".tF c #5698ec",
-"#vP c #569dc6",
-"afn c #569fc1",
-"#Jq c #56a1cf",
-"and c #56a2d3",
-".6n c #57243c",
-"bmm c #57253d",
-"a3L c #57253e",
-"a0q c #57263e",
-"aIw c #572740",
-"#o8 c #572941",
-"aCD c #572942",
-"ayo c #572a42",
-"bJv c #572c4f",
-"a#7 c #572d46",
-"aN2 c #572e46",
-"aXG c #573259",
-"ble c #573347",
-"aEG c #573446",
-"bzw c #573862",
-".A# c #574041",
-"aCG c #57414d",
-"#Hu c #574653",
-".#q c #574b4b",
-"aGD c #574d53",
-".Qj c #574e56",
-"Qt2 c #575353",
-".e1 c #575757",
-"bKw c #575b5f",
-"bR7 c #575c5f",
-"bTC c #575d60",
-"#0S c #575f67",
-".Xl c #576165",
-".Xk c #576166",
-"#oo c #576167",
-"#65 c #57626b",
-"#d6 c #57636d",
-"a1R c #57646b",
-"#Bj c #57666f",
-"aqW c #576670",
-"a.N c #576b79",
-"brW c #576d99",
-"bpj c #57718b",
-"bh. c #577998",
-"#8C c #577fad",
-"#OM c #5789b3",
-"bCy c #5796b6",
-".uK c #5797e5",
-".sk c #5798e8",
-".s2 c #5799ef",
-"bPN c #579ec6",
-"b#I c #579fc6",
-"axe c #57a2d1",
-"#3N c #57a3d1",
-"ane c #57a4d5",
-".w7 c #57f4f0",
-".r0 c #57f6fe",
-".vA c #57fffb",
-"a5j c #58243d",
-"a6K c #58263f",
-"aBn c #582840",
-"a3B c #582a42",
-"aON c #582b44",
-"bSz c #582b4f",
-"aT7 c #582d46",
-"aCz c #58335a",
-"#zS c #583648",
-"aGr c #583a4e",
-"aup c #583b67",
-"acV c #583b68",
-"bBs c #583d53",
-"a9n c #584151",
-"#g2 c #584552",
-"bcx c #584a58",
-"bpX c #584e54",
-"#dn c #585058",
-"afp c #58565f",
-".cm c #585858",
-"bSs c #585959",
-"bKC c #58595a",
-"#Gr c #585961",
-"bmU c #585a63",
-"bNM c #585c5f",
-"axG c #586069",
-".75 c #586167",
-"#hd c #586168",
-"ayz c #58616a",
-".Yt c #586267",
-".CD c #586268",
-"#ZT c #586269",
-"acb c #58626a",
-"#3x c #586369",
-"#9F c #58656e",
-"a3W c #58656f",
-"btb c #58666f",
-"aXh c #586670",
-"a1H c #58676f",
-"bzA c #586995",
-"#x5 c #586b97",
-"axy c #587786",
-"bzb c #58799a",
-"bci c #5893c2",
-".uf c #5897e3",
-".sY c #5898e8",
-".tB c #5899e8",
-"#.q c #589ac5",
-".u# c #589ae9",
-".sl c #589aef",
-".2E c #589bc7",
-"#u0 c #58a1c9",
-"a9j c #58a1cb",
-"#Iw c #58a4d3",
-".ym c #58a598",
-".sF c #58fafe",
-".0s c #59243d",
-".#J c #592459",
-"byX c #59273f",
-"a0Z c #592943",
-".an c #592a5b",
-"bT3 c #592b4f",
-"boa c #592d44",
-"#4i c #592d51",
-"aJQ c #592e55",
-"aRH c #593259",
-"aMY c #59364a",
-"aJc c #59374a",
-"#sf c #593b5b",
-"bMy c #593e6a",
-"#Dg c #594350",
-".X4 c #594552",
-"#r3 c #594652",
-"#mq c #594653",
-"amk c #594754",
-"aEw c #594855",
-"aoZ c #594a58",
-"aKi c #594e55",
-"bei c #595385",
-".bi c #595959",
-"bMr c #595a5b",
-"bQH c #595d60",
-"bTu c #595d61",
-"bR8 c #595e61",
-".Eu c #595f63",
-"bM5 c #596167",
-".Qq c #596268",
-"#mI c #596269",
-"aaB c #59626b",
-".1X c #596367",
-".Yg c #596368",
-"#hi c #59636b",
-"#Fk c #59646d",
-"apk c #59656c",
-"aX8 c #59656e",
-"bM1 c #59656f",
-"adF c #59666e",
-"#T6 c #596771",
-"bwA c #596a95",
-"aOp c #596c77",
-"bfr c #596c8f",
-".AB c #597183",
-".YR c #597989",
-"baG c #597fa3",
-"bWi c #5993b2",
-"bXk c #5999bf",
-".vh c #5999e7",
-".vg c #599ae7",
-"alV c #59a6d5",
-"#Vb c #59a9d6",
-".uv c #59fffc",
-".83 c #5a2942",
-"bIp c #5a2b4e",
-"bQ9 c #5a2b4f",
-"bqk c #5a2f51",
-"aDN c #5a3146",
-"aBT c #5a3158",
-"aTd c #5a3259",
-"aPF c #5a354a",
-"aIL c #5a394e",
-"bCp c #5a3e52",
-"al# c #5a3f51",
-"aJI c #5a3f68",
-"atd c #5a424f",
-"#E1 c #5a4754",
-"aml c #5a4755",
-"aMk c #5a4a75",
-".#Y c #5a4f4f",
-"bgz c #5a5056",
-"bK4 c #5a5283",
-"bMT c #5a587c",
-".dD c #5a5a5a",
-"bLL c #5a6063",
-".0L c #5a6368",
-".ZL c #5a6369",
-"#jf c #5a636a",
-".Ys c #5a6468",
-".6J c #5a6469",
-"#pf c #5a646a",
-"#Ss c #5a646b",
-"#Xa c #5a646c",
-"aAW c #5a646e",
-"#Fl c #5a656f",
-"bz0 c #5a6592",
-"#dX c #5a666f",
-"a5S c #5a6871",
-"aM4 c #5a6d78",
-"aNc c #5a6d79",
-"beY c #5a7685",
-"aZ# c #5a7790",
-"b#P c #5a7a8a",
-".vf c #5a9ae7",
-".sX c #5a9ae8",
-".ve c #5a9be7",
-"#4X c #5aa4ce",
-"awv c #5aa5d0",
-"#4Z c #5aa8d6",
-"a3P c #5b263f",
-"#nm c #5b2841",
-"#cF c #5b2942",
-"aQ2 c #5b2943",
-"axw c #5b2a43",
-"aER c #5b2b43",
-"aD. c #5b2d52",
-"bm5 c #5b2f44",
-"aNB c #5b3158",
-"aaZ c #5b3863",
-".dH c #5b3c63",
-"awM c #5b4350",
-".PW c #5b4754",
-"bht c #5b4b5a",
-"a.y c #5b5a7e",
-".bL c #5b5b5b",
-"bKF c #5b5c5d",
-"bKs c #5b6163",
-"#f6 c #5b646a",
-"#Dc c #5b646b",
-".3. c #5b6569",
-".Yr c #5b656a",
-"bj0 c #5b656d",
-"#tM c #5b6590",
-".9u c #5b666b",
-".4E c #5b666c",
-"aL9 c #5b666f",
-"a9C c #5b676f",
-"bz5 c #5b6770",
-"aOv c #5b686f",
-"#T9 c #5b6870",
-"baw c #5b6973",
-"b#3 c #5b6995",
-"apl c #5b6a73",
-"#7Z c #5b6a74",
-"awj c #5b7585",
-".0b c #5b7886",
-"arG c #5b7889",
-"bzT c #5b96b6",
-".sj c #5b9ae8",
-".vd c #5b9ce7",
-".tA c #5b9ce8",
-"bU5 c #5ba3cc",
-"#Tc c #5ba7d5",
-".ri c #5be4ef",
-".v9 c #5bfffa",
-"boP c #5c253e",
-"#gO c #5c3448",
-".Fi c #5c434b",
-"#A3 c #5c4350",
-"bbF c #5c4451",
-"#ab c #5c4654",
-".Hi c #5c4c50",
-"bqz c #5c4e56",
-".S0 c #5c5056",
-"bhr c #5c5057",
-"#b# c #5c5182",
-"aed c #5c5257",
-"ayP c #5c5258",
-".pU c #5c5434",
-".Bc c #5c565c",
-".#r c #5c5757",
-".nJ c #5c5c5c",
-"bN4 c #5c5d5d",
-"ag8 c #5c5f85",
-"bNJ c #5c6164",
-"bPf c #5c6165",
-".q4 c #5c6166",
-"bPh c #5c6264",
-".AK c #5c6469",
-".Zo c #5c656a",
-".5v c #5c656b",
-"#PE c #5c656d",
-"aqY c #5c656e",
-"#wm c #5c6590",
-".Yh c #5c666b",
-"#.J c #5c666c",
-"bVl c #5c676f",
-"#nR c #5c6770",
-".Ej c #5c686f",
-"#Gd c #5c6870",
-"#Fj c #5c6871",
-"bLl c #5c6872",
-".Ek c #5c6970",
-"aUl c #5c6972",
-"#Mj c #5c6973",
-"alv c #5c6a72",
-"bov c #5c6a73",
-"bf7 c #5c6a74",
-"a1C c #5c6b72",
-"bfg c #5c6b74",
-"asF c #5c6b75",
-"bfi c #5c7282",
-"a86 c #5c729c",
-"bJB c #5c72a2",
-"a6M c #5c7583",
-"abS c #5c7584",
-"bbS c #5c7989",
-"#R4 c #5c7c8e",
-".nM c #5c8a9e",
-".vi c #5c9ce6",
-"bKV c #5ca6ce",
-"#2C c #5ca9d6",
-"boV c #5d263f",
-"bCu c #5d2741",
-"aWf c #5d2943",
-"#wc c #5d2f44",
-"aQK c #5d3056",
-"bKN c #5d3762",
-"ask c #5d384c",
-".ha c #5d4350",
-"#hZ c #5d4855",
-"amj c #5d4956",
-"aHV c #5d4f53",
-"a#l c #5d4f56",
-"aQp c #5d4f83",
-".MF c #5d5056",
-".ak c #5d5252",
-".#Q c #5d5d5d",
-"bTU c #5d5d5e",
-"bPo c #5d5e5e",
-"bDc c #5d608b",
-"bTw c #5d6265",
-"bwB c #5d628d",
-"bSe c #5d6365",
-"bFP c #5d6593",
-".1Y c #5d666b",
-"#cW c #5d666c",
-"ajy c #5d666e",
-".Yq c #5d676b",
-".2h c #5d676d",
-"am3 c #5d6770",
-"als c #5d6871",
-".El c #5d6970",
-".En c #5d6a71",
-"#Aw c #5d6b97",
-"#0G c #5d6c76",
-"a6x c #5d6d76",
-"aa6 c #5d76a2",
-"bF7 c #5d7893",
-".yd c #5d795b",
-"av. c #5d7a8b",
-"#pq c #5d7b8c",
-".rF c #5d9eed",
-".uI c #5d9fe8",
-"#Pk c #5da3d0",
-"aKU c #5db2db",
-"a5p c #5e2640",
-"aBR c #5e2641",
-"a7e c #5e2943",
-"#ro c #5e2b4e",
-"bRa c #5e2b4f",
-"ado c #5e2c46",
-"bla c #5e2e45",
-"aKw c #5e2e54",
-"aNH c #5e3056",
-"aBZ c #5e3158",
-"aXW c #5e354a",
-"#nl c #5e3649",
-"a7c c #5e364c",
-"asc c #5e3863",
-"bxD c #5e395d",
-"aet c #5e3964",
-".Dz c #5e434a",
-"awc c #5e4451",
-"#5. c #5e4565",
-"bDF c #5e4653",
-"aNl c #5e4c79",
-".iX c #5e5553",
-"#Hd c #5e5d65",
-"QtT c #5e5e5e",
-"bTX c #5e5f60",
-"bPb c #5e6466",
-"bMR c #5e6693",
-".Yp c #5e676c",
-".76 c #5e676d",
-"a#j c #5e676f",
-".Yi c #5e686c",
-"##x c #5e686d",
-"#2p c #5e686e",
-".CC c #5e696e",
-".Eo c #5e6970",
-"#gy c #5e6971",
-"apj c #5e6972",
-".Em c #5e6a71",
-"#5T c #5e6a72",
-"a64 c #5e6b72",
-"a5R c #5e6b74",
-"#Q9 c #5e6c74",
-"alu c #5e6c76",
-"axD c #5e6d77",
-".xq c #5e6e45",
-"a33 c #5e6f99",
-"aOq c #5e717d",
-"bkB c #5e737f",
-".RK c #5e7c8c",
-"aVn c #5e7d95",
-"bXw c #5e7e9c",
-"awh c #5e7f8f",
-".s6 c #5e96d2",
-"#LS c #5e99b7",
-"#Np c #5e9dca",
-".si c #5e9de7",
-".sW c #5e9ee7",
-".tz c #5e9fe8",
-".vc c #5ea0e7",
-".u. c #5ea0e8",
-"bXj c #5ea8d0",
-"atF c #5ea8d3",
-"aul c #5eaddd",
-".u1 c #5efffa",
-".#u c #5f225f",
-".9M c #5f233e",
-"ba7 c #5f243f",
-"#no c #5f2842",
-"aMN c #5f2843",
-"aWr c #5f2943",
-"bPK c #5f2a4d",
-"aE9 c #5f2a4e",
-"aUS c #5f2b46",
-"a.G c #5f2c46",
-"#6l c #5f385d",
-"aG0 c #5f4957",
-"#vC c #5f4a57",
-".E2 c #5f4d50",
-"byI c #5f527b",
-"aS6 c #5f545c",
-"apQ c #5f555d",
-".bn c #5f5754",
-".bW c #5f5954",
-".cx c #5f5a54",
-"a.n c #5f5c7f",
-"bAp c #5f5e89",
-".eX c #5f5f5f",
-"bN6 c #5f6061",
-"bV2 c #5f6366",
-"acP c #5f638d",
-".Yo c #5f686d",
-"#bg c #5f686e",
-"#Hl c #5f686f",
-".Yn c #5f696d",
-"#Le c #5f696e",
-"#rD c #5f696f",
-"aPk c #5f6971",
-".Ei c #5f6b72",
-"aMb c #5f6b74",
-"aL7 c #5f6c75",
-"afh c #5f6d76",
-"aCH c #5f6e77",
-"aBJ c #5f719f",
-"aSR c #5f7380",
-"aRi c #5f7480",
-"bJ0 c #5f7489",
-".RM c #5f7581",
-"aUv c #5f7683",
-"a0N c #5f7b93",
-"aZ1 c #5f7b94",
-"#P1 c #5f7d8f",
-"#xX c #5f7f90",
-"bas c #5f8090",
-"#hO c #5f8092",
-"a5L c #5f84a7",
-".ys c #5f8c75",
-"#Pn c #5f9bc4",
-".WU c #5f9bc5",
-".rE c #5f9de8",
-".3T c #5fa1cb",
-"a90 c #5fa3c4",
-"bW# c #5fa6cd",
-"#vR c #5fabd4",
-"anl c #5fb5e3",
-"#Vc c #5fb7e5",
-".tl c #5ffdfd",
-"a6. c #60243f",
-".ac c #60265e",
-"#zW c #602740",
-"a6J c #602843",
-"bXg c #602c50",
-"aZi c #602f54",
-"aNF c #602f55",
-"b.D c #60364b",
-"#lC c #60374a",
-"bpF c #60384d",
-".H# c #604751",
-"aGe c #604770",
-"#Io c #604a57",
-".gA c #604e71",
-"aTL c #604e82",
-"blD c #605359",
-".aP c #605554",
-".id c #605855",
-".#Z c #605c5c",
-"bwy c #605c86",
-"bG3 c #605f89",
-".ag c #606060",
-"bPz c #606161",
-"bJa c #606568",
-".Yj c #60696e",
-"#eX c #60696f",
-"#0T c #606972",
-".Ym c #606a6e",
-".07 c #606a6f",
-"#2d c #606a72",
-"b#E c #606b75",
-"a3r c #606c74",
-"#C5 c #606d75",
-"aOh c #606d76",
-"bpd c #606f77",
-"ah4 c #606f78",
-"#1W c #606f79",
-"aw2 c #607079",
-"aRh c #607480",
-"aS# c #607481",
-"aOk c #607581",
-"alq c #607e90",
-".Az c #608090",
-"#tx c #608192",
-".TH c #6093b6",
-".SM c #609bc0",
-".uL c #609ee3",
-"bDq c #60a8ca",
-"#t1 c #60acd5",
-"#LU c #60acd6",
-"bIt c #60add6",
-".r1 c #60f6ff",
-".tY c #60fefc",
-".wD c #60fff9",
-"bkd c #61243f",
-"aDT c #612541",
-"a1e c #612641",
-"aIx c #612642",
-"bBy c #612741",
-"aT# c #612843",
-"aBl c #612a47",
-"bXe c #612a4d",
-"aGl c #612b4f",
-"aYs c #612e53",
-"aQ5 c #613a50",
-"a19 c #613a51",
-"aEY c #61555c",
-".hu c #615a56",
-"bC9 c #615a84",
-"bCb c #615b85",
-"bdD c #615f8c",
-".aJ c #616161",
-"bNZ c #616162",
-"bKH c #616263",
-"bNO c #616568",
-"bKo c #616669",
-"bSd c #616769",
-"bIz c #616793",
-".AU c #616a6f",
-".6K c #616a70",
-"ajr c #616a71",
-"act c #616a72",
-".0M c #616b70",
-"#ER c #616b71",
-"#QX c #616b72",
-"aq# c #616b73",
-"aWE c #616c75",
-"buX c #616d76",
-"#S5 c #617079",
-"av# c #61707a",
-"ati c #61717a",
-"aOn c #617480",
-"aQi c #617582",
-"aM2 c #617682",
-"a1x c #617d94",
-"#Lk c #618294",
-"#Ms c #6199c3",
-".vj c #619fe3",
-".sh c #61a0e7",
-".ty c #61a2e8",
-"axd c #61b1de",
-"#OL c #61b6e7",
-".#c c #621a62",
-"bjp c #62243f",
-"aGc c #622440",
-"aU7 c #622641",
-"bHi c #622a4d",
-"#nj c #622f45",
-"aNG c #622f55",
-"#kF c #623045",
-"aJJ c #623158",
-"apx c #623660",
-"#tH c #62374b",
-"aFa c #623a50",
-"aMU c #623b51",
-"##3 c #624654",
-"baX c #624856",
-"aru c #624b58",
-"ayQ c #62555b",
-"aoO c #62575e",
-"bFZ c #62575f",
-"bIE c #625961",
-".gN c #625a56",
-"bV# c #625a7e",
-"bq1 c #625a83",
-".d. c #625c57",
-".hc c #626262",
-"bJk c #626263",
-"bM# c #626363",
-"bM. c #626364",
-".Cn c #626669",
-"bSg c #62676a",
-".AJ c #626a6f",
-"#07 c #626a71",
-".Yk c #626b70",
-".ZK c #626b71",
-"adW c #626b73",
-"arU c #626b74",
-"bj2 c #626c74",
-"bAU c #626c75",
-"#rJ c #626d76",
-"#tm c #626e75",
-"#Fi c #626e76",
-"auh c #62717a",
-"am6 c #62717b",
-"aM3 c #627682",
-"bfc c #627683",
-"bjQ c #627783",
-"ays c #627987",
-"a2x c #627c92",
-"bv8 c #627d98",
-".SE c #628394",
-".sV c #62a2e7",
-".t9 c #62a6e8",
-"#3K c #62add3",
-".vB c #62fff9",
-"##g c #632440",
-"a5t c #632641",
-"aBm c #632742",
-"#a. c #632843",
-"aZ9 c #632844",
-"bMC c #63294d",
-"aP. c #632a46",
-"aYG c #633146",
-"#uv c #633247",
-"aqt c #63355f",
-"#jU c #63364b",
-"a95 c #63364c",
-".fC c #634158",
-"aqL c #634653",
-"bde c #634754",
-"amm c #634b59",
-".X9 c #63565d",
-"bAT c #63575d",
-"#xs c #635882",
-".ev c #635c58",
-".dP c #635d58",
-"#Ny c #636072",
-".#U c #636363",
-".jp c #636382",
-"bPy c #636464",
-"bKG c #636465",
-"bJ. c #63686a",
-"afa c #636a72",
-".Yl c #636c70",
-".90 c #636c71",
-"#7. c #636c74",
-".Zp c #636d71",
-"#YF c #636d74",
-"aq2 c #636d75",
-"aqj c #636e76",
-"#T2 c #636e77",
-"aqi c #636f76",
-"asL c #636f77",
-"bce c #637078",
-"#rI c #637079",
-"#6H c #63727b",
-"ai7 c #63727c",
-"#8X c #63749b",
-"aOr c #637682",
-"boz c #637689",
-"aPt c #637783",
-"aUu c #637884",
-"bIO c #637c97",
-"a3k c #637d92",
-".tI c #63a0e0",
-".rD c #63a0e7",
-".uH c #63a6e7",
-"#Y8 c #63a7c9",
-"#Lw c #63a8d3",
-"#Ta c #63abd7",
-"avW c #63afd5",
-"bOq c #63b0da",
-"#LT c #63b1de",
-"alW c #63b3e0",
-".sG c #63fafd",
-".0t c #642440",
-"aUd c #642541",
-"aES c #642542",
-"aB3 c #642744",
-"#mC c #642843",
-"aWX c #642844",
-"bFr c #64294a",
-"bFe c #642c46",
-"aYI c #64364c",
-"atN c #64384d",
-"bkb c #644b5a",
-"ami c #644c5a",
-"#sX c #64557e",
-"#uQ c #64557f",
-"bv1 c #64565c",
-"bCj c #64567f",
-"azM c #64575d",
-".OP c #64575e",
-".fd c #645d59",
-".ks c #646265",
-".#k c #646464",
-".um c #64651b",
-"bJm c #646566",
-"bMn c #646666",
-"bLV c #64686b",
-"bLY c #64686c",
-"#Gx c #646c73",
-"ajx c #646c74",
-"#cX c #646d72",
-"#FI c #646d73",
-"brv c #646d75",
-"arY c #646d76",
-"#1Q c #646e76",
-"be8 c #646e92",
-"#gx c #646f77",
-"aL6 c #646f78",
-"bt# c #647078",
-"bpc c #64727b",
-"axE c #64727c",
-"a8m c #64747d",
-".Ax c #64757d",
-"aPs c #647884",
-"bet c #647987",
-"#95 c #6479a6",
-"bp5 c #647c93",
-"bHJ c #647d97",
-"#G5 c #648496",
-"at7 c #648596",
-"b#6 c #6487a7",
-"#Lz c #6488b1",
-".rM c #648ab0",
-".tx c #64a7e8",
-"auP c #64abcf",
-".yn c #64b2a2",
-"bT9 c #64b2db",
-".pY c #64c5d6",
-".x2 c #64d8ce",
-".xB c #64f1ea",
-".w8 c #64fff9",
-".uw c #64fffb",
-"Qt7 c #651965",
-"a5k c #652440",
-"a5u c #652542",
-"bBq c #652548",
-"aBa c #652642",
-"byW c #652741",
-"aGn c #652744",
-"bKQ c #65294c",
-"bkq c #652a45",
-"#9g c #652b47",
-"aNA c #652e53",
-"#qv c #653046",
-"bjC c #65324a",
-"aIU c #65374c",
-"aL3 c #65374d",
-"bpI c #65384e",
-"bs. c #653a50",
-"#B7 c #654755",
-"aCi c #654956",
-".J. c #654b56",
-"#h7 c #654b58",
-"#a1 c #654c59",
-"bMO c #654f7f",
-"bvp c #65547d",
-"aEo c #65565c",
-"agZ c #655a63",
-".al c #656060",
-"#Fx c #65646c",
-".eh c #656565",
-"bQU c #656566",
-"bSr c #656666",
-"#Vr c #65666c",
-"bTH c #656a6c",
-".B. c #656d72",
-"#ik c #656d73",
-".Zq c #656e73",
-"#hj c #656e75",
-"#YV c #656e76",
-".3# c #656f73",
-"#Bd c #656f76",
-".5I c #657075",
-"#ub c #657179",
-"bx# c #65717a",
-"#yi c #65727b",
-".Gc c #65737a",
-"aXy c #65747c",
-"avR c #65747d",
-"arI c #65747e",
-"bE8 c #6576a3",
-"bjR c #657885",
-"aUr c #657984",
-"aOl c #657985",
-"#nJ c #658293",
-".Ay c #658594",
-"a6p c #6588a8",
-".sg c #65a3e8",
-".qY c #65a3ed",
-".sU c #65a5e7",
-"bAF c #65b1d5",
-"a7# c #65b2d7",
-"#1f c #65b3dc",
-"#Tb c #65b6e2",
-"#4Y c #65b7e5",
-"aUc c #662541",
-".Hs c #66263e",
-"a0p c #662641",
-"aDb c #662744",
-"bV6 c #66294c",
-"bkh c #662b45",
-"aKG c #662d53",
-"ajC c #663059",
-"bnJ c #66374e",
-"aNQ c #664369",
-"aAb c #664855",
-"aPB c #664b7b",
-"aH2 c #664c5a",
-"aEx c #664d5b",
-"#FL c #664e59",
-"aXs c #664e7e",
-"bHt c #664f7f",
-"bzz c #66527a",
-"bBg c #66537c",
-".TX c #66565b",
-".dC c #666666",
-"bTQ c #666667",
-"#4O c #66666e",
-"aec c #66676f",
-"bCK c #666792",
-"bQB c #666b6d",
-"bLJ c #666b6e",
-".AV c #666e72",
-"#Ff c #666e74",
-"#Vk c #666e75",
-".Zr c #666f73",
-"#b9 c #666f74",
-"#rK c #666f76",
-".ZJ c #667075",
-".Eh c #667178",
-"byb c #66717a",
-"#2i c #66737a",
-"a.3 c #66737b",
-"#d7 c #66737c",
-".F9 c #66747b",
-"bhI c #66747c",
-"#Nf c #66747d",
-"#3k c #66767f",
-"a3l c #667984",
-"a6s c #667985",
-"bkA c #667a85",
-"#nH c #667a86",
-"aXv c #667f94",
-"awT c #668191",
-"bd8 c #668291",
-"bUn c #6684a1",
-"#T3 c #66899c",
-".IG c #6697b9",
-".rC c #66a3e7",
-"#Zb c #66b5df",
-"#Qc c #66b5e1",
-"#ws c #66b6df",
-"#Sb c #66b8e5",
-"#Rb c #66bbe8",
-".w. c #66fff9",
-"bcC c #672541",
-"b.. c #672542",
-"a2# c #672642",
-"azd c #672743",
-"aZd c #672744",
-"aO# c #672843",
-"aRG c #672c52",
-"aRY c #672d47",
-"#h3 c #673047",
-"aFX c #67354b",
-".Bi c #67424e",
-"axs c #674855",
-"aUJ c #674b59",
-"#In c #674d5b",
-"aHQ c #67575c",
-"aGR c #67575d",
-".l# c #676568",
-".bd c #676767",
-"bTM c #676768",
-"bTA c #676b6e",
-".AG c #676f73",
-".AI c #676f74",
-"#kP c #676f75",
-"#Qw c #676f76",
-".Zs c #677074",
-".CJ c #677075",
-"#MH c #677076",
-".CB c #677176",
-"#MQ c #677177",
-"aBw c #67717b",
-"#5u c #67737b",
-"bJX c #67737c",
-".G. c #67747c",
-"#Fr c #67747d",
-"a#w c #6774a0",
-".Ga c #67757d",
-"a4J c #67757e",
-"#R7 c #67757f",
-"akd c #677780",
-"aXl c #677a86",
-"aVc c #677b86",
-"aOm c #677b87",
-"aOs c #677b88",
-"#42 c #677ba8",
-"aOt c #677c88",
-"ba9 c #678597",
-".VR c #678698",
-"#Nx c #6786af",
-"#xk c #67899a",
-"#mk c #67899b",
-".qX c #67a2e7",
-"#.6 c #67a7d0",
-".sT c #67a9e8",
-".tw c #67abe8",
-".t8 c #67aee9",
-"a7Q c #67b6de",
-"#t3 c #67b7e0",
-"#wt c #67b8e1",
-"#3M c #67bae7",
-"#Va c #67c2f0",
-"aRM c #682541",
-".5f c #682643",
-".#2 c #682669",
-"aPJ c #682946",
-"aNz c #682d52",
-"aEQ c #68344a",
-"aGv c #68344b",
-"blP c #68354c",
-"a21 c #68354d",
-".Fe c #68424e",
-"aNP c #68466d",
-"aKm c #684a59",
-"av7 c #684c5a",
-"blG c #684c5b",
-"#dv c #684d5a",
-"#FO c #684d5b",
-"bBn c #684e76",
-"bbo c #685382",
-"aGS c #68575d",
-"#4L c #68676f",
-"QtU c #686868",
-"bMi c #686869",
-"bQR c #686969",
-"bN9 c #68696a",
-"bQD c #686d6f",
-".AH c #687074",
-".0N c #687075",
-".4k c #687176",
-"#nw c #687177",
-"bkG c #687179",
-"bl1 c #687279",
-"#4K c #68727a",
-"aIW c #68737b",
-"#Fm c #68737c",
-".Gb c #68747c",
-".G# c #68757d",
-"a42 c #68757e",
-"ard c #687780",
-"a1z c #68787f",
-"ava c #687881",
-"a8q c #687988",
-"aVe c #687a85",
-"a2z c #687b86",
-"a6t c #687c87",
-"a5Q c #687c88",
-"bir c #68849c",
-"#Of c #688799",
-"bgc c #68899e",
-"#VT c #688b9e",
-".YS c #688c9f",
-".UJ c #6897b2",
-"a7G c #68a2cb",
-".VQ c #68abd5",
-".uG c #68ade8",
-"bT8 c #68b9e3",
-".rj c #68f3ff",
-".u2 c #68fffa",
-"auz c #692542",
-"bxZ c #692742",
-"#my c #692744",
-"aUe c #692845",
-"#1M c #69284b",
-"aD1 c #69294c",
-"abG c #692a47",
-"aQF c #692c51",
-"aTZ c #692c52",
-"aJK c #692d54",
-"a8L c #692e47",
-"bnF c #693249",
-"#gL c #69364c",
-"#xn c #69384d",
-"aJb c #69394e",
-"afH c #693f67",
-"bGW c #694269",
-".I6 c #694652",
-"aMp c #694870",
-".BY c #69494f",
-"#9P c #694d5a",
-"#Dk c #694d5b",
-"bpP c #695765",
-"aLh c #69585e",
-"#OB c #695e6a",
-".rU c #69642b",
-".ef c #696969",
-"bL0 c #696d70",
-"bTE c #696e70",
-".AF c #697175",
-"#Rv c #697178",
-".1y c #697277",
-"#uf c #697279",
-".67 c #697378",
-"#Rr c #69737a",
-"#WX c #69747b",
-"a2M c #69757c",
-"#jw c #69757d",
-".F8 c #69767e",
-"bds c #69767f",
-"a9# c #69777f",
-"bbd c #697780",
-"#5y c #697881",
-"a8l c #697882",
-"a63 c #697982",
-"aV0 c #697c87",
-"aZ2 c #697c88",
-"a7B c #697d88",
-"bg# c #697e8d",
-".C1 c #698b9d",
-".XO c #698d9f",
-".m2 c #6991ab",
-"bg6 c #6993ad",
-".GC c #699cb9",
-".yr c #69a189",
-".sf c #69a8e8",
-".vR c #69a9e2",
-".vQ c #69a9e3",
-".sS c #69ace9",
-".vb c #69ade6",
-".tv c #69afe9",
-"#2B c #69bce7",
-"axc c #69bce8",
-".tm c #69fdfc",
-".wE c #69fff7",
-".0u c #6a2441",
-".0A c #6a2442",
-"#Av c #6a2944",
-"aRV c #6a2947",
-"#5o c #6a2a47",
-"#YA c #6a2f55",
-".a7 c #6a2f67",
-"#lq c #6a3148",
-"aMB c #6a355a",
-"b.F c #6a374e",
-"brk c #6a384e",
-"aoz c #6a3b63",
-"#7u c #6a3c60",
-"bwz c #6a4c74",
-".e5 c #6a4c78",
-"#i6 c #6a4d5b",
-"bOv c #6a4f7e",
-".j4 c #6a5252",
-"aBP c #6a5a60",
-".lU c #6a686b",
-".gs c #6a6a6a",
-"bL4 c #6a6a6b",
-"bMd c #6a6b6b",
-"bQ0 c #6a6b6c",
-"#Ws c #6a6e74",
-".Zt c #6a7277",
-".1Z c #6a7377",
-"aqQ c #6a737a",
-"bj1 c #6a737b",
-"am9 c #6a747c",
-".3t c #6a757b",
-"a3X c #6a757d",
-"bF3 c #6a757e",
-"byF c #6a75a1",
-"aaR c #6a767c",
-"a62 c #6a7780",
-"a8k c #6a7880",
-"a1D c #6a7980",
-"bea c #6a7981",
-"akc c #6a7983",
-"#4o c #6a7a83",
-"bc0 c #6a7a85",
-"aYZ c #6a7d88",
-"a4Y c #6a7d89",
-"bcc c #6a829a",
-"bz7 c #6a87a4",
-".3J c #6a8da0",
-".FW c #6a98b3",
-".vT c #6aa8e0",
-".4W c #6aa9d0",
-".vS c #6aa9e1",
-"#u4 c #6abbe5",
-"bRd c #6abce5",
-"anm c #6abeea",
-"aww c #6abfeb",
-".r2 c #6af6fd",
-".tZ c #6afefb",
-"a8T c #6b2442",
-"bBA c #6b2742",
-"bwT c #6b2743",
-"aZc c #6b2745",
-"aUf c #6b2845",
-"bkg c #6b2945",
-"aOQ c #6b2b50",
-"bOj c #6b2b52",
-"bpJ c #6b314b",
-"aWz c #6b334b",
-"bpH c #6b374e",
-".dx c #6b3d64",
-"ala c #6b4256",
-"aIA c #6b476f",
-"avA c #6b4a58",
-"#Eh c #6b4c5a",
-"amq c #6b4e5c",
-"acs c #6b565b",
-"aAy c #6b5b60",
-"aQz c #6b5f67",
-".aQ c #6b6066",
-"acB c #6b636b",
-"#9B c #6b6566",
-".oa c #6b6764",
-"a.0 c #6b6a6a",
-".jl c #6b6b6b",
-"adR c #6b6c6d",
-"bSi c #6b7072",
-".Ru c #6b7378",
-".10 c #6b7478",
-".ZI c #6b7479",
-"aCI c #6b747d",
-"aZO c #6b757d",
-"a2C c #6b767c",
-"#pz c #6b767d",
-"aqV c #6b767e",
-"#Tm c #6b767f",
-"#y1 c #6b777e",
-"#hs c #6b777f",
-"#CE c #6b7780",
-"aAV c #6b7a83",
-"avg c #6b7a84",
-"arc c #6b7b84",
-"aV3 c #6b7b86",
-"b#A c #6b7d88",
-"bit c #6b7e89",
-".xS c #6b855b",
-"auC c #6b8fa2",
-".ug c #6ba6dc",
-"#Z1 c #6ba7c5",
-".rB c #6ba7e7",
-".sR c #6bafea",
-"bU4 c #6bb8e1",
-"bMH c #6bbde8",
-"#t2 c #6bbee7",
-"alX c #6bbee9",
-"#cC c #6c2342",
-".5c c #6c2442",
-"aVq c #6c2443",
-".2Y c #6c2543",
-"aJo c #6c2644",
-"aCC c #6c2645",
-".7K c #6c2745",
-"#3d c #6c284b",
-"aVw c #6c2b4f",
-"bBe c #6c2b50",
-"apy c #6c3058",
-"aSn c #6c374e",
-"bIm c #6c375f",
-"#xZ c #6c384e",
-"aOC c #6c4570",
-"#DR c #6c4c5a",
-"#fQ c #6c4e5c",
-"amh c #6c4f5d",
-"ax6 c #6c5b61",
-".38 c #6c5c62",
-"bBS c #6c5c64",
-"#66 c #6c5f66",
-".qo c #6c6862",
-".bP c #6c6c6c",
-"bPv c #6c6c6d",
-"a3f c #6c6c94",
-"aCg c #6c6f93",
-".77 c #6c7579",
-"#he c #6c757a",
-".8i c #6c767c",
-"bb1 c #6c777f",
-"a.k c #6c789c",
-"#5s c #6c7983",
-"#.i c #6c7a81",
-".Ig c #6c7a82",
-"#Oi c #6c7a83",
-"bp2 c #6c7a84",
-"avf c #6c7b84",
-"arJ c #6c7b85",
-"aWK c #6c7b86",
-"bfh c #6c7c86",
-"a4. c #6c7e87",
-"bjT c #6c7e89",
-"a1y c #6c7f89",
-"a0O c #6c7f8a",
-"b.3 c #6c808b",
-".Tx c #6c91a3",
-"#Qg c #6ca2c7",
-".se c #6cace8",
-".vP c #6cade3",
-"#Pj c #6cb3de",
-".tu c #6cb5ea",
-".yo c #6cb8a5",
-"bHn c #6cbde7",
-"#Or c #6cbee9",
-"#Ub c #6cccfa",
-".sH c #6cfafd",
-".vC c #6cfff9",
-".0B c #6d2442",
-"bbR c #6d2542",
-"aUb c #6d2543",
-"aD2 c #6d2645",
-"a00 c #6d2745",
-"aeM c #6d2947",
-"aTY c #6d2b4f",
-"aXE c #6d2b50",
-"aKt c #6d324b",
-"byJ c #6d3860",
-"azK c #6d394e",
-"ag7 c #6d4067",
-"aHw c #6d456c",
-"#vJ c #6d456d",
-"aEz c #6d4f5d",
-"buK c #6d505d",
-"aAt c #6d556c",
-".mD c #6d6b6d",
-".b# c #6d6d6d",
-"bTs c #6d7174",
-".A9 c #6d7478",
-"#an c #6d767a",
-"#.K c #6d767b",
-"#hk c #6d767d",
-".06 c #6d777c",
-"akf c #6d777e",
-"asI c #6d777f",
-"#pi c #6d7880",
-"bpb c #6d7982",
-"a9N c #6d799e",
-"#8l c #6d7a82",
-".Ii c #6d7b83",
-"#Ng c #6d7b84",
-"#Oh c #6d7b85",
-"aXm c #6d7b87",
-"a1G c #6d7c83",
-".Ih c #6d7c84",
-"aL8 c #6d7c85",
-"atB c #6d7d86",
-"a3o c #6d7f8a",
-".C6 c #6d7f90",
-"aWH c #6d808a",
-"#Mf c #6d808b",
-"ad3 c #6d91a9",
-"bIJ c #6d94ba",
-".sQ c #6db3ea",
-".t7 c #6db7e9",
-"bKX c #6dc1eb",
-"avY c #6dc1ec",
-"bIv c #6dc9f4",
-".ux c #6dfefa",
-".81 c #6e2442",
-"bcD c #6e2542",
-"#aY c #6e2745",
-"awP c #6e2846",
-"aeN c #6e2847",
-"bju c #6e2946",
-"aGd c #6e2a4f",
-"#28 c #6e2b50",
-"aUH c #6e304b",
-"byx c #6e3057",
-"#h5 c #6e374e",
-"bwF c #6e385e",
-"br9 c #6e3a51",
-"#AY c #6e4b59",
-"bdB c #6e4e7e",
-"aGQ c #6e5c61",
-"biP c #6e5c62",
-"ayT c #6e5d62",
-"aAs c #6e6185",
-"bcV c #6e6288",
-"bU8 c #6e6891",
-"bw3 c #6e6992",
-"QtS c #6e6e6e",
-"bQN c #6e6e6f",
-".3a c #6e767b",
-"#Kr c #6e777c",
-"#3E c #6e787f",
-"bcL c #6e7980",
-"#mO c #6e7981",
-"bpZ c #6e7a83",
-"bp1 c #6e7b83",
-"#ov c #6e7b84",
-"bpe c #6e7c83",
-".I. c #6e7c84",
-".Ic c #6e7c85",
-"adY c #6e7d86",
-"#5x c #6e7e87",
-"a4# c #6e808b",
-"bg5 c #6e8595",
-"#Bk c #6e8997",
-"b#v c #6e8eab",
-".WN c #6e93a6",
-"#8v c #6e94b9",
-"bWa c #6ea1c3",
-".qi c #6ea7eb",
-"auk c #6ebde3",
-"#0. c #6ec1ea",
-"bRf c #6ec2ec",
-"ax# c #6ec4ef",
-"#1i c #6ec4f0",
-".qC c #6eecfd",
-".w9 c #6efff7",
-".0v c #6f2443",
-"atg c #6f2543",
-"aIy c #6f2544",
-"a4x c #6f2643",
-"aCp c #6f2645",
-"aQ3 c #6f2745",
-"a58 c #6f2746",
-"aSG c #6f2847",
-"aNy c #6f2a4f",
-"a3O c #6f304c",
-"a97 c #6f354c",
-"aS2 c #6f384f",
-"aK9 c #6f394f",
-"aQ# c #6f3950",
-"ael c #6f4c78",
-"beS c #6f5262",
-"aFA c #6f5a5e",
-".S. c #6f5d64",
-"boX c #6f5e78",
-"Qtu c #6f6f6f",
-"bTx c #6f7375",
-".k# c #6f7698",
-".CA c #6f777c",
-"aca c #6f777f",
-".Zu c #6f787c",
-"#su c #6f787d",
-".CM c #6f797d",
-"bAX c #6f7a82",
-"#6Y c #6f7b82",
-"#hw c #6f7b83",
-"#6U c #6f7c83",
-"#CF c #6f7c84",
-".H9 c #6f7d85",
-"a.7 c #6f7d86",
-".I# c #6f7e86",
-"#Pd c #6f7e87",
-"bjU c #6f818c",
-"aSa c #6f818d",
-"b.4 c #6f828c",
-"bhH c #6f828d",
-".Cs c #6f8695",
-"axA c #6f8898",
-"bgi c #6f88af",
-".C7 c #6f8d9b",
-".Cq c #6f91a2",
-"#XH c #6f91a3",
-"#gH c #6f94a7",
-".VH c #6f95a8",
-".qW c #6fa8e6",
-".sd c #6fafe9",
-".yq c #6fb098",
-".vO c #6fb2e3",
-"bCz c #6fc0e8",
-"#2z c #6fc3ed",
-"awz c #6fc4ef",
-"#Z9 c #6fc4f0",
-"bCC c #6fcef8",
-".w# c #6ffff6",
-"a4C c #702544",
-"aJn c #702545",
-"a4q c #702645",
-"aVG c #702646",
-"bFo c #702745",
-"aeL c #702948",
-"aKI c #70294e",
-"aNr c #702a49",
-"aLE c #702a4e",
-"aJL c #702b51",
-"bCo c #70324c",
-"#gW c #70344c",
-"aX# c #70374f",
-"as7 c #703a50",
-"aJ2 c #703a51",
-"bDd c #703d63",
-"#wW c #704168",
-"bDy c #704d7c",
-"aA6 c #705265",
-"ayM c #70535e",
-"a1K c #70616c",
-"#5# c #706584",
-"aA4 c #706891",
-"Qtp c #707070",
-"bTN c #707071",
-".11 c #70787c",
-".0O c #70787d",
-"#SH c #70787e",
-".78 c #70797d",
-".Cw c #70797e",
-".2g c #707a80",
-"aVX c #707b84",
-"#Rk c #707c85",
-"aU9 c #707d86",
-".Ia c #707e86",
-"#Lo c #707e87",
-".Ib c #707f87",
-"ai8 c #707f88",
-"alt c #707f89",
-"aAh c #708089",
-"a39 c #70889a",
-".yp c #70b7a2",
-".va c #70b7e6",
-"aO6 c #70c4ee",
-"bJz c #70c5ef",
-"#3L c #70c6f1",
-"alY c #70c8f3",
-"bFH c #70ccf6",
-".rk c #70f2ff",
-"aUa c #712544",
-"aO. c #712644",
-"aT9 c #712645",
-"#0w c #71274a",
-"bkn c #712946",
-"aPQ c #71294d",
-"#24 c #71294e",
-"aBe c #712a4f",
-"bHe c #712b50",
-"aev c #712d53",
-"bDf c #71304c",
-"aRu c #713850",
-"#wT c #71394f",
-"#8P c #713c60",
-"bBL c #714d7b",
-"#GI c #71515f",
-"#H6 c #715866",
-".p. c #716540",
-".fN c #717171",
-"bTR c #717272",
-"bLS c #717477",
-"bNw c #717577",
-"bSn c #717678",
-"#XU c #71787f",
-".CY c #71797e",
-"aPp c #717980",
-"#cY c #717a7e",
-".CK c #717a7f",
-"aKb c #717a80",
-"a6V c #717a9e",
-".Eg c #717b81",
-".6Z c #717d83",
-"#P5 c #717f89",
-".Cp c #718088",
-"#4p c #718089",
-"#6G c #71818a",
-".Kf c #71828a",
-"aZ3 c #71828b",
-"aV1 c #71828d",
-"#Pp c #7182a9",
-"a4Z c #71838d",
-"bdH c #71838e",
-"bc4 c #71838f",
-"bjS c #71848e",
-"bUo c #718497",
-"aWc c #718593",
-"#Oz c #7186ad",
-"#Re c #719dc4",
-".sc c #71b2ea",
-".uF c #71bbe9",
-".sP c #71bbec",
-"b.y c #71c5ed",
-"axb c #71c7f2",
-"bAG c #71c9f3",
-"bDt c #71caf4",
-"bHp c #71cdf8",
-"aO7 c #71cef8",
-"anp c #71cef9",
-".tn c #71fcfb",
-".xC c #71fdf3",
-".u3 c #71fff8",
-".#b c #721672",
-"bDm c #722543",
-"a0o c #722544",
-"aQE c #722545",
-"aHE c #722646",
-"aeK c #722747",
-"aTa c #722749",
-"bOk c #72274a",
-"bmn c #722846",
-"aWk c #72294d",
-"aHC c #722d51",
-"aUV c #722e4c",
-"aHJ c #72334b",
-"asj c #723a50",
-"bqg c #723d64",
-"bAo c #723e64",
-"#se c #723f65",
-"ass c #724d5c",
-"#Im c #725160",
-"aEy c #725261",
-"anN c #725363",
-"aA9 c #725f65",
-"ae. c #72676f",
-"#J3 c #72686f",
-".nn c #727172",
-".bM c #727272",
-"bKD c #727273",
-"bQO c #727373",
-"bO5 c #727577",
-"bJ# c #727779",
-".4l c #727a7f",
-"aqb c #727a81",
-".Cy c #727b7f",
-"#cj c #727b82",
-"#9D c #727d85",
-"#vY c #727f87",
-"a40 c #728088",
-"#KA c #728089",
-"a1E c #728188",
-"a6d c #72818a",
-"bv5 c #72818b",
-".Ke c #72828a",
-"#63 c #72838c",
-"aUs c #72838d",
-"aZR c #72848e",
-"bdO c #728490",
-"aTG c #728792",
-".mh c #7293b1",
-".vk c #72acd9",
-".sb c #72b5eb",
-".tt c #72c1ec",
-"bFD c #72c7f0",
-"bFF c #72c8f2",
-"bPO c #72c9f3",
-"bCB c #72caf4",
-"#2A c #72caf5",
-"aNY c #72cbf5",
-"bBF c #72d0fb",
-".r3 c #72f6fd",
-".wF c #72fff6",
-".4a c #732344",
-".0w c #732444",
-"aeJ c #732445",
-"a7g c #732544",
-"aB2 c #732545",
-"aBb c #732645",
-"aN1 c #732747",
-"#7N c #732848",
-"aW0 c #73284d",
-"aJp c #732948",
-"aha c #732b51",
-"aes c #732c52",
-"aP6 c #732d4a",
-"a6I c #732f4c",
-"bCn c #73304c",
-"aTQ c #73364f",
-"#A2 c #734d5c",
-".33 c #73505f",
-"#dj c #73515f",
-"bvX c #736991",
-"#8D c #736d99",
-"QtF c #737373",
-"bQV c #737474",
-"bLW c #737678",
-"bLZ c #737779",
-"bLR c #73777a",
-".AW c #73797c",
-".Cx c #737b80",
-"#eY c #737c80",
-".Cz c #737c81",
-"#YI c #737c83",
-"bjX c #737d84",
-"a2m c #737e86",
-".F7 c #737f87",
-"#P9 c #738087",
-"#yh c #738088",
-"a1V c #738189",
-"a2A c #738289",
-"#fj c #73828b",
-"aw1 c #73828c",
-".J5 c #73838b",
-"bdM c #73838c",
-"#ee c #73848e",
-"b.t c #73858f",
-"#My c #7385ab",
-"a9. c #738690",
-"ak. c #7397ab",
-"avF c #739aad",
-".rA c #73afe8",
-"ann c #73b8e1",
-"aNZ c #73b9e3",
-"agO c #73bae0",
-"al1 c #73bbe5",
-"#Mr c #73c1ea",
-"ano c #73c3ed",
-"al0 c #73c6f0",
-"bFG c #73c8f2",
-"avl c #73c9f3",
-"#u1 c #73caf4",
-"bHo c #73caf5",
-"axa c #73cbf7",
-"alZ c #73ccf6",
-"#Z0 c #73ccf7",
-"#Nn c #73cffb",
-".t0 c #73fef9",
-"##9 c #742444",
-"aE0 c #742445",
-"a0k c #742544",
-"a73 c #742545",
-"bx0 c #742645",
-"bIo c #742649",
-"a8B c #742948",
-"apz c #742b51",
-"#n2 c #74344d",
-"#kt c #74374f",
-"bG4 c #74385d",
-"#zh c #743950",
-"bzy c #74395f",
-".OH c #744e5c",
-"ak0 c #745260",
-"#lw c #745660",
-"aIH c #745d83",
-".OT c #746066",
-"#6b c #746f9a",
-".eY c #747474",
-"bN0 c #747575",
-"bMj c #747576",
-".A5 c #747777",
-"bTt c #747779",
-"bTD c #74787a",
-".B# c #747a7d",
-"#w5 c #747b81",
-".8k c #747c80",
-"#YZ c #747c82",
-"bDL c #747c84",
-"#0z c #747d82",
-"#Qn c #747d84",
-"#MP c #747e84",
-"afk c #747f84",
-"a1S c #748086",
-"aFh c #748088",
-"a1F c #74838a",
-"avK c #74838c",
-"a9S c #74838d",
-".J6 c #74848c",
-"a3m c #74848d",
-".3o c #74848e",
-"#LE c #7484ab",
-".8n c #74858e",
-"aY0 c #74858f",
-"aWJ c #748690",
-"a6X c #7492ad",
-"ae2 c #7499ac",
-".UA c #749aae",
-"bch c #749dc3",
-"al2 c #74a7d0",
-"aO8 c #74aad2",
-".sa c #74b9ec",
-"#T. c #74bee7",
-".t6 c #74c4eb",
-"#Oq c #74c9f3",
-"bDs c #74caf4",
-"bKW c #74cbf5",
-"bDr c #74ccf6",
-"#1g c #74ccf7",
-".vD c #74fff7",
-"Qt8 c #751575",
-".0C c #752444",
-".0x c #752445",
-"a1d c #752545",
-"aC2 c #752546",
-"aI. c #752646",
-"bJu c #752649",
-"bPI c #75264b",
-"aSu c #75274c",
-"aeO c #752848",
-"aCs c #75284c",
-"aCA c #75294e",
-"aqu c #752a50",
-"a4w c #752c4a",
-"auT c #752d55",
-"#gN c #75374f",
-"b.E c #753851",
-"#sW c #75385d",
-"bzY c #754d7b",
-"bbG c #754e5d",
-"boi c #754f6b",
-"amn c #755261",
-".K6 c #755560",
-"acG c #755d64",
-".Y9 c #756167",
-"aGX c #756269",
-"#4M c #75676f",
-"bXo c #756991",
-".#S c #757575",
-".AY c #757b7e",
-".4m c #757d81",
-".79 c #757d82",
-".ZH c #757e83",
-"#Ur c #757e84",
-"ab4 c #757e85",
-"aqh c #757f86",
-".4D c #758086",
-"#pn c #758087",
-"#hh c #758088",
-"a9U c #758188",
-"#Wp c #758189",
-"bpa c #75818a",
-"abY c #75828a",
-"#Pc c #75848c",
-".J4 c #75848d",
-".8c c #75858e",
-"#au c #75858f",
-".Uw c #75868f",
-"aUq c #758690",
-"aXk c #758791",
-"at9 c #7599ad",
-".GF c #7599b3",
-"#Bs c #759bae",
-"#KF c #759fc6",
-"aMM c #75a6cf",
-".XU c #75a9cd",
-".57 c #75b1d6",
-".v# c #75bfe7",
-"bFI c #75c2ec",
-"bBD c #75cbf6",
-"bIu c #75ccf6",
-"#1h c #75ccf7",
-"bJy c #75cdf7",
-"bFE c #75cdf8",
-"bAH c #75cffa",
-"aP1 c #75d0fb",
-"aML c #75d1fb",
-".x3 c #75ece0",
-".sI c #75f9fc",
-"aZs c #762345",
-".0z c #762445",
-"aUg c #762545",
-"aU# c #762546",
-"aH8 c #762646",
-"aOO c #762647",
-"aVx c #76274b",
-"bSB c #76284b",
-"aB0 c #76284d",
-"bjB c #76324e",
-"#xq c #76364f",
-"bqh c #76365b",
-".4h c #76374f",
-"#hR c #763750",
-"bpG c #763851",
-"acQ c #763f65",
-"#fX c #764455",
-"bfS c #76455b",
-"#CN c #764f5d",
-"#pM c #764f5e",
-"#9V c #76535f",
-".TR c #765361",
-"amg c #765362",
-"ba2 c #765565",
-"aKe c #766066",
-"bP4 c #76646b",
-"av1 c #766689",
-"#I5 c #766a71",
-".tb c #76741f",
-".mZ c #767676",
-"bMe c #767677",
-"bLN c #767a7c",
-"#6s c #767da0",
-".Zv c #767e82",
-"arT c #767e85",
-"#WV c #767e86",
-"#op c #767f83",
-"#5N c #767f84",
-"a5A c #767f86",
-"#nC c #768189",
-".Gd c #768389",
-"#hn c #76838b",
-"#tl c #76838c",
-".J7 c #76848d",
-".J8 c #76858e",
-".kR c #7685a7",
-".RH c #76868f",
-".8f c #768690",
-".4z c #768790",
-"bhG c #768892",
-"bep c #768893",
-"bsl c #768b9f",
-".lz c #768fb0",
-"b.Z c #7693ad",
-".8K c #769db0",
-"#q7 c #769eb2",
-"bIw c #76a4ce",
-".vU c #76b2da",
-"aP2 c #76c1ec",
-"#No c #76c9f3",
-"#vQ c #76cdf7",
-"bRe c #76cdf8",
-"bSF c #76cef8",
-"bCA c #76cef9",
-"bMG c #76cff9",
-"bBE c #76cffa",
-"#Uc c #76d0fb",
-"aLO c #76d4fe",
-".qD c #76eeff",
-".uy c #76fef7",
-".x. c #76fff4",
-"aQV c #772142",
-".0y c #772445",
-"aQW c #772545",
-"aII c #772546",
-"ayp c #772647",
-"bR. c #772649",
-"aD# c #77264a",
-"aH9 c #772747",
-"a.H c #772748",
-"aKH c #77274b",
-"bHf c #77284c",
-"aei c #77294f",
-"#YB c #772a4f",
-"aVQ c #77304b",
-"#tL c #773358",
-"brX c #773459",
-"bCk c #77355a",
-".aR c #77367a",
-"#gM c #773750",
-"aOH c #773851",
-".c4 c #774680",
-"bPU c #774c7a",
-"bk1 c #775362",
-"bFT c #775d7a",
-".W5 c #776268",
-"aDv c #77666d",
-".bc c #777777",
-"bMa c #777878",
-"bLO c #777b7d",
-"aM1 c #777e82",
-".6L c #777f83",
-".5w c #777f84",
-"aq8 c #777f87",
-"ahz c #778086",
-"#19 c #778087",
-"aue c #778188",
-".Ep c #778287",
-".Gg c #778389",
-"bDO c #77838c",
-".65 c #77848b",
-".Kb c #77858e",
-".Ka c #77868e",
-".J9 c #77868f",
-".SA c #778790",
-".QF c #778791",
-"#cc c #778891",
-"a60 c #778892",
-"aV2 c #778893",
-"bj. c #778993",
-".C8 c #778c96",
-"#Zz c #77909d",
-".C0 c #7794a3",
-".vN c #77bce3",
-".s# c #77bfed",
-"#u3 c #77cff9",
-"awx c #77d0fa",
-"auQ c #77d1fb",
-"b## c #77d1fd",
-".rl c #77f2fe",
-".2T c #782445",
-"aYF c #782446",
-"bBp c #782447",
-"aBS c #782546",
-"#oh c #782647",
-"bkp c #782747",
-"#6B c #782748",
-"aQI c #78274a",
-"#Zp c #78274b",
-"aZj c #78274c",
-"bFd c #782848",
-"a5o c #782a4a",
-"bCm c #782b4b",
-"#yD c #783156",
-"boR c #78344f",
-".7O c #783750",
-"auu c #783f55",
-"aem c #784268",
-"aa7 c #78547c",
-"#I0 c #785f6d",
-"azO c #786268",
-"bK6 c #786a91",
-"byE c #786f9b",
-".os c #787878",
-"bMs c #787979",
-"aav c #787b7c",
-"bNy c #787b7e",
-"aog c #787f85",
-"#2o c #788085",
-"ab3 c #788087",
-"aDk c #788089",
-".7g c #788186",
-"#Fh c #78838b",
-"aaz c #78848b",
-"#JN c #78868e",
-"#c5 c #78868f",
-".K. c #78878f",
-".K# c #788790",
-"a7C c #788791",
-".Pz c #788891",
-".WH c #788892",
-"#5R c #788991",
-".Pg c #788992",
-"b.s c #788a94",
-"aKV c #7898c0",
-"#oQ c #78a1b5",
-".IF c #78acca",
-".qh c #78ade6",
-".uM c #78b0d4",
-"#Ue c #78c5ee",
-".sO c #78c8ee",
-"awy c #78d0fa",
-"bMF c #78d0fb",
-"avm c #78d2fc",
-"avX c #78d2fd",
-".wa c #78fff5",
-"aVr c #792144",
-"bo# c #792545",
-"aSs c #792546",
-"aT8 c #792547",
-"#4j c #792548",
-"aLw c #792549",
-"##i c #792647",
-"bHh c #792649",
-"bjv c #792748",
-"apA c #79284d",
-"bmZ c #792948",
-"#YD c #79294d",
-"bq3 c #79294f",
-"aMC c #792e52",
-"bCa c #793054",
-"#zn c #793055",
-"aXY c #79354e",
-"aJa c #793a52",
-"br8 c #793b54",
-"abi c #794570",
-"a.d c #794a76",
-"bvr c #794c79",
-"bsJ c #794c7a",
-"#tE c #795463",
-"bt. c #796268",
-"anq c #79779d",
-"#5c c #797895",
-".aG c #797979",
-"bQW c #79797a",
-"bMm c #797a7a",
-"bNT c #797c7e",
-"bS# c #797c7f",
-"bKp c #797d7f",
-".Iv c #798083",
-"ae7 c #798288",
-"#XW c #798289",
-"aty c #79838a",
-"#7W c #79848c",
-"bCU c #79858d",
-"#dW c #79868e",
-"aK# c #79868f",
-".Id c #79878f",
-"#ke c #798790",
-"#jr c #798890",
-".LT c #798891",
-"#iz c #798991",
-".RC c #798992",
-"#pl c #798993",
-"#js c #798a92",
-".XI c #798a93",
-"aJY c #7990b8",
-".4N c #79a1b5",
-".rv c #79bbec",
-"bBG c #79bce6",
-"#Qb c #79c0e9",
-"#u2 c #79d2fc",
-"b#J c #79d3fd",
-".pg c #79d4eb",
-".to c #79fcf9",
-".u4 c #79fef7",
-"aF2 c #7a1f43",
-"aHa c #7a2043",
-"aOJ c #7a2445",
-"aTs c #7a2446",
-"aBc c #7a2545",
-"a2O c #7a2546",
-"aTo c #7a2547",
-"bU1 c #7a2548",
-"#rn c #7a2549",
-"#xr c #7a2647",
-"aNx c #7a264a",
-"aET c #7a2747",
-"#4g c #7a274b",
-"aJM c #7a274c",
-"bBf c #7a2e52",
-"bqi c #7a2f53",
-"bHd c #7a2f55",
-"bBo c #7a3055",
-"aNw c #7a375e",
-".6u c #7a3851",
-"ag5 c #7a3a51",
-"ac1 c #7a436c",
-"aNk c #7a4b68",
-".9H c #7a5362",
-"am# c #7a5363",
-"#ne c #7a5463",
-"art c #7a5564",
-"av6 c #7a5664",
-"al3 c #7a5e83",
-"aHU c #7a6165",
-"aNf c #7a6368",
-"bJP c #7a666d",
-"aLP c #7a678d",
-"bSL c #7a6a91",
-"#Nz c #7a6c72",
-".jn c #7a7a7a",
-"bNS c #7a7e80",
-"#nx c #7a8186",
-".CL c #7a8286",
-"aKd c #7a8288",
-".8I c #7a8388",
-"ajp c #7a848c",
-"#KJ c #7a84a8",
-"b.v c #7a858c",
-"aOi c #7a858d",
-"bf0 c #7a869b",
-"#fb c #7a8790",
-".H8 c #7a8890",
-".LU c #7a8891",
-".NW c #7a8892",
-"a5X c #7a8991",
-".LV c #7a8992",
-"aZT c #7a8993",
-"#gk c #7a8a92",
-".Ph c #7a8a93",
-".8N c #7a8a94",
-".Pj c #7a8b94",
-"bgS c #7a8c96",
-"b.q c #7a96ae",
-"awR c #7aa0b3",
-".5Y c #7aa2b6",
-"##Y c #7aaac6",
-".qV c #7ab1e6",
-".rz c #7ab5e8",
-".rx c #7ab9ea",
-".rw c #7ab9eb",
-".ru c #7abded",
-".s. c #7ac6ef",
-".r4 c #7af5fd",
-".xD c #7afff3",
-"aj6 c #7b2044",
-"bk2 c #7b2445",
-"b.# c #7b2446",
-"aGm c #7b2447",
-"aTj c #7b2546",
-"au4 c #7b2547",
-"a0b c #7b2549",
-"bBz c #7b2646",
-"#mB c #7b2647",
-"acK c #7b264a",
-"#4e c #7b264b",
-"#27 c #7b274b",
-"#YC c #7b284c",
-"aeI c #7b294a",
-"#wl c #7b2d51",
-"aUT c #7b324d",
-"aEO c #7b3750",
-".Fs c #7b394d",
-"aMo c #7b3953",
-"bx5 c #7b4d7a",
-"#AZ c #7b5160",
-"bd3 c #7b5767",
-"amE c #7b5968",
-"aMj c #7b597d",
-".Hj c #7b6063",
-"bOz c #7b6181",
-"aAx c #7b656a",
-".cp c #7b7b7b",
-".91 c #7b8287",
-".So c #7b8387",
-".2I c #7b8388",
-"#SB c #7b8389",
-"awo c #7b838a",
-".05 c #7b868c",
-"ar8 c #7b868e",
-"bnV c #7b8894",
-"a2y c #7b8a91",
-"bgb c #7b8a92",
-".LW c #7b8a93",
-".8g c #7b8a94",
-"#.O c #7b8b93",
-".Pi c #7b8b94",
-"#jB c #7b8b95",
-"asR c #7b8c94",
-"aY1 c #7b8c95",
-"#jn c #7b8c96",
-"bXz c #7b91a6",
-"#06 c #7b9db0",
-"axz c #7b9fb2",
-".D5 c #7ba1b4",
-".2u c #7ba4b8",
-"bHq c #7bb7e0",
-".ry c #7bb8e9",
-".rt c #7bc1ef",
-".t5 c #7bd4ed",
-".t1 c #7bfdf7",
-".wG c #7bfff4",
-"apF c #7c1e41",
-"aeV c #7c1f43",
-"aJt c #7c2044",
-"aRw c #7c2144",
-"aHb c #7c2145",
-"ahe c #7c2245",
-"ac4 c #7c2345",
-"ahd c #7c2346",
-".0D c #7c2446",
-"aIz c #7c2447",
-"bxt c #7c2449",
-".6o c #7c2547",
-"aQJ c #7c2548",
-"bk6 c #7c2647",
-"#9h c #7c2648",
-"#25 c #7c2649",
-"bKO c #7c264a",
-"bjw c #7c2748",
-"#x4 c #7c2b4f",
-"#sd c #7c2c50",
-"a2d c #7c354f",
-"aIQ c #7c374f",
-".88 c #7c3851",
-".bH c #7c3877",
-"bi1 c #7c3955",
-"aFc c #7c3c56",
-"bwX c #7c465b",
-"aWQ c #7c4b78",
-"amp c #7c5564",
-"aP3 c #7c577c",
-".By c #7c5c5c",
-"bsc c #7c5d66",
-"bkZ c #7c646a",
-".c1 c #7c7c7c",
-"bPC c #7c7d7d",
-"bQu c #7c7f81",
-".A8 c #7c8082",
-"afc c #7c838a",
-"#Bc c #7c8488",
-"atp c #7c848b",
-"bcW c #7c8490",
-".Ef c #7c868b",
-"aAU c #7c868e",
-"#ou c #7c878f",
-"a4l c #7c888e",
-"#P. c #7c888f",
-".Ij c #7c8991",
-".NG c #7c8a93",
-"bc5 c #7c8b92",
-".LX c #7c8b93",
-".NH c #7c8b94",
-".3B c #7c8c94",
-".Pk c #7c8c95",
-"#kX c #7c8d96",
-"bkC c #7c8d97",
-".wZ c #7c8e51",
-"bfb c #7c8e98",
-"a47 c #7c909a",
-"bbq c #7c92a3",
-"a7A c #7c98af",
-"asC c #7ca3b7",
-".7k c #7ca4b8",
-".pC c #7cb0ed",
-"#Vd c #7cc7f0",
-".uE c #7cceea",
-".ts c #7cd5ee",
-".qE c #7ceeff",
-".sJ c #7cf8fa",
-".vE c #7cfef4",
-"a1l c #7d1a3f",
-"a#9 c #7d2145",
-"bSA c #7d2245",
-"ahh c #7d2246",
-"bR# c #7d2345",
-"#uO c #7d2346",
-"bMz c #7d2347",
-".4b c #7d2447",
-"#.B c #7d2547",
-"#cE c #7d2548",
-"awe c #7d2648",
-"apB c #7d264a",
-"bjx c #7d2748",
-"bCl c #7d2849",
-"bjy c #7d2949",
-"#uP c #7d294d",
-"aen c #7d2b4f",
-"afG c #7d2c50",
-"bjA c #7d304d",
-"aUh c #7d3650",
-"#yy c #7d3a52",
-"#zd c #7d3a53",
-"akC c #7d4165",
-"aoC c #7d4468",
-"a#H c #7d4773",
-"avq c #7d547f",
-"apV c #7d5564",
-"aKl c #7d5565",
-"bdh c #7d5768",
-"bRr c #7d6180",
-".kN c #7d625c",
-"aGP c #7d656a",
-"bya c #7d666b",
-".SS c #7d666c",
-"bHD c #7d686e",
-"agS c #7d686f",
-"#4N c #7d7077",
-"aST c #7d787f",
-".gv c #7d7d7d",
-"aIY c #7d8183",
-"aFl c #7d848b",
-".Zw c #7d8588",
-"#2. c #7d868e",
-"#UW c #7d878e",
-"#w9 c #7d888f",
-"aR4 c #7d8890",
-"#Gi c #7d8992",
-"#xB c #7d8a92",
-"#IR c #7d8a93",
-"#k5 c #7d8b93",
-"#P4 c #7d8b94",
-"aYj c #7d8c93",
-".LY c #7d8c94",
-".LZ c #7d8c95",
-"bdN c #7d8d94",
-".L4 c #7d8d95",
-".Pl c #7d8d96",
-"aQj c #7d8d97",
-"#os c #7d8e97",
-"#lV c #7d8e98",
-"#hx c #7d8f99",
-"a8W c #7da6ba",
-".WO c #7da8be",
-".r9 c #7dcff1",
-".sN c #7dd2f0",
-".pZ c #7debff",
-"afF c #7e1c41",
-"apE c #7e2043",
-"a#8 c #7e2145",
-".#I c #7e217d",
-"aa. c #7e2245",
-"aS4 c #7e2246",
-"#o6 c #7e2346",
-"aJs c #7e2347",
-"aBd c #7e2445",
-"adp c #7e2446",
-".7I c #7e2447",
-"aTq c #7e2448",
-"aP5 c #7e2547",
-"#mz c #7e2548",
-"abH c #7e2648",
-"#26 c #7e2649",
-"buh c #7e284b",
-"aJZ c #7e2b4e",
-"bjz c #7e2d4b",
-"aoA c #7e2e51",
-"aN0 c #7e3054",
-"a#p c #7e3055",
-"akD c #7e3356",
-"a9o c #7e3750",
-"bjH c #7e3b55",
-"aoB c #7e3d61",
-".cU c #7e4176",
-".Bj c #7e4854",
-".DG c #7e4954",
-"akB c #7e4d71",
-"ayl c #7e5261",
-"#A1 c #7e5262",
-"au3 c #7e5362",
-"aCh c #7e667b",
-"boN c #7e686e",
-".ba c #7e7e7e",
-"aDr c #7e7e7f",
-"bQL c #7e7f80",
-"bKx c #7e8183",
-"bSa c #7e8284",
-".CX c #7e8588",
-".60 c #7e858a",
-"apf c #7e858d",
-".3b c #7e868a",
-"aOj c #7e878d",
-"ayu c #7e8890",
-".Er c #7e898e",
-".Gh c #7e8990",
-"#wz c #7e8991",
-".Gf c #7e8a90",
-"aXw c #7e8c93",
-"aWd c #7e8c94",
-"bga c #7e8d94",
-".NI c #7e8d95",
-".L0 c #7e8d96",
-"#pC c #7e8d97",
-".L1 c #7e8e96",
-".Py c #7e8e97",
-"b#9 c #7e9099",
-"#hB c #7e919c",
-"btV c #7e92a5",
-"#Lj c #7e95a3",
-"bVe c #7e96ac",
-".EJ c #7ea5b8",
-"#Tf c #7ebce3",
-".rs c #7ec8f0",
-".rm c #7ef1fe",
-".uz c #7efdf6",
-".x# c #7efff2",
-".#a c #7f147f",
-"anr c #7f1e41",
-"acJ c #7f1e42",
-"aP4 c #7f1f42",
-"a1k c #7f1f43",
-"akF c #7f2043",
-"aaY c #7f2044",
-"auq c #7f2143",
-"acU c #7f2144",
-"acT c #7f2244",
-"apD c #7f2245",
-"aB1 c #7f2345",
-"#tK c #7f2346",
-"aG7 c #7f2347",
-"#qy c #7f2446",
-"#.z c #7f2447",
-"aCq c #7f2448",
-"aTr c #7f2547",
-".1Q c #7f2548",
-"a8C c #7f2648",
-"akE c #7f2649",
-"aeP c #7f2749",
-"aKW c #7f274a",
-"bqj c #7f274b",
-"aO9 c #7f284b",
-"#nd c #7f354f",
-"aYJ c #7f3751",
-"#iQ c #7f3852",
-"#7k c #7f4976",
-"a#D c #7f4b77",
-".P6 c #7f5362",
-"aKk c #7f5666",
-"av5 c #7f5967",
-"a0J c #7f6087",
-"aF6 c #7f6169",
-"aA5 c #7f6280",
-"aI1 c #7f666b",
-".Ox c #7f676b",
-"#7i c #7f6791",
-"bos c #7f686e",
-"bCP c #7f696d",
-"bvY c #7f6b90",
-".dA c #7f7f7f",
-"bPD c #7f8081",
-"b.n c #7f80a0",
-".D1 c #7f8385",
-"aib c #7f868c",
-"afe c #7f878b",
-".ZG c #7f878c",
-"bjZ c #7f878e",
-".EE c #7f898e",
-".Eb c #7f8a8f",
-"aV# c #7f8b92",
-"bsh c #7f8b93",
-"b.5 c #7f8c94",
-"#k6 c #7f8d95",
-".L3 c #7f8e96",
-".L2 c #7f8e97",
-".LS c #7f8f97",
-".Tr c #7f8f98",
-"b.2 c #7f909a",
-"a9Q c #7f99af",
-"#Bp c #7fa8bc",
-"a7l c #7fa8bd",
-".vM c #7fc8e3",
-".v. c #7fcfe8",
-".sM c #7fddf2",
-".tp c #7ff8f7",
-".wb c #7ffff3",
-"aLQ c #801e41",
-"byy c #801f41",
-"bDe c #801f42",
-"al4 c #802043",
-"aeg c #802044",
-"ajE c #802144",
-"aQx c #802145",
-"atJ c #802244",
-"ab# c #802245",
-"a2g c #802246",
-"bq2 c #802345",
-"afQ c #802346",
-"#ri c #802347",
-".1L c #802447",
-"avC c #802448",
-"#aX c #802547",
-".2V c #802548",
-"#sV c #802549",
-"axu c #802648",
-"bko c #802649",
-"aMD c #80274a",
-"aOP c #80284d",
-"aH7 c #80294b",
-"#zX c #802b50",
-"ag6 c #802f54",
-"aLX c #803c57",
-".Bv c #804754",
-".BU c #804955",
-".Bw c #804956",
-"atV c #805363",
-"#gR c #805665",
-"bbJ c #805969",
-"bbK c #80596a",
-"bec c #805c6d",
-"a.o c #806483",
-".R8 c #80676b",
-".Q. c #80686e",
-"aqE c #80696f",
-"QtW c #808080",
-"bNY c #808081",
-".A0 c #808183",
-".PC c #80888c",
-"aXi c #808990",
-".Eq c #808a8f",
-"a6w c #808d94",
-"aR7 c #808d95",
-"baK c #808f96",
-"a3n c #808f97",
-".NS c #808f98",
-".NJ c #809098",
-".3P c #809099",
-"b#z c #80919b",
-"a8i c #809ab1",
-".D6 c #80a6b8",
-"#za c #80a9be",
-".yc c #80ac83",
-".qN c #80c1ef",
-".r5 c #80f4fc",
-".u5 c #80fef4",
-"Qt9 c #811381",
-"aHD c #811f42",
-"aeo c #812143",
-"afP c #812144",
-"a0x c #812145",
-"aep c #812244",
-"#vI c #812245",
-"#sc c #812345",
-"#rj c #812346",
-"aI# c #812347",
-"aTp c #812348",
-"ari c #812446",
-"#cD c #812447",
-".2W c #812448",
-"#.A c #812547",
-".2U c #812548",
-"#mA c #812549",
-"bwU c #812648",
-"bmo c #812b4b",
-"a8K c #81354f",
-".6y c #813953",
-".c8 c #814c84",
-"#A0 c #815463",
-".W2 c #815666",
-"bfA c #815a66",
-"#96 c #81638d",
-".oX c #817d77",
-".gt c #818181",
-"bOb c #818282",
-"bLU c #818386",
-"bSj c #818587",
-"#.L c #81878a",
-"#NO c #81898e",
-"ab2 c #81898f",
-".Ec c #818a90",
-"#6I c #818a92",
-".Ed c #818b91",
-"bjW c #818b92",
-"bp# c #818e96",
-"#xf c #818f97",
-"a67 c #818fb6",
-"#.m c #819098",
-".Pm c #819099",
-".NK c #819199",
-".YI c #81919a",
-"b#y c #81929b",
-"aZS c #81949d",
-"#k4 c #8195a0",
-"bdo c #81a7bb",
-"#pr c #81aabf",
-".7s c #81bbdd",
-"#Mq c #81c4eb",
-".t4 c #81e5f0",
-".tr c #81e5f2",
-".tq c #81f0f4",
-".sK c #81f4f8",
-".x4 c #81f7e6",
-".t2 c #81faf5",
-".xE c #81fff1",
-"aeh c #821f42",
-"#wk c #822043",
-"#wV c #822143",
-"#x3 c #822144",
-"#rk c #822244",
-"#sa c #822245",
-"a3K c #822247",
-"aex c #822345",
-"apC c #822346",
-"af7 c #822347",
-"aDU c #822348",
-"#Zs c #822446",
-"#qz c #822447",
-".5d c #822448",
-"aJN c #822449",
-"bnx c #822547",
-".1O c #822548",
-"a.I c #822549",
-"abO c #822649",
-"ahf c #82274a",
-"#i2 c #823550",
-"aQu c #823a54",
-"alb c #82485f",
-".cv c #824883",
-"bdd c #825464",
-"#g1 c #825767",
-"any c #826a71",
-"bAQ c #826b90",
-".pI c #827e78",
-".fE c #828282",
-"bSo c #828283",
-"aHN c #828383",
-"bQE c #828587",
-".uR c #82882c",
-".Cv c #82888c",
-"aoc c #82888f",
-"#ua c #82898e",
-".Ee c #828c91",
-"#vf c #828c92",
-"#V0 c #828c93",
-"a1o c #828c94",
-".Ba c #828d92",
-".2f c #828d93",
-"an3 c #828d94",
-"a2n c #828d95",
-"atx c #828e95",
-"#H0 c #829098",
-"bcX c #829197",
-".L6 c #829199",
-".QA c #82919a",
-".NL c #82929a",
-"arK c #82929b",
-"a89 c #82939d",
-"#jq c #8298a4",
-"a88 c #829cb1",
-"#lm c #82a9bc",
-"#Pz c #82abc0",
-"a4j c #82acc5",
-"#Ui c #82bee4",
-".qO c #82c0ee",
-".qM c #82c5f1",
-".sL c #82eaf6",
-".p0 c #82eaff",
-".wH c #82fff1",
-"bxu c #831f41",
-"#sb c #832043",
-"acS c #832143",
-"ain c #832244",
-"#rl c #832245",
-"aXF c #832246",
-"a2. c #832247",
-"#Zr c #832345",
-"#1N c #832346",
-"#5p c #832347",
-"#7O c #832348",
-"#4f c #832446",
-"#1O c #832447",
-".5e c #832448",
-"#nn c #832449",
-".0E c #832548",
-"axv c #832549",
-"acR c #832649",
-"afN c #832b4f",
-"bm0 c #832c4c",
-"a2Y c #83304e",
-"aLu c #833a54",
-".cf c #833f7d",
-"buj c #834a76",
-".dN c #835287",
-".8V c #835766",
-"akZ c #835867",
-"amf c #835968",
-"bIB c #836381",
-"a2t c #83658a",
-"awJ c #83696f",
-".op c #83724d",
-"#Ia c #837278",
-".bg c #838383",
-"acp c #838488",
-"bSm c #838688",
-"bKn c #838788",
-"bQz c #838789",
-".12 c #838a8e",
-"#j1 c #838a8f",
-"#3m c #838a91",
-".3X c #838b8f",
-"bkF c #838b91",
-".68 c #838c91",
-"bjY c #838c92",
-"#Pe c #838c93",
-"a3# c #838d95",
-"#f8 c #839098",
-"#k7 c #839198",
-"b.c c #839199",
-".Sw c #83929a",
-".3C c #83929b",
-".NM c #83939b",
-"ahE c #83939c",
-"ba. c #83939d",
-"#Ai c #83959f",
-"#kd c #8399a5",
-"#ea c #839aa7",
-"#Bl c #83a9bc",
-"#Am c #83acc1",
-".HV c #83aec5",
-".rr c #83d1f3",
-".r8 c #83dcf4",
-".qF c #83eefe",
-".vF c #83fef2",
-"ajD c #841d3e",
-"bIn c #841f41",
-"aME c #842043",
-"bzx c #842244",
-"aCr c #842245",
-"#Zq c #842345",
-"#0x c #842346",
-"awO c #842347",
-"aWg c #842348",
-"#rm c #842447",
-".2X c #842448",
-"#o7 c #842449",
-".1N c #842548",
-".7J c #842549",
-"aeQ c #842649",
-"bk7 c #84284a",
-"a3R c #84294a",
-"a25 c #843352",
-"#wb c #843550",
-"aN9 c #843651",
-"aXX c #843a54",
-"#wi c #843b54",
-"aGu c #843d58",
-".BT c #844b57",
-".B7 c #84575c",
-"a#3 c #845766",
-"#Hx c #845868",
-"a#x c #845981",
-"aBK c #846f91",
-"#KH c #8483a5",
-".pb c #848484",
-"bKr c #848789",
-"ad4 c #848b91",
-"arS c #848b92",
-"ah5 c #848c90",
-"ar4 c #848c92",
-"bsj c #848d94",
-"aVa c #848e94",
-".F6 c #848f95",
-"bIK c #849098",
-".D4 c #849198",
-"bow c #849199",
-"bbr c #84939a",
-".Pv c #84939b",
-".3p c #84939c",
-".NN c #84949b",
-".Pn c #84949c",
-".16 c #84949d",
-"bdI c #84959d",
-"bhE c #8498a6",
-"#iy c #849ba8",
-"bxa c #849cb4",
-"bCD c #84a6ce",
-"#9m c #84a9bc",
-"b.M c #84afc2",
-".tJ c #84b6cb",
-".vV c #84bcd1",
-".qL c #84caf2",
-".rn c #84f1fd",
-".t3 c #84f3f3",
-".uA c #84fbf4",
-"#zm c #852144",
-".#v c #852186",
-"afO c #852244",
-"aSt c #852246",
-"##h c #852348",
-"aDa c #852447",
-".1P c #852448",
-"bnA c #852449",
-"aew c #852547",
-".82 c #852548",
-".1M c #852549",
-"aqO c #852649",
-"ahi c #85274a",
-"aWs c #853651",
-"#sF c #853752",
-"aR3 c #853b55",
-"aJ# c #853b56",
-"aOB c #85425c",
-"aJv c #854767",
-"by1 c #854960",
-".Du c #854d5a",
-".Bk c #855660",
-"#gY c #855868",
-"amo c #855969",
-".Bu c #855f67",
-"byD c #85658f",
-".hf c #8570a1",
-".Bb c #85747a",
-"a0S c #857793",
-".bf c #858585",
-"bN2 c #858586",
-"bPk c #858789",
-"bKy c #85888a",
-"#YT c #858a8f",
-"ad0 c #858b90",
-".5L c #858c91",
-"#II c #858d91",
-"apc c #858d93",
-"bWn c #858f96",
-"#kU c #858f97",
-"aPr c #859096",
-"buW c #859299",
-"bpg c #85929a",
-"#zK c #85939a",
-"b.6 c #85939c",
-".Kd c #85949b",
-".NO c #85949c",
-".WF c #85949d",
-".NP c #85959c",
-".NR c #85959d",
-"#YN c #85959e",
-"aY2 c #85969f",
-"baH c #8599a7",
-"#oB c #85a7ba",
-"#Bo c #85aec3",
-".WX c #85b2ca",
-".qP c #85c1ed",
-".r6 c #85f1fa",
-".xa c #85ffef",
-".## c #861386",
-"aPO c #862245",
-"ab. c #862345",
-"aJP c #862346",
-"aJO c #862347",
-"a4p c #862348",
-"bFq c #862447",
-"a7Z c #862448",
-"aJu c #862546",
-"a7Y c #862549",
-"aG6 c #862649",
-"aG8 c #86264a",
-"a1j c #86274a",
-"aOI c #86274b",
-"bm4 c #863752",
-"a2h c #863857",
-"a.# c #864974",
-"#6d c #864a75",
-"aLo c #865764",
-"ao0 c #865c6d",
-"aZY c #866086",
-"bJJ c #866482",
-".Lc c #86696d",
-"bRp c #866c91",
-"bCT c #866d73",
-".#h c #868686",
-".Aw c #86888a",
-"#ao c #868d92",
-"ahH c #868d93",
-"aRe c #868d94",
-"#9t c #868e92",
-"#PX c #868e93",
-"aci c #869097",
-"aax c #869198",
-"#k8 c #86949b",
-"#j4 c #86949c",
-".Ts c #86949d",
-"#WR c #86959c",
-".NQ c #86959d",
-".4A c #86959e",
-"bln c #86959f",
-".Uv c #86969d",
-".17 c #86969e",
-"a5P c #8697a0",
-"#k. c #869fad",
-"#fi c #86a0ad",
-"#fB c #86a8ba",
-"#Mg c #86aabd",
-".XX c #86b6d1",
-".uD c #86e1ec",
-".r7 c #86e8f7",
-".uB c #86f4f1",
-".#. c #871387",
-"aPP c #872244",
-"aCB c #872347",
-"ayq c #872348",
-"ac3 c #872446",
-"#qA c #872448",
-"aaX c #872548",
-"#aW c #872549",
-"bqu c #872649",
-"aPg c #87274a",
-"aer c #87284b",
-"bm1 c #872e4e",
-"#jS c #873651",
-"aEP c #873955",
-"brj c #873956",
-"aa8 c #87395e",
-"a1m c #873c5a",
-"a3S c #87496a",
-"a0R c #875462",
-".MS c #875464",
-"ate c #875766",
-"#pI c #875867",
-".et c #87588c",
-".hS c #875e6f",
-"#EW c #875f6b",
-"bL# c #876f75",
-".fJ c #878787",
-"aM. c #87898b",
-"bSk c #878a8b",
-".Th c #878e92",
-"#RA c #878e93",
-"aaS c #878e94",
-"ab1 c #878e95",
-"#Vm c #878f95",
-".EF c #879094",
-"#3n c #879096",
-"ara c #879199",
-"b#k c #879299",
-".5H c #879399",
-"#e1 c #87949c",
-".If c #87959b",
-"#ei c #87959d",
-"aRf c #87959e",
-"a3p c #87969d",
-".Po c #87969e",
-".6T c #87969f",
-"blo c #8796a0",
-".3D c #87979e",
-"aXx c #87979f",
-"aVd c #8797a0",
-"aSS c #8798a0",
-"bAY c #879eb5",
-".1v c #87acbe",
-".pB c #87b4e6",
-".vL c #87d4e4",
-".rq c #87dcf6",
-".ph c #87e7ff",
-".uC c #87ebee",
-".u6 c #87fbf2",
-".wc c #87fef0",
-"bPJ c #882244",
-"bHg c #882347",
-"bFp c #882448",
-"byz c #882548",
-"bwV c #882549",
-"a7X c #88264a",
-"bl# c #88274a",
-"apG c #88274b",
-"aeR c #88284b",
-"arj c #88284c",
-"#gU c #883652",
-"aIM c #883753",
-"aXR c #883a55",
-"aWx c #883b57",
-"bku c #883d59",
-"#Ek c #885a6a",
-"bw5 c #886c90",
-"#Fy c #887279",
-"#5a c #88829d",
-".bk c #888888",
-"bUV c #888b8c",
-"#Bi c #888e92",
-".Zx c #888f92",
-".0P c #888f93",
-"#LQ c #889094",
-"#hI c #889096",
-"bDM c #889097",
-".ED c #889195",
-"a3z c #889298",
-".04 c #88939a",
-"#C4 c #88949c",
-"baI c #88969c",
-"atr c #88969e",
-"#qh c #88969f",
-".Sx c #88979e",
-".Pp c #88979f",
-".9A c #8897a0",
-".Pq c #88989f",
-"##A c #8898a0",
-"aY# c #889aa3",
-"bd9 c #88afc2",
-"ai5 c #88b3c9",
-".p9 c #88c5f2",
-"#W. c #88ccf1",
-".p1 c #88eaff",
-".xF c #88ffee",
-"abk c #892547",
-"bbQ c #892549",
-"bnB c #89264a",
-"#sS c #89284b",
-"as3 c #89284c",
-"bl. c #89294c",
-"af6 c #892b4d",
-"bsI c #892e53",
-"ahg c #893050",
-"#wL c #893752",
-"asi c #893c57",
-"aV9 c #894b75",
-".BV c #89595f",
-"bK7 c #896685",
-"#8t c #896a6d",
-".V3 c #896e74",
-"apP c #896f75",
-"QtY c #898989",
-".CV c #898b8c",
-"bTv c #898c8d",
-"#oq c #899094",
-"#Rm c #899197",
-"att c #899298",
-"adU c #89939a",
-"auH c #89939b",
-"#Qs c #899499",
-"aff c #89949a",
-"#rH c #89949b",
-"#nO c #89979d",
-".J3 c #89979e",
-"#pg c #89979f",
-".Pu c #89989f",
-".Pf c #8998a0",
-".Tm c #8998a1",
-".Pt c #8999a0",
-"#ay c #8999a1",
-"a4X c #8999a3",
-".4M c #899da6",
-"#lW c #89a6b5",
-"bc6 c #89aec1",
-".FU c #89afc3",
-"#Bm c #89b2c7",
-"#Bn c #89b3c9",
-"#Br c #89b5ca",
-"au8 c #89b5cb",
-".q. c #89c4f0",
-".p8 c #89c9f3",
-".qK c #89d3f5",
-".ow c #89daf6",
-".qG c #89edfe",
-".wI c #89ffee",
-"boU c #8a264a",
-"a0w c #8a274b",
-"aIa c #8a284b",
-"#pR c #8a284c",
-"aP9 c #8a294c",
-"aeq c #8a294d",
-"#s# c #8a3352",
-"#x0 c #8a3b56",
-"aPi c #8a3c57",
-"aK0 c #8a3d59",
-"bik c #8a4460",
-"bdf c #8a5868",
-"#fP c #8a5a6a",
-"#DU c #8a5b6b",
-"anO c #8a5d6e",
-".fb c #8a5e90",
-"a.x c #8a6583",
-".Jw c #8a676d",
-".Bl c #8a6971",
-"aat c #8a6f6f",
-".bN c #8a8a8a",
-"bUY c #8a8a8b",
-"bPn c #8a8b8b",
-".9c c #8a9195",
-"ab5 c #8a9197",
-"#UR c #8a9297",
-"aE. c #8a939a",
-"ajq c #8a949a",
-"b#U c #8a949c",
-".3s c #8a969c",
-"bdT c #8a969d",
-"#AH c #8a969e",
-"#nP c #8a979e",
-".3n c #8a979f",
-".Kg c #8a989f",
-".8d c #8a98a0",
-"#k9 c #8a999f",
-".Pr c #8a99a0",
-".Ps c #8a99a1",
-".18 c #8a99a2",
-"#oO c #8a9aa0",
-".QE c #8a9aa1",
-"#U0 c #8a9aa2",
-"#Gf c #8aa8b9",
-"#6E c #8aaaba",
-".q5 c #8aacc4",
-"#Bq c #8ab5cb",
-"#AV c #8ab6cb",
-".YW c #8ab6d4",
-".ss c #8ab8d0",
-".qU c #8abde7",
-"#ZZ c #8ac0e2",
-".vK c #8adde6",
-".u9 c #8ae6ea",
-".rp c #8ae8f9",
-".ro c #8aeffb",
-".x5 c #8afbe8",
-".vG c #8afdf0",
-"af8 c #8b274b",
-"a57 c #8b294c",
-"aqv c #8b294d",
-"acI c #8b2d4f",
-"abN c #8b2f50",
-"bHc c #8b365b",
-"bcR c #8b4874",
-".ct c #8b4b94",
-".K3 c #8b5665",
-"#Bw c #8b5868",
-"ars c #8b596a",
-".6d c #8b5a6a",
-"#GF c #8b5c6c",
-"am. c #8b5f6d",
-"aGO c #8b6e73",
-"ad6 c #8b7278",
-".nI c #8b8b8b",
-"bNQ c #8b8e90",
-".4n c #8b9295",
-".40 c #8b9296",
-"#rE c #8b9297",
-"#S7 c #8b949b",
-"aOe c #8b969c",
-".GL c #8b969d",
-"aUm c #8b969e",
-"#y6 c #8b989e",
-"#l. c #8b99a0",
-"#gp c #8b99a1",
-"aai c #8b99a2",
-".QB c #8b9aa1",
-".QC c #8b9aa2",
-".3q c #8b9aa3",
-".Tn c #8b9ba2",
-"#ZD c #8b9ba3",
-"bi7 c #8ba1af",
-"#k3 c #8ba7b6",
-"#AM c #8bacbd",
-"#vr c #8bafc1",
-"#HU c #8bafc2",
-"#.j c #8bb0c3",
-"bcH c #8bb6ca",
-".GB c #8bb7cc",
-".s7 c #8bb9cb",
-".YY c #8bbcd6",
-".qQ c #8bc5ed",
-".u7 c #8bf7f0",
-"aa9 c #8c284b",
-"aG5 c #8c284c",
-"a7U c #8c294c",
-"arC c #8c294d",
-"#sT c #8c2a4d",
-"aPI c #8c2a4e",
-"aJq c #8c2d4f",
-"aRX c #8c3051",
-"#iT c #8c3753",
-"aIT c #8c3b57",
-"#ai c #8c4872",
-"#r4 c #8c5869",
-".MU c #8c636f",
-".Jk c #8c6c6f",
-"aEp c #8c6f73",
-".lu c #8c7063",
-"#Xk c #8c7a80",
-".gu c #8c8c8c",
-"bQA c #8c8f90",
-"ad9 c #8c9297",
-".4G c #8c9397",
-"aSP c #8c9398",
-"#hr c #8c969d",
-".Ge c #8c979d",
-"adC c #8c979e",
-"aTB c #8c979f",
-".Gw c #8c989e",
-"bqI c #8c98a0",
-"#l# c #8c99a0",
-"#Fn c #8c99a1",
-"#la c #8c9aa0",
-"#bz c #8c9aa1",
-"#.Q c #8c9aa2",
-"#nW c #8c9ba1",
-".QD c #8c9ba2",
-".RF c #8c9ba3",
-"#nB c #8c9ba4",
-"aZ4 c #8c9ca2",
-".Sy c #8c9ca3",
-"aWG c #8c9ca4",
-".D8 c #8c9fad",
-"#kY c #8cabba",
-"#Si c #8caed2",
-"#Nm c #8cb2d5",
-"aab c #8cb4c8",
-"bbT c #8cb8cd",
-".qg c #8cbae6",
-"#Ud c #8ccbef",
-".p7 c #8ccef5",
-".pi c #8ce6ff",
-".xb c #8cffec",
-"aiq c #8d274c",
-"#sU c #8d2a4d",
-"bnE c #8d3854",
-"aJ9 c #8d3c57",
-".Dh c #8d4e5c",
-"a4D c #8d4e6d",
-".Di c #8d505e",
-"a0y c #8d536d",
-"#Dh c #8d5a6a",
-"bel c #8d6485",
-".Hk c #8d6b6e",
-"awH c #8d7075",
-".2P c #8d7176",
-".eZ c #8d8d8d",
-"bO. c #8d8e8e",
-".AZ c #8d8f90",
-"bKm c #8d9091",
-".Gl c #8d9295",
-"#HR c #8d9398",
-".Zy c #8d9497",
-".ZF c #8d9498",
-"ave c #8d949a",
-"adJ c #8d959b",
-"#Wq c #8d969c",
-".Gi c #8d989e",
-".F2 c #8d989f",
-"bpf c #8d9aa1",
-"#d8 c #8d9aa2",
-".RE c #8d9ba2",
-"#Gj c #8d9ba3",
-"#n. c #8d9ba4",
-"#kf c #8d9ca2",
-".RD c #8d9ca3",
-"##H c #8d9ca4",
-".19 c #8d9ca5",
-"bLg c #8d9da3",
-".To c #8d9da4",
-".qp c #8d9daa",
-"#Ql c #8d9ea5",
-"#mR c #8da3af",
-".Tz c #8daab9",
-"#go c #8dacbc",
-".FV c #8db9cd",
-".8Q c #8dc5e4",
-".q# c #8dc5f0",
-".wo c #8dc9d4",
-"#V9 c #8dc9ec",
-".p2 c #8de9fe",
-".wd c #8dfeee",
-"boT c #8e294d",
-"aG9 c #8e2a4d",
-"#uN c #8e2a4e",
-"akG c #8e2b4e",
-"ac2 c #8e2e52",
-"aR. c #8e3c57",
-"atM c #8e3d58",
-"aQy c #8e465f",
-".Dv c #8e505e",
-"aB8 c #8e5a69",
-".fZ c #8e6596",
-"bJI c #8e6c8e",
-".S1 c #8e7074",
-".Qi c #8e7075",
-"afl c #8e7c86",
-"amT c #8e8082",
-"b.V c #8e86a2",
-".co c #8e8e8e",
-"bPr c #8e8f8f",
-".ET c #8e9498",
-"#3f c #8e969b",
-"agI c #8e989e",
-"alI c #8e989f",
-".F4 c #8e999f",
-"a0C c #8e99a0",
-"aMd c #8e9aa1",
-"#1T c #8e9ba2",
-"#WT c #8e9ca2",
-".QG c #8e9ca3",
-"#.R c #8e9ca4",
-".6W c #8e9ca5",
-".Tp c #8e9da4",
-".4S c #8e9da5",
-"#rW c #8e9ea5",
-"#m2 c #8e9ea6",
-"#y5 c #8ea6b1",
-"#l4 c #8ea8b4",
-".EI c #8eaab7",
-"#l3 c #8eaab8",
-"awS c #8eb5c8",
-".Z6 c #8ebcd3",
-".wn c #8eccd6",
-".vJ c #8ee8e8",
-".qH c #8eecfd",
-".u8 c #8ef3ed",
-"a7V c #8f2a4e",
-"#vH c #8f2b4e",
-"bk9 c #8f2c4f",
-"abI c #8f3354",
-"aOa c #8f3454",
-"as6 c #8f3d58",
-"aGt c #8f3d5a",
-"bm8 c #8f3d5b",
-"bhw c #8f4560",
-".0i c #8f5c6d",
-"a1u c #8f6084",
-"bUe c #8f6683",
-"a.Z c #8f7070",
-".Q9 c #8f7177",
-"bIF c #8f7379",
-".nF c #8f7a59",
-"a3w c #8f87aa",
-".j6 c #8f8f8f",
-".A7 c #8f9091",
-"bNP c #8f9092",
-"bO9 c #8f9192",
-"#Ac c #8f959a",
-"akr c #8f969b",
-"ah1 c #8f989f",
-"#0U c #8f999f",
-"aKa c #8f99a0",
-".F3 c #8f9aa0",
-"a6e c #8f9aa1",
-".6Y c #8f9ba1",
-"#gg c #8f9ca3",
-"#gt c #8f9ca4",
-"b#8 c #8f9da3",
-"##I c #8f9da4",
-"am7 c #8f9da5",
-"##E c #8f9da6",
-"#qg c #8f9ea4",
-".Ut c #8f9ea5",
-".8b c #8f9ea6",
-"#mc c #8f9fa7",
-"#kg c #8fa0a8",
-"#xK c #8fa4af",
-"a4W c #8fa4b5",
-"#l6 c #8fa5af",
-"#l5 c #8fa7b3",
-"#Ah c #8fa9b6",
-"#c6 c #8fb1c3",
-"auF c #8fbcd2",
-"a9A c #8fbcd3",
-"#sE c #8fbed5",
-".wp c #8fc9d1",
-".wm c #8fd0d8",
-".wl c #8fd3da",
-".qJ c #8fe0f8",
-".vH c #8ffbee",
-".xG c #8fffea",
-"a7W c #902a4e",
-"bMA c #902b4e",
-"aFQ c #902b4f",
-"ark c #902c4f",
-"aTR c #902e51",
-"#q8 c #903855",
-"#lp c #903956",
-"aUI c #903c58",
-"aJ. c #903c59",
-"bRm c #904771",
-"a2i c #905c74",
-"akY c #905d6e",
-"#Hv c #905e6f",
-"bw2 c #905e84",
-".Bx c #906266",
-"bHx c #906683",
-"bDB c #906d90",
-"bmS c #907176",
-"ayR c #907277",
-"aGV c #907278",
-".bJ c #909090",
-"bNX c #909191",
-".AE c #909293",
-"bQw c #909294",
-".CN c #909496",
-".XZ c #909599",
-".6M c #90969a",
-"#SC c #90969b",
-"ab0 c #90989d",
-"bbe c #909aa1",
-".F5 c #909ba1",
-".H7 c #909da3",
-"bp. c #909da4",
-"#pk c #909da5",
-"#gu c #909ea5",
-"#c2 c #909ea6",
-"#qO c #909ea7",
-"biu c #909fa5",
-".VC c #909fa6",
-".Vz c #909fa7",
-"a61 c #909fa8",
-"#ki c #90a1a9",
-"#lb c #90a4ad",
-"#Kv c #90afbf",
-"#ff c #90b3c4",
-"awi c #90bdd4",
-".uh c #90c0c2",
-".pr c #90c6f2",
-".pq c #90c9f4",
-".wJ c #90feec",
-"aVH c #912a4e",
-"aFR c #912b4f",
-"aeU c #912c4f",
-"afR c #912c50",
-"bk8 c #912f51",
-".aB c #91338e",
-"auU c #91345a",
-"#og c #913555",
-"#mu c #913855",
-"a8J c #913955",
-"bxC c #913b5e",
-"aFb c #913d5a",
-"#8Q c #913e62",
-"arr c #91445e",
-"ama c #915367",
-"aDh c #915b6b",
-"#FP c #915c6c",
-"av4 c #915d6d",
-"#ez c #915d6e",
-"baY c #915e6e",
-"a26 c #915f77",
-"aY7 c #915f83",
-".GS c #916e6f",
-"#KQ c #917a7f",
-"bDu c #918db5",
-".lw c #919191",
-"bPe c #919495",
-".5x c #91979b",
-"#mJ c #91989d",
-"bot c #919aa1",
-"a8p c #919b9f",
-".GM c #919ba1",
-"#is c #919ba2",
-"#v0 c #919ca2",
-".2e c #919da3",
-"bx. c #919da4",
-".5P c #919ea5",
-"#cb c #919fa6",
-".2A c #919fa7",
-"#Cy c #919fa8",
-"#lk c #91a0a6",
-".3N c #91a0a7",
-"#.N c #91a0a8",
-"bi9 c #91a0a9",
-"#lf c #91a5af",
-"#kh c #91a6b0",
-"#pw c #91a9b5",
-"#l2 c #91b1c1",
-".IE c #91c2d9",
-".qR c #91c8eb",
-".wk c #91d7dc",
-".p6 c #91d8f8",
-".ox c #91e2ff",
-".qI c #91e8fb",
-".x6 c #91fde6",
-"bV7 c #922a4d",
-"aj5 c #922a4e",
-"bG5 c #922b4e",
-"aS3 c #922b4f",
-"aFS c #922b50",
-"agg c #922c4f",
-"aeS c #922c50",
-"aiv c #923052",
-"aFO c #923153",
-"air c #923354",
-"aYM c #923855",
-"aQ4 c #923858",
-"#jI c #923a58",
-"aF3 c #923c59",
-"aeH c #924962",
-"aVj c #924a73",
-"#rd c #925b6b",
-"beK c #925c6c",
-"bE9 c #925c84",
-"#aQ c #925d6d",
-"ame c #925f6f",
-"aEg c #92797e",
-".Bm c #927f84",
-"#Mz c #9284a4",
-".fI c #929292",
-".A6 c #929393",
-".Bt c #929594",
-"aft c #92989d",
-".Uo c #92999c",
-"#To c #929ba0",
-"btc c #929da4",
-"aRg c #929ea4",
-"#02 c #929ea5",
-"bv2 c #929ea6",
-"aNd c #929fa6",
-"#w7 c #929fa7",
-".XG c #92a0a7",
-".7n c #92a0a8",
-".1q c #92a1a8",
-".NF c #92a1a9",
-".wv c #92a354",
-"b#7 c #92a3ae",
-"#zJ c #92acb8",
-"#jo c #92b5c7",
-"#xL c #92b6c9",
-"b#h c #92c0d7",
-".0a c #92c1da",
-".pp c #92ccf5",
-".pj c #92e6ff",
-".p3 c #92e8fe",
-".vI c #92f6eb",
-".xc c #92ffe9",
-"aiw c #932c50",
-"bxv c #932d50",
-"aFP c #932f52",
-"aPA c #934365",
-"a#m c #935a69",
-"bbH c #935c6c",
-"bid c #936072",
-"aaV c #93636e",
-"aBL c #93657c",
-"#fY c #936885",
-"bMS c #936b8a",
-".gM c #936c9c",
-"aie c #938992",
-"aGH c #9389a6",
-".ek c #939393",
-".Bs c #939695",
-".CW c #939698",
-".6# c #939a9e",
-"avQ c #939a9f",
-"#0K c #939ba1",
-"#lg c #939da3",
-"#zE c #939da4",
-"#5P c #939ea4",
-".03 c #939fa5",
-".YK c #93a0a7",
-"#sw c #93a0a8",
-"bj# c #93a1a7",
-"#hF c #93a1a8",
-".RG c #93a1a9",
-"#rU c #93a2a8",
-".1p c #93a2a9",
-".6S c #93a2aa",
-"a5x c #93a3ad",
-"#mb c #93a7b0",
-"#lc c #93aab4",
-"#l7 c #93acb9",
-"#xM c #93b6c8",
-".HT c #93b7c8",
-"avG c #93c1d7",
-"#zN c #93c1d8",
-".qT c #93c6e8",
-".we c #93fdeb",
-"#tJ c #942d50",
-"akH c #942d51",
-"aba c #942e51",
-"a8D c #942e52",
-"abj c #943155",
-"#pP c #943856",
-"aiT c #943858",
-"adq c #943958",
-"#8G c #94456e",
-"bJG c #944670",
-"aNj c #945968",
-"#FM c #945e6f",
-"btN c #94616f",
-".BW c #946b6b",
-"bOy c #946e8e",
-".M2 c #947479",
-".mc c #947964",
-"a7w c #9488a2",
-"aVf c #948a92",
-".hX c #949494",
-"#0R c #94989d",
-".EU c #94999c",
-"#18 c #94999f",
-".Zz c #949a9d",
-".3z c #949b9f",
-"a6R c #949da2",
-"#q5 c #949ea4",
-"asE c #949ea5",
-".Gv c #949fa4",
-".4C c #94a0a7",
-"#72 c #94a1a8",
-".15 c #94a1a9",
-".3M c #94a2a9",
-"##B c #94a2aa",
-"bkD c #94a2ab",
-"bgT c #94a3a9",
-".L5 c #94a3aa",
-"#VV c #94a3ab",
-"#ZB c #94a4ab",
-"bfn c #94a5ab",
-"aUi c #94a5af",
-"#le c #94afbb",
-"#xN c #94b7c9",
-"#kc c #94b9cc",
-"#kq c #94bcd0",
-".SL c #94bfdc",
-"#Ra c #94c6e8",
-".ps c #94c7f2",
-"aEV c #952c50",
-"ahj c #952d51",
-"ap3 c #952e51",
-"afE c #953553",
-"aeW c #953958",
-"abM c #953a59",
-"arq c #953c59",
-"blf c #95405e",
-"byC c #955981",
-"aAS c #955d6d",
-"#n9 c #955e6f",
-"#eK c #956070",
-".BX c #956a6b",
-"aI2 c #957478",
-"aoN c #95757b",
-".mW c #957c61",
-"bgg c #9586a3",
-".Bn c #95888b",
-".Bo c #958b8e",
-".aK c #959595",
-"bP. c #959798",
-"bQx c #959799",
-"bSc c #959899",
-"ac# c #959ba0",
-"a.L c #959da1",
-"#Sx c #959da2",
-"#m6 c #959ea4",
-"#VS c #959fa5",
-"#j7 c #959fa6",
-"a1T c #95a0a5",
-".9g c #95a1a8",
-"bpY c #95a1a9",
-"#qS c #95a2a9",
-"#ph c #95a2aa",
-"#WO c #95a2ab",
-".5N c #95a3aa",
-".L7 c #95a3ab",
-"baJ c #95a3ac",
-".1e c #95a4ab",
-".9j c #95a4ac",
-"#ld c #95aebb",
-"#l8 c #95b1bf",
-"#Aj c #95bacc",
-".JT c #95bdd2",
-"#ps c #95c3db",
-"#U8 c #95c9e9",
-".qS c #95c9ea",
-".qa c #95caef",
-".po c #95d3f7",
-".nN c #95dafb",
-".p5 c #95e0fa",
-".p4 c #95e5fc",
-".xH c #95ffe6",
-"bsH c #962d50",
-"aFT c #962d51",
-"aqw c #962e51",
-"aeT c #962e52",
-"aio c #962f52",
-"aRx c #963d5a",
-"bqt c #963e5b",
-".Dw c #966069",
-"bHv c #966f90",
-"bgA c #967579",
-"#h1 c #96767a",
-".Bp c #969193",
-".Bq c #969596",
-".cX c #969696",
-".Br c #969697",
-"bN3 c #969797",
-".AX c #969798",
-"#50 c #96989d",
-"#4J c #969ca0",
-".Es c #969da0",
-"#lM c #969da1",
-".7f c #969da2",
-"#bh c #969ea2",
-".vp c #969f3f",
-"#mK c #969fa5",
-"#rR c #96a1a7",
-"aRj c #96a2a8",
-".H2 c #96a2a9",
-".Iz c #96a3a9",
-".Kc c #96a3aa",
-"#j3 c #96a3ab",
-"#1V c #96a3ac",
-"#cg c #96a4aa",
-".4K c #96a4ab",
-"#c# c #96a4ac",
-".3E c #96a5ac",
-"bgd c #96a7b2",
-"a5M c #96a9b7",
-"aXc c #96b2bd",
-"#l9 c #96b4c2",
-"#xO c #96bacd",
-"#l1 c #96bcce",
-"#xP c #96bccf",
-".rN c #96becd",
-"adz c #96c3da",
-".HU c #96c6dd",
-".wj c #96e1de",
-".oy c #96e2ff",
-".pk c #96e5fe",
-".wf c #96f9e8",
-".wK c #96fee9",
-"bqv c #972d51",
-"#o5 c #972e52",
-"a2f c #972f52",
-"bnC c #973053",
-"bHb c #97375d",
-"#ku c #973957",
-"aIR c #973c5a",
-"#sQ c #973d5a",
-"#7v c #973d61",
-"bwE c #974065",
-"a#y c #974167",
-"aRp c #974a72",
-"#8E c #97567d",
-".eU c #975a87",
-"ary c #975d6e",
-"bPX c #976f90",
-".Hl c #977174",
-"aGN c #977477",
-"bz4 c #97767a",
-".rb c #978d40",
-"a3T c #9792b0",
-".aI c #979797",
-"bQZ c #979898",
-".EA c #97999a",
-"agD c #979a9f",
-".ZE c #979da0",
-"#yV c #979da1",
-"asO c #979da3",
-"aPv c #97a1a7",
-"aQe c #97a1a8",
-"aka c #97a2a8",
-"#9J c #97a2a9",
-".Ik c #97a3a9",
-"#hg c #97a3aa",
-".Il c #97a4aa",
-"#vd c #97a4ab",
-"#q2 c #97a4ac",
-"#5w c #97a4ad",
-"#tv c #97a5ab",
-".4Q c #97a5ac",
-"#c4 c #97a5ad",
-".VA c #97a6ad",
-"#ma c #97b3c0",
-"#AQ c #97bbcd",
-"#jp c #97bdd0",
-"#gl c #97c0d3",
-"#hA c #97c0d4",
-"#nZ c #97c2d8",
-"#05 c #97c7de",
-".ab c #982d96",
-"aqN c #982f52",
-"aix c #982f53",
-"a#o c #983453",
-"aJ6 c #983a58",
-"a8I c #983b58",
-"agh c #983d5b",
-"bFM c #98466f",
-"aSh c #984a72",
-".B9 c #986d6e",
-".B8 c #986e6e",
-".hs c #9873a0",
-"aFB c #987679",
-".Ri c #98767b",
-"bdF c #98808e",
-"aIf c #98839e",
-".iD c #989898",
-"bQP c #989899",
-"bSZ c #989ca1",
-"#8f c #989ea2",
-"awp c #989fa4",
-"brw c #989fa5",
-"#oy c #98a0a5",
-".Gx c #98a1a6",
-"#q4 c #98a2a8",
-".H5 c #98a4aa",
-"#yX c #98a5ac",
-"#q3 c #98a5ad",
-"#9o c #98a5ae",
-"biv c #98a6ac",
-".1o c #98a6ad",
-".9e c #98a6ae",
-"aYY c #98a6af",
-".XH c #98a7ae",
-".2. c #98a7af",
-"bnf c #98a8af",
-"#iA c #98a8b1",
-"a6q c #98abb8",
-"bb. c #98c7de",
-".vl c #98c8c1",
-"b#Q c #98c8df",
-".oG c #98c9f5",
-".oF c #98cbf6",
-".9D c #98ceeb",
-".yb c #98d2a6",
-".pn c #98d9f9",
-".x7 c #98fee4",
-"a24 c #992e52",
-"arl c #992f53",
-"aey c #993053",
-"#nc c #993a58",
-".6x c #993b5a",
-"#kH c #993d5b",
-"bCI c #99456e",
-".UR c #996071",
-"#fE c #996171",
-"baD c #996889",
-"aZK c #996c81",
-".Hn c #997375",
-".TW c #99767a",
-"aCk c #997a7d",
-".Dg c #998086",
-"biq c #998ba0",
-".iC c #999999",
-"bNV c #99999a",
-"#Pu c #999aa0",
-"arR c #999fa4",
-"#Ol c #99a0a7",
-"a4E c #99a0bc",
-"#vZ c #99a3a9",
-"#Q3 c #99a3aa",
-".FT c #99a4a9",
-".H3 c #99a5ab",
-"#H1 c #99a5ac",
-"b#x c #99a6ac",
-"#rV c #99a6ad",
-"#q1 c #99a6ae",
-"bgQ c #99a6af",
-".4R c #99a7ae",
-".Uu c #99a7af",
-"#0F c #99a7b0",
-".5G c #99a8af",
-"b.1 c #99a8b0",
-"#aL c #99adb8",
-"#nN c #99b8c8",
-"#oI c #99bbcb",
-"#m. c #99bbcc",
-"#oH c #99bdcf",
-"#k2 c #99c1d5",
-"#iw c #99c3d7",
-"#Ua c #99c6e5",
-"#IN c #99c8df",
-"#pt c #99c8e0",
-"bat c #99c9e0",
-".pm c #99defb",
-".wi c #99e9e0",
-".xd c #99fee5",
-"abd c #9a3053",
-"ajF c #9a3054",
-"bm3 c #9a3a58",
-"aVS c #9a3a59",
-".5n c #9a3b5a",
-"aQo c #9a496f",
-"aUA c #9a4971",
-"bXp c #9a657e",
-".Dj c #9a676c",
-"buR c #9a6f8f",
-"bo7 c #9a787c",
-"ayO c #9a787d",
-".C9 c #9a9094",
-".eW c #9a9a9a",
-"bL7 c #9a9a9b",
-"bR5 c #9a9b9c",
-"#QB c #9a9fa4",
-".ZA c #9aa0a2",
-"bl0 c #9aa1a6",
-"#mL c #9aa2a7",
-".F1 c #9aa4a9",
-"ar# c #9aa4aa",
-"a6Q c #9aa4ab",
-".IN c #9aa5ab",
-".H4 c #9aa6ac",
-".02 c #9aa6ad",
-"#cr c #9aa7ad",
-"#dV c #9aa7ae",
-".Sz c #9aa7af",
-"#ZC c #9aa7b0",
-".v0 c #9aa84e",
-"#fy c #9aa8ae",
-".52 c #9aa8af",
-"#fc c #9aa8b0",
-"#VW c #9aa8b1",
-"aaf c #9aa9b0",
-"a6b c #9ab2be",
-"#oJ c #9ab6c3",
-"#m# c #9abccc",
-"#lX c #9ac5da",
-"atm c #9ac9e1",
-"a6N c #9acbe3",
-".nO c #9addff",
-".pl c #9ae3fe",
-".wg c #9af6e5",
-"aH. c #9b2f53",
-"aoD c #9b3054",
-"afW c #9b3154",
-"a8H c #9b3b59",
-"aNp c #9b3c5b",
-"aIS c #9b3d5c",
-"a2j c #9b8394",
-"ah9 c #9b868c",
-"aV4 c #9b8d94",
-".AD c #9b9a9b",
-".fK c #9b9b9b",
-"bQT c #9b9b9c",
-"#X6 c #9ba0a5",
-"arZ c #9ba1a6",
-"arD c #9ba2a6",
-"aLc c #9ba5ab",
-".H6 c #9ba7ac",
-".Ie c #9ba7ad",
-"#bk c #9ba7ae",
-"#lU c #9ba7af",
-"#NL c #9ba7b1",
-"#v6 c #9ba8ae",
-"#q0 c #9ba8af",
-"ayw c #9ba8b0",
-"abW c #9ba8b1",
-"#XK c #9ba9af",
-".YJ c #9ba9b0",
-"#fd c #9ba9b1",
-"#df c #9baab1",
-"#xJ c #9bacb4",
-"#Y7 c #9bc2e0",
-".xR c #9bc58b",
-"#hy c #9bc5da",
-".1u c #9bc9e0",
-".oH c #9bc9f4",
-"#qj c #9bcbe3",
-".vW c #9bccc3",
-".oE c #9bd0f8",
-".oz c #9be1fe",
-".wh c #9bf1e2",
-".xI c #9bffe3",
-"aFU c #9c3054",
-"a0v c #9c3154",
-"aip c #9c3155",
-"aH6 c #9c3658",
-"#vs c #9c3a59",
-"#rf c #9c3b5a",
-"#hS c #9c3c5a",
-"#vG c #9c3c5b",
-"ash c #9c3e5d",
-"a#n c #9c4460",
-"bAM c #9c456d",
-"auy c #9c6071",
-"#uG c #9c6273",
-"#eu c #9c6373",
-"a.p c #9c6b86",
-"#eQ c #9c6f8f",
-"awI c #9c797d",
-"bi6 c #9c8ea1",
-".AC c #9c9c9b",
-".hd c #9c9c9c",
-".jI c #9c9d9c",
-"bR9 c #9c9e9f",
-"acg c #9c9f9f",
-".CZ c #9ca0a2",
-".ZD c #9ca2a4",
-".Vt c #9ca2a5",
-".2m c #9ca2a6",
-"aUo c #9ca2a7",
-"#Op c #9ca3c2",
-".95 c #9ca8af",
-"#iD c #9ca9b0",
-"#ti c #9ca9b1",
-".LR c #9caab0",
-".99 c #9caab1",
-".4B c #9caab2",
-"b#w c #9cabb5",
-"bWm c #9cafbf",
-"#m1 c #9cb9c7",
-"#xQ c #9cc0d2",
-"#l0 c #9cc8de",
-"#k# c #9cc9de",
-"#pu c #9ccae1",
-".JS c #9cd0e9",
-".wL c #9cfde5",
-"aIb c #9d3054",
-"bFc c #9d3155",
-"aKv c #9d3255",
-".ao c #9d389f",
-"bm2 c #9d3959",
-"bqw c #9d3a59",
-"aFN c #9d3a5b",
-".7P c #9d3c5b",
-"abJ c #9d425f",
-"alc c #9d526a",
-"aYd c #9d5d7f",
-"amd c #9d6274",
-"bzU c #9d678a",
-".en c #9d68af",
-"aXZ c #9d768a",
-"aHT c #9d777a",
-"aGM c #9d787b",
-"boL c #9d7a7f",
-".ib c #9d7ca6",
-"acC c #9d7d82",
-"aZL c #9d8b9a",
-".la c #9d9d9d",
-".EP c #9d9da7",
-".Co c #9d9e9f",
-"agE c #9da0a4",
-"#BT c #9da2a6",
-".1b c #9da3a7",
-"aq1 c #9da3a8",
-"blq c #9da3a9",
-".69 c #9da4a8",
-".Gj c #9da5aa",
-"#MO c #9da6ab",
-"asV c #9da8ad",
-"#tj c #9da8ae",
-"bLf c #9da9b0",
-"#DB c #9da9b1",
-"#md c #9daab0",
-"#rN c #9daab1",
-"#nA c #9daab2",
-".NT c #9dabb2",
-".3r c #9dabb3",
-".JR c #9db8c5",
-"bdV c #9dbccf",
-".xp c #9dbd7a",
-".pA c #9dc3e6",
-"#ix c #9dcae0",
-"#JJ c #9dcbe2",
-".pt c #9dccf1",
-"auD c #9dcde5",
-"a76 c #9dcee6",
-".1h c #9dcfe8",
-"aiS c #9e3255",
-"a#z c #9e3256",
-"boS c #9e3356",
-"a23 c #9e3959",
-"aKu c #9e3b5a",
-"a.e c #9e3e62",
-"by5 c #9e476f",
-"aSo c #9e4963",
-"#6m c #9e5471",
-"akX c #9e6374",
-".PZ c #9e797c",
-"bk# c #9e7a7d",
-"bJQ c #9e7b80",
-"a2G c #9e84a3",
-"a27 c #9e8f9d",
-".cY c #9e9e9e",
-"bN7 c #9e9e9f",
-"aGA c #9e9f9f",
-".ES c #9ea3a5",
-"alL c #9ea3a8",
-"#jg c #9ea4a8",
-"alH c #9ea5aa",
-"#oK c #9ea6ab",
-"are c #9ea8ad",
-"#Fw c #9ea8ae",
-"#ZW c #9ea9af",
-"#rM c #9eaab1",
-"#rO c #9eaab2",
-"#LP c #9eabb1",
-".Kt c #9eabb2",
-"#e# c #9eabb3",
-".J2 c #9eacb2",
-".2r c #9eacb3",
-"#Lp c #9eacb4",
-".IH c #9eb9cd",
-".Z9 c #9ec2db",
-"#Ag c #9ec6da",
-".LH c #9ec6dc",
-"#AP c #9ec8dd",
-"#kZ c #9ecce2",
-"ao8 c #9ecfe7",
-"at8 c #9ecfe8",
-".oD c #9ed5fa",
-".nP c #9eddff",
-".x8 c #9efee0",
-"aiy c #9f3256",
-"bpK c #9f3859",
-"#mm c #9f3b5a",
-"a8G c #9f3c5b",
-"aF0 c #9f3c5c",
-"abL c #9f4361",
-"aaW c #9f4862",
-"aSX c #9f4970",
-"aHc c #9f4a63",
-"#7j c #9f4e75",
-".EZ c #9f5a69",
-"#Bz c #9f6172",
-"#DS c #9f6273",
-".VY c #9f6374",
-"bro c #9f6976",
-".C. c #9f7373",
-"bFJ c #9f7399",
-"aFq c #9f7c81",
-"#7C c #9f8ca4",
-".ei c #9f9f9f",
-"bQ1 c #9fa0a0",
-"bQJ c #9fa1a2",
-"#qY c #9fa6ab",
-"#oM c #9fa8ad",
-"aFj c #9fa8af",
-"a78 c #9fa9af",
-".IM c #9faab0",
-"#zL c #9fabb1",
-".2d c #9fabb2",
-"aSO c #9fabb3",
-".KK c #9facb2",
-".JZ c #9facb3",
-".6X c #9facb4",
-".RB c #9fadb4",
-".2z c #9fadb5",
-"aRa c #9fb1bb",
-"#nK c #9fc3d4",
-".qf c #9fc9e7",
-"ahB c #9fd0e8",
-".m3 c #9fd8fd",
-".oC c #9fd9fc",
-".oA c #9fe1fe",
-".xe c #9ffee2",
-"aRW c #a03155",
-"bFb c #a03356",
-"aNq c #a03357",
-".9T c #a03c5b",
-"arp c #a03e5d",
-"aMV c #a03e5e",
-"#97 c #a04f76",
-"bvQ c #a0546c",
-".EY c #a05c6b",
-"aza c #a06273",
-"aG1 c #a06576",
-".gn c #a0678a",
-"bBH c #a07198",
-".Hm c #a07779",
-".fP c #a078b9",
-"be9 c #a08594",
-"aWL c #a08f96",
-"bjM c #a091a6",
-".lV c #a09e9f",
-".#m c #a0a0a0",
-"bPu c #a0a0a1",
-"bPE c #a0a1a1",
-".FQ c #a0a3a5",
-".WZ c #a0a5a9",
-".3c c #a0a6a9",
-"#PD c #a0a6aa",
-"#fq c #a0a7ac",
-"#Gm c #a0aaaf",
-"#jl c #a0aab0",
-"#hq c #a0aab1",
-"afj c #a0abb0",
-".Ks c #a0acb3",
-"#rP c #a0acb4",
-"bWq c #a0adb3",
-"#bm c #a0adb4",
-".Us c #a0adb5",
-".Tq c #a0aeb5",
-"#Pb c #a0aeb6",
-"bdG c #a0afb5",
-"#Uo c #a0afb6",
-"a6Y c #a0b0ba",
-"#V# c #a0c5e2",
-"#AR c #a0cadf",
-".nW c #a0cbf7",
-"#oG c #a0cce2",
-"#AN c #a0d2ea",
-".wq c #a0d5c7",
-"#.k c #a0d5ef",
-"abl c #a13256",
-"aR2 c #a13357",
-"ahb c #a13457",
-"a8F c #a13c5b",
-"#yz c #a13d5d",
-"aI9 c #a13e5d",
-"aGs c #a13e5e",
-"bnK c #a14162",
-"bSI c #a1446c",
-"#EZ c #a16475",
-".Ls c #a17b7f",
-"aZU c #a1949b",
-"a1n c #a19aa6",
-".no c #a19e9f",
-".mE c #a19fa1",
-".#j c #a1a1a1",
-".Dc c #a1a2a2",
-"bPj c #a1a2a3",
-".Df c #a1a5a4",
-".ZV c #a1a6aa",
-"#.h c #a1a7aa",
-"#Fg c #a1abb1",
-".WI c #a1acb3",
-"#pB c #a1adb3",
-".J0 c #a1adb4",
-"#aI c #a1aeb4",
-".Qz c #a1aeb5",
-".3O c #a1aeb6",
-"bF4 c #a1afb5",
-".8q c #a1afb6",
-"#JO c #a1afb7",
-"#So c #a1b3b9",
-"#Q6 c #a1c4d7",
-"#JG c #a1c6d9",
-".LG c #a1d0e7",
-".59 c #a1d1e8",
-"arF c #a1d2eb",
-"au9 c #a1d3eb",
-"#yw c #a1d3ec",
-".4Y c #a1d5ef",
-".oB c #a1dffe",
-".wM c #a1fce2",
-".xJ c #a1ffdf",
-".#3 c #a230a4",
-"af9 c #a23256",
-"#sR c #a23357",
-"a7d c #a23457",
-"afS c #a23458",
-"#jJ c #a23a5a",
-"##n c #a23c5c",
-"aPG c #a23d5d",
-"blQ c #a24162",
-"bK3 c #a2446b",
-"bdE c #a25777",
-"aFH c #a26677",
-"ba1 c #a26678",
-"aaJ c #a26f7d",
-".Dy c #a27173",
-"bAI c #a279a2",
-"afv c #a27e85",
-"b#s c #a28ba2",
-"aXn c #a29097",
-"bhC c #a290a5",
-"ag3 c #a29ea5",
-".dB c #a2a2a2",
-".Dd c #a2a2a3",
-".De c #a2a3a3",
-"bNH c #a2a3a4",
-"bQG c #a2a4a4",
-"bNx c #a2a4a5",
-".EC c #a2a7a8",
-"aaO c #a2a7aa",
-"##y c #a2a9ac",
-"auf c #a2a9ae",
-"#iI c #a2acb1",
-"a5z c #a2acb2",
-"asK c #a2adb2",
-"#d9 c #a2adb3",
-"#fn c #a2adb4",
-"#kT c #a2aeb4",
-".J1 c #a2aeb5",
-"#IP c #a2afb5",
-".WG c #a2afb6",
-".2q c #a2afb7",
-".51 c #a2b0b7",
-"#Tl c #a2b1b8",
-"#iC c #a2b1b9",
-"#iB c #a2b5be",
-"#pv c #a2c6d8",
-"afm c #a2c8e5",
-"#S3 c #a2cadd",
-".Nw c #a2cce0",
-"#jD c #a2cce1",
-".oI c #a2ccf4",
-".nV c #a2cef8",
-".2G c #a2cfe6",
-".qb c #a2d2ee",
-"bdp c #a2d3eb",
-"#gn c #a2d4ec",
-"ap7 c #a2d4ed",
-".WL c #a2d7f1",
-"aF1 c #a33357",
-"bMB c #a33457",
-"aQ9 c #a33458",
-"bHa c #a3385d",
-"bri c #a3395a",
-"#o4 c #a33b5b",
-"at3 c #a34865",
-".DI c #a37576",
-".Dk c #a37675",
-".Dx c #a37676",
-"bo. c #a37e83",
-"aGW c #a37f82",
-"acw c #a38086",
-".hZ c #a391cb",
-"a2k c #a3a0ab",
-".c0 c #a3a3a3",
-"bND c #a3a4a5",
-".WB c #a3a8ac",
-"aEb c #a3a8ad",
-"agr c #a3a9ae",
-"bBU c #a3aaaf",
-"aUt c #a3abaf",
-"#4B c #a3abb0",
-"#2# c #a3acb2",
-".Im c #a3adb2",
-"#62 c #a3adb3",
-"aMc c #a3adb4",
-"#IS c #a3aeb4",
-"bdr c #a3aeb5",
-"#bj c #a3afb5",
-".Mx c #a3afb6",
-".Mh c #a3afb7",
-".01 c #a3b0b6",
-".Mi c #a3b0b7",
-".Sv c #a3b0b8",
-".Px c #a3b1b8",
-"#iL c #a3b1b9",
-"bry c #a3b1be",
-"aaj c #a3b2b9",
-"a3Z c #a3b3ba",
-"bjN c #a3b5c1",
-".Rw c #a3c0cf",
-"#4l c #a3c2d1",
-".oS c #a3c3eb",
-".Qt c #a3c5d5",
-".O9 c #a3c7da",
-".XL c #a3cee3",
-"#7R c #a3d4ed",
-"#kb c #a3d5ed",
-"#oC c #a3d6ee",
-"#Zv c #a3d8f2",
-"#O7 c #a3d8f3",
-".m4 c #a3d9ff",
-".nQ c #a3dcfe",
-".x9 c #a3fddb",
-"#wj c #a43458",
-"bG8 c #a43558",
-"abb c #a43559",
-"aXS c #a43c5c",
-"a96 c #a43d5d",
-"aTK c #a4486e",
-"at2 c #a44966",
-"#6c c #a44c73",
-".Ff c #a45c6c",
-"be2 c #a46375",
-"av2 c #a4677d",
-"#LF c #a46876",
-"buS c #a4718e",
-".DH c #a47777",
-".SO c #a47a7c",
-".iV c #a485ae",
-".#W c #a4a4a4",
-"bOc c #a4a4a5",
-".Db c #a4a5a5",
-"a0z c #a4a5af",
-".EV c #a4a7a8",
-"#Lv c #a4a7c5",
-"aw0 c #a4a9ae",
-"#XB c #a4abaf",
-"adZ c #a4aeb4",
-"#ho c #a4aeb5",
-"#Q8 c #a4afb5",
-".LE c #a4afb6",
-"aTE c #a4b0b6",
-".Mq c #a4b0b7",
-"#70 c #a4b0b8",
-"#pm c #a4b1b7",
-".Pw c #a4b1b8",
-".97 c #a4b1b9",
-"auI c #a4b2b8",
-".Ur c #a4b2b9",
-"#ZG c #a4b2ba",
-".Sr c #a4bbc7",
-"a3U c #a4bccf",
-".GA c #a4beca",
-".7u c #a4cadc",
-".nX c #a4cbf7",
-".VF c #a4d2e9",
-"#k1 c #a4d6ee",
-"#eb c #a4d6ef",
-"#mX c #a4d7ef",
-".3V c #a4d7f0",
-".1l c #a4d7f1",
-"#Od c #a4d8f2",
-"#PY c #a4d8f3",
-"#S0 c #a4d9f2",
-"#VQ c #a4d9f3",
-".wN c #a4f9de",
-".xf c #a4fede",
-"a2X c #a53458",
-"abc c #a53559",
-"bxB c #a5395c",
-"aTw c #a53b5c",
-".6v c #a53d5d",
-"aL0 c #a53e5e",
-"af5 c #a54966",
-"abK c #a54a66",
-"aef c #a54d67",
-"amc c #a55e73",
-"bpQ c #a56080",
-"aOA c #a5636f",
-"aMi c #a56475",
-"#eJ c #a56778",
-"a6A c #a56877",
-"bPY c #a56d87",
-".Lm c #a57d81",
-"bLa c #a58084",
-".ob c #a59e9b",
-".cq c #a5a5a5",
-"bNB c #a5a6a7",
-"bLF c #a5a7a7",
-".YD c #a5aaad",
-"bb2 c #a5acb0",
-".HS c #a5aeb3",
-"#ue c #a5aeb4",
-".IA c #a5afb4",
-"am4 c #a5afb5",
-".RI c #a5b0b6",
-".9k c #a5b0b7",
-"#m9 c #a5b1b7",
-".LN c #a5b1b8",
-"#0H c #a5b1b9",
-"bWs c #a5b1be",
-"#H. c #a5b2b6",
-"#e. c #a5b2b8",
-".Pe c #a5b2b9",
-".Oj c #a5b2ba",
-"#XO c #a5b3b9",
-".NC c #a5b3ba",
-"#Mk c #a5b3bb",
-".Vv c #a5b4bb",
-".LF c #a5b4bd",
-".Ti c #a5b5bd",
-"#Rj c #a5b6bc",
-"#et c #a5bcc9",
-"#HT c #a5bfcd",
-".Nv c #a5c1d0",
-"#n# c #a5c9da",
-"#xi c #a5cbde",
-".Z2 c #a5cee2",
-".uN c #a5cfb4",
-"#vq c #a5d0e5",
-"#wH c #a5d1e7",
-".XM c #a5d2e8",
-".nU c #a5d2fb",
-"#uq c #a5d4eb",
-"#xU c #a5d6ee",
-"#fh c #a5d7ef",
-"aqT c #a5d7f0",
-"#lY c #a5d8f1",
-"#Q0 c #a5d8f3",
-"#mY c #a5d9f2",
-"#hz c #a5d9f3",
-"#mZ c #a5daf3",
-"#fg c #a5daf4",
-"#lZ c #a5dbf5",
-"aXe c #a5dcf4",
-".ya c #a5eabd",
-"bxw c #a63558",
-"asd c #a63559",
-"bG7 c #a63659",
-"aFV c #a6385b",
-"aFW c #a63b5d",
-"a8E c #a63c5d",
-"bRk c #a6436a",
-"bb9 c #a64e74",
-"aIe c #a6536a",
-"acH c #a65d70",
-"anP c #a6697a",
-".bo c #a674a3",
-"#H5 c #a67685",
-".DJ c #a67778",
-"baP c #a6848c",
-"a5F c #a68da3",
-".j7 c #a6a6a6",
-"bOe c #a6a7a7",
-"bO6 c #a6a7a8",
-".ZC c #a6abad",
-".ZB c #a6abae",
-"#Y0 c #a6acb0",
-".GK c #a6adb1",
-"awZ c #a6aeb4",
-"aR9 c #a6aeb5",
-".JQ c #a6afb5",
-"a5U c #a6b0b5",
-"#ca c #a6b0b6",
-"#gq c #a6b0b7",
-".4y c #a6b1b7",
-"#ND c #a6b1b8",
-"#.T c #a6b2b7",
-"#ui c #a6b2b8",
-".LP c #a6b2b9",
-"#XN c #a6b2ba",
-"a5O c #a6b3b8",
-"bz6 c #a6b3b9",
-".NV c #a6b3ba",
-".Nt c #a6b3bb",
-".XC c #a6b4ba",
-".Ol c #a6b4bb",
-"#Mi c #a6b4bc",
-"a0E c #a6b5bc",
-"a8. c #a6b6bd",
-"#MN c #a6b7bf",
-"#xg c #a6b8c1",
-"a8Y c #a6b9bf",
-".YN c #a6b9c2",
-"#wG c #a6bdc8",
-"#tw c #a6beca",
-"#xh c #a6bfcc",
-".oR c #a6c2e5",
-"#.r c #a6c4d3",
-"#up c #a6c6d5",
-"#.l c #a6cde0",
-".YP c #a6cde1",
-"aSN c #a6cee1",
-"#UY c #a6cfe3",
-".VJ c #a6cfe4",
-"#xT c #a6d0e5",
-".50 c #a6d1e6",
-"#Lh c #a6d4ea",
-"#nM c #a6d4eb",
-"#m0 c #a6d6ed",
-"a9B c #a6d7ee",
-"#xj c #a6d8f1",
-"#rY c #a6d9f1",
-".8R c #a6d9f2",
-".XQ c #a6d9f3",
-".m5 c #a6d9ff",
-"#Ak c #a6daf2",
-"#xV c #a6daf3",
-".9y c #a6daf4",
-"#oF c #a6dbf4",
-"#gm c #a6dbf5",
-"a4H c #a6dcf6",
-".Uz c #a6dcf7",
-".nR c #a6dcfd",
-".xK c #a6ffdb",
-".#H c #a725a6",
-"aJr c #a73559",
-"bT4 c #a73659",
-"ans c #a7365a",
-"aVI c #a73a5c",
-"bnD c #a73b5c",
-"brg c #a73f5e",
-"bMN c #a7436a",
-"be6 c #a74970",
-"baZ c #a76678",
-".E0 c #a76872",
-"bfX c #a76888",
-"#o# c #a77a80",
-"aRy c #a78085",
-"aGI c #a790a6",
-".pc c #a7a7a7",
-".CP c #a7a7a8",
-".In c #a7abaf",
-".XB c #a7acaf",
-"#3C c #a7acb1",
-".7e c #a7aeb2",
-"aTD c #a7afb4",
-"a28 c #a7afb8",
-"a8Z c #a7b0b9",
-"#8i c #a7b1b6",
-"#WI c #a7b1b7",
-"#R3 c #a7b2b8",
-"bVm c #a7b3b8",
-"#y7 c #a7b3b9",
-".LO c #a7b3ba",
-".YF c #a7b3bb",
-"#zD c #a7b4ba",
-".Oq c #a7b4bb",
-".Ok c #a7b4bc",
-".8m c #a7b5bb",
-".O# c #a7b5bc",
-"a8j c #a7b5bd",
-"#Ft c #a7b6bd",
-"a4L c #a7b6be",
-"a1p c #a7b7be",
-"#v7 c #a7b7bf",
-"a7q c #a7b8bf",
-"aOg c #a7b9c2",
-"#rX c #a7bbc5",
-"aPm c #a7c0cc",
-"#mj c #a7c1cd",
-"aR6 c #a7c1ce",
-"#Nb c #a7c7d7",
-"bbW c #a7c8d9",
-"#Kt c #a7c9d9",
-"a4I c #a7c9da",
-"b#j c #a7cadb",
-"a6O c #a7cbdd",
-"aWC c #a7ccde",
-".3L c #a7ccdf",
-"#zM c #a7cde0",
-".4P c #a7cee2",
-".1g c #a7cfe2",
-"#y9 c #a7cfe3",
-"bb# c #a7d0e4",
-"a7m c #a7d1e5",
-"#.7 c #a7d1e6",
-"#N# c #a7d3e9",
-".7l c #a7d4eb",
-".mi c #a7d4fe",
-".2F c #a7d5ec",
-"bbU c #a7d6ec",
-".nT c #a7d6fc",
-".8L c #a7d7ef",
-"#AO c #a7d8f0",
-"aTz c #a7d9f1",
-"#XD c #a7d9f2",
-"#9l c #a7d9f3",
-"#yu c #a7daf3",
-"#R1 c #a7daf4",
-"#wI c #a7dbf4",
-"#oE c #a7dbf5",
-"#ka c #a7dcf6",
-"#k0 c #a7dcf7",
-"aWB c #a7ddf7",
-".wO c #a7f7db",
-".y. c #a7fbd5",
-"bFa c #a83356",
-"aiz c #a83459",
-"bG9 c #a83559",
-"ap2 c #a8365a",
-"afV c #a8375a",
-"aId c #a8395d",
-"#n5 c #a83c5d",
-"#mv c #a83e5e",
-"#zg c #a83e5f",
-"bF. c #a84165",
-"bx3 c #a84968",
-"av3 c #a86274",
-"#oZ c #a86677",
-"bcw c #a8687a",
-".UU c #a88084",
-".jG c #a88cb1",
-".hW c #a8a8a8",
-"bQC c #a8a9aa",
-"#Rw c #a8acb0",
-"#OH c #a8adb1",
-"#7P c #a8afb3",
-"a0D c #a8b0b5",
-"a2l c #a8b1b9",
-".6U c #a8b2b8",
-"b.0 c #a8b3b8",
-".QM c #a8b3b9",
-".4t c #a8b3ba",
-"a7r c #a8b3bb",
-".Nu c #a8b4ba",
-".LQ c #a8b4bb",
-"#.n c #a8b4bc",
-".2c c #a8b5bb",
-".ND c #a8b5bc",
-".5M c #a8b5bd",
-"#d. c #a8b6bc",
-".Oa c #a8b6bd",
-"#.8 c #a8b6be",
-"#kp c #a8b7be",
-"#vp c #a8b7bf",
-".7m c #a8b8bf",
-"#uo c #a8b8c1",
-"b#l c #a8b9bf",
-"a79 c #a8b9c0",
-".3F c #a8bac2",
-"#ll c #a8bac3",
-".YO c #a8bdc8",
-"#qi c #a8bec9",
-"aXf c #a8bfc8",
-"#Mb c #a8bfcb",
-"b.N c #a8c0cc",
-"b#S c #a8c0cd",
-".9z c #a8c1ce",
-"a77 c #a8c3d1",
-"aUk c #a8c4d2",
-"bbV c #a8c6d5",
-"#xS c #a8c7d6",
-"a6c c #a8c7d7",
-"#Pa c #a8c8d8",
-".TF c #a8c8de",
-".2x c #a8c9da",
-"aQb c #a8ccde",
-"#sD c #a8cfe2",
-".pu c #a8d3f1",
-"#ys c #a8d7ee",
-"#mV c #a8d7ef",
-"#UU c #a8d9f1",
-"#QZ c #a8d9f2",
-"#Q1 c #a8daf2",
-"#ur c #a8daf3",
-".nS c #a8dafe",
-"#Lg c #a8dbf3",
-"amP c #a8dbf4",
-"atl c #a8dbf5",
-".7t c #a8dcf4",
-"#v8 c #a8dcf5",
-"#yt c #a8dcf6",
-".Z3 c #a8ddf7",
-"b#R c #a8def8",
-".wP c #a8f3d7",
-".y# c #a8f5cc",
-"bXf c #a93559",
-"atK c #a9375b",
-"#rh c #a93a5d",
-"#wa c #a93c5d",
-"#.F c #a93d5e",
-"byA c #a93d62",
-"as5 c #a93f60",
-"byB c #a94d74",
-"at1 c #a94e69",
-"aZJ c #a95672",
-".G7 c #a96172",
-"ast c #a96677",
-"#.u c #a96779",
-"afC c #a97b80",
-"axk c #a98084",
-"#5V c #a98388",
-".TJ c #a9898c",
-"#5b c #a991a5",
-"a9G c #a9a2ae",
-".D. c #a9a6a7",
-".bR c #a9a9a9",
-".Ct c #a9a9aa",
-"bSf c #a9aaaa",
-".Da c #a9aaab",
-".9t c #a9afb3",
-"#hp c #a9b2b8",
-"beC c #a9b2be",
-"acn c #a9b3b8",
-"#ef c #a9b3b9",
-".1n c #a9b4ba",
-".2y c #a9b4bb",
-"#Ok c #a9b5ba",
-".2s c #a9b5bb",
-".Qs c #a9b5bc",
-"#WS c #a9b5bd",
-"#U1 c #a9b6bb",
-".VB c #a9b6bc",
-".NE c #a9b6bd",
-".2B c #a9b6be",
-"aXg c #a9b7bd",
-".98 c #a9b7be",
-"b.O c #a9b8be",
-"a0A c #a9b8bf",
-"aRc c #a9b8c0",
-"b.b c #a9b8c1",
-"aX5 c #a9b9bf",
-".FX c #a9b9c4",
-"byc c #a9b9c9",
-"#nY c #a9bac2",
-".8M c #a9bac3",
-"aX4 c #a9bbc1",
-"a7n c #a9bbc4",
-"bav c #a9bbc5",
-"#yq c #a9bcc6",
-"#Na c #a9bfcb",
-".8S c #a9c1cd",
-"#xR c #a9c3d0",
-".1m c #a9c9da",
-".na c #a9caf8",
-"#iN c #a9d3e7",
-"#O8 c #a9d5eb",
-"#mU c #a9d6eb",
-"#PZ c #a9d8ef",
-"#XE c #a9d9f0",
-"a.M c #a9d9f1",
-"#0B c #a9daf2",
-"#AS c #a9dbf4",
-"#xW c #a9dcf5",
-"abR c #a9ddf6",
-"#nL c #a9ddf7",
-"#6D c #a9def7",
-".VG c #a9def8",
-"aXd c #a9e3fc",
-".xg c #a9feda",
-"a#A c #aa3356",
-"bpL c #aa365a",
-"aEU c #aa375b",
-"bH# c #aa375c",
-"agd c #aa385b",
-"#s. c #aa385c",
-"a#I c #aa3c61",
-"#iR c #aa3d5e",
-"aK1 c #aa3f60",
-"adw c #aa4f6a",
-"bHr c #aa5f84",
-".OI c #aa687a",
-"a.w c #aa6e87",
-"bvW c #aa748f",
-".DK c #aa7b7c",
-"aHe c #aa7d90",
-"aGE c #aa8084",
-"#JX c #aa819b",
-"a8# c #aaa7b2",
-".cj c #aaaaaa",
-".Cu c #aaaaab",
-"bPc c #aaabab",
-"aX3 c #aaaeb7",
-"#qN c #aaafb2",
-"b.f c #aab0b9",
-"asJ c #aab1b6",
-"adT c #aab3b8",
-"#Mc c #aab4ba",
-"a7o c #aab4bb",
-".Rv c #aab5bb",
-".Sq c #aab5bc",
-"bjV c #aab6bb",
-"#nX c #aab6bc",
-".O8 c #aab6bd",
-"#P3 c #aab6be",
-".PS c #aab7bd",
-".NU c #aab7be",
-"a3V c #aab8bf",
-"aZM c #aab9bf",
-"a29 c #aab9c0",
-"a9F c #aabac0",
-".GG c #aabac6",
-"bBV c #aabac9",
-"b.e c #aabcc1",
-"#0C c #aabcc5",
-"bXs c #aac3dd",
-"#oP c #aac7d5",
-"#yr c #aac8d8",
-"aWA c #aacad9",
-"atj c #aacbdb",
-".n# c #aacdf9",
-".nY c #aacef6",
-"#q6 c #aad0e3",
-".2t c #aad3e7",
-"#Oe c #aad4e8",
-"#pD c #aad4e9",
-".mj c #aad4ff",
-".m6 c #aad8fe",
-"#UT c #aadaf2",
-".4X c #aadbf3",
-".58 c #aadcf4",
-"#z. c #aadef7",
-".WM c #aadef8",
-"#oD c #aadff8",
-".XN c #aadff9",
-".5Z c #aadffa",
-".YQ c #aae0fa",
-".wQ c #aaf1d4",
-"bKP c #ab385b",
-"age c #ab385c",
-"bpM c #ab3a5d",
-"#hU c #ab3c5e",
-".7Q c #ab3d5f",
-"aRv c #ab3e5f",
-"aro c #ab3f60",
-"asg c #ab3f61",
-"#8R c #ab3f63",
-"aXr c #ab5c7c",
-"#vB c #ab687a",
-"amF c #ab6b7d",
-"#IZ c #ab6e7f",
-"bOD c #ab8387",
-"bMW c #ab8388",
-"aGL c #ab8790",
-"bgN c #ab899f",
-"bSq c #ababab",
-"azp c #ababac",
-".D# c #abacac",
-"b#m c #abaeb7",
-"#KU c #abb0b3",
-"aX2 c #abb3c7",
-"aX1 c #abb3d0",
-"#4n c #abb4ba",
-".62 c #abb5bb",
-"bp3 c #abb6bb",
-"#hC c #abb6bc",
-"#aM c #abb6bd",
-"a9R c #abb7bc",
-".96 c #abb7bd",
-".Oi c #abb7be",
-".Om c #abb8be",
-".Pd c #abb8bf",
-".2b c #abb8c0",
-"bo9 c #abb9bf",
-".9E c #abb9c0",
-"#y8 c #abb9c1",
-"aYW c #abbbc1",
-"aYV c #abbcc2",
-"##Z c #abbcc4",
-"#Md c #abbcc5",
-"#BU c #abbec8",
-"#hN c #abcfe1",
-".Z7 c #abcfe2",
-"#WM c #abd1e4",
-"#T1 c #abd4e8",
-".3G c #abd5e9",
-"#Ma c #abdaf2",
-".3U c #abdbf3",
-"asB c #abddf6",
-"a4G c #abdff6",
-"#z# c #abdff8",
-"ae1 c #abdff9",
-"#yv c #abe0f9",
-".4O c #abe0fa",
-"#mW c #abe0fb",
-"bau c #abe1fa",
-".2w c #abe1fb",
-".3K c #abe2fc",
-"bF# c #ac3457",
-"ahk c #ac365a",
-"bH. c #ac365b",
-"arm c #ac375c",
-"ao5 c #ac385c",
-"aVR c #ac3e5f",
-"bJC c #ac4268",
-"bw1 c #ac496f",
-"bqA c #ac6579",
-"#vz c #ac6779",
-"#fL c #ac697b",
-"#9W c #ac6e7a",
-"bFS c #ac6e86",
-"bIA c #ac708c",
-"aNi c #ac777c",
-".Lf c #ac8284",
-"b.g c #ac8b9d",
-"#Qa c #ac8faa",
-"a4R c #ac91a4",
-"afz c #ac9ca0",
-"b.P c #ac9fab",
-"b#W c #aca5b1",
-"aYU c #aca7b2",
-".CO c #acabac",
-".gy c #acacac",
-".Ea c #acafb1",
-"bRG c #acb0b3",
-"#iH c #acb5ba",
-"#U3 c #acb5bb",
-"aAf c #acb6bc",
-"b.r c #acb7bc",
-"#Yb c #acb7bd",
-".Mr c #acb7be",
-".TC c #acb8be",
-".O. c #acb8bf",
-"a6Z c #acb9be",
-".WR c #acb9bf",
-".2# c #acb9c0",
-".2a c #acb9c1",
-".8h c #acbac1",
-"bdU c #acbfc8",
-"#gG c #accada",
-".WK c #accbdc",
-".n. c #acd0fb",
-"a4F c #acd4ea",
-".qc c #acd8ed",
-"aRb c #acd8ee",
-".YT c #acdaf0",
-"#5r c #acdaf1",
-"aUj c #acdff8",
-"b#i c #ace0f9",
-"#AU c #ace0fa",
-"#Al c #ace1fa",
-"#AT c #ace1fb",
-".xL c #acffd6",
-"agc c #ad395c",
-"a2e c #ad395d",
-"aK7 c #ad3d5f",
-"#ze c #ad3e60",
-"a.f c #ad3e62",
-"aJ8 c #ad3f60",
-"bOu c #ad4268",
-"boj c #ad486b",
-"boY c #ad587e",
-"amb c #ad5971",
-"awd c #ad6779",
-"ba0 c #ad697b",
-"bBO c #ad728e",
-"bfB c #ad747f",
-"#JU c #ad7b94",
-"aRl c #ad8587",
-"a8e c #ad92a5",
-"a80 c #ad96a5",
-"#8c c #ad9d9f",
-"#Yh c #ada2ab",
-".bh c #adadad",
-"aCN c #adadae",
-"aks c #adb2b5",
-"#O4 c #adb3b7",
-".7d c #adb4b7",
-"ai3 c #adb4b8",
-"#ON c #adb6bb",
-"axC c #adb7bc",
-"#jk c #adb7bd",
-"bWp c #adb8bd",
-"#vk c #adb8be",
-"#vl c #adb8bf",
-"a5N c #adb9be",
-"#lO c #adb9bf",
-".PL c #adb9c0",
-"#c1 c #adbac0",
-".RA c #adbac1",
-"#z9 c #adbbc2",
-"a8X c #adbcc2",
-".RT c #adc2d2",
-"#BV c #adc6d2",
-"aTy c #adc7d4",
-"#yp c #adc8d5",
-"#W# c #adc8e1",
-"#S1 c #adcede",
-"#G6 c #add3e6",
-".m9 c #add3fc",
-".m7 c #add8fe",
-".Tw c #addff8",
-"atk c #ade1fa",
-".9x c #ade3fd",
-".wR c #adf0cf",
-".#w c #ae26ae",
-"#r9 c #ae395d",
-"#gV c #ae3d5e",
-"aH5 c #ae3d60",
-"#i3 c #ae3e60",
-"aI8 c #ae3f61",
-"bms c #ae4367",
-"at0 c #ae536d",
-"bgH c #ae6177",
-"#eL c #ae687a",
-"akW c #ae6a7c",
-"bem c #ae7287",
-".Qe c #ae8387",
-".9K c #ae8487",
-".Qb c #ae8488",
-"aSU c #ae8587",
-"acD c #ae9ea2",
-".j8 c #aeaeae",
-"aaD c #aeafb2",
-"#Qv c #aeb3b7",
-"aIX c #aeb3b8",
-"#c. c #aeb4b7",
-".7c c #aeb4b8",
-"a4M c #aeb4bb",
-"#Aa c #aeb7bd",
-"##J c #aeb8bc",
-"bXy c #aeb8bd",
-"ago c #aeb8be",
-".Ku c #aeb9be",
-"#uj c #aeb9bf",
-"#wD c #aeb9c0",
-"a6r c #aebabf",
-"#ko c #aebac0",
-"##V c #aebac1",
-"#.X c #aebbc1",
-".5O c #aebbc2",
-"bSW c #aebdcb",
-".4L c #aec1ca",
-"#R2 c #aec7d4",
-".nb c #aeccf8",
-".lA c #aecffe",
-".mk c #aed4ff",
-".m8 c #aed6fe",
-"bbX c #aed7ea",
-"#IK c #aed8ed",
-"abQ c #aedbf1",
-".xh c #aefdd6",
-"ahv c #af395d",
-"agf c #af3a5d",
-"agb c #af3a5e",
-"auV c #af3b5f",
-"a1i c #af3d5f",
-".6z c #af3e5f",
-".6w c #af3e60",
-"bK1 c #af3e63",
-"btA c #af4167",
-"#ac c #af4a68",
-".GP c #af6274",
-"bs1 c #af687a",
-"#sK c #af6c7d",
-"bmk c #af7282",
-"#bY c #af748f",
-"al9 c #af7c85",
-".TS c #af8287",
-".1H c #af8588",
-"#Gs c #af8b8f",
-"ben c #afa6af",
-".tO c #afaf26",
-".af c #afafaf",
-"bf1 c #afb1b6",
-".7b c #afb5b9",
-"an8 c #afb7bc",
-".Ux c #afb8be",
-"#lR c #afb9be",
-".Mp c #afb9bf",
-"#ts c #afb9c0",
-"#to c #afbabf",
-"#uk c #afbac0",
-"#vi c #afbac1",
-".PT c #afbbc1",
-".7o c #afbbc2",
-"bHF c #afbbc3",
-"##R c #afbcc2",
-"#c0 c #afbcc3",
-"#XF c #afbfc7",
-"#Q2 c #afc2cc",
-"bJV c #afc2d3",
-"#Cz c #afc4cf",
-".qq c #afc6ca",
-".oJ c #afd4f4",
-".qe c #afd6e9",
-"#mT c #afd6ea",
-"#N. c #afdbf1",
-".wr c #afe0bc",
-"auE c #afe4fe",
-".wS c #afeecb",
-"ag. c #b0395d",
-"aPh c #b03a5e",
-"al5 c #b03b5e",
-"aWy c #b03e60",
-"bPS c #b04167",
-"bjI c #b04267",
-"beh c #b04269",
-"#b. c #b04b6e",
-"bIx c #b05277",
-"all c #b05670",
-"awN c #b0697b",
-"#g0 c #b06b7d",
-"bgD c #b06b7e",
-".bX c #b07eaf",
-".cy c #b083b0",
-"aEs c #b08588",
-"aqD c #b0878a",
-"aYT c #b08a9c",
-"#He c #b08c8f",
-"#8u c #b08e95",
-"#Pr c #b08e9b",
-"aGG c #b095a9",
-"#Qi c #b09cb2",
-"#S. c #b0a6af",
-".gw c #b0b0b0",
-".EG c #b0b1b1",
-"bQb c #b0b5b7",
-".7a c #b0b6b9",
-"adx c #b0b6ba",
-"asz c #b0b7ba",
-"aXj c #b0b7bc",
-"a4d c #b0b8bc",
-"#li c #b0b9bd",
-"#Nj c #b0b9be",
-"aX6 c #b0b9bf",
-"#xC c #b0babf",
-".Z8 c #b0bac0",
-".Mw c #b0bbc1",
-"#C6 c #b0bbc2",
-"agJ c #b0bcc1",
-".RR c #b0bcc2",
-".53 c #b0bcc3",
-"#p3 c #b0bdc3",
-"#Ku c #b0bec4",
-"#P0 c #b0bfc7",
-"#VR c #b0cbd8",
-".1r c #b0cde1",
-"#1P c #b0d0e0",
-"bcI c #b0d6e9",
-"ap6 c #b0dbf1",
-".0c c #b0dcf2",
-"bG6 c #b13659",
-"ase c #b1395e",
-"#qx c #b13a5e",
-"afT c #b13b5f",
-"#tz c #b13d60",
-"#oU c #b13e60",
-"aQv c #b13e61",
-"##r c #b14167",
-"bi2 c #b14368",
-"bCE c #b15277",
-"bbI c #b1697c",
-"a9H c #b17f93",
-"#Pi c #b1829b",
-"bfo c #b18594",
-".8X c #b18689",
-"aPx c #b1888a",
-"aas c #b1898c",
-".EX c #b1a4a7",
-"aX0 c #b1abc7",
-".be c #b1b1b1",
-".8T c #b1b5b9",
-"a2p c #b1b5bb",
-".Gu c #b1b6b9",
-"ab8 c #b1b7ba",
-"#bn c #b1b7bb",
-"#BZ c #b1b8bc",
-".5F c #b1babf",
-"avI c #b1bac0",
-"a3s c #b1bbc0",
-"bbf c #b1bbc1",
-"#v1 c #b1bbc2",
-"#az c #b1bcc1",
-"#j6 c #b1bcc2",
-"#p6 c #b1bcc3",
-"am8 c #b1bdc2",
-".TD c #b1bdc3",
-"#.0 c #b1bdc4",
-"#Me c #b1bec5",
-"#O9 c #b1bec6",
-"#UV c #b1c3cc",
-".n3 c #b1caef",
-"#.4 c #b1d2e2",
-".ml c #b1d3fe",
-"#Ky c #b1daed",
-"be. c #b1dbef",
-"agl c #b1dcf1",
-"aV. c #b1dff6",
-"a5y c #b1e6fe",
-".xM c #b1ffd3",
-"aur c #b2395e",
-"bty c #b23a5e",
-"aqx c #b23a5f",
-"afU c #b23b5f",
-"#uL c #b23f61",
-"#xp c #b23f62",
-"bvq c #b24166",
-"aCW c #b26979",
-"apW c #b26b7d",
-"#cN c #b2738e",
-"b.Q c #b2758c",
-".iA c #b27f8f",
-"bhs c #b28689",
-"axo c #b2868a",
-".d# c #b287b2",
-"bCR c #b2888c",
-"#LJ c #b28b8e",
-"bmA c #b2a2a8",
-".#l c #b2b2b2",
-"a5B c #b2b2b9",
-".pJ c #b2b5b6",
-"a.4 c #b2b6b9",
-"ar. c #b2b8bd",
-"bsk c #b2b9be",
-"a2J c #b2babe",
-"#m5 c #b2bbc0",
-".3m c #b2bbc1",
-"#un c #b2bcc2",
-"aQc c #b2bcc3",
-"aql c #b2bdc2",
-".Mj c #b2bdc3",
-"#e0 c #b2bdc4",
-"##O c #b2bec4",
-"aAi c #b2bec5",
-"aX9 c #b2bfc4",
-"#Li c #b2bfc6",
-".n4 c #b2caed",
-".oQ c #b2cbe7",
-".lB c #b2cfff",
-".pz c #b2d1e8",
-".nZ c #b2d1f6",
-"aVV c #b2d9ed",
-".qd c #b2daeb",
-"#R0 c #b2dcf1",
-"aR5 c #b2def4",
-".wT c #b2edc7",
-".xi c #b2fcd2",
-"aiA c #b3395e",
-"aPH c #b33b5f",
-"aK3 c #b33e60",
-"aLv c #b33e61",
-"aL2 c #b34062",
-"bMM c #b34066",
-"b#n c #b3637f",
-"#rc c #b36a7c",
-"avB c #b36a7d",
-"bDE c #b36b7c",
-"avw c #b36d7f",
-"aee c #b37e85",
-"aQl c #b3898b",
-".dQ c #b38bb4",
-"aGK c #b3909b",
-"aGJ c #b393a3",
-"aly c #b39a99",
-"#Sn c #b3a2ac",
-"aUw c #b3a9ae",
-"bf. c #b3adb5",
-".#V c #b3b3b3",
-"ack c #b3b7bb",
-".0Q c #b3b8ba",
-".7. c #b3b9bd",
-"aX7 c #b3b9be",
-".8H c #b3babd",
-"#PA c #b3bbbf",
-"#OO c #b3bcc0",
-"#j5 c #b3bcc1",
-"bph c #b3bcc2",
-"#R8 c #b3bdc2",
-"#nF c #b3bdc3",
-"asS c #b3bec3",
-".Vw c #b3bec4",
-".ZX c #b3bec5",
-"bjO c #b3bfc4",
-".00 c #b3bfc5",
-".mr c #b3caf9",
-".mq c #b3cbfb",
-"#YH c #b3cdda",
-"#aK c #b3cdde",
-"aQa c #b3cedc",
-".pv c #b3d9f0",
-".1w c #b3def4",
-"ag# c #b43b5f",
-"aga c #b43c60",
-"aOb c #b43d60",
-"#lz c #b43e61",
-"#hT c #b43f61",
-"arn c #b44063",
-"bkv c #b4456a",
-"ajZ c #b44868",
-"b#X c #b45675",
-"atZ c #b45972",
-"b.h c #b46580",
-"#vA c #b46a7d",
-".2M c #b46c7e",
-".GO c #b46f7e",
-"ao1 c #b46f80",
-"bWf c #b46f86",
-"aYS c #b46f87",
-".Fh c #b47e81",
-"apO c #b4888c",
-"aOx c #b4898b",
-"bOE c #b4898d",
-".iG c #b4a9e5",
-".gr c #b4b4b4",
-"a3a c #b4b5bb",
-"#Tk c #b4b8bf",
-".Kj c #b4b9bc",
-"aVZ c #b4babe",
-"atu c #b4bbbf",
-"#NN c #b4bcc1",
-"#jA c #b4bdc2",
-"aGy c #b4bdc3",
-"#cf c #b4bec3",
-"#y3 c #b4bec4",
-"#P7 c #b4bfc3",
-"#um c #b4bfc4",
-".Ns c #b4bfc5",
-"#zA c #b4bfc6",
-"bkz c #b4bfc9",
-"a2o c #b4c0c5",
-".5C c #b4c0c6",
-"bba c #b4c5cf",
-".kS c #b4cafd",
-".mp c #b4cefc",
-".lC c #b4cfff",
-".oN c #b4d0ed",
-"#YK c #b4d2e1",
-".mm c #b4d3fe",
-".Ky c #b4d5e5",
-"aOf c #b4d9eb",
-"ap1 c #b53c60",
-"arB c #b53d60",
-"#oe c #b53e61",
-"aH4 c #b53e62",
-"aQ. c #b53f62",
-"aK8 c #b54063",
-"blT c #b54168",
-"#98 c #b54267",
-"bmv c #b54268",
-"atf c #b56b7e",
-"a.q c #b57189",
-".Ft c #b58284",
-".ew c #b58fb6",
-"a32 c #b591a3",
-".sy c #b5af39",
-".#R c #b5b5b5",
-"a1q c #b5b5ba",
-"#B2 c #b5b9bc",
-".92 c #b5babd",
-"#ZR c #b5babe",
-"#il c #b5bbbe",
-".Iy c #b5bcbf",
-"a4K c #b5bdc0",
-".Kr c #b5bec2",
-".Kh c #b5bec3",
-"#by c #b5bfc3",
-"#qQ c #b5bfc4",
-".8l c #b5bfc5",
-"#yl c #b5c0c5",
-".1f c #b5c0c6",
-"#xz c #b5c0c7",
-"#ud c #b5c1c6",
-"#j2 c #b5c1c7",
-"#fA c #b5c9d3",
-".mo c #b5cffd",
-"#mS c #b5d1e0",
-".oM c #b5d2ee",
-".mn c #b5d2fe",
-"#YG c #b5ddf1",
-".xj c #b5facd",
-"#uM c #b63c60",
-"ahc c #b63d61",
-"aZt c #b63e61",
-"aK2 c #b63f62",
-"bBK c #b64065",
-"aj0 c #b64265",
-"bzX c #b64267",
-"bli c #b64268",
-"bnM c #b64369",
-"#8F c #b64468",
-"bom c #b64469",
-"bm9 c #b6456a",
-"bCG c #b6466b",
-"aEW c #b66377",
-"aTS c #b6687a",
-"#pK c #b66b7e",
-"#jO c #b66d80",
-"#eA c #b66e80",
-"ad1 c #b68283",
-"bic c #b6888b",
-"aBO c #b6898c",
-".QZ c #b68a8c",
-"bky c #b698a8",
-"a6g c #b6b4ba",
-".cn c #b6b6b6",
-".EH c #b6b7b7",
-".EW c #b6b8b8",
-".N0 c #b6bbbe",
-"#Rp c #b6bfc3",
-"#Tu c #b6bfc4",
-"aua c #b6bfc5",
-"aZQ c #b6c0c4",
-"#OG c #b6c0c5",
-".Ob c #b6c0c6",
-".2o c #b6c0c7",
-"#vj c #b6c1c6",
-".XF c #b6c1c7",
-".9o c #b6c1c8",
-"#c9 c #b6c2c7",
-"#p4 c #b6c2c8",
-".XK c #b6c5cd",
-"a46 c #b6c6cc",
-"#BW c #b6cad3",
-".nc c #b6cff8",
-"aSM c #b6daec",
-"#VP c #b6ddf0",
-"#Oc c #b6ddf1",
-".xQ c #b6ecae",
-".wU c #b6edc1",
-"#tI c #b73d61",
-"aQw c #b73d62",
-"aOc c #b73e61",
-"#7w c #b73e62",
-".89 c #b73f62",
-"asf c #b74063",
-"bDx c #b74064",
-"bdA c #b74268",
-"buN c #b74369",
-"bnL c #b74469",
-"bol c #b7446a",
-"bna c #b7456a",
-"brr c #b7456b",
-"bn# c #b7466a",
-"bo1 c #b7466b",
-"b.R c #b74f70",
-"aYR c #b75b78",
-"buL c #b75d76",
-".G8 c #b76d7b",
-"#Ei c #b76d7f",
-".GQ c #b7717d",
-".Fu c #b78484",
-"##d c #b78a8d",
-".fe c #b793b8",
-".bK c #b7b7b7",
-".EB c #b7b8b8",
-".13 c #b7bbbd",
-"#5z c #b7bbbf",
-"atz c #b7bcbf",
-"apd c #b7bcc0",
-"#TY c #b7bdc0",
-"api c #b7bdc2",
-"bSX c #b7bfc3",
-"#nz c #b7bfc4",
-"#ir c #b7c0c5",
-"#x# c #b7c0c6",
-"a1U c #b7c1c5",
-"#rQ c #b7c1c6",
-".SJ c #b7c1c7",
-"#ax c #b7c2c7",
-".Tl c #b7c2c8",
-"a2D c #b7c3c7",
-"#ow c #b7c3c8",
-"azl c #b7c3c9",
-"a9E c #b7c7cb",
-".ID c #b7ced9",
-"##X c #b7d1e4",
-".xN c #b7ffce",
-"ajK c #b8395f",
-"a#L c #b83a60",
-"afX c #b83b60",
-"ac5 c #b83b61",
-"a.h c #b83c61",
-"#mn c #b83e62",
-"aI7 c #b83f63",
-"#zf c #b84063",
-"bwD c #b84064",
-"bs3 c #b84369",
-"bBJ c #b84468",
-"aZI c #b84567",
-"bpT c #b8456a",
-"blS c #b8466b",
-"ahu c #b84767",
-"bqD c #b8476b",
-"aFM c #b84c6b",
-"bwY c #b84d6f",
-"ajY c #b84f6d",
-"alk c #b8516e",
-"avr c #b85a7b",
-"ald c #b86078",
-"atW c #b86c7f",
-"aeG c #b86f81",
-"btS c #b8748d",
-".Fv c #b88485",
-".Fg c #b88585",
-"aHS c #b88b8e",
-"aDw c #b88b90",
-".f1 c #b896ba",
-"#Lt c #b89aa5",
-"a0F c #b8b0b5",
-".#i c #b8b8b8",
-"#Cx c #b8bcbe",
-"ab6 c #b8bdc0",
-"#Tr c #b8bec1",
-".n9 c #b8bfce",
-"bqL c #b8c0c5",
-"#9C c #b8c1c4",
-"#mN c #b8c1c5",
-"#Ya c #b8c1c6",
-".PM c #b8c2c7",
-"#gh c #b8c2c8",
-"#qU c #b8c2c9",
-"bu0 c #b8c2cd",
-".YM c #b8c3c8",
-".Qy c #b8c3c9",
-"#VZ c #b8c4c8",
-"#rG c #b8c4ca",
-".pK c #b8c7ca",
-".kT c #b8cbff",
-".Y0 c #b8cdd8",
-"be# c #b8ceda",
-".lD c #b8cffe",
-"bbY c #b8d3e1",
-"#CA c #b8d4e1",
-"#Ne c #b8d4e2",
-".Kz c #b8d5e4",
-".tK c #b8d8a6",
-".oK c #b8d8f3",
-".pw c #b8daee",
-"aPl c #b8dbed",
-"bxA c #b9375a",
-"aiE c #b93a5f",
-"af2 c #b93b60",
-"al6 c #b93b61",
-"aez c #b93c61",
-"akI c #b93d61",
-"abm c #b93d62",
-"#pQ c #b93e62",
-".4i c #b93f62",
-".7R c #b93f63",
-"b#o c #b94064",
-"bBI c #b94166",
-"bCH c #b94367",
-"bAK c #b94468",
-"bvS c #b9446a",
-"bo0 c #b9456a",
-"bn. c #b9466b",
-"blg c #b9476c",
-"bfV c #b9486c",
-"bpU c #b94a6d",
-"aYQ c #b94d6e",
-"aiU c #b95f77",
-"#qt c #b96d80",
-"#bG c #b96e81",
-".R4 c #b98a8e",
-"#fG c #b98b8f",
-"a6l c #b993a4",
-".gO c #b999ba",
-".kr c #b9a3c5",
-"aaq c #b9abae",
-"a6S c #b9b4b9",
-".jq c #b9b8f3",
-".bO c #b9b9b9",
-".D3 c #b9b9ba",
-"#ZO c #b9b9bc",
-"bON c #b9bdbf",
-"#64 c #b9c0c5",
-"#fk c #b9c1c6",
-"#Tv c #b9c2c6",
-"a9T c #b9c2c7",
-".ka c #b9c2fa",
-"atw c #b9c3c7",
-"#.S c #b9c3c8",
-".QO c #b9c3c9",
-"#ox c #b9c3ca",
-".UF c #b9c4c9",
-"#.b c #b9c4ca",
-"azn c #b9c5ca",
-"#Mn c #b9c6ca",
-".ms c #b9ccf9",
-".s8 c #b9d6a9",
-".oL c #b9d7f1",
-"bxx c #ba3c60",
-"adb c #ba3c61",
-"#bS c #ba3d61",
-"#a5 c #ba3d62",
-"#a4 c #ba3e62",
-"aH# c #ba3e63",
-"#r8 c #ba3f63",
-"bCF c #ba3f64",
-"aIc c #ba4063",
-"a#K c #ba4064",
-"be5 c #ba4066",
-"bDv c #ba4165",
-"bpS c #ba4368",
-"bqC c #ba4369",
-"brq c #ba446b",
-"by3 c #ba456b",
-"aj7 c #ba5f77",
-"ahw c #ba6078",
-"#E0 c #ba6f80",
-"by9 c #ba748d",
-"#Mo c #ba8493",
-".Fw c #ba8687",
-"bP5 c #ba8c8f",
-".hv c #ba9cbc",
-".ie c #ba9dbb",
-"a4n c #baadb4",
-"a7s c #bab0b7",
-".aH c #bababa",
-"aFm c #bababb",
-".Mb c #babec0",
-"aqa c #babec2",
-"aLe c #babfc1",
-"aqf c #babfc2",
-"arP c #babfc3",
-"#cZ c #bac0c3",
-"#yn c #bac3c8",
-".TB c #bac3c9",
-"#2h c #bac4c9",
-".6R c #bac4ca",
-"#.9 c #bac5ca",
-".9r c #bac5cb",
-"#T7 c #bac6ca",
-"#bA c #bacad5",
-"#di c #baccd6",
-".lE c #bacefd",
-"aM0 c #bacfd9",
-"#6C c #badeef",
-".xk c #bafbc7",
-"aj3 c #bb3c61",
-"#af c #bb3d62",
-"#ae c #bb3e62",
-"#oV c #bb3e63",
-".7W c #bb3f63",
-"apH c #bb3f64",
-".9. c #bb4063",
-".7X c #bb4064",
-"aj1 c #bb4164",
-"as4 c #bb4165",
-"bAJ c #bb4166",
-"aoE c #bb4265",
-"bkw c #bb436a",
-"aYP c #bb4467",
-"bx4 c #bb4569",
-"btP c #bb456b",
-"bmu c #bb466c",
-"by2 c #bb496d",
-"bnN c #bb4a6d",
-"bgL c #bb4b6e",
-"bo2 c #bb4c6f",
-"aut c #bb4f6d",
-"atY c #bb6078",
-"biQ c #bb6f82",
-"#CQ c #bb7081",
-"akV c #bb7082",
-"bSN c #bb7188",
-"bU9 c #bb748d",
-".Jr c #bb8a8c",
-"#jR c #bb8a8d",
-"boM c #bb8d90",
-"#Qk c #bb94a2",
-"bUh c #bb969d",
-".bb c #bbbbbb",
-".oY c #bbbdbd",
-"#4z c #bbbfc1",
-".M. c #bbbfc2",
-"#0Z c #bbc0c3",
-".7# c #bbc1c4",
-".KJ c #bbc3c7",
-"#.P c #bbc3c8",
-"#iM c #bbc4c8",
-".VL c #bbc4c9",
-".9i c #bbc4ca",
-".Op c #bbc5ca",
-".4s c #bbc5cb",
-"#bt c #bbc6cb",
-"#ek c #bbc6cc",
-".lI c #bbc9fc",
-"#Fo c #bbcad2",
-".kU c #bbcbff",
-".n2 c #bbd2f1",
-".px c #bbdaec",
-".wY c #bbdc89",
-".2H c #bbe1f3",
-".xO c #bbffc8",
-"aYO c #bc3b60",
-"aZF c #bc3c61",
-"aiO c #bc3e62",
-"aiP c #bc3e63",
-"##p c #bc3f63",
-"#kG c #bc3f64",
-"#lA c #bc4063",
-".6B c #bc4064",
-"#cJ c #bc4164",
-"#cI c #bc4165",
-".7Y c #bc4265",
-"bzW c #bc4368",
-"apI c #bc4467",
-"bw0 c #bc4468",
-"bwZ c #bc456b",
-"blR c #bc466c",
-"bpR c #bc4a70",
-"bon c #bc4e70",
-"ajX c #bc5673",
-"aWP c #bc607c",
-"aBt c #bc6e81",
-"#GG c #bc7082",
-"bgh c #bc7c95",
-".Ht c #bc7f84",
-".E1 c #bc8787",
-"#Sj c #bc8ba2",
-".7D c #bc8c8f",
-"##6 c #bc8d90",
-".l. c #bca9c6",
-".fM c #bcbcbc",
-"aak c #bcc1c4",
-"bng c #bcc1c5",
-".H1 c #bcc3c6",
-"##C c #bcc4c8",
-"bM2 c #bcc4c9",
-".4u c #bcc5c9",
-"#KS c #bcc5ca",
-".Up c #bcc5cb",
-"a2K c #bcc6ca",
-"#lS c #bcc6cb",
-"##W c #bcc6cc",
-"#.W c #bcc7cc",
-"#qT c #bcc7cd",
-"bVk c #bcc8cf",
-".lJ c #bcc8fb",
-".lH c #bccbfc",
-"#ed c #bcccd4",
-".lF c #bccefe",
-".py c #bcd9ea",
-"#9k c #bcdeee",
-"#4k c #bcdff0",
-".ws c #bce9b0",
-"#8T c #bd3a60",
-"bFK c #bd3b5f",
-"aYN c #bd3c61",
-"aiQ c #bd3e63",
-"#ah c #bd3f62",
-"#.G c #bd3f63",
-".5p c #bd3f64",
-".9U c #bd4064",
-"#xo c #bd4065",
-"#lB c #bd4165",
-"aG4 c #bd4265",
-".6C c #bd4366",
-"by4 c #bd4367",
-"aho c #bd4466",
-"#sI c #bd4467",
-"#uw c #bd4567",
-"bvT c #bd4569",
-"bzV c #bd456b",
-"boZ c #bd466c",
-"blh c #bd476d",
-".a6 c #bd4bb9",
-"bqB c #bd5275",
-"alm c #bd627a",
-".IR c #bd687b",
-"axt c #bd6e81",
-"asu c #bd6f81",
-"#qo c #bd6f82",
-"ade c #bd7183",
-"a.v c #bd738a",
-"aMh c #bd7f85",
-".Fx c #bd8889",
-"#9A c #bd8d8d",
-"aFC c #bd8d90",
-"#9Y c #bd9bac",
-"a8a c #bd9ca7",
-"a30 c #bdb3b8",
-"QtX c #bdbdbd",
-".HP c #bdbfc1",
-"aqg c #bdc2c6",
-"#sA c #bdc3c6",
-"blY c #bdc3c8",
-"#NM c #bdc5c7",
-"#Uu c #bdc5c9",
-".Z0 c #bdc5ca",
-"bhF c #bdc6c9",
-"#ej c #bdc6ca",
-"#AJ c #bdc6cb",
-"aof c #bdc7cb",
-"#bv c #bdc7cc",
-".Z1 c #bdc7cd",
-"#ar c #bdc8cd",
-".nh c #bdccf0",
-".lG c #bdcdfd",
-".oO c #bdd4eb",
-"aqS c #bde0f1",
-".vm c #bde1a5",
-"bHs c #be395d",
-"bxz c #be3a5e",
-"bJD c #be3c5f",
-"aZG c #be3c61",
-"#99 c #be3d60",
-"aZH c #be3d62",
-"#ag c #be3e62",
-"#n4 c #be3e63",
-"bFL c #be3f62",
-"bui c #be3f63",
-"#ad c #be3f64",
-"a#J c #be4063",
-".7T c #be4064",
-"#nk c #be4065",
-"bAL c #be4164",
-"aL1 c #be4165",
-"#of c #be4166",
-"abx c #be4265",
-"#uy c #be4467",
-"ahp c #be4567",
-"#ux c #be4568",
-"ada c #be4667",
-"adc c #be4668",
-"ahq c #be4669",
-"bok c #be466c",
-"#a6 c #be4769",
-"bmt c #be476d",
-"aoH c #be496a",
-"beg c #be4b6d",
-"bnb c #be5172",
-"adr c #be637b",
-"aPz c #be6476",
-"auZ c #be6d80",
-"aFI c #be6f82",
-".Y5 c #be7082",
-"#eI c #be7183",
-"#bQ c #be7283",
-"#Hw c #be7384",
-"a1N c #be7f98",
-".IU c #be8b8c",
-".Ld c #be8c8e",
-"azN c #be8e91",
-"#ZY c #beb7c8",
-"#zz c #bec1c4",
-"#3D c #bec2c5",
-"#1X c #bec2c6",
-"#dT c #bec3c6",
-"blp c #bec3c7",
-"#3A c #bec6ca",
-"bRE c #bec6cb",
-"bDR c #bec6cc",
-".kb c #bec6ff",
-"aPu c #bec7cb",
-".PR c #bec7cc",
-"#xc c #bec7cd",
-".QS c #bec8cc",
-"#.d c #bec8cd",
-"#de c #bec8ce",
-"aBB c #bec9cd",
-"#.g c #bec9ce",
-"a3Y c #bec9cf",
-"boy c #bec9d3",
-"#Un c #becacf",
-".kV c #becbfe",
-"#Gc c #bed0d9",
-"#CB c #bed4df",
-".st c #bed5ab",
-".2D c #bed6e7",
-"#Gg c #bed7e3",
-".n0 c #bed7f5",
-".vX c #bee6aa",
-".xP c #befcc0",
-"bK2 c #bf3e61",
-"bJF c #bf3e62",
-"bJE c #bf3f62",
-"aqy c #bf3f64",
-".7S c #bf4064",
-"#rg c #bf4065",
-"#a3 c #bf4165",
-"#uz c #bf4668",
-"#sG c #bf4769",
-"#tA c #bf486a",
-"#cK c #bf496a",
-"ajR c #bf4a6b",
-"buM c #bf4a6e",
-"bjJ c #bf4a6f",
-"bse c #bf4b6f",
-"btQ c #bf5474",
-"#Bx c #bf6f82",
-"#JT c #bf798e",
-"bad c #bf8d99",
-"aar c #bf9295",
-"#Fz c #bf9396",
-"a85 c #bf99a7",
-"blX c #bfa1a8",
-".qy c #bfb05a",
-".pa c #bfbfbf",
-".E. c #bfbfc0",
-"#xx c #bfc3c5",
-"aDo c #bfc3c6",
-"aVY c #bfc5c8",
-"#8h c #bfc6ca",
-"#c8 c #bfc7cc",
-".QP c #bfc8cd",
-"#ct c #bfc8ce",
-"bjP c #bfc9cd",
-".8a c #bfc9ce",
-".8x c #bfc9cf",
-"bbs c #bfcace",
-"aKc c #bfcacf",
-".HW c #bfccd6",
-".q6 c #bfd1ba",
-".nd c #bfd3f7",
-"#04 c #bfdbe9",
-"ae0 c #bfdfef",
-".wV c #bff2b8",
-"bDw c #c03e61",
-"a#B c #c03e62",
-"a#C c #c03f63",
-"##o c #c03f64",
-".6A c #c04064",
-".7U c #c04065",
-"#wU c #c04165",
-"aJ7 c #c04166",
-"#uA c #c04a6b",
-"ant c #c04b6b",
-"#sH c #c04b6c",
-"aiF c #c04c6c",
-"bvR c #c04e71",
-"bkx c #c05373",
-"brs c #c05574",
-".ec c #c06db0",
-"#Di c #c07083",
-"bUc c #c0758d",
-"aHR c #c08e91",
-"bRw c #c08f91",
-"ax3 c #c09092",
-".lT c #c0b0c9",
-"aTH c #c0b7bb",
-".ER c #c0c0c0",
-".Et c #c0c0c1",
-"#17 c #c0c2c4",
-".Gy c #c0c3c4",
-"#tg c #c0c3c5",
-"ar0 c #c0c4c8",
-".kc c #c0c6ff",
-"#at c #c0c7cc",
-".XS c #c0c8cc",
-"bbc c #c0c8cd",
-"#Pv c #c0c9cd",
-"#Ml c #c0c9ce",
-"#co c #c0c9cf",
-"#8g c #c0cace",
-".8p c #c0cacf",
-".8y c #c0cad0",
-".kW c #c0cafe",
-"aQg c #c0cbcf",
-"aDp c #c0cbd0",
-"a9D c #c0ccd0",
-"#nG c #c0ccd2",
-"bCV c #c0ccd7",
-"bou c #c0cdd2",
-".oP c #c0d4e9",
-"#SZ c #c0e0f0",
-".xl c #c0fdc1",
-"btz c #c13d61",
-"##q c #c13e61",
-"#8S c #c13f63",
-"a.g c #c14064",
-".5o c #c14065",
-"aj2 c #c14165",
-"#jT c #c14166",
-"alj c #c14a6b",
-"aG3 c #c14b6c",
-"af1 c #c14c6c",
-"#bT c #c14d6d",
-"aby c #c14e6e",
-"aqM c #c14f6e",
-"aj4 c #c14f6f",
-"aoG c #c1506f",
-"bmw c #c15776",
-"blj c #c15877",
-"bil c #c15c78",
-"a#2 c #c17283",
-"#vy c #c17285",
-"bbv c #c1768f",
-"a81 c #c18596",
-"a3e c #c192a1",
-".E# c #c1c0c1",
-".EQ c #c1c2c2",
-"bne c #c1c2c6",
-"#p2 c #c1c4c6",
-"#X# c #c1c4c7",
-"a#c c #c1c5c7",
-"agK c #c1c7cb",
-"brx c #c1c8cd",
-"#ch c #c1c9cd",
-"#vh c #c1c9ce",
-"#qP c #c1cace",
-"#.. c #c1cacf",
-"#mQ c #c1cad0",
-"#P6 c #c1cbcf",
-"##Q c #c1cbd0",
-"bnT c #c1cbd1",
-"#8e c #c1ccd1",
-".ni c #c1ccef",
-"beo c #c1cfd3",
-".mt c #c1cff9",
-".n5 c #c1d1ec",
-"bbb c #c1d2db",
-"bdq c #c1d3dc",
-".KA c #c1d5e1",
-"#O6 c #c1e0f0",
-"bxy c #c23e62",
-".7V c #c24065",
-"aHI c #c24066",
-"atL c #c24166",
-"ajL c #c24b6c",
-"anU c #c24e6e",
-"amH c #c24f6f",
-"afY c #c2506e",
-"aiY c #c2506f",
-"abw c #c25170",
-"asx c #c25270",
-"btO c #c25b79",
-"ajW c #c25f79",
-"baz c #c2637e",
-"aAc c #c27184",
-"anQ c #c27586",
-"bRq c #c2758c",
-"bFQ c #c2768e",
-"#KE c #c28399",
-"aoM c #c29194",
-"aFu c #c29aa7",
-"#Sa c #c2bdd0",
-"beA c #c2c1c7",
-".D9 c #c2c2c1",
-".D2 c #c2c2c2",
-".k0 c #c2c6fd",
-"arQ c #c2c8cc",
-"bOL c #c2c9ce",
-"#jt c #c2cace",
-".Oh c #c2cacf",
-".lK c #c2cafb",
-".kX c #c2cafd",
-"#v3 c #c2cbcf",
-"#fu c #c2cbd0",
-"#gj c #c2cbd1",
-"#Qt c #c2ccd0",
-"#bw c #c2ccd1",
-".8z c #c2ccd2",
-"azm c #c2cdd1",
-"#CC c #c2d2da",
-".n1 c #c2d8f4",
-"#G1 c #c2ddea",
-"alp c #c2dfed",
-"amO c #c2e0ef",
-"bRl c #c33d60",
-"bwC c #c33f63",
-"#n3 c #c34065",
-"#iS c #c34066",
-"#qw c #c34166",
-"aus c #c3486b",
-"bfW c #c34f6f",
-"al7 c #c3506f",
-"#a7 c #c35270",
-"aiG c #c35371",
-"amL c #c35472",
-"aFL c #c35a76",
-"bhz c #c35b78",
-"bqE c #c35b79",
-"bsd c #c3677e",
-"adv c #c3697f",
-"#eM c #c36d82",
-"a5. c #c36e82",
-"a1L c #c37084",
-"#iW c #c37385",
-"aiu c #c37685",
-".dI c #c379d5",
-"bjn c #c39193",
-"aSp c #c39194",
-"bay c #c3aab8",
-"blm c #c3c3c9",
-".kd c #c3c6ff",
-"axI c #c3c7ca",
-"aud c #c3c7cb",
-"bnU c #c3c8ca",
-"a.1 c #c3c9cb",
-"#el c #c3c9cc",
-"bVn c #c3cacd",
-".4v c #c3cace",
-"bJW c #c3cbce",
-"#LO c #c3cbcf",
-"#sC c #c3cbd0",
-"bgR c #c3cccf",
-".TE c #c3ccd0",
-"#.c c #c3ccd1",
-"#hM c #c3ccd2",
-"#rF c #c3cdd1",
-".0Z c #c3cdd2",
-"#BX c #c3ced3",
-"#G2 c #c3d1d9",
-".WP c #c3e1ef",
-".ui c #c3e29b",
-".xo c #c3efa3",
-"a.. c #c43b5e",
-"#h4 c #c44167",
-"aeE c #c4516f",
-"auW c #c45170",
-"ad# c #c45270",
-"bcQ c #c45271",
-"#uB c #c45472",
-"bs2 c #c45475",
-"#a8 c #c45573",
-"aeF c #c45674",
-"aoF c #c45774",
-"be4 c #c45e7a",
-"a9I c #c47288",
-"a.r c #c4758b",
-"buQ c #c4778e",
-"#Nl c #c47a8e",
-"#Mp c #c47b90",
-".PX c #c4848d",
-".IQ c #c48694",
-".TT c #c49194",
-"bDJ c #c49396",
-".gB c #c49be7",
-"aEj c #c49caa",
-"a4N c #c4b0b5",
-"#U9 c #c4bcce",
-".jr c #c4c0ff",
-"afd c #c4c5c8",
-".Gt c #c4c6c7",
-"#J9 c #c4c7ca",
-"ab9 c #c4c8ca",
-".kZ c #c4c8fd",
-"#eZ c #c4c9cc",
-"bLk c #c4c9ce",
-"#Tw c #c4cacd",
-".kY c #c4cafe",
-"#9L c #c4cbce",
-"#qd c #c4cbcf",
-"#iE c #c4ccd0",
-".RP c #c4ccd1",
-"#aJ c #c4ccd2",
-".YU c #c4cdd1",
-".Uq c #c4cdd2",
-"#bu c #c4cdd3",
-"#v5 c #c4ced2",
-"#.Z c #c4ced3",
-"#H2 c #c4ced4",
-"bf2 c #c4d0d3",
-".ng c #c4d1f3",
-"bf# c #c4d2d5",
-"b#V c #c4d3d7",
-"#.5 c #c4d9e8",
-"#.3 c #c4dce7",
-".Uy c #c4ddea",
-"#Af c #c4e5f5",
-"bPT c #c53d60",
-"brh c #c54066",
-"aMW c #c54167",
-"#oW c #c55473",
-"aiR c #c55674",
-"amI c #c55775",
-"aiX c #c55875",
-"aj8 c #c55975",
-"abv c #c55976",
-"bs4 c #c55e7b",
-"bpV c #c5607c",
-"af4 c #c56a81",
-".I7 c #c56f82",
-".45 c #c57386",
-"#IY c #c57588",
-"bz1 c #c5768c",
-".IS c #c57986",
-"#OA c #c57a8d",
-".H. c #c5898d",
-"bbi c #c59cac",
-"bll c #c5a0a9",
-".mC c #c5b9cc",
-".nm c #c5bfcd",
-"#KC c #c5c2c8",
-".k1 c #c5c7fc",
-".5y c #c5c9ca",
-"afg c #c5c9cb",
-"#qc c #c5cacd",
-".8s c #c5cbce",
-"#d# c #c5cbcf",
-"#fo c #c5ccd0",
-".Mg c #c5cdd0",
-"#Ad c #c5cdd1",
-"#aG c #c5cdd2",
-"bi8 c #c5ced1",
-"#.V c #c5ced2",
-".9n c #c5ced3",
-".8w c #c5ced4",
-"#Wj c #c5cfd3",
-".8A c #c5cfd4",
-"avJ c #c5cfd5",
-"aaL c #c5d0d4",
-".Tj c #c5d2d8",
-"bcJ c #c5d2d9",
-"#HS c #c5e1ef",
-".xm c #c5febc",
-"#vt c #c64c6e",
-"aqz c #c64e6f",
-"b#Y c #c65573",
-"akJ c #c65874",
-"aiH c #c65976",
-"aiZ c #c65a76",
-"brp c #c65c79",
-"b.i c #c6627c",
-"boo c #c6627d",
-"aV8 c #c6637c",
-"a#R c #c66780",
-"atX c #c66a80",
-".KO c #c66d81",
-"a#Q c #c67288",
-"#fO c #c67486",
-"btR c #c6778d",
-"#Uj c #c68296",
-"bbw c #c68a9e",
-".js c #c6c0ff",
-".ke c #c6c6fe",
-"af# c #c6c8cb",
-".9d c #c6c9cb",
-"am2 c #c6c9cc",
-"aq9 c #c6cacd",
-"#ap c #c6cbcd",
-".IL c #c6cbce",
-"#kQ c #c6cccf",
-"#kR c #c6cdd0",
-"#MG c #c6cdd1",
-"#kl c #c6ced2",
-"#dh c #c6ced3",
-"#A# c #c6ced4",
-"#mi c #c6cfd3",
-".5B c #c6cfd4",
-"##N c #c6cfd5",
-"bge c #c6d0d3",
-"#J8 c #c6d0d4",
-"##M c #c6d0d5",
-"#C3 c #c6d0d6",
-"acy c #c6d1d5",
-".SH c #c6d5dc",
-"aaa c #c6e1ee",
-"an0 c #c6e1ef",
-".aS c #c750cc",
-"b#p c #c75875",
-"b.S c #c75a76",
-"apZ c #c75c78",
-"bgK c #c7627d",
-"bo3 c #c7637e",
-"bbn c #c76b84",
-"ahK c #c77083",
-"#B9 c #c77386",
-"#Pg c #c77589",
-"a.u c #c7758b",
-"bCL c #c7768c",
-"bSM c #c7778e",
-"#Oo c #c7788c",
-"#Tg c #c78095",
-".Qh c #c79396",
-".6i c #c79496",
-"#Wa c #c7b9c9",
-"bnR c #c7bbbf",
-"bdv c #c7bbc3",
-".6N c #c7cacb",
-".8. c #c7cacc",
-"#Y# c #c7cbcd",
-"#Ob c #c7cccf",
-"bVq c #c7ccd2",
-".5D c #c7ced2",
-"#c3 c #c7cfd3",
-"#aH c #c7cfd4",
-"#p7 c #c7cfd5",
-"#bs c #c7d0d4",
-".8B c #c7d0d5",
-"#Gk c #c7d0d6",
-"#Hi c #c7d1d5",
-"#CD c #c7d1d6",
-".3W c #c7e3f1",
-".wW c #c7f6b0",
-".#G c #c82bc8",
-"#a9 c #c85371",
-"#bR c #c85775",
-"bvU c #c85875",
-"aht c #c85b77",
-"#dx c #c85e79",
-"aiN c #c85e7a",
-"ai0 c #c85f7a",
-"#bU c #c8607b",
-"ajV c #c86880",
-"agi c #c86e84",
-"#Q# c #c87386",
-"#On c #c87487",
-"#Wc c #c87488",
-"avv c #c87688",
-"btT c #c8768d",
-"#KD c #c87789",
-"#EX c #c87d8b",
-"#Nk c #c88394",
-"#IV c #c88696",
-"bCO c #c88c92",
-"aLi c #c89496",
-"aEq c #c89497",
-"bnu c #c89598",
-"#JR c #c89aa7",
-".jt c #c8c0ff",
-".GN c #c8c4c4",
-".kf c #c8c5fe",
-"#SD c #c8cbcd",
-"bbg c #c8cccf",
-"#oL c #c8cdd1",
-".93 c #c8ced2",
-"#xa c #c8cfd3",
-"#jC c #c8d0d4",
-".Pc c #c8d0d5",
-"#Lq c #c8d0d6",
-"#.f c #c8d1d5",
-"#cn c #c8d1d6",
-"#Bg c #c8d2d6",
-"aLd c #c8d2d7",
-"awk c #c8d2d9",
-"a#f c #c8d3d8",
-".ne c #c8d6f7",
-"#G7 c #c8d7df",
-"#WH c #c8e2ef",
-"add c #c9617b",
-"arA c #c9627d",
-"ap0 c #c9637d",
-"bct c #c97487",
-"bdg c #c97488",
-"akU c #c97588",
-"bHw c #c9758b",
-"#eH c #c97689",
-"bCN c #c97788",
-"#KI c #c9788b",
-"a66 c #c98b9d",
-"bST c #c99597",
-"#OD c #c9b9bc",
-"adS c #c9caca",
-".Ko c #c9cccd",
-".0R c #c9ccce",
-"#in c #c9cfd3",
-"bJZ c #c9cfd4",
-"#Vo c #c9d0d3",
-"##S c #c9d0d4",
-"#gv c #c9d1d4",
-".YH c #c9d1d5",
-"##P c #c9d1d6",
-".mu c #c9d1f9",
-"#br c #c9d2d6",
-"#w8 c #c9d2d7",
-"bis c #c9d3d6",
-"#C7 c #c9d3d7",
-"#4D c #c9d3d8",
-"a5W c #c9d4d8",
-"#Fp c #c9d4d9",
-".nf c #c9d5f5",
-"#cu c #c9d7df",
-"#HX c #c9dde7",
-"auX c #ca5f7b",
-"aeA c #ca6079",
-"abz c #ca637d",
-"#tB c #ca647d",
-"ali c #ca647e",
-"ale c #ca667f",
-"bsf c #ca6882",
-"#a2 c #ca6981",
-"#JS c #ca7285",
-"aFJ c #ca7286",
-"aG2 c #ca7487",
-"#B8 c #ca7488",
-"#kz c #ca7588",
-"#Ti c #ca7589",
-"#r2 c #ca7789",
-"buT c #ca778c",
-"#tD c #ca788a",
-"#JV c #ca788b",
-"#Vf c #ca7c8e",
-".G9 c #ca9092",
-".Hv c #ca9192",
-"aEr c #ca9598",
-"aTT c #ca9699",
-".iH c #cabcff",
-"#U. c #cabec6",
-".kg c #cac5fe",
-"#Vq c #cac7c9",
-".lL c #cacdfa",
-"#3y c #caced0",
-".KD c #cacfd2",
-"bF6 c #cacfd3",
-".8r c #cad0d3",
-"bWr c #cad0d4",
-"#ZI c #cad1d4",
-"#Fq c #cad1d5",
-"aPq c #cad1d6",
-"#tt c #cad2d5",
-"#bx c #cad2d6",
-".4r c #cad2d7",
-"#Ts c #cad3d6",
-"#fw c #cad3d7",
-"#iq c #cad3d8",
-".n7 c #cad3e7",
-"#Wi c #cad4d8",
-"a4e c #cad5d9",
-".3R c #caddeb",
-"#IJ c #cae4f0",
-".xn c #cafdb4",
-"anT c #cb5f7a",
-"#sJ c #cb627d",
-"ai1 c #cb667f",
-"ahr c #cb677f",
-"#cL c #cb6780",
-"#dy c #cb6880",
-"apJ c #cb6981",
-".Ot c #cb7386",
-"#Ri c #cb7387",
-"#Th c #cb7487",
-"arz c #cb7588",
-"aym c #cb7589",
-"#lt c #cb7689",
-"bV. c #cb768a",
-"#Sm c #cb778a",
-"a.s c #cb778c",
-"#Uk c #cb788a",
-".Hw c #cb9393",
-".0l c #cb9698",
-"aTI c #cba2a4",
-"#R. c #cba9b4",
-"a3b c #cbb1b5",
-".iI c #cbbbff",
-".ju c #cbc0ff",
-".ki c #cbc3fd",
-".kh c #cbc4fe",
-"#H4 c #cbc7cd",
-".k2 c #cbc8fc",
-".Gz c #cbcbcb",
-"ayA c #cbcdce",
-".3d c #cbced0",
-"#Qp c #cbcfd1",
-"#wy c #cbd2d6",
-"#gC c #cbd2d7",
-".64 c #cbd3d7",
-"#vn c #cbd3d8",
-"a7F c #cbd3da",
-"#WU c #cbd4d7",
-"#ul c #cbd4d8",
-"#lN c #cbd4d9",
-"#Ni c #cbd5d8",
-"#Uq c #cbd5d9",
-"bnS c #cbd5da",
-"baa c #cbd6da",
-"bbt c #cbd7db",
-"#Ls c #cbd8da",
-"a5Y c #cbd8db",
-"bax c #cbd8dc",
-".XR c #cbd9e1",
-".UG c #cbdeeb",
-"#Zu c #cbe3ee",
-"#Ll c #cbe6f3",
-".wX c #cbf5a6",
-"#qn c #cc5e7a",
-"aiB c #cc617a",
-"aiD c #cc637b",
-"ajG c #cc637c",
-"#q9 c #cc637d",
-"#uC c #cc637e",
-"#dw c #cc6780",
-"abA c #cc6880",
-"asw c #cc6881",
-"aiI c #cc6981",
-"#Lu c #cc7183",
-"bvV c #cc7288",
-"#Qj c #cc7387",
-"#By c #cc7589",
-"##b c #cc7689",
-"#dK c #cc778c",
-"#o0 c #cc798b",
-"#Tj c #cc8394",
-"#Om c #cc8999",
-"b#2 c #cc8e9d",
-".Hx c #cc9394",
-".Hu c #cc9494",
-".Jl c #cc9495",
-"#ms c #cc9698",
-".RX c #cc9d9f",
-"a9M c #cc9ea9",
-"#Ul c #cc9eab",
-"#Wd c #ccaeb8",
-"bab c #ccc0c8",
-"b#C c #cccbd0",
-"bgf c #ccccd1",
-".GJ c #cccdcf",
-"#IU c #cccdd2",
-"acz c #ccced1",
-"baM c #ccced3",
-"#8p c #cccfd1",
-"bQa c #ccd1d4",
-"#Zt c #ccd2d4",
-".Mo c #ccd2d5",
-"#gs c #ccd2d6",
-".On c #ccd3d7",
-"#sB c #ccd4d7",
-"#xE c #ccd4d8",
-".0Y c #ccd4d9",
-"bp4 c #ccd4db",
-"#Nh c #ccd5d8",
-"#xb c #ccd5d9",
-"#EB c #ccd5da",
-"bRC c #ccd5db",
-"bza c #ccd5dd",
-"#DF c #ccd6d9",
-"a48 c #ccd6da",
-".JU c #ccd7df",
-"arE c #cce8f5",
-".#x c #cd2cce",
-"ajM c #cd637c",
-"ad. c #cd657d",
-"buO c #cd657f",
-"ahn c #cd667d",
-"amG c #cd667f",
-"aVi c #cd677e",
-"aFK c #cd6881",
-"abu c #cd6980",
-"aiW c #cd6a82",
-"aiM c #cd6b82",
-"amJ c #cd6b83",
-"apY c #cd6c84",
-"#vu c #cd6d84",
-"brt c #cd6f86",
-".MB c #cd7185",
-"#Ph c #cd7384",
-"adu c #cd7388",
-"#IW c #cd7588",
-"asv c #cd7689",
-".7y c #cd7789",
-"#eB c #cd778a",
-"a.t c #cd778c",
-"afD c #cd818c",
-"acq c #cd9395",
-"bFY c #cd989b",
-"#Sk c #cd9eaf",
-"a5C c #cdaab0",
-".iY c #cdb4d0",
-".iJ c #cdbbff",
-".np c #cdc0c6",
-".jv c #cdc0fe",
-"#C2 c #cdd0d2",
-"aqZ c #cdd1d3",
-"blZ c #cdd1d4",
-"agL c #cdd2d4",
-".nj c #cdd2ed",
-"#03 c #cdd3d6",
-".My c #cdd3d7",
-"#cq c #cdd4d7",
-"#XQ c #cdd4d8",
-"#gw c #cdd4d9",
-"#8k c #cdd5d8",
-"#aF c #cdd5d9",
-".8v c #cdd5da",
-"#C9 c #cdd6d9",
-"#DE c #cdd6da",
-"#EA c #cdd6db",
-".n6 c #cdd6ea",
-"baL c #cdd7da",
-"bmB c #cdd7db",
-"bfa c #cdd9dc",
-".rO c #cddba7",
-"ai4 c #cde3ee",
-"ao7 c #cde4ef",
-".SD c #cdecfb",
-"aQn c #ce677d",
-"aoI c #ce697f",
-"#cH c #ce6c83",
-"#dz c #ce6d84",
-"abB c #ce6e85",
-"aiV c #ce6f85",
-"#cx c #ce778a",
-"#r. c #ce788b",
-"#Ve c #ce91a1",
-".e6 c #ce91e9",
-"a4i c #ceadbc",
-"aic c #ceb5b6",
-".kj c #cec3fd",
-"a43 c #cec6cc",
-"#Vg c #cec6cd",
-".IO c #ced0d1",
-"#QD c #ced0d2",
-"#.2 c #ced2d4",
-"avP c #ced2d5",
-"#f7 c #ced3d5",
-".SB c #ced3d7",
-".6V c #ced4d7",
-"#gE c #ced4d8",
-"#hD c #ced5d8",
-".SK c #ced5d9",
-"#xD c #ced5da",
-"#fe c #ced6da",
-".8C c #ced6db",
-"bqM c #ced6dc",
-"#tq c #ced7da",
-"#C8 c #ced7db",
-"#y0 c #ced7dc",
-"bcf c #ced8dc",
-"bLe c #ced8df",
-"a5V c #ced9dc",
-"#Um c #cedade",
-".wu c #ceeb87",
-".aa c #cf3ccd",
-"#dF c #cf637e",
-"#dE c #cf6580",
-"ao3 c #cf6881",
-"aLn c #cf6a80",
-"aiJ c #cf6f86",
-"#dA c #cf7086",
-"aln c #cf7187",
-"bqF c #cf7388",
-"#vv c #cf7389",
-"bDD c #cf7688",
-"#Ej c #cf778b",
-"bs7 c #cf788d",
-"aF4 c #cf828e",
-"ais c #cf8590",
-"#Pf c #cf8e9e",
-".GR c #cf9494",
-".Hy c #cf9596",
-"bb5 c #cf97a5",
-".Nh c #cf989a",
-"a3t c #cf99a7",
-"#Q. c #cf9aa8",
-"bUj c #cf9da4",
-"a.j c #cfa1ab",
-".h0 c #cfb6ff",
-".iK c #cfbbff",
-".jw c #cfbffe",
-"a65 c #cfc6cc",
-"#V3 c #cfc8cd",
-".Iw c #cfcfcf",
-"aCb c #cfd0d0",
-".oZ c #cfd1cd",
-"#.M c #cfd1d2",
-".GH c #cfd1d3",
-"#vb c #cfd2d3",
-"#Xb c #cfd2d4",
-"bIN c #cfd3d6",
-".mv c #cfd3f8",
-"#4G c #cfd4d7",
-"#px c #cfd5d8",
-"#lP c #cfd6d9",
-".4J c #cfd6da",
-"#NC c #cfd6db",
-".n8 c #cfd6e9",
-"#kj c #cfd7da",
-"##L c #cfd7db",
-"#vg c #cfd7dc",
-"#D. c #cfd8db",
-"#U2 c #cfd8dc",
-"#X. c #cfd8dd",
-"#JQ c #cfdadd",
-"#IO c #cfe4ee",
-"asA c #cfe9f5",
-"alf c #d0637e",
-"#pH c #d0647f",
-"aRo c #d0687e",
-"a#S c #d06880",
-"alg c #d06882",
-"a.i c #d06c81",
-"a#M c #d06c82",
-"#dC c #d06f86",
-"#dB c #d07087",
-"ao4 c #d07187",
-"af3 c #d07288",
-"anV c #d07388",
-"aiK c #d07389",
-"#bV c #d07489",
-"bo4 c #d0748a",
-"ajU c #d07589",
-"ads c #d0768a",
-"bcv c #d0778b",
-"#Pq c #d0788b",
-"#vx c #d0798c",
-".KP c #d0808e",
-".Na c #d0989a",
-"#bK c #d0999b",
-"aVg c #d0a3a5",
-".hg c #d0adfa",
-"a5Z c #d0b0ba",
-".h1 c #d0b6ff",
-"ai. c #d0c4c6",
-"a44 c #d0c4cc",
-".oc c #d0c9c7",
-".lM c #d0cefa",
-".mx c #d0d1f4",
-"#AL c #d0d2d4",
-".mw c #d0d2f6",
-".IB c #d0d3d3",
-"#Sq c #d0d3d5",
-"aap c #d0d4d6",
-"#4F c #d0d4d7",
-"#Us c #d0d5d7",
-"aj9 c #d0d5d8",
-".QR c #d0d6d9",
-"#bB c #d0d6da",
-".6Q c #d0d7da",
-".Qr c #d0d7db",
-"#xd c #d0d7dc",
-"#DK c #d0d8db",
-"#th c #d0d8dc",
-"#Mm c #d0d9dc",
-"#Wf c #d0d9dd",
-"a4m c #d0dadd",
-"#W9 c #d0dade",
-"#We c #d0dbdf",
-"#cd c #d0dbe1",
-".Ss c #d0e0e9",
-"#.p c #d0e0ed",
-"#T0 c #d0e4ee",
-"#JF c #d0e5ef",
-"#R5 c #d0e7f2",
-".wt c #d0f59f",
-"avs c #d1637f",
-"aUz c #d1687e",
-"aeD c #d16d82",
-"#dD c #d16d85",
-"#eN c #d17187",
-"ahs c #d17288",
-"abC c #d17489",
-"aiL c #d1758a",
-"#jP c #d1768a",
-"#aR c #d1768b",
-".6f c #d1778b",
-".7A c #d1778c",
-"#IX c #d1788b",
-"bs8 c #d1788c",
-"#eG c #d1798c",
-"#R# c #d17e8e",
-"a4f c #d18a9b",
-"#W7 c #d18b9c",
-"a2s c #d18d9b",
-".Le c #d19899",
-".QX c #d1999a",
-".Ra c #d19a9c",
-".jx c #d1bffd",
-".k3 c #d1cafb",
-".my c #d1d0f2",
-".F0 c #d1d2d3",
-"#y2 c #d1d4d5",
-"#Jb c #d1d4d6",
-"#Vn c #d1d6d8",
-"#XR c #d1d6d9",
-"#Tt c #d1d7d9",
-".RQ c #d1d7da",
-".8t c #d1d7db",
-".Sp c #d1d7dc",
-"#bq c #d1d8db",
-".0X c #d1d8dc",
-"#tp c #d1d9dc",
-"#Lr c #d1d9dd",
-"bpi c #d1d9df",
-"#Oj c #d1dadd",
-"#T8 c #d1dade",
-"ah2 c #d1dbdf",
-"#Vh c #d1dce0",
-"ady c #d1e4ee",
-"#T4 c #d1e9f5",
-".uO c #d1ec91",
-"aSg c #d2697e",
-"aI6 c #d26e85",
-"a3u c #d27489",
-"bhA c #d27689",
-"alh c #d2768b",
-".X5 c #d2778b",
-"#kA c #d2778c",
-".35 c #d2788c",
-"agj c #d2788d",
-"#bI c #d2798c",
-"#DT c #d2798d",
-"baN c #d28294",
-"aj# c #d2838d",
-"#Sl c #d28b9b",
-"a6y c #d28e9f",
-"bpW c #d29a9c",
-"ayS c #d29a9d",
-"aEh c #d29c9e",
-"bbu c #d2a4af",
-".h2 c #d2b6ff",
-".iL c #d2bbff",
-".jy c #d2bffe",
-".kk c #d2c4fd",
-".FS c #d2d2d3",
-"aY3 c #d2d4d7",
-"acu c #d2d5d7",
-"aCM c #d2d5d8",
-"#3w c #d2d6d8",
-"bM3 c #d2d7d8",
-".PK c #d2d7da",
-"#eh c #d2d8db",
-".Vu c #d2d8dc",
-"#ci c #d2d9dc",
-".YG c #d2d9dd",
-"#B0 c #d2dadd",
-"#wB c #d2dade",
-"#D5 c #d2dbde",
-"adV c #d2dbdf",
-".LI c #d2dbe1",
-"aTJ c #d3697f",
-"a#T c #d36c83",
-"auY c #d36e86",
-"aDO c #d37386",
-"abn c #d37387",
-"bs5 c #d3768a",
-"a4g c #d3778c",
-"#JW c #d3788a",
-"#dJ c #d3788c",
-"amK c #d3788d",
-".1E c #d3798c",
-".9I c #d3798d",
-"#bW c #d37a8d",
-"ahx c #d37a8e",
-".US c #d37b8d",
-".8W c #d37b8e",
-"aZX c #d38695",
-"bfC c #d38d96",
-"aS5 c #d38f96",
-".Rf c #d39b9c",
-".jh c #d39ca6",
-".KN c #d39fab",
-"aid c #d3a8aa",
-"b#D c #d3a9b5",
-"a6h c #d3b0b3",
-"#U7 c #d3b9c6",
-"bcg c #d3cdd2",
-".mz c #d3d0f1",
-"agF c #d3d3d5",
-"#CG c #d3d5d7",
-"ar3 c #d3d6d9",
-"bIM c #d3d7d9",
-"aco c #d3d7da",
-"bJY c #d3d8d9",
-"#Sz c #d3d8da",
-"aeZ c #d3d8db",
-".ul c #d3d941",
-".2C c #d3d9dc",
-".O7 c #d3d9dd",
-"#nU c #d3dadd",
-".8u c #d3dade",
-"#2e c #d3dbdd",
-"#Be c #d3dbde",
-"aaA c #d3dbdf",
-"acj c #d3dcdf",
-"a2L c #d3dce0",
-".4U c #d3e2ee",
-".4Z c #d3e4ed",
-"#VU c #d3eaf6",
-"#c7 c #d3edfa",
-"#r1 c #d47087",
-"bi3 c #d47188",
-"akM c #d47387",
-"akS c #d4748a",
-"apX c #d4768b",
-"#vw c #d4788c",
-".46 c #d4788d",
-"buU c #d4798c",
-".7z c #d4798d",
-"bcu c #d4798e",
-"anR c #d47a8d",
-"#dI c #d47a8e",
-"bio c #d47b8d",
-"a#X c #d47b8e",
-"bfT c #d48492",
-"#9X c #d48890",
-"amW c #d48d98",
-"ags c #d48e99",
-".I8 c #d49296",
-"aqC c #d4999d",
-"bSR c #d49c9e",
-".h3 c #d4b6ff",
-".iM c #d4bbfe",
-".jz c #d4bffe",
-".lN c #d4cff9",
-".Gk c #d4d4d4",
-".FZ c #d4d4d5",
-".Ix c #d4d6d7",
-"a45 c #d4d6da",
-"#z8 c #d4d7d8",
-"ae8 c #d4d7d9",
-"ar2 c #d4d7da",
-"bHH c #d4d8d9",
-"bIL c #d4d8da",
-"amN c #d4d8db",
-"#WF c #d4d9db",
-".RO c #d4d9dc",
-".L8 c #d4dadd",
-".WC c #d4dade",
-"#Ut c #d4dbdd",
-"#bp c #d4dbde",
-".9s c #d4dbdf",
-"#v2 c #d4dcdf",
-"ar9 c #d4dce0",
-"#KB c #d4dde0",
-"b.7 c #d4dde1",
-"#bC c #d4dfe6",
-".UD c #d4e6ef",
-".vY c #d4f496",
-".aA c #d54ad2",
-"aSW c #d5697e",
-"avt c #d56e86",
-"anS c #d57188",
-"a#U c #d57287",
-"aqA c #d57289",
-"#8U c #d57386",
-"aH3 c #d5768b",
-"a#V c #d5778b",
-"bac c #d5778c",
-"a#W c #d5788c",
-".2N c #d5788d",
-"bvZ c #d5798c",
-"aAT c #d5798d",
-"#CO c #d5798e",
-"bs6 c #d57a8d",
-"#eC c #d57a8e",
-"a#Y c #d57b8e",
-"adt c #d57b8f",
-".1F c #d57c8e",
-".7B c #d57c8f",
-".47 c #d57d8f",
-"beL c #d57d90",
-"beM c #d57e90",
-"bbj c #d57f91",
-"#6n c #d58592",
-"a0I c #d58695",
-"a4h c #d58697",
-".5. c #d59c9e",
-".hh c #d5b0ff",
-"a2q c #d5b3b5",
-".iN c #d5bafe",
-".k4 c #d5cafb",
-"#6T c #d5d5d8",
-"#Tz c #d5d6d8",
-".KL c #d5d7d9",
-"aia c #d5d8d9",
-"#Wk c #d5d8da",
-"#6K c #d5d9da",
-"#61 c #d5d9db",
-"#SY c #d5dadc",
-".9f c #d5dadd",
-"beB c #d5dbdc",
-"ad8 c #d5dbdd",
-"#hG c #d5dbde",
-".Oo c #d5dbdf",
-"#EM c #d5dcde",
-"#bl c #d5dcdf",
-"#ED c #d5dce0",
-".II c #d5dce1",
-"#ZS c #d5dde0",
-".#4 c #d63ed7",
-".bG c #d65ecf",
-"#tC c #d6748b",
-"akT c #d6758b",
-"#uD c #d6778c",
-"#dG c #d6788d",
-"ahl c #d6798b",
-"bw6 c #d6798c",
-"#bH c #d67a8e",
-"#h0 c #d67b8e",
-"#uF c #d67c8f",
-"#dH c #d67c90",
-"bef c #d67e8f",
-"bfI c #d67f91",
-"bfH c #d68091",
-"ahO c #d68190",
-".MC c #d68492",
-".h# c #d690b6",
-"a49 c #d694a4",
-"#S8 c #d698a5",
-"aKf c #d69d9f",
-"a6U c #d6a2aa",
-"aV5 c #d6a6a7",
-"a0G c #d6a9aa",
-"a2E c #d6b0bc",
-".hi c #d6b0ff",
-".h4 c #d6b6ff",
-".kl c #d6c5fc",
-".lO c #d6cef7",
-".pL c #d6d6bd",
-"aQf c #d6d8da",
-"ac. c #d6d9da",
-"asP c #d6d9db",
-"bVo c #d6dadb",
-"#Sr c #d6dadc",
-".3l c #d6dadd",
-".PG c #d6dbdc",
-"#as c #d6dbdd",
-".8G c #d6dbde",
-"##0 c #d6dbdf",
-"#aA c #d6dcde",
-"#ck c #d6dcdf",
-"#jh c #d6dce0",
-"aOu c #d6dddf",
-".0W c #d6dde0",
-"#Hj c #d6dde1",
-"#4y c #d6dee0",
-"a6f c #d6dee1",
-"#7Q c #d6e5ec",
-"#Ks c #d6e6ed",
-"#P2 c #d6e8f2",
-"#Rf c #d6eaf6",
-"#ec c #d6eefa",
-"a3v c #d77488",
-"ao2 c #d7758b",
-"avu c #d7768c",
-"a2F c #d7778a",
-"#eO c #d7778d",
-"#cM c #d7798b",
-"bz2 c #d7798c",
-"#.v c #d77a8e",
-"#CP c #d77a8f",
-"a#1 c #d77b8d",
-"#fN c #d77b8e",
-"#eF c #d77b8f",
-".Y6 c #d77c8f",
-"ajT c #d77c90",
-".c5 c #d77ce6",
-"bfU c #d78090",
-".6g c #d78091",
-"#lu c #d78092",
-"##c c #d78192",
-"afx c #d78293",
-"#Wb c #d78996",
-"bfp c #d791a0",
-".I9 c #d79599",
-"aMg c #d79b9c",
-"al8 c #d79b9f",
-"aNh c #d79d9e",
-".SV c #d79ea0",
-".SP c #d7a6a7",
-".iO c #d7bbfe",
-".jA c #d7bffe",
-".FY c #d7d6d6",
-".GI c #d7d6d7",
-".FR c #d7d7d7",
-".JN c #d7d8da",
-"bSY c #d7d9db",
-".X0 c #d7dadb",
-"aaH c #d7dadc",
-"#1Z c #d7dbdd",
-"aa# c #d7dbde",
-"#M8 c #d7dcde",
-".LD c #d7dcdf",
-"#hE c #d7dce0",
-"a.9 c #d7ddde",
-".9m c #d7dde0",
-"#es c #d7dde1",
-"#EO c #d7dee0",
-"#gi c #d7dee1",
-"#.s c #d7dee2",
-"#EE c #d7e0e3",
-"#5q c #d7e6ed",
-".dw c #d874c9",
-"a50 c #d8778c",
-"bWd c #d8798c",
-".0j c #d8798e",
-"bFR c #d87a8b",
-"bka c #d87a8e",
-"#dk c #d87a8f",
-"#bX c #d87b8d",
-"a#Z c #d87b8e",
-"#ev c #d87b8f",
-"#uE c #d87b90",
-"anu c #d87c8d",
-"#Dj c #d87c8f",
-"ajS c #d87c90",
-"#eD c #d87d90",
-"bBP c #d87e90",
-"a#P c #d88594",
-"aqB c #d88695",
-"aY6 c #d88795",
-"aHd c #d88993",
-"aje c #d88b98",
-"aOz c #d8989b",
-".Lg c #d89c9e",
-"apN c #d89ea0",
-"#MD c #d8a8a9",
-"a6T c #d8afb2",
-".hj c #d8b0ff",
-".h5 c #d8b6ff",
-"#W8 c #d8c0c8",
-".k5 c #d8cbfa",
-".lP c #d8cef6",
-".nk c #d8d6eb",
-"a#i c #d8dadb",
-"#OR c #d8dbdc",
-"ar1 c #d8dbdd",
-".JY c #d8dcde",
-"#Vj c #d8dcdf",
-".QN c #d8dddf",
-"##F c #d8dde0",
-".Vy c #d8dee1",
-"#nT c #d8dee2",
-"#y4 c #d8dfe1",
-"#hf c #d8dfe2",
-"#6Z c #d8dfe3",
-"#9R c #d8e1e4",
-".Rx c #d8e5ec",
-"#XI c #d8ebf4",
-".vZ c #d8f080",
-".vn c #d8f38b",
-"a6z c #d9768c",
-"bb8 c #d9788a",
-"bgM c #d9798b",
-"buP c #d9798c",
-"bk0 c #d9798f",
-".W3 c #d97b8f",
-".6e c #d97b90",
-".34 c #d97c8f",
-"##4 c #d97c90",
-"ac9 c #d97d8d",
-"bhx c #d98091",
-"apK c #d98293",
-"bfJ c #d98494",
-"#aS c #d98594",
-".K4 c #d98895",
-".Jx c #d99c9d",
-"#8b c #d99e9f",
-"#8n c #d99fa2",
-"bVg c #d9a3aa",
-"bnQ c #d9a5a7",
-"a7t c #d9aeb2",
-"a31 c #d9afb2",
-".iP c #d9bbfe",
-".km c #d9c5fb",
-"#EF c #d9ccce",
-".mA c #d9d2ef",
-"aEc c #d9dada",
-"bHI c #d9dbdc",
-"ape c #d9dbdd",
-"bLj c #d9dcdc",
-"#SA c #d9ddde",
-"#Y2 c #d9dddf",
-"aaM c #d9dde0",
-"bLi c #d9dee0",
-".8o c #d9dee1",
-".1c c #d9dee2",
-"#.U c #d9dfe1",
-"#dd c #d9dfe2",
-"#DA c #d9dfe3",
-"#Bf c #d9e0e2",
-"#Ja c #d9e0e3",
-"#DG c #d9e1e3",
-"#H3 c #d9e3e5",
-".9B c #d9e6f0",
-"ahA c #d9e7ed",
-".55 c #d9e7f1",
-"bx9 c #da798b",
-"bCM c #da7a8c",
-"a#0 c #da7b8e",
-"#GH c #da7b90",
-"#h8 c #da7c90",
-"#EY c #da7c91",
-"#eE c #da7d91",
-"#W6 c #da8592",
-"#S# c #da8593",
-"beR c #da8695",
-".fB c #da87c0",
-"bfq c #da8d9c",
-".Jy c #da9d9e",
-".O2 c #da9fa1",
-"b.m c #daa4aa",
-"alC c #daa8b3",
-".gD c #daaaff",
-".gC c #daabff",
-".hk c #dab0ff",
-"a1r c #dab2b3",
-".h6 c #dab6fe",
-"bWj c #dabbc5",
-".jB c #dabffd",
-".k6 c #dacbf8",
-".lQ c #dacef4",
-"bF5 c #dadcdc",
-"bM4 c #dadcdd",
-"#3v c #dadcde",
-"#Tn c #daddde",
-"bDP c #dadddf",
-"#5Q c #dadedf",
-".KE c #dadee0",
-"bLh c #dadfe1",
-".NB c #dadfe2",
-"#aw c #dadfe3",
-"aY. c #dae0e1",
-"##U c #dae0e2",
-".9q c #dae0e3",
-"#2a c #dae1e3",
-"ahD c #dae1e4",
-"#Fv c #dae2e4",
-"#G9 c #dae2e5",
-"#XC c #dae7ed",
-"#JK c #dae9f0",
-".ap c #db4ddf",
-"#7x c #db7588",
-"bUd c #db7a8c",
-"blF c #db7c90",
-".VZ c #db7c91",
-"afw c #db7d91",
-"afZ c #db8492",
-"bjK c #db8594",
-"bop c #db8694",
-".X6 c #db8695",
-"#cy c #db8896",
-"aLm c #db8b94",
-".IT c #db9e9e",
-".Jz c #db9e9f",
-"bor c #db9fa1",
-"aFr c #dba0a2",
-"bAS c #dba1a2",
-"a8b c #dba1a7",
-"ahJ c #dba4ac",
-"aWM c #dba7a9",
-".iQ c #dbbafe",
-".pT c #dbc571",
-".kn c #dbc6fa",
-"akg c #dbd4d5",
-".IC c #dbdada",
-"bOM c #dbdcde",
-"bVp c #dbddde",
-"#NF c #dbdddf",
-".N6 c #dbdedf",
-"#01 c #dbdfe0",
-".PQ c #dbdfe1",
-"#4I c #dbdfe2",
-"##T c #dbe0e2",
-".RS c #dbe0e3",
-".YE c #dbe0e4",
-".5A c #dbe1e3",
-".4q c #dbe1e4",
-"akb c #dbe1e5",
-"#JP c #dbe2e4",
-"#Gl c #dbe2e5",
-"#79 c #dbe3e5",
-"#Lf c #dbe7ed",
-"ap5 c #dbe9f1",
-"bWe c #dc7b8d",
-"bDC c #dc7c8d",
-"aI5 c #dc7c91",
-"#o. c #dc8093",
-"baA c #dc8392",
-"ajN c #dc8492",
-"bcP c #dc8895",
-"beN c #dc8a97",
-".JA c #dc9fa0",
-".M3 c #dc9fa1",
-".R5 c #dca0a2",
-".fQ c #dca3fd",
-".gE c #dcaaff",
-".hl c #dcb0ff",
-".h7 c #dcb6fe",
-".k7 c #dccaf7",
-"bb4 c #dcd4da",
-".IP c #dcdddd",
-"bDQ c #dcddde",
-"ab7 c #dcdedf",
-".Kv c #dcdee0",
-"bRF c #dcdfdf",
-".UE c #dcdfe0",
-"aCa c #dcdfe1",
-"#OP c #dce0e1",
-".Mv c #dce0e2",
-".QQ c #dce0e3",
-"#wF c #dce1e3",
-".ZW c #dce1e4",
-"#cs c #dce1e5",
-"#m3 c #dce2e4",
-"#cp c #dce2e5",
-"#6P c #dce3e5",
-"aUp c #dce3e6",
-"#5L c #dce4e6",
-"#aN c #dce5ea",
-".Qu c #dce6eb",
-"##. c #dce6ec",
-"#QY c #dce7ed",
-".8O c #dce8f1",
-".7q c #dce9f2",
-"#Og c #dceaf2",
-"#eP c #dd7a8c",
-"a1M c #dd7b8f",
-"#pL c #dd7d92",
-"ahm c #dd8693",
-"a1t c #dd8995",
-"bo5 c #dd8b98",
-"ajQ c #dd8c99",
-".2O c #dd8d99",
-"ag0 c #dd8f99",
-"#kC c #dd989e",
-"aFD c #dda1a3",
-".hm c #ddb0ff",
-".h8 c #ddb6fe",
-".jC c #ddc0fc",
-".ko c #ddc6f9",
-"#5Z c #ddc9ca",
-".lR c #ddcef2",
-".od c #ddd4d0",
-".qr c #dddcb0",
-".WQ c #dddfe0",
-"#io c #dddfe1",
-"aq0 c #dddfe2",
-".Og c #dde0e2",
-".Ms c #dde1e3",
-"#fz c #dde1e5",
-"aTF c #dde2e3",
-".Kx c #dde2e4",
-".9p c #dde2e5",
-"#.a c #dde3e5",
-"#ug c #dde3e6",
-"#IT c #dde4e6",
-".#F c #de2ede",
-"baO c #de7c8f",
-"#fM c #de7d92",
-"ac6 c #de8895",
-"abt c #de8995",
-"bz. c #de8c99",
-".0k c #de8e9a",
-"a#O c #de919c",
-"a82 c #de939d",
-"aj. c #de9490",
-"akj c #de9b9b",
-".Jp c #de9fa0",
-"bru c #dea1a3",
-".fR c #dea4ff",
-"aXo c #dea9a9",
-"aZV c #deaaab",
-".gF c #deaaff",
-"agt c #deb3bc",
-".iR c #debbfd",
-".KC c #dedfdf",
-"aBC c #dedfe0",
-".Kq c #dee0e0",
-"#Wr c #dee1e2",
-"atv c #dee2e4",
-".Nx c #dee2e5",
-"#db c #dee3e5",
-"#gF c #dee3e6",
-"#FG c #dee4e5",
-"#fp c #dee4e6",
-"#im c #dee4e7",
-".P. c #dee5e9",
-"#US c #dee8ed",
-".vo c #def06f",
-".cu c #df78ec",
-"#gZ c #df7d92",
-"aYc c #df8794",
-"akR c #df8797",
-"b.T c #df8895",
-"aiC c #df8a96",
-"b#Z c #df8d99",
-"bi5 c #df8f9a",
-"buV c #df8f9b",
-"#fF c #df909b",
-".Jo c #dfa0a0",
-".KR c #dfa1a1",
-"anx c #dfa2a4",
-".gG c #dfaaff",
-".hn c #dfb0ff",
-".h9 c #dfb6fe",
-"amX c #dfb7c1",
-".MA c #dfbac2",
-".jD c #dfc0fb",
-".k8 c #dfcaf5",
-"ai# c #dfd9db",
-"#D6 c #dfdddf",
-"an9 c #dfe0e1",
-"#PC c #dfe1e1",
-"aaw c #dfe1e2",
-"#Dz c #dfe2e3",
-"#Y3 c #dfe2e4",
-"#ot c #dfe3e4",
-"azo c #dfe3e5",
-".ZZ c #dfe3e6",
-"acl c #dfe4e5",
-"##K c #dfe4e6",
-"#eq c #dfe4e7",
-"#EN c #dfe5e7",
-"atq c #dfe5e8",
-"ao. c #dfe6e8",
-".VE c #dfe6ea",
-"##1 c #dfe8ee",
-".uQ c #dfeb5b",
-"b.j c #e08794",
-"a9J c #e08c97",
-"b#q c #e08c98",
-".Ou c #e08c99",
-"aeC c #e08d97",
-"akK c #e08f99",
-"bfK c #e0919c",
-".Jm c #e0a0a1",
-".Y8 c #e0a3a5",
-".fS c #e0a4ff",
-"#5W c #e0b3b4",
-"#V8 c #e0b8c0",
-"aja c #e0bac2",
-"ahL c #e0bac3",
-".iS c #e0bbfd",
-"bcM c #e0bfc8",
-".kp c #e0c6f8",
-".jJ c #e0c7e4",
-".nl c #e0dbee",
-"#ny c #e0e2e2",
-"#8j c #e0e2e3",
-"#5S c #e0e2e4",
-"#60 c #e0e3e4",
-"##D c #e0e3e5",
-"#py c #e0e4e6",
-"#fv c #e0e4e7",
-"bv7 c #e0e4e8",
-"#YW c #e0e5e6",
-"#cl c #e0e5e7",
-".0V c #e0e5e8",
-"ao# c #e0e6e8",
-"#jv c #e0e6e9",
-".6. c #e0e7eb",
-"#0A c #e0e8ed",
-"#M# c #e0e9ee",
-".#y c #e12fe1",
-".ce c #e16bd8",
-".W4 c #e1929d",
-"ae# c #e1959e",
-"bcN c #e19ba4",
-".Jn c #e1a1a2",
-"blE c #e1a3a5",
-"aoL c #e1a4a5",
-".fT c #e1a4ff",
-"aYa c #e1a9aa",
-"aaE c #e1aaab",
-".gH c #e1aaff",
-".ho c #e1b0ff",
-".i. c #e1b6fe",
-".jE c #e1c1fa",
-".kt c #e1c9e3",
-"#5M c #e1dcdd",
-"a#h c #e1e2e3",
-"#or c #e1e3e4",
-".Mk c #e1e3e5",
-"##z c #e1e4e5",
-"#Gh c #e1e4e6",
-".LM c #e1e4e7",
-"#aB c #e1e5e7",
-".9l c #e1e5e8",
-"#xF c #e1e6e8",
-"#aq c #e1e6e9",
-"#DH c #e1e7e8",
-".8D c #e1e7e9",
-"#5v c #e1e7ea",
-"#ZA c #e1ebef",
-".cT c #e273d6",
-"aXq c #e28894",
-"beQ c #e2949e",
-"##5 c #e2959e",
-".Jq c #e2a1a2",
-"aDP c #e2a5a6",
-"bnv c #e2a5a7",
-"bWk c #e2a5a9",
-"b.U c #e2a7ac",
-"bdw c #e2a7b2",
-"#Xj c #e2aaac",
-".gI c #e2aaff",
-"#S9 c #e2acb5",
-"a4O c #e2aeb0",
-".hp c #e2b0ff",
-"bnd c #e2bbbe",
-".iT c #e2bcfc",
-".rT c #e2d757",
-".o0 c #e2d8c1",
-".HR c #e2e2e2",
-"agX c #e2e4e4",
-".8e c #e2e5e7",
-"aug c #e2e6e7",
-".Oc c #e2e6e8",
-"#.# c #e2e6e9",
-"#Gw c #e2e7e8",
-"#er c #e2e7e9",
-"bv6 c #e2e7ea",
-"alJ c #e2e8ea",
-"abP c #e2e9ed",
-"#Mh c #e2edf2",
-"af0 c #e3939c",
-"bnO c #e3949d",
-"bqG c #e3969f",
-".eo c #e396fb",
-"bgC c #e397a0",
-".e7 c #e39eff",
-"aKh c #e3a4a5",
-".fU c #e3a4ff",
-"aCX c #e3a5a6",
-"a3c c #e3b1b1",
-"#XZ c #e3b1ba",
-".lb c #e3cce3",
-".nq c #e3d3d8",
-".PV c #e3e0e2",
-".HZ c #e3e3e4",
-"#mM c #e3e5e7",
-"a.8 c #e3e6e7",
-".VD c #e3e6e8",
-".PN c #e3e6e9",
-"aWI c #e3e7e8",
-".YV c #e3e7e9",
-"#xH c #e3e7ea",
-"#B1 c #e3e8e9",
-"#dU c #e3e8ea",
-"aQd c #e3e8eb",
-"#mr c #e4929e",
-"bv0 c #e497a0",
-"beO c #e499a1",
-".e8 c #e49eff",
-".K5 c #e49fa3",
-"aLl c #e4a1a2",
-".KQ c #e4a4a4",
-".Lk c #e4a4a5",
-".fV c #e4a4ff",
-".OY c #e4a5a6",
-"#WZ c #e4aab5",
-".gJ c #e4aaff",
-"#X0 c #e4adb6",
-"#67 c #e4aeaf",
-"ad2 c #e4b5bc",
-".i# c #e4b6fe",
-".mB c #e4d7f1",
-"#0Q c #e4e1e2",
-".HY c #e4e4e5",
-"#Rx c #e4e4e6",
-"adL c #e4e5e7",
-".SI c #e4e6e7",
-"#71 c #e4e6e8",
-".KB c #e4e6e9",
-"#G8 c #e4e7e8",
-"#bi c #e4e7e9",
-"axH c #e4e7ea",
-"#Fu c #e4e8e9",
-".2n c #e4e8ea",
-"#ft c #e4e8eb",
-"#ym c #e4e9ea",
-"#aC c #e4e9eb",
-"adD c #e4e9ec",
-"#xe c #e4eaed",
-"agk c #e4ebee",
-"akN c #e5919b",
-"bz3 c #e5959f",
-"ac8 c #e5969e",
-"#W0 c #e599a2",
-"#.w c #e59aa1",
-"beP c #e59aa2",
-"#iX c #e59ba2",
-".fW c #e5a4ff",
-".Lj c #e5a5a6",
-".ST c #e5a6a7",
-"bRv c #e5a6a8",
-"a.Y c #e5a7a8",
-".hq c #e5b1ff",
-"#X1 c #e5b4bd",
-"#X2 c #e5bec7",
-"#X3 c #e5c3cc",
-".lW c #e5d0e2",
-".Os c #e5d1d6",
-".lS c #e5d3f5",
-".q7 c #e5e3a1",
-".IJ c #e5e4e5",
-".IK c #e5e5e6",
-"aju c #e5e7e7",
-"acm c #e5e7e8",
-".JP c #e5e7e9",
-"aoi c #e5e8e9",
-"#kS c #e5e8ea",
-".Su c #e5e8eb",
-"#Ie c #e5e9ea",
-".Tk c #e5e9eb",
-"#xy c #e5e9ec",
-"#3z c #e5eaeb",
-"#kn c #e5eaec",
-"a3. c #e5eaed",
-"#Kz c #e5ebee",
-"#M9 c #e5ecee",
-"#S4 c #e5ecef",
-".VN c #e5eef4",
-".MT c #e6919d",
-"bb6 c #e699a0",
-".V0 c #e69ba2",
-"bdz c #e69ca2",
-"bed c #e69ca6",
-"#dl c #e69da3",
-".e9 c #e69eff",
-".Lt c #e6a6a6",
-"aGF c #e6a6a7",
-"bMV c #e6a6a8",
-"bXt c #e6a6a9",
-".Q# c #e6a7a8",
-".gK c #e6abff",
-".j3 c #e6afb0",
-"afq c #e6b3b4",
-"amU c #e6c3be",
-".mF c #e6d3df",
-"aaK c #e6d6db",
-".Kw c #e6e6e6",
-".H0 c #e6e6e7",
-"#Db c #e6e7e8",
-"#Rq c #e6e8e9",
-"#HY c #e6e9ea",
-".NX c #e6e9eb",
-".4I c #e6e9ec",
-"#bo c #e6eaeb",
-".0U c #e6eaec",
-"#mh c #e6eaed",
-"#dc c #e6ebec",
-"#gB c #e6ebed",
-"#WN c #e6ebee",
-".tL c #e6f47b",
-".uj c #e6f877",
-"aWO c #e78c96",
-"baC c #e7949c",
-".ep c #e797ff",
-"ajH c #e79ca2",
-"#nf c #e79ca3",
-"bnc c #e79da3",
-"blk c #e79ea4",
-".f. c #e79eff",
-".36 c #e79fa4",
-"aki c #e7a195",
-".Lh c #e7a6a7",
-"bs9 c #e7a7a8",
-"#gS c #e7a7a9",
-"#FA c #e7a9aa",
-"a5D c #e7abad",
-".k9 c #e7cffa",
-".ta c #e7e43f",
-".HX c #e7e6e5",
-"#16 c #e7e6e6",
-".HQ c #e7e7e7",
-"#sv c #e7e8e8",
-"#QC c #e7e8e9",
-".LB c #e7e8ea",
-"#PB c #e7e9e9",
-"#ZV c #e7e9ea",
-"#NE c #e7e9eb",
-"aSQ c #e7eaea",
-"#Sy c #e7eaeb",
-".3A c #e7eaec",
-"#nV c #e7eaed",
-"#iF c #e7ebec",
-"#.e c #e7ebed",
-"#7X c #e7ebee",
-"aqm c #e7eced",
-"#1U c #e7ecee",
-"#Q7 c #e7ecef",
-".s9 c #e7f182",
-"#Qh c #e7f2f7",
-"ajO c #e89da3",
-"bgJ c #e89fa4",
-"#bJ c #e89fa5",
-"ajP c #e8a0a5",
-"ait c #e8a3a7",
-".fX c #e8a5ff",
-"bmT c #e8a8aa",
-"by# c #e8a9aa",
-"#9Q c #e8b6bf",
-".kq c #e8cafd",
-"a#e c #e8cdd4",
-"aY4 c #e8cecf",
-".Mm c #e8e9ea",
-"#Xg c #e8e9eb",
-"afs c #e8eaea",
-"#AG c #e8eaeb",
-".4w c #e8eaec",
-"#XJ c #e8ebeb",
-"#JL c #e8ebec",
-".YL c #e8ebed",
-"#gD c #e8ebee",
-"#cm c #e8eced",
-"#.Y c #e8ecee",
-"a.O c #e8ecef",
-"aVh c #e9939a",
-".eq c #e997ff",
-"ag1 c #e999a1",
-"bdx c #e99ea3",
-"abs c #e99ea4",
-"aeB c #e9a0a5",
-"bi4 c #e9a1a6",
-".Y7 c #e9a2a7",
-".MD c #e9a9a8",
-"a7v c #e9aaac",
-"#U6 c #e9bdc2",
-"ajd c #e9bfc5",
-"#XY c #e9c4cc",
-".jF c #e9c5ff",
-"#6Q c #e9dadb",
-"#8. c #e9e7e8",
-".KI c #e9e9ea",
-".KF c #e9e9eb",
-".Me c #e9eaea",
-".4o c #e9eaeb",
-".PP c #e9ebeb",
-"#Lm c #e9ebec",
-".XT c #e9ebed",
-"#Up c #e9ecec",
-"#KT c #e9eced",
-".VK c #e9ecee",
-"#hH c #e9edee",
-"#ep c #e9edef",
-"auK c #e9eeef",
-".uP c #e9fb75",
-".dK c #ea8fff",
-".dJ c #ea90fe",
-"bim c #ea9aa3",
-"aPy c #ea9ba0",
-"bfD c #ea9da5",
-".f# c #ea9eff",
-"bw7 c #eaa0a6",
-"#kB c #eaa1a6",
-"bip c #eaa2a6",
-"blU c #eaa2a7",
-"bin c #eaa3a7",
-".Li c #eaa8a9",
-".N# c #eaa9aa",
-".N. c #eaaaab",
-"#I6 c #eaacac",
-"a3d c #eaacad",
-".hr c #eab3ff",
-".ia c #eab9ff",
-".iU c #eac0ff",
-"alB c #ead2d4",
-"#YS c #eae8e8",
-".QU c #eaeaec",
-".Ml c #eaebeb",
-".0S c #eaebec",
-"#0I c #eaebed",
-".PO c #eaecec",
-".Od c #eaeced",
-"#fm c #eaecee",
-"aao c #eaeded",
-".5E c #eaedee",
-".8F c #eaedef",
-".QT c #eaedf0",
-"#5K c #eaeeee",
-"#ju c #eaeeef",
-"#iG c #eaeef0",
-"aqR c #eaf0f4",
-".#E c #eb30ea",
-"aV7 c #eb9099",
-".er c #eb98ff",
-"bbm c #eb9ba1",
-"bAR c #eb9ca4",
-"be3 c #eb9fa5",
-"aoJ c #eba2a7",
-"#U# c #eba3a9",
-".1G c #eba5a8",
-".9J c #eba5a9",
-"bjL c #eba8ab",
-"#dm c #eba9ab",
-".M9 c #ebaaab",
-"alA c #ebab9f",
-"a6i c #ebacad",
-"#6R c #ebc3c3",
-".o9 c #ebd083",
-"#WY c #ebdfe3",
-"#Rz c #ebebed",
-"#9K c #ebecec",
-".Of c #ebeced",
-"#Y1 c #ebeded",
-"#WG c #ebedee",
-"#eg c #ebedef",
-"#m8 c #ebedf0",
-"akt c #ebeeee",
-"#hL c #ebeeef",
-".WE c #ebeef0",
-".su c #ebef87",
-"#Eb c #ebefef",
-"#ji c #ebeff0",
-"#rL c #ebeff1",
-"box c #ebeff2",
-".RN c #ebf0f1",
-"#9v c #ebf1f2",
-"#zI c #ebf7fc",
-".#z c #ec30ec",
-".fa c #ec9efe",
-"apL c #ec9fa5",
-"bBQ c #ec9fa6",
-"bbk c #eca4a7",
-"bb7 c #eca5a9",
-"akL c #eca6a9",
-".fY c #eca6ff",
-"bfG c #eca7a9",
-".7C c #eca7aa",
-"a4Q c #eca9ab",
-".Nj c #ecaaab",
-"ahP c #ecaab0",
-"aF5 c #ecabab",
-".M8 c #ecabac",
-"aLk c #ecacac",
-".gL c #ecadff",
-".oe c #ecd9c7",
-"akk c #ecdadc",
-"agC c #eceaeb",
-".Kp c #ececec",
-".Oe c #ececed",
-"#XS c #eceded",
-".Y1 c #ecedee",
-".WJ c #ecedef",
-"#4H c #eceeee",
-"#jj c #eceeef",
-".4H c #eceef0",
-"aku c #ecefef",
-"#oN c #eceff0",
-".6P c #eceff1",
-"#DI c #ecf0f0",
-"#yW c #ecf0f1",
-"aTC c #ecf0f2",
-".dL c #ed90ff",
-"aUy c #ed989e",
-"#W5 c #ed9ba0",
-"ayN c #ed9da4",
-"akQ c #ed9fa6",
-"alz c #eda69a",
-"anv c #eda7aa",
-".48 c #eda8aa",
-"bmj c #eda8ab",
-"bfL c #eda9ab",
-".Ll c #edaaab",
-"blW c #edaaac",
-".Ov c #edabab",
-".M6 c #edabac",
-".if c #edb4f5",
-"agT c #edc0c1",
-"ae9 c #edd1d0",
-"#Rh c #edd1d7",
-".nr c #edd9da",
-"acx c #ede1e2",
-".Mf c #ededed",
-"#SE c #ededee",
-".Md c #edeeee",
-"#2g c #edeeef",
-"aDq c #edeef0",
-"aQh c #edefee",
-"#Uv c #edefef",
-".TA c #edeff0",
-".4p c #edeff1",
-"#D# c #edf0f0",
-"#kk c #edf0f1",
-"#fx c #edf0f2",
-"#gz c #edf1f2",
-"bkE c #edf1f3",
-".Tv c #edf4f9",
-".es c #ee98fe",
-"aQm c #ee9ba1",
-".UT c #eea0a7",
-"bee c #eea7a9",
-"#V4 c #eea8b0",
-"abr c #eea9ab",
-"bo6 c #eea9ac",
-".6h c #eeaaac",
-".gP c #eeaafa",
-".Nk c #eeabac",
-"baB c #eeabad",
-"bmx c #eeabae",
-"#W4 c #eeabaf",
-".OR c #eeacad",
-"aKg c #eeadad",
-"#J4 c #eeadae",
-"aSe c #eeaeae",
-"#Gt c #eeafaf",
-"aaF c #eeafb0",
-"bmz c #eeb1b2",
-".jK c #eec0f2",
-".ku c #eec5ee",
-"a## c #eecaca",
-".lc c #eecbea",
-".lX c #eed0e5",
-"alD c #eee3e6",
-"akq c #eeecec",
-"ajo c #eeedee",
-".Mn c #eeeeee",
-"#Hk c #eeeeef",
-".N8 c #eeefef",
-"#2f c #eeeff0",
-"a#g c #eef0ef",
-"#Xc c #eef0f0",
-".2p c #eef0f1",
-"##G c #eef0f2",
-"#Ea c #eef1f1",
-"#jy c #eef1f2",
-".KM c #eef2f1",
-"#69 c #eef2f2",
-"#0V c #eef2f3",
-".a# c #ef44ed",
-".dM c #ef90fd",
-".ex c #ef96fa",
-".ff c #ef9dfb",
-"bhB c #efa3a7",
-".f2 c #efa3fb",
-"bfF c #efa6aa",
-"bcO c #efa8aa",
-"#Ye c #efa9aa",
-"b.k c #efa9ab",
-"aEX c #efa9ac",
-"a8c c #efabac",
-".X7 c #efabad",
-".M5 c #efacad",
-".SU c #efacae",
-".49 c #efadad",
-".37 c #efadae",
-"apM c #efaeae",
-"#W3 c #efaeb2",
-"aRm c #efafae",
-"#Ib c #efafaf",
-".hw c #efb0f9",
-"#Yd c #efb1b2",
-"aeb c #efb2b3",
-"#6S c #efb3b4",
-".i0 c #efbbf1",
-".jL c #efc0ee",
-"aUx c #efc1c1",
-".kv c #efc6ea",
-".ld c #efcbe6",
-"#5X c #efcdce",
-"#Xh c #efced0",
-".lY c #efcfe1",
-"a#. c #efd6d6",
-".ns c #efd7d3",
-"alF c #efecec",
-"ahX c #efeded",
-"ahY c #efedee",
-"afi c #efeeee",
-"#Xe c #efeef0",
-"#Xd c #efefef",
-".KG c #efeff0",
-"#00 c #eff0ef",
-".PI c #eff0f0",
-".9h c #eff0f1",
-"#TZ c #eff0f2",
-"#O5 c #eff1f1",
-".3k c #eff1f2",
-".Tt c #eff1f3",
-"#OF c #eff2f2",
-"#m7 c #eff2f3",
-".8E c #eff3f4",
-"bbh c #eff4f5",
-".RJ c #effafe",
-".da c #f087fa",
-".c7 c #f088fc",
-".c6 c #f089ff",
-".dR c #f08ffa",
-"aRn c #f09ca2",
-"aSf c #f09da3",
-"aSV c #f09ea4",
-"#6o c #f0a3a7",
-"#W1 c #f0a3a8",
-"bfE c #f0a4aa",
-"a83 c #f0a7aa",
-"a9K c #f0a8aa",
-"a2r c #f0abab",
-"#Y5 c #f0abac",
-".ME c #f0acac",
-".OZ c #f0acad",
-".V2 c #f0acae",
-"#8W c #f0adad",
-".M4 c #f0adae",
-".V1 c #f0aeae",
-"#7A c #f0aeaf",
-"abo c #f0afaf",
-"akh c #f0b0a5",
-".hx c #f0b0f8",
-".ig c #f0b6f5",
-".iZ c #f0bdf6",
-"agw c #f0c8cc",
-"#ZX c #f0d2d3",
-".mH c #f0d4dc",
-".mG c #f0d7e2",
-"#5Y c #f0dbdb",
-".pM c #f0e1ae",
-"#Yc c #f0ebec",
-"ahZ c #f0efef",
-".Ki c #f0eff0",
-"#Xf c #f0eff1",
-"#9w c #f0f0f0",
-".JW c #f0f0f1",
-".Mc c #f0f1f0",
-"#Ec c #f0f1f1",
-".3e c #f0f1f2",
-".WS c #f0f1f3",
-"#4E c #f0f2f0",
-"#Vi c #f0f2f1",
-"#yg c #f0f2f2",
-".WD c #f0f2f3",
-"#mg c #f0f2f4",
-"#tu c #f0f3f3",
-"#iJ c #f0f3f4",
-"#qZ c #f0f3f5",
-".#D c #f130f1",
-".#5 c #f145f3",
-".cz c #f17ff9",
-".f3 c #f1a4fc",
-".gR c #f1aaf8",
-".gQ c #f1aafa",
-"bmy c #f1abab",
-"blV c #f1acad",
-"bdy c #f1adad",
-".M7 c #f1adae",
-".Ni c #f1aeae",
-"ajJ c #f1aeaf",
-"a#N c #f1afaf",
-"abp c #f1afb0",
-"b#0 c #f1b0af",
-"#8V c #f1b0b0",
-".hy c #f1b0f5",
-"anw c #f1b1b0",
-"#V. c #f1b4b6",
-".ih c #f1b6f3",
-".i1 c #f1bbef",
-".kM c #f1bcb2",
-".jM c #f1c0ec",
-".kw c #f1c6e7",
-".le c #f1cbe3",
-"#Y4 c #f1cecf",
-".lZ c #f1cfde",
-".mI c #f1d4d7",
-".nt c #f1d7cd",
-".o1 c #f1deb8",
-"amV c #f1e0df",
-"#8o c #f1e0e0",
-"#5E c #f1ebea",
-"#3q c #f1ecea",
-"#5F c #f1eceb",
-"#13 c #f1edeb",
-"ajk c #f1edec",
-"afr c #f1eeee",
-"alG c #f1efef",
-"agG c #f1f0f0",
-"#Vp c #f1f0f1",
-".N9 c #f1f1f1",
-"#EQ c #f1f1f2",
-".PJ c #f1f2f1",
-"#OQ c #f1f2f2",
-"#LM c #f1f2f3",
-"#av c #f1f2f4",
-"agW c #f1f3f2",
-"#wC c #f1f3f3",
-".63 c #f1f3f4",
-".Rz c #f1f3f5",
-"#76 c #f1f4f3",
-"#Wh c #f1f4f4",
-"#hJ c #f1f4f5",
-"#.1 c #f1f4f6",
-".#A c #f230f2",
-".bp c #f26ef9",
-".fh c #f29efc",
-".f4 c #f2a4fa",
-"akO c #f2a8ab",
-".hR c #f2a8c9",
-"by. c #f2aaad",
-"a8d c #f2acad",
-"aFt c #f2adad",
-"#Yf c #f2adae",
-"aFs c #f2aeae",
-".R. c #f2aeaf",
-"#iY c #f2afaf",
-"bbl c #f2b0b0",
-"ajI c #f2b1b0",
-"abq c #f2b1b1",
-"ac7 c #f2b3b2",
-"#FD c #f2c1c1",
-"#EJ c #f2d0d0",
-"#Hh c #f2dada",
-"#Gv c #f2dfdf",
-"#EK c #f2e3e4",
-"#FE c #f2e7e7",
-"#3r c #f2eceb",
-"#5D c #f2edeb",
-"#12 c #f2edec",
-"aji c #f2eded",
-".rP c #f2ee8b",
-"#0N c #f2eeec",
-"#ZL c #f2eeed",
-"#77 c #f2eeee",
-"ahW c #f2eeef",
-"#0M c #f2efed",
-"#ZK c #f2efee",
-"#6N c #f2efef",
-"#5C c #f2f0ef",
-"#YQ c #f2f0f0",
-"ajn c #f2f0f1",
-"#5G c #f2f1f0",
-"#9u c #f2f1f1",
-"aaN c #f2f1f2",
-"a.S c #f2f2f1",
-".JX c #f2f2f2",
-"#SG c #f2f2f3",
-"#Qu c #f2f3f2",
-".0T c #f2f3f3",
-"#aD c #f2f3f4",
-"#eo c #f2f3f5",
-"#4C c #f2f4f3",
-"#0W c #f2f4f4",
-".Nr c #f2f4f5",
-"#qf c #f2f4f6",
-"agV c #f2f5f4",
-"#E# c #f2f5f5",
-"#FF c #f2f7f7",
-".aT c #f362f8",
-".bY c #f377fa",
-".ez c #f397fd",
-".ey c #f397ff",
-".fg c #f39efe",
-".f5 c #f3a4f9",
-"aea c #f3a8ac",
-"a1s c #f3a9aa",
-".gS c #f3aaf7",
-"#Y6 c #f3abab",
-"#7y c #f3abac",
-"a7u c #f3acad",
-"bhy c #f3acae",
-"bnP c #f3adae",
-"a9L c #f3aeae",
-".X8 c #f3aeaf",
-".Qg c #f3afaf",
-"bgB c #f3afb0",
-"bgI c #f3b0b0",
-".hz c #f3b0f4",
-"#7z c #f3b1b0",
-"#Gu c #f3b5b5",
-".ii c #f3b6f1",
-".i2 c #f3bbed",
-".jN c #f3c0e9",
-".kx c #f3c6e5",
-".lf c #f3cbe0",
-".l0 c #f3cfda",
-"ai9 c #f3d8d3",
-"#Id c #f3dcdc",
-"#I8 c #f3e4e4",
-".qs c #f3e6a1",
-"#4w c #f3eceb",
-"#4v c #f3edec",
-"#0O c #f3eeed",
-"ajj c #f3eeee",
-"#78 c #f3efee",
-"ahT c #f3efef",
-"ajl c #f3eff0",
-"#11 c #f3f0ef",
-"#4u c #f3f0f0",
-"#6M c #f3f1f0",
-"#J6 c #f3f1f1",
-".JV c #f3f1f2",
-"#5H c #f3f2f1",
-"#6O c #f3f2f2",
-".KH c #f3f2f3",
-"#5B c #f3f3f2",
-".JO c #f3f3f3",
-"#FH c #f3f3f4",
-"#ZU c #f3f4f3",
-".PH c #f3f4f4",
-".Nq c #f3f4f5",
-"#en c #f3f4f6",
-"a.T c #f3f5f4",
-".4x c #f3f5f5",
-"#fl c #f3f5f6",
-"aan c #f3f6f5",
-"#J7 c #f3f6f6",
-"#EL c #f3f6f7",
-"#E. c #f3f7f7",
-"#I9 c #f3f8f8",
-"bb3 c #f3faf9",
-".uk c #f3fe60",
-".#C c #f430f5",
-".a5 c #f460ee",
-".dS c #f490ff",
-".eA c #f497fc",
-".fi c #f49efb",
-".gT c #f4aaf5",
-"aV6 c #f4adad",
-"#lv c #f4adaf",
-".RW c #f4aeae",
-".O0 c #f4afaf",
-".OQ c #f4afb0",
-"aLj c #f4b0b1",
-".hA c #f4b0f2",
-"#Hf c #f4b1b1",
-"#FC c #f4b2b2",
-"aOy c #f4b3b2",
-".ij c #f4b6ef",
-"#Xi c #f4b8b9",
-"#EI c #f4babb",
-".i3 c #f4bbeb",
-".jO c #f4c0e7",
-"#8# c #f4cfcf",
-".oo c #f4d491",
-".mJ c #f4d4d4",
-"#KR c #f4d6d6",
-"#9x c #f4dada",
-"ajg c #f4e2e3",
-"#D7 c #f4ecec",
-"akl c #f4edee",
-"ako c #f4efee",
-"akn c #f4efef",
-"akp c #f4eff0",
-"#ZM c #f4f0ef",
-"ahV c #f4f0f0",
-"am0 c #f4f0f1",
-"#3p c #f4f1f0",
-"am1 c #f4f1f1",
-"amZ c #f4f1f2",
-"#ZJ c #f4f2f1",
-"#D8 c #f4f2f2",
-"amY c #f4f2f3",
-"#5I c #f4f3f3",
-"#Tx c #f4f4f4",
-"#Ty c #f4f4f5",
-"#J. c #f4f5f5",
-".PA c #f4f5f6",
-".61 c #f4f5f7",
-"#Wg c #f4f6f5",
-"#tr c #f4f6f6",
-"#zG c #f4f6f7",
-"#nQ c #f4f6f8",
-"#5J c #f4f7f6",
-"#MF c #f4f7f7",
-"agu c #f4f7f8",
-"#D9 c #f4f8f8",
-"agy c #f4f9f9",
-".#B c #f531f5",
-".cB c #f580ff",
-".db c #f588ff",
-".dT c #f590fe",
-".eT c #f592dd",
-".eB c #f597fc",
-".fj c #f59efa",
-".f6 c #f5a4f8",
-"aZW c #f5a7a8",
-"a0H c #f5a8a9",
-"#V5 c #f5a8aa",
-"aWN c #f5abac",
-"b#1 c #f5acac",
-"akP c #f5acaf",
-"#6p c #f5aeaf",
-"a84 c #f5afae",
-".OS c #f5afb0",
-"b#r c #f5b0af",
-".PY c #f5b0b0",
-".Rg c #f5b0b1",
-"aNg c #f5b3b2",
-"#Hg c #f5b3b3",
-".ky c #f5c6e2",
-".lg c #f5cbdd",
-".l1 c #f5cfd7",
-".of c #f5dcc0",
-"agv c #f5ddde",
-"akm c #f5ecec",
-"ahR c #f5eded",
-"#3s c #f5f0ef",
-"ajm c #f5f0f1",
-"ahU c #f5f1f1",
-"agx c #f5f2f2",
-"#0L c #f5f3f2",
-"agz c #f5f3f4",
-"ajh c #f5f4f4",
-"#Ry c #f5f4f5",
-"ahS c #f5f5f4",
-".5z c #f5f5f5",
-"ajb c #f5f5f6",
-"a.U c #f5f5f7",
-"alE c #f5f6f5",
-"#DJ c #f5f6f6",
-".XJ c #f5f6f7",
-".ZY c #f5f6f8",
-"#Da c #f5f7f6",
-".3g c #f5f7f7",
-"#gr c #f5f7f8",
-"#xI c #f5f7f9",
-"#NB c #f5f8f8",
-"adK c #f5f8f9",
-".WT c #f5f8fa",
-"ahM c #f5f9f9",
-"af. c #f5faf9",
-".tM c #f5fd62",
-".b0 c #f677ff",
-".cA c #f680ff",
-".dd c #f688fe",
-".dc c #f688ff",
-".dU c #f690fd",
-".eC c #f697fb",
-".fk c #f69ef9",
-".f7 c #f6a4f6",
-"aY5 c #f6a8a9",
-"aXp c #f6aaab",
-".gU c #f6aaf4",
-"a6j c #f6adad",
-"acr c #f6aeae",
-"a6k c #f6afae",
-"afy c #f6afb0",
-"aEi c #f6b0af",
-".Ow c #f6b0b0",
-".R# c #f6b0b1",
-"boq c #f6b0b2",
-".hB c #f6b0f0",
-".Rh c #f6b1b1",
-"bw8 c #f6b2b2",
-"#Ic c #f6b4b4",
-"#I7 c #f6b5b4",
-"#8a c #f6b5b6",
-".ik c #f6b6ed",
-".i4 c #f6bbe9",
-"#J5 c #f6bebe",
-".jP c #f6c0e4",
-".mK c #f6d4d0",
-".nu c #f6d8c8",
-"ad7 c #f6dada",
-"ahN c #f6dbdc",
-"#68 c #f6e1e1",
-"#NA c #f6e4e5",
-"agU c #f6efef",
-"#14 c #f6f0ef",
-"#YR c #f6f3f3",
-"#YP c #f6f4f4",
-"#4x c #f6f5f5",
-"#SF c #f6f5f6",
-"aam c #f6f6f5",
-".N7 c #f6f6f6",
-".Mu c #f6f6f7",
-"#Ae c #f6f6f8",
-"#LL c #f6f7f6",
-"#Bh c #f6f7f7",
-".94 c #f6f7f8",
-".0d c #f6f7f9",
-"#J# c #f6f8f7",
-".3h c #f6f8f8",
-".PU c #f6f8f9",
-".tN c #f6f94b",
-"ajc c #f6f9f8",
-"#X5 c #f6fafa",
-".az c #f755f4",
-".aq c #f756fa",
-".br c #f76dff",
-".bZ c #f777ff",
-".cC c #f780ff",
-".de c #f788fe",
-".dV c #f790fc",
-".fl c #f79ef7",
-".f8 c #f7a4f5",
-"aYb c #f7aaab",
-".gV c #f7aaf2",
-"a4P c #f7afad",
-"b.l c #f7afae",
-"#6q c #f7b0af",
-"#Yg c #f7b0b0",
-".hC c #f7b0ef",
-".O1 c #f7b1b1",
-".Qa c #f7b1b2",
-"aoK c #f7b2b2",
-"ax4 c #f7b3b2",
-".lt c #f7c5b0",
-".kz c #f7c6df",
-".lh c #f7cbd9",
-".ra c #f7e76d",
-"#XX c #f7e8ec",
-".q8 c #f7ec90",
-"#ZN c #f7f3f2",
-"#10 c #f7f5f4",
-"#4t c #f7f6f5",
-"#XT c #f7f7f6",
-".8# c #f7f7f7",
-".LK c #f7f7f8",
-".1d c #f7f7f9",
-"#9S c #f7f8f7",
-".14 c #f7f8f8",
-"#aE c #f7f8f9",
-".t. c #f7f96a",
-"#Qo c #f7f9f9",
-".bs c #f86dff",
-".b1 c #f877ff",
-".cD c #f880ff",
-".df c #f888fd",
-".dW c #f890fb",
-".eD c #f897fa",
-"a5E c #f8afae",
-"#7B c #f8b0ae",
-"#6r c #f8b1af",
-".Qf c #f8b1b2",
-".R9 c #f8b2b2",
-"axm c #f8b2b3",
-"#jQ c #f8b3b3",
-"bVf c #f8b4b4",
-".il c #f8b6eb",
-"#EG c #f8b8b8",
-".i5 c #f8bbe6",
-"#9y c #f8bcbd",
-".jQ c #f8c0e1",
-".l2 c #f8cfd3",
-".nE c #f8d39b",
-".mL c #f8d4cc",
-"#X4 c #f8f2f4",
-"#ME c #f8f3f3",
-"#3t c #f8f6f5",
-".7p c #f8f7f8",
-".6O c #f8f8f8",
-".QH c #f8f8f9",
-".XE c #f8f8fa",
-".LL c #f8f9f9",
-".3i c #f8f9fa",
-".Mz c #f8fafa",
-".bq c #f96dff",
-".b2 c #f977ff",
-".cF c #f980fd",
-".cE c #f980fe",
-".dg c #f988fc",
-".dX c #f990fa",
-".eE c #f997f8",
-".fm c #f99ef6",
-".gm c #f9a1d8",
-".f9 c #f9a4f3",
-".gW c #f9aaf0",
-".hD c #f9b0ed",
-".QY c #f9b2b1",
-"aFF c #f9b2b3",
-"#FB c #f9b3b2",
-"axn c #f9b3b3",
-"#EH c #f9b6b6",
-".im c #f9b6e8",
-".kA c #f9c6dc",
-".mb c #f9cbab",
-".li c #f9cbd6",
-".mV c #f9d0a4",
-".nv c #f9d8c3",
-"ahQ c #f9dada",
-".og c #f9dcba",
-".o2 c #f9e0b0",
-".pN c #f9e4a4",
-".sv c #f9f573",
-"#15 c #f9f5f4",
-"agA c #f9f6f6",
-"aoa c #f9f7f8",
-".VM c #f9f8f8",
-".54 c #f9f8f9",
-".LJ c #f9f9f9",
-".L9 c #f9f9fa",
-"#dg c #f9f9fb",
-"#6L c #f9faf9",
-".3f c #f9fafa",
-".Qx c #f9fafb",
-"#OE c #f9fbfb",
-"ahI c #f9fbfc",
-".aV c #fa62ff",
-".aU c #fa63ff",
-".bt c #fa6dff",
-".b4 c #fa77fe",
-".b3 c #fa77ff",
-".cG c #fa80fc",
-".dh c #fa88fb",
-".dY c #fa90f9",
-".eF c #fa97f7",
-".fn c #fa9ef5",
-".g. c #faa4f2",
-".gX c #faaaee",
-"#W2 c #faabac",
-".hE c #fab0ea",
-"a.X c #fab4b4",
-".i6 c #fabbe4",
-".jR c #fac0df",
-"#LK c #fac6c6",
-".l3 c #facfcf",
-".mM c #fad4c7",
-"a.V c #fae1e1",
-"#0P c #faf5f4",
-".Mt c #faf9fa",
-"#3o c #fafaf9",
-".LC c #fafafa",
-"#ce c #fafafb",
-"#iK c #fafafc",
-"#hK c #fafafd",
-"#ip c #fafbfb",
-"#vm c #fafbfc",
-"aal c #fafcfb",
-"#9N c #fafcfd",
-".aW c #fb63ff",
-".bv c #fb6dfe",
-".bu c #fb6dff",
-".b5 c #fb77fd",
-".cH c #fb80fc",
-".di c #fb88fa",
-".dZ c #fb90f8",
-".eG c #fb97f6",
-".fo c #fb9ef3",
-".g# c #fba4f0",
-"ajf c #fbb3b4",
-".in c #fbb6e6",
-"ag2 c #fbb9ba",
-".i7 c #fbbbe1",
-"a.W c #fbc1c1",
-".kB c #fbc6d9",
-".lj c #fbcbd2",
-".nw c #fbd9be",
-".qt c #fbe996",
-".rQ c #fbf17c",
-"agB c #fbf8f8",
-".4T c #fbf9fa",
-"#3u c #fbfaf9",
-"#YY c #fbfafb",
-"#jz c #fbfbfb",
-".Nz c #fbfbfc",
-"#gA c #fbfbfd",
-"#EP c #fbfcfb",
-"#lQ c #fbfcfc",
-".Pa c #fbfcfd",
-"#xG c #fbfdfc",
-".Or c #fbfdfd",
-".a. c #fc46fa",
-".#6 c #fc47fd",
-".at c #fc55ff",
-".ar c #fc56ff",
-".aX c #fc63ff",
-".bF c #fc6df4",
-".bw c #fc6dfe",
-".b6 c #fc77fc",
-".cJ c #fc80fa",
-".cI c #fc80fb",
-".dj c #fc88f9",
-".eb c #fc8fe8",
-".d0 c #fc90f7",
-".eH c #fc97f5",
-".fp c #fc9ef2",
-".gY c #fcaaec",
-".hF c #fcb0e8",
-"bSS c #fcb3b3",
-"#9z c #fcb4b4",
-"aFE c #fcb5b5",
-".io c #fcb6e4",
-".jS c #fcc0db",
-".kC c #fcc6d5",
-".l4 c #fccfcb",
-".oh c #fcddb4",
-".sx c #fcf457",
-"#.o c #fcfafa",
-"aob c #fcfafb",
-".t# c #fcfb56",
-".Qw c #fcfbfc",
-"#v4 c #fcfbfd",
-"#rS c #fcfcfb",
-"#fs c #fcfcfc",
-".P# c #fcfcfd",
-"#vo c #fcfdfc",
-".3j c #fcfdfd",
-"#LN c #fcfefd",
-"#wE c #fcfefe",
-"#yo c #fcfeff",
-"#Rg c #fcffff",
-".au c #fd55fe",
-".as c #fd55ff",
-".aZ c #fd63fd",
-".aY c #fd63fe",
-".bx c #fd6dfd",
-".b7 c #fd77fb",
-".cK c #fd80fa",
-".dk c #fd88f7",
-".d1 c #fd90f6",
-".eI c #fd97f3",
-".fq c #fd9ef0",
-".gb c #fda4ed",
-".ga c #fda4ee",
-".gZ c #fdaaeb",
-".hG c #fdb0e6",
-"axl c #fdb5b5",
-".iz c #fdb5cd",
-".i8 c #fdbbde",
-".jT c #fdc0d9",
-".lk c #fdcbce",
-".l5 c #fdcfc7",
-".mN c #fdd4c2",
-".nx c #fdd9b9",
-".o3 c #fde1a9",
-".pO c #fde59c",
-".q9 c #fded85",
-".rR c #fdf271",
-".sw c #fdf664",
-".3Q c #fdfbfb",
-"#mf c #fdfcfc",
-".NY c #fdfcfd",
-"#lj c #fdfdfc",
-".Ny c #fdfdfd",
-".NA c #fdfdfe",
-"#uh c #fdfefd",
-".1x c #fdfefe",
-"a.R c #fdfffe",
-"#YX c #fdffff",
-".#7 c #fe46ff",
-".av c #fe55fd",
-".a0 c #fe63fd",
-".bz c #fe6dfb",
-".by c #fe6dfc",
-".cd c #fe77f4",
-".b9 c #fe77fa",
-".b8 c #fe77fb",
-".cL c #fe80f9",
-".dv c #fe88ed",
-".dl c #fe88f6",
-".d3 c #fe90f4",
-".d2 c #fe90f5",
-".eJ c #fe97f2",
-".fr c #fe9eef",
-".g0 c #feaae9",
-".hH c #feb0e4",
-"bUi c #feb3b2",
-".ip c #feb6e1",
-".i9 c #febbdc",
-".jU c #fec0d6",
-".kD c #fec6d2",
-".ll c #fecbcb",
-".l6 c #fecfc4",
-".mO c #fed4be",
-".ny c #fed9b5",
-".oi c #feddaf",
-".o4 c #fee2a4",
-".pP c #fee697",
-".qu c #fee98e",
-".qx c #feea7c",
-".r. c #feee7d",
-".SC c #fefcfc",
-".Qv c #fefdfd",
-".Pb c #fefdfe",
-"#m4 c #fefefd",
-".NZ c #fefefe",
-".XD c #fefeff",
-".QI c #feffff",
-".#9 c #ff46fe",
-".#8 c #ff46ff",
-".ax c #ff55fc",
-".aw c #ff55fd",
-".ay c #ff56fc",
-".a4 c #ff62fa",
-".a3 c #ff63fa",
-".a2 c #ff63fb",
-".a1 c #ff63fc",
-".bE c #ff6df7",
-".bD c #ff6df8",
-".bC c #ff6df9",
-".bB c #ff6dfa",
-".bA c #ff6dfb",
-".cc c #ff77f5",
-".cb c #ff77f6",
-".ca c #ff77f7",
-".c# c #ff77f8",
-".c. c #ff77f9",
-".cS c #ff80f2",
-".cR c #ff80f3",
-".cQ c #ff80f4",
-".cP c #ff80f5",
-".cO c #ff80f6",
-".cN c #ff80f7",
-".cM c #ff80f8",
-".du c #ff88ee",
-".dt c #ff88ef",
-".ds c #ff88f0",
-".dr c #ff88f1",
-".dq c #ff88f2",
-".dp c #ff88f3",
-".do c #ff88f4",
-".dn c #ff88f5",
-".dm c #ff88f6",
-".ea c #ff8feb",
-".e# c #ff90ec",
-".e. c #ff90ed",
-".d9 c #ff90ee",
-".d8 c #ff90ef",
-".d7 c #ff90f0",
-".d6 c #ff90f1",
-".d5 c #ff90f2",
-".d4 c #ff90f3",
-".eS c #ff97e7",
-".eR c #ff97e9",
-".eQ c #ff97ea",
-".eP c #ff97eb",
-".eO c #ff97ec",
-".eN c #ff97ee",
-".eM c #ff97ef",
-".eL c #ff97f0",
-".eK c #ff97f1",
-".fA c #ff9ee3",
-".fz c #ff9ee4",
-".fy c #ff9ee6",
-".fx c #ff9ee7",
-".fw c #ff9ee8",
-".fv c #ff9eea",
-".fu c #ff9eeb",
-".ft c #ff9eec",
-".fs c #ff9eee",
-".gl c #ffa4df",
-".gk c #ffa4e0",
-".gj c #ffa4e1",
-".gi c #ffa4e3",
-".gh c #ffa4e4",
-".gg c #ffa4e6",
-".gf c #ffa4e7",
-".ge c #ffa4e8",
-".gd c #ffa4ea",
-".gc c #ffa4eb",
-".g9 c #ffaada",
-".g8 c #ffaadc",
-".g7 c #ffaade",
-".g6 c #ffaadf",
-".g5 c #ffaae1",
-".g4 c #ffaae2",
-".g3 c #ffaae4",
-".g2 c #ffaae5",
-".g1 c #ffaae7",
-".h. c #ffabda",
-".hQ c #ffb0d5",
-".hP c #ffb0d6",
-".hO c #ffb0d8",
-".hN c #ffb0da",
-".hM c #ffb0dc",
-".hL c #ffb0dd",
-".hK c #ffb0df",
-".hJ c #ffb0e1",
-".hI c #ffb0e2",
-"#V7 c #ffb4b3",
-"#V6 c #ffb5b2",
-"ax5 c #ffb6b6",
-".iy c #ffb6d0",
-".ix c #ffb6d2",
-".iw c #ffb6d4",
-".iv c #ffb6d6",
-".iu c #ffb6d8",
-".it c #ffb6da",
-".is c #ffb6dc",
-".ir c #ffb6de",
-".iq c #ffb6df",
-"bCS c #ffb7b7",
-".jf c #ffbbcb",
-".je c #ffbbcd",
-".jd c #ffbbcf",
-".jc c #ffbbd2",
-".jb c #ffbbd4",
-".ja c #ffbbd6",
-".j# c #ffbbd8",
-".j. c #ffbbda",
-".jg c #ffbcca",
-".j1 c #ffc0c6",
-".j0 c #ffc0c8",
-".jZ c #ffc0cb",
-".jY c #ffc0cd",
-".jX c #ffc0d0",
-".jW c #ffc0d2",
-".jV c #ffc0d4",
-".j2 c #ffc1c5",
-".kL c #ffc6bf",
-".kK c #ffc6c0",
-".kJ c #ffc6c3",
-".kI c #ffc6c6",
-".kH c #ffc6c8",
-".kG c #ffc6cb",
-".kF c #ffc6cd",
-".kE c #ffc6d0",
-".ls c #ffcbb8",
-".lr c #ffcbbb",
-".lq c #ffcbbe",
-".lp c #ffcbc0",
-".lo c #ffcbc3",
-".ln c #ffcbc6",
-".lm c #ffcbc8",
-".ma c #ffcfb2",
-".m# c #ffcfb4",
-".m. c #ffcfb8",
-".l9 c #ffcfbb",
-".l8 c #ffcfbe",
-".l7 c #ffcfc1",
-".mU c #ffd4ab",
-".mT c #ffd4ae",
-".mS c #ffd4b1",
-".mR c #ffd4b5",
-".mQ c #ffd4b8",
-".mP c #ffd4bb",
-".nD c #ffd9a3",
-".nC c #ffd9a6",
-".nB c #ffd9aa",
-".nA c #ffd9ae",
-".nz c #ffd9b2",
-".om c #ffdd9e",
-".ol c #ffdda3",
-".ok c #ffdda7",
-".oj c #ffddab",
-".on c #ffde9b",
-".o7 c #ffe296",
-".o6 c #ffe29b",
-".o5 c #ffe29f",
-".o8 c #ffe392",
-".pR c #ffe68c",
-".pQ c #ffe692",
-".pS c #ffe788",
-".qw c #ffea82",
-".qv c #ffea88",
-".r# c #ffee76",
-".rS c #fff368",
-".St c #fffdfd",
-".Ry c #fffdfe",
-"#zH c #fffefd",
-".Tu c #fffefe",
-".Vx c #fffeff",
-"#rT c #fffffe",
-".PB c #ffffff",
-"Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.Qt.",
-"Qt#Qt#Qt#Qt#Qt#Qt#Qt#QtaQt.Qt.QtaQt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#",
-"Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#QtbQt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#Qt#",
-"QtcQtcQtcQtcQtcQtcQtcQtcQtdQteQtfQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQt#QtgQtbQthQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtc",
-"QtcQtcQtcQtcQtcQtcQtcQtcQtiQtjQtkQtlQtmQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQt#QtnQtoQtpQtiQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtcQtc",
-"QtbQtbQtbQtbQtbQtbQtbQtbQtcQtqQtrQtsQttQtiQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtcQtnQtiQtuQtoQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtcQtbQtvQtvQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtb",
-"QtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtwQtxQtyQtzQtAQtiQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtBQtBQtBQtCQtCQtCQtDQtBQtBQtBQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtcQtnQtiQtuQtoQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtiQtvQtgQtiQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtiQtEQtuQtFQtbQtiQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtb",
-"QtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtbQtGQtHQtIQtJQtAQtiQtiQtiQtiQtiQtiQtiQtiQtiQtBQtBQtCQtbQtKQtLQtMQtNQtNQtOQtPQtQQtbQtBQtBQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtbQtRQtiQtSQtvQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtoQtcQtTQtUQt.QtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtVQtWQtXQtYQtvQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQti",
-"QtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtZQt0Qt1Qt2QtoQtoQtiQtiQtiQtiQtoQt3QtBQt4QtNQt5Qt6Qt7Qt8Qt9.#..##.#a.#b.#c.#d.#e.#f.#gQtBQt3QtoQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtbQtRQtiQtuQtvQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtoQti.#h.#i.#jQt#QtvQtiQtiQtiQtiQtiQtiQtiQtiQtiQtoQtV.#k.#l.#m.#nQtvQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQtiQti",
-"QtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQti.#o.#p.#q.#rQtA.#sQtoQtoQtoQtiQtP.#t.#u.#v.#w.#x.#y.#z.#A.#B.#C.#D.#E.#F.#G.#H.#I.#J.#K.#L.#M.#NQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQti.#OQtiQtuQtgQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQto.#P.#Q.#R.#i.#S.#TQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtV.#U.#V.#WQth.#PQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQto",
-"QtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQto.#XQt0.#Y.#ZQtoQtg.#N.#0.#1.#2.#3.#4.#5.#6.#7.#8.#7.#8.#8.#8.#8.#9.#8.#9.a..a#.aa.ab.ac.ad.ae.#NQtv.#NQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQto.#OQtiQtuQtgQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQto.#T.#S.#i.afQthQtVQtoQtoQtoQtoQtoQtoQtoQtoQtv.#T.ag.#l.#m.ahQtVQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtv.#PQtgQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQtoQto",
-"QtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtv.ai.aj.ak.al.#g.am.an.ao.ap.aq.ar.as.at.at.at.as.as.au.av.aw.aw.aw.ax.ax.aw.ay.az.aA.aB.aC.aDQtoQtgQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtv.aEQtiQtS.#PQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtv.aF.aG.aH.aIQtvQtgQtvQtvQtvQtvQtvQtvQtvQtv.aF.aJ.#l.aK.aLQtVQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtg.#P.aM.aNQtgQtvQtvQtvQtvQtvQtv.#P.#T.#PQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtv",
-"QtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtg.aO.aj.aP.aQ.aR.aS.aT.aU.aU.aV.aU.aW.aW.aX.aX.aY.aY.aZ.a0.a1.a1.a2.a2.a3.a4.a3.a3.a5.a6.a7.a8.a9.b.QtgQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtv.#OQti.b#.#PQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtv.aF.ba.bb.bc.aFQtvQtvQtvQtvQtvQtvQtvQtg.aF.bd.be.bfQtbQtVQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtv.#PQt#.bg.bh.bi.#TQtvQtvQtvQtvQtgQtV.bj.bkQtuQtVQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtvQtv",
-"QtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtg.bl.bm.bn.bo.bp.bq.br.bs.bq.bq.bt.bt.bu.bv.bw.bx.bx.by.bz.bA.bB.bB.bC.bC.bD.bE.bD.bF.bG.bH.bI.a9.#PQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtg.aEQtiQtSQtVQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtV.bJ.bK.bL.aFQtgQtgQtgQtgQtgQtg.#PQtV.bM.bhQtp.#P.#PQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtg.#PQtv.bN.bO.bP.#TQtgQtgQtgQtgQtg.#T.bQ.bR.bb.bP.aFQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtg",
-"QtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtg.#P.#P.bS.bT.bU.bV.bW.bX.bY.bZ.b0.bZ.b1.b1.b2.b3.b4.b5.b6.b6.b7.b8.b9.c..c#.c#.ca.cb.cb.cc.cc.cd.ce.cf.cg.ch.ci.#PQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtg.aEQtiQtSQtVQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtVQti.#m.cj.ck.#TQtgQtgQtgQtg.#P.clQt..bk.#j.cm.cl.#PQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtg.#T.ah.cn.co.#T.#PQtgQtgQtgQtg.#P.cl.cp.aH.cq.cr.#TQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtgQtg",
-".#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#PQtVQtg.cs.ct.cu.cv.cw.cx.cy.cz.cA.cB.cA.cC.cD.cE.cF.cG.cH.cI.cJ.cK.cL.cM.cN.cO.cO.cP.cQ.cR.cS.cS.cS.cT.cU.cVQtgQtV.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.cWQtiQtS.#T.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#T.aL.bh.cX.#PQtV.#P.#P.#PQtV.cl.cm.cY.bk.cr.cl.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#T.crQtY.cZ.aF.#P.#P.#P.#P.#P.#TQtc.c0.bO.c1.c2.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P",
-"QtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.#T.#P.c3.c4.c5.c6.c7.c8.c9.d..d#.da.db.db.dc.dd.de.df.dg.dh.di.dj.dj.dk.dl.dm.dn.do.dp.dq.dr.ds.dt.du.dv.dw.dx.dyQtVQtV.#PQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.dzQtiQtS.aFQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.cl.aM.cn.dA.c2.#P.#PQtV.c2Qtc.cp.dB.dC.aF.#T.#PQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.#P.#PQtVQtV.#PQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.#PQtV.#T.#T.#PQtVQtVQtVQtVQtV.cl.dD.#R.be.dE.cl.#PQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV",
-"QtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.#T.dF.dG.dH.dI.dJ.dK.dL.dM.dN.dO.dP.dQ.dR.dS.dS.dT.dU.dV.dW.dX.dY.dZ.d0.d1.d2.d3.d4.d5.d6.d7.d8.d9.e..e#.ea.eb.ec.ed.ee.#T.#TQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.cWQti.b#.aFQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.c2.aJ.bO.ef.eg.#T.c2.#P.eh.ei.bN.aL.egQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.clQtvQtoQtVQtVQtV.c2.cl.aFQtVQtVQtVQtVQtVQtV.#T.c2.aFQtVQtVQtVQtVQtV.aF.cl.ej.bL.#i.ek.cl.c2.cl.cl.#TQtVQtVQtVQtVQtVQtV.aF.eg.c2.aF.aF.c2.aFQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV",
-".#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.aF.el.em.en.eo.ep.ep.eq.er.es.et.eu.ev.ew.ex.ey.ez.eA.eB.eC.eD.eE.eF.eG.eH.eI.eJ.eK.eL.eM.eN.eO.eP.eQ.eR.eS.eS.eT.eU.eVQtV.aF.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.cWQti.bP.cl.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#TQtV.aFQtnQtu.bK.aMQtRQti.eh.eW.cY.eX.cl.clQtV.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.cl.eh.dB.eY.eg.aF.aF.#k.bJ.bd.cl.#T.#T.#TQtV.aFQt#.cp.eZQtE.aF.#T.#T.aF.e0.dD.e1.c1.bO.#h.cm.#Q.#Q.bLQtb.#T.#T.#TQtV.aF.eg.e2.#S.ef.aN.ck.b#.#n.aF.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T",
-".#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.aF.e3.e4.e5.e6.e7.e7.e8.e9.f..f#.fa.fb.fc.fd.fe.ff.fg.fh.fi.fj.fk.fl.fm.fn.fo.fp.fq.fr.fs.ft.fu.fv.fw.fx.fy.fz.fA.fA.fB.fC.fD.aF.aF.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.dzQti.b#.cl.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.aFQtE.aL.fE.#l.b#.c1.c0.cqQtpQto.eg.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.c2.fF.af.cn.fG.fH.#T.fE.#i.aH.fI.c2.#T.#T.#T.aF.cl.fJQtX.eZ.aF.aF.#T.c2.e2.cq.#l.bh.#l.be.fK.aI.bJ.bf.b#Qtb.aF.#T.#T.c2.fL.fJ.dB.eW.eW.eW.#W.fM.fN.fH.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T.#T",
-".aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.cl.#T.fO.fP.fQ.fR.fS.fT.fU.fV.fW.fX.fY.fZ.f0.fd.f1.f2.f3.f4.f5.f6.f7.f8.f9.g..g#.ga.gb.gc.gd.ge.gf.gg.gh.gi.gj.gk.gl.gm.gn.go.gp.cl.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.gqQti.b#.c2.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.c2.ah.cq.be.gr.#l.cX.gsQtc.fH.cl.#T.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.ej.aG.bb.aK.cl.c2.gt.gu.c1.aH.gv.fH.aF.aF.aF.eg.e1.gr.gw.cZ.eg.#T.aF.aFQti.ah.fFQth.cj.fI.aF.aF.cl.eg.eg.aF.#T.#T.c2Qt#.cY.bR.ah.cl.cl.gx.gy.cn.bi.fH.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF",
-".aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.cl.cl.gz.gA.gB.gC.gD.gE.gF.gG.gH.gI.gJ.gK.gL.gMQtr.gN.gO.gP.gQ.gR.gS.gT.gU.gV.gW.gX.gY.gZ.g0.g1.g2.g3.g4.g5.g6.g7.g8.g9.h..h#.ha.hb.cl.cl.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.gqQti.b#.c2.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.fH.hc.be.#i.bcQtR.ej.c2.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.clQtV.hd.bO.bd.aE.fN.bg.ej.gv.gw.bQ.eg.aF.aF.aF.fH.bg.fM.bk.eg.cl.aF.aF.aF.cl.egQtR.bj.bKQtUQtn.cl.aF.aF.aF.aF.aF.cl.eg.bf.bK.bLQtn.clQtn.ag.bO.aK.#T.c2.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF.aF",
-".cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.c2.cl.he.hf.hg.hh.hi.hj.hk.hl.hm.hn.ho.hp.hq.hr.hs.ht.hu.hv.hw.hx.hy.hz.hA.hB.hC.hD.hE.hF.hG.hH.hI.hJ.hK.hL.hM.hN.hO.hP.hQ.hR.hS.hT.hU.c2.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.hVQti.bP.eg.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.c2.#P.#j.bO.hd.cr.fH.aF.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.eg.gx.gw.hWQto.fG.coQtVQtv.eW.#U.fH.cl.cl.cl.c2Qto.dB.gr.fG.fH.cl.cl.cl.cl.cl.clQtn.aG.gw.fL.eg.cl.cl.cl.cl.cl.cl.ej.dD.bK.#h.eg.cl.cl.c2.hX.cqQtE.eg.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl",
-".cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.c2.cl.hY.hZ.h0.h1.h2.h3.h4.h5.h6.h7.h8.h9.i..i#.ia.ib.ic.id.ie.if.ig.ih.ii.ij.ik.il.im.in.io.ip.iq.ir.is.it.iu.iv.iw.ix.iy.iz.iA.iB.hU.c2.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.hVQti.bP.eg.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.fHQt..bR.dB.#V.iCQtb.ej.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.fHQtE.gwQtWQtv.hX.bQ.ej.#PQtE.fH.c2.cl.cl.cl.egQti.hW.aK.cl.c2.cl.cl.cl.cl.cl.c2.c2.eW.fI.eg.c2.cl.cl.cl.cl.cl.c2.#T.iD.af.gx.fH.cl.ej.fG.bK.aGQtR.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl.cl",
-".c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.eg.iE.iF.iG.iH.iI.iJ.iK.iL.iM.iN.iO.iP.iQ.iR.iS.iT.iU.iV.iW.iX.iY.iZ.i0.i1.i2.i3.i4.i5.i6.i7.i8.i9.j..j#.ja.jb.jc.jd.je.jf.jg.jh.ji.jj.eg.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.jkQti.jl.fH.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.ej.e0.#V.bP.dC.bO.aKQto.fH.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.ej.e2.be.jmQtW.bPQtR.c2.c2.fH.c2.c2.c2.c2.c2.ej.e0.gw.#UQtn.c2.c2.c2.c2.c2.c2.ej.aL.be.jlQtR.c2.c2.c2.c2.c2.c2Qtn.eX.aH.jnQtn.c2.eg.c2.bJ.bK.aM.ej.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2",
-".c2.c2.c2.c2.c2.c2.c2.c2.c2.eg.eg.jo.jp.jq.jr.js.jt.ju.jv.jw.jx.jy.jz.jA.jB.jC.jD.jE.jF.jG.jH.jI.jJ.jK.jL.jM.jN.jO.jP.jQ.jR.jS.jT.jU.jV.jW.jX.jY.jZ.j0.j1.j2.j3.j4.j5.eg.eg.c2.c2.c2.c2.c2.c2.c2.c2.c2.hVQti.bP.fH.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2Qtn.aM.cn.ag.aE.#S.bb.j6QtV.ej.c2.c2.c2.c2.c2.c2.c2.c2.c2.eg.fH.c2.c2.c2.c2.c2QtR.bP.#j.#k.fI.cl.fH.c2.c2.c2.c2.c2.c2.c2.c2QtRQtS.j7Qto.fH.c2.c2.c2.c2.c2.c2QtR.#k.#l.#n.ej.c2.c2.c2.c2.c2.eg.c2.hX.j8.ck.ej.c2.#O.aJ.bK.c0Qtg.fH.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2.c2",
-".eg.eg.eg.eg.eg.eg.eg.eg.eg.fH.j9.k..k#.ka.kb.kc.kd.ke.kf.kg.kh.ki.kj.kk.kl.km.kn.ko.kp.kq.kr.ks.jI.kt.ku.kv.kw.kx.ky.kz.kA.kB.kC.kD.kE.kF.kG.kH.kI.kJ.kK.kL.kM.kN.kO.kP.fH.eg.eg.eg.eg.eg.eg.eg.eg.eg.jkQti.bP.ej.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.egQtRQtT.cn.fGQtR.ej.c1.fM.eZ.#T.ej.eg.eg.eg.eg.eg.eg.eg.eg.clQtc.eg.eg.eg.eg.egQtR.bk.co.iD.fGQtR.eg.eg.eg.eg.eg.eg.eg.eg.fH.c2.iC.dAQtR.eg.eg.eg.eg.eg.eg.egQtRQtY.hd.c2.fH.eg.eg.eg.eg.egQtnQth.cn.baQtR.egQtn.ck.hd.cn.#hQtR.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg",
-".eg.eg.eg.eg.eg.eg.eg.eg.eg.fH.j9.kQ.kR.kS.kT.kU.kV.kW.kX.kY.kZ.k0.k1.k2.k3.k4.k5.k6.k7.k8.k9.l..l#.la.lb.lc.ld.le.lf.lg.lh.li.lj.lk.ll.lm.ln.lo.lp.lq.lr.ls.lt.lu.lv.kP.fH.eg.eg.eg.eg.eg.eg.eg.eg.eg.jkQti.bP.ej.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.#O.ef.gr.fFQtn.ej.fHQtW.fM.co.#PQtn.eg.eg.eg.eg.eg.egQtnQt..lwQtv.ej.eg.eg.ej.#T.cY.j7.bfQtR.fH.eg.eg.eg.eg.eg.eg.eg.egQtR.#n.af.bj.#O.eg.eg.eg.eg.eg.eg.ej.#P.hW.aG.#O.eg.eg.eg.eg.eg.eg.#O.jn.#R.ahQtR.ejQtV.fJQtW.bK.bd.aE.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg.eg",
-".fH.fH.fH.fH.fH.fH.fH.fH.fH.ej.lx.ly.lz.lA.lB.lC.lD.lE.lF.lG.lH.lI.lJ.lK.lL.lM.lN.lO.lP.lQ.lR.lS.lT.lU.lV.lW.lX.lY.lZ.l0.l1.l2.l3.l4.l5.l6.l7.l8.l9.m..m#.ma.mb.mc.md.me.ej.fH.fH.fH.fH.fH.fH.fH.fH.fH.mfQti.jlQtn.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.aE.eY.be.ckQtR.fHQtn.egQtW.fM.lwQtgQtR.fH.fH.fH.fH.fHQtnQti.ei.gxQtR.fH.fHQtR.e0.gw.gw.fFQtR.fH.fH.fH.fH.fH.fH.fH.fH.fH.aEQtu.j7QtgQtn.fH.fH.fH.fH.fH.fH.#O.dE.#R.cm.#O.fHQtn.ej.fH.fH.ej.aF.ei.aI.egQtn.ej.gt.bd.c1.cn.fFQtR.fHQtn.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH",
-".fH.fH.fH.fH.fH.fH.fH.fH.fH.ej.lx.mg.mh.mi.mj.mk.ml.mm.mn.mo.mp.mq.mr.ms.mt.mu.mv.mw.mx.my.mz.mA.mB.mC.mD.mE.mF.mG.mH.mI.mJ.mK.mL.mM.mN.mO.mP.mQ.mR.mS.mT.mU.mV.mW.mX.me.ej.fH.fH.fH.fH.fH.fH.fH.fH.fH.mYQti.gsQtn.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.aEQtW.j8QtcQtn.fH.fHQtn.ej.cp.bb.aIQtc.aE.fH.fH.fH.fHQtn.#P.la.e0.#O.fH.fH.cW.dC.bb.bk.#O.ej.fH.fH.fH.fH.fH.fH.fH.fH.ej.fH.eW.bk.#OQtRQtR.fH.fH.fH.fH.fH.aE.mZ.bhQtiQtR.aE.fFQtbQtn.fH.#O.fF.#R.bM.cW.#O.mZ.ba.fH.la.j7.#PQtR.fH.cZ.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH.fH",
-".ej.ej.ej.ej.ej.ej.ej.ej.ejQtn.m0.m1.m2.m3.m4.m5.m6.m7.m8.m9.n..n#.na.nb.nc.nd.ne.nf.ng.nh.ni.nj.nk.nl.nm.nn.no.np.nq.nr.ns.nt.nu.nv.nw.nx.ny.nz.nA.nB.nC.nD.nE.nF.nG.nHQtn.ej.ej.ej.ej.ej.ej.ej.ej.ej.mfQti.jlQtR.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.#O.nI.cjQtvQtR.ej.ej.ejQtRQtR.bM.#i.#m.e0.cW.ej.ej.ejQtRQto.la.cr.#O.ejQtn.ej.eZ.#i.jm.cW.ej.ej.ej.ej.ej.ej.ej.ej.ej.#O.fF.gr.#k.jk.e0.fFQtR.ej.ej.ejQtn.fH.eW.hXQtR.cW.nJQtYQtvQtR.ej.cW.dC.bO.bj.hVQtu.nI.fHQt..gw.ek.aEQtn.dC.dD.#O.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej.ej",
-"QtnQtnQtnQtnQtnQtnQtnQtnQtnQtR.nK.nL.nM.nN.nO.nP.nQ.nR.nS.nT.nU.nV.nW.nX.nY.nZ.n0.n1.n2.n3.n4.n5.n6.n7.n8.n9.o..o#.oa.ob.oc.od.oe.of.og.oh.oi.oj.ok.ol.om.on.oo.op.oq.orQtRQtnQtnQtnQtnQtnQtnQtnQtnQtn.mfQti.jl.#OQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtn.ej.cX.bRQtgQtRQtnQtnQtn.ejQtn.aE.#k.#l.gy.ag.#O.aEQtn.aE.e0.cX.c2QtRQtn.cW.bi.bK.fK.egQtRQtnQtnQtnQtnQtnQtnQtnQtn.ej.cW.bM.#i.b#.jn.aI.fF.#OQtnQtnQtn.#OQtE.be.gv.aE.gs.fE.#P.#O.ejQtn.dz.os.#i.ef.jn.j6.#P.gq.jm.bK.lwQt#.gs.aM.aEQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtn",
-"QtnQtnQtnQtnQtnQtnQtnQtnQtnQtR.ot.ou.ov.ow.ox.oy.oz.oA.oB.oC.oD.oE.oF.oG.oH.oI.oJ.oK.oL.oM.oN.oO.oP.oQ.oR.oS.oT.oU.oV.oW.oX.oY.oZ.o0.o1.o2.o3.o4.o5.o6.o7.o8.o9.p..p#QtRQtRQtnQtnQtnQtnQtnQtnQtnQtnQtn.mYQti.jl.#OQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtn.#O.cl.#j.cn.bQ.aEQtnQtnQtnQtnQtnQtR.dz.e2.dB.bO.bk.gx.#O.gq.osQtS.dzQtnQtn.dz.aG.paQtu.dzQtnQtnQtnQtnQtnQtnQtnQtnQtnQtR.ej.iC.bb.#V.pbQto.cWQtnQtnQtnQtn.cW.e1.bO.pc.lwQtp.ej.#OQtnQtnQtn.dz.aG.fM.bKQtY.#T.aE.dz.bi.aH.gr.#h.fL.cWQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtnQtn",
-"QtRQtRQtRQtRQtRQtRQtRQtRQtRQtR.pd.pe.pf.pg.ph.pi.pj.pk.pl.pm.pn.po.pp.pq.pr.ps.pt.pu.pv.pw.px.py.pz.pA.pB.pC.pD.pE.pF.pG.pH.pI.pJ.pK.pL.pM.pN.pO.pP.pQ.pR.pS.pT.pU.nK.#O.pVQtRQtRQtRQtRQtRQtRQtRQtRQtR.pWQti.gs.aEQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtR.#O.fH.fJ.lwQtv.#OQtRQtRQtRQtRQtRQtRQtR.dz.#P.cp.#l.#V.cX.bJ.fE.eg.#OQtRQtR.cWQtu.c1.aF.#OQtRQtRQtRQtRQtRQtRQtRQtRQtR.#O.fH.pb.bkQth.cW.aEQtnQtRQtRQtRQtR.aE.fL.fK.nI.ah.dz.#OQtnQtRQtRQtR.aE.aNQtY.aJQtn.aEQtn.#O.#T.jl.bi.fH.cWQtnQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtR",
-"QtRQtRQtRQtRQtRQtRQtRQtRQtRQtR.#O.pV.pX.pY.pZ.p0.p1.p2.p3.p4.p5.p6.p7.p8.p9.q..q#.qa.qb.qc.qd.qe.qf.qg.qh.qi.qj.qk.pE.ql.qm.qn.qo.qp.qq.qr.qs.qt.qu.qv.qw.qx.qy.qzQtR.#OQtRQtRQtRQtRQtRQtRQtRQtRQtRQtR.pWQti.gs.aEQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtR.#O.ejQtn.aEQtRQtRQtRQtRQtRQtRQtRQtRQtR.aE.dz.fL.fN.pb.bd.fH.aEQtRQtRQtRQtRQtR.cW.#OQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtR.#OQtR.aE.cWQtRQtRQtRQtRQtRQtRQtRQtR.#O.fH.aE.cWQtRQtRQtRQtRQtRQtRQtR.aE.cW.dz.#OQtRQtRQtR.#O.gq.dz.#OQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtR",
-".#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.aE.#O.qA.qB.qC.qD.qE.qF.qG.qH.qI.qJ.qK.qL.qM.qN.qO.qP.qQ.qR.qS.qT.qU.qV.qW.qX.qY.qZ.q0.pE.q1.q2.q3.q4.q5.q6.q7.q8.q9.r..r#.ra.rb.rc.rd.aE.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.reQti.ef.cW.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.cW.gq.gq.gq.#OQtR.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O",
-".#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.aE.rf.rg.rh.ri.rj.rk.rl.rm.rn.ro.rp.rq.rr.rs.rt.ru.rv.rw.rx.ry.rz.rA.rB.rC.rD.rE.rF.rG.rH.rI.rJ.rK.rL.rM.rN.rO.rP.rQ.rR.rS.rT.rU.rV.aE.aE.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.reQti.gs.cW.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O.#O",
-".aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.rW.rX.rY.rZ.r0.r1.r2.r3.r4.r5.r6.r7.r8.r9.s..s#.sa.sb.sc.sd.se.sf.sg.sh.si.sj.sk.sl.sm.sn.so.sp.sq.sr.ss.st.su.sv.sw.sx.sy.sz.#O.cW.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.sAQti.gs.dz.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE",
-".aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.cW.rW.sB.sC.sD.sE.sF.sG.sH.sI.sJ.sK.sL.sM.sN.sO.sP.sQ.sR.sS.sT.sU.sV.sW.sX.sY.sZ.s0.s1.s2.s3.s4.s5.s6.s7.s8.s9.t..t#.ta.tb.tc.td.cW.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.reQti.gs.dz.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.cW.cW.cW.cW.gq.hV.cW.aE.aE.cW.gq.dz.aE.aE.aE.aE.aE.aE.aE.aE.cW.dz.dz.aE.aE.cW.cW.aE.aE.aE.aE.aE.aE.aE.aE.aE.cW.aE.aE.aE.aE.aE.aE.aE.cW.cW.cW.cW.gq.gq.aE.aE.aE.aE.aE.aE.aE.aE.cW.gq.dz.aE.aE.aE.aE.aE.aE.aE.aE.aE.cW.cW.aE.aE.cW.gq.cW.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.cW.hV.aEQtn.aE.gq.cW.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.cW.cW.aE.aE.cW.gq.cW.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.cW.cW.cW.cW.cW.cW.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.cW.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE.aE",
-".cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.te.tf.tg.th.ti.tj.tk.tl.tm.tn.to.tp.tq.tr.ts.tt.tu.tv.tw.tx.ty.tz.tA.tB.sZ.tC.tD.tE.tD.tF.tG.tH.tI.tJ.tK.tL.tM.tN.tO.tP.tQ.tR.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.sAQti.ef.gq.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.hV.bi.jn.#S.#S.b#.dE.#O.dz.dzQtRQtS.fL.gq.cW.cW.cW.cW.cW.cW.cW.cWQtU.e2.hV.cW.fH.aE.cW.cW.cW.cW.cW.cW.cW.cW.hV.fF.#k.gq.cW.cW.cW.cW.dz.#U.os.#S.#SQtp.bQ.dz.cW.cW.cW.cW.cW.cW.dzQtRQtS.fL.gq.cW.cW.cW.cW.cW.cW.cW.cWQtnQtn.cW.dz.fHQtpQt#.gq.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.dzQtR.bL.jn.c1.aG.#Q.#O.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.ejQtR.cW.gqQtv.fN.#T.dz.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.dz.#T.#S.mZ.mZ.mZ.#SQtv.gq.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.hV.e0.bd.gq.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW",
-".cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.te.te.tS.tT.tU.tV.tW.tX.tY.tZ.t0.t1.t2.t3.t4.t5.t6.t7.t8.t9.u..tA.u#.sZ.tD.ua.ub.tE.uc.ud.ue.uf.ug.uh.ui.uj.uk.ul.um.un.dz.tR.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.sAQti.ef.gq.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.mf.b#QtFQtoQt.Qth.bc.mZ.ej.hV.#O.aJQt#.hV.cW.dz.hV.hV.gq.gq.dz.dz.nJ.gx.jkQtRQtF.ej.jk.cW.dz.hV.jk.jk.gq.cW.mf.cm.bc.jk.cW.cW.cW.cW.hV.c1.dCQtoQtc.jm.bN.ck.jk.gq.hV.jk.hV.dz.dz.#O.aJQt#.gq.gq.dz.gq.jk.hV.dz.cW.mYQtT.jm.pW.gqQtn.#UQto.gq.gq.dz.gq.jk.hV.dz.cW.cW.dz.hV.jk.hV.gq.gq.cW.cW.cW.cW.cW.cW.cW.dz.aE.eY.c1.aL.#P.cr.jmQtR.gq.gq.dz.hV.dz.cW.dz.hV.jk.gq.dz.cW.dz.gq.hV.jk.gq.dz.cW.mf.ef.ck.mY.gq.#T.#k.c2.hV.gq.cW.cW.cW.dz.gq.cW.cW.dz.hV.jk.gq.cW.cW.cW.cW.hVQti.eZ.fFQtcQt.Qt#QtR.gq.gq.dz.hV.dz.cW.dz.hV.jk.hV.dz.cW.cW.dz.gq.jk.hV.dz.cW.cW.dz.gq.jk.pW.jm.cp.jk.cW.dz.hV.jk.hV.dz.cW.cW.gq.dz.gq.jk.gq.cW.gq.jk.hV.dz.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW.cW",
-".dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.uo.up.uq.ur.us.ut.uu.uv.uw.ux.uy.uz.uA.uB.uC.uD.uE.uF.uG.uH.uI.tB.sZ.tC.tD.ub.s0.ud.uJ.uK.uL.uM.uN.uO.uP.uQ.uR.uS.dz.gq.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.uTQtiQtU.hV.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.mf.bPQtU.pW.hV.mf.gq.mZQtU.jk.dz.#PQtR.dz.hVQtn.bQ.ah.egQtgQtn.gq.#T.ej.cW.e0.fJ.aNQto.dz.#OQt#Qth.ah.c2.hV.mf.bj.#S.jk.dz.dz.dz.dz.hV.cp.e1.pW.hV.pW.bcQtT.mY.aF.e0.cZ.cr.dz.dz.dz.#PQtR.gqQtV.fHQtvQth.ck.dz.gq.aF.bcQtUQt.Qtn.dz.#P.#O.gq.#PQtnQti.cZ.cr.dz.dz.dz.hVQtb.cZQtc.egQtV.gq.dz.dz.dz.dz.dz.dz.mf.e1.pbQtn.mf.gq.hV.jk.gq.ej.#T.#O.fL.cl.hV.gq.cr.cZQti.hV.dz.cWQtg.aNQthQto.hV.dzQto.gt.nJQt#.aE.aEQtg.cW.fHQto.dz.dz.dzQtnQti.cW.hVQtR.e0.ah.c2.hV.dz.dz.dz.hVQtoQtY.aF.mf.hV.hV.gq.ejQtV.aE.fL.aF.gq.gqQt#.cZQtc.gq.dz.dz.hVQti.cZ.cr.gq.dz.dz.hVQto.cZQtc.dE.aG.jk.gq.gqQt..cZ.ck.cW.gq.gqQtVQtnQtiQth.#P.mfQtv.cZQtb.gq.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz",
-".dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.uo.uU.uV.uW.uX.uY.uZ.u0.u1.u2.u3.u4.u5.u6.u7.u8.u9.v..v#.va.vb.vc.vd.ve.vf.vg.vh.vh.vi.vj.vk.vl.vm.vn.vo.vp.vq.vr.vs.gq.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.vtQti.ef.hV.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.mf.jl.ef.mf.dz.dz.mY.fL.fJQtn.gq.ba.aL.mY.cl.jn.fN.bd.fN.#hQt#.mf.bc.dE.#O.#k.eZ.eh.dE.mf.aL.mZ.dC.bPQtWQtv.pW.fG.eY.jk.dz.dz.dz.dz.hV.cp.cm.mf.gq.aF.fJ.aN.hV.gsQtS.#k.ba.aJ.mf.dz.ba.aL.pW.jl.cpQtS.#kQtWQtT.mf.fF.#h.bc.#Q.c2.aE.fEQt#.pW.fN.aGQtS.eh.fE.bi.mf.jk.nJ.dA.dCQtS.ba.jl.mf.dz.dz.dz.dz.dz.gq.hV.cp.bL.mY.dz.dz.dz.dz.jk.#n.mZ.gsQtFQtb.hV.bd.jn.eh.gv.cZ.mY.clQtF.gs.dC.fE.e2.jk.fG.nIQtu.dD.#OQtV.bg.egQtv.pb.c2.hV.mf.dDQtF.mY.cl.aG.fN.ef.c1Qtg.hV.dz.dz.hVQtoQtY.c2.pW.mf.mf.mf.ck.aG.ef.eYQt..mf.#U.c1.eh.c1.fG.mf.jk.bL.ba.dC.aG.#Q.jk.mf.aM.gt.bdQtS.jn.bc.mf.gq.bd.c1.eh.osQtp.#O.mY.ef.bcQtS.gs.dA.dDQtFQtU.#h.gx.mf.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz.dz",
-".gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.vu.vu.vv.vw.vx.vy.vz.vA.vB.vC.vD.vE.vF.vG.vH.vI.vJ.vK.vL.vM.vN.vO.vP.vQ.vR.vS.vT.vU.vV.vW.vX.vY.vZ.v0.v1.v2.hV.vs.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.uTQti.ef.jk.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.mY.bP.ef.mY.gq.gq.jk.#T.bN.#P.hVQtW.aL.sA.bdQtS.mY.reQtE.bJQt#.mY.bc.aM.pWQtV.pbQtR.pW.gq.cW.hV.sA.v3QtS.aJ.uT.bj.#S.mf.gq.gq.gq.gq.jk.os.pb.#S.mZ.dA.bd.cW.hV.cW.pW.v3Qtg.#hQtn.gqQtW.aL.reQtS.c1.dz.sA.ck.pbQtR.reQtF.bj.sA.jk.#O.pbQt#.re.eY.mZ.hV.sA.bQ.gt.gqQtv.fJQtg.re.hV.jnQtS.mY.gq.gq.gq.gq.gq.hV.aE.bf.#n.mf.gq.gq.gq.gq.mf.gx.lw.e0.mY.mY.e2.dA.dz.v3Qtv.cp.aE.gq.dz.re.v4Qth.jn.jk.hV.ba.fL.re.jk.#P.bf.c2.mf.os.jm.mY.gq.cp.ah.re.b#.dC.uT.v3.bi.bd.mY.gq.gq.jkQtv.eZ.ef.hc.#k.cm.jk.cr.fI.e2.mY.mY.#n.fE.#O.v3.aF.c1.#OQtb.pb.c2.v3Qtn.gv.aF.eg.fJQtE.sA.jk.eY.aG.pW.aN.pbQtn.pW.dz.cp.dD.sA.bP.gt.aE.sA.bP.bk.fH.uT.#kQtu.mY.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq",
-".gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.vu.vu.vu.vv.v5.v6.v7.v8.v9.w..w#.wa.wb.wc.wd.we.wf.wg.wh.wi.wj.wk.wl.wm.wn.wo.wp.wq.wr.ws.wt.wu.wv.ww.v2.hV.hV.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.uTQti.ef.jk.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.mY.bP.ef.mY.gq.gq.mfQtVQtYQtV.jkQtW.aL.mf.dA.e0.mY.jk.aE.fEQt..mY.mZ.dE.pWQtg.bf.eg.jk.hV.mf.#P.e2.dE.aG.dC.vt.bj.#S.mf.gq.gq.gq.gq.jk.aG.hcQtVQtv.eg.jk.hV.jk.aE.gx.dE.nJ.bk.c2.hVQtW.fL.reQtu.ag.pW.mf.#P.#h.c2.pWQtF.e1.mY.hV.#O.pbQt#.re.#S.bi.pW.mfQtb.bf.aE.jm.os.mf.hV.pWQtTQtu.mY.gq.gq.gq.gq.gq.jk.cW.pb.e0.mY.gq.gq.gq.gq.mf.gx.gt.gq.jk.mY.bP.bc.fF.e2.dE.pb.aF.mY.ej.fF.dE.bd.ba.gq.dz.dA.gx.mf.jk.#P.bf.cl.re.cZ.bc.pWQtb.gt.aE.aE.bf.aJ.aN.ah.aJ.bc.jk.gq.gq.jkQtg.gu.dD.cZ.aM.e0.mfQtE.bf.aE.jk.pW.bd.cp.fF.e2Qth.bg.#T.dD.gt.aNQth.ah.fEQt..aLQtW.gq.hV.pW.jm.aG.re.bP.dC.re.gq.pW.cm.os.re.b#.hc.pW.pW.eXQtp.pW.mY.cm.#S.mY.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq.gq",
-".hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.wx.wx.wy.wz.wA.wB.wC.wD.wE.wF.wG.wH.wI.wJ.wK.wL.wM.wN.wO.wP.wQ.wR.wS.wT.wU.wV.wW.wX.wY.wZ.w0.w1.jk.jk.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.vtQtiQtU.mf.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.pW.jlQtU.pW.hV.hV.re.e0.bf.cW.hVQtW.fL.jk.gt.aL.mY.jk.cW.gtQt#.pW.bc.aM.re.#P.pb.fH.mf.mY.ah.gv.nJ.cZ.aG.eh.vt.fG.eY.mY.hV.hV.hV.hV.mf.jn.fG.sA.mf.jk.hV.jk.#O.#S.jl.jm.bL.fJ.eg.jkQtW.aL.sAQtSQtT.pW.mf.#P.#h.c2.re.eY.e1.pW.jk.aE.pbQtc.sA.#S.e1.pW.mYQtb.bf.cW.e1.#S.mY.hV.pW.nJQtS.pW.hV.hV.hV.hV.hV.hV.mY.mZ.eX.re.hV.hV.hV.hV.mY.#n.gv.gq.jk.mY.fN.#S.jm.bj.bj.cZ.dz.#P.gv.#U.dE.dC.gv.hV.gq.dA.aL.mY.mfQtV.bf.c2.mYQtn.gt.aF.aJ.eh.sAQtn.fJ.hc.fG.bj.e1.gx.jk.hV.hV.mfQtg.bk.eg.re.mY.mY.mYQtE.gt.cW.jk.pW.jl.aG.aM.bj.bj.dE.dz.eX.dA.jm.bj.bj.jmQtR.fF.gv.hV.jk.pW.e2.aG.sAQtp.ag.re.hV.pW.dE.cp.re.bP.eX.pW.pWQtT.b#.pW.pW.cm.eY.pW.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV.hV",
-".jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.w2.jk.jk.jk.w3.w4.w5.w6.w7.w8.w9.x..x#.xa.xb.xc.xd.xe.xf.xg.xh.xi.xj.xk.xl.xm.xn.xo.xp.xq.xr.xs.xt.jk.xu.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.v3Qto.bd.mY.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.re.jl.dC.vt.pW.re.cW.gv.ag.re.hVQtW.fL.sAQtF.eX.vt.uT.#T.eZQtc.re.bc.aM.sA.#P.bfQtR.pW.mY.osQth.v4.xv.dC.bd.v3.fG.eY.sA.jk.hV.jk.jk.mY.jn.e1.re.jk.jk.jk.pW.aL.dA.re.v3.eg.#h.eg.mfQtW.aL.uT.b#.#Q.re.mYQtV.#h.eg.re.eY.bj.uT.mf.aE.pbQtb.uT.#S.bj.re.pWQti.bf.aEQtE.bf.aE.sA.sAQtp.b#.pW.mf.pW.jk.hV.jk.hV.pW.aN.bk.c2.sA.mY.pW.pW.pW.#n.gv.hV.mf.re.aJQtu.v4.sA.uT.uT.re.#QQtS.v3.v4.aL.ba.jk.hV.dA.ck.sA.mYQtV.bf.eg.mf.re.bd.#U.jnQtc.pW.hV.ba.ah.xv.sA.uT.re.hV.jk.jk.mY.#PQtY.cl.mY.jk.jk.pW.cr.fE.dz.mf.pW.bL.#S.v4.uT.uT.uT.reQth.gv.sA.uT.uT.uT.pWQti.#hQtR.re.uT.eX.jn.uT.#Q.#S.sA.mY.uT.dC.gs.vt.b#.ag.re.re.eX.bP.re.re.e1QtF.pW.mf.pW.jk.hV.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk",
-".jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.xw.mf.w2.xx.xy.xz.xA.xB.xC.xD.xE.xF.xG.xH.xI.xJ.xK.xL.xM.xN.xO.xP.xQ.xR.xS.xT.xU.jk.mf.xV.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.v3QtiQtU.mY.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.re.bP.os.fL.e0.bi.gv.bP.gq.mY.hV.fE.aL.uTQtc.pb.nJ.cZQtu.guQtb.re.aG.aM.sAQtn.bf.aM.cl.pW.jl.jl.cl.e2.fJ.dC.v3.gx.bfQt..#O.mf.jk.jk.mY.c1.e1.re.jk.jk.jk.pWQto.bfQt.Qto.jl.gu.fH.mf.fE.aL.uTQtp.eX.re.mYQtVQtY.eg.uT.bd.eYQtg.dz.cW.fJQtc.uT.bc.e1.re.pWQtb.fJQtR.mfQtuQtF.ah.eX.bf.jl.uTQtc.gs.cW.mf.jk.jk.mf.mf.dC.bg.jmQt#.e0.cm.jk.#nQtW.hV.mf.mY.cl.bg.nJQtVQtE.e2.reQth.dA.#PQtE.os.dA.jk.mY.#S.bdQtV.jk.#T.bk.eg.mY.pWQti.fI.os.mf.mf.pW.aN.pb.#nQtV.ahQt..mY.jk.jk.mYQtg.gu.cl.mY.jk.jk.pWQtE.bf.dz.mf.mY.ejQtW.eX.#P.crQth.mf.cW.jn.dCQtgQtc.aM.gq.mf.eY.jl.#P.gx.ba.os.re.fH.gt.dD.#T.cZ.fEQtg.uTQtS.aJ.re.re.agQtu.re.re.bi.mZ.uTQtg.b#.#O.mf.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk.jk",
-".mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.xW.mf.mf.mf.xX.xY.xZ.x0.x1.x2.x3.x4.x5.x6.x7.x8.x9.y..y#.ya.yb.yc.yd.ye.yf.hV.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.v4QtbQtU.pW.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.pW.ah.b#.jl.jl.hcQtE.mY.mY.mf.jk.#Q.#P.mY.pW.#PQtTQtTQto.jnQtb.re.cmQt#.pW.mY.ck.bPQtT.mf.ej.bd.fN.nJ.gx.fF.re.hV.e1QtSQtb.pW.mf.mf.mY.biQtE.pW.mf.mf.mf.mf.mY.ah.bM.jl.aL.bjQtR.mf.#Q.#P.re.dE.#n.pW.mY.ej.aJ.#O.pW.ej.hc.bP.#T.hV.eX.cl.re.bjQtE.pW.mY.c2.ag.cW.pW.gq.cZ.#U.fF.bi.jl.uTQti.bd.dz.mY.mf.mf.jk.mf.mY.#n.b#.#SQtp.jm.mfQtg.nJ.jk.mf.jk.pW.#TQtU.bM.bP.bQ.mY.hV.bL.bM.dCQtE.fG.jk.mYQtV.bdQtUQtRQtn.ag.#O.mY.mf.jk.aJ.gx.pW.jk.mf.pW.gxQtu.fN.bdQtg.mY.mf.mf.mY.fH.#UQtR.mY.mf.mf.mY.#TQtT.hV.mf.jk.pW.c2.dC.bM.b#.aN.mf.pW.ej.hc.bMQtS.aM.hV.mYQtR.hcQtF.#U.cr.bj.mY.pW.aFQtU.bM.gsQtv.pW.pW.cZ.#n.pW.pW.#n.dE.pW.pW.ck.bj.re.#P.ef.aE.mY.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf",
-".mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.yg.mY.yg.yh.vv.yi.yj.yk.yl.ym.yn.yo.yp.yq.yr.ys.yt.yu.yv.yw.yx.mY.mY.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.v4QtbQtU.pW.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.pW.re.re.re.sA.re.mY.mf.mf.mf.re.mY.mf.mY.vt.v3.xv.#OQtW.fH.pW.pW.mY.mf.mf.re.re.pW.mf.mY.re.mY.re.pW.pW.mf.mY.re.pW.mY.mf.mf.mf.mf.re.pW.mf.mf.mf.mf.mf.mf.re.mY.pW.re.re.mY.mf.re.mY.mf.pW.pW.mf.mf.mY.re.mY.mf.mY.sA.pW.mY.mf.re.mY.mf.pW.pW.mf.mf.mY.re.mf.mf.re.v3.v3.yy.dC.#Q.sA.mY.re.mY.mf.mf.mf.mf.mf.mY.re.re.mY.re.re.mf.mY.re.mf.mf.mf.mf.pW.re.mY.pW.re.mf.mY.re.mY.re.re.pW.mf.mf.pW.sA.pW.mf.mY.re.mY.mf.mf.mf.re.pW.mf.mf.mf.mf.re.pW.mY.re.pW.mf.mf.mf.mf.mY.re.mY.mf.mf.mf.mf.mY.re.mf.mf.mf.mf.pW.re.mY.pW.re.mf.mf.mY.sA.mY.pW.re.mf.mf.mY.re.mY.re.pW.pW.mf.mf.pW.re.mY.re.pW.mf.mf.pW.pW.mf.mf.pW.pW.mf.mf.pW.pW.mf.mY.re.mY.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf.mf",
-".mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.yg.yz.yA.yB.yC.yD.yE.yF.yG.yH.yI.yJ.mf.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.xvQtbQtU.re.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.pW.dz.dE.e0.#n.mZ.eh.re.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.re.cr.cZ.fL.nJ.gt.cl.re.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY",
-".mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.yK.pW.yK.yK.yK.yK.yL.yK.yK.yK.pW.pW.pW.yL.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.v4QtbQtU.sA.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.pW.gq.nJQtSQtS.fG.hV.pW.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.sAQtE.jlQtu.eh.#P.sA.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY.mY",
-".pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.xvQtb.bd.uT.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.uT.sA.uT.uT.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.sA.uT.sA.uT.sA.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW",
-".pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.xvQtb.bd.uT.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW.pW",
-".re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.yyQtbQtU.vt.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re",
-".re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.vt.cWQtV.sA.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re.re",
-".sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.uT.vt.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA",
-".sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA.sA",
-".v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3.v3",
-".yM.yN.yO.yP.yP.yP.yP.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yR.yS.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yP.yU.yU.yV.yV.yU.yU.yV.yR.yW.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yQ.yW.yX.yU.yX.yX.yX.yY.yY.yY.yY.yY.yY.yY.yY.yY.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yZ.y0.jm.y1.y2.y3.y4.y5.y6.y7.jm.y8.y9.z..z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z#.z..z#.za.za.za.z..y9.zb.zc.zd.ze.zf.zg.zh.zd.zi.z..za.z#.z..z..zj.zk.jm.zl.y9.z#.z#.z#.z..zm.zn.jm.zo.yZ.yV.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yU.zp.yV.zq.zr.zs.zt.zu.zv.zv.zw.zt.e2.zx.zy.zz.zA.zB.zC.zD.zE.zF.zG.zH.zH.zH.zG.zI.zJ.zK.zL.zM.zN.zI.zO.zP.zQ.zR.yY.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.zS.yT.yT.yT.yT.yT.yT.yW.yU.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yX.yW.yR.yW.yR.yW.yR.yW.yR.yR.yS.zT.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.yP.zS.yT.zU.zV.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yS.zW.zW.yW.yW.yW.yY.zX.zY.zZ.zG.zO.z0.z1.z2.z3.z4.zS.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yP.zW.zW.yW.yW.yW.yW.yW.yW.yW.yW.yW.yW.zW.zW.zW.zS.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.yT.z5.z6.z7.z8.z9.A..A#",
-".Aa.Ab.Ac.Ad.Ae.Ae.Ae.Ae.Ae.Af.Af.Af.Af.Af.Af.Af.Ae.Ae.Ae.Ae.Ae.Ag.Ah.Ai.Ai.Ai.Ai.Ai.Ai.Ai.Ai.Ai.Ai.Ai.Aj.Ak.Al.Ak.Ak.Am.Al.An.Ao.Ap.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Ae.Aq.Ar.Am.Am.As.As.As.As.Ak.Ak.Ak.Ak.Ak.Ak.Ak.Ak.Ak.Ak.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.Au.Av.hd.Aw.Ax.Ay.Az.AA.AB.AC.AD.AE.AF.AG.AH.AH.AH.AH.AH.AH.AH.AH.AH.AH.AH.AH.AH.AH.AH.AH.AI.AH.AH.AF.AF.AG.AJ.AK.AL.AM.AN.AO.AP.AQ.AQ.AR.AR.AR.AQ.AO.AS.AT.AU.AF.AH.AV.AW.AX.AY.AV.AH.AH.AH.AI.AG.AZ.hd.A0.Ar.A1.At.At.At.A2.A2.A2.A2.A2.A2.A2.A2.A2.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.At.Al.A3.Ae.A4.A5.A6.A7.A8.A9.AI.AV.B..B#.A7.ek.Ba.Bb.Bc.Bd.Be.Bf.Bg.Bh.Bi.Bj.Bk.Bl.Bm.Bn.Bo.Bp.Bq.Br.Bs.Bt.Bu.Bv.Bw.Bx.By.Bz.BA.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.Ae.BC.BD.Ai.BE.BE.BE.BF.BG.An.Ak.An.An.An.An.An.An.An.An.An.An.An.An.An.An.BH.BI.Ar.Ar.BI.Ar.Ar.BJ.Ar.Ar.BK.BD.Ae.Ae.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BB.BC.BB.BL.BM.BN.BN.BN.BN.BN.BN.BO.BO.BO.BO.BO.BO.BO.BL.BP.An.BQ.Ar.Ar.BQ.BR.BS.BT.BU.BV.BW.BX.BY.BZ.B0.BL.BO.BO.BO.BO.BO.BO.BO.BO.BO.BO.BO.BO.BO.BO.BO.BL.BC.BQ.Ar.Ar.Ar.Ar.Ar.Ar.Ar.Ar.Ar.Ar.BQ.BQ.BQ.B1.BO.B2.B2.B2.B2.B2.B2.BO.BO.BO.BO.BO.BO.BO.BO.BO.BO.BF.BF.BF.BF.BF.BF.BF.BF.BF.B3.B4.B5.B6.B7.B8.B9.C.",
-".C#.Ca.Cb.Cc.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Ce.Cb.Cf.Cf.Cf.Cf.Cf.Cf.Cf.Cf.Cf.Cf.Cf.Cg.BA.Ch.Ci.Cj.Ck.Ck.Ab.Ce.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Cd.Ce.BA.BQ.Ck.BQ.Cl.Cl.Cl.Cl.Cl.Cl.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.BQ.Cm.Cn.gy.Co.Cp.Cq.Cq.Cr.Cs.gy.Ct.Cu.Cv.Cw.Cx.Cx.Cx.Cx.Cx.Cx.Cx.Cx.Cx.Cx.Cx.Cx.Cx.Cy.Cx.Cx.Cz.Cx.CA.CB.CC.CD.CE.CF.CF.CF.CG.CG.CG.CG.CG.CE.CE.CE.CG.CG.CG.CH.CI.CJ.Cx.Cy.CK.CL.Cx.Cy.Cx.Cx.Cx.Cx.CM.CN.CO.CP.CQ.CR.Ao.Ao.Ao.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.Ag.BJ.BJ.BJ.BJ.BJ.BJ.Ag.CS.CT.Cd.CU.CV.c0.CW.CX.Cx.CY.CK.Cy.Cx.Cw.CX.#j.CZ.C0.C1.C2.C3.C4.C5.C6.C7.C8.C9.D..D#.D#.D#.Da.Db.Dc.Dd.De.Df.Dg.Dh.Di.Dj.Dk.Dl.Dm.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Dn.Dn.Ah.Cf.Do.Do.Do.Ac.Cl.BA.BA.BA.BA.BA.BA.BA.BA.BA.BA.BA.BA.BA.BA.BA.Ab.Ad.Ad.Ad.Ad.Ad.BD.Dp.Dq.Ac.Cb.Dn.Dn.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Cb.Dn.Dn.Cg.Do.Do.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Cg.Ab.Ab.Dp.Dp.Ds.Dt.Du.Dv.Dw.Dx.Dy.Dz.DA.Aa.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.BF.Dp.BA.Dp.Dp.Dp.Dp.Dp.Dp.Dp.DB.Ab.Ab.BA.Ab.DC.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Dr.Do.Do.Do.Do.Do.Do.Do.Do.Do.Do.Do.Do.Do.Do.Do.Do.Do.Dr.DD.DE.DF.DG.DH.DI.DJ.DK",
-".DL.DM.DN.DN.DO.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.DP.DQ.DR.DR.DR.DR.DR.DR.DR.DR.DR.DR.DS.DT.DU.DV.DV.DV.DV.DW.DX.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.Aa.DY.DY.DY.DY.DY.DY.DY.DY.Aa.Aa.Aa.Aa.DZ.DP.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.Ca.D0.D1.D2.D3.D4.D5.D6.D7.D8.D9.E..E#.Ea.Eb.Ec.Ed.Ed.Ed.Ed.Ed.Ed.Ed.Ed.Ed.Ed.Ec.Ed.Ee.Ed.Ef.Eg.Eh.Ei.Ej.Ej.Ek.El.Em.Em.Em.Em.Em.Em.En.Eo.En.El.El.El.El.El.Em.Ej.El.Ep.Ed.Ec.Eq.Ed.Ee.Ee.Ee.Ed.Ec.Er.Es.Et.D2.Eu.Ev.Ca.Ca.Ca.Ca.Ca.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ew.Ca.Ex.Ey.Ez.EA.EB.EC.ED.EE.Eb.Ec.Ed.Ed.Ed.EE.EF.EG.EH.EI.EJ.EK.EL.EM.EN.EO.EPQtX.D2.EQ.Et.ER.E..Et.Et.ES.ET.EU.EV.EW.EX.EY.EZ.E0.E1.E2.E3.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.DO.DO.E5.E6.E7.E8.Ca.Ca.E9.E9.E9.E9.E9.E9.E9.E9.E9.E9.E9.E9.E9.E9.E9.F..DP.DP.DP.DP.DP.F#.Fa.F#.DN.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.E4.DX.Fb.DR.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.DR.DZ.E9.C#.C#.Fd.Fe.Ff.E0.Fg.Fh.Fi.Fj.Fk.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fc.Fl.Fc.Fb.C#.C#.F#.F#.F#.C#.Fd.Fm.Fm.Fn.Fd.E9.E9.Fo.Fp.Fc.Fc.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fp.Fl.Fq.Fr.Fs.Ft.Fu.Fv.Fw.Fx",
-".Fy.Fz.FA.FB.FB.FB.FB.FB.FB.FB.FB.FB.FB.FB.FB.FB.FB.FB.FB.FC.FD.Fk.FE.FE.FE.FE.FE.FE.FE.FE.FE.FF.FG.FH.FI.FI.FJ.DM.FD.FG.FB.FB.FB.FB.FK.FK.FK.FK.FK.FK.FK.FK.FK.FB.FB.FB.FB.FB.FB.FB.FB.FL.FL.FG.FM.FN.FN.FN.FN.FN.FN.FN.FN.FN.FN.FN.FN.FN.FN.FN.FN.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FO.FP.DV.FQ.FR.FS.FT.FU.FV.FW.FX.FY.FZ.FZ.F0.F1.F2.F3.F3.F3.F3.F3.F3.F3.F3.F4.F4.F5.F3.F6.F7.F8.F9.F9.G..G#.G#.G#.G#.G#.G#.Ga.G..G..G..G..G..Gb.G#.G#.G#.G#.G#.G..G#.Gc.Gd.F5.F3.F3.Ge.Gf.Gg.Gh.F4.F3.Gi.Gj.Gk.FR.Gl.Gm.DM.Gn.Gn.Gn.Gn.Gn.Gn.Gn.Gn.Gn.Go.Go.Go.Go.Go.Go.Go.Go.Go.Go.Gn.Gn.Gn.Gn.Gn.Gn.Gn.Gn.Gp.Gp.Gq.Gr.Gs.CN.Gt.Gu.Gv.Gi.F4.F3.F3.F3.F3.F3.Gw.Gx.Gy.Gz.GA.GB.GC.GD.GE.GF.GG.GH.FR.FZ.Gk.FZ.Gk.FZ.GI.GJ.GK.Gi.Gi.GL.GM.Gt.GN.GO.GP.GQ.GR.GS.GT.FL.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.FG.FL.GV.GW.FC.FI.GX.Gp.Gp.Gp.Gp.Gp.Gp.Gp.Gp.Gp.Gp.Gp.Gp.Gp.Gp.Gp.GY.GZ.GZ.FD.GZ.GZ.GZ.Fz.FB.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.GU.FL.FL.G0.G1.G2.G2.G2.G2.G2.G2.G2.G2.G2.G2.G2.G2.G2.G2.G3.G4.G4.G5.G6.G7.G8.G9.H..H#.Ha.Hb.Hc.Hc.Hc.G2.G2.G2.G2.G2.G2.G2.G2.Hd.Hd.Hd.Hd.Hd.Hd.Hd.G1.FG.He.G5.G4.Hf.Hg.He.Hh.Hi.Hj.Hk.Hl.Hm.Hn.Ho.Hp.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hq.Hb.Hr.Hs.Ht.Hu.Hv.Hw.Hx.Hy",
-".GT.Hz.HA.HA.HA.HA.HA.HA.HA.HA.HA.HA.HA.HA.HA.HA.HA.HA.HA.HB.HB.HC.HD.HD.HD.HD.HD.HD.HD.HD.HD.HE.HF.HF.HF.HF.GT.Hz.HA.HG.HG.HG.HG.HG.HG.HA.HA.HA.HA.HA.HA.HA.HA.HH.HH.HH.HH.HI.HJ.HK.HH.HJ.HL.HM.Gp.Gp.Gp.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Gp.Gp.Gp.Gp.Gp.Gp.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.Fo.HN.HO.HP.HQ.HR.HS.HT.HU.HV.HW.HX.HY.HZ.H0.H1.H2.H3.H4.H4.H4.H4.H4.H3.H5.H3.H6.H7.H8.H9.I..H9.I#.I#.I#.I#.I#.I#.H9.H9.H9.H9.H9.I#.I#.I#.I#.Ia.Ib.Ib.Ib.Ib.Ia.I#.I#.Ic.Id.H4.H3.Ie.If.Ig.Ih.Ii.Ij.Ik.Il.Im.HR.HQ.In.FI.Io.Ip.Ip.Iq.Iq.Iq.Iq.Iq.Iq.Iq.Iq.Iq.Iq.Ip.Ip.Ip.Ip.Ip.Ip.Ip.Ip.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Is.It.Iu.Iv.Iw.Ix.Iy.Ik.H5.H4.H4.H4.H4.H4.H4.Iz.IA.IB.IC.ID.IE.IF.IG.IH.II.HQ.HQ.IJ.HZ.HY.HY.HZ.IK.HY.IL.IM.Iz.H3.H4.H3.IN.IO.IP.IQ.IR.IS.IT.IU.IV.IW.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HH.HJ.GV.IX.Ir.GT.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Ir.Ir.IY.IZ.IZ.Hz.IZ.FA.I0.I1.I2.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HE.HH.HJ.GV.I3.I4.I4.I4.I4.I4.I4.I4.I4.I4.I4.I4.I4.I4.I4.Hb.I0.Ir.I5.I6.I7.I8.I9.J..J#.Ja.Jb.Jb.Jb.Jb.Jb.Jb.Jc.Jc.Jc.Jc.Jc.Jc.I4.I4.I4.I4.I4.I4.Jd.Je.Jf.Jg.Jh.Ji.Jh.Jj.Jk.Jl.Jm.Jn.Jm.Jo.Jp.Jq.Jr.Js.Jt.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Ju.Jv.HD.HD.Jv.Ju.Ju.Ju.Ju.Ju.Ju.J#.HD.Jw.Jp.Jx.Jy.Jy.Jz.JA",
-".Ir.I2.JB.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.HJ.JD.JE.JF.JF.JF.JF.JF.JF.JF.JF.JG.JH.JI.JJ.JJ.JH.HI.JC.JC.JC.JC.JC.JC.JC.JC.JC.JK.JK.JK.JK.JK.JK.JK.JK.Hp.Hp.Hp.Hp.JK.JK.HI.JL.IY.IY.JL.JL.JL.JL.JL.JL.JL.JL.JL.IY.IY.IY.IY.JL.IY.JL.IY.IY.IY.IY.IY.Ir.Ir.Ir.Ir.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.IY.He.JM.JN.JO.JP.JQ.JR.JS.JT.JU.JV.JW.JW.JX.JY.JZ.J0.J1.J1.J1.J1.J0.J0.J1.J2.J3.J4.J5.J6.J4.J4.J4.J4.J4.J6.J6.J6.J6.J4.J4.J7.J8.J9.J9.K..K#.K#.K#.K#.K..J9.J9.Ka.J8.J4.Kb.Kc.J1.J1.Kd.Ke.J4.J6.Kf.Kg.J0.Kh.Ki.JO.Kj.Kk.Ip.Kl.Kl.Ji.Ji.Ji.Ji.Ji.Ji.Ji.Ji.Ji.Ji.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Kn.HH.AG.Ko.Kp.HQ.Kq.Kr.Ks.J0.J1.J1.J1.J1.J1.Kt.Ku.Kv.Kw.Kx.Ky.Kz.KA.KB.KC.KD.KE.KF.JW.JW.KG.JW.KH.KI.KJ.KK.Ks.J1.J1.J1.J0.KK.KL.KM.KN.KO.KP.KQ.KR.KS.KT.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.JK.KU.Je.Km.JH.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.Km.KV.KW.KX.HJ.HA.KX.I2.HK.JK.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KY.KY.KY.KY.KY.KY.KY.KY.JK.KU.KZ.K0.K0.K0.K0.K0.K0.K0.K0.K0.K0.K0.K0.K0.K0.KZ.HK.K1.K2.K3.K4.K5.K6.K7.K0.K8.K9.K9.K9.L..L..K9.K9.K9.L..K9.K9.K8.K8.K8.K8.K8.L#.La.JF.Lb.Lc.Ld.Le.Lf.Lg.Lh.Li.Lh.Lj.Lk.Lj.Lj.Lj.Lk.Ll.Lm.L#.L#.Ln.Ln.Ln.Ln.Ln.Ln.Ln.Ln.Ln.Ln.L#.Lo.JG.JK.HK.IW.HI.HK.JK.Lp.L#.Ln.L#.Lq.Lr.Ls.Lt.Lj.Lk.Lj.Lt.Lt.Lh",
-".Lu.HI.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.JC.HH.JG.Lw.Lw.Lw.Lw.Lw.Lw.Lw.Lw.La.Lx.Ly.IY.Ly.JL.HE.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.KU.JK.JK.JK.JK.KU.KU.Lz.Lv.HJ.Lx.HJ.HJ.HJ.HJ.HJ.HJ.HJ.KX.KX.KX.KX.KX.HJ.HJ.HJ.Lx.Lx.Lx.K1.JL.JL.JL.JL.JL.JL.JL.IY.IY.IY.IY.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.JL.HN.LA.LB.LC.LD.LE.LF.LG.LH.LI.LJ.LK.LK.LL.LM.LN.LN.LO.LO.LO.LP.LO.LQ.LR.LS.J8.K#.LT.LT.LT.LT.K#.K#.K#.K#.LT.LU.LV.LW.LX.LY.LZ.L0.L1.LS.LS.LS.LS.L2.L3.L1.L4.LZ.LY.LX.LT.L5.LQ.LP.L6.J9.K#.LT.K#.LU.L7.L8.L9.LC.M..Ir.JH.I0.I0.I0.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.Km.M#.Ma.Mb.Mc.Md.Me.Mf.Mg.Mh.LP.LO.LO.LO.LO.LO.Mi.Mj.Mk.Ml.Mm.Ml.Mn.Mm.Mo.Mp.Mq.Mi.LP.Mr.Ms.LC.Mt.Mu.Mv.Mw.Mh.LP.LO.LO.LO.LO.LO.Mx.My.Mz.MA.MB.MC.MD.ME.MF.MG.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.J#.JK.K1.JH.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.IW.MI.HH.MI.MJ.KW.HH.MK.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.ML.ML.ML.ML.ML.ML.ML.ML.MH.MH.MH.MH.MH.MH.MH.MH.MM.MH.MN.MO.MO.MO.MO.MO.MO.MO.MO.MO.MO.MO.MO.MO.MP.MQ.MK.IW.MR.MS.MT.MU.MV.MP.MW.MX.MW.MW.MW.MY.MW.MX.MX.MX.MX.MX.MZ.MY.MX.MX.MX.MO.M0.M1.M2.M3.M4.M5.M6.M7.M8.M9.M9.N..M9.M9.M9.M9.M9.N#.M5.Na.Nb.Nc.Nd.Nd.Nd.Nd.Nd.Nd.Nd.Nd.Ne.La.KU.HK.IW.IW.IW.IW.IW.IW.IW.IW.MM.Lw.Nf.Ng.Nh.Ni.M9.N#.M9.M9.N#.Nj.Nk",
-".Lu.HK.MM.MM.MM.MM.MM.MM.MM.MM.MM.MM.Nl.Nl.Nl.Nl.Nl.Nl.Nm.MK.MN.Ne.Ne.Ne.Ne.Ne.Ne.Ne.Lw.Nn.JH.Km.Ji.K1.JK.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.Nn.Nn.Nn.Nn.J#.J#.J#.Jt.Jt.MK.HK.HK.HK.HK.No.HK.HH.Je.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.JC.HH.HH.HK.HJ.IW.IW.IW.K1.K1.K1.K1.K1.K1.Km.Km.Km.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.Hf.Np.Nq.Nr.Ns.Nt.Nu.Nv.Nw.Nx.Ny.Nz.Nz.NA.NB.NC.ND.NE.NE.ND.NE.ND.NF.NG.LT.LW.LW.LW.LW.LV.LV.LV.LV.LW.NH.LY.NI.L2.NJ.NK.NL.NM.NN.NO.NP.NQ.NQ.NQ.NR.NO.NN.NM.NL.NK.NS.L3.NT.NU.NV.NJ.NW.LV.LV.LW.LT.L2.NX.NY.NZ.N0.N1.N2.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N2.N4.N5.N6.N7.N8.N8.N9.Mv.O..O#.NE.NE.NE.NE.Oa.NC.Ob.Oc.Od.Oe.Of.Og.Oh.Oi.Oj.Ok.ND.NE.ND.Ol.Om.On.Oo.Op.Oq.Oq.NE.NE.NE.NE.NE.NE.NE.NV.Op.Or.Os.Ot.Ou.Ov.Ow.Ox.Oy.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.J#.No.N2.N2.N3.N3.N3.N3.Oz.Oz.N3.N3.N3.N3.N3.N3.N3.N3.OA.Je.Je.OB.JB.OC.No.OD.Jt.Jt.OE.OE.OE.OE.OE.OE.OE.OE.OE.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OF.OF.OF.OF.OF.OF.J#.J#.Lo.MX.MX.MX.MX.MX.MW.MW.MW.MW.MW.MW.MW.MW.MX.Nd.J#.Oz.OG.OH.OI.OJ.OK.MZ.MZ.MZ.OL.OL.OL.MZ.MZ.OM.MZ.MZ.MZ.MZ.MZ.OM.MZ.MZ.ON.OO.OP.Nh.OQ.M7.M5.OR.M5.M5.M5.M5.M5.M5.M5.M5.M5.M5.OR.M5.OS.OT.OU.Nd.OV.OV.OV.OV.OV.OV.Nd.La.Nn.Oz.Oz.Oz.No.No.No.No.No.No.No.Oz.N3.OW.OX.OY.OQ.Nk.Nk.Nk.OZ.O0.O1.M7.O2",
-".Lu.HK.Nl.Nl.Nl.Nl.Nl.Nl.Nl.Nl.Nl.Nl.Nl.MM.MM.MM.MM.MM.MK.J#.Lo.Ne.Ne.Ne.Ne.Ne.Ne.Ne.O3.Km.Km.IY.K1.Nm.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.Nn.Nn.Nn.O4.J#.J#.Jt.Jt.O5.OD.Je.KW.HK.HH.HH.JC.JC.MK.Lv.Lv.KU.MM.KU.MM.Lv.Lv.Lv.Lv.Lv.Lv.Lv.Lv.MK.JC.JC.Je.HH.HK.HJ.IW.IW.K1.K1.K1.K1.K1.Km.Km.Km.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.K1.M#.O6.NA.O7.NC.NE.O8.Oa.O9.P..NZ.P#.Pa.Pb.Pc.Nt.NU.NU.NE.Pd.Pe.Pf.Pg.Ph.Pi.Pi.Pi.Pj.Ph.Ph.Ph.Pi.Pk.Pl.LS.Pm.NL.Pn.NQ.Po.Pp.Pq.Pf.Pr.Ps.Ps.Ps.Ps.Pr.Pt.Pu.Pp.Po.NQ.Pv.NO.Pw.Pd.Px.Py.Pi.Ph.Ph.Pj.Pz.Pu.PA.P#.PB.PC.PD.N2.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.PE.PF.PG.PH.PI.PJ.N8.PK.PL.ND.NU.NU.NU.NU.NE.Nt.PM.PN.PO.PP.PQ.PR.PS.Oq.NE.NU.NU.NU.NU.NU.NU.NE.Oq.Ol.O#.NE.NU.NU.NU.NU.NU.NU.NU.NU.ND.PT.PU.PV.PW.PX.PY.Ow.PZ.P0.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Nn.P1.Lu.N3.N3.N3.Oz.No.No.N3.N3.N3.N3.N3.N3.N3.N3.Ha.Je.OB.JB.No.Oz.P2.P3.OE.OE.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.P4.P5.P5.MN.MW.MX.MX.MW.MW.MW.MX.MX.MX.MX.MX.MX.MX.MX.OV.Jt.N3.Oz.P6.P7.P8.OV.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.OM.MZ.MZ.MZ.MZ.OO.P9.Q..Q#.OQ.M4.M5.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.Qa.Qb.Qc.Ne.OV.OV.OV.OV.OV.Nd.La.P2.Oz.Oz.Oz.No.No.No.No.No.No.No.HK.HK.No.N3.Qd.Qe.Qf.M5.M4.Qg.Qa.Nk.Qh.Qi.Qj.Qk",
-".MR.HK.MM.Nl.MM.Nl.MM.MM.MM.MM.MM.MM.MM.MM.J#.J#.J#.J#.Nn.J#.Lw.Ne.Ne.Ne.Ne.Ne.Ne.Lw.HK.Km.Ji.N2.Nm.J#.J#.J#.J#.O4.O4.O4.O4.O4.O4.O4.Nn.Nn.Nn.KU.J#.Jt.JG.JG.Ql.JG.MM.HH.HH.HH.JC.Lv.MM.MM.MH.OD.OD.OD.OD.O5.O5.O5.O5.Qm.Qn.Qo.Qo.Qn.Qn.Qm.O5.O5.O5.OD.Qp.Lv.MK.JC.JC.Je.HK.HJ.IW.IW.K1.K1.Km.Km.Km.Km.Km.Km.Km.Km.Km.K1.K1.K1.K1.K1.K1.PD.Qq.PB.Qr.NC.NU.NU.Qs.Qt.Qu.Qv.P#.Qw.Qx.Qy.Oq.NU.NE.Pd.Qz.QA.Pz.Ph.Pi.Pi.Pi.Ph.Ph.Ph.Pi.Pk.Pl.LS.NK.NM.NP.Po.Pp.Pf.Ps.QB.QC.QD.QD.QD.QD.QD.QD.QC.QC.QB.QE.Pr.Pu.Po.QD.ND.Pd.NT.Pk.Pk.Pi.Ph.Ph.QF.QG.QH.P#.QI.QJ.M#.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.QK.QL.QM.N7.PJ.Md.QN.QO.Ok.Oa.NU.NU.NU.NU.NE.Ok.QP.QQ.On.QR.QS.O8.Ok.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE.QT.QU.QV.QW.QX.QY.QZ.Q0.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Q1.N3.N2.N3.N3.N3.Oz.Oz.Oz.N3.N3.N3.N3.N3.N3.N3.N3.Ha.Je.JB.OB.N3.N3.Q1.Jt.Jt.Jt.Jt.Jt.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.P4.P4.P4.P4.P4.P4.P4.P4.Q2.P3.MN.MZ.MZ.MZ.MX.MX.MX.MX.OV.OV.OV.OV.OV.OV.OV.OV.OF.Q3.Q4.Q5.Q6.MZ.OM.OM.OM.MZ.MZ.MZ.MZ.OM.OM.MZ.MY.MY.MY.MZ.MZ.OV.Q7.Q8.Q9.Nk.R..M5.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.R#.Ra.Rb.Rc.OV.OV.OV.OV.Nd.Rd.Q1.Oz.Oz.Oz.No.No.HK.HK.HK.HK.HK.HK.HK.HK.No.No.Q3.Re.Rf.Rg.Rh.R#.O2.Ri.Rj.Rk.Rl.Rm.OO",
-".Lu.HI.MM.MM.MM.MM.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.Nn.Jt.Ne.Ne.Ne.Ne.Ne.Ne.Lw.MK.K1.JL.Km.Je.Q1.O4.O4.O4.O4.O4.J#.J#.J#.J#.KU.KU.KU.KU.MH.JG.JG.JG.JG.JG.Rn.Lv.Hp.JC.Lv.Qp.OD.Ro.Ro.O5.O5.Qn.Qo.Ja.I4.Jc.Jb.Rp.Rq.Rr.Rs.Rs.Rs.Rs.Rq.Rq.Rq.Rp.Jb.Jb.Jc.Ja.Qo.O5.OD.MM.Qp.Lv.MK.JC.HH.HK.HK.IW.K1.K1.K1.K1.K1.K1.Km.Km.Km.Km.KV.KV.KV.Rt.Ru.PB.Qr.Ol.NU.NU.Rv.Rw.Rx.Ry.P#.P#.Rz.RA.ND.NE.Pd.RB.L1.RC.Pi.Pi.Pi.Pi.Ph.Ph.LW.Pi.LZ.Py.Pm.NM.NQ.Pp.Pf.Ps.QB.QD.QD.QD.QD.RD.RD.RE.RD.RE.RD.RF.QD.QD.QD.QB.QB.Pu.RG.NU.Pd.L7.Pl.L4.NH.Pj.LV.RH.RI.NA.Qv.RJ.RK.RL.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N2.M#.RM.RN.Md.RO.Ob.ND.O#.NE.NU.NU.NU.NU.NE.O#.RP.RQ.RR.PS.ND.O#.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Ol.RS.PB.RT.RU.RV.RW.RX.K8.RY.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.P3.HK.Lu.N2.N3.N3.Oz.Oz.N3.N3.N3.N3.N3.N3.N3.N3.RZ.OB.JC.OB.P2.N3.N3.P3.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.P4.P4.P4.P4.P4.P4.P4.P4.P4.OF.OF.OF.OF.OF.OF.OF.OF.R0.R1.OV.OL.MZ.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OF.N3.Hq.L#.P9.MZ.MY.MY.OM.MZ.MY.MZ.OM.OM.MZ.MZ.OL.MZ.MZ.OL.MX.R2.R3.R4.Qg.M7.M5.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.Rg.R5.R3.OU.OV.OV.OV.OV.Lw.P3.Oz.Oz.Oz.HK.HK.HK.HK.HK.No.No.No.No.No.No.No.No.R6.R7.R8.R9.N#.R4.S..S#.OU.M0.ON.OV.OV.Sa",
-".Km.Sb.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.Nn.Ql.Ln.Ln.Ln.Ln.Ln.Nd.O3.K1.JL.K1.HK.KU.J#.J#.J#.J#.J#.J#.J#.MH.MH.KU.KU.KU.MH.JG.JG.JG.JG.JG.Lp.Qm.Lv.Lv.Qp.Ro.Qn.Qn.Qo.Sc.Sc.Jb.Rp.Rq.Rs.Sd.Se.Sf.Sg.Sh.Sh.Si.Si.Si.Si.Si.Si.Sj.Sj.Si.Sh.Sf.Sk.Se.Se.Sl.Rr.Rp.I4.Qo.Qn.Ro.MM.MK.JC.HH.HK.IW.P1.K1.K1.K1.K1.K1.K1.Sm.Sm.Sm.Sm.Sn.So.PB.Sp.Ol.NU.NU.Sq.Sr.Ss.St.Nz.PB.Su.ND.NE.NE.Sv.NS.RC.Pi.Pi.Pi.Pi.Ph.Ph.Pi.Pk.Pl.NS.Sw.NP.Sx.Pt.QB.QC.QD.QD.QD.RD.Sy.RD.RD.RD.RD.RD.RD.Sy.QD.QD.QD.RD.QD.QD.Pr.Sz.NU.NE.RF.NS.L1.Pk.Pi.Ph.SA.SB.PB.SC.SD.SE.FH.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.SF.SG.SH.SI.Ns.O#.ND.NE.NU.NU.NU.NU.NU.NE.ND.SJ.Oh.NE.Oq.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NC.SK.PB.SL.SM.SN.SO.SP.MZ.Nm.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OE.OE.OE.OE.OE.Je.N2.Lu.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.Sb.MK.JK.Nm.No.N2.Oz.P3.OF.OF.OF.P4.P4.P4.P4.P4.P4.P4.P4.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.K7.K7.K7.K7.K7.K7.K7.R0.O3.MZ.OM.MZ.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.P3.Oz.No.OO.MZ.MZ.MZ.MZ.MZ.MZ.MZ.OL.MZ.MZ.MZ.OL.P9.P9.SQ.SR.Nd.SS.ST.Rh.M4.M5.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.SU.M5.R#.SV.SW.OU.Qc.OV.OV.Nd.O3.No.SX.Oz.No.No.No.No.No.No.No.No.SY.SY.SY.SY.SY.SY.SY.SZ.S0.R..S1.S2.Nd.OU.OO.S3.Sa.Sa.Sa.Sa.OV",
-".Km.HK.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.J#.O4.O4.O4.O4.O4.RY.S4.Ne.Ln.Ln.Ne.Ne.Lw.HK.Km.K1.IW.KU.MH.MH.MH.MH.MH.MH.MH.MH.MH.KU.KU.MH.MH.JG.JG.JG.Rn.HD.Jd.Qo.Qp.OD.Qn.Qo.Sc.I4.Jb.S5.Rs.Sl.Sg.Sh.Sj.S6.S7.S8.S8.S8.S9.T..S9.T..T..T..T#.Ta.Tb.T#.Tb.T#.Ta.T..S8.S7.Tc.S6.Td.Sh.Sk.Rs.Rq.Jb.I3.Qn.OD.MM.Lv.Je.HK.KW.OA.OA.OA.OA.OA.Te.Tf.Tf.Tg.Th.PB.O7.Ol.NU.NU.O8.Ti.Tj.Pb.P#.Tk.Tl.O#.NE.NU.Tm.Pz.Pi.Pi.Pi.Pi.Ph.Ph.Pi.Pk.Py.Pm.Pv.Po.Pf.QE.Tn.QD.QD.QD.RD.RD.RD.To.Tp.Tp.Tp.Tp.Tp.To.RD.RD.RD.Sy.QD.QD.QD.QB.Tq.Pd.Mi.Po.NL.Tr.Pl.NH.Pz.Ts.Tt.Tu.Tv.Tw.Tx.Hf.N2.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.JH.Ty.Tz.TA.TB.NC.NE.NU.NU.NU.NU.NU.NU.NU.NE.TC.TD.Oq.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Ol.TE.PB.TF.TG.TH.TI.TJ.TK.Nm.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OE.OE.OE.OE.OE.OE.OE.OE.OE.OE.Jt.Jt.Jt.Jt.P2.TL.TM.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.RZ.Nm.JK.Sb.RZ.TM.Sb.P3.P4.P4.P4.P4.OF.OF.OF.OF.OF.OF.OF.OF.OF.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.R0.J#.TN.OL.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.Nd.J#.Oz.P3.MZ.OL.MZ.MZ.MZ.MZ.P9.P9.P9.P9.P9.TO.P9.ON.TP.TQ.TR.TS.OQ.R..M5.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.M4.Qf.TT.TU.TV.OV.OV.OV.Rd.Q1.Oz.R6.No.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.SX.TW.TX.TY.TZ.Nd.OV.OV.OV.OV.OV.OV.OV.OV",
-".Km.IW.J#.J#.J#.O4.O4.O4.O4.O4.O4.O4.O4.J#.J#.J#.J#.KU.Nn.T0.Ne.Ne.Ne.T1.T1.J#.K1.Km.IW.MK.MH.MH.MH.MH.MH.MH.MH.MH.KU.KU.KU.MH.MH.JG.JG.Qm.Lp.Jv.Jb.Ja.O5.Qn.Sc.I4.Jb.Rq.Sl.Sk.Sh.T2.Tc.S8.T..T3.T4.T5.T6.T7.T8.T9.U..U#.Ua.Ub.Ub.Ua.Ub.Uc.Ud.Ue.Ua.Ua.Uf.U..T8.Ug.Uh.T3.T..S8.S7.Td.Sh.Ui.Uj.Uk.Jc.Ul.Um.Un.JB.MI.MI.MI.Ha.Ha.Ha.Ha.Ha.PE.Uo.PB.O7.Ol.NU.NU.NU.Ol.Up.P#.Pb.Uq.Ur.NU.NU.Us.Pi.Ph.Pi.Pi.Pi.Ph.Ph.Pi.Pk.L2.L6.NO.Pp.Pr.QB.QD.QD.QD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.To.RD.Sy.Tn.Ut.Oq.Pd.Uu.Uv.Pn.Pm.L1.Pk.Uw.Ux.NZ.SC.Uy.Uz.UA.SF.Lu.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N2.UB.UC.UD.UE.Oq.NE.NU.NU.NU.NU.NU.NU.NU.NE.NE.NE.ND.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oq.UF.Qv.UG.UH.UI.UJ.UK.UL.Nm.UM.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OE.OE.OE.OE.OE.OE.OE.OE.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OF.OF.OF.OF.Q1.N3.N2.N3.N3.N3.N3.N3.N3.N3.P1.P1.P1.P1.N3.Sb.Nm.JK.Sb.N3.N2.UN.Jt.OF.OF.OF.OF.OF.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.UO.R0.Q1.MX.MZ.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.ON.RY.N3.MN.TK.P9.MZ.MZ.MZ.OL.MZ.MZ.OL.MZ.MZ.P9.UP.UQ.UR.US.UT.R#.M4.M5.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.M7.Rg.UU.UV.R2.P9.OV.Nd.O3.R6.R6.R6.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.UW.UX.UY.R6.MQ.OV.OV.OV.MZ.MZ.MZ.MZ.MZ.MZ",
-".Km.IW.J#.O4.O4.O4.J#.J#.J#.J#.J#.J#.J#.J#.J#.MH.MH.KU.JD.UZ.T1.T1.T1.T1.MN.IW.Km.K1.MK.MH.ML.ML.ML.ML.ML.ML.ML.KU.KU.MM.MH.JG.O5.Qn.Qo.Ja.U0.U1.U0.Qn.Sc.Jc.Rq.Rs.Se.Sh.Td.S7.S9.U2.T5.U3.Uf.Ua.Ud.U4.U5.U6.U7.U8.U9.V..V#.Va.Vb.Vc.Ma.Vc.Vb.Va.V#.V..Vd.U8.Ve.Vf.Vg.Vh.Ub.U..Vi.Vj.Vk.Vl.Vm.Td.Vn.Vo.Rp.Jc.Vp.Vq.Un.Nl.Vr.JB.OB.OB.Je.Vs.Vt.PB.Vu.Vv.NU.NU.NU.O#.Vw.QH.Vx.Vy.Ol.NE.Pd.Vz.Pz.Pi.Pi.Pi.Ph.Ph.Pi.Pk.L2.NL.NQ.Pu.QB.QD.QD.Sy.RD.To.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.To.Tn.VA.NU.VB.VC.Pq.NQ.NL.L2.Pi.L4.VD.PB.VE.VF.VG.VH.He.N2.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.SF.VI.VJ.VK.VL.Oq.NU.NU.NU.NU.NU.NU.NU.NE.ND.ND.ND.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.Mj.VM.VN.VO.VP.VQ.VR.VS.R1.VT.Nn.Jt.OE.OE.OE.OE.OE.OE.OE.OE.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.VU.P1.TM.P1.P1.P1.P1.P1.P1.P1.N3.N3.N3.N3.OA.OB.JK.HK.Oz.N3.TL.Oz.VV.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.UO.UO.UO.UO.UO.UO.K7.O4.P2.VW.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.Ne.No.No.Nd.OL.VX.OM.OV.MZ.OL.OL.MZ.OM.OL.OL.ON.Q8.VY.VZ.V0.V1.M4.V2.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.R..Qg.V3.ON.OO.TO.TO.Nd.O4.Oz.R6.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.SX.SX.SY.MO.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ",
-".Km.I2.MM.J#.J#.J#.J#.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.JK.KU.V4.V5.V5.V5.V6.Je.Km.K1.No.MH.ML.MH.MH.MH.MH.MH.MH.MM.MM.MM.OD.O5.Qn.Qn.Ja.U0.K0.Vo.Rp.I3.I4.Rq.Sl.Sf.T2.S7.T..V7.Ug.T9.Ub.U5.V8.U7.V9.V..Va.W..W#.Wa.Wb.Wc.Wd.We.Wf.Wg.Wh.Wi.Wi.Wj.Wg.O6.Wk.Wl.Wm.Wn.Wo.Wp.Wq.Wr.Ws.Wt.Vg.Ub.Wu.Wv.Ww.Wx.Wy.Wz.Vn.Sd.U1.Jc.Um.P4.WA.MM.MK.MK.QK.WB.PB.WC.Ol.NU.NU.NU.Oa.Oi.WD.Vx.WE.Oi.NE.NE.WF.RC.Pi.Pi.Pj.RC.Ph.Pk.Py.NL.NQ.Pf.QB.QD.QD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.RD.WG.Pd.WG.Tn.Pr.Po.NM.Tr.WH.WI.NZ.WJ.WK.WL.WM.WN.Fy.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.Lu.SX.WO.WP.WQ.Oq.NE.NU.NU.NU.NU.NU.NU.NU.NE.NE.NE.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oa.WR.WS.WT.WU.WV.WW.WX.Nm.WY.WZ.RZ.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.P4.P4.P4.P4.P4.P4.P4.Q1.N3.N2.N3.N3.N3.N3.N3.N3.N3.N3.N3.W0.W0.OA.JC.Je.IW.W0.W0.W1.N3.O4.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.UO.UO.UO.UO.UO.UO.UO.UO.UO.K7.K7.K7.K7.K7.K7.Nn.R6.Ne.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.Rd.Oz.OF.P9.OL.P9.P9.P9.OV.MZ.MZ.MZ.OL.MZ.VX.SQ.W2.W3.W4.V1.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.M4.Rg.Nk.W5.W6.ON.P9.P9.Lw.Q1.SX.R6.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.R6.SY.SY.SY.SY.SY.SY.W7.SX.P3.MX.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ",
-".Km.K1.KU.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.ML.ML.ML.ML.KY.J#.W8.T1.T1.S3.W9.K1.K1.HK.MM.MH.MH.MH.OD.OD.OD.OD.MM.MM.MM.OD.Qn.Qn.I3.Ja.U0.U1.Vo.K0.Jc.Rr.Sk.Si.S6.S8.T4.T7.Uf.Ud.U5.U7.U9.V#.X..X#.Xa.Xb.Xc.Xd.Xe.Xf.AM.Xg.Xh.Xi.Xj.Xk.Xk.Xk.Xk.Xl.Xm.Xi.Xi.Xh.Xg.Xg.AM.Xn.Wh.Wk.Wm.Xo.Xp.Xq.Xr.Vf.Xs.Xt.Xu.Uh.Xv.Xw.Wz.Xx.Xy.U1.U0.Xz.XA.P4.Qd.XB.PB.Vy.XC.NU.NU.NU.NE.Ol.NX.XD.XE.XF.ND.O#.Pm.RC.Pi.Pi.Ph.Ph.Pi.L1.NK.NQ.Pf.QB.QD.Sy.RD.To.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QD.XG.Qs.NU.XH.QB.QB.Pp.Pn.NJ.XI.RO.XJ.XK.XL.XM.XN.XO.FJ.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.He.XP.XQ.XR.XS.Ol.NU.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE.XT.NZ.XU.XV.XW.XX.XY.XZ.X0.X1.Q1.Jt.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.P4.P4.P4.P4.P4.P4.P4.P4.OF.OF.OF.OF.OF.OF.Q1.N3.N2.N3.W0.W0.W0.W0.W0.W0.W0.W0.W0.W0.Ha.JK.HK.OA.W0.W0.TL.TL.P2.Jt.K7.K7.K7.UO.UO.UO.UO.UO.UO.UO.UO.UO.K7.K7.K7.K7.K7.K7.K7.K7.K7.O3.O3.O3.O3.O3.JG.UM.SX.OO.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.R1.OC.Lw.X2.P9.P9.MZ.P9.OV.OL.OL.OL.MZ.MZ.X3.X4.X5.X6.X7.M4.V2.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.X8.Rg.Ra.X9.Rm.OV.P9.OV.MN.SY.R6.R6.SY.W7.W7.W7.R6.Y..Y..Y..Y..Y..R6.W7.W7.Y#.Ya.Ya.Y#.R6.SY.SY.SY.SY.SY.R6.Yb.Xz.MZ.MZ.MZ.MZ.MZ.Yc.Yc.Yc.Yc.Yc",
-".Km.KV.KY.ML.MH.ML.ML.ML.ML.ML.ML.ML.ML.ML.MH.MH.MH.KU.Qp.Lw.T1.ON.Rd.Sb.K1.IW.P2.OD.OD.OD.OD.OD.OD.OD.MM.MM.OD.Ro.Qn.I3.Ja.U0.K0.Vo.Sk.VW.Rs.Sk.Sj.Tc.T..Uh.T8.Ua.U5.Yd.Vd.Vb.Xp.Xa.Wl.Xd.Ye.Yf.Xg.Xi.Xk.Yg.Yh.Yi.Yj.AU.Yk.Yl.Yk.Yk.AU.AU.Ym.Ym.Yj.Yn.Yo.Yp.Yq.Yr.Ys.Yt.Xi.Xg.Yu.Xe.Yv.Yw.Xo.Yx.Yy.Yd.Yz.Ue.T8.YA.S8.YB.YC.Ui.K8.K0.U0.No.YD.PB.YE.YF.NU.NU.NU.NE.Ol.YG.XD.NA.YH.Ol.ND.YI.RC.Pi.Ph.Ph.Pi.Pl.Pm.NO.Pu.QB.QD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QD.YJ.Pd.Oq.VC.QD.QD.Pf.NP.L1.YK.YL.YM.YN.YO.YP.YQ.YR.UB.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N2.FJ.YS.YT.YU.PL.NE.NU.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.YV.PB.YW.YX.XW.YY.YZ.Y0.Y1.Y2.No.OF.OF.OF.OF.OF.OF.OF.P4.P4.P4.P4.P4.P4.P4.P4.P4.OF.OF.OF.OF.OF.OF.OF.OF.OF.K7.K7.K7.K7.Nn.TL.N2.W0.W0.W0.W0.W0.W0.W0.W0.W0.W0.OA.Je.Nm.OA.W0.W0.W0.Te.Y3.W0.O4.K7.UO.UO.UO.K7.K7.K7.K7.K7.K7.K7.K7.K7.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.Oz.R6.Ne.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.Nd.J#.P2.OV.OL.P9.P9.P9.P9.P9.P9.OL.OL.P9.UP.Y4.Y5.Y6.Y7.M4.OZ.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.M5.Rg.Y8.Y9.Z..Z#.Za.P9.MO.OF.SY.R6.Y#.Ya.Zb.Zc.Y..UM.Lv.Zd.Ze.Zf.Zf.Zd.Lv.UM.JK.Zg.Zh.Zi.Zj.SX.W7.SY.SY.SY.SY.SX.SY.MO.Yc.Yc.Yc.Yc.Yc.Yc.MZ.MZ.MZ.MZ",
-".JH.K1.MK.MH.ML.MH.MH.MH.MH.MH.MH.MH.MH.MH.MH.OD.OD.MM.P3.OO.ON.ON.Nn.IW.K1.IW.OD.OD.OD.OD.OD.OD.OD.MM.MM.OD.O5.Qn.I3.Ja.Rp.K8.Sl.Sg.Sf.Se.Sj.Tc.T..Zk.T8.Ud.Zl.U8.V#.Wp.Wn.Yw.Wi.Zm.Zn.Xi.Yt.Zo.Yo.AU.Zp.Zq.Zr.Zs.Zt.Zu.Zv.Zw.Zx.Zy.Zz.ZA.ZB.ZC.ZD.ZE.ZF.ZG.ZH.Cw.ZI.ZJ.ZK.Yp.ZL.ZM.ZN.ZO.ZP.Xd.ZQ.Wo.ZR.ZS.Yd.U4.T8.Uh.T..ZT.YB.ZU.Ui.Ro.ZV.PB.ZW.Ol.NE.NU.NU.NU.O#.ZX.ZY.Vx.ZZ.NC.NE.NQ.WH.Pi.RC.Ph.Pk.NS.Pv.Pp.QB.QD.Sy.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Tp.Ur.Pd.NT.QD.RD.QC.Pr.NQ.L1.Z0.Z1.Nt.O8.Rv.Z2.Z3.Z4.FJ.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.JH.Z5.Z6.Z7.Z8.NE.NE.NU.NU.NE.NE.NU.NE.NU.NU.NU.NU.NU.NU.NU.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Oq.ZZ.PB.Z9.0..0#.0a.0b.0c.0d.0e.KT.P4.P4.P4.P4.P4.P4.P4.OF.OF.OF.OF.OF.OF.OF.OF.OF.OF.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.Nm.N2.Y3.W0.W0.W0.W0.W0.W0.W0.W0.Te.Te.IW.Nm.HK.Tf.Te.Te.Te.W0.TL.N2.Oz.J#.K7.K7.K7.K7.O3.O3.Jt.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.J#.Qd.P2.Nd.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.Ne.SY.0f.P9.P9.P9.0g.0g.0g.0g.0g.0h.TO.P9.OK.0i.0j.0k.V1.M5.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.M4.Qa.0l.0m.0n.0o.P9.MO.XY.0p.R6.R6.Y..Zg.0q.0r.0s.0t.0u.0v.0w.0x.0y.0z.0w.0v.0A.0B.0C.0D.0E.0F.0G.R6.SY.SY.SY.R6.SX.K7.0H.MZ.MZ.MZ.MZ.MZ.MZ.MZ.VX.VX.VX",
-".Km.Km.Je.MH.MH.MH.MH.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.MM.OD.Nc.0H.Lp.IW.IW.IW.MM.OD.OD.OD.OD.OD.OD.MM.MM.OD.O5.Qo.I3.I4.K0.Vo.Sk.YC.T2.0I.Tc.T..Ww.Vi.Xs.Vf.Vd.0J.0K.Wm.Xd.Xf.Xg.Xj.0L.Yp.0M.Zq.Zs.0N.0O.CL.0P.Vt.0Q.0R.WQ.0S.0T.LC.PB.LL.0U.0V.0W.0X.0Y.0Z.00.Qs.NV.01.KK.02.03.04.05.06.07.Xk.Xg.0e.08.Xc.09.1..V..ZS.Yz.U..T6.LA.1#.1a.U0.1b.PB.ZZ.Oq.NE.NU.NU.NU.NE.Ol.1c.PB.1d.PM.O#.RG.Pz.Ph.Ph.Pi.Py.NL.Po.Ps.QD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QD.1e.NU.ND.XG.QD.QD.QD.QE.NP.QC.1f.ND.NU.NU.Sq.1g.1h.1i.1j.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.SF.1k.1l.1m.1n.NE.NU.Mi.1o.1p.1q.WG.NU.NE.ND.NU.NU.NE.NU.Pd.Pd.NU.NV.Oq.Pd.Pd.Pd.NU.NU.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Oq.Nx.PB.1r.1s.1t.1u.1v.1w.1x.1y.MR.OF.OF.OF.OF.OF.OF.OF.OF.OF.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.Jt.No.Y3.1z.W0.Te.Te.Te.Te.Te.Te.Te.Te.W0.Ha.HK.Oz.W0.W0.W0.W0.W0.SX.Qd.Qd.Oz.Nn.Jt.Jt.Jt.Jt.Q1.J#.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.P2.1A.P3.Nd.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.MV.UW.Nd.X2.0g.0g.0g.0g.0g.0g.1B.0h.TO.1C.1D.1E.1F.1G.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.M7.Qa.1H.1I.Q6.MZ.UP.1J.P2.R6.R6.R6.Ya.1K.0y.1L.1M.1M.1N.0E.0E.0E.1O.1O.1O.0E.0E.0E.0E.1P.1Q.1R.FF.Y..R6.SY.SY.SY.SX.SY.MO.VX.VX.VX.VX.VX.VX.VX.VX.VX.0o.1S",
-".K1.JH.Je.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.MM.Hb.OO.OO.Je.IW.K1.HK.P3.OD.OD.OD.OD.OD.MM.MM.OD.O5.I3.Sc.U0.U1.Sl.Sg.T2.Xw.YB.Wy.T#.T6.1T.1U.Xr.0J.0K.Wm.Wi.1V.1W.1X.1Y.Yk.CJ.1Z.10.11.12.13.WQ.Y1.14.PB.PB.PB.PB.PB.PB.PB.QN.15.16.17.18.19.NF.2..Tq.NV.NU.2#.RA.RA.RA.2a.2b.2c.2d.2e.2f.2g.2h.2i.2j.Yv.Wn.2k.1..Xr.2l.Xt.T7.Uh.Xx.2m.PB.2n.ND.ND.NU.NU.NU.NU.O#.2o.1d.NZ.2p.RR.2q.Pl.RC.Pi.L4.NJ.NP.Pf.QB.RF.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QD.2r.Pd.2r.QD.RD.RD.RD.Pr.Po.2r.NE.NE.NU.O8.2s.2t.2u.1j.N2.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.UB.2v.2w.2x.2y.NU.Kt.QG.QD.RD.QD.Ut.Px.NU.NE.NU.NU.Pd.NU.2z.2A.NQ.Tr.Pm.Ps.L7.2r.Px.O#.Om.Pd.NU.2B.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.2C.Tu.2D.YX.2E.2F.2G.2H.PB.2I.2J.OF.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.UO.UO.UO.UO.VV.Oz.2K.Te.Te.Te.W0.W0.W0.W0.W0.W0.W0.Oz.Je.Oz.SX.SX.SX.SX.SX.SX.SX.SX.Qd.Qd.Qd.SX.No.No.Oz.SX.J#.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.Oz.Qd.R1.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.OV.Nd.Jt.K7.P9.0g.0g.0g.0g.P9.0g.0g.P9.0h.ON.2L.2M.2N.2O.M4.M5.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M5.X8.M4.2P.2Q.SQ.2R.OF.SY.R6.R6.SY.SY.SX.2S.2T.1N.2U.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2V.2W.2X.2Y.2Z.20.Y..SY.SY.SY.SY.R6.R6.21.VX.VX.VX.VX.VX.VX.VX.VX.VX.22.23.24",
-".K1.Km.HK.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.MM.OF.ON.R1.IW.IW.IW.MK.P4.OD.OD.OD.OD.MM.MM.OD.O5.I3.I4.25.K8.Sl.Sg.S6.S8.ZT.26.Zk.T8.Vh.27.28.0K.Wm.Wi.29.Xm.3..Yj.3#.10.3a.Zu.3b.3c.3d.3e.PB.PB.PB.PB.NZ.3f.3g.3h.3i.3j.PB.3k.3l.3m.3n.Uw.3o.SA.RC.Pi.L2.3p.3q.L5.3r.Pe.NU.Pd.O..2#.2#.RA.Pd.Qz.3s.3t.3u.Xn.Yv.ZQ.Xo.3v.3w.3x.3y.ZT.3z.PB.3A.NE.NE.NU.NU.NU.NU.NE.NV.YG.Vx.Vx.0U.NE.Pn.RC.3B.L2.3C.3D.QB.QD.Sy.To.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Tp.NV.Oq.VC.Sy.Tp.To.Sy.Pf.3E.NU.NE.NU.NU.Sq.3F.3G.3H.3I.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.N3.UB.3J.3K.3L.Rv.NE.3M.QD.Tp.Tp.Tp.QD.3N.NV.NU.NE.NU.3O.Ps.Pj.Pz.RC.Ph.Ph.RC.RC.Ph.L4.3P.QC.Qz.Pd.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.ND.YM.3Q.3R.3S.3T.3U.3V.3W.PB.3X.3Y.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.UO.UO.UO.UO.UO.UO.UO.UO.K7.K7.K7.J#.3Z.TL.W0.W0.W0.W0.SX.SX.SX.SX.SX.SX.HK.Ha.Oz.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.30.Qd.Qd.Qd.31.Nm.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.J#.Qd.SX.Lw.OV.OV.OV.OV.OV.OV.OV.OV.Sa.Sa.Sa.Sa.Ne.O4.MO.X2.P9.P9.P9.OL.P9.OL.P9.P9.MZ.32.33.34.35.36.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.37.M5.Rg.ST.38.39.XY.4..SY.R6.R6.SY.SY.SY.SY.SX.4#.4a.1O.2U.2U.2U.2U.2U.2U.2U.2U.2U.2V.2W.2X.4b.4c.4d.Zb.R6.SY.SY.SY.SY.R6.R6.T0.OV.VX.VX.VX.0o.4e.22.0H.0H.4f.4g.4h.4i",
-".K1.Km.Je.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.MM.O3.ON.Nn.OA.IW.Oz.MM.OD.OD.OD.OD.MM.MM.OD.Ro.Qo.I4.K0.Vo.Se.Si.Xw.T..T#.Wx.Vj.U#.Vf.3w.4j.Xb.Wi.29.Xk.Yh.Yk.4k.3a.4l.4m.4n.Mb.4o.PB.PB.PB.3i.4p.4q.4r.4s.RR.Qs.NV.LP.4t.4u.KJ.4v.4w.4x.3l.4y.3C.4z.Ph.Ph.RC.RC.RC.Ph.Py.4A.2A.4B.NV.Pd.Pd.NU.Pd.RA.NE.4C.4D.4E.0e.O6.Yw.Xo.4F.3w.T8.4G.PB.4H.Oi.NE.NU.NU.NU.NU.NU.ND.Pd.4I.PB.PB.4J.NL.RC.Pl.Pm.NR.Pt.Tn.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QD.4K.NU.1o.QD.RD.Tp.To.QC.3N.O#.NU.NU.NU.NU.VB.4L.4M.Nl.Lu.N2.N2.N2.N2.N2.N2.N2.N2.N2.N3.N3.N3.N2.N2.N3.N3.N3.N3.N3.SF.4N.4O.4P.Sq.NU.YJ.QD.Tp.Tp.Tp.RD.QD.4Q.NU.NU.4R.Pi.RC.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Ph.Ph.Pg.3B.4S.Pe.Pd.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.O#.TD.4T.4U.4V.4W.4X.4Y.4Z.PB.40.3Y.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.UO.UO.UO.UO.UO.UO.UO.UO.K7.K7.K7.K7.K7.K7.K7.K7.K7.O3.Nn.Qd.41.SX.SX.SX.SX.SX.SX.SX.SX.SX.OC.Ha.Oz.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.Qd.1A.SX.J#.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.UM.1A.RY.Nd.OV.Sa.Sa.Sa.Sa.Sa.Sa.Sa.Sa.OV.OV.OV.0f.XY.42.0h.42.P9.TK.42.42.42.42.TK.43.44.45.46.47.48.49.M4.M4.M4.M4.M4.M4.M4.M4.37.M5.R#.5..5#.5a.UW.SY.R6.R6.SY.SY.SY.SY.W7.Ya.5b.Hr.5c.0E.2U.2U.2U.2U.2U.2U.2U.2U.2V.5d.5e.5f.5g.5h.5i.Oz.R6.R6.R6.R6.SX.SY.MN.0H.0H.4e.22.1S.23.5j.5k.42.5l.5m.5n.5o.5p",
-".KV.5q.Je.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.MM.Lp.L#.HK.HK.5r.Je.MM.OD.OD.OD.MM.MM.WA.Ro.Qo.U0.K0.Vo.Sk.Sj.Xw.Tb.Uh.LA.T7.Xs.5s.1..Wn.5t.ZO.5u.5v.ZK.4k.CA.4l.5w.5x.5y.5z.PB.NZ.3k.5A.5B.5C.Ol.XH.3q.NM.Py.Pk.Ph.RC.Pz.4z.4z.RH.Pl.02.5D.5E.4H.5F.Pz.Pg.Pi.Pi.Pi.Ph.Ph.RC.RC.Ph.NS.QC.5G.NV.Pd.Pd.NU.2#.2#.J0.5H.5I.Zm.5J.Yw.5K.Yd.5L.PB.WD.PL.5M.NU.NU.NU.NE.ND.PS.Oj.5N.SB.PB.Tt.Px.WF.L4.Sw.3D.QB.QD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QD.Tq.WG.RD.RD.Tp.Tp.QD.Ut.Pw.NU.NE.NU.NE.NU.5O.5P.5Q.5R.5S.5T.N2.HK.N2.5U.QK.QK.QK.5V.N4.5W.5X.1j.N2.N2.N3.N3.N3.N2.HF.5Y.5Z.50.NE.PS.51.RD.RD.Tp.Tp.Tp.Tn.4R.NU.NE.17.RC.Pi.Pi.Pj.Ph.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Pz.LS.52.Pd.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.53.54.55.56.57.58.59.6..PB.6#.6a.K7.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.K7.K7.K7.K7.K7.K7.K7.K7.O3.O3.O3.O3.O3.O3.O3.O3.Jt.Nm.TL.Qd.SX.SX.SX.SX.SX.SX.SX.SX.SX.OC.OC.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.Qd.Qd.P2.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.J#.SX.Qd.XY.OV.Sa.OV.OV.OV.OV.OV.OV.OV.OV.OV.MZ.MX.K7.ON.TO.Rk.6b.42.TK.42.42.42.TK.P9.6c.6d.6e.6f.6g.6h.V2.M4.M4.M4.M5.M5.M4.M4.M5.M4.Qf.6i.6j.6k.SY.R6.R6.SY.SY.SY.SY.SY.Ya.6l.6m.6n.0y.1L.2U.2U.2U.2U.2U.2U.2U.2V.2W.2X.6o.6p.TP.5h.6q.0H.Nc.XY.R0.4..R0.0f.ON.23.23.6r.6s.5k.6t.6u.6v.6w.6x.6y.6z.6A.6B.6C",
-".K1.Km.Je.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.MK.Jd.OD.OA.OA.HK.MK.OD.Un.Un.MM.MM.J#.OD.Qo.U0.K0.Vo.6D.6E.6F.6G.6H.T6.T8.3x.3w.Xp.Yw.6I.3u.6J.6K.CJ.3a.4l.6L.6M.6N.6O.PB.6P.6Q.6R.Oi.RB.6S.6T.Py.XI.Pg.RC.Ph.Ph.Ph.Ph.Ph.Pi.Pi.Pi.XI.4z.Uw.L2.6U.TA.6V.LS.SA.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Ph.RC.RC.Tr.6W.6X.NE.Pd.NU.Pd.RA.Oq.6Y.6Z.CF.5t.Wo.60.NA.61.RR.O#.NU.NU.NU.NE.NE.Pw.WF.4z.RC.62.63.64.51.RD.Pv.Pu.QD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QD.VC.Pw.3N.QD.Tp.RD.RD.VC.WG.NU.NU.NE.NE.Oa.Us.65.66.5t.67.68.69.7..7#.7..7a.7b.7c.7d.7e.7f.7g.7h.T#.N2.5S.7i.N2.N3.N2.7j.7k.XN.7l.7m.NE.O#.VC.RE.Tp.Tp.Tp.QD.2r.Pd.RB.Pk.Pi.Pk.3B.Pi.Pi.LW.Ph.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Pi.RC.Ph.7n.NE.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.7o.7p.7q.7r.7s.7t.7u.4w.PB.2m.5U.R0.K7.K7.K7.K7.K7.K7.K7.K7.K7.K7.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.UN.1A.Qd.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.Qd.1A.Oz.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.RY.Qd.UN.OO.OV.OV.OV.OV.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.UP.MN.7v.TO.TO.Rk.TK.6b.TK.42.7v.6b.7w.7x.7y.7z.7A.7B.7C.M4.M4.M4.M4.M5.M5.M4.M5.M4.Qa.7D.7E.7F.R6.R6.SY.SY.SY.SY.SY.7G.Ya.7H.6n.7I.1M.1O.2U.2V.2U.2U.2U.2U.2U.2V.2W.7J.7K.7L.7M.0o.VX.VX.VX.VX.VX.VX.VX.VX.22.1S.7N.7O.7P.7Q.6w.7R.5o.7S.7T.7U.7V.7T.7W.7X.7Y",
-".K1.Sm.No.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.OD.Un.Un.MM.Qo.OB.7Z.5r.HK.J#.OD.OD.J#.MM.J#.Jt.Lp.KZ.K8.70.6D.6E.71.6G.72.73.U..74.Xq.Xo.Xd.2i.75.76.Zq.77.78.79.6M.8..8#.PB.2n.8a.NE.NT.8b.3p.Pk.RC.RC.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.RC.8c.8d.VD.8e.8d.8f.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Ph.Pg.8g.4A.52.NE.Pd.NU.NU.8h.2#.4C.8i.8j.8k.QH.3i.8l.8m.NU.NU.NU.NU.NV.3C.Pz.Pi.Ph.8n.Kc.8o.8p.ND.4R.QB.QB.QD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.VC.1p.QD.RD.Tp.RD.RD.8q.NU.NE.Ok.NU.RR.8p.8r.8s.8t.8u.8v.8w.8x.Z1.8y.8z.8A.8B.8C.8D.Nr.8E.8E.8F.8G.8H.8I.8J.N1.M#.N1.HN.8K.4O.8L.8M.Sq.NU.3E.QD.Tp.Tp.RD.RD.8q.2#.1e.L4.L2.L2.L1.Pl.LZ.Pk.Pi.8N.Ph.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Ph.Pg.QC.ND.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.7o.7p.8O.8P.8Q.8R.8S.JW.PB.8T.RZ.R0.K7.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.J#.SX.Qd.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.Qd.Qd.Nn.JG.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.Oz.N2.R0.MX.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MX.MN.TN.Nd.MQ.OO.OV.TO.6b.42.Rk.Rk.42.8U.8V.6e.35.35.8W.1G.M4.M4.M4.M4.M4.M4.M5.M4.Qa.8X.8Y.8Z.R6.SY.SY.SY.SY.SY.SY.SY.6l.80.81.82.2U.2V.2U.2U.2U.2U.2U.2U.2U.2V.2W.1O.83.84.85.OL.X2.VX.VX.VX.VX.VX.VX.1B.86.87.88.89.5o.7U.7T.7T.6B.9..6B.6B.6B.6B.6B.6B.7W.7W",
-".Tf.Sm.HK.OD.OD.OD.OD.Un.Un.Un.Un.Un.Un.Un.Un.Un.OD.OD.OD.HK.OA.OA.P2.Jt.OD.Jt.J#.J#.Jt.Qo.MN.MQ.70.6D.T2.71.9#.72.9a.VT.8J.3v.9b.Xe.ZN.6J.AU.4k.CA.4l.9c.9d.LJ.Ny.ZW.UF.O#.9e.NQ.Pi.WH.RC.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.SA.NS.9f.WD.Kc.Uw.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.RC.RC.WF.Uu.NV.Pd.Pd.NU.2#.Pd.9g.Ec.9h.NY.9i.Ol.NU.NU.NU.Pd.9j.Pz.Ph.Pi.Pj.LV.SA.LS.9k.O..Pd.WG.3N.QD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.RD.RD.RD.Tp.Tp.QD.1e.NE.O#.PT.8p.ZW.9l.9m.9n.9o.2#.NU.NU.O..PL.2#.NE.Ol.Ol.Ok.Oq.NE.SJ.9p.VK.9q.9r.0Z.9s.L8.9t.9u.YA.9v.9w.9x.9y.9z.Rv.NU.JZ.QD.RD.Tp.QD.RD.Pw.Pd.2A.NK.NM.NM.Sw.QA.Pm.Tr.L1.LZ.3B.Pi.Ph.Ph.Ph.Pi.Pi.Pi.Pi.Ph.Pz.9A.Oq.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.53.54.9B.9C.9D.XM.9E.3e.PB.KL.TN.Nn.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.P2.Qd.1A.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.9F.9F.9F.Qd.1A.R6.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Nn.N3.Oz.2R.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.7w.MN.XA.SY.R6.SY.UW.21.42.OL.P9.Rk.TK.9G.9H.W3.9I.6f.8W.9J.49.M4.M4.M4.M4.M5.M4.Qa.9K.Rq.R7.R6.SY.9L.9L.9L.9L.9L.9L.R6.R6.9M.82.2V.2V.2U.2U.2U.2U.2U.2U.2U.2V.5d.2V.9N.9O.9P.X2.9Q.X2.X2.VX.VX.VX.VX.OV.9R.9S.9T.7V.9U.7X.7X.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B",
-".9V.9W.KW.Un.Un.Un.Un.Un.OD.OD.OD.OD.OD.OD.OD.OD.OD.Jt.Nn.IW.HK.OA.Nn.Jt.Jt.J#.J#.Jt.O3.Ja.MQ.70.Yc.2Q.71.9#.72.9a.9X.ZS.9Y.ZQ.Xn.9Z.Yr.90.ZI.0O.91.92.0T.1x.9q.1f.ND.RB.Pp.RC.RC.Ph.Ph.Ph.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pz.Pi.93.94.95.Uw.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.RC.RC.NS.4S.3O.Oi.NU.NU.Pd.96.4w.PB.RQ.97.98.NU.NE.NU.Ps.Pz.Pi.Pi.Ph.Ph.Pi.Pk.Pl.Pp.99.Oi.O#.52.Ut.QD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QD.L7.LQ#..#.##.a.YH.Qy.5C#.b.Z1.8y.0Z.9n.8A.5B.5B#.c.6R.7o.NE.NE.NE.NC#.d#.e.0U#.f.Qy.O..NE#.g.8v.3l#.h#.i#.j#.k#.l.Sq.NE.NV.Ut.QD.Tp.RD.Tp.Pe.NU.7n.Uv.Pq.Pp.Sx.Po.NQ.Pn.Sw#.m.L2.Pl.3B.Pi.Ph.Ph.Ph.Pi.Pi.Pi.Ph.Pz.Pf.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU#.n.ZX#.o#.p#.q.WL#.r.01.9m.PB#.s#.t.Nm.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.R6.1A.Qd.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.SX.9F.9F.9F.9F.9F.9F.9F.9F.9F.SX.SX.N2.N3.J#.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.UN.N2.R0.MX.MZ.MZ.MZ.MZ.Yc.Yc.Yc.Yc.Yc.Yc.Yc.MX.0f.SY.R6.R6.SY.R6.R6.SY.Rd.TK.X2.6b.VX.OL#.u#.v.9I.35.X5#.w.M4.M4.M4.M4.37.M4.Qa.8X.Rs.R7.R6.9L.9L.9L.SY.SY.SY.SY.7G#.x#.y#.z#.A.2U.2U.2U.2U.2U.2U.2U.2U.2V.5d#.B#.C#.D#.E.X2.9Q.X2.9Q.X2.X2.VX.VX.0H.9G.9S#.F.7U.7X.7X.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7X#.G",
-".Tf.Sm.RZ.OD.OD.OD.OD.OD.OD.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.J#.HK.Ha.Je.Jt.Jt.J#.J#.Jt.O3.Jd.Rd.VW.TN.2Q.71.9#.72.9a#.H.V..2k.Yw.0e#.I#.J.Zq#.K.CY#.L#.M.NZ.YV.Tl.ND.Oj#.N#.O.SA.RC.Ph.Pi.Pi.Pi.Ph.RC.RC.RC.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.RC.Pz#.P.63#.Q.SA.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Ph.Pz.Pi#.R.Pe.Pd.NE.NE.LM.NA.LK#.S.NV.NE.NE.O8.17.RC.Pi.Pi.RC.Pi.NH.Pl.NK.NO.Pu.5N.NV.Pd.NC.YJ.3N.RD.QD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Ps#.T#.U.9p#.V.6R.Z1.Uq.8B.Pc.8B.8B.8B.8B.8B.8B.8B.8B.Pc.5B#.W.RA.ND.ND#.X.Oc#.Y.Kx.5B#.Z#.b.NE.NC#.0#.1.ZZ#.2#.3#.4.LP.Oq.NE.4R.QD.RD.RD.Ut.NV.Oi.1p.QE.QD.QD.QB.QB.Pr.Pu.3D.NR.NM#.m.Py.LZ.Pi.Ph.Ph.Ph.Pj.Pi.Pi.Ph.Pz.RF.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.O#.Tl.Qv#.5#.6#.7#.8.Pw#.9.LC##.###.Nm.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.J#.SX.1A.SX.SX.SX.SX.SX.9F.9F.9F.9F.9F.9F.9F.9F.9F.9F.SX.SX.SX.SX.SX.SX.SX.SX.SX.N3.N3.P2.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Q1.N3.UN.MQ.MZ.Yc.Yc.Yc.Yc.Yc.MZ.MZ.MZ.MZ.MZ.MZ.UP.SY.SX.SY.SY.SY.SY.SY.SX.UW.6D.7v.7v.7w##a##b.9I.9I.9I.6f##c.6h.M4.M4.M5.M5.Qf##d##e.Q3.No.SY.SY.SY.SY.P2.P2.P2.Nm.6l##f##g.1O.2V.2U.2U.2U.2U.2U.2U.2V.2W##h##i##j.7M##k.X2.X2.X2.X2.X2.9Q.X2.VX.0H##l##m##n##o.9..6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7X##p##q##r",
-".Tf.N2.RZ.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.OF.OA##s.Je.Jt.Jt.Nn.Jt.O3.R1.U0.MO.TN.TK.6F##t.72##u.Yd##v.Wo##w.0e.ZM##x.Zs.3a.Zv##y##z.9l.6R.ND.Oa.52.NS.Ph##A##B.52.NT.RB.Tq.Tq.2r.52.RG.Tm.NS.Ph.RC.RC.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.RC.Uw##C##D.Pl.RC.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.RC.L1##E.Pe.Ol##F.P#.Pb##G.Mj.NC.ND.NU##H.Pz.Pi.Pi.Ph.Pi.Pk.Py.QA.Uv.Pt.QE##I.NT.NE.Pd.ND.Tq.3M.RD.QD.RD.RD.RD.RD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QE.QD##J#.e.Tk##K##L.9n.8B.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B##N.8B.Pc.Uq##O.ND.NU.9q#.Y.Tk##P.5B.Pc##Q.PL.Oj.WC.Vu##R##S##T##U.5B##V.Px.Tp.QD.RD.Tp.Pe.NU.4K.Tn.RD.RD.RD.Sy.QD.Tn.QB.Pr.Pp.NP.NL.NS.L0.Pk.Pi.Ph.Ph.Ph.Pi.Pi.Ph.RC.Uu.Pd.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oq##W.Tu##X##Y##Z.Qs.NE.NV##0##1###.Je.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.UM.Qd.Qd.9F.9F.9F.9F.9F.9F.SX.SX.SX.SX.SX.SX.SX.SX.SX.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N2.Oz.R0.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.UN.N3.O3.0H.MZ.MZ.MZ.MZ.MZ.MZ.MZ.MZ.VX.VX.VX.0H.K7.R6.R6.SY.SY.SY.SY.SY.Y..SY.MO.7v.MX##2##3##4.35.9I.35.6f##5.M4.M4.M4.M5.R###6##7.Y..No.OB.P2.P2.P2.P2.P2.P2.P2.Yb##8##9.1N.2V.2U.2U.2U.2U.2U.2V.2U.5e.2U#a.#a#.85#.E.X2.X2.X2.X2.X2.9Q.9Q.9Q.VX#aa#ab#ac#ad.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W#ae#af#ae#ag#ah#ai#aj",
-".N3.N2.N3.Nn.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.J#.HK.IW.MK.VV.J#.J#.Jt.Qo.MN.MQ.TN.TK#ak.6G#al#am.9X##v.Wb.Yw.0e.Xm.Yo.CJ#an#ao#ap#aq#ar.Oq.5M.Pe.QC.NS#.N.2q.NU.Pd.Pd.Pd.Pd.Om.Pd.Pd.Pd.Pd.NU.Nt.NT#.N.WF.Ph.RC.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.RC.Pg#as#at#au.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Pz.NK##A.Z0.Qw.Nz.NA#av#aw#ax.O#.52.RC.Ph.Pi.Ph.Pi.Pk.Py.Sw.Po#ay.QD.QD.QD.L5.Mi.NE.Pd.NU.8q.VA.3N.Tp.RD.RD.RD.RD.Sy.Sy.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QB##I#az#aA.YV#aB#aC#aD#aE#aF.9n.8B.8B.5B.5B.8A.8w.9n.9n.9n.8A.5B.5B.5B##N.8B.9n.PT.Oq.O7#.Y#.e#aF.9n.8B.8B.Z1.Nt#aG##G.TD.ND.Om.8p.9p.9l#aH#aI.QE.QD.RD.Px.Pd.52.Tn.Tp.Tp.Tp.RD.RD.RD.QD.QD.QB.Pf.Po.Pv.Pm.Py.Pk.Pi.Ph.Ph.Ph.Pi.Pi.RC.NS.Pe.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Nt#aJ.PB#aK#aL#aM.NU.NU.NC.8t#aN#aO.Nm.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.J#.SX.N2.N3.SX.SX.SX.SX.SX.SX.SX.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.Nn.JG.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Q1.N3.P2.ON.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.MQ.SY.SX.SY.9L.9L.9L.9L.9L.Oz.SY.2R.MQ.Q2#aP#aQ.W3.9I.9I#aR#aS.M5.M4.M4.M5.O1.TT#aT#aU.P2.No.P2.P2.P2.P2.P2.P2.P2.P2.0G#aV#aW#aX.2U.2U.2U.2U.2U.2V.5d.2X#aY#aZ.6q#a0.X2.9Q.9Q.X2.X2.9Q.X2.X2.X2.VX#aa#a1#a2#a3#a4.6B.6B.6B.6B.6B.6B.6B.7W#ae#a5#a6#a7#a8#a9#b.#b##ba#bb",
-".N3.N2.TL.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.No.N3.Nn.Jt.J#.Jt.Lp.Jd.Rd.70.TK#bc#bd.6H#am#be#bf.WY.5J.0e.Xm#bg.CJ.3a#bh##F.9q##O.Oq.NU.3O.Pp.RG.NV.Pd.Om.NU.NE.NU.NU.NU.NU.NU.NU.NU.NE.NU.NU.Pd.Pd.NE.4B.WF.RC.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pz.NQ#bi#bj.RC.Ph.Pi.Pi.Pi.Pi.Ph.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Uw#bk.7p.P#.Nz.NA.PB.XE#bl.Oi.Pk.RC.Pi.Ph.Pi.Pk.L2.3C.3D.QE.QD.RD.RD.Sy.Tp.1p.1o.Kt.Px.Oq.NV.Ur.51.8q#bm.2r.NT.4R.3N.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QD.Pr.Kt.6Q.0U.4q.RQ#bn#.e.TA.PB#bo#.Z.5B.9n.5B.64.Qr.9s.Oo#bp.WC#bq.SK.64#br.5B.9n#bs#bt.Nt.0X.VK.YL.4J#bu.8B##M.5B.2##bv.3i#aF#bw.9r.2#.NU#bx.VK#aC#by.QD.Pr.Kt.O..YJ#bz.Tp.Tp.Tp.Tp.Tp.Tp.RD.Sy.QD.QC.Pr.Sx.NO.NK.L2.Pk.Pi.Ph.Ph.Ph.Pi.Ph.Pz##H.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NC.4J.PB#bA.NC.NE.NU.NU.Nt#bB#bC#bD.Nn.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.RY.N3.N3.SX.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.Qd.UM.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.Oz.N3.21.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.0H.R0.SX.R6.SY.SY.SY.SY.SY.SY.No.0p.4..UW#bE#bF#bG#bH.9I.35#bI#bJ.M4.OZ.M5.Rg#bK#bL#bM.Q1.HK.Je.P2.P2.P2.P2.P2.P2.P2.P2.Yb#bN.0y.1N.2U.2U.2U.2U.2W.7J##i.6p.Rk#bO#.E.X2.X2.X2.X2.X2.9Q.X2.X2.X2.X2.4e#bP#bQ#bR#bS.7W.7W.7W.7W.7W##p.6B#ae.7W#bT#bU#bV#bW#bX#bY#bZ#b0#b1#b2",
-".N3.Lu.N2.Nn.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.O3.P2.No.Jt.Jt.J#.O3.Jd.KZ.MO.6D.6E#b3#b4#b5.Yz#b6#b7#b8.8j#.I.76#b9.CY#c.#aC.0Y.O8.ND.NU.NU.Tq.Ur.Pd.NU.NE.NU.NE.NU.NU.Pd.Pd.Pd.NU.NU.98.NE.NU.NU.NU.NE.NU.Pd.NE#c#.L2.Pz.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi#au#ca#aB#cb#cc.Pi.Ph.Ph.RC.RC.Ph.Ph.RC.RC.RC.Ph.Pi.Pi.Pi.Pi.Pi.Pi.RC.NK#cd.Pa.QH#ce.PB.NA.Vx.63#cf.Pg.RC.Ph.Pi.Pk.L2.Sw.Po.Ps.QD.RD.RD.Tp.RD.RD.Sy.QD.RD.Ut.3N.3M.3E.4R.99.JZ.Qz.NT.NT.1p.RD.RD.Tp.Tp.Tp.Tp.RD.Ps#cg#ch.9l#ci.ZX.ND.Kh#cj#aC.2p.Tu.PA.5B#bx#ck#cl.0U#.e#cm#.Y#.Y#.Y.YL#.e.0U.2n##K#bl#cn.Uq.PL.O7#.Y.YL.SK.Uq.8B.8B.8B.Tl#co.P#.0X.9n#.f.5B.Z1.PL.QO#cp.WE#cq#cr.3N.NU.52.QB.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.RD.QD.QD.Ps.Pp.NO.L6.L2.Pk.Pi.LW.Ph.Ph.Pi.Ph.Pi.Tq.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Ol#cs.NZ#ct.Oq.NU.NU.NU.Ol.YH#cu#cv.KU.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.J#.Oz.N2.IW.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N2.Oz.P3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.OF.Nn.N3.Jt.0H.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.MN.No.No.SY.SY.P2.P2.P2.P2.P2.OB.SY.Q0.Xz.8U#cw#cx.9I.9I#aR#cy.37.M5.M5.OQ.O2#cz#cA.OF.Oz.No.P2.P2.P2.P2.Nm.Nm.Nm.Nm.Nm.Je.0G#cB#cC#cD#.z#.z.5d#cE#cF.87#.D##k.X2.X2.X2.9Q.X2.X2.9Q.X2.X2.9Q.X2.X2.9R#cG#cH##p#a5.6B#cI#cI#cJ.6B#ae#a5#cK#cL#bI.8W.9I#cM#cN#cO#cP#cQ#cR#cR",
-".N3.N2.Km.Nm.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Nn.OB.P3.J#.Jt.R1.MN.MQ.TN.2Q.6F.9##cS#cT.ZS#cU##w#cV#.I#cW#cX#cY#cZ#aC.4s.Oq.NE.NU.NU.NE.NU.NU.NE.NU.2###R#c0.Pd.Oq.LN.Pe.Pe.2c.Oi#c1##R#c0.RA.NU.NE.NU.NE.NU.Pd.Ur#c2.Pl.WH.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.RC.RC.QN#c3.L2#cc.Ph.NJ##H.Uu.NT.99#c4.4S.Pn.LZ.RC.RC.Ph.Pi.Pi.Pi.Ph#c5#c6#c7.SC.KD.93.Pb.XD.Pb.NA#c8.Ph.SA.Pi.Pk.Py.NL.17.Ps.QD.RD.Sy.Tp.Tp.Tp.Tp.Tp.RD.RD.RD.RD.RD.QD.QD.QD.RD.QD.RD.Tp.RD.RD.Tp.Tp.Tp.RD.Pt.Rv##K.9q#c9#d..2##.X#d##da.Kx.2p.NZ.LJ#db.2n.YL#dc.0U.0U#.e.0U.0U.0U.0U.0U.0U#.e#.e#cm#cl.4r##W#dd#cm#.e.SK.9n.9n.5B.8B#de.4r.NA.8u#bu.5B.8B.8B.5B.Qy.PT.YH#.Y.9l.Ku.ND#df.QD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.To.RD.QD.QD.QE.Pp.NO.NK.Py.Pk.Pi.Ph.XI.Ph.Pi.Pz.RF.NU.NE.NU.NU.NU.NU.NU.NU.NU.NE.NE.8F#dg.Ns.ND.NU.NU.NU.Ok#dh#di#bM.J#.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.RY.N3.Tf.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.N3.O4.OF.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.JG.JG.JG.Jt.Oz.UM.ON.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.MO.RY.Oz.Je.P2.P2.P2.P2.P2.P2.No.OB.O4.MQ.OL.9G#dj#dk.35.35.6f#dl.V1.M5.Ni#dm#dn#bM.R1.SY.No.Nm.Nm.Nm.Nm.Nm.Nm.Nm#do#do#do.Sb.Sb.Y.#dp#dq#dr#ds#dt#du#a0#a0.9Q.9Q.X2.X2.X2.9Q.X2.X2.X2.X2.X2.6q.9G.23.0g#dv#dw#dx#dy#dz#dA#dB#dC#dD#dE#dF#dG#dH#dI.9I#dJ#dK#dL#dM#cQ#cR#cR#cR",
-".N3.N2.1A.No.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.MK.No.P3.Jt.O3.Jd.Rd.70.TK#dN.6G.6H#dO#dP##v#dQ#dR#dS.5v.90.Cw#dT#dU.5C.Ol.NE.NU.NU.NU.NU.NE.NU.RA.PL#dV#dW#dX#dY#dZ#d0#d1#d2#d3#d4#d5#d6#d7#d8.NU.5O.NU.NE.NU.NE.NU.Pd.Px##H.Pk.RC.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Pi.RC#d9.6P#e..Po#e#.Oq.Pd.Pd.Pd.Pd.Pd.Pd.NE.Sv.9j.Pn.Ph.RC.Ph.Pi.Pi.LT#ea#eb#ec#ed#ee#ef#eg.PB.Pb.PB#eh#ei.4z.Pi.L1.L6.NQ.Pf.QC.QD.RE.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.RD.Tp.Tp.Tp.Tp.QD.RD#ej.Oc#.c#c1.XF#.c#ek.8p#el#em.9p.PA.Ny.Qw#en.0U#.Y.Tt.61#aE#aE.94.61#eo.Tt.4H#ep#.e.0U.0U.YL.4q.8p#eq#.e.0U#er#cl.O7.8B.9n.0Z#es.P#.Qr.9n.8B.8B.5B.8B.Pc.0Z##O.XF.ZW#ep.Vy.Oq.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.To.RD.RE.QD.Ps.Pp.Pn.NK.L1.Pk.Pi.Ph.Ph.Pi.RC.L4.51.NU.NU.NU.NU.NU.NU.NU.NU.ND#.0#aE##G.Oi.Oa.NU.NU.NU.O#.8y#et.TZ.J#.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.J#.Oz.N2.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.Oz.J#.O3.O3.O3.O3.O3.O3.O3.O3.O3.JG.JG.JG.JG.JG.JG.JG.JG.O3.O3.O3.Q1.SX.Nc.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.VX.0H.Jt.Oz.No.P2.P2.P2.P2.P2.P2.No.P2.XA.TN.2Q.TO.7w#eu#ev.9I.6f##c.6h.49.M4.PY.Q.#bM.0f.UM.Sb#do#do#do#do#do#do#do#do.Nm.Sb.OW.Oz.Nn.MQ#ew#ex#a0#a0.85.9P#.E.9Q.X2.9Q.X2.X2.X2.X2#ew#ew#ew.9Q.22#ey.PW#ez#eA#eB#eC#eD#eE#eE#eF#eG#eH#eI#eJ#eK#eL#eM#eN#eO#eP#eQ#eR#b1#cR#cR#cR#cR",
-".N3.N3.N2.Oz.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Q1.P2.Jt.O5.Qo.KZ.K8.6D.6E#eS#eT#am.74#eU.5K#eV#eW#cW#eX#eY#eZ.0V#e0.Oq.NU.NU.NU.NU.NE.NU.5O.Pd#e1#e2#e3#e4#e5#e4#e6#e7#e7#e7#e8#e6#e4#e9#f.#f##fa#fb.ND.RA.NU.NE.98.NU.NU.NU.Qz.Pf.Ph.RC.Pi.Pi.Pi.Pi.Pi.RC.L2#fc.97#fd#dd#fe##O.NE.NU.NU.2B.NU.NU.NU.NU.NU.NU.Pd.NE.99.Pp.Ph.RC.Ph.Ph.LT#ff#fg#fh#fi#fj#.m#fk#fl.PB.PB#fm#fn.Pg#.O.Pm.NP.Pu.QB.Tn.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QD.QB#fo#aC#bt.Tl#.Z.8B.5B.8a#fp#fq#fr.Tk.3i#fs.P#.P##aE#dg.P#.P#.P#.P#.P#.P#.P#.P#.P#.Nz.3i#av.YL.0U#ft#br#aC.0U#.e#.e#.e.YL#er##L#fu#fv.Qx.4r.9n.8B.8B.8B.8B.5B.8B.8B#.b.WR#fw#fx#eq#fy.QE.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.To.RD.QD.QC.Pr.Po.NM.NS.Pl.Pi.8N.Ph.Ph.Ph.Pz.Vz.Pd.NE.NU.NU.NU.NU.NU.NU.Ol#.c.XD#fz.Ol.NE.NU.NU.NU.ND#fA#fB.Nm.VV.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.P2.N3.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.TL.Oz.P3.O3.O3.JG.JG.JG.JG.JG.JG.JG.JG.O3.O3.O3.O3.O3.O3.O3.O3.O3.XY.K7.UM.K7.0H.VX.VX.VX.0o.4e#fC.4f#fD.4f.4f.4f.43.XY.No.No.P2.P2.P2.P2.P2.P2.No.No.Xz.TK.2Q.TO.TO.43#fE#ev.35#aR#fF.V1.M5.R##fG.X2.R0.UM.Sb.Nm.Nm.Nm.Nm.Nm.Nm.Nm.Nm.UN.8Z.UN#fH.Y..UP.9Q.X2.X2.X2.X2.X2.X2.9Q#ew.X2#fI#fI#ew#fI#ew#fI#fI#ew#fJ#fK#fL#eF#fM#fM#fN#fO.OI#fP#fQ.1D#fR#fS#fT.OV#fU.Rk#fV#fW#fX#fY#fZ#f0#f1#f2#f2#f2#f2",
-".N3.N3.N2.N3.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Q1.Jt.O3.Ja.MQ.TN.2Q.6F.9##f3#be.Yy#cU#f4#f5#f6#eX.79#f7.4q.PT.Ok.NU.NU.NU.NE.NU.Pd.ND#f8#f9.Z##g.#g##e7#f##ga#gb#e4#gc#e6#gd#gb#ge#ge#ge#gb#e4#e4#gf#gg.5O.NU.NE.NU.NE.NE.Pd.PL.52.Pm.WH.Ph.Pi.Pi.RC.L0.Us.Pd.NU.ND#gh#gi#gj.PL.ND.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.Pd.NU.NT.NR.RC.Ph#gk.Pi#gl#gm#gn#go.Pz.8c#gp#gq.8t#gr.P##gs#gt.L4.QA.3D.Ps.QD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Pr#gu#gv.0U#.c#bw.5B.5B#gw.9m#er.RN#gx#gy#gz#ce.Ny.P##gA.P#.P##gA#gA.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.XE.8F#aC#dd.0U.0U#.e#.e#gB#.e#.e.0U#gC#gD.QH.WC#bs.9n.5B.8B.8B.8B.8B.8B.Pc.Z1.ND.8a.XJ#gE.QD.QD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Sy.QD.QB.Pf.NQ.NL.L2.Pk.Pi.Ph.Ph.Pi.RC.Tr.NV.NU.NU.NU.NU.NU.NU.NE.Ol#gF.PB##P.Nt.NU.NU.NU.NU.2s#gG#gH#gI.Jt.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.K7.J#.Oz.N2.IW.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.Qd.No.Jt.O3.JG.JG.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.XY.XY.XY.XY.XY.XY.XY.XY.R0.P3.7w.VX.6q.4e.23.4f.42#gJ#gK#gL#gM#gN#gO#gP.6l.No.P2.P2.P2.Nm.Nm.Nm.No.Oz.XY.TK.2Q.TO.7v.TO#gQ#gR.6e.35.6f.V0.V1.M7#gS.0m.OW.HI.Sb.Nm.Nm.Nm.Nm.Nm.Nm.Nm.Nm.OW.JD#gT#gU#gV#gW#gX#ew#ew.X2#ew#fI#ew#fI#fI#ew.X2#ew#fI#ew.X2#fI.9Q.X2.9Q.1C#gY#gZ#eC#g0#g1#g2#g3.X2.9G.1C.X3#fU.OK.7w.OV.OV.0H.MQ.9R#g4#g5#g6#g7#g8#g9#h.#h##ha",
-".N3.N3.Lu.K1.Nn.Jt.Jt.Jt.Jt.Jt.Jt.Nn.J#.Jt.Jt.Jt.Jt.Jt.Jt.J#.MM.Jt.Qo.Rd.70.Sg.S6#bd#hb.Ue.Np##v#hc#dR#hd#eX#he.KD#hf.Oi.ND.NU.NU.NU.NE.O#.97#hg#hh#hi#hj#hk#hl#hm#hn#ho#hp#hq#hr#hs#d4#ht#e4#hu#gb#ge#ge#ge#gb#e4#hv#hw.Pd.Pd.NE.NU.NU.NE.Pd.Ns.Oq.RF#gk.Ph.Ph.RC.1e.Pd.NE.NU.NE.NE.4J#br.Z1.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.Pd.NE.3E.L1.RC.LT#hx#hy#gm#hz#hA#hB.J8.RH.RC.RF#hC#hD#hE.8l#hF.Sx.Pp.Pr.QC.QD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Sy.RD.Ku#hG.YL#gw.Uq.9n#fe#gF.0U#cm#hH.9q#d1#hI#hJ#fs.Ny.P##hK#gA.P#.P##gA#gA.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.3i#hH#hL.0U#.e.0U.0U.0U#.e.0U#.e.4q#fl.PA.0U.YV#es.64.9n.5B.8B.8B.8B.8B.Pc#bv.Ol#hM.3i.Mg.Pr.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.RD.QD.QB.Pp.Pn.NJ.Pl.3B.Ph.Ph.Ph.Ph.RC.YJ.Oi.NU.NU.NU.NU.NU.O#.Ns#aE.L9.1f.O#.NU.NU.NU.NU.2y#hN#hO.JL.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.Jt.No.N3.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.UM.K7.O3.O3.O3.O3.O3.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.K7.OO.0o.23.23#hP#hQ#hR#hS#hT.7T.5o.5o.5o.7U#hU#hV#hW.No.Nm.Nm.Nm.Nm.Sb.RZ.K7.OL#hX#hY#hY.9Q.9Q.23#hZ#h0.35.35#bJ.37.Qa#h1#h2.Sb.Sb.Nm.Nm.Nm.Nm.Nm.Nm.Nm.Nm.SX.9F#h3.5o.5o.7T#h4#h5#h6.VX.9Q#ew#fI#ew#fI#fI.9Q.X2.9Q.X2.X2.X2.X2.X2.X2.X2.P8#h7#h8#gY.42#h9.86.4e.VX.X2.X2.P9.P9.P9.P9.P9.OV#i.#i##ia#ib#ic#id#ie#if#ig#ig#ig#ih",
-".N3.N3.N2.N2.P2.Jt.Jt.Jt.Jt.Jt.Jt.J#.J#.Jt.Jt.Jt.Jt.Jt.J#.Jt.Jt.O3.MN.MQ.Yc.2Q.71#b4#cS.3x#b6#b7#ii#ij.76#ik#il#im.Pd.ND.NU.NU.ND.Ol.7o#co#in#io.5z.PB.PB.PB.PB#ip.On.LN##W#iq.Ns#de#.c#ir#is#it#gb#f.#gb#ge#ge#ge#iu#f.#iv.LN.RA.NE.NU.NU.Oa.2##.b#c1.3E.Pk.Pz.3C.ND.NU.NU.NU.NU.Oq.4s.4r.5B##O.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.Pd.Px.Po.WH.LT#hx#iw#gm#gm#ix#iy#iz.LT.Pz.Uw.Pz.Pn#iA#iB#iC#iD.5N.3N#gu.Tp.RD.RD.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Tp.Tp.QD.RD#iE.8F.8F.WC#hM.8B.Vy#iF#.e.0U.0U#iG#iH#e7#iI#iJ.Ny.Ny.P##gA#hK.P#.P#.P##gA#gA.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.3i##G.Tk.0U.VK.VK#.e.0U.0U.0U.YV#iK##G.0U#.e#cm.0U.NB#.f.9n.5B.8B.8B.5B.8B.4s#iL#aF.Vx#iM.Pq.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Sy.QD.QC.Pr.Uv.NL.Py.Pk.Pi.Ph.Ph.Pi.Pz#.N.Pd.NU.NU.NU.NU.NE.NV#hE.PB.NX.ND.NE.NU.NU.NU.O8.Qs#iN#iO.Lx.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.OF.Nn.N3.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.P2.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.R0.MV.23.0H#iP#iQ#iR.7T.7V.7U.9U.6B.6B.6B.7X.6B#iS#iT.SX.Sb#do#do.Nm.Sb.RZ.J#.P9#hX.9Q.9Q#hY#hY#iU.6q#iV#iW.7z.6f#iX#iY.M9#iZ#i0.Sb.Nm.Nm.Nm.Nm.Nm.Nm.Nm.Nm.9F#i1#i2#ad.6B.9..9..7T#i3#i4.4e.9Q.X2.X2.X2.9Q.X2.9Q.X2.9Q.X2.X2#ew#ew.X2.X2#ew.22#i5#i6.OK##k.X2.X2.X2.X2.X2.9Q.TO.P9.P9.P9.TN#i.#i7#i8#i9#j.#if#ig#if#if#j##j##j##if",
-".N3.N3.N2.Lu.No.Jt.Jt.Jt.Jt.Jt.Jt.J#.J#.J#.Jt.Jt.Jt.Jt.Nn.Jt.Jt.R1.Rd.70.TK#ja#jb.6H#jc#dP.3v#jd#je#jf#bg#jg#er##O.O#.NU.Oa.Ol.5C#jh.PA.PB.PB.Qx#ji#fx#hL.NX.NX.4x.PB#jj#jk.LN.Mw.7n.4A.4Q.Mw##Q#jl#jm#gd#hu#gb#ge#ge#gb#f.#fr.95.RA.NE.NU.NU.ND#.X#de##R.NT.L2.2A.Pd.NE.NU.NU.NE.ND##V.RP#aH.8x.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.ND.8b.RC.LT#jn#jo.3V#gm.3V#jp#jq#jr.LT.RC.RC#js.Pl.17#c2.4K.4K#hF.VC.RD.RD.RD.Tp.Tp.Tp.Tp.Tp.RD.RE.RD.Tp.RD.QD.RD#jt#ep#.e#cl.Uq#fe#aq#.e.0U#.e#ju.63#jv#jw#jx.On#fs#fs.Ny.P##gA#gA#gA.P#.P#.P##hK.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.Nz.8F.Tk#jy#ce#jz.LL##G.0U.YV.Tk.P#.VK.Tk#.e.0U#.e#cm#er#fe#.Z.5B.8B.8B.8B.8B.6R#e0.94.Nz#jA.Pf.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.To.RD.QD.QB.Pp.Pv.NJ.L4#jB.Pi.RC.Pi.RC.Ps.NU.NU.NU.NU.NU.Oq.QO.XE.NZ#jC.Nt.NU.NU.NU.NU.O8.Oa#jD#jE.HH.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.OF.J#.Oz.N2.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.RY.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7#jF#jG#jH#jI.7W.5o.7T.6B.9..6B.6B.6B.6B.6B.6B.6B#ad#jJ#jK.RZ.Nm.Nm.Sb.RZ.J#.MZ#jL#jM.9Q#hY#hY#hY#hY.VX#jN#jO#bH#jP#fF#jQ#jR.JK.RZ.Nm.Nm.Nm.Nm.Nm.Nm.Nm.Nm.UN.Hp#jS#iS.6B.9..6B.6B.9.#jT#jU.4f.P9.9Q.X2.X2#fI#ew#ew#ew#fI#fI#fI.9Q.X2.9Q.X2.X2.P9.X2.43.P9.TO.9Q.TO.TO.TO.TO#hX#hX.TO.P9#jV#e6#jW#jX#jY#if#ig#if#j##j##j##j##j##j##j#",
-".N3.N3.N3.N2.Oz.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.J#.Jt.Jt.Jt.J#.J#.J#.O3.MN.MP.6D.2Q#jZ.9##cS#be#eU.5K#j0#.I.5v#j1.5A#j2.Oq.NE.Ol##V#bp.L9.PB.1x.YL.Pc.PS#fd#j3.NO.Pk.Pi#j4#j5.3e.PB.93.Ks.QO.Ks.Ph.Uw.QA.99#j6#j7#j8#f.#e7#ge#ge#gb#f.#j9.4C.5O.NE.NU.NE.ND##O#fu.TD.Tq#fc.NU.NU.NU.NE.NE#c1.PL.6R#.c.8B#.0.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.NU.L7.NH.Pz#jr#k.#k#.9y#ka#kb#kc#kd#iz#ke#gk.LZ.NS.NL.NQ.Pu.QE.QC.QD.RD.Tp.Tp.RD#kf.RE#kf#kg#kh#ki.RD.QE.1q#gv#ep#.e#aC#kj#bl.Tk#.e#kk.XJ#ip.Ny#ce#kl#km#hs#iJ.NZ#fs.P#.P##gA.P##gA#gA.P#.P##gA.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.94#kn.WD#fs.Ny.Ny.Ny.Ny#fl##K##G.XE.0U.0U#.e#.e#.e.0U#.e.0U.WC.9n.5B.8B.8B.8B.8B#bv#fv.PB.14#bj.Pr.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Sy.Sy.Tn.Pt.NQ.NK.L1.Pi.Pi.RC.Pi.RC.NO.NE.NU.NU.NU.NE.NE.Su.PB#en#ko.ND.NU.NU.NU.NU.O8#kp#kq#kr.Nn.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.O3.JG.JG.JG.JG.JG.O3.R0.No.Qd.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.RY.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.S4#ks.JF#kt.7R.5o.6B.9..6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7U#ku#kv.Oz.Nm.Sb.RZ.J#.MZ#jL#jM#jM.9Q.TO#kw#kw#kw#kx#ky#kz.7z#kA#kB#kC#kD#kE.Sb.Nm.Nm.Nm.Nm.Nm.Nm.Nm.Sb.8Z#kF.5o#kG.7X.6B.6B.6B.7X.7S#kH.TO.6q.9Q.9Q##k#a0.85.85.9P#kw.9Q.9Q.9Q.9Q.X2.TO#hX.TO.TO.P9.TO#hX.TO.TO.TO.TO#hX.TO.TO.P9#kI#kJ#kK#kL#ie#kM#if#j##j##j##j##j##j##j##j##j#",
-".N3.N3.N3.Lu.N3.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Nn.J#.Jt.Qo.Rd.70.TK#ja#b3#hb.9a#dP#kN##w#kO.ZL#kP#kQ#ar.Ol.Oa.NC.8a.WD.PB.Ny.2n#ct.4B.NR.Pi.RC.Pz.RC.Ph.Ph.RC.Uw.Pv#kR.PB#kS#kT.Mj.QO.3C.SA#cc.RC.4S.Mr#kU#kV#gc#gb#ge#ge#e4#kW.9g.RA.NE.NU.NE.ND.XF.9n.00.ND.NE.NU.NE.ND.1f.5B#gj#bv.Z1#.f.Z1.5M.NE.NU.NU.NU.NU.NU.NE.NE.NE.NE.NU.NU.NU.NU.2B.NU.Pd.Tq.NO.Pz#ke#kX#kY#kZ.9y#k0#k1#k2#k3#k4.Pl#k5#k6#k7#k8.J3#k9#l.#l##la#bz.To#lb#lc#ld#le#lf.RD.QE.4Q#ci.8F#.e.3A##K#gF.YL#jy.L9.NZ.NZ.NZ.Tu.PU#lg#lh#li.1x#fs#lj#gA.P##gA.P#.P#.P##gA.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.Nz#kk#kk#fs#fs.Ny.Ny#fs#fs.Ny#.#.94.XJ#.e.0U.0U#.e#.e#.e.0U#.e#.e#dd.9n.5B.8B.8B.8B.5B.0Y.XE.Vx#kS#lk.QD.RD.Tp.To.RD.Tp.Tp.Tp.Tp.RD.RD.QB.Ps.Po.Sw.L2.Pk.Ph.Ph.Ph.RC.Pn.NE.NU.NU.NE.NV#bx.NA.Vx.O7.Oj.NE.NU.NU.NU.NU.O8#ll#lm.S4.J#.O3.O3.O3.O3.O3.O3.JG.JG.JG.JG.JG.JG.JG.O3.O3.O3.O3.O3.K7.RY.N3.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.RY.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.S4#ln#lo#lp.5o.9U.9..6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.9..7V#lq.8Z.Sb.Sb.RZ.Jt.MZ#jL#lr#kw#kw#kw#kw#kw#kw#kw#kx#ls#lt#dJ#lu#lv#lw#lx.Sb.Nm.Nm.Nm.Nm.Nm.Nm.Nm.Nm.R7#ly#lz.9U.7X.6B.6B.6B.6B.6B#lA#lB#lC#fC.X2#a0.TO#lD#lE#lF#lG#.E#a0#.E.9Q.9Q.9Q.X2.TO#hX#hX.TO.TO.TO#hX.TO.TO#hX.TO.TO.P9.UL#lH#lI#lJ#lK#lK#j##j##j##j##j##j##j##j##j##j##j#",
-".N3.N3.N3.N2.N3.J#.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Nn.J#.O3.Jd.MQ.Yc.2Q.Xw.6G.72.Yz#eU#b7#eV#lL#f6#lM#lN.Oa.ND#lO#lP#lQ.PB.WE##Q.RB.NR.RC.RC.Ph.Pi.Ph.Ph.Ph.RC.RC.RC.Pz.SA#kT.3k.94#lR.O.#lS.NQ.SA.Ph.Pz.Ph#bm#ef#it#f.#gb#ge#ge#e4#lT#lU.2#.NE.NU.NE.NE.Z1.8B.9r.2#.5M.NE.Pd#hM.8B.5B.8w.Z1.9n.9n##V.ND.NU.NU.NU.PS.NE.NE#c1#c1.NE.NE.NE.NU.NU.NU.NE.NU.Pd.Oq.Pf.Pz.Ph#jr#lV#lW#lX#lY#lZ#fg#gn#l0#l1#l2#l3#l4#l5#l6#l7#l8#l9#m.#m##ma#mb#mc#kf#kf.Pr#md#dd#.Y#.Y.0U.0U.0U.4p.QH.NZ.NZ.NZ.NZ.NA.PB.Tk#me#dY.0U.Tu#fs.Ny.P##gA#hK.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.QH#ep.LC#mf#fs.Ny#fs.Nz.Nz.XE.9p#ce.Nz.3i#mg#.e#kn.0U#.e#.e#mh#.e#cm.5A#mi.5B.8B.8B.5B.0Z.0U.Nz.P#.5D.Pf.RD.RD.Tp.Tp.RD.Tp.Tp.Tp.Tp.QD.QD.QB.Pp.Pv.Tr.L4.Pi.Pi.RC.RC.Pp.NU.NU.NU.Oq.XF.XJ.Vx.Rz.7o.Ok.NU.NU.NU.NU.NU.Qs#mj#mk#gI.Jt.JG.JG.JG.JG.JG.JG.O3.O3.O3.O3.O3.O3.O3.O3.XY.XY.XY.K7.O4.SX.N2.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.N3.N3.O4.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY#ln#ml#mm.5o.6B.7X.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B#kG#mn#mo#h2.Sb.Sb.O3.P9#jL#lr#lr#mp.TO#kw.TO.9Q.9Q.9Q.1S#mq#eC#aR#mr#ms#mt.3Z.Nm.Je.Je.Je.Je.Je.Je.Je.UN#kv#mu.5o.7W.6B.6B.6B.6B.6B.6B.7X.7T#mv#mw.0g#mx#my#mz.5d#mA#mB#mC#mD#ew.9P.9Q.9Q.9Q.X2.TO.TO.TO.TO.TO.42.7v.42.42.0h.42.42#mE#mF#mG#mH#if#j##j##j##j##j##j##j##j##j##j##j##j#",
-".N3.N3.N3.N2.N2.Nm.OF.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.J#.J#.Jt.Lp.KZ.MO.TK.6E.71#eT#am#.H.3v#jd#kO#mI.ZK#mJ#mK.GM#mL#mM.PB.14.Qr.LO.18.Ph.RC.Ph.Pi.Ph.RC.Ph.Tr.NL.NQ.Ps.Ps.L2.Pz#ee.8d.Kx.NZ#mN.LQ.8p#c2.Pg.Pl.Pl.8f.Tm.SJ#mO#f.#gd#ge#ge#gc#mP.Mq.Pd.NE.NU.Oa.Oi#mQ##P##Q.PL.O###O#bs##N.8B.8B.8y#mQ#cn#.b.ND.NE.NU.NE.ND.TD.8a.9n.9n.Z1.53.NE.NE.NE.NU.NU.NU.NE.NU.ND.Ps.Pz.Ph#gk#ke.RC#mR#mS#mT#mU#mV#fg.2w#mW#gm#lY#mX#mY#mZ#m0#m1#m2#la#bz#kf.QD.Pt.KK#m3#cm#jy.WD.Tk#.e#fl.Ny.NZ.NZ.NZ.NZ.NZ#m4.QI#m5#fI#m6.NZ.Ny#jz.Ny.P##gA#gA.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P##m7.Nr.Ny#fs.Ny.P#.Nz.Nz.NY#m8#eq.NY.P#.P#.P#.XE#kk.0U.0U#.e#.e#.e#.e#cm.NB.Uq.5B.8B.8B#.Z.Oo.Nz.WE.L9#m9.Pr.RD.Tp.Tp.RD.Tp.Tp.Tp.Tp.RD.Sy.QD.Pf.NO.NJ.Pl.3B.Pi.Ph.Pz#n..Pd.NE.Oq.Pd.YL.PB.Nr#.d.Oq.NU.NU.NU.NU.NU.O8.2s#n##na#gI.O3.O3.O3.O3.O3.O3.O3.O3.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.R0.Oz.N2.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.RZ.RZ.N3.P1.3Z.O4.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY#jF#nb#nc.5o.9..7X.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W.7V#nd#h2.RZ.UM.XY#kw#jL#jL#jL#jL#jM#jM#kw.TO.9Q.9Q.9Q.23#ne.6e#kA#nf.S0#ng.Je.Je.Je.Nm.Nm.Nm.Nm.Nm#nh#ni#nj.7U##p.6B.6B.6B.6B.6B.6B.6B.6B#lA#nk#nl#nm#nn.2X.2W.2U.2U.5d.2X.2V#no#lG#np##k#hY.X2.42.42.7v.42.42.42#hX#hX.9Q#nq#nr#ns#nt#nu#kM#if#j##j##j##j##j##j##j##j##j##j##j##j##j#",
-".N2.N3.N3.N3.1A.No.P3.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Jt.Nn.Jt.OF.R1.Rd.70.2Q#bc#jb.6H#dO#b6.09#nv#ij#cW#nw.4l#nx.6M#ny.PB.YV.5C.9j.Py.Pz.Ph.Pi.Ph.RC.Pi.Pf#fd.NV.ND.NU.Pd.Pd.Ur.L7.3C.Uw.Py.9f.PB#nz.LO.8w#nA.8q.Ur.NT#nB.Ps.4s#nC#e7#gd#ge#gb#nD#nE.PS.NU.NE.NU.ND#.X.9n.8B.8p.NE.Tl.Pc.8B.8B.8B.Uq.4s.8B.Uq.2#.ND.NE.ND.TD.9n.8B.8B.8B.8B.5B#bv.PT.ND.NE.NU.NU.NU.NE.NU.ND##A.Pz.Ph.Pi.Ph#jr#gg#nF##M#nG#nH#nI#nJ#nK#nL#mY.1l.3V#nM#nN.Pt#nO#nP.Pr.QC#ko.2n.8G.PQ.3f#.e.5E.LL.NZ.NZ.NZ.NZ.NZ.NZ.NZ.NZ#nQ#nR#nS.2n.PB#fs#fs.Ny#gA#hK#gA.P##gA#gA.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.P#.L9.6P#jz#fs#fs.Ny#fs#iK.Nz.P##nT.8F.NY.P#.P#.P#.P#.Nz#fl#.Y.Tk.0U#.e#.e#.e#.e#nU.Uq##N.8B.8w.4r.3i.0U#nV#bi.Ut.QD.Tp.RD.Tp.Tp.Tp.Tp.Tp.To.RD#nW.Pr.NQ.NK.L0.Pk.Pi.Ph.Pz.1e.Pd.Oq.ND#gF.Vx.Nz#fu.Ol.NE.NU.NU.NU.NU.NU#nX#nY#nZ#n0.Nn.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.1J.R0.No.Tf.N3.Oz.Oz.Oz.Oz.Oz.Oz.Oz.Oz.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.P1.N2.RZ.R0.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7#n1#n2#n3.9..6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W#n4#n5#n6#h2.Nm#n7.9Q#jL#n8#n8#n8#jL#jL#jM.9Q.9Q.9Q.TO#hX#gQ#n9#dk#o.#o#.41.UN.Nm.Nm.Nm.Nm#oa#oa#oa#ob#oc#od#oe.9U.7X.6B.6B.6B.6B.6B.6B.6B.6B.7X#of#og#.z.2W.2V.2U.2U.2U.2U.2V.2W.2X#oh.6p.TO#.E.TO#hX.TO.TO.TO#hX#hX.TO.TK#oi#oj#ok#ol#om#kM#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j#",
-".N3.N2.N3.N3.N2.Oz.Q1.O5.Jt.Jt.Jt.Jt.Jt.Jt.OE.OE.Nn.J#.O3.Ja.MQ.6D.6E#jZ.6G#on.2l.1.#hc.0e#oo.6K#.K#op#oq#or.PB#bl.Oq.RF.RC.RC.Pi.Pi.RC.RC.NR#e#.NU.Pd.NU.NU.NU.NU.NU.NU.Pd.ND#c4#os.L2#ot.Ny#kT.Qs##Q.98.NU.Pd.Pd.Pe.2q#.Z#ou#e4#gd#ge#gb#f.#ov.5O.NE.NU.NE.ND.Tl.8B.8B#ow.Qy.8B.8B.8B.8B.5B.XF.Uq.8B.Qy.ND.NE.Pd#bw.8B##N.8B.8B.8B.8B.Pc.9n#ox.Oi.ND.NE.NU.NU.NE.NU.NV.NR.Pz.Ph.Pi.Ph.Pz.Ph.01.8v#oy.4d#oz#oA#oB#oC#oD#nL#oE#oF#oG#oH#oI#oJ.5C.0U.QQ#oK.4x#hJ.4x.Tu.PB.PB.PB.NZ.NZ.NZ.NZ.Ny.PB#oL#f##oM.PB#fs#jz#fs#fs#fs#gA.P#.Nz#fs.P##fs.Nz.P#.P#.P#.P#.P#.P#.P#.P#.P##eo.PA.Ny#fs.Ny.Ny.Nz#iK.P#.PA#aF.ZY.P#.P#.P#.P#.P#.P#.P#.3i#oN.Tk.0U#.e#gB#.e.0U#aF.9n.8B.8w.5B.63#en.4s.94#ch.Pq.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QC#oO.Po.QA.L1.Pk.Pi.RC.L0.Px.ND.Oi#eq.PB.Pb.8t.Ol.NE.NU.NU.NU.NU.NU.NU.2s#oP#oQ.P2.OF.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.5i.UM.N3.N3.Oz.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ#oR#oR#oR#oR#oR.RZ.P1.KV.UN.R0.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY#oS#oT#oU.9U.7X.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B#oV#lB#oW#oX#oY.Nn.MO#jM#jM#gd#hX#jL#jL#jL#jL#jM.9Q.9Q#jM#jL.VX.K7#oZ#dk#o0#o1#o2#oa#oa#oa#oa#oa#oa#oa#oa#h2#o3#o4.7U.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.7X.9U#o5#o6.2V.2U.2U.2U.2U.2U.2U.2V.2V.5d#o7#o8#ew.9Q.TO.9Q#hX.TO.TK.X2#o9#p.#p##pa#ba#pb#kM#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j#",
-".Je.Lu.N3.N3.N3.N2.No.OD.OE.OE.OE.OE.OE.OE.OE.J#.J#.Jt.Qo.U0.MW.ZU#dN.Xv#pc#pd.ZS#pe.Wf.2i#pf.Zq.Cw.2I.Ix.NZ.4J.Sv.4A.Pz.Ph.Pi.Ph.Pz.NS.1o.NE.Pd.NU.NU.NE.NE.NE.Oa.Oa.Oa.Oa.NE.NU.NV#nB#pg#jj#hL#ph.XF.Qy.O#.NE.NE.NE.Ok.Pd.9n#pi#e5#gb#ge#e7#pj#pk.2#.NE.NU.NE.NU.8p.8B.Uq.8x.8B.8B.8B.8B.8B.XF##W#cn#.c.Pd.O##j2.8B##M.8B.8B.8B.8B.8B##M.8B.8B##Q#e0.ND.NE.NU.NU.NE.NU.Ol.Pp.Pz.Ph.Pi.Pi#pl.Pz#pm.8v#pn.La#po.Qc#pp#pq#pr#ps#pt#pu#pv#pw.4C#aB.0U#kl.NX.Ny.Qx.PB#hL#px#py.63.Ny.PB.PB.NZ.NZ.3i#pz#pA#jy.Ny.6O#jz#fs#fs#fs.P##fs#fs#fs#fs.Nz#iK.P#.P#.P#.P#.P#.P#.P#.P#.Qx.3k#fs#fs#fs.Ny.Ny#fs#iK.NA#eq#nT.NY.Nz.P#.P#.P#.P#.P#.P#.P#.Nz.WD.0U.0U#.e.0U#.e#.##bs.8w.5B.9n#eg.0d##O.SK.L9#pB.Pr.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QD.Ps.Sx.Sw#pC.Pk.Ph.RC.3E.ND.00.VK.PB#ce.SK.ND.NE.NU.NU.NU.NU.NU.NU.O8.ND#pD#pE.IW.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.Nm.N3.P1.RZ.RZ.RZ#oR#oR#oR#oR#oR#oR#oR#oR#oR#oR.RZ.RZ.RZ.RZ.P1#pF#nh.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7#pG#gW.7U.9..6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W#a5#pH#pI.0G.K7.MZ#jL#jL#gd#jM#jL#jL#jL#gd#jL#jL.9Q#hX#jL.TO#pJ#dp#pK#pL#pM#pN#nh#oa#oa#oa#oa#oa#oa#oa.OW#pO#pP.5o.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B#pQ#pR#.z.2U.2U.2U.2U.2U.2U.2U.2U.2U.2V.2W.2U#pS#a0.2Q.2Q#iu#pT#pU#pV#pW#pX#pY#ib#pZ#kM#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j#",
-".OF.Lu.N2.N3.N3.N2.N3.MK.O3.Jt.Jt.Jt.Jt.Jt.Jt.P5.P3.Um.Xz#n7#p0.YC.1a.1##p1#jc.V..5K#ii.3u##x.4k.CY#p2.3f#.c.8q.3C.Pz.Ph.Pi.Ph.RC.RF.Pe.Pd.NU.NU.NE.5M.NE.2##p3.1f.Tl#.b#.b.Qy#p4.ZX##V.9e.Ku.P#.NB.7n#.d##O##O#bv.9r.Ns.2B.RA.8a#p5#e5#ge#ge#f.#e2.2#.NE.NU.NU.ND#p6.5B#p7.8B##N.8B.8B.8B.8B#bv.RR.8B.8B#e0.WR.Uq.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.6R.NU.ND.NE.NU.NE.NU.Oq.9A.Pz.Ph.Pi.Pi.Ph.Ph.Mw#cn#p8#p9.Nd.KZ.Rn.Ne#q.#q##qa#al#qb#qc#iG.SK.Oo.NA.Ny.PB#py.Kt#cb#ph#iD#jk#qd#py#lQ.PB#px#qe#qd.PB.Ny#fs#jz#fs#fs#fs#fs#fs#fs#fs#fs#fs.Nz.Nz#gA#gA.P#.P#.P#.P##gA#iJ.LK.Ny#fs#fs.Ny.Ny.P#.P#.QH#aF##G.NY.Nz.P#.P#.P#.P#.P#.P#.P#.P#.P##qf.0U.0U#.e.0U.YL##U#.V.5B.Uq#nV#ce#.9.WR.61#aB#qg.QD.RD.RD.Tp.Tp.Tp.Tp.RE.RD.QC#qh.NM.Py.Pk.RC.Po.ND.Pc.61.61.9p#.9.NV.ND.NU.NU.NU.NU.NU.NU.NU.Sq#qi#qj#qk.Nn.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.RY.P1.P1#oR#oR#oR#oR#oR.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.P1.P1.Nm.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.S4#ql#qm.7R.6B.7X.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B#oV#a4#qn#qo#qp#qq.9Q#jL#jM#n8#jM#jL#jL#jL#n8#mp#jL#jM.TO#hX.TO.XY#qr#qs#qt.7A#qu.P0#oa#oa#oa#oa#oa#oa#ob#nh#ni#qv#qw.7W.7X.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B#lA#qx.1O#qy.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U#qz#qA#qB#qC#p.#qD#qE#mH#qF#qG#qH#qI#qH#qJ#if#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j#",
-".Rd.N2.Lu.N3.N3.N3.N2.No.OF.OF.OF.OF.OF.OF.OF.J#.OF.XA.Ja.K9.Yc#qK.ZT.YA#qL.3x#qM#hc.6I#oo.0M.CJ#qN.LL.Z1.2q.NM.Pz.Ph.Pi.Ph.RC#qO.NU.NU.NU.NE.ND.NU.00.Z1.Uq.8B.8B#.f#.f#.f#.f#.f.8B#.Z#.d.53.Vy.PB#qP.1o.5B.5B#.f#.f.8B#mQ.TD#p3#qQ#j9#e6#ge#gd#qR#qS.2#.NU.NU.NE.Oa#qT.8B.8B.8B.8B.8B.8B.5B.0Z.O8#mQ.Pc#qU.Tl.Pc##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8p#.X.ND.NE.NU.NE.NU.Nt.WF.WH.Ph.Pi.Pi.WH.QA#hM#ca#qV#qW.OV.OV.Nd#qX.Lw.JE#p9#qY#qZ.9s.5B.Nr.NZ.PB.Ms#q0#j3#q1#q1#q1#c4#q2#q3##S.Mz#q4#q5#lQ.Tu.3e.63.Ny#fs.Ny.Ny#fs#fs#fs#fs.Ny.Ny#fs#fs#fs.P#.P#.Nz.P#.P##fl#mg#mf#fs#fs.Ny.Ny.Ny.3j.PB#ft.YG.Ny.P#.Nz.Nz.Nz.P#.P#.P#.P#.P#.P#.P#.P#.PA.3A.0U#.e.3A#.e.Qr#.Z.9n.0U.P#.8a#iL.9m.PB.KJ.Pq.Tp.Tp.RD.RD.RD.RD.QD.QD.QB.3D.NL.Pl.Ph.RC.6X.2###P#gj.53.ND.Nt.Oq.NE.NU.NU.NU.NU.NU.NU.O8.2y#q6#q7.Sb.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.RY.P1.P1.P1.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ#gI.P1.P1.RY.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.UO.K7#q8.5o.9..6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W#a5#cI#q9#r.#r#.X3#jL#jL#hX#jM#n8#jM#jM#jL#jL#jL#jL#jM#jM#hX.TO.T0#nh#ra#rb#rc#rd#re#nh#ob#ob#ob#ob#ob#ob#nh#qr.GW#rf.6A.7X.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.9U#rg#rh.1Q#ri.2U.2U.2U.2U.2V#cD#rj#rk#rl#rm#rn#ro#rp#rq#rr#rs#rt#pb#ru#f2#rv#rw#rx#lK#if#ry#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j#",
-".MO.No.Lu.N3.N3.N3.N3.Oz.P3.OF.OF.OF.OF.OF.OF.Q1.OF.Xz.U0.MP#rz#ja.Xv#pc#rA.8J#pe#rB#rC#pf#rD#rE.Mu#rF.Oj.9A.Pz.Ph.Pi.Ph.Pg.RF.NU.NU.NU.NE.NU.1f#fu.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n.Uq.3A#ip.LN.LO#br.5B.8B.8B.8B#bs.1f#rG#rH#e4#gb#gb#f.#rI.5O.NE.NU.NU.ND.PT.9n##M.8B.8B.8B.8B.8B#bs#ko.Vw#cn#hM#gj.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.Uq.00.ND.NE.NU.NE.NU.Px.Tr.RC.Pi.Pi.Ph.SA#j3#lN#rJ#p9.Nd.Sa.Sa.Sa.OV.W9#rK#rL.9p.0Z#gF.NZ.PB#kS#rM#q1#iD#rN#rO.2d.JZ#rP#rO#rQ.RQ#rR.0U.PB.2p#hC#iE.NZ.NZ.Ny#fs.Ny.Ny.Ny.Ny.Ny.Ny.Ny.Ny#rS.P##gA.Nz.P#.1d.4p#jz.Ny#fs#fs.Ny.Ny#fs.Ny#ip#fe.VK#rT.Ny.Ny.P#.Nz.Nz.Nz.P#.P#.P#.P#.P#.P#.P##fl.0U.0U#.e#.e#.##aH.Uq#nV.NA.8p#iL#.c.NZ#jj#rU.Ut#rV.YJ.NT.4Q.3N#rW.Tp.RF.Pr.Po.Po.QC#c#.NE.Pd.Oa.Ur.9e.8q.NU.NE.NU.NU.NU.NU.NU.NU.NU.Sq#rX#rY#rZ.RZ.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.RY.P1.P1.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ#gI#gI#gI#gI#gI#gI#gI.P1.P1.P1.O4.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY#pG#r0.7W.6B.6B.6B.6B.6B.6B.6B.6B.7W#oV##p#oV#a5#bT#r1#r2#r3.9O#jL#jM#hX#n8#n8#jL#hX#jM.7v#jL#jM#jL#jM.9Q#jL.2R#oa#nh#nh#re#r4#r5.Oy#oa#oa#oa#oa#oa#oa.UM#nh#r6#r7.9U##p.6B.6B.6B.6B.6B.7W.6B.9U.7R#r8#r8#r9#s.#s#.6o#cD#cD#cD#cD#sa#sb#sc#sd#se#sf#sg#sh#si#sj#sk#if#ie#sl#sm#sn#so#sp#ig#ih#sq#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##sq#if#mH#sr",
-".MX.O3.W1.N2.N3.P1.N3.N3.No.OF.P4.P4.P4.P4.P3.P3.K7.R1.Rd.MW.ZU#dN#bd#b4#ss.Yy.09#b8#st#jf#su#sv.9s.O#.7n.Pz.Ph.Pi.Pi.RC.Py.Pe.NU.NE.ND.2#.8a.8B.8B.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8A.8B#fl#bo#sw#.d.8B.8B.8B.8B.8B.5B.RR#.b#sx#f.#ge#e7#sy.JZ.Pd.NU.NU.NE.ND#bt.8B.8B.8B.8B.8B.8B.8B.Qy.NV.5B.8B.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B.5B.Qy.NE.NE.NU.NE.Pd.NT.Pi.Ph.Pi.Pi.Ph.XI.XF#jk#sz.MQ.OV.OV.OV.MO.OL#sA#.Y.Pc.0Y.L9.PB.WD.1n#q0.JZ.Ks.Qz.WG.01.Mq.LN#pm#sB#qP.2C.Ny.XJ#rQ.01.LN#sC.3k.PB.Tu.Ny#fs.Ny.Ny#fs#fs#fs#fs#lj#gA.Nz.Nz.Nz.6P.14.Ny#fs.Ny#fs#fs#fs#fs.Tu.Tk.Qr#jz.Ny#fs.Ny.Ny.Ny.Nz.Nz.Nz.P#.P#.P#.P#.P#.P#.P#.Nr.0U.0U#.e.YL#ck#bw#nV.NY#br.Ol.Tl#ce.PB#qd.Pu.RD.Ut.5G.Pe.ND.O#.Oq.Ol.Oq.NE.NU.Pd.NU.Pe#nA.4S.3P.SA.7n.Pd.NU.NU.NU.NU.NU.NU.NU.O8.2s#sD#sE#fJ.J#.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.Nn.P1.P1.P1.RZ.RZ#gI#gI#gI#gI#gI#gI#gI#gI.RZ.RZ.RZ.RZ.RZ.3Z.P1#ra.J#.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7#pG#sF.5o.9..6B.6B.6B.6B.6B.7W#ae#sG#sH#ae#sI#sJ.W3#sK#sL#bO#sM#n8#mp#lr#jL#jL#jL#jL#jL#jL#jL#jM#hX.9Q#jL.MX#oa#nh#oa#oa#sN#sO#sP.OW#oa#oa.RY.UM.UM.RY.O4.Q6.X2#sQ.7S.9..6B.6B.6B.6B.9..6B.7W#sR#sS#sT#sU.1L.2V.2V#cD#sa#sa#sV#cD#sW#sX#sY#sZ#s0#s1#s2#s3#sk#if#if#rr#s4#s5#ie#lK#ig#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#s6#s7#s8#s7#j##s9#t.",
-".MW.Rd.N3.Lu.P1.N3.N3.N3.Oz.O4.OF.OF.OF.OF#pJ.R0#t#.MN#ta.MY.5Q#tb#tc#td#jc#eU#te#j0#tf#pf#tg#.e.NE.2r.Pi.Ph.Pi.Pi.Ph.Pz.4S.Pd.NE.ND#c1#gj.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#.Z#th.Ny#c8#ti#cn.5B.8B.8B##M.8B.8a.53#tj#tk#gd#gb#e4#tl.5O.NE.NU.NE.ND#.0.5B.8B.8B.8B.8B.8B.8B#fu#bm##Q.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B.8B#ek.NU.Oa.NU.NE.Pd##B.RC.Ph.Pi.Pi#cc#.R#lN#tm.O3.MX.MZ.MZ.MN#tn#py.0X.0Z.VK.PB.63#to#rN.J1.Mx#m9.LP.LO.2y.Qs.Qs#j6#tp#tq#tr.PB#bB.2y.TC.Mr.Nu#ts#tt#fm.Ny.PB.NZ.Ny#fs#fs#fs.Ny.Ny.P#.Nz.P##mg#tu.Ny.3j.Ny.Ny#fs#fs#fs.Ny#jz#fe.YL.NZ#fs.Ny.Ny.Ny.Ny.Ny.P#.Nz.Nz.P#.P#.P#.P#.P#.P#.Nz.Tt.Tk.0U#.e.YV#mi#gD.Nz.4J##O.RR.ZY.Pb#aD#tv.Tn.RD.Sy.Tp#hF.1o.YJ.YJ.4R.NF.QC.NQ.NK.Pl.Ph.RC.Ph.Pz##H.Pd.NU.NU.NU.NU.NU.NU.O8.1n#tw.XN#tx.K1.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.RY.P1.KV.P1#gI#gI#gI#gI.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.RZ.UN.UN.3Z.3Z.UN.R0.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY#oS#ty#tz.9U.7W.7W.7W.7W#ae#af#bS#tA#dA.35#tB#tC#tD#tE#tF.85#sM#sM#sM#lr#jL#jL#tG#lr#jL.9Q#hX#jL#jL#jM#jL.P9.O4.UN.RY.RY.RY.RY.OW.UM.RY.RY.UM.UM.UM.R0.2R.X2#bO#tH#rg#lA.6B.6B.6B.6B.7W.9U#tI#tJ#o6#tK#.z#.z.1L.2V#rj#sb#tL#tM#tN#tO#tP#tQ#tR#tS#tT#tU#tV#mH#j##if#if#j##if#lK#if#sq#j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#tW#sk#tX#tY#tZ#t0#t1#t2#t3",
-".MX.Nd.Q1.W1.TL.N3.N3.OC.W0.P2.K7.K7.K7.K7.P3.K7.XY.X1.2R.0H#t4#t5#t6#t7#t8#t9#u.#u##f5#ua#aD##O.Oq.NQ.Pz.Pi.Pi.Pi.Ph.Pi.RB.NU.NE.Oi.8p.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.Uq.0V#fl#q2#.S#cn.8B.8B.8B##M#aH.53#j2#ub#e5#ge#e4#uc.Pd.NU.NU.NU.Qs.Pd.0Z##N.8B.8B.8B.8B.5B.5B.Qz#.b.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#de.Pd.5M.O8.NU.NU.RF.Pz.Ph.Pi.Ph.RC#ud#ue#rz.MO.MZ.MZ.W9#uf#ug.0Z.Oo#uh#aE#cf.Kt.Mq#ui.4t.2s#aM.TC#uj#uk#uk#lS#ul.VK.PB.VK#um#j6#un#j6#j6#ko.O8#uj#qP.8G.3k#fs.NZ#fs#jz#lj.P#.Nz.P##qf.2n#fs.P##fs#fs#fs.Ny#fs#fs.Tu.0U#fe#jz.Ny.Ny.Ny#fs#fs.P#.NA.Ny.P#.Nz.Nz.P#.P#.P#.P#.P#.P##ce.WE.Tk.0U#.e.Qr.4p#ce.0Y.8p.RR.Nr.P#.PB#mN.Pf.Tp.Tp.RD.RD.Tn.QB.Pu.NP.NS.3B.RC.Pg.Ph.Ph.Pi.Pi.RC.Ps.Pd.NU.NU.NU.NU.NU#nX#uo#up#uq#ur#us.Sb.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.Nn.3Z.P1.3Z.RZ.RZ.RZ.RZ.RZ.RZ.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z#ut#nh.Jt.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7#uu#uv.5o.7W#uw#ux#uy#uz#uA#uB#uC#uD.Y6#uE#uF#uG#uH#bO#jM#sM#tG#sM#sM#uI#jL#jL#jL#jL#sM#sM#jL#hX#jL#jL#mp.XY.UN.UM.RY.RY.RY.RY.UM.UM.RY.UM.R6.R0.OO.X2.9Q#uJ#uK#uL.9U.7X.6B.6B.6B.7X.6B#uM#uN#uO.1L.2U.2U.2V#cD#sb#uP#uQ#uR#tQ#uS#uT#uU#uV.56.7r#uW#uX#uY#if#j##j##j##if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#sr#s6#uZ#u0#u1#u2#u3#u4#u5#u6#u7",
-".OV.OV.Lw.W0.TL.W0.W0.W0.Oz.OC.OF.K7.K7.K7.O4.Lq.Xz.M0.UP.MZ#lr#u8#u9.72#v.##v#v##va#eW#vb#ug.Ok.9e.RC.Ph.Pi.Pi.Pi.RC.Ps.NU.NE.ND.Qy.Pc##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.5B#iJ#kl.Sz#cn.5B.8B.8B.8B.8B.8p.RA.IM#vc#gd#gd#pj#vd.WR.NU.NU.NE.5M.Z1.8B.8B.8B.8B.8B.8B#cn.LQ.PT#cn.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8x.O..ND.NU.NU.Oq.3p.RC.Ph.Pi.Pz#c2.8B#ve.O3.MZ.MZ.Jt#vf#vg.5B#eo.Ny#vh#bm.LP.Nu#nX.Mr#uj#vi#j6.Mj.Vw.Op#cn#gF.NZ.Nr.PR.1f.SJ.SJ#vj.1f.8l.Vw#j6#vk.Sq#vl#.S#bi.NZ#lj.P#.Nz.Nz#vm.Su.14.Ny.P##fs#fs#fs.Ny.Ny.Ny.LL#vn.VK.NZ#fs.Ny#fs#fs.Ny#vo.Ny.NZ.Ny.P#.Nz.Nz.P#.P#.P#.P#.P#.P##aE#.e.0U#.e#dd#av.QH#.f.8A.4s#mg.NA.PB.RO.Pt.RD.Tp.Tp.Tp.RD.QD.Ps.NQ.NJ.LZ.Pj.Ph.Pi.Pi.Pi.Pi.Pz.Tm.Pd.NU.NU.NU.NU.Sq#vp#vq#gm.XN#vr.K8.R0.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.RY.3Z.3Z.SX.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.UM.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.5i#po#vs.7S#.G#vt#dF#vu#vv#vw#vx#vy#vz#vA#vB#vC#vD#vE#sM#sM#sM#sM#jL#jL#jM#jL#jL#jL#sM#jL#jL#sM#jM#jM#jL.0f.UM.UM.RY.RY.RY.RY.RY.RY.RY.UM.UM.MV.7v#hX#hX#ew#vF#vG.6A.9..6B.6B.6B.6B##p#pQ#vH#o6.1L.2U.2U.2V#vI#sc#vJ#vK#vL#tS#uV.7r.7r.7r.56#tT#uU#vM#vN#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#if#vO#vP#vQ#u1#vR#vS#vT#vU#tW#s7#lK",
-".OV.OV.P9.P3.W1.1z.W0.W0.W0.W0.Nn.K7.K7.R0.R0.OF#vV.X1.UP.OM#vW#u8#vX#cS.74.3v#rB#eV.91.LC.4s.NV.3C.RC.Pi.Pi.Pi.Ph.RC#fc.Pd.ND.NE.8p.8B.8B.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n#fe.WD.95.UF.8B.8B.8B.8B##M#bs.ZX##V#sx#hu#gb#f.#vY.5O.NE.NU.NU.O#.XF.8B.8B.8B.8B.8B.8B#cn.Mj.LP.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B.8a.Pd.Oa.NE.NU.Tq.Pk.RC.Pi.Ph.Ph.4s#vZ.MQ.MX.MX.MN#v0#iq#m3.PB.LD.Mx.4t#nX.TC#uk#v1.Mj#um.Ob.XF.9i.5B#v2.LC#ce.YH.QO#bt.4s.Op#.9.6R.QO#gh.Ob.Ns.Mj#uk.LO#v3.NZ.Ny.Nz.Nz#fs.Tt.4x.Qv#fs#fs.Ny.Ny.Ny.P##fs.Tu#gF.YG#mf.Ny.Ny#fs#fs#fs#fs.3j#fs.Ny.Ny.Ny.P##ip#v4.P#.P#.P#.P#.P#.P##mg.Tk#nV##K.XJ.61#v5.8A.Uq#mg.NY.Ny.63#v6.QB.Tp.Tp.Tp.RE.QC.Pt.NO.Tr.Pk.LW.Ph.Pi.Pi.Pi.Ph.Pz#c#.NU.NE.NE.NU.Sq#v7.Z2.9y#v8#qj#v9.O3.K7.K7.XY.XY.XY.XY.XY.XY.XY.XY#w..K7.RY.3Z.3Z.SX.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.SX.3Z.3Z.RY.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY#ln#w#.6A.7S#wa#wb#wc#wd#kD#we#wf#qp.OE#wg.OV#ex#uI#sM#sM#sM#sM#jL#jL#jL#jL#sM#jL#jL#uI#jM#jM#uI#uI#sM.MP.RY.UM.RY.RY.RY.RY.RY.RY.RY.UM.UM.MV#jL#wh#wh.9Q.9Q#wi#jT.9..6B.6B.6B.6B.7X.9U#wj#o6.1L.2U.2V#cD#wk#wl#wm#uU#uS#uU#uV.7r.7r#uV.7r#wn#wo#wp#wq#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#j##kM#wr#ws#wt#wu#wv#ww#s8#sr#lK#if#j##j#",
-".OV.MZ.OL.MQ.W0.TL.W0.Te.W0.Oz.OC.R0.K7.J#.Jt.K7.Xz.Rd.K8.OL#tG#wx#eT#b5#.H#kN#f4#dR.9d.0U.Oa.Sz.RC.Ph.Pi.Pi.Pi.RC.Ph.Tq.Oi.ND.NE.8p.8B.8B.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.Uq#cp#wy.Qz#br.5B.8B.8B.8B.8B#de.5O#wz#e4#gb#e4#fa.NE.NU.NU.NU.Oa.RR.5B.8B.8B.8B.8B.8B.8B#lS.RB.9n##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#.d.NU.NE.NU.Pd.6S.RC.Ph.Pi.SA.1o.8B#wA.0f.ON.TO#ca#wB#ip#wC#vk.LQ.Mr#wD.Mw#nF.8l.SJ#gh.UF.4s.Uq##L#tr.NZ#hG##W.8a.8a.8a#ct#.d.Z1##W.4s.6R.QO.SJ.Ns#ko.On.NZ.Nz.Nz.Nz.L9.94.Qv.NZ.Ny.Ny.P#.P##fs#fs#m4.WD##P.63#m4.Ny#fs#fs.Ny#fs#fs#fs.Ny#fs.Ny#wE.3j.Nz.Nz.P#.P#.P#.P#.P#.P#.Qx.VK.0U#wF.3i.Tt.9n.8A.5B.63.P#.Qw.PB#v3##A.RD.Tp.Tp.RD.QB.Pp.NM.Py.NH.Ph.RC.Pi.Pi.Ph.RC.4S.NE.NE.LQ.2s.2s#wG#wH#oF#wI#v8#wJ.MM.UP.MN.K7.K7.XY.XY.XY.XY.XY.XY#w..R0.Nm.3Z.3Z.SX.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.RZ.O4.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY#n1#wK#wL#wM.Zj#wN#wO#.x.6k.8Z#wN#wP#wQ.RY.O4.OO#jL#sM#uI#uI#mp#jM#jM#jM#jM#jM#jM#jM#jL#sM#jL#sM#wR.P9.RY.UM.RY.RY.RY.RY.RY.RY.RY.UM.RY.Nc#jL#wh#wh.9Q#wS#wT#wU.6B.7X.6B.6B.6B.9..9U#ad#uN#o6.2U.2V#rj#wV#wW#wX#vL#uU#uV.7r.7r.7r.56.7r#uW#wY#wZ#s6#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#j##j##if#j##w0#pW#w1#w2#if#j##j##j##j##j##j#",
-".OV.MZ.OM.MZ.P3.W1.Te.W0.W0.W0.SX.Q1.O3.Jt.K7.XY.MN.MQ.70.TK#bc#wx#p1#w3#dP#w4.Wc#w5.QH.4s.O#.Pp.Pg.Pi.Pi.Pi.Pi.RC.Pi.8q.O8.NE.ND#.b.8B.8B.8B.8B.8B.8B.5B.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.5B#eq.Mx#...8B.8B.8B.5B##N.9n##R.Ks#w6#e7#gd#ga#w7.2#.NU.NU.Qs.Pd.0Z##M.8B.8B.8B.8B.5B#.c.Kt.8p#bs.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.4s.NE.NE.NU.O#.3C.RC.Pi.Pg.3p#w8#w9.S4#fU#x.#x##ep.PB#xa.Nu#uj.Mw.Mj.8l.Ob#ax.QO.4s.8a.0Z#xb##G.PB#cl.8a#gj#gj#gj#fu##Q#mQ.8p.8a#ct#xc##W.4s.Tl.Op.4H.NY.Nz.P##ce#fs.Ny.NZ.3e.L9.Qv.P#.P##fs.Ny#lQ#xd.9p.Tu.Ny.Ny#fs#fs#fs.Ny#fs#fs.Ny.Ny.3j#wE.3j.P##ce#v4.P#.P#.P#.P#.P#.P##en.Tk.VK.NA.VK.Uq.9n.9n##G.NA#gA.NZ#xe#m2.QD.QG.RD.QD.QC.Po.NJ.3B.LT#c5.K.#ke#jr.K.#xf.4t.Sq.ND#xg#xh#xi#xj.9y#wI.WM#xk.2R.Rd.Rd.X1.XY.K7.K7.XY.XY.XY.XY.K7.R0.UM.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.UN.R0.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.K7.4d.Y.#wO#xl.SY.RY.RY.RY.Q1.J#.Jt.Jt.MH.J#.J#.OK#uI#sM#uI#uI#jL#jL#jL#jL#jL#jL#jL#sM#xm#xm#jL.MX.O4.UM.RY.RY.RY.RY.RY.RY.RY.UM.RY.2R#jL#sM#wh.9Q#ex#xn#xo.9U.7X.6B.6B.9..6B.5o#xp#xq#xr#cD.2V#vI.2V#xs#xt#uS.7r#xu#xu.7r.7r.7r#xu#wo#xv#xw#uY#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#j##j##if#if#kM#lK#j##j##j##j##j##j##j##if#ih",
-".Sa.OL.42.OL.Xz.W0.Qd.W0.W0.OC.R6.OC.J#.O3.O3.R1.KZ.MQ.MX.TK#dN#jb#hb.9a#dP#w4#hc#xx#xy.O#.2q.Pk.XI.Pi.Pi.Pi.Pi.Ph.Ph#e#.NU.NU.ND##V.9n.8B.8B.8B.8B.8B.5B.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n##L.Op#vi#cn.8B.8B.5B.5B#cn#xz.Pd#xA#f.#gb#e4#xB.5O.NU.NU.NE.NE#de.8B.8B.8B.8B.8B##M.8B.J1.6R.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.XF.ND.NE.Pd#fc.RC.Ph.Ph.Ph#gh#xC#xm.S4#wA#xD.3j.4H#uj#ko#un.Ns.1f.SJ#bv.5B#xE.Vy.9s#fw#hH.PB.94#bp.8B#hM.Uq.Uq.0Z#hM#.c#bw##Q.8p.8x#de#bv#gj.2n.P#.Nz.Ny.ZY.Nr.Ny.NZ.2p.Mp.Nq.Tu#fs.Ny.P#.NZ#xF#aF.3f.Ny#fs#fs#fs#fs#fs#fs.Ny#fs.Ny.Ny.Ny.3j#wE#xG.Nz.Nz.P#.P#.P#.P#.P#.P##ce#.Y.Tt.Pb#xH#gF#nU.0Z.9m#v4.Nz.P##xI#xJ.QB.To.RD.QD.QE.RF.4S#xK#l2#xL#xM#xN#xO#xP#xQ#xR#xS#xT#xU#xV#oE#xV#xW#v8#xX.R1.MN.Rd.X1.0f.XY.K7.K7.XY.XY.XY.K7.J#.UN#xY.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.UM.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.R0.UM.UN.RY.RY.RY.RY#wQ.OE.Jt.Jt.JG.JG.Jt.Jt#wQ.Ql.X2#sM#sM#jL#sM#jL#sM#sM#xm#sM#xm#xm.9Q.MO.R0.UM.UM.UM.RY.RY.RY.RY.RY.UM.UM.RY.OO#jL#wh#wh#fI.9P#xZ#xo.9U.9..6B.7X.6B.7U#xo#x0#x1#x2#xr.2W#x3#x4#x5#tQ#x6.7r#x7#x8#x9#y..7r#y.#y##ya#yb#yc#rr#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#j##j##j##j##j##j##j##j##sq#if#lK#ig#j##yd",
-".OV.OL.P9.OL.MN.R6.9F.SX.SX.SX.SX.SX.7G.O3.O3.R1.KZ.MQ.MX.TK#dN#ye#al#yf.ZS##v.ZK#yg#.b.NE#c#.Pz.Ph.Pi.Pi.Pi.Pi.Pi.Pz##H.Pd.NE.NE.5M.Qy.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.5B#aG.96#p7.8B.8B.8B.5B.8B#.W.RA#yh#f.#ge#e5#yi.5O.NU.NU.NE.ND.Qy.8B.8B.8B.8B.8B.8B#cn.2s##O#cn.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n.RR.ND.NE.NE.17.RC.Ph.SA.WG.4r#yj#oa#yk##F.PB#c3#uj.Vw#yl.SJ.QP.WC#.##.e#.Y#aC#xb#ym.NZ#ce.YL#.e#xF#aF.9n.Uq.Uq.Uq.0Z.Uq.8B.4r.64.4J#gi#kk.Nz.Nz.NA.Nr.9q#ip.Ny#jz#yn.JZ.TA.PB.Ny#fs.Qv.WD#vn##G.NZ#fs#fs.Ny.Ny#fs.Ny#fs#fs.Ny#fs.Ny#fs.Ny.3j.3j.P##ce.P#.P#.P#.P#.P#.P#.P##mg#eo.NY#cp#dU.Tk#br.9n.WS.NY.Nz#yo#yp.Pr.RD.QD.QB.Po.NL.NQ.2.#yq#yr#ys.Z3#yt#yt#yt#oF#gm#wI#yu#yu#yu#yv#yw#yx.OD.MN.X1.0f.0f.XY.K7.K7.K7.XY.XY.K7.O4.UN.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z.RY.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.1J.XY.K7.RY.UN.UM.RY.RY#wQ.VV.Jt.Jt.Jt.W9.O5.JG.Jt.W9.VV.Lz.OO#sM#sM#sM#xm#xm#xm#xm#xm#jL.TO.Nc.O4.RY.UM.UM.RY.RY.RY.RY.RY.RY.UM.UN.O4.OV#sM#sM#sM.9Q.Nb#yy#jT.9U.6B.6B.6B.7S#jT#yz#yA#.E#yB#yC.2V#x3#yD#yE#yF#yG#yH#yI#yJ#yK#yK#yL#yM#yN#yO#ba#f2#yP#yQ#vN#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#ih#ig#lK#yQ#om#qJ#ru#ba",
-".6D.42.OM.OL.R1.OC#yR.SX.SX.SX.Oz.R6.SX.Q1.XA.R1.KZ.MQ.MX.TK#yS#yT#al#dO#yU.U9#yV#yW.ND.NU.Ps.Pz.Pi.Pi.Pi.Pi.Pi.Ph.Pz.L2.Ur.NU.NU.NE.NU#de.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.PT#bw##M.8B.8B.5B#aH#bw.2##yX#yY#gd#f.#yZ.RA.NE.NU.NU.Oa##O#bs.8B.8B.8B.8B.8B#cn.RR.Oi.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B##Q.Pd.Oa.Oi.NT.Ph.Ph.Pz##I#y0#y1#pJ#hv#y2.Nr#nF#y3.XF#.S.6R.Vy#hH#.e#.e.0U#nU#eq.Qv.NZ.PA.Tk.0U#.e.Tk#eq#cp.9q.Vy#dd#.a#dU.0U.Tk.8F#aE.P#.Qw.NA##G#aF#y4.NZ.NZ.2C.LQ#ui.RQ.PB.Ny.P#.Nz.9p##G.Qv#fs#fs#fs.Ny.Ny#fs.Ny#fs#fs.Ny#fs#fs#fs#fs.3j.3j#gA#jz.P#.P#.P#.P#.P#.P#.P#.3i#aE#ce#bl#xH.YL#bp#hM#jh.P#.P#.Qv.3W#y5#y6.QB.Pu.NO.NJ.3B.Ph#c2#y7#y8#y9.9y#yu.8R.8R.8R.8R#yu#z.#z##za#zb.Lv.MN.X1.0f.XY.K7.K7.K7.XY.XY.K7.Jt.RY.3Z.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.UN.J#.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.1J.1J.1J.1J.1J.K7.K7.K7.S4.K7.RY.UM.UM.RY.O4.Jt.Jt.K7.K7.O5.O3.O3.O5.O3.O3.O3.P3#wQ.UZ#jL#sM#sM#sM#xm#xm.9Q.MO.XY.RY.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.UM.UN.S4.TO#sM#jL#uI#uI.9Q#zc#zd#ze.7X#rg.7S#zf#zg#zh#zi#zj.9Q#zk#zl#zm#zn#zo#zp#zq#zr#yK#yK#ba#cR#ba#zs#yO#ba#ba#cR#zt#zu#if#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##sq#if#if#lK#ig#lK#rr#zv#zw#zx#rv#f2#qI#f2#f2",
-".TK.42.OL.P9.K7.OC.R6.Oz.SX.SX.SX.OA.Oz.OC.R0.R1.KZ.MQ.MX.TK#bc.6G.72#zy#dP#eU#zz.9s.Ol.Oq.Pm.RC.Pi.Pi.Pi.Ph.RC.Ph.Tr.WF.NT.NU.NU.NU.Oa.Pd.8p#cn.Pc.8B.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#zA##W.8B.8B.8B.8B##M.5B.7o.Om#zB#e4#e4#zC.Om.NE.NU.NU.NE.RA.9n.8B.8B.8B.8B.8B.Pc.QO#zD.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B#.b.5M.NE.NU.Pp.RC.RC.NS#.Z#zE#oa#zF#jz.2C#nF#gh.6R.4s#fu#dU#iF#.e#.e.9q#m3.3j.NZ.Ny.PA.Tk.0U#.e#.e#.e.YL.YL#cm#cm.YL#.e.Tk#oN#ce.P#.P#.Nz.2n#ul.8B.2n.Ny.8F.SJ.Mw.96#vk.63.NZ.Ny##G#jy.Qv#fs#fs#fs.Ny#fs#fs#fs#fs#fs.Ny#fs.Ny.Ny#fs#fs.Ny#vo.P#.Nz.P#.P#.P#.P#.P#.P#.P#.Nz.P##zG.4q.3A#.e#cp.9n.9n.WC.Nr#zH#zI.VF#zJ.J3#zK#.m.L0.Pi.Ph.Pz.Pv#zL#zM.9y#yu.8R.8R#yu#z##z##zN#zO.Rd.Qp.X1.0f.XY.XY.K7.K7.K7.XY.XY.K7.J##nh.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.UM.R0.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.1J.1J.1J.1J.1J.K7.K7.K7.K7.S4.S4.S4.S4.S4#zP#zP#wQ.UM.RY.P3.OE.W9.Jt.W9.O5.O5.OF.O5.O3.K7.O5.O3.O3.O3.VV.La#jL#xm#sM.9Q.MO.T0.O4.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.RY.21#jL#sM#uI#jL#uI#hX#hX.9Q#a0#zQ#zR#zS#zT#zU#zi#a0#fI#wh#hX#zV#zW#zX#zY.XV#yI#yK#yK#ba#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#sm#ie#vN#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#lK#lK#sk#zZ#z0#z1#z2#jX#yO#f1#f2#z3#ha#sm#z4#z5",
-".TK.TK.P9.MO.P2.SX.No.Ha.Oz.SX.SX.No.No.SX.P2.R1.KZ.MQ.MX.TK#z6.6G.72#z7#.H#b7#z8.8a.ND.WG.Pk.Ph.Pi.Ph.RC.NH.18#ti.NV.NU.NU.NE.NU.NU.NU.NE.WR.Qy.Z1#hM.8B#.f.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#bt.00.8B.8B.8B.8B.8B.8B.5C#z9.F8#f.#e6#A..Pw.NU.NU.NU.ND.NU#gj##M.8B.8B.8B.8B.8B.8a.Pe.0Z.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#A#.53.ND.Oi#fd#pl.Ph.RC.1f#Aa#Ab#Ac.QI.QP#gh.Op##W#bv.4J#.e#mh#.e#.##m3#jz.NZ.Ny.NZ.3f.8F.0U.0U.0U.0U.0U.0U.0U.0U#kn.0U.3k#fs.NY.NA.XJ#dd#br#fw#br#jy.94#aC.Uq.8l.Mj#nX#Ad.P##Ae.WE#fs.Ny#fs#fs#fs#fs#fs#fs.Ny#fs#fs#fs#fs#fs#fs.Ny.Ny#fs#fs.P#.Nz.P#.P#.P#.P#.P#.P#.P#.P#.P#.63.WE.0U.0U#er.8B.5B#.c.YL#lj.14#Af#fg#Ag#Ah#Ai.LX#ke#c5#iz#kd#Aj#Ak.XQ#wI#z.#Al#oC#Am#An#Ao.JC.Xz.0f.Nf.XY.K7.K7.K7.XY.XY.1J.K7.Nn#ra.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z#wQ.K7.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.XY.1J.1J.1J.1J.1J.1J.K7.K7.K7.S4.S4.S4.S4.S4.S4#zP#zP#zP#Ap#Ap#Ap#Ap#Aq#Aq.VV.RY.J#.OE.Jt.K7.Jt.OF.O3.OF.O5.OF.OF.O3.OF.O5.O5.O3.O3#wQ.Lo#wh.MX.UO#wN#oY#oY#Ar.8Z#As#Ar.8Z.0G.UM.RY.RY.RY.RY.UM.UN.O4.MX#jL#sM#uI#uI#uI#jL#hX#jL#jL#hX#fI#a0.85.85#At.9Q#jL#wh#hX.TO#Au#Av#Aw#Ax#Ay#Az#ba#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#b2#AA#g9#ih#ig#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##ih#ig#rr#AB#AC#AD#g7#ba#f1#f2#zt#cR#cR#z4#ww#wq#AE#AF#ig",
-".6D.6D.TN.OF.Oz.R6.No.No.Ha.OC.SX.OC.SX.SX.R6.Jt.L#.MQ.MX.TK.S6#jb#al#dO#cT#pf#AG.7o.NU.99.RC.Ph.Ph.RC.Pp.Tq.Pd.Pd.NU.NU.NU.98.NU.NU.NU.NU.NE.ND.5M.NU.RR.Qy#bw.Pc.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M#bw##V.Uq##N.8B.8B.8B.Pc#.b.2##AH#gd#e7#AI#q1.Pd.NU.NU.NE.Oa#.d.8B.8B.8B.8B.8B.5B.Uq#zD.8p.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#co.98.NE.ND.QA.RC.SA#y7#AJ#AK#AL.Tt.QO#lS#xc.8a.8p.ZW#.e#gB.0U.2n#jz.NZ.Ny.NZ.NZ.NZ.P#.3i.PA#mg#jy#jy.3k.63#fl.94.3f.NZ.Tu#ce#xy#aF#br#fw#fw#ul.94#tr.0U.9q.4s.SJ.Vw.NB#ce#oN.3f.Ny#fs#fs.Ny.Ny#fs.Ny.Ny#fs#fs#fs.Ny.Ny.Ny#fs.Ny.Ny#fs#fs#fs.Nz#gA.P#.P#.P#.P#.P#.P#.P#.P###G.QH#fl#ju.0U.SK.9n#bu.YL.LC.YV.NX#AM#AN.4O#AO#AP#AQ#AQ#AR#AS#AT#AU#AU#kb#AV.SE#AW.XA.Je.K7.0f.XY.XY.K7.K7.K7.XY.XY.XY.K7.VV.UM.3Z.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.KT.UN.R0.XY.XY.XY.XY.XY.XY.XY.1J.1J.1J.1J.1J.1J.K7.K7.K7.S4.S4.S4.S4.S4.S4#zP#zP#zP#Ap#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq.Ql.Jt.Jt.K7.K7.OF.O5.O5.OF.K7.OF.O5.OF.K7.O5.OF.JG.JG.W9.Jt.VV.R1.Ty.6k#sP#AX.ha#AY#AZ#A0#A1#A2#A3#A4.Nm.6k.Nm.RY.UM.P2.T0.OL#sM#sM#uI#uI#uI#uI#jL#hX#uI#hX#hX#hX#wh.Q8#jL#jL#hX#Au#A5#hX#A6#A7#A8#A9#AA#yK#cR#cR#cR#cR#cR#cR#cR#cR#b2#cR#AA#B.#B##B##f2#ib#sn#z0#if#ig#lK#if#sq#j##j##j##j##j##j##j##j##if#lK#ig#Ba#h.#Bb#cR#f2#f1#cR#cR#cR#cR#cR#cR#f1#sl#kM#vN#if#if#j#",
-".R1.Qo.WA.R6.7G.No.No.No.No.No.OC.SX.R6.SX.9L.UW.KZ.MQ.MX.TK.Wz.T.#hb.9a#am#Bc.0U.O#.Pd#c4.RC.RC.Ph.RG.NE.Pd.NU.NU.O8.VB.Qs.Qs.NE.Oa.ND.ND.O#.ND.NE.NE.NE.ND.NU#zA##Q.Pc.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.7o.Z1.8B.8B.8B.8B.8B#.d.NU#aI#sy#e6#gb#l#.2#.NU.NU.NU.ND.UF.Pc.8B.8B.8B.8B.8B.5B.Oi.4s.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M#aH#zA.ND.Pd.NF.Pz.Pz#j3#xc#Bd.6O#dd.6R#de#co#..#aH.2n.0U#.e#ug#Be.63.NZ.NZ.Ny.NZ.NZ.NZ.NZ.NZ.NZ.NZ.NZ.NZ.Tu.PB.PB.3j.XJ#nV.8u#tq#fp#cm#Bf#Bg##L#ce.94.Tk.Tk.5B.6R.9p.P###G.14.Ny.Ny.Ny.Ny.Ny#fs#fs.Ny.Ny#fs.Ny#fs#fs#fs#fs.Ny#fs#fs#fs#fs#fs.Nz#hK.P#.P#.P#.P#.P#.P#.Pa#iK.VK#fs.Ny.63#.e.0W#.Z.Uq.0U#Bh#bs.Qv#Bi#Bj#Bk#Bl#Bm#Bn#Bo#Bp#Bq#Br#Bs#Bt#Bu.SQ.Hp.Nn.XY.XY.XY.K7.K7.K7.K7.XY.XY.XY.K7.R0.RY#ra.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z#oa.K7.XY.1J.1J.1J.1J.1J.K7.K7.S4.S4.S4.S4.S4.S4.S4.S4#zP#zP#Ap#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq.O3.Jt.Jt.K7.K7.OF.O5.K7.O5.W9.OF.Ql.W9.OF.K7.O3.W9.W9.W9.J#.O4.RY.8Z#Bv#Bw#Bx.35#ev##4#h8#h8#h8#h8#ev#By#Bz#BA#As.Sb.O4.7w#jL#sM#uI#sM#sM#uI#sM#jL#jL#uI#jL#hX#jL#hX#wh#hX#hX#BB#hX#BC#BD#BE#BF#BG#BH#f1#yK#cR#cR#cR#cR#cR#cR#cR#b2#cR#f2#B.#BI#BJ#BK#BL#BM#BN#BO#B.#BP#rw#om#yQ#lK#ig#lK#ih#if#ih#ih#ih#ig#if#BQ#ha#BR#f2#zt#cR#BS#cR#cR#cR#cR#cR#cR#cR#f2#g9#mH#if#j##j##j#",
-".No.No.No.OC.No.Ha.No.Ha.No.OC.7G.Yb.Yb.Y..9L.SY.Um.Ln.70#rz#lr#eS#eT##u#f3#BT.9q.NC.Pd##B.Pz.Ph.9j.Pd.NU.NE.NU.NE.Oa#BU#BV#BW#BX##Q.6R.8p.4J#bv.NE.ND.NU.NU.NE.ND.NU.9r#bs.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.Pc#xz.1f.8B.8B.8B.8B##N##Q.NU.2##BY#f.#e4#xB.RA.NU.NU.NU.5M.Ns.8B.8B.8B.8B.8B.8B.8B.Mw.XF#.f.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##Q.NU.NU.51.Pk.Pz#.Q.8p#BZ.PB#bx.Z1#..#fu#fu.0X#.e.0U#.e#gF#ul#fw#.a.Qx.PB.PB.NZ.NZ.NZ.NZ.NZ.NZ.PB.PB#uh.3g.Tk.9s#aF#B0#B1#wC.3g#Bh.5E#br#bl.Ny.XJ.0U#.e#fe.9p.NA.Tt.3g#vo.Qv.NZ.NZ.NZ.Ny.Ny#fs#fs#fs.Ny.Ny.Ny#fs#fs#fs#fs#fs#fs.Ny#fs#fs#jz#gA.P#.P#.P#.P#.P#.P#.Qw.94.YL.Ny.Ny.94#.e#fp.5B.Uq#kk.4p.00.3j#B2.Xe#B3#B4.9a#B5.T2.Ln.7w.21.Je#gI.P2.K7.XY.XY.K7.K7.K7.K7.XY.XY.XY.XY.S4.K7.O4.UN.3Z.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z.O4.K7.S4.K7.S4.S4.S4.S4.S4.S4#zP#zP#zP#Ap#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Ap#Aq#Aq.O3.Jt.Jt.O5.Jt.W9.Ql.JG.Ql.OF.W9.OF.Ql.W9.W9.W9.Ql.JG.K7.K7.O4#B6.P0#B7#B8#h8#eC.9I.9I.9I.9I.9I.9I.9I.9I.7z#h8#B9#C.#C##jM#xm#sM#sM#uI#sM#uI#uI#jL#jL#uI#hX#jL#jL#jL#jL#hX#Au.TO#Ca#Cb#xt#Cc#uW#Cd#wn#Ce#Cf#cR#cR#cR#cR#cR#cR#BS#cR#Cg#Ch#Ci#Cj#Ck#Cl.6b#Cm#iU#Cn#Co#Cp#Cq#Cr#ba#yP#z2#Cs#om#Ct#Ba#Cu#Cv#BQ#Cs#ic#Cw#f2#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#AD#sk#vN#j##j##j#",
-".OB.Je.Nm.No.SY.OC.OC.OC.R6.W7.Yb.W7.OC.SX.R6.UW.P3.L#.70.Yc.Q8#u8#eT#am#cS#Cx#ul.Nt.Pd.RG.SA#Cy.Oi.NU.2B.NU.NE.NE#Cz#CA#CB#CC#CD#gi#xH#.e#hH#.e.8u##O.Ol.NE.NE.NU.NE.ND#.0#.c.Pc.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##W.7o.5B.8B.8B.8B.8B.Uq.WR.5O#CE#nD#f.#CF.5O.NU.NU.NU.Oa.7o.5B.8B.8B.8B.8B.8B#.f.Ns.Vw.Pc.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M#aH.5C.ND.NU.Po.SA.6T.0Z#hL#zG#fu#..#gj.0Z.Uq.ZW#.e#.e#gB#.e.0V##L#Bg.8u#er#kk#aE#lQ.Ny#fs#ip.3h.WD.Tk.0W#xb#.f#fw#er#tr#Bh#tr.4x.4x.WD#aF.4q.NZ.PA.Tk#kn.VK.Qw.Tt.Nq.Ny#bq.2C#jz.NZ.NZ.NZ.Ny.Ny#fs#fs#fs.Ny#fs#fs#fs#fs#fs#fs#fs#fs.Ny#fs.Nz.P#.P#.P#.P#.P#.P#.P#.P##fx#oN#m4.Ny#ip#.Y#dU#cn.Pc.LL.9p.Oq#mg#CG#st#rB##v#CH#CI#tb.MZ#n7.XY.K7.R0.K7.K7.K7.K7.K7.XY.XY.XY.XY.XY.XY.K7.VV.UM.3Z.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z#nh#CJ.S4.S4#zP#zP#zP#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Ap#Ap#Ap#Ap#Ap#Ap#Aq.T0#p9.W9.K7.Ql.Jt.O3.Ql.W9.Ql.Jt.Jt.Jt.Jt.OF.Ql.W9.K7.Ql.Jt#CK#CL#wN#CM#CN.7z#CO.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.35#CP#CQ#CR#jM#uI#sM#uI#uI#sM#uI#uI#jL#jL#jL#hX#jL#jL#jL.2Q#BB#CS#CT#CU#CV.7r.56.7r.7r#xu#CW#Az#ba#cR#cR#cR#cR#BS#zt#pa#CX#CY#CZ#qX.Ne.Nd.0H.1B.1B.1B.Qc#kI#oj#C0#cR#zt#f1#f1#ba#nt#BR#qH#nt#ba#f1#f1#cR#BS#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#BS#cQ#rw#lK#lK#if#j#",
-".7G.SY.SY.SY.R6.R6.R6.7G.OC.R6.R6.7G.SY.Oz.No.P2.Q1.R1.MX.TN.2Q.6F.9##cS#C1#C2.Z1.Oq.Pd#c#.3p.Oa.NU.2B.NU.NE.NE.9r#C3#aH#aH#aH.5B#xE.0X.4q#.e#.e.VK#xF#aG.PT.NE.NE.NU.NU.ND.Om#bv.Pc.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B#mQ.PL.Uq.8B.8B.8B.8B.5B.7o.2##C4#gd#e5#C5.5O.NU.NU.NU.NE.O..Uq.8B.8B.8B.8B.8B.Pc.Tl#C6.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8p.NU.Pd.1q.SA.Pv.8B.Nz.0U#co#.c.Uq.Uq#cn#dU.0U#.e#.e#.e#.e#dU#B0#C7#C7#ul#C8.8u#bl#bp#B0#C9#cn#fw#bl#dc#hH#tp#D.#m3#D##Da#tr.4x.4x#tp.Vy.NZ.PA.Tk#av.NA.WD.3k#rT#.#.6R.1f#db.PB.Ny.NZ.NZ.NZ.Ny.Ny#fs#fs#fs.Ny#fs.Ny#fs.Ny.Ny#fs#fs.Nz.Nz.P#.P#.P#.P#.P#.P#.Pa.NA#er.PA.Ny.Ny#fs#ji.Tk#vn.0X.Qv#bp.NV.9l#Db#Dc#rB#Dd#C1#De#t5##k#n7.Xz.K7.R0.K7.XY.XY.XY.XY.XY.XY.XY.XY.S4.W9#oa#ra.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z#oa#zP#Aq#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq.T0.R1.W9.Jt.W9.S4.Jt.Ql.OF.Jt.Jt#p9.Nc.Nc.T0#t#.W9.4..5a.Zc.Z5#Df#Dg#Dh#Di#eF.7z.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.35#Dj#Dk#ex#uI#sM#uI#sM#sM#sM#uI#jL#jL#uI#jL#uI#uI#Au#ge#Dl#Dm#Dn.7r.56.7r#Do.7r#Dp#x7#Dq#yK#ba#cR#cR#cR#BS#f2#Dr#Ds#Dt.IW#nh.UM#Du#Du#Du#Du#Du#Du#Du.HI#Dv#f2#b2#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#b2#cR#cQ#Dw#Dx#if#ig",
-".SY.SY.SY#Dy.0p.7G.7G.SY.SY.R6.R6.R6.R6.Oz.No.SY.Nl.O5.MO.Yc.Si#ak##t#eT.5K#Dz##O.ND.NU#nA.NT.Oi.NE.NU.NE.NE##W.8B#bs#p7.8B.8B.8B.5B.9n.5B.9p#.e.0U#.e#.Y#DA.8y.PL.ND.NU.NU.NE.Oa.1f.8A.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B#bu.O.#mQ##M.8B.8B.8B.8B##O.NU#DB#DC#e5#DD.RA.NE.NU.NU.ND.O8##Q##M.8B.8B.8B.8B#bs.4s.PL.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.5B.TD.NU.XH.Pz.L0#sC.NZ.NB##Q.Uq.9n#aG.Qr#iF#.e#.e#.e#.e#gB#.e.Tk#gF##L#fw#DE##L#tp#DE#DF#DG#DH#DI#tr#DJ.3g#D##DK#br#fe#.a#wC#DJ#tr#bl#gi.Qv.XJ#m7.P#.Rz##G.NZ.4H.Uq#co##W#ct.63.PB.Ny.NZ.NZ.NZ.Ny.Ny.Ny.Ny#fs#fs#fs#fs.Ny.Ny#fs.Ny.P##jz.P#.P#.P#.P#.P#.P#.Qw.L9#aw#ce#fs.Ny.Ny.Tt.0U#fe.9l.3f.4r.Pd.O7.PI.6K#DL#DM#C1#CI#t5.OM.2R.0f.XY.K7.XY.XY.XY.XY.XY.XY.XY.K7.W9.RY.UN.3Z.3Z.SX.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.UN.VV#zP#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq.T0.T0.T0.T0.T0.R1.W9.Lq.W9.W9#t#.JG.Jt.Jt#p9.ON#jL#DN#DN#hX.M0#DO#DP#DQ#DR#DS#fO#ev#h8#bH.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I#DT.6e#DU#ex#uI#uI#uI#sM#uI#wh#jL#uI#jL#jL#jL#hX#Au#DV#DW#Cc#s0.56.7r#Do.7r#Dp#DX#x9#Ay#yK#ba#ba#cR#cR#BS#zt#DY#DZ.Km.3Z#qr#qr#qr#qr#qr#qr#qr#qr#qr#qr.Km#D0#ib#BS#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#zt#zt#zt#zt#zt#zt#zt#B##ba#ic#zw",
-".0p#Dy#D1.0p.0p.W7.OC.SY.SY.R6.R6.R6.R6.Oz.SY.SY.WA.OD.Rd.MX.TK#dN#yT.6G#D2.JP#.0.ND.NE.NE.NU.NE.NU.NE.5M#.b.8B.8B.8B.8B.8B.8B.8B.8B.8B.8w#cn#gF#.e#mh#.e#.e#eq.8B.53.ND.O8.NU.NE.ND#.X.8p.Pc.5B.5B.8B.8B.8B.8B.8B.8B.8B.5B#ko.Z1#p7.8B.8B.8B.8B.XF.5M.ND#D3#D4#fa.Qs.NE.NU.NU.NE.NE#ar.8B.8B.8B.8B.8B##N#de.Pd.9n.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#bt.NU#nA.RC.Pi#c3.PB.0X#.c.9n.5B.9n#es.YL#.e#.e#.e#.e#.e#gB#.e.2n#xE.YG#aC.0U.Tk#D5#D6#D7#D8#D9#E.#E#.4x#tr#Ea.0X#DK#aF#DK#Eb#Bh#y4.9q.Pb#ip.Nz#eo#oN#lj.Nr#.f.Uq.Uq#fu##W#aH#tr.PB.NZ.NZ.NZ.NZ.NZ.NZ.Ny.Ny.Ny.Ny#fs#fs.Ny#fs#fs#fs.Nz.P#.P#.P#.P#.P#.P#.P#.4p.4q.Qv.Ny.Ny.Ny.63.0U#hG.Nq.2p#nT.4s#bq#Ec.ZK#rB#Ed#C1#CI#tb.MZ.21.XY.1J.5i.1J.1J.1J.XY.XY.S4.W9.RY.UN.3Z.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.UM.W9#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.UZ.R1.O3.Jt.W9.W9.Ql.Jt.Jt.S4.T1#hX#xm#Ee#Ee#Ee#Ef#Eg#Eh#Ei#Ej##4.6e#eC.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I#bI.9I#bH.7z.35.35.6e#Ek.85#sM#sM#jL#jL#uI#sM#sM#n8#hX#n8.42.42#El#Em#Dn.7r#uV.7r#uV.7r.7r#En#Eo#yK#yK#ba#cR#cR#cR#cR#cR#nt#Ep.Km#qr#Eq#Eq#Eq#Eq#Eq#Er#Er#Er#Er#Er#Er#xY#Es#Et#B##BS#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#zt#zt#cR#nt#g7#Eu#Ev#Ew#sn#Ex#Ey#Ez#AD#ru#yP#BR#nt",
-"#Dy#D1#Dy.0p#D1.7G.7G.SY.R6.R6.SY.R6.R6.No.SY.UW.P5.MK.R1.Yc.TK#bc#jb#b3.ZN.3k.XF.ND.98.NU.NU.NU.NU.ND.Oi#hM.5B.8B#lN#EA#EB#lN#vn#lN.8v#EB.8B#.f#fp#.e#mh#.e.YL#fv#cn.TD.ND.NE.NU.NU.NE.NE.Qy.5B.8B.5B.8B.8B.8B.8B.8B.8B.8B#j6.6R#bs.8B.8B.8B.Pc.6R.ND.RA#EC.Rc#A..6X.ND.NU.NU.NE.ND#.9.Pc.8B.8B.8B.8B.8B#mQ.Oi.0Z.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##N#hM.2#.8q.Pi.Pi.RS.NZ#vn#bu.5B.5B.5B#.a#iF#.e#.e#.e#.e#.e#gB#.e#ED#ul#xF#.e.Tk#EE#EF#EG#EH#EI#EJ#EK.JO#EL.4x#tr.3k#EM#EN#EO#ul#hL#.a.9q.NZ#fs#mg.WE#EP.3f.0U.0X.8A.9n.Uq#gj.Z1.9n#jy.PB.NZ.Ny.NZ.NZ.NZ.NZ.NZ.Ny.Ny.Ny#fs#fs#fs#fs#fs.Nz.P#.P#.P#.P#.P##gA.NA#cs#iF.NZ.Ny.Ny.Ny.63#aC#hL#jz#oN.YL#rF#nU#EQ#ER#ES#qM#C1#B5#dN.MY.X1.0f.S4.W9.K7.S4.S4.S4.W9.RY.UN.3Z.RY.UN.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z.RY#zP#Aq#Aq#Aq#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.UZ.S4.Ql.K7.K7.W9.Jt.Jt.W9.Nc.TO#wR#Ee#Ee#ET#xm#t5#EU#EV#EW#EX#EY#ev.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.35.9I.W3#EZ#E0#.v.7z#Dj#E1.32.M0#t#.Lq#pJ.VV.MV.1B#E2#iU#BB#E3#E4#Dm#E5.56.7r.7r#uV.7r.56#E6#yJ#E7#ba#cR#cR#cR#cR#cR#cR#f2#E8#Er#ut#Eq#Er#Er#Er#Er#Er#Er#Eq#Eq#Eq#Eq#Eq.UN.K1#E9#F.#F##B##B##B##f2#f2#f2#f2#f2#f2#cR#cR#cR#Cw#BR#Eu#Ew#z2#Fa#Fb#Fc#z5#z5#z5#z5#z5#z5#z5#z5#z5#Fd#h#",
-"#Fe.0p#Dy#Fe#Dy.7G.7G.R6.SY.R6.SY.R6.SY.Oz.SY.P2.Q1.VU.OD.K8#rz.6E#b3#bc#Ff#aE#.S.Oq.NU.NU.NU.NU.NU.5M##R.5B.4r#cn#Fg#Fh#Fi#Fj#Fk#Fl#Fm#Fn#Fo#Fp#Fq##K#.e#mh#mh#.e#fp##P.Ns.O#.NE.NU.NU.NE.ND.7o.8p.8B.5B.8B.8B.8B.8B.8B#cn.Ns.XF.Pc.8B.8B.8B.8B.Z1.ND.5O#Fr#D4#Fs.5N.Oq.2B.NU.NU#Ft.XF.8B.8B.8B.8B.8B.8B#.c.NU#fu.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.TD.Oq.YI.Pi#Fu#ip##P.9n.8B.9n.Pc#er#mh#.e#.e#.e#.e#.e#.e#er#xE#hf.YL.Tk#Fv#Fw#Fx#Fy#Fz#FA#FB#FC#FD#FE#FF.4x#tr#jy#bp.Tk#FG#tq#bp#gi#ce#fx.WD#rS.3f#hH.0U#.e.Vy.8A.9n.9n#.c.8a.8p#eq#jz.PB.NZ.Ny.NZ.NZ.NZ.NZ.NZ.Ny.Ny.Ny.Ny#fs.Nz#iK.Nz.P#.P#.P#.P#.Qw.3i.0X.XJ.Ny.Ny.Ny.Ny#m7.8F.Nz.14.0U#.e#bx.ZZ#FH#FI#hc#bf#FJ#vX#bc.0H.Lo.T0.S4#zP.S4#zP#CJ.RY#ra.KT.UN.VV.O4.UN.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.UN.VV#zP#Ap#Ap#Ap#Ap#Ap#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.XY.O3.K7.K7.K7.Jt.W9.T0.VX#DN#Ee#Ee#Ee#Ee#ET#xm#Ee#DN.OK#DO#FK#FL#vz#bI.6e#bH.9I.35.9I.9I.9I.9I.9I.9I.9I#DT.35#eC#ev#FM#FN#FO#eB#CO#FP.J##qr#qr#qr#qr#qr#qr.30#Eq#FQ#FR#FS#FT#FU#tT#uV.7r.7r.56.7r#y.#FV#ba#yK#ba#cR#cR#cR#b2#cR#zt#f2#FW#FX.Km#qr#Eq#Eq#Eq#Eq#Eq#Eq#Eq#Eq.UM.UM.UM.UM.UM.UN.IW#FY#FZ#F0#ns#F1#F2#F3#F3#F4#F5#mE#F6#sl#Fb#zw#F7#om#yd#BQ#F8#F8#yd#yd#yd#F9#s4#G.#F7#G##id#Ga#Gb#z5",
-".0p.0p.P2#Dy.0p.SY.7G.OC.R6.R6.R6.R6.SY.Oz.Oz.No.OB.VU.OD.X1#rz#t4.6F.Si.91#ip#Gc.O#.O8.NU.NU.NU.NU.ND.XF.4r#qQ#Gd#gb#e5#nD#f.#f.#f.#e5#e9#Ge#Gf#Gg#iq#Gh#.e#gB#gB#.e.RS.8B.Ns.ND.NE.NU.NU.NU.Oa.O8.4s.Pc.8B.8B.5B.8B.8B#cn#gh##O#cn.8B.8B.8B##M#co.NE.RA#Gi#f.#yY#Gj.Nt.NE.NU.NU.Oa##O.8B.8B.8B.8B.8B.8B.Uq.Oi.8x##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.Tl.2B.RF.L2.2p.94#Gk.5B.8B.5B#vn.Tk#.e#.e#.e#.e#.e.0U#.e#nT#tq.Tk.0U#Gl#Gm#Gn#Go#Gp#Gq#Gr#Gs#Gt#FB#Gu#Gv#E.#E##DJ#jy#B0#hH#Gw#br#ul.9q.Nr.Ny.LC.VK.Tk.0U#.e#.e.ZW.64#.Z.9n.Uq#..#bv##P.0U#fs.PB.NZ.Ny.NZ.NZ.NZ.NZ.NZ.NZ.Ny.Ny.Ny#fs.Nz.Nz.Nz.Nz.Nz.NY.8F#bl.Ny#fs.Ny.Ny#fs.3k.LL.Ny#kk#hH.Tk.64#oN.JO#Gx#Gy#Gz#GA#GB#wh.Sa#GC.UZ#Aq#zP.VV#oa.UN.3Z.3Z.O4#t#.O4.Q0.Oz.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z#oa.W9#Aq#Aq#Aq#Aq#Aq#Aq#Aq#Aq.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0#Aq.Nf.O3.Jt.K7.W9.R0.O3#e5#jL#ET#qV#ET#GD#Ee#ET#Ee#ET#Ee#sM.Nc.R0#CK#GE.Ne#cG#GF#GG#CO#GH#bH.9I.9I.9I.9I.9I.9I#bH#GH#fO#GI#gb.4e.M0#GJ#GK#rb#GL#Eq#Eq#Eq#Eq#Eq#Eq#qr.TM#GM#GN#GO#Cd.7r#uV.7r.56#yG#Dp#GP#GQ#E7#ba#cR#cR#cR#zt#f2#F##f2#mF#GR#GS#GT.RZ.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.HK.Ln.7v.Rk#GU#Cm#e7#GV#GW#GX#GY#GZ#sk#if#ih#ih#if#if#ih#ih#if#ih#ih#ih#ih#if#if#if#if#sk#j##rr#G0",
-"#Dy.0p.No.P2.7G.P2.No.OB.P2.P2.No.P2.No.Oz.Oz.No.UM.P5.VU.OF.MW.2Q.Xw#rz.40#ip#G1#xg#nX.NU.NU.NU.NE.Oa#c8#G2#G3#Ab#gb#ge#ge#ge#ge#ge#ge#gb.0H#G4#G5#G6#G7#G8#.e#gB#.e#.e#bl#aH.5C.ND.NE.NU.NU.NU.NE.NE##O#de.8B.8B.5B.8B.Pc.6R#ko#.f.8B.8B.8B.8B#gj.NU.2#.3n#gb#ga#ei.NC.ND.NU.NU.NE.7o.5B.8B.8B.8B.8B.8B.9n.Om.Z1#p7.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##W.NU.L7.8d.LJ.Nr.Pc.5B.8B.8w#th.3A#.e#.e#.e#.e#.e.0U#xy#C8.4q#.e#G9#H.#H##Ha#Hb#Hb#Hb#d3#Hc#Hd#He#Hf#Hg#Hh#E..4x#DJ#Eb#ci#Ea#fp#Hi#fp.Tu.Ny.WD.Tk#.e#.e#.e#.e#.e.2n.9s.8B#bu.Uq#bw#de#lS.YH.Su#jz.PB.Tu.NZ.Ny.NZ.NZ.NZ.NZ.NZ.NZ.NZ.Ny.Ny.Ny#fs.Nz.P##Hj.0U.NZ.Ny.Ny.Ny#jz.14#mf.QH#aD#ip.Tk#cn#tr#Hk#Hl#Hm#Hn#Ho#Hp.Q8.Nd.Lo#Aq.VV.UM#qr.3Z.Oz.UM.O3.S4.Jt.P3.P3.P2.UN.3Z.3Z.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z.O4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.S4.O3.W9.K7.W9.K7.MV.X2#DN#ET#Ee#t5#ET#GD#Ee#ET#Hq#Hr#Ee#jL.R1.R0.XY.0H#jL#Hs#vE#Ht#Hu#Hv#Hw#bW#ev.W3#ev#eF#bW#GG#Hx#Hy#jG#t##Du#sN.Oy#pN#qr#Eq#Eq#Eq#Eq#Eq#Eq#qr#Hz#HA#HB#CV.7r.56.7r.56#yG.56#HC#HD#HE#Az#ba#cR#f2#B##f2#HF#HG#HH#HI#HJ#HK.Lx.Oz.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.Sb.Rd#hX#HL#HM#HN#HO#HP#j##HQ#HQ#sk#if#if#sq#j##j##j##j##j##j##j##j##j##j##j##j##j##j##sq#if#if#ih#ih",
-".RY.P2.No.No.0p.No.No.OB.P2.P2.P2.P2.No.No.N3.No.P2.VU.KU.P3.Xz#kw#dN.MZ#HR.6O#HS#HT.Rv.NU.NU.NU.ND.Oi.Kz#HU#HV#gd#ge#ge#ge#ge#ge#ge#ge#ge#ge#gd.S3#HW#oG#HX#HY#.e#.e#.e.Tk#fe.5B.TD.ND.NU.NU.NU.NU.NE.Oa.NE.TD.8a.8B.8B#bs##W.2c.8B.8B.8B.8B.8B.Uq.WR.NU#ph#HZ#gb#H0.Ur.NE.NU.NU.NE.RA.9n.8B.8B.8B.8B.8B.5B.Oi.4s#jC.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8p.NU#fd#H1.NZ#mg.8B.5B.8B#H2#ED.YL#.e#.e#.e#.e.0U#.e#cl#fe.Tk#H3#H4#H5#H6#H7#H8#H9#oA#I.#oA#Ha#I##Ia#Ib#Ic#Id#E..4x#Bh.0U#bp#Ie#cn.VK.Tu#fs#ju.0U#.e#.e#.e#.e#gB#.e#.e#eq.Qr.5B.0Z#.c#mQ#bv.6R#aG#db.3g.PB.PB.NZ.NZ.NA.NZ.NZ.NZ.NZ.NZ.NZ.NZ.Ny.NY#fx#DE.XJ.Ny.Ny.Ny.Ny#fs.Ny#fs.94#jz#m4#hH.64#lQ#or#If#Gy#Ig#Ih#Ii#hX.OO.K7#oa.3Z.3Z.UN.UM.Jt.La.0f.Xz.Ja.Ja.Ja.Jt.Q1.P2.UM.UN.3Z.3Z.3Z.3Z.UN.UN.UN.3Z.3Z#nh.W9.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.S4.O3.Jt.K7.Jt.O3#e5#jL#ET#t5.71#Hq#xm#xm#Hq#Hq#f##GD#xm#ET.0H.R1.OO#hY#FN#wR#GD#GD#gb#Ij#Ij#Ik#Il#Dk#Im#In#Io#Ip#Iq.1C#Ir#Eq#qr#qr#Eq#Eq#Eq#Eq#Eq#Eq#Eq#Er#Er#Er#Hz#Is#It#uW.56.7r.7r#Iu.56#Iv#Iw#Ix#Iy#ol#B##B##nt#Iz#IA#IB#IC#ID.I2.Lx.RZ.UN.No.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.Sb.Oz.HK.RY#IE#IF#Ds#IG#j##HQ#HQ#sk#sk#j##j##j##j##j##j##j##if#if#if#if#if#if#if#if#if#if#if#ih#if#if#j##yQ#Cu#om",
-".P2.P2.P2.SY.No.P2.0p.No.OB.P2.P2.P2.No.P2.Oz.No.No.OB.Q1.WA.Qo.K8#IH.0o#II#Bh#IJ#xi.2s.O8.NU.NU.NE.Oi#IK#IL#qX#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#qW#IM#IN#IO.4w#gB#mh#.e.0V#cn.9n.PL.5M.NU.NU.NU.NU.NU.NE.Oa.Oa.53.8a#cn#co#IP.5B.8B.8B.8B.5B.9n#c1.NE.NT#IQ#e7#IR.Sv.NE.NU.NU.NE.2##.Z.8B.8B.8B.8B.8B.8B.TC.Qy#.f.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#bw.O..4B#IS.PB#m7#.f.5B.5B.9n.9q.YL#.e#.e#.e#.e.Tk.8F#jh#gi#IT#IU#IV#IW#IX#IY#IZ#I0#I1#I2#I3#Hb#oA#I4#I5#I6#I7#I8#I9#J.#J##xF#DK#br.VK.Tu#fs.8F.0U#.e#.e#.e#.e#.e#gB#.e#.e#iF##K.YG.Pc#.c#mQ.8a.4s.SJ.Up.SK#kS#aE.PB.PB.PB.NZ.NZ.NZ.NZ.NZ.NZ#fs.NY#Ja#cp.Qv#fs.Ny.Ny.Ny.Ny.Ny#fs#fs.Ny.Ny##G#bp.PB#Jb#Jc#Jd#qb#Je#Jf.MX.Jt.UM.3Z.3Z.UN.P2.XY.MQ.MQ.UP.MX.MY.TN.MX.UP.21.XA.R0.Q1.P2.UM.UN.3Z.3Z.3Z.3Z.3Z.RY.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.La.S4.K7.W9.W9.R0.0f.VX#sM.71#GD#GD#ET#ET#ET#GD#ET#ET#GD#GD#GD#E2.0g#n8#wR#Jg#GD#GD#GD#GD#FN#wR#ge#Jh#Ji#ex#uJ#Jj#Jj.P8#pJ#Eq#qr#qr#Eq#Eq#Er#Er#Er#Er#Er#Er#Er#Er#qr#Jk#Jl#Jm#uW.56.56#Jn#Jo#Jp#Jq#Jr#Js#Jt#E7#Ju#Jv#Jw#Jx#Jy.I2.Hz.IW.UN.UN.UN.UN.Sb.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.Sb.UN.IW.UN#Jz#JA#JB#JC#sk#HQ#sk#sk#sq#if#if#if#if#if#if#if#ih#ih#if#if#j##yQ#AF#AE#G0#rx#Cv#JD#F8#z0#om#G.#so#zw#JE#Fd#Fd",
-".P2.P2.P2.UW.P2.0p.7G.No.Ha.No.No.P2.P2.No.Oz.No.Oz.UM.4..MM.OD.X1.Yc.7w.8I.63#JF#eb.8M.Sq.NE.NU.NE.2##JG#JH.0g#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#qW#JI#JJ#JK#JL#dc#mh#.e#es#aH.8p.NE.NE.NU.NU.NU.NU.NU.NU.NE.5M.NE.5C#mQ#q0.0Z.5B.8B.5B.8B.8B.RR.ND.Qz#JM#e4#JN.8q.NE.NU.NU.NE.O.#aG.8B.8B.8B.8B.8B.Pc#vl.1f#cn.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n#.X#JO.3m.PB.WD#br.5B.9n.5B#fp#nV#.e#.e#.e.0U#.e.VK#fe#JP#JQ#JR#JS#JT#JU#JV#JW#JX#JY#JZ#J0#J1#oA#oA#J2#J3#J4#J5#J6#J7#tr#DJ.5A#J8.9l.NZ.Ny.3k.Tk#.e#.e#.e#.e#.e#.e#.e#gB#.e#.e#.e#xH.9q##L.5B.8p##W.QO.Ns#p6#gh.YH#py.Nq.Ny.PB.NZ.NZ.NZ.Ny.P#.XJ##L.4x.Ny#fs#fs.NZ#m4#fs.Ny.Ny.Ny.Ny#mf.PA.YV.PB#J9#K.#K##Ka#tc.OL.0f.Nn.UN.3Z.UN.O4.XY.MQ.Yc.ZU.T2#Kb#Kc#ak.Xw.S6#kw.70.MQ.0f.K7.J#.Nn.UM.UN.3Z.UN.VV.S4.T0.T0.T0.T0.T0.S4.S4.S4.S4.S4.S4.S4.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.XY.R0.W9.W9.R0.Nc#n8#ET#ET#GD#GD#GD#FN#GD#Jg#Jg#Jg#GD#GD#GD#GD#FN#f##GD#GD#GD#GD#GD#GD#GD#wR#ge#wR#wR#wR#wR#wR.0g.S4#Eq#qr#qr#Er#Er#Eq#Eq#Eq#Eq#Eq#Eq#Eq#Eq#Eq.RZ.Ji#Kd#Ke#uW.56#Jn.7r#Kf#Kg#Kh#Ki#Kj#Kk#Kl#Km#Kn.RZ.Hz.IW.UN.Sb.Oz.I2.Hz.Oz#Ko#Ko.IW.UN.UM.UM.UM.UM.UM.UM.UN.UN.Oz.Oz.UM#Es#Kp#Kq#xw#yQ#HQ#sk#sq#if#if#if#sk#ww#AF#AE#GZ#yQ#sp#Cu#JD#om#s5#id#zw#Gb#JE#z5#Fc#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd",
-".X1.RY.No.P2.0p.P2.P2.7G.Je.Ha.No.P2.No.No.No.N3.No.Q1.VU.Un.P3.XA.25.O5#Kr.3k#Ks#fg#Kt.Rv.NE.NU.NE#Ku#Kv#Kw#gd#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#qX#Kx#Ky#Kz.3A#.e#.e.Tk#aF.8B.Tl.O#.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.Oi.7n#ct#cn.8B#.f##Q#gj#.0.O#.51#uc#e5#KA.Tq.NE.NU.NU.NE.PL.9n.8B.8B.8B.8B.8B#CD.WR#e0#cn.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#p7##O.Pe#sC.PB.Nr#xE.5B.5B.Pc#er#gB#.e#.e.0U.Tk##G##K#DE#KB#KC#KD#KE#KF#KG#KH#KI#KJ#KK#KL#KM#KN#KO#KP#Hb#J2#KQ#FB#KR#I9#J.#Bh#hH#Bg#hf.Ny.Ny#Bh.0U.0U#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#cm#.e#dU.4q.0X#aH.8a.QO.Vw#lO.Sq#uj#KS#wF#fs.NZ.Ny#fs.NA#eq#cp.Ny.Ny.PB.PB#KT#aB#rT.Ny.Ny.Ny.Ny.P#.3f.PA.PB#KU#KV#KW.Y2#dN.Yc.XY.RY.UM.UN.Nn.R1.MP.6D.6E#KX#u9#KY#KZ.Ta#yT.9##tc#jZ#t4#rz.MO.21.XY.R0#wQ.J##p9.T0.S4.S4.T0.S4.S4.S4.S4.T0.T0.T0.T0.T0.T0.T0.S4.S4.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.XA.K7.R0.Jt.S4#e5#E2#GD#GD#Jg#qV#GD#f##GD#GD#ET#GD#GD#GD#GD#GD#FN#GD#GD#Jg#GD#wR#FN#K0#wR#wR#ge#wR#wR#wR#K1#hY.MV.UM.3Z#qr#Eq#Eq#Eq#Eq#Eq#Eq.UM.UM.UM.UM.UM.UM.IW.IW#K2#s0#Cd.56#Dp#K3#K4#K5#K6#K7#K8#K9#wP.Hz.I2.Oz.UN.UM.UM.IW.I2#Jz#L.#L##La#Lb#Lb#IB.IW.UN.UN.UN.UN.IW.HK.RY#Ir#Lc#DZ#Ld#ig#HQ#HQ#j##xw#if#ih#if#sp#Cv#s4#id#Gb#JE#z5#JE#z1#JE#Fd#Fd#h.#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd",
-".TO.MP.Q1.UM.P2.0p.P2.No.Je.OB.No.No.No.No.P2.N3.No.No.OB.Nn.WA.O5#n7.OD#Le.6P#Lf#Lg#Lh.98#nX.O8.ND#Li#Lj#f.#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb.T1#Lk#Ll#Lm.0U#gB#.e#fp.8B.5B.2#.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND#Gj.NC.5C.Z1.4s.PL#bv#zA.Oq.51#Ln#D4#Lo#Lp.Oa.NU.NU.NE.2##.Z.8B.8B.8B.8B.8B#Lq#vk##V#cn.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9o.Oj.9n.PB.PA##L.8w.5B.4r#ft#.e#.e#.e.0U#.e#av#Lr#C9#Ls#Lt#Lu#Lv#Lw#Lx#Ly#Lz#LA#LB#LC#LD#LE#LF#LG#LH#nS#LI#LJ#LK#J7#tr#LL#hH#fw#tp.3f#fs#fs#hL.Tk#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#cm#.Y.0U#xH#gF.Vy#nU.4J.SK#hG.YV#LM#ce#LN.Ny.Nr#fe.3h.PB#ip#JL#LO#LP.3A#aE#fs.Ny.Ny.Ny.Ny#fs.Ny.PB#LQ#bf.74.6H#ja.70.K7.Nm#HK#Du.OF.MN.MX.T2.S6#LR#LS#LT#LU#LV.Bd#yT#yT#B5.9##u8#tG#kw.MX.OO.MO.Nd.OO.Lo.T0.S4.S4.S4.T0.T0.MV.Lo.Rd.Nc.Nc.Nc.Rd.MV.MN.T0#g..S4.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0#p9.K7.K7.R0.T0#e4#E2#GD#GD#FN#GD#Jg#GD#GD#GD#GD#FN#FN#FN#GD#GD#FN#FN#FN#GD#K0#FN#wR#GD#wR#wR#wR#wR#wR#wR#E2.MQ.RY.SX.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.K1.Q0#LW#CV.7r.56#uW#LX#LY#LZ#L0#L1#HK.Hz.IW.UN.UM.UM.UM.UM.UN.IW#L2#L3#L4#zt#F##B##f2#qI#Lb#Jy.N3.Je.MK#L5#L6#Ds#L7#L8#L9#yQ#HQ#j##j##if#if#if#yQ#JD#M.#z1#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd",
-".VX.TO.MX.P4.UN.P2.7G.Je.OB.Ha.No.No.P2.P2.No.No.Oz.OC.UM.VU.Nl.O5.Xz.WA#ES#kS#M##Ma#mY#Mb#Mc.Qs#Md#Me#Mf#e5#E2#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#hY#G4#Mg#Mh.3A#gB#mh#iF.8u.8B#bt.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NE.4S.Mi.Oa.NE.Oa.O#.QO.XF#Mi.51#Mj#D4.Ic.NT.ND.NU.NU.NE.2##.Z.8B.8B.8B.8B.8B.YH#lO.O8.Pc.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.Pc#.9#Mk#Ml.PB.94.9s#.Z.5B.0Y.0U#.e#.e.0U.Tk##G.VK#ul#Mm#Mn#Mo#Mp#Mq#Mr#Ms#Mt#Mu#Mv#Mw#Mu#Mx#My#Mz#MA#MB#Gq#d3#MC#MD#ME#MF#Bh#.Y#fw#ul.4x.Ny.Ny.PA.0U.0U#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.YL#.Y#.Y.VK.VK#.Y#.e.Tk.VK.Mz.Ny.WC#xF.8e#AJ#vk.Sz#dV.Mv.LD#MG.NZ#fs.Ny.Ny.Ny.Ny.Ny.14#MH.ZR#C1.9##t4.MQ.R0.UM.UM.O4.O3#n7.MZ#t4#MI#MJ#MK#ML#MM#MN#MO#MP#MQ#MR#tc#vX#td#MS#MT#MU#t5#wh.OL.Nd.MQ.Rd.MV.MV.Rd.MQ.MO.ON.0H.0H.0H.Nd.ON.OO.MQ.Rd.MV.T0.S4.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.O3.K7.K7.R0.M0#e7#wR#GD#FN#GD#GD#GD#GD#FN#Jg#FN#FN#GD#FN#f##GD#ET#GD#GD#GD#GD#GD#wR#wR#ge#wR#wR#wR#f##ge.Nd.R0.SX.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.P1.JL#MV#MW#Dn#Cd#Cc#uU#MX#MY#MZ.OW.Lx.IW.UN.UM.UM.UM.UM.UM.UM.UM.Tf#M0#M1#F##zt#cR#cR#cR#zt#B##M2.Nf#M3#M4#M5#M6#M7#G0#HQ#sk#sk#j##if#if#ih#HQ#rx#s5#Gb#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd",
-".OO.2Q.2Q.OV.XA.P2.No.UM.OB.Ha.No.Sb.Sb.Sb.Sb.Nm.Oz.Sb.No.Nm.Nn.MM.JG.OD.73#M8#M9#N.#hz#N##Na#Nb#q6#p3#rI#e5#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#Nc.0H#Nd#Ne.VK.0U#.e#.e#er#w8.5B#c1.Oa.NU.NU.NU.NU.NU.NU.NU.NU.NU.Ut.8q.2B.NU.NU.Oa.00#p4.Ok.51#Nf#D4#Ng#fd.Ok.NU.NU.NE.2##.Z.8B.8B.8B.8B.8B#br#ko.Pe#bs.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#p7.Z1.XC.8p.PB.LL.ZW.8A.9n##L#.e#.e#.e.0U.0U.Nq.9p#Nh#Ni#Nj#Nk#Nl#Nm#Nn#No#Np#Lx#Nq#Nr#Ns#Nt#Nu#Nv#Nw#Nx#Ny#Gq#J2#Nz#NA#NB.3g#hL#ul#br#.Y.NZ#fs.Nz.8F.Tk#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U.Tk.8F#fs.Tk#NC.XJ#jt.2s#bm#ND#NE#aB#vk.3k.PB.Tu.Ny#fs.Ny.P#.NZ#NF#NG#bf#jc#tc.ZU.Rd.O4#Du.RY.P3.S4#n7.MX.ZU#NH#NI#NJ#NK#NL#NM#.V#br#bx#NN#NO#NP#B5#NQ#jc#NR#NS#NT.71#wh.TO.P9.MZ.MZ.VX.P9#kw#lr#tG#bc#bc#wh#hX.OL.0H.ON.Lw.Lo.MV.T0.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.XY.W9.K7.W9.R0#ta#n8#wR#wR#GD#GD#GD#GD#FN#FN#GD#ge#GD#GD#GD#GD#GD#GD#GD#GD#GD#GD#wR#K1#wR#ge#ge#NU#ga#wR.P9.K7.UM.UN.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.JL#NV#NW#NX#NY#Dn#NZ#N0#N1#N2.HL.IW.UN.UM.UM.UM.UM.UM.UM.UM.UM.UN.I2.Lx#N3#f2#BS#b2#cR#Cr#N4#N5#N6#N7#N8#N9#O.#O##HQ#if#if#j##j##if#ih#if#ie#om#Ga#Fc#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fc",
-".MN.X2.Si.2Q.P9.T0.P2.No.Je.Je.Sb.Sb#Oa#Oa#Oa#Oa.RZ.RZ.Sb.Hp.JK.Lv.KU.OD.Sg#Ob#iG#Oc#Od#xV#xV#lZ#Oe#.0#mO#nD#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb.T1#Of#Og#JL#gB#mh#.e#gi.8B.UF.O#.NU.NU.NU.NU.NU.NU.NU.NU.Om#c2.Tq.PS.NU.NU.NE.7o#j2#Mi.8q#Oh#f.#Oi#fc.2c.NU.NU.NE.O..Uq.8B.8B.8B.8B.8B#br#C6.Tq##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8a.Ol#fu.PB#ip#ft#br#bu#th#iF#.e.0U.Tk#hL.Nq#B0#Oj#Ok#Ol#Om#On#Oo#Op#Oq#Or#Os#Ot#Ou#Jo#Ov#Ow#Ox#Oy#Oz#OA#OB#I##OC#OD#OE#zG#OF#C9#Bg#m3.Ny#fs.Ny#tr.0U.0U#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#xy#hH#jz.Nq.4r#jy.63.0U.0U#lP.YL#kS#un#OG#Fq.8G.3A#jz.PB.Qv#fs.PB#OH#w4#OI#ss#eS.MZ.MV#pJ.O4#Du.R0.O3.21.MP.TN#OJ#OK#OL#OM#ON#OO#.V.TE#OP#Lm#OQ#OR.6M#NG#ss#w3#AK#ss#t7#vX#OS#eS#qV#qV#eS#eS#bd#MS#t5#bd#vX#NT#MU#t5#wh.TO.OV.Ne.Nc.MV.T0.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0#Aq.T0.S4.R0.K7.K7#fU#E2#GD#GD#wR#wR#wR#wR#wR#GD#GD#wR#GD#GD#GD#GD#NU#NU#NU#NU#GD#wR#ge#wR#ge#wR#wR#K1#f##hY.Nf.UM.UN.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.IW.K1#OT#Jm#OU#NY#OV#OW#OX.P1.JL.Oz.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.I2.I2#L2#OY#Cw#cR#cR#cR#Cw#OZ#O0#O1#O2#O3#Dx#qF#if#if#xw#j##if#if#ih#yQ#JD#G##z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fc#lJ#lJ#lJ#lJ#lJ#lJ",
-".K7.MX#kw.TO#kw.X2.MV#do.P1.RZ.Nm#Oa#Oa#do.Sb.Sb.Sb.P1.Sb.Nm.Nn.Hp.MK.J#.Nn#O4#O5#O6#O7#yu#xV#mZ#O8#O9#P.#e9#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge.X2#P##Pa#ep#nV#.e#.e.2n#vn#.c.NE.NE.NU.NU.NU.NU.NU.NU.NU.Pd.Vz.RB.Oi.NU.NU.ND.Pd#.0.Ok#Pb#Pc#gd#Pd.Uu.ND.NU.NU.NE.Pd#hM.8B.8B.8B.8B.8B.YH.Mj#ti##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8y.Ok#de.NZ.3j.8F#bp.0Z#bl#.e#.e.0U#.e#fl.TA#C9#Mm.G##Pe#Pf#Pg#Ph#Pi#Pj#Pk#Pl#uU#Jo#Pm#Pm#Jo#Pn#Po#Pp#Pq#Pr#Ps#Pt#Pu#lQ#tr.4x#B0#br.YG#ce#fs#fs#fs#oN.Tk.0U#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U#.e.LL#jz.Sp#cl.LL.Tk.WE.LJ#lQ.Nr#KT.3A#ci#vl.LQ.Mr#Pv#bB.3A.NZ.LK#hk#w4#Pw#Px#IH.ON.T0#pJ.RY.RY.O4.Jt.R1.21.MP.MO.OM#Py#Pz#PA#mN.YU#v3#tt#PB#Ec.JX.5z#PC#PD#PE#Ka#b5#zy#AK#PF#ss#NR#b5#w3#yf#NR#t7#kO#LH#td#ss#PG#td#MS#ET#hX.VX.ON.Nc.MV.T0.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.La.Nf.W9.K7.5i.K7#e5#E2#GD#GD#wR#wR#ge#ge#wR#NU#NU#NU#NU#NU#NU#NU#NU#NU#NU#NU#NU#wR#ge#ge#GD#wR#f##ga#n8.MV.UM.Oz.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.IY#PH#PI#E5#OU#It#PJ#PK.IY.K1.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.Lx.IW#L2#PL#yO#qI#cR#cR#cR#rv#PM#Cs#M.#BQ#ie#if#ih#if#j##j##if#ih#if#G0#pb#Gb#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#z5#PN#z5#Fd#Fd#Fd#Fd#z5#z5#z5#z5#z5#z5#PN#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#h.#Fd#PO#PP#PQ#PR#PS#PT#PU#PV",
-".R0.Nc.X2#hY#hX#e7.X2.Ne#oa.P1.RZ.Sb.Sb.Nm.Sb.Sb.Nm.RZ.Sb.Sb.Nm.Je.JK.Lz#PW#PX.WD#IO#PY.XQ#yu#Ak#PZ#P0#rH#hX#Jh#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#qW#P1#P2#JL#gB#gB#.e#dd.8B##R.ND.NU.NU.NU.NU.NU.NU.NU.Pd#.N.2r.Pd.NU.NU.NE#P3.NU.2c.8q#P4#ga#P5#c4.O#.NU.NU.NE.NU#hM.8B.8B.8B.8B.8B#cn.8l#c4.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#P6.ND.6R.3j.NZ##G#er#w8.ZW#.e.0U.0U.WD.94.YV#C8#P7#P8#P9#Q.#Q##Qa#Qb#Qc#Mv#Qd#Qe#Pm#Jo#Qf#Qg.WT#Qh#Qi#Qj#Qk#Ql#Qm#Qn#Qo#tr.3g#hf#fw#xb.PA.Ny#fs.Ny.LL#.e.Tk#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U.0U.PA.NZ.NB##L.3f#eo.XJ.LJ.WD.4H.94#m4.Tt#.Y.9q.8l.Mj#j6#ko##T.PB#Qp#Qq#w4#Qr#td#tG.ON.Nf.P3#Du.UM.Q1.R0.O3.0f.0f.X1.MN.JC#zy#Qs#ON#.f.TE#Qt#jC##z.PI#Ec#Qu#J.#OP#Qv#Qw#Hn#Qx#dP#Qy#Hn#Hn#Qz#QA#tf#QB#QC#QD#Gx#AK#Ka#AK#NR#vX#qV#wh.VX.Ne.Rd.MV.T0.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.T0.S4.Jt.OF.5i.S4#e4#wR#wR#wR#wR#GD#wR#GD#wR#wR#GD#GD#wR#GD#wR#GD#NU#NU#GD#GD#wR#wR#GD#wR#wR#wR#wR#gb.MQ.RY.SX.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.Sb.K1.N3#QE#QF#QG#QH#QI#QJ.IY.N3.UN.UM.UM.UM.UM.UM.UM.No.No.No.Oz.Hz#HK#QK#QL#nt#qI#cR#cR#zt#BR#ha#F7#QM#yQ#if#ih#if#sq#j##if#if#ih#yQ#JD#id#z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#QN#QO#JE#z5#z5#z5#JE#JE#z5#h.#rw#Fa#QN#QP#z5#z5#Fd#QQ#Fd#Fd#Fc#QR#zw#M.#F9#Cv#GZ#JC#QS#QT#pV#QU#QV#QW",
-".K7.R1.0H.X2#hX#e7#hY.9Q.7w.R0.RZ.RZ.Nm.Sb.Sb.Sb.HI.Hp.P1.HI.RZ.Sb.Je.Nn#PW#QX#kk#QY#QZ#Q0#yu#hz#Q1#Q2#Q3#Q4#iu#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#e7#Q5#Q6#Q7.3A#.e#nV.Tk#xD.Qy.O#.NU.NU.NU.NU.NU.NU.NU.Pd.7n.NT.Pd.NU.NU.NU.NE.ND.ND.51.Pm#HZ#Pd.6S.Ol.NU.NU.NE.NU##Q.8B.5B.9n.9n.5B#cn#gh##B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##Q.Oa.1f#ce.PB.PA.0U#cp#xF#mh.Tk.6P.XJ.XJ#dd#tq#Q8#H8#Q9#R.#R##Ra#Rb#Rc#uR#Ov#Ow#Ov#Nt#Rd#Re#Rf#Rg#Rh#Ri#Qk#Rj#Rk#Rl#G8.3h.3h#xF#w8#C7#hL.NZ#fs.Ny.Ny.XJ.3A#kn#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U.0U.0U.63#rT#.e.4r.8F#uh.94.VK#th.Qr.14.Ny.Ny#ip.VK#cm#bp.XF.SJ#bv#Bh.Ny#Rm#ES#Dd#Rn#u9#lr.MO.K7.RY.UM.RY.O4.R0.R0.Jt.O3.XY.XY.Nf.K7#pJ#Ro#Rp#C9#mi#v5#.V#eh#Rq#OQ.0T.Mv#ck#OP.92#Rr#Rs#bf#Rt#Ru#Rv#Rw#Rx.Mu#EQ#Ry#Rz#RA#RB#RC#v.#w3#td#qV#lr.MZ.MQ.MV.T0.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.T0.O3.R0.R0.OF.M0.X2#wR#NU#GD#NU#NU#GD#GD#wR#GD#wR#wR#GD#NU#GD#GD#GD#GD#K1#wR#GD#wR#wR#K1#K1#f##ge.MO#wQ.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.IW.IY#RD#RE#RF#RG#RH#PH.IY.RZ.UM.No.No.No.No.No.No.No.No.HK.I2#RI#RJ#RK#RL#zt#B##cR#cR#f2#qH#AC#yc#HQ#if#ih#if#sq#j##j##if#ih#RM#Ba#s4#Gb#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#JE#z4#sm#zu#z2#h##RN#Dw#zu#yP#RO#nt#Cw#ba#nt#Ev#h.#z5#Fc#z5#z0#AE#RP#GY#RQ#RR#RS#GX#GW#RT#RU#RV#RW#RX#RY",
-".R0.K7.ON.VX.0g#hY#hY#hY.9Q.X2.Rd.Sb.RZ.RZ.HI.HI#Oa.Sb.RZ.UN.RZ.Sb.UM.P2.KT#RZ.Tk#gB#R0#mY.XQ.XQ#R1#R2#R3#Hb#e6#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb.Ne#R4#R5#JL#gB#.e#.e.9q##Q.NE.NE.NU.NU.NU.NU.NU.NU.Pd.RG.4B.Pd.NU.NU.NU.5M.NE.ND.51.Pv#R6#R7.Vz.Oj.NU.NU.ND.NE##Q#co.53#.X#.X.53#j2#ko.2A#.Z.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##Q.NE.7o.3g.PB#lQ#hH.Tk.0U.0U.0U#av.XJ.WS#tp#fw#R8#R9#H##S.#S##Sa#Sb#Sc#Sd#Se#Sf#Se#Sg#Sh#Si#Sj#Sk#Sl#Sm#Sn#So#ph#Sp#Sq.Mz.3g#hH#iq#Bg#EN.NZ#fs#fs.Ny.Ny.XJ#cm.Tk.0U.0U.0U.0U#mh#.e.0U#.e#.e#.e#.e#.e#gB.0U.0U.0U#hL.3g.Tu.3k.4J#.#.2n.0W#bx#.f#br.9q.Ny.Ny.3j.Ny.Nq.0U#.e#aH.Qy.9m.PB#Sr#Ss#rB#eU#St#wx#Su.2R.R0#Sv.UM.RY.RY#Sv.O4.R0.Jt.W9.W9#t#.S4.Nf#pJ#Sw#Sx.QN#DK#.V#v5#.V.RQ#Sy#Sz#SA#G8.5A#.f#ON#SB#SC#SD#SE#SF#EQ.KG.JW.KG#SG#SE#SH#SI##v#dP#PF#td#t5#kw.ON.Nc.MV.S4.W9.S4.T0.T0.T0.T0.T0.T0.T0.O3.R0.R0.VV.Nc#hY#GD#wR#GD#GD#GD#wR#wR#wR#GD#NU#K1#wR#GD#K1#GD#wR#K1#wR#wR#K1#wR#f##K1#K1#K1.P9.W9#HK.UN.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.IY#SJ#OW#SK#SL#SM#SN.IY.Oz.No.No.No.No.Ha.Ha.Ha.Ha.Ha.Ha.I2.IZ#SO#SP#SQ#B##f2#cR#zt#f1#sm#Fa#rx#lK#lK#if#xw#j##j##j##if#ih#sk#F8#SR#z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#z5#yP#cR#cR#yO#yO#cR#zt#zt#cR#cR#cR#cR#cR#cR#zt#sn#SS#ST#SU#SV#SW.TP.0g#RV#RW#RY.0h.0h#RY#RY#SX#SX#SX#SX",
-".K7.K7.OO.6q#e6.X2#fI#e7.X2#fI.X2.OO#wQ.3Z#ra.Sb.Sb.UN.UN.3Z.3Z.UN.UM.P2.Oz#vX#SY#hL#SZ#S0.XQ.XQ.9y#S1#vk#sx#e4#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge.0g#S2#S3#S4.3A#.e#nV#aC.0Y#c1.ND.NU.NU.NU.NU.NU.NU.Pd.6S#fd.Pd.NU.NU.NU.ND.NE.2c.Tq.NO#qa#S5##H.Pw.NU.NU.NE.NU.Qy.O..ND.NE.NE.NE.NE.2q.Ps.53.9r.0Z.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8p.NU.NU.3k.PB.NZ#fl#cm.0U.Tk#cm#fl.Mu.Tk#Nh#.9#v5#S6#Pt#S7#S8#S9#T.#T##Ta#Tb#Tc#Td#Te.8P#Tf#Tg#Th#Ti#Tj#Tk#Tl.8q#Tm#Tn.14#DJ#OF#DE#C7#Be#jz#mf#fs.Ny.Ny.Ny.3f#tu#hL.VK.YL.0U.0U.0U.0U.0U.0U.0U.0U.0U.0U#.e#oN#zG#fs#rT.Nr.YG.ZW#bl#C7#br#ul#ul#w8.8F#m4.Ny.Ny.Ny.LL.0U#.e#es##Q.PA#ip#To#ii#NP#yU.Y2#t5.MZ.21.R0.O4#Du#Du#Du#Sv#Tp#pJ#pJ.R0.R0.W9.R1.Rd.MQ#p9.S4#Tq#Tr.ZW#Ts.TE#c3#Tt#Tu.VD#Ts#Tv#Tw.SI.PI#Tx#Ty.JW.KG.JW.JW.JW.JW.KG#FH#Tz#TA#b7#w4#Pw#jc#TB#ak.P9.MQ.MN.T0.S4.S4.T0.T0.T0.T0.T0.S4.W9.K7.O4.R0.OO#n8#wR#wR#wR#wR#GD#wR#wR#f##wR#wR#ga#f##wR#TC#TC#ge#GD#K1#TC#wR#K1#K1#K1#K1#hY.T0.UM.UN.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.JL.3Z#Kd#TD#TE#TF#TG.Fo.OA.No.Ha.Ha.Ha.Ha.Ha.No.No.No.No.OA#RI#TH#TI#TJ#qI#zt#cR#f1#cR#TK#TL#ie#ig#ih#if#j##j##j##j##j##if#lK#G0#id#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#PN#TM#z2#cQ#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#rv#sn#TN#TO#TP#TQ#TR#TS#TT.TP.TP.0g#RX#TU#TU#TV#TW#TX#TC",
-".XY.R0.Nc.VX.VX.P9#fI#fI.P9#fI.X2.X2.OV.R1.UN.UN.UN.UN.UN.3Z.3Z.UN.UN.No.No.J##TY#TZ#T0#Od.XQ#yu#mZ#T1.53#T2#e4#gd#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb.Ne#T3#T4#Lm#dc#gB#.e.4q#j2.ND.NU.NU.NU.NU.NU.NU.Pd##B#iD.Pd.NU.NU.NU.NE.NE.ND.2z.17#T5#T6.RF.Mi.NU.NU.NE.NU.NE.NE.NU.NU.NU.NU.NE.Px.17.Oq.NE.PL.XF.8p.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8a.NU.Oq.2n.PB.NZ.LL##G.Tk.Tk.4H#zG.XJ#m3#T7.UF#T8#pn#Pt#T9#U.#U##Ua#Ub#Uc#Ud#Ue#Uf#Ug#Uh#Ui#Uj#Uk#Ti#Ul#Um#Un#Uo#ca#Up.3g#MF#tr#Be#fw#fw.TA.NZ#fs#fs.Ny#fs.Ny.Ny#fs#jz.LC#aE.XJ.XJ.XJ.XJ.Nq.Nr.Nr#fl#Bh.3f.Ny.Tu.LC.3A#xE#ul#ul#C7#Uq#xb#xb#cn#fe.QH.Ny.Ny.Ny.Ny.3f#.e.3A#cp##K.PB.2n.ZH#rB#K##Ka#p1#IH.ON.M0#Sv#Du#Eq#Du#Du#Du#Sv#Sv#Sv#pJ.K7.0f.Lo.Rd.Rd.Rd.Rd#oa.OV#Ur#Us#dd#Ut#Uu#lS#mi#.V#Sr#Uv.JW.KG.KG.KG.KG.JW.JW.JW.JW.JW.JW.KG.5z.M.#Uw#Ux#tn#yU#PF#vX#IH.VX.MQ.MV.S4.W9.S4.R1.R1.UZ.T0.R0.O4.O4.XY#e4#Uy#GD#K1#K1#K1#K1#TC#GD#K1#wR#wR#K1#TC#TC#Uz#K1#K1#K1#K1#TC#K1#K1#K1#K1#gd.Nc.RY.SX.UN.UM.UM.UM.UM.UM.UM.No.No.No.No.No.No.No.Oz#UA.Km#UB#UC#UD#UE#TG.FM.Tf.No.No.No.No.No.No.No.No.No.No.OA#RI#UF#Kl#g7#F##zt#cR#f1#ba#z3#F7#rr#ig#if#j##j##j##j##j##j##if#if#vN#Cu#z1#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#JE#UG#BR#cR#zt#zt#zt#zt#zt#zt#zt#zt#zt#cR#cR#zt#f1#f2#qI#B##B##cR#UH#UI#UJ#F2#UK#UL#FN#GD#UM#UN#UO#UP",
-".XY.K7.Rd.P9.P9.P9.X2.X2.P9.X2.9Q#fI.X2.P9.R0.3Z.UN.UN.UN.UN.3Z.3Z.UN#Eq.UM#UQ#UR#iJ#US#UT#Q0#yu#mY#UU#UV#UW#e7#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge.VX#UX#UY#S4#iF#.e#.e.Tk#.c.ND.NE.NU.NU.NU.NU.NU.Pd##B#fd.Pd.NU.NU.NU.NE.5M.ND.Tq##A#J2#UZ#U0.WG.NU.NU.NU.NE.NE.NU.NU.NU.NU.NU.NU.Pw.WF.Oq.NE.NE.ND.NU##O#.d.8A.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#.d.NE.NC#ck.PB.NZ.LL.4p.0U.0U.WD.XJ.63#tq#U1#Hi#U2#U3#U4#U5.5P#U6#U7#U8#U9#V.#V##Va#Vb#Vc#Vd#Ve##b#Vf#Vg#Vh#U2.NE#M8#Vi#Da.3g#Bh#m3#w8#fw.8u.3f.Ny#fs.P##fs#fs#fs.Ny#fs.Ny.Ny#mf.Ny.Ny.Ny.Ny.Ny.Ny.Ny.NZ.Tu.Ny.Nr#Be#Hi#br#th#cp#cl#ul#ul#ul.5B.9q.Ny#fs.Ny.Ny.Ny.LJ#.e.Tk#hL#fs.63#Vj#Vk#u.#bf#jc#u9#jL.2R.K7#Sv#Eq#Du#Sv#Du#Vl#Du#Sv.Lq.S4.T0.MV.MV.Rc.Rc.Rd.Rd.Rd.T0.UM#C1#Vm#Vn#m5#bv#rF#Vo.TA.PI.N8.KG#Hk.KG.9h.JW.JW#Vp#Vp.JW.KG.KG.KG#Ry#Vq#Vr#Vs#KV#RB#PF#vX#IH.P9.OO.MV.T0#g.#g.#Ab#g..VV.O4.O4.T0#e7#K1#K1#wR#K1#K1#E2#K1#K1#TC#Uy#TC#Uz#TC#Uy#Vt#Uy#Vu#K1#TC#ge#TC#K1#f##E2.OO#Sv.OW.UN.No.No.No.No.No.No.No.No.No.No.Ha.Ha.No.Oz.HL.Oz#Vv#Vw#Vx#Vy.0n.FM.IW.No.No.No.No.No.No.No.No.No.No.Oz#RI#ID#Vz#VA#F##zt#BS#zt#ba#Dw#z0#ih#ig#if#j##j##j##j##j##j##if#if#RM#sp#Cv#SS#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#h##ha#z3#zu#Ez#ru#Bb#jX#yP#VB#rv#VC#RO#VD#nt#Cw#ba#ba#ba#Cw#ba#VE#VF#F##VG#VH#VI#VJ#VK#VL#VM#VN",
-".XY.K7.MV.OV.P9.P9.P9.X2.X2#ew#fI#ew.9Q.OV.RY.3Z.UN.UN.UN.UN.UN.3Z.3Z.UN.UM#UQ#VO#iG.3A#VP#VQ#yu.XQ#oF#VR#VS#hv#gd#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb.Ne#VT#VU#AG#dc#.e#cm.Oo.PS.NE.NU.NU.NU.NU.NU.Pd#VV#VW.Pd.NU.NU.NU.NE.ND.ND.3O#U0#VX#VY.Tm.3O.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Ur.Ts.NV.NU.NU.NE.NE.ND.NE.53##W.9n.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M##W#d..Ol.Uq#rT.Ny.14.4H#kn#cm#en.Mu.8F#VZ#yl#xb#ul#U2#V0#V1#V2#V3#V4#V5#V6#V7#V8#V9#W.#W##Wa#Wb#Wc#Wd#We#Wf#T8#ar#bo#Wg#tr.3g#Bh#bo#fw#ul#br#fp#fs#m4.NZ#m4.Qv.Ny.Ny.Ny.Ny.Ny.Ny.Ny.Ny#m4.NZ.NZ.NZ#fs.XJ.VK#hf#fe#nU#fp#ju#E#.3h#Wh#kj#fw#Wi#Wj#.e.NZ.Ny.Ny.Ny.Ny.94.Tk.8F#lQ#Bh#Gl#Wk#Wl#KV#Wm#Wn#tk.VX.0f.VV#Du#Du#Du#Du#Du.RY.VV.K7.T0.MV.T0.T0.T0.MV.MV.MV.Nc.MV.M0.MV.W9#nh#Wo#Wp.RI#Wq#Wr#yg.KG.PI.KG.KG.JW.JW.KG.Of#mM##F.Y1#Vp.JW.KG.JW#Bh#EF#Ws#Wt#Wu#Qy#PF#TB#GD.9Q#e4.OO.Nc.Rc#t#.O4.Nn.RY.S4#RY#K1#K1#Uz#Uz#Uy#TC#Uz#Uy#K1#Uy#Uy#K1#Uy#Uy#K1#Uy#Uy#E2#K0#wR#wR#K1#ge.OV.R0.UN.Oz.No.No.No.Ha.Ha.Ha.Ha.Ha.Ha.Ha.Ha.No.No.Oz#UA.UM#Wv#Ww#Wx#Wy.No.G4.IW.No.No.No.No.No.No.No.No.No.No.OA#RI#HK#Wz#WA#B##f2#b2#cR#f1#z4#pb#lK#lK#if#j##j##j##j##j##j##j##xw#if#if#rr#yQ#Cu#z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#id#rx#Cv#zv#Cv#Cu#AE#yQ#rr#rr#rr#rr#ww#yQ#yQ#yQ#AF#G0#Cu#Cv#BQ#F7#JE#Fa#QN#WB#h##g9#Ew#qH#B##F##pY#Cw#WC#WD#WE",
-".XY.R0.XY.Nd.P9.P9.P9.P9.X2.9Q.X2.X2.X2.OV.UM.3Z.UN.UN.UN.UN.UN.UN.3Z.3Z.UN#ut#wx#WF#WG#WH#O7.XQ#yu#fg#G6#WI#WJ#WK#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge.0g#WL#WM#WN.3A#.e#.e#dU.00.ND.NU.NU.NU.NU.NU.Pd#WO.YJ.Pd.NU.NU.NU.NE.Ok.ND.2q.QC#WP#WQ.9A.Us.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oj#WR.Oj.NU.NU.NU.NU.NU.NE.ND.NE.7o.9r#.Z.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.6R#WS.O#.1f#ce.NZ.QH#ji.Tk#kk.XJ.94#cp#WT.QP#WU#br#fe#dd#WV#WW#WX#WY#WZ#W0#W1#W2#W3#W4#W5#W6#W7#W8#W9#Wf#Wf#X.#th.63#tr.4x#tr#Da#jy#xb#ul#fw#fw.8u.9p.VK.63.XJ.14.LL.3f#ce#ip.LC.L9.14.PA#jy.3A#.a#bp##L#bl#er#jy#tr.3h#J#.3g#tr.3g#.a#fw#fw#cn.4x.Ny.Ny.Ny.Ny.Ny.Nq##G.Ny.L9.YG#iG#X##rB#w4#Rn#td#IH.ON.XY#pJ#Eq#Du#Du#Sv.R0.S4.T0.S4.S4.S4.T0.MV.T0.T0.T0.T0.MV.MV.T0.T0.T0.MV.VV.T0#Xa#Xb.PH#Xc#Xc#Xd#Xe.JW#Xf.Of#bl#D.##L#fw#bB#Xg.N9.JW.Ki#Xh#Xi#Xj#Xk#Xl#J1#Wo#AK#Xm#hv#DN#hY#e6.MQ.O3.K7.Nn.Ql#hY#Uy#Uz#TW#TW#Vt#Uy#TW#Uy#TC#Uy#Uy#Vu#K0#Vu#E2#wR#wR#wR#E2#E2#f##K1.P9.K7.No.Oz.Oz.Ha.Ha.Ha.Ha.Ha.No.No.No.No.No.No.No.OA#Xn.UN#Xo#Xp#Xq#Xr.I0#Xn.Oz.No.No.No.No.No.No.No.No.No.Oz.I2.I1#ID#Xs#Xt#f2#f2#cR#BS#cR#AA#Xu#yQ#mH#if#j##j##j##j##j##j##j##j##j##j##j##j##if#vN#sp#z1#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#so#RM#mH#lK#ih#ih#if#if#if#if#if#if#if#if#if#if#if#if#ih#ih#ih#lK#if#AF#F8#yd#yd#F9#F7#Fc#AC#Xv#Xw#Xx#Xy#Xz#XA",
-".XY.K7.K7.Nc.P9.P9.P9.P9.P9.TO.9Q.9Q#hX.MX.UN.3Z.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z.UN.5U#XB.3k#XC#XD.XQ#yu#hz#XE#XF#XG#e4#gb#ge#ge#ge#ge#ge#ge#ge#gb#gd#gd#Jh#gb#ge#ge#ge#ge#ge#ge#ge#gb.T1#XH#XI#XJ#.e.0U#.Y.9n.Ol.NE.NU.NU.NU.NU.Pd.L7#XK.Pd.NU.NU.NU.NE.ND.ND.51.RF#XL#XM.4A.RB.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU#XN.Ts#XO.NU.NU.NU.NU.NU.NU.NU.NE.ND.NE.7o##W.8A.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#p4.NE.NE.NE#hL.PB#aE.WE.3A.Nq.PA#aE#lR#XP#Nj#XQ#XR#XS#XT.Me#XU#XV#XW.3k#XX#XY#XZ#X0#X1#X2#X3#X4#X5#Ja#U2#U2#U2#dd#tr.4x.4x#tr#zG.4x#Be#Bg#Wi#fw#br#br#br#ul#xb#fe#tq#DK#th#tp##L#fe#xb#ul#Nh#tq#bp#fp#kk.3g.3h.3g.3g#zG.3g.3g#tr.3g#ji#ul#cn##L#ip#fs.Ny.Ny#fs.Ny.Ny.NZ.PA.0X#th.QI#X6#w4#Gz#PF#MS#lr#ta.K7#Sv#X7#pJ#X8.S4.T0.T0.T0.T0.S4.S4.S4.T0.T0.T0.T0.T0.T0.T0.T0.MV.T0.T0#X9#Y.#Y##DJ#Xc#Xc#Xc.KG.KG.KG.Of#XR#Vo#Ya#jk#bj#Yb#aH#Gh#OQ#Yc#Yd#Ye#Yf#Yg#Yh#Yi#kN#Yj#Rn#Yk#NS#hv.6E.MX.MQ.Jd.La#hY#Uy#FR#FR#Uy#gb#Yl#Vu#wR#Uy#E2#E2#E2#E2#E2#E2#E2#wR#E2#Uz#ge#f##hY.XY.No.Oz.Oz.No.No.No.No.No.No.No.No.No.No.No.No.IW#Ym.3Z#Yn#Yo#Yp#Yq.HL.I0.Oz.No.No.No.No.No.No.No.No.Oz.I2#RI.Oz#Yr#Ys#Yt#zt#f2#cR#BS#cR#Yu#zt#rw#lK#ih#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#if#s5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fc#Fc#lJ#lJ#lJ#SS#j.#Yv#AF#rr#if#ih#ih#if#if#if#sq#j##j##j##j##j##j##j##j##j##if#if#if#if#if#ih#if#Yw#Yx#Yy#Yz#YA#YB#YC#YD",
-".XY.XY.R0.T0.OV.P9.P9.P9.P9.P9.X2.9Q.TO.Nd.UM.3Z.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Z#YE#YF#gz.0U#YG#PY#yu.8R#oF#YH#YI#e6#gb#ge#ge#ge#ge#ge#ge#gb#Ee#Yk#Yk#Xm#E2#gb#ge#ge#ge#ge#ge#ge#ge.1B#YJ#YK.VK.0U#.e.YL.NB.NU.NE.NU.NU.NU.NU.Pd.5N#fc.Pd.NU.NU.NU.NE.2c.ND.Mh.RF#YL#YM.WF.2r.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NV#YN.Oj.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.NE.TD.8x.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.ZX.NE.PS.NC#nU.PB.14#hL.4H.XJ.94#iF#pA#YO#S7#lP#Rq#YP#YQ#YR#YS#YT#YU#YV#YW.PB.PB.QI#YX.QI.QI.1x#fs#YY#.e#Lr#C8#aC#DJ.4x.4x#tr#MF.3g#iF.0W#Lr#C9#ul#fw#C7#br#C7#w8#ul#fe#th#bp#y4#.a#dU#hH#jy#E#.3g.3h#Bh.3g#tr.3g.3g.3g.3g.3g#tr.3g.3k#C9.8B.4q.Pb#fs.Ny.Ny.Tu#fs#aD.9p#fw#ul.63.Ny#YZ#MR#Qz#Px#t5.MZ.M0#X8.Lq.S4.T0.T0.T0.T0.T0.T0.T0.T0.S4.S4.S4.S4.S4.S4.T0.T0.MV.T0.T0.MV#t#.9Q#Y0.3h#Xc.2p#Xc.9h.PI.N8.N8.N8.N8#Y1#Y2#Uu#fk#cr.Kt#Ml#Y3.3k#Y4#Y5#Y6.M5#Y7#Y8#Y9#U4#Z.#Yj#Ka#t7#sz#yS.ZU.6D#f##UM#GD#Uy#E2#gb#E2#FR#E2#K1#Uy#E2#gb#xm#f##f##f##ge#uI#ge#ge#TV.2R.P2.W0.Oz.No.No.No.No.No.No.No.No.No.No.No.Oz.K1.Fo.QL#Z##Za#Zb#Zc.3Z.HL.Oz.No.No.No.No.No.No.No.Oz.IW.Hz.I2#Zd#Ze#Zf#Zg#qI#f2#cR#BS#cR#cR#zt#B##Ex#ih#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##if#if#lK#HQ#s5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fc#lJ#Zh#lJ#Fd#O1#Zi#Zj#Zk#Zj#Zl#O1#QQ#SS#O3#pb#F8#Ba#yQ#RM#if#ih#if#if#if#j##j##j##j##j##j##j##j##j##j##sk#Zm#Zn#Zo#Zp#Zq#Zr#Zs#qz#Zs",
-".XY.XY.K7.K7.MO.P9.P9.P9.P9.P9.P9.TO.TO.OV.RY.3Z.UN.UN.UN.UN.UN.UN.UN.UN.3Z.3Z.3Y#Su#Zt.TA#Zu#Zv.XQ#yu#fg.qe#j7#Zw#gd#ge#ge#ge#ge#ge#gb#gd#Zx#Zy#H8#H8#Ka#gb#gb#ge#ge#ge#ge#ge#ge#gb.S3#Zz#ZA#.e#.e.0U.0U.6R.O#.NU.NU.NU.NU.Pd#ZB#ZC.Pd.NU.NU.NU.NE.Ok.ND.Sv#ZD#ZE#ZF.Sw#e#.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Ol.4A#ZG.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.NU.Tl#bu.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n.7o.NE.NU.Ol.8a.NZ.QH.3k#aD.PA#lQ#mL#ht#f##ZH#ZI.Oe#ZJ#ZK#ZL#ZM#ZN#ZO#ZP#ZQ#ZR.WD.QI.PB.NZ.Ny.Ny.P#.Ny.Qv.Tt#ZS#oN#tr.4x#tr#tr.3g.3g.3g.3g.4x#m7#ji#cm.0U#dc#.Y#ji#jy#Wh#J7.3g#J#.3h.3h#Bh.3g.3g.3g#MF.3g.3g.3g.3g.3g.3g.3g.3g.3g#tr.3k#C9.5B.Tk.Tu.NZ.NZ#aE.3A.8u.8B.9n#Hi#er.PB.KG#ZT#Gz#t8#u9#lr.0H#fU.T0.S4.O3.T0.T0.T0.R1.R1.R1.R1.R1.R1.R1.R1.T0.T0#g..S4#g.#Ab#Ab.T0#g.#X8#hj#O5#Ie#Uv#O5#O5.PI#Xd#Xd.PI.PI.PI.PI.N9.0T#ZU#ZV#ZW.NO#Yb#bB.YV#ZX#ZY#ZZ#Z0#Z1#Z2#Y9#Ru#tn#RC#Qr#Z3#De#Z4#Z5#DC#yY#Zw#ga#K1#ge#uI#ge#ge#ge#ge#ge#Uy#ge#ge#f##xm#ge#ge#f#.0H.O4.Oz.Oz.No.No.No.No.No.No.No.No.No.No.No.Oz#Xn.Fo#Z6#Z7#Z8#Z9#0.#0#.DZ.OA.No.No.No.No.No.Oz.IW.HL#UA.Ha#0a#0b#0c#L4#f2#B##cR#cR#BS#cR#f1#f2#yO#Ev#qJ#ie#lK#j##j##j##j##j##j##j##j##j##j##sq#if#ih#lK#ig#if#yQ#zv#zw#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#lJ#Zh#Fd#0d#0e#0f#0g#0h#0i#TW#Uz#0j#0k#0l#0m#0n#0o#0p#Fb#lJ#z1#id#G.#F8#0q#0r#0s#0t#rt#if#sq#j##j##j##j##sk#Zm#0u#0v#0w#0x#qz.2U.2V.2U.2U.2U",
-".XY.XY.K7.R0.Rd.P9.P9.P9.P9.TP.TP.TP.42.P9.K7.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.3Z.UN#0y#0z#m7#0A#Ma#hz#yu#mY#0B#0C#I3#e6#gb#ge#ge#ge#ge#gb#Zw#Qm#H8#H8#H8#H8#0D#wR#Jh#ge#ge#ge#ge#ge#ge.1B#0E.Tj.VK.0U#gB#.Y.YG.ND.NE.NU.NU.NU.Pd.1e#0F.Pd.NU.NU.NU.VB.ND.ND.Px#ZD#0G#ht.NS#e#.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU#zD.Po#0H.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE.ND.RR.8p.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#bw.Pd.ND.NU.ND.7o.94.LC.PA.PA.94#0I#0J#lT#Zx#wQ#0K#Hk#0L#ZK#0M#0N#0O#0P#0Q#0R#0S#0T#0U#ZI#0V.3j.NZ.NZ.Ny.Ny.Qv#fl#0W#tr.4x#tr#tr.3g.3g.3g.3g.3g.3g#Bh.3h.3h.3h.3h#J#.3g.3g.3g.3g#tr.3g#tr#tr#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g#hH#Wi.8B.5E.3h#hL.WC.YM.Qy.Uq#J8.5B#vn.14.PB.3d#0X#Qy#ss#MT#jL.P9.MQ.R1.W9.S4.R1.R1.R1.R1.T0.T0.T0.T0.T0.T0.T0.T0.T0#Ab#YO#g.#g.#Ab#g.#Ab#0Y#0Z#ji#cl#Ec#Xc#O5.PI#Xd#Xd.N8#00.N8.N8.N8.N8.N8.JX#01.L1.Pf#cb#02#03#04#05#06#Oh#07#08#u.#NP#09#pe#1.#RC#1##WJ#1a#1b#1c#Zw#NU#K1#ge#sM#sM#Nc#Nc#Cl#K1#Nc#Nc#Cl#E2#f##Uz.21.No.Oz.Oz.No.No.No.No.No.No.No.No.No.No.I2.G4.N3#1d#1e#1f#1g#1h#1i#1j#1k.Oz.No.No.Oz.IW.I1#Xn.K1#1l#UB#1m#1n#1o#f1#B##zt#cR#cR#cR#f1#f2#qH#z3#z1#Cv#sk#ig#if#j##j##j##j##j##if#if#ih#ih#ig#ig#ih#ww#Cv#so#rw#g9#h.#TL#Fc#Fd#Fd#Fd#Fd#Fd#QQ#Fc#h##1p#1q#1r#TX#gX#1s#iU#n8#Uy#1t#UN#1u#1v#1w#1w#1w#1x#1y#1z#1A#Fb#lJ#1B#1C#1D#1E#1F#1G#1H#1I#0s#if#1J#0s#Zm#1K#1L#1M#1N#1O.2U.2V.2U.2U.2U.2U.2U",
-".XY.XY.XY.K7.XY.OV.TP.TP.TP.P9.P9.P9.P9.TO.2R.UN.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN#PW#yS.PK#jj#O6#O7.XQ.8R.9y#1P#1Q.1B#gb#ge#ge#ge#ge#jM#1R#H8#H8#H8#H8#H8#H8#1S#tk#gd#gb#ge#ge#ge#ge#gb#e7#1T#1U#.e#.e#.e#dU##O.ND.NU.NU.NU.Pd#1V#fc.Pd.NU.NU.NU.5M#WS.ND#Tl.QC#1W#qR.NH.2r.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NV.17.Pe.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.WR##W.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.Z1.NE.ND.NU.NE.Ok.2n.Ny.Nq.PA#vm#1X#1w#Gn#NS.5V#1Y#1Z#10#11#ZL#12#13#12#14#15#16#17#18#19#2.#2#.O7#iG#aE.Ny.Ny.LL.4x.4x.4x.4x#tr.3g.3g.3g.3g.3g.3g.3g.3g#tr#tr#tr#tr#tr#tr#tr#tr#tr.3g#tr#tr#tr.3g.3g#tr#tr#tr#tr.3g.3g.3g.3g.3g#tr.3g#2a.8B#.f.8v#fu#vl.Mi.1f.9n.8B.5B.Uq.ZW.Tu.PB#mJ#Wm#Ka#2b#eS#wh.OV.MV.T0#X8.T0.T0.T0.T0.T0.T0#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#X8#2c.Rc#2d#OF#2e#tp#Gw.PI#O5#Xc.PI.N8.N8#2f.N8#XS#2g.N8.N8.PI#Db.4y.QP#2h#2i#2j#u##2k#2l#2m#2n#2o.3z#2p#f4#ES#2q#2r#2s#2t#1##2u#Fs#AI#2v#GD#ge#Uz#Nc#Uy#ge#ge#f##sM#E2#f##ge#hY.Jt.Oz.Oz.No.No.No.No.No.No.No.No.No.No.I0.FM#2w#2x#2y#2z#2A#2B#2C#2D#2E#2F.Fa.HL.HL#Xn.I2#2G#2H#2I#2J#2K#x9#2L#qI#cQ#cQ#cR#cR#zt#f2#qH#z3#z1#rx#if#ig#ih#ih#lK#if#if#ih#lK#ig#ig#ih#yQ#Cu#z0#id#2M#z3#yP#g7#ru#ha#lJ#z5#z5#Fd#Fd#Fd#Fd#QQ#2N#lJ#2O#2P#2Q.OJ#2R#n8#FR#Uy#2S#2T#1u#2U#2V#2W#2X#2V#2V#2X#HZ#2Y#1x#2Z#20#21#22#23#24#25#26#27#28#29#3.#3##3a#3b#3c#3d#qz#1O#cD.2V.2U.2U.2U.2U.2U.2U.2U",
-".XY.XY.XY.R0.K7.Nd.P9.P9.P9.P9.P9.X2.X2.X2.0H.UM.KT.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN#3e#3f#iJ#Lf#QZ.XQ#yu#xV#Ma.19#Z5#gb#gb#ge#ge#ge#3g#1b#H8#H8#H8#H8#H8#3h#H8#3i#Z4#gd#gb#ge#ge#ge#ge#f.#3j#gi#.Y#.e.0U#.Y.Pc.Ol.NE.NU.NU.Pd.1e#fc.Pd.NU.NU.NU.2B.NE.8m.Px.18#3k#3l.Ka.Qz.Oa.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oq.Po#e..NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.NU#.b.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B#xz.ND.NE.NU.NU.Nt.YH.Qv.PA.PA.NA#3m#w6#Wn#Du#ut#3e#3n#3o#3p#ZL#12#3q#3r#3r#12#3s#3t#3u#Tx#3v#X##3w#fp#m3.8D.Nr#Bh.4x.4x.4x.4x.4x#tr.3g.3g.3g.3g.3g.3g.3g#tr#tr#tr#tr#tr#tr#tr#tr.3g.3g.3g.3g.3g.3g.3g#tr#tr#tr#tr.3g.3g.3g#tr.3g#Bh#OF#C9#xb#.d#q2.L7.Pw#de#cn.8B.5B.5B.5B.Tt.PB#EQ#3x#Qz#AK#NS#Ee.TO.ON#Ab#g.#g.#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#g.#Ab#e5#HZ#3y#3z#fw#C7#YW#Vi#O5#Ea#Xc.N8#2f#2f.PI.N8#Ec.N8.N8.N8.N8#3A#.d#bs#Lr#XB#Qz#3B.UO#yY#Gq#3C.PB#J.#3D#3E#p8#3F#3G#3H#wA#Z.#2u#Fs#HZ#UM#f##ge#Uy#E2#ge#ge#sM#K0#GD#GD.MX.P2.N3.Oz.No.No.No.No.No.No.No.No.Ha.I0.FM#3I#3J#3K#3L#3M#3N#3O#Nt#3P#Qd#uU#Jm#OT#3Q#3R#3S#3T#3U#3V#3W#3X#3Y#yK#cR#cR#AA#zt#f2#ba#ru#z5#rx#lK#ig#3Z#30#31#32#33#34#35#qF#rx#j.#Gb#sl#z3#yP#nt#zt#f2#Cw#TK#UG#rw#JE#JE#z5#Fd#Fd#Fd#Fd#QQ#Fd#lJ#36#37#38#39#4.#Vu#E2#4##2T#1u#1v#2U#2V#2V#2V#2U#HZ#2U#4a#2Y#2Y#2Y#2W#4b#4c#4d#4e#Zq#Zs#cD#cD#cD#4f#0x#4g#4h#4i#4j#0x#1O#cD.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U",
-".XY.XY.XY.K7.R0.UP.X2.X2.X2.X2.X2.X2.X2.X2.X2.XY.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN#UQ#Wu.Oc#KT#4k#PY#yu.8R#oE#4l#Jc#1R#gd#Nc#ge#gb#gd#kV#H8#H8#H8#H8#H8#H8#Hb#H8#4m#E2#gb#ge#ge#ge#ge#gd#2v#4n#ep.0U#.e#.e.9p.O..ND.NU.NU.Pd#c##fc.Pd.NU.NU.NU.2B.NE#.n.Mi.18#4o#ga#4p.WG.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.O#.6T.Ur.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.NE.Qy.5B.8B.8B.8B.8B.8B.8B##M.Uq.PL.ND.NU.NU.NU.ND.RR.Nq#Bh.XJ.9h#4q#f##xY#qr#Er#4r#4s#Wr#4t#4u#0O#4v#4w#3r#3r#3r#0O#ZK#J6#4x.VM.8##DJ.4x#tu.63#tr#tr.4x.4x#tr#tr#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g#tr.3g#Bh.3g#Eb#4y#ul#br#Fi#.R.NE.Z1.8B##M.8B.8B#.Z#jh.Ny.PB#4z#RC#Wm#Ho#Z4#sM.6q#D4.T0#X8#g.#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#g.#e5#4A#4B#4C##L#fw#4D#cl#4E#O5#Xc#Xc.PI#2f.N9#ZV#4F#4G#4H.N8.Md#Ec#4I#Ml#.f.8u#mh.4w#4J#RB#Wn#ZR.PB#aD.7e#4K#4L#4M#4N#4O#4P#4Q#4R#4S#kW#4T#1w#4U#K1#ge#ge#wR#GD#FN#wR#ge.MQ.No.Oz.Oz.No.No.No.No.No.No.No.No.Tf.FM#4V#4W#4X#4Y#4Z#3O#yG#Qd#Iu#Nt#uV#Ns#Ns#tS#40#41#42#43#44#45#Dq#46#E7#ba#cR#cR#f1#ba#ru#rw#BQ#sk#ig#ih#30#47#48#49#5.#5##5a#5b#5c#5d#5e#cR#f2#qI#f2#zt#nt#TK#z2#Fb#JE#JE#z5#Fd#Fd#Fd#Fd#Fd#Fd#QQ#Fc#h##5f#5g#5h#gX#FR#Uy#5i#2T#1u#2U#5j#2V#2U#2U#2U#2V#2Z#2X#5k#2W#2W#5k#5l#5m#5n#5o#5p#qz.2V.2U.2U.2U.2U.2U.2V#qz#0x#rm#qz.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U",
-".XY.XY.XY.XY.R0.Nc.X2.X2.X2.X2.X2.X2.X2.X2.X2.0H.UM.KT.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN#PW.7e#m7#5q#XD.XQ#yu#xV#5r#5s#5t#gd#gb#ge#Jh#jL#V1#H8#H8#H8#H8#H8#H8#H8#3h#Hb#e3#gd#gb#ge#ge#ge#gb#e5#5u#5v#.e#.e.0U#.e##W.Oq.NU.NU.Pd#5w#fc.Pd.NU.NU.NU.NU.VB.5M.Px.QC#5x#ga#5y#bm.Ok.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oq.Pp.Ur.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.NE.Qy.5B.8B.8B.8B.8B.8B.8B.4s.NE.NE.NU.NU.NU.NE.Ol#dd.QH#vm#5z#qR#qr#qr#Eq#Eq#qr#5A#LQ.1x#5B#5C#ZK#12#5D#3r#5E#5F#0O#11#5G#5H#5I.PH.4x#tr#tr.4x.4x.4x.4x#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3h#5J#5K#5L##U#5M.RO.62#dZ.Pw.Qy.8B##N.8B.8B.5B.9n.6P.PB.Ny#5N#5O#kW#Z5#MU.X2#f..Rc#g.#X8#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#g.#e5#gb#5P#kk#db#fw#ul#fw#YW#Vi#O5#Xc#Xc#2f#Ec#5Q#hg#5R.LW#1Z.N9.N8.PI#G8.YU#.f.9n#bu#ul.0V#aB#5S.PB.Kv#5T#5U#5V#5W#5X#5Y#5Z#50#51#52#53#Yj#54#55#Zw#GD#wR#Nc#DN#K0#wR#E2.0f.OC.Oz.No.No.No.No.Ha.No.No.Oz.MR.Gp#56#57#58#59#3Y#6.#yG#Iu#Iu#uV#uV#Ns.56#Pm#6##6a#6b#6c#6d#6e#6f#yK#yK#ba#b2#cR#f2#6g#6h#j.#HQ#mH#6i#6j#47#sq#6k#6l#6m#6n#6o#6p#6q#6q#6r#6s#6t#cR#qH#z4#6u#Xu#h##z5#JE#z5#QQ#Fd#Fd#Fd#Fd#Fd#Fd#QQ#Fc#lJ#2O#2P#4##1s#n8#Uy#1t#UN#5k#2U#2V#2V#2W#2V#2V#2V#5j#2V#5k#2V#6v#6w#6x#6y#6z#6A#6B#nn.2W.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U",
-".K7.K7.XY.1J.K7.XY.0H.X2.X2.X2.X2.X2.X2.X2.X2.X2.MV.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.UN#0y#ii#.Y#JL#6C#O7#yu.8R#6D#6E#6F#ge#n8#ge#gd#gb#V1#3h#H8#H8#H8#H8#H8#H8#H8#H8#lT#ge#gb#ge#ge#ge#ge#e7#qa.Z0#ep.0U#.e#.Y#nU.ND.NE.NU.Pd.4Q#0F.Pd.NU.NU.NU.NU.5M.NE.97.QC#6G#qR#6H.Kt#.n.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND##A#iL.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.NE#bt.8B.8B.8B.8B.8B.9n#.X.ND.NU.NU.NU.NU.NU.Ok.QO.PA.3f#6I#6J.3Y#qr#Eq#Eq#Eq.3Y#tG#6K#6L.JO#6M#6N#0O#0N#12#12#ZL#ZK#5C#5G#6O#Tx.4x.4x.4x#tr.4x.4x.4x#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g#zG.3g#Bh#tr#OF#B1#gi#6P#6Q#6R#6S#6T#6U#6V##V.5B##N.8B.5B.8B.9n#xd#lQ.PB.WQ#6W#U5#6X#TB#E2#e4#e5#Ab#g.#g.#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#X8#D4#e5#6Y#6Z#HY#xb#ul#ul#ul#Ie#O5#O5#Ea#2f#2f.9h#60#61#62#63#64.JO#2f.PI#ZV#bs#ul.9n.8w.9n#.c.5B#hf.Mv#65#66#67#68.N9#69#Ea#wC#ZU.0R#7.#Gn#Qm#V1#Yk#pj#ga#ge#ge#Uy#wR#gb.XY.Oz.Oz.Ha.Ha.Ha.Ha.Ha.SX.6a.OG.4##7##7a#7b#7c#7d#7e#BO#7f#3V#uV#uV.56#7g#6##7g#7h#7i#7j#7k#7l#7m#7n#ba#cR#cR#cR#cR#7o#yb#7p#7q#7r#47#if#7s#7t#7u#7v#7w#7x#7y#7z#7A.M4.OZ.SU#7B#7C#7D#Ex#h.#JE#z1#TM#7E#7F#7F#7F#7F#QR#z5#TL#Fd#QQ#Fc#lJ#Fd#7G#7H#1s#n8#Uy#7I#UO#2U#2V#2V#7J#5j#2X#7J#7K#7K#7K#7K#7K#7L#6x#6y#6x#5n#7M#7N#7O.5d.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U",
-".MV.XY.K7.S4.S4.W9.MV.VX.X2.X2.X2.X2.X2.X2.X2.X2.VX.O4.KT.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN.KT#kE#7P#m7#7Q#rY.XQ#yu#R1#7R#7S#7T#gd#ge#gb#gd#7U#H8#H8#H8#H8#H8#H8#H8#H8#3h#7V#e3#gd#Uz#ge#ge#ge#gb#e5#7W#7X#.e#.e#.e#xH##O.ND.NU.Pd.VA#fc.Pd.NU.NU.NU.NU.2B.VB.Ur.QC.8c#7Y#7Z.52.Oq.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oa.Pf#70.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.Pd.8p.8B.8B##M.8B.Qy.Qs.NE.NU.NU.NU.NU.NU.NE.ND.Oc#71#72#73#74#Eq#Eq#Eq#Eq#Eq#75#st#76#DJ.JO#5H#5G#6N#77#78#ZK#ZK#5C#5G#5H.JO#J..4x.4x#tr.4x.4x#tr#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g#tr#cm##T#ck#79#8.#8##8a#8b#8c#OG#vc#8d#8e.8B.5B.8B.5B.5B.Uq#dU.PB.PB#8f#Rn#Rn#Z5#4U#e7#f..W8#YO#X8#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#X8#g.#D4#nS#8g#gi.YG#fw#ul.64#fe#WG.2p#O5#Ea#Xc.N8.N8#Ec#OQ#XS#8h#8i#4H#Ec#Ec#8j.05#8k#.Y.4r#.c.Uq.Uq#w8#8l#8m#8n#8o#69.PI.PI.PI.N8.N8.5z#8p#8q#Hb#Yj#Zx#1c#4U#f##Uy#ge#gb.O3.Oz.Oz.No.No.No.Oz#8r#oY.Qo#8s#8t#8u#8v#8w#8x#8y#Cf#yL#8z.7r#Ov#8A#6##8B#8C#8D#8E#8F#8G#8H#8I#bb#AA#b2#cR#cR#b2#AA#8J#8K#8L#8M#8N#8O#8P#8Q#8R##p#8S#8T#8U#8V.V1.M5.M4.M4.M4.M4#8W#8X#8Y#8Z#80#81#7E#82#83#84#85#86#87#88#89#9.#81#9##Fd#9a#9b#4..Q8#E2#NU#UO#2V#5j#7J#9c#7K#7K#7K#7J#7K#7K#7K#7K#5j#9d#9d#9e#9f#9g#9h.5e.5d.2W.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U",
-".X2.0H#Aq#CJ#zP#zP#zP.ON.X2.X2.X2.X2.X2.X2.X2.X2.X2.ON.UN.KT.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN#9i#9j#xH#KT#9k#O7#9l.8R#oD#9m#Ih#e7#gb#ge#iu#54#Zy#H8#H8#H8#H8#H8#H8#H8#H8#H8#Y.#gb#gb#ge#ge#ge#ge#e4#9n#bB#.Y#gB.0U.VK.9n.Ol.NE.Pd#9o.52.Pd.NU.NU.NU.NU.2B.NE.Pe.RF.Pz#9p#9q#c#.NV.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Ps.Pw.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.RR#.Z.8B.8B#de.NU.NE.NU.NU.NU.NU.NU.NU.NU.O##.S#OG.98#9r#9s#Er#Er#Er#Er#Er#qr#5A#9t.3j#J..PH#5B#5H#5G#YQ#5C#5C#YQ#9u#6O#Tx#J..4x.4x.4x.4x.4x.4x#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.XJ.XJ.XJ#aD#cm#9v#9w#9x#9y#9z#9A#9B#9C#9D#9E#9F#DE#cn#.f.5B.5B.9n##P.3g.PB.0T#9G#9H#Ho#Z4#E2.1B.OO#Ab#g.#g.#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#g.#Ab#nD#9I#9J#Lr#xb#ul#ul#xb#Wi#B0#Xc#Ea#O5#Ea#Xc#Uv.N8.N8.N8#9K.JW##z#9L.SI#yg.Kp#9j#9M.8o#9N#gi#aG.Uq#Ml#9O#9P#9Q#9R.2C.Of.Mc.PI.N8.N8.PI#9S.WB#I3#1##Sw#e3#4U#f##wR#Uy.T0.3Z.UN.No.UM#qr#9T.P3#9U#9V#9W#9X#9Y#9Z#8J#90#E7#91#Ce#92#zp#93#94#95#96#97#98#99a..a.#a.a#8I#AA#b2#cR#cR#b2#AAa.b#bba.ca.da.ea.f.7R.6Aa.g.6A.9U.7Wa.ha.i.6h.V1.M5.M4.M4.V2.M4.Qga.ja.ka.la.ma.na.oa.pa.qa.ra.sa.t#dKa.ua.va.wa.xa.ya.za.A#e7a.B#4.#K1a.C#1v#2V#9ca.D#9c#7J#7K#7J#7K#7J#7K#6w#9d#6ya.Ea.Fa.Ga.Ha.I.5e.2W.2U.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U",
-"##k.X2.0H.UZa.J#zP#CJ.UZ.VX.X2.X2.X2.X2.X2.X2.X2.X2.X2.MV.3Z.Qd.UN.UN.UN.UN.UN.UN.UN.UN.UN.3Za.Ka.L#mg##.a.M#hz#yu#yu#lYa.N#FN#gd#ge#gd#4T#H8#H8#H8#H8#H8#H8#H8#H8#H8#Hb#I3##t#gd#ge#ge#ge#ge#gb#e6#Fwa.O.3A#.e.YL#dd.O8.NE.Pd.VA.YJ.Pd.NU.NU.NU.NU.NU.5M.NV##H.Pha.Pa.Q#.N.Pw.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.18.97.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE#.b#.f.8x.WR.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.NV.Pe#AI.3Y#Eq#Eq#Eq#Eq#Eq#Eq.KT.O4#c.a.R.4x.PH#ZU#5B#6O#J6#9ua.S#5B.JOa.T#tr.4x.4x#tr#tr.4x.4x#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g#DJ.3g.3g.3g.3g.XJ.XJ.XJ.XJ.XJ.XJ.XJa.U.XJ.3h#OE#jza.Va.Wa.Xa.Ya.Za.0a.1.8a#kma.2a.3#Wf#ul#cn.5B.5B#.Z.9s.NZ.PBa.4#9Ha.5#NS#Ee.9Q.ON.Nc#Ab#X8#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Aba.6a.6a.6a.6a.6#g.#X8#Ab#e7#gea.7#ul#xb#ul#xb#xb#xb#iq.0W.9h#O5#O5#Ea#Xc.N8.N8.N8.N8#9K.Of#Hka.8a.9a#.a##.CGa#aa#ba#c.PB.6P.9s#Uua#d#eKa#ea#f#hM#gC.8ea#g.Md.JXa#ha#i.Mma#j#Y.#Pt#1R#Zw#wR#K1.MO.No.UN.UM.UNa#k.OFa#la#ma#na#oa#pa#q#yKa#r#yO#yKa#sa#ta#ua#va#wa#xa#ya#za#Aa#B.7Ta#Ca#Da.aa#E#BS#cR#cR#b2#AAa.ba#Fa#Ga#Ha#Ia#Ba#J.9U.7X.7Xa#K.7X#r8a#La#M.V1.V1.M5.M4.M4a#N#8V.M5#bJa#Oa#Pa#Qa#Ra#Sa#Ta#Ua#Va#W.7za#Xa#Ya#Y#h0a#Za#0a#1a#2a#3a#4#Jia#5#5k#7J#5j#7J#9ca.D#7K#9c#7K#7J#7K#6x#6y#2Va#6a#7#7N.2U.2X.5d.2U.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L#tKa#8a#9a#9a#9aa.#tK.1L.2U",
-".ON.X2.X2.VX.Nc#zP#CJ#zP.OO.X2.X2.X2.X2.X2.X2.X2.X2.X2.VX.S4.3Z.3Z.UN.UN.UN.UN.UN.UN.UN.UN.UN#UQ.72aa##jjaaa#O7.XQ.8R#6Daabaac#e7#gb#Jhaad#Hb#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8aae#gd#gb#ge#ge#ge#ge#e5#Fi#im#.e#.e.0U#kn.XF.O#.Pd.9eaaf.Pd.NU.NU.NU.NU.NU.2B.Oq.4S.Pkaagaah#n..WG.5M.NU.NU.NU.NU.NU.NU.NU.NU.NU.NEaaiaaj.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE##V.Qy.Om.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.RA.RE.O4.3Z.UM.UM.UM.UM.UM.UM.UM.5U.OLaak.PBaal.3f.14.8##3o#3oaam.PH.4x.4x#tr#tr.4x.4xaan.4x#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g#DJ#tr#tr#tr.XJ.XJ#tr#aE.Pa#vm.3f#OE#lQ.3haaoaapaaqaaraasaataauaavaaw#G9aax#e5aayaazaaA#ul.8B.9n.5B.Uq#xF.PB.PHaaB#Ho#Je#Zw#E2#e4.V6.MV#g.#g.#Ab#Aba.6a.6a.6a.6#Aq#Aq#Aq#Aq.T0.T0.T0.S4.XY.OO#E2#e3aaC.Oq#C8#ul#xb#xb#xb#xb#w8.5A#Ec#O5#O5#Ea#Xc#Uv#Xc#Ec#Ec#9K.Of.Y1#OQaaDaaEaaFaaG.41.1A#rz#PD#jz.PBaaHaaIaaJaaKaaL.9n.Uq.UqaaMaaNaaOaaPaaQaaRaaS#U5#Sw#e3#Zw#GD#gd.RY.UN.UM.41aaTaaUaaVaaWaaXaaYaaZaa0aa1#VEaa2aa3aa4aa5aa6aa7aa8aa9ab.ab##o6abaabbabcabdabeabf#cR#BSabg#8Jaa1#f1abhabiabjabkabl#lB.7X.7X.7X.7X.7Xabm#a4abn.M5aboabpabqabpabrabsabtabuabvabw#tAabx##p.7W.7W.7Y#sG#sHaby#a8#dxabzabAabBabC.9I#uF#h8#GGabDabE#6w#6w#6w#5j#5j#5j#5j#7K#6w#6y#6y#9eabFabGabH.2X.5d.2W.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1Laa..2UabIabJabKabKabLabMabNabOa#8",
-".MV.VX.X2.X2.X2.ON#Aq#CJ#Aq.VX.X2.X2.X2.X2.X2.X2.X2.X2.X2.VX.R0.KT.3Z.UN.UN.UN.UN.UN.UN.UN.UN.3Z.5X#cj#m7abPabQ#Q0#yu#yuabRabSabT#gb#gd#f##Z.abU#H8#H8#H8#H8#H8#H8#H8#H8#3h#RtabV#gd#ge#ge#ge#ge#e6#jx.XS#.Y#mh.0U.VK#br.Oq.Pd.XHabW.Pd.NU.NU.NU.NU.NU.NU.O#.Vz.Pl#XMabX.Pp.6X.5M.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.QC.Ur.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.NU.5OabY.5U.UN.UM.UM.UM.UM.UM.UM.UN.UN#DuabZab0ab1ab2ab3ab4ab5ab6#HYaal.Mz#Wg.4x.4x.PA.PA.PA.PAaan#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g#tr#tr.PAa.Ua.U#Bhab7ab8ab9ac.#Sqac#acaacbaccacd#Gqaceacfacg.3e#eq#cnach#f..Z#aciacj#fw.5B.5B.9n.8w#oN.PBack#5t#6X#Z4#GD#e7.T1.Rc#Ab#g.#g.#Aq#Aq.T0.T0.T0.T0.T0.T0.R1.R1.R1.XY.XY.0f.9Q#ge#54#gf.Pi.8p#aF#xb#xb#xb#xb#xb#w8acl#Vi#O5#O5#Ea#Xc.PIacmacnaco.Md#2g.Y1.4xacpacqacracs#oY.Q1.VV.T1actacu.Odacvacwacxacy#.c.Uq#hM.QQacz#mpacAacBacCacD#Zy#w6#AI#4U#f#.T0.3Z.3ZacEacFacGacHacIacJacKacL#g6a#F#7macMacNacOacPacQacRacSacT#o6.4b.7I#.z.7I.4b#o6acUacVacWa#FacXacYacZac0ac1ac2ac3#o6ac4aba.6B.7Xa#K.7X#r8ac5#a6ac6ac7abq.48ac8ac9ad.ad#ada#aeadb#a5#af#ae.7W.7W.7W.7W.7W#oV#ae#ae#af#af#ae.7W#cJadcabyadd.X5.W3adeadfadgadhadiadjadkadkadla.Eadmadnadoa.H.1O.5e.2W.1L.1L.1L#.z#.zadp#o6#o6adp#.z#.z.1L.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1Laa.adqadradsa#Y#uF#uFadt.9Iaduadvadw",
-"#zP.ON.X2.X2.X2.X2.VX.Nc#zP.Nc.VX.X2.X2.X2.X2.X2.X2.X2.X2.X2.0H.VV.KT.3Z.UN.UN.Oz.Oz.Oz.Oz.Oz.Oz.KT.RYadx.WDady#mY.XQ.8R#ytadzadA.0g#gb#gd#V1#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8adB#gd#gb#ge#ge#ge#gb#huadCadD#.e#.e.YL.9q.NU.NU.Uu#fd.Pd.NU.NU.NU.NU.NU.NU.ND.RG.PyadEaag.NM.YJ.O#.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.RF#e..NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.NE#c0adF.5V.UM.UM.UM.UM.UM.UN.3Z.K7#6X#Qm#QmadG#WJ#4madHadIadG#1##daadJ#.2.3h.LL.PAa.U.XJ.XJadK.PA#tr.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.3g.XJ.XJ.94adLaaSadMadNadO#7UadI#d3adP#4madQadRadS.PA#fm#OjadT#GD#gd#g#adUadV#br.9n.5B.5B#cn#zG.3fadW#Xm#NS#tk#jL.0H.Nc.M0.Nf#t#.T0.R1.R1.R1.Xz.Xz.Xz.Xz.Xz.0f.0f.XY.K7.Rd#sM#gbadX#GnadY#c2#ul#xE#xb#xb#xb#xb#ul#iq.Oc#Ec#O5#O5.PI.N8.PI.PP.IM#.OadZ.JY.N8.14ad0ad1ad2ad3.UM.X1#iU#E2.T1#w6ad4ad5ad6ad7#3zad8#bs#.c#NEad9.1Cae.ae#aeaaebaec#HZ#1w#4U.OV.UM#kE.SYaedaeeaefaegaehaeiaejaa1abgaa1aekaelaemaenaeoaep#.z.4b#uOa.Iaeqaer.1O.7I.7I.2V.1L#rjaesaetaeuaev.1Oaewaex#o6#.z.2V#.z#.zaey#mn.7XabmaezaeAaeBabraeCaeDaeE#cJadbadb#a5.7W.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W.7W.7W#a5#bSaeF#DT#h0aeGaeHaeIaeJaeKaeLaeMaeNaeOaeP.1M.82.5d.1L#.z.1L.2UaeQaeR#sUaeSaeTaeyabd#o5aeU#sS.1O#o6#.z.1L.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1LaeVaeWadsadt.9I.9I.9I.9I.9I.9I.9I#dIadt",
-".W9.UZ.VX.X2.X2.X2.X2.X2.ON#Ab#e5.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.0H.VV.KT.3Z.Oz.Oz.UN.UN.UN.UN.UN.UNaeXaeYaeZ.TAae0#O7.XQ.8Rae1ae2.S3#gb#gd#x.#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8ae3#ET#gd#ge#ge#ge#ge#f.#jm.8u#.e#mh#iF.Tk.5C.NE.52#iD.Pd.NU.NU.NU.NU.NU.NU.NE#c#.Tr#XLae4.L2#yX.Oq.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.RF#0H.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NUae5.5U.UM.UM.UM.UM.UM.3Z.XYadH#2t#H8#H8#H8#H8#H8#H8#H8#H8#Hbae6#j9#H8ae7ae8.Nz.XJ.PA#5Iae9#J..XJ#tr.3g.3g.3g#tr#MFaf..3g#DJ.3g.3g.3g.3g.3g.3g.XJ#tr.LK#ce.KFaf##QBafa#1#adGafbafcafd.Md.Nq.PI#aB#Wfafe.T1#TV#D4aff#T8#.f.5B.5B.9n#fe.1xafg#PF#t7#sz#IH##k.UP.21.Xz.XY.XY.Xz.Xz.0f.0f.0f.0f.0f.0f.0f.XY.XY.K7.UP#f##gbaad#Z.afh.Ph#bm##L#ul#xb#xb#xb#xb#ul#WU#Ie.9h#O5#O5#Xdafi.N8.PI.Md#el#l..Piafj#4Hafkaflafmafn#qX#4.#wR#sM#wR.0hafo#e3afpafqafr#Eaafsa.8#FHaftafuafvafwafxafyafzafA#Zw#f##pJ.P0afBafCafDafEafFafGafHafIafJafKafLafMafNafOafPafQ.7I#uOaeQafRafSafT#r8#mnafUafVaba#o6#.z.1L#rj#rk#wk#rj.1L#.z#.z.2V#.z#.z#uO#.zafWa.hafXafYafZaf0aeDaf1#a5af2#a5.7W#kG.6B.7W.7W.7W.7W.7W.7W.7W.7W.7W.7W.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W#ae#uBaf3.Y6#eFaf4af5af6af7af8af9ag.ag#agaafUagbagcagdageagfaga#mn.7W.9U.9U#nk#nk.9U.6B#pQ#qx#wjagg.1O#o6.1L.2U.2U.2U.2U.2U.2U.1Laa.#tKaghagi#dI.35.9I.9I.9I.9I.9I.9I.9Iagj.9I",
-".S4.S4#e5.X2.X2.X2.X2.X2.X2.VX.Nc.0H.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.VX.K7.3Z#qr.UN.UN.UN#Eq#Eq#Eq#Eq#Eq#75#ve#biagkagl#PY#yu#yu#v8agm#qW#gb#tk#Qm#Hb#H8#H8#H8#H8#H8#H8#H8#H8#U4#H8agn#gd#gb#ge#ge#ge#gd#1wago#ft#.e#mh.VK#.V.ND#fc#df.Pd.NU.NU.NU.NU.NU.NU.NE.Uu.YIagp#R6#gk.RG.NC.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE##H#0H.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Pd.Kt.5Q.KT.UM.UM.UM.UM.UN.UM#9H#2t#H8#3h#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#nSagq#Spagr.94#aE#D8agsagt#NB.XJ#tr.XJ#zGaguagvagwagxagy#D9#DJ.3g.3g.3g.3g.3g#DJ#J.agz#YPagAagBagCagDagEagFagG#FH.KG#2g.KG.Nx#ul#2dagH#kV#D4agIadV.8B.5B.9n.5B#er.Ny#he#wx#MS#t5.5Q.MO.Rd.0f.XY.XY.0f.0f.0f.0f.0f.0f.0f.0f.XY.XY.K7.2R#Zw#Fs#4U#ge#Y.#zB.SA.RCagJ#tq#xb#xb#xb#xb#xb#fw#ul#JL#O5#O5.2p.PI#Xd#Xd.N8.PI#Ec.MmagK.H2agLagMagNagOagPagQ#E2#Vu#E2#E2#ge#4.#gbagRagSagTagUagVagW.0TagXagYagZag0ag1ag2ag3#Cl#4U#gb.R1#HKag4ag5ag6ag7ag8ag9ah.abfafLah#aha#saafQ.1Q#.z#uOaeRahbaga.7X#lB.6B.7X.7X.6B.9UahcaeRahd.4baheahfahgahh#uO.4b#uO#uO#.zahiahjahk#a5#a7ahlahmahn#cJaf2#ae.7W.7W#ae#af#af#ae.7W#cIahoahpahp#sI#uxahqadc#sI.9.#af#af#oV.7W.6B.6B.6B.6B.6B.6B.6B.6B.7W#bS#uwahra#Xahsahtahuahv#tI#nk.9U.9U.9U.9U.9U.9U.9U.9U.9U.9U.6B.6B.6B.6B.6B.6B.6B.6B.6B.9U#nk.6BagfaeS#tK.1L.2U.2U.2U.1Laa.#tKaeWahw.9Iadtahxahxahx.9I.9I#bWahx.8W.8Wahx#bW",
-".T0.W9.T0.VX.X2.X2.X2.X2.X2.X2.VX.S3.VX.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.VX.T0#qr#qr#Er#Eq#Eq#Eq#Eq#Eq#Eq#qrahyahz.WDahA#0B#hz#XD#v8ahBahC.S3#Jh#V1#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#Hb#Ru#Hp#gd#ge#ge#ge#gb#e5#nCahD#.e#mh#.Y.Oo.NU#fd.4B.Pd.NU.NU.NU.NU.NU.NU.NU.4BahE#7Z#q..J8.8b.Pw.NE.NU.NU.NU.NU.NU.NU.NU.NU.ND.6W#ZG.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.2##d8.RY.3Z.UM.UM.UM.UN.KT#tkahF#4S#4m#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8ahGadGahH.WDahIahJahKahLahM.XJ.PA#zG#D9ahNahOahPahQahR#MF.3g.3g.3g.3g.3g.5zahS#D8#6M#5CahTahUahUahVahWahXahYahZ#Hk.N8.4q#yn#Spah0#Qm#e6ah1ah2#bs.9n.9n#xE.P#.9d#rA#MS#MT#lr.MY.Rd.MN.XY.K7.XY.0f.0f.0f.0f.0f.0f.XY.XY.K7.MV#Ee#V1#V1#ga#gbah3adGah4.Pk.3B#bv#DE#xb#xb#xb#xb#xb#ul#ul#FG#bo#Xc#O5.2p#Xc.N8.N8.PI.PI.PI#Ec#Ecah5.Nm#WKah6ah7#Vu#4.#4.#sM#wh#wh#E2#E2#n8#e7ah8ah9ai.ai#.HQ.0Taiaaibaicaidaieaifaig#f#aig#wR#E2#e8aihaiiaijaikail#bbaimaevain#rj.1L.2V#.z#uOaioahc#xo.7X.7Xa#K.7X.7X.7X.7Xa#K.7XagaaipaiqairaisaitaiuaivaiwaixaiyaizaiA#uzaiBahmaiCaiD#aeaiE#a5#af#a5.7W#sIaiFaiGaiHaddaiIaiJaf3aiKaiKaiKabCaiLaiLaf3aiMaiN#uB#tA##paiOaiPaiQ#.G.5p.6B#kG##p.7W.6B#ae.7Xadd#bVaiR#lAaiP.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7X.6B.9U##paiS.1L.1L.1Laa.#tKaiTaiU.35#uFahxaiVaiW#cLaiXaiYaiYaiGaiRaiZai0ai1abB",
-".T0.S4.S4.OO.0g.X2.X2.X2.X2.X2.X2.VX.VX.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.Nc#Eq#qrai2#Eq#Eq#Eq#Eq#Eq#Eq#ut#Eqai3#LMai4#mY.XQ.8Rae1ai5#q.#e6#NS#H8#3h#H8#H8#H8#H8#H8#H8#H8#H8ah0adNai6#gd#gb#ge#ge#ge#e4#nS#.c#ft#.e#.e#cl.RR#fd.99.Pd.NU.NU.NU.NU.NU.NU.NU.Tq.4Aai7#f#ai8.QC.Mi.ND.NU.NU.NU.NU.NU.NU.NU.NU.NE.4S.NC.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE.5O.Ij.5U.UN.UM.UM.UM.3Z.K7ahG#dZ#qR#dZ#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#Hb#j9ab3#gzai9aj.aj#aja.3ga.Uajb#zGajcajdajeajfajg#EL.3g.3g.3g.3g.3g.5zajh#J6#11#ZKajiajjajkaji#77ajlajmajnajo.Of#Hjajp#lT#Hb#H8#GDajqadV.8B.9n.5B#eq#rSajr#IH#MT#vW.MZ.MQ.MN.0f.XY.XYajsajsajsajsajs.XY.XY.0f.2R#jL#Pt#Hb#V1#ge#gd#kW#Z.ajt.RC.RC.L6#v5#xb#xb#xb#xb#xb#xb#xb#ul#ul#ul#bl#JL#Ec.PI#Xc.N8.N8.KG.N8#9waju#tn#D4#4..7v#4.#Vu#Vu#E2#E2#ge#ge#ge#E2#E2#Vuajv#e4#Htajw#2macbajxajy#j8ajz#HV#E2#GD#ge#K0#ge#K1#K1#E2.Sg.7vajA#AAajBajCajDajEafQ.1L#.z.7IajF.9U.6B#r8.7X.7X.7X.7Xa#Ka#K.7Xa#K#r8#a5.6BajGajHajIa#NajJad#ajK##pajLajMajNajOajPajQaiHajRaiFabyabwaeFai0aiWaiK.35ahx.8W.8Wahx#bW#bW#dI#h0#eFajS##4#h8##4##4ajTa#YajUajVajWajXajYajZaj0aj1.7T#a3aj2.5o.6AaiQaj3aj4#dA#dzaby#a4#ae.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W.7W##p##paj5aj6#tKadqaj7.35adt#bWabCaj8#cJ.7W#oV#af#ae#ae#ae#af#af#af#oV#cI",
-".T0.T0.W9.T0.0H.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2#ta#qr#qr#Eq#Eq#Eq#Eq#Eq#Eq#Eq#9s#NRaj9#jy#HS#Od.XQ.8Rae1ak..0g#jLagq#H8#H8#H8#H8#H8#H8#H8#H8#H8#Hb#Hbak##Hp#gd#ge#ge#ge#gd#e7akaakb#.e.0U.0U.6R#fc.4B.Pd.NU.NU.NU.NU.NU.NU.NU.Sv##Aakc#geakd.Pp.Qz.ND.2B.NU.NU.NU.NU.NU.NU.NU.NU.8b.NV.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE#zA#c0#.i#9s.UM#HK.UN.UN.KT#OS#Qm#ga#4Uake#3h#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#Hb#lTakfakgakhakiakjakk#DJadKa.U#MF.3gaklakm#MF#MF.3g.3g.3g.3g.3g#DJahS#D8#4u#ZK#0Oajjaknako#78aknakpakqafi.PNakr#5t#Qm#H8#H8#qRadUadV#cn.8B#ul.3jaks#t5#MT#tG##k.MQ.Rd.0f.XY.XY.0f.0f.0f.0f.0f.R1.O3.MN#jL#Fs#d3#2t#7U#gb#gd#kV#H8#Yk#S5.Pl.Pz.Pp#br#ul#xb#xb#xb#xb#xb#xb#xb#ul#ul#fw#aF#dbaktakuakt.Of.Y1.Y1.0T#8p#e3#2R#E2#E2#Vu#Vu#E2#Vt#TC#TC#TC#TC#Uy#Uy#Vu#Uy#E2#Vu#TU#TU#TU#TU#FR#TCakv#NU#f##wR#Uy#K0#wh#1s#n8akwakxaky#yKakzaa3akAakBakCakDakEakF.2VakGakHage.6B.7X.7Xa#K.7Xa#Ka#K#r8akIaezakJakK.V1a#N.OZ.49akLahnakMakNakOakPakQakRakS#dDakT##4.6eajSajSajSajTadt#bH#eF.6e#h8#h8.W3#bH#eGakUakVakWakXakYakZak0#FO#hZak1ak2ak3ak4ak5ak6ak7ak8ak9al.al#alaalbalcaldalealfalgalh.35aliajR#ae#af.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W#af.6B#cI#cJaljalkallalm.6fadt.9I.9Ialn#uAadb.7W.7W.7W.6B.6B.6B.6B.6B.6B.6B.7W.7W",
-".T0.T0.S4.W9.Nc.VX.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.X2.OL.OL.OL.OL.OL.VX.Nf#qr#qr#Eq#Eq#Eq#Eq#Eq#Eq#Eq#0yalo#WF.6Palp#Od.XQ#yu.WMalq#qW#ge#7U#H8#H8#3h#H8#H8#H8#H8#H8#H8#Hb#2talr#gd#gb#ge#ge#ge#nDals#EA#er#.e#.Y#mi#fd.NT.Pd.NU.NU.NU.NU.NU.NU.NU.NV#Gjalt#f#alu.WF.Kt.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.VC.NV.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.XF.8p#c0alv#9i.UN.UN.UN.UM#pJalw#4T#e7#ga#QmabU#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#3hadGalxalyalzalAalBalCalD#zG#tr.3g#NB#D9.3g#DJ.3g.3g.3g.3g.3g.3galEajh#D8#4uahT#78#78#78ahTahTalFalG#GhalH#RtahG#H8#H8#H8#vcalI#T8#ul#bralJ#8jalK#yS#tG.OL.MO.L#.Rd.R1.XY.0f.0f.0f.MN.MN.R1.O3.MN#Z4adH#H8#H8#j9#Uy#iu#Yk#H8#U5#9n.Ph.Pi.Pz#.R#ul#Uq#xb#xb#xb#xb#ul#ul#xb#xb#xb#xb#ul#Wi#th#Lr#Lr#tp##T.MdalEalLalM#Vu#Uy#Uy#Uy#Uy#Uy#Vt#Uy#Uy#Uy#4##TC#TC#TC#TC#Vu#E2#K1#wR#FN#FN#FN#UNalN#wR#wR#wh.RkalOalPalQalRalSalTalUalValWalXalYalZal0al1al2al3al4ahd.2Ual5.7X.7Xa#Ka#K.7X.7X#a5al6al7ajN.Nk#8V.M4.M5.M4.R..Rg.Qa#7Aal8al9am.am#amaambamcamdameamfamgamhamiamjamkamlammamnamoampamqamjamr#Hyams#Jf#fI#At#vEafuamt#bDamuamvamwamx#9d#9damy#9d#6yamzamzamAamzamBamCamDamEamF#Pq.W3#eE#dGamGamH#cJ#a5#ae.7W.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7W#a5.7XamIamJaiV#dzalha#X#uFadt.9I.35amK#DTamLadb.7W.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B",
-".T0.T0.T0.S4.S4.0H.X2.X2.X2.X2.X2.X2.X2.OL.OL.OL.OL.OL.OL.P9.P9.0g.0g.0g.0g.0g.0gamM#X8#ut#qr#Eq#Eq#Eq#Eq#Eq#Eq#Eq#5A#MRamN.4pamO#Od.8RamP#lYamQ#2c#gb#6X#Qm#H8#H8#H8#H8#H8#H8#H8#H8#Hb#K##bd#gd#ge#ge#ge#e7#x.#nF#es#.e#.Y.YG.99.2r.Pd.NU.NU.NU.NU.NU.NU.NU.ND.7n.J9#HZamR.NM#rV#lO.ND.NU.NU.NU.NU.NU.NU.NU.NU.2A.NV.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.NU#gj##Q.5OamS.5V.RY.R0#Ab#e5#n8#7U#gb#gb#gbagq#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#4S#d3amTamUamVamWamX#Ty.3g.3g#tr#tr.3g.3g.3g.3g.3g.3g.XJ.XJ#Da#DJajhamYamZam0ahVahVam1am1#YPam2am3#7T#4S#H8#H8#H8#7V#Zxam4#U2#fw#U2.Nr#Dc.Rd#dN.OL.MX.Ln.Rd.MN.O3.Jd.R1.R1.R1.R1.R1.R1.0H#6X#Hb#H8#H8#Z.#ga#RW#1w#Hb#7Vam5am6.Pl.Ph.SA.XG#aF#xb#xb#xb#xb#xb#ul#ul#xb#xb#xb#ul#xb#DE#.9am7#yXam8#bl#Xd.PHam9.V6#E2#Uy#Uy#Vt#Uy#K0#Uy#Vt#Uy#Uy#ge#E2#E2#ge#E2#E2#Uy#Vuaigaigaigaigaig#E2#1san.an#anaanbancandaneanfanganhanianjankanlanmannanoanpanqanr#.zans.6Ba#K#r8#pQ#a4#a4afXantanuanvanw.M4.R..Rg.Qf.Qganx#fGanyanzanA#hY#ex#ex#fIanB#Hq#fI#At#a0#a0#a0#a0#vE#wS#vE#ex#a0anC#a0anC#wSanD#fI#Nc#ClanE#q.anFanG#6wanHanIanIanIanIamBanJanJanJanJanJanJanJanJ#2X#6xanKanL#2XanManNanOanPanQanRanSanTanU.6C#oV#af#oV.7W.6B.6B.6B.7W.7W#oV#af.6CaiGamJahxahx#bWahx.9I.9I.9I.9I.9I.9I.9IanV#uw#a4.6B.6B.6B.6B.6B.6B.6B.6B.6B.6B.7X.6B.9U",
-".T0.T0.T0.S4.W9.MV.VX.X2.OL.OL.P9.P9.P9.P9.0g.0g.0g.0g.0ganWanWanWanWanXanXanXanX#SXamM#X9#ut#qr#Eq#Eq#Eq#Eq#Eq#Eq#EqanYanZ#as#jyan0#mY#mY#v8#ANan1#2c#gdaadadH#H8#H8abU#H8#H8#H8#H8#Hb#H8an2#gd#gb#ge#ge#gb#nDan3.8C#xF#.Y.9q.2r.2r.Om.NU.NU.NU.NU.NU.NU.NE.NU#q1.Pkan4an5.QA.7n.Mw.NE.NE.NU.NU.NU.NU.NU.NU.NU.7n.NV.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.98.ND##V.9n##Q.2#an6an7#f..0g#hY.0g#7T#Fs#gd#ge#gd#AI#Hb#H8#H8#H8#H8#H8#H8#H8#H8#H8abUabUabU#H8abU#Hb#d3adG#1##R9#dXan8#bian9ao.ao##bo#jy#DJ.3g#tr#tr#tr.XJ.XJ.XJa.U.XJ.XJ.XJajbajb#Ryajhagzaoaaob.JNaocaodaoe#TU#Z.abU#H8#H8ahG#dYaof#xb#fw.4qaog#4r.Ln.Yc.MX.Ln.Rd.MN.R1.R1.R1.MN.Rd.ON.VX.ON#gb#V1#H8#H8#H8#Hb#R6#RW#xm#dZaoh#Y.#Ha.Ph.Pi.Pi.SA.7n#C9#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#ul#aF#Nh.RF.L1#qh.Mx#AG.N9aoiaoj#e4#K0#Uy#E2#E2#E2aig#E2#Vu#ge#ge#E2#E2#E2aok#wh#sM#wh#wh#E2#E2#4..Q8aol#ga#Elaomaonaooaopaoqaor#bbaosaotaouaovaowaoxaoyaozaoAaoBaoC.7I#o6aoD.6B.7X#a4aoEaoFaoGaoHaoIaoJ#HgaoK.R9.QgaoLaoMaoNaoOaoP#gdaoQ#ew#jM#sM#xm#Ef#sM#uI#wh#sM#wh#sM#E2#E2#ge#ge#geaig#sM#E2#xm#DN#wh.P9.UP#taaoR.XY#w.#w..XYajs.Q6.43#ew#ge#UM#5janHaoSaoSanJaoSanJaoT#R6#R6aoTaoTaoSaoU#7YaoVaoWaoWaoXaoYaoZao0ao1#eGao2ao3#a8#a6.7X#af#a5#a5#ae.7X#cKai0af3#bWahx.9I#dJ.9I.9I.9I.9I.9I.9I.9I.9I.9Iao4aho.7W.6B.6B.6B.6B.6B.6B.6B.7X.6B.6B.7T.9Uao5",
-".T0.T0.T0.T0.S4.S4.OO#e6.0g.0g.0ganWanWanWanWanXanXanXanXanX#SX#SX#SX#SX#SX#SX#SX#SX#SX.1B.VV#ut#qr#Eq#Eq#Eq#Eq#Eq#Eq#Eq#0yao6#Ob.63ao7#QZ#S0abRao8ao9.Lw#gd#geaac#Hb#H8#H8#H8#H8#H8#H8#Hbah0#OS#gd#ge#ge#ge#huap..Uq.0X#.e#eq.WG#Lp.96.NU.NU.NU.NU.NU.NU.ND.Pd#e#.Pm#XMap#.NS.RF.Mw.NU.NE.NU.NU.NU.NU.NU.NU.NU.RG.NV.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.5C.8B##Q.NU#P8apa#hY#hY#hY.P9#FN#e3#gb#ge#gb#gbagH#H8#H8#Hb#H8#H8#H8#H8#H8#H8abUabU#Hb#LI#WJ#4mapbapcapd#Y2.TA.WD#jy#m7#jy#hL#er.9m.Vy#.e.PA.Mua.U.PAa.Ua.Ua.Ua.Ua.Ua.U.61.XJ.14.Pa.NzapeapfapgaphalM#SX#SX#Y.#H8#HbahG#2u#65#xE##L#giapiapjapkalo#tG.Nn.Nn.Nm.O3.O3.R1.R1.ON#DN#f##jL#pj#dZ#H8#H8#H8#H8#kV#gd#gd#Zx#H8#H8#Z5apl.Pl.Ph.Pi.Pz##I#xE#ul#xb#xb#xb#xb#xb#xb#xb#ul#xb#tq#J8.KK.Nu#J8#ul#py.Mc#9w.8tapmanW#E2#E2#E2#wh#wh#wh#wh#wh#DN#sMaok#wh#wh#E2#ge#DN#wh#E2#1san.#0japnapoappapqaprapsaptapuapvapwapxapyapzapAapB.1LapC#rkafPapDapEapFapG#s.apH.7WabmapIapJ.9IapKapL#jQapMapNapOapPapQapR#xm#fJ#hu#fI#wh#sM#xm#wR#sM#ge#ge#ge#wR#E2#ge#geaigaigaig#E2#xm#Ef#xm#bc#xm#jL.OO.4..SY.R6.R6.R6.R6.R6.R6.R6.R6.R6.R6.R6.SY.UW.UOaoR.6q#Nc#1u#2XaoSaoSaoS#7K#7K#7K#7K#5j#7KapS#7K#5j#5janGapTapUapganMapVapW#uFapXapY#tBapZaiXap0abB#aR.8W.9I.9I#dJ.9I.9I.9I.9I.9I.9I.9I.9I.9I.9IamKaiL#a6#ae.6B.6B.6B.6B.6B.6B.7X.6B.9Uap1ap2ap3.2U",
-".T0.T0.T0.T0.T0.S4.S4.W8amM#SXanXanX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#e4#pJ#ut#qr#Er#Er#Er#Er#Er#Eq#Eqap4#MT.GK.3eap5ap6#ZvabRap7ap8.Ne#gb#gd#yYadH#H8#H8#H8#H8#H8#H8#H8#Wm#gb#gb#ge#ge#gd#wR#62#iq.ZW#kn.LQ.3r.Oi.NU.NU.NU.NU.NU.NU.NE.NU.WG.WFap9aq..Pk.Pp.Mr##V.Qs.NU.NU.NU.NU.NU.NU.NU##B.NC.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.NE#bt#cn##Q.NV#Go.T1.TO.TO#hY.0g#E2#ga#gd#ge#ge#gb#ge#kmahG#2t#H8#H8#H8#H8#H8#H8#Hb#1##V1#Hbaq##Qvaoi#aE#aE.PA#aD.63.63.63.WD.63.Nr.WD#xH#bl#bl.0U#tr.Mua.U.61.61.XJ.XJ.3i.NA#ce.Odaqaaqb#4TaqcaqdaqealM#e9a.2#1cadH#7V#hlahzaqf.5E.5E#jj.Od#jy.WE#kSaqgaqhaqiaqj#u9.MV.0H.9Q#sM#uI#gb#Yk#Qm#H8#H8#H8#oA#6X#gb#gd#Ee#d3#H8#1#aqk.SA.Pi.Pi.Pi.Pg.Po#cn#xb#xb#xb#xb#xb#ul#xb#fe#D.#DE.8p.J0aql##L#xE##L.Md#Quaqm.Gi#E2#n8#wh#wh#wh#wh#wh#wh#E2aok#wh#E2#E2#E2#E2#ge#Vu.Q8an.#4.aqnaqo#okakzaqpaqqaqraqsaqtaqu.1Q#rlain#ZraexafQ#tK#o6#uO#uO.7I.2VaeQaqvaqwaqx#jT#rgaqyaqzaqA.Y6aqBaqCaqDaqEaqFaqG#fIaoQ#lh#jM#sM#sM#ge#DN#ge#E2#Ef#ge#ge#wRaig#ge#DN#xm#IHaig#DNaig#sM#xm#DN#sM#sM#xm.X2.R0.SX.R6.R6.SY.SY.SY.SY.SY.SY.SY.7G.7G.7G.7G.R6.R6.R6.R6.R6.SY.Z5#ta#e8anE#2XaqHanJ#7KaqIaqIaqIaqI#6w#6waqJ#6w#7KanJanG#A6aqK#qWaqL#kzafwadta#X.8Wahx#bW.9I#dJ.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.35aqM#bS.7W.6B.6B.6B.6B.7X.6Bap1aqNaqO#qy#o6.1L",
-".T0.T0.T0.T0.T0.T0.S4.W9.MV#e4#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SX#SXaqPaqPaqPaqPaqPaqPaqP#SX.1B#X9.3Z#qr#Eq#Eq#Eq#Eq#Eq#Eq.UM#kE.3ZaqQaaHaqRaqS#Od#v8aqTaqU#2c#gb#TV#ge#WW#Qm#H8#H8#3h#H8#Hb#H8#w6#gd#gb#ge#ge#D4aqV.0Y#ul#.e#vj.2r.NU.NU.NU.NU.NU.NU.NU.NU.NE.Pe##AaqW#2v.8c.NO#ui##O.ND.NU.NU.NU.NU.NU.NU.NU.L7.NC.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oa.Pd#gj.8B##Q.97#d2aqe#hY#hY#hY#SXaqX#gb#gb#ge#ge#ge#gb#gd#2v#kWadHacd#1#adIadG#lTacdaqY.7faqZ#kk#Qo.Nr.WD.63.63.63.63.63.63.3k.2p.3k.WD#0W.WD.NX.9m#gi#oN.14#v4.NA.P#.Qxaq0aq1aq2aq3aq4aq5aq6#FRaq7agH#4Taod#4Qaq8#3Caq9.Mk#wC#aD.TA.TA.TA.2p.Nq#tr.YLar.ar#ar#ara#2u.9Q#sM#xm#gb#gearb#H8#H8#H8#H8#1##1w#gd#gb#gd#54#H8#Hb#LIarc.Pk.Ph.Pi.Pi.RC.Pk#.d#DE#ul#xb#xb#ul#aF#ul#.d#lO.7nardare#Lr#ul#xb#Oj#ug#bqah1#OS.0h#E2#E2#wh#E2#wh#wh#E2#E2#wh#wh#wh#ge#ge#4.#hXan.#2Tarf#VH#C0#B#arg#g6arhapx#27ain#Zrari#.z.7I#uO.7I#.zacRarjarkarl#sRarmag#abma#Karnaroarparqarrarsartaruarvarw#e7aoQ#ew#jM#sM#ge#wR#ge#ge#Ef#ge#xm#xm#DN#Ef#sM#DN#DN#DN#Ef#DN#DN#DNaok#Ef#xm#sM#xm#DN#wh#Ef.9Q.O4.SX.R6.SY.7G.7G.7G.7G.7G.7G.7G.7G.7G.SY.SY.SY.SY.SY.SY.SY.SY#HK#HK#HK#HK#CK.OU#lhanE#7YanJanJanHanHanJaqJ#5j#5j#1v#1v#UO#UN#1t#Uz#qlarxaryarz#eF.6e#eF#eC.7z.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I#bWarA#ae#bS.7W#kG.6B.6B.6BarBarC#uO.1L.2V.2U.2U",
-"#w..T0.T0.T0.T0.T0.T0.S4.K7.T0alM#SX#SX#SX#SXaqPaqPaqPaqPaqPaqPaqPaqP#SXaqP#SX#SX#SX#SX#SX#SX#hY#e6.W9#qr.UN.UM.UM.UM.UM.UM.UM.UM.3Z#0y#DearD.0UarE#mY#oEarFarG#hY.0g#gb#gd#qR#V1#Hb#H8#H8#H8#H8ahG#4U#gd#ge#ge#e6arH.PR##N#hf##P.Pe.NE.NU.NU.NU.NU.NU.NU.NU.NU.ND.RFarI#gbarJarK#rO.1f.Oa.NE.NU.NU.NU.NU.NU.98.3E#ZG.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.00.5B#aH.8p.SvarLarM#TU#TU#SXarMarNarO#RW#ge#ge#ge#ge#ge#gb#gb#Uy#e7#e5#gb#I.ab4arP#NE#E#.Nr.63.WD.63.WD.WD.WD.WD.WD.63.63.WD.3k.2p.2p.3k.WD.63.3k.Oc.9sarQarRarSarTarU#2uarVarWarX#4TarYarZar0ar1#ioar2ar3.XT.Nr.4x#wC.2p.TA#jj#jj.3k.XJ#Rq#0Zar4#hi#1w#e6#gb#f##tk#1w#Zw#ge#gaadG#H8#H8#H8#Hb#54#f##gd#ge#gd#gaahG#I.#Z.ar5.Pl.Ph.Pi.Pi.Pi.Ph#cc.Kt##L#xb#xb#xb##L#Hi##B#Pdar6ar7adHar8ar9#T8#w8#jl#hs#LI#hY#hYaig#E2#E2#wh#E2#wh#wh#Vu#Vu#Vu#4.#4.#wR.7vagQas.as#asa#f2#VF#AA#8Jasbasc#mzain.1L.7I#uO#.zahiakGaeyasdasea.h.7Wasfasgashasiasjaskaslasmasn#jN#geasoasp#wS#vE#fI#jM#sM#wR#wR#wR#xm#Ef#Ef#Ef#sM#sM#Ef#Ef#EU#Ef#sM#sM#DN#DN#DNaig#DNaig#xm#ge#sM#DN#DN#DN#sM#xm#NUaoR.SX.R6.7G.7G.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK.SY#CK.Nf#e8#2TaoX#pj#qR#UM#K1#Uy#E2#E2#hY#hY#hY#2R#n8.R2.Z5asqasrassastasuasv#eF.6e#bH.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.35aswasx.6C#a5#af#oV##paiy#uO.1L.2U.2U.2U.2U",
-"asy#C#.R1.T0.T0.T0.T0.T0.S4.S4#Aq#f.#SXaqPaqPaqP#SX#SX#SX#SX#SX#SX#SX#SX#SX#hY#hY#hY#hY#hY#hY#hY#hY.0g#g..UN.UN.No.UM.UM.UM.UM.UM.UM.UM.5U#4r#iiaszasAasB#oE.WMasCasD.Lw#gb#gb#gb#1cagq#H8#H8#H8#H8#Je#gd#Uz#ge#gb#e6asE#lN#w8#xD.XF.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.7n#6G#geasF.NL.5N#gh.Pd.ND.NU.NU.NU.NU.NU.NU.9e.Oj.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.NU.8x.8B##N.8y.PwasGaqe#SX#TUaqeasH.RQad5#e5#ge#ge#ge#ge#gb#e6arV#lh#lTasIasJ#Vj#fl#gr#kk#kk.WD.WD.WD.WD.3k.3k.WD.WD.WD.WD.WD#LM#LM.WD.3k.2p.2p.3k.WD.3e.4H#ckasKasL#qRarXasMalMasNasOasP#0W.XJ.Nr.WD.3k.WD.WD.2p.TA#jj.TA#jj#jj#2g#J..Kv#rE#0J#gdarV#g##gd#Zw#6XasQ#kW#1w#gd#vc#4S#H8#H8#H8#U4#w6#gd#gb#ge#gb#gd#54#H8#Hbaohai8asR.Pi.Pi.Pi.Pi.Pi.Ph#.O.Nu#.f#aF#J8asS#eiasT#ga#Z4ahGahGasU.QMasVasW#e7#D4.1B#4.#E2#Vu#4.#4.#4.aigaigaigaig#E2#E2#E2ah7asXasYasZas0as1#ib#cQ#AA#8Jas2aquain#.z#uO.7Ias3ajFagc#ae.5oas4as5as6as7as8as9at.#wRat#ataataat##.t#.t#jL#sM#sM#sM#Ef#xm#xm#xm#xm#xm#IH#IH#xm#Ef#DN#bc#tG#Ef#DN#Ef#Ef#Ef#DN#DN#DN#DN#whaok#bcaok#sM#sM#bc#DN#sM#wh#wRaoXakv.UW.OW.SY.SY.SY.SY.SY.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK.UM#CK.XY.ON.X2#hY#n8#hY#hY#hY.9Q.9Q#hX.9Q#n8#jL.9Q.6c#ql#wO.RYatbatcatdateatf.35#h8#bH.9I.35.9I.9I.9I.9I.9I.9I.9I#dJ.9Iahx.9Ialnabz#a8ant#uyaioaheaa.a#9a#9a#9a#9",
-"atgath#w..T0.T0.T0.T0.T0.T0.T0.S4.S4.T1#SX#SX#SX#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY.0g.Rc#Eq.Oz.UN.UM.UM.UM.UM.UM.UM.UM.UN#3e#qratiatjatkatl#yvatmatn.0g.X2#ge#gd#gb#e3adG#H8#2t#I2#DN#Jh#ge#ge#e5#Fj#ul.5B.Pc##W.ND.NE.NU.NU.NU.NU.NU.NU.NU.Pd.4R.J4#f#a.Q.NK#.R#yl.RR.ND.NU.NU.NU.NU.NU.Oi.Uu#0H.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.5C#aH.8B.8B.8x.97acdato#SX#SX#YOatpatqatrats.Z##nD#e5#D4#g##yjattatuatv#gr#Qo#zG.63#av.WD.WD.WD.WD.WD.WD.WD.WD.WD.WD.WD.3k#jy.2p#kk#yW#oN#jy.3k.2p.2p#jy.3k.3k#eraaAatwatxatyatz#WG.XJ.WD.TA#jj.TA.TA.TA.TA.TA.TA.TA#jj#jj#jj.TA#J.#5zadH.W6#e4#gb#TV#1b#1##Hb#Qm#1b#gb#e7#e3ahG#H8#HbabU#H8#kV#gd#gb#ge#ge#Jh#EeahG#H8adHatA.Pl.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Ph.RC.NOam7.PoatBae4#e4#GD#Qm#H8#w6#RW#vc#ge.S3#n8#4.#E2#4.#E2#E2#whaigaig#hX.MX.ON.TO#DN.Q8atCatDatEatFatGatH#ba#cRatI#g6ah#.7IatJ#uOacRabaatKabmatLarnatMatNatOatP#.tatQatQ#.t#Ef#Ef#xm#xm#xm#xm#DN#sM#E2#Ef#sM#sM#EU#xm#xm#IH#xm#xm#DN#DN#xm#DN#DN#DN#Ef#DN#sM#bc#sM#tG#tG#bc#tGaokaigaig#sM#sM#sM#sM#sM#whaig#NU#5jatR#Nc.5a#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK#HK.RY.Nf.0H.9Q#jL#jL#jL#jL#jL#jL#jL#jL#jL#jL#jL#.E.SQ#aU.6katS.8ZatTatUatVatW#bH#ev.7z.35#DT.9I.9I.9I.9I.9I.9I#dJ.9I#bW.8Wahx#kAanVatXatYatZat0at1at2at3",
-".2Xat4at5.XY.T0.T0.T0.T0.T0.T0.T0.S4.S4.T1.0g#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#e5#oa.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.3Z#YEat6at7at8#ATae1#nLat9au..S3#ge#gb#gd#gb#e3#HbarNau##gd#gb#ge#e7acAaua#cn.5B.8x.NE.ND.NU.NU.NU.NU.NU.NU.NU.NU.RB.NJaub#6V.Py.Pp.PT.Tl.ND.NE.NU.NU.NU.NU.Oi#fc.Pe.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.NE##W.8B.8B.8B.8p.Pe#U4auc#2RamM#TUaud#ft#.bam4#9D#rJ.Gbaueaufaug.Qx#Qo#fl.63.63.63.63.WD.WD.WD.WD.WD.WD.WD.WD.WD.3k.3k#kk.4p#oN#oN#oN#oN#hL#oN.3k.3k.2p.2p.2p#2f#LM#fv.5C.8p#kk.4x.2p#jj.TA.TA.TA.TA.TA.TA.TA.TA.TA#jj#jj#jj#2f.PHasP#54#e5#ge#gb#gd#qR#Hb#H8#U5#2v#e7#gd#1b#Hb#H8#3h#3h#H8#kW#gb#gb#Uz#ge#gb#gd#kV#2t#d3#OSauh.Pl.8g.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph#pl.Pk#6G#T5#e4#gd#Zx#2t#lT#Uz#gb#jL#n8#wh#jL#gb#jL#E2#E2#DN#DN#wh.0o.R0.SX.SX.UWaui#q.aujaukaulaumaun#cR#cR#AAauoaupauq#cD#sUablaur.6Bausautauuauv#fIauwasp#mp#jM#kw#gd#gd#jL#hX#hX#hX#wh#wh#sM#Ef#DN#DN#xm#DN#DN#DN#xm#xm#xm#xm#IH#Ef#xm#IH#bc#DN#sM#bc#sM#Ef#sM#xm#E2#sMaig#E2#DNaigaigaig#wR#E2#E2#wR#UN#2XaoSaqH#TC.Q0#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK#HK.UO.UP.9Q#jL#jL#jL#jL#jL#jL#lr#lr#lr#jL#jL.OK#Du#HK#HK.Y..8Z#Aratbauxauyasv#h8#bH.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I#dIadt#uF#uF#uF.7Badt",
-".5eauz#p9#w..T0.T0.T0.T0.T0.T0.T0.T0.S4.S4.OO.0g#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#e4.R0.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.N2auAauBauCauD#AlauEauFauG#2c#gd#ge#gb#gd#geaac#0D#ak#TV#ge#gb#gcauH#EB.5B#hM#c1.NE.NU.NU.NU.NU.NU.NU.NU.PS.Ur.Pfap9#qR.J8.3p.LO.Z1.NU.ND.NU.NU.NU.NU#aM.99auI.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.8x##M.8B.8B.8y.Pe#ZyauJ#yealMacc#KT.63#kk#yWauK#.Y#iJ#aE.3i.4x.WD.WD.WD.WD.WD.WD.63.63.63.WD.Tt.Tt.3k.3k#jy.2p#kk#kk#kk#kk#kk#kk#kk#kk#oN#kk#jy.3k.2p.3k#2f#Ec.VD.1f#dh#hL#2f#jj.TA.TA.TA.TA.TA.TA.TA.TA.TA.TA#jj#jj#jj#2f.JW.PH#YI.Z##gb#gb#gb#ge#j9#Z.#1R#gb#gd#ge#kW#H8#H8#3h#H8#H8#Hbaad#gd#gb#ge#ge#gd#GDahGahG#OS#tk#5x.Pk.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Ph#kX.Pkam6auL#e6#gb#f##d3ahG#vc#gb#E2#wh#whauMauM#uI#sM#DN#DN#hX.ON.R0.SY.R6.Oz.IrauNauOauPauQauR#cR#Az#cR#cR#AAa.bauSauTauUauV#uwauWauXauYauZau0au1.1C.OO.Nc.Nc.Nc.Nc.W8.W8.W8.Nc.MV.Nc.Nc.Nc.V6.OO.OO.MX.OV.P9.TO#hX#wh#sM#xm#xm#DN#xm#xm#xm#xm#DNaigaig#xm#xm#Ef#xmaig#K0#K0#K0#wR#ge#ge#K0#K0#E2#wh#K1#1u#7KaqHanHa.D#1u.Z5.OW.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK#pJ.Nc.X2#jL#jL#lr#lr#lr#jL#jL#jL#e7#CK.OW.Q0.Q0.Q0.Q0#HK#B6#Ar#xlau2au3#Bx#ev#bH.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I",
-".2Wau4au5.Z5.S4.T0.T0.R1.R1.R1.R1.R1.R1.S4.S4.V6.0g#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY.0g.T0.UN.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.Luau6amzau7au8.4Oau9av.aoX.Ne#gd#ge#gb#gd#gb#ge#gb#ge#ge#nD#pA#w8.8w#H2.00.5M.NU.NU.NU.NU.NU.NU.NU.2B.ND.4Sav##e7ava.NL.2d#ct.7o.Oa.NU.NU.NU.NU.NU.NT.Pw.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Pd#bw.8B.8B.8B.8y.NV#JdauJavbavcavd#mg.63.63#aD.Nr.Nr.63.63.WD.WD.WD.WD.WD.WD.WD.WD.WD.WD.WD.3k.3k.3k#OF.3k#oN#cm#er#gF#m3.4q#cp#cl.2n.Tk.2n#KT.2p.3k.2p.2p.JW#Gh#lS.L8.2p.TA#jj.TA.TA.TA.TA.TA.TA.TA.TA.TA.TA#jj#jj#2g#2f.9hajbave#0Y#gb#TV#gb#f##4Tagq#e3#2v#f##ga#j9#H8#H8#H8#H8#H8#H8arO#gd#gd#2vaad#gb#gd#54#2t#V1.9Q#tkavf.Pk.Pi.Pi.Pi.Pi.Pi.Ph.Pi.Pyavgavh#gb#e7#E2#gd#Zx#H8#Qm#ht#jL#wh#gbauM#tG#sM#sM#DN.X2.21.Q1.W0.I0.Kl.Jiaviavjavkavlavmavn#yK#yK#cR#AAavoa.bavpavqavravsavtavu#bWavvavwavxavy.21.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.Nc.MO.MX.0H.0h#hX#wh#ge#f##wR#f##f##wR#f##wR#ge#K0#ge#K0#ge#wR#wR#ge#E2#E2aig#NU#2UanHanHaqIaqIaqH#7K#hu#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK#CK.Nc#e7#gb#jL#jL#jL#gb#gb#e7.Q0.OW.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK.6k.8ZavzavAavB#CP#ev.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I",
-"avC.2UavD##l.M0.T0.S4.T0.T0.T0.T0.T0.T0.T0#g.#t##D4.0g#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY.OO.RY.3Z.UN.UM.UM.No.No.No.No.No.No.No.Oz.Km.He.4.avEavF#gnavGavH#q..T1#gd#ge#gb#gb#ge#ge#ge#e7am5avI#C3avJ.9r.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU##BavK#f#avL.YI#j3#lS#zA.ND.NU.NU.NU.NU.NU#fd.Sv.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE##V#.Z.8B.8B.8B##Q.NVak#avMavNavOaaeavP#gr.WD#m7.3k.2p#jy.WD.63.63.63.63.63.63.63.63.63#aD.Nr.Nr#kk.YV#bp.YH##L#bp#y4#cl.0U.8F.4p.2p#jy.2p.YL.YL#jy.WD.WS#Lm.PQ.RQaoi#O5.TA#jj.TA.TA.TA.TA.TA.TA.TA.TA.TA#jj#jj#jj#2f#2f.9h#J.avQ#WK#e7#gb#ge#f##1w#4m#U5#2u#kW#w6#dZ#H8#Hb#H8#H8#H8#Hb#Hbaad#ge#4T#kW#ga#RW#1w#Qm#H8adG#sM#jLavR.Pl.Pi.Pi.Ph.Pi.Pi.Pk.PhamR#2v#e6#gb#ge#gd#ZwadP#ZyadH#ge#4.#wh#sM#sM#sM#jL.VX.XY.HaavS.Ir.5aavTavUavVavWavlavXavYavZ#yJ#yK#cR#VF#f2av0av1av2av3av4av5av6av7av8av9.Qcaw..21.OU#SN#SNaw#awa.MV.W8.W8.W8.W8.W8.W8.W8.W8.W8.W8.W8.W8.Nc.Nc.21.21.MV.21.MV.MV.21.21.21.Nc.MW.OV.TO#n8#E2#f##f##wR#K0#K0#K0#K0#K0#E2#E2#E2#wR#GD#UN#2XanHanH#7KamB#7KanJaqH#1v.OU.OW#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK#pJ#fU#gd#gb#gb#gb#gb#gd.Nf.OW#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#wNawb#wgawcawd#bH#CP.9I.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I",
-".2U.2Waweawf#e8#e5#Ab#g.#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#g..Rc.ON.0g#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY#hY.1B#t#.UN.Oz.UN.No.Ha.Ha.Ha.Ha.Ha.Ha.Ha.Ha.No.OA.1j.GTawgawhavGawiawj#e3abT#e7#gb#gb#ge#ge#gb#nDan3awk.8w.8y.NE.ND.NU.NU.NU.NU.NU.NU.NU.Oi.52.NS#IQawl.Py#.R.XF#ek.5M.NE.NU.NU.NU.Pd.Uu.51.98.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.98.NE.NU.NU.NU.NU.NU.NU.NU.NE.NE.53.5B.8B.8B.8B##Q.OqawmavOavNavNawnawo#yW.3k#hL.5E#oN.WD.WD.WD.63.63.Nr.Nr.Nr.63.WD#oN.Tk.NB#br.Tl.ND.O..Qr.Tk.2p.WD#wC.WD.WD.WD.WD.WD.WD.63.Nr.3k.NX.4J#Ad.Ms.TA.TA#jj.TA.TA.TA.TA.TA.TA.TA.TA.TA#jj#jj#jj.TA#2f.9h.9h.PAawp#WK#RW#ge#gd#ge#qR#54#Hb#Hb#H8#H8#H8#H8#Hb#Hb#H8#H8#H8#H8#2u#ge#dZ#lT#2uaadaad#Z.#H8#Hb#1##xm#jL#6H.Py.Pk.Pl.Py.Pl.Pi#5xawq#e6#gd#ge#ge#gb#gb#Y.#Zy#Hbaad#hXaig#DN#DN.X2.Ja.HH.I1.SXawrawsawtawuawvawwawxawyawzawAawB#8y#ib#B##C0awCawDawEawFawG#Ef#wS#fC.39#C#.Rl.M0.21.21#t#.QkawHawIawJawK.MVawL.MV.W8.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.MV.0f.21.21.21.21.Rd.OO.OV.0h#n8#E2#wR#wRaigaig#E2#K1#UM#1u#2U#7KanJanJanJ#7KaoSanJanJaqHaqH#1u.Q7.5a#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK.Lq.4e#gd#gb#gb#gb#gd.M0.Q0#HK#HK#HK#HK.5a.5a.5a.5a.5a.5a.5a.Y.#Ar.4dawMawN#CP#bH#DT.35.9I.9I.9I.9I.9I.9I.9I.9I.9I.9I",
-".2W.2VawOawP#Uy#TV.T1#g.#g.#Ab#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#pJ.Lq.ON.0g#hY#hY#hY#hY#hY#hY#hY.0g.1B.S3.1B#e7#hY#hY#hY#hY#hY#hY#hY#hY.0g#D4.Nm.N3.Oz.No.No.No.No.No.No.No.No.No.No.No.Oz.Ji.He#qqawQawRawSawTawU.7v#gd#gb#ge#ge#e9#p8.8w.5B.9n.PL.NE.NU.NU.NU.NU.NU.NU.NU.NU.8q.PoawV#2v.8c.Pp#wD#...Pd.NE.NU.NU.NU.Pd#fc.8q.O8.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.NU.NE.NE.NU.NU.NU.NU.NU.NE.NE.00.8B.8B.8B.8B#bw.NDafbavMawW#hbawXawYawZ#ug#bo.TA#aD.63.2p.2n.Tk#.##EN#er#cl#Ja.8v#bw.1f.ND.Px.O.#dh.2n#aD#LM.WD.WD.WD.WD.WD.WD.WD.63.63.YL.4J#fu##O.XF#kS.9h.TA#jj.TA.TA.TA.TA.TA.TA.TA.TA.TA#jj#jj#2g#2f.9h.9h#2f#Bhaw0#gd#e7#ge#gb#Jh#f##1RadG#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#Qm#pj#2uahG#tk#1#acd#Qm#H8abU#H8ahG#qV.X2#WQ.SAaw1aw2aw3aw4#XP#pj#e6#gb#ge#ge#gb#gd#Fs#H8#U4#Ho.6ban.aw5.Rq.Un.Q1aw6aw7aw8aw9ax.ax#axaaxbaxcaxdaxeaxf#Dqaxg#Jtakzaxhaxiaqn#whaxj#kw#jM#uI#sM#hX.ON.Nc.Nc.Nc.2R.21.0f.Rlaxkaxlaxmaxn.R.axoaxpawa.21.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Nc.Rd.MN.Jd.Jd.Ja.Ja.Jd.Ja.Ja.Ja.Ja.Ja.Ja.Ja.Ja.Ja.MN.MN.Rd.OO.0H#hY#E2#NU#VM#2U#7K#7KanJaxqaxranJaqHanHanHanHaqHanHaqHaqH#1u.ON#CK#HK#HK#HK#HK#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK.Y.#HK.Nf#e6#gb#gb#gb#n8#ew.4e#aa.Rl.Q0#HK#HK.UM.UM.Q0.Q0.Q0.RY.RY.Y.#oYatTaxsaxt.6e.7z.35.9I.9I.9I.9I.9I.9I.9I.9I.9I",
-"axu.2X.5daxvaxw#4.#iu.V6#X8#g.#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.#g.#Aq.V6.1B.0g#hY#hY#hY#hY#hY#f..W9.Nm.No.P2.Lq#e5#SX#hY#hY#hY#hY#hY#hY#hY#hY#e4.W9.Oz.Oz.Oz.No.No.No.No.No.No.No.No.No.No.No.No.N3.7j.LuaxxaxyaxzaxAaxB.Xw#hX#Nc#gd#HZaxC#iq.8B.Ns.ND.NU.NU.NU.NU.NU.NU.NU.NE.Oq.RFaxD#f.axE.WF.LN#.c#e0.ND.NU.NU.NU.Om.4B#bm.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE.TD#.0.NE.Oa.NE.NU.NU.NU.ND.NE.XF##M.8B.8B.8B#hM.NE#2mavMawWavNavNaxFaxG#xb#fe.4q#cm#kk.3k#hL.5E#KT#gi#Wi#Hi.UF.NU.NV.Oq.RR#mi.NX#aD.63.WD.WD.WD.WD#m7.WD.WD.3k#juaxH.64#e0.NV.Ur#ax.3A.2p#jj.TA.TA.4H.TA#fm#jj.TA.TA.TA.TA.TA#hL#jj#2f.9h#2f#2f#aEaxI#R6#e6#ge#gb#gd#gb#vcarOahG#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#HbadG#kW#H8am5.9Q#Sw#Zy#H8abU#H8#H8#Qm#OS#wh#jL#qa#R6#gd#e6#e6#e7#n8#ge#ge#ge#ge#gb#E2axJaxKah0axLaxMaxNaxOaxPaxQaxRaxSaxTaxUaxVaxWaxW#CWaxXaxY#AAatHaxg#JtaxZax0ax1#7Iaxjax2#wh#bcaok#bc#DN#sM.OV.MO.2R.Nc.Nc.21.21.M0.21ax3ax4.Rh.OQ.R#ax5.M7ax6ax7.MV.Nc.Nc.Nc.Nc.Nc.Nc.Nc.21#taax8ax9ay.ay##Dv#Dv#Dvayaaybayb#Dvaycayd#pUayeayfaygayhayi#ta.R1.U0.MO#hX#ga#2WaqHaqHaqHanHaqIaqHaqHaqHaqHaqHaqH#7K#qR#ga#sM#sM.X2.OK.0f.Lq#CK.Q0#HK#HK#HK#HK#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.5a.5a.5a.5a.5a.5a#HK.OW#CK#fU#gd#gb#gd#gd#ClanGanGayj#Ij#ta.O4.UM.UM.UM.RY.RY.RY.RY.RY.OW#oYaykaylaym#ev.9I.35.9I.9I.9I.9I.9I.9I.9I",
-"aynayoayp.7Iayq#no.0h#e6#D4#g.#g.#Ab#Ab#Ab#Ab#Ab#Ab#Ab#g.a.6#e4#hY.0g#hY#hY#hY#hY.OO.UM.N3.Oz.Oz.Oz.W0.Oz.W9#e4#hY#hY.TO.TO.TO.TO.TO#hY.0g.W8.UM.N3.Oz.No.No.No.No.No.No.No.No.No.No.No.No.No.Oz.Km.He.UOayrays#KOayt#sM#gb#e5ayu.8v#bs#bt.ND.NE.NU.NU.NU.NU.NU.NU.2B.O8.RG#fj#geayvarKayw.8p.4s.ND.NE.NU.NU.NU.RB#fd.Pd.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU##W#.d#.X.ND.NE.NU.NU.ND.Oa.Tl.8B.8B.8B.8B.9n.Pd#R9avMayxayxayxayyayz#DE.8p#de#fw#DK#v2.9q.Oc.9p#kj#.W.PL.NV.Oq.Ns#bw#v2.2p#wC.WD.WD.WD.WD#m7.WD#wC.8F#m3#B0#bw.RR.Oq.Oq.Nt.Vw#kS.2p#jj.TA.TA.4H.TA.2n#py#2g.TA.TA.TA.TA#jj#jj#2f.9h#2f.9h.94ayA#Vs#f.#ge#gb#gb#gb#f##4Tagq#Hb#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#Hbake#H8alw#jL#sM#Ee#dZ#H8#H8#H8#H8#Hb#AI#gb#gb#RW#gd#gb#ge#ge#ge#Uz#ge#Nc#gb.Q8.TKanHayBayCayDayE#b2#VF#F##F##qI#f2#cQ#E7atHatHatHayF#Jt#B.#zsayGayHayHayIayJayKax2#wh#sMaigaig#DNaig#sM#DN.TO.ON.MQ.Nc.Nc.Nc.NcayL.21#lnayMayNayOayPayQayRayS.V2ayTayU.MV.Nc.Nc.Nc.Nc.Nc.Nc.21.21ayVax8ayW#DvayXayY#h#ayZayZ#qJ#rway0#qJ#qJ#qJay1#Fday2ay3ay4#Klay5ay6ay7ay8#Ij#jL#VN#2YaxraqHaqHaqHaqHaxr#2W#UN#ge#jL.9Q#gd#jL#jL#gb#gb#gd#e7#e6#f.#ta.Nf.Lq#CK#HK#HK#HK#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.5a.5a.5a.5a.5a.5a.5a.5a.5a.Q0.Q0.Q0.Q0.Q0.Q0.UM.UM.UM.S4#e6#gb#gd#ClanG#6w#6w#6w#6way9az..Nf.UM.UN.UM.RY.RY.RY.RY.UM#B6.R7az#aza#CP#bH.35#bI.9I.9I.9I.9I.9I",
-"#Jhazbazc.7Lazdayqaze#ex.0g#f.#Ab#g.#g.#Ab#Ab#Ab#Ab#Ab#Ab#X8#Ab.1B#hY#hY#hY#hY.1B.RY.N3.Oz.No.No.No.No.Oz.N3.Nm#D4#SX#hY.TO#hY#hY#hY#hY#hY#TU#e4.W9.Oz.Oz.Oz.No.No.No.No.No.No.No.No.No.No.No.No.No.No.Oz.GT.SY#NR.Vo.6D#Nc#f.azf.5B.8B#gj.NU.ND.NU.NU.NU.NU.NU.NU.NE.Oi#iD.Pmaaga.P.Pk#w7.6R.0Z.Oi.NE.NU.NU.NE.Px#c#.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Oa.Om##Q.8A.Qy.NU.ND.NE.NE.Oa.00#aH.8B.8B.8B.8B##Oazgazhaziayxayxazjazk#br#.Z.NE#c1azl#.Wazm.0Zazn.2#.Oq.Ol.7o#bw#ul#th#hL.WD.Tt.WD.3k.3k.WD#LM.4p#fp#xE#.W.7o.Oq.Ok.ND.Oq.WRazo.2p#jj.TA.TA.4H.TA.2n#th.3A.TA.TA.TA.TA#jj#jj.TA.9h#2f.9h#aEazpazq#e9#ge#gb#gb#sM#ge#pj#Pt#Z.#Hb#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#Qmaad#jL#xm#jL#AI#Hb#Hb#H8#H8azr#Yk#jL#gb#Uz#ge#ge#Uz#Uz#ge#Uz#wh.2Qazsaztazuazv#BSazw#B.#ib#cR#b2#cR#cR#cQ#b1azx#bbazyazzazAazBazCazDazEazFazGazH#TC.Q8aigaig#sM#K0#ge#wR#wR#wR#wRaig.0H.OO.OO.21.V6.Nc.2R.21.NcazIazJazK#w.azL#SNazLan.azMazNazO#aUaoR.Nc.Rd.Nc.Rd.Rd.Rd.Rd.Rd.Lo.Ja.Ja.X1.86azPazQazR#PQ#O2azSazTazUazVazWazXazYazZaz0az1ay6az2az3az4az5az6az7az8az9#qRaA.aA##2UaAa#gb#gd#gd#gd#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gd#e7#e4#ta#t##CK#HK#HK#HK#HK.Q0.Q0.Q0.5a.5a.5a.5a.5a.5a.5a.5a.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.RY.RY.RY.RY.RY.RY.RY.UM.UN#Sv#e5#gdanE#6w#6w#6w#6w#6w#6w#6wanGazs.M0.UM.UN.UM.RY.RY.RY.RY.UM.R7.LzaAbaAc.6e.9I.35.9I.9I.9I.9I",
-"#FR#TV.X2#wSah7aAdaAe#lh#hY#hY.0g.Rc#L5#g.#Ab#Ab#Ab#Ab#Ab#g.#X8aq4.0g.TO.TO.TO.Nc.Oz.N3.No.No.No.No.No.No.Oz.Oz.Oz.R1#SX#TU#hY#TU#TU#TU#TU#TU#TU#SX#D4.P2.N3.Oz.No.No.No.No.No.No.No.No.No.No.No.No.No.Ha.No.Oz.Oz.P2.Re#E2#iu#vcaAf#iq.5B.RR.Oa.NU.NU.NU.NU.NU.NU.NE.NU.Mq.PfaAg#gdaAh#.Q#j6.8BaAi.ND.NU.NU.NU.Oq.7n.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND##V.Uq.8B.8x.53.ND.NE.NE.PL.Uq##M.8B.8B#cn#.baAjazjayxaAkayxazjaAl.6Q#EB.Tl.Ol.Oq.Ok.NE.NE.O#.Oq.Oq.UF#fw#xb#fw#eq#LM.3k.WD.3k#jy.WD.3k#er#DK#ar##V.NV.Nt.Oq.NE.ND.ND#hG.9h#jj.TA.TA#oN.TA#JL#fe#bp#2g#oN.TA.TA.TA#WG#oN#2f#2f.3e.94.aIaAm.V6#ge#gb#gb#gb#gb#GD#x.agqahG#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#dZ#xm#sM#xm#sM#uI#kW#H8#H8#H8#U4#Sw#Jh#gb#ge#ge#ge#Uz#Nc#E2.7vabEaAnaAo#BS#B##ib#AA#cR#cR#cR#cR#b1azxaAp#ibaAqaAraAsaAtaAuaAvaAw.I2.JB.VWavcaokaok#E2#wR#K0#K0#K0#K0#K0#ge#ge#ge#wR#4..S3.2R.OO.Nc.V6.Nc.Nc.21.Nc.Nc.M0ajs.21.Nc.Nc.Rd.0fazL#AuaAxaAy#aU.21.Rd.Rd.Rd.Rd.Rd#n7#n7.2R.2R#n7.MN.Rd.X1.Ja.Ja.86aAzaAAayV.Ja.Ja.Rd.MQ.UP.MW.7waAB.UPaACaADaAEaAFaAGaAHaAIaAJaAKaALaAMaANaAOaAP#n8aAQafuafuaAQanD#jM#Jh#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gd#e7.OK.M0#pJ#HK#HK#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.UM.UM.M0anE#6w#6w#6w#6w#6w#6w#6w#6w#6wanGazs.M0.UM.No.UM.RY.RY.RY.RY#HK#AraARaASaAT#bH.35#DT.9I.9I",
-"#Uz#Vu#hY.0h.X2#ex#ew#RY#hY#hY#hY.0g.Rc#X8#g.#Ab#Aba.6a.6a.6#g.#g.#f.#hY.TO#hY.S4.N3.Oz.No.No.No.No.No.No.No.No.Oz.N3.R0.1B#TU#TU#TU#TU#TU#TU#TU#TU#TUamM.S4.Oz.OA.Oz.No.No.No.No.Ha.Ha.Ha.Ha.Ha.Ha.Ha.Ha.No.No.UN.3Z.HK#nq#Uz#e5aAU#DE.8B#qU.ND.NE.NU.NU.NU.NU.NU.NU.NE.NE#c2aAV#e6asF.Pp#ui.9n#de.ND.NE.NU.NU.NU.7n.Pe.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND##O.5B.8B.9n.Tl.PS.ND.NE#.d.8B.8B.8B.8B#8eaAWayyayxayxayxaAXaAYazo#DE#.f.O..Oq.NE.ND.ND.Oq.PS.Z1#xb#xb#fw#ul.YL.WD.WD.WD#Ea#m7.Tk#tp##Q#bt.UF#.W#ar##W.4s.TD.NV#wy.2p.TA.TA.TA.TA.TA#WG.8u#cn#.U#2f.TA.TA.TA.TA#WG#2g#2f#aD.9h.mZaAZ#f.#TC#gb#ge#ge#gb#gb#vc#Sw#1##Qm#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#Z.#Ee#jL#xm#xm#jL#tkahG#H8#H8#H8agH#Uy#gb#ge#ge#ge#E2ax2#0jaAnaA0#B##f2#cR#b2#cR#cR#ba#7naA1#bbaA2aA3aA4aA5aA6aA7aA8.HK.Q3.N3.RY.OO#gb#FN#wR#K0#wR#wR#wR#K0#ge#K0#K0#K0#K0#K0#K0#wR#n8.ON.MQ.V6.Rd.V6.21.Nc.X1.MN.Nc.21.21.MN.Nc.Rd#n7#n7.21#w.ayUax6aA9#SN.21#n7#n7#n7#n7.2R#n7.21.21.21#n7#n7.X1.X1.X1.Rd.X1.R1#ta#fU#fU#aa.43.43.43.OK.22.T1aB.aB#aBaaBbaBcaBd#mzaBeaBfaBgaBhaBiaBjaBkaBlaBmaBnaBoaBpaBqaBranDafuafuanD#jM#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gd.4e.M0#pJ.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.UN#t#aBs#6w#6w#6w#6w#6w#6w#6w#6w#6w#6wanG#Jh.K7.UN.UM.RY.RY.RY.RY.UM.6k#HKatdaBt.6e.9I.35.9I",
-"#E2#ge#gb#n8#hY#hY#e7#hY#hY.TO.TO#hY.1B#Ab#t##g.#Aq#Aq.T0.T0.T0#t#.M0.0g#hY#TU.W9.Oz.No.No.No.No.No.No.No.No.No.No.Oz.N3.RY#f.#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#f..R0.Oz.Oz.Oz.Ha.Ha.Ha.No.No.No.No.No.No.No.No.UM.UM#HK.3Z.MH#jL#e4#zC.5B.Pc#co.NE.NE.NU.NU.NU.NU.NU.NU.NE.Pd#q1.L4aBuaBv.Pn#rV#xc.5B#c1.5M.NU.NU.Pd.VA.2r.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND#ox.8B.8B.8B#de.PL.Ok.XF.8B.8B.8B.5B#cnaBwaBxaByaBzaBzazjaBA.Mk#tp#xbaBB.Oq.ND.NE.Ok.7o#Wj#aF#xb#xb#ul#fw#er#LM.4H.YV#m3#.c.TD.XF#Qt#ul#C9#DE#DE#DE#.b.NC#bv#jj.TA#jj.TA.TA.TA#2f#m3#fw#cn#bl.TA.TA.TA#oN#jj#jj#2g.XJaBC.aMaBD#SX#ge#gb#gb#ge#gb#gd#ge#e3#V1#Z.#Qm#Hb#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#j9#xm#sM#sM#sM#jL#54#H8#Hb#H8#Qm#sM#gd#ge#ge#jLax2aBEaBF#f2#zt#cR#b2#cR#cR#cRaBGaBHaBIaBJaBKaBLau3aBMaBN.Qd.8Z#B6#xl.Q0.XY#hY#wR#wR#Uy#K0#ge#K0#wR#wR#wR#ge#ge#K0#ge#Uy#K0#K0#wR#wh.0H.OO.MQ#ta.Lw.Nc.2RaoR.Rd#n7.MN.Rd.Rd.2R#n7.2R.2R.2R#n7.0f.RdaBOaBP##2.21#n7#n7#n7#n7.X1.X1.X1.21.2R.2R.21.21.M0.21.M0#fU.OK.22#gc#fJ#kx.22#aa.OK.1CaBQaBRaBSaxv.2X.2WavC.1L#qz#Zr#cEaBTaBUaBVaBWaBXaBYaBZaB0#cDaB1aB2aB3aB4aB5aBq#4.afuafuanD#Jh#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gd.4e.T0.O4.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.UN#pJ#Jh#6w#6w#6w#6w#6w#6w#6w#6w#6w#6w#6waB6#hu.RY.UN.UM.RY#oa#oa.RY#HK#AraB7aB8.9I#bH.35",
-"#Nc#Nc#gb#n8#n8#hY.0h.TO.TO#hY#hY#hY#hYamM.T0#t#.R1.R1.Xz.Xz.Xz.XY.XY#f.#TU#TU.Nf.N3.Oz.No.No.No.No.No.No.No.No.No.No.Oz.N3.UM.T1#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#SX#e5.R0.UN.SX.UN.No.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.Oz.Rq#gb#NU.6U#iq.9n#c1.Qs.NU.NU.NU.NU.NU.NU.ND.Oi.Mq.9AaahaB9.LZam7#y3.8B#.b.ND.O8.NU.NU.6X#c#.Pd.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.8p.8B.8B.8B##Q.7o##V.9n.8B.8B.8B#lNasIaBxaBzaBzaBzaC.aC#aCa.9s#J8#xb.TD.Oq.NE.Oq.TD#vn#xb#ul#xb#xb#ul#th#bl#br##Q.7o.NV#.b#w8#aF#xb#xb#xb#ul.YM.Ur.PL.Oc#Xc.4H.TA.TA.TA#2f#kS#C9#iq#C7#bl#Uv.TA#jj#jj#2g#2f.3iaCbQtRaCc#gb#ge#gb#ge#ge#gb#gb#gb#f##DC#j9ae6#Qm#I3#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#lT#sM#sM#xm#GD#sM#xm#4m#H8abU#2t#Xm.4e#Jh#e6aCdaCe#Cw#VF#cR#BS#cR#cR#BS#AA#pYaCfaCgaChaCiaCj.Hq.8Z.8Z.Ya#HK#HK#HK.Lq.VXaig#wR#Uy#K0#K0#Uy#ge#K0#ge#sM#geaig#sMaigaig#DN#DNaig#wRaig.P9.UP.2R#fU.2R.2R#n7.X3.Nc.Rd.21.2R#n7.2R#n7.21#n7.2R.X1#ta##2aCk.M7aCl#DO.21.21.2R.21.21.2R.2R.2R.21.2R.M0#ta#ta#ta.OK.43.7waCm.22#fJ.22#aa#ta#ta.M0ajs#bMaCnaCoaCp#mAaCq.2V.2V.1Q.1LaCraCsaCtaCuaCvaCwaCwaCxaCyaCzaCA.1PaCB#1Oau4aCCaCDaCEaCFanDafuaAQ#jM#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#e7.OO.Lq.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.UN.RY#e8anG#6w#6w#6w#6w#6w#6w#6w#6w#6w#6w#6wazs.K7.UN.UM.RY#oa.RY.RY.SY.0G.YaaCGasu.6e",
-"#E2#E2#ge#ge#ge#4.#iU#2R#hY#hY#TU#TU#TU#TU#e4.XY.XY.0f.0f.0f.0f.0f.K7.MV#SX#TU#D4.Oz.N3.No.No.No.No.No.Ha.Ha.Ha.Ha.Ha.Ha.Oz.N3.No#e5#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#SX.T1.W9#Eq.SX.UN.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.Nm.6b#nD#hh.8v.8B#zA.ND.NE.NU.NU.NU.NU.NU.NE.NU.VB.2AaCH#e6aAV.Pf#y7.Uq#bu.O..NE.NU.NE.NV#.N.NE.98.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.7o.9n.8B.8B#vn.0Z.Ns#mQ.8B.8B.8B#lNaCIaBxaBzaBzaCJaCKaCLaCM#.a#.W#Hi#J8.NU.Ok.ND.Oq.RR#J8#DE#C9#C9#fw.Tl##V.NE.Ol.Nt#.W#DE#xb#ul#xb#Uq#xb#.g.Ur.1f#py#Xc#jj#jj.TA.TA#Ie#hE#vn#ul#ul#fw#db#2f#jj#jj#2g.3e.3iaCNaCOaCP#TC#ge#gb#ge#ge#ge#ge#gb#gb#f##1R#U5acdahG#QmabU#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#H8#Z.#Sw#sM#Ee#Yk.0H.X2#jM#hv#Qm#H8#H8#lT#h9auJaCQaCR#B.#AA#BS#cR#cR#BS#cR#f2#yKaCSak8aCT.HI#oY#oc#B6#HK.Q0#HK.OW.O4.ON#gb#wR#K0#K0#K0#Uy#E2#E2#wR#DN#DN#DN#wRaigaigaigaigaig#wRaCU#FN#wR#hX.7w.OO.MQ.Q6.2R.Q6.0f.M0.MN#n7#n7#n7#n7#n7.21.21.X1#n7.39aCVaCW.R9aCXaCYaCZ.M0.21.21.21.2R.2R.2R.MV#ta#ta#fU#aa.22.4e.7w.43.43.OK.22.P8aoR.M0.M0.M0.Nf.R2.86aC0aC1aC2.2W.2V.2V.2V.2V.1QaexaCsaC3aC4#z5#GbaC5aC6aC7aC8aC9aD.aD##rmaDa.5e.2WaBSaDbaDcaDd#4.afuaAQ#jM#gb#gb#gb#gb#gb#gb#gb#gb#gb#gd#e4.T0.RY.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.UN.UM#huanG#6w#6w#6w#6w#6w#6w#6w#6w#6w#6x#6waDe#fU.SY.SY.RY.UW.UW.UW.UW.9LaDfaDgaDh",
-"#E2#ge#wR#wR#wRaigaig#Vu#2R#RY#TU#TU#TU#TU#TU#f..XY.XY.0f.0f.0f.0f.XY.T0amM#TUamM.P2.OA.Ha.Ha.Ha.Ha.Ha.Ha.No.No.No.No.No.No.UN.3Z.UM#e5#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#SX#f..Nf.UM.UN.UN.UN.UM.UM.UM.UM.UM.UM.UM.UM.UN.3Z.Qm.1BaDi#hM#cn#ek.ND.NE.NU.NU.NU.NU.NU.NU.NE.NU#iD.K##f#aDj.NQ#q1##W#cn.Tl.ND.O8.NU.NU.3N.Pe.O8.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.XF.5B.4J#kn#es#bu#.c.5B.8B.8B#EBaDkaDlaBzaDmaDnanBabVaDo.0U#.Z##O#Bg#ar.Oq.ND.ND.Oq#c1#btaDp#bw.ZX.Ol.Ok.ND.Oq##O#ul#xb#xb#xb#xb#xb#ul.53#.d.Od.3e#2f#Xc#2f#Sy#hG.5B.5B#Wi#xb#ul#ul#IeaDq#jj#2g.Nq#2faDraDsaDt#Cl#Nc#gb#ge#ge#ge#ge#ge#ge#gb#ge#yY#1a#4m#Z.#d3#Qm#H8#H8#H8#H8#Zy#H8#H8#Hb#Z.#2u#DC#tk#xm#Zwam5#Z5#yY#yY#yY#xm#Z5#4S#I#aDuaDvaDwaDxaDy#ba#b2#cR#cR#BS#zt#F#aDzaDA.N2aDBaDCaDD#h2.Jt.R1aDEaDF.5a.2R#n8#wR#Uy#E2#E2aigaig#E2#sM#E2#sM#DN#FNaucaig#n8#fI#e8#wS#wS#wS#jM#gbanDaAQ.5h##laDGaDHaDHaDIaDJ#TG.M0.21.2R.2R.2R.39aDK#TGaDLaDHaDMaDNaqyaDO#jQaDPaDQaDR.M0#ta.21.21.2R#ta.M0.Z##aa.OK.43aCm.43.43.OK#aa.P8.P8.M0.M0.M0.M0.M0#ta.22.22#taaDSaDTaDU.2V.2V.2V.2V.2V#.z#rlaDVaDWaDX#SSaDY#if#vN#rt#3a#0saDZaD0aD1#cD#1O#cD.5d.5e.4baD2aB4aD3#4.afuaAQ#jM#gb#gb#gb#gb#gb#gb#gb#gb#gb#e6.MV.O4.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa.UM.UM.UM#gcanG#6w#6w#6w#6w#6w#6wanGaD4aD5aD6aD7#5l#lh.SY.SY.SY.UW.UW.UW.UW.SY.6l.Q1",
-"aig#wRaigaigaigaigaigaigavc#Vu#2RaD8#TU#TU#TU#SX.OO#t#.XY.0f.0fajs#w..XYamM#TU#SX.R0.3Z.No.No.No.No.No.No.No.No.UM.UM.UM.UM.UM.UN.3Z.UM.T1#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#TU#TUamM.Nc.VV.Sb.UN.UN.UN.UM.UM.UM.UM.UM.UM.UN.OzaD9#xmadZ#ul#gj.NU.Qs.NU.NU.NU.NU.NU.NU.NU.NU.Peaai#I#an4.L2.4S#j6.5B#bw.Oi.NE.NU.Pd#c##nA.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NE#de.4r.YV#cm#nT.5B.5B.8B.8B#xbaE.aE#aEa#Hp#qV#ET#f.aEb#kk#C9.Ns.WR.0Z.Ns.Oq.ND.NE.ND.Oq.ND.ND.O#.ND.NE.ND.ND#bw#xb#Uq#xb#xb#xb#WU#.9.RP#2g#Sy#db#db#py#.U.4r.5B.5B#br#xb#xb.64##L#fm#jj#WG.ZYaEcaEdaEe#e4#f##gb#E2#ge#ge#ge#ge#ge#ge#ge#gd#gb#4U#4Tarbagq#lTadI#H8#Hb#Qm#dZ#2u#4Tam5#HZaadam5#7UahGahGahG#Qm#Hb#Hb#H8#Qmats#fJaEfaEgaEhaEiaEj#VE#yK#cR#ba#VE#VEapsaEkaElaEm.Sm.8YaEnaEoaEp.8XaEqaEraEsawHaEt#gd#FNaig#wRaigaigaucaucaigaigaig#DNaig#gd#Ji#FNaEuaEvaEwaExaEyaEzaEAaEBaECaEDaEEaEFaEGaEHaEIaEJaEKaEL.Neaw.aDLaDHaEMaENaEOaEPaEQaERaESaETaEUaEVaEWaEX#I6aEYaCZ#TG.X3.M0.M0.M0.Z#.OK#aa.OKaCmaCm.OK#aa#fU#ta.M0.M0.M0.R2#ta#g##aa#hu#g#.R2#C#aEZaE0.2W.2V.2V.2V.2V.2V.1L#rjaE1aE2aE3aE4aE5aE6aE7#vN#sq#1J#0s#Zm#0uaE8aE9#mz#4f.1LavC.5d.5e.7IaCCaF.aF##iuafu#jM#gb#gb#gb#gb#gb#gb#gb#gb#gb#e7#ta#wQ.UM.UM.UM.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.SY.SY.SY.4eanG#6w#6w#6w#6wanGanGaFaaFbaFcaFdaFeaD5aFfaFg.5b.SY.UW.UW.UW.UW.UW.9L",
-"aigaigaig#n8#hY.Za.MX.0h#E2aokavc#Vu#2RaD8#TU#TU#SX.T1.M0.XY.XY.0f.O3.0f#e6#TU#D4.UN.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.3Z#oa#f.#TU#TU#TU#TU#TU#TUaD8aD8aD8aD8#SX#SXamM.0g#TU#e7#e4.MV.O4.UM.UN.UN.UM.UM.UM.UM.UM.OW.Nm.JEaFh#DE.5B.RR.5M.NU.NU.NU.NU.NU.NU.NU.98.O8.9eaFi#e7#Pd.Pf.Mx##Q#cn.1f.O#.NU.NU.6X##B.Pd.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND#.X.0Z#Lr#iF.YL.9m.9n.5B.8B#iqaFj#Ii.71#sM#n8#n8aFkaFl.4x#tp#.g.Nt.NE.5C.NE.ND.NE.NE.NE.ND.ND.NE.NE.NE.ND.ND.8p#xb#Uq#xb#ul#ul.5B.5B#xF.9q#xb#fw#iq#Wi#fw#ul#ul#iq#ul#xb#xb#Wi#bp#jj.TA.XJaFmaFn#kv#Cl#ge#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#ge#Zw#Xm#w6#kV#Zx#54#DC#vc#1w#1R#kWagq#4S#Hb#Qm#2u#4s#mp.2R.21.21ajsaFo.0oaFpaFqaFraFs.M7aFtaFu#VEaFv#6t#b1aFwaFxaFyaFzaFAaFBaFCaFD.R.aFE.OQapNaoLaxnaFEaFFaqF#lhaucaucauc#DNaigaucaigaucauc#E2#JiaigaFGaEyaFHaFIaFJaiJaFKaFLaFMaFNaFOaFPaeUaFQaFRaFSaFTaFUahbaFVaFW#vGaEPaFXaFYaFZaF0.5o.7U.5o#afaF1aqOahi#sTaF2aF3aF4aF5aF6aF7aF8aF9aG..Ja.U0#n7.SQ.1SaG#.1S.SQ.P8aoR.M0.W6.Z##g#aGa#gcaoQaoQ.1C#TGavyaGbaGc.5daCq.2V.2V.2V.2V.2V.1L.1L#cEaGdaGeaGfaGgaGhaGiaGj#w2#ih#j##j##sk#3a#0taGkaGl#cD#rj.1Q.1QavC.5d.5eaGmaGnaGoaGpafuanD#gb#gb#gb#gb#gb#gb#gb#gb#gb#e7.MV.RY.UN.UM#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.UW.UW.UW.UW.UW.UW.UW.UW.SY.SY#lhanG#6w#6w#6waGqaGr#ad.6A.5o.89aGsaGtaGuaGvaGwaGx.9L.UW.0p.0p.0p.UW",
-"aig#n8.Ne.OF.J#.Q1.RY.P3#hY#4.#4.avc#4.#FR#2RaD8#TU#TU#SX.Nc.O3.R1.R1.T1#SX#e7.R0.3Z.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.3Z.RYalM#TUaD8aD8aD8aD8aD8#TU#TU#TU.0H.T0.T0.MV.MV.V6#e5#f..VX.OK.T0.RY.UN.UN.UN.UM.UM.UN.3Z.HH#yj.Uq#.f.Tl.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.Mi.J7#e6avL.6T.5N#gh.8B##Q.NU.NE.NE.Nt.8b.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.Tl.5B#nT.YL#.e#bl.9n.5B##PaGy#ss#n8#n8.9Q.9Q#e5aGz.3k.Vy##M.Pd.Mi.99.O8.NE.ND.NE.NE.NE.NE.NE.NE.NE.NE.Oq.RR#ul#xb#xb#ul#ul#xb#xb#ul#iq#ul#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#4D.Vy.9h.3gaGAaGB.Rc#Cl#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#ge#GD#2v#vc#Zw#f##sM#7T#Pt#Qm#H8adG#2b#sMaoR#HK#B6.OW#xl#h2aGC.LpaGDaGEaGF.OQ.OZ.M6.M6.M4.OQaGGaGHaGIaGJaGKaGLaGMaGNaGOaGPaGQaGRaGSaGRaGT#CmaGUaGVaGWaGXaGYaig#FNaucauc#DN#FNauc#DN#jL#wSaGZaG0aG1aG2ao3#bRaG3#uxaG4ao5aG5aG6.7JaqOaqO.1OaG7#.z#.zaG7aG7aG7.1LaG8aG9aH.aseaH##rg#wU.7X#r8.7X.7X#xoagaaeTaeR.2UaHaaHbaHcaHdaHeaHfaHgaHhaHiaHjaHkaHlaHmaHnaHoaHp.0f#jGaHqaC0aHraHraHraHraHs#bMaHtaHuaHv##9.5e.1Q.2V.2V.2V.2V.2V.2V.2V.1Q.1L#Zs#wV#mzaHwaHxaHyaHzaHA#GZ#s6#if#j##j##sk#3a#siaHBaHCaHD.1L.2V.1Q.2VavC.5e.5daHEaHFaHGafuanD#Jh#Jh#Jh#Jh#Jh#Jh#gb#gb#gb.0g.T0.SY.R6.SY.RY.RY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.SY.R6.Q0#3g#6w#6wanGaHHaFc.7V.9..7X.9U.7S.7U.5oaHI.4iaHJ.9LaHK.Q0.UW.UW.UW",
-"#wR#hY.Q1.Nn.Q1.UW.20aHL#2Raig#iu#vE#gd#4.aHM#FR#TU#TU#TU#SX.ON.OO.S3#SX#TU#e7.R0.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.3Z.O4#e6#TU#TU#TU#TU#TU#TU#gd#gd#e6.T0.R1.T0.T0.T0.T0.T0.T0#D4.0H.OV.MO.XY.RY.UM.UN.UN.UN.3ZacF.JQ#lN.8a.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.Qs#pka.P#I4.NL.QC.Sq.Uq.Pc.Ns.ND.NU.NU#.N.ND.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU.8p#cn#eq#.e.0U.YG.9n.8B.Uq#fr.ON.9QauM#mp.X2#zy#NF#dU#ul#j2.ND.1e.RG.NE.NE.ND.NE.NE.NE.NE.NE.NE.NE.ND.Oq#bv#xb#ul#xb#xb#xb#lN#ul#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#w8#.a.0TaHNaHO#e6#ge#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#gb#ge#ge#ge#gb#ge#7T#lhaHP#vD.Z5.OW.OW#HK#HK.Q0#h2aDF#kIaHQaHR.M6.X8.Nk.M6.M4.PY.M7apNaHSaHTaHUaHVaHWaHXaHY#HK#AraHZaHZaHZaDFaH0aH1.P8#gd#iuaoQ#hu#e4#E2aig#FN#DN#sM#DN#wR#wR#gd#IqaH2akWanRaH3aiYaH##afaiQaqy.7U.7U#ad#oVaH4aH5aH6aH7aH8aH9aI.aI.aI.ayp#xr.4baI#aI##qzaIaaIbaqx#kG#lB.7X.7XaIc.7X.6BagaaIdaeR#uOaj6.2UaIeaIfaIgaIh#OUaIiaIjaIkaIlaImaInaIoaIp#7MaIqaIraIsaItaIuaIvaIwaIxaIyaIz.5e.2U.1Q.2V.2V.2V.2V.2V.2V.2V.2V.1Q.1L.1L.1Q.1Laeo.1QaIAaIBaICaIDaIE#kM#lK#j##j##j##ifaIFaIGaIHafQab#.2V.2V.2V.1Q.2V.2W.5eaIIaIJaHGafuanD#jM#jL#gb#gb#gb#gb#E2#E2#n8.0H.5i.W7.SY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.SY.R6.Z5anE#2V#f#aIKaIL.7S.9U.9..6B.6B.6B.6B.9..9U.7TaIMaIN.Q0aIO#Sv#CK",
-"aig.0g.Q1.Q1#aP.HqaIPaIQaIRaISaITaIUaIV#AtanD#4.#Vu#2R#2R#TU#TU#TU#TU#TU#TU#TU.T0.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.3Z.W9#e7#gd#gd#gd#gd#gd#gd#gd#gd.0H.MV.T0.T0.MV.MV.MV.MV.T0.T0.MV.ON.P9.P9.MO.T0.O4.UM.3Z.5V#Rr.8v.9n#c1.Qs.NU.NU.NU.NU.NU.NU.NU.NE.NU.Mi#Ln#iu#fj.6T#rV.4s.8B##Q.NU.NE.Oi.Uu#y7.53.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.TD.9n#ul.2n#.e.Tk#fe.9n#xbaIW#e5#jL#jL#jL#gb#f.aIX.WD##L#.d.O#.ND.RF.QC.Pe.NU.NE.ND.NE.NE.NE.NE.NE.NE.ND.O8#Wj#C9#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#ul#Uq#knaIYaIZ#gd#ge#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#gb#gb.OK.Lq.Q0#HK.OW#HK#HK.Q0.Q0#B6aDDaI0aI1.Nh.M7.R..Nk.M5.OQ.R..RaaI2aGDaI3#HKaHZaDFaaT#re#B6#xl#HK.Q0#HK#HK.Q0.XY#e6#E2#wRaigaig#DN#sMaucaCUaig#DN#sM#DN#wR#DN#iuaI4#GG#fMaI5##4aI6.9UaI7aI8aI9aJ.aJ#aJaaJbaJcaJdaJeaJfaJg#ziaJhaJiaJiaJjaJhaJkaJlaGoaJmaDbaJnaJoaJpaJqaJr#a5.7T.7XaIc.7X.7X.9UagaafR#uOaJsaJtaJuaJvaJwaJxaJyaJzaJAaJBaJCaJDaJEaJFaJGaJHaJIaJJaJKaJLaJMaJNaJOaJP#1N#cD.1L.1Q.2V.2V.2V.2V.2V.2V.2V.1Q#cD#Zs#cD#1N#0x#Zq#tKaJQaJRaJSaJTaJUaJV#zZ#wq#if#j##j##ifaJWaJXaJYaJZatJ.2V.2V.2V.2V.1Q.2V.2W.5eaBSaB4aJ0aJ1aHMaAQaAQ#jM#jL#E2#E2#E2#E2#hY.21.RY.R6.SY.UW.UW.UW.UW.UW.UW.UW.0p.0p.0p.0p.0p.0p.0p.0p.UW.SY#HK.M0#2S#gb#gd#NcaJ2.7W.5o.7T.9U.6B.6B.6B.7X.6B.5o.9U#muaJ3#GE.RY",
-"aig#E2.XAaJ4aJ5aJ6aJ7.7V.7U#rg.5o.5oaJ8aJ9aK.#vE#JhanD#iu#RW#SX#TU#TUaD8aD8aD8#f..UN.3Z.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.UN.M0#e7#gd#gd#gd#gd#gd#gd#gd#gd#e7.0H.Rc.T0.T0.MV.MV.MV.MV.T0.T0.T0.Nc.ON.P9.TO.Za.OO#pJ##u.QP#cn.1f.ND.NE.NU.NU.NU.NU.NU.NU.NE.NU.NUaK##e4#ZE.17.RF#aM#.Z.Pc#zA.ND.NU.Px#rV.4s.5M.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Oa.4s.5B.YG#iF#.e#er#w8#lNaKa.1B#n8#sM#jL#sM.V6aKb#fl.VyaKc.ND.ND.NE.NF.3C.52.NE.PS.NE.NE.NE#d..ND.NE.NE.Oq##O#J8#xb#xb#ul#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#ul#ul#nUaKd.ON#Uz#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#e6#e7#2v#EeaoR#HK#HK#HK#HK.Q0.Q0.Q0#B6aDF.JuaKeaKf.R#.R.apMaKg.M5aKhaxkaKiatCaH0aH0aDE#HK.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK.Q0#w..0H#sM#wR#DNaig#K0#DN#DN#sM#wRaCU#ge#DN#sM#FN#wR#DN#IjaKj#EkaKkaKlamgaKmaKnaKoaKpaKq#gbanDaAQafuaAQanD#Jh#sM#ge#ge#Jh#Jh#Jh#jManDanDaAQafuafuanDaBr#4.aHMaKraKsaKtaKu#r8.7U.6BaIc#r8.6B#r8aKv.1L.7I#.zajEacTaKwaKxaKyaKz#SS#z1#z5#z5#QRaC4aCwaKA#1BaCvaKBaKCaKDaKEaKFaKGaKH#cD#1N#cD.1LaCq.2V.2V.2V.2V#rjaKIaKJaKKaKLaKMaKNaKOaCv#QR#CsaKPaKQaKRalSaKS#kM#if#j##j##kMaKTaKUaKVaKWafQ.2V.2V.2V.2V.2V.2V.1Q.2W.5d#.BaIIaD2#cFaKXaHG#jMaAQ#jM#gb#E2#E2#n8.0H.5i.R6.SY.0p.0p.0p.0p.0p.0p.0p.UW.UW.UW.UW.UW.UW.UW.UW#CK.Q0.Q0#fU#gb#gb#JhaKYaKZaK0aK1aK2.7W#kG.9U.5o.7T.6B.6B.5oaK3aK4aK5",
-"#ge#E2aoQaK6aK7.5o.6B.9..6B.6B.7X.6B.7T.5oaK8aK9aL.aslaL#aLaaLb#RWaD8#TU#TU#TU#e7.K7.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.3Z.UM.4e#gd.9Q.9Q.9Q.9Q.9Q.9Q.9Q.9Q.9Q.X2.W8.S4.T0.MV.MV.MV.MV.MV.MV.T0.T0.T0.Nc.OO.0g#n8.0gaLc#xb.Z1.Oa.NE.NU.NU.NU.NU.NU.NU.NU.NE.O..Sz#IQ#IQ.NK.6T.95##W.8B#gj.NU.NE.NE.7n#lS##V.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.PL.Uq.5B.9q#.e#.e.ZWaLd.3m#Xm#hY#sM#jL#sM.0g#Ig#io.0U#rF.Oq.PS.RR.TD.3O.3C.Ps.2r.Mi.Pe.Oq.ND.NE.NE.NU.NU.NE.O8#ax#fw#xb#ul#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#fwar9aLe.W8#e6#gd#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#gd#nD#Jh#xAaLfaLgajs#HK#HK.Q0.Q0.Q0.Q0#wNaHZ#i.aLhaLi.QaaLjaLkaLlaLmaLnaLoaLp#B6#9T#B6#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK.Q0#w..4e#sM#wR#wR#wR#wR#wR#wR#K0aig#wR#K0auc#FN#FN#DN#DN#DNaCU#FN#jM#ex#ex#ex#ex#JjaAQaLq#Jh#E2#sMaucaigaCU#FN#FN#FN#DN#jL.X2#f.#fU.MV.X2#gb#gb#gb#gb#gb#jM#jM#gd#gd#IjaLr#uJaLsaLtaLuaLv.7U.6B#r8.7X.6BatKahi#uO#.z.1L#ZraLwaLxaLyaLzaLA#Fb#z1#zw#7E#1BaCv#Fc#Fc#FcaCvaLBaCwaCwaCvaLCaKKaLDaLE.1L#1NapCafQ.1L.1L#rjaLFaLGaCwaCwaCwaCwaLB#Fc#Fd#z5#CsaLHaLIaLJaLKaLL#vN#if#j##j#aLMaLNaLOaLPaLQavC.2V.2V.2V.2V.2V.2V.1Q.2VavC.2W.5d.5e.5daBS#a.aLRaCFanDaAQ#gd#E2#UNalN#fU.SY.SY.Q0.UW.UW.UW.UW.UW.UW#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#pJ#e7#ge#ge#gbazbamuaLSaLTaLUaLVaLWaLXaro.7S.7S.6B.5o#q8aLY",
-"#SX#gbaLZaL0.5o.7W.6B.6B.6B.6B.6B.6B.6B.9..9UaL1aL2#lBa#K#yzaL3aL4aL5#iu#gd#gd#gd#e4.UM.3Z.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.3Z.R0.X2#kw#kw#kw#kw#kw#kw#kw#kw#kw.9Q.VX.MV.S4.T0.MV.MV.MV.MV.MV.MV.MV.MV.T0.T0.T0.MV.RdaL6#iq#Ml.VB.ND.NU.NU.NU.NU.NU.NU.NU.ND.NU#lOaL7#f.aL8.6T.2A#uk.9n.8B#p4.ND.Pd.9e#uj#.b.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.Tl.5B##P#er#.e#.e.Oo#cnaL9#e5#n8#jL#sM#E2apaaq1#zG#xb.NE##Q#xb#ul#ul.UF.Pf.Pp.3E#fd.99.3r#nA.RG#.N#c##e#.NV.97.J1#Bg#DE#ul#xb#xb#xb#xb#xb#xb#xb#xb#xb#ul#iq#aCaM.aM#aMa#9p#f##e7#e7#gd#gb#gb#gb#gb#gd#gd#gd#e7.0g#e7#e7#gb#f#atsaMbaMcaMdaMe.30.OW#HK.Q0.Q0.Q0.OW#9TaMfaKe#bK#jQaboaMgaMhaMiaMjaMkaMlaMm#Ar#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK.Y..Q0.XY#e4#sM#wR#E2#wR#E2#E2#wR#wR#wR#wR#wR#wR#wR#wR#K0#wR#DN#DNaigaigaigaig#wR#wR#wR#wR#wR#wR#wR#wR#wR#wR#xm#FN#jL.X2.ON.0f.K7#pJ.O4.UM.UM.UN.Nc#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gbanDaLbaLraMnaMo.89#rg.7X.7X.6BagaafR#uO#.z.2V#rjapCaMpaMqaMraMsaJBaMtaMuaMvaMwaMx#FcaCvaCwaCwaCwaCwaLBaCwaLGaMy#z5aMzaMAaMBaMCaMD#saaMEaMFaMG#z5#z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#TM#z5aMHaMIaLKaMJ#kM#if#lK#kMaMKaMLaMM.1QafQ.2V.2V.2V.2V.2V.2V.2V.2V.2V.2V.2V.1Q.2V.2W.5e.5daBSaMNaMOaMPamzaMQanJ#5j#e8#CK#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.9LaoR#gb#ge#ge#gb#gd#.taMRaD7aD7aMSaMSaMTaMUaMV##p.7SaMW#oU",
-"aHp#fCaMXaK8.9U.6B.9U.9U.6B.6B.7X.6B.6B.6B.7X.6B.9U.6B.6B#rgaJ7#yzaMYaigaMZ#gd#gd#e6.T0.UN.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.UN.ON.9Q.9Q.9Q.9Q.9Q.9Q.9Q.9Q.9Q.9Q.9Q.VX.MV.T0.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.T0#g.#ss.KhaM0#.8.NE.NU.NU.NU.NU.NU.NU.NU.NE.NE#.0#gg#f#awV.9Aaai#bm#.d.5B.9n#c1.NE.51#LP#bw.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.NU#gj.8A#Lr.3A#.e.0U#nT.6U#Z5.0g#jL#sM#jL.T1#Wl.2p#.a.Pd#qT#DE#DE#aF.0Z.TD.ND.NE.NE.NE.ND.ND.NC.2r.2A.WF.NL#.N.L7#hF##Q#C8#xb#ul#xb#xb#xb#xb#xb#xb#xb#fw#DE#Y1aM1aM2#nHaM3aM4aM5aM6#ht#e7#hY#f##qR#R6aM7aM8aM9aN.aN#aNaaNbaNcaNd#Lr#lSaNe.2J#HK.Q0.Q0.Q0#HKaDF.J#aNfaKfaNgaNhaNiaNjaNkaNlaNm#tVaNn#wO#xl.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#xl#CK.0f.VX#sM#wR#wR#wR#E2#wR#E2#wR#DN#E2#E2#wR#wR#wR#wh#wh#DN#wR#wR#wR#wR#wR#wR#wR#wR#sM#sM#E2#jL#jL.9Q.9Q#fI.X2.0H.Nf.VV.RY.UM.UM.UM.UM.UM.UM.RY.UM.UM#fU#gd#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#jM#vEaLbaNoaNp.7U.7XaIc.6B#r8aNq.2Uadp.2V.1L#saaNraNsaNtaNuaNvaNw#saaNxaNyaNzaNAaNBaNCaNDaNEaNFaNGaNH#23aNIaNJaNKaNLaNMaNNaNOaNPaNQaNRaNS#z1#z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#CsaNTaNUaNV#rx#vNaNWaNX#u1aNYaNZaN0atJ.2V.2V.2V.2V.2V.2V.2V.2V.2V.2V.2V.2V.2V.2V.1Q.2V.2W.5e.2WaN1aN2aN3aN4###aqJ#Nc.TZ#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#CK#e7#ge#ge#ge#E2#gb#gbalN#6wanHanHaN5aMSaN6aN7aN8aN9.89",
-"aO.aO#aOaaOb.6B.9Uagaao5aOc.9UaL1.9U.6B.6B.6B.6B.6B.6B.6B.9..6B#rg#a3#sQaOd#IjaLr#e7.MV.Lq.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.UN.UM.0f.21.Nc.OO.0H.X2.9Q.9Q.9Q.9Q#.E.9Q.VX.T0.T0.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.Nf#wQaOeaOfaOg#nX.NU.NU.NU.NU.NU.NU.NU.NU.ND.RR#j6aOh#ge.J7.QD#q2#un#bu.8B#ar.5M.NE.4K##Q.TD.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.5C#aH.5B#.a#.e#.e.0U#HjaOi.T1#n8#sM#sM#n8#E2#3D.WD.1f.5C#fw.8p.00.ND.Ol.5M.ND.Qs.NE.Oq.Oq.ND.PS.Oi.ND.YJ.QA.Pl.Pi.NLasS#C8#xb#ul#xb#xb#xb#xb#xb#xb#fw#DK#XSaOjaOkaOlaOl#nHaOmaOmaOnaNaaOoaOpaOqaOnaOraOlaOmaOsaOt#nHaOm.1qaOu#DEaOv#3e.Q0.Q0.Q0#HKaH0#h2aOwaOxaOyaOzaOAaOBaOCaODaOEaOFaOG.N3.Oz.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK#CK.21.X2#wR#wR#E2#E2#E2#E2#wh#wR#E2#wh#wR#wh#wh#wR#wR#wR#wR#wR#wR#ge#E2#gd#ew.0H.OK.MV.Nf#t#.K7.R0.R0#pJ.O4.O4.O4.RY.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.UM.UM.Lq.OK#e7#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gdaLbaGpaOH#kG#rg.7X.7X.9Uao5aOI#uO#.zavCaf7aOJaOKaOLaOMaONaOOaOP.5eapC#1N#1N#1N#cD#1N#1N#1N#1N#0x#1NariaNxaOQaORaOSaOTaOUaOVaOWaOXaOYaKzaOZ#z5#Fd#Fd#Fd#Fd#Fd#Fd#z5aO0aO1aO2aO3aO4aO5aO6#u1aO7aO8aO9afQ.2V.2V.2V.2V.2V.1Q.2VavCavCavCavC.2V.1Q.2V.2V.2V.2V.1QavC.2W#7O#.BaP.aP#aPaaPb#G4.Rl#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#D4#gb#ge#ge#ge#ge#gb#gbalN#6wanHanHanHaN5aPcaPdaPeaPf",
-".5e.2X#qzacRaeUakHahi.1LaPgahj#wjaPh#pQ.7T.9U.6B.7X.6B.6B.6B.6B.9..6B.7U.7XaPiaPjauw.4e.OO.R0.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UN.UN.UN.UN.UN#nh.RY.W9.Nc.VX.9Q.9Q.9Q.9Q.0H.T0.T0.MV.MV.MV.MV.MV.MV.MV.MV.MV.MV.T0#nhaPkaPlaPm.Rv.NU.NU.NU.NU.NU.NU.NU.NU.NE.2#.QO.2daPn#qa.NI#ph.LN#bv.5B.8B.5C.NE.4R.Mj#de.5M.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Qs.NU#mQ.5B#aF.Tk#mh#.e#kn#aGaPo.S3#K0#E2#E2aq4aPp.3gaPq.NE##O#c1.Pd.00#.W##Q.0Z#Wj.9n.8p.Qy##V.ND.Oq.ND.NU.ND#c#.NS.4z.RC.01#ul#DE#ul#xb#xb#xb#xb#xb#ul#tp#4HaPraOkaOlaOlaOlaOlaPsaOlaOmaOm#nH#nHaOlaOlaOlaPsaOlaOlaPt#MfaPu.IIaPv#HK.OW.Q0#HKaH0#B6aPwaPxapMaPyaPzaPAaPBaPCaPDaPEaCZ.I2.UN.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK.Q0.Z5.Q6#jM#wR#wR#wR#wh#wh#DN#E2#wh#E2#DN#E2#wR#E2#xm#xm#wR#sM.9Q.VX.OK.21.XY.R0.O4.RY.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.UM.UM#wQ.S4#D4.0H#ew#gd#FR#gb#gb#gb#gb#gb#gb#gb#gb#IjaLbaPFaPG#kG.7U.6B.6BaPHaPIahd#.z.1Q.2Waf7aPJaPKaPLaPMaPNaN1.5d.2W.2V#aX.1L.2V.2V.2V.2V.2V.2V.2V#1NaPOaPPapCaPQaPRaPSaPTaPUaPVaPWaPX#Fd#TL#Fd#Fd#Fd#Fd#Fd#Fd#z5aO0aO1aPYaPZaP0auQaP1aP2aP3aP4.2V.2V.2V.1Q.2V.2U.5e.5daP5#cEaP5avC.5e.5e.5davC.2V.2V.2V.2V.2V#.z.1OaEVap3aP6aP7#2VaP8#C##HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.UO#gd#ge#ge#ge#ge#ge#gb#gbalN#6wanHanHanHanHanH#fJ.Zc",
-".2U.2U.1L.1L#.z#.z.1L.1L.1Ladp#o6.1OaP9abdage#pQ.7T.6B.9..6B.6B.6B.6B.9..6B.5oaQ.aQ#auc#fJ.VX.O4.3Z.UN.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM#nh#nh#nh#nh#nh#nh#nh#nh#nh.UM.R6.UN.UN.UN.RY.T0.1B#n8#n8#2R.ON.T0.T0.MV.MV.MV.MV.MV.MV.MV.MV.T0.T0.VV#.HaQaaQb.2s.O8.NU.NU.NU.NU.NU.NU.NU.ND.NE.4s.Mj.15ap#arL#pg#bmaQc#.c#aH.0Z##V.Tq.Kt.8B.PL.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.5C.8B.8w#dd#.e.0U#.eaQdaQe#wR#n8#E2#wR.0g#IhaQf.9l.NEaQg#fw#Bg#xb#DE#aF#xb#xb#xb#C9#C9#iq.0Z.XF.NE.ND.NE.NU.Pe.RG.L2.8f#gu#fu#C8#ul#xb#xb#xb#xb#ul#tpaQhadCaOkaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaQiaQj#ck#..ae5.2J#HKaH0aDEaQkaQlaboaQmaQnaQoaQpaQqaQraQs#HK.IW#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK.Lq#e5#jL#xm#wR#wR#E2#wR#E2#E2#wR#wR#xm#xm#xm#ge#uI#e7.0H#fU.T0.K7.O4.RY.RY.UM.UM.UM.UM.UM.Nm.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.P2.UM.UM.UM.UM.RY.O4.W9.T0#fU#e4#e7#gb#gb#gb#gb#gb#gdaLrawnaQtaQuaQv.7U#rgaQwaba#uO#.z.2V#.zaQxaQyaQzaQAaQBaQCaQDaQE.5e.2W.2V.1Q.2V.2V.2V.2V#.z#rjaQFaQGaQHaQIaCr#ZraQJaQKaQLaQMaQNaQOaQP#Gb#z5#Fd#Fd#Fd#Fd#Fd#Fd#z5#TMaQQ#TLaQRaQSaQTaQUaQV.5e.2V.1Q.1Q.2V.1OaQWaQXaQYaQZaQ0aJlaQ1#zlaQ2aQ3#.B.5e.5davC.2V.1Q#.z#.zacRa#zaez#qxaQ4aQ5aQ6aQ7aJ4.5aaQ8#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#HK.Q0.6q#ge#ge#ge#ge#ge#ge#gb#gbaDeanHanHanHanHanHaoX#w.",
-".2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2V.1L#.z#o6.1LaP9aQ9#a4#nkaL1aL1.9U.6B.9..7X.9..7T#jTaR.aR##At.MZ#wQ.UN.UN.UM.UM.UM.UM#nh#nh#nh#nh#nh#nh#nh#nh.UM.UM.UM.UM.UM.UM.UM.UM.UM.UM.SY.SY.W7.R6.R6.SY.0f.VX#n8#hY.V6.S4.T0.MV.MV.MV.MV.MV.MV.MV.T0.T0.T0.0haRaaRbaRc.Sq.NU.NU.NU.NU.NU.NU.NU.NE.ND#gh.Z1.LQ#d8#IQaRd#rV.O8.6R.9n.8B.0Z#e0.RG#...Qy.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Qs.Oi#bw.5B#vn#ft.3A#mh#.Y#jh#ivapa#n8#wR#E2.UZaRe.Nr#vj.XF#C9#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#aF#aF.8p.ND.ND.NE.NE.NU.Pe.9e.3CaRf.8p#C8#ul#ul#xb#xb#ul#D.#UvaRgaRhaPsaOlaOlaPsaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaPsaRi#ND#HjaRj#IraDF#h2aRkaRlaRmaRnaRoaRpaRqaRraRsaRt.UN.IW#HK.Q0.Q0.Q0.5a.5a.5a.5a#HK#HK#HK.UO#f.#jM#ge#sM#sM#E2#wR#E2#sM#xm#xm#xm#sM#gd.4e.Nc.Nf.R0.O4.RY.UM.UM.UM.UM.UM.P2.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.UM.UM.UM.UM.UM.UM#wQ#t##fU.VX#gd#gb#gb#gb#gdaLr#vEaL.aRuaRv.5o.5paix#uO#.z.2VaRwaRx.6haRyaRzaRAaAQaRBaRCaC2.5e.2W.2W.2WavC.2V#rj#24aRDaREaDWaRFaRG#rj.1L#Zr#cEaRHaRIaRJ#if#sk#ie#F8#id#z5#Fd#Fd#Fd#Fd#z5#z5#Fd#zw#HPaRK.K2aRLaRM.0E.2V#aX#.BaRNaROaRPaIOaRQaRQaRR#h6aRS#HsaRTa#6aRUaRV.6o.5d.5davC.1Q#.z#uO#sTahvauraRWaRXaRYaRZaR0aPeaR1.Ty#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#fU#ge#ge#ge#ge#ge#ge#ge#gd#geanGanHanHanHanHanHazs",
-".2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L#.z#o6#sTabcaQ9aR2agb.6B.9U.6B.7X.9..6BatLaR3anC.UO.RY.UN.UN#nh#nh#nh#nh.UM.UM.UM.UM.UM.UM.UM.UM.UM.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.Yb.RY.T0.1B#hY.MV.S4.MV.MV.MV.T0.MV.MV.MV.MV.T0.T0#oaaR4aR5aR6.Rv.NU.NU.NU.NU.NU.NU.NU.NU.Oa##O.Uq.Ob.PwaR7#gdaR8.Ks.Vw.8a.5B.8B.8B.LP#vk#.Z.NU.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.Qy.8B.9n#nT#.e#gB#iF#epaR9#GD#n8#E2#E2.1BaS..N6#ci.NU#Bg#Nh#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#.Z.ND.ND.ND.ND.Oq.ND.NU.ND#ay.3C.UF#C8#xb#ul#xb#fw#DE#WGaRgaS#aPsaOlaOlaPsaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaM3aSa#XQ.9raSbaScaSdaRlaSeaSfaSgaShaSiaSjaSkaSl.IW.Oz#HK.5a.5a.5a.5a.5a.Q0.Q0#HK#HK.UM.Lq#f.#gb#ge#ge#ge#f##f##ge#f##f##gb#e7.4e.MV.K7.O4.UM.UM.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa.UM.UM.UM.RY.RY.SY.SY.SY.SY.SY.5i.Nc.0g#E2#E2#E2#jL#iuaLraSmaSnaRv#adaiw#o6#.zaJtaSo.M4axnaSpaSq#e7#jMaAQaCFaSraIIaSsaBS#.B.5daStaKGaC4#z5#FdaCuaRDaPQ#rj.1Q.1L#ZraSuaSvaSwaSx#lK#lK#if#0q#AB#z5#Fd#Fd#Fd#Fd#PN#Fd#h.aSy#L2.Ya.GWaSz#.A.1NaSAaSBaSC.Ty#CK#CK.Q0.Q0.Q0.Nf#ewaDeaSDaPb#6waSEaSFaSG#.z.5davC.1QaJs.2U.2V#uO#.z.5e.5d.0waSHaSIaIO.5a#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#w.#gb#ge#ge#ge#ge#ge#ge#gb#gd#2S#6wanHanHanHanH#6w",
-".2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L#.z#.zadp#o6#.AaggaPh.9U.6B.6B.7X.6BaL1aSJatS.UN.UM.UM.UM.UM.UM.UM.UM.UM.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.R6.UM.XY.OO.UZ.T0.T0.T0.T0.T0.MV.MV.MV.MV.MVaSKaSLaSMaSN.Qs.O8.NU.NU.NU.NU.NU.NU.NU.NE.PL.Uq##Q.Mj#bk#ZF#D4#gf#bk#.b#.c.5B.8B#ctaSO#cn.00.Ok.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND#c1.9n.5B.64#aC#nV.0U#.e#eqaL9.S3#n8#Vu#n8#D4aSP.8F.WR#.W#aF#xb#xb#xb#xb#xb#xb#xb#xb#xb#xb#aFazn.Ol.ND.NV.NV.NV.NV.Oq.NU.Oq.L2.Pk.96#C8#xb#ul#ul#xEaSQ.Gi.RMaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaSR.Mx#WiaSSaSTaSU#IbaSVaSWaSXaSY#VAaSZaS0.Tf.Oz.UM.Q0.Q0.Q0.Q0.Q0.Q0.Q0.UM.UM.UM.K7.4e#gd#gb#gb#ge#ge#ge#ge#ge#gb#e6#D4#t#.O4.UM.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.SY.SY.UW.4..4..4..4..4..UW.SY.SY.SY.SY.RY.1J.ON#n8#E2#E2#E2#iuaLbaS1aS2#waaS3aS4#9haS5#Hf.M4aFFaS6#gc#4.#4.#jMaAQaS7aS8aJgaS9aT.aT#aTaaTbaLB#QQ#z5aTcaGd#rj.2V.1Q.1Q#rj#rjaTdaTeaTf#if#ih#if#ih#ie#s5#z5#Fd#z5#Fd#Fd#PN#h.aTgaTh.TL#aPaTiaTjaTkaSB#CK#CK#CK#CK#CK#CK.Q0#HK.RY#aaaTlanHaTmaMQaPbaxraTna.GaToaTpaTq.7I#.z.2V.2VaTravC.2W.2XaTsaTtaTuaTv.Q0#CK#CK#CK#CK#CK#CK#CK.Q0.Z5#gd#ge#ge#ge#ge#ge#ge#ge#gb#gbalN#6wanHanHanHanH",
-".2U.2U.2U.2U.2U.1O.1O.0E.0E.1N.1N.1N.2X.5e.5e.5e.5e.1O.1O.2U.2U.1L#o6.1OaQ9.9U.7W.6B.9..7SaTwaTx.5b.R6.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.R6.UM.R0.XY.T0.MV.ON.MV.T0.T0.MV.MV.MV#X9#alaTyaTz.8M.Sq.NU.NU.NU.NU.NU.NU.NU.ND.NE#mQ.5B#.d.MraTA#e4#e4#kmaTB##Q.9n.5B.8B.Qz#.d.0Z#.0.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Oa.Z1.8B.8w#DA#.e#.e.0UaTCaTD#jL#jL#wR#E2#e6#Rt#as#.Z.53#fw#ul#xb#xb#xb#xb#xb#xb#xb#xb#ul#fw##V.Oq.NV.NV.Oq.Oq.NV.NV.NV.NU.1p#au.3oaTE#xb#xb#ul#fwaTF.EdaS#aOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaM2aTG#nUaTHaTI#8VaRnaTJaTKaTLaTMaTNaTO.I2.No.Q0.Q0.Q0.Q0.RY.RY.RY.RY.UM.UM.UM.R0#f.#gd#gb#gb#gb#gb#gb#ge#gb.6q#ta.Lq.UM.UM.UM.UM.SY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.RY.UW.UW.UW.SY.SY.UW.4..4..4..5i.5i.5i.4..4..UW.SY.SY.SY.SY.SY.4..Nc.X2#E2#E2#E2#iuaMZaTPaTQaTRaf7aTS.NjaboaTTaTU#e8#E2#E2#E2#gb#.t#.t#.t#HsaTV#HsaTWaTXay2#lJaCvaMxaTY#rj.2V.2V.2V.1Q.1L#rlaTZaT0aT1#yQ#lK#if#if#if#Cu#Ga#Fd#z1#z5#Fd#z5#TLaT2aT3.OA.ZbaT4aT5aSCaQ8#CK#CK#CK#CK#CK#CK#CK.Q0#HK.TZaz.anGanHanHaTmaMQaMQaT6aT7aT8aDUavCaTr.1QavC.2V.2V.1Q.2W.5eaT9aU..5aaRQ#CK#CK#CK#CK#CK#CK.Q0#CK#e6#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#1vanHanHanHanH",
-"af7.2X.1M.0E.2V.6oaU#aUaaUbauzaUcaUdaBaaUeaUf#aYaT9aUg.6o.2V.1N.1M.1NaI#.0EafT.9U.7W.6B.7W.7VaUh#ln#HK.R6.R6.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.R6.SY.R0.Nc#n8#hY.OO.T0.T0.MV.MV.T0#p9aUiaUjaUk.Rv.O8.NU.NU.NU.NU.NU.NU.NE.ND##W.8B.9n#.9aUl#f.#ge#e4#htaUm.8A.5B#cn.Vw#vk#w8#aH#.W.RA.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.ZX#aH.9n#aF.0U#mh#.e#.Y.5AaUn#e4#wR#E2#E2#e5aUoaUp.QyaKc#xb#xb#xb#xb#xb#xb#xb#xb#ul#aFaKc.Oq.NV.NV.NV.Oq.Oq.Oq.Oq.NV.Oq.YJaUqaUraUs#rN#xb#xb#fw##UaUtaRiaPsaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaOlaUuaUvaM3aUwaUxapMaUyaUzaUAaUBaUCaUDaUE.HB.No.RY.RY.RY.RY.RY.RY.RY.RY.RY.UM.UN#pJ#e5#gd#gb#gb#gb#gb#gb#gd.0H.M0.Lq.RY.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.SY.SY.SY.SY.SY.UW.UW.UW.4..4..5i.4..4..4..4..4..UW.SY.R6.UW.0f.0g#E2#E2#E2anDaUFaUGaUH#cDaUIaUJ#4s#vE#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#Nc#.t#hX#7H#37aUKaDWaULaCr#.z.2V.2V.2V.1Q#.z#rlaPQaUMaUN#zv#if#if#if#lK#HQaMJaUO#z5#z5#Fd#z5#h#aO3aUP#2GaUQ.Q0#CKaQ8#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#HK.Q7aBsanHanHanHaTmaURaPbaT6aUSaCq.2Wau4.6o#.zavC.2V.2V#.z#cDarCaUTaUUaK5.Q0#CK#CK#CK#CK.Q0.Q0#e4#ge#ge#ge#ge#ge#ge#ge#ge#gb#gd#TC#5janHanHanH",
-"aUVa.GaUWaUXaUYaUZaU0aU1aU2.MM.Q1.UW.Q1.Rd#hXauc.4g#nbaU3aU4aU5aU6aU7aB2.4baS3#.G.7T.6B.9..6B.7WaU8avy.RY.R6.R6.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.R6.R6.UM.S4.1B#n8.0H.UZ.T0.T0.MN#nhaU9aV.#xT.NE.O8.NU.NU.NU.NU.NU.NU.NE#Ft.Tl.8B.5B##Q#tl#RW#gb#ge#WKagRaV###M#.f.Uq.LN.9n.8B.8B#bu.Tl.Oi.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.Qs.Pd#.c.5B.5B#.a#.e#.e.0U#yWaVa#e5#jL#4.#DN#f.aVb#db#Hi.1f#ul#ul#xb#xb#xb#xb#xb#Uq#xb#w8#c1.NC.NV.NV.Oq.NV.NV.Oq.Oq.Oq.Oq.99aUsaPtaVc.8f.Sz#ul#xb#fe.OcaVdaSRaOlaOlaOlaOlaOlaOlaOlaOlaOlaUuaUvaVeaVfaVg#8WaVhaViaVjaVkaVlaVmaVnaVo.Km.UM.RY.RY.RY.RY.RY.RY.RY.RY.UM.UN.RY#D4#e7#gb#gb#gb#gb#gd#e4.MV.VV.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.RY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.Q0.SY.SY.SY.SY.0p.UW.4..4..5i.5i.5i.5i.4.#CK.Q0#HK.Q0.Nf#e6#ge#ge#geaKY#HsaVpaVqaVraVsaigaTV#uI#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#Uz#jLaVtaVu#PNaVvaVwaex#.z.2V.2V.2V.2V#.zapCaVxaVyaCw#F7#AF#lK#if#if#ihaVzaVAaVB#TM#Fd#z5#TMaVCaVDaVE.N3.UM#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#ID#gcaB6anHanHanHanHaURaMQaVFaVGaDU.4bau4.4b#.zavC.2V#.z#uOaVHaVIaVJ.5aaRQ#CK#CK#CK.Q0.Q0#f.#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gdagR#6wanHanH",
-"#H8aVKaVLaRQaVM.UW.UW.Q1.Q1.Q1.Q1.Q1.Q1.UW.OF.MZ#uI#Jh.M0ayUaVM#ln.R0aVNaVOaVPaVQaVR.7S.5o.7T.6AaVSaVT#w..R0.R6.R6.R6.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.7G.7G.7G.7G.7G.7G.7G.7G.R6.R6.SY.0f.0g#hY.OO.0f.0f.RYaVUaVV#mY#qi.Sq.NU.NU.NU.NU.NU.NU.NU.ND.TD.5B##M#.Z#bzaVW#gd#ge#ge#e7#gdaVX.9n#vn#j6.Qy.8B##M.8B.8B##Q#j2.NU.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.4s.8B.9n.YG#.e#gB.0U.WEaVY#hv#n8#wh#DN#gd#jLaVZ#ZS.5C##Q#xb#xb#xb#xb#xb#xb#xb#xb#j2.Pe.NV.NV.Oq.NV.NV.Oq.NV.Oq.Oq#.n.1oaV0aPtaV1.Ph.QF#dV#fw#xb.NB.QNaV2aRhaOlaOlaOlaOlaOlaOlaUuaUvaV3aV4aV5aV6aV7aV8aV9aW.aW#aWaaWbaWcaWd.L1aWe#PW.UN.RY.RY.RY.RY.RY.UM.UM.UM.T0#e6#gb#gb#gb#gb#e7#fU.Lq.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.0p.0p.0p.0p.0p.0p.0p.0p.0p.UW.UW.UW.Q0.Q0.Q0.Q0#CK.Z5.Z5.UO.Z5.Z5.Lq.Nf.Lq.Q0.Q0.Nf#e6#ge#ge#uIatQawnaWfaWg.7KaWh#.taTV#uI#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#jL#eyaWiaCwaWjaWkaex#.z.2V.2V.2V.2VaCqapCaKHaMzaCw#z1#BQ#sk#ih#if#wqaWlaWmaWnaOZ#z5#Fd#CsaWoaWpaWq.Q0.UM#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Rl#HV#6wanH#6waB6agR#bDaJhaWraDUavCaP5.0D#.z.1Q.2V#.z#uOaeS#.GaWsaWtaHK.Q0#CK.Q0#HK.OK#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gbalN#6wanH",
-"ajtaWu#3h#Z4.Q1.UW.J#.MM.J#.J#.J#.J#.J#.Q1.Q1.O4.21#hX#ge.OO.VV.J#.Jt.R0#oS#ks.TyaWvaWwaWxaWy.7U#n3aWzavy.T0.S4.SY.R6.R6.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.SY.7G.7G.7G.7G.7G.7G.7G.7G.SY.SY.SY.SY.SY.SY.SY.SY.SY#HK.OW#CK.OK#hY.0H.MV.R0.U.aWAaWBaWC.Qs.O8.NU.NU.NU.NU.NU.NU.NE.NU#mQ.8B.8B.KtaWD#e6#ge#ge#ge#gd#gcaWE#...9n.Mw.8B.8B.8B.8B.8B.8B.8a.PT.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND##O#aH.5B##P#dU#mh#.e#.e#.#aWF#e4#E2#E2#E2#e5#hj#Gl#ar.00#xb#ul#xb#xb#xb#ul#aF#.W.Oj.NV.Oq.Oq.NV.LO.Z1.PT.Oj.Oq.NV.QsaWGaM2aWH.Pi.Pi.Ph.QF.5N#Hi#braWIaeZaWJaOkaOlaOlaOlaUuaUvaWKaWLaWMaWNaWOaWPaWQaWRazxaWaaWSaWT#.O.LX.Ph.Pi.L0aWU.UNaWV.KT.RY.RY.UM.UN.VV#f.#FR#gb#gb#gd.6q.T0#wQ.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.RY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.0p.0p.0p.0p.0p.0p.0p.0p.0p.UW.UW.UW.UW.UW.UW.UW.UW.UW#CK#CK#CK#CK#CK.Q0.Q0.Q0#CK.Z5.Z5.Z5.Z5.UO#t#.Lq.Z5#CK.Z5#ta#e7#ge#ge#.taLqaWW#cD.1LaWXaWYauc#Hs#uI#ge#ge#ge#ge#ge#ge#ge#ge#ge#n8#0h#PPaDWaWZaPQ#Zr#.z.2V.2V.2V.2V.1QapCaW0aW1aC4#Fd#id#G0#lK#if#kMaW2aW3aW4#Cs#z5#Fd#TM#QRaW5aW6.I0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Q0#aaanE#2S#gb#gb#gb#Hs#JhaW7#.z.2WaTraTr.1Q.2V.2V#.zahdabd#qwaTwaW8aHK.Q0.Q0#HK.OK#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gd#gbaoXanH",
-"aWuajtaWuaW9#DC.P3.RY.Q1.J#.MM.J#.MM.MM.MM.J#.J#.O4.OF.P9#ge.MX.R0.W9.K7.K7.K7.J##oS#uu.SQaX.aX##i3##paXaavy.MV.T0.O4.R6.R6.R6.SY.SY.SY.SY.SY.SY.7G.7G.7G.7G.7G.7G.7G.7G.7G.7G.SY.SY.SY.SY.SY.SY.SY.SY.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#xl#C#.9O#cv##laXbaXcaXdaXeaXfaXg.98.O8.NU.NU.NU.NU.NE.ND.5C#bs#cn.O8aXh#e4#gb#ge#ge#ge#gb#g#alx.Up#.d#bw.8B.8B.8B.8B.8B.8B.9n.XF.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.VB.O..0Z.5B.8w.4q#.e#.e.0U#gzaXi#g##gb#E2#E2#gb#tkaXj#EB.2#.9n#xb#xb#xb#ul#xb#Qt.Oq.Pe.NV.Oq.NV.Pe#.9#ul.TC.Pe.Oq.Nt.NVaXkaXl.Pg.Ph.RC.RC.Pg.Uw.Pf#.9#mi.8e.ROaV2aSRaUuaUvaXmaXnaXoaXpaXqaXraXs#Xz#8Ia.baXtaXuaXvaXw.LW.8N.Pi.Ph.PiaXxaXyaXzaXA#PW.UN.UM.M0#e7#gb#gb#gd#e4.S4.UM.No.UM.UM.RY.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.SY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.0p.0p.0p.0p.0p.0p.0p.0p.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.Q0#CK.Z5.UO.Z5.Z5.UO.UO.UO.Z5.Z5.Lq#fU#gd#ge#uIatQaMOaSs##havCaT9aXBaTV#.t#ge#ge#ge#ge#ge#ge#ge#ge#4.aXC#z5#36aKAaXDaXEaXF.1L.1Q.2V.2V.2V#.z#rlaXGaC4#Fc#Fd#z5#z0#RM#if#vN#1JaXHaXIaDX#Cs#z5aO0aXJaXKaXL.K1#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Lq#e4#sM#ge#ge#ge#.tatQaXMaIz.2W.1Q.2V.2V.2V.2V.7I#.zao5.5o#oeaXNaIO#HK.Q0#f.#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#Jh#gd#UNanH",
-"ajtaXO#3haWuaXP#54#qq.RY.Q1.MM.J#.MM.J#.J#.J#.J#.OD.J#.VV.OK#gb.0H.K7.XA.K7.K7.K7.OF.W9.K7.86aLbaXQaXRaXSaXT.Rl.MV.MV.K7.SY.R6.R6.7G.7G.7G.7G.7G.7G.SY.7G.SY.SY.SY.SY.SY.SY.SY.SY.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.9LaHK#xlaXUaXVaXWaXXaXYaXZaX0aX1aX2aX3aXgaX4aX4aX5.98.O8.NU.NE.NU#mQ#w8.Ns#4p#ge#gd#ge#ge#ge#ge#gb#g##dYaX6#fw.5B.5B.8B.8B.8B##M.8B.8B#bt.NU.Oa.O8.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.Oa.Z1.8B.9n.0X#iF#.e#.e#yWaX7#uI#gd#E2#E2#wR.ONaX8#feaX9#j2#xb#xb#Uq#xb#Wj.WR.Pe.NV.Oq.NV.Ur.XF#DE#.g.Pe.NV.NV.NE.18aXl.Pz.RC.Pi.NM#n..8b.8b.Pm.Pi.2d#y7.MsaY.aY#aWKaXnaYaaYbaYcaYdaYeaYfaYga#EaYhaYhaYiaWcaYj#.O.Pi.Pi.Ph.RC.Pi.QB.3N.1p.QBaYk#xY.OK#gb#gb#gb.6q.Nf.UM.R6.UM.RY.RY#oa#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.UW.UW.UW.UW.UW.SY.SY.UW.UW.SY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.0p.0p.0p.0p.0p.0p.0p.0p.0p.UW.UW.UW.UW.UW.UW.UW.UW.UW#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.Z5.UO.UO.UO.UO.UO.UO.UO.UO.Z5aYl#w.#e4#gb#NcataaYmaHE#7O.5d.1LaJm#E2aTV#Nc#ge#ge#ge#ge#ge#ge#E2#0iaYnaYoaYpaYqaYraYs#5p#1N#cD.2V.1Q.2VaexaTYaTcaYt#z5#Fc#Fd#G##sp#lKaYuaYvaYwaYxaYy#CsaYzaYAaYBaYCaYD.Oz#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Q0#fU#gb#ge#ge#ge#uIataaYEaYF.5d.1Q.2V.2V.2V#.z#uOap3.9U.7T#adaYG#CLaQ8#e4#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#n8#gbalNanH",
-"aWuajtaYHaYHaWu#P8#j9.MW.Q0.Q1.P5aVMaVMaVMaQ8#CK.O4.4..J#.O4.MV#jL.6q.K7.Jt.K7.K7.K7.K7.W9.K7.MX#Ij#ewaYIaYJaYK.Nf.UZ.MV.T0.UW.R6.R6.SY.SY.SY.SY.SY.SY.SY.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#wOaYLaYMaLv#kG.7U.5o.5oaYNaYOaiOaYPaYQaYRaYSaYTaYU.OmaYVaYW.NU.NDaAi.8B.8a#j4aub#e7#ge#ge#ge#ge#ge#ge#gc#d2am4#EB.8B.5B.8B.8B.8B.8B.8B.8B.8x.2#.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND#xz.8B#mi##P#dU#mh#.e#.Y.8GadG#e6#wh#wh#wh.9QaYX.Op##Q#j2#xb#lN#xb#cn.PT.Pe.NV.Oq.NV.Ur.00#ul#fw.WR.Pe.Nt.NDaYYaYZaY0.Pl##H.Us.NE.Pd.Pd.NU.WG.3P.SAaY1aY2aY3aY4aaEaY5aY6aY7aY8#Cf#8IatIaYhaY9aZ.aZ#.LW.LX.8N.Pi.Pi.Ph.Ph#.m.Ps.RD.Tp.RD.Ut.TpaZa#e4#gd#e7.M0#oa.UN.UM#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.RY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.SY.SY.Q1.UW.SY.UW.UW.UW.UWaQ8.7G.SY.UW.0p.0p.0p.0p.0p.0p.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5#ta#gd#geatQaZbaZc#7OavC.5daZdaZeata#.t#ge#gb#gb#ge#ge#ge#E2.6E#wh#NcazsaZfaZgaZhaZiaZj#1N#cD.1L#ZraQF#z5#z5#z5#Fd#lJay1aZkaZl#ih#kMaZm.UI#3VaZnaZoaZp.7r#CcaZq.UM.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.Nf#fI#ge#ge#ge#uI#cvaZraZs.1O.1Q.2V.2V.2V#uO#vH##p.9..9UaZtaZuaSC#e7#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gbagR#6w",
-"apmaXPaXPaZvaZwaXP#P8#4S#hX.Q0.QpaZxaT5aZyaZzaZAaTxaZB.VVayU#pGaw.#At.4e.S4.W9.K7.K7.K7.W9.W9.K7.ON#n8#JiaJkaZC.XY.0f.MV.MV.0f.UO#HK#HK#wP.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HKaZDaZE.4i.5o.9U.6B.6B.6B.6B.6B.6B.6B#.GaiOaZFaZGaZHaZIaZJaZKaZL.98aZM.NE.8a#cn.KtaZN#e5#gb#ge#ge#ge#ge#ge#ge#e4#1baLc#DE.8B.5B.8B.8B.8B.8B.8B.8B#bw.7o.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE##V.9n.5B.8w.9p#.e#.e.0U#iGaZO#e5#wR#E2#E2#n8aZPaZQ#ar#rF#xb#xb#fw.TD.Pe.NV.Oq.NV.Ur.TD#fw#DE.QO.Pe.NV.ND.2raZRaY0.4S.Px.Pd.Pd.NU.NU.NU.ND.ND.8qaZSaZTaZUaZVaZWaZXaZYaZZa#ra.b#cQaYh#7naZ0aZ1aZ2aZ3.Pi.8N.Pi.Ph.RC.Pk.Po.Tp.Tp.Tp.Tp.TpaZ4.QC.TpaZ5.T0.RY.R6.UM.RY.RY.SY.SY.SY.SY#Dy.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.SY.SY.SY.SY.SY.SY#Dy.P2.0p.0p.6laZ6aZ7.Nl.Ha.UW.UW.UW.UW.UW.UW.UW.UW#CK#CK#CK.Q0.OW#kE.MG.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK#w.#ew#ge#HsaZ8aZ9#7OavC.5daT8aWWaucaTV#gb#gb#ge#ge#ge#ge#ge#sM#E2#E2#E2#jL#bDa0.a0#a0aaCs.1La0ba0ca0d#Fc#Fc#Fc#HHaYoa0eay3a0f#j.#QQa0gaXK#Dna0h#s0.56aGg#Nsa0i.IW#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Z5.6q#ge#ge#ge#ta#GEa0ja0k.1O.1Q.2V#.z#uOaQ9.7T.7X#lA#mna0l#ql#gd#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gd#geaoX",
-"#7K#jxawqaXPaXPaZwa0maXP#nSa0naZA.7IabO#mA.2VaP5aSsa0oa0pa0qa0ra0sa0t#wS.9G#jF.Lq.Ql.K7.K7.O3.W9.K7.ON#n8#e7#hY.OO.0f.0f.21.21.21.XY.UW#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK#HK#HK#HK#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.UMaZDa0u.7W.7T.9..6B.6B.9U.7T.7W#mn#tIahcahc#mn.7W#qxa0va0wa0xacJacUa0ya0za0A##O#cn##W#.ma0B#e6#gd#ge#ge#ge#ge#ge#ge#e6#pja0C#EB.8B.5B.8B.8B.8B.8B##M.8B.Uq.TD.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.ND.PS.8y.8B.9n.8u#.e#.e.0U#yWa0D#iu#ge#xm#xm#gd#hv#IS#j2#ul#xb#ul#ud.Pe.NV.Oq.NV.Pe.PT#br#DE#v5.LQ.NV.Oq.8q.L2.Ph.9e.NU.NU.NE.NU.NU.NU.NU.NEa0E.O#a0Fa0Ga0Ha0Ia0Ja0Ka0L#bb#cQaYh#VEa0Ma0N.PgaYjaYZa0O.Pi.Pi.Ph.RC.LS.QB.Tp.Tp.Tp.Tp.Tp.Pf.L1.Ph.NS.Pqa0P.Qd.1A.Qd.1A.1A.SX#Dy.UW.R6.1A.Qd.SY.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.UW.SY.SY.SY.4.#qq.XY.0f.4..SY.UW.SX.UYa0Qa0Ra0Sa0Ta0U.R6#CK#CK#CK.Q0#HK#HK.OW.41.2Ja0V.30a0Wa0Xa0Y.OW#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#HK.Q0.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5.4e#E2#.t#E2a0ZaI#avC.2W.5da00aWY#Jh#uI#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#E2.6Ea01a02a03a04a05aCv#z5#FcaCva06#gb.Q8.Q8a07a0e#lJ#TL#s5#1ma08#s0.56.7r.56aYBa09.I0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#CK.OK#gb#gb#t#.Q0a1.a1#a0k.1O.1Q#.z#uOakH.7W.9U##p.4iaGwa1a#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#2S",
-"aB6ay9anJ#d2aWuaXPa0ma0maXPa1ba1c.5d.2W.2U.2U.2U.1O.0E.1N.7J.1N.2Va1da1ea1fa1g.K7#ln.R0.K7.K7.K7.VV.W9.ON#n8#n8#hY.ON.0f.0f.21.21ayL.0f.UO.Q0#HK#HK#HK.Q0.Q0.Q0.Q0.Q0.Q0.Q0#HK.UM.R0#w..K7.Z5.Q0#HK#HK#HK#HK.Q0.Q0.Q0.Q0#wNa1ha1i.7UaL1.9U.7T.9UaPhaoD#uNaeRa1ja1ja1j#sS#sU.0E#o6#.z.1Lab#a1ka1la1ma1naZM#.b##P.Vw.3par6#ge#e7#ge#ge#ge#ge#ge#ge#e7#gea1o#lN.Pc.5B.8B.8B.8B.8B##M.8B#bu.TD.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND#ek.8B.8w#vn.Tk#mh#.e#hH.SKarH#e7#xm#xm#e7#sy#bk#.b#aF#xb#bt.NV.NV.Oq.NV.97##V#cn#xb#.f#c1.Pe.Oq.Pe.NL.3P.RB.Pd.NE.ND.NE.NU.NU.98#kpa1pa1qa1ra1sa1ta1ua1va0La#E#cQaYh#baa1wa1x.RC#k5.Pf.PkaV0a1y.Pi.Ph#pl.NJ.QD.Tp.Tp.Tp.Tp.Tp.NQ.Pi.RC.Ph.Ph.Pi.NJa1za1AaXAa1Ba1Ca1Da1Ea1Fa1Ga1Ha1I.UW.1A.SY.UW.UW.UW.0p.0p.0p.0p.0p.0p.SY.SY.SY.UW.21#.taDe#6w#6w#5jaBs.TZ#lxa1Ja1Ka1La1Ma1Na1Oa1P.I0#CK.Q0.41#kE.0f.Q6#Hra1Qa1Ra1Sa1Ta1U#xb#ula1V.QL#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5#fU#gb#.t#JhaW7#.z.2W.2V.5d.1La1WatQ#uI#ge#ge#ge#ge#gb#gb#gb#gb#gb#ge#ge#E2.Q8a1Xa1YaLB#z5#PNaCv#JEa1Z.Q8#ge#ge#wh#jLa10#z5#Zha11a12a13#FU#tT#tTa14a15.N3#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.Q0#ta#gd.Z5#HK.Q0a1.a16aQW.2U.1Q#.z#o6arkaga#a3.9.a17a18#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb",
-"anGanGaB6anG#T5aYHaXPaYHaYH#6Ja19a2..2W.2U.2U.2U.2U.2U.2U.2V.2U.2U.1O.2X.2XabHa2#a2aa2baHL.Lq.W9.Ql.W9.K7.7w#E2#n8#n8.0H.0f.0f.0f.21.0f.0f.21.0f.O4#HK#HK#HK.Q0.Q0.Q0.Q0#HK#HK#t##ta#ta#ta#ta.M0.Nf.Lq#Sv#HK#HK#HK#HK#HKa2ca2d#qwa2ea0vakHabda2f.1O#o6#.z#.z.1L.1L.1L#.z#.z.1La2ga1kab#a2ha2ia2ja2ka2l.NE.Oa.Tl##P.Ob.RFavR#ZF#e4#gb#ge#ge#ge#ge#ge#e7#WKa2m#w8##P.5B.8B.8B.8B.8B##N.8B#v5.RR.ND.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.Qy.Pc.5B.5B#eq#.e#.e#.e#jv#5T#e9#xm#gb#0Ya2n#Me.Ns#DE##Q.2c.Pe.Oq.NV.Ur#e0#br#xb#xba2o.Pw.Oq.Pw.4A.6T.NV.Pd.NE.Oq.ND.98#kpa1p#kpa2pa2qa2ra2sa2ta2ua#r#bb#cQa2v#cQa2wa2x.Pha2y.Pk##B.Oq.Poa2za1y.Pi.Ph.Pl.QE.Tp.Tp.Tp.Tp.Tp.Po.Ph.RC.Pi.NK.Pq.QC.RD.XG.1q#qg.1q.1q.1q.3N.3N.3N.1p.Tpa2Aa2B.SY.Qd.0p.0p.0p.UW.UW.Q0.SY.SY#CK#w.#aaanEanHanHanHanHanHaN5aVt#3la2Ca2Da2EaH3a2Fa2Ga2Ha2I.N3#kE#kEalK.Eea2J#m5a2K#ul#wBa2L#D5#U2#xb#DEadVa2Ma2N.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CKajs#fI#NcaTVaXMa2O.5d.1Q.2WaIzaS9at##ge#ge#gb.OK.Nf#w.#w.#w.#w.ajs#ta.4e#e7ax2a2P#Gb#Fc#QQaCv#2OaXC#wh#ge#ge#ge#E2#hXa10#lJ#JEa2Qa2Ra2Sa2Ta2Ua2V#CK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Nf.Z5.Q0#CK.Q0a1.a2Wa2O.2U.1Q#.z#uOas3a2Xa2Y.Lo#Jh#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#E2",
-"anG#5lanGaB6#1va2ZaWuaWu#P8a20a21a2..2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2V.2U.2W.1N.1Na1da22.K7#aU.O3.VV.W9.K7.ON#Vu#n8#n8.VX.21.0f.0f.21.M0.M0#ta.0H.7w.XY.Q0#HK#HK#HK.Q0.OW#CK.M0#ta#ta#ta#ta#ta#ta#ta.M0.Nf.UO#CK.Q0#B6.GWa23a24.1L#o6adp#o6#.z.1L.2U.2U.2U.2U.2U.2U.1LakFaP4a25a26a27a28aYWaYVa29.NU.O8.NE.ND#zA.Pc#.d#q3.K#acv#e6#gd#ge#ge#ge#ge#ge#gd#f.aL6#.c#w8.5B.8B.8B.8B.8B##M.8B#.c#c1.ND.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU#Ft#xz.8B.5B#.Z.9m.YL#.e.3Aa3.a3##e9#gb#WK#d0asS.98.TD#.Z.O..Pe.NV.NV.Pe.Tl#ul#xb#xb.Uq.Oq.NV.Ur.Pf#.N.NE.NU.ND.NV.Oqa1p#kpa3aa3ba3ca3da3ea3fa3ga3h#bb#VEa3i#b2a3ja3ka3la3ma3n.RF.6X.ND.ND#c#aXla3o.Pi.Ph#.O.Po.RD.Tp.Tp.Tpa3p.Ph.L4.Pv.QE.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Tp.1q.3N.Iba3q.30.Q0.UW.UW.Q0.Q0.OU#IjaTl#6wanHanHanHanHanHanHayjaoSa3ra3saaA#JQa3ta3ua3va3wa3x.3Z.4.a3ya3z.YHa2L#U2#Mm#th#C9#ul#xb#xb#ul#ul#xb#tq#bxa3AacE.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#CK#CK.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0#CK.Q0.Q0.Q0.Q0.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK#t##e6#NcaRSayn.7K#7OavC#7Oa3BaTV#gb#e4aoR.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Nf.Lna3C#TL#Fc#Fc#TLa3D.Q8#ge#ge#ge#ge#E2#jLa3Ea3Fa3G.LvaFoa3Ha3I.Oz.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.Q0.UO#Ij.UO#HKaIOa3JaTr.2U.1Q.2V.7Ia3Ka3L#uJ#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge",
-"anGanGanG#2V#1v#1va3Ma3Najtajta3Oaf7.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2V.2U.0Eaxva3Pa3Q#ln.K7.O3.K7.K7.ON#gb#gd#n8.X2#fU.M0.M0#ta#ta.M0.M0.OK.X2.X2.UP.K7.Q0#HK.OW#CK.M0#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#taajs#qlaXUa3RaG7.1L.2U.2U.2U.2U.2U.2U.2U.2U.2U.2Wa1kacUa3Sa3Ta3UaYWaYWa3V.NU.NE.NU.NU.NU.NU.NE.ND#.0.8A.5B.WG.Swa3W#ge#e7#ge#ge#ge#ge#ge#gb#D4#p5#AJ#lN.5B.5B.8B.8B.8B##N.8B.8p.Pd.Oa.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.NU.O##j2.8B.8B.8w#fe.0U#gB#gB#1U#jl#gb#gb#e5a3Xa3Y.Ok.RR.PL.Pe.NV.Pe.LO#bt#xb#xb#xb#iq.TD.Pe.NV.L7#fd.Pd.NU.NE.O#a3Z.LQa30a31aFsa2ra32a33a34a35a#E#VEa36a37a38a39#gka4.aOl.8b.Mi.ND.Oq.Oq.Oq.NTaOma4#.Pi.RC.LZ.QD.Tp.Tp.Tp.NP.3B.NP.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Tp.3N#bz.QD.3Ea3pa4a.30.Q0#HK.Rla4b#6wanHanHanHanHanHa4canH#6w#HV#j8a4d#Wf#U2a4e#gCa4fa4ga4ha4ia4ja4ka4l#D.ar9#DE#ul#ul#xb#xb#aF#xb#DE#D.##L#ul#xb#xba4ma4na4o#oc#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#HK#CK.Z5.Z5#CK#CK#CK#CK#CK.Q0#CK.Q0.Q0.Q0#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5.OK#gb#HsawnaJmaIza4pa4q#hY#w.aYl.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.4..R0#FX#Zh#Fda4ra4s.6E#E2#ge#ge#ge#ge#E2#wh#gb#sM#gd#ta.Sb.Oz.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.Q0.Q7aBs#gc.5aaIOa4taP5.1N.5eaWg.0ya4u#Hs#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge",
-"#2V#2V#2V#2V#2V#1v#7K#4Sa4vahFa4waI#.2U.2U.2U.2U.1L.1L.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2V.2U.2Xa4xa4y.UOa4z.UO#jFa4z.MXa4A#vD#jL.X2ajs.Nf#ta#ta#ta.M0.M0.21.4e.VX.VX.VX.7w.2Rajs.M0.M0#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#TGa4Ba4CaI#.2V.2U.2U.2U.2U.2U.2U.2U.2V.1O.2Xaria4Da4Ea4Fa4Ga4Ha4I.Qs.O8.NU.NU.NU.NU.NU.NU.NU.NE.ND.PT#bw#br#lO.Pfa4J#AI#WK#ge#ge#ge#ge#ge#gb#e5#fravI#xb.5B.5B.8B.8B.8B##M.8B.Z1.NE.NE.NU.NU.NU.NU.NU.NU.NU.NU.NU.NE.ND.4s.8B.8B#dh#.f#er#mh#.e#.Y#j5#AI#e6aada4K#ek.O#.Oq.Pe.NV.Pe.PS##Q#aF#xb#xb#Nh#p4.NV.NV.2r.Pw.Pd.NU#kpa4La4Ma4Na4Oa4Pa4Qa4Ra4Sa4T#6t#cQ#VEa36a4Ua4Va4W.2s.L6a2zaM3a4X.NE.Oq.Oq.Oq.Oq#zD.8qa4Ya4Z.Pi.RC.L6.Tp.Tp.Tp.Pu.LS.Ps.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Tp.VCa40a41arHa42.1q.Cp.UO.Q0#lhanGanHanHanHanHanH#6w#6w#5jakv#e4#d0#TuadV#Uq#ul#Uq#ula43a44a45a46a47.NH.3P#LP#.c#C9#th#DE#xb##L#C8#ul#P6.Mj.Mj#C9#xb#ula48a49a5.a5##oc#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#HK.Q0.Z5.UO.UO.UO.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5aoR#gd#NcaTVa5aa5b#aYa5caRR#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.O4a5da5e#qGa5fa5ga5h#hX#ge#ge#ge#ge#ge#E2#E2#ge#gb#f#azs#t##HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Lqazsazs.Rl#CLa5ia5ja5ka5la5maLq#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge",
-"#2V#2V#2V#2V#2V#1v#UO#ZFa3Na5na5o#qz.2U.2U.1L.1L.2U.1L#o6#.z.1L.2U.2U.2U.2U.2U.2U.2U.2U.2V.2V.0E.2Va5pa5qa5ra2ba5sa5ta3Bawn#zV#mp#fU#CK.Nf#ta#ta#ta#ta.M0.M0.M0.M0#ta.ON#e7#gb.OK.Nf#ta#ta#ta#ta#ta#ta#ta#ta#ta.39.MVa5uaWgavC.2U.2U.2U.2U.2U.2U.2V.2U.1NaTsa5va5wa5xa5y#XE#mT#UU.9y#Pa.2s.O8.NU.NU.NU.NU.NU.NU.NU.NE.ND.2#.8x.4r#ax#c2adY#km#e6#ge#ge#ge#ge#ge#ge#nD#lTa5z#EA.8B.5B.8B.8B.8B##M.8B.UF.ND.NE.NU.NE.NU.NU.NE.NU.NU.NE.O#.7o#.Z##M.8B.5B.9n.9q#.e#.e#.e#Ml#T5#L5a5A#dU.8a.Oq.NV.NV.Ur#ko#J8#C9#xb#xE#xb#.W.Oq.Pw#e#.NV.NUa1pa1pa5Ba5Ca5Da5E.7Ca5Fa5Ga5Ha5I#ba#7na5Ja5Ka5La5Ma5Na5O.6T#nHaM3a5P.2c.Oq.Oq.Oq.Oq.Oq.Oq#Lpa5QaUq.Pi.RC.Uv.Tp.Tp.RD.NP.Tn.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.VCa5R#ge#e6#WK.1Ba5S.1qa5T#HVanHanHanHanHanHanH#6waTm#2V#TC#f.#x.a5U#Wf#ul#xb#xb#xb#xba5Va5W.F1a5X.Pz.Ph.Ph#cc.Sw#.R#d9#MG#XQ#j6.52.Ps.L2.Pz.Pi.1f#th#xba5Ya5Za50atfa51#wN#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK#w.#e6#ge#.taTVaZ8a52#fJ.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Lq.O4a53#L0#Fba54a55.MH.Ne#ge#ge#ge#ge#ge#ge#ge#gb#gdakvaB6.Z##HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0aRAay9aGaaRQaVM.M0#uJaLq#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge",
-"#1v#2V#2V#2V#2V#2V#1v#2V#I4a56a5o#cD.2U.1L#.za1jao5agdaoDa57.1L#o6.1L.2U.2U.2U.2U.2U.2U.2U.2U.2V.2U.1N#mAaBSaUg.2V.2X#o7a58aBpasp#vE.UO#HK#t#.Rc#ta#ta#ta#ta#ta.R2.M0.M0.M0.OK#e5.M0.M0#ta#ta#ta#ta#ta#ta#ta#taaDSa59.2X.2W.2U.2U.2U.2U.2U.2U.2V.2W.5ea6.a6#aJ4atSa6aa6batk#v8aTz#xV#xVa6c.VB#nX.NU.NU.NU.NU.NU.NU.NU.NE.ND.Pd#.d#w8#.d#q3a6d#jx#e6#gb#ge#ge#ge#ge#ge#e4#e3a6e#lN#cn.5B.8B.8B.8B.8B.5B.1f.ND.NE.NE.NU.NU.NE.NU.NE.NE.Ns#.c.8B##M.8B.8B.9n.4J.0U.0U.0U##P#d4#4s#f7a6f#bt.NC.NV.Pe.TD#br#xb#ul#ul#aF##Q.NE.NV.51.NV#kp#kpa6ga6ha6ia6ja6ka6la6ma36#6t#baa6na3ia6oa6pa6qa6r.TC#q1.Pla6saXl.18.2c.NV.NV.Oq.Oq.Oq.Oq#.n.4Ba6t.WH.Ph#.O.Ps.Tp.Tp.QD.QD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.1qa42#SX#gd#ge#gb#gd#HZa6ua6vanHanHanHanHanHanH#6w#6w#6w#f##e4#w6#jl#D5#ul#ul#xb#ul#xb#tqaofa6wa6x.Pg.RC.RC.Ph.Ph.RC.Pz.RH.L1.NO.Pg.QF.WH.Ph.Ph.Pg.NS#Yb#ula48.Ona6ya6za6Aa6B#B6#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#CK.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5#fU#gb#ge#.t#uI#gb#ta#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.4..Q1a6C#WBa6Da6Ea6F.Hp.6q#gb#ge#ge#ge#ge#ge#jL#gbanG#6w#hu.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#w.#e8anGa6GanEaB6alN#ge#gb#gb#gb#E2#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge",
-"#1v#1v#2V#2V#2V#2V#2V#1ua6Hae6a6IapC.2U.1L#cDaEU.7T.9U.7T#r8ao5aqwaeQ#o6.7I#.z.1L.1L.2U.2U.2U.2U.2U.2V.2U.1O.1O.2U.2V.2W.5e.1La6JaJl#ex#ks.OW#pJ.M0#ta#ta#ta#ta#ta#ta#ta.M0.M0.M0#ta.M0#ta#ta#ta#ta#ta#ta#ta#taaDHa6KaWg.2V.2U.2U.2U.2U.2U.2V.5e.7Ia6L.UM.Ya.Q0.RY#PW.Ska6Ma6N.4O#yu#xV#xVa6O.98.Sq.NU.NU.NU.NU.NU.NU.NU.NU.ND.NU.Z1#cn.8p.Qz.8ca6P#e6#gb#ge#ge#ge#ge#ge#e8#gb#nC.5B#fw.5B.8B.8B.8B.8B.5B.00.ND.NE.NU.NU.NE.NE.ND#c1.9n.8B##N.8B.8B.8B.5B.5B##K#.e#aC#fea6Qa6R#.e##Q.Oq.Pe.Pe.Ns#Wi#xb#ul#xb#C9.0Z.Pd.Ol.Oa#kp#kpa6Sa6T.ORaV6#7Ba6Ua6V#baa0L#baa6na6na6Wa6Xa6Ya6ra6Z.LO##H.RC#eea60.9e.VB.NV.NV.Oq.Oq.Oq.Oq.NV.NDa61a6s.RC.LV.LZ.QD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.1qa62#xm#gd#ge#ge#gb#geaB6aB6#6wanHanHanHanHanHanH#6w#7K#ga#e6ae4#ISadV#ul#Uq#xb#xb#ul#xb#bj.LW.4z.Pi.Ph.RC.RC.RC.Ph.Ph.Ph.Pi.Ph.RC.Ph.Ph.Pi.Pi.Ph.RC.Pha63a64#fw#Wea65a66a67a68.3Z.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.SY#CKajs.22.Q7.UO.UO.TZ#w.#taaoQ#lh#hu#fU#fU#taaoR.M0.UO#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.4e#ge#ge#ge#ge#gb#fU.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.R0.Q1a69#F8a7.a7#a7a.Ly#w.#ew#gb#ge#ge#ge#gb#gdagRanHanH#Ij.Ty.9L#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Lq#e6#TC#6wanHanHanH#6w#1vakv#ge#gb#gb#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge",
-"a2Z#2V#1v#2V#2V#2V#2V#1v#1va7ba7c.2X.1L#tK.1Lahv.9U.7X.6B.6B.9U.9Uagaa7daeSaP9aqO.1L#o6#o6#.z#.z#.z#.z#.z#.z#.z#.z.1L.1L.2V.2W.2X.7Ia7ea7f#pG#B6#Du.UO.M0#ta#ta#ta#ta#ta#ta#ta.M0#ta#ta#ta#ta#ta#ta#ta#ta#taaoRaDSaU7#7O.2U.2U.2U.2U.2VavC.5ea7ga7ha7i.UO.RY.UM.RY.RY.3Za7ja7ka7l.4O#xW.XQ#oFa7ma7na7o.O8.NU.NU.NU.NU.NU.NU.NU.ND.NU#.W.8B#bw.Sz.LVa7p#e7#gb#ge#ge#ge#ge#ge#gd#nD#jm#OG#EB.8B.5B.8B##M.8B#dh#zA.ND.NE.NE.NU.NU.Pd#.W.8B.8B.8B.8B.8B.8B.5B.9n.Qr#.e#er.8B#iq#cm#tp.PS.Pe.Pe##O#ul#xb#xb#xb#ul.8a.Pd#kpa7qa7ra7sa7t.M5a7ua5Ea7va7wa7xa7y#VE#MLaa2a7za7A#Mi#to.Oi.NU.6X.3p.SA.Pl#.R.Pe.NE#zD.NV.Oq.Oq.Oq.Oq.Oq.NV.NDaV2a7B.L4.RC.LS.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QG.RD.1qa62#fU.M0#ge#ge#ge#gb#gbaDe#6wanH#6wanHanHanHanH#6wanH#UN#e6a6P.L0#C7#xb#ul#xb#xb#ul#C8#rQ.Pz.Pz.RC.Ph.RCa7C.Pz.Ph.RC.RC.RC.Ph.RC.RC.RC.Ph.Pi.Ph.Ph.RC.Pka7D.2Ja7E#m5#JQa7Fa7Ga7Ha7I.K1.Q0#CK#CK#CK#CK#CK.Q0.Q0.UW#CK.UO#aaazs#6watRanGalNanEay9anG#6wanHa7J#6w#6wa7K#T5a7La7M#5j#aa#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5#e4#ge#ge#ge#ge#gb#fU#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.R0.4.a7Na7Oa7Pa7Qa7R.Ly#HK#e5#gb#ge#ge#ge#gb#gb#2SaoXanHagR.Lq#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.UO#fIaDeanHanHanHanHanHanH#6waoXalN#2S#ge#gb#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge",
-"#ZF#2V#1v#2V#2V#2V#2V#2V#1va7Sa7T#6B#5pafQ.1Lahv.6B.6B.6B.6B.7X.6B.9UaL1.9U#r8ahca2eaQ9#o5#sTaeRa7Ua7Va7Wa7Wa7VaG5a7Xa7Y.1M.1N.0E.5ea7Z#cEa70a71#B6.OW.Q0#t#.M0#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#taaoRaw.a72a73.5d.2U.2U.2U.2U#aX.2XaUca74aDK#ta#ta.Lq.UN.RY.RY.RY.RY.Km.RYa75a76#AU#yu.9y#m0a77.NE.Sq.NU.NU.NU.NU.NU.NU.NU.Oa.NE#de##P.96#n.#fj#f##gd#ge#ge#ge#ge#ge#ge#gb#e5#dZa78.8v.8B.5B.8B##N.8B.5B.6R.O..ND.NU#.W.0Z.8B##M.8B.8B.8B.8B.8B.8B.5B.8B#er#gF#.Z.9p#ym.Ns.97.NV.NE#Hi#xb#xb#Wj.Ns##Ra79a8.a8#a8aa8ba8ca8da6k#8Wa8ea8fa8gaW##ML#ola8ha8ia8j.Ku.Oi.VB.NV.Vz.LZ.Pp.52.Mi.ND.Oq.NV.NV.NV.Oq.Oq.Oq.Oq.NV.Oq.8qaVcaWJ.Pu.RC.NL.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.1qa8k.Nf.Q0#e7#ge#ge#ge#gb#Nc#qR#5j#6wanHanHanHanH#6wa4c#1u#gdatsa8l.Ps#br#ul#xb#xb#xb#xb#ul#.Q.SA.RC.RC.Ph.RC.RC.RC.Ph.Ph.RC.Ph.RC.RC.Ph.RC.RC.Ph.Ph.RC.RC.RC.RCa8ma8naCma8oa8pa8qa8ra8s.N3.Q0#CK.Q0.Q0.Q0.9L.Q0.TZ#aa#KwaoXaqHa8ta8tatRa8tatRa6vatRatRanH#5j#1v#6wanHa8ua8va8w#P8#P8#LIauM#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5#e6#ge#ge#ge#ge#gbajs#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.R0.O4a8xaNWa8y#vRa8z.Jt.1B#ge#ge#gb#gb#E2#gb#gb#gbanE#7KaoT.21#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#w.#ClaqJanHanHanHanHanHanHanHanHa4c#2VaDeagR#ge#gb#gb#gb#ge#ge#ge#ge#ge#ge",
-"#1v#2V#2V#5k#5k#5k#5k#5k#5k#1v#A6a8Aa8Ba8Ca8D#ad.6B.9..6B.6B.6B.6B.6B.9..6B#nk#qwatL#cI#zf#kG#pQa8Ea8Fa8Ga8Ha8Ia8Ja8Ka8La8MaMl#oda8Na8Oa8Pa8Qa8R.Z5.OW#HK#HK#Sv#t#.M0#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#ta#TGa8Sa1d#7OavC.2U.2U.2U#aX.1Na8Ta8U#aP.O4.Lq#pJ.UM.UM.RY.RY.RY.RY.RY.UNauAa8Va8W#z..WM#xV.9y#y9#yq.Rv.O8.NU.NU.NU.NU.NU.NU.ND.Pd#gj.6R#gu.Pf#IQ#SX#ge#ge#ge#ge#ge#ge#ge#ge#e4#2v#w9.8B.4r.5B.8B##M.8B.8B.Uq#ar.8a.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.Vy#jh#gw.5E.YU.Pe.NV.Pe.RR#DE#aF#.Za8Xa8Ya8Za80a81a82a83#Yfa84a84a85a86aW#a5Ia3i#yKa87a88.NEa5N.ND.ND.Oq.NT.QC.QC.RB.O8.ND.Oq.NV.Pe.ND.TD.PS.NV.Oq.Oq.Oq.NV.NEa89aS#a9..Ut.NS.NR.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD#hFa9##w..OW#e6#ge#ge#ge#gb#gb#ga#2UanH#6wanHanHanH#6w#6w#2U#gb#e6a9aaY1.Sw#bs#xb#xb#xb#ul#D..4y.8f.RC.Ph.RC.RC.Ph.Ph.Ph.RC.RC.Ph.Ph.RC.Ph.Ph.RC.RC.RC.RC.RC.RC.RC.RC.Pk.Pka8ma9b#75a9caFo#2w.SY.Q0.Q0.Q0#CK.OU#gcazsaB6anHanHanHatRatRatRatRanH#6waoX#q.akv#f##ge#Uy#TC#2SalNaoXatRa9da9eaYHa9f.6q#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UOaYl.UO#e7#ge#ge#ge#ge#ew.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.R0.Q1a9ga9ha9ia9ja9k#Cl.TP#gb#ge#ge#ge#gb#gb#gb#n8#UzaoX#ZF##k.OW.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#taaDeanHanHanHanHanHanHanHanHanHanHanHanH#5j#UN#TC#gb#gb#gb#ge#ge#ge#ge",
-"#5k#5k#5k#2V#2V#2V#2V#5j#5j#2V#2UanKa9la9ma9n.6x.7U.7S.6B.7X.7X.6B.6B.7T.5oaVRa9oa9pa9qa9ra9sa9t#CM.Jua9u.MN.R1#qqa9vat5at5aQ7#C##C#a9wa9xa9x.43#e4#CK.UN.UM.UM.UM.RY#pJ.K7.M0#ta#ta#ta#ta#ta#ta#ta#taaoRaDSa9y.1PavC.2U.2U.2U#aX.0E.0v.Zh.Ya.UM.UM.UM.UM.UM.RY.RY.RY.RY.RY.RY.RY.RY.Km.Sba9za9Aae1amP.9ya9Ba77.Qs#nX.NU.NU.NU.NU.NU.NE.ND.53.9n.52.4S#LnalM#gb#ge#ge#gb#TV#gb#gb#ge#ge#gd#f.a9C#2h#lN.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n#br#fw#aB#wF.NV.Pe.Oq.Pe.WRa9Da9Ea9Fa9Ga9Ha9Ia9Ja9Ka#Na9La6ka9Ma9N#b2a9Oa6naa2a9Pa9Q.LOa9R.LQ.NV.NV.NV.Qz.4R.Pw.O8.LQ.NV.NV.Pe.Oq.Vw#.c#ar.NV.NV.Oq.Oq.NV.LQ#fca6saPtaXl.Pf.Pu.Pu.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.XGa8k.Nf#B6.OK#ge#ge#ge#gb#gb#1u#1u#6w#6wanHanHanH#6w#6w#5j#ge#e7aBua9S.PzaRf#br#ul#xb#DE#tha9T.3B.Pz.RC.Ph.Ph.RC.RC.RC.RC.RC.RC.Ph.RC.Ph.Ph.RC.RC.RC.Ph.Ph.RC.RC.RC.RC.RC.Pz.3N#y7a9Ua9V#PW.3Z#CK.Rl.P8#IjayjanGanHanHanHanHanHanHanHanH#6w#UO#Cl#Uz#gb#gb#gb#gb#sM#gb#gb#gb#gb#ge#2S#UOanHatRa9WaoX.M0#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.Nf#gd#ge#ge#ge#gb.M0#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Q1a9X#RMa9Ya9Za90a91aigaoX#6waoX#TC#2S#UOaDealNanEanHarL#EU#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#CK#CK.Q0.Q0.Q0#CK#CK#CK#CK#CK#CK.Q0.Q0.4e#2VanHanHanHanHanHanHanHanHanHanHanHanHanH#6waDe#TC#gb#gb#ge#ge#ge",
-"#2V#2V#2V#2VanJa92#uI#At#Cl#5j#5jaoX#6xa93a94aBra95a96.7U.7V.5o.7SaI7aroa97a98a99#n1aHLa4z#n1at5#w.#w.#w..XY.XY.XY.XY#qq#qq.0f.Xz.Xz#qq.Xz.XY.XYaXb.21.O4.UM.UM.RY.UM.UM.UM.RY.R0.Nf.M0#ta#ta.R2.R2.R2.39.M0b..#7O.2U.2U.2U.2V.1Ob.#b.a.Zc.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.Nm.Lu#wS#G5#oCae1#xV.8Ra6Ob.b.Rv.O8.NU.NU.NU.NU.Qs.NU.0Z.Ns#Gjb.cb.d#e4#gb#gb#e3arO#1R#ge#gb#gb#ge#gb#e5#P8#Mc#EB.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#DKaku#Nh.NV#zD.NV#Rjb.eb.fb.gb.hb.ib.jb.k#8VaFsb.lb.mb.nb.oa8g#MLa3ib.pb.q.Ntb.r.Oq.LQ.Oq.NV.Oq.NV.Oq.VB.Oq.NV.NV.NV.Om.Tl#gj#ul#.f.WR.Pe.NV.Oq.Oq.NV.Oqb.saM3b.taM2#os.Tp.QD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.3N.Ii.OK.30.M0#gb#ge#ge#ge#gd#f##5j#gaanH#6wanHanHanH#6w#7K#ga#gd#e6b.u.Pl.Pz.QA#mi#D.#.f#2h.4y#.m.Pz.RC.RC.RC.RC.RC.Ph.RC.RC.RC.Ph.RC.RC.Ph.Ph.Ph.RC.RC.RC.RC.RC.Ph.Ph.RC.Pz.Tr#v5#thadV#.Vb.v#9IazsanGanHanHanHanHanHanHanHanHanHanHanHaoX#TC#gb#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#jL#gbaoX#6w#6wanHaBs.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.OK#ge#ge#ge#ge#fU.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Q1b.w#Yv#vNb.xb.yb.zb.A#pjanJ#5j#6wanHanHanH#6wanGatR#6VaHP#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#CK#CK#CK.Q0.Q0#CK#CK#CK#CK#CK#CK.Q0#HK#CK#iuanGanHanHanHanHanHanHanHanHanHanHanHanHanHanH#6walN#Nc#gb#gb#ge",
-"#5j#5j#5janJ#2U.UP.4..O4.4.b.BanE#5j#5jakv#jL#e8b.CaLZb.Db.Eb.Fb.Gb.Hb.Ib.Jaw..XA.K7.1J#qq.Xz.1J#qq.K7.R1#qq#qq.Xz.R1#qq.1J.Xz.Xz.Xz.R1.Xz.Xz#qq.XY.1J.XY.O4.UM.P2.RY.RY.RY.UM.UM.UM.O4.K7.0f.Rc#ta#ta.39a8S.2TaTp.2U.2U#aX.1O.0Db.KaJ4.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.RY.O4.R0#w..O4.N3b.Lb.M.WM#z.#oF#Lhb.N.2s.O8.NU.NU.NU.NE.NE.8a#cn.Mx.Psa5XarL#f.#ga#Qm#2t#H8adG#vc#gd#gb#ge#ge#f.b.d#q4#EB.8B.5B.5B.5B.5B.5B.5B.5B.5B.5B.8B.8B.8B.8B.5B.8B.8B.8B.VD#ym##Q.NDb.O.Olb.Pb.Qb.Rb.Sb.Ta8c#8V.Nib.lb.Ub.Vb.Wb.X#MLa36b.Yb.Z#ZGb.0#q0#c#.Us.NV.NV.Oq.Oq.Oq.Ol.NV.NV.Pe.Oq.XF#J8#ul#DE#Wj#lO.Pe.NV.Oq.Oq.NV.NEb.1aM2b.2.2rb.3b.4.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Utb.5#jM.30.UO#gb#ge#ge#ge#gb#gb#2UagR#UNanH#6wanHanH#6wanH#2v#n8#gd#geard.Pi.RC.SA.3M#yXb.6.RC.8f.WH.LV.RC.RC.RC.RC.RC.RC.RC.RC.RC.Ph.RC.RC.RC.RC.RC.Ph.RC.RC.RC.RC.RC.Ph.RC.Pz.Pl.8a#DE#ul#feb.7.3MaTmanGanHanHanHanHanHanHanHanHanHanHaoX#Nc#n8#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#sM#jL#geanGanHanJ#e8.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO#CKajs#Uy#ge#ge#ge#fU#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.VUb.8b.9#s6b#.b##b#ab#b#2U#7KanHanHanHanHanH#6wanGa7M#nSb#c#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#t##gbaDea4canHanHanHanHanHanHanHanHanHanHanHanHanHanHanG#f##jL#gb",
-"#5j#5j#5jaoX.4e.R0.4..R0.R0b#d.UO#G4anJ#f##e7#gd#gb#iuaLbaLbaLbaLbazb#iu#gb#fI.Nc.5i.UO.K7#t#.K7.1J.XA#qq#qq#qq.Xz#qq#qq.1J.Xz#qq#w.#qq.R1.XY#qq.XY.K7.XY.1J#pJ.RY.RY.RY.RY.RY.RY.UM.UM.UM#oa.O4.W9.XYa9xb#e.1LavC.2U.2V.1O.2Tb#f.Ya.UM.RY.RY.RY.RY.RY.RY.RY.RY.RY#oa#oa#oa.Nm.O4.R0#w.#w..1J#w.#t#.HKaoRb#gb#hb#i#nL#rYb#jb.b.Rv.O8.NU.NE.ND#.0#J8.9naSO.18a3naRd.0g#4m#H8#H8#H8#Qm#1b#gb#gb#ge#ge#e6#GDb#k#ul.5B.9n.9n.5B.5B.5B.5B.5B.5B.8B.8B.8B.5B.8B#iq#2h#64.TA#xF.8ab#lb#mb#nb#ob#pb#q.M5#8V.V1b#rabrb#sb#ta8ga#ra3ib#ub#vb#wb#x.Pf.6T.RG.Us.NV.NV.Oq.Oq.Oq.NV.Oq.NV.NV.7o.8p#w8#xb#aF#.g.ND.Pe.Oq.Oq.Oq.NV.Pe#btb#yb#z.Oq.O8.NFb#A.Sw.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.1p#3j#i0#CK#ew#ge#ge#ge#Uz#gdakv#pj#gb#1vanHanHanH#6w#6w#UN#gb#gb#e7b#B.SA.Ph.RC.RC.QF.SA.Pz.RC.Ph.RC.RC.RC.RC.Ph.Ph.RC.RC.Ph.RC.RC.RC.RC.RC.RC.RC.RC.Ph.Ph.Ph.Ph.Ph.Ph.RC.RC.RC.4zam7#fw#xb#Uqb#Cb#Db#EaVtanHanHanH#6wanHanHanHanHanH#6w#Cl#gd#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#ge#1vanH#HV#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.4e#gb#ge#e7#t#.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO#CKb#Fb#Gb#H#yQb#Ib#J#3Wb#Kb#L#2XanHanHanHanHanH#6wanGb#M#2t#yY#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#fU#geakv#6w#6w#6wanHanHanHanHanHanHanHanHanHanHanHanH#6w#2T#gb",
-"#5j#5j#5j#At.O4.4..W9.P3.P3.R0.Q1.5iagR#ga#e7#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#jM.0H.UP.Nf.UO#qq#qq.Xz#qq.Xz.R1.Xz.XY.R1.XY.R1#qqajs#qq.Xz#qq.0f#ta.0f.XY#qq#qq.Z5.RY.Nm.RY.RY.RY.RY.RY.RY.UM.UM.UM.UM.Zcb#N.5eavC.2V.2U.1Lb#OaJ4.UM.RY.RY.RY#oa#oa#oa#oa#oa#oa#oa#oa.RY.RY.P2#Ir#w.#w.#w.#w.#w.#w..XY.R0.P1afub#Pb#Q#ATb#R#Lhb#S.Qs.Rv#nX.NE.ND#.b#cn.9n.Mx.Ps.Pob#T#tkahG#H8#3h#H8#H8#U5#4U#gd#gb#ge#e7#gbb#U#EB.5B.5B.9n.9n.5B.5B.5B.8B.8B.8B.5B.8B#EA#j7#yj#qc#m7#Bfb#Vb#Wb#Xb#Yb#Z.37#8V.M4b#0b#1b#2b#3b#4#6ta36b#5b#6b#7b#8.L1.Pn.4R.LO.NE.Oq.NV.NV.NV.Oq.Oq.NV.Pe.ND#.b#cn#ul#xb#fw#ax.NV.NV.Oq.Oq.Oq.NV.Ur.5C.Tlb#9.NV.Oq.NV.VBba.aZR.Sx.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Tpba#.41#f.#ge#ge#ge#ge#gb#gb#5j#f##gbaoX#6wanH#6w#6w#2U#gb#gb#ge#e4aPn.Pk.RC.RC.RC.Ph.Ph.Ph.RC.RC.RC.RC.Ph.Ph.Ph.Ph.RC.RC.Ph.RC.RC.RC.RC.RC.Ph.Ph.Ph.Ph.Ph.Ph.Ph.RC.RC.RC.RC.Ph.RC.Uw#aI#tqbaababbacbadae6bae#6wanHanH#6w#6wanHanH#6w#UN#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#gbalNanGaoR#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.M0#ta.UO.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.OBbaf#FWbagbahbaibajbakbal#pj#7KanHanHanHanHanH#6w#6wa6P#P8#yY#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#xl#CLbam#uJ#NcaoXaD5banaN5anHanHanHanHanHanHanHanHanHanHanHanHaDe",
-"#2U#2UaoTbao.O4.P3.Jt#bE.P3.Jt.P3#CK.1J#gd#gd#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gd.2R.UO.XY.XY.R1.XY.Xz.T0.1J#qqajs.Xz.Xz.Nf#qq.0f#qqajsaoR.0H.7wayL.XY.XY.K7.O4.RY.RY.RY.RY.RY.RY.RY.RY.RY.SY#wNbap#qz#aX#.A.2Xbaq.Zc.UM#oa#oa#oa#oa#oa.RY.RY.RY.RY.RY.RY.RY.RY.UW.0p.UW.XYajs#w.#w.#t##w.#w.#w.#w..Nn.P1barbasbatbau#v8.4P.9zbava0A.2s.Pd#.c#jC.8B.Mw.VC.Pfbaw#4T#LH#H8#3h#H8#H8ahG#4T#gb#gb#ge#e7#tkasE#DE#w8#cn.Pc.8B.5B.8B.8B.5B.8B.4r#EBasE#Uy#7..63#aCbaxbaybazbaAbaB#8V.M4a#N.R.baCbaDbaEa35#MLbaFbaGbaHbaI#.O.3C.9e.ND.VB.Oq.NV.Oq.NV.Oq.Oq.NV.NV.NC.53#hM#fw#ul#C9.5B.PT.Pe.NV.Oq.Oq.Oq.Oq.Pe.Oi#brbaJ.NT.Oq.NV.Oq.Oq#LpaZR.LZ.Ps.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.UtbaK.Q6.UO#gb#ge#ge#Uz#gb#gd#UN#1u#gdakv#6w#6w#6waN5anH#2v#gd#ge#gb#e6baw.Pk.Ph.Ph.Pz.Ph.RC.RC.RC.RC.Ph.Ph.Ph.Ph.Ph.RC.RC.RC.Ph.Ph.Ph.Ph.Ph.Ph.Pi.Pi.Pi.Ph.Ph.RC.RC.RC.RC.Ph.Ph.RC.Ph.Pz.Pk#bvbaLbaMbaNbaObaPbaQanGa4canH#6wanH#6waB6#Cl#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#f#aBs#w.#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.O4baRbaS#Cw#b1#8y#Az#ibbaTbaU#2WanHanHanHanHanHaTmanGatRapm#P8baV#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.5a#CLa2c#oc#ni#wN.JtbaWbaXbaYbaZba0ba1ba2ba3ba4banaD5#6wanHanHanHanHanHanHanHanHanH",
-"#2Uba5#HZ.SQ.4..4..4..Z5.R0.Lq.W9.Z5.R0#e6#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb.UP.XY.XY.0f.Xz.Xz.0f.0fajsajs.0f.0f.0f.0f#qq#w.ajs#qq.0f.6q#E2.VX.Q6.0f.XY.R0.RY.UM.RY.RY.RY.RY#oa#oa#oa#HKba6.0v.1O.1Nba7.P2#HK.RY.RY.P2#CK#CK.P2.UW.UW.UW.UW.UW.UW.UW.UW.UW.0p.UW.Jt#t##w.#w.#w.#w.#w.#w.#w..XY#w..O4.Qlba8ba9bb..2w.XN.8Rbb##nY#efbbabbb#aH#Fqbbc.Ks.RFbbdasQadH#H8#H8#3h#H8#H8agH#GD#gd#ge#e6#1bbbe#jk.3maua.PR.9n.8B#EB#DE#cnbbf#mO#Uy#Ykbbgbbh.YGbbibbjbbkbbl.M4#7A#8Vbbmbbnbbo#90#cP#cRbbpbbqbbr#gk.4Aayw.VB.Oi.NV.Om.LQ.NV.Oq.NV.Oq.NV.Pe.NU#qT#w8#iq#xb#xbbbs.NE.Pe.NV.Oq.Oq.Oq.Oq.NV.NV#bw.UF#VV.2c.NV.Oq.Oq.NV.2cb#z.Pl.LS.RD.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Pf.RD.VC.Id.Z5#e4#ge#gb#gb#ge#gb#ge#7K#ga#xm#7Ka4canHanH#6wanJ#UN#jL#Uz#gd#gaarc.Pi.Ph.Ph.RC.Ph.Ph.RC.RC.Ph.Ph.Ph.Ph.Ph.RC.RC.RC.Ph.Pi.Pi.Pi.Pi.Ph.Ph.Ph.Ph.Ph.RC.RC.RC.Pi.Pk.Ph.RC.Ph.Ph.Ph.RC.Uw.Mx#tpbbtbbubbvbbwbbxabE#1w#2XanJ#2VagR#gb#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#gb#ge#gd#w.#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.JBbbybbz#Zg#pYbbAbbB#6w#7KanHanHanHanHanHanH#6wanGbbC#P8aWu#Hr.OW.Q0#CK#CK.Q0#CK#CK#CK#CK#CK.Q0.Q0#CK.Q0#wN#oc.LpbbDbbEbbFbbGbbHbbIarz#ev.W3#eF#CP#ev#evba1bbJbbKbbL#6waGq#6wanHanHanHanHanHanHanH",
-"bbMbbNbbO.6q.O4.Z5.Lq.Lq.R0#jF.R0.TZ.R0#e4#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb.7w#qq.XYajsajsajs.0fajs.0f#qq.XYajs.0f#qq#qq.Xzajs#qq.XY.43#GD#ga#E2.0o.Q6.XY.4..RY.UM.UM#oa.RY.RY.RY.SYaFgbbPbbQbbRaR0.Y#.UW.UW.0p.UW.K7.XY.Q2.UW.SY.0p.UW.UW.UW.UW.UW.0p.SY#Ir#w.#w.#t##t##w.#w.#w.#w.#t#.K7#w..4e#f##sM.P9ba8bbSbbTabR.XNbbUbbVbbW#XDbbXbbY#CC.Pc#j6.1q#Pcajt#U5#Hb#H8#H8#H8#H8#1##1c#gd#gb#e7#gdaad#htb.d#dZbbZbb0#Fmbb1#it#Fs#e9#e7bb2bb3bb4bb5bb6a#N.V1.M4#8Vbb7bb8bb9bc.bc#bcabcbbcc.NIaWd#gu.TC#.W#ar#.Z#J8aKc.5C.NV.NV.NV.Oq.NV.Pe.RR#J8#DE#xb#xb#iq.Tl.NV.NC.Oq.Oq.Oq.Oq.Oq.NV.Ur.1f#ul.Pe.Pw.Oq.Oq.Oq.Oq.Oq.NDa61#jn.NS.Sw.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.LS.Ps.Ut#k5aoR#e6#ge#ge#sM#sM#sM#4UaqH#ga#2XbcdatRatRaqHanHanH#2v#jL#sM.9QaBu.J4.Ph.Ph.Ph.Ph.Ph.Ph.Ph.Ph.Ph.Ph.Ph.Ph.RC.RC.RC.Ph.Ph.Ph.Ph.Ph.Ph.RC.RC.RC.RC.RC.RC.Ph.Pk#Oibce.LX#.O.Ph.RC.Ph.Ph.Pz.Ts#2hbcfbcgbchbcibcjbckbclbcm#UM#wh#n8#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gd#ew.4e.OK#fU#fU.OK#e4#ew#gd#gb#ge#ge#ge#ge#gb#ge#gd.M0aYl.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.5i.Z5.P2.R0bcnbcobcpbcq#2W#7KanHanHanHanHanHanHanHanG#7Kbcr#P8#nSbcs#xl.Q0#CK.Q0#CK#CK#CK#CK.Q0#wN#wO#ni#ocatc#A1awNbct.9I#ev#h8#h8#eFbcu.9I.35.9I.9I.35.9I#CP#GH#GHbcvbcwbcxbcyaN5anHanHanHanHanHanH",
-"aY9bczbcAbcB.P5.Lq.4..Z5.Lq.Lq.Z5.UO.Z5#fU#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#ge.VXayL.XY#w.#w..0fajs.0f#qq.0f#qq.XzayL.XzayL.0f.Xz#qq#qq.4e#2v#qR#2v#4U#sM##k.2R.1J.UW.UW.SY.SY#Dy#Dy.0pbcCbcDbcEbcF.UW.SY#CK.Q2#t#ajsajs.39#qq.OF.Q2.UW.SY.0p.UW.UW.SY.4..XAajsajs#w.#w.#t##w.#w.#w..K7#w.#hu#ga#2v#2v#2v#Ee.42atsbcGbcHabR.2w#wI.XQ#xV#fgaTzbcIbcJ#3A#rV#.mbcK#lT#Z.#Hb#H8#3h#H8#Hb#Zx#gb#gb#gb#ge#E2#gb#TV#e6#e6#e9#f.#e6#gb#f.bcL#fxbcMbcNbcOajJ.V2a#N.37bcPbcQbcRbcSbcTbcUbcVbcWbcX#dV.Op#ul#fe#DE#DE#xb#DE.9r.Pe.NV.Oq.Oq.NV.NV.Qy#iq#xb#xb#C9#J8.7o.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.Pe.PS#C7.UF.Pw.Oq.Nt.Oq.Oq.Oq.Oq.Oq.3ra9..Ps.3B.3D.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.QC.3B.Pp.Uta3pbaV#e7#xm#xm#xm#f##xm#pjbcYaoSbcZa9WatRa9Wa9WatRa9W#pj#ge#xm#jL#DCa9S.Ph.RC.RC.Ph.Ph.Ph.Ph.Ph.Ph.Ph.RC.Ph.RC.Ph.RC.RC.RC.RC.RC.RC.RC.RC.Ph.Ph.Ph#pl.LW.LYbc0bc1bc2bc3bc4bc5.Ph#pl.Ph.Ph.RH.Pi#XK#R8bc6bc7bc8bc9bd.bd#bdabdb.7v#wh#ge#ge#ge#ge#ge#ge#ge#gb.6qaoR.UO.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.UO.M0.4e#gd#ge#ge#ge#ge#ge#gb.4e.UO#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#w.aDe#2Y#2Y#2XanHanHanHanHanHanHanHanHanH#6wanGbdcaWu#P8#dZajs#HK#CK#CK#CK#CK#CK.Q0.5abbGbddbdebdfbdg#h8#CP.7z.9I.9I#DT.9I.9I.9I.9I.9I.9I.9I.9I.9IamK.9I.35.9I#dk#dJbdhaD5aN5anHanHanHanHanH",
-"bdi#Az#Jtbdjbdk#CK#bE.Lq.Z5.Lq.Lq.UO.Z5aoR#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb.9Q.2R.XY.XY#qqajs.Xz.Xz.0f.XzayL.XzayLayL.0f.Xz.0f.0f.OK#4U#vc#OS#qR#qR#Zw#MU#xm#jL.4e.X1.K7.4.aQ8bdla3JaU2bdm.UW.4..OF.XAajsajs.0f.0fayLaoRajs#qq.Xz.OF.Q2.4..4..Q2#t#.XYajs.XY#w.#w..XY.XY.Lq.K7.0f#ew#ga#2v#9I#3l#2v#2v#2v#Ee.P9#qRbdnbdobdp#AU#oD#v8#oF#XD.Kybdq#bwbdr.RF.LWbdsbdt#Hb#H8#H8#H8#H8#2u#ge#gb#ge#GD#f##wR#wR#wR#f##f##FN#iubdubdvbdwbdxbdy.M4.M4#8VbdzabzbdAbdBbdC#8JbdDbdEbdFbdG##Q#C8#fe#xb#xb#xb#ul#C9#bv.Pe.NV.Oq.Oq.Oj.Oq.8a#xb#xb#xb#xb.8a.VB.Pe.NV.Oq.Oq.Oq.Oq.Oq.NV.NV.Pe#.d.5B.Oq.NV.Oq.Oq.Oq.Oq.Oq.Oq.NV.PebdHbdI.3D.NH.QE.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Pf.XI.NP.Tp.VCacv#e7#f##ga#ga#ga#ga#HZbdJbdKbdLbdLbdLbdKbdKbdKbdKa8t#ga#Ee#f#.9Q#9q.Pl.Ph.RC.RC.RC.Ph.Ph.Ph.RC.Pi.Pl.PibdM.Ph.Ph.Pz.Ph.Ph.Ph.Ph.Ph.Ph.Ph.Ph.3BbdNbdObdPbdQ#SSaUKbdRbdQbdS.RC.Ph.RC.Ph.15.L0a3m.UwbdTbdUbdVbdWbdXbdYbdZbd0bd1#0h.Q8#E2#ge#ge#ge#ge#e4#w..Z5.Z5.Z5.Z5.UO.UO.UO.UO.UO#jF.Z5.Z5.Z5.Z5.UO#ta#ew#gb#ge#ge#ge#ge.9Q#g#.Z5aYl.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#w.aB6#7K#7KanHanHanHanHanHanHanHanHanHanHanHanG#6wbd2aWu#d4aYX.Q0.Q0#CK#CK#CK#CK.Q0#xlavAaFI#pL#h8.7z.35.9I.35.35.35.35.35.9I.9I.9I.9I.9I.35.35.6f.X5.X5#aR#kA.35.7z#.vbd3ban#6wanHanHanHanH",
-"#yO#yO#g6bd4.7w.P3.Z5.Lq.Lq.UO.Lq.Lq.Lq.XY#gd#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#gb#Jh#Jh#Jh#Jh#gb#gb.VXaoRbd5.XY#qqayL.0f.0fayL.Xz.0f.XzaoR.0faoR.0H#t5#Zw#OS#qR#OS#Zw#Zw#VM#qR#OS#Zw#hv#qV#gb.X2.6q.6c.Q6.0fayLaoRbd5bd5#qq#qq#qqbd5#qqbd6ajsajsajsajsajsajsajsajs.0fajs.XY.0f.S4ajs#t#.K7#t##fU#jL#3l#2v#2v#tk#tk#4U#2v#9I#tk#2v#Ee.TO#wRbd7bd8bd9.1uaqT#z..YQae1be.be##H1.H8beaajt#V1#H8#H8#3h#H8#H8ah3#f##gd#ge#f##FN#wR#wR#f##f#aTVbebbecbedbee.V1.SUa#N.X7befbegbehbeibejbekbelbembenbeo#C8#DE#xb#ul#xb#xb#ul#xb##Q.Oq.NV.Oq.Oq.NC.Oq.8p#DE#xb#xb#ul.UF.NV.NC.Oq.Oq.Oq.Oq.Oq.Oq.NV.Oq.Pe##V#ul#e0.Ur.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oqbepb.t.Ut.NL.Pl.Tn.Tp.Tp.Tp.Tp.Tp.RD.Po.RC.NL.Tp.1qbds#gb#tk#2v#hv#qR#qR#R6beqbeqberberberberbeqbesbdJbdJ#1c#2v#2v#f#am5#Pd.Pk.Ph.RC.RC.RC.RC.Ph.Pl#6Gaw3#XM#ht#UZ.Ph.Pk.Ph.RC.Ph.Pj.Pi.LY.LY.Pzbetbeubevbewbex#s5#Ga#z5aZkbeybez#.O#gk.Pz.QC.96.Ps.3o.PnbeAbeBbeC#CXbeDbeEbeFbeGbeHbeIax2#E2#ge#gb.OK.Z5.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5#ta#e4#D4.OK#gd#Uy#f#azs.P8.Z5aYl.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5beJanHanHanHanHanHanHanHanHanHanHanHanHanH#6wanGb#BaWu#2taWuauM#HK.Q0#CK#CK#CK#CK.Q0#oc.JvbeK##4#h8#ev.46#kA.35#bWbeLbeM.6f#kA.35#kA#aR.35beMbeNbeObePbeQbeR.35.35.7z.7zbeSban#6wanHanHanH",
-"#ibabg#qHbeT.6q.R0.Lq.Z5.Lq.Lq.Lq.Lq.Lq.Lq.4e#gb#gb#gb#gb#gb#gb#gb#Jh#Jh#Jh#Jh#Jh#Jh#Jh#Jh#Jh#gb#gb#gb#gb#gb#E2#E2.VXaoR.XY#qqbeU.0f.0f.0f.0f.0f.0f.39bd5.0f.UP#sM#OS#Zw#qR#Zw#Zw#Zw#Zw#hv#VMaad#hv#Z4#hv#qR#Z4#tk#wR#sM#sM#jM.VX.OK.OKbaoaXbbd5bd5#qqajsajsajsajs#w.ajsajsajsajsajs#t##t#.XY.0f#ta#e6#ge#2v#2v#9I#9I#2v#2v#2v#tk#ga#2v#9I#ga#2v#3l#uI.P9#ETbeVbeWbeXbeYbeYbeZbe0#d0ats#gd#gd#f#arO#Hb#H8#H8#H8#H8adG#f##gd#E2#ge#f##f##Ncamtbe1be2be3.Ni.M4.M4ajI.V0be4be5be6be7#bbbe8be9bf.bf#bfa#DE#xb#ul#xb#xb#xb#ul#aF#v5.Qs.Pe.Oq.Oq.NV.LO.8a#DE#ul#xb#fw.00.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.NV.NV.ND.ND#.c#bw.Oj.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.NDbfbbfc.Po.Tp.Pm.Pl.Tn.Tp.Tp.To.RD.Tp.NOaZT.L2##I.3Nbbr#Fs#hv#1w#HZ#AI#e3#DCa9dbfdbfdbfdbfdbfdbfeaM8bffbff#ZF#HZ#yY#pj#f#adE.Pl.Pi.LW.Ph.Ph.Pk.RCbfgacA#e4#SX#gd#e7awqai7.RH.Pl.PiavKbfhbfibeubfj#j#bfk#vN#if#ih#ih#sk#ie#BQ#O3bflbfmaYj#pl.4z.NO#lR#tobfnbfobfpbfqbfrbfsbftbfubeFbfvbfwbeI.Q8#gb#fU.Z5.Z5.UO.UO.UO.UO.Z5.MK.MJ.JB.JB.JB.JB.JB.JB.Z5.UO.UO.UO.Z5.Z5.Z5.Z5.Z5.UO#fU#e7#f#anGbeJ#aa.UO#CK.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK#e8#6wanHanHanHanHanHanHanHanHanHanHanHanHanH#6wa6v#3hbfx#P8bfy.O4.5a#CK#CK#CK#CK#CK#CK#CL.R0bfzbfAbfBbfCbfDbfEbfFabrbfGajQbfHbfIbfJbfKajPbfL.V1ajJajJa#N.M4beP#bW#kAaATbcvbfMbananHanHanH",
-"bfNbfObfP#2U#0Y.UW.Lq.R0.Z5.Lq.UO.UO.UO.Z5.0f#gd#Jh#Jh#Jh#Jh#Jh#Jh#Jh#gb#gb#gb#gb#gb#gb#gb#gb#gb#E2#E2#E2#E2#E2#E2#E2#gb.0HaoRajs#qq.0f.0faoRaoRayLayL.0f#qq#qq.0f.6q#wR#Zw#qR#ZwbfQ#Zw#Zw#OS#hv#hv#OS#hv#hv#qR#hv#vc#Z4#vc#Z4#qR#2v#4U#Ee#ET#gb#ew.6q.43.43.Q6aoRaoRaoR.0f.M0aoR.T0#ta.OK.6q.9Q#Ee#2v#2v#2v#2v#2v#2v#2v#2v#2v#2v#2v#2v#2v#ga#2v#2v#qR#ga#9I#f##jL#hY.TO.TO.9Q#uI#f##3l#2v#f##n8#gd#1c#Z.#2t#H8#Hb#H8#4m#ga#gd#ge#geaTVbfRbfSbfT#6pajJ.M5ajJ.M5bfUbfVbfWbfXbfYbfZbf0bf1bf2bcf#xb#ul#xb#xb#xb#xb#xb#ul#xb#J8.Oi.Pe.Oq.Oq.NV.NV#de#aF#ul#xb#cn.PT.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.NV.NV.ND.ND#ow#ul.PL.Pe.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.ND#lVaS#b.t.Ut.RD.NJ.Pl.QE.Tp.RD.QG#qg.Sw.8g.L4.RD#cb.Tp#9naadam5#DC#x.#Fs#6XarLbf3bf3bf3bf4bf3bf3bf3bf5bf6a8v#w6#1R#Xm#AIaadbf7.L0.Pl.PibdM#0G#Ha#gd#e7#gb#ge#ge#gb#e6#gbaagbf8#D3bf9bg.#6ibfk#6i#if#j##j##j##j##j##if#if#ih#RM#O##L3bg#bgabgb.IdbgcbgdbgebgfbggbghbgibgjbgkbglbgmbgnbdZbgo#0i.Rd#CK.Z5.UO.5i.R0.Q1.JCb#Fbgpbgqbgrbgsbgtbgubgvbgwbgx.JB.R0.UO.UO.UO.Z5.UO.UO.Z5.Z5.UO#ta#e8#1vanHalN#Ji.M0#aU.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5#iu#6wanHanHanHanHanHanHanHanHanHanHanHanHanH#6wanH#Gq#P8aWuaWu#.E#HK.Q0#CK#CK#CK#CK#CK#CK.Q0#oc#niaYlbgybgzbgA.RabgB.QaajJ.Nk.6h.M5ajJ.V1.M4.M4.V2.M4.M5.M4a#NbgC.6f.35#.vbgDbgE#6xanHanH",
-"bgFaB6#2WanGajs.4..Z5.Lq.Lq.UO.5i.5i.5i.R0.Lq#e4#ge#gb#gb#gb#gb#gb#gb#gb#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#wR#gd#fUajs#qq.Xz.0faoRajs.XzayL.Xzajs#qqajs#ta#jL#tk#qR#qR#VM#Zw#hvaad#hv#qR#hv#qR#qR#hv#qR#qR#qRaad#qRaad#qR#qR#Z4#Z4#OS#Zw#Zw#ga#Ee#xm#ge#IH#ge#Ef#ge#ge#f##ga#2v#2v#qR#2v#2v#2v#9I#2v#2v#2v#9I#9I#2v#2v#2v#2v#2v#2v#2v#9IagR#2v#2v#9I#9I#2v#2v#2v#9I#2v#2v#ge.OK.0f.0f.K7#jM#FJ#H8#2t#H8#H8#WJ#xmazb#TVbgGbgHakQbgI.M4.M4#8VbgJbgKbgLbgMbgNbgObgPbgQbgR#C8#aF#ul#xb#xb#xb#xb#xb#xb#ul#xb#br#ko.Pe#zD.Oq.NV.NV#ar#aF#xb#xb#C7.PT.97.NV.Oq.Oq.Oq.Oq.Oq.NV.NV.NV.ND.ND#.X#fw#bt#0H.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.2cbgSaQiaPs.WF##I.RF.LS.Pk.Pr.Tp.Tp.Tp.NJ.Pk.Pk.Pf.3NbgTad5#TB#Fs#Zx#54#Sw#kW#V1bd2ap#awqbgUbgUbgVbgWbgXbgXbgYa6P#Zx#6X#Yk#Xm#x.aWDaPnaWD#ZF#wR#n8#sM#sM#gb#gb#Uz#gb#ge#1vanG.TObgZbg0bg1#if#j##j##j##j##j##j##j##j##j##j##j##if#ihbg2bg3bg4bg5bg6bg7bg8bg9bh.bh#bhabhbbhcaO0#JE#zwbhdbhebhfbhgbhh.Q1.P3.P3.4..Z5bhibhjbhkbdYbhlbhmbhmbhmbhmbhmbfvbhnbho.4..Z5.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5ajs#KwanHanH#6wbhpaLq#fJ#g#.M0.Nfajsajs.Rl.Z5#jF.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO#CK.XY#UOanHanHanHanHanHanHanHanHanHanHanHanHanHanH#6w#6w#Go#P8#P8#d4#x.#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0bhq#Ar#AracFbhrbhs.OR.Rh.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.M4.Nk##c.6f#dJaATbhtbananHanH",
-".RY#qq#hu.2R.4..Lq.Lq.Lq.5i.UO.UO.UO.UO.UO.R0.XY#hY#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2.X2.Q6.XY#qqbd5ajsajsaoR.0fajsaoRajs.Nf.0f.4e#xm#Zw#hv#qR#qR#qR#vc#hv#OS#qR#hv#qR#qRaad#OS#Zw#qR#VM#Zw#Hp#Hp#qR#hv#qRaad#Z4#vc#qR#qR#qR#qR#qR#qR#qR#qR#qR#qR#2v#qR#qR#2v#2v#2vagR#2v#2v#2v#2v#2v#2v#2vakv#2v#9I#9I#2v#2v#9I#9I#9I#9I#2v#2v#2v#2v#2v#jM.MV.K7.0f.XY.K7.XY.XY#hXasQ#Hb#H8abXbhubhvbhwbhxbhya#N.M5.V1.37beRbhzbhAbhBbhCbhDbhEbhF#kj#DE#xb#ul#xb#xb#xb#xb#xb#xb#ul#xb#WU.TD.Ur.NV.Oq.NV.NV.Z1#aF#xb#xb#xba2o.Pw.NV.Oq.Oq.Oq.Oq.Oq.Oq.NV.Pe.Oq.NE.NE#v5#v5.ND.Pe.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.OqbhGbfcaPtbhH.Tp.Tp.QD.LS.Pk.QB#gu.RE.L0.L4.L0.NO.1q.4QbhI#Z5#Sw#2u#V1#j9#lT#4m#1##Z.ahG#nSaYHbhJbhJbhKa0mbhLbhM#j9#kW#Sw#54#Fs#e3#pj#hv#hv#hv#tk#Ee#xm#ge#gb#gb#ge#5janH#1ubhNbhO#rs#if#j##j##j##j##j##j##j##j##j##j##j##j##j##j##sqbg2bhPbhQbhRbhSaOZbhTbhUbhVaOZ#z5#Fd#z5#Fd#TM#FcbhWbeFbhXbhYbhZ#L2#JAbh0bh1bh2bh3bh4bh5bh6bh7bh7bh8bh9bh9bh8bi.bi#bia.JB.Z5.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5.22anGanHanHanHanHanH#5jaB6aDeaoXayjaoR#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.OK#1vanHanHanHanHanHanHanHanHanHanHanHanHanHanH#6wanGbib#P8#P8#P8#d1#CK#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#xlaH0bhqaPwbic.PY.X8.M5.M4.M4.M4.M4.M4.M4.M4.OZ#7AbeP#aR.35#evbidaGq#6wanH",
-"bieaYl.O4.4..4..Z5.Z5.UO.UO.Jt.UO.W9.UO.W9.UO.R0#n7#wh#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#E2#4.#4.#4.#4.#4.#4.#4.#4.#4.#4.#E2#wR#gb#ew.Q6#qq#qqajs.0f.0faoRbif.0fajs.39#qq.R2.9Q#4U#Zw#Z4#qR#Zw#Zw#OS#vc#Zw#Zw#qR#qR#hv#hv#VM#qR#qR#qR#qR#hv#qRbigbihbihbihbig#MS#hv#hvaad#qRaad#qR#qR#qRa92#2v#2v#2v#2v#qR#tk#9I#2v#2v#2v#2v#9I#2v#9I#9I#2v#9I#2v#2v#2v#2v#2v#2v#2v#2v#2v#2v#e7.T0.1J.XY.0f.0f.XY.XY.XY.R0.Nfa#dbiibijbikbilbimbbl.M4.V2.V1bin.1FbiobipbgIbiqbir#lO#8k#tq#xb#ul#xb#xb#xb#xb#xb#xb#xb#ul#xb#ul.Ns.Pe.NV.Oq.NV.Pe.UF#C9#xb#xE#xb#.9.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.NV.Oq#.bbis##V.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.WGbitaPtaPsaPt.YIbiu##I.QG.Tr.Pl.Pp.QC.LS.L1.L2.L6.XGbiva6wbiw#j9#4m#1#bix#KP#oA#U4aoh#2t#2tbiy#53#wAbizbiAbiAbiBaWuadH#lT#V1#2u#Sw#Zx#x.#1R#AI#pj#qR#tk#f##ge#ge#2UaTm#HZbiC#if#sk#xw#j##j##j##j##j##j##j##j##j##j##j##j##sq#if#xw#j##j##ifbiD#rs#pb#TL#FdaBW#z5#PN#z5#z5#Fd#Fd#TMbiEbiFbh8biGbiH#kM#skbiI#om#TL#z5#Gb#z1#lJbiJbiKbiLbgnbh7bh8bh8bh9biMbiN.Je.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.Q7anEanHanHanH#6w#1yaqHbdLanHbiO#dZ.SQ#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO#CK.UO#fIalNanHanHanHanHanHanHanHanHanHanHanHanHanHanH#6w#2V#Go#P8#P8#2t#dZ.UO.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#Ar#CKbiPanx.Rh.M5.M5.M4.M4.M4.M4.M4.M4.37.6h#cy#aRaATbiQbiR#5lanH",
-"aT4.9LaRP#oS.R0.R0.OF.K7.UO.Lq.OF.W9.OF.W9.K7.R0.4..OO#E2#E2#E2#E2#E2#E2#E2#4.#4.#4.#4.#4.#4.#4.#4.#4.#4.#E2#E2#E2#E2#E2#E2#E2#E2#ge#ge#f##gb.X2.Q6.0f#qqajsajsajs.0f.0f.0f.0fbd5bd5.SQ#sM#hv#Z4#NT#qR#hvaad#qR#qR#VM#MS#MSaad#qR#qR#qR#qRbihbiS#vcbiTbiUbiVbiWbiXaDna92biY#krbiZbaV#hv#hv#qR#qR#2v#2v#qR#qR#qRa92a92bigbigbig#2vbigbigbig#q.#qR#q.#VM#qR#qR#qR#qR#qR#qR#qR#ga#e6.XY.XY#qq.0f.0f.XY.XY.XY.XYaRR#pGbi0bi1bi2bi3abra#N.M5.M4.M5bi4bi5ajP#7z#8Wbi6bi7bi8#D.#xb#ul#xb#xb#xb#xb#xb#xb#xb#xb#ul#xb#ul.Ns.Pe.NV.Oq.Oq.Pw#zD#iq#xb#ul#C9#ar.Oj.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.Pe.PL#fw.XF.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Qsbi9bfcaPsaOlaPsaVcbj..NM.RE.Tp.NO.L0.NS.NS.L0.Pl.LZ.Pfbj#.QG#LI#Z5#Sw#j9#d3#Hb#U4#2t#Vs#6W#2s#Sp#Sp#Sp#2sbdtbjabjbbjc#2t#Qm#Z.abZ#j9#2u#Sw#Zx#Yk#ht#1caad#2v#Ee#HZaqH#7Kbjd#RQ#si#xw#j##j##j##j##j##j##j##j##j##j##j##j##if#sk#rr#RM#if#sq#j##j##if#if#F8#JE#Fbbje#lJ#z5#PN#PN#JE#zwbjfbhebh9bh8biF#s4#G.#Gb#Fd#Fd#Fd#Fd#PN#PN#JE#Gb#Gbbjgbjhbjibh8bjjbdZbjk.OB.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.4..TZ#HVanHanHanH#6watRasGa9e#P8#d4#dZ.Q6#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.OK#gb#UNanHanHanHanHanHanHanHanHanHanHanHanHanHanH#6w#6wa9f#P8#P8aWubjl.UO.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#HKaHZbjmbjn.R9.M4.M5.M4.M4.M4.M4.M4.OZ.M4bfLbfJ.X5bcvbjobcyanH",
-"aTrbjpbjqaQ8#pG.5i.R0.R0.Jt.Lq.W9.W9.K7.OF.K7.W9.R0.W9.OK#E2#E2#4.#4.#4.#4.#4.#E2#E2#E2#E2#E2#E2#E2#E2#E2#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#K1#GD#f##uI.6q.Q6ajsbd5#qqajsajs.0f.0fajs.Nf.0f.4e#Ee#qR#qRaadaad#qR#qRbiZbaVbaVbiSbjrbjr#kr.1ibjsbjtbjubjvbjwbjxbjybjzbjAbjBbjCbjDaMXbjEbihbih#hv#vc#vca92#9I#Ee#Ef#uI.9Q.9Q.X2#e7#gd#jM#jM#ge#uI#ge#Hq#f##ga#3l#2v#2v#2v#qR#2v#e6.XY.XY#qq.0f.0f.0f.0f#w.#pGbjFbjGbjHbjIbjJbjKa#N.V1.M5.M4.M5.6h.7C.M4.R.bjLbjMbjN#tq#DE#ul#xb#xb#xb#xb#xb#xb#xb#xb#ul#xb#DE#w8bjO.Ur.NV.Oq.Oq.NV.YJbjP#C9#ul#aF##Q.Oq.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.NVaQg#bw.Oq.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Qs.6SbjQaPsaOlaPsaPtaPtbjRaYZbjSaWJbjTbjU.PiaRf.Tm#dV.1n#azbjVbjWbjXbjYbjZbj0aq#bj1bj1bj2bj0#hl#3H#3Hbj3aWF#4P#Rlbj3bj3#Sp#0J#53#I.ahG#dZ#lT#2u#sy#6X#Z5#e3aad#R6a7Ma6Hbj4bj5#rs#if#j##j##j##j##j##j##j##j##j##j##j##j##j##if#j##j##if#if#sq#j##j##j##if#vN#rx#z1bjebj6bj7#z5biEbj8bj9bfubh8bh8bh9bhebiK#z5#z1#z5#Fd#h.#h##h##h##h.#h.#h##z5bglbd0bhmbgnbk..Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO#CK#w.aB6anHanHanH#6wanGa8t#YM#nS#P8#d0.K7.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.M0#gb#f##6wanHanHanHanHanHanHanHanHanHanHanHanHanHanH#6watR#nS#P8#P8#P8#lT.Z5.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#As#i.bk#.RgajJ.M5.M4.M4.M4.M4.M4.M5.V1bina#XbkabkbbkcanH",
-".2U.1N#.Abkdbke#r6#aU.K7.R0.R0.W9.Lq.Lq.K7.R0.UO.Lq.4..R0.OO#E2#E2#E2#E2#E2#E2#E2#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#GD#Zw#MT#gd##k.43aoRajs.39ajsajsajsajs.XY.2R.9Q#tk#qR#qR#VMbihbkfbkgbkhbkibkjbkkbklbkmbknawO.5e.5d.2W.2W#cD#cD#qz.5e.2X.1Nbkobkpbkqbkrbihbks.TO.UPajs.UO.Z5.Z5#CK#CK#CK#CKaQ8#CK#pJ.Z5.Z5.Z5.Lq.UO.UO.UOajs.M0#ta#fU.2R.5i.5i.XY.XY.0fajsa9v#n1.KZbktbkubkvbkwbkx##5#8V.M4.M5.M4.37.M5.37.37.M7a4Qbkybkz#tq#xb#ul#xb#xb#xb#xb#xb#xb#xb#xb#xb#DE#Wi.8p.O..Pe.NV.Oq#zD.Oq.Sz#ax#DE#ul#xb.Uq.Qs.Pe.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Pe.WR#J8.WR.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.Oq.TqbkAaSRaRibkBaOkbfcaUr#MfbkCbkD#vl#..#bB#db#aC#ep.8F#ep#.Y#rL#fxbkEaTC#er.YV.NX.0U.YV.PQ#oL#oKbkFbkG#XaaVbaWF#4PbkH#3Gbj3#3HaPo#Vsaoh#KP#dZ#j9#kW#54#Yk#DCbdca7LbkIbkJ#HQ#if#if#if#j##j##j##j##j##j##j##j##j##j##j##j##j##if#if#j##j##j##j##j##j##j##if#vN#rx#JE#z5#Fb#z5ay1bkKbdYbh8bh9bh8bdYbh8bkLbkMbkN#2NbkObkObkPbkQbkRbkS#L.bkTbkPbkUbkVbkWbkX.O4.4..UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.P8anEanHanHanHanH#6w#6watRbdLbdJ#Ij#CK.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CKajs#gd#TCanGanHanHanH#6wanHanHanHanHanHanHanHanHanHanH#6wanGbdcaWu#P8#P8#d4bkY#HK#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#B6#xlbkZ.M5bgB.M5.M4.M4.M4.M4.M4.M5.V1#fFbk0bk1aFfanG",
-".2U.2V.2U.82bk2bk3aVMaRRa9v.K7.R0.UO.R0.5i.R0.5i.R0.W9.4..Jt.UP#gb#ge#Nc#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ga#qR#qR#sz#xm#jL#wS.OK.Q6aoR.X3.0f.0f.OK#ge#qR#hvbk4bk5bk6aWg#1Obk7bk8bk9bl.bl#.5e.1L.2V.2U.2U.2U.2U.2U.2V.1L.1L#cD.2W.5daWgaxublablbaPe.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Q0.Z5.Z5#CKaSCblcbldbleblfblgblhblibljblk#8V.M4.M5.M4.M5.M5#J4.M5.OZ.Llbllblm#tq#Uq#ul#xb#xb#xb#xb#xb#xb#lN#xb#xb#J8.UF.PL.NV.Oj.Oq.Oq.NV.ND#c4.LP#tq#ul#xb#.f.Om.Pe.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.Pe.XF#zA.Ur.NV.Oq.Oq.Oq.Oq.Oq.Oq.NV.NV.Pe.97.97.Pe.Oq.TCblnblo#9o#y7.Ob.PR#jC##F.2n.8F.WE.WE.8F#.Y#cm.YL#.e#.e.3A.0U.0U.0U#.e#.Y#.Y#.e#.e.VK.6P#mg#hH##K#bi#SrblpblqarTacb#4qaVbaVb#3Gblr#2s#4R#H8#d3#4m#V1#sy#T5bfeblsbltblublv#j##sq#if#sk#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##xw#j##sk#HQ#HQ#HQ#sk#rs#j.#2N#z5#Fd#z5#z1blwbhebh9bh8bdYbheblxblyblzblAblBblC.MK.MK.Q1.Q1.Q1.Q1#aU.Z5.MK.JB.VU.R0.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.UOaDeanHanHanH#6w#6w#6w#6w#6waoX.OU#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5#ta#gd#geaDeanHanH#6w#6w#6w#6wanH#6w#6w#6wanHanHanHanHanH#7L#6w#6V#P8#P8aWu#P8#Ef#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#xlaH0blDblEbgB.M5.M4.M4.M4.M4.M4.M4bi4blFblGblH#f#",
-".2U.2U.2U#aX.1O.1OblI.RYblJblKa9v.Jt.5i.5i.5i.K7.R0.R0.5i.P3.R0.21#ge#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#f##2vaadaadaad#qR#Zw#ga#f##Hr#EU#xm#tk#Zw#MS#qRblLblM.5davC.2U.1L#.z#.z.1L.1L.1L.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2V#cD.2XbjzblNaTv.5a.5a.5a.5a.5a.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#GE.ZcblOblPblQblRblRblSblTbhzblUa#N.M5.M4.M4.SU.M5.OR.37blVblWblXblYa48#ul#xb#xb#xb#xb#Uq#xb#xb#xb#DE#xbaKc.ND.Ur.NV.NV.Oq.Oq.NV.Oq.99#cb.4r#aF#DE#C8##W.Pw.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.Oq.7o.NV.NV.Oq.Oq.NV.NV.Pe.97.97.Oq#lO.Ob.QP#.f.YG.9q#xF.2n#.e.YL.0U#er#eq.4q.9m#bp.YG.YG.0X.0X.YG#bp#nT#gF.YV.0U.3A.0U.Tk.0U.0U#.e#.e.0U.0U.0U.YL.WE#aBazo#m7#iJ.Rz.5EblZbl0bl1#51bkH#3GasN#2r#2s#wA#Hb#1#adGbl2a6Pbl3azTbl4bl5blvbl6#sq#if#sk#j##j##j##j##j##j##j##j##j##j##j##j##j##j##j##HQ#HQbl7bl8#JAbl9#DZbm.bm#bma#PN#Fd#Fd#SSbj6bdYbh7bjibmbbmc#cQ#Xx#babmdbmebmfbmgb#F.JB.Z5.UO.UO.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK#aa#6wanHanHanHanHanHanHanHanH#hu.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5.Z5ajs.6q#gb#gb#2S#6wanH#6wanGa7Ma9d#6wanG#6wbmhanHanG#6wanHa4c#6wanGa9daWu#P8#P8#P8#dZ.K7#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0bmiaPw#gS.M7.SU.M4.M4.M4.M4#8Wbmjbmkbml#Ij#gb",
-".2U.2U.2U.2U.2V.2U.1Nbmm.5a.5aa5da9v.Jt.R0.K7.5i.K7.5i.5i#jF.R0.VV.VXam5#ge#gd#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ga#Zw#hva92#qR#qRaad#Z4#vc#qR#qR#qR#qR#qR#q.bjEbmn.5d.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L.1L.2XbmobmpbmqaZBbmrbmr.4d.O4aIOaTv.5a#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.5aaHK.Q0#mDaMobmsbmtbmublSblSbmvbmwbipa#N.M5.M4.M5.SUbmx.SU.M5bmybmzbmA.PSbmB#ul#xb#ul#xb#xb#DE#DE#xb#fw#v5bbs.Ns#zD.NV.Oq.Oq.Oq.Oq.Oq.NV.Oq.Pp#ax#C8#.Z#.d#lS.PT.Pe.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.Oq.NV.NV.NV.Pe.Pw.Pe.PS.00#..#aF.9q#dU.VK#hH#.e#xH#gF.Vy.YG#aF.4r.8B.5B.8A.8w.8w.8w.8w.8A.8A.8A.8A.8w.9n.5B#br.0X.9q#gF.Tk.YL#.e#gB#.e#.e#.e.0U.0U#.e.VK.9f.5A#.Y#.e#cm.6P#m7azoblparZ#cj#hlaWF#4P#3H#6W#H8#3haYHbmCbmDbmE#SP#M6bmFbmG#j##sq#if#sk#j##j##j##j##j##j##j##j##j##xw#j##HQ#HQ#ifbmH#DZa5d.P3.P3.P3.P3.MMbmIbmJ#h.#Fd#Fd#z5#TLbmKbmLbd0bmMbmN#rv#cR#cR#BS#YubmOblxbmPbmg.O4.Q1.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK#aaaB6anHanHanHanHanHanHanH#iu#CK.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Rl#gc.P8.M0aoR#ta.22#iuaBsanE#f##gb#gbaoX#6waN5#7LbdJapm#P8bd2a8vbmQaWubcrbmR#6waN5anGanIa8vaWu#P8#P8#P8#P8#Hq#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#HKaHZbmS.R9.M5.M4.M4.M4.M4.OZbmTbmUbmVa.P#gd",
-".1L.2V.2U.2U.2U.2V.2U.1NbmWaPe.SY#oS.Z5.P3.Jt.R0.5i.K7.5i.K7.5i.Jt.R0#x.asG#2v#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#Uz#f##2v#MS#qR#qRaad#Z4#vc#qR#qR#qR#vc#vcba5bmXbmYaWgavC.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L.2U.1L.5daTr.0CbmZbm0bm1bm2bm3bm4bm5bm6.5a.Q0#CK#CK#CK#CK#CK#CK#CK#CK.Q0#CLaHKa71bm7bm8bm9blhbn.bn#blSbnablibnbbnca#N.M5.M4.M4.M5baB.SUblV.M5bndbnebnf##W.8v#xb#xb#DE#xb.8B.8a#ax.RR.Om.Oq.NV.Pe.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq#c4#.R#.b.RR.Qs.Oq.Nt.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.Pe.97.ND.Vw#.c.WC#cl.0U#aC##K.Oo#bx.TE#.c#bs#aH.5B.8w.8A.5B.5B.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.5B.8A.5B#hM#..#dd#cm#.e.0U#.e#.e#.e#gB.0U#iF#xH.On#dU.0U.0U.0U.0U#.Y#yW#m7#JLbngatp#51#2r#2r#3H#zBbnhbnibnjbnkbnl#SP#M6bmFblv#j##sq#if#sk#j##j##j##j##j##j##j##j##HQ#skbnm#JAbnn.TZ.P3.R0.UO.UO.UO.UO.5i.MKbno#h.#Fd#Fd#Fd#z5#z1#Fc#h##z5#Gb#Fc#Ey#ba#cR#BS#BS#Xxbnpbh6bnqbnr.JB.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5#w.aRAaBsaB6anG#6w#6walN.P8.Z5.UO.UO.UO.UO.UO.UO.UO.Z5.Z5#3ganH#6w#6w#6w#6wanHanHanH#6w#5jaB6ay9anGatRb#MbnsaWu#P8#P8#P8#P8#P8#P8#P8aWua9fa6PbmQbnt#P8#P8#P8#P8#d4b.d.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.9L#wN.Z.bnu.O1.M5.37.M4.M5#Yfbnvbnw#D3#P8b.d",
-".2U.1L.1L.1L.2U.2U.2V.2Ubnxbnya1.aQ8#CK#CK.5i.Jt.5i.K7.K7.O3.Jt.5i.R0.OO#d3#nS#Z5#Nc#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#f##f##gb#ge#ge#ge#ge#ge#ge#ge#ga#Z4#Z4aad#yY#pj#yY#pj#pj#pj#pj#pjblLbnz.2UaTp.1L.1L.2U.2U.2U.2U.2U.2U.2U.2U.1L.1L.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1O.2XbnA#aWbnBaG5bnCbnDbnEbnFbnGaZB.5a.Q0#CK#CK#CK#CK#CK#CK.Q0.Q3bnHbnIbnJbnKblRblRblSbnablSbnabnLbnMbnNbnO#8V.M5.M4.M5.M5baB.X8bnPbnQbnRbnS.8m.Qz#ar#C8#xb.5B#lS.RR.ND.Pe.Pe.Oj.NV.NV.NV.NV.Nt.Oq.Oq.Ol.Oq.Oq.NV.Oq.2A.WG.Pw.NT.52.9e.YJ.Oq.WR.Pd.Oq.NV.Oq.Oq.Oq.Oq.Oq.Oq.Oq.NV.NV.Pw.NV.Mj#jC.9p.3A.2n.9m#aH.QO#wD.Oq.Qs.TD.4s#hM.5B.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##N.8BbnT#.W#kj.2n.YL#.e#mh#.e#.e#.e.0U#.Y.WC.9m#.e#.e#.e#.e.0U.0U#kn.YL#0V.4HbnUbnVbnW#RlbnXbnYbnZbn0bn1bnkbn2#SPbn3bn4blv#j##if#if#sk#j##j##j##j##xw#sk#HQbn5bl9.TZ.MM.P3.R0.UO.UO.UO.UO.UO.UO.UO.P2bkR#h.#Fd#Fd#Fd#Fd#PN#z5#z5#PN#Fd#z5#TM#Dw#ba#cR#cR#BS#Xxbn6bdZbn7bn8.MI.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5.Rl.NfaoRaoR.UO.Z5.Z5.UO.UO.UO.UO.UO.UO.Z5#CK#ta#1vanHanHanHanHaTmanHanH#6w#6w#6wa8tb#Ma6P#nS#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8aWu#P8#d4a2Z.UO#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0blJbn9.RaaFF.OQ.R..R#ajfbo.#T5#P8#P8aWu",
-".2U#cD.1L.1L.2U.2U.2U.2V.1Obo#aUZaRP#CK#CK.O4.P3.OF.5i.5i.O5.OF.R0.K7.VVbig#mP#P8acAakv#ge#TV#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#2v#qR#2v#2v#4U#4U#ga#ga#4U#2v#pj#pj#pj#pj#pj#yY#yY#yY#yY#pj#pj#pja92#qRboa.2V.5e#.z#.z.1L.1L#.z.1L.1L#.z#o6#.z.2U.2U.2U.2U.2U.2U.2U.2U.2V.1L.5e.0za6.bobbocbodboebofaZBaRQaHKaIO.5a#CK#CK#CK#CK#CK#CK#ID.9FbogbohboibojbokbmublSbn#blSblSbolbombonboobopb#0.M5.M4.M4.M5.V2boqborbosbotbouaLd.JZ.Ur##R.8a##O.LQ.Pe.Oj.NV.Oq.ND.NE.Om.2#.PL.2#.O..Om.Om.2#.WR.NE.NV.NV#c#.4R.LS.Pi.Ph.Pi.PhbovaUlbow.Pw.NU.Oq.NV.Oq.Oq.Oq.NV.Pe.Ur#lO#...Vy#eq#py#ck#.d.PL.Ur.Pw.VB.RR.4s#bu.Pc.8B.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.0Z#cn#cp#.e#.e#.e#.e#.e#.e#.e#db#gE#.e#.e#.e#.e#.e#.e.0U.0U.0U.VK#m7boxboybozboAboBboCboDbn0bn1bnkbn2boEbn3bn4boF#j##if#if#j##j##j##xw#sk#ihboGa5d.MM.R0.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5boH#h.#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#TM#Dw#ba#cR#cR#BS#babmcbfvboIboJ.OB.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5.Z5.Z5.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO#CK.Z5.VXalNanHanH#6w#6waqH#6w#6w#6wbmha6Papm#P8#d4#Gn#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8b.d.R0#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#t#boKaTUboLayS.Y8boMboNboO#Nc#LI#P8#P8",
-".1L.2V.2U.2V.2U.2U.2U.2U#aX.1NboPaR1.Q0#CK#CK.O4.5i.K7.OF.K7.5i.5i.K7.R0.S4#d0#dY#LIa6v#ZF#Z5#f##gd#gb#gb#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#f##qR#yY#yY#yY#pj#yY#yY#pj#pj#yY#pj#yY#pj#pj#pj#yY#yY#pj#pj#pj#pj#pj#pj#pja92#q.boQboRboSboTboUaf8aj5a7X.5daP9a2fasdaeUadp.2U.2U.2U.2U.1Ladp#tKaPgboV#pOaR1#r6aIOaIOaIOaRQ.5a.Q0#CK#CK#CK#CK#CK#CK#CK#CKbhq.8ZboWboXboYboZbo0bo1bo1blSblSbolbnMbo2bo3bo4bo5bo6.M4.V2.M5.M5.PY.Nkbo7bo8#WP.WR#w8##Q.WG.Oq.NV.NV.Pe.NV.Oq.NEbo9.LP#q0bp.bp#bpabpb#yi#rI#rIbpcbpdbpebpf.NE.2#.NV.NT.Pi.Pz.Plakd#I4#gd#f.#gdaYHbpg.NU.Oq.NV.NC.Pw.ND#.9.0X.5A#cqbph.LN.LQ.NV.Mi.NV#e0#de.9n.8B.Pc.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#bl.0U#.e#.e#.e#.e.0U.YV#gv.0U#.e#.e#.e#.e#.e#.e#.e.0U.0U#.e#hL.4xbpibpjbpkbplbpmboDbpnbpobppbpqboEbprbpsbl6#sq#if#sk#j##1J#HQbl7bpt.J#.R0.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.TZ.0p#Km#Fc#PN#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#JE#zu#cR#b2#cR#XxbpubhebpvbeEbpw.OB.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.UO#e7#f##6wanH#6w#6watRatR#6w#6wa8ta8ta6vbpxbiObd2aWu#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#P8#d4#Gq#tk#ID#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Z5#gdaz.#JibpyaTU#qVbgF#gd#gd#x.#P8aWu",
-"#rk#sa#rj#cD#cD.2V.2U.2U.2U.2UaTrbpzbpA.SY.RY#CK.Z5.P3.JG.P3.P3.P4.P3.Jt.J#.X2#D3#mPbmQ#oAbpB#LI#1a#yY#ge#gb#gb#ge#ge#ge#ge#ge#ge#f##ga#qR#pj#yY#yY#pj#pj#pj#pj#pj#pj#yY#pj#vc#vc#Z4#OS#NT#NT#Z4#vc#pj#pj#pj#pj#pj#pja92bpCbpDbpEbpFbpGbpHbpIbpJbpK#ad.7T#a3a2fahe.1L.1L.1L#o6.2U#tJbpLbpMbpNbpOaQ8.Ty#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.5a#Ar.SgbpPbpQbpRbpSbpTbo1blSblSbnabnMbpUbpV#bV.9IbfK.M5.M4.M5.M5.M4.QabpWbpX#DO.1S.D4#j2#lN#bv.Pw.NV.NV.Oq.NU.Pd.NEbpYbpZaWub.d#DC#1c#1c#2v#qR#qR#f#.4e.Nf#IraoRbp0bp1#d8.Pe.Mi.Psbp2#d0#Zx#WJarO#2v#e4#gb#d7.NV.Oj#lO.Uq#bl#ZIbp3#dV.9e.Us.NC.Pe.NV.TD.8p.8B.Pc.8B.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n##P#er#gB#.e#.e#.e.0U#er##S.0U#.e#.e#.e#.e#.e#.e#.e#.e.0U.0U#.e.8F#0Wbp4bp5bp6bpkbp7bp8bp9bpobq.bq##M6bmFbmG#j##sq#rt#1J#HQbn5bqa.MM.R0.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.O4a53bqb#h.#QQ#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#TM#h##BR#cR#cR#BS#AAbqcbqdbeFbqebqf.MK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.XY#gb#UO#6wanHanHanH#6wanHanH#6w#6w#6w#6w#6wanG#6wbdLa41bfx#P8aXP#P8#P8#P8#P8#P8#P8#P8#d4#P8#4Sam5aoR#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#pJ#fI#ge#gb#gdaz.#Jh#ge#ge#gb#gb#Go#P8",
-"bqgbqhbqibqj#x3#cD.2V.2U.2U.1L#0xbqkbqlbqa.UW#CK.OF.OF.OF.OF.OF.OF.5i.K7.K7.R0bqm#mP#mP#mPaPoaPoaPo#wA#T5#ge#gb#ge#ge#ge#ge#ga#4U#2v#pj#pj#pj#yY#pj#pj#pj#pj#vc#vc#OS#bd#OS#u9#5jaTmbqnbqobqp#5j#OSbqq#vc#pj#pj#pj#pj#pj#pj#qR#kr.1i#n0#krbqra94bqsbqt.7S.6BafU#vH.1O.5dbqubqvahv#oU#iRbqwbqxaRQ#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#ocbqybqzbqAbqBbqCbpTbo1blSblSbnabnMbqDbqEbqF.9I.9IbqG.M4.M4.M5.M5bgB.X8.LsbqHaHZ#IDa2Z.JZ.5C#xb.6R.Pe.ND.Pd.MqbqI#8l#me#w6#pj#kV#4m#Qm#H8#H8#H8#H8#H8#Hb#LI#dZ#j9#Zx.VX.MG.OK#GqbqJbqJahG#7V#Hb#Qm#H8ahG#kV#E2bqK#ax.0X.L8bqL#sw.Ps.9e.Pw.ND.Nt.Pe.PL#.d.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B.5B.9n.8B.8B.8B.8B.8B.8B.8B.8B.5B.5B.9q#.e#.e#.e#.e#.e.9p#wy#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U#kn#.e.8F.4xbqMbqNbqObqPbqQbqRbqSbqTbev#SPbn3bn4blv#j##if#skbqUbaR.MM.R0.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.O4bqV#Zh#PN#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5#TM#Ey#cR#cR#BS#yObmcbdYbh9bi.bqWb#F.4..UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.R0.Z5.4..Q1.VU.VU.P2.P2.U0#xm#6wanJanHanHanHanH#6wanHanHanHanH#7K#7KaTm#6wanG#6w#T5aYH#P8aWu#P8#d4#d4#P8aWubqX#sy#Hr#w.#HK.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#CK#e7#ge#ge#ge#ge#ge#ge#ge#gb#gd#ht#P8",
-"#uUbqYbqZbq0bq1akE#x3.2V.1L.1Lbq2bq3bq4bq5bq6.WA.WA.K7.OF.OFbq7.1J.1J.XY.1J.R0.Rda41aPo#dYaPoaPoaPoaPo#4Rbdc#ge#gb#ge#ge#gb#2v#yYaad#pj#yY#yY#yY#Z4#vc#NT#NT#yYaSDbq8bq9br.#UIbr#brabrbbrcbrcazvbrdbeTanG#Ii#pj#pj#pj#pj#pj#pj#qR#qRa92#qR#qRbigbrebrfbrgbrh#qw.5o#uMbribrjbrkbrlaLYaZBbrm.J#.Q0#CK#CK#CK#CK#CK.Q0.Q0#CK#CK#CK#CK#xl#nibrnbrobrpbrqbnLblSblSblSbrrbombnLbrsbrtahx#bI#bW#iX.V1.M4.M5.M4.QabruaGR#B6aDE.Q0#wP#zC.LQ#bt.6R.Oq.NU#md#hw#d4aBu#e3arb#nS#4R#Gn#7V#7V#H8#H8#H8#H8#H8#H8#H8#H8#Zy#2t#2t#4s#xl.30#Sv#f.#lT#2u#hv#e8#NS#Vsbrvbrwbrx#OG#hg.Ts.18.NT.Oq.ND.Oq.Pe.ND.YM.5B.Pc.8B##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##N.8B.Uq.YM#gj.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#fe.Tk#.e#.e#.e#.Y#ci#eh#.Y#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U.0U#.e.WE.4xbrybrzbrAbrBbrCbrDbrEbnkaxObrFbprbmGbrG#sk#lK#FY.MM.R0.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.P2brH#lJ#Fd#Fd#Fd#h.#h.#h.#Fd#Fd#QQ#Fd#Fd#Fd#Fd#Fd#Fd#Fd#SS#6h#cR#cR#BS#yObmcbdYbh8bh9bhmbrIbrJ.TZ.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.4..VU.JB.O4#2wbrKbrLbrMbrNbrObrPbrQbrRbrS#HZ#1w#2X#7KanHanHanHanHanHanH#7K#7Ka4caTm#6w#6wanGbrTaWubrU#AI#4s#AIbrV.0o#w..Q0#HK.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#e6#ge#ge#ge#ge#ge#ge#ge#ge#gb#ge#Ha",
-"#uU#uU#tS#uS#vLbrWbrX#sc#.za1ja57aeobrYbrZbr0br1br2.Nl.P3.5i.1J.1J.1J.1J.XY.K7.R0#Hr#P8#dY#dYaPoaPoaPo#6WaW9a8t#gb#gb#ge#ge#4U#pj#pj#vc#Z4#NT#NT#Z4aN5bcn#20a4sbprbr3#iebiIbr4#sp#G0#F8#j.#TL#qH#B##46br5br6bfQ#pj#pj#pj#qR#qR#qR#qR#qR#qR#pj#qR#pj#krbr7br8br9bs.bs#bsabsbbqraDI#aP#aP.5a.Q0#CK#CK#CK#CK.Q0.Q0.Z5.Z5#CK#CK#CK#oc.ShbscbsdbsebnMbnablSblSblSbnabnMbonbsf.9I#bW.35#aR.W4abo.M4.M5.R..RgbhsbsgaH0.Q0#CK#HK#pJasW.NE.Om.ND#zDbsh#P8arO#4maWu#mP#mP#P8#2t#H8#Hb#Hb#H8#H8#H8#H8#2t#2t#2t#2t#2t#2t#H8#2t#4s.Q0#Ee#e3#AI#e4#2vbsibsjbsk#ir#LP.NS.Pi.RF.Us.ND.Oq.NV.NV.Pe.RR##Q.Pc.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.Pc#.f.Pc.8B.8B.8B#cn#cn#.f.8B.8B.8B.8B.8B.8B.8B.8B##Q.Ns#co.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.5B.5B.9q#.e#.e.0U.0U.5D##K#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U.0U#.Y#kk.8Fbslbsmbsnbsobspbn0bpobq.bq#bl4bn4blv#yQbsq.Q1.R0.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Q1#Xs#z5#Fd#h.#z5#STbkObsraSyaE6#h.#h.#QQ#Fd#Fd#Fd#Fd#Fd#z5#SS#Dw#cR#cR#Xx#AAbqcbssbh8bh9bpvbstbsu.OB.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Q1.VU.MK.JBbsvbrNbswbsxbsybsz#Cw#cR#zt#cR#babsAbszbsBbsCbsDbsE#6w#2Y#7KanHanHanHanHanHanH#7K#7KaTmanH#6wanG#YM#d4brV.30#HK#HK#HK.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#CK#SX#ge#ge#ge#ge#ge#ge#ge#ge#gb#gd#Fs",
-".7r.7r.7r#uV#uUbsFbsG.1LafQ.1OagcbsHbsIbsJbsKbsLbsMbsNbsO.WA.Nl.WA.Q2.5i.XY.XY.R0.K7#sy#yj#d4#dY#dYaPo#dYbpB#Gq#vc#gd#gb#ge#qV#yT#yT#vcaqJbsPbsQbsRbsS#AE#qF#RM#if#if#if#if#ih#ih#igbsT#wq#HQ#zu#zt#cRbsUbsValN#4U#2v#VM#2v#ga#ga#2v#ga#ga#ga#2v#f##f#aVLbsWbsXbsYbsZbih#2v#2v#ga.ON#CK.Q0.Q0#CK#CK#CK#CK#CK.Z5.0faoR.0f#oSazIbs0bs1bs2bs3bnablSblSblSbnabnMbqDbs4bs5bs6bs7bs8.X5a#X.9J.M4.M4.R#bs9bt..O4.6k#CK#CK.Q0#CK.NfaMb.Pd.Oa.Ktbt##3haWubtabpB#mP#dY#mPadI#Sw#tk#jM#Ee#R6#1R#4T#w6#w6#Yk#DC#2b#Wn#2u#4m#4mahG#6X#e7#1c#d0btbbtc.4y#yX.3C.8f.Pl##B.Mi.ND.Oq.NV.NV.NV.ND#.9.5B.8B##M.5B.8B.8B.8B#cn#cn#cn#cn.Pc.5B.9n#bw.8a.Z1.Z1#.d.8a.8p#.d#lS#.9#bv#Ml.Uq#cn#br#cn.8B.Pc.8a.2c#2h#cn#cn#br.9n#.c.5B.8B.8B.8B.8B.8B.5B.9n#vn#xH#.e#.e.NB##S#.e.0U#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.Tk.0U.8F.63a7Fbtdbtebplbp7bqRbqSbtfbtgboEbprbth#HPbti.O4.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.UWbtj#Fc#Fd#h.btkbtlbtm#QJbsv#2wbtn#Kmbto#h.#PN#Fd#Fd#Fd#Fd#JE#z5#yP#cR#BS#AAbtpbhebdYbh8bh8bqdbtq#OZbrPbtr.4..UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.4..JB.JB.RlbrKbrMbtsbtt#OZ#cR#qI#B##f1#zt#cR#cR#cR#cR#cR#cR#zt#ib#VF#VF#ntbtubtv#2U#2XanHanHanHanHanHanHanHanHanHanH#6wanHapmarH.Z5.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0#ew#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb#f#",
-".7r.7r.7r.7r.7rbtwbtx#x4#rj#.zatK.9UbtybtzbtA#b##8JafKbtBbtCbtDbtEbtF.Q2.Qp.IX.Hb.Qp.K0#DCbix#0J#Ru#Ru#Ru#6W#dQ.QV#Ii#ga#1vbtGbtHbtIbtJbtKblu#HQ#sk#RM#if#sq#j##if#if#yQ#G0#Cv#j.#Fd#z2#ic#sm#cR#BS#b2#zt#WCbtL.21#fU.OO.OO.OO.OO.OO.SQ.2R.Nc.Q6.MVaoRaoR#ta.Q6#fU.OK.4e.OK.OK.4e.4e.MV.5i.Z5.Z5.UO.UO.XY.0f.Q6.Q6aw.at5btMbtNbtObtPbomblSblSblSblSbolbombtQaiJa#1btRbtSbtT.35.X5a#X.7C.M7.QaaLibtUaH0#HK#CK#CK.Q0#HKajs.1B.F7.RA#DB#65#nS#d4#mP#dY#d4#3h#j9baV.UO.Z5.M0#e6#gb#ge#ge#ge#Nc#ew#fU#ta#ta.M0ajs.Z5.OW#ta#1waWDam6.Pz.3P.Pl#cc.QF.Pg.QC.Sv.ND.Oq.NV.Oq.Oq.NV#XO#.W.Pc.8B.8B.8B.8B.5B.Uq.8p#lS#ax.Mj#lO.LQ.LN#e.#y7.2s#uk#vj#lS.8p#fu.8p.6R#ko.2s.Nu.Mx#rN#U1.QO.Uq#lS.Nu#ax##P.9n#gh.Nu.Nu#lS.5B.8B.8B.8B.8B.8B.8B.5B.9n#bl.3A.0U#xa.ZW#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U.4I.YL#oN#m7btVbtWbsmbtXbtYbtZbpobt0bt1bt2bt3bt4.UO.Z5#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO#CKb#Fbqb#h.#h#bsrbt5.MK.4..Z5.Z5.Z5.4..P2bqabt6#h.#QQ#Fd#PN#JE#TM#sn#ba#BSbt7bt8bssbdYbh8bh8bh8bdYbmc#zt#Zgbt9bsv.JB.Q1.4..4..4..VU.MK.VU.Q1.4..Z5#aU.UO.R0.Z5.Q1.JB.Nnbu.bu#buabubbucbmcbud#f2#zt#cR#BS#cR#cR#cR#cR#cR#cR#cR#BS#BS#BS#cR#cR#BS#AA#VF#babuebuf#2WanHanHanHanHanHanHanHanHanH#6wanGbdcaW9.0o#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#CK#e7#ge#ge#ge#ge#ge#ge#ge#ge#ge#ge#gb",
-".7r.7r.7r.7r#uV#uSbugbuhafQ.1Oabc.9U.9U.7Xbui#ahbujbukabfbul#F##ChbumbunbuobupbuqburbrMbusbutbuubuvbuwbuxbuybuzbuAbuBbuCbuDbuE#s5buF#HQ#if#if#if#j##j##j##sq#if#sp#so#TL#h##rw#qJbuGbuH#pa#F##f2#zt#f1#FWbuI.Qo.0f.0f.0f.0f.XY.0f.XY.XY.XY.XY.XY.0f.0f.0f#TGaDKaDK.39.0f.0f.0f.0f.0f.0faoRaoRaoRaoRaoR.Q6aoR.0fat5buJbuKbuLbuMbuNbnablSblSblSbnabnMbnNbuObuPbuQbuRbuSbuTbuU.35.6fbuV.X8.R.bo7aol#Ar.Q0#CK.Q0.Q0.Q0#w.#n8#wRbuW.OibuX#LI#mP#dY#nSbuY#EfaoR#CK.UO.4e#gb#f##f##f##f##f##f##f##f##f##f##f##gb#e7#qRbuZamRard.Pk.Pl.Pi.Ph.Ph.RC.3B.5N.Oq.Oq.NV.Oq.Oq.NV.Pe.ND.6R#aH.5B.9n.Uq#fu#.d.UF.8l.PT#uk.1f.6R#bv.8a#bw.9n.5B#cn#cn#cn.Pc.8B.8B.8B#cn#cn#cn#cn.Uq.Nu.Tp#qg.5N.3N#j6.Uqa6r.4K#rU.TC#hM.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.9s#.Y.9m#tt#.e.0U#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.Tk.0U.8F.Nqbu0bu1bu2bu3bpmbqRbqSbu4bn2boEbu5bu6.1J.UO#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.4.#SJbu7#JEbu8bu9.Q1.Z5.UO.UO.UO.UO.UO.Z5.P2#Km#Fc#z5#z5#z5#rw#zu#ba#BS#babmebssbdYbh8bh8bh8bdYbdYbv.#Xx#cR#f2azBbv#bvabvb#QJbvcbvdbvebvfbvgbvh.K7.Q1.MK.VUblKbvabvibvjbvkbvlbhmbfvbvmbtp#yO#BS#cR#cR#cR#cR#cR#BS#BS#AA#AA#ba#AA#AA#AA#ba#BS#cR#cR#BS#AAargbvnbuf#2XanHanHanHanHanHanHanHanHanH#6w#6wap#aeY#HK.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Z5#gd#gb#gb#gb#gb#gb#gb#gb#E2#ge#ge#ge",
-"#xu#s0#tR#tQ#vLbvobvp#wk#.zaIa#tI##p.6B.6B.6B##p##qbvqbvrbvs#yPbvtbvubvvbvw#TK#Ey#Ey#yOakz#C0bvxbvx#Ch#cR#qHbvybvzaZk#pbbvA#if#lK#ih#if#if#xw#j##j##j##j##sq#if#iebvBbaSbvCbvCbvDbvEbvFbvGbn2bvHbtBbvIbuIbvJ.R1.0f.0faoRaoR.0f.0f.0f.0faoR.0f.0f#qqbvKaoRbvLbvMbvNbvO.32aoRaoRaoR.Q6.Q6aoRaoR.Q6.Q6aoRaoRaw.aDKbvPbvQbvRbvSbnablSblSblSbo1bo0bvTbvUbvVbvWbvXbvYbtSbvZbuU.6f.9Ibv0axmaFrbv1aR1aQ8.UO#CK#CK.Z5#t#.4e#wR#GD#Zwbv2.YK#Gnbv3#P8a2Z#ta.9F#CK#fU#gd#f##f##f##f##f##f##f##f##ge#ge#ge#ge#ge#e7#ZFbv4bv5.Pl.Py.Pl.Pi.Ph.Pi.Ph.Pz.NJ.99.ND.Oq.NV.Oq.Oq.NV.Ol#vi#.d#.Z.5B#fu#..#.c#bu.9n.5B.8B#cn#cn#cn#cn#.f.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#aH.8B.8B#mQ#hCbgT.KK#hF#rU.Tp#m9#..##P.8B.5B.8B.8B.8B.8B.8B.8B.5B.9n#iqbv6.ZZ#Ad#xF#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U#xy#cm.3kbv7bv8bv9bw.bw#ayCbpnbpobq.bwaboHbwb.SR.UO.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.Z5#QJbu9#aU.O4.UO.UO.UO.UO.UO.UO.UO.UO#CKblCbwc#g9bwd#Ex#g7#zt#cR#yO#6fbwebssbh8bh8bh8bh8bdYbssbwf#BS#BS#cR#ba#zt#babwgbwhbwi#f2#F##f2#VGbwjbwkbwlbwmbwnbwobwpbdZbwqbfvbdYbh9bwrbwsblx#ba#BS#cR#cR#cR#cR#cR#Yu#BS#AA#ba#ba#ba#AAbwt#ba#BS#cR#cR#cR#b2#AAargbwu#2U#7KanHanHanHanHanHanHanHanH#6wanGbwv#nSaoR#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.Q0.Q0#CK.Q0.UO#gb#ge#ge#ge#ge#ge#ge#uI#gb#gb#gb#gb",
-"bwwbwxbwybwzbwAbwBbuh#rj#o6aqw.7T.9U.6B.7R#r8.6B.9U.7SbwCbwDbwEbwFbwGbwHbwI#30bwJ#6ibwKbwL#ib#AA#AA#AA#AA#kL#soaZlaE7#sr#sr#if#ih#vN#lK#mH#wq#uY#uY#mH#if#sk#HQbwMbwNbwO.Qo.Qo.Qo.Qo.Qo.Qo.Qm.1CbwPbwQ.R1.Xz.0f.0f.0faoR.0f.0f.0f.0f.0faoR.0fbwR.RdbwSbwTbwUbwVbwT#CM.32aoRaoRaoR.Q6aoR.Q6aoR.Q6.Q6aoRaQ7bwWbwXbwYbwZbnablSblSblSbo1bpTbw0bw1bw2bw3bekbw4bw5#dJbw6.35#aRbeMbw7bw8axobw9#CL.K7.K7.R0#qq.0g#E2#wR#4U#4U#GD#MUbx.bx##3harH#pj.Nf#HK#pJ#fI#f##f##f##f##f##f##f##ge#ge#gb#ga#DC#vc#sM#2va.Qa3m.Py.Pk.Pi.LW.Pi.Pi.Pi.Ph.Pz.NO.3O.ND.Ol.Oq.NV.Oq.ND.NV.Mi.96.SJ#hM#aH.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B.8p#XK#nX.J2.1o.LO#lS#cn.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.9n#aH.4q.Tk.Up#XQ#.Y.0U#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#bo#.e.WE#OFbxabxbbxcbxdbxebxfbrEbxgbpqbxhbxibu9.R0.UO#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.4..4..Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.4.b#Fbxj#z3bxk#VC#ba#cR#cR#BSbxlbh9bdYbh8bh8bh8bh8bssbdYbpu#Xx#cR#cR#cR#cR#cR#zt#zt#zt#cR#BS#cR#cR#ztbxmbxnbmObxobpvbdYbwrbmcbv.bxpbmObwt#BSbxqbsAbsAbxn#VG#VGbxrbsA#Xx#BS#BS#BS#BS#BS#BS#BS#cR#cR#cR#cR#cR#cR#cRatIbszbtG#2WanHanHanHanHanHanHanHanHanH#6watRaYH#xm.OW.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK#CK.Z5.Q0.Q0.Q0.P8aoXaoXanGanGanGaoX#1vaDeaDealNanE#f#",
-"bvwbxsbxtbxu#sa#sa#cD.2V#tK#sU.7Wap2#vH#pRarCaFQbxvap3abdbxwbxxbxybxzbxAbxBbxCbxDbxEbxFbxGbxH#VF#ib#ol#AzbxIbxJbxKbxLbxMbxNbxObxPbxQbxRbxSaZmbxTbxUaxQbxV#xwbxWbxXbxY.XzaoRaoRaoRaoRaoRaoRaoR.0f.XA.Xz.XY.0f.0faoRaoR.0f.0f.0f.0faoRaoR.0fbwRatbbxZbbQa7Ybx0bx1.L#azI.Q6aoRaoRaoRaoRaoRaoRaoR.0f.5iazIbx2bx3bmtbpTbnablSblSblSbo1bx4bnLbx5bx6#8Ibx7bx8buSbx9.1E.35#aR#aSby.by#bya.XY.5a.XY.K7.R0.MV#gb#4U#4U#4U#4U#4U#GD#f#byb#zB#x.#gb.M0#HK.Q0.4e#f##f##f##f##f##f##ge#gb#ge#pj#Sw#Z.#H8#1##4Rard.Pl.Pk.Ph.Pi.Pi.Pi.Pi.Pi.Ph.Pz.Pp.Pw.Oq.NV.Oq.Oq.ND.Pe.52#c#.2s.8a.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B#cn.J1.5N.99.Pw#.b.8B#cn.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#bp#nV#gC.Ns.0Y#dU#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.Tk.0U#ep.PHbycbydbxbbyebyfbygbyhbyibnlbrFbpr#JB.TV.Z5#Ir.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO#CKbvbbyj#jX#BR#ba#cR#cR#cR#Xxbt7bwrbssbh8bh8bdYbdYbssbyk#AA#BS#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#cR#BSbylbymbqcbt8bv.bpubwt#cR#Yu#BS#ntbyn#HF#HF#VG#nt#Cwbyobypbyqbyrbysblxblxblxbxpbxlbwt#BS#BS#BS#BS#cR#cR#cR#cR#cQbytbyu#5janHanHanHanHanHanHanHanHanH#6w#6wapmbyv.UN#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.Z5#tk#d0a41b.dajs.R6#e8anHanHanHanHanHanHanHanHanHanH#6w#6w",
-"#QPbywbyxaex#cD#cD.2V.2V.1L.1L#tJ.0E#o6#.z#.z#.z#.z#tKacUbyybyzbyAbyBbyCbyDbyEbyFbyGbyHbyIbyJbyKbyLbyMbyNbyObyP#yG#wnbyQbyQ#uWa0haYB#2E#DnaJAbyRbySbyTbyUbyV.R2.Qo.XzaoRaoRaoRaoRaoRaoRaoRaoRaoR.0f.0f.0faoRaoRaoR.0faoR.0f.0f.0f.0f#qq#qqazIblObyWbyXbyYaD9bvKbd5.Q6aoRaoRaoRaoR.0f.R1.JtbyZbwRby0by1by2by3bnablSblSblSbo1bpTby4by5by6by7#7m#dMby8by9buP.1E.35#aRbz.afy.6ibz##CL.R0.XY.K7.R0.OO#ge#4U#4U#4U#4U#4U#4U#ge#HZaWu#1a#Jh#gb.Z5#HK#pJ#gb#f##f##f##ge#ge#gb#ga#1b#WJ#Hb#H8#H8ahG#EC.J4.Pl.Ph#.O.Pi.Pi.Pi.Pi.Pi.Ph.Pz.Tm.Pe.Oq.Oq.Oq.ND.Tq.RG#c2.Mq#ct##P.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B#.f#gh.4R.PL#ek#mQ.8B.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n##L.Tk.9m.Ob#bu.8A.9s.0U#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U.Tk#.Y#tubzabzbbzcbzdbzebzfbzgbzhbzibzjbzkbzlbzm#2w.4..Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5.UWbznbd4#ib#cR#cR#cR#cR#cR#BS#babzobssbdYbdYbhebykbym#AA#BS#cR#b2#cR#cR#zt#cR#nt#BR#yO#ba#cR#cR#BS#bablxbtpbxl#cR#Yu#Cw#g7bzp#UG#z5#pZ#g9bzqbzrbzsbykbwrbztbdYbdYbdYbdYbdYbdYbdYbdYbhebwrbykbv.bxl#ba#BS#BS#BS#BSabgbzua.PanGanHanHanHanHanHanHanHanHanH#6w#7Lap##tk.OW.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK.UP#A.#d4#d4#P8#d4#lT#w.aBsanHanHanHanHanHanHanHanHanHanHanHanH",
-"#TMbzvbzwbzx.2V#cD.2V.2V#cD.1L#.z.1L.1L.2U#cD#x3#wVbuhbzybzzbzAbzBaIj#LD#Nw#NwbzCbzDbzEbzDbzFbzGbzHbzIbzJ#wn#s0.7r#uU#tSbzK#x6bzLbzMbzNbzObzP#38#UyabV#MU#tk#ge.UP.XY.XY.0faoRaoRaoRaoRaoR.0f.0f.0f.0f.0f.0f.0f.0faoRaoR#qqbwRaDSaDK.Ju.Ju#qqayLbwRbzQbzQayLaoRaoR.Q6aoRaoR.Xz.Qp.QpaL5bzRbzSbzTbzUbzVbpTbo1blSblSbo1bo1bzWbzXbzYbzZ#8IajB#cPbz0bz1bz2#DT.35.6fbz3.M7bz4##eaR1.XY.XY.K7.K7.0H#wR#4U#4U#2v#4U#4U#4U#4U#gdasG#nS#f#am5#2v.Lq#HK#ta#K1#f##f##ge#gb#vc#Sw#d3#H8#H8#H8akeahGbz5.LV.Pi.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Ph.RC.4S.NV.Oq.ND.Oq#nA.RF.2A#aM#gj#w8.Pc.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B#aH.Vwbz6#qT.8B.Pc.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#fe#ft.5A#AJ#bw.8B.9n.8B#gF#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U#.e.2p#ftbz7bz8bz9bA.bA#bAabAbbAcbAdbAea3FbAf.UO.Z5#CK.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CKaoRbAgbAh#B.#cR#cR#cR#cR#cR#cR#BS#AAbmebhebqcbym#ba#BS#BS#AA#cR#zt#cR#g7#Ey#Ex#zu#g7#ba#ba#cR#cR#BS#ibbAi#Yubdi#Eu#Ex#h##M.#Cv#ifbAjbAkbh5bh6bdYbdYbdYbdYbdYbdYbh8bh8bh8bh8bh8bh8bh8bdYbdYbdYbdYbhebt8bzobymbwt#BS#babxqbAl#pj#6wanHanHanHanHanHanHanHanH#6wanHbcr.SQ#HK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0.OW#.EbrU#d4#P8a3N#P8aWu#P8bAmaB6#6wanHanHanHanHanHanHanHanHanHanH#6w",
-"#z5a0dbAn#1N.1L.2U.2U.2U.2U.2U.2V.2U#rjacSaKWbAobApbug#xtbAqbArbzKbAs.7r.7r.7r.7r.7r#uV#uU#uSbzE#s0.7r#uV#uV#uU#tR#tRbAtbAubAvbAwbAxbAy#Gq#LH#Yjam5#ga#2v#qR#qR#2v#jM.2R.XY.XY.XY.0f.0f.0f.0f.0faoRaoR.0f.0f.0f#qqbwRbvKazIbAzbvMbAAbABbACbwR#qq#qqayL.0f.0faoRaoRaoR.0f.OD.0fbADbAEbAFbAGbAHbAIbAJbnablSbo1bo1bo1bAKbALbAMbAN#8Ia#EbAObAPbAQ.35bw6#DT.X5.1EbARbASbAT.Ty#oS.0f.R1.Z5aR1#gc#GD#4U#4U#4U#4U#4U#4U#4U#TU#4RbAUbAV#gdadHbAW.Q0.Nf#ge#f##ge#gb#qR#2u#Qm#H8#H8#H8#H8#Qm#H8bAX.L2.Pi.Ph.Pi.Pi.Pi.Pi.Pi.Pi.RC.Pi.L5.Oq.ND.ND#nA.Tm.RF#vk.9n#w8.8B.5B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B##M.8B.8B#.g.7o.PL##Q.Pc.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#xD#er.ZW#..#.c.8B.8B.5B.9n#aF#aC#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.0U.0U.WE.WDbAYbz8bAZbA0bA1bA2bA3bA4bA5bA6.21.P3.Z5.UO.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.R2aDea7JbA7azw#cR#cR#cR#cR#cR#cR#BS#BS#babxlbwt#Xx#ba#ba#46#zt#nt#ru#sl#rw#sn#RO#cR#cR#cR#cR#cR#cR#zt#ba#yP#Ex#qJ#id#Cv#if#wqbA8bA9bB.biGbjjbh8bh8bB#bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bdYbdYbdYbdYbwrbmcbBabBbayHbBcba5#6wanHanHanHanHanHanHanHanGbdKa2Z.Q0.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HKauM#nS#P8aWu#P8#P8#P8#P8#P8a6PaN5#6wanHanHanHanHanHanHanHanHanH#6watR",
-"#FdaZkbBdbBe#Zr.2V.2U.2U.2U.2Uaex#wkbBfbBg#yE#uUbAqbBh#Ow#8z#x8bBibBjbBkbBk#x7#Jn#Dp#uU.7r.7r.7r#uV#uU#uS#vLbBlbBmbBnbBobBpbBqbBrbBsbBtbBubBvaPobBwbdu#qR#2v#qR#qR#qR#ga#gd.OK.0f.K7.XY.XY.0faoR.0f.0f.0f#qqbzQazIbAzbBxbBybBzbBAbyYayLbd5aoR.0f.0f.0faoR.0f.R1.Qpbhh.QpbBBbBCbBDbBEbBFbBGbBHbBIbpTbo1bo1bnabBJ.7TbBKbBLbBM#8I#b1#dMbBNbBObuPbuU#DT#dJbBPbBQaOxbBRaPe.XY.XY.K7.Z5.OLbBSbBT#Nc#2v#4U#4U#4U#4U#4U#GD#e6bBU#Rm#Go#e3#LI#kW.Q0#e7#f##ge#gb#e3ahG#H8#H8#H8#H8#H8#Qm#Hb#7W.Pf.RC.Ph.Pi.Pi.Pi.Pi.Pi.Ph.Pg.3P.YJ.Qs.ND.Us.Pf.Po.LQ.9n#w8.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.Uq#j2.TD.4s#.Z.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#fe#xH#dd#.c.Uq.8B.8B.8B.8B.5B.8A#gi#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e.NX.0U.8F.NqbBVbBWbBXbBYbBZbB0bB1bB2bB3.U1.21#w..UO.UO.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.22anG#7K#2XbB4#ib#91#cR#cR#cR#cR#cR#cR#cR#BS#BS#BS#cR#cR#zt#qH#ic#rw#TMbB5#g7#zt#cR#cR#cR#cR#zt#ba#g7#Ew#PM#z5#M.#rx#RM#wqbB6bB7bB8biGbjjbh9bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bh8bdYbdYbdYbhebB9bC.bC##vc#2VanHanHanHanHanH#6w#6wa8v#Hr.Y..Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HK##k#3h#P8aWu#P8#P8#P8#P8#P8#P8#YM#6w#6wanHanHanHanHanHanHanHanH#6wanG#YM",
-"#Fd#z5a0daMzacK#rj.2V.2U#sc#wkbCabCbbCc#tQbzE#s0bCdbCebCf#cR#yK#yK#zsbCgbCh#yJbCf#yH.7r#uV#uUbtw#vL#uUbCibCjbCk#rj#sbaex#qz#rmaDa#5p.2VbClbCmbCnbCobCpbiwbCqbCr#2v#qR#qR#qR#2v#f##e7.OO.0f.XY.XY.XYbzQbCs.JubCtbCu.6o.1Pa4xbCv.XzbCs#qqaoR.R1.0f.0faoR.XY.JCbgFbCwbCxbCybCzbCAbCBbCCbCDbCEbCFbCGbo1bnabCH.6B##qbCIbCJaYgatIavobdCbCKbCLbCM#eF#h8#dkbCNbCObCP.0f#oS.0f.XY.S4bCQarwbCRbCSbCT#Ji#4U#4U#4U#4U#4U#4U#n8#d3#HYapca41aWu#2t#yY.Q7#f##ge#ge#4T#Qm#H8#H8#H8#H8#H8#Qm#HbbCU.Pp.Pg.Ph.Pi.Pi.Pi.Pi.Pi.Ph.Pz.6T.Mi.NE.8q##H.Pm.JZ#.c#w8.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn.8B.5B.8B.8B#de#.0.4s.5B.Pc.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.Qr#xH.9m.Uq.9n.8B.8B.8B.8B.8B.8B.9n.4r#er#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#.e#kn.Tk.VK#aDbCVbCWbz8bCXbzebCYbCZbiZ.ZU#kx.2Rajs#w..UO.Z5.UO.UO.UO.Z5.Z5.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.UO.Z5#CK.UOaLq#6wanH#7K#2YbsEbC0atI#91#cR#cR#cR#cR#cR#cR#cR#b2#cR#zt#nt#z3#h##Cs#Fc#zu#cQ#cR#cR#cR#zt#cR#g7#Ey#sl#lJ#Gb#G.#G0#if#lK#ifbC1bkMbjjbh8bh9bhebhebhebh8bh8bh8bh8bh8bh8bh7bh7beFbh7bjjbh7beFbeFbmbbdYbdYbh8bh8bh8bh8bh8bh8bh8bh8bdYbdYbvmbC2bC3bC4bC5#7KanHanH#6w#6watRbC6.Z5.Q0#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK#CK.Q0#HKaZP#d4aWu#P8#P8#P8#P8#P8#P8#P8a9d#6wanHanHanHanHanHanHanHanH#6wanGbpxaWu",
-"#Fd#Fd#z5bC7bC8.4b#rj#sbbBfbC9#Sd#vLbD..7rbD##6.akz#E7#zs#ba#cR#cR#ba#ba#ba#baaun#Ay#92bzCbDabDbbDcbDd#25bDeajE#o6#.z#.z#.z.1L.1L.1L#cD#cD#qzaf7#5p.1ObDfbDgbDhbk4brebDi#3l#3l#tk#tk#Ee#EU#uJbDj.L#bDkbDlaO.axvaDabDmbDn.JdbDo.Um.Qo.Qo.Qo.XA.XAaHp.Qm.MKbDpbDqbDrbBEbCAbDsbDtbDtbDubAKa#KbnabCHbDv.7WbDwbDxbDybDza.b#cQ#cPbDAbDBbCMbDCbDDbDEav4bDFbDGbDH.Ty.UO.0f.Q6.4e#M3bDIbDJaxl.SVbDK#gb#2v#2v#4U#4U#4U#4U.0HbDL#E.bDM#AIbDNadHajs#gd#f##gb#yY#Hb#H8#H8#H8#H8#H8#Hb#Z.bDO.Vz.Pg.Ph.Pi.Pi.Pi.Pi.Pi.Ph.Pg.RF.Oq.NV#c2.3B#gu.6R#w8.Z1#.d.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn#cn.6R.4s.8B.5B.6R.1f#bw.Pc.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#DK#er#ck.9n.5B.8B.8B.8B.8B.8B.8B.8B.5B.9n#bp.0U#.e#.e#.e#.e#.e.0U.Tk#bi#aB#pyatv.Og.PQbDPaa##61ac.bDQbDRbDSbDTbDUbDVbDWbDXbDY.Sg.U1bDZbD0bD1bD2bD3bD4bD5bD6bD7bD8bD8bD8bD9bE.bE#bE#bE#bE#bE#bEabEbbEabEcbEdbEebEfbEfbEgbEgbEh#hYbEibEibEjbEkbElbEmbEnbEo#TSbEpbEqamMaZCbErbEsbEr.QcbEt#TT#TTbEubEvbEw.JgbExbEybEzbEAbEBbECbECbECbECbECbECbEDbEEbEFbEFbEFbEGbEFbEFbEFbEFbEFbEHbEEbEEbEIbEJbEKbELbEybEKbEJbEMbENbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEMbEObEPbEQbEPbEPbERbESbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbEUbEVbEVbEVbEVbEVbEVbEVbEVbEWbEXbEPbEPbEPbEPbEPbEPbEPbEPbEPbEYbEZbE0bEV",
-"#Fd#Fd#FdbE1bE2auqbuhbzzbE3#vLbD.#xubE4bE5#cR#E7#ba#cR#cR#cR#cR#cR#cR#ba#yO#yKbE6bE7aa5bE8bE9bF.bF#bFaaF1bFbbFcaqNaeS#sTaeRaeQ.2U.1L#.z#.zadp#o6#o6#o6a2gafQbFdbFebFfbFgbFhbFibFjbFkbFlbFmbFnboabFo.2UbFpbFq#26bFrbFsbFtbFubxXbFvbFwbFwbFxbFybFzbFAbFBbFCbFDbFEavlbFFbFGbFHbFIbFJbFKaiObDvapH##pbFLbDwbFMbFN#8IatIavobFObFPbFQbFRbFSbFTbFUbFV#ks.MP.Sk#aU.0f.4e#jM#f#bFWbFXbFYaFF.M6bFZbF0#2v#2v#4U#4U#qV#JfbF1.Lw#Rm#C8.01bF2acd#.E.M0#f##f##Nc#kV#Zy#H8#H8#H8#H8#Hb#1#bF3.9e.Ph.Ph.Pi.Pi.Pi.Pi.Pi.Ph.RC#c2.Qs.WG.3C.Tr#nX.8B.8x#ko.00.9n.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn#cn#bvbF4#IP.9n.9n#c9.4s#aH.8B##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#th#ft#jh.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.9n.Pc.ZW#.e#.e#.e#.e#.e.0U.Tk#bi#aB#pyatv.Og.PQbDP.JY#61#6KbF5bF6bF7bF8bF9bG.bG#bGabGb.Sh.RrbDZbD0bD1bD2bD3bD4bD6bD7bGcbGdbGebGfbGgbGgbGgbE#bE#bGhbGibEabGjbGkbEebGlbEebEfbEfbEgbEgbGmbEh.0hbEkbGnbEjbElbEmbEnbGobEpbGpafoaZCbGqbGrbGqato.ZabEt#TTbGsbGtbGubGvbGwbEAbEAbEzbGxbGybGzbGAbGlbGlbGBbGCbGDbGEbGEbGEbGFbGGbGFbGEbGHbGIbGAbGJbGKbGLbGMbGNbGObGObGObGNbGNbGMbGPbGPbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbEGbGQbGRbEQbEQbGSbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbGTbE0bEWbEVbEVbEVbEVbEVbEVbEVbE0bEPbEPbEPbEPbEPbEPbEPbEPbEPbEXbE0bEVbEVbEV",
-"#Fd#Fd#FdbGUbGVbGWbGX#zpbGY#xubGZbCe#BO#E7#ba#cR#cR#cR#cR#cR#cR#ba#yK#yKbG0bG1bG2bG3bG4bG5bG6bui#a3#a3#a3aL1aL1.7T.9U.7W#mnagaaPhao5atKbG7bG8aQ9a7da7daR2aR2aF1bG9bH.bH#bHabHbbHcbHdbHebHf.5ebHgaDabHhbHibHjbHkbHl#sj#sj#HQ#rr#HQ#HQ#HQ#HQ#rr#rrbHm#tXbHnbHobFFbFFavlbHpbHqbHrbHs##p.7X##p##p##qbtAbHtbxHa.b#cQ#cPbHubHvbHwbHxbHy#89bHzbHAbHB.QnaoR#fU#iu#Cl#qR#q.azbbHCaKf.Qa.X8bHD#Ij#ga#2v#4U#4U#JfbF1bj4br.bHEbHFbjO#tibHG#Ee.OW.4e#f##ge#gb#U5#Qm#Hb#H8#H8#H8#Z.#jm.Kt.3P.RC.Pi.Pi.Pi.Pi.Pi.RC.Pk#q2.NE.4R.L2.Kc#xc.8p.PT.NC.XF.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn#.d#pm.RD.3M#.c#.Z#j2.8a.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.Qr#ft#es.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.4r#.a#.e#.e#.e#.e.0U.Tk#bi#aB#pyatv.Og.PQbDP.JY#61bHHbHI#4FbHJbHKbHLbHM.Uc.VjbHN.ShacFbDZbD0bD1bD2bD3bD4bD6bD6atC#FKayKbHObHPbGgbGgbE#bE.bHQbEIbHRbHSbHTbHTbGlbEebEfbEfbEgbEgbEgbGmbHUbHV#RYbEpbGobGobEobEp#0Y.T1aZCaZCbGqbGqamManW.Za.QcbEsbGubHWbGwbHXbELbHYbHZbH0bH1bH1bH1bH2bH3bH4bH5bH6bH6bH7bH7bH7bH7bH7bH7bH7bH7bH7bH7bH8bH9bI.bI#bIabIbbEfbGybGLbEzbGObIcbGPbIdbEGbEGbEGbEGbEGbEGbEMbEMbEGbEGbEGbEGbEGbEGbEGbENbIebEQbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbEYbEVbEVbEVbEVbEVbEVbEVbEVbEVbEUbEPbEPbEPbEPbEYbEXbEXbEUbE0bEVbEVbEVbEVbIf",
-"#Fd#z5#CsaE6bIg.56#uS#s0.56bIhbIi#E7#ba#cR#cR#cR#cR#cR#cR#ba#yK#yK#yLbIjbIkbIlbImbIn#sa#tK.1LaFQansaPh#qx#pQ.9U.6B.6B.6B.6B.6B.9U.9U.9U.7TaL1aL1aL1aL1aL1aL1aJ7#tIabcaqwaIa.1N#rj#1NaJP#4fbIobIpbIqbIr#rs#Zm#0s#sk#j##j##j##j##j##j##j##j##ifbIs#w0bItbIubFFavlbIubIvbIwbIxbxz.7W.7X##pa#BbCFbujbIyaYgatIazx#b1bIzbIAbIBbIC#9.#9.#lJ#QQbID.R2.UP#geakv#qR#qR#2vbFWbIEaoL.PY.OSbIF#JhagR#2v#2v#7TbIG#VLbsDbIHbIIbIJ#hG.NEbIK#kW#CK#HK.4e#f##ge#ge#Yk#AIahG#H8#H8#Hb#d3#1T.9j.Pz.Ph.Pi.Pi.Pi.Ph.Pz.QA.6X.Oq.RF.NK.TC.6R#y7.Pw.Pe.Qy.8B.8B##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn##P#.c.2s.VC.Pr.VC.Op.8w.XF##Q.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#xD.2n#nT.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#br#dd.Tk#.e#.e.0U.Tk#bi#aB#pyatv.Og.PQbDP#1ZbILbIM#ORbINbIObIPbIQ.VdbIR.VjbGb.ShacFbDZbD0bD1bD2bD3bD3bISbIT#FKbIUbIUbIUbIVbIWbHYbHYbIXbIYbIYbIZbI0bHTbHTbGlbEebEfbEfbEgbEgbEgbGmbHUbI1bI1bI1bI2bGJ#i.#e9aqe.T1abTaZCbGqbGq.Sa.SabGr.IVbI3bGwbGjbGwbI4.WxbI5.ZkbI6bI7bH1bH2bH3bI8bH5bH6bH9bH9bH9bH9bH9bH9bH9bH9bH9bH9bI9bJ.bJ#bJabJbbH8bH7bH6bH6bH8bI.bJcbJdbGJbGKbGPbGPbENbEGbEGbEGbEGbGPbJebJfbEMbEGbEGbEGbEGbEGbJgbJhbJibETbETbETbETbETbETbETbETbETbJjbJkbJkbJkbJkbJkbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbESbE0bEVbEVbJlbJmbJmbJmbJmbJmbJnbEXbEYbEXbEUbE0bE0bEVbEVbEVbEVbEVbEVbEVbEVbJo",
-"#z5aO0aPXbJp#uW#s0#DpbD##yM#yK#yK#Yu#cR#cR#cR#cR#cR#ba#yK#yK#yL#yHbJqbJrbJsbJt#cE#cD.2V.2U.1L#.z#.z.2U.2UaeRbG8.9U.9U.6B.7X.6B.6B.6B.6B.6B.6B.6B.6B.7X.9U.6BansaPg#o6#o6.1L#1O#1ObJubJvbJwbJx#3a#Zm#0s#sk#j##j##j##j##j##j##j##j##j##if#s8#sk#u6#t3bJy#u1#vQbJzbJAbJBbJCbJD.7X#lAbJEbJFbJGbANby7#bbajBbAObJHbJIbJJbJKbJLbJM#Fcay1bJNbJO.MQ#E2#qR#qR#qR#qR#9I#KwbJP.M9.OQ.O1bJQ#Nc#Cl#2v#4UbJR#UPbJSbJT#B##AAbJUbJVbJW.NVapj#.E.OW#CK#gd#f##ge#ge#e7aac#H8#H8#H8#lTbJX.ND.NJ.RC.Pi.Pi.Pi.RC.Ph.6W.Pe.NT.NM#gu#un.Kt.L2.99.NV.1f.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B.Pc##P.Uq.Tl#ui.5N.QC.QD#.R.1f.5B.Tl#bw.8B##M.8B.8B.8B.5B.8B#cn#w8#w8.8B.5B#w8##P.8B.5B.8B.5B.9n#vn#er#DA.5B.8A.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.5B.5B#fe#DA#er.Tk.Tk#bi#aB#pyatv.Og.KE.PK.MobILbJYbHIbJZbJ0#dR.X#.Vd.Ud.VjbJ1.Sh.RrbDZbD0bD1bD2bD3bISbD1bJ2bJ3#FKbIUaI0bJ4avcbJ5#TWbJ6bIYbIYbIZbI0bHTbHTbGlbEebJ7bEgbGzbEhbJ8bJ9bK.bGLbK#bKabKbaZCbKc#e9afoafoabTaZCbGqbGrbKdbKebKfbKfbKgbKhbGwbKibKj.Wv.Zk.VkbKkbI7bH1bH2bKlbI8bH5bH8bH9bH9bH9bH9bH9bH9bH8bH8bH8bH8bKmbKnbKobKpbKqbH9bH9bH9bH9bH9bH8bKrbKsbH8bKtbKubGAbKvbEFbEFbEFbEGbIdbKwbKxbGPbGPbEMbEGbEGbEGbKybKzbEGbKAbETbETbETbETbETbETbETbETbKBbKCbKD.DcbKCbKCbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbETbEXbEVbEWbEVbKEbKFbKG.gybKHbKF.orbE0bE0bEVbEVbEVbEVbEVbEVbEVbEVbEVbEVbEVbEVbJo",
-"#TMbhc#Qd#wn#xu#y.bKI#cR#yK#ba#cR#cR#cR#cR#cR#cR#zs#yK#zrbKJbKKbKLbKMbKNbKObzx#cD.2V.2U.2U.2U.2U.1L.1L.1L#.z.1L#tJbKP.7W.9U.7X.6B.6B.6B.6B.6B.7X.6B.7TaPHakH#.z#.z#cD#rm.1LbKQbKRbKS#3a#3a#si#sk#j##j##j##j##j##j##j##j##j##j##if#kMbKTbKUbKVbKW#vQ#vQbKXbKYbKZbK0bK1bK2.7XbJE##qbK3bK4#BS#8I#cQ#cPbK5bK6bK7bK8bK9bJM#QQ#Fc#lJ#7GbL.#ak#2v#VM#qR#qR#2v#ga#3gbL#.OQ.R..R#bLa#UM#Cl#4U#Jf#4UbLbbLc#46#zt#cR#7nbLdbLe.Vw#Fn#j9#CK#HK.UO#ge#f##f##sM#f#ae6#Hb#H8#QmbF2.Kt.2r.Ph.Ph.Pi.Ph.WH.Py#fc.WG.Pf.Pk#hFbLf.NK.Uw.8b.NV##V.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn.8a#nX#cg.RD.QB.Sy.QDbLg#j6#p7#.b#.c.8B##M.8B.8B.5B.8B#w8.8B.4s#nX#iDam7#Gjayw#y3.8B.8B.5B.9n#fw#aq.RS.8B.8A##M.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8A.5B.4r##L.Oo#ddbLhbLiad8.QR.5D.Z0.8r#61bHHbLjbLkbLl.AR.X#.V9.UcbGabLm.Sg.U1bLnbD0bD1bD3bISbD4#CmbLobJ3#FKbLpbJ4bEmbLqbLrbLsbLtbLubIZbIZbI0bHSbEebEebLvbGJbI2bJ9bGLbGLbGLbK#bKabKbbGrbKcbLwbKcalMafoafoabTaZCbLx.IVbLybKfbLzbI3bEGayK.YAbLA#qL#p1.Zk.ZkbI6bH1bI8bLBbLCbGFbGEbIbbLDbLDbLDbGIbIbbLEbKubGGbI#bKtbLFbLGbH8bH8bH9bLHbLIbLJbKpbLKbH9bKnbLLbH9bLMbLNbLObLPbGGbLQbLRbLSbLTbKwbLUbLVbLWbLXbIdbENbEGbKybLYbLZbL0bL1bL2bL3bETbEXbL4bL5bETbETbETbL6bL7bETbETaEdbL8bETbL9bM.bEXbM#bMabMbbETbMcbMdbMebMfbETbMgbMhbMibMjbMkbEVbEVbEVbEVbMl.gy.tfbEVbIfbM.bMmbMnbMobMpbMqbMrbMsbMtbEVbEVbEVbEVbEVbJo",
-"bMu#s0#xu.56bIh#Ay#Az#ba#cR#cR#cR#cR#cR#cR#ba#Az#AybMv#uUbMwbMxbMybMzbzx#cD.2V.2V.2V.2U.2U.2U.2U.2U.2U.2U.2U.1L#o6#.zbMAaPh.9U.6B.6B.6B.6B.7X.6B.6BbMBaeQ#o6.1L#1O#qzbMCbMD#if#Zm#si#sk#j##j##j##j##j##j##j##j##j##if#sr#s8#s7#s8#F8bME#u1bMFbMGbMHbMIbMJbMKbMLbMMbJFbui##qbMNbMObMPbMQatIbAO#b1bMRbMSbMT#QR#9.#z5#Fd#lJ#PO#20#2v#hv#qR#qR#2v#4U#2v#4U#JhbMUbMV.X8.QfbMW#Zw#Nc#Jf#JfbMXbMY#BO#B##cR#Cw#g7bMZbM0.Oo.Pdbpb#Z5#HK.Q0.M0#f##f##f##Uzaad#Hb#Hb#H8#YkbM1#c1#fc.RC.RC.RC.RC.Pf.WG#c4.Pl.Pz.L1.Pm.Pz.Pz.3C.Pe.Oq#co.8B##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn#bw.Nu#cb.QB.QD.RD.Tp.QD.Tpam8.8B.9r#.c.8B#cn#br##P#cn#w8.Uq.TC#c2.L0.Pz#cc.Pz.WH.SA.Ph.4Q#hM.5B.4r#xF#cp.8B.9n.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.5B#.Z.0Z#.c.TE#fubbc#AJ#iMbM2bM3#61bJYbM4#xxbM5bM6.XpbM7bM8.T4bLm.Sf.U1.ScbD0bD1bM9bD2aw5bF1bLobJ3bIUbJ4bN.bN#bNabLrbNbbNcbNdbIZbIXbEeazqbGJbI2bNebNebKbbGLbGLbGLbK#bKbbGrbLwbNfbNgbNhbNi#e9aqe.V5bNjbNkbNlbNmbNmbNnbNobNpbNqa3qbNqbNr.Wv#p1.Zk.VkbNsbNtbNubI4bI4bGIbHSbGlbGlbGlbGlbGlbGlbGlbNvbNvbGlbGlbNwbNxbNybNzbH9bNAbNBbNCbJcbNDbNEbKnbLLbH9bNFbNGbNAbNHbNIbKmbNJbNKbNLbNMbNDbNNbNObNPbGPbGPbEMbKybNQbNRbNSbNTbNUbNVbGTbNWbNXbETbETbETbETbL6bL7bETbETbMbbNYbETbNZbN0bN1bN2Qtm.zsbEXbN3bN4bMg.iCbN5bN6bN7bN8bN9bO.bEVbEVbEVbEVbMl.gy.tfbEVbEVbO#bOabObbN9bOabOcbM#bOdbOebOfbEVbEVbEVbEVbJo",
-"#xu#s0#8zbOg#yK#zs#cR#cR#cR#cR#cR#cR#ba#yK#AAbKI#y.a0hbOhbOibOjbzx#cD.2V.2U.2U.2V.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L#o6axvasd##p.6B.6B.6B##p.7WaqN#o6#.z#cD#0xbOkbOl#if#3a#sk#j##j##j##j##j##j##j##j##j##j##j##s6#ifbOm#u6bOnbOobOpavmbDsbOqbOr#wZb.9bOsbOtbvqbDw##qbOubOvbOwbMQatIavoa2vbOxbOybOzbOA#80#z5#Fd#Fc#lJbOBbOCabV#Zw#pj#qR#2v#ga#2v#ga#f##NcbOD.O1.O1bOE#55#sM#JfbOFbOG#Ch#f2#qH#z4bOH#QN#FcbOIbOJ.Vy.OibOK#AI.OW.Q0#fU#f##f##f##Jh#AI#H8#H8#lT#e4#Fr.2#.JZ.Pk.L2.9A.Sz.WG.Pp.RC.Ph.Pi.Ph.Ph.Ph.Pi#ti.NV##O.8B##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B.8B.00.5N.QB.RD.Tp.Tp.Tp.QD.Tp#yl.0Y.4s#bw.8B#w8.QO.Mj.8l.QO.Mr#qh.Pz#cc#pl.Pi.Pi.Pi.Pi.Pi.Ph.8f#iD#xD.YV#hD.5B.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n#.c#..#Ml.PR#AJ#iMbOLaaHbILae8bOMbONbOO#jd.4j.Ve.Uf.LAbOP#oi.S5bLnbD0.Hd.G2.6bbF1bF1bLobOQ#FKbEkbORbORbNabLrbNbbNcbOSbHYbOTbKvbOUbGKbGKbI2bI2bJ9bK.bOVbKbbEvbNg#SV#SVbNgbOWaqe#2ca9uaHpbNkbOXbOYbNl#BBbNpbOZbO0bdua3qbO1a6abNr.Wv.Wv.U2bO2bO3bNubNubNubI4bGIbGIbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbIbbO4bO5bO6bO7bO8bO9bH9bH8bP.bP#bKnbLLbH9bPabNAbPbbPcbPdbKpbPebPfbPgbPhbKybGIbPibPjbGPbGPbGPbPkbPlbEGbPmbPjbEJbPnbMhbN0bPobETbETbETbETbL6bL7bETbETbPp.#mbPqbPrbPsbPtbPubPvbPwbPx.fKbETbETbPy.eYbPzbObbEVbPA.#jbEVbEVbEVbEVbMl.gy.tfbEVbEVbPBbMrbPCbPDbOa.#mbIfbEVbPEbPFbEVbEVbEVbE0bPp",
-"#Iu#x9#BO#yK#ba#cR#cR#cR#cR#cR#cR#ba#Az#yL#Ena0h#yGbPGbPHbPIbPJaDa#qz.2U.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1Lac4#tJ##p.6B.6B.6B.9UbG8#uO.1L#cD#0xbPKbPL#Zm#sk#j##j##j##j##j##j##j##j##j##j##j##if#w1bPMbPNbPObIubIu#1hbFFbPPbPQa9Ya7ObuGaa1bPRbPSbPTbBKbPUbPVaYgatIavo#cPbPWbPXbPYbPZbP0#7E#Fd#Fd#Fc#lJbP1bP2abV#Zw#qR#qR#qR#ga#2v#qR#gabP3bP4.X8.R9bP5bP6#uIbP7bP8bvx#B.#sm#pX#2M#z5#z1#z5#z5bP9bQ.#hf#AJbQ##AI#HK.Q0.4e#f##f##f##gb#pj#Hb#H8#4T#hubpc.NU.Oq.Mi.Qs.NU.52.LS.RC.Ph.Pi.Ph.Pi.Ph.Pzam7.Oq.LO.8a.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn#bw.Mi.Sy.QD.Tp.Tp.Tp.Tp.QD.QG.VL.ZW#.V#fu#cn#br#uj.NJ.Ph.Ph.Py.NJ.QA.NO.Sw.Pl.Ph.Ph.Pi.Pi.Pi.Ph.Pz.NS.VyaPu.Ps#bw#bs.8B.8B#cn#w8#w8#w8#cn.8B.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.Uq#fu#...QP#AJ#iMbQa#1Zae8bHHab7bQbbQcbM6.Vb.VfbQd.TabQebQf#OJbQgbD0bQh#oibF1bF1bLobOQ#K1bQibQjbQkbQlbNabLrbQmbNcbhNazqbOUbKvbGJbGJbGybI2bJ9bK.bKbbEvbQn#GVa5h#SVbNgaqX#GC.JEbQobQobNk#mtbQp.2QbQq.YZbQrbQsbO0#LRa3qbO1a6abNr#qL.WwbO2bQtbNtbO3bNubNubI4bGIbGIbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbIbbGlbGlbL0bQubQvbQwbLDbKubQxbQybQzbLLbH9bQAbQBbLKbQCbPdbH6bP#bKmbQDbPhbQEbH7bQFbQGbGEbGAbOUbPkbQHbEMbQIbQJbEGbQKbQL.labQMbETbETbETbETbL6bL7bETbETbETbQNbQObQPbGTbETbQQbQRbL7bQSbQTbGTbETbQUbQVbJkbQWbEWbQX.dBbEVbEVbEVbEVbMl.gy.tfbEVbQYbQZ.zlbQ0bPDbOabNVbEVbEVbQ1bPFbEVbE0bEUbEYbEW",
-"bOg#yK#ba#cR#cR#cR#zt#AA#cR#cR#yK#cRbQ2#GPa14bQ3bQ4bQ5bQ6bQ7bQ8bQ9bR.#1O#cD.2V.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L#tKaP9aPH.6B.6B.6B.6B.9UakHbR#.1L#rmbRabRb#3a#j##j##j##j##j##j##j##j##j##j##j##j##if#s6bRcbRdbCA#u1bRebRfbRgbRh#s6bRi#rx#ruaBHbRjbRkbRlbRmbRnaYg#7majB#cPbRobRpbRqbRrbRs#80#z5#Fd#Fd#Fc#FbbRt#q.#MU#qR#qR#qR#qR#2v#ga#qR#2v#IjbRubRvaFFbRwbRxbRybRzbrc#B.#rv#Dw#h##JE#JE#z5#Fd#Fd#FdbRAbRBbRC.KradNbrV#HK.Q0#e4#f##f##f##sM#ge#4SahG#ga#e4bRD#ZD#rN.YJ.RG#gp.Pi.RC.Pi.Pi.Pi.Ph.Pi.RC.Pm.Pw.NV.PL.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B#cn#.b.4Q.QB.RD.Tp.Tp.Tp.Tp.QC#gu#xa.3A#mi.8x#cn#.c#q0#pl.Pz.3B.Sw.Pr.RD.Tp.Tp.Tp.QC.Sw.Ph.Ph.Pi.RC.RC.L0.L4.1n.L2.Py#de.4r#br.Uq##W.Vw.Mw#y3#bt#bu#br#w8.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.Uq#fu#...QP.Z0bRE#WF#1ZbIL#6KbRFbRGbRHbRI.VcbRJbRK.S9.SibQf.Rs.Iu#oiaw5#A5an.bJ3bRLbRMbRNbRObRPbRQ#PUbNabRRbNbbRS#HLbEfbKvbGJbGJbGJbI2bJ9bNebRTbRUbRVbRW#GVbNf#nD.W8.V4.V4.JEbQo#dpa9u#jLbRXbRYbRZbR0bR1bR2bO0bR3a3qbO1bNqbNr#qLbLmbR4bR4bNtbO3bNubNubI4bGIbGIbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbQubJ.bNJbR5.CnbR6.CtbR7bR8bR9bS.bS#bSabSbbScbSdbSebSfbPdbSgbShbNSbSibPhbSjbH9bQFbNxbH6bH8bI.bSkbQwbSlbSmbSnbEFbGlbPEbSobETbETbETbETbETbL6bL7bETbETbETbSpbSqbSrbETbPsbSsbSt.labGT.A6bM#bSu.fKbN5bM#bQWbEVbQX.dBbEVbEVbEVbEVbMl.gy.tfbEVbMtbN3bSvaHNbPDbOabNVbEVbEWbQ1bSwbEXbEYbEPbEPbEW",
-"#zs#ba#cR#cR#cR#BR#z3#sl#sl#TKbSx#En#wn#QdbSy#zZ#kM#ih#0s#Zm#Zm#3abPLbSz#qz#cD.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L#o6bxv#tI.6B.7W.6B.6B.6B.9UarlbSA#qzbSBbSC#3a#j##j##j##j##j##j##j##j##j##j##j##j##ifbSDbSEbRfbCAbSFbKWbSGbSH#tW#s7#if#z1#qH#ib#VEa#DbSIbSJ#VEa.bajB#cPbSKbSLbSMbSNbSO#80#7E#Fd#Fd#Fd#Fd#lJbSPalN#VN#qR#qR#qR#qR#2v#2v#qR#2v#gdbSQbSRbSSbSTbSUbg8#N4#pY#RO#Ex#h##JE#z5#z5#Fd#Fd#Fd#Fd#Fd#z5bSVbSWbSX#H8.X2.UN.Q0.6q#f##f##f##ge#gb#ht#Pt#gd#pj#Pd.Ph.Pz.Pz.L1.QC.Pl.Ph.Pi.Ph.Pi.Pi.Ph.RC.6S.ND.Pe.XF.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.8B.8B.PT#gu.Tn.Tp.Tp.Tp.Tp.Tp.Ps#cg.RO.9h.YU#qT#br#ct#.Q.8f.Ph.Sw.QB.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.Sw.XI.Pz.Pv#nX#.dam7.RC.RC.Pk.TlasS.4Q.NQ.L1.Ph.RC.Ph.L0.Po#q3#v1#bu#w8.8B.5B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.9n.Uq#fu#...PR#KS.5D#SAbM3#3wbSY.WQbSZbS0bRIbS1bS2.U5.T4bS3bS4bF1##e#Kb.8YbS5#A5bJ5bS6bS7bS8bS9bT.bT.bORbT#bNabTabTbbIZazqbOUbGJbTcbI2bGKbTdbTebTf#RTbRVbTgbThaq4.W8#GC.V4bTibTiabTaBsbTjbTkbTkbTlbTmbR0bR1bTnbTobTpbTqbTqbNr#pc.T.##ebR4bR4bNtbO3bNubNubI4bGIbGIbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbTrbTsbTtbTubIbbR6bTvbTwbTxbTybGlbTzbTAbTB.EzbLRbLLbTCbI9bNKbTDbTEbTFbNFbJcbH9.m1bLJbH9bH9bH9bR8bTGbSnbTHbI.bTIbEMbPkbTJbERbETbETbETbETbSpbPobETbETbETbGT.#mbTKbETbTLbTMbTNbTObETbTPbTQbTRbSvbEVbTSbO#bEV.tfbKGbEVbEVbEVbEVbTPbN9bMobEWbTTbTUbTNbPsbTVbTWbTXbEXbEYbNZbTYbEPbEPbEPbEPbEW",
-"#cR#b2#cR#ba#Ew#lJ#z1#JE#SS#FbbTZbT0bT1bT2#w2#lK#xw#j##sk#j##j##sk#0s#rtbT3#1N.1L.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U#.zaxvafU.9U.7X.6B.6B.6B.6B.9UbT4#o6#0xbT5bT6#if#j##j##j##j##j##j##j##j##j##j##j##lKbIsbT7bT8bMGbT9bU.#u6biD#sr#kM#rx#g8#zt#cRajBbU#bUa#BSbUbajB#cPbcUbvXbUcbUdbUebUf#80#z5#Fd#Fd#Fd#Fd#Fda0fbLq#qV#Zw#qR#qR#qR#qR#qR#qR#qR#DNbUgbUhbUibUjbUkbUl#B##ba#Ev#QN#z5#z5#z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#FdbUmbUn.4vaPo#geai2.Q0#e4#f##f##f##ge#ge#ge#ge#e6#Fr.Oq.Sz.9e.99#ti.NK.RC.Ph.Pi.Ph.Pi.Pi.RC.Tr.Sv.NV.NV#de.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B.Pc.9n#e..QD.Sy.Tp.Tp.Tp.Tp.RD.Pt#zLatv#yg.TE#.b#w8#.d.Pp.4z.LS.Pf.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tn.Ph#gu.8p.0Y.Ks.XI.Ph.Ph.NH.NQ.Ph.QF.RC.Ph.Ph.Ph.Ph.Ph.RC#cc.RC.6T#y7#bw#w8.8B.5B.8B.8B.8B.8B.8B.8B.8B.5B.9n.Uq#fu#...PRaofbIL#1Z#9Lae8#ORbLjbUobUpbjc.X#bUqbUrbUsbUtbUubJ3bJ2aw5#gd#M4bRQa4sbUvbUwbUxbUybUzbUAbNabUBbUCah7bIXbHYbOTbKvbGybI2bUDbUEbUFbUGbUHbUIbUJbUKarVaq4#GCaoe.W8#RWaD5bULbUMbUNbUObUPbUQbURbUSaoUbaVaHP#MT.71.71bJRbUT##ebO2bUUbR4bNtbO3bNubNubGEbI4bGIbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbGlbGIbGlbGlbNvbR6bUVbGlbGlbGlbGlbGlbGlbNvbGlbIabI.bH8bH9bH9bH9bH9bH9bH9bH9bH9bH9bH9bH9bH9bH9bH9bH9bH9bH7bH7bH9bUWbPjbUXbEQbEXbERbETbETbETbETbETbETbETbN5bUYbETbETbETbETbETbETbGTbEXbE0bEVbEVbEWbEVbEVbE0bEXbEXbEXbEXbEXbEXbEXbEXbEXbEXbEYbEYbEYbEYbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEW",
-"#b2#cR#qH#ha#z1#z5#Fd#Fd#TM#FbbUZ#QdbT2#sr#if#j##j##j##j##if#sk#j##j##3abU0bU1#cD.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U#tKaeR#pQ.7X.6B.6B.6B.6B.6B##p#mnaIaab#bU2#if#sk#j##j##j##j##j##j##j##j##j##ifbIs#HQbU3bU4bU5aNX#vU#s6#sr#lK#lK#SR#g7#f1#cR#b2#BSbU6#bbavo#cPbU7bU8bU9#ePbV.bV#bVabVa#Fd#Fd#Fd#Fd#Fd#Fd#jYbVbbVc#MU#qR#2v#qR#qR#qR#qR#qR#ETbVdbVebVfbVgbVha0L#zt#nt#Ex#Fc#z1#z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#FdbVibVjbVkbVl#1R.5a#HK.4e#f##f##f##f##f##ge#gd#ZF#rN.NE.NE.ND.52.Pm.RC.Ph.Pi.Pi.Pi.Pi.Ph.RC#.N.ND.Pe.Qs#.c##N.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B#cn##Q.Kt.QB.RD.Tp.Tp.Tp.Tp.RD.PtbVmaoi#LM.YH.00#cn#.c.QD#cc.NM.RF.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QB#XK.0Z#vn.2s.RC.XI.Pi.Pi.Ph.RC.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Pg.Pz.NQ.2y.9n#cn.5B.8B.8B.8B.8B.8B.8B.5B.9n.Uq#fu#Ml#AJ.93.KE#kRbVnbVobVpbVqbVrbVsbVtbVubVvbVwbVxbqnbVybVzbVAbVBbVCaz3bVDbVEbVFbFvbFwbVG#QVbVH#RWbHRbEdbEdbHRbOTbGJbGJbVIbVJbVK#IFbVKbVLbVM#IEbVNbVNarVaFk#nDbVObVPbVQbVRbVSbVTbUObVUbVVau.aN4aPabsbbaV#sz#MT.71.6FbVW##ebVXbR4ayK#MI#NHbVYbIWbVZbVZbHYbHYbHYbGAbNvbGlbGlbGlbGlbGlbGlbGlbIbbGGbH9bI.bV0bGEbV1bV2bGlbGlbGlbGlbGlbGlbGlbGlbNvbGlbGGbH9bH8bH9bH9bH9bH9bH9bH9bH9bH9bH9bH8bH9bH9bH9bH9bH9bH9bH9bV3bTwbKtbV4bIfbE0bEXbERbESbETbETbETbETbMfbNWbETbETbETbESbERbEXbE0bEVbEVbEWbEVbEVbEVbE0bEYbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEW",
-"#cR#sm#qJ#TM#z5#Fd#Fd#z5aO0bhc.7rbV5#w2#if#j##j##j##if#1Jbl6#sq#1J#j##sk#rtbV6#1N.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U#.z.0E#qx.9U.7X.6B.6B.6B.6B.6B.9UahvbV7bV8#6j#sk#j##j##j##j##j##j##j##if#s8bV9bW.bW#bWabWb#j##s8#lK#if#ig#yQ#ha#zt#cR#BS#cR#cR#cRajB#cPbWcaA4btSbWdbWebWfbWgbJL#QR#Fd#Fd#Fd#Fd#Fd#Fd#Gb#QMbWhabV#Zw#qR#qR#qR#qR#qR#qR#xmalNbWibWjbWkbWla7y#cR#RO#ha#z5#z5#PN#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#z5bSVbWmbWn#Y..ON#HK.4e#f##f##f##f##f##f##e4bWo.Om.2r.4R##A.RC#pl.Pi.Pi.Pi.Pi.Pi.Pi.RC.Py.WG.Oq.Pe.Oi.9n.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.8B.5B#cn#bt.4K.QB.RD.Tp.Tp.Tp.Tp.RD.PtbWp.Od#LM.2C.RR.5B.5B.7n.Ph.Pp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.RD.QBbWq.5B#fw#y3.LZ.RC.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.RC#cc#.R.QO#br.5B.8B.8B.8B.8B.8B.5B.9n.Uq#fu.QP.QP.RO#Usa9TbWrar1.KqbWsbWtbWubWvbWwbWxbWybWzbWAbWBbWCbSP#PRbWDbWEbWFbWGbWGbFvbWHbWIbRS.ZabHRbWJbWKbEdbGJbWL#TTbWMbWNbWObWNbWPbWQbWRbWSbWSbVNbWTbWUbWVbWWbWXbWYbWZbW0bW1bUMbW2bW3bW4#6x#q.agR#sM#hX.TKagQ.ULatCbRTbEwbW5bW5.JgbW6bW7bGPbGPbGPbGPbGPbGPbGPbW8bGKbOUbKvbOTbGzbGAbGAbGlbGGbH7bH8bH9bH8bH8bI.bV0bIbbGlbNvbNvbGlbGlbGlbGlbGlbGlbNvbLEbKtbH8bH9bH9bH9bH9bH9bH9bH9bH9bW9bH9bH6bH8bH9bH9bH9bH9bH9bH9bH8bH6bX.bX#bE0bEVbE0bEXbEQbGTbESbESbESbESbEQbEXbE0bEVbEVbEVbEVbEVbEVbEVbEVbEUbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEW",
-"#rv#h##z1#z5#Fd#Fd#Fd#TMbXabXbbOo#s6#if#j##j##j##j##ifbXcbXdbJxbXc#j##sk#0sbXe#rm.2U.2U.2U.2U.2U.2U.2U.2U.2U.2U.1L#tKaFQ#uM.6B.6B.6B.6B.7W.7W.7W.9UbXfbXgbXh#si#j##j##j##j##j##j##if#skbXibXjbXkbXlbXmbXn#if#j##if#lK#JD#z4#f1#cR#BS#cR#cR#cQ#b1bWcbXobU9bWdbw6aATbXpbXq#81#TL#Fd#Fd#Fd#Fd#Fd#Fd#z5#F8#sobn2#2v#4U#qR#qR#qR#qR#qR#2v#gabXrbXsbXtbXua8g#ba#g7#RN#z1#z5#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#Fd#FdbXvbXw#tobXx#WW.M0#f.#f##f##f##f##f##ge#e6#tl.Pd.WF.Pz.RC.Ph.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pg.RF.Oq.NV.NV.NE.Uq.8B.8B.5B.5B.5B.8B.8B.8B.8B.8B.8B.8B.8B#gh.1q.QC.Tp.Tp.Tp.Tp.Tp.RD.QBbXy#WG.9h##K#p6##Q.8B.52.L4.QB.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tp.Tn#q0.Uq##P.YM.Pm.Pg.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Pi.Ph.Pz.Py#vl.8B.8B.8B.8B.8B.8B.5B.9n.Uq#fu#Ml.QP#ch#ej#.PbM3#Tn.N6bXzbXAbXBbXCbXDbXEbXFbXGbXHbXIbXJbXKbXLbXMbXNbWFbXObWGbXPbXQbXR#HMbXSbXSbWJbWJasXbVJbXTbXUbXUbXVbWNbXWbXXbXYbXZbXZbUKbX0bX1bX2bX3bX4bX5bX6bX7bX8bX9bY.bY##TV.1B.QcbGqbGrbErbYabYbbYcbYcbYdbGubW6bW6bW6bExbGvbEybEybELbELbELbEzbEzbGMbIcbYebEJbGPbGPbGPbGPbGKbOUbGJbGlbI.bH9bH9bH9bH9bH8bH9bKtbV0bGEbGlbGlbGlbGlbGlbGlbGlbNvbGlbI#bH9bH9bH9bH9bH9bH9bH9bH9bGGbGAbIbbI#bH9bH9bH9bH9bH9bH9bH9bH9bH6bYfbEXbEXbEZbEUbEXbEYbEPbEPbEPbEPbEZbEVbEVbEVbEVbEVbEVbEVbEWbE0bEXbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbEPbE0"};
+"aF7 c #324068",
+".tO c #32414b",
+"bhW c #32414e",
+"be3 c #32424f",
+"#zG c #324552",
+"b#A c #324654",
+"cfu c #32495e",
+"awi c #324b5c",
+"coD c #324c66",
+"cnA c #324d66",
+"cjJ c #324d67",
+"cj. c #324e67",
+"cgd c #324f6a",
+"cjt c #32506a",
+"ck2 c #32516b",
+"bin c #32516d",
+"bBf c #325266",
+"btq c #325271",
+"aw4 c #325284",
+"#17 c #32546a",
+"aBD c #325475",
+"bv1 c #325576",
+"a17 c #32558c",
+"cn8 c #325774",
+"bXq c #32587c",
+"cpi c #325977",
+"b4i c #325980",
+"buZ c #32598f",
+"asn c #325b7c",
+"b02 c #325b84",
+"#Ih c #325c7b",
+"azB c #325c8b",
+"a0W c #325d7c",
+"b1Q c #325e7a",
+"bJL c #325e93",
+"b1R c #325f7b",
+"b1A c #32607d",
+"#15 c #326085",
+"bZ2 c #32617e",
+"bZW c #32617f",
+"#TF c #326186",
+"bY# c #326283",
+"bUP c #326488",
+"bUO c #326489",
+"bUN c #32658a",
+"bUM c #32658b",
+"a9f c #326598",
+"bWe c #32668b",
+"c.T c #326890",
+"cah c #326891",
+"ccQ c #326893",
+"ceh c #326994",
+"cec c #326a95",
+"bJe c #326a96",
+"aTx c #326a99",
+"c.b c #326b97",
+"clR c #326b98",
+"bKl c #326da1",
+"crb c #326fa4",
+"b5G c #326fa5",
+"clt c #3270a4",
+"#RV c #3270a5",
+"aoT c #3270a6",
+"bdl c #3270a7",
+"cxM c #3272a7",
+"#ix c #32dddc",
+"bqt c #332434",
+"aV5 c #332535",
+"#pX c #332734",
+"biO c #332f3e",
+"bei c #33313c",
+"bIg c #333354",
+"aTo c #333443",
+".ax c #33353f",
+"aGB c #33363e",
+"btZ c #33363f",
+".q0 c #333641",
+"apt c #333642",
+"#fN c #33373b",
+"atQ c #333946",
+"crq c #333949",
+"#aI c #333a40",
+".GD c #333b41",
+"aO2 c #333d45",
+"#sC c #333d47",
+".c5 c #333e46",
+".c6 c #333e47",
+".fI c #333e48",
+".c4 c #333f47",
+".qS c #333f48",
+"#uK c #333f49",
+"#5R c #333f4a",
+"aQs c #333f68",
+".eN c #334048",
+"#9A c #334049",
+"#XW c #33404a",
+"aPV c #33404b",
+"bpQ c #33404c",
+".J8 c #334149",
+"aR# c #33414b",
+"bTP c #33414d",
+".wv c #33424a",
+"bnJ c #33424e",
+"aBB c #334451",
+"crU c #334d66",
+"asx c #334e83",
+"bvY c #335270",
+"bDy c #33536e",
+"buH c #335471",
+"#qz c #335752",
+"a.e c #33588e",
+"cj8 c #335b79",
+"b0j c #335d78",
+"a7f c #335e93",
+"aAw c #335f7b",
+"b1G c #335f7c",
+"b1X c #33607c",
+"b1Y c #33617d",
+"bYg c #336384",
+"a4h c #336494",
+"bUK c #33668b",
+"bUL c #33678c",
+"c.S c #336a94",
+"cbD c #336a95",
+"caP c #336a96",
+"bSh c #336b96",
+"bZA c #336b97",
+"byW c #336c98",
+"ciT c #336c99",
+"aSJ c #336d9c",
+"a0h c #336e9d",
+"cjN c #336e9e",
+"ccN c #336f9f",
+"cd9 c #336fa0",
+"#Ki c #3370a3",
+"#Ly c #3370a4",
+"cc. c #3370a5",
+"bTB c #337199",
+"#Jc c #3371a3",
+"#Ep c #3371a4",
+"cgq c #3371a5",
+"azA c #3371a6",
+"aA7 c #3372a9",
+"bGM c #3377ac",
+".Rv c #342427",
+"bdT c #342434",
+".GF c #34262d",
+"a04 c #342838",
+".WX c #342a39",
+"bFJ c #342b3a",
+"a3B c #342e3d",
+"axK c #34313f",
+"#Hu c #34323c",
+"ayg c #34323d",
+"ag2 c #343342",
+"aSV c #343343",
+"aIk c #343354",
+"blU c #343442",
+".wx c #343538",
+"aSi c #34363e",
+"aT. c #34363f",
+"aT1 c #34373f",
+"aI. c #343741",
+"#NZ c #343743",
+"apD c #343947",
+"cwO c #343a4c",
+"au5 c #343b45",
+"aDY c #343b5c",
+".DR c #343e45",
+".xJ c #343e46",
+"axW c #343e48",
+".dO c #343f47",
+".eQ c #343f48",
+"amv c #343f49",
+"aoJ c #343f4a",
+".dN c #344048",
+".eM c #344049",
+"#qc c #34404a",
+"#om c #34404b",
+".eJ c #344148",
+".eL c #344149",
+"amy c #34414a",
+"#Ez c #34414b",
+"bih c #34414c",
+"bDA c #34414d",
+".uI c #34424a",
+"big c #34424c",
+"#Wj c #34424d",
+"bTQ c #34424e",
+"#ND c #344655",
+".by c #344855",
+"chk c #34485d",
+"b2p c #344d5e",
+".4d c #344f5a",
+"cji c #344f6a",
+"aaQ c #345078",
+"akv c #345185",
+"bhB c #34526e",
+"bsK c #345472",
+"#qA c #34554e",
+"bjX c #345779",
+"bCl c #34598e",
+"cvc c #345c81",
+"bzt c #345c85",
+"#JI c #345d7c",
+"bNm c #345f94",
+"b1W c #34617d",
+"b1F c #34617e",
+"bZd c #346196",
+"b1C c #34627f",
+"#OX c #346388",
+"#2E c #346489",
+"#WP c #346589",
+"aq# c #34668d",
+"aEe c #34669b",
+"bUJ c #34688e",
+"bUG c #34688f",
+"bHD c #346891",
+"bUI c #34698f",
+"bRK c #346990",
+"bQo c #346991",
+"bWd c #346a90",
+"bTj c #346a91",
+"bTf c #346b92",
+"bXN c #346b94",
+"cag c #346b95",
+"c.R c #346b96",
+"cbC c #346c97",
+"ciR c #346c98",
+"c#x c #346d9b",
+"bz4 c #346e9e",
+"cmB c #346f9c",
+"cbs c #3471a0",
+"#6p c #3472a6",
+"bLx c #3473a6",
+"bDC c #3473a7",
+"c.i c #3473a8",
+"#e# c #351c27",
+".Oh c #352434",
+"a4H c #352435",
+"bor c #352636",
+".PT c #35282f",
+"bo5 c #352939",
+"a7B c #352e3d",
+"aS. c #35303f",
+"axb c #353140",
+"#Mz c #35323c",
+".t7 c #35333a",
+"bsk c #353342",
+"bF6 c #35343e",
+".xB c #353546",
+"a21 c #353753",
+".FN c #353944",
+"aut c #353a44",
+".AZ c #353d44",
+".Sm c #353d49",
+"aSE c #353d65",
+".B1 c #353f45",
+"aF# c #353f68",
+".Ds c #354048",
+".fH c #354049",
+"#8C c #35404a",
+"bJr c #35404b",
+"aKy c #354058",
+".z8 c #354147",
+".eK c #354148",
+".eI c #354149",
+".gF c #35414a",
+".Qf c #35414b",
+"#2X c #35414c",
+"bif c #35414d",
+".gG c #354249",
+".fD c #35424a",
+"a.Q c #35424b",
+"#lg c #35424c",
+"#US c #35424d",
+"b7h c #354264",
+"aGo c #35434c",
+"aq7 c #35434d",
+".uE c #35444e",
+"#S6 c #354857",
+"bdp c #354956",
+"alh c #354a59",
+"ci2 c #354a60",
+"b5O c #354e5f",
+"cq3 c #354f69",
+"cny c #35506a",
+"cey c #35526c",
+"agw c #355387",
+"bv0 c #355471",
+"br5 c #355573",
+"bZa c #35566b",
+"bqP c #355673",
+"bq# c #355674",
+"bZn c #355d87",
+"cqx c #355e7e",
+"bAo c #355f92",
+"b1E c #35637f",
+"b1D c #356380",
+"a5Q c #356390",
+"b1B c #356480",
+"#6j c #35678d",
+"bc7 c #35689a",
+"bUH c #356a90",
+"bTe c #356a91",
+"bTh c #356b91",
+"bTc c #356b92",
+"bVr c #356c94",
+"aTw c #356e9a",
+"bZv c #356e9b",
+"caf c #356f99",
+"cbB c #356f9a",
+"bUt c #356f9c",
+"#Fa c #3571a3",
+"bPM c #3573a3",
+"cdr c #3573a5",
+"cs0 c #3573a7",
+"aA8 c #3573a9",
+"cuM c #3574a8",
+"cuN c #3574a9",
+"cvR c #3575a9",
+"#Tu c #3576ad",
+".G5 c #362435",
+".MA c #362528",
+"bC# c #362636",
+"bot c #362737",
+".y5 c #36282f",
+"aUA c #362d3d",
+"aD4 c #362e3d",
+"aG5 c #362e3e",
+".fN c #36303b",
+"b0C c #363051",
+"bbK c #36313f",
+"adQ c #363242",
+"bRd c #363357",
+"#AL c #36343f",
+"bt# c #363543",
+"aHy c #363844",
+"btS c #363a41",
+".fr c #363a42",
+"arT c #363b48",
+"#.P c #363c42",
+"aOy c #363c65",
+".8Y c #363d43",
+"aOC c #363e67",
+".xH c #364048",
+".Nk c #364049",
+".AH c #364147",
+".Fq c #364149",
+".gJ c #36414a",
+"#W9 c #36414b",
+"aoM c #36414c",
+".UE c #364249",
+".fE c #36424a",
+"#Ah c #36424b",
+"#NV c #36424c",
+"bkr c #36424d",
+".z7 c #36434a",
+".m6 c #36434b",
+"#Nd c #36434c",
+"#jQ c #36434d",
+"#QA c #36434e",
+"b.6 c #36434f",
+"bSp c #364350",
+"aiz c #36444c",
+"aPW c #36444e",
+"#W6 c #36444f",
+"bSq c #364450",
+"bSr c #364451",
+"#QP c #364650",
+"cpO c #36495c",
+"b0R c #365061",
+"cpV c #36516a",
+"ckJ c #36526b",
+"b7j c #365366",
+"bvw c #365773",
+"bAn c #36578d",
+"bDo c #365d87",
+"b2z c #365d88",
+"azY c #366281",
+"#Ii c #366386",
+"azu c #366588",
+"b0e c #366784",
+"#2F c #36688f",
+"#V5 c #36698f",
+"bTg c #366b92",
+"bTd c #366b93",
+"bTi c #366c92",
+"bTb c #366c93",
+"bWc c #366c94",
+"bWb c #366c95",
+"bTa c #366d94",
+"bSZ c #366d96",
+"bT. c #366e95",
+"bR5 c #366e97",
+"cdl c #366e9b",
+"bWa c #366f98",
+"bXM c #366f99",
+"#Cr c #366f9b",
+"ce3 c #366f9d",
+"cac c #366f9e",
+"b1b c #36709d",
+"ce0 c #36719d",
+"by1 c #36719e",
+"cg9 c #3671a1",
+"awk c #3671a3",
+"cd8 c #36729f",
+"aPk c #3673a4",
+"cvD c #3673a5",
+"aIg c #3674a4",
+"cqe c #3674a6",
+"#Nr c #3674a8",
+"cs9 c #3675a9",
+"clm c #3675aa",
+"az5 c #3676ab",
+"aA6 c #3676ac",
+"aru c #367cb1",
+".H# c #372435",
+"a3V c #372436",
+".PO c #372529",
+"bxc c #372637",
+"bsu c #372838",
+".xP c #372930",
+"bFM c #372c40",
+"aFh c #372d3d",
+".9k c #373034",
+"aDJ c #37303a",
+"a03 c #37303f",
+"aU. c #37313f",
+"bTR c #373253",
+".UC c #37343a",
+"aSy c #373444",
+"azI c #373541",
+"#9a c #37365a",
+"aHr c #373840",
+"#OV c #373844",
+"bCR c #373852",
+"#ON c #373a45",
+"age c #373b42",
+"bQ# c #373c46",
+"aqW c #373c49",
+".67 c #373d43",
+"aOF c #373d66",
+".xw c #373e4a",
+".48 c #373f45",
+"cj6 c #37414a",
+".b7 c #374248",
+"QtP c #374249",
+".pV c #37424a",
+".Hd c #37424b",
+"#7S c #37424c",
+"#2j c #37424d",
+"c#I c #374263",
+".fF c #37434b",
+".ye c #37434c",
+"#X0 c #37434d",
+"#bb c #37434e",
+"bB9 c #37434f",
+".oX c #37444b",
+".fG c #37444c",
+"#eQ c #37444d",
+"##x c #37444e",
+"blh c #37444f",
+"bQ5 c #374450",
+"aAj c #374650",
+"#OE c #374651",
+".9Z c #374955",
+"bVz c #374f77",
+"cmJ c #374f78",
+"bwX c #374f80",
+"bg. c #375169",
+"cge c #37526c",
+"cp6 c #37556f",
+"bvZ c #375572",
+"aZj c #37558a",
+"bF. c #375676",
+"bh9 c #37568c",
+"btp c #375874",
+"byu c #375c82",
+"b4h c #375d86",
+"bAy c #375e88",
+"bYy c #375f79",
+"bEi c #375f89",
+"#ad c #37629a",
+"#MC c #376386",
+"cpj c #376487",
+"baW c #376496",
+"a41 c #376593",
+"b0. c #376885",
+"bYo c #376888",
+"bWq c #376a8c",
+"bWs c #376a8d",
+"#7h c #376a90",
+"bTk c #376e95",
+"bT# c #376e96",
+"bJ6 c #376f97",
+"bR4 c #376f98",
+"bR3 c #377099",
+"bW# c #37709a",
+"cae c #37709c",
+"cbA c #37719d",
+"cbq c #37719e",
+"cfH c #37729f",
+"clj c #3772a0",
+"aso c #3772a1",
+"#oU c #377370",
+"bOa c #3773a2",
+"aPA c #3773a3",
+"b7v c #3774a1",
+"csx c #3774a3",
+"#Gt c #3774a4",
+"c#8 c #3775a5",
+"#Eo c #3775a6",
+"aPj c #3776a5",
+"cwV c #3776a9",
+"cvX c #3776aa",
+"bM1 c #3777aa",
+"csj c #3777ab",
+"aAH c #3777ac",
+"#86 c #3778aa",
+"bM2 c #3778ab",
+"#j9 c #37cac9",
+"#bL c #37ebed",
+"#gj c #38131f",
+"#h1 c #381320",
+"a3# c #382536",
+"a7E c #382637",
+"#x4 c #382e37",
+".Pu c #38313e",
+"aMt c #383241",
+".xA c #383349",
+"#JD c #38343e",
+"#Hx c #38343f",
+".o6 c #383541",
+"a#s c #38375c",
+".o# c #383840",
+".9D c #383842",
+"aqG c #383844",
+".xx c #383849",
+"aMe c #383941",
+".vD c #383b40",
+"amA c #383b45",
+"arJ c #383c46",
+"bJS c #383d48",
+"a1u c #383d68",
+"aC4 c #383f52",
+".yA c #38424a",
+".H4 c #38424b",
+".Q9 c #38434a",
+".xp c #38434b",
+".xv c #38434c",
+".Bn c #38434d",
+"ata c #38434e",
+".gH c #38444c",
+".zo c #38444d",
+"#46 c #38444e",
+"#1E c #38444f",
+".qR c #38454c",
+".hI c #38454d",
+"##s c #38454e",
+"#NN c #38454f",
+"axu c #384550",
+"bQ7 c #384551",
+"a0L c #38464e",
+"#6M c #384650",
+"bi4 c #384651",
+"#M5 c #384751",
+"bCX c #384851",
+"bh0 c #384956",
+"#GP c #384b59",
+"#IU c #384d5c",
+"cnd c #384d61",
+".N# c #384e5b",
+"cjl c #38526d",
+"chZ c #38536c",
+"cmd c #38536d",
+"cjs c #38566f",
+"bsJ c #385976",
+"bXe c #385e75",
+"bXp c #385e87",
+"aFW c #385f8b",
+"a59 c #386587",
+"bWK c #386683",
+"b0c c #386885",
+"bZ1 c #386987",
+"cgz c #386996",
+"bLM c #386b9e",
+"bWy c #386c8e",
+"#6n c #386c92",
+"bUF c #386e95",
+"cm5 c #386e98",
+"bMn c #386f96",
+"bR2 c #387099",
+"bR1 c #387199",
+"bR0 c #38719a",
+"bHX c #38719d",
+"bW. c #38729b",
+"b2O c #38729e",
+"cbz c #38729f",
+"cad c #38739f",
+"#DI c #3873a0",
+"cbx c #3874a2",
+"cwH c #3875a3",
+"bB. c #3875a4",
+"a8S c #3876a4",
+"cig c #3877ab",
+"cif c #3878ab",
+"b8N c #3878ac",
+"crz c #3878ad",
+"asp c #3879ae",
+"arq c #387aaf",
+"aMQ c #387cac",
+"a5x c #392436",
+"aKI c #392637",
+"aDO c #392838",
+".3T c #392a3a",
+".N8 c #392c2e",
+"bnU c #393040",
+"aFs c #39323c",
+"avN c #393240",
+".xy c #393449",
+"aqN c #393945",
+".sH c #393a44",
+"brK c #393b43",
+"#mo c #393c3d",
+"aQv c #393d67",
+".uJ c #394248",
+".l8 c #39424a",
+".BE c #39444d",
+"#Uq c #39444e",
+"ahd c #39444f",
+".hH c #39454d",
+".y# c #39454e",
+"#0g c #39454f",
+"bal c #394550",
+".oA c #39464d",
+".hJ c #39464e",
+"aiK c #39464f",
+"amw c #394650",
+"b.5 c #394651",
+"bQ6 c #394652",
+"aD9 c #394750",
+"aQA c #394b73",
+"asy c #394b7c",
+"boF c #394c5b",
+"c.D c #394c5c",
+"aKx c #394e6a",
+"auf c #394e7f",
+"aeO c #395082",
+"cg. c #39536d",
+"coP c #39546f",
+"a6N c #39586f",
+"bJK c #39588c",
+"br4 c #395976",
+"bq. c #395a76",
+"aFp c #395a8a",
+"bxD c #395c7f",
+"b01 c #395f89",
+"bBC c #396089",
+"b0V c #396597",
+"#0D c #396787",
+"a0H c #396794",
+"csg c #39688c",
+"b0b c #396986",
+"b0a c #396987",
+"a5w c #39698d",
+"b0# c #396a87",
+"b0h c #396a88",
+"b#B c #396a8c",
+"b0g c #396b88",
+"bcx c #396b9c",
+"cmV c #396c93",
+"#N8 c #396d95",
+"#Qg c #396e94",
+"bRZ c #397199",
+"bRY c #39729a",
+"bRT c #39739c",
+"bXL c #39739d",
+"bV9 c #39739e",
+"cdm c #3974a2",
+"bz5 c #3975a2",
+"c.N c #3975a3",
+"a0T c #3976a3",
+"cjO c #3977a5",
+"aN. c #3977a6",
+"agm c #3977aa",
+"#KF c #3978aa",
+"#ME c #3978ab",
+"ctP c #3978ac",
+"abI c #3979ac",
+"cwS c #397aae",
+"#Q4 c #397bb0",
+"#eB c #3a1622",
+".3L c #3a1b2a",
+".Ha c #3a2436",
+"aWT c #3a2437",
+"bDS c #3a2637",
+"bC0 c #3a2638",
+"bpT c #3a2839",
+"aYM c #3a2c3d",
+"aMC c #3a2d3e",
+".5p c #3a2f30",
+"blK c #3a3040",
+".Yp c #3a3131",
+"aww c #3a3241",
+".xz c #3a324a",
+"#xn c #3a3253",
+"#Hv c #3a3540",
+".p4 c #3a3642",
+"cik c #3a3741",
+"#N5 c #3a3945",
+".3z c #3a3b3d",
+"aVS c #3a3c46",
+"asX c #3a3c49",
+"aQp c #3a3d66",
+".Ab c #3a3e4c",
+".yE c #3a3f4c",
+".T8 c #3a4250",
+".tU c #3a4349",
+"#.m c #3a434f",
+"azV c #3a444e",
+".xq c #3a454c",
+".yd c #3a454e",
+".HH c #3a454f",
+"ahb c #3a4550",
+".nZ c #3a464e",
+".ya c #3a464f",
+"#1D c #3a4650",
+"blg c #3a4651",
+".jP c #3a474e",
+".k1 c #3a474f",
+"#Fn c #3a4750",
+"#HN c #3a4751",
+"#M6 c #3a4752",
+"bcE c #3a4850",
+"#To c #3a4852",
+"bke c #3a4a53",
+"#rv c #3a4e5d",
+"ai9 c #3a5082",
+"aBC c #3a5166",
+"#o3 c #3a5246",
+"cno c #3a546d",
+"ck7 c #3a546e",
+"cl6 c #3a566f",
+"bfi c #3a568b",
+"cjq c #3a5771",
+"cjx c #3a5872",
+"bY4 c #3a5875",
+"b8Y c #3a5b71",
+"bqO c #3a5b77",
+"b0f c #3a6b88",
+"b0i c #3a6d8b",
+"bYf c #3a6d8d",
+"cpk c #3a6f95",
+"bUE c #3a7299",
+"bRX c #3a729b",
+"bRW c #3a739c",
+"aPi c #3a73a0",
+"csw c #3a73a2",
+"ctO c #3a73a4",
+"bRV c #3a749c",
+"bRS c #3a749d",
+"bQF c #3a749e",
+"bQE c #3a759e",
+"bA. c #3a75a2",
+"ce2 c #3a75a3",
+"bXK c #3a76a0",
+"cby c #3a76a2",
+"byX c #3a76a4",
+"aZA c #3a76a5",
+"by0 c #3a77a4",
+"aN8 c #3a77a5",
+"b54 c #3a78a6",
+"cgn c #3a78a9",
+"clg c #3a79aa",
+"bKv c #3a7aad",
+"cnY c #3a7bad",
+"bJs c #3a7bae",
+"#80 c #3a7baf",
+"aal c #3a7cb1",
+"cmN c #3a7db1",
+"bKw c #3a82b4",
+"##V c #3ab6bc",
+"#fm c #3afbfb",
+"#fR c #3b1925",
+"a9x c #3b2436",
+"bCb c #3b2638",
+"bos c #3b2838",
+"bst c #3b2938",
+"a1E c #3b2a3b",
+".PF c #3b2b33",
+".5f c #3b2d36",
+"aZE c #3b2d3e",
+"aXm c #3b3044",
+"aNz c #3b3141",
+"a.v c #3b3142",
+"bG5 c #3b3251",
+"#Ku c #3b3540",
+"aTL c #3b3545",
+".n8 c #3b3742",
+".hU c #3b3a44",
+"aCv c #3b3c45",
+"aRB c #3b3c50",
+".uK c #3b3d40",
+"#ml c #3b3e42",
+"aXl c #3b3f48",
+"bDh c #3b3f49",
+".yc c #3b464f",
+".C9 c #3b4650",
+"ahc c #3b4651",
+".ww c #3b474e",
+".m. c #3b474f",
+".yb c #3b4750",
+".MP c #3b4751",
+"b.4 c #3b4752",
+".l9 c #3b484f",
+".pW c #3b4850",
+"bgK c #3b4851",
+"#BV c #3b4852",
+"bPL c #3b4853",
+"a47 c #3b4952",
+"aRa c #3b4954",
+"bg9 c #3b4b56",
+"axl c #3b4d59",
+"alt c #3b4d5b",
+"axp c #3b4e5b",
+"alg c #3b5467",
+"cm# c #3b556e",
+"cnB c #3b566f",
+"aSp c #3b568c",
+"ccs c #3b596f",
+"cnN c #3b5a7e",
+"bt6 c #3b5b77",
+"b4g c #3b6189",
+"bCv c #3b618a",
+"bzs c #3b618b",
+"#67 c #3b6884",
+"a6z c #3b6995",
+"a9N c #3b6b9b",
+"bZY c #3b6d8a",
+"crv c #3b6d93",
+"cqy c #3b6e95",
+"bYa c #3b6f8f",
+"cn9 c #3b7097",
+"bUX c #3b7194",
+"#Vk c #3b719a",
+"bNI c #3b739c",
+"bcb c #3b759d",
+"bRU c #3b759e",
+"bQD c #3b759f",
+"bbx c #3b769f",
+"bQA c #3b76a1",
+"a51 c #3b779f",
+"bXJ c #3b77a2",
+"#6o c #3b77a6",
+"bXI c #3b78a4",
+"#BQ c #3b78a6",
+"ccP c #3b79a6",
+"aL1 c #3b79a7",
+"c.O c #3b79a8",
+"b4p c #3b7aa8",
+"ctV c #3b7bae",
+"cuQ c #3b7cae",
+"ce1 c #3b7caf",
+"cry c #3b7db1",
+"aeB c #3b83b5",
+".PX c #3c2436",
+".Tg c #3c252b",
+"aYi c #3c2537",
+".Tf c #3c262b",
+"b#U c #3c2839",
+".KT c #3c292c",
+".RH c #3c293b",
+"bav c #3c2a3a",
+"a0O c #3c2c3d",
+"#r5 c #3c2d36",
+"a2Y c #3c2d3e",
+"#cz c #3c2f37",
+"#9o c #3c3142",
+".iJ c #3c333f",
+"#H4 c #3c3440",
+"#Ln c #3c3844",
+"aqy c #3c3a46",
+"aJO c #3c3c44",
+"aQt c #3c3c66",
+".6I c #3c3f30",
+"bEC c #3c3f4b",
+"#dQ c #3c431d",
+".tV c #3c454b",
+".CF c #3c454d",
+".vC c #3c474f",
+".Bo c #3c4750",
+".JI c #3c4751",
+".7F c #3c4752",
+"a#R c #3c484f",
+".m5 c #3c4850",
+".zp c #3c4851",
+".OI c #3c4852",
+"boX c #3c4853",
+".oW c #3c4950",
+"#uG c #3c4951",
+"aEb c #3c4952",
+"#OD c #3c4953",
+"bVQ c #3c4954",
+"#OC c #3c4a54",
+"cbe c #3c4a57",
+"a4m c #3c4b53",
+"aP7 c #3c4f82",
+"bdU c #3c505e",
+"aYF c #3c5389",
+".Fz c #3c5462",
+"cgf c #3c566f",
+"chD c #3c5670",
+"b9a c #3c5973",
+"bwr c #3c5e7e",
+"bDn c #3c618b",
+"b.g c #3c6697",
+"co9 c #3c6797",
+"#.i c #3c689f",
+"a4F c #3c6b8d",
+"#1l c #3c6d8d",
+"#b6 c #3c6dae",
+"bZX c #3c6e8a",
+"bZZ c #3c6e8b",
+"bZ0 c #3c6e8c",
+"a29 c #3c7096",
+"#LZ c #3c7199",
+"#KB c #3c7399",
+"a8T c #3c749a",
+"bQC c #3c76a0",
+"#Vi c #3c76a2",
+"b.t c #3c77a0",
+"bQz c #3c77a1",
+"bV8 c #3c78a3",
+"bPj c #3c78a4",
+"bPe c #3c79a5",
+"#Ae c #3c79a7",
+"cvO c #3c79a8",
+"#79 c #3c7aa6",
+"ccO c #3c7aa8",
+"bZz c #3c7ba8",
+"aSM c #3c7bac",
+"bj2 c #3c7cab",
+"ctl c #3c7cac",
+"cvS c #3c7daf",
+"cuR c #3c7db0",
+"aoU c #3c7db1",
+"#Es c #3c7eaf",
+"#8a c #3c7eb1",
+"aMT c #3c7fb0",
+".iX c #3c84b7",
+".h0 c #3c85b7",
+"#r2 c #3d1f2e",
+".G6 c #3d2436",
+"bx9 c #3d2638",
+"#CH c #3d2839",
+".N9 c #3d292c",
+"#Aw c #3d293a",
+"aZr c #3d2c3e",
+"a8c c #3d2d3e",
+"bCQ c #3d2e4f",
+"aQV c #3d3242",
+"#Hw c #3d3641",
+".bl c #3d3843",
+"a.h c #3d395c",
+"aM8 c #3d3961",
+"aps c #3d3a46",
+"bG0 c #3d3b59",
+".8q c #3d3c38",
+"aWJ c #3d3c64",
+"aQu c #3d3c66",
+"bE2 c #3d3d46",
+".gU c #3d3d47",
+".LJ c #3d3e4e",
+".O2 c #3d3e4f",
+".3j c #3d444a",
+".8H c #3d474e",
+"#5S c #3d4751",
+".xu c #3d4850",
+"#3M c #3d4851",
+".D. c #3d4852",
+"ahf c #3d4853",
+".so c #3d4950",
+".pX c #3d4951",
+".zF c #3d4952",
+".P5 c #3d4953",
+"bcQ c #3d4954",
+".tW c #3d4a51",
+"#F3 c #3d4a53",
+"#sy c #3d4a54",
+"#Pj c #3d4b55",
+"#Sy c #3d4b56",
+"#Tn c #3d4e5b",
+"bhX c #3d4f5c",
+"bek c #3d515f",
+"chY c #3d5770",
+"cq. c #3d5771",
+"bjR c #3d5973",
+"bsH c #3d5c77",
+"bto c #3d5d78",
+"aRP c #3d618e",
+"bfl c #3d6192",
+"bEh c #3d628b",
+"aIH c #3d6798",
+"ay0 c #3d6883",
+"#1n c #3d6984",
+"aZB c #3d6d8d",
+"#Z1 c #3d7297",
+"#3i c #3d7398",
+"bWr c #3d7494",
+"#J# c #3d759e",
+"bIU c #3d769e",
+"#yl c #3d76a2",
+"cqN c #3d77a3",
+"csS c #3d77a4",
+"b8M c #3d78a2",
+"#Uy c #3d78a4",
+"bUB c #3d79a0",
+"bQB c #3d79a3",
+"#QZ c #3d79a5",
+"aIe c #3d79a7",
+"bPh c #3d7aa5",
+"byY c #3d7aa8",
+"bPf c #3d7ba7",
+"byZ c #3d7baa",
+"ceb c #3d7bab",
+"bN1 c #3d7ca8",
+"ccJ c #3d7cab",
+"a2j c #3d7cac",
+"aQK c #3d7dad",
+"#K3 c #3d7daf",
+"bjV c #3d7eae",
+"cuX c #3d7eb0",
+"aJB c #3d7faf",
+"crd c #3d7fb0",
+"cih c #3d7fb1",
+"clr c #3d7fb2",
+"bkv c #3d80b0",
+"aMS c #3d80b1",
+"aMV c #3d81b2",
+"aMU c #3d82b3",
+".nE c #3d84b7",
+"#sQ c #3e2534",
+"a6b c #3e2537",
+"by. c #3e3542",
+"#7z c #3e3655",
+"#nK c #3e3c43",
+"aGT c #3e3c61",
+"aC8 c #3e3d47",
+"aDM c #3e3e47",
+".e3 c #3e3e48",
+".9f c #3e4142",
+".eC c #3e4149",
+".B5 c #3e4751",
+".It c #3e4850",
+"bBv c #3e4853",
+".Mb c #3e494f",
+".je c #3e4951",
+".Dr c #3e4952",
+".zq c #3e4953",
+"aAq c #3e4954",
+"ahK c #3e497a",
+"a#1 c #3e4a51",
+".px c #3e4a52",
+".EX c #3e4a53",
+"#dh c #3e4a54",
+"bMZ c #3e4a55",
+"cdT c #3e4a56",
+"att c #3e4a7a",
+"#zx c #3e4b54",
+"bKu c #3e4b56",
+"aPX c #3e4c55",
+"apX c #3e4c56",
+"axC c #3e4d7f",
+"av3 c #3e5362",
+".JQ c #3e5766",
+"cmp c #3e5871",
+"cjc c #3e5872",
+"#RL c #3e596a",
+"a6w c #3e5b8d",
+"ahs c #3e5c70",
+"cju c #3e5c75",
+"bvv c #3e5d77",
+"bqN c #3e5d78",
+"bQe c #3e6389",
+"bAx c #3e638c",
+"aQf c #3e6685",
+"clu c #3e6788",
+"bNx c #3e6892",
+"#91 c #3e6c87",
+"bb5 c #3e6e9c",
+"aAx c #3e749a",
+"#Ij c #3e77a1",
+"cw6 c #3e78a8",
+"bUC c #3e79a0",
+"cfk c #3e79a5",
+"bUA c #3e7aa2",
+"bPi c #3e7ba6",
+"bPg c #3e7ca7",
+"bN0 c #3e7ca8",
+"bNT c #3e7ca9",
+"c#z c #3e7cab",
+"bPd c #3e7da8",
+"b53 c #3e7dab",
+"cfG c #3e7dac",
+"#FS c #3e7eac",
+"aOI c #3e7ead",
+"#En c #3e7eae",
+"aSI c #3e7eaf",
+"cld c #3e7fb1",
+"#Ob c #3e80b1",
+"crA c #3e80b2",
+"ayy c #3e80b4",
+"aMR c #3e81b2",
+"ce8 c #3e81b3",
+"#G1 c #3e83b0",
+".mL c #3e85b6",
+".g3 c #3e86b8",
+".0j c #3f2033",
+".Tn c #3f2336",
+"aXu c #3f2538",
+"bDT c #3f2638",
+"a8l c #3f273a",
+"aYZ c #3f2a3a",
+"b.y c #3f2b3d",
+"aWC c #3f2c3e",
+"a8Q c #3f2d3e",
+"#Gm c #3f2e3d",
+".U3 c #3f3034",
+"aG1 c #3f3052",
+".wK c #3f3137",
+"aco c #3f3143",
+".Yn c #3f3435",
+"aNT c #3f3a63",
+"adN c #3f3c47",
+"aAe c #3f3e47",
+"aFU c #3f3f48",
+"ahg c #3f434c",
+"#Pt c #3f4952",
+".Aa c #3f4a52",
+"#JA c #3f4a53",
+".zE c #3f4a54",
+"aAp c #3f4a55",
+".24 c #3f4b51",
+"bvo c #3f4b53",
+".zr c #3f4b54",
+".Fp c #3f4b55",
+"bLK c #3f4b56",
+"#rp c #3f4c55",
+"ajR c #3f4c56",
+"#Q# c #3f4c57",
+"a3y c #3f4d56",
+"#6L c #3f4d58",
+".74 c #3f515d",
+"#IR c #3f5361",
+"#j7 c #3f5563",
+"cgj c #3f5972",
+"cjb c #3f5973",
+"aKw c #3f597d",
+"#QO c #3f5b71",
+"bGe c #3f5b76",
+"cnG c #3f5c75",
+"brv c #3f5e78",
+"br3 c #3f5e79",
+"#Q. c #3f5f76",
+"bsI c #3f5f79",
+"bE9 c #3f658e",
+"Qtx c #3f6780",
+"bYt c #3f7392",
+"ark c #3f7397",
+"bYs c #3f7492",
+"bYq c #3f7493",
+"bYe c #3f7593",
+"cmF c #3f769f",
+"cpl c #3f78a3",
+"bUD c #3f79a0",
+"clL c #3f79a3",
+"#8. c #3f7aa4",
+"bLa c #3f7ba3",
+"bPk c #3f7ca7",
+"bNS c #3f7da9",
+"bNU c #3f7daa",
+"aMW c #3f7dae",
+"bNY c #3f7eaa",
+"bXC c #3f7eae",
+"#F# c #3f7fac",
+"csT c #3f7fad",
+"cea c #3f7faf",
+"#L1 c #3f80b1",
+"a## c #3f81b2",
+"cfa c #3f81b4",
+"bDB c #3f82b3",
+"#Qk c #3f82b4",
+"crx c #3f83b6",
+"a.7 c #3f84b9",
+"#Gs c #3f85b2",
+"#FR c #3f86b2",
+".cO c #3f87b8",
+"#jy c #401523",
+"#ky c #401c2a",
+"bAe c #402639",
+".Rw c #40292e",
+".Ck c #402a4a",
+"b3Q c #403051",
+"bx0 c #403252",
+"aaB c #40375a",
+".vE c #403a3b",
+"#LS c #403a46",
+"aNZ c #403a62",
+"aEx c #403b64",
+"ae. c #403c46",
+"aqF c #403c48",
+"a0Q c #403c61",
+"aZv c #403c62",
+"agU c #403e4c",
+"aEl c #403f47",
+"atK c #40414a",
+".9d c #404345",
+"#am c #404524",
+"aK2 c #40454a",
+"acl c #40455b",
+".uL c #40484d",
+"aCS c #404a53",
+"aEP c #404a54",
+"awY c #404a55",
+"#2D c #404b52",
+".BD c #404b54",
+".D# c #404b55",
+"axZ c #404b56",
+"b0P c #404b6d",
+"bxw c #404c54",
+".zD c #404c55",
+".Jl c #404c56",
+".qs c #404d54",
+"#e9 c #404d56",
+"#eR c #404d57",
+"a9k c #404d59",
+"bjP c #404e58",
+"#S5 c #40505b",
+"bpD c #405667",
+"clJ c #405668",
+"ckR c #405a73",
+"cfZ c #405a74",
+"#8l c #405b7d",
+"c#5 c #405c74",
+"cf5 c #405d75",
+"bhr c #406292",
+"ca2 c #40637a",
+"bBB c #40658d",
+"byt c #40668d",
+"bCe c #406882",
+"aiS c #406e88",
+"b.h c #40709d",
+"#8X c #407290",
+"bYp c #407492",
+"a0i c #407499",
+"bYr c #407593",
+"bYu c #407594",
+"bYx c #407595",
+"bYb c #407695",
+"bYv c #407795",
+"a3S c #4078a0",
+"csh c #407ba6",
+"cqz c #407ba7",
+"bUz c #407ca5",
+"#3j c #407ca9",
+"#HE c #407cac",
+"bU6 c #407da1",
+"bLS c #407da6",
+"bNZ c #407da9",
+"bNV c #407daa",
+"bQG c #407ea7",
+"bXH c #407ea9",
+"bNX c #407eaa",
+"#Uw c #407eab",
+"bNW c #407fab",
+"bKH c #407fb0",
+"aKP c #4080af",
+"c#y c #4080b0",
+"#A8 c #4081af",
+"cxF c #4081b1",
+"aOK c #4082b2",
+"cb9 c #4082b4",
+"cjL c #4083b4",
+"#O2 c #4083b5",
+"#E9 c #4084b0",
+"#7j c #4084b7",
+"csi c #4085b7",
+"arp c #4085b9",
+"#DD c #4086b3",
+"#CZ c #4086b4",
+".aW c #4086b8",
+".j7 c #4087b7",
+".dq c #4087b9",
+"ac8 c #4088b9",
+"c.l c #4088ba",
+"bLL c #408abb",
+"#n# c #40a29f",
+"#h2 c #411623",
+"bch c #41273a",
+"aEp c #41283a",
+".IX c #412a2e",
+".MB c #412a2f",
+"aJH c #412b3e",
+"aY5 c #412c3d",
+"a2Z c #412c3e",
+"aHS c #412d3e",
+".GM c #412e32",
+"#jj c #412e37",
+"boY c #412f40",
+"aOt c #413041",
+"aQR c #413144",
+"Qtv c #413540",
+"aWF c #413a62",
+"aqL c #413d49",
+".9e c #414345",
+".Er c #414a52",
+"#3U c #414b53",
+"apT c #414b54",
+"#OB c #414b55",
+".RV c #414c54",
+".zs c #414c55",
+".YL c #414c56",
+".rq c #414d55",
+".zt c #414d56",
+"#3I c #414d57",
+"bum c #414d58",
+"aNh c #414e55",
+"afU c #414e57",
+"azq c #414e58",
+"aPU c #41505a",
+"bCW c #415158",
+"a1l c #415186",
+"bVP c #415260",
+"amc c #415563",
+"aUX c #41568b",
+"ceM c #415c75",
+"btn c #415f79",
+"buG c #41607a",
+"ay6 c #416295",
+"clK c #416a88",
+"bDW c #416c85",
+"aMX c #4171a0",
+"#Tm c #417397",
+"#16 c #41759a",
+"bA8 c #41769d",
+"bYw c #417896",
+"coj c #4178a3",
+"b5H c #417dad",
+"b30 c #417ea7",
+"#Kg c #417eaa",
+"#Nq c #417fab",
+"#Ek c #417fac",
+"cpm c #4180ad",
+"#JJ c #4180ae",
+"bKk c #4182ae",
+"bz6 c #4182b1",
+"a2m c #4182b2",
+"#C5 c #4183b2",
+"#Eq c #4183b3",
+"#SL c #4184b4",
+"cbr c #4184b5",
+"a0V c #4184b6",
+"bCJ c #4185b5",
+"#PG c #4185b6",
+"cs8 c #4185b7",
+"clp c #4185b8",
+"#El c #4186b3",
+"aoV c #4186b9",
+"c.c c #4187b6",
+".#2 c #4187b8",
+"#Cm c #4189b6",
+"#BL c #4189b7",
+"aIf c #4189ba",
+"b8Q c #418aba",
+"#Ud c #418bbc",
+"a#b c #418dbd",
+"#k3 c #421724",
+"#jz c #421725",
+".5H c #42192c",
+"#i3 c #421d2a",
+".ML c #422438",
+"#ob c #42273a",
+"aE4 c #42273b",
+"#ri c #42293c",
+".PP c #422a30",
+"a1G c #422a3b",
+"aNE c #422a3d",
+"aED c #422b3e",
+"aOf c #422c3f",
+"aRz c #422e3f",
+"#Gh c #422f3e",
+"#FE c #422f3f",
+"aa4 c #423043",
+"bPQ c #423051",
+"#aN c #42313b",
+"bG1 c #423455",
+"aKS c #42375c",
+"acU c #423844",
+"a0b c #423b60",
+"aQo c #423b61",
+"a3I c #423b63",
+"aTl c #423e4d",
+"bAs c #423f47",
+".zm c #424049",
+"ax6 c #424575",
+"asz c #424775",
+".1u c #42494f",
+".ZV c #424950",
+".VL c #42495b",
+".Yc c #424b51",
+"c#U c #424b54",
+"bu3 c #424c54",
+"#H# c #424c55",
+"aGm c #424c56",
+".4R c #424d53",
+"#9r c #424d54",
+"#Vg c #424d55",
+".Tu c #424d56",
+"#eP c #424d57",
+".zu c #424e56",
+".17 c #424e57",
+"b#E c #424e58",
+"#1j c #424f57",
+"awR c #424f58",
+"#Ox c #424f59",
+"ame c #42505a",
+"bNp c #425077",
+"#7K c #42515b",
+"aQe c #425169",
+"agx c #425384",
+"aWt c #42558a",
+"bhZ c #425766",
+".TW c #425a69",
+"#W3 c #425b6b",
+"axQ c #425c6b",
+"cjn c #425c75",
+"buF c #426078",
+"bt5 c #42607a",
+"#8j c #426088",
+"csZ c #426191",
+"bCu c #42678e",
+"bbs c #426897",
+"ba9 c #426a82",
+"#71 c #427494",
+"#Yz c #427594",
+"#5o c #427595",
+"bYd c #427796",
+"bYc c #427896",
+"bTl c #427ea3",
+"ba7 c #427fa8",
+"#7i c #427fab",
+"bQU c #4280ab",
+"bMy c #4281ae",
+"#K0 c #4282af",
+"b2M c #4282b1",
+"aqa c #4283b1",
+"#7b c #4283b2",
+"#Lv c #4284b3",
+"aPB c #4284b4",
+"#F. c #4285b2",
+"#Ja c #4285b3",
+"cca c #4285b4",
+"bz9 c #4285b5",
+"bCK c #4285b6",
+"b2L c #4286b5",
+"#97 c #4286b6",
+"bDG c #4286b7",
+"ay5 c #4286b8",
+".f# c #4287b8",
+".mK c #4287b9",
+"cnP c #4287ba",
+"#93 c #4287bb",
+"aLZ c #4288b8",
+".cc c #4288b9",
+"#U5 c #4289ba",
+"#A4 c #428bba",
+"cfd c #428bbc",
+"cdy c #428cbc",
+"#81 c #428cbd",
+"a.. c #428dbe",
+"c#Q c #428dbf",
+"bA3 c #428ebf",
+"#g4 c #42fcfc",
+"#gk c #431625",
+"#q8 c #431724",
+"#mt c #431725",
+"#q9 c #431824",
+".G7 c #432438",
+"a5y c #432539",
+"#FO c #432639",
+"b#h c #43273a",
+"a2p c #43273b",
+".5O c #43283c",
+".Ol c #43293c",
+"a5A c #432a3b",
+"a8k c #432a3d",
+"bnX c #432c3e",
+".yP c #432c50",
+"aRv c #433143",
+"aW# c #433144",
+"blT c #433244",
+"avU c #433843",
+"aN3 c #433960",
+"#6F c #433a61",
+"aNs c #433d49",
+"aqM c #433e4a",
+"apE c #433f4d",
+"aGz c #434148",
+"alj c #43434d",
+"aGk c #434548",
+"bKy c #434575",
+"avB c #434877",
+"#Ps c #434a54",
+".KA c #434c52",
+"#7F c #434d56",
+"aSZ c #434e55",
+"#Nc c #434e56",
+".zv c #434e57",
+"#3H c #434e58",
+"bqZ c #434e59",
+"ag5 c #434f55",
+"bsS c #434f57",
+".Fo c #434f58",
+"auG c #434f59",
+"#UO c #434f5a",
+"bvq c #435058",
+"apW c #435059",
+"axx c #43505a",
+"bjO c #43505b",
+"bCU c #435159",
+"aaY c #43517a",
+"bCV c #435259",
+"#ru c #435461",
+"aVK c #43558a",
+"#wO c #435b6b",
+"cjv c #436079",
+"br2 c #43627a",
+"aDE c #436395",
+"c#L c #43677f",
+"bzr c #43678e",
+"bkm c #436d91",
+"b.v c #43748f",
+"cj9 c #4379a2",
+"bWz c #437c9d",
+"bWJ c #437d9e",
+"bS9 c #4380a6",
+"bcL c #4380a8",
+"bY1 c #4380a9",
+"#Zj c #4380ab",
+"bj1 c #4380ad",
+"bUy c #4381ab",
+"bB3 c #4381af",
+"bOC c #4382ab",
+"bV7 c #4382ad",
+"bNR c #4382af",
+"bMz c #4383af",
+"cqA c #4383b1",
+"#TE c #4384b2",
+"#ZM c #4384b4",
+"#DE c #4385b3",
+"b1d c #4385b4",
+"#X8 c #4385b5",
+"#C0 c #4386b4",
+"b2K c #4386b5",
+"a28 c #4386b6",
+"aao c #4386b7",
+".c# c #4387b6",
+"#0q c #4387b7",
+"#X9 c #4387b8",
+"cwU c #4388b8",
+"crw c #4388ba",
+"#U4 c #4389b9",
+"#BM c #438ab9",
+"#Uc c #438aba",
+"#Su c #438bbb",
+"#Ue c #438bbc",
+"#82 c #438cbc",
+"bFv c #438cbd",
+"#A# c #438dbc",
+"a#a c #438dbd",
+"abJ c #438dbe",
+"bA2 c #438ebe",
+"bEx c #438ebf",
+"#h3 c #441725",
+"#eC c #441927",
+".1Z c #441e31",
+"#zY c #442434",
+"b#i c #442539",
+"a6d c #44263a",
+"aFA c #44273b",
+".Yk c #442930",
+"#oe c #44293d",
+"aD3 c #442a3d",
+"aPH c #442a3e",
+".EE c #442e32",
+"bln c #442e40",
+"bmi c #442e41",
+"afs c #443043",
+"#u8 c #44323b",
+"bSw c #443255",
+"aMs c #443343",
+"bq0 c #443646",
+"beX c #443945",
+"aNY c #443960",
+"bhS c #443a45",
+".wy c #443b3c",
+"azH c #443c48",
+"aE# c #444648",
+"cqi c #444767",
+".Wu c #444c53",
+"#hs c #444e55",
+"axq c #444e56",
+"#C7 c #444f57",
+".zw c #444f58",
+"#jP c #444f59",
+"bvp c #445057",
+".zC c #445058",
+"#4y c #445059",
+"#1i c #445159",
+"#IA c #44515a",
+"aEH c #44515b",
+"aRb c #44525c",
+"aPY c #445460",
+"bhY c #445868",
+"bdC c #44598c",
+"#IQ c #445a69",
+"azh c #445d6c",
+"#qu c #445d6d",
+"ckG c #445f77",
+"ck4 c #445f79",
+"chM c #44617a",
+"bvu c #44627a",
+"bgR c #44627d",
+"co4 c #446583",
+"#SE c #44667c",
+"bwq c #446685",
+"bZm c #44688e",
+"bxC c #44698e",
+"aL4 c #446a92",
+"bbt c #44729e",
+"baY c #4474a0",
+"#QN c #447ba3",
+"bTA c #4480a6",
+"a6# c #4481ac",
+"bKE c #4482ac",
+"#RR c #4483b0",
+"bJd c #4484b0",
+"bMx c #4484b1",
+"#8# c #4484b2",
+"#Em c #4485b3",
+"#Wu c #4486b5",
+"bXD c #4487b5",
+"#KE c #4487b6",
+"#Lx c #4487b7",
+"#V2 c #4487b8",
+"#DF c #4488b5",
+"#Cn c #4488b6",
+"b1c c #4488b7",
+"#Er c #4488b8",
+".bC c #4488b9",
+"cxz c #4489b8",
+"#77 c #4489b9",
+"#VK c #4489ba",
+"cuS c #4489bb",
+"#RE c #448abb",
+"cte c #448abc",
+"#Ti c #448bbb",
+"crC c #448bbc",
+"#Aa c #448cbc",
+"#99 c #448cbd",
+"aJA c #448ebe",
+"#zm c #448fbf",
+"b62 c #448fc0",
+"b61 c #4490c0",
+"a5j c #4492c2",
+"#k4 c #451725",
+"a9w c #45273b",
+"#B8 c #45293c",
+"aFN c #452a3e",
+"#wt c #452e49",
+"b6U c #453050",
+"bbJ c #453244",
+".d2 c #453844",
+"a0R c #453860",
+"aQn c #45395e",
+"a9q c #453a43",
+"aVd c #453a60",
+".qY c #453b47",
+".xs c #453c3c",
+".nf c #453c48",
+"aLa c #454047",
+"ac4 c #45406d",
+"bvn c #454149",
+"asS c #45434d",
+".vF c #454a50",
+"ahv c #454a54",
+".6U c #454c50",
+".vz c #454c54",
+".UO c #454d53",
+"ahz c #454d57",
+".Gm c #454e54",
+".xI c #454e55",
+".On c #454f58",
+"bu4 c #455056",
+".ga c #455057",
+"#LR c #455058",
+".zx c #455059",
+"ap6 c #45505a",
+".dz c #455158",
+"#wC c #455159",
+"#ie c #45515a",
+"aa. c #45515b",
+"aAl c #45525b",
+"aFQ c #45525c",
+"#54 c #45535b",
+"aed c #45535d",
+"bft c #45535e",
+"ab# c #455460",
+".0P c #455a6b",
+".Qq c #455d6c",
+".OO c #455e6d",
+"#YN c #455e6e",
+"bp9 c #455e71",
+".S. c #455f6e",
+"cgi c #455f78",
+"cja c #455f79",
+"clP c #45617a",
+"bqM c #45637b",
+"bDm c #45698e",
+"b2y c #45698f",
+"#RN c #456b81",
+"ax2 c #456d85",
+"a4t c #456e8d",
+".8m c #4573a8",
+"bC3 c #45748f",
+"b.R c #4574a0",
+"#Cl c #457dab",
+"bHE c #4582ac",
+"bSg c #4583ab",
+"bPS c #4584ad",
+"bPl c #4585af",
+"bMw c #4586b2",
+"bMA c #4586b3",
+"#C1 c #4586b5",
+"b1f c #4587b2",
+"#Co c #4587b6",
+"#JL c #4588b6",
+"#C2 c #4588b7",
+"#V4 c #4588b8",
+"#Kh c #4589b7",
+"#BN c #4589b8",
+"#OZ c #4589b9",
+"#78 c #4589ba",
+"#JK c #458ab8",
+"#Jb c #458ab9",
+".dp c #458aba",
+"#98 c #458abb",
+"aAG c #458abc",
+"ax5 c #458abd",
+"a#c c #458bba",
+"#Wv c #458bbb",
+"cpn c #458bbc",
+"cpo c #458bbd",
+"bEw c #458cbc",
+"cmW c #458cbd",
+"#Ac c #458dbd",
+"c.d c #458dbe",
+".76 c #458e9a",
+"#Ab c #458ebe",
+"bCH c #458ebf",
+"#zn c #458fbe",
+"bA6 c #458fbf",
+"bB1 c #458fc0",
+"bB0 c #4590c0",
+"bfw c #4595c7",
+"#jA c #461826",
+".WR c #462236",
+".Hb c #462439",
+".Vg c #46283c",
+".Z0 c #462a32",
+"a96 c #462a3e",
+".WB c #462b31",
+"a2. c #462b3f",
+"aCo c #462c3f",
+"blW c #462d3f",
+".O. c #462e32",
+"aYb c #462f43",
+"bFF c #463345",
+".B6 c #463753",
+"#Nl c #463844",
+"aPt c #46385f",
+"#ey c #463d42",
+"aqx c #463d4a",
+"apr c #463e4b",
+"bAh c #46404d",
+"bGZ c #46405d",
+"aK4 c #46414a",
+"bCS c #464a55",
+".Rh c #464e55",
+".IF c #464f56",
+"#58 c #464f58",
+"bCT c #465057",
+"#Xn c #465058",
+".zB c #465059",
+"#Lk c #465158",
+".zy c #465159",
+".W3 c #46515a",
+"aXW c #465185",
+".z5 c #465259",
+".He c #46525a",
+"aiA c #46525b",
+"abe c #46535b",
+"#Wi c #46535c",
+"aO0 c #46535d",
+"aiy c #46545e",
+".6# c #465965",
+"bVO c #465d71",
+"ayj c #465f6e",
+"cow c #465f78",
+"cmq c #466079",
+"cnm c #466179",
+"bW8 c #466291",
+"bt4 c #46647d",
+"bi9 c #46647e",
+"ccI c #466480",
+"bwp c #46657e",
+"aKv c #466588",
+"bOU c #46688c",
+"b4. c #466895",
+"bAw c #466a8f",
+"bXo c #466a90",
+"bGd c #466b92",
+"c.x c #466e86",
+"beP c #467196",
+"#DC c #4675a1",
+"awj c #467797",
+"#0F c #467a9a",
+"bBg c #467d9a",
+"bWE c #467f9e",
+"c.B c #467fad",
+"bWF c #4680a0",
+"bWG c #4680a1",
+"a6. c #4685b2",
+"#Y1 c #4686b5",
+"bA9 c #4687b5",
+"a27 c #4688b7",
+"#C3 c #4689b8",
+"#BO c #4689b9",
+"#DG c #468ab9",
+"#Cp c #468aba",
+"clM c #468abb",
+"#KD c #468bb9",
+".em c #468bba",
+"#A5 c #468bbb",
+"aQJ c #468bbc",
+"#Ad c #468cbb",
+"#Lw c #468cbc",
+"#KC c #468cbd",
+"#zo c #468dbc",
+"bB2 c #468dbd",
+"#K1 c #468dbe",
+"#8Z c #468dc0",
+"#ZO c #468ebd",
+"bA4 c #468ebf",
+"aKO c #468fbf",
+"bA5 c #468fc0",
+"aLD c #4691c3",
+"#mu c #471927",
+"b#1 c #472439",
+"aWb c #47253a",
+"bCa c #47263a",
+".UW c #472b32",
+".KU c #472d32",
+"bmt c #472e42",
+"bKB c #473050",
+"avM c #473244",
+"aDZ c #47324f",
+".yF c #473353",
+"bR. c #473357",
+"aUl c #47385e",
+".yC c #473e3f",
+"azG c #473f4b",
+"csd c #474151",
+"bDQ c #474451",
+"bL9 c #47464f",
+"#pG c #474a4d",
+"akg c #474b54",
+".6T c #474d52",
+"#OF c #474e57",
+"bM3 c #474f7e",
+"#4x c #475058",
+"#KT c #475159",
+"#Yk c #47515a",
+".YM c #475259",
+".zA c #47525a",
+".zz c #47525b",
+"#ZC c #47525c",
+".Xv c #475265",
+"#j3 c #47535c",
+"#6Z c #47535d",
+"##y c #47545d",
+"#NK c #47545e",
+"ayo c #47555e",
+"buB c #47555f",
+"#lT c #47583e",
+"#Vz c #476070",
+"cla c #47617a",
+"aBx c #476272",
+"coM c #47637c",
+"aMY c #47638f",
+"bvt c #47647b",
+"bys c #476b90",
+".bz c #476c8e",
+"aII c #476d9a",
+"aZl c #476e93",
+"aMP c #476f9c",
+"ca6 c #47738c",
+"bFP c #477896",
+"#Zl c #477c9b",
+"bWC c #477f9f",
+"bWD c #4780a0",
+"bWB c #4780a1",
+"bWA c #4781a0",
+"bWH c #4781a1",
+"#A. c #4781b0",
+"bUY c #4782a4",
+"csU c #4782ad",
+"#3X c #4783a7",
+"#Xd c #4785b3",
+"b0G c #4786ae",
+"#ZN c #4787b5",
+"bN2 c #4788b3",
+"bMv c #4788b5",
+"aAy c #4789b5",
+"bZw c #4789b7",
+"a2n c #4789b8",
+"a.# c #478ab9",
+"#C4 c #478aba",
+"#K2 c #478abb",
+"caX c #478bb9",
+"#A6 c #478bba",
+"#BP c #478bbb",
+"#L0 c #478bbc",
+"#O. c #478cba",
+".bD c #478cbb",
+".aV c #478cbc",
+"#Ux c #478cbd",
+"#RS c #478dbb",
+"#Cq c #478dbc",
+"#N9 c #478dbd",
+"co3 c #478dbe",
+"#zp c #478ebd",
+"cqC c #478ebe",
+"#MD c #478ebf",
+"cqB c #478ec0",
+"ac9 c #478fbe",
+"bCI c #478fbf",
+"#dt c #47fcfb",
+"#sg c #481926",
+"#k5 c #481927",
+".G8 c #48243a",
+"bpS c #48263b",
+"aWU c #482a3c",
+"bo4 c #482b3e",
+"aOS c #482b3f",
+"a01 c #482d3f",
+"bmj c #482e41",
+".Ez c #482f37",
+".S5 c #48313a",
+".ZX c #48313b",
+"#s6 c #48333a",
+"#s4 c #48333b",
+"bpq c #483344",
+".T. c #483535",
+"aTt c #48365c",
+"aRK c #48365d",
+"aE7 c #48385e",
+"aCs c #48444c",
+"auT c #484472",
+".6y c #484542",
+"brb c #48454e",
+".9c c #48484a",
+"afO c #484851",
+"crh c #484a69",
+"#NO c #484f58",
+"#57 c #485157",
+".BC c #48525a",
+".Fn c #48525b",
+".Ar c #485359",
+".qu c #48535a",
+".Bp c #48535b",
+"#4q c #48545c",
+".9R c #48545d",
+"bKM c #485487",
+"aZO c #48555e",
+"aXH c #48555f",
+"atc c #48565f",
+".Z# c #48576a",
+"cox c #48617a",
+"bVv c #486291",
+"cpS c #48637b",
+"cqm c #486391",
+"cf6 c #48647d",
+".ys c #486576",
+"bZl c #486c91",
+"a1n c #486f94",
+"#b5 c #487ec8",
+"bWI c #4882a2",
+"bDH c #4887b6",
+"cnO c #4888b7",
+"bSz c #4889b2",
+"#Rx c #488ab7",
+"#Yx c #488ab9",
+"bMB c #488bb6",
+"bA1 c #488bb9",
+"cuY c #488bba",
+"#DH c #488cbc",
+"#76 c #488dbc",
+"aN# c #488dbd",
+"aL0 c #488dbe",
+"#A7 c #488ebd",
+".cN c #488ebe",
+"#Qh c #488ebf",
+"a3T c #488ec0",
+"#TD c #488fbf",
+"#Q0 c #488fc0",
+"#OY c #4890c2",
+"#h4 c #491a27",
+".9t c #491d32",
+"a6Y c #49243a",
+"aVi c #49253a",
+"bx8 c #49263a",
+"a57 c #492a3f",
+".5k c #492d34",
+".Rx c #492e34",
+".KL c #492f37",
+"aPL c #493244",
+"#w7 c #49343e",
+".A. c #493f3f",
+"aJ7 c #49434b",
+"aOr c #49444c",
+"aU7 c #49454e",
+".qP c #494d55",
+".6Z c #494e51",
+"#M7 c #494f59",
+"amH c #49525d",
+"bqv c #495261",
+"#IL c #49535a",
+".P6 c #49535b",
+".r0 c #49545b",
+".JH c #49545c",
+"atl c #49545d",
+"#Cy c #49555d",
+"#ib c #49555e",
+"#Ov c #49565f",
+"aIr c #495760",
+"anu c #495761",
+"bpC c #495864",
+"aRc c #495964",
+".vq c #496271",
+"#IS c #496273",
+"bBp c #496291",
+"cjC c #49637c",
+"#IT c #496474",
+"bHx c #49647e",
+"aHk c #496a98",
+"aKu c #496b91",
+"bCt c #496d91",
+"bXn c #496d92",
+"beF c #497198",
+"a7j c #49759f",
+"#Z3 c #497f9e",
+"bUx c #498ab3",
+"bao c #498bb6",
+"csO c #498bb9",
+"aLF c #498bbc",
+".lk c #498dbb",
+"bO# c #498eba",
+"#O# c #498ebd",
+"aOJ c #498ebe",
+"#PD c #498fbd",
+"#Qi c #498fbe",
+"bOn c #498fbf",
+"aLE c #498fc0",
+"ce7 c #4990bf",
+"bz7 c #4990c0",
+"bz8 c #4990c1",
+"#WO c #4990c2",
+"#Vj c #4991c1",
+"#RU c #4991c2",
+"b7c c #4992c2",
+"#V3 c #4992c4",
+"#oV c #49958d",
+"bMM c #4996c3",
+"#gl c #4a1927",
+"#n4 c #4a1a29",
+"#pZ c #4a2339",
+".H. c #4a243a",
+"aY1 c #4a253a",
+"aYj c #4a253b",
+"aUB c #4a2a3f",
+"bpR c #4a2c41",
+".S9 c #4a2d34",
+".GN c #4a2e34",
+"a#P c #4a2e43",
+"bm6 c #4a2f43",
+"bJA c #4a2f50",
+".Ro c #4a3737",
+".Mv c #4a3839",
+"aXq c #4a385e",
+"awE c #4a3b47",
+"alv c #4a3b57",
+"arU c #4a4250",
+"aOj c #4a434c",
+"aJ5 c #4a454d",
+"bHr c #4a4852",
+"akb c #4a4b54",
+".wz c #4a4e53",
+"aRr c #4a5082",
+".yW c #4a5259",
+".Da c #4a545c",
+"bhp c #4a5487",
+"#cg c #4a555a",
+".Bq c #4a555c",
+".BB c #4a555d",
+"##r c #4a555e",
+"aaR c #4a557c",
+".VI c #4a565d",
+"#48 c #4a565e",
+"acH c #4a5660",
+"#uE c #4a5760",
+"a46 c #4a5860",
+"#IM c #4a5b67",
+"alw c #4a5b7f",
+"#Pe c #4a6273",
+"bF0 c #4a6291",
+"b#2 c #4a6372",
+"bL5 c #4a6392",
+"cwc c #4a6479",
+"chz c #4a647d",
+"cme c #4a647e",
+"aAf c #4a6675",
+"b.D c #4a6676",
+"b4f c #4a667f",
+"a2W c #4a6d8c",
+"bzq c #4a6d91",
+"bDl c #4a6d92",
+"bVM c #4a6e91",
+"bxB c #4a6e93",
+"b.r c #4a7192",
+"bzW c #4a729d",
+"bCL c #4a83b1",
+"aLG c #4a86b5",
+"#U3 c #4a87b5",
+"#Th c #4a8ab8",
+"#QM c #4a8bb9",
+"b2N c #4a8db8",
+"bMC c #4a8eba",
+"bMu c #4a8fba",
+"#Q1 c #4a90be",
+"cdq c #4a90bf",
+"#XA c #4a90c0",
+"ckg c #4a91c0",
+"a2i c #4a91c1",
+"clC c #4a91c2",
+"aLC c #4a92c1",
+"aro c #4a92c4",
+"ctM c #4a94c3",
+"#iy c #4afcfc",
+"#tb c #4b1a29",
+"#fS c #4b1b2a",
+".G9 c #4b243a",
+"a4I c #4b2a3f",
+"aMD c #4b2a40",
+".1C c #4b2d35",
+"#FM c #4b2d3f",
+"#.X c #4b2e35",
+"aUK c #4b3245",
+"awv c #4b3345",
+"aNy c #4b3346",
+".CV c #4b3437",
+"brG c #4b3648",
+".Od c #4b3a3f",
+".cx c #4b3e4a",
+"biG c #4b3f4a",
+".ew c #4b444a",
+"ahX c #4b4471",
+"aGA c #4b454c",
+".H8 c #4b4640",
+".fW c #4b464e",
+"co6 c #4b4a68",
+".Cw c #4b545a",
+"bHg c #4b5486",
+"bl7 c #4b555c",
+".Dq c #4b555d",
+"#3. c #4b555e",
+".mA c #4b565d",
+"#gv c #4b565e",
+"af3 c #4b565f",
+"awV c #4b5760",
+"#VD c #4b5861",
+"azm c #4b5862",
+"#bl c #4b5962",
+"aAt c #4b5963",
+"adW c #4b5b67",
+"cos c #4b5f72",
+"#Se c #4b6170",
+"aC1 c #4b6394",
+"cpY c #4b647c",
+"bVC c #4b6579",
+"bim c #4b657b",
+"cmg c #4b657e",
+"ckY c #4b6880",
+"bVN c #4b6885",
+"#Tw c #4b6a89",
+"aRR c #4b6a95",
+"bb1 c #4b6c99",
+"bxA c #4b6e91",
+"aKt c #4b709a",
+"aQg c #4b749d",
+"bBZ c #4b82af",
+"#Xc c #4b85b1",
+"#1m c #4b86ad",
+"aap c #4b88b6",
+"aeC c #4b89b8",
+"ck. c #4b8ab8",
+"b5F c #4b8cb6",
+"bHW c #4b8cb7",
+"QtA c #4b8dbb",
+"bXG c #4b8eb9",
+"crD c #4b8ebd",
+"bNd c #4b8fb9",
+"bLw c #4b8fbd",
+".mJ c #4b8fbe",
+"clh c #4b90bf",
+"csp c #4b91c0",
+"ctZ c #4b91c1",
+"cfc c #4b92c1",
+"cjR c #4b92c4",
+"cch c #4b93c4",
+"#Y2 c #4b95c4",
+"arv c #4b99ca",
+"#jB c #4c1a29",
+"#sh c #4c1b29",
+"#r. c #4c1b2a",
+"#n3 c #4c222f",
+"#l. c #4c2436",
+"a7F c #4c263c",
+"aKV c #4c2a3f",
+"a8e c #4c2a40",
+".ZZ c #4c2d34",
+".PQ c #4c2f35",
+".Ry c #4c2f36",
+"axa c #4c3345",
+"bh1 c #4c3547",
+"#KV c #4c3d49",
+"#Bv c #4c3e4b",
+"aqE c #4c414d",
+"asA c #4c4270",
+"aJ4 c #4c454e",
+".Nt c #4c4640",
+".Pa c #4c4641",
+".gp c #4c464d",
+".LQ c #4c4741",
+"bKU c #4c4952",
+"aiP c #4c4c57",
+"a.V c #4c4d55",
+"#cq c #4c4f52",
+"#Sz c #4c515b",
+"aXc c #4c5186",
+"bEW c #4c5486",
+"bpa c #4c565d",
+"#Zb c #4c565e",
+".Br c #4c575e",
+"bmK c #4c575f",
+"a75 c #4c5760",
+"#7W c #4c5860",
+"awO c #4c5861",
+"a8I c #4c5962",
+"#4g c #4c5a63",
+"#8y c #4c5a64",
+"aR. c #4c5d67",
+".4a c #4c5f6c",
+"ckD c #4c6175",
+"chv c #4c667f",
+"cjg c #4c6881",
+"b5S c #4c6a94",
+"bAv c #4c6f93",
+"coY c #4c7095",
+"#A3 c #4c76a1",
+"#4E c #4c7f9d",
+"b8F c #4c7fad",
+"aKN c #4c80ae",
+"bH. c #4c83a6",
+"#X7 c #4c85b1",
+"#ac c #4c88d7",
+"bU5 c #4c89ab",
+"bU1 c #4c8aab",
+"azZ c #4c8ab0",
+"#Ub c #4c8ab7",
+"bR6 c #4c8bb2",
+"#Sq c #4c8bb8",
+"#RD c #4c8bba",
+"bQH c #4c8db5",
+"azv c #4c8db6",
+".Ne c #4c8ebb",
+"aTB c #4c8fbb",
+"ct1 c #4c92c2",
+".cL c #4c93c1",
+".cb c #4c93c2",
+"cvI c #4c94c2",
+"ch8 c #4c94c3",
+"#Q3 c #4c94c4",
+"a9s c #4c95c7",
+"#bM c #4cfafd",
+"#fn c #4cfdfa",
+"#vg c #4d1c2c",
+"#pP c #4d1d2a",
+"#x. c #4d2030",
+"baG c #4d243b",
+"aFg c #4d293f",
+"a1A c #4d2b40",
+".WA c #4d2d35",
+"aZI c #4d2d40",
+"aR5 c #4d2e44",
+".IY c #4d2f35",
+".MC c #4d3036",
+"a22 c #4d355b",
+"aOD c #4d365d",
+"aDV c #4d375e",
+".PI c #4d393a",
+"#hL c #4d3944",
+"#qY c #4d3b3d",
+".N2 c #4d3c3e",
+"axi c #4d3d4a",
+".J9 c #4d3e5b",
+"aKb c #4d424e",
+"bLN c #4d436f",
+".DT c #4d4442",
+"ag. c #4d535c",
+"#IK c #4d565d",
+"bUj c #4d575d",
+".Lp c #4d575e",
+".Jm c #4d575f",
+".BA c #4d585f",
+"#Ys c #4d5860",
+"#2Y c #4d5861",
+"#DO c #4d5961",
+"#c6 c #4d5962",
+"avv c #4d5a63",
+"aKY c #4d5a64",
+"#W5 c #4d5b65",
+"aPZ c #4d5c66",
+"b0L c #4d6391",
+"cl# c #4d667f",
+"#1y c #4d6777",
+"#tN c #4d6778",
+"cmt c #4d677f",
+"ckS c #4d6780",
+"cje c #4d6880",
+"cf9 c #4d6981",
+"cd6 c #4d6a82",
+"#zl c #4d7eab",
+"aQi c #4d80b0",
+"#yk c #4d81ae",
+"aJz c #4d81af",
+"aQh c #4d84b2",
+"#Sp c #4d89b6",
+"bU0 c #4d8aab",
+"#RB c #4d8ab6",
+"bUZ c #4d8bac",
+"ctf c #4d8cb8",
+".OW c #4d8ebc",
+".Qx c #4d8fbd",
+"arl c #4d92be",
+"ctY c #4d93c2",
+"cgt c #4d94c3",
+"ckf c #4d95c4",
+"cvG c #4d95c6",
+"#RT c #4d96c4",
+"ctN c #4d97c5",
+"#tc c #4e1b2a",
+"#ue c #4e1c2b",
+"aLR c #4e273d",
+"aIo c #4e2a3f",
+"aNf c #4e2a40",
+"bss c #4e2b3f",
+".87 c #4e2f37",
+"aWR c #4e2f44",
+".Rn c #4e3037",
+"bkf c #4e3146",
+"#aQ c #4e3539",
+"aQm c #4e355b",
+"aqz c #4e3b4b",
+"agi c #4e416d",
+".I8 c #4e454d",
+".Sz c #4e4642",
+"au4 c #4e4650",
+"cco c #4e4a6a",
+".xt c #4e5055",
+"aT7 c #4e5287",
+"a4g c #4e5686",
+".JG c #4e585f",
+".Fm c #4e5860",
+".Bs c #4e5960",
+"afT c #4e5961",
+"a9D c #4e5962",
+"aWZ c #4e5963",
+"#5d c #4e5a62",
+"#rq c #4e5a64",
+"awU c #4e5b64",
+"#F5 c #4e5b65",
+"avd c #4e5c65",
+"#NC c #4e5c66",
+"bSE c #4e6390",
+"#3T c #4e6676",
+"cmc c #4e6780",
+"chH c #4e6881",
+"b5X c #4e6981",
+"coO c #4e6a82",
+"chG c #4e6a83",
+"ckX c #4e6b83",
+"bBz c #4e7093",
+"bE8 c #4e7094",
+"bBA c #4e7194",
+"byr c #4e7195",
+"b#v c #4e779e",
+"aLH c #4e7fac",
+"ca7 c #4e819e",
+"#U# c #4e89b5",
+"#Ry c #4e8ab5",
+"#St c #4e8ab6",
+"#Ss c #4e8ab7",
+"#RC c #4e8bb7",
+"#Tl c #4e8bb9",
+"bU2 c #4e8cae",
+"cqJ c #4e8cb7",
+"bTm c #4e8eb1",
+".LD c #4e8fbc",
+"ciD c #4e90bd",
+"bNQ c #4e92bc",
+"bPm c #4e93bb",
+"bRk c #4e93bc",
+"cwI c #4e96c4",
+"#0p c #4e96c5",
+"cmE c #4e96c7",
+"aAF c #4e97c7",
+"#6i c #4e98c8",
+"ck# c #4e99c9",
+"b.0 c #4e9ed2",
+"#pQ c #4f1c2b",
+"bxe c #4f253c",
+"aLQ c #4f263c",
+"bxd c #4f263d",
+"aLS c #4f273d",
+"aD2 c #4f293e",
+"#jx c #4f2b37",
+"aF0 c #4f2d41",
+"bbc c #4f2e41",
+"bVi c #4f2e4d",
+"bSv c #4f2e51",
+".UV c #4f2f37",
+".EF c #4f3238",
+"a0Z c #4f3245",
+"aOY c #4f3346",
+"aFC c #4f345a",
+"aHG c #4f3647",
+".KO c #4f3a3b",
+"asB c #4f3a66",
+".Wf c #4f4446",
+"aC6 c #4f4850",
+".eD c #4f4c52",
+"bHl c #4f5579",
+"abX c #4f5586",
+"#7Y c #4f565e",
+".M4 c #4f5960",
+".Bz c #4f5961",
+".Bt c #4f5a61",
+"aAV c #4f5a62",
+"#eX c #4f5b63",
+"#KM c #4f5b64",
+"bSQ c #4f5b65",
+"bro c #4f5c64",
+"bfQ c #4f5c65",
+"auL c #4f5c66",
+"ah. c #4f5d65",
+"#9w c #4f5d66",
+"bcH c #4f5f69",
+"#Bg c #4f6370",
+"cbb c #4f6390",
+"#IP c #4f6471",
+"#rw c #4f6573",
+"#0b c #4f6879",
+"cnn c #4f6881",
+"#ty c #4f697a",
+"ckN c #4f6982",
+"byq c #4f7094",
+"bHv c #4f7193",
+"bAu c #4f7194",
+"bzp c #4f7195",
+"bGc c #4f7295",
+"bHw c #4f7296",
+"cwb c #4f7799",
+"axA c #4f819d",
+"#Tg c #4f89b4",
+"#Ua c #4f8ab5",
+"#dK c #4f8bd7",
+"bU4 c #4f8dae",
+"#4B c #4f8db3",
+"bU3 c #4f8eae",
+"#dJ c #4f8ee0",
+"#Y. c #4f8fba",
+"bO7 c #4f90b8",
+".J2 c #4f90bd",
+"bPy c #4f93bd",
+"bN3 c #4f94be",
+"ctn c #4f95c0",
+"cmA c #4f96c3",
+"ce6 c #4f96c5",
+"ckh c #4f97c6",
+"ctb c #4f98c6",
+".7x c #501b32",
+"#k6 c #501c2b",
+"a7D c #50243c",
+"aDP c #50263d",
+"aLT c #50263e",
+"a56 c #502a40",
+".7d c #502f38",
+".O# c #503238",
+"a2q c #503346",
+"aUm c #50355a",
+".EB c #50363b",
+".N1 c #50393b",
+"aEX c #503c47",
+".B3 c #504444",
+"aJ6 c #504750",
+".Uk c #504946",
+".jN c #505057",
+".yD c #505357",
+"bSA c #505384",
+".By c #505a61",
+".Bu c #505a62",
+"acK c #505a63",
+".Bx c #505b62",
+"#wG c #505b63",
+"#M1 c #505c65",
+"bVK c #505c66",
+"akf c #505d65",
+"a.A c #505d66",
+"bbv c #505d67",
+"aPT c #50606b",
+"aIJ c #50607e",
+"bIr c #506291",
+"#CY c #50648c",
+"#IO c #506572",
+"bA7 c #50668d",
+"ag7 c #506776",
+"cw8 c #50687c",
+"azk c #50697a",
+"cl9 c #506a83",
+"#BK c #506a94",
+"bbO c #506b7a",
+"cjp c #506b83",
+"bGb c #507193",
+"bE7 c #507194",
+"b82 c #50719d",
+"akt c #50719f",
+"bCs c #507295",
+"bjI c #507493",
+"aLI c #50759e",
+"bEy c #5077a4",
+"b63 c #5078a4",
+"agn c #5078a5",
+"crJ c #5083a9",
+"ciC c #508ab4",
+"c#E c #508ab5",
+"#YY c #508bb6",
+"#U. c #508cb7",
+"cvY c #508db7",
+"#YX c #508fbb",
+"bTz c #5090b4",
+".nF c #5090bd",
+".Se c #5091be",
+"#fA c #5091e6",
+"bV6 c #5093bc",
+"aOO c #5094be",
+"aPD c #5095be",
+"bUu c #5095c1",
+"bMD c #5096c1",
+"bdj c #5097c3",
+"cdx c #5098c7",
+"#Q2 c #5099c6",
+"crB c #5099c7",
+"clq c #5099c8",
+"aqb c #509bcb",
+"#SG c #509bcd",
+"#lD c #50e2e0",
+"#n5 c #511c2b",
+"aPb c #51283c",
+"#Dx c #512a3d",
+"aGc c #512a40",
+"aZ8 c #512a41",
+"blL c #512c43",
+"bM8 c #512d4d",
+"bmw c #513044",
+".5j c #513138",
+".KV c #513139",
+"aFJ c #513257",
+"aNU c #51345a",
+"#kQ c #51353f",
+".Mu c #51363a",
+"#vb c #513640",
+"bOp c #51365f",
+".Kv c #513754",
+".IS c #51383b",
+".GI c #513c3d",
+"atu c #51406c",
+".uZ c #514148",
+"azd c #51414d",
+"bkc c #514951",
+".3D c #514c50",
+"#OA c #514c57",
+"bD# c #515282",
+"bT1 c #515384",
+"#SJ c #515960",
+"bed c #515963",
+".Bv c #515b62",
+".Fl c #515b63",
+"auB c #515b64",
+".Bw c #515c63",
+"#Xr c #515c64",
+"a4R c #515c65",
+"#Vy c #515d66",
+"bwk c #515d67",
+"cdU c #515e67",
+"acT c #515f66",
+"#XV c #515f68",
+"aPQ c #51616c",
+"bzX c #516791",
+"cou c #51687d",
+"ayR c #516a7a",
+"chm c #516a81",
+"ceO c #516a83",
+"#Hq c #516c7d",
+"#2O c #516c7e",
+"coI c #516d84",
+"b60 c #5178a4",
+"ctk c #5182a7",
+"ad. c #5184b0",
+"bFw c #5184b1",
+"#X6 c #518ab4",
+"#VW c #518baa",
+"b0I c #518db2",
+"#fB c #518edf",
+"#6c c #5190b3",
+"ay1 c #5191b5",
+".xl c #5191bd",
+"bHF c #5195be",
+"c#A c #5198c5",
+"ct0 c #519ac8",
+"co. c #519ac9",
+"caV c #519aca",
+"#75 c #519bc9",
+"arn c #519dce",
+"#h7 c #522234",
+"aLP c #52253c",
+"bEG c #52253d",
+".7C c #52283f",
+"aL6 c #52293f",
+"aEC c #522a41",
+".Ap c #522b5b",
+"b#T c #522d42",
+"bIf c #522e4e",
+".GH c #523239",
+"#f7 c #52323a",
+".KN c #523339",
+".Ac c #52335c",
+"aFa c #52355b",
+"aUI c #52374b",
+".A8 c #52383b",
+"#cT c #52424f",
+"ayC c #52426d",
+".IW c #524447",
+"aHz c #524451",
+".FP c #524746",
+"bvc c #524751",
+"bpW c #525167",
+"ahN c #525382",
+".Mi c #525a5f",
+".cH c #525b62",
+".Db c #525c63",
+".Lo c #525c64",
+".RW c #525d63",
+".Dp c #525d64",
+"#Yg c #525d65",
+"aiH c #525d66",
+"#FQ c #525d86",
+"a1d c #525e66",
+"#0a c #525e67",
+".Xu c #525f66",
+"#9T c #525f67",
+"aOh c #525f68",
+"##z c #525f69",
+"a48 c #526068",
+"ayW c #526069",
+"bi2 c #52606a",
+"aRd c #52626c",
+".2t c #526470",
+"aLJ c #52658c",
+"#IN c #526674",
+"ci0 c #52667a",
+"bgq c #526a79",
+"#PZ c #526b7d",
+"bIL c #526b83",
+"ckU c #526b84",
+"bgb c #526c82",
+"cjF c #526c84",
+"bru c #526d84",
+"ckp c #526e83",
+"chL c #526e86",
+"b.P c #52709a",
+"bIK c #527294",
+"bGN c #52729e",
+"bZk c #527597",
+"a7W c #527aa0",
+"bA0 c #5283b0",
+"abK c #5284b1",
+"aw1 c #528aa9",
+"#Sr c #528ab5",
+"cuZ c #528db6",
+"ct5 c #528db7",
+".yw c #5291be",
+".HX c #5292bd",
+"#fz c #5292e7",
+"#VM c #5293c0",
+"bSf c #5296bb",
+"bQT c #5296be",
+"bQy c #5297bf",
+"b31 c #5297c1",
+"bMt c #5298c3",
+".g2 c #5298c6",
+"co# c #5299c6",
+"ciE c #5299c8",
+"cpq c #529ac9",
+"b8P c #529bc9",
+"#7a c #529bca",
+"csn c #529cca",
+"#0r c #529dcc",
+"#k. c #52f8f6",
+"#h5 c #531d2c",
+"#si c #531d2d",
+"b.B c #53263e",
+"aG4 c #532940",
+"adR c #532e44",
+".S8 c #533139",
+"#aP c #53313a",
+".3s c #53323a",
+"#el c #53333a",
+"aVO c #533347",
+".7a c #533842",
+".UX c #534042",
+"apq c #534450",
+"atR c #534451",
+"aQB c #534770",
+"aNl c #534a52",
+"a0F c #534d80",
+".4J c #534f3a",
+"af5 c #534f58",
+"aMZ c #53527c",
+"adl c #535585",
+"#iX c #535b62",
+".Pv c #535d62",
+".EY c #535d64",
+".P7 c #535d65",
+"bQg c #535e64",
+".Fk c #535e65",
+"#1b c #535e66",
+"au. c #535e67",
+"bG# c #535f68",
+"#1W c #536068",
+"b#5 c #536069",
+"a9i c #53606a",
+"cjT c #536077",
+"#X1 c #536168",
+"aoA c #53616a",
+"awg c #53616b",
+"aG8 c #53626b",
+"bRp c #536590",
+"aRQ c #536690",
+"bp8 c #53687a",
+"#nm c #536a50",
+"cmn c #536c84",
+"cf0 c #536d85",
+"chO c #536e87",
+"b7m c #536e97",
+"bJ3 c #536f8a",
+"bCr c #537394",
+"bjY c #537595",
+"aET c #5375a1",
+"#RM c #5383a0",
+"cpB c #5387ae",
+"#So c #5389b1",
+"cr0 c #5389b3",
+"csP c #538ab4",
+"bW6 c #538bb0",
+"b## c #538dac",
+"a9t c #538fb0",
+"#3Z c #5390b3",
+"#0E c #5393bc",
+".BW c #5393be",
+"#b2 c #5393e7",
+"#dI c #5393e8",
+"bTs c #5395b7",
+"cu. c #5396c2",
+"bHV c #5399c3",
+"#YZ c #539ac7",
+".bB c #539cca",
+"cki c #539dcb",
+"arm c #539ecf",
+"#fT c #541d2d",
+"#uf c #541d2e",
+"#re c #542139",
+".Vc c #54233b",
+"#p4 c #542840",
+"aSU c #542b43",
+"aTp c #542c43",
+"bLP c #542d4d",
+".Rm c #54323a",
+".CR c #54323b",
+"aIc c #543258",
+".CQ c #54333d",
+"aTj c #543347",
+"aMr c #543348",
+"blS c #543448",
+"#t6 c #543842",
+"aug c #543d68",
+"alC c #543e68",
+"a8O c #54404c",
+"axN c #54404d",
+"bF5 c #54414d",
+"cxD c #544555",
+"aek c #544754",
+"b5K c #544c6a",
+"#RG c #544f59",
+"beD c #544f81",
+".4y c #54504c",
+"aLK c #545375",
+"bRs c #54567b",
+"aaX c #54597c",
+".Ev c #545b60",
+"#7U c #545e65",
+".Jn c #545e66",
+".kr c #545f65",
+".Do c #545f66",
+"at1 c #545f67",
+"axv c #545f68",
+"#yC c #546068",
+"#HO c #546169",
+"bzm c #54616a",
+"bp5 c #54616b",
+"a.E c #54626b",
+"#3z c #54636d",
+"bD8 c #546490",
+".Y9 c #546571",
+"bUi c #546572",
+"bhA c #546c80",
+"cjK c #546d85",
+"ceD c #546d86",
+"cew c #546e86",
+"cjB c #546e87",
+"#FC c #546f7f",
+"cfB c #546f85",
+"ckZ c #547088",
+"b6X c #54729d",
+"#o2 c #547762",
+"#VN c #5488b1",
+"#U6 c #5489b5",
+"cdH c #548cac",
+"c#O c #548dad",
+"#dH c #5492e6",
+".FF c #5493be",
+".fa c #5494bf",
+"#b1 c #5494e6",
+"bTr c #5496b8",
+"bTp c #5496b9",
+"#b4 c #5496ec",
+"bTn c #5497b8",
+"bTo c #5497b9",
+"bTt c #5497ba",
+"bTu c #5498ba",
+"bRR c #5499bf",
+"bPV c #549ac3",
+"bPc c #549bc2",
+"b#G c #549bc5",
+"bLW c #549cc7",
+"ccc c #549cc9",
+"#Oa c #549ecb",
+"caW c #549ecc",
+"ce4 c #549ecd",
+"#PE c #549fcb",
+"#Qj c #549fcc",
+"ci# c #549fcd",
+"c#C c #549fce",
+"cmM c #54a4d3",
+"#g5 c #54fffb",
+"#hC c #551c2d",
+"#gm c #551d2c",
+"#jC c #551d2d",
+"#i9 c #551d2e",
+"#ug c #551e2e",
+"bci c #55253d",
+"a97 c #55263e",
+"aU9 c #552940",
+"aGa c #552e50",
+"aHR c #553145",
+".IR c #55323b",
+"aN0 c #553359",
+".CW c #55353c",
+".NZ c #55353e",
+"#w# c #553944",
+"bGX c #553c5d",
+".1D c #553e45",
+"#mc c #55404b",
+"bGY c #554060",
+"cgv c #55434f",
+"#Ng c #554350",
+"aLg c #554452",
+".PK c #554d4f",
+".QD c #554d6a",
+".fs c #554e56",
+"aeU c #555181",
+"#E8 c #555a81",
+".UM c #555c60",
+"#py c #555d64",
+".8C c #555f65",
+".Dc c #555f66",
+".18 c #555f67",
+".EZ c #556067",
+"#Xo c #556068",
+"#BU c #55616a",
+"a86 c #55626b",
+"ar8 c #55636b",
+"bwm c #55646d",
+"bye c #556592",
+"b8G c #556793",
+"coC c #556d85",
+"cmb c #556d86",
+"axT c #556e7f",
+"cmu c #556e86",
+"cnI c #556e87",
+"cmh c #557088",
+"cjy c #557189",
+"#dG c #5595e5",
+"#b3 c #5595ea",
+"bTv c #5596b8",
+".T1 c #5596c1",
+"bTq c #5597b9",
+"bTy c #5597ba",
+"bTw c #5597bb",
+"#b0 c #5597e6",
+"bNJ c #559ac3",
+"bJc c #559dc7",
+"#92 c #559dc8",
+"cvF c #559fcc",
+"cso c #559fcd",
+"#O0 c #55a0cd",
+"cir c #55a0ce",
+"csr c #55a1ce",
+"c.e c #55a5d3",
+"#hD c #561d2d",
+"#pR c #561e2e",
+"a8V c #56253e",
+"aLN c #562a43",
+"aD0 c #562b44",
+"aYW c #562c44",
+"#yN c #562d41",
+".Mt c #56333b",
+"#hR c #56373f",
+"aaC c #563a5d",
+".y9 c #564041",
+"aLL c #564262",
+"azF c #564653",
+".s1 c #564a55",
+".PN c #564c4e",
+"aJ3 c #564c54",
+"#9U c #56525b",
+"a5O c #565585",
+"#U9 c #56565f",
+".A# c #56585c",
+"aca c #56587c",
+".CL c #565c62",
+".yZ c #565e65",
+".Wk c #565f65",
+".Fj c #566067",
+"#59 c #566069",
+".R8 c #566167",
+".Dd c #566168",
+"afM c #566169",
+"bUf c #56616a",
+"aha c #56636a",
+"afy c #56646c",
+"a9A c #56646e",
+"ax0 c #56656e",
+"bK4 c #566571",
+"cnK c #566f87",
+"ceP c #566f88",
+"#Qw c #567081",
+"bAZ c #56709c",
+"ceG c #567189",
+"cmm c #56728b",
+"a3u c #567794",
+"a#d c #5680ac",
+"ciB c #5688ad",
+"cii c #568bb6",
+"bCf c #5694b5",
+".z1 c #5694c0",
+"#VL c #5695bf",
+".BX c #5695c1",
+"bTx c #5699bd",
+"bS8 c #569abf",
+"bR7 c #569bbf",
+"bQp c #569dc4",
+"bKj c #569dc6",
+"bME c #569ec8",
+"aNc c #569fc9",
+"aw2 c #569fcc",
+"#Y0 c #56a0cd",
+"cdp c #56a0ce",
+"#PF c #56a1ce",
+"b7b c #56a1cf",
+"Qtz c #56a1d0",
+"cpp c #56a2cf",
+"cic c #56a2d1",
+"#hE c #571e2e",
+"#k7 c #571e2f",
+"#uh c #571e30",
+"aLO c #57263e",
+"aLU c #57273f",
+"aD1 c #572840",
+"a0X c #572941",
+"aWD c #572942",
+"bCP c #572a49",
+"br. c #572c42",
+"aSD c #573156",
+".PH c #57333c",
+"aLM c #57334e",
+".xQ c #57343f",
+".H7 c #573453",
+".MD c #57363d",
+"#mf c #573842",
+".z. c #573e42",
+"au7 c #57404d",
+"#Id c #57414e",
+"abA c #57424f",
+".A7 c #574345",
+"aqD c #574552",
+"aO6 c #574b53",
+".3v c #574c4e",
+"bL4 c #575181",
+"a3s c #575383",
+".m3 c #57555c",
+".E0 c #576168",
+"#6. c #576169",
+".Hf c #576269",
+"ald c #57626a",
+"bEf c #57626b",
+"aaa c #57636b",
+"awe c #57636c",
+"bxu c #57646d",
+"a5b c #57656d",
+"#YP c #57666f",
+"aRe c #576974",
+"bCM c #576a95",
+"#4d c #576c7a",
+"bBY c #576d98",
+"#yj c #576d99",
+"ceQ c #577087",
+"ceS c #577088",
+"aYI c #577187",
+"chF c #57718a",
+"coB c #577289",
+"chQ c #57728b",
+"#iu c #577386",
+"chU c #57738c",
+"bd6 c #577fa3",
+"cwW c #578fb4",
+".yx c #5795bf",
+"bEK c #5796b8",
+"#fy c #5797e5",
+"#aa c #5797e6",
+"#ab c #5798ec",
+"#U2 c #579ec9",
+"bS0 c #579fc8",
+"#Z2 c #579fcc",
+"cw4 c #57a0cb",
+"#Zk c #57a0ce",
+"cvU c #57a2cf",
+"cgp c #57a2d0",
+"#6h c #57a3d0",
+".f4 c #57a4d1",
+".f. c #57a4d2",
+"#SH c #57a5d4",
+"#j. c #581e2f",
+"#n6 c #581f2f",
+"#l0 c #582133",
+"a8m c #58213b",
+"be1 c #58243e",
+"aUx c #58253e",
+"by8 c #58253f",
+"a8W c #58263e",
+"aYK c #582a42",
+"#uU c #582b3e",
+"#8r c #582b43",
+"#Gl c #582f44",
+"#Gi c #583044",
+"bov c #583046",
+"aQF c #583257",
+"aN2 c #583258",
+".EA c #58333c",
+".IZ c #58343d",
+".86 c #58343e",
+"bz0 c #583458",
+"avL c #583549",
+".y7 c #58383f",
+"a#t c #583b5d",
+".y8 c #584143",
+"afo c #584856",
+"brV c #584c55",
+"aBN c #584d56",
+".WE c #584f50",
+".WH c #585052",
+".WF c #585252",
+"#Gr c #585a80",
+"#7Z c #585c64",
+"aLY c #585c86",
+".AQ c #586067",
+".M3 c #586169",
+".Dn c #586269",
+".Dm c #58626a",
+".Fi c #586369",
+".MQ c #58636a",
+"aEI c #58636c",
+"alc c #58646c",
+"#gI c #58646d",
+"bkq c #58656d",
+"awQ c #58656f",
+"b32 c #586591",
+"aQ0 c #586670",
+".0M c #586976",
+".FI c #586b76",
+"b6W c #586b97",
+"cxN c #586f80",
+"bhJ c #586f8d",
+"ckL c #587089",
+"#RF c #58708c",
+"ceC c #587189",
+"ch3 c #58718a",
+"ceI c #58738b",
+"#Pf c #587485",
+"#T0 c #587488",
+"a4i c #587794",
+"#3g c #587888",
+"a7x c #588196",
+"#Tf c #5890b6",
+"#Va c #5895b7",
+"#hi c #5898e3",
+"#dF c #5899e7",
+"#5t c #589ac0",
+".dn c #589cc8",
+"bQI c #589ec3",
+"#Wt c #589eca",
+"bTV c #589fc9",
+"#8Y c #589fcb",
+"bMo c #58a0c8",
+"bN4 c #58a0ca",
+"cw. c #58a1cd",
+"bRL c #58a2c9",
+"#Yy c #58a2d0",
+"cli c #58a3d0",
+"cv# c #58a4ce",
+"#ZL c #58a4d1",
+"c.j c #58a4d2",
+"azz c #58a4d3",
+"cwT c #58a5d3",
+".92 c #58dae4",
+"#kz c #591e2f",
+"#hF c #591f30",
+"#l3 c #591f31",
+"#Gp c #59233c",
+"aVu c #59253e",
+".P0 c #59263f",
+"aOe c #592841",
+"aSz c #592c45",
+"bG2 c #592e49",
+"bPO c #592f53",
+".IQ c #59353e",
+".7c c #59363e",
+"bs6 c #59374b",
+".Kb c #593754",
+"asC c #593761",
+".PR c #59383f",
+"#hP c #593842",
+"#v# c #593a46",
+"#HD c #593d5b",
+"bfH c #59424f",
+".rO c #594552",
+"ayK c #594652",
+"aus c #594f58",
+"aTf c #595082",
+".9b c #595254",
+"bBm c #595281",
+".lA c #596268",
+".De c #59636a",
+".P8 c #59636b",
+"#ux c #59646a",
+".E1 c #59646b",
+"axt c #59646c",
+"aFR c #59646d",
+"a.S c #59656d",
+"#Pi c #59656e",
+"#55 c #59666d",
+"a.U c #59666e",
+"a.C c #59666f",
+"a#0 c #59676f",
+"#8I c #596770",
+"b2k c #596790",
+"ayX c #596871",
+"aQ4 c #596a76",
+"azS c #59707e",
+"ciQ c #597285",
+"cpX c #597289",
+"cjd c #59728a",
+"bcv c #597299",
+"cnv c #59738b",
+"ar5 c #597587",
+"cu0 c #598eb4",
+"a.6 c #5994bb",
+".hZ c #5999c4",
+"bS7 c #599ec2",
+"bIV c #59a2cb",
+"b7. c #59a3cd",
+"bV2 c #59a4cf",
+"cvQ c #59a4d2",
+"cke c #59a5d2",
+"ckj c #59a5d3",
+"aAE c #59a6d4",
+"aAz c #59a7d4",
+"aqc c #59a7d8",
+"cu# c #59a8d4",
+"cka c #59a8d6",
+"##W c #59f7fd",
+"#fU c #5a1f30",
+"#r# c #5a1f31",
+"a80 c #5a243e",
+"aYk c #5a253e",
+"#od c #5a2640",
+".15 c #5a2740",
+"bFL c #5a2741",
+"#ur c #5a2841",
+"aVn c #5a2c45",
+"bql c #5a2f47",
+"aEt c #5a3156",
+".E# c #5a315e",
+"aU0 c #5a3348",
+"aHH c #5a3449",
+".EG c #5a353e",
+".A2 c #5a353f",
+"#jk c #5a363f",
+"bt. c #5a374b",
+"aRC c #5a3a57",
+"akw c #5a3a64",
+"aDI c #5a4451",
+".#G c #5a4552",
+".mh c #5a4753",
+"aqX c #5a4a58",
+"aer c #5a4e56",
+".ES c #5a4f57",
+"##. c #5a5256",
+"a4Z c #5a5484",
+"a7z c #5a5760",
+".B4 c #5a5a5e",
+".NQ c #5a6368",
+"#nq c #5a6369",
+"#64 c #5a636a",
+"#o5 c #5a6469",
+".iR c #5a646a",
+".Df c #5a646b",
+"#yD c #5a646c",
+".Oo c #5a656b",
+".sI c #5a656c",
+"ap5 c #5a656d",
+"a1c c #5a656e",
+"crk c #5a6590",
+"a7q c #5a666e",
+"afw c #5a666f",
+"#Mh c #5a676f",
+"bah c #5a6870",
+"a4. c #5a6871",
+"au# c #5a6872",
+"aEf c #5a6e9a",
+"b7p c #5a6f82",
+"cns c #5a718a",
+"cq6 c #5a7289",
+"cg6 c #5a7386",
+"cov c #5a738a",
+"chq c #5a748c",
+"coJ c #5a758c",
+"ckW c #5a758d",
+"bzo c #5a7a9a",
+"b7d c #5a89b3",
+"#Uf c #5a8bb6",
+"cpx c #5a8fb4",
+".mp c #5a97c0",
+".DM c #5a97c1",
+"#fC c #5a99e2",
+"#hg c #5a99e5",
+"#hh c #5a99e6",
+"b#H c #5a9bbe",
+".nD c #5a9bc6",
+"bSa c #5aa0c4",
+"bSb c #5aa0c5",
+"bUw c #5aa2c9",
+"bPn c #5aa2ca",
+"bHG c #5aa2cb",
+"bVq c #5aa4ce",
+"bHU c #5aa5cf",
+"cck c #5aa6d3",
+"ct. c #5aa6d4",
+"cgA c #5aa7d3",
+"c#D c #5aa7d4",
+".el c #5aa8d6",
+"#na c #5ad0cb",
+"#du c #5afdfc",
+"#iz c #5afef9",
+"#kE c #5b1f31",
+"#nD c #5b2031",
+"#ui c #5b2032",
+"#vh c #5b2132",
+".J# c #5b243e",
+"aKJ c #5b253f",
+"aVT c #5b2841",
+"aME c #5b2842",
+"acp c #5b2b44",
+"ag3 c #5b2b45",
+"brJ c #5b2e44",
+"bYY c #5b3356",
+"bb# c #5b3449",
+".GG c #5b353f",
+"bbI c #5b3549",
+".z# c #5b3f43",
+"aEj c #5b414d",
+"apF c #5b4957",
+"aPh c #5b4b73",
+".oa c #5b4e55",
+"#Ej c #5b4e72",
+"aHs c #5b4f56",
+".de c #5b5057",
+".yX c #5b6268",
+".Dl c #5b656c",
+".RP c #5b656d",
+"bxm c #5b658f",
+".Dg c #5b666c",
+"#su c #5b666d",
+"#47 c #5b666e",
+"bCp c #5b666f",
+"ber c #5b6770",
+"bH6 c #5b6791",
+"brZ c #5b6870",
+"#wF c #5b6871",
+"aiL c #5b6971",
+"aAX c #5b6972",
+"#2S c #5b6973",
+"ba3 c #5b6a72",
+"apU c #5b6a74",
+"avu c #5b6b74",
+"aPS c #5b6c78",
+"chX c #5b738b",
+"cm. c #5b738c",
+"cnw c #5b758c",
+"cf7 c #5b768e",
+"ck3 c #5b768f",
+"a8C c #5b83a6",
+"#ag c #5b8ab7",
+"css c #5b8fb3",
+"aoW c #5b95c2",
+"bbz c #5b9abc",
+"#hf c #5b9ae6",
+"#dE c #5b9ce7",
+"b2h c #5b9ec6",
+"bS# c #5ba0c4",
+"bS. c #5ba0c5",
+"bR9 c #5ba1c4",
+"bR8 c #5ba1c5",
+"bSc c #5ba1c6",
+"bSd c #5ba2c7",
+"bXF c #5ba5cf",
+"caS c #5ba6d1",
+"#O1 c #5ba7d4",
+"cuT c #5ba7d5",
+"cqD c #5ba8d5",
+"cjS c #5ba9d6",
+"#oW c #5baca1",
+"#Vb c #5baee0",
+"#nE c #5c2031",
+"#l2 c #5c2032",
+"#gn c #5c2133",
+"aGS c #5c253f",
+".yO c #5c2565",
+".Tr c #5c2640",
+"bG4 c #5c2c4c",
+"#Ec c #5c2d42",
+"bpo c #5c2f44",
+"a0c c #5c3056",
+"aEy c #5c3157",
+"aXg c #5c3449",
+".S7 c #5c363f",
+"#ek c #5c373f",
+"bGW c #5c3857",
+"awn c #5c3861",
+".Oa c #5c3b41",
+".A3 c #5c3c43",
+"bDP c #5c3c50",
+".LP c #5c3d57",
+"#cC c #5c4548",
+"##k c #5c4654",
+".iQ c #5c4b55",
+"aFr c #5c4c57",
+".p# c #5c4e56",
+"a.4 c #5c4f56",
+"cr3 c #5c5065",
+"bY9 c #5c5578",
+".tP c #5c585f",
+"#pw c #5c656b",
+"#Jz c #5c656c",
+".Fh c #5c666c",
+".Dh c #5c666d",
+"bsE c #5c666e",
+".E2 c #5c676d",
+"#Z. c #5c676e",
+".7R c #5c676f",
+"a87 c #5c6870",
+"aEG c #5c6871",
+"a#X c #5c6972",
+"be. c #5c6a73",
+"aFj c #5c6b74",
+"ad0 c #5c6b75",
+"a3k c #5c6c75",
+"aW3 c #5c6e7a",
+"aSc c #5c6f7a",
+"cna c #5c6f82",
+"#MY c #5c7281",
+"cq7 c #5c738b",
+"ceR c #5c748c",
+"cmo c #5c748d",
+"chI c #5c758e",
+"chJ c #5c768e",
+"a5R c #5c7990",
+"aZ2 c #5c7a94",
+"aAS c #5c7c8e",
+"cqI c #5c8fb2",
+"#.h c #5c9be7",
+"#he c #5c9de6",
+"#bZ c #5c9de7",
+"bSe c #5ca4c8",
+"bS6 c #5ca4ca",
+"bMs c #5ca6cf",
+"bPx c #5ca7ce",
+"bXE c #5ca7d1",
+"az0 c #5ca7d2",
+"bO. c #5ca9d2",
+"b8O c #5ca9d7",
+"az4 c #5caad8",
+"ctm c #5cabd6",
+"#bN c #5cfafc",
+"#fo c #5cfef9",
+"#nz c #5d2032",
+".9z c #5d253f",
+"#y6 c #5d2639",
+"b.A c #5d2640",
+"a0M c #5d2841",
+"a0N c #5d2842",
+"bms c #5d2a43",
+"#rd c #5d2b3f",
+"aQQ c #5d2b44",
+"b8A c #5d2c4a",
+"aRJ c #5d3055",
+"aN1 c #5d3155",
+"aNx c #5d3448",
+"bbF c #5d344a",
+".KM c #5d363e",
+".N0 c #5d3740",
+".y6 c #5d3741",
+"ags c #5d3761",
+".5i c #5d3841",
+"bEB c #5d384e",
+".KW c #5d3941",
+".Ns c #5d3f59",
+"#Nm c #5d4150",
+".H5 c #5d416a",
+"#N0 c #5d4855",
+"asY c #5d4a59",
+"c.q c #5d4e6c",
+"aA# c #5d5158",
+"bzY c #5d547a",
+".eE c #5d555b",
+"b8E c #5d5e88",
+"aJM c #5d6163",
+"bgG c #5d628e",
+".AG c #5d666b",
+".i. c #5d666c",
+".Dk c #5d676e",
+"#3L c #5d676f",
+".Di c #5d686e",
+"#tw c #5d6870",
+"av9 c #5d6970",
+"#C9 c #5d6972",
+"b.l c #5d6a73",
+"bcn c #5d6b73",
+"b.U c #5d6b74",
+"ayS c #5d6b75",
+"aQ9 c #5d6f7a",
+"c.L c #5d7485",
+"ceN c #5d758e",
+"bh. c #5d7686",
+"coH c #5d768e",
+"ceK c #5d778f",
+"ceH c #5d788f",
+"akW c #5d7a8c",
+"a42 c #5d7a92",
+"a19 c #5d7b94",
+"abF c #5d7ea5",
+"cgR c #5d8daf",
+"cgS c #5d90b5",
+".mM c #5d95be",
+".cd c #5d9ac3",
+"#a# c #5d9ae5",
+"bgf c #5d9ec8",
+"#fx c #5d9ee5",
+"ayv c #5da0c3",
+"bQS c #5da5cb",
+"bV5 c #5da8d0",
+"bMF c #5da8d1",
+"bTX c #5da8d2",
+"bZy c #5da9d3",
+"bNa c #5da9d5",
+"cgo c #5daad7",
+"cmY c #5dabd8",
+"#l9 c #5e2032",
+"#td c #5e2132",
+"#uj c #5e2133",
+"bbg c #5e243f",
+"bC1 c #5e253f",
+".MN c #5e2641",
+"a8Y c #5e2741",
+"aFM c #5e2842",
+"#o# c #5e293d",
+"aSw c #5e2943",
+".yG c #5e2966",
+"aa5 c #5e2a44",
+"#xm c #5e2a4a",
+"bmo c #5e2b44",
+"#s2 c #5e3040",
+"aYQ c #5e3056",
+"aX0 c #5e3349",
+".GO c #5e3741",
+"#cB c #5e3840",
+"ahW c #5e3860",
+"amM c #5e3960",
+"#nP c #5e3a44",
+"aM0 c #5e3a62",
+".A9 c #5e3b42",
+".PS c #5e3c43",
+"bkT c #5e4a5a",
+"#Ck c #5e4d72",
+"aP1 c #5e5058",
+"bNk c #5e5080",
+"#z9 c #5e567c",
+"aaS c #5e5b7c",
+"#kZ c #5e6062",
+"b5v c #5e608a",
+"#d3 c #5e6165",
+".ZP c #5e6366",
+"#o7 c #5e666c",
+".Fg c #5e686e",
+".Dj c #5e686f",
+".Op c #5e696f",
+"aT0 c #5e6a72",
+"aej c #5e6b71",
+"#c0 c #5e6b73",
+"a6n c #5e6b74",
+"av4 c #5e6d76",
+"asj c #5e6d77",
+"bdK c #5e6e77",
+"aRf c #5e707c",
+".yz c #5e727d",
+"aZ# c #5e7380",
+"cgg c #5e768e",
+"chn c #5e778e",
+"chw c #5e778f",
+"#Ro c #5e7889",
+"cmj c #5e7991",
+"aaq c #5e7aa5",
+"at# c #5e7b8d",
+"a6M c #5e96be",
+"bS5 c #5ea6cc",
+"#Ul c #5ea7d0",
+"bQJ c #5ea8ce",
+"bRh c #5eaad4",
+".6d c #5eabbc",
+"bML c #5eacd5",
+"bLv c #5eacd6",
+"csk c #5eacd8",
+"cfb c #5eacd9",
+"ct3 c #5eadda",
+"ciF c #5eafdc",
+"#n7 c #5f2133",
+"#ny c #5f2134",
+"#hG c #5f2335",
+"a6Z c #5f2540",
+"aLV c #5f2640",
+"a5p c #5f2742",
+"a7A c #5f2842",
+"a.w c #5f2a45",
+"bW1 c #5f2a49",
+"bRa c #5f2b4b",
+"aQl c #5f2f54",
+"aZw c #5f3055",
+"bm8 c #5f3349",
+".Rl c #5f3841",
+"ahY c #5f3861",
+".1B c #5f3941",
+".3r c #5f3942",
+"bDO c #5f3c4f",
+".xR c #5f3e44",
+".P# c #5f445e",
+"cs5 c #5f4958",
+"bxt c #5f5058",
+"bIB c #5f545e",
+"#Pr c #5f5560",
+"bT6 c #5f5a7c",
+"#zk c #5f5b85",
+"b6Z c #5f5d87",
+"abk c #5f5f67",
+"#kU c #5f6264",
+"bG. c #5f6870",
+".E3 c #5f6970",
+".La c #5f6a70",
+"#rF c #5f6a72",
+"ale c #5f6c75",
+"#Zz c #5f6d75",
+"b.. c #5f6d77",
+"anG c #5f6e76",
+"aAs c #5f6e77",
+"aeD c #5f709a",
+"aS5 c #5f727e",
+"aWk c #5f737f",
+"a0w c #5f7380",
+"aUQ c #5f7480",
+"a0x c #5f7683",
+"aZa c #5f7783",
+"ck8 c #5f778f",
+"chy c #5f7790",
+"btm c #5f788d",
+"cjI c #5f7890",
+"ch4 c #5f7891",
+"cnF c #5f7991",
+"bfK c #5f7b8d",
+"a6A c #5f7c93",
+"ayN c #5f8093",
+"a9g c #5f84a6",
+"cqg c #5f8bb4",
+"#SI c #5f8da8",
+"cvZ c #5f90b1",
+".oK c #5f99c2",
+".lM c #5f9fc8",
+"cct c #5fa2c6",
+"arw c #5fa2cd",
+"#Tt c #5fa3c8",
+"bRQ c #5fa8cd",
+"bQN c #5fa9ce",
+"#5p c #5faad3",
+"bNP c #5fabd3",
+"b1e c #5facd5",
+"ccf c #5fadda",
+"cgs c #5faeda",
+"#7# c #5faedb",
+"bdk c #5faedc",
+"#pn c #602133",
+"#qM c #602134",
+"#pi c #602234",
+"aUw c #60223d",
+"#j# c #602335",
+"aDQ c #602540",
+"a8d c #602842",
+"boo c #602944",
+"bOr c #602b4c",
+"bq1 c #60354c",
+".CX c #603841",
+".ZY c #603842",
+"#.W c #603942",
+".Rk c #603a44",
+".1z c #603b46",
+".Nl c #604a70",
+"arV c #604b59",
+"#G0 c #604f76",
+".hl c #605157",
+"bgF c #605180",
+"bqD c #60525a",
+".x8 c #60525b",
+"bEd c #60545c",
+"a2R c #605482",
+"bON c #60587a",
+"aJy c #605980",
+"azC c #605c7c",
+".Av c #60696f",
+"#b. c #606a70",
+".Jo c #606a71",
+".Hg c #606b71",
+"as. c #606b72",
+"#8x c #606b73",
+"#Wf c #606b74",
+".sT c #606c73",
+"a9C c #606c74",
+"#oK c #606c75",
+"a9S c #606d76",
+"aq6 c #606e77",
+"b#z c #606f78",
+"bil c #60707c",
+"#8k c #607194",
+"aTX c #607480",
+"aWl c #607481",
+"aUP c #607581",
+"aXO c #607682",
+"b7r c #607688",
+"aYw c #607783",
+"cht c #607890",
+"chx c #607992",
+"ceJ c #607a92",
+"chN c #607b93",
+"aq5 c #607d8f",
+"bDk c #607d9a",
+"bj# c #60859f",
+"ciA c #6089a7",
+"cgQ c #608ba8",
+"#Vc c #608bb1",
+"cu1 c #608fb1",
+".wr c #609dc8",
+"#hj c #609de0",
+".VE c #609ec7",
+"#a. c #609fe6",
+"#68 c #60a7ce",
+"bQM c #60a9ce",
+"bQL c #60a9cf",
+"bQO c #60aace",
+"bQK c #60aacf",
+"bS4 c #60aad0",
+"bN5 c #60abd3",
+"b3Z c #60acd7",
+"bZx c #60add5",
+"bHH c #60add6",
+"b#. c #60add8",
+"bJb c #60aed9",
+"bHT c #60aeda",
+"csm c #60afdb",
+"clB c #60afdc",
+".do c #60b0dc",
+"aqd c #60b0de",
+"bcN c #60b0df",
+"#l1 c #612134",
+"#jD c #612233",
+"#fV c #612335",
+"#kF c #612436",
+"#oc c #612540",
+"aFf c #612742",
+"a3D c #612843",
+"bm1 c #612943",
+"aWa c #612a45",
+"bxZ c #612f4f",
+"anN c #613058",
+"bFE c #61324a",
+"axJ c #61354b",
+"aew c #61355c",
+".Ms c #613942",
+"b8D c #61395d",
+"byN c #613a5c",
+"#je c #613e4c",
+"#w8 c #61404b",
+"aJn c #614152",
+"#jl c #61444b",
+".QN c #61475f",
+"aqw c #614c58",
+"aL# c #615055",
+"bOP c #61555d",
+"aFq c #615870",
+"bAY c #615982",
+"b6Y c #615c86",
+".Eh c #616a70",
+".Tv c #616b72",
+"anw c #616c73",
+"#mF c #616c74",
+"#yy c #616c75",
+"#Mb c #616d76",
+"awa c #616e76",
+"acL c #616f78",
+"ayT c #616f79",
+"a9c c #616f96",
+"#2f c #617079",
+"a70 c #617581",
+"ba1 c #617582",
+"aTY c #617682",
+"aXN c #617783",
+"bwo c #61798d",
+"cgh c #617992",
+"ceA c #617a91",
+"ch1 c #617a92",
+"chS c #617c93",
+"be4 c #617d8f",
+"#yK c #618092",
+"#b7 c #6199d0",
+"#bY c #61a1e7",
+"ca5 c #61a3c6",
+"#dD c #61a3e7",
+"bQP c #61aacf",
+"bQw c #61abd1",
+"bRP c #61acd1",
+"bRO c #61acd2",
+"bPo c #61aed3",
+"aSO c #61aed9",
+"cdw c #61b0dc",
+"cuE c #61b0dd",
+"#74 c #61b1de",
+"#po c #622234",
+"#qL c #622235",
+"#nx c #622336",
+"a2# c #622842",
+"aUC c #622944",
+"bVl c #62294b",
+"aIl c #622a4a",
+"#6G c #622e53",
+"aVZ c #622f53",
+"aMq c #62364b",
+".85 c #623a43",
+".Yj c #623a44",
+"avV c #624553",
+"#OO c #624b59",
+"agy c #62507b",
+"arI c #62555e",
+"bEz c #62567f",
+"b5w c #625a85",
+".rH c #625c62",
+"#ev c #626465",
+"#fH c #626927",
+".E4 c #626c72",
+".Hh c #626c73",
+"#Xq c #626c74",
+".kC c #626c79",
+"#4n c #626d74",
+"#D# c #626d75",
+"bxv c #626d76",
+"#m4 c #626e76",
+"btj c #626e77",
+"brW c #626f77",
+"#Ba c #626f78",
+"anv c #627077",
+"#WD c #627078",
+"a1a c #627079",
+"aFk c #62707a",
+"abb c #62717a",
+"aRg c #62747f",
+"bjM c #627681",
+"aW5 c #627682",
+"bjN c #627683",
+"ckC c #627689",
+"aYu c #627783",
+"cfY c #627a92",
+"ckK c #627b93",
+"ch5 c #627b94",
+"cmi c #627c94",
+"auz c #628092",
+"baI c #628395",
+"csV c #6285ab",
+"b.Z c #629bc1",
+".FE c #629cc4",
+"#.g c #629fe6",
+"bDX c #62a7cc",
+"bQR c #62abd0",
+"bQQ c #62acd1",
+"bQx c #62acd2",
+"bY2 c #62add6",
+"bUv c #62aed5",
+"bO8 c #62afd7",
+"caQ c #62b0db",
+"bJH c #62b1dd",
+"ce5 c #62b2de",
+"aAD c #62b2df",
+"#ZH c #62b6e2",
+"#k# c #62fdf9",
+"#z. c #631c38",
+"#h6 c #632234",
+"#pS c #632235",
+"#k8 c #632335",
+"#ph c #632336",
+".0p c #632642",
+"aOR c #632742",
+"a0j c #632743",
+"b8C c #632848",
+"a24 c #632e52",
+"aM1 c #632f54",
+"aJf c #633147",
+"blR c #63324b",
+"bjy c #63374f",
+".I0 c #633b45",
+".ME c #633f46",
+"#nQ c #63414a",
+".#Y c #63414e",
+"a8n c #63495a",
+"aqC c #634b58",
+"a#h c #634c7c",
+"bzd c #63517f",
+".X1 c #63534e",
+".IT c #635354",
+".rQ c #63535b",
+"aKM c #63567e",
+"bKO c #63678f",
+"a05 c #636973",
+".py c #636c72",
+".Ff c #636c73",
+"#7L c #636c74",
+".E5 c #636d73",
+".HG c #636d74",
+"#f. c #636d75",
+"a4v c #636e75",
+"aQ1 c #636e76",
+"##q c #636f78",
+"bi. c #636f90",
+"aYs c #637079",
+"aAr c #637179",
+"awP c #63717a",
+"buC c #63717b",
+"#0d c #63727b",
+"acx c #63727c",
+"aXM c #637581",
+"aPR c #637681",
+"a7m c #637682",
+"aVD c #637783",
+"aS7 c #637784",
+"aS6 c #637884",
+"aBy c #637887",
+"bjS c #637889",
+"a.a c #637aa3",
+"cfV c #637b91",
+"coy c #637b92",
+"ch0 c #637b94",
+"cmf c #637c94",
+"#83 c #637ca7",
+"coN c #637d94",
+"awN c #638093",
+"b2u c #63809c",
+"bk3 c #638199",
+"#Rp c #63859a",
+"bdF c #6387a5",
+"c.m c #6388af",
+"bjU c #6394b3",
+"#hd c #63a6e5",
+"ca3 c #63a7cd",
+"#fw c #63a9e6",
+"bQv c #63aed4",
+"bRN c #63afd4",
+"bQu c #63afd5",
+"b0H c #63afd8",
+"bS3 c #63b0d7",
+"bLj c #63b1da",
+"bLk c #63b1db",
+"ckd c #63b2de",
+"csl c #63b3df",
+"cie c #63b3e0",
+"#6g c #63b4df",
+".#1 c #63b4e0",
+"az1 c #63b5e2",
+"#z3 c #641c38",
+"#vi c #642336",
+"#m. c #642537",
+"aY2 c #642641",
+"aEB c #642742",
+"aJG c #642743",
+"aft c #642945",
+"aUi c #642d50",
+"aX6 c #642d51",
+"aNV c #642e52",
+"bau c #643148",
+"aTK c #64314a",
+"bDN c #64364e",
+".Wz c #643a45",
+".Wy c #643b46",
+".za c #644248",
+"bxf c #644453",
+"#Bt c #644652",
+".xS c #64494b",
+"aSo c #644a79",
+"#zi c #644f74",
+"b3S c #645179",
+"#yi c #64557e",
+"aah c #64565c",
+"atJ c #64565e",
+".eF c #645a61",
+"ago c #645a82",
+"amB c #645c65",
+".wt c #645f65",
+"#Sx c #646070",
+"#2C c #64676e",
+"bP5 c #64678f",
+"aDF c #646792",
+"#.K c #64686b",
+".E6 c #646d74",
+".E7 c #646e74",
+".Lb c #646e75",
+"#Yj c #646e76",
+"avk c #646e77",
+"#3u c #646f76",
+"#H. c #646f77",
+"bIH c #647078",
+"abt c #647179",
+"b.m c #64737c",
+"aSf c #647782",
+"aRh c #647783",
+"a6C c #647883",
+"aSd c #647884",
+"aW4 c #647985",
+"cl3 c #647b90",
+"cjG c #647c94",
+"ckP c #647c95",
+"aAi c #647d8d",
+"cex c #647d93",
+"cho c #647d94",
+"cf8 c #647e96",
+"chR c #647f97",
+"cgP c #6488a3",
+"cpy c #6491b0",
+".wq c #649cc3",
+"bY3 c #649dbd",
+"#bX c #64a7e6",
+"bap c #64acd2",
+"#dC c #64ace7",
+"bPs c #64afd5",
+"bPt c #64afd6",
+"bPr c #64b0d6",
+"bQs c #64b0d7",
+"bPb c #64b1d7",
+"bV4 c #64b2d9",
+"bJ7 c #64b2db",
+"#72 c #64b2dc",
+"bLi c #64b2dd",
+"bNK c #64b3da",
+"bLl c #64b3dc",
+"bOB c #64b3dd",
+"ctc c #64b4e0",
+"ct2 c #64b5e0",
+"aqe c #64b5e1",
+"cjM c #64b6e1",
+"ciG c #64b7e4",
+"##X c #64f5fd",
+"#lE c #64fbf7",
+"#g6 c #64fef8",
+".5I c #651c37",
+".9u c #651f3c",
+"#mv c #652336",
+"#qK c #652337",
+"#wg c #652437",
+"#sr c #652642",
+"a5q c #652743",
+"aNF c #652d47",
+"aJg c #652f46",
+"aHQ c #65334a",
+"aPe c #65354c",
+".EH c #653a44",
+".PG c #653b45",
+".Iq c #653b65",
+"#DB c #653d5f",
+"#A2 c #65466b",
+".xT c #65494b",
+".wL c #654c4f",
+"bOI c #65507e",
+".88 c #655255",
+".p7 c #65545c",
+".tl c #65565e",
+"b2d c #655778",
+"aHg c #655961",
+"aks c #655a83",
+".21 c #655b43",
+"aaW c #655c7d",
+"aGp c #655e66",
+".ST c #656d73",
+".E8 c #656e75",
+"bSS c #656f74",
+".19 c #656f76",
+"aeq c #656f77",
+"a6p c #657078",
+"#ba c #657179",
+"bMd c #65727a",
+"aG9 c #65737c",
+"acw c #65747d",
+"a8z c #65759b",
+"aQ8 c #657884",
+"aYx c #657885",
+"aSe c #657984",
+"aRi c #657985",
+"ci1 c #65798c",
+"aQ5 c #657a86",
+".N. c #657b88",
+"chu c #657d95",
+"chB c #657e96",
+"ceF c #657f96",
+"cjw c #657f97",
+".HR c #658597",
+"cu2 c #6591af",
+"cqK c #6593b3",
+"a50 c #6594bd",
+"bPq c #65b0d7",
+"bPp c #65b1d7",
+"bQt c #65b1d8",
+"bPu c #65b2d7",
+"bPw c #65b2d8",
+"bS2 c #65b2d9",
+"bMG c #65b3da",
+"bMr c #65b3db",
+"bHI c #65b3dc",
+"bLh c #65b5de",
+"b2g c #65b5e0",
+"ckk c #65b6e2",
+"ckb c #65b7e4",
+".aU c #65b8e5",
+"#dv c #65fdf8",
+"#Cg c #661c39",
+"#z# c #661d39",
+"#AX c #661e39",
+"#rU c #662337",
+"#pg c #662437",
+"bAd c #662541",
+"aXy c #662642",
+"aTH c #662743",
+"bRb c #662a4a",
+"a2f c #662b4e",
+"aOE c #662d51",
+"#Dp c #662f46",
+"aPu c #662f53",
+"bGV c #66314f",
+".UU c #663b46",
+"#mg c #663c46",
+"#f6 c #663d46",
+"#qX c #663f47",
+".83 c #663f4a",
+"#x1 c #664651",
+"#K# c #664856",
+"aZY c #664a7a",
+"app c #664b59",
+"#Nj c #664c5a",
+"a.d c #664c7b",
+".Sy c #664f65",
+"aX3 c #66545d",
+"aJ2 c #66555c",
+"bJO c #665b7c",
+".xo c #66656c",
+"aDv c #66676a",
+"aq8 c #666971",
+"aj3 c #666e76",
+".E9 c #666f75",
+".5R c #666f76",
+".F. c #667076",
+".Jp c #667077",
+"auK c #667179",
+"#Ma c #66727b",
+"#G6 c #66737c",
+"bfa c #66747c",
+"bi3 c #66747d",
+"cbf c #66757e",
+"bzV c #6676a0",
+"a1e c #667985",
+"aQ6 c #667a86",
+"aSg c #667a87",
+"cma c #667e95",
+"cmr c #667e96",
+"cjm c #667f97",
+"cnE c #668097",
+"chT c #668098",
+"ck1 c #668198",
+"bZg c #6684a0",
+"#B2 c #668598",
+".HP c #66889b",
+"csQ c #6688af",
+"ct6 c #6691ae",
+"cgT c #669cbf",
+".oJ c #66a4cb",
+"##9 c #66a5e6",
+"bRM c #66b2d9",
+"bQr c #66b2da",
+"bPa c #66b3da",
+"bN6 c #66b4db",
+"#Sn c #66b4dd",
+"bPv c #66b5da",
+"bLm c #66b5de",
+"bKi c #66b5df",
+"cpr c #66b7e2",
+"bOz c #66b7e3",
+"cdo c #66b8e3",
+"b2q c #66b9e2",
+"#02 c #66c0ed",
+"#BF c #671d3a",
+"#z2 c #671e39",
+"#Dy c #671f3b",
+"#GZ c #67213d",
+"#rV c #672337",
+"#rT c #672437",
+"#vj c #672438",
+"aTG c #672542",
+"#nF c #672639",
+"bnv c #672a45",
+"aJF c #672a4d",
+"b#S c #673148",
+"a2r c #67324a",
+"aHI c #67354c",
+".5h c #673d47",
+"#hQ c #673d48",
+".B. c #673e47",
+"#kK c #674251",
+"aP6 c #674a74",
+".vS c #674d52",
+"bDI c #674e76",
+"bwW c #674e7b",
+"b5x c #674f76",
+"bth c #67565d",
+"aWy c #67585f",
+".DU c #675868",
+"aIK c #675963",
+".hF c #675e64",
+".El c #676f73",
+".Az c #676f74",
+".Hi c #677076",
+".Jq c #677177",
+"#eM c #677178",
+"avg c #677179",
+".JF c #677278",
+"#M4 c #67727a",
+"aAW c #67727b",
+"#Lb c #67747c",
+"a6D c #67757c",
+"a49 c #67767e",
+"aCR c #67767f",
+"b.W c #677680",
+"aaM c #67789b",
+"aZb c #677985",
+"aXK c #677a85",
+"a0t c #677a86",
+"ba2 c #677b86",
+"aQ7 c #677b87",
+"cbm c #677b88",
+"bgW c #677c8b",
+"coF c #677e95",
+"coQ c #677e96",
+"coE c #677f96",
+"ck9 c #677f97",
+"a7k c #678096",
+"ci9 c #678097",
+"cjr c #678198",
+"cgO c #6787a0",
+"#Or c #67889c",
+"csv c #678ea8",
+"crE c #6791ae",
+"#o1 c #67967f",
+".vx c #67a3cb",
+"#bW c #67abe7",
+"bP# c #67b3da",
+"bNN c #67b5dc",
+"bN7 c #67b6dd",
+"b37 c #67b6de",
+"#SF c #67b6e1",
+"bNO c #67b7de",
+"bHJ c #67b7e0",
+"bHS c #67b7e1",
+"cnZ c #67b7e3",
+"bQq c #67b8df",
+"bV3 c #67b8e0",
+"aR0 c #67b8e2",
+"bLT c #67b8e3",
+"ci. c #67b8e4",
+"bVo c #67b9e4",
+"aqf c #67bae5",
+"#qN c #682437",
+"#n8 c #682438",
+"#nv c #682439",
+"#wh c #682539",
+"bC2 c #682542",
+"a4x c #682743",
+"blM c #682844",
+"b8B c #682948",
+"blO c #682a45",
+"bJz c #682a4a",
+"aVb c #682c50",
+"aDU c #682d51",
+"#CL c #682e44",
+"aYe c #683047",
+"blQ c #68314b",
+"awu c #68354d",
+".F9 c #68396b",
+"#qW c #683d48",
+".UT c #683d49",
+"#pB c #683e48",
+"#t8 c #683f4b",
+"#9b c #684163",
+"#pC c #68434c",
+"#v8 c #68434f",
+".xU c #68484d",
+"#zj c #68496f",
+"bIp c #684f7c",
+".p9 c #68575d",
+"#Tp c #686067",
+"aiW c #68626a",
+"bxn c #68688f",
+".NT c #686f74",
+".Ay c #687076",
+"#5m c #687176",
+".F# c #687178",
+".fj c #687277",
+".Fe c #687278",
+"#L# c #68727a",
+"beu c #68737b",
+"#Mc c #68747b",
+"b4b c #68757d",
+"bhf c #68767e",
+"av5 c #68767f",
+"b.F c #687780",
+"bhy c #687781",
+"#kq c #687846",
+"bxx c #687881",
+"aZS c #687a85",
+"a0u c #687a86",
+"aS8 c #687b86",
+"cnJ c #688097",
+"cl7 c #688098",
+"ci6 c #688198",
+"clc c #688199",
+"chK c #688299",
+"csR c #6886ab",
+"#QL c #688ab0",
+".BS c #688b9f",
+".c. c #688ea6",
+"#Ww c #68a0c6",
+".f5 c #68a1c7",
+".DN c #68a3ca",
+"#.f c #68a3e5",
+"#Te c #68aed5",
+"coa c #68b4de",
+"a93 c #68b6dd",
+"ak. c #68b6de",
+"bNM c #68b7de",
+"bS1 c #68b7df",
+"bMH c #68b7e0",
+"#oX c #68b8aa",
+"bLn c #68b8e0",
+"azw c #68b8e3",
+"cmX c #68b9e5",
+"bJa c #68bae3",
+"ct4 c #68bae5",
+".cM c #68bbe6",
+"cu9 c #68bbe8",
+"clA c #68c0ec",
+"#iA c #68fff6",
+".3M c #691d39",
+"#z4 c #691e3a",
+"#nw c #692438",
+"aDR c #692441",
+".YH c #692543",
+"#pp c #692639",
+"#vs c #692642",
+".3S c #692643",
+"aOd c #692744",
+"blN c #692845",
+"aG2 c #69294b",
+"#xl c #692a4a",
+"a3J c #692b50",
+"a1v c #692c4f",
+"#wP c #692f46",
+"blP c #692f49",
+"#Gk c #693149",
+"bKz c #693157",
+"aSX c #69364d",
+".S6 c #693d47",
+".3q c #693e49",
+".GP c #693f49",
+".KX c #69424a",
+"#r8 c #69444b",
+"ayd c #694757",
+"#Kv c #694a58",
+"b64 c #694a71",
+"#a6 c #694c5a",
+".Q6 c #694d5e",
+"bBX c #694d75",
+"aIX c #694e5c",
+"aMd c #69575e",
+".PJ c #695c5d",
+"bFx c #696a94",
+"ad# c #696f97",
+".Fa c #697278",
+".Fb c #697279",
+"#S1 c #69727a",
+".YN c #697379",
+"#og c #69737a",
+"afQ c #69747c",
+"amd c #69757d",
+"##A c #69767e",
+"bfp c #69777f",
+"#1B c #697780",
+"a9j c #697880",
+"a0y c #697986",
+"aYt c #697b86",
+"a8G c #697c87",
+"bgQ c #697d8a",
+"cnb c #697d8f",
+"cot c #697d90",
+"coR c #698098",
+"cnp c #698198",
+"cmw c #698199",
+"chE c #69829a",
+"atZ c #698a9c",
+"ayn c #698a9f",
+".ws c #698b9f",
+"cnW c #698cb1",
+"cwX c #6993ae",
+".8l c #69a6e9",
+".iW c #69a7ce",
+"#bV c #69b0e8",
+"ca8 c #69b6de",
+"#T9 c #69b6df",
+"bN8 c #69b8df",
+"bP. c #69b9df",
+"bO9 c #69b9e1",
+"bMq c #69b9e2",
+"bSy c #69b9e4",
+"bLg c #69bae2",
+"bHK c #69bae3",
+"bHL c #69bae4",
+"b.u c #69bae5",
+"ctd c #69bae6",
+"b0S c #69bbe3",
+"ccj c #69bbe6",
+"#4C c #69bbe7",
+"cdv c #69bce7",
+"#3Y c #69bce8",
+"ciH c #69beea",
+"#X5 c #69c0eb",
+"cuG c #69c1ec",
+"cvJ c #69c1ed",
+"ctQ c #69c2ed",
+"cs1 c #69c3ee",
+"#za c #6a1e3b",
+".5J c #6a1e3c",
+"#AY c #6a1f3c",
+"#rW c #6a2439",
+"#pf c #6a2539",
+"aYl c #6a2542",
+"a3X c #6a2543",
+"aGb c #6a2643",
+"aZ5 c #6a2644",
+"aXx c #6a2744",
+"aie c #6a2845",
+"a#Q c #6a2846",
+"atv c #6a3157",
+"bDM c #6a324c",
+"aU# c #6a354d",
+"aj# c #6a3b64",
+".Yi c #6a3e49",
+"aQC c #6a4064",
+"#zV c #6a4a57",
+"b5u c #6a4a72",
+"b6V c #6a4b72",
+".Uj c #6a5468",
+"cqh c #6a5565",
+"ad9 c #6a565e",
+".hp c #6a585f",
+"cnU c #6a5b7c",
+"aaT c #6a5d80",
+"#gg c #6a6066",
+"bp4 c #6a646b",
+".Jr c #6a7379",
+"##m c #6a7479",
+".to c #6a747a",
+"amg c #6a747b",
+".HF c #6a757c",
+"at8 c #6a767d",
+"#NM c #6a777e",
+"brY c #6a777f",
+"#9M c #6a7780",
+"bqJ c #6a7880",
+"apV c #6a7882",
+"aIC c #6a789b",
+"abc c #6a7981",
+"#0R c #6a7982",
+"a1f c #6a7a87",
+"bcD c #6a7c87",
+"bpB c #6a7c8a",
+"aYv c #6a7d88",
+"bf9 c #6a7d8b",
+"bfm c #6a7f94",
+"#HV c #6a8290",
+"ci7 c #6a8299",
+"#Tj c #6a86ad",
+"bBh c #6a89a9",
+"a4s c #6a8fb4",
+"cu3 c #6a93ae",
+"#dL c #6aa3d9",
+".BY c #6aa6cc",
+"b5P c #6ab7de",
+"ax3 c #6ab8e0",
+"bMJ c #6ab9e2",
+"bLo c #6abae2",
+"bNL c #6abae3",
+"bMI c #6abbe3",
+"bIW c #6abce5",
+"bHO c #6abce6",
+"csq c #6abce7",
+"bHN c #6abde6",
+"bHP c #6abde7",
+"cjZ c #6abde8",
+"cxx c #6abde9",
+"c.f c #6abfea",
+"aOa c #6ac3ed",
+"#bO c #6af9fb",
+"#fp c #6afdf8",
+".7y c #6b1e3a",
+"#CT c #6b1e3b",
+"#zb c #6b1e3c",
+"#BG c #6b1f3c",
+"#Ef c #6b203d",
+".9v c #6b203e",
+"aYL c #6b2141",
+"#rS c #6b2539",
+"#pT c #6b253a",
+"#jE c #6b253b",
+"by6 c #6b2543",
+"#wi c #6b263a",
+"aif c #6b2745",
+"b5t c #6b2848",
+"a60 c #6b2b45",
+"#Gj c #6b324a",
+"bzZ c #6b3a60",
+"aQj c #6b3c62",
+".CY c #6b3e49",
+"#r7 c #6b3f49",
+"#f5 c #6b414b",
+"awF c #6b4a59",
+".k8 c #6b4b58",
+"#eK c #6b4d5b",
+".MI c #6b5257",
+"ai2 c #6b5860",
+"bJT c #6b5d65",
+"acG c #6b6e75",
+".SZ c #6b7174",
+".Lc c #6b747a",
+".7G c #6b747b",
+"afL c #6b757c",
+"#51 c #6b757d",
+"aba c #6b767e",
+"#eW c #6b777f",
+"#Yf c #6b7880",
+"bRA c #6b7881",
+"bik c #6b7981",
+"b#t c #6b799c",
+"#9h c #6b799d",
+"ahk c #6b7a82",
+"bc. c #6b7a83",
+"a3v c #6b7c85",
+"a2J c #6b7e89",
+"chp c #6b839a",
+"chs c #6b839b",
+"ciz c #6b8a9f",
+"#oQ c #6b8da0",
+"azP c #6b8da1",
+"a9O c #6b8dab",
+"bJY c #6b91b6",
+"b5A c #6ba4cd",
+".FG c #6ba7cd",
+"##8 c #6babe7",
+".ek c #6baed5",
+"bN9 c #6bb9e0",
+"ba8 c #6bb9e1",
+"bMp c #6bbbe3",
+"bLu c #6bbbe4",
+"bLc c #6bbce4",
+"bLf c #6bbce5",
+"bHM c #6bbee8",
+"b8L c #6bbee9",
+"bNc c #6bbfe9",
+"aqg c #6bbfea",
+"cwJ c #6bc0ec",
+"#3S c #6bc1eb",
+"#Ws c #6bc3ee",
+"csa c #6bc5ef",
+"#Xb c #6bc9f5",
+".3N c #6c1e3c",
+"#z5 c #6c1f3c",
+"#BE c #6c203b",
+"#y9 c #6c213c",
+".9w c #6c2240",
+"bEI c #6c2441",
+"#rX c #6c253a",
+".RF c #6c2543",
+"#qJ c #6c263b",
+"aMF c #6c2744",
+"aid c #6c2947",
+"aM2 c #6c2b4f",
+"a1H c #6c3048",
+"#CG c #6c3148",
+"#B7 c #6c3149",
+"aQd c #6c3654",
+"#hB c #6c3e4c",
+".1A c #6c3f4a",
+"#.V c #6c404a",
+"#pA c #6c414c",
+".Ob c #6c474d",
+"#Bs c #6c4957",
+".bX c #6c4d5b",
+"aHA c #6c4f5e",
+"#Mv c #6c505c",
+"cr6 c #6c5670",
+"aNq c #6c5960",
+".ZF c #6c5b50",
+"aaV c #6c5d82",
+"a5f c #6c6a73",
+"#ep c #6c6b6d",
+"ac# c #6c6d8c",
+".AM c #6c7378",
+"#J8 c #6c747b",
+"abL c #6c749d",
+".Ld c #6c757b",
+".Fd c #6c757c",
+"ano c #6c767c",
+"afE c #6c767d",
+"a3l c #6c767e",
+"#50 c #6c777e",
+"at9 c #6c777f",
+"bOV c #6c7881",
+"bqI c #6c7983",
+"#0U c #6c7a81",
+"a45 c #6c7a82",
+"adZ c #6c7a83",
+"a.5 c #6c7a8d",
+"awZ c #6c7b84",
+"a7Z c #6c7e89",
+"a9y c #6c7f89",
+"a4l c #6c7f8a",
+"bhF c #6c808e",
+"ckF c #6c8195",
+"cni c #6c839a",
+"cng c #6c849a",
+"ckH c #6c849b",
+"ci8 c #6c859c",
+"cjz c #6c859d",
+".HW c #6ca2c7",
+".xm c #6ca6cc",
+"#dB c #6cb5e9",
+"bEL c #6cbce4",
+"bLp c #6cbce5",
+"bMK c #6cbde4",
+"bLd c #6cbde5",
+"bLe c #6cbde6",
+"bLs c #6cbee6",
+"b8Z c #6cbee7",
+"bKf c #6cbfe7",
+"caR c #6cbfea",
+"aAC c #6cbfeb",
+"bKe c #6cc0e7",
+"bKd c #6cc0e8",
+"cce c #6cc0ea",
+"ciM c #6cc0eb",
+".ca c #6cc1ec",
+".bA c #6cc3ef",
+"#01 c #6ccaf6",
+"#AZ c #6d203d",
+"#p0 c #6d2240",
+"#qO c #6d253a",
+"bbM c #6d2543",
+"bfJ c #6d2544",
+"#sY c #6d263a",
+"#x# c #6d263b",
+"aIn c #6d2644",
+"#9p c #6d2745",
+"bR# c #6d2748",
+"bTT c #6d284a",
+"aQk c #6d284c",
+"aRF c #6d2a4e",
+"#7A c #6d2a4f",
+"#CX c #6d2d4e",
+"aw5 c #6d2e53",
+"auU c #6d2f55",
+"#D6 c #6d3149",
+"a0o c #6d334b",
+"#BJ c #6d3456",
+"ax# c #6d364e",
+"a.i c #6d3a5d",
+"bCN c #6d476d",
+".o2 c #6d4a5a",
+".aS c #6d5465",
+"acF c #6d565e",
+"aaU c #6d5d82",
+".1j c #6d5f4b",
+"aC2 c #6d658a",
+".ev c #6d666c",
+"bOL c #6d698f",
+".Fc c #6d767c",
+"at5 c #6d767d",
+"aS# c #6d767e",
+".h8 c #6d777c",
+"#cX c #6d777d",
+"awS c #6d777e",
+"azl c #6d777f",
+"#2U c #6d7880",
+"bdu c #6d7980",
+"#yA c #6d7981",
+"bHt c #6d7983",
+"a74 c #6d7b83",
+"#0S c #6d7c84",
+"aXJ c #6d7e89",
+"#9Q c #6d7f88",
+"a9R c #6d7f89",
+"bng c #6d7f8a",
+"aVE c #6d808b",
+"#5l c #6d8492",
+"cq4 c #6d849a",
+"b2x c #6d859a",
+"cl5 c #6d859c",
+"bXm c #6d89a5",
+"bjT c #6d8b9f",
+"bc8 c #6d8eab",
+"cqM c #6d92ac",
+"cgU c #6da7c9",
+"#Rw c #6db1d7",
+"#bU c #6db8e8",
+"bLq c #6dbde6",
+"bLb c #6dbee6",
+"bLr c #6dbfe6",
+"bK. c #6dbfe7",
+"bK# c #6dbfe8",
+"#6d c #6dbfea",
+"bKa c #6dc0e8",
+"bHQ c #6dc0e9",
+"aTC c #6dc0eb",
+"bJ# c #6dc1e9",
+"aTD c #6dc1ea",
+"cta c #6dc1eb",
+"#ZI c #6dc1ec",
+"cuU c #6dc2ec",
+"ckc c #6dc2ed",
+"az2 c #6dc2ef",
+"#7. c #6dc3ee",
+"ciI c #6dc4ef",
+"b5B c #6dc8f3",
+".93 c #6df1fd",
+"#ka c #6dfef5",
+".10 c #6e1f3d",
+"#z6 c #6e203e",
+"#xf c #6e213f",
+".YE c #6e2240",
+"a8X c #6e2544",
+"#rR c #6e263b",
+"aG3 c #6e2644",
+"adS c #6e2745",
+"bmr c #6e2846",
+"aic c #6e2847",
+"aTs c #6e294d",
+"aRI c #6e2a4d",
+"a23 c #6e2a4f",
+"aZG c #6e314a",
+"aJx c #6e3155",
+"bo6 c #6e354e",
+"avK c #6e374f",
+".84 c #6e414c",
+".I1 c #6e454d",
+"a16 c #6e517e",
+"aC3 c #6e5a73",
+"aHx c #6e5c64",
+"#8V c #6e7279",
+"#pa c #6e7378",
+"#0C c #6e737a",
+".hc c #6e777d",
+"awd c #6e777e",
+"bt1 c #6e787f",
+"#56 c #6e797f",
+"#Yr c #6e7980",
+"#3B c #6e7981",
+"a7T c #6e7a9c",
+"b4e c #6e7c83",
+"aZo c #6e7c84",
+"ax1 c #6e7c85",
+"ar7 c #6e7d86",
+"b3T c #6e7fa7",
+"a3w c #6e808a",
+"a2X c #6e818d",
+"a0I c #6e8392",
+"coU c #6e859c",
+"cgN c #6e899b",
+"bEg c #6e89a3",
+"a#U c #6e8a9b",
+"bUd c #6e8aa5",
+"avb c #6e8ea1",
+"#GO c #6e8fa2",
+"#g0 c #6e90a4",
+"ctg c #6e93ac",
+"bge c #6e98b2",
+"#Xe c #6ea2c5",
+"cgV c #6eaacc",
+".Xp c #6eabd0",
+"axB c #6ebfe8",
+"bKb c #6ec0e8",
+"bJ9 c #6ec1e8",
+"bKc c #6ec1e9",
+"bHR c #6ec1ea",
+"b3Y c #6ec1eb",
+"bRj c #6ec1ec",
+"bLt c #6ec2e9",
+"bKg c #6ec2eb",
+"bKF c #6ec2ec",
+"ciN c #6ec2ed",
+"cid c #6ec3ed",
+"#0o c #6ec4ee",
+"ciq c #6ec4ef",
+"cg3 c #6ec7f3",
+".#0 c #6ec9f3",
+"#tj c #6f1e3e",
+".7z c #6f1f3e",
+"#A0 c #6f203e",
+"#CU c #6f203f",
+"aJv c #6f2443",
+"aEq c #6f2544",
+"#rY c #6f263b",
+"#sj c #6f263c",
+"aKU c #6f2644",
+"a3C c #6f2645",
+"#qI c #6f273c",
+"b.x c #6f2745",
+"bIe c #6f2849",
+"aOx c #6f284b",
+"a2g c #6f294c",
+"aFK c #6f294d",
+"aRG c #6f2a4d",
+"aFb c #6f2a4f",
+"#Cc c #6f2d44",
+"a1C c #6f364e",
+"ai8 c #6f385e",
+".EI c #6f414c",
+"bAX c #6f4268",
+".zb c #6f444d",
+".MF c #6f4950",
+"b8H c #6f4c70",
+"bP2 c #6f4c78",
+"azE c #6f4f5d",
+"#k2 c #6f505a",
+"ahp c #6f5d63",
+"#8Q c #6f5e66",
+"bKQ c #6f5e7e",
+"aFS c #6f7275",
+".Au c #6f777c",
+"#p7 c #6f777e",
+".FL c #6f787e",
+"#Yt c #6f787f",
+"avs c #6f7880",
+"aKZ c #6f797f",
+".Qe c #6f7a81",
+"#vL c #6f7a82",
+"buA c #6f7b83",
+"brn c #6f7c84",
+"aAm c #6f7d86",
+"#1A c #6f7e87",
+"a9U c #6f7f88",
+"a7l c #6f8089",
+"bgc c #6f808a",
+"bdI c #6f808b",
+"aZR c #6f818b",
+"cl2 c #6f8397",
+"chr c #6f879e",
+"aAU c #6f8897",
+"coL c #6f889f",
+".xn c #6f8a9b",
+"bcU c #6f93a7",
+"ct9 c #6f94ae",
+"aTE c #6f98be",
+".HY c #6faad0",
+".lN c #6fadd3",
+"##7 c #6fafe7",
+"#hc c #6fb5e6",
+"#fv c #6fbbe7",
+"bI7 c #6fc2eb",
+"bI6 c #6fc3eb",
+"bI5 c #6fc3ec",
+"c.y c #6fc3ed",
+"#6f c #6fc3ee",
+"bI3 c #6fc4ec",
+"ay2 c #6fc4ee",
+"bPU c #6fc4ef",
+"#73 c #6fc4f0",
+"bKh c #6fc5ed",
+"ccd c #6fc5ef",
+"ciJ c #6fc6f1",
+"cfy c #6fc7f2",
+"#4v c #6fc7f3",
+"cqn c #6fcaf4",
+"cdJ c #6fcaf5",
+"ccu c #6fcbf6",
+"bFU c #6fcdf8",
+"#0k c #6fcffb",
+"#nb c #6feae1",
+"#zc c #70203f",
+"#Dz c #70213f",
+"aWS c #702444",
+"aib c #702544",
+"#tk c #702645",
+"#qP c #70273c",
+"#pe c #70273d",
+"avC c #702c50",
+"bCO c #702e50",
+"#Bh c #70324a",
+"bxY c #703354",
+"#yh c #703e64",
+".3p c #70434d",
+"aRq c #70436b",
+".xV c #704a50",
+"bRm c #704c79",
+"aqB c #704e5d",
+"asR c #705d66",
+"cfo c #706c8f",
+".c0 c #70787e",
+".Oq c #70797f",
+"#wH c #707980",
+"bRB c #707a80",
+"atj c #707a81",
+"avx c #707b83",
+"#HP c #707d84",
+"aIq c #707e86",
+"afx c #707e87",
+"aBz c #707f87",
+"auM c #707f88",
+"aVF c #708088",
+"a6B c #708089",
+"a7n c #70818b",
+"aZ. c #70818c",
+"bfu c #70828c",
+"cl1 c #708397",
+"apS c #7092a4",
+"coi c #7094ac",
+"arx c #7095bc",
+"cqH c #70a1be",
+".#3 c #70a6ca",
+"##6 c #70b3e9",
+"##5 c #70b6e9",
+"aqn c #70bae2",
+"cv7 c #70bee6",
+"bI4 c #70c3ec",
+"bFQ c #70c3ed",
+"bJ8 c #70c4ec",
+"bI2 c #70c4ed",
+"ct# c #70c4ef",
+"bIX c #70c5ee",
+"bTW c #70c5ef",
+"ay4 c #70c5f0",
+"Qty c #70c6f0",
+"aAB c #70c7f2",
+"ciK c #70c8f3",
+"cmO c #70c9f2",
+"c#P c #70caf5",
+"bHa c #70cbf5",
+"cb. c #70cbf6",
+"bEO c #70ccf6",
+"bCg c #70cef9",
+"##Y c #70f4fb",
+"#dw c #70fcf7",
+"#uq c #711f3f",
+"#vp c #71203e",
+"#E6 c #71203f",
+"#Eg c #712040",
+".3O c #71213f",
+"#Ch c #712140",
+"#CS c #71223f",
+"aRy c #712342",
+"aia c #712343",
+"aDS c #712442",
+"aMH c #712443",
+".Ja c #712444",
+".RG c #712544",
+"#rZ c #71263c",
+"aFe c #712645",
+"#rQ c #71273d",
+"#xa c #71273e",
+"aYc c #712746",
+"#mx c #71283f",
+"aTk c #712847",
+"bYW c #712848",
+"aEz c #71284d",
+"aSC c #71294b",
+"asD c #71294e",
+"#rM c #712b42",
+"aHP c #71334c",
+"#zh c #713357",
+"aHJ c #71344d",
+"aQa c #71364f",
+"#pj c #713a4a",
+".B# c #71434e",
+"aZi c #714a76",
+"bur c #715560",
+"bEb c #715d64",
+"aJ9 c #715f66",
+".dj c #717a7f",
+".MR c #717a80",
+"#Zc c #717a81",
+".kD c #717a8a",
+"ati c #717b82",
+"afF c #717b83",
+"a8t c #717c83",
+"#M# c #717c84",
+"afz c #717e85",
+"aL9 c #717e86",
+"#7Q c #717e87",
+"a5a c #717f86",
+"b5V c #717f88",
+"c#V c #718088",
+"ayp c #718089",
+"bgP c #71818a",
+"aW1 c #71828c",
+"bb9 c #71828d",
+"bk5 c #71838d",
+"aRj c #71838e",
+"chl c #71869a",
+"cfW c #71899f",
+"#oY c #71bcab",
+"b8J c #71bfe7",
+"aQN c #71bfe8",
+"aR3 c #71c0eb",
+"bI8 c #71c4ed",
+"bI1 c #71c5ee",
+"bEM c #71c5ef",
+"bI0 c #71c6ef",
+"bKG c #71c6f0",
+"b67 c #71c7f0",
+"bPT c #71c7f1",
+"#69 c #71c7f2",
+"aAA c #71c7f3",
+"cqE c #71c8f2",
+"cd1 c #71c8f3",
+"cv8 c #71c8f4",
+"#03 c #71c9f3",
+"cbi c #71c9f4",
+"c#0 c #71c9f5",
+"bIj c #71caf3",
+"bDZ c #71caf4",
+"#Td c #71caf5",
+"cxA c #71cbf6",
+"cp. c #71ccf7",
+"#ZJ c #71cdf8",
+"bC5 c #71d0fa",
+"#CV c #72203f",
+"#wn c #72213f",
+"#BH c #722140",
+"#E7 c #722343",
+".To c #722443",
+"bEH c #722444",
+".WW c #722544",
+".5N c #722545",
+"aVo c #722646",
+"#mw c #72273d",
+"bM4 c #722a4f",
+"bLO c #722b4f",
+"bDL c #722c4a",
+"bbH c #723650",
+"aaD c #723a5d",
+"b5y c #723c61",
+"bH7 c #723d62",
+"bz# c #724155",
+"#qV c #72434e",
+".5g c #72434f",
+"#w4 c #724a56",
+".s9 c #724e5c",
+"ahO c #725076",
+"#My c #72515f",
+"#Kd c #725160",
+"bIu c #725d7d",
+".qf c #725e65",
+".fB c #726269",
+".CU c #726465",
+".2O c #727171",
+"#pF c #727577",
+".Pz c #72787c",
+"#px c #72787e",
+"#sv c #727a80",
+"#Xk c #727a82",
+".Hj c #727b81",
+"#ZZ c #727b82",
+".j1 c #727c81",
+".HE c #727c83",
+".M2 c #727d84",
+"#Qz c #727e85",
+"bk6 c #727e87",
+"#eO c #727f86",
+"a#Y c #728087",
+"azp c #728089",
+"aoB c #728189",
+"avt c #72818a",
+"afV c #72838c",
+"a72 c #72838d",
+"biY c #728399",
+"b.T c #72848e",
+"bk4 c #72848f",
+"b9# c #728591",
+"aW6 c #728691",
+"cne c #72869a",
+"ci3 c #72899e",
+".no c #72a7c8",
+"#o0 c #72ae96",
+"b3V c #72bbe4",
+"#4w c #72bfe7",
+"cdI c #72c5f0",
+"bI9 c #72c6ef",
+"bIZ c #72c6f0",
+"bJ. c #72c7ef",
+"bIY c #72c7f0",
+"ch9 c #72c7f1",
+"azy c #72c8f2",
+"bVp c #72c8f3",
+"#5q c #72c8f4",
+"bD0 c #72c9f1",
+"ckl c #72c9f2",
+"bFT c #72c9f3",
+"ayx c #72c9f4",
+"aR2 c #72caf4",
+"#U1 c #72caf5",
+"#ZK c #72cbf5",
+"cmZ c #72ccf6",
+"cps c #72ccf7",
+"b5D c #72cdf7",
+"b8K c #72cef8",
+"b5C c #72cef9",
+"aqm c #72cff9",
+"aSQ c #72d1fc",
+"aql c #72d4fe",
+"aqk c #72d4ff",
+"#g7 c #72fdf5",
+"#Eh c #731f3f",
+"#Ei c #73203f",
+".11 c #732041",
+"#zd c #732140",
+"#yb c #732241",
+".9y c #732343",
+"#y. c #732440",
+"aWE c #732544",
+"#rh c #732545",
+"acq c #732646",
+"#k9 c #73273d",
+"#qR c #73273f",
+"#tZ c #73283e",
+"aNX c #73284c",
+"#qH c #73293e",
+"ahV c #732a4e",
+"atw c #732b4f",
+"bsr c #73324b",
+"bq5 c #73374f",
+"bny c #733750",
+"akr c #733960",
+"#ej c #73444e",
+"aNw c #734759",
+".NN c #734b6b",
+"beY c #734d5c",
+"bg7 c #734f5d",
+".GX c #735d63",
+"bzl c #735e65",
+"aK# c #736067",
+"aFV c #73626d",
+"bCm c #736a8f",
+".kG c #736c7f",
+"#.H c #73777a",
+".ZN c #73787b",
+".yY c #73797f",
+".AF c #737b7f",
+".Le c #737b81",
+".W4 c #737c82",
+"adT c #737d82",
+"a5l c #737d83",
+"ba5 c #737e84",
+"ar6 c #737e85",
+"#dg c #737e86",
+"#GB c #737f87",
+"baK c #738189",
+"ave c #73828b",
+"ayt c #73838b",
+"#0e c #73838c",
+"#M2 c #73838d",
+"#rn c #73848d",
+"cor c #738698",
+"bAt c #738da6",
+"bel c #7395aa",
+"cv0 c #7395ab",
+"#tz c #7398ad",
+".vy c #7398af",
+"ary c #739cc4",
+"aPF c #73a6cd",
+"arz c #73b7df",
+"bW5 c #73b9de",
+"#dA c #73c6ea",
+"cm0 c #73c6ee",
+"c.A c #73c6ef",
+"cxK c #73c7f1",
+"bH# c #73c8f2",
+"bJG c #73c9f2",
+"bEN c #73c9f3",
+"bFS c #73c9f4",
+"cg2 c #73caf3",
+"aSP c #73caf4",
+"#5s c #73caf5",
+"aQM c #73cbf5",
+"ciL c #73cbf6",
+"cob c #73ccf7",
+"b68 c #73cdf7",
+"#T8 c #73cdf8",
+"c.g c #73cff9",
+".77 c #73e6f6",
+"#lF c #73fcf7",
+"#CW c #741e3c",
+"#DA c #741e3d",
+"#Ci c #741f3e",
+"#vq c #742140",
+".7A c #742141",
+"#Cj c #742343",
+"a6c c #742444",
+"aLp c #742645",
+"ag4 c #742646",
+"aDT c #742648",
+"#sZ c #74273e",
+"aMI c #742746",
+"aVc c #74274a",
+"#n9 c #74283e",
+"b3R c #74284b",
+"#sX c #74293f",
+"aM3 c #74294e",
+"bGU c #742c49",
+"bq6 c #742e4b",
+"#E2 c #74314a",
+"bq4 c #74354e",
+"bEA c #743559",
+"aMp c #743750",
+".7b c #74434e",
+".GQ c #744851",
+".KY c #744d53",
+"b34 c #745871",
+"aDH c #74596e",
+"ack c #745f7e",
+"#em c #746263",
+"byh c #74688f",
+".1L c #747171",
+".Rc c #747a7d",
+"#Up c #747d84",
+"b5U c #747e84",
+"afD c #747e85",
+"#Al c #747f87",
+"#eZ c #748088",
+"bII c #748188",
+"amf c #748189",
+"a1r c #748289",
+"aVA c #74828a",
+"a73 c #74838a",
+"#2e c #74838b",
+"#br c #74848d",
+".2d c #74848e",
+".MY c #74858e",
+"bfn c #74858f",
+"bgJ c #748690",
+"a2K c #748790",
+"cgM c #748999",
+"b8I c #7489ae",
+"arA c #748bb0",
+"#sD c #7491a0",
+"aSR c #7494bc",
+"#Ge c #7497ab",
+"a98 c #7498ac",
+"b3U c #74a6cd",
+"#VJ c #74a7cb",
+"cgW c #74b0d2",
+"#.e c #74b0e7",
+"cqG c #74b9dd",
+".4e c #74bad2",
+"cbj c #74c2ea",
+"coc c #74c5ee",
+"cfx c #74c6ef",
+"cqF c #74c8f2",
+"b3W c #74c9f3",
+"bRi c #74caf4",
+"cdn c #74cbf4",
+"aR1 c #74cbf5",
+"b69 c #74cbf6",
+"cgB c #74ccf6",
+"bDY c #74ccf7",
+"bby c #74ccf8",
+"c.z c #74cdf7",
+"ax4 c #74cdf8",
+"b80 c #74cef9",
+"b7k c #74cefa",
+"aqj c #74d2fd",
+"#bP c #74f9f9",
+"#wo c #752140",
+"#sp c #752142",
+".5K c #752242",
+"aLo c #752444",
+"aMG c #752445",
+".Ok c #752545",
+"aUD c #752646",
+"#r0 c #75273e",
+"#qQ c #75283f",
+"aSA c #752849",
+"a3K c #75284b",
+"#rP c #75293f",
+"b#R c #75334c",
+"aQD c #753558",
+"#.U c #754551",
+"aqA c #75475a",
+"adi c #754b77",
+"#Bu c #754e5c",
+"ayh c #754e5d",
+".ss c #754f5f",
+"apo c #755161",
+"b5z c #755277",
+"bE1 c #755c65",
+".WC c #756769",
+".Sn c #756992",
+".KS c #756a6d",
+"bzU c #757199",
+"bc6 c #757396",
+"ahh c #75757c",
+"ai1 c #75777d",
+"#dV c #757c81",
+".2. c #757d83",
+"#WL c #757d84",
+"#C8 c #757e83",
+"avZ c #757e84",
+"avn c #757e85",
+"arh c #757f86",
+"bcY c #758087",
+"aQZ c #75828a",
+"bqH c #75828b",
+"bij c #75848e",
+"ba4 c #75858d",
+"##L c #75858e",
+".Ty c #75858f",
+".x# c #75868f",
+"aZQ c #758690",
+"bir c #758691",
+"a8H c #758792",
+"aZm c #758793",
+"cl0 c #75889b",
+"coZ c #7588a8",
+"buE c #758b9c",
+"aoX c #7596bc",
+".Xk c #7598ad",
+"bdq c #7599ac",
+".J3 c #75add2",
+"#hk c #75aed6",
+"#oZ c #75b9a3",
+"##4 c #75bfeb",
+"cw3 c #75c1e6",
+"cn0 c #75c5ec",
+"clz c #75c7ee",
+"cci c #75cbf4",
+"c.k c #75cbf5",
+"aqh c #75ccf5",
+"bJF c #75ccf6",
+"ca4 c #75ccf7",
+"ctX c #75cdf6",
+"b3X c #75cdf7",
+"#6e c #75cdf8",
+"ca9 c #75cdf9",
+"bFR c #75cef9",
+"bC4 c #75cefa",
+"aOP c #75d2fc",
+"aPE c #75d2fd",
+"#iB c #75fef5",
+".0k c #762141",
+"#ze c #762142",
+"aZs c #762445",
+"#p3 c #762446",
+"aUh c #762545",
+"aQP c #762546",
+"ax7 c #762649",
+"bG3 c #762748",
+"aWI c #762749",
+"aNW c #76274a",
+"#tY c #76293f",
+"#wj c #762940",
+"#z8 c #76294a",
+"#tP c #762e47",
+"aYo c #76334d",
+"aHK c #76344e",
+"bq2 c #763650",
+"aCp c #763a52",
+"ahP c #763d62",
+"#cA c #764551",
+".CZ c #764651",
+"#H5 c #764f5e",
+"aL5 c #765379",
+"biH c #765761",
+"#pL c #765d65",
+"a#e c #766b92",
+"#pb c #767d80",
+".As c #767e82",
+".Gc c #767e83",
+"#yv c #767e85",
+".Vj c #767f85",
+"bQf c #768085",
+"abx c #768086",
+"axU c #768088",
+"#gH c #768189",
+".#W c #768289",
+"aWY c #76828a",
+".RU c #76838a",
+"bqK c #76838b",
+"a0K c #76848c",
+"a5H c #76848d",
+"#dd c #76858d",
+"atb c #76858e",
+"#5h c #76858f",
+"#2Q c #76868e",
+".uq c #76868f",
+".Qa c #768690",
+".zM c #768790",
+"a4j c #768793",
+"cfe c #7687ac",
+"blx c #768891",
+"aTZ c #768892",
+"a1o c #768893",
+"aOb c #7688ac",
+"aoY c #768aaf",
+"beG c #768c9d",
+"cm4 c #768e9d",
+"acu c #7695a9",
+"cpw c #7695aa",
+"#vP c #769baf",
+"c#Z c #76c0e6",
+"#U0 c #76c3eb",
+"cv9 c #76ccf6",
+"bNb c #76cdf7",
+"bLU c #76cdf8",
+"az3 c #76cef7",
+"bLV c #76cef8",
+"#5r c #76cef9",
+"ayw c #76cff9",
+"bcc c #76d0fb",
+"#fq c #76fdf5",
+"#BI c #771e3e",
+"#A1 c #771f3f",
+"#FP c #771f40",
+".3P c #772242",
+".9x c #772243",
+"#up c #772341",
+"aLq c #772445",
+"aNe c #772545",
+"aTq c #772546",
+"aa6 c #772646",
+"#ws c #772748",
+"bJv c #77274a",
+"#s0 c #77283f",
+"#pU c #772940",
+"axD c #77294c",
+"#sW c #772a41",
+"aKT c #772a4e",
+"aOu c #773650",
+"#r6 c #774652",
+"bT0 c #774c77",
+".Oc c #775156",
+"bf2 c #775d83",
+".ob c #775f65",
+"buz c #776066",
+"bT9 c #776a73",
+"#WM c #778086",
+".JE c #778188",
+"#ho c #778237",
+"aj4 c #778289",
+"#Jx c #77838c",
+"bdt c #77858d",
+"#QH c #7785a7",
+"a.D c #77868f",
+"#Dj c #77878f",
+".MZ c #778790",
+".lt c #778791",
+".Vp c #778890",
+".vk c #778891",
+"a43 c #778892",
+"a8# c #778993",
+"ciy c #778d9c",
+"#.n c #7797b1",
+"#ES c #779bae",
+"b#l c #779caf",
+"#H1 c #779db2",
+".J1 c #77a9cb",
+".8k c #77ade5",
+".z2 c #77b0d4",
+"bEP c #77bfe6",
+"cd0 c #77c6ee",
+"#bT c #77cbec",
+"cv. c #77cff8",
+"b5E c #77cffa",
+"#4D c #77d0fc",
+"aqi c #77d1fa",
+".94 c #77effe",
+"#rf c #782041",
+"#xg c #782142",
+"#rg c #782143",
+".12 c #782243",
+".0l c #782244",
+"#sq c #782343",
+"aIb c #782445",
+"aZ7 c #782446",
+"b#f c #782545",
+"aXB c #782546",
+"aV. c #782649",
+"aVU c #78264a",
+"aST c #782747",
+"ac3 c #78274c",
+"bFD c #782848",
+"#sk c #782940",
+"#tX c #782941",
+"#qG c #782a41",
+"bDJ c #782f53",
+"aHO c #78334d",
+"aR9 c #783750",
+"bbG c #783751",
+"aoj c #78425b",
+"#aO c #784752",
+".xW c #784c54",
+"abS c #784c77",
+"aeE c #784e73",
+".n# c #784f5f",
+".wM c #785558",
+"aOQ c #785d81",
+"aJ1 c #786066",
+"#Mk c #786870",
+"aoZ c #787297",
+"#.t c #787435",
+"amG c #787a81",
+"#.C c #787f84",
+".Or c #788086",
+"#53 c #788187",
+"aht c #788188",
+".R7 c #788287",
+"#8P c #788288",
+"ahu c #788289",
+"#tH c #78828a",
+"af4 c #788389",
+".HD c #78838a",
+"#Lc c #78848c",
+"#iS c #788544",
+"brX c #78858c",
+"bfP c #78868e",
+"#GI c #78868f",
+"#oo c #78878f",
+".9J c #788790",
+"beK c #788791",
+"#J0 c #788890",
+".ns c #788891",
+".lr c #788892",
+"#sw c #788991",
+".os c #788992",
+"bcy c #7893ab",
+"#Qv c #789aae",
+".xh c #789db1",
+"cgX c #78b5d7",
+"#iM c #78b6db",
+"#iL c #78b8dc",
+"#hb c #78c0e5",
+"cgC c #78c3e9",
+"#0l c #78cdf5",
+"b5Q c #78cff9",
+"azx c #78d1fb",
+"ay3 c #78d1fc",
+"bcM c #78d2fc",
+"##Z c #78f2fb",
+"#z7 c #791e3f",
+"#zf c #792142",
+".5L c #792344",
+".yH c #79237e",
+".7B c #792445",
+"aLW c #792446",
+"aM7 c #792447",
+".K5 c #792546",
+"bqs c #792646",
+"bRc c #792648",
+"bDK c #792747",
+"aRH c #792749",
+"akx c #79274a",
+"#t1 c #792840",
+"#t0 c #792a40",
+"aPv c #792a4d",
+"aHM c #79344f",
+"aHL c #79354f",
+"agp c #79395d",
+"aoi c #794a5e",
+"bfY c #794a76",
+"bD5 c #794b76",
+"aEW c #795361",
+"ctS c #795362",
+"cfg c #795363",
+"ao0 c #795d81",
+".jq c #796065",
+"bE3 c #79656d",
+"aar c #796990",
+"#M8 c #796a72",
+"cs. c #796b8e",
+".Ys c #797679",
+"#Mj c #797e86",
+"#o9 c #798084",
+"#VQ c #798089",
+".At c #798185",
+".P9 c #798187",
+"as7 c #798287",
+"#OL c #798288",
+"aUN c #798289",
+".rr c #798389",
+"#5e c #79838a",
+".TD c #79848b",
+"#Tk c #7985a8",
+"#LG c #79868e",
+"bUg c #79868f",
+"aS2 c #79878f",
+"#jZ c #798790",
+"#gQ c #798890",
+"#c9 c #798891",
+"#gM c #798892",
+"aD8 c #798991",
+".kg c #798992",
+".pu c #798993",
+"#IB c #798a92",
+".OC c #798a93",
+"a71 c #798a94",
+"cgL c #798a95",
+"cw1 c #79909d",
+"cu4 c #7997a9",
+"bdV c #799db2",
+"#sO c #79a0b6",
+".6u c #79aae4",
+"#iK c #79bbdd",
+"cbh c #79bbde",
+"#ZP c #79bee4",
+"#YW c #79c5eb",
+"bOA c #79d2fc",
+"#g8 c #79fcf3",
+"#kb c #79fff3",
+"#Gq c #7a2141",
+".yN c #7a217f",
+".YF c #7a2244",
+".3Q c #7a2344",
+"aOc c #7a2446",
+"aig c #7a2546",
+"bM7 c #7a2547",
+"aVY c #7a2548",
+"auh c #7a264a",
+"ahJ c #7a274a",
+"ai# c #7a2949",
+"#x6 c #7a2a41",
+"#sV c #7a2b42",
+"#qF c #7a2b43",
+"#xk c #7a2c50",
+"#By c #7a2d46",
+"aKL c #7a2d51",
+"bAW c #7a2e51",
+"aHN c #7a354f",
+"b66 c #7a3f62",
+"#s5 c #7a4854",
+".zc c #7a4a54",
+".sr c #7a505e",
+"#h9 c #7a5362",
+".lc c #7a5a65",
+"acb c #7a6282",
+"byd c #7a6287",
+"aDG c #7a6685",
+".3t c #7a686c",
+"amC c #7a777e",
+"aNd c #7a7ea3",
+"#p. c #7a8085",
+".AE c #7a8185",
+".Js c #7a8288",
+"#LD c #7a8289",
+".Vq c #7a8389",
+"#6H c #7a8489",
+"afC c #7a848a",
+"#ip c #7a858c",
+".Ln c #7a868c",
+"#Ey c #7a868e",
+"#GC c #7a878f",
+"#Di c #7a8890",
+"#Dh c #7a8891",
+"aY9 c #7a8990",
+"beI c #7a8991",
+".X# c #7a8992",
+"be# c #7a8993",
+"##B c #7a8a92",
+".kf c #7a8a93",
+".mv c #7a8a94",
+".7K c #7a8b93",
+".ls c #7a8b94",
+"bb7 c #7a8b95",
+"cxI c #7a97a9",
+"bfv c #7ab2d3",
+".j6 c #7ab4d7",
+"cgY c #7abadb",
+"cfl c #7ac1e6",
+"cwK c #7ac4ea",
+"aU3 c #7b1e42",
+"#zg c #7b1f41",
+"aLy c #7b1f43",
+".13 c #7b2345",
+".5M c #7b2445",
+".Vf c #7b2446",
+"aLr c #7b2546",
+"aXp c #7b2547",
+"aE6 c #7b2548",
+"aTr c #7b2648",
+"aLn c #7b2747",
+"bmq c #7b2748",
+"aM4 c #7b274b",
+"#r1 c #7b2841",
+"#x9 c #7b2843",
+"#ra c #7b2a42",
+"bop c #7b2a49",
+"#tW c #7b2b42",
+"#u4 c #7b2b43",
+"#rO c #7b2c43",
+"bm2 c #7b334f",
+"aWd c #7b344e",
+"b#Q c #7b354f",
+"b2c c #7b3658",
+"aQO c #7b395d",
+"aR4 c #7b3a5e",
+".EJ c #7b4c55",
+"aoh c #7b4f62",
+"aMO c #7b5375",
+"bGO c #7b557c",
+"apG c #7b5666",
+"caZ c #7b5a74",
+"cgx c #7b6580",
+".sm c #7b6c72",
+"a7e c #7b7999",
+".8L c #7b7f83",
+".AP c #7b8186",
+".Aw c #7b8287",
+"#o6 c #7b8387",
+".oB c #7b8388",
+"#BS c #7b8489",
+"as# c #7b858b",
+"#qq c #7b858d",
+".RO c #7b868d",
+"#Sv c #7b86a8",
+"aSh c #7b878d",
+"brp c #7b888f",
+"#Rr c #7b8890",
+"cw0 c #7b8991",
+"#ig c #7b8992",
+"#GH c #7b8a92",
+".ts c #7b8a93",
+"#BZ c #7b8a94",
+".W6 c #7b8b93",
+".mw c #7b8b94",
+"#GJ c #7b8b95",
+"a9m c #7b8c94",
+"#ik c #7b8c95",
+"ct7 c #7b97a7",
+"bb6 c #7b98ae",
+"b.i c #7b98af",
+"bh# c #7b9fb2",
+"ajO c #7ba0b4",
+"cxJ c #7baac3",
+"c#1 c #7bb7d7",
+".lO c #7bb7da",
+"cpt c #7bb8d6",
+"#.a c #7bbbea",
+"#.# c #7bbcec",
+"b81 c #7bbfe3",
+"cis c #7bc0e5",
+"cfw c #7bc1e5",
+"#.. c #7bc1ed",
+"#0s c #7bc5eb",
+"##3 c #7bc8ed",
+"cfz c #7bcbf1",
+"#dx c #7bfaf5",
+"aJk c #7c1f43",
+"aWw c #7c2044",
+"#yc c #7c2243",
+"aEA c #7c2343",
+"aH2 c #7c2346",
+".0m c #7c2445",
+"aFd c #7c2446",
+"aJw c #7c2447",
+"aXC c #7c2546",
+".Jb c #7c2547",
+"aRE c #7c2548",
+"aEs c #7c2648",
+"alD c #7c2649",
+"#ya c #7c2744",
+"#y7 c #7c2b42",
+"#x7 c #7c2b43",
+"#yg c #7c2b4e",
+"aLX c #7c2c50",
+"#xe c #7c2e4a",
+"aqo c #7c3659",
+"ao1 c #7c395c",
+"aYE c #7c4673",
+".wN c #7c555a",
+"bd4 c #7c557e",
+"bsv c #7c5e67",
+"bL7 c #7c617f",
+"cgF c #7c6b91",
+"#d5 c #7c7f81",
+"bac c #7c7f9e",
+".AY c #7c8286",
+"#rk c #7c848a",
+"a.P c #7c848b",
+".Hk c #7c858b",
+".s# c #7c868c",
+"a.O c #7c8890",
+"#ZD c #7c898f",
+"b86 c #7c8992",
+"bj. c #7c8a91",
+"a44 c #7c8a92",
+"cgK c #7c8a93",
+"a5S c #7c8b92",
+"#rs c #7c8b93",
+".ke c #7c8b94",
+".pr c #7c8b95",
+".lq c #7c8c94",
+".kh c #7c8c95",
+"bBw c #7c8c96",
+"bd9 c #7c8c97",
+"bdb c #7c8d96",
+"#li c #7c8d97",
+"bgZ c #7c96ab",
+"cwY c #7c97a8",
+".H0 c #7c98a8",
+"ayk c #7ca3b8",
+".nG c #7cadce",
+".LE c #7cb2d4",
+"#fD c #7cb3cf",
+"cod c #7cb5d2",
+"#iN c #7cb7d5",
+"aj9 c #7cb8da",
+"aiR c #7ccbf3",
+"ahH c #7d1f43",
+"akD c #7d2044",
+"arC c #7d2144",
+"aVQ c #7d2145",
+"#p1 c #7d2244",
+"agq c #7d2245",
+".WT c #7d2346",
+"aPg c #7d2347",
+".0n c #7d2446",
+".WU c #7d2447",
+"a3W c #7d2547",
+"ahZ c #7d2649",
+"ahQ c #7d284b",
+"aH1 c #7d2a4a",
+"#uk c #7d2b43",
+"#y8 c #7d2b44",
+"#tV c #7d2c44",
+"#EW c #7d344e",
+".Ba c #7d4b56",
+"#Lo c #7d5563",
+"cmS c #7d5565",
+"au3 c #7d5665",
+"acZ c #7d5f68",
+"#85 c #7d6087",
+"ay7 c #7d628a",
+".of c #7d646b",
+"aBO c #7d656b",
+".wY c #7d656c",
+"bKW c #7d666d",
+"cba c #7d6b8f",
+".9g c #7d7e7f",
+"a9J c #7d7e9d",
+".kF c #7d8092",
+"#aw c #7d8184",
+"#cj c #7d8185",
+"#.D c #7d8285",
+".X8 c #7d8386",
+".AD c #7d8388",
+"#jG c #7d858b",
+"#Vf c #7d868b",
+"#Zg c #7d868c",
+"#eS c #7d888f",
+"bsD c #7d8991",
+"aOZ c #7d8a91",
+"bp6 c #7d8a92",
+"bXj c #7d8a93",
+"a4k c #7d8b92",
+"a5# c #7d8b93",
+"cbl c #7d8b94",
+"a1p c #7d8c93",
+"at0 c #7d8c94",
+".pq c #7d8c95",
+"bhz c #7d8d94",
+".nr c #7d8d95",
+".ps c #7d8d96",
+"ask c #7d8e97",
+"#ln c #7d8f99",
+"cpA c #7d98a6",
+"crI c #7d98a9",
+".xk c #7daccd",
+".0G c #7dadce",
+"cw2 c #7daec8",
+"cp# c #7dbee3",
+"#Y3 c #7dbee4",
+"#iJ c #7dc2e0",
+"#fu c #7dd2e9",
+"#bS c #7ddbee",
+".78 c #7debfe",
+"#bQ c #7df4f5",
+"#nc c #7df6e9",
+"#iC c #7dfdf2",
+"#lG c #7dfef2",
+"ai6 c #7e1d40",
+"b2a c #7e1e41",
+"alI c #7e1f44",
+"#yd c #7e2042",
+"akC c #7e2145",
+"aRD c #7e2244",
+"asH c #7e2245",
+"aZF c #7e2345",
+"aeJ c #7e2346",
+"ajd c #7e2347",
+".YG c #7e2446",
+".PY c #7e2447",
+"bKA c #7e2546",
+"b#e c #7e2547",
+"atx c #7e2548",
+"aX1 c #7e2849",
+"arB c #7e2a4d",
+"#vk c #7e2b43",
+"#te c #7e2b44",
+"aQE c #7e2b4e",
+"#sU c #7e2c44",
+"asF c #7e2f51",
+"aSu c #7e3752",
+"a#u c #7e3b5d",
+"#t7 c #7e4a56",
+"bhi c #7e4f62",
+"a1k c #7e5079",
+".I2 c #7e5359",
+"bE0 c #7e5564",
+".MG c #7e575b",
+"bXb c #7e607f",
+".q1 c #7e646c",
+"cin c #7e6580",
+"aIz c #7e686f",
+"bzT c #7e6990",
+"bHi c #7e6c8f",
+".nX c #7e6d72",
+".Ru c #7e7678",
+"aEU c #7e7d9a",
+"aIv c #7e8082",
+"#dZ c #7e8185",
+".AC c #7e8488",
+".AB c #7e8489",
+".uf c #7e858b",
+"#29 c #7e868b",
+".TE c #7e878d",
+"aH. c #7e888e",
+"#vD c #7e888f",
+".HC c #7e8990",
+"b0X c #7e8a91",
+"#W8 c #7e8a92",
+"bqG c #7e8b92",
+"a0J c #7e8c93",
+"ccH c #7e8c95",
+"#BY c #7e8d95",
+".ou c #7e8d96",
+".kd c #7e8e96",
+".qo c #7e8e97",
+"bid c #7e8f97",
+"#7J c #7e8f98",
+"bkp c #7e8f99",
+"##G c #7e919b",
+"#mH c #7e939f",
+"ctj c #7e95a4",
+"bE6 c #7e95aa",
+"bbi c #7ea4b9",
+"aAg c #7ea5ba",
+"azi c #7ea7bd",
+"cm1 c #7eb4d1",
+"ccE c #7eb5d1",
+"#.b c #7ebce9",
+"cgZ c #7ebedf",
+".aT c #7ebee6",
+"#Tc c #7ec0e5",
+"cg1 c #7ec2e4",
+"##2 c #7ed7f1",
+"#dz c #7edfee",
+"a6h c #7f1e41",
+"a37 c #7f1e42",
+"a63 c #7f1f43",
+"asE c #7f2043",
+"agh c #7f2044",
+"a36 c #7f2144",
+"a4O c #7f2145",
+"agr c #7f2244",
+"asG c #7f2245",
+"akE c #7f2246",
+".WS c #7f2345",
+"aij c #7f2346",
+"aH3 c #7f2347",
+"auV c #7f2446",
+".PZ c #7f2447",
+".Ve c #7f2547",
+".3R c #7f2548",
+"aPG c #7f2749",
+"ai7 c #7f274a",
+"akq c #7f274b",
+"#Cf c #7f2845",
+"#u5 c #7f2942",
+"aVt c #7f2a4b",
+"bm5 c #7f2b4b",
+"#sl c #7f2c44",
+"#u3 c #7f2c45",
+"bm4 c #7f2d4c",
+".B7 c #7f3684",
+"bs9 c #7f3a55",
+"aRp c #7f405b",
+"azc c #7f5464",
+"aHB c #7f5766",
+".gl c #7f6368",
+"cfi c #7f647f",
+"cij c #7f6578",
+"bNn c #7f6c8d",
+".CS c #7f6e71",
+".UZ c #7f7879",
+".s0 c #7f8088",
+".AX c #7f8489",
+"aWi c #7f878d",
+"#uy c #7f888d",
+"#Ts c #7f888e",
+"#1Y c #7f898f",
+"#Md c #7f8990",
+"bes c #7f8a90",
+"aL8 c #7f8a91",
+"br0 c #7f8c94",
+"a7Y c #7f8d94",
+"a5. c #7f8d95",
+"cko c #7f8d96",
+"clO c #7f8e95",
+".rl c #7f8e96",
+".mu c #7f8e97",
+".qp c #7f8f97",
+".YP c #7f8f98",
+"bnh c #7f8f99",
+"ba0 c #7f9099",
+"#dc c #7f949f",
+"cst c #7f97a6",
+".uD c #7fa1b6",
+"#XT c #7fa4ba",
+"cv6 c #7fabc3",
+"bFV c #7fafd4",
+"bC6 c #7fb0d6",
+".lL c #7fb6d5",
+"#.d c #7fb8e7",
+"cdK c #7fbce1",
+".95 c #7feffc",
+"#fr c #7ffbf3",
+"anQ c #801e41",
+"anP c #802042",
+"aev c #802043",
+"a62 c #802044",
+"#ye c #802143",
+"a6g c #802144",
+"#yf c #802244",
+"aeI c #802245",
+"#8s c #802345",
+".Vd c #802346",
+"aKj c #802347",
+"boZ c #802348",
+"#xh c #802446",
+".14 c #802447",
+".K9 c #802448",
+".WV c #802547",
+".MM c #802548",
+"b2b c #802649",
+"#ti c #802845",
+"bGT c #802849",
+"ax8 c #80284b",
+"#so c #802a47",
+"#AO c #802c46",
+"#tU c #802d45",
+"aZJ c #803550",
+"bFy c #80456b",
+"aav c #804974",
+"#KW c #805464",
+"#h8 c #805665",
+"arW c #805767",
+".kH c #806679",
+"bM. c #80676d",
+"#kR c #80696f",
+"cdN c #806b8d",
+"bEY c #806c8e",
+"bJM c #806c8f",
+"bk2 c #80829c",
+"akl c #80858c",
+".AW c #80868a",
+".AV c #80868b",
+".AA c #80878b",
+"aj2 c #80878d",
+"#sA c #80888e",
+".Vz c #80898e",
+"#VU c #80898f",
+"aO4 c #808b92",
+"aZn c #808e95",
+"c#4 c #808e96",
+"bf4 c #808f96",
+".up c #808f97",
+".lu c #808f98",
+".rk c #809098",
+".OD c #809099",
+"ccG c #809198",
+"cd5 c #809199",
+"#mP c #80919c",
+"bkn c #8094a2",
+"#9u c #8096a2",
+"b4a c #8096a9",
+"cw7 c #8097a4",
+"bbu c #809bb1",
+"#NE c #80a5bb",
+"#nl c #80a87c",
+".LC c #80b0d0",
+"bCh c #80bae2",
+"bZb c #80bbdd",
+"#.c c #80bbe7",
+"cg0 c #80bfe0",
+".e9 c #80c0e2",
+".99 c #80cbf0",
+"#bR c #80ebf4",
+"anR c #811f42",
+"aqp c #812043",
+"ajc c #812144",
+"#xi c #812244",
+"ahR c #812245",
+"awo c #812344",
+"aui c #812345",
+"#wp c #812346",
+"aii c #812347",
+"bcS c #812348",
+"aIm c #812446",
+".Tq c #812447",
+".K8 c #812448",
+".Jc c #812548",
+"aPw c #812748",
+"#y# c #812a46",
+"#wk c #812c45",
+"#pV c #812d45",
+"#x8 c #812d46",
+"#sT c #812e47",
+"bpn c #813450",
+"aTF c #81355a",
+"awt c #813954",
+".C0 c #81515a",
+".rM c #815362",
+"cjV c #81647f",
+"ahE c #81686d",
+".VZ c #817286",
+"#P9 c #81768d",
+".1H c #81797a",
+".N5 c #817b7c",
+".AU c #81878b",
+".AT c #81878c",
+".SU c #81888c",
+".Ax c #81898d",
+"#Ai c #81898e",
+".0t c #81898f",
+"a6P c #818b91",
+"aiw c #818c93",
+"a4T c #818c94",
+"beH c #818f96",
+"#Si c #818f97",
+"a5i c #818faf",
+"acv c #819098",
+".rg c #819099",
+"#XX c #819198",
+".ki c #819199",
+"#mC c #81919a",
+".56 c #81929a",
+"#Hk c #8197a4",
+"aAT c #81a5b9",
+"#Sg c #81a8be",
+"#Hp c #81a9bf",
+"c.I c #81acc2",
+"c.H c #81adc3",
+".cP c #81b0d0",
+"#dy c #81f1f2",
+"##0 c #81f1fa",
+"anO c #821f41",
+"b65 c #821f42",
+"aKK c #822042",
+"#xj c #822043",
+"bzP c #822143",
+"ahI c #822144",
+"aeu c #822145",
+"aty c #822244",
+"aFB c #822245",
+"aPx c #822246",
+"ahU c #822345",
+"atz c #822346",
+"#p2 c #822347",
+"aVj c #822348",
+"#wr c #822446",
+"alE c #822447",
+".K7 c #822448",
+".Oi c #822547",
+".Jd c #822548",
+"aKo c #822648",
+"#z1 c #822b47",
+"a6i c #822b4c",
+".Ad c #822c86",
+"#u2 c #822d46",
+"bnV c #822e4d",
+"a1I c #823550",
+"aJe c #823752",
+"avJ c #823954",
+"aok c #823d57",
+"bCk c #824a73",
+"bPZ c #824a75",
+"#pm c #825361",
+"aog c #825567",
+".wO c #82565d",
+"aL. c #82656a",
+".gq c #82696f",
+".KP c #827778",
+".U1 c #827c7d",
+".5x c #827d80",
+"aCW c #828689",
+".AS c #82888c",
+".Ky c #82898d",
+".gf c #82898e",
+"aiB c #828a90",
+"#2W c #828b91",
+"#6K c #828c93",
+"#lf c #828d94",
+"a1q c #829097",
+"a7o c #829098",
+"#8A c #829198",
+".Tz c #829199",
+".ot c #82919a",
+"bhx c #82919b",
+".rh c #82929a",
+"#j0 c #82939b",
+"##K c #82959f",
+"cpz c #8298a5",
+"cv1 c #8299a6",
+".uv c #829aa8",
+"b.S c #829bb0",
+"afv c #82a5b9",
+"#Wg c #82a6ba",
+"#fh c #82a7bc",
+"axm c #82a8bc",
+".Fx c #82abc1",
+".2p c #82abc7",
+"c#Y c #82acc3",
+".g4 c #82b0d0",
+"#Wr c #82b2d5",
+"cj0 c #82bade",
+".Y3 c #82bbdc",
+"cvK c #82bde0",
+"#ha c #82d7e8",
+"#kc c #82feef",
+"bJw c #832042",
+"bM5 c #832143",
+"aky c #832244",
+"ajm c #832245",
+"#7B c #832345",
+"#7C c #832346",
+"aih c #832347",
+"#9q c #832348",
+"aSB c #832446",
+".Tp c #832447",
+".L. c #832448",
+".Je c #832548",
+".Ji c #832549",
+"ah0 c #832648",
+"bFC c #832649",
+"#o. c #832b44",
+"#v3 c #832d46",
+"#rN c #832d47",
+"a5C c #833051",
+"aGM c #83314e",
+"bq3 c #833553",
+"#s1 c #83374c",
+"aMo c #833855",
+"aPa c #833e5e",
+"ayD c #834067",
+"bJJ c #834a73",
+"#t3 c #834d5a",
+"#wa c #834e5b",
+"bKI c #834e77",
+"aHF c #835163",
+".xX c #83525b",
+".GR c #83565c",
+"aHt c #83676d",
+".in c #83686e",
+"#Ug c #837287",
+"#1h c #837f85",
+"aWn c #83838c",
+".7n c #838585",
+"#aJ c #83898d",
+"#.Q c #838a8d",
+".5# c #838a8e",
+"#HQ c #838a90",
+"afB c #838b91",
+"amF c #838c92",
+"b.n c #838e93",
+"#7H c #838e96",
+"#bm c #838f97",
+"bNz c #839097",
+"aTT c #839198",
+"bca c #839199",
+".0y c #83929a",
+".zK c #83929b",
+"c.K c #83939a",
+".ri c #83939b",
+"bjL c #83939c",
+"#mO c #8399a5",
+"cu8 c #839aa7",
+"#Hl c #839ba8",
+"bgr c #83a8bc",
+"cb# c #83aace",
+"cqo c #83bbdc",
+".8d c #83c1ef",
+"#ft c #83e4ec",
+"#fs c #83f1f0",
+"ajb c #842143",
+"ajl c #842244",
+"ajk c #842245",
+"agA c #842345",
+"aE5 c #842346",
+"#vr c #842347",
+".0o c #842348",
+"bJy c #842446",
+"#wq c #842447",
+".Oj c #842448",
+".Jg c #842449",
+".K6 c #842548",
+".Jf c #842549",
+"aik c #842649",
+"#v4 c #842c46",
+"#AW c #842c48",
+"#ul c #842d47",
+"#tf c #842e47",
+"#tT c #842e48",
+"bm3 c #843150",
+"bnY c #843955",
+"aeV c #844066",
+"aUW c #844873",
+"aP5 c #844b67",
+"ako c #844f61",
+"a4q c #845564",
+".gN c #845565",
+"#Mx c #845867",
+"#i8 c #845e69",
+"#pO c #84666f",
+".oc c #84696d",
+"aEg c #846f90",
+"blu c #84859c",
+"#jo c #848688",
+"#i0 c #84888b",
+"beN c #8488a7",
+".80 c #848a8e",
+".AR c #848a8f",
+"afA c #848c92",
+".kE c #848c9b",
+"#9C c #848d92",
+"#KS c #848d93",
+"#gV c #848e94",
+"#9N c #848e95",
+".HB c #848f96",
+"#Cv c #849097",
+"bd8 c #849298",
+"#mQ c #84929a",
+".lp c #84939b",
+".Qc c #84939c",
+".sP c #84949b",
+".rj c #84949c",
+"cdV c #84949d",
+"#il c #8496a0",
+"baZ c #849db0",
+"#CE c #84abc2",
+"aox c #84acc1",
+"ckm c #84adc3",
+".Y2 c #84b2d0",
+".Nf c #84b9d9",
+".8e c #84c2ee",
+"##1 c #84e8f5",
+"#g9 c #84faf0",
+"amN c #852042",
+"aeH c #852245",
+"aeX c #852345",
+"aFL c #852346",
+"aV6 c #852448",
+".Jh c #852449",
+"ahS c #852649",
+"bqp c #85264a",
+"bo0 c #85274a",
+"#v5 c #852c47",
+"#rb c #852e47",
+"#u1 c #852e48",
+"#AQ c #852f47",
+"#sS c #852f48",
+"b#P c #853551",
+"#t2 c #85394e",
+"aQU c #853954",
+"aUH c #853a56",
+"aSn c #853c5f",
+"a64 c #853f5e",
+"a7G c #854364",
+"bRo c #854973",
+".zd c #85515c",
+"atS c #855666",
+".fn c #85656b",
+".sz c #85676e",
+"bNu c #856a71",
+"#Qa c #857379",
+"#.Y c #85787a",
+"ae# c #858990",
+".AN c #858b8f",
+"aLB c #858ba9",
+"aoK c #858c91",
+".MS c #858c92",
+"#HM c #858d92",
+"#Xs c #858d93",
+"#Lj c #858e94",
+"#eV c #858f95",
+"a69 c #858f96",
+"#lu c #859097",
+"abp c #859197",
+".JD c #859198",
+"#Cw c #85929a",
+"a2L c #85929b",
+"bd# c #85939a",
+"buD c #85939b",
+"bcA c #85939c",
+".kc c #85949c",
+".or c #85949d",
+".r6 c #85959c",
+".tt c #85959d",
+"bgd c #859aa5",
+"b#w c #859eb1",
+".9K c #85a3b3",
+"cbg c #85a5b5",
+"#D3 c #85aec4",
+"ccv c #85b8da",
+"cjY c #85b9dc",
+".8c c #85c7f0",
+".79 c #85ebff",
+"#lH c #85ffee",
+"bPP c #862244",
+"aM6 c #862346",
+"aM5 c #862347",
+"ah1 c #862446",
+"bFK c #862448",
+"aSS c #862548",
+"bnu c #862549",
+"aLs c #862649",
+"aKi c #86264a",
+"ahT c #86274a",
+"aKp c #862c4d",
+"#v2 c #862e48",
+"#u6 c #86374d",
+"bLZ c #86466f",
+"aXV c #864771",
+"biP c #86495f",
+"bAm c #864a74",
+"avW c #865465",
+"aEi c #86596a",
+".KZ c #865d61",
+"b8T c #866077",
+"civ c #86647f",
+".js c #86666b",
+"aur c #866c72",
+"#NP c #867077",
+"#aZ c #867f82",
+"#pE c #868283",
+".Gh c #868c8f",
+"#ef c #868c90",
+".ly c #868d92",
+"aFl c #868d93",
+"#LQ c #868e94",
+"bNB c #868f94",
+"#p9 c #868f95",
+"btk c #868f96",
+"#p8 c #869096",
+"aS3 c #869097",
+"asg c #869098",
+"aep c #869196",
+"#Bb c #869197",
+"aNj c #869198",
+"bNy c #86939a",
+"a3x c #86949c",
+"#uF c #86949d",
+"bgx c #86959c",
+".rn c #86959d",
+".Li c #86959e",
+".zL c #86969d",
+".we c #86969e",
+"bic c #86969f",
+"bcC c #8697a0",
+"bd7 c #8699a6",
+"bsG c #8699a8",
+"#jY c #869daa",
+"#jU c #869eab",
+"cpu c #86a5b6",
+"c.G c #86a6b8",
+"#Y# c #86aac6",
+".JS c #86b0c7",
+".98 c #86d9f3",
+"aFc c #872246",
+"aZ6 c #872347",
+"aEr c #872448",
+"a61 c #872549",
+"bmp c #87264a",
+"anS c #87274a",
+"#v1 c #872e48",
+"aXj c #872e4e",
+"#AP c #872f48",
+"#zZ c #872f49",
+"#tS c #872f4a",
+"aH0 c #873452",
+"a4L c #873552",
+"agz c #873b61",
+"aWs c #874872",
+"#va c #874e5c",
+"ada c #875377",
+"aNv c #875968",
+"bF4 c #875969",
+"bF7 c #876b72",
+"aKs c #877f9c",
+"aYy c #878490",
+"#6# c #878e93",
+"#F9 c #878e94",
+"amD c #878f95",
+".RX c #879095",
+"#2i c #879096",
+"#4c c #879197",
+"a2I c #879198",
+"#KN c #87939a",
+"bti c #87939b",
+"#mR c #87949b",
+"bi1 c #87949c",
+"#Gc c #87959c",
+"ahm c #87959d",
+"csu c #87959e",
+"#UT c #87969c",
+".tx c #87969d",
+".r7 c #87969e",
+"a0v c #87969f",
+"aJI c #87979e",
+"crF c #8798a1",
+"cth c #8799a4",
+"ct8 c #879aa4",
+"blv c #879dac",
+"#e4 c #87a2b0",
+"c.J c #87a3b2",
+"cbk c #87abbf",
+".2w c #87c0de",
+"b38 c #87ccf0",
+".6e c #87e7fc",
+"#nd c #87f7e7",
+"by7 c #882349",
+"be2 c #882449",
+"bFB c #882548",
+"bqo c #88264a",
+"ai5 c #882749",
+"ajK c #88274a",
+"akF c #88274b",
+"air c #88284b",
+"aMJ c #882a4c",
+".Ao c #882c8a",
+"#xb c #882e49",
+"#pW c #882e4a",
+"#AR c #882f49",
+"#z0 c #882f4a",
+"#u0 c #88304a",
+"#Dw c #88334f",
+"aeF c #883457",
+"bo3 c #883553",
+"bpr c #883b59",
+".Bb c #88565f",
+"bv9 c #885767",
+"#N4 c #88596a",
+"#OP c #885a6a",
+"#Pq c #886372",
+"aJP c #886b72",
+"ccy c #886b8c",
+"cdz c #8885a3",
+"bkl c #88869f",
+"#ca c #888824",
+".V0 c #888889",
+"#jt c #88898a",
+"beO c #888dad",
+"#jh c #888e93",
+"atf c #888f95",
+".Lf c #889095",
+".RQ c #889096",
+"#1x c #889298",
+"##p c #88949b",
+"akY c #88959c",
+"#mS c #88969c",
+"#rB c #88969d",
+".TG c #88969e",
+"bJ0 c #88969f",
+".9O c #88979d",
+".r8 c #88979e",
+".mt c #88979f",
+".2g c #8897a0",
+"#fe c #88989f",
+"cwZ c #889ba4",
+"cu5 c #889ba5",
+"#Hj c #88a1ae",
+"c#2 c #88a1af",
+"clN c #88a2b0",
+"#ij c #88a2b1",
+"coe c #88a3b0",
+".z3 c #88a8b9",
+"axo c #88b1c8",
+"axR c #88b2c9",
+"#04 c #88c3e4",
+"#00 c #88c8e9",
+"#iI c #88cfe1",
+".96 c #88effc",
+"#iD c #88fcee",
+"ajw c #89264a",
+"b#d c #89274b",
+"ail c #89284b",
+"aVp c #89294c",
+"#v0 c #892f4a",
+".Cj c #89358b",
+"b#O c #893755",
+"b#N c #893855",
+"aOX c #893955",
+"aT6 c #89456f",
+"bNh c #894a72",
+"#AK c #895766",
+".EK c #895a60",
+"abM c #895d82",
+"#nA c #89606c",
+"aGs c #8984a1",
+".Gi c #898f92",
+"#f1 c #898f93",
+".26 c #898f94",
+"aei c #899095",
+"asf c #899096",
+"#4o c #899197",
+"#7M c #899297",
+"a1V c #899299",
+"a1T c #89939a",
+"#M. c #89949b",
+"ad1 c #89969d",
+"bly c #89969e",
+"#qn c #89979d",
+"#mT c #89979e",
+"#rA c #89979f",
+"#EJ c #8997a0",
+".sN c #89989f",
+".kj c #8998a0",
+".OE c #8998a1",
+".0z c #8999a0",
+"a2G c #8999a1",
+"coh c #899aa2",
+"bHb c #899bbf",
+"bae c #899fb0",
+"cm2 c #89a2af",
+"#lm c #89a5b3",
+"#bo c #89a5b5",
+"#op c #89a6b6",
+"#m7 c #89aec3",
+"clD c #89b4d4",
+".lP c #89bad7",
+"#h. c #89f6ed",
+"bFA c #8a2446",
+"aeG c #8a2648",
+"aH5 c #8a264a",
+"aja c #8a2749",
+"aKh c #8a274b",
+"alH c #8a284b",
+"bM6 c #8a284c",
+"agB c #8a294c",
+"ajv c #8a2c4e",
+"bFz c #8a2c50",
+"#vl c #8a2f49",
+"#sm c #8a2f4a",
+"aLz c #8a2f4f",
+"#AS c #8a304a",
+"#uZ c #8a304b",
+"#tR c #8a314c",
+"#zI c #8a3653",
+"blX c #8a3a57",
+"adm c #8a436a",
+"bOG c #8a4a72",
+"azD c #8a5d6f",
+"bHk c #8a6582",
+"aqv c #8a6770",
+"aJ0 c #8a6b71",
+".eb c #8a6e73",
+".ft c #8a7076",
+"a#8 c #8a7f87",
+"b8R c #8a82a1",
+"#Mt c #8a9298",
+"#sK c #8a9399",
+"#Tz c #8a949a",
+".HA c #8a959b",
+"#ok c #8a959c",
+"#XZ c #8a959d",
+"bwl c #8a969d",
+"#lh c #8a979e",
+"#F6 c #8a979f",
+"#S9 c #8a989f",
+".tu c #8a98a0",
+".pp c #8a98a1",
+".yi c #8a999f",
+".sO c #8a99a0",
+".Tx c #8a99a1",
+"b#x c #8a99a2",
+".2h c #8a9aa1",
+"bd. c #8a9aa2",
+"bxz c #8a9fb1",
+"#GG c #8aa0ac",
+"#Op c #8aa7b8",
+"#Zx c #8ab1c6",
+".Nd c #8ab5d3",
+".om c #8ab6d0",
+".97 c #8ae7f8",
+"b2# c #8b2447",
+"aI8 c #8b274b",
+"aOw c #8b294c",
+"#AT c #8b304b",
+"bsq c #8b3755",
+"aTe c #8b4269",
+"abY c #8b4770",
+"#Br c #8b5968",
+"aBL c #8b5f6c",
+"aOq c #8b6770",
+"bhs c #8b6986",
+".cD c #8b6a72",
+"bjw c #8b6d73",
+"a6v c #8b7895",
+"csW c #8b86a2",
+"#lY c #8b8f91",
+"#9z c #8b9399",
+".W5 c #8b9499",
+"#52 c #8b959c",
+"#Fy c #8b989e",
+"aWh c #8b989f",
+"awJ c #8b98a1",
+"#mU c #8b99a0",
+".ty c #8b99a1",
+".TZ c #8b99a2",
+".0E c #8b9aa0",
+".tv c #8b9aa1",
+".rf c #8b9aa2",
+"c#X c #8b9aa3",
+"cd4 c #8b9ba1",
+".Ls c #8b9ba2",
+"cpv c #8b9ba3",
+"cv5 c #8b9ea7",
+"c.F c #8b9ea9",
+"#GK c #8ba3b1",
+"#mN c #8ba6b5",
+"#5k c #8bb5cb",
+"av2 c #8bb5cc",
+"cdZ c #8bbdd8",
+".cK c #8bc0de",
+".lj c #8bc1e0",
+".8b c #8bd0f4",
+"#kd c #8bfdec",
+"aXi c #8c274b",
+"aI9 c #8c284c",
+"asI c #8c294c",
+"aJj c #8c2a4d",
+"aYd c #8c2c4e",
+"aMN c #8c2d4e",
+"#w0 c #8c2e4a",
+"#um c #8c304b",
+"#sR c #8c314c",
+"alL c #8c3152",
+"#w1 c #8c344e",
+"a5D c #8c4e69",
+"#pd c #8c5063",
+".bq c #8c5b6b",
+"asZ c #8c5e6e",
+".I3 c #8c6165",
+".vT c #8c6368",
+"brc c #8c687a",
+"bBu c #8c6d73",
+".ue c #8c6e74",
+"aUd c #8c7378",
+".uF c #8c7479",
+".XL c #8c8a8c",
+"#kv c #8c8f92",
+"#an c #8c9296",
+"bl4 c #8c92a2",
+"#cc c #8c9396",
+"#63 c #8c9398",
+".H3 c #8c9499",
+"#zy c #8c949a",
+"aFP c #8c969d",
+"aIp c #8c979d",
+"bqF c #8c979e",
+"c.E c #8c999f",
+"#tu c #8c99a0",
+"a.B c #8c99a2",
+"b87 c #8c9aa0",
+".Ql c #8c9aa1",
+".tr c #8c9aa2",
+".Qu c #8c9aa3",
+"b#y c #8c9ba1",
+".tw c #8c9ba2",
+".Vm c #8c9ba3",
+"cg5 c #8c9ca2",
+".Sc c #8c9ca3",
+"bb8 c #8c9ca4",
+"bdG c #8c9ea9",
+"#HW c #8cb2c6",
+"#0P c #8cb4c9",
+".6t c #8cb6e4",
+".yy c #8cbddb",
+"aim c #8d2a4d",
+"aLx c #8d2a4e",
+"#wZ c #8d304c",
+"#AU c #8d314b",
+"#vZ c #8d314c",
+"aMn c #8d3a57",
+"blo c #8d3c5b",
+"#v6 c #8d3f54",
+"a6j c #8d516b",
+".wP c #8d5b64",
+"bgn c #8d5b6a",
+".LK c #8d5d9f",
+"aEV c #8d6b7d",
+"atI c #8d6f76",
+"bOQ c #8d7076",
+"#pc c #8d7e86",
+".T9 c #8d84b3",
+"baV c #8d86a0",
+".CB c #8d9296",
+".4O c #8d9497",
+".Hl c #8d959a",
+"aou c #8d959b",
+".5S c #8d969b",
+"av7 c #8d969c",
+"#ZW c #8d979c",
+"#1C c #8d979d",
+"#XS c #8d989f",
+"#1X c #8d99a0",
+"#Mg c #8d99a1",
+"cu7 c #8d9a9f",
+"#2g c #8d9aa1",
+"bK1 c #8d9aa2",
+"cog c #8d9ba0",
+"#j1 c #8d9ba1",
+".M7 c #8d9ba2",
+".Dx c #8d9ba3",
+".vi c #8d9ba4",
+"#m2 c #8d9ca2",
+".vj c #8d9ca3",
+".BI c #8d9ca4",
+"a1W c #8d9ca5",
+".JK c #8d9da3",
+".7N c #8d9da4",
+"#m1 c #8d9da5",
+"a7X c #8da2b2",
+"#tF c #8da3af",
+"#Hm c #8da9b9",
+"#lS c #8dac72",
+"cuH c #8db5d4",
+"#2c c #8db6cc",
+"azj c #8db7ce",
+"#4u c #8dbddc",
+".OX c #8dc1de",
+".6m c #8dc4ef",
+".6l c #8dc7f1",
+".6f c #8de5fd",
+".8. c #8de8fc",
+"aI7 c #8e284c",
+"bo1 c #8e2a4d",
+"aRx c #8e2b4e",
+"#BD c #8e304c",
+"#uY c #8e324d",
+"#FL c #8e3452",
+"#FF c #8e3753",
+"aHX c #8e3855",
+"a4P c #8e556f",
+"a3h c #8e566e",
+"#IZ c #8e5a6a",
+"#OU c #8e5c6c",
+"aP# c #8e5d7f",
+"bP7 c #8e6783",
+"bNo c #8e6883",
+"afY c #8e6d72",
+"bwe c #8e6d8d",
+"aK. c #8e6f78",
+".pf c #8e7076",
+"apY c #8e7e85",
+".Zq c #8e8b8d",
+"#cn c #8e9193",
+".3k c #8e9497",
+".Kx c #8e9498",
+".le c #8e9599",
+".vd c #8e959a",
+".Jt c #8e969b",
+"#Z0 c #8e969c",
+"#Wn c #8e979d",
+"ckn c #8e999e",
+"#ID c #8e999f",
+"#EC c #8e99a0",
+"cv4 c #8e9aa0",
+".Lm c #8e9aa1",
+"bMf c #8e9aa2",
+"b89 c #8e9ba0",
+"b88 c #8e9ba1",
+"aTS c #8e9ba2",
+"bOW c #8e9ba3",
+"cti c #8e9ca1",
+"cm3 c #8e9ca2",
+".Lr c #8e9ca3",
+".OT c #8e9ca4",
+"bwn c #8e9da3",
+".wf c #8e9da4",
+".kb c #8e9da5",
+"aWm c #8e9ea6",
+"#Fx c #8ea6b1",
+"cmL c #8ea9c8",
+"#lj c #8eaebe",
+"crm c #8eb1d1",
+"#dn c #8eb8ce",
+"#Dm c #8eb9d1",
+".6k c #8ecaf3",
+"#iH c #8edde1",
+"#h# c #8eefea",
+"#iE c #8ef9eb",
+"ac2 c #8f294c",
+"aVs c #8f2a4d",
+"aiq c #8f2b4e",
+"#Bz c #8f304b",
+"#tg c #8f314c",
+"#BA c #8f314d",
+"#tQ c #8f324d",
+"aol c #8f3c59",
+"abB c #8f5568",
+".xY c #8f5a64",
+"auw c #8f5a6b",
+"a.b c #8f5b81",
+"#gr c #8f5c6c",
+"bzS c #8f5c82",
+"aof c #8f5d6d",
+"cj3 c #8f5d6e",
+"aHC c #8f5e6f",
+".C1 c #8f5f66",
+".t8 c #8f6a70",
+"aAP c #8f6f75",
+".XK c #8f8495",
+".Rp c #8f8586",
+"#12 c #8f878c",
+"aZc c #8f8892",
+"bjb c #8f92ab",
+"#to c #8f979c",
+"#eU c #8f999f",
+"aO3 c #8f9aa2",
+"b9. c #8f9ba0",
+"crH c #8f9ca1",
+"cu6 c #8f9ca2",
+"#T2 c #8f9ca3",
+"aNi c #8f9ca4",
+"crG c #8f9da2",
+"cof c #8f9da3",
+".Sb c #8f9da4",
+".qq c #8f9da5",
+"bmF c #8f9da6",
+"baf c #8f9ea4",
+".JV c #8f9ea5",
+".lo c #8f9ea6",
+"a5U c #8f9fa5",
+"#mV c #8fa1aa",
+"bGa c #8fa3b6",
+"a8D c #8fa5b4",
+"#gP c #8fafbf",
+"#mI c #8fb0c2",
+"#VX c #8fb2c7",
+"aAh c #8fb5ca",
+".Xo c #8fb8d3",
+"azR c #8fb9cf",
+"#xJ c #8fbbd3",
+"ans c #8fbcd4",
+".8j c #8fbfe6",
+".8f c #8fc6ec",
+".yI c #901f93",
+"alF c #902b4e",
+"a35 c #902b4f",
+"ah2 c #902c4f",
+"#AV c #90314d",
+"#wY c #90314e",
+"#BB c #90324d",
+"#vY c #90324e",
+"#uX c #90334f",
+"aXh c #903353",
+"aPK c #903a58",
+"bmx c #903d5c",
+"aWx c #90425c",
+"aRo c #905766",
+".#O c #905c6d",
+"aNu c #905d6e",
+"brL c #90626f",
+"bW9 c #906d8d",
+"aIL c #906e72",
+".gT c #906f75",
+"aSj c #907075",
+"aDL c #907078",
+"bf3 c #907080",
+"aAd c #907176",
+"#pN c #907880",
+"#Rz c #90819c",
+"#f8 c #908485",
+"bD1 c #908fb1",
+".jf c #90979b",
+"afG c #90979d",
+".51 c #90989e",
+"auO c #909aa0",
+"#jO c #909aa1",
+"#jR c #909ca3",
+"byn c #909ca4",
+"cv2 c #909da2",
+".Qd c #909da3",
+".HL c #909da4",
+".5X c #909da5",
+".Sd c #909ea5",
+".Fu c #909ea6",
+"#Aj c #909ea7",
+".BJ c #909fa6",
+"aJJ c #909fa7",
+"a5T c #90a0a6",
+"bfo c #90a0a7",
+"#lo c #90a2ab",
+"#oH c #90a3ad",
+"#m0 c #90a4ae",
+"ccD c #90a6b3",
+"#ih c #90b0c1",
+"cs2 c #90b0d0",
+".BZ c #90b3c5",
+"#Av c #90b4c8",
+"bha c #90bad0",
+"#tA c #90bcd2",
+".8a c #90ddf7",
+".8# c #90e7fa",
+"#lI c #90fee9",
+".yM c #911e95",
+"aJ. c #91294d",
+"bId c #912a4e",
+"b#c c #912b4e",
+"bqq c #912b4f",
+"ain c #912c4f",
+"aTJ c #912f51",
+"#vm c #91324e",
+"#vX c #91334f",
+"a0m c #913957",
+"ax. c #913a57",
+"aaE c #913c5f",
+"a3g c #91415f",
+"ai. c #914a62",
+"cuJ c #915c6d",
+"cpc c #915d6d",
+"clw c #916482",
+"#LT c #916571",
+".MH c #916669",
+"bSH c #916782",
+".K2 c #916b6f",
+"#l4 c #916e78",
+"bJI c #916f95",
+".od c #917076",
+"aT# c #917176",
+"#pt c #91757f",
+".6V c #919496",
+".Gq c #91969a",
+".Qg c #91989d",
+".Os c #91989e",
+"#VZ c #91999e",
+"#Ty c #919aa0",
+".Hz c #919ba1",
+"aua c #919ba2",
+"a.N c #919da3",
+"#tG c #919ea5",
+"#QB c #919ea6",
+".Nc c #919fa6",
+".un c #919fa7",
+"bcB c #919fa8",
+"bag c #91a0a6",
+".Lt c #91a0a7",
+"bdH c #91a0a8",
+"beJ c #91a1a7",
+"#lr c #91a1a9",
+"#Gb c #91a7b2",
+"#DS c #91a7b4",
+"csb c #91b0d0",
+"ctR c #91b0d1",
+"#d. c #91b3c5",
+"#UQ c #91b3c7",
+".DG c #91bed5",
+"cg4 c #91ceec",
+".6g c #91e5fd",
+"#iG c #91e8e4",
+"#iF c #91f1e6",
+"#ne c #91fae5",
+"aVr c #922a4e",
+"aI6 c #922b4f",
+"awp c #922c4f",
+"aeN c #922d4f",
+"b19 c #922d50",
+"ax9 c #922e51",
+"#rc c #92314d",
+"#un c #92324e",
+"#BC c #92324f",
+"#wX c #92334f",
+"#uW c #923450",
+"bkU c #924361",
+"aXb c #92446d",
+".ze c #925d66",
+"#LX c #925d6e",
+".GS c #926468",
+"bqw c #926584",
+"co8 c #926d8d",
+"bL6 c #926e8a",
+"bzh c #92708d",
+"aT2 c #927176",
+"aVG c #927277",
+"#Pu c #92777c",
+"bdN c #927b84",
+"#pM c #927c82",
+"#hZ c #92898d",
+".9a c #928d8d",
+".6z c #92908d",
+".Wi c #92989b",
+".fg c #92999d",
+"aEK c #92999e",
+"at2 c #929ba0",
+"#eT c #929ba1",
+"aD7 c #929ca3",
+"#vG c #929da3",
+"#J5 c #929ea5",
+"#Kr c #929fa6",
+"#8K c #929fa7",
+".JW c #92a0a7",
+".Lj c #92a0a8",
+".36 c #92a1a8",
+"#Ix c #92a1a9",
+"bjJ c #92a2ab",
+"#rz c #92aebe",
+"bfL c #92bed5",
+"ayO c #92bfd7",
+".Lv c #92c0d7",
+"b#3 c #92c0d8",
+"#iO c #92c7ca",
+".4f c #92e1fc",
+"#ke c #92fbe8",
+"bqn c #932b4f",
+"aio c #932d50",
+"#Cd c #93334f",
+"#vS c #933552",
+"#B6 c #933756",
+"#y3 c #935969",
+".p0 c #935a6c",
+".vU c #936469",
+"a38 c #93647b",
+"agf c #936b74",
+"brk c #937177",
+"aUS c #937277",
+"aIW c #937279",
+"bQa c #93737a",
+"bHp c #93747a",
+".s5 c #937b80",
+"a65 c #937f9d",
+".Zp c #938c98",
+"aga c #938d93",
+".07 c #939192",
+"#L9 c #93999e",
+".R6 c #939a9f",
+"abl c #939da4",
+"a9V c #939ea4",
+"aak c #939eb7",
+"c#W c #939fa5",
+".M1 c #939fa6",
+".DA c #93a0a7",
+"a1X c #93a0a8",
+"b5W c #93a1a7",
+".HK c #93a1a8",
+".sM c #93a1a9",
+"beq c #93a2a8",
+".TY c #93a2a9",
+"bl6 c #93a2aa",
+"bja c #93a3ad",
+"bBy c #93a6b9",
+"#lp c #93aab6",
+"#mW c #93abb7",
+"#Hi c #93b5c7",
+"cfv c #93b8cd",
+"#e2 c #93b9cd",
+".OV c #93bbd4",
+".vn c #93bed4",
+"#Xa c #93beda",
+".0Q c #93bee2",
+"#PX c #93bfd7",
+".0H c #93c3dc",
+".f3 c #93ccea",
+"bH8 c #942b4e",
+"aKg c #942c4f",
+"aeY c #942d50",
+"am# c #942e51",
+"bpl c #943052",
+"#xW c #94314e",
+"#wm c #943150",
+"#sn c #94324f",
+"#xX c #943350",
+"a2t c #943857",
+"bs8 c #943c59",
+"aom c #94415c",
+"bKL c #94476e",
+"bfh c #944770",
+"awG c #945d6e",
+"#Nh c #945f70",
+"b2m c #94637a",
+"btT c #946572",
+"bwf c #946e8b",
+"bY6 c #946e8c",
+".tf c #946f77",
+"bwi c #947379",
+"bE5 c #94757b",
+"#QQ c #94797f",
+"bIk c #9487aa",
+"a7H c #9491ad",
+"#cJ c #949596",
+"#cI c #949696",
+".6S c #94989a",
+"#i. c #949ca1",
+"adX c #949da4",
+".#V c #949ea3",
+".aP c #949ea5",
+"b#6 c #949fa5",
+"#Wm c #949fa6",
+"#Fp c #94a0a7",
+"#e7 c #94a1a8",
+"#G8 c #94a1a9",
+".rm c #94a2a9",
+".i7 c #94a2aa",
+"bgw c #94a3a9",
+".Dz c #94a3aa",
+"bVL c #94a8bc",
+"#mZ c #94abb7",
+"ciO c #94afbd",
+"#DT c #94b5c6",
+"#mM c #94b8cc",
+"#DV c #94b9cb",
+"#gO c #94b9cc",
+".DO c #94b9cd",
+"#DU c #94bacd",
+"ayQ c #94c0d8",
+".DI c #94c3db",
+".6j c #94d4f7",
+"b2. c #952c4f",
+"aeW c #952d4f",
+"aKk c #952d50",
+"b#b c #952d51",
+"aVw c #952e51",
+"aLm c #953354",
+"#wW c #953450",
+"#vW c #953451",
+"au2 c #954a63",
+"a0E c #954f74",
+".eV c #955c6d",
+"adM c #955e6f",
+"#eJ c #955f70",
+"bVw c #956d8c",
+".O3 c #9571ad",
+".v3 c #957479",
+"bSJ c #95747a",
+"ccl c #95809f",
+".5l c #958a8c",
+".2P c #958b8c",
+".7h c #958f90",
+"#cK c #959697",
+"#cH c #959798",
+".Gj c #959a9d",
+"#lV c #959a9e",
+"afK c #959ca0",
+"ajL c #959da2",
+"aAo c #959da3",
+"aIs c #959fa5",
+"#ia c #959fa6",
+".JC c #95a0a7",
+"#c7 c #95a1a8",
+".MX c #95a2a9",
+"#de c #95a2aa",
+"cip c #95a2c0",
+".yj c #95a3aa",
+".i6 c #95a3ab",
+".i5 c #95a4ab",
+"bc9 c #95a6b0",
+"bK3 c #95a8b8",
+"a9h c #95a9b8",
+"#lq c #95aebb",
+"b7l c #95b6d2",
+"#3v c #95b7c9",
+"#H0 c #95c3db",
+".mI c #95cbe6",
+"add c #962a4c",
+"aLt c #962d51",
+"aje c #962e51",
+"aip c #962e52",
+"a4N c #962f52",
+"#vo c #963150",
+"#xV c #963250",
+"#th c #963350",
+"#CN c #963450",
+"#CO c #963451",
+"#xU c #963551",
+"#vV c #963552",
+"aNG c #963757",
+"aTN c #963a59",
+"aH6 c #963d5b",
+"aX2 c #964b64",
+"bpX c #964d6d",
+"beZ c #965c6e",
+"#Ic c #965e6f",
+"aP4 c #965f6e",
+"#N1 c #965f70",
+"aqY c #966071",
+"aoe c #966171",
+".K0 c #966a6d",
+".u0 c #966a6e",
+"a5E c #967487",
+"aZT c #968b93",
+"#cG c #969697",
+".3a c #96999b",
+"#HU c #969ca2",
+"avj c #969da2",
+"aB9 c #96a0a5",
+"aab c #96a0a6",
+"#9v c #96a1a7",
+"#e5 c #96a3aa",
+"#im c #96a3ab",
+"bnF c #96a4aa",
+".ms c #96a4ab",
+".qn c #96a4ac",
+".Xa c #96a5ac",
+"aTU c #96a5ad",
+"cmP c #96a9c6",
+".39 c #96b2c2",
+"cdY c #96b7c9",
+"ais c #96c3db",
+"bbP c #96c5dc",
+".zW c #96c6df",
+".6h c #96e3fb",
+"aJ# c #972d51",
+"akz c #972e52",
+"aeK c #972f52",
+"#xc c #973250",
+"#uo c #973351",
+"#Ce c #973450",
+"#vn c #973451",
+"aVP c #973a59",
+"bxX c #973a5c",
+"adb c #973b5f",
+"bow c #973d5d",
+"#u7 c #975664",
+"#OT c #976070",
+".p8 c #977379",
+"arH c #97757b",
+"#ms c #978189",
+".A4 c #978b8b",
+".2N c #97979e",
+"#kN c #979ca0",
+"#3G c #979da1",
+"aPO c #97a1a7",
+"aB8 c #97a2a9",
+".RN c #97a3aa",
+"#vC c #97a4ab",
+"#zC c #97a4ac",
+".5Z c #97a5ab",
+".yk c #97a5ac",
+".i8 c #97a5ad",
+"#Xj c #97a6ad",
+"bp# c #97a7ae",
+".ON c #97aab4",
+"#mY c #97b1be",
+"#mX c #97b2c0",
+"#oG c #97b3c2",
+"#oA c #97b7c8",
+".aQ c #97b7c9",
+"#DW c #97bccf",
+".yv c #97bcd6",
+"#lx c #97bed3",
+"#db c #97c0d5",
+"be5 c #97c3db",
+".wi c #97c7e0",
+".Qy c #97c9e3",
+".6n c #97c9ef",
+".mo c #97cce7",
+".6i c #97dcfa",
+"#lJ c #97ffe5",
+"aI5 c #982e52",
+"amO c #982f52",
+"aKn c #982f53",
+"a5B c #983053",
+"#CR c #983351",
+"#CP c #983451",
+"#xT c #983452",
+"bsp c #983556",
+"#vU c #983653",
+"#xM c #983755",
+"aOV c #983a59",
+"bbb c #983b59",
+"aet c #983b5a",
+"ac1 c #983b5b",
+"ahG c #983c5b",
+"byM c #983c5e",
+"#w2 c #984b5f",
+"axO c #985e6f",
+"#gp c #986071",
+"#LW c #986171",
+".wQ c #98636c",
+"aEh c #986981",
+"bT4 c #986e8c",
+"bxl c #98728f",
+"bRv c #98767c",
+"ad8 c #98777c",
+"bjG c #98889f",
+"a11 c #988994",
+"bjd c #988ba7",
+".A6 c #988e8e",
+".ED c #989092",
+"bmD c #9896a7",
+".6O c #989ea1",
+".cn c #989ea2",
+".RK c #989ea3",
+".eg c #989fa3",
+".Q. c #989fa4",
+"aCV c #98a0a5",
+"amE c #98a1a6",
+"#uN c #98a1a7",
+"#uC c #98a2a8",
+"#wJ c #98a2a9",
+"#2h c #98a3a9",
+"ccC c #98a4aa",
+"#b# c #98a4ab",
+".Qb c #98a5ac",
+"#mD c #98a5ad",
+".i4 c #98a6ad",
+".Xn c #98a6ae",
+"#T7 c #98a7c3",
+"a3m c #98a8b0",
+"#bq c #98bed1",
+"#sG c #98bfd3",
+"#ox c #98c1d6",
+"#rI c #98c4db",
+"azQ c #98c5dd",
+".BP c #98c8e1",
+".4l c #98caf5",
+".4g c #98e0fd",
+"alM c #992f53",
+"bOq c #993053",
+"#CM c #993451",
+"#uV c #993552",
+"#wV c #993553",
+"bat c #993958",
+"au1 c #993a59",
+"aMm c #993a5a",
+"avI c #993b5a",
+"bSC c #99446c",
+".b3 c #996574",
+".Bc c #99666d",
+".vV c #99676e",
+".EL c #996a6f",
+"cfj c #996b85",
+"biV c #996d8c",
+"aJZ c #997478",
+".kZ c #997d82",
+"a6k c #997f91",
+"a0z c #998a95",
+".06 c #99949b",
+".OJ c #999fa4",
+".w# c #99a0a5",
+"aec c #99a1a6",
+"aQW c #99a3a9",
+"#xy c #99a3aa",
+"a7M c #99a4aa",
+"#cZ c #99a5ac",
+".7O c #99a6ad",
+".ov c #99a6ae",
+"avf c #99a7ad",
+".Dy c #99a7ae",
+".JX c #99a7af",
+"aZL c #99afba",
+"#oz c #99bccd",
+".VD c #99bcd4",
+"#oB c #99bdcf",
+"#oy c #99bdd0",
+"#DX c #99bfd1",
+"#DY c #99c0d5",
+"#jX c #99c1d6",
+"#ll c #99c3d8",
+".pI c #99c4de",
+"#FB c #99c6dd",
+"#GN c #99c7df",
+"#tB c #99c8e0",
+"#5j c #99d2ee",
+"#nf c #99fbe1",
+"axE c #9a2f53",
+"avD c #9a3053",
+"#Dt c #9a3552",
+"#Ds c #9a3553",
+"a00 c #9a3a5a",
+"a.j c #9a3b5e",
+"bHf c #9a456b",
+"aas c #9a5579",
+".qV c #9a5e71",
+"#I5 c #9a6071",
+"aIY c #9a6373",
+"aac c #9a6f78",
+"biw c #9a8098",
+"a1g c #9a8b95",
+".4z c #9a9793",
+".#Z c #9a9bb7",
+"aC# c #9a9c9e",
+".Rb c #9a9ea0",
+"#dT c #9a9fa2",
+"auE c #9aa0a5",
+"#Rj c #9aa1a6",
+".2# c #9aa2a6",
+"a1b c #9aa3aa",
+"axs c #9aa6ae",
+"#oI c #9aa7ad",
+"#m3 c #9aa7ae",
+".i3 c #9aa7af",
+"#xG c #9aa8ae",
+".lv c #9aa8af",
+".wd c #9aa8b0",
+"#Sm c #9aa9c6",
+"#sH c #9abdd0",
+".pl c #9abdd1",
+".T0 c #9abdd5",
+".FH c #9abfd1",
+"#ZG c #9ac2dd",
+".4m c #9ac7f3",
+"ab. c #9ac9e0",
+".g1 c #9ad0eb",
+"#kf c #9afbe4",
+"adc c #9b2f51",
+"aYX c #9b2f53",
+"b.w c #9b3154",
+"#CQ c #9b3452",
+"#Du c #9b3553",
+"#Dr c #9b3653",
+"#wU c #9b3654",
+"#vT c #9b3754",
+"#B4 c #9b3858",
+"aYY c #9b3959",
+"akp c #9b3b5b",
+"#xY c #9b445c",
+"a.c c #9b466c",
+".jT c #9b6071",
+"#Kw c #9b6171",
+"#a7 c #9b6172",
+"aLh c #9b6173",
+".xZ c #9b656e",
+"#U8 c #9b6d7c",
+"bKP c #9b6e88",
+"bDa c #9b6f8c",
+"b0M c #9b708c",
+"aBM c #9b777c",
+".oe c #9b777d",
+".u9 c #9b787e",
+"#kD c #9b7d86",
+"a90 c #9b7d92",
+"a3n c #9b929a",
+".Py c #9b9fa2",
+".AO c #9b9fa3",
+"#.v c #9ba0a4",
+"#Fo c #9ba0a5",
+".6J c #9ba1a4",
+"abn c #9ba1a6",
+"#8t c #9ba2a6",
+"bfb c #9ba3a8",
+"aiJ c #9ba3a9",
+"#YL c #9ba6ad",
+".OH c #9ba7ad",
+"##C c #9ba7ae",
+"#jK c #9ba8ae",
+".5V c #9ba8af",
+".i2 c #9ba8b0",
+"adY c #9ba9af",
+".ln c #9ba9b0",
+".2e c #9ba9b1",
+"#DR c #9badb7",
+"#oC c #9bc4d9",
+"#1M c #9bc5e1",
+"a.z c #9bc6db",
+"#uR c #9bc9e2",
+"bhb c #9bcae2",
+".zR c #9bcae3",
+".dm c #9bcfea",
+"bIc c #9c2f53",
+"akG c #9c3154",
+"#xd c #9c3453",
+"bpm c #9c3858",
+"aRw c #9c3b5a",
+"bba c #9c3b5b",
+"axI c #9c3d5c",
+"bm9 c #9c3e5f",
+"aon c #9c4260",
+"byc c #9c4b71",
+".ay c #9c6172",
+"aNt c #9c6273",
+"aHD c #9c6374",
+".I4 c #9c7072",
+"aC7 c #9c787e",
+"alp c #9c898f",
+"ac. c #9c899d",
+"#nu c #9c9197",
+".VM c #9c9ecf",
+".Iv c #9ca0a3",
+".8z c #9ca1a4",
+"#.z c #9ca1a5",
+"#6X c #9ca3a7",
+".Hm c #9ca3a8",
+"#sJ c #9ca4a9",
+"aAk c #9ca5ac",
+"#xB c #9ca6ac",
+"#gG c #9ca7ad",
+"bcz c #9ca8ae",
+".Ll c #9ca8af",
+"bBx c #9ca8b0",
+"#if c #9ca9af",
+".Qk c #9ca9b0",
+".i9 c #9ca9b1",
+"aQ2 c #9caab0",
+".oq c #9caab1",
+"#uA c #9caab2",
+"#7E c #9cb4c0",
+".Qw c #9cbfd7",
+".J5 c #9cc0d3",
+"#He c #9cc5d9",
+"#oq c #9cc6db",
+"#ii c #9cc6dc",
+"#ow c #9cc7dc",
+"#tC c #9ccae2",
+".yp c #9ccbe3",
+".Lx c #9ccde5",
+"#qj c #9ccee7",
+".8g c #9cceec",
+"ajx c #9d3054",
+"aUE c #9d3154",
+"byI c #9d3155",
+"bqr c #9d3255",
+"#E# c #9d3654",
+"#xS c #9d3655",
+"aY3 c #9d3657",
+"#wT c #9d3755",
+"bs7 c #9d3c5c",
+"#OS c #9d6373",
+"acc c #9d6d87",
+".hT c #9d767d",
+".e2 c #9d787e",
+"cre c #9d829e",
+"bi# c #9d8491",
+".Yl c #9d9394",
+".7q c #9d999b",
+".X6 c #9da0a3",
+"aFm c #9da1a4",
+".Kz c #9da2a5",
+".Ju c #9da4a9",
+"#Xm c #9da4aa",
+"#WI c #9da7ad",
+"a5n c #9da8ae",
+"abm c #9da8af",
+"#uM c #9da9b0",
+".Qn c #9da9b1",
+".RT c #9daab0",
+".JL c #9daab1",
+".pv c #9daab2",
+"bZi c #9dabb1",
+"##F c #9dabb2",
+".Vl c #9dabb3",
+"#Wz c #9dabbf",
+"aWV c #9daeb6",
+"a9P c #9daeb8",
+"cdX c #9db4c2",
+".h1 c #9dbfd6",
+".ut c #9dc2d5",
+"#oD c #9dc5da",
+"#Hn c #9dc6da",
+"#jV c #9dc9e0",
+"axS c #9dcde6",
+".xe c #9dcde7",
+"##H c #9dcee6",
+".4k c #9dd0f6",
+".oI c #9dd2ed",
+".yJ c #9e1ca1",
+"aJa c #9e3054",
+"aVq c #9e3155",
+"ajj c #9e3255",
+"#wl c #9e3654",
+"#Ea c #9e3655",
+"#E. c #9e3755",
+"#wR c #9e3756",
+"aJd c #9e3b5b",
+"aSm c #9e5066",
+"#84 c #9e5479",
+"aMf c #9e6273",
+"azb c #9e6374",
+".kI c #9e6d80",
+"#pq c #9e717f",
+".SR c #9e7283",
+"aWo c #9e7c81",
+"#QI c #9e7e98",
+"a3i c #9e8e9d",
+"b.q c #9e9bb1",
+".UG c #9ea2a5",
+".Me c #9ea3a5",
+".R. c #9ea3a6",
+"#7R c #9ea4a9",
+"#mA c #9ea5a9",
+"awT c #9ea5ab",
+".7H c #9ea6ab",
+"aP0 c #9ea8ae",
+"bco c #9ea9af",
+"bcX c #9ea9b0",
+"#uB c #9eaab1",
+"brq c #9eaab2",
+"bMe c #9eabb1",
+".OL c #9eabb2",
+"#bn c #9eabb3",
+"b0Y c #9eacb2",
+".i1 c #9eacb3",
+"bHu c #9eb0c0",
+"#sI c #9eb9c6",
+".Za c #9eb9e1",
+"#b8 c #9ec5bb",
+"#Hh c #9ec8de",
+"#tD c #9ecae0",
+"a9z c #9ecae1",
+"baJ c #9ecee6",
+".Sf c #9ed0e9",
+".2x c #9edbfd",
+".4h c #9edffc",
+".yL c #9f1ca1",
+"aYm c #9f3155",
+"aeL c #9f3256",
+"bqm c #9f3356",
+"#yW c #9f3454",
+"#Dv c #9f3553",
+"#Dq c #9f3654",
+"#D9 c #9f3755",
+"#xR c #9f3756",
+"#wS c #9f3856",
+"aHZ c #9f3b5b",
+"bkg c #9f4a66",
+"bh8 c #9f4f74",
+"apH c #9f6475",
+"cdB c #9f6577",
+"cqj c #9f657e",
+"cri c #9f667f",
+"bOM c #9f6e88",
+"bSF c #9f718d",
+"abj c #9f777c",
+".Pw c #9fa3a6",
+".25 c #9fa5a7",
+".YO c #9fa6aa",
+"#6N c #9fa6ab",
+"auI c #9fa8ae",
+"#jJ c #9faab1",
+".TB c #9fabb1",
+".JB c #9fabb2",
+".JM c #9fabb3",
+"#qo c #9facb2",
+".wg c #9facb3",
+".LA c #9facb4",
+".uo c #9fadb4",
+"acy c #9faeb4",
+"a#Z c #9faeb5",
+"b5T c #9fb2c1",
+"#oF c #9fc5d8",
+"#oE c #9fcadf",
+"#sF c #9fcbe1",
+"#mL c #9fcde4",
+"bcl c #9fcfe8",
+"#HX c #9fd0e9",
+".nn c #9fd2eb",
+".nC c #9fd2ec",
+".4j c #9fd7fb",
+".4i c #9fddfa",
+"#kg c #9ff8df",
+"#lK c #9ffedf",
+"bso c #a03155",
+"aeM c #a03256",
+"atA c #a03356",
+"#D8 c #a03756",
+"#xQ c #a03857",
+"brH c #a03c5c",
+"bL3 c #a0436a",
+"bEV c #a0446a",
+"bAi c #a04865",
+"bzR c #a05175",
+"#v7 c #a05a6a",
+"#Bn c #a05c6f",
+"#JF c #a06274",
+".c9 c #a06374",
+"#OQ c #a06375",
+"aod c #a06475",
+".zf c #a06c73",
+".C2 c #a07074",
+"csY c #a0718d",
+"a5N c #a0728e",
+"bEQ c #a07294",
+"aK5 c #a0797f",
+"asQ c #a07a80",
+"#RA c #a07e98",
+"#pl c #a07f89",
+"#pD c #a09091",
+"a5F c #a098a5",
+".ZJ c #a0a4a6",
+"#no c #a0a5a8",
+"bnI c #a0a6aa",
+".RY c #a0a7ab",
+".z6 c #a0a7ac",
+"ap8 c #a0a8ad",
+"aw# c #a0aaaf",
+"#lt c #a0aab0",
+"awh c #a0abb0",
+"brr c #a0acb2",
+"#ld c #a0acb3",
+"#e8 c #a0adb3",
+".pt c #a0adb4",
+".yh c #a0adb5",
+"#oh c #a0aeb4",
+".JY c #a0aeb5",
+"#8B c #a0aeb6",
+"a66 c #a0aec0",
+"bis c #a0b0b6",
+"#Y9 c #a0b0b7",
+"b00 c #a0b2c3",
+".HZ c #a0c4d5",
+"#dM c #a0c8b4",
+".4n c #a0cbf2",
+"#mJ c #a0cde4",
+".lR c #a0cfe5",
+"#e3 c #a0cfe7",
+"#lk c #a0d0e8",
+"adV c #a0d0e9",
+"#zF c #a0d1ea",
+"#nk c #a0d4a4",
+"#ng c #a0fbda",
+"aMK c #a13256",
+"aw6 c #a13457",
+"#E1 c #a13656",
+"a32 c #a13a5a",
+"b#M c #a13b5b",
+"aUa c #a13c5c",
+"#y0 c #a1415c",
+"bNj c #a1436a",
+"ac0 c #a15a6f",
+"#H6 c #a16274",
+"aHE c #a16374",
+".jY c #a1727b",
+"bsC c #a17a7f",
+"#pk c #a17f89",
+"bbr c #a18ca1",
+"#VE c #a1a1a7",
+".tN c #a1a4a9",
+".R5 c #a1a7ab",
+"avo c #a1a7ac",
+"aJK c #a1a8ac",
+"avi c #a1a8ae",
+"#YS c #a1aab0",
+"#4i c #a1abb1",
+".9N c #a1acb2",
+"#jL c #a1acb3",
+".RL c #a1adb3",
+".Ov c #a1adb4",
+"#EA c #a1adb5",
+"ah# c #a1adb6",
+".TC c #a1aeb4",
+".OF c #a1aeb5",
+".i0 c #a1aeb6",
+".Xw c #a1aedc",
+".x. c #a1afb5",
+".kk c #a1afb6",
+"#2T c #a1afb7",
+".oL c #a1c3da",
+".BN c #a1cadf",
+"#ov c #a1d0e7",
+"bhd c #a1d0e8",
+"#Ho c #a1d1ea",
+"amb c #a1d2ea",
+"ayl c #a1d3ec",
+".Xl c #a1d4ee",
+".zQ c #a1d5ef",
+"bTS c #a23357",
+"akU c #a23457",
+"a95 c #a23458",
+"ajD c #a23558",
+"#Eb c #a23655",
+"#yV c #a23757",
+"#yU c #a23857",
+"#xP c #a23958",
+"bsl c #a23c5d",
+"a#g c #a2446a",
+"abN c #a24b6e",
+"alJ c #a24c67",
+"a#f c #a25174",
+"bgH c #a25776",
+"Qtw c #a26274",
+"bVy c #a26b84",
+"b2l c #a2728c",
+"cql c #a2728d",
+"bSL c #a27a7e",
+"caY c #a28099",
+"#kA c #a2858e",
+"#aR c #a29a9a",
+".Ta c #a29e9e",
+".0# c #a29fa0",
+"#YT c #a2a4aa",
+".CE c #a2a6a9",
+".Ww c #a2a7a9",
+".1x c #a2a7aa",
+"#5c c #a2a7ab",
+".M5 c #a2a8ad",
+"#9Z c #a2a9ad",
+"ajS c #a2a9ae",
+"bt2 c #a2abb0",
+"auJ c #a2adb3",
+".0w c #a2adb4",
+"afW c #a2aeb4",
+"#VB c #a2aeb5",
+"#4h c #a2aeb6",
+".55 c #a2afb5",
+".j. c #a2afb6",
+".ka c #a2afb7",
+".sQ c #a2b0b7",
+"a7. c #a2b2b9",
+".hX c #a2bac7",
+".oj c #a2becb",
+".Y0 c #a2c3d3",
+"#tE c #a2c3d4",
+".nl c #a2c4d6",
+"b2r c #a2c4df",
+".aX c #a2c5db",
+".mn c #a2cbdf",
+"#qi c #a2cce2",
+"#hl c #a2cfb7",
+".lh c #a2d0e6",
+"bgs c #a2d0e7",
+"awM c #a2d3eb",
+"bcV c #a2d3ec",
+".rb c #a2d7f0",
+"#GF c #a2d7f1",
+"#kh c #a2f2db",
+".yK c #a31ca5",
+"aJi c #a33457",
+"awq c #a33458",
+"aNH c #a33558",
+"#FK c #a33757",
+"a3f c #a3375a",
+"#EY c #a33857",
+"#EZ c #a33858",
+"#xO c #a33959",
+"a1D c #a33a5c",
+"au0 c #a33c5d",
+"aes c #a36475",
+".GT c #a37477",
+"#OG c #a38187",
+"b.f c #a38da2",
+"boD c #a3a1a8",
+".1q c #a3a5a7",
+"#cv c #a3a6a7",
+".ZW c #a3a7aa",
+".Yd c #a3a8aa",
+".J7 c #a3a9ac",
+"#2w c #a3a9ad",
+"#WK c #a3abaf",
+"ath c #a3acb1",
+"akX c #a3adb3",
+"#Fq c #a3aeb3",
+".zN c #a3aeb4",
+".tz c #a3aeb5",
+".Vr c #a3afb4",
+"#Ld c #a3afb5",
+"ciP c #a3afb6",
+"#NJ c #a3afb7",
+"atk c #a3b0b6",
+".2c c #a3b0b7",
+".RS c #a3b0b8",
+".mr c #a3b1b8",
+"aAY c #a3b1b9",
+"bJ2 c #a3b2c0",
+"#45 c #a3b5bf",
+"cqL c #a3bccc",
+".gZ c #a3becc",
+".ql c #a3c0d0",
+".f2 c #a3c2d2",
+"#DZ c #a3c4d5",
+"#Pb c #a3c5d6",
+".r3 c #a3c6d8",
+".2D c #a3caf6",
+".8i c #a3cee8",
+"#Ga c #a3cfe5",
+"#PU c #a3d1e9",
+".BO c #a3d3eb",
+"bdr c #a3d4ed",
+"bhc c #a3d5ed",
+".ol c #a3d5ee",
+".xd c #a3d6ee",
+".hY c #a3d6ef",
+"#ou c #a3d7f0",
+".qk c #a3d8f1",
+"#bp c #a3d8f2",
+"anr c #a3d8f3",
+".yo c #a3d9f2",
+".2y c #a3d9fb",
+"alG c #a43458",
+"aH4 c #a43558",
+"auj c #a43559",
+"ajE c #a43659",
+"#E0 c #a43756",
+"aI4 c #a4375a",
+"#xN c #a43a59",
+"a#v c #a43c60",
+"#yX c #a4425e",
+"bD. c #a44268",
+"#9c c #a4647b",
+"#Bo c #a46575",
+"acj c #a46d86",
+".vW c #a46e75",
+".wR c #a46f75",
+"aK9 c #a47c80",
+".rZ c #a47d82",
+".EC c #a49a9b",
+"cxB c #a49eb7",
+".8P c #a4a6a7",
+"#aE c #a4a6a8",
+"a6l c #a4a6b0",
+".SY c #a4a8aa",
+".mk c #a4a9ad",
+"aDt c #a4aaae",
+".nw c #a4abae",
+".w8 c #a4abaf",
+"#vI c #a4abb0",
+"av8 c #a4acb2",
+"a6o c #a4aeb4",
+"#Gd c #a4afb4",
+".Ox c #a4afb5",
+".W8 c #a4b0b6",
+"#DN c #a4b0b7",
+"##o c #a4b1b7",
+".nt c #a4b1b8",
+".Qt c #a4b1b9",
+".Qv c #a4b2b8",
+".qr c #a4b2b9",
+"cdW c #a4b2ba",
+"a4a c #a4b4bb",
+"bp7 c #a4b4c2",
+".wa c #a4b6c0",
+".pi c #a4b7c1",
+".iT c #a4b8c2",
+".DC c #a4b9c3",
+".4v c #a4bad8",
+"#MZ c #a4c2d3",
+".e7 c #a4c4d5",
+"#qm c #a4c5d6",
+"#ah c #a4c6c0",
+".HN c #a4c6d7",
+".ei c #a4c6d8",
+".DE c #a4c7d8",
+"#j6 c #a4c9dd",
+".dl c #a4cadd",
+"#As c #a4ccdf",
+"#B0 c #a4cce0",
+"#Bf c #a4cce1",
+".qj c #a4cde2",
+"#ry c #a4d1e8",
+"#zE c #a4d2e8",
+"#Pg c #a4d3ea",
+".j4 c #a4d4eb",
+".T2 c #a4d4ed",
+".pj c #a4d5ee",
+".iU c #a4d6ee",
+".pk c #a4d6ef",
+".ej c #a4d7f0",
+".ok c #a4d8f1",
+"#Wd c #a4d8f2",
+"#UM c #a4d8f3",
+".nm c #a4d9f3",
+"#ot c #a4d9f4",
+"bfM c #a4daf4",
+"#kj c #a4e8d3",
+"#ki c #a4edd6",
+"avE c #a53458",
+"akS c #a53559",
+"ajJ c #a53659",
+"#D7 c #a53757",
+"akR c #a5385a",
+"#wQ c #a53958",
+"#yT c #a53959",
+"a3c c #a53a5b",
+"aXD c #a53b5c",
+"aPf c #a53c5d",
+"#Bq c #a56575",
+"#OR c #a56677",
+"arX c #a56678",
+"bgI c #a56e80",
+"bvj c #a5728c",
+"bkd c #a57c82",
+"bNs c #a57f84",
+"bIz c #a58085",
+"aGq c #a58186",
+"#cN c #a59ea1",
+".Mz c #a59fa1",
+".4Y c #a5a7a9",
+".64 c #a5a7aa",
+".KF c #a5a9ac",
+".R4 c #a5abaf",
+"#1F c #a5abb0",
+"a4Q c #a5abb4",
+"avw c #a5aeb4",
+"aBA c #a5afb4",
+"#Zv c #a5afb5",
+"#gY c #a5b0b6",
+"#gJ c #a5b1b7",
+"#xA c #a5b1b8",
+"abd c #a5b2b8",
+".g9 c #a5b2b9",
+"QtD c #a5b2ba",
+".a0 c #a5b3ba",
+"b.j c #a5b3bb",
+".ve c #a5b4bb",
+"#Dk c #a5b5bd",
+"bbT c #a5b6bb",
+"bl5 c #a5b7c0",
+"#On c #a5bbc7",
+".sK c #a5bcc8",
+"#CC c #a5beca",
+"#yI c #a5becb",
+"aSb c #a5c0ce",
+".lQ c #a5c3d8",
+".Fv c #a5c6d7",
+".DD c #a5c7d8",
+"#bE c #a5c7da",
+"aY8 c #a5c9db",
+"#qt c #a5cbde",
+"aS1 c #a5cce0",
+"aVz c #a5cde2",
+".cJ c #a5cee2",
+"aXG c #a5cfe4",
+"b.E c #a5d0e6",
+"#Rm c #a5d1e7",
+"b#4 c #a5d2e8",
+".8h c #a5d3e9",
+".VC c #a5d3ea",
+"#GL c #a5d4eb",
+"aZM c #a5d5ed",
+"#At c #a5d7ef",
+"#ql c #a5d7f0",
+"a08 c #a5d8ef",
+"aWf c #a5d8f0",
+".mH c #a5d8f1",
+"#da c #a5d8f2",
+"#5P c #a5d9f1",
+".g0 c #a5d9f2",
+"#d# c #a5d9f3",
+"#YI c #a5daf2",
+"#jW c #a5daf3",
+".Qr c #a5daf4",
+"#os c #a5dbf5",
+"a07 c #a5dcf6",
+"bsn c #a63358",
+"ajC c #a63659",
+"ajf c #a6365a",
+"#yS c #a63a5a",
+"bo7 c #a64062",
+"bvL c #a64369",
+"aju c #a64765",
+".DV c #a649aa",
+"agg c #a6526b",
+"crf c #a66272",
+"a3r c #a66a86",
+"bAp c #a6708d",
+"aCt c #a67e83",
+".Z8 c #a6a3a3",
+"#hx c #a6a4a6",
+"#aB c #a6a7a9",
+"aQ3 c #a6a8ac",
+".GC c #a6a9ab",
+".Iz c #a6aaac",
+".NW c #a6aaad",
+"#WC c #a6aab2",
+"ahi c #a6abb0",
+"a39 c #a6abb4",
+".R3 c #a6acb0",
+"#Xv c #a6acb1",
+"a5G c #a6acb5",
+"#WE c #a6adb1",
+"#q. c #a6adb2",
+"#Za c #a6aeb3",
+"#qb c #a6afb4",
+"amu c #a6b0b5",
+"#Yq c #a6b0b6",
+"a0r c #a6b0b7",
+".Lh c #a6b1b7",
+"#BW c #a6b1b8",
+".Y1 c #a6b2b8",
+"#gC c #a6b2b9",
+"#Me c #a6b2ba",
+"#Uk c #a6b3b9",
+".#6 c #a6b3ba",
+".#7 c #a6b3bb",
+".ff c #a6b4ba",
+".aZ c #a6b4bb",
+"#3x c #a6b4bc",
+"ag9 c #a6b5bc",
+"#Ar c #a6b6be",
+".59 c #a6b6bf",
+"#gS c #a6b7c0",
+"#m6 c #a6b8c1",
+"#zD c #a6b9c4",
+"aQY c #a6bac4",
+".JU c #a6bac5",
+"#wN c #a6bbc7",
+"aYr c #a6bcc7",
+".ra c #a6beca",
+"aUM c #a6c0cd",
+"bhe c #a6c0ce",
+"#Qt c #a6c1d0",
+"bbR c #a6c2d0",
+"#oP c #a6c2d1",
+"#TW c #a6c3d1",
+"aWX c #a6c3d2",
+".Qs c #a6c3d3",
+".vf c #a6c4d2",
+"a8s c #a6c4d3",
+".JO c #a6c4d4",
+"a7L c #a6c5d4",
+"#TV c #a6c5d5",
+"#S3 c #a6c7d8",
+".Sa c #a6c8d9",
+"#D1 c #a6cadc",
+"#FA c #a6cbde",
+".TX c #a6ccdf",
+"bbj c #a6cde0",
+".2C c #a6cef8",
+"bds c #a6cfe4",
+".bx c #a6d0e6",
+"be7 c #a6d1e6",
+"bdX c #a6d3ea",
+".6o c #a6d3ef",
+"be6 c #a6d5ed",
+"b#m c #a6d7ef",
+"#TU c #a6d7f0",
+".lK c #a6d8ef",
+".nB c #a6d8f0",
+"#D2 c #a6d8f1",
+"at. c #a6d9f1",
+"#B1 c #a6d9f2",
+".OQ c #a6d9f3",
+".e8 c #a6daf3",
+".S# c #a6daf4",
+".DF c #a6dbf4",
+"#EP c #a6dbf5",
+"#Au c #a6dcf5",
+"aY7 c #a6dcf6",
+"a7K c #a6ddf5",
+"#lL c #a6fdda",
+"alN c #a73458",
+"brI c #a73459",
+"aJb c #a73559",
+"#yR c #a73a5a",
+"#yP c #a73b5b",
+"bOH c #a74368",
+"aZX c #a75072",
+".H6 c #a759ad",
+"#w3 c #a76070",
+"#x0 c #a76272",
+"bEZ c #a76677",
+"c#H c #a76880",
+"a4f c #a76a86",
+"biI c #a7757f",
+"bjc c #a77994",
+"aMc c #a77d82",
+".fV c #a77f84",
+"alk c #a78187",
+".oV c #a7878b",
+"aJT c #a791a7",
+"#Oz c #a7939d",
+"#mh c #a7999c",
+"cly c #a7a3bc",
+"#hW c #a7a7a8",
+".IH c #a7aaad",
+".Mn c #a7abae",
+"#ks c #a7acaf",
+"#rl c #a7adb1",
+".Hn c #a7adb2",
+".Hy c #a7afb4",
+"aea c #a7b1b6",
+"aiN c #a7b2b8",
+"#TZ c #a7b2b9",
+".3Z c #a7b3b8",
+"#NI c #a7b3b9",
+"##E c #a7b3ba",
+".nP c #a7b3bb",
+".Y6 c #a7b4ba",
+"QtK c #a7b4bb",
+".kw c #a7b4bc",
+".Qi c #a7b5bb",
+".cX c #a7b5bc",
+".h7 c #a7b5bd",
+"bcp c #a7b6bc",
+".uh c #a7b6be",
+"a4U c #a7b7be",
+"#Jr c #a7b7c0",
+"b#8 c #a7b8be",
+"aTR c #a7b8c0",
+"aZN c #a7b8c1",
+".tp c #a7b8c2",
+"b#n c #a7b9bf",
+"aTP c #a7b9c2",
+"bcm c #a7b9c3",
+"aWg c #a7bac4",
+".HT c #a7bbc5",
+"bem c #a7bcc6",
+".Lz c #a7bcc7",
+".YZ c #a7bcc8",
+"bKZ c #a7bccf",
+"a09 c #a7bdc6",
+".Xm c #a7becb",
+"#uQ c #a7bfcb",
+".Nb c #a7bfcc",
+".OR c #a7c0ce",
+"bcW c #a7c2d1",
+"#D0 c #a7c3d1",
+"#xI c #a7d0e4",
+"#TX c #a7d4eb",
+".OZ c #a7d6ee",
+"#EM c #a7d7ee",
+".oH c #a7d7ef",
+".2z c #a7d8fb",
+"#or c #a7d9f1",
+".Ly c #a7d9f2",
+"#HY c #a7d9f3",
+".iV c #a7daf2",
+".li c #a7daf3",
+"#EQ c #a7daf4",
+"aa9 c #a7dbf4",
+".FA c #a7dbf5",
+"axn c #a7dcf5",
+"#mK c #a7dcf6",
+".JP c #a7dcf7",
+".Fw c #a7ddf7",
+"a8p c #a7e1fa",
+"#kk c #a7e6ce",
+"#nh c #a7f9d5",
+"bYX c #a83457",
+"ay. c #a83559",
+"aUb c #a8365a",
+"adn c #a8375a",
+"#zN c #a8395a",
+"#FG c #a83a5a",
+"#yQ c #a83b5b",
+"byL c #a83b5c",
+"aMl c #a83c5e",
+"bBl c #a84469",
+"bho c #a8446b",
+"#yZ c #a85168",
+"aJl c #a8556c",
+"#xZ c #a85d6d",
+"aoc c #a86778",
+"b33 c #a8718c",
+"cr1 c #a87f95",
+"bDe c #a88084",
+"#Uh c #a88b8f",
+"#UZ c #a897b1",
+".Z4 c #a8a4a4",
+"bbU c #a8aab3",
+".Cv c #a8acaf",
+".4L c #a8adaf",
+".i# c #a8aeb1",
+"aW2 c #a8afb2",
+"baO c #a8afb7",
+"#Ym c #a8b0b6",
+"b#o c #a8b0b9",
+"axr c #a8b2b8",
+"bsF c #a8b2b9",
+".ur c #a8b3b9",
+"#yH c #a8b3ba",
+"a8F c #a8b4b9",
+".9W c #a8b4ba",
+".oP c #a8b4bb",
+"QtL c #a8b4bc",
+"#qs c #a8b5bb",
+"QtF c #a8b5bc",
+".ab c #a8b5bd",
+".nq c #a8b6bc",
+".a4 c #a8b6bd",
+"a5I c #a8b6be",
+"#PV c #a8b6bf",
+"#T3 c #a8b7be",
+".M8 c #a8b7bf",
+"bIJ c #a8b7c4",
+"#Fz c #a8b8c0",
+"b.H c #a8b9bf",
+"#qe c #a8b9c3",
+"bbk c #a8bac3",
+"#3R c #a8bad1",
+"#rH c #a8bbc5",
+"#0m c #a8c1d8",
+"#EL c #a8c8d8",
+".ui c #a8c9da",
+".Lu c #a8cadc",
+".6s c #a8cae6",
+".lS c #a8ccdc",
+"a7I c #a8cee4",
+".2B c #a8d2fc",
+".b9 c #a8d3e9",
+".0R c #a8d6fe",
+"a85 c #a8d8f0",
+"#1v c #a8d9f1",
+"#W1 c #a8d9f2",
+"#Pd c #a8daf4",
+".j5 c #a8dbf3",
+".Na c #a8dbf4",
+"#Hg c #a8dbf5",
+"#ER c #a8dcf5",
+"#sE c #a8dcf6",
+"#EO c #a8ddf6",
+"#EN c #a8ddf7",
+"#Hf c #a8def8",
+"a7J c #a8e0f9",
+"#kl c #a8e3c9",
+"aTI c #a93559",
+"ajy c #a9365a",
+"bGS c #a9375b",
+"#zM c #a9385a",
+"#FJ c #a93959",
+"aJh c #a93b5d",
+"aOv c #a93c5e",
+"aaF c #a93d61",
+"#zO c #a93e5d",
+"bnz c #a94265",
+"bvd c #a96276",
+"b7g c #a9697f",
+".Bd c #a9767a",
+"byl c #a98085",
+"aiU c #a98388",
+"bU# c #a98792",
+"ag# c #a98b90",
+"cn1 c #a99ab3",
+"b#9 c #a9a6b0",
+"#fP c #a9a7a9",
+".81 c #a9acae",
+"aPP c #a9acaf",
+".69 c #a9adaf",
+".R2 c #a9afb3",
+"#gw c #a9b0b4",
+"bc0 c #a9b2b9",
+"#0T c #a9b3b9",
+".bw c #a9b4ba",
+"#Ao c #a9b4bb",
+"a7# c #a9b4bc",
+".Xs c #a9b5ba",
+".r2 c #a9b5bb",
+".cS c #a9b5bc",
+".BH c #a9b5bd",
+".rA c #a9b6bc",
+".bH c #a9b6bd",
+".er c #a9b6be",
+".qH c #a9b7bd",
+".fd c #a9b7be",
+"a1# c #a9b8be",
+"a3j c #a9b8bf",
+".j3 c #a9b8c1",
+"a1. c #a9b9bf",
+"a1S c #a9bbc1",
+".rc c #a9c5d3",
+".2E c #a9cbf5",
+"#it c #a9d1e5",
+"#vO c #a9d1e6",
+".QA c #a9d2e7",
+".VG c #a9d3e7",
+"#tM c #a9d5ea",
+"#iP c #a9d7b9",
+".pH c #a9d7ed",
+"a84 c #a9d8ef",
+".Nh c #a9d9ef",
+"#YJ c #a9d9f0",
+"#0# c #a9d9f1",
+"bgt c #a9daf1",
+"#rx c #a9dcf4",
+".JT c #a9dcf5",
+"a99 c #a9dcf6",
+"a#T c #a9ddf6",
+"#yJ c #a9ddf7",
+"#qk c #a9def7",
+".HO c #a9dffa",
+"bIb c #aa3559",
+"ajF c #aa375b",
+"#zL c #aa395b",
+"#yO c #aa3b5c",
+"avH c #aa3d5f",
+"a1K c #aa4463",
+"bzc c #aa446a",
+"#Ky c #aa687a",
+"cmK c #aa6c83",
+"aOk c #aa7f84",
+".hq c #aa8286",
+"aJY c #aa8387",
+"bbl c #aa9da9",
+"#UW c #aaa8ad",
+"#Xi c #aaabb3",
+"aHb c #aaacae",
+"biZ c #aaacb1",
+"a1R c #aaacb5",
+".NS c #aaadb0",
+".5c c #aaaeaf",
+"#.R c #aaaeb0",
+".Gd c #aaaeb1",
+"#Iw c #aaafb3",
+"aae c #aab0b4",
+".9E c #aab1b5",
+"#Xp c #aab1b6",
+"a#W c #aab3b8",
+".HM c #aab4ba",
+".OS c #aab4bb",
+".JN c #aab5ba",
+".sJ c #aab5bb",
+".ug c #aab5bc",
+"bvs c #aab6bb",
+".pN c #aab6bc",
+".ko c #aab6bd",
+".37 c #aab6be",
+".lG c #aab7bd",
+"QtE c #aab7be",
+"#J1 c #aab7bf",
+".9G c #aab8be",
+".OG c #aab8bf",
+"#S4 c #aab8c0",
+"a68 c #aab9c0",
+"a2E c #aabac0",
+"a2F c #aabac1",
+"bSO c #aabac8",
+"#EK c #aabdc6",
+"#dm c #aac3d0",
+"#sN c #aac8d7",
+".dr c #aac9de",
+"#We c #aacada",
+".Sh c #aacbdc",
+"#S7 c #aacbde",
+".tq c #aaccdd",
+".J4 c #aacee0",
+"#8w c #aad3e7",
+"#Os c #aad4ea",
+"aTQ c #aad7ee",
+".2A c #aad7fc",
+".T3 c #aadaf1",
+".VF c #aadaf2",
+"#9t c #aaddf6",
+"act c #aaddf7",
+"awL c #aadef7",
+"#GM c #aadef8",
+"#Dl c #aadff8",
+"#HZ c #aadff9",
+"aLu c #ab375b",
+"bH9 c #ab385b",
+"ajB c #ab385c",
+"#FI c #ab395a",
+"#FH c #ab3a5a",
+"#zK c #ab3a5b",
+"aJc c #ab3b5e",
+"#Ax c #ab3c5d",
+"aOW c #ab3d5e",
+"#Pp c #ab6c7d",
+"cgu c #ab6f81",
+"bht c #ab7284",
+".x0 c #ab757a",
+".K1 c #ab7b7b",
+"aHu c #ab8185",
+"#Ml c #ab868b",
+"bb0 c #ab92a4",
+".8x c #ab9f4f",
+".A5 c #aba2a2",
+".Mw c #aba4a5",
+"#hT c #ababac",
+".Ix c #abaeb1",
+"aiG c #abb1b6",
+".Lq c #abb2b6",
+"a2D c #abb3bb",
+"#iq c #abb4ba",
+"bet c #abb5bb",
+"ahn c #abb5bc",
+"b.a c #abb5bd",
+"#xH c #abb6bb",
+"#Tr c #abb6bc",
+"#EF c #abb6bd",
+"bXf c #abb6ca",
+"a8E c #abb7bc",
+"#j5 c #abb7bd",
+".k# c #abb7be",
+"#YQ c #abb7bf",
+".cl c #abb8be",
+".h3 c #abb8bf",
+".M0 c #abb8c0",
+"#Ex c #abb9bf",
+".Lk c #abb9c0",
+"a67 c #abbbc1",
+"a6m c #abbcc2",
+".OM c #abbcc5",
+"#TY c #abc3ce",
+"#UN c #abc3d0",
+".Xr c #abc6d4",
+"#Js c #abc7d5",
+"#fg c #abc8d6",
+"aY6 c #abc8d7",
+"#gZ c #abcfe1",
+"#XR c #abd4ea",
+"ajN c #abdaf1",
+"aWW c #abdef6",
+".HS c #abdef8",
+".OP c #abdff8",
+"#CD c #abdff9",
+"apR c #abe0f9",
+"bdW c #abe0fa",
+"aKl c #ac375b",
+"b#a c #ac385b",
+"a4M c #ac385c",
+"aU2 c #ac395c",
+"#EX c #ac3a5b",
+"aLl c #ac3b5e",
+"#Ay c #ac3d5e",
+"auZ c #ac3d5f",
+"aoo c #ac4262",
+"bC7 c #ac597c",
+"bFW c #ac5a7c",
+"bgo c #ac6879",
+"bJN c #ac6f87",
+".u1 c #ac797c",
+".EM c #ac7b7d",
+"#pr c #ac848f",
+".tS c #ac8c90",
+"baP c #ac91a0",
+"#l8 c #ac929a",
+"b.I c #acadb5",
+"#hO c #acafb2",
+"#ap c #acb0b3",
+"#dR c #acb1b3",
+".R1 c #acb1b4",
+"amx c #acb2b6",
+"#Wb c #acb3b7",
+"a7N c #acb3ba",
+"#1c c #acb4b9",
+"#dl c #acb6bc",
+"#3N c #acb7bc",
+"#mE c #acb7bd",
+"bvr c #acb7be",
+".HU c #acb7bf",
+"bnf c #acb8bd",
+".kl c #acb8be",
+".ch c #acb8bf",
+".nQ c #acb9be",
+".bL c #acb9bf",
+".wo c #acb9c0",
+"#gK c #acbac0",
+".FB c #acc5d2",
+".wm c #acc6d4",
+".sL c #acccdc",
+"#W2 c #accfe1",
+".DJ c #acd1e3",
+".yn c #acd1e4",
+".M9 c #acd4e8",
+".Y4 c #acd5ea",
+"ben c #acd6eb",
+".LG c #acd7eb",
+"be8 c #acd7ec",
+"#43 c #acd8ed",
+"atY c #acdaf1",
+".Sg c #acdcf3",
+".BT c #acdcf4",
+".Xq c #acddf4",
+"bbQ c #ace0fa",
+"#nj c #acebbd",
+"#ni c #acf5cb",
+"#lM c #acfcd4",
+"axF c #ad375b",
+"bVk c #ad385c",
+"bq8 c #ad395c",
+"aLw c #ad3a5d",
+"#zJ c #ad3c5d",
+"aSW c #ad3d5f",
+"#Bm c #ad516c",
+"#y1 c #ad596e",
+"aU4 c #ad5b72",
+".M. c #ad649e",
+"#Bp c #ad697b",
+"cr7 c #ad6980",
+"co7 c #ad6b7f",
+"a2Q c #ad6c87",
+"bRq c #ad738d",
+"bF1 c #ad748d",
+".I5 c #ad7c7e",
+"aai c #ad8084",
+".ER c #ad8186",
+"aJU c #ad92a4",
+"a1Q c #ad9eab",
+"bhu c #ada0a9",
+"#hS c #ada4a5",
+".Tc c #adaaaa",
+"b5R c #adadbe",
+".5u c #adaeae",
+"#aL c #adb0b3",
+"#fJ c #adb1b4",
+".Ga c #adb2b4",
+".MT c #adb2b6",
+".hd c #adb3b6",
+"awX c #adb3b7",
+"##n c #adb4b7",
+"a10 c #adb4ba",
+"#Am c #adb5b9",
+"acI c #adb6bc",
+"a5m c #adb7bb",
+"#lc c #adb7bc",
+"#ro c #adb7bd",
+".4. c #adb8bd",
+"#BT c #adb8be",
+"#vF c #adb8bf",
+"#F7 c #adb9be",
+".ow c #adb9bf",
+".g8 c #adb9c0",
+"b2v c #adbabf",
+".j# c #adbac0",
+".uA c #adbac1",
+"#GA c #adbbc2",
+".Y5 c #adbbc3",
+"baN c #adbec3",
+"#PW c #adbfc8",
+".8r c #adc0c0",
+"#Zu c #adc4d0",
+".0S c #add4fe",
+"#6J c #add6ea",
+"bfN c #add6eb",
+"#TT c #addbf1",
+"ayP c #ade1fb",
+"ajz c #ae385c",
+"ajA c #ae395d",
+"aUG c #ae3b5e",
+"#B5 c #ae3c5e",
+"ayE c #ae4467",
+"#zP c #ae4d68",
+"aLA c #ae6379",
+"#zU c #ae6777",
+"be0 c #ae687a",
+"aP. c #ae697c",
+"bp3 c #ae8287",
+"#QG c #ae859b",
+"ad6 c #ae8c91",
+"#i4 c #ae989f",
+"b#p c #ae9ca8",
+"bn6 c #aea0a9",
+"#jw c #aea3a8",
+"aXP c #aea9af",
+"afP c #aeb1b5",
+".En c #aeb2b4",
+".Ec c #aeb2b5",
+".Cn c #aeb3b5",
+"acS c #aeb3b7",
+".yg c #aeb4b7",
+".JJ c #aeb4b8",
+"#0z c #aeb5b9",
+"avh c #aeb5ba",
+"a5J c #aeb5bc",
+"afX c #aeb6b9",
+"#bv c #aeb6ba",
+"#Vd c #aeb7bb",
+"bbS c #aeb7bd",
+"a9B c #aeb8bd",
+"#jI c #aeb8be",
+"##M c #aeb9be",
+".2q c #aeb9bf",
+"#Iz c #aeb9c0",
+"a9Q c #aebabf",
+".cg c #aebac0",
+".lF c #aebac1",
+".0J c #aebbc0",
+".tL c #aebbc1",
+".Qm c #aebbc2",
+"#YK c #aebec7",
+".0I c #aec0ca",
+".r4 c #aecad9",
+"#2a c #aecbda",
+"#qg c #aed1e2",
+"a8r c #aed5e9",
+"a8q c #aed7eb",
+"a.y c #aedaef",
+".Qz c #aeddf4",
+"a0q c #aedef6",
+"aym c #aee3fd",
+"avF c #af395d",
+"ajn c #af3a5d",
+"a34 c #af3b5e",
+"aUJ c #af3d5f",
+"b#K c #af3d60",
+"bIo c #af4165",
+"aI3 c #af4263",
+"bGP c #af466a",
+"bxg c #af536f",
+"bBi c #af5477",
+"#yY c #af5f72",
+".K. c #af66bc",
+"bfI c #af697b",
+".#H c #af6a7c",
+"bP6 c #af738b",
+"bbm c #af748a",
+"bit c #af8896",
+"a1P c #af8d9c",
+"bde c #af8f98",
+"a2C c #af9fac",
+"ad5 c #afb2b6",
+"#hr c #afb3b5",
+"axY c #afb5b8",
+"ahy c #afb5b9",
+"#3J c #afb6ba",
+"#Uo c #afb7bc",
+"#vM c #afb8bc",
+"#WJ c #afb8bd",
+"#Ve c #afb8be",
+"aa# c #afb9be",
+"#D. c #afb9bf",
+"b.k c #afbabf",
+"#zA c #afbac0",
+".7X c #afbac1",
+"#yx c #afbbc0",
+"QtG c #afbbc1",
+".cY c #afbbc2",
+"#La c #afbcc2",
+".lg c #afbcc3",
+"#gR c #afbfc7",
+"#Pc c #afc0c9",
+".T4 c #afc3cd",
+"#5Q c #afc7d2",
+".0W c #afcbf9",
+".z0 c #afccde",
+".4o c #afd3f3",
+"#Qx c #afdaf0",
+".OY c #afdcf2",
+"#km c #afe6c2",
+"ade c #b0385b",
+"a6f c #b0385c",
+"bq9 c #b0395d",
+"ajG c #b03a5e",
+"ah3 c #b03b5e",
+"aw9 c #b03e60",
+"bP1 c #b04165",
+"bgE c #b04167",
+"#Mw c #b06a7c",
+"bc1 c #b06f86",
+"bdB c #b0728b",
+".FQ c #b073ae",
+"bIs c #b0748d",
+".vX c #b07a7f",
+"btY c #b08388",
+"aO7 c #b08488",
+"#ps c #b08490",
+"ad7 c #b0858a",
+"#8R c #b0868b",
+"als c #b0a3ad",
+"#ea c #b0a7ab",
+"#dY c #b0b3b5",
+".CC c #b0b3b6",
+".LI c #b0b4b8",
+".ks c #b0b6ba",
+"aS4 c #b0b7bb",
+"btl c #b0b8bc",
+"a7r c #b0b8bd",
+"#T# c #b0b8bf",
+"#wI c #b0b9be",
+".xa c #b0babf",
+"#le c #b0bac0",
+"c#3 c #b0bac1",
+".rz c #b0bbc1",
+".qi c #b0bbc2",
+"#lw c #b0bcc1",
+".um c #b0bcc2",
+"#on c #b0bcc3",
+"brt c #b0bcc4",
+"#0O c #b0bdc4",
+"#bD c #b0c1c9",
+"a8o c #b0cdde",
+"#8v c #b0dbf0",
+"aXF c #b0dff5",
+"amV c #b1375d",
+"awr c #b13a5e",
+"a33 c #b13b5f",
+"#Bj c #b13d60",
+"aws c #b13e61",
+"bzQ c #b1466a",
+"aUc c #b16377",
+"aob c #b16b7d",
+".zg c #b17c80",
+"bcq c #b17e93",
+"a1O c #b18298",
+"a2B c #b18496",
+"aF4 c #b18689",
+"bbV c #b18c9d",
+"bVF c #b193a3",
+".wu c #b1979b",
+".9j c #b1aeaf",
+".45 c #b1b3b5",
+"#au c #b1b4b5",
+".8J c #b1b4b6",
+"#nr c #b1b4b7",
+"afI c #b1b6ba",
+"a6G c #b1babf",
+"a9l c #b1bac0",
+"aZP c #b1bbc0",
+"a0s c #b1bcc1",
+".oE c #b1bcc2",
+".uz c #b1bcc3",
+".sb c #b1bdc2",
+".lw c #b1bdc3",
+".JA c #b1bdc4",
+"bqL c #b1bec7",
+"#1w c #b1c3cd",
+"#1N c #b1c3d7",
+".0X c #b1c7f8",
+".4r c #b1cdec",
+"#Jt c #b1d0e0",
+"#4b c #b1d1e1",
+".0T c #b1d3fc",
+".6p c #b1d8ec",
+"#2N c #b1dbf0",
+"#XQ c #b1dcf1",
+".Ng c #b1ddf3",
+"a3e c #b2375c",
+"bIa c #b2395d",
+"bsm c #b2395e",
+"asJ c #b23a5e",
+"aji c #b23b5f",
+"#Bi c #b23d5f",
+"aHY c #b23e61",
+"bnZ c #b24468",
+"bCi c #b2597d",
+"abC c #b26176",
+"bh2 c #b26679",
+"#eG c #b26b7d",
+"a4Y c #b26b85",
+"b0O c #b26c83",
+"bxo c #b2748c",
+".H9 c #b27ab2",
+"aP3 c #b27c84",
+"abE c #b27e92",
+".C3 c #b27f82",
+".pa c #b2858a",
+"ba. c #b28799",
+".sh c #b28e98",
+"a8u c #b2aeb6",
+"#T4 c #b2b2b8",
+"a4V c #b2b4bb",
+"#.y c #b2b6b8",
+".Jv c #b2b7bb",
+"a7w c #b2b8cb",
+"bpA c #b2b9be",
+"a53 c #b2bbbf",
+".Vo c #b2bcc1",
+"#vK c #b2bcc2",
+"bK2 c #b2bdc2",
+".h6 c #b2bdc3",
+"#mG c #b2bdc4",
+"aK0 c #b2bec3",
+".Qh c #b2bec4",
+".VH c #b2bec5",
+"#0n c #b2bfd2",
+"byp c #b2c0cc",
+"#Sd c #b2c2cb",
+"#Qu c #b2c5ce",
+"#3t c #b2c6d0",
+".0V c #b2cdfa",
+".0U c #b2d0fc",
+"#qh c #b2d5e6",
+"a06 c #b2daed",
+".pG c #b2daef",
+"aa8 c #b2dbef",
+"#6I c #b2dcf1",
+"a83 c #b2ddf3",
+"#lN c #b2fbd0",
+"auW c #b33a5e",
+"aw7 c #b33b5f",
+"bVj c #b33c5f",
+"bI# c #b33c60",
+"aMk c #b33d60",
+"amQ c #b33d61",
+"aR8 c #b33e61",
+"abZ c #b34064",
+"bps c #b34468",
+"bD2 c #b34c6e",
+"aZh c #b35272",
+"beC c #b35374",
+"au8 c #b36a7d",
+"#JE c #b36c7e",
+"a1N c #b37a96",
+".u2 c #b37d82",
+"csX c #b38196",
+"aFv c #b3868a",
+"bhG c #b3abb2",
+"a6q c #b3b4bb",
+".8N c #b3b5b7",
+".Gv c #b3b6b8",
+"aj5 c #b3b8bc",
+".R0 c #b3b9bd",
+"#qr c #b3bbbf",
+"cv3 c #b3bcc2",
+".Ow c #b3bdc2",
+"#bB c #b3bdc3",
+".rv c #b3bec4",
+"#c8 c #b3bec5",
+"aGi c #b3bfc4",
+".eq c #b3bfc5",
+"a4# c #b3bfc6",
+"#EI c #b3cbd7",
+".4u c #b3cbe6",
+".2F c #b3cff6",
+".Zb c #b3cffe",
+"aYq c #b3d8ea",
+"aKf c #b43a5e",
+"amW c #b43a5f",
+"#Bk c #b43c60",
+"ajH c #b43d60",
+"aQb c #b43e61",
+"blY c #b44066",
+"#Bl c #b44565",
+"bza c #b44a6b",
+"#Az c #b44e6a",
+"bbW c #b45c78",
+"#y2 c #b46879",
+"apj c #b46c7e",
+"a1M c #b46d8c",
+"btc c #b46f7f",
+"bRr c #b47087",
+".Kc c #b480b4",
+".GU c #b48283",
+".I7 c #b48385",
+".LR c #b485b4",
+"aIS c #b4878a",
+".iE c #b48e91",
+"bgz c #b4a2aa",
+".GB c #b4b7b9",
+".6M c #b4b8bb",
+".RZ c #b4babd",
+".Fs c #b4bbbe",
+"bDi c #b4bbbf",
+"a4S c #b4bcc1",
+".OB c #b4bdc2",
+"#sx c #b4bdc3",
+"a9T c #b4bec2",
+"#Tx c #b4bec3",
+".2m c #b4bec4",
+"bNA c #b4bec5",
+".YR c #b4bfc4",
+".cW c #b4bfc5",
+".Jz c #b4bfc6",
+".sV c #b4c0c5",
+".Jy c #b4c0c6",
+"br1 c #b4c0c8",
+"bmE c #b4c4cc",
+"aWe c #b4cbd7",
+".iY c #b4cfe1",
+".LF c #b4dbee",
+"auy c #b4dcf0",
+".H1 c #b4ddf2",
+"alO c #b5395f",
+"akH c #b53a5f",
+"aML c #b53b5f",
+"ajg c #b53c60",
+"aKm c #b53d60",
+"a1J c #b53d61",
+"auY c #b53f62",
+"biT c #b54066",
+"boA c #b54168",
+"bn3 c #b54268",
+"aat c #b54366",
+"bHc c #b54a6b",
+"brd c #b55678",
+"aKq c #b56276",
+"cwN c #b56c7e",
+"apk c #b56c7f",
+"cgy c #b56e85",
+".wS c #b57e82",
+"#Sk c #b58a98",
+".Nu c #b58bb6",
+".SA c #b594b7",
+"bnd c #b59eab",
+"#Ya c #b59eb2",
+".7e c #b5aeaf",
+"#ex c #b5afb2",
+".Td c #b5b0b2",
+".5o c #b5b1b1",
+".Gy c #b5b7ba",
+".Gu c #b5b8bb",
+".1m c #b5b9bb",
+".zH c #b5babd",
+"#Z9 c #b5bbbe",
+".Nj c #b5bbbf",
+"#5f c #b5bcc0",
+"asa c #b5bdc1",
+"#i# c #b5bec3",
+"aKX c #b5bec4",
+"bZh c #b5bfc3",
+"#dj c #b5bfc4",
+"##D c #b5bfc5",
+"#F4 c #b5bfc6",
+".wn c #b5c0c5",
+".ep c #b5c0c6",
+".35 c #b5c0c7",
+"awf c #b5c1c6",
+"#bC c #b5c1c7",
+"bCq c #b5c2ce",
+"#Rn c #b5c7d0",
+"#J2 c #b5c9d4",
+".Zc c #b5cdfe",
+"#kp c #b5d385",
+"#fE c #b5d9a5",
+"apQ c #b5dbee",
+"#0N c #b5dcf0",
+"#UL c #b5ddf0",
+".wh c #b5def2",
+".Ae c #b631b7",
+"bI. c #b63c60",
+"am. c #b63d61",
+"bo2 c #b63e61",
+"b#L c #b63f62",
+"bna c #b64268",
+"bnB c #b64368",
+"bsy c #b64369",
+"bpv c #b64469",
+"a2A c #b6617c",
+"ay8 c #b66281",
+"crp c #b66c7e",
+"baQ c #b66f86",
+"acd c #b6738a",
+"c#F c #b67d94",
+"aIM c #b6878a",
+"aGQ c #b6888c",
+"auq c #b68a8d",
+".Pb c #b68eb7",
+".QO c #b692b8",
+".GA c #b6b8bb",
+".Gz c #b6b9bb",
+".bN c #b6babd",
+".Ho c #b6bbbf",
+"aWj c #b6bcbf",
+"aTW c #b6bec2",
+".6A c #b6bfbd",
+"#rG c #b6bfc4",
+"#Mi c #b6bfc5",
+"#Aq c #b6c0c4",
+".YS c #b6c0c5",
+".e6 c #b6c0c6",
+".2l c #b6c0c7",
+"#bA c #b6c1c6",
+".sf c #b6c1c7",
+"#gL c #b6c2c7",
+"b#7 c #b6c2c8",
+"bdY c #b6c2c9",
+".4q c #b6d2ef",
+"beo c #b6d3e2",
+"be9 c #b6d4e3",
+"#NF c #b6d7e8",
+"aow c #b6dbed",
+"aq4 c #b6ddf0",
+"amP c #b73a5f",
+"akT c #b73d61",
+"bJx c #b73d62",
+"ajh c #b73e61",
+"aY4 c #b73e62",
+"bpw c #b74369",
+"brP c #b74469",
+"brg c #b7446a",
+"bmy c #b7456a",
+"bpu c #b7466a",
+"bo8 c #b7466b",
+"bcr c #b74768",
+"aTd c #b7546f",
+"a15 c #b76a83",
+"#H7 c #b76d7f",
+"aNp c #b7888d",
+".Bl c #b7898e",
+".fu c #b78d91",
+"cpa c #b78ea3",
+"a4b c #b7b3b9",
+".My c #b7b4b4",
+"aoC c #b7b4b7",
+"#cF c #b7b5b5",
+"#az c #b7b9ba",
+".6Q c #b7b9bb",
+".Gt c #b7babc",
+".Gs c #b7babd",
+"aem c #b7bcbf",
+"bmJ c #b7bcc0",
+"#0h c #b7c0c5",
+".DP c #b7c1c6",
+".g5 c #b7c1c7",
+"a5W c #b7c2c6",
+"#gN c #b7c2c7",
+".dw c #b7c2c8",
+"baL c #b7c3c8",
+"axw c #b7c3c9",
+".7L c #b7c9d2",
+".2J c #b7c9ed",
+"aS0 c #b7d3e1",
+"bgu c #b7d4e2",
+".6r c #b7d6e8",
+"aQX c #b7d7e7",
+".6q c #b7d9eb",
+"aVy c #b7dbec",
+".yt c #b7dcee",
+"arD c #b83a60",
+"ao3 c #b83c61",
+"bq7 c #b83d61",
+"aMM c #b83e61",
+"agC c #b83e62",
+"bD4 c #b83f63",
+"abO c #b84063",
+"bFZ c #b84267",
+"bES c #b84268",
+"brO c #b84369",
+"bxi c #b84469",
+"brf c #b8446a",
+"bnA c #b8456a",
+"box c #b8456b",
+"bpZ c #b8466a",
+"bn2 c #b8466b",
+"aop c #b84768",
+"a1L c #b86280",
+".bY c #b86d80",
+"#Lr c #b86e80",
+"bBq c #b8748b",
+"crj c #b8768d",
+"#Wq c #b88296",
+"aE0 c #b8898e",
+"aHh c #b88b90",
+"aJX c #b88c93",
+"cgD c #b88ea3",
+"a88 c #b8b3b9",
+".6Y c #b8b9ba",
+"#ce c #b8bcbe",
+"at4 c #b8bdc0",
+".TJ c #b8bdc1",
+".Lg c #b8bec2",
+"bko c #b8c1c5",
+".FC c #b8c1c6",
+".5W c #b8c1c7",
+".7P c #b8c2c7",
+".RR c #b8c2c8",
+".#8 c #b8c3c8",
+".Q# c #b8c3c9",
+"av6 c #b8c4ca",
+"#MV c #b8c7cf",
+".0Y c #b8cbf7",
+".zP c #b8ced9",
+"#Ju c #b8d2e0",
+"aSa c #b8dbec",
+"#42 c #b8ddf0",
+"#lO c #b8fbca",
+"byK c #b9395c",
+"aaJ c #b93a60",
+"ads c #b93b60",
+"aLj c #b93c60",
+"agN c #b93c61",
+"ao2 c #b93d61",
+"axG c #b93d62",
+"ajI c #b93e62",
+"aLv c #b93f62",
+"aaG c #b93f63",
+"bFY c #b94165",
+"bC8 c #b94166",
+"bD3 c #b94266",
+"bC9 c #b94267",
+"bAl c #b94368",
+"bfX c #b94569",
+"bAk c #b9456a",
+"boz c #b9466b",
+"blp c #b9476b",
+"bte c #b9486b",
+"bh6 c #b9486c",
+"a2z c #b94b6b",
+".E. c #b94bb5",
+"#AA c #b9536f",
+"bdx c #b96681",
+"#Ib c #b96d80",
+".bm c #b96e80",
+"ah9 c #b96f81",
+"bvk c #b9748b",
+"atH c #b98a8f",
+"cit c #b98da2",
+"cjX c #b98da3",
+"#2B c #b98f93",
+"a#C c #b997a5",
+"aef c #b99a9e",
+"#8U c #b99b9f",
+"#nC c #b9a3aa",
+".T# c #b9b4b4",
+".8X c #b9bbbc",
+".UK c #b9bcbd",
+".3m c #b9bcbe",
+".mB c #b9bec1",
+".Dv c #b9bfc1",
+".BG c #b9bfc2",
+"a54 c #b9c1c5",
+"#9R c #b9c2c6",
+".0D c #b9c2c7",
+".f1 c #b9c2c8",
+"#ZB c #b9c3c7",
+".eh c #b9c3c8",
+".a3 c #b9c3c9",
+"#XY c #b9c3ca",
+"#Iy c #b9c4c9",
+".57 c #b9c4ca",
+"baM c #b9c6cc",
+".4p c #b9d7f0",
+".FJ c #b9dbec",
+".J6 c #b9e0f4",
+"agO c #ba3c61",
+"ae0 c #ba3d61",
+"ab6 c #ba3d62",
+"a#w c #ba3e62",
+"bIl c #ba3f61",
+"aqq c #ba3f62",
+"afc c #ba3f63",
+"ae1 c #ba4063",
+"bET c #ba4064",
+"bhn c #ba4167",
+"bCj c #ba4268",
+"byb c #ba4468",
+"bya c #ba456a",
+"bn1 c #ba466b",
+"bpt c #ba466c",
+"buu c #ba4a6d",
+"bjD c #ba4b6e",
+"bqx c #ba4e73",
+"aGO c #ba526e",
+"#AB c #ba536f",
+"aYD c #ba5472",
+"bbn c #ba5774",
+"#zQ c #ba677b",
+"#zS c #ba6b7d",
+"axj c #ba6e80",
+"ccn c #ba6f85",
+"bD9 c #ba758c",
+".x1 c #ba8487",
+"aFt c #ba858c",
+"aqu c #ba888d",
+"aP2 c #ba898e",
+"aAc c #ba8a8e",
+"aEm c #ba8a8f",
+"#RH c #ba8f92",
+"b.O c #ba98a6",
+"bnE c #baa1ac",
+"#hY c #bab5b7",
+"#jv c #bab6b8",
+"#cM c #bab7b8",
+"#aH c #babbbd",
+".X9 c #babcbd",
+"#.O c #babcbe",
+".CJ c #babdc0",
+".qv c #babec1",
+"#wD c #babfc2",
+".YY c #babfc3",
+"aiO c #bac0c2",
+".e5 c #bac0c3",
+".Hx c #bac1c5",
+"#gU c #bac2c6",
+"#tq c #bac3c7",
+".mm c #bac3c8",
+"#OK c #bac3c9",
+"#gT c #bac4c8",
+".2b c #bac4c9",
+".bG c #bac4ca",
+"#Ak c #bac4cb",
+"bt3 c #bac4cd",
+".TH c #bac5ca",
+"azU c #bac5cb",
+".Zg c #bac8fa",
+".Xx c #bac8fd",
+".Ze c #bacbfc",
+".2I c #baccef",
+".Zd c #baccfd",
+".nH c #bad4e3",
+"#lR c #bae6a0",
+".An c #bb31bc",
+"bER c #bb3c60",
+"a2x c #bb3c61",
+"akI c #bb3d62",
+"bSB c #bb3e61",
+"afb c #bb3e62",
+"adr c #bb3e63",
+"ab5 c #bb3f63",
+"aQc c #bb3f64",
+"afa c #bb4063",
+"adq c #bb4064",
+"agM c #bb4164",
+"a2y c #bb4165",
+"alV c #bb4265",
+"bh7 c #bb4267",
+"bmz c #bb436a",
+"bxj c #bb4569",
+"bn. c #bb466c",
+"brN c #bb476c",
+"#AC c #bb5570",
+"brM c #bb5d7a",
+"#zR c #bb6d7e",
+"awH c #bb6e81",
+"#cU c #bb6f82",
+"bF3 c #bb6f83",
+"as0 c #bb7082",
+"bIt c #bb7389",
+".Be c #bb8688",
+".EN c #bb8788",
+".QE c #bb97de",
+"aJV c #bb98a5",
+"#ew c #bbb9ba",
+".7p c #bbbabb",
+".3h c #bbbcbe",
+"#fO c #bbbdbe",
+".ZH c #bbbec0",
+".Q7 c #bbbfc1",
+"aNk c #bbbfc3",
+".fk c #bbc0c3",
+"a2H c #bbc1c5",
+"aUO c #bbc3c7",
+".YQ c #bbc3c8",
+"aDr c #bbc3c9",
+"#qa c #bbc4c8",
+".zI c #bbc4c9",
+"#Ks c #bbc4ca",
+"#60 c #bbc5c9",
+".cT c #bbc5ca",
+".rB c #bbc5cb",
+".Xe c #bbc6cb",
+"bIG c #bbcad5",
+"#W4 c #bbdbeb",
+"adU c #bbddee",
+"anq c #bbdeee",
+"ar4 c #bbdeef",
+"#YO c #bbdef0",
+".9L c #bbe2f5",
+"bKJ c #bc3a5d",
+"a2w c #bc3a5e",
+"a2v c #bc3a5f",
+"a2u c #bc3b60",
+"a3d c #bc3c61",
+"ab0 c #bc3e62",
+"auX c #bc3e63",
+"bKK c #bc3f62",
+"agP c #bc3f63",
+"agQ c #bc3f64",
+"aq3 c #bc4063",
+"ab4 c #bc4064",
+"alU c #bc4265",
+"afd c #bc4366",
+"bre c #bc4369",
+"asK c #bc4467",
+"bBj c #bc456a",
+"bqy c #bc456b",
+"bw# c #bc466b",
+"bn# c #bc466c",
+"btV c #bc476c",
+"bbX c #bc4869",
+"aI2 c #bc516f",
+"#zT c #bc6b7d",
+"#Ka c #bc6f82",
+"a4r c #bc7386",
+".u3 c #bc8589",
+"aRn c #bc868b",
+".q. c #bc8a8e",
+"cwL c #bc8a9f",
+"aNm c #bc8c8f",
+"aBP c #bc8c90",
+"bcu c #bc96a4",
+"a7a c #bcb3b9",
+"#cD c #bcb7b7",
+".Rq c #bcb9b9",
+".9i c #bcbbbc",
+"#.8 c #bcbcbd",
+"#.7 c #bcbdbd",
+"#iZ c #bcbebf",
+".Gr c #bcbfc1",
+"#NA c #bcc0c3",
+".O1 c #bcc1c4",
+"#gx c #bcc2c6",
+"aeb c #bcc4c8",
+"#rt c #bcc4c9",
+"#in c #bcc5c9",
+"#jH c #bcc5ca",
+".72 c #bcc5cb",
+".Zh c #bcc5fa",
+".mD c #bcc6cb",
+".Ou c #bcc6cc",
+".vu c #bcc7cb",
+"#P2 c #bcc7cc",
+"#tr c #bcc7cd",
+".Xy c #bcc7ff",
+"#ZV c #bcc8cd",
+".Zf c #bccafc",
+".4s c #bcd2ea",
+"#qf c #bcd5e1",
+"#NB c #bcd9e7",
+"#5i c #bcdded",
+"ccF c #bcdff1",
+"bFX c #bd3a5d",
+"aMj c #bd3b61",
+"bIm c #bd3c60",
+"aGN c #bd3c62",
+"abR c #bd3d61",
+"aaI c #bd3e62",
+"avG c #bd3e63",
+"adp c #bd3f63",
+"anl c #bd3f64",
+"aeZ c #bd4064",
+"bcs c #bd4265",
+"akN c #bd4466",
+"bzb c #bd4468",
+"af# c #bd4567",
+"agR c #bd4668",
+"bpY c #bd466c",
+"afe c #bd4769",
+"bAj c #bd476c",
+"boy c #bd476d",
+"by# c #bd4a6e",
+"brQ c #bd5071",
+"aT5 c #bd5471",
+"aXU c #bd5573",
+"#AD c #bd5b74",
+"bus c #bd677d",
+"bjz c #bd687d",
+"apn c #bd6f82",
+"#a8 c #bd7082",
+"#eI c #bd7083",
+"bwd c #bd768d",
+"#Rv c #bd879c",
+"bAr c #bd8d91",
+"a8y c #bd98a4",
+".Z1 c #bdb7b9",
+"#k0 c #bdb8ba",
+"#aY c #bdb9ba",
+"#hX c #bdbabc",
+"#.6 c #bdbdbe",
+".CD c #bdbfc1",
+".8G c #bdc0c2",
+".sS c #bdc1c3",
+".0u c #bdc2c5",
+"aee c #bdc3c6",
+".TV c #bdc3c7",
+".TI c #bdc5c9",
+".mx c #bdc6cb",
+".b8 c #bdc6cc",
+".LB c #bdc7cc",
+".TF c #bdc7cd",
+"bqE c #bdc8cc",
+"#tv c #bdc8cd",
+"bHd c #be3a5e",
+"byJ c #be3b5f",
+"bGR c #be3d60",
+"bGQ c #be3d61",
+"aUF c #be3e63",
+"adh c #be3f62",
+"bHe c #be3f63",
+"aSv c #be3f64",
+"ab3 c #be4064",
+"akA c #be4065",
+"adg c #be4165",
+"bBk c #be4266",
+"alP c #be4366",
+"ay# c #be4467",
+"alW c #be4668",
+"bn0 c #be476d",
+"agL c #be4869",
+"bxh c #be4a6e",
+"brh c #be5273",
+"aUV c #be5471",
+"akQ c #be5773",
+"bw. c #be5775",
+"bgp c #be6f82",
+".n5 c #be6f83",
+"ahF c #be7c89",
+".Pt c #be7ca6",
+".vY c #be868a",
+"ahA c #be8a90",
+"bXh c #be97a5",
+".uH c #be9da2",
+"#4t c #beb6c6",
+"#.9 c #bebabc",
+"#cL c #bebdbe",
+"#aX c #bebebf",
+"#.4 c #bebfbf",
+"#.J c #bebfc0",
+"#cp c #bebfc1",
+".Et c #bec0c3",
+"#cf c #bec1c3",
+"#5Z c #bec1c5",
+".lB c #bec2c4",
+"#8N c #bec2c5",
+".Xj c #bec3c6",
+"#eN c #bec5c8",
+"aho c #bec6c8",
+".Oy c #bec6ca",
+"#1d c #bec6cb",
+".70 c #bec7cb",
+".tH c #bec7cc",
+".YT c #bec7cd",
+".ph c #bec8cc",
+".tK c #bec8cd",
+".Xf c #bec8ce",
+"#rr c #bec9ce",
+"bhv c #becbcf",
+".4t c #bed1e7",
+"#.o c #bed3b8",
+"#J3 c #bed3dd",
+"awK c #bedcec",
+"#Om c #bedeee",
+"#0c c #bee1f3",
+"abP c #bf3c60",
+"abQ c #bf3d61",
+"ado c #bf3e61",
+"bwV c #bf3e62",
+"bL1 c #bf3f62",
+"auk c #bf3f63",
+"ank c #bf3f64",
+"adf c #bf4064",
+"aw8 c #bf4065",
+"akB c #bf4165",
+"aos c #bf486a",
+"am2 c #bf496a",
+"al8 c #bf496b",
+"atB c #bf4a6a",
+"agD c #bf4a6b",
+"alX c #bf4b6b",
+"as5 c #bf4b6c",
+"bwa c #bf4e70",
+"btW c #bf5373",
+"apI c #bf7083",
+"#VO c #bf778b",
+"a6L c #bf7a90",
+"b5I c #bf7b91",
+".o5 c #bf848c",
+".n7 c #bf848d",
+".I6 c #bf898a",
+"aK8 c #bf8d92",
+"#Y8 c #bf9ba7",
+"aW8 c #bf9ca2",
+"b.b c #bfb2b9",
+"a9E c #bfb3b9",
+"ahr c #bfb7c6",
+".1O c #bfbcbe",
+"#X4 c #bfbecf",
+"#.5 c #bfbfc0",
+"#.3 c #bfc0c0",
+"#d4 c #bfc0c1",
+"#co c #bfc0c2",
+"#dP c #bfc133",
+".6X c #bfc1c2",
+"#iW c #bfc2c4",
+"abr c #bfc2c5",
+".mz c #bfc3c6",
+"#6Y c #bfc5c9",
+"alf c #bfc6ca",
+".Qj c #bfc6cb",
+".Xz c #bfc6fe",
+".7M c #bfc7cc",
+"#HR c #bfc7ce",
+"#e6 c #bfc8cc",
+".f8 c #bfc8cd",
+"#jS c #bfc8ce",
+"bjK c #bfc9cb",
+"#Li c #bfc9cd",
+".3Y c #bfc9ce",
+".7W c #bfc9cf",
+"aVC c #bfcace",
+"b.Y c #bfcfd8",
+".2G c #bfd4f5",
+".pJ c #bfd7e7",
+".lJ c #bfdfef",
+"#1z c #bfe3f4",
+"#kn c #bfeeb7",
+"#lP c #bffcc2",
+"bL0 c #c03d60",
+"bEU c #c03d61",
+"bP0 c #c03e61",
+"bL2 c #c03e62",
+"bxV c #c03f63",
+"anj c #c03f64",
+"ab2 c #c04064",
+"aLk c #c04065",
+"axH c #c04568",
+"af. c #c04c6c",
+"bsx c #c04c70",
+"aff c #c04d6d",
+"bgD c #c04f70",
+"bjE c #c05474",
+"aVJ c #c05673",
+"#AE c #c0647b",
+"bsw c #c0697e",
+"#AJ c #c06f80",
+"#Ni c #c07184",
+"#Sw c #c07282",
+"api c #c07284",
+"bxk c #c0738a",
+"#VI c #c0788b",
+"#Ta c #c07f8f",
+"cfm c #c08a9e",
+".rR c #c08c91",
+"a9b c #c091a0",
+"aJW c #c0949d",
+"a.p c #c09aa5",
+"#fW c #c0a9b0",
+"#hH c #c0aab0",
+"#Rt c #c0adb7",
+".9h c #c0c1c1",
+"#aA c #c0c1c2",
+".47 c #c0c2c3",
+".6L c #c0c3c5",
+"bpz c #c0c3c8",
+".8B c #c0c4c5",
+"#DL c #c0c4c7",
+"aXL c #c0c5c8",
+"a.0 c #c0c6c9",
+"#sL c #c0c6ca",
+".W7 c #c0c8cc",
+".7Q c #c0c8cd",
+"#CB c #c0c8ce",
+"#9P c #c0c9cd",
+".pM c #c0c9ce",
+".qE c #c0c9cf",
+"#Db c #c0cace",
+".aY c #c0cacf",
+"bf# c #c0cad0",
+"#YR c #c0cbd0",
+"bi0 c #c0ccd0",
+"bep c #c0d0d9",
+"#Oo c #c0d5e1",
+"##I c #c0e4f5",
+"aau c #c13b5e",
+"bNi c #c13d61",
+"bIn c #c13e61",
+"a#x c #c13e62",
+"aaH c #c13f63",
+"ani c #c14065",
+"aU1 c #c14165",
+"anh c #c14266",
+"aoq c #c14b6c",
+"anW c #c14d6d",
+"aya c #c14e6e",
+"but c #c14e71",
+"apN c #c14f6e",
+"agS c #c14f6f",
+"ae9 c #c1506f",
+"bqz c #c15675",
+".aC c #c17083",
+"aVR c #c17183",
+".hN c #c17184",
+"a5g c #c17285",
+"bvi c #c1758c",
+"#U7 c #c17b90",
+"cqp c #c1869b",
+"cj1 c #c1879b",
+".t9 c #c18a8c",
+".GV c #c18b8c",
+"aW7 c #c1bec3",
+"#ju c #c1c0c1",
+"#aW c #c1c1c2",
+".8O c #c1c2c3",
+"#cw c #c1c3c4",
+".Mg c #c1c3c5",
+"#nM c #c1c4c5",
+"#aq c #c1c4c6",
+"ahe c #c1c5c8",
+"#62 c #c1c6c9",
+"#cY c #c1c7ca",
+".MW c #c1c9cd",
+".mV c #c1c9ce",
+".h2 c #c1cacf",
+".7Z c #c1cad0",
+".vt c #c1cbcf",
+".Xg c #c1cbd0",
+"#IC c #c1cbd1",
+".tD c #c1ccd2",
+"bcZ c #c1cdd1",
+"bia c #c1ced2",
+".0Z c #c1cff8",
+"#Vx c #c1dfee",
+".LH c #c1e1f0",
+"ava c #c1e2f2",
+"bxW c #c23f62",
+"ab1 c #c24064",
+"aYn c #c24065",
+"a0k c #c24166",
+"ang c #c24568",
+"ayF c #c24c6d",
+"aKe c #c24e6e",
+"akJ c #c24f6e",
+"aqr c #c2516f",
+"alY c #c25271",
+"am3 c #c25371",
+"bp0 c #c25877",
+"bkj c #c25977",
+"a1j c #c26c83",
+"ai4 c #c27182",
+"#Lp c #c27285",
+"#Po c #c27387",
+"aci c #c2758a",
+".zh c #c28b8d",
+".C4 c #c28c8d",
+"aU6 c #c29094",
+"#9V c #c29397",
+"b39 c #c2a4ae",
+"b.J c #c2a9af",
+"a7O c #c2aeb3",
+"bd0 c #c2afbb",
+"#k1 c #c2b5b9",
+"#g. c #c2c0c0",
+"#.2 c #c2c2c2",
+".66 c #c2c4c5",
+".8K c #c2c4c6",
+".1o c #c2c5c6",
+"#.B c #c2c5c7",
+".XA c #c2c6fd",
+"#1V c #c2c7ca",
+".QC c #c2c7cb",
+"#rC c #c2c8cc",
+"#M3 c #c2c9cd",
+".gY c #c2cace",
+".nO c #c2cacf",
+"#Z# c #c2cad0",
+"#Dd c #c2cbcf",
+".oD c #c2cbd0",
+"#e1 c #c2cbd1",
+".Tw c #c2ccd0",
+"#by c #c2ccd1",
+"a#9 c #c2cdd2",
+"aPN c #c2cdd4",
+".2H c #c2d3f2",
+"#M0 c #c2d5df",
+"ama c #c2e0ef",
+"#Pa c #c2e1f0",
+"#GE c #c2e4f5",
+"ajo c #c3516f",
+"blZ c #c35274",
+"bve c #c35374",
+"al7 c #c35472",
+"al9 c #c35573",
+"afg c #c35673",
+"bpx c #c35a77",
+"bvf c #c35a78",
+"ayc c #c3647c",
+"abD c #c36c7d",
+"#N2 c #c37386",
+"#WA c #c37487",
+"#ZQ c #c38396",
+"#0t c #c38497",
+".p3 c #c3868e",
+".wT c #c38a8d",
+"aCu c #c39094",
+".gr c #c39295",
+"b#q c #c3949f",
+"#X2 c #c3adb5",
+"#nZ c #c3bfc2",
+".7o c #c3c3c4",
+".VN c #c3c3fc",
+"#aG c #c3c4c5",
+".8W c #c3c4c6",
+".6W c #c3c5c6",
+".68 c #c3c6c7",
+".5b c #c3c6c8",
+"#IE c #c3c7ca",
+".Ot c #c3c7cb",
+".Hp c #c3c8cb",
+"avq c #c3c9cc",
+"#oJ c #c3cace",
+".Vn c #c3cbcf",
+".jk c #c3cbd0",
+"azT c #c3cbd1",
+"afR c #c3cccf",
+".Jx c #c3ccd0",
+".0A c #c3ccd1",
+"#Bd c #c3ccd2",
+"#Jw c #c3cdd1",
+"#bz c #c3cdd2",
+"#Sj c #c3ced3",
+"bfO c #c3cfd6",
+"#J4 c #c3d1d8",
+"bf. c #c3d2db",
+"bgv c #c3d3db",
+"#VA c #c3ddeb",
+"#Rq c #c3e4f5",
+"bRn c #c43d60",
+"anf c #c4496b",
+"aXa c #c45371",
+"ajr c #c45471",
+"aWr c #c45572",
+"ar2 c #c45674",
+"as4 c #c45875",
+"btf c #c45d79",
+"agT c #c47285",
+"#H8 c #c47286",
+"#Kx c #c47386",
+"c.p c #c47387",
+"arY c #c47486",
+"bwg c #c4778c",
+"aO9 c #c4848c",
+"#Ye c #c48595",
+"aIT c #c49194",
+"aeh c #c49a9d",
+"aYz c #c49da1",
+".vB c #c4a2a6",
+"bdw c #c4c1c9",
+".VO c #c4c1ff",
+"#.I c #c4c5c6",
+".65 c #c4c6c7",
+"#aK c #c4c6c8",
+".4N c #c4c7c8",
+"#dU c #c4c7c9",
+"#7T c #c4c8ca",
+"bev c #c4c8cc",
+"a.G c #c4c9cb",
+"#4# c #c4c9cc",
+"bmH c #c4c9cd",
+"#uL c #c4cacc",
+"bne c #c4cacf",
+"b4d c #c4cbcf",
+"bmG c #c4cbd0",
+".50 c #c4ccd0",
+".2r c #c4ccd1",
+"blw c #c4cdcf",
+".es c #c4cdd1",
+".TP c #c4cdd2",
+".7Y c #c4cdd3",
+"#0y c #c4ced2",
+".33 c #c4ced3",
+"bVJ c #c4ced5",
+"bgX c #c4cfd3",
+"bgY c #c4d2d8",
+"#Jv c #c4d3db",
+"as9 c #c4e1ee",
+"#1u c #c4e1ef",
+"#2P c #c4e3f3",
+"a0n c #c54066",
+".B8 c #c543c8",
+"aor c #c54e6e",
+"ao4 c #c55773",
+"aF1 c #c55874",
+"anm c #c55875",
+"alZ c #c55976",
+"apM c #c55a77",
+"afn c #c57386",
+"aph c #c57487",
+"#1R c #c5768a",
+"#Tb c #c57a8d",
+"#WB c #c57b8e",
+"#QE c #c58496",
+"a#7 c #c58e92",
+".UB c #c58f9b",
+"aH9 c #c59396",
+"bZc c #c59aa5",
+"bey c #c59fab",
+"#ja c #c5b2b7",
+"#ZU c #c5bec4",
+"#jm c #c5bfc0",
+".GL c #c5bfc1",
+"#.1 c #c5c3c3",
+".XC c #c5c3fb",
+"#aV c #c5c4c5",
+"#ec c #c5c4c6",
+"#aF c #c5c6c7",
+".8V c #c5c6c8",
+".XB c #c5c6fd",
+"#av c #c5c7c8",
+"#f2 c #c5c7c9",
+".5a c #c5c8ca",
+"aiI c #c5c8cb",
+"#Cu c #c5c9cb",
+".Zi c #c5c9fa",
+"bmI c #c5cacd",
+"boE c #c5cbcd",
+".Vy c #c5cbce",
+".Hw c #c5cccf",
+".nk c #c5ccd0",
+"#vJ c #c5ccd1",
+".71 c #c5cdd1",
+"#q# c #c5cdd2",
+"#wE c #c5cdd3",
+".lE c #c5ced2",
+".BM c #c5ced3",
+"#is c #c5ced4",
+"#gF c #c5cfd3",
+"#vN c #c5cfd4",
+"#9L c #c5d1d5",
+"##S c #c5d7e2",
+"#3s c #c5e1ef",
+"ane c #c64e6f",
+"agK c #c65874",
+"btd c #c65877",
+"ah7 c #c65975",
+"ae8 c #c65d78",
+"afh c #c65d79",
+"asM c #c65e79",
+"bmA c #c6607b",
+"biS c #c6617c",
+"bhm c #c6627d",
+"amR c #c6697e",
+"#AF c #c66d81",
+"#AI c #c67083",
+"a9p c #c67285",
+"#Y6 c #c67287",
+"bY8 c #c67288",
+"apm c #c67386",
+"#Y4 c #c6798d",
+"clE c #c68395",
+"cdL c #c68398",
+".EO c #c68f90",
+"ai3 c #c68f95",
+".zl c #c69295",
+".gC c #c6979b",
+"a7S c #c69ba6",
+"#0X c #c6becb",
+".89 c #c6c2c2",
+"#VR c #c6c6c9",
+".1p c #c6c8c9",
+".4T c #c6c8ca",
+"#.A c #c6c9ca",
+".Md c #c6c9cb",
+".h# c #c6c9cc",
+"#zv c #c6cacc",
+"#IJ c #c6cacd",
+".3V c #c6cdd1",
+".Y7 c #c6cdd2",
+"bUh c #c6ced1",
+".JZ c #c6ced2",
+".tJ c #c6ced3",
+"#bt c #c6ced4",
+".uB c #c6cfd3",
+".YV c #c6cfd4",
+"#fd c #c6cfd5",
+"#Ha c #c6d0d4",
+".34 c #c6d0d5",
+"bdZ c #c6d0d6",
+"#Mf c #c6d3da",
+".BV c #c6dbe8",
+"cd2 c #c6e1ee",
+"#lQ c #c6fbb8",
+"aul c #c75372",
+"amX c #c75975",
+"biU c #c75977",
+"apO c #c75d79",
+"ajs c #c75e79",
+"ah8 c #c75e7a",
+"am4 c #c75f7a",
+"asL c #c7617c",
+"bkV c #c7637d",
+"bsz c #c7637e",
+"bd1 c #c77388",
+".kJ c #c77487",
+"#VG c #c77489",
+"bT5 c #c77589",
+"#T6 c #c7768a",
+"#QK c #c7778b",
+"#Xh c #c77a8d",
+"#LU c #c77d8c",
+"cnV c #c78193",
+".Bf c #c79091",
+"bvm c #c79295",
+"aAa c #c79396",
+"aIA c #c79397",
+"#Pm c #c7a8b2",
+"#hA c #c7babd",
+"#n2 c #c7bcc0",
+"#fZ c #c7bdc1",
+".VP c #c7c0fd",
+"#.0 c #c7c4c4",
+".6R c #c7c9ca",
+"#cy c #c7c9cb",
+"#ao c #c7cacc",
+".Hq c #c7cbce",
+".9S c #c7cccf",
+"#5N c #c7ccd0",
+"##P c #c7cdd0",
+"b.# c #c7ced1",
+"b7q c #c7cfd2",
+".a1 c #c7cfd3",
+".Xb c #c7cfd4",
+"#G7 c #c7cfd5",
+".9F c #c7d0d4",
+".Vv c #c7d0d5",
+"#Bc c #c7d0d6",
+"bRz c #c7d0d7",
+"aEL c #c7d1d4",
+"#II c #c7d1d5",
+"atd c #c7d1d6",
+"#NL c #c7d2d6",
+"ag6 c #c7e1ee",
+"#Fw c #c7e2f0",
+"ayG c #c85876",
+"alQ c #c85a76",
+"ayb c #c85b78",
+"al6 c #c8607b",
+"afi c #c8617c",
+"al0 c #c8627c",
+"btU c #c8637d",
+"ajt c #c86981",
+"#Ru c #c87286",
+"#AH c #c87385",
+"#AG c #c87386",
+"axP c #c87487",
+"#Pn c #c87489",
+"#Ia c #c87588",
+"bHj c #c8758a",
+"#P6 c #c87689",
+"#07 c #c8798c",
+"a9Z c #c87a91",
+"abv c #c88190",
+"crn c #c88194",
+".ne c #c88992",
+".u4 c #c88f92",
+".u. c #c89092",
+".q2 c #c89297",
+"aBf c #c89497",
+"bF8 c #c89498",
+".Ul c #c8acca",
+".U. c #c8b9fb",
+"#0j c #c8bcc9",
+".XD c #c8c4fb",
+"#aT c #c8c5c5",
+".Ee c #c8cacc",
+"#eg c #c8cbcd",
+"aIt c #c8ccce",
+".kA c #c8ccd7",
+"#la c #c8cdd1",
+"#2Z c #c8ced0",
+"#jM c #c8cfd3",
+"#Wk c #c8cfd4",
+"ayq c #c8cfd5",
+".aa c #c8d0d4",
+".YU c #c8d0d5",
+"bXl c #c8d0d6",
+".2K c #c8d0ec",
+"a76 c #c8d1d4",
+".9V c #c8d1d5",
+"#jT c #c8d1d6",
+"#GD c #c8d2d6",
+"#NG c #c8d3d9",
+"#ko c #c8f2a9",
+"and c #c95574",
+"bfg c #c95c77",
+"as6 c #c9617c",
+"atU c #c9627d",
+"buv c #c9667f",
+"bki c #c96780",
+"#Wp c #c97286",
+"#0u c #c97387",
+"avX c #c97488",
+"#UY c #c97588",
+"bux c #c9778b",
+"cr2 c #c9788a",
+"#ZT c #c97a8c",
+".si c #c97f90",
+".x2 c #c99092",
+"bjx c #c99396",
+"acE c #c99397",
+"aBQ c #c99497",
+".pU c #c99ca0",
+"bpy c #c9a6ab",
+"a6r c #c9b1b6",
+"#P5 c #c9b5be",
+"acP c #c9bbbd",
+".2Q c #c9bcba",
+"#aU c #c9c6c7",
+".8F c #c9cbcd",
+".UF c #c9cccd",
+"aO1 c #c9ccce",
+".a6 c #c9cdcf",
+".Sj c #c9cdd0",
+".Jw c #c9ced1",
+"bni c #c9ced2",
+"aw. c #c9cfd2",
+"a.T c #c9d0d3",
+".#5 c #c9d0d4",
+"#Ap c #c9d0d5",
+"bSP c #c9d1d3",
+".qy c #c9d1d5",
+".TO c #c9d1d6",
+"#HT c #c9d2d5",
+"#wK c #c9d2d6",
+"#LE c #c9d2d7",
+"#yB c #c9d3d7",
+"bMc c #c9d5dd",
+"#Ot c #c9d8e1",
+".j8 c #c9dce9",
+"#PT c #c9e2ef",
+"aMi c #ca5b78",
+"anT c #ca617b",
+"ar1 c #ca617c",
+"aq2 c #ca627c",
+"al1 c #ca647e",
+"brR c #ca6880",
+"bkY c #ca6881",
+".Ku c #ca6dbe",
+"#Y5 c #ca7186",
+"#VH c #ca7387",
+"#Y7 c #ca7388",
+"#QF c #ca7487",
+"#06 c #ca7488",
+"aoa c #ca7588",
+"#ZR c #ca7589",
+"cff c #ca7689",
+"#ZS c #ca768a",
+"aza c #ca778a",
+"#Yd c #ca778b",
+"c.n c #ca798d",
+"b7e c #ca7a8e",
+"#VP c #ca8193",
+"aSl c #ca838d",
+"ba# c #ca8392",
+".Nm c #ca8ae2",
+".vZ c #ca9193",
+".So c #caaff4",
+"#1S c #cab0ba",
+".U# c #cabafe",
+".VQ c #cabffb",
+"#hz c #cac2c5",
+"#hy c #cac4c7",
+"#en c #cac6c6",
+".9. c #cac7c7",
+"#4m c #cacbce",
+".Go c #cacccd",
+".Gg c #caccce",
+".Cr c #cacdce",
+"#Xt c #caced0",
+"bnH c #cacfd1",
+"#QD c #cacfd5",
+"bK0 c #cad0d3",
+".oi c #cad0d4",
+"#gB c #cad1d4",
+".fc c #cad1d5",
+"#BX c #cad1d6",
+"#ls c #cad2d5",
+".M6 c #cad2d6",
+".53 c #cad2d7",
+"b.G c #cad2d8",
+".00 c #cad2f6",
+"#ED c #cad3d6",
+"#Hb c #cad3d7",
+"#gE c #cad3d8",
+"#Dc c #cad4d7",
+"#28 c #cad4d8",
+".0C c #cad4da",
+"a8K c #cad5d8",
+"a8L c #cad5d9",
+"bdv c #cad6d9",
+"a8a c #cad6da",
+"bdc c #cad7db",
+"a#S c #cae1ed",
+".Ni c #cae2ee",
+"#iR c #cae47e",
+"#iQ c #caf0a1",
+"aKd c #cb5f7b",
+"bdy c #cb627b",
+"al4 c #cb667f",
+"afj c #cb6680",
+"a.k c #cb677e",
+"al3 c #cb6780",
+"al2 c #cb6880",
+"bp1 c #cb6a82",
+"bri c #cb6b83",
+"#P7 c #cb7488",
+".fO c #cb7588",
+"#H9 c #cb7589",
+"apl c #cb7689",
+"apg c #cb778a",
+"bY7 c #cb778b",
+"ak0 c #cb798c",
+"a5Z c #cb7a8e",
+"#Xg c #cb7d8f",
+"#T5 c #cb7f90",
+"ak1 c #cb8091",
+".iP c #cb8e94",
+"#0v c #cb92a0",
+".dd c #cb9397",
+"b0U c #cb9ca6",
+"bcG c #cbaeb7",
+"#al c #cbc541",
+"#eb c #cbc7c9",
+".9# c #cbc8c8",
+"#hq c #cbcecf",
+"#tp c #cbd1d4",
+"#bs c #cbd1d5",
+".2i c #cbd2d5",
+"#oO c #cbd2d6",
+".cI c #cbd2d7",
+"#ZA c #cbd3d6",
+".lx c #cbd3d7",
+"#e0 c #cbd3d8",
+"#zB c #cbd4d7",
+"#xE c #cbd4d8",
+"#Cx c #cbd4d9",
+"bZj c #cbd4db",
+"bc# c #cbd5d8",
+"ahl c #cbd5d9",
+"a7s c #cbd6da",
+"a8J c #cbd7da",
+"#9s c #cbe2ed",
+"aI1 c #cc617c",
+"adA c #cc637d",
+"ah4 c #cc647c",
+"al5 c #cc6881",
+"am5 c #cc6982",
+"an4 c #cc6a82",
+"bnb c #cc6b82",
+"akO c #cc6c83",
+"bqA c #cc6d84",
+"#Sl c #cc7185",
+"baR c #cc7287",
+"adL c #cc7487",
+"bX. c #cc778b",
+"aZd c #cc9fa4",
+"a6I c #ccacb6",
+"b0T c #ccbbc8",
+"a77 c #ccbdc4",
+".VR c #ccbffc",
+"bcF c #ccc7cd",
+".GJ c #ccc8c8",
+".5n c #ccc9c9",
+".Rs c #cccaca",
+".5w c #cccbcc",
+".Zj c #ccccf9",
+".Eq c #ccced0",
+"aGj c #cccfd0",
+".02 c #cccff2",
+".pz c #ccd0d2",
+"#7X c #ccd0d3",
+".TK c #ccd1d3",
+".Hv c #ccd1d4",
+"#Zd c #ccd2d4",
+".31 c #ccd2d5",
+".DB c #ccd3d6",
+".mU c #ccd3d7",
+".qI c #ccd3d8",
+"#j4 c #ccd4d7",
+".Vu c #ccd4d8",
+"#sz c #ccd4d9",
+"#Rs c #ccd5d8",
+"#CA c #ccd5d9",
+"#LF c #ccd5da",
+"bn7 c #ccd6d9",
+"a9W c #ccd6da",
+"beL c #ccd8db",
+"#Ph c #ccdfe9",
+"#7D c #cce2ed",
+"adC c #cd617b",
+"adD c #cd647e",
+"apd c #cd647f",
+"anX c #cd657d",
+"bbo c #cd677e",
+"alT c #cd687f",
+"ay9 c #cd6981",
+"aLi c #cd6a82",
+"an5 c #cd6a83",
+"bfW c #cd6b82",
+"ar3 c #cd6b83",
+"an3 c #cd6d84",
+"a0D c #cd6e83",
+"#QJ c #cd7588",
+"#I# c #cd768a",
+"#KX c #cd778a",
+"#I0 c #cd778b",
+"cmQ c #cd7b8e",
+"cuI c #cd7c90",
+"ccw c #cd7e90",
+"#UX c #cd8192",
+"akh c #cd8691",
+"bqC c #cd9599",
+"aAb c #cd9699",
+"bKV c #cd979a",
+"a7d c #cd9aa4",
+"b#s c #cd9fa7",
+".6H c #cdb869",
+"#kG c #cdbcc1",
+"bdL c #cdbdc4",
+"#08 c #cdc1c7",
+"#nR c #cdc5c6",
+"#cE c #cdc9c9",
+"#eo c #cdc9ca",
+".mN c #cdc9d1",
+"a9X c #cdcdd1",
+".03 c #cdceef",
+".Gk c #cdcfd0",
+".8E c #cdcfd1",
+"avr c #cdd1d3",
+"a7y c #cdd1d6",
+".01 c #cdd1f3",
+"abs c #cdd2d5",
+"ash c #cdd3d6",
+"#3b c #cdd3d7",
+".dk c #cdd4d7",
+".fe c #cdd4d8",
+"#fc c #cdd4d9",
+"#IG c #cdd5d8",
+".7T c #cdd5d9",
+".2k c #cdd5da",
+"#HS c #cdd6d9",
+".TQ c #cdd6da",
+"#gD c #cdd6db",
+"bhw c #cdd7db",
+"bib c #cdd9dc",
+"#Wc c #cde3ee",
+".Ci c #ce46cd",
+"adB c #ce607a",
+"bbY c #ce647c",
+"ayH c #ce6781",
+"bc2 c #ce6a80",
+"as3 c #ce6a82",
+"aaK c #ce6b81",
+"adE c #ce6b83",
+"an6 c #ce6c84",
+"atC c #ce6e85",
+"afk c #ce6f85",
+"boB c #ce7085",
+"atT c #ce7086",
+"az. c #ce7186",
+"aIZ c #ce7187",
+".jV c #ce7689",
+"az# c #ce768a",
+"aqZ c #ce778a",
+"#I. c #ce778b",
+"#P8 c #ce788c",
+"#X# c #ce798a",
+"cr9 c #ce798d",
+"amn c #ce7c8d",
+"cs3 c #ce7c8f",
+"csc c #ce7c90",
+".qX c #ce8d95",
+".C5 c #ce9495",
+".wU c #ce9596",
+"aXk c #ce979a",
+".vA c #ce9ea1",
+"a8v c #ceabaf",
+"a78 c #ceafb9",
+"bdd c #ceb0b8",
+"a5K c #ceb2b6",
+".Ua c #cebafe",
+".VS c #cebffc",
+".XE c #cec5fa",
+"#jn c #cecaca",
+".7f c #cecbcb",
+".7g c #cecccc",
+".5v c #cecece",
+"#mp c #cececf",
+".ZU c #cecfd0",
+".Rg c #cecfd1",
+".Ml c #ced0d1",
+"#B# c #ced0d3",
+".oh c #ced1d3",
+".jd c #ced1d4",
+"#xC c #ced3d6",
+".DK c #ced4d7",
+".ry c #ced4d8",
+".uy c #ced5d8",
+".2n c #ced5d9",
+"#IF c #ced6d9",
+"#gX c #ced6da",
+"#An c #ced6db",
+"#QC c #ced7da",
+"#Da c #ced7db",
+"#G9 c #ced7dc",
+"a6H c #cedade",
+".en c #cedfea",
+".zX c #cee3ec",
+"av1 c #cee8f5",
+"#hm c #ceec96",
+"anc c #cf6680",
+"apc c #cf6882",
+"bct c #cf6980",
+"akP c #cf6c83",
+"am6 c #cf7187",
+"bnC c #cf7287",
+"apb c #cf7388",
+".k9 c #cf7589",
+".hO c #cf768a",
+"ao# c #cf778b",
+".jz c #cf788b",
+"amj c #cf7b8b",
+"apa c #cf7f8e",
+"#VF c #cf8697",
+"alK c #cf8792",
+"#Wo c #cf92a1",
+".u# c #cf9597",
+"a7t c #cf98a5",
+"b2s c #cf9ea5",
+"#8S c #cfa1a4",
+"#X. c #cfa3af",
+"#ZE c #cfb1ba",
+"beM c #cfb5bc",
+"a79 c #cfc1c7",
+"a8M c #cfc2c8",
+"#.Z c #cfcccc",
+".N7 c #cfcccd",
+"bhH c #cfced2",
+".S3 c #cfd0d1",
+".Yb c #cfd0d2",
+".ID c #cfd1d2",
+"aTV c #cfd2d4",
+"#5g c #cfd3d5",
+".hW c #cfd5d8",
+".f9 c #cfd5d9",
+".Ft c #cfd5da",
+".2f c #cfd6d9",
+".54 c #cfd6da",
+"#EH c #cfd7da",
+".TN c #cfd7db",
+"#EB c #cfd7dc",
+"#T. c #cfd8db",
+"#2v c #cfd8dc",
+"#Pl c #cfd9dd",
+"#4e c #cfe0e9",
+"akV c #cfe3ed",
+"#Zt c #cfe3ee",
+"#Qs c #cfe4ee",
+".F8 c #d05fca",
+"aI0 c #d06a83",
+"akM c #d06c82",
+"adt c #d06d82",
+"agJ c #d06e83",
+"an7 c #d06e86",
+"adF c #d07187",
+"apL c #d07388",
+"bn4 c #d07488",
+"ann c #d07489",
+"afl c #d0748a",
+"aiY c #d0758a",
+".fP c #d0768a",
+".d6 c #d0768b",
+".d5 c #d0778b",
+".jy c #d0778c",
+".iL c #d0788b",
+".iK c #d0788c",
+"apf c #d0798d",
+".iu c #d07a8d",
+".iv c #d07a8e",
+".iw c #d07b8f",
+".rN c #d07c8d",
+"amk c #d08798",
+".GW c #d09696",
+".tg c #d09698",
+"aJQ c #d0969a",
+"brU c #d0979b",
+".Ub c #d0bafd",
+"#nG c #d0c0c5",
+".4A c #d0cdc8",
+".46 c #d0d1d2",
+"#.x c #d0d2d4",
+".Hr c #d0d4d6",
+"av0 c #d0d4d7",
+"##O c #d0d6d8",
+".Xi c #d0d6d9",
+".9H c #d0d6da",
+".sU c #d0d7da",
+"#bx c #d0d7db",
+"#Fr c #d0d8db",
+"#1U c #d0d8dc",
+"#0w c #d0d9dc",
+"#Wl c #d0d9dd",
+"#1T c #d0dbde",
+".mG c #d0e4ee",
+"#T1 c #d0e5ef",
+"ab7 c #d16e83",
+"ape c #d16f86",
+"bvg c #d17388",
+"adG c #d17489",
+"aot c #d17589",
+"am7 c #d1758a",
+"adH c #d1768a",
+".cA c #d1768b",
+".b0 c #d1778b",
+".gO c #d1788b",
+".d4 c #d1788c",
+"cqk c #d1798c",
+"#Kc c #d1798d",
+".jx c #d17a8d",
+".nb c #d17b8c",
+".ix c #d17b8f",
+"#2s c #d17c8d",
+".iy c #d17c8f",
+".hz c #d17d91",
+".hy c #d17e90",
+".hA c #d17e91",
+".hC c #d17e92",
+".jA c #d17f91",
+".u5 c #d19798",
+"aIN c #d1999c",
+".fv c #d19ea0",
+".l7 c #d1a2a4",
+"aiQ c #d1aeb7",
+"#Yb c #d1bdc9",
+".VT c #d1bffd",
+"bhI c #d1c8cc",
+"b.p c #d1cbcf",
+"#aS c #d1cdcd",
+"#kT c #d1cdce",
+".1E c #d1cece",
+".28 c #d1d2d3",
+".Wh c #d1d3d4",
+".8D c #d1d3d5",
+".gg c #d1d4d6",
+"#1a c #d1d4d7",
+"aoN c #d1d5d7",
+"atW c #d1d5d8",
+"alo c #d1d6d8",
+"#rD c #d1d6d9",
+"#Le c #d1d6da",
+".vl c #d1d7da",
+".qF c #d1d7db",
+".tG c #d1d8db",
+"##N c #d1d8dc",
+"#mB c #d1d8dd",
+"#yE c #d1d9dc",
+"#1. c #d1d9dd",
+"auN c #d1dadd",
+"aC. c #d1dade",
+"#09 c #d1dbdf",
+"b.o c #d1dcdf",
+".bE c #d1e2ed",
+"atX c #d1e4ee",
+"bwb c #d26b82",
+"adz c #d27186",
+"an8 c #d27389",
+"aOp c #d27488",
+"a6J c #d27489",
+"apJ c #d2768b",
+"cn2 c #d2778a",
+".d3 c #d2778b",
+".cz c #d2778c",
+".#M c #d2788c",
+"adI c #d2788d",
+"bSG c #d2798b",
+"au9 c #d2798c",
+".aA c #d2798d",
+"ayJ c #d27a8d",
+".iz c #d27c90",
+"#1Q c #d27e8e",
+".kW c #d27e91",
+".hB c #d27f92",
+".iA c #d28092",
+".pQ c #d28193",
+".iB c #d28194",
+"a9Y c #d28797",
+"aH7 c #d28893",
+"bea c #d28c9b",
+"biJ c #d29298",
+".jt c #d29699",
+".Bg c #d29798",
+"#SA c #d29a9c",
+".q8 c #d29a9d",
+"a9n c #d29fab",
+"aZU c #d2a2a5",
+"ahC c #d2a6a9",
+"#8T c #d2afb2",
+".Sp c #d2b2fe",
+"#3f c #d2b4b9",
+"a8b c #d2b6be",
+".Uc c #d2b9fb",
+"#hK c #d2bfc4",
+"a5X c #d2c3ca",
+"a.M c #d2cbcf",
+"#mq c #d2cdcf",
+"#f9 c #d2cece",
+".5m c #d2cfcf",
+".2M c #d2d2e1",
+".4Q c #d2d4d5",
+"aK1 c #d2d5d6",
+".et c #d2d5d7",
+"a8. c #d2d5d9",
+"#bw c #d2d6d8",
+"#0B c #d2d6d9",
+"ad4 c #d2d7d9",
+".5T c #d2d7da",
+"#LI c #d2d7db",
+"a1Y c #d2d8db",
+".jj c #d2d8dc",
+".ck c #d2d9dc",
+".YW c #d2d9dd",
+"#LH c #d2dadd",
+"#UV c #d2dade",
+"#KO c #d2dbde",
+"a55 c #d2dbdf",
+"#8H c #d2dcdf",
+"#P4 c #d2dce0",
+"#5O c #d2e4ed",
+"a7u c #d37186",
+"aKc c #d37188",
+"cdM c #d37789",
+"a5Y c #d3778b",
+".b1 c #d3788c",
+".bo c #d3788d",
+".Ka c #d378da",
+"bXa c #d3798b",
+".#L c #d3798d",
+"am8 c #d37a8d",
+"adJ c #d37a8e",
+"am9 c #d37b8e",
+".hP c #d37d8e",
+".jJ c #d38093",
+".jK c #d38193",
+".l4 c #d38194",
+".iC c #d38294",
+".jB c #d38393",
+".nU c #d38395",
+".m0 c #d38496",
+".qL c #d38697",
+"ajU c #d38997",
+"#YU c #d38a98",
+".EP c #d39799",
+".x3 c #d39899",
+"a4W c #d3b1b4",
+".Sq c #d3b2ff",
+".kB c #d3b3b8",
+".XF c #d3c6f9",
+".Zk c #d3cdf8",
+".3C c #d3d0d3",
+"#mk c #d3d3d4",
+".4W c #d3d4d5",
+".X4 c #d3d5d6",
+"#vA c #d3d6d8",
+"avp c #d3d7d8",
+".Hs c #d3d7d9",
+"aW0 c #d3d7da",
+"at7 c #d3d8da",
+".9Q c #d3d8db",
+"#VT c #d3d8dc",
+"#Be c #d3d9db",
+".iZ c #d3d9dc",
+".w9 c #d3d9dd",
+"QtJ c #d3dadd",
+"#oN c #d3dade",
+"#Ms c #d3dbde",
+"#0x c #d3dbdf",
+"bzn c #d3dbe0",
+"a6Q c #d3dce0",
+"acs c #d3e4ec",
+".nA c #d3e6ee",
+"#hn c #d3e770",
+"ar0 c #d47087",
+"beb c #d47388",
+"cs4 c #d4768a",
+"b7f c #d4778b",
+"c.o c #d4788b",
+"buw c #d4788c",
+".na c #d4788d",
+".jU c #d4798c",
+".#K c #d4798d",
+"#I4 c #d4798e",
+"as1 c #d47a8d",
+"#Lq c #d47a8e",
+".d. c #d47b8e",
+"bBs c #d47c8e",
+".fQ c #d47e8f",
+".md c #d4808f",
+".hx c #d48092",
+".l. c #d48191",
+".it c #d48192",
+".pR c #d48396",
+".oT c #d48496",
+".l3 c #d48596",
+"#9d c #d48793",
+".Nr c #d48de2",
+".mg c #d49299",
+"bxs c #d49a9d",
+"bfd c #d49aa5",
+"aq9 c #d4c2c4",
+"#fY c #d4c6ca",
+"#mr c #d4cbce",
+".N3 c #d4d2d2",
+".3B c #d4d5d5",
+".23 c #d4d5d6",
+".UD c #d4d6d7",
+".Vk c #d4d7d9",
+"#VY c #d4d7da",
+"#Zf c #d4d8da",
+".7S c #d4d9db",
+"acr c #d4d9dc",
+".3W c #d4d9dd",
+"bUe c #d4dadc",
+".op c #d4dadd",
+".ac c #d4dade",
+".h. c #d4dbde",
+".TM c #d4dbdf",
+"bJ1 c #d4dcde",
+"#Fs c #d4dcdf",
+"ayI c #d5738a",
+"anb c #d5748a",
+"cpb c #d57588",
+"cwM c #d57589",
+"aq1 c #d5758b",
+"cro c #d57689",
+"cmR c #d5768a",
+"apK c #d5768b",
+"ae2 c #d57789",
+"a6K c #d5778b",
+"ah6 c #d5788a",
+"bxp c #d5788c",
+"an9 c #d5788d",
+"am1 c #d5798b",
+"bDb c #d5798c",
+"arZ c #d5798d",
+".aB c #d5798e",
+"ach c #d57a8d",
+"#I3 c #d57a8e",
+"afm c #d57b8e",
+"an. c #d57b8f",
+"a8N c #d57c8f",
+".d# c #d57d8f",
+"brS c #d57e90",
+"amU c #d5808f",
+".d7 c #d58191",
+".jC c #d58898",
+"a7v c #d58a9a",
+".v0 c #d59a9b",
+"aAM c #d59b9e",
+"#NQ c #d59ea0",
+"akc c #d5a0a3",
+"a3o c #d5a4a6",
+".Ud c #d5b8fb",
+".VU c #d5befc",
+"#jd c #d5c4c9",
+".Yq c #d5d4d4",
+".3A c #d5d5d6",
+".Ye c #d5d6d7",
+"aE. c #d5d6d8",
+".KG c #d5d7d8",
+"#5U c #d5d8db",
+"#8O c #d5d9db",
+"#Zs c #d5dadc",
+"QtC c #d5dadd",
+"QtM c #d5dbde",
+".9I c #d5dbdf",
+"auC c #d5dcde",
+".9X c #d5dcdf",
+"bDj c #d5dce0",
+"amt c #d5dde0",
+"a.H c #d5dee1",
+"#fG c #d5df54",
+".O0 c #d5e4eb",
+"#Rl c #d5e5ed",
+"#P0 c #d5e7ef",
+"cfA c #d5e7f0",
+"aZW c #d67486",
+"cxC c #d67587",
+"cqq c #d67588",
+"a9o c #d6768b",
+"aum c #d6768c",
+"b8S c #d6778a",
+"amY c #d6798a",
+"ana c #d6798d",
+"akK c #d67a8c",
+".#I c #d67a8e",
+"#Kb c #d67a8f",
+"ae7 c #d67b8b",
+"aun c #d67b8e",
+"#N3 c #d67b8f",
+"arE c #d67d8d",
+"bh5 c #d67d8e",
+"bxq c #d67f90",
+".p1 c #d68090",
+"bhU c #d68091",
+".iM c #d68291",
+".eW c #d68392",
+"biu c #d68393",
+".LO c #d683df",
+"auo c #d68493",
+".kK c #d68797",
+".jI c #d68e9d",
+".P. c #d694e2",
+"aup c #d6999d",
+".zi c #d69a9c",
+".sA c #d69a9d",
+"#2t c #d69aa7",
+".wV c #d69b9c",
+".q# c #d69b9e",
+"#M9 c #d69ea0",
+".QM c #d69ee6",
+".hr c #d69fa1",
+".fw c #d6a2a3",
+"a1h c #d6a3a8",
+"a0A c #d6a5a7",
+"a89 c #d6acb1",
+".Sr c #d6b3ff",
+".V1 c #d6bdd9",
+"#hJ c #d6c6cb",
+"#hI c #d6c8cc",
+".lT c #d6c9d0",
+".Zl c #d6ccf5",
+".4X c #d6d7d8",
+".PB c #d6d7d9",
+".2L c #d6d7ea",
+".Ed c #d6d8d9",
+"aiF c #d6d9da",
+".MU c #d6d9db",
+"afJ c #d6dadc",
+"#jN c #d6dbde",
+".X. c #d6dbdf",
+"b4c c #d6dcde",
+".kv c #d6dcdf",
+".eo c #d6dce0",
+".30 c #d6dddf",
+".Vw c #d6dde0",
+".Ip c #d76bcd",
+"c#G c #d7788b",
+"as2 c #d7788d",
+"byi c #d7798c",
+"ace c #d77a8d",
+".cy c #d77a8e",
+".az c #d77a8f",
+"bwc c #d77b8d",
+"#I2 c #d77b8f",
+"clx c #d77c8d",
+"an# c #d77c8f",
+"ao. c #d77c90",
+"#X3 c #d7808e",
+"bCn c #d78091",
+".jw c #d78092",
+".da c #d78191",
+"bsA c #d78292",
+".hw c #d78595",
+".l5 c #d78b9a",
+"anB c #d7929e",
+".mZ c #d793a1",
+".th c #d79b9c",
+"aaj c #d7a1a4",
+"a12 c #d7a3a7",
+".Sx c #d7a8e7",
+"a7b c #d7aeb2",
+".Ue c #d7b8fb",
+"bp. c #d7c3c7",
+"#fX c #d7c8cc",
+".Zm c #d7ccf3",
+"#i6 c #d7ced1",
+"#kS c #d7d3d4",
+".U2 c #d7d4d5",
+".Ym c #d7d5d5",
+".3# c #d7d8d8",
+".4V c #d7d8d9",
+".Gb c #d7d9da",
+".3l c #d7d9db",
+".jg c #d7dadb",
+".T6 c #d7dadc",
+"#4p c #d7dadd",
+".zY c #d7dbdd",
+".xj c #d7dbde",
+"#6O c #d7dcde",
+".TL c #d7dcdf",
+".r# c #d7dde0",
+".Vs c #d7dde1",
+"#1# c #d7dee0",
+".7J c #d7dee1",
+"cr8 c #d8798b",
+"bBr c #d8798c",
+"acf c #d87a8d",
+"adK c #d87a8e",
+".bZ c #d87a8f",
+"bvh c #d87b8e",
+".bp c #d87b8f",
+"#cW c #d87b90",
+"#LV c #d87c90",
+"aMh c #d87d90",
+"atD c #d88292",
+"bez c #d88795",
+"bec c #d88d9a",
+".jD c #d893a1",
+".ij c #d89b9d",
+".ua c #d89c9d",
+"aHw c #d89c9f",
+"a6u c #d89ca4",
+".sG c #d89d9f",
+".io c #d89ea0",
+"aBb c #d89ea1",
+"a4c c #d8aeb1",
+"#1O c #d8bcc5",
+"all c #d8c4c7",
+".XG c #d8c6f7",
+"#i7 c #d8cfd1",
+".1N c #d8d8d8",
+".Is c #d8dadb",
+".ad c #d8dbdc",
+".Hu c #d8dbdd",
+"auD c #d8dcdd",
+".BK c #d8dddf",
+".a5 c #d8dde0",
+".oO c #d8dde1",
+"#rm c #d8dee0",
+".ro c #d8dee1",
+"#Yn c #d8dee2",
+"#EE c #d8dfe1",
+"#QT c #d8dfe2",
+".BU c #d8dfe3",
+"#9G c #d8e0e2",
+".vw c #d8e5ed",
+".xc c #d8e6ed",
+"cfn c #d97688",
+"cj2 c #d9768a",
+"clF c #d9778b",
+"cjW c #d9798a",
+"a5h c #d9798b",
+"bE. c #d9798c",
+"acg c #d97a8d",
+"b0N c #d97a8e",
+"blq c #d97b8e",
+".#J c #d97b8f",
+"#eH c #d97b90",
+"#I1 c #d97c90",
+"ady c #d97d8e",
+"aq0 c #d97d90",
+"aqs c #d98290",
+"bgC c #d98292",
+"bjC c #d98392",
+"bhV c #d98594",
+"aKr c #d98d98",
+".o3 c #d98e98",
+".kX c #d98f9c",
+"#05 c #d998a2",
+".u6 c #d99d9e",
+"bab c #d9a3aa",
+".QG c #d9abff",
+".Ss c #d9b3ff",
+".VY c #d9b7ea",
+".VV c #d9bffb",
+".XM c #d9c2da",
+".Zo c #d9c7e8",
+".05 c #d9cee6",
+".04 c #d9d2f0",
+".8A c #d9dadb",
+".4P c #d9dbdc",
+"#3# c #d9dcde",
+".f0 c #d9dcdf",
+"aj7 c #d9dde0",
+"#oj c #d9dee0",
+".mW c #d9dee1",
+"#tL c #d9dee2",
+"#8L c #d9dfe0",
+".bI c #d9dfe1",
+".dv c #d9dfe2",
+"arf c #d9e0e2",
+"#Ze c #d9e0e3",
+"af7 c #d9e1e3",
+".vr c #d9e5eb",
+"#4a c #d9e6ed",
+".Af c #da36da",
+"cgE c #da768a",
+"ciu c #da778a",
+"cio c #da788a",
+"b5J c #da788b",
+"bAq c #da798b",
+"cdA c #da798c",
+"a#y c #da7a8c",
+".bn c #da7b90",
+".#N c #da7c90",
+"bhT c #da7d91",
+"anY c #da8190",
+"aki c #da8192",
+"anV c #da8291",
+"alR c #da8391",
+"bkZ c #da8593",
+"byj c #da8695",
+".rD c #da90a1",
+"bEa c #da9ca0",
+".Bh c #da9d9e",
+".ti c #da9ea0",
+".x7 c #da9ea1",
+"ab9 c #daa1a8",
+"#9e c #daa1a9",
+".QF c #daaefe",
+".Ui c #dab1ed",
+".St c #dab1fd",
+"#0Y c #dabbc1",
+".VW c #dabff8",
+"a2M c #dac5c8",
+".Zr c #dac7d9",
+"#9K c #daccce",
+"#jc c #dacdd1",
+".6B c #dad7bb",
+"#mj c #dad8d9",
+".3u c #dad9d9",
+".4U c #dadbdc",
+"#iU c #dadcdc",
+"#.w c #dadcdd",
+"#Gz c #dadddf",
+"auH c #dadedf",
+"are c #dadee0",
+"bex c #dadee1",
+"#xF c #dadfe1",
+".dt c #dadfe2",
+"#Fu c #dadfe3",
+"bJZ c #dae0e1",
+"#wL c #dae0e2",
+".tI c #dae0e3",
+"#9H c #dae1e4",
+"#8G c #dae2e4",
+".9Y c #dae4eb",
+"#Sc c #dae6ec",
+"#W0 c #dae7ed",
+".ll c #dae7f0",
+"##J c #dae8ef",
+"#Sh c #daeaf2",
+"aZg c #db7889",
+"ccm c #db7a8c",
+"bX# c #db7a8d",
+"bg8 c #db7c90",
+"#cV c #db7c91",
+"bDc c #db8494",
+"agI c #db8694",
+"bE# c #db8796",
+"biQ c #db8895",
+"adx c #db8896",
+".is c #db8a97",
+"ak5 c #db939f",
+".v1 c #db9e9f",
+"a.W c #db9fa1",
+"#Mm c #dba0a2",
+"#2l c #dba0aa",
+".s2 c #dba5a9",
+".nT c #dba5b0",
+".We c #dba9a8",
+"ahw c #dbaaad",
+".QH c #dbacff",
+".Uf c #dbb9fd",
+".XH c #dbc6f6",
+".0. c #dbdada",
+".Cu c #dbdcdd",
+".Eo c #dbdddd",
+"#6W c #dbddde",
+"#Yu c #dbdee0",
+"ate c #dbdfe0",
+"#WG c #dbdfe1",
+".oQ c #dbdfe2",
+".0x c #dbe0e2",
+".sY c #dbe0e3",
+"#Cz c #dbe0e4",
+".zZ c #dbe1e2",
+"#j2 c #dbe1e3",
+"#fb c #dbe1e4",
+"afu c #dbe6ed",
+"ajM c #dbe7ec",
+".DL c #dbe8f0",
+"#Zy c #dbebf2",
+"bVx c #dc7a8c",
+"aMg c #dc7d91",
+"ajp c #dc8694",
+"bls c #dc8996",
+"an2 c #dc8a98",
+".hv c #dc8e9a",
+"anA c #dc9199",
+"acV c #dc96a4",
+"bc5 c #dc98a0",
+"af0 c #dc9a9c",
+"atG c #dc9fa2",
+"aIR c #dca0a1",
+"bEe c #dca0a2",
+".jE c #dca1ae",
+".O4 c #dca3fb",
+".QI c #dcadff",
+"a9F c #dcaeb2",
+".08 c #dccbd6",
+"#jb c #dccfd3",
+".jo c #dcdae0",
+".PL c #dcdbdb",
+".3. c #dcdcdd",
+".Cp c #dcddde",
+".Gf c #dcdede",
+"arg c #dcdee0",
+".qD c #dcdfe1",
+"bXk c #dce0e2",
+".0K c #dce0e3",
+"anx c #dce1e2",
+".ny c #dce1e3",
+".ja c #dce1e4",
+"aIu c #dce2e3",
+"#yF c #dce2e4",
+".2a c #dce2e5",
+"ase c #dce3e5",
+".0B c #dce6ec",
+"#0. c #dce7ed",
+".oG c #dce8ee",
+"#b9 c #dcea89",
+"#2d c #dceaf1",
+"#0Q c #dcebf2",
+".Am c #dd37dd",
+"#gq c #dd7d92",
+"biv c #dd8191",
+"beB c #dd8290",
+".b2 c #dd8294",
+"bjF c #dd8592",
+"blr c #dd8c97",
+".gP c #dd8f9a",
+".hD c #dd95a0",
+".EQ c #dd9f9f",
+".zj c #dd9fa0",
+".ub c #dd9fa1",
+".O5 c #dda2fd",
+".kL c #dda5b1",
+"ar# c #dda9b1",
+"ap1 c #ddacb6",
+".Su c #ddb3fe",
+"#1L c #ddb8be",
+"ara c #ddb9c1",
+".XJ c #ddc3ef",
+".hj c #dddce1",
+".1M c #dddddd",
+".Wo c #dddede",
+".Gw c #dddedf",
+"QtO c #dddfe0",
+"#9S c #dddfe1",
+"acW c #dde0e2",
+".oC c #dde1e2",
+".vs c #dde1e3",
+".qG c #dde1e4",
+".bM c #dde2e4",
+".OU c #dde2e5",
+".nx c #dde3e5",
+"#bu c #dde3e6",
+"a#4 c #dde4e6",
+"#XU c #ddebf3",
+"#dN c #ddf181",
+"bdM c #de8394",
+".cB c #de8796",
+"ao5 c #de8b97",
+".db c #de8b98",
+"bkX c #de8d98",
+".gy c #de939e",
+"ap# c #de949c",
+".C6 c #de9fa0",
+".u7 c #dea0a1",
+".gS c #dea0a2",
+"bHq c #dea1a3",
+"ael c #dea4af",
+"b.c c #deaeb2",
+"a6s c #deb1b4",
+".Ug c #deb9fb",
+".Zn c #decff5",
+"#kJ c #ded2d6",
+"#i5 c #ded5d7",
+"#kC c #ded8da",
+".UY c #dedcdc",
+".1F c #dedddd",
+".Z9 c #deddde",
+"#nT c #dededf",
+"#ku c #dedfdf",
+".Ei c #dedfe0",
+".6K c #dee0e1",
+"bSR c #dee1e2",
+".iS c #dee2e4",
+".kp c #dee2e5",
+"aVB c #dee3e4",
+".sW c #dee3e5",
+".kn c #dee3e6",
+".Vx c #dee4e6",
+"#8M c #dee4e7",
+"asd c #dee6e8",
+"#S2 c #dee8ed",
+".xi c #dee9ee",
+".oM c #dee9f0",
+".vm c #deeff9",
+"bF2 c #df7b8d",
+"aYC c #df808e",
+"bkh c #df8695",
+".jv c #df8a98",
+"agE c #df8c97",
+"bzi c #df8f9a",
+".qW c #df919a",
+".d8 c #df939c",
+"bmC c #dfa3a7",
+"a9I c #dfa6ab",
+".QJ c #dfadff",
+"bVD c #dfafb4",
+".jH c #dfafba",
+".Sv c #dfb1fd",
+"#Yc c #dfb3bb",
+".ii c #dfc0c5",
+".4I c #dfc47e",
+"#0V c #dfcdd1",
+"#mb c #dfd3d6",
+"#l6 c #dfd8da",
+".Ep c #dfe0e0",
+".1l c #dfe0e1",
+".6N c #dfe1e2",
+".pg c #dfe1e3",
+".Ht c #dfe2e3",
+".B0 c #dfe2e4",
+"af8 c #dfe2e5",
+".mE c #dfe3e5",
+".jl c #dfe3e6",
+".oF c #dfe4e5",
+".sc c #dfe4e6",
+"#sM c #dfe4e7",
+".wb c #dfe4e8",
+"#9O c #dfe5e7",
+"asc c #dfe6e7",
+".qA c #dfeaef",
+"#Wh c #dfebf1",
+"aXT c #e0818f",
+"aT4 c #e08391",
+"aTc c #e08592",
+"bd2 c #e08b96",
+"akL c #e08e99",
+"bff c #e08f99",
+".gx c #e094a0",
+".jL c #e099a3",
+".Bi c #e0a1a3",
+".x4 c #e0a2a3",
+"aK7 c #e0a2a4",
+".O6 c #e0a3ff",
+"aqt c #e0a4a5",
+"bl3 c #e0a5a9",
+".gs c #e0a6a8",
+".qO c #e0abad",
+"b.K c #e0acaf",
+".jF c #e0acb7",
+"bfS c #e0b6be",
+".oS c #e0b7c0",
+"#nJ c #e0d6d9",
+"#n1 c #e0dadd",
+".3i c #e0e1e1",
+".4M c #e0e1e2",
+"#nt c #e0e2e2",
+"#1Z c #e0e3e4",
+".YX c #e0e4e5",
+".OA c #e0e4e6",
+".Xc c #e0e4e7",
+"#di c #e0e5e6",
+".mC c #e0e5e7",
+"#wM c #e0e5e8",
+"#ZY c #e0e6e8",
+".QB c #e0e6e9",
+"aUU c #e18491",
+"#2r c #e18e97",
+"aiX c #e18e9a",
+"amo c #e18f99",
+"alS c #e1919b",
+"btX c #e1949d",
+".gz c #e197a1",
+"a5M c #e199a0",
+".tj c #e1a2a3",
+"aK6 c #e1a2a5",
+".O7 c #e1a5ff",
+".fx c #e1a9aa",
+".QK c #e1abff",
+"a7P c #e1adb0",
+"#1P c #e1adb3",
+"#23 c #e1b8bf",
+".kV c #e1bac2",
+".XI c #e1cafa",
+".1G c #e1dfdf",
+".Ct c #e1e2e2",
+".1n c #e1e2e3",
+"aJL c #e1e4e5",
+"at6 c #e1e4e6",
+"#Yp c #e1e4e7",
+".pD c #e1e5e7",
+".7I c #e1e5e8",
+".fl c #e1e6e7",
+".5U c #e1e6e8",
+"#EG c #e1e6e9",
+"#io c #e1e7e9",
+"#Qy c #e1eaee",
+"QtB c #e1edf5",
+"aX# c #e28592",
+"aOo c #e28e98",
+".LL c #e28ef5",
+".ir c #e2949f",
+"bqB c #e2959e",
+"adw c #e2959f",
+"bhj c #e2969f",
+".fR c #e2979f",
+"bfT c #e299a1",
+".n6 c #e29ba0",
+".gA c #e29da5",
+"aGr c #e2a1a2",
+"bgA c #e2a1ab",
+".wW c #e2a3a4",
+".fU c #e2a3a5",
+"apZ c #e2a4a6",
+"aSk c #e2a5a6",
+"baU c #e2a7aa",
+"ami c #e2a8ae",
+".jG c #e2b6bf",
+"#2u c #e2d3d7",
+"#l5 c #e2dcde",
+".8s c #e2e1ab",
+".IV c #e2e1e2",
+".Ek c #e2e3e4",
+"aH# c #e2e4e5",
+".he c #e2e4e6",
+"#uz c #e2e5e6",
+"#Oy c #e2e5e7",
+"#2x c #e2e5e8",
+".nR c #e2e6e7",
+".ds c #e2e6e8",
+".Xd c #e2e6e9",
+"asb c #e2e7e8",
+".0v c #e2e7e9",
+"#zz c #e2e7ea",
+"#DP c #e2e8ea",
+"a.x c #e2e8eb",
+"#8u c #e2e9eb",
+"aux c #e2ecf2",
+"ccx c #e37a8a",
+"aVI c #e38693",
+"aWq c #e38794",
+"a3q c #e3939b",
+"ao7 c #e3939c",
+"bp2 c #e3969e",
+".cC c #e398a0",
+".eX c #e39aa0",
+"#2m c #e39aa1",
+"b#r c #e39ea4",
+"a.L c #e3a2a5",
+".sC c #e3a4a5",
+".ea c #e3a4a6",
+"aTa c #e3a7a7",
+"aT3 c #e3a7a8",
+"#YV c #e3adb3",
+"boC c #e3b5b7",
+"#22 c #e3bcc2",
+"bfc c #e3d4d9",
+"#ma c #e3dbdd",
+"#l7 c #e3ddde",
+".Wp c #e3e3e4",
+".Gl c #e3e4e4",
+".Q8 c #e3e4e5",
+"#Kp c #e3e5e7",
+"amh c #e3e6e7",
+"#NH c #e3e6e8",
+".Dw c #e3e6e9",
+".ru c #e3e7e8",
+".g. c #e3e7e9",
+".9P c #e3e7ea",
+"#UU c #e3e8e9",
+".7V c #e3e8ea",
+"apP c #e3e8eb",
+"#KP c #e3e9ea",
+"#7I c #e3e9eb",
+"aa7 c #e3e9ec",
+"#ai c #e3ea8e",
+"#TS c #e3eaed",
+"#3w c #e3ecf0",
+".kz c #e3edf0",
+"ae6 c #e4969e",
+"ao6 c #e4979f",
+"bwh c #e497a0",
+"aJm c #e49ba1",
+"anz c #e49d9f",
+".lb c #e49ea3",
+"aoF c #e4a2a6",
+".nV c #e4a2a8",
+".pS c #e4a2a9",
+".sB c #e4a3a5",
+".oU c #e4a3a9",
+".O8 c #e4a3ff",
+".x5 c #e4a4a5",
+"buy c #e4a4a6",
+"bnc c #e4a4a8",
+"bzk c #e4a5a6",
+"asP c #e4a5a7",
+"bn5 c #e4a7a9",
+"aUT c #e4a8a8",
+"aJS c #e4a9ae",
+"#0i c #e4acaf",
+".tQ c #e4adaf",
+".VX c #e4c3fd",
+".pP c #e4cace",
+".2R c #e4d4cf",
+"#9I c #e4dadc",
+"#kI c #e4dcde",
+".IU c #e4e3e3",
+"#np c #e4e5e6",
+"#gA c #e4e6e7",
+"bnG c #e4e6e8",
+"ake c #e4e7e8",
+".yl c #e4e7e9",
+"#ff c #e4e7ea",
+"aiM c #e4e8e9",
+".sX c #e4e8ea",
+".cm c #e4e8eb",
+"at3 c #e4e9ea",
+".9U c #e4e9eb",
+"aov c #e4e9ec",
+"#2M c #e4eaec",
+"#XP c #e4eaed",
+"#UR c #e4ecf0",
+"baS c #e5949c",
+"bbZ c #e5959d",
+"anU c #e5959e",
+"amZ c #e598a0",
+"ajq c #e599a0",
+"bhl c #e599a1",
+".Np c #e599fe",
+"aiZ c #e59aa1",
+".hQ c #e59ba2",
+".No c #e59bff",
+".Nn c #e59cfd",
+"ak4 c #e5a1a7",
+".qM c #e5a3aa",
+".uc c #e5a5a6",
+".rE c #e5a6ac",
+".4B c #e5d9c3",
+"#kB c #e5dee0",
+"#n0 c #e5e1e2",
+".29 c #e5e6e6",
+".9M c #e5e7e7",
+"aM. c #e5e7e8",
+"a#2 c #e5e8e8",
+".pC c #e5e8e9",
+".kx c #e5e8ea",
+".wc c #e5e8eb",
+"#P3 c #e5e9ea",
+".lD c #e5e9eb",
+".9T c #e5e9ec",
+"#Qe c #e5eaeb",
+".TR c #e5eaec",
+".uC c #e5edf2",
+"#Wy c #e5f0f5",
+"#fF c #e5f87d",
+"bbp c #e6939c",
+"bc3 c #e69ba2",
+"brj c #e69ca2",
+".gw c #e69da5",
+"aoE c #e6a0a1",
+".nc c #e6a0a5",
+".pb c #e6a5a8",
+".e1 c #e6a6a8",
+"bnD c #e6a6a9",
+".m1 c #e6a6ac",
+"blt c #e6a9ab",
+".Uh c #e6beff",
+"abw c #e6dde1",
+"#kH c #e6dee0",
+"#1g c #e6dee1",
+"acN c #e6dfe0",
+".Mc c #e6e7e7",
+"#gy c #e6e7e8",
+"#Jy c #e6e7e9",
+".ia c #e6e8e9",
+"#Ou c #e6e8ea",
+".HI c #e6e8eb",
+"#3a c #e6e9ea",
+".Oz c #e6e9eb",
+"#oi c #e6e9ec",
+"ayr c #e6eaea",
+".MV c #e6eaeb",
+".zJ c #e6eaec",
+"#fa c #e6eaed",
+"##R c #e6ebec",
+"#tK c #e6ebed",
+"a1Z c #e6ecee",
+".ih c #e6edef",
+"anZ c #e79ba1",
+"biR c #e79ca3",
+".sD c #e7a6a7",
+"aMa c #e7a6a8",
+"aHv c #e7a7a8",
+"aIO c #e7a7a9",
+"alr c #e7adaf",
+"bo9 c #e7adb0",
+".jM c #e7afb0",
+".X0 c #e7b8aa",
+"af6 c #e7babd",
+".kM c #e7c4cb",
+".l2 c #e7d7da",
+"#V. c #e7dbdd",
+"#m# c #e7dee1",
+".ZO c #e7e8e8",
+"aDu c #e7e8ea",
+"b2w c #e7e9ea",
+".nu c #e7e9eb",
+".Si c #e7eaeb",
+".sd c #e7eaec",
+".7U c #e7eaed",
+".0F c #e7ebec",
+".Vt c #e7ebed",
+"#S8 c #e7ecee",
+".73 c #e7eff4",
+"#Xf c #e7f7fc",
+"a2P c #e8949c",
+".ju c #e89aa2",
+"bfe c #e89ba1",
+"akj c #e89ca2",
+"agH c #e89ea3",
+"bmB c #e89ea4",
+"bfV c #e89fa4",
+".hu c #e89fa5",
+"bvl c #e8a0a5",
+"bl1 c #e8a1a5",
+"an1 c #e8a1a6",
+".iN c #e8a3a6",
+".d9 c #e8a3a7",
+".Bj c #e8a6a7",
+".rS c #e8a6a8",
+"aHi c #e8a6a9",
+".ud c #e8a7a8",
+"arG c #e8a7a9",
+".Sw c #e8b5ff",
+"#24 c #e8c6cc",
+"#mi c #e8e5e5",
+".Z3 c #e8e6e7",
+"#5Y c #e8e8e8",
+".Gp c #e8e8e9",
+".X3 c #e8e9e9",
+"acJ c #e8e9eb",
+".rx c #e8eaeb",
+"#Hd c #e8eaec",
+"#P1 c #e8ebeb",
+".RM c #e8ebec",
+".vh c #e8ebed",
+".se c #e8eced",
+".Xh c #e8ecee",
+"#Dg c #e8ecef",
+".58 c #e8eef1",
+".vg c #e8eef2",
+".mq c #e8eff4",
+".FD c #e8f0f4",
+".fb c #e8f0f5",
+".LM c #e992fd",
+"#1H c #e9959c",
+"a4e c #e9959d",
+".Q5 c #e99dc6",
+"bgB c #e99ea2",
+"brT c #e9a0a5",
+"am0 c #e9a2a6",
+"aOn c #e9a4a6",
+".l# c #e9a5a7",
+"aNo c #e9a6a9",
+".u8 c #e9a7a9",
+".sE c #e9a8a9",
+"bIA c #e9a8aa",
+"#9g c #e9abad",
+"#9f c #e9abaf",
+"#21 c #e9c7cd",
+".20 c #e9ca8f",
+".qK c #e9dcde",
+"#nB c #e9e2e4",
+"a.I c #e9e4e5",
+"bfR c #e9e7e9",
+".KQ c #e9e8e8",
+".UN c #e9eaea",
+"#gz c #e9eaeb",
+".lH c #e9ebeb",
+".tF c #e9ebec",
+"#YH c #e9ebed",
+".ux c #e9ecec",
+".W9 c #e9eced",
+".po c #e9ecee",
+".ji c #e9edee",
+"#xD c #e9edef",
+"as8 c #e9edf0",
+".pF c #e9eeef",
+"#Wx c #e9f1f5",
+"a14 c #ea969d",
+"#ZF c #ea9da2",
+"ae5 c #eaa2a6",
+".me c #eaa5a7",
+".jW c #eaa6a8",
+".tk c #eaa8a9",
+".wX c #eaa8aa",
+"bbq c #eaaaac",
+".QL c #eaafff",
+".m2 c #eab1b2",
+".1i c #eac79a",
+"#25 c #ead1d5",
+"ajV c #eadade",
+"#nI c #eae1e4",
+"#nS c #eae7e8",
+"#5b c #eae8e8",
+".N4 c #eae9e9",
+".Gx c #eaeaea",
+".IE c #eaebeb",
+".kt c #eaeced",
+"#J7 c #eaecee",
+".jm c #eaeded",
+".j2 c #eaedee",
+".bK c #eaedef",
+"#DM c #eaedf0",
+"#W7 c #eaeeee",
+".2j c #eaeeef",
+"aXI c #eaeef0",
+".nI c #eaf1f5",
+"#G# c #eaf5fb",
+".DW c #eb5eed",
+".NM c #eb90d4",
+".dc c #eb9ca4",
+"atE c #eb9ea5",
+"bBt c #eba0a6",
+"aF2 c #eba2a7",
+"biK c #eba4a8",
+"bsB c #eba5a8",
+".gv c #eba5aa",
+".gQ c #eba6a8",
+".p2 c #eba7a8",
+".hS c #eba8a9",
+".qa c #eba8aa",
+".rU c #eba9aa",
+"a9. c #eba9ab",
+".gt c #ebabad",
+"aoD c #ebadaf",
+"a6t c #ebafaf",
+".fy c #ebafb0",
+"#LJ c #ebbec0",
+"anD c #ebbfc4",
+".ZE c #ebc0a3",
+"aoH c #ebd4d8",
+"#11 c #ebd5d7",
+"anC c #ebd6d9",
+".mY c #ebe9e9",
+"al. c #ebe9ea",
+".Z2 c #ebeaea",
+".N6 c #ebebeb",
+".yu c #ebecec",
+"afS c #ebeced",
+".r9 c #ebecee",
+".l0 c #ebeded",
+".hh c #ebedee",
+"#dk c #ebedef",
+".mX c #ebeeee",
+".ci c #ebeeef",
+".r1 c #ebeef0",
+".l1 c #ebefef",
+"#f# c #ebeff0",
+"#df c #ebeff1",
+"av# c #ebf1f3",
+".wp c #ebf1f6",
+".iq c #eca3a8",
+"btg c #eca5a9",
+"adv c #eca6a9",
+".e. c #eca7a9",
+"amS c #eca7aa",
+".O9 c #eca7ff",
+".eY c #eca9aa",
+"aMb c #eca9ab",
+"#0W c #eca9ad",
+"aOm c #ecabac",
+"aRm c #ecacad",
+"aW9 c #ecadae",
+"bk1 c #ecadaf",
+".sj c #ecadb0",
+"a.K c #ecb1b4",
+".sk c #ecb4b4",
+"aoG c #ecd1d3",
+"aml c #ecdee1",
+".PM c #eceaea",
+".Mh c #ececec",
+"alb c #eceded",
+"#1f c #ecedee",
+".rw c #ecedef",
+".ig c #eceeee",
+"#Pk c #eceeef",
+".cR c #eceef0",
+".hi c #ecefef",
+".km c #eceff0",
+"#uP c #eceff1",
+".nS c #ecf0ef",
+"anp c #ecf0f2",
+"bdA c #ed9da2",
+"a.l c #ed9da3",
+"#2n c #ed9fa4",
+"bkW c #ed9fa5",
+"aYB c #eda2a6",
+"bd3 c #eda3a7",
+"bl2 c #eda6a9",
+"bkk c #eda7aa",
+".C7 c #eda9aa",
+".rT c #eda9ab",
+".rV c #edaaab",
+".zk c #edaaac",
+".sF c #edabac",
+"b.e c #edabad",
+"aXR c #edafaf",
+".rG c #edb3b5",
+".rF c #edb4b5",
+"aXQ c #edc3c4",
+".XN c #edc9ea",
+"#4s c #edcfcf",
+"#26 c #edd7db",
+"ak2 c #eddfe1",
+"#Lf c #ede2e2",
+"#nH c #ede4e7",
+"ahx c #edebeb",
+".Mx c #ededed",
+".Eg c #ededee",
+".Wj c #edeeee",
+".5Y c #edeeef",
+"#Yo c #edeef0",
+"awW c #edefee",
+".if c #edefef",
+"#yw c #edeff0",
+"##Q c #edeff1",
+".jn c #edf0f0",
+".re c #edf0f1",
+"QtH c #edf0f2",
+"a1U c #edf1f2",
+"a.Z c #edf2f2",
+".qB c #edf2f4",
+".38 c #edf3f7",
+".kU c #edf4f4",
+".LN c #ee93fd",
+"a0C c #ee999f",
+".Nq c #ee9eff",
+"ahB c #eea0a6",
+"beA c #eea6a9",
+".jX c #eea8aa",
+"ab8 c #eea9ab",
+"aIB c #eeaaab",
+"bl0 c #eeaaac",
+".rW c #eeabac",
+"aHj c #eeabad",
+".hR c #eeacac",
+"aNn c #eeacad",
+".iD c #eeacaf",
+".hs c #eeadae",
+"aYA c #eeaeae",
+".jp c #eeaeb0",
+".gB c #eeaeb1",
+".fz c #eeb2b2",
+".SB c #eeb3f5",
+".qN c #eeb5b6",
+".V3 c #eec1ea",
+".V2 c #eec1ef",
+"#Tq c #eec2c2",
+".2S c #eed7ca",
+"ajT c #eedadc",
+".kN c #eedfe2",
+"#3c c #eee2e3",
+"#.s c #eee360",
+"#.p c #eee990",
+"ala c #eeeced",
+"anF c #eeedee",
+".UL c #eeeeee",
+".ky c #eeefee",
+".gj c #eeefef",
+".T5 c #eeeff0",
+"#KR c #eeeff1",
+".ie c #eef0ef",
+".rt c #eef0f0",
+".k. c #eef0f1",
+".cZ c #eef0f2",
+"ayU c #eef1f0",
+".pB c #eef1f1",
+".32 c #eef1f2",
+".qC c #eef1f3",
+"a.Y c #eef2f2",
+".TU c #eef2f3",
+".kT c #eef3f2",
+".Al c #ef38ef",
+".Ag c #ef39ef",
+".B9 c #ef4ff1",
+"bh3 c #efa2a7",
+"bjA c #efa3a8",
+"bDd c #efa7ab",
+"byk c #efa8ab",
+"an0 c #efaaac",
+".qe c #efabac",
+"ae4 c #efabad",
+".o4 c #efacac",
+".rX c #efacad",
+".eZ c #efadad",
+"a5L c #efaeae",
+".hk c #efafb1",
+"#2z c #efb9ba",
+"a.J c #efc6c7",
+".XO c #efc7e4",
+".Zs c #efd0e5",
+"amm c #efd6d8",
+".09 c #efd7df",
+"ap0 c #efdcdd",
+"#2y c #efe7e8",
+".rC c #efeaec",
+"ap3 c #efecec",
+"ak9 c #efeced",
+".lI c #efefee",
+".WD c #efefef",
+"#0A c #efeff0",
+".qx c #eff0f0",
+".gX c #eff0f1",
+".ym c #eff0f2",
+"ays c #eff1f0",
+".qz c #eff1f1",
+".ku c #eff1f2",
+".OK c #eff1f3",
+"#Rk c #eff2f2",
+"#lv c #eff2f3",
+".uj c #eff2f4",
+".uw c #eff3f4",
+".6. c #eff5f9",
+".Pe c #f0a3f7",
+".Pd c #f0a3f9",
+".Pc c #f0a5fc",
+"bfU c #f0a8aa",
+"bhk c #f0a9aa",
+"ap. c #f0aaac",
+"#3d c #f0abac",
+".qd c #f0abad",
+".gu c #f0abae",
+"bh4 c #f0acac",
+".qc c #f0acad",
+"a#A c #f0acae",
+"biN c #f0adad",
+".gR c #f0adae",
+"ah5 c #f0aeae",
+"aZe c #f0aeaf",
+".QP c #f0aefc",
+".l6 c #f0b3b4",
+".SD c #f0b3f1",
+".SC c #f0b3f3",
+".hE c #f0b4b4",
+".s3 c #f0b5b6",
+"acO c #f0b5b7",
+".Un c #f0b9ed",
+"#1G c #f0c1c8",
+"#3O c #f0c6c7",
+"#9J c #f0cbcb",
+".Zt c #f0cede",
+"#OJ c #f0d5d6",
+"#Na c #f0dcdd",
+"aiC c #f0dfe0",
+"amq c #f0e4e4",
+"#Mq c #f0e6e6",
+"akZ c #f0e6e7",
+"#9D c #f0eaea",
+"#6S c #f0ebeb",
+"arb c #f0ebec",
+"#9Y c #f0ecec",
+"ap4 c #f0eeef",
+"al# c #f0eff0",
+".Iy c #f0f0f0",
+"#1e c #f0f0f1",
+"acM c #f0f0f2",
+".mF c #f0f1f0",
+".pE c #f0f1f1",
+"#Nb c #f0f1f2",
+"#uO c #f0f1f3",
+"b0Z c #f0f2f1",
+".sa c #f0f2f2",
+".jc c #f0f2f3",
+"#vB c #f0f2f4",
+"#gW c #f0f3f3",
+".TT c #f0f3f4",
+".qm c #f0f3f6",
+".pK c #f0f4f7",
+".oR c #f0f5f5",
+".Ch c #f14ff2",
+".K# c #f18bfb",
+".Nx c #f199f8",
+".Nv c #f19cfb",
+"aZV c #f19fa3",
+"bCo c #f1a6aa",
+"aXS c #f1a7a9",
+"baa c #f1a9ab",
+"ao8 c #f1a9ac",
+"bk0 c #f1aaab",
+"#3Q c #f1abab",
+"aJR c #f1abad",
+".q6 c #f1acad",
+"a#z c #f1acae",
+".QR c #f1acf4",
+".v2 c #f1adad",
+".mf c #f1adae",
+".QQ c #f1adf8",
+".la c #f1aead",
+".fS c #f1aeae",
+"aaL c #f1aeaf",
+"bc4 c #f1afaf",
+"#N# c #f1b5b5",
+".pT c #f1b7b7",
+".Uo c #f1b9eb",
+".Um c #f1bcf5",
+"#Mp c #f1c0c1",
+".V4 c #f1c1e7",
+"#LN c #f1d2d2",
+".1. c #f1d5d9",
+"#2k c #f1d5db",
+"#NT c #f1d6d6",
+"#Px c #f1dada",
+"a#5 c #f1dadb",
+".kO c #f1e3e5",
+"#LO c #f1e4e5",
+"#4j c #f1ebeb",
+"alm c #f1ebec",
+"ap2 c #f1ebed",
+"#5# c #f1ecec",
+"ams c #f1eded",
+"#3F c #f1eeee",
+"a#3 c #f1efef",
+"cd3 c #f1f0ef",
+".WG c #f1f0f0",
+"#10 c #f1f0f1",
+"#c# c #f1f146",
+".Ef c #f1f1f1",
+"#5V c #f1f1f2",
+"bxy c #f1f2f1",
+".id c #f1f2f2",
+".dy c #f1f2f3",
+".g7 c #f1f2f4",
+"#4r c #f1f3f2",
+".lC c #f1f3f3",
+".sR c #f1f3f4",
+"#ir c #f1f3f5",
+".mO c #f1f3f6",
+".kS c #f1f4f5",
+"#NU c #f1f5f6",
+".np c #f1f5f8",
+"bew c #f1f6f7",
+".D9 c #f25fed",
+".I. c #f27ef9",
+".LS c #f292fb",
+"#2q c #f2a0a3",
+"aZf c #f2a3a7",
+"#2p c #f2a7a8",
+"a3p c #f2aaa9",
+"baT c #f2acac",
+"b.L c #f2acad",
+"a4X c #f2adad",
+".iO c #f2adae",
+".ip c #f2adaf",
+".nd c #f2aeae",
+".ht c #f2aeaf",
+"#Qb c #f2afaf",
+"#2A c #f2afb0",
+"adu c #f2b0af",
+"ae3 c #f2b0b0",
+"#Mo c #f2b1b1",
+".sl c #f2b7b7",
+"#LM c #f2babb",
+"#QS c #f2c1c1",
+"aad c #f2c6c8",
+"#9W c #f2c7c7",
+".XP c #f2c7e1",
+".Zu c #f2ced9",
+".1# c #f2d3d1",
+".4C c #f2ddb6",
+"#RJ c #f2e1e1",
+"#Qd c #f2e7e8",
+"ar. c #f2e8e8",
+"#6R c #f2ecec",
+"#7O c #f2eded",
+"#5. c #f2eeee",
+"#3D c #f2efef",
+"#9E c #f2eff0",
+"#8D c #f2f0f0",
+"#8E c #f2f0f1",
+"#9F c #f2f1f1",
+".nz c #f2f2f1",
+".Eu c #f2f2f2",
+"brs c #f2f3f2",
+".hg c #f2f3f3",
+".rd c #f2f3f4",
+".jb c #f2f3f5",
+"#KQ c #f2f4f3",
+".kR c #f2f4f4",
+".TA c #f2f4f5",
+".oz c #f2f4f6",
+"#LP c #f2f5f5",
+"#qp c #f2f5f6",
+".HV c #f2f5f7",
+"#Mr c #f2f6f6",
+".ce c #f2f6fa",
+".Y8 c #f2f7fb",
+".Ia c #f37eff",
+".Kd c #f388fa",
+".LT c #f391fd",
+".LU c #f392fb",
+".Nw c #f39bfd",
+".Pf c #f3a3f5",
+"#2o c #f3a6a8",
+"ahq c #f3a9a9",
+"#3P c #f3abab",
+"a8x c #f3acab",
+"a4d c #f3acac",
+"b.d c #f3acad",
+"biM c #f3acae",
+".e# c #f3adae",
+"bNt c #f3adaf",
+".q7 c #f3aeae",
+".q5 c #f3aeaf",
+"aBd c #f3aeb0",
+".x6 c #f3afaf",
+"#OI c #f3afb0",
+"bdz c #f3b0af",
+"bjB c #f3b0b0",
+"agG c #f3b1b0",
+"#NS c #f3b1b1",
+"agF c #f3b3b1",
+".fA c #f3b3b5",
+"ai0 c #f3b6b6",
+".s4 c #f3b7b8",
+".nW c #f3b8b8",
+".Up c #f3bae9",
+"abh c #f3c2c2",
+"ajX c #f3d8d8",
+"abg c #f3e1e1",
+".6C c #f3e2a7",
+"#20 c #f3e4e7",
+".kP c #f3eaeb",
+"ajY c #f3ebeb",
+"abq c #f3ecec",
+"#4k c #f3eded",
+"#6Q c #f3eeee",
+"anE c #f3efee",
+"#49 c #f3efef",
+"amr c #f3eff0",
+"#7N c #f3f0f0",
+"#Lh c #f3f0f1",
+"#3C c #f3f1f1",
+"aln c #f3f1f2",
+".Yr c #f3f2f2",
+"byo c #f3f3f2",
+".CK c #f3f3f3",
+".DQ c #f3f3f4",
+".tE c #f3f4f3",
+".gi c #f3f4f4",
+".qw c #f3f4f5",
+".ox c #f3f4f6",
+"#ZX c #f3f5f4",
+".jh c #f3f5f5",
+".HJ c #f3f5f6",
+".TS c #f3f5f7",
+".lZ c #f3f6f6",
+".Xt c #f3f6f7",
+".pm c #f3f6f9",
+"#SC c #f3f7f7",
+".on c #f3f7f9",
+".Kf c #f487fd",
+".LW c #f491f8",
+".LV c #f492fa",
+"a7R c #f4abab",
+"aeg c #f4abad",
+"a9G c #f4acad",
+"biL c #f4acae",
+"b.N c #f4adad",
+"afZ c #f4adaf",
+".QS c #f4adf2",
+"a.n c #f4aeae",
+".q3 c #f4aeaf",
+".Bk c #f4aeb0",
+".e0 c #f4afaf",
+".pc c #f4afb0",
+"#Pv c #f4b0b0",
+"a.m c #f4b1b0",
+".SE c #f4b3ef",
+"#Qc c #f4b5b4",
+".kY c #f4b6b7",
+".tR c #f4b9b9",
+".V5 c #f4c1e3",
+".XQ c #f4c8dd",
+"any c #f4d1d1",
+".1a c #f4d4cb",
+"#9X c #f4e1e2",
+"#Lg c #f4eaea",
+"#5W c #f4eeee",
+"#6T c #f4efef",
+"ak7 c #f4f0ef",
+"#3E c #f4f0f0",
+"arc c #f4f1f1",
+".kQ c #f4f1f2",
+"aj0 c #f4f3f3",
+".Ej c #f4f4f4",
+"#Xu c #f4f4f5",
+"acA c #f4f5f4",
+"#IH c #f4f5f5",
+".lf c #f4f5f6",
+".ul c #f4f5f7",
+"#3A c #f4f6f5",
+".gh c #f4f6f6",
+".sg c #f4f6f7",
+"#8F c #f4f7f6",
+"#Uj c #f4f7f7",
+"#MU c #f4f7f8",
+"aiD c #f4f8f8",
+"ajZ c #f4f9f9",
+".4# c #f4f9fc",
+".FR c #f572fa",
+".FT c #f572ff",
+".Kg c #f587fd",
+".Ke c #f587ff",
+".LX c #f592f7",
+".Nz c #f59bf7",
+".Ny c #f59bf8",
+".Pg c #f5a4f3",
+"a9# c #f5acad",
+"aX. c #f5acae",
+".QT c #f5acf0",
+"a7Q c #f5adad",
+"aWp c #f5adaf",
+"b.M c #f5aeae",
+"bzj c #f5aeaf",
+".qb c #f5afaf",
+".fT c #f5afb0",
+".q4 c #f5b0b1",
+"aBc c #f5b1b0",
+"#Pw c #f5b1b1",
+"asN c #f5b2b2",
+"amT c #f5b3b2",
+"#LL c #f5b3b3",
+"a#6 c #f5bab9",
+"a2N c #f5bcbc",
+".gk c #f5bebf",
+".Zv c #f5ced5",
+"aiV c #f5d4d5",
+".2T c #f5d9c1",
+"ak6 c #f5dad9",
+"ard c #f5f2f2",
+"#2V c #f5f4f4",
+".r5 c #f5f5f5",
+".g# c #f5f5f6",
+"#27 c #f5f6f5",
+".rs c #f5f6f6",
+".h5 c #f5f6f7",
+".pw c #f5f6f8",
+"#Yi c #f5f7f6",
+".pA c #f5f7f7",
+"#De c #f5f7f8",
+"#DQ c #f5f7f9",
+"aHa c #f5f8f7",
+"aiE c #f5f8f8",
+"ajW c #f5f9f9",
+".H2 c #f5f9fa",
+"ak3 c #f5faf9",
+"aoI c #f5fafa",
+".2s c #f5fafd",
+".FS c #f672fe",
+".FU c #f672ff",
+".Kh c #f688fc",
+".Ph c #f6a3f2",
+"a7c c #f6adac",
+"aTb c #f6adaf",
+"a13 c #f6aeae",
+"a1i c #f6aeaf",
+"aVH c #f6aeb0",
+"a0B c #f6afaf",
+".rY c #f6afb0",
+"#OH c #f6b0b0",
+"aOl c #f6b0b1",
+"aRl c #f6b1b2",
+".SF c #f6b3ed",
+".V6 c #f6c1e0",
+"#SB c #f6d6d6",
+"a.X c #f6d8d8",
+"akd c #f6e1e1",
+".lU c #f6ecee",
+".pL c #f6f6f6",
+".cf c #f6f6f7",
+"#Ft c #f6f6f8",
+"#tJ c #f6f7f7",
+".a2 c #f6f7f8",
+"#Yh c #f6f8f7",
+".hf c #f6f8f8",
+".rp c #f6f8f9",
+"#c. c #f6f965",
+"aEM c #f6f9f8",
+".2o c #f6f9fb",
+"#V# c #f6faf9",
+"#dO c #f6fb5e",
+".0L c #f6fbfd",
+".Ah c #f73bf8",
+".FW c #f772fe",
+".I# c #f77dff",
+".Ic c #f77efe",
+".Ib c #f77eff",
+".Kj c #f787f9",
+".Ki c #f788fa",
+".NA c #f79bf5",
+".Pi c #f7a4ef",
+"a2O c #f7adad",
+"a9a c #f7aead",
+"a8w c #f7aeae",
+"a#B c #f7afae",
+"bSK c #f7afaf",
+"#Mn c #f7b0b0",
+"arF c #f7b0b1",
+"aIQ c #f7b0b2",
+"abi c #f7b1b1",
+"asO c #f7b1b2",
+"ao9 c #f7b2b2",
+"atF c #f7b2b3",
+".Uq c #f7bae6",
+".XR c #f7c8d8",
+".Zw c #f7ced0",
+".8t c #f7e995",
+".8w c #f7ea77",
+"ak8 c #f7f3f3",
+"#6P c #f7f4f4",
+".zO c #f7f7f7",
+".#9 c #f7f7f8",
+"afH c #f7f8f7",
+".ic c #f7f8f8",
+".dx c #f7f8f9",
+".FK c #f7f8fa",
+"#5T c #f7f9f8",
+"#Xl c #f7f9f9",
+"#tI c #f7f9fa",
+".lY c #f7fafb",
+".Id c #f87efd",
+".L9 c #f891e6",
+".LY c #f891f5",
+".NB c #f89bf4",
+".QU c #f8adef",
+"#3e c #f8aeaf",
+"a9H c #f8afae",
+"bU. c #f8afaf",
+".pd c #f8b1b1",
+".pe c #f8b1b2",
+".SQ c #f8b1ce",
+"#NR c #f8b2b1",
+"bxr c #f8b2b2",
+".SG c #f8b3e9",
+".Ur c #f8bae2",
+".1b c #f8d5c5",
+"#5a c #f8f2f2",
+"#4l c #f8f5f5",
+"aj1 c #f8f6f6",
+".GK c #f8f7f7",
+".KR c #f8f7f8",
+".Cq c #f8f8f8",
+".mS c #f8f8f9",
+".nL c #f8f8fa",
+".ib c #f8f9f9",
+".nM c #f8f9fa",
+"ad2 c #f8faf9",
+"ad3 c #f8fafa",
+".f6 c #f8fafb",
+".pO c #f8fbfa",
+".lX c #f8fbfb",
+".lV c #f8fbfc",
+".us c #f8fbfd",
+".Ak c #f93bfa",
+".Ai c #f93cfb",
+".DX c #f963fc",
+".DY c #f964fe",
+".DZ c #f964ff",
+".FX c #f972fd",
+".FV c #f972ff",
+".If c #f97efb",
+".Ie c #f97efc",
+".LZ c #f992f4",
+".NC c #f99bf2",
+".QV c #f9adec",
+"a.o c #f9b0ae",
+"aIU c #f9b2b2",
+"aIP c #f9b2b4",
+"#N. c #f9b3b2",
+"#RI c #f9b3b3",
+"aF3 c #f9b4b4",
+".V7 c #f9c0dd",
+"acC c #f9c7c7",
+".XS c #f9c8d4",
+".2U c #f9d9ba",
+"acB c #f9ebeb",
+"#Ui c #f9f1f1",
+"#aj c #f9f36f",
+"#6U c #f9f4f4",
+"#5X c #f9f5f4",
+".CT c #f9f8f8",
+".Co c #f9f9f9",
+".kq c #f9f9fa",
+".mR c #f9f9fb",
+"#7P c #f9faf9",
+".J0 c #f9fafa",
+".mT c #f9fafb",
+".qJ c #f9fbfb",
+".mQ c #f9fbfc",
+".lW c #f9fcfc",
+".Aj c #fa3bfb",
+".D2 c #fa64fc",
+".D1 c #fa64fd",
+".D0 c #fa64fe",
+".FZ c #fa72fb",
+".FY c #fa73fc",
+".Kl c #fa87f7",
+".Kk c #fa87f8",
+".L0 c #fa91f3",
+".Pj c #faa3ed",
+"#QR c #fab1b1",
+"aAN c #fab2b2",
+"aAO c #fab2b3",
+"acD c #fab3b3",
+"aH8 c #fab3b4",
+".SH c #fab3e6",
+"aU5 c #fab5b4",
+"amp c #fabab9",
+".uG c #fabbbb",
+"akk c #fabdbe",
+".V8 c #fac0d8",
+".fm c #facdce",
+".4D c #fadfac",
+".6D c #fae59d",
+"#6V c #faf7f7",
+".Rt c #faf9f9",
+"#oM c #faf9fb",
+".vv c #fafafa",
+".cU c #fafafb",
+"#Hc c #fafbfa",
+".nN c #fafbfb",
+".52 c #fafbfc",
+"#VS c #fafcfb",
+".sZ c #fafcfc",
+".C. c #fb52fc",
+".Cd c #fb53fc",
+".F7 c #fb72f4",
+".Io c #fb7df3",
+".Ih c #fb7ef8",
+".Ig c #fb7ef9",
+".Km c #fb87f5",
+".Kt c #fb88ee",
+".Ps c #fba3df",
+".Pk c #fba4eb",
+".QW c #fbace8",
+"#1I c #fbadac",
+"bVE c #fbb0ae",
+"bE4 c #fbb3b3",
+"aO8 c #fbb3b4",
+".SI c #fbb3e3",
+"#LK c #fbb4b3",
+"aGP c #fbb6b5",
+"alq c #fbbabb",
+".Us c #fbbadf",
+".Zx c #fbcecb",
+".2V c #fbdab3",
+"#.q c #fbef7c",
+"#ak c #fbf75e",
+".nK c #fbfafb",
+"#oL c #fbfafc",
+".ml c #fbfbfb",
+".lm c #fbfbfc",
+".3X c #fbfbfd",
+"#G. c #fbfcfb",
+".tM c #fbfcfc",
+".mP c #fbfcfd",
+"#9y c #fbfdfc",
+"#0f c #fbfdfd",
+".Cb c #fc53fe",
+".D8 c #fc63fa",
+".D3 c #fc64fb",
+".F0 c #fc72fa",
+".F1 c #fc73f9",
+".L1 c #fc91f1",
+".ND c #fc9aee",
+".NL c #fc9be4",
+".Q4 c #fcabd8",
+"bXg c #fcb0ae",
+".Ut c #fcbadc",
+".UA c #fcbbcc",
+".Wd c #fcc2c4",
+".XU c #fcc8cc",
+".XT c #fcc8d0",
+".Zy c #fccec6",
+".1c c #fcd5bf",
+".2W c #fcdbae",
+".4E c #fce0a5",
+".4H c #fce195",
+".6G c #fce788",
+".8u c #fceb88",
+".oN c #fcfbfb",
+".oo c #fcfbfc",
+".my c #fcfbfd",
+".hV c #fcfcfc",
+".a# c #fcfcfd",
+".h4 c #fcfcfe",
+"#WF c #fcfdfc",
+".nJ c #fcfdfd",
+".#4 c #fcfdfe",
+".Ce c #fd52fa",
+".Cc c #fd52fe",
+".C# c #fd53fc",
+".Cg c #fd53fd",
+".F6 c #fd72f5",
+".F2 c #fd72f8",
+".Ii c #fd7ef7",
+".Ko c #fd87f3",
+".L2 c #fd92ef",
+".NF c #fd9aec",
+".NE c #fd9bee",
+".Pl c #fda3e9",
+".QX c #fdace6",
+".SP c #fdb0d0",
+"#0Z c #fdb2b0",
+".SJ c #fdb3e0",
+".V9 c #fdc0d4",
+".XZ c #fdc9bc",
+".ZD c #fdceb2",
+".1d c #fdd5bb",
+".1h c #fdd6a9",
+".2Z c #fddca0",
+".6E c #fde593",
+"#.r c #fdef70",
+".pn c #fdfcfc",
+".cV c #fdfcfd",
+".uk c #fdfcfe",
+"#Df c #fdfdfc",
+".g6 c #fdfdfd",
+".a. c #fdfdfe",
+".BL c #fdfefe",
+"#3K c #fdffff",
+".Ca c #fe52fe",
+".Cf c #fe53fc",
+".D7 c #fe64fa",
+".D4 c #fe64fb",
+".F5 c #fe72f6",
+".F4 c #fe72f7",
+".Im c #fe7ef1",
+".In c #fe7ef2",
+".Ks c #fe87ec",
+".Kr c #fe88ef",
+".Kn c #fe88f5",
+".L7 c #fe90e8",
+".L8 c #fe91e9",
+".NH c #fe9be9",
+".Pr c #fea3dd",
+".Pm c #fea3e7",
+".QY c #feade4",
+".SK c #feb3de",
+"#1K c #feb5b3",
+"#1J c #feb6b4",
+".Uu c #febad9",
+".XX c #fec7c1",
+".XV c #fec8c8",
+".Zz c #fecec3",
+".1g c #fed4ae",
+".1f c #fed4b2",
+".1e c #fed5b7",
+".2X c #fedba9",
+".4F c #fee19f",
+".8v c #feea7f",
+"#Fv c #fefcfd",
+".f7 c #fefdfd",
+"QtI c #fefdfe",
+".oy c #fefdff",
+".bF c #fefefd",
+".cj c #fefefe",
+".bJ c #fefeff",
+"#lb c #feffff",
+".D6 c #ff64f9",
+".D5 c #ff64fa",
+".F3 c #ff73f7",
+".Il c #ff7ef4",
+".Ij c #ff7ef6",
+".Ik c #ff7ff6",
+".Kq c #ff87f0",
+".Kp c #ff88f3",
+".L5 c #ff91ec",
+".L4 c #ff91ee",
+".L6 c #ff92eb",
+".L3 c #ff92ef",
+".NJ c #ff9ae5",
+".NK c #ff9be4",
+".NI c #ff9be8",
+".NG c #ff9beb",
+".Pq c #ffa3e0",
+".Pp c #ffa4e2",
+".Po c #ffa4e4",
+".Pn c #ffa4e5",
+".Q3 c #ffabd9",
+".Q2 c #ffacdd",
+".Q1 c #ffadde",
+".Q0 c #ffade0",
+".QZ c #ffade3",
+".SO c #ffb1d3",
+".SN c #ffb3d7",
+".SM c #ffb3d9",
+".SL c #ffb3dc",
+"aBe c #ffb5b5",
+"aIV c #ffb6b6",
+"aFu c #ffb7b6",
+".Uz c #ffb9cc",
+".Ux c #ffb9d1",
+".Uv c #ffb9d6",
+".Uy c #ffbace",
+".Uw c #ffbad4",
+".Wc c #ffc1c6",
+".Wb c #ffc1c8",
+".Wa c #ffc1cc",
+".W# c #ffc1ce",
+".W. c #ffc1d2",
+".XY c #ffc8bd",
+".XW c #ffc8c5",
+".ZC c #ffceb5",
+".ZB c #ffceba",
+".ZA c #ffcebe",
+".2Y c #ffdaa4",
+".4G c #ffe099",
+".6F c #ffe68d",
+".xb c #fffdfd",
+"#yG c #fffdff",
+".cQ c #fffefe",
+".du c #fffeff",
+".j9 c #fffffe",
+"QtN c #ffffff",
+"Qt.Qt#QtaQtaQtaQtaQtaQtaQtbQtcQtdQtdQtdQtbQteQteQteQteQteQteQteQteQteQteQteQteQteQteQteQteQteQteQteQteQteQtfQtgQthQtdQtiQtjQtkQtlQtmQtnQtoQtnQtmQtmQtmQteQteQtpQtfQtqQtdQtdQtdQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtrQtsQttQtuQtvQtwQtxQtyQtzQtAQtBQtCQtDQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtFQtGQtHQtIQtIQtJQtKQtFQtLQtMQtNQtOQtPQtQQtRQtRQtRQtjQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtTQtTQtTQtTQtTQtjQtdQtdQtjQtjQtRQtRQtUQthQtbQtVQtkQtWQtWQtXQtXQtYQtYQtZQt0Qt0Qt1Qt2Qt3Qt4Qt5Qt6Qt6Qt7Qt7Qt8Qt9.#..##.#a.#b.#b.#c.#c.#d.#d.#e.#e.#e.#f.#f.#f.#f.#f.#f.#e.#e.#e.#d.#d.#c.#b.#g.#d.#h.#i.#j.#k.#l.#m.#n.#o.#p.#qQt4.#r.#r.#s.#t.#s.#u.#v.#u.#w.#x.#y.#z.#z.#z.#zQtSQtSQtSQtSQtRQtR.#A.#A.#A.#A.#A.#A.#y.#y.#B.#CQtpQtp.#CQtpQtp.#C.#C.#C.#CQtp.#C.#C.#C.#C.#C.#C.#C.#C.#CQtpQtp.#C.#CQtpQtpQteQteQteQteQtpQteQteQteQtfQteQteQteQtfQteQteQte.#D.#y.#A.#A.#A.#A.#A.#y.#yQtZ.#C.#C.#E.#F.#G.#H.#I.#J.#K.#L.#M.#L.#L.#L.#M.#L.#L.#L.#M.#K.#N.#O.#P.#F.#C.#Q.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#B.#C",
+"QtaQtaQtaQtaQtaQtaQtaQta.#RQtjQtjQti.#SQtf.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#P.#PQteQt0QtfQt0QtfQteQteQtfQtaQthQtiQtdQtdQtdQtiQthQtaQtfQtp.#CQtpQt2.#TQtpQtpQte.#B.#yQtiQtdQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtj.#U.#V.#W.#X.#Y.#Z.#0.#1.#2.#3.#4.#5.#6QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.#7.#8.#9.a..a#.aa.#6.abQtK.acQtN.ad.ae.afQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtRQtjQtjQtjQtjQtjQtjQtjQtTQtjQtTQtT.agQtT.ah.aiQtWQtWQtWQtXQtYQtYQtZQt0Qt0Qt1Qt2Qt3Qt4Qt5Qt6.ajQt7Qt7Qt9Qt9.##.##.#a.#b.#c.#d.#d.#e.#e.#f.#f.#f.ak.ak.ak.ak.ak.ak.ak.ak.#f.#f.#f.#e.#e.#d.#c.#b.al.#h.#i.#i.am.am.#n.an.ao.ap.aq.ar.#r.#r.#s.#s.#t.#u.#v.#x.#A.#z.#x.#x.#z.#zQtSQtSQtSQtSQtTQtS.#A.#A.#A.#A.#y.#y.#y.as.#Q.#C.#C.#C.at.#C.#C.#C.#C.#C.#C.#CQtp.#CQtp.#CQtpQtp.#C.#CQtpQtpQtpQtpQtpQtp.#CQtpQteQteQtpQteQteQteQteQteQteQteQteQteQtpQtpQt1.au.#y.#A.#A.#A.#A.#A.#y.avQtZ.#C.aw.ax.ay.#I.az.#L.aA.#L.#L.#L.#M.#L.#L.#M.#L.#L.#L.#L.#L.aB.aC.aD.#P.#CQte.aEQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aF.#T.#C",
+"QtaQtaQtaQtaQtaQtaQtaQta.#DQtdQtTQtj.#BQteQte.aGQteQteQteQte.aG.aHQte.aHQtn.aHQtlQtn.aHQtnQtmQte.aI.#yQtdQtdQtdQtjQtjQtjQtdQtd.aJ.#D.aK.aL.#PQteQte.aL.aI.aMQtjQtiQtdQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtj.aNQtdQtdQtd.aO.aP.aQ.aR.aS.aT.aU.aV.aW.aXQtN.aY.aZQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.a0.a1.a#.a#.a2.a3QtK.a4QtF.a5QtN.a6.a7.a8QtjQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtRQtjQtjQtjQtjQtjQtj.aNQtdQtjQtjQtRQtRQtSQtS.aE.#tQtWQtXQtXQtYQtYQtZQt0Qt1Qt2Qt3Qt4Qt4Qt6Qt6Qt7Qt8Qt9.#..##.#a.#b.#b.#c.#d.#e.#f.#f.ak.ak.a9.a9.a9.a9.a9.b..b..a9.a9.a9.a9.a9.ak.ak.#f.#f.#e.#b.ao.#h.b#.ba.#h.bb.#j.bc.an.ap.bd.be.bf.bg.#r.#s.#t.#u.#x.bh.#x.bi.#x.#x.#x.#z.#zQtSQtSQtSQtRQtS.#y.#y.#A.#A.#y.#y.#y.#y.#Q.at.#C.#C.#C.#C.#C.#CQtpQtp.#C.#C.#C.#CQtpQtp.#C.#C.#C.#C.#CQtp.#CQtp.#CQteQteQtpQteQteQtpQtpQteQtpQtpQtpQtpQtpQtpQtp.bj.#C.#P.#A.#y.#A.#A.#A.#A.#A.#y.#AQtf.bk.bl.bm.bn.bo.#M.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#M.bp.bq.#PQtp.#CQtp.aF.br.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#yQtVQtp.#C.#C",
+"QtaQtaQtaQtaQtaQtaQtaQtbQtSQtdQtdQtbQtlQto.aHQtnQtoQtnQtoQtoQtmQtlQtmQtmQtlQteQteQtp.bs.bsQtf.#w.btQtiQtdQtjQtjQtjQtjQtjQtjQtjQtdQtdQtj.bu.au.bi.#A.buQtjQtdQtdQtdQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtdQtjQtdQtdQtdQtdQtdQtdQtjQtjQtjQtTQtjQtd.bv.bw.bx.by.bz.bA.bB.bC.bD.bE.bF.bGQtKQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.#6.bI.bJ.a..bK.bLQtFQtF.bH.bMQtN.bN.#DQtrQtjQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtRQtjQtjQtj.aNQtjQtjQtjQtjQtRQtjQtRQtRQtSQtS.#z.#x.bO.bPQtYQtYQtZQt0Qt0Qt2Qt2Qt3Qt4Qt5Qt6Qt7Qt7Qt9.#..##.#a.#b.#c.#d.#d.#e.#f.ak.a9.a9.a9.b..b..bQ.bQ.bQ.bQ.bQ.bQ.bQ.bQ.b..b..a9.a9.a9.ak.#f.#d.#c.#i.bR.b#.ba.#h.bb.#j.an.an.#q.bS.ar.bT.#r.#r.#t.#v.bh.bU.bV.bi.#x.#x.#x.#z.#zQtSQtSQtSQtRQtS.#y.#y.#y.#A.#A.#y.#yQtS.aK.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQtpQtp.#CQtpQtp.#CQtp.#C.#C.#CQtpQtpQtpQtpQtpQtpQtp.#CQtp.#C.#C.#C.#C.bj.bj.bj.#CQtf.#AQth.#A.#A.#A.#A.#A.#y.ah.bW.bX.bY.bZ.#M.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#M.b0.b1.b2.b3.b4.#P.#C.#C.#T.aL.ahQth.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#yQtaQtp.#C.#C.#C",
+"QtaQtaQtaQtaQtaQtaQta.#RQtjQti.#y.bWQtlQtlQtmQtmQte.b5.bsQtp.b5Qte.#F.#FQteQteQtpQtpQtp.aI.buQtdQtd.agQtjQtjQtjQtjQtjQtjQtjQtjQtjQtdQtdQtdQtdQtdQtdQtdQtdQtdQtdQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtdQtdQtdQtdQtdQtdQtjQtdQtjQtjQtjQtTQtRQtRQtRQtRQtRQtRQtS.b6.b7.b8.b9.c..c#.ca.cb.cc.cd.ce.cf.cgQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.ch.ci.a..cj.ck.a0.bHQtF.cl.cmQtN.cn.coQtdQtTQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtRQtjQtjQtTQtjQtjQtjQtjQtTQtTQtjQtSQtS.#y.#z.#z.#w.cp.cqQtZQt0Qt0Qt1Qt2Qt3Qt4Qt5Qt6.ajQt7Qt9.#..##.#a.#b.#c.#d.#e.#f.ak.ak.a9.b..b..bQ.bQ.bQ.cr.cr.cr.cr.cr.cr.cr.cr.bQ.bQ.bQ.b..b..a9.ak.ak.#d.#f.b#.bR.b#.ba.#h.bb.#j.an.apQt5.cs.bf.bT.bT.#t.bV.#v.ct.bV.bi.bi.#x.#x.#x.#z.#zQtSQtSQtR.as.#A.#y.#y.#A.#A.#y.#y.#y.cu.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQtpQtp.#C.#C.#C.#C.#C.#CQtpQtpQtpQtp.#CQtpQtpQtpQtp.#C.#C.#C.#C.#C.bj.bj.#C.cv.#y.#y.#A.#A.#A.#A.#A.#y.cw.cx.#K.cy.#M.#L.#L.#L.#L.#L.#L.#L.#L.#L.#M.#M.cz.b0.cA.cB.cC.cD.cE.bW.#C.#C.#C.#CQtf.#D.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.buQtbQtp.#C.#C.#C.#C",
+"QtaQtaQtaQtaQtaQtaQta.#DQtdQtdQtkQtpQtp.#T.#TQtpQtpQtp.#TQtp.#FQtpQtpQtpQteQtpQtpQtf.#RQtdQtdQtdQtjQtjQtjQtjQtjQtjQtjQtjQtdQtdQtdQtdQtjQth.#y.#A.au.#A.#y.cFQtjQtdQtdQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtj.aN.aNQtdQtdQtdQtdQtdQtdQtdQtjQtjQtjQtjQtjQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtS.cG.cH.cI.cJ.cK.cL.cM.cN.cO.cP.cQ.cR.cS.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.#7.cT.cU.cV.a2.cWQtKQtE.cX.cY.cZ.bJ.c0.c1QtjQtTQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtTQtjQtjQtd.aNQtjQtjQtjQtRQtjQtjQthQth.#z.#z.#z.aE.c2.c3.cqQt0Qt1Qt2Qt3Qt4Qt5Qt6.ajQt7Qt8Qt9.##.#a.#b.#c.#d.#e.#f.ak.a9.a9.b..bQ.bQ.cr.c4.c4.c4.c4.c4.c4.c4.c4.c4.c4.c4.c5.bQ.b..b..a9.ak.ak.#f.#e.ba.c6.bR.b#.ba.#h.#j.bc.an.aq.c7.be.bf.#r.#t.#u.#u.ct.bV.bV.bi.#x.#x.#x.#z.#zQtSQtRQtRQtS.#A.#A.#y.#y.#A.#A.#A.#yQta.bj.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQtp.#CQtp.#C.#CQtp.#CQtpQtp.#CQtpQtp.#C.#C.#C.#C.#C.#C.#C.#C.c8Qth.#y.#A.#A.#A.#A.#AQth.au.c9.bp.#M.#L.#L.#L.#L.#L.#L.#L.#M.cz.b0.d..d#.da.db.dc.dd.de.df.#P.#C.#C.#C.#C.#CQte.aE.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQthQt.Qte.#C.#C.#C.#C.#C",
+"QtaQtaQtaQtaQtaQtaQtaQthQti.#SQte.#C.#C.#C.#C.#C.#C.#C.#CQtpQtpQtpQtpQtpQtpQtpQtWQthQtiQtdQtjQtjQtjQtjQtjQtjQtjQtdQtdQtdQtdQth.dg.dhQtb.bOQtaQtaQtaQtaQtbQtb.#R.ahQtjQtdQtdQtjQtjQtjQtjQtjQtjQtjQtjQtjQtjQtj.aNQtdQtdQtdQtdQtdQtdQtdQtTQtdQtjQtjQtjQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.di.dj.dk.dl.dm.dn.do.dp.dq.drQtN.ds.#6.abQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.a0.dt.cj.du.dv.#6.bHQtE.#6.dw.dx.dy.dz.dAQtjQtTQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtRQtjQtjQtjQtjQtjQtjQtd.dB.dCQtiQtSQth.#x.#z.dD.bi.dE.#v.#s.dFQt1Qt2Qt3Qt3.dGQt5.dH.dI.dJ.dK.dL.ap.#c.#d.dM.ak.a9.a9.#h.b..bQ.cr.cr.c4.c4.dN.dN.dO.dO.c4.c4.c4.c4.c4.c4.cr.c4.cr.cr.bQ.dP.dQ.dQ.dR.#h.c6.c6.bR.dS.ba.#i.#j.bc.dT.dU.be.bg.bP.dV.c3.dW.#u.ct.dX.bi.dY.#x.#x.#z.#z.#zQtTQtR.#y.#A.#A.#y.#y.#A.#y.#y.#y.dZQt2.#C.#C.#C.#C.#C.#C.#CQteQtf.cqQtW.#QQtWQtW.cvQtfQtp.#C.#C.#C.#C.#CQtp.#CQtpQtpQtpQtp.#CQtpQtp.#C.#C.#C.#C.#C.#C.#C.#C.#CQtV.d0.#A.#A.#A.#A.#A.#A.d1.d2.b0.d3.#M.#M.#M.d4.d4.d4.d5.d6.d6.d7.d8.d9.e..e#.ea.eb.ec.edQtp.#C.#C.#C.#C.#C.#CQte.dh.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQth.dhQte.#C.#C.#C.#C.#C.#C",
+"QtaQtaQtaQtaQtaQtaQtbQtj.aN.#BQtp.#C.#C.#C.#C.#C.#C.#C.#CQtpQtpQtpQtpQtpQteQtk.btQtiQtdQtjQtjQtjQtjQtjQtjQtdQtdQtd.ee.#A.#RQtb.bOQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQt#.#R.buQtdQtdQtdQtjQtjQtdQtdQtdQtdQtdQtdQtjQtjQtjQtjQtTQtjQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.ef.eg.eh.ei.ej.ek.el.bC.em.enQtN.eo.a0.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.cX.ep.dx.a..a2.eq.cXQtE.er.a0.es.cj.et.a7.coQtjQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtjQtj.aNQtjQtiQtQ.#A.eu.ev.ew.ex.#A.#x.#y.bi.bi.#v.#v.ctQtW.aw.ey.ey.ez.eA.eB.eC.eD.eE.eF.eG.#a.#d.dM.ak.a9.a9.b..bQ.eH.bQ.cr.cr.c4.c4.c4.eI.eI.eJ.eK.eI.eI.eI.eI.eI.eL.eJ.eL.eM.eN.eO.eO.eP.dQ.#h.c6.eQ.c6.bR.ba.eR.bb.#j.eS.dU.eT.arQtX.bP.dV.c3.dW.ct.#v.bi.bi.#x.#x.eU.#z.#zQtRQth.#z.#A.#A.#A.#A.#A.#y.#y.#y.dZ.#C.#C.at.at.at.#CQte.c8QtV.bO.bOQtVQtVQtVQtVQtaQtaQtV.cqQtp.#C.#C.#CQtp.#CQtpQtpQtpQtp.#CQtp.bj.#C.#CQtpQtpQtpQtpQtpQtpQteQtb.#y.#A.#A.#A.#A.#A.#y.aN.eV.cy.b0.#M.#M.#M.#M.#M.d5.d6.eW.eX.eY.eZ.e0.e1.e2.e3.ed.#P.#C.#C.#C.#C.#C.#C.#C.#CQte.au.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQth.aEQt0.#C.#C.#C.#C.#C.#C.#C",
+"QtaQtaQtaQtaQtaQta.#RQtd.#D.#F.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQtmQteQteQteQtWQtjQtiQtdQtjQtjQtjQtjQtjQtdQtdQtdQth.aEQtb.bOQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtb.#yQtdQtdQtdQtjQtjQtTQtjQtjQtjQtRQtRQtRQtRQtR.e4QtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtRQtRQtRQtRQtRQtRQtS.e5.e6.e7.e8.e9.f..f#.fa.fbQtN.fc.a0.fdQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK.dsQtI.bJ.fe.#6.erQtE.bH.ff.ckQtN.fg.c1QtTQtTQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtjQtT.ef.fh.fi.fj.fk.fl.fm.fn.fo.#z.#A.#x.bi.dE.aE.as.fp.fq.fr.fs.ft.fu.fv.fw.fx.fy.fz.fA.fB.fC.#d.dM.ak.a9.a9.b..cr.c4.c4.eN.dN.eI.fD.fE.fE.fE.fF.fG.fG.fF.fF.fF.fF.fE.fE.fE.eI.eM.dN.eN.eO.dP.a9.c6.fH.fI.bR.dS.ba.#h.bb.fJQt3.fK.fLQtXQtX.bP.dV.dW.#u.ct.dX.bi.dY.#x.eU.#z.#zQtTQtR.#y.#A.#A.#A.#A.#A.#y.#y.#y.cu.#C.#C.#C.#CQt2.cqQta.bOQtV.fM.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bOQtaQtf.#C.#CQtp.#CQtpQtpQtpQtpQtpQtp.#C.#C.#CQtpQtpQteQteQteQteQte.aE.#y.#A.#A.#A.#A.#A.cw.fN.fO.#M.#M.#M.#M.#M.b0.fP.fQ.fR.eY.fS.fT.fU.fV.fW.fX.fY.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQtf.#SQth.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aEQt0.#C.#C.#C.#C.#C.#C.#C.#C",
+"QtaQtaQtaQtaQtaQta.#SQtjQtY.#C.#C.bsQtpQtpQtpQtpQtpQteQte.#P.#P.#P.#P.cq.fZQtiQtdQtjQtjQtjQtjQtdQtdQtdQth.aEQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQthQtTQtRQtRQtSQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtRQtRQtRQtRQtRQtRQtjQtRQtRQtRQtRQtRQtdQt3.f0.f1.f2.e8.f3.f4.aW.f5.f6.f7.f8.aZQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.a0.f9.bJQtI.g.QtK.bHQtEQtE.bH.cS.bK.g#.ga.gbQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.gc.gd.co.ge.gf.gg.gh.gi.gj.gk.gl.gm.#z.#z.as.gn.as.go.gp.gq.gr.gs.gt.gu.gv.gw.gx.gy.gz.gA.gB.gC.gD.#c.gE.a9.b..eP.eO.eN.eN.gF.gG.fE.fF.fF.fF.gH.fG.fG.gH.gH.fG.gH.gH.fG.fF.fF.fE.fE.eI.eL.eL.eN.eO.dQ.gI.gJ.eQ.c6.bR.dS.ba.#i.gKQt4.gL.cs.cs.gMQtX.dV.c3.#u.ct.dX.bi.dY.#x.#x.eU.#zQtSQtS.#y.#A.#A.#y.#y.#A.#y.#yQtSQtk.#C.#C.#CQteQtkQtbQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtaQtaQtZ.#CQtp.#CQtpQtpQtpQteQtp.#C.#C.#C.#C.#C.#CQtpQteQteQteQtf.#AQth.#A.#A.#A.#A.#y.cw.gN.bp.b0.#M.#M.#M.cA.gO.gP.gQ.gR.fT.gS.gT.gU.gV.gVQtp.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.cv.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#AQtZ.#C.#C.#C.#C.#C.#C.#C.#C.#C",
+"QtaQtaQtaQtaQtaQtbQth.aEQte.#FQteQte.#P.gW.#P.gW.gWQtf.gWQtf.gWQtl.cv.buQtiQtdQtjQtjQtjQtjQtdQtdQth.aEQtbQtgQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQta.dh.agQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtRQtRQtRQtRQtRQtRQtjQtRQtRQtRQtRQtRQtRQtRQtR.e4QtSQtSQtS.b6.eP.gX.gY.gZ.g0.g1.g2.g3.g4.cj.cU.g5QtKQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.#7.es.a..g6.g7.g8QtFQtEQtEQtE.g9.h.QtN.h#.bi.haQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.hbQtQ.co.bQ.hc.hd.he.hf.hg.hh.hi.hj.hk.hl.hm.ee.hn.ho.hp.hq.hr.hs.ht.hu.hv.hw.hx.hy.hy.hz.hA.hB.hC.hD.hE.hF.hG.dP.eP.eO.eN.eM.eL.gF.fE.fF.fF.gH.gH.hH.hI.hH.hH.hJ.hH.hH.hH.hI.hI.gH.gH.fF.fF.fE.eI.eL.eL.eN.eP.bQ.gJ.fH.eQ.c6.dS.ba.eR.hKQt3.gL.fK.fi.fLQtX.bP.dV.dW.hL.#v.bi.bi.#x.#x.eU.#zQtS.#y.#y.#A.#A.#y.#y.#A.#A.#y.asQtW.#C.atQtfQta.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtaQt0.#C.#CQtpQtpQteQte.#C.#C.#C.#C.#C.#C.#C.#C.#CQtpQte.cq.#y.#y.#A.#A.#A.#A.cF.hM.hN.b1.d5.d4.d4.hO.hP.hQ.hR.fS.hS.hT.hU.ed.fYQtp.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQtp.aI.d0.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQth.#A.aL.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C",
+"QtaQtaQtaQtaQta.#wQth.aI.#P.#P.gW.gWQtfQtfQtfQtl.aG.aH.aG.aGQte.aL.ahQtiQtdQtjQtjQtjQtdQtdQtj.#SQt.QtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQt#.#SQtTQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.e4QtRQtRQtRQtRQtRQtRQtRQtRQtRQtjQtjQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.a8.ga.hV.hW.hX.ej.hY.hZ.h0.h1QtN.a#.h2.aZQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.h3.h3.h3.h3.h3.h3.h3.h3QtEQtEQtEQtEQtEQtK.h2QtI.h4.h5.h6.h7QtEQtEQtEQtK.f8.hVQtN.h8.dAQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.co.#U.h9.i..i#.ia.ib.ic.id.ie.if.ig.ih.ii.ij.ik.il.im.in.io.ip.iq.ir.is.it.iu.iv.iw.iv.iv.ix.iy.iz.iA.iB.iC.iD.iE.iF.dQ.eP.eO.eM.eL.gF.fE.fF.fF.gH.hI.hH.hH.hJ.hJ.hJ.hJ.hJ.hJ.hJ.hJ.hH.hH.hI.fG.fG.fF.fE.eI.eL.eL.dP.dP.fH.gJ.fH.fI.gI.dS.bb.iG.iH.bv.bv.gL.cs.gM.iI.dV.dW.#u.ct.dX.bi.dY.#x.eU.#zQtR.#y.#A.#A.#A.#y.#y.#A.#AQth.#y.#B.#CQtZQtaQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bOQtVQteQtp.#CQtpQteQtp.#C.#C.#C.#C.#C.#C.#C.#C.#CQtpQtpQtW.as.#y.#A.#A.#A.#A.cw.iJ.iK.d4.d4.iL.hO.iM.iN.eZ.iO.iP.iQ.fX.gVQtp.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#FQtbQtS.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.d0.#AQtZ.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C",
+"QtaQtaQtaQtaQta.#D.#SQtf.aH.aH.aG.aGQteQteQteQteQteQteQteQteQtf.#DQtiQtdQtdQtdQtjQtdQtd.bu.dhQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtjQtRQtSQtSQtSQtSQtSQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.af.iRQtN.iS.iT.iU.iV.iW.iX.iYQtN.cj.iZ.a0.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.h3.bH.g9.i0.i1.i2.i3.i4.i5.i6.i7.i8.i9.j.QtKQtEQtE.bH.bH.j#.ja.bJ.jb.cgQtFQtEQtEQtE.a4.cg.jcQtN.jd.#v.hbQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.ef.ag.je.jf.jg.jh.ji.jj.jk.es.jl.jm.hh.jn.jo.jp.jq.jr.js.jt.ju.jv.jw.jx.jy.fP.jz.jA.jB.jC.jD.jE.jF.jG.jH.jI.jJ.jK.jL.jM.jN.jO.eO.eN.eM.eI.fE.fF.fF.gH.hH.hH.hJ.hJ.hJ.jP.hJ.jP.jP.jP.jP.hJ.hJ.hJ.hH.hI.gH.fF.fF.fE.fD.eL.eO.bQ.gJ.gJ.fH.eQ.gI.dS.bb.jQ.jR.jR.bv.gL.fi.fLQtX.dV.c3.#u.ct.dX.bi.dY.#x.eUQtSQtSQth.#y.#y.#A.#A.#A.#A.#A.#y.#A.cv.aL.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.#BQtp.#CQteQte.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQte.aEQth.#y.#A.#A.#A.as.jS.jT.jU.iL.jV.eW.jW.gR.jX.jY.jZ.j0Qtp.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#P.#A.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#A.aL.#C.#C.#C.#C.#C.#C.#C.#C.#CQtpQtpQtp",
+"QtaQtaQtaQtaQta.#SQtaQteQteQteQteQteQteQteQteQteQteQteQteQtf.#RQtdQtdQtjQtjQtRQtTQtj.#AQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtb.#w.#AQtjQtRQtRQtRQtRQtRQtjQtjQtjQtRQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.gb.j1QtN.j2.j3.j4.j5.j6.j7.j8.j9.a..k..k#QtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.h3.bH.ka.i4.kb.kc.kd.ke.kf.kg.kg.kg.kg.kg.kg.kg.kg.kh.ki.kj.kk.h3QtEQtF.kl.km.kn.#7.bHQtEQtEQtE.koQtK.kpQtN.kq.ga.#UQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtTQtQ.#z.kr.ks.kt.ku.kv.bG.cl.kwQtKQtD.dw.kx.ky.kz.kA.kB.kC.kD.kE.kF.kG.kH.kI.kJ.kK.kL.kM.kN.kO.kP.kQ.kR.kS.kT.kU.kV.kW.iC.kX.kY.kZ.k0.eO.eL.eI.fE.fF.fF.gH.hI.hH.hJ.hJ.jP.jP.jP.k1.k1.jP.jP.jP.jP.jP.hJ.hJ.hH.hI.gH.fF.fF.fE.fD.eN.eP.c6.gJ.gJ.eQ.fI.bR.k2.k3.a7.k4.k5.fK.fi.fLQtX.bP.c3.dW.hL.#v.bi.dY.#x.#xQtS.#z.#A.#y.#y.#A.#A.#A.#A.#AQtS.k6.aFQtaQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.#P.#CQtpQtp.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.cq.#y.#y.#A.#A.#A.#z.k7.k8.#K.k9.l..l#.la.lb.lcQtn.aw.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.cq.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.cq.ld.#CQtpQtpQtpQtpQtpQtp.#C.#CQtp.#C.#C",
+"QtaQtaQtaQtaQtb.dh.#BQteQteQteQteQteQteQteQteQteQteQteQte.cuQtjQtRQtSQtSQtRQtTQth.dhQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQt#.#RQtcQtTQtjQtjQtjQtRQtRQtRQtRQtRQtR.e4QtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.dA.leQtN.lf.lg.lh.li.lj.lk.ll.cQ.lm.a#.h2.aZQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3QtE.a0.ln.lo.lp.lq.kg.lr.kg.kf.kf.kf.kf.kf.kf.kf.kf.ls.kf.kf.kf.kf.lt.lu.lvQtEQtEQtF.lwQtG.abQtEQtEQtEQtEQtE.#6.lx.bJQtN.ly.lzQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.#U.#y.lA.lB.lC.lD.lE.lF.aZ.cX.bH.lGQtEQtK.cW.lH.lI.lJ.lK.lL.lM.lN.lO.lP.lQ.lR.lS.lT.lU.lV.lW.lX.lY.lZ.km.l0.ig.ig.l1.l2.l3.l4.l5.l6.l7.l8Qts.eL.fE.fE.fF.gH.hH.hH.hJ.hJ.jP.jP.l9.l9.l9.m..m..m..m..m..jP.jP.jP.hJ.hH.hI.gH.fF.fE.fD.eL.eO.dP.gI.fH.fH.eQ.dS.gK.k3.m#.k4.k5.bv.gL.ma.gM.iI.dV.mb.hL.#v.bi.bi.#x.#x.#z.#y.aE.#y.#A.#A.#A.#A.#A.#y.d0.dh.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO.c8.#TQtpQtp.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQtp.#wQth.#y.#A.#A.fp.mc.bY.b0.md.me.mf.mg.mh.dfQte.#C.#C.#C.#C.#C.bS.bSQtp.bS.#C.#C.#C.#C.#C.#C.#C.#C.#CQtpQtaQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.cq.#CQtp.#C.#CQtp.#C.#C.#C.#C.#C.#C.#C.at.at",
+"QtaQtaQtaQta.miQtb.aLQteQteQteQteQteQteQteQteQteQteQte.c8QtRQtRQtSQtSQtRQtTQth.#w.bOQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtb.#AQtjQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.mj.mkQtN.ml.mm.mn.e8.mo.mp.mq.f7.lmQtI.g.QtK.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3QtE.mr.ms.mt.mu.kg.lr.kg.kf.mv.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.mw.i1QtEQtEQtF.a4QtE.h3.h3QtE.bHQtEQtK.mx.myQtN.mzQtdQtTQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.dB.dB.mA.mB.lC.mC.mD.bH.aZ.a4QtEQtEQtEQtE.cX.bL.mE.mF.mG.mH.mI.mJ.mK.mL.mL.mM.mN.mO.mP.g6.mQ.mR.kq.mS.mT.#9.mU.mV.aa.mW.mX.mY.mZ.jJ.m0.m1.m2.m3.m4.eI.fE.fF.gH.hH.hH.hJ.hJ.jP.k1.l9.l9.m5.m5.m5.m5.l9.m5.m..m..m..jP.hJ.hJ.hH.hH.gH.m6.fE.eL.eL.cr.m7.c6.fH.eQ.baQt8.k3.m8.a7.k4.dU.fK.ma.gM.iI.m9.mb.#u.ct.#v.bi.dY.#x.#z.#y.#A.#A.#A.#A.#A.#A.#A.#y.dgQtbQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.cp.cu.#PQtp.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.cq.#y.#y.#A.#y.n..n#.na.nb.nc.nd.ne.nf.j0Qtp.#C.#C.bSQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtp.bS.#C.#C.#C.#CQte.#DQth.#A.#A.#A.#A.#A.#y.cw.ng.hm.hm.nh.hm.ng.c8.#C.#C.#C.#C.at.#C.at.at.at.at.at.at.at.at.at",
+"QtaQtaQtaQtaQtb.#R.aLQteQteQteQteQteQteQteQteQteQte.aLQthQtTQtSQtSQtRQtT.ahQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQta.#RQthQtjQtRQtSQtSQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.ni.nj.e5QtNQtI.nk.nl.nm.nn.no.np.cV.a#.a#.mT.#8QtKQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.bH.h3.nq.i2.kj.nr.ns.kg.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.kf.kf.kf.mw.mw.mw.kf.mw.mw.ns.kjQtEQtEQtE.ko.nt.lv.i6.ka.h3QtEQtF.h6.a2.bJ.nu.nv.dBQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.hbQtQ.bQ.nw.ci.nx.#8.cXQtK.bHQtEQtEQtEQtEQtEQtKQtG.ny.nz.nA.nB.nC.nD.nE.nF.nG.nH.nI.nJ.nK.cU.cU.mR.nL.nM.nN.gX.nO.nPQtF.#6.nQ.nR.nS.nT.jJ.nU.nV.nW.nX.nY.gF.fE.fF.gH.hH.hJ.nZ.k1.m..m..l9.l9.m5.m5.m5.m5.m5.m5.l9.m..l9.k1.k1.nZ.hJ.hH.gH.fF.m6.fD.eL.eN.bQ.b..c6.eM.ao.n0.n0.n1.n2.iH.jR.fK.fi.fL.n3.m9.c3.#u.ct.#v.bi.bi.#xQtS.#y.#A.#y.#A.#A.#A.#A.#y.#yQtbQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.cqQtp.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQte.#DQth.#A.aN.n4.n5.iK.n6.fS.n7.n8.dfQtp.bSQt2QtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtmQtp.#C.cq.#yQth.#y.#A.#y.cF.n9.o..o#.oa.ob.oc.od.oe.of.og.#F.at.at.at.at.at.at.at.at.at.at.at.at.at.at",
+"QtaQtaQtaQta.#RQt#QtfQteQteQteQteQteQteQteQteQteQtf.auQtTQtR.e4QtRQtU.ahQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQt..#yQtjQtjQtRQtRQtRQtRQtRQtjQtRQthQthQtSQthQthQtRQtjQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtT.#v.ohQtNQtI.oi.oj.ok.ol.om.on.oo.a#.lm.bJ.op.a0.bHQtEQtEQtEQtEQtEQtE.bHQtFQtK.bHQtE.oq.or.kg.os.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.kf.kf.kf.kf.mw.mw.kf.mw.mw.mw.kf.kf.mw.kf.kg.lp.bH.fd.bHQtK.ot.ns.ns.ou.ovQtEQtF.ow.ox.oy.oz.oA.cGQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.mj.dU.oB.oC.lD.oD.#6QtK.bHQtEQtEQtEQtEQtEQtE.kw.oE.oF.nz.oG.oH.oI.oJ.oK.oL.oM.hV.cj.oN.cU.cU.cU.cU.nM.mT.a2.oO.oE.oP.ch.bL.ch.ch.oQ.oR.oS.iA.oT.oU.nW.oV.m4.eL.fF.fF.hH.hH.hJ.nZ.k1.m..l9.l9.oW.m5.m5.m5.m5.m5.m5.oW.l9.l9.l9.k1.nZ.nZ.hH.gH.oX.m6.fD.eL.eN.cr.dQ.dP.m7.hK.oY.jQ.n0.oZ.k4.k5.o0.c7.ar.n3.bP.c3.#u.ct.#v.bi.bi.#z.o1.#A.aE.#A.#A.#A.#A.#y.#y.dhQta.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtaQtkQte.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#CQtpQt#.#y.#y.n9.o2.d3.o3.o4.o5.o6.o7.bSQtpQtpQtpQtpQtpQtpQtp.o8.o8Qtm.aG.aG.aG.aG.aG.aGQtpQteQtpQtp.o8Qt..n..aE.#R.ng.o9.p..p#.pa.pb.pc.pd.pe.pe.pd.pe.pf.bW.#C.at.at.at.at.at.at.at.at.at.at.at.at.at",
+"QtaQtaQtaQtb.#RQtWQteQteQteQteQteQteQteQteQteQtf.#vQtTQtRQtRQtjQtj.#AQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtb.miQtbQtaQtaQtb.#AQtRQtjQtRQtSQthQtS.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#yQtSQthQtRQtRQtTQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.hb.h9.pgQtN.a#.ph.pi.pj.pk.pl.pm.pn.a#.a#.a..po.bH.bHQtEQtEQtEQtEQtEQtE.bHQtFQtE.ka.pp.kf.kg.kf.mw.mw.mw.mw.mw.mw.mw.kf.mw.kf.kf.kf.kf.kf.mw.ke.kh.ke.kh.pq.pq.pq.pr.kh.mw.mw.mw.kf.ps.ptQtEQtE.kk.mw.pu.mw.kg.kf.pvQtE.cg.ox.a..pw.px.cGQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.#U.#x.py.pz.pA.pB.pC.f8.cX.bHQtEQtEQtEQtEQtEQtE.aZ.lw.pD.pE.pF.pG.pH.pI.pJ.pK.pL.dy.hV.my.cU.kq.cU.kq.mT.lm.k..pMQtF.ko.ch.ch.bL.ch.pN.op.pO.pP.pQ.pR.pS.pT.pU.pV.eL.fF.gH.hH.hJ.nZ.jP.m..pW.l9.oW.oW.m5.pX.pX.pX.pX.m5.m5.m5.m5.l9.m..k1.nZ.hH.gH.oX.fF.fD.eL.dN.c4.dP.a9.#d.pY.pY.pZ.n0.k3.a7.jR.o0.c7.ma.gM.bP.c3.dW.#u.#v.bi.bi.#z.#z.#A.#A.#A.#A.#A.#y.#y.#D.bO.#s.#s.#s.#s.#s.#s.#Q.#s.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtVQtV.bO.cu.#P.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.#C.ldQtWQthQthQth.p0.p1.p2.p3.p4.p5QtpQtpQtpQteQteQteQte.aG.aHQteQteQte.dF.aG.aG.aGQteQte.o8.o8Qtp.bW.#Q.p6.p7.e2.p8.p9.q..q#.qa.qb.mf.qc.qc.qd.qe.qe.qc.qc.qf.qg.#C.at.at.at.at.at.at.at.at.at.at.at.at",
+"QtaQtaQta.c2Qt.QtZQteQteQteQteQteQteQteQteQteQtkQtTQtTQtRQtTQtj.#DQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQta.miQtbQtbQtaQtb.bO.bO.#R.#yQthQtS.#y.#S.#A.#A.#A.#A.#A.#A.#A.qh.#y.#y.#y.#y.#y.#y.#yQtS.#y.#y.#y.#y.#y.#yQtSQtRQtjQtRQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.b6.a9.hhQtN.lf.qi.cX.qj.qk.ql.qm.cV.a#.a#.a#.jc.g8QtFQtEQtEQtEQtEQtE.bHQtEQtE.qn.ps.ns.kf.mw.mw.mw.mw.mw.mw.mw.kf.kf.kf.kf.kf.mw.pr.nr.kd.qo.qp.qp.lu.lu.lu.lu.lu.qp.mu.ou.ou.pq.ke.kg.qq.k#.h3.i3.kg.kf.mw.mw.kg.lp.qr.oD.cV.a#.dx.qs.qtQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtT.ef.qu.qv.qw.kR.if.qx.qyQtK.bHQtEQtEQtEQtEQtEQtE.aZ.oE.pD.qz.if.pF.qA.qB.qC.qD.mx.lF.g8.qE.qF.mS.lm.cU.oo.cU.qG.oEQtK.lG.ch.ch.bL.bL.g8.qH.qI.qJ.qK.qL.pR.qM.qN.qO.qP.qQ.fF.gH.hH.hJ.nZ.m..m..m5.m5.m5.oW.oW.pX.pX.pX.pX.oW.m5.oW.oW.m5.m..m..nZ.oA.gH.qR.m6.fE.eI.dN.qS.eP.dQ.#e.qT.oY.oY.qU.k3.a7.k5.bv.c7.maQtX.bP.c3.mb.#u.#v.bi.bi.#z.#z.#A.#A.#A.#A.#A.#y.#yQt..#tQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.#s.#s.#s.#s.#s.#Q.#QQtVQtVQtVQtVQtVQtVQtaQtaQtaQtVQta.bO.bW.#C.#C.#C.#C.#C.#C.#C.#C.bSQtpQtpQtp.bSQtpQtpQtpQtY.#y.#y.#A.qV.qW.qX.qY.qZQteQteQteQteQtp.o8Qtm.aHQte.aG.aG.aH.dF.aH.aG.aG.aGQtmQtm.o8Qte.fX.q0.q1.q2.qa.ht.q3.q4.fT.q5.q6.qc.qc.qc.qc.qc.qc.qc.qe.q7.q8.q9.#F.at.at.at.at.at.at.at.at.at.at.#CQtf",
+"QtaQtaQta.#R.bOQtfQteQteQteQteQteQteQteQte.cvQthQtRQtRQtRQtj.#DQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQtbQtb.bO.bOQtaQtVQtb.au.#y.#y.#A.#A.#A.#S.#A.ah.#y.dg.r..k6.k6.k6.#D.#D.aE.#D.dg.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#yQthQtRQtjQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.co.hH.jbQtN.r#.#6.cS.ra.rb.rc.rd.cV.a#.a#.a..re.k#QtFQtEQtEQtEQtEQtE.h3.g9.rf.kg.kg.mw.mw.mw.mw.mw.mw.kf.kf.kf.kf.kf.mw.ke.ps.mu.lu.rg.ki.rh.rh.ri.rj.rj.rj.rj.ri.rh.rh.ki.rk.qp.rl.ke.i7.h3.h3.rm.os.kf.mw.mw.kf.os.rn.ro.du.lm.rp.rq.#UQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtT.nj.rr.ig.rs.rt.if.pE.ru.chQtFQtEQtEQtEQtEQtEQtE.aZ.rv.kn.if.j2.rw.if.rx.ry.rz.g9QtK.a4.bH.#7.g9.fc.qw.dx.kx.h2.cS.rA.h3.ch.ch.ch.g8.bL.j#.k#.rB.hf.rC.rD.oT.rE.rF.rG.rHQts.fF.gH.hH.hJ.nZ.m..pW.m5.m5.oW.oW.oW.pX.pX.pX.pX.oW.oW.oW.oW.m5.pW.m..nZ.oA.hI.fG.fG.fE.eI.dN.eN.eP.b..gE.#g.pZ.rI.rJ.k3Qt4.k5.bv.gL.fLQtX.bP.m9.#u.#u.#v.bi.bi.o1.#A.aE.#A.#A.#A.#A.d0.#D.bOQtVQtVQtVQtVQtVQtVQtVQtVQtV.bO.bO.#t.#t.#t.#t.#tQtV.#t.#tQtV.#t.#t.#t.#tQtV.#t.#Q.#sQtVQtaQtYQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQtpQteQte.aL.rKQtS.rL.rM.rN.rO.j0Qt1.o8.o8.o8QtmQtmQte.o8Qtm.aH.dF.aH.fq.fq.dF.rP.dF.dF.fq.aG.#BQtm.rQ.rR.rS.rT.rU.rV.rW.rW.rX.qc.qc.qc.qc.qc.qc.qc.qc.qc.qe.rY.rZ.#P.#T.at.at.at.at.at.at.at.atQtp.aLQt..#y",
+"QtaQtaQta.#RQtWQteQteQteQteQteQteQteQteQtf.#DQtT.e4QtRQtj.aE.bOQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQtbQtbQtaQtaQtVQtVQtVQt..#y.#y.#y.#S.#A.ah.#A.#D.aE.#R.#R.#w.#R.dh.#R.#R.#R.#R.#R.#R.dh.dh.#A.#A.dg.dg.#y.#A.#y.#yQtSQtS.#y.#y.#y.#yQtRQtjQtRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.cG.r0.cU.r1.g8QtF.ko.r2.r3.r4.r5.pn.a#.lmQtI.kp.#7.bHQtEQtEQtEQtEQtE.ln.lu.ns.kf.mw.mw.mw.mw.mw.kf.kf.kf.kf.mw.mw.ps.qo.lu.ki.rh.lp.r6.rn.r7.r7.r7.r8.mt.r8.r7.r8.r7.rn.kc.rj.ri.ki.qp.oq.h3.k#.kb.os.kf.kf.mw.mw.kf.nr.kx.cj.a..r9.s..dBQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtTQth.s#.jc.sa.if.rt.pE.ru.epQtK.bHQtEQtEQtEQtEQtE.aZ.sb.sc.sd.se.j2.bM.h2QtE.g9QtK.fdQtEQtEQtE.bH.bHQtK.lw.sf.cS.#7.ko.cl.h3.ch.ch.ch.ch.g8.j#.chQtG.r1.sg.sh.si.sj.sk.sl.sm.sn.m6.gH.hH.nZ.k1.m..pW.m5.oW.oW.oW.pX.pX.pX.pX.pX.pX.so.oW.m5.m5.m..m..nZ.oA.hI.gH.fF.fE.eI.eL.eO.eP.dP.dR.sp.pY.rJ.qU.m8.n2.iH.fK.fi.fLQtX.bP.dV.dW.#u.#v.bi.bi.#z.#A.#A.aE.#A.#A.#yQthQtb.sq.bO.bO.bO.bO.bO.bO.bO.bO.bO.bO.bO.ai.bO.bO.bO.bO.bO.bOQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtb.c8QtpQtpQtpQtpQtpQteQteQteQteQteQteQteQteQte.o8.o8Qtf.#SQtS.ng.sr.ss.stQt1.bsQtpQtpQtp.o8.o8Qtm.aG.aG.fq.rP.su.sv.sv.sv.sw.sv.sv.sx.aK.sy.sz.sA.sB.sC.sD.sE.rU.rV.sF.rX.rX.qc.qc.qc.qc.qc.qc.qc.qe.iO.sG.sH.aw.at.at.at.at.at.at.atQtp.c8.dh.#y.#y.#y",
+"QtaQtaQtbQtb.aLQteQteQteQteQteQteQte.#PQtaQtjQtRQtRQtj.au.bOQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQtb.bO.bOQtaQtVQtk.#Q.cu.aE.#y.#y.#A.#A.dh.#w.#RQtbQtbQtbQtaQtbQta.aiQtbQta.bOQtbQtbQt.QtbQtb.#w.#wQtbQtb.#w.dh.aE.#A.#y.dg.dg.#y.#yQtS.#y.#y.#yQtRQtjQtj.e4QtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.af.sIQtN.ro.a0.bHQtE.sJ.sK.sL.gi.g6.a#.lmQtI.qI.a0QtEQtE.bHQtE.bH.sM.kf.kg.mw.mw.mw.mw.mw.kf.kf.kf.kf.mw.kh.ou.mu.rk.rh.rj.rn.r7.r8.sN.sN.sN.sO.sO.sO.sO.sO.sO.sO.sN.sN.mt.r8.rn.rj.sP.sQQtE.bH.mt.kg.kf.kf.kf.mw.kg.mt.sR.a.QtN.sSQtdQtTQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.cG.sT.j2.sa.if.pE.jm.sU.lFQtKQtEQtEQtEQtEQtE.bHQtK.sV.sW.sX.lD.sY.h2.cl.a0QtF.bH.bHQtEQtEQtEQtE.bHQtEQtE.abQtF.bHQtE.k#.h3.k#.cl.ch.ch.g8.g8.j#.g8QtE.sW.sZ.s0.s1.s2.s3.s4.s5.s6.m6.gH.hH.nZ.nZ.m..pW.m5.oW.oW.so.pX.pX.pX.pX.pX.pX.pX.so.m5.m5.m..jP.jP.hJ.hH.gH.fF.fE.eI.eN.eO.cr.dS.a9.#d.qT.n0.qU.k3.a7.iH.gL.fi.ma.gM.bP.dV.mb.#u.#v.bi.#x.eU.aE.#A.aE.#A.#AQth.#zQt..bOQtbQtb.bO.bO.bO.bO.bO.bO.bOQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.#t.#t.#t.#t.#t.#t.#t.#v.s7QtmQtm.aH.aHQtmQtmQtm.aHQtmQtmQtmQtmQtmQtmQtm.bs.dF.ahQtj.s8.s9.t.QtfQtp.o8.o8.o8QtmQtm.aG.aG.fq.su.sv.sv.t#.ta.tb.tc.tc.tb.tc.td.te.tf.tg.th.ti.tj.sC.sD.tk.rU.sF.rW.rX.rX.qc.qc.qc.qc.qc.rX.qc.rV.tl.df.#C.at.at.at.at.at.#C.cq.dh.#y.#y.#y.#A.#A",
+"QtaQtaQtbQtb.aLQteQteQteQteQteQteQte.c8.eeQtRQtRQtT.ahQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQtb.aiQtaQtk.aIQtWQtW.aF.ai.#A.dg.au.dhQtb.bOQta.bO.bOQtaQtaQtkQtkQtkQtk.aI.aI.#BQtW.c8.#B.cv.cq.#B.#BQtW.aI.aIQtkQtVQtVQtaQt..tm.dh.#R.aE.#A.ah.#yQth.#yQthQtRQtjQtjQtRQtRQtRQtRQtRQtRQtRQtSQtSQtSQtSQtSQtSQtS.tn.toQtN.ro.aZ.cSQtE.pN.tp.tq.rd.#4.a#.lm.lm.mD.aZQtEQtEQtE.kw.tr.ns.kf.mw.mw.mw.mw.mw.kf.kf.kf.ts.ke.ps.qp.ki.ri.tt.r7.mt.tu.kj.sO.tv.tv.tv.tv.tw.tw.tw.tv.tv.tv.tv.tv.sO.sN.sN.tx.tyQtKQtE.#6.ot.mw.kf.kf.kf.kf.ns.ty.lf.cVQtN.tz.tA.niQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.tB.tC.tD.tE.pE.tF.tG.e6QtKQtFQtEQtEQtEQtEQtE.bHQtK.tH.tI.tJ.tK.f8.chQtK.bH.bH.bH.bH.bH.bHQtEQtEQtE.bHQtEQtEQtE.lGQtEQtE.h3.h3.h3.cl.bL.g8.j#.j#.j#.tL.lG.jj.tM.tN.tO.tP.tQ.tR.tS.tT.m6.gH.hH.hH.nZ.m..pW.m5.oW.tU.tV.so.tW.pX.pX.tW.pX.pX.pX.m5.m5.m..jP.nZ.hH.hH.gH.fF.m6.eL.dN.c4.cr.dP.dQ.#e.tX.qU.n1.m8.a7.jR.fK.fi.maQtX.bP.m9.mb.#u.#v.bi.#x.#x.#A.aE.#A.#AQthQth.aE.bO.bO.bO.bOQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.#t.#t.#t.#tQtaQtaQtaQtaQtaQtaQtaQta.#uQta.#vQtg.aH.aG.dF.dF.aG.aG.aG.aG.o8.o8.o8.o8.o8.o8.o8.o8.dF.#S.tY.tZ.t0.t1QtmQtmQtmQtm.aG.aG.aG.fq.su.sv.sw.t2.tb.t3.t4.t4.t5.t5.t6.e4.t7.t8.t9.u..u#.ua.ub.tj.uc.ud.rU.sF.sF.rX.rX.qc.qc.qc.qc.qe.qc.rY.ue.aw.#T.at.at.at.at.#C.bWQt..#y.#y.#y.#A.#A.#z.#y",
+"QtaQta.#RQtbQtfQteQteQteQteQteQteQtf.#zQtTQtRQtT.ahQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtb.bOQta.cu.aIQtWQtW.#QQtkQtb.au.aE.#RQtbQtaQtVQtkQtkQtk.#Q.aIQtW.#B.aL.aLQtfQtf.#PQteQtpQtpQtpQtpQtpQtp.#FQteQte.aHQtfQtfQtf.aL.#B.aI.aKQtaQtgQtaQtb.#R.dh.au.dg.#y.#yQthQtSQthQthQthQthQtRQtRQtjQtRQtRQtRQtSQtSQtS.gd.ufQtN.oO.aZ.bHQtE.ug.uh.ui.uj.uk.a#.a#.ul.umQtFQtF.bHQtF.kj.ns.kf.mw.mw.mw.mw.kf.kf.kf.kf.mw.pq.mu.ki.ri.r6.r7.sN.sN.sO.tv.tv.tv.tv.tv.tw.tv.tv.tv.tv.tv.tw.tw.tv.tw.tv.sO.sO.sN.un.bH.h3.uo.up.pq.mw.kf.kf.kf.uq.ur.a..cV.us.ut.df.haQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.ni.uu.uv.uw.ux.uy.uz.aZQtF.bHQtEQtEQtEQtEQtE.bHQtK.pM.ck.umQtK.cS.kw.bHQtEQtE.bH.bH.bH.bH.bHQtEQtEQtE.bHQtEQtEQtE.lGQtE.cl.h3.h3.ch.ch.ch.g8.uA.j#.lF.tL.k#.uB.tM.uC.uD.uE.uF.uG.uH.uI.fD.gH.hH.hH.nZ.m..m..m5.oW.uJ.uK.uL.so.tW.pX.pX.pX.pX.m5.m5.l9.k1.k1.nZ.hH.qR.fG.m6.fD.eL.dN.c4.eP.dP.dQ.uM.#g.rJ.uN.m8.a7.dU.fK.gL.cs.gM.iI.c3.mb.#u.#v.dE.uO.#x.aE.aE.#A.#zQtRQth.#R.#t.#t.#t.#t.#t.#t.#t.#t.#t.#t.#t.#t.#t.#t.#tQtaQtaQtaQta.bOQta.bO.ai.ai.ai.aiQtbQtbQtbQtbQtb.#R.c2QtY.su.fq.fq.uP.uP.uPQtmQtmQtmQtmQtmQtmQtmQtmQtm.rP.rKQtj.uQQtV.aG.aG.aG.aG.aH.su.su.su.sv.t#.tc.uR.t3.uS.uT.uU.uV.uW.uX.uY.uZ.u0.u1.u2.u3.u4.u5.u6.u7.sC.sD.u8.eY.sF.rW.rX.rX.qc.qc.qe.qc.rY.u9.#F.#F.at.at.at.atQteQta.#y.#y.#y.#y.#y.#y.#y.dg.#A",
+"QtaQtaQtbQtg.aLQteQteQteQtfQtfQteQtbQtTQtRQtTQtSQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtb.bO.bO.cu.aIQtW.#QQtkQtW.aIQtb.#RQtbQta.dZ.aI.aI.aI.#B.#B.aLQtfQteQtpQtp.#C.at.cE.cE.v..aj.v..v..aj.v..v#.v#.va.va.va.vb.vb.cE.at.at.at.vcQtpQte.bW.#B.aI.aFQtkQtaQt..dh.au.dg.dg.#yQtS.#yQtS.#yQtSQthQthQtRQtRQtRQtRQtR.dA.vdQtN.dv.ve.bHQtE.koQtF.vf.vg.g6.lmQtN.vh.cS.bHQtF.ko.vi.ns.kf.mw.mw.mw.mw.mw.kf.kf.kf.mw.ou.lu.rh.kc.r8.sN.sO.sO.tv.tv.tv.tw.tw.tw.vj.vj.tw.vj.tw.tw.tw.tv.tv.tv.tv.tv.tv.tv.sN.i4QtE.k#.i5.qp.mu.kh.kf.kf.kf.vk.vlQtN.f7.vm.vnQtZ.voQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.e4.vp.vq.vr.vsQtGQtKQtFQtEQtEQtEQtEQtEQtEQtE.erQtF.eq.vt.ko.g9.bH.bHQtEQtEQtE.bH.bH.bH.bH.bH.bHQtEQtE.bH.bHQtEQtEQtE.lGQtE.cl.h3.h3.ch.ch.ch.j#.g8.j#.lF.lF.kl.vu.vv.vw.vx.vy.vz.vA.vB.uI.fD.gH.gH.hH.nZ.nZ.m..m5.m5.vC.vD.vE.vF.pX.pX.pX.m5.m5.m5.l9.l9.m..nZ.hH.hH.qR.fG.m6.fD.eL.eN.eP.dP.dQ.dR.#d.vG.rJ.uN.m8.a7Qt3.o0.c7.ma.gM.bP.c3.c3.#u.#v.bi.#A.aE.aE.aE.#AQtSQtR.#yQtb.bO.bO.bO.bO.bO.bO.bO.bO.bO.bO.bO.bOQta.bO.bO.bO.ai.ai.ai.aiQtbQtbQtb.#v.#v.#v.#v.#v.#v.c2.c2.aE.vH.svQtY.su.fq.fq.aG.aG.aG.uP.uP.uP.uP.uP.uP.uP.uP.vIQtSQtR.vJQtY.dF.aH.rP.su.su.gM.sv.t#.vK.vL.t4.t6.uU.vM.vN.vO.vP.vQ.vR.vS.vT.vU.vV.vW.vX.vY.vZ.v0.v1.tj.uc.ud.rU.eY.sF.rW.rX.rX.qe.v2.q3.v3.#C.#P.at.at.atQtp.aI.#S.#y.#y.ah.#A.#A.au.au.v4.v4.v4",
+"QtaQtaQtbQtaQtfQtfQtfQtfQteQte.#B.fZQtdQtdQtR.#RQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtb.bOQtaQtkQtWQtWQtk.aF.#B.aIQtbQtb.aK.aI.#B.#B.cv.aL.aHQtp.#C.vc.v5.v..v..aq.#..v6.##.v7.v6.v6.#g.#g.v6.v8.v9.v8.#b.v8.v8.#a.v8.v8.#g.v6.w..#..dT.va.va.cE.#CQteQtfQtf.#B.aIQta.bOQt..#w.#A.#y.#y.dg.ah.#y.#yQtS.#yQthQtRQtjQtQ.w#QtN.sY.aZ.bHQtEQtE.bH.wa.wbQtI.lm.wc.g5QtK.bHQtE.wd.kg.kg.mw.mw.mw.mw.mw.kf.kf.mw.kh.mu.ki.rj.we.sN.sO.tv.tv.tv.tw.tv.vj.vj.vj.wf.wf.wf.wf.wf.wf.wf.vj.vj.tw.tw.tw.tv.tv.tv.sO.wg.k#QtF.tw.rh.rk.ou.mw.kf.lr.r7.sRQtI.nM.wh.wi.wj.wkQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.tB.wl.wm.gj.wn.a0.bHQtEQtEQtEQtEQtEQtEQtEQtE.bHQtE.umQtK.#6.bHQtEQtEQtEQtEQtE.bH.bH.bH.bH.bH.bHQtEQtE.bH.bHQtEQtEQtEQtEQtE.k#.h3.h3.ch.bL.g8.wo.g8.j#.lF.tL.g8.ep.g#.wp.wq.wr.ws.wt.wu.wv.uI.fF.gH.hH.nZ.nZ.m..m..pW.m5.ww.wx.wy.wz.px.m5.m5.m5.l9.l9.m..m..hJ.hI.gH.fF.fE.gG.eL.eN.eO.eP.dP.dQ.uM.#d.vG.jQ.m8.m8.k4.k5.bv.gL.fLQtX.iI.c3.#u.hL.#v.#v.#z.#x.bi.#A.#yQtRQtR.#yQtb.bO.bO.bO.bO.bO.bO.bO.bO.bO.bO.bO.bO.bO.ai.ai.aiQtbQtbQtb.#v.#v.#v.c2.#R.bi.bi.bi.bi.bi.bi.bi.#x.e4.vK.sv.sv.su.wA.fq.fq.fq.aH.aG.aG.aG.aG.aG.aH.aG.wBQtR.wC.#x.su.su.su.su.wB.t#.t#.t2.wD.t3.uS.wE.uV.wF.wG.wH.wI.wJ.wK.wL.wM.wN.wO.wP.wQ.wR.wS.wT.wU.wV.u7.wW.sD.tk.eY.sF.rW.rX.rW.iO.wX.wY.wZ.w0.at.at.w1.bWQt..dg.au.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2",
+"QtaQtaQtbQtkQtpQtpQtpQtpQtpQte.#AQtT.aNQtd.aE.bOQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQtbQta.cu.aF.#Q.aF.aI.aI.aL.cvQta.cu.aI.#B.aLQtfQteQtp.#C.w3.w4.w5.dT.v7.v8.ap.w6.w6.#d.bc.#f.gE.a9.b..b..bQ.bQ.dS.bQ.bQ.m7.bQ.m7.bQ.b..eR.#h.gE.bc.w6.ap.v8.v6.w..dT.aj.cE.#CQtp.gW.aL.aI.aF.cuQt..au.au.au.#A.dg.#A.au.au.au.ah.w7.w8QtN.sY.aZ.bHQtEQtEQtE.g9.w9.du.hV.tK.g9QtE.bH.bH.rn.ns.kf.mw.mw.mw.mw.kf.kf.mw.pq.qp.rh.kc.r8.sN.sO.tv.tv.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.vj.tw.tv.vj.#6.h3.x..r8.rj.ki.mu.kh.mw.x#.xa.bJ.xb.xc.xd.xe.xf.xgQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.ee.xh.xi.xj.#6.bHQtEQtEQtEQtEQtEQtEQtEQtE.bHQtFQtFQtKQtK.bHQtEQtEQtEQtEQtEQtE.bH.bH.bH.bH.bH.bHQtEQtE.bH.bH.bHQtEQtEQtEQtE.k#.h3.bL.ch.h3.wo.bL.uA.j#.lF.lF.uA.uz.cR.mT.xk.xl.xm.xn.xo.xp.eL.fD.fF.hH.hH.nZ.k1.m..m..m5.m5.xq.xr.xs.xt.xu.m..m..m..nZ.nZ.oA.hI.hI.xv.xw.xx.xy.xz.xA.xB.xC.m7.dQ.dR.#e.vG.jQ.m8.oZ.k4.k5.o0.fi.fLQtX.bP.c3.#u.#u.#v.aE.#x.aE.aE.#A.#yQtSQtS.#yQt..bO.bO.bO.bO.bO.ai.ai.ai.bO.bO.ai.ai.ai.aiQtbQtbQtb.#v.#v.#R.#R.bi.bi.bi.bh.bh.bh.bh.bh.bh.#x.xD.xE.o9.xF.xG.xH.xI.xJ.xK.fq.fq.fq.fq.fq.aH.fq.aH.fq.dV.ni.xL.dZ.fq.su.sv.t#.sw.vK.ta.vL.t3.uS.wE.uV.xM.xN.xO.xP.xQ.xR.xS.xT.xU.xV.xW.xX.xY.xZ.x0.x1.x2.x3.v1.x4.x5.sD.rU.eY.rW.rW.x6.x7.x8.df.#F.#C.at.bj.aF.au.k6.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtaQtaQtb.aI.#C.#C.#C.#C.bsQtaQtUQtjQtd.#SQtbQtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQtbQtaQtV.aF.aI.aI.aI.aLQte.bWQtk.aI.cqQtfQtfQte.#C.at.v..w5.#..#g.y..an.gE.#h.ba.cr.cr.c4.dN.eI.fE.fF.gH.y#.y#.ya.m..yb.yc.ya.yd.y#.hH.gH.ye.m6.eI.eM.dN.c4.bQ.b..gE.yf.#c.#a.w..w5.v#.ezQtpQtf.cv.cqQtWQtaQtb.#R.dh.k6.au.k6.au.au.bt.ygQtN.dt.#6.bHQtEQtEQtE.a0.aa.cj.cj.jj.a0.bHQtE.yh.ts.kf.mw.mw.mw.mw.kf.kf.mw.pq.qp.rh.rn.r8.yi.tv.tv.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tv.yjQtEQtE.yk.r8.rn.rh.mu.pq.kf.kd.ylQtN.ym.yn.yo.yp.yq.wkQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.yr.ys.yt.yu.wnQtKQtEQtEQtEQtEQtEQtEQtEQtE.bHQtKQtKQtFQtK.bHQtEQtEQtEQtEQtEQtEQtE.bH.bH.bH.bH.bH.bHQtEQtE.bH.bH.bHQtEQtEQtEQtE.k#.h3.bL.h3.h3.wo.g8.j#.j#.lF.lF.cg.g8.sX.nJ.yv.yw.yx.yy.yz.ge.eL.rq.fF.oX.hH.hH.nZ.hH.nZ.nZ.m..m..yA.yB.yC.yD.xu.m..nZ.nZ.oA.oA.yE.yF.yG.yH.yI.yJ.yK.yL.yM.yN.yO.yP.yQ.yR.vG.jQ.k3.n2.k4.dU.c7.fLQtX.bP.dV.#u.yS.#v.#v.bi.aE.aE.aE.#A.#y.#A.#y.#y.#w.ai.ai.ai.ai.ai.ai.ai.ai.ai.aiQtbQt.QtbQtb.#w.#v.#w.#R.bi.bi.bi.bh.bh.bh.#x.eU.uO.uO.uO.uO.uO.yT.yU.yV.xF.yW.yX.yY.yZ.y0.fq.fq.fq.fq.fq.fq.fq.fq.fq.mi.haQtR.sv.su.sv.t#.t#.t2.ta.t3.t4.uS.y1.y2.xM.y3.y4.y5.y6.y7.y8.y9.z..z#.za.zb.zc.zd.ze.zf.zg.zh.wU.zi.zj.wW.sD.tk.eY.zk.fT.zl.zm.dfQtp.#C.#CQte.bO.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtaQtaQtb.#B.#C.#C.#C.#C.bWQtSQtTQtTQthQt.QtaQtaQtaQtaQtaQtaQtaQtaQtaQtbQtb.bOQtaQtk.aI.aI.#B.cvQtfQtpQtf.aI.aI.aLQteQtp.at.cE.aq.v7.#b.#d.#f.#h.zn.eQ.eK.fE.xv.zo.yd.yb.zp.zq.zr.zs.zt.zu.zv.zw.zx.zy.zz.zA.zB.zC.zC.zw.zu.zD.zE.zF.yb.yc.hH.fF.gG.dN.cr.#h.ak.#d.#b.v6.zG.va.v5.#TQte.bW.c8.cuQtaQtbQt.Qt..#R.aM.bu.zHQtN.sY.a0.bHQtEQtEQtE.aZ.zI.a#QtI.zJQtF.nq.h3.i7.ns.kf.mw.mw.mw.kf.kf.kf.pq.qp.zK.zL.sN.sO.tv.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.pt.h3QtK.wf.sN.r8.ri.rk.ou.zM.zN.a..zO.zP.zQ.g0.zR.zS.zTQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.zU.zV.zW.zX.zY.nt.bHQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK.bHQtK.bHQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.bH.bH.bH.bHQtEQtE.bH.bH.bHQtEQtEQtEQtEQtE.h3.ch.h3.bL.wo.g8.j#.lF.lF.tL.tL.ch.zZ.nJ.z0.yw.z1.z2.z3.z4.z5.z6.z7.fE.oX.gH.gH.hH.nZ.nZ.nZ.nZ.nZ.z8.z9.A..A#.Aa.m..nZ.Ab.Ac.Ad.Ae.Af.Ag.Ah.Ai.Aj.Ak.Ah.Al.Am.An.Ao.Ap.Aq.jQ.a7.k4.k5.o0.fK.cs.bTQtX.m9.#s.#t.ai.#w.bi.aE.#w.aE.#z.Ar.As.At.Au.fK.ai.ai.ai.ai.ai.bg.Av.Aw.Ax.AyQtW.#w.#w.#w.#w.dZ.Az.AA.AB.AC.AD.AE.AF.AG.AH.AI.AJ.AJ.AJ.AJ.AJ.AK.AL.bV.xF.AM.AN.AO.AP.xH.su.fq.fq.fq.AQ.AR.AS.AT.AT.AC.AE.AD.AU.AU.AV.AU.AW.AX.AX.AY.AZ.wE.vN.xM.xN.A0.A1.A2.A3.A4.A5.A6.A7.A8.A9.B..B#.Ba.Bb.Bc.Bd.Be.Bf.Bg.Bh.Bi.uc.Bj.rU.Bk.Bl.Bm.dfQtp.#C.#C.aL.aE.r..x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtaQtbQtb.cq.bj.bj.bj.#F.#RQtTQtjQtj.aE.bOQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQt#.cu.aI.aIQtW.#B.aLQteQtp.#P.#B.aLQte.#C.at.va.v7.y..an.gE.ba.gI.dN.pV.Bn.yd.Bo.zF.zE.zD.zv.zx.zA.Bp.Bq.Br.Br.Bs.Bt.Bu.Bu.Bv.Bw.Bv.Bx.By.Bz.Bs.BA.Br.Br.BB.BC.zy.zw.zu.BD.zF.yb.BE.eI.c4.bQ.a9.yf.y..##.BF.vb.at.#TQte.aLQtW.aFQtVQtaQtb.#R.BGQtN.jl.#6.BHQtEQtEQtE.cXQtG.a2.cV.h5.lwQtF.h3.BI.ns.kf.mw.mw.mw.kf.kf.kh.qp.rh.we.sN.sO.tv.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.BJQtKQtE.wg.sO.sO.sN.kc.ki.ou.kg.BK.BL.BM.BN.BO.g0.BP.BQ.zUQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.BR.BS.BT.BU.eh.#6QtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.bH.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.bH.bH.bH.bHQtEQtE.bH.bH.bHQtEQtEQtEQtE.h3.h3.ch.h3.ch.wo.g8.j#.uA.lF.tL.cY.kl.w9.a#.BV.BW.BX.BY.BZ.w6.oB.B0.Aa.dN.oX.oX.gH.qR.hJ.nZ.nZ.m..m..jP.B1.B2.B3.B4.B5.B6.B7.B8.B9.C..C#.Ca.Cb.Cc.Cd.Ce.Cf.Cf.Cg.Ce.Ch.Ci.Cj.Ck.ClQt4.Cm.dU.fK.fK.bg.bT.#r.bP.c3.#t.ai.bi.bi.bi.#A.#w.CnQtN.Co.Cp.pYQt.Qt.Qt.Qt.Qt..Av.gi.Cq.Co.CrQt2.#w.#w.#w.#R.Cs.Ct.ml.zO.Cq.zO.Cq.zO.gj.Cu.Cv.Cw.Cx.xD.AJ.Cy.Cz.AL.CA.sw.CB.CC.CD.CE.CF.su.CG.CH.CI.CJQtN.Cq.zO.Cq.Cq.Cq.Cq.Cq.Cq.Cq.Cq.zO.Cq.Cq.CK.CL.CM.xM.CN.CO.CP.CQ.CR.CS.r5.vv.CT.CU.CV.CW.CX.CY.CZ.C0.C1.C2.C3.C4.C5.th.C6.x5.C7.rV.rZ.C8.awQtpQtpQtp.aI.au.k6.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtaQtb.c2.aL.bj.bj.bjQtWQthQtRQtT.ahQtbQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQtbQtk.aI.aI.#B.aLQtfQte.#CQtpQtfQtp.vc.v..w5.v7.y..#f.#h.eO.dN.fE.hH.C9.D..D#.zv.zx.Bp.Da.Br.Bs.Bv.Db.Dc.Dd.De.Df.Dg.Dh.Di.Dj.Dj.Dk.Dk.Dk.Dk.Dh.Dl.Df.Dm.Dn.Dd.Do.Dp.Bu.BA.Dq.Bp.zC.zv.zD.Dr.nZ.fF.Ds.gI.b..k2.ap.v7.Dt.Du.v5.#TQteQtZ.#BQtk.bO.DvQtN.DwQtK.rAQtEQtEQtE.bHQtE.re.a..g6.oD.#7.h3.Dx.lr.mw.mw.mw.kf.kf.mw.mu.rh.rn.sN.sO.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tv.DyQtE.bH.Dz.sO.sO.sN.rn.rh.mw.DA.h5.DB.DC.DD.DE.DF.DG.o7.hbQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.hb.DH.DI.DJ.DK.bH.bHQtE.bHQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.bH.bH.bH.bH.bHQtE.bH.bH.bHQtEQtEQtE.cl.h3.h3.ch.h3.ch.bL.wo.g8.j#.lF.lF.cY.bL.uy.nJ.DL.DM.z1.DN.DO.oX.DP.DQ.Dp.dP.oX.gH.gH.hH.hH.nZ.oA.nZ.jP.jP.k1.DR.DS.DT.DU.DV.DW.DX.DY.DZ.DZ.DY.D0.D1.D2.D3.D4.D5.D6.D6.D7.D8.D9.E..E#.EaQt4.Eb.eT.be.bfQtX.#r.#s.#t.#u.#w.aE.aE.#x.#A.ai.Ec.Co.CK.Ed.n1.aEQt.Qt.Qt.Qt8.Ee.Cq.Ef.Eg.Eh.#w.#w.#R.#R.bi.z4.Ei.r5.Ef.Ej.Cq.Cq.zO.Ej.r5.Cq.Ek.El.Em.xD.Em.yU.wk.yV.xF.En.Eo.Ep.Eq.Er.su.su.CH.Es.Et.hV.Ej.Ej.r5.r5.CK.Ef.Ef.Eu.Ej.r5.r5.Ej.r5.qx.Ev.vN.Ew.Ex.Ey.Ez.EA.EB.EC.hV.Eu.CT.ED.EE.EF.EG.EH.EI.EJ.EK.EL.EM.EN.EO.EP.EQ.x5.ER.ESQte.bW.#C.#CQteQtb.dg.v4.w2.w2.v4.ET.ET.EU.EV.EW.EV.EV.EV.EV.EW.EV.EU.EV.EV",
+"Qta.c2Qt.Qtf.#C.#CQte.#AQthQtRQtR.#RQtaQtaQtaQtaQtaQtaQtaQtaQtbQtb.bOQtkQtW.aI.aL.aLQteQtp.#C.#CQte.vc.v..#..v6.#c.k2.b#.qS.fE.gH.yb.EX.zt.zC.zA.BB.BA.Bx.EY.EZ.E0.E1.E2.E3.E4.E5.E6.E5.E7.E8.E9.F..F#.Fa.Fb.Fc.Fd.Fb.Fe.F..E7.Ff.Eh.Fg.Fh.Dl.Fi.Fj.Fk.Fl.Fm.Dq.Fn.Fo.Fp.zp.y#.fF.Fq.c4.a9.y..v6.dT.Fr.v..#CQteQtf.aI.FsQtN.kx.kw.bHQtEQtEQtE.bH.aZ.FtQtI.du.mW.a0.h3.Fu.ns.kf.mw.kf.kg.mw.ou.ki.r6.mt.sO.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.vj.mr.h3.nt.vj.tv.tv.sO.r7.ri.mw.nk.lx.a0.fd.r2.Fv.Fw.Fx.FyQtTQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.yr.Fz.FA.FB.FC.kw.bHQtE.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtFQtF.bHQtEQtEQtEQtE.bH.bH.bH.bH.bH.bH.bHQtE.bH.bH.bH.bHQtEQtE.cl.h3.h3.h3.h3.h3.bL.wo.g8.j#.lF.lF.tL.ch.fc.a#.FD.FE.FF.FG.FH.FI.FJ.FK.FL.FM.m6.oX.oX.gH.gH.hH.oA.oA.oA.hJ.hJ.hH.FN.FO.FP.FQ.FR.FS.FT.FU.FV.FW.FX.FY.FZ.F0.F1.F2.F3.F4.F5.F5.F6.F7.F8.F9.G..dU.fK.fi.fLQtX.G#.#s.#t.ai.#w.#x.#x.#A.aE.#t.Ga.Co.Ej.GbQt7.#A.dhQt.Qt..Gc.zO.Ef.CK.Gd.#s.#R.bi.bi.bi.bi.Ge.Gf.r5.r5.Gg.Gh.Gi.Gj.Gk.Eu.Eu.r5.Gl.Gm.Em.AK.AL.Gn.ct.sw.Go.pL.CK.Gp.xI.su.CH.CH.m#.Gq.Gr.Gs.Gt.Gu.Gv.Gw.CK.Ef.Gx.Gr.Gy.Gz.GA.GB.GC.GD.xM.GE.A0.GF.GG.GH.GI.GJ.Cq.Ef.GK.GL.GM.GN.CR.GO.GP.GQ.GR.GS.GT.GU.GV.GW.tg.GX.aH.ed.#F.GYQtpQtW.aE.au.GZ.G0.EV.G1.G2.G3.G4.G5.G6.G7.G8.G9.H..H..G7.G6.H#.Ha.Hb",
+"QtaQtbQtbQtf.#CQtpQtb.vJQtRQtj.#SQtbQtaQtaQtaQtaQtaQtaQtaQtbQtbQta.aF.aI.#B.cvQtfQtp.#C.Hc.v5.vc.cE.dT.v6.y..k2.bQ.dN.Hd.nZ.zp.BD.Fo.He.BB.Fm.Bv.Do.Hf.Dl.Dj.Hg.Hh.E5.E9.Hi.to.Hj.Hk.Hl.Hm.Hn.Ho.Hp.Hq.Hr.Hs.Ht.Hu.Hv.Hw.Hx.Hy.Hz.HA.HB.HC.HD.HE.HF.HG.Dg.Fj.EY.Bv.Fm.Dq.zA.zv.EX.HH.zo.fF.c4.#h.yf.y..#a.##.va.v5.#P.ksQtN.HI.kw.bHQtEQtEQtEQtEQtF.um.HJ.du.k..koQtF.i4.ns.kf.mw.kf.kf.pq.rk.rj.r8.sO.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.HK.bHQtE.i4.tw.vj.tv.sO.r7.rh.HL.aaQtF.bH.ko.HM.HN.HO.HP.vpQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtR.HQ.HR.HS.HT.cS.ab.bHQtEQtK.sQ.uoQtKQtE.bH.bH.bHQtEQtEQtEQtEQtE.bHQtEQtE.h3.h3QtEQtEQtE.bH.bH.bH.bH.bH.bH.bHQtE.bH.bH.bHQtEQtEQtE.k#.h3.h3.h3.h3.h3.ch.wo.j#.j#.uA.lF.tL.HU.a1.lm.HV.HW.HX.HY.HZ.H0.H1.H2.H3.#d.fD.m6.fF.oX.gH.qR.qR.hI.hI.hH.hH.H4.H5.H6.H7.H8.H9.I..I#.Ia.Ib.Ic.Id.Ie.If.Ig.Ih.Ii.Ij.Ik.Il.Il.Im.In.Io.Ip.Iq.Ir.fK.bg.bTQtX.#s.#t.ai.#v.bi.aE.aE.#A.aE.ai.Ec.Co.Ej.GbQt8.#y.#A.dh.ae.Is.pL.Ef.Ei.It.aE.aE.bi.bi.aE.bh.Iu.Ei.r5.Co.Iv.AJ.yT.yT.Iw.Ix.pL.Iy.Cq.Iz.AJ.IA.IB.IC.jS.AI.ID.zO.Eu.IE.IF.su.su.fq.fq.GY.IG.IG.Cl.t#.bi.IH.Cq.r5.Ed.II.IJ.IK.IL.IM.IN.vN.CN.IO.IP.IQ.IR.IS.IT.IU.r5.Eu.Ej.IV.IW.IX.IY.IZ.I0.I1.I2.I3.I4.I5.I6.I7.I8Qt.Qt0.o8.#PQtW.dh.r..v4.x9.I9.J..Hb.J#.Ja.Jb.Jc.Jd.Je.Jf.Jg.Jh.Jh.Jg.Jf.Je.Jd.Jd.Ji",
+"QtaQt..bO.#P.#C.t1QthQthQtRQth.#RQtaQtaQtaQtaQtaQtaQtaQtbQtb.cu.aF.Jj.#B.aLQteQtp.#C.cE.Du.vb.Jk.##.v8.yf.bQ.dN.fF.nZ.D..Jl.zx.r0.Jm.Fl.Jn.E0.Dg.Jo.E6.Jp.Jq.Jr.Js.Jt.Ju.Jv.Jw.Ht.k..nNQtNQtNQtNQtN.nN.kv.Jx.mD.Jy.Jy.Jy.Jz.JA.uA.uA.wo.wo.h3QtF.g9.JB.JC.JD.JE.JF.sI.EY.JG.Dq.JH.zC.zr.JI.yd.xv.dN.bQ.#f.#c.y..#..#C.JJQtN.po.bH.bHQtEQtEQtEQtE.bH.#6QtM.du.g6.w9.nt.nt.ou.kg.kf.kf.ke.mu.rh.r7.sO.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.JK.tw.JLQtE.g9.wf.vj.vj.tw.tv.mt.ri.JM.h3.bHQtEQtE.JN.JO.JP.JQ.JRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtTQtc.JS.JT.JU.r2QtEQtK.Dy.JV.vj.tv.JW.#6QtEQtKQtFQtEQtEQtEQtE.h3QtE.nt.oq.JX.JYQtK.cl.h3QtEQtE.bH.bH.bH.bH.bHQtE.bH.bHQtEQtEQtEQtE.k#.cl.h3.h3.ch.bL.wo.bL.g8.g8.j#.tL.lF.h3.JZ.J0.f6.J1.J2.J3.J4.J5.J6.sZ.J7.an.J8.fE.m6.m6.fF.fG.fG.fG.gH.gH.fF.J9.K..K#.Ka.Kb.H8.Kc.Kd.Ke.Kf.Kg.Kh.Ki.Kj.Kk.Kl.Km.Kn.Ko.Kp.Kq.Kq.Kr.Ks.Kt.Ku.Kv.ar.bT.#r.bP.c3.Kw.ai.#w.bi.aE.aE.aE.dD.ai.Ec.Co.Ej.EdQt8.#A.#z.#A.Kx.Co.Iy.Ef.Ky.#A.#A.aE.aE.bi.#D.#x.Iu.Gw.r5.Co.Kz.xD.yT.Em.Em.KA.IE.Eu.Eu.Ep.KB.Gn.IB.KC.ta.CG.ID.zO.Eu.Gx.IF.su.su.KD.fq.fq.fq.fq.KE.e4.o..KF.Co.Ej.KG.KH.KI.KI.uS.uV.wF.KJ.GE.KK.KL.KM.KN.KO.KP.pL.Ef.KQ.Ef.KR.KS.KT.KU.KV.KW.KX.KY.KZ.K0.K1.K2.K3.dh.suQtY.ai.#A.r..v4.x9.w2.EV.K4.K5.Jh.K6.Je.K7.K8.K8.K8.K8.Jc.Jc.Jc.Jc.K8.K8.Jc.K9.L.",
+"QtaQtb.#uQteQtp.dhQthQthQtR.#AQtbQtaQtaQtaQtaQtaQtaQtbQtbQta.aF.aI.cvQtfQteQtp.at.L#.dT.w5.dT.#g.w6.bb.c4.fF.y#.zF.zD.zx.BB.Bs.EY.E0.sI.La.Lb.Fe.Lc.Ld.Le.Lf.Lg.hh.lmQtNQtNQtNQtNQtNQtNQtNQtNQtN.lf.Lh.ps.lr.mw.qp.ot.Li.rf.Lj.lv.JYQtD.bH.h3.wo.ch.Lk.wo.wo.h3.g9.Ll.Lm.Ln.HG.Lo.Lp.BB.zy.zt.EX.zp.hH.gJ.cr.bb.yf.aq.LqQtN.re.k#.bHQtEQtEQtEQtEQtEQtF.lw.ox.g6.a#.a1QtK.qq.ns.kf.mw.nr.ki.tt.sN.tv.tv.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.Lr.g9QtE.i4.Ls.vj.vj.tw.tv.r8.Lt.bH.bHQtEQtEQtE.bw.Lu.Lv.ey.hbQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.zU.Lw.Lx.Ly.Lz.pNQtF.rm.tv.vj.vj.wf.tw.HK.oPQtEQtEQtEQtE.h3.#6.qn.rn.nr.kg.os.mw.ki.rf.sM.JX.LA.#6QtE.bHQtF.bH.bH.bH.bHQtEQtEQtEQtEQtE.cl.h3.h3.h3.ch.wo.ch.g8.g8.j#.lF.tL.bL.LB.cR.g6.LC.LD.LE.LF.LG.LH.nJ.LI.#e.eO.fD.fD.fD.m6.m6.m6.fF.fG.fF.LJ.LK.LL.LM.LN.LO.LP.LQ.LR.LS.LT.LU.LV.LW.LX.LY.LZ.L0.L1.L2.L3.L4.L5.L6.L7.L8.L9.M..M#.Ma.G#.#s.#t.Kw.#u.#w.#w.aE.aE.aE.aE.ai.Ec.Co.Ej.GbQt8.#A.#A.Mb.Mc.Ej.pL.Md.m8.#y.#D.bi.aE.bi.#D.#x.Iu.Ei.r5.Co.Me.yT.yT.Em.Em.Mf.Mg.Cq.Iy.Mh.Mi.Mj.Mk.AJ.xF.CH.Ml.zO.CK.IE.IF.su.fq.fq.fq.fq.fq.su.mi.Mm.ta.Mn.Co.Ej.KG.Mo.KI.Mp.uS.uV.vN.KJ.Mq.Mr.Ms.Mt.Mu.Mv.Mw.vv.Mx.My.CK.CT.Mz.MA.MB.MC.MD.ME.MF.MG.MH.MI.MJ.uO.td.uO.br.as.au.dh.w2.w2.w2.MK.ML.Jh.K9.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K7.K8.MN",
+"QtaQtbQtaQt1.aLQtS.#yQtRQtR.dhQtaQtaQtaQtaQtaQtaQtbQtbQta.aF.#B.aLQtfQtp.#C.v5.MO.##.v6.w..#b.dM.dS.eK.gH.MP.zs.zx.Da.Bu.Do.MQ.Fg.E5.F#.Fc.hc.MR.MS.MT.MU.h5QtNQtNQtNQtN.g6.ic.jc.bK.ci.re.kR.lm.cj.jh.MV.MW.MX.kf.MY.MZ.os.lr.kg.mw.mu.kc.Dx.qn.uo.aZQtE.h3.h3.h3.M0.wo.j#.#6.M1.M2.M3.M4.Dq.zA.Fo.zD.zp.y#.eI.dS.#a.M5QtN.jc.ow.abQtEQtEQtEQtEQtEQtE.a0.M6QtI.g6.cU.f8.Lt.ns.kf.ke.qp.ri.r7.sO.tv.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.rmQtE.j..vj.vj.wf.wf.tw.sO.M7.g9QtE.erQtEQtE.ko.M8.M9.N..JRQtRQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.BR.N#.Na.e8.Nb.ugQtF.Nc.tw.wf.wf.wf.vj.tv.ykQtFQtE.bHQtK.yk.rk.ns.kg.kf.kf.kf.kf.pu.kg.ns.kg.ts.ki.i7.#6QtE.bH.bH.bHQtEQtEQtEQtEQtEQtE.k#.h3.h3.h3.ch.bL.ch.g8.g8.j#.lF.lF.lF.g8.mC.cj.Nd.Ne.Nf.Ng.Nh.Ni.tM.Nj.#e.eO.eL.eL.eL.fD.fD.fD.fE.fE.Nk.Nl.Nm.Nn.No.Np.Nq.Nr.Ns.Nt.Nu.Nv.Nw.Nx.Ny.Nz.NA.NB.NC.ND.NE.NF.NG.NH.NI.NJ.NK.NL.NM.NN.NO.#r.#s.NP.#t.ai.aE.aE.aE.aE.aE.aE.ai.Ec.Cq.Ej.GbQt8.#A.#w.Mn.Co.Eu.Eu.NQ.#y.#A.aE.bi.aE.bi.aE.#x.Iu.Gw.Ej.Co.Me.yT.Em.Em.Em.NR.NS.ml.Ef.CK.NT.NU.AL.NV.t2.CH.Ml.zO.Eu.IE.IF.su.su.fq.fq.fq.KD.su.uO.bh.sx.NW.Co.r5.KG.Mo.KI.Mp.uT.uV.vN.NX.NY.NZ.N0.GH.N1.N2.N3.zO.N4.N5.N6.Cq.N7.N8.N9.O..O#.Oa.Ob.Oc.Od.xM.yU.Oe.OfQtTQtc.au.v4.dh.w2.w2.w2.Og.Oh.Oi.K8.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K8.Oj.Ok.Ol.sq",
+"QtaQtb.Om.#F.aiQthQtSQtR.#yQtbQtaQtaQtaQtaQtaQtbQtb.bO.aF.#B.aLQtfQtp.#C.vb.w5.v6.#b.#g.y..gE.c4.fF.ya.zr.On.Da.M4.Dc.Oo.Op.E7.Fa.Oq.Le.Or.Os.Ot.gXQtNQtNQtN.cU.k..sc.mU.Ou.rz.#6.uo.oq.i2.lv.Ov.Ow.Ox.Oy.Oz.dx.OA.OB.M7.ns.OC.mw.kf.kf.kg.kg.os.ts.OD.OE.i6.OFQtF.h3QtEQtEQtE.wo.OG.OH.HC.Hh.Bv.r0.zA.zC.zD.OI.BE.a9.OJQtN.lfQtG.cXQtEQtEQtEQtEQtE.bHQtFQtK.sY.du.a..OK.Ll.kg.kf.nr.ki.tt.sN.tv.tv.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.Ls.OL.bH.HK.tw.wf.wf.wf.tv.tv.ptQtE.bHQtEQtEQtE.bH.OM.ON.n1.b6QtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtSQtS.e4QtRQtSQtSQtSQtSQtSQtS.vp.OO.OP.OQ.OR.OSQtE.i4.tw.wf.wf.wf.wf.vj.tw.i0QtEQtE.OT.ns.kg.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.kf.kg.kg.zK.JX.a4.bHQtFQtEQtEQtEQtEQtEQtEQtE.cl.h3.h3.h3.ch.wo.wo.g8.j#.lF.lF.j#QtE.OU.cj.OV.OW.OX.OY.OZ.O0.a#.O1.uM.eP.eN.eN.eL.eL.eL.eL.eI.eI.O2.O3.O4.O5.O6.O7.O8.O9.P..P#.Pa.Pb.Pc.Pd.Pe.Pf.Pg.Ph.Pi.Pj.Pk.Pl.Pm.Pn.Po.Pp.Pq.Pr.Ps.Pt.Pu.G#.#s.#t.#u.ai.#w.#w.aE.aE.aE.aE.ai.Ga.Co.Ej.GbQt8.#A.Pv.Iy.CK.Cq.Mn.av.#z.aE.bi.bi.bi.aE.dD.#x.Iu.Ei.Ej.Cq.Pw.Em.Em.Px.NR.NR.Py.ml.Eu.zO.Pz.PA.yU.vL.tb.IK.Ml.pL.Eu.IE.IF.su.su.fq.CH.fq.CH.t#.rK.iI.su.Mn.Cq.r5.PB.PC.KI.Mp.PD.uV.vN.PE.PF.PG.PH.O#.PI.PJ.N4.zO.N3.PK.PL.pL.PM.PN.PO.PP.PQ.PR.PS.PT.PU.PV.vQ.PW.ha.vJ.#y.#A.k6.dh.tm.v4.EV.td.PX.PY.K8.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K7.PZ.P0.P1.P2.t1",
+"QtaQtb.aKQt0.#AQthQtSQtS.dhQt#QtaQtaQtaQtaQtbQtb.bOQtk.#BQtfQtfQtp.#C.vb.P3.v8.w6.y..P4.ba.eI.hH.P5.zv.P6.Bs.P7.P8.E3.E8.Lc.c0.Hj.P9.Q..jd.ibQtNQtN.sR.sc.M6.Q#.cl.yh.sM.r7.rg.kh.mw.kg.kg.ns.MZ.Qa.zM.x#.nr.Qb.nk.j2.gh.nk.lu.MZ.mw.mw.mw.mw.mw.kf.kf.kg.kg.kh.Qc.Lt.uo.bH.h3QtEQtE.h3.wo.g9.Qd.Qe.Dp.r0.BC.zx.zD.Qf.Qg.bJ.#9.Qh.QiQtEQtEQtEQtE.bHQtK.bHQtD.JV.Qj.tM.bJ.LB.Lt.pq.kd.ri.r7.sO.tv.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.JVQtK.Qk.Ql.vj.wf.wf.vj.tv.lv.koQtEQtEQtE.bHQtE.Qm.Qn.eN.Qo.gbQtQ.a8.agQtT.b6Qtr.dB.dB.dB.a8.#U.gb.diQtr.fZQtRQtRQtSQtSQtSQtS.Qp.Qq.HS.Qr.Qs.JNQtE.uo.tw.vj.wf.wf.wf.vj.vj.QtQtE.nt.mu.kg.mw.kf.kf.mw.kf.mw.mw.mw.mw.mw.mw.mw.mw.kf.kg.kg.Qu.QvQtE.bHQtEQtEQtEQtEQtEQtE.k#.h3.h3.h3.ch.bL.wo.g8.g8.j#.lF.lFQtE.sY.cj.Qw.Qx.Qy.Qz.QA.QB.a#.QC.al.dP.eP.eO.c4.eN.dN.eN.dN.eQ.QD.QE.QF.QG.QH.QI.QJ.QK.QL.QM.QN.Pa.QO.QP.QQ.QR.QS.QT.QU.QV.QW.QX.QY.QZ.Q0.Q1.Q2.Q3.Q4.Q5.Q6.c3.c3.#t.ai.ai.#w.#w.#w.aE.aE.#A.ai.Ec.Co.Ej.Gb.n0.G#.Q7.Co.Ej.Q8.Q9.#y.#A.dh.#R.bi.bi.bh.bh.#x.Iu.Gw.Ej.Co.R..Em.R#.NR.Ra.Cz.Rb.vv.Ef.Cq.Rc.PA.Ra.Rd.Re.Rf.Rg.zO.CK.IE.Rh.AI.su.su.fq.CH.rP.c2.o..sv.su.Mn.Co.r5.Ed.Ri.t3.Mp.IN.uV.vO.Rj.Rk.Rl.Rm.Rn.Ro.Rp.Co.Cq.Rq.Rr.Rs.zO.Rt.Ru.Rv.Rw.Rx.Ry.Rz.RA.RB.RC.vQ.AL.haQtRQtS.#S.au.au.RD.RE.Hb.RF.PZ.K8.K9.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K8.Oj.RG.RH.RI.RJ.at.#C",
+"QtaQtbQtkQtkQthQtSQtS.#yQt..miQtbQtb.miQtbQtbQta.cu.c8QtfQteQtp.vc.L#.eS.#b.yf.bc.#f.gI.fF.C9.BD.He.Br.Lo.Dn.Dj.E6.Fb.FL.Le.P9.RK.Hr.tMQtN.jcQtJ.LB.cg.nt.ln.lo.Qc.kh.os.kg.pu.kf.kf.kf.kf.kf.mw.mw.mw.mw.kf.MZ.uq.kd.RL.RM.nR.Dx.x#.kf.mw.mw.mw.mw.mw.mw.mv.kf.kg.kg.kf.kc.i6.Qt.h3.h3QtEQtE.wo.h3.RN.RO.RP.Bq.Bp.EX.RQ.g6.cU.RRQtKQtEQtEQtE.bHQtF.bH.RS.kc.MZ.MZ.RT.RM.sX.bH.i4.ri.ri.sN.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.yk.sQ.vj.vj.wf.wf.vj.tw.Dy.bHQtEQtE.bH.bHQtF.uo.RU.RV.eK.RW.Fc.RX.RY.RZ.R0.R1.R2.i#.R3.R4.R5.R6.R7.R8.R9.#wQtQ.#UQtTQtSQtSQtS.vp.S..OP.S#.Sa.bw.er.#6.Sb.Sc.wf.wf.wf.tw.SdQtK.h3.qn.kg.mw.mw.mw.kf.kf.kf.kf.kf.kf.mw.mw.mw.mw.mw.mw.mw.kf.ns.lp.JY.h3QtEQtEQtEQtEQtEQtEQtE.cl.h3.h3.h3.ch.bL.wo.g8.j#.j#.g8.qH.dv.cj.h1.Se.Sf.Sg.Sh.Si.nJ.Sj.Sk.dR.Sl.eP.eP.eO.eO.c4.qS.Sm.Sn.So.Sp.Sq.Sr.Ss.St.Su.Sv.Sw.Sx.Sy.Sz.SA.SB.SC.SD.SE.SF.SG.SH.SI.SJ.SK.SL.SM.SN.SO.SP.SQ.SR.SS.#t.#u.ai.#w.#w.dE.dE.aE.aE.#A.ai.Ec.Cq.CK.Ed.fK.ST.r5.Eu.Co.SU.#y.av.bi.#w.#R.bi.bi.#D.#x.#x.SV.Ei.Ej.Cq.R..NR.KC.Cz.SW.SX.SY.ml.Eu.r5.SZ.S0.uU.Mp.S1.S2.S3.pL.CK.IE.Rh.sx.su.su.su.CH.sv.bh.wB.su.su.NW.Cq.r5.KG.PC.t3.Mp.IN.CM.S4.S5.S6.S7.S8.S9.T..T#.Co.zO.Ta.Tb.Tc.Cq.Cq.Td.Te.Tf.Tg.Th.Ti.Tj.Tk.Tl.y4.IB.yU.niQtS.#S.Tm.dg.Tn.To.Tp.K7.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K7.Tq.Tr.Ts.P2.dH.at.at.at",
+"Qta.#R.aK.#RQthQtSQtS.#AQtb.bOQtaQta.bOQtbQtV.aK.c8.bWQteQtp.at.Du.fJ.y..yf.#i.Tt.c4.zo.OI.Tu.JH.Bu.Fj.Dl.Tv.Jq.Fc.MR.P9.w#.gg.hVQtN.zJ.Tw.bL.ka.yk.Tx.rk.kf.ns.kg.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.pu.Ty.TzQtC.TA.TB.Ty.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.kg.lr.ps.BI.yhQtE.h3QtEQtE.h3.j#.TC.TD.Dm.zy.TE.J0.a#.TF.aZQtEQtEQtE.bHQtE.mr.rk.ns.mw.kf.x#.TGQtMQtM.k#.i0.JV.r8.sO.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.rm.Nc.tw.wf.wf.wf.tv.Dy.koQtE.bH.kwQtF.k#.TH.TI.TJ.TK.TL.TM.TN.TO.TP.BM.TO.lx.TQ.TN.eo.TR.TS.TT.TU.r1.vs.TV.Jt.Bx.fL.tn.dBQtR.JR.TW.HS.e8.TX.oP.ko.bH.TY.tw.wf.wf.wf.vj.Lj.bHQtE.TZ.ke.ou.ou.ps.pq.mw.mw.kf.kf.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.kg.kd.i2.h3QtEQtEQtEQtEQtEQtE.k#.h3.h3.h3.h3.bL.wo.wo.g8.j#.wo.qH.dv.cj.T0.T1.T2.T3.T4.T5.nJ.T6.eN.#e.T7.dP.bQ.dS.eP.cr.eP.T8.T9.U..U#.Ua.Ub.Uc.Ud.Ue.Uf.Ug.Uh.Ui.Uj.Uk.Ul.Um.Un.Uo.Up.Uq.Ur.Us.Ut.Uu.Uv.Uw.Ux.Uy.Uz.UA.UB.UC.#t.ai.ai.#w.#w.#w.aE.aE.#A.#A.ai.Ec.Cq.Ej.UD.UE.Gk.zO.zO.UF.h9.#z.aE.bi.#w.bi.bi.aE.aE.#x.eU.SV.Gw.Ej.Co.UG.UH.Cz.SW.UI.UJ.UK.Cq.Ef.UL.UM.S4.wE.IN.Mp.IM.Ml.zO.Eu.UN.UO.sw.sx.AI.su.sv.#u.dW.sx.ta.vK.KF.Co.Ej.PB.Mo.t3.UP.UQ.UR.US.UT.UU.EG.UV.UW.UX.UY.Ej.pL.UZ.U0.U1.Co.r5.U2.U3.U4.U5.U6.U7.U8.U9.V..V#.Va.Gn.IAQtR.cF.Vb.Vc.Vd.Tq.Ve.Ve.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.L..Vf.Vg.RI.Vh.at.at.at.at.at",
+"QtbQtbQtb.#yQtSQthQth.#RQtVQtVQtVQtV.bOQtV.aF.c8.bWQteQtp.at.Du.fJ.y..bc.ba.Vi.dN.y#.EX.zC.Br.Dp.Dn.Dj.E7.Lc.hc.Vj.Q..Vk.g6.a..sW.cT.bH.Vl.Vm.qp.kg.os.OC.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.ns.mw.Vn.cU.Vo.Vp.kg.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.kf.ns.kf.rf.pt.bH.h3QtEQtE.h3.wo.Ov.HC.Vq.sR.cj.es.a0QtEQtEQtEQtEQtE.kb.MZ.kf.mw.mw.kf.MZ.OC.Vr.DP.h3QtF.i4.tv.Ls.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.vj.wf.wf.wf.vj.wf.g9.koQtF.ch.LB.Vs.sW.sW.ck.aY.QhQtEQtK.aZ.kwQtFQtK.a0.#7QtK.#7QtK.h3.cT.ro.Vt.zJ.Vu.Vv.Vw.Vx.Vy.Vz.pV.#z.VA.VB.pk.FA.VC.M8.pNQtE.ln.tv.vj.wf.wf.vj.yjQtEQtK.r7.rk.ki.ki.ki.rk.up.ou.ps.ke.kf.kf.kf.kf.mw.mw.mw.mw.mw.mw.mw.kg.ke.Dy.h3QtEQtEQtEQtEQtEQtE.cl.h3.h3.h3.ch.bL.wo.g8.g8.j#QtE.sY.cj.VD.VE.VF.VG.VH.g7.my.k..VI.VJ.Sk.dQ.T7.dP.dP.dP.VK.VL.VM.VN.VO.VP.VQ.VR.VS.VT.VU.VV.VW.VX.VY.VZ.V0.V1.V2.V3.V4.V5.V6.V7.V8.V9.W..W#.Wa.Wb.Wc.Wd.We.Wf.Wg.ai.#w.#w.dE.dE.aE.aE.#A.#A.ai.Ec.Co.Ej.Wh.Wi.pL.CK.Wj.Wk.#y.#A.dE.#R.bi.bi.aE.dD.#x.#x.eU.Wl.Gw.r5.Co.UG.SW.IC.UI.Wm.Wn.Wo.CK.Ef.Wp.Wq.xM.Wr.wE.Ws.Wt.Ml.zO.Ef.IE.Wu.Wv.sw.sx.AI.t#.c2.o9.yT.ni.Mm.Ww.Cq.r5.KG.KH.t3.UP.wE.xM.Wx.Wy.Wz.IZ.WA.WB.WC.WD.Ef.WD.WE.U0.WF.Iy.CK.WG.WH.WI.WJ.WK.WL.WM.WN.WO.WP.S0.WQ.Oe.xL.cw.WR.WS.WT.WU.Ve.WV.MM.Jc.Jc.Jc.Jc.Jc.Jc.MM.L..WW.WX.WY.#C.at.at.at.at.at.at",
+"QtVQtV.WZQthQtSQtSQtc.bO.#Q.#QQtV.bO.bOQtk.aI.aLQteQtp.vc.w4.fJ.W0.W1.W2.dN.eI.ya.D#.W3.Jm.Fk.Df.Hg.Jq.FL.W4.W5.Jw.lm.a#.dv.wn.cX.sQ.OT.W6.ns.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.vk.OB.lm.W7.OC.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.os.mw.kc.i6.g9.k#QtEQtE.h3.h3.W8.W9QtN.X..mr.bHQtEQtEQtEQtF.ot.kg.mw.mw.mw.kf.kg.kf.X#.rj.XaQtKQtE.JY.JW.tw.tw.JK.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.Dz.#6.bH.Xb.Xc.Xd.jj.LB.uz.uz.eq.dw.Xe.Xf.aY.Xg.Xg.aY.Q#QtG.bH.bHQtE.bH.a0.Qh.g..Xh.h..cW.lF.a4.Jy.TN.Xi.Xj.oB.gH.Xk.Xl.g0.Xm.cSQtE.mr.vj.vj.wf.wf.vj.ykQtE.#6.sN.r6.r7.tx.rn.kc.Qc.rh.rg.qp.ps.kh.mw.kf.kf.kf.mw.mw.mw.mw.mw.mw.pu.ts.Xn.k#.bHQtEQtEQtEQtE.k#.cl.h3.h3.h3.ch.bL.wo.g8.g8.fd.kn.cj.Xo.Xp.Xq.Xr.Xs.kn.a#.Xt.Xu.eS.uM.dR.dR.dQ.dQ.dQ.dR.Xv.Xw.Xx.Xy.Xz.XA.XB.XC.XD.XE.XF.XG.XH.XI.XJ.XK.XL.XM.XN.XO.XP.XQ.XR.XS.XT.XU.XV.XW.XX.XY.XZ.X0.X1.X2.#w.#w.#w.#w.aE.aE.#A.#A.#A.ai.Ga.Co.Eu.X3.IE.Eu.pL.X4.#g.#S.dh.#w.bi.#R.aE.bi.#x.bh.#x.uO.X5.Ei.r5.Co.X6.IC.yU.X7.S4.X8.pL.Eu.r5.X9.Y..Y#.CM.CM.Wr.Ya.Yb.zO.CK.IE.Yc.tb.Wv.xF.xF.o9.R#.niQtRQtR.fZ.Yd.Co.Ej.Ye.Ri.Mp.uS.Yf.Yg.Yh.Yi.Yj.PH.WA.Yk.Yl.hV.r5.Ym.Yn.Yo.Yp.Yq.Ef.Yr.Ys.Yt.Yu.Yv.Yw.Yx.Yy.Yz.YA.YB.Va.yU.YC.YD.YE.YF.Vf.YG.PZ.Ve.MM.MM.Jc.Jc.Jc.Jc.Tq.L..YH.YI.YJ.vc.at.at.at.at.at.at.at",
+".#Q.#Q.bOQtSQthQtS.#SQtV.#Q.#QQtV.bOQtV.aI.aLQteQtp.#C.w4.eS.#c.W1.YK.eM.Bn.ya.YL.YM.BA.Jn.Oo.E4.YN.FL.Fc.YO.rdQtN.kn.sf.cXQtK.i8.YP.vk.kg.kg.kg.kg.kg.kg.kg.kf.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.Qa.ur.nM.YQ.Ty.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.kg.ns.mu.un.#6.h3QtE.bHQtE.yl.a..cf.YR.a0QtE.bH.bH.g9.nr.kf.mw.mw.mw.kf.kf.mw.ou.ki.kc.Sd.j..h3QtF.JL.Lt.tw.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.wf.YS.mW.kn.qy.RR.sf.YT.TP.Vv.YU.YU.YU.Vv.Vv.Vv.Vv.Vv.YU.YV.tK.uzQtF.bH.bHQtK.YW.Xh.zJ.Vu.BM.bG.bL.a0.h3.Ou.uj.YX.YY.YZ.T3.Y0.Y1.bHQtF.rm.tw.wf.wf.vj.i4QtE.#6.tr.sN.sO.sO.sO.sN.mt.zL.kc.ri.Tz.lu.ps.ke.kf.kf.kf.kf.mw.mw.mw.mw.mw.kg.mw.lnQtE.bHQtEQtEQtEQtE.k#.h3.h3.h3.h3.ch.bL.wo.g8.lG.ds.cj.Y2.Y3.Y4.Y5.Y6.Y7.cV.Y8.Y9.Z..#e.uM.uM.uM.uM.uM.uM.Z#.Za.Zb.Zc.Zd.Ze.Zf.Zg.Zh.Zi.Zj.Zk.Zl.Zm.Zn.Zo.Zp.Zq.Zr.Zs.Zt.Zu.Zv.Zw.Zx.Zy.Zz.ZA.ZB.ZC.ZD.ZE.ZF.ZG.#w.#w.aE.aE.aE.aE.#A.#A.#A.ai.Ec.Co.Iy.Ej.pL.Eu.Eu.Ej.ZH.#c.#w.#R.bi.dh.aE.bi.#x.#x.#x.AJ.ZI.Ei.r5.Cq.ZJ.ZK.ZL.ZM.ZN.ZO.CK.Ef.r5.ZP.ZQ.KJ.ZR.Y#.ZS.ZT.ZU.zO.CK.UN.ZV.KI.tb.Wv.vK.R#QtTQtRQtSQtSQtS.ZW.Co.r5.KG.KH.t3.IN.y1.X7.ZX.EI.ZY.Mt.ZZ.Z0.Z1.Z2.Z3.Z4.Z5.Z6.Z7.Z8.Z9.0..0#.0a.0b.0c.0d.0e.Yy.0f.0g.0h.S0.tB.0i.0j.0k.0l.0m.0n.PZ.Ve.WV.MM.Jc.Jc.MM.K8.0o.0p.0q.0r.0s.at.at.at.at.at.at.#C.#C",
+".#Q.#QQtVQtSQthQtS.dhQtV.#QQtV.bOQtV.aF.c8QtfQtp.#C.vb.Dt.y..#f.YK.dN.HH.zp.YL.Bp.Fm.Dc.Dh.E6.Lc.c0.0t.0u.h5.0v.b8.kw.ab.uo.or.MZ.kf.rk.kc.mt.pp.rf.Tx.kj.Li.ki.kh.kg.os.kg.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.MY.RT.jh.0w.x#.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.ns.ot.ms.#6QtEQtF.0x.hVQtI.j2.kl.#6.bH.bH.#6.rl.kg.mw.mw.mw.kf.kf.kh.mu.0y.r7.sN.tv.DyQtK.h3QtF.OF.yk.wf.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.0z.wf.JZ.Xh.tI.0A.TF.BM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.rBQtEQtF.#7.YU.po.vh.r#.TP.YU.BM.epQtF.qr.aa.g..h2.TL.0B.0C.0D.h3QtK.OF.vj.vj.wf.vj.i4.h3.aZ.vj.sO.tv.tv.tv.tv.0E.sN.sN.r8.zL.rj.ki.qp.ps.mw.kf.kf.kf.kf.mw.mw.mw.mw.os.qo.ntQtE.bHQtEQtEQtEQtE.cl.h3.h3.h3.h3.ch.bL.h3.k#.0F.cV.0G.0H.0I.k#.k#.0J.0K.0L.0M.n1.yR.yR.0N.#e.#e.0O.0N.0P.0Q.0R.0S.0T.0U.0V.0W.0X.0Y.0Z.00.01.02.03.04.05.06.07.08.09.1..1#.1a.1b.1c.1d.1e.1f.1g.1h.1i.1j.1k.#w.#w.#w.aE.aE.#A.#A.#A.#A.ai.Ga.Cq.Eu.IE.1l.zO.Eu.Iy.vv.1mQt3.#w.bi.aE.aE.bi.#x.eU.uO.yT.KB.Ei.r5.CK.1n.1o.1p.Ye.Ej.Ej.Ef.zO.1q.1r.1r.1s.Y..Y..KJ.1t.S3.zO.Ej.UN.1u.1v.KI.tb.1w.Oe.Mm.vJQtSQtSQtS.1x.Co.Ej.X4.1y.t3.PD.Ra.wG.1z.1A.1B.CR.1C.1D.1E.1F.1G.1H.1I.1J.1K.1L.1M.1N.1O.1P.1Q.1R.0c.1S.1T.1U.1V.1W.1X.1Y.1Z.10.11.12.13.0n.PY.PZ.WV.MM.MM.Jc.L..14.15.t..RJ.at.at.at.at.at.at.at.at.#C.#C",
+".#Q.#Q.bO.#yQtSQthQt..#Q.#Q.bO.cu.aFQtW.bWQte.#C.v5.16.Cs.bc.T7.qS.yd.zE.17.BC.Fm.18.Dh.19.Ld.2..2#.TL.2a.2bQtKQtF.#7.kb.X#.YP.Lt.kkQtK.bHQtE.h3.h3.h3.cl.koQtK.2c.lv.kb.rh.mw.kg.pu.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.2d.ur.re.sN.MZ.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.ns.lu.Dy.2e.2f.cU.lm.cj.pD.RRQtE.#7.bH.2g.lr.mw.mw.mw.mw.mw.ke.lu.lp.r8.sO.tv.sO.BJ.wgQtE.h3.k#.g9.i4.Sb.tw.tw.Sc.vj.vj.vj.vj.vj.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.2h.Nc.W8.2i.2j.TR.sX.HJ.2k.BM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YU.aY.cl.#7.b8.vh.Vt.Xc.BM.YV.Vv.Xb.2l.#6.eq.jb.h6QtD.2m.2n.g..OU.h2.ch.JW.tv.wf.vj.ykQtEQtF.BJ.tw.vj.vj.vj.tw.tw.tv.tv.0E.sN.r8.rn.0y.rk.ou.pr.kf.kf.kf.kf.mw.mw.mw.kf.ns.rf.koQtEQtEQtEQtEQtE.k#.cl.h3.h3.h3.ch.chQtE.j#.cZ.2o.2p.wa.2q.g8.g8QtE.2r.2s.2t.m8.2u.sp.sp.yR.2v.0N.sp.JQ.2w.2x.2y.2z.2A.2B.2C.2D.2E.2F.2G.2H.2I.2J.2K.2L.2M.2N.2O.2P.2Q.2R.2S.2T.2U.2V.2W.2X.2Y.2Z.20.21.22.#w.aE.aE.aE.aE.#A.#A.#A.#A.ai.Ec.Co.Ej.23.24.25.pL.Ef.Ef.vv.26.#w.dE.bi.aE.#x.#x.eU.AJ.Em.27.28.29.Ct.Gl.Gp.Gp.Gl.Ei.3..3#.3a.3b.3c.3d.ZQ.3e.GE.3f.3g.3h.3i.Cp.Ye.3j.UP.S1.t3.IA.OfQtR.vJQtSQtSQtS.3k.3l.X4.3m.3n.t5.Ra.1X.3o.3p.3q.3r.3s.S9.3t.UY.PL.3u.3v.3w.3x.3y.3z.3A.3B.3C.3D.3E.1R.3F.Yv.3G.3H.3I.3J.3K.3L.3M.3N.3O.3P.3Q.0m.YG.PZ.Ve.3R.K8.K7.3S.3T.3U.Vh.at.at.at.at.at.at.at.at.at.at.#T",
+".#Q.#QQtV.#AQth.#y.bO.#QQtVQta.aF.aI.cvQte.#C.v5.w5.v8.yf.ba.qS.hH.zr.zw.qu.Bs.EZ.Dk.19.Ld.TE.TV.Vt.qIQtFQtKQtE.Vl.ot.lp.lvQtF.h3.h3QtEQtEQtEQtEQtEQtEQtEQtEQtE.k#.h3.h3QtF.j..qn.zK.kg.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.uq.3V.3W.lq.kg.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.W6.kg.mw.mw.sJ.ib.3X.lmQtI.lm.po.3Y.bH.Dz.ns.kf.mw.mw.mw.mw.kh.lu.rj.r8.sO.tv.tw.vj.tw.ms.OF.g9.bH.h3QtK.wg.yk.TY.Lt.JV.qq.wf.wf.vj.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.rf.3Z.30.bK.lD.31.Vt.lD.a..32.Xb.YV.Vv.YV.YV.BM.BM.BM.BM.33.BM.BM.BM.BM.BM.YV.YV.Vv.YU.a3.a0.dw.zJ.zJ.g..BM.BM.34.Vv.TP.k#.ch.sR.kv.35.g8.#6.j#.lx.zJ.sc.nO.ms.sO.tw.rm.bH.bH.36.vj.wf.wf.wf.wf.vj.vj.tw.tv.tv.sO.sN.r7.sP.ki.qp.pq.mw.ls.kf.kf.mw.mw.mw.kf.kg.i4.h3.bHQtEQtEQtEQtE.k#.cl.h3.h3.h3.ch.37.h6.g#.38.39.4..wo.lF.lF.k#.es.4#.4a.h9.4b.VJ.2u.2u.4c.4c.vG.4d.4e.4f.4g.4h.4i.4j.4k.4l.4m.4n.4o.4p.4q.4r.4s.4t.4u.4v.4w.4x.4y.4z.4A.4B.4C.4D.4E.4F.4G.4H.4I.4J.4K.aE.aE.aE.aE.#A.#A.#A.#A.#A.ai.4L.yu.4M.4N.Eb.ai.4O.Cu.Is.4P.4Q.4R.#R.bi.aE.#x.eU.uO.xD.R#.4S.4T.4U.4V.Ye.4W.23.4X.Ye.Ye.4Y.4Z.40.41.42.42.43.3d.3e.44.45.46.Go.47.48.IN.uS.49.5..ha.MmQtR.vJQtSQtS.5#.5a.5b.5c.5d.IC.Va.5e.5f.5g.5h.5i.5j.5k.5l.5m.5n.5o.5p.5q.5r.5s.5t.5u.5v.5w.5x.5y.5z.5A.5B.5C.5D.5E.5F.5G.5H.5I.5J.3O.5K.5L.5M.0n.PY.PZ.Tp.5N.5O.5P.0r.#C.at.at.at.at.at.at.at.at.at.at.#C.5Q",
+".#Q.#Q.#Q.#DQth.#AQtaQtV.bOQtk.aF.cvQtfQtp.v5.MO.v6.w6.YK.c4.xv.EX.zw.P6.Fm.Dc.E2.5R.Lc.5S.5T.5U.cT.aZ.bHQtE.LA.mt.5V.bH.h3QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.k#.h3QtK.qn.qp.os.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.kd.qG.5W.ou.kg.mw.mw.mw.mw.mw.kf.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.lt.5X.5Y.hV.nN.lm.a#.a..lm.Xc.rz.kh.os.mw.mw.mw.mw.pq.rk.rj.r8.sO.tv.tv.vj.vj.tw.tw.OT.JW.yk.oq.OF.2c.qr.g9QtD.g9.g9.g9.2c.ln.BJ.vj.vj.wf.wf.wf.wf.wf.wf.wf.vj.Tx.5Z.50.lD.ro.0A.fc.51.5U.lD.52.a#QtM.TP.BM.BM.53.w9.dv.nx.kn.kn.kn.ja.dv.h..YW.54.TO.BM.YV.TP.bH.sf.Vt.Vt.sX.tJ.YV.Vv.Vv.Vv.sf.k#.dy.sX.BM.YV.qE.Qh.oP.RR.sW.km.mW.55.2h.vj.a0.bH.36.vj.wf.wf.wf.wf.wf.wf.wf.vj.tw.tv.sO.sN.r8.kc.56.qp.pq.mw.mw.kf.kf.kf.mw.mw.kg.rk.#6QtEQtEQtEQtEQtEQtE.k#.cl.h3.h3.h3.a4.57.ml.58.59.ch.g8.j#.j#.k#.Vn.6..6#.Hc.6a.6b.6b.6b.4b.VJ.VJ.6c.6d.6e.6f.6g.6h.6i.6j.6k.6l.6m.6n.6o.6p.6q.6r.6s.6t.6u.6v.6w.6x.6y.6z.6A.6B.6C.6D.6E.6F.6G.6H.6I.dE.aE.aE.aE.#A.#A.#A.#A.#A.#A.ai.6J.6K.4U.6L.n0.#z.n0.6M.Cp.Ed.6N.6O.dW.bi.o..#x.uO.uQ.Em.NR.6P.6Q.6R.1p.6S.6T.6U.6V.6W.6X.6Y.6Z.60.61.61.62.41.42.Ey.63.64.65.66.X9.67.y1.uU.AL.wk.IA.niQtRQthQtSQtS.5#.Md.68.69.7..IB.X7.7#.7a.7b.I0.7c.7d.EB.7e.7f.7g.7h.7i.7j.7k.7l.7m.7n.7o.7p.7q.7r.5z.5A.3F.7s.5D.7t.7u.7v.7w.7x.7y.7z.7A.12.5M.WT.Vd.7B.7C.Ts.YJ.dH.#C.#C.at.at.at.at.at.at.at.at.at.at.#F.7D",
+".#Q.#Q.#Q.dhQtS.7EQtVQtaQta.aF.Jj.bWQtp.vc.L#.##.y..gE.eO.fE.7F.zw.Bq.M4.Dc.Dh.E6.7G.7H.7I.7J.um.aZ.bHQtE.bH.ntQtK.h3QtEQtEQtEQtEQtEQtE.h3.h3.h3.h3.h3QtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.nt.BI.7K.kg.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.Qa.Ll.lD.wg.ns.kf.kf.kf.kg.kg.ke.nr.kh.kf.os.kg.kf.kf.mw.mw.mw.mw.mw.mw.kf.ke.7L.Y8.vv.HJ.lm.bJ.oo.cj.a2.7M.mw.ns.mw.mw.mw.kh.lu.rj.r8.sO.tv.tv.tw.wf.wf.7N.vj.vj.tw.tw.tw.vj.wf.JV.Nc.rm.ms.Dy.i4.yk.7O.wf.vj.wf.wf.wf.wf.wf.wf.tv.Nc.7P.kn.ja.XeQtF.g9.7Q.7R.7S.Vt.cU.a..Xc.TP.ck.kn.lD.Vt.vh.Vt.Vt.vh.vh.vh.Xh.Xh.Vt.zJ.sX.OU.7T.YV.sf.2b.7U.Vt.7V.TP.BM.Vv.Vv.Vv.7W.7X.TA.bK.7Y.YV.Vv.Vv.7Z.uz.g8.cI.Vt.zJ.70.JV.lvQtE.36.Sc.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.tv.tv.yi.r8.tt.rh.qp.pq.mw.mw.kf.kf.mw.mw.mw.ns.TY.h3.bHQtEQtEQtEQtEQtE.k#.cl.h3.h3.cX.71QtI.kx.bH.h3.g8.g8.g8.OG.72.73.74.bS.eS.6a.6a.6a.hK.dL.dL.75.76.77.78.79.8..8#.8a.8b.8c.8d.8e.8f.8g.8h.8i.8j.8k.8l.8m.8n.8o.8p.8q.Oq.8r.8s.8t.8u.8v.8w.8x.8y.aE.aE.aE.#A.#A.#A.#A.#A.#A.#A.ai.8zQtO.8A.8B.n0.#z.aE.8C.8D.8E.8F.8G.8H.bi.bh.#x.uO.AJ.Em.NR.8I.8J.8K.1p.8L.A0.WP.8M.8N.8O.8O.8P.8Q.8R.8R.8S.8S.41.8T.8U.4Y.8V.8W.8X.8Y.uV.8Z.Va.zU.ha.niQtR.vJQtS.vJ.80.Md.68.81.7..Cz.82.ZT.83.84.85.86.87.88.89.9..9#.9a.9b.9c.9d.9e.9f.9g.9h.9i.9j.9k.9l.1R.9m.9n.9o.9p.9q.YA.9r.9s.9t.9u.9v.9w.9x.9y.9z.9A.9B.Vh.at.at.at.at.at.at.at.at.at.at.at.at.at.#C.9C.9D",
+".#Q.#Q.#QQt.Qth.#R.cu.bOQtk.aF.aLQte.#C.w4.dT.y..bc.bR.UE.yb.zt.Bp.Bz.Jn.Fh.Hh.7G.9E.zJ.9FQtFQtFQtEQtEQtE.fdQtEQtEQtEQtE.9G.uA.cY.ch.nt.wg.Qk.OL.JL.pt.ntQtK.cl.lF.Qm.woQtE.bHQtEQtE.bHQtE.k#.uo.Tx.W6.kg.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.zM.9H.9I.kj.Qa.kg.qp.tu.Xn.JY.sQ.kk.oq.i7.ty.rg.kf.kg.kf.mw.mw.mw.mw.mw.9J.9K.9L.qJ.9M.9N.iSQtN.cV.a#QtN.2i.lu.MZ.kf.mw.ke.qp.ri.9O.sO.tv.tv.vj.wf.wf.wf.wf.wf.wf.wf.vj.vj.vj.vj.vj.vj.tw.tw.tw.tw.Sc.vj.vj.wf.wf.wf.wf.vj.sO.RT.kv.9P.oD.cS.k#.cY.g8.9Q.9R.9S.Xh.lm.a#.ku.mC.Vt.Vt.zJ.Oz.zJ.zJ.zJ.Oz.lD.9T.TR.zJ.zJ.Vt.Vt.vh.9U.54.aY.lE.RM.Vt.0v.9V.YV.33.YV.Vv.0A.mD.a2.r1.33.YV.Vv.34.Vv.Xb.tK.g8.sf.tI.2j.tG.9WQtE.rm.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.tv.tv.sO.r8.or.rh.qp.kh.ts.mw.kf.kf.mw.mw.kg.rk.#6QtEQtEQtEQtEQtEQtEQtE.k#.cl.h3QtK.w9.cj.9X.cX.bL.wo.wo.g8.k#.mm.9Y.9ZQt3.Z..Z..Z.Qt9.eS.eS.eS.90.91.92.93.94.95.96.97.98.99#..#.##.a#.b#.c#.d#.e#.f#.g#.h#.i#.j#.k#.l#.m#.n#.o#.p#.q#.r#.s#.t#.u.aE.aE.aE.#A.#A.#A.#A.#A.#A.#A.ai#.v#.w#.x#.y.a7.aE.#w.bT#.z#.A.6L#.B#.C.bi.#x.#x.uO.o1.R#.AK.8I.8J.6W.1p#.D#.E#.F#.G#.H#.I#.J.66#.K#.L#.M#.M.8R.8S.41#.N.4Y.8V.66#.O#.P.wG.PA.IB.yU.Oe.niQtRQtRQtSQtS#.Q.Md.68#.R#.S.t4.AK#.T#.U#.V#.W.IR#.X#.Y#.Z.9.#.0#.1#.2#.3#.4#.5#.5#.6#.7#.8#.9##.###.1R.9m.9n##a##b.9q.3J##c##d.IC##e##f##g##h##i.#F.YJ.#C.at.at.at.at.at.at.at.at.at.at.at.at.#F##j.#P.ec##k",
+".#Q.#Q.#QQtbQtc.#w.cuQtV.aI.c8Qtf.#C.v5.Jk.#g##l.W2.eI.y#.zr.zA.M4.Dc.Oo.Tv##m##n.Xh.0A.aZ.bHQtEQtEQtEQtEQtEQtE.bH.Lk.Qm##o##p##q##r##s##t##u##v##v##w##x.zq##y##z##A.MX.ch.lFQtE.bHQtEQtEQtE.h3QtE.wg.mt##B.kg.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.mw.kg##C.ku##D.Li.i4##EQtE.h3.h3.k#.h3.h3.h3.clQtK##F.Vm.ps.os.kf.mw.mw.mw.X###G##H##I##J##K.lq.Qj.HJQtN.myQtN.bM.Sb##L.OC.kh.mu.rh.tx.sN.tv.tv.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.2h##M.7I##NQtG.lF.Ou.YV.#8.3Y##O.yc##P.OK.a#.lm.cU##Q.lD.po.sa.h5.dx.nM.dx.h5.jh.sR.cZ.bK.po.zJ.9T.zJ.Vt.Xd.0A.2n.se.Vt.lD.9P.mC.YW.Xb.BM.BM.qy.mT.lD.TP.YV.Vv.Vv.Vv.Vv.Vv.YV.#8.cl.aa##R.vh.Vu.Qk.tv.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.tv.tv.yi.r8.kc.ki.mu.ke.kf.kf.kf.mw.mw.kf.ns.Xa.h3QtEQtEQtEQtEQtEQtEQtE.k#.lG.bH.kxQtI.71.kw.ch.bL.wo.woQtE.DP##S##TQt3.v#.MO.MO.MO.MO.MO.Z..Z.##U##V##W##X##Y##Z##0##1##2##3##4##5##6##7##8##9#a.#a##aa#ab#ac#ad#ae#af#ag#ah#ai#aj#ak#al#am.aE.aE.aE.#A.#A.#A.#A.#A.#A.#A.#A.ai#an#ao.5b#ap.k3.#w.#w.#w.Eh#ao#aq.6L.Cv.n0.dD.eU.#z#ar.AK#as#at#au.6W#av#aw#.E.YA#ax#ay#az.8O#aA#aB#aC#.L#.L.8R.60.8S#aD#aE#aF#aG#aH#aI.y4.1X.WQ.Gn.ha.niQtRQtRQtSQtS#aJ#.A#aK#aL#aM.yV.wC#aN#aO.Yi.Rl#aP#aQ#aR#aS#aT#aU#aV#aW#aW.9h.9h.9h.9h#aX#.8#aY#aZ###.5A.9m.7s##a#a0#a1.7u#a2.wF.Mp#a3QtX.fL#a4Qt1.dU.#C.5P.0s.at.at.at.at.at.#C.#C.#C.#C.5P.#E#a5#a6#a7#a8.d5",
+".#Q.#Q.#QQta.#yQtb.cpQtk.aI.bWQte.w3.Du#a9.y..#i.eM.zo.zF.zw.Br.Do.Dl#b..Ld.Nj.Vt.tK.#6.bHQtEQtEQtEQtEQtE.bH.h3.uA#b##ba#bb#bc#bd#be#bf#bg.w4#bh#bg#bg#bf#bi#be#bj#bj#bk#bl#bm.bH.wo.bHQtEQtEQtEQtEQtEQtF.ln.Qc.kg.kf.mw.mw.mw.mw.mw.kf.kg.ot#bn.nt.DyQtM.ac.dw.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.h3.nt.Lj.qp.lr.kf.mw.mw.9J#bo#bp.e8#bq.vk#br.kj#bs.cUQtNQtN.sR.4..mw.lr.ou.ki.rn.sN.sO.tv.tv.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.r8.Vo.Vt.qy.qi.3Y.Vv.Vv#bt.Ou#bu#bv.Vi#bw.HJ.a#.a#.a#.52.h5.cU.a#.a#.a#.a#.a#.a#.a#.a#.a#.lm.nN.dx.ku.sd.TR.se.fe.a5.Vt.Vt.Vt.Vt.Vt.se.0v#bx#by.54.g6.kn#bz.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.qE.k##bA.OU.jc.54.BJ.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.tv.sO.sN.r8.lp.rk.ou.mw.mw.kf.kf.mw.mw.kg.zKQtF.bHQtEQtEQtEQtEQtEQtEQtE.rA#bB.h5.dx#bC.a4.h3.ch.ch.bL.k##bD#bE#bF#bG#bH.v#.v#.v#.v##bI.v#.MO#bJ#bK#bL#bM#bN#bO#bP#bQ#bR#bS#bT#bU#bV#bW#bX#bY#bZ#b0#b1#b2#b3#b4#b5#b6#b7#b8#b9#c.#c##ca#cb.aE.aE.#A.#A.#A.#A.#A.#A.#A.#A.#A.ai#cc#ao#.B#ap.n0.#w.#w.#R#cd#ce#.B.6L#cf#cg.#x#ch.AJ#ci.NR.Cz#at.8J.6W.1p#cj#ck.40#cl#cm#cn#.I#co#cp#cq#cr#.L#cs#ct.40#cu#cv.65#cw.X9#cx.0h.PA.Mk.Gn.ha.xLQtR#ciQtSQtR#aJ#cy.5a#aL#aM.t4.AK#cz#cA.B.#cB.3s#cC#cD#cE.9.#cF#cG#cH#cI#cJ#cK#cJ#cJ.5u#cL#cM#cN#cO.5B.0c.7s#cP#cQ#cR#cS.xN.Wr.t5.tb.sx.fq.uP.bs.bS.#C.5P.0s.at.at.at.at.at.#C.#C.#C.#C.DH#cT#cU.#J.#N#cV#cW",
+".#Q.#Q.#Q.#t.#AQtbQtV.aI.cv.#P.#C.vb.Dt.Cs.bc.gI.ye.yb.17.Da.Db.Df.E3#cX#cY.sX.a3.aZ.bHQtEQtEQtEQtE.bH.cl.uA#cZ#c0#c1#c2#bi#bg#be#bi#bi#be#c3#bi#c4#c5#c5#c5#c5#c5#c5.w4#c3.vb#c6#c7.uAQtE.bHQtEQtE.bH.bHQtE.rA.i7.qo.kg.kf.mw.mw.kf.kg.zK.g9.h3QtE.bH#c8.tI.BM.h6.cXQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.g9.36.nr.os.kf.kf#c9#d.#d##da#db#dc#dd.kg#de.xa.oQ.J0QtN.kv.rm.mw.kd.kc.r8.sO.tv.tv.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tv.tv.mm.vh.qI.LB.YV.YV.YV.lx.9I.7I#df#dg#dh#di.h5.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.dx.po.zJ.oO.mC.zJ.Vt.Vt.Vt.Vt.Vt.sd.MV.fe.9XQtI.7I.9F.BM.YV.Vv.Vv.Vv.Vv.Vv.YV.Vv#by.j#.cl.eo.a2#dj.kj.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.tv.sO.sN.tx.rh.qp.pq.ls.kf.kf.kf.mw.kf.kg.lvQtEQtEQtEQtEQtEQtEQtE.fd.#6.9F.g6#dk.h3.er.h3.h3.h3.ch#dl#dm#dn#do#bG#do#do#do#dp#dp#bH#bH#bH#bH#dq#dr#ds#dt#du#dv#dw#dx#dy#dz#dA#dB#dC#dD#dE#dF#dG#dH#dI#b2#b2#dJ#dK#dL#dM#dN#dO#dP#dQ.aE.aE.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.ai#an#ao#.B#dR#dS.tm.#w.#w.#s#dT#.A#aq#dU#dV.bh#dW.AJ.Cy.NR#dX.8I#dY.6W.1p#dZ#ck#d0#d1#d2#d3#.I#d4#.I#d5#d6#d7#d8#d9#e.#e##ea#eb#ec.8X#ed#ee.NU.WQ.Gn.Oe.niQtRQtSQtRQtj#ef#eg.5a#aL#eh.t4.AK#ei#ej.5h#ek#el#em#en.GJ#eo#ep#eq#er#es#es#es#et#eu#ev#aW#ew#ex#ey.1R.5B#ez#eA#eB#eC#eD.xN#eE.Mp.tb.sx.fq.uP.o8.bS.#C.5P.0s.at.at.at.at.at.at.at.at.#T#eF#eG#eH.#L#eI#eJ#eK",
+".#Q.#Q.#QQtV.aE.bO.aF.c8.bWQtp.at.Du.#g#eL.#h.gF.y##dh.zA.Bz.Hf#b.#eM#eN.0v.Qh.aZQtEQtEQtEQtE.bH.a4.#6.ov#eO#eP.C9.9R#eQ.Vi#eR#eS#eT#eU#eV#eW#eX#eY.w4#bj#bi#c4#c5#c5#c5#c5#c4#be.Tt#eZ.ch.h3.bHQtEQtE.bHQtF.bL.lw.kk.mt.kg.kf.mw.kf.ts.wg.h3.bHQtE.bH.aZ.2n#e0#e1.k#QtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.2c.rf.kf.OC##B.X##e2.S#.DF#e3#e4.9J.MZ.Ty.mw#e5.FC.dt.sX#e6#e7.kc.kc.r8.sO.tv.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.Ls#e8.71.se.kv#by.YV.BM.TN.nx.TR.Vt.Xh.kn#e9#f.#f#.dx.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.nM.se.0F.zJ.zJ.Vt#fa.Vt.Vt.Vt.zJ.Vt#fb.sX.lm.vh.g..dv#fc#bt.BM#fd.Vv.Vv.Vv.34.Vv.TP.ch.lG.sW.pw.4.#fe.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tv.tv.sO.mt.r6.ki.kd.mw.mw.kf.kf.mw.kf.ns.rf.bHQtEQtEQtEQtEQtEQtE.bHQtF#ff.cj.eo.kwQtE.h3.h3.h3.h3.ug#fg#fh.fL#bG.dG#fi#fi#do#do#do#do#do#do#fj#fk#fl#fm#fn#fo#fp#fq#fr#fs#ft#fu#fv#fw#fx#fy#b1#fz#dI#fz#fA#fB#fC#fD#fE#fF#fG#fH#fI.aE.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#w#cc#ao.5b#fJ.n0.#w.#w.#v.#w.Au#ao#aq#aq.Pw.vK.Vb.Cx.xD.49.Ra.8I.45.6W#av#dZ#fK.0g#fL#fM#fN#fO.6X#aA#fP#fQ#fR#fS#fT#fU#fV#fW#fX#fY#fZ#f0#ee.NU.WQ.Gn.Oe.niQtRQtRQtU.bi#f1.8F#f2#ap#f3.t4.82#f4#f5#f6.A2#f7#f8#f9.9.#g.#g##ga#gb#gc#gc#gc#gd#ge#gf#cL#ew.Z1#gg#gh#gi#gj#gk#gl#gm#gn#go.IC.Mp.tb.t2.su.uP.aG.bs.#C.5P.0s.at.at.at.at.at.at.at.at.#C.#C#gp#gq#gr#gs#gt#gu",
+".#Q.#Q.#QQtV.bOQtV.aI.aLQte.w1.vb.Dt.y..#j.c4.xv.OI.zw#gv.Dc.Dj.Hh#gw.lD.rvQtKQtEQtEQtE.bH.aZQtF.oE.4.#gx.Vk#gy.dy.dx#gz#gA#gB#gC.cW#gD#gE.aY#gF##D#gG#gH.zt.w3#be#c4#c5#c5#c5#c5#bg#bi#gI#gJ#gK.bHQtEQtEQtEQtF.lF#gL.rA.36.mw.kg#gM.DxQtE.bHQtEQtEQtEQtK#gN.qI.Vv.2bQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.i3.qp.lr.kg.ls#gO.e8#d#.ej#gP.X##gQ.kg.lt.Qa.mw.Tx#gJ#gR#gS.5V.Nc.tw.Ql.tw.tw.vj.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw#gT.lD.2j.ja.BM.33.YU#fb.sd.Vt.Vt.zJ.pF#gU#bg#gV.Xh.ml.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.pA#gW.zJ.zJ.zJ.zJ.lD.TR.zJ.Vt.Vt.mC.TA.dx.zJ##R.Vt.zJ.sW#gX.BM.YV.Vv.Vv.Vv.Vv.Vv#by.k#QtF.sd.h5#gY#fe.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.tv.tv.sN.rn.ri.lu.pq.mw.mw.kg.mw.kf.kg.otQtKQtFQtEQtEQtEQtEQtE.#7.tK.cU.ml.LBQtK.k#.cl.h3.h3.h3.rA#gZ#g0.wD.RI.Vh.Vh.dG.Vh.dG.dG.dG.dG.dG.dG#g1#g2#g3#g4#g5#g6#g7#g8#g9#h.#h##ha#hb#hc#hd#he#hf#hg#hh#hi#hj#hk#hl#hm#hn#ho#hp.aE.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.ai#.z.Gb#hq#hr.rJQtbQtbQtbQtb#hs#.B#.B#aq.Gv#ht.Vb.Cx#hu.49.Ra.8I.8J.66.1p#dZ#fK#ct#ck#hv#hw#hx#hy#hz#hA#hB#hC#hD#hE#hF#hG#hH#hI#hJ#hK#hL#hM#hN.WQ.Gn.ha.niQtU.xL.bh.dF.26.8F.5a#hO#f3.t4#hu.vN#hP#hQ.GG#hR#hS#f9.9.#hT#hU.7l#ga.7m#gd#gc#ge#gc#hV#hW#hX#hY#hZ#h0#h1#h2#h3#h4#h5#h6#h7.Cz.IN.KI.t2.su.uP.aG.bs.#C.5P.0s.at.at.at.at.at.at.at.at.at.9C#h8#h9.at.#E.#C.at",
+".#Q.#Q.#Q.bOQtV.aF.#BQtfQtp#bg#c4.#a##l.ba.fE.HH.zD.P6.Bu.Oo.Dj#i..lD.mD.aZ.bHQtE.bH.#7.cg.qy.Si.a2QtNQtNQtNQtNQtNQtNQtNQtNQtN.sR#i#.Qk.RR.Ov.OT.JL.oE.aY.oD#ia#ib#bi#be#ic#c5#c5#c5#c4#id#ie#if.uA.bHQtEQtE.bHQtF.h6.mD.ch.lv.qo.X#.JYQtEQtEQtEQtEQtE.bH.h3.TP.YV.BM.j#QtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtE.h3.RS.we.ns.X##ig#ih.pk.S#.ok#ii#ij.X##gQ.kf.OC.ns.MZ#ik#il.rm.Qk.JL.7O#im.JW.vj.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.vj.vj.wf.Vm.tv#in.ji.Vt.9U.YU.7Y.YW.9P.Vt.TR.lD.zJ.j2#io#ip#bi#iq#ir.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.sR.lD.lD##Q.cf.lf.ku.sd.zJ.zJ.sW.cU.sR.lD.Vt.Vt.Vt.Vt.zJ.mW.Xb#is.Vv.Vv.Vv#fd.Vv.Xg.cS.pM.cj.jc#e8.sO.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tv.tv.sO.r8.kc.ki.ou.ke.mw.kg.kf.mw.kf.ps.2c.bH.bHQtEQtEQtE.ab.lG.sd.cj#dk.lG.bHQtEQtE.k#.cl.h3.cS#it#iu.bV.5Q.RI.RI.RI.Vh.Vh.Vh#bG.Vh.Vh.Vh.Vh#iv#iw#ix#iy#iz#iA#iB#iC#iD#iE#iF#iG#iH#iI#iJ#iK#iL#iM#iN#iO#iP#iQ#iR#iS#iT.aE.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#AQtk#.zQtO#iU#aqQt9QtbQtbQtbQtb#iV#iW.4V.UD.8D#iX#iY.Cx#hu.49.Ra#.S#iZ.S3.28#i0#fK.WP#i1#i2#i3#i4#i5#i6#i7#i8#fT#i9#j.#fU#j##ja#jb#jc#jd#je#jf#jg.AL.yU.ha.xL.xL.uO.dFQte#jh.8F.5a#aL#ji.t4#hu.yU#jj.3q#jk#jl#jm#jn.Rs#jo#jp#jq#ga#ga.7m#gc#jr#js.7m#jt#ju#jv#jw#jx#jy#jz#jA#jB#jC#jD#jE#jF.Cy.KI.xF.su.aG.o8.bs.#C.5P.0s.at.at.at.at.at.#C.#C.at.at.#C#a5.#F.#F.at.at.at",
+".#Q.#QQtVQta.cu.aI.aLQte.at.vb.Dt.y..W1.c4.y#.P5.zC.Jm.EZ.Dh#jG.vs.h2.#6.bH.fdQtK.cl.qI.sRQtNQtN.kq.cm.qy.eh#jH#jI#jJ#jK#jL#jM.dxQtN#jN.5V.rz.2l.lp.MY.lq.qq.rz.mD#jO#jP#bj#bg#c5#c5#c5#c4#bd#jQ#jR.uA.bHQtEQtE.bHQtF.ep#jS.wo.JY.mt.2c.koQtEQtEQtE.bHQtFQtK.a3.7Z#jT.bGQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.k#QtF.Vm.ns.kg.9J#jU#jV#jW.S#.g0#jX#jY.kf#jZ.kg.kf.mw.pq.qp#j0.r7.sN.sO.tw.tv.vj.wf.wf.wf.wf.wf.wf.vj#j1.BI.lo.tw.vj.tw.vj.7M.po.Vt.Vt.9X.tJ#j2.sd.zJ.se.k..qw.mS.sg.53#e9#j3.ro.a#.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.lm.bK.9U.jc.lm.a#.a#.a#.dx#dk.5U.5U.a..cR.lD.Vt.Vt.Vt.Vt.Vt.vh.mC.M6.33.Vv.Vv.Vv.Vv.Vv#by.Jz.kuQtN#dk.5Z.tv.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.tv.tv.sN.r7.rh.qp.kh.kf.kf.kg.mw.kf.kf##F.bH.bH.bHQtEQtE.a0.lx.g6.g6#j4.g9.fdQtEQtEQtEQtE#j5.cS#j6#j7.mi.5Q.5Q.5Q.RI.RI.RI.RI.RI.RI.RI.RI.RI.RI#iv#j8#j9#k.#k##ka#kb#kc#kd#ke#kf#kg#kh#ki#kj#kk#kl#km#kn#ko#kp#kq#kr.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.au.#R.#s.Kz.6K#iU#aq.dTQtbQtbQtbQtbQta#ks.6K.Is.Cp#dV.bU.Cx#hu.49.uW#kt#f2.4U#ku#kv#kw#kx#ky#hF#kz#kA#kB#kC#kB#kD#i9#j.#fU#kE#kF#kG#kH#kI#kJ#kK#jf#kL.AL#kM.ha.Of.uO.dFQteQte#kN.6K.Cu#iW#kO.t4#kP.SW#kQ.5h.A2#kR#kS#kT.5w#kU#kV#kW#jq#ga#ga#ga#kX#kY#kY#kZ#.5#k0#k1#k2#k3#k4#k5#k6#k7#k8#k9#l.#ci.t3.xF.su.aG.o8.bs.#C.5P.0s.at.at.at.at.at.#C.#C.at.at.at.RI.#C.#C.at.at.at",
+".#Q.#Q.bOQtaQtV.c8QtfQtp.v5.Du.v6##l#l#.gJ.C9.zt.r0.Fl.Df#eM#la.uB.aZ.bH.bHQtK.3Y.ciQtNQtN.lf.ro#gN.i2.OE.rh.lq.zM.zM.MZ.x#.uq.OT.DB#lb.sR#lc.TC.tK#ld.ls.MZ.ns.lu.Dy#le#lf#lg#bj#c4#c5#c5#c5#be.W2#lh.uA.bHQtEQtE.bHQtF.bG.7Z.lF.mr.g9QtEQtEQtE.bH.cl.57.RR.sf.bG.YV.TP.chQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtE.bH.i6.ou.kg.9J#li#lj#lk.Qr.DF.pk#ll#lm#ln#c9#c9.ts.rl.ki.r6.r8.sN.sO.tv.tw.vj.vj.vj.M7#j1.7N#lo#lp#lq#lr.M7.tv.HK#ls.po.Vt.zJ.OU##N.ds.zJ.bK.lf.tM.cj.cj.cj.TA#lt.w0#lu.TS.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a2.kx#lv.hV.lm.a#.a#.a#.a#.cU.ds.2j.cU.zJ.lD.Vt.Vt.Vt.Vt.Vt.Vt.Vt.7V#gX.TP.YV.Vv.Vv.Vv.Vv#by.ro.g6QtN.TL.M7.Sc.wf.vj.vj.vj.vj.wf.wf.wf.wf.vj.tw.tv.sO.mt.sP.rk.ps.mw.kf.kf.mw.kg.kg.i2QtE.bH.bH.bHQtF.cW.lf.cV.qw#lw.cXQtEQtEQtEQtEQtE.lG.h7#lx#ly.c3Qt0Qt0Qt0.5Q.5Q.5Q.5Q.5Q.5Q.5Q.5Q.5Q.5Q#lz#lA#lB#lC#lD#lE#lF#lG#lH#lI#lJ#lK#lL#lM#lN#lO#lP#lQ#lR#lS#lT#lU.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#z.#A.#w.#w.c3.8z.6K.Is#aq.dTQtbQtb.miQtb.mi.SU.6K.Is.4P#lV.bU.Cx#lW.xD.uW#lX#f2.8A#ku#lY#lZ#l0#l1#l2#l3#l4#l5#l6#l7#l8#j.#fU#kE#l9#m.#kG#m##ma#mb#mc#md.Mk.Gn.zU#me.uO.rPQteQteQtp#kN.6N.Cu.6L#kO.t4#kP.y2#mf#mg.EG#mh#mi#mj#mk#ml#mm#kV#kV#kW#mn#kW#kW#jq#kW#mo#mp#mq#mr#ms#mt#mu#jB#h5#fU#mv#mw#mx#my.bV.t2.su.c7#a4QteQt2Qt2#mz.dH.at.at.at.at.at.at.at.at.at.at.#C.#C.at.at.at",
+".#QQtVQta.cpQtk.cvQte.#C.vb.Jk.Cs.#f.gI.gH.zF.zx.Jm.Fj.Oo#mA#mB.kw.fd.cX.cS.w9.cVQtN.a2.r#.cW.yk#mC.kg.os.kg.kf.mw.mw.mw.mw.kf.vk.zM.zN.reQtN.nk#mD.aY#mE.mw.kg.kf.vk.lq.LA.xa#mF#bg#bf#c5#c5#c5#bi.YK.Lm.uA.bHQtEQtE.bH.bH.aY.TP#mG.rA.bHQtE.bHQtF.mD.9V.Vv.oD.mD#by.Vv.sfQtF.bHQtEQtEQtEQtEQtE.bH.bHQtF.bH.bHQtEQtEQtEQtEQtE.bHQtE.h3.j..Qc.ns##B#jZ#mH#mI#mJ#d##mK.g0#mL#mM#mN#mO#mP.YP.rk#mQ#mR#mS#mT.sO#mU.Sc#mV#mW#mX#mY#mZ#m0#m1#m2.tv#m3.sU.po.zJ.zJ.zJ.Xd.lD.bK.lf.a#.cj.cj.cj.cj.cj.zJ#m4#m5.50.a..3X.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.lm##Q.ci.lm.a#.lm.lm.hV.lm.lm.a#.mC.ox.lm.jc.se.lD.zJ.Vt.Vt.Vt.Vt.zJ.Vt.zJ.YW.TP.YV.Vv.Vv.Vv.YV.YV.sR.my.a..0D#fe.vj.vj.tw.tw.vj.vj.wf.wf.wf.vj.tw.tv.tv.sN.kc.ki.ou.ke.mw.kg.kf.kf.kg.i9QtE.bH.bHQtFQtL.mC.cVQtI.ck.mr.bHQtEQtEQtEQtEQtE.ko#m6#m7.RJ.#r#a4#a4#a4#a4Qt0.bg.bg.bg.bg.bg.bg.bgQt0Qt0.bg#m8#m9#n.#n##na#nb#nc#nd#ne#nf#ng#nh#ni#nj#nk#nl#nm#nn.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#D.#wQtb.#w.#s#no#np.6N#aqQt8QtbQtb.miQtg.mi#nq#np.6K.6K#nr.td.Vb#lW.xD.49#ns.Go#nt.Q8#nu#nv#nw#nx#ny#nz#nA#nB#kB#nB#nC#nD#nE#l9#l1#nF#nG#nH#nI#nJ#nK##d.AL#nL.Gn.uQ.rPQte.o8QteQte#.v#gA.Ek#nM#nN.t4.uU#nO#nP.5h#nQ#nR#nS#nT.9h#nU#nV#mm#nW#nW#kW#kW#nW#nX#nX#nY#nZ#n0#n1#n2#n3#n4#n5#n6#n7#n8#n9#o.#o#.wD#a3QtW#oa#ob.P0#oc#od#oe#of.RJ.Vh.at.at.at.at.at.at.at.at.at.at.at.at.at",
+".#Q.bOQtaQtV.aIQtfQtp.at.Du.eS.w6.Tt.eM.ya.zD.r0.Bv.De#og.nk.lF.#6#oh.2b#oiQtN.cj.0v#gN.lv.zK.kg.kg.kf.mw.mw.kf.kg.kg.os.os.kg.kg.kg.x#.rn.vlQtN#oj#m3.rB.rz.qo.ns.kf.kg.x#.mt.h6#ok#ol#be#c5#c5#c5#be#om#b#.uA.bHQtEQtEQtF.g8#bz.YV.YT.ch.ab.bH.g8.BM.Vv.Vv.YV.oD.TF.Vv.Xg.ko.a4QtEQtEQtE.bHQtFQtE.lw.sf#on.bHQtF.bHQtEQtEQtEQtEQtEQtE.h3.#6.Li.ns.mw.X##oo#ln#op#oq#or.nm#os#ot#ou#ov#ow#ox#oy#oz#oA#oB#oC#oD#oE#oF#oG#oH.vj.Ql.M7.vj.0z#oI.dt.Xh.W9.po.zJ.zJ.Vt.rd.hV.cj.cj.cj.cj.cj.f7.a##oJ.#m#oK.TT.a.#oL.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.mS.vh.dx.a#.lm.lm.lm.lm.lm.a#.a2.ro#oM.a#.a#.mT.sR.0F.lD.zJ.Vt.Vt.Vt.sd.Vt.0F#oN.TP.YV.Vv.Vv.Vv#bz.OU.lm.HJ.HJ#if.sO.vj.vj.Dx.tw.vj.wf.wf.wf.wf.vj.tv.tv.sO.r7.rh.kd#ik.mw.kf.kg.kf.mw.OF.bHQtFQtF.g9#oO.a#.hV.r1QtG.aZQtEQtEQtEQtEQtEQtE.r2#oP#oQ.#x.cq.bT.bT#oR.bf#oR#a4.bg#a4Qt0#a4Qt0Qt0Qt0QteQt0QtfQtf.vI#oS#oT#oU#oV#oW#oX#oY#oZ#o0#o1#o2#o3#o4.#z.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#S.aEQtbQtbQtb.#w.#t#o5#o6.At#o7.s7QtbQta.miQtg.mi#o8#.C#o9#p..yZ.1w.bh.Cx#lW.49#p##pa#pb#pc#pd#pe#pf#pg#ph#pi#pj#pk#pl#pk#pm#nz#pn#po#mv#pp#pq#pr#ps#pt#pu.IB.AL.AL#pv.su.uP.aG.o8QtpQtp#pw.AB#px#py.Wv.t4.NR#pz#pA#pB#pC#pD#pE#pF#pG#pH#jp#nV#pI#pI#mm#mm#mm#mm#pJ#pK#pL#pM#pN#pO#pP#pQ#pR#l2#pS#pT#pU#pV#pW#pX#pY#pZ#p0#p1.WS.Vd#p2.Tq#p3#p4#p5.0r.dH.at.at.at.at.at.at.at.at.at.at.5P",
+".#Q.sq.bO#p6.#BQte.#C.vb.Jk.#g.yf.dS.fF.zF.zC.Jm.18.Dk#p7#p8#p9.RQ#q..jhQtN.cZ#q#.sQ.kj.kg.kg.kf.mw.mw.kf.os.mw.or.ty.BI.36.qn.BJ.rk.kg.Qa.OC#qaQtN.ru.7O.bG.bG#fe.mw.rj.rh.kf.kg.ko#qb#qc#qd#c5#c5#c5#bj.Jl.OF.h3.bHQtEQtE.cX.uz#bt.Vv.h2.k#.kw.cW.9F.Vv.Vv.Vv.YV.TH.YV.Vv.eq.cX.bHQtE.bHQtF.uz#by.Vv.YU.YV.3Y#mG.bH.abQtEQtEQtEQtEQtE.bHQtEQtK.r7.ns.kf.mw.X##oo.lq#qe#qf#qg#qh#qi#qj.S##qk#mK.Qr.S#.Qr#jW#ql#qm.qq#qn#mU.tv.tw.tv.sN#qo.ja.7U.zJ.pA.vh.9U.ci.kq.cj.cj.cj.cj.cj.cj.cj.cj#qp#qq#be#qrQtN.a#.cU.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.ku.ku.a#.lm.lm.lm.lm.lm.a#.a..7U#fb.g6.lm.a#.a#.a#.nM.re.lD.zJ.Vt.Vt.Vt.Vt.Vt.zJ.54.TP.YV.Vv.Vv.BM.54.cU.se.a2#oj.rf.vj.vj.vj.vj.vj.wf.wf.wf.wf.vj.tw.tv.sO.r8.lp.qp.kh.mw.kf.kg.kf.kd.ntQtF.#6.nt.72.dxQtI.r1.wn.#6.lGQtEQtEQtEQtEQtE.ko#qs#qt#qu.vH#m8#m8QtZQtZ.dF.dFQtZ.dFQtfQtfQtfQtfQtfQtfQtfQtfQtfQtfQtf.cv.ai#qv#qw#qx#qy#qz#qA#qB#qC#qD.#z.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aEQtbQtbQtbQtb.#w.#w.#w.#wQtbQtbQtbQtbQtaQtaQtaQtg.mi.mi.mi.ct.td.bV.1w#iY.Cx#hu.82.Of#qE#qF#qG#qH#qI#qJ#n8#qK#k8#qL#qL#qM#l1#pS#mv#qN#nw#qO#qP#qQ#qR#qS#qT.IB.AL#dW.AI.uP.aG.o8.o8.o8Qtp.aG.dF.vI.t#.o9.1v.AK#qU#qV#qW#qX#qY#qZ#q0#q1#q2#q3#jp#jp#jp#q4#q4#jp#q5#q6#q7#q8#q9#k5#r.#k6#fT#r##pS#nw#mw#ra#rb#rc#rd#re#rf#rg.13.5M.YG.PZ.WV.K8.Oj#rh#ri.#C.3U.#C.5P.0s.#C.0s.#C.0s.at.#C.at",
+"QtVQtaQtV.aI.aLQtm.w3#rj.Dt.y..gE.qS.ya.zD.zA.Bu.Dn.Hh.Fc.2.#rk#rl.lf.bJ#rm.ow.i7.ps.ns.kf.mw.mw.kf.kg.ou.lo.OF.bH.h3.h3.h3.h3.h3QtK.Xn.tt.ns#rn.OwQtN.kx.MX.rB.YT.LAQtF.nq.2c.qn.rn.kl#ro#rp#bj#c5#c5#c5#id#rqQtKQtE.bHQtE.bH.cX.dw.Vv.Vv.LBQtK.57.YU.Vv.Vv.Vv.Vv.bG.aY.9V.aY.bH.bHQtEQtF.cY.BM.Vv.Vv.Vv.Vv.Vv.9F#rr.umQtF.bHQtEQtEQtEQtE.bHQtEQtK.kc.ns.kf.mw.kf.kg#rs.RN#rt#oO#jI#ru#rv#rw#oG#rx.OQ.ej.ok.pk#ry#rz#rA#rB#mT.sO.tw.rz.lD.0K#rC.ic.cZ.lD.ku.hV.cj.cj.cj.cj.cj.cj.cj.g6QtN#rD#rE#rF.TA.cQ.lm.lm.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.kq.j2.nM.a#.lm.a#.a#.lm.lm.lm.ooQtJ.zJ.a..lm.a#.a#.a#.a#.lm.lf.vh.lD.Vt.Vt.Vt.Vt.Vt.cm.TO.BM.Vv.Vv.YV.Xb.qw.TA.sU.lm#rG#fe.7N.wf.wf.wf.wf.wf.wf.wf.wf.tw.tv.tv.r8.Qc.rk.ps.mw.mw.kf.kg.kcQtFQtF.j..7P.kR.cV.#9.2b.QtQtF.bHQtEQtEQtEQtEQtE.pN#rH#rI#rJ#rK.dF.dFQtfQtfQtfQtfQtfQtfQtfQtfQtfQtfQtfQtfQtfQtf.dF.aL.cqQtV.#x.#A.#A.aE.aE.aE.aE.aE.aE.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#y.#wQtbQtbQtbQtbQtb.#w.#w.#w.#wQtbQtbQtbQtaQtaQtaQtaQtaQtaQtg.mi.mi.ct.c2.1w.1w.bU.Cx.AK#rL#rM#rN#rO#qG#rP#rQ#rR#rS#n8#rT#rU#rV#rV#rV#rW#rX#rY#rZ#r0#r1#r2#md#hN.AL.NV.vI.dF.aG.o8Qtp.dH#r3#r3.gW#r4.su.t#.o9.Mp.IA#r5#r6#r7#r8#r9#s.#s##sa#q1#sb#sc#sc#sc#sc#sc#sd#se#sf#sg#k5#sh#pQ#si#k7#nE#qL#pf#sj#sk#sl#sm#sn#so.10#sp#sq.13.0n.PZ.PZ.MM.MM.Jc.L..Tq#sr#ss.RJ.at.at.#C#st.vc.at.at.#C.at",
+".bO.bO.aFQtWQtfQtp#bg.Jk.##.w6.#h.fE.zp.zv.Dq.Fk#su.5R#sv.P9.RY.h5.tM.M6.Qt.rf#sw.kg.mw.mw.kf.kg.mw.vi.2c.h3QtEQtEQtEQtEQtEQtEQtEQtE.h3.bH.wd.0y.Ty#inQtN.9Q.tr.YT.dwQtFQtEQtE.h3.bH.i1.a3#sx#sy#be#c5#c5#c4.#T##A.j#.bHQtEQtE.bH.bH.qE.Vv.Vv.sV.TH.Vv.Vv.Vv.Vv.Vv.TF.dw.Vv.YV.lwQtF.bHQtE.7Z.Vv#fd.Vv.Vv.Vv.Vv.Vv.Vv.YV.rB.chQtF.bHQtEQtEQtE.bHQtE.mr.zK.ns.kf.mw.mw.kf.Qa.rh##D#sz#sA.td#sB#sC#sD.yp.FA#sE.e8#d##da#sF#sG#sH#sI.ep.7V.zJ#sJ.MU.nN.re.J0QtNQtNQtN.cQ.cj.cj.cj.cj.cj.cj.mP#sK#c5#sLQtN.nN.ib.lm.lm.lm.lm.lm.a#.a#.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.g7.jc.a#.a#.a#.a#.lm.lm.a#.a#.jc.M6.ul.a#.a#.a#.a#.a#.a#.a#.a#.nM.cR.lD.zJ.Vt.Vt.Vt.Vt#sM.YV.YV.Vv.YV#q#.bK.g6.tK.mW.qw.7O.tv.wf.wf.wf.wf.wf.wf.wf.wf.tw.tv.tv.sN.kc.rk.ps.mw.mw.kf.kg.Xa.bH.g9.mD.HJ.a..kq.tJQtDQtF.bH.bH.bH.bH.bHQtE.ko.r2#sN#sO#sP.wB.aL.aL.dF.dF.dF.rP.dF.rPQtfQtfQtfQtf.dF.dF.aL.aL.aL.#BQtV.#D.#y.#A.aE.aE.aE.aE.aE.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#zQt..bOQtbQtbQtbQtbQtb.#w.#w.#wQtbQtbQtbQtbQtaQtaQtaQtaQtaQtaQtgQtg.mi.mi.mi.td.bV.bU.Cx.aJ#sQ#sR#sS#sT#sU#sV#sW#sX#qP#sj#rR#sY#rX#rX#rR#qP#mw#sZ#s0#s1#s2.y4#hN.IC.t3.sx.rP.aH.aGQtpQt2Qt2Qtp.o8.aG.aH.su.sw.dW.t4#s3#s4#s5.84#s6.7u#d1#d1#s7#s8#s8#s9#s9#sa#t.#t.#t##ta#tb#tc#k6#si#k7#fU#td#pS#nw#rY#pU#te#tf#tg#th#ti#tj.5K.5L.0m.0n.PZ.Ve.WV.Jc.Jc.MM.K8.0o#tk#tl#tm.at.at.at.at.5P.GY.vc#tn",
+".bOQta.aI.c8Qte.#C.vb.Dt.#g##l.bQ.zo.EX.zy.Bs.Dd.E3.Jr#sv#to.ku.nM.TP.OF.rn.ns.kf.mw.mw.kf.kg.rn#bn.h3QtEQtEQtEQtE.bH.bH.bH.abQtFQtFQtF.a4QtEQtK.36.mw#tpQtN#tq.Lj.0AQtGQtF.bHQtFQtFQtF.#6#tr.HM#ts#bi#c5#c5.w4#tt#tu.j#.bHQtEQtEQtF.cY.BM.Vv.0A.3Y.YV.Vv.Vv.Vv.34.0A.cg.BM.Vv#tvQtFQtF.rv.Xb#fd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.0A.cWQtF.bHQtEQtEQtE.bHQtE.g9.rn.ns.kf.mw.mw.mw.ns.rg.RR#jT#tw.c8Qtf.rP#tx#ty#tz#tA#tB#tC#tD#tE#tF#tG.OA.se.M6.ac.a..ic.g6QtN.iS.uy.pD.sR.nJQtNQtN.cj.g6QtN.mW##s#tH#tI.tM#tJ.J0.nN.nN.lm.a#.lm.a#.a#.lm.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.kq.km.cU.a#.a#.a#.a#.lm.lm.lm.a..dv.kv.a#.lm.a#.a#.a#.a#.a#.a#.a#.a#.52.jc.lD.zJ.Vt.Vt#tK.Vt#tL.0A.YV#fd.TP.sX.g6.2r.qi.ib#jN.tv.vj.wf.wf.wf.wf.wf.wf.wf.vj.tw.tv.sO.rn.ki.ps.mw.mw.ns.ppQtK.#6.a1.kq.cj.lf.2r.a0QtF.bH.bH.bH.bH.bH.bH.bH.cS.a4#tM#tN.uQ.cv.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.#BQtk.aE.#y.#A.aE.aE.aE.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#AQtb.bOQtaQtbQtbQtbQtbQtbQtbQtb.#wQtbQtbQtbQtbQtaQtaQtaQtaQtaQtaQtaQtaQtg.mi.mi.td.td.bV.vH#tO#tP#tQ#tR#tS#tT#tU#tV#tW#tX#tY#qQ#n9#tZ#tZ#qQ#tY#t0#t1#t2#t3#t4.5e.NR.tb.sv.rP.fq.aG.o8Qt2Qt2.bS.#T.o8.aG.dF.su.sw.tc#as#t5#t6#t7#t8#t9#u.#u#.7u#ua#d1#d1#s7#s7#s7#ub#uc#ud#ue#uf#ug#uh#ui#uj#po#rU#pf#qP#pU#uk#ul#um#un#uo#up#uq.3P.13.0m.YG.PZ.WV.MM.Jc.Jc.Jc.Jc.K9.0o#ur#us.w3.0s.bS#bi#ut#uu#uv#uw",
+"Qta.cu.#Q.cvQtm.at.va.w..#b.k2.c4.yc.zD.qu.Bv#ux.E4.Lc#uy#uz.sR.bG.yh.ot.ns.kf.mw.mw.kg.nr.i6QtFQtEQtEQtEQtE.bHQtF.bH.ch.um#c8.ep.sf#bA.ep.eq.um.g8#uA.rm.hh.nN.ur#uB.oD.k#.oE.bG.#8.QhQtEQtK#rr#uC#uD#bg#c5#c5#be#uE.9GQtEQtEQtE.bH.kw.bG.Vv.Vv.YV.YV.Vv.Vv.Vv.Vv.YV.ow.Ou.Vv.BM.ow.k#.0A.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Xb.Ou.h3QtF.bHQtEQtEQtEQtE.ff#uF.ns.W6.mw.mw.mw.ns.rn.Tw.2b#uG.aL.#C.aH.t#.vI#uH#uI#uJ#uK.c6.xv#uL#f#QtJ.9V.a2.nJ.cj.cj.mU.MX.Sd.rm#uM##M.50.sW.nM.cj.52#uN#j3.bM.cj.cU.J0.cU.a#.lm.lm.3X.lm.a#.3X.lm.a#.lm.lm.3X.a#.a#.a#.a#.a#.a#.a#.oo#uO.lf.a#.a#.a#.a#.a#.lm.a#.a#.h5.TO#uP.cV.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.qw.MV.9T.Vt.Vt.Vt.zJ.54.TP.Vv#bz.Xc.cj.Vu.mrQtJ.cj#sx.sN.wf.wf.wf.wf.wf.wf.wf.wf.tv.tv.sO.kc.ki.ps.mw.kf.nr.yh.lw.sc.lm.g7.oO.cW.QtQtK.bH.bH.bH.bH.bH.bH.bH.bH.9W#uQ#uR#uS#sP.#B.#B.#B.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.aL.#B.aF.dh.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aEQt#QtaQtaQtaQtaQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtaQtaQtaQta.dZ.dZQta.dZQta.dZQtg.mi.mi.td#uT.#y#uU#uV#uW#uX#uY#uZ#u0#u1#u2#u3#uk#uk#tW#ra#u4#uk#uk#u5#u6#u7#u8#u9.t4.t#.sx.su.aH.aGQteQt2.bS.w1.#C.#C.o8QteQtf.su.iI.Em.xg#v.#v##va#vb#vc#vd#ve#ve.3J.5F.5F.7u.7u.7u#vf.3I#vg#l2#vh#uj#pi#vi#vj#pT#sj#tZ#sk#vk#rb#vl#vm#vn#vo#vp#vq.5L.0m.0n.PZ.Ve.WV.Jc.Jc.Jc.Jc.Jc.Jc#vr#vs.vc#tn#vt#vu#vv#vw#vx#vy#vz",
+"QtaQtk.c8Qtf.bs.v5.w5.v6.w6.bb.gJ.zp.zv.Bq.EY.E2.E7.MR#vA#vB.h6.i0.ki.ns.mw.mw.mw.kg.pq.i1.h3QtEQtEQtE.bHQtF.g8.dw.qE.TP.9F.Vv.YU.YU.YU.YU.Vv.YV.0A.Ou.ow.nO.kq.ku#vC.kl#gF.BM.9V.9V.Vv.7Z.rv.oP.aY#vD#bj#c4#c5#c4#vE#jR.uA.bHQtEQtE.ab#vF.TP.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.cW.2q.Vv.Vv#mG.ep.YU.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.Vv.Vv#by.lwQtF.bHQtEQtE.bHQtE.#6.ri.ns.kf.mw.mw.mw.MZ.HK.TQ#vG.#TQtp.at.at.#CQtpQte.aG.aL#vH#vI#lv.dv#by.5U.cj.g6.g6#vJ.sM#e5.Qb.7O.Qb#im#e5.yk#vK.ic.7V#vL#vM.nJ.hV.a2.lf.lm.mP.hV.3X.a#.3X.3X.hV.lm.lm.hV.lm.a#.3X.lm.a#.lm.a#.a#.a#.HJ.k..lm.a#.a#.lm.lm.lm.lm.hV.a..sc#bx.lm.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.lf.MV.TR.Vt.Vt.Vt.g..YU#vN.TP#sMQtN.qF.g9.g5.cU#uO#vC.rf.vj.vj.tw.tw.vj.vj.wf.tw.tv.sO.rn.rk.pq.kf.MZ.sMQtF.vtQtM.LB.g8.#6.Qt.#6QtF.bH.bH.bH.bH.bH.bH.bH.cS.Y6#vO#vP.vH.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aI.cu.#DQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aEQt#QtaQtaQtaQtaQtaQtbQtbQtbQtbQtbQtbQtbQtbQtbQtbQtaQtaQta.aK.aK.aK.aK.dZ.dZ.dZ.dZ.dZ#vQ.o9.au#vR#vS#vT#vU#vV#vW#vX#vY#vZ#uZ#v0#v1#v2#v3#tf#ul#v4#v5#v6#v7#v8#v9.jS.sxQtY.dF.fq.aG.o8Qt2.w1.vc.dH.#C.0sQtpQte.aGQtY.Vb.zU.Gn#w.#w##wa#wb#wc.9r##c.1W#ve#ve#wd.3J.3J.3J#we#wf#qL#k8#wg#n8#wh#wi#qI#tZ#wj#ra#wk#ul#sm#un#vn#wl#wm#wn#wo.5L.0m.YG.PZ.Ve.MM.Jc.Tq#wp.Tp#wq#wr#ws#wt#wu#wv#ww#ww#wx#wy#wz#wA#wB",
+".bO.#Q.#BQte.#C.vb.Dt.v8.P4.YK.ye#dh#wC.Fm.Fj#b..Lb#wD.rp.rv.Qt.rn.ns.kf.mw.mw.kf.mw.oq.h3QtEQtE.bHQtFQtG.Ou.BM.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YU#wE.3Y.h..cj#rm.Dx.mD.YU.34.Vv.Vv.Vv.Vv.rB.k#.YT.9R#bj#c4#c5#bj#wF.uA.bHQtEQtE.bHQtF.dw.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.tK##o.h2.YU.Xf.3Y.Xb.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV#gN.bH.bHQtEQtE.bHQtE.sQ.qo.kg.mw.mw.mw.kf.ns#j5.Vv#wG.#B.#C.at.at.at.at.5P.aK#wH.qC#sM.BM#gX.cU.g6.cj.#5.yj.7O#m3.5V.Qk.JL.JL.OL.OL.JL.bM#wI#wJ#ir.f7.TA#bB.aaQtN.a#.hV.a#.a#.a#.a#.hV.a#.lm.a#.a#.a#.a#.lm.lm.lm.a#.a#.a2.j2.kq.lm.a#.a#.lm.lm.lm.lm.tM.dx#wK.VtQtI.lm.hV.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.sg.zJ.zJ.Vt#tK.Vt#wL#bz.7Y#wMQtN.w9.a0.lG.ciQtN.71.mt.Nc.yk.yk.ms.Lt.vj.tw.tv.sO.sN.kc.rk.mu.ki.2g.i0.bHQtF.aZ.nt##FQtKQtKQtF.bH.bH.bH.bH.bH.bH.bH.bH.oP#wN#da#wO.uQ.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtV.aEQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#y.#y.#y.#y.#y.#RQtaQtaQtaQtaQtaQtaQtaQtaQtbQtbQtbQtbQtbQtbQtbQtbQtaQtaQtaQta.aK.aKQtk.aK.aK.aK.aK.aK.dZ.dZ.mi.k6#wP#wQ#wR#wS#wT#wU#wV#vn#wW#wX#wY#wZ#vZ#vZ#um#w0#w1#w2#w3#w4#w5.s7QtY.su.aH.aG.o8.#T.#C.dH.w1#bi.vc.dH.w1.bS.o8.aG.mi.ni.xL.ha#w6#w7#w8#t5#w9.y4.y4#ee##c##c.1W.1W#ve#ve#vd#x.#qO#x##x##qI#xa#tZ#wj#tW#sU#u2#xb#um#un#vn#xc#xd#xe#xf#xg.13.0n.YG.Ve#xh#xi#xj#wp#xk#xl#xm#xn#xo#xp#xq#ww#xr#xs#xt#xu#xv#xw#xx",
+".cu.c8.aLQte.at.w4.eS.Cs##l.dS.y#.D#.zA.Bz.Dn.Di.Q..J0#jS.aZ.OT.ns.kf.mw.mw.kf.ns.Lj.h3QtEQtE.bHQtE.57.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#bz#sM.a..7P.yj.Vv.YV.Vv.Vv.Vv.Vv.Vv.sf.eq#xy.Fr.w4#c5#bg#xz.7O.h3.bHQtEQtEQtFQtE#by.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM#xA.ep.YU.Vv.Xb.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.OuQtEQtFQtEQtE.bH.h3.oq.W6.kf.mw.mw.mw.ns.lp.33#xB.vaQtp.at.at.at.atQte.an#xC#xD#xE.TP.kmQtNQtN#xF#xG.5V.JL.OL.wg.pt.OF.OF.j..j..rzQtJ#xH.ny.hV.ic.mD.wg#gC.jj.kqQtI.a#.hV.a#.a#.lm.a#.lm.3X.lm.hV.hV.lm.lm.a#.a#.cU.j2.h5.a#.lm.a#.a#.lm.a#.a#.lm.g6.Xc#fc.mT.a#.lm.lm.hV.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.lf.Oz.zJ.Vt.zJ.TR#fc#by.dsQtN.oOQtF.#6.sW.du.lf.RT.Tx.Sd.HK.i1QtK.g9.JY.i1##F##F.OL.pt.ntQtF.chQtEQtF.yh.sM.zK.ke.pt.bH.bH.bH.bH.bH.bH.bH.bH.bH.cS.kw#xI#xJ#xK#vQ.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtV.aEQth.#y.#A.#A.#A.#A.#A.#A.#A.#y.#y.#y.#y.#y.#y.#y.#y.#yQtS.#y.#yQtS.#yQt.Qta.aK.aKQtaQtaQtaQtaQtaQtaQtbQtbQtbQtbQtbQtbQtbQtbQtaQtaQta.aK.aKQtkQtkQtk.aKQtk.aK.aK.aK.aKQtq#xL#xM#xN#xN#xO#xP#xQ#xR#xS#wU#xT#vn#xU#xV#xW#xX#xY#xZ#x0#x1.sx.cq.aG.fq.aG.b5Qtp.bS.#C.dH.at.at.at.w3#bi.dH.#C.bj.wBQtSQtRQtR.ni#x2#x3#x4#qT.IB.Va.PE.y4#ee#ee#ee.0h#a2.Va#x5#sj#sZ#rP#wj#pU#x6#x7#sU#x8#v2#v2#x9#y.#y##ya#wn#vq#yb#sp.YF#yc#yd#ye#yf#yg#yh#yi#yj#yk#yl#ym#yn#yo#ww#xr#xr#yp#yq#yr#ys#xs#yt#yu",
+".aF.c8.bW.#F.v5#c4.dL.w6.#f.c4.yb.YL.P6.Bx.E0#yv#yw.TN.aZ.JX.kg.kf.mw.mw.mw.kf.ou.qrQtEQtE.a4.k##jS.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.YV.cZ.sR#e5#yx#jT.YV.Vv.Vv.Vv.Vv.BM.cg.57#yy#yz#c4#c4#bj#yA.Qm.bHQtEQtE.bH.aZ.a3.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YU.ow#qs.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.qE.bLQtFQtEQtE.bHQtE.sM.os.kf.mw.mw.kf.MZ#ld#yB#yC.aI.#C.at.at.at.aF#yD.r1.ac#by#tL.cjQtN.jl.Ov.oq.wg.OF.j..j..nt.g9.#6.#6.g9.Xb.7T#yE.ic.g6.2n.g9QtFQtK.g9.dw#yF.a2QtI.a..a#.lm.a#.lm.a#.lm.lm.a#.lm.lm.lm.a##lv.ku.a#.lm.a#.a#.a#.lm.a#.lm.tM.a2.TO.zJ.a..lm.lm.lm.lm.lm.lm.tM.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.sR.lD.zJ.Vt.Vt#yF.Xg.mCQtN.sY.ep.qr.iZ#yG.du.f9.0z.vj.vj.tw.36.lv.OF.g9.#6.#6.g9.OF.2e.yk.un.tr.Qc.ke.os.kg.ns.lnQtE.bH.bH.bH.bH.bH.bH.bH.cS#yH#yI#yJ#yK#yL.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtV.#DQth.#y.#A.#y.#y.#y.#y.#y.#yQtS.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.#AQtb.#tQtk.aK.aK.aKQtaQtaQtaQtaQtaQtaQtbQtbQtbQtbQtbQtbQtaQtaQta.aK.aKQtkQtkQtkQtkQtkQtkQtkQtkQtk.dZ#yM#yN#yO#yP#yQ#yQ#yR#yS#yT#yU#yV#yW#yX#yY#yZ#y0#y1#y2#y3.d2.#QQtfQtm.o8Qtp.o8.#T.at.vc.at.at.Hc.Hc.Hc.Hc.w3.vc.w1Qte.#zQthQtS.vJQtR.xL#me#y4.AL.AL.xg.Va.NU.PA.PA.NU.Yg.wF#y5#y6#y7#x6#x7#y8#sl#x8#u2#v2#sm#ul#y9#z.#z##za#zb#zc#zd#ze#zf#zg#zh#zi#zj#zk#zl.em#zm#zn#zo#zp#zq#zr#zs#zs#xr#zt#xq#zu#yt#yu#xr#zs#zs",
+".aF.cv.bWQtp.vb#c5.v6.W0.gE.eM.OI.zv.mA.Lo.De#zv.lD.kw.g9.lu.kg.mw.mw.mw.mw.ns.trQtE.fd.bH.37.3Y.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.7T.ml.uy.qq.TP.Vv.Vv.Vv.Vv.Vv.Vv.rBQtE.HM#ts#bg#c5#zw#zxQtKQtEQtEQtEQtEQtF.um.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.RR.wg.TP.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.0AQtGQtK.bHQtEQtE.bH.rf.vk.kf.mw.mw.kg.rk#bz#wJ.#FQtp.at.at.at.cu#zy#zz.TP.M6.h5QtN.g..55.OL.OF.2c.g9.#6QtKQtF.bH.k#.cl.j#.lx.Vu.re.cj.km.h6.ow#zA.cg.ow.bH.cX.cW.op#uP.cU.du.a..a#.lm.lm.lm.hV.lm.lm.a#.HJ.lD.cU.lm.lm.a#.lm.a#.lm.a#.lm.a..5U#zB.kq.oo.lm.lm.lm.lm.hV.g6.g6.hV.lm.lm.a#.a#.a#.a#.a#.a#.a#.lm.re.lD.zJ.Vt.lD.TO.lD.bJ.a5.7Z.bH.mU.bJ.oo.h5#if.tv.wf.vj.vj.tw.tw.M7.tw.tv.rn.lu.mw.kg.ns.kg.kg.kf.mw.pu.ns#zCQtE.bH.bH.bH.bH.bH.bH.cSQtF#zD#zE#zF#zG.o..#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtW.cu.auQth.#y.#yQtS.#y.#y.#y.#y.#y.#y.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#yQth.k6QtaQtkQtkQtkQtk.aK.aK.aKQtaQtaQtaQtaQtaQtaQtbQtbQtbQtbQtaQtaQtaQta.aK.aKQtkQtkQtkQtkQtk.s7Qtk.s7.s7Qta#zH#zI#zJ#yO#zK#zL#zL#zM#zM#zN#zO#zP#zQ#zR#zS#zT#zU#zV#zW.gVQtp.#C.bS.bS.0s.at.at.cE.cE.v5.vb#bg#bg.v5.v5.at.at.#TQtb.bu.ah.#yQtS.fZQtR.xL.Oe.Oe.Oe.Gn.xg.IB#zX#dX.PD.uS.Em#zY#wk#tU#x8#u2#tf#u1#zZ#z0#wZ#z1#z2#z3#z4#z5#z6.3O#yb.0k#z7#z8#z9#A.#A##Aa#Ab#Ac#Ad.bD.em.cN#Ae#Af#wx#zs#zs#zs#Ag#xr#xr#zs#zs#zs#zs#zs",
+".c8.gV.bW.#C#bg#c5.v6##l.YK#Ah.zF.zw.BA.Bz#Ai.pA.epQtF#Aj.ns.kf.mw.mw.mw.kf.ts.wgQtE.bHQtF#Ak.YU.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#fd.TP#j2.nM.5V.pN#jT.YV.Vv.Vv.Vv.Vv.BM.lF.eq#Al#id#c4.w4#c5#tu.j#.bHQtEQtE.bH.k##by.34.Vv.Vv.Vv.Vv.Vv.Xb.aY.lv.tK.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TP.uz.kw.bHQtEQtEQtK.Qc.ns.mw.mw.kf.MZ.pt.0A.zr.#B.at.at.#CQte#Am#An.0A.0vQtN##Q#Ao.uo.nt.g9.#6QtK.lG.cl.ow.cg.rz.um.mD#jT.g..a..a2.pM.uz.rv.rv.h6.h6.rzQtG.ow.lG.ch.bG.w9.po.cU.lm.3X.a#.3X.a#.a#.cU.nx.ul.a#.lm.a#.a#.lm.a#.a#.lm.a#.pA.9V.lD.a..lm.a#.a#.lm.lm.lm.g6.g6.a#.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.J0.po.lD.Vt.Vt.YW.Xh.a..h..TP#gN#Ap.cj.lm.du#Aq#fe.wf.wf.wf.wf.wf.tv.sO.r8.lp.qp.mw.kf.kg.mw.mw.mw.mw##B.ns.i6QtE.bH.bH.bH.bH.bH.r2#Ar#As#At#Au#Av.ma.aK.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aI.cu.dhQth.#y.#y.#y.#y.dg.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aEQta.aI.aIQtkQtkQtkQtk.aK.aK.aKQtaQtaQtaQtaQtaQtaQtbQtbQtaQtaQtaQta.aK.aKQtkQtkQtkQtkQtk.aI.aI.aI.aI.aI.w2#Aw#Ax#Ax#Ay#Az#AA#AB#AC#AD#AE#AF#AG#AH#AI#AJ#AK#AL##j.#F.#T.vc.0s.w1.at.vc.cE.w3#dp.vb#bg.v5.vb#bg.v5.v5.Hc.atQtk.as.ah.#A.ah.#yQtSQthQtR.Mm.ni.xL.Gn.Oe.uT.vL.vL#AM.vH#AN#AO#AP#AQ#zZ#AR#AS#AT#AU#AV#AW#AX#z##AY#AZ#A0.3O.7A#A1#sq#A2#A3#A4.dp#A5#A5#Ad#A6.bD.em#A7#A8#A9#B.#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".fX.cv#c2.#T.vb.16.v8##l.ba.fF.zF.zy.Fm.Dp#B#.VtQtF.j..kh.OC.mw.mw.mw.mw.lr.kjQtE.bHQtF.lF.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.kmQtC.qq.TP.Vv.Vv.Vv.Vv.Vv.Vv#tv.fd.0w#xz#bg#c4#bj#Ba.Qm.bHQtEQtE.erQtF.Ou.Vv.Vv.Vv.Vv.Vv.Vv.YV.YV.JL.ep.9V.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM#mGQtK.bHQtEQtE.j..ou.kg.mw.mw.kg.rg#bz#Bb.aI.#C.atQtp.#q.Ow#Bc.7T.cU.dx#bB.JY.g9QtK.bH.k#.ow#yx.oE.h6.rv.rv.b8.9V.dv.lm.lm.lx.cW.RR.RR#gN.g5.sf.e6.cW.rv.oE.cg.lG.#6.a0QtM.cj.a#.lm.lm.mP.a#.bK##Q.g6.mP.a#.a#.a#.a#.a#.lm.lm.g6.dv.54.lm.lm.lm.lm.lm.lm.lm.lm.lm.g6.g6.a#.hV.cU.a#.a#.a#.a#.a#.a#.a#.a#.h5.lD.zJ.0F.tI.re.a#.sU#Bd#e1.fe.g6.a#QtN#Be.kj.vj.wf.wf.wf.vj.tw.tv.r8.ri.kd.mw.kf.kf.mw.mw.mw.mw.kf.ns.i8QtE.bH.bH.bH.bH.r2.h7#Bf.Qr.e8.ol#Bg.vH.aF.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQta.#D.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#z.#y.#y.#w.aK.aI.aI.aI.aIQtkQtkQtkQtkQtk.aK.aKQtaQtaQtaQtaQtaQtaQtaQtaQtaQtaQta.aKQtkQtkQtkQtk.aI.aI.aI.aI.aI.aI.aK.x9#Bh#Bi#Bj#Bk#Bl#Bm#Bn#Bo#Bp#Bq#Br#Bs#Bt#Bu#Bv.3U#Bw.at.w3.v5.v5.v5#bi.cE.w3#dp#bg.vb.vb.w4.vb#bg.w4.vb#bg.vb.cq.dg.r..au.au.#A.#y.#yQtSQtSQtRQtR.aO.e4.vK.t#.ta.tc.dX#Bx#By#Bz#AT#um#wZ#wZ#BA#BB#BC#BD#BE#BF#BG#z6#A0#BH#vq#BI#BJ#BK#BL#BM#BN#BO#BP.bD.bD#A6.bD#A7#BQ#BR#zr#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+"QtW.aw.#P.#C.vb.Dt.y..W1.cr.gH.EX.zA.mA#BS.nN.pMQtF.kb.ns.kf.mw.mw.mw.mw.ns.ms.h3QtE.cX.rv.Vv.Vv.YV.BM.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.M6.TA#gY#BT#jT.Vv.Vv.Vv.Vv#fd.YV.cY.g8#BU#bj#c5#bi#BV.QvQtEQtEQtEQtEQtF.ep.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT#xA.kl.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.Vv.TP.oEQtF.bH.bH.h3.i8.kg.kf.mw.kf.MZ#BW.Xg.yb.aL.#CQte.Qf.LB#jT.ci.cj#BX.OFQtKQtF.cl.cg#zA.oE.rv.cW.e6.g5.cT.YV#yE#tJQtI.r#.a3.mD.mD.mD.cT.cT.bG.2b.f1.g5.ep.rv.oE.rz.rA.0A.cj.a#.lm.lm.oo.lf.re.a#.lm.lm.3X.a#.lm.a#.a#.lm.g6QtH.tJ##Q.g6.lm.a#.lm.lm.lm.lm.lm.lm.lm.a#.g6.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.lm.k..lD.zJ#yF.TA.cU.cI#Bd.7Y.ck.a#.a#.a#.ul.RL.2h.wf.wf.wf.vj.tv.sO.r7.rh#BY#BZ.kg.kf.mw.mw.mw.mw.lr.rh.a0.bHQtKQtF.cS.oP.59#B0#d##B1.FA#B2.aI.bW.aF.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.aI.bO.auQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.ah.bOQtk.aI.aI.aI.aI.aI.aI.aIQtkQtkQtkQtk.aK.aKQtaQtaQtaQtaQtaQtaQtaQtaQta.aK.aKQtkQtkQtk.aI.aI.aI.aI.aI.aI.aI.#t#B3#B4#B5#B6#B7#B8.J..CA.mi.xF.mi#B9#C.#C##Ca.st.w3.cE.w3.w3.cE.v5.Hc.vb#bg#bg.vb.v#.w4.Du.w4.Du.w4.vb#bg.w4#id.au.au.v4.k6.au.au.#A.#S.#y.#yQtRQtR.rK.sv.su.t#.xF.vK#Cb#Cc#sn#BB#vY#vm#un#un#Cd#xX#Ce#Cf#Cg#BG#AZ#zc#Ch#Ci#Cj#Ck#Cl#Cm#Cn#Co#BO#Cp#BP#BP.em#Cq.bD#Cr#Cs#Ct#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".c8.bW.#P.at.Du#a9.W0.#f.c4.BE.zE.zA.Jm#Cu#uP.nq.nt.nr.kg.mw.mw.mw.mw.kf.kg.ln.h3QtEQtF.uz.YV.34.YV.BM.YV.Vv.Vv.YV.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TP.Vw.TL.i4.YV.YV.Vv.Vv.Vv.Vv.Vv.rB.wo#Cv.v5#c4#c4#c4#Cw.uA.bHQtEQtE.a4QtG.YV.Vv.Vv.Vv.Vv.Vv.Vv#jT#zA.j..Xb.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.Vv.TP.7XQtFQtEQtE.bH.kj.ns.mw.mw.ns.kj#Cx.HD#p6.#C.aL#Cy.BM#CzQtN.sdQtKQtK.k#.cgQtG.oE.rv##D.g5.RR.eh.cT.TP#CA.jcQtN#ff.YT.f8#CB.pM#jS#jS.YT.b8.b8.72.cT.a3.RR.e6.rv.cg.op.cj.lm.lm.a#.lm#tJ.lm.a#.a..hV.a#.lm.lm.a#.a#.a#.cU.54.ro.a#.lm.lm.lm.a#.lm.a#.lm.lm.lm.lm.3X.g6.hV.hV.cU.lm.a#.a#.a#.a#.a#.a#.a#.mS.sd.lD.bI.a2.a2.qy.7Y.lE.h..a#.lm.lmQtN.Y7#fe.vj.wf.vj.tw.sO.sN.r6.ki.pq.7K.kg.mw.kf.kf.kf.kg.lu.j.QtE.cS#gC#yHQtK#CC#ry#d#.mH#CD#CE#xK.wZ.#P.aL.aF.aI.#B.#B.#B.#B.#B.#B.c8.aFQtb.#A.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQth.#AQta.aIQtWQtW.aI.aI.aI.aI.aI.aI.aI.aIQtkQtkQtkQtk.aK.aKQtaQtaQtaQtaQta.aK.aK.aKQtkQtkQtk.aI.aI.aI.aI.aI.aI.aI.aIQtV#CF#CG#CH#CI#CJ#CK.ET.G0.v4.G0.r..dhQtbQtb.#RQt#Qtf#bg.w4.vb#bg.vb.vb#bc.w4.Du.vb.Du.w4.w4.w4.Du.Du.v#.w4.vc.tm.au.dh.v4.v4.k6.k6.au.#A.#AQtc.vJ.bh.dF.aG.ma.su.wB.sw#CL#CM#CN#wW#wW#wW#CO#wW#CP#CQ#CR#CS#CT#z6#CU#CV#CW#CX#CY#CZ#C0#C1#C2#C3#BO.em#C4.bD#A7#C5#C6#wx#yu#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".cvQtfQtp.at.Du.##.#c.#j.gI.gH.zr#C7#C8.J0.pM.cX.Dz.ns.kf.mw.mw.mw.mw.kf.X#.oq.h3QtE.bH.k#.0A.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.BM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.TP.mC.Y1#bA.9V.Vv.Vv.Vv.Vv.YV#by.ch.55.Qf#xK#c5#bj#C9.tL.bHQtEQtE.bHQtE.0A#fd.Vv.Vv.Vv.Vv.Vv.Vv.a3.JL.TP#fd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#by.chQtFQtEQtE.i0.kh.kg.mw.kf.kh.Ou#D.#uDQte.#B#D##Da.h5.a#.LB.aZ#vF#zA.oE.h6.cW.sf#gN.eh.cT#Db.TP#Dc.bK.du.k..oD.pM.oD.oD#Dd.h2.h2.h2.pM#jS#jS.YT.b8.72.2b.e6.nO.qw.a#.lm.a#.cU.cU.a#.a#.cU.sX.g6.g6.lm.lm.a#.lmQtI.mC.TO#De.a#.lm.lm.lm.lm.lm.a#.lm.lm.lm.a#.a##Df.g6.g6.lm.lm.a#.a#.a#.a#.a#.a#.a#.hV.OK.sX.po.a#.sR.9V.BM.33.TN.nN.a#.lm.a.#Dg.BJ.tw.wf.vj.tv.tv.sO.sP.kd.kf#gQ#oo#gQ#gQ#Dh#Di#Dj#e5#j5.r2#Dk.JU#oP.qj.mH.OQ#da#Dl#Dm#Dn.aIQtfQtZ##j.c8Qtk.aI.#B.#B.#B.#B.#BQtkQt..#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aEQtVQtW.#B.#BQtWQtWQtW.aI.aI.aI.aI.aI.aI.aI.aIQtkQtkQtkQtk.aK.aK.aK.aK.aK.aKQtkQtkQtkQtk.aI.aI.aI.aI.aI.aI.aI.aI.aIQtk.aK.EV.MK.G0#Do.w2.w2.v4.v4.w2QtbQtbQtbQtbQtb.#RQt#.#F.Du#c4#c4.Du.Du.w4.w4.w4.w4.w4.Du#c4#c4#c4#c4.vb.#F.bO.v4.tm.dh.dh.v4.v4.v4.k6.au.ahQthQtb.o8Qtm.aG.KDQtZ.ma#Dp#Dq#Dr#Ds#Dt#uV#uV#uV#Du#Dv#Dw#Dx#Dy.7z#Dz#CV#DA#DB#DC#DD#DE#DF#C3#DG#BO#DG#BP#DH#A6#DI#DJ#DK#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".cv.#P.#C.at.Du#a9.w6.gE.qS.y#.D#.dz#DL#DMQtKQtF.rj.ns.mw.mw.mw.mw.mw.kf.os.yk.h3QtE.fd.cX.RR.Vv.34.Vv.Vv.Vv.Vv.YV.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#is.Vu.Vn#DN#jT.Vv.Vv.Vv.Vv.BM.tJ.um.h3#yC#bj#c4#be#j3QtEQtEQtEQtE.bHQtF.tK.Vv.Vv.Vv.Vv.Vv.Vv.YV.7Z.Qk.3Y.9F.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Xf.bH.bH.fdQtE.lo.ns.kf.mw.ns##E#Cx#DO.aI#vH#eS#DPQtN.ds.lG.2q.rz.h6.cW##D.RR.h2.qy#bx.sY#oN#wK.MV.cj.cU.sW.lx.TP.TP.TP.TP.0A.0A.oD.oD.h2.h2.pM#jS.YT.b8.aa.km.a#.lm.a#.nL.km.ml.lm.cj.dk.Ow.BL.g6.lm.lm.lm.cV.jc.BM.sd.a..lm.lm.lm.lm.lm.lm.a#.lm.lm.a#.a#.lm.hV.a#.g6.hV.cU.a#.a#.a#.a#.a#.a#.a#.a#.mS.MV.k.QtI.j2#wL#yF.YV.33.Vt.g6.lm.a##DQ#DR#mU.wf.tw.tv.sO.sO.vj.JV#DS#DT#DU#gO#DV#DW#DX#DY#DZ#D0#D1.VC#D2.DF.DF.li.g0#CD#D3#D4.o9Qtf.cv.gV.gV.aIQtk.aI.#B.#B.#B.aI.aKQt..ah.as.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#wQtk.wB.#B.#B.#B.#B.#BQtWQtW.aI.aI.aI.aI.aI.aI.aI.aIQtkQtkQtkQtkQtkQtk.aKQtkQtkQtkQtkQtk.aI.aI.aI.aI.aI.aI.aI.aI.aIQtWQtk.dh.G1.v4.w2.v4.v4.v4.dhQt.QtbQtaQtb.bOQtbQtaQtb.#R.aF.v5#c4.Du.Du.Du.Du.w4.Du.Du#c4.Du.Du#c4.v5.#E.bO.v4.v4.w2.w2.tm.dh.dh.dh.v4.v4.dg.qh.dZ.bS.#TQtpQtp.#E#D5#D6#D7#D8#D9#E.#E.#E##Ea#Eb#vW#Ec#Ed#Ee#Ef#Eg#Eh#Ei#Ej#Ek#El#Em#DE#En#Eo#Ep#Eq.em#Er#Es#Et#Eu#Ev#Ew#xr#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".aL.#PQtp.vc.Du.xG.w6.#i.dN.yd.zF.E6.qw.tKQtK.OF.mw.kg.mw.mw.mw.mw.mw.kf.kg.rn.bH.bHQtE.bH.k#.Xg.Vv.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#fd#is.9F##E.0A#fd.Vv.Vv.Vv.YV.YV.dw#Ex#Ey#be#c4#bg#Ez#EA.h3QtEQtEQtEQtF.dw.Vv.Vv.Vv.Vv.Vv.Vv#fd.YV.wg.#8.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Q#.Qi.bHQtE.qr.mu.kg.mw.ns.sM#EB.RO.aK.#T#EC.ku.hV.mx.ow.oE.cW.ep.a3.YU.sY.lD.se.Xh.5U#ED.5U.cj.hV.ci.TR.Vt.eo.uB.0A#bz#bz#bz.0A.0A.lE.9F.9F.M6.54#j2.sR.a#.mP.a#.#9#EE.sR.a#.a..lD.OF#EF.J0.cj.a#.cU.lm.nM.TN.r#.a#.lm.a#.lm.lm.a#.lm.a#.a#.lm.a#.a#.lm.lm.lm.lm.g6.g6.lm.lm.a#.a#.a#.a#.a#.a#.a#.lm.re.jc.cj#EG#yF.po.h.#by#EH.cU.lm.lm.a.#EI.sO.vj.tv.sO.r8.lp.rk#EJ.Vl#EK#EL#EM#EN#EO#EO#EO.FA.DF#EP#EQ.li.li#B1#ER#sE#ES#ET#vQ.bW.gV.gV.c8.aFQtkQtW.#B.#B.#B.aIQta.dh.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#AQtbQtW.wB.#B.#B.#B.#B.#B.#B.#BQtWQtWQtWQtW.aI.aI.aI.aI.aI.aI.aI.aIQtkQtkQtkQtkQtkQtkQtk.aI.aI.aI.aI.aI.aI.aI.aI.aIQtWQtW.aIQt..r..v4.w2.v4.v4.dhQtbQtaQtaQtbQtaQta.bOQtbQtbQta.#RQtbQtp.w4.w4.Du.Du#c4.w4.Du.Du#c4.w4#be.gVQt.#EU.v4.v4.w2.w2.w2.w2.w2.dh.dh.dh.au.r..vI.dG.at.#C.bS.RI#EV#EW#EX#yT#EY#EZ#yU#yU#E0#E1#E2#E3.st#E4#E5#E6#Ei#E7#E8#E9#F.#F##Fa#Fb#Fc#Fd#Fe#Ff#Fg#Fh#Fi#Fj#wA#Fk#Fl#yt#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+"#Fm.#PQtp.vc#c4.##.W0.#h.eI.hJ#Fn#Fo#lv.cSQtE.qn.ns.kf.mw.mw.mw.mw.mw.kf.kf.kh.OFQtEQtEQtEQtFQtG.33.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.tJ.cg.Ou.Vv.Vv.Vv.Vv.Vv.Vv.XfQtE.Ll#xz#bg.w4.#o#Fp.woQtEQtEQtE.cX.h6.Xb.Vv.Vv.Vv.Vv.Vv.Vv.Vv#xA.lw.9V.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.tJ.uz.cXQtE.h3.sM.ns.kf.kg.0y.YV#Fq.vcQtf.P7.g#.sX.cg.h6.cW.g5.dw#Ap.Vt.Xh.Vt.Vt.9T#Fr.OU.g6.cj.dx.Oz.zJ.Vt.Vt#wM#wL.ro#FsQtJ.h..mW#sM.9U.9U.sX.ci.kq.a#.lm.g6.qw.ck.2n.ib.g6.ox#gN.qr.r2.wcQtN.a#.lm.a..sX.OU.cU.a#.lm.a#.lm.lm.lm.lm.lm.a#.a#.lm.a#.lm.a#.a#.lm.lm.g6.lm.cU.a#.a#.a#.a#.a#.a#.a#.a#.a2#Ft.g6#Fu#yF.vh.mC.YV.TP.vh.cj.hV#Fv#Fw#Fx#Fy.sO.sN.rn.Tz.ou.kf.W6.ms#Ao#Fz#FA.e8.li#B1#B1#B1#B1.li#B1.li#Dl#FB#FC#Fm.s7.aw.gV#FD.c8.aF.aF.aI.#B.#B.c8.aF.ai.au.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aE.cu.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtWQtW.aI.aI.aI.aI.aI.aI.aI.aI.aI.aI.aI.aI.aI.aI.aI.aIQtkQtkQtkQtkQtk.#s.#s.aIQt..r..v4.v4.v4.dhQt.QtbQtaQtVQtaQtb.bOQtaQtbQtbQtbQtaQtbQt..bW.vb.w4.w4.Du.Du#c4#c4#bg.#P.sq.dh.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.k6.au.#R.#F.cE.cE.v5.Hc.dH.#T#FE#FF#FG#EX#FH#FI#FJ#FK#FL#FMQtZ#FNQtf.9C#FO#FP.9y#FQ#FR#FS#FT#FU#FV#FW#xu#FX#FY#FZ#Fi#wB#wB#F0#F0#wA#F1#B.#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".cv.#PQtp.at#F2.##.#c.Sk.eI.y##F3.Hq.7JQtDQtE.rf.ns.mw.mw.mw.mw.mw.kf.kg.kf.lq.JWQtEQtEQtE.lGQtF.rv.YV.Vv.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.sf#F4.Vv.Vv.Vv.Vv.Vv.Vv.TP.h3.bL#F5#bj#c4#c5#F6.uA.bHQtEQtE.a4.g8.BM.Vv.Vv.Vv.Vv.Vv.Vv#jT#F7#j5.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.oDQtEQtFQtE.#6.YP.kg.kf.X###D.f8.fH#F8#F9QtN.lE.uz.sf.RR.2b.cT.ro.Vt.zJ#tK.Vt.7J.dv#G..cj.g6.ic.lD.zJ.Vt.Vt.Vt.se.se.vh.Vt.vh.se.Vt.zJ.lD.ku.ml.oo.a#.a#.ji.7T.YV.eo.a#.kq#fc.2q.ow.g9.2b.hV.cj.a##De.sX.J0.a#.a#.a#.lm.lm.lm.lm.a#.lm.lm.lm.lm.lm.a#.a#.lm.lm.hV.hV.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.lm.lm.mT.ro.sX#tK.MV#e0.33.M6.0v.dx.f7#G##Ga#Gb#rB#Gc.ri.YP.kh.kf.kg.kg.Tx#Gd#D0.S#.li#B1.mH.mH#B1#ER#CD.ol#Ge#Gf.Vb.c8##j.gV.c8.aI.aF.aF.KE.#B.#B.aIQtkQt..dg.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#y.#w.aF.c8.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtWQtW.aI.aI.aI.aI.aI.aIQtkQtkQtkQtkQtkQtkQtkQtkQtVQtk.#sQtWQtW.aI.bO.au.v4.v4.dhQtbQta.bOQtbQtb.bOQtbQta.bOQtaQtaQtbQtbQtaQtb.#R.cv.w4#c4#c4.w4#bi.#P.#Q.w2.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.#t.vc.w4.vb#dp.cE.cE.vc.dH.YJ#Gg#Gh#Gi#Gj#Gk#Gl#Gm#Gn#GoQt1QtmQt1.#E#Gp#Gq#Gr#Gs#Gt#Gu#Gv#Gw#FZ#Gx#FX#FY#FY#F0#F0#F0#F0#F0#F0#wA#Fl#Gy#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".cv.#PQtp.at.w4.##.w6.Sk.eI.zo#ie#Gz.aYQtKQtF.ri.kg.mw.mw.mw.kf.kg.rk.lo.pv.ntQtK.bHQtEQtEQtE.bHQtF.Qh.aY.0A.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Xf.cg.BM#fd.Vv.Vv.Vv.Vv.YV.oE#GA#GB#bj#c4#bi#GC.Qm.bHQtEQtEQtF.lG.Xg.34.Vv.Vv.Vv.Vv.Vv.YU.ep.#6.BM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.Vv.57QtF.bH.h3.36.lt.mw.MZ.OF#Cx#c6.b5.oh#De.sf.g5.a3.cT.mD.oD.9P.zJ.Vt.Vt.sc.ro.J0.cj.g6.cj.cU.vh.cm.zJ.zJ.zJ.zJ.zJ.zJ.zJ.zJ.zJ.lD.lD.rd.hV.a#.a..nM.kn.TO#GD.Vv.mC.hV.se#bx.cW.h6.cgQtF.a5QtI.cU.0F.cf.a#.lm.lm.3X.lm.lm.lm.lm.a#.lm.lm.lm.lm.lm.a#.lm.lm.a#.lm.3X.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.lf.g..0F.lD.Vt##N.BM.YV#bz.ku.a#.Rt#GE#GF#oy#GG.rk#GH#c9#c9#c9#GI#GJ#GK#GL.li.li.li.Na.OP#GM#GN#GO#GPQtaQta.gV.gV.c8QtW.aF.aF.aI.#B.#BQtW.aIQta.aE.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#AQta.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtWQtWQtW.#sQtkQtkQtkQtkQtkQtVQtkQtkQtkQtkQtk.#s.bPQtWQtW.#B.aI.bO.v4.v4Qt..bOQtaQtaQtbQtb.miQtbQtbQtb.bOQta.miQtaQtbQtbQtaQtb.#RQtZ#c4.vb.#P.sq.G0#GQ#GR#GR#GR.Og#GS.G0.v4.w2.w2.w2.w2.w2.w2.w2.v4.v4QtZ#bg#c4.w4.w4.vb.vb.v5.at.w3.at.Vh.WY#GT#GU#GV#GW#GX.#FQtpQtpQtpQt1#GY#GZ#G0#G1#G2#vz#FV#G3#Gx#Gx#FX#FY#FY#F0#F0#F0#F0#F0#F0#F0#F0#xv#G4#Ct#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".aLQtfQtp.at.L#.#..W0.#h.dN.gF.Dj#dk#on.a4.g9.ou.OC.mw.kf.os.rk.msQtK.h3.h3QtEQtEQtEQtEQtEQtEQtE.er.bH.bH.k#.um.dw#Db.uB.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TP.ch.3Y.Vv.Vv.Vv.Vv.Vv.Vv#bA.h3#tG#vE.w4#G5#G6.QmQtEQtEQtE.bHQtF.LB#G7.Vv.Vv.Vv.Vv.Vv.Vv.Ou.Qv.0A.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.uB.umQtFQtE.ka.kh.kf.MZ#G8#G9#H.#H#.KR.nx.ep.2b.mD.b8.YT.2n.Vt#tK.Vt.lD.sc.dx.cj.g6.cj.cj.a..nM.rd.km.po.Vt.Vt.Vt.Vt.se.bK.km.32.a2.g6.a#.oo##Q#yE#Ha#wK#Hb.9V.ci#Hc.zJ.Xd.pM#bA.rv#vF.mU.a###Q.jc.a#.lm.lm.lm.a#.lm.lm.lm.a#.lm.lm.a#.a#.lm.lm.a#.lm.a#.lm.lm.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a##lv.TA.km.Xh.Vt.tI.33#fd.YV.cZ.nN#Hd.vg#He#Hf#Hg#Hh#Hi#Hj#Hk#Hl#Hm#Hn.g0.li.JT#CD.OP#Ho#Hp#Hq#bF#HrQtq.c8.gV.c8.aI.aF.aF.aI.#B.#B.#B.aIQtVQt..#A.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.dhQtk.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtWQtWQtWQtWQtWQtW.#Q.#sQtWQtWQtWQtWQtWQtW.#s.#s.#sQtWQtWQtWQtWQtWQtW.#B.#B.#B.#B.aIQtVQtbQtbQtbQta.bO.bOQtb.bOQtbQtaQtb.bO.bOQtbQtaQtaQtbQtbQtbQtbQtbQtb.bW.bW#GS#Hs#EU.CH#Ht#Hu#Hv#Hw#Hx.t0#Hy#Hz#HA.G0.w2.w2.w2.v4.v4.bO.#T.Du.Du.v..w4#bg.vb#bg.w3.v5.v5.at.at.at#bi.dH.#T.#C.w1.#C.#C.uP#HB.ma#HC#HD#HE#HF#vz#Gw#FZ#Gx#FX#HG#FY#FY#F0#F0#F0#F0#F0#wB#HH#F0#F0#wA#HI#HJ#wx#yt#HK#HL#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs",
+".aL.#P.bj.5P.vb.zG.W0.#i.qS.gI#HM.po.cXQtE.OF.mw.kf.kf.kg.Vm.a0.h3QtEQtEQtEQtEQtE.bH.bH.bH.bHQtE.bH.bH.bH.bH.a4QtF.cS.lF.bG.TP.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.uz.e6.Vv.Vv.Vv.Vv.Vv.Vv.TH.bH#gC#HN#bi#be#HO.uAQtEQtEQtEQtE.kw.#8.Vv.Vv.Vv.Vv.Vv.Vv.YV.Xg.g9#jS.Xb.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.3Y.rA.bHQtE.kj.ns.ns.rn#xE#HP#HQQtN.M6.eh.b8#HR.pM.qE.tI.Vt.Vt.Vt#j2.bKQtN.cj.g6.cj.cj.cj.cj.cQ.cj.a#.lm.nN.nN.nN.hV.g6.cjQtN.du.hV.sR.dv#wK#Fr.9X#HS#HT#wK.sR.lm.0F.Vt.ck.2b.sf.2n.lm.sR.re.lm.lm.lm.lm.hV.lm.a#.lm.lm.lm.lm.lm.lm.lm.lm.lm.lm.a#.lm.lm.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.hV.po.dx.oo.sg#oi.0v.9F.YV.BM.cR.ib.mU.pn#HU#HV#HW#HX#mK.Na.Ly#HY#EQ#HZ#GM.mH#H0#H1#Hq#H2.qZ.#S.cu.c8.c8QtW.aI.aF.aF.aI.#B.#B.#B.aIQtk.bO.k6.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#AQtb.aI.#B.#B.#B.#B.#BQtWQtWQtWQtWQtWQtWQtWQtWQtWQtWQtWQtWQtWQtWQtWQtWQtW.#B.#B.#B.#B.#B.#B.#B.#B.#BQtW.#B.#B.#B.#B.#B.#B.#B.#BQtW.aK.aiQtbQtbQtbQtbQta.bOQtbQtbQtaQtaQtbQtbQtaQtaQtaQtbQtbQtaQtbQtbQtb.w2#Hz#H3#H4#H5#H6#H7#H8#H9#I..iL#I##Ia#Ib#Ic#IdQtg#GS.w2.v4.dh.cv.vb.Du.w4.Du.Du.Du.L##bc#bc#bg.vb.v5.v5.v5Qt4.v5.w3.at.vc.GY.uP#Ie#If#Ig#Ih#Ii#Ij#Ik#vz#Il#Gx#Gx#FX#HG#FY#Im#F0#F0#F0#F0#F0#In#Io#Ip#Iq#FX#In#Ir#In#Is#It#Iu#zt#yt#HK#xr#zs#zs#zs#zs#zs#zs#zs#HL",
+".aIQtfQtp.at#Iv.#..y..gE.qS.cr#Iw.tI.a0QtE##F.kg.kg.kf#Ix.bHQtE.bHQtEQtE.bH.cS.ko.bL.cY.lw.g8.rA.g8.um.bHQtFQtEQtE.er.bHQtF.ch.Xe.YV.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#Iy#Iz.YV.Vv.Vv.Vv.Vv.34#jSQtF.tL#gI#bj#bi#IA.bHQtEQtEQtEQtEQtF.Qh.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.oP.57.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.YV.rv.kwQtE.i9##B#IB.ou#IC#ID#IE.52.pM.b8#jS.pM.h2.tJ.g..zJ.Vt.sX#IF.Vu.mC.ic.cQ.cj.cj.cj.cj.g6.hV.hV.g6.g6.cj.cj.cQQtN.lm#lv.Xd.9X#IG.h..Oz.hg#IH.pD#II.Vu#De.nN.Vt.Vt.Xc#jS.qF.cU.sg##Q.lm.a#.lm.g6.g6.g6.lm.lm.lm.3X.lm.lm.lm.lm.lm.a#.lm.lm.a#.lm.a#.lm.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.mP.cU.lD.cU.a#.J0.Vt.zJ.7T.BM.BM.re.TA.sf.mT#IJ#IK#IL#IM#IN#IO#IP#IQ#IR#IS#IT#IU#IVQtq#IWQtgQtW.c8QtW.aF.#Q.aI.aI.aI.#B.#B.#B.#BQtW.cu.#R.#S.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#AQtaQtW.c8QtWQtWQtWQtWQtWQtW.#QQtWQtWQtWQtWQtWQtW.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQtk.bOQtaQtaQtbQtbQtbQtaQtaQtb.bOQtb.bO.bOQtbQtb.bOQtaQtb.bOQtbQtbQtb.dh#IX#IY#IZ#I0#I1#I2#I3#I4.#L.#L.aA.#L.#L#I3.bp.#I#I5#I6#Ca#I7.vc#I8#c4.Du#c4.w4#c4#c4#c4.vb.w4.L##bc#bg#bg.v5.v5.v5.Hc.0s.jR#I9#J.#J##Ja#BN#Jb.em#Jc#Jd#Je#Gx#FX#FY#FY#FY#Im#F0#F0#F0#wA#In#Jf#Jg#Jh#Ji#Jj#Jk#Jl#Jm#Jn#Gx#Io#Je#Eu#Jo#Jp#Jq#zt#wx#yu#yu#yu#yu#wx#xr",
+".bOQtfQte.#C.vb.Dt.Cs.#j.gI.fH#IJ.53.#7.h3.oq.kg.lr.LjQtE.bH.bHQtEQtE.rA#Jr#Js#Jt#Ju#Jv.Vu#IF.lx.tI.g.#Fr.QhQtK.bHQtEQtEQtE.bHQtF.cW#Jw.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#G7.h2.lG#e1.34.Vv.Vv.Vv.Vv.TwQtE.Qm#Jx#G5#bg##x.ntQtEQtEQtEQtE.a4.lF.YV.Vv.Vv.Vv.Vv.Vv.Vv.YV.ch.ep#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.aY.bH.bHQtK.ki.lr.mw.dw.cT.gX.ji.Ou.pM.mV.oD#by#bx.0F.zJ.Vt.zJ.nx#CA#II##N.re.tM.cjQtNQtN.cQ.cj.cQQtNQtN.cQ.ml.TA.sXQtJ#wK.lx.tI.hh#IH.r5#IH#IH.hh#wK#yE.cU.J0#fa.Vt.knQtM.cU.h5.ci.lm.sR.g7QtI.cj.cj.cj.g6.lm.lm.lm.lm.lm.lm.lm.lm.lm.lm.a#.a#.a#.lm.lm.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.ul.sX.a#.lm.lm.cR.zJ.9X.7Y.9V.#9.lDQtF#uP#Jy#Jz.r0#JA.Nk.yf#eh.#C.rP.mi#JB.o..miQtkQtW.aI.#Q.aI.aI.aI.aI.#B.#B.#B.#B.#B.c8.aI.ai.au.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#y.#RQtkQtWQtWQtWQtWQtWQtWQtWQtWQtWQtW.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtW.aF.bOQtb.bO.mi.bO.bOQtbQtbQtaQtbQtbQtbQtb.bO.bOQtbQtbQtbQta.bOQtbQt.#GQ#JC#JD#JE#I1.#L.d4.aA.aA.aA.aA.aA.aA.aA.aA.aA.d4.aA#I1#JF#JG#Iv#c4#c4#c4#c4#c4#c4.Du.w4.w4.vb.w4.Du.w4.Du#dp.v5#bg.vc.uP#JH#JI#JJ.em#JK#JL#C2#C2#C2#JM#Fd#FX#FY#FY#FY#Im#Im#Im#F0#Io#JN#JO#JP#JQ#c4.5P.at.w3.at.ez.ez#JR#JS#JT#FZ#Io#JU#HG#JV#wz#xw#JW#JX#Jo#JY#xw#JZ",
+".dh.cv.#F.#T.v5#c5.#b.yf.ba.m5.Hu.OuQtK.k#.oq#J0.TZ.bHQtEQtEQtEQtEQtF#J1#J2#J3#J4.34#is.M6.mC.zJ.Xh.Vt.po.0v.es.bHQtK.bH.fdQtE.bHQtF.bL.TF.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.TP.k#.LB.Vv.Vv.Vv.Vv.Vv.TP.ch.h3#J5#uD#J6.YK#uM.h3QtEQtEQtE.cS.k##bz.Vv.Vv.Vv.Vv.Vv.Vv.Vv.cY.h6.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Xb.Qh.cXQtE.OT.ns.kg.sb.tIQtN.r#.YT.h2.h2.0A.0A.tI.Vt.Vt.Vt.Vt.Vt.5U#bx#GD#xE.YW.sY.se.jc.lf.lf.TA.k..sd.OU.ck#wK#zB.54.9V#wL.qz.rs#IH#IH.gi.kR.jc.7T.9X.hV#oM.0F.lD.ci.cU.#9.po.kq.nM.es.a3#J7.j9.g6.cj.cj.g6.a#.lm.lm.lm.lm.lm.lm.lm.lm.a#.a#.3X.lm.lm.a#.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.cV.j2.j2.uk.lm.a#.sR.zJ#fb.TP#bx.g6.r#.RS.ja.a2#J8.Bp.YL.hH.cr.an.w5.vc.#E.c8.aF.aK.aI.aIQtW.KE.#B.#B.#B.#B.#B.#B.#B.#B.aIQta.k6.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#A.bO.aI.c8.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.c8.aFQtaQtbQtbQtbQtaQtV.bOQtbQtbQtbQtb.bOQtbQtbQtbQtaQtaQtbQtbQt..r.#J9.r.#K.#K##Ka#Kb.d4.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA#Kc.aA#I2#Kd#Ke#c4#c4#c4#c4.Du#c4#c4.Du.w4.w4.Du.w4.w4.w4.vb.vb.iH#JR#Kf#Kg.aV#DG#C3#Cn#Kh#BN#C3#A6#Ki#Kj#FY#FY#Im#Im#F0#Im#F0#In#Kk#Kl#Km.dW.dZQtkQtWQtW.c8.cq.bW.aw.bW#Kn#Ko#HG#HH#F0#F0#wA#JU#Io#JU#JU#JU#JU#Io#JU",
+".v4QtVQteQtp.v5.Jk.v8.yf.gE.Lp#Kp.umQtFQtE.uo.rk.QtQtE.bHQtEQtEQtF.ch.aY.YU.Xb.Xb.Xb.YV.YV.9V.M6.tI.Vt.zJ.Vt.se.r#.rB.j#QtF.bHQtEQtE.bHQtF.ep.BM.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Xb.cg.dw.Vv.Vv.Vv.Vv.Vv.YV.cY.bH##o#eQ#bi#Kq#Kr.woQtEQtEQtE.bH.bH.qE.34.Vv.Vv.Vv.Vv.Vv.9V.eq.cg.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.3YQtF.k#.Xn.ns.ns#vF.vh.g6.lx.pM.oD.TP.TP.Xb.0v#fa.Vt.Vt.Vt.Vt.Vt.sXQtJ.lx#yB#wK.9V#Hb#xE#xE#xE.lx.9F#II#Fr.sc.k..rd.sW#wK#gX.sY#yw.rs#IH.gi.gi#yE#Fr.cU.kq.Oz.cR.lm.mS.0F.a2.cV.kv#Ks.g5.lE.ml.cj.BL.cj.cj.cj.g6.a#.lm.lm.lm.a#.lm.lm.lm.lm.a#.lm.lm.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.lm.a#.OU.jb.a#.a#.a#.sg.MV.mC.TP.OU.a..54.#7.Vu.cj.Hk.zA#eP.y#.Vi.P4.w5.vc.bW.gV.c8.aF#Kt.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQta.dh.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.aE.aK.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.c8.aFQtbQtb.miQtbQtaQtaQtaQtbQtbQtb.aF.bWQtpQte.c8QtaQta.dh#Hz#GQ.wB#Ku#Kv#Kw#Kx#I2#I3.d4.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.d4.#I#Ky#Kz.w4#c4#c4#c4#c4#c4.w4.w4.Du.w4.Du.w4.w4.Du.w4.GY#KA#KB#KC#A5#DG#C3#DG#DG#DG#KD#KE#KF#Ff#KG#Im#F0#F0#F0#KH#F0#wB#KI#KJ.wCQtS.#y.#y.#y.#y.#y.#y.#y.#y.#y.#y.wC#KK#In#Im#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0",
+".v4Qtb.aL.#T.w3.va.v6.w6#KL.Hh.poQtE.bH.bH.RS.lvQtE.bHQtEQtE.ab#vF.oD.Vv.34.Vv.Vv.Vv.Vv.Vv.YV.BM.Xb.OU.Vt#fa.Vt.Xh.7V#gX.Q#.bH.bHQtEQtE.bHQtF.lF.7Z.Vv.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.rv.Qh.34.Vv.Vv.Vv.Vv.Vv.eqQtF.wo#KM#c3.w4#KN.uAQtEQtEQtE.erQtF.mD.Vv.Vv.Vv.Vv.Vv.Vv#jT.RR#j5#vN.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.YVQtG.bH.pv.kg.MZ.pt#uP.hf.BM.0A.TP.TP.TP.7T.zJ.Vt.Vt.Vt.Vt.Vt#tK.Vt.TR.mC#gX#yB#gX.YW#Fs#oN#Cx#KO#KP.ji#KQ#IH#IH#IH#IH.OU#GD#Hb.Vu.sW.hg#IH#IH.9X#yE.lm.cU#KR.cU.dx.W9.h5.g6.jl.h2.h2.tH.a3.ro.cj.cj.BL.cj.cj.cj.g6.g6.a#.lm.lm.lm.lm.lm.lm.a#.a#.lm.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.lm.a2.9X.nM.mP.a#.a#.mS.0F.9P.Vv.OK.qw.YV#Iz.h2QtN#KS#KT.zt.y#.W2.w6.Jk.#C.bW.gV.c8.aI.#B.#B.#B.#B.#B.#B.#BQtW.aF.bO.dh.dg.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.av.#y.#y.#w.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtW.aIQtaQtbQtb.bO.bOQta.miQtb.bOQtk.#P#bc#c4#c5#c4#bc.ed#GQ#KU#KV#KW.#H#KX.bp#I2.#L.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.#L#eI#KY#J6#c4.Du#c4#c4.Du#c4.w4.Du.Du.w4.vb.Du.vb.GY#KZ#K0#K1.em#DG.em#BP#K2.em#C4#K3#K4#K5#K6#HG#KH#F0#F0#F0#F0#K7#K8.o1.vJ.#y.#A.#A.#A.#A.#A.#A.#A.#A.#z.#z.av.Mm#K9#K6#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0",
+".dh.#R.c8Qtp.w1.Du.#..W0#L.#L#.r1.k#.abQtE.bH.lG.bHQtEQtE.bH.ko.qE.Vv.34.Vv.Vv.Vv.YV.YV.YV.YV.Vv#is.YU.kn.Vt#tK.Vt.Vt.lDQtJ.tKQtE.a4QtEQtEQtE.abQtE.57.YV.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.9V.dw.cg.Xb.Vv.Vv.Vv.Vv.Vv.dw.cX#La#Lb#bd.w1#Lc.bL.bHQtEQtEQtEQtK.dw.Vv.Vv.Vv.Vv.Vv.Vv.YU.Xe.rA.33.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.57.cS.kk.mw.zM#Ld.h5.re.0A.TP.TP.BM.BM.9I.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.sd.5U#Hb.TN.sX.zJ.Vt.tI#Le#Lf#Lg#Lh.lZ.TA.rd.gi#IH#IH.mE#xE.h.#jT#yE#yw.rs.roQtJ.lm.lm.cU.dx.W9.qw.cj.zJ.0A.TP.0A.h2.f8.72.sW.f7.cj.g6.cj.cj.cj.cj.bF.g6.hV.lm.lm.a#.lm.lm.a#.lm.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.vh.dv.cV.lm.a#.a#.kq.0F.9P.ac.52.sd.tI#Li.h2QtN#Lj#Lk.zs.y#.dS.W0.Fr.#T.bW.aw#Ll.aI.#BQtWQtWQtWQtWQtW.aI.cu.#R.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#AQtaQtW.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQta.bOQtbQtbQtbQta.bOQtbQtaQt0#bg#c4#c5#Lm#c5#c5#F2#Ln#Lo#Lp.#K#I1.#I.#L.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA#Lq.aA.d4.aA#Lq#Lr#Ls#F2#c4.w4.L#.w4.w4#c4#c4.w4.vb.w4#bg.0s#Lt#Lu#Lv#Lw#DG#BP#BP.em#BP#BP#Lx#Ly#HG#K5#HG#KH#F0#F0#F0#F0#KH#wA#Lz#LA.vJ.dg.#A.#A.#A.av.#z.#z.#z.#y.#z.#A.#A.#A.#y.au#LB#LC#Im#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0",
+".v4.tm.cu.b5.w1.w4.zG.y..Dt#LD.oz.lFQtFQtEQtEQtEQtEQtEQtEQtFQtG.YV.YV.Vv#LE#LF#LF#Cx#Cx#Cx#Cx#LF#gE.tJ.YU.kn.Vt#tK.Vt.Vt.MV.h..tK.klQtFQtEQtEQtE.bHQtF.lw.oD.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.mD.k#.tJ.Vv.Vv.Vv.Vv.Vv.TH.cX.Qm#LG#be#bj#LbQtFQtK.bHQtEQtEQtF.cW.Vv.Vv.Vv.Vv.Vv.Vv.9F.tKQtF.0A.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.oDQtEQtD.qo.x###MQtN.lD.oD.TP.BM.YV.BM#j2.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ#LH#Hb.sc.sd.zJ.2a#LI#LJ#LK#LL#LM#LN#LO.id#LP.gi#IH#IH.scQtJ.g..54.7T#yw#yF#LH.lm.a#.a2.vh.sR.a..qw.7I#IF.TP.TP.TP.oD.pM.72.ro.lmQtN.cj.g6.cj.cj.cj.cj.cj.g6.g6.a#.lm.lm.lm.lm.lm.lm.a#.a#.a#.a#.a#.a#.a#.lm.a#.r#.MV.a..lm.a#.a#.kq.zJ.lD.k..cU.W9.vh.BM.TPQtN#LQ#LR.zt.zo.dS.ap.va.#T#Fm.gV.#Q.aFQtWQtWQtWQtWQtkQta.dh.#y.#y.au.ah.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.dhQtk.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQtaQta.cuQtbQtbQtbQtbQtaQtZ.w3#c4#c5#c5#c5#c5#c5#c5#ic#LS#LT#LU#LV#I3.aA.d4.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.d4.aA#Kb#LW#Ka#I3.d4#eH#LX.DH.awQtk.bOQt.Qt..bO.c8.#T.vb.w4.vc.cE#LY#LZ#KC#L0#Cp.em.em.em.em#BP#L1#K4#L2#HG#F0#KH#F0#F0#F0#F0#F0#L3#xu#L4.wC.#y.#A.#S.#y.#y.#S.#A.#A.#A.dg.dg.dg.au.au.au.#y#L5#L6#L7#Io#Io#Io#LC#L8#L8#L8#L8#L8#L8",
+".dg.v4.w2.cv.#C.vb.16.v8.aj#L9#De.oE.oPQtEQtEQtEQtEQtEQtEQtF.sf.9F#jT#gE#D.#M.#M##Ma##q#Mb#Mc#Md#Me#Mf#jT.aa.sW.vh#fa.Vt.Vt.zJQtJ.oD.owQtF.fdQtEQtE.bHQtFQtE#Iy.uB.YV.YV.Vv.Vv.Vv.Vv.Vv.YV.pM.rA.TP.Vv.Vv.Vv.Vv.Vv.YTQtF.wo#Mg.16#bj#Mh.ntQtK.bHQtEQtE.a4.um.YV.Vv.Vv.Vv.Vv.Vv#fd.aY#qs.Xg.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.BMQtGQtF.Li.2d#MiQtN.mC.oD.YV.Vv.YV.Xb.mC#fa.Vt.Vt.Vt.Vt.Vt.Vt.Vt#sM#wK.9X.Vt.sd.nx.lE#Mj#Mk#Ml#Mm.qb#Mn#Mo#Mp#Mq#Mr.gi.gi#IH.OAQtJ.ci#Fs.TN.9X#yE.nN.jb#J7#De.a#.lf.lD.zJ.zJ#Ms.TP.TP.TP.0A.qE.mD.Vu.jcQtN.j9.cj.cj.cj.cj.cj.cj.cj.g6.g6.g6.hV.lm.lm.lm.lm.a#.a#.a#.a#.a#.a#.a##De.7T.lf.a#.a#.a#.a#.mS.zJ.re.g6.qw.lD.0F.M6#bxQtN#Mt#C7.zD.ye.dS.#a.va.ldQtZ.cv.aI#p6.#B.#B.aIQta.aE.#yQth.ahQt.Qt..#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#yQtb.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aFQta.bOQtaQtbQtbQtb.bOQtW#be#c4#c5#c5#c5#c5#c5#c5#c5#c5#c4.gV#GQ#Mu#Mv#Mw.#K.bp#I4.aA#Kc.aA.aA.aA.aA.aA.aA.aA.aA.aA.aA.d4#Lq.#L#Mx#Iv#My.#K#I1#Ka#Mz#MA.#y.#y.#y.#y.#y.#y.#y.ahQtk.wA#MB#MC#JL#MD#BP#C4.bD#BP#BP#BP#Cp#ME#MF#K5#HG#F0#F0#F0#F0#F0#F0#F0#wA#Ir#MG#MHQtS.#y.#A.#A.dg.r..au.au.r..r..k6.v4.v4.v4.v4.v4.x9.#z#MI#MJ#MK#ML#MM#MN#MO#MP#MQ#MR#MS#MT",
+".ah.v4.dhQtaQtp#bc#c5.#g.n1.Hn#MU#MVQtK.bHQtEQtEQtEQtE.bHQtF.LB.M6.mx.Qe#MW#bj#id#id#bd#id#id#id#MX#MY#MZ#M0.#5.sc.Vt#tK#fa.Vt.lD#gX.Xg.g8QtFQtEQtEQtEQtE.bHQtF.lw#bz.Vv.Vv#fd.Vv.Vv.Vv.34#bz#qs#e1.Vv.Vv.Vv.Vv.Vv.aY.bHQtE.Qk.W2#id#M1.OF.#6.bHQtEQtE.bH.g8.BM.Vv.Vv.Vv.Vv.Vv.Vv.oD.oP.Xf.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.ep.bH.TY#M2#M3QtN#fb.0A.Vv.Vv.YV.M6.sX#tK.Vt.Vt.Vt.Vt.Vt.Vt.zJ.TM.Vu.sX.Vt#bu.9F#M4#bb#M5#M6#M7#M8#M9#N.#Mn#N##Na#Mr.sR.gi#IH.mEQtJ#Nb.ro.Vv#gE.tI.vh.kq.a#.HJ.zJ.zJ.Vt.Vt.zJ.7J.Vv.TP.TP.es.oD.b8.pM#rm.ghQtN.j9.cj.cj.cj.cj.cj.cj.cj.cj.g6.g6.a#.hV.lm.lm.lm.lm.lm.a#.a#.a..lD.ac.my.lm.a#.a#.a#.sg#yw.nN.cU.W9.zJ.zJ.M6.jlQtN#sK#Nc.zr#Nd#l#.#o.Du.#C.bW.cv.aI.aI.aFQt#.#D.#yQth.#y.aEQta.w2.dg.br.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.au.cu.c8.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtW.aFQtaQtbQtb.bOQta.bOQtk#r3.w4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.w4.#BQtb.v4#Ne#Nf#Ng#Nh#Ni#I3#I2.#L.d4.aA.aA.aA.aA.aA.aA.aA.#L.bp#Lp#Nj#xK#Nk.#E#Nl#Nm#Nn.cw.#y.#z.#A.#A.#A.#A.#A.dg.IA#No#Np#Nq#MD.aV#A6#A6.bD.bD#Cp#Jb#BP#Nr#K7#Fi#FY#KH#KH#F0#F0#F0#Io#LC#wA#Ns#Kk#Nt.#A.au.v4.r..r..k6.r..r..r..k6.v4.v4.w2.w2.w2.w2.w2.w2.x9.#y.hL#bg#bh.at#Nu#Nv.GY#Nw#Nx#Ny#Nz",
+".av.r..w2Qtb.cv.v5#c4.#..oY#NA.lf#NB#Ar.bHQtEQtEQtEQtE.a4QtE.2r.tD#NC.wZ.w4#c5#c5#c5#c5#c5#c5#c5.w4#id#ND#NE#NF#NG#NH.Vt#tK#tK.Vt.5U.M6#by.g8QtFQtEQtEQtEQtE.erQtF.lF.Xe.TP.Vv.Vv.YV.Vv.Vv.BM#NI.3Y#fd.Vv.Vv.Vv.Vv.0A.k#.bH#NJ#BV#c2#NK.OL.g9.bHQtEQtE.bHQtE.0A.Vv.Vv.Vv.Vv.Vv.Vv.TP.oP.Ou#G7.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.rB.rA.oq.zM#BeQtN.dv.7Y.Vv.Vv.BM#gX.zJ.Vt.Vt.Vt.Vt.Vt.Vt#tK.g.#xE.dv.vh.kn#NL#NM#NN.D#.YL.D#.Fp#M6#NO#NP#NQ#NR#NS#NT#NU.sR.gi#IH.dt.eo.kR.r#.Vv#Fr.mT.a#.kq.Vt.lD.Vt.Vt.Vt.Vt.Vt.5U#bx.TP.TP.TP.oD.pM.rB.pM.a5.qwQtNQtN.cj.BL.cj.cj.cj.cj.cj.cj.cj.g6.g6.g6.a#.a#.lm.lm.lm.lm.ck.Oz.g6.lm.a#.a#.a#.lf.a2.a#.lf.ku.h5.g..Xb.XhQtN#LQ#Nc.EX#NV.#i.#q.w4Qtp.bW.c8QtkQtb.au.#y.#y.dg.auQt#.cuQtbQt..#R.dg.ah.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#y.tm.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQtaQtaQtbQtb.bOQta.cv.w1#c4#c5#c5#c5#c4#c5#c5#c4#c5#c5#c4#c5.w3QtaQtbQtb.cq#NW#NX#NY#NZ#N0#N1#N2.#I#I2#N3#Kb#I2#I2#I2#I3#Lp#N4#N5#GV.c8.dh.as.rL#N6#B9.#A.#A.#A.#A.#A.#A.#A.#AQtR#N7#N8#Lw#N9#O..bD.bD.bD.em#BN#O##Oa#Ob#Oc#Fi#F0#F0#F0#JU#LC#wA#Gw#Od#Oe#Of#Og.GZ.#y.v4.w2.w2.v4.k6.au.au.au.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.r..#R.#C.cE#bc#Oh#Oi#Oj#Ok#Ol#xr#xq#xq",
+".#A.k6.x9.#w.cu.#T.L#.Dt.#.#DL.kR#Om#On.cSQtEQtEQtEQtE.cS.lg#Oo#Op#Oq.w4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w1#uH#Or#Os#Ot#Ou.Vt.Vt#tK.Vt#yF.Xb#by.k#QtFQtEQtEQtEQtEQtE.bHQtF.bL.TH.BM.Vv.YV#fd.YV#gJ.Ou.Vv.Vv.Vv.Vv.Vv.7Y.ch.ab.#6#Ov#Ow#Ox.Qk.ntQtFQtEQtE.cS.ko.h2.Vv.Vv.Vv.Vv.Vv.Vv.BM.oP.TH.aa.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#fd.3Y.cS.RS.mu#OyQtN.mW.33.Vv.Vv.TP#oN.Vt.Vt.Vt.Vt.Vt.Vt.zJ.Vt#j2.Vu.lD.Vx#yE#Oz#OA#OB#OC#dh.Fp.Fp#dh#OD#OE#OF#OG#OH#OI#OJ#LP.gi.gi.qw.kv.ja.RM.9F.sY.a#.lm.lf.lD.zJ.Vt.Vt.Vt.Vt.Vt.Vt.zJ#fb#e0.TP#bz.0A#Dd.tK#OK.tHQtJ.cZ.g6QtN.cj.cj.cj.cj.cj.cj.cj.cj.cj.cj.cj.g6.g6.lm.a#.ji#xE.gh.a#.lm.a#.a#.a#.lm.lm.lm.sR.cU.a#.MV.BM#lv.g6#OL#Nc.EX.eI.k2.fJ#bcQte.c8.bO.au.#y.#y.dg.#AQt.QtW.aFQtVQtVQtVQtVQt..dh.v4.r..dg.#y.#y.#y.#A.#A.#A.#A.#A.#A.#y.#y.#A.bO.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aFQtaQtbQtbQta.bOQtkQte.Du#c5#c5#uH#c5#c5#c5#c4#c5#c5#c5#c4#ic#c4QtfQtbQtW.#F#c4#c5#uH#c5#OM#NX#NY#ON#OO#OP#OQ#OR#OS#OT#OU#Nj#OV#gt#OW.au.#y.#y.av.#A.#A.#S.#S.#A.#S.#A.#A.av.#AQtS.e4#OX#OY.bD#A6.bD.bD.em#OZ#O##O0#O1#O2#Fi#Fi#wA#Io#Io#xu#O3#O4#O5#O6#O7.o1.o1.#A.v4.w2.w2.w2.w2.w2.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.x9.v4.dg.#v#Nw#O8#O9#Ok#P.#ww#xq#xq#ww#ww#zs#zs",
+".#y.r..dh#P#Qtb#m8.vb.16.aq#NA.id#Pa#Pb#Ao.koQtEQtEQtE.r2#Pc#Pd#PeQte#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#bc.#C#Pf#Pg#Ph.Si.0F.Vt#tK.sd.TM.BM#jS.bH.bHQtEQtEQtEQtEQtEQtE.bHQtF.g8.TH.BM.Vv#jT##o.g5.Vv.Vv.Vv.Vv.Vv.YV.lF.cXQtF#Pi.wZ#Pj.i4.ntQtFQtEQtE.bH.cS.3Y.Vv.Vv.Vv.Vv.Vv.Vv.YV#qs.dw.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#e1QtE.g9.r7#Pk.cj.oO.BM.Vv.Vv.TP.dv.vh.Vt.Vt.Vt.Vt.Vt.lD.RM##N.Vw.Xd#Pl#Pm#Pn#Po#Pp#Pq#Pr#Ps#Pt.zE#dh#dh#OD#BV#Pu#Pv#Pw#Px.lZ.gi#IH.lC.YW.mW.TO#sM.g6.a#.rd.lD.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.lD.dv.mU.TP.oD.h2.pM.b8.dw.RR.aa.pD.h5.cjQtNQtN.cj.cj.cj.cj.cj.cj.cj.cj.g6.lm.lm.h..dv.a#.a#.a#.a#.a#.a#.a#.a#.lm.cU.a#.g6.r1.lx.nM.ox.Jq.Tu.pX.qS.bc.16.w1QtW.dh.ahQth.#y.#A.#RQtk.cv.#B.cv.bWQtfQtf.bW.#BQtV.cuQtb.#R.k6.#A.ah.#y.#y.#A.#A.#A.#y.#y.aEQtk.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aI.bOQtbQtb.bOQta.#B.vc#c4#c5#c5#c5#c5#c5#uH#c5#c4#c5#c4#c5#c5#c5#bc.#PQtp#bg#c5#c5#c5#c5#c5#c5#c5#c5#J6#Py#NX.w4.Du#J6#tm#Ke#Pz#OW.#yQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A#PA#PB#PC#BP#Ad.bD.bD.em#Jb#PD#PE#PF#PG#PH#PI#PJ#PK#PL#PM#PN#PO#PP.br.br.#A.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.7E.#A#PQ#PR#Nz#PS#HL#xq#xq#ww#zs#zs#zs#zs#zs#zs#zs",
+".#y.#y.v4.w2Qt.Qtk.#F.JkQt6.Jv#Nb#PT#PU#PV.koQtEQtEQtE.cS#PW#PX#PY.v5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4Qtm#PZ.BO#P0#P1#tK.Vt.Vt.cm.lx.YV#Iy.cX.bHQtEQtEQtEQtEQtEQtEQtE.bHQtF.j##P2#wK#gC.2q#jT.Vv.Vv.YV.YV.YV.uzQtLQtF#Lb.wZ#M6.rm.j..bHQtEQtE.fd.a4.tK.Vv.Vv.Vv.Vv.Vv.Vv.9F.pN.sV.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TP.bL.g9.qq.sR.cj.dv.BM#fd.YV.BM.nx.7U.Vt.Vt.Vt.Vt.zJ.0F#P3#Hb.kn#P4#P5#P6#P7#KX.d5#P8.fO#P9#Q.#Q##Pt.zE#dh#sy#OE#Qa#Qb#Qc#Qd.lZ.gi.rs#yw#IF.9F.tI.a#.a#.h5.lD.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.vh#Qe.OU#yE.9V.0A.pM.YT.cT#bA.rv.RR.aa.sc.HJ.cjQtNQtN.cj.cj.cj.cj.cj.hV.hV#vB#zB.jc.a#.lm.lm.a#.a#.a#.a#.lm.lm.hV.a#.a#.sR.Vw.g6#dk.Dk.17.yb.cr.#a.v5.#BQt..dg.#y.as.#AQt..aF.bWQteQtp.vc#bc.vb.vb.w3.vc#r3#Fm.#BQtkQtaQt..dh.au.ah.#y.#y.#y.ahQtb.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtW.aFQtbQtbQtbQtbQta.#P.w4#c5#uH#uH#c5#c5#c5#uH#uH#uH#c5#c5#c5#c5#c5.Jk.w4#c4#c5#uH#c5#uH#c5#c5#uH#c5#c4#c4#c4#c4#c4#F2#rj#rj#NW.#Q.#SQth.#y.#y.dg.#A.#A.#A.#A.ah.#A.au.#A.dg.r..au.Mm#Qf#Qg#Qh.bD#A6.em#DG#Qi#Oa#Qj#Qk#Ql#Qm#HI#LB#Qn#Qo#Qp.#z.o1.av.k6.v4.w2.v4.#A.#z.o1.k6.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.#A.#A#O7#Qq#Qr#wv#xq#xq#ww#ww#zs#xr#xr#xr#HK#HK#yu#HK#xr#HK",
+".d0.#A.au.v4.#R.bO.c8.#T.#F.R4.ku#Qs.nm#Qt.r2QtE.bH.bH.cS#Qu#Qv.bj.L##c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#r3#Qw#Qx#Qy.0F.Vt#tK.Vt.sW.YV.YV.lwQtFQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF#bC#xA#gJ.53.YV#fd.YV.YV.uB.Qh#qsQtK#Qz#id#QA#QB.j..bHQtEQtE.fd.bH.tK.Vv.Vv.Vv.Vv.Vv.Vv.9V.pN.lw.9F.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.lwQtK.2c.h5.g6.ja.TP.YV.BM.Xb.5U#tK.Vt.Vt.Vt#tK.lD.k.#EE#QC.7J#QD#QE#QF#QG#QH#QI#QJ#QK#QL#QM#QN#QO.EX.P5#dh#dh#QP#QQ#QR#QS.CK#IH.jh.rs.kx.9F#LH.ml.3X.J0.0F.TR.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.vh.Vt.cm#yF.jj.aa.aY.72.#8.cW.7X.ch.h6.h2.kv.po#tJ.g6.g6.cj.g6.lm.cV#yF#QT.a#.lm.lm.lm.bJ.a2.lm.lm.lm.lm.lm.a#.lm.a2.zJ.bJ.B0.Dp.zs.ye.#f.vaQtpQtk.#R.dg.av.dg.#R.aF.bW.bj.v5.w4#QU#QV#QW#QX.dL.Dt.Jk#dp.w1.#F.aLQtWQta.bO.#R.k6.dg.au.cu.#B.#B.#B.#B.#B.#B.KE.KE.KE.aI.aI.aI.aI.aI.aI.KE.KE.KE.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQtkQtbQtbQtb.#wQtkQtp#c4#uH#uH#uH#c5#c5#c5#c5#c5#c5#c5#c5#c5#uH#c5#c5#uH#c5#c5#uH#uH#c5#c5#c5#c5#c5#c4#c4.w4.w4.Jk.Jk#c4#bc.cv.#A.d0.#y.dg.#A.#A.dg.au.au.au.au.r..au.au.r..k6.v4.wC#QY#QZ#Q0#Ad.em.em#Q1#Q2#Q3#Q4#Q5#Q6#Q7#Q8#Q9.au.o1.#z.k6.v4.w2.w2.au.o1.#z#PP#R.#R##Ra#Rb.#A.GZ.w2.w2.w2.w2.x9.v4.k6.#A.#A.#A#Rc#Rd#Re#Rf#ww#xq#ww#zs#zs#xr#HK#HK#ww#Rg#Rh#Ew#Jp#Ri#Jp#xx#xx",
+"QtS.#y.dg.v4.w2Qtb.aIQtf.cu#Rj#Rk#Rl#jW#Rm.a4.bH.bH.bH.oP#Rn#Ro.aH#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#JR.#C#Rp#Rq#Hd##R.Vt#tK.0F.h..7Y#Jw.lG.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.fd.i8.7O.h2#bt.Vv.9F.#8.3Y.ep.cX.g9#Rr#bi.eM.Lr.OF.bHQtEQtE.fd.bH.3Y.Vv.Vv.Vv.Vv.Vv.Vv#Bc.pN#Iz#G7.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.sf.aZ.qi.dx.g6.jl.BM#is.YV.TO.sX.Vt.Vt.Vt.Vt.zJ.vh.pB#Rs#gX#Pl#Rt#Ru#Rv#Rw#Rx#Ry#Rz#RA#RB#RC#RD#RE#RF#RG.zF.D#.zE.Fp#RH#RI#RJ.lZ.lZ.pA.2j#LE#Rs.h5.hV.a#.32.9U.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.vh.vh.lD.Xc.Vs.7T.lE.YT.RR.h6.ow.rA.oE.54.ic.cV.a#.g6.a#.sR.Vu.sR.g6.a.QtN.lm.TL.VnQtI.a#.lm.a#.a#.3X.mP.cU.dxQtN.9S.zy.yb.c4.ap.vb#r3Qta.dh.au.dg.k6QtV.aLQtp.v5.Jk#RK#RL#RM#RN#RO#RP.w6.w6.#a.Z..va.cE.#C.#P.cvQtk.cuQtWQtZ.#B.aI.aI.KE.#B.KE.aI.c8.aI.c8.#B.cv.cv.cv.cv.#B.c8.c8.c8.KE.KE.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtW.aKQt#QtbQtbQtb.c8#bi#c4#uH#uH#uH#uH#uH#c5#c4#c5#uH#c5#c5#c5#uH#c5#c5#uH#c5#c5#c5#c5#c5#c5#uH#c5#c5#c5#c4#c4#c4#c5#c5.w4.aw.aE.#y.ah.k6.au.v4.v4.r..au.k6.au.k6.au.k6.r..r..k6.v4QtR#RQ#RR.cN.em.em#RS#RT#RU#RV#RW#RX#RY#RZ.#z.o1.#A.v4.w2.w2.w2.w2.au.#y#R0#R1#R2#R3#FZ#Io#Ir#R4#R5.#A.v4.v4.k6.#A.av.w2#R6#R7#R.#R8#Rf#ww#R9#ww#zs#zs#xr#xr#xr#S.#S##HJ#Sa#Sb#Jo#Jo#JW#JW#JW#Jo#Jo",
+".e4.#y.#A.v4.v4Qt..cu.cvQtq#LQ#lv#Sc.Ly#ql#rH.cS.fd.bH#qs#Sd#Se#r3#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4#Sf#Sg#Sh.rx#tK.Vt.Vt.7V#wK.Xb#IyQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.i2.qqQtE.lF.Jy.lwQtF.TH.RRQtK.2c#Si#c5#uK.Ql.ptQtFQtEQtE.fd.bH.3Y.Vv.Vv.Vv.Vv.Vv.Vv.9V.pN.pN.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.bGQtKQtG.lf.g6.g..YV.uB#is.Vu.lD.Vt.Vt.Vt.zJ#oi.ku.g.#wK.TN#Sj#Sk#Sl#Sm#Sn#So#Sp#Sq#Sr#Ss#RC#St#Su#Sv#Sw#Sx#Sy.BD#sy#Sz#SA#SB#SC.jh.pA.ji#Hb#wK.cZ.cV.a#.ic.zJ.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Xh.vh.zJ.sX.7I.kn.ja.OU.g..sd.po.rd.hV.hV.ml.9X.tIQtI.dx#dk.9H#ro.ms.qG##Q.jb.a#.a#.a#.a#.a#.a#.lmQtN.R3.zF.BE.bQ#L..cEQtf.bO.dh.#A.au.WZ.fXQtf.v5#c4#SD#SE#SF#SG#SH#SI#SJ.gE.ao.ap.an.y..##.16.Du.vc#be.vc.atQtp.gW.aL.c8.aI.aI.aI.#B.cv.aL.bW.#PQte.#P.#PQtfQtf.bW.cv.#B.c8.aI.KE.KE.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQtV.bOQtbQtbQtb.aL#bg#ic#uH#uH#uH#uH#uH#uH#c5#uH#c5#c5#uH#c5#c5#c5#c5#c5#uH#c5#c5#c5#c5#c5#c5#c5#c4#c5#c5#c4#c4.Jk#c5.#FQtb.dg.v4.x9.w2.w2.w2.w2.v4.v4.G1.au.k6.k6.k6.au.k6.v4.#AQtS#SK#KE#zp.em#A5#N9#SL#SM#SN#SO#SP.o1.o1.k6.v4.w2.w2.w2.w2.w2.w2.#A#SQ#SR#Ns#LC#Fk#Io#F0#F0#Im#L7#SS.AK.#x.#v.fX#ST#SU#SV#SW#SX#ww#xq#ww#zs#zs#HL#xr#HK#ww#SY#SZ#S0#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo",
+".e4Qth.av.au.v4.#R.bO.aF.aM#S1QtH#S2.T3.Qr#S3.9W.bw#rH.OM#S4#S5#be#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.#C#S6#S7#S8.sd.Vt#fa.Vt.tI.BM.BM.chQtFQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.oq.kb.k#.bHQtF.a4QtF.ep.57QtK.2c#rB#c1#ts#S9.OLQtFQtEQtE.fd.bH.3Y.Vv.Vv.Vv.Vv.Vv.Vv.TO#EF#gC.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TF.kw.g8.sR.cj.0F.M6.BM.BM#gX.zJ.Vt.Vt.Vt.zJ.vh.jh.h.#xE#T.#T##Ta#Tb#Tc#Td#Te#Tf#Tg#RB#St#RC#Th#Ti#Tj#Tk#Tl#Tm#Tn#JA#To#Tp#Tq.pA.gh.pA.ji#Hb#II.sX.a..lm.hV##Q.lD#fa.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.vh.Xh.se.Vt.zJ.zJ.sX.jc.a..Oz.M6.g7#Dd.bw.JB.i6#uM.kp.pD#Tr.sR.cV.a#.a#.a#.a#.a#.lm.a.#Ts.zp.ye.gE.fJ.at.aLQt..au.au.dhQtV.c8.#P.at.vb#tt#Tt#Tu#Tv#Tw.um#Tx#Ty#Tz#yy#qc.#o.#f.w6.#o#TA.y..#q.Jk.vb.vcQtpQtf.bW.bW#FmQtfQteQtp.#T.w1.vc.w3.w1.#CQtp.#F.#PQtf.cv.c8.aI.KE.KE.KE.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQtaQtbQtbQtbQta.#P.vb#c5#uH#c5#c5#c5#uH#uH#uH#uH#c5#c5#c4#c5#c5#uH#c5#c5#c5#uH#c5#uH#uH#c5.Jk#c4.w4#c5#c5#c5#c5#c4.#TQtV.dg.au.x9.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..r..r..k6.au.dgQtS#TB#TC.em#N9#A7#TD#TE#TF#TG#SPQtS.o1.k6.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2#PA#R2#TH#wA#F0#KH#F0#F0#wA#Io#TI#TJ#TK#TL#TM#TN#TO#TP#xq#xr#xr#zs#zs#zs#xr#HK#zt#Rg#TQ#Sa#TR#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo",
+"QtSQtS.#y.#y.au.v4Qt.QtV.aM.M4.0F#TS#TT.nm#TU#TV#TW#TX#TY#TZ#HN#bi#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4Qtl#T0#T1.RM#tK.Vt.7U.zJ#IF.YV.a3QtK.bHQtEQtEQtEQtEQtEQtEQtEQtE.k#.i1.BI.koQtEQtEQtE.bH.oE.a3QtK.2c#T2.YK.Sl#mT.JLQtKQtEQtE#T3.bH.tK.Vv.Vv.Vv.Vv.Vv.Vv#LE.kl.OF.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.qEQtF.cg.qwQtN.cR.54.BM.TP##N.Vt.Vt.Vt.Vt.lD#yw.pB.Vu#Da.Xg#T4#T5#T6#T7#Td#T8#T9#U.#U##Ua#Ub.dp#OZ#Uc#Ud#Ue#Uf#Ug#Ox#dh#To#Uh#Ui#Uj#Uj.km.Vu#II.ro.a#.lm.a#.#9.lD.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.sd.dx.sg.TO.Oz.km.RR#zA.OF#Uk.vh.Vt#gC.qGQtNQtI.a..a#.tM.a#.a#.a.#J7#yD.OI.fE.yf.Dt.#C.cq.w2.r..v4.w2Qta.fX.#P.#C.vc#uS#Ul#Um#Un.bw.YS.TP#jT#e0.9F#Uo#Up#Uq#Ur.ba.c6#l#.#j.w6#L..16.w4.cE#bi.vc#bi#bi.v5.vb#c4#c5#Us.16.Jk#c4.w4.w3.w1Qtp.#P.bW.gV.c8.aI.KE.KE.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQtaQtb.bOQtbQtkQtp#c4.Jk#c5#c5#c5#c5#c5#c5#c5#uH#c5#c5#uH#c5#c5#uH#uH#c5#uH#Ut#uH#uH#c4#c5#c5#c5.Du#c4#uH#c5#c5#bi.#Q.#A.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4.vJ#Uu#Uv#Uw#TD#TD#Ux#Uy#Uz#UA.#yQtS.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.au.o1#UB#UC#wA#F0#F0#F0#F0#G3#UD#Od#UE#UF#UG#ys#UH#UI#xr#xr#HL#zs#zs#xr#HK#yu#xs#UJ#UK#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo",
+"QtSQtSQth.ah.ah.au.dh.bOQtq.y#.bM.Xh#UL#UM.li.S#.DF.DF#UN#Tr#UO#be#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#bc#UP#UQ#UR.sd.Vt#tK.Vt.sW.uB.0A.lG.bHQtEQtEQtEQtEQtEQtEQtEQtE.ko.pt.Vm.erQtEQtEQtEQtF.bL.RRQtK.OF.JV#US.W2#UT.oqQtFQtEQtE.fdQtF.tK.34.Vv.Vv.Vv.Vv.Vv#LE#zA.Qk#is.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Xg.rA.lF.lfQtN.jc.bI.BM.BM.YW.vh.Vt.Vt.zJ.zJ.jh#UU.M6#UV.Lm#UW#UX#Pn#UY#UZ#U0#U1#U2#U3#RB#QM#A5#A5.dp#U4#U5#U6#U7#U8.zt#OD#U9#V.#V##Uj.sa#gX.9V#QC.dx.hV.lm.lm##Q.sX.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.a2.a#.ac.9I.nM.vh.zJ.Oz.fe.bK.vh.rv.cg.oD.mU.dt.cZ.a.QtI.a#.lmQtN.Xj.On.zp.dN.w6.JkQtp.#Q.v4.G0.k6Qt.Qta.aI.bWQteQtp.bs#Va#Vb#Vc#Vd#Ve.YV.Xg#bx.lD.km.ku.Sj#Vf#Vg.dQ.cr.gI.W2.gE.an#KL.#o.dL.#q.fJ.#q.#o.#o#TA#L..v8.an.ao#TA.#q.Dt#c4.v5.w1#r3.bW.cv.c8.aI.aI.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtW.aFQtaQtbQtbQtbQtk.w1.Jk#uH#c5#c5.Jk#c4#c4#c5#c5#c5#c5#c5#c5#uH#uH#c5#c5#c5#uH#uH#uH#uH#c4#c4#c4#c5#c4#c4#c4#c4#bc.t1.au.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.k6QtR#Vh#Vi#Qh#Vj.em#Vk#Vl#Vm.Mm.#y.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.#A.br#Vn#Vo#Ns#In#Im#F0#F0#FX#wz#Vp#Vq#Vr#Sa#Vs#Vt#wx#xr#HL#zs#zs#HL#xr#HK#Vu#Rh#Vv#Vw#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo",
+".e4QtSQtS.#y.#A.r..dh.#wQt..Dt.TK#Pk#Vx#bp.li.li.li.Qr.Xr#D.#Vy#bj#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5Qte#Vz#VA.tF##R.Vt#tK.zJ#bx.YV.rvQtFQtEQtEQtEQtEQtEQtEQtEQtE.bH.OF.tr.lGQtEQtEQtE.bHQtE.rvQtK#VB.HK#sy#VC.TG.lnQtFQtEQtEQtEQtK.LB.Vv.Vv.Vv.Vv.Vv.Vv#wK.oE.yk.7Y.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#by.bH.ch.lCQtN.jh.g..M6.TP.ro.vh.Vt.zJ.lD.pB.pA.7J.7T.oD#VD#VE#VF#VG#VH#VI#VJ.#1QtA#VK#BP#BP#Lw.bD.dp#VL#VM#VN#VO#VP#VQ.P5.D.#VR#VS.pA.jhQtJ#wK#wK.32.a#.lm.lm.mS.zJ.lD.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD.lfQtI.mC.lx.a2.k..7V#dk.nL.lm.jb.lD.MV.dv.cW.g9#qs.cg.M6#VT#uP.g6.a##VU.BD.m..gI.#o.vb.#PQta.dh.k6.auQt..miQtk.c8.awQte.rP#VV#VW#VX#bv#sx.BM.oD.Jx.sY#yw.rt.hg.sR#VY#VZ.P7.c4.dS.dN.gI.dS.W2.YK.#h.eR.ba.bR.dS.dM.Aa.zv#V0.W2.#h.#j.P4.#a.16.w4.atQtp.gW.cv.c8.aI.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtk.bOQtbQtbQtb.c8.vc#c5#uH#uH#c4#c5#c4#c4#c4#uH#uH#uH#c5#c5#c5#uH#uH#c5#uH#uH#uH#uH#c5#c5.Du#c5#c5#c4#c4#c5.w4.cv.aE.dg.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4QtS.w2#V1#V2#V3#V4#V5#V6.w2.Mm.#A.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.#A.br#R7#V7#Jf#Ir#wA#F0#F0#wB#V8#V9#W.#vy#TQ#W##ww#HK#xr#Ag#zs#zs#xr#HK#xr#Vt#Jp#Sa#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Wa#F1",
+"QtSQtSQth.#y.#y.v4.v4.tm.bOQtb#Wb.ku#Wc#Wd#B1.li.li.S##We.2q#Wf#bj#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4#F2#Wg#Wh.sd.Vt.Vt.Vt.mC.Vv.mDQtF.bHQtEQtEQtEQtEQtEQtEQtEQtE.kk.rf.bHQtEQtEQtEQtE.bH.koQtK.2c.ms#Wi#Wj.r8.lvQtFQtEQtEQtEQtF.Ou#Wk.Vv.Vv.Vv.Vv.Vv.9V.rv.HK.TP.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.0AQtE.ko.kmQtN.ic.zJ#wL.BM.sc.7U.zJ.lD.ci.rs.sR.TN#Wl#Wm#ts#Wn#Wo#Wp#Wq#Wr#Ws#Wt#Wu#Wv#A5#A5.dp.f##Ww#Wx#Wy#Wz#WA#WB#WC#WD.Qf#WE#WF.gh.pA.tI#wK#jT.sX.a..lm.lm.a#.rd.kx.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD.OKQtI.j2.BM.vh.mS.j2.lf.kq.jb.bK.HJ.cV#vB.sd.g..mxQtG.rz#zA.ow.scQtN#WG.Do.zs.hH.bQ.fJ#bi.awQta.dh.av.au.dhQt.Qta.aF.c8.cv.aw.sv#WH.He#WI#WJ.Vv.oD.Xg.oD.kv.mX.qx.if.sa.sR.TL#WK.Dh.xv.gF.gH.ye.ye.zo.y#.y##Ah.BD#WL.oh#Gz#WM.gJ.dN.dN.W2.#j.ao#vE.Du.vc.#F#c2.cv.aI.aI.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.#BQtWQtV.bOQtb.bOQtb.cq#bc#c5#c5#c4#c5#c5#c5#c5#c5#c5#uH#uH.Jk#c5#c5#c5#c5#uH#uH#c5#uH#c5#c5#c5#c5#c4#c5#c5#c4#c4.5Q.#w.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.#AQtc#WN#N8#WO#Wu#WP#WQ.auQtR.k6.w2.w2.w2.w2.w2.x9.GZ.v4.v4.v4.v4.au.o1.#A#WR#WS#Jf#LC#wA#Im#F0#F0#WT#WU#vy#Ew#Rg#xr#HK#HK#xr#zs#zs#zs#xr#HK#yo#SY#WV#WW#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#WX#Fl#F1#F1#WY#F1#F1#F1#F1#F1#F1#WY#JW",
+"QtSQthQthQth.#y.au.v4.v4Qtb#WZ.Lf#gW#W0#W1.g0.li.li.e8#W2.cg#Al.v..w4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5Qtp#W3#W4.RM##R.Vt.Vt.0F.YW.0AQtE.bHQtEQtEQtEQtEQtEQtEQtEQtE.sQ.Tx.cSQtEQtEQtEQtE.bHQtKQtK.2c.yk#W5#W6.rn.i4QtFQtEQtEQtEQtF.bG.YU.Vv.YV.Xb.Xb.Vv#jT.g5.qq#Bd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TP.clQtF.0FQtN.hV.po.lD#oN.9P#tK.TR.se.gh#tJ#W7.7T#wK#W8#W9.TD#X.#X##Xa#Xb#VL#Xc#OZ#A5#A5#Wv.dp#Xd#Xe#XfQtN.lU#Xg#Xh#Xi#Xj#IA#Xk#Xl.gh#tJ.0v#wK.9V.tI.a#.lm.lm.lm.lm.k..sX.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD##Q.a#.h5.M6.Vw.dx.g6.dx##Q#bx.9X.h5.a#.a#.cU.po.Vt#sM.a3.ep.ep#jS.a2QtN#Xm#Xn.zr.Hd.gE.16.#C.cvQt#.k6.ah.#A.v4.#RQtaQtV.aFQtWQtW#FD.cv.cu.Hc#Xo.pM#jT.0A.TP#bz.mU.OA.T5.id#Nb.a5.9I.r##Xp#Xq.zF.ya#dh.zF.P5#Xr#Xs#Xt.rd#Xu.DQ#Nb#Xv.zv.zo.Hd.c4.#i#Xw#vE.w4.w1#id.bW.c8.aI.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.aIQtaQt.QtbQtbQta.#P.w4#c5#c5#c5#c5#c5#c5#c5#c4#c5#c5#c4#c5#c5#c5#Xx#c5#uH#c5#c5#c4#c5.Jk.Jk#c5#c5#c5#c5#c4.#FQtb.dg.au.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.k6QtR#Xy#Xz#XA#V4#XB#XCQtcQtS.v4.w2.v4.v4.v4.v4.k6.au.au.#A.dg.#A.br.wC#XD#XE#XF#FX#Ir#wA#F0#F0#wA#Gw#XG#XH#xs#zt#HK#xr#HL#zs#zs#zs#xr#xr#xr#Jq#XH#Sa#WY#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#XI#XJ#xu#XK#XL#WW#WY#XM#xw#XN#Eu#XO#WT#FW",
+"QthQtSQtRQtS.#y.#S.k6.v4Qt..bt.YN.32#XP#XQ#d##B1.li.OQ#XR.tL#XS#bk.w4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#JR#XT#XU#Hd##R.Vt.Vt#wM.9V#onQtFQtEQtEQtEQtEQtEQtEQtEQtE.2c.tu.bHQtEQtEQtEQtEQtFQtFQtF.nt.yk#XV#XW#XX.DzQtK.bHQtEQtEQtK#XY.YU.72.cW.cW##D#gN.LB.cW.Dx.0A.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#bz.ch.#7.XcQtN.cj.sR.lD.sX.TR.Vt.lD.jn.pA.pA.Vx.7T#GD#XZ#X0#X1#X2#X3#X4#X5#X6#X7#X8#Er#Er.dp#X9#Y.#Y##Ya#Yb#Yc#Yd#Ye.Ox.qr#Yf#Yg.hi#Yh#Yi.ci#Hb#GD#yE.kq.oo.a#.a#.a#.52.k..lD.TR.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD.zJ#uO.lm.mT.YW.h..j2.po#fb#Hb#wK#jT#Cz.a#.lm.mP.a#.h5.lD.vh.9I.a3.RR.mWQtN.Oz#Yj#Yk.D..eM.P4.DuQtp.c8Qt..r..au.G0.v4.w2QtbQtaQtVQtVQtk.aFQtW.aIQtg#Yl#ie#Ym#Yn.lx.BM.TP.TP.Vu.kp#Yo.qG.fe#Yp.sX#bx#Yq#Yr#Ys#Yt#WE#Yu#Xu.lf#Nb.gX.gX#Nb.ib.R4.zF.zF.yd.fH.eR#Xw#uH.vb.#TQtf.cv.c8.aI.aI.#B.#B.#B.#B.#B.#B.#B.#B.aIQtaQtbQtb.#R.aK.bj.Du#c5#c5#c5#uH#uH#c5#c5#c5#c5#c5#c5#c5#c5#uH#c5#c5#c4#c4#c5#c4#c4#c5#c5#c5#c4#c4#c4#biQtV.dg.k6.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4QtR#Yv#Yw#Yx#Yy#Yz#YAQtR.vJ.k6.au.au.#A.dg.#A.dg.#y.#y.#y.#y.#y#ci#YB#YC#PN#YD#HH#LC#F0#F0#wA#wA#FV#JW#YE#wx#yu#xr#xr#zs#zs#zs#zs#xr#HK#zu#S##UK#WW#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#WW#V8#YF#HG#FX#YG#Gw#K6#wB#F0#wA#wA#F0#F0",
+".#y.#y.#y.#A.#y.#A.r..x9.dh.fZ.r0.zJ#YH.lJ.nm#B1.li#YI#YJ#YK#YL#YM#bg#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.bj#YN#YO.0F.0F.Vt#fa.0F#oN.bG.kw.erQtEQtEQtEQtEQtEQtEQtE.mr.tuQtFQtEQtEQtEQtE.bHQtKQtF.nt.yj#YP.gE.nr.LjQtDQtFQtEQtEQtF.a3.bGQtFQtFQtFQtFQtF.bH#DN.mt.sf.Xg.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#by#YQQtD.eoQtN.cj.ml.se.zJ.sd.zJ.lD.dy.gh.kR#LH.9V#YR#YS.JI#Fn#YT#YU#YV#YW#YX#YY#YZ#Y0.aV#Wu#Y1#Y2#Y3#Y4#Y5#Y6#Y7#Y8#Y9.j.#tu#Z..Si.hf.sg#gW#gX#yB#Hb.sR.a#.lm.a#.a#.a#.lm.qw.W9.lD.lD.lD.TR.zJ.zJ.zJ.zJ.zJ.zJ.zJ.zJ.zJ.zJ.zJ.lD.sd.cZ.mS.a..cU#tLQtJ.5U.YW.9F.Vv#Hb#xE#Hb.ci.a#.lm.a#.a#.cU.vh.zJ.9U.jk#Z#.sg.BL#Za#Zb.zx.yc.eO.#o.vb.#F.aFQt..k6.v4.v4.v4Qt..#R.bOQtbQtV#I7QtkQtV.aF.cvQtf.cu.Du#Zc#Zd#Ze.YV#Bd#by.TP.a5.2m.kx#wL.TP.bG.a1.dt#Zf.5Y#tJ.DQ#Nb.ku#Nb#Nb#Nb#Nb#Nb.ku#Zg.px.zD.yb.gF.ba.ao#Us.w3Qte.aL.c8.aI.aI.#B.#B.#B.#B.#B.#BQtW.aI.bOQt.QtbQt..c8.vc.Du#c5#c4#c5#uH#c5#c4#c5#c5#c4#c4#c5#c5#c4#c4#c5#c5#c5#c5#c5#c4#c4#c4#c5#c4#c5#c4.v5.t1.au.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.#y#Zh#Zi#Zj#Zk#Zl#Zm.IA.Mm.#y.#y.#y.#y.#y.#y.#y.#y.#y.#A.#S.#A.br#YB#R5#Zn#Zo#LC#Io#F0#F0#JU#F0#XO#WW#Jq#Zp#yu#xr#zs#zs#zs#zs#zs#zs#xr#wx#SY#Zq#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#XI#Zr#wA#F0#F0#wA#F0#F0#F0#F0#F0#F0#F0#F0",
+".#A.#A.#A.#A.#y.#y.#A.k6.x9.au.0N#Zs.ci#Zt#bp.e8.li.e8.li#Zu#Zv.EX#bi#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#Zw#uH#Zx#Zy.RM.Vt.Vt.Vt.mC.lE.bH.bHQtEQtEQtEQtEQtEQtEQtE.qr.kjQtKQtEQtEQtEQtE.bH.#6QtF.nt.HK#Zz.#m#c9.BJ#Uk.bHQtEQtE.bH.ch.bH.bHQtEQtEQtEQtE.bH.sQ.lp.g9.k#.h6.Ou.BM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.oDQtE.a0#ZAQtN.cj.nJ.gi.ji.zJ.lD.vh#IH.pA.j2#gX##D.TP#ZB#ZC#X0#ZD#ZE#ZF#ZG#ZH#ZI#ZJ#ZK#ZL#ZM#ZN#ZO#ZP#ZQ#ZR#ZS#ZT#ZU#ZV.nt.5V#ZW#ZX.rs.pA#IH.9X#Hb#jT#ZY.a..lm.lm.a#.a#.a#.a#.cU#tJ.qw.sR.cZ.km.bK#dk.ci.bK.po.vh.vh.Xh.bK.re.TA.kq.a#.g6.a2.dv.2n.9I.lx#Hb#xE#CA#xE.9F.TN.mS.a#.a#.a#.a#.lm#dk.lD.Vt.fe.qGQtN#oi#ZZ.BC.zt.zo.eR.fJ.w3.bW.aF.dh.k6.v4.w2.v4.dhQt.Qt..w2QtbQtaQta.#Q.aL.#P.aGQtf.aL.aM.ba#Z0#wL.TM.0A#bx.W7.ep#bx.TP.mx#xC.hh.qz.id.gX.rt.qx.qx.gX.gX.qx.gX.qx.qx.hg.oC.HG#Nc#eP.zp.fE.#h#Xw#c5.vc.#P.cv.aF#rK.#B.#B.#B.#B.#B.c8QtkQtbQtbQtbQtb.aL#bg#c5#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c4#c5#c5#c4#c4#c4.Du#c4#c5.Du#c4#c4#c4#c5#c4.w4.bW.dh.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.x9.v4.v4.v4.k6.au.au.#A#ci.Oe#UA#Z1#Z2#Z3#Z4.IA.xL.#y.#A.av.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.AK#YC#K8#Z5#Ir#wA#KH#F0#JU#FX#wz#Z6#xq#yt#xr#zs#zs#zs#zs#zs#zs#zs#zs#xr#zt#Z7#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#F1#Z8#JN#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0",
+".#A.#A.#A.#A.#A.#y.ah.dg.v4.dhQt##Z9QtH#0.#0##d#.li.li.S#.sL#j5#0a#be#F2#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#ic#r3#0b#0c.RM##R.Vt#fa.zJQtJ.j#QtFQtEQtEQtEQtEQtEQtEQtE.nt.sNQtFQtEQtEQtEQtE.bHQtKQtF.nt.36#0d#uD#0e.qq.g9.bHQtEQtE.bH.bH.bHQtEQtEQtEQtEQtEQtE.qr.zK.nt.bHQtFQtF.lF.a3.0A.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.h2.ko.aZ.ph.g6.g6#0f.rs.po.lD.lD.km#Yi.pA#wM.pM#BT.lx#CA#M4#0g#ie#0h#0i#0j#0k#0l#0m#0n#0o#0p#0q#0r#0s#0t#H9#0u#0v#0w#0x.#8.2c.2f.lC.pA.pA#Yi.sW#wK#Hb.7T.lf.a#.lm.a#.lm.a#.lm.lm.a#.a#.hV.a#.a#.lm.lm.lm.lm.cU.cU.cU.cU.lm.a#.a#.a..cU.sX#bx#0y.Vv#Hb.TN#CA#xE#CA#Hb.TP.2a.cV.lm.a#.a#.a#.a#.cR.zJ.zJ.kn.ib#De.TL.Jr#LR.EX.eM.yf.Dt.#T.cv.bO.au#XD.w2.w2.v4.v4.dh.#R.dhQt..aK.#B.bW.bW.bWQtf.#P.aH.aH.aF#F8#Ys#0z.ro.kn#D..3Y.TP.TP.ja.qz.if.T5.T5#Pk.T5#0A#0A#0A#0A.qx.qx#0A#0A#0A.gi#0B#0C#Lk.zv.zp.fE.#h.y..Jk.vc.#P.cv.aI.aI.aI.KE.#BQtW.aKQt..#RQt.Qtb.#F.L##c4#c5#c5#c4#c5.Jk#c4#c5#c4#c4#c4.Du#c4.Du#c4.w4.w4.Du#c4.Du#c4#c4.Du#c4.Jk#c4.#PQt..dg.v4.w2.w2.v4.v4.v4.v4.v4.k6.au.au.dg.#A.#A.#A.#y.#y.vJ.ha#UA#0D#0E#0F#0G.IA.xL.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y#PA.GZ#0H#0I#LC#wA#KH#F0#wA#Il#Fl#0J#HK#vw#xr#zs#zs#zs#zs#zs#zs#zs#HL#xr#xr#ww#Z7#WY#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#F1#0K#0L#0M#wB#HH#F0#wA#wA#JU#JU#JU",
+".#A.#A.#A.#A.#A.#y.#y.av.r..v4.uu#HM#lv.zJ#0N#UM.li.li#jW#Os#0O#eW#be#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4#ET#0P#0Q.RM#tK.Vt.Vt#wM.RRQtKQtEQtEQtEQtEQtEQtEQtE.g9.tuQtFQtEQtEQtEQtE.bHQtKQtF.nt.rm#0R#uH#0S.7N.nt.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.g9.0y.2cQtEQtE.bHQtFQtF.ch.sf.h2.Xb.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.3Y.rA.cX.lw.ic.cj.nJ.rs.se.zJ.lD#gW.sg.jh.Vw.r2.ph#Hb#Da#0T#eP#NV#0U#0V#0W#0X#0Y#0Z.fz#00#01#02#03#04#05#06#07#08#09#1..Vv#lw.0F.dy.gh.pA#tJ.0F#Hb#xE.9V.dv.lm.a#.a#.lm.a#.lm.lm.a#.lm.lm.a#.a#.a#.a#.a#.a#.a#.a#.a#.a#.a..a..cU.ku#1#.Vv.lx#yE#yF.ji#Rk#Fr#Hb#CA#wK.BM.r1.a..lm.a#.a#.a#.lm.bK.lD.0F.kq.tM#ZY#1a.Dc.zw.ya.dS.P4.Jk.#F.c8Qt..G0.v4.w2.w2.x9.v4.v4.w2Qtk.c8.cv.gV.cv.bW.aL.bW.bW.bW.bW.cv.cv#sPQte#1b#1c.OF.2b.DP#1d.T5.T5.if.if.5Y.T5.qx.qx#1e#1e#1e#1f.gj.qx#0A.qx#0A.gi#1g#1h#1i#1j.zp.gF.#i#Xw.Jk.v5QtpQtf.cv.c8.c8.c8Qta.#R.dh.dh.bO.bj.Du.Du#c4#c4#c4#c4.w4.vb.w4.Du.Du.Du.Du.Du.w4.Du.Du.Du.vb.Du.Jk.w4.Du#c4#c5.Jk.3U.bO.dg.r..k6.k6.au.au.#A.dg.#A.#y.#A.#y.#y.#y.#y.#y.#A.#y.MmQtS#1k#1l#1m#1n#1o.Oe.xL.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.AK.r.#1p#Od#Io#Io#KH#F0#wA#K6#1q#UH#Zp#yu#HL#zs#zs#zs#zs#zs#zs#zs#zs#xr#zt#ww#1r#xx#WW#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#Vr#Vw#Vw#Vw#Zq#Sa#Ev#Vw#yq#Sb#Jo#JY#JY#V9#1s",
+".#A.#A.#A.#A.#A.#A.#y.#y.#A.r.#1t.Bt.Xh.W9#1u#bp.e8.li#B1#1v#1w#1x.Jk.w4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4Qte#1y#1z.0F.0F.Vt#tK.Vt.aaQtK.bHQtEQtEQtEQtEQtEQtE.a0.kjQtFQtEQtEQtEQtE.bHQtKQtF.2c.rm#1A.bd#1B.Vm.nt.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.g9.0y.sQQtEQtEQtEQtE.bHQtFQtF.lG.cW.3Y.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.tK.bHQtF.rA.j2QtN.nJ.rs.po.lD.se.gh.pA.pB.oD.rA#Hb#wK#Hb#Da#1C#1D#1E#1F#1G#1H#1I#1J#1K#1L#1M#1N#1O#1P#1Q#1R#1S#1T##N#1U.TQ.aa.gi.qw#IH.gh.pA.ku#HS#xE#Hb.9V.7I.a2.cU.a..a..a..cV.g6.cV.a#.a#.cV.cV.cV.a..a..a..cV.lm.a2#uP.sc#LH#IF.9X.ds.km.jh#Yh.hf#Yh.ja#wK#CA.Vv.M6.a2.a#.a#.a#.a#.a#.kq.MV.se.a2.g6.kn#io#1V.P6.zD.Hd.YK.#o.vb.bWQtk.w2.au.w2.v4.v4.v4.w2Qta.aI.KE.#B.cv.aw.gV.gV.c8.cv.cvQtf.bW.cv.c8.c8.c8#EU.#T#1W#1X#1Y#1Z.id.if.if.hi.T5.qx.qx.gX.Si.Xc.ro#xE.dt#Nb.qx#0A#0A.gX#10#11#12.zA#rp.zp#Ah#l#.P4#13.Du.vc.#F.gW.wZQtV.#w.dh.dh.bO.#T.Du.Du.w4.w4.Du.w4.w4.w4.Du.Du#c4.w4.Du.Du.w4#c4#c4.Du.w4.w4.w4#c4#c4.Du#c4#biQtk.#y.#y.dg.#A.#y.#y.#y.#y.#y.#y.#y.av.#y.av.#A.#A.#A.#y.wC.e4#14#15#16#17#18.5..Mm.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.AK.#y#19#2.#In#Io#F0#Im#F0#wA#Z8#Z7#yt#yu#HL#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#xr#zu#2##xs#WW#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Z7#zt#xq#xq#xq#ww#ww#wx#wx#yt#yu#vw#vw#vw#vw#vw",
+".#A.#A.#A.#A.#A.#A.#A.#y.#y.#A.Tm#bI#xC.cR.xc#D2.OQ.li#B1.S##2a#WI.W2#bg#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4#2b#2c#2d#Hd.Vt.Vt.Vt.sYQtEQtFQtEQtEQtEQtEQtEQtE.#6.kj.cXQtEQtEQtEQtE.bH.#6QtK.nt.i7#2e.#o#2f.Dx#NJ.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE#gC.0y.j.QtEQtEQtEQtEQtEQtEQtE.bH.cX.bH#mG.3Y.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.rBQtF.bH.#6.roQtN.hV.gh.po.lD.k..pA#tJ.zJ#2g#2h#gX#wK.Vv.Vw.0v#2i#2j#ie#Xt#2k#2l#2m#2n#2o#2p#2q#2r#2s#2t#2u#P4#2v#1.#1.#CA.bM.r5.Ej.gi.jh.pA#IH.h.#Hb#xE#wK.9V#Rs#Ms.kn.zJ.ci.k.#vB.jb.TA.HJ.ox.TA#vB.k..po.g.#j2QtJ.Vu#zB.kv.sX.jc.pA.hf#tJ.pA.pA.pA.pA.jn.7T#xE.BMQtJ.oo.lm.a#.a#.a#.a#.dx#yw.mTQtI.ds.M6.mT#2w.zs.P5.eM#MW.fJ#bi.c8.cu.v4.G0.v4.dhQt#Qtk.aI.#B.#B.KE.KE.#B.cv.cv.cv.cv.gV.c8.#B.cv.c8.gV.gV.c8.cv.aF.cu.RP.Ht.kR#yw.rt.if#yw.qx.qx.T5#2x#gX.7T#Fr#sz.tJ.op.hh.pE.gX#2y#2z.iO#2A#2B#2C#2D.zp#Ah.dS.bc.#o#c5.w4.w1.bW.aF.bOQt.Qt..#T.Du.w4.w4.Du.w4.w4.vb.w4.w4.w4.w4.w4.w4.vb.w4.Du.Du.Du#c4#c4#c4.w4#c4#c5#bg.#Q.dgQth.#y.#A.#z.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.IA.e4#14#2E#2F#2G#2H.GnQtS.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y#ci#YB#O7#2I#2J#JN#LC#F0#Im#F0#F0#HH#Fl#xq#vw#HL#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#yu#xq#2K#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#2L#xq#wx#yu#HK#xr#HL#Ag#zs#zs#HL#Ag#Ag#Ag#Ag#HL",
+".#A.#A.#A.#A.#A.#A.#A.#A.#y.ah.#A.w7.2##lv#2M#2N#UM.li.li#d#.M9.cS.Fo#be#c4#c5#c5#c5#c5#c5#c5#c5#c5#c4.w4#c4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#icQte#2O#2P.RM##R.Vt.zJ.lD.dwQtKQtEQtEQtEQtEQtEQtE.#6.kj.kwQtEQtEQtEQtE.bH.#6QtK.g9.i6#2Q#2R#2S.Vm.j..bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.#6.lp#2TQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.kw.bH#c8.aY.Xb.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.RRQtKQtE.aZ.vt.cj.tM.gh.vh.Vt#IH.gh.ic.a1##x#2U#2v.9V.sY.DQ#2V#1e#2W#2X#2Y#2Z.cQ#20#21#22#23#24#25#26.nK.lW.5U.YW.TN#1U#gX.Oz#IH#IH.gi#IH.gh#27.oF#II#28#Hb#wK.9V#GD.9V.9V#wK#wK#Hb#Hb#Hb#Hb#Hb#wK#wK#wK#wK#CA.tG.7J.mC.pB.pA.hf.pA.pA.pA.pA.pA.pA.gh.gh.gh.h.#Hb.TP.sc.f7.lm.a#.a#.a#.my.a#.a..mS.kp.33.5U.BL#29.EX.ya.cr.ao.Jk.#F.aIQt#.v4Qt.Qta.aFQtW.#B.#B.#B.#B.#B.#B.KE.KE.#B.aIQtW.cv.cv.gV.gV.cv.aw.aw.gV.fX.cu#3.#3#.lf.gX.gX.rt.if.if.if.rt#3a#3b.aa#bB.tz.5V#m3.bG.mU.RM.lC#3c.rW#3d.mf#3e#3f#3g.zD.zF.Bn.qS.#h#V0.#q.vb.bjQtf.t1.aF.w1#c4.Du.w4.w4.w4.w4.vb.w4#c4.w4.w4.Du#c4#c4#c4#c4#c4#c4#c4#c5#c5#c5#c5.w4.aw.au.br.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.xL.ee#3h#3i#3j#3k.ah.IA.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQtS.AK.r.#3l#3m#3n#HH#LC#F0#Im#F0#F0#Im#wA#3o#yt#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#wx#Jp#Wa#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#WY#WY#3p#3p#3p#F1#3q#0J#Rh#3r#ww#xr#HK#xr#xr#xr#zs#zs#zs#zs",
+".#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.tA.Oo#f#.RM#3s#bp.OQ.li#B1.Ly#3t#3u#be#c4#c5#c5#c5#c5#c5#c5#c5.w4.#p#YM#YM.YK.16#c4#c5#c5#c5#c5#c5#c5#c5#c5.w4#MX#3v#3w.0F.Vt.Vt.vh.7T.aZ.bHQtEQtEQtEQtEQtE.#6.kj#3xQtEQtEQtEQtE.bH.#6QtK.g9.yj.kf#3y#3z.vj.OF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.aZ.lp.j.QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.kw.k#.g5#Bd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.cWQtKQtEQtF.rz.lf.g6#3A.0F.jn.pA.pA.sR#3B#c2.W3.tJ.7T.id#3C#3D#3E#3F#3G#3H#3I#3J#lbQtNQtN#lb#3K#3K#3K.a#.lm.a#.sR.ro#gXQtJ.qz.Ej.jh.gi#IH.gh.rs.ku.sY.h..TN#xE#wK#wK.9V#wK#wK#wK#xE.7T#Fr.h.#1##yF.ds.se.jn.sR.gh#tJ.hf.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh.jh.rs#EE#wK.BM##Q.uk.a#.lm.a#.cV.hV.HJ.7I.Vu.uB.7J.lm.ym#3L#3M.fF.#h.#q#bg.bW.#QQtaQtV.aI.#B.#B.#B.#B.#B.#B.#B.#B.#B.#B.KE.KEQtW.aI.KE.KE.cv.cv.cv.cv.aw.gV.qZ.0O.BG#tJ.T5#Nb#Nb.gX.T5.if.if.if.rt.qx.hi.OA.3V.W7#3N.Lr#zA.qy#NH.pE#3O.qe#3P#3Q#3R#3S#3T#3U.zq.yb#X0.gI.yf#L..Jk#bg.#C.Fr#3V#uH#c5.Du.w4.Du.Du.w4.Du#c4#c4#c4#Lm#c5#c5#c4#c4#c4#c4#c5#c4#c5#c4.w0.#RQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.br.Oe.au#3W#3X#3Y#3Z#30.7#.br.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.br#PA.br#31#32#33#Je#LC#Io#F0#Im#F0#F0#F0#wA#Io#Eu#zu#yu#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#HK#xq#34#Wa#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#WY#3p#3p#TR#35#36#BR#37#37#KI#38#39#F1#WW#S0#2L#4.#UJ#W##xq#xr#HK#xr#xr",
+".#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQtj.fK#4#.km#4a#1v#d#.li.li.S##4b#4c#c4#JR#c5#c5#c5#c5#c5#c5#c4.w4.Qf.YL.YL.YL.zo#13.w4#c5#c5#c5#c5#c5#c5#c5#Lm.#F#4d#4e.RM#tK.Vt.Vt.5U.qiQtFQtEQtEQtEQtEQtE.#6.kj.kwQtEQtEQtEQtE.bH.#6QtK.#6.ms.W6#4f#4g.vj.pt.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtK.lp#4hQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.a4QtK.cg.LB.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.umQtFQtE.bH.kw.wc.cj.gh.km.jh.gh.sZ.Lg#xz.W2#vE#4i.TM.Yr#3D#3F#4j#4k#4l#4m#4n#0g#4o.iS.qJQtN.cj.a#.lm.a#.lm.a#.cV.dx.kn#QT.gi.gi.gi.gi.gh.pA.pA.pA#tJ.gh.sR.re.j2.zJ#P3.lD.vh.ci.pB#gW.jh.gh.pA.hf.hf.hf.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh.pA.ro#jT.YU.lf.cV.a..g6.ib.po.eo.9V.0A.uB.Vu.jbQtN#4p#4q.ya.fH.yf.Jk.#C.#P.cvQtW.aI.#B.#B.#B.#B.#B.KE.#B.KE.KE.KE.KE.KE.KE.KE.#B.#B.KE.#B.#B.cv.#B.c8.fX.aw.Vq.qw.g.#Yo#Nb#Nb.gX.gj.if.gj.gj.rt.gj.rt.qx#4r.sa.id#in.ki.JW.cT.9X.yl#4s#4t#4u#4v#4w#IP#4x#4y#eP.OI.xv.eM.dS.#f.#o.#f.gE.P4.#p#Ut#uH#c5#c4.w4#c4#c4#c4#c4#c4#c5#c4#c4#c4#c5#c4#c5#c5.Fr#biQtV.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.IA.Of#4z#4A#4B#4C#4D#4E.#D.xL.#A.#A.#A.#A.#A.#A.av.#yQtS.wC.wC.aE#4F#4G#4H#4I#Io#Io#F0#F0#F0#F0#F0#JU#JU#PK#4J#1s#W##yu#4K#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#Ag#xr#HK#yu#yt#xr#xq#UH#4L#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#WY#3p#F1#36#4M#4N#4O#4P#4Q#4R.w4#c4#c4#4S#4T#4U#4V#4W#4X#35#4Y#TR#W.#4Z#34#UJ#40",
+".#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y#41.JFQtH.sd#42#Wd.e8.li#B1#43.j.#W9#bg#Lm#c5#c5#c5#c5#c5.w4#rE.zs.YL.YL.YL.YL.HH.fJ.w4#c4#c5#c5#c5#c5#c5#c5#J6#44#45.Xh.sd.Vt.zJ.Xh.lE.#7.bHQtEQtEQtEQtE#gC.kj.nPQtEQtEQtEQtE.bHQtKQtK.#6.ms.ps#46#Wi.vj.wgQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtK.kc.55QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.rA.e6.TP.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.0A.ch.cSQtEQtE.a0#bxQtN.h5.qw.pA.gh.pA#47#eY.OI.aF#48.Vu#3C#49#5.#4j#4j#5##5a#5b#5c#Xo#5d#5e#5f.g.#tI.a#.g6.a#.a#.mP.a#.nM#yw.gi.gi.gi.gi.gh.pA.pA.pA.pA.pA.pA.pA#Yh.hf.hf.hf.hf.hf.pA.pA.pA.pA.gh.gh.gh.gh#IH.gh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh.jh#LH#II#fc.nM.ic.km#tL.LB#bA.Xg.33.BM#bz.tI.g6QtN.MT#1D.zo.m7.#a.Du.v5Qtp.cv.aF.aI.KE.KE.KE.KE.KE.#B.KE.#B.#B.#B.#B.#B.cv.cv.cv.cv.aw.cv.c8.c8.gVQtW.#B#rE#5g#J7.sc.id.gX#Nb.gX.ky.gj.gj.gj.rt.if.gj.gj.if.gj.if.lC.TB#5h.sO#G8#yH.BK#5i#5j#5k#5l#5m.Bz#Lk#C7#eP.zr.OI.y##Ah.zo.y#.eM#l###l#tt#Ut#uH#c4.Du.Du#c4#c4#c4#c4#c5#c5#c4#c4#c4#c4#c5#c4Qt0.#AQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQtS.Gn.ag#5n#5o#5p#5q#5r#5s#5t#5u.xL.#A.#A.#A.#A.#yQtS.wC#as.#y#5v#5w#5x#5y#5z#In#LC#wA#F0#KH#F0#F0#JU#wA#Je#Is#F1#Ew#ww#yt#HK#zs#zs#zs#zs#zs#zs#zs#HL#xr#xr#HK#yu#vw#yt#ww#SY#5A#Jo#5B#F1#F1#WY#Jo#Jo#Jo#Jo#Jo#Jo#5C#WY#4Y#5D#5E#Jk#5F#bc.atQt5.cE#bc.Du#uH#5G#5H#5I#5I#5J#5J#5J#5K#5L#4W#5D#3p#Jo#3p#5M#F1",
+".#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQtj.bg#5N.hi#5O#5P.OQ.li#B1.li#5Q.Dd#be#c4#c5#c5#c5#c5#c5.w4#5R#eP.YL.YL.YL.YL.YL#5S.#m.w4#c4#c5#c5#c5#c5#c5.Jk#G5#G6#bu.vh.Vt.Vt.Vt#j2.k#QtFQtEQtEQtEQtE.#6.sOQtKQtEQtEQtEQtE.bHQtKQtK.#6.i6.nr#0g#Pj.Vm.wgQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.or.j..fdQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK.um.7Z.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.tK.cS.a4QtEQtEQtK.cT.hV.zO#IH.pA#5T#5U#xz.D..JI.ai.bu#ID#5V#3E#5.#5##4j#4j#4j#5W#5X#5Y#5Z#to#50#51#52#gU.kp#lv.J0.g6.cV.52#IH.gi.gi.gi.gi#IH.gh.pA.pA.pA.pA.pA.pA.pA.pA.gh#IH#IH.gh.gh#IH.jh.gi.gi.gh.gh.gh.gh.gh.pA.pA.gh#IH.gh#IH.gh.pA.pA.pA.pA.pA.pA.pA.j2#wK#gF.53.h..9F.h6.OF.ow#e1.Vv.YV.YV.YV.jc.du.lm#Up#bb.eI.gE.#a.Fr.v5.#P.c8.aI.#B.#B.#B.#B.#B.#B.cv.cv.cv.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.gV.fX.#F.wZ#53.gh#yEQtJ.g..T5#Nb.gX.T5.gj.if.if.jn#Pk.j2#Pk.Wj.gj.if.id.FC.cS.Ou.RL#54#55#56#57.l8#58#59#6.#6#.Jo#LR#Vg.zw.zv.zx.zw.zr#1D.Qf.Vi#V0.#p#uH#uH.Jk.w4#c4#c4#c4#c4#c4#c5#c4#c4#c5#c4.w4.aFQtc.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQtR.AL#6a#6b#6c#6d#6e#6f#6g#6h#6i#6j#kM.vJ.br#ci.IA.ICQtS#6k#6l#6m#6n#6o#6p#6q#PJ#HH#F0#F0#KH#F0#JU#wA#Je#XJ#6r#Rh#yu#yt#yu#xr#xr#HL#zs#zs#xr#xr#HK#vw#yt#HK#ww#Rg#UJ#6s#JW#6t#XO#xv#6u#6v#3p#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#5C#3p#Vr#6w#6x.cEQt5.vb#bg.vb#c4#c5#Sf#5G#5K#6y#6y#6y#6y#6y#6y#6z#5I#5J#6A#6B#6C#6D#6E#6F#6G",
+".#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.dC#6H.TU.TR#6I#UM.li.li#jW#6J#6K#bk.w4#c4#c5#c5#c5#c5#c4#NV.Tu.YL.YL.YL.YL.YL.YL.zE.Tt#c4#c4#c5#c5#c5#c5#c5#bg#Ez#jH.Xh.zJ.Vt.zJ.0F.mD.aZQtEQtEQtEQtE.#6.kjQtKQtEQtEQtEQtE.bHQtFQtK.#6.yj.qo#6L#6M.Tx.ptQtKQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.kc.kkQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK.ow#jS.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.sfQtK.bHQtEQtE.bH.h3.k..J0.gh.gh#VS#6N#2R.zqQt6QthQti.eN#6O#6P#6Q#6R#4j#6S#4j#4j#4j#6T#6U#6V.hg#6W.Jv#6X#6Y.Vw.dv.7V.jc.mS.gi.gi.gi.gi.tE.gi.lf.pA.pA.pA.pA.pA.pA.pA.pA#IH.gi.gi.gi.gi.gi.gi.gi.gh.pA.pA.pA.pA.pA.pA.pA.gh#IH.gh.gh.gh.pA.pA.pA.pA.pA.pA.pA.ro#wK#Hb.rB.OL.Qb.pt.Q##jT.Vv.YV.Vv.BM##N.hV.du.qD#6Z.ye.gI.#j.#q#JR.w1#Fm.c8.c8.cv.cv.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.fX.bW.bj#46#3#.g.#yB#GD#wL.pE#Nb#Nb.gX.if.if.jn.rt.ig#J7#1f.hi.rt#Pk.qz.5T.rz#Z##Hb#60.Jq.ah#61.Jk#ts#46##r.T6.ym#62#63#64.Bw.Bw#Zb.zA.zt.JI#NV.Vi.bc#65#vE#c5#c5#c4.Du#c4#c4#c4#c4#c4#c4#c5#biQt.Qth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.Mm.Gn#66#67#68#69#7.#7##7a#Qi#OZ#Er.dp#7b#7c#5u#7d#7e#7f#7g#7h#7i#A5#7j#7k#7l#7m#F0#wB#F0#F0#wA#JU#PK#Eu#6r#Rh#yu#vw#HK#Vu#7n#7o#7p#7q#7p#yt#wx#xq#7r#7s#Zq#yr#wz#7t#Ns#HG#wA#PK#7u#7v#5B#Sb#F1#Wa#Jo#Jo#Jo#Jo#Jo#Jo#Jo#3p#4Y#7w#7xQt5.vb.w4.w4.w4#c4#Ut#5G#5K#6y#5K#5K#6y#5K#6y#6y#6y#6y#6y#6y#6y#6y#5H#7y#7z#7A#7B#7C",
+"Qth.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQti.cr.0x.hh#7D#Wd.OQ.li.OQ#Hg#7E#7F#7G.w4#c4#c5#c5#c4#c4.Bn.YL.YL.YL.YL.YL.YL.YL.YL.D#.#n.w4#Lm#c5#c5#c5#c5#c4#be#7H#7I.Vt.Vt.Vt.se.YWQtF.bHQtEQtEQtE.#6.kj.Y6QtEQtEQtEQtE.bHQtKQtK.#6.yj#7J#7K#uK.r7.2cQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtK.rn.kkQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.k#.LB.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TP.cgQtFQtEQtEQtE.er.aZ.jj.hV.jh.gh#De#7L.YK.fKQtj.#y.#y.tA#7M.nN#7N#6Q#6R#6S#4j#6R#4j#4j#5##7O#7N#2V.Co#7P.gh.rd#yw.W9.se.kR.gi.gi.gi.gi.gi.rd.jh.pA.pA.pA.pA.pA.pA.pA.pA.gh.gh.gh.gh.gh.gh.gh.gh.gh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.se.Vu.TQ.xa#7Q#Ix.#6.57.Vv.Vv.Vv.Vv.YV.BM.po.cQQtN#7R#NV#7S.dS.P4#uH.v5Qtp.cv.fX.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.cv.gV.cv.#T#7G#gw.kR#gX#28#Hb.qG#5V#Nb#Nb.gX.if.if.if.gj.rt.Oz.Si.if.if.ig.5Y.RM.h2.h2.9V.tI#ir#7T#7U.ah#7V#7W#7XQtN.a.#1a.#V.HG#7Y#7Z.Db.Br.P6.17.zp#lg.W2.yf#3V#uH#uH#c4#c4#c4#c5#c5#c4#c4#c5#bj.#A.d0.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A#ci.Oe#70#71#72#73#74#75#76#Er#77#78.dp.dp.dp#A5#V4#79#8.#8##Qh#Q0#RE#8a#8b#8c#KG#wB#wB#wB#F0#wA#PK#4J#8d#0J#zt#vw#HK#xr#8e#8f#8g#8h#8i#8j#8k#8l#8m#Zr#HI#Gw#wB#JU#Io#Io#Io#PK#XO#0K#8n#F1#Sb#F1#5C#Jo#Jo#Jo#Jo#Jo#Jo#5C#WY#3p#UG#8o#4Q.og.w4#c4#c4#uH#Sf#5H#6z#6y#5K#5K#5K#6y#5K#5K#6y#6y#6y#6y#6y#6y#8p#7y#8q#8r#8s#7C.Jc.Jc",
+".#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQtr#8t.uj#8u#8v#d#.li.iV.S##8w#8x#qc.Du.w4#c5#c5#c4#Us.JI.YL.YL.YL.YL.YL.YL.YL.YL.YL#X0#rj#c4#c5#c5#c5#c5#c5#be#IA.7T.Xh#tK.Vt.7U.7V.Qh.cXQtEQtEQtE.ve.0zQtKQtEQtEQtEQtE.bHQtFQtF.#6.yj.YP#8y#8z#8A.j.QtKQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.r7#8B.k#QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.kw.ko.TF.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.LBQtF.bHQtEQtEQtEQtEQtF.ep.rs.gh.ib#vA#8CQtXQtj.#y.#A.#AQti.uM.YX.pL#49#5.#5##4j#4j#6R#4j#4j#4j#5.#3D#8D#8E.hg.jh#IH#IH#IH.gi.jh#IH.gi.gi.gh.rs#8F.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA#Yh.gh.j2#8G#8H#QC#Wl#vL#8I.uA.ep#Ha.Vv.Vv.Vv.Vv.BM#EH.ml.cj.rd#Z.#7S.gF.#i#8J.w4.#C.#P.c8.c8.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.c8.bj#c1#WI.sR.ja#LE#CA#Hb.sY.Ef#Nb#Nb.gX.if.if.ie.pC.xa#8K.Lr#8L.pE.if#Pk.rt.fe.0A.9V.9V.Vv#8M#J7#8N.Hl#8OQtN.B0#8P#H##8Q#8R#8S#8T#8U#8V.JH#KT.zt#1D#qc.#i#KL#Ut#c5#c4#c4.Du#c4#c4#c4#c4.bW.#y.#y.qh.#A.#A.#A.#A.#A.#A.#A.#A.#yQtR.Gn#8W#8X#8Y#8Z#80#A5#OZ.bC#OZ.dp#Cp#Cp#A5#A5#Wv#81#82#83#84#85#86#87#88#Fi#Fi#wB#F0#F0#wA#wA#JV#yr#xx#ww#yt#yu#zt#89#9.#9##9a#9b#9c#9d#9e#9f.e0#9g#9h#9i#JU#JU#F0#K6#WT#Z8#9j#9k#F1#Sb#F1#Wa#Jo#Jo#Jo#Jo#Jo#Jo#Jo#5C#Jo#3p#Vr#9l#9mQt5.vb.w4#c5#Ut#9n#8p#6y#5K#8p#6y#5K#5K#5K#6y#5K#5K#6y#6y#6y#6y#8p#7y#7y#9o#9p#9q.K8.MM.Jc.Jc",
+"QtbQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQtr#9r.g..kt#9s#bp.OQ.li#B1#9t#9u.Hd.16#c4#c5#c5.w4#13.zq.D#.YL.YL.YL.YL.YL.YL.YL.D#.D#.#m.w4#c5#c5#c5#c5#c5.L##bg#9v#S8.sd.Vt.zJ.Xh.aa.aZ.bHQtEQtE.aZ.sOQtKQtEQtEQtEQtE.bHQtFQtF.g9.ms.YP#9w#9x#BY.OF.#6.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.rn#EA#EFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.k#.3Y.Vv.Vv.Vv.Vv.Vv.Vv.YV.sbQtFQtEQtEQtEQtEQtE.bHQtK.sW#Yh#9y#9z#9AQtR.#y.#A.#A.#A.#y#9B#9C.nJ.CK#8D#5.#7O#5##4j#4j#9D#6S#7O#49#9E#9E#9F.hg.gi.gi.Ej.gi.gi#IH.hg.gi.jh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA#tJ#Yh.kR.MV#9G#9H#9I#9J#9K#9L#zx#9M.35.BM.Vv.Vv.Vv.Vv.YV.33#P3.j9QtN#IE.yb.zo.dS.bc#vE.vcQtp.cv.fX.cv.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.c8#bj#NW#9N#9O.RM.Vu#Hb#xE#wK.OU#5V#Nb#Nb.gX.if.if.rt#9P.Ll.ns#9Q#9R.hg.if.rt.rt.kv.uB#vN.BM.YV.oD.BM.TM#f##lb#9S#9T#9U#9V#9W#9X#9Y.sa#3D.Q8#9Z.Fk.zw.zr.zo#ts##l#65#uH#c5.w4#c4#c4#c5#c4.c8QtS.#y.#A.#A.#A.av.#y.#z.av.#y.aN#90.Ra#J6#91#92#93#94#95#96#97#Cp#Cp#Cp#A5#Cp#98#99a..a.#a.aa.ba.ca.da.ea.f#Fi#wB#wB#F0#KH#F0#HG#6u#Sa#Jq#yt#7o#89#9.#89a.ga.ha.ia.ja.ka.l#Mna.ma.n.iO.mfa.oa.p#8c#Fia.q#6v#5B#WY#WW#Sb#F1#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#3p#TRa.ra.s.cE.vb.w4#c5#Sf#5H#5K#6y#6y#6y#5K#5K#5K#5K#5K#5K#6y#5K#5K#6y#8pa.ta.u#5Ka.va.w.WU.Tp.K8.MM.Jc.Jc.Jc",
+".#F.ah.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.uu.RY.uja.xa.y.nm.li.iV.FAa.z#Wi.#o.w4#c5#c5.w4#uH.D..YL.YL.YL.YL.YL.YL.YL.YL.YL.YL#X0.w4#c4#c5#c5#c5#c5.Fr#qda.A#Yn.vh.Vt.Vt.Vt.ja.h3QtFQtEQtEQtKa.BQtKQtEQtEQtEQtE.bHQtFQtK.#6.ms.qpa.C#Uta.D.wgQtK.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.rn.pt.k#QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.g8.oD.Vv.Vv.Vv.Vv.Vv.OuQtF.bHQtEQtEQtEQtEQtEQtE.aZ.LB#tJ.rt.ROa.EQti.d0.#A.#A.#A.#AQtja.Fa.G#Hc.hg#8E#3D#5.#7O#7O#6R#5##6R#7O#3D#3D#8E.hg.gi.gi.gi#IH#IH#IH.gi.Ej.gh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.sR.RM.ja.cka.Ha.Ia.Ja.Ka.La.Ma.N#bja.O.Xg.Vv.YV.YV.YV.Vv#vN.53.ic.cj.g6a.Pa.Q.eM.Tt#TA.w4.#C.#P.c8.c8.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.fXQtfa.Ra.S#xE.pDQtJ#Hb#xE#Hb#Hb.kx#Nb#Nb#Nb.gX.if.if.if.if.qza.T.rn#e5.qz.rt.rt.sa##P.YT.TM.TO#bz.TP.TP.0A.oD.lxa.Ua.Va.Wa.Xa.Ya.Z.jn.jn#yw.k..qwa.0.Lo.zs.JI.gF.Tt.#o#13#c5.w4#c4#c4#c4QtkQth.#y.#z.#y.#y.#y.dg.dg#B9a.1a.2a.3a.4a.5a.6a.7#94a.8a.9a#.a###BP#A5#Cp#Wva#aa#ba#ca#da#ea#fa#ga#ha#ia#ja#k#Im#F0#F0#F0#F0#F0a#la#ma#na#oa#pa#qa#ra#sa#ta#ua#va#wa#xa#y.nd#Qb.mfa#z.mf.mfa#Aa#Ba#Ca#D#xw#WW#Sba#Ea#Fa#Ga#Ha#Ha#Ha#Ha#Ha#Ga#Ia#J#F1#Jo#Jo#3p#3pa#Ka#L.L#Qt6.w4#c4#Ut#5K#8p#5K#6y#6ya#M#5K#5K#5K#5K#6y#6y#8p#6y#5Ka#N#7y#7ya#Oa#Pa#Q.PZ.Oj.K8.MM.Jc.Jc.Jc.Jc.Jc",
+"#bg.c8.as.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQtra#R.OA.r9a#S#Wd.OQ.li#B1a#Ta#U.bc#bg#c4#c5#c4#c5.C9.D#.YL.YL.YL.YL.YL.YL.YL.YL.Jl.Fpa#V.w4#c4#c5#c5#c5#c5.w4#uHa#W.Xh.zJ.Vt.zJ.sd.cTQtKQtEQtEQtK.trQtKQtEQtEQtEQtEQtE.bHQtFQtK.yk.YPa#X.Fra#Y.ln.#6.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.r7a#Z.k#QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK.h6.BM.Vv.Vv.Vv.0A.owQtFQtEQtEQtEQtEQtEQtEQtE.bH.bH.kx.31.pva#0.co.#y.#A.#A.#y.av.#A.coa#1a#2#tJ.hg.id.WGa#3#5.#5.#5.#5.#3D#3Da#3#8D.CK.jh.gi.gi.gi#IH#IH.gi#IH.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.rs.se#EE.r#a#4.W9a#5a#6#Mna#7a#8a#9aa.#id#Fp#xE.BM.YV.BM.YV.Vv.TP.tI.cj.bJ#9S#ie#Ah.dS.an.16.v5.#F.aw.fX.t1.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV#vH.bj.YKaa##0w.YW#Hb#xE#CA#xE#HS.hh.gX#Nb#Nb.T5.if.ig.if.if#Pk.ci.Xc#Tx.7S.qz.if#8F#Iwaaa.2n#LP.ck.0A.0A.BM#jTaab#LAaacaad.TR.jm.rt.jn.rt.if.if.5Y.qwaae.zu.P5#7S.W2.ao.fJ#c5#c4#c4#c4.aF.as.dg.#A.au.v4.k6.Tmaaf#Doaagaahaaiaajaakaal#Fkaam#Fi#Fiaanaao#BP#Ti#81#82aapaaqaaraasaataauaavaawaaxaay#KH#F0#F0#F0#F0#KH#HHaaz#0LaaAaaBaaCaaDaaEaaFaaGaaHaaHaaIaaJaaKaaL.mf.q6.mf.mf.mf.mf.v2.sFaaMaaNaaOa#Ga#EaaPaaQaaRaaSaaTaaUaaVaaWaaXaaYaaZ#5Ca#Haa0#BRaa1aa2Qt6.vb#c4#Ut#9n#8p#6y#8p#8p#6y#6y#6y#5K#5K#5K#5K#6y#5K#8p#7yaa3#8qaa4aa5aa6.Tp.L..K8.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc",
+".w4#biQtbQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.ef#to.ujaa7aa8.nm.e8.liaa9ab.ab#.v..w4#c5#c5#c4#46.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL#NV.Du#c4#c5#c5#c5#c5#c5#yzaba.QB.Vt.Vt.Vt.Xh.54QtK.bHQtEQtK.trQtKQtEQtEQtEQtEQtEQtE.bHQtK#mD.rkabb.w4abc.i4.g9.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.mt.JY.k#QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.57.YV.Vv.BM.QhQtK.bHQtEQtEQtEQtEQtEQtEQtEQtEQtK.lwabdQtEabeQtr.#S.#y.#y.#y.#y.dg.#Aabf##m#tJ#IH.gi.hg#9F.WGa#3#3Da#3a#3#3D.WG#9F.gi#IH.gi#IH#IH.gi#IH.gi.TA.gh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh.gh.gh#IH.gh.ku#gW.hgabgabhabi.q7abjabk.0Dabl#be.w4abm#2v.9V.9F.BM.Vv.YV.BM.cZQtNQtNabn.qS.eM.gEabo#c4#be.#E.cv.c8.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw##j.t1.cv#be#3Vabp#2v#xE#Hb#xE#CA#CA#28#yE.T5#Nb#Nb#Nb.gX.if.hi#Pk.if#Pk.vh#dk#Pk.fca.T.rtabqabr.ah#1jabs.a..sX#Hb.oD.Vvabtabuabvabw#II.7T#3a.qx.rt.if.ig#Pk.sa.rtabx#1D#46.Vi##l.bd#Us#c5#c4.cv.dg.v4.v4.w2.w2#HzabyabzabAabBabCabDabEabFabG#Qm#wB#FiabHabI#WvabJ#WvabKabLabMabNabOabPabQabRabSabTabU#KH#F0#F0#F0#F0#F0#HHabVabWabXabYabZab0aaHab1ab2ab3ab4ab4ab5ab6ab7ab8.fS.q6.mf.mf.mf.mf.mf.ndab9ac.ac#acaacbaccacd#QK.aAaceacfacgacfacfach.jyaciacjackaclacm.dH.vb.Du.16#9n#5K#6y#6y#8p#6y#6y#6y#6y#6y#5K#5K#5K#8pa.taa3acnacoacpacq.Tq.Oj.K7.Jc.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc",
+".w4.w4Qtp.#DQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.ex.yRacr#dkacs.mH.OQ.li#B1actacu.bb.w4#c4#c5.w4#qc#eP.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL#JA.#o.w4#c4#c5#c5#c5#c5#bi##s.71.Xh.zJ.Vt.Vt.mCQtGQtFQtEQtK.DxQtKQtEQtEQtEQtEQtEQtEQtFQtK.7Oacvacw#bgacx.i7.nt.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.kjacy#EFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.ch.0A.33.eqQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.kw.ab.OFacz.aN.au.au.k6.k6.v4.v4.v4.v4.aN.jf.tMacA.gi.gi.hg#5V#9F.WG.WG#10.lC.hg.gi.gi.gi.Ej.gi.gi#IH.gi.kR.gh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh.gh.gh.gh.gh.gh.gh.gh.gh#tJ.J0.tMacBacCacD.qbacEacFacG.DB#DaacH#bj.#pacI#T.#xE.YV.YV.Vv.BM.Vu.J0QtNacJacK.qS.W2#Xw#Us#bg#r3.bW.c8.c8.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.cv.cv.gV.fX.wZ.Fr.FracL.Xg.TQ#xE#xE#CA#CA#CA#Hb.9XacM#Nb#Nb#Nb.gX.if.ig#Pk#Pk#Pk.vh#J7#J7#yw.opacNacOacPacQacR.apacSQtN.hf.ja.roacTacUacV.oQ#by.0A.Xb#wL.ig.ig.kuacW.e5#5U.Hp.Fp#X0.gI#V0#65#Us#c4.3U.#A.v4.w2.w2.G0acXacYacZac0ac1ac2ac3ac4ac5ac6#wB#Fi#wBac7ac8ac9ad.ad#adaadbadcaddadeadfadgadhadiadjaay#KH#F0#F0#F0#F0aayabVadkadladmadnadoadpab4ab4adqab4ab4ab4adradsadt.gR.fS.qc.mf.mfaaL#QbaduadvadwadxadyadzadAadBadCadDadEadFadGadHadIadJ.d..d..d.#I3adKacgadLadMadNadO#5G#8pa#M#8p#6y#6y#6y#6y#6y#6y#6y#6y#5Ka.tadPa.tadQadRadS.14.Oj.K7.K8.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc",
+".w4.w4#J6.gW.ah.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#UadT.TU.sdadU#bp.OQ.li.liadVadW#bc#c4#c5.w4#xz.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.qS.w4#c4#c5#c5#c5#c5#c4.v5adX.TR.Vt.Vt.zJ.Vt.aY.#7QtE.#6.DxQtKQtEQtEQtEQtEQtEQtE.bHQtFadY.kiadZ.L#ad0.lo.j.QtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.kj.JYQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF#c8QtG.cX.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.uAad1Qt..r..w2.w2.w2.w2.w2.w2.w2.w2.Tm.au#mA.nJ.J0#Xl#tJ#IH.gh.J0.ic.gi.gi.gi.gi#IH.gi.jh.gi.gi.gi.hg#3A.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh#IH.gh.pA.gh.gh.gh.J0.tM.qJad2ad3.sZ#Uj.j2ad4ad5ad6ad7ad8ad9ae.ae#.kx.BUaea#I8.w4.YKaeb#Da#LE.BM.Vv.Vv.TP.Vw.cjQtNaec.YK#ts.yf.bd#c4.#C.gW.cv.c8.gV.aw.aw.aw.aw.aw.cv.cv.cv.cv.cv.#B.#B.#B.#B.#B.aI.#B.#T.16#8zaed.i6.7T#xE#CA#CA#CA#CA#CA#wK.dt#Nb#Nb#Nb#Nb.gX.if.hh.if.k..T5.0F.vh.j2.hgaeeaefaegaeh.fM.r..eeQtWaei.sRQtN.nMaejaekael.7J#e1.lE.TP.oD##N.32aem.fFaenaeoaepaeq#XW.Vi.P4.bd#uH#c4Qt..r..w2.v4#Ne.G1aeraesaetaeuaevaewaexaeyaayaeza.faeAaeBaeCaeDaeEaeFaeGaeHaeIaeJ.JdaeKaeLaeMaeNaeOaeP#F0#KH#HHaeQaeRaeSaeTaeUaeVaeWaeXaeYaeZadqab4ab4ab4ab4ab4ae0ae1ae2.rX.fS.htae3ae3ae4ae5ae6ae7ae8ae9af.af#afaafbab6ab6ab6afcafdaf#afeaffafgafhafiafjafkafl.#Lafm#eHafnafoafpa#Mafq#8p#8p#8p#8p#8p#8pafqafr#7y#7y#8qafsaft.Vf.0o.L..K8.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc",
+".w4.w4.w4#bg.cv.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQth.#s.e5.32afu#0##d#.li#B1#Dlafv.w5.w4#c5.w4#bk.Fp.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.JI.Dt#F2#c5#c5#c5#c5#c5#bjafw.ro.se#tK.Vt.vh.ac.Qi.bHQtK.OT.cXQtEQtEQtEQtEQtEQtE.bH.oP.oq.kiafx.w5afy.tw.ptQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.mt.i0.fdQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtFQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.bH.Qmafz.bt.v4.w2.w2.w2.w2.w2.w2.w2.k6.r..bO#V0afAafBafCafDafEafFafG#62#Pk.nNafH.gi.gi.gi#IH.gh.jh.gi.gi#IH.gh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh.gh#IH.gh#IH#IH.rd#4#afI.a6afJ.etafKafLafM#3H#1D#HN#0gafNafOafP.kR.sc#DaafQ#c2.w4#EzafR.TQ.9V.BM.Vv.YV#bz.5UQtNafSafT.ba.#j.#a#c5.w3#r3.aw.c8.c8.cv.cv.cv.cv.#B.#B.#B.#B.#B.#B.#B.#B.cv.cv.gV.c8.bW.MO#Us#uKafUafV.oE#Da#xE#CA#CA#CA#CA#CA#ED.OU.id#Nb#Nb#Nb.gX.5Y.qz.XiafW#xF.ci.bK.po.giafXafYafZaf0af1af2.v4Qtg.fXaf3.O1QtNaf4af5af6af7.oD.0A.TP#by.9Xaf8.fDaf9ag.#QQag#aga##t.baagb#vE#13QtZ.dg.w2agcagdageagfagg.Jiagh.0nagiagjaeQ#JNagkaglagmagnagoagp#7C#xjaeI.14.PZ.14.14.14aeJagqagqagragsagtaeRaguagvagwagxagyagzac2agAaeJaeJagBagCaeZab5ab4ab4ab5adsagDagEagFagG.qcagHagIagJagKagLagMagNagOab6afbadrab5agPab4agQagQab5ab5adradrafbab6ab6ae0a#wagMagRagSafj.aA.cyagTagUagVagWagXagYagZ#6y#6yag0ag1ag2aa4ag3ag4.Tq.Oj.K7.Jc.MM.14.14.PZ.PZ.PZaeJaeJaeJ.PZ.PZ.14.Jc.Jc.Jc.Jc.Jc.Jc",
+".w4.w4.w4.w4#bg.c8.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.coag5.OA.hhag6#Wd.OQ.li.li.Naag7Qtl#ic#c4.Jk.yb.zs.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.Jlag8.w4#c4#c5#c5#c5#c5#bg.W2#rG.zJ.Vt.Vt.sd#EG.cYQtFQtK.BIag9QtEQtEQtEQtEQtEQtE.bHQtF.uo.rh#0e#ttah..mt.QkQtK.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.kjah#QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.bH.Qmaha#7V.v4.w2.w2.w2.w2.w2.x9.r..#Q.W2#dh.D#.HH#0gahb.C9ahc.C9ahd.zq#tw#Rj.MU.ic.zO#IH.gh.pA.gh#Uj#Uj#IH.jh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh.gh#IH.a2aheaeq.W3.zt.zs#2jahcahf.D#.zE#1Dahgahh#vA.jh.ux#1U.ph#46#bi#bg#46.50#CA.YV.YV.Vv.BM.BM.bKQtNahi.Sk.#h#Xw#Ut.w4.bj#c2.gV.c8.c8.#B.#B.#B.cv.cv.cv.cv.cv.cv.aw.aw.aw.aw.gV.c8.bj#13#c4ahj.YLahk.ri#Jw#CA#CA#CA#CA#CA#CAahl#LE.pD.id#Nb#Nb.gX.rt.if.qz#ojahm.khahn#wL#yw.giahoahpahqahrahsQtTQtZ.v5#c4#qd#uHahtahuahvahwahx.OU.2n.TP.7Z.mCahy#I7ahzahAahB#MnahC.zE#V0#bk.bd#bg.aE.r.#C.ahDahEahFahGahHahIahJahKahL#HHahM#F0ahNahOahPahQ#xjahR.14.14aeJaeJahSahT.Jd.PZaeJaeJ.14.14ahUahVahWahXahYahZah0ah1ahR.PZ.14.Jc.14aeJah2ah3agQab4ab6ab6ah4ae5ah5ae6ah6ah7agRagNadsae0adrab5ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab5adrab6a#wah8adJ#I3ah9ai.ai#aiaaibaicaidaieaif#tkaig.TqaihaiiaijaeJaeJaij.14.Jdaikailaimainaioaipaioaiqair.Jd.PZaeJ.14.Jc.Jc.Jc",
+".w4.w4.w4.w4.w4#bg.cq.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#y.tA.j1#uP#fa#UL#UM.li#B1a#Taisait.vc#c4.w4#qc.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.Boaiu#JR#c5#c5#c5#c5#c4aivaiwa#4.Vt.Vt#tK.vh.f8QtKQtF.kb.cXQtEQtEQtEQtEQtEQtEQtE.bH.OF.Qc.uqaixaiy.or.7O.#6.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.Tx.i0QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.rAaiza.2.w2.w2.w2.w2.w2.w2.r.Qt0.C9#eP.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.D.ahdaiAaiBafJ.nN.pA.gh.gh#6RaiCaiD.gh.pA.pA.pA.pA.gh.ghaiDaiE.rs.pA.pA.pA.pA.pA.pA.pA.pA.gh.jh.ib.giaiFaiG.TEaiH.JI.JI#1Daf3#XsaiI.k.#4r.5Y.OA#2vaiJ.cE#J6#xKaiK.50#CA.BM.BM#fd.BM.9V.TA#IH.Hf.0N.#d.##.Fr.vc.gW.cv.fX.c8.cv.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.c8.c8.#T#uH.w4#44.OIaiL.kf.Sd#CA#xE#CA#CA#CA#CA#CA#xE#HbaiM.Ef#Nb#Nb.T5.gj.ig.if.qz#Oy.sJ.pq.ouaiNaiMaiOaiPaiQaiRaiSaiT.w4#c4#c4.Du.vba.R#m5.uEaiUaiVa.Z#Pk.pD.sY.re.R2#GUaiWaiXaiYaiZai0ai1.16#uD#vE.qZ.Tm.CHai2ai3ai4ai5ai6ai7ai8ai9aeSaeQaeQaj.aj#ajaajbajc.Tq.14ajd.14airajeajfajgajhajiadnajjailaeJ.PZ.Jc#wrajkajlajm.Tq.Tq.14.Jc.Jc.Jc.Jc.PZ.WTailajnafbagNajoajpajqae2ajragMadsagOadragPab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4afbafaajs.cA#N3.cyajtajuajv.Vd#wpajwajxajyajzajAajBadnajCajDajDajEajFajGajHajIab5ab4aeZaeZaeZab5agCajiajJaeKajKaij.PZ.Jc",
+".w4.w4.w4.w4.w4.w4#bg.bW.ahQth.#y.#A.#A.#A.#A.#A.#A.#A.#AQth.fZajL.jcajMajN#d#.li#B1#DlajO#bc.w4.w4#7G.D#.D#.YL.YL.YL.YL.YL.YL.YL.YL.YL#OB.Jlag8.w4#c4#c5#c5#c5#c5ajP#48.53.lD.sd.Vt.Xh#bxQtKQtF.lo.cXQtEQtEQtEQtEQtEQtEQtE.bH.j..rn.kgajQajR.Tz.yj.nt.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.Tx.JYQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.h3.5V.dJ.Tm.w2.w2.w2.w2.w2.r.Qtb#Uq#eP.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.zE#0g##rajS.jh#5TaiDajTajUajVajW.gh.gh.pA.pA.gh.EuajXajYajZ.pA.pA.pA.pA.pA.pA.pA.pA.pA.jh.giaj0#6P#6Vaj1.6Naj2aj3abn#VY.lC#KQ.rt.ig#yw.ja#2vaj4#ol##w.w4#Fn.JZ#xE.uB.YV.YV.TP.9XQtNaj5.al.an.#q.16.v5Qte#Fm.gV.c8.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.c8.fXQte.#oaj6#Us#uH#0gaiA##L.ns.pt#Da#xE#CA#CA#CA#CA#CA#Hb#zB.sd#5V#Nb#Nb.gX.if.if.rt#yw#yw.kmaj7.bw.kd.DP.1xaj8aj9ak.ak##st.w4.w4.w4.w4.Du#c4.w4akaakbakcakd#lv.re.rt.rtakeakfakgakhakiakjakkakl#bh#vE#c4QtbakmaknakoakpakqakraksaktakuaazaeRakvakwakxaky.14.MM.MMaeJ.JeakzajBagCaeZakAab4ab4aeZab3akBajFaijaeJ.14.14akCaeJakDakE.14.14.PZaeJaeJ.PZakFakGakHakIakJakKakLakMakNadsab6ab5ab4agPab5adrafbab6ab6a#wadrab5ab5ab5ab5ab5adrab5a#wab6adrab5agPab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab5ab6agDakOadJ.#LakPakQakRakSajgadgab3aeZaeZaeZab3akAadgadgakAaeZaeZab4ab4ab4ab4ab4ab4ab4ab4ab4aeZakAaeZakTakUakFaeJ",
+".w4.w4.w4.w4.w4.w4.w4#bc.#P.k6.#y.#y.#y.#A.#A.#A.#A.#A.#A.#AQtjQtX.Lg#NbakV.g0#d#.li#B1a#TakWQtl#c4#Us.JI.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.Fp#H#.#q.w4#c5#c5#c5#c5#c4#vEakX.Vx.Vt.Vt.Vt.sc.g8QtK.Fu.cXQtEQtEQtEQtEQtEQtEQtEQtF.nt.kj.ke#1DaiK.up.un.2cQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.rf.i0QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.uAakY.dh.G0.w2.w2.w2.w2.v4.ah#MW#eP.zE.D..YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.P5#0ga.P.kt.qJakZak0ak1ak2ak3#IH.gh.gh#Uj#6Tak4ak5ak6#3E.lf.pA.pA.pA.pA.pA.pA.pA#IH.CK#3C#49#49ak7ak8ak9al..kQal#alaalb.ig.ig#yw#1##IIalc.D..YL#7G#BV.aa#xE.lE.Vv.BM.YV.re.h5ald.#q.#o#uH.vb.#TQtf.cv.fX.c8.aw.aw.aw.aw.aw.aw.aw.aw##j.gV.gV#Ll.#P.#p#NV#uK#Us#c4#X0#1Dale.ps.nsQtG#Da#xE#CA#CA#CA#CA#CA#xE#Hb.kx.T5#5V#Nb.gX.T5.gj#yw.rt.gj.rt.rt#yw.dsalf.m.#Zhalgalh.#C.w4.vb.w4#c4.w4.Du.w4.w4.w4alialjalkallalmaln.qwad2alo#c0alpalqalralsalt.aj.16#c4.w4.#T.w0alualvalwalxalyalzalAalBalCalDajlalE.Jc.Jc.14aeJalFajGaeZaeZab4ab4ab4ab4ab4ab4ab4ab5afbalGalHalEalIalJalK#zSalLalEahSailalFalMalNalOalPalQalRalSalTalUadsafbab5adrakIab6ab5alValWalXalYalZal0al1al2al3al4al3al5al5al3al6al7al8afda#wab6afbadrab5ab5ab4ab4ab4ab4ab4ab4ab5ab6alUal2.#Mal9akIadpadgaeZab4adqab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab3am.am#",
+".w4.w4.w4.w4.w4.w4.w4.w4.w4.#TQt..as.#y.#y.#A.#A.#A.#A.#A.#A.#A.ef.nZ.0x.hhama#bp.OQ.Lyaa9ambamc#r3.w4#ts.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.zE#ie.xv#bg#c4#c5#c5#c5#c5#yzamd#UV.zJ#tK.zJ.zJ.dw.#6.Fuag9QtEQtEQtEQtEQtEQtEQtE.bH.#6.wf.muame##t.ke.tw.j.QtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.rf.kkQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.Qmamf.bt.v4.w2.w2.w2.w2.r.Qt2.D#.P5#rE.HH.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D##46amgamh.J0amiamjamkamlajW.gh.jh.gh.pAammamnamoampamq#Mr.pA.pA.pA.pA.pA.pA#IH.CKamr#49#49#6Q#6Qams#9Y#9Y#7O#5.amr.qx#1f.r9amtamu.zF.zE.YLamvamw.lE#xE.BM.YV.TPQtJQtNamx.###TA#vE.Du.vcQtf#Fm.c8.c8.gV.aw.aw.aw.aw.aw.aw.aw.gV.gV.gV.wZ#13#NV#dh##t#c4.L#amy.OI#zx.9J.mv.kf.TH.TQ#CA#CA#CA#CA#CA#CAahl#xE#gX#Fr.bM#yw#Nb.gX.gj.Wj.if.if.rt.gj#Pk.TA.P9amz#bg.bs.#C.Du.vb.w4.w4.Du.Du#c4#c4#c4#c4#c4aliajPamAamBamCamD#RjaiGamEamFamGamHamI#c4#13#c5#c4#c5#c5#c5#F2amJ.zS.HcamKamL#K7amMamN#wr.Jc.Jc.Jc.14.PZamOab5aeZab4adqab4ab4ab4ab4ab4ab4ab4a#wab5agPamPamQamRamSamTamTamUamVamWamPagNalPamXamYamZam0am1afdadsadradqadqafdam2am3am4am5am6am7.#Mam8am9am9am9adJadJam9.d..d.an.an.an#an#anaanbancandaneanfanganhanianiab2anjankanlagPadrae0a#wanmannal2am2ae0ab5ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4aeZab5",
+".w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.vcQt..as.#y.#A.#A.#A.#A.#A.#A.#A.#y.dCanoanp#faanqanr.OQ#B1a#Tansant.bj.#p.zE.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.Fp.On.#q.w4#c5#c5#c5#c5#bi#Nd.eh.kn.Vt.Vt.Xh.lE.a0.unQtKQtEQtEQtEQtEQtEQtEQtE.bHQtK.Lj.ODanu#44.ns#fe.OFQtK.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.BI.kaQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.ch.cYanv#7V.x9.w2.w2.w2.v4.au#uK.Fp#tt#bk.D#.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.HHanwanxanyanzanAanB#LOak3.lZ.jh.ghaiEanCanD#SB.hg.gh.pA.pA.pA.pA.pA.pA#IH.CK#7N#49#7O#5##7O#6Q#6QanE#49#6T#7NanF.UL.ds.mm#ib#0g.YL.Jl#46#0g.Jx#CA.BM.BM.Vv.r1.Eg.Da.zG#vE.Du.atQte.rP.cv.fX.cv.aw.aw.aw.aw.aw.aw.aw.gV.c8.#T#c5#ts.zq.Fp.YK.w4#c4##w.Jl.W2anG.ps.kg.qp.Tw#CA#CA#CA#CA#CA#CA#CA#CA#CA#xE#xE#yB##N.kx#Nb#Nb.qz.T5.if.if.ig#yw.tF.P7#c2.Du#rj.Du.w4.w4#c4#c4#c4#c4#c4#c5#c4#c4#c4#c4#c4anHanI#qd.3U#bg.w4#c5#c4#J6#c4#Us#uH#c5#c5#c5#c4#c4#c5anJQt6#c5anKanLa.9anManNanOanPanQanRahR.PZanSajFajGakTab5ab4ab4ab4ab5ab5ab5ab5ab5afbagNa#wanTanU#2A.fS.qc#QbanVab6anWanXanYanZan0an1an2am8an3an4an4an5an6an7an8an9#I2ao.#N3#I3.#K.#L.#K.aB.#I#I2.bp.bp.cy.#Kao#aoa#Lpah9aobaocaodaoeaof#N4aogaohaoiaojaokaolaomaonaooaopaoqaneaoraosal9am6aotajsaf#ae0adrab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab5afbafbafbab0",
+".w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#be.aEQth.#y.#A.#A.#A.#A.#A.#A.#AQth.aNaou#lvaovaow#Wd.e8#B1#CDaoxaoy.w3.Sk.zE.YL.D#.YL.YL.YL.YL.YL.YL.YL.YL.D#.zv.fFaoz#c4#c5#c5#c5#c4#be#ok#oN.Oz.sd.XhQtJQtK.Lj.nPQtEQtEQtEQtEQtEQtEQtEQtEQtF#zC.otaoA#UsaoB.kc.wg.oP.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.kb.2cQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.cW.g5.uA#9T#7V.w2.v4.v4.k6.ah.bT.zq#8z#bg.#l.D#.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YLahc#0aaoCaoDaoEaoFaoGaoH#V##Uj#IH.pAak3aoIaiD.pA.pA.pA.pA.pA.pA.pA.pA.gh.gi#3C#49#6Q#49#49#6Q#6Q#7O#49anEak9ala.po#ls.M3aoJ.YL.YL.YL#46#HN.TP#CA.9F#jT#LH#lbaoKQt6#vE.Du.atQteQtf.bW.c8.gV.aw.aw.aw.aw.aw.aw.aw.gV.c8#bg#xz.MP.D#.FpaoL.w4.w4ahj.JlaoM#BV.kf.mw#gM.kc.9F#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#28#xE.bI.mC.mC.kp.mE.Oz.j2.saaoN#qc#zw#c4#c4.Du#c4#c4#c4.w4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#Lm#c5#uH#uH#13#Ut#5K#ET#c5#c5#c4#c4.va.og.v5aoOaoPaoQaoRaoSaoTaoUaoVaoWaoXaoYaoZao0ao1.14.14.14.Jdajnab4ab4ab4ab5ab5a#wa#wafcao2ao3ao4ao5.gRadu.mf.q6.mf.gRao6ao7ao8ao9ap.ap#apaapbapcapdape#Lqapfapgaphapiah9apj.#H#Mwapk.hNapl#KXapmapn.#H.c9#OUapoappapqaprapsaptapuaiu#c4.w4apvapwapxapyapzapzapzapAadPapBapCapDapEapFapGapHapIapJapK.#Kam9apLapMagRab6afbab5ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab4ab5afbalUagSalYapNapO",
+".w4.w4.w4.w4.w4.w4#bg#bg#bg#bg#bg#bh#r3.#A.#y.#y.#A.#A.#A.#A.#A.#A.#yQtj.#x.Jt.reapPapQ#bp.OQ#B1apRapS.#C#bg#2R.JI.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.D#apT.#q.w4#c5#c5#c5#c4#bj#M1.Vv#fb.Vt.Vt.sWQtE.36QtKQtEQtEQtEQtEQtEQtEQtEQtE.bH.Qk.riapU.v5apV.ri.Qk.koQtFQtEQtEQtEQtEQtEQtEQtEQtEQtE.JV.2cQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.bL.BM.a3.h3apW#7V.dg.dg.k6Qt.QtV#tt#X0#rj.w4#3V.zq.D#.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.JIapXapYapZ.meap0#VFap1ap2.gh.gh.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.pA.gh#IH.gi.Yr#7N#49#49#49#6Q#49#49ap3ap4#YH#Veap5#46.YL.YL.YL.YL#X0ap6.9V#CA#xE#yB.kS.mB.Dt.dT.w4.atQtpQtfQtf.cv.fX.cv.aw.aw.aw##j##j.gV.gV#FD.Dt#46.YL.D#.Fp#uK.w4.w4ap7.Fp#dh.#n#2f.ps.kf.lr.kj#Hb#xE#CA#CA#CA#CA#CA#xE#xE#CA#CA#CA#xE.lx#Hb#xE.33.aY#II#Hb.9X.if#IHap8aiv#bg.w4#c4#c4.w4.w4.Du#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5#c5#c5#c4#c5#uH#c5#c4.L#.Cl.w1ap9aq.aq#aqaaqbaqcaqdaqeaqfaqgaqhaqiaqjaqkaqlaqmaqnaqoaqpaeJamOab5adqab4ab5afbaqqab5ao2ao3aqraqsan0ae3.mf.qc.mf.iO.fT.pe#PvaqtaquaqvaqwaqxaqyaqzaqAaqBaqCaqDaqEaqFaqGaptaqHaqI#RPaqJaqK#NZaqLaqMaqNaqOaqP#J6aliaqQaqQ#NXapw#OMakaaqR#2b#9n#5H#5K#6ya#MaqSaqSaqSa#Ma#MaqTafq#8paqUadPaqVapy#UPaqWaqXaqY.hNaqZ#I3aq0aq1aq2affadqab6ab6adragPab4ab4ab4ab4ab4ab4ab4ab4ab4aq3ab5ab6afaalYafk.aAam8.#Lam9",
+"#bg#bg#bg#bg#bg#bg#bg#bg#bg#bg#bg#bg.w4#a4.#y.#y.#A.#A.#A.#A.#A.#A.#A.#AQtj.ee.5S.TTaa7aq4anr.OQ.li#sEaq5Qte#bg#vE#7S.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.Jl#qc.w4#c4#c5#c5#c5.w4#3V.ur.TN.lD.Vt.sX.h6.36QtFQtEQtEQtEQtEQtEQtEQtEQtEQtE.j..Liacw.w3aq6.rh.ms.g8QtKQtEQtEQtEQtEQtEQtEQtEQtE.bH.un.2cQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtK.cW.9V#XY.cXaiz.aNQt..c8.#P.w1#bi#YM.#m.w4#c4.Du#X0.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.zqaq7aq8aq9ar.ar#araarb.gh#27.rs#IH.gh.gh.pA.pA.pA.pA.pA.gh.gh.gh.gh#IH.gi.CK.Yrarc#3E#3E#7Nard.r5areafD.#p#bg.zE.YL.YL.YL.YL.Bn#Yj#1U#xE#Hbarfarg.qS#r3.va.vcQtpQtfQtf.cv.c8.gV.aw.gV.gV.gV.gV.gV.c8#bj#V0.P5.YL.D#.Fp#46.Jk.w4.#p.P5.Jl#8C#HN.kf.kf.mw.vk.tr#xE#CA#CA#CA#CA#CA#CA#xE#xE#CA#CA#CA#xE#xE#xE.9F.tr.kf.or.pN.sW.ie#ZXarh.wZ.w4#c4.w4.w4.w4.Du.w4.w4#c4#c5#c5#c4#c4#c4#c4#c4.Du#c4.Du.w4#c4#c4#c4#c4#c4.L#.cE.ezariarjarkarlarmarnaroarparqarrarsartartaruarvarwarxaryarzarAarBarCalFajIab4ab4ab5ab6ao2agOarDam2arE.e.ae3.fS.ht.fT.pearFarG.zlarHarIarJ.w4arK#PyarLarM#NXarNaqQ#Py#Pyali#NY#NY#J6anH#J6#F2#F2#F2#NYalialiapv#J6#F2#F2.w4#c4#c4#c4#Xx#uH#ETarOa#N#6yarPaqSaqSaqSaqSaqSaqSa#Ma#Ma#Ma#Ma#Ma#Ma#Ma#Ma#M#8parQarRarSarTarUarVarWarXarYarZar0ar1alYalWafcab6afbab5ab5ab4ab4ab5ab5afbab6agRar2ar3.aAam8.#L.#L.#L.#L",
+"#bg#bg#bg#bg#bg#bg#bg.w4.w4.w4.w4.w4.w4.w4QtZ.#y.#y.#A.#A.#A.#A.#A.#A.#A.#yQtj.cF#Z0.OK#XPar4#Wd.OQ#EQ#B1ar5Qte.w4.Du.YK.zq.YL.YL.YL.YL.YL.YL.YL.YL.D#.EX.#q.w4#c4#c5#c5#c4#c2ar6#gE.7J.vh.sd.RR.sMQtFQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK.Txar7#c4ar8.ot.qqQtG.cS.bHQtEQtEQtEQtEQtEQtEQtEQtE.Lt.2cQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.cT#jT.bG.2car9.cv.vc#bg.w4.w4.v5.b##Ut#c4#c5.w4#m5.D#.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.D#.D#.D#.Jl.zE#1D.C9#j3as.as#asaasbascasdase.ja.ds.hi#IH#IH#IH#IH.gh.gh.gh.gh.gh.gh.gh.gh#IH#IH#IH.jh.gi.gi.zO.ml#6Wasf#46.cv#be#c4.7F.YL.YL.YL.YL.HHasg#Wl#gE#CAash.zwasi.cvQteQtpQteQtf.aL.c8.gV.aw.gV.cv.#P.vc.vb.w3.vb#ts.D#.YL.YL.YL.zq.#p.w4#c4#X0.D#.D##7Gasjask.kf.mw.ns.ty#gE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#2v.2c.qo.rm.i4.mx.rt.rt.0Fald#yz.w4#c4.w4#c4.Du.Du#c4.w4.Du.Du#c4#c4.w4.w4.w4.w4#c4#c4.w4.w4.w4#c4.L#.cE.m#aslasmasnasoaspasqasrassastasuasvaswasxasyaszasAasBasCasDasEaijasFasGasHasIasJaeZagPab6asKasLasMalYalTagHasNasO.pe.q3asP.zlasQasRasSasTasUasU#Oq#F2#c4#c5#c5#c4#c4#c4#c4#c4#c4#c4#c4#c5#c5#c4#c5#c5#c4#c4#c4#c5#c5.L#.w1QtZQtV#OW#OWQt..v4.au.k6.dhQt..bOQtV.bW#be#F2#2ba#Na#MasVa#Ma#Ma#Ma#Ma#Ma#Ma#Ma#Ma#M#6y#5I#6y#5KarQarSasWasW#5HasXasYasZas0as1as2as3as4as5alVab5ab6ae0adraf#af.as6annadJadJ.#L.bo.#L.#L.#L.#L",
+".w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bc#m8.#y.#y.#A.#A.#z.#A.#z.av.#z.#AQtj.aNas7.Sias8as9at.#d#aa9#B1at#.#C.w4.w4#3Vata.YL.YL.YL.YL.YL.YL.YL.D#.YL#qc.w4#c4#c5#c5#c5ajP.P5.tH#LE.5U.po#CB.TYQtFQtEQtEQtEQtEQtEQtEQtEQtEQtF.rA.wfatb#m5atc.rk.sO#vF.klQtFQtEQtEQtEQtEQtEQtEQtE.k#.Lt.2cQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.ko.h2atd.TH.OL#YM#be.w4.w4.w4.w4#biap7#uH.Du#c5#c4#c4#uK.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.Fp#dh.OI#0g#ib.Or.JJafJ.km#De.h5.jc.sR.sR#lv.ci.sc.ck.9X.sd.jh.rs#IH.jh#IH.gh.gh.gh.gh.jh.lZ.jh.gh.pA.nN.nNateatf#uKatg#bj.w4#bg#bg#46.YL.D#.zE.MPahdath#Ms.ck#j2#q.atiatj#Xo.qS.cu#sP.aM#P#.c8.gV.gV.t1.vc#uH#uH#F2.bdata.YL.YL.YL.YL.zE#MW.w4.w4#8z.Fp.D#.HH#Ez.9J.mw.mw.mw.ns.rn.9V#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#xE.TQ#xEatk.Qk#Jw.TQ#1U.RM.rt.ig#0xatl#bj#c4.Du.Du.Du.w4.w4.w4.Du.Du.w4.Du.w4.Du#c4.Du.Du#c4#c4.Du.L#.cEQt5atmatnatoatpatqatraeS#KjatsattatuahWatvatwahJatx#xhatyakyajkajkatz.14.14asH.14atAajIaeZab5ae0atBatCam8atDatEatF.fSatGatHatIatJatKaqPatLatMali.w4#c4#c4#c4#c4#c4#c5#c5#c4#c4#c4#c4.Jk#c5#c4#c4#c4#c4#c4#c5#c4#c4#c5#c5#c4.#F.bO.au.dg.au.v4.v4.v4.v4.v4.v4.v4.v4.v4.k6.au.dg.r..dh.bO.gV#be#ic#9na#MaqSaqSatNa#M#8pafqa#M#6y#6y#6y#6ya#M#8p#6y#8patOasWatPatQatRatS#Niaq0.czatTan4atUapOal2ann.#MadJ.#L.#L.#L.#L.#L.#L.#L.#L.#L",
+".w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bg.bW.#y.#y.#y.#A.#y.#A.#A.#A.#A.#A.buatV.DnatW#NbatXatY.nmaa9.JTatZ#bh.w4#JR#c4.YK.P5.YL.YL.YL.YL.YL.YL.YL.zE.#l.w4#c4#c5#c5#c4#bgaab#gE##N.ji.qy.TYQtFQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.Ljat0#Ez#F3.kd.rn.9W.oE.cXQtEQtEQtEQtEQtEQtEQtE.cl.sM.sQQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.7X.BM.YU.bG.7O#2R#bi.w4.w4.w4.w4#bg#3V#c4#JR#c5#c5#c4#c4aoL.MP.YL.YL.YL.YL.YL.YL.YL.YL.YL.zq#eQ.OIat1at2.BK.lf.dx.lf.rd.sR.jc.sR.sR.jc.sa.sR.kR.k..mCQtJ#LHat3.jh.rs.gh.gh.gh.gh.gh.gh.hf#9y#Xl.RMat4.W4#ts#FD#c3#bg#bg#bg#bg#c2#8z.C9.zr#c6at5#9Zat6.ig.if.if.pB.TA#Rk#Pkat7#Tzat8at9au..bW.#P#bg.Fr#uH#c5.w4.#l.D..YL.YL.YL.YL.OI#7G.w4#c4#c5#1D.Fp.Fp.Viau#.ps.kf.mw.mw.kg.rk.0A#CA#CA#CA#CA#CA#CA#CA#CA.TQ#Da#HS.Tw#DN#Ld#28.TQ#Hb.9I.qx.rt#ioaua.Jk.v5.vb.w4.w4.w4.vb.vb.Du.Du.w4.Du.w4.Du.Du#c4.Du.Du.Du.L#Qt5#c4aubaucaud#wB#L7aguaueaufaugatvauhauiajlajk#7B#wr.Tq.Tq.14.PZaeJaeJaij.TqaikagBaioaujab3adfadfaukaulaumaunauoaupauqaurausaut.w4auuauuali#F2#c4#c4#c4#c4#c4#c4#c4#c4#c5#c5#c4#c4#c4#c4#c5#c4#c4#c4#c4#c4#c4#c5#c5#c4#c4#c4.w4.#Q.dg.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.v4.v4.v4.au.r..au.bO.aw#xK#Sf#6ya#M#6y#6y#6ya#Ma#Ma#M#6y#8p#6y#6y#6y#5K#5K#6yaqUafp#GTauvauw#I3#I2adJam9am9adJ.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L",
+".w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4QtZ.#y.ah.#A.r..r..au.au.au.k6.v4.v4.w7.dR#6X.ktauxauy#bp#EQ#DlauzQtl#bc#Lm.w4#uD#7S.D#.YL.YL.YL.YL.YL.YL.MP#vE#c4#c5#c5#c5#bj#C9#gE.YV#sM.ro.DyQtKQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.yk.rk#eRauA.kg.0y.pt.cWQtKQtEQtEQtEQtEQtEQtEQtE.cl.i7.kaQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.cX.sf.YV.YU#Iy#zC#tt#bi.w4.w4.w4.w4#be#bi.Jk#c4#c5#c5#c5#c4.w4#uH#tsahb.D..D..D..JI.MP#1D.HHauB#Xs.Xj.kx#De.gh.sR.jc.sR.sR.sR.sR.sR.sR.ku.if.rt.ku.sR.rd.k..dsQtJauC.ci#tJ.tM.nJ.nJ#0f#XlauDauE.Lb.zD#be#NoauF#c2.16#MW#MW#Us.#nauG#wH#7R.0uauH.rt#3A.qz.rt.rt.if.rt#KQ.rs.r1#rCauIauJ#xBauK.Jk#c5#Ut#uH#c4#c4.Vi.Fp.YL.YL.YL.YL#X0#Ut.w4#I8.w4.Sk.Fp.Fp.JIauL.mw.kf.kf.mw.mw.kf.kg.wn#QC#xE#CA#CA#CA#xE#CA#xE.3YQtG.QbauM#lf.2n#CA#xE#CAauN#8M.ckauO.#m.vc.w4.vb.w4.Du.Du.vb.Du.Du.Du.vb.Du.Du.Du.w4#JR.w4.ez.ClauPauQauR#FX#LC#L8abGauSauTauUauVajl#7B.Tq.14.PZ.PZaeJaeJ.14.JeairalFaeKaeMajyauWagNauXab5auYauZau0au1au2.#Oau3appau4au5#J6au6atL#J6#c4.Jk#c4#c4#c5#c4#c4#c4#c4#c5#c4#c4#c5#c5#c4#c4#c4#c5#c4#c4#c4#c4#c5#c4#c5#c5#c4#c4#c4#c4#c4.aF.ah.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.w2.w2.w2.v4.v4.v4.k6.w2.t1ajP#ETa#Na#Ma#Ma#Ma#M#8pa#MatN#6ya#N#5H#9n#5G#SfarO#3V.DH.w2au7au8au9#eH#eH.#I.#K.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L.#L",
+".w4.w4.w4.w4.w4#bg#bg.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.vb.bW.#A.r..v4.v4.v4.v4.v4.w2.w2.w2.w2a.2av..DoaiIav#ava#Wd.e8.Lyavb#MXQtpavc#c4#c4.#k#1D.YL.YL.YL.YL.YL.YL#5R.w4#c4#c5#c5#bg#YM.OB#jT.7T.kn.koQtK.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE##F.lpavd.bdave.rgavf.e6QtF.bHQtEQtEQtEQtEQtEQtE.h3.ms#2TQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.bH.3Y.Vv#Wk.a3#im#tt#bi.w4.w4.w4#bi#44#4y.vb#c4#c5#c5#c5#c5#c5#c4#F2#c5#uH#c4#be.Jk#46avgamx#1Z.kR.TA.TA.sR.sR.sR.lC.jc.jc.ku#Nb.jc.sR.jc.ku.rt.T5.rt.ku.jc.dy.T5.OA##N#j4avhaviavj#HMavk#YMavl.qgavm.3U.ztavnavo.Hpavpavpavqavr.Si.rd#3A#ZX.sa.rt.ie.if.hi.k..gh#Pkavrabnavs.zo#bg#c5#uD#uD#13#bk.#q.Jk#c5#NV.YL.YL.YL.YL.D.#xz#c4#c4#c5#c4#c5#1D.Fp#dh.zravt#ik.kf.mw.mw.mw.mw.MZ#e5#CA#CA#CA#CA#CA#Da.Tw.HK##Lavuavv.zqaaa#wK.TQ#Wl#GDavwavx#3I.vb#bi.Du.vb.vb.v#.Du.v#.Du.vb.Du.w4.w4.w4.v#.Du#c4.5P.ClavyavzavA#In#PJ#F0ahMa#kavBavCaty#7B.Tq.14aeJaeJ.TqanSalFavDavEavFao3avGagQauYavHavIavJavKavLavMavNavOavP#KzavQarMaqQavR#NX#xK#F2#c4#c4#c4#c4#c5#c4#c4#c4#c4#c5#c5#c5#c5#c5#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5#c5#c4#c4#c4#c4#c4#c4#c4#uH#bj.ah.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.au.w2.c8ajP#2b#6y#6zavS#5G#Ut#uH.Jk.Du.w4.vb.w4.vb.vb.Du.dIavT.sxavUavVavW#IbavX.aA#eH.bp.#I.#L.#M.#L.#L.#L.#L.#L",
+"#bg#bg#bg#bg#bg#bg#bg.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#id.aE.r..GZ.w2.w2.w2.w2.w2.w2.w2.w2.v4avY.5QavZav0av1.li.e8.OPav2av3Qtl.w4#ic.w4#c5#xz.JI.YL.YL.YL.YL.7F#Us.w4#c5#c5#c4#be#9N#LF.BMQtJ.tK.g8.a4QtEQtEQtEQtEQtEQtEQtEQtE.ko.2c.weav4#bgav5.rg.JW.e6.g8QtFQtEQtEQtEQtEQtEQtE.h3.yk.kkQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK#mG#bt.Vv.YUav6#e5#3V#be.w4.w4.w4.fYav7av8#id#c4#c5#c5#c5#c5#c5#c4ajP#yz#bd#YMav9aecaw..ku.h5.km.ci.k..sR.jc.jc#Nb.rt.rt.ku.sa.jc.jc.jc.jc.sR#Nb.gX.rt.T5.rt.ku.ku.qz.hh.h.aw#awaawbawcavm#FD.fY#tsawd.Hoa#2.gh.rs.kR.lC#4r.kR.hg.k..if.if.if.if.if#Pk#Pk.qw#1Z.Hmat1#7Ga.R#Ow#be#c4#65#8z#XW#5R#m5.w4#Ut#0g.YL.YL.D#.YL.D..#p#bg#c4#c5#c5.w4.YK.YL.D#.D.awe.kh.kf.mw.mw.mw.mw.mw.kf.kg#Uk#Cx#Da#Da#wKawf.mtawgaix#vE.C9.zE.YLawh#Hb#ia##y.Du.wZ#bj.vb.Du.w4.Du.vb.w4.w4.Du.w4.Du.Du.Du.w4.Du.Du.at.wAawiawjawkawl#In#HH#KHaeQawmawnawo#7B.TqauVaeJ.JeawpawqawrauXaniab3awsavIawtawuawvawwawxawyawzawAawAawzapwanH#F2#F2#c4#c4#c4#c4#c4#c4#c5#c4#c4#c4#c4#c5#c4#c4#c4#c4#c5#c5#c5#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5#c4#c4#c4#c4#c4#c4.Jk#3V#9nQtV.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4Qt..t1.#F.v5.vb.w4.vb.vb.vb.vb.w4.vb.Du.Du.Du.Du.Fr.dIawB.MK#NeawCawDawEawFawGawH.#K#eH#I3.#Lau9.#L.#L",
+"#bg#bg#bg#bg.vb.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.#TQt..dg.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.TmavYawIawJawK.OP#B1awLawMawNarO.w1#c5#c4.w4.16.W2.OI.YL.D#.Fp.Tt.w4#c4#c5#c5#beawO.34.Vv.Vv.es.lw.cXQtEQtEQtEQtEQtEQtEQtEQtE.fdQtK.rfawP.bjawQ.OD.rf.oE.rvQtF.fdQtEQtEQtEQtEQtE.h3.i4.j.QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.ab.lG.qE.Vv.Vv.YU.a3#vCa#V#NW#bg.w4#be.YKad4#vKawR.wZ#bg.w4.w4#bg#bj#bi.W3awSawTat7#lv#Xl.pA.sR.jc.jc.dy.jc#Nb.ku.gX.ku.ku.ku.ku#Nb.ku.gX.gX.gX.k.#yw.5Y.T5.T5.T5.rt.rt.rt#Nb#Nb.0v#1U#rG#vDawUawV#VZafJ.kR#ZX.rt.if.ig.ig#Pk.ig.ig.if#Pk.ig.igawW#Pk.ig.if.hgawX.zr#c2#bj.w4#c4#tt#uKata.D#.D..YK.Jk#bg#bk.JI.YL.Jl.YL.YL.P5#c1.w4#c4#c5#c5#c4#c5.yb.YLawY.zpauM.kh.kf.mw.mw.mw.mw.mw.kf.kf.kf.Dx.wg.wg.rfawZaq7#bi#c4.MP.YL.ya#3V#4f.17#rj.#F.w4.vb.Du.Du.w4.w4.Du.Du.Du.Du.w4.v5.v5#c4#c4#c4.cE.wAaw0aw1aw2aw3#7m#wB#F0#HHabGaw4aw5ajl.VdaeJ.14aimaw6aw7aw8aniaw9ax.ax#axaaxbaxcawAaxdawzaxe#F2#F2#c4#c5#c4#c4#c5#c5#c4#c5#c4#c4#c5#c4#c5#c4#c4#c4#c4#c4#c5#c4#c4#c4#c4#c5#c4#c4#c5#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5.w4#c4#c4#c4#c4#c5avSa#M#Sf.w2.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.k6Qt..c8.#T.vb.Du.Du#c4#c4#c4#c4#c4#c5#c5#c5#c5#c5#c4#NW.fX.ETaxfaxg#Hzaxhaxi.gNaxj#I3.bZ.#K.#M",
+".w4.w4.w4.w4#bh.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#biQtV.dg.v4.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2axk#7Vaxlaxmaxn#yJaa9#CDaxoaxpQte#c4#c5#c4.w4#uHahj.MP#7Waxq.w5.w4#c4#c5#c4#3Vaxr#gE#is.TP.eqQtKQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.Ltar7#c4aiy.YP.rn.pN.a3QtF.bHQtEQtEQtEQtEQtE.h3.JX.j.QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.lw.YV.Vv.Vv.YU.eh.7O.zn.va#bg#bg#FD.7G.kS#gNaxsaxt.Qf.Vi#XWaxuaxvajS.Oz#De.nM.sg.sR.sR.sR.sR.jc#Nb#Nb#Nb.jc.jc.ku.gX.gX#1e#1e.gX.rt#yw.if#Pk.if.if.if.ig.5Y.gX.T5.rt.gX#Nb.rt.qx.kx.TNaxw.TH#3a.rs.sa.if.ig.if.if.if.if#Pk.if.if.if#Pk.ig#Pk#Pk.ig.if#KQ.9E#c4#bj#c5#c5#F2#c5#0g#eP.YL#W9#vE.w4.w4#MW.zq.YL.Jl.YL.YL.zE.#l#J6#c4#c5#c5#c4.w4.Tt.YL.YL.bRaxx.kh.kf.mw.mw.mw.mw.mw.mw.mw.mw.kf.ns.lt.kfar7axy#be.w4#ol.YL.Fp#c1.w4#bg.#T.v5.vb.vb.w4.vb.Du.Du.Du#c4#c4#c4.vc.bO.v4.v4.gV.w4.wAaxzaxAaxB#SG#HH#L2#F0#F0#HHabGaxCaxDajc.PZairaxEaxFaxGab3axHaxIaxJaxK#F2awAawzanH#c4#rjavc#c5#c4#c4#c4#c5#c4#c5#c4#c5#c5#c4#c4#c4#c4#c4#c4#c4#c4#c5#c5#c4#c4#c5#c5#c4#c4#c4#c4#c4#c4#c5#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5#Uta#Na#MaqS#2b.tm.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.sq.#P.w4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.v5#OW.v4.v4#HzaxLaxfaxMaxNaxOaxP.bp",
+".w4.w4.w4.w4#bh.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bg.cv.#A.r..x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.uu.r.axQaxRawL.OPapRaxSaxT.bj#bc#c5#c5#c4.w4#uH.ya.JH.zn.Du#c4#c5#c5#idaxU#LF.YV.BM.a3QtF.bHQtEQtEQtEQtEQtEQtEQtEQtE.cl.i8.lqaxVajQ.X#.rh#VB.LB.ch.nqQtEQtEQtEQtEQtE.h3.ln.j.QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtK.sf.Vv.Vv.Vv.YU.a3.7OaxWaxX.Dt#bg#idaxY.dx.sc#j2#QT.3V#M3.DK.vs.jc#Xl.TA.sR.sR.sR.sR.sR.sR.sR.jc.jc.jc.jc.gX.gX.gX.gX.gX.k..T5.if.if.if.rt.rt.rt.rt.rt.if.ig.T5.rt.T5.rt.T5.if.qz.pD.uz.ph#P1.qx.if.ig.if.if.if.if.if.if.if.if.if.if.if.if.ig#Pk.if.pE.nRawR#bd#c5#c4#c4#JR.W2.zt.C9.#k.L#.w4#c4axy.D#.YL.YL.YLaxZ.YL#uK#bh#c4#c4#c4#c5#c4#c5.C9.YL#W9.v5ax0.qo.kf.mw.mw.mw.mw.mw.mw.mw.mw.kf#ik.psax1#Ez#bi#c4.Jk.C9.YL.W2.w4#c4.w4.Du.w4.vb.w4.Du.vb.w4#c4#c4.L#.#T.#Q.au.au.GZ.v4.ha#bgax2ax3ax4ax5aam#Fi#F0#F0#HHa#kax6ax7ax8ax9ay.akTay#ayaaybaycayd#Kzayeayf#bj.#FQte.#P.#P#c2Qtf.gW.#PQte#r3#r3Qtp.#T#be.vc.v5.vb.w4#c4#c4#c4#c5#c5#c5#c5#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5#c4#c5#c5#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4.w4#c4arO#8pa#Ma#Ma#MavS.bO.G1.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4#OW.aw.v5.Jk#c5#c5#c4#c4#c4#c4#c4#c4#c4#c4#m8.G1.v4.w2.w2.w2.v4#HzaxL.r.aygayh",
+".w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.#P.dh.dg.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.auacRayiayjaykaylaym#CDaynaoyQte.Du#c5#c5#c4#bh#13#2R.Jk#c4#c5#c5#be.Fo.jk#jT.TP.3Y.bH.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.i1.riayo.16ayp.rk.Dy.Ou.cWQtFQtEQtEQtEQtEQtE.h3.oq.kk.fdQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.oP.bG.Vv.Vv.Vvayqav6#m3amv.W0##l.dT#7G#8O.gh.TA.TA.sR.kS.HJ.sg.lf.TA.sR#Nb.ku#Nb.sR#Nb.ku.jc.jc.jc.jc.ku.gX.gX.gX.rt.if.T5.rt.rt.ig.jm#P1#3aayr.RM.jm.ig.5Y.if.T5.T5.rt.T5#1f.qz.0x.qi#ZA#yw.rt.ig.ig.if.if.if.if.if.if.if.if.if.if.if#Pk#Pk#Pk.ifays#Nb#53#bj#c4#c4#c4#ic#tt.MP#qc#3V#c4.w4#c5.Qf.YL.YL.YL.YL.YL.YL.D.#m5.w4.w4#uH#Us#c4.w4#xz.YL#1D.16.w4av4ask.kf.mw.mw.mw.mw.mw.mw.kf.mw.qoayt#F5#c1#bg#c5.w4#MW.YL.JI.#k.w4.w4.vb#c4.Du.Du.Du.w4.Du#c4#bg#a4.bO.au.r..auQtS.Oe.eeayuayvaywayxayyayz#wB#F0#F0#JNayAayBayCayDayEayFayGayHayI.#NayJayKayL.o7.c8.c8.c8.aI.c8.aI.aI.aI.c8.c8.aI.aI.aI.c8.aI.c8.aI.c8.#B.#B.aL.bWQtfQte.#T#bi#bg.w4.Du#c5#c5#c4#c4#c5#c4#c4#c5#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4.Du#c4#c4#c4#c4.Du#c5#5Ga#Ma#Ma#Ma#Ma#Ma#M.#P.dg.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.au.w2.t1.w3#c4#c4#c4#c4#c4#c4#c4#c4.L#.bO#XD.w2.w2.w2.w2.w2.w2.w2.v4#GR#Ne",
+"#bi.w4.w4.w4#bh.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.vc.cu.dg.v4.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.dgQtrQtRayMayNayOayPayQayR#uH.b5#c4#c5#c5#c4#c4#c4#c5#c5#c5.w4.Jk#4i#Cx.oD.TP.g8QtFQtEQtEQtEQtEQtEQtEQtEQtE.bH.nt.mtayS#beayT.OD.JW.dw.a3.kw.fdQtEQtEQtEQtE.h3##F.OF.koQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtFQtF.YT.34.Vv.Vv.YU#Iy#m3amv.W0.W1.W0.ao.Sj.a2.jc.sR.sR.sR.jc.jc.jc.sR.sR.jc.jc.jc.sR.jc.ku#Nb.ku.jc#Nb.gX.gX.gXays.if.RM.ru#wLQtM.h..eo.dt.sW.pD.yl.Oz.ru.kp.sd.rt.rt.T5#yw#yw.0x.f8.vs.qz.if.ig.if.if.if.if.if.if.if.if.if#Pk.if.if#Pk.ig.if.ifayU.idaiB#r3.w4#c5#c4#c5#3V#qc.Qf#MW#V0ayV.#p#Uq.YL.YL.YL.YL.YL.YL.YL#ts#bg.w4#3V.Vi#c5.w4#uH.JI.YL.Bn#c5#bgayWask.kf.mw.mw.mw.mw.mw.kf.kh.mwayX#ol.w1.w4#c5#c4#c5.MP.YL.zE.#l.vb#c4.vb.w4.w4.vb.Du.Du.vc.cv.dh.dg.#yQtRQtRayYayZay0ay1ay2ay3ay4ay5asu#wB#F0#HHahMahMay6ay7ay8ay9az.az#azaah9azbazcazd.ed.fX.c8.aI.aF.c8#Kt.#B.#B.#B.#B.#B.KE.#B.KE.#B.c8.c8.c8.c8.c8.c8.c8.aI.c8.c8.c8.c8.gV.gV.cv.bW.w0.#T.dI.w4#c4#c5#c5#c5#c4#Lm#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4.w4#c4#ETa#Na#Ma#Ma#Ma#MaqSa#Ma#M#Sfaze.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.v4.r..w2.cv.v5#c5#c5#c5#c5#c5#c5#c4QtV.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.v4",
+".cv#bi.w4#bh#bh.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bg.aw.v4.au.v4.v4.v4.au.k6.au.#A.dg.ah.dg.dg.#A.#y.hbazfazgazhazi.pkazjazk#Ut.bj.w4#c5#c5#c5#c5#c5#c5#c4#idazl#Cx.oD.YV.rvQtFQtEQtEQtEQtEQtEQtEQtEQtEQtE#qs.Vm#1A.L#azm.YP.ty.um.tH.bH.bHQtEQtEQtEQtE.h3.lv.uoQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtE.7Z.Vv.Vv.Vv.9V.bG.5V#8C.W0.W1aznazo#29.kS.jc#yw#KR#Pk#dk.T5.sR.sR.sR.sR.sR.sR.sR.sR.sR.sR.rd.rd.sR.ku.Si.r#.lE.#8.3Y.tG.tI.g.#J7.k.#Nb#Nb#5V#Nb#Nb#Nb#Pk#yw.gX#Nb.rt.sW#IF#jN.j2ays.ig.ig.if.if.if.if.if.if.if.if.if.if.if.ig.ig#Pk.if.rt.rt#4r#HM#bd.w4#c4#c4#c5#uD.#i.zF#1D#1D#1D.Bn.7F.YL.D#.D#.YL.YL.YL.YL.zE#c1.bd.Qf#NV.#k.w4#c4#2X.YL.D#.yb#uH#bga.A.qo.kg.mw.mw.mw.mw#ik.psazpazq#bgaoz#c5#c5#c4.w4#ts.YL.YL.Qf.w4.Du.Du.Du.Du.Du#bc.#FQtV.#AQtU.xLazrazsaztazuazvazw#5razxazyazzazAa.9#wB#F0#L8#HHazBazCazD#AKazEazFazGazHazI.Ea#I7azJazK.gV.aIaze.uPazL.aI#XDaze.#B.#B.#B.KE.KE.KE.#B.#B.#B.cv.#B.cv.cv.cv.#B.cv.cv.cv.aw.cv##j.gV.gV.c8.c8.c8.gV#m8.#E.#F.vc#bg.w4#c4#c5#c4#c4#c4#c4.w4.Du#c4#c4#c4#c5azM#5G#8pa#Ma#Ma#Ma#MaqSaqSa#Ma#MasVazN.w2.G1.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4Qt..aw.vb#c5#c5#c5#c5#c5#be.tm.r..v4.w2.w2.w2.w2.w2.w2.w2.w2",
+".gV.c8#m8#be.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.bj.bO.#y.#y.dg.#y.#y.#y.#y.#y.av.#z.#z.#A.#A.#A.#A.vJ.tBQtjazOazPazQazRazS#8zQtp#bg#c4#c4#c5#c5#c5#bi#OD.tHazT.YU.57QtK.bHQtEQtEQtEQtEQtEQtEQtEQtE.bH.36.lq##w#lg.mw.tt.r2.qE.lFQtFQtEQtEQtEQtE.h3.Dy.i1.h3QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.ch#Jw.Vv.Vv.Vv.9VazU.Qk.gJ.W0.W1.W1axXazV.W7.r1#Pk#J7.W9.T5.jc.ku.k.#uO.jc.sR.rd.rd.sR.cZ.j2.0v#wL#ZA#Iy.ch.mr.aZ.mU.RM.gX#Nb#5V#Nb.ku#Nb.ku#Nb#Nb.gX#Nb.dy.gX.Oz.a5.LB.h2aiM.rt.if.ig.if.if.if.if.if.if.if.if.if.if.if#Pk#Pk#Pk.if.if.rt.if.lCatf#qd.w4#c5#c4.w4#uH#V0#NV.D#.YL.YL.YL.YL.YL.YL.D#.D#.YL.YL.YL.YL#qc#ic#1D.C9#ts#46#3V#NV.YL.YL.YL.D.#uD#bgaiy.qo.mw.ps.ps.mwatbaypav4#xz#be#c4#c5#c5#c4.w4#7G.zr.YL.C9#c5.w4.Du.Du.L#.vcQtW.o1.xL.#yazWazXazYazZaz0az1az2#5raz3azyaz4az5#L2#L2#Io#Ioaz6az7az8az9aA..vb#NXaqQ#Py#Ke##j.df#I7.gV.aw.gV.c8azeaA#aAaaAbaAcaAdaAe#LA.qZ.cv.cv.#B.#B.cv.cv.cv.cv.aw.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.#B.cv.#B.#B.#B.cv.gV.gV.gV.gV.cv.bW.#F.vc.vb#c4#c4#c4#c4#c4#c4#uH#3V#5H#6ya#Ma#Ma#Ma#Ma#MaqSaqSaqSaqSaqSaqSaqS#ic.ed.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.#OW.w0#c4#c5#c5#c5#c5.dI.t1.w2.v4.v4.v4.v4.w2.w2.w2.w2",
+".aw.gVQta.dh.c8#bi#bg.w4.w4.w4.w4.w4.w4.w4#bg#be.#F#bj.vcaoz.w4.w4.w4.w4.w4.w4.w4.w4.w4#bg.cv.#AQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.Of.tB#tmaAfaAgaAhaAiaAj.aq.Fr#c4#c5#c5#c4.w4aAk#wK.9F.qE.cS.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.5V.0yaAl.FraAm.zK#e8.f8.epQtFQtEQtEQtEQtE.h3.ln##F.h3QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.bHQtF.bHQtEQtEQtEQtEQtEQtEQtE.bH.cg.BM.Vv.Vv.Vv.YU.rB.oq.fF.W0.W1.W1aznaAn#47.qy.tI#P3.k..rd.sR.k..g..g..sc.tI.dv.sY.r#.TN.oD.Q#.cg.g9.qr.g8.9F#NH#Nb#Nb#Nb.gX#Nb.gX.ku#Nb.ku#Nb#5V#5V#J7.kv.rB.Q#.bH.tH.jm.rtawW.if.if.if.if.if.ig.if.if.if.if.if.if#Pk.ig.if.ie.rt.rt.if.kRaAo#c3#J6#c5#c5#c4#c4#65.Vi.zp.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.zq.#o#2XaAp.#paoL.D#aAq.YL.YL.YL.YL.zE#tt#c4#7GaAravtaAsaAt#OD#YM#2R#c4.w4#c5#c5#c5#c5#c4#c5.C9.YL.D#.#n.ClQt5.GYQtZQta.dhaAuaAvaAwaAxaAyaAzaAAaABaACaADaAEaAFaAGaAH#HG#7m#PJ#GxaAIaAJaAK#bc.xK.w4#F2#c4#c4#c4.vb#r3.bW.t1.gV.cv##j.gV.edaALaAMaAN.fTabiaAOasPaAPaAQ#Yv.aw.cv.cv.cv.cv.#B.cv.aw.aw.cv.cv.cv.#B.wBQtW.s7.bPQtWQtWQtW.s7QtWQtW.bP.t#.dV.s7.s7QtWQtWQtW.c8.#B.bW.#F#bi#c4#Ut#5H#6ya#Ma#Ma#MaqSa#Ma#Ma#MaqSaqSaqSaqSaqSaqSaqSa#M#6y#c5QtZaze.v4.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.qZ.vc.Fr#c5#c5#c5#c4#bg#bg#NW.gV#OW.v4.k6.v4.v4",
+".aw.aw.gV.c8.#E.bj#bg.w4.w4.w4.w4.w4.w4#bj.bO.#A.#y.#A.k6Qtk.#T.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.bjQtbQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQtjazf.eeaARaASaATaAUaAV.aj.w3#c5#c5a.RaAW#sz.YV.BM.g8QtFQtEQtEQtEQtEQtEQtEQtEQtE.lG.j..r7aAX#r3au#.ot.yk.rB.tKQtF.bHQtEQtEQtEQtE##F.ln.h3QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.g8.ep.clQtF.bHQtEQtEQtEQtEQtE.bH.um.YV.Vv.Vv.Vv#G7.LB.JL.zo.W0.W1.W1.W1##l.v9aw##Da#HS#fb.zJ.cR.T5#dk#Pk.hh.ds#xE#yB.h2.um.#6.qr.oP.lw.lE.kx.hg.dy#Nb#Nb#Nb.gX#Nb.gX#Nb#Nb.gX.kt.sX.tG.RRQtK.g9aAY.f8.ig.rt.if.if.if.if.gj.if.tF.if.if.if.if.if.if.if#Pk.if.rt.rt.rt.ifafHaem#uD#bi#c5#c5#c5.w4#c5#V0#2X.zr.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D##46.Qf.YLahj#c4#bk.zE.YL.YL.YL.YL.YL.D#.#l#c5#c4#c5.#m.w4#bi#bg.w4.w4#c4#c5#c5#c5#c5#c5.w4.#n#3U#3U.OIaAZaA0aA1aA2aA3#LBaA4aA5#RVaA6aA7#RVaz5aA8#8baA9#8c#L2a.8a.8a.9#PJaB.#MTaB##F2Qt5.v.#c4#c4#c4#c4#c4#c4.w4.bj#c2.cv.gV.aw.aw.gV.cv.edaBaaBbaBcarF.pcaBdacDaBeaBfaBgaBh.cv.cv.#B.cv.#B.cv.#B.cv.cv.gV#Km#WRaBiaBjaBkaBlaBlaBmaBmaBmaBnaBnaBoaBpaBiaBqaBraBs#TJaBtaBuavl#FDQtk.#B.aG.v5#ET#6zaqSaqSa#Ma#Ma#Ma#Ma#MaqSa#Ma#MaqSaqSaBv#9n#uH.Du.w4.Du#bg#bj.gV.qZ#OW.w2.v4.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.dh.aw.w4#c5#c5#c4#c4avSaqSa#MavSazN.#EaBw.v4",
+".aw.aw.gV.c8.#P.vb#bg.w4.w4.w4.w4#bh#id.auQth.#y.#y.#y.#y.#y.#A.c8#bi.w4.w4.w4.w4.w4.w4.w4.w4.w4.v5.cq.#A.d0.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.hbazf#gtaBxaBy.AaQtW#c4#c5#biaiK.mD.Vv.Vv.cW.kwQtEQtEQtEQtEQtEQtEQtEQtE.er.#7.VmaBz.FrafU.lu.Sb.ep.TP.ow.a4QtEQtEQtE.ko.OF.qnQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF#c8.Xg.cW.bHQtFQtEQtEQtEQtE.a4.oE.YV.Vv.Vv.Vv.Vv.aY.OL.Bn.W0.W1.W1.W1##l.yfaBA.TQ.57.Tw#Hb#gX#LH.ro.mC.jl#LH#0y.rvQtKQtDQtF.Jy#Jw.YW.rw.id#Nb.gX#Nb#Nb#Nb#Nb#Nb#5V#J7.jaQtJ.uB.cWQtK.a0QtK.g9.bG.jm.qx.if.rt.rt.rt.rt.if.ny#P3.rt.rt.rt.rt.rt.rt.ig.ig.rt.rt.if.if.gh#DL.zF#c3#c5#c5#c4#c4#c4.#o#ts#1D.D#.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.zE.YL.HH#uH#uH#c5.YK.zsaxZ.YL.YL.YL.YLap7#c5#c4#c4.w4#c4#c5#c5#c5#c5#c5#c5#c5.Jk.Du.og.vbaBBaBCaBDaBEaBF#JN#L7#LC#Fk#LC#Io#In#Fi#L2#L2#7m#L2#7maBG#PJ#PJ#FZ#G3#FXaBHaBIaBJ.m#Qt6#c4#c4#c5#c4#c4#c4#c4#c5#c4#bi.#P#c2.gV.cv.aw.gV.gV.cv.gVaBKaBLahBaBMaBNaBNaBOaBP.q5aBQaBRaBS.cv.cv.cv.aw.aw.cv.cv.cv.gV.gV#KmaBTaBUaBVaBWaBX#TRaBYaBZ#9k#xt#xt#xt#9kaBY#9kaB0#JW#VwaB1aB2aB3aB4#NtaB5#vtaB6#NW.og#13#6za#MaqSaqSaqSaqSaqSaqSa#N#Utavc.w4.Du.Du.Du#c4#c4#c5#c5#c4.w4#bg#be.#P.cvQtVaze.dh.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.sq#be#c4#c4.DuavSaqSaqSaqSaqSaqSaB7#F2",
+".aw.aw.aw.gV.fX#bj.w4.w4.w4.w4.w4.vc.#wQth.#y.#A.#A.#A.#A.#A.#yQth.#R.#F.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bjQtb.#y.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQth.dB#OW.eN.GY.bj#c5#c4.vbaB8#Cx.Vv.OuQtF.bHQtEQtEQtEQtEQtEQtEQtEQtE.bH.HK#BY#USa#Va.D.kj.ch.BM.epQtFQtEQtEQtE.fd.a0.Lj.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK.RR.Vv.tKQtGQtF.bHQtEQtE.a4.g8.BM.Vv.Vv.Vv.34.BM.2c.ycaxX.W1.W1.W1##l.v9aB9aC.#gN.#6.um.rB.Xf.33#vN.aY.lwQtL.#6QtK.um#Jw#xE#QC#YH#Nb.ku#Nb#Nb#Nb#Nb#Nb#5V#yw.jl#CA.3Y.rvQtF.#6QtKQtF.#6.lw#3a.qx.if.rt.rt.rt.rt.if.bI.h..if.jn.rt.rt.rt.rt.if#Pk.if.rt.if.rt.pAaC#aCaajP#c4#c5#c4#c5#c4#uHap7#NV.D..YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D##8z#c5#Ut#uH#c5#X0.YL.YL.YL.YL.Jl.W2#c4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c4.v..v.aCbaCc#MMaCd#JNaCe#In#wA#F0#KH#F0#F0#F0#F0#JNayAagkaCfaCg#FYaChaCiaBnaCjaCkaClaCmaCn#c4.v..Du.Du#c4#c4#c4#c4#c4#c4#c4#c4.L#.#F.#E#Fm.gV.gV.gV.gV.cv.cv.gVawBaCoaCp.edaCq.GZaCr.aLaCsaCtaCuaCv.GZ.cv.cv.cv.aw.cv.cv.cv.cv.cv.cv.#BQtW.s7.wB#KmaCwaCxaCyaCz#WW#SbaCAaCBaCCaCDaCE#uuaCFaCGaCHaCIaCJaCKaCLaCMaCNaCOaCPaB5aCQ#IV#3V#6zasVa#MaB7#ET.Du.w4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.w4#bi.#P.fX.bO.dh.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4#P#.bW.vb.Jka#NaqSaqSaqSaqSaqSaqSaqS",
+".aw.aw.aw.aw.gV.c8#bi.w4.w4.w4#bc.aw.#y.#y.#A.#A.#A.#A.#A.#A.#A.#y.#y.#S.cv#bg.w4.w4.w4.w4.w4.w4.w4.w4.w4.w3.wZ.auQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#AQth.#yQta.mi#c4#ic#id##q#gE.9F#Bd.k#.bHQtEQtEQtEQtEQtEQtEQtEQtE.bH.JL.or#W5.bjaCR.ttatk.oD.3Y.rA.bHQtEQtEQtEQtF.JV.#6.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.Ou.9V.BM#gN.lGQtFQtE.bH.bH.pM.Vv.Vv.Vv.Vv.Vv#mEaCSaCTaCU##l.bc.W1#a9aCV#KO.9V.k#.#6.#6QtK.bH.koQtK.aZQtK.bH.Ou#xE#CA#Hb.OU#5V.ku#Nb#Nb.gX.gX#Nb.k..ds#1U.Xg.um.#6.g9.#7QtKQtKQtK.ch.mE.qz.if.rt.rt.rt.jn.T5#j2#GD.pD.qx.rt.rt.rt.rt.if.igawW.rt.if.qz#gWaCW#d0.#F#c5#c5#c4#c4#c4#c4#bk#ts.HH.zq.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.zF#uD#uH#Ut#Ut#c5.#p.zq.YL.YL.YL.YL#7S#c4#JR#c5#c5#c5#c5#c5#c5#Lm.v..L#aubaCXaCY#L8#LC#In#HH#F0#F0#F0#F0#F0#JNaCZ#9iayAaC0aC1aC2aC3aC4aC5#SQ.br#ci.#u.0s.v..v..Du.Fr#c4#c4#c4#c4#c4#c4.Du#c4#c4#c4#c4.vb.#P.gW.wZ.gV.gV.cv.gV.cv.cv.#B.gV.st.#B.o7.cv.cv.cv.c8#5u#LAaC6aC7aC8#O7.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.gV.cv.cqQtW.wBaC9aD.aD#aDaaDbQtW.bP.wBQtYQtZ.bW#a4QtfQtf.bW.#EaDcaDdaDeaDfaDgaDhaDiaDjaDkaDlaDmaDnaDo#rjaDpaDpaDqavQavQ#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.w4.vc#a4.qZ.w2.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4QtV.w4a#MaqSaqSaqSaqSaqSaqSaqS",
+".aw.aw.aw.aw.aw.fX.bW#bg.w4.w4#bcQtVQth.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#yQtV#bi.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#beQta.#y.#y.#y.#A.#A.#A.#A.#A.#A.#A.av.#y.#A.#y.#y.#y.#y.#y.#A.#y.#y.#y.#F#c5#xK.zoaDr#jT.Xb.oEQtFQtEQtEQtEQtEQtEQtEQtEQtE.bHabd.Txacw.bjaoA.rj.i4.mD.tJQtGQtFQtEQtEQtEQtE.Lj.JYQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtFQtE.Xg.Vv.Vv.3YQtGQtF.bH.kw.RR.Vv.Vv.Vv.Vv#jT.cW.zx.v9aCU##l.bc.W1aDsaDt#fb#xE.LB.#6QtKQtKQtKQtKQtK.#7.k#.vt.TQ#CA#xE#xE#Hd.pE#Nb#Nb.gX.T5#Nb.Oz#oN.TP.a3.h6#c8.cW.Qh.h6.h6.BHQtKQtM.qz.if.rt.rt.rt.jn.rt.pD#yB#Hbayr.T5.rt.rt.rt.rt.if#Pk.if.if.kRaDuaDvaDw#be#c5#c4#c5#c5#c4.w4aDx#MW#NV.JI.zE.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.zF#Ut#c5#Ut#Ut#Ut#c5#ts.YLaxZ.YL.YL#2javc#c4#c5#c5#c5#c5#c5.Du.v.aDyaDz#FX#L7#wA#F0#KH#F0#F0#F0#wBaDAaDBaDCaDDaDEaDFaDGaDHaDIaDJaiT.d0.av.dg.bO.#T#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4.Jk#c4.w4#c4#c4#c4#bgQteQtf.aw.gV.gV##j.gV.cv.cv.cv.cv.c8.c8.cv.#B.cv.#B.#B.cv.gV#LAaDKaDLaDMaBh.cv.cv.cv.cv.#B.cv.cv.cv.cv.cv.cv.cv.cv.aw.cv.cv.cv.#B.wB.c8.gV.wZ.bW.#Pa.Ra.R#TK#qd.DHa.RazgaDNaDOaDPaDQaDRaDSaDTaDUaDVaDWaDX#OeaDYaDZaD0aD1aD2aD3aD4aD5aD6#F2aDpaDpavQavc#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#bg.#P.fXQt..v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..bO#F2a#MaqSaqSaqSaqSaqSaqS",
+".cv.cv.cv.cv.cv.c8.c8.#T.w4.w4#bg.bO.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#y.#y.#w.#T.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bg#idQt..#y.#y.#y.#z.#y.#y.#y.#y.#y.#A.#y.#A.dg.au.au.k6.v4.v4.v4.G0.#R.w4#ic#bgaD7#LF.Vv.57QtK.bHQtEQtEQtEQtEQtEQtEQtEQtF.bH.JWaD8ap7aD9.qp.wf.h6.uB.bGQtF.bHQtEQtE.h3.ov.Xn.h3QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.7X.33.Vv.Vv.BM#bAQtFQtK.um.uB.Vv.Vv.Vv.Vv.3Y#c6aAn.W1.W1.W1.W1.zG.R5.MV#Hb#e0.uz.#6QtFQtKQtF.Qi.rv.Vv#CA#CA#CA#xE#Hb.sd.id#5V.ig#3a#Oy.qy.mD.dw.LB.9V#xE#CA#CA.TQ.Vv.rzQtD.qy.qz.rt.rt.rt.rt.rt.rt.ux#gX#GD#jT.kx.rt.rt.rt.rt.if.ig.ig.if.pAaE.aE#aEa#bg#c5#c4#c4#c5#c5.w4#c4.bd.W2#UqaEb.Fp.D#.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.Qf.bd#uH#Ut#Ut#uH#Ut.D..YL.YL.Jl.P5.Jk.w4#c5#c5#c5#c4Qt6aCbaEc#FZ#L7#F0#F0#F0#F0#F0#F0#wBaEdabGaEeaEfaEgaEhaEiaEjaEk.bi#Ne#GQ.G0.v4.w2#c2.L#.Du.Du.Du#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4.Du#c4#bcQteQtf.aw.gV.gV.aw.gV.gV.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cvaBhaElaEmaEn#O7.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.cv.gV.t1.gV#c2a.R#id#qd#TKaEo#TK#ida.R#c2aEpaEq.L.aEr.Oj.L..L.alE#7CagAaEsaEtaEuaEv#JoaEwaExaEyaEz.0naEA.RGaEBaECaEDaEEaEFanHaDpaDqavc#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.Fr.vb.#F.#Q.w2.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4#XD.dh#be#8paqSaqSaqSaqSaqS",
+".#B.#B.#B.cv.cv.cv.fX#m8#bg.w4#bg.bO.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQth.aE#bj.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bg.bW.#R.#y.ah.#A.#A.au.au.v4.v4.v4.v4.x9.w2.w2.w2.w2.w2.w2.x9.dg.#B.Fr#bjaEG#xE.Xb.Xg.bH.a4QtEQtEQtEQtEQtEQtEQtE.ab.k#.RT.rjaEH#3Vatb.mt#Uk.oD.0A.h3QtFQtEQtEQtE.OF.unQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.kw.ep.Xb#fd.Vv.YV.cT.bH.bH.oD.34.Vv.Vv.YV#fdaEI#RP.W1.W1.W1.W1aEJaEK#f#.9F.Vu.TP.cSQtKQtFQtK.ch#wK.TQ#CA#CA#CA#xE#Hb.r#.kx.sY.ck.aa.g8.g9.rv#Ha#CA#CA#CA#CA#CAaEL.lF.RS.7P.ig.rt.if.rt.rt.rt.rt.rt.r##wK#wK#wK.pD.qx.rt.rt.if#Pk#Pk.gjaEM#ceaENaEO#c4#c5#c4#c5#c5#c5#c4.w4#c5#tt#uK.ya.zF#dhaEP.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D.#uDaka#uH#uH#Ut#c5#xz.D#.YL.YL#eP.al.3U#c5#ic.L#.L#aEQaER#L7#HH#Im#KH#F0#F0#F0#JNaCZaESaETaEUaEVaEWaEXaEY.dgaxL#GS.v4.v4.v4.v4.t1.v5#c4#c4.Du.w4.w4#c4.Du#c4#c4#c5#c4.Du.Du#c4#c4#c4#c4#c4#c4#c4.w4#bj.gW.aw.t1.gV.aw.gV.gV.cv.cv.cv.cv.cv.cv.cv.aw.cv.cv.cv.cv.cv.gVaEZaE0.zlaE1#PP.cv.cv.cv.cv.cv.#B.cv.cv.cv.cv.#B.cv.cv.#B.c8.awavla.R#bd#idaEo#qd#qda.Ra.R.aw.gV.cvaE2aE3aE4.0p.14.Tq.MM.Jc.Jc.JcalEaE5aE6aE7a#EaE8aE9aF.#6EaF#aFaaFb.K7aFc#vraFdaFeaFfaFgaFhaFiaDqaDpavQ#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.v5.#E.bO.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.#EaB7aqSaqSaqSaqS",
+".cv.aw.aw.aw.aw.aw.gV.fX.#T.w4.w4.fX.as.#y.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#A.#yQth.au#id.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w3.bW.ai.au.k6.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au#be.w4##t.YS#LE.BM.lFQtFQtEQtEQtEQtEQtEQtEQtE.bH.ko#gC.rfaFj#bjaFk.r6.lv.mD.Xb.sf.cX.bHQtE.bHQtK.OTQtK.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.cS#P2.Vv.Vv.YV.YV.TF.cl.rB.Vv.Vv.Vv.Vv.34axvaAn.W1.W1.W1.W1aEJaFl.jc.Xb.oD.7T#Iy.#6QtFQtKQtK.cW#wK#CA#CA#CA#CA#CA.BM.tK.cW.cg.#7QtK.3Y#CA#CA#CA#CA#CAahl.uB.ow.RS.rv.OA.qx.rt.rt.if.if.if.Si#1##xE#xE#Hb#CA.Si.T5.rt.if.ig.ig.qz.lZaFmaFn##caka#c5#c4#c5#c5#c5#c5#c5#c4#c5#m5#uK.ya.pX.P5.zE.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.OI#Ut#c5#bk.#m#c4#uH#c5#qc.YL.YL.YL.JI.#PaliajPa#MaFoamL#wA#Im#F0#F0#F0#F0#Im#HH#L8aFpaFqaFraFs.tbaxfaxf#Hz.v4.w2.v4.v4.k6QtV#be#c4#c4#c4#JR.Du.Du#c4.w4#c4#c4#c4#c4#c5#c4.Du#JR.w4.Du.Du#c4#c4#c4.Jk.w1.#PQtf.gV.fX.aw.aw.gV.aw.cv.cv.#B.cv.cv.cv.cv.cv.cv.cv.cv.cv.o7.aIaFtaFuaFvaFwaFx.cv.cv.#B.#B.#B.cv.cv.cv.#B.cv.c8.gV.gVavla.RaEo#qda.R#id#qd#qda.R.aw.gV.c8.c8.gV.p5.staFyaFzaFA.YG.K8.Jc.Jc.Jc.Jc.MM.TqaFBaFCaFD#F1#WYaFEaFFaFGaFHaFIaFJaFK.TqaFL#wq.L..PYaFeaFMaFNaFO.FraDpaDq#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.Du.#T.fX.dh.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.gV#5GaqSaqSaqS",
+".gV##j.aw.aw.aw.aw.aw.t1Qtf.vb.w4#id.#yQth.#A.#A.#A.#A.#A.#z.#y.#y.#y.#y.#y.#y.#A.dg.#y.au#id.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bg.#PQta.au.au.v4.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r.Qt#.L##bjaFP#CA.Vv.e6QtF.erQtEQtEQtEQtEQtEQtEQtE.bHQtF.TY#2e#c5aFQ.0y.JV.h6.lE.oD.k#.a4QtEQtEQtE.Fu.JYQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.a4.ch#Bd#fd#jT.kv.qI.f8.cT.YV.Vv.Vv.Vv#jTaFR#RP.W1.W1aCU##l.w5#p7.lC##N.dw.Xg#xEQtG.#6QtFQtK.#6QtG.YV#xE#CA#CA.aY.rA.#6.#6QtK.#6.a3.7T#CA#CA#CA#CA#CA#CA.tK.Qt.pM.rx.rt.if.if.if.ifaiM.h..9V.9F#xE#CA#28##N.5Y.if.if.ig.ig#KQ.iaaFSaFT.49#Xx#c4.Fr#c5#c5#c5#c5#c5#c5.w4#c5#tt.Vi#46.zpaEb.zq.D#.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.OI##w.#n#c5#c4.Tt.Du.cv.vb#Lm#vE.C9.YL.D##sy#9xaFUaFVaFWaFX#JN#Im#F0#F0#F0#F0#F0#LC#WTaFY.Ea.t4af2#GR#Caaby#Ne#GS.v4.v4.v4.bO.#F.Du.Du.Du.Du.w4.Du.Du.w4.Du.Du#c4#c4.Du#c4#c4.Jk#c4#c4#c4#c4#c4#c4#c4#c4#c5.w4.#F.gW.bW.gV.gV.aw.gV.aw.cv.cv.cv.cv.cv.cv.cv.gV.o7.gV.cv.gVaFZaF0aF1aF2aF3aF4aF5#PP.#B.#B.#B.#B.#B.cv.cv.gV.gV.gV.awa.R#qd#qdaEo#TK#id#ida.Ravl.gV.c8.c8.#B.c8.c8.gVavla.RaDbaF6#oc#9q.Jc.Jc.Jc.Jc.Jc.Jc.TqauVaF7#6D#WW#WW#4.#zt#ztaF8aF9aG.aG#aGaax7#7C.Tp.K7.L.alE.VfaGbaGcaFhaGdaGeaDq#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.v5.bWQtb.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..fXaGfaqSaqS",
+".c8.gV.aw.aw.aw.aw.aw.gV#Fm.cE.w4aGg.#wQth.#y.#y.#y.#y.#y.#A.#A.dg.#A.au.k6.v4.v4.v4.v4.au.auQte#bc.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bg.#T.aF.dh.r..v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.ah.aLaGh#gI#Hb.Vv.TFQtF.bHQtEQtEQtEQtEQtEQtEQtE.bHQtE#bn.kcamw#44#c9.sNabd.aY.Vv.cWQtF.bHQtEQtE.yk.i8.h3QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.koQtK.Qh.YV.Vu.Vt.g..7T.0A.BM.Vv.Vv.YV#xEamg.zGaCU##l.ap.#q.v5.Jn.T5.7J.0A.cl.vt.BM.cSQtKQtFQtF.#7QtE.cY.epaGiQtFQtK.kwQtKQtK.ow#wK#CA#CA#CA#CA#CA#CA.Vv.ch#jM.rt.rt.ig#Pk.if.Si.r#.Xb.TP.BM#Hb#CA#CA#yB.ro.rt.if.hh.if.a2aGjaGkaGlQtm#uH#c4#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#Ut#8z.Qf#0g.Bo.yb.zF.YL.YL.YL.YLaGm.D#.D..D.#Uq#ts#7G#3V.#l.#k.YK#qc#NV.zo.zo.yeaGn#vE#UtaGo.D#aGpaGqaGraGsaGt#wB#KH#F0#F0#Im#F0#Io#HHaGu#QqaGvaGwaGx#Ca.svaGyaGzaGAaGBaGC.w2.#E.vb#c4#c4.w4#c4.Du.Du.Du.w4.vb.w4.w4.Du#I8#c4.w4#J6arM#NX#NXapv.w4.DuarMadOawzawAawzaGDazg#guaGEawBaGFaFZaGFaGG.st.gV.gV.cv.#BaF6aGHaGIaGJ.qZaGKaGLaGMaGNaGOam0aGPaGQaGR#O7.#B.#B.#B.#B.cv.c8.gV.aw.#E#idaEo#qd#qdaEo#qda.R.#E.gV.c8.c8.c8.c8.c8.cvavla.Ra.R.aw.c8ayiaGS#9q.Jc.Jc.Jc.Jc.Jc.Jc.14#7BaGTaGUaGVaGWaGXaGY#wx#xr#xr#ztaGZaGZaG0aG1aG2.Tq#wq.K8.K8.L..Tp.PYaG3aG4aG5#KzaDpavQ#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#bc.#E.bO.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..aFaG6aqS",
+".#T.c8.c8.gV.aw.aw.aw.o7.aL.cE.w4#biQt..#y.au.au.au.k6.v4.v4.w2.v4.w2.w2.w2.w2.w2.w2.w2.w2.au.k6.#F.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4.w4#bi.bW.bO.k6.r..v4.v4.w2.w2.w2.w2.w2.w2.w2.v4.auQteaG7#Mi#LE.0A.k#QtFQtEQtEQtEQtEQtEQtEQtEQtEQtE.#6.LtaG8aGhaG9.zL.i4.a3.YV.h2.bH.bHQtE.bH.JY.qqQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.rB#Ha#fb.Xh.sX.7T.TP.YV.Vv.YV#LFaH.aEJ.y..fJ#Us.Du#NW.C9aH#.OA#Hb.um.#6.Xe.bGQtKQtFQtFQtFQtK.aZ.#6.#6QtKQtFQtFQtF.#6#Iy.TQ#xE#CA#CA#CA#xE.9V.sf.mU.qz.ylQtMQtJauC.9X#xE.YV.Vv.BM#jT#xE#CA#xE#Hb.jl.qx.ig#PkaHaaHbaHcaHd#Xx#c5#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c5#bk#l##qc#X0.zo#Uq.zE.zq.C9aoM.W2.#m#uD#tt#c1#8zaoM.P5#eP.Fp.MP.HHahb#1D.yb.OI#0gaHeaHfaHgaHhaHi.q3aHjaHkaHl#F0#wB#K7aHmalAaHnaHoaHpa.2aHqaHraHsaHtaHuaAbaHv.mf.rXapZaHwaHxasU.Du.w4#c4#c4.w4.w4.w4.Du#c4.Du.Du.Du#c4#NYaqQ.w4aHyaHzaHAaHBaHCaHDaHEaHFaHGaHHaHIaHJaHKaHLaHMaHNaHOaHPaHQaHRaHSaHTaHUawBaFZaHVaHWaHXaHYaHZaH0aH1aH2aH3aH4aH5aH6aH7aH8aH9aI.aCq#I7.#Q.c8.c8.gVavla.R#qd#qd#qd#qd#qdaEoa.R.fY.c8.c8.c8.cv.cvavla.R#TK#qda.R.cv.c8aI#aIaaIb.K7.Jc.Jc.Jc.Jc.Jc.Jc.Tq#wpaIcaIdaIeaIfaIgaIh#Vv#B.#yu#zs#zs#ww#yoaIiaIjaIkaIlaIm#7C.Tq.Jc.K8.L..Oj.PYaInaIoaEE#F2aDpavc#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4.#P.bO.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..fX#9n",
+".w4.w1#c2.c8.gV.aw##j.fX.#E#bc#bg.c8.dg.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r..dh#bj.w4.w4.w4.w4.w4.w4.w4.w4#bh#bh#bh#bg.vc#be#be#bi#bg#bg.#T.gVQt..au.au.v4.w2.w2.w2.w2.w2.w2.r.Qt.#bjaIp.TQ.YV.oE.cXQtEQtEQtEQtEQtEQtEQtEQtE.fdQtE.QkaIq.v5aIr.rj.wf.ow.0A.Vv.rv.cXQtE.bH.kw.tr.#6.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.ch#e1.53.0v.vh.9P.qI.BM#fd.YV#CxaIs.BF#Us.w4.v5.vb.vb.fJaIt.j2.Vu.Xe.a0.qrQtGQtEQtKQtKQtFQtFQtFQtFQtFQtFQtFQtFQtF.aZ.lw#xE#xE#CA#CA#xE#Hb.uB#wKaIu.9X#xE#Hb#28#28#gE#xE#xE#CA#xE#xE#CA#CA#Hb#28aiM.ie.pE#ywaIv.vO#c2#uH#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5.#o.#k#eY.dSaoL.#i#2R#uH#2R.W2#2j.OI.zE.YLaxZ.HH#MWaIw#a4.bO.w2Qt..w2#HzaIxaIyaIzaIAaIB.iO.qe.rV#3daICaIDaIEaIFaIGaIHaIIaIJaIKaILaIMaINaIO.pcaIPaIQaIRaISaIT.pcaIUaIVaIWau6.w4.Du#JR.Du.w4.w4.Du.w4#c4#c4#rj#NXali#N5aIXaIYapIaIZapeaI0aI1aI2aI3aI4aI5aI6aI7aI8ajwaH5aH5aI9aJ.aJ#aJaaJbajAaJcaJdaJeaJfaJgaJhaniab3aw8aniadraJiahT.TqaJjaJk.14aJlaJm#NQaJnaJoaJpaJqaJraJs.#B.cv.awazgaFz#GUa.R#gt.gV.c8#FD.cvavla.R#qda.Ra.Ra.Ravl.bkawBaJtaJuaJv.L..MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14atzaJwaJxaJyaJzaJAaJBaJCaJDaJE#xr#zs#zs#zs#wwaIi#2#aIkaJFatzatz.MM.Jc.Jc.K8.L.#wqaFdaJGaJHaGdaDpavQ#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4.bWQt..v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..gV",
+".w4.w4.w4.#F.c8.gV.fX.cv#bi.w4#bg.#w.k6.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..dh#be.w4#bh#bh#bh#bh#bh#bh.w4.w4.w4.#F.c8.gV.gV.gV.aL.bW.bW#bj.#T.#P.#Q.dh.dg.k6.v4.w2.w2.w2.v4.dg#Hra.S#gE.Vv.a3.cX.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.nt.r8##t#eYat0aJI.wg.tK.Xb.qE.bH.bHQtEQtEaJJ##FQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.eq.BM#bx.zJ.Vt.0v#Hb.BM.Vv#LE#lc#7G.v5.vb.vb.w4.vb#c2aJK.jh#bx.BM.bH.RS.ykQtKQtFQtKQtFQtFQtKQtFQtFQtFQtFQtFQtFQtKQtK.aY#CA#Cx#CA#xE#xE#CA#CA#Hb#Hb#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#xE#CA.RM.kRaJLaJMaJN.w4#c5#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#ET#3V.#p#Ut#uH#c4#UsaoL#Uq.D#.JI.W2.16.#F#OW.r..r..v4.v4.v4#Ne#HzaJOaJPaJQaJR.qd.eY.rV.rV.rWa.naJSaJTaJUaJVaJWaJXaJYaJZaJ0aJ1aJ2aJ3aJ4aJ5aJ6aJ7aJ8.ed.BFaJ9aK.aK#aKa#J6#c4#c4#c4#c4#c4#c4#c4.Du.w4anI.vaaKb#OU#LpaKcaKdaKeasKadqab5aKfaKgaKhaKiaikahSanS.Je.14.Tq.Tq.Tq.14aKjaijaeJ.K7anSaKkaKlab6aniaw8ab4adqab4adqab4ab3aKmaKnairaKoasHaJkaKpaKqaKraKsaKtaKuaKvaKwaKxaKyaKzaKAaKBaKCaKDaKEQtW#guaKFaKG#GT#GTaKGaKF#GTazgaKH#CFaKIaKJ.PY.L..K9.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.TqatzaKKaKLaKMaKNaKOaKPaKQaKR#Zp#xr#zs#zs#zs#zuaIi#HLaKSaKTanR.Tq.Jc.Jc.Jc.Jc.K8.L.alEaKUaKVaKWaDpavQ#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#bc#m8.dh.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au",
+"#bg#bh.w4#bg#be#r3.#F#bi.w4.w4#bi.#w.k6.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.GZ.r.Qt.#bi.w4.w4.w4.w4.w4.w4.w4.w4.Du.vc.c8.o7##j##j.gV.gV.gV.gV.gV.bW.#F.#T.#P.#Q.dh.r..G1.v4.x9.v4.bu.#naKX#LE.7Z.bH.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.cS.DyaKY.#Tabc.zL.HK.h6.lE.9F#mGQtF.erQtE.wd.yk.cg.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.LB.YV.eo.Vt.Vt.mC.9V.BM.Vv.aY.zr#bj#bg.w4.w4.vb.gVaKZ.gh#rm#HbaK0QtK.i4.tv.#6.cSQtKQtFQtFQtFQtFQtFQtFQtFQtFQtFQtK#Iz#wK#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#xE#IF#RkaK1aK2aK3#c5#c5.Fr#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c4#c5#c5#c4#c4#c4#a9.vc.vaaf9.t1.au.r..r..v4.w2.w2.v4acX.w2aK4aK5aK6.e#an0.qa.qa.rW.e#.iOaK7aK8aK9aL.aL#aLaaLb.su.k6aLcaGxaGxaLdacXaLeaLdaxL#O7.3U.Du#Ivau6aLfau6#J6.Du.Du#c4.Du.Du#c4#c4#c4#J6aDsaLgaLhaz##LVaLiafdaLjae0adpadfaLkaLkaniankadr#BkaLlaLmaLnaLo.OkaLpaLp.OkaLoaLqaLr.PZ#p2aiiaiiaLsaLtaLuaLvab3aeZagPab4adqab4aeZakTaLwaLxaeJaijaLyaLzaLAaLBaLCaLD#RUaLEaLFaLGaLHaLIaLJaLKaLLaLMaLNaLOaLPaLQaLRaLSaLTaLUaLV.RFaLW.K7.Oj.K8.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc#wp#xjaLXaLYaLZaL0aL1aL2#Zp#HK#zs#zs#zs#zs#ztaL3aL4aL5aev#wp.Jc.Jc.Jc.Jc.Jc.Jc.L..Tp.OkaL6aL7aDpavQavc#Lm#c5#c5#c5#c5#c5#c5#c5#c4.vc.aF.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4",
+".vb#bh#bg#bg.w4.w4.w4.w4.w4.w4#bgQtV.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r..cu.vb.Du.Du.Du.Du.Du.Du.Du.Du.w4.#C.cv.o7.gV.aw.aw.aw.aw.aw.gV.gV.cv.#P.vc.dI.w0.#QQt..au.au.dgQtdaL8#LF.BM.lFQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.ntaL9#id#W5.tt.sN.j..qE.Vv.qE.bH.bHQtE.nt.Lj.sf.cX.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.cg.7Y.Xb.OU.Vt.Vt.OU.YV.YV#gE#rF#c2.vb.vb.w4.w4.#F.BDaM..sX#xE.Xe.#6QtF.JV.lp.yh.bHQtFQtKQtFQtFQtFQtFQtFQtFQtFQtF.#6#bC#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#Hb#9G.HraM#azf#ET#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#Lm#c4#c4#c4#be.qZ#P#.dh.r..v4.v4.w2.w2.w2.v4acX.aKa.4aGQaMa.iOae4.eYaMb.q6.ipatGaMcaMdaMe.wB#NeaGxaLd#Ne#GS.G0.v4.v4.w2.w2.v4.v4.w2.gV#bi.Du.Du.w4.Du.Du.Du.Du.Du#c4#c4#c4#c4#c4#c4#c4#J6aqHaMf.bnaMg.bpaMhaMiaMjadraMkaMlaMmaMnaMoaMpaMqaMraMsaMtaMuaMvaMwaMxaMyavPaMzaMvaMAaMBaMCaMDaMEaMFaMGaMHaMIaMJaMKaMLadpab4adqab4ab4ab4aeZaMMaeKaij.PZaijahHaMNaMOaMPaMQaMR#0q#VKaMSaMTaMUaMVaMWaMXaMYaMZaM0aM1aM2aM3aM4.K7aM5aM6aFLalE.Tq.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MMalE#7C#7CaE5aE5aE5#7BaM7aM8aM9aN.aN##BOaNa#Rg#Ct#xr#zs#zs#zs#ZpaNbaNcaNd.Veaui.Jc.Jc.Jc.Jc.Jc.MM.Jc.K7.TpaNeaNfaD5aNg.JkaDqaDpavQ#ic#c5#c5#c5#c5#c5.Du.#PQt..r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"#rj#J6#xK#bg#bg#bh.w4.w4.w4.w4.w4.#P.#A.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.dg.cq.vb.Du.Du.Du.Du.Du.Du.Du.Du.Du#bg.#T.wZ.gV.gV.aw.aw.aw.aw.aw##j.gV.gV.cv.#P.vc.v5.vc.#P.#Q.eeaNh.YV.9V.dw.kw.fdQtEQtEQtEQtEQtEQtEQtEQtE.bHQtFaNi#xzaG7.nr.rj.rm.cW.BM.Vv.rv.cX.fdQtE.qq##D.cgQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.cX.dw.YV#wK.g..Vt.Vt.7J.BM#szaNj#id#bg#bg#bh.w4.v5.v5aNk.lC#Fr#tv.#6QtK.bH.qn.qp#IxQtK.bHQtFQtFQtFQtKQtKQtFQtFQtFQtKQtF.aY#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#xE.TNa.0.zw.fY#c5#Lm#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#bf#bg#uH#13Qt0.v4.r..v4.v4.v4.w2.w2.w2.v4acX.TmaNlaNm.iO.q3.qe.sFaNn.iOaNoaNpaNqaNrabyaGx#Ne#Hz.v4.x9.w2.w2.w2.w2.w2.w2.v4.v4.w2.gV.v5#c4#c4#c4#c4.Du#c4#c4#c4#c4#c4#c5#c5#Lm#c4#c5#c4#c4arMaNsaNt#N1aNu.#OaNvaNwaNxaNyaNzaNA#Kz#NYarLawAawAawAawzapwavQanHaNB#OM#ic#ic#icavQavQavQaDqaDpapw.FraMxaNCaNDaEEaNEaNFaNGagCaniab3ab4adqadqab4agPaNH.JeaeJ.14aijaqpaM7aFCaNIaNJaNK#WX#TR#Jo#JW#3paNLaNMaNNaNOaNP#yqaNQaNRaNSaNTaNUaNVaNW#xhagA#7C.Tq.Jc.Jc.Jc.Jc.Jc.Jc.JcalEaNXaNYaNZaN0aN1aN2aN3aN4aN5aN6#JoaN7#LxaL0aN8aN9#Gy#xr#zs#zs#zsaO.aO#aOaaObagr#wp.Jc.Jc.Jc.Jc.Jc.Jc.MM.Jc.K7alEaOc#rhaOdaOeaOfaD5#F2aDpavQ#ic#c5#c5#c5#c5.vcQtV.au.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+".L#aOgawx#NYaqQ#xK#bg#bh#bh#bh.w4#bg.ai.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.#F.Du.Du.Du.Du.Du.Du.Du.Du.Du.Du.Du#bg.bW.fX.gV.aw.aw.aw.aw.aw.aw.aw.gV.gV.gV.cv.#P.w1.vb#bg.fJ.HM#gE.3Y.bH.bHQtEQtEQtEQtEQtEQtEQtEQtE.bH.cS.2caOh#r3av5.tt.tv.2c.h2.Xb.h2.bH.bH.k#.i4#gJ.rB.cX.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.lG.Xg.BM#bx.zJ.Vt.zJ#Fr#jT#iq.#p.vc.w4.vb#bh.vb#FD.P9#tJ.ny.3Y.#6.#6.bH.h3.pt.lp.lp#bn.#6QtKQtKQtFQtFQtF.bH.bHQtFQtKQtF.um.TP.TQ#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#xE#Hb.knat1aOi#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#bj#bg#eRawR.ba.#E.v4.v4.w2.w2.w2.w2.w2.G0acX.v4aOjaOkaNnaOl.gRaOmaOnaOoaOpaOqaOrQt.aGx#GR.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4#P#.p5#bi#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5#c5#c5#c5#c5#c5#c4#c4#NYali#NX#NX#PyaqQarNadOarLanH#F2#c4#c4#c4#c5#c5#c4#c5#c5.Du.vb#bc#bi.3U#bi#c5#c5#c5#c5#c5#icavc#F2#rjanHaGeawz#NXaOsaOtaOuaOvanjab3ab4adqagQaeZajnaOw.WT.14.JcalEajkaOxaOyaNOa#EaN6#S0#Ev#S0#W.#F1#WY#TR#WY#WYaNPaNPaOzaE9aE9aOAaOBaOCaODaOEaE6aE5#7C.Tq.Tq.Jc.Jc.K8#xhaOFaOGaE9aE9aOzaOzaE9aNL#WY#Jo#F1#EvaOHaOIaOJaOKaOLaOM#xr#zs#zs#zsaONaOOaOPaOQanQ.Tq.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.Jc.K7.K7.L..OjalEaIbaORaOSaOT#rjaDpavQ#c4.DuakaaOU#be.dh.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"aOVaOWaOvaOXaOY.L#aqQ#bg.vb.w4.w4.Du.#F.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r.Qt##bg.Du.Du.Du.Du.Du.Du.Du.Du.Du.Du.Du.cE.aL.fX##j.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.gV.gV.gV.#P.#Paj4#Cx#by.lGQtFQtEQtEQtEQtEQtEQtEQtEQtE.er.bH.claOZ.w1aO0.rj.we#mD.sf.BM.9F.JyQtFQtE.sQ.i7#e1.k#QtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.Qh.YV.BM.dv.Vt.Vt.g..lx.0A#7W#c2#Iv.vb.w4.w4.#T#46.oC.hh#gF.#6.2b.9F.YV#gF.LB.ty.kf.mt.rm.yk.lv.OL.pt.ov.Dy.uo.qrQtFQtF.wg.wn#QC#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#Hb#0xaO1aO2aix#13.w4#bg.w4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4.w4.w4#bc#bg#bg#bi#uD#0aaO3aO4aO5.v4.r..v4.w2.w2.w2.w2.G0acXQtaaO6aO7.rWaO8.hR.u6aO9aP.aP#aPaaPbaPc#Hs.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.t1#bi#c4#c4.w4.w4.Du.w4#c4.w4.w4#c4#c4#c4.Du#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5#c5#c4.vb#bi.w0.cv.qZ#OWQt..w2.r..#Q#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5avcawzadOaPdaPeaPfanjaeZafaab5aeZajhaeKauV.PZ.Jc.Jc#7CaPgaPhaPiaPjaPkaPlaPmaPnaPoaPpaNPaE9aE9aE9aE9aNP#WY#WYaOzaEvaPqaPraN5aPsaPtaPuaPvaPwaFBaFBaPxaH2aPyaE8#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#JoaN6aPzaPA#76aPBaPC#Zp#xr#zs#xr#zraPDaPEaPFaPG#wp.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K7.L..Tpaig.0paPHaPIaDqaPJa#MaqSa#M#ic.sq.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"aLkab3ab3aniagPaPKaPL#NYaPM.vb.v##rj#bgQtW.#A.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.dg.#P.Du.Du.Du.Du.Du.Du.Du.Du.Du.Du.Du.Du.v5.cv.o7##j.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.c8#vH.zD.LBaPN.fd.bHQtEQtEQtEQtEQtEQtEQtEQtEQtE.a4.g8.wgamw#uD#oo.we.qq.oP.h2.YV.0A.ch.a4.bH.Fu.mD.epQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.TF.Xb.TO.ds.Vt.Vt.mC.TQaPO.Jk#bi.w4.w4.w4#bg.aw.Hm#YiQtJQtF#P2.TQ#CA.TQ#QC#Li##o.kk.#6QtK.a0.nt.sQ##F.rm.mt.ot.rn.i7.ka.un#j5.TQ#CA#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#xE#Hb.9UaPPaPQaPRaPSaPTaPUaPV.#p#bc.w4.w4#bg#bg#bg.vb.L##c5#m5#8z#4faPWaPXaPYaPZaP0#gD#dl.dKav..v4.w2.w2.w2.w2.w2#GR.aIaP1aP2.pcae3aINaP3aP4aP5aP6aP7aP8aP9aQ..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4aze.aw#bg.Fr#c4#c4#c4#c4.w4#c4#c4#c4.Du#c4#JR#c4#c4.Du#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c4#c5#c4#c4#c5#c4#c4#c4#c4.w4.vc.bW.qZQt..dh.v4.v4.v4.v4.v4.v4.v4.v4.w0#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#F2arNaQ#aQaaQbankaQcadqab4ab4ajCahSaeJ.14.Jc.Tq#7CaQdaQeaQfaQgaQhaQiaQjaQkaQlaQmaQnaQoaQpaDWaQqaQraQsaQtaQuaQvaQwaQx#yqaQyaQzaQAaQBaQCaQDaQEaQFaQGaQH#XI#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#WW#WaaQIaQJaQK#34#Zp#xraONaQL#69aQMaQNaQOajc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K7.L.alEaQPaQQaQRaQS.tCaQTaqSarO.gV.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"ab4ab4ab4adqab4aLkajIaQUaQVadO#tm.vb.vb.awQtb.r..GZ.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.auQt..3U.cE.cE#bc.vb.Du.Du.Du.Du.Du.Du.Du.Du.w3#vH.gV##j.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.fX.bWaQWaQXaQY.rAQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.ch#mGaQZaGhaQ0.0z.ty.Qk.RR.BM.9F.XeQtFQtE.Xn#Tr.h2QtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtFQtG.tJ.BM#yE.Vt#fa.Vt.mC#HbaQ1#c2.vb#bh#bh.w4#c2#wG.mX.kx#Iz.cW#HS#CA.34.a3.bHQtFQtK.#6QtKQtKQtKQtKQtF.bHQtF.OF.Dz.mu.lq.Tx.kdaQ2.Vv#gX#xE#CA#CA#CA#CA#CA#CA#CA#CA#xE#xE.ciaQ3aQ4aQ5aQ6aQ7aQ7aQ8aQ9aR.aR##YMaRaaRbaRcaR.aRdaReaRfaRgaRhaRiaRiaRj.kl#UV.33#1i.w7.v4.w2.w2.w2.GZ#GRacXaRkaOkaRlaRmaRnaRoaRpaRqaRraRsaRtaRu.#y.au.x9.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.bO.#E#bg.Du#c4#c4#c4#c4#c4#c4#c4.w4#c4#c4.Du.w4.w4#c4#c4#c4#c4#c4#c4#c5#c4#c4#c4.L#.vb#bi.vc.3U.#F.#P.#EQtZ.gV.fX.fX.#Q.qZ#OW.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.v4.v4.fX.dI#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5anHarNaRvaRwanjab4adqab4aeZajiaRxaeJ.14.Jc.TqaFBaRyaRzaRAaRBaRCaRDaeH.Jd#7CagA#7CatzaREaRFaRGauVatzatz#wp.PYaRHaRIaRJaRKaRLaRMaRNaROaRPaRQaRRaRSaRTaRU#WW#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1aN6aRV#OZaRWaRXaRYaRZaR0aR1aR2aR3aR4ajc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K7.Oj.14#9paR5aR6.tCaR7a#N.#E.k6.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"ab4ab4ab4ab4ab4adqaeZaniaR8aR9aS.aqQapv.#P.cv.#R.k6.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.k6Qt..tmQt..bO.#Q.bW.vc.vb.Du.Du.Du.Du.Du.vc.c8.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aMaS#aSaaSb.r2QtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.sfabdaKY.bd#1A.Vm.yj#j5.h2.BM.YV.h6.ab.sQ.Dy.Xb.cgQtFQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.Ou.9F.BM.sW.Vt#tK.Vt#9O.OB.zo.#F.w4.w4.w4.v5.w4#wD#IH.LB.cl#rr.rv.bH.#7QtF.g8.cY#on.cW.h6.g8.cS.#6.#6QtKQtF.bH.nt.BJ.lq.ns.MZ.Tx.LB#Da#xE#xE#CA#CA#CA#CA#CA#CA#Hb#CA#PkaaeaScaSdaRiaSeaRiaRiaQ6aQ7aSfaPRaQ6aQ7aQ7aQ7aQ7aSgaQ6aRiaRiaSdaSe.un.kv.2kaSh#7V.v4.w2.w2.x9#GRaLdaSiaSjaSk.rXaSlaSmaSnaSoaSpaSqaSraSs.#z.av.v4.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4aBw.#F.vb#c4#c4#c4.w4.w4#c4#c4.Du#c4.Du.w4#c4#c4.Du.Du#c4#c4#c4#c4.w4.vb#bi#be.#E.gV.qZaBw#OW.w2.dh.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4#P#.#Q.#F#bg#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5avcawAaStaSuaSvaniaeZab4aeZajIaeKaeJ.14.Jc.JcalEaKjaSw#8qaSxaSyaSzaSA.JcalE.K8.TqalEaSB.Tp.Tq.Tq.Tq.Tq.TqalE.TpaE5ajk.VdaSCaSDaSEaSFaSGaSHaSIaQJaSJ#WXaN6#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1aSKaSLaSM#JYaSNaSOaywaSPaSQaSRaij#wp.Jc.Jc.Jc.Jc.Jc.MM.K8.K7.L..L..L..L..K7.K8.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14#p2aSSaSTaSUaSV.tCamI#bj.au.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"ab4ab4ab4ab4ab4ab4adqadqaeZaniaSWaSXaSY#KeQte.#F.aE.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4.v4.r..r..k6.bO.bW#bi.Du.Du.Du.Du.#T.c8.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.cv.awazeaSZaS0aS1#qs.koQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.e6.rv#e5auG#m5aS2.yj.OF.dw#Bd.YV.oDQtE.bH.Lj.tK.bGQtK.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtFQtG.BM.BM#IG.Oz.zJ.Vt.Vt#9HaS3.#T#bg.w4.vb.w4.fY.Fd.rs.op.#6#c8.Q#.epazU.TP#wK#xE#CA#CA#CA#Hb.YV.LB.h6.bH.#7QtKQtF.bH.j..tr.mw.Ty.qp#zA#CA#CA#xE#CA#CA#CA#CA#CA#xE#CA.hiaS4aS5aS6aRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaS7aS8.7P.TM#jIaIwaxk.w2.w2#GRaS9aT.aT#aTaaTbaTcaTdaTeaTfaTgaThaTi.GZ.br.k6.dh.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.aF.#T#c4#c4#c4#c4.Du.Du#c4#c4#c4#c4.w4#c4#c4#c4#c4#c4#c4.w4#bg.#T.#P.gV.qZ#OW.w2.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.w2.v4.GZ.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.w2aBw.c8.bW.#F#bi.w4#c4#c5#c5#c5#c5#c5#c5#c5#c5anH.DuaTjaOX#Bjanjab3ab4agPaeM.14aij.Jc.MM.TqaihaTkaTlaTmaTnaToaTpaTq#9q.K7.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Tqatz.JbaTraeHaeH#xhaTsaTtaTu#5MaTvaTwaTxaTy#JW#WY#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1aSKaTzaTA#JXaTBaTCaTDaTEaTFajc.MM.Jc.Jc.Jc.MM.K7.Oj.3R.RGaTGaTHaGbaG3aTq.YG.L..L..K7.Jc.Jc.Jc.Jc.Jc.14.14anSaMKaTIaTJaTKaTLa.uaTM.G1.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"ab4aeZab4ab4ab4ab4ab4ab4ab4adqab3ab2aTNaQV#Ke.vb.5Q.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.r..tm.c8.Hc.Du.Du.w4#r3.fX.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.cv.gV.c8aTOaTPaTQaTR.cSQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.sb.LBQtFaTS#HN#eYaTT.i1.k#.YT.TP.YV.72.bLaTU.r2.YV.k#.a4QtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF#P2.Vv.BM.tI.Vt.Vt.Vt.zJ.9V#Ov.#P.vb.vb.vb#bi#MWaTV.j2.kl.Q##gX#CA#HS#CA#CA#CA#CA#CA#CA#CA#CA.TQ#CA.YV.sfQtFQtKQtKQtF.rA.LA.rf.ke.ns.5V#Hb#CA#xE#CA#CA#CA#CA#xE#CA.mXaTWaTXaRhaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaTYaTZ.ck#xEaT0.w7.v4#GRacXaT1aT2aT3aTbaT4aT5aT6aT7#5zaT8aT9.w2.#z.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.fX#be#c4#c4#c4.Du#c4#c4#c4.Du#c4#c4#c4#c4.Du#c4.w4.w3.3U.#E.c8aBwQt..dh.v4.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4.v4.w2Qt..sq.fX.#E.w1.w4.Jk#c5#c5#c5#c5avcawA#NYaU.aU#aUaaSvaw8aeZaUb.K6aij.Jc.Jc.14aghaUcaUdaUeaUfaUgawxaGcaUhaih.K8.WV.Jc.Jc.Jc.Jc.Jc.K9#wpaUiaUjaUkaUlaNW#7BaSB#7BaNWaUmaUnaUoaJDaUpaUqaUr#2K#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#F1#8naN6aUsaUtaUuaUvaUw.L..K8.MM.Jc.14.L..OkaUxaUyaUz#P##I7#be#KzawxaUAaUBaUCaUD.14aihalE.K8.Jc.Jc.Jc.PZ.JdaUEajIaUFaUGaUHaUI.0q.EV.EV.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"ajeajiaeZaeZaeZaeZab4ab4ab4ab4adqab4aLkaUJaUKaUL.L#.aw.#A.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..dh.t1.vc.Du.w4.bW.fX.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.cv.aI#mR.NgaUM.r2QtEQtEQtEQtEQtEQtEQtEQtEQtE.nq.bL.h2.ep.j.#mTajQ#NN#8KQtKaGi#Z#.BM.Vv.Xg.oP.rm.Vv.Jy.cX.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.lw.YV#is#Hb.lD.7U#tK.Vt.9T#uC.Du#bi#bh.vb.w4.awaUN.gh.pMQtE#wK#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#HS.g5.#6QtFQtFQtF.bH.bH.2c.yk.kf#vC#GD#HS#xE#CA#CA#CA#xE#xE.W9aUOaUPaSdaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaUQ.TC.TM.h6#eQaURaLdaT1aUSaUTaTbaUUaUVaUWaUXaUYaUZ#3l.#A.#z.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4Qt..gV.vc.Du#c4.Du.w4.w4.w4#c4#c4.Du#c4#c4.w4.v5.3U.aw.aF#OW.w2.v4.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4.v4.v4aze.fX.w0#bg.Fr#c5#c5#c5#icanHawAavPaU0au1auXaU1aU2.Jdaij.Jc.JcaU3aU4aU5aU6aU7aiv#F2aDpaU8aU9aLWaih.K7.K7.K7.K8.Jc.TqaV.aV#aVaaN5aVaaPyaVbatz.TqalEagAaVcaVdaB1aVe#yt#HK#ww#SY#SZ#Vw#Jo#Jo#Jo#Jo#Jo#F1#F1#WY#F1aVfaVgaVh#P#aVi.WU.K7.14aVjaDQaVk.EV.EV.G0.v4.v4.v4.dfajPanHaVl#7yaVmaQRaVnaVo.14.Tp.Tq.14.14.14aeJaVpajCaVqaVraVsaVtaVuaVv#sB.EV.G0.dh.w2.w2.w2.w2.w2.w2",
+"aeJaikavDakGaVwaeLajiaeZab4adqab4ab4afaab2aOvaVxazK.dh.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.dh.c8.w1.v5.cv.o7.aw.aw.aw##j.aw.aw.aw.aw.aw.aw.gV#2H.DkaVyaVzQtF.koQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.tK.qE#Iz.j.aVA#idazq#de.cY.a3.0A.BM.Vv.BM.i4.a3.oD.BH.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.bH.7W.Vv.TP.bI.Vt.Vt#fa.Xh.kv#0a.#P.vb#bg#bh#bi.T7.VkaVBQtKaVC#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CAahl#CA.cW.#7QtFQtK.#6.#6QtKQtFQtE.LA.kf.Sb#by#QC#xE#CA#CA#xE#Hb.lD.HxaTXaVDaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaVDaVE.mU.3YaVF.G0aT1aVGaUTaVHaVIaVJaUWaVKaVLaVM#SQ.o1.#A.dh.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4Qt..cv.v5#c5#c4.vb.vb.w4.Du.Du.Du.Du.Du#bg.#T.awQtVQt..v4.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.w2.w2.v4.v4.v4.v4.v4.w2.#Q.#F.w4#c5#c5#c5#c5#F2aVN#KzaVOaVPankaTI.Jd.PZ.14aVQaVRa.m.peaAMaVS#Nw#c5avQaDpaFOaVT.Ok#tk.WW.K5.Tq#vraVUaVVaN5#Jo#JXaVWaVXaVY.Tq.Jc.JcalEatzaVZaV0aV1#xq#yu#HK#HK#xsaV2#S0#Jo#Jo#Jo#Jo#Jo#5C#JoaV3#ItaV4.dg.EWaV5.WWaV6aLWaV7aV8.v4.w2.w2.w2.w2.v4.v4.w2.gV#Lma#MaQTaV9.tCaW.aW#aWaaTqatz#wpaij.PZ.PZaijaeJaij.PZ.Tq.Oj.14aJvaWbaWc.EW.v4.w2.w2.w2.w2.w2",
+".Jc.14aeJaeJaeJaeJ.JeavDaKmaeZab5ab4ab4afaaniaWd#J9.G0.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.k6.au.#QQtf.gV.gV.aw.gV.cv.gV.gV.aw.aw.aw.aw.awaze.pVaWeaWfaWg.ugQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF#Iy.BM.72.bLaWh#vE#qd#NN#1X.eq.f8.BM.YV#jT#Iz#xA.TO.um.kw.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.ep.Vv.BM.lx.lD#tK.Vt.zJ#xD#YS.Jk.v5.vb.vb.w4.gVaWi.32.Qh.um#xE#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA.Vu.9V.koQtK.#6.g9.g9.g9.g9.g9QtK.h3.rm##L.lp#gN#gX#CA#xE#CA#yB.5UaWjaWkaSdaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaWl#qo#WlaWmaWnaWoaTaaWpaWqaWraWsaWtaWuaWv#RZ.o1.#A.dh.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4aze.bW#Iv#c5#c5#c5#c4.w4.vb.Du.Du.vb#bi.#P.#Q#OW.v4.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.bOQtVQtVQtV.bOQt..dh.v4.v4.v4.v4.v4#OW.gV#bi#c4#c5#c5#c5#F2awA#KzaTjakpaMK.TpaWwaWx.rW.fS.qc.pdaWyau6#c5#c5#icaDqavQaWzaSYaWAaWBaWCaWDaWEaWFaE9aOA#JoaWGaWH.Jb#wp.Jc.Jc.MM.TqaE5aWIaWJaWKaWL#wx#xr#xr#HK#ww#Jp#Vw#Jo#Jo#Jo#Jo#Jo#5C#JW#2KaWM.vJ.EWaWN#ocaib#sP#XD.w2.w2.w2.w2.w2.w2.w2.v4.r.aBw#bia#NaqSaqSaR7aWOaWPaWQaWR#9p.VdaH3aJw.PZ.14.Jc.Jc.Jc.Jc.K8.L..OjaWSaWT.v4.EV.x9.w2.w2.w2",
+".K7.Jc.K8.Jc.Jc.Jc.14aeJanSavFaeZadqab4adqab4ab5aWU#HA.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.k6.auQtb.c8.aw.c8.#P.gW.c8.gV.aw.aw.aw.aw.fX.aGaWVaWWaWX.r2.lGQtEQtEQtEQtEQtEQtEQtEQtE.cX.eq.Xb.0A.eh.JBaxV#bg#bi#bkaWY.TH.0A.BM#Ha.aY.i3.BM.XgQtGQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.a4.k##bz.YV.BM#wL.Vt.Vt.zJ.se.dvaWZ#id.Du.Du.Du.vcamyaW0#Hb.g8#Jw#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA.TF.#6.#6.g9.g9.#6.#6.#6.g9.g9.#6.nt.muaW1.mw.rz#QC#CA#Cx#Hb#QTaW2aW3aW4aRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaW5aW6auNaW7aW8aW9aX.aX#aXaaXbaXcaXdaXeaXf.o1.au.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.bW.vb#c5#c5#c5#c5#c5#c5#I8.w4.vc#a4QtV.w2.v4.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.dh.bOQtVQtVQtk.#Q.#Q.#QQtV.bO.dh.v4.v4.v4.v4.G0.dh#I7.3U.Du#c5#c5#c5#F2aVNaEFaXgaXhaXiaXjaH7#2A#PvaXkaXlali#c5#c5#c5#c5avcavQavcavcavQaDpaDpaMzaXmaXnaNL#WYaN5aXoaXp.Tq.Jc.Jc.Jc.Jc.MMatzaKjaXqa#EaXr#xr#HK#zs#xr#yu#xsaUr#Jo#F1#F1#Jo#Jo#5C#TRaXsaXt.#y#GQQtqaXuaXv.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..#Q#ica#MaqSaqSaQTaR7.tCaXwafsaXx.Vdaii.14.PZ.14.Tq.Jc.Jc.MM.Jc.L..TqaXyaXzaXA.ET.w2.w2",
+"aXBaXC.3R.K7.Oj.Oj.L..JdaeJasIagCab4ab4ab4adqab3aXDaXE.I9.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.k6.k6.bO.c8.v5.w4.#T.cv.gV##j.aw.aw.gV#EU#LcaXFaXG.nq.bHQtEQtEQtEQtEQtEQtEQtEQtEQtFQtG.YV.BM.oDQtGaXH#be#c5#bg.Fr#eZ.tK.BM.YV#jT#DN.g5#jT.YV.rB.bLQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.rB.Vv.BM.7T.lD#fa.Vt.zJaXI.Hz.3U.vb.w4.Du.vb.wZafG#9O.tKawf.Vu#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#Hb.7X.g9.g9.#6.#6.#6.g9.#6.#6.#6.g9.#6.rnaXJaXK.os.pN.TQ#CA#xE.TMaXLaXMaS7aRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaSdaXNaXOaXPaXQaXRaXSaXTaXUaXVaXWaXXaXY#SQ.o1.k6.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.dh.gV.v5#c5#c5#c5#c5#c5#c5#c4.dI.#EQtV.w2.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.w2.dh.v4.w2Qt.Qt..bOQtVQtk.#QQtVQtbQt.Qt.Qt.Qt..dh.r..v4.bO.#P.w4#c5#c5#c5#F2aVNaXZaX0aX1akFaX2aX3au5#xK#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5avQ#rjaX4aX5aNPaNLaSFaX6#7B.K9.Jc.Jc.Jc.Jc.MM.TqauiaIcaX7aX8#W##wx#xr#zs#xr#wx#Z7aX9#yr#XI#Jo#Jo#F1aV3aY.aY#aYaQtj.G0Qt..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..v4.#EarOaqSaqSaqSaqSaQT.tCag0aYbaYc#p2aijaOc.Vf.14.Tq.Jc.Jc.Jc.14.OjaYdaYeaYf.MK.v4",
+"aYgaJpaYhaYiaYjaYkaYlaXB.14#wpaYmab3ab4ab4ab4afaaYnaYoazKQt..au.v4.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..k6QtV.#T.w4.v5.bW.c8.gV.aw.awaYpaAVaYq#daaYr.cS.lGQtEQtEQtEQtEQtEQtEQtE.cS.h3#bz.YV#q##IyaYs#be#c4#c5.w4#bi#Ma.LB.Vv.Vv.#8#gC.9V.Vv.Vv.TP.sf.koQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.h6.Xb.YV.BM.nx.Vt.Vt.Vt.Xh#bs#Ez.#T.vb.vb.Du.#F.zx.0x#RsaGi#0y#CA#CA#CA#CA#CA#CA#CA#CA#xE#CA.mD.g9.g9.g9.#6.g9.#6.#6.#6.g9.#6.#6.#6.r7aYtaYuaYv.kg#gC#HS#xE#Hb.5U.bwaWlaSdaRiaRiaRiaRiaRiaRiaRiaRiaRiaRiaSdaYwaYxaYyaYzaYAaYBaYCaYDaYEaYFaYGaYHaYI.n2.bt.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.#Q#bi#c4#c5#c5#c5#c5#c4#bg.#E.qZ.w2.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.x9.x9.v4.v4.v4.v4.v4.w2Qt.QtaQtVQtV.bOQtVQtVQtaQt..v4.v4.r.#OW#a4#bg#c5#c5#c5#F2awzaYJaYKaLyaYLaYManH#F2#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#ic.va#c4aYNaYOaYPaQsaREatz.MM.Jc.Jc.Jc.Jc.MM.Tq#7BaYQaYRa#J#Z7#yo#yu#Ag#xr#wxaYSaNaaTAaN6#F1#Jo#F1#XIaYTaYUaYV.#S.dg.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.G1Qt.#bi#8paqSaqSaqSaqSaQT.tCaW.aYW.0naii.VfaLW.WU.14.Tq.Jc.Jc.14aijaYXaYYaYZaV8",
+"avQ#NkaJt.I9#yM#Do.dZaV7aY0aY1aY2aY3aSvaniab3ab4aeZaY4aY5aI#.qZ.k6.v4.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.k6.fX#bi.v.Qtp.gV.gV.gVaBw.eHaY6aY7aY8#Ao.koQtEQtEQtEQtEQtEQtEQtE.bHQtF.3Y.Vv.BM.tKaY9.16.w4#c5#c5#JR#bjalc.eh#LE.YV.Y1.pM.Vv.Vv.Vv.Vv.h2#mG.koQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.k##by.Vv.BM.YW.0F#tK.Vt.zJ.Vt#D#.bW.Du.w4.Du.w4.3U#gw#QT.cT.sf#CA#xE#CA#CA#CA#CA#CA#Cx#CA.BMQtF.g9.#6.#6.g9.g9.g9.g9.#6.#6.#6.g9.g9.otaSdaS7aZ..OC#gM#xA.TQ#xEQtJ.7V.BIaZ#aSdaRiaRiaRiaRiaRiaRiaRiaS6aZaaZbaZcaZdaZeaZfaZgaZhaZiaZjaZkadjaZlaZmaZnaZoaZpa.2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4aBw.#T#c4#c5#c5#c5#c5.w4#bj.aFQt..v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4.w2Qt..bOQtV.#Q.#QQtV.bOaBw.qZ.w2.r..w2.bW.vb#c5#c5#c5aDqaZqaZraZsaii#uraWBaDpavQ#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4Qt6aZtaZu#5MaZvauV#p2.MM.Jc.Jc.Jc.Jc.MM.TqajmaZwaYRaNL#4L#UH#HK#xr#zs#ytaZxaZyaZz#XI#F1#Jo#Jo#WW#5BaZAaZBaZC.#y.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..o7aX4aqSaqSaqSa#Ma#NaHeaZDaZEaFe.0o.14.WU.Vfaij.14.Jc.Jc.14aZFaVqajIaZG",
+".#T.Fr#c4.t1.#RQt..#wQt.#Do#DoQt.aZHaZIaZJavHaSvaLkaLkaOVaZK.bk.c8Qt..au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.w2.w2.v4.v4.w2.w2.w2.v4.v4.w2#c2#bc.v5#c2.gV.fX.aGaZLactaZMaZN.cSQtEQtEQtEQtEQtEQtEQtE.fdQtF.sf.Vv.YV.oD.wf#XW#bg#c5#c5#c5#c4#bjaZOaZP.TQ#gN.h6#jT.Vv.Vv.Vv.Vv.Xb.3Y.7X.cX.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.oP.TH.Vv.YV.qy.9P.7U.Vt.zJanp.7H#be.w4#c4#c4#c4aEo#yD#j2.uB.j#.Vv#CA#CA#CA#CA#CA#CA#CA#wK.g8.nt.#6.#6.#6.g9.g9.Qh.lG.g9.#6.#6.#6.2caZQaW5aZR.kf.mw.pu.lr.55#CA#jT#wL.jl.LiaUQaSdaRiaRiaRiaRiaS6aZaaZSaZTaZU#QbaZVaZWaZXaZYaZZaaxaZ0aZ1aZ2#rs#rs.kf.qoaIqQt9.aN.r..v4.w2.w2.w2.v4.v4.w2.#E.w4#c5#c5#c5#c4.v5.aw#OW.v4.v4.v4.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.v4.v4.v4.w2.bOQtV.#QQtV.cuQtW.aF.bOQt..v4Qta.#P.w4#c5#c5#c4aZ3aZ4aZ5aZ6aZ7aZ8aZ9aDqaDq#ic#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#Lma0.a0#aNLa0aa0b.YGalE.MM.Jc.Jc.Jc.Jc.MM.Tqajka0ca0daNP#Wa#Vv#xs#yu#xr#xr#Zpa0ea0fa0gaN6aOA#Jo#F1aQHa0ha0i#7d.dg.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..w2#be#5Ka#NaqR#c4#c4#c4aDpaU8a0jaih.Tq.14.PY.14.Jc.Jc.Jc.14aeJalGa0k",
+"Qt..cv.w4#c4.bWQt.Qtb.miQtbQtbQtb.w2.EV.x9a0laHHa0mab5a0na0oazK.ey.cv.cu.au.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.w2.w2.v4.v4.w2.w2.w2.v4.v4.w2.w2.w2.w2.w2.v4.r.QtV.vc.w4.#Ta0p#Hz.psa0q.S##TV.bw.koQtEQtEQtEQtEQtEQtEQtE.a4.kl.0A.Vv.YVaQ2aIr#be#c5#c5#c5#c5#ic#bjaiKa0r#sza0s.BM.Vv.Vv.Vv.Vv.Vv.Vv.BM.dw.bH.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF#mG.9F.YV.33.tI.Vt.Vt.Vt#xD.hW#XW.vc.w4.Du#c4.w4.Jk#3JauN.um#Iy#CA#CA#CA#CA#CA#CA#zB.rv.RS.g9.#6.#6.#6.nt.rv#Hb.ow.qr.g9.a0QtK.2ea0ta0u.kg.mw.mw.kf.kg.vk.Qb.BM.TO#sM.jla0va0waSdaRiaS6a0xa0ya0za0Aa0Ba0Ca0Da0Ea0Fa0GaaxabUaEda0Ha0Ia0J.W6.mv.mw.kf.psa0Ka0L.9C#I7.Tm.w2.v4.au.qZ#bi#c5#c5#c5#c4.dH.c8.dh.k6.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4Qt.QtV.#Q.#QQtVQtVQtk.#QQtVQtV.bO.aF#bj#c4#c5#c5anHaFia0M#vr.0o.K5a0Na0OavQ#F2#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.v.a0PaNL#WYa0aa0Q.WUatz.MM.Jc.Jc.Jc.Jc.MM.TqaFBa0R#6D#WY#Jo#Vw#UJ#zt#xr#HLaJEa0Sa0Ta0UaSK#F1#Jo#XI#Joa0Va0WQtR.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..aF.dI#c4#c4#c5#c5#c5anH.Fra0Xaii.K8.Jc.Jc.Jc.Jc.Jc.Jc.PZ.JdajG",
+"Qt..dhQtk#bi#ic.#PQtb.bOQtaQtb.bOQtbQtb.bO#OWa0Y.w4a0Za00adra01aFZ.cv.aw.c8Qtq.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.MK.EWa0pa02a03a04a05a06a07a08a09a1.a1.a1#.fd.37QtEQtEQtE.bHQtF.sf.Vv#jT##Ea1a#bi#c4#c5#c5#c5#c5#c5#bj#YMa1b.BM.oD.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Xf.chQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.a4.k#.0A.34.BM.TN.zJ.Vt.Vt.Vt.Vta1c#c2.Du#c4#c4.Du#ida1d#HS.Ou.k##jT#xE#CA#CA#CA#xE.57.g9.g9.#6.#6.#6.nt.cg.53#0y.#6.g9.#6.#6##E#lia1e.vk.kf.kg.kg.W6.ke.mw.ns.r7#bB.DP.tI.7I.FuaUPa0xa1fa1ga1ha1ia0Ca1ja1ka1la.fa1maay#K7agja1na1oa1p.mv.mw.mw.mw.kf.kf.tw.tva1qa1r.0Na.2#P#.bW.Du#c5#c5#c4.vc.c8.dh#EU.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.w2.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.Qt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.c8.vc#ic#c5avQ#F2aZ8auV.L.aih.14aEC#c4aDq#c5#c5#c5#c5#c5#c5#c5#c5#c5.aja1sa1t#35#3p#5Ma1uaNW#7C.Tq.Jc.Jc.Jc.Jc.MMatza1va1wa#E#F1#Jo#JoaUr#xq#yu#4KaJEa1x#OZa1y#JoaN6#XI#JYaOKaN#a1z.fZ.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.w2#id#c4#c5#c5#c5#I8avQ#rja1A.PZalE.MM.Jc.Jc.Jc.Jc.JcagqaVw",
+".#RQt..#RQta.#P.Du.#P.bO.bOQtbQtaQtbQtbQtbQtbQta#Nka1BaSYa1Ca1Da1E.j0.gV.aw.gVQta.au.v4.v4.w2.w2.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.EVa1Fa1Ga1Ha1IaOVaR8a1Ja1Ka1La1Ma1Na1Oa1Pa1Qa1RQtEa1Sa1..fd.bHQtE.bH.lG.oD#LE.h6#J0#7G.w4#c5#c5#c5#c5#c5#c5#bi.#pa1T#LE.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv#e1.cYQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF#P2.Vv#bt.Vv.0v.sd.Vt.zJa1Ua1V.wZ#c4.w4#c4.Du.w4.16#Fq#xE.ko.tK#CA#CA#CA#CA#P2QtK.g9.#6.#6.#6.qr.ow.9V#HS#c8.nt.#6.#6.oPa1Wa1e.zM.kg.pq.kj.yk.pt.j..pt.i4.kg.X#.Sda1Xa1Ya1Za10a11a12a13a14a15a16a17aDCabV#HH#K7aHma18a19#rs.W6.mv.mw.mw.kf.kf.mw.Tz.Ls.wf.JV.Lta1q.J8.wZ#c4#c5#c4#bi.fX.dh.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.w2Qt..v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.bW.w4#c5#c4aDpa2.aFd.K7.K8#p2a2#aU8aDp#c4#c5#c5#c5#c5#c5#c5#c5.Fr.Frasla2aa2ba2ca2da2ea2f.K9#7CalE.K9.Jc.Jc.Tqa2ga2h#W.#WW#Jo#Jo#Jo#S0#Rh#yu#xr#ww#FSa2ia2ja2k#JWa2la2m#N9a2na2oQtS.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.c8.w4#c5#c5#c5#c5avcanHa2paFd.K7.K9.Jc.Jc.Jc.JcaijanS",
+".G0.tmQt.Qt.QtbQtZ.w4.#P.bO.bOQtbQtbQtb.bOQtaQtbQtV.#T#IvaqQa2qa2r.#B.o7.aw.aw.aw.#Q.dh.v4.v4.w2.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.Oga2sa2taSva0kaniaLkaeZaeZadpa2ua2va2wa2va2xa2ya2za2Aa2Ba2Ca2Da2Ea2FQtE.kw.cW.9V.3Ya2GapX.w1#c5#c5#c5#c5#c5#c5#c5#bg.w4#Lc#jT#jT.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.cWQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.kw.ep.Vv.YV.BM#Cz.Vt.Vt.zJ#f#a2H.16.vb.w4#c4.Du.Du.#Ta2IaC..lw#gF#CA#Cx#CA.vt.cS.g9.#6.g9.#6.g9.k##Ha.TQ.aY#gC.g9.#6QtK.Xna2J#rn.mu.qq.j.QtE.h3.h3QtEQtF#qs.yk.kf.MZa2Ka2La2Ma2Na2Oa2Pa2Qa2Ra2Sa2Ta2U#HHaHmalAa2Va2Wa2Xa1p.ts.mv.mw.mw.kf.kg.qo.sN.wf.wf.wf.vj.Lr.BJ#Gc.Fp#be.vb.aw.w2.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.w2.w2.v4.v4.w2.w2.w2.w2.v4#Hz.#y.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r.Qt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtaQtk.vc#c5avcaZqa2YaZsaih.K8aih#rha2Z#F2anHavc#c5#c5#c5#c5#c5#c5.Fr.Fr.va.aj.aj.ajaxea20a21a22a23atz#7CalEaeHa24a25#WY#WY#Jo#3p#3p#Jo#WY#HJ#ww#Gya26a27aLE#A5aOKa28#A5#A5#Q0a29.dh.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.aBw#bi#c5#c5#c5#c5#Iva3.a3#.0n.K7.MM.Jc.Jc.JcaijalH",
+"aV5a3aaJq#vQ.w2#yM.st.dI#a4.bOQtbQtbQtbQtaQtbQtbQtb.bO.#T.w4aUL#tm.#P.gV.gV.aw.aw.aw.gVaBw.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.dh.Oga3ba3caYnab4ab4adqab4ab4ab4ab4aeZaeZaeZaeZaeZagPaUFa3da3ea3fa3ga3ha3ia2Ea3j.bH.h2.TOatka3k#bg.w4#c5#c5#c5#c5#c5#c5#c5.w4#bia3l.BM#LE.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.dwQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.ab.g8.BM.Vv.BM.ck.Vt.Vt.Vt.Vt.scaf3#bj#c4.w4.Du#c4#bj#p8.34.dw#Rs#xE#CA.TP.ko.nt.#6.g9.#6.nt.rA#0y#HS#Hb.ch.nt.g9QtK.OL.Ty#M2.Tx.mr.h3.clQtEQtEQtEQtE.bH.#6QtFa3m.YPa3na3oa3pa3qa3ra3sa3taZkaay#HHanLaHma18a3uaZma3va3w.mw.mw.mw.mw.kf.kf.sP.vj.wf.wf.wf.wf.vj.tv.kc.2ha3xa3y#2H.r..v4.w2.w2.v4.v4.v4.r..G0.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.v4.v4.v4.v4.v4.v4.v4.w2.w2.w2#GS#Caa3za3A.dV.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.axk#7V#7V.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.dhQta.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.#P#c4#icaDpa3Ba3C#9q.K8alEaH3a3Da3Ea3FavQ#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.Fr.Fr.DuaDpa3Ga3Ha3Ia3Ja3KaVba3LaN5#WYaOA#WYa3Ma3Na3Oa3Pa3QaN6#ZqaN6a3Ra3Sa3T#N9#N9#Ad.aV#Lwa.#a3UQtS.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.G1.w2#id#c4#c5#c5.3U.v4.EWa3V.Vf.K7.MM.Jc.JcaeJakG",
+".Jca3WaTqa3X.9zaWba3Ya3Z.vca30#DoQtb.miQtaQtbQtaQtbQtbQtg.#F.Du#rj.vb.#F.gV.gV.aw.aw.aw.aw.aF.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.dh.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2#GSa31a32aLkafaab4ab4ab4aeZaeZajI#Bka33a34a34ah3#Bkam.akSa35.JdasHa36a37.Jca38a39.a4#F4#jT.pM.Vma4.#3V#bi#c5#c5#c5#c5#c5#c5#c5#c4#id#C9.h2.53.YV.Vv.Vv.Vv.Vv.Vv.34.Vv.Vv.a3QtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.a4.bH.aY.34#is.M6.cm#fa.Vt.zJaXI#9N#id#c4#c4#c4#c4.#Pavxa4#.aY#CA#CA.Vv.bL.nt.#6.#6.#6.g9.oP#by#CA#HS.#8.nt.g9.#6.pt.kd.ns.HK.bHQtE.bH.bHQtEQtEQtEQtE.bH.aZa4aa4ba4ca4da4ea4fa4g#FeaZkaay#JNaHmaHma4ha4ia4ja4k#GHaXKa4l.mw.mw.mw.kf.kh#fe.wf.wf.wf.wf.wf.wf.r7.kh.kg.kf#mC.rha4ma.2a.2a.2a.2.TmQt.a4na4o#m8.dha.2axk.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4#OW.aw#qd.aw.w0.w2.r..w2.TmaURa4pa4qa4ra4sa4t#5u.au.w2.w2.w2.x9.r..r..Tma.2#7Va4ua.2#m8.gGav9a4va.Fa.2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r.Qt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.cv.w4#c5aDpa4wa4xaih.K8.K8.0o.K5aGcaSY#F2#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.Fr.aja4ya4za0da4Aa4BaN5#F1#Wa#WYa4Ca4Da0..vaanJasla4E#F1#WYaQH#8oa4Fa3T#A5.aV.aV#A5#Qha4GQtR.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.cv.L##c5.aw.r..v4.EWa4HaFd.K7.MM.Jc.PZanS",
+".K8.K8.K7.L..Oj.Oj.K9.Ok.0pa4Ia4JQtaa4KQt..bOQtbQtbQtaQtbQtbQtp.Du.Du.v.#be.gV.gV.aw.aw.aw.aw.gV.qZ.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.dh.sq.fX.fXQtVaze.dh.v4.v4.v4.w2.w2.w2.w2.w2.G0.dha4LaYnaeZaeZab4aeZab5a4Ma4Nail.Je.Jd.Jc.Jc.JcahSanSaeJaeJ.14.Jc.Vdagha37a4Oa4Pa4Q.fd.bG#jT.57.VmaCR##t#be#c4#c5#c5#c5#c5#c5#c5#c4a.Ra4R.eh#gE.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.bGQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.72.Vv.YV.BM.kn.Vt.Vt.Vt#xDa4S#c1#bg#c4.Du.w4.Jka4T.lF.YV#CA#Hb.h6.nt.g9.#6.#6.qrQtF#by#CA#CA.tK#gC.nt.#6.RS.ki.pq.ln.k#QtEQtKQtK.bHQtEQtEQtE.fda4Ua4Va4Wa4Xa4ea4Ya4Za40a1m#HH#ImamL#K7a41a42a43a44acv.ty.kgaS8a2J.mw.mw.kf.pq.sO.wf.wf.wf.wf.wf.OT.kc##B.kf.kf.kf.kf.qo.lua45a46a47a48a49.RUa4ka5.a5#a5aa5ba5c.r.axk.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.G1.v4.fYazNaB7a5daqSaqSa#M#2baBw.w7a5ea5fa5g#eHa5ha5ia5jazs.#y.w2.v4axkav..5Q#doa5k.hJ#yCa5l#eUa5m.uBaC.#0xa5na5oa.2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bOQtW.dI#c5aDqaSYa5p.K7.Tq.MM.Tq#vra5q#Kz#F2#c5#c5#c5#c5#c5#c4.w4.w4.w4#c4#c4#c5#c5.Fr.aja5ra5s#WY#5C#F1#F1aNLa5ta0.#c4#c5#c5.Fr.Dua5u#Sb#3p#6sa5va5w#WO#KC#Lw#MD#C3#70.#y.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..qZ#bg.gV.r..v4.v4.EWa5x.0n.K7.MM.JcauV",
+".Jc.Jc.Jc.Jc.Jc.Jc.Jc.K7.L..L.aJwaTGa5yaJqa5zQtbQtb.bOQtb.bOQta.#T.Du.Du.Du.vc.aw.gV.gV.aw.aw.gV.gV.bW.cv#OW.v4.v4.v4.w2.w2.w2.w2.w2.v4.k6.#Q.aw.aw.aw.aw.wZ.t1.qZ#OW.w2.v4.v4.v4.w2.w2.MKa5AadfakTalGaipaina5Baio.14aeJ.PZ.14.14.14.14.14.14.14.Jc.14agha36a5Ca5Da5Ea5Fa5G.cS.bHQtF.sf.YU.mD.sMa5H#Wi#bc.w4#c5#c5#c5#c5#c5#c5#c4#id#4yaa##CA.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.a3QtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtK.RR.Vv.Vv.TP.9I.Vt.Vt.Vt.se.54auG#be.Du.Du#id#47.dwQtF.0A#CA#gN.g9.g9##E#gC.nt.ch#bz#CA#CA.9FQtF.nt.#6.j..lp.Qc.2c.h3.bHQtK.#6.bHQtE.fda4Ua5Ia5Ja5Ka5La5Ma5Na5Oa5Paax#HH#L3aHmamLa5Qa5R.OCa5S.kg.lp.uo.2c.ouaXKa2J.mw.kf.mw.rn.vj.wf.wf.wf.wf.wf.rj.kg.kg.kf.qo.r7.2h.vj.wf.Lt.Lt.BJ.36.36a5T.BJa5U.BJ.Lt.36.Ql#0U##s.ah.Tm.w2.w2.w2.w2.w2.w2.v4.v4.v4aze.qZ.#EaqRaqSaqSaqSaqSaqSaqSa#Ma5V#uD#cXa5Wa5X.#Ka5Ya5Za50a51.dh.r.axka52.gF.s#a53a54#60.9V#Wla55aC.#T.#HS#CA#xE#0x#2h.#Q.Tm.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.auQt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.#T#c4#F2#c4a56.PY.K7.MM.K8#p2a57aGe#c4#c5#c5.w4#m8QtW.aF.#Q.#Q.aFQtW.cv.#P.vc.vb.aja5u#Sb#WY#Jo#Jo#4Ya58a0.#c4#c5#c5#c5#c4anJa2b#F1aB0#RY.o7a59a6.#C3a6#a6a.ah.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..sq.qZ.v4aze.v4.v4.EVa6b.PZ.K8.MM.Jc",
+".Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K8.L..Oja6ca6d.aK.tm.bOQtbQtbQtbQt#Qt2.Du.v#.Du#bg.bW.c8.gV.aw.aw.gV.gV.bW.#T#bj.fX.w2.v4.v4.v4.w2.w2.r.Qt..cv.aw.aw.aw.aw.aw.aw.aw.aw.gV.fXaBw.w2.v4.v4a6e#Gja6fajKaeJaeJ.PYaeJaij.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Tqa6ga6ha6ia6ja6ka6l.OGa6ma2E.OGQtEQtE.bH.cX.eq.9F.0A.wg.rga6n#c1#bi#c5#c5#c5#c5#c5#c5#c5#bj#NNa6o#CA.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.sf.kw.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.e6.Vv.Vv.BM.Vu.lD#tK.Vt.vh.2aa6p#id.w4.w4.w4#2h#onQtK.3Y.tK.#6.g9.g9.g9.nt.qi.9V#CA#xE#CA#gN.nt.g9.2c.r6.Dx.oPQtE.bH.#6.nt.cXa4U#T3a6qa6ra6sa6ta6ua6va6wa6xaaxaay#wBa6y#HGa6za6Aa1ea6B.W6.rh.rm.mr.#6.Y6.Txa6Ca4l.mw.kf.kf.rk.vj.wf.wf.wf.vj.kc.kg.mw.ki.mt.vj.wf.wf.wf.wf.wf.wf.wf.wf.vj.wf.wf.wf.wf.wf.JK.wf.36.vja6Da6Eaxk.v4.w2.w2.v4.v4.v4.p5#J6#9na#MaqSaqSaqSaqSaqSaqSa5da4Da6F#c0a6G#0xa6Ha6Ia6Ja6Ka6La6Ma6Na6O.tm.m6a6P.2ra6Q.TN#gX#Da#CA#xE#Hb#xE.lx#xE#xE#xE#xE#1..Ln#Ne#XD.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.dh.w2.dh.v4.v4.v4.w2.w2.x9.x9.w2.w2.w2.w2.v4.au.dhQtVQtk.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.bW#Zw#ica6Ra2ZaZs.0o.K8aVjaInaGdavQ.w4.3U.t1.bOQtVQtVQtVQtVQtVQtVQtVQtVQtV.aF.aL.bs#4N#WY#WY#JoaNLa6S.v##c4#c5#c5#c5#c5.FranJa6Ta6U#JkQtX.#ya6Va6Wa6XQtS.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4#zw.DH.r..v4.EVa6Y#9q.K8.MM",
+".Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.Jc.L..K7a6ZaV7#DoQt.QtbQtaQtbQta.#T.Du#rj.Du.w4.#F.gV.gV.aw.aw.aw.gV.gV.w0.w4.vb.#F.#Q.w2.v4.v4.r.aze.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.fX#OW.EUa60a61aeJ.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14a62a63a64a65a66.qHa67a68QtEQtEQtEQtEQtEQtEQtE.bHQtF.um.TP#jT#j5.mtaAm#jQ#be#c4#c5#c5#c5#c5#c5#c5#bj##vaPO#CA.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.uB.QhQtF.bHQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtEQtK.e6.Vv.Vv.YV.YV.mC.7U.Vt.sd#EGa69#r3#rj#be#3I.mx.bH.bH.epQtF.g9.#6.g9.g9.ep.53#CA#xE#CA#e1#qs.g9.qr.BJ#mDQtEQtE.koQtF.qra7.a7#a7aa7ba5La7ca7da7ea7fa7gagk#HH#wBa7ha7ia7ja7k.kfa7laVD.OD.ln.g9QtK.#6.g9.oP.36a7ma7n.mw.kf.kg.r7.wf.wf.wf.vj.ot.kf.ri.tv.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.BJ.Sb.Lt.Dza7oa6E#B9.w2.v4.v4.o7asla#MaqSaqSaqSaqSaqSaqSa#MaqS#6ya7pa7qa7r#2v#2v#xEa7sa7ta7u.#La7va7wa7x.#c#Tz#zB#KO.TQ#xE#xE#xE#xE#xE.Vu#xE#xE#CA.7T#Hb.lx.lx#xEa7ya7z.n9.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.dh.bOQtaQt..w2.dh.v4.v4.dh.v4.v4.v4.v4.v4.dhQt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.c8#bg#icaDpaWBa7A.14.0o.YGa7BacQQtV.bOQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtVQta.aFaBj#3p#5CaFEaCAa7C.va#c5#c5#c5#c5#c5.Fr.Duasl#c4#c4.#P.dhQth.dg.x9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4azeazN#F2#OW#Hz.G0a7D.K7.L.",
+".Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K8.OjaWEa7E#OWa5z.ai.bOQtb.bO.P2aDq#ic#c5#c4.#E.fX.aw.aw.aw.aw.gV.gV.aw#NW#bc.vb.v5.w0.cv#I7.bO.gV.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gVa30a7FaeJ.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K8.Oj#wqa7Ga7Ha7Ia7Ja7Ka7L.r2.koQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.g8.Xg.53.g5.wf#igaa.#be#c4#c5#c5#c5#c5#c5#c5#zw#Ut.HB#gE.Vv.YV.Vv.Vv.Vv.Vv.34.Vv.BMQtG.cX.fdQtEQtEQtEQtEQtEQtEQtEQtEQtEQtE.bHQtK#Iy.Vv.Vv.YV.TP#QT.Vt.Vt#tK.7Va7M#bg#rj.bWamF#IIQtFQtF.g9.qr.#6.g9.#6.rB#CA#CA#Cx#CA.Vv.ch.qr.g9.ln.OF.h3QtEQtE#T3.uha7Na7Oa7Pa7Qa7Ra7Sa7Ta7Ua7gayA#F0aDAa7Va7ia7Wa7X.2ca7Ya7Za70a71.g9QtK.#6.g9.#6.#6QtK.XnaSda72.mw.kg.mw.tw.wf.wf.wf.tt.pq.kj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.wf.BJa73.17aoAa5..Dza74Qt..Tm.bO#J6a#MaqSaqSaqSaqSaqSaqSa#MaqS#8p#Sf#bda75a76aC.#CA#Cx#CAahla77a78a79a8..iTa8#.kg.Vm.wn#CA#Da#gX#xE#Hb#xE#CA#gX#gX#wK.pM#Db#CA#xE#xEa8aa8b.#M.k8#Ne.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.au.bO.#Q.#QQtVQtVQtVQtaQta.bOQt.Qt..bO.sqQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bOQtV#NW#c5aDqaDqa8ca8d.Vfa8eawB.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q#I7Qtaa8faCN#4Ya0ga8ga8hQt6#c5#c5#c5#c5#c5#c5.Fr.Fr.Fr#c4#c4#uHa.R.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4aliatO.w0.G0.I9aYiaYl",
+"aijauV.14.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.L..PZaKJa8i.s7a5zaBKa8ja6da8k#rjavQ#c5#bi#OW.fX.aw.aw.aw.aw.aw.gV.gV.gV.aw#c2#bj.vb#c4.w4.#E.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gV.sta8lalE.K8.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K8.L..Vfa8ma8na8oa8pa8qa8r#B1.g0a8s.r2.koQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.cl.Xf.TO.3Y.yk.qp#9w#bg.w4#c5#c5#c5#c5#c5#c5#bg#bga8t.YV#LE.YV.Vv.Vv.Vv.Vv.Vv.Vv.Xg.chQtFQtEQtE.bH.bH.bHQtEQtEQtEQtEQtEQtF.cl.0A.34.Vv.Vv.BM.TN.sd#tK.Vt.7V.HM.#p#id.17#6O.9F.rA.g9.nt.g9.nt.rA.h2.7T#CA#CA#CA#Hb.um.nt.nt##F.g9QtE.bH#T3.h7a8ua8valra8wa8xa8ya8za8Aa7g#K7#F0aDAa7Va8Ba8Ca8Da8Ea8F.tva8Ga70a8H.kk.#6.g9.#6.#6.#6.#6QtK.i8aSe.x#.mw.OC.lu.wf.wf.wf.tv.ki.tv.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw#8I.16#be#be.#kaIq.Xaa8I.fXavSaqSaqSaqSaqSaqSaqSaqSaqSa#MarO#c4#id.P5#0h#2v#xE#CA#CA#CA#sza8Ja6Ha8Ka5n.X#.ns.kf.lr#IB#G8#DN.YS#IF#UV#xE.mD.sJ.7O#rA.ps.rn#Jw.TQ#xEa8La8Ma8N.b1a8Oa8P.dh.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r..w2QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.#P.Du#c5aDqanHaFOa8Q#IvQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#u#Uua8R#TRa8Sa8Ta8UQte#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#ica#N#bh.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.##jarO#ic.bO.Og#sP",
+"aKnam#air.14aeJ.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.K8.Oj.WUaYla8Va8Wa8X.K7.Tqa8YaMAa3F#c4.bW.k6aBw.gV.aw.aw.aw.aw#m8.ey.gV.gV.gV.fY.#P#bg.w0.fX.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.bka8ZaUhaVj.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K9.K7.Tqa80a81.Oga82.ora83#Aua84a85.e8#B1aWX#Ao.koQtEQtEQtEQtEQtEQtEQtEQtEQtEQtF.bH.mD#jT.TP.TC.rha86.w4.w4#c5#c5#c5#c5#c5#c5.w4.#Ta87.mx#Hb.YV.Vv.Vv.Vv.Vv.Vv.Vv.3Y.koQtFQtE.bH.bH.bHQtEQtEQtE.bH.bH.ch.tK.Vv.Vv.Vv.Vv.YV.YU#EG.Vt.Vt.7V#rG.al.gV#9Z.5U.9F.cl.nt.g9.nt.ow#gF#HS#CA#CA#CA#xEawf.g9.2c.oq.g9QtEa4U.era88a89a9.a9#a9aa9ba9ca9da9e#K7#F0aHma7ha9fa9ga9h#F7a8E.i9.ODaS8a70a8#.kkQtK.g9.#6.#6.#6.#6.#6QtF#IxaSe.os.mw.kg.lp.wf.wf.vj.kj.tv.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.wf.BJa9i#bj.w4#c5#c5#bi##ta9ja9k#8paqSaqSaqSaqSaqSaqSaqSa#MaqSarO#c4#qd#1Da9l#1.#xE#CA#CA#CA#xE#CA#QCaKX.Jp#1B.kf.kf.ns.kf.kf.ns.zM.MZ.tu.W8.Dxa9m.lt.lt.kg.kf.ns.Dx.Xg#Wl#zBahla9na9oa9pa9qaxf.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtVQtVQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bOQtW.v5#c5#icavQavQ#c5.#FQt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#uQtVa9r#Joa9sa9ta9u.vH#NW#c4#c5#c5#c5#c5#c5#c5#c5.w4#ETaqS#XxaBw.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.dh.dfaX4aB7.DH.DH",
+"aeZaeZagCaU2aKnahTaeJ.PZ.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K8.L..Oj.Oj.L..Jc.K8.OjaTqa4IaSYarM#I7.au.w2.fX.aw.aw.aw.aw.aw.aw.aw.aw.gV.gV.gV.aw.gV.gV.aw.aw.aw.aw.aw.aw.aw.aw.awa9va9w#vr.K9.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K8.L.aZsa9x.ET.EU.x9.Tm.cqa9ya9z#HZ.li.Ly.e8#D2#TV.cS.pNQtEQtEQtEQtEQtEQtEQtEQtEQtE.ab.bH.TH.Vv.BM.pN.r7a9A#c5#bc#c5#c5#c5#c5#c5#c5#c4#yz#ZCa9B#LF.Vv.YV.Vv.Vv.Vv.Vv.Vv.Ou.bH.bH.lG.bH.bHQtEQtEQtEQtF.g8#bz.Vv.Vv.Vv.Vv.Vv.YV.BM.Vw.vh.zJ.5U.lEa9Ca9D.yl#T..sf.nt.g9.ntQtG#II#CA#xEahl#CA#CA.bG#Uk.g9.#6.cXa4U.koa9Ea9FaNna9Ga9Ha9Ia9Ja9Ka9L#JN#wBa9Ma9Ma9Na9Oa9Pa9Q#j5.#6.qq.mwa9RaS8.zK.2c.#6.g9.#6.#6.#6.#6.#6.g9QtFa1WaXK.pu.kf.kg.r8.wf.wf.vj.vj.Lr.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.BJa9S.w4#JR#c5#c5#c4#c5#5GaB7aB7aqSaqSaqSaqSaqSaqSaqSa#Ma5d#5K#c4#bj.Foa9T#Wl#xE#CA#CA#CA#xE#CA#Jw.5V.uqa9U.ls.kg.kf.kg.ns.kg.kf.mw.kf.ns.zM.lr.kf.mw.mw.kf.kg.kg.vk.rka9V.TQa9Wa9Xa9Ya9Za90a91.cF.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2#Do.r..v4.bOQtaQtaQtVQtVQtV.bOQtaQtV.#Q.aFQtkQtVQtVQtVQta.bOQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q#I7.#t.cu.dI#c5#c5#Lm#c5#c4#id.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.cta92#2L.cLa93a94.haQtV#NW#I8#c5#c5#c5#c5#c5#c4#c4#8paqSarO.fX.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.aw.vb#5Ga#MaqS",
+"ab4ab4ab4aeZaeZaKma95aim.JdaeJaeJauV.PZ.14.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K7.L.a3Ca96#eFazK.r..v4aBw.gV.aw.aw.aw.aw.aw.aw.aw.aw.gV.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.awa9va97.0o.Jc.Jc.Jc.Jc.Jc.Jc.MM.K8.Oja3XaJp.EU.v4.v4.w2.w2#HzQtd#bFa98a99a99#B1.li.e8#D1.M8.r2QtEQtEQtEQtEQtEQtEQtEQtEQtE.bH.cS.bG.Xb.TP.h6.kjb...#m#bg#c4#c5#c5#c5#c5#c5#c4#bd#qc#vG#gE.9V.YV.Vv.Vv.Vv.Vv.Vv.72.bH.abQtEQtE.bH.bHQtFQtK.TH.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV#wK.g..vh.nx.9F.a3b.#.0v.Xe.nt.nt.nt.um#wK#CA#CA#CA.7T#xE.cTQtFQtF.a4a4Ub.ab.bb.c.qcb.da13b.eb.fb.ga7gagj#wBa7ha.fb.hb.ib.jb.k.k#QtF.kk.r7.MZ.vk.ou.yk.rA##E.g9.#6.#6.#6.#6.#6.#6.#6#MeaTZaYt.kf.kf.ts.sO.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.Ltb.l.#F.vb#c5#c5#c5#c4#c4#5Ha#MaqSaqSaqSaqSaqSaqSaqSa#Ma#Ma#N#Lm.w4#Ox.Ox#KO#xE#xE#CA#CA#CA#CA.9V.ty#rn.ns.kf.kf.kg.ns.MZ.kg.ns.ns.kg.kf.kf.kg.kg.kf.mw.mw.kf.kg.kg.mwb.mQtVb.na55b.ob.pb.qaAFb.r#6a.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4aBw.3U#ET#F2.w0.aw.bW.#PajP#icarOavSarO#c5#uHaka.w4.w4#4R.wZQtbQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#t.aF.w4#c5#c5#c5#c5#c5#bjQtaQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.#ub.s#vxb.tb.ub.v.ah.#y#FD.v5#c4#c5#c5#c5#c5#c4#ET#8paqS#8p.#E.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.dh.3U#c4avSaqS",
+"ab4ab4ab4adqab4ab4adgab5ajGaH4b.waioail.Je.PZaeJaeJaeJaeJaeJaeJaeJaeJaij.PZ.14.Tq.K8.K7.Oj.Ojb.xb.y.aF#Hz.r..w2.qZ.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gVb.zb.A#9q.Jc.Jc.Jc.Jc.Jc.MM.K8.PZb.Bb.CaF6.gV.sq.v4.v4.w2.w2.v4.ee.eeb.D#tBapR.li.li.S#b.E.Lz.r2.koQtEQtEQtEQtEQtEQtEQtEQtE.bHQtF.#8.uB.TP.Ov.web.F#13.w4#c5#c5#c5#c5#c5#c5#c5#bi.w4#M#.TPb.G.YV.Vv.Vv.Vv.Vv.Vv.TF.k#QtF.bHQtF#Iz.cY.Q#.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.33.r#.po#Yn#bz##N.km.M6QtK.nt.nt.ow#wK#CA#CA#xE.Vv.oD.eqQtFb.H.uhb.Ib.Jb.K.q6b.Lb.Mb.Nb.Ob.Pb.QaEd#wBa7h#Fib.Rb.S.h7a9Q.koQtFQtK.ms.pq.kg.zK.Lt.j.QtF.#6.g9.g9.#6.#6.#6.#6.#6.#6QtK.i1a1eb.T.rk.kg.nr.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.Ltb.UQt#aze.w4#c5#c5#c5#c4#c5avS#8pa#Ma#MaqSaqSaqSaqSa#Ma#Mb.V#uH#beajR.ke.mD.TQ#xE#CA#CA#CA#xE#Da#NI##L.ns.kg.kg.kg.ns##L.MZ.ns.kg.ns.kg.ns.kg.ns.ns.ns.kf.kf.kg.ns.ns.mwb.Wb.X.#QaldaPO#IIb.Yb.Zb.0b.1QtU.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4aBw.#E#F2#5KaqSaqSaqSaqSa#Ma#Mb.VaqSb.2aqSaqSaqSa#Mb.3b.4.P5b.5b.6#2X.#q.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO.#Q.w4#c5#c5#c5#c5#c5.w0.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.#ub.7b.8b.9b#.b###7dQtj.gV#c4#c5#c5#c5#c5#c5#c4#c4#Kqa#MaqS.dK.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r..w2#be#Kqb.V",
+"ab4ab4ab4ab4ab4ab4adqab4aeZadgadgaeZa#w#Bkb#aaeLb#baI5aJaaJaaYmaJaaJaaI5b#cb#d.Jeb#eaigb#fb#f.Vf.K5#urb#g#Hz.v4.v4.w2.qZ.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.ey.o7b#haOc.K8.Jc.Jc.Jc.Jc.MM.K8.14b#ib#j.p5.cv.gV.qZ.v4.v4.w2.w2.w2.w2.v4.b6b#kb#l.JTa#T#B1.S#b#m#TV.M8.ug.lGQtEQtEQtEQtEQtEQtEQtE.bHQtF.rB.9V#zA.Sc.r8#NN#bi#c5#c5#c5#c5#c5#c5#c5#c5.w4#bj#0a#sx#Cx.Vv.YV.Vv.Vv.Vv.Vv#e1.eq.k#.bH#bA.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM#xE#tK.YW#vN#P3.jl.lF.mr.g9.nt.tK.TQ#CA#xE.Q#.bH#T3b#nb#ob#pb#qb#r#3Qb.L.e#a9ab#sb#t#KHac6#JNa6yb#ub#vb#w.bH#F7.bHQtFQtK.OFa0v.lq.OT.OF#UkQtK.#6.g9.g9.g9.#6.g9.#6.#6.#6.#6.g9.cSb#xaUP#ikb#y.kf.lu.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.vj.LtaAX.v4.r..dI#c5#c5#c5#c5#c4#c5#tta#Ma#Ma#MaqSaqSaqSa#Ma#Ma#MaqR#bg#8zauM.mw.7P.TQ#CA#CA#CA#xE#CA.vt.mu.zM.ns.ns.ns.kg.ns.MZ.ns.kg.ns.ns.kg.ns.ns.ns.ns.ns.ns.kg.ns.ns.ns.kg#ik.mwb#zauA#P#.eL.Jqb#Ab#Bb#CQtS.w2.w2.v4.v4.v4.v4.dhQtV.w0azNavSb.VaqSaqSaqSaqSaqSaqSaqSaqSa#MaBv#5KarO#SfavS#6ya#Mb#DaPV.P5b#EauGb#E#MW.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.fM.bO.t1#c4#c5#c5#c5#c5#c4.cv.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.#u#K9#DKb#Fb#Gb#Hb#I.bs#c5#c5#c4#c4#c4#c4#c5#c4#c4#c5#9nb#J.#p#OW.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.Qt.#bcaBv",
+"adqab4ab4ab4ab4ab4ab4ab4adqab4aw8aLkaaGaSWaXDb#Kb#Lb#Mb#Nb#Ob#PaZJb#Qb#Rb#Sb#Tb#Ua4Ja31b#Vb#Vb#Wb#Xb#Yb#Z.qZ.r..v4.v4.v4.w2QtV.t1.gV.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.aw.gVa9vb#0.PY.K7.MM.Jc.Jc.Jc.Jc.K8.K7b#1.EW.v4.w2aze.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.ee.ahb#2b#3#CD.Na.e8.S#b#4.ra.r2.koQtEQtEQtEQtEQtEQtEQtE.bH.bH.3Y.YT.BJ.JVb#5#bj#c4#c5#c5#c5#c5#c5#c5#c5#c5#c4#bd#NVb#6#Cx.Vv.YV.Vv.Vv.Vv.Vv.Vv#Bd#by.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM#yF.lx.9I#YH.2b.mr.g9.g9.#6.BM.TQ#wKb#7b#8#T3b#9ba.ba#ae6baa.nd.iOa13babbacbada7g#K7a9Ma9db#vbae.pN#mEQtK.g9QtK.nt.qn#rA.un##o.rA.#6.g9.#6.g9.g9.ch.sf.rA.g9.#6.#6.#6.#6.#6.OvaYvaTYaZQbaf.ki.rh.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.vjbagbah.au.r..3U#c5#c5#c5#c4#c4#c5#3V#5Ka#MaqSaqSaqSaqSaqSa#MaQT#3V.w4.w3#YP.qo.ns.ep#Da#CA#CA#CA#CA#CAa1X.2d.kg.ns.ns.kg.ns.ns.ns.kg.ns.ns.ns.ns.ns.ns.kg.ns.ns.ns.kg.kg.ns.ns.ns.kg.kf.kh.kfabbbaibaj.exQtc.au.v4.v4.v4.v4#OW.gVajPaX4#5KaqSaqSaqSaqSaqSaqSaqSaqSa#M#8p#9n#Sf#uH#c5#c4#c4#c4#c5#c5#uHarOaB7aqSbakbal#ODb.4.16Qt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.bW#c4#c5#c5#c5#c5.dIaBwQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtk.#ubamban#zubaobapbaqbar#c4#c5#uH#uH#c5#c4#c4#c5#c4#c4#ETbas.W2#I7.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.QtV#Xx",
+"ankadfab4adqadqab4ab4aeZaLkanlbatbaubav#CFbawbaxbaybazavTavTbaAbaBbaBa3.a3.baCbaDbaEbaF.cp.sq.sq.sq.sq.bW#be.bO.r..v4.w2.v4.v4#P#aze.cu.t1.aw.aw.aw.aw.aw.aw.aw.aw.aw.gVa9va97.Oj.K9.Jc.Jc.Jc.Jc.K8.L.baG.I9.G0.w2.v4.v4.v4#Do.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4acRbaHbaIbaJ.OP.li.S#aWfa7L.ab.ugQtEQtEQtEQtEQtEQtEQtEQtF.7X.BM#e8.twbaK.16.w4#c5#c5#c4#c4#c4#c4#c4#c5#c5#c5#zw#c5#gH.BM#LE.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM#jT.M6.RMQtM.RS.nt.#6.g9.g9baLbaMbaNbaObaPbaQbaRbaSbaT#Qb.iOa#BbaUbaVbaWbaXaHma9M#K7baYbaZ.ur.Xs.#6.g9.g9.#6.g9.wg.QkQtK.rA.#6.g9.g9.nt.g9.kl.bG.Vv.2b.nt.g9.#6.#6.#6.g9##Eba0ba1aSdba2.mt.sO.r7.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.BJba3.v4.Tm.aw#I8#c5#c5#c5#c4#c4a6F#5Ga#Ma#MaqSaqSaqSaqSaQTaqS#5H.Du.w4#YMba4.mw.kf.cT.TQ#CA.TQ#QC.TN.Y1.uq.ns.ns.ns.ns.kg.kg.ns.ns.kg.kg.ns.kg.ns.kg.ns.ns.ns.ns.kg.kg.ns.kg.ns.kg.ns.kg.ns.rj#zA.bwba5Qtsav..r.#OW.p5#qd#Lm#9na#MaqSaqSaqSaqSaqSaqSaqSaqSaqSa#Maoy#uH#c4#c4#c4#c4#c4.Jk#c5#c5#c4#c4#c4#c4#Lm#ETavSa#Mb.Vb.Va#M.3U.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.#F#c5#c5#c5#c5#c4.t1.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.#uba6#ypaO.ba7ba8ba9#9nayVaqSa#M#5H#c5#Kq#5Ka#N#5K#SfaB7bb..Qf#m8.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.w2.w2.w2.v4.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..gV",
+"bb#bbaankanianiadfafcaMkbbbbbcbbdbbebaBa5zbaFaBKbbf.sqQtV.sqQtVQtVQtV.cpQtVQtVQtV.#sQtkQtVQtV.fM.fMQtVQtVQtW.cv.w2.r..w2.w2.w2.v4.v4.v4.dh.bO.fX.gV.aw.aw.aw.gV.gV.gV.p5aHU.RGaVj.Jc.Jc.Jc.MM.K8.Ojbbg.v4.G0.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.v4.bu.hbbbhbbia#Ta99.e8.e8bbjbbk.sJ.koQtEQtEQtEQtEQtE.a4.bH#e1.rB.vj.wfa.A#bj#c4#c4#c5ahj#om#ol#c5.w4#c4#c5#c5.w4#bja.S.2b#Cx.YV.Vv.Vv.Vv.Vv.YV.YV.YV.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.TP.bI.ci.9F.nt.g9.g9.a0b#8.lGbblbbmbbnbbobbp.iO#Qb.iOa#Bbbqbbrbbsa7galAa9M#7mbbtbbu.#7#xH.#6.#6.#6.g9.g9.#6.g9.#6.oP.#6.g9.g9.g9.oP.lw.TF.uB#CA#JwQtF.g9.#6.#6.#6.g9.#6.XnaSdaYv#rnba1.ps.Lr.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.BJa6naBw.Tm.fX#c4#c5#c5#c5#c5.w4#Sf#MXaqRasVa#MaqSaqSaqSa#MaQT#6z#c4#c4#bfbbv.ps.kf.kg.oE#Wl#xE#Db.rz#vC.kg.MZ.ns.ns.ns.ns.kg.ns.ns.ns.ns.ns.ns.ns.kg.kg.ns.kg.ns.kg.ns.ns.ns.ns.ns.kg.ns.kf.MZ#BT#1.#2va55#Li#2Uaka#2ba#MaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqS#6y#uH.Du#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.Du#Lm#8pa5da#MaqSaB7.aF.sq.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtV.w4#c5#c5#c5#c5.#P.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.#u#uu#UJbbwbbxbbybbzbbAbbBa6Fa#Ma#MaqSaqSaqSaqSaqSa#M#8pbbC.y#.w0.Tm.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.w2.w2.w2.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4",
+"bbDbbEbbFbbGbbHbbIbbJbbK#Kz#GT.qZQtVQtaQtaQtVQtVQtVQtVQtVQtV.#Q.#Q.fM.fM.fM.fM.#Q.fMQtVQtVQtVQtkQtkQtV.#QQtVQtVQtV.v4.v4.w2.w2.w2.w2.w2.v4.v4.v4.w2QtV.c8.cv.bW.aw.bW.eybbL.0n.K8.Jc.Jc.Jc.K8.K7bbMbbN.EV.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.dhQt..bOQtV.#S.cubbObbPbbQ.Na.S#aZMbbRQtF.pNQtEQtEQtEQtE.bH.cS.3Y.TO#Tr.sN.9Oa9iaGg#bgap7.YL#eP.YL#1D#2R.w4#c4#c5#c5#c4#idauGbbS#Cx.YV.YV.BM.BM.BM.BM.BM.BM.BM.BM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.lx.W9.kn.tK.aZ.bHbbTbbUbbVbbWbbXbbYbbZ#Qb#Qb.mfa13.o4bb0bb1bb2bb3bb4#Qmbb5bb6##Ea8F.oq#im.i2.nt#gC.g9.#6.#6.#6.g9.g9.#6.g9.g9.g8.qE#jT#xE#CA.aYQtF.nt.#6.#6.#6.g9.g9.lGbb7aW5bb8#mDa0tbb9.Lr.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.BJaL9.aw.Tm#OW.vb#c5#c5#c5#c5#c4.JkaBv#c5atOasVa#MaqSaqSa#Ma#MasV#Xx.w4#c4#3Vbc..kh.kf.ns.rn#qo.tw.ps.zM.Qa.ns.kg.ns.kg.kg.kg.kg.kg.kg.ns.ns.ns.kg.ns.kg.ns.ns.ns.ns.ns.kg.ns.ns.ns.ns.ns.ns.kg.MZ.Y1#T.#xEbc##QC#P4bcaatOa#MaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqS#6y#c5.w4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.Duaka#6yaqSaqS#Xx.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q#I7.bO#be#c5#c5#c5#c5.#E.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.#vaCD#vybbwbcbbccb#.bcdbcebcfaqSaqSaqSaqSaqSaqSaqSa#M#8p#QA.OI.RJagc.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.G1",
+"#c5#F2awAadObcgawAawzanH#c4#c4.vc.c8.bOQtVQtVQtVQtVQtVQtVQtVQtVQtV.fM.fM.fM.fM.fM.#QQtkQtVQtVQtk.aF.fMQtVQtV.cp.aFQtV.w2.v4.v4.w2.w2.w2.w2.w2.x9.v4.v4.v4Qt.QtV.c8.aLa30bch.L..K9.Jc.Jc.14.Ojbcibcj.G0.dh.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.dhQt..bOQtaQtaQtaQtV.bO.e4bck.HPbcl#CDaa9#d#aS1bcm.r2.koQtEQtEQtEQtF.lw.YV.Xb#gJ.sN.2hbcn.16#uD.zr.YL.YL.YL.zEahj#ic#c4#c5#c5#c5#bj.Qfbco#gE.TP.TP.TP.TP.BM.BM.TP.BM.YV.Vv.Vv.Vv.Vv.Vv.YV.Vv#Cx#Mi.dk.k..sY.rBbcp.a4bcqbcrbcsbctanZ#Qb#Qb.gR.x6a4dbcubcvbcwbb3aHma6ybcxbcy#8Bbcz.tybcA.Vm.Dy.2c.g9.#6.#6.g9.g9.#6.#6.g9.nt.ko.2b.uB#Hb#CA#xE#Iy.#6.g9.#6.#6.#6.#6.qr.lF.kla8GbcBQtK#qsbcCbcD.0y.Lr.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.qqbcEav..v4#be#c5#c5#c5#c5#Lm.L##5H#5G#c4a#NaqSaqSaqSb.Va#MaBv#uH.w4#c4#bg##x.os.kg.kg.kg.MZ.uq.ns.kg.ns.kg.ns.kg.kf.kg.kg.kg.kf.kg.kg.ns.ns.kg.ns.ns.ns.ns.kg.ns.kg.ns.ns.kg.kf.kf.kg.ns.kg.kg.kg.mu.cT.TQ#xEahlbcFbcGbcHa4DaqSaqSaqSa#MaqSaqSaqSaqSaqSaqSa#M#ET.w4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#uH#8paqS#2b.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#t.c8.w4#c5#c5#bgQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtabcIbcJ#1qbcKbcLbcMbcNbcObcP#6zasVaqSaqSaqSaqSaqSaqSa#MaqS#BVbcQ.vcagc.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.v5.bW#m8.aFQtVQtVQtV.#Q.#Q.fM.fM.fM.#Q.fM.#Q.fM.fM.#Q.fM.fM.fM#I7Qtk.c8.c8QtVQtV.fMQtV.bO.v4.v4.v4.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4.w2bcRaYj.OjbcS.Jc.K9.L.aYla1F.I9.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r..dhQtaQtVQtVQtVQta.bOQtaQta.#R.fZbcTbcUbcV#CD.FAaZMbcW.a4.r2.bHQtE.bHQtF.a3.YU.YV.oP.sO.tradZ#rE.YK.YL.YL.YL.YL.YL#46#65.w4#c4#c5#c5#beaoLbcX#LE.TP.TP.BM.TP.TP.BM.BM.Vv.Vv.Vv.Vv.Vv.YV.Vv#0ybcYa6pamh#yw.eobcZbc0bc1a2ybc2bc3bc4#Qb.mfaaLa13bc5bc6#FYac6aHma7hbc7bc8bc9#tG.kd.YP.Lj.kkQtF.oP.#6.g9.#6.#6.g9.g9.g9.g9.g9.h6.0A#wK#xE.7T.Vv.uz.nt.g9.#6.#6.#6.#6.g9.Y6.BM.lobd.QtF.g9.#6QtDbb7.2daJI.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wfbd#bda.Tm.#E#c4#c5#c5#c5#c5#c4#uHaBv#c4#c4#6yaqSaqSaqSa#Mb.V#Ut.w4#c4#c5#biazmbdb.kg.kg.kg.ns.kf.kg.kg.ns.ns.kg.kg.kf.kg.kg.kf.kf.kg.ns.ns.ns.ns.ns.ns.kg.kg.kg.kg.kg.kf.kf.mw.kf.kg.kg.kg.kg.kg.kg.MZ.mt#Ha#CAbdcbddanabdebdfaqUa#MaQTaqSa#Ma#MaqSaqSaqSarPa#N#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#9na#M.#E.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtaQtW.#FQte.aFQtV.fM.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.qZ.bVbdgbdh#Q6bdibdjbdkbdl#xvbdm#6zb.VaqSaqSaqSaqSaqSa#M#8pbdnaxZ.P5.vc.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c4#bg.cv.bOQtV.#Q.fM.fM.fM.#QQtVQtV.fM.fM.fM.#Q.fM.fM.fM.#QQtkQtV.cv#r3.cv.aFQtVQtVaBw.dh.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.MKb#1.K6.K9.MM.K7aWEbdo#GS.tm.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.G0.v4QtV.#QQtVQtVQtaQtaQtaQtaQtVQtV.rK.#SbdpbdqbdrapRaa9bds.Nb.M8.bH.rA.bHQtE#by.Vv#jT.h6.Sd.2hbdt#X0.Vi.YL.YL.YL.YL.YL.zq#eY#c4.w4#Lm#c5#be#XW#Yq#LE.TP.BM.TP.TP.TP.BM.Vv.Vv.Vv.Vv.YV.53.YVbduaBw#mA.lf.7Ibdvbdwbdxbdyao6bc4#Qb.mf.fSbdzbdAbdBbdCbdDaHma7hbdEbdFbdG#qn##B.psbdH.g9.bH.Y6.g9.g9.#6.#6.#6.#6.g9.g9.g9.lG.Ou.Vv#Hb#CA#HS.Xg.lG.nt.g9.#6.#6.#6.#6QtD.g9#P2.2b.we.#6.#6.#6.g9QtK.XnbdI.ke.sO.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wfa5Ta45.Tm.sq#c4#c5#c5#c5#c5#c4#c4a#N#Sf.w4#Uta#Ma#MaqSaqSa#MbdJ#c4#JR#c5#c4#J6bdK.ps.kg.kf.kg.ns.kg.ns.ns.kg.kg.kg.kg.kg.kg.kf.kf.kg.ns.kg.kf.kg.kg.kf.kf.kf.kf.mw.mw.mw.mw.kf.kg.kg.kg.kg.kg.kg.kg.kg.kg.uq.TC.TQa8abdL.d3bdMbdNamIaBvaQTaqSa#Ma#MaqSaqS#6y#Sf#c4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#ET#Sf.fX.bO#I7.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtVQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#ubdObdP#In#FWbdQ#Fi#wB#L8bdR#m5asVaqSaqSaqSaqSaqSaqSa#M#8paPVb#E.P5.RJ.ah.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.G0#Hz#GSaxf",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.L#.bWQtVQtV.fM.#Q.fM.#sQtVQtV.fM.fM.fM.fM.#Q.#s.fM.fM.fMQtV.cu.cq.vc.v5.#P.#QQtVQtV.bO.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2#GSbdS.WV.Tq.K8.PYbdT#CK.v4.w2.w2.w2.v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.bOQtaQtVQtVQtaQtVQtVQtVQtaQtVQtV.bO.#y.bibdUbdVbcVbdW.FAbdXbdsbcW.r2.oPbdYbdZ#jM.TO.pM.wg.twa5.aEH#ts.D#.YL.YL.YL.YL.YL.Bn#uD.w4#c4#c5#bj.OIbbS.53#wK#LE#gE#Hb#gE#Cx#Hb#gE#sz#sz.Voafw.fYatl#NH.kmb.obd0bd1bd2.fS#2A.mf.mfae3bd3ah6bd4bd5aZ0aEda8Bbd6bd7bd8#c9.rk.sM##o#qs.g9.#6.#6.g9.#6.g9.g9.#6.#6.nt.#6.cW.TP#wK#Hb#CA#xE#Iy.#6.qr.#6.#6.#6.#6.#6.#6.Qt.uz#CA#zC.2e##E.g9.#6.#6.g9.#6bd9.mw.kd.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.Sc.wf.Ltbe.a.2#be#c5#c4#c4#c4#c4#c4#UtasV#c5#Xxa#Ma#Ma#Ma#Ma#Ma#MaBv#c4#JR#c5.w4#m5a9U.mw.kg.kf.kf.kg.kg.kg.kg.kf.kg.kg.kg.kg.kf.kf.kg.ns.kg.kf.mw.mw.mw.mw.mw.mw.mw.kf.kf.kg.kg.kg.kg.kf.kfbe#.kf.kf.kf.kg.kg.ns.rk.TP#CA#bsbeabebbecbed#2ba6FasVaqSaqSa#M#Kq#c4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#c4.c8.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.bObee#Jf#L8#In#F0befbeg#6zasVaqSaqSaqSaqSaqSaqSaqSa#MasV#HNauG.MP.#P.ah.v4.w2.w2.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.G0#IXaxL#CaQtkbehbei#KV",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.w0QtaQtV.#Q.fM.fM.fM.#Q.fM.fM.fM.fM.fM.fM.fMQtVQtV.fMQtVQtV.aF.#T#c5#c4.vc.aw.#Q.bO.dh.v4.v4.v4.w2.w2.w2.w2.w2.w2.v4.k6aKJ.Oj.K7.G7.EW.v4.w2.w2.v4.v4.w2Qt..v4.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.v4.au.bOQtVQtVQta.bOQtVQtVQtVQtVQtVQtV.bO.#Q.vc.w3bejbekbel.zRbdW#EN#B1.Sa#Fzbembenbeobep.Vv.#5.2mbeq.kcaoA#ts.zq.YL.YL.YL.YL.YL.D.#8z.w4#c4#c5#bi#Nd#0aber#oK#50bes#EC#vG#dlbet#Wmbeu##t.wZ#YMbevbewbexbeybezbeAae3.mf.mf#Qban0beBbeCbeDbeEaDC#ImbeFbeGbeHbeI.Qc.JL.kl#on#Iy.ep.um.ep.#6.g9.#6.#6.#6.g9.nt.k#.aY#xE#CA#Rs.7T#II.lw.nt.g9.#6.#6.#6.#6.#6.#6.g9.rA#jT#Iy.yk##E.g9.#6.#6.#6.g9QtK.Vm#GJ.pq.ki.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.kc.sP.wfbeJbcnaxk.w4#c4#c5#c5#c5#c5#c5#6z#8p#c5#8paqSaQTb.2aqSa5da5d#6yavc#c4#c4#bg#EzbeK.kf.kg.kg.kg.kg.kf.kf.kg.kg.kg.kg.kg.kg.kf.kg.kg.kg.mw.mw.mw.kf.kf.kf.kg.kg.kg.kg.kg.kg.kg.kg.kfa.D.ns.W6.kg.kg.kg.kg.kg.kg.ns.oE#2vbeLbeMbeNbeObePbeQbeR#6zbcf#5H#uH.Du#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#Lm#c4.w4.vb.vb.w4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c4#c5.w4.cq.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtV.ct#4zbeSbeT#PKbeUbeV#6zb.VaqSaqSaqSaqSaqSaqSaqSa#M#8pbeW.Jlb#E#0g.t1.r..v4.w2.v4.w2.w2.w2.w2.w2.w2.G1#Hz.v4#Hz#HA.s7beXbeYbeZbe0apI.fO#I3",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.#TQtVQtV.fM.fM.fM.#Q.#Q.fM.fM.fM.fM.fM.aF.aFQtk.#QQtk.aFQtVQtk.#F#uH#3V#vE.Du.vc.#P.#QQt..k6.v4.v4#Do.w2.w2.w2.v4.EVbe1be2aLP.ET.G0.w2.v4.v4.v4#OWQtV.#QQtVaze#P#.v4.v4.v4.w2.w2.w2.w2.v4.v4#OWQtV.#QQtVQtVQtV.bO.bOQtVQtV#I7.bO.#Q#bg#uH#uH#uH.L##bcbe3be4be5#Dl#ENbe6be7.g0#jW#orbe8be9bf.bf#.OL.kjbfa#X0#Uq.D#.YL.YL.YL.YL.D##qc.16.w4#c5#bg#bj#bj#qd#id#bj#be#bg.#m#m5#bg#id#bg#bibfbajZbfcbfdbfe.nd.mf.qcaaL.fSbffbfgbfhbfibfjbfkbflbfm#rs#rs.OT.2q#0y#Hb#wK#xE.TQ#HS#by.rA.g9.#6.g9.#6.#6.g9.h6.9F#CA#CA#CA#CA.Xg.ko.nt.g9.#6.#6.#6.#6.#6.#6.#6.qr.vu#wK.Y6.nt.#6.#6.#6.#6.#6.g9.#6.2ebfn.mt.kf.tx.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.rg.rk.wfbfobfp#P##c4#uH#uH#uH#uH#c5#uHbfq#6za#Nbfrbfrbfrbfrb.2bfsbfsaBv#c5#c5#c5#bg#F3.mw.OC.kg.kg.kg.kg.kf.kf.kg.kg.kg.kf.kf.kg.kg.kg.kg.kf.kg.kg.kg.kg.kg.kg.kg.kg.kf.kg.kg.kf.kg.ke#dd.zwbftbfuat0.mw.kg.kf.kg.kf.ns.ps#DN#Ha.DKbfvbfw#99bfxbfybfzbfAbfB.og#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#bi.#P.t1.aFQtVQtV.#tQtV.aF.c8.bW.#F.v5#c4#c5#c5#c5#c5#c5#c5#c4.#PQtaQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.ai.0sbfCbfDbfEaBvarPaqSaqSaqSaqSaqSaqSaqSaqSaqSa#M#8p#NNb#E#3H#qc#OW.r..w2.w2.w2.w2.w2.w2.v4.G0axhbfFQtabfGbfHbfI.#M#eH#eH.az#I3.#K.#L",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#bi.c8QtVQtV.#Q.fM.fM.fMQtk.fM.#QQtk.aF.#Q.aF.#Q.#s.aFQtkQtVQtV.vb#tt#3V#tt#3V#uH#c4.v5.#P.fXaze.v4.v4.v4.v4#HzaWNbfJ.Hb.dh.G0.v4.v4.dhQt.QtV.fM.aF.qZ.#Q.aFQtk.cuaze.w2.v4.v4.v4.v4.v4Qt.QtV.fMQtVQtVQtVQtVQtaQtaQtV.bOQta.cv.w4#uH#Ut#uH#uH#Ut#Ut#c4.#Cb.3bfKbfLa#T#HZ#EQ#B1.e8.e8bfMbfNbfO.3V.ur.vjbfPbfQauG.zE.D#.YL.YL.YL.YL.HH#3V.w4#c4#c4#uH#uH#c5#c5#c5#c4#c4.w4#c4#c4#bdaT0bfRbfSbfTbfUaaLa#z.mfae3bfVbfWbfXbfYbfZbf0bf1bf2bf3bf4.yk.#8#xE#Da#HS#CA#CA#CA#CA.33.cS.nt.#6.#6.#6.g9.#6#Iy#xE#CA#xE#CA#Hb.a3.#6.qr.g9.#6.#6.#6.#6.#6.#6.#6.ntQtG#zB.ep.nt.g9.g9.#6.#6.#6.#6.g9.#6.ntb.T.or.kc.W6.sO.wf.wf.wf.wf.wf.wf.wf.wf.vj.qo.nr.vj.JVa5S.Du#c4#uH#uH#Ut#Ut#uH#Ut##vbf5bf5bf6bf5bf5bf5bf7bf5bf8b#J.bd#c5#uH#c4#eYavt.kf.kg.kg.kg.kf.kg.kg.kf.kg.kg.kg.kf.kf.kh.kf.ns.kg.kg.kg.kg.kg.kf.kf.kf.kf.kf.kg.kg.W6at0bf9bg.#MJbg#bgabgbbgc.W6.kg.kg.kg.ke.kg.MZ.0y.Sbbgdbgebgfbggbghbgibgjbgkbgla7p.v.#ic#c5#c5#c5#c5#c5#c5.w4#r3.#QQtVQtaQtVQtVQtVQtVQtVQtVQtVQtVQtV.bOQtaQtW.#F#Iv#c5#c5#c5#c5#c5#c5.w3.t1.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO#F2asVa6Fa6FaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqS#8pbgm.Jlazqb#E#V0.r..v4.w2.w2.w2.w2.w2.v4#GRbgn.iKbgobgp.#L.az.#L.#M.#L.#L.#L.#L.#L",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4.#PQtVQtVQtV.fM.aFQtk.#sQtV.aF.#s.#sQtkQtW.#QQtk.aF.gV.#F#c5#tt#3V#3V#3V#3V#3V#3V#Ut#Ut.Jk#bi.w0.gV.cu#5u#sBaVk.EV.v4#P#Qt..bOQtVQtk.aF.aFQtV.aF.aI.#QQtk.fM.#QQtV.bO.bOQt.#OW.bOQtVQtVQtVQtVQtVQtVQtVQtVQtVQtVQtV.bW.Du#Ut#uH#uH#uH#uH#uH#uH#uH#uH.Jk.#Cb#Jbgqbgrbgs.HS#CD#yJ#ERbgta8qbgubgv.cWbgw.vjbgx#Ba.zE.YL.YL.YL.YL.YL.D..#m.w4#c4#c4#c5#uH#c5#c4#c5#c4#c5#c5aNBbgybgzbgAbgB.mf.mf.mf#Qb.qdbgCbgDbgEbgFaYGaeQbgGbgHbgI#WI#ZV.TQ#QC#CA#CA#CA#CA#xE#CA#II.cl.nt.#6.#6.#6.g9.#7.3Y#CA#CA#CA#CA.Vv.um.Qt.g9.#6.g9.#6.#6.#6.#6.#6.g9.g9.#6.TP#by#gC.g9.#6.#6.#6.#6.#6.#6.g9.g9QtKa71bgJ.Sb.qp.ps.tw.wf.wf.wf.wf.wf.wf.wf.tw.ke.mw.tw.wf.tvbgK.w4#ET#Ut#uD#uD#3V.#p#eYaG7ahj#YMbgLbgMbeWbgLbeWbgNbgO#44#Ut#Ut#Ut.L#aAt.ps.kf.kg.ns.kg.kf.kf.kf.kg.kg#ik.kgbgPayT#1A.mw.kg.kg.kf.kg.kg.kg.kg.kg.kf.W6at0.W6bgQbgRbgSbgT#4Y#4YaFEbgUbgVbgW.lq.kg.kg.zK.TC.kd.Qa.MY#jRbgXbgYbgZ#8nbg0bg1bg2bg3bg4bg5Qt6.Fr#c5#c5#c5#c4#beQtk.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtVQtaQtV.bW.v5#c5#c4#c4#c5#Lm#c4#xK.c8.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO#J6aqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#Ma#M#0gb#Eb#E.Fp.dI.r..v4.w2.w2.w2.w2.v4#Hzbg6bg7agTbg8.#L.#M.#M.#M.#M.#M.#M.cz.cz",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#bi.bW.aFQtVQtV.#s.aFQtV.fM.#Q.#QQtV.qZ.aF.aF.aF.bW.vb#Ut#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#5G#Ut#Us.Jk.w4#tm#bj.bW.cv.cv.c8.#QQtk.cuQtkQtk.aF.#Q.fX.qZQtVQtVQtV.aFQtVQtV.#Q.#QQtVQtV.qZQtVQtaQtVQtV.bOQta.aF.#T#c4#uH#uH#uH#Ut#uH#uH#uH#uH#uH#13#Ut#uH.Jk.at.Frbg9bh.bh#bhabhbbhc.OQ#Wdbhdbhea.NbhfafyajR.#l.HH.YL.YL.YL.Jl.YL.zq#7G.w4#c4#c4#c5#c4#c5#c5#c4#icbhgbhhbhibhjbhk.fSa#z.mfadubhlbhmbhnbhobhpbhqbhrbhsbhtbhubhvbhw#gX#CA#xE#CA#CA#CAahl#CA#wK.7X.nt.#6.g9.#6.g9.#6.3Y.7T#CA#CA#CA#Jw.k#.nt.g9.#6.#6.g9.#6.#6.#6.#6.g9.#6.nt.cW#CAQtG.nt.#6.g9.#6.#6.#6.#6.#6.g9.#6#qsbhxa1e.tu.Ls.kh.qp.tw.wf.wf.wf.wf.wf.vj.sO.W6#BZ.kj.vj.Ltb.l#c4#3V.#p.2u#5Jagbagbahj#5R#5R#XW#5Rbdf#VC##w#VC##waxy#eY#tt#bk.#p#Ut#c1bhy.ps.kf.kf.kg.kg.kg.kfa9m.kfav4##x#7G#bg#XW#1B.ps.kh.kf.kf.kf.mw.khbhz.tsa2XbhAbhBbhCbhD#UI#W##UJ#Vv#2K#TRa#FbhEbhF.ke.kf.ns.qq.rz.qq.MZ.mwbhGbhHbhIbhJbhKbhLbhMbhNbhObhPbhQa0.#c4#c5#c4.#FQtaQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.fMQtk.#sQtk.#s.#Q.#Q.#Q.#QQtV.bOQtV.aw.cv.#Q.#P#bg#c4#c5#Sf#J6.c8.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.sq#ida#MaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqS#8pbb.#3I#3I#3H#NVaze.r..w2.w2.w2.w2.w2.w2#GQbhRbhS#Lr#N3bhT#I1aun#Lq.d#bhUbhVadx",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c4#bi.bW.#QQtVQtV.aF.#QQtV.aF.#Q.aF.aF.#Q.aFQtV.#Q#bj#c5#3V#3V#Ut#3V#tt#3V#3V#3V#3V#3V#3V#3V#tt#3V#3V#tt#3V#uH#uH#c5.Jk.w4#bi#bj.#P.#E.cv.#Q.aF.#Q.qZ.aFQtV.qZQtV.cp.aF.fMQtVQtVQtVQtV.cp.bO.aF.t1.#F.w4#uH#Ut#Ut#uH#Ut#Ut#uH#uH#uH#uH#uH#Ut#Ut#uH#uH#Ut#uH.w4.#C.cE#bkbhWbhXbhYbhZbh0#Gf#uD#c4#bi#bi.w4#uH.Qf#OB.YL.YL.YL.YL.zE#rE.w4#c4#c4#c4#c4#c5aGe#xKbh1bh2bh3.x6.mf.qc#Qbbh4bh5bh6bh7bh8bh9abVbi.bi#aXPbiabib.TQ#CA#xE#CA#CA#CA#CA#CA#CA#xE.Qh.RS.g9.#6.g9.g9.#6#tv#CA#CA#CA#CA#rrQtK.nt.#6.#6.#6.#6.g9.#6.#6.#6.g9.#6QtFQtE#II#rr.nt.#6.#6.#6.#6.#6.#6.#6.#6.g9.#6QtFbicaTXa43.Sb.tv.lqbid.tw.wf.wf.wf.vj.vj#fe#BZ.kf.r6.wf.un#Di#m5#bkbie#7G#V0#MW#MW.Vibifbig#lg#US#lg#2X#2X#2XbihaPVaPVbdn.#lbiiagbaqP#F3##B.kh.kf.kf.kg.mw.lqadZaxx#c4#bi.w4#c4#bg#uDaIrav5.9J.mwbijbikbilbimbin#TNbiobcKaOM#xr#xr#xr#HK#wx#ww#Rh#Vvbipbiqbir.W6.mw.vk.QcaZP.ehbisbitbiubivbiwbix#EvbiybizbhNbiAbiBbiCQt6#c4.#PQtaQtV.#Q.#Q.#Q.#Q.#QQtV.ct.yVbiD.wD.ct.ct.ct.ctbiDQtV.#Q.#Q.#Q.#QQtV.#tQtVQtVQtaQtV.bW#bi#c5a#N#8p.w4.cvQta.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtWaoyaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSbiEbiFaApb#E#3I#3Iaen.Tm.v4.w2.w2.w2.w2.w2.v4.v4#GRazLbiGbiHbiIbiJbiKbiLbiMbiN.qc",
+"#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4.w4#bi.cv.aFQtV.#QQtk.aF.#Q.aF.#QQtW.aFQtVQtkQtV.cv.w1#uH#3V#Ut#3V#65#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#tt#3V#3V#3V#3V#Ut#Ut#Ut#uH#c4.v5.vc.bj.w0.#E.gV.t1.fX.c8QtW.c8.t1.#Q#m8.#E#be#bg#c4#uH#uH#Ut#Ut#Ut#uH#uH#uH#Ut#uH#Ut#uH#uH#Ut#Ut#uH#uH#uH#Ut#uH#uH#uH#c5.Fr.vb#bc.cE.L##c5#uH#Ut#Ut#uH#c4.w3#bg#V0.D..YL.YL.YL.YL.zE#ol.w4#c4#c4#F2adObiObiPbiQ.fTaaL.qc.mf#QbbiRbiSbiTbiUbiVbiWbiXbiYbiZbi0bhw#HS.Vu#CA#CA#CA#CA#CA#CA#CA#CA#xE.ep.nt.g9.#6.#6.g9.#6.LB#CA#CA#CA#Rs.bG.g9.g9.#6.#6.#6.#6.#6.g9.#6.#6.g9.#6QtF.cX#tv#LEQtE.nt.#6.#6.g9.#6.#6.#6.#6.#6.g9.#6QtFbica70aXK.r7.wf.tv.mw.qo.tv.wf.wf.vj.wf.r7.mw.kf#mC.JV.unbi1#Ez.#n.k2.gE.#h.W2.ba#uK#46#0g#0gaiKamwaiK#0g#0g##s#eQ#X0.Qf.Tt.gE#MW#V0#L.bi2.mw.kh.mw##Lbi3aZO#KL#bi#c4#c5#c5#c5#c5.w4.w1#ttbi4#eRaxybi5bi6bi7bcK#zt#xr#zs#zs#zs#zs#zs#zs#zs#xr#HK#wx#xsbi8bi9#BYat0bj.a1obj#bja.esav0bjbbjcbjdbjebjf#WWbjgbjhbjibjjbjkbjlQtf.bOQtV.#Q.#Q.#QQtV.#u.yVbjmbjnbjobjpbjqbjrbjpbjsbjtbjuQtb.ct.fM.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtVQtV.cq.#T#SfaqSa#Maka.3U.c8QtaQta.#tQtVQtVQtVQtVQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQta.bW#5GaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaBva#M.MPb#Eb#Eb#E.eM.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2#HzaxL#Hs.G0bjvaC6bjwbjx.qc.pe",
+"#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5.w4#bj.#QQtVQtk.aF.aFQtV.fM.#Q.aFQtkQtVQtkQtk.aF#c2#c4#uD#3V#3V#3V#3V#3V#tt.#p#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#tt#tt#5G#3V#3V#Ut#uH#c5.Jk#c5#c4#c4#c4#c4#c5#c5#uH#Ut#Ut#Ut#Ut#Ut#Ut#uH#uH#uH#Ut#uH#uH#uH#Ut#uH#uH#Ut#uH#uH#uH#uH#Ut#uH#Ut#uH#uH#Ut#uH#Ut#Ut#uH#uH#Ut#Ut#uH.vc.cqQtVQtVQtV#c5amv.D#.YL.YL.YL.D#ap7.w4awzaWAbjybjzbjAbjBbiN.qc.fS.qcbjCbjDbjEbjFbjGbjHbjIbjJbjKbhw.TQ#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#Iy.nt.g9.#6.#6.g9.#6.LB#CA#CA#Cx#CA.rB.nt.g9.#6.#6.#6.#6.#6.#6.g9.#6.#6.#6QtFQtF.eq#Rs.g5.mr.g9.#6.g9.#6.#6.#6.#6.#6.#6.g9.#6#qsbjLbjMbjNbgJ.wf.wf.sO.mw.kh.Tx.wf.wf.wf.0y.kf.mw.qo.qq.Fu.qq.Fo.#n.YK.dS#ts#9Aamy#om#0g.zF.zF#sy#dh#dh#sy#OD.zF.zp.yb.MP.Qf.dS.W2.YK.#j.#f.D.bjObjP.Qf#3V.w4#c5#uH#c4#c4#c4#c5#c4.Du#5Ga#MaGf.#C#O9#W##Vt#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#wxbjQbjRbjSbjTbjUbjVbjWbjXbjYbjZbj0bj1bj2#WX#F1#Sb#6rbj3bg1bjjbj4.yV.#u.c3.c3.#t.#t#I7bj5bj6bj7bj8bj9bj9bj9bj9bj9bj9bgibk.bk#bkaQtaQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.fMQtV.bO.aFaGgbkbb.2aqS#8p#ET#J6.#T.#P.cv.t1.t1QtWQtWQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO#J6a#MaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#MbiE#1Eb#Eb#E#3I.zq#m8.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4#HzacXaLd.svbkcbkd",
+".vb.Jk#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#bg.#P.aFQtVQtV.aF.aFQtV.aF.#QQtk.aF.#Q.qZQtV.t1#be#c5#3V#3V#3V#3V#3V#tt#5G#3V#tt#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#tt#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#Ut#Ut#Ut#3V#Ut#Ut#Ut#Ut#uH#uH#uH#uH#uH#uH#uH#uH#uH#uH#uH#uH#uH#uH#uH#Ut#uH#uH#Ut#uH#uH#uH#Ut#Ut#uH.Fr.#PQtaQtVQtkQtVQta.#QQtV.MO#NV.D##eP.YLbkea#Vbkfbkgbkh.q5aaL.qc.mfae3bfVbkibkjajpbkkbklbkmbknbko#HS.TQ#CA#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#HS.LB.g9.g9.#6.#6.g9.#6.LB#CA#CAahl#CA.Xg.#6.g9.g9.#6.#6.#6.#6.#6.#6.g9.#6.g9QtF.ab.g8#wK.aY.#6.g9.#6.#6.#6.#6.#6.#6.#6.#6.#6.g9.g9QtFbkpaTYaSdaYx.Qc.wf.wf.kj.mw.kh.Tx.wf.vj.qp.mw.ou.ou.rf.Lj.msbkq#MW#uK#Ez#lgbkr.Bn#Uq.y#.yb.zp.zF#dh.zD#3I.zt.zt.Jl.Fp#dh.OI.Qf.eMajQaxy#eY.bc.ao#TA#bk#tt#3V#Ut#uH#c5#c5#c4#c4#c4#ttaqSa#M#ETbks#xr#xr#xr#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xrbkt#3rbkubkvbkwbkxbkybkzbkAaGX#XI#JW#F1#F1#Jo#XI#xtbkBbk.bkCbkDbkEbkFbambkG#QrbkHbkIbkJbkKbkLbjibhObkMbg1bkNbkNbkObg1biAbkPbkQbiD.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO#m8arOaqSaqSaqSaqSaqSa#M#8pavSaoyavSavS.3UQt#QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO.aFbkRasVaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaBv#bbb#Eb#Eb#E#3I#rj.Tm.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.aLdaby",
+".bW.Du.Jk#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#Lm#c5#c4.w3.#P.aF.aF.qZ.qZ.qZ.qZ.#Q.c8.qZQtVQtV.qZ.qZ.bW.w4#Ut#tt#3V#3V#3V#5G#3V#tt#3V#3Vaoy#3V.bd#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#Kq#3V#3V#3V#3V#3V#3V#tt#tt#tt#3V#Ut#Ut#3V#Ut#Ut#Ut#Ut#Ut#3V#Ut#uH#uH#uH#uH#Ut#uH#uH#Ut#uH#Ut#uH#uH#uH#uH#uH#uH#uH#uH#uH#Ut#uH#Ut#Ut#rjQtZ.cuQtVQtVQtVQtVQtVQtVQtV#OW.c8#vEbkSbgKbkTbkUbkVbkWbjB.mf.q6.mf.qcbkXbkYbkZbk0bk1bk2bk3.Xs#ZA#Da#CA#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA.3Y.#6.Qt.#6.g9.#6abd.TH#CA#CA#xE#CA.YV.lG.qr.#6.g9.#6.#6.#6.#6.#6.#6.g9.#6.g9.g9QtF.aY.Vv.cl.g9.g9.#6.#6.g9.g9.#6.#6.#6.#6.#6.g9QtD#Ukbk4bjNaRiaVDbk5.wf.wf.wf.sOa9m.ou.sN.wf.lu.pq.ou.ps.tx.rm.ykbk6ajQ#jQ#46.HH.yb.zp.zF#dh.Fp.Fp.zD.Jl.zt.zt.17.zw.zx.zCauG.17.zp#46#jQ#2X#XWar9.dS.#h.gE#V0#2Rawb#uD#13aka#c5#c4#5GaqSaqTavSbk7#yo#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#HL#xraRY#S##2K#WY#Jo#Jo#F1#Jo#F1#F1#Jo#Jo#Joa5sbk8bk9bj8bhObl.#yu#2##wx#xsbl##3p#WW#WW#F1#8nblablbblcbldbhNbg1bkObkObkNbleblf.bVQtk.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q#I7.bOQtk#F2a#MaqSaqSaqSaqSa#Ma#Mb#Da#MapCblg.#oQt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO#bj#uH#6yaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#MbiEblhb#Eb#Eb#Eb#E.4b.r..r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4",
+"Qtb.#P.Du.Jk#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c5#c5.v5.#P.#QQtV.qZQtV.qZ.fX.#Q.#Q.#Q.qZQtV.#Q.fX.w0#c4#Sf.#p#tt#3V#3V#tt#3V#3V#3V#3V#3V#3V#tt#3V#3V#3V#3V#3V#3Vblibljbli#3VawbawbblkbliaPJbllbllblm#3V#3V#3V#3V#3V#Ut#Ut#uH#Ut#3V#Ut#Ut#Ut#Ut#Ut#uH#uH#Ut#Ut#Ut#Ut#uH#Ut#Ut#uH#uH#Ut#Ut#Ut#uH#Ut#Ut#Ut#Ut#uH#bc.t1.bOQtVQtVQtVQtVQtVQtVQtVQtV.sqbaA.#Rblnbloblpblq.qc#Qba#z.mf.mfbkkblrblsbkkaBcbltblublvblw.TN#CA#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA.7T#rr.#6.g9.#6.#6.#6.2c.pN#xE#CA#Cx#CA.9V.ow.nt#gC.#6.g9.#6.#6.#6.#6.#6.#6.g9.#6.#6.nt.cW#xE.h6.nt.g9.#6.#6.#6.#6.#6.#6.#6.#6.#6.#6.g9QtK.lvaRiaS7aRiaSdaSdblx.kc.tw.JV.0z.qp.kh.ki.Tz.ou.qp.qp.rh.Nc.yjbly.zp#Ah#46.yb#dh.YL.zt.17.zC#wC#wC.zy.He.zy.zx#wC#wC.qu.Bq.r0.zx.zq.zp.ya#46#jQ#Ez#uK.Vi.#h.gE.#l#c1#65#Ut#uHayVaqSa#Mbcfblz#zt#ww#Ag#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#xr#zu#xr#xr#zs#zs#zs#xr#wx#xs#3q#Jo#WY#8n#F1#F1#F1#Jo#Jo#F1aN6blAblBblCbg1bldblD#Rh#ys#Vw#Jo#Jo#JoaOA#F1#F1#WW#Vw#WWblEblFbg1bg1bkNblCblGblHblIQtk.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.3Ua#NaqSaqSaqSa#Mbfq##x#1DbcQb#E#3I#uDQt.QtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO.#Q#Zw#c5#8paqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#Ma#Mblgb#Eb#Eb#Eb#E.2u.r..r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtbQt##bj#c4.Jk#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c5#c5.L#.#T#m8.c8.qZQtV.#Q.qZ.#QQtVQtV.#Q.#Q.sq.t1#be#uH#3V#3V#3V#3V#3V#3V#3V#3VaPJbljblk#3V#3V#3V#3VblmblJblKblLaUCblMblNblOblPblQblRblSblTblU#bkbliblV#Kq#3V#3V#3V#Ut#Ut#Ut#3V#Ut#Ut#vE#c5#c5#c5#c5#uH#13#uH#uH#Ut#uH#Ut#Ut#Ut#3V#3V#Ut#Ut#Ut#Ut#ET.vb.t1.cuQtV.fMQtV.aFQtV.fM.fM.cpbaB.s7blWblXblYblZalS#Qb.mf.qc.mf.qcbl0bl1bl2#Qba.nbl3bl4bl5#IF.TQ#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#Cx#CA#CA.tK.#6.g9.#6.#6.#6.g9.lv#Db.7T#CA#CA#Hb.um.nt.#6.#6.#6.g9.#6.#6.#6.#6.#6.#6.g9.#6.g9.cS.9V.LB.qr.g9.g9.#6.#6.#6.g9.g9.#6.#6.#6.#6.#6.#6QtKbl6aYuaSdaRiaRiaSdaVDaQ8a9ya43.rg.lp.x##M2beKbe#.kg.kg.kc.HK.Dy.HK#KM.Fp#YsauB.zD.EX.zy.Da.P6.BC.qu.qu.r0#Zb.Br#Zb.mA.Da.Bqbl7.Da.Fo.zt.Fp.zF.yb#0g#X0#Ezar9.b#.#i.#f.#n#65#bkbl8bfCasVbl9bm.#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#xr#ww#zt#xr#zs#zs#zs#zs#zs#HK#zt#HJ#F1#9kbm##WU#WY#F1#F1#WWbmabmbbg1bkNbjibmcbhLbmd#JX#Jo#Jo#Jo#Jo#Jo#Jo#JW#8n#8n#JW#WW#VwbmebmfbiAbj9bmg#6k.#t.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.aiajPaqSaqSaqSa#Ma#Ma#MbeWb.5#3I.Fo#3I.MOQtbQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.vc#c4arOa#MaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqS#8paqS.P5b#Eb#Eb#E#3I#vEazr.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtaQtb.bO#bj#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#uH#uH#c4.#F.aw.t1.qZ.cu.qZ.#Q.aF.aF.#QQtV.aFQtZ.w4#Ut#3V#3V#3V#tt#Kqbmhbmibmjbmkawb#8Jbmlbmmbmnbmo.PZ.Oj.Oj.L..Tp.Tp.L..Oja61bmp.Jibmqbmrbmsbmtbmubljawb#3V#uH#bg#bj.bW.t1.#QQtVaBwaBwaBwQtV.sqQtV.#Q.fX.fX.gV.gV.aw.#P.#F.#F.#T#be#bi.vb.vc.#Q.bOQtV.#Q.fM.#QQtVQtVaJtbaBbmvbmwbmxbmybmzbmAbmBbjB.mfa#z.mf.qc.rW.rW.rX.mfa4XbmCbmDbmE.TN#CA#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA#HS#wK#gN.a0.g9.#6.#6.#6.#6.Dy#gN.TQ#xE#CA#Hb.Qh.nt.g9.#6.g9.#6.g9.#6.#6.#6.#6.#6.#6.g9.a0.nt.RR.Vv.cS.g9.#6.g9.#6.#6.#6.#6.#6.#6.#6.#6.#6.#6.#6.#6.OFa8GaVDaSdaSdaVDaW5aTYaWla0waW5a4lbb7bmF.TB#0hbmG.sU.ja.sc.kn.tI#oj.BK.kp.mEbmH.Lg#cYbmI#1VbmJ#rl.5S#og.E0.Fm.BrbmK.Fm.Bs.BA.mA.Bq.zA#wC.zv.Jl#dh.zp.y#bkr#qc.gI.T7.#j.P4aoLbgOb#JbmLbmM#zt#xr#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#xr#xr#xr#zs#zs#zs#zs#zs#zs#zs#xr#wx#TQ#F1#3pbmN#WX#F1bmObmPbmQbk.bjibkNbkNbg1bkCbmRblEaB1#F1#8n#4Y#F1bmSbmTbmUbmVaCN#Vw#TRbmWbmXbmYbmZ#6k.#u.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.aF#2baqTaqSaqSaqSa#Ma#Ma#Mbm0bihaxu##w.aF.#t.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q#I7QtV.bO.w0#c4#Uta#MaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSbiEbgm.Jlb#Eb#Eb#EaxZ.dI.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtaQtbQtb.mi.#F#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#3V#3V#uH#c4.dI.#F.bW.c8.fX.#Q.aF.#Q.#QQtV.fX#be#uH#3V#3Vawb#65bm1#9qaihaUDbm2a4Lbm3bm4bm5aii.Tq.Jc.Jc.Jc.Jc.Jc.Jc.14.14.14.14.Tq.Tp.Oj.Tpag4bm6#bcaF6#OW.dh.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.dh.w2.tm.dh.w2Qt.Qt.Qt.#OWbaBbaBbm7bm8bm9bn.bn#bnabnb.jX#Qb.qc.mf.mf.qc.qe.rW.rX.qc.q6bncbndbne#QC#Cx#CA#CA#CA#CA#CA#CA#CA#CA#CA#xE#CA#CA#gF.bG.cl.nt.g9.#6.g9.#6QtK.i6bnf#QC#CA#xE#xE.ep.nt#gC.#6.g9.g9.#6.g9.#6.#6.#6.#6.#6.#6.g9.g9.g9.aY.um.nt.#6.#6.g9.#6.#6.#6.#6.g9.#6.#6.g9.g9.g9.nt.mr.ntbfuaTXaQ6bngbirbnh.QcbdH#TZ.mx.w9.mC.se#xD.2j.2j.bK.Xh.vh.vh.Xh.po#xD.Xh.Xh#f##uPaXI#xD.r1.cZ.sR.qz.r#bni.Nj.Hm.0t.Fe.Dc.Jm.Fm.Bu.Bs.Br.Da.z5.Fo.zt#dh.MP#Uq#lg#uK.W2#ts#XW.Vibnjbnk#vx#xr#zt#xr#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#ww#xq#zu#ztaUr#Wa#F1#F1#F1#WYbnlbnmbg1bkNbkNbkNbg1bnnbnobkCbnpbnqaBV#RYbnrbjmQtaQtaQtabnsbkFbnt#18biD.#u.yV.#t.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.fMQtVQta#Nka#MaqSaqSaqSaqSa#Ma#MbiEa#Ma#M#JR.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bOQta.#F#c4#c5aBvaqSaqSaqSa#Ma#MaqSaqSaqSa#Ma5daqSaqSaqSaqSaqSaqSa#M#8p#US#ePb#Eb#Eb#Eahc.#Q.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtbQtbQtbQtbQtb#c2#c4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c4#uH#3V#3V#3V#3V#Ut#c5.w4#bi.#P.#F.#E.#E#be#c4#Ut.#p#3VblmblK.PZbcS.K8alEbnuaV6.TpalEalE.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.K8alE.L.bnvbnw.Og.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.v4.G0.MK#Hzbnxbnybnzbn#bn.bnAbnBbnC.qe.fS.qc.mf.qc.qc.rW.rV.rW.qc.q6bnDbnEbne.TQ#xE#CA#CA#CA#CA#CA#CA#CA#CA#CA#CA.TQ#bz.lw.Y6.nt.g9.g9.#6.#6.#6QtK.i8bnF#xE#CA#xE#CA.aY.Qt.g9.g9.#6.#6.#6.#6.g9.#6.#6.#6.#6.#6.#6.g9.g9.ow.ep.nt.#6.#6.#6.g9.#6.#6.g9.g9.g9.nt.RS.nt.#6.ko.lw#gN.h2#0h.zI#ls#jN.kp.sX.Oz.se.Xh.zJ#UU.ds.pD.OA.mC.5U.g..lD.zJ.lD.lD.lD.lD.zJ.lD.zJ.zJ.Vt.zJ.zJ.lD.zJ#J7#dk.ja.j2#IH.qzbnGbnHbnI.Oq.Do.M4.Bx.Bt.BA.Bq.zA.zC.YL.P5.ya#X0#Ez#46#2XbnJbnK#Cs#uw#xr#xr#xr#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#Ag#zs#xq#xq#HL#wvbnL#vv#vw#TP#W.#Wa#Jo#Jo#F1#SabnMbhNbkObkNbg1bg1bnN#PKbnObnPbnQbj6bnRbnSbiDQtV.#sQtkQtV.#t.#uQtVQtVQtk.#s.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bObnTaqSaqSaqSaqSaqSa#Ma#Ma#Ma#Ma#NQtW.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q#I7QtVQtV.bO.c8#bi#c4#c4#5KaqSaqSaqSa#MaqSaqSa#Ma#Ma#Ma#Ma#Ma#MaqSaqSaqSaqSa#M#8pbdn.zEb#Eb#Eazqb#E.YK.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtbQtbQtbQtbQtbQtb.bW#Ut#F2#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#Ut#3V#3V#3V#3V#tt#5GaOU#3V#Ut#Ut#3V#3V#3V#5G#3VblVbnU.YG.K8.Jc.Jc.14.14.14.Jc.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14a61bnVbnW#CK.EV.EV.EW.EW.EV.ET.G0.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.ET#CJ.o9bnXbnYbnZbn0bn1bn2bmybn3bn4biN.fS.qc.mf.mf.qc.rW.rV.rW.qebaTbn5bn6.Xfbn7.Vu#CA#CA#CA#CA#CA#CA#CA#CA.TQ#HS#xE.3Y#qs.nt.g9.#6.#6.#6.#6.#6QtK.OF.lp.h2#gX#CA.7T#CA.Jy.mr.#6.g9.#6.g9.g9.#6.g9.#6.#6.#6.#6.#6.#6.#6.g9.ko.#6.g9.#6.#6.g9.g9.nt.RS.Qt.#6.ow#gN.2r.2f.ro.kn.sX.sd.0F.sd.sX#sM.tI.9X.tG.54.lx#wK.9V.YU.Vv.Vv.9F.9F.YU.53.7T.op#j2.g..Oz.MV.lD.sX.lD.Vt.Vt.Vt.Vt.zJ.lD.lD.vh.RM.ac.kx.j2#dk.re#vB#3a#cY#7M.Lb#7U.Jm.Fm.JG.Bq.zy.17.Fp.yb.MP.zF.ydbn8bn9bo.bmVaZx#wx#xr#zs#xr#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#xq#wwbo#boabobboc#I7.dfbodboebof#Jo#Jo#Jo#Jo#W.bogbkObhObhNbj8boh#K7#L3#Imboi#Inbojbokbolbom.yV.#u.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.c8atOaqSaqSaqSaqSaqSaqSaqSaqSaQT.w0.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#t.bO.bOQta.#tQta.bOQta.aFQtZ.#T#c4#c5.w4#ETaQTaqSa#MbiEasVbalbalbl8a#Mbon#Wj#4f#8pa#Ma#Ma#Ma#M#8pb#Jb.4b#Eb#Eb#Eb#E#dh.#F.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtbQtaQtbQtbQtbQtbQtb#vE#ol#c4.w4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#3V#3V#3V#3V#3V#3V#tt#tt#tt#tt#3V#3V#3V#3V#3V#3Vboo.Tp.K8.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14aij.TpbopaYiboqborbosbosbotbou#B3#P#.I9.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.EV.MKaZHbovbowboxboybozbmybn2bmyboAboBbiN.mf.qc.mf.qc.rX.rW.rW.rW.qe.qeboCboD.cXbn7.Vu#CA#CA#CA#CA#HS#HS#xE.9V.TP.Ou.sf.cg.g9.g9.#6.#6.#6.#6.#6.#6.g9QtK.sO#e8#HS#by.dw.oEQtG.bH.g9.#6.g9.#6.#6.#6.#6.g9.#6.#6.#6.#6.#6.#6.#6.#6.g9.g9.#6.nt.mr.qr.ko.cW.h2.54.sY.g..Vt.vh.zJ.Xd.sW.roQtJ.2n#wK.YV#vN.BM.BM.BM#is.YV.YV.YV.YV.YV.YV.YV.YV.YV.YV#vN.BM.BM#wK.tG.op.ja.Vt.Vt.zJ.Vt.Vt.Vt.Vt.Vt.zJ.zJ.sd.OU.lx.lD.zJ.zJ.zJ.Vt#f#.r1.vsboE.Ju.Jr.M4.Jm.Lp.P6.zC.zs.17.zsboFboGboHboIboJa8RbjQ#xr#xr#xr#xr#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#Ag#ww#xq#wwboKboLboMQtV.#u.#tQtVQtVQtV.#t.ctboN#F1#5C#Jo#Jo#F1#SbboObfyboPboQ#JXboR#xu#F0#F0#Im#Im#F0boSboTboUboV.yVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQta.#Q#Lm#8paqSaqSaqSaqSaqSaqSaqS#be.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQta.t1#c4#Iv#be#beajP#J6akaavSa#NavS#Sf#c5#c4a#Na#Ma#Ma#MboW.MPb#Eb#E.FpboX#dhb#E.YL##xbgmaqSb.Vb#Dbdf#dhb#Eb#Eb#Eb#Eb#E#7G.au.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtbQtbQtbQtbQtaQtbQtbQtW#W9bkr#3V.w4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#c5#c5#c5#c5#c4#c4#c5#Ut#3V#3V#3V#3V.#p#3V#3V#3V#3V#3V#3V#3V#3VbllboYaiiboZ.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14.14.14.Tq.Jd.PZbo0aOwbo1ajAbo2aJhbo3bo4.dh.G0.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.Og.v4bo5bo6bo7bn#bn#bn2bmybn2bn2bo8bn3al2aIB.fS.qc.mf.q6.rX.rW.rW.qc#3dbo9bp..epbp#.RR#CA#CA#CA#HS#CA#II#rr#bC.g8.cS.#6.nt.nt.qr.g9.#6.#6.#6.#6.#6.#6.#6.#6.JL.lp.cg.j#QtK.#6.#6.#6.g9.#6.#6.#6.#6.g9.g9.#6.g9.#6.#6.#6.#6.#6.#6.#6.#6.g9.mrQtK.lw.pM.ck.sc.zJ.lD.sc.eo.fe#Db.#8#gN.LB.0A.33.BM#vN.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV#is#Bd.a3.aa.9P.vh.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.Vt#bx.9X.Vt.Vt.zJ.zJ.zJ.lD.Vt.r1#lvaJL.Lqawd.Bzbpabl7.mA.r0aRbbpbbpcbpdbpeboIbo.a8RbjQ#xr#xr#xr#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#xq#HK#QrboMbocQta.#tQtV.#Q.#Q.#Q.#Q.#Q.#QQtVa8fbpf#WY#Jo#Jo#Jo#F1#WW#Vw#WW#WW#F1#WW#xt#0L#HH#KH#F0#F0#wBbpgbphbpibpj.miQta#I7.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.gV#bi#Xx#KqavS#5KavS.w4.#QQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta#ETaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#M#8p#5Kbkbb#JaPV#HN.Jlb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#E.JlbcQ.OI#dh.17b#Eb#Eb#Eb#Eb#E#9A#OW.G0.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtbQtbQtbQtb.bOQtbQtb.#R.v#.Jl.EX#ol#c4.L##c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#c5#c5#c4#c4#c4#c4#c4#c4#c4#c4#c4#Ut#3V#3V#3V.#p#3V#3V#3V#3V#3V#3V#3V#3VblmbpkaLW#9q.K8.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.Jc.K6aV6aV6bnuakFaOwbplbpmbpnbpo#Awbpp.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.Tm#HAaDcbpqbprbpsboybptbn2bpubn2bn2bmybpvbpwbpxadv.fS.qc.mf.qc.rX.rW.qeaOl.sBbpybpza8a#Ld.uo.sf#QC#CA.Vv.LB.lwQtF.nt.nt.Qt.g9.g9.g9.g9.g9.g9.#6.#6.#6.g9.#6.#6.#6QtK.yj.lv.g9.oq.yk.sM.Lt.i8.g9QtE.h3QtK.g9.g9.g9.#6.g9.#6.#6.#6.#6.g9.g9.RS.nt.ow.oD.bI.lD.zJ.OU.2n.LB.oEQtF.nt#Me#zA.bG#by.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.TF.oD.dt.Vt.Vt#tK.Vt.Vt.Vt.Vt#tK.Vt.mC.aa.zJ#tK.Vt.Vt.Vt.zJ.zJ.sX.lD.Xh.jc.pCbpAbpBbpC.JG.BzbpDbpEbpFbpGbpdbpHboIbpIbpJbjQ#xr#xr#xr#xr#zs#zs#zs#zs#zs#zs#Ag#ww#wwbpKbpLQtaQtaQtV.#s.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtabpM#It#WY#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Sb#TR#V8#HH#F0#F0#F0#PKassbk.bpNbpObpP.mi.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQta.#tQtV.aFQtW.aFQtaQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.#F#5KaqSaqSaqSaqSa5da#MaqSaqSa#Ma#MaqSbgmbpQblg.Fpb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#E#W9#I7.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+".bOQtaQtbQtaQtbQtbQtb.bOQtb.W2auG.JlbgmaqR#c4.w4#c4#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#Ut#3V#Ut#uH#uH#uH#uH#uH#uH#Ut#Ut#3V#3V#3V#3V.#p#5G#65.#p#3V#3V#3V#3V#Ut#3V#KqbpRaigaihaKj.PZ.14.14.14.PZ.14.Jc.14.PZaeJ.14.Tq.14.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14.K7.PZaYlbci.G9bpSaD3bpTbpU#sB.EW#CJ.EW.G0.x9.w2.w2.w2.w2.w2.w2.w2.r.af2bpVbpWbpXbozbpYbozbn2bo8bn2bn2bmybpvbpZbp0bp1bp2#Qb.qc.mf.mf.qe.rW.q5.qcbp3bp4.mr.34.uB.Qk.nt.lG.tK.cW.cS.nt.qr.g9.g9.#6.#6#qs.bHQtE.h3.h3.cl.k#.lGQtE.clQtEQtF.#6.#6.qn.oq.lp.kf.kg.ns.7K.MZbp5awebp6.wgQtE.#6.g9.#6.g9.#6.#6.g9.nt.mr.rA.cTQtJ.mC.sc.OU#ZA.epQtF.sQ.2c#qs.7X.Q##by.Vv.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.0A#bx.0v.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.9F.mC.sd.Vt.Vt.Vt.Vt.Vt#tK.zJ.lD.zJ.bK.jc.9Tbp7bp8bp9bq.bq#bpEbpFbqabpdboHbqbaCCbqc#zs#zt#xr#xr#zs#zs#zs#zs#zs#zs#xqbqdbqeQtVQtaQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.aF.hLbqf#3p#5C#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#F1#XO#F0#KH#F0#Im#PKbqgbqhbqibmXbqj.#u.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.fMQtVQtVQtVQtV#I7.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bOaBw#bg#5GaqSaqSaqSa#MaqSb.Va#Ma#Mb.V#4fbal.zr.17auG.Fob#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#E#3I#ts.qZ.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtbQtaQtaQtbQtbQtbQtbQtaQtb.#Pblg.Fo.OIbqkbdn.Tt#13.w4#JR#c4#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c5#c4#c4#Ut#3V#tt#tt#3V#3V#tt#tt#3V#3V.#p#3V#3V#3V#3V#3V#3V#3V.#p#5G#tt#tt#3V#3V#3V#3V#UtblVavObqlbqmbqna61aLsbqoaI7.Ojaijbqpbqqbqrajj.Tqaij.Jc.Jc.Jc.Jc.Jc.PZaeJalEbqsbqt#P#.EV.EW.EW.EW.EW.EV.G0.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4af2bqubqvbqwbqxbqybnAbo8bn2bn2bn2bmybpvbnAbqzbqAam8bqBbiNbiN.mf.mf.qc.mfaOlbqCbqD#r3a73.dw#xEbqE.pt.#6.g9.Qt.nt.g9.g9.g9QtKQtEQtK.pt#b#bqFbqGbqHbqI#1Bav5av5bqJbhf#LG#e5QtEQtE.sQ.2c.ot.MZ.kf.psasjaPV#bi#c3#bg#jQbqKQtFQtK.g9.g9.g9.2c.g9.ep.7T.kn.a5#in.Y1atk.oP.nt.sQ.g9.um.LB#0y.Vv.9V.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.7Y.9V.OU.Vt.Vt.Vt.Vt.Vt.Vt.se.TO.dt.vh.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD.lD.0F.if#ZXbqLbqMbqNbqObqPbqQbqRbqSbqTbqUboIa8RaZx#zs#xr#xr#xr#zs#zs#zs#ww#xqbqVbjm.#uQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#u#L5#It#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Sb#JXbdQ#F0#F0#F0boiaeQbldbqhblGbqWQt#.#t.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.#Q.w4#ETa#MaqSaqSa#Mb.Vb#Ja#Ma#MbonbqXbf5bf5bqY#USb.4.Jlb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#EbqZ.P5.#p.w2.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+".w2QtbQtaQtbQtbQt.Qtb.miQtbQt..fJ.YL.zv#QA#0g.17.P5#qc.#l#c5#JR#c4#c5#c5#c5#c5#c5#c5#c4#c4#c4#c5#Ut#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#3V#65#3V#3V#3V#3V#Ut#Ut#3V#3V#3V#3VblmaHebq0bq1bq2bq3bq4bq5bq6aw6bq7ab4akBbq8aijaij.Jc.Jc.Jc.PZaeJailavDbq9br.br#.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.x9#GRbrabrbbrcbrdbrebrfbo8bn2bn2bn2bmybrgbmybrhbri.#M.d#brj.mf.mfa#z.mf.qc.pc.rWbrkbrlbrmaEb.OF.Xe#CA.#8.2c.#6.#6.g9.#6QtFQtEQtF#vCbrn#Wi#NV#xz.#l#bk#uH#Us#13#c5aoz#FDaBw.dh.Vhbrobrp.MX##E.oP.Fu.YPa9Saix.W2#7S.YK#Ut#bg#J6#Vybrq.g9.mr.bH.pM.eo.op#0hbrr.i6.lv.sQ.g9.nt.#6.uz.pM.Vv.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.33#bx.zJ.Vt.Vt.Vt.Vt.Vt.Vt#wK.dv.se.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.lD.7U.igbrsbrtbrubrvbq.bqPbpEbrwbrxbrybrzboJbrA#uw#xr#xr#xr#xr#zs#ww#wwbrB.edQta.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bObmU#3p#Wa#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#XI#6v#wB#KH#F0#Im#PKbrCbg1bg1brDbj6.bVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO#m8#c4#5Ga#MaqSaqSaqSa#Ma#MaqSa#Ma#Ma#Ma#Ma#Ma#Ma#Ma#Mbasata.YLb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#Eb#E#3Hb#E.D..Tt.aw.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"Qt.QtbQtbQtbQt.QtbQt.QtaQtV.bOQtk#qc#3H#3I.zt.zC#4y#wC.17#0g#5H.w4#c4#c5#c5#c5#c4#c4#c5#c5#vE#Ut#3V#3V#3V#3V#Ut#3V#3V#3V#3V#3V#Ut#uD#a9.w.#a9.fJ#uD#5Gaoy.fJ.zG.fJ#Ut#3V#Ut#3V#3V#Ut#3V#3V#KqbgybrEblVaqRbllbrF#3VbrGbrHadfab5agCaeY.PZaeJaeJ.14aI9brIafbab4anlbrJ#CJ.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r.axfbrKbrLbrMbrNbrObo8bn2bn2bn2bn2bmybrPbrQbrR.#M.#MbrSbrTaaL.mfa#z.qc.mfarFbrUbrVaLcaxk.cvbrW.bH.rB#CA#gN.Qt.g9QtK.bH.j..HLbrX#BUaG7#13.#kaoM.OI.D#.Jl.D#.D#.D#.zE.D.#0g#8C#YM#tt.#Q.bu.JkaxxbrY#eZbrZ.P5.D##eP.YL.YL.OI##w#uD#bebr0#P2.2n.mWbmG.Ll.r7.kb.i1.#6##E.g9.g9.ow.tK.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#vN#wK.0v.Vt.Vt.Vt.Vt#tK.zJ#q#.kp.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ#tK.zJ.lD.sd#Pk#27br1br2br3br4br5bpEbr6br7bryboIbo.brA#SX#ww#zt#wx#Ag#wwbrBQtV.#t.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.#vbr8#TR#TR#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#F1#WT#F0#F0#F0#PKbr9bg1bkObg1bjibs..yVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtkQtkQtkQtk.ai.#F#SfaqSaqSaqSaqSaqSaqSaqSb.VaqSaqSaqSaqSaqSa#MaqSa#Ma#M#8pbeWbcQb#E#3Ib#Eb#Eb#Eb#EauGbqZ#eP#dh#W9.4b.t1.r..r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"QtbQtaQtbQtbQt.QtbQtb.bOQtVQtV.bO.vc.P5.Fo.17.Fo.zC.Fo.zC#4y#HN#5H.w4#c4#c5#c5#c4#c5#3V#3V#3V#3V.#p#3V#3V#3V#3V#3V#uD.#q.#q#ttbs#bsabsbbscbsdbsebsfbsgbsgbshatobsiaB7.zG.bd#3V#Ut#3V#3V#3V#3V#3V#3V#UtaqR#Ut#UtaqRbsjbskbslaniaLkankbsmbsnbsobspbsqbsrbssbstbsubnw.v4.v4.w2.w2.w2.w2.w2.w2.x9.x9.w2.w2.w2.w2.G0a8P.uNbsvbswbsxbpwbmybn2bn2bn2bn2bmybsybjDbsz.d3adJ.czbsAbsB.fS.q6.mf.qc.fT.q5bsC.syaGx.r..v4.bWbsDQtE.TP.Xe.oPQtK.bH#e7#9MapW.YK#2R.W2.zp.17.17.D#.YL.YL.YL.YL.YL.YL.YL.YL.YLaGm.YL#eP.zE#uDaxkazr.bu.qZ#uH#dh#0g#8z#m5#uK#1D.D#bsEaAo.#5.50bsF.tw.zK.sM.2cQtK.#6.g9.ntQtF.Q##is.Vv.Vv.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.0A.bG.TP.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.r#.Vt.Vt.Vt.Vt.Vt.0v#Dd.9U.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.lD.vh.if.rtbsGbsHbsIbsJbsKbsLbsMbpdboHboIa8RaZx#ww#xr#wx#wwbqV.edQta.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q#I7.#vbsN#Vw#WY#5C#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#JoaOA#XIbsO#wA#F0#F0#wBbsPbg1bkNbkObhObsQbsR.#u.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#s.#t.#ubiDbiD.#uQtV#18bjmbjm#18.df#bg.Jk#2Rbcfa6FasVaqSaqSaqSaqSaqSaqSaqSaqSaqSa#Ma5daqSa#Ma#M#8pb#J#ODb#EbgK#Ez##s#bb#9A.al.Jk#m8.w2.r..G0.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+".dX.aiQtaQtbQtbQtbQtbQtbQtaQtVQtV.bO#V0b#EbsSbsS.Fo.Fo.zC.zCbsS#bbaqR.w4#Lm#c5.w4#c4#3V#3V#3V#3V#3V#3V#3V.#q.#q#ttbsTbsUbsVbsWbsXbsY#0JbsZ#Vsbs0#xtbs1bs2aDA#L7#JNbs3bs4.bd#3V#3V#3V#3V#Ut#Ut#Ut#Ut#Ut#Ut#Ut#3V#Ut#Utbs5bs6avHbslbs7bs8bs9bt.bt##c5btabtb.EW.EW.ET.w2.w2.w2.w2.w2.w2.v4.v4.v4.v4.v4.v4.v4#GR#yMaJ3btcbtdbrObrgbn2bn2bn2bn2bmybpvbtebtfannadJ.#L.czbrSbtg.fS.q6.qc.mfasOa.Wbth#H3aby.w2.v4.v4.#TbtiQtE.sV.g9QtF##Cbtj#Ez.Tt.Qf.EX.Fo#4y.17.YL.Jl.YL#eP.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.YL.D#.4bakmQt1#uHaoLaoL.vb#bj#c5##rbtkbtl.MW.HM.rn.ns.rh.i4.g9QtK.#6.#6.g9.g9.uz#e1.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.aYaK0.3Y.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.53.Xd.Vt.Vt.Vt.se.h..qy.se.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.zJ.lD.W9.gX.kpbtmbtnbtobtpbtqbqRbpGbtrbrzbqbbrAbjQ#ww#xrbm.bts.#u#I7.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.qZ.#ubtt#4Z#JW#Jo#JW#8n#8nbtu#JW#3p#8n#JW#Jo#5C#Jo#Jo#Jo#Jo#Jo#Jo#Jo#W.#JZ#wA#F0#Im#PKbtvbqhbkNbkNbjibjjbtw#puQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtV.#u.#v#18btxbtybtz#XFbtAbtBbtCbtCbtDbtEbtFbtGbtHbtIbtJa5dbcfasVaqSaqSaqSaqSaqSaqSaqSa#Ma#MaqSaqSa#MaqS#8pbqX.Jl#dh.RI#P##OW.dh.r..r..G0.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"btK#6k#dWblI.c2QtbQtbQtb.bOQtVQtV.bO#m8.y##3Hb#Eb#E.FobsS.zC.Fo.YL##w#c4#c4#c5.w4#Lm#3V#3V#65.fJ#a9.#paqUbtLa3PbtMbqc#yp#xq#vx#xr#xr#HK#HK#yu#vw#Ct#CtaJE#Zq#F0#F0#PJ#JNbtN.#.#tt#3V#3V#Ut#Ut#Ut#Ut#Ut#Ut#Sf#3V#Ut#3V#Ut#3VbtObtPbtQ#KqbtRbtRblm#Kq.Du.sq.G1.v4.w2.w2.w2.w2.w2.v4.v4.v4.bO.qZ.bOQt#.v4#IXbtSbtTbtUbtVbsybmybn2bn2bn2bn2brgbrPbtWafk#Lq.#L.aA.#M.cAbtX.ht.qc.qc.q5.fTbtYbtZbt0#XD.w2.w2.r.Qt..16#1XQtF.g9QtK#bm#Ys.ya.zt#wC.He#wC.zC.17.D##dh#X0#uK#uK#X0#46.HH.MP.D..P5.zq.zq.zq.zE.YL#eP#eP.YLa6E.Du#Utap7#bc##xbt1bt2#sx#NI.vj.kg.kg.kj.ln.#6##E.#6.g9.g9.nt.ko.Xe.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT#jT#jT.TO#jT#jT.Vv.Vv.YV.YV.YV.YV.Vv#jT#jT.TO#jT#jT.Vv.Vv.Vv.Vv.Vv.Ou.ko.aY.Vv.Vv.Vv#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.33QtJ.zJ.Vt.sd.MV.0A.tI.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.Vt.lD.zJ.hhbrsbt3bt4bt5bt6bq#bpEbt7br7bt8boIbpIbt9#ww#xqbu..#uQtk.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#ubnrbpf#3p#Wa#3pa8Rbu##KK#2Ibua#19bubbuc#WY#Jo#5C#Jo#Jo#Jo#Jo#Jo#F1#F1#0L#F0#F0boiagvbizblCblBblBbudblCbue#32.dXQta.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#s.#t.hLbiD.ctQtVbjmaD#bufbtD#0M#Io#7l#Io#Io#JU#JU#JU#JU#JU#JU#Io#L8#HHaBHbugbuhbui#m5asVaqSaqSaqSaqSaqSaqSaqSasVaqSaqSaqSa#M#8paPVauG#YM.r..G0.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"bujbuk#V7bulbjm.bibnS.bV.mi.bOQtVQtV.bO#Us#1D.P5bum.17.Fo.Fo.Fo.Fo.Fp#8z.L#.L#.v..aq#a9ayVbun#Jibuobupbuq#SX#UI#vx#xr#xr#zs#Ag#xr#xr#HK#xr#xq#Rg#Iu#UJaUr#Eu#F0#F0#KH#L8#Jn#zw.v5#bg.vb.w4#bg#bc#bc.vb#bc#bg#bg#bi#be.#F.#T#Nk.dI#Iv#bg.Du#c4.w4.w4#c4#bg.qZ.v4.v4.v4.v4.v4.v4.dhQt..qZ.c8.c8.Jj#O7.n1burbusbutbnabmybn2bn2bn2bn2bmybpvbuubuvbuwach#Ydbux.#M.#M.#M.hu.fS.qc#OHbuybuz.aiaLc.w2.w2.w2.v4.r..c8ajQ#ifQtK.g9buA.rq.zu#wC#wC#wC.He.He.zC#NV.16.RI.fX.aw#bi#c5#c5#c5#uH#uH.dI.#F.#E#a4.RI.Du.4b#uD#7G.Qf#vE#7GbuBbuCbuD#de.kc.kg.x#.MZ.kj.ptQtKQtK.#6.g9.g9.#6.nt.cg.TP.Vv.Vv.Vv.Vv.Vv.Vv#jT#jT#jT.Vv.BM#e1.tK#Iy.ep.h6.2q.pN#j5.qi.cW.a3.rB.a3.2m#Tr#gC.r2.ow.rv.mD.TP#jT#jT.Vv.a3.2c.cW.Vv#jT.Vv.qE.e6.cT.BM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Xb.sW.Vt.sd.ro.TP.zJ.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD.vh#Pk.igbuEbuFbuGbr4buHbuIbuJbpdbrzbqbbsX#uw#AgbuK.#vQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#tbuLbuM#3p#5CaV3bmU.edbiD.hL.hL.hL.#u.ct#7dbuN#F1#WY#Jo#Jo#Jo#Jo#F1#W.#1q#HG#HG#HGbuObkCblCblCblBblBblBbuPbuQ#PKbuR#6kbiDQtV.#Q.#Q.#Q.#Q.#QQtkQtkQtk.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtk.#ubiD.ctbuSaCxbuTbuUbuVbuk#Gx#7l#JU#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KHbuW#InbuXbuYa#Ma6FaQTaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#Ma#M.MP.P5.#Q.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"buZbu0bdh#JN#ZobgSbu1aCGbu2boc.#u.#v.dX.bV.vb.fIbu3#hsaxqbu4.ga.ga#C7.P5bu5bu6bu7bu8bu9bv.a#Kbv#bva#vx#xr#xr#HL#zs#zs#xr#xr#xr#W##0J#34#3q#WWbvb#FV#JN#wA#wA#Im#F0#KH#LC#2JQta.cuQtkQtV.aF.aF.#Q.aFQtk.aFQtV.aFQtk.aKQtk.#Q.c8.aI.c8.c8.c8.c8.#Q.c8.c8.c8.c8.aF.cuQt#.bOaBw.cu.aF.c8.cv.#B.cuaF6bvcbvdbvebrfbrgbn2bn2bn2bn2bo8brgbn2bvfbvgbvhbvibvjbvk.iK.bo.czadIbvl.ndabibvmbvnaGx#Hz.w2.w2.w2.v4.r..qZ.w1#e9.2cQtK#yAbvobvp#wC#wCbvq.EX#NV#7G.Vh.w2#OW.#E.w4#uH#Ut#Ut#uH#uH#uH#uH#F2#Iv.w4.w4#bg.3UQt..Tm#m8#eY#9wayp.kh.ps.kf.ns.kf.kg.kh.yj.#6.#6.#6.#6.#6.#6.#6.qr.rA.Xg.Vv.Vv.Vv.Vv.YV#Bd.3Y.Q#.oEbvrbvs.9W##E#NI.9W#EF#zA.2m.a3.tK#e1.BM.YV.9F.YV.BM.Xg.TH.RR.cW.W8.wf.rm.TCaGi.cg#DN.TH#LE.aY.cgadY#m3.rz.0A.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TP#yE.zJ.lD.0A.dv.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.hhbrs.Jzbvtbvubvvbvwbvxbt7br7bt8boIaBW#Rgbvy#6k.ai.bOQtk.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#u#WR#4Y#3p#3pbmUbocQta.#Q.#Q.#Q.#Q.#Q.#Q.#t.#ubvz#3p#5C#Jo#F1#F1#WW#WU#Je#KH#FXbvAbvBblCblBblBblBblBblCbvBbuO#HG#IobvCbvD#18.ct.hL.#u.#u.ct.qZboc#I7.#u.hLQtaQtV.#QQtV.#t.hLbiDbnsaC5bvEbvFbvGbizbvHbkNbvI#8c#KH#FY#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#HG#FY#FY#KH#KH#KH#KH#KH#HH#JNbvJbvK#6ya5daqSaqSaqSaqSaqSaqSaqSaqSaqSaqS#8pboW#3I.90.Tm.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"bvLbgFbvMbvNbvObvPbvQ#JUaz6bvRbvSbvTbvUbvVbvWbvXbvYbvZbv0bv1bv2bv3bv4bv5bvSbv6#JoaUr#UH#UI#zt#xr#HL#zs#zs#zs#zs#zs#zs#xr#xr#Vv#8n#xtbtu#8n#3p#F1#39bv7#G3#Fk#LC#LC#LC#xubks.dW.aF.aF.aF.aFQtkQtVQtVQtVQtV.aFQtk.aFQtk.aF#I7ayiawBayi.df.c8QtW.#Q.aI.aI.aI.aI.aF.c8.c8.cv.c8.c8.c8.aI.qZbaCbv8bv9bw.bw#brPbo8bn2bn2bn2bn2bmybpwbwabwbbwcbwdbwebwfbwg.jU.b1.b0am8bwh.fT.q6bwibwjaGx.v4.x9.v4.v4.v4.v4.fX.w4#bgbwk.h3bwl#VgbsS#wC.zC.zo.Vh.t1aze#P#.gV#bg#uH#Ut#Ut#Ut#Ut#Ut#Ut#Ut#Ut#Ut#Ut#Ut#Ut#uH.w4#3V#ODbwmbc.atb.ps.kh.mw.kf.mw.kf.ns.rg.JLQtK.#6.g9.#6.#6.#6.g9.g9.qi.oD.YV.BM#by.7Z.qE.tK.mD.bG.a3.#8.rB.Xg.BM.YV.Vv#jT.TO.TO#jT#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT#jT#jT.YU.oE.7O.HK.tv.sO.JB.rB#NI.Sdbwn#Uk.Xg#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.bI.0F.w9.Xb.Vt.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD.vh.gj.OAbwobwpbwqbwrbuHbwsbwtbpdbrzbqbbwubvybwvQta.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtabuKbmTbwwbwx.qZ.#t.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.aibwy#Vw#F1#Jo#JW#V9#Eubwz#KH#HG#OcbwAbwBblCblBblBblBblBblCbwCbwD#FY#KH#JU#OcbwEbwFbwGaCIbwHa6SbtAbwEbwIbwJa0PbwK.dWbiDQtVbwL#KlbwMbwNbwObwPbiAbwQbwRbwSbiz#OcbwT#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#HG#8c#8c#HG#KH#KH#KH#KH#FY#KH#L8bwUbfCaBvaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#MbiEblg#46.w2.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"aukbwVa#gbwWbwXbwYbwZbw0bw1bw2bs1#9k#xwbw3#Ir#L8aCeahMahMaCeaCebw4#8maFEbw5#UJ#vx#wx#zt#HK#xr#xr#xr#HL#zs#zs#zs#zs#zs#xr#xr#1rbdgaCxboNbw6bw7bw8bw9.fXbx.boI#LBbx#bxabks.dZQtkQtVQtV.#Q.#Q.aF.aFQtk.qZQtV.aFQtk.#Q.sqazKbxbbxcbxd.9zbxe.#B.t1.c8QtW.aI.gV.c8.c8.fX.c8.c8.c8.c8QtW#PPQt1bxfbxgbxhbxibmybn2bn2bn2bo8bn2bxjal8am4bxkbxlbxmbxnbxobxpbuw.#M.cAbxqbrTbxrbxsbxt#Hz.v4#OW.dh.dh#OWQtV.gV.vc#c5#Ut#bibxuQtEbxvbxw#wCaEba#VaEZ.Tm.w2.#T#c5#Ut#Ut#Ut#Ut#Ut#Ut#Ut#Ut#Ut#uH#c5#c5#uH#uH#c4#uHaEHbxx.mw.qo.ps#ik.mw.kf.mw.mw.kf.ns.mt.j.QtK#gC.g9.#6.#6.g9.g9.cS.wn.TF.oD.BM#e1.7Z.TP#vN.YV.Vv.YU#jT#jT#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.YU.Vv#j5.yk#EF.qq.vj.vj.Ov.LB#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.TP#bx#P3.ro#Db.5U.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.lD.zJ.mXbxybxzbxAbxBbxCbxDbpEbr6br7bxEboIbpI#Rgbtt.#u.bO.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#tQtV.qZ.#uQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#t.dfbxF#xwbxGbxHbxI#wB#KH#KH#FXbxJbvBblCblBblBblBblBblBbwBbxKbxL#FX#KH#FY#FX#HG#Io#JU#8c#HH#Io#Io#JUbuW#HH#HGbxMbxN#MObxObxPbkPbiAbiAbhNblCbxQbizbxRbtvbxS#8c#FY#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#KH#HG#8c#8c#HG#KH#KH#KH#KH#KH#KH#KHaCebxT#m5a#MaqSaqSaqSaqSaqSaqSaqSaqSaqSaqS#8pbxU#3I.vc.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"afaab4bxVbxWaR8bxXbxYbxZbx0bx1bx2bx3#B.bvabx4buW#HH#F0#F0#F0#PKaV3bl#bx5#zrbx6#zr#wx#xq#zs#3r#zs#xr#Zp#zr#zr#Zp#xr#zu#ww#xq#vwbjm.Wg.c3.dW.dW.dWQtk.s7.o9.wD.c3.qZ.#u.dWQtV.#sQtk.aF.#sQtV.aFQtVQtkQtkQtVQtV.fM.sqbx7bx8.RG.L.be2aFdbx9QtV.c8.c8QtW.#Q.c8.c8QtW.c8.c8#FD.c8.qZaFZby.au2by#byabmybn2bn2bn2bn2bo8byabybbycbydbyebyfbygbyh.iLbyi.#M.#M.cAbyjbyk.q4bylbym#Ne.bOQtVQtaQtaQtZ#c4#c5#uH#Ut#uH#uH#bibxubyn.Fobxw.Vi#c4aEZ.r..w2#Iv#Ut#Ut#Ut#Ut#Ut#Ut#Ut#Ut#uH#uH#c5azM#xz#eY#3V#uH.QfawP.kh.ps.mw.mw.mw.mw.mw.mw.mw.kf.kg.OT.g9.#6.g9.#6.#6.#6.#6QtK.sQ.OF.r2#Iy.TP.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#Iy.ms.r2.Qk.Dy#NI.bG.YU#jT.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.TO.mC.mC.ep.9V.se.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.lD.lD#J7byobypbyqbyrbysbytbyubyvbywbrybyxbo.byybyz.dW.cu.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtkQtk.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.qZ#BRbyAbyB#XO#K6#8c#KH#KH#HGbyCbvBblCblBblBblBblBblBbwBbiz#HG#HG#KH#KH#KH#KH#KH#KH#KH#KH#KHa9d#KH#KH#KH#KH#HH#F0#L8byDblCblCbyEbnQbuQbyFbyG#8c#ImbwT#Gx#FU#Je#Je#Gw#NsaCh#GwaBH#FZ#FX#FY#FY#FY#FY#FY#FY#FY#KH#KH#KH#KH#KH#KH#KHa9dbuWbyH#6yaBvaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#MaqS.Jl#7G.Tm.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"aimaiqaeYbyIavFab2a#xbyJbyKbyLbyMbyNbyObyPbyQbyR#HH#L8#JN#Fibw3bySbyTbyUbyVaNabyW#DIbyXbyYbyZ#BQby0by1byWby2by3bsY#Vuby4boLby5.aFQtk.aF.aF.aF.aF.#Q.aI.aIQtW.aFQtkQtk.fM.fM.fM.#Q.#Q.#sQtV.aF.fM.#Q.aF.#s.#Q.sqb#Vby6by7aV6aUhby8by9awB.c8.c8.aIQtW.t1.c8QtW.aI.KE.c8.aI.w2bz.bz#bzabqybmybo8bn2bn2bn2bo8bn2bzbbzcbzdbzeaaxbzfbzgbzhbyibuw.aA.#M.#Mbzibzjbzkbzl.bO.G0QtVQtaQta.bO.5Q#c5#uH#Ut#uH#uH#uH#uH#bg.17#rF#1D#3V#rj.qZ.r..r..#P#Ut#Ut#Ut#Ut#Ut#Ut#Ut#uH#c5#uHa#V#uK.OI.YL.D#.zpbzmatb.ps.kf.kf.mw.mw.mw.mw.mw.mw.kf.kga1X.Y6.#6.g9.g9.#6QtK.#6.OLa1X.lv.2m.BM.Vv.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#vN#m3.rm.Qk.2c.Jy.BM#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.YV#j2.lD.es.rv.9V.kn.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.vh.pEbznbzobzpbzqbzrbzsbztbzubzvboHbo.bzwbvybzx.#u.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO#6k#2Jbzy#WT#K6#8c#KH#KH#KH#FXbxLbzzbwBblBblBblBblCblCbwCbzA#FX#Im#KH#KH#KH#KH#KH#KH#KH#KH#KH#HG#KH#KH#KH#FYbwTbzBbzCbk9btvbzDbzE#KH#Im#Imbwz#xubzFbzG#WT#Ns#G3#K6#OcbxLbzHbzIbzJbzKbzLbzMbzMbzNbyFbxJ#8c#FY#FY#FY#KH#KH#KH#KH#KH#KHaaybzOb.VatNaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#Ma#MbcQ.Sl.r..r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+".PZ.PZasGasEbzPb.wbzQbzRbzSbzTbzUbzVbzWbzXbzYbzZbz0bz1bz2bz3bz4bz5bz6#Cp#Qh#TD#Qh#Qh.cN.cNbz7bz8#Qhbz9bA.bA#bAabAbbAcaDb.c3.vK.c8.aF.aFQtk.aF.aF.#Q.aF.aF.#QQtV.fM.#Q.fMQtk.#Q.#QQtV.aF.#QQtkQtk.#Q.aF.#s.fM.sqa2sbAd.9zbAebAfayiayi.c8.gV.#Q.aI.aIQtW.c8.c8.aI.td.1wbAgbAhbAibAjbAkbpubn2bn2bn2bn2bo8bAkbAlbAmbAnaaxaayagkbAobApbAq.bo.aA.czau9bwhaOlbArbAs#GR.bOQta.bO.bOQtV.#Taka#Ut#Ut#uH#uH#uH#uH#uH#c4.zobxw#c1#c4#rj.w2.r..w2#rj#Ut#Ut#Ut#Ut#Ut#uH#c5.bdaoL#0g.D#.YL.YL.zE#3Ia1a.lq#ik.kf.mw.mw.mw.mw.mw.mw.mw.OC.kf.TYQtK.#6.g9.#6QtK.nt.qn.Vm.ms#lw.BM#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.oE.ms.kl.TH.3Y.Vv.Vv.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.9X.Oz#bx.cW.BM.BM.7T.sX.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.T5#YHbAtbAubAvbAwbAxbAybAzbAAbABbvTaYSbACbADbAE.ai.bO.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtVQtbbAFbAG#K7#wB#8c#KH#KH#Im#Im#PK#FibqgblCblCblCblCbyEbAHbxL#HG#FY#KH#KH#KH#KH#KH#KHbwz#Gw#Gw#K6#HG#KH#KH#FY#HGbAIbAJbyF#8c#Im#FY#FXbzF#4JbAK#Wa#2K#8nbALbAMbANbAObAPbyEbAQblCblCbg1bg1blCblCblCblCblCbAQbizbARbASbAT#HG#FY#FY#FY#KH#KHaBGbAUbAVafqaqSaqSaqSaqSaqSaqSaqSaqSaqSaqSa#Ma#M.OI#V0.Tm.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2",
+"#xj#wpbAWbAXbAYbAZbA0bA1bA2bA3bA3aJAbA4bA5bA6aJzbA7bA8bA9.aV.cN.cN#N9.aV.aV#Lw#N9#TDaL0#X8bB.bB#bBabBbbBc#c4.Dt.fJ#vE.Fr.#F.#QQtV.aFQtk.#Q.#QQtk.aFQtkQtk.#QQtk.fM.#Q#I7.aF.#Q.#Q.aF.aF.#QQtVbBdaJtbaDQtW.#QQtV.qZ.sqbaEbbf.sq.aF.c8.c8.c8.c8.aI.aI.td.Cx#vQbBebBfbBgbBhbBibBjbmybo8bn2bn2bo8bo8bnAbBkbBlbBmagjbBnamLbBobBpbBqbBr.#M.#L.cAbBsbBtaNnbBu#D5#HzQtVQtVQta.bO.c8#bg#uH#Ut#Ut#uH#uH#uH#uH#uH.w4#uD.zC#ts#c4#rE#c4.w2.r..p5#uH#Ut#Ut#uH#Xx#c5#c1#qcahf.YL.YL.YL.YLbBvaiAadZbBw.ls.kf.mw.mw.mw.mw.mw.mw.mw.kg.kh#mD.#6QtD.#6QtK.JY.lo.mtbBx.#8.Vv.TO.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.ow.nt.mD.Vv.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.ac.lDauC.a3.0A.Vv.YV.BM.r#.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.lD.zJ.hhbrsbBybBzbBAbysbBBbBCbBDbBEbBFbBGbx#aRMbBH#6k.bOQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO.cqbBIbBJ#In#KH#Im#Im#Im#Im#Im#Im#PK#HGbBKbhMbwCbtvbBL#FX#FY#HG#HG#KH#KH#F0#0Mbzy#ZrbBMbBN#xu#HG#KH#KH#KH#HG#OcbxJ#HH#FYbBO#7tbBP#JW#vy#0J#wx#R9bBQbsQbBRbvBblCblCblCblCblCblBblBblBblBblBblBblBblBblBblCblCblCblCbwCbBSbBTbBU#8c#HG#FY#wBbBVbBW#5JaQTaqSaqSaqSaqSaqSaqSaqSaqSaqSa#MasVb.4.dK.r..v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r.",
+"bBXbBYbBZ#zobB0bB1#Ab#KC#Lw.aV.aV.aV.aV#BP#LwbB2bA5#MD#Ux.aV.aV#BP.aVbB2bA6bB2bB3bB4bB5bB6bB7bB8bB9#Uq.eQ#bk#Ut#Ut#Ut#Ut#Ut.Du.#PQtkQtVQtkQtVQtkQtV#I7QtV.#s.aF.#s.#Q.fM.fM.fM.#s.aF.fMazKaJtbC.bC#bCabCbbCcQtV.#s.fMQtV.#Q.#sQtV.c8.c8.aI.c8.aK#WHbCdbCebCfa93bCgbChbCibCjbmybn2bn2bo8bo8bmybh7ae1bCkbClaax#HHayA#FYbCmbuxbyi.#M.#M.b0bCnbCo#SAaJ3#Hz#5u.#QQtVQtV.w2.aF.Du#uH#Ut#Ut#Ut#uH#uH#uH#uH#uH#UtbCp.17#bk#3V.zE.0N.r..ed#c4#Ut#Ut#uH#c5.#m#NV.zE.YL.YL.YL.YL.YL.D.a1d#Di.pq.kf.mw.mw.mw.mw.mw.mw.mw.kf.kg.rg##FQtK.#6QtK.JY.Dx.lp#if.mD.TO#jT.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Xb.LB.g8#j5.Xg.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.h..sX.r#.b8.0A.Vv.Vv.Vv.BM.YU.OA.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD#J7.tEbCqbCrbCsbCtbCubCvbztbCwbCxbCybCzbCA.c3.bOQtV.sq.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.t1arOa5dbCB#JN#Im#Im#Im#Im#Im#Im#Im#Imboi#PKassbpg#HG#FY#HG#HGbCC#wB#HGbzG#Zr#5B#xw#Z8#FX#KH#KH#KH#KH#KH#KH#KH#KH#G3#4JbAK#3p#UK#S##HKbCDaWLbCEbCFbhNbwRblBblBblBblBblBblBblBblBblBblBblBblBblBblBblBblBblBblBblBblBblCblCblCbvBbyEbAObASbyGahMbCG#uIa#Na#MaqSaqSaqSaqSaqSaqSaqSaqSbkbbqX#X0#OW.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..t1",
+"bCHbB0bCI#A5#0qbCJbCK#78#Cq.bD.aVaN#.aVaQJ.aV.aV.aV.aV.aV#BP#LwbA6bCHbCLbCMbCNbCObCPbCQbCRbCSbCTbCUbCVbCWbCXbCY.bd#Ut#Ut#Ut#Ut#uH.w4.#P.#QQtVQtVQtV.#QQtV.aF.#QQtV.#Q.fMQtV.qZaJtbaFbCZbC0bC1aIbbC2bC0.KE.aF.#Q.fMQtVQtk.#Q.aF.aF.aI.dZ.1w#lWatLbC3axB#5rbC4bC5bC6bC7bC8bo8bn2bo8bo8bmybC9bwVbD.bD#abUbBn#HHaZ0bAobDabBrbDbadI.#M.cAbDcbDdbDebDfaIxQtVQtVQtVQtV.GZbDgbDhaka#uH#Ut#Ut#Ut#uH#uH#uH#uH#zw#59bDi.zu.Sl.#l#eP.Tt.Tm.dI#Ut#Ut#uH#c4#ts.zr.YL.YL.YL.YL.YL.YL.JIaEI.Ql.ps.kf.mw.mw.mw.mw.mw.mw.mw.kf.ns.r7.j.QtKQtK.ka.vj.qp.Qb.mD#LE.9V.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#e1#mG.j#.ep#bz.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.h..sX.h..3Y#Bd.Vv.Vv.Vv.Vv.YV.33#bx.lD.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.vhbxybDjbDkbCsbDlbDmbDnbDobDpbDqbDr.#C.rP#FDQtVQtV.sq.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.#EavSaqSag0ato#K7#FY#Im#Im#Im#Im#Im#Im#Im#Imboiboiboiboi#KH#KH#8c#FXa.q#xw#Sb#Jo#Eu#HG#F0#KH#KH#Im#Im#Im#L3#FXbDs#Zr#JY#6r#Vv#40#vwbDt#xsbDubDvbmcbkNbjibDwbkObkObkObkObkObkObkObkObkObkObkObkObkObkObkObkObkOblBblBblBblBblBblBblCblCblCblCbwCbBSbDxbDybDz#5Ja#MaqSaqSaqSaqSaqSaqSa#M#8pbDA.#m.Tm.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.r..fX#46",
+"#A5bDBbDCaoSatq#96#96bDD#8bbDEbDF#6pbDG.aV.aV.aV#BP#Lw#Ab#zmbDHbAZbDIbDJ#xibzPatz#vraSB.WVbDKbDLbDMbDNbDObDPbDQbDR#Kq#ET#Ut#Ut#uH#Ut#Ut.Jk.vc#a4.#QQtVQtVQtVQtVQtV.fMazKbaDaJrbDSaKJaIbaV6.RGbDT.s7baDQtV.aF.aF.#sQtk.#s.fMQtk.1w.s7bDUbDVbDWbDXbDYaSPbDZbD0bD1bD2bC9bo8bo8bmybxibD3aaIbD4bD5bD6a1m#HHamLbD7bD8bD9bE..#M.#L.azadKbE#bEabEb.cu.GZQtWQtk.cp.aFbEcbEdbEeaIT.bd#c5#uH#uH#uH#uH#uH#uH#uH#bi.R2.pz.rq.rq.JI#ePa5k.qZ#uH#Ut#uH#uH#5R.zD.YL.YL.YL.YL.YL.D#.OIbEf.Ql.ps.kf.mw.mw.mw.mw.mw.mw.mw.kf.W6.un#gCQtK.2c.Lj.kd.Fu.7P#wK.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.34.Vv.Vv.YV.34.Vv.YV.rBQtG#Iy.BM.YU.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.kv.yl##N.oD.BM.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.sY.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.sd.rt.lDbEgbCsbAvbAwbEhbEibEjbEkQt6.#C.#P.gV.#QQtVQtV.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.bO.cu#bfaBvaqSaqSag0bEl#Z5#wB#FY#Im#Im#Im#Im#Im#Im#Im#Im#Im#Im#Im#ImbEmbEn#xt#6r#S0#FlbEo#F0#Im#FY#Im#FY#Im#FXbDs#XN#8dbEp#4LbyS#Jq#yu#ZpbEqbErbkKbmcbvHbjibkObkObkObkObkObkObkObkObkObkObkObjibjibvHbkNbkNbvHbjibjibjibkObkObkObkObkObkObkObkObkObkObjibjibjibEsbEtbEu#5J#2Rb.Vb.Vb.Vb.VaBva#Mb#J.qS.t1.r..w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.Tm.#pauG",
+"#87#8c#L2#Fi#wB#wB#wBaez#Fi#Fi#FiaambEv#77#BPbEwbEx#JbbEybEzbEAaij#xj#wp.Tq.Jc.Jc.Jc.Jc.Tq.Tq#p2#p2#p2aPxaPxbmqbEBbECbEDbgybEE#Ut#Ut#Ut#Ut#Ut#Ut#c4.vc#c2.fXaJtbaDQtWbEFbEGbEH.L..L.bEIa6b.xFbbfQtVQtVQtVQtkQtk.c3.#t.fMQta.1wbEJbEKbELbEM#6ebDYbENbEObEPbEQbERbESbmybxibC9bETagPbEUbEVbEWadja#k#L3aHnbEXbEY.gOacg.bp.#IaoabEZbE0bE1bE2aIx.bOQtk.aF.c8.w0aivbE3.zkbE4bE5ali#uH#uH#uH#uH#uH#uH#uH#c4.zpat6.9S#eY.zt#3I#0g.fX.w4#Ut#uH#uH.ye.YL.YL.YL.YL.YL.YL.YL.D.aaaa1X.ps.pu.mw.mw.mw.mw.mw.mw.mw.kg.nr.ov.oP.Qt.36.psbcA.ow#jT.YV.h2.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT.TP.aY.YV.Vv.TP.cW.YR#by.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.BM.r#.0v#bx.0A.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM#fc.7V.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ#Pk.ifbE6bE7bE8bAwbE9bF.bF#.xG#c5.vc#r3#m8.#QQtVQtV.#Q.#Q.#Q.#QQtVQtV#I7.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#Q.#QQtV.bO.gV#ETaqSaqSaqSb.Va6Fa#MbFa#K6#FY#FY#Im#Im#Im#Im#Im#Im#Im#Im#FY#PK#0IbFb#VwaN6bEp#MG#K6#FYbFc#FY#FY#FX#YG#6t#V9#WWbFd#5A#Jp#R9#Zp#CtbFeaJEbFfbFgbFhbFibFjbFjbFkbFkbFkbFkbFlbwCbvBblBblBblBbAQbizbFmbFnbFobiBbFpbFqbBSbyEbhMbjibjibkObkObkObkObkObkObkObkObkObkObjibFrbkCbFsbFt#8Ja6Fb.Vb.VaBvavS#rjQtV.v4.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r..dSb#E",
+"#Fi#wB#F0#F0#F0#F0#F0#F0#F0#F0#PKa.9bFubFv#AbbFwbFxbFybFzbFAbFBahSbFC.Tq.PZaeJaeJaeJaeJ.PZ.PZ.PZ.PZ.14.14.14aiiatzbFDbFEbFFavO#uHbFGbFHamJamJapyapyapybFIaSYbFJa7Fa3X.PZbFKaM5.5MbFLbFMbFNbod#18#18#18#18#18#6k#4FbFO.taatLbFPbFQbFRaR1bFSbENbFTbFUbFVbFWbFXbFYbFZbETab5ab5adoaaGbD5aawa1m#HHaCZbeEbF0bF1bF2bDbbF3bF4bF5bF6.uPaag.fq.v4Qtk.c8.#P.w3#F2anHbF7.iOaIUbF8bF9#F2#uH#Ut#uH#uH#uH#uH#uH.#TbG..lD.tHbG##uK.YLaen.fYazM#Ut#Xxa#V.D#.YL.YL.YL.YL.YL.YL.zqawO.MX.zK.kg.mw.mw.mw.mw.mw.mw.mw.os.qo.oq.rA.i1.rg.X#.JL.0A.Vv.RR.qi.h2.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv#jT.Vv.2m#DN.3Y.9V.h2#mG.mD.Xb.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#vN.tJ.ro.0v.TN.TP.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.30.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.zJ.hhbxybGabGbbGcbGdbGe#Ah.a9.ap#c5.v5Qtp.aw.fX.bOQtV.#Q.#Q.#QQtVQtV.cvQta.#t.fM.#Q.#Q.#Q.#Q.#Q.#QQtVQtV.bO.#Q#xKa#NaqSaqSaqSaqSb.Vb.V#6zbGfbGg#K6#wB#FY#FY#FY#FY#Im#FY#FY#wBbGh#3o#6r#WW#Vr#Wa#V8#HG#HG#HG#wBbGibGjbGk#2KbGl#S#bGmaONbGnaO.#yubsXa3MbGobGpbGqbGrbGsbGtbGubGvbGwbGxbGybGzbGAbGBbGCbGDbGEbGFbGGbGHbjf#Evbmd#6r#Wa#TR#1sbGIbyCbAObvBbvBbAQbAQbAQbAQbAQbvBbvBbAQbAQbAQbxQbvBbk.bGJbGKbGL#8J#6yaG6QtV.dg.k6.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.tm.dg.cp#1D#3I",
+"#F0#F0#F0#F0#F0#F0#F0#L3#PK#Fia#.bGM#KEbGNbGObGPbGQbGRafbajIaY4ajgaw7ajnbGSawqajjaeKawpaimagBakFanSahSahSaikahSbFC.K6a61aLsbGTbGUbGVbGWbGXbGYbGZbG0bG1bG2aFe.14bFKaZ6alEbG3bG4bG5bG6bG7#zsbvy#wvbG8bG8#wv#wv#P.#HLaIjbG9bH.bDY#5sazybENbH#bFTbHabHbbHcbHdab5ab5ab5agPbHeadobHfbHgbfka#k#F0aDCbHhbHiau9bHjbHkbHlbHmbHnbHo.dhQt0.wB.aF.#P#bi#c4#uH#ic#UtbHp.e0aOlbHqbHrali#13#Ut#Ut#uH#uH.16.aq#ETbHsbHt.lx.bH#LG#1D.#n.v4#Iv#Ut#Ut#ic#eYaGm.YL.YL.YL.YL.YLaEP#3H#1X.kb.ns.mw.mw.mw.mw.mw.mw.kf.ns.ri.uoQtF.qn.kf.mt#bA.Vv.TH.ko.k#.h2.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv#jT.YV.wn#if.JV.eh.TO.TF#c8.oD.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.roaiM#bx.TP.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.Vv#j2.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.lD#J7.tEbHubHvbHwbHx.zr.fH.ba.sp#Xx.w3.#F.gV.fXQtVQtV.#Q.#Q.#Q.bO.#E#8p#JR.t1.ai.#tQtVQtVQtV.#t.bOQtV.cv#bgavSaqSaqSb.VaqSb.Vb.Vb.Vb.VaBvbcfbHybHzbHA#xu#wB#HH#HH#8c#Im#WT#6v#Vw#2K#Vw#Vw#Wa#HI#PK#FX#FX#FWbHB#Vw#Z7#SW#wxbHC#Aga0SbHDbHEbHFbHGbHHbHIbHJbHKbHKbHLbHMbHNbHObHPbHQbHRbHQbHQbHObHSbHTbHUbHVbHW#UwbHXbHYbHZbH0#S0bH1#2L#vy#V9#FWbH2bH3bFlbwCbxQbxQbxQbwCbizbnQbwCbwCbxQbxQbxQbxQbhMbjibH4bH5.bc#NWazr#EU#P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P#.w2#P##P#.w2#P##P##P#.w2.w2#P##P##P##P##EU#Hz#rj.Jl.Jl",
+"#F0#F0#F0#F0#F0#F0#Fi#FiaA9#ME#SLbH6bH7.PZajbbH8bH9ab5adgadgadgakAaeZaeZab3adgadgaeZaeZab5aaGaMMakTbI.#BkbI##BkbI#bI#bI#ajga#wagObIabIbbIcbIdanSalE.TpaZ6aM5.VebIebIfbIg#xobIhaGZ#xp#ww#ww#ww#ww#ww#ww#ww#ww#ww#zsaONbIi#3YbFTazyazybENaQMbIjbIkbIlbImadqab4ab4agPbInbIobIpbIqa1m#JNaCZaDBbIrbIsbItbIubIva#Ha#HbIw#VwbIx.#u.cv.#T.Jk#Ut#3V#UtanHbIybIzaOl.e0bIAbIBavR#Ut#Ut#Ut#uH.16.aqbICbIDbIEbIFbIG.YS.aZbIH.b#.v4.v4.vb#Ut#Ut#c5#qc.C9.C9.YL.YL.YL.YL.C9bII.yh.kg.OC.mw.mw.mw.mw.mw.kf.kg.pp.Qv.g9.TZ.kh#xA#bz.#8.lG.mr.cg.TP.Vv.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT.Vv.RRaQ2.tw.0z#zA#LE.mD.ep.7Y.Vv.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.ro.sX#bx.TP.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.qy#fb.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.lD.sX.W9.tEbIJbIKbIL.Lp.pX.pV.ba.ao#uH.v5.#F.gV.fXQtVQtV.#Q.#QQtV.aiaX4aqSaqS#5K#bg.bW.c8.c8.cv.w0#bg#2bbdJb.Vb.VaqSb.Vb.Vb.Vb.Vb.Vb.Vb.Vb.Vb.VbdJ#5IbIMbINbIObIPbIQbIRbIS#Wa#2K#W.#Sb#Vw#S0#1qaez#PK#Il#9jaUr#W##Zp#zrbHCbITbIUbIVbIWbIXbIYbIZbI0bI1bI2bI3bI4bI4bI4bI5bI6bI5bI6bI7bI7bI7bI7bI4bI4bI8bI1bI9bJ.bJ.bI2bJ#bJabJbbJcbJdbJebJf#SabJg#Fd#K5bJhbJibJibJjbJjbJkbJl#0MbJmbFmbJnbJibJjbJjbJobJibJpbFnbJq.dZazr#P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P##P#azr#P#bJr.YL.YL",
+"#F0#F0#F0#F0#PK#FibEvbJs.f#bJtbJubJvbJw.Tq.14aeJ.14aimbqmbqmajCajiab4ab4adqab4ab4ab4ab4ab4ab4ab4ab4ab4ab4aeZaeZaeZaeZadgbJxbqrbo1.JeaijaeJ.PZ#p2bJy.JbbJzbJAbJBbJCaGZaGZ#yo#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ytbJDbJEbHLbJFbJGbENaqhbDZbJHbJIbImab0adqadqagPbwVbD4bJJbJKaaxaay#JNaZ0bJLbJMbJNbJObJPbJQbJR#WY#3pb#Fbzx.wB.w1#Us#Ut#3V#Ut#UtarLbJSaNm.pd.q5b.ebJTbJU#uH#Ut#Ut#13.w5#2bbJVbJW#InbJXbJYbJZ#qs.Dyaa..#FagcQt.#c4#Ut#Ut#uH#m5#vE#1D.YL.YL.YL.JI#Vy.nt.kj.ns.ls.mw.mw.mw.mw.kg.kh.TY#qs##F.lubJ0.ep.2m.7O.OF.mr.7X.BM.Vv#fd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT#G7.TF#Ld.vj.tv.tv#gC.YU.Ou.g5.BM.Vv.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.7Y.h.at3##N.TP.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.VvbJ1.5U.zJ.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.Vt.zJ.7V.sX.9P.RMbrsbJ2bJ3#yC#LR.zp.fE.ba.ao.16.v5#r3.gV.fXQtV.sq.#QQtV.ai#bea#MaqSaqSaqSaqSasV#6z#bk#m5bJ4aqSb.Vb.Vb.Vb.Vb.Vb.Vb.Vb.Vb.Vb.Vb.Vb.Vb.Va6FaBv#6z#5J#65#a9aDsbtL#KI#S0#Vw#Sb#Sb#Vw#Vw#S0#F1bAK#XG#34#ztaJE#CtbJ5bJ5bJ6bJ7bI8bJ8bJ9bK.bK#bKabKabKabK.bKbbKbbKcbKabKdbKebKabKfbK.bKbbKbbKbbKabKabKabKabJ#bKcbKabKcbKcbKgbI5bKhbIXbKgbKibKjbKkbKlbKmbKnbKobKpbFibkLbFibFq#GwbEo#GwbKqbDvbKrbKrbkCbkCbkLbFjbKsbKtQtS.v4.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.w2.v4.r.#F2bKubKu#Q#",
+"#F0#F0aez#FibEvbKv#WvbKwbKxbKybKz.14.Tq.Jc.Jc.Jc.14auVaeJaeJaeJ.JdatAab5aeZab4adqab4ab4ab4ab4ab4ab4ab4ab4ab4afaab4ab4bGSail.WTaij.14.Tq#vrbKAbJzbKBbKCbvyaIi#xp#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#yu#DKbKDbKEbKFaR1bENbJFbKGaAzbKHbKIbKJagPadqadqbHebKKbKLbKMbKNbBn#F0ayA#ImbKObKPbKQbKRbKSa#E#Jo#3p#WWbKT#id.#C#c5#Ut#Ut#Ut#uH#uH#NYbKUbKVabi.iO.q6bKW#NX#uH#uH#uH.w5#uHbKX#Jn#In#L8#wBbKYbKZbK0QtKbrX#eY.dh.G1.fX#uH#Ut#Ut#uH#bg#ol.YL.YL.YL.D#.ybbK1.i0.ts.kg.mw.mw.mw.kf.kg.rj.wg.ntbmF.nr.i3bK2.i7#gM.i7.#6.cl.TP.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT.Vv.tK.2q.Qk.Sd.tv.vj.tv.TC.Vv.TF.RR.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.TO#LE.Vv#by.Xg.Vv#wK.Vv.YV.Vv.Vv.YV.BM.YW#UU.w9.TP.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.M6.h..nx.g..zJ.Vt.Vt.Vt.Vt.Vt.zJ.g..dt.kv.sX.9P.7U#4rbK3bK4.JG.Fo.pX.fE.W2.ap#c5.w3#id.gV.#Q.bO.sq.#Q.bO.aw#8paqSaqSaqSa#Ma6FbK5bK6bK7bK8bK9a6FarPb.Vb.Vb.VaqSaqSaqSa5daBv#6z#5H#5G#Sf#ET#uH#c5.FranJa7pbL.aB1#F1#Sb#Sb#Vw#Vw#Vw#Vw#VwaUr#1r#ytban#Ctbx6bL##uwbLabKabLbbLcbLdbLdbLdbLebLbbLbbLfbLgbHJbLhbLibLjbLkbLlbLmbLnbLobLpbLqbLrbLbbLdbLebLebLdbLdbLebLebLebLebLebLebLebLsbK.bKcbLtbLubLvbLwbLxbLybLzbLAbLBbLC#NsaChbLDbLDaChbLEbDvbLFbLFbBRbwObLGbLHbLI.#y.G0.x9.x9.x9.x9#O7#O7#O7#O7.x9.x9#O7.x9#O7#O7#O7.x9.x9#O7#O7#O7.x9#O7.x9#O7#O7#O7#O7#O7#O7#O7#O7bLJ.qZ#jQbLKajRajR",
+"aez#L2aA9bKvaQJbLLbLMbLNbLOaFB#7B.Tq.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.MM.14.PZaimaH4agCaeZab4adqab4ab4ab4ab4ab4adqab4ab3#Bkaip.14aeJ.MMalEaE5aXBbLPbLQaIjaIi#xp#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xraO.bLRbLSbLTbLUaqhbLV#6fbLWbLXbLYbLZbL0agPab4bL1bL2bL3bL4#FYabV#HHaCZaDBbL5bL6bL7bL8bKSa#J#Jo#WY#3pa1t#4O.aj#uH#Ut#Ut#Ut#uH#uH#uH#4RbL9bHqaOl.iO.q6bM.aliaka#vE.BF.w5bM#bMa#JN#Io#F0#F0aDAbMbbMc#gN.QkawO.3U.r..r..#E#Ut#Ut#Ut#Xx#c4#0g.YL.YL.YL#0gbMd.h3.yj.ns.kf.mw.mw.kg.kf.Fu.g9.i7.ou.pq.7O#im.kh.x#.mt.g9.#6.tK.Vv.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv#jT.YV.sf#qo.Sd.tv.Ls.vj.vj.sO.wg.BM.3Y.57.YV.Vv.Vv.Vv.Vv.YV.Vv.TO.TO.aYQtGbMe.un.Li.Qc.Fu##E.Xg.TO.YV.YV.TP#Fr.7VQtJ#0y.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.YV.BM.YV.lx##N#1#.sY.sW#yF.dt.h..Vu.BM.r#.lD.sX.0F.sabMf.P7#Zb.Fo.MP.gF.#h#TA#c5.w3Qte.fX.#QQtaQtV.bO.cqavSaqSaqSaqSa5dag0bMgbMh#TR#TRaFEbMhbK5a#Vb.VaqSaqSa#M#6yavS#Sf#ET#c5#c5#c4#c4#c4.L##QUaa2a#L#KI#Sb#Sb#Sb#Vw#Vw#Vw#Vr#4LbMibx5#ZpbMjbMkbMl#xrbMmbMnbMobLdbMpbMqbMqbMqbLobLgbMrbMsbMtbMubMvbMwbMx#K0bMybMy#K0bMzbMAbMBbMCbMDbMEbMFbMGbMHbLobMIbLobMqbMqbLobLobLobLobLobLobLobMJbLobLobLcbMKbMqbMLbMMbMNbMObMPbMQ#0LbzG#JfbeUbeUbMRbMSbMTbMUbMUbMVbLFbMWbw7.bu#XD.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ.GZ#XDbMXbMYbMZbcQbMZbMZ",
+"bM0bM1#Cp#zobM2bM3bM4bM5#7C.Tq.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14.PZaeJbM6adnaeZab4adqab4ab4ab4adqab4ab4alGbo0.WT.14.Tq#wqbM7bM8bM9bN.aGZ#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#ZpbJ5#zr#zrbcKbN#bNa#5rbLVbNbbNcbNdbNebNfbNgbNhbNiagPadhbwVbNjbNkbNla1maayamLaYGbNmbNnbNobNpbNqbJR#Jo#5C#WY#WY#6CbNr.zG#uD#Ut#Ut#uH#uH#uH#uH#uH#4RbNsaIU.iObNtbNuali#c5.Dt.w5bNvbdR#FZ#LC#wA#F0#FX#WTbNwbNx.9X.cgbNy.JI#a4.r..v4ajP#Ut#Ut#Ut.16#Ut.P5.D#.YL.OI.#pbNz.h3.TY.ns.OC.kg.ns.ot.i9.OF.r7.os.kg.qp.ps.ns.kg.ou.pt.g9bNA.Vv#fd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.Vv.g5adY.Vm.tv.vj.vj.wf.vj.tv.TC.33.qE.bG.YV.Vv#jT#jT#jT#jT#jT.Vv.e6#m3.rh.kg.lt.ns.kg.kg.ns.MZbJ0.e6.YU.7Y##N.sX.9I#0y.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.YV.BM.BM.YV.9F.YV.YV.BM.BM.YV#sM.lD.sX.vh.pBbNB.Bv.Bq.17.yb.eI.bb.#a#c4.vc.gW.t1.qZQta.bO.t1#9naqSaqSaqSaqSbcfbNC#36aFE#Wa#5C#WYaNLbnKa6Fa#Ma#N#5G#uH#c5#c4#c4#c4#c4#c4#c4a02aDo#Lma1sbND#WW#F1#yq#Sb#Vw#S0#ZqbNE#HJ#VtbNFbx6bNG#CtbNHbNIbNJbNKbNLbLobNMbNNbNMbNNbNObNPbNQbNRbNSbNTbNUbNSbNVbNWbNWbNXbNYbNYbNYbNZbNSbN0bN0bN1bNSbMybN2bN3bN4bN5bN6bNMa93bN7bN7bN7bN7bNMbNMbNMbNMbNMbNMbN7bNMbN8bN9bNNbO.bO#bOabObbOcbOdbOcbOebOf#UCbOgbOhbOibOibOjbOkbOlbOm.as#LA.x9.x9.x9.x9.x9.x9.x9.x9.x9.x9.x9.x9.x9.x9.GZ.x9.x9.x9.x9.x9.x9.x9.x9.x9.x9.x9.x9.G0.G0#uDahfbcQ.P5.P5bcQ",
+"aaobOnbDBbOobOpahR#7B.Jc.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14aij.PZb.wajIab4ab4ab4ab4ab4afcbOqaeJ.PZ.MM.TqatzbOrbOsbN.#xp#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#wxbOtbOubOvbOwbOxbOybOzbOAaR1bOBbOCbODbOEbOFbEXbOGbNibHebInbOHbOIbOJaaxaay#JN#9ibOKbOLbOMbONa0da#G#F1#Jo#5C#WY#SbbOO#Ut#13#Ut#Ut#uH#Ut#uH#Ut#uH#uHbJUbOPae4.e#.qbbOQ#4R.Jk.aqbORbOSaz6#L7#wB#FW#Z8boR#V9#TRbOTbOU#tL.QhbOV.Bn.fX.v4.v4.w4#Ut#Ut#Utaka#3V.zE.D#.Fp#7G#UtbOW.lG.7O.ns.pr.ri.un.2c.i7.kh.kg.kf.mw.kf.kf.kf.kg.i7.Y6#qs#by.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT.Xg#xA.vj.tv.vj.wf.wf.wf.vj.sOatk.TO#by.a3.YV.Vv#wK.RR.g5.a3.mD#gN.rm.mw.MZ.kg.kf.mw.mw.mw.mw.mw.kf.lt.kc#jS.TM.5U.M6.BM.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#fd.YV.YV.YV.YV.Vv.BM.7T.sX.sX.sX.j2#Pk#VU.Bv.r0.zt.ya.dN.k2.#q.Du.#C.#P.gV.qZQtb.aw#9naqSaqSaqSasVa6FbOXbOY#TR#Sb#Jo#Jo#JoaNLbOZ#Sf#Xx#c4#c4#c4#c4#c4#c5#c5#c5a02anJa02bO0bO1#WW#F1#yq#SbbO2#TQ#SY#R9aOMaOM#ytbO3bO4bO5bO6bO7bO8bO9bP.bN7bP#bP#bPabPbbPbbPbbPcbPdbPebPfbPfbPgbN0bPgbN0bN0bN0bPgbPgbPhbPhbPibPhbPhbPhbPhbPhbPhbPebPjbPjbPkbPlbPmbPnbPobPpbPqbPrbPrbPrbPsbPrbPsbPrbPrbPrbPtbPrbPpbPubPvbPwbPxbPy#AebPzbPAbPBbPCbPDbEnbPEbPFbPGbPFbPHbPIbPJ#H3#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LA#LAaCrbPK#c5b.5bPL#M6bPLbPLbPL",
+"#UxbPMbPNbPObPP#7C.Tq.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jcaij.Oia33ab4ab4ab4ab4aeZalGagq.14.MMalEaXpbPQ#xraGZ#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zsaJEbPRbPSbNc#5sbPTbPUbFTazybPVbPWbPXbPYaSK#Fi#HHbPZbInbP0bP1bP2bP3aaxaay#JN#9ibP4bP5bP6bP7bP8bP9#F1#Jo#Jo#5C#WY#5DbQ..Dt#vE#Ut#Ut#Ut#c5#uH#vE#Ut#UtanHbQ#.q8#OH.fTbQa.vb.MObQbbQcaz6#L7#K6bzy#0K#8n#F1#Sb#F1#F1bQdbQe.TM#wKavk#X0QtV.v4.w2#c4#Ut#Ut#Utaka#Ut.zq.YL#0g.Jk#9x.5X.ko.sQ.oq.mrQtF.nt.kj.ns.kf.mw.mw.kf.mw.kf.os.r7.g9.nt.eq.Vv.34.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.Vv#bA.yj.2h.vj.wf.wf.wf.wf.vj.sOabd.ckQtJ.mD.BM.YU#jT.ow.mu.kh.kh.qo.qp.nr.ki.rg.kd.kf.kf.mw.mw.mw.mw.mw.mw.Qa#D..Vt.Lh.i4.YU.Vv.Vv#fd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.TP.r#.lD.sX.sX#dk.rxbQfbQg.zA.zr.zo.qS.yf#vE.w4.bj.bW.#Q.#Q.#ParOaqSaqSarPa6FbQhbQi#2K#TR#yq#Jo#Jo#Jo#JoaNLbQj#uH.w4#c4#c5#c5#c5#c5#c5.Fr.Du#c4bQkbQl#2K#TR#Wa#35#6s#S##zubQm#Ct#ytbx2bMjbNGbQnbQobQpbQqbO9bN6bQrbQsbQtbPtbQubQvbQwbQxbQybQzbQAbQBbQzbQzbQzbQzbQzbQzbQzbQzbQzbQCbQzbQCbQzbbxbQCbQzbQzbQCbbxbbxbQzbQCbQDbQEbQFbQDbQGbQHbQIbQJbQKbQLbQMbQNbQMbQObQObQMbQObQObQObQObQMbQObQPbQQbQRbQSbQTbQUbQVbQWboRbQXbQYbQZbQ0bQ1bQ2bQ3bQ4bQ4bMX#PB#PB#PB#PB#PB#PB#PB#PB#PB#PB#PB#PB#PB#PB#PB.GZ#PB#PB.GZ.GZ.GZ.GZ.GZ#H3.GZaqRbQ5bQ6bQ7bQ6blgblgblg",
+"bQ8bQ9bR.bR#bRabRbbRc.Tp.Tq.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14aijajjaeZadqab4ab4ab4agPaiqaeJ.Jc.TqaVYbRdbRebRf#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#zrbRgbRhbLVbRibENbLVbRjbRkbRl#GybL##R9#XN#HHabVbRmbRnaaGbRobJKaaxa#k#JNagk#OcbRpbRqbRrbRsaa0a#I#Jo#Jo#JoaOA#3pbMhbRt.Dt#Ut#ET#Ut#Ut#uH#uH#Ut#uH#uH#icbRuad7arF.fTbRv.vabRwbRx#Z5#L7#xubsObmN#WY#Sb#F1#F1#Jo#Jo#JobgTbRybRz#HTaEI.QfQt..v4#OW#c5#Ut#Ut#Ut#uH#c4#46#ePahj#c4#c4bRA.wd.g9.nt.pv.yj.qo.lr.kf.mw.mw.mw.kf.mw.kg.ps.uo.#6.#6.3Y.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.33.Y6.vj.tw.wf.wf.wf.wf.wf.vj.sOaa#.mC.bI.bG.BM#jT.Xg.5V.kg.lr.OC.kd.rj.sO.vj.vj.vj.tw.tt.kh.kf.kf.mw.mw.kg.ns.Ty#m3.2q.MY.Sb.YV.Vv.TO#wK.YV.Xg.tK#jS.0A.9F#LE#jT.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.YU.mC.lD.sX.lD#Pk.pCbRB.Dc.BB.P5.ye.bQ.#n.16.v5.#F.w0.#T.w4#c4#c5#8pasVa#VbRCbRDaFE#F1#yq#Jo#Jo#Jo#Jo#F1bREbRFaOU#c4#c4#c5#c5#c5#c5#c4.vabRGbRHbRI#TR#Wa#WWaUr#Rg#yu#Zp#yu#yu#ytbO3bRJbPXbRKbRLbMqbN8bRMbPbbQubRNbRObRPbQKbQKbQLbRQbRRbRSbRTbRUbRSbRVbRWbRXbRXbRXbRXbRXbRYbRXbRYbRZbRZbR0bR1bR1bR1bR2bR0bR2bR1bR2bR0bR0bR0bR0bR3bR4bR5bR3bLabR6bR7bR8bR9bS.bS#bS.bS#bS#bSabS.bSbbSbbS.bS.bScbScbScbSdbSebSbbSfbSgbShbSibSjbSkbSlbSmbSnaBpbSobLJaCrbLJaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaBSaCrbrm#7da6FbSpbSq#W6bSrbSrbSrbQ5bQ5",
+"bSs#HKbSt#HLbRf#HKbSubSv.YGalE.MM.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14.JeakSab5ab4ab4ab4ab4ab4agPa35aeJ.14alEbSwbRe#Vu#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zsbL#bSxbSybLVbENbNbay2bSza0SbOt#DK#Zp#4.#XO#wAa1mbSAbSBbSCbHgabUbBn#JNaDCbSDbSEbSFbSGbSHbSIbKS#F1#Jo#Jo#Jo#Jo#WYa1t#4T.DtaqR#uH#uH#uH#uH#Ut#uH#Ut#uH#uH#bgbSJ.pebSKbSLbtLbSMaBF#L7#Fia.qbSN#F1#Sb#F1#Jo#Jo#Jo#Jo#Jo#Jo#F1#XIbSObSPbSQ.Tt.r..v4#OW#c5#Ut#Ut#Ut#c5.w4#V0.zq#bk.w4##tayp.kg.mw.kf.lt.ri.YP.X#.mw.mw.mw.mw.mw.kf.ns.kbQtK.nt.ch.BM#fd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv#jT#jSadY.tv.vj.wf.wf.wf.wf.wf.tw.Ls#tq#yw.0x.sf#bz#wK.a3#rA.Qa.kg.ou.rn.Vm.wf.wf.wf.wf.wf.wf.wf#fe.kh.kg.pu.ns.Dx.ug.JL.mu.kh.vk.mt.YV.aYbvr.7O.tr.zK.qp.lu.kc.qq.wg.rv.TP#LE#jT.Vv#fd.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.TP##N.lD.0v.ds.zJ.rtbSRbSS.Fk.M4bsS.yb#Ah#V0.16.#qaOU#Ut#Ut#c5.Du.v.bSTbSUbQi#Vw#3p#Wa#Wa#Wa#F1#Jo#WY#TRaRUbSVa#M#Sf.w4#c4#c5.Jk.va.v#bSWa58#36#TR#JX#Wa#6s#1raOM#vwbSX#vw#Zp#DK#HKbSYbSZbS0bLnbS1bS2bQsbS3bQubS4bS5bS6bS6bSdbSdbS7bS8bS9bT.bJ6bJ6bT#bT#bT#bT.bT.bTabTbbTabTbbTbbTcbTdbTcbTcbTebTdbTcbTcbTcbTdbTfbTcbTdbTcbTgbThbTgbTibTgbTcbTjbRKbTkbTlbTmbTnbTobTobTpbTqbTqbTobTrbTpbTsbTrbTrbTtbTqbTubTvbTobTwbTxbTybTzbTAbTBbTCbTDbTEbOYbTFbTGbTHbTIbTJbTKbTKbTKbTKbTLbTLbTLbTMbTMbTMbTLbTLbTLbTLbTLbTLbTNbrmaCrbTObeWbDAbTPbnJbTQbTQbTQbTQbTPbTP",
+"#xr#HL#ww#ww#ww#ww#xpbN.bTRaJw.Tq.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14aeJbTSab3ab4ab5ab4ab4ab4ab4aeZb.wagq#wrbTTbTU#vx#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xraO.bOubTVazxaQMbTWbTXbTYbTZaJE#vw#R9#xw#HG#wAaaybuZbT0bT1bT2a1m#HHagkbT3bBpbT4akKbT5bT6bT7a#F#Jo#Jo#Jo#Jo#Jo#JoaSKbT8.Dt#Ut#Ut#uH#Ut#Ut#uH#Ut#uH#Ut#uH#NYbT9.q5bU.bU#bUa#0L#L8#In#JVbUb#WY#Sb#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#JobUcbUdbUebUf.Tt#P#.v4#OW#c5#Ut#Ut#Ut#uH#uH#c5#tt#uH#uHbUgbl6.r7.r7.kj.i6.Vm.kf.kf.mw.mw.mw.mw.mw.pu.pq.pt.#6.nt.cW.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv#jT.e6.HK.tv.vj.wf.wf.wf.wf.wf.tv.JVbUh.lC.ny#c8#by.TO.RR.ot.zM.lq.sP.twbwn.wf.wf.wf.wf.wf.wf.wf.wf.wf.rn.ns.kh#xA.Xb#bz.qq.ns.kf.kg.rh.5V.ot.ns.lt.ns.kg.OC.pu.kg.ns.lt.kf.r7.55.tK#wK.9V.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.tJ.OU.lD.TM.mC.Vt#4r.f8bUi.P7bUj#Xn.zE.HHaGn.#i#MW#bk.bd.w5.aj#c4bUka58bUl#3p#S0#Vv#UJ#1r#S##F1#F1bRIbUmbUnaBv#tt#c5#c4#c5#c4a02a7CbUobUpbUq#TR#Wa#4Z#S#aOMbUrbSX#yu#B.bMj#CsbUsbUtbUubLhbS1bN6bMGbUvbUwbHFbUxbUybUzbUAbUBbUCbUDbUEbUFbTcbUGbUHbUIbRKbUJbUJbUJbUJbUKbUKbULbUKbUMbUKbUMbUNbUNbUNbUObUNbUPbUPbUQbURbUSbUTbUSbUTbUUbUVbUVbUVbUUbUUbUWbUTbUSbUMbUXbUYbUZbUZbUZbUZbU0bUZbUZbUZbUZbUZbU1bUZbUZbUZbUZbUZbUZbUZbU2bU3bU4bU5bU6bU7bU8bU9bV.bV#bjmbTJbTJbVabTLbTLbTLbTLbTMbTMbTLbTLbTLbTLbTLbTLbTJbVbbVcbVdaB7bVebVfbVfbVgbVgbVgbVebf8bVhbVhb#D",
+"#zs#zs#zs#zs#zs#zs#zs#wwbRebVi#7C.K9.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jcaij.JebVjab4adqab4ab4ab4ab4ab4aeZbVk.PZatzbVl#2##zu#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ZpbVmbVnbVobVpbVqbVrbVs#SW#DK#xr#yt#4.#7t#Io#F0#ImaaybVtbzea1maayayAbVubVvbVw#I0bVxbVybVzbVAa#E#Jo#Jo#Jo#Jo#Jo#Jo#W.#3rbVB.fJ#Ut#vE#13#Ut#Ut#uH#Ut#Ut#uHbejbVCbVDbVEbVFbVGbVH#wA#OcbsO#5B#XI#F1#Wa#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#JobkAbVIbVJbeu#qc.qZ.G0#OW#c5#Ut#Ut#Ut#Ut#Ut#Ut#uH#bgbVK.nqQtKQtKQtF.#6.qq.kg.kf.mw.mw.mw.mw.mw.mw.ns.Qu.#6.g9.qr.TH.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.Vv#Iz.wf.tw.wf.wf.wf.wf.wf.wf.tv.yj#Be.CK.OA.uz.3Y#LE#Ks.Tz.vk.lu.kjbwn.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tv.lp.rz#gE#wE.Sd.uq.kf.mw.kf.kf.ns.kg.kf.mw.mw.mw.mw.mw.mw.mw.mw.kf.os.vk.ki.JM.tK#LE.Vv.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM.TN.Oz.bI#e0.sX.W9brsbVLbVMbVNbVObVPbVQ.ye.c6bVR.#navSbVSbVTbVUbVV#yq#3p#vy#wy#VubVW#vwbx2#0J#39bVXbVYbVZ.Dt#uH#c5#c4.FranJbV0a3P#BRbEp#TR#WabFd#S##yu#yua#q#yu#Ct#yt#BRbV1aN8#DEbV2bV3bMHbV4bV5bV6bV7bV8bV9bW.bW#bWabWbbWcbTcbWdbUJbUJbULbWebUObURbURbWfbWfbWgbWgbWhbWibWibWjbWkbWibWhbWlbWlbWmbWnbWnbWnbWobWpbWqbWrbWsbWtbWobWubWvbWwbWwbWnbWnbWwbWwbWnbWobWxbWnbWybWzbWAbWBbWCbWDbWBbWEbWFbWDbWBbWDbWDbWFbWDbWFbWGbWDbWFbWGbWHbWFbWIbWIbWJbWKbWLbWMaQTbWNbWObWPbWQbWQbWQbWQbVabVabVabVabWRbWSbWTbWQbWUbVdarRbf8bWVbu5bu5bWWbWWbWVbfsbWXbWYbWZbWYbWY",
+"#zs#zs#zs#xr#xr#zs#zs#zs#xpbW0aXp.Tq.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jcaij.JebVjab4ab4ab4ab4ab4ab4ab4agPab5bOqasGbW1bW2#yo#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ZpbW3bW4bPlbW5bW6bW7#wwbJD#zr#xr#HK#ztaV3#FX#wA#KH#F0#F0#KHaay#HHaHnbKNbW8bW9bX.bX#bXabXbbXca#I#JX#Jo#Jo#Jo#Jo#Jo#Jo#Sb#UHbXd.zG#13#Ut#vE#13#Ut#Ut#Ut#Ut#uH.ezbXebXfbXgbXhbf1bD7#F0#xubBM#F1#Sb#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1bgUbHuat2.C9bXi#HzQt.#c5#Ut#Ut#Ut#Ut#Ut#Ut#uH#F2bXjQtE#Uk.#6.Dy.0y.kg.kf.mw.mw.mw.mw.mw.mw.kf.mw#bn.#6.g9.#6.7W#G7.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.Vv.BMabd.Ql.vj.wf.wf.wf.wf.wf.wf.2h.5VbXk.hg#3a.eq.57#LE.tK.r7#sw.sP.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.Lt.7P#wKbXl.Qk.MZ.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.MZ.ki#xA.BM#jT.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.9F.sc#wM.YV##N.zJ#Pk.SibXmbXnbXobE9bXpbXqbXrbXsbXtbXubXv#5D#WW#4Y#WY#Vv#7r#xrbVW#HKbVW#HK#Z7a#KbXwa0..va#c4#c4#c4.vaa02asWbXxbXybEp#TR#Wa#Sa#wya#qbUra#qa#qbXz#xrbXAbXBbXC#LvbXDbXEbLn#SnbXFbXGbXHbXIbXJbXKbXLbXMbXNbXObXPbXQbXRbXSbXTbXUbXVbXWbXXbGobXYaUpbXYbXZbXZaCiaCibX0bX1bX2bX3bX4bX5bX6bX7bX8bX9bY.bY#bYabYbbYcbYdbYebYfbYgbYhbYibYjbYibYkbYibYlbYlbYkbYmbYibYnbX7bYobYpbYebYqbYrbYsbYsbYsbYebYrbYrbYtbYrbYebYrbYrbYrbYebYrbYrbYubYbbYvbYwbYxbYybYzbYAbWMbYBbYCbYDbYEbWRbWRbWRbWRbWQbYEbYFbYGbYHbYIbYJbYKbYKbYKbYLbfrbfrbYMbYNbYObYPbYQbYRbYRbYS",
+"#zs#zs#xrbYTbYU#xr#zs#zs#yobYVbYWatz.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14aeJbqmab3ab4ab4ab4ab4ab4ab4ab4ab4afcbYXbYYbYZ#zu#zs#zs#zs#zs#zs#zs#zs#zs#wx#DKbY0bY1bY2bY3bY4bY5aO.#xr#zs#Ag#vw#UH#XJ#wA#F0#Im#F0#F0#F0#JNagk#OcbSEbY6bY7bE.acfbY8bY9bZ.a#J#Jo#Jo#Jo#Jo#Jo#Jo#JoaOA#JpbZ#aA0.P3#Ut#Ut#Ut#Ut#uH#Ut#Ut#uH.ClbZabZb.rGbZcbZdbZe#F0#xubUb#WW#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#JobZfbZgbZh#ib#uK.fX#5u#c4#Ut#Ut#Ut#Ut#Ut#Ut#c4ajQ##C.nt.ty.or.kf.kg.mw.mw.mw.mw.mw.mw.mw.mw.kg.mt.#6.#6.g9.#6.Xg#fd.Vv.Vv.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.YU.Xg.OL.tv.vj.wf.wf.wf.wf.wf.wf.TxbZiat6.id#Pk#jH.eq.9V#by.tr.W6.mt.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.Sd.g5#jT.9V#xA.os.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.ns.qq.cT.TO.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.BM#Hb.tI#fc#bz#wL.Vt.qzbZjbZkbZlbZmbDnbZnbZobZpbZqbZraPz#4Y#WY#F1aUr#7rbZs#xr#zs#HK#vw#Rg#3q#KIarRanJ#c4.Fr.va.va#LmbQk#6w#UGbEp#TR#Wa#2K#XH#vxbFea#qa#q#ZpbZtbZubZv#7b#Lxbz6bZwbZxbLnbZyaTBbZzby1bZAbZBbZCbZDbZEbZF#UGbdibZGbZHbjQ#zs#ytbXzbZIbZJbZKbZLbZLbZMbZKbZKbZNbZO#Olbvy#uwbZPaZx#AfbpJbZQbZRbZSbZTbZUbZVbZWbZXbZYbZZbZ0bZYbZYbZ1bZ2bZ3bZ4bZ5bZ6bZ5bZ7bZ5bZ5bZ5bZ5bZ6bZ8bZ9b0.b0#b0ab0#b0#b0bb0bb0bb0ab0cb0db0db0eb0fb0gb0fb0ab0fb0fb0fb0fb0hb0hb0ib0jb0kaCbb0lb0mbWXb0nb0ob0pb0qb0qbYGb0rbSWb0sb0tbYNb0ub0ub0ub0ub0vb0ub0u#6xbYQb0wbYPb0xb0ybYPb0z",
+"#zs#zs#ztb0Ab0B#zt#zs#zs#yobYVbYW#wr.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14.Jdbqma1Jab5ab4ab4aaGajiajhaeZajIaVqbM7b0Cb0Db0E#zr#yu#zs#zs#zs#xrbW3b0Fb0Gb0Hb0Ib0Jbqdb0K#xr#zs#zs#xr#wx#Vv#xv#JU#F0#KH#F0#F0amLaZ0#HGb0Lb0M.jzbBrarZb0Nb0Ob0Pb0Q#TR#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#3q#S#bRE#6C#13#Ut#uH#Ut#uH#uH#Ut#Ut.t.b0Rb0Sb0Tb0Ub0VbZe#F0#Kjbvb#Sb#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#XIb0Wbr1b0X#3M#2RQtV#rj#Ut#Ut#Ut#Ut#Ut#Ut#bg#uE.rA.JX.ns.ns.kf.mw.mw.mw.mw.mw.mw.mw.mw.kf.kg.Dy##E.g9.g9.g9#tv.Vv.Vv.Vv.YV.BM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV#jT.3Yavf.2h.vj.wf.wf.wf.wf.wf.wf.sOb0Y.flb0Z.qz#ls.cg.YV.BM.HK.nr.tv.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.h6.TO.TO.sJ.kf.pu.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.lt.Tz.YR#LE.Xb.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.YV.BM.BM.Vv.7I.W9brsb00bZlbAwbBBb01b02b03b04b05b06b07#JW#Vw#UJa#qbVWbvy#HLbVW#2#bAD#6r#F1b08bO0.v.b09aslbQka3Pb1.#2K#TR#TR#Jo#WW#ys#xq#yt#HKa#qb1##wxb1ab1b#X8b1cb1d#C5.lkbOBb1eb1fb1gb1hbv7b1iaUr#Z7#xx#UJ#S##Rh#SW#3r#Agb1jbm.bG8b1kbZKb1lb1mb1nb1ob1ob1pb1qb1rb1sb1tb1ub1vb1vb1pb1wbZN#Ol#P.aIjbvy#P.#Olb1xb1yb1zb1Ab1Bb1Bb1Cb1Db1Db1Eb1Fb1Gb1Hb1Ib1Jb1Kb1Kb1Lb1Lb1Lb1Lb1Mb1Nb1Ob1Pb1Qb1Qb1Rb1Qb1Rb1Rb1Qb1Rb1Sb1Tb1Ub1Vb1Ib1Sb1Wb1Wb1Wb1Xb1Yb1Yb1Wb1Yb1C#5wb1Zb10b11b11b11b12b12b12b12b12b12bYSb13b14b14bRGbRGbRGbRGb14b15b16b10b10b0wb17b17b17b10b0w",
+"#zs#zs#xr#HL#xr#xr#zs#zs#xpb18aXpaii.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.Jc.14aijahTaimaiob19aOw.JdagBb2.b2#b2ab2bb2cb2daZlb2e#CsbPY#DKbJDaO.b2fb2gb2hb2ib2jaRY#xr#zs#zs#zs#HK#zt#8n#HG#wA#L3#F0#F0#wBaDC#Ipb2kb2l.jzbBr.jU.#Kanab2mb2nb2o#WY#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Vw#wy#4L#3pbOY#2b#uH#Ut#uH#vE#UtaqRaFwb2pb2qb2rb2sb.gbb2#F0#IlbAK#Sb#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Job2tb2ub2v.JD.zr.bW#bg#Ut#Ut#Ut#Ut#Ut#Ut#bi#wF.ch.Vm.ns.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.rhabd.g9.#6.g9.Qt.sf.Vv.Vv.Vv.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.YV#jT.72.ms.tv.7N.wf.wf.wf.wf.wf.wf.Tx.5V#uz.pEays#wL.g8#by.YU.Qk.rk.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tv.pN#jT.Vv#NI.kf.kg.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.ns.qo.rv.TO.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV.BM.TN.sd.hhb2wb2xb2ybZmbzsb2zb2Ab2Bb2Cb2Db2E#4Z#xx#xs#wx#xr#Ag#xr#yt#1r#3q#Wa#Waa0d#F1bRIb2Fb2G#KI#ItbEp#TR#F1#Wa#Wa#Z6b2H#Zp#HK#Agb2I#Zpb2JbZAb1d#BN#KEb2Kb2Lb2Mb2Nb2Ob2P#Zq#5AaUraUraUr#Vvb2Qb2QbACbsYbyT#SWaZx#Agbvy#wvbZNbZNb2Rb1mb1ob1vb1vb2Sb2Tb2Ub2Vb2Wb2Xb2Wb2Yb2Zb20b20b20b2Zb21b1vb22b23b24bZNbZNb25aBmb26b27b28b29b28b3.b3#b3#b3#b3#b3ab3bb3cb3db3db3eb3fb3db3eb3gb3hb3ib3jb3kb3lb3lb3jb3lb3lb3kb3jb3mb3nb1mb3ob3pb3qb3rb3sb3tb29b3ub3vb3wb3xb3yb3zb3Ab3Bb3zb3zb3Cb3Cb3Db3Cb3zb3zbwyb3Eb3Fb3GbCAb3Hb3Ib3Jb3Kb3Lb3Mb3Nb3Nb3Ob3Nb3Pb12b12",
+"#zs#zs#zs#ww#ww#zs#zs#wwbReb3Q#7C.14.Jc.Jc.Jc.Jc.Tq.Tq.Jc.Jc.TqahI#xj#xjbzPaFB.Tq.14.PZaeJaeJ.PZ.14anQb3Rb3Sb3Tb3Ub3Vb3Wb3Xb3Yb3Zb30bRKbTkb31b2gbIU#AfaO.#xr#zs#zs#zs#HL#yt#7r#XJ#wA#F0#Im#F0#wB#L2#K7b32b33.jUbBr.jU.#Manaao#b34bvyb35#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1b2Q#UK#Jo#TRb36aSx#uH#vE#uH#uHaqR.ezb2pb37b38b39b4.bb2#F0#xub4##F1#Sb#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#XIbEpb4a.W7afM#c1#bg#Ut#Ut#Ut#Ut#Ut#Ut#bib4b.bH.Tz.kg.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.ns.BJQtK.g9.#6.#6.g9.ch.YV.YV.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV#jT.bG.JW.tv.wf.wf.wf.wf.wf.wf.wf.tw.JWb4c.sa.rt.RM.ep.Ou#Hbbvr.lp.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.sO.j..Vv#jT#gC.X#.kg.mw.mw.mw.mw.mw.mw.mw.kf.kf.kf.kf.kf.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.ns.rk.eh#jT.YV.Vv.Vv.Vv.Vv.YV#vN.YV.Vv.Vv.34.TP.dv.j2.gXb4db4eb4fb4gb4hb4ib4jb4kb4l#Oeb4m#zu#yu#wx#xr#Ag#HK#xr#xx#Vw#Wa#Wa#Wa#Wa#Wa#F1#TR#TR#TR#F1#Wa#Wa#Wa#6rbAD#zt#vwb4n#HKbOtbwwb4obz6#OZ#KE#Cob2Kb4pb4qb4r#4Lb4s#vybO2#It#Zqb4t#6sb4ubofb2Qb4vb4wb4x#SWb4y#HL#Olb24b4zb1lb1pb1vb4Ab2Zb2Zb2Yb2Yb4Bb2Yb4Cb4Cb4Db4Db4Db4Eb4Fb4Db4Gb4Hb4Ib4Db2Vb4Jb1lb4Kb4Lb22b4Kb4Mb4Nb4Ob4Pb4Qb4Qb4Rb4Sb4Rb4Tb4Rb4Ub4Vb4Wb4Xb4Yb4Yb4Zb4Xb40b4Mb41b42b43b44b44b44b44b43b43b45b46b47b2Ub48b48b49b47bBHb5.b5#b5ab5bb5cb5db5eb5fb5fb5fb5fb5gb5gb5gb5gb5fb5eb5cb5hb3yb3yb5fb3Db5ib5jb5kaCGb5lb5mb5nb5ob5pb5qb5rb5s",
+"#zs#zs#zs#zs#zs#zs#zs#yobStb5t#p2.Jc.Jc.Jc#wpbzPaIm.MMahIbzPahQb5ub5vb5wb5xb5yahQbzP.Tq.MM.Tqajca6gb5zb5Ab5Bb5Cb5D#U1bENbENaSPbJFbLVb5EbPTb5FbKDaON#yu#zs#zs#zs#zs#xr#yt#0J#XO#Io#F0#F0#wBaam#96b5Gb5Hb5Ib5J.b1.aAadIadK#Pob5Kb5Lb5M#WW#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#ZqbGl#TR#Jo#WYb5N#ET#13#uH#vE#uHaFwb5Ob5Pb5Qb5Rb5Sbb2#F0#Fi#4JbSN#XI#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#TR#F1#F1#Jo#Jo#Jo#Jo#Jo#JobgT#Jgb5Tb5U#2j#Ut#uH#Ut#Ut#Ut#Ut#uH#bhb5V.g9.kh.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.ts##F.nP.#6.#6.#6.#6.#6.pM.Vv.Vv.Vv.Vv.Vv.Vv.Vv.Vv.YV#jT.bGb5W.Ls.wf.wf.wf.wf.wf.wf.wf.tw.wf.DB.sa.if.rt.JZ.sf#wK.rB.M7.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.Ls.Dy#bz#LE.sJ.kf.kg.mw.mw.mw.mw.mw.kf.kf.kf.mw.ps.kd.qp.kd.kh.kf.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.MZ.rf#bz.Vv.Vv.Vv.Vv.YV.YV.TO.BM.Vv.Vv.YV.TP.pD#Pk.rx#gJ.JWba0b5Xb5Yb5Zbr7b50b51brAbjQ#ww#zt#wx#zs#vw#Vu#Vv#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Z6#yp#yt#yub4n#Ct#zsb52b53#Jbb1c#C2#Wub54b55#F1bw5bs0#S0#2K#2K#ItbO2bNEbNEb56b4ubuMb2QaJDbv#b4x#3r#xr#vwb57bZKb58b1ob1qb2Tb2Tb2Xb59b6.b6#b2Xb6ab4Eb4Hb6bb6bb6cb6bb6bb4Hb6bb6db6eb6bb6bb6bb6eb4Db2Wb2Sb1vb48b6fb6gb6hb6ib6ib6ib6jb6ib6jb6kb6lb6jb6mb6nb6ob6pb6qb6pb6pb6pb6rb6sb6mb6tb6ub6mb6mb6ub6ub6tb6sb6vb6wb2Zb2Vb2Tb2Zb6xb6yb6yb6zb6Ab6Bb6Cb6Cb6Db6Db6Cb6Cb6Cb6Bb6Db6Eb6FbkGbntb6Gb6Hb6Ib6Jb6Kb6Lb6jb6Mb4Rb4Qb6Nb6Ob6Pb6Qb6Rb6SbZ2",
+"#zs#zs#zs#zs#zs#zs#wwb6Tb6UaFL.Tp.TpajmahIbAWb6Vb6Wb6Xb6Yb6Zb60#Acb61b62#zm#A6b63b64.3Rb65a36b66arAb67b68bENbH#bENbENbENbENazyaSPb69b7.b7#b7a#Zp#zs#zs#zs#zs#zs#xr#wx#4Z#Il#wA#F0#wBa.9#wB#80b7bb7cb7db7eb7f.aA.#Lacfb7gb7ha#o#zt#3q#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#WWbGl#WW#Jo#Jo#3pb7i.w5#uH#uH#Ut.ezb7jb5Pb7kb7lb7mbJX#F0#xu#Z8byB#yr#WW#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Vw#6s#Z7#UJ#UH#40#Rh#S##Z7#4Z#F1#Jo#Jo#Job7nb7ob7paCS#qc#c5#uH#Ut#Ut#Ut.16#c4bUg.i0.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.kg.ot.g9.#6.#6.#6.g9.#6.nt.rv.Vv#fd.Vv.Vv.Vv.Vv.Vv.YV#jT#OK.JW.rf.wf.wf.wf.wf.wf.wf.wf.vj.tw.gY#Rk.rt.qz.0x.eq.Vv.3Y.ms.tv.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.HK.LB.53#lw.mw.kg.mw.mw.mw.mw.kf.kf.ts.YP.r6.sO.tw.vj.vj.tw.sO.r7#XX.mw.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.MZ#yH#jT.YV.Vv.YV.BM#IF.TO.YV.Vv.YV.TP.7T.kt.k.b7q.BJ.Fu.JV.sOb7rb7sb7tbyxbo.b7u#zs#xr#zt#xrbVW#xs#4Z#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WW#XH#2#bVW#HLbFe#B.#PMby1#V4.dp#OZaPBb7vb7w#Sb#Sa#S0#Vw#Vr#Vw#2K#2K#ItbO2bNEb7xb4u#34#Rib7ybADbsYbAC#DJb7zb7zb7Ab7Bb7Cb7Db7Eb7Fb3gb3gb7Gb7Hb4Wb4Xb7Ib7Jb6qb7Kb7Lb7Mb7Nb7Ob7Pb7Qb7Rb7Rb7Sb7Rb7Rb7Tb7Rb7Rb7Qb6eb4Db2Yb7Ub7Vb7Wb7Wb7Wb7Wb7Wb7Wb7Wb7Wb7Xb7Xb7Xb7Yb7Zb70bBHbBHb71b72b73b7Xb74b74b7Yb75b74b7Xb7Xb7Xb7Wb76b77b2Zb78b79b8.b8.b8#b8#b8ab8bb8cb8.b8db8eb8fa92b8gb8hb8ib8jb8kb8lb8mb6mb46b8nb8ob8ob8pb6ib8qb8rb8sb8tb8ub8vb8wb8xb8yb6R",
+"#zs#zs#zs#zs#zs#zs#vxb8zb8Ab8Bb8C#xmb8Db8Eb8F#AbbA6bCHb62b62#Ab#Lw#BP.aV.aV#LwbCHabJb8Gb8Hb8Ib8Jb8KbFTbH#bENbENbENbENazybENbLVb8Lb8M#Jq#DK#xr#zs#zs#zs#zs#zs#yu#zt#8n#wB#wA#wBaam#Fib8Nb8Oay2b8Pb8Qb8Rb8S.#Mana.jUb8Tb8Ub8V#yu#Rh#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#WYaUrb7x#WY#3p#3pb8WbuoaTO#uH.16b8Xb8Yb8Zb80b81b82b83#PK#JV#EubyBb84#9k#XI#Jo#Jo#Jo#Jo#Jo#WWaUr#UJ#xs#xr#wx#HK#HK#xr#xr#HK#wx#xr#W##Zq#Jo#Jo#JobgTb85bLK.BD#m5#c5#Ut#Ut#Ut#Xx.16b86##F.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.ns.DxQtK.g9.#6.#6.g9.#6.g9.#6.3Y.Vv#fd.Vv.Vv.Vv.Vv#jT.2b.JWb87b88b89b9.aTS.Lr.wf.wf.vj.0z.YS.rt.if.rt.l0.b8#Bd.7Zavf.2h.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.vj.cW#gE#bA.lu.ns.mw.mw.mw.kf.kf.kf.rk.kj.vj.wf.wf.wf.wf.wf.wf.wf.wf.vj.sN.rk.kf.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.lr.r7.0A.Vv.Vv.YV.BM.TP.BM.YV.Vv.BM.TP.bI#yw.Si.Ov.qq.OT.tv#rA.r8b9#b9ab9b#AfbjQ#xr#xr#xr#yub9c#Sa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WabRI#SY#yt#HK#yuaJEaKRb9daPB.aV#OZbXCbQVb9e#6r#Sa#W.#Vw#Vw#Vw#Vw#6r#6rbO2#ZqbNE#Z6aUr#UKb9fb9gb9hbyVb9ib9jb9kb9lb9mbYib9nb9ob9pb9qb1Nb9rb9sb9tb9ub9vb7Ib40b7Jb7Jb9wb9xb7Jb9yb70b9zb9Ab9Bb9Cb9Db7Rb9Eb9Fb7Sb7Sb9Gb9Gb9Gb7Qb4Hb9Hb9Ib9Jb9Jb9Jb9Jb9Kb9Kb9Kb9Lb9Mb9Jb9Jb9Kb9Nb7Zb9Ob9Pb9Qb9Rb9Nb9Nb9Rb9Rb9Nb9Nb9Nb9Rb9Rb9Kb9Kb9Rb9Sb9Tb9Ub9Vb9Vb9Ub9Wb9Xb9YaV4#1pb9Zb90b91b9Kb74b92b74b74b93b94b93b95b95b95b95b96b96b97b46b46b6kb98b8rb8tb99c..c.#c.a",
+"#zs#zs#zs#zs#zs#zs#zu#HLbqd#OlbY0c.bc.c#zmc.d#Lw.aV.aV.aV.aV.aV.aV#BP.aV#Ux.aV#V2.cNc.ec.fc.g#U1bENbENbENbENbENbENazybRiaR1b#Gc.hbPY#wx#zs#zs#zs#zs#zs#xr#yt#W##XL#wA#F0#L2#L2c.ic.jbPTc.k#PFc.lc.mc.nc.oadKc.pc.qc.rc.s#xr#zt#3q#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#5C#XIc.taDlc.uc.vc.wa6T#JH.16.vaa02c.xc.yc.zc.Ac.BanM#Fi#7t#IsbyB#Eu#Eu#6v#XI#F1#Jo#Jo#WW#4.#Vt#xr#yu#xr#HL#zs#zs#zs#zs#zs#zs#zs#xr#wx#ww#4Z#Jo#Jo#F1c.Cc.D#H##uK#c4#uH#Ut#Ut#uH#vEaS2.oq.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.kf.ns.ykQtK.#6.#6.#6.g9.#6.#6.qr.ow.TP.Vv.Vv.Vv#jT#wK.#8.JWc.Ec.Fc.Gc.Hc.Ic.J.M7aTS.vj.0z.bw.hh.if.ig.qz.sU.Xe#BdaQ2.sO.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.2h.9W#LE.bG.0y.ns.ls.mw.kf.kf.kf.rk.sN.OT.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf#m2.sP#BZ.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.mw.kf.kg.rz#jT.YV.Xb.9W.RR.YU.Vv.YV.BM#II.ds.qz.50.kb.wf.tv.sN.rn.ric.K.upc.Lc.Mbio#wx#xr#wx#xs#Sa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WaaUr#S.#yt#yu#yuaJEb2Jc.N#L0#OZc.Oc.PbH0#EvaN6#Vw#Sb#Sb#Sb#Vw#Vw#Vw#S0bO2bO2#WW#8nbcJaRTb4qc.Qc.Rc.Sc.Tc.Uc.Vc.Wc.Xc.Yc.Zb9oc.0b1Lb1Nc.1b7Gb9ub7Ib9wb7Jb6pb6pb6qc.2c.3c.3c.4b70bBHc.5c.6bpK#uvb9Sc.7c.8c.9c#.b9Ec##c##c##c##c##b7Rb6ab70c#ac#bb7Zb7Zb7Zb7Zc#ab7Zb7Zb7Zc#ab7Zc#bc#bb7Zc#bb7Zb7Zb7Zb7Zb9Qb7Zb7Zc#ab7Zc#ab7Zb7Zc#ac#cc#dc#ec#faV4c#gc#hc#ic#cc#ac#ac#ac#jc#jb72b72c#kc#kb9Kb9Kc#lc#lb9Jb6nb6nc#mb74b94c#nb4Vc#oc#pc#qb8qc#rc#sc#tc#uc#v",
+"#zs#zs#zs#zs#zs#zs#yt#Gyc#wc#x.bC#TD#N9.aV#BP.aV.aV.aV.aV.aV#BP.aV.cN#Lwc#yc#zc#Ab8LaR1aSPbVpbENbENbENbENbENaSPbRib3X#6dbQFaOMbJ5#xr#zs#zs#zs#zs#zs#xr#Zp#ys#7u#JU#Fja.8c#Bc#CbTWbNbbRic#D#U5c#Ec#Fc#Gb0Nc#Hc#Ic#J#zt#zs#wx#Rh#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#5C#WYc#Ka4DbST#zWaBvc#Lc#MaGC.t.c#Nc#O#5saR1c#Pc#Qc#R#JN#7uc#S#Eu#Eu#Eu#Eu#MG#V9#WY#Sb#ys#xs#wx#xr#HL#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#wx#W##Sb#Jo#JoaFEc#Tc#U.7F#Ut#uH#Ut#Ut#c5.16c#V##F.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.pu.kf.OL.#6.#6.#6.#6.g9.#6.#6.#6.g9.g8#by.Vv.BM.cW##oc#Wc#Xc#Yc#Zazy#Tdc#0c.Ac#1c#2#Fy#m3.pD.qx.ig.qx.ru.Q#.TP.Ov.sO.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tv#if.YV#jS.r7.lt.kf.mw.kf.kg.kd.r8.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.kc.kf.kg.mw.mw.mw.mw.mw.mw.mw.mw.mw.mw.MZ#uB.TO#jTc#3.wf.TP.Vv.YV.BM.YV.TN#Pk#oj.i6.BI.tw.sN.tx.lp.ki.YP.ouc#4.Tyc#5bjQb0K#xq#Zq#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wac#6bGl#zt#vwbSXbana#qc#7b2K#OZc#8c#9#Waca.#WW#yq#yq#Sb#Sb#Sb#Sb#VwaN6#4Lca##Fl#R4caacabcaccadcaecafcagcahcaicajcakcalcamcancaocapcaqb1Vb9rcarb4Zb40b9xb6pb70b70b70bBHbBHb7KbBHbBHcasb59b4Db4Db4Db4Db2Yb2Ycatb9Hb7Nb7Nc.9caucavc##b7Scawb7Sb9DbBHb9Ob9Pb9Pb9Pb9Pb9Pb9Pb9Pb9Ob9Ob9Ob9Ob9Pb9Pb9Pb9Pb9Pb9Pb9Pb9Pb9Pb9Pb9Pb9Pcaxb9Icaxb7Zb7Zcaxcaycaxcazcazcazc#cb7Zb7Zb7Zc#ccazcazc#acaAb72b72b72b72b72b72caBb7Vb7VcaCcaDb94caEcaFb4VcaGcaHcaIcaJcaKcaLcaMcaN",
+"#zs#zs#zs#zs#HK#zrcaOcaP#BNbz7#A5.bC#78.aV.aV#A5.aV.aV#BP.aV.cN#V4byXaRSbNXcaQaSPbRibH#bENbENbENbENbENbRiazycaR#3YcaScaTbTZ#yu#zs#zs#zs#zs#zs#zs#HK#xr#F1#FX#wBa.8caUcaVay2az3azy#4CcaW#REcaXcaYbAq.d6caZca0b8V#HL#zs#xr#wx#34#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#WY#W.ca1.IG#5Gca2ca3ca4ca5ca6ca7ca8ca9bFTcb.cb#cbacbbcbc#Eu#Eu#0I#0I#0I#0I#0IbyB#Jo#W##yu#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#vx#S0#Jo#Jo#TRcbdcbe.zs.YK#c5#Ut#Ut#uH#c5cbf##F.nr.kf.mw.mw.mw.mw.mw.mw.mw.mw##B.ou.OF.#6.#6.#6.#6.#6.#6.#6.#6.#6.qr.pN.RT.yj#j1aWhcbgcbhcbiaR2bFTbENbENbFT#Tdcbjcbk#Zf.sa#Pk.if.qx.a1.tK.r2.Tx.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.Lt.YT.BM.qq.MZ.mw.mw.kf.ke.rj.Sc.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.r7.ou.kf.kf.mw.mw.mw.mw.mw.mw.mw.mw.vk.JW#jT.3Y.rn#Tr.TO.YV.YV.YV#GD.sW#Oy.JB.Vm.tw.sN.tx.kc.rh.rk.ou.pq.ts#rscblcbmcbncbobEp#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#6r#Ew#yt#yu#yubJ5cbpcbqcbrcbscbtcbu#Ev#WW#Wa#Wa#Wa#yq#yq#yq#Sb#SbaN6bs0#8ncbvcbwcbxby0cbycbzcbAcbBcbCcbDcbEcbFcbGcbHcbIcbJcbKcbLcbMcbNb1VcbOcbPcbQcbRb6pb70b70b9zbBHbBHbBHbubb71b77b9Hb4Db78b78b2Yb2Yb78b4Db4Db4Eb4Eb4FcbScbTb6ab6bb7Rb9Ecavb7RcbSc.4cbUc.2cbVcbVcbVcbVb9Pb9Pb9Pb9Pb9Pb9PcbWcbVcbVcbVcbVcbVcbVcbVb9Pb9Pb9Ob9Ob9Ib9Ib9wb9wb9wb7Zb9wb9wb9wb9wb9wb9wb7JcbXb6pb70cbYb9zb7Kb7Kb71b9Sb7McbZcbZb7U#uvcb0cb1b9yb40b9tcb2cb3cb4c#pb1Ocb5cb6cb7caMcaN",
+"#zs#zs#xr#Ct#TOcb8#Wu#Qhcb9cc.aA9bM0cc#cc.#X9.aV#A5.aV#N9ccab1bccb#CrcccccdaR1bENazybENbENbENbRibENcceccf#7a.dpccgb4xaO.#xr#zs#zs#zs#zs#zs#zs#vw#7r#XN#wBa.8bEvcch#ZIcciccjcck#0p.aV#77#Wvcclccmccnccoccpccq#zs#zs#zs#yu#S.#S0#Jo#Jo#Jo#Jo#F1a#J#Jo#WY#3pccrbSTccscct#5sbDYbENbDYbFRbFRaR1azyccuccvccwccxccyccz#Is#Eu#Eu#EubsO#0IccA#TQ#zu#wx#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#UI#S0#Jo#Jo#WYaFEccBbu3blg#uH#uH#Ut#vE.L#ad0.i6.0y.kg.mw.mw.mw.mw.mw.mw.mw.mw.kf.qp.2c.#6.#6.#6.#6.g9.#6.#6.#6.#6.#6#Ld.OHccCccDccEbIZ#TdbFTbH#bENbENbENbENbJG#03b3WccF.WD.ie.rt.pD.a3.h6.Dx.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.oE#LE.5V.MZ.kf.kf.pu.up.sO.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tv.rh.OC.kf.kf.mw.mw.mw.mw.mw.mw.lr.M7.YV.yk.r7.TP.Vv.YV.BM.YV.h..kx.Y1.rf.tw.sN.tx.kc#j0ccG.muccH.kh.X#.X#.X#at0#gMccIbjf#XI#Wa#Wa#Wa#Wa#Wa#yq#Wa#Wa#Wa#Sa#1r#yt#HKbanbZPb9iccJccKccL#Ev#Ev#WW#Wa#Wa#Wa#Wa#yq#yq#yq#Sb#SbaN6bs0ccMccNc.OccOccPby0a0T#DIcbAcbBcbCccQccRccSbXQccTccUc.YcbKccVccWccXaCJaV4b9YccYccZcc0cc1cc2bBHbBHbubb7Kb71b77cc3b2Xb2Yb2Ycatb78b78b4Db2Yb4Db4Db4Dcc4cc5b4Eb4Eb4Eb4Eb7Pb6bb6eb6ecc6c.3b70cbUcbUcbUb70b70b70c.2cbUb71c.5b9zc.2c.2b70b70c.2c.2c.2c.2c.2cbVcc7b9Ob9Ob9Ob9Ob9wb9wb9wcc8bqfcascc9b7UcbZb9Hcatb6ab4Eb6eb6db4Hb6bb6bb4Hb6eb6eb4Ib4Ib4Gb4Cb4Ib4Ccatcd.cd#cdacdbcdccddcdecdfcdgcdh",
+"#xr#Zp#ztcdi#FSbOn#X9cdj#HH#KG#Fi#wB#PKa.9cdk#A5.aV#C5cdlaRScdm#76.#1aSPbENbENaSPcdnaSPbKG#ZIcdocdpcdq.bCcdrcdsaRY#yt#zs#zs#zs#zs#zs#zs#xr#Zp#4.cdtcduatqaAGcdvbPTcdwcdx#A5#77.dp#A5cdycdzcdAcdBcdCcdD#xr#zs#zs#zs#HL#HK#wy#Wa#Jo#Jo#Joa#JaRMcdEa#E#XIcdFcdGcdHcdI#6ebENazybENazybENbENbH#cdJcdKcdLcdMcdNcdOcdP#Eu#EubsO#0I#wzaB1#S.#yt#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#vx#AfcdQ#WW#TR#3pcdRcdScdT.zs#xz#Lm#uH#Ut#bccdUcdV.kc.kg.mw.mw.mw.mw.mw.mw.mw.mw##B.kd#4h##E.#6.#6.#6#Uk.#6#Uk#gCaiNcdWcdXcdYcdZcd0#TdbFTbJGbENbENbENbENbENbENbENbENcd1bH#cd2cd3#yw.jk.qi.sM.tv.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tv#VB.53.pN.os.kf.kf.mw.kc.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.0y.kf.kf.mw.mw.mw.mw.mw.mw.vk.rm.pN.MZabd#wK.34.YV.BM.7T.zJ.Ow.0zcd4.sN.tx.tt.ricd5.qp.ou.kh.ts.ts.X##c9.X#.ts#GHcd6#6raGX#Wa#Wa#Wa#Wa#Wa#Wa#Wa#4Z#S.#yt#CtaJEcd7cd8cd9ce.ce#aN6#WW#Wa#Wa#Wa#Wa#Wa#Wa#yq#yq#yq#S0#SaaPza1yaOIceacebccPcbqceccedaZyceecefcegcehceicejcekcelcemcenccVceocepceqb6Eb6Ccerb8.cesb8bb8bcetceucevb77cewcexceyb71cbZb2Yb78catcezceAceBb4Db6ab4Cb4DceCceDb4Eb4Eb4Eb4Eb4Eb4Eb4Eb7Pb7Pc.7c.3ceEceFceGceHceIceJceKcc4b4Db4FcatbpKb9zc.2b70cbUcbUcbUc.2c.2c.2c.2cbXcbXcbXb9Ob9wb9wb9wb9wceLceMceNceOcePceQceRceSceTb6eb6eb6eb6eb7Pb6db6db7Pb4Eb4Eb4Eb4CceUb4Db4Db2Yb2Yb2ZceVb48ceWceXceYceZbZ6",
+"#zrcbpce0.aV.cNce1bM0#L2#wB#F0#F0#F0#F0#Fi#8caSMce2ce3b53#77ce4ccecdnbENaSPbEN#ZIce5b7bce6ce7#A5#Jbce8ce9cf.#zr#xr#zs#zs#zs#zs#zs#zs#yu#zt#F1cf##JUcfaaqeQtycfbcfc#OZ.dp#A5.aV#Lwcfdcfecffcfgcfh#yn#zs#zs#zs#zs#zs#xr#wx#5A#Jo#Jo#Joa#EaFFcficfjca.#XMcfk#T9#6eaSPbH#bENbENbENbENbENbH#c#Pcflcfmcfncfocfpcfq#0I#0IbOc#MG#Jo#YE#wx#HK#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#HKbk7#6xcfrcfsaa1cftbn9aCAcfu#H#ahc#uH#uH#Ut#JR#sy.ts.mw.kf.mw.W6.W6.W6a5Sa5Sa44a44.X#.X#axs#gC#DN#DN#DN.nt#gC#gCQtDcfvcfwcfxazycbibFTbENbJGbENbENbENbENbENbENbENbENbENbENcfycfzcfA.zY.#6.i4.tv.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.yj#Bd.a3.mu.lr.kf.ps.kj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.M7.rg.kg.kf.mw.mw.mw.mw.mw.ns.OT.rk.ou.LB.Vv.YV.BM.YV.mC.a1.tv.sO.sN.tx.rj.ri.ki.qp.ou.kh.mw.ts.ts.X#.X#.ts.X#.kf#rscfBaGubkA#Wa#Wa#yq#Wa#WaaUr#vxbx2aJEcfCbOycbscfDcfEaN6#WW#Wa#Wa#Wa#Wa#Wa#Wa#Wa#yq#yq#Vw#EvcfFaN7a2jcfGcfHbOycfIcfJcfKbpJ#xq#wx#Gy#uwbZucfLcfMcfNb9jcfOcfPcfQcfRcfSb6FceqcfTcfTcfUb8#b8db8#b8.b9Ub8ab9UboecfVcfWceCb71casb7Ub59b2YcfXcfYb7Nb4Db78b4Db4FcfZcf0cf1cf2b4Cb4Cb4Eb4Eb4Cb4Cb4Eb4Dcf3cf4cf5cf6cf7cf8cf9cg.cg#b4Cb4Cb4Cb4EcatbpKcgacgbcbUcgccbUb9Pcc7b6pb6pb6pb6pb9Ob9wb9wb9wb9wb40cgdcgecgfcggcghcgicgjcgkceTcglcf1b4Eb4Eb4Eb4Eb4Db4Eb4Eb4Db4Db4Db2Yb2Zb2Zb2Tb2Ub48b1pb1ob1mb1lbZLb24",
+"cgmb1d#TD.em#6p#Fi#Fi#F0#F0#F0#F0#F0#F0#wB#wBcgnb2K#A5aN#cgoazybRibRiazyccjcgp#XA#78.bC.dp.bD#0qcgqbUbcgr#yt#xr#zs#zs#zs#zs#zs#HL#yt#TPbJg#7maz5cgsbJG.#1cgt.dp#CpaQJ.aV.aV#A5bFv#QLcgucgvcgwc.s#zs#zs#zs#zs#zs#zs#wx#ww#S0#Jo#Joa#JbJRcgxccmcgycgzcgAaR1cgBbENcdnbRibENazybENbENbH#c#PcgCcgDcgEcgFcgG#JZ#Eu#0I#EucgH#xx#wx#yu#HL#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#vxboKcgI.va.va.va.va.aj.va#c4bV0bYL.zE.YL.Vi#c5#uH#c5cgJaBz.lqa44a44cgKcgLcgMcgNcgOcgPcgQcgRcgScgTcgUcgVcgWcgXcgYcgZcg0cg1azycbiayxbENbENcg2bENbENbENbENbENbENbENbENbENbENbENbENbH#cg3cg4.rz.i4.tw.tw.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vjcg5.wn.0A.mt.lt.kg.rk.tv.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.sO.mw.kf.mw.mw.mw.mw.mw.kf.mw.zM.Lj#jT.YV.YV.33.h..mW.JW.mt.r8.rn.rj.ri.ki.qp.mu.pq.mw.kf.ts.X#.X#.X#.X#.X#.kg.kf#rscg6cg7bkA#F1#yq#Wa#Vv#ztbJ5#uwcg8cg9ch.bEp#Ev#WW#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WW#Sabv6ch#aZAby1chacbd#TO#xq#zt#zt#xq#zs#3r#zs#3rb2QbGlaP8chbb52chcchdchechfchgb5bchhceqchicfTcfUb8db8db8#cetchjchjcetchkchlchmchncascgachochpchqchrchschtchub4Db4Cb4FchvchwchxchychzchAchBchCb4FchDchEc.9cbZb9zb9zb9zchFchGcbZb4EchHchIb4Db4DcePchJchzchBchKchLcc7chMcf8chNchOchPchQchRchSchTchUchVb9xchVb40chWb6achXchYb4Eb4EchZch0ch1cghch2b4Ech3ch4ch4ch5chvb4Cb4Db4Db2Yb2Zb2Wb2Tb2Ub1vb1pb1mb2RbZMbZNch6",
+"#N9aN##X9ch7#L2#wB#F0#F0#F0#F0#F0#F0#Im#L2bDF#Lw#OZch8bLTcdncdnch9ci.ci#.cN#A5#Lw#A5#Cq#CpabIcia#2L#Gy#xr#zs#zs#zs#zs#zs#zs#xr#ytbsZbMQcibciccidcie#Uxcifcigcih#A5.aV.aV#BP#Aaciicijcikcil#vw#ww#zs#zs#zs#zs#zs#zs#HK#W##F1#Joa#EcimcincioccwcipciqcgBbRibENaTCce5ciraTCbENbENazyc#Pciscitciucivciwcix#Z8#0IccA#Z7#xr#wx#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#zu#vwbk7atm.Fr.L##c5#c5#c4#c5.Jk#c4.Du.L##om.YL.zq#tt.JkbST.ez#2SciycizciAciBciCciDciEazzciFciGciHciIciJciKcbicbi#TdbDZcbi#TdciLbRibH#Qtyc.y#ZIciMaTCciNbTWbPTbENbRicdnbRicg2bENbENbENbENbFTbIZciO.Qd.tw.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.2hciP#jT.Qb.uq.kf.rh.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.0y.kg.kf.mw.mw.mw.mw.mw.kf.vk.sJ#LE.YV.BM#LE.mC.RL.r6.r7.rn.rj.rh.rk.qp.ou.pq.mw.kf.kf.X##c9.X#.ts.ts.kf.kg.kg.kga44ciQa4BaGX#yqbGl#B.bJ5cfJciRciS#JY#Ev#WW#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Vw#F1aIhciTciUcbd#JqaOM#zu#xq#3raKRciVaZxbZP#3r#xqciWb56bGl#Ri#0J#xxbQn#O9ciXciYciZb5bchhceqchicfTcfUb8db8dcetb8dchjchjcetci0ci1ci2ci3ci4ci5ci6ci7ch1ci8ci9cj.cj#b4Db4Fb4DcjachIcjbchDci9ch2ch1cjcb4Ecjdcjeb4Eb4Dcjfc.4b9zcjgchFcjhb4CcjichxcjjcjkchBcjlcjmcjncjocjpcjqcjrcjscjtchKcjucjvcjwcjxcjycjzcjAchVchVb7Jb9wb9zcjBcjCb6eb6ecjDchZcjEcjFcjGcjHcgicjIcjJcjKcjmcatb4Db4Db2Yb2Zb21b2Tb2Ub1vb1ob1mbZMbZNch6bm.",
+".aVcjLbEv#L2#wB#F0#F0#F0#F0#F0#F0#F0#FibM0cb9#A5#O0cidaz3azycjM#7aaaocgncjNbkwcjOaN#bDBcjP#Is#UH#yt#xr#zs#zs#zs#zs#zs#zs#yu#ztbjfcjQcjRcjS#VjaoT#96a.9#KGa.8bDC.aV.aV.aV#Lw.lkcjTaBacjU#vv#vx#zs#zs#zs#zs#zs#zs#zs#HK#0J#Joa#Ja4BcjVcjWcjXcjY#ZJb3XaqhcjZcdp.cN#EraN#bKFbENbENccucj0cj1cj2cj3aSx.xKcj4#XNb56#xq#wx#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#vx#vw#KKcj5.Fr.Du#I8#c4#c4#c5#c4#c4#c5#c4#c4#c4#Ut.D.bu3cj6arQcj7cj8cj9ck.aN#ck#ckackbckcazybRicdnbRiaSPbENbENbENbENbENazybENaSPbKFckdckeb8PckfckgbOnaOJbOncdq.cbckhckickjcgsckkbNcbTWcg2bRibENcklcbickmckn.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.HK#e1.2q.kg.kg.lp.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.sO.W6.kf.mw.mw.mw.mw.mw.kf.mu.Ou#jT.BM.uB.kn#jH.lp.rn.kc.ri.ki.rk.mucko.pq.mw.kf.mw.ps.rk.lp.tx.mt.sN#fe.tx.lp.qp.tsbj.ckp#W.#Z7aO.brAckqckrcks#Ev#WW#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WW#WWcktckuckvb#F#wx#zt#uw#SX#3r#3raKRaKRciVaZx#SX#xq#wyaUrb56#34ckwciWbv##vub5kckxckyciZb5bchhckzckAcfTcetb8db8db8#b8#b9Ub9UckBckCckDckEckFckGcgbckHckIc.6ckJckKc.5b7Ub9Cb9Hb4DbILckLb7PckMcghckNcePchzcc5ch1cc4b4Eb4Eb9Cb9zc.4chLchLb9Sb4EckOckPckQckRckSckTcjmckUch2ckVcjpceKb70cbUckWckXckYckZcbXck0ck1ck2b9Ob9wb9wb9wb9wck3ck4ck5b4Hck6ck7cjdck8ck9cl.cl#clab4Eclbclcclbb4Db4Db2Yb2Zb2Tb2Ub1vb1qb1ob1mb2Rb4zbG8bm.",
+"cldcle#L2#F0#F0#F0#F0#F0#F0#wA#F0#Ficlfclgclh.#1az3bVpclicljclkcllce.#F1aSK#WWaPAclm#HH#9k#xq#yt#4K#zs#zs#zs#zs#zs#xr#Zpclncloclpclqclr#88cls#KG#wB#wB#Ficlt#Cp.aV.aV#A5#MDcluaTOavcchC#vx#ww#zs#zs#zs#zs#zs#zs#zs#wx#34a#GclvclwclxclyclzclAclBazzclC#Ob#OZ#A5#Erckecg2bFTcdJclDclEclF#Mwapu#rjclGclH#yp#yt#xr#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#zubVWchCcj5.Jk.Fr#c4#c4#c5#c5#c4#c4#c5#c4#c5#c5#c4#c4.5PclIclJclKclLbCK#MDaAFcjScjZch9bRibRibENbENbENbENbENbENbENbENbENbENazyaSPay2c#D#XA#OZ#77#OZ.dp#Cp#A5#A5#A5clM#Cp.dp#OZ#OZ#77.dpaN#cgt#PFce5cidbRiaR2#iKclNb89b88.Lr.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tv.rz.7Z.lp.ns.kc.OT.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.qp.kg.mw.mw.mw.mw.mw.ns.Dx.YV.YV.33.Vw.dv.rf.ri.ri.ri.ki.qp.kd.ou.ps.mw.mw.Tz.sN.tw.vj.vj.vj.vj.vj.vj.vj.vj.vj.tu.lpclOclP#wxclQclRc#9#6raN6#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WW#F1clSbU8clTb2I#zu#SXbZPaZxaZx#3r#3r#RgaKRciVaZx#SX#RgaUr#6sb56#34ckwbsZclUclVclWckxciYbkGb5bchhclXckAb6EclYb8#b8#b8#b8#b9UchjclZcl0cl1cl2cl2cl3cl4cl5ci4b9Scl6cl7cl8cjhcjhcjhc.8cl9ceCb4Ecf1cm.ckUcm#cmackUcmbb4Eb4Eb4Eb6ab9AcbUcf6chJb71b4Db4EceScmccghcjcb6acmdcmecmfcjIcmgchqcbUb70cmhcmicjgcmjcbXcmkchKcmlb9wb9wb9wb9wb9wcmmckZb2Wb4Ecmncmocmpcmqcmrcmscmtcmub4Ecmvcmwcmxb4Db4Db2Yb2Zb2Zb2Ub1vb1qb22b24bG8bZNch6cmy",
+"#96#Fj#F0#F0#F0#F0#F0#F0#HGbGh#Zr#xwcmzaPobSi#DIcmAcmB#F1bkxaN6aN6#WW#Sb#TRbzGcmCbw3cmD#yt#HK#zs#zs#zs#zs#zs#zs#xrbkt#HJ#GtcmEaoU#F0a.8#wB#F0#F0#F0#wB#K7cld.aV#A5#BP#MDcmFcmG.vablz#wx#ww#zs#zs#zs#zs#zs#zs#zs#xrcmHcmIcmJcmK.oTcmLcmMcmNbEv#wBa.8#8ccjL.aV#OZcgtay2aR2cmOcmPcmQcmRbg8cmSaqQcj5#OkaOM#xr#HL#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zu#ztcmTbVY.Fr.Fr#c4#c4#c4#c4#c5#c4#c4.Du.L#.v..og.og.ezaslcmUcmV#X8cmW#Q3ckjcmXbKGbRibRibENbENbENbENbENbENbENbENbENbENbENbENazybENciMckh#Er.dp#A5.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV#A5#A5#A5.dp#OZ#OZckgcmYb67cmZcm0cm1cm2b88cm3bwn.wf.wf.wf.wf.wfbwnbwn.wf.wf.tv.Qk#jT#m3.MZ.rn.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.kc.kg.kf.mw.mw.mw.mw.MZ.wg.TO.TP#gX.po#mE.OD.rh.ki.rk.qp.ou.ou.pq.mw.kh.rn#m2bwn.vj.vj.vj.vj.vj.vj.vj.vj.vj.vjb#y.M7b89cm4cm5cm6#V9#Ev#WW#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WW#JX#yq#wy#HK#zucm7bZPbjQbZPaZxaZx#3r#3r#RgaKRbZP#uw#xq#Jpbpfb7xb56#34b2QbsXcm8cm9clWcn.ciYbkGchhceqcn#ckAcfUb6Cb8#b8#b8.b8#b8.b9UcnacnbbkGcnccndcnecnfcngcl6b7LcnhcnicnjcnkcjhcnlcnlcnmcnnckMcnocnpcnqcnrcnsci7cntb4Eb7Pb4Eb4Ecnuc.3cnvcnwc.5b4Eb6ecnxcl7ckKb4Ecnycnzb4EcnAck9cnBceRcnCcnDcnEb5XceGcnFb9PchPclccnGb9Ob9Ob9Ob9wb9wchSckZcnHb2WcnIchXcnrcjCcnJaBicnKcmub4Ecjjci7cnLb4Eb4Db2Yb20b2Wb2Tb2Ub1pb23bvycnM#Olch6bmM",
+"#Fi#F0#F0#F0#F0#F0#wB#XO#xw#F1#WW#F1#F1#F1aN6ca.aSKaSK#F1#Jo#Jo#Jo#F1#XI#Z8#Io#Is#S##Ct#xr#zs#zs#zs#zs#zs#zs#yt#wxcnNcnOcnPcnQa.8#wB#F0#F0#F0#F0#F0#F0a.9bM2#N9.aV#Lw#WucnR.xKcnS#Rf#vx#zs#zs#zs#zs#zs#zs#zs#xrccpcnTcnUaplcnVcnWcnXa.9a.8#Fi#wB#K6cnY#76#A5.bDcnZcmZcn0cn1cn2b7f.#I#N1cn3b6G#vw#zu#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zu#vxbnL#TL#c4.va#c4#c4.L#.v..ogQt5Qt5.cE#c4cn4cn5cn6bn8cn7cn8cn9#V2#Qh#75ce5cidbRibRibENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbKGco.#Er#A5.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV#A5.dp.bCco#coabFTcobcoccodcoe.Lmb88bwn.wfbwncof.Lr.vj.Lr.vj.Sb.Ou.RR.mw.rn.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.kj.kf.kg.mw.mw.kf.kf.kf#lw.Vv#Hb.7V.TL.sN.Tx#fe.YP.ou.ou.ou.kh.mw.kf.kc.vj.vj.wf.vj.vj.vj.vj.vj.vj.vj.vj.vj.vjcogcohcoicojcok#Ev#WW#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WW#WW#JXcol#zucom#4KbjQbZPbZPbZPbZPaZxaZx#3r#3raKRaKRbZP#wwbyybNEbpfb7xb56conb2Qcmxcoocm9b3xcopcoqb5bchhb6Fcn#b6Eb6Cb6Bb8.b8.b8.cesb8.cn#corcosb9UchjchhcotcoucfWceMb9Scl8cngcnJcovcnlcnlcjhcmgcl5ceAci7cowb7Pb6dcoxcoycozb6db7Pb7Pb4Hb6acoAchqcoBcnlb4Hb7Pcf2ck8coCb7PcoDcoEcoFcmabILcoGceAcmaci6coHcgbcoIcoJb9OcoKcoLcoMb9wb9wb9wb9wb9wcoNcoOb9wcascoPci7coQcoRcngcoSbILcl#b4HcoTcoUcoVb4Eb4Cb4Db2Yb2ZceVb2Ub1qb24#P.coWcnMbvybmM",
+"#F0#F0#F0#F0#F0#FW#1q#F1#Sb#F1#Jo#Jo#Jo#Jo#Jo#Jo#F1#Jo#Jo#Jo#Jo#Jo#XI#TRbw3#V9#ww#yt#HL#zs#zs#zs#zs#zs#xr#zrcoXcoYcoZco0co1aez#F0#F0#F0#F0#F0#F0#F0#Fico2#0q.aV#Adco3co4.Cl#c4#KK#vx#ww#zs#zs#zs#zs#zs#xrco5#xrco6co7.#Mco8co9beE#L2#wB#F0#wB#L2cgq.aV#A5#OZcgocp.cp#cpacpb.b1.bpcpcbV0bqV#vx#zu#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#ww#ww#vwcpdcpe.v..cE.cE.cE#c4bVScpfcpgcphcpicpjcpkcplcpm#X8#78cpncpoa2icppbVoch9bRibENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENaSPcdw#V4#A5.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV#A5#78a.#cpqcprbFTcpsc.Acptcpucpvb88.vjcpwcpxcpycpz.Ql#xH.TO.qq.ki.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tv.lq.kf.mw.kf.kf.kc.rg.tK.YV#yF.r1#BTa9m.rk.tr.ty.ou.pq.kh.mw.kf.kh.tv.vj.wf.wf.vj.vj.vj.vj.vj.vj.vj.Sc#m2cogcpAcpBcpCaN6#S0#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WW#F1bEp#UJaOM#zu#SX#SXbjQbZPbZPbZPbZPbZPaZx#3r#3r#3r#Rg#SX#S.bofaB1bpfb7xb4u#SZcpDcpEcpFcpGcpHcopcpIcpJcpKclXb6Eb6Db6CcercpLcpMcpMb8cb6AcpNcpOb8db8ab9Ub9UcpPaubcpQcpRb7Lb9Scl8cpScpTcjhc.8c.8bUocpUcpVbUoc.9b7PcpWcpXcpYcpZb6db6db6db4Eb6dc.9cp0cp1b7Pb7Pb4Ecp2coRcoVb4Eb4EbKXcoxbRFb4Eb4Eb4Ecp3beScp4cp4c#icp5b9IchPcp6cp7cazcazcazcazcazcp8cp7cazcazc.5cp9cq.cc4cnqcq#cq#cqab6bcqbcqc#4Gb4Eb4Cb4Db4Db2Zb2Tb2Ub1vbZKaIjbnkb7ubZH#Af",
+"#F0#F0#F0#HH#0L#8n#W.#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#WY#xw#Vv#yt#xr#zs#zs#zs#zs#zs#zs#yu#wxcqdcqecqfa.f#Fj#F0#F0#F0#F0#F0#F0#F0#F0#Ficc#bCK.aV#A5#82cqgcqh.RI#TL#wx#ww#zs#zs#zs#zs#xrco5#xrcqicqjcqkcqlcqmaDBbD7#Im#F0#F0#wB#L2aan.dp.aV#77#PFcqncqocqpcqq.#M.#Iaescqrcqs#vx#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zu#ztcqtcqu.xK.Frb0tcqvcqwcqxcqycqzcqA#OZaQJco3cqB#MDcqC#N9aQJ.dpcfccqDaqgcg2bRibENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbENbEN#Qj#Er.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV#A5#78#Cpb8PcprcqEcobcqFcqGcqHcqI#BN#UecpncqJcqKcqL#D.acv.vj.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.kd.kf.kf.kg.TG.0y.qq.TP.cT.Vt.ja.mt.nr.ou.mu.Vm.Tx.lq.kf.mw.kf.nr.vj.wf.wf.wf.vj.vj.vj.vj.vj.vjcg5cm3cpvcqMcqNcqObw5#Vw#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WWbEp#6r#40#vw#xr#SX#SX#SXbjQbZPbZPbZPbjQbZPaZx#3r#3r#3r#3r#ww#UJcqP#Zqbpfb7xaUrbdicqQcqRcqScm9cpHcqTb5jb5icqUcqVb6Db5ccqWb6Ab6AcqXcpMcpMcpMb8cb8cb6Acesb8#b8.b8acqYb7Lb77c.5c.5b7Lb9ScqZc.7cq0cq0cjhcnl#R8#R8cq1cq2cq3cq4cq5b6bb6bb6bb4Hb6bb6bb6cb6cb6cb6bb6bb6bcq6cq7b6bb6bb6bb6bb6bb6bcpZb7Pb7Pb7Pb6ecq8b9Ib9Pb9Ic#ic#ib7Zb7Zb7Zb7Zcazcazb7Zcazcazb7Zc#cb7ZcbZb2Yb78b4Eb6cb7Rb7Rb6cb6bb6bb4Hb4Hb4Ib4Eb4Db2Yb2Wb2Tb48ceWcq9bjQbqcb4mcr.",
+"#F0#F0#HH#V8#3p#WW#WY#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#WY#Sb#W##yt#xr#zs#zs#zs#zs#zs#xr#B.cr#cracrbcrc#7m#wB#F0#F0#F0#F0#F0#F0#F0#F0aez#F0crdaN##A5aQJ#Wvcrecrf#Ohb1kaOM#yu#yu#wxc.sa#ocrgcrhcric#GcrjcrkbSDaDC#JN#F0#F0#F0#wB#L2crl#78.aV#Erco.b5Bcrmcrncro.#M.cycrpcrqcrr#vx#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#Zpb4KcrsbYNcrtcrucrv#Kg#Lxcrwcrxcrycrzb8NcrA#A5.aV.aV.aV.dp#OZ.cbcmYbKFbRiaSPbENbENbENbENbENbENbENbENbENbENbENbENbENbENaSPbRicdnaR1aR1aR1bRibENbKGclq#77.aV.aV.aV.aV.aV#A5#Cp.dp#OZ#77#OZ#77#OZ.dp.dp#Cp#A5#A5.aV.aV.aV.aV.aV.aV.aV.aV.aV#A5#OZ#CpcrBclBckcciLc#0ciGcki#A5#77#WvcrCcrDcrEcrFcrGbwn.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.qo.kg.kg.r7.Vm.ltciP#DN.#5.32.mm.kf.ou.pq.mw.qp.OTaJI.mw.kf.kf.mw.tv.wf.wf.wf.vj.vj.vj.vj.vj#m2crHcrIcrJcrKaPz#Ev#Vw#Wa#WW#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WW#F1#2KcrL#yu#zs#zs#SX#SX#SXcm7bjQbZPbZPbjQbZPaZx#uw#3r#3r#S.#Rg#6s#4L#Zq#4Zb7xcrMaCycrNcoocqS#NycrOby5crPb3Db3Ab5fb3Bb5hcrQcrRcqXcqXcrScqXcqXcpMcqXcpMcpMb8cb8.b8.cetcc2bBHb7Kb71b77cq8cq8cqZc.7c.7cq0cq0boLcnlcnlcnlcrTbs4b6dcpZcpZcpZcpZb6db7Pb6dcpZcpZcpZcpZcpZcrUcrVcpZcpZcpZcpZcpZcpZb6db6db6db6db6db9DcrWb7Zb7Zc#ac#ac#ac#ac#ac#ac#ac#ac#ac#ac#acaAc#ac#ab9Ob2Wb4Db4Db4Db6bb9Eb9Eb6bb7Rb7Qb6bb6eb4Eb4Fb2Yb2Zb2Tb1vb1ob58bmMcrXb4mbZG",
+"#F0#HH#V8#WY#Sb#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#3p#3q#zu#yu#zs#zs#zs#zs#zs#zs#yu#ytcrYcbwcrZ#7m#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#wB#FiabIaN##A5.aV#Ticr0cr1cr2cr3aF8#SWcr#b#Fcr4cr5cr6cr7cr8cr9cs.cs#aZ0#JN#F0#F0#F0#F0#wB#L2cdj.dp.aV#ErckhcsacsbcsccmR.aAarZapmcsdcse#zu#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#yt#zr#xqbwFcsfcsgcsh#V4#KCcsicsjco0abH#wB#Fi#L2#L2#7mcgq.aV#A5#OZcfccsk#6fbRicg2bENbENbENbENbENcg2cg2bENbENbENbENbENbENbRibRibPTaTCccjckkcslcsmcqDcgp#7ackf#Cq#A5.aV.aV.aV#A5#OZ.aVcfcckhcsncsob7bcsocsncrBckfcsp#A7#Cp.dp#A5.aV.aV.aV.aV.aV.aV.aV.aV.aV#A5.dp#OZbOncso.#1bTWbRicsqcsr.aV#OZ#RE#REcsscstcm3.Lr.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.ps.kgcsu.Nc.qp.kh.Qu#uBat3.sX.un.kf.kh.mw.mw.kfc.K.JV.kc.kg.kf.kf.kj.wf.wf.wf.vj.vj.vj.vjcm3cpvcsvcswcsxcsy#2K#WW#Vw#WW#Wa#Wa#WW#Wa#Wa#Wa#Wa#Wa#Wa#Wa#WWcszbRI#zu#yu#zs#zs#zs#zs#SX#SX#SXbjQbZPbZPbjQbZPbZPaZx#3r#xq#ww#0Jb9f#It#Zq#6s#6sbdicsAcrNcsBcqRbx.csCcsDaD#csEcsFcpKcsGcrRcrQcrRcsHcqXcqXcrScqXcqXcqXcqXcpMcpMcpMb8cb8e#O5b9zb9zc.4bubb71c.5cjfcq8b7Lcc2c.7cq0cq0cq0cq0csIcnlb9Db7Qb4Hb6db6db6db6db6db6bcsJcsKcsKb9Db6bb6dcpZb6dcpZcpZcpZcpZcpZcpZb6dcpZb6bb6cc.8csLcsLc#jc#jc#jb9Nb9Nb9Nb9Nb9Nb9Nb9Nb9Nb9Nb9Nb9Nb9RcbUb4Db4Cb6ab6ab4Eb7Rcavb7Rb6bb6bb4Hb6eb4Ib4Db20b2Wb48b1qb1mbZKcnMbZH#Af",
+"#JV#XN#F1#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#F1#Z7#2##xr#zs#zs#zs#zs#zs#xr#B.bmTcsMcsN#JN#Fi#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#wB#L2c#B.aV.aV.aV.aV#A5csOcsPcsQcsRcsSb4pcfGcsTcsUcsVcsWcsXcsYcsZbBoaCZ#F0#F0#F0#F0#F0#wB#Fics0#A5.aV#OZch8cs1cs2cs3cs4.aA.#K.b0cs5cs6#vx#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#wx#zr#uwb2Jcs7cdmb1d.cN#Q0cs8cs9aoS#8c#L2#Fi#wB#wB#L3#F0#L3aamcdj#JbaOJct.bKFcdnaSPbENaSPcdncg2ch9ct#bKFciMciMcta#6fbKGbENbENay2cprc#Dctb#Qi#A5.dp#OZ#OZ#OZ#OZ#OZ.dp#A5#A5#A5#A5#Cp.dpckicsqbPTbENbENaSPbRiaSPbENazybKG#6fcaRctcckh#Er#A5.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV#A5#OZ#78ckgcdpctc#ZIctdcir.aV#A5ctectfctgcthcm3cofbwnbwn.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tv.kd.kj.Nc.lp.ke.pq#mC.Xb.2j.aa.kf.ts.kf.mw.mw.mw.W6.sN.vj.rk.kg.kf.kj.wf.wf.wf.vj.Sc#m2ctictjctkctlctmctn#Wa#S0#Vw#Vw#6v#yr#WW#Vw#Wa#Wa#Wa#Wa#Wa#Wa#Vw#8naB1aOM#yu#4K#4K#zs#zs#zs#SX#SX#SXcm7bjQbZPbjQbjQbZPbZP#uw#ww#TPbNEb9f#It#Zq#6s#UKaCKctocrNctpctqctrctscttctuctvctwctxctycqVctzcsHctActActBctBctCctDcrScrScqXcqXcqXcrSccYb6qb6pb70b9zb9zb9zcoAcoAb71c.5c.5cq8cc2cc2cc2ctEcq0ctEcq0ctFb6bctGb7PctGctGctGcpZc#.ctHcsLctIb7RcpZcpZcpZcpZcpZcpZcpZcpZcpZb6db6db6db6db6dbkQctJctJctJb9RctJb9Rb9Rb9Rb9Rb9Rb9Rb9Rb9Rb9Rb9Rb9Rb9Lcq8b4Eb4Eb4Db4Db7Pb9Gb9Gb9Eb7Rb7Rb6bb7Pb4Eb2Yb2Zb2Ub4Jb1mbZMch6#vw#vw",
+"#F1#Sb#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Sb#8n#TQ#yt#xr#zs#zs#zs#zs#zs#yt#xrctKctL#Q5#Fi#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#L2aoS#77.aV.aV.aV.aV.aV#LwbEwbFv#99.cNaN##N9aQJ#98ctMctNctObwzbD7#HH#F0#F0#F0#F0#F0#Fi#KHctP.aV.aV#OZcgtctQctRcs3b7f.aA.#L.#IctSctT#7n#ww#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#zs#xr#Zp#yu#38ctUbyX#Lx.cNbOn#78ctVctW#F0#L2#Fi#wB#F0#F0#F0#F0#F0#wB#L2csN#78c.jctabNbctXcdnbENbKGb8LcsmcgpcrBctYckgcdqcdqce7ctZckfct0.bBct1#Cp#OZ.dp#A5#A5#A5.aV.aV.aV.aV.aV.aV.aV#A5#A5#Cqcfc#O1ct2ct3clBct4ay4aqhaqhbENbENbENbENbENaSPaR1bTW#ZL#OZ#A5.aV.aV.aV.aV.aV#Cq#A5#OZ#Er.bC.dp.aV#Cq.aV.dp.dpaOJ.bBcmYcaW#A5#A5cpn#98ct5ct6ct7ct8cpv#m2cof.Qdcofbwn.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tv.tw.JV#rA.mw.mw.kf.r2.Xd.Vt.JB.MZ.mw.mw.mw.mw.kf.kg.ps.Dx.tv.kh.kg.kc.wf.wf.wf.vj#j1.0zct9cu.cu#bH#bLvaPoca.#VwcuabzyboR#Vw#WW#Wa#Wa#Wa#Wa#Wa#WWaN6cubbmd#yt#yu#HL#4K#4K#zs#zs#zs#zs#SX#SX#SXcm7bZPbjQbjQbjQbjQ#zs#zt#TQ#2Kb9f#It#ZqbNEcfK#TLcucctocudcuecufcugcuhcuic.ZcujcukculcumcuncsHcuocupctzctBctActBctBctAcuqctDcrScurcusb9xcc7b9yc.2c.3b70c.3bBHbBHb9Acf3cf3cf3cutcutcc2cq8cc2cuuctEb9Db6cb6bcpZcpZcpZb6cb7Rcuvcuwcuxb7Rb6ccpZcpZcpZcpZcpZcpZcpZcpZcpZcpZcpZcpZb6ccuyctJcuzcuzcuzcuzcuzcuzcuzcuzcuzcuzcuzcuzcuzcuzcuzcuAcuBcc6b6db4Eb4Eb4Fb7Pcavb9Gcavb9Eb7Rb6bb4Ib4Db2Zb2Ub1qb1mcuCbZNbXzbvy",
+"#F1#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#WY#WW#xw#8d#wx#xr#zs#zs#zs#zs#xr#zrbITcuDbDF#K7#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#Fiatqcbr.aV#A5.aV.aV.aV.aV.aV.aV.aV.aV#A5#78#A5cdxcuEckjc#BcuF#K7#F0#F0#F0#F0#F0#wB#L2cc##Ob#Cq.aV#OZcgtcuGcuHcuIcs4.aA.#M.bpcuJbBcbNF#zu#zs#zs#zs#zs#zs#zs#zs#zs#zs#4K#yu#B.cuKcuLaTw#Lvco3#QhaN#bCKcuMa#.#Fj#Fi#wB#F0#F0#F0#F0#F0#F0#F0#Fi#FiazAb7bbHMciMbdkclqcpn#Qk#80cuNcuObDFcuPcuOazAcs9cuQbDBbCK#X9.dp#Cp#A5.bD.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV#A5#BP#Cq#QicuRbM0#K6#wBcc#aoTcuScuTcuUc.kcdnbENbH#bENazybENcklcck#OZ#A5.aV#L0.aV#OZbKvcuVaA9a#.a#.bEvcuWcgqcigcuXcbr#Er#Jb#Cp#Cq.bD#A5.aV#LwcpncpncuYcuZcu0cu1cu2cu3cu4cu5cu6.Qdcof.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.tw.vj.Lr.tw.kh.mw.kg.JW.9X.vh.mW.rg.pu.mw.kf.kf.kf.mw.kf.kg.kc.wf.r6.kg.ps.tw.wf.Lrcu7cu8cgUcu9#69cv.cv#bv7cva#3oa.q#HGbHB#2K#WW#Wa#Wa#Wa#Wa#Wa#WW#Evcvbcvc#yu#Zp#HL#HL#4K#4K#zs#zs#zs#zs#zs#SX#SX#SXbjQbjQbjQbjQbjQ#zu#xs#Zq#2K#It#ItbNE#vycvdbTFcuccvecvf#utb0wb6JcvgbX6cvhbZVcvicvjcvkcvlb9I#Qob5ccvmcvncvocvmcvocvmcvmcuocvpcepcvqb9wb9wb7Jb7Jb6pc.2b70c.3c.3c.3cvrcoAcf3cvscf3cvscvsaCjcutaCjcvtb7PcpZcpZcpZcpZcpZcvucpZcq2b9Db7Rb6cb7Rb7Rb7Rb7Rb7Rb6cb6cb7Rb6cb7Rb6cb7Rb6bb7RcvvcvwcvxcvycvycvycvycvycvycvycvycvycvycvycvycvycvycvzcvAcsJcpZb6db4Eb4Eb4Eb9EcvBc##b9Eb7Rb6bb4Eb4DceVb1vceWb2RbZJch6#vw",
+"#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#XIbmN#GwaXr#Zp#zs#zs#zs#xr#zr#3rcvCcvDcvE#Fi#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#wB#wBcuQ#N9#A5.aV.aV.aV.aV.aV.aV.aV#A5.bC.aVcvFct4QtycvG#JUa.9#HH#F0#F0#F0#F0#wB#Fi#FicvH#V2aN#.aV.bCcvIcvJcvKclEcro.aA.#MadK#BqcvLcvM#vx#zs#zs#zs#zs#zs#zs#zs#zs#xr#yt#B.b#FcvNcvO#Cp.cNaN##76cbrcgqcvP#L2#Fi#wB#F0#F0#F0#F0#F0#F0#F0#wBa.9cle#78bHTcvQa0VbDF#HHa.8clsa.8aama.9#KG#Fj#Fj#Fj#Fj#L2#Fi#F0#88cc##87cdjcvRcvScbr.dp#CqaN#.aV.aV.aV.aV.aV.aV.aV.aV.aV.aVaQJ#Adcbr#F0#L2#wB#wBaez#L2cls#KGcvTco3ce5azybJFbENazyazybENazycvU.bC#A5.aVcbrcvVa.f#KG#Fi#wB#wB#Fi#Fj#L2#KG#Oc#JUcvWaancvX#Ob#Er#A5aN##N9.aV.aVaQJcpncpncrCcrCcrC#A5cvYcvZcv0cv1.vjcv2.Qdbwnbwn.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.tw.Lr.qp.kf.kg.kc.uB.lD.pocv3.zM.kf.kf.kf.mw.mw.mw.mw.kf.kh.tw.vj.kd.nsaTTcv4cv5cv6cv7cv8b3Wcv9cw.bv6cw#bEn#Gx#JNbEn#6r#WW#Wa#Wa#Wa#Wa#Wa#WWaN6cwacwb#Dicwc#yu#Gy#HL#HL#4K#4K#zs#zs#zs#zs#zs#SX#SX#SXcnMbjQbjQ#xrcwd#Z7#Vw#2K#2K#ItcwecfKcwfb15#O8bHs#utbYScwgcwhcwicwjbYkbZ4cwkcwlb3hcaFcwmb4Mcwncwobw7b5bcwpb6Fcwqcwpcwrc#hb7Ib7Ib40b9wb9Ob9OcbXcc7cc7cbUcbUcbUcsKcsKcvrcnkbkDctIaCEaCEaCEcvscwsboLb7Qb7Rcwtcwtcvucwtb7Rcwub9Ecwvcvub9Eb9Eb9Eb9Eb9Eb9Eb9Eb9Eb9Eb9Eb9Eb9Ecwvb9EcwwcwxcwycwzcwAcwBcwCcwDcwBcwDcwDcwDcwDcwBcwBcwBcwBcwCcwEc#hb7RcwFcpZcpZb7Pb7Pb9EcvBcvBb9Gb7Rb6bb4Eb2Zb2Ub1pb1mb23ch6#vw",
+"#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#Sbbs1#K6#HH#WV#Zp#zs#zs#xr#ytcwGcwHcs9#JN#Fi#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#wB#FjbDC.aV.aV.aV.aV.aV.aV.aV.aV#A5.dpbOn#ZLcdvcdnb8L#8acls#Fi#F0#F0#F0#F0#F0#wB#FicvWcvS.aV.aV#A5.dpcwIcwJcwKcwLcwM.#M.#LarZcwNcwOcwP#ww#zs#zs#zs#zs#zs#zs#zs#zs#xr#wxcwQcwRaKP#N9.cN.aV.aVaaoazA#96#KG#wB#L3#F0#F0#F0#F0#F0#F0#F0#F0#Fi#8cazActb#0p#RV#KGa.8#Fj#wB#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#wB#wB#wB#PK#Fi#Fj#L2#Fi#96aA9cdjbJs#0q.aV.aV.aV.aV.aV.aV.aV.aV.aV.aV#CqcuQ#L2#wB#F0#F0#F0#F0#F0#wB#KGcls#96cwScwTccdcg2azyazybENay2cvI.dpcwUaoSa.9#wB#F0#F0#F0#F0#F0#F0#F0#Im#wB#wB#Fi#Fj#Fj#wB#Fhco0cwVcrA#Er.aV.aV.aV#Lw#Lw#Lw#LwaQJcpncrCcpn#QMcwWcwXcwYcwZ.Lrcof.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.wf.Tz.kg.kg.zK.mx.sX.0F.DB.ps.kg.mw.mw.mw.mw.mw.mw.mw.kf.kg.kc.wf.kjcw0cw1cw2cw3#03bFTbH#bRicw4aPzcw5#Il#L3#ImaXd#WW#Vw#Wa#Wa#Wa#Wa#Wa#WW#WW#Q6cw6cw7aZn.X#cw8cw9#Zp#HL#4K#4K#4K#zs#zs#zs#zs#zs#SX#SX#zs#zsbnk#zs#xqcx.#2K#2K#2K#4Lcx.clb#Ohb15b15cx#cxabYRbYPb11cxbcxcbYlcxdcxecxfcxgcxhcxicb2cxjb9ub9vcxkcxjcxjcxjc#kc#kb7Ib9wcazcazb9wb9wb9Pb9Pb9Pcp4cbVcbVbkQcvrcsKcsKcsKcvvcvv#Of#Ofcxlcxl#Q8cxlcxmb9Gb9Gcxncxnb9Gcxnb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9Gb9GcxocxpcxpcxqcxrcwDcxscxscxscxscxscwBcwEcwEcwEcwEcwEcwEcxtcxt#Q8b9Gb7Rb7RcwtcpZb7Pb6bcvBcvBcavb6bb4Hb4Db2Tb1vcxubZMch6#Ol",
+"#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#Jo#F1#Fl#PK#F0#wA#wz#2##xr#2#cmHcxv#V4cxwa.9#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#Ficc##OZ.aV#BP.aV.aV.aV.aV.aV#Cp#Cp#ZLctabRiaR1cxx#RVcxy#wB#F0#F0#F0#F0#F0#wB#L2cuVcxzaN#.aV#A5#CpckgciMcxAcxBcxC.#M.#L.#L.d5cxDcxE#vx#zs#zs#zs#zs#zs#zs#zs#zs#xraOMcg8cxFaL0#N9.aV.aV.aVcrAcxGa.9#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#L2cxHa##csnc.iclsa.9#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#wB#wB#Fi#L2#FjatqcuM#OZ.aV.aV.aV.aV.aV.aV.aV.aV.aVaMR#wB#wB#F0#F0#F0#F0#F0#F0#F0#F0aeza.8#L2csjcmXaSPbENazycdncsm#OZctPaam#wB#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#F0#wB#Fi#KG#L2#wBcvWbDC#SL#A5.aV.aV.aV.aV.aV.aV.aVaQJaQJcrCcrC#BP#U.cvZcxI.Vm.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.wf.vj.vj.sO.kg.pu.ps.e6.ds.lD.5U.Nc.zM.kf.mw.mw.mw.mw.mw.mw.mw.mw.kg.nr.twbb8cxJcbj#TdaR2bH#cxKc.kaw2#JYcxL#Im#Im#Im#xu#xtaN6#Wa#Wa#Wa#Wa#Wa#SbaN6bPCcxMcu0.2h.r8.X#cgKcxN#HL#yu#HL#4K#4K#4K#zs#zs#zs#zs#zs#SX#zs#zs#zsaOMcxO#W.#2K#2K#2K#SaconcxP#OhcxQcxR#NxbWYcxSbYHcxTcxUcxVcxWcxdbZ7cxfcxgcxXcaFcb0cwtb9Eb9EcwtctFc.7b9Ob72b72caAc#ccazcazb7Zb9Qc#ic#ib9PctHctHcxYcxZcx0c#hcsKcsKcuxcx1cx1cx1cxrcxrcx2c#gcx3cwtcx4cx5b9Gcx5b9Gb9Gcx4cx4cx4cx4cx4cx4cx6cx4cx4cx4cx4cx4cx4cx4cx4cx6cx4cx4cx4cx7cx7cx8cx9cy.cy#cy#cyacy#cyacyacy#cy#cyacyacyacyacybcyccydb9GcwvcwvcvucpZb6db6dcavcyecyfb7Rb4Eb20b2Ub1obZMbZNbG8"};
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt
index 542bcc8f7c..3f8c7dd921 100644
--- a/libs/CMakeLists.txt
+++ b/libs/CMakeLists.txt
@@ -1,17 +1,18 @@
add_subdirectory( version )
add_subdirectory( global )
add_subdirectory( koplugin )
add_subdirectory( widgetutils )
add_subdirectory( widgets )
add_subdirectory( store )
add_subdirectory( odf )
add_subdirectory( flake )
add_subdirectory( basicflakes )
add_subdirectory( pigment )
add_subdirectory( kundo2 )
add_subdirectory( brush )
add_subdirectory( psd )
add_subdirectory( color )
add_subdirectory( image )
add_subdirectory( ui )
add_subdirectory( vectorimage )
+add_subdirectory( impex )
diff --git a/libs/flake/tests/TestShapeStrokeCommand.cpp b/libs/flake/tests/TestShapeStrokeCommand.cpp
index 43a7494d4d..f08d48ac3b 100644
--- a/libs/flake/tests/TestShapeStrokeCommand.cpp
+++ b/libs/flake/tests/TestShapeStrokeCommand.cpp
@@ -1,69 +1,72 @@
/* 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 "TestShapeStrokeCommand.h"
#include <MockShapes.h>
#include "KoShapeStrokeModel.h"
#include "KoShapeStroke.h"
#include "KoShapeStrokeCommand.h"
#include <KoInsets.h>
#include <QTest>
void TestShapeStrokeCommand::refCounting()
{
MockShape * shape1 = new MockShape();
KoShapeStrokeModel *whiteStroke = new KoShapeStroke(1.0, QColor(Qt::white));
KoShapeStrokeModel *blackStroke = new KoShapeStroke(1.0, QColor(Qt::black));
KoShapeStrokeModel *redStroke = new KoShapeStroke(1.0, QColor(Qt::red));
shape1->setStroke(whiteStroke);
QVERIFY(shape1->stroke() == whiteStroke);
QCOMPARE(whiteStroke->useCount(), 1);
// old stroke is white, new stroke is black
KUndo2Command *cmd1 = new KoShapeStrokeCommand(shape1, blackStroke);
cmd1->redo();
QVERIFY(shape1->stroke() == blackStroke);
// change stroke back to white stroke
cmd1->undo();
QVERIFY(shape1->stroke() == whiteStroke);
// old stroke is white, new stroke is red
KUndo2Command *cmd2 = new KoShapeStrokeCommand(shape1, redStroke);
cmd2->redo();
QVERIFY(shape1->stroke() == redStroke);
// this command has the white stroke as the old stroke
delete cmd1;
// set stroke back to white stroke
cmd2->undo();
QVERIFY(shape1->stroke() == whiteStroke);
// if white is deleted when deleting cmd1 this will crash
KoInsets insets;
whiteStroke->strokeInsets(shape1, insets);
delete cmd2;
delete shape1;
+ delete whiteStroke;
+ delete blackStroke;
+ delete redStroke;
}
QTEST_MAIN(TestShapeStrokeCommand)
diff --git a/libs/global/kis_shared_ptr.h b/libs/global/kis_shared_ptr.h
index 4b35c488af..5453bfabb3 100644
--- a/libs/global/kis_shared_ptr.h
+++ b/libs/global/kis_shared_ptr.h
@@ -1,508 +1,521 @@
/*
* 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 << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return d;
}
/**
* @see data()
*/
inline const T* data() const {
if (!isConsistent()) {
warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return d;
}
/**
* @see data()
*/
inline const T* constData() const {
if (!isConsistent()) {
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 << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return *d;
}
inline T& operator*() {
if (!isValid()) {
warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return *d;
}
inline const T* operator->() const {
if (!isValid()) {
warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return d;
}
inline T* operator->() {
if (!isValid()) {
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);
}
+
+ /**
+ * @brief toStrongRef returns a KisSharedPtr which may be dereferenced.
+ *
+ * Weak pointers should only be used to track ownership but never be used as pointers.
+ * This has historically not been the case, but in new API this function should be used
+ * instead of directly using a weak pointer as pointer.
+ * @return a KisSharedPtr, which may be null
+ */
+ inline KisSharedPtr<T> toStrongRef() const {
+ return KisSharedPtr<T>(*this);
+ }
+
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/libs/image/brushengine/kis_paintop_preset.cpp b/libs/image/brushengine/kis_paintop_preset.cpp
index 5e42e14d55..30172a7897 100644
--- a/libs/image/brushengine/kis_paintop_preset.cpp
+++ b/libs/image/brushengine/kis_paintop_preset.cpp
@@ -1,405 +1,405 @@
/* 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 <brushengine/kis_paintop_preset.h>
#include <QFile>
#include <QSize>
#include <QImage>
#include <QImageWriter>
#include <QImageReader>
#include <QDomDocument>
#include <QBuffer>
#include <brushengine/kis_paintop_settings.h>
#include "kis_paintop_registry.h"
#include "kis_painter.h"
#include <brushengine/kis_paint_information.h>
#include "kis_paint_device.h"
#include "kis_image.h"
#include "kis_paintop_settings_update_proxy.h"
#include <brushengine/kis_paintop_config_widget.h>
#include <KoStore.h>
struct Q_DECL_HIDDEN KisPaintOpPreset::Private {
Private()
: settings(0),
dirtyPreset(false)
{
}
KisPaintOpSettingsSP settings;
bool dirtyPreset;
QScopedPointer<KisPaintopSettingsUpdateProxy> updateProxy;
};
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();
+ KisPaintOpPresetSP preset(new KisPaintOpPreset());
if (settings()) {
preset->setSettings(settings()); // the settings are cloned inside!
}
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::setOptionsWidget(KisPaintOpConfigWidget* widget)
{
if (m_d->settings) {
m_d->settings->setOptionsWidget(widget);
if (widget) {
widget->setConfigurationSafe(m_d->settings);
}
}
}
void KisPaintOpPreset::setSettings(KisPaintOpSettingsSP settings)
{
Q_ASSERT(settings);
Q_ASSERT(!settings->getString("paintop", QString()).isEmpty());
DirtyStateSaver dirtyStateSaver(this);
KisPaintOpConfigWidget *oldOptionsWidget = 0;
if (m_d->settings) {
oldOptionsWidget = m_d->settings->optionsWidget();
m_d->settings->setOptionsWidget(0);
m_d->settings->setPreset(0);
m_d->settings = 0;
}
if (settings) {
m_d->settings = settings->clone();
m_d->settings->setPreset(KisPaintOpPresetWSP(this));
if (oldOptionsWidget) {
m_d->settings->setOptionsWidget(oldOptionsWidget);
oldOptionsWidget->setConfigurationSafe(m_d->settings);
}
}
setValid(m_d->settings);
if (m_d->updateProxy) {
m_d->updateProxy->notifyUniformPropertiesChanged();
m_d->updateProxy->notifySettingsChanged();
}
}
KisPaintOpSettingsSP KisPaintOpPreset::settings() const
{
Q_ASSERT(m_d->settings);
Q_ASSERT(!m_d->settings->getString("paintop", QString()).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://")) {
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()) {
warnKrita << "Could not open store on bundle" << bn;
return false;
}
if (resourceStore->isOpen()) resourceStore->close();
if (!resourceStore->open(fn)) {
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", QString());
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", QString());
elt.setAttribute("paintopid", paintopid);
elt.setAttribute("name", name());
// sanitize the settings
bool hasTexture = m_d->settings->getBool("Texture/Pattern/Enabled");
if (!hasTexture) {
Q_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, QString());
KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->settings(id);
if (!settings) {
setValid(false);
warnKrita << "Could not load settings for preset" << paintopid;
return;
}
settings->fromXML(presetElt);
// sanitize the settings
bool hasTexture = settings->getBool("Texture/Pattern/Enabled");
if (!hasTexture) {
Q_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);
}
KisPaintopSettingsUpdateProxy* KisPaintOpPreset::updateProxy() const
{
if (!m_d->updateProxy) {
m_d->updateProxy.reset(new KisPaintopSettingsUpdateProxy());
}
return m_d->updateProxy.data();
}
KisPaintopSettingsUpdateProxy* KisPaintOpPreset::updateProxyNoCreate() const
{
return m_d->updateProxy.data();
}
QList<KisUniformPaintOpPropertySP> KisPaintOpPreset::uniformProperties()
{
return m_d->settings->uniformProperties(m_d->settings);
}
KisPaintOpPreset::UpdatedPostponer::UpdatedPostponer(KisPaintOpPreset *preset)
: m_updateProxy(preset->updateProxyNoCreate())
{
if (m_updateProxy) {
m_updateProxy->postponeSettingsChanges();
}
}
KisPaintOpPreset::UpdatedPostponer::~UpdatedPostponer()
{
if (m_updateProxy) {
m_updateProxy->unpostponeSettingsChanges();
}
}
diff --git a/libs/image/brushengine/kis_paintop_registry.cc b/libs/image/brushengine/kis_paintop_registry.cc
index 415a18f85f..f2ddde1361 100644
--- a/libs/image/brushengine/kis_paintop_registry.cc
+++ b/libs/image/brushengine/kis_paintop_registry.cc
@@ -1,174 +1,173 @@
/*
* 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 <QGlobalStatic>
#include <klocalizedstring.h>
#include <KoGenericRegistry.h>
#include <KoPluginLoader.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOp.h>
#include <KoID.h>
#include "kis_paint_device.h"
#include "kis_painter.h"
#include "kis_debug.h"
#include "kis_layer.h"
#include "kis_image.h"
#include "kis_paintop_config_widget.h"
Q_GLOBAL_STATIC(KisPaintOpRegistry, s_registryInstance)
KisPaintOpRegistry::KisPaintOpRegistry()
{
}
KisPaintOpRegistry::~KisPaintOpRegistry()
{
Q_FOREACH (const QString & id, keys()) {
delete get(id);
}
dbgRegistry << "Deleting KisPaintOpRegistry";
}
void KisPaintOpRegistry::initRegistry()
{
KoPluginLoader::instance()->load("Krita/Paintop", "(Type == 'Service') and ([X-Krita-Version] == 28)");
QStringList toBeRemoved;
Q_FOREACH (const QString & id, keys()) {
KisPaintOpFactory *factory = get(id);
if (!factory->settings()) {
toBeRemoved << id;
} else {
factory->processAfterLoading();
}
}
Q_FOREACH (const QString & id, toBeRemoved) {
remove(id);
}
}
KisPaintOpRegistry* KisPaintOpRegistry::instance()
{
if (!s_registryInstance.exists()) {
s_registryInstance->initRegistry();
}
return s_registryInstance;
}
#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;
}
}
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;
+ return KisPaintOpPresetSP();
}
+ KisPaintOpPresetSP preset(new KisPaintOpPreset());
+ preset->setName(i18n("default"));
+
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;
Q_FOREACH (const QString & key, keys()) {
answer.append(KoID(key, get(key)->name()));
}
return answer;
}
diff --git a/libs/image/brushengine/kis_paintop_settings.cpp b/libs/image/brushengine/kis_paintop_settings.cpp
index b73ba900f7..8cdb613799 100644
--- a/libs/image/brushengine/kis_paintop_settings.cpp
+++ b/libs/image/brushengine/kis_paintop_settings.cpp
@@ -1,430 +1,434 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008 Lukáš Tvrdý <lukast.dev@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 <brushengine/kis_paintop_settings.h>
#include <QImage>
#include <QColor>
#include <QPointer>
#include <KoPointerEvent.h>
#include <KoColor.h>
#include <KoCompositeOpRegistry.h>
#include <KoViewConverter.h>
#include "kis_paint_layer.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_paint_device.h"
#include "kis_paintop_registry.h"
#include <brushengine/kis_paint_information.h>
#include "kis_paintop_config_widget.h"
#include <brushengine/kis_paintop_preset.h>
#include "kis_paintop_settings_update_proxy.h"
#include <time.h>
#include<kis_types.h>
#include <kis_signals_blocker.h>
#include <brushengine/kis_locked_properties_server.h>
#include <brushengine/kis_locked_properties_proxy.h>
struct Q_DECL_HIDDEN KisPaintOpSettings::Private {
Private()
: disableDirtyNotifications(false)
{}
QPointer<KisPaintOpConfigWidget> settingsWidget;
QString modelName;
KisPaintOpPresetWSP preset;
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
bool disableDirtyNotifications;
class DirtyNotificationsLocker {
public:
DirtyNotificationsLocker(KisPaintOpSettings::Private *d)
: m_d(d),
m_oldNotificationsState(d->disableDirtyNotifications)
{
m_d->disableDirtyNotifications = true;
}
~DirtyNotificationsLocker() {
m_d->disableDirtyNotifications = m_oldNotificationsState;
}
private:
KisPaintOpSettings::Private *m_d;
bool m_oldNotificationsState;
Q_DISABLE_COPY(DirtyNotificationsLocker)
};
KisPaintopSettingsUpdateProxy* updateProxyNoCreate() const {
- return preset ? preset->updateProxyNoCreate() : 0;
+ auto presetSP = preset.toStrongRef();
+ return presetSP ? presetSP->updateProxyNoCreate() : 0;
}
KisPaintopSettingsUpdateProxy* updateProxyCreate() const {
- return preset ? preset->updateProxy() : 0;
+ auto presetSP = preset.toStrongRef();
+ return presetSP ? presetSP->updateProxy() : 0;
}
};
KisPaintOpSettings::KisPaintOpSettings()
: d(new Private)
{
d->preset = 0;
}
KisPaintOpSettings::~KisPaintOpSettings()
{
}
KisPaintOpSettings::KisPaintOpSettings(const KisPaintOpSettings &rhs)
: KisPropertiesConfiguration(rhs)
, d(new Private)
{
d->settingsWidget = 0;
d->preset = rhs.preset();
d->modelName = rhs.modelName();
}
void KisPaintOpSettings::setOptionsWidget(KisPaintOpConfigWidget* widget)
{
d->settingsWidget = widget;
}
void KisPaintOpSettings::setPreset(KisPaintOpPresetWSP preset)
{
d->preset = preset;
}
KisPaintOpPresetWSP KisPaintOpSettings::preset() const
{
return d->preset;
}
bool KisPaintOpSettings::mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode)
{
Q_UNUSED(modifiers);
Q_UNUSED(currentNode);
setRandomOffset(paintInformation);
return true; // ignore the event by default
}
void KisPaintOpSettings::setRandomOffset(const KisPaintInformation &paintInformation)
{
if (getBool("Texture/Pattern/Enabled")) {
if (getBool("Texture/Pattern/isRandomOffsetX")) {
setProperty("Texture/Pattern/OffsetX",
paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetX")));
}
if (getBool("Texture/Pattern/isRandomOffsetY")) {
setProperty("Texture/Pattern/OffsetY",
paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetY")));
}
}
}
KisPaintOpSettingsSP KisPaintOpSettings::clone() const
{
QString paintopID = getString("paintop");
if (paintopID.isEmpty())
return 0;
KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->settings(KoID(paintopID, ""));
QMapIterator<QString, QVariant> i(getProperties());
while (i.hasNext()) {
i.next();
settings->setProperty(i.key(), QVariant(i.value()));
}
settings->setPreset(this->preset());
return settings;
}
void KisPaintOpSettings::activate()
{
}
void KisPaintOpSettings::setPaintOpOpacity(qreal value)
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
proxy->setProperty("OpacityValue", value);
}
void KisPaintOpSettings::setPaintOpFlow(qreal value)
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
proxy->setProperty("FlowValue", value);
}
void KisPaintOpSettings::setPaintOpCompositeOp(const QString &value)
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
proxy->setProperty("CompositeOp", value);
}
qreal KisPaintOpSettings::paintOpOpacity()
{
KisLockedPropertiesProxySP proxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this);
return proxy->getDouble("OpacityValue", 1.0);
}
qreal KisPaintOpSettings::paintOpFlow()
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
return proxy->getDouble("FlowValue", 1.0);
}
QString KisPaintOpSettings::paintOpCompositeOp()
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
return proxy->getString("CompositeOp", COMPOSITE_OVER);
}
void KisPaintOpSettings::setEraserMode(bool value)
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
proxy->setProperty("EraserMode", value);
}
bool KisPaintOpSettings::eraserMode()
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
return proxy->getBool("EraserMode", false);
}
QString KisPaintOpSettings::effectivePaintOpCompositeOp()
{
return !eraserMode() ? paintOpCompositeOp() : COMPOSITE_ERASE;
}
qreal KisPaintOpSettings::savedEraserSize() const
{
return getDouble("SavedEraserSize", 0.0);
}
void KisPaintOpSettings::setSavedEraserSize(qreal value)
{
setProperty("SavedEraserSize", value);
setPropertyNotSaved("SavedEraserSize");
}
qreal KisPaintOpSettings::savedBrushSize() const
{
return getDouble("SavedBrushSize", 0.0);
}
void KisPaintOpSettings::setSavedBrushSize(qreal value)
{
setProperty("SavedBrushSize", value);
setPropertyNotSaved("SavedBrushSize");
}
qreal KisPaintOpSettings::savedEraserOpacity() const
{
return getDouble("SavedEraserOpacity", 0.0);
}
void KisPaintOpSettings::setSavedEraserOpacity(qreal value)
{
setProperty("SavedEraserOpacity", value);
setPropertyNotSaved("SavedEraserOpacity");
}
qreal KisPaintOpSettings::savedBrushOpacity() const
{
return getDouble("SavedBrushOpacity", 0.0);
}
void KisPaintOpSettings::setSavedBrushOpacity(qreal value)
{
setProperty("SavedBrushOpacity", value);
setPropertyNotSaved("SavedBrushOpacity");
}
QString KisPaintOpSettings::modelName() const
{
return d->modelName;
}
void KisPaintOpSettings::setModelName(const QString & modelName)
{
d->modelName = modelName;
}
KisPaintOpConfigWidget* KisPaintOpSettings::optionsWidget() const
{
if (d->settingsWidget.isNull())
return 0;
return d->settingsWidget.data();
}
bool KisPaintOpSettings::isValid() const
{
return true;
}
bool KisPaintOpSettings::isLoadable()
{
return isValid();
}
QString KisPaintOpSettings::indirectPaintingCompositeOp() const
{
return COMPOSITE_ALPHA_DARKEN;
}
QPainterPath KisPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode)
{
QPainterPath path;
if (mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline) {
path = ellipseOutline(10, 10, 1.0, 0);
if (mode == CursorTiltOutline) {
path.addPath(makeTiltIndicator(info, QPointF(0.0, 0.0), 0.0, 2.0));
}
path.translate(info.pos());
}
return path;
}
QPainterPath KisPaintOpSettings::ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation)
{
QPainterPath path;
QRectF ellipse(0, 0, width * scale, height * scale);
ellipse.translate(-ellipse.center());
path.addEllipse(ellipse);
QTransform m;
m.reset();
m.rotate(rotation);
path = m.map(path);
return path;
}
QPainterPath KisPaintOpSettings::makeTiltIndicator(KisPaintInformation const& info,
QPointF const& start, qreal maxLength, qreal angle)
{
if (maxLength == 0.0) maxLength = 50.0;
maxLength = qMax(maxLength, 50.0);
qreal const length = maxLength * (1 - info.tiltElevation(info, 60.0, 60.0, true));
qreal const baseAngle = 360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0);
QLineF guideLine = QLineF::fromPolar(length, baseAngle + angle);
guideLine.translate(start);
QPainterPath ret;
ret.moveTo(guideLine.p1());
ret.lineTo(guideLine.p2());
guideLine.setAngle(baseAngle - angle);
ret.lineTo(guideLine.p2());
ret.lineTo(guideLine.p1());
return ret;
}
void KisPaintOpSettings::setCanvasRotation(qreal angle)
{
Private::DirtyNotificationsLocker locker(d.data());
setProperty("runtimeCanvasRotation", angle);
setPropertyNotSaved("runtimeCanvasRotation");
}
void KisPaintOpSettings::setCanvasMirroring(bool xAxisMirrored, bool yAxisMirrored)
{
Private::DirtyNotificationsLocker locker(d.data());
setProperty("runtimeCanvasMirroredX", xAxisMirrored);
setPropertyNotSaved("runtimeCanvasMirroredX");
setProperty("runtimeCanvasMirroredY", yAxisMirrored);
setPropertyNotSaved("runtimeCanvasMirroredY");
}
void KisPaintOpSettings::setProperty(const QString & name, const QVariant & value)
{
if (value != KisPropertiesConfiguration::getProperty(name) &&
- !d->disableDirtyNotifications && this->preset()) {
-
- this->preset()->setPresetDirty(true);
+ !d->disableDirtyNotifications) {
+ KisPaintOpPresetSP presetSP = preset().toStrongRef();
+ if (presetSP) {
+ presetSP->setPresetDirty(true);
+ }
}
KisPropertiesConfiguration::setProperty(name, value);
onPropertyChanged();
}
void KisPaintOpSettings::onPropertyChanged()
{
KisPaintopSettingsUpdateProxy *proxy = d->updateProxyNoCreate();
if (proxy) {
proxy->notifySettingsChanged();
}
}
bool KisPaintOpSettings::isLodUserAllowed(const KisPropertiesConfigurationSP config)
{
return config->getBool("lodUserAllowed", true);
}
void KisPaintOpSettings::setLodUserAllowed(KisPropertiesConfigurationSP config, bool value)
{
config->setProperty("lodUserAllowed", value);
}
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(d->uniformProperties);
if (props.isEmpty()) {
using namespace KisStandardUniformPropertiesFactory;
props.append(createProperty(opacity, settings, d->updateProxyCreate()));
props.append(createProperty(size, settings, d->updateProxyCreate()));
props.append(createProperty(flow, settings, d->updateProxyCreate()));
d->uniformProperties = listStrongToWeak(props);
}
return props;
}
diff --git a/libs/image/commands/kis_change_filter_command.h b/libs/image/commands/kis_change_filter_command.h
index c095c531b5..f949ec1480 100644
--- a/libs/image/commands/kis_change_filter_command.h
+++ b/libs/image/commands/kis_change_filter_command.h
@@ -1,96 +1,96 @@
/*
* Copyright (c) 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_CHANGE_FILTER_COMMAND_H
#define KIS_CHANGE_FILTER_COMMAND_H
#include <kundo2command.h>
#include <QRect>
#include "kis_types.h"
#include <klocalizedstring.h>
#include "filter/kis_filter_configuration.h"
#include "kis_node.h"
#include "kis_node_filter_interface.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter.h"
#include "generator/kis_generator_registry.h"
#include "generator/kis_generator.h"
class KisChangeFilterCmd : public KUndo2Command
{
public:
KisChangeFilterCmd(KisNodeSP node,
const QString &filterNameBefore,
const QString &xmlBefore,
const QString &filterNameAfter,
const QString &xmlAfter,
bool useGeneratorRegistry)
: KUndo2Command(kundo2_i18n("Change Filter")) {
m_node = node;
m_filterInterface = dynamic_cast<KisNodeFilterInterface*>(node.data());
Q_ASSERT(m_filterInterface);
m_useGeneratorRegistry = useGeneratorRegistry;
m_xmlBefore = xmlBefore;
m_xmlAfter = xmlAfter;
m_filterNameBefore = filterNameBefore;
m_filterNameAfter = filterNameAfter;
}
public:
virtual void redo() {
m_filterInterface->setFilter(createConfiguration(m_filterNameAfter, m_xmlAfter));
m_node->setDirty();
}
virtual void undo() {
m_filterInterface->setFilter(createConfiguration(m_filterNameBefore, m_xmlBefore));
m_node->setDirty();
}
private:
KisFilterConfigurationSP createConfiguration(const QString &name, const QString &data)
{
KisFilterConfigurationSP config;
if (m_useGeneratorRegistry) {
KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(name);
- config = generator->defaultConfiguration(0);
+ config = generator->defaultConfiguration(KisPaintDeviceSP());
} else {
KisFilterSP filter = KisFilterRegistry::instance()->value(name);
- config = filter->defaultConfiguration(0);
+ config = filter->defaultConfiguration(KisPaintDeviceSP());
}
config->fromXML(data);
return config;
}
private:
KisNodeSP m_node;
KisNodeFilterInterface *m_filterInterface;
bool m_useGeneratorRegistry;
QString m_xmlBefore;
QString m_xmlAfter;
QString m_filterNameBefore;
QString m_filterNameAfter;
};
#endif
diff --git a/libs/image/commands/kis_deselect_global_selection_command.cpp b/libs/image/commands/kis_deselect_global_selection_command.cpp
index 05c929077a..919757c055 100644
--- a/libs/image/commands/kis_deselect_global_selection_command.cpp
+++ b/libs/image/commands/kis_deselect_global_selection_command.cpp
@@ -1,48 +1,54 @@
/*
* 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_selection_commands.h"
#include <klocalizedstring.h>
#include "kis_image.h"
#include "kis_selection.h"
#include "kis_undo_adapter.h"
#include "kis_selection_mask.h"
#include "kis_pixel_selection.h"
KisDeselectGlobalSelectionCommand::KisDeselectGlobalSelectionCommand(KisImageWSP image, KUndo2Command * parent) :
KUndo2Command(kundo2_i18n("Deselect"), parent)
, m_image(image)
{
}
KisDeselectGlobalSelectionCommand::~KisDeselectGlobalSelectionCommand()
{
}
void KisDeselectGlobalSelectionCommand::redo()
{
- m_oldSelection = m_image->globalSelection();
- m_image->deselectGlobalSelection();
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ m_oldSelection = image->globalSelection();
+ image->deselectGlobalSelection();
+ }
}
void KisDeselectGlobalSelectionCommand::undo()
{
- m_image->setGlobalSelection(m_oldSelection);
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ image->setGlobalSelection(m_oldSelection);
+ }
}
diff --git a/libs/image/commands/kis_image_change_layers_command.cpp b/libs/image/commands/kis_image_change_layers_command.cpp
index 9649f2cc64..7fe87eea15 100644
--- a/libs/image/commands/kis_image_change_layers_command.cpp
+++ b/libs/image/commands/kis_image_change_layers_command.cpp
@@ -1,48 +1,52 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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_image_commands.h"
#include "kis_image.h"
#include "kis_group_layer.h"
#include <klocalizedstring.h>
KisImageChangeLayersCommand::KisImageChangeLayersCommand(KisImageWSP image, KisNodeSP oldRootLayer, KisNodeSP newRootLayer)
: KisImageCommand(kundo2_noi18n("change-layer-command"), image)
{
m_oldRootLayer = oldRootLayer;
m_newRootLayer = newRootLayer;
}
void KisImageChangeLayersCommand::redo()
{
- m_image->setRootLayer(static_cast<KisGroupLayer*>(m_newRootLayer.data()));
-
- m_image->refreshGraphAsync();
- m_image->notifyLayersChanged();
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ image->setRootLayer(static_cast<KisGroupLayer*>(m_newRootLayer.data()));
+ image->refreshGraphAsync();
+ image->notifyLayersChanged();
+ }
}
void KisImageChangeLayersCommand::undo()
{
- m_image->setRootLayer(static_cast<KisGroupLayer*>(m_oldRootLayer.data()));
-
- m_image->refreshGraphAsync();
- m_image->notifyLayersChanged();
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ image->setRootLayer(static_cast<KisGroupLayer*>(m_oldRootLayer.data()));
+ image->refreshGraphAsync();
+ image->notifyLayersChanged();
+ }
}
diff --git a/libs/image/commands/kis_image_command.cpp b/libs/image/commands/kis_image_command.cpp
index 8bccab5b6f..f0679bca30 100644
--- a/libs/image/commands/kis_image_command.cpp
+++ b/libs/image/commands/kis_image_command.cpp
@@ -1,81 +1,84 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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_image_commands.h"
#include <klocalizedstring.h>
#include "kis_image.h"
#include "kis_layer.h"
KisImageCommand::KisImageCommand(const KUndo2MagicString& name, KisImageWSP image, KUndo2Command *parent)
: KUndo2Command(name, parent)
, m_image(image)
{
}
KisImageCommand::~KisImageCommand()
{
}
static inline bool isLayer(KisNodeSP node) {
return qobject_cast<KisLayer*>(node.data());
}
KisImageCommand::UpdateTarget::UpdateTarget(KisImageWSP image,
KisNodeSP removedNode,
const QRect &updateRect)
: m_image(image), m_updateRect(updateRect)
{
/**
* We are saving an index, but not shared pointer, because the
* target node may suddenly reincarnate into another type of a
* layer during the removal process
*/
m_removedNodeParent = removedNode->parent();
m_removedNodeIndex = m_removedNodeParent ? m_removedNodeParent->index(removedNode) : -1;
}
void KisImageCommand::UpdateTarget::update() {
if (!m_removedNodeParent) return;
KIS_ASSERT_RECOVER_RETURN(m_removedNodeIndex >= 0);
KisNodeSP node;
int index = m_removedNodeIndex;
while ((node = m_removedNodeParent->at(index)) && !isLayer(node)) {
index++;
}
if (!node) {
index = qMax(0, m_removedNodeIndex - 1);
while ((node = m_removedNodeParent->at(index)) && !isLayer(node)) {
index--;
}
}
if (node) {
node->setDirty(m_updateRect);
} else {
- m_image->refreshGraphAsync(m_removedNodeParent);
- m_removedNodeParent->setDirty(m_updateRect);
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ image->refreshGraphAsync(m_removedNodeParent);
+ m_removedNodeParent->setDirty(m_updateRect);
+ }
}
}
diff --git a/libs/image/commands/kis_image_layer_add_command.cpp b/libs/image/commands/kis_image_layer_add_command.cpp
index e92280fc97..95a144315f 100644
--- a/libs/image/commands/kis_image_layer_add_command.cpp
+++ b/libs/image/commands/kis_image_layer_add_command.cpp
@@ -1,80 +1,88 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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_image_commands.h"
#include "kis_image.h"
#include <klocalizedstring.h>
KisImageLayerAddCommand::KisImageLayerAddCommand(KisImageWSP image,
KisNodeSP layer,
KisNodeSP parent,
KisNodeSP aboveThis,
bool doRedoUpdates,
bool doUndoUpdates)
: KisImageCommand(kundo2_i18n("Add Layer"), image),
m_index(-1),
m_doRedoUpdates(doRedoUpdates),
m_doUndoUpdates(doUndoUpdates)
{
m_layer = layer;
m_parent = parent;
m_aboveThis = aboveThis;
}
KisImageLayerAddCommand::KisImageLayerAddCommand(KisImageWSP image,
KisNodeSP layer,
KisNodeSP parent,
quint32 index,
bool doRedoUpdates,
bool doUndoUpdates)
: KisImageCommand(kundo2_i18n("Add Layer"), image),
m_index(index),
m_doRedoUpdates(doRedoUpdates),
m_doUndoUpdates(doUndoUpdates)
{
m_layer = layer;
m_parent = parent;
m_aboveThis = 0;
}
void KisImageLayerAddCommand::redo()
{
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_aboveThis || m_index == quint32(-1)) {
- m_image->addNode(m_layer, m_parent, m_aboveThis);
+ image->addNode(m_layer, m_parent, m_aboveThis);
} else {
- m_image->addNode(m_layer, m_parent, m_index);
+ image->addNode(m_layer, m_parent, m_index);
}
if (m_doRedoUpdates) {
- m_layer->setDirty(m_image->bounds());
+ m_layer->setDirty(image->bounds());
}
}
void KisImageLayerAddCommand::undo()
{
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_doUndoUpdates) {
- UpdateTarget target(m_image, m_layer, m_image->bounds());
- m_image->removeNode(m_layer);
+ UpdateTarget target(image, m_layer, image->bounds());
+ image->removeNode(m_layer);
target.update();
} else {
- m_image->removeNode(m_layer);
+ image->removeNode(m_layer);
}
}
diff --git a/libs/image/commands/kis_image_layer_move_command.cpp b/libs/image/commands/kis_image_layer_move_command.cpp
index 23f0635e9f..be6d55c300 100644
--- a/libs/image/commands/kis_image_layer_move_command.cpp
+++ b/libs/image/commands/kis_image_layer_move_command.cpp
@@ -1,89 +1,96 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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_image_commands.h"
#include <QString>
#include <QBitArray>
#include <klocalizedstring.h>
#include "KoColor.h"
#include "KoColorProfile.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_group_layer.h"
#include "kis_undo_adapter.h"
KisImageLayerMoveCommand::KisImageLayerMoveCommand(KisImageWSP image, KisNodeSP layer, KisNodeSP newParent, KisNodeSP newAbove, bool doUpdates)
: KisImageCommand(kundo2_i18n("Move Layer"), image)
{
m_layer = layer;
m_newParent = newParent;
m_newAbove = newAbove;
m_prevParent = layer->parent();
m_prevAbove = layer->prevSibling();
m_index = -1;
m_useIndex = false;
m_doUpdates = doUpdates;
}
KisImageLayerMoveCommand::KisImageLayerMoveCommand(KisImageWSP image, KisNodeSP node, KisNodeSP newParent, quint32 index)
: KisImageCommand(kundo2_i18n("Move Layer"), image)
{
m_layer = node;
m_newParent = newParent;
m_newAbove = 0;
m_prevParent = node->parent();
m_prevAbove = node->prevSibling();
m_index = index;
m_useIndex = true;
m_doUpdates = true;
}
void KisImageLayerMoveCommand::redo()
{
-
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_useIndex) {
- m_image->moveNode(m_layer, m_newParent, m_index);
+ image->moveNode(m_layer, m_newParent, m_index);
} else {
- m_image->moveNode(m_layer, m_newParent, m_newAbove);
+ image->moveNode(m_layer, m_newParent, m_newAbove);
}
if (m_doUpdates) {
- m_image->refreshGraphAsync(m_prevParent);
+ image->refreshGraphAsync(m_prevParent);
if (m_newParent != m_prevParent) {
- m_layer->setDirty(m_image->bounds());
+ m_layer->setDirty(image->bounds());
}
}
}
void KisImageLayerMoveCommand::undo()
{
- m_image->moveNode(m_layer, m_prevParent, m_prevAbove);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->moveNode(m_layer, m_prevParent, m_prevAbove);
if (m_doUpdates) {
- m_image->refreshGraphAsync(m_newParent);
+ image->refreshGraphAsync(m_newParent);
if (m_newParent != m_prevParent) {
- m_layer->setDirty(m_image->bounds());
+ m_layer->setDirty(image->bounds());
}
}
}
diff --git a/libs/image/commands/kis_image_layer_remove_command.cpp b/libs/image/commands/kis_image_layer_remove_command.cpp
index 8e6274866f..6217e1fb14 100644
--- a/libs/image/commands/kis_image_layer_remove_command.cpp
+++ b/libs/image/commands/kis_image_layer_remove_command.cpp
@@ -1,80 +1,88 @@
/*
* 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_image_layer_remove_command.h"
#include <klocalizedstring.h>
#include "kis_image.h"
#include "kis_image_layer_remove_command_impl.h"
KisImageLayerRemoveCommand::KisImageLayerRemoveCommand(KisImageWSP image,
KisNodeSP node,
bool doRedoUpdates,
bool doUndoUpdates)
: KisImageCommand(kundo2_i18n("Remove Layer"), image),
m_node(node),
m_doRedoUpdates(doRedoUpdates),
m_doUndoUpdates(doUndoUpdates)
{
addSubtree(image, node);
}
KisImageLayerRemoveCommand::~KisImageLayerRemoveCommand()
{
}
void KisImageLayerRemoveCommand::addSubtree(KisImageWSP image, KisNodeSP node)
{
// Simple tail-recursion to remove nodes in bottom-up way
//
// Alert: the nodes must be traversed in last-to-first order,
// because each KisImageLayerRemoveCommandImpl stores a
// pointer to the previous node of the stack
KisNodeSP child = node->lastChild();
while (child) {
addSubtree(image, child);
child = child->prevSibling();
}
new KisImageLayerRemoveCommandImpl(image, node, this);
}
void KisImageLayerRemoveCommand::redo()
{
- UpdateTarget target(m_image, m_node, m_image->bounds());
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ UpdateTarget target(image, m_node, image->bounds());
KisImageCommand::redo();
if (m_doRedoUpdates) {
target.update();
}
}
void KisImageLayerRemoveCommand::undo()
{
KisImageCommand::undo();
if (m_doUndoUpdates) {
/**
* We are removing the group recursively, so the updates should
* come recursively as well
*/
- m_image->refreshGraphAsync(m_node, m_image->bounds());
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->refreshGraphAsync(m_node, image->bounds());
}
}
diff --git a/libs/image/commands/kis_image_layer_remove_command_impl.cpp b/libs/image/commands/kis_image_layer_remove_command_impl.cpp
index 8f2acc600a..124afa8939 100644
--- a/libs/image/commands/kis_image_layer_remove_command_impl.cpp
+++ b/libs/image/commands/kis_image_layer_remove_command_impl.cpp
@@ -1,137 +1,157 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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_image_layer_remove_command_impl.h"
#include "kis_image.h"
#include <klocalizedstring.h>
#include "kis_layer.h"
#include "kis_clone_layer.h"
#include "kis_paint_layer.h"
struct Q_DECL_HIDDEN KisImageLayerRemoveCommandImpl::Private {
Private(KisImageLayerRemoveCommandImpl *_q) : q(_q) {}
KisImageLayerRemoveCommandImpl *q;
KisNodeSP node;
KisNodeSP prevParent;
KisNodeSP prevAbove;
QList<KisCloneLayerSP> clonesList;
QList<KisLayerSP> reincarnatedNodes;
void restoreClones();
void processClones(KisNodeSP node);
void moveChildren(KisNodeSP src, KisNodeSP dst);
void moveClones(KisLayerSP src, KisLayerSP dst);
};
KisImageLayerRemoveCommandImpl::KisImageLayerRemoveCommandImpl(KisImageWSP image, KisNodeSP node, KUndo2Command *parent)
: KisImageCommand(kundo2_i18n("Remove Layer"), image, parent),
m_d(new Private(this))
{
m_d->node = node;
m_d->prevParent = node->parent();
m_d->prevAbove = node->prevSibling();
}
KisImageLayerRemoveCommandImpl::~KisImageLayerRemoveCommandImpl()
{
delete m_d;
}
void KisImageLayerRemoveCommandImpl::redo()
{
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
m_d->processClones(m_d->node);
- m_image->removeNode(m_d->node);
+ image->removeNode(m_d->node);
}
void KisImageLayerRemoveCommandImpl::undo()
{
- m_image->addNode(m_d->node, m_d->prevParent, m_d->prevAbove);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->addNode(m_d->node, m_d->prevParent, m_d->prevAbove);
m_d->restoreClones();
}
void KisImageLayerRemoveCommandImpl::Private::restoreClones()
{
Q_ASSERT(reincarnatedNodes.size() == clonesList.size());
-
+ KisImageSP image = q->m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
for (int i = 0; i < reincarnatedNodes.size(); i++) {
KisCloneLayerSP clone = clonesList[i];
KisLayerSP newNode = reincarnatedNodes[i];
- q->m_image->addNode(clone, newNode->parent(), newNode);
+ image->addNode(clone, newNode->parent(), newNode);
moveChildren(newNode, clone);
moveClones(newNode, clone);
- q->m_image->removeNode(newNode);
+ image->removeNode(newNode);
}
}
void KisImageLayerRemoveCommandImpl::Private::processClones(KisNodeSP node)
{
- KisLayerSP layer = dynamic_cast<KisLayer*>(node.data());
+ KisLayerSP layer(dynamic_cast<KisLayer*>(node.data()));
if(!layer || !layer->hasClones()) return;
if(reincarnatedNodes.isEmpty()) {
/**
* Initialize the list of reincarnates nodes
*/
Q_FOREACH (KisCloneLayerWSP _clone, layer->registeredClones()) {
KisCloneLayerSP clone = _clone;
Q_ASSERT(clone);
clonesList.append(clone);
reincarnatedNodes.append(clone->reincarnateAsPaintLayer());
}
}
+ KisImageSP image = q->m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+
/**
* Move the children and transitive clones to the
* reincarnated nodes
*/
for (int i = 0; i < reincarnatedNodes.size(); i++) {
KisCloneLayerSP clone = clonesList[i];
KisLayerSP newNode = reincarnatedNodes[i];
- q->m_image->addNode(newNode, clone->parent(), clone);
+ image->addNode(newNode, clone->parent(), clone);
moveChildren(clone, newNode);
moveClones(clone, newNode);
- q->m_image->removeNode(clone);
+ image->removeNode(clone);
}
}
void KisImageLayerRemoveCommandImpl::Private::moveChildren(KisNodeSP src, KisNodeSP dst)
{
+ KisImageSP image = q->m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
KisNodeSP child = src->firstChild();
while(child) {
- q->m_image->moveNode(child, dst, dst->lastChild());
+ image->moveNode(child, dst, dst->lastChild());
child = child->nextSibling();
}
}
void KisImageLayerRemoveCommandImpl::Private::moveClones(KisLayerSP src, KisLayerSP dst)
{
Q_FOREACH (KisCloneLayerWSP _clone, src->registeredClones()) {
KisCloneLayerSP clone = _clone;
Q_ASSERT(clone);
clone->setCopyFrom(dst);
}
}
diff --git a/libs/image/commands/kis_image_lock_command.cpp b/libs/image/commands/kis_image_lock_command.cpp
index 7a6bc52e5e..c743aeb544 100644
--- a/libs/image/commands/kis_image_lock_command.cpp
+++ b/libs/image/commands/kis_image_lock_command.cpp
@@ -1,41 +1,49 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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_image_commands.h"
#include "kis_image.h"
#include <klocalizedstring.h>
KisImageLockCommand::KisImageLockCommand(KisImageWSP image, bool lockImage)
: KisImageCommand(kundo2_noi18n("lock image"), image)
{
Q_UNUSED(lockImage)
}
void KisImageLockCommand::redo()
{
- m_image->refreshGraph();
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->refreshGraph();
}
void KisImageLockCommand::undo()
{
- m_image->refreshGraph();
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->refreshGraph();
}
diff --git a/libs/image/commands/kis_image_set_projection_color_space_command.cpp b/libs/image/commands/kis_image_set_projection_color_space_command.cpp
index fbf292296f..e4d2a3293f 100644
--- a/libs/image/commands/kis_image_set_projection_color_space_command.cpp
+++ b/libs/image/commands/kis_image_set_projection_color_space_command.cpp
@@ -1,43 +1,52 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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_image_commands.h"
#include "KoColorSpace.h"
#include "kis_image.h"
#include <klocalizedstring.h>
KisImageSetProjectionColorSpaceCommand::KisImageSetProjectionColorSpaceCommand(KisImageWSP image, const KoColorSpace * afterColorSpace)
: KisImageCommand(kundo2_i18n("Convert Image Type"), image)
{
- m_beforeColorSpace = image->colorSpace();
- m_afterColorSpace = afterColorSpace;
+ KisImageSP imageSP = image.toStrongRef();
+ if (imageSP) {
+ m_beforeColorSpace = imageSP->colorSpace();
+ m_afterColorSpace = afterColorSpace;
+ }
}
void KisImageSetProjectionColorSpaceCommand::redo()
{
- m_image->setProjectionColorSpace(m_afterColorSpace);
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ image->setProjectionColorSpace(m_afterColorSpace);
+ }
}
void KisImageSetProjectionColorSpaceCommand::undo()
{
- m_image->setProjectionColorSpace(m_beforeColorSpace);
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ image->setProjectionColorSpace(m_beforeColorSpace);
+ }
}
diff --git a/libs/image/commands/kis_node_property_list_command.cpp b/libs/image/commands/kis_node_property_list_command.cpp
index 290a76daa3..070df0762e 100644
--- a/libs/image/commands/kis_node_property_list_command.cpp
+++ b/libs/image/commands/kis_node_property_list_command.cpp
@@ -1,115 +1,121 @@
/*
* 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 <klocalizedstring.h>
#include "kis_node.h"
#include "kis_layer.h"
#include "kis_image.h"
#include "kis_selection_mask.h"
#include "kis_paint_layer.h"
#include "commands/kis_node_property_list_command.h"
#include "kis_undo_adapter.h"
KisNodePropertyListCommand::KisNodePropertyListCommand(KisNodeSP node, KisBaseNode::PropertyList newPropertyList)
: KisNodeCommand(kundo2_i18n("Property Changes"), node),
m_newPropertyList(newPropertyList),
m_oldPropertyList(node->sectionModelProperties())
/**
* TODO instead of "Property Changes" check which property
* has been changed and display either lock/unlock, visible/hidden
* or "Property Changes" (this require new strings)
*/
{
}
void KisNodePropertyListCommand::redo()
{
m_node->setSectionModelProperties(m_newPropertyList);
doUpdate(m_oldPropertyList, m_newPropertyList);
}
void KisNodePropertyListCommand::undo()
{
m_node->setSectionModelProperties(m_oldPropertyList);
doUpdate(m_newPropertyList, m_oldPropertyList);
}
void KisNodePropertyListCommand::doUpdate(const KisBaseNode::PropertyList &oldPropertyList,
const KisBaseNode::PropertyList &newPropertyList)
{
bool oldPassThroughValue = false;
bool newPassThroughValue = false;
Q_FOREACH (const KisBaseNode::Property &prop, oldPropertyList) {
if (prop.name == i18n("Pass Through")) {
oldPassThroughValue = prop.state.toBool();
}
}
Q_FOREACH (const KisBaseNode::Property &prop, newPropertyList) {
if (prop.name == i18n("Pass Through")) {
newPassThroughValue = prop.state.toBool();
}
}
if (oldPassThroughValue && !newPassThroughValue) {
- KisLayer *layer = qobject_cast<KisLayer*>(m_node.data());
- layer->image()->refreshGraphAsync(layer);
+ KisLayerSP layer(qobject_cast<KisLayer*>(m_node.data()));
+ KisImageSP image = layer->image().toStrongRef();
+ if (image) {
+ image->refreshGraphAsync(layer);
+ }
} else if (m_node->parent() && !oldPassThroughValue && newPassThroughValue) {
- KisLayer *layer = qobject_cast<KisLayer*>(m_node->parent().data());
- layer->image()->refreshGraphAsync(layer);
+ KisLayerSP layer(qobject_cast<KisLayer*>(m_node->parent().data()));
+ KisImageSP image = layer->image().toStrongRef();
+ if (image) {
+ image->refreshGraphAsync(layer);
+ }
} else {
m_node->setDirty(); // TODO check if visibility was changed or not
}
}
void KisNodePropertyListCommand::setNodePropertiesNoUndo(KisNodeSP node, KisImageSP image, PropertyList proplist)
{
bool undo = true;
Q_FOREACH (const KisBaseNode::Property &prop, proplist) {
if (prop.isInStasis) undo = false;
if (prop.name == i18n("Visible") && node->visible() != prop.state.toBool()) undo = false;
if (prop.name == i18n("Locked") && node->userLocked() != prop.state.toBool()) undo = false;
if (prop.name == i18n("Active")) {
if (KisSelectionMask *m = dynamic_cast<KisSelectionMask*>(node.data())) {
if (m->active() != prop.state.toBool()) {
undo = false;
}
}
}
if (prop.name == i18n("Alpha Locked")) {
if (KisPaintLayer* l = dynamic_cast<KisPaintLayer*>(node.data())) {
if (l->alphaLocked() != prop.state.toBool()) {
undo = false;
}
}
}
}
QScopedPointer<KUndo2Command> cmd(new KisNodePropertyListCommand(node, proplist));
if (undo) {
image->undoAdapter()->addCommand(cmd.take());
}
else {
image->setModified();
cmd->redo();
}
}
diff --git a/libs/image/commands/kis_reselect_global_selection_command.cpp b/libs/image/commands/kis_reselect_global_selection_command.cpp
index c38dc269ba..9012c38283 100644
--- a/libs/image/commands/kis_reselect_global_selection_command.cpp
+++ b/libs/image/commands/kis_reselect_global_selection_command.cpp
@@ -1,54 +1,62 @@
/*
* 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_selection_commands.h"
#include <klocalizedstring.h>
#include "kis_image.h"
#include "kis_selection.h"
#include "kis_undo_adapter.h"
#include "kis_selection_mask.h"
#include "kis_pixel_selection.h"
KisReselectGlobalSelectionCommand::KisReselectGlobalSelectionCommand(KisImageWSP image, KUndo2Command * parent) :
KUndo2Command(kundo2_i18n("Reselect"), parent)
, m_image(image)
{
}
KisReselectGlobalSelectionCommand::~KisReselectGlobalSelectionCommand()
{
}
void KisReselectGlobalSelectionCommand::redo()
{
- m_canReselect = m_image->canReselectGlobalSelection();
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ m_canReselect = image->canReselectGlobalSelection();
if (m_canReselect) {
- m_image->reselectGlobalSelection();
+ image->reselectGlobalSelection();
}
}
void KisReselectGlobalSelectionCommand::undo()
{
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_canReselect) {
- m_image->deselectGlobalSelection();
+ image->deselectGlobalSelection();
}
}
diff --git a/libs/image/commands/kis_set_global_selection_command.cpp b/libs/image/commands/kis_set_global_selection_command.cpp
index 9e2d264fa9..681c317093 100644
--- a/libs/image/commands/kis_set_global_selection_command.cpp
+++ b/libs/image/commands/kis_set_global_selection_command.cpp
@@ -1,49 +1,61 @@
/*
* 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_selection_commands.h"
#include <klocalizedstring.h>
#include "kis_image.h"
#include "kis_selection.h"
#include "kis_undo_adapter.h"
#include "kis_selection_mask.h"
#include "kis_pixel_selection.h"
KisSetGlobalSelectionCommand::KisSetGlobalSelectionCommand(KisImageWSP image, KisSelectionSP selection)
: m_image(image)
{
- m_oldSelection = m_image->globalSelection();
+ KisImageSP imageSP = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ m_oldSelection = imageSP->globalSelection();
m_newSelection = selection;
}
void KisSetGlobalSelectionCommand::redo()
{
- m_image->setGlobalSelection(m_newSelection);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setGlobalSelection(m_newSelection);
}
void KisSetGlobalSelectionCommand::undo()
{
- m_image->setGlobalSelection(m_oldSelection);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setGlobalSelection(m_oldSelection);
}
KisSetEmptyGlobalSelectionCommand::KisSetEmptyGlobalSelectionCommand(KisImageWSP image)
- : KisSetGlobalSelectionCommand(image, new KisSelection(new KisSelectionEmptyBounds(image)))
+ : KisSetGlobalSelectionCommand(image, KisSelectionSP(new KisSelection(KisSelectionEmptyBoundsSP(new KisSelectionEmptyBounds(image)))))
{
}
diff --git a/libs/image/commands_new/kis_activate_selection_mask_command.cpp b/libs/image/commands_new/kis_activate_selection_mask_command.cpp
index 12eeaa13ad..9670ad592e 100644
--- a/libs/image/commands_new/kis_activate_selection_mask_command.cpp
+++ b/libs/image/commands_new/kis_activate_selection_mask_command.cpp
@@ -1,53 +1,53 @@
/*
* 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_activate_selection_mask_command.h"
#include "kis_layer.h"
#include "kis_selection_mask.h"
KisActivateSelectionMaskCommand::KisActivateSelectionMaskCommand(KisSelectionMaskSP selectionMask, bool value)
: m_selectionMask(selectionMask),
m_value(value)
{
if (m_previousActiveMask != m_selectionMask) {
- KisLayerSP parent = dynamic_cast<KisLayer*>(selectionMask->parent().data());
+ KisLayerSP parent(dynamic_cast<KisLayer*>(selectionMask->parent().data()));
if (parent) {
m_previousActiveMask = parent->selectionMask();
}
}
m_previousValue = selectionMask->active();
}
void KisActivateSelectionMaskCommand::redo()
{
m_selectionMask->setActive(m_value);
}
void KisActivateSelectionMaskCommand::undo()
{
m_selectionMask->setActive(m_previousValue);
if (m_value && m_previousActiveMask) {
m_previousActiveMask->setActive(true);
}
}
diff --git a/libs/image/commands_new/kis_change_projection_color_command.cpp b/libs/image/commands_new/kis_change_projection_color_command.cpp
index 9e0d433eba..5d4c0d8bbf 100644
--- a/libs/image/commands_new/kis_change_projection_color_command.cpp
+++ b/libs/image/commands_new/kis_change_projection_color_command.cpp
@@ -1,70 +1,78 @@
/*
* 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_change_projection_color_command.h"
#include "kis_image.h"
#include "kis_image_animation_interface.h"
KisChangeProjectionColorCommand::KisChangeProjectionColorCommand(KisImageSP image, const KoColor &newColor, KUndo2Command *parent)
: KUndo2Command(kundo2_noi18n("CHANGE_PROJECTION_COLOR_COMMAND"), parent),
m_image(image),
m_oldColor(image->defaultProjectionColor()),
m_newColor(newColor)
{
}
KisChangeProjectionColorCommand::~KisChangeProjectionColorCommand()
{
}
int KisChangeProjectionColorCommand::id() const
{
// we don't have a common commands id source in Krita yet, so
// just use a random one ;)
// http://www.scientificamerican.com/article/most-popular-numbers-grapes-of-math/
return 142857;
}
bool KisChangeProjectionColorCommand::mergeWith(const KUndo2Command* command)
{
const KisChangeProjectionColorCommand *other =
dynamic_cast<const KisChangeProjectionColorCommand*>(command);
if (!other || other->id() != id()) {
return false;
}
m_newColor = other->m_newColor;
return true;
}
void KisChangeProjectionColorCommand::redo()
{
- m_image->setDefaultProjectionColor(m_newColor);
- m_image->animationInterface()->setDefaultProjectionColor(m_newColor);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setDefaultProjectionColor(m_newColor);
+ image->animationInterface()->setDefaultProjectionColor(m_newColor);
}
void KisChangeProjectionColorCommand::undo()
{
- m_image->setDefaultProjectionColor(m_oldColor);
- m_image->animationInterface()->setDefaultProjectionColor(m_oldColor);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setDefaultProjectionColor(m_oldColor);
+ image->animationInterface()->setDefaultProjectionColor(m_oldColor);
}
diff --git a/libs/image/commands_new/kis_image_resize_command.cpp b/libs/image/commands_new/kis_image_resize_command.cpp
index d7971f51e5..86f5350aa1 100644
--- a/libs/image/commands_new/kis_image_resize_command.cpp
+++ b/libs/image/commands_new/kis_image_resize_command.cpp
@@ -1,44 +1,56 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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_image_resize_command.h"
#include <klocalizedstring.h>
#include "kis_image.h"
KisImageResizeCommand::KisImageResizeCommand(KisImageWSP image,
const QSize& newSize)
: KUndo2Command(kundo2_i18n("Resize Image")),
m_image(image)
{
// do we really need a translatable name for the command?
- m_sizeBefore = image->size();
+ KisImageSP imageSP = m_image.toStrongRef();
+ if (!imageSP) {
+ return;
+ }
+ m_sizeBefore = imageSP->size();
m_sizeAfter = newSize;
}
void KisImageResizeCommand::redo()
{
- m_image->setSize(m_sizeAfter);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setSize(m_sizeAfter);
}
void KisImageResizeCommand::undo()
{
- m_image->setSize(m_sizeBefore);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setSize(m_sizeBefore);
}
diff --git a/libs/image/commands_new/kis_image_set_resolution_command.cpp b/libs/image/commands_new/kis_image_set_resolution_command.cpp
index 2a8a9fcc6c..4136cebed2 100644
--- a/libs/image/commands_new/kis_image_set_resolution_command.cpp
+++ b/libs/image/commands_new/kis_image_set_resolution_command.cpp
@@ -1,106 +1,120 @@
/*
* 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_image_set_resolution_command.h"
#include <klocalizedstring.h>
#include <kis_image.h>
KisImageSetResolutionCommand::KisImageSetResolutionCommand(KisImageWSP image, qreal newXRes, qreal newYRes, KUndo2Command *parent)
: KUndo2Command(kundo2_i18n("Set Image Resolution"), parent)
, m_image(image)
, m_newXRes(newXRes)
, m_newYRes(newYRes)
- , m_oldXRes(m_image->xRes())
- , m_oldYRes(m_image->yRes())
+ , m_oldXRes(0)
+ , m_oldYRes(0)
{
+ KisImageSP imageSP = image.toStrongRef();
+ if (!imageSP) {
+ return;
+ }
+ m_oldXRes = imageSP->xRes();
+ m_oldYRes = imageSP->yRes();
}
void KisImageSetResolutionCommand::undo()
{
- m_image->setResolution(m_oldXRes, m_oldYRes);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setResolution(m_oldXRes, m_oldYRes);
}
void KisImageSetResolutionCommand::redo()
{
- m_image->setResolution(m_newXRes, m_newYRes);
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setResolution(m_newXRes, m_newYRes);
}
#include "kis_processing_visitor.h"
#include "kis_adjustment_layer.h"
#include "generator/kis_generator_layer.h"
#include "kis_external_layer_iface.h"
#include "kis_transparency_mask.h"
#include "kis_filter_mask.h"
#include "kis_transform_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
class ResetShapesProcessingVisitor : public KisProcessingVisitor
{
public:
void visit(KisNode*, KisUndoAdapter*) override {}
void visit(KisPaintLayer*, KisUndoAdapter*) override {}
void visit(KisGroupLayer*, KisUndoAdapter*) override {}
void visit(KisCloneLayer*, KisUndoAdapter*) override {}
void visit(KisAdjustmentLayer *layer, KisUndoAdapter*) override { layer->internalSelection()->updateProjection(); }
void visit(KisGeneratorLayer *layer, KisUndoAdapter*) override { layer->internalSelection()->updateProjection(); }
void visit(KisExternalLayer *layer, KisUndoAdapter*) override { layer->resetCache(); }
void visit(KisFilterMask *mask, KisUndoAdapter*) override { mask->selection()->updateProjection(); }
void visit(KisTransformMask *mask, KisUndoAdapter*) override { KIS_ASSERT_RECOVER_NOOP(!mask->selection()); }
void visit(KisTransparencyMask *mask, KisUndoAdapter*) override { mask->selection()->updateProjection(); }
void visit(KisSelectionMask *mask, KisUndoAdapter*) override { mask->selection()->updateProjection(); }
void visit(KisColorizeMask *, KisUndoAdapter*) override {}
};
KisResetShapesCommand::KisResetShapesCommand(KisNodeSP rootNode)
: KUndo2Command(kundo2_noi18n("RESET_SHAPES_COMMAND")),
m_rootNode(rootNode)
{
}
void KisResetShapesCommand::undo()
{
KUndo2Command::undo();
resetNode(m_rootNode);
}
void KisResetShapesCommand::redo()
{
KUndo2Command::redo();
resetNode(m_rootNode);
}
void KisResetShapesCommand::resetNode(KisNodeSP node)
{
ResetShapesProcessingVisitor visitor;
node->accept(visitor, 0);
node = node->firstChild();
while(node) {
resetNode(node);
node = node->nextSibling();
}
}
diff --git a/libs/image/commands_new/kis_node_move_command2.cpp b/libs/image/commands_new/kis_node_move_command2.cpp
index e59f9c1753..fa70fc47f8 100644
--- a/libs/image/commands_new/kis_node_move_command2.cpp
+++ b/libs/image/commands_new/kis_node_move_command2.cpp
@@ -1,44 +1,44 @@
/*
* Copyright (c) 2014 Stuart Dickson <stuartmd@kogmbh.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_move_command2.h"
#include "kis_selection_mask.h"
KisNodeMoveCommand2::KisNodeMoveCommand2(KisNodeSP object, const QPoint& oldPos, const QPoint& newPos, KUndo2Command *parent)
: KisMoveCommandCommon<KisNodeSP>(object, oldPos, newPos, parent)
{
}
void KisNodeMoveCommand2::redo() {
KisMoveCommandCommon<KisNodeSP>::redo();
tryNotifySelection(m_object);
}
void KisNodeMoveCommand2::undo() {
KisMoveCommandCommon<KisNodeSP>::undo();
tryNotifySelection(m_object);
}
void KisNodeMoveCommand2::tryNotifySelection(KisNodeSP node)
{
- KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(node.data());
+ KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(node.data());
if (!mask) return;
mask->notifySelectionChangedCompressed();
}
diff --git a/libs/image/filter/kis_filter.cc b/libs/image/filter/kis_filter.cc
index cf4f94353d..6154b95512 100644
--- a/libs/image/filter/kis_filter.cc
+++ b/libs/image/filter/kis_filter.cc
@@ -1,174 +1,174 @@
/*
* Copyright (c) 2004,2006-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 "filter/kis_filter.h"
#include <QString>
#include <KoCompositeOpRegistry.h>
#include "kis_bookmarked_configuration_manager.h"
#include "filter/kis_filter_configuration.h"
#include "kis_processing_information.h"
#include "kis_transaction.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
#include "kis_types.h"
#include <kis_painter.h>
KoID KisFilter::categoryAdjust()
{
return KoID("adjust_filters", i18n("Adjust"));
}
KoID KisFilter::categoryArtistic()
{
return KoID("artistic_filters", i18n("Artistic"));
}
KoID KisFilter::categoryBlur()
{
return KoID("blur_filters", i18n("Blur"));
}
KoID KisFilter::categoryColors()
{
return KoID("color_filters", i18n("Colors"));
}
KoID KisFilter::categoryEdgeDetection()
{
return KoID("edge_filters", i18n("Edge Detection"));
}
KoID KisFilter::categoryEmboss()
{
return KoID("emboss_filters", i18n("Emboss"));
}
KoID KisFilter::categoryEnhance()
{
return KoID("enhance_filters", i18n("Enhance"));
}
KoID KisFilter::categoryMap()
{
return KoID("map_filters", i18n("Map"));
}
KoID KisFilter::categoryNonPhotorealistic()
{
return KoID("nonphotorealistic_filters", i18n("Non-photorealistic"));
}
KoID KisFilter::categoryOther()
{
return KoID("other_filters", i18n("Other"));
}
KisFilter::KisFilter(const KoID& _id, const KoID & category, const QString & entry)
: KisBaseProcessor(_id, category, entry),
m_supportsLevelOfDetail(false)
{
init(id() + "_filter_bookmarks");
}
KisFilter::~KisFilter()
{
}
void KisFilter::process(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const
{
- process(device, device, 0, applyRect, config, progressUpdater);
+ process(device, device, KisSelectionSP(), applyRect, config, progressUpdater);
}
void KisFilter::process(const KisPaintDeviceSP src,
KisPaintDeviceSP dst,
KisSelectionSP selection,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater ) const
{
if (applyRect.isEmpty()) return;
QRect needRect = neededRect(applyRect, config, src->defaultBounds()->currentLevelOfDetail());
KisPaintDeviceSP temporary;
KisTransaction *transaction = 0;
bool weirdDstColorSpace =
dst->colorSpace() != dst->compositionSourceColorSpace() &&
*dst->colorSpace() != *dst->compositionSourceColorSpace();
if(src == dst && !selection && !weirdDstColorSpace) {
temporary = src;
}
else {
temporary = dst->createCompositionSourceDevice(src, needRect);
transaction = new KisTransaction(temporary);
}
try {
processImpl(temporary, applyRect, config, progressUpdater);
}
catch (std::bad_alloc) {
warnKrita << "Filter" << name() << "failed to allocate enough memory to run.";
}
if(transaction) {
delete transaction;
KisPainter::copyAreaOptimized(applyRect.topLeft(), temporary, dst, applyRect, selection);
}
}
QRect KisFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP c, int lod) const
{
Q_UNUSED(c);
Q_UNUSED(lod);
return rect;
}
QRect KisFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP c, int lod) const
{
Q_UNUSED(c);
Q_UNUSED(lod);
return rect;
}
bool KisFilter::supportsLevelOfDetail(const KisFilterConfigurationSP config, int lod) const
{
Q_UNUSED(config);
Q_UNUSED(lod);
return m_supportsLevelOfDetail;
}
void KisFilter::setSupportsLevelOfDetail(bool value)
{
m_supportsLevelOfDetail = value;
}
bool KisFilter::needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const
{
Q_UNUSED(config);
Q_UNUSED(cs);
return false;
}
diff --git a/libs/image/filter/kis_filter_registry.cc b/libs/image/filter/kis_filter_registry.cc
index 537a7269ad..d9fb0ae4f0 100644
--- a/libs/image/filter/kis_filter_registry.cc
+++ b/libs/image/filter/kis_filter_registry.cc
@@ -1,81 +1,81 @@
/*
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2004-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 "filter/kis_filter_registry.h"
#include <math.h>
#include <QString>
#include <QApplication>
#include <klocalizedstring.h>
#include <KoPluginLoader.h>
#include "kis_debug.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "filter/kis_filter.h"
#include "kis_filter_configuration.h"
KisFilterRegistry::KisFilterRegistry(QObject *parent)
: QObject(parent)
{
}
KisFilterRegistry::~KisFilterRegistry()
{
dbgRegistry << "deleting KisFilterRegistry";
Q_FOREACH (KisFilterSP filter, values()) {
remove(filter->id());
filter.clear();
}
}
KisFilterRegistry* KisFilterRegistry::instance()
{
KisFilterRegistry *reg = qApp->findChild<KisFilterRegistry *>(QString());
if (!reg) {
reg = new KisFilterRegistry(qApp);
KoPluginLoader::instance()->load("Krita/Filter", "Type == 'Service' and ([X-Krita-Version] == 28)");
}
return reg;
}
void KisFilterRegistry::add(KisFilterSP item)
{
add(item->id(), item);
}
void KisFilterRegistry::add(const QString &id, KisFilterSP item)
{
KoGenericRegistry<KisFilterSP>::add(id, item);
emit(filterAdded(id));
}
KisFilterConfigurationSP KisFilterRegistry::cloneConfiguration(const KisFilterConfigurationSP kfc)
{
Q_ASSERT(kfc);
KisFilterSP filter = value(kfc->name());
- KisFilterConfigurationSP newkfc = filter->defaultConfiguration(0);
+ KisFilterConfigurationSP newkfc(filter->defaultConfiguration(KisPaintDeviceSP()));
newkfc->fromXML(kfc->toXML());
return newkfc;
}
diff --git a/libs/image/generator/kis_generator_layer.cpp b/libs/image/generator/kis_generator_layer.cpp
index cef1bd12ac..7dc6bc5d3f 100644
--- a/libs/image/generator/kis_generator_layer.cpp
+++ b/libs/image/generator/kis_generator_layer.cpp
@@ -1,166 +1,166 @@
/*
* 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_generator_layer.h"
#include <klocalizedstring.h>
#include "kis_debug.h"
#include <KoIcon.h>
#include <kis_icon.h>
#include "kis_selection.h"
#include "filter/kis_filter_configuration.h"
#include "kis_processing_information.h"
#include "generator/kis_generator_registry.h"
#include "generator/kis_generator.h"
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_thread_safe_signal_compressor.h"
#include "kis_recalculate_generator_layer_job.h"
#define UPDATE_DELAY 100 /*ms */
struct Q_DECL_HIDDEN KisGeneratorLayer::Private
{
Private()
: updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::FIRST_INACTIVE)
{
}
KisThreadSafeSignalCompressor updateSignalCompressor;
};
KisGeneratorLayer::KisGeneratorLayer(KisImageWSP image,
const QString &name,
KisFilterConfigurationSP kfc,
KisSelectionSP selection)
: KisSelectionBasedLayer(image, name, selection, kfc, true),
m_d(new Private)
{
connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
update();
}
KisGeneratorLayer::KisGeneratorLayer(const KisGeneratorLayer& rhs)
: KisSelectionBasedLayer(rhs),
m_d(new Private)
{
connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
}
KisGeneratorLayer::~KisGeneratorLayer()
{
}
void KisGeneratorLayer::setFilter(KisFilterConfigurationSP filterConfig)
{
KisSelectionBasedLayer::setFilter(filterConfig);
update();
}
void KisGeneratorLayer::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());
+ KisLayerSP parentLayer(dynamic_cast<KisLayer*>(parent().data()));
if (!parentLayer) return;
KisImageSP image = parentLayer->image();
if (image) {
- image->addSpontaneousJob(new KisRecalculateGeneratorLayerJob(this));
+ image->addSpontaneousJob(new KisRecalculateGeneratorLayerJob(KisGeneratorLayerSP(this)));
}
}
void KisGeneratorLayer::update()
{
KisFilterConfigurationSP filterConfig = filter();
if (!filterConfig) {
warnImage << "BUG: No Filter configuration in KisGeneratorLayer";
return;
}
KisGeneratorSP f = KisGeneratorRegistry::instance()->value(filterConfig->name());
if (!f) return;
QRect processRect = exactBounds();
resetCache(f->colorSpace());
KisPaintDeviceSP originalDevice = original();
KisProcessingInformation dstCfg(originalDevice,
processRect.topLeft(),
- 0);
+ KisSelectionSP());
f->generate(dstCfg, processRect.size(), filterConfig.data());
// hack alert!
// this avoids cyclic loop with KisRecalculateGeneratorLayerJob::run()
KisSelectionBasedLayer::setDirty(extent());
}
bool KisGeneratorLayer::accept(KisNodeVisitor & v)
{
return v.visit(this);
}
void KisGeneratorLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
return visitor.visit(this, undoAdapter);
}
QIcon KisGeneratorLayer::icon() const
{
return KisIconUtils::loadIcon("fillLayer");
}
KisBaseNode::PropertyList KisGeneratorLayer::sectionModelProperties() const
{
KisFilterConfigurationSP filterConfig = filter();
KisBaseNode::PropertyList l = KisLayer::sectionModelProperties();
l << KisBaseNode::Property(KoID("generator", i18n("Generator")),
KisGeneratorRegistry::instance()->value(filterConfig->name())->name());
return l;
}
void KisGeneratorLayer::setX(qint32 x)
{
KisSelectionBasedLayer::setX(x);
m_d->updateSignalCompressor.start();
}
void KisGeneratorLayer::setY(qint32 y)
{
KisSelectionBasedLayer::setY(y);
m_d->updateSignalCompressor.start();
}
void KisGeneratorLayer::setDirty(const QRect & rect)
{
KisSelectionBasedLayer::setDirty(rect);
m_d->updateSignalCompressor.start();
}
diff --git a/libs/image/generator/kis_generator_registry.cpp b/libs/image/generator/kis_generator_registry.cpp
index 030ef6aa6d..ee8a66511e 100644
--- a/libs/image/generator/kis_generator_registry.cpp
+++ b/libs/image/generator/kis_generator_registry.cpp
@@ -1,80 +1,80 @@
/*
* 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 "generator/kis_generator_registry.h"
#include <math.h>
#include <QString>
#include <QApplication>
#include <klocalizedstring.h>
#include <KoPluginLoader.h>
#include "filter/kis_filter_configuration.h"
#include "kis_debug.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "generator/kis_generator.h"
KisGeneratorRegistry::KisGeneratorRegistry(QObject *parent)
: QObject(parent)
{
}
KisGeneratorRegistry::~KisGeneratorRegistry()
{
Q_FOREACH (KisGeneratorSP generator, values()) {
remove(generator->id());
generator.clear();
}
dbgRegistry << "deleting KisGeneratorRegistry";
}
KisGeneratorRegistry* KisGeneratorRegistry::instance()
{
KisGeneratorRegistry *reg = qApp->findChild<KisGeneratorRegistry *>(QString());
if (!reg) {
reg = new KisGeneratorRegistry(qApp);
KoPluginLoader::instance()->load("Krita/Generator", "Type == 'Service' and ([X-Krita-Version] == 28)");
}
return reg;
}
void KisGeneratorRegistry::add(KisGeneratorSP item)
{
dbgPlugins << "adding " << item->name();
add(item->id(), item);
}
void KisGeneratorRegistry::add(const QString &id, KisGeneratorSP item)
{
dbgPlugins << "adding " << item->name() << " with id " << id;
KoGenericRegistry<KisGeneratorSP>::add(id, item);
emit(generatorAdded(id));
}
KisFilterConfigurationSP KisGeneratorRegistry::cloneConfiguration(const KisFilterConfigurationSP kfc)
{
KisGeneratorSP filter = value(kfc->name());
- KisFilterConfigurationSP newkfc = filter->defaultConfiguration(0);
+ KisFilterConfigurationSP newkfc(filter->defaultConfiguration(KisPaintDeviceSP()));
newkfc->fromXML(kfc->toXML());
return newkfc;
}
diff --git a/libs/image/kis_algebra_2d.h b/libs/image/kis_algebra_2d.h
index 20df3d995b..4e48e8e3c2 100644
--- a/libs/image/kis_algebra_2d.h
+++ b/libs/image/kis_algebra_2d.h
@@ -1,379 +1,401 @@
/*
* 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_ALGEBRA_2D_H
#define __KIS_ALGEBRA_2D_H
#include <QPoint>
#include <QPointF>
#include <QVector>
#include <QPolygonF>
#include <cmath>
#include <kis_global.h>
#include <kritaimage_export.h>
class QPainterPath;
namespace KisAlgebra2D {
template <class T>
struct PointTypeTraits
{
};
template <>
struct PointTypeTraits<QPoint>
{
typedef int value_type;
typedef qreal calculation_type;
};
template <>
struct PointTypeTraits<QPointF>
{
typedef qreal value_type;
typedef qreal calculation_type;
};
template <class T>
typename PointTypeTraits<T>::value_type dotProduct(const T &a, const T &b)
{
return a.x() * b.x() + a.y() * b.y();
}
template <class T>
typename PointTypeTraits<T>::value_type crossProduct(const T &a, const T &b)
{
return a.x() * b.y() - a.y() * b.x();
}
template <class T>
qreal norm(const T &a)
{
return std::sqrt(pow2(a.x()) + pow2(a.y()));
}
template <class Point>
Point normalize(const Point &a)
{
const qreal length = norm(a);
return (1.0 / length) * a;
}
/**
* Usual sign() function with positive zero
*/
template <typename T>
T signPZ(T x) {
return x >= T(0) ? T(1) : T(-1);
}
/**
* Usual sign() function with zero returning zero
*/
template <typename T>
T signZZ(T x) {
return x == T(0) ? T(0) : x > T(0) ? T(1) : T(-1);
}
/**
* Copies the sign of \p y into \p x and returns the result
*/
template <typename T>
inline T copysign(T x, T y) {
T strippedX = qAbs(x);
return y >= T(0) ? strippedX : -strippedX;
}
template <class T>
T leftUnitNormal(const T &a)
{
T result = a.x() != 0 ? T(-a.y() / a.x(), 1) : T(-1, 0);
qreal length = norm(result);
result *= (crossProduct(a, result) >= 0 ? 1 : -1) / length;
return -result;
}
template <class T>
T rightUnitNormal(const T &a)
{
return -leftUnitNormal(a);
}
template <class T>
T inwardUnitNormal(const T &a, int polygonDirection)
{
return polygonDirection * leftUnitNormal(a);
}
/**
* \return 1 if the polygon is counterclockwise
* -1 if the polygon is clockwise
*
* Note: the sign is flipped because our 0y axis
* is reversed
*/
template <class T>
int polygonDirection(const QVector<T> &polygon) {
typename PointTypeTraits<T>::value_type doubleSum = 0;
const int numPoints = polygon.size();
for (int i = 1; i <= numPoints; i++) {
int prev = i - 1;
int next = i == numPoints ? 0 : i;
doubleSum +=
(polygon[next].x() - polygon[prev].x()) *
(polygon[next].y() + polygon[prev].y());
}
return doubleSum >= 0 ? 1 : -1;
}
template <typename T>
bool isInRange(T x, T a, T b) {
T length = qAbs(a - b);
return qAbs(x - a) <= length && qAbs(x - b) <= length;
}
void KRITAIMAGE_EXPORT adjustIfOnPolygonBoundary(const QPolygonF &poly, int polygonDirection, QPointF *pt);
/**
* Let \p pt, \p base1 are two vectors. \p base1 is uniformly scaled
* and then rotated into \p base2 using transformation matrix S *
* R. The function applies the same transformation to \pt and returns
* the result.
**/
QPointF KRITAIMAGE_EXPORT transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2);
qreal KRITAIMAGE_EXPORT angleBetweenVectors(const QPointF &v1, const QPointF &v2);
namespace Private {
inline void resetEmptyRectangle(const QPoint &pt, QRect *rc) {
*rc = QRect(pt, QSize(1, 1));
}
inline void resetEmptyRectangle(const QPointF &pt, QRectF *rc) {
static const qreal eps = 1e-10;
*rc = QRectF(pt, QSizeF(eps, eps));
}
}
template <class Point, class Rect>
inline void accumulateBounds(const Point &pt, Rect *bounds)
{
if (bounds->isEmpty()) {
Private::resetEmptyRectangle(pt, bounds);
}
if (pt.x() > bounds->right()) {
bounds->setRight(pt.x());
}
if (pt.x() < bounds->left()) {
bounds->setLeft(pt.x());
}
if (pt.y() > bounds->bottom()) {
bounds->setBottom(pt.y());
}
if (pt.y() < bounds->top()) {
bounds->setTop(pt.y());
}
}
template <class Point, class Rect>
inline Point clampPoint(Point pt, const Rect &bounds)
{
if (pt.x() > bounds.right()) {
pt.rx() = bounds.right();
}
if (pt.x() < bounds.left()) {
pt.rx() = bounds.left();
}
if (pt.y() > bounds.bottom()) {
pt.ry() = bounds.bottom();
}
if (pt.y() < bounds.top()) {
pt.ry() = bounds.top();
}
return pt;
}
QPainterPath KRITAIMAGE_EXPORT smallArrow();
/**
* Multiply width and height of \p rect by \p coeff keeping the
* center of the rectangle pinned
*/
template <class Rect>
Rect blowRect(const Rect &rect, qreal coeff)
{
typedef decltype(rect.x()) CoordType;
CoordType w = rect.width() * coeff;
CoordType h = rect.height() * coeff;
return rect.adjusted(-w, -h, w, h);
}
QPoint KRITAIMAGE_EXPORT ensureInRect(QPoint pt, const QRect &bounds);
QPointF KRITAIMAGE_EXPORT ensureInRect(QPointF pt, const QRectF &bounds);
QRect KRITAIMAGE_EXPORT ensureRectNotSmaller(QRect rc, const QSize &size);
/**
* Attempt to intersect a line to the area of the a rectangle.
*
* If the line intersects the rectange, it will be modified to represent the intersecting line segment and true is returned.
* If the line does not intersect the area, it remains unmodified and false will be returned.
*
* @param segment
* @param area
* @return true if successful
*/
bool KRITAIMAGE_EXPORT intersectLineRect(QLineF &line, const QRect rect);
template <class Point>
inline Point abs(const Point &pt) {
return Point(qAbs(pt.x()), qAbs(pt.y()));
}
class RightHalfPlane {
public:
RightHalfPlane(const QPointF &a, const QPointF &b)
: m_a(a), m_p(b - a), m_norm_p_inv(1.0 / norm(m_p))
{
}
RightHalfPlane(const QLineF &line)
: RightHalfPlane(line.p1(), line.p2())
{
}
qreal valueSq(const QPointF &pt) const {
const qreal val = value(pt);
return signZZ(val) * pow2(val);
}
qreal value(const QPointF &pt) const {
return crossProduct(m_p, pt - m_a) * m_norm_p_inv;
}
int pos(const QPointF &pt) const {
return signZZ(value(pt));
}
QLineF getLine() const {
return QLineF(m_a, m_a + m_p);
}
private:
const QPointF m_a;
const QPointF m_p;
const qreal m_norm_p_inv;
};
class OuterCircle {
public:
OuterCircle(const QPointF &c, qreal radius)
: m_c(c),
m_radius(radius),
m_radius_sq(pow2(radius)),
m_fadeCoeff(1.0 / (pow2(radius + 1.0) - m_radius_sq))
{
}
qreal valueSq(const QPointF &pt) const {
const qreal val = value(pt);
return signZZ(val) * pow2(val);
}
qreal value(const QPointF &pt) const {
return kisDistance(pt, m_c) - m_radius;
}
int pos(const QPointF &pt) const {
return signZZ(valueSq(pt));
}
qreal fadeSq(const QPointF &pt) const {
const qreal valSq = kisSquareDistance(pt, m_c);
return (valSq - m_radius_sq) * m_fadeCoeff;
}
private:
const QPointF m_c;
const qreal m_radius;
const qreal m_radius_sq;
const qreal m_fadeCoeff;
};
/**
* Cuts off a portion of a rect \p rc defined by a half-plane \p p
* \return the bounding rect of the resulting polygon
*/
KRITAIMAGE_EXPORT
QRectF cutOffRect(const QRectF &rc, const KisAlgebra2D::RightHalfPlane &p);
/**
* Solves a quadratic equation in a form:
*
* a * x^2 + b * x + c = 0
*
* WARNING: Please note that \p a *must* be nonzero! Otherwise the
* equation is not quadratic! And this function doesn't check that!
*
* \return the number of solutions. It can be 0, 1 or 2.
*
* \p x1, \p x2 --- the found solution. The variables are filled with
* data iff the corresponding solution is found. That
* is: 0 solutions --- variabled are not touched, 1
* solution --- x1 is filled with the result, 2
* solutions --- x1 and x2 are filled.
*/
KRITAIMAGE_EXPORT
int quadraticEquation(qreal a, qreal b, qreal c, qreal *x1, qreal *x2);
/**
* Finds the points of intersections between two circles
* \return the found circles, the result can have 0, 1 or 2 points
*/
KRITAIMAGE_EXPORT
QVector<QPointF> intersectTwoCircles(const QPointF &c1, qreal r1,
const QPointF &c2, qreal r2);
+
+/**
+ * Scale the relative point \pt into the bounds of \p rc. The point might be
+ * outside the rectangle.
+ */
+inline QPointF relativeToAbsolute(const QPointF &pt, const QRectF &rc) {
+ return rc.topLeft() + QPointF(pt.x() * rc.width(), pt.y() * rc.height());
+}
+
+/**
+ * Get the relative position of \p pt inside rectangle \p rc. The point can be
+ * outside the rectangle.
+ */
+inline QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc) {
+ if (!rc.isValid()) return QPointF();
+
+ const QPointF rel = pt - rc.topLeft();
+ return QPointF(rel.x() / rc.width(), rel.y() / rc.height());
+
+}
+
+
}
#endif /* __KIS_ALGEBRA_2D_H */
diff --git a/libs/image/kis_annotation.h b/libs/image/kis_annotation.h
index d758c9ef8a..e106124211 100644
--- a/libs/image/kis_annotation.h
+++ b/libs/image/kis_annotation.h
@@ -1,113 +1,126 @@
/*
* This file is part of the KDE project
*
* 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.
*/
/**
* @file this file is part of the Krita application in calligra
* @author Boudewijn Rempt
* @author comments by hscott
* @since 1.4 or 2005
*/
#ifndef _KIS_ANNOTATION_H_
#define _KIS_ANNOTATION_H_
#include <kis_shared.h>
#include <QByteArray>
#include <QString>
/**
* @class KisAnnotation A data extension mechanism for Krita.
*
* An annotation can be of something like a QByteArray or a QString or
* a more specific datatype that can be attached to an image (or maybe
* later, if needed, to a layer) and contains data that must be
* associated with an image for purposes of import/export.
*
* Annotations will be saved to krita images and may be exported in
* filetypes that support them.
*
* Examples of annotations are EXIF data and ICC profiles.
*/
class KisAnnotation : public KisShared
{
public:
/**
* creates a new annotation object. The annotation object cannot
* be changed later.
*
* @param type a non-localized string identifying the type of the
* annotation
* @param description a localized string describing the annotation
* @param data a binary blob containing the annotation data
*/
KisAnnotation(const QString & type, const QString & description, const QByteArray & data)
: m_type(type),
m_description(description),
m_annotation(data) {}
virtual ~KisAnnotation() {}
+ virtual KisAnnotation* clone() const {
+ return new KisAnnotation(*this);
+ }
+
/**
* gets a non-localized string identifying the type of the
* annotation.
* @return a non-localized string identifiying the type of the
* annotation
*/
const QString & type() const {
return m_type;
}
/**
* gets a localized string describing the type of annotations for
* used interface purposes.
* @return a localized string describing the type of the
* annotations for user interface purposes.
*/
const QString & description() const {
return m_description;
}
/**
* gets a binary blob representation of this annotation
* @return a binary blob representation of this annotation
*/
const QByteArray & annotation() const {
return m_annotation;
}
/**
* @brief displayText: override this to return an interpreted version of the annotation
*/
virtual QString displayText() const {
return QString::fromUtf8(m_annotation);
}
+protected:
+ KisAnnotation(const KisAnnotation &rhs)
+ : KisShared(),
+ m_type(rhs.m_type),
+ m_description(rhs.m_description),
+ m_annotation(rhs.m_annotation)
+ {
+ }
+
protected:
QString m_type;
QString m_description;
QByteArray m_annotation;
};
#endif // _KIS_ANNOTATION_H_
diff --git a/libs/image/kis_base_node.cpp b/libs/image/kis_base_node.cpp
index 90547649cc..e3d7fa9bf0 100644
--- a/libs/image/kis_base_node.cpp
+++ b/libs/image/kis_base_node.cpp
@@ -1,424 +1,428 @@
/*
* 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_base_node.h"
#include <klocalizedstring.h>
#include <kis_icon.h>
#include <KoProperties.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
#include "kis_paint_device.h"
#include "kis_layer_properties_icons.h"
#include "kis_scalar_keyframe_channel.h"
struct Q_DECL_HIDDEN KisBaseNode::Private
{
QString compositeOp;
KoProperties properties;
- bool systemLocked;
KisBaseNode::Property hack_visible; //HACK
QUuid id;
+ QMap<QString, KisKeyframeChannel*> keyframeChannels;
+ QScopedPointer<KisScalarKeyframeChannel> opacityChannel;
+
+ bool systemLocked;
bool collapsed;
bool supportsLodMoves;
-
- QMap<QString, KisKeyframeChannel*> keyframeChannels;
bool animated;
bool useInTimeline;
- QScopedPointer<KisScalarKeyframeChannel> opacityChannel;
-
Private()
- : animated(false)
+ : id(QUuid::createUuid())
+ , systemLocked(false)
+ , collapsed(false)
+ , supportsLodMoves(false)
+ , animated(false)
, useInTimeline(false)
{
}
+
+ Private(const Private &rhs)
+ : compositeOp(rhs.compositeOp),
+ id(QUuid::createUuid()),
+ systemLocked(false),
+ collapsed(rhs.collapsed),
+ supportsLodMoves(rhs.supportsLodMoves),
+ animated(rhs.animated),
+ useInTimeline(rhs.useInTimeline)
+ {
+ QMapIterator<QString, QVariant> iter = rhs.properties.propertyIterator();
+ while (iter.hasNext()) {
+ iter.next();
+ properties.setProperty(iter.key(), iter.value());
+ }
+ }
};
KisBaseNode::KisBaseNode()
: m_d(new Private())
{
/**
* Be cautious! These two calls are vital to warm-up KoProperties.
* We use it and its QMap in a threaded environment. This is not
* officially suported by Qt, but our environment guarantees, that
* there will be the only writer and several readers. Whilst the
* value of the QMap is boolean and there are no implicit-sharing
* calls provocated, it is safe to work with it in such an
* environment.
*/
setVisible(true, true);
setUserLocked(false);
setCollapsed(false);
setSupportsLodMoves(true);
- setSystemLocked(false);
m_d->compositeOp = COMPOSITE_OVER;
-
- setUuid(QUuid::createUuid());
}
KisBaseNode::KisBaseNode(const KisBaseNode & rhs)
: QObject()
, KisShared()
- , m_d(new Private())
+ , m_d(new Private(*rhs.m_d))
{
- QMapIterator<QString, QVariant> iter = rhs.m_d->properties.propertyIterator();
- while (iter.hasNext()) {
- iter.next();
- m_d->properties.setProperty(iter.key(), iter.value());
- }
- setCollapsed(rhs.collapsed());
- setSupportsLodMoves(rhs.supportsLodMoves());
-
- setSystemLocked(false);
- m_d->compositeOp = rhs.m_d->compositeOp;
-
- setUuid(QUuid::createUuid());
}
KisBaseNode::~KisBaseNode()
{
delete m_d;
}
quint8 KisBaseNode::opacity() const
{
if (m_d->opacityChannel) {
qreal value = m_d->opacityChannel->currentValue();
if (!qIsNaN(value)) {
return value;
}
}
return nodeProperties().intProperty("opacity", OPACITY_OPAQUE_U8);
}
void KisBaseNode::setOpacity(quint8 val)
{
if (m_d->opacityChannel) {
KisKeyframeSP activeKeyframe = m_d->opacityChannel->currentlyActiveKeyframe();
if (activeKeyframe) {
m_d->opacityChannel->setScalarValue(activeKeyframe, val);
}
}
if (opacity() == val) return;
nodeProperties().setProperty("opacity", val);
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
quint8 KisBaseNode::percentOpacity() const
{
return int(float(opacity() * 100) / 255 + 0.5);
}
void KisBaseNode::setPercentOpacity(quint8 val)
{
setOpacity(int(float(val * 255) / 100 + 0.5));
}
const QString& KisBaseNode::compositeOpId() const
{
return m_d->compositeOp;
}
void KisBaseNode::setCompositeOpId(const QString& compositeOp)
{
if (m_d->compositeOp == compositeOp) return;
m_d->compositeOp = compositeOp;
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
KisBaseNode::PropertyList KisBaseNode::sectionModelProperties() const
{
KisBaseNode::PropertyList l;
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::visible, visible(), m_d->hack_visible.isInStasis, m_d->hack_visible.stateInStasis);
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::locked, userLocked());
return l;
}
void KisBaseNode::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
{
setVisible(properties.at(0).state.toBool());
m_d->hack_visible = properties.at(0);
setUserLocked(properties.at(1).state.toBool());
}
KoProperties & KisBaseNode::nodeProperties() const
{
return m_d->properties;
}
void KisBaseNode::mergeNodeProperties(const KoProperties & properties)
{
QMapIterator<QString, QVariant> iter = properties.propertyIterator();
while (iter.hasNext()) {
iter.next();
m_d->properties.setProperty(iter.key(), iter.value());
}
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
bool KisBaseNode::check(const KoProperties & properties) const
{
QMapIterator<QString, QVariant> iter = properties.propertyIterator();
while (iter.hasNext()) {
iter.next();
if (m_d->properties.contains(iter.key())) {
if (m_d->properties.value(iter.key()) != iter.value())
return false;
}
}
return true;
}
QImage KisBaseNode::createThumbnail(qint32 w, qint32 h)
{
try {
QImage image(w, h, QImage::Format_ARGB32);
image.fill(0);
return image;
} catch (std::bad_alloc) {
return QImage();
}
}
bool KisBaseNode::visible(bool recursive) const
{
bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
KisBaseNodeSP parentNode = parentCallback();
return recursive && isVisible && parentNode ?
parentNode->visible() : isVisible;
}
void KisBaseNode::setVisible(bool visible, bool loading)
{
const bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
if (!loading && isVisible == visible) return;
m_d->properties.setProperty(KisLayerPropertiesIcons::visible.id(), visible);
notifyParentVisibilityChanged(visible);
if (!loading) {
emit visibilityChanged(visible);
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
}
bool KisBaseNode::userLocked() const
{
return m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), false);
}
void KisBaseNode::setUserLocked(bool locked)
{
const bool isLocked = m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), true);
if (isLocked == locked) return;
m_d->properties.setProperty(KisLayerPropertiesIcons::locked.id(), locked);
emit userLockingChanged(locked);
baseNodeChangedCallback();
}
bool KisBaseNode::systemLocked() const
{
return m_d->systemLocked;
}
void KisBaseNode::setSystemLocked(bool locked, bool update)
{
m_d->systemLocked = locked;
if (update) {
emit systemLockingChanged(locked);
baseNodeChangedCallback();
}
}
bool KisBaseNode::isEditable(bool checkVisibility) const
{
bool editable = true;
if (checkVisibility) {
editable = (visible(false) && !userLocked() && !systemLocked());
}
else {
editable = (!userLocked() && !systemLocked());
}
if (editable) {
KisBaseNodeSP parentNode = parentCallback();
if (parentNode && parentNode != this) {
editable = parentNode->isEditable(checkVisibility);
}
}
return editable;
}
bool KisBaseNode::hasEditablePaintDevice() const
{
return paintDevice() && isEditable();
}
void KisBaseNode::setCollapsed(bool collapsed)
{
m_d->collapsed = collapsed;
}
bool KisBaseNode::collapsed() const
{
return m_d->collapsed;
}
void KisBaseNode::setColorLabelIndex(int index)
{
const int currentLabel = colorLabelIndex();
if (currentLabel == index) return;
m_d->properties.setProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), index);
baseNodeChangedCallback();
}
int KisBaseNode::colorLabelIndex() const
{
return m_d->properties.intProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), 0);
}
QUuid KisBaseNode::uuid() const
{
return m_d->id;
}
void KisBaseNode::setUuid(const QUuid& id)
{
m_d->id = id;
baseNodeChangedCallback();
}
bool KisBaseNode::supportsLodMoves() const
{
return m_d->supportsLodMoves;
}
void KisBaseNode::setImage(KisImageWSP image)
{
Q_UNUSED(image);
}
void KisBaseNode::setSupportsLodMoves(bool value)
{
m_d->supportsLodMoves = value;
}
QList<KisKeyframeChannel*> KisBaseNode::keyframeChannels() const
{
return m_d->keyframeChannels.values();
}
KisKeyframeChannel * KisBaseNode::getKeyframeChannel(const QString &id) const
{
QMap<QString, KisKeyframeChannel*>::const_iterator i = m_d->keyframeChannels.constFind(id);
if (i == m_d->keyframeChannels.constEnd()) {
return 0;
}
return i.value();
}
KisKeyframeChannel * KisBaseNode::getKeyframeChannel(const QString &id, bool create)
{
KisKeyframeChannel *channel = getKeyframeChannel(id);
if (!channel && create) {
channel = requestKeyframeChannel(id);
if (channel) {
addKeyframeChannel(channel);
}
}
return channel;
}
bool KisBaseNode::isAnimated() const
{
return m_d->animated;
}
void KisBaseNode::enableAnimation()
{
m_d->animated = true;
baseNodeChangedCallback();
}
bool KisBaseNode::useInTimeline() const
{
return m_d->useInTimeline;
}
void KisBaseNode::setUseInTimeline(bool value)
{
if (value == m_d->useInTimeline) return;
m_d->useInTimeline = value;
baseNodeChangedCallback();
}
void KisBaseNode::addKeyframeChannel(KisKeyframeChannel *channel)
{
m_d->keyframeChannels.insert(channel->id(), channel);
emit keyframeChannelAdded(channel);
}
KisKeyframeChannel *KisBaseNode::requestKeyframeChannel(const QString &id)
{
if (id == KisKeyframeChannel::Opacity.id()) {
Q_ASSERT(m_d->opacityChannel.isNull());
KisPaintDeviceSP device = original();
if (device) {
KisScalarKeyframeChannel * channel = new KisScalarKeyframeChannel(
KisKeyframeChannel::Opacity,
0, 255,
device->defaultBounds(),
KisKeyframe::Linear
);
m_d->opacityChannel.reset(channel);
return channel;
}
}
return 0;
}
diff --git a/libs/image/kis_base_node.h b/libs/image/kis_base_node.h
index 7ab24acb9a..9e430ae2bc 100644
--- a/libs/image/kis_base_node.h
+++ b/libs/image/kis_base_node.h
@@ -1,579 +1,579 @@
/*
* 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 _KIS_BASE_NODE_H
#define _KIS_BASE_NODE_H
#include <QObject>
#include <QIcon>
#include <QUuid>
#include <QString>
#include <KoID.h>
#include "kis_shared.h"
#include "kis_paint_device.h"
#include "kis_processing_visitor.h" // included, not forward declared for msvc
class KoProperties;
class KoColorSpace;
class KoCompositeOp;
class KisNodeVisitor;
class KisUndoAdapter;
class KisKeyframeChannel;
#include "kritaimage_export.h"
/**
* A KisBaseNode is the base class for all components of an image:
* nodes, layers masks, selections. A node has a number of properties,
* can be represented as a thumbnail and knows what to do when it gets
* a certain paint device to process. A KisBaseNode does not know
* anything about its peers. You should not directly inherit from a
* KisBaseNode; inherit from KisNode instead.
*/
class KRITAIMAGE_EXPORT KisBaseNode : public QObject, public KisShared
{
Q_OBJECT
public:
/**
* Describes a property of a document section.
*
* FIXME: using a QList instead of QMap and not having an untranslated identifier,
* either enum or string, forces applications to rely on the order of properties
* or to compare the translated strings. This makes it hard to robustly extend the
* properties of document section items.
*/
struct Property
{
QString id;
/** i18n-ed name, suitable for displaying */
QString name;
/** Whether the property is a boolean (e.g. locked, visible) which can be toggled directly from the widget itself. */
bool isMutable;
/** Provide these if the property isMutable. */
QIcon onIcon;
QIcon offIcon;
/** If the property isMutable, provide a boolean. Otherwise, a string suitable for displaying. */
QVariant state;
/** If the property is mutable, specifies whether it can be put into stasis. When a property
is in stasis, a new state is created, and the old one is stored in stateInStasis. When
stasis ends, the old value is restored and the new one discarded */
bool canHaveStasis;
/** If the property isMutable and canHaveStasis, indicate whether it is in stasis or not */
bool isInStasis;
/** If the property isMutable and canHaveStasis, provide this value to store the property's
state while in stasis */
bool stateInStasis;
bool operator==(const Property &rhs) const {
return rhs.name == name;
}
Property(): isMutable( false ) { }
/// Constructor for a mutable property.
Property( const KoID &n, const QIcon &on, const QIcon &off, bool isOn )
: id(n.id()), name( n.name() ), isMutable( true ), onIcon( on ), offIcon( off ), state( isOn ), canHaveStasis( false ) { }
/** Constructor for a mutable property accepting stasis */
Property( const KoID &n, const QIcon &on, const QIcon &off, bool isOn,
bool _isInStasis, bool _stateInStasis )
: id(n.id()), name(n.name()), isMutable( true ), onIcon( on ), offIcon( off ), state( isOn ),
canHaveStasis( true ), isInStasis( _isInStasis ), stateInStasis( _stateInStasis ) { }
/// Constructor for a nonmutable property.
Property( const KoID &n, const QString &s )
: id(n.id()), name(n.name()), isMutable( false ), state( s ) { }
};
/** Return this type for PropertiesRole. */
typedef QList<Property> PropertyList;
public:
/**
* Create a new, empty base node. The node is unnamed, unlocked
* visible and unlinked.
*/
KisBaseNode();
/**
* Create a copy of this node.
*/
KisBaseNode(const KisBaseNode & rhs);
/**
* Delete this node
*/
virtual ~KisBaseNode();
/**
* Return the paintdevice you can use to change pixels on. For a
* paint layer these will be paint pixels, for an adjustment layer or a mask
* the selection paint device.
*
* @return the paint device to paint on. Can be 0 if the actual
* node type does not support painting.
*/
virtual KisPaintDeviceSP paintDevice() const = 0;
/**
* @return the rendered representation of a node
* before the effect masks have had their go at it. Can be 0.
*/
virtual KisPaintDeviceSP original() const = 0;
/**
* @return the fully rendered representation of this layer: its
* rendered original and its effect masks. Can be 0.
*/
virtual KisPaintDeviceSP projection() const = 0;
virtual const KoColorSpace *colorSpace() const = 0;
/**
* Return the opacity of this layer, scaled to a range between 0
* and 255.
* XXX: Allow true float opacity
*/
quint8 opacity() const; //0-255
/**
* Set the opacity for this layer. The range is between 0 and 255.
* The layer will be marked dirty.
*
* XXX: Allow true float opacity
*/
void setOpacity(quint8 val); //0-255
/**
* return the 8-bit opacity of this layer scaled to the range
* 0-100
*
* XXX: Allow true float opacity
*/
quint8 percentOpacity() const; //0-100
/**
* Set the opacity of this layer with a number between 0 and 100;
* the number will be scaled to between 0 and 255.
* XXX: Allow true float opacity
*/
void setPercentOpacity(quint8 val); //0-100
/**
* Return the composite op associated with this layer.
*/
virtual const KoCompositeOp *compositeOp() const = 0;
const QString& compositeOpId() const;
/**
* Set a new composite op for this layer. The layer will be marked
* dirty.
*/
void setCompositeOpId(const QString& compositeOpId);
/**
* @return unique id, which is now used by clone layers.
*/
QUuid uuid() const;
/**
* Set the uuid of node. This should only be used when loading
* existing node and in constructor.
*/
void setUuid(const QUuid& id);
/**
* return the name of this node. This is the same as the
* QObject::objectName.
*/
QString name() const {
return objectName();
}
/**
* set the QObject::objectName. This is also the user-visible name
* of the layer. The reason for this is that we want to see the
* layer name also when debugging.
*/
void setName(const QString& name) {
setObjectName(name);
baseNodeChangedCallback();
}
/**
* @return the icon used to represent the node type, for instance
* in the layerbox and in the menu.
*/
virtual QIcon icon() const {
return QIcon();
- };
+ }
/**
* Return a the properties of this base node (locked, visible etc,
* with the right icons for their representation and their state.
*
* Subclasses can extend this list with new properties, like
* opacity for layers or visualized for masks.
*
* The order of properties is, unfortunately, for now, important,
* so take care which properties superclasses of your class
* define.
*
* KisBaseNode defines visible = 0, locked = 1
* KisLayer defines opacity = 2, compositeOp = 3
* KisMask defines active = 2 (KisMask does not inherit kislayer)
*/
virtual PropertyList sectionModelProperties() const;
/**
* Change the section model properties.
*/
virtual void setSectionModelProperties(const PropertyList &properties);
/**
* Return all the properties of this layer as a KoProperties-based
* serializable key-value list.
*/
KoProperties & nodeProperties() const;
/**
* Merge the specified properties with the properties of this
* layer. Whereever these properties overlap, the value of the
* node properties is changed. No properties on the node are
* deleted. If there are new properties in this list, they will be
* added on the node.
*/
void mergeNodeProperties(const KoProperties & properties);
/**
* Compare the given properties list with the properties of this
* node.
*
* @return false only if the same property exists in both lists
* but with a different value. Properties that are not in both
* lists are disregarded.
*/
bool check(const KoProperties & properties) const;
/**
* Accept the KisNodeVisitor (for the Visitor design pattern),
* should call the correct function on the KisNodeVisitor for this
* node type, so you need to override it for all leaf classes in
* the node inheritance hierarchy.
*
* return false if the visitor could not successfully act on this
* node instance.
*/
virtual bool accept(KisNodeVisitor &) {
return false;
}
/**
* Accept the KisNodeVisitor (for the Visitor design pattern),
* should call the correct function on the KisProcessingVisitor
* for this node type, so you need to override it for all leaf
* classes in the node inheritance hierarchy.
*
* The processing visitor differs from node visitor in the way
* that it accepts undo adapter, that allows the processing to
* be multithreaded
*/
virtual void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) {
Q_UNUSED(visitor);
Q_UNUSED(undoAdapter);
}
/**
* @return a thumbnail in requested size. The thumbnail is a rgba
* QImage and may have transparent parts. Returns a fully
* transparent QImage of the requested size if the current node
* type cannot generate a thumbnail. If the requested size is too
* big, return a null QImage.
*/
virtual QImage createThumbnail(qint32 w, qint32 h);
/**
* Ask this node to re-read the pertinent settings from the krita
* configuration.
*/
virtual void updateSettings() {
}
/**
* @return true if this node is visible (i.e, active (except for
* selection masks where visible and active properties are
* different)) in the graph
*/
virtual bool visible(bool recursive = false) const;
/**
* Set the visible status of this node. Visible nodes are active
* in the graph (except for selections masks which can be active
* while hidden), that is to say, they are taken into account
* when merging. Invisible nodes play no role in the final image
*, but will be modified when modifying all layers, for instance
* when cropping.
*
* Toggling the visibility of a node will not automatically lead
* to recomposition.
*
* @param visible the new visibility state
* @param isLoading if true, the property is set during loading.
*/
virtual void setVisible(bool visibile, bool loading = false);
/**
* Return the locked status of this node. Locked nodes cannot be
* edited.
*/
bool userLocked() const;
/**
* Set the locked status of this node. Locked nodes cannot be
* edited.
*/
void setUserLocked(bool l);
/**
* Return the locked status of this node. System Locked nodes indicates
* that an algorithm is processing them and that an other
* algorithm need to wait before accessing it.
*/
bool systemLocked() const;
/**
* Set the locked status of this node. System Locked nodes indicates
* that an algorithm is processing them and that an other
* algorithm need to wait before accessing it.
*
* A KisNode will update the layer model when the lock is released.
*
* @param l lock state
* @param update set false if the tools shouldn't be locked
*/
virtual void setSystemLocked(bool l, bool update = true);
/**
* @return true if the node can be edited:
*
* if checkVisibility is true, then the node is only editable if it is visible and not locked.
* if checkVisibility is false, then the node is editable if it's not locked.
*/
bool isEditable(bool checkVisibility = true) const;
/**
* @return true if the node is editable and has a paintDevice()
* which which can be used for accessing pixels. It is an
* equivalent to (isEditable() && paintDevice())
*/
bool hasEditablePaintDevice() const;
/**
* @return the x-offset of this layer in the image plane.
*/
virtual qint32 x() const {
return 0;
}
/**
* Set the x offset of this layer in the image place.
* Re-implement this where it makes sense, by default it does
* nothing. It should not move child nodes.
*/
virtual void setX(qint32) {
}
/**
* @return the y-offset of this layer in the image plane.
*/
virtual qint32 y() const {
return 0;
}
/**
* Set the y offset of this layer in the image place.
* Re-implement this where it makes sense, by default it does
* nothing. It should not move child nodes.
*/
virtual void setY(qint32) {
}
/**
* Returns an approximation of where the bounds on actual data are
* in this node.
*/
virtual QRect extent() const {
return QRect();
}
/**
* Returns the exact bounds of where the actual data resides in
* this node.
*/
virtual QRect exactBounds() const {
return QRect();
}
/**
* Sets the state of the node to the value of @param collapsed
*/
void setCollapsed(bool collapsed);
/**
* returns the collapsed state of this node
*/
bool collapsed() const;
/**
* Sets a color label index associated to the layer. The actual
* color of the label and the number of available colors is
* defined by Krita GUI configuration.
*/
void setColorLabelIndex(int index);
/**
* \see setColorLabelIndex
*/
int colorLabelIndex() const;
/**
* Returns true if the offset of the node can be changed in a LodN
* stroke. Currently, all the nodes except shape layers support that.
*/
bool supportsLodMoves() const;
/**
* Return the keyframe channels associated with this node
* @return list of keyframe channels
*/
QList<KisKeyframeChannel *> keyframeChannels() const;
/**
* Get the keyframe channel with given id.
* If the channel does not yet exist and the node supports the requested
* channel, it will be created if create is true.
* @param id internal name for channel
* @param create attempt to create the channel if it does not exist yet
* @return keyframe channel with the id, or null if not found
*/
KisKeyframeChannel *getKeyframeChannel(const QString &id, bool create);
KisKeyframeChannel *getKeyframeChannel(const QString &id) const;
bool useInTimeline() const;
void setUseInTimeline(bool value);
bool isAnimated() const;
virtual void enableAnimation();
virtual void setImage(KisImageWSP image);
protected:
void setSupportsLodMoves(bool value);
/**
* FIXME: This method is a workaround for getting parent node
* on a level of KisBaseNode. In fact, KisBaseNode should inherit
* KisNode (in terms of current Krita) to be able to traverse
* the node stack
*/
virtual KisBaseNodeSP parentCallback() const {
- return 0;
+ return KisBaseNodeSP();
}
virtual void notifyParentVisibilityChanged(bool value) {
Q_UNUSED(value);
}
/**
* This callback is called when some meta state of the base node
* that can be interesting to the UI has changed. E.g. visibility,
* lockness, opacity, compositeOp and etc. This signal is
* forwarded by the KisNode and KisNodeGraphListener to the model
* in KisLayerBox, so it can update its controls when information
* changes.
*/
virtual void baseNodeChangedCallback() {
}
virtual void baseNodeInvalidateAllFramesCallback() {
}
/**
* Add a keyframe channel for this node. The channel will be added
* to the common hash table which will be available to the UI.
*
* WARNING: the \p channel object *NOT* become owned by the node!
* The caller must ensure manually that the lifetime of
* the object coincide with the lifetime of the node.
*/
virtual void addKeyframeChannel(KisKeyframeChannel* channel);
/**
* Attempt to create the requested channel. Used internally by getKeyframeChannel.
* Subclasses should implement this method to catch any new channel types they support.
* @param id channel to create
* @return newly created channel or null
*/
virtual KisKeyframeChannel * requestKeyframeChannel(const QString &id);
Q_SIGNALS:
/**
* This signal is emitted when the visibility of the layer is changed with \ref setVisible.
*/
void visibilityChanged(bool);
/**
* This signal is emitted when the node is locked or unlocked with \ref setUserLocked.
*/
void userLockingChanged(bool);
/**
* This signal is emitted when the node is locked or unlocked with \ref setSystemLocked.
*/
void systemLockingChanged(bool);
void keyframeChannelAdded(KisKeyframeChannel *channel);
private:
struct Private;
Private * const m_d;
};
Q_DECLARE_METATYPE( KisBaseNode::PropertyList )
#endif
diff --git a/libs/image/kis_base_processor.cpp b/libs/image/kis_base_processor.cpp
index a416d36be4..d0c57893ff 100644
--- a/libs/image/kis_base_processor.cpp
+++ b/libs/image/kis_base_processor.cpp
@@ -1,198 +1,198 @@
/*
* Copyright (c) 2004,2006-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_base_processor.h"
#include <QString>
#include "kis_bookmarked_configuration_manager.h"
#include "filter/kis_filter_configuration.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
class KisBaseProcessorConfigurationFactory : public KisSerializableConfigurationFactory
{
public:
KisBaseProcessorConfigurationFactory(KisBaseProcessor* _generator) : m_generator(_generator) {}
~KisBaseProcessorConfigurationFactory() override {}
KisSerializableConfigurationSP createDefault() override {
- return m_generator->factoryConfiguration(0);
+ return m_generator->factoryConfiguration(KisPaintDeviceSP());
}
KisSerializableConfigurationSP create(const QDomElement& e) override {
- KisSerializableConfigurationSP config = m_generator->factoryConfiguration(0);
+ KisSerializableConfigurationSP config = m_generator->factoryConfiguration(KisPaintDeviceSP());
config->fromXML(e);
return config;
}
private:
KisBaseProcessor* m_generator;
};
struct Q_DECL_HIDDEN KisBaseProcessor::Private {
Private()
: bookmarkManager(0)
, supportsPainting(false)
, supportsAdjustmentLayers(true)
, supportsThreading(true)
, showConfigurationWidget(true)
, colorSpaceIndependence(FULLY_INDEPENDENT) {
}
KisBookmarkedConfigurationManager* bookmarkManager;
KoID id;
KoID category; // The category in the filter menu this filter fits
QString entry; // the i18n'ed accelerated menu text
QKeySequence shortcut;
bool supportsPainting;
bool supportsAdjustmentLayers;
bool supportsThreading;
bool showConfigurationWidget;
ColorSpaceIndependence colorSpaceIndependence;
};
KisBaseProcessor::KisBaseProcessor(const KoID& id, const KoID & category, const QString & entry)
: d(new Private)
{
d->id = id;
d->category = category;
d->entry = entry;
}
void KisBaseProcessor::init(const QString& configEntryGroup)
{
d->bookmarkManager = new KisBookmarkedConfigurationManager(configEntryGroup, new KisBaseProcessorConfigurationFactory(this));
}
KisBaseProcessor::~KisBaseProcessor()
{
delete d->bookmarkManager;
delete d;
}
KisFilterConfigurationSP KisBaseProcessor::factoryConfiguration(const KisPaintDeviceSP) const
{
return new KisFilterConfiguration(id(), 0);
}
KisFilterConfigurationSP KisBaseProcessor::defaultConfiguration(const KisPaintDeviceSP pd) const
{
KisFilterConfigurationSP fc = 0;
// if (bookmarkManager()) {
// fc = dynamic_cast<KisFilterConfiguration*>(bookmarkManager()->defaultConfiguration());
// }
if (!fc || !fc->isCompatible(pd)) {
fc = factoryConfiguration(pd);
}
return fc;
}
KisConfigWidget * KisBaseProcessor::createConfigurationWidget(QWidget *, const KisPaintDeviceSP) const
{
return 0;
}
KisBookmarkedConfigurationManager* KisBaseProcessor::bookmarkManager()
{
return d->bookmarkManager;
}
const KisBookmarkedConfigurationManager* KisBaseProcessor::bookmarkManager() const
{
return d->bookmarkManager;
}
QString KisBaseProcessor::id() const
{
return d->id.id();
}
QString KisBaseProcessor::name() const
{
return d->id.name();
}
KoID KisBaseProcessor::menuCategory() const
{
return d->category;
}
QString KisBaseProcessor::menuEntry() const
{
return d->entry;
}
QKeySequence KisBaseProcessor::shortcut() const
{
return d->shortcut;
}
void KisBaseProcessor::setShortcut(const QKeySequence & shortcut)
{
d->shortcut = shortcut;
}
bool KisBaseProcessor::supportsPainting() const
{
return d->supportsPainting;
}
bool KisBaseProcessor::supportsAdjustmentLayers() const
{
return d->supportsAdjustmentLayers;
}
bool KisBaseProcessor::supportsThreading() const
{
return d->supportsThreading;
}
ColorSpaceIndependence KisBaseProcessor::colorSpaceIndependence() const
{
return d->colorSpaceIndependence;
}
void KisBaseProcessor::setSupportsPainting(bool v)
{
d->supportsPainting = v;
}
void KisBaseProcessor::setSupportsAdjustmentLayers(bool v)
{
d->supportsAdjustmentLayers = v;
}
void KisBaseProcessor::setSupportsThreading(bool v)
{
d->supportsThreading = v;
}
void KisBaseProcessor::setColorSpaceIndependence(ColorSpaceIndependence v)
{
d->colorSpaceIndependence = v;
}
bool KisBaseProcessor::showConfigurationWidget()
{
return d->showConfigurationWidget;
}
void KisBaseProcessor::setShowConfigurationWidget(bool v)
{
d->showConfigurationWidget = v;
}
diff --git a/libs/image/kis_clone_layer.cpp b/libs/image/kis_clone_layer.cpp
index 54172ea484..c3def95ed8 100644
--- a/libs/image/kis_clone_layer.cpp
+++ b/libs/image/kis_clone_layer.cpp
@@ -1,319 +1,327 @@
/*
* 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.h"
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <KoIcon.h>
#include <kis_icon.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
#include "kis_default_bounds.h"
#include "kis_paint_device.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_paint_layer.h"
#include <QStack>
#include <kis_effect_mask.h>
#include "kis_lod_capable_layer_offset.h"
struct Q_DECL_HIDDEN KisCloneLayer::Private
{
Private(KisDefaultBoundsBaseSP defaultBounds)
: offset(defaultBounds)
{
}
KisPaintDeviceSP fallback;
KisLodCapableLayerOffset offset;
KisLayerSP copyFrom;
KisCloneInfo copyFromInfo;
CopyLayerType type;
};
KisCloneLayer::KisCloneLayer(KisLayerSP from, KisImageWSP image, const QString &name, quint8 opacity)
: KisLayer(image, name, opacity)
, m_d(new Private(new KisDefaultBounds(image)))
{
- m_d->fallback = new KisPaintDevice(image->colorSpace());
+ KisImageSP imageSP = image.toStrongRef();
+ if (!imageSP) {
+ return;
+ }
+ m_d->fallback = new KisPaintDevice(imageSP->colorSpace());
m_d->copyFrom = from;
m_d->type = COPY_PROJECTION;
// When loading the layer we copy from might not exist yet
if (m_d->copyFrom) {
m_d->copyFrom->registerClone(this);
}
}
KisCloneLayer::KisCloneLayer(const KisCloneLayer& rhs)
: KisLayer(rhs)
, m_d(new Private(new KisDefaultBounds(rhs.image())))
{
m_d->fallback = new KisPaintDevice(rhs.m_d->fallback->colorSpace());
m_d->copyFrom = rhs.copyFrom();
m_d->type = rhs.copyType();
m_d->offset = rhs.m_d->offset;
if (m_d->copyFrom) {
m_d->copyFrom->registerClone(this);
}
}
KisCloneLayer::~KisCloneLayer()
{
if (m_d->copyFrom) {
m_d->copyFrom->unregisterClone(this);
}
delete m_d;
}
KisLayerSP KisCloneLayer::reincarnateAsPaintLayer() const
{
KisPaintDeviceSP newOriginal = new KisPaintDevice(*original());
KisPaintLayerSP newLayer = new KisPaintLayer(image(), name(), opacity(), newOriginal);
newLayer->setX(newLayer->x() + x());
newLayer->setY(newLayer->y() + y());
newLayer->setCompositeOpId(compositeOpId());
newLayer->mergeNodeProperties(nodeProperties());
return newLayer;
}
bool KisCloneLayer::allowAsChild(KisNodeSP node) const
{
return node->inherits("KisMask");
}
KisPaintDeviceSP KisCloneLayer::paintDevice() const
{
return 0;
}
KisPaintDeviceSP KisCloneLayer::original() const
{
if (!m_d->copyFrom || !m_d->copyFrom->projection()) return m_d->fallback;
KisPaintDeviceSP retval;
switch (m_d->type) {
case COPY_PROJECTION:
retval = m_d->copyFrom->projection();
break;
case COPY_ORIGINAL:
default:
retval = m_d->copyFrom->original();
}
return retval;
}
bool KisCloneLayer::needProjection() const
{
return m_d->offset.x() || m_d->offset.y();
}
void KisCloneLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const
{
QRect copyRect = rect;
copyRect.translate(-m_d->offset.x(), -m_d->offset.y());
KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, copyRect);
}
void KisCloneLayer::setDirtyOriginal(const QRect &rect)
{
/**
* The original will be updated when the clone becomes visible
* again.
*/
if (!visible(true)) return;
/**
* HINT: this method is present for historical reasons only.
* Long time ago the updates were calculated in
* "copyOriginalToProjection" coordinate system. Now
* everything is done in "original()" space.
*/
KisLayer::setDirty(rect);
}
void KisCloneLayer::notifyParentVisibilityChanged(bool value)
{
- KisLayer::setDirty(image()->bounds());
+ KisImageSP imageSP = image().toStrongRef();
+ if (!imageSP) {
+ return;
+ }
+ KisLayer::setDirty(imageSP->bounds());
KisLayer::notifyParentVisibilityChanged(value);
}
QRect KisCloneLayer::needRectOnSourceForMasks(const QRect &rc) const
{
QStack<QRect> applyRects_unused;
bool rectVariesFlag;
QList<KisEffectMaskSP> effectMasks = this->effectMasks();
if (effectMasks.isEmpty()) return QRect();
QRect needRect = this->masksNeedRect(effectMasks,
rc,
applyRects_unused,
rectVariesFlag);
if (needRect.isEmpty() ||
(!rectVariesFlag && needRect == rc)) {
return QRect();
}
return needRect;
}
qint32 KisCloneLayer::x() const
{
return m_d->offset.x();
}
qint32 KisCloneLayer::y() const
{
return m_d->offset.y();
}
void KisCloneLayer::setX(qint32 x)
{
m_d->offset.setX(x);
}
void KisCloneLayer::setY(qint32 y)
{
m_d->offset.setY(y);
}
QRect KisCloneLayer::extent() const
{
QRect rect = original()->extent();
// HINT: no offset now. See a comment in setDirtyOriginal()
return rect | projection()->extent();
}
QRect KisCloneLayer::exactBounds() const
{
QRect rect = original()->exactBounds();
// HINT: no offset now. See a comment in setDirtyOriginal()
return rect | projection()->exactBounds();
}
QRect KisCloneLayer::accessRect(const QRect &rect, PositionToFilthy pos) const
{
QRect resultRect = rect;
if(pos & (N_FILTHY_PROJECTION | N_FILTHY)) {
if (m_d->offset.x() || m_d->offset.y()) {
resultRect |= rect.translated(-m_d->offset.x(), -m_d->offset.y());
}
/**
* KisUpdateOriginalVisitor will try to recalculate some area
* on the clone's source, so this extra rectangle should also
* be taken into account
*/
resultRect |= needRectOnSourceForMasks(rect);
}
return resultRect;
}
QRect KisCloneLayer::outgoingChangeRect(const QRect &rect) const
{
return rect.translated(m_d->offset.x(), m_d->offset.y());
}
bool KisCloneLayer::accept(KisNodeVisitor & v)
{
return v.visit(this);
}
void KisCloneLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
return visitor.visit(this, undoAdapter);
}
void KisCloneLayer::setCopyFrom(KisLayerSP fromLayer)
{
if (m_d->copyFrom) {
m_d->copyFrom->unregisterClone(this);
}
m_d->copyFrom = fromLayer;
if (m_d->copyFrom) {
m_d->copyFrom->registerClone(this);
}
}
KisLayerSP KisCloneLayer::copyFrom() const
{
return m_d->copyFrom;
}
void KisCloneLayer::setCopyType(CopyLayerType type)
{
m_d->type = type;
}
CopyLayerType KisCloneLayer::copyType() const
{
return m_d->type;
}
KisCloneInfo KisCloneLayer::copyFromInfo() const
{
return m_d->copyFrom ? KisCloneInfo(m_d->copyFrom) : m_d->copyFromInfo;
}
void KisCloneLayer::setCopyFromInfo(KisCloneInfo info)
{
Q_ASSERT(!m_d->copyFrom);
m_d->copyFromInfo = info;
}
QIcon KisCloneLayer::icon() const
{
return KisIconUtils::loadIcon("cloneLayer");
}
KisBaseNode::PropertyList KisCloneLayer::sectionModelProperties() const
{
KisBaseNode::PropertyList l = KisLayer::sectionModelProperties();
if (m_d->copyFrom)
l << KisBaseNode::Property(KoID("copy_from", i18n("Copy From")), m_d->copyFrom->name());
return l;
}
void KisCloneLayer::syncLodCache()
{
KisLayer::syncLodCache();
m_d->offset.syncLodOffset();
}
diff --git a/libs/image/kis_colorspace_convert_visitor.cpp b/libs/image/kis_colorspace_convert_visitor.cpp
index 36e323d14d..40dca99d8f 100644
--- a/libs/image/kis_colorspace_convert_visitor.cpp
+++ b/libs/image/kis_colorspace_convert_visitor.cpp
@@ -1,154 +1,163 @@
/*
* 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_colorspace_convert_visitor.h"
#include "kis_image.h"
#include "kis_paint_device.h"
#include "kis_undo_adapter.h"
#include "kis_adjustment_layer.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "lazybrush/kis_colorize_mask.h"
#include "kis_external_layer_iface.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter.h"
#include "generator/kis_generator_layer.h"
#include "kis_time_range.h"
#include <kundo2command.h>
KisColorSpaceConvertVisitor::KisColorSpaceConvertVisitor(KisImageWSP image,
const KoColorSpace *srcColorSpace,
const KoColorSpace *dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags)
: KisNodeVisitor()
, m_image(image)
, m_srcColorSpace(srcColorSpace)
, m_dstColorSpace(dstColorSpace)
, m_renderingIntent(renderingIntent)
, m_conversionFlags(conversionFlags)
{
}
KisColorSpaceConvertVisitor::~KisColorSpaceConvertVisitor()
{
}
bool KisColorSpaceConvertVisitor::visit(KisGroupLayer * layer)
{
convertPaintDevice(layer);
KisLayerSP child = dynamic_cast<KisLayer*>(layer->firstChild().data());
while (child) {
child->accept(*this);
child = dynamic_cast<KisLayer*>(child->nextSibling().data());
}
layer->resetCache();
return true;
}
bool KisColorSpaceConvertVisitor::visit(KisPaintLayer *layer)
{
return convertPaintDevice(layer);
}
bool KisColorSpaceConvertVisitor::visit(KisGeneratorLayer *layer)
{
return convertPaintDevice(layer);
}
bool KisColorSpaceConvertVisitor::visit(KisAdjustmentLayer * layer)
{
// XXX: Make undoable!
if (layer->filter()->name() == "perchannel") {
// Per-channel filters need to be reset because of different number
// of channels. This makes undo very tricky, but so be it.
// XXX: Make this more generic for after 1.6, when we'll have many
// channel-specific filters.
KisFilterSP f = KisFilterRegistry::instance()->value("perchannel");
layer->setFilter(f->defaultConfiguration(0));
}
layer->resetCache();
return true;
}
bool KisColorSpaceConvertVisitor::visit(KisExternalLayer *layer)
{
Q_UNUSED(layer)
return true;
}
bool KisColorSpaceConvertVisitor::convertPaintDevice(KisLayer* layer)
{
if (*m_dstColorSpace == *layer->colorSpace()) return true;
bool alphaLock = false;
if (m_srcColorSpace->colorModelId() != m_dstColorSpace->colorModelId()) {
layer->setChannelFlags(m_emptyChannelFlags);
KisPaintLayer *paintLayer = 0;
if ((paintLayer = dynamic_cast<KisPaintLayer*>(layer))) {
alphaLock = paintLayer->alphaLocked();
paintLayer->setChannelLockFlags(QBitArray());
}
}
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return false;
+ }
+
if (layer->original()) {
KUndo2Command* cmd = layer->original()->convertTo(m_dstColorSpace, m_renderingIntent, m_conversionFlags);
if (cmd) {
- m_image->undoAdapter()->addCommand(cmd);
+ image->undoAdapter()->addCommand(cmd);
}
}
if (layer->paintDevice()) {
KUndo2Command* cmd = layer->paintDevice()->convertTo(m_dstColorSpace, m_renderingIntent, m_conversionFlags);
if (cmd) {
- m_image->undoAdapter()->addCommand(cmd);
+ image->undoAdapter()->addCommand(cmd);
}
}
if (layer->projection()) {
KUndo2Command* cmd = layer->projection()->convertTo(m_dstColorSpace, m_renderingIntent, m_conversionFlags);
if (cmd) {
- m_image->undoAdapter()->addCommand(cmd);
+ image->undoAdapter()->addCommand(cmd);
}
}
KisPaintLayer *paintLayer = 0;
if ((paintLayer = dynamic_cast<KisPaintLayer*>(layer))) {
paintLayer->setAlphaLocked(alphaLock);
}
layer->setDirty();
layer->invalidateFrames(KisTimeRange::infinite(0), layer->extent());
return true;
}
bool KisColorSpaceConvertVisitor::visit(KisColorizeMask *mask)
{
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return false;
+ }
KUndo2Command* cmd = mask->setColorSpace(m_dstColorSpace, m_renderingIntent, m_conversionFlags);
if (cmd) {
- m_image->undoAdapter()->addCommand(cmd);
+ image->undoAdapter()->addCommand(cmd);
}
return true;
}
diff --git a/libs/image/kis_default_bounds.h b/libs/image/kis_default_bounds.h
index f25b088181..c32449a319 100644
--- a/libs/image/kis_default_bounds.h
+++ b/libs/image/kis_default_bounds.h
@@ -1,79 +1,79 @@
/*
* Copyright (c) 2010 Boudewijn Rempt <boud@valdyas.org>
* 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_DEFAULT_BOUNDS_H
#define KIS_DEFAULT_BOUNDS_H
#include <QRect>
#include "kis_types.h"
#include "kis_default_bounds_base.h"
class KisDefaultBounds;
class KisSelectionDefaultBounds;
class KisSelectionEmptyBounds;
typedef KisSharedPtr<KisDefaultBounds> KisDefaultBoundsSP;
typedef KisSharedPtr<KisSelectionDefaultBounds> KisSelectionDefaultBoundsSP;
typedef KisSharedPtr<KisSelectionEmptyBounds> KisSelectionEmptyBoundsSP;
class KRITAIMAGE_EXPORT KisDefaultBounds : public KisDefaultBoundsBase
{
public:
KisDefaultBounds(KisImageWSP image = 0);
virtual ~KisDefaultBounds();
QRect bounds() const;
bool wrapAroundMode() const;
int currentLevelOfDetail() const;
int currentTime() const;
bool externalFrameActive() const;
protected:
friend class KisPaintDeviceTest;
static const QRect infiniteRect;
private:
Q_DISABLE_COPY(KisDefaultBounds)
struct Private;
Private * const m_d;
};
class KRITAIMAGE_EXPORT KisSelectionDefaultBounds : public KisDefaultBounds
{
public:
- KisSelectionDefaultBounds(KisPaintDeviceSP parentPaintDevice = 0, KisImageWSP image = 0);
+ KisSelectionDefaultBounds(KisPaintDeviceSP parentPaintDevice, KisImageWSP image = 0);
~KisSelectionDefaultBounds();
QRect bounds() const;
private:
Q_DISABLE_COPY(KisSelectionDefaultBounds)
struct Private;
Private * const m_d;
};
class KRITAIMAGE_EXPORT KisSelectionEmptyBounds : public KisDefaultBounds
{
public:
KisSelectionEmptyBounds(KisImageWSP image);
virtual ~KisSelectionEmptyBounds();
QRect bounds() const;
};
#endif // KIS_DEFAULT_BOUNDS_H
diff --git a/libs/image/kis_histogram.cc b/libs/image/kis_histogram.cc
index 787f112b69..3e6dbcd547 100644
--- a/libs/image/kis_histogram.cc
+++ b/libs/image/kis_histogram.cc
@@ -1,240 +1,243 @@
/*
* Copyright (c) 2004 Boudewijn Rempt
* (c) 2005 Bart Coppens <kde@bartcoppens.be>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_histogram.h"
#include <QVector>
#include "kis_image.h"
#include "kis_paint_layer.h"
#include "kis_paint_device.h"
#include "KoColorSpace.h"
#include "kis_debug.h"
#include "kis_iterator_ng.h"
KisHistogram::KisHistogram(const KisPaintLayerSP layer,
KoHistogramProducer *producer,
const enumHistogramType type)
: m_paintDevice(layer->projection())
{
Q_ASSERT(producer);
- m_bounds = layer->image()->bounds();
+ KisImageSP imageSP = layer->image().toStrongRef();
+ if (imageSP) {
+ m_bounds = imageSP->bounds();
+ }
m_type = type;
m_producer = producer;
m_selection = false;
m_channel = 0;
updateHistogram();
}
KisHistogram::KisHistogram(const KisPaintDeviceSP paintdev,
const QRect &bounds,
KoHistogramProducer *producer,
const enumHistogramType type)
: m_paintDevice(paintdev)
{
Q_ASSERT(producer);
m_bounds = bounds;
m_producer = producer;
m_type = type;
m_selection = false;
m_channel = 0;
// TODO: Why does Krita crash when updateHistogram() is *not* called here?
updateHistogram();
}
KisHistogram::~KisHistogram()
{
delete m_producer;
}
void KisHistogram::updateHistogram()
{
if (m_bounds.isEmpty()) {
int numChannels = m_producer->channels().count();
m_completeCalculations.clear();
m_completeCalculations.resize(numChannels);
m_completeCalculations.clear();
m_completeCalculations.resize(numChannels);
return;
}
KisSequentialConstIterator srcIt(m_paintDevice, m_bounds);
const KoColorSpace* cs = m_paintDevice->colorSpace();
// Let the producer do it's work
m_producer->clear();
int i;
// XXX: the original code depended on their being a selection mask in the iterator
// if the paint device had a selection. When we changed that to passing an
// explicit selection to the createRectIterator call, that broke because
// paint devices didn't know about their selections anymore.
// updateHistogram should get a selection parameter.
do {
i = srcIt.nConseqPixels();
m_producer->addRegionToBin(srcIt.oldRawData(), 0, i, cs);
} while (srcIt.nextPixels(i));
computeHistogram();
}
void KisHistogram::computeHistogram()
{
if (!m_producer) return;
m_completeCalculations = calculateForRange(m_producer->viewFrom(),
m_producer->viewFrom() + m_producer->viewWidth());
if (m_selection) {
m_selectionCalculations = calculateForRange(m_selFrom, m_selTo);
} else {
m_selectionCalculations.clear();
}
#if 1
dump();
#endif
}
KisHistogram::Calculations KisHistogram::calculations()
{
return m_completeCalculations.at(m_channel);
}
KisHistogram::Calculations KisHistogram::selectionCalculations()
{
return m_selectionCalculations.at(m_channel);
}
QVector<KisHistogram::Calculations> KisHistogram::calculateForRange(double from, double to)
{
QVector<Calculations> calculations;
if (m_producer) {
uint count = m_producer->channels().count();
for (uint i = 0; i < count; i++) {
calculations.append(calculateSingleRange(i, from, to));
}
}
return calculations;
}
KisHistogram::Calculations KisHistogram::calculateSingleRange(int channel, double from, double to)
{
Calculations c;
// XXX If from == to, we only want a specific bin, handle that properly!
double max = from, min = to, total = 0.0, mean = 0.0; //, median = 0.0, stddev = 0.0;
quint32 high = 0, low = (quint32) - 1, count = 0;
if (m_producer->count() == 0) {
// We won't get anything, even if a range is specified
// XXX make sure all initial '0' values are correct here!
return c;
}
qint32 totbins = m_producer->numberOfBins();
quint32 current;
// convert the double range into actual bins:
double factor = static_cast<double>(totbins) / m_producer->viewWidth();
qint32 fromBin = static_cast<qint32>((from - m_producer->viewFrom()) * factor);
qint32 toBin = fromBin + static_cast<qint32>((to - from) * factor);
// Min, max, count, low, high
for (qint32 i = fromBin; i < toBin; i++) {
current = m_producer->getBinAt(channel, i);
double pos = static_cast<double>(i) / factor + from;
if (current > high)
high = current;
if (current < low)
low = current;
if (current > 0) {
if (pos < min)
min = pos;
if (pos > max)
max = pos;
}
// We do the count here as well.
// we can't use m_producer->count() for this, because of the range
count += current;
total += current * pos;
}
if (count > 0)
mean = total / count;
c.m_high = high;
c.m_low = low;
c.m_count = count;
c.m_min = min;
c.m_max = max;
c.m_mean = mean;
c.m_total = total;
return c;
}
void KisHistogram::dump()
{
dbgMath << "Histogram";
switch (m_type) {
case LINEAR:
dbgMath << "Linear histogram";
break;
case LOGARITHMIC:
dbgMath << "Logarithmic histogram";
}
dbgMath << "Dumping channel" << m_channel;
Calculations c = calculations();
/* for( int i = 0; i <256; ++i ) {
dbgMath <<"Value"
<< QString().setNum(i)
<< ": "
<< QString().setNum(m_values[i])
<< "\n";
}*/
dbgMath << "";
dbgMath << "Max:" << QString().setNum(c.getMax()) << "";
dbgMath << "Min:" << QString().setNum(c.getMin()) << "";
dbgMath << "High:" << QString().setNum(c.getHighest()) << "";
dbgMath << "Low:" << QString().setNum(c.getLowest()) << "";
dbgMath << "Mean:" << m_producer->positionToString(c.getMean()) << "";
dbgMath << "Total:" << QString().setNum(c.getTotal()) << "";
// dbgMath <<"Median:" << QString().setNum(m_median) <<"";
// dbgMath <<"Stddev:" << QString().setNum(m_stddev) <<"";
// dbgMath <<"percentile:" << QString().setNum(m_percentile) <<"";
dbgMath << "";
}
diff --git a/libs/image/kis_image.cc b/libs/image/kis_image.cc
index 8457e823a9..93c9915a11 100644
--- a/libs/image/kis_image.cc
+++ b/libs/image/kis_image.cc
@@ -1,1619 +1,1692 @@
/*
* 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 <klocalizedstring.h>
#include "KoColorSpaceRegistry.h"
#include "KoColor.h"
#include "KoColorProfile.h"
#include <KoCompositeOpRegistry.h>
#include "KisProofingConfiguration.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_layer.h"
#include "kis_painter.h"
#include "kis_selection.h"
#include "kis_transaction.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_image_animation_interface.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_utils.h"
#include "kis_lod_transform.h"
#include "kis_suspend_projection_updates_stroke_strategy.h"
#include "kis_sync_lod_cache_stroke_strategy.h"
#include "kis_projection_updates_filter.h"
#include "kis_layer_projection_plane.h"
#include "kis_update_time_monitor.h"
#include "kis_image_barrier_locker.h"
#include <QtCore>
#include <functional>
#include "kis_time_range.h"
// #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
struct KisImageSPStaticRegistrar {
KisImageSPStaticRegistrar() {
qRegisterMetaType<KisImageSP>("KisImageSP");
}
};
static KisImageSPStaticRegistrar __registrar;
class KisImage::KisImagePrivate
{
public:
- KisImagePrivate(KisImage *_q, qint32 w, qint32 h, const KoColorSpace *c, KisUndoStore *u)
+ KisImagePrivate(KisImage *_q, qint32 w, qint32 h,
+ const KoColorSpace *c,
+ KisUndoStore *undo,
+ KisImageAnimationInterface *_animationInterface)
: q(_q)
, lockedForReadOnly(false)
, width(w)
, height(h)
- , colorSpace(c)
+ , colorSpace(c ? c : KoColorSpaceRegistry::instance()->rgb8())
, nserver(1)
- , undoStore(u)
- , legacyUndoAdapter(u, _q)
- , postExecutionUndoAdapter(u, _q)
+ , undoStore(undo ? undo : new KisDumbUndoStore())
+ , legacyUndoAdapter(undoStore.data(), _q)
+ , postExecutionUndoAdapter(undoStore.data(), _q)
, recorder(_q)
, signalRouter(_q)
- , animationInterface(0)
+ , animationInterface(_animationInterface)
, scheduler(_q)
- {}
+ , axesCenter(QPointF(0.5, 0.5))
+ {
+ {
+ KisImageConfig cfg;
+ if (cfg.enableProgressReporting()) {
+ scheduler.setProgressProxy(&compositeProgressProxy);
+ }
+
+ // Each of these lambdas defines a new factory function.
+ scheduler.setLod0ToNStrokeStrategyFactory(
+ [=](bool forgettable) {
+ return KisLodSyncPair(
+ new KisSyncLodCacheStrokeStrategy(KisImageWSP(q), forgettable),
+ KisSyncLodCacheStrokeStrategy::createJobsData(KisImageWSP(q)));
+ });
+
+ scheduler.setSuspendUpdatesStrokeStrategyFactory(
+ [=]() {
+ return KisSuspendResumePair(
+ new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), true),
+ KisSuspendProjectionUpdatesStrokeStrategy::createSuspendJobsData(KisImageWSP(q)));
+ });
+
+ scheduler.setResumeUpdatesStrokeStrategyFactory(
+ [=]() {
+ return KisSuspendResumePair(
+ new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), false),
+ KisSuspendProjectionUpdatesStrokeStrategy::createResumeJobsData(KisImageWSP(q)));
+ });
+ }
+
+ connect(q, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
+ }
+
+ ~KisImagePrivate() {
+ /**
+ * Stop animation interface. It may use the rootLayer.
+ */
+ delete animationInterface;
+
+ /**
+ * First delete the nodes, while strokes
+ * and undo are still alive
+ */
+ rootLayer.clear();
+ }
KisImage *q;
quint32 lockCount = 0;
bool lockedForReadOnly;
qint32 width;
qint32 height;
double xres = 1.0;
double yres = 1.0;
const KoColorSpace * colorSpace;
KisProofingConfigurationSP proofingConfig;
KisSelectionSP deselectedGlobalSelection;
KisGroupLayerSP rootLayer; // The layers are contained in here
- QList<KisLayer*> dirtyLayers; // for thumbnails
QList<KisLayerCompositionSP> compositions;
KisNodeSP isolatedRootNode;
bool wrapAroundModePermitted = false;
KisNameServer nserver;
- KisUndoStore *undoStore;
+ QScopedPointer<KisUndoStore> undoStore;
KisLegacyUndoAdapter legacyUndoAdapter;
KisPostExecutionUndoAdapter postExecutionUndoAdapter;
KisActionRecorder recorder;
vKisAnnotationSP annotations;
QAtomicInt disableUIUpdateSignals;
KisProjectionUpdatesFilterSP projectionUpdatesFilter;
KisImageSignalRouter signalRouter;
KisImageAnimationInterface *animationInterface;
KisUpdateScheduler scheduler;
QAtomicInt disableDirtyRequests;
KisCompositeProgressProxy compositeProgressProxy;
bool blockLevelOfDetail = false;
+ QPointF axesCenter;
+
bool tryCancelCurrentStrokeAsync();
void notifyProjectionUpdatedInPatches(const QRect &rc);
};
KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace * colorSpace, const QString& name)
: QObject(0)
, KisShared()
+ , m_d(new KisImagePrivate(this, width, height,
+ colorSpace, undoStore,
+ new KisImageAnimationInterface(this)))
{
setObjectName(name);
- // Handle undoStore == 0 and colorSpace == 0 cases
- if (!undoStore) {
- undoStore = new KisDumbUndoStore();
- }
-
- const KoColorSpace *c;
- if (colorSpace != 0) {
- c = colorSpace;
- } else {
- c = KoColorSpaceRegistry::instance()->rgb8();
- }
- m_d = new KisImagePrivate(this, width, height, c, undoStore);
-
-
- {
- KisImageConfig cfg;
- if (cfg.enableProgressReporting()) {
- m_d->scheduler.setProgressProxy(&m_d->compositeProgressProxy);
- }
-
- // Each of these lambdas defines a new factory function.
- m_d->scheduler.setLod0ToNStrokeStrategyFactory(
- [=](bool forgettable) {
- return KisLodSyncPair(
- new KisSyncLodCacheStrokeStrategy(KisImageWSP(this), forgettable),
- KisSyncLodCacheStrokeStrategy::createJobsData(KisImageWSP(this)));
- });
-
- m_d->scheduler.setSuspendUpdatesStrokeStrategyFactory(
- [=]() {
- return KisSuspendResumePair(
- new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(this), true),
- KisSuspendProjectionUpdatesStrokeStrategy::createSuspendJobsData(KisImageWSP(this)));
- });
-
- m_d->scheduler.setResumeUpdatesStrokeStrategyFactory(
- [=]() {
- return KisSuspendResumePair(
- new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(this), false),
- KisSuspendProjectionUpdatesStrokeStrategy::createResumeJobsData(KisImageWSP(this)));
- });
- }
-
setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
-
- m_d->animationInterface = new KisImageAnimationInterface(this);
-
- connect(this, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
}
KisImage::~KisImage()
{
dbgImage << "deleting kisimage" << objectName();
/**
* Request the tools to end currently running strokes
*/
waitForDone();
- /**
- * Stop animation interface. It may use the rootLayer.
- */
- delete m_d->animationInterface;
-
- /**
- * First delete the nodes, while strokes
- * and undo are still alive
- */
- m_d->rootLayer = 0;
-
- delete m_d->undoStore;
delete m_d;
disconnect(); // in case Qt gets confused
}
+KisImage *KisImage::clone(bool exactCopy)
+{
+ return new KisImage(*this, 0, exactCopy);
+}
+
+KisImage::KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy)
+ : KisNodeFacade(),
+ KisNodeGraphListener(),
+ KisShared(),
+ m_d(new KisImagePrivate(this,
+ rhs.width(), rhs.height(),
+ rhs.colorSpace(),
+ undoStore ? undoStore : new KisDumbUndoStore(),
+ new KisImageAnimationInterface(*rhs.animationInterface(), this)))
+{
+ setObjectName(rhs.objectName());
+
+ m_d->xres = rhs.m_d->xres;
+ m_d->yres = rhs.m_d->yres;
+
+
+ if (rhs.m_d->proofingConfig) {
+ m_d->proofingConfig = toQShared(new KisProofingConfiguration(*rhs.m_d->proofingConfig));
+ }
+
+ KisNodeSP newRoot = rhs.root()->clone();
+ newRoot->setGraphListener(this);
+ newRoot->setImage(this);
+ m_d->rootLayer = dynamic_cast<KisGroupLayer*>(newRoot.data());
+ setRoot(newRoot);
+
+ if (exactCopy) {
+ QQueue<KisNodeSP> linearizedNodes;
+ KisLayerUtils::recursiveApplyNodes(rhs.root(),
+ [&linearizedNodes](KisNodeSP node) {
+ linearizedNodes.enqueue(node);
+ });
+ KisLayerUtils::recursiveApplyNodes(newRoot,
+ [&linearizedNodes](KisNodeSP node) {
+ KisNodeSP refNode = linearizedNodes.dequeue();
+ node->setUuid(refNode->uuid());
+ });
+ }
+
+ Q_FOREACH (KisLayerCompositionSP comp, rhs.m_d->compositions) {
+ m_d->compositions << toQShared(new KisLayerComposition(*comp, this));
+ }
+
+ rhs.m_d->nserver = KisNameServer(rhs.m_d->nserver);
+
+ vKisAnnotationSP newAnnotations;
+ Q_FOREACH (KisAnnotationSP annotation, rhs.m_d->annotations) {
+ newAnnotations << annotation->clone();
+ }
+ m_d->annotations = newAnnotations;
+
+ KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->projectionUpdatesFilter);
+ KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableUIUpdateSignals);
+ KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableDirtyRequests);
+
+ m_d->blockLevelOfDetail = rhs.m_d->blockLevelOfDetail;
+}
+
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);
}
void KisImage::invalidateAllFrames()
{
invalidateFrames(KisTimeRange::infinite(0), QRect());
}
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 QString &_baseName) const
{
QString baseName = _baseName;
if (m_d->nserver.currentSeed() == 0) {
m_d->nserver.number();
return i18n("background");
}
if (baseName.isEmpty()) {
baseName = i18n("Layer");
}
return QString("%1 %2").arg(baseName).arg(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(bool readOnly)
{
if (!locked()) {
requestStrokeEnd();
m_d->scheduler.barrierLock();
m_d->lockedForReadOnly = readOnly;
} else {
m_d->lockedForReadOnly &= readOnly;
}
m_d->lockCount++;
}
bool KisImage::tryBarrierLock(bool readOnly)
{
bool result = true;
if (!locked()) {
result = m_d->scheduler.tryBarrierLock();
m_d->lockedForReadOnly = readOnly;
}
if (result) {
m_d->lockCount++;
m_d->lockedForReadOnly &= readOnly;
}
return result;
}
bool KisImage::isIdle()
{
return !locked() && m_d->scheduler.isIdle();
}
void KisImage::lock()
{
if (!locked()) {
requestStrokeEnd();
m_d->scheduler.lock();
}
m_d->lockCount++;
m_d->lockedForReadOnly = false;
}
void KisImage::unlock()
{
Q_ASSERT(locked());
if (locked()) {
m_d->lockCount--;
if (m_d->lockCount == 0) {
m_d->scheduler.unlock(!m_d->lockedForReadOnly);
}
}
}
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.applyVisitorAllFrames(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.applyVisitorAllFrames(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.applyVisitorAllFrames(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 scaleX, qreal scaleY, KisFilterStrategy *filterStrategy)
{
KUndo2MagicString actionName(kundo2_i18n("Scale Layer"));
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(this, node,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName);
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(scaleX, scaleY,
0, 0,
QPointF(),
0,
0, 0,
filterStrategy);
applicator.applyVisitorAllFrames(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.applyVisitorAllFrames(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.applyVisitorAllFrames(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();
}
void KisImage::flatten()
{
KisLayerUtils::flattenImage(this);
}
void KisImage::mergeMultipleLayers(QList<KisNodeSP> mergedNodes, KisNodeSP putAfter)
{
if (!KisLayerUtils::tryMergeSelectionMasks(this, mergedNodes, putAfter)) {
KisLayerUtils::mergeMultipleLayers(this, mergedNodes, putAfter);
}
}
void KisImage::mergeDown(KisLayerSP layer, const KisMetaData::MergeStrategy* strategy)
{
KisLayerUtils::mergeDown(this, layer, strategy);
}
void KisImage::flattenLayer(KisLayerSP layer)
{
KisLayerUtils::flattenLayer(this, layer);
}
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 QSize& scaledImageSize, const KoColorProfile *profile)
{
if (scaledImageSize.isEmpty()) {
return QImage();
}
KisPaintDeviceSP dev = new KisPaintDevice(colorSpace());
KisPainter gc;
gc.copyAreaOptimized(QPoint(0, 0), projection(), dev, bounds());
gc.end();
double scaleX = qreal(scaledImageSize.width()) / width();
double scaleY = qreal(scaledImageSize.height()) / height();
QPointer<KoUpdater> updater = new KoDummyUpdater();
KisTransformWorker worker(dev, scaleX, scaleY, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, updater, KisFilterStrategyRegistry::instance()->value("Bicubic"));
worker.run();
delete updater;
return dev->convertToQImage(profile);
}
void KisImage::notifyLayersChanged()
{
m_d->signalRouter.emitNotification(LayersChangedSignal);
}
QRect KisImage::bounds() const
{
return QRect(0, 0, width(), height());
}
QRect KisImage::effectiveLodBounds() const
{
QRect boundRect = bounds();
const int lod = currentLevelOfDetail();
if (lod > 0) {
KisLodTransform t(lod);
boundRect = t.map(boundRect);
}
return boundRect;
}
KisPostExecutionUndoAdapter* KisImage::postExecutionUndoAdapter() const
{
- return &m_d->postExecutionUndoAdapter;
+ const int lod = currentLevelOfDetail();
+ return lod > 0 ?
+ m_d->scheduler.lodNPostExecutionUndoAdapter() :
+ &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;
+ m_d->undoStore.reset(undoStore);
}
KisUndoStore* KisImage::undoStore()
{
- return m_d->undoStore;
+ return m_d->undoStore.data();
}
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 = original->defaultPixel();
}
m_d->rootLayer = rootLayer;
m_d->rootLayer->disconnect();
m_d->rootLayer->setGraphListener(this);
m_d->rootLayer->setImage(this);
KisPaintDeviceSP newOriginal = m_d->rootLayer->original();
newOriginal->setDefaultPixel(defaultProjectionColor);
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();
}
KisImageSignalRouter* KisImage::signalRouter()
{
return &m_d->signalRouter;
}
void KisImage::waitForDone()
{
requestStrokeEnd();
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.
*/
if (strokeStrategy->requestsOtherStrokesToEnd()) {
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();
}
return m_d->scheduler.startStroke(strokeStrategy);
}
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(std::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());
invalidateAllFrames();
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());
invalidateAllFrames();
// 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);
m_d->scheduler.addJob(id, data);
}
void KisImage::endStroke(KisStrokeId id)
{
m_d->scheduler.endStroke(id);
}
bool KisImage::cancelStroke(KisStrokeId id)
{
return m_d->scheduler.cancelStroke(id);
}
bool KisImage::KisImagePrivate::tryCancelCurrentStrokeAsync()
{
return scheduler.tryCancelCurrentStrokeAsync();
}
void KisImage::requestUndoDuringStroke()
{
emit sigUndoDuringStrokeRequested();
}
void KisImage::requestStrokeCancellation()
{
if (!m_d->tryCancelCurrentStrokeAsync()) {
emit sigStrokeCancellationRequested();
}
}
+UndoResult KisImage::tryUndoUnfinishedLod0Stroke()
+{
+ return m_d->scheduler.tryUndoLastStrokeAsync();
+}
+
void KisImage::requestStrokeEnd()
{
emit sigStrokeEndRequested();
emit sigStrokeEndRequestedActiveNodeFiltered();
}
void KisImage::requestStrokeEndActiveNode()
{
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;
m_d->animationInterface->notifyNodeChanged(root.data(), rc, true);
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;
m_d->animationInterface->notifyNodeChanged(root.data(), rc, true);
m_d->scheduler.fullRefreshAsync(root, rc, cropRect);
}
void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect)
{
KIS_ASSERT_RECOVER_RETURN(pseudoFilthy);
m_d->animationInterface->notifyNodeChanged(pseudoFilthy.data(), rc, false);
m_d->scheduler.updateProjectionNoFilthy(pseudoFilthy, rc, cropRect);
}
void KisImage::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
{
m_d->scheduler.addSpontaneousJob(spontaneousJob);
}
void KisImage::setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter)
{
// udpate filters are *not* recursive!
KIS_ASSERT_RECOVER_NOOP(!filter || !m_d->projectionUpdatesFilter);
m_d->projectionUpdatesFilter = filter;
}
KisProjectionUpdatesFilterSP KisImage::projectionUpdatesFilter() const
{
return m_d->projectionUpdatesFilter;
}
void KisImage::disableDirtyRequests()
{
setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP(new KisDropAllProjectionUpdatesFilter()));
}
void KisImage::enableDirtyRequests()
{
setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP());
}
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) {
int lod = currentLevelOfDetail();
QRect dirtyRect = !lod ? rc : KisLodTransform::upscaledRect(rc, lod);
if (dirtyRect.isEmpty()) return;
emit sigImageUpdated(dirtyRect);
}
}
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)
{
if (rect.isEmpty()) return;
KisNodeGraphListener::requestProjectionUpdate(node, rect);
m_d->scheduler.updateProjection(node, rect, cropRect);
}
void KisImage::requestProjectionUpdate(KisNode *node, const QRect& rect)
{
if (m_d->projectionUpdatesFilter
&& m_d->projectionUpdatesFilter->filter(this, node, rect)) {
return;
}
m_d->animationInterface->notifyNodeChanged(node, rect, false);
/**
* 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) {
const QRect boundRect = effectiveLodBounds();
KisWrappedRect splitRect(rect, boundRect);
Q_FOREACH (const QRect &rc, splitRect) {
requestProjectionUpdateImpl(node, rc, boundRect);
}
} else {
requestProjectionUpdateImpl(node, rect, bounds());
}
}
void KisImage::invalidateFrames(const KisTimeRange &range, const QRect &rect)
{
m_d->animationInterface->invalidateFrames(range, rect);
}
void KisImage::requestTimeSwitch(int time)
{
m_d->animationInterface->requestTimeSwitchNonGUI(time);
}
QList<KisLayerCompositionSP> KisImage::compositions()
{
return m_d->compositions;
}
void KisImage::addComposition(KisLayerCompositionSP composition)
{
m_d->compositions.append(composition);
}
void KisImage::removeComposition(KisLayerCompositionSP composition)
{
m_d->compositions.removeAll(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.wrapAroundModeSupported();
}
void KisImage::setDesiredLevelOfDetail(int lod)
{
if (m_d->blockLevelOfDetail) {
qWarning() << "WARNING: KisImage::setDesiredLevelOfDetail()"
<< "was called while LoD functionality was being blocked!";
return;
}
m_d->scheduler.setDesiredLevelOfDetail(lod);
}
int KisImage::currentLevelOfDetail() const
{
if (m_d->blockLevelOfDetail) {
return 0;
}
return m_d->scheduler.currentLevelOfDetail();
}
void KisImage::setLevelOfDetailBlocked(bool value)
{
KisImageBarrierLockerRaw l(this);
if (value && !m_d->blockLevelOfDetail) {
m_d->scheduler.setDesiredLevelOfDetail(0);
}
m_d->blockLevelOfDetail = value;
}
void KisImage::explicitRegenerateLevelOfDetail()
{
if (!m_d->blockLevelOfDetail) {
m_d->scheduler.explicitRegenerateLevelOfDetail();
}
}
bool KisImage::levelOfDetailBlocked() const
{
return m_d->blockLevelOfDetail;
}
void KisImage::notifyNodeCollpasedChanged()
{
emit sigNodeCollapsedChanged();
}
KisImageAnimationInterface* KisImage::animationInterface() const
{
return m_d->animationInterface;
}
void KisImage::setProofingConfiguration(KisProofingConfigurationSP proofingConfig)
{
m_d->proofingConfig = proofingConfig;
emit sigProofingConfigChanged();
}
KisProofingConfigurationSP KisImage::proofingConfiguration() const
{
if (!m_d->proofingConfig) {
KisImageConfig cfg;
m_d->proofingConfig = cfg.defaultProofingconfiguration();
}
return m_d->proofingConfig;
}
+
+QPointF KisImage::mirrorAxesCenter() const
+{
+ return m_d->axesCenter;
+}
+
+void KisImage::setMirrorAxesCenter(const QPointF &value) const
+{
+ m_d->axesCenter = value;
+}
diff --git a/libs/image/kis_image.h b/libs/image/kis_image.h
index ed2c77a706..d420171435 100644
--- a/libs/image/kis_image.h
+++ b/libs/image/kis_image.h
@@ -1,948 +1,984 @@
/*
* 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.
*/
#ifndef KIS_IMAGE_H_
#define KIS_IMAGE_H_
#include <QObject>
#include <QString>
#include <QPainter>
#include <QRect>
#include <QRegion>
#include <QBitArray>
#include <KoColorConversionTransformation.h>
#include "kis_paint_device.h" // msvc cannot handle forward declarations, so include kis_paint_device here
#include "kis_types.h"
#include "kis_shared.h"
#include "kis_node_graph_listener.h"
#include "kis_node_facade.h"
#include "kis_image_interfaces.h"
+#include "kis_strokes_queue_undo_result.h"
#include <kritaimage_export.h>
class KisDocument;
class KoColorSpace;
class KoColor;
class KisCompositeProgressProxy;
class KisActionRecorder;
class KisUndoStore;
class KisUndoAdapter;
class KisImageSignalRouter;
class KisPostExecutionUndoAdapter;
class KisFilterStrategy;
class KoColorProfile;
class KisLayerComposition;
class KisSpontaneousJob;
class KisImageAnimationInterface;
class KUndo2MagicString;
class KisProofingConfiguration;
namespace KisMetaData
{
class MergeStrategy;
}
/**
* This is the image class, it contains a tree of KisLayer stack and
* meta information about the image. And it also provides some
* functions to manipulate the whole image.
*/
class KRITAIMAGE_EXPORT KisImage : public QObject,
public KisStrokesFacade,
+ public KisStrokeUndoFacade,
public KisUpdatesFacade,
public KisProjectionUpdateListener,
public KisNodeFacade,
public KisNodeGraphListener,
public KisShared
{
Q_OBJECT
public:
/// @param colorSpace can be null. in that case it will be initialised to a default color space.
- KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace * colorSpace, const QString& name);
+ KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString& name);
virtual ~KisImage();
public: // KisNodeGraphListener implementation
void aboutToAddANode(KisNode *parent, int index);
void nodeHasBeenAdded(KisNode *parent, int index);
void aboutToRemoveANode(KisNode *parent, int index);
void nodeChanged(KisNode * node);
void invalidateAllFrames();
void notifySelectionChanged();
void requestProjectionUpdate(KisNode *node, const QRect& rect);
void invalidateFrames(const KisTimeRange &range, const QRect &rect);
void requestTimeSwitch(int time);
public: // KisProjectionUpdateListener implementation
void notifyProjectionUpdated(const QRect &rc);
public:
+ /**
+ * Makes a copy of the image with all the layers. If possible, shallow
+ * copies of the layers are made.
+ *
+ * \p exactCopy shows if the copied image should look *exactly* the same as
+ * the other one (according to it's .kra xml representation). It means that
+ * the layers will have the same UUID keys and, therefore, you are not
+ * expected to use the copied image anywhere except for saving. Don't use
+ * this option if you plan to work with the copied image later.
+ */
+ KisImage* clone(bool exactCopy = false);
+
/**
* Render the projection onto a QImage.
*/
QImage convertToQImage(qint32 x1,
qint32 y1,
qint32 width,
qint32 height,
const KoColorProfile * profile);
/**
* Render the projection onto a QImage.
* (this is an overloaded function)
*/
QImage convertToQImage(QRect imageRect,
const KoColorProfile * profile);
/**
* XXX: docs!
*/
QImage convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile);
/**
* Calls KisUpdateScheduler::lock (XXX: APIDOX -- what does that mean?)
*/
void lock();
/**
* Calls KisUpdateScheduler::unlock (XXX: APIDOX -- what does that mean?)
*/
void unlock();
/**
* Returns true if lock() has been called more often than unlock().
*/
bool locked() const;
/**
* @return the global selection object or 0 if there is none. The
* global selection is always read-write.
*/
KisSelectionSP globalSelection() const;
/**
* Retrieve the next automatic layername (XXX: fix to add option to return Mask X)
*/
QString nextLayerName(const QString &baseName = "") const;
/**
* Set the automatic layer name counter one back.
*/
void rollBackLayerName();
/**
* Resize the image to the specified rect. The resize
* method handles the creating on an undo step itself.
*
* @param newRect the rect describing the new width, height and offset
* of the image
*/
void resizeImage(const QRect& newRect);
/**
* Crop the image to the specified rect. The crop
* method handles the creating on an undo step itself.
*
* @param newRect the rect describing the new width, height and offset
* of the image
*/
void cropImage(const QRect& newRect);
/**
* Crop a node to @newRect. The node will *not* be moved anywhere,
* it just drops some content
*/
void cropNode(KisNodeSP node, const QRect& newRect);
/// XXX: ApiDox
void scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy);
/// XXX: ApiDox
void scaleNode(KisNodeSP node, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy);
/**
* Execute a rotate transform on all layers in this image.
* Image is resized to fit rotated image.
*/
void rotateImage(double radians);
/**
* Execute a rotate transform on on a subtree of this image.
* Image is not resized.
*/
void rotateNode(KisNodeSP node, double radians);
/**
* Execute a shear transform on all layers in this image.
*/
void shear(double angleX, double angleY);
/**
* Shear a node and all its children.
* @param angleX, @param angleY are given in degrees.
*/
void shearNode(KisNodeSP node, double angleX, double angleY);
/**
* Convert the image and all its layers to the dstColorSpace
*/
void convertImageColorSpace(const KoColorSpace *dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
/**
* Set the color space of the projection (and the root layer)
* to dstColorSpace. No conversion is done for other layers,
* their colorspace can differ.
* NOTE: Note conversion is done, only regeneration, so no rendering
* intent needed
*/
void convertProjectionColorSpace(const KoColorSpace *dstColorSpace);
// Get the profile associated with this image
const KoColorProfile * profile() const;
/**
* Set the profile of the image to the new profile and do the same for
* all layers that have the same colorspace and profile of the image.
* It doesn't do any pixel conversion.
*
* This is essential if you have loaded an image that didn't
* have an embedded profile to which you want to attach the right profile.
*
* This does not create an undo action; only call it when creating or
* loading an image.
*
* @returns false if the profile could not be assigned
*/
bool assignImageProfile(const KoColorProfile *profile);
/**
* Returns the current undo adapter. You can add new commands to the
* undo stack using the adapter. This adapter is used for a backward
* compatibility for old commands created before strokes. It blocks
* all the porcessing at the scheduler, waits until it's finished
* adn executes commands exclusively.
*/
KisUndoAdapter* undoAdapter() const;
/**
* This adapter is used by the strokes system. The commands are added
* to it *after* redo() is done (in the scheduler context). They are
* wrapped into a special command and added to the undo stack. redo()
* in not called.
*/
KisPostExecutionUndoAdapter* postExecutionUndoAdapter() const;
/**
* Replace current undo store with the new one. The old store
* will be deleted.
* This method is used by KisDocument for dropping all the commands
* during file loading.
*/
void setUndoStore(KisUndoStore *undoStore);
/**
* Return current undo store of the image
*/
KisUndoStore* undoStore();
/**
* @return the action recorder associated with this image
*/
KisActionRecorder* actionRecorder() const;
/**
* Tell the image it's modified; this emits the sigImageModified
* signal. This happens when the image needs to be saved
*/
void setModified();
/**
* The default colorspace of this image: new layers will have this
* colorspace and the projection will have this colorspace.
*/
const KoColorSpace * colorSpace() const;
/**
* X resolution in pixels per pt
*/
double xRes() const;
/**
* Y resolution in pixels per pt
*/
double yRes() const;
/**
* Set the resolution in pixels per pt.
*/
void setResolution(double xres, double yres);
/**
* Convert a document coordinate to a pixel coordinate.
*
* @param documentCoord PostScript Pt coordinate to convert.
*/
QPointF documentToPixel(const QPointF &documentCoord) const;
/**
* Convert a document coordinate to an integer pixel coordinate.
*
* @param documentCoord PostScript Pt coordinate to convert.
*/
QPoint documentToIntPixel(const QPointF &documentCoord) const;
/**
* Convert a document rectangle to a pixel rectangle.
*
* @param documentRect PostScript Pt rectangle to convert.
*/
QRectF documentToPixel(const QRectF &documentRect) const;
/**
* Convert a document rectangle to an integer pixel rectangle.
*
* @param documentRect PostScript Pt rectangle to convert.
*/
QRect documentToIntPixel(const QRectF &documentRect) const;
/**
* Convert a pixel coordinate to a document coordinate.
*
* @param pixelCoord pixel coordinate to convert.
*/
QPointF pixelToDocument(const QPointF &pixelCoord) const;
/**
* Convert an integer pixel coordinate to a document coordinate.
* The document coordinate is at the centre of the pixel.
*
* @param pixelCoord pixel coordinate to convert.
*/
QPointF pixelToDocument(const QPoint &pixelCoord) const;
/**
* Convert a document rectangle to an integer pixel rectangle.
*
* @param pixelCoord pixel coordinate to convert.
*/
QRectF pixelToDocument(const QRectF &pixelCoord) const;
/**
* Return the width of the image
*/
qint32 width() const;
/**
* Return the height of the image
*/
qint32 height() const;
/**
* Return the size of the image
*/
QSize size() const {
return QSize(width(), height());
}
/**
* @return the root node of the image node graph
*/
KisGroupLayerSP rootLayer() const;
/**
* Return the projection; that is, the complete, composited
* representation of this image.
*/
KisPaintDeviceSP projection() const;
/**
* Return the number of layers (not other nodes) that are in this
* image.
*/
qint32 nlayers() const;
/**
* Return the number of layers (not other node types) that are in
* this image and that are hidden.
*/
qint32 nHiddenLayers() const;
/**
* Merge all visible layers and discard hidden ones.
*/
void flatten();
/**
* Merge the specified layer with the layer
* below this layer, remove the specified layer.
*/
void mergeDown(KisLayerSP l, const KisMetaData::MergeStrategy* strategy);
/**
* flatten the layer: that is, the projection becomes the layer
* and all subnodes are removed. If this is not a paint layer, it will morph
* into a paint layer.
*/
void flattenLayer(KisLayerSP layer);
/**
* Merges layers in \p mergedLayers and creates a new layer above
* \p putAfter
*/
void mergeMultipleLayers(QList<KisNodeSP> mergedLayers, KisNodeSP putAfter);
/// @return the exact bounds of the image in pixel coordinates.
QRect bounds() const;
/**
* Returns the actual bounds of the image, taking LevelOfDetail
* into account. This value is used as a bounds() value of
* KisDefaultBounds object.
*/
QRect effectiveLodBounds() const;
/// use if the layers have changed _completely_ (eg. when flattening)
void notifyLayersChanged();
/**
* Sets the default color of the root layer projection. All the layers
* will be merged on top of this very color
*/
void setDefaultProjectionColor(const KoColor &color);
/**
* \see setDefaultProjectionColor()
*/
KoColor defaultProjectionColor() const;
void setRootLayer(KisGroupLayerSP rootLayer);
/**
* Add an annotation for this image. This can be anything: Gamma, EXIF, etc.
* Note that the "icc" annotation is reserved for the color strategies.
* If the annotation already exists, overwrite it with this one.
*/
void addAnnotation(KisAnnotationSP annotation);
/** get the annotation with the given type, can return 0 */
KisAnnotationSP annotation(const QString& type);
/** delete the annotation, if the image contains it */
void removeAnnotation(const QString& type);
/**
* Start of an iteration over the annotations of this image (including the ICC Profile)
*/
vKisAnnotationSP_it beginAnnotations();
/** end of an iteration over the annotations of this image */
vKisAnnotationSP_it endAnnotations();
/**
* Called before the image is delted and sends the sigAboutToBeDeleted signal
*/
void notifyAboutToBeDeleted();
KisImageSignalRouter* signalRouter();
/**
* Returns whether we can reselect current global selection
*
* \see reselectGlobalSelection()
*/
bool canReselectGlobalSelection();
/**
* Returns the layer compositions for the image
*/
QList<KisLayerCompositionSP> compositions();
/**
* Adds a new layer composition, will be saved with the image
*/
void addComposition(KisLayerCompositionSP composition);
/**
* Remove the layer compostion
*/
void removeComposition(KisLayerCompositionSP composition);
/**
* Permit or deny the wrap-around mode for all the paint devices
* of the image. Note that permitting the wraparound mode will not
* necessarily activate it right now. To be activated the wrap
* around mode should be 1) permitted; 2) supported by the
* currently running stroke.
*/
void setWrapAroundModePermitted(bool value);
/**
* \return whether the wrap-around mode is permitted for this
* image. If the wrap around mode is permitted and the
* currently running stroke supports it, the mode will be
* activated for all paint devices of the image.
*
* \see setWrapAroundMode
*/
bool wrapAroundModePermitted() const;
/**
* \return whether the wraparound mode is activated for all the
* devices of the image. The mode is activated when both
* factors are true: the user permitted it and the stroke
* supports it
*/
bool wrapAroundModeActive() const;
/**
* \return curent level of detail which is used when processing the image.
* Current working zoom = 2 ^ (- currentLevelOfDetail()). Default value is
* null, which means we work on the original image.
*/
int currentLevelOfDetail() const;
/**
* Notify KisImage which level of detail should be used in the
* lod-mode. Setting the mode does not guarantee the LOD to be
* used. It will be activated only when the stokes supports it.
*/
void setDesiredLevelOfDetail(int lod);
+ /**
+ * Relative position of the mirror axis center
+ * 0,0 - topleft corner of the image
+ * 1,1 - bottomright corner of the image
+ */
+ QPointF mirrorAxesCenter() const;
+
+ /**
+ * Sets the relative position of the axes center
+ * \see mirrorAxesCenter() for details
+ */
+ void setMirrorAxesCenter(const QPointF &value) const;
+
public Q_SLOTS:
/**
* Explicitly start regeneration of LoD planes of all the devices
* in the image. This call should be performed when the user is idle,
* just to make the quality of image updates better.
*/
void explicitRegenerateLevelOfDetail();
public:
/**
* Blocks usage of level of detail functionality. After this method
* has been called, no new strokes will use LoD.
*/
void setLevelOfDetailBlocked(bool value);
/**
* \see setLevelOfDetailBlocked()
*/
bool levelOfDetailBlocked() const;
/**
* Notifies that the node collapsed state has changed
*/
void notifyNodeCollpasedChanged();
KisImageAnimationInterface *animationInterface() const;
/**
* @brief setProofingConfiguration, this sets the image's proofing configuration, and signals
* the proofingConfiguration has changed.
* @param proofingConfig - the kis proofing config that will be used instead.
*/
void setProofingConfiguration(KisProofingConfigurationSP proofingConfig);
/**
* @brief proofingConfiguration
* @return the proofing configuration of the image.
*/
KisProofingConfigurationSP proofingConfiguration() const;
public:
bool startIsolatedMode(KisNodeSP node);
void stopIsolatedMode();
KisNodeSP isolatedModeRoot() const;
Q_SIGNALS:
/**
* Emitted whenever an action has caused the image to be
* recomposited.
*
* @param rc The rect that has been recomposited.
*/
void sigImageUpdated(const QRect &);
/**
Emitted whenever the image has been modified, so that it
doesn't match with the version saved on disk.
*/
void sigImageModified();
/**
* The signal is emitted when the size of the image is changed.
* \p oldStillPoint and \p newStillPoint give the receiver the
* hint about how the new and old rect of the image correspond to
* each other. They specify the point of the image around which
* the conversion was done. This point will stay still on the
* user's screen. That is the \p newStillPoint of the new image
* will be painted at the same screen position, where \p
* oldStillPoint of the old image was painted.
*
* \param oldStillPoint is a still point represented in *old*
* image coordinates
*
* \param newStillPoint is a still point represented in *new*
* image coordinates
*/
void sigSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint);
void sigProfileChanged(const KoColorProfile * profile);
void sigColorSpaceChanged(const KoColorSpace* cs);
void sigResolutionChanged(double xRes, double yRes);
void sigRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes);
/**
* Inform the model that a node was changed
*/
void sigNodeChanged(KisNodeSP node);
/**
* Inform that the image is going to be deleted
*/
void sigAboutToBeDeleted();
/**
* The signal is emitted right after a node has been connected
* to the graph of the nodes.
*
* WARNING: you must not request any graph-related information
* about the node being run in a not-scheduler thread. If you need
* information about the parent/siblings of the node connect
* with Qt::DirectConnection, get needed information and then
* emit another Qt::AutoConnection signal to pass this information
* to your thread. See details of the implementation
* in KisDummiesfacadeBase.
*/
void sigNodeAddedAsync(KisNodeSP node);
/**
* This signal is emitted right before a node is going to removed
* from the graph of the nodes.
*
* WARNING: you must not request any graph-related information
* about the node being run in a not-scheduler thread.
*
* \see comment in sigNodeAddedAsync()
*/
void sigRemoveNodeAsync(KisNodeSP node);
/**
* Emitted when the root node of the image has changed.
* It happens, e.g. when we flatten the image. When
* this happens the receiver should reload information
* about the image
*/
void sigLayersChangedAsync();
/**
* Emitted when the UI has requested the undo of the last stroke's
* operation. The point is, we cannot deal with the internals of
* the stroke without its creator knowing about it (which most
* probably cause a crash), so we just forward this request from
* the UI to the creator of the stroke.
*
* If your tool supports undoing part of its work, just listen to
* this signal and undo when it comes
*/
void sigUndoDuringStrokeRequested();
/**
* Emitted when the UI has requested the cancellation of
* the stroke. The point is, we cannot cancel the stroke
* without its creator knowing about it (which most probably
* cause a crash), so we just forward this request from the UI
* to the creator of the stroke.
*
* If your tool supports cancelling of its work in the middle
* of operation, just listen to this signal and cancel
* the stroke when it comes
*/
void sigStrokeCancellationRequested();
/**
* Emitted when the image decides that the stroke should better
* be ended. The point is, we cannot just end the stroke
* without its creator knowing about it (which most probably
* cause a crash), so we just forward this request from the UI
* to the creator of the stroke.
*
* If your tool supports long strokes that may involve multiple
* mouse actions in one stroke, just listen to this signal and
* end the stroke when it comes.
*/
void sigStrokeEndRequested();
/**
* Same as sigStrokeEndRequested() but is not emitted when the active node
* is changed.
*/
void sigStrokeEndRequestedActiveNodeFiltered();
/**
* Emitted when the isolated mode status has changed.
*
* Can be used by the receivers to catch a fact of forcefully
* stopping the isolated mode by the image when some complex
* action was requested
*/
void sigIsolatedModeChanged();
/**
* Emitted when one or more nodes changed the collapsed state
*
*/
void sigNodeCollapsedChanged();
/**
* Emitted when the proofing configuration of the image is being changed.
*
*/
void sigProofingConfigChanged();
public Q_SLOTS:
KisCompositeProgressProxy* compositeProgressProxy();
bool isIdle();
/**
* @brief barrierLock APIDOX
* @param readOnly
*/
void barrierLock(bool readOnly = false);
/**
* @brief barrierLock APIDOX
* @param readOnly
*/
bool tryBarrierLock(bool readOnly = false);
/**
* @brief barrierLock APIDOX
* @param readOnly
*/
void waitForDone();
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy);
void addJob(KisStrokeId id, KisStrokeJobData *data);
void endStroke(KisStrokeId id);
bool cancelStroke(KisStrokeId id);
/**
* @brief blockUpdates block updating the image projection
*/
void blockUpdates();
/**
* @brief unblockUpdates unblock updating the image project. This
* only restarts the scheduler and does not schedule a full refresh.
*/
void unblockUpdates();
/**
* Disables notification of the UI about the changes in the image.
* This feature is used by KisProcessingApplicator. It is needed
* when we change the size of the image. In this case, the whole
* image will be reloaded into UI by sigSizeChanged(), so there is
* no need to inform the UI about individual dirty rects.
*/
void disableUIUpdates();
/**
* \see disableUIUpdates
*/
void enableUIUpdates();
/**
* Disables the processing of all the setDirty() requests that
* come to the image. The incoming requests are effectively
* *dropped*.
*
* This feature is used by KisProcessingApplicator. For many cases
* it provides its own updates interface, which recalculates the
* whole subtree of nodes. But while we change any particular
* node, it can ask for an update itself. This method is a way of
* blocking such intermediate (and excessive) requests.
*
* NOTE: this is a convenience function for setProjectionUpdatesFilter()
* that installs a predefined filter that eats everything. Please
* note that these calls are *not* recursive
*/
void disableDirtyRequests();
/**
* \see disableDirtyRequests()
*/
void enableDirtyRequests();
/**
* Installs a filter object that will filter all the incoming projection update
* requests. If the filter return true, the incoming update is dropped.
*
* NOTE: you cannot set filters recursively!
*/
void setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter);
/**
* \see setProjectionUpdatesFilter()
*/
KisProjectionUpdatesFilterSP projectionUpdatesFilter() const;
- void refreshGraphAsync(KisNodeSP root = 0);
+ void refreshGraphAsync(KisNodeSP root = KisNodeSP());
void refreshGraphAsync(KisNodeSP root, const QRect &rc);
void refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect);
/**
* Triggers synchronous recomposition of the projection
*/
- void refreshGraph(KisNodeSP root = 0);
+ void refreshGraph(KisNodeSP root = KisNodeSP());
void refreshGraph(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void initialRefreshGraph();
/**
* Initiate a stack regeneration skipping the recalculation of the
* filthy node's projection.
*
* Works exactly as pseudoFilthy->setDirty() with the only
* exception that pseudoFilthy::updateProjection() will not be
* called. That is used by KisRecalculateTransformMaskJob to avoid
* cyclic dependencies.
*/
void requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect);
/**
* Adds a spontaneous job to the updates queue.
*
* A spontaneous job may do some trivial tasks in the background,
* like updating the outline of selection or purging unused tiles
* from the existing paint devices.
*/
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
/**
* This method is called by the UI (*not* by the creator of the
* stroke) when it thinks the current stroke should undo its last
* action, for example, when the user presses Ctrl+Z while some
* stroke is active.
*
* If the creator of the stroke supports undoing of intermediate
* actions, it will be notified about this request and can undo
* its last action.
*/
void requestUndoDuringStroke();
/**
* This method is called by the UI (*not* by the creator of the
* stroke) when it thinks current stroke should be cancelled. If
* there is a running stroke that has already been detached from
* its creator (ended or cancelled), it will be forcefully
* cancelled and reverted. If there is an open stroke present, and
* if its creator supports cancelling, it will be notified about
* the request and the stroke will be cancelled
*/
void requestStrokeCancellation();
+ /**
+ * This method requests the last stroke executed on the image to become undone.
+ * If the stroke is not ended, or if all the Lod0 strokes are completed, the method
+ * returns UNDO_FAIL. If the last Lod0 is going to be finished soon, then UNDO_WAIT
+ * is returned and the caller should just wait for its completion and call global undo
+ * instead. UNDO_OK means one unfinished stroke has been undone.
+ */
+ UndoResult tryUndoUnfinishedLod0Stroke();
+
/**
* This method is called when image or some other part of Krita
* (*not* the creator of the stroke) decides that the stroke
* should be ended. If the creator of the stroke supports it, it
* will be notified and the stroke will be cancelled
*/
void requestStrokeEnd();
/**
* Same as requestStrokeEnd() but is called by view manager when
* the current node is changed. Use to dintinguish
* sigStrokeEndRequested() and
* sigStrokeEndRequestedActiveNodeFiltered() which are used by
* KisNodeJugglerCompressed
*/
void requestStrokeEndActiveNode();
private:
- KisImage(const KisImage& rhs);
+ KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy);
KisImage& operator=(const KisImage& rhs);
void emitSizeChanged();
void resizeImageImpl(const QRect& newRect, bool cropLayers);
void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode,
bool resizeImage, double radians);
void shearImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode,
bool resizeImage, double angleX, double angleY,
const QPointF &origin);
void safeRemoveTwoNodes(KisNodeSP node1, KisNodeSP node2);
void refreshHiddenArea(KisNodeSP rootNode, const QRect &preparedArea);
void requestProjectionUpdateImpl(KisNode *node,
const QRect& rect,
const QRect &cropRect);
friend class KisImageResizeCommand;
void setSize(const QSize& size);
friend class KisImageSetProjectionColorSpaceCommand;
void setProjectionColorSpace(const KoColorSpace * colorSpace);
friend class KisDeselectGlobalSelectionCommand;
friend class KisReselectGlobalSelectionCommand;
friend class KisSetGlobalSelectionCommand;
friend class KisImageTest;
/**
* Replaces the current global selection with globalSelection. If
* \p globalSelection is empty, removes the selection object, so that
* \ref globalSelection() will return 0 after that.
*/
void setGlobalSelection(KisSelectionSP globalSelection);
/**
* Deselects current global selection.
* \ref globalSelection() will return 0 after that.
*/
void deselectGlobalSelection();
/**
* Reselects current deselected selection
*
* \see deselectGlobalSelection()
*/
void reselectGlobalSelection();
private:
class KisImagePrivate;
KisImagePrivate * m_d;
};
#endif // KIS_IMAGE_H_
diff --git a/libs/image/kis_image_animation_interface.cpp b/libs/image/kis_image_animation_interface.cpp
index 4223cdfe53..09cdc21f3e 100644
--- a/libs/image/kis_image_animation_interface.cpp
+++ b/libs/image/kis_image_animation_interface.cpp
@@ -1,335 +1,354 @@
/*
* 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_image_animation_interface.h"
#include "kis_global.h"
#include "kis_image.h"
#include "kis_regenerate_frame_stroke_strategy.h"
#include "kis_switch_time_stroke_strategy.h"
#include "kis_keyframe_channel.h"
#include "kis_time_range.h"
#include "kis_post_execution_undo_adapter.h"
#include "commands_new/kis_switch_current_time_command.h"
#include "kis_layer_utils.h"
struct KisImageAnimationInterface::Private
{
Private()
: image(0),
externalFrameActive(false),
frameInvalidationBlocked(false),
cachedLastFrameValue(-1),
m_currentTime(0),
m_currentUITime(0)
{
}
+ Private(const Private &rhs, KisImage *newImage)
+ : image(newImage),
+ externalFrameActive(false),
+ frameInvalidationBlocked(false),
+ fullClipRange(rhs.fullClipRange),
+ playbackRange(rhs.playbackRange),
+ framerate(rhs.framerate),
+ cachedLastFrameValue(-1),
+ m_currentTime(rhs.m_currentTime),
+ m_currentUITime(rhs.m_currentUITime)
+ {
+ }
+
KisImage *image;
bool externalFrameActive;
bool frameInvalidationBlocked;
KisTimeRange fullClipRange;
KisTimeRange playbackRange;
int framerate;
int cachedLastFrameValue;
KisSwitchTimeStrokeStrategy::SharedTokenWSP switchToken;
inline int currentTime() const {
return m_currentTime;
}
inline int currentUITime() const {
return m_currentUITime;
}
inline void setCurrentTime(int value) {
m_currentTime = value;
}
inline void setCurrentUITime(int value) {
m_currentUITime = value;
}
private:
int m_currentTime;
int m_currentUITime;
};
KisImageAnimationInterface::KisImageAnimationInterface(KisImage *image)
: m_d(new Private)
{
m_d->image = image;
m_d->framerate = 24;
m_d->fullClipRange = KisTimeRange::fromTime(0, 100);
connect(this, SIGNAL(sigInternalRequestTimeSwitch(int, bool)), SLOT(switchCurrentTimeAsync(int, bool)));
}
+KisImageAnimationInterface::KisImageAnimationInterface(const KisImageAnimationInterface &rhs, KisImage *newImage)
+ : m_d(new Private(*rhs.m_d, newImage))
+{
+ connect(this, SIGNAL(sigInternalRequestTimeSwitch(int, bool)), SLOT(switchCurrentTimeAsync(int, bool)));
+}
+
KisImageAnimationInterface::~KisImageAnimationInterface()
{
}
bool KisImageAnimationInterface::hasAnimation() const
{
bool hasAnimation = false;
KisLayerUtils::recursiveApplyNodes(
m_d->image->root(),
[&hasAnimation](KisNodeSP node) {
hasAnimation |= node->isAnimated();
});
return hasAnimation;
}
int KisImageAnimationInterface::currentTime() const
{
return m_d->currentTime();
}
int KisImageAnimationInterface::currentUITime() const
{
return m_d->currentUITime();
}
const KisTimeRange& KisImageAnimationInterface::fullClipRange() const
{
return m_d->fullClipRange;
}
void KisImageAnimationInterface::setFullClipRange(const KisTimeRange range) {
m_d->fullClipRange = range;
emit sigFullClipRangeChanged();
}
const KisTimeRange& KisImageAnimationInterface::playbackRange() const
{
return m_d->playbackRange.isValid() ? m_d->playbackRange : m_d->fullClipRange;
}
void KisImageAnimationInterface::setPlaybackRange(const KisTimeRange range)
{
m_d->playbackRange = range;
emit sigPlaybackRangeChanged();
}
int KisImageAnimationInterface::framerate() const
{
return m_d->framerate;
}
void KisImageAnimationInterface::setFramerate(int fps)
{
m_d->framerate = fps;
emit sigFramerateChanged();
}
KisImageWSP KisImageAnimationInterface::image() const
{
return m_d->image;
}
bool KisImageAnimationInterface::externalFrameActive() const
{
return m_d->externalFrameActive;
}
void KisImageAnimationInterface::requestTimeSwitchWithUndo(int time)
{
if (currentUITime() == time) return;
requestTimeSwitchNonGUI(time, true);
}
void KisImageAnimationInterface::setDefaultProjectionColor(const KoColor &color)
{
int savedTime = 0;
saveAndResetCurrentTime(currentTime(), &savedTime);
m_d->image->setDefaultProjectionColor(color);
restoreCurrentTime(&savedTime);
}
void KisImageAnimationInterface::requestTimeSwitchNonGUI(int time, bool useUndo)
{
emit sigInternalRequestTimeSwitch(time, useUndo);
}
void KisImageAnimationInterface::explicitlySetCurrentTime(int frameId)
{
m_d->setCurrentTime(frameId);
}
void KisImageAnimationInterface::switchCurrentTimeAsync(int frameId, bool useUndo)
{
if (currentUITime() == frameId) return;
KisTimeRange range = KisTimeRange::infinite(0);
KisTimeRange::calculateTimeRangeRecursive(m_d->image->root(), currentUITime(), range, true);
const bool needsRegeneration = !range.contains(frameId);
KisSwitchTimeStrokeStrategy::SharedTokenSP token =
m_d->switchToken.toStrongRef();
if (!token || !token->tryResetDestinationTime(frameId, needsRegeneration)) {
{
KisPostExecutionUndoAdapter *undoAdapter = useUndo ?
m_d->image->postExecutionUndoAdapter() : 0;
KisSwitchTimeStrokeStrategy *strategy =
new KisSwitchTimeStrokeStrategy(frameId, needsRegeneration,
this, undoAdapter);
m_d->switchToken = strategy->token();
KisStrokeId stroke = m_d->image->startStroke(strategy);
m_d->image->endStroke(stroke);
}
if (needsRegeneration) {
KisStrokeStrategy *strategy =
new KisRegenerateFrameStrokeStrategy(this);
KisStrokeId strokeId = m_d->image->startStroke(strategy);
m_d->image->endStroke(strokeId);
}
}
m_d->setCurrentUITime(frameId);
emit sigUiTimeChanged(frameId);
}
void KisImageAnimationInterface::requestFrameRegeneration(int frameId, const QRegion &dirtyRegion)
{
KisStrokeStrategy *strategy =
new KisRegenerateFrameStrokeStrategy(frameId,
dirtyRegion,
this);
QList<KisStrokeJobData*> jobs = KisRegenerateFrameStrokeStrategy::createJobsData(m_d->image);
KisStrokeId stroke = m_d->image->startStroke(strategy);
Q_FOREACH (KisStrokeJobData* job, jobs) {
m_d->image->addJob(stroke, job);
}
m_d->image->endStroke(stroke);
}
void KisImageAnimationInterface::saveAndResetCurrentTime(int frameId, int *savedValue)
{
m_d->externalFrameActive = true;
*savedValue = m_d->currentTime();
m_d->setCurrentTime(frameId);
}
void KisImageAnimationInterface::restoreCurrentTime(int *savedValue)
{
m_d->setCurrentTime(*savedValue);
m_d->externalFrameActive = false;
}
void KisImageAnimationInterface::notifyFrameReady()
{
emit sigFrameReady(m_d->currentTime());
}
void KisImageAnimationInterface::notifyFrameCancelled()
{
emit sigFrameCancelled();
}
KisUpdatesFacade* KisImageAnimationInterface::updatesFacade() const
{
return m_d->image;
}
void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node,
const QRect &rect,
bool recursive)
{
if (externalFrameActive() || m_d->frameInvalidationBlocked) return;
if (node->inherits("KisSelectionMask")) return;
KisKeyframeChannel *channel =
node->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (recursive) {
KisTimeRange affectedRange;
KisTimeRange::calculateTimeRangeRecursive(node, currentTime(), affectedRange, false);
invalidateFrames(affectedRange, rect);
} else if (channel) {
const int currentTime = m_d->currentTime();
invalidateFrames(channel->affectedFrames(currentTime), rect);
} else {
invalidateFrames(KisTimeRange::infinite(0), rect);
}
}
void KisImageAnimationInterface::invalidateFrames(const KisTimeRange &range, const QRect &rect)
{
m_d->cachedLastFrameValue = -1;
emit sigFramesChanged(range, rect);
}
void KisImageAnimationInterface::blockFrameInvalidation(bool value)
{
m_d->frameInvalidationBlocked = value;
}
int findLastKeyframeTimeRecursive(KisNodeSP node)
{
int time = 0;
KisKeyframeChannel *channel;
Q_FOREACH (channel, node->keyframeChannels()) {
KisKeyframeSP keyframe = channel->lastKeyframe();
if (keyframe) {
time = std::max(time, keyframe->time());
}
}
KisNodeSP child = node->firstChild();
while (child) {
time = std::max(time, findLastKeyframeTimeRecursive(child));
child = child->nextSibling();
}
return time;
}
int KisImageAnimationInterface::totalLength()
{
if (m_d->cachedLastFrameValue < 0) {
m_d->cachedLastFrameValue = findLastKeyframeTimeRecursive(m_d->image->root());
}
int lastKey = m_d->cachedLastFrameValue;
lastKey = std::max(lastKey, m_d->fullClipRange.end());
lastKey = std::max(lastKey, m_d->currentUITime());
return lastKey + 1;
}
diff --git a/libs/image/kis_image_animation_interface.h b/libs/image/kis_image_animation_interface.h
index c077d872c7..b596c69292 100644
--- a/libs/image/kis_image_animation_interface.h
+++ b/libs/image/kis_image_animation_interface.h
@@ -1,165 +1,166 @@
/*
* 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_IMAGE_ANIMATION_INTERFACE_H
#define __KIS_IMAGE_ANIMATION_INTERFACE_H
#include <QObject>
#include <QScopedPointer>
#include "kis_types.h"
#include "kritaimage_export.h"
class KisUpdatesFacade;
class KisTimeRange;
class KoColor;
namespace KisLayerUtils {
struct SwitchFrameCommand;
}
class KRITAIMAGE_EXPORT KisImageAnimationInterface : public QObject
{
Q_OBJECT
public:
KisImageAnimationInterface(KisImage *image);
+ KisImageAnimationInterface(const KisImageAnimationInterface &rhs, KisImage *newImage);
~KisImageAnimationInterface();
/**
* Returns true of the image has at least one animated layer
*/
bool hasAnimation() const;
/**
* Returns currently active frame of the underlying image. Some strokes
* can override this value and it will report a different value.
*/
int currentTime() const;
/**
* Same as currentTime, except it isn't changed when background strokes
* are running.
*/
int currentUITime() const;
/**
* While any non-current frame is being regenerated by the
* strategy, the image is kept in a special state, named
* 'externalFrameActive'. Is this state the following applies:
*
* 1) All the animated paint devices switch its state into
* frameId() defined by global time.
*
* 2) All animation-not-capable devices switch to a temporary
* content device, which *is in undefined state*. The stroke
* should regenerate the image projection manually.
*/
bool externalFrameActive() const;
void requestTimeSwitchWithUndo(int time);
void requestTimeSwitchNonGUI(int time, bool useUndo = false);
public Q_SLOTS:
/**
* Switches current frame (synchronously) and starts an
* asynchronous regeneration of the entire image.
*/
void switchCurrentTimeAsync(int frameId, bool useUndo = false);
public:
/**
* Start a backgroud thread that will recalculate some extra frame.
* The result will be reported using two types of signals:
*
* 1) KisImage::sigImageUpdated() will be emitted for every chunk
* of updated area.
*
* 2) sigFrameReady() will be emitted in the end of the operation.
* IMPORTANT: to get the result you must connect to this signal
* with Qt::DirectConnection and fetch the result from
* frameProjection(). After the signal handler is exited, the
* data will no longer be available.
*/
void requestFrameRegeneration(int frameId, const QRegion &dirtyRegion);
void notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive);
void invalidateFrames(const KisTimeRange &range, const QRect &rect);
/**
* Changes the default color of the "external frame" projection of
* the image's root layer. Please note that this command should be
* executed from a context of an exclusive job!
*/
void setDefaultProjectionColor(const KoColor &color);
/**
* The current time range selected by user.
* @return current time range
*/
const KisTimeRange& fullClipRange() const;
void setFullClipRange(const KisTimeRange range);
const KisTimeRange &playbackRange() const;
void setPlaybackRange(const KisTimeRange range);
int framerate() const;
public Q_SLOTS:
void setFramerate(int fps);
public:
KisImageWSP image() const;
int totalLength();
private:
// interface for:
friend class KisRegenerateFrameStrokeStrategy;
friend class KisAnimationFrameCacheTest;
friend struct KisLayerUtils::SwitchFrameCommand;
friend class KisImageTest;
void saveAndResetCurrentTime(int frameId, int *savedValue);
void restoreCurrentTime(int *savedValue);
void notifyFrameReady();
void notifyFrameCancelled();
KisUpdatesFacade* updatesFacade() const;
void blockFrameInvalidation(bool value);
friend class KisSwitchTimeStrokeStrategy;
void explicitlySetCurrentTime(int frameId);
Q_SIGNALS:
void sigFrameReady(int time);
void sigFrameCancelled();
void sigUiTimeChanged(int newTime);
void sigFramesChanged(const KisTimeRange &range, const QRect &rect);
void sigInternalRequestTimeSwitch(int frameId, bool useUndo);
void sigFramerateChanged();
void sigFullClipRangeChanged();
void sigPlaybackRangeChanged();
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_IMAGE_ANIMATION_INTERFACE_H */
diff --git a/libs/image/kis_image_config.cpp b/libs/image/kis_image_config.cpp
index 4d37b44bb4..c99542ebda 100644
--- a/libs/image/kis_image_config.cpp
+++ b/libs/image/kis_image_config.cpp
@@ -1,455 +1,455 @@
/*
* 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 <KoConfig.h>
#include <KoColorProfile.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorConversionTransformation.h>
#include "kis_debug.h"
#include <QThread>
#include <QApplication>
#include <QColor>
#include <QDir>
#include "kis_global.h"
#include <cmath>
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
#include <errno.h>
#endif
KisImageConfig::KisImageConfig(bool readOnly)
: m_config( KSharedConfig::openConfig()->group(QString())),
m_readOnly(readOnly)
{
}
KisImageConfig::~KisImageConfig()
{
if (m_readOnly) return;
if (qApp->thread() != QThread::currentThread()) {
dbgKrita << "KisImageConfig: requested config synchronization from nonGUI thread! Called from" << kisBacktrace();
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 = QDir::tempPath();
return !requestDefault ?
m_config.readEntry("swaplocation", swap) : swap;
}
void KisImageConfig::setSwapDir(const QString &swapDir)
{
m_config.writeEntry("swaplocation", swapDir);
}
int KisImageConfig::numberOfOnionSkins() const
{
return m_config.readEntry("numberOfOnionSkins", 10);
}
void KisImageConfig::setNumberOfOnionSkins(int value)
{
m_config.writeEntry("numberOfOnionSkins", value);
}
int KisImageConfig::onionSkinTintFactor() const
{
return m_config.readEntry("onionSkinTintFactor", 192);
}
void KisImageConfig::setOnionSkinTintFactor(int value)
{
m_config.writeEntry("onionSkinTintFactor", value);
}
int KisImageConfig::onionSkinOpacity(int offset) const
{
int value = m_config.readEntry("onionSkinOpacity_" + QString::number(offset), -1);
if (value < 0) {
const int num = numberOfOnionSkins();
const qreal dx = qreal(qAbs(offset)) / num;
value = 0.7 * exp(-pow2(dx) / 0.5) * 255;
}
return value;
}
void KisImageConfig::setOnionSkinOpacity(int offset, int value)
{
m_config.writeEntry("onionSkinOpacity_" + QString::number(offset), value);
}
bool KisImageConfig::onionSkinState(int offset) const
{
bool enableByDefault = (qAbs(offset) <= 2);
return m_config.readEntry("onionSkinState_" + QString::number(offset), enableByDefault);
}
void KisImageConfig::setOnionSkinState(int offset, bool value)
{
m_config.writeEntry("onionSkinState_" + QString::number(offset), value);
}
QColor KisImageConfig::onionSkinTintColorBackward() const
{
return m_config.readEntry("onionSkinTintColorBackward", QColor(Qt::red));
}
void KisImageConfig::setOnionSkinTintColorBackward(const QColor &value)
{
m_config.writeEntry("onionSkinTintColorBackward", value);
}
QColor KisImageConfig::onionSkinTintColorForward() const
{
return m_config.readEntry("oninSkinTintColorForward", QColor(Qt::green));
}
void KisImageConfig::setOnionSkinTintColorForward(const QColor &value)
{
m_config.writeEntry("oninSkinTintColorForward", value);
}
bool KisImageConfig::lazyFrameCreationEnabled(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("lazyFrameCreationEnabled", true) : true;
}
void KisImageConfig::setLazyFrameCreationEnabled(bool value)
{
m_config.writeEntry("lazyFrameCreationEnabled", value);
}
#if defined Q_OS_LINUX
#include <sys/sysinfo.h>
#elif defined Q_OS_FREEBSD || defined Q_OS_NETBSD || defined Q_OS_OPENBSD
#include <sys/sysctl.h>
#elif defined Q_OS_WIN
#include <windows.h>
-#elif defined Q_OS_MAC
+#elif defined Q_OS_OSX
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#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 || defined Q_OS_NETBSD || defined Q_OS_OPENBSD
u_long physmem;
# if defined HW_PHYSMEM64 // NetBSD only
int mib[] = {CTL_HW, HW_PHYSMEM64};
# else
int mib[] = {CTL_HW, HW_PHYSMEM};
# endif
size_t len = sizeof(physmem);
error = sysctl(mib, 2, &physmem, &len, 0, 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_MAC
+#elif defined Q_OS_OSX
int mib[2] = { CTL_HW, HW_MEMSIZE };
u_int namelen = sizeof(mib) / sizeof(mib[0]);
uint64_t size;
size_t len = sizeof(size);
errno = 0;
if (sysctl(mib, namelen, &size, &len, 0, 0) >= 0) {
totalMemory = size >> 20;
error = 0;
}
else {
dbgKrita << "sysctl(\"hw.memsize\") raised error" << strerror(errno);
}
#endif
if (error) {
warnKrita << "Cannot get the size of your RAM. Using 1 GiB by default.";
}
return totalMemory;
}
bool KisImageConfig::showAdditionalOnionSkinsSettings(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("showAdditionalOnionSkinsSettings", true) : true;
}
void KisImageConfig::setShowAdditionalOnionSkinsSettings(bool value)
{
m_config.writeEntry("showAdditionalOnionSkinsSettings", value);
}
int KisImageConfig::defaultFrameColorLabel() const
{
return m_config.readEntry("defaultFrameColorLabel", 0);
}
void KisImageConfig::setDefaultFrameColorLabel(int label)
{
m_config.writeEntry("defaultFrameColorLabel", label);
}
KisProofingConfigurationSP KisImageConfig::defaultProofingconfiguration()
{
KisProofingConfiguration *proofingConfig= new KisProofingConfiguration();
proofingConfig->proofingProfile = m_config.readEntry("defaultProofingProfileName", "Chemical proof");
proofingConfig->proofingModel = m_config.readEntry("defaultProofingProfileModel", "CMYKA");
proofingConfig->proofingDepth = m_config.readEntry("defaultProofingProfileDepth", "U8");
proofingConfig->intent = (KoColorConversionTransformation::Intent)m_config.readEntry("defaultProofingProfileIntent", 3);
if (m_config.readEntry("defaultProofingBlackpointCompensation", true)) {
proofingConfig->conversionFlags |= KoColorConversionTransformation::ConversionFlag::BlackpointCompensation;
} else {
proofingConfig->conversionFlags = proofingConfig->conversionFlags & ~KoColorConversionTransformation::ConversionFlag::BlackpointCompensation;
}
QColor def;
def = m_config.readEntry("defaultProofingGamutwarning", QColor(Qt::gray));
KoColor col(KoColorSpaceRegistry::instance()->rgb8());
col.fromQColor(def);
col.setOpacity(1.0);
proofingConfig->warningColor = col;
proofingConfig->adaptationState = (double)m_config.readEntry("defaultProofingAdaptationState", 1.0);
return toQShared(proofingConfig);
}
void KisImageConfig::setDefaultProofingConfig(const KoColorSpace *proofingSpace, int proofingIntent, bool blackPointCompensation, KoColor warningColor, double adaptationState)
{
m_config.writeEntry("defaultProofingProfileName", proofingSpace->profile()->name());
m_config.writeEntry("defaultProofingProfileModel", proofingSpace->colorModelId().id());
m_config.writeEntry("defaultProofingProfileDepth", proofingSpace->colorDepthId().id());
m_config.writeEntry("defaultProofingProfileIntent", proofingIntent);
m_config.writeEntry("defaultProofingBlackpointCompensation", blackPointCompensation);
QColor c;
warningColor.toQColor(&c);
m_config.writeEntry("defaultProofingGamutwarning", c);
m_config.writeEntry("defaultProofingAdaptationState",adaptationState);
}
bool KisImageConfig::useLodForColorizeMask(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("useLodForColorizeMask", false) : false;
}
void KisImageConfig::setUseLodForColorizeMask(bool value)
{
m_config.writeEntry("useLodForColorizeMask", value);
}
diff --git a/libs/image/kis_image_interfaces.cpp b/libs/image/kis_image_interfaces.cpp
index cb45b2dfde..0511ad718a 100644
--- a/libs/image/kis_image_interfaces.cpp
+++ b/libs/image/kis_image_interfaces.cpp
@@ -1,31 +1,35 @@
/*
* 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_image_interfaces.h"
KisStrokesFacade::~KisStrokesFacade()
{
}
KisUpdatesFacade::~KisUpdatesFacade()
{
}
KisProjectionUpdateListener::~KisProjectionUpdateListener()
{
}
+
+KisStrokeUndoFacade::~KisStrokeUndoFacade()
+{
+}
diff --git a/libs/image/kis_image_interfaces.h b/libs/image/kis_image_interfaces.h
index 8dae5a3a4f..5635a88da7 100644
--- a/libs/image/kis_image_interfaces.h
+++ b/libs/image/kis_image_interfaces.h
@@ -1,69 +1,77 @@
/*
* 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_IMAGE_INTERFACES_H
#define __KIS_IMAGE_INTERFACES_H
#include "kis_types.h"
#include <kritaimage_export.h>
class QRect;
class KisStrokeStrategy;
class KisStrokeJobData;
+class KisPostExecutionUndoAdapter;
class KRITAIMAGE_EXPORT KisStrokesFacade
{
public:
virtual ~KisStrokesFacade();
virtual KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) = 0;
virtual void addJob(KisStrokeId id, KisStrokeJobData *data) = 0;
virtual void endStroke(KisStrokeId id) = 0;
virtual bool cancelStroke(KisStrokeId id) = 0;
};
class KRITAIMAGE_EXPORT KisUpdatesFacade
{
public:
virtual ~KisUpdatesFacade();
virtual void blockUpdates() = 0;
virtual void unblockUpdates() = 0;
virtual void disableUIUpdates() = 0;
virtual void enableUIUpdates() = 0;
virtual void disableDirtyRequests() = 0;
virtual void enableDirtyRequests() = 0;
- virtual void refreshGraphAsync(KisNodeSP root = 0) = 0;
+ virtual void refreshGraphAsync(KisNodeSP root) = 0;
virtual void refreshGraphAsync(KisNodeSP root, const QRect &rc) = 0;
virtual void refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect) = 0;
virtual void setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter) = 0;
virtual KisProjectionUpdatesFilterSP projectionUpdatesFilter() const = 0;
};
class KRITAIMAGE_EXPORT KisProjectionUpdateListener
{
public:
virtual ~KisProjectionUpdateListener();
virtual void notifyProjectionUpdated(const QRect &rc) = 0;
};
+class KRITAIMAGE_EXPORT KisStrokeUndoFacade
+{
+public:
+ virtual ~KisStrokeUndoFacade();
+ virtual KisPostExecutionUndoAdapter* postExecutionUndoAdapter() const = 0;
+};
+
#endif /* __KIS_IMAGE_INTERFACES_H */
diff --git a/libs/image/kis_image_signal_router.cpp b/libs/image/kis_image_signal_router.cpp
index 4f44093850..b2a857ac9f 100644
--- a/libs/image/kis_image_signal_router.cpp
+++ b/libs/image/kis_image_signal_router.cpp
@@ -1,146 +1,157 @@
/*
* 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_image_signal_router.h"
#include <QThread>
#include "kis_image.h"
#define CONNECT_TO_IMAGE(signal) \
connect(this, SIGNAL(signal), m_image, SIGNAL(signal), Qt::DirectConnection)
struct ImageSignalsStaticRegistrar {
ImageSignalsStaticRegistrar() {
qRegisterMetaType<KisImageSignalType>("KisImageSignalType");
}
};
static ImageSignalsStaticRegistrar __registrar;
KisImageSignalRouter::KisImageSignalRouter(KisImageWSP image)
: m_image(image)
{
connect(this, SIGNAL(sigNotification(KisImageSignalType)),
SLOT(slotNotification(KisImageSignalType)));
CONNECT_TO_IMAGE(sigImageModified());
CONNECT_TO_IMAGE(sigSizeChanged(const QPointF&, const QPointF&));
CONNECT_TO_IMAGE(sigProfileChanged(const KoColorProfile*));
CONNECT_TO_IMAGE(sigColorSpaceChanged(const KoColorSpace*));
CONNECT_TO_IMAGE(sigResolutionChanged(double, double));
CONNECT_TO_IMAGE(sigRequestNodeReselection(KisNodeSP, const KisNodeList&));
CONNECT_TO_IMAGE(sigNodeChanged(KisNodeSP));
CONNECT_TO_IMAGE(sigNodeAddedAsync(KisNodeSP));
CONNECT_TO_IMAGE(sigRemoveNodeAsync(KisNodeSP));
CONNECT_TO_IMAGE(sigLayersChangedAsync());
}
KisImageSignalRouter::~KisImageSignalRouter()
{
}
void KisImageSignalRouter::emitNotifications(KisImageSignalVector notifications)
{
Q_FOREACH (const KisImageSignalType &type, notifications) {
emitNotification(type);
}
}
void KisImageSignalRouter::emitNotification(KisImageSignalType type)
{
/**
* All the notifications except LayersChangedSignal should go in a
* queued way. And LayersChangedSignal should be delivered to the
* recipients in a non-reordered way
*/
if (type.id == LayersChangedSignal) {
slotNotification(type);
} else {
emit sigNotification(type);
}
}
void KisImageSignalRouter::emitNodeChanged(KisNodeSP node)
{
emit sigNodeChanged(node);
}
void KisImageSignalRouter::emitNodeHasBeenAdded(KisNode *parent, int index)
{
KisNodeSP newNode = parent->at(index);
if (!newNode->inherits("KisSelectionMask")) {
- m_image->invalidateAllFrames();
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ image->invalidateAllFrames();
+ }
}
emit sigNodeAddedAsync(newNode);
}
void KisImageSignalRouter::emitAboutToRemoveANode(KisNode *parent, int index)
{
KisNodeSP removedNode = parent->at(index);
if (!removedNode->inherits("KisSelectionMask")) {
- m_image->invalidateAllFrames();
+ KisImageSP image = m_image.toStrongRef();
+ if (image) {
+ image->invalidateAllFrames();
+ }
}
emit sigRemoveNodeAsync(removedNode);
}
void KisImageSignalRouter::slotNotification(KisImageSignalType type)
{
+ KisImageSP image = m_image.toStrongRef();
+ if (!image) {
+ return;
+ }
+
switch(type.id) {
case LayersChangedSignal:
- m_image->invalidateAllFrames();
+ image->invalidateAllFrames();
emit sigLayersChangedAsync();
break;
case ModifiedSignal:
emit sigImageModified();
break;
case SizeChangedSignal:
- m_image->invalidateAllFrames();
+ image->invalidateAllFrames();
emit sigSizeChanged(type.sizeChangedSignal.oldStillPoint,
type.sizeChangedSignal.newStillPoint);
break;
case ProfileChangedSignal:
- m_image->invalidateAllFrames();
- emit sigProfileChanged(m_image->profile());
+ image->invalidateAllFrames();
+ emit sigProfileChanged(image->profile());
break;
case ColorSpaceChangedSignal:
- m_image->invalidateAllFrames();
- emit sigColorSpaceChanged(m_image->colorSpace());
+ image->invalidateAllFrames();
+ emit sigColorSpaceChanged(image->colorSpace());
break;
case ResolutionChangedSignal:
- m_image->invalidateAllFrames();
- emit sigResolutionChanged(m_image->xRes(), m_image->yRes());
+ image->invalidateAllFrames();
+ emit sigResolutionChanged(image->xRes(), image->yRes());
break;
case NodeReselectionRequestSignal:
if (type.nodeReselectionSignal.newActiveNode ||
!type.nodeReselectionSignal.newSelectedNodes.isEmpty()) {
emit sigRequestNodeReselection(type.nodeReselectionSignal.newActiveNode,
type.nodeReselectionSignal.newSelectedNodes);
}
break;
}
}
diff --git a/libs/image/kis_layer.cc b/libs/image/kis_layer.cc
index 71025049b0..8c5b679fa0 100644
--- a/libs/image/kis_layer.cc
+++ b/libs/image/kis_layer.cc
@@ -1,859 +1,874 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* 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_layer.h"
#include <klocalizedstring.h>
#include <QImage>
#include <QBitArray>
#include <QStack>
#include <QMutex>
#include <QMutexLocker>
#include <KoIcon.h>
#include <kis_icon.h>
#include <KoProperties.h>
#include <KoCompositeOpRegistry.h>
#include <KoColorSpace.h>
#include "kis_debug.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_mask.h"
#include "kis_effect_mask.h"
#include "kis_selection_mask.h"
#include "kis_meta_data_store.h"
#include "kis_selection.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_psd_layer_style.h"
#include "kis_layer_projection_plane.h"
#include "layerstyles/kis_layer_style_projection_plane.h"
#include "krita_utils.h"
#include "kis_layer_properties_icons.h"
#include "kis_layer_utils.h"
class KisSafeProjection {
public:
KisPaintDeviceSP getDeviceLazy(KisPaintDeviceSP prototype) {
QMutexLocker locker(&m_lock);
if (!m_reusablePaintDevice) {
m_reusablePaintDevice = new KisPaintDevice(*prototype);
}
if(!m_projection ||
*m_projection->colorSpace() != *prototype->colorSpace()) {
m_projection = m_reusablePaintDevice;
m_projection->makeCloneFromRough(prototype, prototype->extent());
m_projection->setProjectionDevice(true);
}
return m_projection;
}
void freeDevice() {
QMutexLocker locker(&m_lock);
m_projection = 0;
if(m_reusablePaintDevice) {
m_reusablePaintDevice->clear();
}
}
private:
QMutex m_lock;
KisPaintDeviceSP m_projection;
KisPaintDeviceSP m_reusablePaintDevice;
};
class KisCloneLayersList {
public:
void addClone(KisCloneLayerWSP cloneLayer) {
m_clonesList.append(cloneLayer);
}
void removeClone(KisCloneLayerWSP cloneLayer) {
m_clonesList.removeOne(cloneLayer);
}
void setDirty(const QRect &rect) {
- Q_FOREACH (KisCloneLayerWSP clone, m_clonesList) {
- clone->setDirtyOriginal(rect);
+ Q_FOREACH (KisCloneLayerSP clone, m_clonesList) {
+ if (clone) {
+ clone->setDirtyOriginal(rect);
+ }
}
}
const QList<KisCloneLayerWSP> registeredClones() const {
return m_clonesList;
}
bool hasClones() const {
return !m_clonesList.isEmpty();
}
private:
QList<KisCloneLayerWSP> m_clonesList;
};
struct Q_DECL_HIDDEN KisLayer::Private
{
KisImageWSP image;
QBitArray channelFlags;
KisMetaData::Store* metaDataStore;
KisSafeProjection safeProjection;
KisCloneLayersList clonesList;
KisPSDLayerStyleSP layerStyle;
KisAbstractProjectionPlaneSP layerStyleProjectionPlane;
KisAbstractProjectionPlaneSP projectionPlane;
};
KisLayer::KisLayer(KisImageWSP image, const QString &name, quint8 opacity)
: KisNode()
, m_d(new Private)
{
setName(name);
setOpacity(opacity);
m_d->image = image;
m_d->metaDataStore = new KisMetaData::Store();
m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this));
}
KisLayer::KisLayer(const KisLayer& rhs)
: KisNode(rhs)
, m_d(new Private())
{
if (this != &rhs) {
m_d->image = rhs.m_d->image;
m_d->metaDataStore = new KisMetaData::Store(*rhs.m_d->metaDataStore);
m_d->channelFlags = rhs.m_d->channelFlags;
setName(rhs.name());
m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this));
if (rhs.m_d->layerStyle) {
setLayerStyle(rhs.m_d->layerStyle->clone());
}
}
}
KisLayer::~KisLayer()
{
delete m_d->metaDataStore;
delete m_d;
}
const KoColorSpace * KisLayer::colorSpace() const
{
- if (m_d->image)
- return m_d->image->colorSpace();
- return 0;
+ KisImageSP image = m_d->image.toStrongRef();
+ if (!image) {
+ return nullptr;
+ }
+ return image->colorSpace();
}
const KoCompositeOp * KisLayer::compositeOp() const
{
/**
* FIXME: This function duplicates the same function from
* KisMask. We can't move it to KisBaseNode as it doesn't
* know anything about parent() method of KisNode
* Please think it over...
*/
KisNodeSP parentNode = parent();
if (!parentNode) return 0;
if (!parentNode->colorSpace()) return 0;
const KoCompositeOp* op = parentNode->colorSpace()->compositeOp(compositeOpId());
return op ? op : parentNode->colorSpace()->compositeOp(COMPOSITE_OVER);
}
KisPSDLayerStyleSP KisLayer::layerStyle() const
{
return m_d->layerStyle;
}
void KisLayer::setLayerStyle(KisPSDLayerStyleSP layerStyle)
{
if (layerStyle) {
m_d->layerStyle = layerStyle;
KisAbstractProjectionPlaneSP plane = !layerStyle->isEmpty() ?
KisAbstractProjectionPlaneSP(new KisLayerStyleProjectionPlane(this)) :
KisAbstractProjectionPlaneSP(0);
m_d->layerStyleProjectionPlane = plane;
} else {
m_d->layerStyleProjectionPlane.clear();
m_d->layerStyle.clear();
}
}
KisBaseNode::PropertyList KisLayer::sectionModelProperties() const
{
KisBaseNode::PropertyList l = KisBaseNode::sectionModelProperties();
l << KisBaseNode::Property(KoID("opacity", i18n("Opacity")), i18n("%1%", percentOpacity()));
if (compositeOp()) {
l << KisBaseNode::Property(KoID("compositeop", i18n("Composite Mode")), compositeOp()->description());
}
if (m_d->layerStyle && !m_d->layerStyle->isEmpty()) {
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::layerStyle, m_d->layerStyle->isEnabled());
}
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::inheritAlpha, alphaChannelDisabled());
return l;
}
void KisLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
{
KisBaseNode::setSectionModelProperties(properties);
Q_FOREACH (const KisBaseNode::Property &property, properties) {
if (property.id == KisLayerPropertiesIcons::inheritAlpha.id()) {
disableAlphaChannel(property.state.toBool());
}
if (property.id == KisLayerPropertiesIcons::layerStyle.id()) {
if (m_d->layerStyle &&
m_d->layerStyle->isEnabled() != property.state.toBool()) {
m_d->layerStyle->setEnabled(property.state.toBool());
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
}
}
}
void KisLayer::disableAlphaChannel(bool disable)
{
QBitArray newChannelFlags = m_d->channelFlags;
if(newChannelFlags.isEmpty())
newChannelFlags = colorSpace()->channelFlags(true, true);
if(disable)
newChannelFlags &= colorSpace()->channelFlags(true, false);
else
newChannelFlags |= colorSpace()->channelFlags(false, true);
setChannelFlags(newChannelFlags);
}
bool KisLayer::alphaChannelDisabled() const
{
QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->channelFlags;
return flags.count(true) == 0 && !m_d->channelFlags.isEmpty();
}
void KisLayer::setChannelFlags(const QBitArray & channelFlags)
{
Q_ASSERT(channelFlags.isEmpty() ||((quint32)channelFlags.count() == colorSpace()->channelCount()));
if (KritaUtils::compareChannelFlags(channelFlags,
this->channelFlags())) {
return;
}
if (!channelFlags.isEmpty() &&
channelFlags == QBitArray(channelFlags.size(), true)) {
m_d->channelFlags.clear();
} else {
m_d->channelFlags = channelFlags;
}
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
QBitArray & KisLayer::channelFlags() const
{
return m_d->channelFlags;
}
bool KisLayer::temporary() const
{
return nodeProperties().boolProperty("temporary", false);
}
void KisLayer::setTemporary(bool t)
{
nodeProperties().setProperty("temporary", t);
}
KisImageWSP KisLayer::image() const
{
return m_d->image;
}
void KisLayer::setImage(KisImageWSP image)
{
m_d->image = image;
KisNodeSP node = firstChild();
while (node) {
KisLayerUtils::recursiveApplyNodes(node,
[image] (KisNodeSP node) {
node->setImage(image);
});
node = node->nextSibling();
}
}
bool KisLayer::canMergeAndKeepBlendOptions(KisLayerSP otherLayer)
{
return
this->compositeOpId() == otherLayer->compositeOpId() &&
this->opacity() == otherLayer->opacity() &&
this->channelFlags() == otherLayer->channelFlags() &&
!this->layerStyle() && !otherLayer->layerStyle() &&
(this->colorSpace() == otherLayer->colorSpace() ||
*this->colorSpace() == *otherLayer->colorSpace());
}
KisLayerSP KisLayer::createMergedLayerTemplate(KisLayerSP prevLayer)
{
const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer);
KisLayerSP newLayer = new KisPaintLayer(image(), prevLayer->name(), OPACITY_OPAQUE_U8);
if (keepBlendingOptions) {
newLayer->setCompositeOpId(compositeOpId());
newLayer->setOpacity(opacity());
newLayer->setChannelFlags(channelFlags());
}
return newLayer;
}
void KisLayer::fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer)
{
const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer);
QRect layerProjectionExtent = this->projection()->extent();
QRect prevLayerProjectionExtent = prevLayer->projection()->extent();
bool alphaDisabled = this->alphaChannelDisabled();
bool prevAlphaDisabled = prevLayer->alphaChannelDisabled();
KisPaintDeviceSP mergedDevice = dstLayer->paintDevice();
if (!keepBlendingOptions) {
-
- KisNodeSP parentNode = parent();
KisPainter gc(mergedDevice);
+ KisImageSP imageSP = image().toStrongRef();
+ if (!imageSP) {
+ return;
+ }
+
//Copy the pixels of previous layer with their actual alpha value
prevLayer->disableAlphaChannel(false);
- prevLayer->projectionPlane()->apply(&gc, prevLayerProjectionExtent | image()->bounds());
+ prevLayer->projectionPlane()->apply(&gc, prevLayerProjectionExtent | imageSP->bounds());
//Restore the previous prevLayer disableAlpha status for correct undo/redo
prevLayer->disableAlphaChannel(prevAlphaDisabled);
//Paint the pixels of the current layer, using their actual alpha value
if (alphaDisabled == prevAlphaDisabled) {
this->disableAlphaChannel(false);
}
- this->projectionPlane()->apply(&gc, layerProjectionExtent | image()->bounds());
+ this->projectionPlane()->apply(&gc, layerProjectionExtent | imageSP->bounds());
//Restore the layer disableAlpha status for correct undo/redo
this->disableAlphaChannel(alphaDisabled);
}
else {
//Copy prevLayer
KisPaintDeviceSP srcDev = prevLayer->projection();
mergedDevice->makeCloneFrom(srcDev, srcDev->extent());
//Paint layer on the copy
KisPainter gc(mergedDevice);
gc.bitBlt(layerProjectionExtent.topLeft(), this->projection(), layerProjectionExtent);
}
}
void KisLayer::registerClone(KisCloneLayerWSP clone)
{
m_d->clonesList.addClone(clone);
}
void KisLayer::unregisterClone(KisCloneLayerWSP clone)
{
m_d->clonesList.removeClone(clone);
}
const QList<KisCloneLayerWSP> KisLayer::registeredClones() const
{
return m_d->clonesList.registeredClones();
}
bool KisLayer::hasClones() const
{
return m_d->clonesList.hasClones();
}
void KisLayer::updateClones(const QRect &rect)
{
m_d->clonesList.setDirty(rect);
}
KisSelectionMaskSP KisLayer::selectionMask() const
{
KoProperties properties;
properties.setProperty("active", true);
QList<KisNodeSP> masks = childNodes(QStringList("KisSelectionMask"), properties);
// return the first visible mask
Q_FOREACH (KisNodeSP mask, masks) {
if (mask->visible()) {
return dynamic_cast<KisSelectionMask*>(mask.data());
}
}
- return 0;
+ return KisSelectionMaskSP();
}
KisSelectionSP KisLayer::selection() const
{
KisSelectionMaskSP mask = selectionMask();
if (mask) {
return mask->selection();
}
- else if (m_d->image) {
- return m_d->image->globalSelection();
- }
- else {
- return 0;
+
+ KisImageSP image = m_d->image.toStrongRef();
+ if (image) {
+ return image->globalSelection();
}
+ return KisSelectionSP();
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
QList<KisEffectMaskSP> KisLayer::effectMasks(KisNodeSP lastNode) const
{
QList<KisEffectMaskSP> masks;
if (childCount() > 0) {
KoProperties properties;
properties.setProperty("visible", true);
QList<KisNodeSP> nodes = childNodes(QStringList("KisEffectMask"), properties);
Q_FOREACH (const KisNodeSP& node, nodes) {
if (node == lastNode) break;
KisEffectMaskSP mask = dynamic_cast<KisEffectMask*>(const_cast<KisNode*>(node.data()));
if (mask)
masks.append(mask);
}
}
return masks;
}
bool KisLayer::hasEffectMasks() const
{
if (childCount() == 0) return false;
KisNodeSP node = firstChild();
while (node) {
if (node->inherits("KisEffectMask") && node->visible()) {
return true;
}
node = node->nextSibling();
}
return false;
}
QRect KisLayer::masksChangeRect(const QList<KisEffectMaskSP> &masks,
const QRect &requestedRect,
bool &rectVariesFlag) const
{
rectVariesFlag = false;
QRect prevChangeRect = requestedRect;
/**
* We set default value of the change rect for the case
* when there is no mask at all
*/
QRect changeRect = requestedRect;
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
changeRect = mask->changeRect(prevChangeRect);
if (changeRect != prevChangeRect)
rectVariesFlag = true;
prevChangeRect = changeRect;
}
return changeRect;
}
QRect KisLayer::masksNeedRect(const QList<KisEffectMaskSP> &masks,
const QRect &changeRect,
QStack<QRect> &applyRects,
bool &rectVariesFlag) const
{
rectVariesFlag = false;
QRect prevNeedRect = changeRect;
QRect needRect;
for (qint32 i = masks.size() - 1; i >= 0; i--) {
applyRects.push(prevNeedRect);
needRect = masks[i]->needRect(prevNeedRect);
if (prevNeedRect != needRect)
rectVariesFlag = true;
prevNeedRect = needRect;
}
return needRect;
}
KisNode::PositionToFilthy calculatePositionToFilthy(KisNodeSP nodeInQuestion,
KisNodeSP filthy,
KisNodeSP parent)
{
if (parent == filthy || parent != filthy->parent()) {
return KisNode::N_ABOVE_FILTHY;
}
if (nodeInQuestion == filthy) {
return KisNode::N_FILTHY;
}
KisNodeSP node = nodeInQuestion->prevSibling();
while (node) {
if (node == filthy) {
return KisNode::N_ABOVE_FILTHY;
}
node = node->prevSibling();
}
return KisNode::N_BELOW_FILTHY;
}
QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
KisPaintDeviceSP destination,
const QRect &requestedRect,
KisNodeSP filthyNode,
KisNodeSP lastNode) const
{
Q_ASSERT(source);
Q_ASSERT(destination);
QList<KisEffectMaskSP> masks = effectMasks(lastNode);
QRect changeRect;
QRect needRect;
if (masks.isEmpty()) {
changeRect = requestedRect;
if (source != destination) {
copyOriginalToProjection(source, destination, requestedRect);
}
} else {
QStack<QRect> applyRects;
bool changeRectVaries;
bool needRectVaries;
/**
* FIXME: Assume that varying of the changeRect has already
* been taken into account while preparing walkers
*/
changeRectVaries = false;
changeRect = requestedRect;
//changeRect = masksChangeRect(masks, requestedRect,
// changeRectVaries);
needRect = masksNeedRect(masks, changeRect,
applyRects, needRectVaries);
if (!changeRectVaries && !needRectVaries) {
/**
* A bit of optimization:
* All filters will read/write exactly from/to the requested
* rect so we needn't create temporary paint device,
* just apply it onto destination
*/
Q_ASSERT(needRect == requestedRect);
if (source != destination) {
copyOriginalToProjection(source, destination, needRect);
}
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
const QRect maskApplyRect = applyRects.pop();
const QRect maskNeedRect =
applyRects.isEmpty() ? needRect : applyRects.top();
PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast<KisLayer*>(this));
mask->apply(destination, maskApplyRect, maskNeedRect, maskPosition);
}
Q_ASSERT(applyRects.isEmpty());
} else {
/**
* We can't eliminate additional copy-op
* as filters' behaviour may be quite insane here,
* so let them work on their own paintDevice =)
*/
KisPaintDeviceSP tempDevice = new KisPaintDevice(colorSpace());
tempDevice->prepareClone(source);
copyOriginalToProjection(source, tempDevice, needRect);
QRect maskApplyRect = applyRects.pop();
QRect maskNeedRect = needRect;
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast<KisLayer*>(this));
mask->apply(tempDevice, maskApplyRect, maskNeedRect, maskPosition);
if (!applyRects.isEmpty()) {
maskNeedRect = maskApplyRect;
maskApplyRect = applyRects.pop();
}
}
Q_ASSERT(applyRects.isEmpty());
KisPainter::copyAreaOptimized(changeRect.topLeft(), tempDevice, destination, changeRect);
}
}
return changeRect;
}
QRect KisLayer::updateProjection(const QRect& rect, KisNodeSP filthyNode)
{
QRect updatedRect = rect;
KisPaintDeviceSP originalDevice = original();
if (!rect.isValid() ||
!visible() ||
!originalDevice) return QRect();
if (!needProjection() && !hasEffectMasks()) {
m_d->safeProjection.freeDevice();
} else {
if (!updatedRect.isEmpty()) {
KisPaintDeviceSP projection =
m_d->safeProjection.getDeviceLazy(originalDevice);
updatedRect = applyMasks(originalDevice, projection,
updatedRect, filthyNode, 0);
}
}
return updatedRect;
}
QRect KisLayer::partialChangeRect(KisNodeSP lastNode, const QRect& rect)
{
bool changeRectVaries = false;
QRect changeRect = outgoingChangeRect(rect);
changeRect = masksChangeRect(effectMasks(lastNode), changeRect,
changeRectVaries);
return changeRect;
}
/**
* \p rect is a dirty rect in layer's original() coordinates!
*/
void KisLayer::buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect)
{
QRect changeRect = partialChangeRect(lastNode, rect);
KisPaintDeviceSP originalDevice = original();
KIS_ASSERT_RECOVER_RETURN(needProjection() || hasEffectMasks());
if (!changeRect.isEmpty()) {
applyMasks(originalDevice, projection,
changeRect, this, lastNode);
}
}
bool KisLayer::needProjection() const
{
return false;
}
void KisLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const
{
KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect);
}
KisAbstractProjectionPlaneSP KisLayer::projectionPlane() const
{
return m_d->layerStyleProjectionPlane ?
m_d->layerStyleProjectionPlane : m_d->projectionPlane;
}
KisAbstractProjectionPlaneSP KisLayer::internalProjectionPlane() const
{
return m_d->projectionPlane;
}
KisPaintDeviceSP KisLayer::projection() const
{
KisPaintDeviceSP originalDevice = original();
return needProjection() || hasEffectMasks() ?
m_d->safeProjection.getDeviceLazy(originalDevice) : originalDevice;
}
QRect KisLayer::changeRect(const QRect &rect, PositionToFilthy pos) const
{
QRect changeRect = rect;
changeRect = incomingChangeRect(changeRect);
if(pos == KisNode::N_FILTHY) {
QRect projectionToBeUpdated = projection()->exactBoundsAmortized() & changeRect;
bool changeRectVaries;
changeRect = outgoingChangeRect(changeRect);
changeRect = masksChangeRect(effectMasks(), changeRect, changeRectVaries);
/**
* If the projection contains some dirty areas we should also
* add them to the change rect, because they might have
* changed. E.g. when a visibility of the mask has chnaged
* while the parent layer was invinisble.
*/
if (!projectionToBeUpdated.isEmpty() &&
!changeRect.contains(projectionToBeUpdated)) {
changeRect |= projectionToBeUpdated;
}
}
// TODO: string comparizon: optimize!
if (pos != KisNode::N_FILTHY &&
pos != KisNode::N_FILTHY_PROJECTION &&
compositeOpId() != COMPOSITE_COPY) {
changeRect |= rect;
}
return changeRect;
}
QRect KisLayer::incomingChangeRect(const QRect &rect) const
{
return rect;
}
QRect KisLayer::outgoingChangeRect(const QRect &rect) const
{
return rect;
}
QImage KisLayer::createThumbnail(qint32 w, qint32 h)
{
KisPaintDeviceSP originalDevice = original();
return originalDevice ?
originalDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()) : QImage();
}
qint32 KisLayer::x() const
{
KisPaintDeviceSP originalDevice = original();
return originalDevice ? originalDevice->x() : 0;
}
qint32 KisLayer::y() const
{
KisPaintDeviceSP originalDevice = original();
return originalDevice ? originalDevice->y() : 0;
}
void KisLayer::setX(qint32 x)
{
KisPaintDeviceSP originalDevice = original();
if (originalDevice)
originalDevice->setX(x);
}
void KisLayer::setY(qint32 y)
{
KisPaintDeviceSP originalDevice = original();
if (originalDevice)
originalDevice->setY(y);
}
-QRect KisLayer::extent() const
+QRect KisLayer::layerExtentImpl(bool needExactBounds) const
{
QRect additionalMaskExtent = QRect();
QList<KisEffectMaskSP> effectMasks = this->effectMasks();
Q_FOREACH(KisEffectMaskSP mask, effectMasks) {
additionalMaskExtent |= mask->nonDependentExtent();
}
KisPaintDeviceSP originalDevice = original();
- QRect layerExtent = originalDevice ? originalDevice->extent() : QRect();
+ QRect layerExtent;
- return layerExtent | additionalMaskExtent;
-}
+ if (originalDevice) {
+ layerExtent = needExactBounds ?
+ originalDevice->exactBounds() :
+ originalDevice->extent();
+ }
-QRect KisLayer::exactBounds() const
-{
- QRect additionalMaskExtent = QRect();
- QList<KisEffectMaskSP> effectMasks = this->effectMasks();
+ QRect additionalCompositeOpExtent;
+ if (compositeOpId() == COMPOSITE_DESTINATION_IN ||
+ compositeOpId() == COMPOSITE_DESTINATION_ATOP) {
- Q_FOREACH(KisEffectMaskSP mask, effectMasks) {
- additionalMaskExtent |= mask->nonDependentExtent();
+ additionalCompositeOpExtent = originalDevice->defaultBounds()->bounds();
}
- KisPaintDeviceSP originalDevice = original();
- QRect layerExtent = originalDevice ? originalDevice->exactBounds() : QRect();
+ return layerExtent | additionalMaskExtent | additionalCompositeOpExtent;
+}
- return layerExtent | additionalMaskExtent;
+QRect KisLayer::extent() const
+{
+ return layerExtentImpl(false);
+}
+
+QRect KisLayer::exactBounds() const
+{
+ return layerExtentImpl(true);
}
KisLayerSP KisLayer::parentLayer() const
{
return dynamic_cast<KisLayer*>(parent().data());
}
KisMetaData::Store* KisLayer::metaData()
{
return m_d->metaDataStore;
}
diff --git a/libs/image/kis_layer.h b/libs/image/kis_layer.h
index 0dd57af03c..4515b297d4 100644
--- a/libs/image/kis_layer.h
+++ b/libs/image/kis_layer.h
@@ -1,384 +1,387 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
* 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_LAYER_H_
#define KIS_LAYER_H_
#include <QRect>
#include <QRegion>
#include <QMetaType>
#include <QObject>
#include "kritaimage_export.h"
#include "kis_base_node.h"
#include "kis_types.h"
#include "kis_node.h"
#include "kis_psd_layer_style.h"
template <class T>
class QStack;
class QBitArray;
class KisCloneLayer;
class KisPSDLayerStyle;
class KisAbstractProjectionPlane;
namespace KisMetaData
{
class Store;
}
/**
* Abstract class that represents the concept of a Layer in Krita. This is not related
* to the paint devices: this is merely an abstraction of how layers can be stacked and
* rendered differently.
* Regarding the previous-, first-, next- and lastChild() calls, first means that it the layer
* is at the top of the group in the layerlist, using next will iterate to the bottom to last,
* whereas previous will go up to first again.
*
*
* TODO: Add a layer mode whereby the projection of the layer is used
* as a clipping path?
**/
class KRITAIMAGE_EXPORT KisLayer : public KisNode
{
Q_OBJECT
public:
/**
* @param image is the pointer of the image or null
* @param opacity is a value between OPACITY_TRANSPARENT_U8 and OPACITY_OPAQUE_U8
**/
KisLayer(KisImageWSP image, const QString &name, quint8 opacity);
KisLayer(const KisLayer& rhs);
virtual ~KisLayer();
/// returns the image's colorSpace or null, if there is no image
virtual const KoColorSpace * colorSpace() const override;
/// returns the layer's composite op for the colorspace of the layer's parent.
const KoCompositeOp * compositeOp() const override;
KisPSDLayerStyleSP layerStyle() const;
void setLayerStyle(KisPSDLayerStyleSP layerStyle);
/**
* \see a comment in KisNode::projectionPlane()
*/
virtual KisAbstractProjectionPlaneSP projectionPlane() const override;
/**
* The projection plane representing the layer itself without any
* styles or anything else. It is used by the layer styles projection
* plane to stack up the planes.
*/
virtual KisAbstractProjectionPlaneSP internalProjectionPlane() const;
QRect partialChangeRect(KisNodeSP lastNode, const QRect& rect);
void buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect);
virtual bool needProjection() const;
/**
* Return the fully rendered representation of this layer: its
* data and its effect masks
*/
KisPaintDeviceSP projection() const override;
/**
* Return the layer data before the effect masks have had their go
* at it.
*/
virtual KisPaintDeviceSP original() const override = 0;
/**
* @return the selection associated with this layer, if there is
* one. Otherwise, return 0;
*/
virtual KisSelectionMaskSP selectionMask() const;
/**
* @return the selection contained in the first KisSelectionMask associated
* with this layer or the image, if either exists, otherwise, return 0.
*/
virtual KisSelectionSP selection() const;
virtual KisBaseNode::PropertyList sectionModelProperties() const override;
virtual void setSectionModelProperties(const KisBaseNode::PropertyList &properties) override;
/**
* set/unset the channel flag for the alpha channel of this layer
*/
void disableAlphaChannel(bool disable);
/**
* returns true if the channel flag for the alpha channel
* of this layer is not set.
* returns false otherwise.
*/
bool alphaChannelDisabled() const;
/**
* set the channelflags for this layer to the specified bit array.
* The bit array must have exactly the same number of channels as
* the colorspace this layer is in, or be empty, in which case all
* channels are active.
*/
virtual void setChannelFlags(const QBitArray & channelFlags);
/**
* Return a bit array where each bit indicates whether a
* particular channel is active or not. If the channelflags bit
* array is empty, all channels are active.
*/
QBitArray & channelFlags() const;
/**
* Returns true if this layer is temporary: i.e., it should not
* appear in the layerbox, even though it is temporarily in the
* layer stack and taken into account on recomposition.
*/
bool temporary() const;
/**
* Set to true if this layer should not appear in the layerbox,
* even though it is temporarily in the layer stack and taken into
* account on recomposition.
*/
void setTemporary(bool t);
/// returns the image this layer belongs to, or null if there is no image
KisImageWSP image() const;
/**
* Set the image this layer belongs to.
*/
void setImage(KisImageWSP image) override;
/**
* Create and return a layer that is the result of merging
* this with layer.
*
* This method is designed to be called only within KisImage::mergeLayerDown().
*
* Decendands override this to create specific merged types when possible.
* The KisLayer one creates a KisPaintLayerSP via a bitBlt, and can work on all layer types.
*
* Decendants that perform there own version do NOT call KisLayer::createMergedLayer
*/
virtual KisLayerSP createMergedLayerTemplate(KisLayerSP prevLayer);
virtual void fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer);
/**
* Clones should be informed about updates of the original
* layer, so this is a way to register them
*/
void registerClone(KisCloneLayerWSP clone);
/**
* Deregisters the clone from the update list
*
* \see registerClone()
*/
void unregisterClone(KisCloneLayerWSP clone);
/**
* Return the list of the clones of this node. Be careful
* with the list, because it is not thread safe.
*/
const QList<KisCloneLayerWSP> registeredClones() const;
/**
* Returns whether we have a clone.
*
* Be careful with it. It is not thread safe to add/remove
* clone while checking hasClones(). So there should be no updates.
*/
bool hasClones() const;
/**
* It is calles by the async merger after projection update is done
*/
void updateClones(const QRect &rect);
public:
qint32 x() const override;
qint32 y() const override;
void setX(qint32 x) override;
void setY(qint32 y) override;
/**
* Returns an approximation of where the bounds
* of actual data of this layer are
*/
QRect extent() const override;
/**
* Returns the exact bounds of where the actual data
* of this layer resides
*/
QRect exactBounds() const override;
QImage createThumbnail(qint32 w, qint32 h) override;
public:
/**
* Returns true if there are any effect masks present
*/
bool hasEffectMasks() const;
/**
* @return the list of effect masks
*/
- QList<KisEffectMaskSP> effectMasks(KisNodeSP lastNode = 0) const;
+ QList<KisEffectMaskSP> effectMasks(KisNodeSP lastNode = KisNodeSP()) const;
/**
* Get the group layer that contains this layer.
*/
KisLayerSP parentLayer() const;
/**
* @return the metadata object associated with this object.
*/
KisMetaData::Store* metaData();
protected:
// override from KisNode
QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const override;
protected:
/**
* Ask the layer to assemble its data & apply all the effect masks
* to it.
*/
QRect updateProjection(const QRect& rect, KisNodeSP filthyNode);
/**
* Layers can override this method to get some special behavior
* when copying data from \p original to \p projection, e.g. blend
* in indirect painting device. If you need to modify data
* outside \p rect, please also override outgoingChangeRect()
* method.
*/
virtual void copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const;
/**
* For KisLayer classes change rect transformation consists of two
* parts: incoming and outgoing.
*
* 1) incomingChangeRect(rect) chande rect transformation
* performed by the transformations done basing on global
* projection. It is performed in KisAsyncMerger +
* KisUpdateOriginalVisitor classes. It happens before data
* coming to KisLayer::original() therefore it is
* 'incoming'. See KisAdjustmentLayer for example of usage.
*
* 2) outgoingChangeRect(rect) change rect transformation that
* happens in KisLayer::copyOriginalToProjection(). It applies
* *only* when the layer is 'filthy', that is was the cause of
* the merge process. See KisCloneLayer for example of usage.
*
* The flow of changed areas can be illustrated in the
* following way:
*
* 1. Current projection of size R1 is stored in KisAsyncMerger::m_currentProjection
* |
* | <-- KisUpdateOriginalVisitor writes data into layer's original() device.
* | The changed area on KisLayer::original() is
* | R2 = KisLayer::incomingChangeRect(R1)
* |
* 2. KisLayer::original() / changed rect: R2
* |
* | <-- KisLayer::updateProjection() starts composing a layer
* | It calls KisLayer::copyOriginalToProjection() which copies some area
* | to a temporaty device. The temporary device now stores
* | R3 = KisLayer::outgoingChangeRect(R2)
* |
* 3. Temporary device / changed rect: R3
* |
* | <-- KisLayer::updateProjection() continues composing a layer. It merges a mask.
* | R4 = KisMask::changeRect(R3)
* |
* 4. KisLayer::original() / changed rect: R4
*
* So in the end rect R4 will be passed up to the next layers in the stack.
*/
virtual QRect incomingChangeRect(const QRect &rect) const;
/**
* \see incomingChangeRect()
*/
virtual QRect outgoingChangeRect(const QRect &rect) const;
/**
* @param rectVariesFlag (out param) a flag, showing whether
* a rect varies from mask to mask
* @return an area that should be updated because of
* the change of @requestedRect of the layer
*/
QRect masksChangeRect(const QList<KisEffectMaskSP> &masks,
const QRect &requestedRect,
bool &rectVariesFlag) const;
/**
* Get needRects for all masks
* @param changeRect requested rect to be updated on final
* projection. Should be a return value
* of @ref masksChangedRect()
* @param applyRects (out param) a stack of the rects where filters
* should be applied
* @param rectVariesFlag (out param) a flag, showing whether
* a rect varies from mask to mask
* @return a needRect that should be prepared on the layer's
* paintDevice for all masks to succeed
*/
QRect masksNeedRect(const QList<KisEffectMaskSP> &masks,
const QRect &changeRect,
QStack<QRect> &applyRects,
bool &rectVariesFlag) const;
QRect applyMasks(const KisPaintDeviceSP source,
KisPaintDeviceSP destination,
const QRect &requestedRect,
KisNodeSP filthyNode, KisNodeSP lastNode) const;
bool canMergeAndKeepBlendOptions(KisLayerSP otherLayer);
private:
friend class KisLayerProjectionPlane;
friend class KisTransformMask;
friend class KisLayerTest;
+private:
+ QRect layerExtentImpl(bool exactBounds) const;
+
private:
struct Private;
Private * const m_d;
};
Q_DECLARE_METATYPE(KisLayerSP)
#endif // KIS_LAYER_H_
diff --git a/libs/image/kis_layer_composition.cpp b/libs/image/kis_layer_composition.cpp
index e8a856bb4c..6dddde0389 100644
--- a/libs/image/kis_layer_composition.cpp
+++ b/libs/image/kis_layer_composition.cpp
@@ -1,169 +1,207 @@
/*
* 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 "lazybrush/kis_colorize_mask.h"
+#include "kis_layer_utils.h"
+#include "kis_node_query_path.h"
#include <QDomDocument>
class KisCompositionVisitor : public KisNodeVisitor {
public:
enum Mode {
STORE,
APPLY
};
KisCompositionVisitor(KisLayerComposition* layerComposition, Mode mode) : m_layerComposition(layerComposition), m_mode(mode)
{
}
bool visit(KisNode* node) override { return process(node); }
bool visit(KisGroupLayer* layer) override
{
bool result = visitAll(layer);
if(layer == layer->image()->rootLayer()) {
return result;
}
return result && process(layer);
}
bool visit(KisAdjustmentLayer* layer) override { return process(layer); }
bool visit(KisPaintLayer* layer) override { return process(layer); }
bool visit(KisExternalLayer* layer) override { return process(layer); }
bool visit(KisGeneratorLayer* layer) override { return process(layer); }
bool visit(KisCloneLayer* layer) override { return process(layer); }
bool visit(KisFilterMask* mask) override { return process(mask); }
bool visit(KisTransformMask* mask) override { return process(mask); }
bool visit(KisTransparencyMask* mask) override { return process(mask); }
bool visit(KisSelectionMask* mask) override { return process(mask); }
bool visit(KisColorizeMask* mask) override { 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()
{
}
+KisLayerComposition::KisLayerComposition(const KisLayerComposition &rhs, KisImageWSP otherImage)
+ : m_image(otherImage ? otherImage : rhs.m_image),
+ m_name(rhs.m_name),
+ m_exportEnabled(rhs.m_exportEnabled)
+{
+ {
+ auto it = rhs.m_visibilityMap.constBegin();
+ for (; it != rhs.m_visibilityMap.constEnd(); ++it) {
+ QUuid nodeUuid = it.key();
+ KisNodeSP node = KisLayerUtils::findNodeByUuid(rhs.m_image->root(), nodeUuid);
+ if (node) {
+ KisNodeQueryPath path = KisNodeQueryPath::absolutePath(node);
+ KisNodeSP newNode = path.queryUniqueNode(m_image);
+ KIS_ASSERT_RECOVER(newNode) { continue; }
+
+ m_visibilityMap.insert(newNode->uuid(), it.value());
+ }
+ }
+ }
+
+ {
+ auto it = rhs.m_collapsedMap.constBegin();
+ for (; it != rhs.m_collapsedMap.constEnd(); ++it) {
+ QUuid nodeUuid = it.key();
+ KisNodeSP node = KisLayerUtils::findNodeByUuid(rhs.m_image->root(), nodeUuid);
+ if (node) {
+ KisNodeQueryPath path = KisNodeQueryPath::absolutePath(node);
+ KisNodeSP newNode = path.queryUniqueNode(m_image);
+ KIS_ASSERT_RECOVER(newNode) { continue; }
+
+ m_collapsedMap.insert(newNode->uuid(), it.value());
+ }
+ }
+ }
+}
+
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());
dbgKrita << "contains" << m_collapsedMap.contains(iter.key());
if (m_collapsedMap.contains(iter.key())) {
dbgKrita << "colapsed :" << m_collapsedMap[iter.key()];
valueElement.setAttribute("collapsed", m_collapsedMap[iter.key()]);
}
compositionElement.appendChild(valueElement);
}
element.appendChild(compositionElement);
}
diff --git a/libs/image/kis_layer_composition.h b/libs/image/kis_layer_composition.h
index 3f45810f40..e793b1fbd6 100644
--- a/libs/image/kis_layer_composition.h
+++ b/libs/image/kis_layer_composition.h
@@ -1,89 +1,91 @@
/*
* 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.
*/
#ifndef _KIS_LAYERCOMPOSITION_H
#define _KIS_LAYERCOMPOSITION_H
#include "kritaimage_export.h"
#include <QMap>
#include <QUuid>
#include <QDomDocument>
#include <QDomElement>
#include "kis_image.h"
/**
* Storage class for layer compositions. Layer compositions allow to have several state for visible layers
* e.g. used in storyboarding with one background and differnt foregrounds
*/
class KRITAIMAGE_EXPORT KisLayerComposition
{
public:
KisLayerComposition(KisImageWSP image, const QString& name);
~KisLayerComposition();
+ KisLayerComposition(const KisLayerComposition &rhs, KisImageWSP otherImage = 0);
+
/**
* Sets name of the composition
*/
void setName(const QString& name);
/**
* Name of the composition as show in the docker
* \return name of the composition
*/
QString name();
/**
* Stores the current visibility of all layers in the composition
*/
void store();
/**
* Applies the stored visibility to all the nodes
*/
void apply();
/**
* Set the export enabled flag, if false the compositions will not be exported
*/
void setExportEnabled(bool enabled);
/**
* Export enabled flag, if false the compositions will not be exported
* \return name of the composition
*/
bool isExportEnabled();
void setVisible(QUuid id, bool visible);
void setCollapsed(QUuid id, bool collapsed);
void save(QDomDocument& doc, QDomElement& element);
private:
KisImageWSP m_image;
QString m_name;
QMap<QUuid, bool> m_visibilityMap;
QMap<QUuid, bool> m_collapsedMap;
bool m_exportEnabled;
friend class KisCompositionVisitor;
};
#endif
diff --git a/libs/image/kis_layer_projection_plane.cpp b/libs/image/kis_layer_projection_plane.cpp
index 1161221c56..b45424f79a 100644
--- a/libs/image/kis_layer_projection_plane.cpp
+++ b/libs/image/kis_layer_projection_plane.cpp
@@ -1,118 +1,121 @@
/*
* 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_projection_plane.h"
#include <QBitArray>
#include <KoColorSpace.h>
#include <KoChannelInfo.h>
#include <KoCompositeOpRegistry.h>
#include "kis_painter.h"
#include "kis_projection_leaf.h"
struct KisLayerProjectionPlane::Private
{
KisLayer *layer;
};
KisLayerProjectionPlane::KisLayerProjectionPlane(KisLayer *layer)
: m_d(new Private)
{
m_d->layer = layer;
}
KisLayerProjectionPlane::~KisLayerProjectionPlane()
{
}
QRect KisLayerProjectionPlane::recalculate(const QRect& rect, KisNodeSP filthyNode)
{
return m_d->layer->updateProjection(rect, filthyNode);
}
void KisLayerProjectionPlane::apply(KisPainter *painter, const QRect &rect)
{
KisPaintDeviceSP device = m_d->layer->projection();
if (!device) return;
QRect needRect = rect;
- if (m_d->layer->compositeOpId() != COMPOSITE_COPY) {
+ if (m_d->layer->compositeOpId() != COMPOSITE_COPY &&
+ m_d->layer->compositeOpId() != COMPOSITE_DESTINATION_IN &&
+ m_d->layer->compositeOpId() != COMPOSITE_DESTINATION_ATOP) {
+
needRect &= device->extent();
}
if(needRect.isEmpty()) return;
QBitArray channelFlags = m_d->layer->projectionLeaf()->channelFlags();
// if the color spaces don't match we will have a problem with the channel flags
// because the channel flags from the source layer doesn't match with the colorspace of the projection device
// this leads to the situation that the wrong channels will be enabled/disabled
const KoColorSpace* srcCS = device->colorSpace();
const KoColorSpace* dstCS = painter->device()->colorSpace();
if (!channelFlags.isEmpty() && srcCS != dstCS) {
bool alphaFlagIsSet = (srcCS->channelFlags(false,true) & channelFlags) == srcCS->channelFlags(false,true);
bool allColorFlagsAreSet = (srcCS->channelFlags(true,false) & channelFlags) == srcCS->channelFlags(true,false);
bool allColorFlagsAreUnset = (srcCS->channelFlags(true,false) & channelFlags).count(true) == 0;
if(allColorFlagsAreSet) {
channelFlags = dstCS->channelFlags(true, alphaFlagIsSet);
} else if(allColorFlagsAreUnset) {
channelFlags = dstCS->channelFlags(false, alphaFlagIsSet);
} else {
//TODO: convert the cannel flags properly
// for now just the alpha channel bit is copied and the other channels are left alone
for (quint32 i=0; i < dstCS->channelCount(); ++i) {
if (dstCS->channels()[i]->channelType() == KoChannelInfo::ALPHA) {
channelFlags.setBit(i, alphaFlagIsSet);
break;
}
}
}
}
painter->setChannelFlags(channelFlags);
painter->setCompositeOp(m_d->layer->compositeOpId());
painter->setOpacity(m_d->layer->projectionLeaf()->opacity());
painter->bitBlt(needRect.topLeft(), device, needRect);
}
KisPaintDeviceList KisLayerProjectionPlane::getLodCapableDevices() const
{
return KisPaintDeviceList() << m_d->layer->projection();
}
QRect KisLayerProjectionPlane::needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
{
return m_d->layer->needRect(rect, pos);
}
QRect KisLayerProjectionPlane::changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
{
return m_d->layer->changeRect(rect, pos);
}
QRect KisLayerProjectionPlane::accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
{
return m_d->layer->accessRect(rect, pos);
}
diff --git a/libs/image/kis_layer_utils.cpp b/libs/image/kis_layer_utils.cpp
index d6af9d363a..db2db3187d 100644
--- a/libs/image/kis_layer_utils.cpp
+++ b/libs/image/kis_layer_utils.cpp
@@ -1,1240 +1,1279 @@
/*
* 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_utils.h"
#include <algorithm>
+#include <QUuid>
#include <KoColorSpaceConstants.h>
#include "kis_painter.h"
#include "kis_image.h"
#include "kis_node.h"
#include "kis_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_group_layer.h"
#include "kis_selection.h"
#include "kis_selection_mask.h"
#include "kis_meta_data_merge_strategy.h"
#include <kundo2command.h>
#include "commands/kis_image_layer_add_command.h"
#include "commands/kis_image_layer_remove_command.h"
#include "commands/kis_image_layer_move_command.h"
#include "commands/kis_image_change_layers_command.h"
#include "commands_new/kis_activate_selection_mask_command.h"
#include "kis_abstract_projection_plane.h"
#include "kis_processing_applicator.h"
#include "kis_image_animation_interface.h"
#include "kis_keyframe_channel.h"
#include "kis_command_utils.h"
#include "kis_processing_applicator.h"
#include "commands_new/kis_change_projection_color_command.h"
#include "kis_layer_properties_icons.h"
#include "lazybrush/kis_colorize_mask.h"
#include "commands/kis_node_property_list_command.h"
namespace KisLayerUtils {
void fetchSelectionMasks(KisNodeList 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);
}
}
}
struct MergeDownInfoBase {
MergeDownInfoBase(KisImageSP _image)
: image(_image),
storage(new SwitchFrameCommand::SharedStorage())
{
}
virtual ~MergeDownInfoBase() {}
KisImageWSP image;
QVector<KisSelectionMaskSP> selectionMasks;
KisNodeSP dstNode;
SwitchFrameCommand::SharedStorageSP storage;
QSet<int> frames;
virtual KisNodeList allSrcNodes() = 0;
virtual KisLayerSP dstLayer() { return 0; }
};
struct MergeDownInfo : public MergeDownInfoBase {
MergeDownInfo(KisImageSP _image,
KisLayerSP _prevLayer,
KisLayerSP _currLayer)
: MergeDownInfoBase(_image),
prevLayer(_prevLayer),
currLayer(_currLayer)
{
frames =
fetchLayerFramesRecursive(prevLayer) |
fetchLayerFramesRecursive(currLayer);
}
KisLayerSP prevLayer;
KisLayerSP currLayer;
KisNodeList allSrcNodes() override {
KisNodeList mergedNodes;
mergedNodes << currLayer;
mergedNodes << prevLayer;
return mergedNodes;
}
KisLayerSP dstLayer() override {
return dynamic_cast<KisLayer*>(dstNode.data());
}
};
struct MergeMultipleInfo : public MergeDownInfoBase {
MergeMultipleInfo(KisImageSP _image,
KisNodeList _mergedNodes)
: MergeDownInfoBase(_image),
mergedNodes(_mergedNodes)
{
foreach (KisNodeSP node, mergedNodes) {
frames |= fetchLayerFramesRecursive(node);
}
}
KisNodeList mergedNodes;
KisNodeList allSrcNodes() override {
return mergedNodes;
}
};
typedef QSharedPointer<MergeDownInfoBase> MergeDownInfoBaseSP;
typedef QSharedPointer<MergeDownInfo> MergeDownInfoSP;
typedef QSharedPointer<MergeMultipleInfo> MergeMultipleInfoSP;
struct FillSelectionMasks : public KUndo2Command {
FillSelectionMasks(MergeDownInfoBaseSP info) : m_info(info) {}
void redo() override {
fetchSelectionMasks(m_info->allSrcNodes(), m_info->selectionMasks);
}
private:
MergeDownInfoBaseSP m_info;
};
struct DisableColorizeKeyStrokes : public KisCommandUtils::AggregateCommand {
DisableColorizeKeyStrokes(MergeDownInfoBaseSP info) : m_info(info) {}
void populateChildCommands() override {
Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
recursiveApplyNodes(node,
[this] (KisNodeSP node) {
if (dynamic_cast<KisColorizeMask*>(node.data()) &&
KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool()) {
KisBaseNode::PropertyList props = node->sectionModelProperties();
KisLayerPropertiesIcons::setNodeProperty(&props,
KisLayerPropertiesIcons::colorizeEditKeyStrokes,
false);
addCommand(new KisNodePropertyListCommand(node, props));
}
});
}
}
private:
MergeDownInfoBaseSP m_info;
};
+ struct DisableOnionSkins : public KisCommandUtils::AggregateCommand {
+ DisableOnionSkins(MergeDownInfoBaseSP info) : m_info(info) {}
+
+ void populateChildCommands() override {
+ Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
+ recursiveApplyNodes(node,
+ [this] (KisNodeSP node) {
+ if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::onionSkins, false).toBool()) {
+
+ KisBaseNode::PropertyList props = node->sectionModelProperties();
+ KisLayerPropertiesIcons::setNodeProperty(&props,
+ KisLayerPropertiesIcons::onionSkins,
+ false);
+
+ addCommand(new KisNodePropertyListCommand(node, props));
+ }
+ });
+ }
+ }
+
+ private:
+ MergeDownInfoBaseSP m_info;
+ };
+
struct RefreshHiddenAreas : public KUndo2Command {
RefreshHiddenAreas(MergeDownInfoBaseSP info) : m_info(info) {}
void redo() override {
KisImageAnimationInterface *interface = m_info->image->animationInterface();
const QRect preparedRect = !interface->externalFrameActive() ?
m_info->image->bounds() : QRect();
foreach (KisNodeSP node, m_info->allSrcNodes()) {
refreshHiddenAreaAsync(node, preparedRect);
}
}
private:
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 refreshHiddenAreaAsync(KisNodeSP rootNode, const QRect &preparedArea) {
QRect realNodeRect = realNodeExactBounds(rootNode);
if (!preparedArea.contains(realNodeRect)) {
QRegion dirtyRegion = realNodeRect;
dirtyRegion -= preparedArea;
foreach(const QRect &rc, dirtyRegion.rects()) {
m_info->image->refreshGraphAsync(rootNode, rc, realNodeRect);
}
}
}
private:
MergeDownInfoBaseSP m_info;
};
struct KeepMergedNodesSelected : public KisCommandUtils::AggregateCommand {
KeepMergedNodesSelected(MergeDownInfoSP info, bool finalizing)
: m_singleInfo(info),
m_finalizing(finalizing) {}
KeepMergedNodesSelected(MergeMultipleInfoSP info, KisNodeSP putAfter, bool finalizing)
: m_multipleInfo(info),
m_finalizing(finalizing),
m_putAfter(putAfter) {}
void populateChildCommands() override {
KisNodeSP prevNode;
KisNodeSP nextNode;
KisNodeList prevSelection;
KisNodeList nextSelection;
KisImageSP image;
if (m_singleInfo) {
prevNode = m_singleInfo->currLayer;
nextNode = m_singleInfo->dstNode;
image = m_singleInfo->image;
} else if (m_multipleInfo) {
prevNode = m_putAfter;
nextNode = m_multipleInfo->dstNode;
prevSelection = m_multipleInfo->allSrcNodes();
image = m_multipleInfo->image;
}
if (!m_finalizing) {
addCommand(new KeepNodesSelectedCommand(prevSelection, KisNodeList(),
prevNode, KisNodeSP(),
image, false));
} else {
addCommand(new KeepNodesSelectedCommand(KisNodeList(), nextSelection,
KisNodeSP(), nextNode,
image, true));
}
}
private:
MergeDownInfoSP m_singleInfo;
MergeMultipleInfoSP m_multipleInfo;
bool m_finalizing;
KisNodeSP m_putAfter;
};
struct CreateMergedLayer : public KisCommandUtils::AggregateCommand {
CreateMergedLayer(MergeDownInfoSP info) : m_info(info) {}
void populateChildCommands() override {
// actual merging done by KisLayer::createMergedLayer (or specialized decendant)
m_info->dstNode = m_info->currLayer->createMergedLayerTemplate(m_info->prevLayer);
if (m_info->frames.size() > 0) {
m_info->dstNode->enableAnimation();
m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
}
}
private:
MergeDownInfoSP m_info;
};
struct CreateMergedLayerMultiple : public KisCommandUtils::AggregateCommand {
CreateMergedLayerMultiple(MergeMultipleInfoSP info, const QString name = QString() )
: m_info(info),
m_name(name) {}
void populateChildCommands() override {
QString mergedLayerName;
if (m_name.isEmpty()){
const QString mergedLayerSuffix = i18n("Merged");
mergedLayerName = m_info->mergedNodes.first()->name();
if (!mergedLayerName.endsWith(mergedLayerSuffix)) {
mergedLayerName = QString("%1 %2")
.arg(mergedLayerName).arg(mergedLayerSuffix);
}
} else {
mergedLayerName = m_name;
}
m_info->dstNode = new KisPaintLayer(m_info->image, mergedLayerName, OPACITY_OPAQUE_U8);
if (m_info->frames.size() > 0) {
m_info->dstNode->enableAnimation();
m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
}
QString compositeOpId;
QBitArray channelFlags;
bool compositionVaries = false;
foreach (KisNodeSP node, m_info->allSrcNodes()) {
if (compositeOpId.isEmpty()) {
compositeOpId = node->compositeOpId();
} else if (compositeOpId != node->compositeOpId()) {
compositionVaries = true;
break;
}
KisLayerSP layer = dynamic_cast<KisLayer*>(node.data());
if (layer && layer->layerStyle()) {
compositionVaries = true;
break;
}
}
if (!compositionVaries) {
if (!compositeOpId.isEmpty()) {
m_info->dstNode->setCompositeOpId(compositeOpId);
}
if (m_info->dstLayer() && !channelFlags.isEmpty()) {
m_info->dstLayer()->setChannelFlags(channelFlags);
}
}
}
private:
MergeMultipleInfoSP m_info;
QString m_name;
};
struct MergeLayers : public KisCommandUtils::AggregateCommand {
MergeLayers(MergeDownInfoSP info) : m_info(info) {}
void populateChildCommands() override {
// actual merging done by KisLayer::createMergedLayer (or specialized decendant)
m_info->currLayer->fillMergedLayerTemplate(m_info->dstLayer(), m_info->prevLayer);
}
private:
MergeDownInfoSP m_info;
};
struct MergeLayersMultiple : public KisCommandUtils::AggregateCommand {
MergeLayersMultiple(MergeMultipleInfoSP info) : m_info(info) {}
void populateChildCommands() override {
KisPainter gc(m_info->dstNode->paintDevice());
foreach (KisNodeSP node, m_info->allSrcNodes()) {
QRect rc = node->exactBounds() | m_info->image->bounds();
node->projectionPlane()->apply(&gc, rc);
}
}
private:
MergeMultipleInfoSP m_info;
};
struct MergeMetaData : public KUndo2Command {
MergeMetaData(MergeDownInfoSP info, const KisMetaData::MergeStrategy* strategy)
: m_info(info),
m_strategy(strategy) {}
void redo() override {
QRect layerProjectionExtent = m_info->currLayer->projection()->extent();
QRect prevLayerProjectionExtent = m_info->prevLayer->projection()->extent();
int prevLayerArea = prevLayerProjectionExtent.width() * prevLayerProjectionExtent.height();
int layerArea = layerProjectionExtent.width() * layerProjectionExtent.height();
QList<double> scores;
double norm = qMax(prevLayerArea, layerArea);
scores.append(prevLayerArea / norm);
scores.append(layerArea / norm);
QList<const KisMetaData::Store*> srcs;
srcs.append(m_info->prevLayer->metaData());
srcs.append(m_info->currLayer->metaData());
m_strategy->merge(m_info->dstLayer()->metaData(), srcs, scores);
}
private:
MergeDownInfoSP m_info;
const KisMetaData::MergeStrategy *m_strategy;
};
KeepNodesSelectedCommand::KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
const KisNodeList &selectedAfter,
KisNodeSP activeBefore,
KisNodeSP activeAfter,
KisImageSP image,
bool finalize, KUndo2Command *parent)
: FlipFlopCommand(finalize, parent),
m_selectedBefore(selectedBefore),
m_selectedAfter(selectedAfter),
m_activeBefore(activeBefore),
m_activeAfter(activeAfter),
m_image(image)
{
}
void KeepNodesSelectedCommand::end() {
KisImageSignalType type;
if (isFinalizing()) {
type = ComplexNodeReselectionSignal(m_activeAfter, m_selectedAfter);
} else {
type = ComplexNodeReselectionSignal(m_activeBefore, m_selectedBefore);
}
m_image->signalRouter()->emitNotification(type);
}
KisLayerSP constructDefaultLayer(KisImageSP image) {
return new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
}
RemoveNodeHelper::~RemoveNodeHelper()
{
}
/**
* 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 RemoveNodeHelper::safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image) {
const bool lastLayer = scanForLastLayer(image, nodes);
while (!nodes.isEmpty()) {
KisNodeList::iterator it = nodes.begin();
while (it != nodes.end()) {
if (!checkIsSourceForClone(*it, nodes)) {
KisNodeSP node = *it;
addCommandImpl(new KisImageLayerRemoveCommand(image, node, false, true));
it = nodes.erase(it);
} else {
++it;
}
}
}
if (lastLayer) {
KisLayerSP newLayer = constructDefaultLayer(image);
addCommandImpl(new KisImageLayerAddCommand(image, newLayer,
image->root(),
KisNodeSP(),
false, false));
}
}
bool RemoveNodeHelper::checkIsSourceForClone(KisNodeSP src, const KisNodeList &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;
}
bool RemoveNodeHelper::scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove) {
bool removeLayers = false;
Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) {
if (dynamic_cast<KisLayer*>(nodeToRemove.data())) {
removeLayers = true;
break;
}
}
if (!removeLayers) return false;
bool lastLayer = true;
KisNodeSP node = image->root()->firstChild();
while (node) {
if (!nodesToRemove.contains(node) &&
dynamic_cast<KisLayer*>(node.data())) {
lastLayer = false;
break;
}
node = node->nextSibling();
}
return lastLayer;
}
SimpleRemoveLayers::SimpleRemoveLayers(const KisNodeList &nodes,
KisImageSP image)
: m_nodes(nodes),
m_image(image)
{
}
void SimpleRemoveLayers::populateChildCommands() {
if (m_nodes.isEmpty()) return;
safeRemoveMultipleNodes(m_nodes, m_image);
}
void SimpleRemoveLayers::addCommandImpl(KUndo2Command *cmd) {
addCommand(cmd);
}
struct InsertNode : public KisCommandUtils::AggregateCommand {
InsertNode(MergeDownInfoBaseSP info, KisNodeSP putAfter)
: m_info(info), m_putAfter(putAfter) {}
void populateChildCommands() override {
addCommand(new KisImageLayerAddCommand(m_info->image,
m_info->dstNode,
m_putAfter->parent(),
m_putAfter,
true, false));
}
private:
virtual void addCommandImpl(KUndo2Command *cmd) {
addCommand(cmd);
}
private:
MergeDownInfoBaseSP m_info;
KisNodeSP m_putAfter;
};
struct CleanUpNodes : private RemoveNodeHelper, public KisCommandUtils::AggregateCommand {
CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter)
: m_info(info), m_putAfter(putAfter) {}
static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent) {
if (!putAfter) {
putAfter = nodesToDelete.last();
}
// Add the new merged node on top of the active node -- checking
// whether the parent is going to be deleted
parent = putAfter->parent();
while (parent && nodesToDelete.contains(parent)) {
parent = parent->parent();
}
}
void populateChildCommands() override {
KisNodeList nodesToDelete = m_info->allSrcNodes();
KisNodeSP parent;
findPerfectParent(nodesToDelete, m_putAfter, parent);
if (!parent) {
KisNodeSP oldRoot = m_info->image->root();
- KisNodeSP newRoot =
- new KisGroupLayer(m_info->image, "root", OPACITY_OPAQUE_U8);
+ KisNodeSP newRoot(new KisGroupLayer(m_info->image, "root", OPACITY_OPAQUE_U8));
addCommand(new KisImageLayerAddCommand(m_info->image,
m_info->dstNode,
newRoot,
KisNodeSP(),
true, false));
addCommand(new KisImageChangeLayersCommand(m_info->image, oldRoot, newRoot));
}
else {
if (parent == m_putAfter->parent()) {
addCommand(new KisImageLayerAddCommand(m_info->image,
m_info->dstNode,
parent,
m_putAfter,
true, false));
}
else {
addCommand(new KisImageLayerAddCommand(m_info->image,
m_info->dstNode,
parent,
parent->lastChild(),
true, false));
}
reparentSelectionMasks(m_info->image,
m_info->dstLayer(),
m_info->selectionMasks);
safeRemoveMultipleNodes(m_info->allSrcNodes(), m_info->image);
}
}
private:
void addCommandImpl(KUndo2Command *cmd) override {
addCommand(cmd);
}
void reparentSelectionMasks(KisImageSP image,
KisLayerSP newLayer,
const QVector<KisSelectionMaskSP> &selectionMasks) {
foreach (KisSelectionMaskSP mask, selectionMasks) {
addCommand(new KisImageLayerMoveCommand(image, mask, newLayer, newLayer->lastChild()));
addCommand(new KisActivateSelectionMaskCommand(mask, false));
}
}
private:
MergeDownInfoBaseSP m_info;
KisNodeSP m_putAfter;
};
SwitchFrameCommand::SharedStorage::~SharedStorage() {
}
SwitchFrameCommand::SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage)
: FlipFlopCommand(finalize),
m_image(image),
m_newTime(time),
m_storage(storage) {}
SwitchFrameCommand::~SwitchFrameCommand() {}
void SwitchFrameCommand::init() {
KisImageAnimationInterface *interface = m_image->animationInterface();
const int currentTime = interface->currentTime();
if (currentTime == m_newTime) {
m_storage->value = m_newTime;
return;
}
interface->image()->disableUIUpdates();
interface->saveAndResetCurrentTime(m_newTime, &m_storage->value);
}
void SwitchFrameCommand::end() {
KisImageAnimationInterface *interface = m_image->animationInterface();
const int currentTime = interface->currentTime();
if (currentTime == m_storage->value) {
return;
}
interface->restoreCurrentTime(&m_storage->value);
interface->image()->enableUIUpdates();
}
struct AddNewFrame : public KisCommandUtils::AggregateCommand {
AddNewFrame(MergeDownInfoBaseSP info, int frame) : m_info(info), m_frame(frame) {}
void populateChildCommands() override {
KUndo2Command *cmd = new KisCommandUtils::SkipFirstRedoWrapper();
KisKeyframeChannel *channel = m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id());
channel->addKeyframe(m_frame, cmd);
addCommand(cmd);
}
private:
MergeDownInfoBaseSP m_info;
int m_frame;
};
QSet<int> fetchLayerFrames(KisNodeSP node) {
KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!channel) return QSet<int>();
return channel->allKeyframeIds();
}
QSet<int> fetchLayerFramesRecursive(KisNodeSP rootNode) {
QSet<int> frames = fetchLayerFrames(rootNode);
KisNodeSP node = rootNode->firstChild();
while(node) {
frames |= fetchLayerFramesRecursive(node);
node = node->nextSibling();
}
return frames;
}
void updateFrameJobs(FrameJobs *jobs, KisNodeSP node) {
QSet<int> frames = fetchLayerFrames(node);
if (frames.isEmpty()) {
(*jobs)[0].insert(node);
} else {
foreach (int frame, frames) {
(*jobs)[frame].insert(node);
}
}
}
void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode) {
updateFrameJobs(jobs, rootNode);
KisNodeSP node = rootNode->firstChild();
while(node) {
updateFrameJobsRecursive(jobs, node);
node = node->nextSibling();
}
}
void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy)
{
if (!layer->prevSibling()) return;
// XXX: this breaks if we allow free mixing of masks and layers
KisLayerSP prevLayer = dynamic_cast<KisLayer*>(layer->prevSibling().data());
if (!prevLayer) return;
if (!layer->visible() && !prevLayer->visible()) {
return;
}
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(image, 0,
KisProcessingApplicator::NONE,
emitSignals,
kundo2_i18n("Merge Down"));
if (layer->visible() && prevLayer->visible()) {
MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer));
- // disable key strokes on all colorize masks and wait until
- // update is finished with a barrier
+ // disable key strokes on all colorize masks, all onion skins on
+ // paint layers and wait until update is finished with a barrier
applicator.applyCommand(new DisableColorizeKeyStrokes(info));
+ applicator.applyCommand(new DisableOnionSkins(info));
applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
applicator.applyCommand(new KeepMergedNodesSelected(info, false));
applicator.applyCommand(new FillSelectionMasks(info));
applicator.applyCommand(new CreateMergedLayer(info), KisStrokeJobData::BARRIER);
if (info->frames.size() > 0) {
foreach (int frame, info->frames) {
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
applicator.applyCommand(new AddNewFrame(info, frame));
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
}
} else {
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
}
applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
applicator.applyCommand(new CleanUpNodes(info, layer),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new KeepMergedNodesSelected(info, true));
} else if (layer->visible()) {
applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
layer, KisNodeSP(),
image, false));
applicator.applyCommand(
new SimpleRemoveLayers(KisNodeList() << prevLayer,
image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
KisNodeSP(), layer,
image, true));
} else if (prevLayer->visible()) {
applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
layer, KisNodeSP(),
image, false));
applicator.applyCommand(
new SimpleRemoveLayers(KisNodeList() << layer,
image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
KisNodeSP(), prevLayer,
image, true));
}
applicator.end();
}
bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
{
KisNodeList nodeParents;
KisNodeSP parent = node->parent();
while (parent) {
nodeParents << parent;
parent = parent->parent();
}
foreach(KisNodeSP perspectiveParent, parents) {
if (nodeParents.contains(perspectiveParent)) {
return true;
}
}
return false;
}
bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes)
{
bool result = false;
KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
if (clone) {
KisNodeSP cloneSource = KisNodeSP(clone->copyFrom());
Q_FOREACH(KisNodeSP subtree, nodes) {
result =
recursiveFindNode(subtree,
[cloneSource](KisNodeSP node) -> bool
{
return node == cloneSource;
});
if (!result) {
result = checkIsCloneOf(cloneSource, nodes);
}
if (result) {
break;
}
}
}
return result;
}
void filterMergableNodes(KisNodeList &nodes, bool allowMasks)
{
KisNodeList::iterator it = nodes.begin();
while (it != nodes.end()) {
if ((!allowMasks && !dynamic_cast<KisLayer*>(it->data())) ||
checkIsChildOf(*it, nodes)) {
qDebug() << "Skipping node" << ppVar((*it)->name());
it = nodes.erase(it);
} else {
++it;
}
}
}
void sortMergableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
{
KisNodeList::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());
}
KisNodeList sortMergableNodes(KisNodeSP root, KisNodeList nodes)
{
KisNodeList result;
sortMergableNodes(root, nodes, result);
return result;
}
KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks)
{
KIS_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
KisNodeSP root;
Q_FOREACH(KisNodeSP node, nodes) {
KisNodeSP localRoot = node;
while (localRoot->parent()) {
localRoot = localRoot->parent();
}
if (!root) {
root = localRoot;
}
KIS_ASSERT_RECOVER(root == localRoot) { return nodes; }
}
KisNodeList result;
sortMergableNodes(root, nodes, result);
filterMergableNodes(result, allowMasks);
return result;
}
void addCopyOfNameTag(KisNodeSP node)
{
const QString prefix = i18n("Copy of");
QString newName = node->name();
if (!newName.startsWith(prefix)) {
newName = QString("%1 %2").arg(prefix).arg(newName);
node->setName(newName);
}
}
KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
{
KisNodeList nodes;
if ((!excludeRoot || root->parent()) && root->check(props)) {
nodes << root;
}
KisNodeSP node = root->firstChild();
while (node) {
nodes += findNodesWithProps(node, props, excludeRoot);
node = node->nextSibling();
}
return nodes;
}
KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter)
{
KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; }
KIS_ASSERT_RECOVER(putAfter) { return nodes; }
KisNodeList visibleNodes;
int putAfterIndex = -1;
Q_FOREACH(KisNodeSP node, nodes) {
if (node->visible()) {
visibleNodes << node;
} else {
*invisibleNodes << node;
if (node == *putAfter) {
putAfterIndex = visibleNodes.size() - 1;
}
}
}
if (!visibleNodes.isEmpty() && putAfterIndex >= 0) {
putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1);
*putAfter = visibleNodes[putAfterIndex];
}
return visibleNodes;
}
void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color)
{
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(image,
image->root(),
KisProcessingApplicator::RECURSIVE,
emitSignals,
kundo2_i18n("Change projection color"),
0,
142857 + 1);
applicator.applyCommand(new KisChangeProjectionColorCommand(image, color), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
applicator.end();
}
void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter,
bool flattenSingleLayer, const KUndo2MagicString &actionName,
bool cleanupNodes = true, const QString layerName = QString())
{
+ if (!putAfter) {
+ putAfter = mergedNodes.first();
+ }
+
filterMergableNodes(mergedNodes);
{
KisNodeList tempNodes;
qSwap(mergedNodes, tempNodes);
sortMergableNodes(image->root(), tempNodes, mergedNodes);
}
if (mergedNodes.size() <= 1 &&
(!flattenSingleLayer && mergedNodes.size() == 1)) return;
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes);
KisProcessingApplicator applicator(image, 0,
KisProcessingApplicator::NONE,
emitSignals,
actionName);
KisNodeList originalNodes = mergedNodes;
KisNodeList invisibleNodes;
mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter);
if (!invisibleNodes.isEmpty()) {
applicator.applyCommand(
new SimpleRemoveLayers(invisibleNodes,
image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) {
MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes));
- // disable key strokes on all colorize masks and wait until
- // update is finished with a barrier
+ // disable key strokes on all colorize masks, all onion skins on
+ // paint layers and wait until update is finished with a barrier
applicator.applyCommand(new DisableColorizeKeyStrokes(info));
+ applicator.applyCommand(new DisableOnionSkins(info));
applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, false));
applicator.applyCommand(new FillSelectionMasks(info));
applicator.applyCommand(new CreateMergedLayerMultiple(info, layerName), KisStrokeJobData::BARRIER);
if (info->frames.size() > 0) {
foreach (int frame, info->frames) {
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
applicator.applyCommand(new AddNewFrame(info, frame));
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
}
} else {
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
}
//applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
if (cleanupNodes){
applicator.applyCommand(new CleanUpNodes(info, putAfter),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
} else {
applicator.applyCommand(new InsertNode(info, putAfter),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, true));
}
applicator.end();
}
void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
{
mergeMultipleLayersImpl(image, mergedNodes, putAfter, false, kundo2_i18n("Merge Selected Nodes"));
}
void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter)
{
KisNodeList mergedNodes;
mergedNodes << image->root();
mergeMultipleLayersImpl(image, mergedNodes, putAfter, true, kundo2_i18n("New From Visible"), false, i18nc("New layer created from all the visible layers", "Visible"));
}
struct MergeSelectionMasks : public KisCommandUtils::AggregateCommand {
MergeSelectionMasks(MergeDownInfoBaseSP info, KisNodeSP putAfter)
: m_info(info),
m_putAfter(putAfter){}
void populateChildCommands() override {
KisNodeSP parent;
CleanUpNodes::findPerfectParent(m_info->allSrcNodes(), m_putAfter, parent);
KisLayerSP parentLayer;
do {
parentLayer = dynamic_cast<KisLayer*>(parent.data());
parent = parent->parent();
} while(!parentLayer && parent);
KisSelectionSP selection = new KisSelection();
foreach (KisNodeSP node, m_info->allSrcNodes()) {
KisMaskSP mask = dynamic_cast<KisMask*>(node.data());
if (!mask) continue;
selection->pixelSelection()->applySelection(
mask->selection()->pixelSelection(), SELECTION_ADD);
}
KisSelectionMaskSP mergedMask = new KisSelectionMask(m_info->image);
mergedMask->initSelection(parentLayer);
mergedMask->setSelection(selection);
m_info->dstNode = mergedMask;
}
private:
MergeDownInfoBaseSP m_info;
KisNodeSP m_putAfter;
};
struct ActivateSelectionMask : public KisCommandUtils::AggregateCommand {
ActivateSelectionMask(MergeDownInfoBaseSP info)
: m_info(info) {}
void populateChildCommands() override {
KisSelectionMaskSP mergedMask = dynamic_cast<KisSelectionMask*>(m_info->dstNode.data());
addCommand(new KisActivateSelectionMaskCommand(mergedMask, true));
}
private:
MergeDownInfoBaseSP m_info;
};
bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
{
QList<KisSelectionMaskSP> selectionMasks;
for (auto it = mergedNodes.begin(); it != mergedNodes.end(); /*noop*/) {
KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(it->data());
if (!mask) {
it = mergedNodes.erase(it);
} else {
selectionMasks.append(mask);
++it;
}
}
if (mergedNodes.isEmpty()) return false;
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(selectionMasks.first()->parent().data());
KIS_ASSERT_RECOVER(parentLayer) { return 0; }
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(image, 0,
KisProcessingApplicator::NONE,
emitSignals,
kundo2_i18n("Merge Selection Masks"));
MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes));
applicator.applyCommand(new MergeSelectionMasks(info, putAfter));
applicator.applyCommand(new CleanUpNodes(info, putAfter),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new ActivateSelectionMask(info));
applicator.end();
return true;
}
void flattenLayer(KisImageSP image, KisLayerSP layer)
{
if (!layer->childCount() && !layer->layerStyle())
return;
KisNodeList mergedNodes;
mergedNodes << layer;
mergeMultipleLayersImpl(image, mergedNodes, layer, true, kundo2_i18n("Flatten Layer"));
}
void flattenImage(KisImageSP image)
{
KisNodeList mergedNodes;
mergedNodes << image->root();
mergeMultipleLayersImpl(image, mergedNodes, 0, true, kundo2_i18n("Flatten Image"));
}
KisSimpleUpdateCommand::KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent)
: FlipFlopCommand(finalize, parent),
m_nodes(nodes)
{
}
void KisSimpleUpdateCommand::end()
{
updateNodes(m_nodes);
}
void KisSimpleUpdateCommand::updateNodes(const KisNodeList &nodes)
{
Q_FOREACH(KisNodeSP node, nodes) {
node->setDirty(node->extent());
}
}
void recursiveApplyNodes(KisNodeSP node, std::function<void(KisNodeSP)> func)
{
func(node);
node = node->firstChild();
while (node) {
recursiveApplyNodes(node, func);
node = node->nextSibling();
}
}
KisNodeSP recursiveFindNode(KisNodeSP node, std::function<bool(KisNodeSP)> func)
{
if (func(node)) {
return node;
}
node = node->firstChild();
while (node) {
KisNodeSP resultNode = recursiveFindNode(node, func);
if (resultNode) {
return resultNode;
}
node = node->nextSibling();
}
return 0;
}
+
+ KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid)
+ {
+ return recursiveFindNode(root,
+ [uuid] (KisNodeSP node) {
+ return node->uuid() == uuid;
+ });
+ }
+
}
diff --git a/libs/image/kis_layer_utils.h b/libs/image/kis_layer_utils.h
index 94d12b7fc8..6086f6baa5 100644
--- a/libs/image/kis_layer_utils.h
+++ b/libs/image/kis_layer_utils.h
@@ -1,198 +1,204 @@
/*
* 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_LAYER_UTILS_H
#define __KIS_LAYER_UTILS_H
#include <functional>
#include "kundo2command.h"
#include "kis_types.h"
#include "kritaimage_export.h"
#include "kis_command_utils.h"
class KoProperties;
class KoColor;
+class QUuid;
namespace KisMetaData
{
class MergeStrategy;
}
namespace KisLayerUtils
{
KRITAIMAGE_EXPORT void sortMergableNodes(KisNodeSP root, QList<KisNodeSP> &inputNodes, QList<KisNodeSP> &outputNodes);
KRITAIMAGE_EXPORT KisNodeList sortMergableNodes(KisNodeSP root, KisNodeList nodes);
KRITAIMAGE_EXPORT void filterMergableNodes(KisNodeList &nodes, bool allowMasks = false);
KRITAIMAGE_EXPORT bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents);
/**
* Returns true if:
* o \p node is a clone of some layer in \p nodes
* o \p node is a clone any child layer of any layer in \p nodes
* o \p node is a clone of a clone of a ..., that in the end points
* to any layer in \p nodes of their children.
*/
KRITAIMAGE_EXPORT bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes);
KRITAIMAGE_EXPORT KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks = false);
KRITAIMAGE_EXPORT void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy);
KRITAIMAGE_EXPORT QSet<int> fetchLayerFrames(KisNodeSP node);
KRITAIMAGE_EXPORT QSet<int> fetchLayerFramesRecursive(KisNodeSP rootNode);
KRITAIMAGE_EXPORT void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter);
KRITAIMAGE_EXPORT void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter);
KRITAIMAGE_EXPORT bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter);
KRITAIMAGE_EXPORT void flattenLayer(KisImageSP image, KisLayerSP layer);
KRITAIMAGE_EXPORT void flattenImage(KisImageSP image);
KRITAIMAGE_EXPORT void addCopyOfNameTag(KisNodeSP node);
KRITAIMAGE_EXPORT KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot);
KRITAIMAGE_EXPORT void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color);
typedef QMap<int, QSet<KisNodeSP> > FrameJobs;
void updateFrameJobs(FrameJobs *jobs, KisNodeSP node);
void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode);
struct SwitchFrameCommand : public KisCommandUtils::FlipFlopCommand {
struct SharedStorage {
/**
* For some reason the absence of a destructor in the SharedStorage
* makes Krita crash on exit. Seems like some compiler weirdness... (DK)
*/
~SharedStorage();
int value;
};
typedef QSharedPointer<SharedStorage> SharedStorageSP;
public:
SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage);
~SwitchFrameCommand();
private:
void init();
void end();
private:
KisImageWSP m_image;
int m_newTime;
SharedStorageSP m_storage;
};
/**
* A command to keep correct set of selected/active nodes thoroughout
* the action.
*/
class KRITAIMAGE_EXPORT KeepNodesSelectedCommand : public KisCommandUtils::FlipFlopCommand
{
public:
KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
const KisNodeList &selectedAfter,
KisNodeSP activeBefore,
KisNodeSP activeAfter,
KisImageSP image,
bool finalize, KUndo2Command *parent = 0);
void end();
private:
KisNodeList m_selectedBefore;
KisNodeList m_selectedAfter;
KisNodeSP m_activeBefore;
KisNodeSP m_activeAfter;
KisImageWSP m_image;
};
KRITAIMAGE_EXPORT KisLayerSP constructDefaultLayer(KisImageSP image);
class KRITAIMAGE_EXPORT RemoveNodeHelper {
public:
virtual ~RemoveNodeHelper();
protected:
virtual void addCommandImpl(KUndo2Command *cmd) = 0;
void safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image);
private:
bool checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes);
static bool scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove);
};
struct SimpleRemoveLayers : private KisLayerUtils::RemoveNodeHelper, public KisCommandUtils::AggregateCommand {
SimpleRemoveLayers(const KisNodeList &nodes,
KisImageSP image);
void populateChildCommands();
protected:
virtual void addCommandImpl(KUndo2Command *cmd);
private:
KisNodeList m_nodes;
KisImageSP m_image;
KisNodeList m_selectedNodes;
KisNodeSP m_activeNode;
};
class KRITAIMAGE_EXPORT KisSimpleUpdateCommand : public KisCommandUtils::FlipFlopCommand
{
public:
KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent = 0);
void end();
static void updateNodes(const KisNodeList &nodes);
private:
KisNodeList m_nodes;
};
template <typename T>
bool checkNodesDiffer(KisNodeList nodes, std::function<T(KisNodeSP)> checkerFunc)
{
bool valueDiffers = false;
bool initialized = false;
T currentValue = T();
Q_FOREACH (KisNodeSP node, nodes) {
if (!initialized) {
currentValue = checkerFunc(node);
initialized = true;
} else if (currentValue != checkerFunc(node)) {
valueDiffers = true;
break;
}
}
return valueDiffers;
}
/**
* Applies \p func to \p node and all its children recursively
*/
void KRITAIMAGE_EXPORT recursiveApplyNodes(KisNodeSP node, std::function<void(KisNodeSP)> func);
/**
* Walks through \p node and all its children recursively until
* \p func returns true. When \p func returns true, the node is
* considered to be found, the search is stopped and the found
* node is returned to the caller.
*/
KisNodeSP KRITAIMAGE_EXPORT recursiveFindNode(KisNodeSP node, std::function<bool(KisNodeSP)> func);
+
+ /**
+ * Recursively searches for a node with specified Uuid
+ */
+ KisNodeSP KRITAIMAGE_EXPORT findNodeByUuid(KisNodeSP root, const QUuid &uuid);
};
#endif /* __KIS_LAYER_UTILS_H */
diff --git a/libs/image/kis_name_server.h b/libs/image/kis_name_server.h
index 9ec26ac9bc..aed1e92d63 100644
--- a/libs/image/kis_name_server.h
+++ b/libs/image/kis_name_server.h
@@ -1,40 +1,39 @@
/*
* 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.
*/
#ifndef KIS_NAMESERVER_H_
#define KIS_NAMESERVER_H_
#include <QString>
#include <kritaimage_export.h>
class KRITAIMAGE_EXPORT KisNameServer
{
public:
KisNameServer(qint32 seed = 1);
~KisNameServer();
qint32 number();
qint32 currentSeed() const;
void rollback();
private:
qint32 m_generator;
- QString m_prefix;
};
#endif // KIS_NAMESERVER_H_
diff --git a/libs/image/kis_node.cpp b/libs/image/kis_node.cpp
index 9e4a605708..89735ebd9b 100644
--- a/libs/image/kis_node.cpp
+++ b/libs/image/kis_node.cpp
@@ -1,629 +1,629 @@
/*
* 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.h"
#include <QList>
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>
#include <QPainterPath>
#include <QRect>
#include <QCoreApplication>
#include <KoProperties.h>
#include "kis_global.h"
#include "kis_node_graph_listener.h"
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_node_progress_proxy.h"
#include "kis_busy_progress_indicator.h"
#include "kis_clone_layer.h"
#include "kis_safe_read_list.h"
typedef KisSafeReadList<KisNodeSP> KisSafeReadNodeList;
#include "kis_abstract_projection_plane.h"
#include "kis_projection_leaf.h"
#include "kis_undo_adapter.h"
#include "kis_keyframe_channel.h"
/**
*The link between KisProjection ans KisImageUpdater
*uses queued signals with an argument of KisNodeSP type,
*so we should register it beforehand
*/
struct KisNodeSPStaticRegistrar {
KisNodeSPStaticRegistrar() {
qRegisterMetaType<KisNodeSP>("KisNodeSP");
}
};
static KisNodeSPStaticRegistrar __registrar1;
struct KisNodeListStaticRegistrar {
KisNodeListStaticRegistrar() {
qRegisterMetaType<KisNodeList>("KisNodeList");
}
};
static KisNodeListStaticRegistrar __registrar2;
/**
* Note about "thread safety" of KisNode
*
* 1) One can *read* any information about node and node graph in any
* number of threads concurrently. This operation is safe because
* of the usage of KisSafeReadNodeList and will run concurrently
* (lock-free).
*
* 2) One can *write* any information into the node or node graph in a
* single thread only! Changing the graph concurrently is *not*
* sane and therefore not supported.
*
* 3) One can *read and write* information about the node graph
* concurrently, given that there is only *one* writer thread and
* any number of reader threads. Please note that in this case the
* node's code is just guaranteed *not to crash*, which is ensured
* by nodeSubgraphLock. You need to ensure the sanity of the data
* read by the reader threads yourself!
*/
struct Q_DECL_HIDDEN KisNode::Private
{
public:
Private(KisNode *node)
: graphListener(0)
, nodeProgressProxy(0)
, busyProgressIndicator(0)
, projectionLeaf(new KisProjectionLeaf(node))
{
}
KisNodeWSP parent;
KisNodeGraphListener *graphListener;
KisSafeReadNodeList nodes;
KisNodeProgressProxy *nodeProgressProxy;
KisBusyProgressIndicator *busyProgressIndicator;
QReadWriteLock nodeSubgraphLock;
KisProjectionLeafSP projectionLeaf;
const KisNode* findSymmetricClone(const KisNode *srcRoot,
const KisNode *dstRoot,
const KisNode *srcTarget);
void processDuplicatedClones(const KisNode *srcDuplicationRoot,
const KisNode *dstDuplicationRoot,
KisNode *node);
};
/**
* Finds the layer in \p dstRoot subtree, which has the same path as
* \p srcTarget has in \p srcRoot
*/
const KisNode* KisNode::Private::findSymmetricClone(const KisNode *srcRoot,
const KisNode *dstRoot,
const KisNode *srcTarget)
{
if (srcRoot == srcTarget) return dstRoot;
KisSafeReadNodeList::const_iterator srcIter = srcRoot->m_d->nodes.constBegin();
KisSafeReadNodeList::const_iterator dstIter = dstRoot->m_d->nodes.constBegin();
for (; srcIter != srcRoot->m_d->nodes.constEnd(); srcIter++, dstIter++) {
KIS_ASSERT_RECOVER_RETURN_VALUE((srcIter != srcRoot->m_d->nodes.constEnd()) ==
(dstIter != dstRoot->m_d->nodes.constEnd()), 0);
const KisNode *node = findSymmetricClone(srcIter->data(), dstIter->data(), srcTarget);
if (node) return node;
}
return 0;
}
/**
* This function walks through a subtrees of old and new layers and
* searches for clone layers. For each clone layer it checks whether
* its copyFrom() lays inside the old subtree, and if it is so resets
* it to the corresponding layer in the new subtree.
*
* That is needed when the user duplicates a group layer with all its
* layer subtree. In such a case all the "internal" clones must stay
* "internal" and not point to the layers of the older group.
*/
void KisNode::Private::processDuplicatedClones(const KisNode *srcDuplicationRoot,
const KisNode *dstDuplicationRoot,
KisNode *node)
{
if (KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node)) {
KIS_ASSERT_RECOVER_RETURN(clone->copyFrom());
const KisNode *newCopyFrom = findSymmetricClone(srcDuplicationRoot,
dstDuplicationRoot,
clone->copyFrom());
if (newCopyFrom) {
KisLayer *newCopyFromLayer = dynamic_cast<KisLayer*>(const_cast<KisNode*>(newCopyFrom));
KIS_ASSERT_RECOVER_RETURN(newCopyFromLayer);
clone->setCopyFrom(newCopyFromLayer);
}
}
KisSafeReadNodeList::const_iterator iter;
FOREACH_SAFE(iter, node->m_d->nodes) {
KisNode *child = const_cast<KisNode*>((*iter).data());
processDuplicatedClones(srcDuplicationRoot, dstDuplicationRoot, child);
}
}
KisNode::KisNode()
: m_d(new Private(this))
{
m_d->parent = 0;
m_d->graphListener = 0;
moveToThread(qApp->thread());
}
KisNode::KisNode(const KisNode & rhs)
: KisBaseNode(rhs)
, m_d(new Private(this))
{
m_d->parent = 0;
m_d->graphListener = 0;
moveToThread(qApp->thread());
// NOTE: the nodes are not supposed to be added/removed while
// creation of another node, so we do *no* locking here!
KisSafeReadNodeList::const_iterator iter;
FOREACH_SAFE(iter, rhs.m_d->nodes) {
KisNodeSP child = (*iter)->clone();
child->createNodeProgressProxy();
m_d->nodes.append(child);
child->setParent(this);
}
m_d->processDuplicatedClones(&rhs, this, this);
}
KisNode::~KisNode()
{
if (m_d->busyProgressIndicator) {
m_d->busyProgressIndicator->prepareDestroying();
m_d->busyProgressIndicator->deleteLater();
}
if (m_d->nodeProgressProxy) {
m_d->nodeProgressProxy->prepareDestroying();
m_d->nodeProgressProxy->deleteLater();
}
{
QWriteLocker l(&m_d->nodeSubgraphLock);
m_d->nodes.clear();
}
delete m_d;
}
QRect KisNode::needRect(const QRect &rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
return rect;
}
QRect KisNode::changeRect(const QRect &rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
return rect;
}
QRect KisNode::accessRect(const QRect &rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
return rect;
}
KisAbstractProjectionPlaneSP KisNode::projectionPlane() const
{
KIS_ASSERT_RECOVER_NOOP(0 && "KisNode::projectionPlane() is not defined!");
static KisAbstractProjectionPlaneSP plane =
toQShared(new KisDumbProjectionPlane());
return plane;
}
KisProjectionLeafSP KisNode::projectionLeaf() const
{
return m_d->projectionLeaf;
}
bool KisNode::accept(KisNodeVisitor &v)
{
return v.visit(this);
}
void KisNode::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
- return visitor.visit(this, undoAdapter);
+ visitor.visit(this, undoAdapter);
}
int KisNode::graphSequenceNumber() const
{
return m_d->graphListener ? m_d->graphListener->graphSequenceNumber() : -1;
}
KisNodeGraphListener *KisNode::graphListener() const
{
return m_d->graphListener;
}
void KisNode::setGraphListener(KisNodeGraphListener *graphListener)
{
m_d->graphListener = graphListener;
QReadLocker l(&m_d->nodeSubgraphLock);
KisSafeReadNodeList::const_iterator iter;
FOREACH_SAFE(iter, m_d->nodes) {
KisNodeSP child = (*iter);
child->setGraphListener(graphListener);
}
}
void KisNode::setParent(KisNodeWSP parent)
{
QWriteLocker l(&m_d->nodeSubgraphLock);
m_d->parent = parent;
}
KisNodeSP KisNode::parent() const
{
QReadLocker l(&m_d->nodeSubgraphLock);
- return m_d->parent.isValid() ? KisNodeSP(m_d->parent) : 0;
+ return m_d->parent.isValid() ? KisNodeSP(m_d->parent) : KisNodeSP();
}
KisBaseNodeSP KisNode::parentCallback() const
{
return parent();
}
void KisNode::notifyParentVisibilityChanged(bool value)
{
QReadLocker l(&m_d->nodeSubgraphLock);
KisSafeReadNodeList::const_iterator iter;
FOREACH_SAFE(iter, m_d->nodes) {
KisNodeSP child = (*iter);
child->notifyParentVisibilityChanged(value);
}
}
void KisNode::baseNodeChangedCallback()
{
if(m_d->graphListener) {
m_d->graphListener->nodeChanged(this);
}
}
void KisNode::baseNodeInvalidateAllFramesCallback()
{
if(m_d->graphListener) {
m_d->graphListener->invalidateAllFrames();
}
}
void KisNode::addKeyframeChannel(KisKeyframeChannel *channel)
{
channel->setNode(this);
KisBaseNode::addKeyframeChannel(channel);
}
KisNodeSP KisNode::firstChild() const
{
QReadLocker l(&m_d->nodeSubgraphLock);
return !m_d->nodes.isEmpty() ? m_d->nodes.first() : 0;
}
KisNodeSP KisNode::lastChild() const
{
QReadLocker l(&m_d->nodeSubgraphLock);
return !m_d->nodes.isEmpty() ? m_d->nodes.last() : 0;
}
KisNodeSP KisNode::prevChildImpl(KisNodeSP child)
{
/**
* Warning: mind locking policy!
*
* The graph locks must be *always* taken in descending
* order. That is if you want to (or it implicitly happens that
* you) take a lock of a parent and a chil, you must first take
* the lock of a parent, and only after that ask a child to do the
* same. Otherwise you'll get a deadlock.
*/
QReadLocker l(&m_d->nodeSubgraphLock);
int i = m_d->nodes.indexOf(child) - 1;
return i >= 0 ? m_d->nodes.at(i) : 0;
}
KisNodeSP KisNode::nextChildImpl(KisNodeSP child)
{
/**
* See a comment in KisNode::prevChildImpl()
*/
QReadLocker l(&m_d->nodeSubgraphLock);
int i = m_d->nodes.indexOf(child) + 1;
return i > 0 && i < m_d->nodes.size() ? m_d->nodes.at(i) : 0;
}
KisNodeSP KisNode::prevSibling() const
{
KisNodeSP parentNode = parent();
return parentNode ? parentNode->prevChildImpl(const_cast<KisNode*>(this)) : 0;
}
KisNodeSP KisNode::nextSibling() const
{
KisNodeSP parentNode = parent();
return parentNode ? parentNode->nextChildImpl(const_cast<KisNode*>(this)) : 0;
}
quint32 KisNode::childCount() const
{
QReadLocker l(&m_d->nodeSubgraphLock);
return m_d->nodes.size();
}
KisNodeSP KisNode::at(quint32 index) const
{
QReadLocker l(&m_d->nodeSubgraphLock);
if (!m_d->nodes.isEmpty() && index < (quint32)m_d->nodes.size()) {
return m_d->nodes.at(index);
}
return 0;
}
int KisNode::index(const KisNodeSP node) const
{
QReadLocker l(&m_d->nodeSubgraphLock);
return m_d->nodes.indexOf(node);
}
QList<KisNodeSP> KisNode::childNodes(const QStringList & nodeTypes, const KoProperties & properties) const
{
QReadLocker l(&m_d->nodeSubgraphLock);
QList<KisNodeSP> nodes;
KisSafeReadNodeList::const_iterator iter;
FOREACH_SAFE(iter, m_d->nodes) {
if (*iter) {
if (properties.isEmpty() || (*iter)->check(properties)) {
bool rightType = true;
if(!nodeTypes.isEmpty()) {
rightType = false;
Q_FOREACH (const QString &nodeType, nodeTypes) {
if ((*iter)->inherits(nodeType.toLatin1())) {
rightType = true;
break;
}
}
}
if (rightType) {
nodes.append(*iter);
}
}
}
}
return nodes;
}
KisNodeSP KisNode::findChildByName(const QString &name)
{
KisNodeSP child = firstChild();
while (child) {
if (child->name() == name) {
return child;
}
if (child->childCount() > 0) {
KisNodeSP grandChild = child->findChildByName(name);
if (grandChild) {
return grandChild;
}
}
child = child->nextSibling();
}
return 0;
}
bool KisNode::add(KisNodeSP newNode, KisNodeSP aboveThis)
{
Q_ASSERT(newNode);
if (!newNode) return false;
if (aboveThis && aboveThis->parent().data() != this) return false;
if (!allowAsChild(newNode)) return false;
if (newNode->parent()) return false;
if (index(newNode) >= 0) return false;
int idx = aboveThis ? this->index(aboveThis) + 1 : 0;
// threoretical race condition may happen here ('idx' may become
// deprecated until the write lock will be held). But we ignore
// it, because it is not supported to add/remove nodes from two
// concurrent threads simultaneously
if (m_d->graphListener) {
m_d->graphListener->aboutToAddANode(this, idx);
}
{
QWriteLocker l(&m_d->nodeSubgraphLock);
newNode->createNodeProgressProxy();
m_d->nodes.insert(idx, newNode);
newNode->setParent(this);
newNode->setGraphListener(m_d->graphListener);
}
if (m_d->graphListener) {
m_d->graphListener->nodeHasBeenAdded(this, idx);
}
return true;
}
bool KisNode::remove(quint32 index)
{
if (index < childCount()) {
KisNodeSP removedNode = at(index);
if (m_d->graphListener) {
m_d->graphListener->aboutToRemoveANode(this, index);
}
{
QWriteLocker l(&m_d->nodeSubgraphLock);
removedNode->setGraphListener(0);
removedNode->setParent(0); // after calling aboutToRemoveANode or then the model get broken according to TT's modeltest
m_d->nodes.removeAt(index);
}
if (m_d->graphListener) {
m_d->graphListener->nodeHasBeenRemoved(this, index);
}
return true;
}
return false;
}
bool KisNode::remove(KisNodeSP node)
{
return node->parent().data() == this ? remove(index(node)) : false;
}
KisNodeProgressProxy* KisNode::nodeProgressProxy() const
{
if (m_d->nodeProgressProxy) {
return m_d->nodeProgressProxy;
} else if (parent()) {
return parent()->nodeProgressProxy();
}
return 0;
}
KisBusyProgressIndicator* KisNode::busyProgressIndicator() const
{
if (m_d->busyProgressIndicator) {
return m_d->busyProgressIndicator;
} else if (parent()) {
return parent()->busyProgressIndicator();
}
return 0;
}
void KisNode::createNodeProgressProxy()
{
if (!m_d->nodeProgressProxy) {
m_d->nodeProgressProxy = new KisNodeProgressProxy(this);
m_d->busyProgressIndicator = new KisBusyProgressIndicator(m_d->nodeProgressProxy);
m_d->nodeProgressProxy->moveToThread(this->thread());
m_d->busyProgressIndicator->moveToThread(this->thread());
}
}
void KisNode::setDirty()
{
setDirty(extent());
}
void KisNode::setDirty(const QVector<QRect> &rects)
{
Q_FOREACH (const QRect &rc, rects) {
setDirty(rc);
}
}
void KisNode::setDirty(const QRegion &region)
{
setDirty(region.rects());
}
void KisNode::setDirty(const QRect & rect)
{
if(m_d->graphListener) {
m_d->graphListener->requestProjectionUpdate(this, rect);
}
}
void KisNode::invalidateFrames(const KisTimeRange &range, const QRect &rect)
{
if(m_d->graphListener) {
m_d->graphListener->invalidateFrames(range, rect);
}
}
void KisNode::requestTimeSwitch(int time)
{
if(m_d->graphListener) {
m_d->graphListener->requestTimeSwitch(time);
}
}
void KisNode::syncLodCache()
{
// noop. everything is done by getLodCapableDevices()
}
KisPaintDeviceList KisNode::getLodCapableDevices() const
{
KisPaintDeviceList list;
KisPaintDeviceSP device = paintDevice();
if (device) {
list << device;
}
KisPaintDeviceSP originalDevice = original();
if (originalDevice && originalDevice != device) {
list << originalDevice;
}
list << projectionPlane()->getLodCapableDevices();
return list;
}
diff --git a/libs/image/kis_node_facade.cpp b/libs/image/kis_node_facade.cpp
index 4109002cd8..756ef943b1 100644
--- a/libs/image/kis_node_facade.cpp
+++ b/libs/image/kis_node_facade.cpp
@@ -1,201 +1,200 @@
/*
* 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_facade.h"
#include "kis_node_graph_listener.h"
#include <kis_debug.h>
struct Q_DECL_HIDDEN KisNodeFacade::Private
{
public:
KisNodeWSP root;
};
KisNodeFacade::KisNodeFacade()
: m_d(new Private())
{
}
KisNodeFacade::KisNodeFacade(KisNodeSP root)
: m_d(new Private())
{
m_d->root = root;
}
KisNodeFacade::~KisNodeFacade()
{
- delete m_d;
}
void KisNodeFacade::setRoot(KisNodeSP root)
{
m_d->root = root;
}
const KisNodeSP KisNodeFacade::root() const
{
return m_d->root;
}
bool KisNodeFacade::moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis)
{
dbgImage << "moveNode " << node << " " << parent << " " << aboveThis;
if (!node) {
dbgImage << "cannot move null node"; return false;
}
if (!parent) {
dbgImage << "cannot move to null parent"; return false;
}
if (node == parent) {
dbgImage << "cannot move self inside self"; return false;
}
if (node == aboveThis) {
dbgImage << "cannot move self above self"; return false;
}
if (parent == aboveThis) {
dbgImage << "cannot move above parent"; return false;
}
if (!node->parent()) {
dbgImage << "node does not have a parent"; return false;
}
if (aboveThis && aboveThis->parent() != parent) {
dbgImage << "above this parent is not the parent"; return false;
}
int newIndex = aboveThis ? parent->index(aboveThis) + 1 : 0;
return moveNode(node, parent, newIndex);
}
bool KisNodeFacade::moveNode(KisNodeSP node, KisNodeSP parent, quint32 newIndex)
{
dbgImage << "moveNode " << node << " " << parent << " " << newIndex;
int oldIndex = node->parent()->index(node);
if (node->graphListener())
node->graphListener()->aboutToMoveNode(node.data(), oldIndex, newIndex);
KisNodeSP aboveThis = parent->at(newIndex - 1);
if (aboveThis == node) return false;
if (node->parent()) {
if (!node->parent()->remove(node)) return false;
}
dbgImage << "moving node to " << newIndex;
bool success = addNode(node, parent, aboveThis);
if (node->graphListener())
node->graphListener()->nodeHasBeenMoved(node.data(), oldIndex, newIndex);
return success;
}
bool KisNodeFacade::addNode(KisNodeSP node, KisNodeSP parent)
{
dbgImage << "Add node " << node << " to " << parent;
if (!node) return false;
if (!parent && !m_d->root) return false;
if (parent)
return parent->add(node, parent->lastChild());
else
return m_d->root->add(node, m_d->root->lastChild());
}
bool KisNodeFacade::addNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis)
{
if (!node) return false;
if (!parent) return false;
return parent->add(node, aboveThis);
}
bool KisNodeFacade::addNode(KisNodeSP node, KisNodeSP parent, quint32 index)
{
if (!node) return false;
if (!parent) return false;
if (index == parent->childCount())
return parent->add(node, parent->lastChild());
else if (index != 0)
return parent->add(node, parent->at(index));
else
return parent->add(node, 0);
}
bool KisNodeFacade::removeNode(KisNodeSP node)
{
if (!node) return false;
if (!node->parent()) return false;
return node->parent()->remove(node);
}
bool KisNodeFacade::raiseNode(KisNodeSP node)
{
if (!node) return false;
if (!node->parent()) return false;
if (node->nextSibling())
return moveNode(node, node->parent(), node->nextSibling());
else
return true; // we're already at the top, but there is no
// sense in complaining.
}
bool KisNodeFacade::lowerNode(KisNodeSP node)
{
if (!node) return false;
if (!node->parent()) return false;
KisNodeSP parent = node->parent();
KisNodeSP prevSibling = node->prevSibling();
if (node->prevSibling()) {
int prevIndex = parent->index(prevSibling);
return moveNode(node, parent, prevIndex);
} else {
return true; // We're already at bottom, but there's no sense
// in complaining
}
}
bool KisNodeFacade::toTop(KisNodeSP node)
{
if (!node) return false;
if (!node->parent()) return false;
if (node == node->parent()->lastChild()) return true;
return moveNode(node, node->parent(), node->parent()->lastChild());
}
bool KisNodeFacade::toBottom(KisNodeSP node)
{
if (!node) return false;
if (!node->parent()) return false;
KisNodeSP parent = node->parent();
if (node == parent->firstChild()) return true;
// Sets the parent of this node to 0
if (!parent->remove(node)) return false;
return parent->add(node, 0);
}
diff --git a/libs/image/kis_node_facade.h b/libs/image/kis_node_facade.h
index 7ab5299e8a..12b50cfd4b 100644
--- a/libs/image/kis_node_facade.h
+++ b/libs/image/kis_node_facade.h
@@ -1,140 +1,142 @@
/*
* 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 _KIS_NODE_FACADE_H
#define _KIS_NODE_FACADE_H
+#include <QScopedPointer>
+
#include "kis_types.h"
#include "kis_node.h"
#include "kritaimage_export.h"
/**
* KisNodeFacade is the public interface to adding and removing nodes.
*/
class KRITAIMAGE_EXPORT KisNodeFacade
{
public:
/**
* Create a new, empty KisNodeFacade
*/
KisNodeFacade();
/**
* Create a new kisnodefacade for the given root.
*/
KisNodeFacade(KisNodeSP root);
virtual ~KisNodeFacade();
/**
* Set the rootnode for this facade
*/
void setRoot(KisNodeSP root);
/**
* Return the root node for the graph this facade managed
*/
const KisNodeSP root() const;
/**
* Move the given node to specified position. If the node already
* has a parent, it is removed from the parent's node list.
*/
bool moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis);
/**
* Move the givent node at the specified index. If the node already
* has a parent, it is removed from the parent's node list.
*
* childCount() is a valid index and appends to the end.
*/
bool moveNode(KisNodeSP node, KisNodeSP parent, quint32 index);
/**
* Add an already existing node to the image. The node is put on top
* of the nodes in the specified nodegroup. If parent is 0, then
* the root is used as parent.
*
* @param node the node to be added
* @param parent the parent node
*/
- bool addNode(KisNodeSP node, KisNodeSP parent = 0);
+ bool addNode(KisNodeSP node, KisNodeSP parent = KisNodeSP());
/**
* Add already existing node to the graph.
*
* @param node the node to be added
* @param parent the parent node
* @param aboveThis in the list with child nodes of the specified
* parent, add this node above the specified sibling.
* if 0, the node is put in the lowermost position in
* its group.
* returns false if adding the node didn't work, true if the node got added
*/
bool addNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis);
/**
* Adds the node as a child of the given parent at the specified
* index.
*
* childCount() is a valid index and appends to the end. Fails and
* returns false if the node is already in this group or any
* other (remove it first.)
*/
bool addNode(KisNodeSP node, KisNodeSP parent, quint32 index);
/**
* Remove the specified node.
*
* @return false if removing the node failed
*/
bool removeNode(KisNodeSP node);
/**
* Move node up one slot, i.e., nextSibling becomes prevSibling
*/
bool raiseNode(KisNodeSP node);
/**
* Move node down one slot -- i.e, prevSibling becomes
* nextSibling.
*
* @return false if moving the node failed
*/
bool lowerNode(KisNodeSP node);
/**
* Move the given node to the top-most position among its
* siblings.
*
* @return false if moving the node failed.
*/
bool toTop(KisNodeSP node);
/**
* Move the given node to bottom-most position among its siblings.
*
* @return false if moving the node failed.
*/
bool toBottom(KisNodeSP node);
private:
struct Private;
- Private * const m_d;
+ QScopedPointer<Private> m_d;
};
#endif
diff --git a/libs/image/kis_node_graph_listener.cpp b/libs/image/kis_node_graph_listener.cpp
index 1f82516bda..1fd38c778a 100644
--- a/libs/image/kis_node_graph_listener.cpp
+++ b/libs/image/kis_node_graph_listener.cpp
@@ -1,103 +1,102 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
* 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_graph_listener.h"
#include "kis_time_range.h"
#include <QRect>
#include <QtGlobal>
struct Q_DECL_HIDDEN KisNodeGraphListener::Private
{
Private() : sequenceNumber(0) {}
int sequenceNumber;
};
KisNodeGraphListener::KisNodeGraphListener()
: m_d(new Private())
{
}
KisNodeGraphListener::~KisNodeGraphListener()
{
- delete m_d;
}
void KisNodeGraphListener::aboutToAddANode(KisNode */*parent*/, int /*index*/)
{
m_d->sequenceNumber++;
}
void KisNodeGraphListener::nodeHasBeenAdded(KisNode */*parent*/, int /*index*/)
{
m_d->sequenceNumber++;
}
void KisNodeGraphListener::aboutToRemoveANode(KisNode */*parent*/, int /*index*/)
{
m_d->sequenceNumber++;
}
void KisNodeGraphListener::nodeHasBeenRemoved(KisNode */*parent*/, int /*index*/)
{
m_d->sequenceNumber++;
}
void KisNodeGraphListener::aboutToMoveNode(KisNode * /*node*/, int /*oldIndex*/, int /*newIndex*/)
{
m_d->sequenceNumber++;
}
void KisNodeGraphListener::nodeHasBeenMoved(KisNode * /*node*/, int /*oldIndex*/, int /*newIndex*/)
{
m_d->sequenceNumber++;
}
int KisNodeGraphListener::graphSequenceNumber() const
{
return m_d->sequenceNumber;
}
void KisNodeGraphListener::nodeChanged(KisNode * /*node*/)
{
}
void KisNodeGraphListener::invalidateAllFrames()
{
}
void KisNodeGraphListener::notifySelectionChanged()
{
}
void KisNodeGraphListener::requestProjectionUpdate(KisNode * /*node*/, const QRect& /*rect*/)
{
}
void KisNodeGraphListener::invalidateFrames(const KisTimeRange &range, const QRect &rect)
{
Q_UNUSED(range);
Q_UNUSED(rect);
}
void KisNodeGraphListener::requestTimeSwitch(int time)
{
Q_UNUSED(time);
}
diff --git a/libs/image/kis_node_graph_listener.h b/libs/image/kis_node_graph_listener.h
index 3130339aac..d8b1ae410d 100644
--- a/libs/image/kis_node_graph_listener.h
+++ b/libs/image/kis_node_graph_listener.h
@@ -1,120 +1,123 @@
/*
* 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 KIS_NODE_GRAPH_LISTENER_H_
#define KIS_NODE_GRAPH_LISTENER_H_
#include "kritaimage_export.h"
+
+#include <QScopedPointer>
+
class KisTimeRange;
class KisNode;
class QRect;
/**
* Implementations of this class are called by nodes whenever the node
* graph changes. These implementations can then emit the right
* signals so Qt interview models can be updated before and after
* changes.
*
* The reason for this go-between is that we don't want our nodes to
* be QObjects, nor to have sig-slot connections between every node
* and every mode.
*
* It also manages the sequence number of the graph. This is a number
* which can be used as a checksum for whether the graph has chenged
* from some period of time or not. \see graphSequenceNumber()
*/
class KRITAIMAGE_EXPORT KisNodeGraphListener
{
public:
KisNodeGraphListener();
virtual ~KisNodeGraphListener();
/**
* Inform the model that we're going to add a node.
*/
virtual void aboutToAddANode(KisNode *parent, int index);
/**
* Inform the model we're done adding a node.
*/
virtual void nodeHasBeenAdded(KisNode *parent, int index);
/**
* Inform the model we're going to remove a node.
*/
virtual void aboutToRemoveANode(KisNode *parent, int index);
/**
* Inform the model we're done removing a node.
*/
virtual void nodeHasBeenRemoved(KisNode *parent, int index);
/**
* Inform the model we're about to start moving a node (which
* includes removing and adding the same node)
*/
virtual void aboutToMoveNode(KisNode * node, int oldIndex, int newIndex);
/**
* Inform the model we're done moving the node: it has been
* removed and added successfully
*/
virtual void nodeHasBeenMoved(KisNode * node, int oldIndex, int newIndex);
virtual void nodeChanged(KisNode * node);
virtual void invalidateAllFrames();
/**
* Inform the model that one of the selections in the graph is
* changed. The sender is not passed to the function (at least for
* now) because the UI should decide itself whether it needs to
* fetch new selection of not.
*/
virtual void notifySelectionChanged();
/**
* Inform the model that a node has been changed (setDirty)
*/
virtual void requestProjectionUpdate(KisNode * node, const QRect& rect);
virtual void invalidateFrames(const KisTimeRange &range, const QRect &rect);
virtual void requestTimeSwitch(int time);
/**
* Returns the sequence of the graph.
*
* Every time some operation performed, which might change the
* hierarchy of the nodes, the sequence number grows by one. So
* if you have any information about the graph which was acquired
* when the sequence number was X and now it has become Y, it
* means your information is outdated.
*
* It is used in the scheduler for checking whether queued walkers
* should be regenerated.
*/
int graphSequenceNumber() const;
private:
struct Private;
- Private * const m_d;
+ QScopedPointer<Private> m_d;
};
#endif
diff --git a/libs/image/kis_paint_device.cc b/libs/image/kis_paint_device.cc
index 48a5225924..b119cbb2cc 100644
--- a/libs/image/kis_paint_device.cc
+++ b/libs/image/kis_paint_device.cc
@@ -1,2092 +1,2113 @@
/*
* 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 <qmath.h>
#include <klocalizedstring.h>
#include <KoChannelInfo.h>
#include <KoColorProfile.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoIntegerMaths.h>
#include <KoMixColorsOp.h>
#include <KoUpdater.h>
#include "kis_image.h"
#include "kis_random_sub_accessor.h"
#include "kis_selection.h"
#include "kis_node.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_lod_transform.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_paint_device_cache.h"
#include "kis_paint_device_data.h"
#include "kis_paint_device_frames_interface.h"
#include "kis_transform_worker.h"
#include "kis_filter_strategy.h"
#include "krita_utils.h"
struct KisPaintDeviceSPStaticRegistrar {
KisPaintDeviceSPStaticRegistrar() {
qRegisterMetaType<KisPaintDeviceSP>("KisPaintDeviceSP");
}
};
static KisPaintDeviceSPStaticRegistrar __registrar;
struct KisPaintDevice::Private
{
/**
* Used when the paint device is loading to ensure no lod/animation
* interferes the process.
*/
static const KisDefaultBoundsSP transitionalDefaultBounds;
public:
class KisPaintDeviceStrategy;
class KisPaintDeviceWrappedStrategy;
Private(KisPaintDevice *paintDevice);
~Private();
KisPaintDevice *q;
KisNodeWSP parent;
QScopedPointer<KisRasterKeyframeChannel> contentChannel;
KisDefaultBoundsBaseSP defaultBounds;
QScopedPointer<KisPaintDeviceStrategy> basicStrategy;
QScopedPointer<KisPaintDeviceWrappedStrategy> wrappedStrategy;
QScopedPointer<KisPaintDeviceFramesInterface> framesInterface;
bool isProjectionDevice;
KisPaintDeviceStrategy* currentStrategy();
void init(const KoColorSpace *cs, const quint8 *defaultPixel);
KUndo2Command* convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags);
bool assignProfile(const KoColorProfile * profile);
inline const KoColorSpace* colorSpace() const
{
return currentData()->colorSpace();
}
inline KisDataManagerSP dataManager() const
{
return currentData()->dataManager();
}
inline qint32 x() const
{
return currentData()->x();
}
inline qint32 y() const
{
return currentData()->y();
}
inline void setX(qint32 x)
{
currentData()->setX(x);
}
inline void setY(qint32 y)
{
currentData()->setY(y);
}
inline KisPaintDeviceCache* cache()
{
return currentData()->cache();
}
void cloneAllDataObjects(Private *rhs, bool copyFrames)
{
m_lodData.reset();
m_externalFrameData.reset();
if (!m_frames.isEmpty()) {
m_frames.clear();
}
if (!copyFrames) {
if (m_data) {
m_data->prepareClone(rhs->currentNonLodData(), true);
} else {
m_data = toQShared(new KisPaintDeviceData(rhs->currentNonLodData(), true));
}
} else {
if (m_data && !rhs->m_data) {
m_data.clear();
} else if (!m_data && rhs->m_data) {
m_data = toQShared(new KisPaintDeviceData(rhs->m_data.data(), true));
} else if (m_data && rhs->m_data) {
m_data->prepareClone(rhs->m_data.data(), true);
}
if (!rhs->m_frames.isEmpty()) {
FramesHash::const_iterator it = rhs->m_frames.constBegin();
FramesHash::const_iterator end = rhs->m_frames.constEnd();
for (; it != end; ++it) {
DataSP data = toQShared(new KisPaintDeviceData(it.value().data(), true));
m_frames.insert(it.key(), data);
}
}
m_nextFreeFrameId = rhs->m_nextFreeFrameId;
}
if (rhs->m_lodData) {
m_lodData.reset(new KisPaintDeviceData(rhs->m_lodData.data(), true));
}
}
void prepareClone(KisPaintDeviceSP src)
{
prepareCloneImpl(src, src->m_d->currentData());
Q_ASSERT(fastBitBltPossible(src));
}
bool fastBitBltPossible(KisPaintDeviceSP src)
{
return fastBitBltPossibleImpl(src->m_d->currentData());
}
int currentFrameId() const
{
KIS_ASSERT_RECOVER(contentChannel) {
return -1;
}
return !defaultBounds->currentLevelOfDetail() ?
contentChannel->frameIdAt(defaultBounds->currentTime()) :
-1;
}
KisDataManagerSP frameDataManager(int frameId) const
{
DataSP data = m_frames[frameId];
return data->dataManager();
}
void invalidateFrameCache(int frameId)
{
DataSP data = m_frames[frameId];
return data->cache()->invalidate();
}
private:
typedef KisPaintDeviceData Data;
typedef QSharedPointer<Data> DataSP;
typedef QHash<int, DataSP> FramesHash;
class FrameInsertionCommand : public KUndo2Command
{
public:
FrameInsertionCommand(FramesHash *hash, DataSP data, int frameId, bool insert, KUndo2Command *parentCommand)
: KUndo2Command(parentCommand),
m_hash(hash),
m_data(data),
m_frameId(frameId),
m_insert(insert)
{
}
void redo() override
{
doSwap(m_insert);
}
void undo() override
{
doSwap(!m_insert);
}
private:
void doSwap(bool insert)
{
if (insert) {
m_hash->insert(m_frameId, m_data);
} else {
DataSP deletedData = m_hash->take(m_frameId);
}
}
private:
FramesHash *m_hash;
DataSP m_data;
int m_frameId;
bool m_insert;
};
public:
int getNextFrameId() {
int frameId = 0;
while (m_frames.contains(frameId = m_nextFreeFrameId++));
KIS_SAFE_ASSERT_RECOVER_NOOP(!m_frames.contains(frameId));
return frameId;
}
int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
{
KIS_ASSERT_RECOVER(parentCommand) {
return -1;
}
DataSP data;
bool initialFrame = false;
if (m_frames.isEmpty()) {
/**
* Here we move the contents of the paint device to the
* new frame and clear m_data to make the "background" for
* the areas where there is no frame at all.
*/
data = toQShared(new Data(m_data.data(), true));
m_data->dataManager()->clear();
m_data->cache()->invalidate();
initialFrame = true;
} else if (copy) {
DataSP srcData = m_frames[copySrc];
data = toQShared(new Data(srcData.data(), true));
} else {
DataSP srcData = m_frames.begin().value();
data = toQShared(new Data(srcData.data(), false));
}
if (!initialFrame && !copy) {
data->setX(offset.x());
data->setY(offset.y());
}
int frameId = getNextFrameId();
KUndo2Command *cmd =
new FrameInsertionCommand(&m_frames,
data,
frameId, true,
parentCommand);
cmd->redo();
return frameId;
}
void deleteFrame(int frame, KUndo2Command *parentCommand)
{
KIS_ASSERT_RECOVER_RETURN(m_frames.contains(frame));
KIS_ASSERT_RECOVER_RETURN(parentCommand);
DataSP deletedData = m_frames[frame];
KUndo2Command *cmd =
new FrameInsertionCommand(&m_frames,
deletedData,
frame, false,
parentCommand);
cmd->redo();
}
QRect frameBounds(int frameId)
{
DataSP data = m_frames[frameId];
QRect extent = data->dataManager()->extent();
extent.translate(data->x(), data->y());
return extent;
}
QPoint frameOffset(int frameId) const
{
DataSP data = m_frames[frameId];
return QPoint(data->x(), data->y());
}
void setFrameOffset(int frameId, const QPoint &offset)
{
DataSP data = m_frames[frameId];
data->setX(offset.x());
data->setY(offset.y());
}
const QList<int> frameIds() const
{
return m_frames.keys();
}
bool readFrame(QIODevice *stream, int frameId)
{
bool retval = false;
DataSP data = m_frames[frameId];
retval = data->dataManager()->read(stream);
data->cache()->invalidate();
return retval;
}
bool writeFrame(KisPaintDeviceWriter &store, int frameId)
{
DataSP data = m_frames[frameId];
return data->dataManager()->write(store);
}
void setFrameDefaultPixel(const KoColor &defPixel, int frameId)
{
DataSP data = m_frames[frameId];
KoColor color(defPixel);
color.convertTo(data->colorSpace());
data->dataManager()->setDefaultPixel(color.data());
}
KoColor frameDefaultPixel(int frameId) const
{
- DataSP data = m_frames[frameId];
+ DataSP data = m_frames[frameId];
return KoColor(data->dataManager()->defaultPixel(),
data->colorSpace());
}
void fetchFrame(int frameId, KisPaintDeviceSP targetDevice);
void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice);
void uploadFrameData(DataSP srcData, DataSP dstData);
struct LodDataStructImpl;
LodDataStruct* createLodDataStruct(int lod);
void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect);
void uploadLodDataStruct(LodDataStruct *dst);
QRegion regionForLodSyncing() const;
void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);
private:
QRegion syncWholeDevice(Data *srcData);
inline DataSP currentFrameData() const
{
DataSP data;
const int numberOfFrames = contentChannel->keyframeCount();
if (numberOfFrames > 1) {
int frameId = contentChannel->frameIdAt(defaultBounds->currentTime());
if (frameId == -1) {
data = m_data;
} else {
KIS_ASSERT_RECOVER(m_frames.contains(frameId)) {
return m_frames.begin().value();
}
data = m_frames[frameId];
}
} else if (numberOfFrames == 1) {
data = m_frames.begin().value();
} else {
data = m_data;
}
return data;
}
inline Data* currentNonLodData() const
{
Data *data = m_data.data();
if (contentChannel) {
data = currentFrameData().data();
} else if (isProjectionDevice && defaultBounds->externalFrameActive()) {
if (!m_externalFrameData) {
QMutexLocker l(&m_dataSwitchLock);
if (!m_externalFrameData) {
m_externalFrameData.reset(new Data(m_data.data(), false));
}
}
data = m_externalFrameData.data();
}
return data;
}
inline void ensureLodDataPresent() const
{
if (!m_lodData) {
Data *srcData = currentNonLodData();
QMutexLocker l(&m_dataSwitchLock);
if (!m_lodData) {
m_lodData.reset(new Data(srcData, false));
}
}
}
inline Data* currentData() const
{
Data *data;
if (defaultBounds->currentLevelOfDetail()) {
ensureLodDataPresent();
data = m_lodData.data();
} else {
data = currentNonLodData();
}
return data;
}
void prepareCloneImpl(KisPaintDeviceSP src, Data *srcData)
{
currentData()->prepareClone(srcData);
q->setDefaultPixel(KoColor(srcData->dataManager()->defaultPixel(), colorSpace()));
q->setDefaultBounds(src->defaultBounds());
}
bool fastBitBltPossibleImpl(Data *srcData)
{
return x() == srcData->x() && y() == srcData->y() &&
*colorSpace() == *srcData->colorSpace();
}
QList<Data*> allDataObjects() const
{
QList<Data*> dataObjects;
if (m_frames.isEmpty()) {
dataObjects << m_data.data();
}
dataObjects << m_lodData.data();
dataObjects << m_externalFrameData.data();
Q_FOREACH (DataSP value, m_frames.values()) {
dataObjects << value.data();
}
return dataObjects;
}
void transferFromData(Data *data, KisPaintDeviceSP targetDevice);
struct Q_DECL_HIDDEN StrategyPolicy;
typedef KisSequentialIteratorBase<ReadOnlyIteratorPolicy<StrategyPolicy>, StrategyPolicy> InternalSequentialConstIterator;
typedef KisSequentialIteratorBase<WritableIteratorPolicy<StrategyPolicy>, StrategyPolicy> InternalSequentialIterator;
private:
friend class KisPaintDeviceFramesInterface;
private:
DataSP m_data;
mutable QScopedPointer<Data> m_lodData;
mutable QScopedPointer<Data> m_externalFrameData;
mutable QMutex m_dataSwitchLock;
FramesHash m_frames;
int m_nextFreeFrameId;
};
const KisDefaultBoundsSP KisPaintDevice::Private::transitionalDefaultBounds = new KisDefaultBounds();
#include "kis_paint_device_strategies.h"
KisPaintDevice::Private::Private(KisPaintDevice *paintDevice)
: q(paintDevice),
basicStrategy(new KisPaintDeviceStrategy(paintDevice, this)),
isProjectionDevice(false),
m_data(new Data(paintDevice)),
m_nextFreeFrameId(0)
{
}
KisPaintDevice::Private::~Private()
{
m_frames.clear();
}
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(wrapRect, q, this));
}
return wrappedStrategy.data();
}
struct KisPaintDevice::Private::StrategyPolicy {
StrategyPolicy(KisPaintDevice::Private::KisPaintDeviceStrategy *strategy,
KisDataManager *dataManager, qint32 offsetX, qint32 offsetY)
: m_strategy(strategy),
m_dataManager(dataManager),
m_offsetX(offsetX),
m_offsetY(offsetY)
{
}
KisHLineConstIteratorSP createConstIterator(const QRect &rect)
{
return m_strategy->createHLineConstIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
}
KisHLineIteratorSP createIterator(const QRect &rect)
{
return m_strategy->createHLineIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
}
int pixelSize() const
{
return m_dataManager->pixelSize();
}
KisPaintDeviceStrategy *m_strategy;
KisDataManager *m_dataManager;
int m_offsetX;
int m_offsetY;
};
struct KisPaintDevice::Private::LodDataStructImpl : public KisPaintDevice::LodDataStruct {
LodDataStructImpl(Data *_lodData) : lodData(_lodData) {}
QScopedPointer<Data> lodData;
};
QRegion KisPaintDevice::Private::regionForLodSyncing() const
{
Data *srcData = currentNonLodData();
return srcData->dataManager()->region().translated(srcData->x(), srcData->y());
}
KisPaintDevice::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod)
{
Data *srcData = currentNonLodData();
Data *lodData = new Data(srcData, false);
LodDataStruct *lodStruct = new LodDataStructImpl(lodData);
int expectedX = KisLodTransform::coordToLodCoord(srcData->x(), newLod);
int expectedY = KisLodTransform::coordToLodCoord(srcData->y(), newLod);
/**
* We compare color spaces as pure pointers, because they must be
* exactly the same, since they come from the common source.
*/
if (lodData->levelOfDetail() != newLod ||
lodData->colorSpace() != srcData->colorSpace() ||
lodData->x() != expectedX ||
lodData->y() != expectedY) {
lodData->prepareClone(srcData);
lodData->setLevelOfDetail(newLod);
lodData->setX(expectedX);
lodData->setY(expectedY);
// FIXME: different kind of synchronization
}
//QRegion dirtyRegion = syncWholeDevice(srcData);
lodData->cache()->invalidate();
return lodStruct;
}
void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *_dst, const QRect &originalRect)
{
LodDataStructImpl *dst = dynamic_cast<LodDataStructImpl*>(_dst);
KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
Data *lodData = dst->lodData.data();
Data *srcData = currentNonLodData();
const int lod = lodData->levelOfDetail();
const int srcStepSize = 1 << lod;
KIS_ASSERT_RECOVER_RETURN(lod > 0);
const QRect srcRect = KisLodTransform::alignedRect(originalRect, lod);
const QRect dstRect = KisLodTransform::scaledRect(srcRect, lod);
if (!srcRect.isValid() || !dstRect.isValid()) return;
KIS_ASSERT_RECOVER_NOOP(srcRect.width() / srcStepSize == dstRect.width());
const int pixelSize = srcData->dataManager()->pixelSize();
int rowsAccumulated = 0;
int columnsAccumulated = 0;
KoMixColorsOp *mixOp = colorSpace()->mixColorsOp();
QScopedArrayPointer<quint8> blendData(new quint8[srcStepSize * srcRect.width() * pixelSize]);
quint8 *blendDataPtr = blendData.data();
int blendDataOffset = 0;
const int srcCellSize = srcStepSize * srcStepSize;
const int srcCellStride = srcCellSize * pixelSize;
const int srcStepStride = srcStepSize * pixelSize;
const int srcColumnStride = (srcStepSize - 1) * srcStepStride;
QScopedArrayPointer<qint16> weights(new qint16[srcCellSize]);
{
const qint16 averageWeight = qCeil(255.0 / srcCellSize);
const qint16 extraWeight = averageWeight * srcCellSize - 255;
KIS_ASSERT_RECOVER_NOOP(extraWeight == 1);
for (int i = 0; i < srcCellSize - 1; i++) {
weights[i] = averageWeight;
}
weights[srcCellSize - 1] = averageWeight - extraWeight;
}
InternalSequentialConstIterator srcIntIt(StrategyPolicy(currentStrategy(), srcData->dataManager().data(), srcData->x(), srcData->y()), srcRect);
InternalSequentialIterator dstIntIt(StrategyPolicy(currentStrategy(), lodData->dataManager().data(), lodData->x(), lodData->y()), dstRect);
int rowsRemaining = srcRect.height();
while (rowsRemaining > 0) {
int colsRemaining = srcRect.width();
while (colsRemaining > 0) {
memcpy(blendDataPtr, srcIntIt.rawDataConst(), pixelSize);
blendDataPtr += pixelSize;
columnsAccumulated++;
if (columnsAccumulated >= srcStepSize) {
blendDataPtr += srcColumnStride;
columnsAccumulated = 0;
}
srcIntIt.nextPixel();
colsRemaining--;
}
rowsAccumulated++;
if (rowsAccumulated >= srcStepSize) {
// blend and write the final data
blendDataPtr = blendData.data();
for (int i = 0; i < dstRect.width(); i++) {
mixOp->mixColors(blendDataPtr, weights.data(), srcCellSize, dstIntIt.rawData());
blendDataPtr += srcCellStride;
dstIntIt.nextPixel();
}
// reset counters
rowsAccumulated = 0;
blendDataPtr = blendData.data();
blendDataOffset = 0;
} else {
blendDataOffset += srcStepStride;
blendDataPtr = blendData.data() + blendDataOffset;
}
rowsRemaining--;
}
}
void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *_dst)
{
LodDataStructImpl *dst = dynamic_cast<LodDataStructImpl*>(_dst);
KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
KIS_SAFE_ASSERT_RECOVER_RETURN(
dst->lodData->levelOfDetail() == defaultBounds->currentLevelOfDetail());
ensureLodDataPresent();
m_lodData->prepareClone(dst->lodData.data());
m_lodData->dataManager()->bitBltRough(dst->lodData->dataManager(), dst->lodData->dataManager()->extent());
}
void KisPaintDevice::Private::transferFromData(Data *data, KisPaintDeviceSP targetDevice)
{
QRect extent = data->dataManager()->extent();
extent.translate(data->x(), data->y());
targetDevice->m_d->prepareCloneImpl(q, data);
targetDevice->m_d->currentStrategy()->fastBitBltRough(data->dataManager(), extent);
}
void KisPaintDevice::Private::fetchFrame(int frameId, KisPaintDeviceSP targetDevice)
{
DataSP data = m_frames[frameId];
transferFromData(data.data(), targetDevice);
}
void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
{
DataSP dstData = m_frames[dstFrameId];
KIS_ASSERT_RECOVER_RETURN(dstData);
DataSP srcData = srcDevice->m_d->m_frames[srcFrameId];
KIS_ASSERT_RECOVER_RETURN(srcData);
uploadFrameData(srcData, dstData);
}
void KisPaintDevice::Private::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
{
DataSP dstData = m_frames[dstFrameId];
KIS_ASSERT_RECOVER_RETURN(dstData);
DataSP srcData = srcDevice->m_d->m_data;
KIS_ASSERT_RECOVER_RETURN(srcData);
uploadFrameData(srcData, dstData);
}
void KisPaintDevice::Private::uploadFrameData(DataSP srcData, DataSP dstData)
{
if (srcData->colorSpace() != dstData->colorSpace() &&
*srcData->colorSpace() != *dstData->colorSpace()) {
KUndo2Command tempCommand;
srcData = toQShared(new Data(srcData.data(), true));
srcData->convertDataColorSpace(dstData->colorSpace(),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags(),
&tempCommand);
}
dstData->dataManager()->clear();
dstData->cache()->invalidate();
const QRect rect = srcData->dataManager()->extent();
dstData->dataManager()->bitBltRough(srcData->dataManager(), rect);
}
void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
{
Data *data = m_lodData.data();
Q_ASSERT(data);
transferFromData(data, targetDevice);
}
KUndo2Command* KisPaintDevice::Private::convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
class DeviceChangeColorSpaceCommand : public KUndo2Command
{
public:
DeviceChangeColorSpaceCommand(KisPaintDeviceSP device)
: m_firstRun(true),
m_device(device)
{
}
void emitNotifications()
{
m_device->emitColorSpaceChanged();
m_device->setDirty();
}
void redo() override
{
KUndo2Command::redo();
if (!m_firstRun) {
m_firstRun = false;
return;
}
emitNotifications();
}
void undo() override
{
KUndo2Command::undo();
emitNotifications();
}
private:
bool m_firstRun;
KisPaintDeviceSP m_device;
};
KUndo2Command *parentCommand = new DeviceChangeColorSpaceCommand(q);
QList<Data*> dataObjects = allDataObjects();;
Q_FOREACH (Data *data, dataObjects) {
if (!data) continue;
data->convertDataColorSpace(dstColorSpace, renderingIntent, conversionFlags, parentCommand);
}
if (!parentCommand->childCount()) {
delete parentCommand;
parentCommand = 0;
} else {
q->emitColorSpaceChanged();
}
return parentCommand;
}
bool KisPaintDevice::Private::assignProfile(const KoColorProfile * profile)
{
if (!profile) return false;
const KoColorSpace *dstColorSpace =
KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
if (!dstColorSpace) return false;
QList<Data*> dataObjects = allDataObjects();;
Q_FOREACH (Data *data, dataObjects) {
if (!data) continue;
data->assignColorSpace(dstColorSpace);
}
q->emitProfileChanged();
// no undo information is provided here
return true;
}
void KisPaintDevice::Private::init(const KoColorSpace *cs, const quint8 *defaultPixel)
{
QList<Data*> dataObjects = allDataObjects();;
Q_FOREACH (Data *data, dataObjects) {
if (!data) continue;
KisDataManagerSP dataManager = new KisDataManager(cs->pixelSize(), defaultPixel);
data->init(cs, dataManager);
}
}
KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name)
: QObject(0)
, m_d(new Private(this))
{
init(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(colorSpace, defaultBounds, parent, name);
}
void KisPaintDevice::init(const KoColorSpace *colorSpace,
KisDefaultBoundsBaseSP defaultBounds,
KisNodeWSP parent, const QString& name)
{
Q_ASSERT(colorSpace);
setObjectName(name);
// temporary def. bounds object for the initialization phase only
m_d->defaultBounds = m_d->transitionalDefaultBounds;
if (!defaultBounds) {
// Reuse transitionalDefaultBounds here. Change if you change
// semantics of transitionalDefaultBounds
defaultBounds = m_d->transitionalDefaultBounds;
}
QScopedArrayPointer<quint8> defaultPixel(new quint8[colorSpace->pixelSize()]);
colorSpace->fromQColor(Qt::transparent, defaultPixel.data());
m_d->init(colorSpace, defaultPixel.data());
Q_ASSERT(m_d->colorSpace());
setDefaultBounds(defaultBounds);
setParentNode(parent);
}
KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs, bool copyFrames, KisNode *newParentNode)
: QObject()
, KisShared()
, m_d(new Private(this))
{
if (this != &rhs) {
// temporary def. bounds object for the initialization phase only
m_d->defaultBounds = m_d->transitionalDefaultBounds;
// copy data objects with or without frames
m_d->cloneAllDataObjects(rhs.m_d, copyFrames);
if (copyFrames) {
KIS_ASSERT_RECOVER_RETURN(rhs.m_d->framesInterface);
KIS_ASSERT_RECOVER_RETURN(rhs.m_d->contentChannel);
m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this));
m_d->contentChannel.reset(new KisRasterKeyframeChannel(*rhs.m_d->contentChannel.data(), newParentNode, this));
}
setDefaultBounds(rhs.m_d->defaultBounds);
setParentNode(0);
}
}
KisPaintDevice::~KisPaintDevice()
{
delete m_d;
}
void KisPaintDevice::setProjectionDevice(bool value)
{
m_d->isProjectionDevice = value;
}
void KisPaintDevice::prepareClone(KisPaintDeviceSP src)
{
m_d->prepareClone(src);
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::requestTimeSwitch(int time)
{
if (m_d->parent.isValid()) {
m_d->parent->requestTimeSwitch(time);
}
}
int KisPaintDevice::sequenceNumber() const
{
return m_d->cache()->sequenceNumber();
}
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::moveTo(const QPoint &pt)
{
m_d->currentStrategy()->move(pt);
m_d->cache()->invalidate();
}
QPoint KisPaintDevice::offset() const
{
return QPoint(x(), y());
}
void KisPaintDevice::moveTo(qint32 x, qint32 y)
{
moveTo(QPoint(x, y));
}
void KisPaintDevice::setX(qint32 x)
{
moveTo(QPoint(x, m_d->y()));
}
void KisPaintDevice::setY(qint32 y)
{
moveTo(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();
}
QRect KisPaintDevice::exactBoundsAmortized() const
{
return m_d->cache()->exactBoundsAmortized();
}
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 = defaultPixel().opacityU8();
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) {
const KoColor defaultPixel = this->defaultPixel();
Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data());
endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp);
} else {
Impl::CheckFullyTransparent compareOp(m_d->colorSpace());
endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp);
}
return endRect;
}
QRegion KisPaintDevice::regionExact() const
{
QRegion resultRegion;
QVector<QRect> rects = region().rects();
const KoColor defaultPixel = this->defaultPixel();
Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data());
Q_FOREACH (const QRect &rc1, rects) {
const int patchSize = 64;
QVector<QRect> smallerRects = KritaUtils::splitRectIntoPatches(rc1, QSize(patchSize, patchSize));
Q_FOREACH (const QRect &rc2, smallerRects) {
const QRect result =
Impl::calculateExactBoundsImpl(this, rc2, QRect(), compareOp);
if (!result.isEmpty()) {
resultRegion += result;
}
}
}
return resultRegion;
}
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()
{
KisDataManagerSP dm = m_d->dataManager();
dm->purge(dm->extent());
}
void KisPaintDevice::setDefaultPixel(const KoColor &defPixel)
{
KoColor color(defPixel);
color.convertTo(colorSpace());
m_d->dataManager()->setDefaultPixel(color.data());
m_d->cache()->invalidate();
}
KoColor KisPaintDevice::defaultPixel() const
{
return KoColor(m_d->dataManager()->defaultPixel(), colorSpace());
}
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;
retval = m_d->dataManager()->read(stream);
m_d->cache()->invalidate();
return retval;
}
void KisPaintDevice::emitColorSpaceChanged()
{
emit colorSpaceChanged(m_d->colorSpace());
}
void KisPaintDevice::emitProfileChanged()
{
emit profileChanged(m_d->colorSpace()->profile());
}
KUndo2Command* KisPaintDevice::convertTo(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
KUndo2Command *command = m_d->convertColorSpace(dstColorSpace, renderingIntent, conversionFlags);
return command;
}
bool KisPaintDevice::setProfile(const KoColorProfile * profile)
{
return m_d->assignProfile(profile);
}
KisDataManagerSP KisPaintDevice::dataManager() const
{
return m_d->dataManager();
}
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
+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;
}
inline bool moveBy(KisSequentialConstIterator& iter, int numPixels)
{
int pos = 0;
while (pos < numPixels) {
int step = std::min(iter.nConseqPixels(), numPixels - pos);
if (!iter.nextPixels(step))
return false;
pos += step;
}
return true;
}
static KisPaintDeviceSP createThumbnailDeviceInternal(const KisPaintDevice* srcDev, qint32 srcX0, qint32 srcY0, qint32 srcWidth, qint32 srcHeight, qint32 w, qint32 h, QRect outputRect)
{
KisPaintDeviceSP thumbnail = new KisPaintDevice(srcDev->colorSpace());
qint32 pixelSize = srcDev->pixelSize();
KisRandomConstAccessorSP srcIter = srcDev->createRandomConstAccessorNG(0, 0);
KisRandomAccessorSP dstIter = thumbnail->createRandomAccessorNG(0, 0);
for (qint32 y = outputRect.y(); y < outputRect.y() + outputRect.height(); ++y) {
qint32 iY = srcY0 + (y * srcHeight) / h;
for (qint32 x = outputRect.x(); x < outputRect.x() + outputRect.width(); ++x) {
qint32 iX = srcX0 + (x * srcWidth) / w;
srcIter->moveTo(iX, iY);
dstIter->moveTo(x, y);
memcpy(dstIter->rawData(), srcIter->rawDataConst(), pixelSize);
}
}
return thumbnail;
}
+QSize fixThumbnailSize(QSize size)
+{
+ if (!size.width() && size.height()) {
+ size.setWidth(1);
+ }
+
+ if (size.width() && !size.height()) {
+ size.setHeight(1);
+ }
+
+ return size;
+}
+
KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect, QRect outputRect) const
{
QSize thumbnailSize(w, h);
- int srcWidth, srcHeight;
- int srcX0, srcY0;
QRect imageRect = rect.isValid() ? rect : extent();
+ if ((thumbnailSize.width() > imageRect.width()) || (thumbnailSize.height() > imageRect.height())) {
+ thumbnailSize.scale(imageRect.size(), Qt::KeepAspectRatio);
+ }
+
+ thumbnailSize = fixThumbnailSize(thumbnailSize);
+
//can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
- if (imageRect.isEmpty() || !imageRect.isValid()) {
+ if (imageRect.isEmpty() || thumbnailSize.isEmpty()) {
return new KisPaintDevice(colorSpace());
}
+ int srcWidth, srcHeight;
+ int srcX0, srcY0;
imageRect.getRect(&srcX0, &srcY0, &srcWidth, &srcHeight);
- if ((thumbnailSize.width() > imageRect.width()) || (thumbnailSize.height() > imageRect.height())) {
- thumbnailSize.scale(imageRect.size(), Qt::KeepAspectRatio);
- }
-
if (!outputRect.isValid()) {
outputRect = QRect(0, 0, w, h);
}
KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
thumbnailSize.width(), thumbnailSize.height(), outputRect);
return thumbnail;
}
KisPaintDeviceSP KisPaintDevice::createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect, QRect outputTileRect) const
{
QSize thumbnailSize(w, h);
qreal oversampleAdjusted = qMax(oversample, 1.);
QSize thumbnailOversampledSize = oversampleAdjusted * thumbnailSize;
QRect outputRect;
- QRect imageRect = (rect.isValid() && !rect.isNull()) ? rect : extent();
-
- //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
- if (imageRect.isEmpty() || !imageRect.isValid()) {
- return new KisPaintDevice(colorSpace());
- }
+ QRect imageRect = rect.isValid() ? rect : extent();
qint32 hstart = thumbnailOversampledSize.height();
if ((thumbnailOversampledSize.width() > imageRect.width()) || (thumbnailOversampledSize.height() > imageRect.height())) {
thumbnailOversampledSize.scale(imageRect.size(), Qt::KeepAspectRatio);
}
+ thumbnailOversampledSize = fixThumbnailSize(thumbnailOversampledSize);
+
+ //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
+ if (imageRect.isEmpty() || thumbnailSize.isEmpty() || thumbnailOversampledSize.isEmpty()) {
+ return new KisPaintDevice(colorSpace());
+ }
+
oversampleAdjusted *= (hstart > 0) ? ((qreal)thumbnailOversampledSize.height() / hstart) : 1.; //readjusting oversample ratio, given that we had to adjust thumbnail size
outputRect = QRect(0, 0, thumbnailOversampledSize.width(), thumbnailOversampledSize.height());
if (outputTileRect.isValid()) {
//compensating output rectangle for oversampling
outputTileRect = QRect(oversampleAdjusted * outputTileRect.topLeft(), oversampleAdjusted * outputTileRect.bottomRight());
outputRect = outputRect.intersected(outputTileRect);
}
KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
thumbnailOversampledSize.width(), thumbnailOversampledSize.height(), outputRect);
if (oversample != 1. && oversampleAdjusted != 1.) {
KoDummyUpdater updater;
KisTransformWorker worker(thumbnail, 1 / oversampleAdjusted, 1 / oversampleAdjusted, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
&updater, KisFilterStrategyRegistry::instance()->value("Bilinear"));
worker.run();
}
return thumbnail;
}
QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
- KisPaintDeviceSP dev = createThumbnailDeviceOversampled(w, h, oversample, rect);
+ QSize size = fixThumbnailSize(QSize(w, h));
+
+ KisPaintDeviceSP dev = createThumbnailDeviceOversampled(size.width(), size.height(), oversample, rect);
QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags);
return thumbnail;
}
QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
- return m_d->cache()->createThumbnail(w, h, oversample, renderingIntent, conversionFlags);
+ QSize size = fixThumbnailSize(QSize(w, h));
+
+ return m_d->cache()->createThumbnail(size.width(), size.height(), oversample, renderingIntent, conversionFlags);
}
KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
{
m_d->cache()->invalidate();
return m_d->currentStrategy()->createHLineIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y());
}
KisHLineConstIteratorSP KisPaintDevice::createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
{
return m_d->currentStrategy()->createHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y());
}
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
{
return new KisRepeatHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y(), _dataWidth);
}
KisRepeatVLineConstIteratorSP KisPaintDevice::createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const
{
return new KisRepeatVLineConstIteratorNG(m_d->dataManager().data(), 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)
{
const KoColorSpace *colorSpace = m_d->colorSpace();
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 KoColor defaultPixel = this->defaultPixel();
bool transparentDefault = (defaultPixel.opacityU8() == OPACITY_TRANSPARENT_U8);
for (qint32 y = 0; y < r.height(); y++) {
do {
// XXX: Optimize by using stretches
colorSpace->applyInverseAlphaU8Mask(devIt->rawData(), selectionIt->rawDataConst(), 1);
if (transparentDefault && colorSpace->opacityU8(devIt->rawData()) == OPACITY_TRANSPARENT_U8) {
memcpy(devIt->rawData(), defaultPixel.data(), 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->fastBitBltPossible(src);
}
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);
}
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;
}
KisRasterKeyframeChannel *KisPaintDevice::createKeyframeChannel(const KoID &id)
{
Q_ASSERT(!m_d->framesInterface);
m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this));
Q_ASSERT(!m_d->contentChannel);
m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, m_d->defaultBounds));
// Raster channels always have at least one frame (representing a static image)
KUndo2Command tempParentCommand;
m_d->contentChannel->addKeyframe(0, &tempParentCommand);
return m_d->contentChannel.data();
}
KisRasterKeyframeChannel* KisPaintDevice::keyframeChannel() const
{
Q_ASSERT(m_d->contentChannel);
return m_d->contentChannel.data();
}
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);
Q_FOREACH (KoChannelInfo * channelInfo, channels) {
sizes.append(channelInfo->size());
}
return sizes;
}
KisPaintDevice::MemoryReleaseObject::~MemoryReleaseObject()
{
KisDataManager::releaseInternalPools();
}
KisPaintDevice::MemoryReleaseObject* KisPaintDevice::createMemoryReleaseObject()
{
return new MemoryReleaseObject();
}
KisPaintDevice::LodDataStruct::~LodDataStruct()
{
}
QRegion KisPaintDevice::regionForLodSyncing() const
{
return m_d->regionForLodSyncing();
}
KisPaintDevice::LodDataStruct* KisPaintDevice::createLodDataStruct(int lod)
{
return m_d->createLodDataStruct(lod);
}
void KisPaintDevice::updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect)
{
m_d->updateLodDataStruct(dst, srcRect);
}
void KisPaintDevice::uploadLodDataStruct(LodDataStruct *dst)
{
m_d->uploadLodDataStruct(dst);
}
KisPaintDeviceFramesInterface* KisPaintDevice::framesInterface()
{
return m_d->framesInterface.data();
}
/******************************************************************/
/* KisPaintDeviceFramesInterface */
/******************************************************************/
KisPaintDeviceFramesInterface::KisPaintDeviceFramesInterface(KisPaintDevice *parentDevice)
: q(parentDevice)
{
}
QList<int> KisPaintDeviceFramesInterface::frames()
{
return q->m_d->frameIds();
}
int KisPaintDeviceFramesInterface::createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
{
return q->m_d->createFrame(copy, copySrc, offset, parentCommand);
}
void KisPaintDeviceFramesInterface::deleteFrame(int frame, KUndo2Command *parentCommand)
{
return q->m_d->deleteFrame(frame, parentCommand);
}
void KisPaintDeviceFramesInterface::fetchFrame(int frameId, KisPaintDeviceSP targetDevice)
{
q->m_d->fetchFrame(frameId, targetDevice);
}
void KisPaintDeviceFramesInterface::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
{
q->m_d->uploadFrame(srcFrameId, dstFrameId, srcDevice);
}
void KisPaintDeviceFramesInterface::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
{
q->m_d->uploadFrame(dstFrameId, srcDevice);
}
QRect KisPaintDeviceFramesInterface::frameBounds(int frameId)
{
return q->m_d->frameBounds(frameId);
}
QPoint KisPaintDeviceFramesInterface::frameOffset(int frameId) const
{
return q->m_d->frameOffset(frameId);
}
void KisPaintDeviceFramesInterface::setFrameDefaultPixel(const KoColor &defPixel, int frameId)
{
KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
q->m_d->setFrameDefaultPixel(defPixel, frameId);
}
KoColor KisPaintDeviceFramesInterface::frameDefaultPixel(int frameId) const
{
KIS_ASSERT_RECOVER(frameId >= 0) {
return KoColor(Qt::red, q->m_d->colorSpace());
}
return q->m_d->frameDefaultPixel(frameId);
}
bool KisPaintDeviceFramesInterface::writeFrame(KisPaintDeviceWriter &store, int frameId)
{
KIS_ASSERT_RECOVER(frameId >= 0) {
return false;
}
return q->m_d->writeFrame(store, frameId);
}
bool KisPaintDeviceFramesInterface::readFrame(QIODevice *stream, int frameId)
{
KIS_ASSERT_RECOVER(frameId >= 0) {
return false;
}
return q->m_d->readFrame(stream, frameId);
}
int KisPaintDeviceFramesInterface::currentFrameId() const
{
return q->m_d->currentFrameId();
}
KisDataManagerSP KisPaintDeviceFramesInterface::frameDataManager(int frameId) const
{
KIS_ASSERT_RECOVER(frameId >= 0) {
return q->m_d->dataManager();
}
return q->m_d->frameDataManager(frameId);
}
void KisPaintDeviceFramesInterface::invalidateFrameCache(int frameId)
{
KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
return q->m_d->invalidateFrameCache(frameId);
}
void KisPaintDeviceFramesInterface::setFrameOffset(int frameId, const QPoint &offset)
{
KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
return q->m_d->setFrameOffset(frameId, offset);
}
KisPaintDeviceFramesInterface::TestingDataObjects
KisPaintDeviceFramesInterface::testingGetDataObjects() const
{
TestingDataObjects objects;
objects.m_data = q->m_d->m_data.data();
objects.m_lodData = q->m_d->m_lodData.data();
objects.m_externalFrameData = q->m_d->m_externalFrameData.data();
typedef KisPaintDevice::Private::FramesHash FramesHash;
FramesHash::const_iterator it = q->m_d->m_frames.constBegin();
FramesHash::const_iterator end = q->m_d->m_frames.constEnd();
for (; it != end; ++it) {
objects.m_frames.insert(it.key(), it.value().data());
}
objects.m_currentData = q->m_d->currentData();
return objects;
}
QList<KisPaintDeviceData*> KisPaintDeviceFramesInterface::testingGetDataObjectsList() const
{
return q->m_d->allDataObjects();
}
void KisPaintDevice::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
{
m_d->tesingFetchLodDevice(targetDevice);
}
diff --git a/libs/image/kis_paint_device.h b/libs/image/kis_paint_device.h
index f6a0ad124b..e0b47c9152 100644
--- a/libs/image/kis_paint_device.h
+++ b/libs/image/kis_paint_device.h
@@ -1,868 +1,868 @@
/*
* 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_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;
class KisKeyframe;
class KisRasterKeyframeChannel;
class KisPaintDeviceFramesInterface;
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).
+ * to read and write pixels. A paint device has an integer x, y position
+ * (it is 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
*/
explicit 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(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds = KisDefaultBoundsBaseSP(), const QString& name = QString());
/**
* Creates a copy of this device.
*
* If \p copyFrames is false, the newly created device clones the
* current frame of \p rhs only (default and efficient
* behavior). If \p copyFrames is true, the new device is a deep
* copy of the source with all the frames included.
*/
KisPaintDevice(const KisPaintDevice& rhs, bool copyFrames = false, KisNode *newParentNode = 0);
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
+ * the default pixel is not completely transparent
*/
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)
+ * Moves the device to these new coordinates (no incremental move)
*/
void moveTo(qint32 x, qint32 y);
/**
* Convenience method for the above.
*/
virtual void moveTo(const QPoint& pt);
/**
* Return an X,Y offset of the device in a convenient form
*/
QPoint offset() const;
/**
* 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;
/**
* Relaxed version of the exactBounds() that can be used in tight
* loops. If the exact bounds value is present in the paint
* device cache, returns this value. If the cache is invalidated,
* returns extent() and tries to recalculate the exact bounds not
* faster than once in 1000 ms.
*/
QRect exactBoundsAmortized() 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;
/**
* The slow version of region() that searches for exact bounds of
* each rectangle in the region
*/
QRegion regionExact() 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.
* If frame ID is given, set default pixel for that frame. Otherwise use active frame.
*/
void setDefaultPixel(const KoColor &defPixel);
/**
* Get a pointer to the default pixel.
* If the frame parameter is given, get the default pixel of
* specified frame. Otherwise use currently active frame.
*/
KoColor 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.
*/
bool 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;
+ 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(), QRect outputRect = QRect()) const;
KisPaintDeviceSP createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect = QRect(), QRect outputRect = 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
* @param oversample: ratio used for antialiasing
*/
QImage createThumbnail(qint32 maxw, qint32 maxh, QRect rect, qreal oversample = 1,
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, qreal oversample = 1,
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;
/**
* Create a keyframe channel for the content on this device.
* @param id identifier for the channel
* @param node the parent node for the channel
* @return keyframe channel
*/
KisRasterKeyframeChannel *createKeyframeChannel(const KoID &id);
KisRasterKeyframeChannel* keyframeChannel() const;
/**
* An interface to modify/load/save frames stored inside this device
*/
KisPaintDeviceFramesInterface* framesInterface();
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);
/**
* Called by KisTransactionData when it thinks current time should
* be changed. And the requests is forwarded to the image if
* needed.
*/
void requestTimeSwitch(int time);
/**
* \return a sequence number corresponding to the current paint
* device state. Every time the paint device is changed,
* the sequence number is increased
*/
int sequenceNumber() const;
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();
public:
struct LodDataStruct {
virtual ~LodDataStruct();
};
QRegion regionForLodSyncing() const;
LodDataStruct* createLodDataStruct(int lod);
void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect);
void uploadLodDataStruct(LodDataStruct *dst);
void setProjectionDevice(bool value);
void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);
private:
KisPaintDevice& operator=(const KisPaintDevice&);
void init(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;
void emitColorSpaceChanged();
void emitProfileChanged();
private:
friend class KisPaintDeviceFramesInterface;
protected:
friend class KisSelectionTest;
KisNodeWSP parentNode() const;
private:
struct Private;
Private * const m_d;
};
#endif // KIS_PAINT_DEVICE_IMPL_H_
diff --git a/libs/image/kis_paint_device_cache.h b/libs/image/kis_paint_device_cache.h
index 9fcd545c2b..f783981879 100644
--- a/libs/image/kis_paint_device_cache.h
+++ b/libs/image/kis_paint_device_cache.h
@@ -1,167 +1,166 @@
/*
* 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_PAINT_DEVICE_CACHE_H
#define __KIS_PAINT_DEVICE_CACHE_H
#include "kis_lock_free_cache.h"
#include <QElapsedTimer>
class KisPaintDeviceCache
{
public:
KisPaintDeviceCache(KisPaintDevice *paintDevice)
: m_paintDevice(paintDevice),
m_exactBoundsCache(paintDevice),
m_nonDefaultPixelAreaCache(paintDevice),
m_regionCache(paintDevice),
m_sequenceNumber(0)
{
}
KisPaintDeviceCache(const KisPaintDeviceCache &rhs)
: m_paintDevice(rhs.m_paintDevice),
m_exactBoundsCache(rhs.m_paintDevice),
m_nonDefaultPixelAreaCache(rhs.m_paintDevice),
m_regionCache(rhs.m_paintDevice),
m_sequenceNumber(0)
{
}
void setupCache() {
invalidate();
}
void invalidate() {
m_thumbnailsValid = false;
m_exactBoundsCache.invalidate();
m_nonDefaultPixelAreaCache.invalidate();
m_regionCache.invalidate();
m_sequenceNumber++;
}
QRect exactBounds() {
return m_exactBoundsCache.getValue();
}
QRect exactBoundsAmortized() {
QRect bounds;
bool result = m_exactBoundsCache.tryGetValue(bounds);
if (!result) {
/**
* The calculation of the exact bounds might be too slow
* in some special cases, e.g. for an empty canvas of 7k
* by 6k. So we just always return extent, when the exact
* bounds is not available.
*/
bounds = m_paintDevice->extent();
}
return bounds;
}
QRect nonDefaultPixelArea() {
return m_nonDefaultPixelAreaCache.getValue();
}
QRegion region() {
return m_regionCache.getValue();
}
QImage createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) {
QImage thumbnail;
if(m_thumbnailsValid) {
thumbnail = findThumbnail(w, h, oversample);
}
else {
m_thumbnails.clear();
m_thumbnailsValid = true;
}
if(thumbnail.isNull()) {
thumbnail = m_paintDevice->createThumbnail(w, h, QRect(), oversample, renderingIntent, conversionFlags);
cacheThumbnail(w, h, oversample, thumbnail);
}
- Q_ASSERT(!thumbnail.isNull() || m_paintDevice->extent().isEmpty());
return thumbnail;
}
int sequenceNumber() const {
return m_sequenceNumber;
}
private:
inline QImage findThumbnail(qint32 w, qint32 h, qreal oversample) {
QImage resultImage;
if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h) && m_thumbnails[w][h].contains(oversample)) {
resultImage = m_thumbnails[w][h][oversample];
}
return resultImage;
}
inline void cacheThumbnail(qint32 w, qint32 h, qreal oversample, QImage image) {
m_thumbnails[w][h][oversample] = 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, QMap<qreal,QImage> > > m_thumbnails;
QAtomicInt m_sequenceNumber;
};
#endif /* __KIS_PAINT_DEVICE_CACHE_H */
diff --git a/libs/image/kis_painter.cc b/libs/image/kis_painter.cc
index 614c4828fb..744f4320ca 100644
--- a/libs/image/kis_painter.cc
+++ b/libs/image/kis_painter.cc
@@ -1,2886 +1,2888 @@
/*
* 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 <klocalizedstring.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_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 <brushengine/kis_paint_information.h>
#include "kis_paintop_registry.h"
#include "kis_perspective_math.h"
#include "tiles3/kis_random_accessor.h"
#include <kis_distance_information.h>
#include <KoColorSpaceMaths.h>
#include "kis_lod_transform.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) {}
Private(KisPainter *_q, const KoColorSpace *cs)
: q(_q), paintColor(cs), backgroundColor(cs) {}
KisPainter *q;
KisPaintDeviceSP device;
KisSelectionSP selection;
KisTransaction* transaction;
KoUpdater* progressUpdater;
QVector<QRect> dirtyRects;
KisPaintOp* paintOp;
KoColor paintColor;
KoColor backgroundColor;
KoColor customColor;
KisFilterConfigurationSP 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 mirrorHorizontally;
bool mirrorVertically;
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, device->colorSpace()))
{
init();
Q_ASSERT(device);
begin(device);
}
KisPainter::KisPainter(KisPaintDeviceSP device, KisSelectionSP selection)
: d(new Private(this, device->colorSpace()))
{
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->mirrorHorizontally = false;
d->mirrorVertically = 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);
}
}
}
KisPaintDeviceSP KisPainter::convertToAlphaAsAlpha(KisPaintDeviceSP src)
{
const KoColorSpace *srcCS = src->colorSpace();
const QRect processRect = src->extent();
- KisPaintDeviceSP dst = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
+ KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
KisSequentialConstIterator srcIt(src, processRect);
KisSequentialIterator dstIt(dst, processRect);
do {
const quint8 *srcPtr = srcIt.rawDataConst();
quint8 *alpha8Ptr = dstIt.rawData();
const quint8 white = srcCS->intensity8(srcPtr);
const quint8 alpha = srcCS->opacityU8(srcPtr);
*alpha8Ptr = KoColorSpaceMaths<quint8>::multiply(alpha, KoColorSpaceMathsTraits<quint8>::unitValue - white);
} while (srcIt.nextPixel() && dstIt.nextPixel());
return dst;
}
KisPaintDeviceSP KisPainter::convertToAlphaAsGray(KisPaintDeviceSP src)
{
const KoColorSpace *srcCS = src->colorSpace();
const QRect processRect = src->extent();
- KisPaintDeviceSP dst = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
+ KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
KisSequentialConstIterator srcIt(src, processRect);
KisSequentialIterator dstIt(dst, processRect);
do {
const quint8 *srcPtr = srcIt.rawDataConst();
quint8 *alpha8Ptr = dstIt.rawData();
*alpha8Ptr = srcCS->intensity8(srcPtr);
} while (srcIt.nextPixel() && dstIt.nextPixel());
return dst;
}
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 &&
+ compositeOp->id() != COMPOSITE_DESTINATION_IN &&
+ compositeOp->id() != COMPOSITE_DESTINATION_ATOP &&
!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();
+ 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();
+ 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();
+ 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->mirrorHorizontally || d->mirrorVertically) {
KisLodTransform lod(d->device);
QPointF effectiveAxesCenter = lod.map(d->axesCenter);
QTransform C1 = QTransform::fromTranslate(-effectiveAxesCenter.x(), -effectiveAxesCenter.y());
QTransform C2 = QTransform::fromTranslate(effectiveAxesCenter.x(), effectiveAxesCenter.y());
QTransform t;
QPainterPath newPath;
QRect newRect;
if (d->mirrorHorizontally) {
t = C1 * QTransform::fromScale(-1,1) * C2;
newPath = t.map(path);
newRect = t.mapRect(requestedRect);
d->fillPainterPathImpl(newPath, newRect);
}
if (d->mirrorVertically) {
t = C1 * QTransform::fromScale(1,-1) * C2;
newPath = t.map(path);
newRect = t.mapRect(requestedRect);
d->fillPainterPathImpl(newPath, newRect);
}
if (d->mirrorHorizontally && d->mirrorVertically) {
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(KisFilterConfigurationSP generator)
{
d->generator = generator;
}
const KisFilterConfigurationSP 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;
}
/**
* TODO: Rename this setCompositeOpId(). See KoCompositeOpRegistry.h
*/
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 {
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 mirrorHorizontally, bool mirrorVertically)
{
d->axesCenter = axesCenter;
d->mirrorHorizontally = mirrorHorizontally;
d->mirrorVertically = mirrorVertically;
}
void KisPainter::copyMirrorInformation(KisPainter* painter)
{
painter->setMirrorInformation(d->axesCenter, d->mirrorHorizontally, d->mirrorVertically);
}
bool KisPainter::hasMirroring() const
{
return d->mirrorHorizontally || d->mirrorVertically;
}
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->mirrorHorizontally && !d->mirrorVertically) 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->mirrorHorizontally && !d->mirrorVertically) 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();
KisLodTransform t(d->device);
QPointF effectiveAxesCenter = t.map(d->axesCenter);
int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
if (d->mirrorHorizontally && d->mirrorVertically){
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->mirrorHorizontally){
dab->mirror(true, false);
bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
}
else if (d->mirrorVertically){
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();
KisLodTransform t(d->device);
QPointF effectiveAxesCenter = t.map(d->axesCenter);
int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
if (d->mirrorHorizontally && d->mirrorVertically){
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->mirrorHorizontally){
dab->mirror(true, false);
mask->mirror(true, false);
bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
}else if (d->mirrorVertically){
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->mirrorHorizontally || d->mirrorVertically){
- KisFixedPaintDeviceSP mirrorDab = new KisFixedPaintDevice(dab->colorSpace());
+ 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->mirrorHorizontally || d->mirrorVertically){
- KisFixedPaintDeviceSP mirrorDab = new KisFixedPaintDevice(dab->colorSpace());
+ 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();
KisLodTransform t(d->device);
QPointF effectiveAxesCenter = t.map(d->axesCenter);
int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
rects << rc;
if (d->mirrorHorizontally && d->mirrorVertically){
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->mirrorHorizontally) {
rects << QRect(mirrorX, y, rc.width(), rc.height());
} else if (d->mirrorVertically) {
rects << QRect(x, mirrorY, rc.width(), rc.height());
}
Q_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);
}
Q_FOREACH (const QRect &rc, rects) {
bitBlt(rc.topLeft(), dab, rc);
}
Q_FOREACH (const QRect &rc, rects) {
renderMirrorMask(rc, dab);
}
}
diff --git a/libs/image/kis_pixel_selection.h b/libs/image/kis_pixel_selection.h
index bc2d1354b6..4db95de4ce 100644
--- a/libs/image/kis_pixel_selection.h
+++ b/libs/image/kis_pixel_selection.h
@@ -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.
*/
#ifndef KIS_PIXEL_SELECTION_H_
#define KIS_PIXEL_SELECTION_H_
#include <QRect>
#include <QPainterPath>
#include "kis_types.h"
#include "kis_global.h"
#include "kis_paint_device.h"
#include "kis_selection_component.h"
#include "kis_selection.h"
#include <kritaimage_export.h>
/**
* KisPixelSelection contains a byte-map representation of a layer, where
* the value of a byte signifies whether a corresponding pixel is selected, or not.
*/
class KRITAIMAGE_EXPORT KisPixelSelection : public KisPaintDevice, public KisSelectionComponent
{
public:
/**
* Create a new KisPixelSelection. This selection will not have a
* parent paint device.
*/
- KisPixelSelection(KisDefaultBoundsBaseSP defaultBounds = 0, KisSelectionWSP parentSelection = 0);
+ KisPixelSelection(KisDefaultBoundsBaseSP defaultBounds = KisDefaultBoundsBaseSP(), KisSelectionWSP parentSelection = KisSelectionWSP());
/**
* Copy the selection
*/
KisPixelSelection(const KisPixelSelection& rhs);
virtual ~KisPixelSelection();
KisSelectionComponent* clone(KisSelection*);
const KoColorSpace* compositionSourceColorSpace() const;
bool read(QIODevice *stream);
/**
* Fill the specified rect with the specified selectedness.
*/
void select(const QRect & r, quint8 selectedness = MAX_SELECTED);
/**
* Invert the total selection. This will also invert the default value
* of the selection paint device, from MIN_SELECTED to MAX_SELECTED or
* back.
*/
void invert();
/**
* Set the specified rect to MIN_SELECTED.
*/
void clear(const QRect & r);
/**
* Reset the entire selection. The selectedRect and selectedExactRect
* will be empty. The selection will be completely deselected.
*/
void clear();
/**
* Copies alpha channel form the specified \p src device
*/
void copyAlphaFrom(KisPaintDeviceSP src, const QRect &processRect);
/**
* Apply a selection to the selection using the specified selection mode
*/
void applySelection(KisPixelSelectionSP selection, SelectionAction action);
/// Tests if the rect is totally outside the selection
bool isTotallyUnselected(const QRect & r) const;
/**
* Rough, but fastish way of determining the area
* of the tiles used by the selection.
*/
QRect selectedRect() const;
/**
* Slow, but exact way of determining the rectangle
* that encloses the selection.
*/
QRect selectedExactRect() const;
/**
* @brief outline returns the outline of the current selection
* @return a vector of polygons that can be used to draw the outline
*/
QVector<QPolygon> outline() const;
/**
* Overridden from KisPaintDevice to handle outline cache moves
*/
void moveTo(const QPoint& pt);
using KisPaintDevice::moveTo;
bool isEmpty() const;
QPainterPath outlineCache() const;
bool outlineCacheValid() const;
void recalculateOutlineCache();
void setOutlineCache(const QPainterPath &cache);
void invalidateOutlineCache();
bool thumbnailImageValid() const;
QImage thumbnailImage() const;
QTransform thumbnailImageTransform() const;
void recalculateThumbnailImage(const QColor &maskColor);
void setParentSelection(KisSelectionWSP selection);
KisSelectionWSP parentSelection() const;
virtual void renderToProjection(KisPaintDeviceSP projection);
virtual void renderToProjection(KisPaintDeviceSP projection, const QRect& r);
private:
/**
* Add a selection
*/
void addSelection(KisPixelSelectionSP selection);
/**
* Subtracts a selection
*/
void subtractSelection(KisPixelSelectionSP selection);
/**
* Intersects a selection using min-T-norm for this.
*/
void intersectSelection(KisPixelSelectionSP selection);
private:
// We don't want these methods to be used on selections:
using KisPaintDevice::extent;
using KisPaintDevice::exactBounds;
private:
struct Private;
Private * const m_d;
};
#endif // KIS_PIXEL_SELECTION_H_
diff --git a/libs/image/kis_processing_applicator.cpp b/libs/image/kis_processing_applicator.cpp
index 04ff99aeba..310fba5bc4 100644
--- a/libs/image/kis_processing_applicator.cpp
+++ b/libs/image/kis_processing_applicator.cpp
@@ -1,324 +1,323 @@
/*
* 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.h"
#include "kis_image.h"
#include "kis_node.h"
#include "kis_clone_layer.h"
#include "kis_processing_visitor.h"
#include "commands_new/kis_processing_command.h"
#include "kis_stroke_strategy_undo_command_based.h"
#include "kis_layer_utils.h"
#include "kis_command_utils.h"
class DisableUIUpdatesCommand : public KisCommandUtils::FlipFlopCommand
{
public:
DisableUIUpdatesCommand(KisImageWSP image,
bool finalUpdate)
: FlipFlopCommand(finalUpdate),
m_image(image)
{
}
void init() override {
m_image->disableUIUpdates();
}
void end() override {
m_image->enableUIUpdates();
}
private:
KisImageWSP m_image;
};
class UpdateCommand : public KisCommandUtils::FlipFlopCommand
{
public:
UpdateCommand(KisImageWSP image, KisNodeSP node,
KisProcessingApplicator::ProcessingFlags flags,
bool finalUpdate)
: FlipFlopCommand(finalUpdate),
m_image(image),
m_node(node),
m_flags(flags)
{
}
private:
void init() override {
/**
* We disable all non-centralized updates here. Everything
* should be done by this command's explicit updates.
*
* If you still need third-party updates work, please add a
* flag to the applicator.
*/
m_image->disableDirtyRequests();
}
void end() override {
m_image->enableDirtyRequests();
if(m_flags.testFlag(KisProcessingApplicator::RECURSIVE)) {
m_image->refreshGraphAsync(m_node);
}
m_node->setDirty(m_image->bounds());
updateClones(m_node);
}
void updateClones(KisNodeSP node) {
// simple tail-recursive iteration
KisNodeSP prevNode = node->lastChild();
while(prevNode) {
updateClones(prevNode);
prevNode = prevNode->prevSibling();
}
KisLayer *layer = dynamic_cast<KisLayer*>(m_node.data());
if(layer && layer->hasClones()) {
Q_FOREACH (KisCloneLayerSP clone, layer->registeredClones()) {
if(!clone) continue;
QPoint offset(clone->x(), clone->y());
QRegion dirtyRegion(m_image->bounds());
dirtyRegion -= m_image->bounds().translated(offset);
clone->setDirty(dirtyRegion);
}
}
}
private:
KisImageWSP m_image;
KisNodeSP m_node;
KisProcessingApplicator::ProcessingFlags m_flags;
};
class EmitImageSignalsCommand : public KisCommandUtils::FlipFlopCommand
{
public:
EmitImageSignalsCommand(KisImageWSP image,
KisImageSignalVector emitSignals,
bool finalUpdate)
: FlipFlopCommand(finalUpdate),
m_image(image),
m_emitSignals(emitSignals)
{
}
void end() override {
if (isFinalizing()) {
doUpdate(m_emitSignals);
} else {
KisImageSignalVector reverseSignals;
KisImageSignalVector::iterator i = m_emitSignals.end();
while (i != m_emitSignals.begin()) {
--i;
reverseSignals.append(i->inverted());
}
doUpdate(reverseSignals);
}
}
private:
void doUpdate(KisImageSignalVector emitSignals) {
Q_FOREACH (KisImageSignalType type, emitSignals) {
m_image->signalRouter()->emitNotification(type);
}
}
private:
KisImageWSP m_image;
KisImageSignalVector m_emitSignals;
};
KisProcessingApplicator::KisProcessingApplicator(KisImageWSP image,
KisNodeSP node,
ProcessingFlags flags,
KisImageSignalVector emitSignals,
const KUndo2MagicString &name,
KUndo2CommandExtraData *extraData,
int macroId)
: m_image(image),
m_node(node),
m_flags(flags),
m_emitSignals(emitSignals),
m_finalSignalsEmitted(false)
{
KisStrokeStrategyUndoCommandBased *strategy =
- new KisStrokeStrategyUndoCommandBased(name, false,
- m_image->postExecutionUndoAdapter());
+ new KisStrokeStrategyUndoCommandBased(name, false, m_image.data());
if (m_flags.testFlag(SUPPORTS_WRAPAROUND_MODE)) {
strategy->setSupportsWrapAroundMode(true);
}
if (extraData) {
strategy->setCommandExtraData(extraData);
}
strategy->setMacroId(macroId);
m_strokeId = m_image->startStroke(strategy);
if(!m_emitSignals.isEmpty()) {
applyCommand(new EmitImageSignalsCommand(m_image, m_emitSignals, false), KisStrokeJobData::BARRIER);
}
if(m_flags.testFlag(NO_UI_UPDATES)) {
applyCommand(new DisableUIUpdatesCommand(m_image, false), KisStrokeJobData::BARRIER);
}
if (m_node) {
applyCommand(new UpdateCommand(m_image, m_node, m_flags, false));
}
}
KisProcessingApplicator::~KisProcessingApplicator()
{
}
void KisProcessingApplicator::applyVisitor(KisProcessingVisitorSP visitor,
KisStrokeJobData::Sequentiality sequentiality,
KisStrokeJobData::Exclusivity exclusivity)
{
if(!m_flags.testFlag(RECURSIVE)) {
applyCommand(new KisProcessingCommand(visitor, m_node),
sequentiality, exclusivity);
}
else {
visitRecursively(m_node, visitor, sequentiality, exclusivity);
}
}
void KisProcessingApplicator::applyVisitorAllFrames(KisProcessingVisitorSP visitor,
KisStrokeJobData::Sequentiality sequentiality,
KisStrokeJobData::Exclusivity exclusivity)
{
KisLayerUtils::FrameJobs jobs;
if (m_flags.testFlag(RECURSIVE)) {
KisLayerUtils::updateFrameJobsRecursive(&jobs, m_node);
} else {
KisLayerUtils::updateFrameJobsRecursive(&jobs, m_node);
}
if (jobs.isEmpty()) {
applyVisitor(visitor, sequentiality, exclusivity);
return;
}
KisLayerUtils::FrameJobs::const_iterator it = jobs.constBegin();
KisLayerUtils::FrameJobs::const_iterator end = jobs.constEnd();
KisLayerUtils::SwitchFrameCommand::SharedStorageSP switchFrameStorage(
new KisLayerUtils::SwitchFrameCommand::SharedStorage());
for (; it != end; ++it) {
const int frame = it.key();
const QSet<KisNodeSP> &nodes = it.value();
applyCommand(new KisLayerUtils::SwitchFrameCommand(m_image, frame, false, switchFrameStorage), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
foreach (KisNodeSP node, nodes) {
applyCommand(new KisProcessingCommand(visitor, node),
sequentiality, exclusivity);
}
applyCommand(new KisLayerUtils::SwitchFrameCommand(m_image, frame, true, switchFrameStorage), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
}
}
void KisProcessingApplicator::visitRecursively(KisNodeSP node,
KisProcessingVisitorSP visitor,
KisStrokeJobData::Sequentiality sequentiality,
KisStrokeJobData::Exclusivity exclusivity)
{
// simple tail-recursive iteration
KisNodeSP prevNode = node->lastChild();
while(prevNode) {
visitRecursively(prevNode, visitor, sequentiality, exclusivity);
prevNode = prevNode->prevSibling();
}
applyCommand(new KisProcessingCommand(visitor, node),
sequentiality, exclusivity);
}
void KisProcessingApplicator::applyCommand(KUndo2Command *command,
KisStrokeJobData::Sequentiality sequentiality,
KisStrokeJobData::Exclusivity exclusivity)
{
/*
* One should not add commands after the final signals have been
* emitted, only end or cancel the stroke
*/
KIS_ASSERT_RECOVER_RETURN(!m_finalSignalsEmitted);
m_image->addJob(m_strokeId,
new KisStrokeStrategyUndoCommandBased::Data(KUndo2CommandSP(command),
false,
sequentiality,
exclusivity));
}
void KisProcessingApplicator::explicitlyEmitFinalSignals()
{
KIS_ASSERT_RECOVER_RETURN(!m_finalSignalsEmitted);
if (m_node) {
applyCommand(new UpdateCommand(m_image, m_node, m_flags, true));
}
if(m_flags.testFlag(NO_UI_UPDATES)) {
applyCommand(new DisableUIUpdatesCommand(m_image, true), KisStrokeJobData::BARRIER);
}
if(!m_emitSignals.isEmpty()) {
applyCommand(new EmitImageSignalsCommand(m_image, m_emitSignals, true), KisStrokeJobData::BARRIER);
}
// simple consistency check
m_finalSignalsEmitted = true;
}
void KisProcessingApplicator::end()
{
if (!m_finalSignalsEmitted) {
explicitlyEmitFinalSignals();
}
m_image->endStroke(m_strokeId);
}
void KisProcessingApplicator::cancel()
{
m_image->cancelStroke(m_strokeId);
}
diff --git a/libs/image/kis_processing_information.h b/libs/image/kis_processing_information.h
index 9b14dbe22e..8c3f612c10 100644
--- a/libs/image/kis_processing_information.h
+++ b/libs/image/kis_processing_information.h
@@ -1,78 +1,78 @@
/*
* 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 _KIS_PROCESSING_INFORMATION_H_
#define _KIS_PROCESSING_INFORMATION_H_
#include "kis_types.h"
#include "kritaimage_export.h"
/**
* This class is used in KisFilter to contain information needed to apply a filter
* on a paint device.
* This one have only a const paint device and holds information about the source.
*/
class KRITAIMAGE_EXPORT KisConstProcessingInformation
{
public:
- KisConstProcessingInformation(const KisPaintDeviceSP device, const QPoint& topLeft, const KisSelectionSP selection = 0);
+ KisConstProcessingInformation(const KisPaintDeviceSP device, const QPoint& topLeft, const KisSelectionSP selection);
KisConstProcessingInformation(const KisConstProcessingInformation& _rhs);
KisConstProcessingInformation& operator=(const KisConstProcessingInformation& _rhs);
~KisConstProcessingInformation();
/**
* @return the paint device
*/
const KisPaintDeviceSP paintDevice() const;
/**
* @return the active selection
*/
const KisSelectionSP selection() const;
/**
* @return the top left pixel that need to process
*/
const QPoint& topLeft() const;
private:
struct Private;
Private* const d;
};
/**
* This class is used in KisFilter to contain information needed to apply a filter
* on a paint device.
* This one can have a non const paint device and holds information about the destination.
*/
class KRITAIMAGE_EXPORT KisProcessingInformation : public KisConstProcessingInformation
{
public:
- KisProcessingInformation(KisPaintDeviceSP device, const QPoint& topLeft, const KisSelectionSP selection = 0);
+ KisProcessingInformation(KisPaintDeviceSP device, const QPoint& topLeft, const KisSelectionSP selection);
KisProcessingInformation(const KisProcessingInformation& _rhs);
KisProcessingInformation& operator=(const KisProcessingInformation& _rhs);
~KisProcessingInformation();
/**
* @return the paint device
*/
KisPaintDeviceSP paintDevice();
private:
struct Private;
Private* const d;
};
#endif
diff --git a/libs/image/kis_raster_keyframe_channel.cpp b/libs/image/kis_raster_keyframe_channel.cpp
index 98c1189483..28a0dc4790 100644
--- a/libs/image/kis_raster_keyframe_channel.cpp
+++ b/libs/image/kis_raster_keyframe_channel.cpp
@@ -1,274 +1,274 @@
/*
* Copyright (c) 2015 Jouni Pentikäinen <joupent@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_raster_keyframe_channel.h"
#include "kis_node.h"
#include "kis_dom_utils.h"
#include "kis_global.h"
#include "kis_paint_device.h"
#include "kis_paint_device_frames_interface.h"
#include "kis_time_range.h"
#include "kundo2command.h"
#include "kis_onion_skin_compositor.h"
struct KisRasterKeyframe : public KisKeyframe
{
KisRasterKeyframe(KisRasterKeyframeChannel *channel, int time, int frameId)
: KisKeyframe(channel, time)
, frameId(frameId)
{}
int frameId;
};
struct KisRasterKeyframeChannel::Private
{
Private(KisPaintDeviceWSP paintDevice, const QString filenameSuffix)
: paintDevice(paintDevice),
filenameSuffix(filenameSuffix),
onionSkinsEnabled(false)
{}
KisPaintDeviceWSP paintDevice;
QMap<int, QString> frameFilenames;
QString filenameSuffix;
bool onionSkinsEnabled;
};
KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KoID &id, const KisPaintDeviceWSP paintDevice, KisDefaultBoundsBaseSP defaultBounds)
: KisKeyframeChannel(id, defaultBounds),
m_d(new Private(paintDevice, QString()))
{
}
KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KisRasterKeyframeChannel &rhs, const KisNodeWSP newParentNode, const KisPaintDeviceWSP newPaintDevice)
: KisKeyframeChannel(rhs, newParentNode),
m_d(new Private(newPaintDevice, rhs.m_d->filenameSuffix))
{
KIS_ASSERT_RECOVER_NOOP(&rhs != this);
m_d->frameFilenames = rhs.m_d->frameFilenames;
m_d->onionSkinsEnabled = rhs.m_d->onionSkinsEnabled;
}
KisRasterKeyframeChannel::~KisRasterKeyframeChannel()
{
}
int KisRasterKeyframeChannel::frameId(KisKeyframeSP keyframe) const
{
KisRasterKeyframe *key = dynamic_cast<KisRasterKeyframe*>(keyframe.data());
Q_ASSERT(key != 0);
return key->frameId;
}
int KisRasterKeyframeChannel::frameIdAt(int time) const
{
KisKeyframeSP activeKey = activeKeyframeAt(time);
if (activeKey.isNull()) return -1;
return frameId(activeKey);
}
void KisRasterKeyframeChannel::fetchFrame(KisKeyframeSP keyframe, KisPaintDeviceSP targetDevice)
{
m_d->paintDevice->framesInterface()->fetchFrame(frameId(keyframe), targetDevice);
}
void KisRasterKeyframeChannel::importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand)
{
KisKeyframeSP keyframe = addKeyframe(time, parentCommand);
const int frame = frameId(keyframe);
m_d->paintDevice->framesInterface()->uploadFrame(frame, sourceDevice);
}
QRect KisRasterKeyframeChannel::frameExtents(KisKeyframeSP keyframe)
{
return m_d->paintDevice->framesInterface()->frameBounds(frameId(keyframe));
}
QString KisRasterKeyframeChannel::frameFilename(int frameId) const
{
return m_d->frameFilenames.value(frameId, QString());
}
-void KisRasterKeyframeChannel::setFilenameSuffix(const QString suffix)
+void KisRasterKeyframeChannel::setFilenameSuffix(const QString &suffix)
{
m_d->filenameSuffix = suffix;
}
void KisRasterKeyframeChannel::setFrameFilename(int frameId, const QString &filename)
{
Q_ASSERT(!m_d->frameFilenames.contains(frameId));
m_d->frameFilenames.insert(frameId, filename);
}
QString KisRasterKeyframeChannel::chooseFrameFilename(int frameId, const QString &layerFilename)
{
QString filename;
if (m_d->frameFilenames.isEmpty()) {
// Use legacy naming convention for first keyframe
filename = layerFilename + m_d->filenameSuffix;
} else {
filename = layerFilename + m_d->filenameSuffix + ".f" + QString::number(frameId);
}
setFrameFilename(frameId, filename);
return filename;
}
KisKeyframeSP KisRasterKeyframeChannel::createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand)
{
int srcFrame = (copySrc != 0) ? frameId(copySrc) : 0;
int frameId = m_d->paintDevice->framesInterface()->createFrame((copySrc != 0), srcFrame, QPoint(), parentCommand);
KisKeyframeSP keyframe(new KisRasterKeyframe(this, time, frameId));
return keyframe;
}
void KisRasterKeyframeChannel::destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand)
{
m_d->paintDevice->framesInterface()->deleteFrame(frameId(key), parentCommand);
}
void KisRasterKeyframeChannel::uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame)
{
KisRasterKeyframeChannel *srcRasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(srcChannel);
KIS_ASSERT_RECOVER_RETURN(srcRasterChannel);
const int srcId = srcRasterChannel->frameIdAt(srcTime);
const int dstId = frameId(dstFrame);
m_d->paintDevice->framesInterface()->
uploadFrame(srcId,
dstId,
srcRasterChannel->m_d->paintDevice);
}
QRect KisRasterKeyframeChannel::affectedRect(KisKeyframeSP key)
{
KeyframesMap::iterator it = keys().find(key->time());
QRect rect;
// Calculate changed area as the union of the current and previous keyframe.
// This makes sure there are no artifacts left over from the previous frame
// where the new one doesn't cover the area.
if (it == keys().begin()) {
// Using the *next* keyframe at the start of the timeline avoids artifacts
// when deleting or moving the first key
it++;
} else {
it--;
}
if (it != keys().end()) {
rect = m_d->paintDevice->framesInterface()->frameBounds(frameId(it.value()));
}
rect |= m_d->paintDevice->framesInterface()->frameBounds(frameId(key));
if (m_d->onionSkinsEnabled) {
const QRect dirtyOnionSkinsRect =
KisOnionSkinCompositor::instance()->calculateFullExtent(m_d->paintDevice);
rect |= dirtyOnionSkinsRect;
}
return rect;
}
QDomElement KisRasterKeyframeChannel::toXML(QDomDocument doc, const QString &layerFilename)
{
m_d->frameFilenames.clear();
return KisKeyframeChannel::toXML(doc, layerFilename);
}
void KisRasterKeyframeChannel::loadXML(const QDomElement &channelNode)
{
m_d->frameFilenames.clear();
KisKeyframeChannel::loadXML(channelNode);
}
void KisRasterKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename)
{
int frame = frameId(keyframe);
QString filename = frameFilename(frame);
if (filename.isEmpty()) {
filename = chooseFrameFilename(frame, layerFilename);
}
keyframeElement.setAttribute("frame", filename);
QPoint offset = m_d->paintDevice->framesInterface()->frameOffset(frame);
KisDomUtils::saveValue(&keyframeElement, "offset", offset);
}
KisKeyframeSP KisRasterKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode)
{
int time = keyframeNode.attribute("time").toUInt();
QPoint offset;
KisDomUtils::loadValue(keyframeNode, "offset", &offset);
QString frameFilename = keyframeNode.attribute("frame");
KisKeyframeSP keyframe;
if (m_d->frameFilenames.isEmpty()) {
// First keyframe loaded: use the existing frame
Q_ASSERT(keyframeCount() == 1);
keyframe = constKeys().begin().value();
// Remove from keys. It will get reinserted with new time once we return
keys().remove(keyframe->time());
keyframe->setTime(time);
m_d->paintDevice->framesInterface()->setFrameOffset(frameId(keyframe), offset);
} else {
KUndo2Command tempCommand;
int frameId = m_d->paintDevice->framesInterface()->createFrame(false, 0, offset, &tempCommand);
keyframe = toQShared(new KisRasterKeyframe(this, time, frameId));
}
setFrameFilename(frameId(keyframe), frameFilename);
return keyframe;
}
bool KisRasterKeyframeChannel::hasScalarValue() const
{
return false;
}
void KisRasterKeyframeChannel::setOnionSkinsEnabled(bool value)
{
m_d->onionSkinsEnabled = value;
}
bool KisRasterKeyframeChannel::onionSkinsEnabled() const
{
return m_d->onionSkinsEnabled;
}
diff --git a/libs/image/kis_raster_keyframe_channel.h b/libs/image/kis_raster_keyframe_channel.h
index 5de70800b9..7c275771ec 100644
--- a/libs/image/kis_raster_keyframe_channel.h
+++ b/libs/image/kis_raster_keyframe_channel.h
@@ -1,91 +1,91 @@
/*
* Copyright (c) 2015 Jouni Pentikäinen <joupent@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_RASTER_KEYFRAME_CHANNEL_H
#define _KIS_RASTER_KEYFRAME_CHANNEL_H
#include "kis_keyframe_channel.h"
class KRITAIMAGE_EXPORT KisRasterKeyframeChannel : public KisKeyframeChannel
{
Q_OBJECT
public:
KisRasterKeyframeChannel(const KoID& id, const KisPaintDeviceWSP paintDevice, KisDefaultBoundsBaseSP defaultBounds);
KisRasterKeyframeChannel(const KisRasterKeyframeChannel &rhs, const KisNodeWSP newParentNode, const KisPaintDeviceWSP newPaintDevice);
~KisRasterKeyframeChannel();
public:
/**
* Return the ID of the active frame at a given time. The active frame is
* defined by the keyframe at the given time or the last keyframe before it.
* @param time
* @return active frame id
*/
int frameIdAt(int time) const;
/**
* Copy the active frame at given time to target device.
* @param keyframe keyframe to copy from
* @param targetDevice device to copy the frame to
*/
void fetchFrame(KisKeyframeSP keyframe, KisPaintDeviceSP targetDevice);
/**
* Copy the content of the sourceDevice into a new keyframe at given time
* @param time position of new keyframe
* @param sourceDevice source for content
*/
void importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand);
QRect frameExtents(KisKeyframeSP keyframe);
QString frameFilename(int frameId) const;
/**
* When choosing filenames for frames, this will be appended to the node filename
*/
- void setFilenameSuffix(const QString suffix);
+ void setFilenameSuffix(const QString &suffix);
bool hasScalarValue() const;
QDomElement toXML(QDomDocument doc, const QString &layerFilename);
void loadXML(const QDomElement &channelNode);
void setOnionSkinsEnabled(bool value);
bool onionSkinsEnabled() const;
protected:
KisKeyframeSP createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand);
void destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand);
void uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame);
QRect affectedRect(KisKeyframeSP key);
void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename);
KisKeyframeSP loadKeyframe(const QDomElement &keyframeNode);
private:
void setFrameFilename(int frameId, const QString &filename);
QString chooseFrameFilename(int frameId, const QString &layerFilename);
int frameId(KisKeyframeSP keyframe) const;
struct Private;
QScopedPointer<Private> m_d;
};
#endif
diff --git a/libs/image/kis_regenerate_frame_stroke_strategy.cpp b/libs/image/kis_regenerate_frame_stroke_strategy.cpp
index 0e11686ace..efd43a4471 100644
--- a/libs/image/kis_regenerate_frame_stroke_strategy.cpp
+++ b/libs/image/kis_regenerate_frame_stroke_strategy.cpp
@@ -1,223 +1,251 @@
/*
* 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_regenerate_frame_stroke_strategy.h"
#include <QRegion>
#include "kis_image_interfaces.h"
#include "kis_image_animation_interface.h"
#include "kis_node.h"
#include "kis_image.h"
#include "krita_utils.h"
#include "kis_full_refresh_walker.h"
#include "kis_async_merger.h"
#include "kis_projection_updates_filter.h"
struct KisRegenerateFrameStrokeStrategy::Private
{
Type type;
int frameId;
int previousFrameId;
QRegion dirtyRegion;
KisImageAnimationInterface *interface;
KisProjectionUpdatesFilterSP prevUpdatesFilter;
class Data : public KisStrokeJobData {
public:
Data(KisNodeSP _root, const QRect &_rect, const QRect &_cropRect)
: KisStrokeJobData(CONCURRENT),
root(_root), rect(_rect), cropRect(_cropRect)
{}
KisStrokeJobData* createLodClone(int levelOfDetail) override {
Q_UNUSED(levelOfDetail);
return new KisStrokeJobData(CONCURRENT);
}
KisNodeSP root;
QRect rect;
QRect cropRect;
};
void saveAndResetUpdatesFilter() {
- prevUpdatesFilter = interface->image()->projectionUpdatesFilter();
- interface->image()->setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP());
+ KisImageSP image = interface->image().toStrongRef();
+ if (!image) {
+ return;
+ }
+ prevUpdatesFilter = image->projectionUpdatesFilter();
+ image->setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP());
}
void restoreUpdatesFilter() {
- interface->image()->setProjectionUpdatesFilter(prevUpdatesFilter);
+ KisImageSP image = interface->image().toStrongRef();
+ if (!image) {
+ return;
+ }
+ image->setProjectionUpdatesFilter(prevUpdatesFilter);
prevUpdatesFilter.clear();
}
};
KisRegenerateFrameStrokeStrategy::KisRegenerateFrameStrokeStrategy(int frameId,
const QRegion &dirtyRegion,
KisImageAnimationInterface *interface)
: KisSimpleStrokeStrategy("regenerate_external_frame_stroke"),
m_d(new Private)
{
m_d->type = EXTERNAL_FRAME;
m_d->frameId = frameId;
m_d->dirtyRegion = dirtyRegion;
m_d->interface = interface;
enableJob(JOB_INIT);
enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
enableJob(JOB_CANCEL, true, KisStrokeJobData::BARRIER);
enableJob(JOB_DOSTROKE);
enableJob(JOB_SUSPEND);
enableJob(JOB_RESUME);
setRequestsOtherStrokesToEnd(false);
setClearsRedoOnStart(false);
setCanForgetAboutMe(true);
}
KisRegenerateFrameStrokeStrategy::KisRegenerateFrameStrokeStrategy(KisImageAnimationInterface *interface)
: KisSimpleStrokeStrategy("regenerate_current_frame_stroke", kundo2_i18n("Render Animation")),
m_d(new Private)
{
m_d->type = CURRENT_FRAME;
m_d->frameId = 0;
m_d->dirtyRegion = QRegion();
m_d->interface = interface;
enableJob(JOB_INIT);
enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
enableJob(JOB_CANCEL, true, KisStrokeJobData::BARRIER);
enableJob(JOB_SUSPEND);
enableJob(JOB_RESUME);
// switching frames is a distinct user action, so it should
// cancel the playback or any action easily
setRequestsOtherStrokesToEnd(true);
setClearsRedoOnStart(false);
}
KisRegenerateFrameStrokeStrategy::~KisRegenerateFrameStrokeStrategy()
{
}
void KisRegenerateFrameStrokeStrategy::initStrokeCallback()
{
+ KisImageSP image = m_d->interface->image().toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_d->type == EXTERNAL_FRAME) {
m_d->saveAndResetUpdatesFilter();
- m_d->interface->image()->disableUIUpdates();
+ image->disableUIUpdates();
m_d->interface->saveAndResetCurrentTime(m_d->frameId, &m_d->previousFrameId);
} else if (m_d->type == CURRENT_FRAME) {
m_d->interface->blockFrameInvalidation(true);
- m_d->interface->updatesFacade()->refreshGraphAsync();
+ m_d->interface->updatesFacade()->refreshGraphAsync(KisNodeSP());
}
}
void KisRegenerateFrameStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
Private::Data *d = dynamic_cast<Private::Data*>(data);
KIS_ASSERT(d);
KIS_ASSERT(!m_d->dirtyRegion.isEmpty());
KIS_ASSERT(m_d->type == EXTERNAL_FRAME);
KisBaseRectsWalkerSP walker = new KisFullRefreshWalker(d->cropRect);
walker->collectRects(d->root, d->rect);
KisAsyncMerger merger;
merger.startMerge(*walker);
}
void KisRegenerateFrameStrokeStrategy::finishStrokeCallback()
{
+ KisImageSP image = m_d->interface->image().toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_d->type == EXTERNAL_FRAME) {
m_d->interface->notifyFrameReady();
m_d->interface->restoreCurrentTime(&m_d->previousFrameId);
- m_d->interface->image()->enableUIUpdates();
+ image->enableUIUpdates();
m_d->restoreUpdatesFilter();
} else if (m_d->type == CURRENT_FRAME) {
m_d->interface->blockFrameInvalidation(false);
}
}
void KisRegenerateFrameStrokeStrategy::cancelStrokeCallback()
{
+ KisImageSP image = m_d->interface->image().toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_d->type == EXTERNAL_FRAME) {
m_d->interface->notifyFrameCancelled();
m_d->interface->restoreCurrentTime(&m_d->previousFrameId);
- m_d->interface->image()->enableUIUpdates();
+ image->enableUIUpdates();
m_d->restoreUpdatesFilter();
} else if (m_d->type == CURRENT_FRAME) {
m_d->interface->blockFrameInvalidation(false);
}
}
KisStrokeStrategy* KisRegenerateFrameStrokeStrategy::createLodClone(int levelOfDetail)
{
Q_UNUSED(levelOfDetail);
/**
* We need to regenerate animation frames on LodN level only if
* we are processing current frame. Return dummy stroke otherwise
*/
return m_d->type == CURRENT_FRAME ?
new KisRegenerateFrameStrokeStrategy(m_d->interface) :
new KisSimpleStrokeStrategy("dumb-lodn-KisRegenerateFrameStrokeStrategy");
}
void KisRegenerateFrameStrokeStrategy::suspendStrokeCallback()
{
+ KisImageSP image = m_d->interface->image().toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_d->type == EXTERNAL_FRAME) {
m_d->interface->restoreCurrentTime(&m_d->previousFrameId);
- m_d->interface->image()->enableUIUpdates();
+ image->enableUIUpdates();
m_d->restoreUpdatesFilter();
} else if (m_d->type == CURRENT_FRAME) {
m_d->interface->blockFrameInvalidation(false);
}
}
void KisRegenerateFrameStrokeStrategy::resumeStrokeCallback()
{
+ KisImageSP image = m_d->interface->image().toStrongRef();
+ if (!image) {
+ return;
+ }
if (m_d->type == EXTERNAL_FRAME) {
m_d->saveAndResetUpdatesFilter();
- m_d->interface->image()->disableUIUpdates();
+ image->disableUIUpdates();
m_d->interface->saveAndResetCurrentTime(m_d->frameId, &m_d->previousFrameId);
} else if (m_d->type == CURRENT_FRAME) {
m_d->interface->blockFrameInvalidation(true);
}
}
QList<KisStrokeJobData*> KisRegenerateFrameStrokeStrategy::createJobsData(KisImageWSP _image)
{
using KritaUtils::splitRectIntoPatches;
using KritaUtils::optimalPatchSize;
KisImageSP image = _image;
const QRect cropRect = image->bounds();
QVector<QRect> rects = splitRectIntoPatches(image->bounds(), optimalPatchSize());
QList<KisStrokeJobData*> jobsData;
Q_FOREACH (const QRect &rc, rects) {
jobsData << new Private::Data(image->root(), rc, cropRect);
}
return jobsData;
}
diff --git a/libs/image/kis_selection.cc b/libs/image/kis_selection.cc
index ac237af7fd..278fafce55 100644
--- a/libs/image/kis_selection.cc
+++ b/libs/image/kis_selection.cc
@@ -1,320 +1,320 @@
/*
* Copyright (c) 2004 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_selection.h"
#include "kundo2command.h"
#include "kis_selection_component.h"
#include "kis_pixel_selection.h"
#include "kis_node_graph_listener.h"
#include "kis_node.h"
#include "kis_image.h"
#include "kis_default_bounds.h"
#include "kis_iterator_ng.h"
struct Q_DECL_HIDDEN KisSelection::Private {
Private()
: isVisible(true),
shapeSelection(0)
{
}
// used for forwarding setDirty signals only
KisNodeWSP parentNode;
bool isVisible; //false is the selection decoration should not be displayed
KisDefaultBoundsBaseSP defaultBounds;
KisPixelSelectionSP pixelSelection;
KisSelectionComponent *shapeSelection;
};
KisSelection::KisSelection(KisDefaultBoundsBaseSP defaultBounds)
: m_d(new Private)
{
if (!defaultBounds) {
- defaultBounds = new KisSelectionDefaultBounds();
+ defaultBounds = new KisSelectionDefaultBounds(KisPaintDeviceSP());
}
m_d->defaultBounds = defaultBounds;
m_d->pixelSelection = new KisPixelSelection(m_d->defaultBounds, this);
m_d->pixelSelection->setParentNode(m_d->parentNode);
}
KisSelection::KisSelection(const KisSelection& rhs)
: KisShared(),
m_d(new Private)
{
copyFrom(rhs);
}
KisSelection &KisSelection::operator=(const KisSelection &rhs)
{
if (&rhs != this) {
copyFrom(rhs);
}
return *this;
}
void KisSelection::copyFrom(const KisSelection &rhs)
{
m_d->isVisible = rhs.m_d->isVisible;
m_d->defaultBounds = rhs.m_d->defaultBounds;
m_d->parentNode = 0; // not supposed to be shared
Q_ASSERT(rhs.m_d->pixelSelection);
m_d->pixelSelection = new KisPixelSelection(*rhs.m_d->pixelSelection);
m_d->pixelSelection->setParentSelection(this);
if (rhs.m_d->shapeSelection) {
m_d->shapeSelection = rhs.m_d->shapeSelection->clone(this);
Q_ASSERT(m_d->shapeSelection);
Q_ASSERT(m_d->shapeSelection != rhs.m_d->shapeSelection);
}
else {
m_d->shapeSelection = 0;
}
}
KisSelection::~KisSelection()
{
delete m_d->shapeSelection;
delete m_d;
}
void KisSelection::setParentNode(KisNodeWSP node)
{
m_d->parentNode = node;
m_d->pixelSelection->setParentNode(node);
}
// for testing purposes only
KisNodeWSP KisSelection::parentNode() const
{
return m_d->parentNode;
}
bool KisSelection::outlineCacheValid() const
{
return hasShapeSelection() ||
m_d->pixelSelection->outlineCacheValid();
}
QPainterPath KisSelection::outlineCache() const
{
QPainterPath outline;
if (hasShapeSelection()) {
outline += m_d->shapeSelection->outlineCache();
} else if (m_d->pixelSelection->outlineCacheValid()) {
outline += m_d->pixelSelection->outlineCache();
}
return outline;
}
void KisSelection::recalculateOutlineCache()
{
Q_ASSERT(m_d->pixelSelection);
if (hasShapeSelection()) {
m_d->shapeSelection->recalculateOutlineCache();
} else if (!m_d->pixelSelection->outlineCacheValid()) {
m_d->pixelSelection->recalculateOutlineCache();
}
}
bool KisSelection::thumbnailImageValid() const
{
return m_d->pixelSelection->thumbnailImageValid();
}
void KisSelection::recalculateThumbnailImage(const QColor &maskColor)
{
m_d->pixelSelection->recalculateThumbnailImage(maskColor);
}
QImage KisSelection::thumbnailImage() const
{
return m_d->pixelSelection->thumbnailImage();
}
QTransform KisSelection::thumbnailImageTransform() const
{
return m_d->pixelSelection->thumbnailImageTransform();
}
bool KisSelection::hasPixelSelection() const
{
return m_d->pixelSelection && !m_d->pixelSelection->isEmpty();
}
bool KisSelection::hasShapeSelection() const
{
return m_d->shapeSelection && !m_d->shapeSelection->isEmpty();
}
KisPixelSelectionSP KisSelection::pixelSelection() const
{
return m_d->pixelSelection;
}
KisSelectionComponent* KisSelection::shapeSelection() const
{
return m_d->shapeSelection;
}
void KisSelection::setShapeSelection(KisSelectionComponent* shapeSelection)
{
m_d->shapeSelection = shapeSelection;
}
KisPixelSelectionSP KisSelection::projection() const
{
return m_d->pixelSelection;
}
void KisSelection::updateProjection(const QRect &rc)
{
if(hasShapeSelection()) {
m_d->shapeSelection->renderToProjection(m_d->pixelSelection, rc);
m_d->pixelSelection->setOutlineCache(m_d->shapeSelection->outlineCache());
}
}
void KisSelection::updateProjection()
{
if(hasShapeSelection()) {
m_d->pixelSelection->clear();
m_d->shapeSelection->renderToProjection(m_d->pixelSelection);
m_d->pixelSelection->setOutlineCache(m_d->shapeSelection->outlineCache());
}
}
void KisSelection::setVisible(bool visible)
{
bool needsNotification = visible != m_d->isVisible;
m_d->isVisible = visible;
if (needsNotification) {
notifySelectionChanged();
}
}
bool KisSelection::isVisible()
{
return m_d->isVisible;
}
bool KisSelection::isTotallyUnselected(const QRect & r) const
{
return m_d->pixelSelection->isTotallyUnselected(r);
}
QRect KisSelection::selectedRect() const
{
return m_d->pixelSelection->selectedRect();
}
QRect KisSelection::selectedExactRect() const
{
return m_d->pixelSelection->selectedExactRect();
}
qint32 KisSelection::x() const
{
return m_d->pixelSelection->x();
}
qint32 KisSelection::y() const
{
return m_d->pixelSelection->y();
}
void KisSelection::setX(qint32 x)
{
Q_ASSERT(m_d->pixelSelection);
qint32 delta = x - m_d->pixelSelection->x();
m_d->pixelSelection->setX(x);
if (m_d->shapeSelection) {
m_d->shapeSelection->moveX(delta);
}
}
void KisSelection::setY(qint32 y)
{
Q_ASSERT(m_d->pixelSelection);
qint32 delta = y - m_d->pixelSelection->y();
m_d->pixelSelection->setY(y);
if (m_d->shapeSelection) {
m_d->shapeSelection->moveY(delta);
}
}
void KisSelection::setDefaultBounds(KisDefaultBoundsBaseSP bounds)
{
m_d->defaultBounds = bounds;
m_d->pixelSelection->setDefaultBounds(bounds);
}
void KisSelection::clear()
{
// FIXME: check whether this is safe
delete m_d->shapeSelection;
m_d->shapeSelection = 0;
m_d->pixelSelection->clear();
}
KUndo2Command* KisSelection::flatten()
{
KUndo2Command *command = 0;
if (hasShapeSelection()) {
command = m_d->shapeSelection->resetToEmpty();
}
return command;
}
void KisSelection::notifySelectionChanged()
{
KisNodeWSP parentNode;
if (!(parentNode = this->parentNode())) return;
KisNodeGraphListener *listener;
if (!(listener = parentNode->graphListener())) return;
listener->notifySelectionChanged();
}
quint8 KisSelection::selected(qint32 x, qint32 y) const
{
KisHLineConstIteratorSP iter = m_d->pixelSelection->createHLineConstIteratorNG(x, y, 1);
const quint8 *pix = iter->oldRawData();
return *pix;
}
diff --git a/libs/image/kis_selection.h b/libs/image/kis_selection.h
index 88f8fa4363..e4dcb1395e 100644
--- a/libs/image/kis_selection.h
+++ b/libs/image/kis_selection.h
@@ -1,219 +1,219 @@
/*
* 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.
*/
#ifndef KIS_SELECTION_H_
#define KIS_SELECTION_H_
#include <QRect>
#include "kis_types.h"
#include "kritaimage_export.h"
#include "kis_default_bounds.h"
#include "kis_image.h"
enum SelectionMode {
PIXEL_SELECTION,
SHAPE_PROTECTION
};
enum SelectionAction {
SELECTION_REPLACE,
SELECTION_ADD,
SELECTION_SUBTRACT,
SELECTION_INTERSECT,
SELECTION_DEFAULT
};
#include "kis_pixel_selection.h"
class KisSelectionComponent;
class QPainterPath;
/**
* KisSelection is a composite object. It may contain an instance
* of KisPixelSelection and a KisShapeSelection object. Both these
* selections are merged into a projection of the KisSelection.
*
* Every pixel in the paint device can indicate a degree of selectedness, varying
* between MIN_SELECTED and MAX_SELECTED.
*
* The projection() paint device itself is only a projection: you can
* read from it, but not write to it. You need to keep track of
* the need for updating the projection yourself: there is no
* automatic updating after changing the contents of one or more
* of the selection components.
*/
class KRITAIMAGE_EXPORT KisSelection : public KisShared
{
public:
/**
* Create a new KisSelection.
*
* @param defaultBounds defines the bounds of the selection when
* Select All is initiated.
*/
- KisSelection(KisDefaultBoundsBaseSP defaultBounds = 0);
+ KisSelection(KisDefaultBoundsBaseSP defaultBounds = KisDefaultBoundsBaseSP());
/**
* Copy the selection. The selection components are copied, too.
*/
KisSelection(const KisSelection& rhs);
KisSelection& operator=(const KisSelection &rhs);
/**
* Delete the selection. The shape selection component is deleted, the
* pixel selection component is contained in a shared pointer, so that
* may still be valid.
*/
virtual ~KisSelection();
/**
* The paint device of the pixel selection should report
* about it's setDirty events to its parent. The creator
* should set the parent manually if it wants to get the
* signals
*/
void setParentNode(KisNodeWSP node);
bool hasPixelSelection() const;
bool hasShapeSelection() const;
bool outlineCacheValid() const;
QPainterPath outlineCache() const;
void recalculateOutlineCache();
/**
* Tells whether the cached thumbnail of the selection is still valid
*/
bool thumbnailImageValid() const;
/**
* Recalculates the thumbnail of the selection
*/
void recalculateThumbnailImage(const QColor &maskColor);
/**
* Returns the thumbnail of the selection.
*/
QImage thumbnailImage() const;
/**
* Returns the transformation which should be applied to the thumbnail before
* being painted over the image
*/
QTransform thumbnailImageTransform() const;
/**
* return the pixel selection component of this selection. Pixel
* selection component is always present in the selection. In case
* the user wants a vector selection, pixel selection will store
* the pixelated version of it.
*
* NOTE: use pixelSelection() for changing the selection only. For
* reading the selection and passing the data to bitBlt function use
* projection(). Although projection() and pixelSelection() currently
* point ot the same paint device, this behavior may change in the
* future.
*/
KisPixelSelectionSP pixelSelection() const;
/**
* return the vector selection component of this selection or zero
* if hasShapeSelection() returns false.
*/
KisSelectionComponent* shapeSelection() const;
void setShapeSelection(KisSelectionComponent* shapeSelection);
/**
* Returns the projection of the selection. It may be the same
* as pixel selection. You must read selection data from this
* paint device only
*/
KisPixelSelectionSP projection() const;
/**
* Updates the projection of the selection. You should call this
* method after the every change of the selection components.
* There is no automatic updates framework present
*/
void updateProjection(const QRect& rect);
void updateProjection();
void setVisible(bool visible);
bool isVisible();
/**
* Convenience functions. Just call the corresponding methods
* of the underlying projection
*/
bool isTotallyUnselected(const QRect & r) const;
QRect selectedRect() const;
/**
* @brief Slow, but exact way of determining the rectangle
* that encloses the selection.
*
* Default pixel of the selection device may vary and you would get wrong bounds.
* selectedExactRect() handles all these cases.
*
*/
QRect selectedExactRect() const;
void setX(qint32 x);
void setY(qint32 y);
qint32 x() const;
qint32 y() const;
void setDefaultBounds(KisDefaultBoundsBaseSP bounds);
void clear();
/**
* @brief flatten creates a new pixel selection component from the shape selection
* and throws away the shape selection. This has no effect if there is no
* shape selection.
*/
KUndo2Command* flatten();
void notifySelectionChanged();
/// XXX: This method was marked KDE_DEPRECATED but without information on what to
/// replace it with. Undeprecate, therefore.
quint8 selected(qint32 x, qint32 y) const;
private:
friend class KisSelectionTest;
friend class KisMaskTest;
friend class KisAdjustmentLayerTest;
KisNodeWSP parentNode() const;
void copyFrom(const KisSelection &rhs);
private:
struct Private;
Private * const m_d;
};
#endif // KIS_SELECTION_H_
diff --git a/libs/image/kis_selection_based_layer.cpp b/libs/image/kis_selection_based_layer.cpp
index d30feb5a55..09daf2217e 100644
--- a/libs/image/kis_selection_based_layer.cpp
+++ b/libs/image/kis_selection_based_layer.cpp
@@ -1,303 +1,324 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* 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_selection_based_layer.h"
#include <klocalizedstring.h>
#include "kis_debug.h"
#include <KoCompositeOpRegistry.h>
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_default_bounds.h"
#include "kis_selection.h"
#include "kis_pixel_selection.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter.h"
#include "kis_raster_keyframe_channel.h"
struct Q_DECL_HIDDEN KisSelectionBasedLayer::Private
{
public:
Private() : useSelectionInProjection(true) {}
KisSelectionSP selection;
KisPaintDeviceSP paintDevice;
bool useSelectionInProjection;
};
KisSelectionBasedLayer::KisSelectionBasedLayer(KisImageWSP image,
const QString &name,
KisSelectionSP selection,
KisFilterConfigurationSP filterConfig,
bool useGeneratorRegistry)
: KisLayer(image.data(), name, OPACITY_OPAQUE_U8),
KisNodeFilterInterface(filterConfig, useGeneratorRegistry),
m_d(new Private())
{
if (!selection)
initSelection();
else
setInternalSelection(selection);
-
- m_d->paintDevice = new KisPaintDevice(this, image->colorSpace(), new KisDefaultBounds(image));
- connect(image.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(slotImageSizeChanged()));
+ KisImageSP imageSP = image.toStrongRef();
+ if (!imageSP) {
+ return;
+ }
+ m_d->paintDevice = KisPaintDeviceSP(new KisPaintDevice(this, imageSP->colorSpace(), KisDefaultBoundsSP(new KisDefaultBounds(image))));
+ connect(imageSP.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(slotImageSizeChanged()));
}
KisSelectionBasedLayer::KisSelectionBasedLayer(const KisSelectionBasedLayer& rhs)
: KisLayer(rhs)
, KisIndirectPaintingSupport()
, KisNodeFilterInterface(rhs)
, m_d(new Private())
{
setInternalSelection(rhs.m_d->selection);
m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data());
}
KisSelectionBasedLayer::~KisSelectionBasedLayer()
{
delete m_d;
}
void KisSelectionBasedLayer::initSelection()
{
- m_d->selection = new KisSelection(new KisDefaultBounds(image()));
+ m_d->selection = KisSelectionSP(new KisSelection(KisDefaultBoundsSP(new KisDefaultBounds(image()))));
m_d->selection->pixelSelection()->setDefaultPixel(KoColor(Qt::white, m_d->selection->pixelSelection()->colorSpace()));
m_d->selection->setParentNode(this);
m_d->selection->updateProjection();
}
void KisSelectionBasedLayer::slotImageSizeChanged()
{
if (m_d->selection) {
/**
* Make sure exactBounds() of the selection got recalculated after
* the image changed
*/
m_d->selection->pixelSelection()->setDirty();
setDirty();
}
}
void KisSelectionBasedLayer::setImage(KisImageWSP image)
{
- m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image));
+ m_d->paintDevice->setDefaultBounds(KisDefaultBoundsSP(new KisDefaultBounds(image)));
KisLayer::setImage(image);
connect(image.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(slotImageSizeChanged()));
}
bool KisSelectionBasedLayer::allowAsChild(KisNodeSP node) const
{
return node->inherits("KisMask");
}
KisPaintDeviceSP KisSelectionBasedLayer::original() const
{
return m_d->paintDevice;
}
KisPaintDeviceSP KisSelectionBasedLayer::paintDevice() const
{
return m_d->selection->pixelSelection();
}
bool KisSelectionBasedLayer::needProjection() const
{
return m_d->selection;
}
void KisSelectionBasedLayer::setUseSelectionInProjection(bool value) const
{
m_d->useSelectionInProjection = value;
}
KisSelectionSP KisSelectionBasedLayer::fetchComposedInternalSelection(const QRect &rect) const
{
- if (!m_d->selection) return 0;
+ if (!m_d->selection) return KisSelectionSP();
m_d->selection->updateProjection(rect);
KisSelectionSP tempSelection = m_d->selection;
KisIndirectPaintingSupport::ReadLocker l(this);
if (hasTemporaryTarget()) {
/**
* Cloning a selection with COW
* FIXME: check whether it's faster than usual bitBlt'ing
*/
tempSelection = new KisSelection(*tempSelection);
KisPainter gc2(tempSelection->pixelSelection());
setupTemporaryPainter(&gc2);
gc2.bitBlt(rect.topLeft(), temporaryTarget(), rect);
}
return tempSelection;
}
void KisSelectionBasedLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const
{
KisSelectionSP tempSelection;
if (m_d->useSelectionInProjection) {
tempSelection = fetchComposedInternalSelection(rect);
}
projection->clear(rect);
KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect, tempSelection);
}
QRect KisSelectionBasedLayer::cropChangeRectBySelection(const QRect &rect) const
{
return m_d->selection ?
rect & m_d->selection->selectedRect() :
rect;
}
QRect KisSelectionBasedLayer::needRect(const QRect &rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
return rect;
}
void KisSelectionBasedLayer::resetCache(const KoColorSpace *colorSpace)
{
+ KisImageSP imageSP = image().toStrongRef();
+ if (!imageSP) {
+ return;
+ }
if (!colorSpace)
- colorSpace = image()->colorSpace();
+ colorSpace = imageSP->colorSpace();
if (!m_d->paintDevice ||
*m_d->paintDevice->colorSpace() != *colorSpace) {
- m_d->paintDevice = new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image()));
+ m_d->paintDevice = KisPaintDeviceSP(new KisPaintDevice(KisNodeWSP(this), colorSpace, new KisDefaultBounds(image())));
} else {
m_d->paintDevice->clear();
}
}
KisSelectionSP KisSelectionBasedLayer::internalSelection() const
{
return m_d->selection;
}
void KisSelectionBasedLayer::setInternalSelection(KisSelectionSP selection)
{
if (selection) {
m_d->selection = new KisSelection(*selection.data());
m_d->selection->setParentNode(this);
m_d->selection->updateProjection();
} else {
m_d->selection = 0;
}
- if (selection->pixelSelection()->defaultBounds()->bounds() != image()->bounds()) {
+ KisImageSP imageSP = image().toStrongRef();
+ if (!imageSP) {
+ return;
+ }
+ if (selection->pixelSelection()->defaultBounds()->bounds() != imageSP->bounds()) {
qWarning() << "WARNING: KisSelectionBasedLayer::setInternalSelection"
<< "New selection has suspicious default bounds";
qWarning() << "WARNING:" << ppVar(selection->pixelSelection()->defaultBounds()->bounds());
- qWarning() << "WARNING:" << ppVar(image()->bounds());
+ qWarning() << "WARNING:" << ppVar(imageSP->bounds());
}
}
qint32 KisSelectionBasedLayer::x() const
{
return m_d->selection ? m_d->selection->x() : 0;
}
qint32 KisSelectionBasedLayer::y() const
{
return m_d->selection ? m_d->selection->y() : 0;
}
void KisSelectionBasedLayer::setX(qint32 x)
{
if (m_d->selection) {
m_d->selection->setX(x);
}
}
void KisSelectionBasedLayer::setY(qint32 y)
{
if (m_d->selection) {
m_d->selection->setY(y);
}
}
void KisSelectionBasedLayer::setDirty(const QRect & rect)
{
KisLayer::setDirty(rect);
}
KisKeyframeChannel *KisSelectionBasedLayer::requestKeyframeChannel(const QString &id)
{
if (id == KisKeyframeChannel::Content.id()) {
KisRasterKeyframeChannel *contentChannel = m_d->selection->pixelSelection()->createKeyframeChannel(KisKeyframeChannel::Content);
contentChannel->setFilenameSuffix(".pixelselection");
return contentChannel;
}
return KisLayer::requestKeyframeChannel(id);
}
void KisSelectionBasedLayer::setDirty()
{
Q_ASSERT(image());
-
- setDirty(image()->bounds());
+ KisImageSP imageSP = image().toStrongRef();
+ if (!imageSP) {
+ return;
+ }
+ setDirty(imageSP->bounds());
}
QRect KisSelectionBasedLayer::extent() const
{
Q_ASSERT(image());
-
+ KisImageSP imageSP = image().toStrongRef();
+ if (!imageSP) {
+ return QRect();
+ }
return m_d->selection ?
- m_d->selection->selectedRect() : image()->bounds();
+ m_d->selection->selectedRect() : imageSP->bounds();
}
QRect KisSelectionBasedLayer::exactBounds() const
{
Q_ASSERT(image());
+ KisImageSP imageSP = image().toStrongRef();
+ if (!imageSP) {
+ return QRect();
+ }
return m_d->selection ?
- m_d->selection->selectedExactRect() : image()->bounds();
+ m_d->selection->selectedExactRect() : imageSP->bounds();
}
QImage KisSelectionBasedLayer::createThumbnail(qint32 w, qint32 h)
{
KisSelectionSP originalSelection = internalSelection();
KisPaintDeviceSP originalDevice = original();
return originalDevice && originalSelection ?
originalDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()) :
QImage();
}
diff --git a/libs/image/kis_stroke.cpp b/libs/image/kis_stroke.cpp
index 0cd90d4064..d940602317 100644
--- a/libs/image/kis_stroke.cpp
+++ b/libs/image/kis_stroke.cpp
@@ -1,306 +1,315 @@
/*
* 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.h"
#include "kis_stroke_strategy.h"
KisStroke::KisStroke(KisStrokeStrategy *strokeStrategy, Type type, int levelOfDetail)
: m_strokeStrategy(strokeStrategy),
m_strokeInitialized(false),
m_strokeEnded(false),
m_strokeSuspended(false),
m_isCancelled(false),
m_prevJobSequential(false),
m_worksOnLevelOfDetail(levelOfDetail),
m_type(type)
{
m_initStrategy.reset(m_strokeStrategy->createInitStrategy());
m_dabStrategy.reset(m_strokeStrategy->createDabStrategy());
m_cancelStrategy.reset(m_strokeStrategy->createCancelStrategy());
m_finishStrategy.reset(m_strokeStrategy->createFinishStrategy());
m_suspendStrategy.reset(m_strokeStrategy->createSuspendStrategy());
m_resumeStrategy.reset(m_strokeStrategy->createResumeStrategy());
if(!m_initStrategy) {
m_strokeInitialized = true;
}
else {
enqueue(m_initStrategy.data(), m_strokeStrategy->createInitData());
}
}
KisStroke::~KisStroke()
{
Q_ASSERT(m_strokeEnded);
Q_ASSERT(m_jobsQueue.isEmpty());
}
bool KisStroke::supportsSuspension()
{
return !m_strokeInitialized || (m_suspendStrategy && m_resumeStrategy);
}
void KisStroke::suspendStroke(KisStrokeSP recipient)
{
if (!m_strokeInitialized || m_strokeSuspended ||
(m_strokeEnded && !hasJobs())) {
return;
}
KIS_ASSERT_RECOVER_NOOP(m_suspendStrategy && m_resumeStrategy);
prepend(m_resumeStrategy.data(),
m_strokeStrategy->createResumeData(),
worksOnLevelOfDetail(), false);
recipient->prepend(m_suspendStrategy.data(),
m_strokeStrategy->createSuspendData(),
worksOnLevelOfDetail(), false);
m_strokeSuspended = true;
}
void KisStroke::addJob(KisStrokeJobData *data)
{
Q_ASSERT(!m_strokeEnded || m_isCancelled);
enqueue(m_dabStrategy.data(), data);
}
KisStrokeJob* KisStroke::popOneJob()
{
KisStrokeJob *job = dequeue();
if(job) {
m_prevJobSequential = job->isSequential() || job->isBarrier();
m_strokeInitialized = true;
m_strokeSuspended = false;
}
return job;
}
KUndo2MagicString KisStroke::name() const
{
return m_strokeStrategy->name();
}
bool KisStroke::hasJobs() const
{
return !m_jobsQueue.isEmpty();
}
qint32 KisStroke::numJobs() const
{
return m_jobsQueue.size();
}
void KisStroke::endStroke()
{
Q_ASSERT(!m_strokeEnded);
m_strokeEnded = true;
enqueue(m_finishStrategy.data(), m_strokeStrategy->createFinishData());
}
/**
* About cancelling the stroke
* There may be four different states of the stroke, when cancel
* is requested:
* 1) Not initialized, has jobs -- just clear the queue
* 2) Initialized, has jobs, not finished -- clear the queue,
* enqueue the cancel job
* 5) Initialized, no jobs, not finished -- enqueue the cancel job
* 3) Initialized, has jobs, finished -- clear the queue, enqueue
* the cancel job
* 4) Initialized, no jobs, finished -- it's too late to cancel
* anything
* 6) Initialized, has jobs, cancelled -- cancelling twice is a permitted
* operation, though it does nothing
*/
void KisStroke::cancelStroke()
{
// case 6
if (m_isCancelled) return;
- if(!m_strokeInitialized) {
+ const bool effectivelyInitialized =
+ m_strokeInitialized || m_strokeStrategy->needsExplicitCancel();
+
+ if(!effectivelyInitialized) {
/**
* Lod0 stroke cannot be suspended and !initialized at the
* same time, because the suspend job is created iff the
* stroke has already done some meaningful work.
*
* At the same time, LodN stroke can be prepended with a
* 'suspend' job even when it has not been started yet. That
* is obvious: we should suspend the other stroke before doing
* anything else.
*/
KIS_ASSERT_RECOVER_NOOP(type() == LODN ||
sanityCheckAllJobsAreCancellable());
clearQueueOnCancel();
}
- else if(m_strokeInitialized &&
+ else if(effectivelyInitialized &&
(!m_jobsQueue.isEmpty() || !m_strokeEnded)) {
clearQueueOnCancel();
enqueue(m_cancelStrategy.data(),
m_strokeStrategy->createCancelData());
}
// else {
// too late ...
// }
m_isCancelled = true;
m_strokeEnded = true;
}
+bool KisStroke::canCancel() const
+{
+ return m_isCancelled || !m_strokeInitialized ||
+ !m_jobsQueue.isEmpty() || !m_strokeEnded;
+}
+
bool KisStroke::sanityCheckAllJobsAreCancellable() const
{
Q_FOREACH (KisStrokeJob *item, m_jobsQueue) {
if (!item->isCancellable()) {
return false;
}
}
return true;
}
void KisStroke::clearQueueOnCancel()
{
QQueue<KisStrokeJob*>::iterator it = m_jobsQueue.begin();
while (it != m_jobsQueue.end()) {
if ((*it)->isCancellable()) {
delete (*it);
it = m_jobsQueue.erase(it);
} else {
++it;
}
}
}
bool KisStroke::isInitialized() const
{
return m_strokeInitialized;
}
bool KisStroke::isEnded() const
{
return m_strokeEnded;
}
bool KisStroke::isCancelled() const
{
return m_isCancelled;
}
bool KisStroke::isExclusive() const
{
return m_strokeStrategy->isExclusive();
}
bool KisStroke::supportsWrapAroundMode() const
{
return m_strokeStrategy->supportsWrapAroundMode();
}
int KisStroke::worksOnLevelOfDetail() const
{
return m_worksOnLevelOfDetail;
}
bool KisStroke::canForgetAboutMe() const
{
return m_strokeStrategy->canForgetAboutMe();
}
bool KisStroke::prevJobSequential() const
{
return m_prevJobSequential;
}
bool KisStroke::nextJobSequential() const
{
return !m_jobsQueue.isEmpty() ?
m_jobsQueue.head()->isSequential() : false;
}
bool KisStroke::nextJobBarrier() const
{
return !m_jobsQueue.isEmpty() ?
m_jobsQueue.head()->isBarrier() : false;
}
void KisStroke::enqueue(KisStrokeJobStrategy *strategy,
KisStrokeJobData *data)
{
// factory methods can return null, if no action is needed
if(!strategy) {
delete data;
return;
}
m_jobsQueue.enqueue(new KisStrokeJob(strategy, data, worksOnLevelOfDetail(), true));
}
void KisStroke::prepend(KisStrokeJobStrategy *strategy,
KisStrokeJobData *data,
int levelOfDetail,
bool isCancellable)
{
// factory methods can return null, if no action is needed
if(!strategy) {
delete data;
return;
}
// LOG_MERGE_FIXME:
Q_UNUSED(levelOfDetail);
m_jobsQueue.prepend(new KisStrokeJob(strategy, data, worksOnLevelOfDetail(), isCancellable));
}
KisStrokeJob* KisStroke::dequeue()
{
return !m_jobsQueue.isEmpty() ? m_jobsQueue.dequeue() : 0;
}
void KisStroke::setLodBuddy(KisStrokeSP buddy)
{
m_lodBuddy = buddy;
}
KisStrokeSP KisStroke::lodBuddy() const
{
return m_lodBuddy;
}
KisStroke::Type KisStroke::type() const
{
if (m_type == LOD0) {
KIS_ASSERT_RECOVER_NOOP(m_lodBuddy && "LOD0 strokes must always have a buddy");
} else if (m_type == LODN) {
KIS_ASSERT_RECOVER_NOOP(m_worksOnLevelOfDetail > 0 && "LODN strokes must work on LOD > 0!");
} else if (m_type == LEGACY) {
KIS_ASSERT_RECOVER_NOOP(m_worksOnLevelOfDetail == 0 && "LEGACY strokes must work on LOD == 0!");
}
return m_type;
}
diff --git a/libs/image/kis_stroke.h b/libs/image/kis_stroke.h
index 9d33e834cf..196c9a6bda 100644
--- a/libs/image/kis_stroke.h
+++ b/libs/image/kis_stroke.h
@@ -1,125 +1,127 @@
/*
* 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_STROKE_H
#define __KIS_STROKE_H
#include <QQueue>
#include <QScopedPointer>
#include <kis_types.h>
#include "kritaimage_export.h"
#include "kis_stroke_job.h"
class KisStrokeStrategy;
class KUndo2MagicString;
class KRITAIMAGE_EXPORT KisStroke
{
public:
enum Type {
LEGACY,
LOD0,
LODN,
SUSPEND,
RESUME
};
public:
KisStroke(KisStrokeStrategy *strokeStrategy, Type type = LEGACY, int levelOfDetail = 0);
~KisStroke();
void addJob(KisStrokeJobData *data);
KUndo2MagicString name() const;
bool hasJobs() const;
qint32 numJobs() const;
KisStrokeJob* popOneJob();
void endStroke();
void cancelStroke();
+ bool canCancel() const;
+
bool supportsSuspension();
void suspendStroke(KisStrokeSP recipient);
bool isInitialized() const;
bool isEnded() const;
bool isCancelled() const;
bool isExclusive() const;
bool supportsWrapAroundMode() const;
int worksOnLevelOfDetail() const;
bool canForgetAboutMe() const;
bool prevJobSequential() const;
bool nextJobSequential() const;
bool nextJobBarrier() const;
void setLodBuddy(KisStrokeSP buddy);
KisStrokeSP lodBuddy() const;
Type type() const;
private:
void enqueue(KisStrokeJobStrategy *strategy,
KisStrokeJobData *data);
// for suspend/resume jobs
void prepend(KisStrokeJobStrategy *strategy,
KisStrokeJobData *data,
int levelOfDetail,
bool isCancellable);
KisStrokeJob* dequeue();
void clearQueueOnCancel();
bool sanityCheckAllJobsAreCancellable() const;
private:
// for testing use only, do not use in real code
friend class KisStrokeTest;
friend class KisStrokeStrategyUndoCommandBasedTest;
QQueue<KisStrokeJob*>& testingGetQueue() {
return m_jobsQueue;
}
private:
// the strategies are owned by the stroke
QScopedPointer<KisStrokeStrategy> m_strokeStrategy;
QScopedPointer<KisStrokeJobStrategy> m_initStrategy;
QScopedPointer<KisStrokeJobStrategy> m_dabStrategy;
QScopedPointer<KisStrokeJobStrategy> m_cancelStrategy;
QScopedPointer<KisStrokeJobStrategy> m_finishStrategy;
QScopedPointer<KisStrokeJobStrategy> m_suspendStrategy;
QScopedPointer<KisStrokeJobStrategy> m_resumeStrategy;
QQueue<KisStrokeJob*> m_jobsQueue;
bool m_strokeInitialized;
bool m_strokeEnded;
bool m_strokeSuspended;
bool m_isCancelled; // cancelled strokes are always 'ended' as well
bool m_prevJobSequential;
int m_worksOnLevelOfDetail;
Type m_type;
KisStrokeSP m_lodBuddy;
};
#endif /* __KIS_STROKE_H */
diff --git a/libs/image/kis_stroke_strategy.cpp b/libs/image/kis_stroke_strategy.cpp
index ed17a45b72..cbb6faad9c 100644
--- a/libs/image/kis_stroke_strategy.cpp
+++ b/libs/image/kis_stroke_strategy.cpp
@@ -1,196 +1,208 @@
/*
* 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.h"
#include <KoCompositeOpRegistry.h>
#include "kis_stroke_job_strategy.h"
KisStrokeStrategy::KisStrokeStrategy(QString id, const KUndo2MagicString &name)
: m_exclusive(false),
m_supportsWrapAroundMode(false),
m_needsIndirectPainting(false),
m_indirectPaintingCompositeOp(COMPOSITE_ALPHA_DARKEN),
m_clearsRedoOnStart(true),
m_requestsOtherStrokesToEnd(true),
m_canForgetAboutMe(false),
+ m_needsExplicitCancel(false),
m_id(id),
m_name(name)
{
}
KisStrokeStrategy::KisStrokeStrategy(const KisStrokeStrategy &rhs)
: m_exclusive(rhs.m_exclusive),
m_supportsWrapAroundMode(rhs.m_supportsWrapAroundMode),
m_needsIndirectPainting(rhs.m_needsIndirectPainting),
m_indirectPaintingCompositeOp(rhs.m_indirectPaintingCompositeOp),
m_clearsRedoOnStart(rhs.m_clearsRedoOnStart),
m_requestsOtherStrokesToEnd(rhs.m_requestsOtherStrokesToEnd),
- m_canForgetAboutMe(false),
+ m_canForgetAboutMe(rhs.m_canForgetAboutMe),
+ m_needsExplicitCancel(rhs.m_needsExplicitCancel),
m_id(rhs.m_id),
m_name(rhs.m_name)
{
KIS_ASSERT_RECOVER_NOOP(!rhs.m_cancelStrokeId &&
"After the stroke has been started, no copying must happen");
}
KisStrokeStrategy::~KisStrokeStrategy()
{
}
KisStrokeJobStrategy* KisStrokeStrategy::createInitStrategy()
{
return 0;
}
KisStrokeJobStrategy* KisStrokeStrategy::createFinishStrategy()
{
return 0;
}
KisStrokeJobStrategy* KisStrokeStrategy::createCancelStrategy()
{
return 0;
}
KisStrokeJobStrategy* KisStrokeStrategy::createDabStrategy()
{
return 0;
}
KisStrokeJobStrategy* KisStrokeStrategy::createSuspendStrategy()
{
return 0;
}
KisStrokeJobStrategy* KisStrokeStrategy::createResumeStrategy()
{
return 0;
}
KisStrokeJobData* KisStrokeStrategy::createInitData()
{
return 0;
}
KisStrokeJobData* KisStrokeStrategy::createFinishData()
{
return 0;
}
KisStrokeJobData* KisStrokeStrategy::createCancelData()
{
return 0;
}
KisStrokeJobData* KisStrokeStrategy::createSuspendData()
{
return 0;
}
KisStrokeJobData* KisStrokeStrategy::createResumeData()
{
return 0;
}
KisStrokeStrategy* KisStrokeStrategy::createLodClone(int levelOfDetail)
{
Q_UNUSED(levelOfDetail);
return 0;
}
bool KisStrokeStrategy::isExclusive() const
{
return m_exclusive;
}
bool KisStrokeStrategy::supportsWrapAroundMode() const
{
return m_supportsWrapAroundMode;
}
bool KisStrokeStrategy::needsIndirectPainting() const
{
return m_needsIndirectPainting;
}
QString KisStrokeStrategy::indirectPaintingCompositeOp() const
{
return m_indirectPaintingCompositeOp;
}
QString KisStrokeStrategy::id() const
{
return m_id;
}
KUndo2MagicString KisStrokeStrategy::name() const
{
return m_name;
}
void KisStrokeStrategy::setExclusive(bool value)
{
m_exclusive = value;
}
void KisStrokeStrategy::setSupportsWrapAroundMode(bool value)
{
m_supportsWrapAroundMode = value;
}
void KisStrokeStrategy::setNeedsIndirectPainting(bool value)
{
m_needsIndirectPainting = value;
}
void KisStrokeStrategy::setIndirectPaintingCompositeOp(const QString &id)
{
m_indirectPaintingCompositeOp = id;
}
bool KisStrokeStrategy::clearsRedoOnStart() const
{
return m_clearsRedoOnStart;
}
void KisStrokeStrategy::setClearsRedoOnStart(bool value)
{
m_clearsRedoOnStart = value;
}
bool KisStrokeStrategy::requestsOtherStrokesToEnd() const
{
return m_requestsOtherStrokesToEnd;
}
void KisStrokeStrategy::setRequestsOtherStrokesToEnd(bool value)
{
m_requestsOtherStrokesToEnd = value;
}
bool KisStrokeStrategy::canForgetAboutMe() const
{
return m_canForgetAboutMe;
}
void KisStrokeStrategy::setCanForgetAboutMe(bool value)
{
m_canForgetAboutMe = value;
}
+
+bool KisStrokeStrategy::needsExplicitCancel() const
+{
+ return m_needsExplicitCancel;
+}
+
+void KisStrokeStrategy::setNeedsExplicitCancel(bool value)
+{
+ m_needsExplicitCancel = value;
+}
diff --git a/libs/image/kis_stroke_strategy.h b/libs/image/kis_stroke_strategy.h
index 063243fe93..d38c94ed3b 100644
--- a/libs/image/kis_stroke_strategy.h
+++ b/libs/image/kis_stroke_strategy.h
@@ -1,135 +1,139 @@
/*
* 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_STROKE_STRATEGY_H
#define __KIS_STROKE_STRATEGY_H
#include <QString>
#include "kis_types.h"
#include "kundo2magicstring.h"
#include "kritaimage_export.h"
class KisStrokeJobStrategy;
class KisStrokeJobData;
class KRITAIMAGE_EXPORT KisStrokeStrategy
{
public:
KisStrokeStrategy(QString id = QString(), const KUndo2MagicString &name = KUndo2MagicString());
virtual ~KisStrokeStrategy();
virtual KisStrokeJobStrategy* createInitStrategy();
virtual KisStrokeJobStrategy* createFinishStrategy();
virtual KisStrokeJobStrategy* createCancelStrategy();
virtual KisStrokeJobStrategy* createDabStrategy();
virtual KisStrokeJobStrategy* createSuspendStrategy();
virtual KisStrokeJobStrategy* createResumeStrategy();
virtual KisStrokeJobData* createInitData();
virtual KisStrokeJobData* createFinishData();
virtual KisStrokeJobData* createCancelData();
virtual KisStrokeJobData* createSuspendData();
virtual KisStrokeJobData* createResumeData();
virtual KisStrokeStrategy* createLodClone(int levelOfDetail);
bool isExclusive() const;
bool supportsWrapAroundMode() const;
bool needsIndirectPainting() const;
QString indirectPaintingCompositeOp() const;
/**
* Returns true if mere start of the stroke should cancel all the
* pending redo tasks.
*
* This method should return true in almost all circumstances
* except if we are running an undo or redo stroke.
*/
bool clearsRedoOnStart() const;
/**
* Returns true if the other currently running strokes should be
* politely asked to exit. The default value is 'true'.
*
* The only known exception right now is
* KisRegenerateFrameStrokeStrategy which does not requests ending
* of any actions, since it performs purely background action.
*/
bool requestsOtherStrokesToEnd() const;
/**
* Returns true if the update scheduler can cancel this stroke
* when some other stroke is going to be started. This makes the
* "forgettable" stroke very low priority.
*
* Default is 'false'.
*/
bool canForgetAboutMe() const;
+ bool needsExplicitCancel() const;
+
QString id() const;
KUndo2MagicString name() const;
/**
* Set up by the strokes queue during the stroke initialization
*/
void setCancelStrokeId(KisStrokeId id) { m_cancelStrokeId = id; }
protected:
/**
* The cancel job may populate the stroke with some new jobs
* for cancelling. To achieve this it needs the stroke id.
*
* WARNING: you can't add new jobs in any places other than
* cancel job, because the stroke may be ended in any moment
* by the user and the sequence of jobs will be broken
*/
KisStrokeId cancelStrokeId() { return m_cancelStrokeId; }
// you are not supposed to change these parameters
// after the KisStroke object has been created
void setExclusive(bool value);
void setSupportsWrapAroundMode(bool value);
void setNeedsIndirectPainting(bool value);
void setIndirectPaintingCompositeOp(const QString &id);
void setClearsRedoOnStart(bool value);
void setRequestsOtherStrokesToEnd(bool value);
void setCanForgetAboutMe(bool value);
+ void setNeedsExplicitCancel(bool value);
protected:
/**
* Protected c-tor, used for cloning of hi-level strategies
*/
KisStrokeStrategy(const KisStrokeStrategy &rhs);
private:
bool m_exclusive;
bool m_supportsWrapAroundMode;
bool m_needsIndirectPainting;
QString m_indirectPaintingCompositeOp;
bool m_clearsRedoOnStart;
bool m_requestsOtherStrokesToEnd;
bool m_canForgetAboutMe;
+ bool m_needsExplicitCancel;
QString m_id;
KUndo2MagicString m_name;
KisStrokeId m_cancelStrokeId;
};
#endif /* __KIS_STROKE_STRATEGY_H */
diff --git a/libs/image/kis_stroke_strategy_undo_command_based.cpp b/libs/image/kis_stroke_strategy_undo_command_based.cpp
index 852e7f3f4e..0b0654fad9 100644
--- a/libs/image/kis_stroke_strategy_undo_command_based.cpp
+++ b/libs/image/kis_stroke_strategy_undo_command_based.cpp
@@ -1,170 +1,170 @@
/*
* 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_image_interfaces.h"
#include "kis_post_execution_undo_adapter.h"
#include "commands_new/kis_saved_commands.h"
KisStrokeStrategyUndoCommandBased::
KisStrokeStrategyUndoCommandBased(const KUndo2MagicString &name,
bool undo,
- KisPostExecutionUndoAdapter *undoAdapter,
+ KisStrokeUndoFacade *undoFacade,
KUndo2CommandSP initCommand,
KUndo2CommandSP finishCommand)
: KisSimpleStrokeStrategy("STROKE_UNDO_COMMAND_BASED", name),
m_undo(undo),
m_initCommand(initCommand),
m_finishCommand(finishCommand),
- m_undoAdapter(undoAdapter),
+ m_undoFacade(undoFacade),
m_macroId(-1),
m_macroCommand(0)
{
enableJob(KisSimpleStrokeStrategy::JOB_INIT);
enableJob(KisSimpleStrokeStrategy::JOB_FINISH);
enableJob(KisSimpleStrokeStrategy::JOB_CANCEL);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
}
KisStrokeStrategyUndoCommandBased::
-KisStrokeStrategyUndoCommandBased(const KisStrokeStrategyUndoCommandBased &rhs,
- bool suppressUndo)
+KisStrokeStrategyUndoCommandBased(const KisStrokeStrategyUndoCommandBased &rhs)
: KisSimpleStrokeStrategy(rhs),
m_undo(false),
m_initCommand(rhs.m_initCommand),
m_finishCommand(rhs.m_finishCommand),
- m_undoAdapter(!suppressUndo ? rhs.m_undoAdapter : 0),
+ m_undoFacade(rhs.m_undoFacade),
m_macroCommand(0)
{
KIS_ASSERT_RECOVER_NOOP(!rhs.m_macroCommand &&
!rhs.m_undo &&
"After the stroke has been started, no copying must happen");
}
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());
+ if(m_undoFacade) {
+ m_macroCommand = m_undoFacade->postExecutionUndoAdapter()->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);
+ Q_ASSERT(m_undoFacade);
postProcessToplevelCommand(m_macroCommand);
- m_undoAdapter->addMacro(m_macroCommand);
+ m_undoFacade->postExecutionUndoAdapter()->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) {
+ if (m_undoFacade && m_macroCommand) {
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::setMacroId(int value)
{
m_macroId = value;
}
void KisStrokeStrategyUndoCommandBased::postProcessToplevelCommand(KUndo2Command *command)
{
if (m_commandExtraData) {
command->setExtraData(m_commandExtraData.take());
}
KisSavedMacroCommand *savedCommand = dynamic_cast<KisSavedMacroCommand*>(command);
if (savedCommand) {
savedCommand->setMacroId(m_macroId);
}
}
diff --git a/libs/image/kis_stroke_strategy_undo_command_based.h b/libs/image/kis_stroke_strategy_undo_command_based.h
index 93e13a3ac7..1099dc47df 100644
--- a/libs/image/kis_stroke_strategy_undo_command_based.h
+++ b/libs/image/kis_stroke_strategy_undo_command_based.h
@@ -1,141 +1,140 @@
/*
* 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_STROKE_STRATEGY_UNDO_COMMAND_BASED_H
#define __KIS_STROKE_STRATEGY_UNDO_COMMAND_BASED_H
#include <kundo2command.h>
#include <QVector>
#include <QMutex>
#include "kis_types.h"
#include "kis_simple_stroke_strategy.h"
class KisStrokeJob;
-class KisPostExecutionUndoAdapter;
class KisSavedMacroCommand;
+class KisStrokeUndoFacade;
class KRITAIMAGE_EXPORT KisStrokeStrategyUndoCommandBased : public KisSimpleStrokeStrategy
{
public:
class Data : public KisStrokeJobData {
public:
Data(KUndo2CommandSP _command,
bool _undo = false,
Sequentiality _sequentiality = SEQUENTIAL,
Exclusivity _exclusivity = NORMAL)
: KisStrokeJobData(_sequentiality, _exclusivity),
command(_command),
undo(_undo)
{
}
Data(KUndo2Command *_command,
bool _undo = false,
Sequentiality _sequentiality = SEQUENTIAL,
Exclusivity _exclusivity = NORMAL)
: KisStrokeJobData(_sequentiality, _exclusivity),
command(_command),
undo(_undo)
{
}
KUndo2CommandSP command;
bool undo;
};
public:
KisStrokeStrategyUndoCommandBased(const KUndo2MagicString &name,
bool undo,
- KisPostExecutionUndoAdapter *undoAdapter,
+ KisStrokeUndoFacade *undoFacade,
KUndo2CommandSP initCommand = KUndo2CommandSP(0),
KUndo2CommandSP finishCommand = KUndo2CommandSP(0));
using KisSimpleStrokeStrategy::setExclusive;
void initStrokeCallback();
void finishStrokeCallback();
void cancelStrokeCallback();
void doStrokeCallback(KisStrokeJobData *data);
/**
* Set extra data that will be assigned to the command
* represecting this action. Using extra data has the following
* restrictions:
*
* 1) The \p data must be set *before* the stroke has been started.
* Setting the \p data after the stroke has been started with
* image->startStroke(strokeId) leads to an undefined behaviour.
*
* 2) \p data becomes owned by the strategy/command right after
* setting it. Don't try to change it afterwards.
*/
void setCommandExtraData(KUndo2CommandExtraData *data);
/**
* Sets the id of this action. Will be used for merging the undo commands
*
* The \p value must be set *before* the stroke has been started.
* Setting the \p value after the stroke has been started with
* image->startStroke(strokeId) leads to an undefined behaviour.
*/
void setMacroId(int value);
/**
* The undo-command-based is a low-level strategy, so it allows
* changing its wraparound mode status.
*
* WARNING: the switch must be called *before* the stroke has been
* started! Otherwise the mode will not be activated.
*/
using KisStrokeStrategy::setSupportsWrapAroundMode;
void setUsedWhileUndoRedo(bool value);
protected:
void runAndSaveCommand(KUndo2CommandSP command,
KisStrokeJobData::Sequentiality sequentiality,
KisStrokeJobData::Exclusivity exclusivity);
void notifyCommandDone(KUndo2CommandSP command,
KisStrokeJobData::Sequentiality sequentiality,
KisStrokeJobData::Exclusivity exclusivity);
- KisStrokeStrategyUndoCommandBased(const KisStrokeStrategyUndoCommandBased &rhs,
- bool suppressUndo);
+ KisStrokeStrategyUndoCommandBased(const KisStrokeStrategyUndoCommandBased &rhs);
virtual void postProcessToplevelCommand(KUndo2Command *command);
private:
void executeCommand(KUndo2CommandSP command, bool undo);
private:
bool m_undo;
KUndo2CommandSP m_initCommand;
KUndo2CommandSP m_finishCommand;
- KisPostExecutionUndoAdapter *m_undoAdapter;
+ KisStrokeUndoFacade *m_undoFacade;
QScopedPointer<KUndo2CommandExtraData> m_commandExtraData;
int m_macroId;
// protects done commands only
QMutex m_mutex;
KisSavedMacroCommand *m_macroCommand;
};
#endif /* __KIS_STROKE_STRATEGY_UNDO_COMMAND_BASED_H */
diff --git a/libs/image/kis_strokes_queue.cpp b/libs/image/kis_strokes_queue.cpp
index da4e5adfad..f3e4f629a3 100644
--- a/libs/image/kis_strokes_queue.cpp
+++ b/libs/image/kis_strokes_queue.cpp
@@ -1,616 +1,795 @@
/*
* 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_strokes_queue.h"
#include <QQueue>
#include <QMutex>
#include <QMutexLocker>
#include "kis_stroke.h"
#include "kis_updater_context.h"
#include "kis_stroke_job_strategy.h"
#include "kis_stroke_strategy.h"
+#include "kis_undo_stores.h"
+#include "kis_post_execution_undo_adapter.h"
typedef QQueue<KisStrokeSP> StrokesQueue;
typedef QQueue<KisStrokeSP>::iterator StrokesQueueIterator;
+#include "kis_image_interfaces.h"
+class KisStrokesQueue::LodNUndoStrokesFacade : public KisStrokesFacade
+{
+public:
+ LodNUndoStrokesFacade(KisStrokesQueue *_q) : q(_q) {}
+
+ KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override {
+ return q->startLodNUndoStroke(strokeStrategy);
+ }
+
+ void addJob(KisStrokeId id, KisStrokeJobData *data) override {
+ KisStrokeSP stroke = id.toStrongRef();
+ KIS_SAFE_ASSERT_RECOVER_NOOP(stroke);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!stroke->lodBuddy());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(stroke->type() == KisStroke::LODN);
+
+ q->addJob(id, data);
+ }
+
+ void endStroke(KisStrokeId id) override {
+ KisStrokeSP stroke = id.toStrongRef();
+ KIS_SAFE_ASSERT_RECOVER_NOOP(stroke);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!stroke->lodBuddy());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(stroke->type() == KisStroke::LODN);
+
+ q->endStroke(id);
+ }
+
+ bool cancelStroke(KisStrokeId id) override {
+ Q_UNUSED(id);
+ qFatal("Not implemented");
+ return false;
+ }
+
+private:
+ KisStrokesQueue *q;
+};
+
+
struct Q_DECL_HIDDEN KisStrokesQueue::Private {
- Private()
- : openedStrokesCounter(0),
+ Private(KisStrokesQueue *_q)
+ : q(_q),
+ openedStrokesCounter(0),
needsExclusiveAccess(false),
wrapAroundModeSupported(false),
currentStrokeLoaded(false),
lodNNeedsSynchronization(true),
desiredLevelOfDetail(0),
- nextDesiredLevelOfDetail(0) {}
+ nextDesiredLevelOfDetail(0),
+ lodNStrokesFacade(_q),
+ lodNPostExecutionUndoAdapter(&lodNUndoStore, &lodNStrokesFacade) {}
+ KisStrokesQueue *q;
StrokesQueue strokesQueue;
int openedStrokesCounter;
bool needsExclusiveAccess;
bool wrapAroundModeSupported;
bool currentStrokeLoaded;
bool lodNNeedsSynchronization;
int desiredLevelOfDetail;
int nextDesiredLevelOfDetail;
QMutex mutex;
KisLodSyncStrokeStrategyFactory lod0ToNStrokeStrategyFactory;
KisSuspendResumeStrategyFactory suspendUpdatesStrokeStrategyFactory;
KisSuspendResumeStrategyFactory resumeUpdatesStrokeStrategyFactory;
+ KisSurrogateUndoStore lodNUndoStore;
+ LodNUndoStrokesFacade lodNStrokesFacade;
+ KisPostExecutionUndoAdapter lodNPostExecutionUndoAdapter;
void cancelForgettableStrokes();
void startLod0ToNStroke(int levelOfDetail, bool forgettable);
bool canUseLodN() const;
StrokesQueueIterator findNewLod0Pos();
StrokesQueueIterator findNewLodNPos(KisStrokeSP lodN);
bool shouldWrapInSuspendUpdatesStroke() const;
void switchDesiredLevelOfDetail(bool forced);
bool hasUnfinishedStrokes() const;
+ void tryClearUndoOnStrokeCompletion(KisStrokeSP finishingStroke);
};
KisStrokesQueue::KisStrokesQueue()
- : m_d(new Private)
+ : m_d(new Private(this))
{
}
KisStrokesQueue::~KisStrokesQueue()
{
Q_FOREACH (KisStrokeSP stroke, m_d->strokesQueue) {
stroke->cancelStroke();
}
delete m_d;
}
template <class StrokePair, class StrokesQueue>
typename StrokesQueue::iterator
executeStrokePair(const StrokePair &pair, StrokesQueue &queue, typename StrokesQueue::iterator it, KisStroke::Type type, int levelOfDetail) {
KisStrokeStrategy *strategy = pair.first;
QList<KisStrokeJobData*> jobsData = pair.second;
KisStrokeSP stroke(new KisStroke(strategy, type, levelOfDetail));
strategy->setCancelStrokeId(stroke);
it = queue.insert(it, stroke);
Q_FOREACH (KisStrokeJobData *jobData, jobsData) {
stroke->addJob(jobData);
}
stroke->endStroke();
return it;
}
void KisStrokesQueue::Private::startLod0ToNStroke(int levelOfDetail, bool forgettable)
{
// precondition: lock held!
// precondition: lod > 0
KIS_ASSERT_RECOVER_RETURN(levelOfDetail);
if (!this->lod0ToNStrokeStrategyFactory) return;
KisLodSyncPair syncPair = this->lod0ToNStrokeStrategyFactory(forgettable);
executeStrokePair(syncPair, this->strokesQueue, this->strokesQueue.end(), KisStroke::LODN, levelOfDetail);
this->lodNNeedsSynchronization = false;
}
void KisStrokesQueue::Private::cancelForgettableStrokes()
{
if (!strokesQueue.isEmpty() && !hasUnfinishedStrokes()) {
Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
KIS_ASSERT_RECOVER_NOOP(stroke->isEnded());
if (stroke->canForgetAboutMe()) {
stroke->cancelStroke();
}
}
}
}
bool KisStrokesQueue::Private::canUseLodN() const
{
Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
if (stroke->type() == KisStroke::LEGACY) {
return false;
}
}
return true;
}
bool KisStrokesQueue::Private::shouldWrapInSuspendUpdatesStroke() const
{
Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
if (stroke->isCancelled()) continue;
if (stroke->type() == KisStroke::RESUME) {
/**
* Resuming process is long-running and consists of
* multiple actions, therefore, if it has already started,
* we cannot use it to guard our new stroke, so just skip it.
* see https://phabricator.kde.org/T2542
*/
if (stroke->isInitialized()) continue;
return false;
}
}
return true;
}
StrokesQueueIterator KisStrokesQueue::Private::findNewLod0Pos()
{
StrokesQueueIterator it = strokesQueue.begin();
StrokesQueueIterator end = strokesQueue.end();
for (; it != end; ++it) {
if ((*it)->isCancelled()) continue;
if ((*it)->type() == KisStroke::RESUME) {
// \see a comment in shouldWrapInSuspendUpdatesStroke()
if ((*it)->isInitialized()) continue;
return it;
}
}
return it;
}
StrokesQueueIterator KisStrokesQueue::Private::findNewLodNPos(KisStrokeSP lodN)
{
StrokesQueueIterator it = strokesQueue.begin();
StrokesQueueIterator end = strokesQueue.end();
for (; it != end; ++it) {
if ((*it)->isCancelled()) continue;
if (((*it)->type() == KisStroke::SUSPEND ||
(*it)->type() == KisStroke::RESUME) &&
(*it)->isInitialized()) {
// \see a comment in shouldWrapInSuspendUpdatesStroke()
continue;
}
if ((*it)->type() == KisStroke::LOD0 ||
(*it)->type() == KisStroke::SUSPEND ||
(*it)->type() == KisStroke::RESUME) {
if (it != end && it == strokesQueue.begin()) {
KisStrokeSP head = *it;
if (head->supportsSuspension()) {
head->suspendStroke(lodN);
}
}
return it;
}
}
return it;
}
+KisStrokeId KisStrokesQueue::startLodNUndoStroke(KisStrokeStrategy *strokeStrategy)
+{
+ QMutexLocker locker(&m_d->mutex);
+
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->lodNNeedsSynchronization);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->desiredLevelOfDetail > 0);
+
+ KisStrokeSP buddy(new KisStroke(strokeStrategy, KisStroke::LODN, m_d->desiredLevelOfDetail));
+ strokeStrategy->setCancelStrokeId(buddy);
+ m_d->strokesQueue.insert(m_d->findNewLodNPos(buddy), buddy);
+
+ KisStrokeId id(buddy);
+ m_d->openedStrokesCounter++;
+
+ return id;
+}
+
KisStrokeId KisStrokesQueue::startStroke(KisStrokeStrategy *strokeStrategy)
{
QMutexLocker locker(&m_d->mutex);
KisStrokeSP stroke;
KisStrokeStrategy* lodBuddyStrategy;
m_d->cancelForgettableStrokes();
if (m_d->desiredLevelOfDetail &&
m_d->canUseLodN() &&
(lodBuddyStrategy =
strokeStrategy->createLodClone(m_d->desiredLevelOfDetail))) {
if (m_d->lodNNeedsSynchronization) {
m_d->startLod0ToNStroke(m_d->desiredLevelOfDetail, false);
}
stroke = KisStrokeSP(new KisStroke(strokeStrategy, KisStroke::LOD0, 0));
KisStrokeSP buddy(new KisStroke(lodBuddyStrategy, KisStroke::LODN, m_d->desiredLevelOfDetail));
lodBuddyStrategy->setCancelStrokeId(buddy);
stroke->setLodBuddy(buddy);
m_d->strokesQueue.insert(m_d->findNewLodNPos(buddy), buddy);
if (m_d->shouldWrapInSuspendUpdatesStroke()) {
KisSuspendResumePair suspendPair = m_d->suspendUpdatesStrokeStrategyFactory();
KisSuspendResumePair resumePair = m_d->resumeUpdatesStrokeStrategyFactory();
StrokesQueueIterator it = m_d->findNewLod0Pos();
it = executeStrokePair(resumePair, m_d->strokesQueue, it, KisStroke::RESUME, 0);
it = m_d->strokesQueue.insert(it, stroke);
it = executeStrokePair(suspendPair, m_d->strokesQueue, it, KisStroke::SUSPEND, 0);
} else {
m_d->strokesQueue.insert(m_d->findNewLod0Pos(), stroke);
}
} else {
stroke = KisStrokeSP(new KisStroke(strokeStrategy, KisStroke::LEGACY, 0));
m_d->strokesQueue.enqueue(stroke);
}
KisStrokeId id(stroke);
strokeStrategy->setCancelStrokeId(id);
m_d->openedStrokesCounter++;
if (stroke->type() == KisStroke::LEGACY) {
m_d->lodNNeedsSynchronization = true;
}
return id;
}
void KisStrokesQueue::addJob(KisStrokeId id, KisStrokeJobData *data)
{
QMutexLocker locker(&m_d->mutex);
KisStrokeSP stroke = id.toStrongRef();
Q_ASSERT(stroke);
KisStrokeSP buddy = stroke->lodBuddy();
if (buddy) {
KisStrokeJobData *clonedData =
data->createLodClone(buddy->worksOnLevelOfDetail());
KIS_ASSERT_RECOVER_RETURN(clonedData);
buddy->addJob(clonedData);
}
stroke->addJob(data);
}
void KisStrokesQueue::endStroke(KisStrokeId id)
{
QMutexLocker locker(&m_d->mutex);
KisStrokeSP stroke = id.toStrongRef();
Q_ASSERT(stroke);
stroke->endStroke();
m_d->openedStrokesCounter--;
KisStrokeSP buddy = stroke->lodBuddy();
if (buddy) {
buddy->endStroke();
}
}
bool KisStrokesQueue::cancelStroke(KisStrokeId id)
{
QMutexLocker locker(&m_d->mutex);
KisStrokeSP stroke = id.toStrongRef();
if(stroke) {
stroke->cancelStroke();
m_d->openedStrokesCounter--;
KisStrokeSP buddy = stroke->lodBuddy();
if (buddy) {
buddy->cancelStroke();
}
}
return stroke;
}
bool KisStrokesQueue::Private::hasUnfinishedStrokes() const
{
Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
if (!stroke->isEnded()) {
return true;
}
}
return false;
}
bool KisStrokesQueue::tryCancelCurrentStrokeAsync()
{
bool anythingCanceled = false;
QMutexLocker locker(&m_d->mutex);
/**
* We cancel only ended strokes. This is done to avoid
* handling dangling pointers problem (KisStrokeId). The owner
* of a stroke will cancel the stroke itself if needed.
*/
if (!m_d->strokesQueue.isEmpty() &&
!m_d->hasUnfinishedStrokes()) {
anythingCanceled = true;
Q_FOREACH (KisStrokeSP currentStroke, m_d->strokesQueue) {
KIS_ASSERT_RECOVER_NOOP(currentStroke->isEnded());
currentStroke->cancelStroke();
// we shouldn't cancel buddies...
if (currentStroke->type() == KisStroke::LOD0) {
/**
* If the buddy has already finished, we cannot undo it because
* it doesn't store any undo data. Therefore we just regenerate
* the LOD caches.
*/
m_d->lodNNeedsSynchronization = true;
}
}
}
/**
* NOTE: We do not touch the openedStrokesCounter here since
* we work with closed id's only here
*/
return anythingCanceled;
}
+UndoResult KisStrokesQueue::tryUndoLastStrokeAsync()
+{
+ UndoResult result = UNDO_FAIL;
+
+ QMutexLocker locker(&m_d->mutex);
+
+ std::reverse_iterator<StrokesQueue::ConstIterator> it(m_d->strokesQueue.constEnd());
+ std::reverse_iterator<StrokesQueue::ConstIterator> end(m_d->strokesQueue.constBegin());
+
+ KisStrokeSP lastStroke;
+ KisStrokeSP lastBuddy;
+ bool buddyFound = false;
+
+ for (; it != end; ++it) {
+ if ((*it)->type() == KisStroke::LEGACY) {
+ break;
+ }
+
+ if (!lastStroke && (*it)->type() == KisStroke::LOD0 && !(*it)->isCancelled()) {
+ lastStroke = *it;
+ lastBuddy = lastStroke->lodBuddy();
+
+ KIS_SAFE_ASSERT_RECOVER(lastBuddy) {
+ lastStroke.clear();
+ lastBuddy.clear();
+ break;
+ }
+ }
+
+ KIS_SAFE_ASSERT_RECOVER(!lastStroke || *it == lastBuddy || (*it)->type() != KisStroke::LODN) {
+ lastStroke.clear();
+ lastBuddy.clear();
+ break;
+ }
+
+ if (lastStroke && *it == lastBuddy) {
+ KIS_SAFE_ASSERT_RECOVER(lastBuddy->type() == KisStroke::LODN) {
+ lastStroke.clear();
+ lastBuddy.clear();
+ break;
+ }
+ buddyFound = true;
+ break;
+ }
+ }
+
+ if (!lastStroke) return UNDO_FAIL;
+ if (!lastStroke->isEnded()) return UNDO_FAIL;
+ if (lastStroke->isCancelled()) return UNDO_FAIL;
+
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!buddyFound ||
+ lastStroke->isCancelled() == lastBuddy->isCancelled());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(lastBuddy->isEnded());
+
+ if (!lastStroke->canCancel()) {
+ return UNDO_WAIT;
+ }
+ lastStroke->cancelStroke();
+
+ if (buddyFound && lastBuddy->canCancel()) {
+ lastBuddy->cancelStroke();
+ } else {
+ // TODO: assert that checks that there is no other lodn strokes
+ locker.unlock();
+ m_d->lodNUndoStore.undo();
+ m_d->lodNUndoStore.purgeRedoState();
+ locker.relock();
+ }
+
+ result = UNDO_OK;
+
+ return result;
+}
+
+void KisStrokesQueue::Private::tryClearUndoOnStrokeCompletion(KisStrokeSP finishingStroke)
+{
+ if (finishingStroke->type() != KisStroke::RESUME) return;
+
+ bool hasResumeStrokes = false;
+ bool hasLod0Strokes = false;
+
+ Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
+ if (stroke == finishingStroke) continue;
+
+ hasLod0Strokes |= stroke->type() == KisStroke::LOD0;
+ hasResumeStrokes |= stroke->type() == KisStroke::RESUME;
+ }
+
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!hasLod0Strokes || hasResumeStrokes);
+
+ if (!hasResumeStrokes && !hasLod0Strokes) {
+ lodNUndoStore.clear();
+ }
+}
+
void KisStrokesQueue::processQueue(KisUpdaterContext &updaterContext,
bool externalJobsPending)
{
updaterContext.lock();
m_d->mutex.lock();
while(updaterContext.hasSpareThread() &&
processOneJob(updaterContext,
externalJobsPending));
m_d->mutex.unlock();
updaterContext.unlock();
}
bool KisStrokesQueue::needsExclusiveAccess() const
{
return m_d->needsExclusiveAccess;
}
bool KisStrokesQueue::wrapAroundModeSupported() const
{
return m_d->wrapAroundModeSupported;
}
bool KisStrokesQueue::isEmpty() const
{
QMutexLocker locker(&m_d->mutex);
return m_d->strokesQueue.isEmpty();
}
qint32 KisStrokesQueue::sizeMetric() const
{
QMutexLocker locker(&m_d->mutex);
if(m_d->strokesQueue.isEmpty()) return 0;
// just a rough approximation
return qMax(1, m_d->strokesQueue.head()->numJobs()) * m_d->strokesQueue.size();
}
void KisStrokesQueue::Private::switchDesiredLevelOfDetail(bool forced)
{
if (forced || nextDesiredLevelOfDetail != desiredLevelOfDetail) {
Q_FOREACH (KisStrokeSP stroke, strokesQueue) {
if (stroke->type() != KisStroke::LEGACY)
return;
}
const bool forgettable =
forced && !lodNNeedsSynchronization &&
desiredLevelOfDetail == nextDesiredLevelOfDetail;
desiredLevelOfDetail = nextDesiredLevelOfDetail;
lodNNeedsSynchronization |= !forgettable;
if (desiredLevelOfDetail) {
startLod0ToNStroke(desiredLevelOfDetail, forgettable);
}
}
}
void KisStrokesQueue::explicitRegenerateLevelOfDetail()
{
QMutexLocker locker(&m_d->mutex);
m_d->switchDesiredLevelOfDetail(true);
}
void KisStrokesQueue::setDesiredLevelOfDetail(int lod)
{
QMutexLocker locker(&m_d->mutex);
if (lod == m_d->nextDesiredLevelOfDetail) return;
m_d->nextDesiredLevelOfDetail = lod;
m_d->switchDesiredLevelOfDetail(false);
}
void KisStrokesQueue::notifyUFOChangedImage()
{
QMutexLocker locker(&m_d->mutex);
m_d->lodNNeedsSynchronization = true;
}
+void KisStrokesQueue::debugDumpAllStrokes()
+{
+ QMutexLocker locker(&m_d->mutex);
+
+ qDebug() <<"===";
+ Q_FOREACH (KisStrokeSP stroke, m_d->strokesQueue) {
+ qDebug() << ppVar(stroke->name()) << ppVar(stroke->type()) << ppVar(stroke->numJobs()) << ppVar(stroke->isInitialized()) << ppVar(stroke->isCancelled());
+ }
+ qDebug() <<"===";
+}
+
void KisStrokesQueue::setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory)
{
m_d->lod0ToNStrokeStrategyFactory = factory;
}
void KisStrokesQueue::setSuspendUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory)
{
m_d->suspendUpdatesStrokeStrategyFactory = factory;
}
void KisStrokesQueue::setResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory)
{
m_d->resumeUpdatesStrokeStrategyFactory = factory;
}
+KisPostExecutionUndoAdapter *KisStrokesQueue::lodNPostExecutionUndoAdapter() const
+{
+ return &m_d->lodNPostExecutionUndoAdapter;
+}
+
KUndo2MagicString KisStrokesQueue::currentStrokeName() const
{
QMutexLocker locker(&m_d->mutex);
if(m_d->strokesQueue.isEmpty()) return KUndo2MagicString();
return m_d->strokesQueue.head()->name();
}
bool KisStrokesQueue::hasOpenedStrokes() const
{
QMutexLocker locker(&m_d->mutex);
return m_d->openedStrokesCounter;
}
bool KisStrokesQueue::processOneJob(KisUpdaterContext &updaterContext,
bool externalJobsPending)
{
if(m_d->strokesQueue.isEmpty()) return false;
bool result = false;
qint32 numMergeJobs;
qint32 numStrokeJobs;
updaterContext.getJobsSnapshot(numMergeJobs, numStrokeJobs);
int levelOfDetail = updaterContext.currentLevelOfDetail();
if(checkStrokeState(numStrokeJobs, levelOfDetail) &&
checkExclusiveProperty(numMergeJobs, numStrokeJobs) &&
checkSequentialProperty(numMergeJobs, numStrokeJobs) &&
checkBarrierProperty(numMergeJobs, numStrokeJobs,
externalJobsPending)) {
KisStrokeSP stroke = m_d->strokesQueue.head();
updaterContext.addStrokeJob(stroke->popOneJob());
result = true;
}
return result;
}
bool KisStrokesQueue::checkStrokeState(bool hasStrokeJobsRunning,
int runningLevelOfDetail)
{
KisStrokeSP stroke = m_d->strokesQueue.head();
bool result = false;
/**
* We cannot start/continue a stroke if its LOD differs from
* the one that is running on CPU
*/
bool hasLodCompatibility = checkLevelOfDetailProperty(runningLevelOfDetail);
bool hasJobs = stroke->hasJobs();
/**
* The stroke may be cancelled very fast. In this case it will
* end up in the state:
*
* !stroke->isInitialized() && stroke->isEnded() && !stroke->hasJobs()
*
* This means that !isInitialised() doesn't imply there are any
* jobs present.
*/
if(!stroke->isInitialized() && hasJobs && hasLodCompatibility) {
/**
* It might happen that the stroke got initialized, but its job was not
* started due to some other reasons like exclusivity. Therefore the
* stroke might end up in loaded, but uninitialized state.
*/
if (!m_d->currentStrokeLoaded) {
m_d->needsExclusiveAccess = stroke->isExclusive();
m_d->wrapAroundModeSupported = stroke->supportsWrapAroundMode();
m_d->currentStrokeLoaded = true;
}
result = true;
}
else if(hasJobs && hasLodCompatibility) {
/**
* If the stroke has no initialization phase, then it can
* arrive here unloaded.
*/
if (!m_d->currentStrokeLoaded) {
m_d->needsExclusiveAccess = stroke->isExclusive();
m_d->wrapAroundModeSupported = stroke->supportsWrapAroundMode();
m_d->currentStrokeLoaded = true;
}
result = true;
}
else if(stroke->isEnded() && !hasJobs && !hasStrokeJobsRunning) {
+ m_d->tryClearUndoOnStrokeCompletion(stroke);
+
m_d->strokesQueue.dequeue(); // deleted by shared pointer
m_d->needsExclusiveAccess = false;
m_d->wrapAroundModeSupported = false;
m_d->currentStrokeLoaded = false;
m_d->switchDesiredLevelOfDetail(false);
if(!m_d->strokesQueue.isEmpty()) {
result = checkStrokeState(false, runningLevelOfDetail);
}
}
return result;
}
bool KisStrokesQueue::checkExclusiveProperty(qint32 numMergeJobs,
qint32 numStrokeJobs)
{
if(!m_d->strokesQueue.head()->isExclusive()) return true;
Q_UNUSED(numMergeJobs);
Q_UNUSED(numStrokeJobs);
Q_ASSERT(!(numMergeJobs && numStrokeJobs));
return numMergeJobs == 0;
}
bool KisStrokesQueue::checkSequentialProperty(qint32 numMergeJobs,
qint32 numStrokeJobs)
{
Q_UNUSED(numMergeJobs);
KisStrokeSP stroke = m_d->strokesQueue.head();
if(!stroke->prevJobSequential() && !stroke->nextJobSequential()) return true;
Q_ASSERT(!stroke->prevJobSequential() || numStrokeJobs <= 1);
return numStrokeJobs == 0;
}
bool KisStrokesQueue::checkBarrierProperty(qint32 numMergeJobs,
qint32 numStrokeJobs,
bool externalJobsPending)
{
KisStrokeSP stroke = m_d->strokesQueue.head();
if(!stroke->nextJobBarrier()) return true;
return !numMergeJobs && !numStrokeJobs && !externalJobsPending;
}
bool KisStrokesQueue::checkLevelOfDetailProperty(int runningLevelOfDetail)
{
KisStrokeSP stroke = m_d->strokesQueue.head();
return runningLevelOfDetail < 0 ||
stroke->worksOnLevelOfDetail() == runningLevelOfDetail;
}
diff --git a/libs/image/kis_strokes_queue.h b/libs/image/kis_strokes_queue.h
index 794ca3ce3c..08e8639a56 100644
--- a/libs/image/kis_strokes_queue.h
+++ b/libs/image/kis_strokes_queue.h
@@ -1,90 +1,101 @@
/*
* 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_STROKES_QUEUE_H
#define __KIS_STROKES_QUEUE_H
#include "kritaimage_export.h"
#include "kundo2magicstring.h"
#include "kis_types.h"
#include "kis_stroke_job_strategy.h"
#include "kis_stroke_strategy.h"
#include "kis_stroke_strategy_factory.h"
+#include "kis_strokes_queue_undo_result.h"
class KisUpdaterContext;
class KisStroke;
class KisStrokeStrategy;
class KisStrokeJobData;
+class KisPostExecutionUndoAdapter;
class KRITAIMAGE_EXPORT KisStrokesQueue
{
public:
KisStrokesQueue();
~KisStrokesQueue();
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy);
void addJob(KisStrokeId id, KisStrokeJobData *data);
void endStroke(KisStrokeId id);
bool cancelStroke(KisStrokeId id);
bool tryCancelCurrentStrokeAsync();
+ UndoResult tryUndoLastStrokeAsync();
+
void processQueue(KisUpdaterContext &updaterContext,
bool externalJobsPending);
bool needsExclusiveAccess() const;
bool isEmpty() const;
qint32 sizeMetric() const;
KUndo2MagicString currentStrokeName() const;
bool hasOpenedStrokes() const;
bool wrapAroundModeSupported() const;
void setDesiredLevelOfDetail(int lod);
void explicitRegenerateLevelOfDetail();
void setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory);
void setSuspendUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory);
void setResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory);
+ KisPostExecutionUndoAdapter* lodNPostExecutionUndoAdapter() const;
/**
* Notifies the queue, that someone else (neither strokes nor the
* queue itself have changed the image. It means that the caches
* should be regenerated
*/
void notifyUFOChangedImage();
+ void debugDumpAllStrokes();
+
private:
bool processOneJob(KisUpdaterContext &updaterContext,
bool externalJobsPending);
bool checkStrokeState(bool hasStrokeJobsRunning,
int runningLevelOfDetail);
bool checkExclusiveProperty(qint32 numMergeJobs, qint32 numStrokeJobs);
bool checkSequentialProperty(qint32 numMergeJobs, qint32 numStrokeJobs);
bool checkBarrierProperty(qint32 numMergeJobs, qint32 numStrokeJobs,
bool externalJobsPending);
bool checkLevelOfDetailProperty(int runningLevelOfDetail);
+
+ class LodNUndoStrokesFacade;
+ KisStrokeId startLodNUndoStroke(KisStrokeStrategy *strokeStrategy);
+
private:
struct Private;
Private * const m_d;
};
#endif /* __KIS_STROKES_QUEUE_H */
diff --git a/libs/image/kis_image_interfaces.cpp b/libs/image/kis_strokes_queue_undo_result.h
similarity index 75%
copy from libs/image/kis_image_interfaces.cpp
copy to libs/image/kis_strokes_queue_undo_result.h
index cb45b2dfde..574990220f 100644
--- a/libs/image/kis_image_interfaces.cpp
+++ b/libs/image/kis_strokes_queue_undo_result.h
@@ -1,31 +1,29 @@
/*
- * Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
+ * Copyright (c) 2016 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_interfaces.h"
+#ifndef KIS_STROKES_QUEUE_UNDO_RESULT_H
+#define KIS_STROKES_QUEUE_UNDO_RESULT_H
-KisStrokesFacade::~KisStrokesFacade()
-{
-}
+enum UndoResult {
+ UNDO_FAIL,
+ UNDO_OK,
+ UNDO_WAIT
+};
-KisUpdatesFacade::~KisUpdatesFacade()
-{
-}
+#endif // KIS_STROKES_QUEUE_UNDO_RESULT_H
-KisProjectionUpdateListener::~KisProjectionUpdateListener()
-{
-}
diff --git a/libs/image/kis_suspend_projection_updates_stroke_strategy.cpp b/libs/image/kis_suspend_projection_updates_stroke_strategy.cpp
index 6523660fd8..cabc0991d9 100644
--- a/libs/image/kis_suspend_projection_updates_stroke_strategy.cpp
+++ b/libs/image/kis_suspend_projection_updates_stroke_strategy.cpp
@@ -1,285 +1,302 @@
/*
* 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_suspend_projection_updates_stroke_strategy.h"
#include <kis_image.h>
#include <krita_utils.h>
#include <kis_projection_updates_filter.h>
inline uint qHash(const QRect &rc) {
return rc.x() +
(rc.y() << 16) +
(rc.width() << 8) +
(rc.height() << 24);
}
struct KisSuspendProjectionUpdatesStrokeStrategy::Private
{
KisImageWSP image;
bool suspend;
class SuspendLod0Updates : public KisProjectionUpdatesFilter
{
typedef QHash<KisNodeSP, QVector<QRect> > RectsHash;
public:
struct Request {
Request() {}
Request(KisNodeSP _node, QRect _rect) : node(_node), rect(_rect) {}
KisNodeSP node;
QRect rect;
};
public:
SuspendLod0Updates()
{
}
bool filter(KisImage *image, KisNode *node, const QRect &rect) override {
if (image->currentLevelOfDetail() > 0) return false;
QMutexLocker l(&m_mutex);
m_requestsHash[KisNodeSP(node)].append(rect);
return true;
}
static inline QRect alignRect(const QRect &rc, const int step) {
static const int decstep = step - 1;
static const int invstep = ~decstep;
int x0, y0, x1, y1;
rc.getCoords(&x0, &y0, &x1, &y1);
x0 &= invstep;
y0 &= invstep;
x1 |= decstep;
y1 |= decstep;
QRect result;
result.setCoords(x0, y0, x1, y1);
return result;
}
void notifyUpdates(KisNodeGraphListener *listener) {
RectsHash::const_iterator it = m_requestsHash.constBegin();
RectsHash::const_iterator end = m_requestsHash.constEnd();
const int step = 64;
for (; it != end; ++it) {
KisNodeSP node = it.key();
const QVector<QRect> &rects = it.value();
QVector<QRect>::const_iterator it = rects.constBegin();
QVector<QRect>::const_iterator end = rects.constEnd();
QRegion region;
for (; it != end; ++it) {
region += alignRect(*it, step);
}
Q_FOREACH (const QRect &rc, region.rects()) {
// FIXME: constness: port rPU to SP
listener->requestProjectionUpdate(const_cast<KisNode*>(node.data()), rc);
}
}
}
private:
RectsHash m_requestsHash;
QMutex m_mutex;
};
class SuspendData : public KisStrokeJobData {
public:
SuspendData()
: KisStrokeJobData(SEQUENTIAL)
{}
};
class ResumeAndIssueGraphUpdatesData : public KisStrokeJobData {
public:
ResumeAndIssueGraphUpdatesData()
: KisStrokeJobData(SEQUENTIAL)
{}
};
class UpdatesBarrierData : public KisStrokeJobData {
public:
UpdatesBarrierData()
: KisStrokeJobData(BARRIER)
{}
};
class IssueCanvasUpdatesData : public KisStrokeJobData {
public:
IssueCanvasUpdatesData(QRect _updateRect)
: KisStrokeJobData(CONCURRENT),
updateRect(_updateRect)
{}
QRect updateRect;
};
};
KisSuspendProjectionUpdatesStrokeStrategy::KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP image, bool suspend)
: KisSimpleStrokeStrategy(suspend ? "suspend_stroke_strategy" : "resume_stroke_strategy"),
m_d(new Private)
{
m_d->image = image;
m_d->suspend = suspend;
/**
* Here we add a dumb INIT job so that KisStrokesQueue would know that the
* stroke has already started or not. When the queue reaches the resume
* stroke ans starts its execution, no Lod0 can execute anymore. So all the
* new Lod0 strokes should go to the end of the queue and wrapped into
* their own Suspend/Resume pair.
*/
enableJob(JOB_INIT, true);
enableJob(JOB_DOSTROKE, true);
enableJob(JOB_CANCEL, true);
+
+ setNeedsExplicitCancel(true);
}
KisSuspendProjectionUpdatesStrokeStrategy::~KisSuspendProjectionUpdatesStrokeStrategy()
{
}
/**
* When the Lod0 stroke is being recalculated in the background we
* should block all the updates it issues to avoid user distraction.
* The result of the final stroke should be shown to the user in the
* very end when everything is fully ready. Ideally the use should not
* notice that the image has changed :)
*
* (Don't mix this process with suspend/resume capabilities of a
* single stroke. That is a different system!)
*
* The process of the Lod0 regeneration consists of the following:
*
* 1) Suspend stroke executes. It sets a special updates filter on the
* image. The filter blocks all the updates and saves them in an
* internal structure to be emitted in the future.
*
* 2) Lod0 strokes are being recalculated. All their updates are
* blocked and saved in the filter.
*
* 3) Resume stroke starts:
*
* 3.1) First it disables emitting of sigImageUpdated() so the gui
* will not get any update notifications.
*
* 3.2) Then it enables updates themselves.
*
* 3.3) Initiates all the updates that were requested by the Lod0
* stroke. The node graph is regenerated, but the GUI does
* not get this change.
*
* 3.4) Special barrier job waits for all the updates to finish
* and, when they are done, enables GUI notifications again.
*
* 3.5) In a multithreaded way emits the GUI notifications for the
* entire image. Multithreaded way is used to conform the
* double-stage update principle of KisCanvas2.
*/
void KisSuspendProjectionUpdatesStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
Private::SuspendData *suspendData = dynamic_cast<Private::SuspendData*>(data);
Private::ResumeAndIssueGraphUpdatesData *resumeData = dynamic_cast<Private::ResumeAndIssueGraphUpdatesData*>(data);
Private::UpdatesBarrierData *barrierData = dynamic_cast<Private::UpdatesBarrierData*>(data);
Private::IssueCanvasUpdatesData *canvasUpdates = dynamic_cast<Private::IssueCanvasUpdatesData*>(data);
+ KisImageSP image = m_d->image.toStrongRef();
+ if (!image) {
+ return;
+ }
+
if (suspendData) {
- m_d->image->setProjectionUpdatesFilter(
+ image->setProjectionUpdatesFilter(
KisProjectionUpdatesFilterSP(new Private::SuspendLod0Updates()));
} else if (resumeData) {
- m_d->image->disableUIUpdates();
+ image->disableUIUpdates();
resumeAndIssueUpdates(false);
} else if (barrierData) {
- m_d->image->enableUIUpdates();
+ image->enableUIUpdates();
} else if (canvasUpdates) {
- m_d->image->notifyProjectionUpdated(canvasUpdates->updateRect);
+ image->notifyProjectionUpdated(canvasUpdates->updateRect);
}
}
QList<KisStrokeJobData*> KisSuspendProjectionUpdatesStrokeStrategy::createSuspendJobsData(KisImageWSP /*image*/)
{
QList<KisStrokeJobData*> jobsData;
jobsData << new Private::SuspendData();
return jobsData;
}
QList<KisStrokeJobData*> KisSuspendProjectionUpdatesStrokeStrategy::createResumeJobsData(KisImageWSP _image)
{
QList<KisStrokeJobData*> jobsData;
jobsData << new Private::ResumeAndIssueGraphUpdatesData();
jobsData << new Private::UpdatesBarrierData();
using KritaUtils::splitRectIntoPatches;
using KritaUtils::optimalPatchSize;
KisImageSP image = _image;
QVector<QRect> rects = splitRectIntoPatches(image->bounds(), optimalPatchSize());
Q_FOREACH (const QRect &rc, rects) {
jobsData << new Private::IssueCanvasUpdatesData(rc);
}
return jobsData;
}
void KisSuspendProjectionUpdatesStrokeStrategy::resumeAndIssueUpdates(bool dropUpdates)
{
+ KisImageSP image = m_d->image.toStrongRef();
+ if (!image) {
+ return;
+ }
+
KisProjectionUpdatesFilterSP filter =
- m_d->image->projectionUpdatesFilter();
+ image->projectionUpdatesFilter();
if (!filter) return;
Private::SuspendLod0Updates *localFilter =
dynamic_cast<Private::SuspendLod0Updates*>(filter.data());
if (localFilter) {
- m_d->image->setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP());
+ image->setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP());
if (!dropUpdates) {
- localFilter->notifyUpdates(m_d->image.data());
+ localFilter->notifyUpdates(image.data());
}
}
}
void KisSuspendProjectionUpdatesStrokeStrategy::cancelStrokeCallback()
{
+ KisImageSP image = m_d->image.toStrongRef();
+ if (!image) {
+ return;
+ }
+
/**
* We shouldn't emit any ad-hoc updates when cancelling the
* stroke. It generates weird temporary holes on the canvas,
* making the user feel awful, thinking his image got
* corrupted. We will just emit a common refreshGraphAsync() that
* will do all the work in a beautiful way
*/
resumeAndIssueUpdates(true);
if (!m_d->suspend) {
// FIXME: optimize
- m_d->image->refreshGraphAsync();
+ image->refreshGraphAsync();
}
}
diff --git a/libs/image/kis_types.h b/libs/image/kis_types.h
index 358cd130df..e7bf16c44a 100644
--- a/libs/image/kis_types.h
+++ b/libs/image/kis_types.h
@@ -1,306 +1,302 @@
/*
* 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.
*/
#ifndef KISTYPES_H_
#define KISTYPES_H_
#include <QVector>
#include <QPoint>
#include <QList>
#include "kritaimage_export.h"
template<class T>
class KisWeakSharedPtr;
template<class T>
class KisSharedPtr;
template<class T> class QSharedPointer;
template<class T> class QWeakPointer;
template <class T>
uint qHash(KisSharedPtr<T> ptr) {
return qHash(ptr.data());
}
template <class T>
uint qHash(KisWeakSharedPtr<T> ptr) {
return qHash(ptr.data());
}
/**
* Define lots of shared pointer versions of Krita classes.
* Shared pointer classes have the advantage of near automatic
* memory management (but beware of circular references)
* These types should never be passed by reference,
* because that will mess up their reference counter.
*
* An example of the naming pattern used:
*
* KisPaintDeviceSP is a KisSharedPtr of KisPaintDevice
* KisPaintDeviceWSP is a KisWeakSharedPtr of KisPaintDevice
* vKisPaintDeviceSP is a QVector of KisPaintDeviceSP
* vKisPaintDeviceSP_it is an iterator of vKisPaintDeviceSP
*
*/
class KisImage;
typedef KisSharedPtr<KisImage> KisImageSP;
typedef KisWeakSharedPtr<KisImage> KisImageWSP;
class KisPaintDevice;
typedef KisSharedPtr<KisPaintDevice> KisPaintDeviceSP;
typedef KisWeakSharedPtr<KisPaintDevice> KisPaintDeviceWSP;
typedef QVector<KisPaintDeviceSP> vKisPaintDeviceSP;
typedef vKisPaintDeviceSP::iterator vKisPaintDeviceSP_it;
class KisFixedPaintDevice;
typedef KisSharedPtr<KisFixedPaintDevice> KisFixedPaintDeviceSP;
class KisMask;
typedef KisSharedPtr<KisMask> KisMaskSP;
typedef KisWeakSharedPtr<KisMask> KisMaskWSP;
class KisNode;
typedef KisSharedPtr<KisNode> KisNodeSP;
typedef KisWeakSharedPtr<KisNode> KisNodeWSP;
typedef QVector<KisNodeSP> vKisNodeSP;
typedef vKisNodeSP::iterator vKisNodeSP_it;
typedef vKisNodeSP::const_iterator vKisNodeSP_cit;
class KisBaseNode;
typedef KisSharedPtr<KisBaseNode> KisBaseNodeSP;
typedef KisWeakSharedPtr<KisBaseNode> KisBaseNodeWSP;
class KisEffectMask;
typedef KisSharedPtr<KisEffectMask> KisEffectMaskSP;
typedef KisWeakSharedPtr<KisEffectMask> KisEffectMaskWSP;
class KisFilterMask;
typedef KisSharedPtr<KisFilterMask> KisFilterMaskSP;
typedef KisWeakSharedPtr<KisFilterMask> KisFilterMaskWSP;
class KisTransformMask;
typedef KisSharedPtr<KisTransformMask> KisTransformMaskSP;
typedef KisWeakSharedPtr<KisTransformMask> KisTransformMaskWSP;
class KisTransformMaskParamsInterface;
typedef QSharedPointer<KisTransformMaskParamsInterface> KisTransformMaskParamsInterfaceSP;
typedef QWeakPointer<KisTransformMaskParamsInterface> KisTransformMaskParamsInterfaceWSP;
class KisTransparencyMask;
typedef KisSharedPtr<KisTransparencyMask> KisTransparencyMaskSP;
typedef KisWeakSharedPtr<KisTransparencyMask> KisTransparencyMaskWSP;
class KisColorizeMask;
typedef KisSharedPtr<KisColorizeMask> KisColorizeMaskSP;
typedef KisWeakSharedPtr<KisColorizeMask> KisColorizeMaskWSP;
class KisLayer;
typedef KisSharedPtr<KisLayer> KisLayerSP;
typedef KisWeakSharedPtr<KisLayer> KisLayerWSP;
class KisShapeLayer;
typedef KisSharedPtr<KisShapeLayer> KisShapeLayerSP;
class KisPaintLayer;
typedef KisSharedPtr<KisPaintLayer> KisPaintLayerSP;
class KisAdjustmentLayer;
typedef KisSharedPtr<KisAdjustmentLayer> KisAdjustmentLayerSP;
class KisGeneratorLayer;
typedef KisSharedPtr<KisGeneratorLayer> KisGeneratorLayerSP;
class KisCloneLayer;
typedef KisSharedPtr<KisCloneLayer> KisCloneLayerSP;
typedef KisWeakSharedPtr<KisCloneLayer> KisCloneLayerWSP;
class KisGroupLayer;
typedef KisSharedPtr<KisGroupLayer> KisGroupLayerSP;
typedef KisWeakSharedPtr<KisGroupLayer> KisGroupLayerWSP;
class KisSelection;
typedef KisSharedPtr<KisSelection> KisSelectionSP;
typedef KisWeakSharedPtr<KisSelection> KisSelectionWSP;
class KisSelectionComponent;
typedef KisSharedPtr<KisSelectionComponent> KisSelectionComponentSP;
-class KisBackground;
-typedef KisSharedPtr<KisBackground> KisBackgroundSP;
-
class KisSelectionMask;
typedef KisSharedPtr<KisSelectionMask> KisSelectionMaskSP;
class KisPixelSelection;
typedef KisSharedPtr<KisPixelSelection> KisPixelSelectionSP;
class KisHistogram;
typedef KisSharedPtr<KisHistogram> KisHistogramSP;
typedef QVector<QPoint> vKisSegments;
class KisFilter;
typedef KisSharedPtr<KisFilter> KisFilterSP;
class KisLayerStyleFilter;
typedef KisSharedPtr<KisLayerStyleFilter> KisLayerStyleFilterSP;
class KisGenerator;
typedef KisSharedPtr<KisGenerator> KisGeneratorSP;
class KisConvolutionKernel;
typedef KisSharedPtr<KisConvolutionKernel> KisConvolutionKernelSP;
class KisAnnotation;
typedef KisSharedPtr<KisAnnotation> KisAnnotationSP;
typedef QVector<KisAnnotationSP> vKisAnnotationSP;
typedef vKisAnnotationSP::iterator vKisAnnotationSP_it;
typedef vKisAnnotationSP::const_iterator vKisAnnotationSP_cit;
class KisAnimationFrameCache;
typedef KisSharedPtr<KisAnimationFrameCache> KisAnimationFrameCacheSP;
typedef KisWeakSharedPtr<KisAnimationFrameCache> KisAnimationFrameCacheWSP;
class KisPaintingAssistant;
typedef QSharedPointer<KisPaintingAssistant> KisPaintingAssistantSP;
typedef QWeakPointer<KisPaintingAssistant> KisPaintingAssistantWSP;
// Repeat iterators
class KisHLineIterator2;
template<class T> class KisRepeatHLineIteratorPixelBase;
typedef KisRepeatHLineIteratorPixelBase< KisHLineIterator2 > KisRepeatHLineConstIteratorNG;
typedef KisSharedPtr<KisRepeatHLineConstIteratorNG> KisRepeatHLineConstIteratorSP;
class KisVLineIterator2;
template<class T> class KisRepeatVLineIteratorPixelBase;
typedef KisRepeatVLineIteratorPixelBase< KisVLineIterator2 > KisRepeatVLineConstIteratorNG;
typedef KisSharedPtr<KisRepeatVLineConstIteratorNG> KisRepeatVLineConstIteratorSP;
// NG Iterators
class KisHLineIteratorNG;
typedef KisSharedPtr<KisHLineIteratorNG> KisHLineIteratorSP;
class KisHLineConstIteratorNG;
typedef KisSharedPtr<KisHLineConstIteratorNG> KisHLineConstIteratorSP;
class KisVLineIteratorNG;
typedef KisSharedPtr<KisVLineIteratorNG> KisVLineIteratorSP;
class KisVLineConstIteratorNG;
typedef KisSharedPtr<KisVLineConstIteratorNG> KisVLineConstIteratorSP;
class KisRandomConstAccessorNG;
typedef KisSharedPtr<KisRandomConstAccessorNG> KisRandomConstAccessorSP;
class KisRandomAccessorNG;
typedef KisSharedPtr<KisRandomAccessorNG> KisRandomAccessorSP;
class KisRandomSubAccessor;
typedef KisSharedPtr<KisRandomSubAccessor> KisRandomSubAccessorSP;
// Things
typedef QVector<QPointF> vQPointF;
class KisPaintOpPreset;
typedef KisSharedPtr<KisPaintOpPreset> KisPaintOpPresetSP;
typedef KisWeakSharedPtr<KisPaintOpPreset> KisPaintOpPresetWSP;
template <typename T>
class KisPinnedSharedPtr;
class KisPaintOpSettings;
typedef KisPinnedSharedPtr<KisPaintOpSettings> KisPaintOpSettingsSP;
template <typename T>
class KisRestrictedSharedPtr;
typedef KisRestrictedSharedPtr<KisPaintOpSettings> KisPaintOpSettingsRestrictedSP;
class KisPaintOp;
typedef KisSharedPtr<KisPaintOp> KisPaintOpSP;
class KoID;
typedef QList<KoID> KoIDList;
class KoUpdater;
template<class T> class QPointer;
typedef QPointer<KoUpdater> KoUpdaterPtr;
class KisProcessingVisitor;
typedef KisSharedPtr<KisProcessingVisitor> KisProcessingVisitorSP;
class KUndo2Command;
typedef QSharedPointer<KUndo2Command> KUndo2CommandSP;
typedef QList<KisNodeSP> KisNodeList;
typedef QSharedPointer<KisNodeList> KisNodeListSP;
typedef QList<KisPaintDeviceSP> KisPaintDeviceList;
class KisStroke;
typedef QSharedPointer<KisStroke> KisStrokeSP;
typedef QWeakPointer<KisStroke> KisStrokeWSP;
typedef KisStrokeWSP KisStrokeId;
class KisFilterConfiguration;
typedef KisPinnedSharedPtr<KisFilterConfiguration> KisFilterConfigurationSP;
class KisPropertiesConfiguration;
typedef KisPinnedSharedPtr<KisPropertiesConfiguration> KisPropertiesConfigurationSP;
class KisLockedProperties;
typedef KisSharedPtr<KisLockedProperties> KisLockedPropertiesSP;
class KisProjectionUpdatesFilter;
typedef QSharedPointer<KisProjectionUpdatesFilter> KisProjectionUpdatesFilterSP;
class KisAbstractProjectionPlane;
typedef QSharedPointer<KisAbstractProjectionPlane> KisAbstractProjectionPlaneSP;
typedef QWeakPointer<KisAbstractProjectionPlane> KisAbstractProjectionPlaneWSP;
class KisProjectionLeaf;
typedef QSharedPointer<KisProjectionLeaf> KisProjectionLeafSP;
typedef QWeakPointer<KisProjectionLeaf> KisProjectionLeafWSP;
class KisKeyframe;
typedef QSharedPointer<KisKeyframe> KisKeyframeSP;
typedef QWeakPointer<KisKeyframe> KisKeyframeWSP;
-
class KisFilterChain;
typedef KisSharedPtr<KisFilterChain> KisFilterChainSP;
class KisProofingConfiguration;
typedef QSharedPointer<KisProofingConfiguration> KisProofingConfigurationSP;
typedef QWeakPointer<KisProofingConfiguration> KisProofingConfigurationWSP;
class KisLayerComposition;
typedef QSharedPointer<KisLayerComposition> KisLayerCompositionSP;
typedef QWeakPointer<KisLayerComposition> KisLayerCompositionWSP;
#include <QSharedPointer>
#include <QWeakPointer>
#include <kis_shared_ptr.h>
#include <kis_restricted_shared_ptr.h>
#include <kis_pinned_shared_ptr.h>
#endif // KISTYPES_H_
diff --git a/libs/image/kis_undo_stores.cpp b/libs/image/kis_undo_stores.cpp
index 896a77800c..828139ab15 100644
--- a/libs/image/kis_undo_stores.cpp
+++ b/libs/image/kis_undo_stores.cpp
@@ -1,139 +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_undo_stores.h"
#include <kundo2stack.h>
/*****************************************************************/
/* KisSurrogateUndoStore */
/*****************************************************************/
KisSurrogateUndoStore::KisSurrogateUndoStore()
: m_undoStack(new KUndo2Stack)
{
}
KisSurrogateUndoStore::~KisSurrogateUndoStore()
{
delete m_undoStack;
}
const KUndo2Command* KisSurrogateUndoStore::presentCommand()
{
return m_undoStack->command(m_undoStack->index() - 1);
}
void KisSurrogateUndoStore::undoLastCommand()
{
m_undoStack->undo();
}
void KisSurrogateUndoStore::addCommand(KUndo2Command *command)
{
if(!command) return;
m_undoStack->push(command);
}
void KisSurrogateUndoStore::beginMacro(const KUndo2MagicString& macroName)
{
m_undoStack->beginMacro(macroName);
}
void KisSurrogateUndoStore::endMacro()
{
m_undoStack->endMacro();
}
void KisSurrogateUndoStore::undo()
{
m_undoStack->undo();
}
void KisSurrogateUndoStore::redo()
{
m_undoStack->redo();
}
void KisSurrogateUndoStore::purgeRedoState()
{
m_undoStack->purgeRedoState();
}
+void KisSurrogateUndoStore::clear()
+{
+ m_undoStack->clear();
+}
+
void KisSurrogateUndoStore::undoAll()
{
while(m_undoStack->canUndo()) {
m_undoStack->undo();
}
}
void KisSurrogateUndoStore::redoAll()
{
while(m_undoStack->canRedo()) {
m_undoStack->redo();
}
}
/*****************************************************************/
/* KisDumbUndoStore */
/*****************************************************************/
const KUndo2Command* KisDumbUndoStore::presentCommand()
{
return 0;
}
void KisDumbUndoStore::undoLastCommand()
{
/**
* Ermm.. Do we actually have one? We are dumb! ;)
*/
}
void KisDumbUndoStore::addCommand(KUndo2Command *command)
{
/**
* Ermm.. Done with it! :P
*/
command->redo();
delete command;
}
void KisDumbUndoStore::beginMacro(const KUndo2MagicString& macroName)
{
/**
* Yes, sir! >:)
*/
Q_UNUSED(macroName);
}
void KisDumbUndoStore::endMacro()
{
/**
* Roger that! :)
*/
}
void KisDumbUndoStore::purgeRedoState()
{
/**
* Erm... what? %)
*/
}
diff --git a/libs/image/kis_undo_stores.h b/libs/image/kis_undo_stores.h
index 744e93a21e..27b100a821 100644
--- a/libs/image/kis_undo_stores.h
+++ b/libs/image/kis_undo_stores.h
@@ -1,72 +1,74 @@
/*
* 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_UNDO_STORES_H
#define __KIS_UNDO_STORES_H
#include "kis_undo_store.h"
class KUndo2Stack;
class KUndo2MagicString;
/**
* KisSurrogateUndoAdapter -- saves commands directly to the
* internal stack. Used for wrapping around legacy code into
* a single command.
*/
class KRITAIMAGE_EXPORT KisSurrogateUndoStore : public KisUndoStore
{
public:
KisSurrogateUndoStore();
~KisSurrogateUndoStore();
const KUndo2Command* presentCommand();
void undoLastCommand();
void addCommand(KUndo2Command *cmd);
void beginMacro(const KUndo2MagicString& macroName);
void endMacro();
void undo();
void redo();
void undoAll();
void redoAll();
void purgeRedoState();
+ void clear();
+
private:
KUndo2Stack *m_undoStack;
};
/**
* @brief The KisDumbUndoStore class doesn't actually save commands,
* so you cannot undo or redo!
*/
class KRITAIMAGE_EXPORT KisDumbUndoStore : public KisUndoStore
{
public:
const KUndo2Command* presentCommand();
void undoLastCommand();
void addCommand(KUndo2Command *cmd);
void beginMacro(const KUndo2MagicString& macroName);
void endMacro();
void purgeRedoState();
};
#endif /* __KIS_UNDO_STORES_H */
diff --git a/libs/image/kis_update_scheduler.cpp b/libs/image/kis_update_scheduler.cpp
index cfb003b094..17440739f0 100644
--- a/libs/image/kis_update_scheduler.cpp
+++ b/libs/image/kis_update_scheduler.cpp
@@ -1,455 +1,465 @@
/*
* 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 "klocalizedstring.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) \
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(KisUpdateScheduler *_q, KisProjectionUpdateListener *p)
: q(_q)
, projectionUpdateListener(p)
{}
KisUpdateScheduler *q;
KisSimpleUpdateQueue updatesQueue;
KisStrokesQueue strokesQueue;
KisUpdaterContext updaterContext;
bool processingBlocked = false;
qreal balancingRatio = 1.0; // updates-queue-size/strokes-queue-size
KisProjectionUpdateListener *projectionUpdateListener;
KisQueuesProgressUpdater *progressUpdater = 0;
QAtomicInt updatesLockCounter;
QReadWriteLock updatesStartLock;
KisLazyWaitCondition updatesFinishedCondition;
};
KisUpdateScheduler::KisUpdateScheduler(KisProjectionUpdateListener *projectionUpdateListener)
: m_d(new Private(this, projectionUpdateListener))
{
updateSettings();
connectSignals();
}
KisUpdateScheduler::KisUpdateScheduler()
: m_d(new Private(this, 0))
{
}
KisUpdateScheduler::~KisUpdateScheduler()
{
delete m_d->progressUpdater;
delete m_d;
}
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, currentLevelOfDetail());
processQueues();
}
void KisUpdateScheduler::updateProjectionNoFilthy(KisNodeSP node, const QRect& rc, const QRect &cropRect)
{
m_d->updatesQueue.addUpdateNoFilthyJob(node, rc, cropRect, currentLevelOfDetail());
processQueues();
}
void KisUpdateScheduler::fullRefreshAsync(KisNodeSP root, const QRect& rc, const QRect &cropRect)
{
m_d->updatesQueue.addFullRefreshJob(root, rc, cropRect, currentLevelOfDetail());
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(true);
}
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();
}
+UndoResult KisUpdateScheduler::tryUndoLastStrokeAsync()
+{
+ return m_d->strokesQueue.tryUndoLastStrokeAsync();
+}
+
bool KisUpdateScheduler::wrapAroundModeSupported() const
{
return m_d->strokesQueue.wrapAroundModeSupported();
}
void KisUpdateScheduler::setDesiredLevelOfDetail(int lod)
{
m_d->strokesQueue.setDesiredLevelOfDetail(lod);
/**
* The queue might have started an internal stroke for
* cache synchronization. Process the queues to execute
* it if needed.
*/
processQueues();
}
void KisUpdateScheduler::explicitRegenerateLevelOfDetail()
{
m_d->strokesQueue.explicitRegenerateLevelOfDetail();
// \see a comment in setDesiredLevelOfDetail()
processQueues();
}
int KisUpdateScheduler::currentLevelOfDetail() const
{
int levelOfDetail = -1;
if (levelOfDetail < 0) {
levelOfDetail = m_d->updaterContext.currentLevelOfDetail();
}
if (levelOfDetail < 0) {
levelOfDetail = m_d->updatesQueue.overrideLevelOfDetail();
}
if (levelOfDetail < 0) {
levelOfDetail = 0;
}
return levelOfDetail;
}
void KisUpdateScheduler::setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory)
{
m_d->strokesQueue.setLod0ToNStrokeStrategyFactory(factory);
}
void KisUpdateScheduler::setSuspendUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory)
{
m_d->strokesQueue.setSuspendUpdatesStrokeStrategyFactory(factory);
}
void KisUpdateScheduler::setResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory)
{
m_d->strokesQueue.setResumeUpdatesStrokeStrategyFactory(factory);
}
+KisPostExecutionUndoAdapter *KisUpdateScheduler::lodNPostExecutionUndoAdapter() const
+{
+ return m_d->strokesQueue.lodNPostExecutionUndoAdapter();
+}
+
void KisUpdateScheduler::updateSettings()
{
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(bool resetLodLevels)
{
if (resetLodLevels) {
/**
* Legacy strokes may have changed the image while we didn't
* control it. Notify the queue to take it into account.
*/
m_d->strokesQueue.notifyUFOChangedImage();
}
m_d->processingBlocked = false;
processQueues();
}
bool KisUpdateScheduler::isIdle()
{
bool result = false;
if (tryBarrierLock()) {
result = true;
unlock(false);
}
return result;
}
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)
{
Q_UNUSED(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/libs/image/kis_update_scheduler.h b/libs/image/kis_update_scheduler.h
index f26c6136ac..5bea48054d 100644
--- a/libs/image/kis_update_scheduler.h
+++ b/libs/image/kis_update_scheduler.h
@@ -1,234 +1,241 @@
/*
* 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_UPDATE_SCHEDULER_H
#define __KIS_UPDATE_SCHEDULER_H
#include <QObject>
#include "kritaimage_export.h"
#include "kis_types.h"
#include "kis_image_interfaces.h"
#include "kis_stroke_strategy_factory.h"
+#include "kis_strokes_queue_undo_result.h"
class QRect;
class KoProgressProxy;
class KisProjectionUpdateListener;
class KisSpontaneousJob;
+class KisPostExecutionUndoAdapter;
class KRITAIMAGE_EXPORT KisUpdateScheduler : public QObject, public KisStrokesFacade
{
Q_OBJECT
public:
KisUpdateScheduler(KisProjectionUpdateListener *projectionUpdateListener);
virtual ~KisUpdateScheduler();
/**
* Sets the proxy that is going to be notified about the progress
* of processing of the queues. If you want to switch the proxy
* on runtime, you should do it under the lock held.
*
* \see lock(), unlock()
*/
void setProgressProxy(KoProgressProxy *progressProxy);
/**
* Blocks processing of the queues.
* The function will wait until all the executing jobs
* are finished.
* NOTE: you may add new jobs while the block held, but they
* will be delayed until unlock() is called.
*
* \see unlock()
*/
void lock();
/**
* Unblocks the process and calls processQueues()
*
* \see processQueues()
*/
void unlock(bool resetLodLevels = true);
/**
* Called when it is necessary to reread configuration
*/
void updateSettings();
/**
* Waits until all the running jobs are finished.
*
* If some other thread adds jobs in parallel, then you may
* wait forever. If you you don't want it, consider lock() instead.
*
* \see lock()
*/
void waitForDone();
/**
* Waits until the queues become empty, then blocks the processing.
* To unblock processing you should use unlock().
*
* If some other thread adds jobs in parallel, then you may
* wait forever. If you you don't want it, consider lock() instead.
*
* \see unlock(), lock()
*/
void barrierLock();
/**
* Works like barrier lock, but returns false immediately if barrierLock
* can't be acquired.
*
* \see barrierLock()
*/
bool tryBarrierLock();
/**
* Tells if there are no strokes or updates are running at the
* moment. Internally calls to tryBarrierLock(), so it is not O(1).
*/
bool isIdle();
/**
* Blocks all the updates from execution. It doesn't affect
* strokes execution in any way. This type of lock is supposed
* to be held by the strokes themselves when they need a short
* access to some parts of the projection of the image.
*
* From all the other places you should use usual lock()/unlock()
* methods
*
* \see lock(), unlock()
*/
void blockUpdates();
/**
* Unblocks updates from execution previously locked by blockUpdates()
*
* \see blockUpdates()
*/
void unblockUpdates();
void updateProjection(KisNodeSP node, const QRect& rc, const QRect &cropRect);
void updateProjectionNoFilthy(KisNodeSP node, const QRect& rc, const QRect &cropRect);
void fullRefreshAsync(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void fullRefresh(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy);
void addJob(KisStrokeId id, KisStrokeJobData *data);
void endStroke(KisStrokeId id);
bool cancelStroke(KisStrokeId id);
/**
* Sets the desired level of detail on which the strokes should
* work. Please note that this configuration will be applied
* starting from the next stroke. Please also note that this value
* is not guaranteed to coincide with the one returned by
* currentLevelOfDetail()
*/
void setDesiredLevelOfDetail(int lod);
/**
* Explicitly start regeneration of LoD planes of all the devices
* in the image. This call should be performed when the user is idle,
* just to make the quality of image updates better.
*/
void explicitRegenerateLevelOfDetail();
/**
* Install a factory of a stroke strategy, that will be started
* every time when the scheduler needs to synchronize LOD caches
* of all the paint devices of the image.
*/
void setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory);
/**
* Install a factory of a stroke strategy, that will be started
* every time when the scheduler needs to postpone all the updates
* of the *LOD0* strokes.
*/
void setSuspendUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory);
/**
* \see setSuspendUpdatesStrokeStrategyFactory()
*/
void setResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory);
+ KisPostExecutionUndoAdapter* lodNPostExecutionUndoAdapter() const;
+
+
/**
* tryCancelCurrentStrokeAsync() checks whether there is a
* *running* stroke (which is being executed at this very moment)
* which is not still open by the owner (endStroke() or
* cancelStroke() have already been called) and cancels it.
*
* \return true if some stroke has been found and cancelled
*
* \note This method is *not* part of KisStrokesFacade! It is too
* low level for KisImage. In KisImage it is combined with
* more high level requestStrokeCancellation().
*/
bool tryCancelCurrentStrokeAsync();
+ UndoResult tryUndoLastStrokeAsync();
+
bool wrapAroundModeSupported() const;
int currentLevelOfDetail() const;
protected:
// Trivial constructor for testing support
KisUpdateScheduler();
void connectSignals();
void processQueues();
private Q_SLOTS:
void continueUpdate(const QRect &rect);
void doSomeUsefulWork();
void spareThreadAppeared();
private:
friend class UpdatesBlockTester;
bool haveUpdatesRunning();
void tryProcessUpdatesQueue();
void wakeUpWaitingThreads();
void progressUpdate();
protected:
struct Private;
Private * const m_d;
};
class KisTestableUpdaterContext;
class KisTestableSimpleUpdateQueue;
class KRITAIMAGE_EXPORT KisTestableUpdateScheduler : public KisUpdateScheduler
{
public:
KisTestableUpdateScheduler(KisProjectionUpdateListener *projectionUpdateListener,
qint32 threadCount);
KisTestableUpdaterContext* updaterContext();
KisTestableSimpleUpdateQueue* updateQueue();
using KisUpdateScheduler::processQueues;
};
#endif /* __KIS_UPDATE_SCHEDULER_H */
diff --git a/libs/image/lazybrush/kis_colorize_mask.cpp b/libs/image/lazybrush/kis_colorize_mask.cpp
index c5da3ddbe0..d9308c42c9 100644
--- a/libs/image/lazybrush/kis_colorize_mask.cpp
+++ b/libs/image/lazybrush/kis_colorize_mask.cpp
@@ -1,916 +1,915 @@
/*
* Copyright (c) 2016 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_colorize_mask.h"
#include <QCoreApplication>
#include <KoColorSpaceRegistry.h>
#include "kis_pixel_selection.h"
#include "kis_icon_utils.h"
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_painter.h"
#include "kis_fill_painter.h"
#include "kis_lazy_fill_tools.h"
#include "kis_cached_paint_device.h"
#include "kis_paint_device_debug_utils.h"
#include "kis_layer_properties_icons.h"
#include "kis_signal_compressor.h"
#include "kis_colorize_stroke_strategy.h"
#include "kis_multiway_cut.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_macro_based_undo_store.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_command_utils.h"
#include "kis_processing_applicator.h"
#include "krita_utils.h"
#include "kis_command_utils.h"
using namespace KisLazyFillTools;
struct KisColorizeMask::Private
{
Private()
- : needAddCurrentKeyStroke(false),
+ : coloringProjection(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())),
+ fakePaintDevice(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())),
+ filteredSource(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())),
+ needAddCurrentKeyStroke(false),
showKeyStrokes(true),
showColoring(true),
needsUpdate(true),
originalSequenceNumber(-1),
updateCompressor(1, KisSignalCompressor::POSTPONE)
{
}
Private(const Private &rhs)
: coloringProjection(new KisPaintDevice(*rhs.coloringProjection)),
fakePaintDevice(new KisPaintDevice(*rhs.fakePaintDevice)),
+ filteredSource(new KisPaintDevice(*rhs.filteredSource)),
needAddCurrentKeyStroke(rhs.needAddCurrentKeyStroke),
showKeyStrokes(rhs.showKeyStrokes),
showColoring(rhs.showColoring),
needsUpdate(false),
originalSequenceNumber(-1),
updateCompressor(1000, KisSignalCompressor::POSTPONE),
offset(rhs.offset)
{
Q_FOREACH (const KeyStroke &stroke, rhs.keyStrokes) {
- keyStrokes << KeyStroke(new KisPaintDevice(*stroke.dev), stroke.color, stroke.isTransparent);
+ keyStrokes << KeyStroke(KisPaintDeviceSP(new KisPaintDevice(*stroke.dev)), stroke.color, stroke.isTransparent);
}
}
QList<KeyStroke> keyStrokes;
KisPaintDeviceSP coloringProjection;
KisPaintDeviceSP fakePaintDevice;
KisPaintDeviceSP filteredSource;
KoColor currentColor;
KisPaintDeviceSP currentKeyStrokeDevice;
bool needAddCurrentKeyStroke;
bool showKeyStrokes;
bool showColoring;
KisCachedSelection cachedSelection;
KisCachedSelection cachedConversionSelection;
bool needsUpdate;
int originalSequenceNumber;
KisSignalCompressor updateCompressor;
QPoint offset;
};
KisColorizeMask::KisColorizeMask()
: m_d(new Private)
{
- const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
- m_d->fakePaintDevice = new KisPaintDevice(colorSpace);
- m_d->filteredSource = new KisPaintDevice(colorSpace);
- m_d->coloringProjection = new KisPaintDevice(colorSpace);
-
connect(&m_d->updateCompressor,
SIGNAL(timeout()),
SLOT(slotUpdateRegenerateFilling()));
m_d->updateCompressor.moveToThread(qApp->thread());
}
KisColorizeMask::~KisColorizeMask()
{
}
KisColorizeMask::KisColorizeMask(const KisColorizeMask& rhs)
: KisEffectMask(rhs),
m_d(new Private(*rhs.m_d))
{
connect(&m_d->updateCompressor,
SIGNAL(timeout()),
SLOT(slotUpdateRegenerateFilling()));
m_d->updateCompressor.moveToThread(qApp->thread());
}
void KisColorizeMask::initializeCompositeOp()
{
- KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
+ KisLayerSP parentLayer(dynamic_cast<KisLayer*>(parent().data()));
if (!parentLayer || !parentLayer->original()) return;
KisImageSP image = parentLayer->image();
if (!image) return;
const qreal samplePortion = 0.1;
const qreal alphaPortion =
KritaUtils::estimatePortionOfTransparentPixels(parentLayer->original(),
image->bounds(),
samplePortion);
setCompositeOpId(alphaPortion > 0.3 ? COMPOSITE_BEHIND : COMPOSITE_MULT);
}
const KoColorSpace* KisColorizeMask::colorSpace() const
{
return m_d->fakePaintDevice->colorSpace();
}
struct SetKeyStrokesColorSpaceCommand : public KUndo2Command {
SetKeyStrokesColorSpaceCommand(const KoColorSpace *dstCS,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags,
QList<KeyStroke> *list,
KisColorizeMaskSP node)
: m_dstCS(dstCS),
m_renderingIntent(renderingIntent),
m_conversionFlags(conversionFlags),
m_list(list),
m_node(node) {}
void undo() override {
KIS_ASSERT_RECOVER_RETURN(m_list->size() == m_oldColors.size());
for (int i = 0; i < m_list->size(); i++) {
(*m_list)[i].color = m_oldColors[i];
}
}
void redo() override {
if (m_oldColors.isEmpty()) {
Q_FOREACH(const KeyStroke &stroke, *m_list) {
m_oldColors << stroke.color;
m_newColors << stroke.color;
m_newColors.last().convertTo(m_dstCS, m_renderingIntent, m_conversionFlags);
}
}
KIS_ASSERT_RECOVER_RETURN(m_list->size() == m_newColors.size());
for (int i = 0; i < m_list->size(); i++) {
(*m_list)[i].color = m_newColors[i];
}
}
private:
QVector<KoColor> m_oldColors;
QVector<KoColor> m_newColors;
const KoColorSpace *m_dstCS;
KoColorConversionTransformation::Intent m_renderingIntent;
KoColorConversionTransformation::ConversionFlags m_conversionFlags;
QList<KeyStroke> *m_list;
KisColorizeMaskSP m_node;
};
void KisColorizeMask::setProfile(const KoColorProfile *profile)
{
// WARNING: there is no undo information, used only while loading!
m_d->fakePaintDevice->setProfile(profile);
m_d->coloringProjection->setProfile(profile);
for (auto stroke : m_d->keyStrokes) {
stroke.color.setProfile(profile);
}
}
KUndo2Command* KisColorizeMask::setColorSpace(const KoColorSpace * dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags)
{
using namespace KisCommandUtils;
CompositeCommand *composite = new CompositeCommand();
composite->addCommand(m_d->fakePaintDevice->convertTo(dstColorSpace, renderingIntent, conversionFlags));
composite->addCommand(m_d->coloringProjection->convertTo(dstColorSpace, renderingIntent, conversionFlags));
KUndo2Command *strokesConversionCommand =
new SetKeyStrokesColorSpaceCommand(
dstColorSpace, renderingIntent, conversionFlags,
- &m_d->keyStrokes, this);
+ &m_d->keyStrokes, KisColorizeMaskSP(this));
strokesConversionCommand->redo();
composite->addCommand(new SkipFirstRedoWrapper(strokesConversionCommand));
return composite;
}
bool KisColorizeMask::needsUpdate() const
{
return m_d->needsUpdate;
}
void KisColorizeMask::setNeedsUpdate(bool value)
{
if (value != m_d->needsUpdate) {
m_d->needsUpdate = value;
baseNodeChangedCallback();
if (!value) {
m_d->updateCompressor.start();
}
}
}
void KisColorizeMask::slotUpdateRegenerateFilling()
{
KisPaintDeviceSP src = parent()->original();
KIS_ASSERT_RECOVER_RETURN(src);
bool filteredSourceValid = m_d->originalSequenceNumber == src->sequenceNumber();
m_d->originalSequenceNumber = src->sequenceNumber();
m_d->coloringProjection->clear();
- KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
+ KisLayerSP parentLayer(dynamic_cast<KisLayer*>(parent().data()));
if (!parentLayer) return;
KisImageSP image = parentLayer->image();
if (image) {
KisColorizeStrokeStrategy *strategy =
new KisColorizeStrokeStrategy(src,
m_d->coloringProjection,
m_d->filteredSource,
filteredSourceValid,
image->bounds(),
- this);
+ KisColorizeMaskSP(this));
Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
const KoColor color =
!stroke.isTransparent ?
stroke.color :
KoColor(Qt::transparent, stroke.color.colorSpace());
strategy->addKeyStroke(stroke.dev, color);
}
connect(strategy, SIGNAL(sigFinished()), SLOT(slotRegenerationFinished()));
KisStrokeId id = image->startStroke(strategy);
image->endStroke(id);
}
}
void KisColorizeMask::slotRegenerationFinished()
{
setNeedsUpdate(true);
}
KisBaseNode::PropertyList KisColorizeMask::sectionModelProperties() const
{
KisBaseNode::PropertyList l = KisMask::sectionModelProperties();
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeNeedsUpdate, needsUpdate());
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeEditKeyStrokes, showKeyStrokes());
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeShowColoring, showColoring());
return l;
}
void KisColorizeMask::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
{
KisMask::setSectionModelProperties(properties);
Q_FOREACH (const KisBaseNode::Property &property, properties) {
if (property.id == KisLayerPropertiesIcons::colorizeNeedsUpdate.id()) {
if (m_d->needsUpdate != property.state.toBool()) {
setNeedsUpdate(property.state.toBool());
}
}
if (property.id == KisLayerPropertiesIcons::colorizeEditKeyStrokes.id()) {
if (m_d->showKeyStrokes != property.state.toBool()) {
setShowKeyStrokes(property.state.toBool());
}
}
if (property.id == KisLayerPropertiesIcons::colorizeShowColoring.id()) {
if (m_d->showColoring != property.state.toBool()) {
setShowColoring(property.state.toBool());
}
}
}
}
KisPaintDeviceSP KisColorizeMask::paintDevice() const
{
- return m_d->showKeyStrokes ? m_d->fakePaintDevice : 0;
+ return m_d->showKeyStrokes ? m_d->fakePaintDevice : KisPaintDeviceSP();
}
KisPaintDeviceSP KisColorizeMask::coloringProjection() const
{
return m_d->coloringProjection;
}
QIcon KisColorizeMask::icon() const
{
return KisIconUtils::loadIcon("colorizeMask");
}
bool KisColorizeMask::accept(KisNodeVisitor &v)
{
return v.visit(this);
}
void KisColorizeMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
return visitor.visit(this, undoAdapter);
}
QRect KisColorizeMask::decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect &rect,
PositionToFilthy maskPos) const
{
Q_UNUSED(maskPos);
KIS_ASSERT(dst != src);
// Draw the filling and the original layer
{
KisPainter gc(dst);
if (m_d->showKeyStrokes &&
m_d->filteredSource &&
!m_d->filteredSource->extent().isEmpty()) {
// TODO: the filtered source should be converted back into alpha!
gc.setOpacity(128);
gc.bitBlt(rect.topLeft(), m_d->filteredSource, rect);
} else {
gc.setOpacity(255);
gc.bitBlt(rect.topLeft(), src, rect);
}
if (m_d->showColoring && m_d->coloringProjection) {
gc.setOpacity(opacity());
gc.setCompositeOp(compositeOpId());
gc.bitBlt(rect.topLeft(), m_d->coloringProjection, rect);
}
}
// Draw the key strokes
if (m_d->showKeyStrokes) {
KisIndirectPaintingSupport::ReadLocker locker(this);
KisSelectionSP selection = m_d->cachedSelection.getSelection();
KisSelectionSP conversionSelection = m_d->cachedConversionSelection.getSelection();
KisPixelSelectionSP tempSelection = conversionSelection->pixelSelection();
KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE;
const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect();
KisFillPainter gc(dst);
QList<KeyStroke> extendedStrokes = m_d->keyStrokes;
if (m_d->currentKeyStrokeDevice &&
m_d->needAddCurrentKeyStroke &&
!isTemporaryTargetErasing) {
extendedStrokes << KeyStroke(m_d->currentKeyStrokeDevice, m_d->currentColor);
}
Q_FOREACH (const KeyStroke &stroke, extendedStrokes) {
selection->pixelSelection()->makeCloneFromRough(stroke.dev, rect);
gc.setSelection(selection);
if (stroke.color == m_d->currentColor ||
(isTemporaryTargetErasing &&
temporaryExtent.intersects(selection->pixelSelection()->selectedRect()))) {
if (temporaryTarget) {
tempSelection->copyAlphaFrom(temporaryTarget, rect);
KisPainter selectionPainter(selection->pixelSelection());
setupTemporaryPainter(&selectionPainter);
selectionPainter.bitBlt(rect.topLeft(), tempSelection, rect);
}
}
gc.fillSelection(rect, stroke.color);
}
m_d->cachedSelection.putSelection(selection);
m_d->cachedSelection.putSelection(conversionSelection);
}
return rect;
}
QRect KisColorizeMask::extent() const
{
QRect rc;
// TODO: take care about the filtered device, which can be painted
// semi-transparent sometimes
if (m_d->showColoring && m_d->coloringProjection) {
rc |= m_d->coloringProjection->extent();
}
if (m_d->showKeyStrokes) {
Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
rc |= stroke.dev->extent();
}
KisIndirectPaintingSupport::ReadLocker locker(this);
KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
if (temporaryTarget) {
rc |= temporaryTarget->extent();
}
}
return rc;
}
QRect KisColorizeMask::exactBounds() const
{
QRect rc;
if (m_d->showColoring && m_d->coloringProjection) {
rc |= m_d->coloringProjection->exactBounds();
}
if (m_d->showKeyStrokes) {
Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
rc |= stroke.dev->exactBounds();
}
KisIndirectPaintingSupport::ReadLocker locker(this);
KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
if (temporaryTarget) {
rc |= temporaryTarget->exactBounds();
}
}
return rc;
}
QRect KisColorizeMask::nonDependentExtent() const
{
return extent();
}
KisImageSP KisColorizeMask::fetchImage() const
{
- KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
- if (!parentLayer) return 0;
+ KisLayerSP parentLayer(dynamic_cast<KisLayer*>(parent().data()));
+ if (!parentLayer) return KisImageSP();
return parentLayer->image();
}
void KisColorizeMask::setImage(KisImageWSP image)
{
- KisDefaultBoundsSP bounds = new KisDefaultBounds(image);
+ KisDefaultBoundsSP bounds(new KisDefaultBounds(image));
auto it = m_d->keyStrokes.begin();
for(; it != m_d->keyStrokes.end(); ++it) {
it->dev->setDefaultBounds(bounds);
}
m_d->coloringProjection->setDefaultBounds(bounds);
m_d->fakePaintDevice->setDefaultBounds(bounds);
m_d->filteredSource->setDefaultBounds(bounds);
}
void KisColorizeMask::setCurrentColor(const KoColor &_color)
{
KoColor color = _color;
color.convertTo(colorSpace());
WriteLocker locker(this);
setNeedsUpdate(true);
QList<KeyStroke>::const_iterator it =
std::find_if(m_d->keyStrokes.constBegin(),
m_d->keyStrokes.constEnd(),
[color] (const KeyStroke &s) {
return s.color == color;
});
KisPaintDeviceSP activeDevice;
bool newKeyStroke = false;
if (it == m_d->keyStrokes.constEnd()) {
activeDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
activeDevice->setParentNode(this);
- activeDevice->setDefaultBounds(new KisDefaultBounds(fetchImage()));
+ activeDevice->setDefaultBounds(KisDefaultBoundsBaseSP(new KisDefaultBounds(fetchImage())));
newKeyStroke = true;
} else {
activeDevice = it->dev;
}
m_d->currentColor = color;
m_d->currentKeyStrokeDevice = activeDevice;
m_d->needAddCurrentKeyStroke = newKeyStroke;
}
struct KeyStrokeAddRemoveCommand : public KisCommandUtils::FlipFlopCommand {
KeyStrokeAddRemoveCommand(bool add, int index, KeyStroke stroke, QList<KeyStroke> *list, KisColorizeMaskSP node)
: FlipFlopCommand(!add),
m_index(index), m_stroke(stroke),
m_list(list), m_node(node) {}
void init() override {
m_list->insert(m_index, m_stroke);
emit m_node->sigKeyStrokesListChanged();
}
void end() override {
KIS_ASSERT_RECOVER_RETURN((*m_list)[m_index] == m_stroke);
m_list->removeAt(m_index);
emit m_node->sigKeyStrokesListChanged();
}
private:
int m_index;
KeyStroke m_stroke;
QList<KeyStroke> *m_list;
KisColorizeMaskSP m_node;
};
void KisColorizeMask::mergeToLayer(KisNodeSP layer, KisPostExecutionUndoAdapter *undoAdapter, const KUndo2MagicString &transactionText,int timedID)
{
Q_UNUSED(layer);
WriteLocker locker(this);
KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE;
const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect();
KisSavedMacroCommand *macro = undoAdapter->createMacro(transactionText);
KisMacroBasedUndoStore store(macro);
KisPostExecutionUndoAdapter fakeUndoAdapter(&store, undoAdapter->strokesFacade());
/**
* Add a new key stroke plane
*/
if (m_d->needAddCurrentKeyStroke && !isTemporaryTargetErasing) {
KeyStroke key(m_d->currentKeyStrokeDevice, m_d->currentColor);
KUndo2Command *cmd =
new KeyStrokeAddRemoveCommand(
- true, m_d->keyStrokes.size(), key, &m_d->keyStrokes, this);
+ true, m_d->keyStrokes.size(), key, &m_d->keyStrokes, KisColorizeMaskSP(this));
cmd->redo();
fakeUndoAdapter.addCommand(toQShared(cmd));
}
/**
* When erasing, the brush affects all the key strokes, not only
* the current one.
*/
if (!isTemporaryTargetErasing) {
mergeToLayerImpl(m_d->currentKeyStrokeDevice, &fakeUndoAdapter, transactionText, timedID, false);
} else {
Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
if (temporaryExtent.intersects(stroke.dev->extent())) {
mergeToLayerImpl(stroke.dev, &fakeUndoAdapter, transactionText, timedID, false);
}
}
}
mergeToLayerImpl(m_d->fakePaintDevice, &fakeUndoAdapter, transactionText, timedID, false);
m_d->currentKeyStrokeDevice = 0;
m_d->currentColor = KoColor();
releaseResources();
/**
* Try removing the key strokes that has been completely erased
*/
if (isTemporaryTargetErasing) {
for (int index = 0; index < m_d->keyStrokes.size(); /*noop*/) {
const KeyStroke &stroke = m_d->keyStrokes[index];
if (stroke.dev->exactBounds().isEmpty()) {
KUndo2Command *cmd =
new KeyStrokeAddRemoveCommand(
- false, index, stroke, &m_d->keyStrokes, this);
+ false, index, stroke, &m_d->keyStrokes, KisColorizeMaskSP(this));
cmd->redo();
fakeUndoAdapter.addCommand(toQShared(cmd));
} else {
index++;
}
}
}
undoAdapter->addMacro(macro);
}
void KisColorizeMask::writeMergeData(KisPainter *painter, KisPaintDeviceSP src)
{
const KoColorSpace *alpha8 = KoColorSpaceRegistry::instance()->alpha8();
const bool nonAlphaDst = !(*painter->device()->colorSpace() == *alpha8);
if (nonAlphaDst) {
Q_FOREACH (const QRect &rc, src->region().rects()) {
painter->bitBlt(rc.topLeft(), src, rc);
}
} else {
KisSelectionSP conversionSelection = m_d->cachedConversionSelection.getSelection();
KisPixelSelectionSP tempSelection = conversionSelection->pixelSelection();
Q_FOREACH (const QRect &rc, src->region().rects()) {
tempSelection->copyAlphaFrom(src, rc);
painter->bitBlt(rc.topLeft(), tempSelection, rc);
}
m_d->cachedSelection.putSelection(conversionSelection);
}
}
bool KisColorizeMask::showColoring() const
{
return m_d->showColoring;
}
void KisColorizeMask::setShowColoring(bool value)
{
QRect savedExtent;
if (m_d->showColoring && !value) {
savedExtent = extent();
}
m_d->showColoring = value;
if (!savedExtent.isEmpty()) {
setDirty(savedExtent);
}
}
bool KisColorizeMask::showKeyStrokes() const
{
return m_d->showKeyStrokes;
}
void KisColorizeMask::setShowKeyStrokes(bool value)
{
QRect savedExtent;
if (m_d->showKeyStrokes && !value) {
savedExtent = extent();
}
m_d->showKeyStrokes = value;
if (!savedExtent.isEmpty()) {
setDirty(savedExtent);
}
}
KisColorizeMask::KeyStrokeColors KisColorizeMask::keyStrokesColors() const
{
KeyStrokeColors colors;
// TODO: thread safety!
for (int i = 0; i < m_d->keyStrokes.size(); i++) {
colors.colors << m_d->keyStrokes[i].color;
if (m_d->keyStrokes[i].isTransparent) {
colors.transparentIndex = i;
}
}
return colors;
}
struct SetKeyStrokeColorsCommand : public KUndo2Command {
SetKeyStrokeColorsCommand(const QList<KeyStroke> newList, QList<KeyStroke> *list, KisColorizeMaskSP node)
: m_newList(newList),
m_oldList(*list),
m_list(list),
m_node(node) {}
void redo() override {
*m_list = m_newList;
emit m_node->sigKeyStrokesListChanged();
m_node->setDirty();
}
void undo() override {
*m_list = m_oldList;
emit m_node->sigKeyStrokesListChanged();
m_node->setDirty();
}
private:
QList<KeyStroke> m_newList;
QList<KeyStroke> m_oldList;
QList<KeyStroke> *m_list;
KisColorizeMaskSP m_node;
};
void KisColorizeMask::setKeyStrokesColors(KeyStrokeColors colors)
{
KIS_ASSERT_RECOVER_RETURN(colors.colors.size() == m_d->keyStrokes.size());
QList<KeyStroke> newList = m_d->keyStrokes;
for (int i = 0; i < newList.size(); i++) {
newList[i].color = colors.colors[i];
newList[i].color.convertTo(colorSpace());
newList[i].isTransparent = colors.transparentIndex == i;
}
- KisProcessingApplicator applicator(fetchImage(), this,
+ KisProcessingApplicator applicator(fetchImage(), KisNodeSP(this),
KisProcessingApplicator::NONE,
KisImageSignalVector(),
kundo2_i18n("Change Key Stroke Color"));
applicator.applyCommand(
new SetKeyStrokeColorsCommand(
- newList, &m_d->keyStrokes, this));
+ newList, &m_d->keyStrokes, KisColorizeMaskSP(this)));
applicator.end();
}
void KisColorizeMask::removeKeyStroke(const KoColor &_color)
{
KoColor color = _color;
color.convertTo(colorSpace());
QList<KeyStroke>::iterator it =
std::find_if(m_d->keyStrokes.begin(),
m_d->keyStrokes.end(),
[color] (const KeyStroke &s) {
return s.color == color;
});
KIS_SAFE_ASSERT_RECOVER_RETURN(it != m_d->keyStrokes.end());
const int index = it - m_d->keyStrokes.begin();
- KisProcessingApplicator applicator(fetchImage(), this,
+ KisProcessingApplicator applicator(KisImageWSP(fetchImage()), KisNodeSP(this),
KisProcessingApplicator::NONE,
KisImageSignalVector(),
kundo2_i18n("Remove Key Stroke"));
applicator.applyCommand(
new KeyStrokeAddRemoveCommand(
- false, index, *it, &m_d->keyStrokes, this));
+ false, index, *it, &m_d->keyStrokes, KisColorizeMaskSP(this)));
applicator.end();
}
QVector<KisPaintDeviceSP> KisColorizeMask::allPaintDevices() const
{
QVector<KisPaintDeviceSP> devices;
Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
devices << stroke.dev;
}
devices << m_d->coloringProjection;
devices << m_d->fakePaintDevice;
return devices;
}
void KisColorizeMask::resetCache()
{
m_d->filteredSource->clear();
m_d->originalSequenceNumber = -1;
rerenderFakePaintDevice();
}
void KisColorizeMask::rerenderFakePaintDevice()
{
m_d->fakePaintDevice->clear();
KisFillPainter gc(m_d->fakePaintDevice);
KisSelectionSP selection = m_d->cachedSelection.getSelection();
Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
const QRect rect = stroke.dev->extent();
selection->pixelSelection()->makeCloneFromRough(stroke.dev, rect);
gc.setSelection(selection);
gc.fillSelection(rect, stroke.color);
}
m_d->cachedSelection.putSelection(selection);
}
void KisColorizeMask::testingAddKeyStroke(KisPaintDeviceSP dev, const KoColor &color, bool isTransparent)
{
m_d->keyStrokes << KeyStroke(dev, color, isTransparent);
}
void KisColorizeMask::testingRegenerateMask()
{
slotUpdateRegenerateFilling();
}
KisPaintDeviceSP KisColorizeMask::testingFilteredSource() const
{
return m_d->filteredSource;
}
QList<KeyStroke> KisColorizeMask::fetchKeyStrokesDirect() const
{
return m_d->keyStrokes;
}
void KisColorizeMask::setKeyStrokesDirect(const QList<KisLazyFillTools::KeyStroke> &strokes)
{
m_d->keyStrokes = strokes;
KisImageSP image = fetchImage();
KIS_SAFE_ASSERT_RECOVER_RETURN(image);
setImage(image);
}
qint32 KisColorizeMask::x() const
{
return m_d->offset.x();
}
qint32 KisColorizeMask::y() const
{
return m_d->offset.y();
}
void KisColorizeMask::setX(qint32 x)
{
const QPoint oldOffset = m_d->offset;
m_d->offset.rx() = x;
moveAllInternalDevices(m_d->offset - oldOffset);
}
void KisColorizeMask::setY(qint32 y)
{
const QPoint oldOffset = m_d->offset;
m_d->offset.ry() = y;
moveAllInternalDevices(m_d->offset - oldOffset);
}
KisPaintDeviceList KisColorizeMask::getLodCapableDevices() const
{
KisPaintDeviceList list;
auto it = m_d->keyStrokes.begin();
for(; it != m_d->keyStrokes.end(); ++it) {
list << it->dev;
}
list << m_d->coloringProjection;
list << m_d->fakePaintDevice;
list << m_d->filteredSource;
return list;
}
void KisColorizeMask::moveAllInternalDevices(const QPoint &diff)
{
QVector<KisPaintDeviceSP> devices = allPaintDevices();
Q_FOREACH (KisPaintDeviceSP dev, devices) {
dev->moveTo(dev->offset() + diff);
}
}
diff --git a/libs/image/lazybrush/kis_lazy_fill_graph.h b/libs/image/lazybrush/kis_lazy_fill_graph.h
index c554e464d9..2a7eefd072 100644
--- a/libs/image/lazybrush/kis_lazy_fill_graph.h
+++ b/libs/image/lazybrush/kis_lazy_fill_graph.h
@@ -1,1029 +1,1028 @@
/*
* Copyright (c) 2016 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_LAZY_FILL_GRAPH_H
#define __KIS_LAZY_FILL_GRAPH_H
#include <numeric>
#include <boost/limits.hpp>
#include <boost/operators.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <QRegion>
//#define USE_LAZY_FILL_SANITY_CHECKS 1
#ifdef USE_LAZY_FILL_SANITY_CHECKS
#define LF_SANITY_ASSERT(x) KIS_ASSERT(x)
#define LF_SANITY_ASSERT_RECOVER(x) KIS_ASSERT_RECOVER(x)
#else
#define LF_SANITY_ASSERT(x)
#define LF_SANITY_ASSERT_RECOVER(x) if (0)
#endif /* USE_LAZY_FILL_SANITY_CHECKS */
using namespace boost;
/*
BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<ScribbleMap, vertex_descriptor> )); //read flow-values from vertices
*/
class KisLazyFillGraph;
//===================
// Index Property Map
//===================
template <typename Graph,
typename Descriptor,
typename Index>
struct lazy_fill_graph_index_map {
public:
typedef Index value_type;
typedef Index reference_type;
typedef reference_type reference;
typedef Descriptor key_type;
typedef readable_property_map_tag category;
lazy_fill_graph_index_map() { }
lazy_fill_graph_index_map(const Graph& graph) :
m_graph(&graph) { }
value_type operator[](key_type key) const {
value_type index = m_graph->index_of(key);
LF_SANITY_ASSERT(index >= 0);
return index;
}
friend inline Index
get(const lazy_fill_graph_index_map<Graph, Descriptor, Index>& index_map,
const typename lazy_fill_graph_index_map<Graph, Descriptor, Index>::key_type& key)
{
return (index_map[key]);
}
protected:
const Graph* m_graph;
};
//==========================
// Reverse Edge Property Map
//==========================
template <typename Descriptor>
struct lazy_fill_graph_reverse_edge_map {
public:
typedef Descriptor value_type;
typedef Descriptor reference_type;
typedef reference_type reference;
typedef Descriptor key_type;
typedef readable_property_map_tag category;
lazy_fill_graph_reverse_edge_map() { }
value_type operator[](const key_type& key) const {
return (value_type(key.second, key.first));
}
friend inline Descriptor
get(const lazy_fill_graph_reverse_edge_map<Descriptor>& rev_map,
const typename lazy_fill_graph_reverse_edge_map<Descriptor>::key_type& key)
{
return (rev_map[key]);
}
};
//=================
// Function Objects
//=================
namespace kis_detail {
// vertex_at
template <typename Graph>
struct lazy_fill_graph_vertex_at {
typedef typename graph_traits<Graph>::vertex_descriptor result_type;
lazy_fill_graph_vertex_at() : m_graph(0) {}
lazy_fill_graph_vertex_at(const Graph* graph) :
m_graph(graph) { }
result_type
operator()
(typename graph_traits<Graph>::vertices_size_type vertex_index) const {
return (vertex(vertex_index, *m_graph));
}
private:
const Graph* m_graph;
};
// out_edge_at
template <typename Graph>
struct lazy_fill_graph_out_edge_at {
private:
typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
public:
typedef typename graph_traits<Graph>::edge_descriptor result_type;
lazy_fill_graph_out_edge_at() : m_vertex(), m_graph(0) {}
lazy_fill_graph_out_edge_at(vertex_descriptor source_vertex,
const Graph* graph) :
m_vertex(source_vertex),
m_graph(graph) { }
result_type
operator()
(typename graph_traits<Graph>::degree_size_type out_edge_index) const {
return (out_edge_at(m_vertex, out_edge_index, *m_graph));
}
private:
vertex_descriptor m_vertex;
const Graph* m_graph;
};
// in_edge_at
template <typename Graph>
struct lazy_fill_graph_in_edge_at {
private:
typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
public:
typedef typename graph_traits<Graph>::edge_descriptor result_type;
lazy_fill_graph_in_edge_at() : m_vertex(), m_graph(0) {}
lazy_fill_graph_in_edge_at(vertex_descriptor target_vertex,
const Graph* graph) :
m_vertex(target_vertex),
m_graph(graph) { }
result_type
operator()
(typename graph_traits<Graph>::degree_size_type in_edge_index) const {
return (in_edge_at(m_vertex, in_edge_index, *m_graph));
}
private:
vertex_descriptor m_vertex;
const Graph* m_graph;
};
// edge_at
template <typename Graph>
struct lazy_fill_graph_edge_at {
typedef typename graph_traits<Graph>::edge_descriptor result_type;
lazy_fill_graph_edge_at() : m_graph(0) {}
lazy_fill_graph_edge_at(const Graph* graph) :
m_graph(graph) { }
result_type
operator()
(typename graph_traits<Graph>::edges_size_type edge_index) const {
return (edge_at(edge_index, *m_graph));
}
private:
const Graph* m_graph;
};
// adjacent_vertex_at
template <typename Graph>
struct lazy_fill_graph_adjacent_vertex_at {
public:
typedef typename graph_traits<Graph>::vertex_descriptor result_type;
lazy_fill_graph_adjacent_vertex_at(result_type source_vertex,
const Graph* graph) :
m_vertex(source_vertex),
m_graph(graph) { }
result_type
operator()
(typename graph_traits<Graph>::degree_size_type adjacent_index) const {
return (target(out_edge_at(m_vertex, adjacent_index, *m_graph), *m_graph));
}
private:
result_type m_vertex;
const Graph* m_graph;
};
} // namespace kis_detail
class KisLazyFillGraph
{
public:
typedef KisLazyFillGraph type;
typedef long VertexIndex;
typedef long EdgeIndex;
// sizes
typedef VertexIndex vertices_size_type;
typedef EdgeIndex edges_size_type;
typedef EdgeIndex degree_size_type;
struct VertexDescriptor : public equality_comparable<VertexDescriptor>
{
enum VertexType {
NORMAL = 0,
LABEL_A,
LABEL_B
};
vertices_size_type x;
vertices_size_type y;
VertexType type;
VertexDescriptor(vertices_size_type _x, vertices_size_type _y, VertexType _type = NORMAL)
: x(_x), y(_y), type(_type) {}
VertexDescriptor(VertexType _type)
: x(0), y(0), type(_type) {}
VertexDescriptor()
: x(0), y(0), type(NORMAL) {}
bool operator ==(const VertexDescriptor &rhs) const {
return rhs.x == x && rhs.y == y && rhs.type == type;
}
};
// descriptors
typedef VertexDescriptor vertex_descriptor;
typedef std::pair<vertex_descriptor, vertex_descriptor> edge_descriptor;
friend QDebug operator<<(QDebug dbg, const KisLazyFillGraph::edge_descriptor &e);
friend QDebug operator<<(QDebug dbg, const KisLazyFillGraph::vertex_descriptor &v);
// vertex_iterator
typedef counting_iterator<vertices_size_type> vertex_index_iterator;
typedef kis_detail::lazy_fill_graph_vertex_at<KisLazyFillGraph> vertex_function;
typedef transform_iterator<vertex_function, vertex_index_iterator> vertex_iterator;
// edge_iterator
typedef counting_iterator<edges_size_type> edge_index_iterator;
typedef kis_detail::lazy_fill_graph_edge_at<type> edge_function;
typedef transform_iterator<edge_function, edge_index_iterator> edge_iterator;
// out_edge_iterator
typedef counting_iterator<degree_size_type> degree_iterator;
typedef kis_detail::lazy_fill_graph_out_edge_at<type> out_edge_function;
typedef transform_iterator<out_edge_function, degree_iterator> out_edge_iterator;
// adjacency_iterator
typedef kis_detail::lazy_fill_graph_adjacent_vertex_at<type> adjacent_vertex_function;
typedef transform_iterator<adjacent_vertex_function, degree_iterator> adjacency_iterator;
// categories
typedef directed_tag directed_category;
typedef disallow_parallel_edge_tag edge_parallel_category;
struct traversal_category : virtual public incidence_graph_tag,
virtual public adjacency_graph_tag,
virtual public vertex_list_graph_tag,
virtual public edge_list_graph_tag,
virtual public adjacency_matrix_tag { };
static inline vertex_descriptor null_vertex()
{
vertex_descriptor maxed_out_vertex(
std::numeric_limits<vertices_size_type>::max(),
std::numeric_limits<vertices_size_type>::max(),
vertex_descriptor::NORMAL);
return (maxed_out_vertex);
}
KisLazyFillGraph() {}
KisLazyFillGraph(const QRect &mainRect,
const QRegion &aLabelRegion,
const QRegion &bLabelRegion)
: m_x(mainRect.x()),
m_y(mainRect.y()),
m_width(mainRect.width()),
m_height(mainRect.height())
{
m_mainArea = mainRect;
m_aLabelArea = aLabelRegion.boundingRect();
m_bLabelArea = bLabelRegion.boundingRect();
m_aLabelRects = aLabelRegion.rects();
m_bLabelRects = bLabelRegion.rects();
KIS_ASSERT(m_mainArea.contains(m_aLabelArea));
KIS_ASSERT(m_mainArea.contains(m_bLabelArea));
m_numVertices = m_width * m_height + 2;
m_edgeBins << EdgeIndexBin(0, m_mainArea.adjusted(0, 0, -1, 0), HORIZONTAL);
m_edgeBins << EdgeIndexBin(m_edgeBins.last(), m_mainArea.adjusted(0, 0, -1, 0), HORIZONTAL_REVERSED);
m_edgeBins << EdgeIndexBin(m_edgeBins.last(), m_mainArea.adjusted(0, 0, 0, -1), VERTICAL);
m_edgeBins << EdgeIndexBin(m_edgeBins.last(), m_mainArea.adjusted(0, 0, 0, -1), VERTICAL_REVERSED);
Q_FOREACH (const QRect &rc, m_aLabelRects) {
m_edgeBins << EdgeIndexBin(m_edgeBins.last(), rc, LABEL_A);
}
// out_edge_at relies on the sequential layout of reversed edges of one type
m_aReversedEdgesStart = m_edgeBins.last().last() + 1;
Q_FOREACH (const QRect &rc, m_aLabelRects) {
m_edgeBins << EdgeIndexBin(m_edgeBins.last(), rc, LABEL_A_REVERSED);
}
m_numAEdges = m_edgeBins.last().last() + 1 - m_aReversedEdgesStart;
Q_FOREACH (const QRect &rc, m_bLabelRects) {
m_edgeBins << EdgeIndexBin(m_edgeBins.last(), rc, LABEL_B);
}
// out_edge_at relies on the sequential layout of reversed edges of one type
m_bReversedEdgesStart = m_edgeBins.last().last() + 1;
Q_FOREACH (const QRect &rc, m_bLabelRects) {
m_edgeBins << EdgeIndexBin(m_edgeBins.last(), rc, LABEL_B_REVERSED);
}
m_numBEdges = m_edgeBins.last().last() + 1 - m_bReversedEdgesStart;
m_numEdges = m_edgeBins.last().last() + 1;
}
~KisLazyFillGraph() {
}
QSize size() const { return QSize(m_width, m_height); }
QRect rect() const { return QRect(m_x, m_y, m_width, m_height); }
vertices_size_type m_x;
vertices_size_type m_y;
vertices_size_type m_width;
vertices_size_type m_height;
vertices_size_type m_numVertices;
vertices_size_type m_numEdges;
vertices_size_type m_aReversedEdgesStart;
vertices_size_type m_bReversedEdgesStart;
vertices_size_type m_numAEdges;
vertices_size_type m_numBEdges;
enum EdgeIndexBinId {
HORIZONTAL,
HORIZONTAL_REVERSED,
VERTICAL,
VERTICAL_REVERSED,
LABEL_A,
LABEL_A_REVERSED,
LABEL_B,
LABEL_B_REVERSED,
};
struct EdgeIndexBin {
EdgeIndexBin()
: start(0), stride(0), size(0) {}
EdgeIndexBin(edges_size_type _start,
const QRect &_rect,
EdgeIndexBinId _binId)
: start(_start),
stride(_rect.width()),
size(_rect.width() * _rect.height()),
xOffset(_rect.x()),
yOffset(_rect.y()),
binId(_binId),
isReversed(int(_binId) & 0x1),
rect(_rect) {}
EdgeIndexBin(const EdgeIndexBin &putAfter,
const QRect &_rect,
EdgeIndexBinId _binId)
: start(putAfter.last() + 1),
stride(_rect.width()),
size(_rect.width() * _rect.height()),
xOffset(_rect.x()),
yOffset(_rect.y()),
binId(_binId),
isReversed(int(_binId) & 0x1),
rect(_rect) {}
edges_size_type last() const {
return start + size - 1;
}
bool contains(edges_size_type index) const {
index -= start;
return index >= 0 && index < size;
}
bool contains(const edge_descriptor &edge) const {
return indexOf(edge) >= 0;
}
edges_size_type indexOf(const edge_descriptor &edge) const {
vertex_descriptor src_vertex = source(edge, *this);
vertex_descriptor dst_vertex = target(edge, *this);
const bool srcColoredA = src_vertex.type == vertex_descriptor::LABEL_A;
const bool dstColoredA = dst_vertex.type == vertex_descriptor::LABEL_A;
const bool srcColoredB = src_vertex.type == vertex_descriptor::LABEL_B;
const bool dstColoredB = dst_vertex.type == vertex_descriptor::LABEL_B;
if (srcColoredA || dstColoredA) {
const bool edgeReversed = srcColoredA;
if (isReversed != edgeReversed ||
(binId != LABEL_A && binId != LABEL_A_REVERSED) ||
(srcColoredA && (dst_vertex.type != vertex_descriptor::NORMAL)) ||
(dstColoredA && (src_vertex.type != vertex_descriptor::NORMAL))) {
return -1;
}
} else if (srcColoredB || dstColoredB) {
const bool edgeReversed = srcColoredB;
if (isReversed != edgeReversed ||
(binId != LABEL_B && binId != LABEL_B_REVERSED) ||
(srcColoredB && (dst_vertex.type != vertex_descriptor::NORMAL)) ||
(dstColoredB && (src_vertex.type != vertex_descriptor::NORMAL))) {
return -1;
}
} else {
const vertices_size_type xDiff = dst_vertex.x - src_vertex.x;
const vertices_size_type yDiff = dst_vertex.y - src_vertex.y;
const vertices_size_type xAbsDiff = qAbs(xDiff);
const vertices_size_type yAbsDiff = qAbs(yDiff);
const bool edgeReversed = xDiff < 0 || yDiff < 0;
if (isReversed != edgeReversed ||
(xDiff && binId != HORIZONTAL && binId != HORIZONTAL_REVERSED) ||
(yDiff && binId != VERTICAL && binId != VERTICAL_REVERSED) ||
xAbsDiff > 1 ||
yAbsDiff > 1 ||
xAbsDiff == yAbsDiff) {
return -1;
}
}
if (isReversed) {
std::swap(src_vertex, dst_vertex);
}
// using direct QRect::contains makes the code 30% slower
const int x = src_vertex.x;
const int y = src_vertex.y;
if (x < rect.x() || x > rect.right() ||
y < rect.y() || y > rect.bottom()) {
return -1;
}
edges_size_type internalIndex =
(src_vertex.x - xOffset) +
(src_vertex.y - yOffset) * stride;
LF_SANITY_ASSERT_RECOVER(internalIndex >= 0 && internalIndex < size) {
return -1;
}
return internalIndex + start;
}
edge_descriptor edgeAt(edges_size_type index) const {
edges_size_type localOffset = index - start;
if (localOffset < 0 || localOffset >= size) {
return edge_descriptor();
}
const edges_size_type x = localOffset % stride + xOffset;
const edges_size_type y = localOffset / stride + yOffset;
vertex_descriptor src_vertex(x, y, vertex_descriptor::NORMAL);
vertex_descriptor dst_vertex;
switch (binId) {
case HORIZONTAL:
case HORIZONTAL_REVERSED:
dst_vertex.x = x + 1;
dst_vertex.y = y;
dst_vertex.type = vertex_descriptor::NORMAL;
break;
case VERTICAL:
case VERTICAL_REVERSED:
dst_vertex.x = x;
dst_vertex.y = y + 1;
dst_vertex.type = vertex_descriptor::NORMAL;
break;
case LABEL_A:
case LABEL_A_REVERSED:
dst_vertex.type = vertex_descriptor::LABEL_A;
break;
case LABEL_B:
case LABEL_B_REVERSED:
dst_vertex.type = vertex_descriptor::LABEL_B;
break;
}
if (isReversed) {
std::swap(src_vertex, dst_vertex);
}
return std::make_pair(src_vertex, dst_vertex);
}
edges_size_type start;
edges_size_type stride;
edges_size_type size;
edges_size_type xOffset;
edges_size_type yOffset;
EdgeIndexBinId binId;
bool isReversed;
QRect rect;
};
QVector<EdgeIndexBin> m_edgeBins;
QRect m_aLabelArea;
QRect m_bLabelArea;
QRect m_mainArea;
QVector<QRect> m_aLabelRects;
QVector<QRect> m_bLabelRects;
-protected:
public:
// Returns the number of vertices in the graph
inline vertices_size_type num_vertices() const {
return (m_numVertices);
}
// Returns the number of edges in the graph
inline edges_size_type num_edges() const {
return (m_numEdges);
}
// Returns the index of [vertex] (See also vertex_at)
vertices_size_type index_of(vertex_descriptor vertex) const {
vertices_size_type vertex_index = -1;
switch (vertex.type) {
case vertex_descriptor::NORMAL:
vertex_index = vertex.x - m_x + (vertex.y - m_y) * m_width;
break;
case vertex_descriptor::LABEL_A:
vertex_index = m_numVertices - 2;
break;
case vertex_descriptor::LABEL_B:
vertex_index = m_numVertices - 1;
break;
}
return vertex_index;
}
// Returns the vertex whose index is [vertex_index] (See also
// index_of(vertex_descriptor))
vertex_descriptor vertex_at (vertices_size_type vertex_index) const {
vertex_descriptor vertex;
if (vertex_index == m_numVertices - 2) {
vertex.type = vertex_descriptor::LABEL_A;
} else if (vertex_index == m_numVertices - 1) {
vertex.type = vertex_descriptor::LABEL_B;
} else if (vertex_index >= 0) {
vertex.x = vertex_index % m_width + m_x;
vertex.y = vertex_index / m_width + m_y;
vertex.type = vertex_descriptor::NORMAL;
}
return vertex;
}
// Returns the edge whose index is [edge_index] (See also
// index_of(edge_descriptor)). NOTE: The index mapping is
// dependent upon dimension wrapping.
edge_descriptor edge_at(edges_size_type edge_index) const {
int binIndex = 0;
while (binIndex < m_edgeBins.size() &&
!m_edgeBins[binIndex].contains(edge_index)) {
binIndex++;
}
if (binIndex >= m_edgeBins.size()) {
return edge_descriptor();
}
return m_edgeBins[binIndex].edgeAt(edge_index);
}
// Returns the index for [edge] (See also edge_at)
edges_size_type index_of(edge_descriptor edge) const {
edges_size_type index = -1;
auto it = m_edgeBins.constBegin();
for (; it != m_edgeBins.constEnd(); ++it) {
index = it->indexOf(edge);
if (index >= 0) break;
}
return index;
}
private:
static vertices_size_type numVacantEdges(const vertex_descriptor &vertex, const QRect &rc) {
vertices_size_type vacantEdges = 4;
if (vertex.x == rc.x()) {
vacantEdges--;
}
if (vertex.y == rc.y()) {
vacantEdges--;
}
if (vertex.x == rc.right()) {
vacantEdges--;
}
if (vertex.y == rc.bottom()) {
vacantEdges--;
}
return vacantEdges;
}
static inline bool findInRects(const QVector<QRect> &rects, const QPoint &pt) {
bool result = false;
auto it = rects.constBegin();
for (; it != rects.constEnd(); ++it) {
if (it->contains(pt)) {
result = true;
break;
}
}
return result;
}
public:
// Returns the number of out-edges for [vertex]
degree_size_type out_degree(vertex_descriptor vertex) const {
degree_size_type out_edge_count = 0;
if (index_of(vertex) < 0) return out_edge_count;
switch (vertex.type) {
case vertex_descriptor::NORMAL: {
out_edge_count = numVacantEdges(vertex, m_mainArea);
const QPoint pt = QPoint(vertex.x, vertex.y);
if (m_aLabelArea.contains(pt) && findInRects(m_aLabelRects, pt)) {
out_edge_count++;
}
if (m_bLabelArea.contains(pt) && findInRects(m_bLabelRects, pt)) {
out_edge_count++;
}
break;
}
case vertex_descriptor::LABEL_A:
out_edge_count = m_numAEdges;
break;
case vertex_descriptor::LABEL_B:
out_edge_count = m_numBEdges;
break;
}
return (out_edge_count);
}
// Returns an out-edge for [vertex] by index. Indices are in the
// range [0, out_degree(vertex)).
edge_descriptor out_edge_at (vertex_descriptor vertex,
degree_size_type out_edge_index) const {
const QPoint pt = QPoint(vertex.x, vertex.y);
vertex_descriptor dst_vertex = vertex;
switch (vertex.type) {
case vertex_descriptor::NORMAL:
if (vertex.x > m_mainArea.x() && !out_edge_index--) {
dst_vertex.x--;
} else if (vertex.y > m_mainArea.y() && !out_edge_index--) {
dst_vertex.y--;
} else if (vertex.x < m_mainArea.right() && !out_edge_index--) {
dst_vertex.x++;
} else if (vertex.y < m_mainArea.bottom() && !out_edge_index--) {
dst_vertex.y++;
} else if (m_aLabelArea.contains(pt) && findInRects(m_aLabelRects, pt) && !out_edge_index--) {
dst_vertex = vertex_descriptor(0, 0, vertex_descriptor::LABEL_A);
} else if (m_bLabelArea.contains(pt) && findInRects(m_bLabelRects, pt) && !out_edge_index--) {
dst_vertex = vertex_descriptor(0, 0, vertex_descriptor::LABEL_B);
} else {
qDebug() << ppVar(vertex) << ppVar(out_edge_index) << ppVar(out_degree(vertex));
qFatal("Wrong edge sub-index");
}
break;
case vertex_descriptor::LABEL_A: {
edge_descriptor edge = edge_at(m_aReversedEdgesStart + out_edge_index);
dst_vertex = edge.second;
break;
}
case vertex_descriptor::LABEL_B: {
edge_descriptor edge = edge_at(m_bReversedEdgesStart + out_edge_index);
dst_vertex = edge.second;
break;
}
}
return std::make_pair(vertex, dst_vertex);
}
public:
//================
// VertexListGraph
//================
friend inline std::pair<typename type::vertex_iterator,
typename type::vertex_iterator>
vertices(const type& graph) {
typedef typename type::vertex_iterator vertex_iterator;
typedef typename type::vertex_function vertex_function;
typedef typename type::vertex_index_iterator vertex_index_iterator;
return (std::make_pair
(vertex_iterator(vertex_index_iterator(0),
vertex_function(&graph)),
vertex_iterator(vertex_index_iterator(graph.num_vertices()),
vertex_function(&graph))));
}
friend inline typename type::vertices_size_type
num_vertices(const type& graph) {
return (graph.num_vertices());
}
friend inline typename type::vertex_descriptor
vertex(typename type::vertices_size_type vertex_index,
const type& graph) {
return (graph.vertex_at(vertex_index));
}
//===============
// IncidenceGraph
//===============
friend inline std::pair<typename type::out_edge_iterator,
typename type::out_edge_iterator>
out_edges(typename type::vertex_descriptor vertex,
const type& graph) {
typedef typename type::degree_iterator degree_iterator;
typedef typename type::out_edge_function out_edge_function;
typedef typename type::out_edge_iterator out_edge_iterator;
return (std::make_pair
(out_edge_iterator(degree_iterator(0),
out_edge_function(vertex, &graph)),
out_edge_iterator(degree_iterator(graph.out_degree(vertex)),
out_edge_function(vertex, &graph))));
}
friend inline typename type::degree_size_type
out_degree
(typename type::vertex_descriptor vertex,
const type& graph) {
return (graph.out_degree(vertex));
}
friend inline typename type::edge_descriptor
out_edge_at(typename type::vertex_descriptor vertex,
typename type::degree_size_type out_edge_index,
const type& graph) {
return (graph.out_edge_at(vertex, out_edge_index));
}
//===============
// AdjacencyGraph
//===============
friend typename std::pair<typename type::adjacency_iterator,
typename type::adjacency_iterator>
adjacent_vertices (typename type::vertex_descriptor vertex,
const type& graph) {
typedef typename type::degree_iterator degree_iterator;
typedef typename type::adjacent_vertex_function adjacent_vertex_function;
typedef typename type::adjacency_iterator adjacency_iterator;
return (std::make_pair
(adjacency_iterator(degree_iterator(0),
adjacent_vertex_function(vertex, &graph)),
adjacency_iterator(degree_iterator(graph.out_degree(vertex)),
adjacent_vertex_function(vertex, &graph))));
}
//==================
// Adjacency Matrix
//==================
friend std::pair<typename type::edge_descriptor, bool>
edge (typename type::vertex_descriptor source_vertex,
typename type::vertex_descriptor destination_vertex,
const type& graph) {
std::pair<typename type::edge_descriptor, bool> edge_exists =
std::make_pair(std::make_pair(source_vertex, destination_vertex), false);
const edges_size_type index = graph.index_of(edge_exists.first);
edge_exists.second = index >= 0;
return edge_exists;
}
//==============
// EdgeListGraph
//==============
friend inline typename type::edges_size_type
num_edges(const type& graph) {
return (graph.num_edges());
}
friend inline typename type::edge_descriptor
edge_at(typename type::edges_size_type edge_index,
const type& graph) {
return (graph.edge_at(edge_index));
}
friend inline std::pair<typename type::edge_iterator,
typename type::edge_iterator>
edges(const type& graph) {
typedef typename type::edge_index_iterator edge_index_iterator;
typedef typename type::edge_function edge_function;
typedef typename type::edge_iterator edge_iterator;
return (std::make_pair
(edge_iterator(edge_index_iterator(0),
edge_function(&graph)),
edge_iterator(edge_index_iterator(graph.num_edges()),
edge_function(&graph))));
}
//=============================
// Index Property Map Functions
//=============================
friend inline typename type::vertices_size_type
get(vertex_index_t,
const type& graph,
typename type::vertex_descriptor vertex) {
type::vertices_size_type index = graph.index_of(vertex);
LF_SANITY_ASSERT(index >= 0);
return index;
}
friend inline typename type::edges_size_type
get(edge_index_t,
const type& graph,
typename type::edge_descriptor edge) {
type::edges_size_type index = graph.index_of(edge);
LF_SANITY_ASSERT(index >= 0);
return index;
}
friend inline lazy_fill_graph_index_map<
type,
typename type::vertex_descriptor,
typename type::vertices_size_type>
get(vertex_index_t, const type& graph) {
return (lazy_fill_graph_index_map<
type,
typename type::vertex_descriptor,
typename type::vertices_size_type>(graph));
}
friend inline lazy_fill_graph_index_map<
type,
typename type::edge_descriptor,
typename type::edges_size_type>
get(edge_index_t, const type& graph) {
return (lazy_fill_graph_index_map<
type,
typename type::edge_descriptor,
typename type::edges_size_type>(graph));
}
friend inline lazy_fill_graph_reverse_edge_map<
typename type::edge_descriptor>
get(edge_reverse_t, const type& graph) {
Q_UNUSED(graph);
return (lazy_fill_graph_reverse_edge_map<
typename type::edge_descriptor>());
}
template<typename Graph,
typename Descriptor,
typename Index>
friend struct lazy_fill_graph_index_map;
template<typename Descriptor>
friend struct lazy_fill_graph_reverse_edge_map;
};
namespace boost {
template <>
struct property_map<KisLazyFillGraph, vertex_index_t> {
typedef lazy_fill_graph_index_map<KisLazyFillGraph,
typename graph_traits<KisLazyFillGraph>::vertex_descriptor,
typename graph_traits<KisLazyFillGraph>::vertices_size_type> type;
typedef type const_type;
};
template<>
struct property_map<KisLazyFillGraph, edge_index_t> {
typedef lazy_fill_graph_index_map<KisLazyFillGraph,
typename graph_traits<KisLazyFillGraph>::edge_descriptor,
typename graph_traits<KisLazyFillGraph>::edges_size_type> type;
typedef type const_type;
};
}
namespace boost {
template <>
struct property_map<KisLazyFillGraph, edge_reverse_t> {
typedef lazy_fill_graph_reverse_edge_map<typename graph_traits<KisLazyFillGraph>::edge_descriptor> type;
typedef type const_type;
};
}
QDebug operator<<(QDebug dbg, const KisLazyFillGraph::vertex_descriptor &v) {
const QString type =
v.type == KisLazyFillGraph::vertex_descriptor::NORMAL ? "normal" :
v.type == KisLazyFillGraph::vertex_descriptor::LABEL_A ? "label_A" :
v.type == KisLazyFillGraph::vertex_descriptor::LABEL_B ? "label_B" : "<unknown>";
dbg.nospace() << "(" << v.x << ", " << v.y << ", " << type << ")";
return dbg.space();
}
QDebug operator<<(QDebug dbg, const KisLazyFillGraph::edge_descriptor &e) {
KisLazyFillGraph::vertex_descriptor src = e.first;
KisLazyFillGraph::vertex_descriptor dst = e.second;
dbg.nospace() << "(" << src << " -> " << dst << ")";
return dbg.space();
}
#endif /* __KIS_LAZY_FILL_GRAPH_H */
diff --git a/libs/image/lazybrush/kis_multiway_cut.cpp b/libs/image/lazybrush/kis_multiway_cut.cpp
index 2e38af28a3..bd750fb2f8 100644
--- a/libs/image/lazybrush/kis_multiway_cut.cpp
+++ b/libs/image/lazybrush/kis_multiway_cut.cpp
@@ -1,178 +1,178 @@
/*
* Copyright (c) 2016 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_multiway_cut.h"
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include <KoColor.h>
#include "kis_paint_device.h"
#include "kis_painter.h"
#include "kis_lazy_fill_tools.h"
#include "kis_sequential_iterator.h"
#include <floodfill/kis_scanline_fill.h>
using namespace KisLazyFillTools;
struct KisMultiwayCut::Private
{
KisPaintDeviceSP src;
KisPaintDeviceSP dst;
KisPaintDeviceSP mask;
QRect boundingRect;
QVector<KeyStroke> keyStrokes;
static void maskOutKeyStroke(KisPaintDeviceSP keyStrokeDevice, KisPaintDeviceSP mask, const QRect &boundingRect);
};
KisMultiwayCut::KisMultiwayCut(KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &boundingRect)
: m_d(new Private)
{
m_d->src = src;
m_d->dst = dst;
m_d->mask = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
m_d->boundingRect = boundingRect;
}
KisMultiwayCut::~KisMultiwayCut()
{
}
void KisMultiwayCut::addKeyStroke(KisPaintDeviceSP dev, const KoColor &color)
{
m_d->keyStrokes << KeyStroke(dev, color);
}
void KisMultiwayCut::Private::maskOutKeyStroke(KisPaintDeviceSP keyStrokeDevice, KisPaintDeviceSP mask, const QRect &boundingRect)
{
KIS_ASSERT_RECOVER_RETURN(keyStrokeDevice->pixelSize() == 1);
KIS_ASSERT_RECOVER_RETURN(mask->pixelSize() == 1);
QRegion region =
keyStrokeDevice->region() &
mask->exactBounds() & boundingRect;
Q_FOREACH (const QRect &rc, region.rects()) {
KisSequentialIterator dstIt(keyStrokeDevice, rc);
KisSequentialConstIterator mskIt(mask, rc);
do {
if (*mskIt.rawDataConst() > 0) {
*dstIt.rawData() = 0;
}
} while (dstIt.nextPixel() && mskIt.nextPixel());
}
}
bool keyStrokesOrder(const KeyStroke &a, const KeyStroke &b)
{
const bool aTransparent = a.color.opacityU8() == OPACITY_TRANSPARENT_U8;
const bool bTransparent = b.color.opacityU8() == OPACITY_TRANSPARENT_U8;
if (aTransparent && !bTransparent) return true;
if (!aTransparent && bTransparent) return false;
const QRect aRect = a.dev->extent();
const QRect bRect = b.dev->extent();
const int aArea = aRect.width() * aRect.height();
const int bArea = bRect.width() * bRect.height();
return aArea > bArea;
}
void KisMultiwayCut::run()
{
- KisPaintDeviceSP other = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
+ KisPaintDeviceSP other(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
/**
* First sort all the key strokes in a way that all the
* transparent strokes go to the beginning of the list.
*
* This is juat an heuristic: the transparent stroke usually
* represents the background so it is the bigger one. And since
* our algorithm is greedy, we should cover the biggest area
* as fast as possible.
*/
std::stable_sort(m_d->keyStrokes.begin(), m_d->keyStrokes.end(), keyStrokesOrder);
while (m_d->keyStrokes.size() > 1) {
KeyStroke current = m_d->keyStrokes.takeFirst();
// if current scribble is empty, it just has no effect
if (current.dev->exactBounds().isEmpty()) continue;
KisPainter gc(other);
Q_FOREACH (const KeyStroke &s, m_d->keyStrokes) {
const QRect rc = s.dev->extent() & m_d->boundingRect;
gc.bitBlt(rc.topLeft(), s.dev, rc);
}
// if other is empty, it means that *all* other strokes are
// empty, so there is no reason to continue the process
if (other->exactBounds().isEmpty()) {
m_d->keyStrokes.clear();
m_d->keyStrokes << current;
break;
}
KisLazyFillTools::cutOneWay(current.color,
m_d->src,
current.dev,
other,
m_d->dst,
m_d->mask,
m_d->boundingRect);
other->clear();
}
// TODO: check if one can use the last cut for this purpose!
if (m_d->keyStrokes.size() == 1) {
KeyStroke current = m_d->keyStrokes.takeLast();
m_d->maskOutKeyStroke(current.dev, m_d->mask, m_d->boundingRect);
QVector<QPoint> points =
KisLazyFillTools::splitIntoConnectedComponents(current.dev, m_d->boundingRect);
Q_FOREACH (const QPoint &pt, points) {
KisScanlineFill fill(m_d->mask, pt, m_d->boundingRect);
fill.fillColor(current.color, m_d->dst);
}
}
}
KisPaintDeviceSP KisMultiwayCut::srcDevice() const
{
return m_d->src;
}
KisPaintDeviceSP KisMultiwayCut::dstDevice() const
{
return m_d->dst;
}
diff --git a/libs/image/metadata/kis_exif_info_visitor.h b/libs/image/metadata/kis_exif_info_visitor.h
index bb4e99448e..f830821d51 100644
--- a/libs/image/metadata/kis_exif_info_visitor.h
+++ b/libs/image/metadata/kis_exif_info_visitor.h
@@ -1,96 +1,95 @@
/*
* 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.
*/
#ifndef KIS_EXIF_INFO_VISITOR_H
#define KIS_EXIF_INFO_VISITOR_H
#include <kis_node_visitor.h>
#include <metadata/kis_meta_data_store.h>
#include <metadata/kis_meta_data_filter_registry_model.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
class KisExifInfoVisitor : public KisNodeVisitor
{
public:
KisExifInfoVisitor() :
m_exifInfo(0),
m_countPaintLayer(0) { }
public:
bool visit(KisNode*) {
return true;
}
bool visit(KisCloneLayer*) {
return true;
}
bool visit(KisFilterMask*) {
return true;
}
bool visit(KisTransformMask*) {
return true;
}
bool visit(KisTransparencyMask*) {
return true;
}
bool visit(KisSelectionMask*) {
return true;
}
bool visit(KisColorizeMask*) {
return true;
}
bool visit(KisExternalLayer*) {
return true;
}
bool visit(KisGeneratorLayer*) {
return true;
}
bool visit(KisAdjustmentLayer*) {
return true;
}
bool visit(KisPaintLayer* layer) {
m_countPaintLayer++;
if (!layer->metaData()->empty()) {
m_exifInfo = layer->metaData();
}
return true;
}
-
bool visit(KisGroupLayer* layer) {
dbgFile << "Visiting on grouplayer" << layer->name() << "";
return visitAll(layer, true);
}
public:
inline uint countPaintLayer() {
return m_countPaintLayer;
}
inline KisMetaData::Store* exifInfo() {
return m_exifInfo;
}
private:
KisMetaData::Store* m_exifInfo;
uint m_countPaintLayer;
};
#endif
diff --git a/libs/image/processing/kis_crop_processing_visitor.cpp b/libs/image/processing/kis_crop_processing_visitor.cpp
index 4e57038ed1..946122a8b2 100644
--- a/libs/image/processing/kis_crop_processing_visitor.cpp
+++ b/libs/image/processing/kis_crop_processing_visitor.cpp
@@ -1,94 +1,94 @@
/*
* 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_crop_processing_visitor.h"
#include <klocalizedstring.h>
#include "commands_new/kis_node_move_command2.h"
#include "kis_external_layer_iface.h"
#include "kis_paint_device.h"
#include "kis_image.h"
#include "kis_transaction.h"
#include "kis_undo_adapter.h"
#include "kis_transform_mask.h"
#include "lazybrush/kis_colorize_mask.h"
KisCropProcessingVisitor::KisCropProcessingVisitor(const QRect &rect, bool cropLayers, bool moveLayers)
: m_rect(rect),
m_cropLayers(cropLayers),
m_moveLayers(moveLayers)
{
}
void KisCropProcessingVisitor::visitExternalLayer(KisExternalLayer *layer, KisUndoAdapter *undoAdapter)
{
KUndo2Command* command = layer->crop(m_rect);
undoAdapter->addCommand(command);
}
void KisCropProcessingVisitor::moveNodeImpl(KisNode *node, KisUndoAdapter *undoAdapter)
{
if (m_moveLayers) {
QPoint oldPos(node->x(), node->y());
QPoint newPos(node->x() - m_rect.x(), node->y() - m_rect.y());
- KUndo2Command *command = new KisNodeMoveCommand2(node, oldPos, newPos);
+ KUndo2Command *command = new KisNodeMoveCommand2(KisNodeSP(node), oldPos, newPos);
undoAdapter->addCommand(command);
}
}
void KisCropProcessingVisitor::cropPaintDeviceImpl(KisPaintDeviceSP device, KisUndoAdapter *undoAdapter)
{
/**
* TODO: implement actual robust cropping of the selections,
* including the cropping of vector (!) selection.
*/
if (m_cropLayers) {
KisTransaction transaction(kundo2_noi18n("crop"), device);
device->crop(m_rect);
transaction.commit(undoAdapter);
}
}
void KisCropProcessingVisitor::visitNodeWithPaintDevice(KisNode *node, KisUndoAdapter *undoAdapter)
{
cropPaintDeviceImpl(node->paintDevice(), undoAdapter);
moveNodeImpl(node, undoAdapter);
}
void KisCropProcessingVisitor::visit(KisTransformMask *node, KisUndoAdapter *undoAdapter)
{
moveNodeImpl(node, undoAdapter);
KisSimpleProcessingVisitor::visit(node, undoAdapter);
}
void KisCropProcessingVisitor::visitColorizeMask(KisColorizeMask *node, KisUndoAdapter *undoAdapter)
{
QVector<KisPaintDeviceSP> devices = node->allPaintDevices();
Q_FOREACH (KisPaintDeviceSP device, devices) {
cropPaintDeviceImpl(device, undoAdapter);
}
moveNodeImpl(node, undoAdapter);
}
diff --git a/libs/image/recorder/kis_node_query_path.cc b/libs/image/recorder/kis_node_query_path.cc
index 9683bda2f2..5416cd5ed4 100644
--- a/libs/image/recorder/kis_node_query_path.cc
+++ b/libs/image/recorder/kis_node_query_path.cc
@@ -1,217 +1,225 @@
/*
* 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 {
dbgKrita << "No parent";
}
break;
}
case PathElement::Index: {
if (pe.index < _node->childCount()) {
queryLevel(_level + 1, _node->at(pe.index), _result);
} else {
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;
}
+KisNodeSP KisNodeQueryPath::queryUniqueNode(KisImageWSP image, KisNodeSP currentNode) const
+{
+ QList<KisNodeSP> result = queryNodes(image, currentNode);
+ KIS_ASSERT_RECOVER_NOOP(result.size() <= 1);
+
+ return !result.isEmpty() ? result.first() : 0;
+}
+
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
}
Q_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/libs/image/recorder/kis_node_query_path.h b/libs/image/recorder/kis_node_query_path.h
index 31bad05ae3..ea104a9570 100644
--- a/libs/image/recorder/kis_node_query_path.h
+++ b/libs/image/recorder/kis_node_query_path.h
@@ -1,60 +1,61 @@
/*
* 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.
*/
#ifndef _KIS_NODE_QUERY_PATH_H_
#define _KIS_NODE_QUERY_PATH_H_
#include <kis_types.h>
#include <kritaimage_export.h>
/**
* This class represent a path to access a node starting from an other node.
*/
class KRITAIMAGE_EXPORT KisNodeQueryPath
{
KisNodeQueryPath();
public:
~KisNodeQueryPath();
KisNodeQueryPath(const KisNodeQueryPath&);
KisNodeQueryPath& operator=(const KisNodeQueryPath&);
QList<KisNodeSP> queryNodes(KisImageWSP image, KisNodeSP currentNode) const;
+ KisNodeSP queryUniqueNode(KisImageWSP image, KisNodeSP currentNode = 0) const;
bool isRelative() const;
// Use "///" style because of the needed "/*"
/// This function return a string representing this path. Which is a list separated by '\' of:
/// - '*': represents all layers
/// - '..': represents the parent layer
/// - number: index of the layer
/// - '.': represents the current layer
///
/// For instance: "1/*" return all children of the first layer, "../3" return the third layer of the parent
/// of the current layer
/// If the string starts with "/" then it's an aboslute path, otherwise it's a relative path.
QString toString() const;
/**
* @param path
* @param err if non null, it will be filled with an error message
* @see toString for an explanation of the string format
*/
static KisNodeQueryPath fromString(const QString& path);
static KisNodeQueryPath absolutePath(KisNodeSP node);
private:
struct Private;
Private* const d;
};
#endif
diff --git a/libs/image/tests/kis_filter_processing_information_test.cpp b/libs/image/tests/kis_filter_processing_information_test.cpp
index 92f4d99843..8e8724d290 100644
--- a/libs/image/tests/kis_filter_processing_information_test.cpp
+++ b/libs/image/tests/kis_filter_processing_information_test.cpp
@@ -1,37 +1,36 @@
/*
* 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_filter_processing_information_test.h"
#include <QTest>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_paint_device.h"
#include "kis_types.h"
#include "kis_selection.h"
#include "kis_processing_information.h"
void KisProcessingInformationTest::testCreation()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
- KisProcessingInformation test(dev, QPoint(0, 0));
+ KisProcessingInformation test(dev, QPoint(0, 0), KisSelectionSP());
}
-
QTEST_MAIN(KisProcessingInformationTest)
diff --git a/libs/image/tests/kis_image_test.cpp b/libs/image/tests/kis_image_test.cpp
index b94acbefa2..ba1447836e 100644
--- a/libs/image/tests/kis_image_test.cpp
+++ b/libs/image/tests/kis_image_test.cpp
@@ -1,904 +1,1015 @@
/*
* 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>
#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>
#include "kis_keyframe_channel.h"
#include "kis_selection_mask.h"
#include "kis_layer_utils.h"
+#include "kis_annotation.h"
+#include "KisProofingConfiguration.h"
#include "kis_undo_stores.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::benchmarkCreation()
{
const QRect imageRect(0,0,3000,2000);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QList<KisImageSP> images;
QList<KisSurrogateUndoStore*> stores;
QBENCHMARK {
for (int i = 0; i < 10; i++) {
stores << new KisSurrogateUndoStore();
}
for (int i = 0; i < 10; i++) {
KisImageSP image = new KisImage(stores.takeLast(), imageRect.width(), imageRect.height(), cs, "test image");
images << image;
}
}
}
#include "testutil.h"
#include "kis_stroke_strategy.h"
#include <functional>
class ForbiddenLodStrokeStrategy : public KisStrokeStrategy
{
public:
ForbiddenLodStrokeStrategy(std::function<void()> lodCallback)
: m_lodCallback(lodCallback)
{
}
KisStrokeStrategy* createLodClone(int levelOfDetail) override {
Q_UNUSED(levelOfDetail);
m_lodCallback();
return 0;
}
private:
std::function<void()> m_lodCallback;
};
void notifyVar(bool *value) {
*value = true;
}
void KisImageTest::testBlockLevelOfDetail()
{
TestUtil::MaskParent p;
QCOMPARE(p.image->currentLevelOfDetail(), 0);
p.image->setDesiredLevelOfDetail(1);
p.image->waitForDone();
QCOMPARE(p.image->currentLevelOfDetail(), 0);
{
bool lodCreated = false;
KisStrokeId id = p.image->startStroke(
new ForbiddenLodStrokeStrategy(
std::bind(&notifyVar, &lodCreated)));
p.image->endStroke(id);
p.image->waitForDone();
QVERIFY(lodCreated);
}
p.image->setLevelOfDetailBlocked(true);
{
bool lodCreated = false;
KisStrokeId id = p.image->startStroke(
new ForbiddenLodStrokeStrategy(
std::bind(&notifyVar, &lodCreated)));
p.image->endStroke(id);
p.image->waitForDone();
QVERIFY(!lodCreated);
}
p.image->setLevelOfDetailBlocked(false);
p.image->setDesiredLevelOfDetail(1);
{
bool lodCreated = false;
KisStrokeId id = p.image->startStroke(
new ForbiddenLodStrokeStrategy(
std::bind(&notifyVar, &lodCreated)));
p.image->endStroke(id);
p.image->waitForDone();
QVERIFY(lodCreated);
}
}
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);
KisFilterConfigurationSP 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::testCloneImage()
+{
+ KisImageSP image = new KisImage(0, IMAGE_WIDTH, IMAGE_WIDTH, 0, "layer tests");
+ QVERIFY(image->rootLayer() != 0);
+ QVERIFY(image->rootLayer()->firstChild() == 0);
+
+ KisAnnotationSP annotation = new KisAnnotation("mytype", "mydescription", QByteArray());
+ image->addAnnotation(annotation);
+ QVERIFY(image->annotation("mytype"));
+
+ KisProofingConfigurationSP proofing = toQShared(new KisProofingConfiguration());
+ image->setProofingConfiguration(proofing);
+ QVERIFY(image->proofingConfiguration());
+
+ const KoColor defaultColor(Qt::green, image->colorSpace());
+ image->setDefaultProjectionColor(defaultColor);
+ QCOMPARE(image->defaultProjectionColor(), defaultColor);
+
+ KisLayerSP layer = new KisPaintLayer(image, "layer1", OPACITY_OPAQUE_U8);
+ image->addNode(layer);
+ KisLayerSP layer2 = new KisPaintLayer(image, "layer2", OPACITY_OPAQUE_U8);
+ image->addNode(layer2);
+
+ QVERIFY(layer->visible());
+ QVERIFY(layer2->visible());
+
+ QVERIFY(TestUtil::findNode(image->root(), "layer1"));
+ QVERIFY(TestUtil::findNode(image->root(), "layer2"));
+
+ QUuid uuid1 = layer->uuid();
+ QUuid uuid2 = layer2->uuid();
+
+ {
+ KisImageSP newImage = image->clone();
+
+ KisNodeSP newLayer1 = TestUtil::findNode(newImage->root(), "layer1");
+ KisNodeSP newLayer2 = TestUtil::findNode(newImage->root(), "layer2");
+
+ QVERIFY(newLayer1);
+ QVERIFY(newLayer2);
+
+ QVERIFY(newLayer1->uuid() != uuid1);
+ QVERIFY(newLayer2->uuid() != uuid2);
+
+ KisAnnotationSP newAnnotation = newImage->annotation("mytype");
+ QVERIFY(newAnnotation);
+ QVERIFY(newAnnotation != annotation);
+
+ KisProofingConfigurationSP newProofing = newImage->proofingConfiguration();
+ QVERIFY(newProofing);
+ QVERIFY(newProofing != proofing);
+
+ QCOMPARE(newImage->defaultProjectionColor(), defaultColor);
+ }
+
+ {
+ KisImageSP newImage = image->clone(true);
+
+ KisNodeSP newLayer1 = TestUtil::findNode(newImage->root(), "layer1");
+ KisNodeSP newLayer2 = TestUtil::findNode(newImage->root(), "layer2");
+
+ QVERIFY(newLayer1);
+ QVERIFY(newLayer2);
+
+ QVERIFY(newLayer1->uuid() == uuid1);
+ QVERIFY(newLayer2->uuid() == uuid2);
+ }
+}
+
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);
+ KisLayerSP layer = new KisPaintLayer(image, "layer1", OPACITY_OPAQUE_U8);
image->addNode(layer);
- KisLayerSP layer2 = new KisPaintLayer(image, "layer 2", OPACITY_OPAQUE_U8);
+ KisLayerSP layer2 = new KisPaintLayer(image, "layer2", 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();
+ KisLayerCompositionSP comp3 = toQShared(new KisLayerComposition(image, "comp 3"));
+ comp3->store();
+ image->addComposition(comp3);
+
comp.apply();
QVERIFY(layer->visible());
QVERIFY(layer2->visible());
comp2.apply();
QVERIFY(layer->visible());
QVERIFY(!layer2->visible());
+
+ comp.apply();
+
+ QVERIFY(layer->visible());
+ QVERIFY(layer2->visible());
+
+ KisImageSP newImage = image->clone();
+
+ KisNodeSP newLayer1 = TestUtil::findNode(newImage->root(), "layer1");
+ KisNodeSP newLayer2 = TestUtil::findNode(newImage->root(), "layer2");
+
+ QVERIFY(newLayer1);
+ QVERIFY(newLayer2);
+
+ QVERIFY(newLayer1->visible());
+ QVERIFY(newLayer2->visible());
+
+ KisLayerComposition newComp1(comp, newImage);
+ newComp1.apply();
+ QVERIFY(newLayer1->visible());
+ QVERIFY(newLayer2->visible());
+
+ KisLayerComposition newComp2(comp2, newImage);
+ newComp2.apply();
+ QVERIFY(newLayer1->visible());
+ QVERIFY(!newLayer2->visible());
+
+ newComp1.apply();
+ QVERIFY(newLayer1->visible());
+ QVERIFY(newLayer2->visible());
+
+ QVERIFY(!newImage->compositions().isEmpty());
+ KisLayerCompositionSP newComp3 = newImage->compositions().first();
+ newComp3->apply();
+ QVERIFY(newLayer1->visible());
+ QVERIFY(!newLayer2->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;
undoStore = p.undoStore;
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->setCompositeOpId(COMPOSITE_ADD);
layer8->setCompositeOpId(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, layer2);
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->setCompositeOpId(COMPOSITE_ADD);
group1->setCompositeOpId(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();
// 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;
KisSurrogateUndoStore *undoStore;
KisPaintLayerSP layer1;
KisPaintLayerSP layer2;
KisTransparencyMaskSP tmask;
KisGroupLayerSP group1;
KisPaintLayerSP layer3;
KisPaintLayerSP layer4;
KisPaintLayerSP layer5;
KisPaintLayerSP layer6;
KisPaintLayerSP layer7;
KisPaintLayerSP layer8;
};
template<class ContainerTest>
KisLayerSP flattenLayerHelper(ContainerTest &p, KisLayerSP layer, bool nothingHappens = false)
{
QSignalSpy spy(p.image.data(), SIGNAL(sigNodeAddedAsync(KisNodeSP)));
//p.image->flattenLayer(layer);
KisLayerUtils::flattenLayer(p.image, layer);
p.image->waitForDone();
if (nothingHappens) {
Q_ASSERT(!spy.count());
return layer;
}
Q_ASSERT(spy.count() == 1);
QList<QVariant> arguments = spy.takeFirst();
KisNodeSP newNode = arguments.first().value<KisNodeSP>();
KisLayerSP newLayer = dynamic_cast<KisLayer*>(newNode.data());
return newLayer;
}
void KisImageTest::testFlattenLayer()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker chk("flatten", "imagetest");
{
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
KisLayerSP newLayer = flattenLayerHelper(p, p.layer2);
//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 = flattenLayerHelper(p, p.group1);
//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 = flattenLayerHelper(p, p.layer5, 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>
template<class ContainerTest>
KisLayerSP mergeHelper(ContainerTest &p, KisLayerSP layer)
{
KisNodeSP parent = layer->parent();
const int newIndex = parent->index(layer) - 1;
p.image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
//KisLayerUtils::mergeDown(p.image, layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
KisLayerSP newLayer = dynamic_cast<KisLayer*>(parent->at(newIndex).data());
return newLayer;
}
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 = mergeHelper(p, p.layer5);
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 = mergeHelper(p, p.layer2);
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 = mergeHelper(p, p.group1);
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 = mergeHelper(p, p.layer2);
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 = mergeHelper(p, p.layer6);
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 = mergeHelper(p, p.group1);
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 = mergeHelper(p, p.layer8);
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);
}
}
#include "kis_image_animation_interface.h"
void KisImageTest::testMergeDownMultipleFrames()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker img("flatten", "imagetest");
TestUtil::ExternalImageChecker chk("mergedown_simple", "imagetest");
QSet<int> initialFrames;
{
KisLayerSP l = p.layer5;
l->enableAnimation();
KisKeyframeChannel *channel = l->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
channel->addKeyframe(10);
channel->addKeyframe(20);
channel->addKeyframe(30);
QCOMPARE(channel->keyframeCount(), 4);
initialFrames = KisLayerUtils::fetchLayerFramesRecursive(l);
QCOMPARE(initialFrames.size(), 4);
}
{
QCOMPARE(p.layer5->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.layer5->alphaChannelDisabled(), true);
KisLayerSP newLayer = mergeHelper(p, p.layer5);
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);
QVERIFY(newLayer->isAnimated());
QSet<int> newFrames = KisLayerUtils::fetchLayerFramesRecursive(newLayer);
QCOMPARE(newFrames, initialFrames);
foreach (int frame, newFrames) {
KisImageAnimationInterface *interface = p.image->animationInterface();
int savedSwitchedTime = 0;
interface->saveAndResetCurrentTime(frame, &savedSwitchedTime);
QCOMPARE(newLayer->exactBounds(), QRect(100,100,100,100));
interface->restoreCurrentTime(&savedSwitchedTime);
}
p.undoStore->undo();
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
}
}
template<class ContainerTest>
KisNodeSP mergeMultipleHelper(ContainerTest &p, QList<KisNodeSP> selectedNodes, KisNodeSP putAfter)
{
QSignalSpy spy(p.image.data(), SIGNAL(sigNodeAddedAsync(KisNodeSP)));
p.image->mergeMultipleLayers(selectedNodes, putAfter);
//KisLayerUtils::mergeMultipleLayers(p.image, selectedNodes, putAfter);
p.image->waitForDone();
Q_ASSERT(spy.count() == 1);
QList<QVariant> arguments = spy.takeFirst();
KisNodeSP newNode = arguments.first().value<KisNodeSP>();
return newNode;
}
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 = mergeMultipleHelper(p, selectedNodes, 0);
//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 = mergeMultipleHelper(p, selectedNodes, 0);
//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);
}
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 = mergeHelper(p, layer3);
QCOMPARE(newLayer->colorSpace(), p.image->colorSpace());
p.undoStore->undo();
p.image->waitForDone();
}
{
layer2->disableAlphaChannel(true);
KisLayerSP newLayer = mergeHelper(p, layer3);
QCOMPARE(newLayer->colorSpace(), p.image->colorSpace());
p.undoStore->undo();
p.image->waitForDone();
}
}
void KisImageTest::testMergeCrossColorSpace()
{
testMergeCrossColorSpaceImpl(true, false);
testMergeCrossColorSpaceImpl(true, true);
testMergeCrossColorSpaceImpl(false, false);
testMergeCrossColorSpaceImpl(false, true);
}
void KisImageTest::testMergeSelectionMasks()
{
QRect refRect;
TestUtil::MaskParent p;
QRect rect1(100, 100, 100, 100);
QRect rect2(150, 150, 150, 150);
QRect rect3(50, 50, 100, 100);
KisPaintLayerSP layer1 = p.layer;
layer1->paintDevice()->fill(rect1, KoColor(Qt::red, layer1->colorSpace()));
p.image->initialRefreshGraph();
KisSelectionSP sel = new KisSelection(layer1->paintDevice()->defaultBounds());
sel->pixelSelection()->select(rect2, MAX_SELECTED);
KisSelectionMaskSP mask1 = new KisSelectionMask(p.image);
mask1->initSelection(sel, layer1);
p.image->addNode(mask1, layer1);
QVERIFY(!layer1->selection());
mask1->setActive(true);
QCOMPARE(layer1->selection()->selectedExactRect(), QRect(150,150,150,150));
sel->pixelSelection()->select(rect3, MAX_SELECTED);
KisSelectionMaskSP mask2 = new KisSelectionMask(p.image);
mask2->initSelection(sel, layer1);
p.image->addNode(mask2, layer1);
QCOMPARE(layer1->selection()->selectedExactRect(), QRect(150,150,150,150));
mask2->setActive(true);
QCOMPARE(layer1->selection()->selectedExactRect(), QRect(50,50,250,250));
QList<KisNodeSP> selectedNodes;
selectedNodes << mask2
<< mask1;
{
KisNodeSP newLayer = mergeMultipleHelper(p, selectedNodes, 0);
QCOMPARE(newLayer->parent(), KisNodeSP(layer1));
QCOMPARE((int)layer1->childCount(), 1);
QCOMPARE(layer1->selection()->selectedExactRect(), QRect(50,50,250,250));
}
}
void KisImageTest::testFlattenImage()
{
FlattenTestImage p;
KisImageSP image = p.image;
TestUtil::ExternalImageChecker img("flatten", "imagetest");
{
KisLayerUtils::flattenImage(p.image);
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
p.undoStore->undo();
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
}
}
-QTEST_GUILESS_MAIN(KisImageTest)
+QTEST_MAIN(KisImageTest)
diff --git a/libs/image/tests/kis_image_test.h b/libs/image/tests/kis_image_test.h
index 053a203a29..5d098a246d 100644
--- a/libs/image/tests/kis_image_test.h
+++ b/libs/image/tests/kis_image_test.h
@@ -1,55 +1,56 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_IMAGE_TESTER_H
#define KIS_IMAGE_TESTER_H
#include <QtTest>
class KisImageTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void layerTests();
void benchmarkCreation();
void testBlockLevelOfDetail();
void testConvertImageColorSpace();
void testGlobalSelection();
+ void testCloneImage();
void testLayerComposition();
void testFlattenLayer();
void testMergeDown();
void testMergeDownDestinationInheritsAlpha();
void testMergeDownDestinationCustomCompositeOp();
void testMergeDownDestinationSameCompositeOpLayerStyle();
void testMergeDownDestinationSameCompositeOp();
void testMergeDownMultipleFrames();
void testMergeMultiple();
void testMergeCrossColorSpace();
void testMergeSelectionMasks();
void testFlattenImage();
};
#endif
diff --git a/libs/image/tests/kis_stroke_strategy_undo_command_based_test.cpp b/libs/image/tests/kis_stroke_strategy_undo_command_based_test.cpp
index 4388b9a52b..2ac4fb82ed 100644
--- a/libs/image/tests/kis_stroke_strategy_undo_command_based_test.cpp
+++ b/libs/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>
#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() override {
m_result += QString(" ") + text().toString() + undoString(true);
}
void redo() override {
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");
KisStrokeStrategy *strategy =
new KisStrokeStrategyUndoCommandBased(kundo2_noi18n("test"), false,
- image->postExecutionUndoAdapter(),
+ image.data(),
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() override { checkState(); }
void undo() override { 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");
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);
dbgKrita << "Concurrency observed:" << hadConcurrency
<< "/" << NUM_CHECKS * NUM_JOBS;
}
QTEST_MAIN(KisStrokeStrategyUndoCommandBasedTest)
diff --git a/libs/image/tests/kis_strokes_queue_test.cpp b/libs/image/tests/kis_strokes_queue_test.cpp
index 77ab286306..8e16067f26 100644
--- a/libs/image/tests/kis_strokes_queue_test.cpp
+++ b/libs/image/tests/kis_strokes_queue_test.cpp
@@ -1,472 +1,647 @@
/*
* 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_strokes_queue_test.h"
#include <QTest>
#include "scheduler_utils.h"
#include "kis_strokes_queue.h"
#include "kis_updater_context.h"
#include "kis_update_job_item.h"
#include "kis_merge_walker.h"
void KisStrokesQueueTest::testSequentialJobs()
{
KisStrokesQueue queue;
KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy("tri_", false));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.endStroke(id);
KisTestableUpdaterContext context(2);
QVector<KisUpdateJobItem*> jobs;
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "tri_init");
VERIFY_EMPTY(jobs[1]);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "tri_dab");
COMPARE_NAME(jobs[1], "tri_dab");
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "tri_dab");
VERIFY_EMPTY(jobs[1]);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "tri_finish");
VERIFY_EMPTY(jobs[1]);
}
void KisStrokesQueueTest::testConcurrentSequentialBarrier()
{
KisStrokesQueue queue;
KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy("tri_", false));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.endStroke(id);
// make the number of threads higher
KisTestableUpdaterContext context(3);
QVector<KisUpdateJobItem*> jobs;
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "tri_init");
VERIFY_EMPTY(jobs[1]);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "tri_dab");
COMPARE_NAME(jobs[1], "tri_dab");
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "tri_finish");
VERIFY_EMPTY(jobs[1]);
}
void KisStrokesQueueTest::testExclusiveStrokes()
{
KisStrokesQueue queue;
KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy("excl_", true));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.endStroke(id);
// well, this walker is not initialized... but who cares?
KisBaseRectsWalkerSP walker = new KisMergeWalker(QRect());
KisTestableUpdaterContext context(2);
QVector<KisUpdateJobItem*> jobs;
context.addMergeJob(walker);
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_WALKER(jobs[0], walker);
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), true);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "excl_init");
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), true);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "excl_dab");
COMPARE_NAME(jobs[1], "excl_dab");
QCOMPARE(queue.needsExclusiveAccess(), true);
context.clear();
context.addMergeJob(walker);
queue.processQueue(context, false);
COMPARE_WALKER(jobs[0], walker);
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), true);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "excl_dab");
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), true);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "excl_finish");
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), true);
context.clear();
queue.processQueue(context, false);
QCOMPARE(queue.needsExclusiveAccess(), false);
}
void KisStrokesQueueTest::testBarrierStrokeJobs()
{
KisStrokesQueue queue;
KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy("nor_", false));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::BARRIER));
queue.addJob(id, new KisStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.endStroke(id);
// yes, this walker is not initialized again... but who cares?
KisBaseRectsWalkerSP walker = new KisMergeWalker(QRect());
bool externalJobsPending = false;
KisTestableUpdaterContext context(3);
QVector<KisUpdateJobItem*> jobs;
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_init");
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
context.clear();
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_dab");
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
// Now some updates has come...
context.addMergeJob(walker);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_dab");
COMPARE_WALKER(jobs[1], walker);
VERIFY_EMPTY(jobs[2]);
// No difference for the queue
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_dab");
COMPARE_WALKER(jobs[1], walker);
VERIFY_EMPTY(jobs[2]);
// Even more updates has come...
externalJobsPending = true;
// Still no difference for the queue
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_dab");
COMPARE_WALKER(jobs[1], walker);
VERIFY_EMPTY(jobs[2]);
// Now clear the context
context.clear();
// And still no difference for the queue
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
VERIFY_EMPTY(jobs[0]);
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
// Process the last update...
context.addMergeJob(walker);
externalJobsPending = false;
// Yep, the queue is still waiting
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
COMPARE_WALKER(jobs[0], walker);
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
context.clear();
// Finally, we can do our work. Barrier job is executed alone
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_dab");
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
// Barrier job has finished
context.clear();
jobs = context.getJobs();
VERIFY_EMPTY(jobs[0]);
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
// fetch the last (concurrent) one
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_dab");
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
context.clear();
// finish the stroke
queue.processQueue(context, externalJobsPending);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_finish");
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
context.clear();
}
void KisStrokesQueueTest::testStrokesOverlapping()
{
KisStrokesQueue queue;
KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy("1_", false, true));
queue.addJob(id, 0);
// comment out this line to catch an assert
queue.endStroke(id);
id = queue.startStroke(new KisTestingStrokeStrategy("2_", false, true));
queue.addJob(id, 0);
queue.endStroke(id);
// uncomment this line to catch an assert
// queue.addJob(id, 0);
KisTestableUpdaterContext context(2);
QVector<KisUpdateJobItem*> jobs;
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "1_dab");
VERIFY_EMPTY(jobs[1]);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "2_dab");
VERIFY_EMPTY(jobs[1]);
}
void KisStrokesQueueTest::testImmediateCancel()
{
KisStrokesQueue queue;
KisTestableUpdaterContext context(2);
KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy("1_", false, false));
queue.cancelStroke(id);
// this should not crash
queue.processQueue(context, false);
}
void KisStrokesQueueTest::testOpenedStrokeCounter()
{
KisStrokesQueue queue;
QVERIFY(!queue.hasOpenedStrokes());
KisStrokeId id0 = queue.startStroke(new KisTestingStrokeStrategy("0"));
QVERIFY(queue.hasOpenedStrokes());
KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy("1"));
QVERIFY(queue.hasOpenedStrokes());
queue.endStroke(id0);
QVERIFY(queue.hasOpenedStrokes());
queue.endStroke(id1);
QVERIFY(!queue.hasOpenedStrokes());
KisTestableUpdaterContext context(2);
queue.processQueue(context, false); context.clear();
queue.processQueue(context, false); context.clear();
queue.processQueue(context, false); context.clear();
queue.processQueue(context, false); context.clear();
}
void KisStrokesQueueTest::testAsyncCancelWhileOpenedStroke()
{
KisStrokesQueue queue;
KisStrokeId id = queue.startStroke(new KisTestingStrokeStrategy("nor_", false));
queue.addJob(id, 0);
queue.addJob(id, 0);
queue.addJob(id, 0);
// no async cancelling until the stroke is ended by the owner
QVERIFY(!queue.tryCancelCurrentStrokeAsync());
queue.endStroke(id);
QVERIFY(queue.tryCancelCurrentStrokeAsync());
bool externalJobsPending = false;
KisTestableUpdaterContext context(3);
QVector<KisUpdateJobItem*> jobs;
queue.processQueue(context, externalJobsPending);
// no? really?
jobs = context.getJobs();
VERIFY_EMPTY(jobs[0]);
VERIFY_EMPTY(jobs[1]);
VERIFY_EMPTY(jobs[2]);
}
+struct KisStrokesQueueTest::LodStrokesQueueTester {
+
+ LodStrokesQueueTester(bool real = false)
+ : fakeContext(2),
+ realContext(2),
+ context(!real ? fakeContext : realContext)
+ {
+ queue.setSuspendUpdatesStrokeStrategyFactory(
+ []() {
+ return KisSuspendResumePair(
+ new KisTestingStrokeStrategy("susp_u_", false, true, true),
+ QList<KisStrokeJobData*>());
+ });
+
+ queue.setResumeUpdatesStrokeStrategyFactory(
+ []() {
+ return KisSuspendResumePair(
+ new KisTestingStrokeStrategy("resu_u_", false, true, true),
+ QList<KisStrokeJobData*>());
+ });
+ queue.setLod0ToNStrokeStrategyFactory(
+ [](bool forgettable) {
+ Q_UNUSED(forgettable);
+ return KisSuspendResumePair(
+ new KisTestingStrokeStrategy("sync_u_", false, true, true),
+ QList<KisStrokeJobData*>());
+ });
+ }
-void KisStrokesQueueTest::testStrokesLevelOfDetail()
-{
KisStrokesQueue queue;
- queue.setSuspendUpdatesStrokeStrategyFactory(
- []() {
- return KisSuspendResumePair(
- new KisTestingStrokeStrategy("susp_u_", false, true, true),
- QList<KisStrokeJobData*>());
- });
+ KisTestableUpdaterContext fakeContext;
+ KisUpdaterContext realContext;
+ KisUpdaterContext &context;
+ QVector<KisUpdateJobItem*> jobs;
+
+ void processQueueNoAdd() {
+ if (&context != &fakeContext) return;
+
+ fakeContext.clear();
+
+ jobs = fakeContext.getJobs();
+ VERIFY_EMPTY(jobs[0]);
+ VERIFY_EMPTY(jobs[1]);
+ }
+
+ void processQueue() {
+ processQueueNoAdd();
+ queue.processQueue(context, false);
+
+ if (&context == &realContext) {
+ context.waitForDone();
+ }
+ }
- queue.setResumeUpdatesStrokeStrategyFactory(
- []() {
- return KisSuspendResumePair(
- new KisTestingStrokeStrategy("resu_u_", false, true, true),
- QList<KisStrokeJobData*>());
- });
+ void checkOnlyJob(const QString &name) {
+ KIS_ASSERT(&context == &fakeContext);
+
+ jobs = fakeContext.getJobs();
+ COMPARE_NAME(jobs[0], name);
+ VERIFY_EMPTY(jobs[1]);
+ QCOMPARE(queue.needsExclusiveAccess(), false);
+ }
+
+ void checkOnlyExecutedJob(const QString &name) {
+ realContext.waitForDone();
+ QVERIFY(!globalExecutedDabs.isEmpty());
+ QCOMPARE(globalExecutedDabs[0], name);
+
+ QCOMPARE(globalExecutedDabs.size(), 1);
+ globalExecutedDabs.clear();
+ }
+};
+
+
+void KisStrokesQueueTest::testStrokesLevelOfDetail()
+{
+ LodStrokesQueueTester t;
+ KisStrokesQueue &queue = t.queue;
// create a stroke with LOD0 + LOD2
queue.setDesiredLevelOfDetail(2);
KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy("lod_", false, true));
queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
queue.endStroke(id2);
// create a update with LOD == 0 (default one)
// well, this walker is not initialized... but who cares?
KisBaseRectsWalkerSP walker = new KisMergeWalker(QRect());
KisTestableUpdaterContext context(2);
QVector<KisUpdateJobItem*> jobs;
context.addMergeJob(walker);
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_WALKER(jobs[0], walker);
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), false);
context.clear();
jobs = context.getJobs();
VERIFY_EMPTY(jobs[0]);
VERIFY_EMPTY(jobs[1]);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "clone2_lod_dab");
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), false);
// walker of a different LOD must not be allowed
QCOMPARE(context.isJobAllowed(walker), false);
context.clear();
context.addMergeJob(walker);
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_WALKER(jobs[0], walker);
COMPARE_NAME(jobs[1], "susp_u_init");
QCOMPARE(queue.needsExclusiveAccess(), false);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "lod_dab");
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), false);
context.clear();
queue.processQueue(context, false);
jobs = context.getJobs();
COMPARE_NAME(jobs[0], "resu_u_init");
VERIFY_EMPTY(jobs[1]);
QCOMPARE(queue.needsExclusiveAccess(), false);
context.clear();
}
+#include <kundo2command.h>
+#include <kis_post_execution_undo_adapter.h>
+struct TestUndoCommand : public KUndo2Command
+{
+ TestUndoCommand(const QString &text) : KUndo2Command(kundo2_noi18n(text)) {}
+
+ void undo() {
+ ENTER_FUNCTION();
+ undoCount++;
+ }
+
+ void redo() {
+ ENTER_FUNCTION();
+ redoCount++;
+ }
+
+ int undoCount = 0;
+ int redoCount = 0;
+};
+
+void KisStrokesQueueTest::testLodUndoBase()
+{
+ LodStrokesQueueTester t;
+ KisStrokesQueue &queue = t.queue;
+
+ // create a stroke with LOD0 + LOD2
+ queue.setDesiredLevelOfDetail(2);
+ KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy("str1_", false, true));
+ queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
+ queue.endStroke(id1);
+
+ KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy("str2_", false, true));
+ queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
+ queue.endStroke(id2);
+
+ t.processQueue();
+ t.checkOnlyJob("clone2_str1_dab");
+
+
+ QSharedPointer<TestUndoCommand> undoStr1(new TestUndoCommand("str1_undo"));
+ queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr1);
+
+ t.processQueue();
+ t.checkOnlyJob("clone2_str2_dab");
+
+ QSharedPointer<TestUndoCommand> undoStr2(new TestUndoCommand("str2_undo"));
+ queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr2);
+
+ t.processQueue();
+ t.checkOnlyJob("susp_u_init");
+
+ t.processQueue();
+ t.checkOnlyJob("str1_dab");
+
+ t.processQueue();
+ t.checkOnlyJob("str2_dab");
+
+ t.processQueue();
+ t.checkOnlyJob("resu_u_init");
+}
+
+void KisStrokesQueueTest::testLodUndoBase2()
+{
+ LodStrokesQueueTester t(true);
+ KisStrokesQueue &queue = t.queue;
+
+ // create a stroke with LOD0 + LOD2
+ queue.setDesiredLevelOfDetail(2);
+ KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy("str1_", false, true, false, true));
+ queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
+ queue.endStroke(id1);
+
+ KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy("str2_", false, true, false, true));
+ queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
+ queue.endStroke(id2);
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("sync_u_init");
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("clone2_str1_dab");
+
+ QSharedPointer<TestUndoCommand> undoStr1(new TestUndoCommand("str1_undo"));
+ queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr1);
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("clone2_str2_dab");
+
+ QSharedPointer<TestUndoCommand> undoStr2(new TestUndoCommand("str2_undo"));
+ queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr2);
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("susp_u_init");
+
+ queue.tryUndoLastStrokeAsync();
+ t.processQueue();
+
+ while (queue.currentStrokeName() == kundo2_noi18n("str2_undo")) {
+ //queue.debugPrintStrokes();
+ t.processQueue();
+ }
+
+ QCOMPARE(undoStr2->undoCount, 1);
+
+ t.checkOnlyExecutedJob("str1_dab");
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("str2_cancel");
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("resu_u_init");
+}
+
+
QTEST_MAIN(KisStrokesQueueTest)
diff --git a/libs/image/tests/kis_strokes_queue_test.h b/libs/image/tests/kis_strokes_queue_test.h
index f0765d45e1..3fdefdf84c 100644
--- a/libs/image/tests/kis_strokes_queue_test.h
+++ b/libs/image/tests/kis_strokes_queue_test.h
@@ -1,41 +1,46 @@
/*
* 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_STROKES_QUEUE_TEST_H
#define __KIS_STROKES_QUEUE_TEST_H
#include <QtTest>
class KisStrokesQueueTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testSequentialJobs();
void testConcurrentSequentialBarrier();
void testExclusiveStrokes();
void testBarrierStrokeJobs();
void testStrokesOverlapping();
void testImmediateCancel();
void testOpenedStrokeCounter();
void testAsyncCancelWhileOpenedStroke();
void testStrokesLevelOfDetail();
+ void testLodUndoBase();
+ void testLodUndoBase2();
+
+private:
+ struct LodStrokesQueueTester;
};
#endif /* __KIS_STROKES_QUEUE_TEST_H */
diff --git a/libs/image/tests/scheduler_utils.h b/libs/image/tests/scheduler_utils.h
index 45d234f44a..c374269599 100644
--- a/libs/image/tests/scheduler_utils.h
+++ b/libs/image/tests/scheduler_utils.h
@@ -1,223 +1,231 @@
/*
* 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"
#include "kis_image.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, int lod = 0) {
if(walker->requestedRect() == rect && walker->levelOfDetail() == lod) {
return true;
}
else {
dbgKrita << "walker rect:" << walker->requestedRect();
dbgKrita << "expected rect:" << rect;
dbgKrita << "walker lod:" << walker->levelOfDetail();
dbgKrita << "expected lod:" << lod;
return false;
}
}
class KisNoopSpontaneousJob : public KisSpontaneousJob
{
public:
KisNoopSpontaneousJob(bool overridesEverything = false, int lod = 0)
: m_overridesEverything(overridesEverything),
m_lod(lod)
{
}
void run() {
}
bool overrides(const KisSpontaneousJob *otherJob) {
Q_UNUSED(otherJob);
return m_overridesEverything;
}
int levelOfDetail() const {
return m_lod;
}
private:
bool m_overridesEverything;
int m_lod;
};
+static QStringList globalExecutedDabs;
+
class KisNoopDabStrategy : public KisStrokeJobStrategy
{
public:
KisNoopDabStrategy(QString name)
: m_name(name),
m_isMarked(false)
{}
void run(KisStrokeJobData *data) {
Q_UNUSED(data);
+
+ globalExecutedDabs << m_name;
}
QString name() {
return m_name;
}
void setMarked() {
m_isMarked = true;
}
bool isMarked() const {
return m_isMarked;
}
private:
QString m_name;
bool m_isMarked;
};
class KisTestingStrokeJobData : public KisStrokeJobData
{
public:
KisTestingStrokeJobData(Sequentiality sequentiality = SEQUENTIAL,
Exclusivity exclusivity = NORMAL)
: KisStrokeJobData(sequentiality, exclusivity)
{
}
KisTestingStrokeJobData(const KisTestingStrokeJobData &rhs)
: KisStrokeJobData(rhs)
{
}
KisStrokeJobData* createLodClone(int levelOfDetail) {
Q_UNUSED(levelOfDetail);
return new KisTestingStrokeJobData(*this);
}
};
class KisTestingStrokeStrategy : public KisStrokeStrategy
{
public:
KisTestingStrokeStrategy(const QString &prefix = QString(),
bool exclusive = false,
bool inhibitServiceJobs = false,
- bool forceAllowInitJob = false)
- : m_prefix(prefix),
+ bool forceAllowInitJob = false,
+ bool forceAllowCancelJob = false)
+ : KisStrokeStrategy(prefix, kundo2_noi18n(prefix)),
+ m_prefix(prefix),
m_inhibitServiceJobs(inhibitServiceJobs),
m_forceAllowInitJob(forceAllowInitJob),
+ m_forceAllowCancelJob(forceAllowCancelJob),
m_cancelSeqNo(0)
{
setExclusive(exclusive);
}
KisTestingStrokeStrategy(const KisTestingStrokeStrategy &rhs, int levelOfDetail)
: KisStrokeStrategy(rhs),
m_prefix(rhs.m_prefix),
m_inhibitServiceJobs(rhs.m_inhibitServiceJobs),
m_forceAllowInitJob(rhs.m_forceAllowInitJob),
m_cancelSeqNo(rhs.m_cancelSeqNo)
{
m_prefix = QString("clone%1_%2").arg(levelOfDetail).arg(m_prefix);
}
KisStrokeJobStrategy* createInitStrategy() {
return m_forceAllowInitJob || !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "init") : 0;
}
KisStrokeJobStrategy* createFinishStrategy() {
return !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "finish") : 0;
}
KisStrokeJobStrategy* createCancelStrategy() {
- return !m_inhibitServiceJobs ?
+ return m_forceAllowCancelJob || !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "cancel") : 0;
}
KisStrokeJobStrategy* createDabStrategy() {
return new KisNoopDabStrategy(m_prefix + "dab");
}
KisStrokeStrategy* createLodClone(int levelOfDetail) {
return new KisTestingStrokeStrategy(*this, levelOfDetail);
}
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_forceAllowInitJob;
+ bool m_forceAllowCancelJob;
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/libs/image/tiles3/kis_memento_item.h b/libs/image/tiles3/kis_memento_item.h
index 1dcb6dc109..0d1c8c555d 100644
--- a/libs/image/tiles3/kis_memento_item.h
+++ b/libs/image/tiles3/kis_memento_item.h
@@ -1,226 +1,226 @@
/*
* 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.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);
+ return KisTileSP(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);
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/libs/image/tiles3/kis_memento_manager.cc b/libs/image/tiles3/kis_memento_manager.cc
index a4900ea2f1..0af0bb22e7 100644
--- a/libs/image/tiles3/kis_memento_manager.cc
+++ b/libs/image/tiles3/kis_memento_manager.cc
@@ -1,422 +1,422 @@
/*
* 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 <QtGlobal>
#include "kis_memento_manager.h"
#include "kis_memento.h"
//#define DEBUG_MM
#ifdef DEBUG_MM
#define DEBUG_LOG_TILE_ACTION(action, tile, col, row) \
printf("### MementoManager (0x%X): %s " \
"\ttile:\t0x%X (%d, %d) ###\n", (quintptr)this, action, \
(quintptr)tile, col, row)
#define DEBUG_LOG_SIMPLE_ACTION(action) \
printf("### MementoManager (0x%X): %s\n", (quintptr)this, action)
#define DEBUG_DUMP_MESSAGE(action) do { \
printf("\n### MementoManager (0x%X): %s \t\t##########\n", \
(quintptr)this, action); \
debugPrintInfo(); \
printf("##################################################################\n\n"); \
} while(0)
#else
#define DEBUG_LOG_TILE_ACTION(action, tile, col, row)
#define DEBUG_LOG_SIMPLE_ACTION(action)
#define DEBUG_DUMP_MESSAGE(action)
#endif
/**
* The class is supposed to store the changes of the paint device
* it is associated with. The history of changes is presented in form
* of transactions (revisions). If you purge the history of one
* transaction (revision) with purgeHistory() we won't be able to undo
* the changes made by this transactions.
*
* The Memento Manager can be in two states:
* - Named Transaction is in progress - it means the caller
* has explicitly requested creation of a new transaction.
* The handle for the transaction is stored on a side of
* the caller. And the history will be automatically purged
* when the handler dies.
* - Anonymous Transaction is in progress - the caller isn't
* bothered about transactions at all. We pretend as we do
* not support any versioning and do not have any historical
* information. The history of such transactions is not purged
* automatically, but it is free'd when younger named transaction
* is purged.
*/
#define blockRegistration() (m_registrationBlocked = true)
#define unblockRegistration() (m_registrationBlocked = false)
#define registrationBlocked() (m_registrationBlocked)
#define namedTransactionInProgress() ((bool)m_currentMemento)
KisMementoManager::KisMementoManager()
: m_index(0),
m_headsHashTable(0),
m_registrationBlocked(false)
{
/**
* Tile change/delete registration is enabled for all
* devices by default. It can't be delayed.
*/
}
KisMementoManager::KisMementoManager(const KisMementoManager& rhs)
: m_index(rhs.m_index, 0),
m_revisions(rhs.m_revisions),
m_cancelledRevisions(rhs.m_cancelledRevisions),
m_headsHashTable(rhs.m_headsHashTable, 0),
m_currentMemento(rhs.m_currentMemento),
m_registrationBlocked(rhs.m_registrationBlocked)
{
Q_ASSERT_X(!m_registrationBlocked,
"KisMementoManager", "(impossible happened) "
"The device has been copied while registration was blocked");
}
KisMementoManager::~KisMementoManager()
{
// Nothing to be done here. Happily...
// Everything is done by QList and KisSharedPtr...
DEBUG_LOG_SIMPLE_ACTION("died\n");
}
/**
* NOTE: We don't assume that the registerTileChange/Delete
* can be called once a commit only. Reverse can happen when we
* do sequential clears of the device. In such a case the tiles
* will be removed and added several times during a commit.
*
* TODO: There is an 'uncomfortable' state for the tile possible
* 1) Imagine we have a clear device
* 2) Then we painted something in a tile
* 3) It registered itself using registerTileChange()
* 4) Then we called clear() and getMemento() [==commit()]
* 5) The tile will be registered as deleted and successfully
* committed to a revision. That means the states of the memento
* manager at stages 1 and 5 do not coinside.
* This will not lead to any memory leaks or bugs seen, it just
* not good from a theoretical perspective.
*/
void KisMementoManager::registerTileChange(KisTile *tile)
{
if (registrationBlocked()) return;
DEBUG_LOG_TILE_ACTION("reg. [C]", tile, tile->col(), tile->row());
KisMementoItemSP mi = m_index.getExistedTile(tile->col(), tile->row());
if(!mi) {
mi = new KisMementoItem();
mi->changeTile(tile);
m_index.addTile(mi);
if(namedTransactionInProgress())
m_currentMemento->updateExtent(mi->col(), mi->row());
}
else {
mi->reset();
mi->changeTile(tile);
}
}
void KisMementoManager::registerTileDeleted(KisTile *tile)
{
if (registrationBlocked()) return;
DEBUG_LOG_TILE_ACTION("reg. [D]", tile, tile->col(), tile->row());
KisMementoItemSP mi = m_index.getExistedTile(tile->col(), tile->row());
if(!mi) {
mi = new KisMementoItem();
mi->deleteTile(tile, m_headsHashTable.defaultTileData());
m_index.addTile(mi);
if(namedTransactionInProgress())
m_currentMemento->updateExtent(mi->col(), mi->row());
}
else {
mi->reset();
mi->deleteTile(tile, m_headsHashTable.defaultTileData());
}
}
void KisMementoManager::commit()
{
if (m_index.isEmpty()) {
if(namedTransactionInProgress()) {
//warnTiles << "Named Transaction is empty";
/**
* We still need to continue commit, because
* a named transaction may be reverted by the user
*/
}
else {
m_currentMemento = 0;
return;
}
}
KisMementoItemList revisionList;
KisMementoItemSP mi;
KisMementoItemSP parentMI;
bool newTile;
KisMementoItemHashTableIterator iter(&m_index);
while ((mi = iter.tile())) {
parentMI = m_headsHashTable.getTileLazy(mi->col(), mi->row(), newTile);
mi->setParent(parentMI);
mi->commit();
revisionList.append(mi);
m_headsHashTable.deleteTile(mi->col(), mi->row());
iter.moveCurrentToHashTable(&m_headsHashTable);
//++iter; // previous line does this for us
}
KisHistoryItem hItem;
hItem.itemList = revisionList;
hItem.memento = m_currentMemento.data();
m_revisions.append(hItem);
m_currentMemento = 0;
Q_ASSERT(m_index.isEmpty());
DEBUG_DUMP_MESSAGE("COMMIT_DONE");
// Waking up pooler to prepare copies for us
KisTileDataStore::instance()->kickPooler();
}
KisTileSP KisMementoManager::getCommitedTile(qint32 col, qint32 row)
{
/**
* Our getOldTile mechanism is supposed to return current
* tile, if the history is disabled. So we return zero if
* no named transaction is in progress.
*/
if(!namedTransactionInProgress())
- return 0;
+ return KisTileSP();
KisMementoItemSP mi = m_headsHashTable.getReadOnlyTileLazy(col, row);
Q_ASSERT(mi);
return mi->tile(0);
}
KisMementoSP KisMementoManager::getMemento()
{
/**
* We do not allow nested transactions
*/
Q_ASSERT(!namedTransactionInProgress());
// Clear redo() information
m_cancelledRevisions.clear();
commit();
m_currentMemento = new KisMemento(this);
DEBUG_LOG_SIMPLE_ACTION("GET_MEMENTO_DONE");
return m_currentMemento;
}
KisMementoSP KisMementoManager::currentMemento() {
return m_currentMemento;
}
#define forEachReversed(iter, list) \
for(iter=list.end(); iter-- != list.begin();)
void KisMementoManager::rollback(KisTileHashTable *ht)
{
commit();
if (! m_revisions.size()) return;
KisHistoryItem changeList = m_revisions.takeLast();
KisMementoItemSP mi;
KisMementoItemSP parentMI;
KisMementoItemList::iterator iter;
blockRegistration();
forEachReversed(iter, changeList.itemList) {
mi=*iter;
parentMI = mi->parent();
if (mi->type() == KisMementoItem::CHANGED)
ht->deleteTile(mi->col(), mi->row());
if (parentMI->type() == KisMementoItem::CHANGED)
ht->addTile(parentMI->tile(this));
m_headsHashTable.deleteTile(parentMI->col(), parentMI->row());
m_headsHashTable.addTile(parentMI);
// This is not necessary
//mi->setParent(0);
}
/**
* NOTE: tricky hack alert.
* We have just deleted some tiles from the original hash table.
* And they accurately reported to us about their death. Should
* have reported... But we have prevented their registration with
* explicitly blocking the process. So all the dead tiles are
* going to /dev/null :)
*
* PS: It could cause some race condition... But we insist on
* serialization of rollback()/rollforward() requests. There is
* not much sense in calling rollback() concurrently.
*/
unblockRegistration();
// We have just emulated a commit so:
m_currentMemento = 0;
Q_ASSERT(!namedTransactionInProgress());
m_cancelledRevisions.prepend(changeList);
DEBUG_DUMP_MESSAGE("UNDONE");
}
void KisMementoManager::rollforward(KisTileHashTable *ht)
{
Q_ASSERT(m_index.isEmpty());
if (!m_cancelledRevisions.size()) return;
KisHistoryItem changeList = m_cancelledRevisions.takeFirst();
KisMementoItemSP mi;
blockRegistration();
Q_FOREACH (mi, changeList.itemList) {
if (mi->parent()->type() == KisMementoItem::CHANGED)
ht->deleteTile(mi->col(), mi->row());
if (mi->type() == KisMementoItem::CHANGED)
ht->addTile(mi->tile(this));
m_index.addTile(mi);
}
// see comment in rollback()
m_currentMemento = changeList.memento;
commit();
unblockRegistration();
DEBUG_DUMP_MESSAGE("REDONE");
}
void KisMementoManager::purgeHistory(KisMementoSP oldestMemento)
{
if (m_currentMemento == oldestMemento) {
commit();
}
qint32 revisionIndex = findRevisionByMemento(oldestMemento);
if (revisionIndex < 0) return;
for(; revisionIndex > 0; revisionIndex--) {
resetRevisionHistory(m_revisions.first().itemList);
m_revisions.removeFirst();
}
Q_ASSERT(m_revisions.first().memento == oldestMemento);
resetRevisionHistory(m_revisions.first().itemList);
DEBUG_DUMP_MESSAGE("PURGE_HISTORY");
}
qint32 KisMementoManager::findRevisionByMemento(KisMementoSP memento) const
{
qint32 index = -1;
for(qint32 i = 0; i < m_revisions.size(); i++) {
if (m_revisions[i].memento == memento) {
index = i;
break;
}
}
return index;
}
void KisMementoManager::resetRevisionHistory(KisMementoItemList list)
{
KisMementoItemSP parentMI;
KisMementoItemSP mi;
Q_FOREACH (mi, list) {
parentMI = mi->parent();
if(!parentMI) continue;
while (parentMI->parent()) {
parentMI = parentMI->parent();
}
mi->setParent(parentMI);
}
}
void KisMementoManager::setDefaultTileData(KisTileData *defaultTileData)
{
m_headsHashTable.setDefaultTileData(defaultTileData);
m_index.setDefaultTileData(defaultTileData);
}
void KisMementoManager::debugPrintInfo()
{
printf("KisMementoManager stats:\n");
printf("Index list\n");
KisMementoItemSP mi;
KisMementoItemHashTableIterator iter(&m_index);
while ((mi = iter.tile())) {
mi->debugPrintInfo();
++iter;
}
printf("Revisions list:\n");
qint32 i = 0;
Q_FOREACH (const KisHistoryItem &changeList, m_revisions) {
printf("--- revision #%d ---\n", i++);
Q_FOREACH (mi, changeList.itemList) {
mi->debugPrintInfo();
}
}
printf("\nCancelled revisions list:\n");
i = 0;
Q_FOREACH (const KisHistoryItem &changeList, m_cancelledRevisions) {
printf("--- revision #%d ---\n", m_revisions.size() + i++);
Q_FOREACH (mi, changeList.itemList) {
mi->debugPrintInfo();
}
}
printf("----------------\n");
m_headsHashTable.debugPrintInfo();
}
diff --git a/libs/image/tiles3/kis_tile_hash_table_p.h b/libs/image/tiles3/kis_tile_hash_table_p.h
index d37e5f33f1..adc10ab726 100644
--- a/libs/image/tiles3/kis_tile_hash_table_p.h
+++ b/libs/image/tiles3/kis_tile_hash_table_p.h
@@ -1,396 +1,396 @@
/*
* 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;
+ TileTypeSP nativeTile;
+ TileTypeSP 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 = TileTypeSP(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;
+ return TileTypeSP();
}
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;
+ TileTypeSP prevTile;
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->setNext(TileTypeSP());
tile->notifyDead();
- tile = 0;
+ tile = TileTypeSP();
m_numTiles--;
return tile;
}
prevTile = tile;
}
- return 0;
+ return TileTypeSP();
}
template<class T>
inline void KisTileHashTableTraits<T>::setDefaultTileDataImp(KisTileData *defaultTileData)
{
if (m_defaultTileData) {
m_defaultTileData->release();
m_defaultTileData = 0;
}
if (defaultTileData) {
defaultTileData->acquire();
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;
+ TileTypeSP tile = TileTypeSP();
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->setNext(TileTypeSP());
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) {
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/libs/image/tiles3/kis_tiled_data_manager.cc b/libs/image/tiles3/kis_tiled_data_manager.cc
index 5cf36da9f2..2191de50af 100644
--- a/libs/image/tiles3/kis_tiled_data_manager.cc
+++ b/libs/image/tiles3/kis_tiled_data_manager.cc
@@ -1,812 +1,812 @@
/*
* Copyright (c) 2004 C. Boemann <cbo@boemann.dk>
* (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
* (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 <QRect>
#include <QVector>
#include "kis_tile.h"
#include "kis_tiled_data_manager.h"
#include "kis_tile_data_wrapper.h"
#include "kis_tiled_data_manager_p.h"
#include "kis_memento_manager.h"
#include "swap/kis_legacy_tile_compressor.h"
#include "swap/kis_tile_compressor_factory.h"
#include "kis_paint_device_writer.h"
#include "kis_global.h"
/* The data area is divided into tiles each say 64x64 pixels (defined at compiletime)
* The tiles are laid out in a matrix that can have negative indexes.
* The matrix grows automatically if needed (a call for writeacces to a tile
* outside the current extent)
* Even though the matrix has grown it may still not contain tiles at specific positions.
* They are created on demand
*/
KisTiledDataManager::KisTiledDataManager(quint32 pixelSize,
const quint8 *defaultPixel)
{
/* See comment in destructor for details */
m_mementoManager = new KisMementoManager();
m_hashTable = new KisTileHashTable(m_mementoManager);
m_pixelSize = pixelSize;
m_defaultPixel = new quint8[m_pixelSize];
setDefaultPixel(defaultPixel);
m_extentMinX = qint32_MAX;
m_extentMinY = qint32_MAX;
m_extentMaxX = qint32_MIN;
m_extentMaxY = qint32_MIN;
}
KisTiledDataManager::KisTiledDataManager(const KisTiledDataManager &dm)
: KisShared()
{
/* See comment in destructor for details */
/* We do not clone the history of the device, there is no usecase for it */
m_mementoManager = new KisMementoManager();
m_mementoManager->setDefaultTileData(dm.m_hashTable->defaultTileData());
m_hashTable = new KisTileHashTable(*dm.m_hashTable, m_mementoManager);
m_pixelSize = dm.m_pixelSize;
m_defaultPixel = new quint8[m_pixelSize];
/**
* We won't call setDefaultTileData here, as defaultTileDatas
* has already been made shared in m_hashTable(dm->m_hashTable)
*/
memcpy(m_defaultPixel, dm.m_defaultPixel, m_pixelSize);
m_extentMinX = dm.m_extentMinX;
m_extentMinY = dm.m_extentMinY;
m_extentMaxX = dm.m_extentMaxX;
m_extentMaxY = dm.m_extentMaxY;
}
KisTiledDataManager::~KisTiledDataManager()
{
/**
* Here is an explanation why we use hash table and The Memento Manager
* dynamically allocated We need to destroy them in that very order. The
* reason is that when hash table destroying all her child tiles they all
* cry about it to The Memento Manager using a pointer. So The Memento
* Manager sould be alive during that destruction. We could use shared
* pointers instead, but they create too much overhead.
*/
delete m_hashTable;
delete m_mementoManager;
delete[] m_defaultPixel;
}
void KisTiledDataManager::setDefaultPixel(const quint8 *defaultPixel)
{
QWriteLocker locker(&m_lock);
setDefaultPixelImpl(defaultPixel);
}
void KisTiledDataManager::setDefaultPixelImpl(const quint8 *defaultPixel)
{
KisTileData *td = KisTileDataStore::instance()->createDefaultTileData(pixelSize(), defaultPixel);
m_hashTable->setDefaultTileData(td);
m_mementoManager->setDefaultTileData(td);
memcpy(m_defaultPixel, defaultPixel, pixelSize());
}
bool KisTiledDataManager::write(KisPaintDeviceWriter &store)
{
QReadLocker locker(&m_lock);
bool retval = true;
if(CURRENT_VERSION == LEGACY_VERSION) {
char str[80];
sprintf(str, "%d\n", m_hashTable->numTiles());
retval = store.write(str, strlen(str));
}
else {
retval = writeTilesHeader(store, m_hashTable->numTiles());
}
KisTileHashTableIterator iter(m_hashTable);
KisTileSP tile;
KisAbstractTileCompressorSP compressor =
KisTileCompressorFactory::create(CURRENT_VERSION);
while ((tile = iter.tile())) {
retval = compressor->writeTile(tile, store);
if (!retval) {
warnFile << "Failed to write tile";
break;
}
++iter;
}
return retval;
}
bool KisTiledDataManager::read(QIODevice *stream)
{
if (!stream) return false;
clear();
QWriteLocker locker(&m_lock);
KisMementoSP nothing = m_mementoManager->getMemento();
if (!stream) {
m_mementoManager->commit();
return false;
}
const qint32 maxLineLength = 79; // Legacy magic
QByteArray line = stream->readLine(maxLineLength);
line = line.trimmed();
quint32 numTiles;
qint32 tilesVersion = LEGACY_VERSION;
if (line[0] == 'V') {
QList<QByteArray> lineItems = line.split(' ');
QString keyword = lineItems.takeFirst();
Q_ASSERT(keyword == "VERSION");
tilesVersion = lineItems.takeFirst().toInt();
if(!processTilesHeader(stream, numTiles))
return false;
}
else {
numTiles = line.toUInt();
}
KisAbstractTileCompressorSP compressor =
KisTileCompressorFactory::create(tilesVersion);
bool readSuccess = true;
for (quint32 i = 0; i < numTiles; i++) {
if (!compressor->readTile(stream, this)) {
readSuccess = false;
}
}
m_mementoManager->commit();
return readSuccess;
}
bool KisTiledDataManager::writeTilesHeader(KisPaintDeviceWriter &store, quint32 numTiles)
{
QString buffer;
buffer = QString("VERSION %1\n"
"TILEWIDTH %2\n"
"TILEHEIGHT %3\n"
"PIXELSIZE %4\n"
"DATA %5\n")
.arg(CURRENT_VERSION)
.arg(KisTileData::WIDTH)
.arg(KisTileData::HEIGHT)
.arg(pixelSize())
.arg(numTiles);
return store.write(buffer.toLatin1());
}
#define takeOneLine(stream, maxLine, keyword, value) \
do { \
QByteArray line = stream->readLine(maxLine); \
line = line.trimmed(); \
QList<QByteArray> lineItems = line.split(' '); \
keyword = lineItems.takeFirst(); \
value = lineItems.takeFirst().toInt(); \
} while(0) \
bool KisTiledDataManager::processTilesHeader(QIODevice *stream, quint32 &numTiles)
{
/**
* We assume that there is only one version of this header
* possible. In case we invent something new, it'll be quite easy
* to modify the behavior
*/
const qint32 maxLineLength = 25;
const qint32 totalNumTests = 4;
bool foundDataMark = false;
qint32 testsPassed = 0;
QString keyword;
qint32 value;
while(!foundDataMark && stream->canReadLine()) {
takeOneLine(stream, maxLineLength, keyword, value);
if (keyword == "TILEWIDTH") {
if(value != KisTileData::WIDTH)
goto wrongString;
}
else if (keyword == "TILEHEIGHT") {
if(value != KisTileData::HEIGHT)
goto wrongString;
}
else if (keyword == "PIXELSIZE") {
if((quint32)value != pixelSize())
goto wrongString;
}
else if (keyword == "DATA") {
numTiles = value;
foundDataMark = true;
}
else {
goto wrongString;
}
testsPassed++;
}
if(testsPassed != totalNumTests) {
warnTiles << "Not enough fields of tiles header present"
<< testsPassed << "of" << totalNumTests;
}
return testsPassed == totalNumTests;
wrongString:
warnTiles << "Wrong string in tiles header:" << keyword << value;
return false;
}
void KisTiledDataManager::purge(const QRect& area)
{
QWriteLocker locker(&m_lock);
QList<KisTileSP> tilesToDelete;
{
const qint32 tileDataSize = KisTileData::HEIGHT * KisTileData::WIDTH * pixelSize();
KisTileData *tileData = m_hashTable->defaultTileData();
tileData->blockSwapping();
const quint8 *defaultData = tileData->data();
KisTileHashTableIterator iter(m_hashTable);
KisTileSP tile;
while ((tile = iter.tile())) {
if (tile->extent().intersects(area)) {
tile->lockForRead();
if(memcmp(defaultData, tile->data(), tileDataSize) == 0) {
tilesToDelete.push_back(tile);
}
tile->unlock();
}
++iter;
}
tileData->unblockSwapping();
}
Q_FOREACH (KisTileSP tile, tilesToDelete) {
m_hashTable->deleteTile(tile);
}
recalculateExtent();
}
quint8* KisTiledDataManager::duplicatePixel(qint32 num, const quint8 *pixel)
{
const qint32 pixelSize = this->pixelSize();
/* FIXME: Make a fun filling here */
quint8 *dstBuf = new quint8[num * pixelSize];
quint8 *dstIt = dstBuf;
for (qint32 i = 0; i < num; i++) {
memcpy(dstIt, pixel, pixelSize);
dstIt += pixelSize;
}
return dstBuf;
}
void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel)
{
QWriteLocker locker(&m_lock);
if (clearPixel == 0)
clearPixel = m_defaultPixel;
if (clearRect.isEmpty())
return;
const qint32 pixelSize = this->pixelSize();
bool pixelBytesAreDefault = !memcmp(clearPixel, m_defaultPixel, pixelSize);
bool pixelBytesAreTheSame = true;
for (qint32 i = 0; i < pixelSize; ++i) {
if (clearPixel[i] != clearPixel[0]) {
pixelBytesAreTheSame = false;
break;
}
}
if (pixelBytesAreDefault) {
clearRect &= extentImpl();
}
qint32 firstColumn = xToCol(clearRect.left());
qint32 lastColumn = xToCol(clearRect.right());
qint32 firstRow = yToRow(clearRect.top());
qint32 lastRow = yToRow(clearRect.bottom());
const quint32 rowStride = KisTileData::WIDTH * pixelSize;
// Generate one row
quint8 *clearPixelData = 0;
quint32 maxRunLength = qMin(clearRect.width(), KisTileData::WIDTH);
clearPixelData = duplicatePixel(maxRunLength, clearPixel);
KisTileData *td = 0;
if (!pixelBytesAreDefault &&
clearRect.width() >= KisTileData::WIDTH &&
clearRect.height() >= KisTileData::HEIGHT) {
td = KisTileDataStore::instance()->createDefaultTileData(pixelSize, clearPixel);
td->acquire();
}
bool needsRecalculateExtent = false;
for (qint32 row = firstRow; row <= lastRow; ++row) {
for (qint32 column = firstColumn; column <= lastColumn; ++column) {
QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT,
KisTileData::WIDTH, KisTileData::HEIGHT);
QRect clearTileRect = clearRect & tileRect;
if (clearTileRect == tileRect) {
// Clear whole tile
m_hashTable->deleteTile(column, row);
needsRecalculateExtent = true;
if (!pixelBytesAreDefault) {
- KisTileSP clearedTile = new KisTile(column, row, td, m_mementoManager);
+ KisTileSP clearedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager));
m_hashTable->addTile(clearedTile);
updateExtent(column, row);
}
} else {
const qint32 lineSize = clearTileRect.width() * pixelSize;
qint32 rowsRemaining = clearTileRect.height();
KisTileDataWrapper tw(this,
clearTileRect.left(),
clearTileRect.top(),
KisTileDataWrapper::WRITE);
quint8* tileIt = tw.data();
if (pixelBytesAreTheSame) {
while (rowsRemaining > 0) {
memset(tileIt, *clearPixelData, lineSize);
tileIt += rowStride;
rowsRemaining--;
}
} else {
while (rowsRemaining > 0) {
memcpy(tileIt, clearPixelData, lineSize);
tileIt += rowStride;
rowsRemaining--;
}
}
}
}
}
if (needsRecalculateExtent) {
recalculateExtent();
}
if (td) td->release();
delete[] clearPixelData;
}
void KisTiledDataManager::clear(QRect clearRect, quint8 clearValue)
{
quint8 *buf = new quint8[pixelSize()];
memset(buf, clearValue, pixelSize());
clear(clearRect, buf);
delete[] buf;
}
void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *clearPixel)
{
clear(QRect(x, y, w, h), clearPixel);
}
void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, quint8 clearValue)
{
clear(QRect(x, y, w, h), clearValue);
}
void KisTiledDataManager::clear()
{
QWriteLocker locker(&m_lock);
m_hashTable->clear();
m_extentMinX = qint32_MAX;
m_extentMinY = qint32_MAX;
m_extentMaxX = qint32_MIN;
m_extentMaxY = qint32_MIN;
}
template<bool useOldSrcData>
void KisTiledDataManager::bitBltImpl(KisTiledDataManager *srcDM, const QRect &rect)
{
QWriteLocker locker(&m_lock);
if (rect.isEmpty()) return;
const qint32 pixelSize = this->pixelSize();
const quint32 rowStride = KisTileData::WIDTH * pixelSize;
qint32 firstColumn = xToCol(rect.left());
qint32 lastColumn = xToCol(rect.right());
qint32 firstRow = yToRow(rect.top());
qint32 lastRow = yToRow(rect.bottom());
for (qint32 row = firstRow; row <= lastRow; ++row) {
for (qint32 column = firstColumn; column <= lastColumn; ++column) {
// this is the only variation in the template
KisTileSP srcTile = useOldSrcData ?
srcDM->getOldTile(column, row) :
srcDM->getTile(column, row, false);
QRect tileRect(column*KisTileData::WIDTH, row*KisTileData::HEIGHT,
KisTileData::WIDTH, KisTileData::HEIGHT);
QRect cloneTileRect = rect & tileRect;
if (cloneTileRect == tileRect) {
// Clone whole tile
m_hashTable->deleteTile(column, row);
srcTile->lockForRead();
KisTileData *td = srcTile->tileData();
- KisTileSP clonedTile = new KisTile(column, row, td, m_mementoManager);
+ KisTileSP clonedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager));
srcTile->unlock();
m_hashTable->addTile(clonedTile);
updateExtent(column, row);
} else {
const qint32 lineSize = cloneTileRect.width() * pixelSize;
qint32 rowsRemaining = cloneTileRect.height();
KisTileDataWrapper tw(this,
cloneTileRect.left(),
cloneTileRect.top(),
KisTileDataWrapper::WRITE);
srcTile->lockForRead();
// We suppose that the shift in both tiles is the same
const quint8* srcTileIt = srcTile->data() + tw.offset();
quint8* dstTileIt = tw.data();
while (rowsRemaining > 0) {
memcpy(dstTileIt, srcTileIt, lineSize);
srcTileIt += rowStride;
dstTileIt += rowStride;
rowsRemaining--;
}
srcTile->unlock();
}
}
}
}
template<bool useOldSrcData>
void KisTiledDataManager::bitBltRoughImpl(KisTiledDataManager *srcDM, const QRect &rect)
{
QWriteLocker locker(&m_lock);
if (rect.isEmpty()) return;
qint32 firstColumn = xToCol(rect.left());
qint32 lastColumn = xToCol(rect.right());
qint32 firstRow = yToRow(rect.top());
qint32 lastRow = yToRow(rect.bottom());
for (qint32 row = firstRow; row <= lastRow; ++row) {
for (qint32 column = firstColumn; column <= lastColumn; ++column) {
/**
* We are cloning whole tiles here so let's not be so boring
* to check any borders :)
*/
// this is the only variation in the template
KisTileSP srcTile = useOldSrcData ?
srcDM->getOldTile(column, row) :
srcDM->getTile(column, row, false);
m_hashTable->deleteTile(column, row);
srcTile->lockForRead();
KisTileData *td = srcTile->tileData();
- KisTileSP clonedTile = new KisTile(column, row, td, m_mementoManager);
+ KisTileSP clonedTile = KisTileSP(new KisTile(column, row, td, m_mementoManager));
srcTile->unlock();
m_hashTable->addTile(clonedTile);
updateExtent(column, row);
}
}
}
void KisTiledDataManager::bitBlt(KisTiledDataManager *srcDM, const QRect &rect)
{
bitBltImpl<false>(srcDM, rect);
}
void KisTiledDataManager::bitBltOldData(KisTiledDataManager *srcDM, const QRect &rect)
{
bitBltImpl<true>(srcDM, rect);
}
void KisTiledDataManager::bitBltRough(KisTiledDataManager *srcDM, const QRect &rect)
{
bitBltRoughImpl<false>(srcDM, rect);
}
void KisTiledDataManager::bitBltRoughOldData(KisTiledDataManager *srcDM, const QRect &rect)
{
bitBltRoughImpl<true>(srcDM, rect);
}
void KisTiledDataManager::setExtent(qint32 x, qint32 y, qint32 w, qint32 h)
{
setExtent(QRect(x, y, w, h));
}
void KisTiledDataManager::setExtent(QRect newRect)
{
QRect oldRect = extent();
newRect = newRect.normalized();
// Do nothing if the desired size is bigger than we currently are:
// that is handled by the autoextending automatically
if (newRect.contains(oldRect)) return;
QWriteLocker locker(&m_lock);
KisTileSP tile;
QRect tileRect;
{
KisTileHashTableIterator iter(m_hashTable);
while (!iter.isDone()) {
tile = iter.tile();
tileRect = tile->extent();
if (newRect.contains(tileRect)) {
//do nothing
++iter;
} else if (newRect.intersects(tileRect)) {
QRect intersection = newRect & tileRect;
intersection.translate(- tileRect.topLeft());
const qint32 pixelSize = this->pixelSize();
tile->lockForWrite();
quint8* data = tile->data();
quint8* ptr;
/* FIXME: make it faster */
for (int y = 0; y < KisTileData::HEIGHT; y++) {
for (int x = 0; x < KisTileData::WIDTH; x++) {
if (!intersection.contains(x, y)) {
ptr = data + pixelSize * (y * KisTileData::WIDTH + x);
memcpy(ptr, m_defaultPixel, pixelSize);
}
}
}
tile->unlock();
++iter;
} else {
iter.deleteCurrent();
}
}
}
recalculateExtent();
}
void KisTiledDataManager::recalculateExtent()
{
m_extentMinX = qint32_MAX;
m_extentMinY = qint32_MAX;
m_extentMaxX = qint32_MIN;
m_extentMaxY = qint32_MIN;
KisTileHashTableIterator iter(m_hashTable);
KisTileSP tile;
while ((tile = iter.tile())) {
updateExtent(tile->col(), tile->row());
++iter;
}
}
void KisTiledDataManager::updateExtent(qint32 col, qint32 row)
{
const qint32 tileMinX = col * KisTileData::WIDTH;
const qint32 tileMinY = row * KisTileData::HEIGHT;
const qint32 tileMaxX = tileMinX + KisTileData::WIDTH - 1;
const qint32 tileMaxY = tileMinY + KisTileData::HEIGHT - 1;
m_extentMinX = qMin(m_extentMinX, tileMinX);
m_extentMaxX = qMax(m_extentMaxX, tileMaxX);
m_extentMinY = qMin(m_extentMinY, tileMinY);
m_extentMaxY = qMax(m_extentMaxY, tileMaxY);
}
QRect KisTiledDataManager::extentImpl() const
{
qint32 x = m_extentMinX;
qint32 y = m_extentMinY;
qint32 w = (m_extentMaxX >= m_extentMinX) ? m_extentMaxX - m_extentMinX + 1 : 0;
qint32 h = (m_extentMaxY >= m_extentMinY) ? m_extentMaxY - m_extentMinY + 1 : 0;
return QRect(x, y, w, h);
}
void KisTiledDataManager::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const
{
QRect rect = extent();
rect.getRect(&x, &y, &w, &h);
}
QRect KisTiledDataManager::extent() const
{
QReadLocker locker(&m_lock);
return extentImpl();
}
QRegion KisTiledDataManager::region() const
{
QRegion region;
KisTileHashTableIterator iter(m_hashTable);
KisTileSP tile;
while ((tile = iter.tile())) {
region += tile->extent();
++iter;
}
return region;
}
void KisTiledDataManager::setPixel(qint32 x, qint32 y, const quint8 * data)
{
QWriteLocker locker(&m_lock);
KisTileDataWrapper tw(this, x, y, KisTileDataWrapper::WRITE);
memcpy(tw.data(), data, pixelSize());
}
void KisTiledDataManager::writeBytes(const quint8 *data,
qint32 x, qint32 y,
qint32 width, qint32 height,
qint32 dataRowStride)
{
QWriteLocker locker(&m_lock);
// Actial bytes reading/writing is done in private header
writeBytesBody(data, x, y, width, height, dataRowStride);
}
void KisTiledDataManager::readBytes(quint8 *data,
qint32 x, qint32 y,
qint32 width, qint32 height,
qint32 dataRowStride) const
{
QReadLocker locker(&m_lock);
// Actual bytes reading/writing is done in private header
readBytesBody(data, x, y, width, height, dataRowStride);
}
QVector<quint8*>
KisTiledDataManager::readPlanarBytes(QVector<qint32> channelSizes,
qint32 x, qint32 y,
qint32 width, qint32 height) const
{
QReadLocker locker(&m_lock);
// Actial bytes reading/writing is done in private header
return readPlanarBytesBody(channelSizes, x, y, width, height);
}
void KisTiledDataManager::writePlanarBytes(QVector<quint8*> planes,
QVector<qint32> channelSizes,
qint32 x, qint32 y,
qint32 width, qint32 height)
{
QWriteLocker locker(&m_lock);
// Actial bytes reading/writing is done in private header
bool allChannelsPresent = true;
Q_FOREACH (const quint8* plane, planes) {
if (!plane) {
allChannelsPresent = false;
break;
}
}
if (allChannelsPresent) {
writePlanarBytesBody<true>(planes, channelSizes, x, y, width, height);
} else {
writePlanarBytesBody<false>(planes, channelSizes, x, y, width, height);
}
}
qint32 KisTiledDataManager::numContiguousColumns(qint32 x, qint32 minY, qint32 maxY) const
{
qint32 numColumns;
Q_UNUSED(minY);
Q_UNUSED(maxY);
if (x >= 0) {
numColumns = KisTileData::WIDTH - (x % KisTileData::WIDTH);
} else {
numColumns = ((-x - 1) % KisTileData::WIDTH) + 1;
}
return numColumns;
}
qint32 KisTiledDataManager::numContiguousRows(qint32 y, qint32 minX, qint32 maxX) const
{
qint32 numRows;
Q_UNUSED(minX);
Q_UNUSED(maxX);
if (y >= 0) {
numRows = KisTileData::HEIGHT - (y % KisTileData::HEIGHT);
} else {
numRows = ((-y - 1) % KisTileData::HEIGHT) + 1;
}
return numRows;
}
qint32 KisTiledDataManager::rowStride(qint32 x, qint32 y) const
{
Q_UNUSED(x);
Q_UNUSED(y);
return KisTileData::WIDTH * pixelSize();
}
void KisTiledDataManager::releaseInternalPools()
{
KisTileData::releaseInternalPools();
}
diff --git a/libs/image/tiles3/swap/kis_legacy_tile_compressor.cpp b/libs/image/tiles3/swap/kis_legacy_tile_compressor.cpp
index c5614b2262..5370fc3d1f 100644
--- a/libs/image/tiles3/swap/kis_legacy_tile_compressor.cpp
+++ b/libs/image/tiles3/swap/kis_legacy_tile_compressor.cpp
@@ -1,124 +1,126 @@
/*
* 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_legacy_tile_compressor.h"
#include "kis_paint_device_writer.h"
#include <QIODevice>
#define TILE_DATA_SIZE(pixelSize) ((pixelSize) * KisTileData::WIDTH * KisTileData::HEIGHT)
KisLegacyTileCompressor::KisLegacyTileCompressor()
{
}
KisLegacyTileCompressor::~KisLegacyTileCompressor()
{
}
bool KisLegacyTileCompressor::writeTile(KisTileSP tile, KisPaintDeviceWriter &store)
{
const qint32 tileDataSize = TILE_DATA_SIZE(tile->pixelSize());
const qint32 bufferSize = maxHeaderLength() + 1;
- quint8 *headerBuffer = new quint8[bufferSize];
+ QScopedArrayPointer<quint8> headerBuffer(new quint8[bufferSize]);
- bool retval = writeHeader(tile, headerBuffer);
+ bool retval = writeHeader(tile, headerBuffer.data());
+ Q_ASSERT(retval); // currently the code returns true unconditionally
+ if (!retval) {
+ return false;
+ }
- store.write((char *)headerBuffer, strlen((char *)headerBuffer));
+ store.write((char *)headerBuffer.data(), strlen((char *)headerBuffer.data()));
tile->lockForRead();
retval = store.write((char *)tile->data(), tileDataSize);
tile->unlock();
- delete[] headerBuffer;
-
return retval;
}
bool KisLegacyTileCompressor::readTile(QIODevice *stream, KisTiledDataManager *dm)
{
const qint32 tileDataSize = TILE_DATA_SIZE(pixelSize(dm));
const qint32 bufferSize = maxHeaderLength() + 1;
quint8 *headerBuffer = new quint8[bufferSize];
qint32 x, y;
qint32 width, height;
stream->readLine((char *)headerBuffer, bufferSize);
sscanf((char *) headerBuffer, "%d,%d,%d,%d", &x, &y, &width, &height);
qint32 row = yToRow(dm, y);
qint32 col = xToCol(dm, x);
KisTileSP tile = dm->getTile(col, row, true);
tile->lockForWrite();
stream->read((char *)tile->data(), tileDataSize);
tile->unlock();
return true;
}
void KisLegacyTileCompressor::compressTileData(KisTileData *tileData,
quint8 *buffer,
qint32 bufferSize,
qint32 &bytesWritten)
{
bytesWritten = 0;
const qint32 tileDataSize = TILE_DATA_SIZE(tileData->pixelSize());
Q_UNUSED(bufferSize);
Q_ASSERT(bufferSize >= tileDataSize);
memcpy(buffer, tileData->data(), tileDataSize);
bytesWritten += tileDataSize;
}
bool KisLegacyTileCompressor::decompressTileData(quint8 *buffer,
qint32 bufferSize,
KisTileData *tileData)
{
const qint32 tileDataSize = TILE_DATA_SIZE(tileData->pixelSize());
if (bufferSize >= tileDataSize) {
memcpy(tileData->data(), buffer, tileDataSize);
return true;
}
return false;
}
qint32 KisLegacyTileCompressor::tileDataBufferSize(KisTileData *tileData)
{
return TILE_DATA_SIZE(tileData->pixelSize());
}
inline qint32 KisLegacyTileCompressor::maxHeaderLength()
{
static const qint32 LEGACY_MAGIC_NUMBER = 79;
return LEGACY_MAGIC_NUMBER;
}
inline bool KisLegacyTileCompressor::writeHeader(KisTileSP tile,
quint8 *buffer)
{
qint32 x, y;
qint32 width, height;
tile->extent().getRect(&x, &y, &width, &height);
sprintf((char *)buffer, "%d,%d,%d,%d\n", x, y, width, height);
return true;
}
diff --git a/libs/image/tiles3/swap/kis_tile_compressor_factory.h b/libs/image/tiles3/swap/kis_tile_compressor_factory.h
index 80a4f3631f..610b3e512a 100644
--- a/libs/image/tiles3/swap/kis_tile_compressor_factory.h
+++ b/libs/image/tiles3/swap/kis_tile_compressor_factory.h
@@ -1,47 +1,47 @@
/*
* 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_TILE_COMPRESSOR_FACTORY_H
#define __KIS_TILE_COMPRESSOR_FACTORY_H
#include "tiles3/swap/kis_legacy_tile_compressor.h"
#include "tiles3/swap/kis_tile_compressor_2.h"
class KRITAIMAGE_EXPORT KisTileCompressorFactory
{
public:
static KisAbstractTileCompressorSP create(qint32 version) {
switch(version) {
case 1:
- return new KisLegacyTileCompressor();
+ return KisAbstractTileCompressorSP(new KisLegacyTileCompressor());
break;
case 2:
- return new KisTileCompressor2();
+ return KisAbstractTileCompressorSP(new KisTileCompressor2());
break;
default:
qFatal("Unknown version of the tiles");
- return 0;
+ return KisAbstractTileCompressorSP();
};
}
private:
KisTileCompressorFactory();
};
#endif /* __KIS_TILE_COMPRESSOR_FACTORY_H */
diff --git a/libs/impex/AnimationCheck.h b/libs/impex/AnimationCheck.h
new file mode 100644
index 0000000000..290c264156
--- /dev/null
+++ b/libs/impex/AnimationCheck.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef AnimationCHECK_H
+#define AnimationCHECK_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <kis_image_animation_interface.h>
+#include <KoColorSpace.h>
+
+class AnimationCheck : public KisExportCheckBase
+{
+public:
+
+ AnimationCheck(const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning, true)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "This image has <b>animated layers</b>. Animation cannot be saved to this format.");
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ return image->animationInterface()->hasAnimation();
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+};
+
+class AnimationCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ AnimationCheckFactory() {}
+
+ virtual ~AnimationCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new AnimationCheck(id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "AnimationCheck";
+ }
+};
+
+#endif // AnimationCHECK_H
diff --git a/libs/impex/CMakeLists.txt b/libs/impex/CMakeLists.txt
new file mode 100644
index 0000000000..eb3c18676a
--- /dev/null
+++ b/libs/impex/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(kritaimpex_LIB_SRCS
+ KisExportCheckBase.cpp
+ KisPreExportChecker.cpp
+ KisExportCheckRegistry.cpp
+)
+
+add_library(kritaimpex SHARED ${kritaimpex_HEADERS_MOC} ${kritaimpex_LIB_SRCS} )
+
+generate_export_header(kritaimpex BASE_NAME kritaimpex)
+
+target_link_libraries(kritaimpex KF5::CoreAddons KF5::I18n kritaimage)
+
+set_target_properties(kritaimpex
+ PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
+)
+
+install(TARGETS kritaimpex ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/impex/CheckImageSize.h b/libs/impex/CheckImageSize.h
new file mode 100644
index 0000000000..c4c8636d82
--- /dev/null
+++ b/libs/impex/CheckImageSize.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef CHECKIMAGESIZE_H
+#define CHECKIMAGESIZE_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <kis_image_ImageSize_interface.h>
+#include <KoColorSpace.h>
+
+class ImageSizeCheck : public KisExportCheckBase
+{
+public:
+
+ ImageSizeCheck(int maxWidth, int maxHeight, const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning, true)
+ , m_maxW(maxWidth)
+ , m_maxH(maxHeight)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "This image is larger than <b>%1 x %2</b>. Images this size cannot be saved to this format.", m_maxW, m_maxH);
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ return image->width() <= m_maxW && image->height() <= m_maxH;
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+ int m_maxW;
+ int m_maxH;
+};
+
+class ImageSizeCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ ImageSizeCheckFactory() {}
+
+ virtual ~ImageSizeCheckFactory() {}
+
+ KisExportCheckBase *create(int maxWidth, int maxHeight, KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new ImageSizeCheck(maxWidth, maxHeight, id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "ImageSizeCheck";
+ }
+};
+
+
+#endif // CHECKIMAGESIZE_H
diff --git a/libs/impex/ColorModelCheck.h b/libs/impex/ColorModelCheck.h
new file mode 100644
index 0000000000..b54bf64191
--- /dev/null
+++ b/libs/impex/ColorModelCheck.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef COLORMODELCHECK_H
+#define COLORMODELCHECK_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <KoColorSpace.h>
+#include <KoColorModelStandardIds.h>
+
+class ColorModelCheck : public KisExportCheckBase
+{
+public:
+
+ ColorModelCheck(const KoID &colorModelID, const KoID &colorDepthID, const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning)
+ , m_colorModelID(colorModelID)
+ , m_colorDepthID(colorDepthID)
+ {
+ Q_ASSERT(!colorModelID.name().isEmpty());
+ Q_ASSERT(!colorDepthID.name().isEmpty());
+
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning",
+ "XXX The color model <b>%1</b> or channel depth <b>%2</b> cannot be saved to this format. Your image will be converted.",
+ m_colorModelID.name(),
+ m_colorDepthID.name());
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ return (image->colorSpace()->colorModelId() == m_colorModelID && image->colorSpace()->colorDepthId() == m_colorDepthID);
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+ const KoID m_colorModelID;
+ const KoID m_colorDepthID;
+};
+
+class ColorModelCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ ColorModelCheckFactory(const KoID &colorModelID, const KoID &colorDepthId)
+ : m_colorModelID(colorModelID)
+ , m_colorDepthID(colorDepthId)
+ {
+ }
+
+ virtual ~ColorModelCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new ColorModelCheck(m_colorModelID, m_colorDepthID, id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "ColorModelCheck/" + m_colorModelID.id() + "/" + m_colorDepthID.id();
+ }
+
+ const KoID m_colorModelID;
+ const KoID m_colorDepthID;
+};
+
+#endif // COLORMODELCHECK_H
diff --git a/libs/impex/ColorModelHomogenousCheck.h b/libs/impex/ColorModelHomogenousCheck.h
new file mode 100644
index 0000000000..c6bceedcdb
--- /dev/null
+++ b/libs/impex/ColorModelHomogenousCheck.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef COLORMODELHOMOGENOUSCHECK_H
+#define COLORMODELHOMOGENOUSCHECK_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <KoColorSpace.h>
+#include <KoColorModelStandardIds.h>
+#include <kis_layer.h>
+#include <kis_node_visitor.h>
+#include "kis_node.h"
+#include "kis_paint_layer.h"
+#include "kis_group_layer.h"
+#include "kis_adjustment_layer.h"
+#include "kis_external_layer_iface.h"
+#include "kis_clone_layer.h"
+#include "generator/kis_generator_layer.h"
+
+class KisColorModelHomogenousCheckVisitor : public KisNodeVisitor
+{
+public:
+
+ using KisNodeVisitor::visit;
+
+ KisColorModelHomogenousCheckVisitor(KoID colorModelID, KoID colorDepthID)
+ : m_count(0)
+ , m_colorModelID(colorModelID)
+ , m_colorDepthID(colorDepthID)
+ {
+ }
+
+ quint32 count() {
+ return m_count;
+ }
+
+ bool visit(KisNode* node) {
+ return check(node);
+ }
+
+ bool visit(KisPaintLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisGroupLayer *layer) {
+ return check(layer);
+ }
+
+
+ bool visit(KisAdjustmentLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisExternalLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisCloneLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisGeneratorLayer * layer) {
+ return check(layer);
+ }
+
+ virtual bool visit(KisFilterMask *) {return true;}
+
+ virtual bool visit(KisTransformMask *) {return true;}
+
+ virtual bool visit(KisTransparencyMask *) {return true;}
+
+ virtual bool visit(KisSelectionMask *) {return true;}
+
+ virtual bool visit(KisColorizeMask *) {return true;}
+
+
+private:
+ bool check(KisNode * node)
+ {
+ KisLayer *layer = dynamic_cast<KisLayer*>(node);
+ if (layer) {
+
+ const KoColorSpace * cs = layer->colorSpace();
+ if (cs->colorModelId() != m_colorModelID || cs->colorDepthId() != m_colorDepthID) {
+ m_count++;
+ }
+ }
+ visitAll(node);
+ return true;
+ }
+
+ quint32 m_count;
+ const KoID m_colorModelID;
+ const KoID m_colorDepthID;
+
+};
+
+class ColorModelHomogenousCheck : public KisExportCheckBase
+{
+public:
+
+ ColorModelHomogenousCheck(const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning, true)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "Your image contains layers with a color model that is different from the image. The layers will be converted.");
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ const KoColorSpace *cs = image->colorSpace();
+ KisColorModelHomogenousCheckVisitor v(cs->colorModelId(), cs->colorDepthId());
+ image->rootLayer()->accept(v);
+ return (v.count() > 0);
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+};
+
+class ColorModelHomogenousCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ ColorModelHomogenousCheckFactory()
+ {
+ }
+
+ virtual ~ColorModelHomogenousCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new ColorModelHomogenousCheck(id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "ColorModelHomogenousCheck";
+ }
+
+};
+
+
+#endif // COLORMODELHOMOGENOUSCHECK_H
diff --git a/libs/impex/ColorModelPerLayerCheck.h b/libs/impex/ColorModelPerLayerCheck.h
new file mode 100644
index 0000000000..9603b9958d
--- /dev/null
+++ b/libs/impex/ColorModelPerLayerCheck.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ColorModelPerLayerCHECK_H
+#define ColorModelPerLayerCHECK_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <KoColorSpace.h>
+#include <KoColorModelStandardIds.h>
+#include <kis_layer.h>
+#include <kis_node_visitor.h>
+#include "kis_node.h"
+#include "kis_paint_layer.h"
+#include "kis_group_layer.h"
+#include "kis_adjustment_layer.h"
+#include "kis_external_layer_iface.h"
+#include "kis_clone_layer.h"
+#include "generator/kis_generator_layer.h"
+
+class KisColorModelCheckVisitor : public KisNodeVisitor
+{
+public:
+
+ using KisNodeVisitor::visit;
+
+ KisColorModelCheckVisitor (KoID colorModelID, KoID colorDepthID)
+ : m_count(0)
+ , m_colorModelID(colorModelID)
+ , m_colorDepthID(colorDepthID)
+ {
+ }
+
+ quint32 count() {
+ return m_count;
+ }
+
+ bool visit(KisNode* node) {
+ return check(node);
+ }
+
+ bool visit(KisPaintLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisGroupLayer *layer) {
+ return check(layer);
+ }
+
+
+ bool visit(KisAdjustmentLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisExternalLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisCloneLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisGeneratorLayer * layer) {
+ return check(layer);
+ }
+
+ virtual bool visit(KisFilterMask *) {return true;}
+
+ virtual bool visit(KisTransformMask *) {return true;}
+
+ virtual bool visit(KisTransparencyMask *) {return true;}
+
+ virtual bool visit(KisSelectionMask *) {return true;}
+
+ virtual bool visit(KisColorizeMask *) {return true;}
+
+
+private:
+ bool check(KisNode * node)
+ {
+ KisLayer *layer = dynamic_cast<KisLayer*>(node);
+ if (layer) {
+ const KoColorSpace * cs = layer->colorSpace();
+ if (cs->colorModelId() == m_colorModelID && cs->colorDepthId() == m_colorDepthID) {
+ m_count++;
+ }
+ }
+ visitAll(node);
+ return true;
+ }
+
+ quint32 m_count;
+ const KoID m_colorModelID;
+ const KoID m_colorDepthID;
+
+};
+
+class ColorModelPerLayerCheck : public KisExportCheckBase
+{
+public:
+
+ ColorModelPerLayerCheck(const KoID &colorModelID, const KoID &colorDepthID, const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning, true)
+ , m_ColorModelID(colorModelID)
+ , m_colorDepthID(colorDepthID)
+ {
+ Q_ASSERT(!colorModelID.name().isEmpty());
+ Q_ASSERT(!colorDepthID.name().isEmpty());
+
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "Your image contains layers with the color model <b>%1</b> and channel depth <b>%2</b> which cannot be saved to this format. The layers will be converted."
+ , m_ColorModelID.name()
+ ,m_colorDepthID.name());
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ KisColorModelCheckVisitor v(m_ColorModelID, m_colorDepthID);
+ image->rootLayer()->accept(v);
+ return (v.count() > 0);
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+ const KoID m_ColorModelID;
+ const KoID m_colorDepthID;
+};
+
+class ColorModelPerLayerCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ ColorModelPerLayerCheckFactory(const KoID &ColorModelID, const KoID &colorDepthId)
+ : m_colorModelID(ColorModelID)
+ , m_colorDepthID(colorDepthId)
+ {
+ }
+
+ virtual ~ColorModelPerLayerCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new ColorModelPerLayerCheck(m_colorModelID, m_colorDepthID, id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "ColorModelPerLayerCheck/" + m_colorModelID.id() + "/" + m_colorDepthID.id();
+ }
+
+ const KoID m_colorModelID;
+ const KoID m_colorDepthID;
+};
+
+#endif // ColorModelPerLayerCHECK_H
diff --git a/libs/impex/CompositionsCheck.h b/libs/impex/CompositionsCheck.h
new file mode 100644
index 0000000000..d5decf8fcf
--- /dev/null
+++ b/libs/impex/CompositionsCheck.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef CompositionsCHECK_H
+#define CompositionsCHECK_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+
+class CompositionsCheck : public KisExportCheckBase
+{
+public:
+
+ CompositionsCheck(const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "The image contains <b>compositions</b>. The compositions will not be saved.");
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ return (image->compositions().size() > 0);
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+};
+
+class CompositionsCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ CompositionsCheckFactory()
+ {
+ }
+
+ virtual ~CompositionsCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new CompositionsCheck(id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "CompositionsCheck";
+ }
+};
+
+#endif // CompositionsCHECK_H
diff --git a/libs/impex/ExifCheck.h b/libs/impex/ExifCheck.h
new file mode 100644
index 0000000000..dfe2587590
--- /dev/null
+++ b/libs/impex/ExifCheck.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ExifCHECK_H
+#define ExifCHECK_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <KoColorSpace.h>
+#include <metadata/kis_meta_data_store.h>
+#include <metadata/kis_meta_data_filter_registry_model.h>
+#include <metadata/kis_exif_info_visitor.h>
+
+class ExifCheck : public KisExportCheckBase
+{
+public:
+
+ ExifCheck(const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "The image contains <b>Exif</b> metadata. The metadata will not be saved.");
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ KisExifInfoVisitor eIV;
+ eIV.visit(image->rootLayer().data());
+ return eIV.exifInfo();
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+};
+
+class ExifCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ ExifCheckFactory()
+ {
+ }
+
+ virtual ~ExifCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new ExifCheck(id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "ExifCheck";
+ }
+
+};
+
+#endif // ExifCHECK_H
diff --git a/libs/impex/ImageSizeCheck.h b/libs/impex/ImageSizeCheck.h
new file mode 100644
index 0000000000..1452365e91
--- /dev/null
+++ b/libs/impex/ImageSizeCheck.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef CHECKIMAGESIZE_H
+#define CHECKIMAGESIZE_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include "kritaimpex_export.h"
+
+class KRITAIMPEX_EXPORT ImageSizeCheck : public KisExportCheckBase
+{
+public:
+
+ ImageSizeCheck(int maxWidth, int maxHeight, const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning, true)
+ , m_maxW(maxWidth)
+ , m_maxH(maxHeight)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "This image is larger than <b>%1 x %2</b>. Images this size cannot be saved to this format.", m_maxW, m_maxH);
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ return image->width() >= m_maxW && image->height() >= m_maxH;
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+ int m_maxW;
+ int m_maxH;
+};
+
+class KRITAIMPEX_EXPORT ImageSizeCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ ImageSizeCheckFactory() {}
+
+ virtual ~ImageSizeCheckFactory() {}
+
+ KisExportCheckBase *create( KisExportCheckBase::Level level, const QString &customWarning = QString())
+ {
+ return new ImageSizeCheck(100000000, 100000000, id(), level, customWarning);
+ }
+
+ KisExportCheckBase *create(int maxWidth, int maxHeight, KisExportCheckBase::Level level, const QString &customWarning = QString())
+ {
+ return new ImageSizeCheck(maxWidth, maxHeight, id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "ImageSizeCheck";
+ }
+};
+
+
+#endif // CHECKIMAGESIZE_H
diff --git a/libs/ui/kra/kis_kra_utils.h b/libs/impex/KisExportCheckBase.cpp
similarity index 57%
copy from libs/ui/kra/kis_kra_utils.h
copy to libs/impex/KisExportCheckBase.cpp
index 052900495f..c6f81eee8d 100644
--- a/libs/ui/kra/kis_kra_utils.h
+++ b/libs/impex/KisExportCheckBase.cpp
@@ -1,32 +1,50 @@
/* This file is part of the KDE project
- * Copyright 2011 (C) Silvio Heinrich <plassy@web.de>
+ * Copyright (C) 2016 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.
*/
-#ifndef _KIS_KRA_UTILS_
-#define _KIS_KRA_UTILS_
-#include <QString>
-#include <QBitArray>
+#include "KisExportCheckBase.h"
-namespace KRA {
+KisExportCheckBase::KisExportCheckBase(const QString &id, Level level, const QString &customWarning, bool _perLayerCheck)
+ : m_id(id)
+ , m_level(level)
+ , m_perLayerCheck(_perLayerCheck)
+{
+ if (!customWarning.isEmpty()) {
+ m_warning = customWarning;
+ }
+}
+
+KisExportCheckBase::~KisExportCheckBase()
+{
+}
-QString flagsToString(const QBitArray& flags, int size=-1, char trueToken='1', char falseToken='0', bool defaultTrue=true);
-QBitArray stringToFlags(const QString& string, int size=-1, char token='0', bool defaultTrue=true);
+QString KisExportCheckBase::id() const
+{
+ return m_id;
+}
+
+bool KisExportCheckBase::perLayerCheck() const
+{
+ return m_perLayerCheck;
+}
+QString KisExportCheckBase::warning() const
+{
+ return m_warning;
}
-#endif // _KIS_KRA_UTILS_
diff --git a/libs/impex/KisExportCheckBase.h b/libs/impex/KisExportCheckBase.h
new file mode 100644
index 0000000000..09179bdb58
--- /dev/null
+++ b/libs/impex/KisExportCheckBase.h
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2016 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.
+ */
+#ifndef KISEXPORTCHECKBASE_H
+#define KISEXPORTCHECKBASE_H
+
+#include <QString>
+
+#include "KoGenericRegistry.h"
+
+#include <kis_types.h>
+
+#include "kritaimpex_export.h"
+
+/**
+ * @brief The KisExportCheckBase class defines the interface
+ * of the individual checks of an export filter's capabilities
+ */
+class KRITAIMPEX_EXPORT KisExportCheckBase
+{
+public:
+
+ /// The level determines the level of support the export
+ /// filter has for the given feature.
+ enum Level {
+ SUPPORTED, //< The filter fully supports this
+ PARTIALLY, //< The filter can handle this, but the user needs to be warned about possible degradation
+ UNSUPPORTED //< This cannot be saved using this filter
+ };
+
+ /**
+ * @brief KisExportCheckBase
+ * @param level the level of support the filter has for the given feature
+ * @param customWarning A custom warning to use instead of the default one
+ */
+ KisExportCheckBase(const QString &id, Level level, const QString &customWarning = QString(), bool perLayerCheck = false);
+
+ virtual ~KisExportCheckBase();
+
+ /// @return the unique id of the check
+ virtual QString id() const;
+
+ /// @return whether the image uses this feature
+ virtual bool checkNeeded(KisImageSP image) const = 0;
+
+ /// @returns true if this check is only relevant for file formats that can save multiple layers
+ virtual bool perLayerCheck() const;
+
+ /// @return the level of support for this feature
+ virtual Level check(KisImageSP image) const = 0;
+
+ /// @return the message to show the user
+ QString warning() const;
+
+protected:
+
+ QString m_id;
+ Level m_level {UNSUPPORTED};
+ QString m_warning;
+ bool m_perLayerCheck {false};
+
+};
+
+class KRITAIMPEX_EXPORT KisExportCheckFactory
+{
+public:
+ virtual KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning = QString()) = 0;
+ virtual ~KisExportCheckFactory() {}
+ virtual QString id() const = 0;
+};
+
+
+#endif
diff --git a/libs/impex/KisExportCheckRegistry.cpp b/libs/impex/KisExportCheckRegistry.cpp
new file mode 100644
index 0000000000..0d1dc1b184
--- /dev/null
+++ b/libs/impex/KisExportCheckRegistry.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2016 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 "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <KoColorSpace.h>
+#include <KoColorModelStandardIds.h>
+#include <KoColorSpaceRegistry.h>
+
+#include <AnimationCheck.h>
+#include <ColorModelCheck.h>
+#include <ColorModelPerLayerCheck.h>
+#include <CompositionsCheck.h>
+#include <ExifCheck.h>
+#include <MultiLayerCheck.h>
+#include <PSDLayerStylesCheck.h>
+#include <sRGBProfileCheck.h>
+#include <NodeTypeCheck.h>
+#include <ImageSizeCheck.h>
+#include <ColorModelHomogenousCheck.h>
+
+#include <QGlobalStatic>
+
+Q_GLOBAL_STATIC(KisExportCheckRegistry, s_instance)
+
+KisExportCheckRegistry::KisExportCheckRegistry ()
+{
+ KisExportCheckFactory *chkFactory = 0;
+
+ // Multilayer check
+ chkFactory = new MultiLayerCheckFactory();
+ add(chkFactory->id(), chkFactory);
+
+ // Animation check
+ chkFactory = new AnimationCheckFactory();
+ add(chkFactory->id(), chkFactory);
+
+ // Compositions
+ chkFactory = new CompositionsCheckFactory();
+ add(chkFactory->id(), chkFactory);
+
+ // Layer styles
+ chkFactory = new PSDLayerStyleCheckFactory();
+ add(chkFactory->id(), chkFactory);
+
+ // Check the layers for the presense of exiv info: note this is also
+ // done for multilayer images even though jpeg, which supports exiv,
+ // only can handle one layer.
+ chkFactory = new ExifCheckFactory();
+ add(chkFactory->id(), chkFactory);
+
+ // Check whether the image is sRGB
+ chkFactory = new sRGBProfileCheckFactory();
+ add(chkFactory->id(), chkFactory);
+
+ // Image size
+ chkFactory = new ImageSizeCheckFactory();
+ add(chkFactory->id(), chkFactory);
+
+ // Do all layer have the image colorspace
+ chkFactory = new ColorModelHomogenousCheckFactory();
+ add(chkFactory->id(), chkFactory);
+
+ QList<KoID> allColorModels = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::AllColorSpaces);
+ Q_FOREACH(const KoID &colorModelID, allColorModels) {
+ QList<KoID> allColorDepths = KoColorSpaceRegistry::instance()->colorDepthList(colorModelID.id(), KoColorSpaceRegistry::AllColorSpaces);
+ Q_FOREACH(const KoID &colorDepthID, allColorDepths) {
+
+ Q_ASSERT(!colorModelID.name().isEmpty());
+ Q_ASSERT(!colorDepthID.name().isEmpty());
+
+ // Per layer color model/channel depth checks
+ chkFactory = new ColorModelPerLayerCheckFactory(colorModelID, colorDepthID);
+ add(chkFactory->id(), chkFactory);
+
+ // Image color model/channel depth checks
+ chkFactory = new ColorModelCheckFactory(colorModelID, colorDepthID);
+ add(chkFactory->id(), chkFactory);
+ }
+ }
+
+ // Node type checks
+ chkFactory = new NodeTypeCheckFactory("KisCloneLayer", i18n("Clone Layer"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisGroupLayer", i18n("Group Layer"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisFileLayer", i18n("File Layer"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisShapeLayer", i18n("Vector Layer"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisAdjustmentLayer", i18n("Filter Layer"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisGeneratorLayer", i18n("Generator Layer"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisColorizeMask", i18n("Colorize Mask"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisFilterMask", i18n("Filter Mask"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisTransformMask", i18n("Transform Mask"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisTransparencyMask", i18n("Transparency Mask"));
+ add(chkFactory->id(), chkFactory);
+ chkFactory = new NodeTypeCheckFactory("KisSelectionMask", i18n("Selection Mask"));
+ add(chkFactory->id(), chkFactory);
+
+
+}
+
+KisExportCheckRegistry::~KisExportCheckRegistry ()
+{
+ qDeleteAll(values());
+}
+
+KisExportCheckRegistry *KisExportCheckRegistry ::instance()
+{
+ return s_instance;
+}
+
diff --git a/libs/ui/kra/kis_kra_utils.h b/libs/impex/KisExportCheckRegistry.h
similarity index 59%
copy from libs/ui/kra/kis_kra_utils.h
copy to libs/impex/KisExportCheckRegistry.h
index 052900495f..418bb0d5a6 100644
--- a/libs/ui/kra/kis_kra_utils.h
+++ b/libs/impex/KisExportCheckRegistry.h
@@ -1,32 +1,44 @@
-/* This file is part of the KDE project
- * Copyright 2011 (C) Silvio Heinrich <plassy@web.de>
+/*
+ * Copyright (C) 2016 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.
*/
-#ifndef _KIS_KRA_UTILS_
-#define _KIS_KRA_UTILS_
+
+#ifndef KISEXPORTCHECKREGISTRY_H
+#define KISEXPORTCHECKREGISTRY_H
#include <QString>
-#include <QBitArray>
-namespace KRA {
+#include <KoGenericRegistry.h>
+
+#include "KisExportCheckBase.h"
+
+#include "kritaimpex_export.h"
+
+
+class KRITAIMPEX_EXPORT KisExportCheckRegistry : public QObject, public KoGenericRegistry<KisExportCheckFactory*>
+{
+public:
+ KisExportCheckRegistry();
+ virtual ~KisExportCheckRegistry();
+ static KisExportCheckRegistry *instance();
-QString flagsToString(const QBitArray& flags, int size=-1, char trueToken='1', char falseToken='0', bool defaultTrue=true);
-QBitArray stringToFlags(const QString& string, int size=-1, char token='0', bool defaultTrue=true);
+private:
+ Q_DISABLE_COPY(KisExportCheckRegistry)
+};
-}
-#endif // _KIS_KRA_UTILS_
+#endif // KISEXPORTCHECKREGISTRY_H
diff --git a/libs/impex/KisPreExportChecker.cpp b/libs/impex/KisPreExportChecker.cpp
new file mode 100644
index 0000000000..f7180cf7fa
--- /dev/null
+++ b/libs/impex/KisPreExportChecker.cpp
@@ -0,0 +1,82 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2016 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 "KisPreExportChecker.h"
+#include "KisExportCheckBase.h"
+#include "KisExportCheckRegistry.h"
+
+#include <kis_image.h>
+
+KisPreExportChecker::KisPreExportChecker()
+{
+ KisExportCheckRegistry::instance();
+}
+
+bool KisPreExportChecker::check(KisImageSP image, QMap<QString, KisExportCheckBase*> filterChecks)
+{
+ bool doPerLayerChecks = false;
+ if (filterChecks.contains("MultiLayerCheck") && filterChecks["MultiLayerCheck"]->check(image) == KisExportCheckBase::SUPPORTED) {
+ doPerLayerChecks = true;
+ }
+
+ Q_FOREACH(const QString &id, KisExportCheckRegistry::instance()->keys()) {
+
+ KisExportCheckFactory *factory = KisExportCheckRegistry::instance()->get(id);
+ KisExportCheckBase *check = factory->create(KisExportCheckBase::SUPPORTED);
+
+ if (!doPerLayerChecks && check->perLayerCheck()) {
+ continue;
+ }
+
+ if (check->checkNeeded(image)) {
+ if (!filterChecks.contains(id)) {
+ m_warnings << check->warning();
+ }
+ else {
+ KisExportCheckBase *filterCheck = filterChecks[id];
+ KisExportCheckBase::Level level = filterCheck->check(image);
+ QString warning = filterCheck->warning();
+
+ if (level == KisExportCheckBase::PARTIALLY) {
+ m_warnings << warning;
+ }
+ else if (level == KisExportCheckBase::UNSUPPORTED) {
+ m_errors << warning;
+ }
+ else {
+ continue;
+ }
+ }
+ }
+ delete check;
+ }
+ return m_warnings.isEmpty() && m_errors.isEmpty();
+}
+
+QStringList KisPreExportChecker::errors() const
+{
+ return m_errors;
+}
+
+
+QStringList KisPreExportChecker::warnings() const
+{
+ return m_warnings;
+}
+
diff --git a/libs/impex/KisPreExportChecker.h b/libs/impex/KisPreExportChecker.h
new file mode 100644
index 0000000000..91f15df7ec
--- /dev/null
+++ b/libs/impex/KisPreExportChecker.h
@@ -0,0 +1,50 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2016 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.
+ */
+#ifndef KISPREEXPORTCHECKER_H
+#define KISPREEXPORTCHECKER_H
+
+#include <kis_types.h>
+#include "kritaimpex_export.h"
+
+#include "KisExportCheckBase.h"
+
+class KisExportConverterBase;
+
+class KRITAIMPEX_EXPORT KisPreExportChecker
+{
+public:
+ KisPreExportChecker();
+
+ /**
+ * @brief check checks the image against the capabilities of the export filter
+ * @param image the current image
+ * @param filterChecks the list of capabilities the filter possesses
+ * @return true if no warnings and no conversions are needed
+ */
+ bool check(KisImageSP image, QMap<QString, KisExportCheckBase *> filterChecks);
+ QStringList warnings() const;
+ QStringList errors() const;
+
+private:
+
+ QStringList m_errors;
+ QStringList m_warnings;
+};
+
+#endif // KISPREEXPORTCHECKER_H
diff --git a/libs/impex/MultiLayerCheck.h b/libs/impex/MultiLayerCheck.h
new file mode 100644
index 0000000000..b8e197c6ea
--- /dev/null
+++ b/libs/impex/MultiLayerCheck.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef MultiLayerCheck_H
+#define MultiLayerCheck_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <kis_group_layer.h>
+
+class MultiLayerCheck : public KisExportCheckBase
+{
+public:
+
+ MultiLayerCheck(const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "The image has <b>more than one layer</b>. Only the flattened image will be saved.");
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ return (image->rootLayer()->childCount() > 1);
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+};
+
+class MultiLayerCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ MultiLayerCheckFactory() {}
+
+ virtual ~MultiLayerCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new MultiLayerCheck(id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "MultiLayerCheck";
+ }
+};
+
+#endif // MultiLayerCheck_H
diff --git a/libs/impex/NodeTypeCheck.h b/libs/impex/NodeTypeCheck.h
new file mode 100644
index 0000000000..58afde86be
--- /dev/null
+++ b/libs/impex/NodeTypeCheck.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef NodeTypeCheck_H
+#define NodeTypeCheck_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <kis_count_visitor.h>
+
+class NodeTypeCheck : public KisExportCheckBase
+{
+public:
+
+ NodeTypeCheck(const QString &nodeType, const QString &description, const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning, true)
+ , m_nodeType(nodeType)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "The image contains layers of unsupported type <b>%1</b>. Only the rendered result will be saved.", description);
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ QStringList nodetypes = QStringList() << m_nodeType;
+ KoProperties props;
+ KisCountVisitor v(nodetypes, props);
+ image->rootLayer()->accept(v);
+
+ // There is always one group layer, the root layer.
+ if (m_nodeType == "KisGroupLayer") {
+ return (v.count() > 1);
+ }
+ else {
+ return (v.count() > 0);
+ }
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+ QString m_nodeType;
+};
+
+class NodeTypeCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ NodeTypeCheckFactory(const QString &nodeType, const QString &description)
+ : m_nodeType(nodeType)
+ , m_description(description)
+ {}
+
+ virtual ~NodeTypeCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new NodeTypeCheck(m_nodeType, m_description, id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "NodeTypeCheck/" + m_nodeType;
+ }
+
+ QString m_nodeType;
+ QString m_description;
+
+};
+
+#endif // NodeTypeCheck_H
diff --git a/libs/impex/PSDLayerStylesCheck.h b/libs/impex/PSDLayerStylesCheck.h
new file mode 100644
index 0000000000..70e96f20f4
--- /dev/null
+++ b/libs/impex/PSDLayerStylesCheck.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef PSDLayerStyleCHECK_H
+#define PSDLayerStyleCHECK_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <KoColorSpace.h>
+#include <kis_node_visitor.h>
+#include "kis_node.h"
+#include "kis_paint_layer.h"
+#include "kis_group_layer.h"
+#include "kis_adjustment_layer.h"
+#include "kis_external_layer_iface.h"
+#include "kis_clone_layer.h"
+#include "generator/kis_generator_layer.h"
+
+class KisLayerStyleVisitor : public KisNodeVisitor
+{
+public:
+
+ using KisNodeVisitor::visit;
+
+ KisLayerStyleVisitor()
+ : m_count(0)
+ {
+ }
+
+ quint32 count() {
+ return m_count;
+ }
+
+ bool visit(KisNode* node) {
+ return check(node);
+ }
+
+ bool visit(KisPaintLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisGroupLayer *layer) {
+ return check(layer);
+ }
+
+
+ bool visit(KisAdjustmentLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisExternalLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisCloneLayer *layer) {
+ return check(layer);
+ }
+
+ bool visit(KisGeneratorLayer * layer) {
+ return check(layer);
+ }
+
+ virtual bool visit(KisFilterMask *) {return true;}
+
+ virtual bool visit(KisTransformMask *) {return true;}
+
+ virtual bool visit(KisTransparencyMask *) {return true;}
+
+ virtual bool visit(KisSelectionMask *) {return true;}
+
+ virtual bool visit(KisColorizeMask *) {return true;}
+
+
+private:
+ bool check(KisNode * node)
+ {
+ KisLayer *layer = dynamic_cast<KisLayer*>(node);
+ if (layer) {
+ if (layer->layerStyle()) {
+ m_count++;
+ }
+ }
+ visitAll(node);
+ return true;
+ }
+
+ quint32 m_count;
+};
+
+
+class PSDLayerStyleCheck : public KisExportCheckBase
+{
+public:
+
+ PSDLayerStyleCheck(const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning, true)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "The image contains <b>layer styles</b>. The layer styles will not be saved.");
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ KisLayerStyleVisitor v;
+ image->rootLayer()->accept(v);
+ return (v.count() > 0);
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+};
+
+class PSDLayerStyleCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ PSDLayerStyleCheckFactory() {}
+
+ virtual ~PSDLayerStyleCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new PSDLayerStyleCheck(id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "PSDLayerStyleCheck";
+ }
+};
+
+#endif // PSDLayerStyleCHECK_H
diff --git a/libs/impex/sRGBProfileCheck.h b/libs/impex/sRGBProfileCheck.h
new file mode 100644
index 0000000000..b8a41bd006
--- /dev/null
+++ b/libs/impex/sRGBProfileCheck.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef sRGBProfileCheck_H
+#define sRGBProfileCheck_H
+
+#include "KisExportCheckRegistry.h"
+#include <KoID.h>
+#include <klocalizedstring.h>
+#include <kis_image.h>
+#include <KoColorSpace.h>
+#include <KoColorProfile.h>
+
+/**
+ * Check whether the image is sRGB, so the loss of the profile is not a problem
+ */
+class sRGBProfileCheck : public KisExportCheckBase
+{
+public:
+
+ sRGBProfileCheck(const QString &id, Level level, const QString &customWarning = QString())
+ : KisExportCheckBase(id, level, customWarning)
+ {
+ if (customWarning.isEmpty()) {
+ m_warning = i18nc("image conversion warning", "The image is not tagged as <b>non-linear gamma sRGB</b>. The image will be converted to sRGB.");
+ }
+ }
+
+ bool checkNeeded(KisImageSP image) const
+ {
+ bool sRGB = image->colorSpace()->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive);
+
+ // XXX: add an isLinear function to KoColorProfile that uses the information already available through lcms
+ bool linear = image->colorSpace()->profile()->name().contains(QLatin1String("g10"), Qt::CaseInsensitive);
+
+ return (!sRGB || linear);
+ }
+
+ Level check(KisImageSP /*image*/) const
+ {
+ return m_level;
+ }
+
+};
+
+class sRGBProfileCheckFactory : public KisExportCheckFactory
+{
+public:
+
+ sRGBProfileCheckFactory()
+ {
+ }
+
+ virtual ~sRGBProfileCheckFactory() {}
+
+ KisExportCheckBase *create(KisExportCheckBase::Level level, const QString &customWarning)
+ {
+ return new sRGBProfileCheck(id(), level, customWarning);
+ }
+
+ QString id() const {
+ return "sRGBProfileCheck";
+ }
+};
+
+#endif // sRGBProfileCheck_H
diff --git a/libs/koplugin/KoJsonTrader.cpp b/libs/koplugin/KoJsonTrader.cpp
index b6a655bdc2..8c8211a241 100644
--- a/libs/koplugin/KoJsonTrader.cpp
+++ b/libs/koplugin/KoJsonTrader.cpp
@@ -1,154 +1,154 @@
/* 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 "KoJsonTrader.h"
#include "KritaPluginDebug.h"
#include <QCoreApplication>
#include <QPluginLoader>
#include <QJsonObject>
#include <QJsonArray>
#include <QDirIterator>
#include <QDir>
#include <QProcessEnvironment>
#include <QGlobalStatic>
KoJsonTrader::KoJsonTrader()
{
// Allow a command line variable KRITA_PLUGIN_PATH to override the automatic search
auto requestedPath = QProcessEnvironment::systemEnvironment().value("KRITA_PLUGIN_PATH");
if (!requestedPath.isEmpty()) {
m_pluginPath = requestedPath;
}
else {
QList<QDir> searchDirs;
QDir appDir(qApp->applicationDirPath());
appDir.cdUp();
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
// Help Krita run without deployment
QDir d(appDir);
d.cd("../../../");
searchDirs << d;
#endif
searchDirs << appDir;
// help plugin trader find installed plugins when run from uninstalled tests
#ifdef CMAKE_INSTALL_PREFIX
searchDirs << QDir(CMAKE_INSTALL_PREFIX);
#endif
Q_FOREACH (const QDir& dir, searchDirs) {
Q_FOREACH (QString entry, dir.entryList()) {
QFileInfo info(dir, entry);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
if (info.isDir() && info.fileName().contains("PlugIns")) {
m_pluginPath = info.absoluteFilePath();
break;
}
else if (info.isDir() && (info.fileName().contains("lib"))) {
#else
if (info.isDir() && info.fileName().contains("lib")) {
#endif
QDir libDir(info.absoluteFilePath());
// on many systems this will be the actual lib dir (and krita subdir contains plugins)
if (libDir.entryList(QStringList() << "kritaplugins").size() > 0) {
m_pluginPath = info.absoluteFilePath() + "/kritaplugins";
break;
}
// on debian at least the actual libdir is a subdir named like "lib/x86_64-linux-gnu"
// so search there for the Krita subdir which will contain our plugins
Q_FOREACH (QString subEntry, libDir.entryList()) {
QFileInfo subInfo(libDir, subEntry);
if (subInfo.isDir()) {
if (QDir(subInfo.absoluteFilePath()).entryList(QStringList() << "kritaplugins").size() > 0) {
m_pluginPath = subInfo.absoluteFilePath() + "/kritaplugins";
break; // will only break inner loop so we need the extra check below
}
}
}
if (!m_pluginPath.isEmpty()) {
break;
}
}
}
if (!m_pluginPath.isEmpty()) {
break;
}
}
debugPlugin << "KoJsonTrader will load its plugins from" << m_pluginPath;
}
}
Q_GLOBAL_STATIC(KoJsonTrader, s_instance)
KoJsonTrader* KoJsonTrader::instance()
{
return s_instance;
}
QList<QPluginLoader *> KoJsonTrader::query(const QString &servicetype, const QString &mimetype) const
{
QList<QPluginLoader *>list;
QDirIterator dirIter(m_pluginPath, QDirIterator::Subdirectories);
while (dirIter.hasNext()) {
dirIter.next();
if (dirIter.fileInfo().isFile() && dirIter.fileName().startsWith("krita") && !dirIter.fileName().endsWith(".debug")) {
debugPlugin << dirIter.fileName();
QPluginLoader *loader = new QPluginLoader(dirIter.filePath());
QJsonObject json = loader->metaData().value("MetaData").toObject();
debugPlugin << mimetype << json << json.value("X-KDE-ServiceTypes");
if (json.isEmpty()) {
delete loader;
qWarning() << dirIter.filePath() << "has no json!";
}
else {
QJsonArray serviceTypes = json.value("X-KDE-ServiceTypes").toArray();
if (serviceTypes.isEmpty()) {
qWarning() << dirIter.fileName() << "has no X-KDE-ServiceTypes";
}
if (!serviceTypes.contains(QJsonValue(servicetype))) {
delete loader;
continue;
}
if (!mimetype.isEmpty()) {
QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(',');
mimeTypes += json.value("MimeType").toString().split(';');
mimeTypes += json.value("X-KDE-NativeMimeType").toString();
if (! mimeTypes.contains(mimetype)) {
qWarning() << dirIter.filePath() << "doesn't contain mimetype" << mimetype << "in" << mimeTypes;
delete loader;
continue;
}
}
list.append(loader);
}
}
}
return list;
}
diff --git a/libs/odf/KoDocumentBase.h b/libs/odf/KoDocumentBase.h
index 2cb79a77fe..9ce0d7c9a5 100644
--- a/libs/odf/KoDocumentBase.h
+++ b/libs/odf/KoDocumentBase.h
@@ -1,169 +1,115 @@
/* 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 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2009 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.
*/
#ifndef KODOCUMENTBASE_H
#define KODOCUMENTBASE_H
class KoStore;
class KoOdfReadStore;
class KoOdfWriteStore;
class KoEmbeddedDocumentSaver;
class QUrl;
class QByteArray;
class QString;
#include "kritaodf_export.h"
/**
* Base class for documents that can load and save ODF. Most of the
* implementation is still in KoDocument, though that should probably
* change.
*/
class KRITAODF_EXPORT KoDocumentBase
{
public:
// context passed on saving to saveOdf
struct SavingContext {
SavingContext(KoOdfWriteStore &odfStore, KoEmbeddedDocumentSaver &embeddedSaver)
: odfStore(odfStore)
, embeddedSaver(embeddedSaver) {}
KoOdfWriteStore &odfStore;
KoEmbeddedDocumentSaver &embeddedSaver;
};
/**
* create a new KoDocumentBase
*/
KoDocumentBase();
/**
* delete this document
*/
virtual ~KoDocumentBase();
- /**
- * Return true if url() is a real filename, false if url() is
- * an internal url in the store, like "tar:/..."
- */
- virtual bool isStoredExtern() const = 0;
-
/**
* @return the current URL
*/
virtual QUrl url() const = 0;
virtual void setUrl(const QUrl &url) = 0;
- /**
- * @brief Saves a document to a store.
- */
- virtual bool saveToStore(KoStore *store, const QString &path) = 0;
-
- /**
- * Reimplement this method to load the odf document. Take care to
- * make sure styles are loaded before body text is loaded by the
- * text shape.
- */
- virtual bool loadOdf(KoOdfReadStore &odfStore) = 0;
-
- /**
- * Reimplement this method to save the contents of your %Calligra document,
- * using the ODF format.
- */
- virtual bool saveOdf(SavingContext &documentContext) = 0;
-
/**
* Checks whether the document is currently in the process of autosaving
*/
virtual bool isAutosaving() const = 0;
/**
* Returns true if this document or any of its internal child documents are modified.
*/
virtual bool isModified() const = 0;
- /**
- * @return true if the document is empty.
- */
- virtual bool isEmpty() const = 0;
-
/**
* Returns the actual mimetype of the document
*/
virtual QByteArray mimeType() const = 0;
/**
* @brief Sets the mime type for the document.
*
* When choosing "save as" this is also the mime type
* selected by default.
*/
virtual void setMimeType(const QByteArray & mimeType) = 0;
virtual QString localFilePath() const = 0;
-
- /**
- * Return the set of SupportedSpecialFormats that the application wants to
- * offer in the "Save" file dialog.
- */
- virtual int supportedSpecialFormats() const = 0;
-
- /// Enum values used by specialOutputFlag - note that it's a bitfield for supportedSpecialFormats
- enum { /*SaveAsCalligra1dot1 = 1,*/ // old and removed
- SaveAsDirectoryStore = 2,
- SaveAsFlatXML = 4,
- SaveEncrypted = 8
- // bitfield! next value is 16
- };
- virtual int specialOutputFlag() const = 0;
-
/**
* @brief Set the format in which the document should be saved.
*
* This is called on loading, and in "save as", so you shouldn't
* have to call it.
*
* @param mimeType the mime type (format) to use.
- * @param specialOutputFlag is for "save as older version" etc.
*/
- virtual void setOutputMimeType(const QByteArray & mimeType, int specialOutputFlag = 0) = 0;
+ virtual void setOutputMimeType(const QByteArray & mimeType) = 0;
virtual QByteArray outputMimeType() const = 0;
- /**
- * Sets the document URL to empty URL
- * KParts doesn't allow this, but %Calligra apps have e.g. templates
- * After using loadNativeFormat on a template, one wants
- * to set the url to QUrl()
- */
- virtual void resetURL() = 0;
-
private:
class Private;
Private *const d;
};
#endif
diff --git a/libs/odf/KoDocumentInfo.cpp b/libs/odf/KoDocumentInfo.cpp
index d72caa3bdd..73df833b3c 100644
--- a/libs/odf/KoDocumentInfo.cpp
+++ b/libs/odf/KoDocumentInfo.cpp
@@ -1,471 +1,471 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
Copyright (C) 2004 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 "KoDocumentInfo.h"
#include "KoDocumentBase.h"
#include "KoOdfWriteStore.h"
#include "KoXmlNS.h"
#include <QDateTime>
#include <KoStoreDevice.h>
#include <KoXmlWriter.h>
#include <QDomDocument>
#include <KoXmlReader.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <OdfDebug.h>
#include <klocalizedstring.h>
#include <kuser.h>
#include <kemailsettings.h>
#include <KritaVersionWrapper.h>
KoDocumentInfo::KoDocumentInfo(QObject *parent) : QObject(parent)
{
m_aboutTags << "title" << "description" << "subject" << "abstract"
<< "keyword" << "initial-creator" << "editing-cycles" << "editing-time"
<< "date" << "creation-date" << "language";
m_authorTags << "creator" << "initial" << "author-title"
<< "email" << "telephone" << "telephone-work"
<< "fax" << "country" << "postal-code" << "city"
<< "street" << "position" << "company";
setAboutInfo("editing-cycles", "0");
setAboutInfo("time-elapsed", "0");
setAboutInfo("initial-creator", i18n("Unknown"));
setAboutInfo("creation-date", QDateTime::currentDateTime()
.toString(Qt::ISODate));
}
KoDocumentInfo::~KoDocumentInfo()
{
}
bool KoDocumentInfo::load(const KoXmlDocument &doc)
{
m_authorInfo.clear();
if (!loadAboutInfo(doc.documentElement()))
return false;
if (!loadAuthorInfo(doc.documentElement()))
return false;
return true;
}
bool KoDocumentInfo::loadOasis(const KoXmlDocument &metaDoc)
{
m_authorInfo.clear();
KoXmlNode t = KoXml::namedItemNS(metaDoc, KoXmlNS::office, "document-meta");
KoXmlNode office = KoXml::namedItemNS(t, KoXmlNS::office, "meta");
if (office.isNull())
return false;
if (!loadOasisAboutInfo(office))
return false;
if (!loadOasisAuthorInfo(office))
return false;
return true;
}
QDomDocument KoDocumentInfo::save(QDomDocument &doc)
{
updateParametersAndBumpNumCycles();
QDomElement s = saveAboutInfo(doc);
if (!s.isNull())
doc.documentElement().appendChild(s);
s = saveAuthorInfo(doc);
if (!s.isNull())
doc.documentElement().appendChild(s);
if (doc.documentElement().isNull())
return QDomDocument();
return doc;
}
bool KoDocumentInfo::saveOasis(KoStore *store)
{
updateParametersAndBumpNumCycles();
KoStoreDevice dev(store);
KoXmlWriter* xmlWriter = KoOdfWriteStore::createOasisXmlWriter(&dev,
"office:document-meta");
xmlWriter->startElement("office:meta");
xmlWriter->startElement("meta:generator");
xmlWriter->addTextNode(QString("Calligra/%1")
.arg(KritaVersionWrapper::versionString()));
xmlWriter->endElement();
if (!saveOasisAboutInfo(*xmlWriter))
return false;
if (!saveOasisAuthorInfo(*xmlWriter))
return false;
xmlWriter->endElement();
xmlWriter->endElement(); // root element
xmlWriter->endDocument();
delete xmlWriter;
return true;
}
void KoDocumentInfo::setAuthorInfo(const QString &info, const QString &data)
{
if (!m_authorTags.contains(info)) {
return;
}
m_authorInfoOverride.insert(info, data);
}
void KoDocumentInfo::setActiveAuthorInfo(const QString &info, const QString &data)
{
if (!m_authorTags.contains(info)) {
return;
}
if (data.isEmpty()) {
m_authorInfo.remove(info);
} else {
m_authorInfo.insert(info, data);
}
emit infoUpdated(info, data);
}
QString KoDocumentInfo::authorInfo(const QString &info) const
{
if (!m_authorTags.contains(info))
return QString();
return m_authorInfo[ info ];
}
void KoDocumentInfo::setAboutInfo(const QString &info, const QString &data)
{
if (!m_aboutTags.contains(info))
return;
m_aboutInfo.insert(info, data);
emit infoUpdated(info, data);
}
QString KoDocumentInfo::aboutInfo(const QString &info) const
{
if (!m_aboutTags.contains(info)) {
return QString();
}
return m_aboutInfo[info];
}
bool KoDocumentInfo::saveOasisAuthorInfo(KoXmlWriter &xmlWriter)
{
Q_FOREACH (const QString & tag, m_authorTags) {
if (!authorInfo(tag).isEmpty() && tag == "creator") {
xmlWriter.startElement("dc:creator");
xmlWriter.addTextNode(authorInfo("creator"));
xmlWriter.endElement();
} else if (!authorInfo(tag).isEmpty()) {
xmlWriter.startElement("meta:user-defined");
xmlWriter.addAttribute("meta:name", tag);
xmlWriter.addTextNode(authorInfo(tag));
xmlWriter.endElement();
}
}
return true;
}
bool KoDocumentInfo::loadOasisAuthorInfo(const KoXmlNode &metaDoc)
{
KoXmlElement e = KoXml::namedItemNS(metaDoc, KoXmlNS::dc, "creator");
if (!e.isNull() && !e.text().isEmpty())
setActiveAuthorInfo("creator", e.text());
KoXmlNode n = metaDoc.firstChild();
for (; !n.isNull(); n = n.nextSibling()) {
if (!n.isElement())
continue;
KoXmlElement e = n.toElement();
if (!(e.namespaceURI() == KoXmlNS::meta &&
e.localName() == "user-defined" && !e.text().isEmpty()))
continue;
QString name = e.attributeNS(KoXmlNS::meta, "name", QString());
setActiveAuthorInfo(name, e.text());
}
return true;
}
bool KoDocumentInfo::loadAuthorInfo(const KoXmlElement &e)
{
KoXmlNode n = e.namedItem("author").firstChild();
for (; !n.isNull(); n = n.nextSibling()) {
KoXmlElement e = n.toElement();
if (e.isNull())
continue;
if (e.tagName() == "full-name")
setActiveAuthorInfo("creator", e.text().trimmed());
else
setActiveAuthorInfo(e.tagName(), e.text().trimmed());
}
return true;
}
QDomElement KoDocumentInfo::saveAuthorInfo(QDomDocument &doc)
{
QDomElement e = doc.createElement("author");
QDomElement t;
Q_FOREACH (const QString &tag, m_authorTags) {
if (tag == "creator")
t = doc.createElement("full-name");
else
t = doc.createElement(tag);
e.appendChild(t);
t.appendChild(doc.createTextNode(authorInfo(tag)));
}
return e;
}
bool KoDocumentInfo::saveOasisAboutInfo(KoXmlWriter &xmlWriter)
{
Q_FOREACH (const QString &tag, m_aboutTags) {
if (!aboutInfo(tag).isEmpty() || tag == "title") {
if (tag == "keyword") {
Q_FOREACH (const QString & tmp, aboutInfo("keyword").split(';')) {
xmlWriter.startElement("meta:keyword");
xmlWriter.addTextNode(tmp);
xmlWriter.endElement();
}
} else if (tag == "title" || tag == "description" || tag == "subject" ||
tag == "date" || tag == "language") {
QByteArray elementName(QString("dc:" + tag).toLatin1());
xmlWriter.startElement(elementName.constData());
xmlWriter.addTextNode(aboutInfo(tag));
xmlWriter.endElement();
} else {
QByteArray elementName(QString("meta:" + tag).toLatin1());
xmlWriter.startElement(elementName.constData());
xmlWriter.addTextNode(aboutInfo(tag));
xmlWriter.endElement();
}
}
}
return true;
}
bool KoDocumentInfo::loadOasisAboutInfo(const KoXmlNode &metaDoc)
{
QStringList keywords;
KoXmlElement e;
forEachElement(e, metaDoc) {
QString tag(e.localName());
if (! m_aboutTags.contains(tag) && tag != "generator") {
continue;
}
//debugOdf<<"localName="<<e.localName();
if (tag == "keyword") {
if (!e.text().isEmpty())
keywords << e.text().trimmed();
} else if (tag == "description") {
//this is the odf way but add meta:comment if is already loaded
KoXmlElement e = KoXml::namedItemNS(metaDoc, KoXmlNS::dc, tag);
if (!e.isNull() && !e.text().isEmpty())
setAboutInfo("description", aboutInfo("description") + e.text().trimmed());
} else if (tag == "abstract") {
//this was the old way so add it to dc:description
KoXmlElement e = KoXml::namedItemNS(metaDoc, KoXmlNS::meta, tag);
if (!e.isNull() && !e.text().isEmpty())
setAboutInfo("description", aboutInfo("description") + e.text().trimmed());
} else if (tag == "title"|| tag == "subject"
|| tag == "date" || tag == "language") {
KoXmlElement e = KoXml::namedItemNS(metaDoc, KoXmlNS::dc, tag);
if (!e.isNull() && !e.text().isEmpty())
setAboutInfo(tag, e.text().trimmed());
} else if (tag == "generator") {
setOriginalGenerator(e.text().trimmed());
} else {
KoXmlElement e = KoXml::namedItemNS(metaDoc, KoXmlNS::meta, tag);
if (!e.isNull() && !e.text().isEmpty())
setAboutInfo(tag, e.text().trimmed());
}
}
if (keywords.count() > 0) {
setAboutInfo("keyword", keywords.join(", "));
}
return true;
}
bool KoDocumentInfo::loadAboutInfo(const KoXmlElement &e)
{
KoXmlNode n = e.namedItem("about").firstChild();
KoXmlElement tmp;
for (; !n.isNull(); n = n.nextSibling()) {
tmp = n.toElement();
if (tmp.isNull())
continue;
if (tmp.tagName() == "abstract")
setAboutInfo("abstract", tmp.text());
setAboutInfo(tmp.tagName(), tmp.text());
}
return true;
}
QDomElement KoDocumentInfo::saveAboutInfo(QDomDocument &doc)
{
QDomElement e = doc.createElement("about");
QDomElement t;
Q_FOREACH (const QString &tag, m_aboutTags) {
if (tag == "abstract") {
t = doc.createElement("abstract");
e.appendChild(t);
t.appendChild(doc.createCDATASection(aboutInfo(tag)));
} else {
t = doc.createElement(tag);
e.appendChild(t);
t.appendChild(doc.createTextNode(aboutInfo(tag)));
}
}
return e;
}
void KoDocumentInfo::updateParametersAndBumpNumCycles()
{
KoDocumentBase *doc = dynamic_cast< KoDocumentBase *>(parent());
if (doc && doc->isAutosaving()) {
return;
}
setAboutInfo("editing-cycles", QString::number(aboutInfo("editing-cycles").toInt() + 1));
setAboutInfo("date", QDateTime::currentDateTime().toString(Qt::ISODate));
updateParameters();
}
void KoDocumentInfo::updateParameters()
{
KoDocumentBase *doc = dynamic_cast< KoDocumentBase *>(parent());
- if (doc && (!doc->isModified() && !doc->isEmpty())) {
+ if (doc && (!doc->isModified())) {
return;
}
KConfig config("kritarc");
config.reparseConfiguration();
KConfigGroup authorGroup(&config, "Author");
QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
config.reparseConfiguration();
KConfigGroup appAuthorGroup(&config, "Author");
QString profile = appAuthorGroup.readEntry("active-profile", "");
if (profiles.contains(profile)) {
KConfigGroup cgs(&authorGroup, "Author-" + profile);
setActiveAuthorInfo("creator", cgs.readEntry("creator"));
setActiveAuthorInfo("initial", cgs.readEntry("initial"));
setActiveAuthorInfo("author-title", cgs.readEntry("author-title"));
setActiveAuthorInfo("email", cgs.readEntry("email"));
setActiveAuthorInfo("telephone", cgs.readEntry("telephone"));
setActiveAuthorInfo("telephone-work", cgs.readEntry("telephone-work"));
setActiveAuthorInfo("fax", cgs.readEntry("fax"));
setActiveAuthorInfo("country",cgs.readEntry("country"));
setActiveAuthorInfo("postal-code",cgs.readEntry("postal-code"));
setActiveAuthorInfo("city", cgs.readEntry("city"));
setActiveAuthorInfo("street", cgs.readEntry("street"));
setActiveAuthorInfo("position", cgs.readEntry("position"));
setActiveAuthorInfo("company", cgs.readEntry("company"));
} else {
if (profile == "anonymous") {
setActiveAuthorInfo("creator", QString());
setActiveAuthorInfo("telephone", QString());
setActiveAuthorInfo("telephone-work", QString());
setActiveAuthorInfo("email", QString());
} else {
KUser user(KUser::UseRealUserID);
setActiveAuthorInfo("creator", user.property(KUser::FullName).toString());
setActiveAuthorInfo("telephone-work", user.property(KUser::WorkPhone).toString());
setActiveAuthorInfo("telephone", user.property(KUser::HomePhone).toString());
KEMailSettings eMailSettings;
setActiveAuthorInfo("email", eMailSettings.getSetting(KEMailSettings::EmailAddress));
}
setActiveAuthorInfo("initial", "");
setActiveAuthorInfo("author-title", "");
setActiveAuthorInfo("fax", "");
setActiveAuthorInfo("country", "");
setActiveAuthorInfo("postal-code", "");
setActiveAuthorInfo("city", "");
setActiveAuthorInfo("street", "");
setActiveAuthorInfo("position", "");
setActiveAuthorInfo("company", "");
}
//alllow author info set programatically to override info from author profile
Q_FOREACH (const QString &tag, m_authorTags) {
if (m_authorInfoOverride.contains(tag)) {
setActiveAuthorInfo(tag, m_authorInfoOverride.value(tag));
}
}
}
void KoDocumentInfo::resetMetaData()
{
setAboutInfo("editing-cycles", QString::number(0));
setAboutInfo("initial-creator", authorInfo("creator"));
setAboutInfo("creation-date", QDateTime::currentDateTime().toString(Qt::ISODate));
setAboutInfo("editing-time", QString::number(0));
}
QString KoDocumentInfo::originalGenerator() const
{
return m_generator;
}
void KoDocumentInfo::setOriginalGenerator(const QString &generator)
{
m_generator = generator;
}
diff --git a/libs/odf/KoEmbeddedDocumentSaver.cpp b/libs/odf/KoEmbeddedDocumentSaver.cpp
index 79d8a1664d..f947700efa 100644
--- a/libs/odf/KoEmbeddedDocumentSaver.cpp
+++ b/libs/odf/KoEmbeddedDocumentSaver.cpp
@@ -1,259 +1,189 @@
/* This file is part of the KDE project
Copyright (C) 2004-2006 David Faure <faure@kde.org>
Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2010 Thomas Zander <zander@kde.org>
Copyright (C) 2011 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; 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 "KoEmbeddedDocumentSaver.h"
#include <QList>
#include <OdfDebug.h>
#include <QUrl>
#include <KoStore.h>
#include <KoXmlWriter.h>
#include <KoOdfWriteStore.h>
#include "KoDocumentBase.h"
#include <KoOdfManifestEntry.h>
#define INTERNAL_PROTOCOL "intern"
struct FileEntry {
QString path;
QByteArray mimeType; // QBA because this is what addManifestEntry wants
QByteArray contents;
};
class Q_DECL_HIDDEN KoEmbeddedDocumentSaver::Private
{
public:
Private() {}
QHash<QString, int> prefixes; // Used in getFilename();
// These will be saved when saveEmbeddedDocuments() is called.
- QList<KoDocumentBase*> documents; // Embedded documents
QList<FileEntry*> files; // Embedded files.
QList<KoOdfManifestEntry*> manifestEntries;
};
KoEmbeddedDocumentSaver::KoEmbeddedDocumentSaver()
: d(new Private())
{
}
KoEmbeddedDocumentSaver::~KoEmbeddedDocumentSaver()
{
qDeleteAll(d->files);
qDeleteAll(d->manifestEntries);
delete d;
}
QString KoEmbeddedDocumentSaver::getFilename(const QString &prefix)
{
int index = 1;
if (d->prefixes.contains(prefix)) {
index = d->prefixes.value(prefix);
}
// This inserts prefix into the map if it's not there.
d->prefixes[prefix] = index + 1;
//return prefix + QString("%1").arg(index, 4, 10, QChar('0'));
return prefix + QString("%1").arg(index);
}
-void KoEmbeddedDocumentSaver::embedDocument(KoXmlWriter &writer, KoDocumentBase * doc)
-{
- Q_ASSERT(doc);
- d->documents.append(doc);
-
- QString ref;
- if (!doc->isStoredExtern()) {
- const QString name = getFilename("Object ");
-
- // set URL in document so that saveEmbeddedDocuments will save
- // the actual embedded object with the right name in the store.
- QUrl u;
- u.setScheme(INTERNAL_PROTOCOL);
- u.setPath(name);
- debugOdf << u;
- doc->setUrl(u);
- ref = "./" + name;
- } else {
- ref = doc->url().url();
- }
-
- debugOdf << "saving reference to embedded document as" << ref;
- writer.addAttribute("xlink:href", /*"#" + */ref);
-
- //<draw:object xlink:type="simple" xlink:show="embed"
- // xlink:actuate="onLoad" xlink:href="#./Object 1"/>
- writer.addAttribute("xlink:type", "simple");
- writer.addAttribute("xlink:show", "embed");
- writer.addAttribute("xlink:actuate", "onLoad");
-
-}
// Examples:
// Videos/Video1.mov ← the number is autogenerated
// Videos/Video2.mov
// Object1/foo ← the number is autogenerated
// Object1/bar
// Note: The contents QByteArray is implicitly shared. It needs to be
// copied since otherwise the actual array may disappear before
// the real saving is done.
//
void KoEmbeddedDocumentSaver::embedFile(KoXmlWriter &writer, const char *element,
const QString &path, const QByteArray &mimeType,
const QByteArray &contents)
{
// Put the file in the list of files to be written to the store later.
FileEntry *entry = new FileEntry;
entry->mimeType = mimeType;
entry->path = path;
entry->contents = contents;
d->files.append(entry);
writer.startElement(element);
// Write the attributes that refer to the file.
//<draw:object xlink:href="#./Object 1" xlink:type="simple" xlink:show="embed"
// xlink:actuate="onLoad"/>
writer.addAttribute("xlink:type", "simple");
writer.addAttribute("xlink:show", "embed");
writer.addAttribute("xlink:actuate", "onLoad");
debugOdf << "saving reference to embedded file as" << path;
writer.addAttribute("xlink:href", path);
writer.endElement();
}
void KoEmbeddedDocumentSaver::saveFile(const QString &path, const QByteArray &mimeType,
const QByteArray &contents)
{
// Put the file in the list of files to be written to the store later.
FileEntry *entry = new FileEntry;
entry->mimeType = mimeType;
entry->path = path;
entry->contents = contents;
d->files.append(entry);
debugOdf << "saving reference to embedded file as" << path;
}
/**
*
*/
void KoEmbeddedDocumentSaver::saveManifestEntry(const QString &fullPath, const QString &mediaType,
const QString &version)
{
d->manifestEntries.append(new KoOdfManifestEntry(fullPath, mediaType, version));
}
bool KoEmbeddedDocumentSaver::saveEmbeddedDocuments(KoDocumentBase::SavingContext & documentContext)
{
- KoStore *store = documentContext.odfStore.store();
-
- // Write embedded documents.
- Q_FOREACH (KoDocumentBase *doc, d->documents) {
- QString path;
- if (doc->isStoredExtern()) {
- debugOdf << " external (don't save) url:" << doc->url().url();
- path = doc->url().url();
- } else {
- // The name comes from addEmbeddedDocument (which was set while saving the document).
- Q_ASSERT(doc->url().scheme() == INTERNAL_PROTOCOL);
- const QString name = doc->url().path();
- debugOdf << "saving" << name;
-
- // Embedded object doesn't support OpenDocument, save in the old format.
- debugOdf << "Embedded object doesn't support OpenDocument, save in the old format.";
-
- if (!doc->saveToStore(store, name)) {
- return false;
- }
-
-
- Q_ASSERT(doc->url().scheme() == INTERNAL_PROTOCOL);
- path = store->currentPath();
- if (!path.isEmpty()) {
- path += '/';
- }
- path += doc->url().path();
- if (path.startsWith(QLatin1Char('/'))) {
- path.remove(0, 1); // remove leading '/', no wanted in manifest
- }
- }
-
- // OOo uses a trailing slash for the path to embedded objects (== directories)
- if (!path.endsWith('/')) {
- path += '/';
- }
- documentContext.odfStore.manifestWriter()->addManifestEntry(path, "");
- }
+ KoStore *store = documentContext.odfStore.store();
// Write the embedded files.
Q_FOREACH (FileEntry *entry, d->files) {
QString path = entry->path;
debugOdf << "saving" << path;
// To make the children happy cd to the correct directory
store->pushDirectory();
int index = path.lastIndexOf('/');
const QString dirPath = path.left(index);
const QString fileName = path.right(path.size() - index - 1);
store->enterDirectory(dirPath);
if (!store->open(fileName)) {
return false;
}
store->write(entry->contents);
store->close();
// Now that we're done leave the directory again
store->popDirectory();
// Create the manifest entry.
if (path.startsWith(QLatin1String("./"))) {
path.remove(0, 2); // remove leading './', not wanted in manifest
}
documentContext.odfStore.manifestWriter()->addManifestEntry(path, entry->mimeType);
}
// Write the manifest entries.
KoXmlWriter *manifestWriter = documentContext.odfStore.manifestWriter();
Q_FOREACH (KoOdfManifestEntry *entry, d->manifestEntries) {
manifestWriter->startElement("manifest:file-entry");
manifestWriter->addAttribute("manifest:version", entry->version());
manifestWriter->addAttribute("manifest:media-type", entry->mediaType());
manifestWriter->addAttribute("manifest:full-path", entry->fullPath());
manifestWriter->endElement(); // manifest:file-entry
}
return true;
}
diff --git a/libs/odf/KoEmbeddedDocumentSaver.h b/libs/odf/KoEmbeddedDocumentSaver.h
index f0b0dd9310..015d37dfd1 100644
--- a/libs/odf/KoEmbeddedDocumentSaver.h
+++ b/libs/odf/KoEmbeddedDocumentSaver.h
@@ -1,99 +1,91 @@
/* This file is part of the KDE project
Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2011 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; 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 KOEMBEDDEDDOCUMENTSAVER_H
#define KOEMBEDDEDDOCUMENTSAVER_H
#include "KoDocumentBase.h"
#include "kritaodf_export.h"
#include <QString>
class KoXmlWriter;
/**
* This class is used to save embedded objects in ODF documents.
*
* @see KoEmbeddedFileSaver
*/
class KRITAODF_EXPORT KoEmbeddedDocumentSaver
{
public:
KoEmbeddedDocumentSaver();
~KoEmbeddedDocumentSaver();
/**
* Get a unique file name with the given prefix, to be used as a name for an embedded file in the ODF store.
* @param the prefix of the filename to be created.
* return a unique file name for use in the odf store.
*/
QString getFilename(const QString &prefix);
- /**
- * Adds the object specific attributes to the tag, but does NOT
- * write the content of the embedded document. Saving of the
- * embedded documents themselves is done in @ref save. This
- * function should be called from within KoDocumentBase::saveOdf.
- */
- void embedDocument(KoXmlWriter &writer, KoDocumentBase *doc);
-
/**
* Adds the object specific attributes to the tag, and queues the
* file for saving into the store.
*
* However, it does NOT write the content of the embedded document
* to the store. Saving of the embedded files themselves is done
* in @ref saveEmbeddedFiles. This function should be called from
* within saveOdf in a shape or a document.
*/
void embedFile(KoXmlWriter &writer, const char *element,
const QString &path, const QByteArray &mimeType,
const QByteArray &contents);
/**
* Queues the file for saving into the store.
*
* Saving of the embedded files themselves is done in @ref
* saveEmbeddedFiles. This function should be called from within
* saveOdf in a shape or a document if you don't wish to have a
* reference to the file within content.xml, e.g. when the file is
* part of an embedded object with embedded files within it.
*/
void saveFile(const QString &path, const QByteArray &mimeType,
const QByteArray &contents);
/**
*
*/
void saveManifestEntry(const QString &fullPath, const QString &mediaType,
const QString &version = QString());
/**
* Save all embedded documents to the store.
*/
bool saveEmbeddedDocuments(KoDocumentBase::SavingContext &documentContext);
private:
class Private;
Private * const d;
Q_DISABLE_COPY(KoEmbeddedDocumentSaver)
};
#endif /* KOEMBEDDEDDOCUMENTSAVER_H */
diff --git a/libs/pigment/KoColorSpace.cpp b/libs/pigment/KoColorSpace.cpp
index 2325277f55..634bbdb533 100644
--- a/libs/pigment/KoColorSpace.cpp
+++ b/libs/pigment/KoColorSpace.cpp
@@ -1,818 +1,821 @@
/*
* 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; 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 "KoColorSpace.h"
#include "KoColorSpace_p.h"
#include "KoChannelInfo.h"
#include "DebugPigment.h"
#include "KoCompositeOp.h"
#include "KoColorTransformation.h"
#include "KoColorTransformationFactory.h"
#include "KoColorTransformationFactoryRegistry.h"
#include "KoColorConversionCache.h"
#include "KoColorConversionSystem.h"
#include "KoColorSpaceRegistry.h"
#include "KoColorProfile.h"
#include "KoCopyColorConversionTransformation.h"
#include "KoFallBackColorTransformation.h"
#include "KoUniqueNumberForIdServer.h"
#include "KoMixColorsOp.h"
#include "KoConvolutionOp.h"
#include "KoCompositeOpRegistry.h"
#include "KoColorSpaceEngine.h"
#include <QThreadStorage>
#include <QByteArray>
#include <QBitArray>
#include <QPolygonF>
#include <QPointF>
#include <math.h>
KoColorSpace::KoColorSpace()
: d(new Private())
{
}
KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp)
: d(new Private())
{
d->id = id;
d->idNumber = KoUniqueNumberForIdServer::instance()->numberForId(d->id);
d->name = name;
d->mixColorsOp = mixColorsOp;
d->convolutionOp = convolutionOp;
d->transfoToRGBA16 = 0;
d->transfoFromRGBA16 = 0;
d->transfoToLABA16 = 0;
d->transfoFromLABA16 = 0;
d->gamutXYY = QPolygonF();
d->TRCXYY = QPolygonF();
d->colorants = QVector <qreal> (0);
d->lumaCoefficients = QVector <qreal> (0);
d->iccEngine = 0;
d->deletability = NotOwnedByRegistry;
}
KoColorSpace::~KoColorSpace()
{
Q_ASSERT(d->deletability != OwnedByRegistryDoNotDelete);
qDeleteAll(d->compositeOps);
Q_FOREACH (KoChannelInfo * channel, d->channels) {
delete channel;
}
if (d->deletability == NotOwnedByRegistry) {
KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache();
if (cache) {
cache->colorSpaceIsDestroyed(this);
}
}
delete d->mixColorsOp;
delete d->convolutionOp;
delete d->transfoToRGBA16;
delete d->transfoFromRGBA16;
delete d->transfoToLABA16;
delete d->transfoFromLABA16;
delete d;
}
bool KoColorSpace::operator==(const KoColorSpace& rhs) const
{
const KoColorProfile* p1 = rhs.profile();
const KoColorProfile* p2 = profile();
return d->idNumber == rhs.d->idNumber && ((p1 == p2) || (*p1 == *p2));
}
QString KoColorSpace::id() const
{
return d->id;
}
QString KoColorSpace::name() const
{
return d->name;
}
//Color space info stuff.
QPolygonF KoColorSpace::gamutXYY() const
{
if (d->gamutXYY.empty()) {
//now, let's decide on the boundary. This is a bit tricky because icc profiles can be both matrix-shaper and cLUT at once if the maker so pleases.
//first make a list of colors.
qreal max = 1.0;
if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") {
//boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general.
max = this->channels()[0]->getUIMax();
}
int samples = 5;//amount of samples in our color space.
QString name = KoColorSpaceRegistry::instance()->colorSpaceFactory("XYZAF32")->defaultProfile();
const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32", name);
quint8 *data = new quint8[pixelSize()];
quint8 data2[16]; // xyza f32 is 4 floats, that is 16 bytes per pixel.
//QVector <qreal> sampleCoordinates(pow(colorChannelCount(),samples));
//sampleCoordinates.fill(0.0);
// This is fixed to 5 since the maximum number of channels are 5 for CMYKA
QVector <float> channelValuesF(5);//for getting the coordinates.
for(int x=0;x<samples;x++){
if (colorChannelCount()==1) {//gray
channelValuesF[0]=(max/(samples-1))*(x);
channelValuesF[1]=max;
fromNormalisedChannelsValue(data, channelValuesF);
convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags());
xyzColorSpace->normalisedChannelsValue(data2, channelValuesF);
qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]);
qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]);
d->gamutXYY << QPointF(x,y);
} else {
for(int y=0;y<samples;y++){
for(int z=0;z<samples;z++){
if (colorChannelCount()==4) {
for(int k=0;k<samples;k++){
channelValuesF[0] = (max / (samples - 1)) * (x);
channelValuesF[1] = (max / (samples - 1)) * (y);
channelValuesF[2] = (max / (samples - 1)) * (z);
channelValuesF[3] = (max / (samples - 1)) * (k);
channelValuesF[4] = max;
fromNormalisedChannelsValue(data, channelValuesF);
convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags());
xyzColorSpace->normalisedChannelsValue(data2, channelValuesF);
qreal x = channelValuesF[0] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]);
qreal y = channelValuesF[1] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]);
d->gamutXYY<< QPointF(x,y);
}
} else {
channelValuesF[0]=(max/(samples-1))*(x);
channelValuesF[1]=(max/(samples-1))*(y);
channelValuesF[2]=(max/(samples-1))*(z);
channelValuesF[3]=max;
if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz.
fromNormalisedChannelsValue(data, channelValuesF);
convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags());
xyzColorSpace->normalisedChannelsValue(data2,channelValuesF);
}
qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]);
qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]);
d->gamutXYY<< QPointF(x,y);
}
}
}
}
}
delete[] data;
//if we ever implement a boundary-checking thing I'd add it here.
return d->gamutXYY;
} else {
return d->gamutXYY;
}
}
QPolygonF KoColorSpace::estimatedTRCXYY() const
{
if (d->TRCXYY.empty()){
qreal max = 1.0;
if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") {
//boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general.
max = this->channels()[0]->getUIMax();
}
QString name = KoColorSpaceRegistry::instance()->colorSpaceFactory("XYZAF16")->defaultProfile();
const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F16", name);
quint8 *data = new quint8[pixelSize()];
quint8 data2[8]; // xyza is 8 bytes per pixel.
// This is fixed to 5 since the maximum number of channels are 5 for CMYKA
QVector <float> channelValuesF(5);//for getting the coordinates.
for (quint32 i=0; i<colorChannelCount(); i++) {
qreal colorantY=1.0;
if (colorModelId().id()!="CMYKA") {
for (int j=5; j>0; j--){
channelValuesF.fill(0.0);
channelValuesF[i] = ((max/4)*(5-j));
if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz.
fromNormalisedChannelsValue(data, channelValuesF);
convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags());
xyzColorSpace->normalisedChannelsValue(data2,channelValuesF);
}
if (j==0) {
colorantY = channelValuesF[1];
if (d->colorants.size()<2){
d->colorants.resize(3*colorChannelCount());
d->colorants[i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]);
d->colorants[i+1]= channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]);
d->colorants[i+2]= channelValuesF[1];
}
}
d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/4)*(5-j)));
}
} else {
for (int j=0; j<5; j++){
channelValuesF.fill(0.0);
channelValuesF[i] = ((max/4)*(j));
fromNormalisedChannelsValue(data, channelValuesF);
convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags());
xyzColorSpace->normalisedChannelsValue(data2,channelValuesF);
if (j==0) {
colorantY = channelValuesF[1];
if (d->colorants.size()<2){
d->colorants.resize(3*colorChannelCount());
d->colorants[i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]);
d->colorants[i+1]= channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]);
d->colorants[i+2]= channelValuesF[1];
}
}
d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/4)*(j)));
}
}
}
delete[] data;
return d->TRCXYY;
} else {
return d->TRCXYY;
}
}
QVector <qreal> KoColorSpace::colorants() const
{
if (d->colorants.size()>1){
return d->colorants;
} else if (profile() && profile()->hasColorants()) {
d->colorants.resize(3*colorChannelCount());
d->colorants = profile()->getColorantsxyY();
return d->colorants;
} else {
estimatedTRCXYY();
return d->colorants;
}
}
QVector <qreal> KoColorSpace::lumaCoefficients() const
{
if (d->lumaCoefficients.size()>1){
return d->lumaCoefficients;
} else {
d->lumaCoefficients.resize(3);
if (colorModelId().id()!="RGBA") {
d->lumaCoefficients.fill(0.33);
} else {
colorants();
if (d->colorants[2]<0 || d->colorants[5]<0 || d->colorants[8]<0) {
d->lumaCoefficients[0]=0.2126;
d->lumaCoefficients[1]=0.7152;
d->lumaCoefficients[2]=0.0722;
} else {
d->lumaCoefficients[0]=d->colorants[2];
d->lumaCoefficients[1]=d->colorants[5];
d->lumaCoefficients[2]=d->colorants[8];
}
}
return d->lumaCoefficients;
}
}
QList<KoChannelInfo *> KoColorSpace::channels() const
{
return d->channels;
}
QBitArray KoColorSpace::channelFlags(bool color, bool alpha) const
{
QBitArray ba(d->channels.size());
if (!color && !alpha) return ba;
for (int i = 0; i < d->channels.size(); ++i) {
KoChannelInfo * channel = d->channels.at(i);
if ((color && channel->channelType() == KoChannelInfo::COLOR) ||
(alpha && channel->channelType() == KoChannelInfo::ALPHA))
ba.setBit(i, true);
}
return ba;
}
void KoColorSpace::addChannel(KoChannelInfo * ci)
{
d->channels.push_back(ci);
}
bool KoColorSpace::hasCompositeOp(const QString& id) const
{
return d->compositeOps.contains(id);
}
QList<KoCompositeOp*> KoColorSpace::compositeOps() const
{
return d->compositeOps.values();
}
KoMixColorsOp* KoColorSpace::mixColorsOp() const
{
return d->mixColorsOp;
}
KoConvolutionOp* KoColorSpace::convolutionOp() const
{
return d->convolutionOp;
}
const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const
{
const QHash<QString, KoCompositeOp*>::ConstIterator it = d->compositeOps.constFind(id);
if (it != d->compositeOps.constEnd()) {
return it.value();
}
else {
warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER;
return d->compositeOps.value(COMPOSITE_OVER);
}
}
void KoColorSpace::addCompositeOp(const KoCompositeOp * op)
{
if (op->colorSpace()->id() == id()) {
d->compositeOps.insert(op->id(), const_cast<KoCompositeOp*>(op));
}
}
const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const
{
if (!d->transfoToLABA16) {
d->transfoToLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ;
}
return d->transfoToLABA16;
}
const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const
{
if (!d->transfoFromLABA16) {
d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->lab16(), this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ;
}
return d->transfoFromLABA16;
}
const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const
{
if (!d->transfoToRGBA16) {
d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ;
}
return d->transfoToRGBA16;
}
const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const
{
if (!d->transfoFromRGBA16) {
d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->rgb16() , this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ;
}
return d->transfoFromRGBA16;
}
void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
toLabA16Converter()->transform(src, dst, nPixels);
}
void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
fromLabA16Converter()->transform(src, dst, nPixels);
}
void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
toRgbA16Converter()->transform(src, dst, nPixels);
}
void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
fromRgbA16Converter()->transform(src, dst, nPixels);
}
KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
if (*this == *dstColorSpace) {
return new KoCopyColorConversionTransformation(this);
} else {
return KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, dstColorSpace, renderingIntent, conversionFlags);
}
}
bool KoColorSpace::convertPixelsTo(const quint8 * src,
quint8 * dst,
const KoColorSpace * dstColorSpace,
quint32 numPixels,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
if (*this == *dstColorSpace) {
if (src != dst) {
memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize());
}
} else {
KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent, conversionFlags);
cct.transformation()->transform(src, dst, numPixels);
}
return true;
}
KoColorConversionTransformation * KoColorSpace::createProofingTransform(const KoColorSpace *dstColorSpace, const KoColorSpace *proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const
{
if (!d->iccEngine) {
d->iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
}
if (!d->iccEngine) return 0;
return d->iccEngine->createColorProofingTransformation(this, dstColorSpace, proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning, adaptationState);
}
bool KoColorSpace::proofPixelsTo(const quint8 *src,
quint8 *dst,
quint32 numPixels,
KoColorConversionTransformation *proofingTransform) const
{
proofingTransform->transform(src, dst, numPixels);
//the transform is deleted in the destructor.
return true;
}
void KoColorSpace::bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
Q_ASSERT_X(*op->colorSpace() == *this, "KoColorSpace::bitBlt", QString("Composite op is for color space %1 (%2) while this is %3 (%4)").arg(op->colorSpace()->id()).arg(op->colorSpace()->profile()->name()).arg(id()).arg(profile()->name()).toLatin1());
if(params.rows <= 0 || params.cols <= 0)
return;
if(!(*this == *srcSpace)) {
if (preferCompositionInSourceColorSpace() &&
srcSpace->hasCompositeOp(op->id())) {
quint32 conversionDstBufferStride = params.cols * srcSpace->pixelSize();
QVector<quint8> * conversionDstCache = threadLocalConversionCache(params.rows * conversionDstBufferStride);
quint8* conversionDstData = conversionDstCache->data();
for(qint32 row=0; row<params.rows; row++) {
convertPixelsTo(params.dstRowStart + row * params.dstRowStride,
conversionDstData + row * conversionDstBufferStride, srcSpace, params.cols,
renderingIntent, conversionFlags);
}
// FIXME: do not calculate the otherOp every time
const KoCompositeOp *otherOp = srcSpace->compositeOp(op->id());
KoCompositeOp::ParameterInfo paramInfo(params);
paramInfo.dstRowStart = conversionDstData;
paramInfo.dstRowStride = conversionDstBufferStride;
otherOp->composite(paramInfo);
for(qint32 row=0; row<params.rows; row++) {
srcSpace->convertPixelsTo(conversionDstData + row * conversionDstBufferStride,
params.dstRowStart + row * params.dstRowStride, this, params.cols,
renderingIntent, conversionFlags);
}
} else {
quint32 conversionBufferStride = params.cols * pixelSize();
QVector<quint8> * conversionCache = threadLocalConversionCache(params.rows * conversionBufferStride);
quint8* conversionData = conversionCache->data();
for(qint32 row=0; row<params.rows; row++) {
srcSpace->convertPixelsTo(params.srcRowStart + row * params.srcRowStride,
conversionData + row * conversionBufferStride, this, params.cols,
renderingIntent, conversionFlags);
}
KoCompositeOp::ParameterInfo paramInfo(params);
paramInfo.srcRowStart = conversionData;
paramInfo.srcRowStride = conversionBufferStride;
op->composite(paramInfo);
}
}
else {
op->composite(params);
}
}
QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
{
QVector<quint8> * ba = 0;
if (!d->conversionCache.hasLocalData()) {
ba = new QVector<quint8>(size, '0');
d->conversionCache.setLocalData(ba);
} else {
ba = d->conversionCache.localData();
if ((quint8)ba->size() < size)
ba->resize(size);
}
return ba;
}
KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash<QString, QVariant> & parameters) const
{
KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id);
if (!factory) return 0;
QPair<KoID, KoID> model(colorModelId(), colorDepthId());
QList< QPair<KoID, KoID> > models = factory->supportedModels();
if (models.isEmpty() || models.contains(model)) {
return factory->createTransformation(this, parameters);
} else {
// Find the best solution
// TODO use the color conversion cache
KoColorConversionTransformation* csToFallBack = 0;
KoColorConversionTransformation* fallBackToCs = 0;
KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverters(this, models, csToFallBack, fallBackToCs);
Q_ASSERT(csToFallBack);
Q_ASSERT(fallBackToCs);
KoColorTransformation* transfo = factory->createTransformation(fallBackToCs->srcColorSpace(), parameters);
return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo);
}
}
void KoColorSpace::increaseLuminosity(quint8 * pixel, qreal step) const{
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
if (profile()->hasTRC()){
//only linearise and crunch the luma if there's a TRC
profile()->linearizeFloatValue(channelValues);
qreal hue, sat, luma = 0.0;
toHSY(channelValues, &hue, &sat, &luma);
luma = pow(luma, 1/2.2);
luma = qMin(1.0, luma + step);
luma = pow(luma, 2.2);
channelValues = fromHSY(&hue, &sat, &luma);
profile()->delinearizeFloatValue(channelValues);
} else {
qreal hue, sat, luma = 0.0;
toHSY(channelValues, &hue, &sat, &luma);
luma = qMin(1.0, luma + step);
channelValues = fromHSY(&hue, &sat, &luma);
}
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
void KoColorSpace::decreaseLuminosity(quint8 * pixel, qreal step) const {
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
if (profile()->hasTRC()){
//only linearise and crunch the luma if there's a TRC
profile()->linearizeFloatValue(channelValues);
qreal hue, sat, luma = 0.0;
toHSY(channelValues, &hue, &sat, &luma);
luma = pow(luma, 1/2.2);
if (luma-step<0.0) {
luma=0.0;
} else {
luma -= step;
}
luma = pow(luma, 2.2);
channelValues = fromHSY(&hue, &sat, &luma);
profile()->delinearizeFloatValue(channelValues);
} else {
qreal hue, sat, luma = 0.0;
toHSY(channelValues, &hue, &sat, &luma);
if (luma-step<0.0) {
luma=0.0;
} else {
luma -= step;
}
channelValues = fromHSY(&hue, &sat, &luma);
}
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
void KoColorSpace::increaseSaturation(quint8 * pixel, qreal step) const{
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
profile()->linearizeFloatValue(channelValues);
qreal hue, sat, luma = 0.0;
toHSY(channelValues, &hue, &sat, &luma);
sat += step;
sat = qBound(0.0, sat, 1.0);
channelValues = fromHSY(&hue, &sat, &luma);
profile()->delinearizeFloatValue(channelValues);
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
void KoColorSpace::decreaseSaturation(quint8 * pixel, qreal step) const{
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
profile()->linearizeFloatValue(channelValues);
qreal hue, sat, luma = 0.0;
toHSY(channelValues, &hue, &sat, &luma);
sat -= step;
sat = qBound(0.0, sat, 1.0);
channelValues = fromHSY(&hue, &sat, &luma);
profile()->delinearizeFloatValue(channelValues);
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
void KoColorSpace::increaseHue(quint8 * pixel, qreal step) const{
int channelnumber = channelCount(); //doesn't work for cmyka...
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
profile()->linearizeFloatValue(channelValues);
qreal hue, sat, luma = 0.0;
toHSY(channelValues, &hue, &sat, &luma);
if (hue+step>1.0){
hue=(hue+step)- 1.0;
} else {
hue += step;
}
channelValues = fromHSY(&hue, &sat, &luma);
profile()->delinearizeFloatValue(channelValues);
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
void KoColorSpace::decreaseHue(quint8 * pixel, qreal step) const{
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
profile()->linearizeFloatValue(channelValues);
qreal hue, sat, luma = 0.0;
toHSY(channelValues, &hue, &sat, &luma);
if (hue-step<0.0){
hue=1.0-(step-hue);
} else {
hue -= step;
}
channelValues = fromHSY(&hue, &sat, &luma);
profile()->delinearizeFloatValue(channelValues);
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
void KoColorSpace::increaseRed(quint8 * pixel, qreal step) const{
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
profile()->linearizeFloatValue(channelValues);
qreal y, u, v = 0.0;
toYUV(channelValues, &y, &u, &v);
u += step;
u = qBound(0.0, u, 1.0);
channelValues = fromYUV(&y, &u, &v);
profile()->delinearizeFloatValue(channelValues);
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
void KoColorSpace::increaseGreen(quint8 * pixel, qreal step) const{
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
profile()->linearizeFloatValue(channelValues);
qreal y, u, v = 0.0;
toYUV(channelValues, &y, &u, &v);
u -= step;
u = qBound(0.0, u, 1.0);
channelValues = fromYUV(&y, &u, &v);
profile()->delinearizeFloatValue(channelValues);
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
+
void KoColorSpace::increaseBlue(quint8 * pixel, qreal step) const{
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
profile()->linearizeFloatValue(channelValues);
qreal y, u, v = 0.0;
toYUV(channelValues, &y, &u, &v);
v += step;
v = qBound(0.0, v, 1.0);
channelValues = fromYUV(&y, &u, &v);
profile()->delinearizeFloatValue(channelValues);
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
+
void KoColorSpace::increaseYellow(quint8 * pixel, qreal step) const{
int channelnumber = channelCount();
QVector <double> channelValues(channelnumber);
QVector <float> channelValuesF(channelnumber);
normalisedChannelsValue(pixel, channelValuesF);
for (int i=0;i<channelnumber;i++){
channelValues[i]=channelValuesF[i];
}
profile()->linearizeFloatValue(channelValues);
qreal y, u, v = 0.0;
toYUV(channelValues, &y, &u, &v);
v -= step;
v = qBound(0.0, v, 1.0);
channelValues = fromYUV(&y, &u, &v);
profile()->delinearizeFloatValue(channelValues);
for (int i=0;i<channelnumber;i++){
channelValuesF[i]=channelValues[i];
}
fromNormalisedChannelsValue(pixel, channelValuesF);
setOpacity(pixel, 1.0, 1);
}
+
QImage KoColorSpace::convertToQImage(const quint8 *data, qint32 width, qint32 height,
const KoColorProfile *dstProfile,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
QImage img = QImage(width, height, QImage::Format_ARGB32);
const KoColorSpace * dstCS = KoColorSpaceRegistry::instance()->rgb8(dstProfile);
if (data)
this->convertPixelsTo(const_cast<quint8 *>(data), img.bits(), dstCS, width * height, renderingIntent, conversionFlags);
return img;
}
bool KoColorSpace::preferCompositionInSourceColorSpace() const
{
return false;
}
diff --git a/libs/pigment/KoCompositeOpRegistry.cpp b/libs/pigment/KoCompositeOpRegistry.cpp
index 00b741f7b0..72fcf9772c 100644
--- a/libs/pigment/KoCompositeOpRegistry.cpp
+++ b/libs/pigment/KoCompositeOpRegistry.cpp
@@ -1,212 +1,214 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.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 "KoCompositeOpRegistry.h"
#include <QGlobalStatic>
#include <QList>
#include <klocalizedstring.h>
#include <KoID.h>
#include "KoCompositeOp.h"
#include "KoColorSpace.h"
Q_GLOBAL_STATIC(KoCompositeOpRegistry, registry)
KoCompositeOpRegistry::KoCompositeOpRegistry()
{
m_categories
<< KoID("arithmetic", i18n("Arithmetic"))
<< KoID("dark" , i18n("Darken"))
<< KoID("light" , i18n("Lighten"))
<< KoID("negative" , i18n("Negative"))
<< KoID("mix" , i18n("Mix"))
<< KoID("misc" , i18n("Misc"))
<< KoID("hsy" , i18n("HSY"))
<< KoID("hsi" , i18n("HSI"))
<< KoID("hsl" , i18n("HSL"))
<< KoID("hsv" , i18n("HSV"));
m_map.insert(m_categories[0], KoID(COMPOSITE_ADD , i18n("Addition")));
m_map.insert(m_categories[0], KoID(COMPOSITE_SUBTRACT , i18n("Subtract")));
m_map.insert(m_categories[0], KoID(COMPOSITE_MULT , i18n("Multiply")));
m_map.insert(m_categories[0], KoID(COMPOSITE_DIVIDE , i18n("Divide")));
m_map.insert(m_categories[0], KoID(COMPOSITE_INVERSE_SUBTRACT, i18n("Inverse Subtract")));
m_map.insert(m_categories[1], KoID(COMPOSITE_BURN , i18n("Burn")));
m_map.insert(m_categories[1], KoID(COMPOSITE_LINEAR_BURN, i18n("Linear Burn")));
m_map.insert(m_categories[1], KoID(COMPOSITE_DARKEN , i18n("Darken")));
m_map.insert(m_categories[1], KoID(COMPOSITE_GAMMA_DARK , i18n("Gamma Dark")));
m_map.insert(m_categories[1], KoID(COMPOSITE_DARKER_COLOR , i18n("Darker Color")));
m_map.insert(m_categories[2], KoID(COMPOSITE_DODGE , i18n("Color Dodge")));
m_map.insert(m_categories[2], KoID(COMPOSITE_LINEAR_DODGE, i18n("Linear Dodge")));
m_map.insert(m_categories[2], KoID(COMPOSITE_LIGHTEN , i18n("Lighten")));
m_map.insert(m_categories[2], KoID(COMPOSITE_LINEAR_LIGHT, i18n("Linear Light")));
m_map.insert(m_categories[2], KoID(COMPOSITE_SCREEN , i18n("Screen")));
m_map.insert(m_categories[2], KoID(COMPOSITE_PIN_LIGHT , i18n("Pin Light")));
m_map.insert(m_categories[2], KoID(COMPOSITE_VIVID_LIGHT , i18n("Vivid Light")));
m_map.insert(m_categories[2], KoID(COMPOSITE_HARD_LIGHT , i18n("Hard Light")));
m_map.insert(m_categories[2], KoID(COMPOSITE_SOFT_LIGHT_PHOTOSHOP, i18n("Soft Light (Photoshop)")));
m_map.insert(m_categories[2], KoID(COMPOSITE_SOFT_LIGHT_SVG, i18n("Soft Light (SVG)")));
m_map.insert(m_categories[2], KoID(COMPOSITE_GAMMA_LIGHT , i18n("Gamma Light")));
m_map.insert(m_categories[2], KoID(COMPOSITE_LIGHTER_COLOR , i18n("Lighter Color")));
m_map.insert(m_categories[3], KoID(COMPOSITE_DIFF , i18n("Difference")));
m_map.insert(m_categories[3], KoID(COMPOSITE_EQUIVALENCE , i18n("Equivalence")));
m_map.insert(m_categories[3], KoID(COMPOSITE_ADDITIVE_SUBTRACTIVE, i18n("Additive Subtractive")));
m_map.insert(m_categories[3], KoID(COMPOSITE_EXCLUSION , i18n("Exclusion")));
m_map.insert(m_categories[3], KoID(COMPOSITE_ARC_TANGENT , i18n("Arcus Tangent")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_OVER , i18n("Normal")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_BEHIND , i18n("Behind")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_GREATER , i18n("Greater")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_OVERLAY , i18n("Overlay")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_ERASE , i18n("Erase")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_ALPHA_DARKEN , i18n("Alpha Darken")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_HARD_MIX , i18n("Hard Mix")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_GRAIN_MERGE , i18n("Grain Merge")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_GRAIN_EXTRACT , i18n("Grain Extract")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_PARALLEL , i18n("Parallel")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_ALLANON , i18n("Allanon")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_GEOMETRIC_MEAN, i18n("Geometric Mean")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_OVER , i18n("Normal")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_BEHIND , i18n("Behind")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_GREATER , i18n("Greater")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_OVERLAY , i18n("Overlay")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_ERASE , i18n("Erase")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_ALPHA_DARKEN , i18n("Alpha Darken")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_HARD_MIX , i18n("Hard Mix")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_GRAIN_MERGE , i18n("Grain Merge")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_GRAIN_EXTRACT , i18n("Grain Extract")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_PARALLEL , i18n("Parallel")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_ALLANON , i18n("Allanon")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_GEOMETRIC_MEAN , i18n("Geometric Mean")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_DESTINATION_ATOP, i18n("Destination Atop")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_DESTINATION_IN , i18n("Destination In")));
m_map.insert(m_categories[5], KoID(COMPOSITE_BUMPMAP , i18n("Bumpmap")));
m_map.insert(m_categories[5], KoID(COMPOSITE_COMBINE_NORMAL, i18n("Combine Normal Map")));
m_map.insert(m_categories[5], KoID(COMPOSITE_DISSOLVE , i18n("Dissolve")));
m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_RED , i18n("Copy Red")));
m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_GREEN, i18n("Copy Green")));
m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_BLUE , i18n("Copy Blue")));
m_map.insert(m_categories[5], KoID(COMPOSITE_COPY , i18n("Copy")));
m_map.insert(m_categories[5], KoID(COMPOSITE_TANGENT_NORMALMAP, i18n("Tangent Normalmap")));
m_map.insert(m_categories[6], KoID(COMPOSITE_COLOR , i18n("Color")));
m_map.insert(m_categories[6], KoID(COMPOSITE_HUE , i18n("Hue")));
m_map.insert(m_categories[6], KoID(COMPOSITE_SATURATION , i18n("Saturation")));
m_map.insert(m_categories[6], KoID(COMPOSITE_LUMINIZE , i18n("Luminosity")));
m_map.insert(m_categories[6], KoID(COMPOSITE_DEC_SATURATION, i18n("Decrease Saturation")));
m_map.insert(m_categories[6], KoID(COMPOSITE_INC_SATURATION, i18n("Increase Saturation")));
m_map.insert(m_categories[6], KoID(COMPOSITE_DEC_LUMINOSITY, i18n("Decrease Luminosity")));
m_map.insert(m_categories[6], KoID(COMPOSITE_INC_LUMINOSITY, i18n("Increase Luminosity")));
m_map.insert(m_categories[7], KoID(COMPOSITE_COLOR_HSI , i18n("Color HSI")));
m_map.insert(m_categories[7], KoID(COMPOSITE_HUE_HSI , i18n("Hue HSI")));
m_map.insert(m_categories[7], KoID(COMPOSITE_SATURATION_HSI , i18n("Saturation HSI")));
m_map.insert(m_categories[7], KoID(COMPOSITE_INTENSITY , i18n("Intensity")));
m_map.insert(m_categories[7], KoID(COMPOSITE_DEC_SATURATION_HSI, i18n("Decrease Saturation HSI")));
m_map.insert(m_categories[7], KoID(COMPOSITE_INC_SATURATION_HSI, i18n("Increase Saturation HSI")));
m_map.insert(m_categories[7], KoID(COMPOSITE_DEC_INTENSITY , i18n("Decrease Intensity")));
m_map.insert(m_categories[7], KoID(COMPOSITE_INC_INTENSITY , i18n("Increase Intensity")));
m_map.insert(m_categories[8], KoID(COMPOSITE_COLOR_HSL , i18n("Color HSL")));
m_map.insert(m_categories[8], KoID(COMPOSITE_HUE_HSL , i18n("Hue HSL")));
m_map.insert(m_categories[8], KoID(COMPOSITE_SATURATION_HSL , i18n("Saturation HSL")));
m_map.insert(m_categories[8], KoID(COMPOSITE_LIGHTNESS , i18n("Lightness")));
m_map.insert(m_categories[8], KoID(COMPOSITE_DEC_SATURATION_HSL, i18n("Decrease Saturation HSL")));
m_map.insert(m_categories[8], KoID(COMPOSITE_INC_SATURATION_HSL, i18n("Increase Saturation HSL")));
m_map.insert(m_categories[8], KoID(COMPOSITE_DEC_LIGHTNESS , i18n("Decrease Lightness")));
m_map.insert(m_categories[8], KoID(COMPOSITE_INC_LIGHTNESS , i18n("Increase Lightness")));
m_map.insert(m_categories[9], KoID(COMPOSITE_COLOR_HSV , i18n("Color HSV")));
m_map.insert(m_categories[9], KoID(COMPOSITE_HUE_HSV , i18n("Hue HSV")));
m_map.insert(m_categories[9], KoID(COMPOSITE_SATURATION_HSV , i18n("Saturation HSV")));
m_map.insert(m_categories[9], KoID(COMPOSITE_VALUE , i18n("Value")));
m_map.insert(m_categories[9], KoID(COMPOSITE_DEC_SATURATION_HSV, i18n("Decrease Saturation HSV")));
m_map.insert(m_categories[9], KoID(COMPOSITE_INC_SATURATION_HSV, i18n("Increase Saturation HSV")));
m_map.insert(m_categories[9], KoID(COMPOSITE_DEC_VALUE , i18n("Decrease Value")));
m_map.insert(m_categories[9], KoID(COMPOSITE_INC_VALUE , i18n("Increase Value")));
}
const KoCompositeOpRegistry& KoCompositeOpRegistry::instance()
{
return *registry;
}
KoID KoCompositeOpRegistry::getDefaultCompositeOp() const
{
return KoID(COMPOSITE_OVER, i18n("Normal"));
}
KoID KoCompositeOpRegistry::getKoID(const QString& compositeOpID) const
{
KoIDMap::const_iterator itr = qFind(m_map.begin(), m_map.end(), KoID(compositeOpID));
return (itr != m_map.end()) ? *itr : KoID();
}
KoCompositeOpRegistry::KoIDMap KoCompositeOpRegistry::getCompositeOps() const
{
return m_map;
}
KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCategories() const
{
return m_categories;
}
KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCompositeOps(const KoID& category, const KoColorSpace* colorSpace) const
{
qint32 num = m_map.count(category);
KoIDMap::const_iterator beg = m_map.find(category);
KoIDMap::const_iterator end = beg + num;
KoIDList list;
list.reserve(num);
if(colorSpace) {
for(; beg!=end; ++beg){
if(colorSpace->hasCompositeOp(beg->id()))
list.push_back(*beg);
}
}
else {
for(; beg!=end; ++beg)
list.push_back(*beg);
}
return list;
}
KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCompositeOps(const KoColorSpace* colorSpace) const
{
KoIDMap::const_iterator beg = m_map.begin();
KoIDMap::const_iterator end = m_map.end();
KoIDList list;
list.reserve(m_map.size());
if(colorSpace) {
for(; beg!=end; ++beg){
if(colorSpace->hasCompositeOp(beg->id()))
list.push_back(*beg);
}
}
else {
for(; beg!=end; ++beg)
list.push_back(*beg);
}
return list;
}
bool KoCompositeOpRegistry::colorSpaceHasCompositeOp(const KoColorSpace* colorSpace, const KoID& compositeOp) const
{
return colorSpace ? colorSpace->hasCompositeOp(compositeOp.id()) : false;
}
diff --git a/libs/pigment/KoCompositeOpRegistry.h b/libs/pigment/KoCompositeOpRegistry.h
index bc9b610bb7..5461712840 100644
--- a/libs/pigment/KoCompositeOpRegistry.h
+++ b/libs/pigment/KoCompositeOpRegistry.h
@@ -1,173 +1,175 @@
/*
* 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 <QString>
#include <QList>
#include <QMultiMap>
#include <QBitArray>
#include "kritapigment_export.h"
class KoColorSpace;
#include <KoID.h>
// TODO : convert this data blob into a modern design with an enum class.
// This will reduce the need for runtime string comparisons.
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_DESTINATION_IN = "destination-in";
+const QString COMPOSITE_DESTINATION_ATOP = "destination-atop";
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_GREATER = "greater";
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 KRITAPIGMENT_EXPORT KoCompositeOpRegistry
{
typedef QMultiMap<KoID,KoID> KoIDMap;
typedef QList<KoID> KoIDList;
public:
KoCompositeOpRegistry();
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/KoMixColorsOp.h b/libs/pigment/KoMixColorsOp.h
index a82b05988f..799be0276f 100644
--- a/libs/pigment/KoMixColorsOp.h
+++ b/libs/pigment/KoMixColorsOp.h
@@ -1,61 +1,81 @@
/*
* Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2006-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 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_MIX_COLORS_OP_H
#define KO_MIX_COLORS_OP_H
#include <limits.h>
/**
* Base class of the mix color operation. It's defined by
* sum(colors[i] * weights[i]) / 255. You access the KoMixColorsOp
* of a colorspace by calling KoColorSpace::mixColorsOp.
*/
class KoMixColorsOp
{
public:
virtual ~KoMixColorsOp() { }
/**
* Mix the colors.
* @param colors a pointer toward the source pixels
* @param weights the coeffient of the source pixels (if you want
* to average the sum of weights must be equal to 255)
* @param nColors the number of pixels in the colors array
* @param dst the destination pixel
*
* @code
* quint8* colors[nColors];
* colors[0] = ptrToFirstPixel;
* colors[1] = ptrToSecondPixel;
* ...
* colors[nColors-1] = ptrToLastPixel;
* qint16 weights[nColors];
* weights[0] = firstWeight;
* weights[1] = secondWeight;
* ...
* weights[nColors-1] = lastWeight;
*
* mixColors(colors, weights, nColors, ptrToDestinationPixel);
* @endcode
*/
virtual void mixColors(const quint8 * const*colors, const qint16 *weights, quint32 nColors, quint8 *dst) const = 0;
virtual void mixColors(const quint8 *colors, const qint16 *weights, quint32 nColors, quint8 *dst) const = 0;
+
+
+ /**
+ * Mix the colors uniformly, without weightening
+ * @param colors a pointer toward the source pixels
+ * @param nColors the number of pixels in the colors array
+ * @param dst the destination pixel
+ *
+ * @code
+ * quint8* colors[nColors];
+ * colors[0] = ptrToFirstPixel;
+ * colors[1] = ptrToSecondPixel;
+ * ...
+ * colors[nColors-1] = ptrToLastPixel;
+ *
+ * mixColors(colors, nColors, ptrToDestinationPixel);
+ * @endcode
+ */
+ virtual void mixColors(const quint8 * const*colors, quint32 nColors, quint8 *dst) const = 0;
+ virtual void mixColors(const quint8 *colors, quint32 nColors, quint8 *dst) const = 0;
};
#endif
diff --git a/libs/pigment/KoMixColorsOpImpl.h b/libs/pigment/KoMixColorsOpImpl.h
index e600022bbf..1dddc9de33 100644
--- a/libs/pigment/KoMixColorsOpImpl.h
+++ b/libs/pigment/KoMixColorsOpImpl.h
@@ -1,149 +1,205 @@
/*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2007 Emanuele Tamponi <emanuele@valinor.it>
*
* 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 KOMIXCOLORSOPIMPL_H
#define KOMIXCOLORSOPIMPL_H
#include "KoMixColorsOp.h"
template<class _CSTrait>
class KoMixColorsOpImpl : public KoMixColorsOp
{
public:
KoMixColorsOpImpl() {
}
virtual ~KoMixColorsOpImpl() { }
- virtual void mixColors(const quint8 * const* colors, const qint16 *weights, quint32 nColors, quint8 *dst) const {
- mixColorsImpl(ArrayOfPointers(colors), weights, nColors, dst);
+ virtual void mixColors(const quint8 * const* colors, const qint16 *weights, quint32 nColors, quint8 *dst) const override {
+ mixColorsImpl(ArrayOfPointers(colors), WeightsWrapper(weights), nColors, dst);
}
- virtual void mixColors(const quint8 *colors, const qint16 *weights, quint32 nColors, quint8 *dst) const {
- mixColorsImpl(PointerToArray(colors, _CSTrait::pixelSize), weights, nColors, dst);
+ virtual void mixColors(const quint8 *colors, const qint16 *weights, quint32 nColors, quint8 *dst) const override {
+ mixColorsImpl(PointerToArray(colors, _CSTrait::pixelSize), WeightsWrapper(weights), nColors, dst);
+ }
+
+ virtual void mixColors(const quint8 * const* colors, quint32 nColors, quint8 *dst) const override {
+ mixColorsImpl(ArrayOfPointers(colors), NoWeightsSurrogate(nColors), nColors, dst);
+ }
+
+ virtual void mixColors(const quint8 *colors, quint32 nColors, quint8 *dst) const override {
+ mixColorsImpl(PointerToArray(colors, _CSTrait::pixelSize), NoWeightsSurrogate(nColors), nColors, dst);
}
private:
struct ArrayOfPointers {
ArrayOfPointers(const quint8 * const* colors)
: m_colors(colors)
{
}
const quint8* getPixel() const {
return *m_colors;
}
void nextPixel() {
m_colors++;
}
private:
const quint8 * const * m_colors;
};
struct PointerToArray {
PointerToArray(const quint8 *colors, int pixelSize)
: m_colors(colors),
m_pixelSize(pixelSize)
{
}
const quint8* getPixel() const {
return m_colors;
}
void nextPixel() {
m_colors += m_pixelSize;
}
private:
const quint8 *m_colors;
const int m_pixelSize;
};
+ struct WeightsWrapper
+ {
+ typedef typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype compositetype;
+
+ WeightsWrapper(const qint16 *weights)
+ : m_weights(weights)
+ {
+ }
+
+ inline void nextPixel() {
+ m_weights++;
+ }
+
+ inline void premultiplyAlphaWithWeight(compositetype &alpha) const {
+ alpha *= *m_weights;
+ }
+
+ inline int normalizeFactor() const {
+ return 255;
+ }
+
+ private:
+ const qint16 *m_weights;
+ };
+
+ struct NoWeightsSurrogate
+ {
+ typedef typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype compositetype;
+
+ NoWeightsSurrogate(int numPixels)
+ : m_numPixles(numPixels)
+ {
+ }
+
+ inline void nextPixel() {
+ }
- template<class AbstractSource>
- void mixColorsImpl(AbstractSource source, const qint16 *weights, quint32 nColors, quint8 *dst) const {
+ inline void premultiplyAlphaWithWeight(compositetype &) const {
+ }
+
+ inline int normalizeFactor() const {
+ return m_numPixles;
+ }
+
+ private:
+ const int m_numPixles;
+ };
+
+ template<class AbstractSource, class WeightsWrapper>
+ void mixColorsImpl(AbstractSource source, WeightsWrapper weightsWrapper, quint32 nColors, quint8 *dst) const {
// Create and initialize to 0 the array of totals
typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype totals[_CSTrait::channels_nb];
typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype totalAlpha = 0;
memset(totals, 0, sizeof(totals));
// Compute the total for each channel by summing each colors multiplied by the weightlabcache
while (nColors--) {
const typename _CSTrait::channels_type* color = _CSTrait::nativeArray(source.getPixel());
typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype alphaTimesWeight;
if (_CSTrait::alpha_pos != -1) {
alphaTimesWeight = color[_CSTrait::alpha_pos];
} else {
alphaTimesWeight = KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::unitValue;
}
- alphaTimesWeight *= *weights;
+ weightsWrapper.premultiplyAlphaWithWeight(alphaTimesWeight);
for (int i = 0; i < (int)_CSTrait::channels_nb; i++) {
if (i != _CSTrait::alpha_pos) {
totals[i] += color[i] * alphaTimesWeight;
}
}
totalAlpha += alphaTimesWeight;
source.nextPixel();
- weights++;
+ weightsWrapper.nextPixel();
}
// set totalAlpha to the minimum between its value and the unit value of the channels
- const int sumOfWeights = 255;
+ const int sumOfWeights = weightsWrapper.normalizeFactor();
if (totalAlpha > KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::unitValue * sumOfWeights) {
totalAlpha = KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::unitValue * sumOfWeights;
}
typename _CSTrait::channels_type* dstColor = _CSTrait::nativeArray(dst);
if (totalAlpha > 0) {
for (int i = 0; i < (int)_CSTrait::channels_nb; i++) {
if (i != _CSTrait::alpha_pos) {
typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype v = totals[i] / totalAlpha;
if (v > KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::max) {
v = KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::max;
}
if (v < KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::min) {
v = KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::min;
}
dstColor[ i ] = v;
}
}
if (_CSTrait::alpha_pos != -1) {
dstColor[ _CSTrait::alpha_pos ] = totalAlpha / sumOfWeights;
}
} else {
memset(dst, 0, sizeof(typename _CSTrait::channels_type) * _CSTrait::channels_nb);
}
}
+
};
#endif
diff --git a/libs/pigment/compositeops/KoCompositeOpDestinationAtop.h b/libs/pigment/compositeops/KoCompositeOpDestinationAtop.h
new file mode 100644
index 0000000000..685acf56c3
--- /dev/null
+++ b/libs/pigment/compositeops/KoCompositeOpDestinationAtop.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
+ * Copyright (c) 2012 José Luis Vergara <pentalis@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.
+*/
+
+#ifndef _KOCOMPOSITEOPDESTINATIONATOP_H_
+#define _KOCOMPOSITEOPDESTINATIONATOP_H_
+
+#include "KoCompositeOpBase.h"
+
+/**
+ * Generic implementation of the Destination-atop composite op, based off the behind composite op.
+ * This is necessary for Open Raster support.
+ * https://www.w3.org/TR/compositing-1/
+ */
+template<class CS_Traits>
+class KoCompositeOpDestinationAtop : public KoCompositeOpBase<CS_Traits, KoCompositeOpDestinationAtop<CS_Traits> >
+{
+ typedef KoCompositeOpBase<CS_Traits, KoCompositeOpDestinationAtop<CS_Traits> > base_class;
+ typedef typename CS_Traits::channels_type channels_type;
+
+ static const qint8 channels_nb = CS_Traits::channels_nb;
+ static const qint8 alpha_pos = CS_Traits::alpha_pos;
+
+public:
+ KoCompositeOpDestinationAtop(const KoColorSpace * cs)
+ : base_class(cs, COMPOSITE_DESTINATION_ATOP, i18n("Destination Atop"), KoCompositeOp::categoryMix()) { }
+
+public:
+ template<bool alphaLocked, bool allChannelFlags>
+ inline static channels_type composeColorChannels(const channels_type* src, channels_type srcAlpha,
+ channels_type* dst, channels_type dstAlpha,
+ channels_type maskAlpha, channels_type opacity,
+ const QBitArray& channelFlags ) {
+ using namespace Arithmetic;
+
+ channels_type appliedAlpha = mul(maskAlpha, srcAlpha, opacity);
+
+ channels_type newDstAlpha = appliedAlpha;
+
+ if (dstAlpha != zeroValue<channels_type>() && srcAlpha != zeroValue<channels_type>()) {
+ // blend the color channels as if we were painting on the layer below
+ for (qint8 channel = 0; channel < channels_nb; ++channel)
+ if(channel != alpha_pos && (allChannelFlags || channelFlags.testBit(channel))) {
+ /*each color blended in proportion to their calculated opacity*/
+ channels_type srcMult= mul(src[channel], appliedAlpha);
+ dst[channel] = lerp(srcMult,dst[channel],dstAlpha);
+ }
+ }
+ else if (srcAlpha != zeroValue<channels_type>()) {
+ // don't blend if the color of the destination is undefined (has zero opacity)
+ // copy the source channel instead
+ for (qint8 channel = 0; channel < channels_nb; ++channel)
+ if(channel != alpha_pos && (allChannelFlags || channelFlags.testBit(channel)))
+ dst[channel] = src[channel];
+ }
+
+ return newDstAlpha;
+ }
+};
+
+#endif // _KOCOMPOSITEOPDESTINATIONATOP_H_
diff --git a/libs/pigment/compositeops/KoCompositeOpDestinationIn.h b/libs/pigment/compositeops/KoCompositeOpDestinationIn.h
new file mode 100644
index 0000000000..49ba88a7ae
--- /dev/null
+++ b/libs/pigment/compositeops/KoCompositeOpDestinationIn.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
+ * Copyright (c) 2012 José Luis Vergara <pentalis@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.
+*/
+
+#ifndef _KOCOMPOSITEOPDESTINATIONIN_H_
+#define _KOCOMPOSITEOPDESTINATIONIN_H_
+
+#include "KoCompositeOpBase.h"
+
+/**
+ * Generic implementation of the Destination-in composite op, based off the behind composite op.
+ * This is necessary for Open Raster support.
+ * https://www.w3.org/TR/compositing-1/
+ */
+template<class CS_Traits>
+class KoCompositeOpDestinationIn : public KoCompositeOpBase<CS_Traits, KoCompositeOpDestinationIn<CS_Traits> >
+{
+ typedef KoCompositeOpBase<CS_Traits, KoCompositeOpDestinationIn<CS_Traits> > base_class;
+ typedef typename CS_Traits::channels_type channels_type;
+
+ static const qint8 channels_nb = CS_Traits::channels_nb;
+ static const qint8 alpha_pos = CS_Traits::alpha_pos;
+
+public:
+ KoCompositeOpDestinationIn(const KoColorSpace * cs)
+ : base_class(cs, COMPOSITE_DESTINATION_IN, i18n("Destination In"), KoCompositeOp::categoryMix()) { }
+
+public:
+ template<bool alphaLocked, bool allChannelFlags>
+ inline static channels_type composeColorChannels(const channels_type* src, channels_type srcAlpha,
+ channels_type* dst, channels_type dstAlpha,
+ channels_type maskAlpha, channels_type opacity,
+ const QBitArray& channelFlags ) {
+ using namespace Arithmetic;
+ Q_UNUSED(src);
+ Q_UNUSED(dst);
+ Q_UNUSED(channelFlags);
+
+ channels_type appliedAlpha = mul(maskAlpha, srcAlpha, opacity);
+
+ channels_type newDstAlpha = mul(dstAlpha, appliedAlpha);
+
+ return newDstAlpha;
+ }
+};
+
+#endif // _KOCOMPOSITEOPDESTINATIONIN_H_
diff --git a/libs/pigment/compositeops/KoCompositeOps.h b/libs/pigment/compositeops/KoCompositeOps.h
index e5fa0a5f44..fb0f4ea771 100644
--- a/libs/pigment/compositeops/KoCompositeOps.h
+++ b/libs/pigment/compositeops/KoCompositeOps.h
@@ -1,243 +1,247 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
* 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 _KOCOMPOSITEOPS_H_
#define _KOCOMPOSITEOPS_H_
#include <boost/type_traits.hpp>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorSpaceMaths.h>
#include "compositeops/KoCompositeOpGeneric.h"
#include "compositeops/KoCompositeOpOver.h"
#include "compositeops/KoCompositeOpCopyChannel.h"
#include "compositeops/KoCompositeOpAlphaDarken.h"
#include "compositeops/KoCompositeOpErase.h"
#include "compositeops/KoCompositeOpCopy2.h"
#include "compositeops/KoCompositeOpDissolve.h"
#include "compositeops/KoCompositeOpBehind.h"
+#include "compositeops/KoCompositeOpDestinationIn.h"
+#include "compositeops/KoCompositeOpDestinationAtop.h"
#include "compositeops/KoCompositeOpGreater.h"
#include "KoOptimizedCompositeOpFactory.h"
namespace _Private {
template<class Traits, bool flag>
struct AddGeneralOps
{
static void add(KoColorSpace* cs) { Q_UNUSED(cs); }
};
template<class Traits>
struct OptimizedOpsSelector
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return new KoCompositeOpAlphaDarken<Traits>(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return new KoCompositeOpOver<Traits>(cs);
}
};
template<>
struct OptimizedOpsSelector<KoBgrU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp32(cs);
}
};
template<>
struct OptimizedOpsSelector<KoLabU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp32(cs);
}
};
template<>
struct OptimizedOpsSelector<KoRgbF32Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return new KoCompositeOpAlphaDarken<KoRgbF32Traits>(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp128(cs);
}
};
template<class Traits>
struct AddGeneralOps<Traits, true>
{
typedef typename Traits::channels_type Arg;
typedef Arg (*CompositeFunc)(Arg, Arg);
static const qint32 alpha_pos = Traits::alpha_pos;
template<CompositeFunc func>
static void add(KoColorSpace* cs, const QString& id, const QString& description, const QString& category) {
cs->addCompositeOp(new KoCompositeOpGenericSC<Traits, func>(cs, id, description, category));
}
static void add(KoColorSpace* cs) {
cs->addCompositeOp(OptimizedOpsSelector<Traits>::createOverOp(cs));
cs->addCompositeOp(OptimizedOpsSelector<Traits>::createAlphaDarkenOp(cs));
cs->addCompositeOp(new KoCompositeOpCopy2<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpErase<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpBehind<Traits>(cs));
+ cs->addCompositeOp(new KoCompositeOpDestinationIn<Traits>(cs));
+ cs->addCompositeOp(new KoCompositeOpDestinationAtop<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpGreater<Traits>(cs));
add<&cfOverlay<Arg> >(cs, COMPOSITE_OVERLAY , i18n("Overlay") , KoCompositeOp::categoryMix());
add<&cfGrainMerge<Arg> >(cs, COMPOSITE_GRAIN_MERGE , i18n("Grain Merge") , KoCompositeOp::categoryMix());
add<&cfGrainExtract<Arg> >(cs, COMPOSITE_GRAIN_EXTRACT , i18n("Grain Extract") , KoCompositeOp::categoryMix());
add<&cfHardMix<Arg> >(cs, COMPOSITE_HARD_MIX , i18n("Hard Mix") , KoCompositeOp::categoryMix());
add<&cfGeometricMean<Arg> >(cs, COMPOSITE_GEOMETRIC_MEAN, i18n("Geometric Mean"), KoCompositeOp::categoryMix());
add<&cfParallel<Arg> >(cs, COMPOSITE_PARALLEL , i18n("Parallel") , KoCompositeOp::categoryMix());
add<&cfAllanon<Arg> >(cs, COMPOSITE_ALLANON , i18n("Allanon") , KoCompositeOp::categoryMix());
add<&cfScreen<Arg> >(cs, COMPOSITE_SCREEN , i18n("Screen") , KoCompositeOp::categoryLight());
add<&cfColorDodge<Arg> >(cs, COMPOSITE_DODGE , i18n("Color Dodge") , KoCompositeOp::categoryLight());
add<&cfAddition<Arg> >(cs, COMPOSITE_LINEAR_DODGE, i18n("Linear Dodge"), KoCompositeOp::categoryLight());
add<&cfLightenOnly<Arg> >(cs, COMPOSITE_LIGHTEN , i18n("Lighten") , KoCompositeOp::categoryLight());
add<&cfHardLight<Arg> >(cs, COMPOSITE_HARD_LIGHT , i18n("Hard Light") , KoCompositeOp::categoryLight());
add<&cfSoftLightSvg<Arg> >(cs, COMPOSITE_SOFT_LIGHT_SVG, i18n("Soft Light (SVG)") , KoCompositeOp::categoryLight());
add<&cfSoftLight<Arg> >(cs, COMPOSITE_SOFT_LIGHT_PHOTOSHOP, i18n("Soft Light (Photoshop)") , KoCompositeOp::categoryLight());
add<&cfGammaLight<Arg> >(cs, COMPOSITE_GAMMA_LIGHT , i18n("Gamma Light") , KoCompositeOp::categoryLight());
add<&cfVividLight<Arg> >(cs, COMPOSITE_VIVID_LIGHT , i18n("Vivid Light") , KoCompositeOp::categoryLight());
add<&cfPinLight<Arg> >(cs, COMPOSITE_PIN_LIGHT , i18n("Pin Light") , KoCompositeOp::categoryLight());
add<&cfLinearLight<Arg> >(cs, COMPOSITE_LINEAR_LIGHT, i18n("Linear Light"), KoCompositeOp::categoryLight());
add<&cfColorBurn<Arg> >(cs, COMPOSITE_BURN , i18n("Color Burn") , KoCompositeOp::categoryDark());
add<&cfLinearBurn<Arg> >(cs, COMPOSITE_LINEAR_BURN , i18n("Linear Burn"), KoCompositeOp::categoryDark());
add<&cfDarkenOnly<Arg> >(cs, COMPOSITE_DARKEN , i18n("Darken") , KoCompositeOp::categoryDark());
add<&cfGammaDark<Arg> >(cs, COMPOSITE_GAMMA_DARK , i18n("Gamma Dark") , KoCompositeOp::categoryDark());
add<&cfAddition<Arg> >(cs, COMPOSITE_ADD , i18n("Addition") , KoCompositeOp::categoryArithmetic());
add<&cfSubtract<Arg> >(cs, COMPOSITE_SUBTRACT , i18n("Subtract") , KoCompositeOp::categoryArithmetic());
add<&cfInverseSubtract<Arg> >(cs, COMPOSITE_INVERSE_SUBTRACT, i18n("Inversed-Subtract"), KoCompositeOp::categoryArithmetic());
add<&cfMultiply<Arg> >(cs, COMPOSITE_MULT , i18n("Multiply") , KoCompositeOp::categoryArithmetic());
add<&cfDivide<Arg> >(cs, COMPOSITE_DIVIDE , i18n("Divide") , KoCompositeOp::categoryArithmetic());
add<&cfArcTangent<Arg> >(cs, COMPOSITE_ARC_TANGENT , i18n("Arcus Tangent") , KoCompositeOp::categoryNegative());
add<&cfDifference<Arg> >(cs, COMPOSITE_DIFF , i18n("Difference") , KoCompositeOp::categoryNegative());
add<&cfExclusion<Arg> >(cs, COMPOSITE_EXCLUSION , i18n("Exclusion") , KoCompositeOp::categoryNegative());
add<&cfEquivalence<Arg> >(cs, COMPOSITE_EQUIVALENCE , i18n("Equivalence") , KoCompositeOp::categoryNegative());
add<&cfAdditiveSubtractive<Arg> >(cs, COMPOSITE_ADDITIVE_SUBTRACTIVE , i18n("Additive-Subtractive") , KoCompositeOp::categoryNegative());
cs->addCompositeOp(new KoCompositeOpDissolve<Traits>(cs, KoCompositeOp::categoryMisc()));
}
};
template<class Traits, bool flag>
struct AddRGBOps
{
static void add(KoColorSpace* cs) { Q_UNUSED(cs); }
};
template<class Traits>
struct AddRGBOps<Traits, true>
{
typedef float Arg;
static const qint32 red_pos = Traits::red_pos;
static const qint32 green_pos = Traits::green_pos;
static const qint32 blue_pos = Traits::blue_pos;
template<void compositeFunc(Arg, Arg, Arg, Arg&, Arg&, Arg&)>
static void add(KoColorSpace* cs, const QString& id, const QString& description, const QString& category) {
cs->addCompositeOp(new KoCompositeOpGenericHSL<Traits, compositeFunc>(cs, id, description, category));
}
static void add(KoColorSpace* cs) {
cs->addCompositeOp(new KoCompositeOpCopyChannel<Traits,red_pos >(cs, COMPOSITE_COPY_RED , i18n("Copy Red") , KoCompositeOp::categoryMisc()));
cs->addCompositeOp(new KoCompositeOpCopyChannel<Traits,green_pos>(cs, COMPOSITE_COPY_GREEN, i18n("Copy Green"), KoCompositeOp::categoryMisc()));
cs->addCompositeOp(new KoCompositeOpCopyChannel<Traits,blue_pos >(cs, COMPOSITE_COPY_BLUE , i18n("Copy Blue") , KoCompositeOp::categoryMisc()));
add<&cfTangentNormalmap <HSYType,Arg> >(cs, COMPOSITE_TANGENT_NORMALMAP , i18n("Tangent Normalmap") , KoCompositeOp::categoryMisc());
add<&cfReorientedNormalMapCombine <HSYType, Arg> >(cs, COMPOSITE_COMBINE_NORMAL, i18n("Combine Normal Maps"), KoCompositeOp::categoryMisc());
add<&cfColor <HSYType,Arg> >(cs, COMPOSITE_COLOR , i18n("Color") , KoCompositeOp::categoryHSY());
add<&cfHue <HSYType,Arg> >(cs, COMPOSITE_HUE , i18n("Hue") , KoCompositeOp::categoryHSY());
add<&cfSaturation <HSYType,Arg> >(cs, COMPOSITE_SATURATION , i18n("Saturation") , KoCompositeOp::categoryHSY());
add<&cfIncreaseSaturation<HSYType,Arg> >(cs, COMPOSITE_INC_SATURATION, i18n("Increase Saturation"), KoCompositeOp::categoryHSY());
add<&cfDecreaseSaturation<HSYType,Arg> >(cs, COMPOSITE_DEC_SATURATION, i18n("Decrease Saturation"), KoCompositeOp::categoryHSY());
add<&cfLightness <HSYType,Arg> >(cs, COMPOSITE_LUMINIZE , i18n("Luminosity") , KoCompositeOp::categoryHSY());
add<&cfIncreaseLightness <HSYType,Arg> >(cs, COMPOSITE_INC_LUMINOSITY, i18n("Increase Luminosity"), KoCompositeOp::categoryHSY());
add<&cfDecreaseLightness <HSYType,Arg> >(cs, COMPOSITE_DEC_LUMINOSITY, i18n("Decrease Luminosity"), KoCompositeOp::categoryHSY());
add<&cfDarkerColor <HSYType,Arg> >(cs, COMPOSITE_DARKER_COLOR, i18n("Darker Color"), KoCompositeOp::categoryDark());//darker color as PSD does it//
add<&cfLighterColor <HSYType,Arg> >(cs, COMPOSITE_LIGHTER_COLOR, i18n("Lighter Color"), KoCompositeOp::categoryLight());//lighter color as PSD does it//
add<&cfColor <HSIType,Arg> >(cs, COMPOSITE_COLOR_HSI , i18n("Color HSI") , KoCompositeOp::categoryHSI());
add<&cfHue <HSIType,Arg> >(cs, COMPOSITE_HUE_HSI , i18n("Hue HSI") , KoCompositeOp::categoryHSI());
add<&cfSaturation <HSIType,Arg> >(cs, COMPOSITE_SATURATION_HSI , i18n("Saturation HSI") , KoCompositeOp::categoryHSI());
add<&cfIncreaseSaturation<HSIType,Arg> >(cs, COMPOSITE_INC_SATURATION_HSI, i18n("Increase Saturation HSI"), KoCompositeOp::categoryHSI());
add<&cfDecreaseSaturation<HSIType,Arg> >(cs, COMPOSITE_DEC_SATURATION_HSI, i18n("Decrease Saturation HSI"), KoCompositeOp::categoryHSI());
add<&cfLightness <HSIType,Arg> >(cs, COMPOSITE_INTENSITY , i18n("Intensity") , KoCompositeOp::categoryHSI());
add<&cfIncreaseLightness <HSIType,Arg> >(cs, COMPOSITE_INC_INTENSITY , i18n("Increase Intensity") , KoCompositeOp::categoryHSI());
add<&cfDecreaseLightness <HSIType,Arg> >(cs, COMPOSITE_DEC_INTENSITY , i18n("Decrease Intensity") , KoCompositeOp::categoryHSI());
add<&cfColor <HSLType,Arg> >(cs, COMPOSITE_COLOR_HSL , i18n("Color HSL") , KoCompositeOp::categoryHSL());
add<&cfHue <HSLType,Arg> >(cs, COMPOSITE_HUE_HSL , i18n("Hue HSL") , KoCompositeOp::categoryHSL());
add<&cfSaturation <HSLType,Arg> >(cs, COMPOSITE_SATURATION_HSL , i18n("Saturation HSL") , KoCompositeOp::categoryHSL());
add<&cfIncreaseSaturation<HSLType,Arg> >(cs, COMPOSITE_INC_SATURATION_HSL, i18n("Increase Saturation HSL"), KoCompositeOp::categoryHSL());
add<&cfDecreaseSaturation<HSLType,Arg> >(cs, COMPOSITE_DEC_SATURATION_HSL, i18n("Decrease Saturation HSL"), KoCompositeOp::categoryHSL());
add<&cfLightness <HSLType,Arg> >(cs, COMPOSITE_LIGHTNESS , i18n("Lightness") , KoCompositeOp::categoryHSL());
add<&cfIncreaseLightness <HSLType,Arg> >(cs, COMPOSITE_INC_LIGHTNESS , i18n("Increase Lightness") , KoCompositeOp::categoryHSL());
add<&cfDecreaseLightness <HSLType,Arg> >(cs, COMPOSITE_DEC_LIGHTNESS , i18n("Decrease Lightness") , KoCompositeOp::categoryHSL());
add<&cfColor <HSVType,Arg> >(cs, COMPOSITE_COLOR_HSV , i18n("Color HSV") , KoCompositeOp::categoryHSV());
add<&cfHue <HSVType,Arg> >(cs, COMPOSITE_HUE_HSV , i18n("Hue HSV") , KoCompositeOp::categoryHSV());
add<&cfSaturation <HSVType,Arg> >(cs, COMPOSITE_SATURATION_HSV , i18n("Saturation HSV") , KoCompositeOp::categoryHSV());
add<&cfIncreaseSaturation<HSVType,Arg> >(cs, COMPOSITE_INC_SATURATION_HSV, i18n("Increase Saturation HSV"), KoCompositeOp::categoryHSV());
add<&cfDecreaseSaturation<HSVType,Arg> >(cs, COMPOSITE_DEC_SATURATION_HSV, i18n("Decrease Saturation HSV"), KoCompositeOp::categoryHSV());
add<&cfLightness <HSVType,Arg> >(cs, COMPOSITE_VALUE , i18n("Value") , KoCompositeOp::categoryHSV());
add<&cfIncreaseLightness <HSVType,Arg> >(cs, COMPOSITE_INC_VALUE , i18n("Increase Value") , KoCompositeOp::categoryHSV());
add<&cfDecreaseLightness <HSVType,Arg> >(cs, COMPOSITE_DEC_VALUE , i18n("Decrease Value") , KoCompositeOp::categoryHSV());
}
};
}
/**
* This function add to the colorspace all the composite ops defined by
* the pigment library.
*/
template<class _Traits_>
void addStandardCompositeOps(KoColorSpace* cs)
{
typedef typename _Traits_::channels_type channels_type;
static const bool useGeneralOps = true;
static const bool useRGBOps = (boost::is_base_of<KoBgrTraits<channels_type>, _Traits_>::value
|| boost::is_base_of<KoRgbTraits<channels_type>, _Traits_>::value);
_Private::AddGeneralOps<_Traits_, useGeneralOps>::add(cs);
_Private::AddRGBOps <_Traits_, useRGBOps >::add(cs);
}
#endif
diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp
index 1363974b9d..5512eaf80f 100644
--- a/libs/pigment/resources/KoColorSet.cpp
+++ b/libs/pigment/resources/KoColorSet.cpp
@@ -1,740 +1,742 @@
/* 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 <resources/KoColorSet.h>
#include <sys/types.h>
#include <QtEndian> // qFromLittleEndian
#include <QImage>
#include <QPoint>
#include <QVector>
#include <QFile>
#include <QTextStream>
#include <QFileInfo>
#include <QBuffer>
#include <QByteArray>
#include <QPainter>
#include <QXmlStreamReader>
#include <QTextCodec>
#include <DebugPigment.h>
#include <klocalizedstring.h>
#include "KoColor.h"
#include "KoColorSpaceRegistry.h"
#include "KoColorModelStandardIds.h"
struct KoColorSet::Private {
QByteArray data;
QString name;
QString comment;
qint32 columns;
QVector<KoColorSetEntry> colors;
};
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;
}
else if (fi.suffix().toLower() == "xml") {
return KoColorSet::XML;
}
return KoColorSet::UNKNOWN;
}
KoColorSet::KoColorSet(const QString& filename)
: KoResource(filename)
, d(new Private())
{
// Implemented in KoResource class
d->columns = 0; // Set the default value that the GIMP uses...
}
KoColorSet::KoColorSet()
: KoResource(QString())
, d(new Private())
{
d->columns = 0; // Set the default value that the GIMP uses...
}
/// Create an copied palette
KoColorSet::KoColorSet(const KoColorSet& rhs)
: QObject(0)
, KoResource(QString())
, d(new Private())
{
setFilename(rhs.filename());
d->name = rhs.d->name;
d->comment = rhs.d->comment;
d->columns = rhs.d->columns;
d->colors = rhs.d->colors;
setValid(true);
}
KoColorSet::~KoColorSet()
{
}
bool KoColorSet::load()
{
QFile file(filename());
if (file.size() == 0) return false;
if (!file.open(QIODevice::ReadOnly)) {
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);
d->data = dev->readAll();
Q_ASSERT(d->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 d->colors.count();
}
qint32 KoColorSet::getIndexClosestColor(KoColor color, bool useGivenColorSpace)
{
qint32 closestIndex = 0;
quint8 highestPercentage = 0;
quint8 testPercentage = 0;
KoColor compare = color;
for (qint32 i=0; i<nColors(); i++) {
KoColor entry = d->colors.at(i).color;
if (useGivenColorSpace==true && compare.colorSpace()!=entry.colorSpace()) {
entry.convertTo(compare.colorSpace());
} else if(compare.colorSpace()!=entry.colorSpace()) {
compare.convertTo(entry.colorSpace());
}
testPercentage = (255 - compare.colorSpace()->difference(compare.data(), entry.data()));
if (testPercentage>highestPercentage)
{
closestIndex = i;
highestPercentage = testPercentage;
}
}
return closestIndex;
}
QString KoColorSet::closestColorName(KoColor color, bool useGivenColorSpace)
{
int i = getIndexClosestColor(color, useGivenColorSpace);
QString name = d->colors.at(i).name;
return name;
}
bool KoColorSet::saveToDevice(QIODevice *dev) const
{
QTextStream stream(dev);
stream << "GIMP Palette\nName: " << name() << "\nColumns: " << d->columns << "\n#\n";
for (int i = 0; i < d->colors.size(); i++) {
const KoColorSetEntry& entry = d->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()
{
d->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 (d->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);
d->data = file.readAll();
file.close();
}
bool res = false;
PaletteType paletteType = detectFormat(filename(), d->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;
case XML:
res = loadXml();
break;
default:
res = false;
}
setValid(res);
if (d->columns == 0) {
d->columns = 10;
}
QImage img(d->columns * 4, (d->colors.size() / d->columns) * 4, QImage::Format_ARGB32);
QPainter gc(&img);
gc.fillRect(img.rect(), Qt::darkGray);
int counter = 0;
for(int i = 0; i < d->columns; ++i) {
for (int j = 0; j < (d->colors.size() / d->columns); ++j) {
if (counter < d->colors.size()) {
QColor c = d->colors.at(counter).color.toQColor();
gc.fillRect(i * 4, j * 4, 4, 4, c);
counter++;
}
else {
break;
}
}
}
setImage(img);
// save some memory
d->data.clear();
return res;
}
void KoColorSet::add(const KoColorSetEntry & c)
{
d->colors.push_back(c);
}
void KoColorSet::remove(const KoColorSetEntry & c)
{
for (auto it = d->colors.begin(); it != d->colors.end(); /*noop*/) {
if ((*it) == c) {
it = d->colors.erase(it);
return;
}
++it;
}
}
void KoColorSet::removeAt(quint32 index)
{
d->colors.remove(index);
}
void KoColorSet::clear()
{
d->colors.clear();
}
KoColorSetEntry KoColorSet::getColor(quint32 index)
{
return d->colors[index];
}
void KoColorSet::setColumnCount(int columns)
{
d->columns = columns;
}
int KoColorSet::columnCount()
{
return d->columns;
}
QString KoColorSet::defaultFileExtension() const
{
return QString(".gpl");
}
bool KoColorSet::loadGpl()
{
QString s = QString::fromUtf8(d->data.data(), d->data.count());
if (s.isEmpty() || s.isNull() || s.length() < 50) {
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: ")) {
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();
d->columns = columns.toInt();
index = 3;
}
for (qint32 i = index; i < lines.size(); i++) {
if (lines[i].startsWith('#')) {
d->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 < d->data.size(); i += 3) {
quint8 r = d->data[i];
quint8 g = d->data[i+1];
quint8 b = d->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, d->data.constData(), sizeof(RiffHeader));
header.colorcount = qFromBigEndian(header.colorcount);
for (int i = sizeof(RiffHeader);
(i < (int)(sizeof(RiffHeader) + header.colorcount) && i < d->data.size());
i += 4) {
quint8 r = d->data[i];
quint8 g = d->data[i+1];
quint8 b = d->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(d->data.data(), d->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;
}
void scribusParseColor(KoColorSet *set, QXmlStreamReader *xml)
{
KoColorSetEntry currentColor;
//It's a color, retrieve it
QXmlStreamAttributes colorProperties = xml->attributes();
QStringRef colorValue;
// RGB or CMYK?
if (colorProperties.hasAttribute("RGB")) {
dbgPigment << "Color " << colorProperties.value("NAME") << ", RGB " << colorProperties.value("RGB");
QStringRef colorName = colorProperties.value("NAME");
currentColor.name = colorName.isEmpty() || colorName.isNull() ? i18n("Untitled") : colorName.toString();
currentColor.color = KoColor(KoColorSpaceRegistry::instance()->rgb8());
colorValue = colorProperties.value("RGB");
if (colorValue.length() != 7 && colorValue.at(0) != '#') { // Color is a hexadecimal number
xml->raiseError("Invalid rgb8 color (malformed): " + colorValue);
return;
}
else {
bool rgbOk;
quint32 rgb = colorValue.mid(1).toUInt(&rgbOk, 16);
if (!rgbOk) {
xml->raiseError("Invalid rgb8 color (unable to convert): " + colorValue);
return;
}
quint8 r = rgb >> 16 & 0xff;
quint8 g = rgb >> 8 & 0xff;
quint8 b = rgb & 0xff;
dbgPigment << "Color parsed: "<< r << g << b;
currentColor.color.data()[0] = r;
currentColor.color.data()[1] = g;
currentColor.color.data()[2] = b;
currentColor.color.setOpacity(OPACITY_OPAQUE_U8);
set->add(currentColor);
while(xml->readNextStartElement()) {
//ignore - these are all unknown or the /> element tag
xml->skipCurrentElement();
}
return;
}
}
else if (colorProperties.hasAttribute("CMYK")) {
dbgPigment << "Color " << colorProperties.value("NAME") << ", CMYK " << colorProperties.value("CMYK");
QStringRef colorName = colorProperties.value("NAME");
currentColor.name = colorName.isEmpty() || colorName.isNull() ? i18n("Untitled") : colorName.toString();
currentColor.color = KoColor(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString()));
colorValue = colorProperties.value("CMYK");
if (colorValue.length() != 9 && colorValue.at(0) != '#') { // Color is a hexadecimal number
xml->raiseError("Invalid cmyk color (malformed): " + colorValue);
return;
}
else {
bool cmykOk;
quint32 cmyk = colorValue.mid(1).toUInt(&cmykOk, 16); // cmyk uses the full 32 bits
if (!cmykOk) {
xml->raiseError("Invalid cmyk color (unable to convert): " + colorValue);
return;
}
quint8 c = cmyk >> 24 & 0xff;
quint8 m = cmyk >> 16 & 0xff;
quint8 y = cmyk >> 8 & 0xff;
quint8 k = cmyk & 0xff;
dbgPigment << "Color parsed: "<< c << m << y << k;
currentColor.color.data()[0] = c;
currentColor.color.data()[1] = m;
currentColor.color.data()[2] = y;
currentColor.color.data()[3] = k;
currentColor.color.setOpacity(OPACITY_OPAQUE_U8);
set->add(currentColor);
while(xml->readNextStartElement()) {
//ignore - these are all unknown or the /> element tag
xml->skipCurrentElement();
}
return;
}
}
else {
xml->raiseError("Unknown color space for color " + currentColor.name);
}
}
bool loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml)
{
//1. Get name
QXmlStreamAttributes paletteProperties = xml->attributes();
QStringRef paletteName = paletteProperties.value("Name");
dbgPigment << "Processed name of palette:" << paletteName;
set->setName(paletteName.toString());
//2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them
while(xml->readNextStartElement()) {
QStringRef currentElement = xml->name();
if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) {
scribusParseColor(set, xml);
}
else {
xml->skipCurrentElement();
}
}
if(xml->hasError()) {
return false;
}
return true;
}
bool KoColorSet::loadXml() {
bool res = false;
QXmlStreamReader *xml = new QXmlStreamReader(d->data);
if (xml->readNextStartElement()) {
QStringRef paletteId = xml->name();
if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus
dbgPigment << "XML palette: " << filename() << ", Scribus format";
res = loadScribusXmlPalette(this, xml);
}
else {
// Unknown XML format
xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId);
}
}
// If there is any error (it should be returned through the stream)
if (xml->hasError() || !res) {
warnPigment << "Illegal XML palette:" << filename();
warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString();
return false;
}
else {
dbgPigment << "XML palette parsed successfully:" << filename();
return true;
}
}
quint16 readShort(QIODevice *io) {
quint16 val;
quint64 read = io->read((char*)&val, 2);
if (read != 2) return false;
return qFromBigEndian(val);
}
bool KoColorSet::loadAco()
{
QFileInfo info(filename());
setName(info.baseName());
QBuffer buf(&d->data);
buf.open(QBuffer::ReadOnly);
quint16 version = readShort(&buf);
quint16 numColors = readShort(&buf);
KoColorSetEntry e;
if (version == 1 && buf.size() > 4+numColors*10) {
buf.seek(4+numColors*10);
version = readShort(&buf);
numColors = readShort(&buf);
}
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(), QString()));
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(), QString()));
reinterpret_cast<quint16*>(e.color.data())[0] = ch1 * (quint16_MAX / 10000);
e.color.setOpacity(OPACITY_OPAQUE_U8);
}
else {
warnPigment << "Unsupported colorspace in palette" << filename() << "(" << colorSpace << ")";
skip = true;
}
if (version == 2) {
quint16 v2 = readShort(&buf); //this isn't a version, it's a marker and needs to be skipped.
+ Q_UNUSED(v2);
quint16 size = readShort(&buf) -1; //then comes the length
if (size>0) {
QByteArray ba = buf.read(size*2);
if (ba.size() == size*2) {
QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE");
e.name = Utf16Codec->toUnicode(ba);
} else {
warnPigment << "Version 2 name block is the wrong size" << filename();
}
}
v2 = readShort(&buf); //end marker also needs to be skipped.
+ Q_UNUSED(v2);
}
if (!skip) {
add(e);
}
}
return true;
}
diff --git a/libs/pigment/tests/KoRgbU8ColorSpaceTester.cpp b/libs/pigment/tests/KoRgbU8ColorSpaceTester.cpp
index 5f035e33e5..39bb1698cd 100644
--- a/libs/pigment/tests/KoRgbU8ColorSpaceTester.cpp
+++ b/libs/pigment/tests/KoRgbU8ColorSpaceTester.cpp
@@ -1,268 +1,308 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2008 Bart Coppens <kde@bartcoppens.be>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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 "KoRgbU8ColorSpaceTester.h"
#include "KoColorModelStandardIds.h"
#include <QTest>
#include <DebugPigment.h>
#include <string.h>
#include "KoColor.h"
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "KoCompositeOp.h"
#include "KoMixColorsOp.h"
#include <KoCompositeOpRegistry.h>
#define NUM_CHANNELS 4
#define RED_CHANNEL 0
#define GREEN_CHANNEL 1
#define BLUE_CHANNEL 2
#define ALPHA_CHANNEL 3
#include <KoColorProfile.h>
void KoRgbU8ColorSpaceTester::testBasics()
{
}
#define PIXEL_RED 0
#define PIXEL_GREEN 1
#define PIXEL_BLUE 2
#define PIXEL_ALPHA 3
void KoRgbU8ColorSpaceTester::testMixColors()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KoMixColorsOp * mixOp = cs->mixColorsOp();
// Test mixColors.
quint8 pixel1[4];
quint8 pixel2[4];
quint8 outputPixel[4];
pixel1[PIXEL_RED] = 255;
pixel1[PIXEL_GREEN] = 255;
pixel1[PIXEL_BLUE] = 255;
pixel1[PIXEL_ALPHA] = 255;
pixel2[PIXEL_RED] = 0;
pixel2[PIXEL_GREEN] = 0;
pixel2[PIXEL_BLUE] = 0;
pixel2[PIXEL_ALPHA] = 0;
const quint8 *pixelPtrs[2];
qint16 weights[2];
pixelPtrs[0] = pixel1;
pixelPtrs[1] = pixel2;
weights[0] = 255;
weights[1] = 0;
mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
QVERIFY((int)outputPixel[PIXEL_RED] == 255);
QVERIFY((int)outputPixel[PIXEL_GREEN] == 255);
QVERIFY((int)outputPixel[PIXEL_BLUE] == 255);
QVERIFY((int)outputPixel[PIXEL_ALPHA] == 255);
weights[0] = 0;
weights[1] = 255;
mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
QVERIFY((int)outputPixel[PIXEL_RED] == 0);
QVERIFY((int)outputPixel[PIXEL_GREEN] == 0);
QVERIFY((int)outputPixel[PIXEL_BLUE] == 0);
QVERIFY((int)outputPixel[PIXEL_ALPHA] == 0);
weights[0] = 128;
weights[1] = 127;
mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
QVERIFY((int)outputPixel[PIXEL_RED] == 255);
QVERIFY((int)outputPixel[PIXEL_GREEN] == 255);
QVERIFY((int)outputPixel[PIXEL_BLUE] == 255);
QVERIFY((int)outputPixel[PIXEL_ALPHA] == 128);
pixel1[PIXEL_RED] = 200;
pixel1[PIXEL_GREEN] = 100;
pixel1[PIXEL_BLUE] = 50;
pixel1[PIXEL_ALPHA] = 255;
pixel2[PIXEL_RED] = 100;
pixel2[PIXEL_GREEN] = 200;
pixel2[PIXEL_BLUE] = 20;
pixel2[PIXEL_ALPHA] = 255;
mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
QVERIFY((int)outputPixel[PIXEL_RED] == 150);
QCOMPARE((int)outputPixel[PIXEL_GREEN], 149);
QVERIFY((int)outputPixel[PIXEL_BLUE] == 35);
QVERIFY((int)outputPixel[PIXEL_ALPHA] == 255);
pixel1[PIXEL_RED] = 0;
pixel1[PIXEL_GREEN] = 0;
pixel1[PIXEL_BLUE] = 0;
pixel1[PIXEL_ALPHA] = 0;
pixel2[PIXEL_RED] = 255;
pixel2[PIXEL_GREEN] = 255;
pixel2[PIXEL_BLUE] = 255;
pixel2[PIXEL_ALPHA] = 254;
weights[0] = 89;
weights[1] = 166;
mixOp->mixColors(pixelPtrs, weights, 2, outputPixel);
QVERIFY((int)outputPixel[PIXEL_RED] == 255);
QVERIFY((int)outputPixel[PIXEL_GREEN] == 255);
QVERIFY((int)outputPixel[PIXEL_BLUE] == 255);
QVERIFY((int)outputPixel[PIXEL_ALPHA] == 165);
}
+void KoRgbU8ColorSpaceTester::testMixColorsAverage()
+{
+ const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
+ KoMixColorsOp * mixOp = cs->mixColorsOp();
+
+ // Test mixColors.
+ quint8 pixel1[4];
+ quint8 pixel2[4];
+ quint8 outputPixel[4];
+
+ pixel1[PIXEL_RED] = 255;
+ pixel1[PIXEL_GREEN] = 255;
+ pixel1[PIXEL_BLUE] = 255;
+ pixel1[PIXEL_ALPHA] = 255;
+
+ pixel2[PIXEL_RED] = 0;
+ pixel2[PIXEL_GREEN] = 0;
+ pixel2[PIXEL_BLUE] = 0;
+ pixel2[PIXEL_ALPHA] = 0;
+
+ const quint8 *pixelPtrs[2];
+
+ pixelPtrs[0] = pixel1;
+ pixelPtrs[1] = pixel2;
+
+ mixOp->mixColors(pixelPtrs, 2, outputPixel);
+
+ QCOMPARE((int)outputPixel[PIXEL_RED], 255);
+ QCOMPARE((int)outputPixel[PIXEL_GREEN], 255);
+ QCOMPARE((int)outputPixel[PIXEL_BLUE], 255);
+ QCOMPARE((int)outputPixel[PIXEL_ALPHA], 127);
+
+ pixel2[PIXEL_ALPHA] = 255;
+ mixOp->mixColors(pixelPtrs, 2, outputPixel);
+
+ QCOMPARE((int)outputPixel[PIXEL_RED], 127);
+ QCOMPARE((int)outputPixel[PIXEL_GREEN], 127);
+ QCOMPARE((int)outputPixel[PIXEL_BLUE], 127);
+ QCOMPARE((int)outputPixel[PIXEL_ALPHA], 255);
+}
void KoRgbU8ColorSpaceTester::testCompositeOps()
{
// Just COMPOSITE_COPY for now
QList<KoID> depthIDs = KoColorSpaceRegistry::instance()->colorDepthList(RGBAColorModelID.id(),
KoColorSpaceRegistry::AllColorSpaces);
Q_FOREACH (const KoID& depthId, depthIDs) {
if (depthId.id().contains("Float")) continue;
dbgPigment << depthId.id();
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(
RGBAColorModelID.id(), depthId.id(), "");
const KoCompositeOp* copyOp = cs->compositeOp(COMPOSITE_COPY);
KoColor src(cs), dst(cs);
QColor red(255, 0, 0);
QColor blue(0, 0, 255);
QColor transparentRed(255, 0, 0, 0);
// Copying a color over another color should replace the original color
src.fromQColor(red);
dst.fromQColor(blue);
dbgPigment << src.toQColor() << dst.toQColor();
QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
0, 0, 1, 1, OPACITY_OPAQUE_U8);
src.fromQColor(red);
QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
// Copying something transparent over something non-transparent should, of course, make the dst transparent
src.fromQColor(transparentRed);
dst.fromQColor(blue);
QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
0, 0, 1, 1, OPACITY_OPAQUE_U8);
src.fromQColor(transparentRed);
QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
// Copying something solid over something transparent
src.fromQColor(blue);
dst.fromQColor(transparentRed);
QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) != 0);
copyOp->composite(dst.data(), cs->pixelSize(), src.data(), cs->pixelSize(),
0, 0, 1, 1, OPACITY_OPAQUE_U8);
src.fromQColor(blue);
QVERIFY(memcmp(dst.data(), src.data(), cs->pixelSize()) == 0);
}
}
void KoRgbU8ColorSpaceTester::testCompositeOpsWithChannelFlags()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
QList<KoCompositeOp*> ops = cs->compositeOps();
Q_FOREACH (const KoCompositeOp *op, ops) {
/**
* ALPHA_DARKEN composite op doesn't take channel
* flags into account, so just skip it
*/
if (op->id() == COMPOSITE_ALPHA_DARKEN) continue;
if (op->id() == COMPOSITE_DISSOLVE) continue;
quint8 src[] = {128,128,128,129};
quint8 goodDst[] = {10,10,10,11};
quint8 badDst[] = {12,12,12,0};
KoCompositeOp::ParameterInfo params;
params.maskRowStart = 0;
params.dstRowStride = 0;
params.srcRowStride = 0;
params.maskRowStride = 0;
params.rows = 1;
params.cols = 1;
params.opacity = 1.0f;
params.flow = 1.0f;
QBitArray channelFlags(4, true);
channelFlags[2] = false;
params.channelFlags = channelFlags;
params.srcRowStart = src;
params.dstRowStart = goodDst;
op->composite(params);
params.dstRowStart = badDst;
op->composite(params);
/**
* The badDst has zero alpha, so the channels should be zeroed
* before increasing alpha of the pixel
*/
if (badDst[3] != 0 && badDst[2] != 0) {
dbgPigment << op->id()
<< "easy case:" << goodDst[2]
<< "difficult case:" << badDst[2];
dbgPigment << "The composite op has failed to erase the color "
"channel which was hidden by zero alpha.";
dbgPigment << "Expected Blue channel:" << 0;
dbgPigment << "Actual Blue channel: " << badDst[2];
QFAIL("Failed to erase color channel");
}
}
}
QTEST_GUILESS_MAIN(KoRgbU8ColorSpaceTester)
diff --git a/libs/pigment/tests/KoRgbU8ColorSpaceTester.h b/libs/pigment/tests/KoRgbU8ColorSpaceTester.h
index dd00625f25..8a5bdf0c00 100644
--- a/libs/pigment/tests/KoRgbU8ColorSpaceTester.h
+++ b/libs/pigment/tests/KoRgbU8ColorSpaceTester.h
@@ -1,36 +1,37 @@
/*
* 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.
*/
#ifndef KORGBCOLORSPACETESTER_H
#define KORGBCOLORSPACETESTER_H
#include <QObject>
class KoRgbU8ColorSpaceTester : public QObject
{
Q_OBJECT
void testCompositeOps();
private Q_SLOTS:
void testBasics();
void testMixColors();
+ void testMixColorsAverage();
void testCompositeOpsWithChannelFlags();
};
#endif
diff --git a/libs/store/KoZipStore.cpp b/libs/store/KoZipStore.cpp
index 690912af2b..8446d9c9d0 100644
--- a/libs/store/KoZipStore.cpp
+++ b/libs/store/KoZipStore.cpp
@@ -1,242 +1,242 @@
/* This file is part of the KDE project
Copyright (C) 2000-2002 David Faure <faure@kde.org>
Copyright (C) 2010 C. 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 "KoZipStore.h"
#include "KoStore_p.h"
#include <QBuffer>
#include <QByteArray>
#include <QTemporaryFile>
#include <kzip.h>
#include <StoreDebug.h>
#include <QUrl>
KoZipStore::KoZipStore(const QString & _filename, Mode mode, const QByteArray & appIdentification,
bool writeMimetype)
: KoStore(mode, writeMimetype)
{
debugStore << "KoZipStore Constructor filename =" << _filename
<< " mode = " << int(mode)
<< " mimetype = " << appIdentification << endl;
Q_D(KoStore);
d->localFileName = _filename;
m_pZip = new KZip(_filename);
init(appIdentification); // open the zip file and init some vars
}
KoZipStore::KoZipStore(QIODevice *dev, Mode mode, const QByteArray & appIdentification,
bool writeMimetype)
: KoStore(mode, writeMimetype)
{
m_pZip = new KZip(dev);
init(appIdentification);
}
KoZipStore::KoZipStore(QWidget* window, const QUrl &_url, const QString & _filename, Mode mode,
const QByteArray & appIdentification, bool writeMimetype)
: KoStore(mode, writeMimetype)
{
debugStore << "KoZipStore Constructor url" << _url.url(QUrl::PreferLocalFile)
<< " filename = " << _filename
<< " mode = " << int(mode)
<< " mimetype = " << appIdentification << endl;
Q_D(KoStore);
d->url = _url;
d->window = window;
if (mode == KoStore::Read) {
d->localFileName = _filename;
} else {
QTemporaryFile f("kozip");
f.open();
d->localFileName = f.fileName();
f.close();
}
m_pZip = new KZip(d->localFileName);
init(appIdentification); // open the zip file and init some vars
}
KoZipStore::~KoZipStore()
{
Q_D(KoStore);
debugStore << "KoZipStore::~KoZipStore";
if (!d->finalized)
finalize(); // ### no error checking when the app forgot to call finalize itself
delete m_pZip;
// When writing, we write to a temp file that then gets copied over the original filename
- if (d->mode == Write) {
+ if (d->mode == Write && (!d->localFileName.isEmpty() && !d->url.isEmpty())) {
QFile f(d->localFileName);
if (f.copy(d->url.toLocalFile())) {
f.remove();
}
}
}
void KoZipStore::init(const QByteArray& appIdentification)
{
Q_D(KoStore);
m_currentDir = 0;
d->good = m_pZip->open(d->mode == Write ? QIODevice::WriteOnly : QIODevice::ReadOnly);
if (!d->good)
return;
if (d->mode == Write) {
//debugStore <<"KoZipStore::init writing mimetype" << appIdentification;
m_pZip->setCompression(KZip::NoCompression);
m_pZip->setExtraField(KZip::NoExtraField);
// Write identification
if (d->writeMimetype) {
(void)m_pZip->writeFile(QLatin1String("mimetype"), appIdentification);
}
m_pZip->setCompression(KZip::DeflateCompression);
// We don't need the extra field in Krita - so we leave it as "no extra field".
} else {
d->good = m_pZip->directory() != 0;
}
}
void KoZipStore::setCompressionEnabled(bool e)
{
if (e) {
m_pZip->setCompression(KZip::DeflateCompression);
} else {
m_pZip->setCompression(KZip::NoCompression);
}
}
bool KoZipStore::doFinalize()
{
return m_pZip->close();
}
bool KoZipStore::openWrite(const QString& name)
{
Q_D(KoStore);
d->stream = 0; // Don't use!
return m_pZip->prepareWriting(name, "", "" /*m_pZip->rootDir()->user(), m_pZip->rootDir()->group()*/, 0);
}
bool KoZipStore::openRead(const QString& name)
{
Q_D(KoStore);
const KArchiveEntry * entry = m_pZip->directory()->entry(name);
if (entry == 0) {
return false;
}
if (entry->isDirectory()) {
warnStore << name << " is a directory !";
return false;
}
// Must cast to KZipFileEntry, not only KArchiveFile, because device() isn't virtual!
const KZipFileEntry * f = static_cast<const KZipFileEntry *>(entry);
delete d->stream;
d->stream = f->createDevice();
d->size = f->size();
return true;
}
qint64 KoZipStore::write(const char* _data, qint64 _len)
{
Q_D(KoStore);
if (_len == 0) return 0;
//debugStore <<"KoZipStore::write" << _len;
if (!d->isOpen) {
errorStore << "KoStore: You must open before writing" << endl;
return 0;
}
if (d->mode != Write) {
errorStore << "KoStore: Can not write to store that is opened for reading" << endl;
return 0;
}
d->size += _len;
if (m_pZip->writeData(_data, _len)) // writeData returns a bool!
return _len;
return 0;
}
QStringList KoZipStore::directoryList() const
{
QStringList retval;
const KArchiveDirectory *directory = m_pZip->directory();
Q_FOREACH (const QString &name, directory->entries()) {
const KArchiveEntry* fileArchiveEntry = m_pZip->directory()->entry(name);
if (fileArchiveEntry->isDirectory()) {
retval << name;
}
}
return retval;
}
bool KoZipStore::closeWrite()
{
Q_D(KoStore);
debugStore << "Wrote file" << d->fileName << " into ZIP archive. size" << d->size;
return m_pZip->finishWriting(d->size);
}
bool KoZipStore::enterRelativeDirectory(const QString& dirName)
{
Q_D(KoStore);
if (d->mode == Read) {
if (!m_currentDir) {
m_currentDir = m_pZip->directory(); // initialize
Q_ASSERT(d->currentPath.isEmpty());
}
const KArchiveEntry *entry = m_currentDir->entry(dirName);
if (entry && entry->isDirectory()) {
m_currentDir = dynamic_cast<const KArchiveDirectory*>(entry);
return m_currentDir != 0;
}
return false;
} else // Write, no checking here
return true;
}
bool KoZipStore::enterAbsoluteDirectory(const QString& path)
{
if (path.isEmpty()) {
m_currentDir = 0;
return true;
}
m_currentDir = dynamic_cast<const KArchiveDirectory*>(m_pZip->directory()->entry(path));
Q_ASSERT(m_currentDir);
return m_currentDir != 0;
}
bool KoZipStore::fileExists(const QString& absPath) const
{
const KArchiveEntry *entry = m_pZip->directory()->entry(absPath);
return entry && entry->isFile();
}
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 7e1cdcaa24..ffb4595cb4 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -1,545 +1,527 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile
${EXIV2_INCLUDE_DIR}
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
${OCIO_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}
)
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_config.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_infinity_manager.cpp
canvas/kis_change_guides_command.cpp
canvas/kis_guides_decoration.cpp
canvas/kis_guides_manager.cpp
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.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_filter.cpp
dialogs/kis_dlg_stroke_selection_properties.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/kis_dlg_import_image_sequence.cpp
dialogs/kis_delayed_save_dialog.cpp
dialogs/kis_dlg_internal_color_selector.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
brushhud/kis_uniform_paintop_property_widget.cpp
brushhud/kis_brush_hud.cpp
brushhud/kis_round_hud_button.cpp
brushhud/kis_dlg_brush_hud_config.cpp
brushhud/kis_brush_hud_properties_list.cpp
brushhud/kis_brush_hud_properties_config.cpp
kis_aspect_ratio_locker.cpp
kis_autogradient.cc
kis_bookmarked_configurations_editor.cc
kis_bookmarked_configurations_model.cc
kis_bookmarked_filter_configurations_model.cc
kis_base_option.cpp
kis_canvas_resource_provider.cpp
kis_derived_resources.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_cursor_cache.cpp
kis_custom_pattern.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_juggler_compressed.cpp
kis_node_selection_adapter.cpp
kis_node_insertion_adapter.cpp
kis_node_model.cpp
kis_node_filter_proxy_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_script_manager.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
kis_multinode_property.cpp
- kis_async_action_feedback.cpp
kis_stopgradient_editor.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
- kra/kis_colorize_dom_utils.cpp
opengl/kis_opengl.cpp
opengl/kis_opengl_canvas2.cpp
opengl/kis_opengl_canvas_debugger.cpp
opengl/kis_opengl_image_textures.cpp
opengl/kis_texture_tile.cpp
opengl/kis_opengl_shader_loader.cpp
kis_fps_decoration.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_stabilized_events_sampler.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
tool/strokes/kis_color_picker_stroke_strategy.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_tone_curve_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_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
widgets/kis_lod_availability_widget.cpp
widgets/kis_color_label_selector_widget.cpp
widgets/kis_color_filter_combo.cpp
widgets/kis_elided_label.cpp
widgets/kis_stopgradient_slider_widget.cpp
widgets/kis_spinbox_color_selector.cpp
widgets/kis_screen_color_picker.cpp
widgets/kis_visual_color_selector.cpp
widgets/KoDualColorButton.cpp
widgets/kis_color_input.cpp
widgets/kis_color_button.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_change_frame_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
KisNodeDelegate.cpp
kis_node_view_visibility_delegate.cpp
KisNodeToolTip.cpp
KisNodeView.cpp
kis_node_view_color_scheme.cpp
- KisFilterChain.cpp
- KisFilterChainLink.cpp
- KisFilterChainLinkList.cpp
KisImportExportFilter.cpp
- KisFilterEdge.cpp
KisFilterEntry.cpp
- KisFilterGraph.cpp
KisImportExportManager.cpp
- KisFilterVertex.cpp
KisMainWindow.cpp
KisOpenPane.cpp
KisPart.cpp
KisPrintJob.cpp
KisTemplate.cpp
KisTemplateCreateDia.cpp
KisTemplateGroup.cpp
KisTemplates.cpp
KisTemplatesPane.cpp
KisTemplateTree.cpp
KisUndoStackAction.cpp
KisView.cpp
thememanager.cpp
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
qtlockedfile/qtlockedfile.cpp
qtsingleapplication/qtlocalpeer.cpp
qtsingleapplication/qtsingleapplication.cpp
KisResourceBundle.cpp
KisResourceBundleManifest.cpp
kis_md5_generator.cpp
KisApplicationArguments.cpp
KisNetworkAccessManager.cpp
KisMultiFeedRSSModel.cpp
KisRemoteFileFetcher.cpp
KisPaletteModel.cpp
kis_palette_delegate.cpp
kis_palette_view.cpp
KisColorsetChooser.cpp
KisSaveGroupVisitor.cpp
)
if(WIN32)
if (NOT Qt5Gui_PRIVATE_INCLUDE_DIRS)
message(FATAL_ERROR "Qt5Gui Private header are missing!")
endif()
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
)
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
kis_animation_frame_cache.cpp
kis_animation_cache_populator.cpp
canvas/kis_animation_player.cpp
kis_animation_exporter.cpp
kis_animation_importer.cpp
)
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
input/wintab/qxcbconnection_xi2.cpp
input/wintab/qxcbconnection.cpp
input/wintab/kis_xi2_event_filter.cpp
)
endif()
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggenerators.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/wdgimportimagesequence.ui
forms/wdgstrokeselectionproperties.ui
forms/KisDetailsPaneBase.ui
forms/KisOpenPaneBase.ui
forms/wdgstopgradienteditor.ui
brushhud/kis_dlg_brush_hud_config.ui
forms/wdgdlginternalcolorselector.ui
dialogs/kis_delayed_save_dialog.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
)
QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h)
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network
- kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES}
+ kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES}
)
if (HAVE_KIO)
target_link_libraries(kritaui KF5::KIOCore)
endif()
if (NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${X11_X11_LIB}
${X11_Xinput_LIB}
${XCB_LIBRARIES})
endif()
if(APPLE)
target_link_libraries(kritaui ${FOUNDATION_LIBRARY})
endif ()
target_link_libraries(kritaui ${OPENEXR_LIBRARIES})
# Add VSync disable workaround
if(NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras)
endif()
if(X11_FOUND)
target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES})
endif()
target_include_directories(kritaui
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/canvas>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/flake>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/ora>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/tool>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/utils>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/widgets>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/input/wintab>
)
set_target_properties(kritaui
PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS})
if (APPLE)
install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita)
endif ()
diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp
index f1ce44f38e..c54d93da4e 100644
--- a/libs/ui/KisApplication.cpp
+++ b/libs/ui/KisApplication.cpp
@@ -1,740 +1,739 @@
/* 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 <stdlib.h>
#ifdef Q_OS_WIN
#include <windows.h>
#include <tchar.h>
#endif
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDir>
#include <QFile>
#include <QLocale>
#include <QMessageBox>
#include <QMessageBox>
#include <QProcessEnvironment>
#include <QSettings>
#include <QStandardPaths>
#include <QStringList>
#include <QStyle>
#include <QStyleFactory>
#include <QSysInfo>
#include <QTimer>
#include <QWidget>
#include <klocalizedstring.h>
#include <kdesktopfile.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <KoColorSpaceRegistry.h>
#include <KoPluginLoader.h>
#include <KoShapeRegistry.h>
#include <KoDpi.h>
#include "KoGlobal.h"
#include "KoConfig.h"
#include <resources/KoHashGeneratorProvider.h>
#include <KoResourcePaths.h>
#include <KisMimeDatabase.h>
#include "thememanager.h"
#include "KisPrintJob.h"
#include "KisDocument.h"
#include "KisMainWindow.h"
#include "KisAutoSaveRecoveryDialog.h"
#include "KisPart.h"
#include <kis_icon.h>
#include "kis_md5_generator.h"
#include "kis_splash_screen.h"
#include "kis_config.h"
#include "flake/kis_shape_selection.h"
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter_configuration.h>
#include <generator/kis_generator_registry.h>
#include <generator/kis_generator.h>
#include <brushengine/kis_paintop_registry.h>
#include <metadata/kis_meta_data_io_backend.h>
#include "kisexiv2/kis_exiv2.h"
#include "KisApplicationArguments.h"
#include <kis_debug.h>
#include "kis_action_registry.h"
#include <kis_brush_server.h>
#include <kis_resource_server_provider.h>
#include <KoResourceServerProvider.h>
#include "kis_image_barrier_locker.h"
#include "opengl/kis_opengl.h"
#include <KritaVersionWrapper.h>
namespace {
const QTime appStartTime(QTime::currentTime());
}
class KisApplicationPrivate
{
public:
KisApplicationPrivate()
: splashScreen(0)
{}
KisSplashScreen *splashScreen;
};
class KisApplication::ResetStarting
{
public:
ResetStarting(KisSplashScreen *splash = 0)
: m_splash(splash)
{
}
~ResetStarting() {
if (m_splash) {
KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen");
bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false);
if (hideSplash) {
m_splash->hide();
}
else {
m_splash->setWindowFlags(Qt::Tool);
QRect r(QPoint(), m_splash->size());
m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center());
m_splash->setWindowTitle(qAppName());
m_splash->setParent(qApp->activeWindow());
Q_FOREACH (QObject *o, m_splash->children()) {
QWidget *w = qobject_cast<QWidget*>(o);
if (w && w->isHidden()) {
w->setVisible(true);
}
}
m_splash->show();
}
}
}
KisSplashScreen *m_splash;
};
KisApplication::KisApplication(const QString &key, int &argc, char **argv)
: QtSingleApplication(key, argc, argv)
, d(new KisApplicationPrivate)
, m_autosaveDialog(0)
, m_mainWindow(0)
, m_batchRun(false)
{
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
setApplicationDisplayName("Krita");
setApplicationName("krita");
// Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird.
// setOrganizationName("krita");
setOrganizationDomain("krita.org");
QString version = KritaVersionWrapper::versionString(true);
setApplicationVersion(version);
setWindowIcon(KisIconUtils::loadIcon("calligrakrita"));
if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) {
QStringList styles = QStringList() << "fusion" << "plastique";
if (!styles.contains(style()->objectName().toLower())) {
Q_FOREACH (const QString & style, styles) {
if (!setStyle(style)) {
qDebug() << "No" << style << "available.";
}
else {
qDebug() << "Set style" << style;
break;
}
}
}
}
else {
qDebug() << "Style override disabled, using" << style()->objectName();
}
KisOpenGL::initialize();
qDebug() << "krita has opengl" << KisOpenGL::hasOpenGL();
}
#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(0 != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
{
//handle error
}
}
return bIsWow64;
}
#endif
void initializeGlobals(const KisApplicationArguments &args)
{
int dpiX = args.dpiX();
int dpiY = args.dpiY();
if (dpiX > 0 && dpiY > 0) {
KoDpi::setDPI(dpiX, dpiY);
}
}
void addResourceTypes()
{
// All Krita's resource types
KoResourcePaths::addResourceType("kis_pics", "data", "/pics/");
KoResourcePaths::addResourceType("kis_images", "data", "/images/");
KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/");
KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/");
KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/");
KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/");
KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/");
KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/");
KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/");
KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl");
KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true);
KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/");
KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true);
KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true);
KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/");
KoResourcePaths::addResourceType("kis_actions", "data", "/actions");
KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc");
KoResourcePaths::addResourceType("ko_effects", "data", "/effects/");
KoResourcePaths::addResourceType("tags", "data", "/tags/");
KoResourcePaths::addResourceType("templates", "data", "/templates");
KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita");
// // Extra directories to look for create resources. (Does anyone actually use that anymore?)
// KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp");
// KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp"));
// KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp");
// KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp"));
// KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp");
// KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp"));
// KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches");
// KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches"));
// Make directories for all resources we can save, and tags
QDir d;
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/");
}
void KisApplication::loadResources()
{
setSplashScreenLoadingText(i18n("Loading Gradients..."));
KoResourceServerProvider::instance()->gradientServer(true);
// Load base resources
setSplashScreenLoadingText(i18n("Loading Patterns..."));
KoResourceServerProvider::instance()->patternServer(true);
setSplashScreenLoadingText(i18n("Loading Palettes..."));
KoResourceServerProvider::instance()->paletteServer(false);
setSplashScreenLoadingText(i18n("Loading Brushes..."));
KisBrushServer::instance()->brushServer(true);
// load paintop presets
setSplashScreenLoadingText(i18n("Loading Paint Operations..."));
KisResourceServerProvider::instance()->paintOpPresetServer(true);
setSplashScreenLoadingText(i18n("Loading Resource Bundles..."));
KisResourceServerProvider::instance()->resourceBundleServer();
}
void KisApplication::loadPlugins()
{
KoShapeRegistry* r = KoShapeRegistry::instance();
r->add(new KisShapeSelectionFactory());
KisActionRegistry::instance();
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
KisPaintOpRegistry::instance();
KoColorSpaceRegistry::instance();
// Load the krita-specific tools
setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool..."));
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// Load dockers
setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock..."));
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// XXX_EXIV: make the exiv io backends real plugins
setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO..."));
KisExiv2::initialize();
}
bool KisApplication::start(const KisApplicationArguments &args)
{
-#if defined(Q_OS_WIN) || defined (Q_OS_MAC)
+#if defined(Q_OS_WIN) || defined (Q_OS_OSX)
#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
#endif
setSplashScreenLoadingText(i18n("Initializing Globals"));
initializeGlobals(args);
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();
m_batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty());
// 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 showSplashScreen = !m_batchRun && qEnvironmentVariableIsEmpty("NOSPLASH") && qgetenv("XDG_CURRENT_DESKTOP") != "GNOME";
if (showSplashScreen) {
d->splashScreen->show();
d->splashScreen->repaint();
processEvents();
}
KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator());
// Initialize all Krita directories etc.
KoGlobal::initialize();
KConfigGroup group(KSharedConfig::openConfig(), "theme");
Digikam::ThemeManager themeManager;
themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark"));
ResetStarting resetStarting(d->splashScreen); // remove the splash when done
Q_UNUSED(resetStarting);
// Make sure we can save resources and tags
setSplashScreenLoadingText(i18n("Adding resource types"));
addResourceTypes();
// Load all resources and tags before the plugins do that
loadResources();
// Load the plugins
loadPlugins();
if (needsMainWindow) {
// show a mainWindow asap, if we want that
setSplashScreenLoadingText(i18n("Loading Main Window..."));
m_mainWindow = KisPart::instance()->createMainWindow();
if (showmainWindow) {
m_mainWindow->initializeGeometry();
m_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)
if (!m_batchRun) {
checkAutosaveFiles();
}
setSplashScreenLoadingText(QString()); // done loading, so clear out label
// Get the command line arguments which we have to parse
int argsCount = args.filenames().count();
if (argsCount > 0) {
// Loop through arguments
short int nPrinted = 0;
for (int argNumber = 0; argNumber < argsCount; 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 (m_batchRun) {
continue;
}
if (createNewDocFromTemplate(fileName, m_mainWindow)) {
++numberOfOpenDocuments;
}
// now try to load
}
else {
if (exportAs) {
QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName);
if (outputMimetype == "application/octetstream") {
dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl;
return 1;
}
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
doc->openUrl(QUrl::fromLocalFile(fileName));
qApp->processEvents(); // For vector layers to be updated
KisImageBarrierLocker locker(doc->image());
KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK;
KisImportExportManager manager(doc);
manager.setBatchMode(true);
QByteArray mime(outputMimetype.toLatin1());
status = manager.exportDocument(exportFileName, mime);
if (status != KisImportExportFilter::OK) {
dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << (int)status;
}
nPrinted++;
QTimer::singleShot(0, this, SLOT(quit()));
}
else if (m_mainWindow) {
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
if (m_mainWindow->openDocumentInternal(QUrl::fromLocalFile(fileName), doc)) {
if (print) {
m_mainWindow->slotFilePrint();
nPrinted++;
// TODO: trigger closing of app once printing is done
}
else if (exportAsPdf) {
KisPrintJob *job = m_mainWindow->exportToPdf(exportFileName);
if (job)
connect (job, SIGNAL(destroyed(QObject*)), m_mainWindow,
SLOT(slotFileQuit()), Qt::QueuedConnection);
nPrinted++;
} else {
// Normal case, success
numberOfOpenDocuments++;
}
} else {
// .... if failed
// delete doc; done by openDocument
}
}
}
}
if (m_batchRun) {
return nPrinted > 0;
}
}
// fixes BUG:369308 - Krita crashing on splash screen when loading.
// trying to open a file before Krita has loaded can cause it to hang and crash
d->splashScreen->displayLinks();
d->splashScreen->displayRecentFiles();
// not calling this before since the program will quit there.
return true;
}
KisApplication::~KisApplication()
{
delete d;
}
void KisApplication::setSplashScreen(QWidget *splashScreen)
{
d->splashScreen = qobject_cast<KisSplashScreen*>(splashScreen);
}
void KisApplication::setSplashScreenLoadingText(QString textToLoad)
{
d->splashScreen->loadingLabel->setText(textToLoad);
d->splashScreen->repaint();
}
void KisApplication::hideSplashScreen()
{
if (d->splashScreen) {
// hide the splashscreen to see the dialog
d->splashScreen->hide();
}
}
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;
}
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;
}
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) {
QString filename = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
createNewDocFromTemplate(filename, mw);
}
else if (QFile(filename).exists()) {
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
mw->openDocumentInternal(QUrl::fromLocalFile(filename), doc);
}
}
}
}
void KisApplication::fileOpenRequested(const QString &url)
{
KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first();
if (mainWindow) {
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
mainWindow->openDocumentInternal(QUrl::fromLocalFile(url), doc);
}
}
void KisApplication::checkAutosaveFiles()
{
if (m_batchRun) return;
// 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 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
m_autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden);
// Allow the user to make their selection
if (m_autosaveFiles.size() > 0) {
if (d->splashScreen) {
// hide the splashscreen to see the dialog
d->splashScreen->hide();
}
m_autosaveDialog = new KisAutoSaveRecoveryDialog(m_autosaveFiles, activeWindow());
QDialog::DialogCode result = (QDialog::DialogCode) m_autosaveDialog->exec();
if (result == QDialog::Accepted) {
QStringList filesToRecover = m_autosaveDialog->recoverableFiles();
Q_FOREACH (const QString &autosaveFile, m_autosaveFiles) {
if (!filesToRecover.contains(autosaveFile)) {
QFile::remove(dir.absolutePath() + "/" + autosaveFile);
}
}
m_autosaveFiles = filesToRecover;
} else {
m_autosaveFiles.clear();
}
if (m_autosaveFiles.size() > 0) {
QList<QUrl> autosaveUrls;
Q_FOREACH (const QString &autoSaveFile, m_autosaveFiles) {
const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile);
autosaveUrls << url;
}
if (m_mainWindow) {
Q_FOREACH (const QUrl &url, autosaveUrls) {
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
m_mainWindow->openDocumentInternal(url, doc);
}
}
}
// cleanup
delete m_autosaveDialog;
m_autosaveDialog = nullptr;
}
}
bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow)
{
QString templatePath;
const QUrl templateUrl = QUrl::fromLocalFile(fileName);
if (QFile::exists(fileName)) {
templatePath = templateUrl.toLocalFile();
dbgUI << "using full path...";
}
else {
QString desktopName(fileName);
const QString templatesResourcePath = QStringLiteral("templates/");
QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName);
if (paths.isEmpty()) {
paths = KoResourcePaths::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()) {
QUrl templateBase;
templateBase.setPath(templatePath);
KDesktopFile templateInfo(templatePath);
QString templateName = templateInfo.readUrl();
QUrl templateURL;
templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName);
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
if (mainWindow->openDocumentInternal(templateURL, doc)) {
doc->resetURL();
- doc->setEmpty();
doc->setTitleModified();
dbgUI << "Template loaded...";
return true;
}
else {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Template %1 failed to load.", templateURL.toDisplayString()));
}
}
return false;
}
void KisApplication::clearConfig()
{
KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread());
KSharedConfigPtr config = KSharedConfig::openConfig();
// find user settings file
bool createDir = false;
QString kritarcPath = KoResourcePaths::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();
}
}
}
diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp
index 04536c6942..ebf57d2391 100644
--- a/libs/ui/KisDocument.cpp
+++ b/libs/ui/KisDocument.cpp
@@ -1,2505 +1,1653 @@
/* 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 <KisMimeDatabase.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 <KoXmlReader.h>
+#include <KoStoreDevice.h>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kdesktopfile.h>
#include <kconfiggroup.h>
#include <QTemporaryFile>
#include <kbackup.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_idle_watcher.h>
#include <kis_signal_auto_connection.h>
#include <kis_debug.h>
+#include <kis_canvas_widget_base.h>
// Local
#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"
#include "KisApplication.h"
#include "KisDocument.h"
#include "KisImportExportManager.h"
#include "KisPart.h"
#include "KisView.h"
-#include "kis_async_action_feedback.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "kis_image_barrier_lock_adapter.h"
#include <mutex>
-static const char CURRENT_DTD_VERSION[] = "2.0";
-
// Define the protocol used here for embedded documents' URL
// This used to "store" but QUrl didn't like it,
// so let's simply make it "tar" !
#define STORE_PROTOCOL "tar"
// The internal path is a hack to make QUrl 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() override {
// signal that the job is done
setValue(-1);
}
int maximum() const override {
return 100;
}
void setValue(int value) override {
if (m_mainWindow) {
m_mainWindow->slotProgress(value);
}
}
void setRange(int /*minimum*/, int /*maximum*/) override {
}
void setFormat(const QString &/*format*/) override {
}
};
}
//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) override {
KisImageWSP image = this->image();
image->requestStrokeCancellation();
if(image->tryBarrierLock()) {
KUndo2Stack::setIndex(idx);
image->unlock();
}
}
void notifySetIndexChangedOneCommand() override {
KisImageWSP image = this->image();
image->unlock();
/**
* Some very weird commands may emit blocking signals to
* the GUI (e.g. KisGuiContextCommand). Here is the best thing
* we can do to avoid the deadlock
*/
while(!image->tryBarrierLock()) {
QApplication::processEvents();
}
}
void undo() override {
KisImageWSP image = this->image();
image->requestUndoDuringStroke();
+
+ if (image->tryUndoUnfinishedLod0Stroke() == UNDO_OK) {
+ return;
+ }
+
if(image->tryBarrierLock()) {
KUndo2Stack::undo();
image->unlock();
}
}
void redo() override {
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
+ Private() :
docInfo(0),
progressUpdater(0),
progressProxy(0),
importExportManager(0),
- specialOutputFlag(0), // default is native format
isImporting(false),
isExporting(false),
password(QString()),
modifiedAfterAutosave(false),
isAutosaving(false),
- autoErrorHandlingEnabled(true),
backupFile(true),
- backupPath(QString()),
doNotSaveExtDoc(false),
- storeInternal(false),
- isLoading(false),
undoStack(0),
m_saveOk(false),
m_waitForSave(false),
m_duringSaveAs(false),
- m_bTemp(false),
m_bAutoDetectedMime(false),
modified(false),
readwrite(true),
disregardAutosaveFailure(false),
nserver(0),
macroNestDepth(0),
imageIdleWatcher(2000 /*ms*/),
- kraLoader(0),
suppressProgress(false),
fileProgressProxy(0)
{
if (QLocale().measurementSystem() == QLocale::ImperialSystem) {
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;
KoUnit unit;
KisImportExportManager *importExportManager; // 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] = {true, true}; // 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.
+ int autoSaveDelay {300}; // in seconds, 0 to disable.
bool modifiedAfterAutosave;
bool isAutosaving;
- 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;
KisGuidesConfig guidesConfig;
- bool isEmpty;
-
- KoPageLayout pageLayout;
-
QUrl 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
- QUrl m_url; // Remote (or local) url - the one displayed to the user.
+ bool m_saveOk;
+ bool m_waitForSave;
+ bool m_duringSaveAs;
+ bool m_bAutoDetectedMime; // whether the mimetype in the arguments was detected by the part itself
+ QUrl m_url; // 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;
QMutex savingMutex;
bool modified;
bool readwrite;
QDateTime firstMod;
QDateTime lastMod;
bool disregardAutosaveFailure;
KisNameServer *nserver;
qint32 macroNestDepth;
KisImageSP image;
KisNodeSP preActivatedNode;
KisShapeController* shapeController;
KoShapeController* koShapeController;
KisIdleWatcher imageIdleWatcher;
QScopedPointer<KisSignalAutoConnection> imageIdleConnection;
-
- KisKraLoader* kraLoader;
- KisKraSaver* kraSaver;
-
bool suppressProgress;
KoProgressProxy* fileProgressProxy;
QList<KisPaintingAssistantSP> assistants;
KisGridConfig gridConfig;
- bool openFile() {
- document->setFileProgressProxy();
- document->setUrl(m_url);
-
- bool ok = document->openFile();
-
- document->clearFileProgressProxy();
- 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
- QString mime = KisMimeDatabase::mimeTypeForFile(m_url.toLocalFile());
- mimeType = mime.toLocal8Bit();
- m_bAutoDetectedMime = true;
- }
- const bool ret = openFile();
- if (ret) {
- emit document->completed();
- } else {
- emit document->canceled(QString());
- }
- return ret;
- }
-
- // 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();
- }
- }
-
void setImageAndInitIdleWatcher(KisImageSP _image) {
image = _image;
imageIdleWatcher.setTrackedImage(image);
if (image) {
imageIdleConnection.reset(
new KisSignalAutoConnection(
&imageIdleWatcher, SIGNAL(startedIdleMode()),
image.data(), SLOT(explicitRegenerateLevelOfDetail())));
}
}
class SafeSavingLocker;
};
class KisDocument::Private::SafeSavingLocker {
public:
- SafeSavingLocker(KisDocument::Private *_d)
- : d(_d),
- m_locked(false),
- m_imageLock(d->image, true),
- m_savingLock(&d->savingMutex)
+ SafeSavingLocker(KisDocument::Private *_d, KisDocument *document)
+ : d(_d)
+ , m_locked(false)
+ , m_imageLock(d->image, true)
+ , m_savingLock(&d->savingMutex)
+ , m_document(document)
{
const int realAutoSaveInterval = KisConfig().autoSaveInterval();
const int emergencyAutoSaveInterval = 10; // sec
/**
* Initial try to lock both objects. Locking the image guards
* us from any image composition threads running in the
* background, while savingMutex guards us from entering the
* saving code twice by autosave and main threads.
*
* Since we are trying to lock multiple objects, so we should
* do it in a safe manner.
*/
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
if (!m_locked) {
if (d->isAutosaving) {
d->disregardAutosaveFailure = true;
if (realAutoSaveInterval) {
- d->document->setAutoSave(emergencyAutoSaveInterval);
+ document->setAutoSaveDelay(emergencyAutoSaveInterval);
}
} else {
d->image->requestStrokeEnd();
QApplication::processEvents();
// one more try...
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
}
}
if (m_locked) {
d->disregardAutosaveFailure = false;
}
}
~SafeSavingLocker() {
if (m_locked) {
m_imageLock.unlock();
m_savingLock.unlock();
const int realAutoSaveInterval = KisConfig().autoSaveInterval();
- d->document->setAutoSave(realAutoSaveInterval);
+ m_document->setAutoSaveDelay(realAutoSaveInterval);
}
}
bool successfullyLocked() const {
return m_locked;
}
private:
KisDocument::Private *d;
bool m_locked;
KisImageBarrierLockAdapter m_imageLock;
StdLockableWrapper<QMutex> m_savingLock;
+ KisDocument *m_document;
};
KisDocument::KisDocument()
- : d(new Private(this))
+ : d(new Private())
{
d->undoStack = new UndoStack(this);
d->undoStack->setParent(this);
- d->isEmpty = true;
d->importExportManager = new KisImportExportManager(this);
d->importExportManager->setProgresUpdater(d->progressUpdater);
connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
- setAutoSave(defaultAutoSave());
+ KisConfig cfg;
+ setAutoSaveDelay(cfg.autoSaveInterval());
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;
-
d->firstMod = QDateTime::currentDateTime();
d->lastMod = QDateTime::currentDateTime();
// preload the krita resources
KisResourceServerProvider::instance();
- init();
+ d->nserver = new KisNameServer(1);
+
+ d->shapeController = new KisShapeController(this, d->nserver);
+ d->koShapeController = new KoShapeController(0, d->shapeController);
+
undoStack()->setUndoLimit(KisConfig().undoStackLimit());
connect(d->undoStack, SIGNAL(indexChanged(int)), this, SLOT(slotUndoStackIndexChanged(int)));
setBackupFile(KisConfig().backupFile());
}
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->importExportManager;
// Despite being QObject they needs to be deleted before the image
delete d->shapeController;
delete d->koShapeController;
if (d->image) {
d->image->notifyAboutToBeDeleted();
/**
* WARNING: We should wait for all the internal image jobs to
* finish before entering KisImage's destructor. The problem is,
* while execution of KisImage::~KisImage, all the weak shared
* pointers pointing to the image enter an inconsistent
* state(!). The shared counter is already zero and destruction
* has started, but the weak reference doesn't know about it,
* because KisShared::~KisShared hasn't been executed yet. So all
* the threads running in background and having weak pointers will
* enter the KisImage's destructor as well.
*/
d->image->requestStrokeCancellation();
d->image->waitForDone();
// clear undo commands that can still point to the image
d->undoStack->clear();
d->image->waitForDone();
KisImageWSP sanityCheckPointer = d->image;
Q_UNUSED(sanityCheckPointer);
// The following line trigger the deletion of the image
d->image.clear();
// check if the image has actually been deleted
KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityCheckPointer.isValid());
}
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 QUrl &_url, KisPropertiesConfigurationSP exportConfiguration)
{
+ //qDebug() << "exportDocument" << _url.toDisplayString() << "is autosaving" << d->isAutosaving;
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).
//
QUrl oldURL = url();
QString oldFile = localFilePath();
+ //qDebug() << "\toldUrl" << oldURL << "oldFile" << oldFile << "export url" << _url;
+
bool wasModified = isModified();
- QByteArray oldMimeType = mimeType();
// save...
ret = saveAs(_url, exportConfiguration);
//
// This is sooooo hacky :(
// Hopefully we will restore enough state.
//
dbgUI << "Restoring KisDocument state to before export";
- // always restore url & m_file because KParts has changed them
- // (regardless of failure or success)
+ // always restore url & m_file regardless of failure or success
+ //qDebug() << "\tafter saveAs: url" << url() << "local file path" << localFilePath();
setUrl(oldURL);
setLocalFilePath(oldFile);
+ //qDebug() << "\tafter restoring: url" << url() << "local file path" << localFilePath();
+
// 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(KisPropertiesConfigurationSP exportConfiguration)
+bool KisDocument::saveAs(const QUrl &url, KisPropertiesConfigurationSP exportConfiguration)
+{
+ //qDebug() << "saveAs" << url;
+ if (!url.isValid() || !url.isLocalFile()) {
+ errKrita << "saveAs: Malformed URL " << url.url() << endl;
+ return false;
+ }
+ d->m_duringSaveAs = true;
+ d->m_originalURL = d->m_url;
+ d->m_originalFilePath = d->m_file;
+ d->m_url = url; // Store where to upload in saveToURL
+ d->m_file = d->m_url.toLocalFile();
+
+ bool result = save(exportConfiguration); // 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 = QUrl();
+ d->m_originalFilePath.clear();
+ }
+
+ return result;
+}
+
+bool KisDocument::save(KisPropertiesConfigurationSP exportConfiguration)
+{
+ //qDebug() << "save" << d->m_file << d->m_url << url() << localFilePath();
+
+ d->m_saveOk = false;
+ if (d->m_file.isEmpty()) { // document was created empty
+ d->m_file = d->m_url.toLocalFile();
+ }
+
+ updateEditingTime(true);
+
+ setFileProgressProxy();
+ setUrl(url());
+
+ bool ok = saveFile(localFilePath(), exportConfiguration);
+
+ clearFileProgressProxy();
+
+ if (ok) {
+ setModified( false );
+ emit completed();
+ d->m_saveOk = true;
+ d->m_duringSaveAs = false;
+ d->m_originalURL = QUrl();
+ d->m_originalFilePath.clear();
+ return true; // Nothing to do
+ }
+ else {
+ emit canceled(QString());
+ }
+ return false;
+}
+
+bool KisDocument::saveFile(const QString &filePath, KisPropertiesConfigurationSP exportConfiguration)
{
+ Private::SafeSavingLocker locker(d, this);
+ if (!locker.successfullyLocked()) {
+ qWarning() << "Could not lock image for saving, it's still busy";
+ return false;
+ }
+
// Unset the error message
setErrorMessage("");
// Save it to be able to restore it after a failed save
const bool wasModified = isModified();
- // Show the dialog with the options, if any
-
- // The output format is set by koMainWindow, and by openFile
+ // The output format is set by KisMainWindow, and by openFile
QByteArray outputMimeType = d->outputMimeType;
- if (outputMimeType.isEmpty())
+
+ if (outputMimeType.isEmpty()) {
outputMimeType = d->outputMimeType = nativeFormatMimeType();
+ }
+
+ //qDebug() << "saveFile. Is Autosaving?" << isAutosaving() << "url" << filePath << d->outputMimeType;
- QApplication::setOverrideCursor(Qt::WaitCursor);
- if (backupFile()) {
+ if (d->backupFile) {
Q_ASSERT(url().isLocalFile());
- KBackup::backupFile(url().toLocalFile(), d->backupPath);
+ KBackup::backupFile(url().toLocalFile());
}
qApp->processEvents();
bool ret = false;
bool suppressErrorDialog = false;
KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK;
setFileProgressUpdater(i18n("Saving Document"));
- QFileInfo fi(localFilePath());
+ QFileInfo fi(filePath);
QString tempororaryFileName;
{
QTemporaryFile tf(QDir::tempPath() + "/XXXXXX" + fi.baseName() + "." + fi.completeSuffix());
tf.open();
tempororaryFileName = tf.fileName();
}
Q_ASSERT(!tempororaryFileName.isEmpty());
- if (!isNativeFormat(outputMimeType)) {
- Private::SafeSavingLocker locker(d);
- if (locker.successfullyLocked()) {
- status = d->importExportManager->exportDocument(tempororaryFileName, outputMimeType, exportConfiguration);
- } else {
- status = KisImportExportFilter::UsageError;
- }
+ //qDebug() << "saving to tempory file" << tempororaryFileName;
+ status = d->importExportManager->exportDocument(tempororaryFileName, outputMimeType, !d->isExporting , exportConfiguration);
- ret = status == KisImportExportFilter::OK;
- suppressErrorDialog = (status == KisImportExportFilter::UserCancelled || status == KisImportExportFilter::BadConversionGraph);
- dbgFile << "Export status was" << status;
- } else {
- // Native format => normal save
- ret = saveNativeFormat(tempororaryFileName);
- }
+ ret = (status == KisImportExportFilter::OK);
+ suppressErrorDialog = (isAutosaving() || status == KisImportExportFilter::UserCancelled || status == KisImportExportFilter::BadConversionGraph);
+ //qDebug() << "Export status was" << status;
if (ret) {
- if (!d->suppressProgress) {
+
+ //qDebug() << "copying temporary file" << tempororaryFileName << "to" << filePath;
+
+ if (!d->isAutosaving && !d->suppressProgress) {
QPointer<KoUpdater> updater = d->progressUpdater->startSubtask(1, "clear undo stack");
updater->setProgress(0);
d->undoStack->setClean();
updater->setProgress(100);
} else {
d->undoStack->setClean();
}
QFile tempFile(tempororaryFileName);
- QString s = localFilePath();
+ QString s = filePath;
QFile dstFile(s);
while (QFileInfo(s).exists()) {
s.append("_");
}
bool r;
- if (s != localFilePath()) {
+ if (s != filePath) {
r = dstFile.rename(s);
if (!r) {
setErrorMessage(i18n("Could not rename original file to %1: %2", dstFile.fileName(), dstFile. errorString()));
}
}
if (tempFile.exists()) {
-
- r = tempFile.copy(localFilePath());
+ r = tempFile.copy(filePath);
if (!r) {
setErrorMessage(i18n("Copying the temporary file failed: %1 to %2: %3", tempFile.fileName(), dstFile.fileName(), tempFile.errorString()));
}
else {
r = tempFile.remove();
if (!r) {
setErrorMessage(i18n("Could not remove temporary file %1: %2", tempFile.fileName(), tempFile.errorString()));
}
- else if (s != localFilePath()) {
+ else if (s != filePath) {
r = dstFile.remove();
if (!r) {
setErrorMessage(i18n("Could not remove saved original file: %1", dstFile.errorString()));
}
}
}
}
else {
setErrorMessage(i18n("The temporary file %1 is gone before we could copy it!", tempFile.fileName()));
}
if (errorMessage().isEmpty()) {
- removeAutoSaveFiles();
+ if (!isAutosaving()) {
+ removeAutoSaveFiles();
+ }
}
else {
qWarning() << "Error while saving:" << errorMessage();
}
// Restart the autosave timer
// (we don't want to autosave again 2 seconds after a real save)
- setAutoSave(d->autoSaveDelay);
+ if (!isAutosaving()) {
+ setAutoSaveDelay(d->autoSaveDelay);
+ }
d->mimeType = outputMimeType;
- setConfirmNonNativeSave(isExporting(), false);
}
else {
if (!suppressErrorDialog) {
if (errorMessage().isEmpty()) {
setErrorMessage(KisImportExportFilter::conversionStatusString(status));
}
if (errorMessage().isEmpty()) {
- QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", localFilePath()));
+ QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", filePath));
} else {
- QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", localFilePath(), errorMessage()));
+ QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", filePath, 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);
}
+ emit sigSavingFinished();
clearFileProgressUpdater();
- QApplication::restoreOverrideCursor();
-
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)
+void KisDocument::setOutputMimeType(const QByteArray & mimeType)
{
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::fileBatchMode() const
{
- return d->importExportManager->getBatchMode();
+ return d->importExportManager->batchMode();
}
void KisDocument::setFileBatchMode(const bool batchMode)
{
d->importExportManager->setBatchMode(batchMode);
}
bool KisDocument::isImporting() const
{
return d->isImporting;
}
bool KisDocument::isExporting() const
{
return d->isExporting;
}
-void KisDocument::setAutoErrorHandlingEnabled(bool b)
+void KisDocument::slotAutoSave()
{
- d->autoErrorHandlingEnabled = b;
-}
+ //qDebug() << "slotAutoSave. Modified:" << d->modified << "modifiedAfterAutosave" << d->modified << "url" << url() << localFilePath();
-bool KisDocument::isAutoErrorHandlingEnabled() const
-{
- return d->autoErrorHandlingEnabled;
-}
+ if (!d->isAutosaving && d->modified && d->modifiedAfterAutosave) {
-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->isAutosaving = true;
- bool ret = saveNativeFormat(autoSaveFile(localFilePath()));
- setModified(true);
- if (ret) {
- d->modifiedAfterAutosave = false;
- d->autoSaveTimer.stop(); // until the next change
- }
- d->isAutosaving = 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?"));
- }
+ connect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int)));
+ emit statusBarMessage(i18n("Autosaving..."));
+ d->isAutosaving = true;
+ QString autoSaveFileName = generateAutoSaveFileName(localFilePath());
+
+ QByteArray mimetype = d->outputMimeType;
+ d->outputMimeType = nativeFormatMimeType();
+ bool ret = exportDocument(QUrl::fromLocalFile(autoSaveFileName));
+ d->outputMimeType = mimetype;
+
+ if (ret) {
+ d->modifiedAfterAutosave = false;
+ d->autoSaveTimer.stop(); // until the next change
+ }
+ d->isAutosaving = 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);
+ setAutoSaveDelay(d->autoSaveDelay);
Q_FOREACH (KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) {
mainWindow->setReadWrite(readwrite);
}
-
}
-void KisDocument::setAutoSave(int delay)
+void KisDocument::setAutoSaveDelay(int delay)
{
+ //qDebug() << "setting autosave delay from" << d->autoSaveDelay << "to" << delay;
d->autoSaveDelay = delay;
- if (isReadWrite() && d->autoSaveDelay > 0)
+ if (isReadWrite() && d->autoSaveDelay > 0) {
d->autoSaveTimer.start(d->autoSaveDelay * 1000);
- else
+ }
+ else {
d->autoSaveTimer.stop();
+ }
}
KoDocumentInfo *KisDocument::documentInfo() const
{
return d->docInfo;
}
bool KisDocument::isModified() const
{
return d->modified;
}
-bool KisDocument::saveNativeFormat(const QString & file)
-{
- Private::SafeSavingLocker locker(d);
- if (!locker.successfullyLocked()) return false;
-
- d->lastErrorMessage.clear();
- //dbgUI <<"Saving to store";
-
- KoStore::Backend backend = KoStore::Auto;
- if (d->specialOutputFlag == SaveAsDirectoryStore) {
- backend = KoStore::Directory;
- dbgUI << "Saving as uncompressed XML, using directory store.";
- }
- else if (d->specialOutputFlag == SaveAsFlatXML) {
- 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;
- }
-
- 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;
- return false;
- }
-
- bool result = false;
-
- if (!d->isAutosaving) {
- KisAsyncActionFeedback f(i18n("Saving document..."), 0);
- result = f.runAction(std::bind(&KisDocument::saveNativeFormatCalligra, this, store));
- } else {
- result = saveNativeFormatCalligra(store);
- }
- return result;
-}
-
-bool KisDocument::saveNativeFormatCalligra(KoStore *store)
-{
- dbgUI << "Saving root";
- if (store->open("root")) {
- KoStoreDevice dev(store);
- if (!saveToStream(&dev) || !store->close()) {
- 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;
- }
- 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())
- warnUI << "wrote " << nwritten << "- expected" << s.size();
- return nwritten == (int)s.size();
-}
-
-// Called for embedded documents
-bool KisDocument::saveToStore(KoStore *_store, const QString & _path)
-{
- dbgUI << "Saving document to store" << _path;
-
- _store->pushDirectory();
- // Use the path as the internal url
- if (_path.startsWith(STORE_PROTOCOL))
- setUrl(QUrl(_path));
- else // ugly hack to pass a relative URI
- setUrl(QUrl(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();
-
- 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);
- return QPixmap::fromImage(d->image->convertToQImage(newSize, 0));
+ QPixmap px = QPixmap::fromImage(d->image->convertToQImage(newSize, 0));
+ if (px.size() == QSize(0,0)) {
+ px = QPixmap(newSize);
+ QPainter gc(&px);
+ QBrush checkBrush = QBrush(KisCanvasWidgetBase::createCheckersImage(newSize.width() / 5));
+ gc.fillRect(px.rect(), checkBrush);
+ gc.end();
+ }
}
return QPixmap(size);
}
-QString KisDocument::autoSaveFile(const QString & path) const
+QString KisDocument::generateAutoSaveFileName(const QString & path) const
{
QString retval;
// Using the extension allows to avoid relying on the mime magic when opening
const QString extension (".kra");
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-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).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-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension);
#endif
} else {
QFileInfo fi(path);
QString dir = fi.absolutePath();
QString filename = fi.fileName();
retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension);
}
+
+ //qDebug() << "generateAutoSaveFileName() for path" << path << ":" << retval;
return retval;
}
bool KisDocument::importDocument(const QUrl &_url)
{
bool ret;
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) {
dbgUI << "success, resetting url";
resetURL();
setTitleModified();
}
d->isImporting = false;
return ret;
}
bool KisDocument::openUrl(const QUrl &_url, KisDocument::OpenUrlFlags flags)
{
if (!_url.isLocalFile()) {
- qDebug() << "not a local file" << _url;
return false;
}
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;
}
QUrl url(_url);
bool autosaveOpened = false;
- d->isLoading = true;
if (url.isLocalFile() && !fileBatchMode()) {
QString file = url.toLocalFile();
- QString asf = autoSaveFile(file);
+ QString asf = generateAutoSaveFileName(file);
if (QFile::exists(asf)) {
KisApplication *kisApp = static_cast<KisApplication*>(qApp);
kisApp->hideSplashScreen();
//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
QFileInfo fi(url.toLocalFile());
setReadWrite(fi.isWritable());
}
}
return ret;
}
bool KisDocument::openFile()
{
//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;
+ QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath()));
return false;
}
- QApplication::setOverrideCursor(Qt::WaitCursor);
-
- d->specialOutputFlag = 0;
-
QString filename = localFilePath();
QString typeName = mimeType();
if (typeName.isEmpty()) {
typeName = KisMimeDatabase::mimeTypeForFile(filename);
}
//qDebug() << "mimetypes 4:" << typeName;
// Allow to open backup files, don't keep the mimetype application/x-trash.
if (typeName == "application/x-trash") {
QString path = filename;
while (path.length() > 0) {
path.chop(1);
typeName = KisMimeDatabase::mimeTypeForFile(path);
//qDebug() << "\t" << path << typeName;
if (!typeName.isEmpty()) {
break;
}
}
//qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName;
}
dbgUI << localFilePath() << "type:" << typeName;
- QString importedFile = localFilePath();
-
setFileProgressUpdater(i18n("Opening Document"));
- if (!isNativeFormat(typeName.toLatin1())) {
- KisImportExportFilter::ConversionStatus status;
-
- importedFile = d->importExportManager->importDocument(localFilePath(), typeName, status);
- if (status != KisImportExportFilter::OK) {
- QApplication::restoreOverrideCursor();
-
- QString msg = KisImportExportFilter::conversionStatusString(status);
-
- 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;
- clearFileProgressUpdater();
- return false;
- }
- d->isEmpty = false;
- //qDebug() << "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
+ KisImportExportFilter::ConversionStatus status;
- // remove temp file - uncomment this to debug import filters
- if (!importedFile.isEmpty()) {
-#ifndef NDEBUG
- if (!getenv("CALLIGRA_DEBUG_FILTERS"))
-#endif
- QFile::remove(importedFile);
+ status = d->importExportManager->importDocument(localFilePath(), typeName);
+ if (status != KisImportExportFilter::OK) {
+ QString msg = KisImportExportFilter::conversionStatusString(status);
+ if (!msg.isEmpty()) {
+ QString errorMsg(i18n("Could not open %2.\nReason: %1.\n%3", msg, prettyPathOrUrl(), errorMessage()));
+ QMessageBox::critical(0, i18nc("@title:window", "Krita"), errorMsg);
}
+ clearFileProgressUpdater();
+ return false;
}
- if (ok) {
- setMimeTypeAfterLoading(typeName);
- emit sigLoadingFinished();
- }
+ setMimeTypeAfterLoading(typeName);
+ emit sigLoadingFinished();
if (!d->suppressProgress && d->progressUpdater) {
QPointer<KoUpdater> updater = d->progressUpdater->startSubtask(1, "clear undo stack");
updater->setProgress(0);
undoStack()->clear();
updater->setProgress(100);
clearFileProgressUpdater();
} else {
undoStack()->clear();
}
- d->isLoading = false;
- return ok;
+ return true;
}
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)
+
+bool KisDocument::loadNativeFormat(const QString & file_)
{
- //dbgUI <<"Trying to open" << filename;
+ return openUrl(QUrl::fromLocalFile(file_));
+}
- if (!store->open(filename)) {
- 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) {
- 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;
- }
- dbgUI << "File" << filename << " loaded and parsed";
- return true;
+void KisDocument::setModified()
+{
+ d->modified = true;
}
-bool KisDocument::loadNativeFormat(const QString & file_)
+void KisDocument::setModified(bool mod)
{
- 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;
- }
+ if (mod) {
+ updateEditingTime(false);
}
- QApplication::setOverrideCursor(Qt::WaitCursor);
-
- dbgUI << file;
+ if (d->isAutosaving) // ignore setModified calls due to autosaving
+ return;
- 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;
- }
+ if ( !d->readwrite && d->modified ) {
+ errKrita << "Can't set a read-only document to 'modified' !" << endl;
+ return;
+ }
- 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 ?
- //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 {
- 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 {
- 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 {
- //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 {
- dbgKrita << "couldn't open " << url;
- }
-
- _store->pushDirectory();
- // Store as document URL
- if (url.startsWith(STORE_PROTOCOL)) {
- setUrl(QUrl::fromUserInput(url));
- } else {
- setUrl(QUrl(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 (mod) {
- updateEditingTime(false);
- }
-
- if (d->isAutosaving) // ignore setModified calls due to autosaving
- return;
-
- if ( !d->readwrite && d->modified ) {
- errKrita << "Can't set a read-only document to 'modified' !" << endl;
- return;
- }
-
- //dbgUI<<" url:" << url.path();
- //dbgUI<<" 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);
+ setAutoSaveDelay(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);
}
void KisDocument::updateEditingTime(bool forceStoreElapsed)
{
QDateTime now = QDateTime::currentDateTime();
int firstModDelta = d->firstMod.secsTo(now);
int lastModDelta = d->lastMod.secsTo(now);
if (lastModDelta > 30) {
d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod)));
d->firstMod = now;
} else if (firstModDelta > 60 || forceStoreElapsed) {
d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta));
d->firstMod = now;
}
d->lastMod = now;
}
QString KisDocument::prettyPathOrUrl() const
{
QString _url(url().toDisplayString());
#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->image->blockUpdates();
- d->kraLoader->loadBinaryData(store, d->image, url().url(), isStoredExtern());
- d->image->unblockUpdates();
- 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)
-{
- d->kraSaver->saveKeyframes(store, url().url(), isStoredExtern());
- d->kraSaver->saveBinaryData(store, d->image, url().url(), isStoredExtern(), d->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("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;
- KisImageSP image;
-
- 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->setImageAndInitIdleWatcher(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 {
- QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open %1\nReason: %2", localFilePath(), errorMessage()));
- }
-}
-
-bool KisDocument::isLoading() const
-{
- return d->isLoading;
-}
-
void KisDocument::removeAutoSaveFiles()
{
+ //qDebug() << "removeAutoSaveFiles";
// Eliminate any auto-save file
- QString asf = autoSaveFile(localFilePath()); // the one in the current dir
- if (QFile::exists(asf))
+ QString asf = generateAutoSaveFileName(localFilePath()); // the one in the current dir
+ //qDebug() << "\tfilename:" << asf << "exists:" << QFile::exists(asf);
+ if (QFile::exists(asf)) {
+ //qDebug() << "\tremoving autosavefile" << asf;
QFile::remove(asf);
- asf = autoSaveFile(QString()); // and the one in $HOME
- if (QFile::exists(asf))
+ }
+ asf = generateAutoSaveFileName(QString()); // and the one in $HOME
+ //qDebug() << "Autsavefile in $home" << asf;
+ if (QFile::exists(asf)) {
+ //qDebug() << "\tremoving autsavefile 2" << 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;
- //dbgUI<<"="<<d->storeInternal<<" doc:"<<url().url();
-}
-
-bool KisDocument::hasExternURL() const
-{
- return !url().scheme().isEmpty()
- && url().scheme() != STORE_PROTOCOL
- && url().scheme() != 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)
+void KisDocument::setBackupFile(bool saveBackup)
{
- d->pageLayout = pageLayout;
+ d->backupFile = saveBackup;
}
KoUnit KisDocument::unit() const
{
return d->unit;
}
void KisDocument::setUnit(const KoUnit &unit)
{
if (d->unit != unit) {
d->unit = unit;
emit unitChanged(unit);
}
}
KUndo2Stack *KisDocument::undoStack()
{
return d->undoStack;
}
KisImportExportManager *KisDocument::importExportManager() const
{
return d->importExportManager;
}
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::clearUndoHistory()
{
d->undoStack->clear();
}
KisGridConfig KisDocument::gridConfig() const
{
return d->gridConfig;
}
void KisDocument::setGridConfig(const KisGridConfig &config)
{
d->gridConfig = config;
}
const KisGuidesConfig& KisDocument::guidesConfig() const
{
return d->guidesConfig;
}
void KisDocument::setGuidesConfig(const KisGuidesConfig &data)
{
if (d->guidesConfig == data) return;
d->guidesConfig = data;
emit sigGuidesConfigChanged(d->guidesConfig);
}
-bool KisDocument::isEmpty() const
-{
- return d->isEmpty;
-}
-
-void KisDocument::setEmpty()
-{
- d->isEmpty = true;
-}
-
-
-// static
-int KisDocument::defaultAutoSave()
-{
- return 300;
-}
-
void KisDocument::resetURL() {
setUrl(QUrl());
setLocalFilePath(QString());
}
-int KisDocument::pageCount() const {
- return 1;
-}
-
KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const
{
return new KoDocumentInfoDlg(parent, docInfo);
}
bool KisDocument::isReadWrite() const
{
return d->readwrite;
}
QUrl KisDocument::url() const
{
return d->m_url;
}
bool KisDocument::closeUrl(bool promptToSave)
{
if (promptToSave) {
- if ( d->document->isReadWrite() && d->document->isModified()) {
+ if ( isReadWrite() && isModified()) {
Q_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 QUrl &kurl, KisPropertiesConfigurationSP exportConfiguration)
-{
- if (!kurl.isValid())
- {
- errKrita << "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(exportConfiguration); // 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 = QUrl();
- d->m_originalFilePath.clear();
- }
-
- return result;
-}
-
-bool KisDocument::save(KisPropertiesConfigurationSP exportConfiguration)
-{
- d->m_saveOk = false;
- if ( d->m_file.isEmpty() ) { // document was created empty
- d->prepareSaving();
- }
-
- updateEditingTime(true);
-
- d->document->setFileProgressProxy();
- d->document->setUrl(url());
-
- bool ok = d->document->saveFile(exportConfiguration);
-
- d->document->clearFileProgressProxy();
-
- if (ok) {
- return saveToUrl();
- }
- else {
- emit canceled(QString());
- }
- return false;
-}
-
-
-bool KisDocument::waitSaveComplete()
-{
- return d->m_saveOk;
-}
-
void KisDocument::setUrl(const QUrl &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 = QUrl();
- d->m_originalFilePath.clear();
- return true; // Nothing to do
- }
- return false;
-}
-
-
bool KisDocument::openUrlInternal(const QUrl &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();
- }
+ bool ret;
+ // set the mimetype only if it was not already set (for example, by the host application)
+ if (d->mimeType.isEmpty()) {
+ // get the mimetype of the file
+ // using findByUrl() to avoid another string -> url conversion
+ QString mime = KisMimeDatabase::mimeTypeForFile(d->m_url.toLocalFile());
+ d->mimeType = mime.toLocal8Bit();
+ d->m_bAutoDetectedMime = true;
+ }
+ setFileProgressProxy();
+ setUrl(d->m_url);
+ ret = openFile();
+ clearFileProgressProxy();
+ if (ret) {
+ emit completed();
+ } else {
+ emit canceled(QString());
+ }
+ return ret;
+ }
return false;
}
-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);
KisConfig cfg;
KisImageSP 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()));
+ connect(image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection);
image->setResolution(imageResolution, imageResolution);
image->assignImageProfile(cs->profile());
documentInfo()->setAboutInfo("title", name);
if (name != i18n("Unnamed") && !name.isEmpty()) {
setUrl(QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) + '/' + name + ".kra"));
}
documentInfo()->setAboutInfo("abstract", 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);
} 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;
Q_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<KisPaintingAssistantSP> KisDocument::assistants() const
{
return d->assistants;
}
void KisDocument::setAssistants(const QList<KisPaintingAssistantSP> value)
{
d->assistants = value;
}
void KisDocument::setPreActivatedNode(KisNodeSP activatedNode)
{
d->preActivatedNode = activatedNode;
}
KisNodeSP KisDocument::preActivatedNode() const
{
return d->preActivatedNode;
}
-void KisDocument::prepareForImport()
-{
- /* TODO: remove this function? I kept it because it might be useful for
- * other kind of preparing, but currently it was checking on d->nserver
- * being null and then calling init() if it was, but the document is always
- * initialized in the constructor (and init() does other things too).
- * Moreover, nserver cannot be nulled by some external call.*/
-}
-
void KisDocument::setFileProgressUpdater(const QString &text)
{
- d->suppressProgress = d->importExportManager->getBatchMode();
+ d->suppressProgress = d->importExportManager->batchMode();
if (!d->suppressProgress) {
d->progressUpdater = new KoProgressUpdater(d->progressProxy, KoProgressUpdater::Unthreaded);
d->progressUpdater->start(100, text);
d->importExportManager->setProgresUpdater(d->progressUpdater);
connect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int)));
connect(KisPart::instance()->currentMainwindow(), SIGNAL(sigProgressCanceled()), this, SIGNAL(sigProgressCanceled()));
}
}
void KisDocument::clearFileProgressUpdater()
{
if (!d->suppressProgress && d->progressUpdater) {
disconnect(KisPart::instance()->currentMainwindow(), SIGNAL(sigProgressCanceled()), this, SIGNAL(sigProgressCanceled()));
disconnect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int)));
delete d->progressUpdater;
d->importExportManager->setProgresUpdater(0);
d->progressUpdater = 0;
}
}
void KisDocument::setFileProgressProxy()
{
- if (!d->progressProxy && !d->importExportManager->getBatchMode()) {
+ if (!d->progressProxy && !d->importExportManager->batchMode()) {
d->fileProgressProxy = progressProxy();
} else {
d->fileProgressProxy = 0;
}
}
void KisDocument::clearFileProgressProxy()
{
if (d->fileProgressProxy) {
setProgressProxy(0);
delete d->fileProgressProxy;
d->fileProgressProxy = 0;
}
}
KisImageWSP KisDocument::image() const
{
return d->image;
}
void KisDocument::setCurrentImage(KisImageSP image)
{
- if (!image) return;
-
if (d->image) {
// Disconnect existing sig/slot connections
d->image->disconnect(this);
d->shapeController->setImage(0);
+ d->image = 0;
}
+
+ if (!image) return;
+
d->setImageAndInitIdleWatcher(image);
d->shapeController->setImage(image);
setModified(false);
- connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()));
+ connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection);
d->image->initialRefreshGraph();
- setAutoSave(KisConfig().autoSaveInterval());
-}
-
-void KisDocument::initEmpty()
-{
- KisConfig cfg;
- const KoColorSpace * rgb = KoColorSpaceRegistry::instance()->rgb8();
- newImage("", cfg.defImageWidth(), cfg.defImageHeight(), rgb);
+ setAutoSaveDelay(KisConfig().autoSaveInterval());
}
void KisDocument::setImageModified()
{
setModified(true);
}
KisUndoStore* KisDocument::createUndoStore()
{
return new KisDocumentUndoStore(this);
}
bool KisDocument::isAutosaving() const
{
return d->isAutosaving;
}
diff --git a/libs/ui/KisDocument.h b/libs/ui/KisDocument.h
index e0380ee336..be662dc9b6 100644
--- a/libs/ui/KisDocument.h
+++ b/libs/ui/KisDocument.h
@@ -1,828 +1,593 @@
/* 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.
*/
#ifndef KISDOCUMENT_H
#define KISDOCUMENT_H
#include <QDateTime>
#include <QTransform>
#include <QList>
#include <klocalizedstring.h>
#include <KoPageLayout.h>
#include <KoDocumentBase.h>
#include <kundo2stack.h>
#include <kis_properties_configuration.h>
#include <kis_types.h>
#include <kis_painting_assistant.h>
#include <kis_debug.h>
#include "kritaui_export.h"
class QString;
class KUndo2Command;
class KoUnit;
class KoColor;
class KoColorSpace;
class KoShapeBasedDocumentBase;
class KoShapeLayer;
class KoStore;
class KoOdfReadStore;
class KoDocumentInfo;
class KoProgressUpdater;
class KoProgressProxy;
class KoDocumentInfoDlg;
class KisImportExportManager;
class KisUndoStore;
class KisPaintingAssistant;
class KisPart;
class KisGridConfig;
class KisGuidesConfig;
class QDomDocument;
class KisPart;
#define KIS_MIME_TYPE "application/x-krita"
/**
* The %Calligra document class
*
* This class provides some functionality each %Calligra document should have.
*
* @short The %Calligra document class
*/
class KRITAUI_EXPORT KisDocument : public QObject, public KoDocumentBase
{
Q_OBJECT
- Q_PROPERTY(bool backupFile READ backupFile WRITE setBackupFile)
- Q_PROPERTY(int pageCount READ pageCount)
protected:
explicit KisDocument();
public:
enum OpenUrlFlags {
OPEN_URL_FLAG_NONE = 1 << 0,
OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES = 1 << 1,
};
/**
* Destructor.
*
* The destructor does not delete any attached KisView objects and it does not
* delete the attached widget as returned by widget().
*/
virtual ~KisDocument();
/**
* @brief reload Reloads the document from the original url
* @return the result of loading the document
*/
bool reload();
/**
* @brief openUrl Open an URL
* @param url The URL to open
* @param flags Control specific behavior
* @return success status
*/
bool openUrl(const QUrl &url, OpenUrlFlags flags = OPEN_URL_FLAG_NONE);
/**
* Opens the document given by @p url, without storing the URL
* in the KisDocument.
* Call this instead of openUrl() to implement KisMainWindow's
* File --> Import feature.
*
* @note This will call openUrl(). To differentiate this from an ordinary
* Open operation (in any reimplementation of openUrl() or openFile())
* call isImporting().
*/
bool importDocument(const QUrl &url);
/**
* Saves the document as @p url without changing the state of the
* KisDocument (URL, modified flag etc.). Call this instead of
* KisParts::ReadWritePart::saveAs() to implement KisMainWindow's
* File --> Export feature.
*
* @note This will call KisDocument::saveAs(). To differentiate this
* from an ordinary Save operation (in any reimplementation of
* saveFile()) call isExporting().
*/
bool exportDocument(const QUrl &url, KisPropertiesConfigurationSP exportConfiguration = 0);
/**
* @brief Sets whether the document can be edited or is read only.
*
* This recursively applied to all child documents and
* KisView::updateReadWrite is called for every attached
* view.
*/
void setReadWrite(bool readwrite = true);
/**
* To be preferred when a document exists. It is fast when calling
* it multiple times since it caches the result that readNativeFormatMimeType()
* delivers.
* This comes from the X-KDE-NativeMimeType key in the .desktop file.
*/
static QByteArray nativeFormatMimeType() { return KIS_MIME_TYPE; }
/// Checks whether a given mimetype can be handled natively.
bool isNativeFormat(const QByteArray& mimetype) const;
/// Returns a list of the mimetypes considered "native", i.e. which can
/// be saved by KisDocument without a filter, in *addition* to the main one
static QStringList extraNativeMimeTypes() { return QStringList() << KIS_MIME_TYPE; }
-
- /// Enum values used by specialOutputFlag - note that it's a bitfield for supportedSpecialFormats
- enum { /*SaveAsCalligra1dot1 = 1,*/ // old and removed
- SaveAsDirectoryStore = 2,
- SaveAsFlatXML = 4,
- SaveEncrypted = 8
- // bitfield! next value is 16
- };
-
- /**
- * Return the set of SupportedSpecialFormats that the application wants to
- * offer in the "Save" file dialog.
- */
- virtual int supportedSpecialFormats() const;
-
/**
* Returns the actual mimetype of the document
*/
QByteArray mimeType() const;
/**
* @brief Sets the mime type for the document.
*
* When choosing "save as" this is also the mime type
* selected by default.
*/
void setMimeType(const QByteArray & mimeType);
/**
* @brief Set the format in which the document should be saved.
*
* This is called on loading, and in "save as", so you shouldn't
* have to call it.
*
* @param mimeType the mime type (format) to use.
- * @param specialOutputFlag is for "save as older version" etc.
*/
- void setOutputMimeType(const QByteArray & mimeType, int specialOutputFlag = 0);
+ void setOutputMimeType(const QByteArray & mimeType);
QByteArray outputMimeType() const;
- int specialOutputFlag() const;
-
- /**
- * Returns true if this document was the result of opening a foreign
- * file format and if the user hasn't yet saved the document (in any
- * format).
- *
- * Used by KisMainWindow to warn the user when s/he lazily presses
- * CTRL+S to save in the same foreign format, putting all his/her
- * formatting at risk (normally an export confirmation only comes up
- * with Save As).
- *
- * @param exporting specifies whether this is the setting for a
- * File --> Export or File --> Save/Save As operation.
- */
- bool confirmNonNativeSave(const bool exporting) const;
- void setConfirmNonNativeSave(const bool exporting, const bool on);
-
/**
* @return true if file operations should inhibit the option dialog
*/
bool fileBatchMode() const;
/**
* @param batchMode if true, do not show the option dialog for file operations.
*/
void setFileBatchMode(const bool batchMode);
/**
* Sets the error message to be shown to the user (use i18n()!)
* when loading or saving fails.
* If you asked the user about something and they chose "Cancel",
*/
void setErrorMessage(const QString& errMsg);
/**
* Return the last error message. Usually KisDocument takes care of
* showing it; this method is mostly provided for non-interactive use.
*/
QString errorMessage() const;
-
- /**
- * Show the last error message in a message box.
- * The dialog box will mention a loading problem.
- * openUrl/openFile takes care of doing it, but not loadNativeFormat itself,
- * so this is often called after loadNativeFormat returned false.
- */
- void showLoadingErrorDialog();
-
-
/**
* @brief Generates a preview picture of the document
* @note The preview is used in the File Dialog and also to create the Thumbnail
*/
QPixmap generatePreview(const QSize& size);
/**
* Tells the document that its title has been modified, either because
* the modified status changes (this is done by setModified() ) or
* because the URL or the document-info's title changed.
*/
void setTitleModified();
- /**
- * @return true if the document is empty.
- */
- virtual bool isEmpty() const;
-
/**
* @brief Sets the document to empty.
*
* Used after loading a template
* (which is not empty, but not the user's input).
*
* @see isEmpty()
*/
- void setEmpty();
-
- /**
- * @brief Loads a document from a store.
- *
- * You should never have to reimplement.
- *
- * @param store The store to load from
- * @param url An internal url, like tar:/1/2
- */
- bool loadFromStore(KoStore *store, const QString& url);
-
- /// Unused
- virtual bool loadOdf(KoOdfReadStore & odfStore);
- /// Unused
- virtual bool saveOdf(SavingContext &documentContext);
-
- /**
- * @brief Saves a sub-document to a store.
- *
- * You should not have to reimplement this.
- */
- virtual bool saveToStore(KoStore *store, const QString& path);
-
- /**
- * Reimplement this method to load the contents of your Calligra document,
- * from the XML document. This is for the pre-Oasis file format (maindoc.xml).
- */
- virtual bool loadXML(const KoXmlDocument & doc, KoStore *store);
-
- /**
- * Reimplement this to save the contents of the %Calligra document into
- * a QDomDocument. The framework takes care of saving it to the store.
- */
- QDomDocument saveXML();
+ void setEmpty(bool empty = true);
/**
* Return a correctly created QDomDocument for this KisDocument,
* including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element.
* @param tagName the name of the tag for the root element
* @param version the DTD version (usually the application's version).
*/
QDomDocument createDomDocument(const QString& tagName, const QString& version) const;
/**
* Return a correctly created QDomDocument for an old (1.3-style) %Calligra document,
* including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element.
* This static method can be used e.g. by filters.
* @param appName the app's instance name, e.g. words, kspread, kpresenter etc.
* @param tagName the name of the tag for the root element, e.g. DOC for words/kpresenter.
* @param version the DTD version (usually the application's version).
*/
static QDomDocument createDomDocument(const QString& appName, const QString& tagName, const QString& version);
- /**
- * The first thing to do in loadOasis is get hold of the office:body tag, then its child.
- * If the child isn't the expected one, the error message can indicate what it is instead.
- * This method returns a translated name for the type of document,
- * e.g. i18n("Word Processing") for office:text.
- */
- static QString tagNameToDocumentType(const QString& localName);
-
- /**
+ /**
* Loads a document in the native format from a given URL.
* Reimplement if your native format isn't XML.
*
* @param file the file to load - usually KReadOnlyPart::m_file or the result of a filter
*/
bool loadNativeFormat(const QString & file);
- /**
- * Saves the document in native format, to a given file
- * You should never have to reimplement.
- * Made public for writing templates.
- */
- bool saveNativeFormat(const QString & file);
-
- /**
- * Saves the document in the native format to the given store.
- */
- bool saveNativeFormatCalligra(KoStore *store);
-
/**
* Activate/deactivate/configure the autosave feature.
* @param delay in seconds, 0 to disable
*/
- void setAutoSave(int delay);
-
- /**
- * Set whether the next openUrl call should show error message boxes in case
- * of errors. This is usually the case, but e.g. not when generating thumbnail
- * previews.
- */
- void setAutoErrorHandlingEnabled(bool b);
-
- /**
- * Checks whether error message boxes should be shown.
- */
- bool isAutoErrorHandlingEnabled() const;
-
- /**
- * Retrieve the default value for autosave in seconds.
- * Called by the applications to use the correct default in their config
- */
- static int defaultAutoSave();
+ void setAutoSaveDelay(int delay);
/**
* @return the information concerning this document.
* @see KoDocumentInfo
*/
KoDocumentInfo *documentInfo() const;
/**
* @return the object to report progress to.
*
* This is only not zero if loading or saving is in progress.
*
* One can add more KoUpdaters to it to make the progress reporting more
* accurate. If no active progress reporter is present, 0 is returned.
**/
KoProgressUpdater *progressUpdater() const;
/**
* Set a custom progress proxy to use to report loading
* progress to.
*/
void setProgressProxy(KoProgressProxy *progressProxy);
KoProgressProxy* progressProxy() const;
- /**
- * Return true if url() is a real filename, false if url() is
- * an internal url in the store, like "tar:/..."
- */
- virtual bool isStoredExtern() const;
-
- /**
- * @return the page layout associated with this document (margins, pageSize, etc).
- * Override this if you want to provide different sized pages.
- *
- * @see KoPageLayout
- */
- KoPageLayout pageLayout(int pageNumber = 0) const;
- void setPageLayout(const KoPageLayout &pageLayout);
-
/**
* Performs a cleanup of unneeded backup files
*/
void removeAutoSaveFiles();
- void setBackupFile(bool _b);
-
- bool backupFile()const;
-
/**
- * Returns true if this document or any of its internal child documents are modified.
+ * @brief setBackupFile enable/disable saving a backup of the file on saving
+ * @param saveBackup if true, Krita will save a backup of the file
*/
- bool isModified() const;
+ void setBackupFile(bool saveBackup);
/**
- * Returns true during loading (openUrl can be asynchronous)
- */
- bool isLoading() const;
-
- /**
- * Sets the backup path of the document
- */
- void setBackupPath(const QString & _path);
-
- /**
- * @return path to the backup document
+ * Returns true if this document or any of its internal child documents are modified.
*/
- QString backupPath()const;
+ bool isModified() const;
/**
* @return caption of the document
*
* Caption is of the form "[title] - [url]",
* built out of the document info (title) and pretty-printed
* document URL.
* If the title is not present, only the URL it returned.
*/
QString caption() const;
/**
* Sets the document URL to empty URL
* KParts doesn't allow this, but %Calligra apps have e.g. templates
* After using loadNativeFormat on a template, one wants
* to set the url to QUrl()
*/
void resetURL();
- /**
- * Set when you want an external embedded document to be stored internally
- */
- void setStoreInternal(bool i);
-
- /**
- * @return true when external embedded documents are stored internally
- */
- bool storeInternal() const;
-
- bool hasExternURL() const;
-
/**
* @internal (public for KisMainWindow)
*/
void setMimeTypeAfterLoading(const QString& mimeType);
- /**
- * @return returns the number of pages in the document.
- */
- virtual int pageCount() const;
-
/**
* Returns the unit used to display all measures/distances.
*/
KoUnit unit() const;
/**
* Sets the unit used to display all measures/distances.
*/
void setUnit(const KoUnit &unit);
- /**
- * Save the unit to the settings writer
- *
- * @param settingsWriter
- */
- bool loadNativeFormatFromByteArray(QByteArray &data);
-
KisGridConfig gridConfig() const;
void setGridConfig(const KisGridConfig &config);
/// returns the guides data for this document.
const KisGuidesConfig& guidesConfig() const;
void setGuidesConfig(const KisGuidesConfig &data);
void clearUndoHistory();
/**
* Sets the modified flag on the document. This means that it has
* to be saved or not before deleting it.
*/
void setModified(bool _mod);
void updateEditingTime(bool forceStoreElapsed);
- /**
- * Initialize an empty document using default values
- */
- void initEmpty();
-
/**
* Returns the global undo stack
*/
KUndo2Stack *undoStack();
/**
* @brief importExportManager gives access to the internal import/export manager
* @return the document's import/export manager
*/
KisImportExportManager *importExportManager() const;
public Q_SLOTS:
/**
* Adds a command to the undo stack and executes it by calling the redo() function.
* @param command command to add to the undo stack
*/
void addCommand(KUndo2Command *command);
/**
* Begins recording of a macro command. At the end endMacro needs to be called.
* @param text command description
*/
void beginMacro(const KUndo2MagicString &text);
/**
* Ends the recording of a macro command.
*/
void endMacro();
Q_SIGNALS:
/**
* This signal is emitted when the unit is changed by setUnit().
* It is common to connect views to it, in order to change the displayed units
* (e.g. in the rulers)
*/
void unitChanged(const KoUnit &unit);
/**
* Progress info while loading or saving. The value is in percents (i.e. a number between 0 and 100)
* Your KisDocument-derived class should emit the signal now and then during load/save.
* KisMainWindow will take care of displaying a progress bar automatically.
*/
void sigProgress(int value);
/**
* Progress cancel button pressed
* This is emitted by KisDocument
*/
void sigProgressCanceled();
/**
* Emitted e.g. at the beginning of a save operation
* This is emitted by KisDocument and used by KisView to display a statusbar message
*/
void statusBarMessage(const QString& text);
/**
* Emitted e.g. at the end of a save operation
* This is emitted by KisDocument and used by KisView to clear the statusbar message
*/
void clearStatusBarMessage();
/**
* Emitted when the document is modified
*/
void modified(bool);
void titleModified(const QString &caption, bool isModified);
void sigLoadingFinished();
void sigSavingFinished();
void sigGuidesConfigChanged(const KisGuidesConfig &config);
private:
friend class KisPart;
friend class SafeSavingLocker;
/**
* Generate a name for the document.
*/
QString newObjectName();
- QString autoSaveFile(const QString & path) const;
+ QString generateAutoSaveFileName(const QString & path) const;
/**
* Loads a document
*
* Applies a filter if necessary, and calls loadNativeFormat in any case
* You should not have to reimplement, except for very special cases.
*
* NOTE: this method also creates a new KisView instance!
*
* This method is called from the KReadOnlyPart::openUrl method.
*/
bool openFile();
/**
* Saves a document
*
- * Applies a filter if necessary, and calls saveNativeFormat in any case
+ * Applies a filter if necessary, and calls exportDocument in any case
* You should not have to reimplement, except for very special cases.
*/
- bool saveFile(KisPropertiesConfigurationSP exportConfiguration = 0);
-
- /**
- * Overload this function if you have to load additional files
- * from a store. This function is called after loadXML()
- * and after loadChildren() have been called.
- */
- bool completeLoading(KoStore *store);
-
- /**
- * If you want to write additional files to a store,
- * then you must do it here.
- * In the implementation, you should prepend the document
- * url (using url().url()) before the filename, so that everything is kept relative
- * to this document. For instance it will produce urls such as
- * tar:/1/pictures/picture0.png, if the doc url is tar:/1
- * But do this ONLY if the document is not stored extern (see isStoredExtern() ).
- * If it is, then the pictures should be saved to tar:/pictures.
- */
- bool completeSaving(KoStore *store);
+ bool saveFile(const QString &filePath, KisPropertiesConfigurationSP exportConfiguration = 0);
/** @internal */
void setModified();
/**
* Returns whether or not the current openUrl() or openFile() call is
* actually an import operation (like File --> Import).
* This is for informational purposes only.
*/
bool isImporting() const;
/**
* Returns whether or not the current saveFile() call is actually an export
* operation (like File --> Export).
* If this function returns true during saveFile() and you are changing
* some sort of state, you _must_ restore it before the end of saveFile();
* otherwise, File --> Export will not work properly.
*/
bool isExporting() const;
- /**
- * Legacy method from KoDocumentBase. Don't use it anywhere
- * outside KisDocument!
- */
+public:
+
bool isAutosaving() const;
public:
QString localFilePath() const;
void setLocalFilePath( const QString &localFilePath );
KoDocumentInfoDlg* createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const;
bool isReadWrite() const;
QUrl url() const;
void setUrl(const QUrl &url);
bool closeUrl(bool promptToSave = true);
bool saveAs(const QUrl &url, KisPropertiesConfigurationSP exportConfigration = 0);
-public Q_SLOTS:
-
- bool save(KisPropertiesConfigurationSP exportConfiguration = 0);
- bool waitSaveComplete();
-
-Q_SIGNALS:
-
- void completed();
- void canceled(const QString &);
-
-private Q_SLOTS:
-
- void setImageModified();
-
- void slotAutoSave();
-
- /// Called by the undo stack when undo or redo is called
- void slotUndoStackIndexChanged(int idx);
-
-protected:
-
- bool oldLoadAndParse(KoStore *store, const QString& filename, KoXmlDocument& doc);
-
-public:
-
- /**
- * Create a new image that has this document as a parent and
- * replace the current image with this image.
- */
- bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, const QString &imageDescription, const double imageResolution);
-
/**
* Create a new image that has this document as a parent and
* replace the current image with this image.
*/
bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, bool backgroundAsLayer,
int numberOfLayers, const QString &imageDescription, const double imageResolution);
- /**
- * Create a new image that has this document as a parent and
- * replace the current image with this image.
- */
- KisImageWSP newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * colorspace);
KisImageWSP image() const;
-
- /**
- * Makes an otherwise empty document ready for import/export
- */
- void prepareForImport();
-
/**
* Adds progressproxy for file operations
*/
void setFileProgressProxy();
/**
* Clears progressproxy for file operations
*/
void clearFileProgressProxy();
/**
* Adds progressupdater for file operations
*/
void setFileProgressUpdater(const QString &text);
/**
* Clears progressupdater for file operations
*/
void clearFileProgressUpdater();
/**
* Set the current image to the specified image and turn undo on.
*/
void setCurrentImage(KisImageSP image);
KisUndoStore* createUndoStore();
/**
* The shape controller matches internal krita image layers with
* the flake shape hierarchy.
*/
KoShapeBasedDocumentBase * shapeController() const;
KoShapeLayer* shapeForNode(KisNodeSP layer) const;
/**
* @return a list of all layers that are active in all current views
*/
vKisNodeSP activeNodes() const;
/**
* set the list of nodes that were marked as currently active
*/
void setPreActivatedNode(KisNodeSP activatedNode);
/**
* @return the node that was set as active during loading
*/
KisNodeSP preActivatedNode() const;
QList<KisPaintingAssistantSP> assistants() const;
void setAssistants(const QList<KisPaintingAssistantSP> value);
-private:
+ bool save(KisPropertiesConfigurationSP exportConfiguration = 0);
- void init();
+Q_SIGNALS:
- bool saveToStream(QIODevice *dev);
+ void completed();
+ void canceled(const QString &);
- bool loadNativeFormatFromStoreInternal(KoStore *store);
+private Q_SLOTS:
- bool savePreview(KoStore *store);
+ void setImageModified();
+
+ void slotAutoSave();
+
+ /// Called by the undo stack when undo or redo is called
+ void slotUndoStackIndexChanged(int idx);
+
+
+private:
QString prettyPathOrUrl() const;
- bool saveToUrl();
bool openUrlInternal(const QUrl &url);
class Private;
Private *const d;
};
Q_DECLARE_METATYPE(KisDocument*)
#endif
diff --git a/libs/ui/KisFilterChain.cpp b/libs/ui/KisFilterChain.cpp
deleted file mode 100644
index 2a4e5fe5c8..0000000000
--- a/libs/ui/KisFilterChain.cpp
+++ /dev/null
@@ -1,366 +0,0 @@
-/* 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 "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 <kis_debug.h>
-
-#include <limits.h> // UINT_MAX
-#include <KisMimeDatabase.h>
-
-// 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)
- : KisShared()
- , m_manager(manager)
- , m_state(Beginning)
- , 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();
- 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();
-
- // 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();
- m_state = Middle;
- manageIO();
- }
-
- if (!m_chainLinks.current()) {
- 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();
- 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) {
- 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()
-{
- qDebug() << m_outputQueried << m_outputFile;
-
- if (m_outputQueried == File) {
- return m_outputFile;
- }
-
- else if (m_outputQueried != Nil) {
- 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;
-}
-
-void KisFilterChain::setOutputFile(const QString &outputFile)
-{
- m_outputQueried = File;
- m_outputFile = outputFile;
-}
-
-
-KisDocument *KisFilterChain::inputDocument()
-{
- if (m_inputQueried == Document) {
- return m_inputDocument;
- }
- else if (m_inputQueried) {
- warnFile << "You already asked for some different source.";
- return 0;
- }
-
- m_inputDocument = filterManagerKisDocument();
-
- m_inputQueried = Document;
- return m_inputDocument;
-}
-
-KisDocument* KisFilterChain::outputDocument()
-{
- if (m_outputQueried == Document)
- return m_outputDocument;
- else if (m_outputQueried != Nil) {
- 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 = KisPart::instance()->createDocument();
-
- m_outputQueried = Document;
- return m_outputDocument;
-}
-
-KisPropertiesConfigurationSP KisFilterChain::filterManagerExportConfiguration() const
-{
- return m_manager->exportConfiguration();
-}
-
-void KisFilterChain::prependChainLink(KisFilterEntrySP 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();
-}
-
-void KisFilterChain::manageIO()
-{
- m_inputQueried = Nil;
- m_outputQueried = Nil;
-
- 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;
-
- }
-
- 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) {
- 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) {
- 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
- }
-}
-
-int KisFilterChain::weight() const
-{
- return m_chainLinks.count();
-}
diff --git a/libs/ui/KisFilterChain.h b/libs/ui/KisFilterChain.h
deleted file mode 100644
index 1731cf63c1..0000000000
--- a/libs/ui/KisFilterChain.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/* 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.
-*/
-
-#ifndef KIS_FILTER_CHAIN_H
-#define KIS_FILTER_CHAIN_H
-
-#include <QHash>
-#include <QList>
-#include <QStringList>
-
-#include <KoStoreDevice.h>
-#include "kis_shared_ptr.h"
-
-#include "KisFilterEntry.h"
-#include "KisImportExportFilter.h"
-#include "KisFilterChainLinkList.h"
-
-#include "kis_shared.h"
-
-#include "kritaui_export.h"
-
-
-class QTemporaryFile;
-class KisImportExportManager;
-class KisDocument;
-
-namespace CalligraFilter
-{
- class Graph;
- class ChainLink;
- class Vertex;
- class Edge;
-}
-
-/**
- * @brief This class represents a chain of plain @ref KisImportExportFilter instances.
- *
- * @author Werner Trobin <trobin@kde.org>
- * @todo the class has no constructor and therefore cannot initialize its private class
- */
-class KRITAUI_EXPORT KisFilterChain : public KisShared
-{
-public:
-
- explicit KisFilterChain(const KisImportExportManager *manager);
- virtual ~KisFilterChain();
-
- /**
- * The filter manager returned may be 0!
- */
- const KisImportExportManager* manager() const {
- return m_manager;
- }
-
- /**
- * Starts the filtering process.
- * @return The return status of the conversion. KisImportExportFilter::OK
- * if everything is alright.
- */
- KisImportExportFilter::ConversionStatus invokeChain();
-
- /**
- * Tells the @ref KisFilterManager the output file of the
- * filter chain in case of an import operation. If it's
- * an empty QString we directly manipulated the document.
- */
- QString chainOutput() const;
-
- /**
- * Get the current file to read from. This part of the API
- * is for the filters in our chain.
- */
- QString inputFile();
-
- /**
- * Get the current file to write to. This part of the API
- * is for the filters in our chain.
- */
- QString outputFile();
- void setOutputFile(const QString &outputFile);
-
- /**
- * This method allows your filter to work directly on the
- * @ref KisDocument of the application.
- * This part of the API is for the filters in our chain.
- * @return The document containing the data. May return 0 on error.
- */
- KisDocument *inputDocument();
-
- /**
- * This method allows your filter to work directly on the
- * @ref KisDocument of the application.
- * This part of the API is for the filters in our chain.
- * @return The document you have to write to. May return 0 on error.
- */
- KisDocument *outputDocument();
-
- KisPropertiesConfigurationSP filterManagerExportConfiguration() const;
-
- /// returns the amount of filters this chain contains representing the weight
- int weight() const;
-
-private:
- // ### API for Calligra::Graph:
- // Construct a filter chain belonging to some KisFilterManager.
- // The parent filter manager may be 0.
-
- friend class CalligraFilter::Graph;
-
- void prependChainLink(KisFilterEntrySP filterEntry, const QByteArray& from, const QByteArray& to);
-
- // These methods are friends of KisFilterManager and provide access
- // to a private part of its API. As I don't want to include
- // koFilterManager.h in this header the direction is "int" here.
-
- friend class KisImportExportManager;
-
- QString filterManagerImportFile() const;
- QString filterManagerExportFile() const;
- KisDocument* filterManagerKisDocument() const;
- int filterManagerDirection() const;
-
- // Helper methods which keep track of all the temp files and documents,
- // and properly delete them as soon as they are not
- // needed anymore.
- void manageIO();
- void finalizeIO();
-
- bool createTempFile(QTemporaryFile** tempFile, bool autoDelete = true);
-
- void inputFileHelper(KisDocument* document, const QString& alternativeFile);
- void outputFileHelper(bool autoDelete);
-
- // "A whole is that which has beginning, middle, and end" - Aristotle
- // ...but we also need to signal "Done" state, Mr. Aristotle
- enum Whole { Beginning = 1, Middle = 2, End = 4, Done = 8 };
-
- // Don't copy or assign filter chains
- KisFilterChain(const KisFilterChain& rhs);
- KisFilterChain& operator=(const KisFilterChain& rhs);
-
- const KisImportExportManager* const m_manager;
-
- CalligraFilter::ChainLinkList m_chainLinks;
-
- // stuff needed for bookkeeping
- int m_state;
-
- QString m_inputFile; // Did we pass around plain files?
- QString m_outputFile;
-
- KisDocument* m_inputDocument; // ...or even documents?
- KisDocument* m_outputDocument;
-
- QTemporaryFile* m_inputTempFile;
- QTemporaryFile* m_outputTempFile;
-
- // These two flags keep track of the input/output the
- // filter (=user) asked for
- enum IOState { Nil, File, Document };
- IOState m_inputQueried, m_outputQueried;
-
- class Private;
- Private * const d;
-};
-
-#endif // __KO_FILTER_CHAIN_H__
diff --git a/libs/ui/KisFilterChainLink.cpp b/libs/ui/KisFilterChainLink.cpp
deleted file mode 100644
index 6768348378..0000000000
--- a/libs/ui/KisFilterChainLink.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/* 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 <kis_debug.h>
-#include "KisFilterEntry.h"
-#include "KisImportExportManager.h"
-#include "KoProgressUpdater.h"
-#include "KoUpdater.h"
-
-namespace
-{
- KoUpdater *createUpdater(KisFilterChainSP 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(KisFilterChainSP chain, KisFilterEntrySP 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()
- {
- if (!m_filterEntry) {
- errFile << "This filter entry is null. Strange stuff going on." << endl;
- return KisImportExportFilter::FilterEntryNull;
- }
-
- m_filter = m_filterEntry->createFilter(m_chain);
-
- if (!m_filter) {
- errFile << "Couldn't create the filter." << endl;
- return KisImportExportFilter::FilterCreationError;
- }
-
- Q_ASSERT(m_updater);
- if (m_updater) {
- // if there is an updater, use that for progress reporting
- m_filter->setUpdater(m_updater);
- }
-
- KisImportExportFilter::ConversionStatus status = m_filter->convert(m_from, m_to, m_chain->filterManagerExportConfiguration());
- delete m_filter;
- m_filter = 0;
- if (m_updater) {
- m_updater->setProgress(100);
- }
- return status;
- }
-
- void ChainLink::dump() const
- {
- dbgFile << " Link:" << m_filterEntry->loader()->fileName();
- }
-
-
-}
diff --git a/libs/ui/KisFilterChainLink.h b/libs/ui/KisFilterChainLink.h
deleted file mode 100644
index 36bce33cd4..0000000000
--- a/libs/ui/KisFilterChainLink.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* 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.
-*/
-#ifndef KISFILTERCHAINLINK_H
-#define KISFILTERCHAINLINK_H
-
-#include <KisFilterChain.h>
-
-class QByteArray;
-
-namespace CalligraFilter {
-
-/**
- * A small private helper class with represents one single filter
- * (one link of the chain)
- * @internal
- */
-class ChainLink
-{
-
-public:
- ChainLink(KisFilterChainSP chain, KisFilterEntrySP filterEntry,
- const QByteArray& from, const QByteArray& to);
-
- ~ChainLink();
-
- KisImportExportFilter::ConversionStatus invokeFilter();
-
- QByteArray from() const {
- return m_from;
- }
- QByteArray to() const {
- return m_to;
- }
-
- // debugging
- void dump() const;
-
- QPointer<KoUpdater> updater() const {
- return m_updater;
- }
-
-private:
- ChainLink(const ChainLink& rhs);
- ChainLink& operator=(const ChainLink& rhs);
-
- KisFilterChainSP m_chain;
- KisFilterEntrySP m_filterEntry;
- QByteArray m_from, m_to;
-
- // This hack is only needed due to crappy Microsoft design and
- // circular dependencies in their embedded files :}
- KisImportExportFilter *m_filter;
-
- QPointer<KoUpdater> const m_updater;
-};
-
-}
-#endif // KOFILTERCHAINLINK_H
diff --git a/libs/ui/KisFilterChainLinkList.cpp b/libs/ui/KisFilterChainLinkList.cpp
deleted file mode 100644
index 7dd123699f..0000000000
--- a/libs/ui/KisFilterChainLinkList.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/* This file is part of the Calligra libraries
- Copyright (C) 2009 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 "KisFilterChainLinkList.h"
-#include "KisFilterChainLink.h"
-
-namespace CalligraFilter {
-
- ChainLinkList::ChainLinkList() {}
-
- ChainLinkList::~ChainLinkList()
- {
- deleteAll();
- }
-
- void ChainLinkList::deleteAll()
- {
- while(!m_chainLinks.isEmpty()) {
- delete m_chainLinks.takeFirst();
- }
- }
-
- int ChainLinkList::count() const
- {
- return m_chainLinks.count();
- }
-
- ChainLink* ChainLinkList::current() const
- {
- // use value() because m_current might be out of range for m_chainLinks
- return m_chainLinks.value(m_current);
- }
-
- ChainLink* ChainLinkList::first()
- {
- m_current = 0;
- return current();
- }
-
- ChainLink* ChainLinkList::next()
- {
- ++m_current;
- return current();
- }
-
- void ChainLinkList::prepend(ChainLink* link)
- {
- Q_ASSERT(link);
- m_chainLinks.prepend(link);
- m_current = 0;
- }
-
- void ChainLinkList::append(ChainLink* link)
- {
- Q_ASSERT(link);
- m_chainLinks.append(link);
- m_current = m_chainLinks.count() -1;
- }
-}
diff --git a/libs/ui/KisFilterChainLinkList.h b/libs/ui/KisFilterChainLinkList.h
deleted file mode 100644
index af53a31909..0000000000
--- a/libs/ui/KisFilterChainLinkList.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* This file is part of the Calligra libraries
- Copyright (C) 2009 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.
-*/
-#ifndef KISFILTERCHAINLINKLIST_H
-#define KISFILTERCHAINLINKLIST_H
-
-#include <QList>
-
-namespace CalligraFilter {
-
- class ChainLink;
-
-
- class ChainLinkList
- {
- public:
- ChainLinkList();
- ~ChainLinkList();
-
- void deleteAll();
- int count() const;
-
- /**
- * Return a pointer to the current position in the chain.
- * @return pointer to the current ChainLink or 0 if the ChainLinkList is empty.
- **/
- ChainLink* current() const;
-
- /**
- * Move the position to the first position in the chain.
- * @return pointer to the first ChainLink or 0 if the ChainLinkList is empty.
- **/
- ChainLink* first();
-
- ChainLink* next();
-
- void prepend(ChainLink* link);
-
- void append(ChainLink* link);
-
- private:
-
- QList<ChainLink*> m_chainLinks;
- int m_current;
-
- };
-
-}
-
-#endif // KOFILTERCHAINLINKLIST_H
diff --git a/libs/ui/KisFilterEdge.cpp b/libs/ui/KisFilterEdge.cpp
deleted file mode 100644
index 97d51bae5b..0000000000
--- a/libs/ui/KisFilterEdge.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* 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, KisFilterEntrySP 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)
- dbgFile << indent << "Edge -> '" << m_vertex->mimeType()
- << "' (" << m_filterEntry->weight << ")" << endl;
- else
- dbgFile << indent << "Edge -> '(null)' ("
- << m_filterEntry->weight << ")" << endl;
-}
-
-}
diff --git a/libs/ui/KisFilterEdge.h b/libs/ui/KisFilterEdge.h
deleted file mode 100644
index 5bb8763ce9..0000000000
--- a/libs/ui/KisFilterEdge.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* 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.
-*/
-#ifndef KISFILTEREDGE_H
-#define KISFILTEREDGE_H
-
-#include "KisFilterChain.h"
-#include "PriorityQueue_p.h"
-
-namespace CalligraFilter {
-/**
- * An internal class representing a filter (=edge) in the filter graph.
- * @internal
- */
-class Edge
-{
-
-public:
- // creates a new edge to "vertex" with the given weight.
- Edge(Vertex* vertex, KisFilterEntrySP filterEntry);
- ~Edge() {}
-
- unsigned int weight() const {
- return m_filterEntry ? m_filterEntry->weight : 0;
- }
- KisFilterEntrySP filterEntry() const {
- return m_filterEntry;
- }
- const Vertex* vertex() const {
- return m_vertex;
- }
-
- // Relaxes the "connected" vertex (i.e. the weight of the
- // connected vertex = "predec.->key()" (parameter) + weight of this edge
- // As this will only be called once we calculate the weight
- // of the edge "on the fly"
- // Note: We have to pass the queue as we have to call keyDecreased :}
- void relax(const Vertex* predecessor, PriorityQueue<Vertex>& queue);
-
- // debugging
- void dump(const QByteArray& indent) const;
-
-private:
- Edge(const Edge& rhs);
- Edge& operator=(const Edge& rhs);
-
- Vertex* m_vertex;
- KisFilterEntrySP m_filterEntry;
-
- class Private;
- Private * const d;
-};
-}
-
-#endif // KOFILTEREDGE_H
diff --git a/libs/ui/KisFilterEntry.cpp b/libs/ui/KisFilterEntry.cpp
index 535933ecde..deadd33277 100644
--- a/libs/ui/KisFilterEntry.cpp
+++ b/libs/ui/KisFilterEntry.cpp
@@ -1,86 +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 <kis_debug.h>
#include <KoJsonTrader.h>
#include <kpluginfactory.h>
#include <QFile>
-#include <KisFilterChain.h>
#include <limits.h> // UINT_MAX
KisFilterEntry::KisFilterEntry(QPluginLoader *loader)
: KisShared()
, 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();
}
KisFilterEntry::~KisFilterEntry()
{
delete m_loader;
}
QList<KisFilterEntrySP> KisFilterEntry::query()
{
QList<KisFilterEntrySP> lst;
QList<QPluginLoader *> offers = KoJsonTrader::instance()->query("Krita/FileFilter", QString());
unsigned int max = offers.count();
dbgFile <<"Query returned" << max <<" offers";
Q_FOREACH(QPluginLoader *pluginLoader, offers) {
//dbgFile <<" desktopEntryPath=" << (*it)->entryPath()
// << " library=" << (*it)->library() << endl;
// Append converted offer
lst.append(KisFilterEntrySP(new KisFilterEntry(pluginLoader)));
}
return lst;
}
-KisImportExportFilter* KisFilterEntry::createFilter(KisFilterChainSP chain)
+KisImportExportFilter* KisFilterEntry::createFilter()
{
KLibFactory *factory = qobject_cast<KLibFactory *>(m_loader->instance());
if (!factory) {
warnUI << m_loader->errorString();
return 0;
}
QObject *obj = factory->create<KisImportExportFilter>(0);
if (!obj || !obj->inherits("KisImportExportFilter")) {
delete obj;
return 0;
}
KisImportExportFilter* filter = static_cast<KisImportExportFilter*>(obj);
- filter->setChain(chain);
return filter;
}
diff --git a/libs/ui/KisFilterEntry.h b/libs/ui/KisFilterEntry.h
index 01910298fe..b71a52b70d 100644
--- a/libs/ui/KisFilterEntry.h
+++ b/libs/ui/KisFilterEntry.h
@@ -1,102 +1,102 @@
/* 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.
*/
#ifndef KIS_FILTER_ENTRY_H
#define KIS_FILTER_ENTRY_H
#include <QList>
#include <QStringList>
#include "kis_types.h"
#include "kis_shared.h"
#include "kis_shared_ptr.h"
#include "kritaui_export.h"
class QObject;
class QPluginLoader;
class KisImportExportFilter;
class KisFilterEntry;
typedef KisSharedPtr<KisFilterEntry> KisFilterEntrySP;
/**
* Represents an available filter.
*/
class KRITAUI_EXPORT KisFilterEntry : public KisShared
{
public:
//KisFilterEntry() : weight( 0 ) { m_service = 0; } // for QList
explicit KisFilterEntry(QPluginLoader *loader);
~KisFilterEntry();
- KisImportExportFilter *createFilter(KisFilterChainSP chain);
+ KisImportExportFilter *createFilter();
/**
* The imported mimetype(s).
*/
QStringList import;
/**
* The exported mimetype(s).
*/
QStringList export_;
/**
* The "weight" of this filter path. Has to be > 0 to be valid.
*/
unsigned int weight;
/**
* Do we have to check during runtime?
*/
QString available;
/**
* @return TRUE if the filter can import the requested mimetype.
*/
bool imports(const QString& _mimetype) const {
return (import.contains(_mimetype));
}
/**
* @return TRUE if the filter can export the requested mimetype.
*/
bool exports(const QString& _m) const {
return (export_.contains(_m));
}
/**
* This function will query KDED to find all available filters.
*/
static QList<KisFilterEntrySP> query();
QPluginLoader *loader() const {
return m_loader;
}
private:
QPluginLoader *m_loader;
};
#endif
diff --git a/libs/ui/KisFilterGraph.cpp b/libs/ui/KisFilterGraph.cpp
deleted file mode 100644
index df15d649fe..0000000000
--- a/libs/ui/KisFilterGraph.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/* 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 "KisFilterEntry.h"
-#include "KisDocument.h"
-
-#include "PriorityQueue_p.h"
-#include "KisFilterEdge.h"
-#include "KisFilterChainLink.h"
-#include "KisFilterVertex.h"
-
-#include <QMetaMethod>
-#include <QPluginLoader>
-#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()
-{
- Q_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" ...
- Q_FOREACH (Vertex* vertex, m_vertices) {
- vertex->reset();
- }
- // ...and re-run the shortest path search for the new source mime
- shortestPaths();
-}
-
-KisFilterChainSP Graph::chain(const KisImportExportManager *manager, QByteArray &to) const
-{
- if (!isValid() || !manager)
- return KisFilterChainSP();
-
- Q_ASSERT(!to.isEmpty());
-
- const Vertex* vertex = m_vertices.value(to);
- if (!vertex || vertex->key() == UINT_MAX)
- return KisFilterChainSP();
-
- KisFilterChainSP filterChain(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);
- filterChain->prependChainLink(edge->filterEntry(), tmp->mimeType(), vertex->mimeType());
- vertex = tmp;
- tmp = tmp->predecessor();
- }
- return filterChain;
-}
-
-void Graph::dump() const
-{
-#ifndef NDEBUG
- dbgFile << "+++++++++ Graph::dump +++++++++";
- dbgFile << "From:" << m_from;
- Q_FOREACH (Vertex *vertex, m_vertices) {
- vertex->dump(" ");
- }
- dbgFile << "+++++++++ Graph::dump (done) +++++++++";
-#endif
-}
-
-// Query the trader and create the vertices and edges representing
-// available mime types and filters.
-void Graph::buildGraph()
-{
- // no constraint here - we want *all* :)
- const QList<KisFilterEntrySP> filters(KisFilterEntry::query());
-
- Q_FOREACH (KisFilterEntrySP filter, filters) {
-
- // First add the "starting points" to the dict
- Q_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));
- }
-
- Q_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
- Q_FOREACH (const QString& import, filter->import) {
- m_vertices[import.toLatin1()]->addEdge(new Edge(exp, filter));
- }
-
- }
- }
-}
-
-// 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;
-}
-
-}
diff --git a/libs/ui/KisFilterGraph.h b/libs/ui/KisFilterGraph.h
deleted file mode 100644
index 3dcfeffcdc..0000000000
--- a/libs/ui/KisFilterGraph.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* 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.
-*/
-#ifndef KIS_FILTERGRAPH_H
-#define KIS_FILTERGRAPH_H
-
-#include "kritaui_export.h"
-#include "KisFilterChain.h"
-#include <QByteArray>
-#include <QHash>
-
-namespace CalligraFilter {
-
-/**
- * The main worker behind the scenes. Manages the creation of the graph,
- * processing the information in it, and creating the filter chains.
- * @internal
- * Only exported for unit tests.
- */
-class KRITAUI_EXPORT Graph
-{
-
-public:
- explicit Graph(const QByteArray& from);
- ~Graph();
-
- bool isValid() const {
- return m_graphValid;
- }
-
- QByteArray sourceMimeType() const {
- return m_from;
- }
- void setSourceMimeType(const QByteArray& from);
-
- // Creates a chain from "from" to the "to" mimetype
- // If the "to" mimetype isEmpty() then we try to find the
- // closest Calligra mimetype and use that as destination.
- // After such a search "to" will contain the dest. mimetype (return value)
- // if the search was successful. Might return 0!
- KisFilterChainSP chain(const KisImportExportManager* manager, QByteArray& to) const;
-
- // debugging
- void dump() const;
-
-private:
- Graph(const Graph& rhs);
- Graph& operator=(const Graph& rhs);
-
- void buildGraph();
- void shortestPaths();
-
- QHash<QByteArray, CalligraFilter::Vertex*> m_vertices;
- QByteArray m_from;
- bool m_graphValid;
-
- class Private;
- Private * const d;
-};
-
-}
-#endif // KOFILTERGRAPH_H
diff --git a/libs/ui/KisFilterVertex.cpp b/libs/ui/KisFilterVertex.cpp
deleted file mode 100644
index 9ee988ec33..0000000000
--- a/libs/ui/KisFilterVertex.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/* 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;
- Q_FOREACH (Edge* e, m_edges) {
- if (e->vertex() == vertex &&
- (!edge || e->weight() < edge->weight())) {
- edge = e;
- }
- }
- return edge;
-}
-
-void Vertex::relaxVertices(PriorityQueue<Vertex>& queue)
-{
- Q_FOREACH (Edge* e, m_edges) {
- e->relax(this, queue);
- }
-}
-
-void Vertex::dump(const QByteArray& indent) const
-{
-#ifdef NDEBUG
- Q_UNUSED(indent)
-#else
- dbgFile << indent << "Vertex:" << m_mimeType << " (" << m_weight << "):";
- const QByteArray i(indent + " ");
- Q_FOREACH (Edge* edge, m_edges) {
- edge->dump(i);
- }
-#endif
-}
-
-}
diff --git a/libs/ui/KisFilterVertex.h b/libs/ui/KisFilterVertex.h
deleted file mode 100644
index 37ae34349c..0000000000
--- a/libs/ui/KisFilterVertex.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* 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.
-*/
-#ifndef KIS_FILTERVERTEX_H
-#define KIS_FILTERVERTEX_H
-
-#include "KisFilterChain.h"
-
-namespace CalligraFilter {
-template <typename T> class PriorityQueue;
-/**
- * An internal class representing a mime type (=node, vertex) in the filter graph.
- * @internal
- */
-class Vertex
-{
-
-public:
- explicit Vertex(const QByteArray &mimeType);
- ~Vertex();
-
- QByteArray mimeType() const {
- return m_mimeType;
- }
-
- // Current "weight" of the vertex - will be "relaxed" when
- // running the shortest path algorithm. Returns true if it
- // really has been "relaxed"
- bool setKey(unsigned int key);
-
- unsigned int key() const {
- return m_weight;
- }
-
- // Can be used to set the key back to "Infinity" (UINT_MAX)
- // and reset the predecessor of this vertex
- void reset();
-
- // Position in the heap, needed for a fast keyDecreased operation
- void setIndex(int index) {
- m_index = index;
- }
-
- int index() const {
- return m_index;
- }
-
- // predecessor on the way from the source to the destination,
- // needed for the shortest path algorithm
- void setPredecessor(const Vertex* predecessor) {
- m_predecessor = predecessor;
- }
-
- const Vertex* predecessor() const {
- return m_predecessor;
- }
-
- // Adds an outgoing edge to the vertex, transfers ownership
- void addEdge(Edge* edge);
-
- // Finds the lightest(!) edge pointing to the given vertex, if any (0 if not found)
- // This means it will always search the whole list of edges
- const Edge* findEdge(const Vertex* vertex) const;
-
- // This method is called when we need to relax all "our" edges.
- // We need to pass the queue as we have to notify it about key changes - ugly :(
- void relaxVertices(PriorityQueue<Vertex>& queue);
-
- // debugging
- void dump(const QByteArray& indent) const;
-
-private:
-
- Vertex(const Vertex& rhs);
- Vertex& operator=(const Vertex& rhs);
-
- QList<Edge*> m_edges;
- const Vertex* m_predecessor;
- QByteArray m_mimeType;
- unsigned int m_weight; // "key" inside the queue
- int m_index; // position inside the queue, needed for a fast keyDecreased()
-
- class Private;
- Private * const d;
-};
-
-}
-#endif // KOFILTERVERTEX_H
diff --git a/libs/ui/KisImportExportFilter.cpp b/libs/ui/KisImportExportFilter.cpp
index c3dbcbd503..5def976d74 100644
--- a/libs/ui/KisImportExportFilter.cpp
+++ b/libs/ui/KisImportExportFilter.cpp
@@ -1,207 +1,268 @@
/* 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 <kis_debug.h>
#include <QStack>
#include "KisImportExportManager.h"
+#include <KoColorSpaceRegistry.h>
+#include <KoColorModelStandardIds.h>
+#include <KisExportCheckBase.h>
+#include <KisExportCheckRegistry.h>
#include "KoUpdater.h"
#include <klocalizedstring.h>
class Q_DECL_HIDDEN KisImportExportFilter::Private
{
public:
QPointer<KoUpdater> updater;
+ QByteArray mime;
+ QString filename;
+ bool batchmode;
+
+ QMap<QString, KisExportCheckBase*> capabilities;
Private()
: updater(0)
+ , batchmode(false)
{}
- /**
- * Use this pointer to access all information about input/output
- * during the conversion. @em Don't use it in the constructor -
- * it's invalid while constructing the object!
- */
- KisFilterChainSP chain;
+ ~Private()
+ {
+ qDeleteAll(capabilities);
+ }
};
+
KisImportExportFilter::KisImportExportFilter(QObject *parent)
: QObject(parent)
, d(new Private)
{
}
-KisDocument *KisImportExportFilter::inputDocument() const
+KisImportExportFilter::~KisImportExportFilter()
{
- return d->chain->inputDocument();
+ Q_ASSERT(d->updater);
+ if (d->updater) {
+ d->updater->setProgress(100);
+ }
+ delete d;
}
-KisDocument *KisImportExportFilter::outputDocument() const
+
+QString KisImportExportFilter::filename() const
{
- return d->chain->outputDocument();
+ return d->filename;
}
-QString KisImportExportFilter::inputFile() const
+bool KisImportExportFilter::batchMode() const
{
- return d->chain->inputFile();
+ return d->batchmode;
}
-QString KisImportExportFilter::outputFile() const
+
+void KisImportExportFilter::setBatchMode(bool batchmode)
{
- return d->chain->outputFile();
+ d->batchmode = batchmode;
}
-bool KisImportExportFilter::getBatchMode() const
+void KisImportExportFilter::setFilename(const QString &filename)
{
- return d->chain->manager()->getBatchMode();
+ d->filename = filename;
}
-KisImportExportFilter::~KisImportExportFilter()
+void KisImportExportFilter::setMimeType(const QString &mime)
{
- Q_ASSERT(d->updater);
- if (d->updater) d->updater->setProgress(100);
- delete d;
+ d->mime = mime.toLatin1();
}
-void KisImportExportFilter::setChain(KisFilterChainSP chain)
+QByteArray KisImportExportFilter::mimeType() const
{
- d->chain = chain;
+ return d->mime;
}
QString KisImportExportFilter::conversionStatusString(ConversionStatus status)
{
QString msg;
switch (status) {
case OK: break;
case FilterCreationError:
msg = i18n("Could not create the filter plugin"); break;
case CreationError:
msg = i18n("Could not create the output document"); break;
case FileNotFound:
msg = i18n("File not found"); break;
case StorageCreationError:
msg = i18n("Cannot create storage"); break;
case BadMimeType:
msg = i18n("Bad MIME type"); break;
- case EmbeddedDocError:
- msg = i18n("Error in embedded document"); break;
-
case WrongFormat:
msg = i18n("Format not recognized"); break;
case NotImplemented:
msg = i18n("Not implemented"); break;
case ParsingError:
msg = i18n("Parsing error"); break;
- case PasswordProtected:
- msg = i18n("Document is password protected"); break;
-
case InvalidFormat:
msg = i18n("Invalid file format"); break;
case InternalError:
- case UnexpectedEOF:
- case UnexpectedOpcode:
- case StupidError: // ?? what is this ??
case UsageError:
msg = i18n("Internal error"); break;
- case OutOfMemory:
- msg = i18n("Out of memory"); break;
-
- case FilterEntryNull:
- msg = i18n("Empty Filter Plugin"); break;
-
- case NoDocumentCreated:
- msg = i18n("Trying to load into the wrong kind of document"); break;
-
- case DownloadFailed:
- msg = i18n("Failed to download remote file"); break;
-
case ProgressCancelled:
msg = i18n("Cancelled by user"); break;
case BadConversionGraph:
msg = i18n("Unknown file type"); break;
case UnsupportedVersion:
msg = i18n("Unsupported file version"); break;
case UserCancelled:
// intentionally we do not prompt the error message here
break;
default: msg = i18n("Unknown error"); break;
}
return msg;
}
KisPropertiesConfigurationSP KisImportExportFilter::defaultConfiguration(const QByteArray &from, const QByteArray &to) const
{
Q_UNUSED(from);
Q_UNUSED(to);
return 0;
}
KisPropertiesConfigurationSP KisImportExportFilter::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
{
return defaultConfiguration(from, to);
}
KisConfigWidget *KisImportExportFilter::createConfigurationWidget(QWidget *, const QByteArray &from, const QByteArray &to) const
{
Q_UNUSED(from);
Q_UNUSED(to);
return 0;
}
-void KisImportExportFilter::setUpdater(const QPointer<KoUpdater>& updater)
+QMap<QString, KisExportCheckBase *> KisImportExportFilter::exportChecks()
+{
+ qDeleteAll(d->capabilities);
+ initializeCapabilities();
+ return d->capabilities;
+}
+
+void KisImportExportFilter::setUpdater(QPointer<KoUpdater> updater)
{
- Q_ASSERT(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)
+void KisImportExportFilter::setProgress(int value)
{
- Q_ASSERT(d->updater);
if (d->updater) {
d->updater->setValue(value);
}
}
+
+void KisImportExportFilter::initializeCapabilities()
+{
+ // XXX: Initialize everything to fully supported?
+}
+
+void KisImportExportFilter::addCapability(KisExportCheckBase *capability)
+{
+ d->capabilities[capability->id()] = capability;
+}
+
+
+
+void KisImportExportFilter::addSupportedColorModels(QList<QPair<KoID, KoID> > supportedColorModels, const QString &name, KisExportCheckBase::Level level)
+{
+ Q_ASSERT(level != KisExportCheckBase::SUPPORTED);
+ QString layerMessage;
+ QString imageMessage;
+ QList<KoID> allColorModels = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::AllColorSpaces);
+ Q_FOREACH(const KoID &colorModelID, allColorModels) {
+ QList<KoID> allColorDepths = KoColorSpaceRegistry::instance()->colorDepthList(colorModelID.id(), KoColorSpaceRegistry::AllColorSpaces);
+ Q_FOREACH(const KoID &colorDepthID, allColorDepths) {
+
+ KisExportCheckFactory *colorModelCheckFactory =
+ KisExportCheckRegistry::instance()->get("ColorModelCheck/" + colorModelID.id() + "/" + colorDepthID.id());
+ KisExportCheckFactory *colorModelPerLayerCheckFactory =
+ KisExportCheckRegistry::instance()->get("ColorModelPerLayerCheck/" + colorModelID.id() + "/" + colorDepthID.id());
+
+ if(!colorModelCheckFactory || !colorModelPerLayerCheckFactory) {
+ qDebug() << "No factory for" << colorModelID << colorDepthID;
+ continue;
+ }
+
+ if (supportedColorModels.contains(QPair<KoID, KoID>(colorModelID, colorDepthID))) {
+ addCapability(colorModelCheckFactory->create(KisExportCheckBase::SUPPORTED));
+ addCapability(colorModelPerLayerCheckFactory->create(KisExportCheckBase::SUPPORTED));
+ }
+ else {
+
+
+ if (level == KisExportCheckBase::PARTIALLY) {
+ imageMessage = i18nc("image conversion warning",
+ "%1 cannot save images with color model <b>%2</b> and depth <b>%3</b>. The image will be converted."
+ ,name, colorModelID.name(), colorDepthID.name());
+
+ layerMessage =
+ i18nc("image conversion warning",
+ "%1 cannot save layers with color model <b>%2</b> and depth <b>%3</b>. The layers will be converted or skipped."
+ ,name, colorModelID.name(), colorDepthID.name());
+ }
+ else {
+ imageMessage = i18nc("image conversion warning",
+ "%1 cannot save images with color model <b>%2</b> and depth <b>%3</b>. The image will not be saved."
+ ,name, colorModelID.name(), colorDepthID.name());
+
+ layerMessage =
+ i18nc("image conversion warning",
+ "%1 cannot save layers with color model <b>%2</b> and depth <b>%3</b>. The layers will be skipped."
+ , name, colorModelID.name(), colorDepthID.name());
+ }
+
+
+
+ addCapability(colorModelCheckFactory->create(level, imageMessage));
+ addCapability(colorModelPerLayerCheckFactory->create(level, layerMessage));
+ }
+ }
+ }
+}
diff --git a/libs/ui/KisImportExportFilter.h b/libs/ui/KisImportExportFilter.h
index 3ffdb394b7..48c60dddaf 100644
--- a/libs/ui/KisImportExportFilter.h
+++ b/libs/ui/KisImportExportFilter.h
@@ -1,194 +1,174 @@
/* This file is part of the Calligra 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.
*/
#ifndef KIS_IMPORT_EXPORT_FILTER_H
#define KIS_IMPORT_EXPORT_FILTER_H
#include <QObject>
+#include <QIODevice>
#include <QMap>
#include <QPointer>
#include <QString>
+#include <QPair>
+#include <QList>
+#include <KoID.h>
#include <QSharedPointer>
#include <kis_properties_configuration.h>
#include <kis_types.h>
+#include <KisExportCheckBase.h>
class KoUpdater;
class KisDocument;
class KisConfigWidget;
#include "kritaui_export.h"
/**
* @brief The base class for import and export filters.
*
* Derive your filter class from this base class and implement
* the @ref convert() method. Don't forget to specify the Q_OBJECT
* macro in your class even if you don't use signals or slots.
* This is needed as filters are created on the fly.
- * The m_chain member allows access to the @ref KisFilterChain
- * which invokes the filter to query for input/output.
*
* @note Take care: The m_chain pointer is invalid while the constructor
* runs due to the implementation -- @em don't use it in the constructor.
* After the constructor, when running the @ref convert() method it's
* guaranteed to be valid, so no need to check against 0.
*
* @note If the code is compiled in debug mode, setting CALLIGRA_DEBUG_FILTERS
* environment variable to any value disables deletion of temporary files while
* importing/exporting. This is useful for testing purposes.
*
* @author Werner Trobin <trobin@kde.org>
* @todo the class has no constructor and therefore cannot initialize its private class
*/
class KRITAUI_EXPORT KisImportExportFilter : public QObject
{
Q_OBJECT
public:
/**
* This enum is used to signal the return state of your filter.
* Return OK in @ref convert() in case everything worked as expected.
* Feel free to add some more error conditions @em before the last item
* if it's needed.
*/
enum ConversionStatus { OK,
- StupidError,
UsageError,
CreationError,
FileNotFound,
StorageCreationError,
BadMimeType,
BadConversionGraph,
- EmbeddedDocError,
WrongFormat,
NotImplemented,
ParsingError,
InternalError,
- UnexpectedEOF,
- UnexpectedOpcode,
UserCancelled,
- OutOfMemory,
- PasswordProtected,
InvalidFormat,
- FilterEntryNull,
- NoDocumentCreated,
- DownloadFailed,
FilterCreationError,
ProgressCancelled,
UnsupportedVersion,
JustInCaseSomeBrokenCompilerUsesLessThanAByte = 255
};
virtual ~KisImportExportFilter();
- /**
- * @brief setChain set the chain information on the filter. The chain information
- * lets the filter know what document it's working on. The filter will not delete
- * @param chain the actual filter chain
- */
- void setChain(KisFilterChainSP chain);
+ void setBatchMode(bool batchmode);
+ void setFilename(const QString &filename);
+ void setMimeType(const QString &mime);
+ void setUpdater(QPointer<KoUpdater> updater);
/**
* The filter chain calls this method to perform the actual conversion.
* The passed mimetypes should be a pair of those you specified in your
* .desktop file.
* You @em have to implement this method to make the filter work.
*
- * @param from The mimetype of the source file/document
- * @param to The mimetype of the destination file/document
* @return The error status, see the @ref #ConversionStatus enum.
* KisImportExportFilter::OK means that everything is alright.
*/
- virtual ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0) = 0;
-
- /**
- * Set the updater to which the filter will report progress.
- * Every emit of the sigProgress signal is reported to the updater.
- */
- void setUpdater(const QPointer<KoUpdater>& updater);
+ virtual ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) = 0;
/**
* Get the text version of the status value
*/
static QString conversionStatusString(ConversionStatus status);
/**
* @brief defaultConfiguration defines the default settings for the given import export filter
* @param from The mimetype of the source file/document
* @param to The mimetype of the destination file/document
* @return a serializable KisPropertiesConfiguration object
*/
virtual KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const;
/**
* @brief lastSavedConfiguration return the last saved configuration for this filter
* @param from The mimetype of the source file/document
* @param to The mimetype of the destination file/document
* @return a serializable KisPropertiesConfiguration object
*/
virtual KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const;
/**
* @brief createConfigurationWidget creates a widget that can be used to define the settings for a given import/export filter
* @param parent the ownder of the widget; the caller is responsible for deleting
* @param from The mimetype of the source file/document
* @param to The mimetype of the destination file/document
*
* @return the widget
*/
virtual KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
-
-Q_SIGNALS:
/**
- * Emit this signal with a value in the range of 1...100 to have some
- * progress feedback for the user in the statusbar of the application.
- *
- * @param value The actual progress state. Should always remain in
- * the range 1..100.
+ * @brief generate and return the list of capabilities of this export filter. The list
+ * @return returns the list of capabilities of this export filter
*/
- void sigProgress(int value);
+ virtual QMap<QString, KisExportCheckBase*> exportChecks();
protected:
/**
* This is the constructor your filter has to call, obviously.
*/
KisImportExportFilter(QObject *parent = 0);
- KisDocument *inputDocument() const;
- KisDocument *outputDocument() const;
- QString inputFile() const;
- QString outputFile() const;
- bool getBatchMode() const;
+ QString filename() const;
+ bool batchMode() const;
+ QByteArray mimeType() const;
+
+ void setProgress(int value);
+ virtual void initializeCapabilities();
+ void addCapability(KisExportCheckBase *capability);
+ void addSupportedColorModels(QList<QPair<KoID, KoID> > supportedColorModels, const QString &name, KisExportCheckBase::Level level = KisExportCheckBase::PARTIALLY);
private:
KisImportExportFilter(const KisImportExportFilter& rhs);
KisImportExportFilter& operator=(const KisImportExportFilter& rhs);
class Private;
Private *const d;
-private Q_SLOTS:
- void slotProgress(int value);
};
#endif
diff --git a/libs/ui/KisImportExportManager.cpp b/libs/ui/KisImportExportManager.cpp
index c466363b00..646714d421 100644
--- a/libs/ui/KisImportExportManager.cpp
+++ b/libs/ui/KisImportExportManager.cpp
@@ -1,341 +1,436 @@
-/* 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.
-*/
+/*
+ * Copyright (C) 2016 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 "KisImportExportManager.h"
#include <QFile>
#include <QLabel>
#include <QVBoxLayout>
#include <QList>
#include <QApplication>
#include <QByteArray>
#include <QPluginLoader>
#include <QFileInfo>
#include <QMessageBox>
#include <QJsonObject>
+#include <QTextBrowser>
+#include <QCheckBox>
+#include <QGroupBox>
#include <klocalizedstring.h>
#include <ksqueezedtextlabel.h>
#include <kpluginfactory.h>
-#include "KoProgressUpdater.h"
-#include "KoJsonTrader.h"
-
+#include <kis_icon_utils.h>
+#include <KoDialog.h>
+#include <KoProgressUpdater.h>
+#include <KoJsonTrader.h>
+#include <KisMimeDatabase.h>
+#include <kis_config_widget.h>
#include <kis_debug.h>
#include <KisMimeDatabase.h>
-
+#include <KisPreExportChecker.h>
+#include <KisPart.h>
+#include "kis_config.h"
#include "KisImportExportFilter.h"
#include "KisDocument.h"
-
-#include <queue>
-#include <unistd.h>
+#include <kis_image.h>
+#include <kis_paint_layer.h>
+#include "kis_guides_config.h"
+#include "kis_grid_config.h"
+#include "kis_popup_button.h"
+#include <kis_iterator_ng.h>
// static cache for import and export mimetypes
QStringList KisImportExportManager::m_importMimeTypes;
QStringList KisImportExportManager::m_exportMimeTypes;
class Q_DECL_HIDDEN KisImportExportManager::Private
{
public:
- bool batch;
- QByteArray importMimeType;
- QWeakPointer<KoProgressUpdater> progressUpdater;
-
- Private(KoProgressUpdater *progressUpdater_ = 0)
- : progressUpdater(progressUpdater_)
- {
- }
-
+ bool batchMode {false};
+ QPointer<KoProgressUpdater> progressUpdater {0};
};
KisImportExportManager::KisImportExportManager(KisDocument* document)
: m_document(document)
- , m_graph("")
- , d(new Private(0))
-{
- d->batch = false;
-}
-
-
-KisImportExportManager::KisImportExportManager(const QString& location)
- : m_document(0)
- , m_importFileName(location)
- , m_graph("")
- , d(new Private)
-{
- d->batch = false;
-}
-
-KisImportExportManager::KisImportExportManager(const QByteArray& mimeType)
- : m_document(0)
- , m_graph("")
, d(new Private)
{
- d->batch = false;
- d->importMimeType = mimeType;
}
KisImportExportManager::~KisImportExportManager()
{
delete d;
}
-QString KisImportExportManager::importDocument(const QString& location,
- const QString& documentMimeType,
- KisImportExportFilter::ConversionStatus& status)
+KisImportExportFilter::ConversionStatus KisImportExportManager::importDocument(const QString& location, const QString& mimeType)
{
- // Find the mime type for the file to be imported.
- QString typeName = documentMimeType;
- if (typeName.isEmpty()) {
- typeName = KisMimeDatabase::mimeTypeForFile(location);
- }
- m_graph.setSourceMimeType(typeName.toLatin1()); // .latin1() is okay here (Werner)
-
- if (!m_graph.isValid()) {
- errFile << "Couldn't create a valid graph for this source mimetype: "
- << typeName;
- status = KisImportExportFilter::BadConversionGraph;
- return QString();
- }
-
- KisFilterChainSP 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
- KisFilterChainSP 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 {
- errFile << "You aren't supposed to use import() from a filter!" << endl;
- status = KisImportExportFilter::UsageError;
- return QString();
- }
-
- if (!chain) {
- 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_importFileName = location;
- m_exportFileName.clear();
- status = chain->invokeChain();
-
- m_importFileName.clear(); // Reset the import URL
-
- if (status == KisImportExportFilter::OK)
- return chain->chainOutput();
- return QString();
+ return convert(Import, location, mimeType, false, 0);
}
-KisImportExportFilter::ConversionStatus KisImportExportManager::exportDocument(const QString& location, QByteArray& mimeType, KisPropertiesConfigurationSP exportConfiguration)
+KisImportExportFilter::ConversionStatus KisImportExportManager::exportDocument(const QString& location, QByteArray& mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
- 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_exportFileName = location;
- m_exportConfiguration = exportConfiguration;
-
- KisFilterChainSP 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 {
- QString t = KisMimeDatabase::mimeTypeForFile(m_importFileName);
- m_graph.setSourceMimeType(t.toLatin1());
- }
-
- if (!m_graph.isValid()) {
- errFile << "Couldn't create a valid graph for this source mimetype.";
- QApplication::restoreOverrideCursor();
- 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) {
- 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."));
- }
- QApplication::restoreOverrideCursor();
- return KisImportExportFilter::BadConversionGraph;
- }
-
- return chain->invokeChain();
+ return convert(Export, location, mimeType, showWarnings, exportConfiguration);
}
// The static method to figure out to which parts of the
// graph this mimetype has a connection to.
QStringList KisImportExportManager::mimeFilter(Direction direction)
{
// Find the right mimetype by the extension
QSet<QString> mimeTypes;
-// mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster";
+ // mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster";
if (direction == KisImportExportManager::Import) {
if (m_importMimeTypes.isEmpty()) {
KoJsonTrader trader;
QList<QPluginLoader *>list = trader.query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
- Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",")) {
- if (!mimetype.isEmpty()) {
- mimeTypes << mimetype;
- }
+ Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",", QString::SkipEmptyParts)) {
+ //qDebug() << "Adding import mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
+ mimeTypes << mimetype;
}
}
qDeleteAll(list);
m_importMimeTypes = mimeTypes.toList();
}
return m_importMimeTypes;
}
else if (direction == KisImportExportManager::Export) {
if (m_exportMimeTypes.isEmpty()) {
KoJsonTrader trader;
QList<QPluginLoader *>list = trader.query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
- Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",")) {
- if (!mimetype.isEmpty()) {
- mimeTypes << mimetype;
- }
+ Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",", QString::SkipEmptyParts)) {
+ //qDebug() << "Adding export mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
+ mimeTypes << mimetype;
}
}
qDeleteAll(list);
m_exportMimeTypes = mimeTypes.toList();
}
return m_exportMimeTypes;
}
return QStringList();
}
KisImportExportFilter *KisImportExportManager::filterForMimeType(const QString &mimetype, KisImportExportManager::Direction direction)
{
int weight = -1;
KisImportExportFilter *filter = 0;
KoJsonTrader trader;
QList<QPluginLoader *>list = trader.query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
QString directionKey = direction == Export ? "X-KDE-Export" : "X-KDE-Import";
- if (json.value(directionKey).toString().split(",").contains(mimetype)) {
+ if (json.value(directionKey).toString().split(",", QString::SkipEmptyParts).contains(mimetype)) {
KLibFactory *factory = qobject_cast<KLibFactory *>(loader->instance());
if (!factory) {
warnUI << loader->errorString();
continue;
}
QObject* obj = factory->create<KisImportExportFilter>(0);
if (!obj || !obj->inherits("KisImportExportFilter")) {
delete obj;
continue;
}
- KisImportExportFilter *f = static_cast<KisImportExportFilter*>(obj);
+ KisImportExportFilter *f = qobject_cast<KisImportExportFilter*>(obj);
if (!f) {
delete obj;
continue;
}
int w = json.value("X-KDE-Weight").toInt();
if (w > weight) {
delete filter;
filter = f;
f->setObjectName(loader->fileName());
weight = w;
}
}
}
qDeleteAll(list);
+ filter->setMimeType(mimetype);
return filter;
}
-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;
+ d->batchMode = batch;
}
-bool KisImportExportManager::getBatchMode(void) const
+bool KisImportExportManager::batchMode(void) const
{
- return d->batch;
+ return d->batchMode;
}
void KisImportExportManager::setProgresUpdater(KoProgressUpdater *updater)
{
d->progressUpdater = updater;
}
-KoProgressUpdater* KisImportExportManager::progressUpdater() const
+KisImportExportFilter::ConversionStatus KisImportExportManager::convert(KisImportExportManager::Direction direction, const QString &location, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
- if (d->progressUpdater.isNull()) {
- // somebody, probably its parent, deleted our progress updater for us
- return 0;
+
+ QString typeName = mimeType;
+ if (typeName.isEmpty()) {
+ typeName = KisMimeDatabase::mimeTypeForFile(location);
+ }
+
+ QSharedPointer<KisImportExportFilter> filter(filterForMimeType(typeName, direction));
+ if (!filter) {
+ return KisImportExportFilter::FilterCreationError;
+ }
+
+ filter->setFilename(location);
+ filter->setBatchMode(batchMode());
+ filter->setMimeType(typeName);
+
+ if (d->progressUpdater) {
+ filter->setUpdater(d->progressUpdater->startSubtask());
+ }
+
+ QByteArray from, to;
+ if (direction == Export) {
+ from = m_document->nativeFormatMimeType();
+ to = mimeType.toLatin1();
+ }
+ else {
+ from = mimeType.toLatin1();
+ to = m_document->nativeFormatMimeType();
+ }
+
+ if (!exportConfiguration) {
+ exportConfiguration = filter->lastSavedConfiguration(from, to);
+ if (exportConfiguration) {
+ // Fill with some meta information about the image
+ KisImageWSP image = m_document->image();
+
+ // the image must be locked at the higher levels
+ KIS_SAFE_ASSERT_RECOVER_NOOP(image->locked());
+ KisPaintDeviceSP pd = image->projection();
+
+ bool isThereAlpha = false;
+ KisSequentialConstIterator it(pd, image->bounds());
+ const KoColorSpace* cs = pd->colorSpace();
+ do {
+ if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) {
+ isThereAlpha = true;
+ break;
+ }
+ } while (it.nextPixel());
+
+ exportConfiguration->setProperty("ImageContainsTransparency", isThereAlpha);
+ exportConfiguration->setProperty("ColorModelID", cs->colorModelId().id());
+ exportConfiguration->setProperty("ColorDepthID", cs->colorDepthId().id());
+ bool sRGB = (cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive) && !cs->profile()->name().contains(QLatin1String("g10")));
+ exportConfiguration->setProperty("sRGB", sRGB);
+ }
+
+ }
+
+ KisPreExportChecker checker;
+ if (direction == Export) {
+ checker.check(m_document->image(), filter->exportChecks());
+ }
+
+ KisConfigWidget *wdg = filter->createConfigurationWidget(0, from, to);
+ bool alsoAsKra = false;
+
+ QStringList warnings = checker.warnings();
+ QStringList errors = checker.errors();
+
+ // Extra checks that cannot be done by the checker, because the checker only has access to the image.
+ if (!m_document->assistants().isEmpty() && typeName != m_document->nativeFormatMimeType()) {
+ warnings.append(i18nc("image conversion warning", "The image contains <b>assistants</b>. The assistants will not be saved."));
+ }
+ if (m_document->guidesConfig().hasGuides() && typeName != m_document->nativeFormatMimeType()) {
+ warnings.append(i18nc("image conversion warning", "The image contains <b>guides</b>. The guides will not be saved."));
+ }
+ if (!m_document->gridConfig().isDefault() && typeName != m_document->nativeFormatMimeType()) {
+ warnings.append(i18nc("image conversion warning", "The image contains a <b>custom grid configuration</b>. The configuration will not be saved."));
+ }
+
+ if (!batchMode() && !errors.isEmpty()) {
+ QString error = "<html><body><p><b>"
+ + i18n("Error: cannot save this image as a %1.", KisMimeDatabase::descriptionForMimeType(typeName))
+ + "</b> Reasons:</p>"
+ + "<p/><ul>";
+ Q_FOREACH(const QString &w, errors) {
+ error += "\n<li>" + w + "</li>";
+ }
+
+ error += "</ul>";
+
+ QMessageBox::critical(KisPart::instance()->currentMainwindow(), i18nc("@title:window", "Krita: Export Error"), error);
+ return KisImportExportFilter::UserCancelled;
+ }
+
+ if (!batchMode() && (wdg || !warnings.isEmpty())) {
+
+ KoDialog dlg;
+
+ dlg.setButtons(KoDialog::Ok | KoDialog::Cancel);
+ dlg.setWindowTitle(KisMimeDatabase::descriptionForMimeType(mimeType));
+
+ QWidget *page = new QWidget(&dlg);
+ QVBoxLayout *layout = new QVBoxLayout(page);
+
+ if (!checker.warnings().isEmpty()) {
+
+ if (showWarnings) {
+
+ QHBoxLayout *hLayout = new QHBoxLayout();
+
+ QLabel *labelWarning = new QLabel();
+ labelWarning->setPixmap(KisIconUtils::loadIcon("dialog-warning").pixmap(32, 32));
+ hLayout->addWidget(labelWarning);
+
+ KisPopupButton *bn = new KisPopupButton(0);
+
+ bn->setText(i18nc("Keep the extra space at the end of the sentence, please", "Warning: saving as %1 will lose information from your image. ", KisMimeDatabase::descriptionForMimeType(mimeType)));
+
+
+ hLayout->addWidget(bn);
+
+ layout->addLayout(hLayout);
+
+ QTextBrowser *browser = new QTextBrowser();
+ browser->setMinimumWidth(bn->width());
+ bn->setPopupWidget(browser);
+
+ QString warning = "<html><body><p><b>"
+ + i18n("You will lose information when saving this image as a %1.", KisMimeDatabase::descriptionForMimeType(typeName));
+
+ if (warnings.size() == 1) {
+ warning += "</b> Reason:</p>";
+ }
+ else {
+ warning += "</b> Reasons:</p>";
+ }
+ warning += "<p/><ul>";
+
+ Q_FOREACH(const QString &w, warnings) {
+ warning += "\n<li>" + w + "</li>";
+ }
+
+ warning += "</ul>";
+ browser->setHtml(warning);
+ }
+ }
+
+ if (wdg) {
+ QGroupBox *box = new QGroupBox(i18n("Options"));
+ QVBoxLayout *boxLayout = new QVBoxLayout(box);
+ wdg->setConfiguration(exportConfiguration);
+ boxLayout->addWidget(wdg);
+ layout->addWidget(box);
+ }
+
+
+ QCheckBox *chkAlsoAsKra = 0;
+ if (showWarnings) {
+ if (!checker.warnings().isEmpty()) {
+ chkAlsoAsKra = new QCheckBox(i18n("Also save your image as a Krita file."));
+ chkAlsoAsKra->setChecked(KisConfig().readEntry<bool>("AlsoSaveAsKra", false));
+ layout->addWidget(chkAlsoAsKra);
+ }
+ }
+
+ dlg.setMainWidget(page);
+ dlg.resize(dlg.minimumSize());
+
+ if (showWarnings || wdg) {
+ if (!dlg.exec()) {
+ return KisImportExportFilter::UserCancelled;
+ }
+ }
+
+ if (chkAlsoAsKra) {
+ KisConfig().writeEntry<bool>("AlsoSaveAsKra", chkAlsoAsKra->isChecked());
+ alsoAsKra = chkAlsoAsKra->isChecked();
+ }
+
+ if (wdg) {
+ exportConfiguration = wdg->configuration();
+ }
+
+ }
+
+ QFile io(location);
+
+ if (direction == Import) {
+ if (!io.exists()) {
+ return KisImportExportFilter::FileNotFound;
+ }
+
+ if (!io.open(QFile::ReadOnly)) {
+ return KisImportExportFilter::FileNotFound;
+ }
+ }
+ else if (direction == Export) {
+ if (!io.open(QFile::WriteOnly)) {
+ return KisImportExportFilter::CreationError;
+ }
}
- return d->progressUpdater.data();
+ else {
+ return KisImportExportFilter::BadConversionGraph;
+ }
+
+ if (!batchMode()) {
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ }
+
+ KisImportExportFilter::ConversionStatus status = filter->convert(m_document, &io, exportConfiguration);
+ io.close();
+
+ if (exportConfiguration) {
+ KisConfig().setExportConfiguration(typeName, exportConfiguration);
+ }
+
+ if (alsoAsKra) {
+ QString l = location + ".kra";
+ QByteArray ba = m_document->nativeFormatMimeType();
+ KisImportExportFilter *filter = filterForMimeType(QString::fromLatin1(ba), Export);
+ QFile f(l);
+ f.open(QIODevice::WriteOnly);
+ if (filter) {
+ filter->setFilename(l);
+ filter->convert(m_document, &f);
+ }
+ f.close();
+ delete filter;
+
+ }
+
+ if (!batchMode()) {
+ QApplication::restoreOverrideCursor();
+ }
+
+ return status;
+
}
+
#include <KisMimeDatabase.h>
diff --git a/libs/ui/KisImportExportManager.h b/libs/ui/KisImportExportManager.h
index bb2b3c0c14..6a29700ea1 100644
--- a/libs/ui/KisImportExportManager.h
+++ b/libs/ui/KisImportExportManager.h
@@ -1,204 +1,144 @@
-/* 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>
-
-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.
-*/
+/*
+ * Copyright (C) 2016 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.
+ */
#ifndef KIS_IMPORT_EXPORT_MANAGER_H
#define KIS_IMPORT_EXPORT_MANAGER_H
#include <QObject>
#include <QMap>
#include <QByteArray>
#include <QUrl>
-#include "KisFilterGraph.h"
#include "KisImportExportFilter.h"
#include "kritaui_export.h"
-class KisFilterChain;
+
class KisDocument;
class KoProgressUpdater;
/**
* @brief The class managing all the filters.
*
* This class manages all filters for a %Calligra application. Normally
* you will not have to use it, since KisMainWindow takes care of loading
* and saving documents.
*
* @ref KisFilter
*
* @author Kalle Dalheimer <kalle@kde.org>
* @author Torben Weis <weis@kde.org>
* @author Werner Trobin <trobin@kde.org>
*/
class KRITAUI_EXPORT KisImportExportManager : public QObject
{
Q_OBJECT
public:
/**
* This enum is used to distinguish the import/export cases
*/
enum Direction { Import = 1, Export = 2 };
/**
* Create a filter manager for a document
*/
explicit KisImportExportManager(KisDocument *document);
- /**
- * Create a filter manager for the Shape Collection docker.
- * @param mimeType the mimetype to import to.
- */
- explicit KisImportExportManager(const QByteArray& mimeType);
-
- /**
- * Create a filter manager for a filter which wants to embed something.
- * The path it passes is the file to convert. You cannot use
- * the @ref importDocument() method -- use @ref exportDocument() to convert
- * the file to the destination mimetype you prefer.
- *
- * @param path The location you want to export
- */
- explicit KisImportExportManager(const QString& location);
+public:
virtual ~KisImportExportManager();
/**
* Imports the specified document and returns the resultant filename
* (most likely some file in /tmp).
* @p path can be either a URL or a filename.
* @p documentMimeType gives importDocument a hint about what type
* the document may be. It can be left empty.
- * @p status signals the success/error of the conversion.
+ *
+ * @return status signals the success/error of the conversion.
* If the QString which is returned isEmpty() and the status is OK,
* then we imported the file directly into the document.
*/
- QString importDocument(const QString& location,
- const QString& documentMimeType,
- KisImportExportFilter::ConversionStatus& status);
+ KisImportExportFilter::ConversionStatus importDocument(const QString &location, const QString &mimeType);
/**
* @brief Exports the given file/document to the specified URL/mimetype.
*
* If @p mimeType is empty, then the closest matching Calligra part is searched
* and when the method returns @p mimeType contains this mimetype.
* Oh, well, export is a C++ keyword ;)
*/
- KisImportExportFilter::ConversionStatus exportDocument(const QString& location, QByteArray& mimeType, KisPropertiesConfigurationSP exportConfiguration = 0);
+ KisImportExportFilter::ConversionStatus exportDocument(const QString &location, QByteArray &mimeType, bool showWarnings = true, KisPropertiesConfigurationSP exportConfiguration = 0);
///@name Static API
//@{
/**
* Suitable for passing to KoFileDialog::setMimeTypeFilters. The default mime
* gets set by the "users" of this method, as we do not have enough
* information here.
* Optionally, @p extraNativeMimeTypes are added after the native mimetype.
*/
static QStringList mimeFilter(Direction direction);
/**
* @brief filterForMimeType loads the relevant import/export plugin and returns it. The caller
* is responsible for deleting it!
* @param mimetype the mimetype we want to import/export. If there's more than one plugin, the one
* with the highest weight as defined in the json description will be taken
* @param direction import or export
* @return a pointer to the filter plugin or 0 if none could be found
*/
static KisImportExportFilter *filterForMimeType(const QString &mimetype, Direction direction);
/**
* Set the filter manager is batch mode (no dialog shown)
* instead of the interactive mode (dialog shown)
*/
void setBatchMode(const bool batch);
/**
* Get if the filter manager is batch mode (true)
* or in interactive mode (true)
*/
- bool getBatchMode(void) const;
+ bool batchMode(void) const;
void setProgresUpdater(KoProgressUpdater *updater);
- /**
- * Return the KoProgressUpdater or 0 if there is none.
- **/
- KoProgressUpdater *progressUpdater() const;
+private Q_SLOTS:
+
+
private:
- // === API for KisFilterChains === (internal)
- // The friend methods are private in KisFilterChain and
- // just forward calls to the methods here. Should be
- // pretty safe.
- friend QString KisFilterChain::filterManagerImportFile() const;
- QString importFile() const
- {
- return m_importFileName;
- }
-
- friend QString KisFilterChain::filterManagerExportFile() const;
- QString exportFile() const
- {
- return m_exportFileName;
- }
-
- friend KisDocument *KisFilterChain::filterManagerKisDocument() const;
- KisDocument *document() const
- {
- return m_document;
- }
-
- friend int KisFilterChain::filterManagerDirection() const;
- int direction() const
- {
- return static_cast<int>(m_direction);
- }
-
- friend KisPropertiesConfigurationSP KisFilterChain::filterManagerExportConfiguration() const;
- KisPropertiesConfigurationSP exportConfiguration() const
- {
- return m_exportConfiguration;
- }
+
+ KisImportExportFilter::ConversionStatus convert(Direction direction, const QString &location, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration);
// Private API
KisImportExportManager(const KisImportExportManager& rhs);
KisImportExportManager &operator=(const KisImportExportManager& rhs);
- // Convert file path string or URL string into QUrl
- QUrl locationToUrl(QString location) const;
-
- void importErrorHelper(const QString& mimeType, const bool suppressDialog = false);
-
KisDocument *m_document;
- QString m_importFileName;
- QString m_exportFileName;
- CalligraFilter::Graph m_graph;
- Direction m_direction;
- KisPropertiesConfigurationSP m_exportConfiguration;
/// A static cache for the availability checks of filters
static QStringList m_importMimeTypes;
static QStringList m_exportMimeTypes;
class Private;
Private * const d;
};
#endif // __KO_FILTER_MANAGER_H__
diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp
index 4c33f50529..7e456d37d1 100644
--- a/libs/ui/KisMainWindow.cpp
+++ b/libs/ui/KisMainWindow.cpp
@@ -1,2433 +1,2384 @@
/* 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"
#include <KoConfig.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 <QToolButton>
#include <QSignalMapper>
#include <QTabBar>
#include <QMoveEvent>
#include <QUrl>
#include <QMessageBox>
#include <QTemporaryFile>
#include <QStatusBar>
#include <QMenu>
#include <QMenuBar>
#include <KisMimeDatabase.h>
#include <QMimeData>
#include <kactioncollection.h>
#include <QAction>
#include <kactionmenu.h>
#include <kis_debug.h>
#include <kedittoolbar.h>
#include <khelpmenu.h>
#include <klocalizedstring.h>
#include <kaboutdata.h>
#ifdef HAVE_KIO
#include <krecentdocument.h>
#endif
#include <krecentfilesaction.h>
#include <KoResourcePaths.h>
#include <ktoggleaction.h>
#include <ktoolbar.h>
#include <kmainwindow.h>
#include <kxmlguiwindow.h>
#include <kxmlguifactory.h>
#include <kxmlguiclient.h>
#include <kguiitem.h>
#include <kwindowconfig.h>
#include "KoDockFactoryBase.h"
#include "KoDockWidgetTitleBar.h"
#include "KoDocumentInfoDlg.h"
#include "KoDocumentInfo.h"
#include "KoFileDialog.h"
#include <kis_icon.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 <KisMimeDatabase.h>
#include <brushengine/kis_paintop_settings.h>
#include "dialogs/kis_about_application.h"
#include "dialogs/kis_delayed_save_dialog.h"
#include "dialogs/kis_dlg_preferences.h"
#include "kis_action.h"
#include "kis_action_manager.h"
#include "KisApplication.h"
#include "kis_canvas2.h"
#include "kis_canvas_controller.h"
#include "kis_canvas_resource_provider.h"
#include "kis_clipboard.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_config_notifier.h"
#include "kis_custom_image_widget.h"
#include <KisDocument.h>
#include "KisDocument.h"
#include "KisDocument.h"
#include "kis_group_layer.h"
#include "kis_icon_utils.h"
#include "kis_image_from_clipboard_widget.h"
#include "kis_image.h"
#include <KisImportExportFilter.h>
#include "KisImportExportManager.h"
#include "kis_mainwindow_observer.h"
#include "kis_node.h"
#include "KisOpenPane.h"
#include "kis_paintop_box.h"
#include "KisPart.h"
#include "KisPrintJob.h"
#include "kis_resource_server_provider.h"
#include "kis_signal_compressor_with_param.h"
#include "KisView.h"
#include "KisViewManager.h"
#include "thememanager.h"
#include "kis_animation_importer.h"
#include "dialogs/kis_dlg_import_image_sequence.h"
#include "kis_animation_exporter.h"
#include <mutex>
#ifdef Q_OS_WIN
#include <QtPlatformHeaders/QWindowsWindowFunctions>
#endif
class ToolDockerFactory : public KoDockFactoryBase
{
public:
ToolDockerFactory() : KoDockFactoryBase() { }
QString id() const override {
return "sharedtooldocker";
}
QDockWidget* createDockWidget() override {
KoToolDocker* dockWidget = new KoToolDocker();
dockWidget->setTabEnabled(false);
return dockWidget;
}
DockPosition defaultDockPosition() const override {
return DockRight;
}
};
class Q_DECL_HIDDEN KisMainWindow::Private
{
public:
Private(KisMainWindow *parent)
: q(parent)
, 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))
, mdiArea(new QMdiArea(parent))
, windowMapper(new QSignalMapper(parent))
, documentMapper(new QSignalMapper(parent))
- , lastExportSpecialOutputFlag(0)
{
}
~Private() {
qDeleteAll(toolbarList);
}
KisMainWindow *q {0};
KisViewManager *viewManager {0};
QPointer<KisView> activeView;
QPointer<QProgressBar> progress;
QPointer<QToolButton> progressCancel;
QMutex progressMutex;
QList<QAction *> toolbarList;
bool firstTime {true};
bool windowSizeDirty {false};
bool readOnly {false};
bool isImporting {false};
bool isExporting {false};
bool noCleanup {false};
KisAction *showDocumentInfo {0};
KisAction *saveAction {0};
KisAction *saveActionAs {0};
// KisAction *printAction;
// KisAction *printActionPreview;
KisAction *exportPdf {0};
KisAction *importAnimation {0};
KisAction *closeAll {0};
// KisAction *reloadFile;
KisAction *importFile {0};
KisAction *exportFile {0};
KisAction *undo {0};
KisAction *redo {0};
KisAction *newWindow {0};
KisAction *close {0};
KisAction *mdiCascade {0};
KisAction *mdiTile {0};
KisAction *mdiNextWindow {0};
KisAction *mdiPreviousWindow {0};
KisAction *toggleDockers {0};
KisAction *toggleDockerTitleBars {0};
KisAction *expandingSpacers[2];
KActionMenu *dockWidgetMenu;
KActionMenu *windowMenu;
KActionMenu *documentMenu;
KHelpMenu *helpMenu {0};
KRecentFilesAction *recentFiles {0};
QUrl lastExportUrl;
QMap<QString, QDockWidget *> dockWidgetsMap;
QMap<QDockWidget *, bool> dockWidgetVisibilityMap;
QByteArray dockerStateBeforeHiding;
KoToolDocker *toolOptionsDocker {0};
QCloseEvent *deferredClosingEvent {0};
Digikam::ThemeManager *themeManager {0};
QMdiArea *mdiArea;
QMdiSubWindow *activeSubWindow {0};
QSignalMapper *windowMapper;
QSignalMapper *documentMapper;
QByteArray lastExportedFormat;
- int lastExportSpecialOutputFlag {0};
QScopedPointer<KisSignalCompressorWithParam<int> > tabSwitchCompressor;
QMutex savingEntryMutex;
KisActionManager * actionManager() {
return viewManager->actionManager();
}
QTabBar* findTabBarHACK() {
QObjectList objects = mdiArea->children();
Q_FOREACH (QObject *object, objects) {
QTabBar *bar = qobject_cast<QTabBar*>(object);
if (bar) {
return bar;
}
}
return 0;
}
};
KisMainWindow::KisMainWindow()
: KXmlGuiWindow()
, d(new Private(this))
{
KisConfig cfg;
d->viewManager = new KisViewManager(this, actionCollection());
KConfigGroup group( KSharedConfig::openConfig(), "theme");
d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), 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
+#ifdef Q_OS_OSX
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);
KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), d->viewManager);
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));
d->toolOptionsDocker->toggleViewAction()->setEnabled(true);
}
QMap<QString, QAction*> dockwidgetActions;
dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction();
Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) {
KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker);
QDockWidget *dw = createDockWidget(factory);
dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction();
}
if (d->toolOptionsDocker) {
dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction();
}
connect(KoToolManager::instance(), SIGNAL(toolOptionWidgetsChanged(QList<QPointer<QWidget> >)), this, SLOT(newOptionWidgets(QList<QPointer<QWidget> >)));
Q_FOREACH (QString title, dockwidgetActions.keys()) {
d->dockWidgetMenu->addAction(dockwidgetActions[title]);
}
Q_FOREACH (QDockWidget *wdg, dockWidgets()) {
if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) {
wdg->setVisible(true);
}
}
Q_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);
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("krita", false);
subWindowActivated();
updateWindowMenu();
if (isHelpMenuEnabled() && !d->helpMenu) {
// workaround for KHelpMenu (or rather KAboutData::applicationData()) internally
// not using the Q*Application metadata ATM, which results e.g. in the bugreport wizard
// not having the app version preset
// fixed hopefully in KF5 5.22.0, patch pending
QGuiApplication *app = qApp;
KAboutData aboutData(app->applicationName(), app->applicationDisplayName(), app->applicationVersion());
aboutData.setOrganizationDomain(app->organizationDomain().toUtf8());
d->helpMenu = new KHelpMenu(this, aboutData, false);
// workaround-less version:
// d->helpMenu = new KHelpMenu(this, QString()/*unused*/, false);
// The difference between using KActionCollection->addAction() is that
// these actions do not get tied to the MainWindow. What does this all do?
KActionCollection *actions = d->viewManager->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' libs 4''s 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;
Q_FOREACH (QAction* action, actionCollection()->actions()) {
if(action->shortcut() == QKeySequence(0)) {
continue;
}
dbgKrita << "shortcut " << action->text() << " " << action->shortcut();
Q_ASSERT(!existingShortcuts.contains(action->shortcut()));
existingShortcuts.insert(action->shortcut());
}
#endif
configChanged();
// If we have customized the toolbars, load that first
setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita.xmlgui"));
setXMLFile(":/kxmlgui5/krita.xmlgui");
guiFactory()->addClient(this);
// Create and plug toolbar list for Settings menu
QList<QAction *> toolbarList;
Q_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
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();
#ifdef Q_OS_WIN
auto w = qApp->activeWindow();
if (w) QWindowsWindowFunctions::setHasBorderInFullScreen(w->windowHandle(), true);
#endif
QTimer::singleShot(1000, this, SLOT(checkSanity()));
{
using namespace std::placeholders; // For _1 placeholder
std::function<void (int)> callback(
std::bind(&KisMainWindow::switchTab, this, _1));
d->tabSwitchCompressor.reset(
new KisSignalCompressorWithParam<int>(500, callback, KisSignalCompressor::FIRST_INACTIVE));
}
}
void KisMainWindow::setNoCleanup(bool noCleanup)
{
d->noCleanup = noCleanup;
}
KisMainWindow::~KisMainWindow()
{
// Q_FOREACH (QAction *ac, actionCollection()->actions()) {
// QAction *action = qobject_cast<QAction*>(ac);
// if (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(QAction::ActiveShortcut).toString()
// << "defaultShortcut=" << action->shortcut(QAction::DefaultShortcut).toString()
// << "isCheckable=" << QString((action->isChecked() ? "true" : "false"))
// << "statusTip=" << action->statusTip()
// << "/>" ;
// }
// else {
// dbgKrita << "Got a QAction:" << ac->objectName();
// }
// }
// 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)
{
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());
/**
* Hack alert!
*
* Here we explicitly request KoToolManager to emit all the tool
* activation signals, to reinitialize the tool options docker.
*
* That is needed due to a design flaw we have in the
* initialization procedure. The tool in the KoToolManager is
* initialized in KisView::setViewManager() calls, which
* happens early enough. During this call the tool manager
* requests KoCanvasControllerWidget to emit the signal to
* update the widgets in the tool docker. *But* at that moment
* of time the view is not yet connected to the main window,
* because it happens in KisViewManager::setCurrentView a bit
* later. This fact makes the widgets updating signals be lost
* and never reach the tool docker.
*
* So here we just explicitly call the tool activation stub.
*/
KoToolManager::instance()->initializeCurrentToolForCanvas();
if (d->mdiArea->subWindowList().size() == 1) {
imageView->showMaximized();
}
else {
imageView->show();
}
// No, no, no: do not try to call this _before_ the show() has
// been called on the view; only when that has happened is the
// opengl context active, and very bad things happen if we tell
// the dockers to update themselves with a view if the opengl
// context is not active.
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?
Q_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( KSharedConfig::openConfig(), "theme");
group.writeEntry("Theme", d->themeManager->currentThemeName());
// reload action icons!
Q_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 QUrl &url)
{
dbgUI << "KisMainWindow::addRecentURL url=" << url.toDisplayString();
// 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.adjusted(QUrl::StripTrailingSlash).toLocalFile();
const QStringList tmpDirs = KoResourcePaths::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
#ifdef HAVE_KIO
if (ok) {
KRecentDocument::add(QUrl::fromLocalFile(path));
}
#endif
}
#ifdef HAVE_KIO
else {
KRecentDocument::add(url.adjusted(QUrl::StripTrailingSlash));
}
#endif
if (ok) {
d->recentFiles->addUrl(url);
}
saveRecentFiles();
}
}
void KisMainWindow::saveRecentFiles()
{
// Save list of recent files
KSharedConfigPtr config = KSharedConfig::openConfig();
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
Q_FOREACH (KMainWindow* window, KMainWindow::memberList())
static_cast<KisMainWindow *>(window)->reloadRecentFileList();
}
void KisMainWindow::reloadRecentFileList()
{
d->recentFiles->loadEntries( KSharedConfig::openConfig()->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().isEmpty())
d->saveAction->setToolTip(i18n("Save as %1", d->activeView->document()->url().fileName()));
else
d->saveAction->setToolTip(i18n("Save"));
}
}
void KisMainWindow::updateCaption(const QString & caption, bool mod)
{
dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")";
#ifdef KRITA_ALPHA
setCaption(QString("ALPHA %1: %2").arg(KRITA_ALPHA).arg(caption), mod);
return;
#endif
#ifdef KRITA_BETA
setCaption(QString("BETA %1: %2").arg(KRITA_BETA).arg(caption), mod);
return;
#endif
#ifdef KRITA_RC
setCaption(QString("RELEASE CANDIDATE %1: %2").arg(KRITA_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 QUrl &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 QUrl &url, KisDocument *newdoc)
{
if (!url.isLocalFile()) {
qDebug() << "KisMainWindow::openDocumentInternal. Not a local file:" << url;
return false;
}
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);
+ bool openRet = (!d->isImporting) ? newdoc->openUrl(url) : newdoc->importDocument(url);
if (!openRet) {
delete newdoc;
return false;
}
KisPart::instance()->addDocument(newdoc);
updateReloadFileAction(newdoc);
if (!QFileInfo(url.toLocalFile()).isWritable()) {
setReadWrite(false);
}
return true;
}
void KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document)
{
KisView *view = KisPart::instance()->createView(document, resourceManager(), actionCollection(), this);
addView(view);
emit guiLoadingFinished();
}
QStringList KisMainWindow::showOpenFileDialog()
{
KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument");
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import));
- dialog.setCaption(isImporting() ? i18n("Import Images") : i18n("Open Images"));
+ dialog.setCaption(d->isImporting ? i18n("Import Images") : i18n("Open Images"));
return dialog.filenames();
}
// Separate from openDocument to handle async loading (remote URLs)
void KisMainWindow::slotLoadCompleted()
{
KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
+ if (newdoc && newdoc->image()) {
+ addViewAndNotifyLoadingCompleted(newdoc);
- addViewAndNotifyLoadingCompleted(newdoc);
-
- 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 &)));
+ 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();
+ emit loadCompleted();
+ }
}
void KisMainWindow::slotLoadCanceled(const QString & errMsg)
{
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)
{
dbgUI << "KisMainWindow::slotSaveCanceled";
if (!errMsg.isEmpty()) // empty when canceled by user
QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
slotSaveCompleted();
}
void 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::hackIsSaving() const
{
StdLockableWrapper<QMutex> wrapper(&d->savingEntryMutex);
std::unique_lock<StdLockableWrapper<QMutex>> l(wrapper, std::try_to_lock);
return !l.owns_lock();
}
-bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool silent, int specialOutputFlag)
+bool KisMainWindow::saveDocument(KisDocument *document, bool saveas)
{
if (!document) {
return true;
}
/**
* Make sure that we cannot enter this method twice!
*
* The lower level functions may call processEvents() so
- * double-entry is quite possible to achive. Here we try to lock
+ * double-entry is quite possible to achieve. Here we try to lock
* the mutex, and if it is failed, just cancel saving.
*/
StdLockableWrapper<QMutex> wrapper(&d->savingEntryMutex);
std::unique_lock<StdLockableWrapper<QMutex>> l(wrapper, std::try_to_lock);
if (!l.owns_lock()) return false;
KisDelayedSaveDialog dlg(document->image(), this);
dlg.blockIfImageIsBusy();
if (dlg.result() != QDialog::Accepted) {
return false;
}
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 &)));
QUrl oldURL = document->url();
QString oldFile = document->localFilePath();
QByteArray _native_format = document->nativeFormatMimeType();
QByteArray oldOutputFormat = document->outputMimeType();
- int oldSpecialOutputFlag = document->specialOutputFlag();
-
QUrl suggestedURL = document->url();
QStringList mimeFilter;
- if (specialOutputFlag) {
- mimeFilter = KisMimeDatabase::suffixesForMimeType(_native_format);
- }
- else {
- mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export);
- }
-
+ mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export);
- if (!mimeFilter.contains(oldOutputFormat) && !isExporting()) {
+ if (!mimeFilter.contains(oldOutputFormat) && !d->isExporting) {
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 = KisMimeDatabase::suffixesForMimeType(_native_format).first();
if (!ext.isEmpty()) {
if (c < 0)
suggestedFilename = 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 = suggestedURL.adjusted(QUrl::RemoveFilename);
suggestedURL.setPath(suggestedURL.path() + 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()) {
+ if (d->isExporting && !d->lastExportUrl.isEmpty()) {
dialog.setDefaultDir(d->lastExportUrl.toLocalFile(), true);
}
else {
dialog.setDefaultDir(suggestedURL.toLocalFile(), true);
}
// Default to all supported file types if user is exporting, otherwise use Krita default
dialog.setMimeTypeFilters(mimeFilter, QString(_native_format));
QUrl newURL = QUrl::fromUserInput(dialog.filename());
if (newURL.isLocalFile()) {
QString fn = newURL.toLocalFile();
if (QFileInfo(fn).completeSuffix().isEmpty()) {
fn.append(KisMimeDatabase::suffixesForMimeType(_native_format).first());
newURL = QUrl::fromLocalFile(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) {
- QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newURL.toLocalFile());
- outputFormat = outputFormatString.toLatin1();
- }
+ QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newURL.toLocalFile());
+ 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);
+ if (!d->isExporting) {
+ justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType());
+ }
+ else {
+ justChangingFilterOptions = (newURL == d->lastExportUrl) && (outputFormat == d->lastExportedFormat);
+ }
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) {
- //dbgKrita << "save to directory: " << newURL.url();
- }
- }
-
if (bOk) {
bool wantToSave = true;
// don't change this line unless you know what you're doing :)
- if (!justChangingFilterOptions || document->confirmNonNativeSave(isExporting())) {
+ if (!justChangingFilterOptions) {
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"
+ // 1. A check like "d->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
+ document->setOutputMimeType(outputFormat);
+ if (!d->isExporting) { // Save As
ret = document->saveAs(newURL);
if (ret) {
dbgUI << "Successful Save As!";
addRecentURL(newURL);
setReadWrite(true);
} else {
dbgUI << "Failed Save As!";
document->setUrl(oldURL);
document->setLocalFilePath(oldFile);
- document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag);
+ document->setOutputMimeType(oldOutputFormat);
}
} 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);
+ document->setOutputMimeType(oldOutputFormat);
}
- 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();
- }
+ // be sure document has the correct outputMimeType!
+ if (d->isExporting || document->isModified()) {
+ ret = document->save();
+ }
- if (!ret) {
- dbgUI << "Failed Save!";
- document->setUrl(oldURL);
- document->setLocalFilePath(oldFile);
- }
- } else
- ret = false;
+ if (!ret) {
+ dbgUI << "Failed Save!";
+ document->setUrl(oldURL);
+ document->setLocalFilePath(oldFile);
+ }
}
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();
QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
if ((action) && (action->isChecked())) {
action->setChecked(false);
}
KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow");
cfg.writeEntry("ko_geometry", saveGeometry().toBase64());
cfg.writeEntry("ko_windowstate", saveState().toBase64());
{
KConfigGroup group( KSharedConfig::openConfig(), "theme");
group.writeEntry("Theme", d->themeManager->currentThemeName());
}
-
- 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;
Q_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
Q_FOREACH (QDockWidget* dockWidget, d->dockWidgetsMap)
dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget));
}
} else {
e->setAccepted(false);
}
}
void KisMainWindow::saveWindowSettings()
{
KSharedConfigPtr config = KSharedConfig::openConfig();
if (d->windowSizeDirty ) {
dbgUI << "KisMainWindow::saveWindowSettings";
KConfigGroup group = config->group("MainWindow");
KWindowConfig::saveWindowSize(windowHandle(), 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 = KSharedConfig::openConfig()->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()));
dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x());
dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y());
dockGroup.writeEntry("width", (int) i.value()->widget()->width());
dockGroup.writeEntry("height", (int) i.value()->widget()->height());
}
}
}
KSharedConfig::openConfig()->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());
d->viewManager->setCurrentView(view);
}
void KisMainWindow::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasUrls() ||
event->mimeData()->hasFormat("application/x-krita-node") ||
event->mimeData()->hasFormat("application/x-qt-image")) {
event->accept();
}
}
void KisMainWindow::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
Q_FOREACH (const QUrl &url, event->mimeData()->urls()) {
openDocument(url);
}
}
}
void KisMainWindow::dragMoveEvent(QDragMoveEvent * event)
{
QTabBar *tabBar = d->findTabBarHACK();
if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) {
qWarning() << "WARNING!!! Cannot find QTabBar in the main window! Looks like Qt has changed behavior. Drag & Drop between multiple tabs might not work properly (tabs will not switch automatically)!";
}
if (tabBar && tabBar->isVisible()) {
QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos()));
if (tabBar->rect().contains(pos)) {
const int tabIndex = tabBar->tabAt(pos);
if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) {
d->tabSwitchCompressor->start(tabIndex);
}
} else if (d->tabSwitchCompressor->isActive()) {
d->tabSwitchCompressor->stop();
}
}
}
void KisMainWindow::dragLeaveEvent(QDragLeaveEvent * /*event*/)
{
if (d->tabSwitchCompressor->isActive()) {
d->tabSwitchCompressor->stop();
}
}
void KisMainWindow::switchTab(int index)
{
QTabBar *tabBar = d->findTabBarHACK();
if (!tabBar) return;
tabBar->setCurrentIndex(index);
}
void KisMainWindow::slotFileNew()
{
const QStringList mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Import);
KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/"));
startupWidget->setWindowModality(Qt::WindowModal);
KisConfig cfg;
int w = cfg.defImageWidth();
int h = cfg.defImageHeight();
const double resolution = cfg.defImageResolution();
const QString colorModel = cfg.defColorModel();
const QString colorDepth = cfg.defaultColorDepth();
const QString colorProfile = cfg.defColorProfile();
CustomDocumentWidgetItem item;
item.widget = new KisCustomImageWidget(startupWidget,
w,
h,
resolution,
colorModel,
colorDepth,
colorProfile,
i18n("Unnamed"));
item.icon = "application-x-krita";
startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon);
QSize sz = KisClipboard::instance()->clipSize();
if (sz.isValid() && sz.width() != 0 && sz.height() != 0) {
w = sz.width();
h = sz.height();
}
item.widget = new KisImageFromClipboard(startupWidget,
w,
h,
resolution,
colorModel,
colorDepth,
colorProfile,
i18n("Unnamed"));
item.title = i18n("Create from Clipboard");
item.icon = "klipper";
startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon);
// calls deleteLater
connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*)));
// calls deleteLater
connect(startupWidget, SIGNAL(openTemplate(const QUrl&)), KisPart::instance(), SLOT(openTemplate(const QUrl&)));
startupWidget->exec();
// Cancel calls deleteLater...
}
void KisMainWindow::slotFileOpen()
{
QStringList urls = showOpenFileDialog();
if (urls.isEmpty())
return;
Q_FOREACH (const QString& url, urls) {
if (!url.isEmpty()) {
bool res = openDocument(QUrl::fromLocalFile(url));
if (!res) {
warnKrita << "Loading" << url << "failed";
}
}
}
}
void KisMainWindow::slotFileOpenRecent(const QUrl &url)
{
- (void) openDocument(QUrl(url));
+ (void) openDocument(QUrl::fromLocalFile(url.toLocalFile()));
}
void KisMainWindow::slotFileSave()
{
- if (saveDocument(d->activeView->document()))
+ if (saveDocument(d->activeView->document())) {
emit documentSaved();
+ }
}
void KisMainWindow::slotFileSaveAs()
{
- if (saveDocument(d->activeView->document(), true))
+ 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()
{
Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
if (subwin) {
if(!subwin->close())
return false;
}
}
updateCaption();
return true;
}
void KisMainWindow::slotFileQuit()
{
if(!slotFileCloseAll())
return;
close();
Q_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)
+KisPrintJob* KisMainWindow::exportToPdf(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;
+ KoPageLayout pageLayout;
+ pageLayout.width = 0;
+ pageLayout.height = 0;
+ pageLayout.topMargin = 0;
+ pageLayout.bottomMargin = 0;
+ pageLayout.leftMargin = 0;
+ pageLayout.rightMargin = 0;
+
if (pdfFileName.isEmpty()) {
KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
QString defaultDir = group.readEntry("SavePdfDialog");
if (defaultDir.isEmpty())
defaultDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
QUrl startUrl = QUrl::fromLocalFile(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();
+ QString fileName = startUrl.toLocalFile();
fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" );
startUrl = startUrl.adjusted(QUrl::RemoveFilename);
startUrl.setPath(startUrl.path() + 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");
QUrl url = QUrl::fromUserInput(dialog.filename());
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::importAnimation()
{
if (!activeView()) return;
KisDocument *document = activeView()->document();
if (!document) return;
KisDlgImportImageSequence dlg(this, document);
if (dlg.exec() == QDialog::Accepted) {
QStringList files = dlg.files();
int firstFrame = dlg.firstFrame();
int step = dlg.step();
document->setFileProgressProxy();
document->setFileProgressUpdater(i18n("Import frames"));
KisAnimationImporter importer(document);
KisImportExportFilter::ConversionStatus status = importer.import(files, firstFrame, step);
document->clearFileProgressUpdater();
document->clearFileProgressProxy();
if (status != KisImportExportFilter::OK && status != KisImportExportFilter::InternalError) {
QString msg = KisImportExportFilter::conversionStatusString(status);
if (!msg.isEmpty())
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
}
activeView()->canvasBase()->refetchDataFromImage();
}
}
void KisMainWindow::slotConfigureToolbars()
{
KConfigGroup group = KSharedConfig::openConfig()->group("krita");
saveMainWindowSettings(group);
KEditToolBar edit(factory(), this);
connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
(void) edit.exec();
applyToolBarLayout();
}
void KisMainWindow::slotNewToolbarConfig()
{
applyMainWindowSettings(KSharedConfig::openConfig()->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)
{
//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 = KSharedConfig::openConfig()->group("krita");
saveMainWindowSettings(group);
}
} else
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();
StdLockableWrapper<QMutex> wrapper(&d->progressMutex);
std::unique_lock<StdLockableWrapper<QMutex>> l(wrapper, std::try_to_lock);
if (!l.owns_lock()) return;
dbgUI << "KisMainWindow::slotProgress" << value;
if (value <= -1 || value >= 100) {
if (d->progress) {
statusBar()->removeWidget(d->progress);
delete d->progress;
d->progress = 0;
disconnect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled()));
statusBar()->removeWidget(d->progressCancel);
delete d->progressCancel;
d->progressCancel = 0;
}
d->firstTime = true;
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;
disconnect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled()));
statusBar()->removeWidget(d->progressCancel);
delete d->progressCancel;
d->progress = 0;
}
d->progressCancel = new QToolButton(statusBar());
d->progressCancel->setMaximumHeight(statusBar()->fontMetrics().height());
d->progressCancel->setIcon(KisIconUtils::loadIcon("process-stop"));
statusBar()->addPermanentWidget(d->progressCancel);
d->progress = new QProgressBar(statusBar());
d->progress->setMaximumHeight(statusBar()->fontMetrics().height());
d->progress->setRange(0, 100);
statusBar()->addPermanentWidget(d->progress);
connect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled()));
d->progress->show();
d->progressCancel->show();
d->firstTime = false;
}
if (!d->progress.isNull()) {
d->progress->setValue(value);
}
qApp->processEvents();
}
void KisMainWindow::slotProgressCanceled()
{
emit sigProgressCanceled();
}
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;
}
QUrl 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()
{
dbgUI << "slotImportFile()";
d->isImporting = true;
slotFileOpen();
d->isImporting = false;
}
void KisMainWindow::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) {
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());
}
titleBar->setFont(KoDockRegistry::dockFont());
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 = KSharedConfig::openConfig()->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 = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id());
collapsed = group.readEntry("Collapsed", collapsed);
locked = group.readEntry("Locked", locked);
//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()];
}
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
dockWidget->setAttribute(Qt::WA_MacSmallSize, true);
#endif
dockWidget->setFont(KoDockRegistry::dockFont());
connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts()));
return dockWidget;
}
void KisMainWindow::forceDockTabFonts()
{
Q_FOREACH (QObject *child, children()) {
if (child->inherits("QTabBar")) {
((QTabBar *)child)->setFont(KoDockRegistry::dockFont());
}
}
}
QList<QDockWidget*> KisMainWindow::dockWidgets() const
{
return d->dockWidgetsMap.values();
}
QDockWidget* KisMainWindow::dockWidget(const QString &id)
{
if (!d->dockWidgetsMap.contains(id)) return 0;
return d->dockWidgetsMap[id];
}
QList<KoCanvasObserverBase*> KisMainWindow::canvasObservers() const
{
QList<KoCanvasObserverBase*> observers;
Q_FOREACH (QDockWidget *docker, dockWidgets()) {
KoCanvasObserverBase *observer = dynamic_cast<KoCanvasObserverBase*>(docker);
if (observer) {
observers << observer;
}
else {
warnKrita << docker << "is not a canvas observer";
}
}
return observers;
}
void KisMainWindow::toggleDockersVisibility(bool visible)
{
if (!visible) {
d->dockerStateBeforeHiding = saveState();
Q_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());
Q_FOREACH (QToolBar *tb, toolBars()) {
if (tb->objectName() == "BrushesAndStuff") {
tb->setEnabled(enabled);
}
}
updateCaption();
d->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();
Q_FOREACH (QPointer<KisDocument> doc, KisPart::instance()->documents()) {
if (doc) {
QString title = doc->url().toDisplayString();
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().toDisplayString());
}
else {
text = i18n("%1 %2", i + 1, child->document()->url().toDisplayString());
}
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);
//dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow;
if (subwin && subwin != d->activeSubWindow) {
KisView *view = qobject_cast<KisView *>(subwin->widget());
//dbgKrita << "\t" << view << activeView();
if (view && view != activeView()) {
setActiveView(view);
}
d->activeSubWindow = subwin;
}
updateWindowMenu();
d->actionManager()->updateGUI();
}
void KisMainWindow::configChanged()
{
KisConfig cfg;
QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry<int>("mdi_viewmode", (int)QMdiArea::TabbedView);
d->mdiArea->setViewMode(viewMode);
Q_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( KSharedConfig::openConfig(), "theme");
d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
d->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);
addViewAndNotifyLoadingCompleted(doc);
d->actionManager()->updateGUI();
}
void KisMainWindow::newWindow()
{
KisPart::instance()->createMainWindow()->show();
}
void KisMainWindow::closeCurrentWindow()
{
d->mdiArea->currentSubWindow()->close();
d->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(this);
dlg.exec();
}
QPointer<KisView>KisMainWindow::activeKisView()
{
if (!d->mdiArea) return 0;
QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow();
//dbgKrita << "activeKisView" << activeSubWindow;
if (!activeSubWindow) return 0;
return qobject_cast<KisView*>(activeSubWindow->widget());
}
void KisMainWindow::newOptionWidgets(const QList<QPointer<QWidget> > &optionWidgetList)
{
Q_FOREACH (QWidget *w, optionWidgetList) {
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
w->setAttribute(Qt::WA_MacSmallSize, true);
#endif
w->setFont(KoDockRegistry::dockFont());
}
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)
QString extension = KisMimeDatabase::suffixesForMimeType(d->activeView->document()->outputMimeType()).first();
if (title.endsWith(extension)) {
title.chop(extension.length());
}
}
if (title.isEmpty()) {
// #139905
title = i18n("%1 unsaved document (%2)", qApp->applicationDisplayName(),
QLocale().toString(QDate::currentDate(), QLocale::ShortFormat));
}
printer.setDocName(title);
}
void KisMainWindow::createActions()
{
KisActionManager *actionManager = d->actionManager();
actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew()));
actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen()));
actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit()));
actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars()));
actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool)));
d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles()));
KSharedConfigPtr configPtr = KSharedConfig::openConfig();
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 = actionManager->createAction("file_export_pdf");
connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf()));
d->importAnimation = actionManager->createAction("file_import_animation");
d->importAnimation->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation()));
d->closeAll = actionManager->createAction("file_close_all");
connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll()));
// d->reloadFile = actionManager->createAction("file_reload_file");
// d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED);
// connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile()));
d->importFile = actionManager->createAction("file_import_file");
connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile()));
d->exportFile = actionManager->createAction("file_export_file");
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 = actionManager->createAction("file_documentinfo");
connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo()));
d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this));
d->themeManager->registerThemeActions(actionCollection());
connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged()));
d->toggleDockers = actionManager->createAction("view_toggledockers");
d->toggleDockers->setChecked(true);
connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool)));
d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars");
{
KisConfig cfg;
d->toggleDockerTitleBars->setChecked(cfg.showDockerTitleBars());
}
connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool)));
actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu);
actionCollection()->addAction("window", d->windowMenu);
d->mdiCascade = actionManager->createAction("windows_cascade");
connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows()));
d->mdiTile = actionManager->createAction("windows_tile");
connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows()));
d->mdiNextWindow = actionManager->createAction("windows_next");
connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow()));
d->mdiPreviousWindow = actionManager->createAction("windows_previous");
connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow()));
d->newWindow = actionManager->createAction("view_newwindow");
connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow()));
d->close = actionManager->createAction("file_close");
connect(d->close, SIGNAL(triggered()), SLOT(closeCurrentWindow()));
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( KSharedConfig::openConfig(), "MainWindow");
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://docs.krita.org"));
}
void KisMainWindow::showDockerTitleBars(bool show)
{
Q_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/libs/ui/KisMainWindow.h b/libs/ui/KisMainWindow.h
index e0d770a8c8..2581bacbb5 100644
--- a/libs/ui/KisMainWindow.h
+++ b/libs/ui/KisMainWindow.h
@@ -1,482 +1,454 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright (C) 2000-2004 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.
*/
#ifndef KIS_MAIN_WINDOW_H
#define KIS_MAIN_WINDOW_H
#include "kritaui_export.h"
#include <QPointer>
#include <QPrinter>
#include <xmlgui/kxmlguiwindow.h>
#include <QUrl>
#include <KoCanvasObserverBase.h>
#include <KoCanvasSupervisor.h>
#include "KisView.h"
class QCloseEvent;
class QMoveEvent;
struct KoPageLayout;
class KoCanvasResourceManager;
class KisDocument;
-class KisView;
class KisPrintJob;
class KoDockFactoryBase;
class QDockWidget;
class KisView;
class KisViewManager;
/**
* @brief Main window for Krita
*
* This class is used to represent a main window within a Krita session. Each
* main window contains a menubar and some toolbars, and potentially several
* views of several canvases.
*
*/
class KRITAUI_EXPORT KisMainWindow : public KXmlGuiWindow, public KoCanvasSupervisor
{
Q_OBJECT
public:
/**
* Constructor.
*
* Initializes a Calligra main window (with its basic GUI etc.).
*/
explicit KisMainWindow();
/**
* Destructor.
*/
virtual ~KisMainWindow();
/**
* Update caption from document info - call when document info
* (title in the about page) changes.
*/
void updateCaption();
// If noCleanup is set, KisMainWindow will not delete the root document
// or part manager on destruction.
void setNoCleanup(bool noCleanup);
/**
* @brief showView shows the given view. Override this if you want to show
* the view in a different way than by making it the central widget, for instance
* as an QMdiSubWindow
*/
virtual void showView(KisView *view);
/**
* @returns the currently active view
*/
KisView *activeView() const;
/**
* Sets the maximum number of recent documents entries.
*/
void setMaxRecentItems(uint _number);
/**
* The document opened a URL -> store into recent documents list.
*/
void addRecentURL(const QUrl &url);
/**
* Load the desired document and show it.
* @param url the URL to open
*
* @return TRUE on success.
*/
bool openDocument(const QUrl &url);
void setReadWrite(bool readwrite);
/// Return the list of dock widgets belonging to this main window.
QList<QDockWidget*> dockWidgets() const;
QDockWidget* dockWidget(const QString &id);
QList<KoCanvasObserverBase*> canvasObservers() const;
KoCanvasResourceManager *resourceManager() const;
int viewCount() const;
/**
* A wrapper around restoreState
* @param state the saved state
* @return TRUE on success
*/
bool restoreWorkspace(const QByteArray &state);
KisViewManager *viewManager() const;
void addViewAndNotifyLoadingCompleted(KisDocument *document);
QStringList showOpenFileDialog();
/**
* Shows if the main window is saving anything right now. If the
* user presses Ctrl+W too fast, then the document can be close
* before the saving is completed. I'm not sure if it is fixable
* in any way without avoiding using porcessEvents()
* everywhere (DK)
*
* Don't use it unless you have no option.
*/
bool hackIsSaving() const;
Q_SIGNALS:
/**
* This signal is emitted if the document has been saved successfully.
*/
void documentSaved();
/// This signal is emitted when this windows has finished loading of a
/// document. The document may be opened in another window in the end.
/// In this case, the signal means there is no link between the window
/// and the document anymore.
void loadCompleted();
/// This signal is emitted right after the docker states have been succefully restored from config
void restoringDone();
/// This signal is emitted when the color theme changes
void themeChanged();
/// This signal is emitted when the shortcut key configuration has changed
void keyBindingsChanged();
void guiLoadingFinished();
/// This signal is emitted when the user clicked on the progressbar cancel
void sigProgressCanceled();
public Q_SLOTS:
/**
* Slot for opening a new document.
*
* If the current document is empty, the new document replaces it.
* If not, a new mainwindow will be opened for showing the document.
*/
void slotFileNew();
/**
* Slot for opening a saved file.
*
* If the current document is empty, the opened document replaces it.
* If not a new mainwindow will be opened for showing the opened file.
*/
void slotFileOpen();
/**
* Slot for opening a file among the recently opened files.
*
* If the current document is empty, the opened document replaces it.
* If not a new mainwindow will be opened for showing the opened file.
*/
void slotFileOpenRecent(const QUrl &);
/**
* @brief slotPreferences open the preferences dialog
*/
void slotPreferences();
/**
* Saves the current document with the current name.
*/
void slotFileSave();
- KisPrintJob* exportToPdf(const QString &pdfFileName = QString());
+ KisPrintJob* exportToPdf(QString pdfFileName = QString());
void slotProgress(int value);
/**
* Saves the document, asking for a filename if necessary.
*
* @param saveas if set to TRUE the user is always prompted for a filename
- *
* @param silent if set to TRUE rootDocument()->setTitleModified will not be called.
*
- * @param specialOutputFlag set to enums defined in KisDocument if save to special output format
- *
* @return TRUE on success, false on error or cancel
* (don't display anything in this case, the error dialog box is also implemented here
* but restore the original URL in slotFileSaveAs)
*/
- bool saveDocument(KisDocument *document, bool saveas = false, bool silent = false, int specialOutputFlag = 0);
+ bool saveDocument(KisDocument *document, bool saveas = false);
/**
* Update the option widgets to the argument ones, removing the currently set widgets.
*/
void newOptionWidgets(const QList<QPointer<QWidget> > & optionWidgetList);
private Q_SLOTS:
/**
* Save the list of recent files.
*/
void saveRecentFiles();
void slotLoadCompleted();
void slotLoadCanceled(const QString &);
void slotSaveCompleted();
void slotSaveCanceled(const QString &);
void forceDockTabFonts();
void slotProgressCanceled();
/**
* @internal
*/
void slotDocumentTitleModified(const QString &caption, bool mod);
/**
* Prints the actual document.
*/
void slotFilePrint();
/**
* Saves the current document with a new name.
*/
void slotFileSaveAs();
void slotFilePrintPreview();
- KisPrintJob* exportToPdf(KoPageLayout pageLayout, QString pdfFileName = QString());
-
void importAnimation();
/**
* Show a dialog with author and document information.
*/
void slotDocumentInfo();
/**
* Closes all open documents.
*/
bool slotFileCloseAll();
/**
* @brief showAboutApplication show the about box
*/
virtual void showAboutApplication();
/**
* Closes the mainwindow.
*/
void slotFileQuit();
/**
* Configure toolbars.
*/
void slotConfigureToolbars();
/**
* Post toolbar config.
* (Plug action lists back in, etc.)
*/
void slotNewToolbarConfig();
/**
* Shows or hides a toolbar
*/
void slotToolbarToggled(bool toggle);
/**
* Toggle full screen on/off.
*/
void viewFullscreen(bool fullScreen);
/**
* Toggle docker titlebars on/off.
*/
void showDockerTitleBars(bool show);
/**
* Reload file
*/
void slotReloadFile();
/**
* File --> Import
*
* This will call slotFileOpen(). To differentiate this from an ordinary
* call to slotFileOpen() call @ref isImporting().
*/
void slotImportFile();
/**
* File --> Export
*
* This will call slotFileSaveAs(). To differentiate this from an ordinary
* call to slotFileSaveAs() call @ref isExporting().
*/
void slotExportFile();
/**
* Hide the dockers
*/
void toggleDockersVisibility(bool visible);
/**
* Handle theme changes from theme manager
*/
void slotThemeChanged();
void undo();
void redo();
void subWindowActivated();
void updateWindowMenu();
void setActiveSubWindow(QWidget *window);
void configChanged();
void newView(QObject *document);
void newWindow();
void closeCurrentWindow();
void checkSanity();
/// Quits Krita with error message from m_errorMessage.
void showErrorAndDie();
protected:
void closeEvent(QCloseEvent * e);
void resizeEvent(QResizeEvent * e);
/// Set the active view, this will update the undo/redo actions
virtual void setActiveView(KisView *view);
// QWidget overrides
virtual void dragEnterEvent(QDragEnterEvent * event);
virtual void dropEvent(QDropEvent * event);
virtual void dragMoveEvent(QDragMoveEvent * event);
virtual void dragLeaveEvent(QDragLeaveEvent * event);
void setToolbarList(QList<QAction*> toolbarList);
private:
/**
* Add a the given view to the list of views of this mainwindow.
* This is a private implementation. For public usage please use
* newView() and addViewAndNotifyLoadingCompleted().
*/
void addView(KisView *view);
friend class KisApplication;
/**
* Returns the dockwidget specified by the @p factory. If the dock widget doesn't exist yet it's created.
* Add a "view_palette_action_menu" action to your view menu if you want to use closable dock widgets.
* @param factory the factory used to create the dock widget if needed
* @return the dock widget specified by @p factory (may be 0)
*/
QDockWidget* createDockWidget(KoDockFactoryBase* factory);
bool openDocumentInternal(const QUrl &url, KisDocument *newdoc = 0);
- /**
- * Returns whether or not the current slotFileSave[As]() or saveDocument()
- * call is actually an export operation (like File --> Export).
- *
- * If this is true, you must call KisDocument::export() instead of
- * KisDocument::save() or KisDocument::saveAs(), in any reimplementation of
- * saveDocument().
- */
- bool isExporting() const;
-
- /**
- * Returns whether or not the current slotFileOpen() or openDocument()
- * call is actually an import operation (like File --> Import).
- *
- * If this is true, you must call KisDocument::import() instead of
- * KisDocument::openUrl(), in any reimplementation of openDocument() or
- * openDocumentInternal().
- */
- bool isImporting() const;
-
/**
* Reloads the recent documents list.
*/
void reloadRecentFileList();
/**
* Updates the window caption based on the document info and path.
*/
void updateCaption(const QString & caption, bool mod);
void updateReloadFileAction(KisDocument *doc);
void saveWindowSettings();
QPointer<KisView>activeKisView();
void applyDefaultSettings(QPrinter &printer);
- bool exportConfirmation(const QByteArray &outputFormat);
-
void createActions();
void applyToolBarLayout();
protected:
void moveEvent(QMoveEvent *e);
private Q_SLOTS:
void initializeGeometry();
void showManual();
void switchTab(int index);
private:
/**
* Struct used in the list created by createCustomDocumentWidgets()
*/
struct CustomDocumentWidgetItem {
/// Pointer to the custom document widget
QWidget *widget;
/// title used in the sidebar. If left empty it will be displayed as "Custom Document"
QString title;
/// icon used in the sidebar. If left empty it will use the unknown icon
QString icon;
};
class Private;
Private * const d;
QString m_errorMessage;
bool m_dieOnError;
};
#endif
diff --git a/libs/ui/KisPart.cpp b/libs/ui/KisPart.cpp
index 8eaa55a8d1..4874b88124 100644
--- a/libs/ui/KisPart.cpp
+++ b/libs/ui/KisPart.cpp
@@ -1,499 +1,504 @@
/* 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>
* 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.
*/
#include "KisPart.h"
#include "KoProgressProxy.h"
#include <KoCanvasController.h>
#include <KoCanvasControllerWidget.h>
#include <KoColorSpaceEngine.h>
#include <KoCanvasBase.h>
#include <KoToolManager.h>
#include <KoShapeBasedDocumentBase.h>
#include <KoResourceServerProvider.h>
#include <kis_icon.h>
#include "KisApplication.h"
#include "KisDocument.h"
#include "KisView.h"
#include "KisViewManager.h"
#include "KisImportExportManager.h"
#include <kis_debug.h>
#include <KoResourcePaths.h>
#include <KoDialog.h>
#include <kdesktopfile.h>
#include <QMessageBox>
#include <klocalizedstring.h>
#include <kactioncollection.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <QKeySequence>
#include <QDialog>
#include <QApplication>
#include <QDomDocument>
#include <QDomElement>
#include <QGlobalStatic>
#include <KisMimeDatabase.h>
#include "KisView.h"
#include "KisDocument.h"
#include "kis_config.h"
#include "kis_shape_controller.h"
#include "kis_resource_server_provider.h"
#include "kis_animation_cache_populator.h"
#include "kis_idle_watcher.h"
#include "kis_image.h"
#include "KisImportExportManager.h"
#include "KisDocument.h"
#include "KoToolManager.h"
#include "KisViewManager.h"
#include "kis_script_manager.h"
#include "KisOpenPane.h"
#include "kis_color_manager.h"
#include "kis_debug.h"
#include "kis_action.h"
#include "kis_action_registry.h"
Q_GLOBAL_STATIC(KisPart, s_instance)
class Q_DECL_HIDDEN KisPart::Private
{
public:
Private(KisPart *_part)
: part(_part)
, idleWatcher(2500)
, animationCachePopulator(_part)
{
}
~Private()
{
}
KisPart *part;
QList<QPointer<KisView> > views;
QList<QPointer<KisMainWindow> > mainWindows;
QList<QPointer<KisDocument> > documents;
QList<KisAction*> scriptActions;
KActionCollection *actionCollection{0};
KisIdleWatcher idleWatcher;
KisAnimationCachePopulator animationCachePopulator;
void loadActions();
};
// Basically, we are going to insert the current UI/MainWindow ActionCollection
// into the KisActionRegistry.
void KisPart::loadActions()
{
d->actionCollection = currentMainwindow()->viewManager()->actionCollection();
KisActionRegistry * actionRegistry = KisActionRegistry::instance();
Q_FOREACH (QAction *action, d->actionCollection->actions()) {
actionRegistry->addAction(action->objectName(), action);
}
};
KisPart* KisPart::instance()
{
return s_instance;
}
KisPart::KisPart()
: d(new Private(this))
{
// Preload all the resources in the background
Q_UNUSED(KoResourceServerProvider::instance());
Q_UNUSED(KisResourceServerProvider::instance());
Q_UNUSED(KisColorManager::instance());
connect(this, SIGNAL(documentOpened(QString)),
this, SLOT(updateIdleWatcherConnections()));
connect(this, SIGNAL(documentClosed(QString)),
this, SLOT(updateIdleWatcherConnections()));
connect(KisActionRegistry::instance(), SIGNAL(shortcutsUpdated()),
this, SLOT(updateShortcuts()));
connect(&d->idleWatcher, SIGNAL(startedIdleMode()),
&d->animationCachePopulator, SLOT(slotRequestRegeneration()));
d->animationCachePopulator.slotRequestRegeneration();
}
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::updateIdleWatcherConnections()
{
QVector<KisImageSP> images;
Q_FOREACH (QPointer<KisDocument> document, documents()) {
images << document->image();
}
d->idleWatcher.setTrackedImages(images);
}
void KisPart::addDocument(KisDocument *document)
{
//dbgUI << "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();
Q_FOREACH(QAction *action, d->scriptActions) {
mw->viewManager()->scriptManager()->addAction(action);
}
dbgUI <<"mainWindow" << (void*)mw << "added to view" << this;
d->mainWindows.append(mw);
return mw;
}
KisView *KisPart::createView(KisDocument *document,
KoCanvasResourceManager *resourceManager,
KActionCollection *actionCollection,
QWidget *parent)
{
// If creating the canvas fails, record this and disable OpenGL next time
KisConfig cfg;
KConfigGroup grp( KSharedConfig::openConfig(), "crashprevention");
if (grp.readEntry("CreatingCanvas", false)) {
cfg.setUseOpenGL(false);
}
if (cfg.canvasState() == "OPENGL_FAILED") {
cfg.setUseOpenGL(false);
}
grp.writeEntry("CreatingCanvas", true);
grp.sync();
QApplication::setOverrideCursor(Qt::WaitCursor);
KisView *view = new KisView(document, resourceManager, actionCollection, parent);
QApplication::restoreOverrideCursor();
// Record successful canvas creation
grp.writeEntry("CreatingCanvas", false);
grp.sync();
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;
/**
* HACK ALERT: we check here explicitly if the document (or main
* window), is saving the stuff. If we close the
* document *before* the saving is completed, a crash
* will happen.
*/
if (view->mainWindow()->hackIsSaving()) {
return;
}
emit sigViewRemoved(view);
QPointer<KisDocument> doc = view->document();
d->views.removeAll(view);
if (doc) {
bool found = false;
Q_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;
Q_FOREACH (QPointer<KisView> view, d->views) {
if (view && view->isVisible() && view->document() == doc) {
count++;
}
}
return count;
}
}
void KisPart::removeMainWindow(KisMainWindow *mainWindow)
{
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::addScriptAction(KisAction *action)
{
d->scriptActions << action;
}
KisIdleWatcher* KisPart::idleWatcher() const
{
return &d->idleWatcher;
}
KisAnimationCachePopulator* KisPart::cachePopulator() const
{
return &d->animationCachePopulator;
}
void KisPart::openExistingFile(const QUrl &url)
{
Q_ASSERT(url.isLocalFile());
qApp->setOverrideCursor(Qt::BusyCursor);
KisDocument *document = createDocument();
if (!document->openUrl(url)) {
delete document;
return;
}
if (!document->image()) {
delete document;
return;
}
document->setModified(false);
addDocument(document);
KisMainWindow *mw = currentMainwindow();
mw->addViewAndNotifyLoadingCompleted(document);
qApp->restoreOverrideCursor();
}
void KisPart::updateShortcuts()
{
// Update any non-UI actionCollections. That includes:
// - Shortcuts called inside of tools
// - Perhaps other things?
KoToolManager::instance()->updateToolShortcuts();
// Now update the UI actions.
Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) {
KActionCollection *ac = mainWindow->actionCollection();
ac->updateShortcuts();
// Loop through mainWindow->actionCollections() to modify tooltips
// so that they list shortcuts at the end in parentheses
Q_FOREACH ( QAction* action, ac->actions())
{
// Remove any existing suffixes from the tooltips.
// Note this regexp starts with a space, e.g. " (Ctrl-a)"
QString strippedTooltip = action->toolTip().remove(QRegExp("\\s\\(.*\\)"));
// Now update the tooltips with the new shortcut info.
if(action->shortcut() == QKeySequence(0))
action->setToolTip(strippedTooltip);
else
action->setToolTip( strippedTooltip + " (" + action->shortcut().toString() + ")");
}
}
}
void KisPart::openTemplate(const QUrl &url)
{
qApp->setOverrideCursor(Qt::BusyCursor);
KisDocument *document = createDocument();
bool ok = document->loadNativeFormat(url.toLocalFile());
document->setModified(false);
document->undoStack()->clear();
if (ok) {
QString mimeType = KisMimeDatabase::mimeTypeForFile(url.toLocalFile());
// 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();
+ if (document->errorMessage().isEmpty()) {
+ QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1", document->localFilePath()));
+ }
+ else {
+ QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1\nReason: %2", document->localFilePath(), document->errorMessage()));
+ }
+ delete document;
+ return;
}
addDocument(document);
KisMainWindow *mw = currentMainwindow();
mw->addViewAndNotifyLoadingCompleted(document);
KisOpenPane *pane = qobject_cast<KisOpenPane*>(sender());
if (pane) {
pane->hide();
pane->deleteLater();
}
qApp->restoreOverrideCursor();
}
void KisPart::viewDestroyed()
{
KisView *view = qobject_cast<KisView*>(sender());
if (view) {
removeView(view);
}
}
void KisPart::addRecentURLToAllMainWindows(QUrl url)
{
// Add to recent actions list in our mainWindows
Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) {
mainWindow->addRecentURL(url);
}
}
void KisPart::startCustomDocument(KisDocument* doc)
{
addDocument(doc);
KisMainWindow *mw = currentMainwindow();
KisOpenPane *pane = qobject_cast<KisOpenPane*>(sender());
if (pane) {
pane->hide();
pane->deleteLater();
}
mw->addViewAndNotifyLoadingCompleted(doc);
}
KisInputManager* KisPart::currentInputManager()
{
return instance()->currentMainwindow()->viewManager()->inputManager();
}
diff --git a/libs/ui/KisSaveGroupVisitor.cpp b/libs/ui/KisSaveGroupVisitor.cpp
index 5347592d1e..174e0d1cc3 100644
--- a/libs/ui/KisSaveGroupVisitor.cpp
+++ b/libs/ui/KisSaveGroupVisitor.cpp
@@ -1,140 +1,138 @@
/*
* Copyright (C) 2016 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 "KisSaveGroupVisitor.h"
#include <KisDocument.h>
#include <kis_painter.h>
#include <kis_paint_layer.h>
#include <KisPart.h>
KisSaveGroupVisitor::KisSaveGroupVisitor(KisImageWSP image,
bool saveInvisible,
bool saveTopLevelOnly,
const QString &path,
const QString &baseName,
const QString &extension,
const QString &mimeFilter)
: m_image(image)
, m_saveInvisible(saveInvisible)
, m_saveTopLevelOnly(saveTopLevelOnly)
, m_path(path)
, m_baseName(baseName)
, m_extension(extension)
, m_mimeFilter(mimeFilter)
{
}
KisSaveGroupVisitor::~KisSaveGroupVisitor()
{
}
bool KisSaveGroupVisitor::visit(KisNode* ) {
return true;
}
bool KisSaveGroupVisitor::visit(KisPaintLayer *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisAdjustmentLayer *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisExternalLayer *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisCloneLayer *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisFilterMask *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisTransformMask *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisTransparencyMask *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisGeneratorLayer * ) {
return true;
}
bool KisSaveGroupVisitor::visit(KisSelectionMask* ) {
return true;
}
bool KisSaveGroupVisitor::visit(KisColorizeMask* ) {
return true;
}
bool KisSaveGroupVisitor::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 *exportDocument = KisPart::instance()->createDocument();
- exportDocument->prepareForImport();
-
KisImageWSP dst = new KisImage(exportDocument->createUndoStore(), r.width(), r.height(), m_image->colorSpace(), layer->name());
dst->setResolution(m_image->xRes(), m_image->yRes());
exportDocument->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();
exportDocument->setOutputMimeType(m_mimeFilter.toLatin1());
exportDocument->setFileBatchMode(true);
QString path = m_path + "/" + m_baseName + "_" + layer->name().replace(' ', '_') + '.' + m_extension;
QUrl url = QUrl::fromLocalFile(path);
exportDocument->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 exportDocument;
}
return true;
}
diff --git a/libs/ui/KisTemplateCreateDia.cpp b/libs/ui/KisTemplateCreateDia.cpp
index 74a8e71e93..b7c167d05c 100644
--- a/libs/ui/KisTemplateCreateDia.cpp
+++ b/libs/ui/KisTemplateCreateDia.cpp
@@ -1,518 +1,518 @@
/*
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 <QInputDialog>
#include <QTemporaryFile>
#include <QLineEdit>
#include <QDir>
#include <klocalizedstring.h>
#include <kis_icon.h>
#include <KisDocument.h>
#include <KisTemplates.h>
#include <KisTemplateTree.h>
#include <KisTemplateGroup.h>
#include <KisTemplate.h>
#include <QMessageBox>
#include <KoResourcePaths.h>
#include <kis_debug.h>
#include <kis_icon.h>
#include <kconfiggroup.h>
#include <QUrl>
#include <ksharedconfig.h>
// ODF thumbnail extent
static const int thumbnailExtent = 128;
class KisTemplateCreateDiaPrivate {
public:
KisTemplateCreateDiaPrivate(const QString &templatesResourcePath,
const QString &filePath, const QPixmap &thumbnail)
: m_tree(templatesResourcePath, true)
, m_filePath(filePath)
, m_thumbnail(thumbnail)
{ }
KisTemplateTree m_tree;
QLineEdit *m_name;
QRadioButton *m_default;
QRadioButton *m_custom;
QPushButton *m_select;
QLabel *m_preview;
QString m_customFile;
QPixmap m_customPixmap;
QTreeWidget *m_groups;
QPushButton *m_add;
QPushButton *m_remove;
QCheckBox *m_defaultTemplate;
QString m_filePath;
QPixmap m_thumbnail;
bool m_changed;
};
/****************************************************************************
*
* Class: KisTemplateCreateDia
*
****************************************************************************/
KisTemplateCreateDia::KisTemplateCreateDia(const QString &templatesResourcePath,
const QString &filePath, const QPixmap &thumbnail, QWidget *parent)
: KoDialog(parent)
, d(new KisTemplateCreateDiaPrivate(templatesResourcePath, filePath, thumbnail))
{
setButtons( KoDialog::Ok|KoDialog::Cancel );
setDefaultButton( KoDialog::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 QLineEdit(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);
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->setVisible( false );
d->m_defaultTemplate->setToolTip(i18n("Use the new template every time Krita starts"));
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() != 0 )
{
d->m_name->setText( item->text( 0 ) );
}
}
void KisTemplateCreateDia::createTemplate(const QString &templatesResourcePath,
const char *suffix,
KisDocument *document, QWidget *parent)
{
Q_UNUSED(suffix);
QString fileName;
{
QTemporaryFile tempFile;
if (!tempFile.open()) {
qWarning("Creation of temporary file to store template failed.");
return;
}
fileName = tempFile.fileName();
}
- bool retval = document->saveNativeFormat(fileName);
+ bool retval = document->exportDocument(QUrl::fromLocalFile(fileName));
if (!retval) {
qWarning("Could not save template");
return;
}
const QPixmap thumbnail = document->generatePreview(QSize(thumbnailExtent, thumbnailExtent));
KisTemplateCreateDia *dia = new KisTemplateCreateDia(templatesResourcePath, 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( KoDialog::Cancel );
return;
}
// is it a group or a template? anyway - get the group :)
if(item->parent() != 0)
item=item->parent();
if(!item) { // *very* safe :P
d->m_tree.writeTemplateTree();
slotButtonClicked( KoDialog::Cancel );
return;
}
KisTemplateGroup *group=d->m_tree.find(item->text(0));
if(!group) { // even safer
d->m_tree.writeTemplateTree();
slotButtonClicked( KoDialog::Cancel );
return;
}
if(d->m_name->text().isEmpty()) {
d->m_tree.writeTemplateTree();
slotButtonClicked( KoDialog::Cancel );
return;
}
// copy the tmp file and the picture the app provides
QString dir = KoResourcePaths::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";
QString ext = ".kra";
QString dest = templateDir + file + ext;
if (QFile::exists(dest)) {
do {
file = file.prepend( '_' );
dest = templateDir + file + ext;
tmpIcon=".icon/" + file + ".png";
icon=iconDir + file + ".png";
}
while (QFile(dest).exists());
}
bool ignore = false;
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;
}
}
QDir path;
if (!path.mkpath(templateDir) || !path.mkpath(iconDir)) {
d->m_tree.writeTemplateTree();
slotButtonClicked( KoDialog::Cancel );
return;
}
QString orig;
orig = d->m_filePath;
// don't overwrite the hidden template file with a new non-hidden one
if (!ignore) {
if (!QFile::copy(d->m_filePath, dest)) {
qWarning() << "Could not copy" << d->m_filePath << "to" << dest;
}
// save the picture as icon
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 {
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 = (*it) + ".directory";
// Check if we can read the file
if (QFile(orig).exists()) {
dest = dir + "/.directory";
// We copy the file with overwrite
if (!QFile(orig).copy(dest)) {
warnKrita << "Failed to copy from" << orig << "to" << dest;
}
ready = true;
}
}
}
d->m_tree.writeTemplateTree();
if ( d->m_defaultTemplate->isChecked() )
{
KConfigGroup grp( KSharedConfig::openConfig(), "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);
// QT5TODO
// 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 = QString();// 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() {
const QString name = QInputDialog::getText(this, i18n("Add Group"), i18n("Enter group name:"));
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 = KoResourcePaths::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() == 0) {
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() == 0) {
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()) {
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
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() {
Q_FOREACH (KisTemplateGroup *group, d->m_tree.groups()) {
if(group->isHidden())
continue;
QTreeWidgetItem *groupItem=new QTreeWidgetItem(d->m_groups, QStringList() << group->name());
Q_FOREACH (KisTemplate *t, group->templates()) {
if(t->isHidden())
continue;
(void)new QTreeWidgetItem(groupItem, QStringList() << t->name());
}
}
}
diff --git a/libs/ui/KisView.cpp b/libs/ui/KisView.cpp
index 95b3ec4433..542feb3dd2 100644
--- a/libs/ui/KisView.cpp
+++ b/libs/ui/KisView.cpp
@@ -1,943 +1,938 @@
/*
* 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 <kis_icon.h>
#include <kactioncollection.h>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kselectaction.h>
#include <kconfiggroup.h>
#include <kactioncollection.h>
#include <QMenu>
#include <QMessageBox>
#include <QUrl>
#include <QTemporaryFile>
#include <QApplication>
#include <QDesktopWidget>
#include <QDockWidget>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QImage>
#include <QList>
#include <QPrintDialog>
#include <QToolBar>
#include <QUrl>
#include <QStatusBar>
#include <QMoveEvent>
#include <QTemporaryFile>
#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_utils.h"
#include "input/kis_input_manager.h"
#include "KisRemoteFileFetcher.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(KisView *_q,
KisDocument *document,
KoCanvasResourceManager *resourceManager,
KActionCollection *actionCollection)
: actionCollection(actionCollection)
, viewConverter()
, canvasController(_q, actionCollection)
, canvas(&viewConverter, resourceManager, _q, document->shapeController())
, zoomManager(_q, &this->viewConverter, &this->canvasController)
, paintingAssistantsDecoration(new KisPaintingAssistantsDecoration(_q))
, floatingMessageCompressor(100, KisSignalCompressor::POSTPONE)
{
}
KisUndoStackAction *undo = 0;
KisUndoStackAction *redo = 0;
bool inOperation; //in the middle of an operation (no screen refreshing)?
QPointer<KisDocument> document; // our KisDocument
QWidget *tempActiveWidget = 0;
/**
* Signals the document has been deleted. Can't use document==0 since this
* only happens in ~QObject, and views get deleted by ~KisDocument.
* XXX: either provide a better justification to do things this way, or
* rework the mechanism.
*/
bool documentDeleted = false;
KActionCollection* actionCollection;
KisCoordinatesConverter viewConverter;
KisCanvasController canvasController;
KisCanvas2 canvas;
KisZoomManager zoomManager;
KisViewManager *viewManager = 0;
KisNodeSP currentNode;
KisPaintingAssistantsDecorationSP paintingAssistantsDecoration;
bool isCurrent = false;
bool showFloatingMessage = false;
QPointer<KisFloatingMessage> savedFloatingMessage;
KisSignalCompressor floatingMessageCompressor;
bool softProofing = false;
bool gamutCheck = false;
// 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(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 = 0;
int m_stretch;
bool m_permanent;
bool m_connected = false;
bool m_hidden = false;
};
};
KisView::KisView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent)
: QWidget(parent)
, d(new Private(this, document, resourceManager, actionCollection))
{
Q_ASSERT(document);
connect(document, SIGNAL(titleModified(QString,bool)), this, SIGNAL(titleModified(QString,bool)));
setObjectName(newObjectName());
d->document = document;
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->canvas.setup();
KisConfig cfg;
d->canvasController.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
d->canvasController.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
d->canvasController.setDrawShadow(false);
d->canvasController.setCanvasMode(KoCanvasController::Infinite);
d->canvasController.setVastScrolling(cfg.vastScrolling());
d->canvasController.setCanvas(&d->canvas);
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->canvas.addDecoration(d->paintingAssistantsDecoration);
d->paintingAssistantsDecoration->setVisible(true);
d->showFloatingMessage = cfg.showCanvasMessages();
}
KisView::~KisView()
{
if (d->viewManager) {
KoProgressProxy *proxy = d->viewManager->statusBar()->progress()->progressProxy();
KIS_ASSERT_RECOVER_NOOP(proxy);
image()->compositeProgressProxy()->removeProxy(proxy);
if (d->viewManager->filterManager()->isStrokeRunning()) {
d->viewManager->filterManager()->cancel();
}
}
KisPart::instance()->removeView(this);
KoToolManager::instance()->removeCanvasController(&d->canvasController);
delete d;
}
void KisView::notifyCurrentStateChanged(bool isCurrent)
{
d->isCurrent = isCurrent;
if (!d->isCurrent && d->savedFloatingMessage) {
d->savedFloatingMessage->removeMessage();
}
KisInputManager *inputManager = globalInputManager();
if (d->isCurrent) {
inputManager->attachPriorityEventFilter(&d->canvasController);
} else {
inputManager->detachPriorityEventFilter(&d->canvasController);
}
}
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();
}
}
}
bool KisView::canvasIsMirrored() const
{
return d->canvas.xAxisMirrored() || d->canvas.yAxisMirrored();
}
void KisView::setViewManager(KisViewManager *view)
{
d->viewManager = view;
KoToolManager::instance()->addController(&d->canvasController);
KoToolManager::instance()->registerToolActions(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()));
// executed in a context of an image thread
connect(image(), SIGNAL(sigNodeAddedAsync(KisNodeSP)),
SLOT(slotImageNodeAdded(KisNodeSP)),
Qt::DirectConnection);
// executed in a context of the gui thread
connect(this, SIGNAL(sigContinueAddNode(KisNodeSP)),
SLOT(slotContinueAddNode(KisNodeSP)),
Qt::AutoConnection);
// executed in a context of an image thread
connect(image(), SIGNAL(sigRemoveNodeAsync(KisNodeSP)),
SLOT(slotImageNodeRemoved(KisNodeSP)),
Qt::DirectConnection);
// executed in a context of the gui thread
connect(this, SIGNAL(sigContinueRemoveNode(KisNodeSP)),
SLOT(slotContinueRemoveNode(KisNodeSP)),
Qt::AutoConnection);
/*
* 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;
}
void KisView::slotImageNodeAdded(KisNodeSP node)
{
emit sigContinueAddNode(node);
}
void KisView::slotContinueAddNode(KisNodeSP newActiveNode)
{
/**
* When deleting the last layer, root node got selected. We should
* fix it when the first layer is added back.
*
* Here we basically reimplement what Qt's view/model do. But
* since they are not connected, we should do it manually.
*/
if (!d->isCurrent &&
(!d->currentNode || !d->currentNode->parent())) {
d->currentNode = newActiveNode;
}
}
void KisView::slotImageNodeRemoved(KisNodeSP node)
{
emit sigContinueRemoveNode(KritaUtils::nearestNodeAfterRemoval(node));
}
void KisView::slotContinueRemoveNode(KisNodeSP newActiveNode)
{
if (!d->isCurrent) {
d->currentNode = newActiveNode;
}
}
QAction *KisView::undoAction() const
{
return d->undo;
}
QAction *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);
Q_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) {
QMenu popup;
popup.setObjectName("drop_popup");
QAction *insertAsNewLayer = new QAction(i18n("Insert as New Layer"), &popup);
QAction *insertManyLayers = new QAction(i18n("Insert Many Layers"), &popup);
QAction *openInNewDocument = new QAction(i18n("Open in New Document"), &popup);
QAction *openManyDocuments = new QAction(i18n("Open Many Documents"), &popup);
QAction *cancel = new QAction(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) {
QTemporaryFile *tmp = 0;
Q_FOREACH (QUrl url, urls) {
if (!url.isLocalFile()) {
// download the file and substitute the url
KisRemoteFileFetcher fetcher;
tmp = new QTemporaryFile();
tmp->setAutoRemove(true);
if (!fetcher.fetchFile(url, tmp)) {
qDebug() << "Fetching" << url << "failed";
continue;
}
url = url.fromLocalFile(tmp->fileName());
}
if (url.isLocalFile()) {
if (action == insertAsNewLayer || action == insertManyLayers) {
d->viewManager->imageManager()->importImage(url);
activateWindow();
}
else {
Q_ASSERT(action == openInNewDocument || action == openManyDocuments);
if (mainWindow()) {
mainWindow()->openDocument(url);
}
}
}
delete tmp;
tmp = 0;
}
}
}
}
}
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;
}
-KoPageLayout KisView::pageLayout() const
-{
- return document()->pageLayout();
-}
-
QPrintDialog *KisView::createPrintDialog(KisPrintJob *printJob, QWidget *parent)
{
Q_UNUSED(parent);
QPrintDialog *printDialog = new QPrintDialog(&printJob->printer(), this);
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();
}
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::slotSoftProofing(bool softProofing)
{
d->softProofing = softProofing;
QString message;
if (canvasBase()->image()->colorSpace()->colorDepthId().id().contains("F"))
{
message = i18n("Soft Proofing doesn't work in floating point.");
viewManager()->showFloatingMessage(message,QIcon());
return;
}
if (softProofing){
message = i18n("Soft Proofing turned on.");
} else {
message = i18n("Soft Proofing turned off.");
}
viewManager()->showFloatingMessage(message,QIcon());
canvasBase()->slotSoftProofing(softProofing);
}
void KisView::slotGamutCheck(bool gamutCheck)
{
d->gamutCheck = gamutCheck;
QString message;
if (canvasBase()->image()->colorSpace()->colorDepthId().id().contains("F"))
{
message = i18n("Gamut Warnings don't work in floating point.");
viewManager()->showFloatingMessage(message,QIcon());
return;
}
if (gamutCheck){
message = i18n("Gamut Warnings turned on.");
if (!d->softProofing){
message += "\n "+i18n("But Soft Proofing is still off.");
}
} else {
message = i18n("Gamut Warnings turned off.");
}
viewManager()->showFloatingMessage(message,QIcon());
canvasBase()->slotGamutCheck(gamutCheck);
}
bool KisView::softProofing()
{
return d->softProofing;
}
bool KisView::gamutCheck()
{
return d->gamutCheck;
}
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();
}
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);
zoomManager()->updateImageBoundsSnapping();
}
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()->updateImageBoundsSnapping();
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()->updateImageBoundsSnapping();
zoomManager()->updateGUI();
}
diff --git a/libs/ui/KisView.h b/libs/ui/KisView.h
index 7cf9883c7e..7089eeb532 100644
--- a/libs/ui/KisView.h
+++ b/libs/ui/KisView.h
@@ -1,288 +1,281 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright (C) 2007 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.
*/
#ifndef KIS_VIEW_H
#define KIS_VIEW_H
#include <QWidget>
#include <KoColorSpace.h>
#include <KoColorProfile.h>
#include <kis_types.h>
#include "kritaui_export.h"
#include "widgets/kis_floating_message.h"
class KisDocument;
class KisMainWindow;
class KisPrintJob;
class KisCanvasController;
class KisZoomManager;
class KisCanvas2;
class KisViewManager;
class KisDocument;
class KisCanvasResourceProvider;
class KisCoordinatesConverter;
class KisInputManager;
class KoZoomController;
class KoZoomController;
struct KoPageLayout;
class KoCanvasResourceManager;
// KDE classes
class QAction;
class KActionCollection;
// Qt classes
class QDragEnterEvent;
class QDropEvent;
class QPrintDialog;
class QCloseEvent;
class QStatusBar;
/**
* This class is used to display a @ref KisDocument.
*
* Multiple views can be attached to one document at a time.
*/
class KRITAUI_EXPORT KisView : public QWidget
{
Q_OBJECT
public:
/**
* Creates a new view for the document.
*/
KisView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent = 0);
~KisView();
QAction *undoAction() const;
QAction *redoAction() const;
// Temporary while teasing apart view and mainwindow
void setViewManager(KisViewManager *view);
KisViewManager *viewManager() const;
public:
/**
* Retrieves the document object of this view.
*/
KisDocument *document() const;
/**
* Reset the view to show the given document.
*/
void setDocument(KisDocument *document);
/**
* Tells this view that its document has got deleted (called internally)
*/
void setDocumentDeleted();
/**
* In order to print the document represented by this view a new print job should
* be constructed that is capable of doing the printing.
* The default implementation returns 0, which silently cancels printing.
*/
KisPrintJob * createPrintJob();
- /**
- * @return the page layout to be used for printing.
- * Default is the documents layout.
- * Reimplement if your application needs to use a different layout.
- */
- KoPageLayout pageLayout() const;
-
/**
* Create a QPrintDialog based on the @p printJob
*/
QPrintDialog *createPrintDialog(KisPrintJob *printJob, QWidget *parent);
/**
* @return the KisMainWindow in which this view is currently.
*/
KisMainWindow * mainWindow() const;
/**
* @return the statusbar of the KisMainWindow in which this view is currently.
*/
QStatusBar * statusBar() const;
/**
* This adds a widget to the statusbar for this view.
* If you use this method instead of using statusBar() directly,
* KisView will take care of removing the items when the view GUI is deactivated
* and readding them when it is reactivated.
* The parameters are the same as QStatusBar::addWidget().
*/
void addStatusBarItem(QWidget * widget, int stretch = 0, bool permanent = false);
/**
* Remove a widget from the statusbar for this view.
*/
void removeStatusBarItem(QWidget * widget);
/**
* Return the zoomController for this view.
*/
KoZoomController *zoomController() const;
/// create a list of actions that when activated will change the unit on the document.
QList<QAction*> createChangeUnitActions(bool addPixelUnit = false);
public:
/**
* The zoommanager handles everything action-related to zooming
*/
KisZoomManager *zoomManager() const;
/**
* The CanvasController decorates the canvas with scrollbars
* and knows where to start painting on the canvas widget, i.e.,
* the document offset.
*/
KisCanvasController *canvasController() const;
KisCanvasResourceProvider *resourceProvider() const;
/**
* Filters events and sends them to canvas actions. Shared
* among all the views/canvases
*
* NOTE: May be null while initialization!
*/
KisInputManager* globalInputManager() const;
/**
* @return the canvas object
*/
KisCanvas2 *canvasBase() const;
/// @return the image this view is displaying
KisImageWSP image() const;
KisCoordinatesConverter *viewConverter() const;
void resetImageSizeAndScroll(bool changeCentering,
const QPointF &oldImageStillPoint = QPointF(),
const QPointF &newImageStillPoint = QPointF());
void setCurrentNode(KisNodeSP node);
KisNodeSP currentNode() const;
KisLayerSP currentLayer() const;
KisMaskSP currentMask() const;
/**
* @brief softProofing
* @return whether or not we're softproofing in this view.
*/
bool softProofing();
/**
* @brief gamutCheck
* @return whether or not we're using gamut warnings in this view.
*/
bool gamutCheck();
/// Convenience method to get at the active selection (the
/// selection of the current layer, or, if that does not exist,
/// the global selection.
KisSelectionSP selection();
void notifyCurrentStateChanged(bool isCurrent);
void setShowFloatingMessage(bool show);
void showFloatingMessageImpl(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment);
bool canvasIsMirrored() const;
public Q_SLOTS:
/**
* Display a message in the status bar (calls QStatusBar::message())
* @todo rename to something more generic
*/
void slotActionStatusText(const QString &text);
/**
* End of the message in the status bar (calls QStatusBar::clear())
* @todo rename to something more generic
*/
void slotClearStatusText();
/**
* @brief slotSoftProofing set whether or not we're softproofing in this view.
* Will be setting the same in the canvas belonging to the view.
*/
void slotSoftProofing(bool softProofing);
/**
* @brief slotGamutCheck set whether or not we're gamutchecking in this view.
* Will be setting the same in the vans belonging to the view.
*/
void slotGamutCheck(bool gamutCheck);
bool queryClose();
private Q_SLOTS:
void slotImageNodeAdded(KisNodeSP node);
void slotContinueAddNode(KisNodeSP newActiveNode);
void slotImageNodeRemoved(KisNodeSP node);
void slotContinueRemoveNode(KisNodeSP newActiveNode);
Q_SIGNALS:
// From KisImage
void sigSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint);
void sigProfileChanged(const KoColorProfile * profile);
void sigColorSpaceChanged(const KoColorSpace* cs);
void titleModified(QString,bool);
void sigContinueAddNode(KisNodeSP newActiveNode);
void sigContinueRemoveNode(KisNodeSP newActiveNode);
protected:
// QWidget overrides
void dragEnterEvent(QDragEnterEvent * event);
void dropEvent(QDropEvent * event);
void closeEvent(QCloseEvent *event);
/**
* Generate a name for this view.
*/
QString newObjectName();
public Q_SLOTS:
void slotLoadingFinished();
void slotSavingFinished();
void slotImageResolutionChanged();
void slotImageSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint);
private:
class Private;
Private * const d;
static bool s_firstView;
};
#endif
diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp
index 8eb7c0eccb..e49a0f0272 100644
--- a/libs/ui/KisViewManager.cpp
+++ b/libs/ui/KisViewManager.cpp
@@ -1,1274 +1,1273 @@
/*
* 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 <QAction>
#include <QApplication>
#include <QBuffer>
#include <QByteArray>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QGridLayout>
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QObject>
#include <QPoint>
#include <QPrintDialog>
#include <QRect>
#include <QScrollBar>
#include <QStatusBar>
#include <QToolBar>
#include <QUrl>
#include <QWidget>
#include <kactioncollection.h>
#include <klocalizedstring.h>
#include <KoResourcePaths.h>
#include <kselectaction.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 <KoColorSpaceRegistry.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 "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 "KisDocument.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 "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 <brushengine/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_script_manager.h"
#include "kis_icon_utils.h"
#include "kis_guides_manager.h"
#include "kis_derived_resources.h"
class BlockingUserInputEventFilter : public QObject
{
bool eventFilter(QObject *watched, QEvent *event) override
{
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(KisViewManager *_q, QWidget *_q_parent)
: filterManager(_q)
, createTemplate(0)
, saveIncremental(0)
, saveIncrementalBackup(0)
, openResourcesDirectory(0)
, rotateCanvasRight(0)
, rotateCanvasLeft(0)
, resetCanvasRotation(0)
, wrapAroundAction(0)
, levelOfDetailAction(0)
, showRulersAction(0)
, rulersTrackMouseAction(0)
, zoomTo100pct(0)
, zoomIn(0)
, zoomOut(0)
, selectionManager(_q)
, statusBar(_q)
, controlFrame(_q, _q_parent)
, nodeManager(_q)
, imageManager(_q)
, gridManager(_q)
, canvasControlsManager(_q)
, paintingAssistantsManager(_q)
, actionManager(_q)
, mainWindow(0)
, showFloatingMessage(true)
, currentImageView(0)
, canvasResourceProvider(_q)
, canvasResourceManager()
, guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q)
, mirrorManager(_q)
, inputManager(_q)
, scriptManager(_q)
, actionAuthor(0)
{
canvasResourceManager.addDerivedResourceConverter(toQShared(new KisCompositeOpResourceConverter));
canvasResourceManager.addDerivedResourceConverter(toQShared(new KisEffectiveCompositeOpResourceConverter));
canvasResourceManager.addDerivedResourceConverter(toQShared(new KisOpacityResourceConverter));
canvasResourceManager.addDerivedResourceConverter(toQShared(new KisFlowResourceConverter));
canvasResourceManager.addDerivedResourceConverter(toQShared(new KisSizeResourceConverter));
canvasResourceManager.addDerivedResourceConverter(toQShared(new KisLodAvailabilityResourceConverter));
canvasResourceManager.addDerivedResourceConverter(toQShared(new KisEraserModeResourceConverter));
canvasResourceManager.addResourceUpdateMediator(toQShared(new KisPresetUpdateMediator));
}
public:
KisFilterManager filterManager;
KisAction *createTemplate;
KisAction *createCopy;
KisAction *saveIncremental;
KisAction *saveIncrementalBackup;
KisAction *openResourcesDirectory;
KisAction *rotateCanvasRight;
KisAction *rotateCanvasLeft;
KisAction *resetCanvasRotation;
KisAction *wrapAroundAction;
KisAction *levelOfDetailAction;
KisAction *showRulersAction;
KisAction *rulersTrackMouseAction;
KisAction *zoomTo100pct;
KisAction *zoomIn;
KisAction *zoomOut;
KisAction *softProof;
KisAction *gamutCheck;
KisSelectionManager selectionManager;
KisGuidesManager guidesManager;
KisStatusBar statusBar;
KisControlFrame controlFrame;
KisNodeManager nodeManager;
KisImageManager imageManager;
KisGridManager gridManager;
KisCanvasControlsManager canvasControlsManager;
KisPaintingAssistantsManager paintingAssistantsManager;
BlockingUserInputEventFilter blockingEventFilter;
KisActionManager actionManager;
QMainWindow* mainWindow;
QPointer<KisFloatingMessage> savedFloatingMessage;
bool showFloatingMessage;
QPointer<KisView> currentImageView;
KisCanvasResourceProvider canvasResourceProvider;
KoCanvasResourceManager canvasResourceManager;
KisSignalCompressor guiUpdateCompressor;
KActionCollection *actionCollection;
KisMirrorManager mirrorManager;
KisInputManager inputManager;
KisSignalAutoConnectionsStore viewConnections;
KisScriptManager scriptManager;
KSelectAction *actionAuthor; // Select action for author profile.
QByteArray canvasState;
};
KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection)
: d(new KisViewManagerPrivate(this, parent))
{
d->actionCollection = _actionCollection;
d->mainWindow = dynamic_cast<QMainWindow*>(parent);
d->canvasResourceProvider.setResourceManager(&d->canvasResourceManager);
connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout()));
createActions();
setupManagers();
// These initialization functions must wait until KisViewManager ctor is complete.
d->statusBar.setup();
d->controlFrame.setup(parent);
//Check to draw scrollbars after "Canvas only mode" toggle is created.
this->showHideScrollbars();
QScopedPointer<KoDummyCanvasController> dummy(new KoDummyCanvasController(actionCollection()));
KoToolManager::instance()->registerToolActions(actionCollection(), dummy.data());
QTimer::singleShot(0, this, SLOT(initializeStatusBarVisibility()));
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.writeKoColor("LastForeGroundColor",resourceProvider()->fgColor());
cfg.writeKoColor("LastBackGroundColor",resourceProvider()->bgColor());
}
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());
if (viewCount() == 0) {
d->statusBar.showAllStatusBarItems();
}
}
void KisViewManager::slotViewRemoved(KisView *view)
{
d->inputManager.removeTrackedCanvas(view->canvasBase());
if (viewCount() == 0) {
d->statusBar.hideAllStatusBarItems();
}
}
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();
}
// Restore the last used brush preset, color and background color.
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());
}
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoColor foreground(Qt::black, cs);
d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground));
KoColor background(Qt::white, cs);
d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background));
}
d->softProof->setChecked(view->softProofing());
d->gamutCheck->setChecked(view->gamutCheck());
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(requestStrokeEndActiveNode()));
d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15()));
d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15()));
d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation()));
d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool)));
d->wrapAroundAction->setChecked(canvasController->wrapAroundMode());
d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool)));
d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode());
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(setShowRulers(bool)));
d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool)));
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->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) );
d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) );
imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked());
imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked());
-
+
showHideScrollbars();
}
d->filterManager.setView(imageView);
d->selectionManager.setView(imageView);
d->guidesManager.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->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();
Q_EMIT viewChanged();
}
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)
{
d->statusBar.addStatusBarItem(widget, stretch, permanent);
}
void KisViewManager::removeStatusBarItem(QWidget *widget)
{
d->statusBar.removeStatusBarItem(widget);
}
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()
{
return d->nodeManager.activeNode();
}
KisLayerSP KisViewManager::activeLayer()
{
return d->nodeManager.activeLayer();
}
KisPaintDeviceSP KisViewManager::activeDevice()
{
return d->nodeManager.activePaintDevice();
}
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()
{
KisConfig cfg;
d->saveIncremental = actionManager()->createAction("save_incremental_version");
connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental()));
d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup");
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 = actionManager()->createAction("tablet_debugger");
connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger()));
d->createTemplate = actionManager()->createAction("create_template");
connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate()));
d->createCopy = actionManager()->createAction("create_copy");
connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy()));
d->openResourcesDirectory = actionManager()->createAction("open_resources_directory");
connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory()));
d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right");
d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left");
d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation");
d->wrapAroundAction = actionManager()->createAction("wrap_around_mode");
d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode");
d->softProof = actionManager()->createAction("softProof");
d->gamutCheck = actionManager()->createAction("gamutCheck");
KisAction *tAction = actionManager()->createAction("showStatusBar");
tAction->setChecked(cfg.showStatusBar());
connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool)));
tAction = actionManager()->createAction("view_show_canvas_only");
tAction->setChecked(false);
connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool)));
//Workaround, by default has the same shortcut as mirrorCanvas
KisAction *a = dynamic_cast<KisAction*>(actionCollection()->action("format_italic"));
if (a) {
a->setDefaultShortcut(QKeySequence());
}
a = actionManager()->createAction("edit_blacklist_cleanup");
connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup()));
d->showRulersAction = actionManager()->createAction("view_ruler");
d->showRulersAction->setChecked(cfg.showRulers());
connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool)));
d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse");
d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse());
connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool)));
d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct");
d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, "");
d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, "");
d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("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::setupManagers()
{
// Create the managers for filters, selections, layers etc.
// XXX: When the currentlayer changes, call updateGUI on all
// managers
d->filterManager.setup(actionCollection(), actionManager());
d->selectionManager.setup(actionManager());
d->guidesManager.setup(actionManager());
d->nodeManager.setup(actionCollection(), actionManager());
d->imageManager.setup(actionManager());
d->gridManager.setup(actionManager());
d->paintingAssistantsManager.setup(actionManager());
d->canvasControlsManager.setup(actionManager());
d->mirrorManager.setup(actionCollection());
d->scriptManager.setup(actionCollection(), actionManager());
}
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;
}
KisGridManager * KisViewManager::gridManager() const
{
return &d->gridManager;
}
KisGuidesManager * KisViewManager::guidesManager() const
{
return &d->guidesManager;
}
KisPaintingAssistantsManager* KisViewManager::paintingAssistantsManager() const
{
return &d->paintingAssistantsManager;
}
KisDocument *KisViewManager::document() const
{
if (d->currentImageView && d->currentImageView->document()) {
return d->currentImageView->document();
}
return 0;
}
KisScriptManager *KisViewManager::scriptManager() const
{
return &d->scriptManager;
}
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( QStringLiteral("templates/"), ".kra", 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);
mw->addViewAndNotifyLoadingCompleted(doc);
}
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()->setFileBatchMode(true);
document()->saveAs(QUrl::fromUserInput(fileName));
document()->setFileBatchMode(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(QUrl::fromUserInput(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()->setFileBatchMode(true);
QFile::copy(fileName, backupFileName);
document()->saveAs(QUrl::fromUserInput(fileName));
document()->setFileBatchMode(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);
Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
child->installEventFilter(&d->blockingEventFilter);
}
}
void KisViewManager::enableControls()
{
d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
child->removeEventFilter(&d->blockingEventFilter);
}
}
void KisViewManager::showStatusBar(bool toggled)
{
KisMainWindow *mw = mainWindow();
if(mw && mw->statusBar()) {
mw->statusBar()->setVisible(toggled);
KisConfig cfg;
cfg.setShowStatusBar(toggled);
}
}
void KisViewManager::switchCanvasOnly(bool toggled)
{
KisConfig cfg;
KisMainWindow* main = mainWindow();
if(!main) {
dbgUI << "Unable to switch to canvas-only mode, main window not found";
return;
}
if (toggled) {
d->canvasState = qtMainWindow()->saveState();
}
if (cfg.hideStatusbarFullscreen()) {
if (main->statusBar()) {
if (!toggled) {
if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
if (main->statusBar()->property("wasvisible").toBool()) {
main->statusBar()->setVisible(true);
}
}
}
else {
main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
main->statusBar()->setVisible(false);
}
}
}
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 (!toggled) {
if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
if (main->menuBar()->property("wasvisible").toBool()) {
main->menuBar()->setVisible(true);
}
}
}
else {
main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
main->menuBar()->setVisible(false);
}
}
if (cfg.hideToolbarFullscreen()) {
QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
Q_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_canvas_only")->shortcut().toString()), QIcon());
}
else {
main->restoreState(d->canvasState);
}
}
void KisViewManager::toggleTabletLogger()
{
d->inputManager.toggleTabletLogger();
}
void KisViewManager::openResourcesDirectory()
{
QString dir = KoResourcePaths::locateLocal("data", "");
QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
}
void KisViewManager::updateIcons()
{
if (mainWindow()) {
QList<QDockWidget*> dockers = mainWindow()->dockWidgets();
Q_FOREACH (QDockWidget* dock, dockers) {
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::initializeStatusBarVisibility()
{
KisConfig cfg;
d->mainWindow->statusBar()->setVisible(cfg.showStatusBar());
}
void KisViewManager::guiUpdateTimeout()
{
d->nodeManager.updateGUI();
d->selectionManager.updateGUI();
d->filterManager.updateGUI();
if (zoomManager()) {
zoomManager()->updateGUI();
}
d->gridManager.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_canvas_only")->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::slotSaveShowRulersState(bool value)
{
KisConfig cfg;
cfg.setShowRulers(value);
}
void KisViewManager::slotSaveRulersTrackMouseState(bool value)
{
KisConfig cfg;
cfg.setRulersTrackMouse(value);
}
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();
Q_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());
Q_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/libs/ui/actions/kis_selection_action_factories.cpp b/libs/ui/actions/kis_selection_action_factories.cpp
index b602ede316..2bf683397b 100644
--- a/libs/ui/actions/kis_selection_action_factories.cpp
+++ b/libs/ui/actions/kis_selection_action_factories.cpp
@@ -1,640 +1,640 @@
/*
* 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_selection_action_factories.h"
#include <QMimeData>
#include <klocalizedstring.h>
#include <kundo2command.h>
#include <KisMainWindow.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <KoPathShape.h>
#include <KoShapeController.h>
#include <KoShapeRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <KoOdfPaste.h>
#include <KoOdfLoadingContext.h>
#include <KoOdfReadStore.h>
#include <KoShapeManager.h>
#include <KoSelection.h>
#include <KoDrag.h>
#include <KoShapeOdfSaveHelper.h>
#include <KoShapeController.h>
#include <KoDocumentResourceManager.h>
#include <KoShapeStroke.h>
#include "KisViewManager.h"
#include "kis_canvas_resource_provider.h"
#include "kis_clipboard.h"
#include "kis_pixel_selection.h"
#include "kis_paint_layer.h"
#include "kis_image.h"
#include "kis_image_barrier_locker.h"
#include "kis_fill_painter.h"
#include "kis_transaction.h"
#include "kis_iterator_ng.h"
#include "kis_processing_applicator.h"
#include "kis_group_layer.h"
#include "commands/kis_selection_commands.h"
#include "commands/kis_image_layer_add_command.h"
#include "kis_tool_proxy.h"
#include "kis_canvas2.h"
#include "kis_canvas_controller.h"
#include "kis_selection_manager.h"
#include "kis_transaction_based_command.h"
#include "kis_selection_filters.h"
#include "kis_shape_selection.h"
#include "KisPart.h"
#include "kis_shape_layer.h"
#include <kis_shape_controller.h>
#include <processing/fill_processing_visitor.h>
#include <kis_selection_tool_helper.h>
#include "kis_canvas_resource_provider.h"
#include "kis_figure_painting_tool_helper.h"
namespace ActionHelper {
void copyFromDevice(KisViewManager *view, KisPaintDeviceSP device, bool makeSharpClip = false)
{
KisImageWSP image = view->image();
if (!image) return;
KisSelectionSP selection = view->selection();
QRect rc = (selection) ? selection->selectedExactRect() : image->bounds();
KisPaintDeviceSP clip = new KisPaintDevice(device->colorSpace());
Q_CHECK_PTR(clip);
const KoColorSpace *cs = clip->colorSpace();
// TODO if the source is linked... copy from all linked layers?!?
// Copy image data
KisPainter::copyAreaOptimized(QPoint(), device, clip, rc);
if (selection) {
// Apply selection mask.
KisPaintDeviceSP selectionProjection = selection->projection();
KisHLineIteratorSP layerIt = clip->createHLineIteratorNG(0, 0, rc.width());
KisHLineConstIteratorSP selectionIt = selectionProjection->createHLineIteratorNG(rc.x(), rc.y(), rc.width());
const KoColorSpace *selCs = selection->projection()->colorSpace();
for (qint32 y = 0; y < rc.height(); y++) {
for (qint32 x = 0; x < rc.width(); x++) {
/**
* Sharp method is an exact reverse of COMPOSITE_OVER
* so if you cover the cut/copied piece over its source
* you get an exactly the same image without any seams
*/
if (makeSharpClip) {
qreal dstAlpha = cs->opacityF(layerIt->rawData());
qreal sel = selCs->opacityF(selectionIt->oldRawData());
qreal newAlpha = sel * dstAlpha / (1.0 - dstAlpha + sel * dstAlpha);
float mask = newAlpha / dstAlpha;
cs->applyAlphaNormedFloatMask(layerIt->rawData(), &mask, 1);
} else {
cs->applyAlphaU8Mask(layerIt->rawData(), selectionIt->oldRawData(), 1);
}
layerIt->nextPixel();
selectionIt->nextPixel();
}
layerIt->nextRow();
selectionIt->nextRow();
}
}
KisClipboard::instance()->setClip(clip, rc.topLeft());
}
}
void KisSelectAllActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Select All"));
if (!image->globalSelection()) {
ap->applyCommand(new KisSetEmptyGlobalSelectionCommand(image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
struct SelectAll : public KisTransactionBasedCommand {
SelectAll(KisImageSP image) : m_image(image) {}
KisImageSP m_image;
KUndo2Command* paint() override {
KisSelectionSP selection = m_image->globalSelection();
KisSelectionTransaction transaction(selection->pixelSelection());
selection->pixelSelection()->select(m_image->bounds());
return transaction.endAndTake();
}
};
ap->applyCommand(new SelectAll(image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisDeselectActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
KUndo2Command *cmd = new KisDeselectGlobalSelectionCommand(image);
KisProcessingApplicator *ap = beginAction(view, cmd->text());
ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisReselectActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
KUndo2Command *cmd = new KisReselectGlobalSelectionCommand(image);
KisProcessingApplicator *ap = beginAction(view, cmd->text());
ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisFillActionFactory::run(const QString &fillSource, KisViewManager *view)
{
KisNodeSP node = view->activeNode();
if (!node || !node->hasEditablePaintDevice()) return;
KisSelectionSP selection = view->selection();
QRect selectedRect = selection ?
selection->selectedRect() : view->image()->bounds();
Q_UNUSED(selectedRect);
KisPaintDeviceSP filled = node->paintDevice()->createCompositionSourceDevice();
Q_UNUSED(filled);
bool usePattern = false;
bool useBgColor = false;
if (fillSource.contains("pattern")) {
usePattern = true;
} else if (fillSource.contains("bg")) {
useBgColor = true;
}
KisProcessingApplicator applicator(view->image(), node,
KisProcessingApplicator::NONE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Flood Fill Layer"));
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(view->image(), node, 0, view->resourceProvider()->resourceManager());
+ new KisResourcesSnapshot(view->image(), node, view->resourceProvider()->resourceManager());
if (!fillSource.contains("opacity")) {
resources->setOpacity(1.0);
}
KisProcessingVisitorSP visitor =
new FillProcessingVisitor(QPoint(0, 0), // start position
selection,
resources,
false, // fast mode
usePattern,
true, // fill only selection,
0, // feathering radius
0, // sizemod
80, // threshold,
false, // unmerged
useBgColor);
applicator.applyVisitor(visitor,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.end();
}
void KisClearActionFactory::run(KisViewManager *view)
{
// XXX: "Add saving of XML data for Clear action"
view->canvasBase()->toolProxy()->deleteSelection();
}
void KisImageResizeToSelectionActionFactory::run(KisViewManager *view)
{
// XXX: "Add saving of XML data for Image Resize To Selection action"
KisSelectionSP selection = view->selection();
if (!selection) return;
view->image()->cropImage(selection->selectedExactRect());
}
void KisCutCopyActionFactory::run(bool willCut, bool makeSharpClip, KisViewManager *view)
{
KisImageSP image = view->image();
if (!image) return;
bool haveShapesSelected = view->selectionManager()->haveShapesSelected();
if (haveShapesSelected) {
// XXX: "Add saving of XML data for Cut/Copy of shapes"
KisImageBarrierLocker locker(image);
if (willCut) {
view->canvasBase()->toolProxy()->cut();
} else {
view->canvasBase()->toolProxy()->copy();
}
} else {
KisNodeSP node = view->activeNode();
if (!node) return;
KisSelectionSP selection = view->selection();
if (selection.isNull()) return;
{
KisImageBarrierLocker locker(image);
KisPaintDeviceSP dev = node->paintDevice();
if (!dev) {
dev = node->projection();
}
if (!dev) {
view->showFloatingMessage(
i18nc("floating message when cannot copy from a node",
"Cannot copy pixels from this type of layer "),
QIcon(), 3000, KisFloatingMessage::Medium);
return;
}
if (dev->exactBounds().isEmpty()) {
view->showFloatingMessage(
i18nc("floating message when copying empty selection",
"Selection is empty: no pixels were copied "),
QIcon(), 3000, KisFloatingMessage::Medium);
return;
}
ActionHelper::copyFromDevice(view, dev, makeSharpClip);
}
if (willCut) {
KUndo2Command *command = 0;
if (willCut && node->hasEditablePaintDevice()) {
struct ClearSelection : public KisTransactionBasedCommand {
ClearSelection(KisNodeSP node, KisSelectionSP sel)
: m_node(node), m_sel(sel) {}
KisNodeSP m_node;
KisSelectionSP m_sel;
KUndo2Command* paint() override {
KisSelectionSP cutSelection = m_sel;
// Shrinking the cutting area was previously used
// for getting seamless cut-paste. Now we use makeSharpClip
// instead.
// QRect originalRect = cutSelection->selectedExactRect();
// static const int preciseSelectionThreshold = 16;
//
// if (originalRect.width() > preciseSelectionThreshold ||
// originalRect.height() > preciseSelectionThreshold) {
// cutSelection = new KisSelection(*m_sel);
// delete cutSelection->flatten();
//
// KisSelectionFilter* filter = new KisShrinkSelectionFilter(1, 1, false);
//
// QRect processingRect = filter->changeRect(originalRect);
// filter->process(cutSelection->pixelSelection(), processingRect);
// }
KisTransaction transaction(m_node->paintDevice());
m_node->paintDevice()->clearSelection(cutSelection);
m_node->setDirty(cutSelection->selectedRect());
return transaction.endAndTake();
}
};
command = new ClearSelection(node, selection);
}
KUndo2MagicString actionName = willCut ?
kundo2_i18n("Cut") :
kundo2_i18n("Copy");
KisProcessingApplicator *ap = beginAction(view, actionName);
if (command) {
ap->applyCommand(command,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
}
KisOperationConfiguration config(id());
config.setProperty("will-cut", willCut);
endAction(ap, config.toXML());
}
}
}
void KisCopyMergedActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
image->barrierLock();
KisPaintDeviceSP dev = image->root()->projection();
ActionHelper::copyFromDevice(view, dev);
image->unlock();
KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Copy Merged"));
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisPasteActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
KisPaintDeviceSP clip = KisClipboard::instance()->clip(image->bounds(), true);
if (clip) {
KisPaintLayer *newLayer = new KisPaintLayer(image.data(), image->nextLayerName() + i18n("(pasted)"), OPACITY_OPAQUE_U8, clip);
KisNodeSP aboveNode = view->activeLayer();
KisNodeSP parentNode = aboveNode ? aboveNode->parent() : image->root();
KUndo2Command *cmd = new KisImageLayerAddCommand(image, newLayer, parentNode, aboveNode);
KisProcessingApplicator *ap = beginAction(view, cmd->text());
ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL);
endAction(ap, KisOperationConfiguration(id()).toXML());
} else {
// XXX: "Add saving of XML data for Paste of shapes"
view->canvasBase()->toolProxy()->paste();
}
}
void KisPasteNewActionFactory::run(KisViewManager *viewManager)
{
Q_UNUSED(viewManager);
KisPaintDeviceSP clip = KisClipboard::instance()->clip(QRect(), true);
if (!clip) return;
QRect rect = clip->exactBounds();
if (rect.isEmpty()) return;
KisDocument *doc = KisPart::instance()->createDocument();
KisImageSP image = new KisImage(doc->createUndoStore(),
rect.width(),
rect.height(),
clip->colorSpace(),
i18n("Pasted"));
KisPaintLayerSP layer =
new KisPaintLayer(image.data(), image->nextLayerName() + i18n("(pasted)"),
OPACITY_OPAQUE_U8, clip->colorSpace());
KisPainter::copyAreaOptimized(QPoint(), clip, layer->paintDevice(), rect);
image->addNode(layer.data(), image->rootLayer());
doc->setCurrentImage(image);
KisPart::instance()->addDocument(doc);
KisMainWindow *win = viewManager->mainWindow();
win->addViewAndNotifyLoadingCompleted(doc);
}
void KisInvertSelectionOperaton::runFromXML(KisViewManager* view, const KisOperationConfiguration& config)
{
KisSelectionFilter* filter = new KisInvertSelectionFilter();
runFilter(filter, view, config);
}
void KisSelectionToVectorActionFactory::run(KisViewManager *view)
{
KisSelectionSP selection = view->selection();
if (selection->hasShapeSelection() ||
!selection->outlineCacheValid()) {
return;
}
QPainterPath selectionOutline = selection->outlineCache();
QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline));
shape->setShapeId(KoPathShapeId);
/**
* Mark a shape that it belongs to a shape selection
*/
if(!shape->userData()) {
shape->setUserData(new KisShapeSelectionMarker);
}
KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Convert to Vector Selection"));
ap->applyCommand(view->canvasBase()->shapeController()->addShape(shape),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
endAction(ap, KisOperationConfiguration(id()).toXML());
}
class KisShapeSelectionPaste : public KoOdfPaste
{
public:
KisShapeSelectionPaste(KisViewManager* view) : m_view(view)
{
}
~KisShapeSelectionPaste() override {
}
bool process(const KoXmlElement & body, KoOdfReadStore & odfStore) override {
KoOdfLoadingContext loadingContext(odfStore.styles(), odfStore.store());
KoShapeLoadingContext context(loadingContext, m_view->canvasBase()->shapeController()->resourceManager());
KoXmlElement child;
QList<KoShape*> shapes;
forEachElement(child, body) {
KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, context);
if (shape) {
shapes.append(shape);
}
}
if (!shapes.isEmpty()) {
KisSelectionToolHelper helper(m_view->canvasBase(), kundo2_i18n("Convert shapes to vector selection"));
helper.addSelectionShapes(shapes);
}
return true;
}
private:
KisViewManager* m_view;
};
void KisShapesToVectorSelectionActionFactory::run(KisViewManager* view)
{
QList<KoShape*> shapes = view->canvasBase()->shapeManager()->selection()->selectedShapes();
KoShapeOdfSaveHelper saveHelper(shapes);
KoDrag drag;
drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
QMimeData* mimeData = drag.mimeData();
Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text)));
KisShapeSelectionPaste paste(view);
paste.paste(KoOdf::Text, mimeData);
}
void KisSelectionToShapeActionFactory::run(KisViewManager *view)
{
KisSelectionSP selection = view->selection();
if (!selection->outlineCacheValid()) {
return;
}
QPainterPath selectionOutline = selection->outlineCache();
QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline));
shape->setShapeId(KoPathShapeId);
KoColor fgColor = view->canvasBase()->resourceManager()->resource(KoCanvasResourceManager::ForegroundColor).value<KoColor>();
KoShapeStroke* border = new KoShapeStroke(1.0, fgColor.toQColor());
shape->setStroke(border);
view->document()->shapeController()->addShape(shape);
}
void KisStrokeSelectionActionFactory::run(KisViewManager *view, StrokeSelectionOptions params)
{
KisImageWSP image = view->image();
if (!image) {
return;
}
KisSelectionSP selection = view->selection();
if (!selection) {
return;
}
int size = params.lineSize;
KisPixelSelectionSP pixelSelection = selection->projection();
if (!pixelSelection->outlineCacheValid()) {
pixelSelection->recalculateOutlineCache();
}
QPainterPath outline = pixelSelection->outlineCache();
QColor color = params.color.toQColor();
KisNodeSP currentNode = view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value<KisNodeWSP>();
if (!currentNode->inherits("KisShapeLayer") && currentNode->childCount() == 0) {
KoCanvasResourceManager * rManager = view->resourceProvider()->resourceManager();
KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush;
KisPainter::FillStyle fillStyle = params.fillStyle();
KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Polyline"),
image,
currentNode,
rManager ,
strokeStyle,
fillStyle);
helper.setFGColorOverride(params.color);
helper.setSelectionOverride(0);
QPen pen(Qt::red, size);
pen.setJoinStyle(Qt::RoundJoin);
if (fillStyle != KisPainter::FillStyleNone) {
helper.paintPainterPathQPenFill(outline, pen, params.fillColor);
}
else {
helper.paintPainterPathQPen(outline, pen, params.fillColor);
}
}
else {
QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(outline));
shape->setShapeId(KoPathShapeId);
KoShapeStroke* border = new KoShapeStroke(size, color);
shape->setStroke(border);
view->document()->shapeController()->addShape(shape);
}
image->setModified();
}
void KisStrokeBrushSelectionActionFactory::run(KisViewManager *view, StrokeSelectionOptions params)
{
KisImageWSP image = view->image();
if (!image) {
return;
}
KisSelectionSP selection = view->selection();
if (!selection) {
return;
}
KisPixelSelectionSP pixelSelection = selection->projection();
if (!pixelSelection->outlineCacheValid()) {
pixelSelection->recalculateOutlineCache();
}
KisNodeSP currentNode = view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value<KisNodeWSP>();
if (!currentNode->inherits("KisShapeLayer") && currentNode->childCount() == 0)
{
KoCanvasResourceManager * rManager = view->resourceProvider()->resourceManager();
QPainterPath outline = pixelSelection->outlineCache();
KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush;
KisPainter::FillStyle fillStyle = KisPainter::FillStyleNone;
KoColor color = params.color;
KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Polyline"),
image,
currentNode,
rManager ,
strokeStyle,
fillStyle);
helper.setFGColorOverride(color);
helper.setSelectionOverride(0);
helper.paintPainterPath(outline);
image->setModified();
}
}
diff --git a/libs/ui/canvas/kis_animation_player.cpp b/libs/ui/canvas/kis_animation_player.cpp
index 4fba8b7601..116a7f3569 100644
--- a/libs/ui/canvas/kis_animation_player.cpp
+++ b/libs/ui/canvas/kis_animation_player.cpp
@@ -1,372 +1,383 @@
/*
* Copyright (c) 2015 Jouni Pentikäinen <joupent@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_animation_player.h"
#include <QElapsedTimer>
#include <QTimer>
#include <QtMath>
//#define PLAYER_DEBUG_FRAMERATE
#include "kis_global.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_image.h"
#include "kis_canvas2.h"
#include "kis_animation_frame_cache.h"
#include "kis_signal_auto_connection.h"
#include "kis_image_animation_interface.h"
#include "kis_time_range.h"
#include "kis_signal_compressor.h"
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/rolling_mean.hpp>
using namespace boost::accumulators;
typedef accumulator_set<qreal, stats<tag::rolling_mean> > FpsAccumulator;
struct KisAnimationPlayer::Private
{
public:
Private(KisAnimationPlayer *_q)
: q(_q),
realFpsAccumulator(tag::rolling_window::window_size = 24),
droppedFpsAccumulator(tag::rolling_window::window_size = 24),
droppedFramesPortion(tag::rolling_window::window_size = 24),
dropFramesMode(true),
nextFrameExpectedTime(0),
expectedInterval(0),
expectedFrame(0),
lastTimerInterval(0),
lastPaintedFrame(0),
playbackStatisticsCompressor(1000, KisSignalCompressor::FIRST_INACTIVE)
{}
KisAnimationPlayer *q;
bool useFastFrameUpload;
bool playing;
QTimer *timer;
+ int initialFrame;
int firstFrame;
int lastFrame;
int fps;
qreal playbackSpeed;
KisCanvas2 *canvas;
KisSignalAutoConnectionsStore cancelStrokeConnections;
QElapsedTimer realFpsTimer;
FpsAccumulator realFpsAccumulator;
FpsAccumulator droppedFpsAccumulator;
FpsAccumulator droppedFramesPortion;
bool dropFramesMode;
QElapsedTimer playbackTime;
int nextFrameExpectedTime;
int expectedInterval;
int expectedFrame;
int lastTimerInterval;
int lastPaintedFrame;
KisSignalCompressor playbackStatisticsCompressor;
void stopImpl(bool doUpdates);
int incFrame(int frame, int inc) {
frame += inc;
if (frame > lastFrame) {
frame = firstFrame + frame - lastFrame - 1;
}
return frame;
}
};
KisAnimationPlayer::KisAnimationPlayer(KisCanvas2 *canvas)
: QObject(canvas)
, m_d(new Private(this))
{
m_d->useFastFrameUpload = false;
m_d->playing = false;
m_d->fps = 15;
m_d->canvas = canvas;
m_d->playbackSpeed = 1.0;
m_d->timer = new QTimer(this);
connect(m_d->timer, SIGNAL(timeout()), this, SLOT(slotUpdate()));
m_d->timer->setSingleShot(true);
connect(KisConfigNotifier::instance(),
SIGNAL(dropFramesModeChanged()),
SLOT(slotUpdateDropFramesMode()));
slotUpdateDropFramesMode();
connect(&m_d->playbackStatisticsCompressor, SIGNAL(timeout()),
this, SIGNAL(sigPlaybackStatisticsUpdated()));
}
KisAnimationPlayer::~KisAnimationPlayer()
{}
void KisAnimationPlayer::slotUpdateDropFramesMode()
{
KisConfig cfg;
m_d->dropFramesMode = cfg.animationDropFrames();
}
void KisAnimationPlayer::connectCancelSignals()
{
m_d->cancelStrokeConnections.addConnection(
m_d->canvas->image().data(), SIGNAL(sigUndoDuringStrokeRequested()),
this, SLOT(slotCancelPlayback()));
m_d->cancelStrokeConnections.addConnection(
m_d->canvas->image().data(), SIGNAL(sigStrokeCancellationRequested()),
this, SLOT(slotCancelPlayback()));
m_d->cancelStrokeConnections.addConnection(
m_d->canvas->image().data(), SIGNAL(sigStrokeEndRequested()),
this, SLOT(slotCancelPlaybackSafe()));
m_d->cancelStrokeConnections.addConnection(
m_d->canvas->image()->animationInterface(), SIGNAL(sigFramerateChanged()),
this, SLOT(slotUpdatePlaybackTimer()));
m_d->cancelStrokeConnections.addConnection(
m_d->canvas->image()->animationInterface(), SIGNAL(sigFullClipRangeChanged()),
this, SLOT(slotUpdatePlaybackTimer()));
m_d->cancelStrokeConnections.addConnection(
m_d->canvas->image()->animationInterface(), SIGNAL(sigPlaybackRangeChanged()),
this, SLOT(slotUpdatePlaybackTimer()));
}
void KisAnimationPlayer::disconnectCancelSignals()
{
m_d->cancelStrokeConnections.clear();
}
void KisAnimationPlayer::slotUpdatePlaybackTimer()
{
m_d->timer->stop();
const KisImageAnimationInterface *animation = m_d->canvas->image()->animationInterface();
const KisTimeRange &range = animation->playbackRange();
if (!range.isValid()) return;
m_d->fps = animation->framerate();
+
+ m_d->initialFrame = animation->currentUITime();
m_d->firstFrame = range.start();
m_d->lastFrame = range.end();
m_d->expectedFrame = qBound(m_d->firstFrame, m_d->expectedFrame, m_d->lastFrame);
m_d->expectedInterval = qreal(1000) / m_d->fps / m_d->playbackSpeed;
m_d->lastTimerInterval = m_d->expectedInterval;
m_d->timer->start(m_d->expectedInterval);
if (m_d->playbackTime.isValid()) {
m_d->playbackTime.restart();
} else {
m_d->playbackTime.start();
}
m_d->nextFrameExpectedTime = m_d->playbackTime.elapsed() + m_d->expectedInterval;
}
void KisAnimationPlayer::play()
{
m_d->playing = true;
slotUpdatePlaybackTimer();
m_d->expectedFrame = m_d->firstFrame;
m_d->lastPaintedFrame = m_d->firstFrame;
connectCancelSignals();
}
void KisAnimationPlayer::Private::stopImpl(bool doUpdates)
{
q->disconnectCancelSignals();
timer->stop();
playing = false;
if (doUpdates) {
- canvas->refetchDataFromImage();
+ KisImageAnimationInterface *animation = canvas->image()->animationInterface();
+ if (animation->currentUITime() == initialFrame) {
+ canvas->refetchDataFromImage();
+ } else {
+ animation->switchCurrentTimeAsync(initialFrame);
+ }
}
emit q->sigPlaybackStopped();
}
void KisAnimationPlayer::stop()
{
m_d->stopImpl(true);
}
void KisAnimationPlayer::forcedStopOnExit()
{
m_d->stopImpl(false);
}
bool KisAnimationPlayer::isPlaying()
{
return m_d->playing;
}
int KisAnimationPlayer::currentTime()
{
return m_d->lastPaintedFrame;
}
void KisAnimationPlayer::displayFrame(int time)
{
uploadFrame(time);
}
void KisAnimationPlayer::slotUpdate()
{
uploadFrame(-1);
}
void KisAnimationPlayer::uploadFrame(int frame)
{
if (frame < 0) {
const int currentTime = m_d->playbackTime.elapsed();
const int framesDiff = currentTime - m_d->nextFrameExpectedTime;
const qreal framesDiffNorm = qreal(framesDiff) / m_d->expectedInterval;
// qDebug() << ppVar(framesDiff)
// << ppVar(m_d->expectedFrame)
// << ppVar(framesDiffNorm)
// << ppVar(m_d->lastTimerInterval);
if (m_d->dropFramesMode) {
const int numDroppedFrames = qMax(0, qRound(framesDiffNorm));
frame = m_d->incFrame(m_d->expectedFrame, numDroppedFrames);
} else {
frame = m_d->expectedFrame;
}
m_d->nextFrameExpectedTime = currentTime + m_d->expectedInterval;
m_d->lastTimerInterval = qMax(0.0, m_d->lastTimerInterval - 0.5 * framesDiff);
m_d->expectedFrame = m_d->incFrame(frame, 1);
m_d->timer->start(m_d->lastTimerInterval);
m_d->playbackStatisticsCompressor.start();
}
if (m_d->canvas->frameCache() && m_d->canvas->frameCache()->uploadFrame(frame)) {
m_d->canvas->updateCanvas();
m_d->useFastFrameUpload = true;
emit sigFrameChanged();
} else {
m_d->useFastFrameUpload = false;
+ m_d->canvas->image()->barrierLock(true);
+ m_d->canvas->image()->unlock();
+
// no OpenGL cache or the frame just not cached yet
m_d->canvas->image()->animationInterface()->switchCurrentTimeAsync(frame);
emit sigFrameChanged();
}
if (!m_d->realFpsTimer.isValid()) {
m_d->realFpsTimer.start();
} else {
const int elapsed = m_d->realFpsTimer.restart();
m_d->realFpsAccumulator(elapsed);
int numFrames = frame - m_d->lastPaintedFrame;
if (numFrames < 0) {
numFrames += m_d->lastFrame - m_d->firstFrame + 1;
}
m_d->droppedFramesPortion(qreal(int(numFrames != 1)));
if (numFrames > 0) {
m_d->droppedFpsAccumulator(qreal(elapsed) / numFrames);
}
#ifdef PLAYER_DEBUG_FRAMERATE
qDebug() << " RFPS:" << 1000.0 / rolling_mean(m_d->realFpsAccumulator)
<< "DFPS:" << 1000.0 / rolling_mean(m_d->droppedFpsAccumulator) << ppVar(numFrames);
#endif /* PLAYER_DEBUG_FRAMERATE */
}
m_d->lastPaintedFrame = frame;
}
qreal KisAnimationPlayer::effectiveFps() const
{
return 1000.0 / rolling_mean(m_d->droppedFpsAccumulator);
}
qreal KisAnimationPlayer::realFps() const
{
return 1000.0 / rolling_mean(m_d->realFpsAccumulator);
}
qreal KisAnimationPlayer::framesDroppedPortion() const
{
return rolling_mean(m_d->droppedFramesPortion);
}
void KisAnimationPlayer::slotCancelPlayback()
{
stop();
}
void KisAnimationPlayer::slotCancelPlaybackSafe()
{
/**
* If there is no openGL support, then we have no (!) cache at
* all. Therefore we should regenerate frame on every time switch,
* which, yeah, can be very slow. What is more important, when
* regenerating a frame animation interface will emit a
* sigStrokeEndRequested() signal and we should ignore it. That is
* not an ideal solution, because the user will be able to paint
* on random frames while playing, but it lets users with faulty
* GPUs see at least some preview of their animation.
*/
if (m_d->useFastFrameUpload) {
stop();
}
}
qreal KisAnimationPlayer::playbackSpeed()
{
return m_d->playbackSpeed;
}
void KisAnimationPlayer::slotUpdatePlaybackSpeed(double value)
{
m_d->playbackSpeed = value;
if (m_d->playing) {
slotUpdatePlaybackTimer();
}
}
diff --git a/libs/ui/canvas/kis_canvas2.cpp b/libs/ui/canvas/kis_canvas2.cpp
index b3dc768bf5..82ccd85f8f 100644
--- a/libs/ui/canvas/kis_canvas2.cpp
+++ b/libs/ui/canvas/kis_canvas2.cpp
@@ -1,969 +1,980 @@
/* 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 <KoShapeController.h>
#include <kis_lod_transform.h>
#include "kis_tool_proxy.h"
#include "kis_coordinates_converter.h"
#include "kis_prescaled_projection.h"
#include "kis_image.h"
#include "kis_image_barrier_locker.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"
#include "kis_grid_config.h"
#include "kis_animation_player.h"
#include "kis_animation_frame_cache.h"
#include "opengl/kis_opengl_canvas2.h"
#include "opengl/kis_opengl.h"
#include "kis_fps_decoration.h"
#include "KoColorConversionTransformation.h"
#include "KisProofingConfiguration.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"
+#include "KoZoomController.h"
+
class Q_DECL_HIDDEN KisCanvas2::KisCanvas2Private
{
public:
KisCanvas2Private(KoCanvasBase *parent, KisCoordinatesConverter* coordConverter, QPointer<KisView> view, KoCanvasResourceManager* resourceManager)
: coordinatesConverter(coordConverter)
, view(view)
, shapeManager(parent)
, toolProxy(parent)
, displayColorConverter(resourceManager, view)
{
}
KisCoordinatesConverter *coordinatesConverter;
QPointer<KisView>view;
KisAbstractCanvasWidget *canvasWidget = 0;
KoShapeManager shapeManager;
bool currentCanvasIsOpenGL;
int openGLFilterMode;
KisToolProxy toolProxy;
KisPrescaledProjectionSP prescaledProjection;
bool vastScrolling;
KisSignalCompressor updateSignalCompressor;
QRect savedUpdateRect;
QBitArray channelFlags;
KisProofingConfigurationSP proofingConfig;
bool softProofing = false;
bool gamutCheck = false;
bool proofingConfigUpdated = false;
KisPopupPalette *popupPalette = 0;
KisDisplayColorConverter displayColorConverter;
KisCanvasUpdatesCompressor projectionUpdatesCompressor;
KisAnimationPlayer *animationPlayer;
KisAnimationFrameCacheSP frameCache;
bool lodAllowedInCanvas;
bool bootstrapLodBlocked;
bool effectiveLodAllowedInCanvas() {
return lodAllowedInCanvas && !bootstrapLodBlocked;
}
};
KisCanvas2::KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResourceManager *resourceManager, KisView *view, KoShapeBasedDocumentBase *sc)
: KoCanvasBase(sc, resourceManager)
, m_d(new KisCanvas2Private(this, coordConverter, view, resourceManager))
{
/**
* While loading LoD should be blocked. Only when GUI has finished
* loading and zoom level settled down, LoD is given a green
* light.
*/
m_d->bootstrapLodBlocked = true;
connect(view->mainWindow(), SIGNAL(guiLoadingFinished()), SLOT(bootstrapFinished()));
m_d->updateSignalCompressor.setDelay(10);
m_d->updateSignalCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
}
void KisCanvas2::setup()
{
// a bit of duplication from slotConfigChanged()
KisConfig cfg;
m_d->vastScrolling = cfg.vastScrolling();
m_d->lodAllowedInCanvas = cfg.levelOfDetailEnabled();
createCanvas(cfg.useOpenGL());
setLodAllowedInCanvas(m_d->lodAllowedInCanvas);
m_d->animationPlayer = new KisAnimationPlayer(this);
connect(m_d->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 = static_cast<KisShapeController*>(shapeController()->documentBase());
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*)));
connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate()));
}
KisCanvas2::~KisCanvas2()
{
if (m_d->animationPlayer->isPlaying()) {
m_d->animationPlayer->forcedStopOnExit();
}
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() const
{
return m_d->currentCanvasIsOpenGL;
}
KisOpenGL::FilterMode KisCanvas2::openGLFilterMode() const
{
return KisOpenGL::FilterMode(m_d->openGLFilterMode);
}
void KisCanvas2::gridSize(QPointF *offset, QSizeF *spacing) const
{
QTransform transform = coordinatesConverter()->imageToDocumentTransform();
const QPoint intSpacing = m_d->view->document()->gridConfig().spacing();
const QPoint intOffset = m_d->view->document()->gridConfig().offset();
QPointF size = transform.map(QPointF(intSpacing));
spacing->rwidth() = size.x();
spacing->rheight() = size.y();
*offset = transform.map(QPointF(intOffset));
}
bool KisCanvas2::snapToGrid() const
{
return m_d->view->document()->gridConfig().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 globalShapeManager();
if (!viewManager()->nodeManager()) return globalShapeManager();
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 globalShapeManager();
}
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())) {
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()
{
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);
m_d->frameCache = KisAnimationFrameCache::getFrameCache(canvasWidget->openGLImageTextures());
setCanvasWidget(canvasWidget);
if (canvasWidget->needsFpsDebugging() && !decoration(KisFpsDecoration::idTag)) {
addDecoration(new KisFpsDecoration(imageView()));
}
}
void KisCanvas2::createCanvas(bool useOpenGL)
{
// deinitialize previous canvas structures
m_d->prescaledProjection = 0;
m_d->frameCache = 0;
KisConfig cfg;
QDesktopWidget dw;
const KoColorProfile *profile = cfg.displayProfile(dw.screenNumber(imageView()));
m_d->displayColorConverter.setMonitorProfile(profile);
if (useOpenGL) {
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 {
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(sigProofingConfigChanged()), SLOT(slotChangeProofingConfig()));
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);
setLodAllowedInCanvas(m_d->lodAllowedInCanvas);
}
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;
}
KisConfig cfg;
bool needReset = (m_d->currentCanvasIsOpenGL != useOpenGL) ||
(m_d->currentCanvasIsOpenGL &&
m_d->openGLFilterMode != cfg.openGLFilteringMode());
if (needReset) {
createCanvas(useOpenGL);
connectCurrentCanvas();
notifyZoomChanged();
}
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(QSharedPointer<KisDisplayFilter> displayFilter)
{
m_d->displayColorConverter.setDisplayFilter(displayFilter);
KisImageWSP image = this->image();
image->barrierLock();
m_d->canvasWidget->setDisplayFilter(displayFilter);
image->unlock();
}
QSharedPointer<KisDisplayFilter> KisCanvas2::displayFilter() const
{
return m_d->displayColorConverter.displayFilter();
}
KisDisplayColorConverter* KisCanvas2::displayColorConverter() const
{
return &m_d->displayColorConverter;
}
KisExposureGammaCorrectionInterface* KisCanvas2::exposureGammaCorrectionInterface() const
{
QSharedPointer<KisDisplayFilter> displayFilter = m_d->displayColorConverter.displayFilter();
return displayFilter ?
displayFilter->correctionInterface() :
KisDumbExposureGammaCorrectionInterface::instance();
}
void KisCanvas2::setProofingOptions(bool softProof, bool gamutCheck)
{
m_d->proofingConfig = this->image()->proofingConfiguration();
if (!m_d->proofingConfig) {
qDebug()<<"Canvas: No proofing config found, generating one.";
KisImageConfig cfg;
m_d->proofingConfig = cfg.defaultProofingconfiguration();
}
KoColorConversionTransformation::ConversionFlags conversionFlags = m_d->proofingConfig->conversionFlags;
if (softProof && this->image()->colorSpace()->colorDepthId().id().contains("U")) {
conversionFlags |= KoColorConversionTransformation::SoftProofing;
} else {
conversionFlags = conversionFlags & ~KoColorConversionTransformation::SoftProofing;
}
if (gamutCheck && softProof && this->image()->colorSpace()->colorDepthId().id().contains("U")) {
conversionFlags |= KoColorConversionTransformation::GamutCheck;
} else {
conversionFlags = conversionFlags & ~KoColorConversionTransformation::GamutCheck;
}
m_d->proofingConfig->conversionFlags = conversionFlags;
m_d->proofingConfigUpdated = true;
startUpdateInPatches(this->image()->bounds());
}
void KisCanvas2::slotSoftProofing(bool softProofing)
{
m_d->softProofing = softProofing;
setProofingOptions(m_d->softProofing, m_d->gamutCheck);
}
void KisCanvas2::slotGamutCheck(bool gamutCheck)
{
m_d->gamutCheck = gamutCheck;
setProofingOptions(m_d->softProofing, m_d->gamutCheck);
}
void KisCanvas2::slotChangeProofingConfig()
{
setProofingOptions(m_d->softProofing, m_d->gamutCheck);
}
void KisCanvas2::setProofingConfigUpdated(bool updated)
{
m_d->proofingConfigUpdated = updated;
}
bool KisCanvas2::proofingConfigUpdated()
{
return m_d->proofingConfigUpdated;
}
KisProofingConfigurationSP KisCanvas2::proofingConfiguration() const
{
if (!m_d->proofingConfig) {
m_d->proofingConfig = this->image()->proofingConfiguration();
if (!m_d->proofingConfig) {
qDebug()<<"Canvas: No proofing config found, generating one.";
KisImageConfig cfg;
m_d->proofingConfig = cfg.defaultProofingconfiguration();
}
}
return m_d->proofingConfig;
}
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();
}
notifyLevelOfDetailChange();
updateCanvas(); // update the canvas, because that isn't done when zooming using KoZoomAction
}
void KisCanvas2::notifyLevelOfDetailChange()
{
if (!m_d->effectiveLodAllowedInCanvas()) return;
KisImageSP image = this->image();
const qreal effectiveZoom = m_d->coordinatesConverter->effectiveZoom();
KisConfig cfg;
const int maxLod = cfg.numMipmapLevels();
int lod = KisLodTransform::scaleToLod(effectiveZoom, maxLod);
image->setDesiredLevelOfDetail(lod);
}
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::refetchDataFromImage()
{
KisImageSP image = this->image();
KisImageBarrierLocker l(image);
startUpdateInPatches(image->bounds());
}
void KisCanvas2::slotSetDisplayProfile(const KoColorProfile *monitorProfile)
{
if (m_d->displayColorConverter.monitorProfile() == monitorProfile) return;
m_d->displayColorConverter.setMonitorProfile(monitorProfile);
{
KisImageSP image = this->image();
KisImageBarrierLocker l(image);
m_d->canvasWidget->setDisplayProfile(&m_d->displayColorConverter);
}
refetchDataFromImage();
}
void KisCanvas2::addDecoration(KisCanvasDecorationSP deco)
{
m_d->canvasWidget->addDecoration(deco);
}
KisCanvasDecorationSP 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->view->resourceProvider(), m_d->canvasWidget->widget());
+ m_d->popupPalette = new KisPopupPalette(viewManager(), m_d->coordinatesConverter, favoriteResourceManager, displayColorConverter()->displayRendererInterface(),
+ m_d->view->resourceProvider(), m_d->canvasWidget->widget());
+ connect(m_d->popupPalette, SIGNAL(zoomLevelChanged(int)), this, SLOT(slotZoomChanged(int)));
+ connect(m_d->popupPalette, SIGNAL(sigUpdateCanvas()), this, SLOT(updateCanvas()));
+
m_d->popupPalette->showPopupPalette(false);
}
+void KisCanvas2::slotZoomChanged(int zoom ) {
+ m_d->view->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, (qreal)(zoom/100.0)); // 1.0 is 100% zoom
+ notifyZoomChanged();
+}
+
void KisCanvas2::setCursor(const QCursor &cursor)
{
canvasWidget()->setCursor(cursor);
}
KisAnimationFrameCacheSP KisCanvas2::frameCache() const
{
return m_d->frameCache;
}
KisAnimationPlayer *KisCanvas2::animationPlayer() const
{
return m_d->animationPlayer;
}
void KisCanvas2::slotSelectionChanged()
{
KisShapeLayer* shapeLayer = dynamic_cast<KisShapeLayer*>(viewManager()->activeLayer().data());
if (!shapeLayer) {
return;
}
m_d->shapeManager.selection()->deselectAll();
Q_FOREACH (KoShape* shape, shapeLayer->shapeManager()->selection()->selectedShapes()) {
m_d->shapeManager.selection()->select(shape);
}
}
bool KisCanvas2::isPopupPaletteVisible() const
{
if (!m_d->popupPalette) {
return false;
}
return m_d->popupPalette->isVisible();
}
void KisCanvas2::setWrapAroundViewingMode(bool value)
{
KisCanvasDecorationSP infinityDecoration =
m_d->canvasWidget->decoration(INFINITY_DECORATION_ID);
if (infinityDecoration) {
infinityDecoration->setVisible(!value);
}
m_d->canvasWidget->setWrapAroundViewingMode(value);
}
bool KisCanvas2::wrapAroundViewingMode() const
{
KisCanvasDecorationSP infinityDecoration =
m_d->canvasWidget->decoration(INFINITY_DECORATION_ID);
if (infinityDecoration) {
return !(infinityDecoration->visible());
}
return false;
}
void KisCanvas2::bootstrapFinished()
{
if (!m_d->bootstrapLodBlocked) return;
m_d->bootstrapLodBlocked = false;
setLodAllowedInCanvas(m_d->lodAllowedInCanvas);
}
void KisCanvas2::setLodAllowedInCanvas(bool value)
{
if (!KisOpenGL::supportsLoD()) {
qWarning() << "WARNING: Level of Detail functionality is available only with openGL + GLSL 1.3 support";
}
m_d->lodAllowedInCanvas =
value &&
m_d->currentCanvasIsOpenGL &&
KisOpenGL::supportsLoD() &&
(m_d->openGLFilterMode == KisOpenGL::TrilinearFilterMode ||
m_d->openGLFilterMode == KisOpenGL::HighQualityFiltering);
KisImageSP image = this->image();
if (m_d->effectiveLodAllowedInCanvas() != !image->levelOfDetailBlocked()) {
image->setLevelOfDetailBlocked(!m_d->effectiveLodAllowedInCanvas());
notifyLevelOfDetailChange();
}
KisConfig cfg;
cfg.setLevelOfDetailEnabled(m_d->lodAllowedInCanvas);
}
bool KisCanvas2::lodAllowedInCanvas() const
{
return m_d->lodAllowedInCanvas;
}
void KisCanvas2::slotShowPopupPalette(const QPoint &p)
{
if (!m_d->popupPalette) {
return;
}
m_d->popupPalette->showPopupPalette(p);
}
KisPaintingAssistantsDecorationSP KisCanvas2::paintingAssistantsDecoration() const
{
KisCanvasDecorationSP deco = decoration("paintingAssistantsDecoration");
return qobject_cast<KisPaintingAssistantsDecoration*>(deco.data());
}
diff --git a/libs/ui/canvas/kis_canvas2.h b/libs/ui/canvas/kis_canvas2.h
index 2efb1ee384..d0fe7de592 100644
--- a/libs/ui/canvas/kis_canvas2.h
+++ b/libs/ui/canvas/kis_canvas2.h
@@ -1,296 +1,297 @@
/* This file is part of the KDE project
* Copyright (C) 2006, 2010 Boudewijn Rempt <boud@valdyas.org>
* 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.
*/
#ifndef KIS_CANVAS_H
#define KIS_CANVAS_H
#include <QObject>
#include <QWidget>
#include <QSize>
#include <QString>
#include <KoConfig.h>
#include <KoColorConversionTransformation.h>
#include <KoCanvasBase.h>
#include <kritaui_export.h>
#include <kis_types.h>
#include <KoPointerEvent.h>
#include "opengl/kis_opengl.h"
#include "kis_ui_types.h"
#include "kis_coordinates_converter.h"
#include "kis_canvas_decoration.h"
#include "kis_painting_assistants_decoration.h"
class KoToolProxy;
class KoColorProfile;
class KisViewManager;
class KisFavoriteResourceManager;
class KisDisplayFilter;
class KisDisplayColorConverter;
struct KisExposureGammaCorrectionInterface;
class KisView;
class KisInputManager;
class KisAnimationPlayer;
class KisShapeController;
class KisCoordinatesConverter;
class KoViewConverter;
/**
* KisCanvas2 is not an actual widget class, but rather an adapter for
* the widget it contains, which may be either a QPainter based
* canvas, or an OpenGL based canvas: that are the real widgets.
*/
class KRITAUI_EXPORT KisCanvas2 : public QObject, public KoCanvasBase
{
Q_OBJECT
public:
/**
* Create a new canvas. The canvas manages a widget that will do
* the actual painting: the canvas itself is not a widget.
*
* @param viewConverter the viewconverter for converting between
* window and document coordinates.
*/
KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResourceManager *resourceManager, KisView *view, KoShapeBasedDocumentBase *sc);
virtual ~KisCanvas2();
void notifyZoomChanged();
virtual void disconnectCanvasObserver(QObject *object);
public: // KoCanvasBase implementation
bool canvasIsOpenGL() const;
KisOpenGL::FilterMode openGLFilterMode() const;
void gridSize(QPointF *offset, QSizeF *spacing) const;
bool snapToGrid() const;
// XXX: Why?
void addCommand(KUndo2Command *command);
virtual QPoint documentOrigin() const;
QPoint documentOffset() const;
/**
* Return the right shape manager for the current layer. That is
* to say, if the current layer is a vector layer, return the shape
* layer's canvas' shapemanager, else the shapemanager associated
* with the global krita canvas.
*/
KoShapeManager * shapeManager() const;
/**
* Return the shape manager associated with this canvas
*/
KoShapeManager * globalShapeManager() const;
void updateCanvas(const QRectF& rc);
virtual void updateInputMethodInfo();
const KisCoordinatesConverter* coordinatesConverter() const;
virtual KoViewConverter *viewConverter() const;
virtual QWidget* canvasWidget();
virtual const QWidget* canvasWidget() const;
virtual KoUnit unit() const;
virtual KoToolProxy* toolProxy() const;
const KoColorProfile* monitorProfile();
/**
* Prescale the canvas represention of the image (if necessary, it
* is for QPainter, not for OpenGL).
*/
void preScale();
// FIXME:
// Temporary! Either get the current layer and image from the
// resource provider, or use this, which gets them from the
// current shape selection.
KisImageWSP currentImage() const;
/**
* Filters events and sends them to canvas actions. Shared
* among all the views/canvases
*
* NOTE: May be null while initialization!
*/
KisInputManager* globalInputManager() const;
KisPaintingAssistantsDecorationSP paintingAssistantsDecoration() const;
public: // KisCanvas2 methods
KisImageWSP image() const;
KisViewManager* viewManager() const;
QPointer<KisView> imageView() const;
/// @return true if the canvas image should be displayed in vertically mirrored mode
void addDecoration(KisCanvasDecorationSP deco);
KisCanvasDecorationSP decoration(const QString& id) const;
void setDisplayFilter(QSharedPointer<KisDisplayFilter> displayFilter);
QSharedPointer<KisDisplayFilter> displayFilter() const;
KisDisplayColorConverter *displayColorConverter() const;
KisExposureGammaCorrectionInterface* exposureGammaCorrectionInterface() const;
/**
* @brief setProofingOptions
* set the options for softproofing, without affecting the proofing options as stored inside the image.
*/
void setProofingOptions(bool softProof, bool gamutCheck);
KisProofingConfigurationSP proofingConfiguration() const;
/**
* @brief setProofingConfigUpdated This function is to set whether the proofing config is updated,
* this is needed for determining whether or not to generate a new proofing transform.
* @param updated whether it's updated. Just set it to false in normal usage.
*/
void setProofingConfigUpdated(bool updated);
/**
* @brief proofingConfigUpdated ask the canvas whether or not it updated the proofing config.
* @return whether or not the proofing config is updated, if so, a new proofing transform needs to be made
* in KisOpenGL canvas.
*/bool proofingConfigUpdated();
void setCursor(const QCursor &cursor);
KisAnimationFrameCacheSP frameCache() const;
KisAnimationPlayer *animationPlayer() const;
void refetchDataFromImage();
Q_SIGNALS:
void imageChanged(KisImageWSP image);
void sigCanvasCacheUpdated();
void sigContinueResizeImage(qint32 w, qint32 h);
void documentOffsetUpdateFinished();
// emitted whenever the canvas widget thinks sketch should update
void updateCanvasRequested(const QRect &rc);
public Q_SLOTS:
/// Update the entire canvas area
void updateCanvas();
void startResizingImage();
void finishResizingImage(qint32 w, qint32 h);
/// canvas rotation in degrees
qreal rotationAngle() const;
/// Bools indicating canvasmirroring.
bool xAxisMirrored() const;
bool yAxisMirrored() const;
void slotSoftProofing(bool softProofing);
void slotGamutCheck(bool gamutCheck);
void slotChangeProofingConfig();
+ void slotZoomChanged(int zoom);
void channelSelectionChanged();
/**
* Called whenever the display monitor profile resource changes
*/
void slotSetDisplayProfile(const KoColorProfile *profile);
void startUpdateInPatches(const QRect &imageRect);
private Q_SLOTS:
/// The image projection has changed, now start an update
/// of the canvas representation.
void startUpdateCanvasProjection(const QRect & rc);
void updateCanvasProjection();
/**
* Called whenever the view widget needs to show a different part of
* the document
*
* @param documentOffset the offset in widget pixels
*/
void documentOffsetMoved(const QPoint &documentOffset);
/**
* Called whenever the configuration settings change.
*/
void slotConfigChanged();
void slotSelectionChanged();
void slotDoCanvasUpdate();
void bootstrapFinished();
public:
bool isPopupPaletteVisible() const;
void slotShowPopupPalette(const QPoint& = QPoint(0,0));
// interface for KisCanvasController only
void setWrapAroundViewingMode(bool value);
bool wrapAroundViewingMode() const;
void setLodAllowedInCanvas(bool value);
bool lodAllowedInCanvas() const;
void initializeImage();
void setFavoriteResourceManager(KisFavoriteResourceManager* favoriteResourceManager);
private:
Q_DISABLE_COPY(KisCanvas2)
void connectCurrentCanvas();
void createCanvas(bool useOpenGL);
void createQPainterCanvas();
void createOpenGLCanvas();
void updateCanvasWidgetImpl(const QRect &rc = QRect());
void setCanvasWidget(QWidget *widget);
void resetCanvas(bool useOpenGL);
void notifyLevelOfDetailChange();
// Completes construction of canvas.
// To be called by KisView in its constructor, once it has been setup enough
// (to be defined what that means) for things KisCanvas2 expects from KisView
// TODO: see to avoid that
void setup();
private:
friend class KisView; // calls setup()
class KisCanvas2Private;
KisCanvas2Private * const m_d;
};
#endif
diff --git a/libs/ui/canvas/kis_canvas_widget_base.cpp b/libs/ui/canvas/kis_canvas_widget_base.cpp
index 916a02e296..614dcb4467 100644
--- a/libs/ui/canvas/kis_canvas_widget_base.cpp
+++ b/libs/ui/canvas/kis_canvas_widget_base.cpp
@@ -1,246 +1,248 @@
/*
* Copyright (C) 2007, 2010 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_canvas_widget_base.h"
#include <QImage>
#include <QPainter>
#include <QTimer>
#include <QMenu>
#include <KoShapeManager.h>
#include <KoToolManager.h>
#include <KoViewConverter.h>
#include <KoToolProxy.h>
#include <KoCanvasController.h>
#include <KoShape.h>
#include <KoSelection.h>
#include <KoShapePaintingContext.h>
#include "kis_coordinates_converter.h"
#include "kis_canvas_decoration.h"
#include "kis_config.h"
#include "kis_canvas2.h"
#include "KisViewManager.h"
#include "kis_selection_manager.h"
#include "KisDocument.h"
struct KisCanvasWidgetBase::Private
{
public:
Private(KisCanvas2 *newCanvas, KisCoordinatesConverter *newCoordinatesConverter)
: canvas(newCanvas)
, coordinatesConverter(newCoordinatesConverter)
, viewConverter(newCanvas->viewConverter())
, toolProxy(newCanvas->toolProxy())
, ignorenextMouseEventExceptRightMiddleClick(0)
, borderColor(Qt::gray)
{}
QList<KisCanvasDecorationSP> decorations;
KisCanvas2 * canvas;
KisCoordinatesConverter *coordinatesConverter;
const KoViewConverter * viewConverter;
KoToolProxy * toolProxy;
QTimer blockMouseEvent;
bool ignorenextMouseEventExceptRightMiddleClick; // HACK work around Qt bug not sending tablet right/dblclick http://bugreports.qt.nokia.com/browse/QTBUG-8598
QColor borderColor;
};
KisCanvasWidgetBase::KisCanvasWidgetBase(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter)
: m_d(new Private(canvas, coordinatesConverter))
{
m_d->blockMouseEvent.setSingleShot(true);
}
KisCanvasWidgetBase::~KisCanvasWidgetBase()
{
/**
* Clear all the attached decoration. Oherwise they might decide
* to process some events or signals after the canvas has been
* destroyed
*/
//5qDeleteAll(m_d->decorations);
m_d->decorations.clear();
delete m_d;
}
void KisCanvasWidgetBase::drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const
{
- gc.save();
if (!m_d->canvas) {
dbgFile<<"canvas doesn't exist, in canvas widget base!";
+ return;
}
+ gc.save();
+
// Setup the painter to take care of the offset; all that the
// classes that do painting need to keep track of is resolution
gc.setRenderHint(QPainter::Antialiasing);
gc.setRenderHint(QPainter::TextAntialiasing);
// This option does not do anything anymore with Qt4.6, so don't reenable it since it seems to break display
// http://www.archivum.info/qt-interest@trolltech.com/2010-01/00481/Re:-(Qt-interest)-Is-QPainter::HighQualityAntialiasing-render-hint-broken-in-Qt-4.6.html
// gc.setRenderHint(QPainter::HighQualityAntialiasing);
gc.setRenderHint(QPainter::SmoothPixmapTransform);
gc.save();
gc.setClipRect(updateWidgetRect);
QTransform transform = m_d->coordinatesConverter->flakeToWidgetTransform();
gc.setTransform(transform);
// Paint the shapes (other than the layers)
m_d->canvas->globalShapeManager()->paint(gc, *m_d->viewConverter, false);
// draw green selection outlines around text shapes that are edited, so the user sees where they end
gc.save();
QTransform worldTransform = gc.worldTransform();
gc.setPen( Qt::green );
Q_FOREACH (KoShape *shape, canvas()->shapeManager()->selection()->selectedShapes()) {
if (shape->shapeId() == "ArtisticText" || shape->shapeId() == "TextShapeID") {
gc.setWorldTransform(shape->absoluteTransformation(m_d->viewConverter) * worldTransform);
KoShape::applyConversion(gc, *m_d->viewConverter);
gc.drawRect(QRectF(QPointF(), shape->size()));
}
}
gc.restore();
// Draw text shape over canvas while editing it, that's needs to show the text selection correctly
QString toolId = KoToolManager::instance()->activeToolId();
if (toolId == "ArtisticTextTool" || toolId == "TextTool") {
gc.save();
gc.setPen(Qt::NoPen);
gc.setBrush(Qt::NoBrush);
Q_FOREACH (KoShape *shape, canvas()->shapeManager()->selection()->selectedShapes()) {
if (shape->shapeId() == "ArtisticText" || shape->shapeId() == "TextShapeID") {
KoShapePaintingContext paintContext(canvas(), false);
gc.save();
gc.setTransform(shape->absoluteTransformation(m_d->viewConverter) * gc.transform());
canvas()->shapeManager()->paintShape(shape, gc, *m_d->viewConverter, paintContext);
gc.restore();
}
}
gc.restore();
}
// - some tools do not restore gc, but that is not important here
// - we need to disable clipping to draw handles properly
gc.setClipping(false);
toolProxy()->paint(gc, *m_d->viewConverter);
gc.restore();
// ask the decorations to paint themselves
Q_FOREACH (KisCanvasDecorationSP deco, m_d->decorations) {
deco->paint(gc, m_d->coordinatesConverter->widgetToDocument(updateWidgetRect), m_d->coordinatesConverter,m_d->canvas);
}
gc.restore();
}
void KisCanvasWidgetBase::addDecoration(KisCanvasDecorationSP deco)
{
m_d->decorations.push_back(deco);
}
KisCanvasDecorationSP KisCanvasWidgetBase::decoration(const QString& id) const
{
Q_FOREACH (KisCanvasDecorationSP deco, m_d->decorations) {
if (deco->id() == id) {
return deco;
}
}
return 0;
}
void KisCanvasWidgetBase::setDecorations(const QList<KisCanvasDecorationSP > &decorations)
{
m_d->decorations=decorations;
}
QList<KisCanvasDecorationSP > KisCanvasWidgetBase::decorations() const
{
return m_d->decorations;
}
void KisCanvasWidgetBase::setWrapAroundViewingMode(bool value)
{
Q_UNUSED(value);
}
QImage KisCanvasWidgetBase::createCheckersImage(qint32 checkSize)
{
KisConfig cfg;
if(checkSize < 0)
checkSize = cfg.checkSize();
QColor checkColor1 = cfg.checkersColor1();
QColor checkColor2 = cfg.checkersColor2();
QImage tile(checkSize * 2, checkSize * 2, QImage::Format_RGB32);
QPainter pt(&tile);
pt.fillRect(tile.rect(), checkColor2);
pt.fillRect(0, 0, checkSize, checkSize, checkColor1);
pt.fillRect(checkSize, checkSize, checkSize, checkSize, checkColor1);
pt.end();
return tile;
}
void KisCanvasWidgetBase::notifyConfigChanged()
{
KisConfig cfg;
m_d->borderColor = cfg.canvasBorderColor();
}
QColor KisCanvasWidgetBase::borderColor() const
{
return m_d->borderColor;
}
KisCanvas2 *KisCanvasWidgetBase::canvas() const
{
return m_d->canvas;
}
KisCoordinatesConverter* KisCanvasWidgetBase::coordinatesConverter() const
{
return m_d->coordinatesConverter;
}
KoToolProxy *KisCanvasWidgetBase::toolProxy() const
{
return m_d->toolProxy;
}
QVariant KisCanvasWidgetBase::processInputMethodQuery(Qt::InputMethodQuery query) const
{
if (query == Qt::ImMicroFocus) {
QRectF rect = m_d->toolProxy->inputMethodQuery(query, *m_d->viewConverter).toRectF();
return m_d->coordinatesConverter->flakeToWidget(rect);
}
return m_d->toolProxy->inputMethodQuery(query, *m_d->viewConverter);
}
void KisCanvasWidgetBase::processInputMethodEvent(QInputMethodEvent *event)
{
m_d->toolProxy->inputMethodEvent(event);
}
diff --git a/libs/ui/canvas/kis_grid_config.cpp b/libs/ui/canvas/kis_grid_config.cpp
index 3354404fd1..b97395e71a 100644
--- a/libs/ui/canvas/kis_grid_config.cpp
+++ b/libs/ui/canvas/kis_grid_config.cpp
@@ -1,88 +1,97 @@
/*
* 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_grid_config.h"
#include <QDomElement>
#include "kis_config.h"
#include "kis_dom_utils.h"
struct KisGridConfigStaticRegistrar {
KisGridConfigStaticRegistrar() {
qRegisterMetaType<KisGridConfig>("KisGridConfig");
}
};
static KisGridConfigStaticRegistrar __registrar;
Q_GLOBAL_STATIC(KisGridConfig, staticDefaultObject)
const KisGridConfig& KisGridConfig::defaultGrid()
{
staticDefaultObject->loadStaticData();
return *staticDefaultObject;
}
void KisGridConfig::loadStaticData()
{
KisConfig cfg;
m_lineTypeMain = LineTypeInternal(cfg.getGridMainStyle());
m_lineTypeSubdivision = LineTypeInternal(cfg.getGridSubdivisionStyle());
m_colorMain = cfg.getGridMainColor();
m_colorSubdivision = cfg.getGridSubdivisionColor();
}
void KisGridConfig::saveStaticData() const
{
KisConfig cfg;
cfg.setGridMainStyle(m_lineTypeMain);
cfg.setGridSubdivisionStyle(m_lineTypeSubdivision);
cfg.setGridMainColor(m_colorMain);
cfg.setGridSubdivisionColor(m_colorSubdivision);
}
QDomElement KisGridConfig::saveDynamicDataToXml(QDomDocument& doc, const QString &tag) const
{
QDomElement gridElement = doc.createElement(tag);
KisDomUtils::saveValue(&gridElement, "showGrid", m_showGrid);
KisDomUtils::saveValue(&gridElement, "snapToGrid", m_snapToGrid);
KisDomUtils::saveValue(&gridElement, "offset", m_offset);
KisDomUtils::saveValue(&gridElement, "spacing", m_spacing);
KisDomUtils::saveValue(&gridElement, "offsetAspectLocked", m_offsetAspectLocked);
KisDomUtils::saveValue(&gridElement, "spacingAspectLocked", m_spacingAspectLocked);
KisDomUtils::saveValue(&gridElement, "subdivision", m_subdivision);
+ KisDomUtils::saveValue(&gridElement, "angleLeft", m_angleLeft);
+ KisDomUtils::saveValue(&gridElement, "angleRight", m_angleRight);
+ KisDomUtils::saveValue(&gridElement, "cellSpacing", m_cellSpacing);
+ KisDomUtils::saveValue(&gridElement, "gridType", m_gridType);
+
+
return gridElement;
}
bool KisGridConfig::loadDynamicDataFromXml(const QDomElement &gridElement)
{
bool result = true;
result &= KisDomUtils::loadValue(gridElement, "showGrid", &m_showGrid);
result &= KisDomUtils::loadValue(gridElement, "snapToGrid", &m_snapToGrid);
result &= KisDomUtils::loadValue(gridElement, "offset", &m_offset);
result &= KisDomUtils::loadValue(gridElement, "spacing", &m_spacing);
result &= KisDomUtils::loadValue(gridElement, "offsetAspectLocked", &m_offsetAspectLocked);
result &= KisDomUtils::loadValue(gridElement, "spacingAspectLocked", &m_spacingAspectLocked);
result &= KisDomUtils::loadValue(gridElement, "subdivision", &m_subdivision);
+ result &= KisDomUtils::loadValue(gridElement, "angleLeft", &m_angleLeft);
+ result &= KisDomUtils::loadValue(gridElement, "angleRight", &m_angleRight);
+ result &= KisDomUtils::loadValue(gridElement, "gridType", &m_gridType);
return result;
}
diff --git a/libs/ui/canvas/kis_grid_config.h b/libs/ui/canvas/kis_grid_config.h
index a0fc15920d..80ec47f992 100644
--- a/libs/ui/canvas/kis_grid_config.h
+++ b/libs/ui/canvas/kis_grid_config.h
@@ -1,200 +1,242 @@
/*
* 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_GRID_CONFIG_H
#define __KIS_GRID_CONFIG_H
#include <QPoint>
#include <QColor>
#include <QPen>
#include <boost/operators.hpp>
#include "kritaui_export.h"
class QDomElement;
class QDomDocument;
class KRITAUI_EXPORT KisGridConfig : boost::equality_comparable<KisGridConfig>
{
public:
enum LineTypeInternal {
LINE_SOLID = 0,
LINE_DASHED,
LINE_DOTTED
};
public:
KisGridConfig()
: m_showGrid(false),
m_snapToGrid(false),
m_spacing(20,20),
m_offsetAspectLocked(true),
m_spacingAspectLocked(true),
+ m_angleLeft(45),
+ m_angleRight(45),
+ m_cellSpacing(30),
+ m_gridType(0),
m_subdivision(2),
m_lineTypeMain(LINE_SOLID),
m_lineTypeSubdivision(LINE_DOTTED),
m_colorMain(200, 200, 200, 200),
m_colorSubdivision(200, 200, 200, 150)
{
loadStaticData();
}
bool operator==(const KisGridConfig &rhs) const {
return
m_showGrid == rhs.m_showGrid &&
m_snapToGrid == rhs.m_snapToGrid &&
m_spacing == rhs.m_spacing &&
m_offset == rhs.m_offset &&
m_offsetAspectLocked == rhs.m_offsetAspectLocked &&
m_spacingAspectLocked == rhs.m_spacingAspectLocked &&
+ m_angleRight == rhs.m_angleRight &&
+ m_angleLeft == rhs.m_angleLeft &&
+ m_gridType == rhs.m_gridType &&
+ m_cellSpacing == rhs.m_cellSpacing &&
m_subdivision == rhs.m_subdivision &&
m_lineTypeMain == rhs.m_lineTypeMain &&
m_lineTypeSubdivision == rhs.m_lineTypeSubdivision &&
m_colorMain == rhs.m_colorMain &&
m_colorSubdivision == rhs.m_colorSubdivision;
}
bool showGrid() const {
return m_showGrid;
}
void setShowGrid(bool value) {
m_showGrid = value;
}
bool snapToGrid() const {
return m_snapToGrid;
}
void setSnapToGrid(bool value) {
m_snapToGrid = value;
}
QPoint offset() const {
return m_offset;
}
void setOffset(const QPoint &value) {
m_offset = value;
}
QPoint spacing() const {
return m_spacing;
}
void setSpacing(const QPoint &value) {
m_spacing = value;
}
int subdivision() const {
return m_subdivision;
}
void setSubdivision(int value) {
m_subdivision = value;
}
+ int angleLeft() const {
+ return m_angleLeft;
+ }
+ void setAngleLeft(int angle) {
+ m_angleLeft = angle;
+ }
+
+ int angleRight() const {
+ return m_angleRight;
+ }
+
+ void setAngleRight(int angle) {
+ m_angleRight = angle;
+ }
+
+
+ int cellSpacing() const {
+ return m_cellSpacing;
+ }
+ void setCellSpacing(int spacing) {
+ m_cellSpacing = spacing;
+ }
+
+ int gridType() const {
+ return m_gridType;
+ }
+ void setGridType(int type) {
+ m_gridType = type;
+ }
+
+
bool offsetAspectLocked() const {
return m_offsetAspectLocked;
}
void setOffsetAspectLocked(bool value) {
m_offsetAspectLocked = value;
}
bool spacingAspectLocked() const {
return m_spacingAspectLocked;
}
void setSpacingAspectLocked(bool value) {
m_spacingAspectLocked = value;
}
LineTypeInternal lineTypeMain() const {
return m_lineTypeMain;
}
void setLineTypeMain(LineTypeInternal value) {
m_lineTypeMain = value;
}
LineTypeInternal lineTypeSubdivision() const {
return m_lineTypeSubdivision;
}
void setLineTypeSubdivision(LineTypeInternal value) {
m_lineTypeSubdivision = value;
}
QColor colorMain() const {
return m_colorMain;
}
void setColorMain(const QColor &value) {
m_colorMain = value;
}
QColor colorSubdivision() const {
return m_colorSubdivision;
}
void setColorSubdivision(const QColor &value) {
m_colorSubdivision = value;
}
QPen penMain() const {
return QPen(m_colorMain, 0, toPenStyle(m_lineTypeMain));
}
QPen penSubdivision() const {
return QPen(m_colorSubdivision, 0, toPenStyle(m_lineTypeSubdivision));
}
void loadStaticData();
void saveStaticData() const;
QDomElement saveDynamicDataToXml(QDomDocument& doc, const QString &tag) const;
bool loadDynamicDataFromXml(const QDomElement &parent);
static const KisGridConfig& defaultGrid();
bool isDefault() const {
return *this == defaultGrid();
}
private:
static Qt::PenStyle toPenStyle(LineTypeInternal type) {
return type == LINE_SOLID ? Qt::SolidLine :
type == LINE_DASHED ? Qt::DashLine :
type == LINE_DOTTED ? Qt::DotLine :
Qt::DashDotDotLine;
}
private:
// Dynamic data. Stored in KisDocument.
bool m_showGrid;
bool m_snapToGrid;
-
- QPoint m_offset;
QPoint m_spacing;
-
bool m_offsetAspectLocked;
bool m_spacingAspectLocked;
-
+ int m_angleLeft;
+ int m_angleRight;
+ int m_cellSpacing;
+ int m_gridType; // 0 is rectangle, 1 is isometric
int m_subdivision;
+ QPoint m_offset;
+
+
// Static data. Stored in the Krita config.
LineTypeInternal m_lineTypeMain;
LineTypeInternal m_lineTypeSubdivision;
QColor m_colorMain;
QColor m_colorSubdivision;
};
#endif /* __KIS_GRID_CONFIG_H */
diff --git a/libs/ui/canvas/kis_grid_decoration.cpp b/libs/ui/canvas/kis_grid_decoration.cpp
index 10e95cad71..901a6a3d3b 100644
--- a/libs/ui/canvas/kis_grid_decoration.cpp
+++ b/libs/ui/canvas/kis_grid_decoration.cpp
@@ -1,128 +1,225 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006 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_grid_decoration.h"
#include <QPainter>
#include <QPen>
#include <QtMath>
#include <klocalizedstring.h>
#include <KoUnit.h>
#include "kis_grid_config.h"
#include "kis_coordinates_converter.h"
struct KisGridDecoration::Private
{
KisGridConfig config;
};
KisGridDecoration::KisGridDecoration(KisView* parent)
: KisCanvasDecoration("grid", parent),
m_d(new Private)
{
}
KisGridDecoration::~KisGridDecoration()
{
}
void KisGridDecoration::setGridConfig(const KisGridConfig &config)
{
m_d->config = config;
}
void KisGridDecoration::drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter* converter, KisCanvas2* canvas)
{
if (!m_d->config.showGrid()) return;
Q_UNUSED(canvas);
QTransform transform = converter->imageToWidgetTransform();
const qreal scale = KoUnit::approxTransformScale(transform);
const int minWidgetSize = 3;
const int effectiveSize = qMin(m_d->config.spacing().x(), m_d->config.spacing().y());
int scaleCoeff = 1;
quint32 subdivision = m_d->config.subdivision();
while (qFloor(scale * scaleCoeff * effectiveSize) <= minWidgetSize) {
if (subdivision > 1) {
scaleCoeff = subdivision;
subdivision = 1;
} else {
scaleCoeff *= 2;
}
if (scaleCoeff > 32768) {
qWarning() << "WARNING: Grid Scale Coeff is too high! That is surely a bug!";
return;
}
}
const QPen mainPen = m_d->config.penMain();
const QPen subdivisionPen = m_d->config.penSubdivision();
gc.save();
gc.setTransform(transform);
gc.setRenderHints(QPainter::Antialiasing, false);
gc.setRenderHints(QPainter::HighQualityAntialiasing, false);
qreal x1, y1, x2, y2;
QRectF imageRect =
converter->documentToImage(updateArea) &
converter->imageRectInImagePixels();
imageRect.getCoords(&x1, &y1, &x2, &y2);
- { // vertical lines
- const int offset = m_d->config.offset().x();
- const int step = scaleCoeff * m_d->config.spacing().x();
- const int lineIndexFirst = qCeil((x1 - offset) / step);
- const int lineIndexLast = qFloor((x2 - offset) / step);
- for (int i = lineIndexFirst; i <= lineIndexLast; i++) {
- int w = offset + i * step;
+ // for angles. This will later be a combobox to select different types of options
+ // also add options to hide specific lines (vertical, horizonta, angle 1, etc
+ int gridType = m_d->config.gridType();
+
+ if (gridType == 0) { // rectangle
+
+ {
+ // vertical lines
+ const int offset = m_d->config.offset().x();
+ const int step = scaleCoeff * m_d->config.spacing().x();
+ const int lineIndexFirst = qCeil((x1 - offset) / step);
+ const int lineIndexLast = qFloor((x2 - offset) / step);
+
+ for (int i = lineIndexFirst; i <= lineIndexLast; i++) {
+ int w = offset + i * step;
+
+ gc.setPen(i % subdivision == 0 ? mainPen : subdivisionPen);
+ gc.drawLine(QPointF(w, y1),QPointF(w, y2));
+ }
+ }
+
+ {
+ // horizontal lines
+ const int offset = m_d->config.offset().y();
+ const int step = scaleCoeff * m_d->config.spacing().y();
+ const int lineIndexFirst = qCeil((y1 - offset) / step);
+ const int lineIndexLast = qFloor((y2 - offset) / step);
+
+ for (int i = lineIndexFirst; i <= lineIndexLast; i++) {
+ int w = offset + i * step;
- gc.setPen(i % subdivision == 0 ? mainPen : subdivisionPen);
- gc.drawLine(QPointF(w, y1),QPointF(w, y2));
+ gc.setPen(i % subdivision == 0 ? mainPen : subdivisionPen);
+ gc.drawLine(QPointF(x1, w),QPointF(x2, w));
+ }
}
}
- { // horizontal lines
- const int offset = m_d->config.offset().y();
+ if (gridType== 1) { // isometric
+
+ const int offset = m_d->config.offset().x();
+ const int offsetY = m_d->config.offset().y();
const int step = scaleCoeff * m_d->config.spacing().y();
const int lineIndexFirst = qCeil((y1 - offset) / step);
- const int lineIndexLast = qFloor((y2 - offset) / step);
+ const int cellSpacing = m_d->config.cellSpacing();
+
+ gc.setClipping(true);
+ gc.setClipRect(imageRect, Qt::IntersectClip);
+
+
+
+
+
+ // left angle
+ {
+ int gridXAngle = m_d->config.angleLeft();
+ int counter = lineIndexFirst;
+ int bottomRightOfImageY = y2; // this should be the height of the image
+ int finalY = 0;
+
+
+ // figure out the spacing based off the angle. The spacing needs to be perpendicular to the angle,
+ // so we need to do a bit of trig to get the correct spacing.
+ float correctedAngleSpacing = cellSpacing;
+ if (gridXAngle > 0) {
+ correctedAngleSpacing = cellSpacing / qCos( qDegreesToRadians((float)gridXAngle));
+
+ }
- for (int i = lineIndexFirst; i <= lineIndexLast; i++) {
- int w = offset + i * step;
- gc.setPen(i % subdivision == 0 ? mainPen : subdivisionPen);
- gc.drawLine(QPointF(x1, w),QPointF(x2, w));
+ while (finalY < bottomRightOfImageY) {
+
+ int w = (counter * correctedAngleSpacing) - offsetY;
+ gc.setPen(mainPen);
+
+ // calculate where the ending point will be based off the angle
+ int startingY = w;
+ int horizontalDistance = x2;
+
+ int length2 = qTan( qDegreesToRadians((float)gridXAngle)) * x2; // qTan takes radians, so convert first before sending it
+
+ finalY = startingY - length2;
+ gc.drawLine(QPointF(x1, w),QPointF(horizontalDistance, finalY));
+
+ counter = counter +1;
+ }
}
+
+
+ // right angle (almost the same thing, except starting the lines on the right side)
+ {
+ int gridXAngle = m_d->config.angleRight(); // TODO: add another angle property
+ int counter = lineIndexFirst;
+ int bottomLeftOfImageY = y2;
+ int finalY = 0;
+
+
+ // figure out the spacing based off the angle
+ float correctedAngleSpacing = cellSpacing;
+ if (gridXAngle > 0) {
+ correctedAngleSpacing = cellSpacing / qCos( qDegreesToRadians((float)gridXAngle));
+ }
+
+
+ while (finalY < bottomLeftOfImageY) {
+
+ int w = (counter * correctedAngleSpacing) - offsetY - offset;
+ gc.setPen(mainPen);
+
+ // calculate where the ending point will be based off the angle
+ int startingY = w;
+ int horizontalDistance = x2; // distance is the same (width of the image)
+
+ int length2 = qTan( qDegreesToRadians((float)gridXAngle)) * horizontalDistance; // qTan takes radians, so convert first before sending it
+
+ finalY = startingY - length2;
+ gc.drawLine(QPointF(x2, w),QPointF(0, finalY));
+
+ counter = counter +1;
+ }
+ }
+
+
}
gc.restore();
}
diff --git a/libs/ui/canvas/kis_mirror_axis.cpp b/libs/ui/canvas/kis_mirror_axis.cpp
index 8a0d6ca48a..47d115cde0 100644
--- a/libs/ui/canvas/kis_mirror_axis.cpp
+++ b/libs/ui/canvas/kis_mirror_axis.cpp
@@ -1,406 +1,401 @@
/*
* 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 "kis_mirror_axis.h"
#include "KoConfig.h"
#include <QPainter>
#include <QToolButton>
#include <QApplication>
#include <QPaintEngine>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <kis_icon.h>
#include "kis_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include "KisViewManager.h"
#include "KisView.h"
#include "kis_image.h"
#include "canvas/kis_canvas_controller.h"
#include "input/kis_input_manager.h"
#include "kis_algebra_2d.h"
class KisMirrorAxis::Private
{
public:
Private(KisMirrorAxis* qq)
: q(qq)
, resourceProvider(0)
, mirrorHorizontal(false)
, mirrorVertical(false)
, lockHorizontal(false)
, lockVertical(false)
, hideVerticalDecoration(false)
, hideHorizontalDecoration(false)
, handleSize(32.f)
, xActive(false)
, yActive(false)
, horizontalHandlePosition(64.f)
, verticalHandlePosition(64.f)
, sideMargin(10.f)
, minHandlePosition(10.f + 32.f)
, horizontalContainsCursor(false)
, verticalContainsCursor(false)
, horizontalAxis(QLineF())
, verticalAxis(QLineF())
{ }
void setAxisPosition(float x, float y);
void recomputeVisibleAxes(QRect viewport);
KisMirrorAxis* q;
KisCanvasResourceProvider* resourceProvider;
KisImageWSP image;
bool mirrorHorizontal;
bool mirrorVertical;
bool lockHorizontal;
bool lockVertical;
bool hideVerticalDecoration;
bool hideHorizontalDecoration;
float handleSize;
QPixmap horizontalHandleIcon;
QPixmap verticalHandleIcon;
QPixmap horizontalIcon;
QPixmap verticalIcon;
QPointF axisPosition;
QRectF horizontalHandle;
QRectF verticalHandle;
bool xActive;
bool yActive;
float horizontalHandlePosition;
float verticalHandlePosition;
float sideMargin;
float minHandlePosition;
bool horizontalContainsCursor;
bool verticalContainsCursor;
QLineF horizontalAxis;
QLineF verticalAxis;
};
KisMirrorAxis::KisMirrorAxis(KisCanvasResourceProvider* provider, QPointer<KisView>parent)
: KisCanvasDecoration("mirror_axis", parent)
, d(new Private(this))
{
d->resourceProvider = provider;
connect(d->resourceProvider, SIGNAL(mirrorModeChanged()), SLOT(mirrorModeChanged()));
connect(d->resourceProvider, SIGNAL(moveMirrorVerticalCenter()), SLOT(moveVerticalAxisToCenter()));
connect(d->resourceProvider, SIGNAL(moveMirrorHorizontalCenter()), SLOT(moveHorizontalAxisToCenter()));
d->mirrorHorizontal = d->resourceProvider->mirrorHorizontal();
d->mirrorVertical = d->resourceProvider->mirrorVertical();
d->horizontalIcon = KisIconUtils::loadIcon("mirrorAxis-HorizontalMove").pixmap(d->handleSize, QIcon::Normal, QIcon::On);
d->verticalIcon = KisIconUtils::loadIcon("mirrorAxis-VerticalMove").pixmap(d->handleSize, QIcon::Normal, QIcon::On);
d->horizontalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->handleSize, QIcon::Normal, QIcon::On);
d->verticalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->handleSize, QIcon::Normal, QIcon::On);
setVisible(d->mirrorHorizontal || d->mirrorVertical);
d->image = parent->canvasBase()->image();
-
- int imageWidth = d->image->width();
- int imageHeight = d->image->height();
- QPointF point(imageWidth / 2, imageHeight / 2);
- d->resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::MirrorAxesCenter, point);
}
KisMirrorAxis::~KisMirrorAxis()
{
delete d;
}
float KisMirrorAxis::handleSize() const
{
return d->handleSize;
}
void KisMirrorAxis::setHandleSize(float newSize)
{
if(d->handleSize != newSize) {
d->handleSize = newSize;
d->horizontalIcon = KisIconUtils::loadIcon("symmetry-horyzontal").pixmap(d->handleSize, QIcon::Normal, QIcon::On);
d->verticalIcon = KisIconUtils::loadIcon("symmetry-vertical").pixmap(d->handleSize, QIcon::Normal, QIcon::On);
d->horizontalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->handleSize, QIcon::Normal, QIcon::On);
d->verticalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->handleSize, QIcon::Normal, QIcon::On);
d->minHandlePosition = d->sideMargin + newSize;
emit handleSizeChanged();
}
}
void KisMirrorAxis::drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter* converter, KisCanvas2* canvas)
{
Q_UNUSED(updateArea);
Q_UNUSED(converter);
Q_UNUSED(canvas);
gc.setPen(QPen(QColor(0, 0, 0, 128), 1));
gc.setBrush(Qt::white);
gc.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
QOpenGLContext *ctx = QOpenGLContext::currentContext();
bool hasMultisample = ((gc.paintEngine()->type() == QPaintEngine::OpenGL2) &&
(ctx->hasExtension("GL_ARB_multisample")));
// QPainter cannot anti-alias the edges of circles etc. when using OpenGL
// So instead, use native OpenGL anti-aliasing when available.
if (hasMultisample) {
gc.beginNativePainting();
ctx->functions()->glEnable(GL_MULTISAMPLE);
gc.endNativePainting();
}
float halfHandleSize = d->handleSize / 2;
d->recomputeVisibleAxes(gc.viewport());
if(d->mirrorHorizontal && !d->hideHorizontalDecoration) {
if (!d->horizontalAxis.isNull()) {
// QPointF horizontalIndicatorCenter = d->horizontalAxis.unitVector().pointAt(15);
// QRectF horizontalIndicator = QRectF(horizontalIndicatorCenter.x() - halfHandleSize, horizontalIndicatorCenter.y() - halfHandleSize, d->handleSize, d->handleSize);
float horizontalHandlePosition = qBound<float>(d->minHandlePosition, d->horizontalHandlePosition, d->horizontalAxis.length() - d->minHandlePosition);
QPointF horizontalHandleCenter = d->horizontalAxis.unitVector().pointAt(horizontalHandlePosition);
d->horizontalHandle = QRectF(horizontalHandleCenter.x() - halfHandleSize, horizontalHandleCenter.y() - halfHandleSize, d->handleSize, d->handleSize);
gc.setPen(QPen(QColor(0, 0, 0, 64), 2, Qt::DashDotDotLine, Qt::RoundCap, Qt::RoundJoin));
gc.drawLine(d->horizontalAxis);
// gc.drawEllipse(horizontalIndicator);
// gc.drawPixmap(horizontalIndicator.adjusted(5, 5, -5, -5).toRect(), d->horizontalIcon);
// don't draw the handles if we are locking the axis for movement
if (!d->lockHorizontal) {
gc.setPen(QPen(QColor(0, 0, 0, 128), 2));
gc.drawEllipse(d->horizontalHandle);
gc.drawPixmap(d->horizontalHandle.adjusted(5, 5, -5, -5).toRect(), d->horizontalIcon);
}
} else {
d->horizontalHandle = QRectF();
}
}
if(d->mirrorVertical && !d->hideVerticalDecoration) {
if (!d->verticalAxis.isNull()) {
gc.setPen(QPen(QColor(0, 0, 0, 64), 2, Qt::DashDotDotLine, Qt::RoundCap, Qt::RoundJoin));
gc.drawLine(d->verticalAxis);
// QPointF verticalIndicatorCenter = d->verticalAxis.unitVector().pointAt(15);
// QRectF verticalIndicator = QRectF(verticalIndicatorCenter.x() - halfHandleSize, verticalIndicatorCenter.y() - halfHandleSize, d->handleSize, d->handleSize);
float verticalHandlePosition = qBound<float>(d->minHandlePosition, d->verticalHandlePosition, d->verticalAxis.length() - d->minHandlePosition);
QPointF verticalHandleCenter = d->verticalAxis.unitVector().pointAt(verticalHandlePosition);
d->verticalHandle = QRectF(verticalHandleCenter.x() - halfHandleSize, verticalHandleCenter.y() - halfHandleSize, d->handleSize, d->handleSize);
// don't draw the handles if we are locking the axis for movement
if (!d->lockVertical) {
gc.setPen(QPen(QColor(0, 0, 0, 128), 2));
gc.drawEllipse(d->verticalHandle);
gc.drawPixmap(d->verticalHandle.adjusted(5, 5, -5, -5).toRect(), d->verticalIcon);
}
} else {
d->verticalHandle = QRectF();
}
}
if (hasMultisample) {
gc.beginNativePainting();
ctx->functions()->glDisable(GL_MULTISAMPLE);
gc.endNativePainting();
}
}
bool KisMirrorAxis::eventFilter(QObject* target, QEvent* event)
{
if (!visible()) return false;
QObject *expectedCanvasWidget = view() ?
view()->canvasBase()->canvasWidget() : 0;
if (!expectedCanvasWidget || target != expectedCanvasWidget) return false;
if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::TabletPress) {
QMouseEvent *me = dynamic_cast<QMouseEvent*>(event);
QTabletEvent *te = dynamic_cast<QTabletEvent*>(event);
QPoint pos = me ? me->pos() : (te ? te->pos() : QPoint(77,77));
if(d->mirrorHorizontal && d->horizontalHandle.contains(pos) && !d->lockHorizontal && !d->hideHorizontalDecoration ) {
d->xActive = true;
QApplication::setOverrideCursor(Qt::ClosedHandCursor);
event->accept();
return true;
}
if(d->mirrorVertical && d->verticalHandle.contains(pos) && !d->lockVertical && !d->hideVerticalDecoration) {
d->yActive = true;
QApplication::setOverrideCursor(Qt::ClosedHandCursor);
event->accept();
return true;
}
}
if(event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove) {
QMouseEvent *me = dynamic_cast<QMouseEvent*>(event);
QTabletEvent *te = dynamic_cast<QTabletEvent*>(event);
QPoint pos = me ? me->pos() : (te ? te->pos() : QPoint(77,77));
if(d->xActive) {
float axisX = view()->viewConverter()->widgetToImage<QPoint>(pos).x();
d->setAxisPosition(axisX, d->axisPosition.y());
d->horizontalHandlePosition = KisAlgebra2D::dotProduct<QPointF>(pos - d->horizontalAxis.p1(), d->horizontalAxis.unitVector().p2() - d->horizontalAxis.p1());
event->accept();
return true;
}
if(d->yActive) {
float axisY = view()->viewConverter()->widgetToImage<QPoint>(pos).y();
d->setAxisPosition(d->axisPosition.x(), axisY);
d->verticalHandlePosition = KisAlgebra2D::dotProduct<QPointF>(pos - d->verticalAxis.p1(), d->verticalAxis.unitVector().p2() - d->verticalAxis.p1());
event->accept();
return true;
}
if(d->mirrorHorizontal && !d->hideHorizontalDecoration) {
if(d->horizontalHandle.contains(pos) && !d->lockHorizontal) {
if(!d->horizontalContainsCursor) {
QApplication::setOverrideCursor(Qt::OpenHandCursor);
d->horizontalContainsCursor = true;
}
} else if(d->horizontalContainsCursor) {
QApplication::restoreOverrideCursor();
d->horizontalContainsCursor = false;
}
}
if(d->mirrorVertical && !d->hideVerticalDecoration) {
if(d->verticalHandle.contains(pos) && !d->lockVertical) {
if(!d->verticalContainsCursor) {
QApplication::setOverrideCursor(Qt::OpenHandCursor);
d->verticalContainsCursor = true;
}
} else if(d->verticalContainsCursor) {
QApplication::restoreOverrideCursor();
d->verticalContainsCursor = false;
}
}
}
if(event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::TabletRelease) {
if(d->xActive) {
QApplication::restoreOverrideCursor();
d->xActive = false;
event->accept();
return true;
}
if(d->yActive) {
QApplication::restoreOverrideCursor();
d->yActive = false;
event->accept();
return true;
}
}
return QObject::eventFilter(target, event);
}
void KisMirrorAxis::mirrorModeChanged()
{
d->mirrorHorizontal = d->resourceProvider->mirrorHorizontal();
d->mirrorVertical = d->resourceProvider->mirrorVertical();
d->lockHorizontal = d->resourceProvider->mirrorHorizontalLock();
d->lockVertical = d->resourceProvider->mirrorVerticalLock();
d->hideHorizontalDecoration = d->resourceProvider->mirrorHorizontalHideDecorations();
d->hideVerticalDecoration = d->resourceProvider->mirrorVerticalHideDecorations();
setVisible(d->mirrorHorizontal || d->mirrorVertical);
}
void KisMirrorAxis::setVisible(bool v)
{
KisCanvasDecoration::setVisible(v);
KisInputManager *inputManager = view() ? view()->canvasBase()->globalInputManager() : 0;
if (!inputManager) return;
if (v) {
inputManager->attachPriorityEventFilter(this);
} else {
inputManager->detachPriorityEventFilter(this);
}
}
void KisMirrorAxis::moveHorizontalAxisToCenter()
{
d->setAxisPosition(d->image->width()/2, d->axisPosition.y());
}
void KisMirrorAxis::moveVerticalAxisToCenter()
{
d->setAxisPosition(d->axisPosition.x(), d->image->height()/2 );
}
-
void KisMirrorAxis::Private::setAxisPosition(float x, float y)
{
QPointF newPosition = QPointF(x, y);
- resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::MirrorAxesCenter, newPosition);
+ const QPointF relativePosition = KisAlgebra2D::absoluteToRelative(newPosition, image->bounds());
+ image->setMirrorAxesCenter(relativePosition);
q->view()->canvasBase()->updateCanvas();
}
void KisMirrorAxis::Private::recomputeVisibleAxes(QRect viewport)
{
KisCoordinatesConverter *converter = q->view()->viewConverter();
- axisPosition = resourceProvider->resourceManager()->resource(KisCanvasResourceProvider::MirrorAxesCenter).toPointF();
+ axisPosition = KisAlgebra2D::relativeToAbsolute(image->mirrorAxesCenter(), image->bounds());
QPointF samplePt1 = converter->imageToWidget<QPointF>(axisPosition);
QPointF samplePt2 = converter->imageToWidget<QPointF>(QPointF(axisPosition.x(), axisPosition.y() - 100));
horizontalAxis = QLineF(samplePt1, samplePt2);
if (!KisAlgebra2D::intersectLineRect(horizontalAxis, viewport)) horizontalAxis = QLineF();
samplePt2 = converter->imageToWidget<QPointF>(QPointF(axisPosition.x() - 100, axisPosition.y()));
verticalAxis = QLineF(samplePt1, samplePt2);
if (!KisAlgebra2D::intersectLineRect(verticalAxis, viewport)) verticalAxis = QLineF();
}
diff --git a/libs/ui/canvas/kis_qpainter_canvas.cpp b/libs/ui/canvas/kis_qpainter_canvas.cpp
index fde4b6739f..387aeb2c76 100644
--- a/libs/ui/canvas/kis_qpainter_canvas.cpp
+++ b/libs/ui/canvas/kis_qpainter_canvas.cpp
@@ -1,261 +1,261 @@
/*
* 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_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;
QImage buffer;
};
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);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
setAttribute(Qt::WA_AcceptTouchEvents, false);
#else
setAttribute(Qt::WA_AcceptTouchEvents, true);
#endif
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_d->buffer.size() != size()) {
m_d->buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
}
QPainter gc(&m_d->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();
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);
drawImage(gc, ev->rect());
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_d->buffer, ev->rect());
}
void KisQPainterCanvas::drawImage(QPainter & gc, const QRect &updateWidgetRect) const
{
KisCoordinatesConverter *converter = coordinatesConverter();
QTransform imageTransform = converter->viewportToWidgetTransform();
gc.setTransform(imageTransform);
gc.setRenderHint(QPainter::SmoothPixmapTransform, true);
QRectF viewportRect = converter->widgetToViewport(updateWidgetRect);
gc.setCompositionMode(QPainter::CompositionMode_SourceOver);
gc.drawImage(viewportRect, m_d->prescaledProjection->prescaledQImage(),
viewportRect);
}
QVariant KisQPainterCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
{
return processInputMethodQuery(query);
}
void KisQPainterCanvas::inputMethodEvent(QInputMethodEvent *event)
{
processInputMethodEvent(event);
}
void KisQPainterCanvas::channelSelectionChanged(const 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(QSharedPointer<KisDisplayFilter> displayFilter)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setDisplayFilter(displayFilter);
canvas()->startUpdateInPatches(canvas()->image()->bounds());
}
void KisQPainterCanvas::setWrapAroundViewingMode(bool value)
{
Q_UNUSED(value);
dbgKrita << "Wrap around viewing mode not implemented in QPainter Canvas.";
return;
}
void KisQPainterCanvas::finishResizingImage(qint32 w, qint32 h)
{
m_d->prescaledProjection->slotImageSizeChanged(w, h);
}
KisUpdateInfoSP KisQPainterCanvas::startUpdateCanvasProjection(const QRect & rc, const 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/libs/ui/canvas/kis_tool_proxy.cpp b/libs/ui/canvas/kis_tool_proxy.cpp
index 9560d52462..255511c2ae 100644
--- a/libs/ui/canvas/kis_tool_proxy.cpp
+++ b/libs/ui/canvas/kis_tool_proxy.cpp
@@ -1,244 +1,248 @@
/*
* 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 - QPointF(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::forwardHoverEvent(QEvent *event)
{
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)
{
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 = widgetToDocument(tabletEvent->posF());
tabletEvent->accept();
this->tabletEvent(tabletEvent, docPoint);
forwardToTool(state, action, tabletEvent, docPoint);
retval = tabletEvent->isAccepted();
- } else if (touchEvent) {
+ }
+ 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) {
+ }
+ else if (mouseEvent) {
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) {
+ }
+ else if (event && event->type() == QEvent::KeyPress) {
QKeyEvent* kevent = static_cast<QKeyEvent*>(event);
keyPressEvent(kevent);
- } else if(event->type() == QEvent::KeyRelease) {
+ }
+ else if (event && 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/libs/ui/dialogs/kis_about_application.cpp b/libs/ui/dialogs/kis_about_application.cpp
index 3728a860ae..ef6a15cd34 100644
--- a/libs/ui/dialogs/kis_about_application.cpp
+++ b/libs/ui/dialogs/kis_about_application.cpp
@@ -1,172 +1,172 @@
/*
* 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 <kis_debug.h>
#include <QStandardPaths>
#include <QTabWidget>
#include <QLabel>
#include <QTextEdit>
#include <QString>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QApplication>
#include <QFile>
#include <klocalizedstring.h>
#include "../../krita/data/splash/splash_screen.xpm"
#include "kis_splash_screen.h"
KisAboutApplication::KisAboutApplication(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(qApp->applicationVersion(), QPixmap(splash_screen_xpm), true);
splash->setWindowFlags(Qt::Widget);
splash->displayLinks();
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>");
QFile fileDevelopers(":/developers.txt");
Q_ASSERT(fileDevelopers.exists());
fileDevelopers.open(QIODevice::ReadOnly);
Q_FOREACH (const QByteArray &author, fileDevelopers.readAll().split('\n')) {
authors.append(QString::fromUtf8(author));
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>");
QFile fileBackers(":/backers.txt");
Q_ASSERT(fileBackers.exists());
fileBackers.open(QIODevice::ReadOnly);
Q_FOREACH (const QByteArray &backer, fileBackers.readAll().split('\n')) {
backers.append(QString::fromUtf8(backer));
backers.append(", ");
}
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>");
QFile fileCredits(":/credits.txt");
Q_ASSERT(fileCredits.exists());
fileCredits.open(QIODevice::ReadOnly);
Q_FOREACH (const QString &credit, QString::fromUtf8(fileCredits.readAll()).split('\n', QString::SkipEmptyParts)) {
if (credit.contains(":")) {
QList<QString> creditSplit = credit.split(':');
credits.append(creditSplit.at(0));
credits.append(" (<i>" + creditSplit.at(1) + "</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>");
QFile licenseFile(":/LICENSE");
Q_ASSERT(licenseFile.exists());
licenseFile.open(QIODevice::ReadOnly);
QByteArray ba = licenseFile.readAll();
license.append(QString::fromUtf8(ba));
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(10);
hlayout->addStretch(10);
hlayout->addWidget(bnClose);
vlayout->addLayout(hlayout);
+
}
diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc
index ef0e756426..452c1f4b69 100644
--- a/libs/ui/dialogs/kis_dlg_preferences.cc
+++ b/libs/ui/dialogs/kis_dlg_preferences.cc
@@ -1,1052 +1,1052 @@
/*
* 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 <KisDocument.h>
#include <KoColorProfile.h>
#include <KisApplication.h>
#include <KoFileDialog.h>
#include <KisPart.h>
#include <KoColorSpaceEngine.h>
#include <kis_icon.h>
#include <KoConfig.h>
#include "KoID.h"
#include <KoConfigAuthorPage.h>
#include <KoVBox.h>
#include <klocalizedstring.h>
#include <kundo2stack.h>
#include <KoResourcePaths.h>
#include "kis_action_registry.h"
#include "widgets/squeezedcombobox.h"
#include "kis_clipboard.h"
#include "widgets/kis_cmb_idlist.h"
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "KoColorConversionTransformation.h"
#include "kis_cursor.h"
#include "kis_config.h"
#include "kis_canvas_resource_provider.h"
#include "kis_preference_set_registry.h"
#include "kis_color_manager.h"
#include "KisProofingConfiguration.h"
#include "kis_image_config.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_cmbCursorShape->addItem(i18n("Black Pixel"));
m_cmbCursorShape->addItem(i18n("White Pixel"));
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());
KoColor mdiColor;
mdiColor.fromQColor(cfg.getMDIBackgroundColor());
m_mdiColor->setColor(mdiColor);
m_backgroundimage->setText(cfg.getMDIBackgroundImage());
m_chkCanvasMessages->setChecked(cfg.showCanvasMessages());
m_chkCompressKra->setChecked(cfg.compressKra());
m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker());
m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt());
m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport());
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));
KoColor mdiColor;
mdiColor.fromQColor(cfg.getMDIBackgroundColor(true));
m_mdiColor->setColor(mdiColor);
m_backgroundimage->setText(cfg.getMDIBackgroundImage(true));
m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true));
m_chkCompressKra->setChecked(cfg.compressKra(true));
m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true));
m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true));
m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport(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();
}
bool GeneralTab::switchSelectionCtrlAlt()
{
return m_chkSwitchSelectionCtrlAlt->isChecked();
}
bool GeneralTab::convertToImageColorspaceOnImport()
{
return m_chkConvertOnImport->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.filename();
// 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("");
}
ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name)
: QWidget(parent)
{
setObjectName(name);
QGridLayout * l = new QGridLayout(this);
l->setMargin(0);
m_page = new WdgShortcutSettings(this);
l->addWidget(m_page, 0, 0);
KisPart::instance()->loadActions();
KisActionRegistry::instance()->setupDialog(m_page);
}
void ShortcutSettingsTab::setDefault()
{
m_page->allDefault();
}
void ShortcutSettingsTab::saveChanges()
{
m_page->save();
KisActionRegistry::instance()->settingsPageSaved();
}
void ShortcutSettingsTab::revertChanges()
{
m_page->allDefault();
}
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)));
if (KisColorManager::instance()->devices().size() > 0) {
m_page->chkUseSystemMonitorProfile->setVisible(false);
}
m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys());
m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace());
m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open"));
m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") );
connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile()));
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));
}
}
m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation());
m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization());
KisImageConfig cfgImage;
KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration();
m_page->sldAdaptationState->setMaximum(20);
m_page->sldAdaptationState->setMinimum(0);
m_page->sldAdaptationState->setValue((int)proofingConfig->adaptationState*20);
const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,proofingConfig->proofingDepth,proofingConfig->proofingProfile);
m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace);
m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent);
m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation));
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());
}
void ColorSettingsTab::installProfile()
{
KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC");
dialog.setCaption(i18n("Install Color Profiles"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile");
QStringList profileNames = dialog.filenames();
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
Q_ASSERT(iccEngine);
QString saveLocation = KoResourcePaths::saveLocation("icc_profiles");
Q_FOREACH (const QString &profileName, profileNames) {
QUrl file(profileName);
if (!QFile::copy(profileName, saveLocation + file.fileName())) {
dbgKrita << "Could not install profile!";
return;
}
iccEngine->addProfile(saveLocation + file.fileName());
}
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::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]);
Q_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");
refillMonitorProfiles(KoID("RGBA", ""));
KisConfig cfg;
KisImageConfig cfgImage;
KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration();
const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,proofingConfig->proofingDepth,proofingConfig->proofingProfile);
m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace);
m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent);
m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation));
m_page->sldAdaptationState->setValue(0);
//probably this should become the screenprofile?
KoColor ga(KoColorSpaceRegistry::instance()->rgb8());
ga.fromKoColor(proofingConfig->warningColor);
m_page->gamutAlarm->setColor(ga);
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);
Q_FOREACH (const KoColorProfile *profile, profileList) {
// //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 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_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(i18n(" %"));
sliderMemoryLimit->setRange(1, 100, 2);
sliderMemoryLimit->setSingleStep(0.01);
sliderPoolLimit->setSuffix(i18n(" %"));
sliderPoolLimit->setRange(0, 20, 2);
sliderMemoryLimit->setSingleStep(0.01);
sliderUndoLimit->setSuffix(i18n(" %"));
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,
std::bind(&KisIntParseSpinBox::value,
intMemoryLimit));
connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged()));
sync2->slotParentValueChanged();
m_syncs << sync2;
SliderAndSpinBoxSync *sync3 =
new SliderAndSpinBoxSync(sliderUndoLimit,
intUndoLimit,
std::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));
{
KisConfig cfg2;
chkOpenGLLogging->setChecked(cfg2.enableOpenGLDebugging(requestDefault));
chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(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());
{
KisConfig cfg2;
cfg2.setEnableOpenGLDebugging(chkOpenGLLogging->isChecked());
cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked());
}
}
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"
DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name)
: WdgDisplaySettings(parent, name)
{
KisConfig cfg;
if (!KisOpenGL::hasOpenGL()) {
grpOpenGL->setEnabled(false);
grpOpenGL->setChecked(false);
chkUseTextureBuffer->setEnabled(false);
chkDisableVsync->setEnabled(false);
cmbFilterMode->setEnabled(false);
} else {
grpOpenGL->setEnabled(true);
grpOpenGL->setChecked(cfg.useOpenGL());
chkUseTextureBuffer->setEnabled(cfg.useOpenGL());
chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer());
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::hasOpenGL3()) {
cmbFilterMode->removeItem(3);
}
}
if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") {
grpOpenGL->setVisible(false);
grpOpenGL->setMaximumHeight(0);
}
KoColor c;
c.fromQColor(cfg.selectionOverlayMaskColor());
c.setOpacity(1.0);
btnSelectionOverlayColor->setColor(c);
sldSelectionOverlayOpacity->setRange(0.0, 1.0, 2);
sldSelectionOverlayOpacity->setSingleStep(0.05);
sldSelectionOverlayOpacity->setValue(cfg.selectionOverlayMaskColor().alphaF());
intCheckSize->setValue(cfg.checkSize());
chkMoving->setChecked(cfg.scrollCheckers());
KoColor ck1(KoColorSpaceRegistry::instance()->rgb8());
ck1.fromQColor(cfg.checkersColor1());
colorChecks1->setColor(ck1);
KoColor ck2(KoColorSpaceRegistry::instance()->rgb8());
ck2.fromQColor(cfg.checkersColor2());
colorChecks2->setColor(ck2);
KoColor cb(KoColorSpaceRegistry::instance()->rgb8());
cb.fromQColor(cfg.canvasBorderColor());
canvasBorder->setColor(cb);
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;
if (!KisOpenGL::hasOpenGL()) {
grpOpenGL->setEnabled(false);
grpOpenGL->setChecked(false);
chkUseTextureBuffer->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);
chkDisableVsync->setEnabled(true);
chkDisableVsync->setChecked(cfg.disableVSync(true));
cmbFilterMode->setEnabled(true);
cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true));
}
chkMoving->setChecked(cfg.scrollCheckers(true));
intCheckSize->setValue(cfg.checkSize(true));
KoColor ck1(KoColorSpaceRegistry::instance()->rgb8());
ck1.fromQColor(cfg.checkersColor1(true));
colorChecks1->setColor(ck1);
KoColor ck2(KoColorSpaceRegistry::instance()->rgb8());
ck2.fromQColor(cfg.checkersColor2(true));
colorChecks2->setColor(ck2);
KoColor cvb(KoColorSpaceRegistry::instance()->rgb8());
cvb.fromQColor(cfg.canvasBorderColor(true));
canvasBorder->setColor(cvb);
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)
{
chkUseTextureBuffer->setEnabled(isChecked);
chkDisableVsync->setEnabled(isChecked);
cmbFilterMode->setEnabled(isChecked);
}
//---------------------------------------------------------------------------------------------------
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"));
setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults);
button(QDialogButtonBox::Ok)->setDefault(true);
setFaceType(KPageDialog::List);
// General
KoVBox *vbox = new KoVBox();
KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General"));
page->setObjectName("general");
page->setHeader(i18n("General"));
page->setIcon(KisIconUtils::loadIcon("go-home"));
addPage(page);
m_general = new GeneralTab(vbox);
// Shortcuts
vbox = new KoVBox();
page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts"));
page->setObjectName("shortcuts");
page->setHeader(i18n("Shortcuts"));
page->setIcon(KisIconUtils::loadIcon("document-export"));
addPage(page);
m_shortcutSettings = new ShortcutSettingsTab(vbox);
connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges()));
// Canvas 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("configure"));
// Display
vbox = new KoVBox();
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 KoVBox();
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 KoVBox();
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);
// Tablet
vbox = new KoVBox();
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 KoVBox();
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"));
QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults);
connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges()));
connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges()));
KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance();
Q_FOREACH (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) {
KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet();
vbox = new KoVBox();
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() == "general") {
m_general->setDefault();
}
else if (currentPage()->objectName() == "shortcuts") {
m_shortcutSettings->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() == "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().toQColor());
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());
cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt());
cfg.setConvertToImageColorspaceOnImport(dialog->m_general->convertToImageColorspaceOnImport());
KisPart *part = KisPart::instance();
if (part) {
Q_FOREACH (QPointer<KisDocument> doc, part->documents()) {
if (doc) {
- doc->setAutoSave(dialog->m_general->autoSaveInterval());
+ doc->setAutoSaveDelay(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());
KisImageConfig cfgImage;
cfgImage.setDefaultProofingConfig(dialog->m_colorSettings->m_page->proofingSpaceSelector->currentColorSpace(), dialog->m_colorSettings->m_page->cmbProofingIntent->currentIndex(), dialog->m_colorSettings->m_page->ckbProofBlackPoint->isChecked(), dialog->m_colorSettings->m_page->gamutAlarm->color(), (double)dialog->m_colorSettings->m_page->sldAdaptationState->value()/20);
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();
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.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked());
cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value());
cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked());
cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color().toQColor());
cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color().toQColor());
cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color().toQColor());
cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked());
KoColor c = dialog->m_displaySettings->btnSelectionOverlayColor->color();
c.setOpacity(dialog->m_displaySettings->sldSelectionOverlayOpacity->value());
cfg.setSelectionOverlayMaskColor(c.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());
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/libs/ui/dialogs/kis_dlg_stroke_selection_properties.cpp b/libs/ui/dialogs/kis_dlg_stroke_selection_properties.cpp
index 60ecdff634..c27203cd9a 100644
--- a/libs/ui/dialogs/kis_dlg_stroke_selection_properties.cpp
+++ b/libs/ui/dialogs/kis_dlg_stroke_selection_properties.cpp
@@ -1,397 +1,397 @@
/*
* Copyright (c) 2016 Kapustin Alexey <akapust1n@yandex.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_stroke_selection_properties.h"
#include <QPushButton>
#include <QRadioButton>
#include <QLayout>
#include <QLabel>
#include <QSpinBox>
#include <QSlider>
#include <QCheckBox>
#include <QPlainTextEdit>
#include <QTextEdit>
#include <klocalizedstring.h>
#include <KoColorSpace.h>
#include "KoColorProfile.h"
#include "KoColorSpaceRegistry.h"
#include "KoColor.h"
#include "KoColorConversionTransformation.h"
#include "KoColorPopupAction.h"
#include "kis_icon_utils.h"
#include "KoID.h"
#include "kis_image.h"
#include "kis_annotation.h"
#include "kis_config.h"
#include "kis_signal_compressor.h"
#include "widgets/kis_cmb_idlist.h"
#include "widgets/squeezedcombobox.h"
#include "kis_layer_utils.h"
#include <kis_ls_utils.h>
#include "kis_canvas_resource_provider.h"
#include "KoUnit.h"
#include "kis_display_color_converter.h"
KisDlgStrokeSelection::KisDlgStrokeSelection(KisImageWSP image, KisViewManager *view, bool isVectorLayer)
: KoDialog(view->mainWindow())
{
m_resourceManager = view->mainWindow()->resourceManager();
converter = view->canvasBase()->displayColorConverter();
setButtons(Ok | Cancel);
setDefaultButton(Ok);
setCaption(i18n("Stroke selection properties"));
m_page = new WdgStrokeSelection(this);
m_image = image;
setMainWidget(m_page);
resize(m_page->sizeHint());
QString filterConfig = KisConfig().exportConfiguration("StrokeSelection");
- KisPropertiesConfiguration cfg;
- cfg.fromXML(filterConfig);
+ KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
+ cfg->fromXML(filterConfig);
auto &m_options = m_page->m_options;
- m_options.color = cfg.getColor("color");
- m_options.lineColorSource = cfg.getInt("lineColorSource");
+ m_options.color = cfg->getColor("color");
+ m_options.lineColorSource = cfg->getInt("lineColorSource");
m_page->lineColorBox->setCurrentIndex(m_options.lineColorSource);
m_page->colorSelector->setColor(getSelectedColor().toQColor());
- m_options.brushSelected = cfg.getBool("useBrush", 0);
+ m_options.brushSelected = cfg->getBool("useBrush", 0);
m_page->typeBox->setCurrentIndex(m_options.brushSelected? 0 : 1);
- m_options._colorFillSource = cfg.getInt("colorFillSource", 0);
+ m_options._colorFillSource = cfg->getInt("colorFillSource", 0);
m_page->fillBox->setCurrentIndex(m_options._colorFillSource);
- m_options.customColor = cfg.getColor("customColor");
+ m_options.customColor = cfg->getColor("customColor");
if (m_options._colorFillSource == static_cast<int>(colorFillSource::CustomColor)) {
m_page->colorFillSelector->setColor(m_options.customColor.toQColor());
}
else {
m_page->colorFillSelector->setColor(getFillSelectedColor().toQColor());
}
- m_options.fillColor = cfg.getColor("fillColor");
+ m_options.fillColor = cfg->getColor("fillColor");
if (m_options._colorFillSource == static_cast<int>(colorFillSource::None)) {
m_page->colorFillSelector->setDisabled(true);
}
else {
m_page->colorFillSelector->setDisabled(false); }
- m_options.lineSize = cfg.getInt("lineSize", 1);
+ m_options.lineSize = cfg->getInt("lineSize", 1);
m_page->lineSize->setValue(m_options.lineSize);
if (m_options.brushSelected) {
m_page->lineSize->setDisabled(true);
m_page->fillBox->setDisabled(true);
m_page->colorFillSelector->setDisabled(true);
m_page->sizeBox->setDisabled(true);
}
- m_options.lineDimension = cfg.getInt("lineDimension", 0);
+ m_options.lineDimension = cfg->getInt("lineDimension", 0);
m_page->sizeBox->setCurrentIndex(m_options.lineDimension);
connect(m_page, SIGNAL(colorSelectorChanged()), SLOT(setColorButton()));
connect(m_page, SIGNAL(colorFillSelectorChanged()), SLOT(setColorFillButton()));
connect(m_page->colorFillSelector, SIGNAL(changed(const QColor&)), SLOT(colorFillChanged(const QColor&)));
connect(m_page->colorSelector, SIGNAL(changed(const QColor&)), SLOT(colorChanged(const QColor&)));
if (isVectorLayer) {
lockVectorLayerFunctions();
}
}
KisDlgStrokeSelection::~KisDlgStrokeSelection()
{
auto &m_options = m_page->m_options;
m_options.lineSize = m_page->lineSize->value();
m_options.lineDimension = m_page->sizeBox->currentIndex();
m_options.lineColorSource = m_page->lineColorBox->currentIndex();
- KisPropertiesConfiguration cfg;
- cfg.setProperty("lineSize", m_options.lineSize);
- cfg.setProperty("colorFillSource", m_options._colorFillSource);
- cfg.setProperty("useBrush", m_options.brushSelected);
- cfg.setProperty("lineDimension", m_options.lineDimension);
- cfg.setProperty("lineColorSource", m_options.lineColorSource);
+ KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
+ cfg->setProperty("lineSize", m_options.lineSize);
+ cfg->setProperty("colorFillSource", m_options._colorFillSource);
+ cfg->setProperty("useBrush", m_options.brushSelected);
+ cfg->setProperty("lineDimension", m_options.lineDimension);
+ cfg->setProperty("lineColorSource", m_options.lineColorSource);
QVariant colorVariant("KoColor");
colorVariant.setValue(m_options.customColor);
- cfg.setProperty("customColor", colorVariant);
+ cfg->setProperty("customColor", colorVariant);
QVariant _colorVariant("KoColor");
_colorVariant.setValue(m_options.color);
- cfg.setProperty("color", _colorVariant);
+ cfg->setProperty("color", _colorVariant);
QVariant _cVariant("KoColor");
_cVariant.setValue(m_options.fillColor);
- cfg.setProperty("fillColor", _cVariant);
+ cfg->setProperty("fillColor", _cVariant);
KisConfig().setExportConfiguration("StrokeSelection", cfg);
delete m_page;
}
KoColor KisDlgStrokeSelection::getSelectedColor() const
{
KoColor color;
QString currentSource = m_page->lineColorBox->currentText();
if (currentSource == "Foreground color") {
color = m_resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value<KoColor>();
}
else if (currentSource == "Background color") {
color = m_resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value<KoColor>();
}
else {
color = m_page->m_options.color;
}
return color;
}
KoColor KisDlgStrokeSelection::getFillSelectedColor() const
{
KoColor color;
colorFillSource currentSource = static_cast<colorFillSource>(m_page->fillBox->currentIndex());
if (currentSource == colorFillSource::FGColor) {
color = m_resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value<KoColor>();
}
else if (currentSource == colorFillSource::BGColor) {
color = m_resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value<KoColor>();
}
else if (currentSource == colorFillSource::PaintColor) {
color = converter->approximateFromRenderedQColor(m_page->colorSelector->color());
}
else {
color = m_page->m_options.customColor;
}
return color;
}
bool KisDlgStrokeSelection::isBrushSelected() const
{
int index = m_page->typeBox->currentIndex();
drawType type = static_cast<drawType>(index);
if (type == drawType::brushDraw){
return true;
}
else {
return false;
}
}
StrokeSelectionOptions KisDlgStrokeSelection::getParams() const
{
StrokeSelectionOptions params;
params.lineSize = getLineSize();
params.color = getSelectedColor();
params.brushSelected = isBrushSelected();
params.fillColor = getFillSelectedColor();
params._colorFillSource = m_page->m_options._colorFillSource;
return params;
}
void KisDlgStrokeSelection::lockVectorLayerFunctions()
{
m_page->colorFillSelector->setEnabled(false);
m_page->lineSize->setEnabled(false);
m_page->sizeBox->setEnabled(false);
m_page->fillBox->setEnabled(false);
m_page->typeBox->setEnabled(false);
}
void KisDlgStrokeSelection::unlockVectorLayerFunctions()
{
m_page->colorFillSelector->setEnabled(true);
m_page->lineSize->setEnabled(true);
m_page->sizeBox->setEnabled(true);
m_page->fillBox->setEnabled(true);
m_page->typeBox->setEnabled(true);
}
void KisDlgStrokeSelection::setColorFillButton()
{
m_page->colorFillSelector->setColor(getFillSelectedColor().toQColor());
}
void KisDlgStrokeSelection::setColorButton()
{
m_page->colorSelector->setColor(getSelectedColor().toQColor());
}
int KisDlgStrokeSelection::getLineSize() const
{
int value = m_page->lineSize->value();
if (m_page->sizeBox->currentText() == "px") {
return value;
}
else if (m_page->sizeBox->currentText() == "mm"){
int pixels = static_cast<int>(KoUnit::convertFromUnitToUnit(value,KoUnit(KoUnit::Millimeter), KoUnit(KoUnit::Pixel)));
return pixels;
}
else {
int pixels = static_cast<int>(KoUnit::convertFromUnitToUnit(value, KoUnit(KoUnit::Inch), KoUnit(KoUnit::Pixel)));
return pixels;
}
}
linePosition KisDlgStrokeSelection::getLinePosition() const
{/* TODO
int index = m_page->linePosition->currentIndex();
switch(index)
{
case(0):
return linePosition::OUTSIDE;
case(1):
return linePosition::INSIDE;
case(2):
return linePosition::CENTER;
default:
return linePosition::CENTER;
}*/
return linePosition::CENTER;
}
void KisDlgStrokeSelection::colorChanged(const QColor &newColor)
{
if (m_page->fillBox->currentText() == "Paint color") {
m_page->colorFillSelector->setColor(newColor);
}
QColor BGColor = m_resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value<KoColor>().toQColor();
QColor FGColor = m_resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value<KoColor>().toQColor();
KoColor tempColor= converter->approximateFromRenderedQColor(newColor);
if (!(newColor == BGColor) && !(newColor == FGColor)) {
m_page->m_options.color = tempColor;
m_page->lineColorBox->setCurrentIndex(2); //custom color
}
}
void KisDlgStrokeSelection::colorFillChanged(const QColor &newColor)
{
QColor PaintColor = m_page->colorSelector->color();
QColor BGcolor = m_resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value<KoColor>().toQColor();
QColor FGColor = m_resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value<KoColor>().toQColor();
KoColor tempColor= converter->approximateFromRenderedQColor(newColor);
if (!(newColor == FGColor) && !(newColor == BGcolor) && !(newColor == PaintColor)) {
m_page->m_options.customColor = tempColor;
m_page->fillBox->setCurrentIndex(static_cast<int>(colorFillSource::CustomColor));
}
m_page->m_options.fillColor = tempColor;
}
WdgStrokeSelection::WdgStrokeSelection(QWidget *parent) : QWidget(parent)
{
setupUi(this);
}
void WdgStrokeSelection::on_fillBox_currentIndexChanged(int index)
{
if (index == static_cast<int>(colorFillSource::None)) {
colorFillSelector->setDisabled(true);
}
else {
colorFillSelector->setDisabled(false);
emit colorFillSelectorChanged();
}
m_options._colorFillSource = index;
}
void WdgStrokeSelection::on_typeBox_currentIndexChanged(const QString &arg1)
{
if (arg1 == "Current Brush") {
m_options.brushSelected = true;
lineSize->setDisabled(true);
fillBox->setDisabled(true);
colorFillSelector->setDisabled(true);
sizeBox->setDisabled(true);
}
else {
m_options.brushSelected = false;
lineSize->setDisabled(false);
fillBox->setDisabled(false);
colorFillSelector->setDisabled(false);
sizeBox->setDisabled(false);
}
}
void WdgStrokeSelection::on_lineColorBox_currentIndexChanged(const QString &/*arg1*/)
{
emit colorSelectorChanged();
}
StrokeSelectionOptions ::StrokeSelectionOptions():
lineSize(1),
brushSelected(false),
_colorFillSource(0),
lineColorSource(0),
lineDimension(0)
{
color.fromQColor(Qt::black);
fillColor.fromQColor(Qt::black);
customColor.fromQColor(Qt::black);
}
KisPainter::FillStyle StrokeSelectionOptions::fillStyle() const
{
colorFillSource tempColor = static_cast<colorFillSource>(_colorFillSource);
KisPainter::FillStyle style;
switch (tempColor) {
case colorFillSource::PaintColor:
style = KisPainter::FillStyleForegroundColor;
break;
case colorFillSource::BGColor:
style = KisPainter::FillStyleBackgroundColor;
break;
case colorFillSource::CustomColor:
style = KisPainter::FillStyleBackgroundColor;
break;
case colorFillSource::None:
style = KisPainter::FillStyleNone;
break;
case colorFillSource::FGColor:
style = KisPainter::FillStyleBackgroundColor;
break;
default:
style = KisPainter::FillStyleBackgroundColor;
}
return style;
}
diff --git a/libs/ui/flake/kis_shape_layer.cc b/libs/ui/flake/kis_shape_layer.cc
index be23321690..6b32337ec3 100644
--- a/libs/ui/flake/kis_shape_layer.cc
+++ b/libs/ui/flake/kis_shape_layer.cc
@@ -1,637 +1,636 @@
/*
* 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 <kis_debug.h>
#include <kundo2command.h>
#include <commands_new/kis_node_move_command2.h>
#include <QMimeData>
#include <QTemporaryFile>
#include <kis_debug.h>
#include <kis_icon.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_default_bounds.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) override {
SimpleShapeContainerModel::add(child);
q->shapeManager()->addShape(child);
}
void remove(KoShape *child) override {
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)
: KisShapeLayer(rhs, rhs.m_d->controller)
{
}
KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs, KoShapeBasedDocumentBase* controller)
: 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(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
+ * Small hack alert: we should avoid updates on shape deletion
*/
- KisLayer::setImage(0);
+ m_d->canvas->prepareForDestroying();
Q_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)
{
setSupportsLodMoves(false);
setShapeId(KIS_SHAPE_LAYER_ID);
m_d->converter = new KisImageViewConverter(image());
KIS_ASSERT_RECOVER_NOOP(this->image());
m_d->paintDevice = new KisPaintDevice(image()->colorSpace());
m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(this->image()));
m_d->paintDevice->setParentNode(this);
m_d->canvas = new KisShapeLayerCanvas(this, m_d->converter);
m_d->canvas->setProjection(m_d->paintDevice);
m_d->canvas->moveToThread(this->thread());
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::createMergedLayerTemplate(KisLayerSP prevLayer)
{
KisShapeLayer *prevShape = dynamic_cast<KisShapeLayer*>(prevLayer.data());
if (prevShape)
return new KisShapeLayer(*prevShape, *this);
else
return KisExternalLayer::createMergedLayerTemplate(prevLayer);
}
void KisShapeLayer::fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer)
{
if (!dynamic_cast<KisShapeLayer*>(dstLayer.data())) {
KisLayer::fillMergedLayerTemplate(dstLayer, prevLayer);
}
}
void KisShapeLayer::setParent(KoShapeContainer *parent)
{
Q_UNUSED(parent)
KIS_ASSERT_RECOVER_RETURN(0)
}
QIcon KisShapeLayer::icon() const
{
return KisIconUtils::loadIcon("vectorLayer");
}
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;
Q_FOREACH (KoShape* shape, shapeManager()->shapes()) {
if (!dynamic_cast<KoShapeGroup*>(shape)) {
shapes.append(shape);
}
}
Q_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);
+ KoDocumentBase::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);
+// 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();
// 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)) {
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();
Q_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.
Q_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/libs/ui/flake/kis_shape_layer_canvas.cpp b/libs/ui/flake/kis_shape_layer_canvas.cpp
index 9f8605c341..c5d129e93e 100644
--- a/libs/ui/flake/kis_shape_layer_canvas.cpp
+++ b/libs/ui/flake/kis_shape_layer_canvas.cpp
@@ -1,169 +1,175 @@
/*
* 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_shape_layer_canvas.h"
#include <QPainter>
#include <QMutexLocker>
#include <KoShapeManager.h>
#include <KoViewConverter.h>
#include <KoColorSpace.h>
#include <kis_paint_device.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_painter.h>
#include <flake/kis_shape_layer.h>
#include <KoCompositeOpRegistry.h>
#include <KoSelection.h>
#include <KoUnit.h>
#include <kis_debug.h>
//#define DEBUG_REPAINT
KisShapeLayerCanvas::KisShapeLayerCanvas(KisShapeLayer *parent, KoViewConverter * viewConverter)
: QObject()
, KoCanvasBase(0)
+ , m_isDestroying(false)
, m_viewConverter(viewConverter)
, m_shapeManager(new KoShapeManager(this))
, m_projection(0)
, m_parentLayer(parent)
{
m_shapeManager->selection()->setActiveLayer(parent);
connect(this, SIGNAL(forwardRepaint()), SLOT(repaint()), Qt::QueuedConnection);
}
KisShapeLayerCanvas::~KisShapeLayerCanvas()
{
delete m_shapeManager;
}
+void KisShapeLayerCanvas::prepareForDestroying()
+{
+ m_isDestroying = true;
+}
+
void KisShapeLayerCanvas::gridSize(QPointF *offset, QSizeF *spacing) const
{
Q_ASSERT(false); // This should never be called as this canvas should have no tools.
Q_UNUSED(offset);
Q_UNUSED(spacing);
}
bool KisShapeLayerCanvas::snapToGrid() const
{
Q_ASSERT(false); // This should never be called as this canvas should have no tools.
return false;
}
void KisShapeLayerCanvas::addCommand(KUndo2Command *)
{
Q_ASSERT(false); // This should never be called as this canvas should have no tools.
}
KoShapeManager *KisShapeLayerCanvas::shapeManager() const
{
return m_shapeManager;
}
#ifdef DEBUG_REPAINT
# include <stdlib.h>
#endif
void KisShapeLayerCanvas::updateCanvas(const QRectF& rc)
{
dbgUI << "KisShapeLayerCanvas::updateCanvas()" << rc;
//image is 0, if parentLayer is being deleted so don't update
- if (!m_parentLayer->image()) {
+ if (!m_parentLayer->image() || m_isDestroying) {
return;
}
QRect r = m_viewConverter->documentToView(rc).toRect();
r.adjust(-2, -2, 2, 2); // for antialias
{
QMutexLocker locker(&m_dirtyRegionMutex);
m_dirtyRegion += r;
qreal x, y;
m_viewConverter->zoom(&x, &y);
}
emit forwardRepaint();
}
void KisShapeLayerCanvas::repaint()
{
QRect r;
{
QMutexLocker locker(&m_dirtyRegionMutex);
r = m_dirtyRegion.boundingRect();
m_dirtyRegion = QRegion();
}
if (r.isEmpty()) return;
r = r.intersected(m_parentLayer->image()->bounds());
QImage image(r.width(), r.height(), QImage::Format_ARGB32);
image.fill(0);
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing);
p.setRenderHint(QPainter::TextAntialiasing);
p.translate(-r.x(), -r.y());
p.setClipRect(r);
#ifdef DEBUG_REPAINT
QColor color = QColor(random() % 255, random() % 255, random() % 255);
p.fillRect(r, color);
#endif
m_shapeManager->paint(p, *m_viewConverter, false);
p.end();
KisPaintDeviceSP dev = new KisPaintDevice(m_projection->colorSpace());
dev->convertFromQImage(image, 0);
KisPainter::copyAreaOptimized(r.topLeft(), dev, m_projection, QRect(QPoint(), r.size()));
m_parentLayer->setDirty(r);
}
KoToolProxy * KisShapeLayerCanvas::toolProxy() const
{
// Q_ASSERT(false); // This should never be called as this canvas should have no tools.
return 0;
}
KoViewConverter *KisShapeLayerCanvas::viewConverter() const
{
return m_viewConverter;
}
QWidget* KisShapeLayerCanvas::canvasWidget()
{
return 0;
}
const QWidget* KisShapeLayerCanvas::canvasWidget() const
{
return 0;
}
KoUnit KisShapeLayerCanvas::unit() const
{
Q_ASSERT(false); // This should never be called as this canvas should have no tools.
return KoUnit(KoUnit::Point);
}
diff --git a/libs/ui/flake/kis_shape_layer_canvas.h b/libs/ui/flake/kis_shape_layer_canvas.h
index c79080a31f..82a0163ccb 100644
--- a/libs/ui/flake/kis_shape_layer_canvas.h
+++ b/libs/ui/flake/kis_shape_layer_canvas.h
@@ -1,82 +1,84 @@
/*
* 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 KIS_SHAPE_LAYER_CANVAS_H
#define KIS_SHAPE_LAYER_CANVAS_H
#include <QMutex>
#include <QRegion>
#include <KoCanvasBase.h>
#include <kis_types.h>
class KoShapeManager;
class KoToolProxy;
class KoViewConverter;
class KUndo2Command;
class QWidget;
class KoUnit;
/**
* KisShapeLayerCanvas is a special canvas implementation that Krita
* uses for non-krita shapes to request updates on.
*
* Do NOT give this canvas to tools or to the KoCanvasController, it's
* not made for that.
*/
class KisShapeLayerCanvas : public QObject, public KoCanvasBase
{
Q_OBJECT
public:
KisShapeLayerCanvas(KisShapeLayer *parent, KoViewConverter * viewConverter);
virtual ~KisShapeLayerCanvas();
/// This canvas won't render onto a widget, but a projection
void setProjection(KisPaintDeviceSP projection) {
m_projection = projection;
}
+ void prepareForDestroying();
void gridSize(QPointF *offset, QSizeF *spacing) const;
bool snapToGrid() const;
void addCommand(KUndo2Command *command);
KoShapeManager *shapeManager() const;
void updateCanvas(const QRectF& rc);
KoToolProxy * toolProxy() const;
KoViewConverter *viewConverter() const;
QWidget* canvasWidget();
const QWidget* canvasWidget() const;
KoUnit unit() const;
virtual void updateInputMethodInfo() {}
virtual void setCursor(const QCursor &) {}
private Q_SLOTS:
void repaint();
Q_SIGNALS:
void forwardRepaint();
private:
+ bool m_isDestroying;
KoViewConverter * m_viewConverter;
KoShapeManager * m_shapeManager;
KisPaintDeviceSP m_projection;
KisShapeLayer *m_parentLayer;
QRegion m_dirtyRegion;
QMutex m_dirtyRegionMutex;
};
#endif
diff --git a/libs/ui/input/kis_input_manager.cpp b/libs/ui/input/kis_input_manager.cpp
index 1a3f376204..62d872e32b 100644
--- a/libs/ui/input/kis_input_manager.cpp
+++ b/libs/ui/input/kis_input_manager.cpp
@@ -1,613 +1,613 @@
/* 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 <kis_debug.h>
#include <QQueue>
#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 <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());
}
#define start_ignore_cursor_events() d->blockMouseEvents()
#define stop_ignore_cursor_events() d->allowMouseEvents()
#define break_if_should_ignore_cursor_events() if (d->ignoringQtCursorEvents()) break;
#define break_if_tablet_active() if (d->tabletActive) break;
// Touch rejection: if touch is disabled on canvas, no need to block mouse press 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;
#define touch_eat_one_mouse_press() if (d->disableTouchOnCanvas) d->eatOneMousePress();
KisInputManager::KisInputManager(QObject *parent)
: QObject(parent), d(new Private(this))
{
d->setupActions();
connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)),
SLOT(slotToolChanged()));
connect(&d->moveEventCompressor, SIGNAL(timeout()), SLOT(slotCompressedMoveEvent()));
QApplication::instance()->
installEventFilter(new Private::ProximityNotifier(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();
}
void KisInputManager::attachPriorityEventFilter(QObject *filter, int priority)
{
Private::PriorityList::iterator begin = d->priorityEventFilter.begin();
Private::PriorityList::iterator it = begin;
Private::PriorityList::iterator end = d->priorityEventFilter.end();
it = std::find_if(begin, end,
[filter] (const Private::PriorityPair &a) { return a.second == filter; });
if (it != end) return;
it = std::find_if(begin, end,
[priority] (const Private::PriorityPair &a) { return a.first > priority; });
d->priorityEventFilter.insert(it, qMakePair(priority, filter));
d->priorityEventFilterSeqNo++;
}
void KisInputManager::detachPriorityEventFilter(QObject *filter)
{
Private::PriorityList::iterator it = d->priorityEventFilter.begin();
Private::PriorityList::iterator end = d->priorityEventFilter.end();
it = std::find_if(it, end,
[filter] (const Private::PriorityPair &a) { return a.second == filter; });
if (it != end) {
d->priorityEventFilter.erase(it);
}
}
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)
{
Q_UNUSED(value);
// not used anymore
}
#if defined (__clang__)
#pragma GCC diagnostic ignored "-Wswitch"
#endif
bool KisInputManager::eventFilter(QObject* object, QEvent* event)
{
if (object != d->eventsReceiver) return false;
if (d->eventEater.eventFilter(object, event)) return false;
if (!d->matcher.hasRunningShortcut()) {
int savedPriorityEventFilterSeqNo = d->priorityEventFilterSeqNo;
for (auto it = d->priorityEventFilter.begin(); it != d->priorityEventFilter.end(); /*noop*/) {
const QPointer<QObject> &filter = it->second;
if (filter.isNull()) {
it = d->priorityEventFilter.erase(it);
d->priorityEventFilterSeqNo++;
savedPriorityEventFilterSeqNo++;
continue;
}
if (filter->eventFilter(object, event)) return true;
/**
* If the filter removed itself from the filters list or
* added something there, just exit the loop
*/
if (d->priorityEventFilterSeqNo != savedPriorityEventFilterSeqNo) {
return true;
}
++it;
}
// 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);
}
// Continue with the actual switch statement...
return eventFilterImpl(event);
}
template <class Event>
bool KisInputManager::compressMoveEventCommon(Event *event)
{
/**
* We construct a copy of this event object, so we must ensure it
* has a correct type.
*/
static_assert(std::is_same<Event, QMouseEvent>::value ||
std::is_same<Event, QTabletEvent>::value,
- "event should a mouse or a tablet event");
+ "event should be a mouse or a tablet event");
bool retval = false;
/**
* Compress the events if the tool doesn't need high resolution input
*/
if ((event->type() == QEvent::MouseMove ||
event->type() == QEvent::TabletMove) &&
(!d->matcher.supportsHiResInputEvents() ||
d->testingCompressBrushEvents)) {
d->compressedMoveEvent.reset(new Event(*event));
d->moveEventCompressor.start();
/**
* 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) {
event->setAccepted(true);
}
retval = true;
} else {
slotCompressedMoveEvent();
retval = d->handleCompressedTabletEvent(event);
}
return retval;
}
bool KisInputManager::eventFilterImpl(QEvent * event)
{
// TODO: Handle touch events correctly.
bool retval = false;
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick: {
d->debugEvent<QMouseEvent, true>(event);
//Block mouse press events on Genius tablets
break_if_tablet_active();
break_if_should_ignore_cursor_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);
}
//Reset signal compressor to prevent processing events before press late
d->resetCompressor();
event->setAccepted(retval);
break;
}
case QEvent::MouseButtonRelease: {
d->debugEvent<QMouseEvent, true>(event);
break_if_should_ignore_cursor_events();
break_if_touch_blocked_press_events();
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent);
event->setAccepted(retval);
break;
}
case QEvent::ShortcutOverride: {
d->debugEvent<QKeyEvent, false>(event);
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
Qt::Key key = KisExtendedModifiersMapper::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 = KisExtendedModifiersMapper::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);
retval = compressMoveEventCommon(mouseEvent);
break;
}
case QEvent::Wheel: {
d->debugEvent<QWheelEvent, false>(event);
QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
KisSingleActionShortcut::WheelAction action;
/**
* Ignore delta 0 events on OSX, since they are triggered by tablet
* proximity when using Wacom devices.
*/
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
if(wheelEvent->delta() == 0) {
retval = true;
break;
}
#endif
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);
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();
Q_FOREACH (Qt::Key key, mapper.queryExtendedModifiers()) {
QKeyEvent kevent(QEvent::ShortcutOverride, key, modifiers);
eventFilterImpl(&kevent);
}
}
stop_ignore_cursor_events();
break;
case QEvent::TabletRelease: {
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
stop_ignore_cursor_events();
#endif
// 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);
break;
}
case QEvent::TabletMove: {
d->debugEvent<QTabletEvent, false>(event);
QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
retval = compressMoveEventCommon(tabletEvent);
/**
* 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 QEvent::TabletPress: {
d->debugEvent<QTabletEvent, false>(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();
//Reset signal compressor to prevent processing events before press late
d->resetCompressor();
d->eatOneMousePress();
break;
}
case QEvent::TouchBegin:
touch_start_block_press_events();
touch_eat_one_mouse_press();
if (d->tryHidePopupPalette()) {
retval = true;
} else {
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
}
// d->resetSavedTabletEvent(event->type());
break;
case QEvent::TouchUpdate: {
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
int count = 0;
Q_FOREACH (const QTouchEvent::TouchPoint &point, tevent->touchPoints()) {
if (point.state() != Qt::TouchPointReleased) {
count++;
}
}
if (count < 2 && tevent->touchPoints().length() > count) {
touch_stop_block_press_events();
d->saveTouchEvent(tevent);
retval = d->matcher.touchEndEvent(tevent);
delete d->lastTouchEvent;
d->lastTouchEvent = 0;
} else {
#endif
touch_start_block_press_events();
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.touchUpdateEvent(tevent);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
}
#endif
event->accept();
// 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());
delete d->lastTouchEvent;
d->lastTouchEvent = 0;
break;
default:
break;
}
return !retval ? d->processUnhandledEvent(event) : true;
}
void KisInputManager::slotCompressedMoveEvent()
{
if (d->compressedMoveEvent) {
// touch_stop_block_press_events();
(void) d->handleCompressedTabletEvent(d->compressedMoveEvent.data());
d->compressedMoveEvent.reset();
dbgKrita << "Compressed move event received.";
} else {
dbgKrita << "Unexpected empty move event";
}
}
KisCanvas2* KisInputManager::canvas() const
{
return d->canvas;
}
KisToolProxy* KisInputManager::toolProxy() const
{
return d->toolProxy;
}
QTouchEvent *KisInputManager::lastTouchEvent() const
{
return d->lastTouchEvent;
}
void KisInputManager::slotToolChanged()
{
KoToolManager *toolManager = KoToolManager::instance();
KoToolBase *tool = toolManager->toolById(canvas(), toolManager->activeToolId());
if (tool && tool->isInTextMode()) {
d->forwardAllEventsToTool = true;
d->matcher.suppressAllActions(true);
} else {
d->forwardAllEventsToTool = false;
d->matcher.suppressAllActions(false);
}
d->maskSyntheticEvents(tool->maskSyntheticEvents());
}
QPointF KisInputManager::widgetToDocument(const QPointF& position)
{
const QPointF half = QPointF(.5f, .5f);
QPointF pixel = position + half;
return d->canvas->coordinatesConverter()->widgetToDocument(pixel);
}
void KisInputManager::profileChanged()
{
d->matcher.clearShortcuts();
KisInputProfile *profile = KisInputProfileManager::instance()->currentProfile();
if (profile) {
const QList<KisShortcutConfiguration*> shortcuts = profile->allShortcuts();
for (KisShortcutConfiguration * const shortcut : shortcuts) {
dbgUI << "Adding shortcut" << shortcut->keys() << "for action" << shortcut->action()->name();
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 {
dbgKrita << "No Input Profile Found: canvas interaction will be impossible";
}
}
diff --git a/libs/ui/input/kis_input_manager_p.cpp b/libs/ui/input/kis_input_manager_p.cpp
index eed19ec135..7a934715b2 100644
--- a/libs/ui/input/kis_input_manager_p.cpp
+++ b/libs/ui/input/kis_input_manager_p.cpp
@@ -1,537 +1,537 @@
/*
* 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_input_manager.h"
#include "kis_config.h"
#include "kis_abstract_input_action.h"
#include "kis_tool_invocation_action.h"
#include "kis_stroke_shortcut.h"
#include "kis_touch_shortcut.h"
#include "kis_input_profile_manager.h"
/**
* This hungry class EventEater encapsulates event masking logic.
*
* Its basic role is to kill synthetic mouseMove events sent by Xorg or Qt after
* tablet events. Those events are sent in order to allow widgets that haven't
* implemented tablet specific functionality to seamlessly behave as if one were
* using a mouse. These synthetic events are *supposed* to be optional, or at
* least come with a flag saying "This is a fake event!!" but neither of those
* methods is trustworthy. (This is correct as of Qt 5.4 + Xorg.)
*
* Qt 5.4 provides no reliable way to see if a user's tablet is being hovered
* over the pad, since it converts all tablethover events into mousemove, with
* no option to turn this off. Moreover, sometimes the MouseButtonPress event
* from the tapping their tablet happens BEFORE the TabletPress event. This
* means we have to resort to a somewhat complicated logic. What makes this
* truly a joke is that we are not guaranteed to observe TabletProximityEnter
* events when we're using a tablet, either, you may only see an Enter event.
*
* Once we see tablet events heading our way, we can say pretty confidently that
* every mouse event is fake. There are two painful cases to consider - a
* mousePress event could arrive before the tabletPress event, or it could
* arrive much later, e.g. after tabletRelease. The first was only seen on Linux
* with Qt's XInput2 code, the solution was to hold onto mousePress events
* temporarily and wait for tabletPress later, this is contained in git history
* but is now removed. The second case is currently handled by the
* eatOneMousePress function, which waits as long as necessary to detect and
* block a single mouse press event.
*/
static bool isMouseEventType(QEvent::Type t)
{
return (t == QEvent::MouseMove ||
t == QEvent::MouseButtonPress ||
t == QEvent::MouseButtonRelease ||
t == QEvent::MouseButtonDblClick);
}
bool KisInputManager::Private::EventEater::eventFilter(QObject* target, QEvent* event )
{
Q_UNUSED(target)
auto debugEvent = [&]() {
if (KisTabletDebugger::instance()->debugEnabled()) {
QString pre = QString("[BLOCKED]");
QMouseEvent *ev = static_cast<QMouseEvent*>(event);
dbgTablet << KisTabletDebugger::instance()->eventToString(*ev,pre);
}
};
if (peckish && event->type() == QEvent::MouseButtonPress
// Drop one mouse press following tabletPress or touchBegin
&& (static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton)) {
peckish = false;
debugEvent();
return true;
} else if (isMouseEventType(event->type()) &&
(hungry
// On Mac, we need mouse events when the tablet is in proximity, but not pressed down
// since tablet move events are not generated until after tablet press.
- #ifndef Q_OS_MAC
+ #ifndef Q_OS_OSX
|| (eatSyntheticEvents && static_cast<QMouseEvent*>(event)->source() != Qt::MouseEventNotSynthesized)
#endif
)) {
// Drop mouse events if enabled or event was synthetic & synthetic events are disabled
debugEvent();
return true;
}
return false; // All clear - let this one through!
}
void KisInputManager::Private::EventEater::activate()
{
if (!hungry && (KisTabletDebugger::instance()->debugEnabled()))
qDebug() << "Start ignoring mouse events.";
hungry = true;
}
void KisInputManager::Private::EventEater::deactivate()
{
if (hungry && (KisTabletDebugger::instance()->debugEnabled()))
dbgTablet << "Stop ignoring mouse events.";
hungry = false;
}
void KisInputManager::Private::EventEater::eatOneMousePress()
{
// #if defined(Q_OS_WIN)
// Enable on other platforms if getting full-pressure splotches
peckish = true;
// #endif
}
bool KisInputManager::Private::ignoringQtCursorEvents()
{
return eventEater.hungry;
}
void KisInputManager::Private::maskSyntheticEvents(bool value)
{
eventEater.eatSyntheticEvents = value;
}
KisInputManager::Private::Private(KisInputManager *qq)
: q(qq)
, moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE)
, priorityEventFilterSeqNo(0)
, canvasSwitcher(this, qq)
{
KisConfig cfg;
disableTouchOnCanvas = cfg.disableTouchOnCanvas();
moveEventCompressor.setDelay(cfg.tabletEventsDelay());
testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents();
testingCompressBrushEvents = cfg.testingCompressBrushEvents();
}
static const int InputWidgetsThreshold = 2000;
static const int OtherWidgetsThreshold = 400;
KisInputManager::Private::CanvasSwitcher::CanvasSwitcher(Private *_d, QObject *p)
: QObject(p),
d(_d),
eatOneMouseStroke(false),
focusSwitchThreshold(InputWidgetsThreshold)
{
}
void KisInputManager::Private::CanvasSwitcher::setupFocusThreshold(QObject* object)
{
QWidget *widget = qobject_cast<QWidget*>(object);
KIS_SAFE_ASSERT_RECOVER_RETURN(widget);
thresholdConnections.clear();
thresholdConnections.addConnection(&focusSwitchThreshold, SIGNAL(timeout()), widget, SLOT(setFocus()));
}
void KisInputManager::Private::CanvasSwitcher::addCanvas(KisCanvas2 *canvas)
{
QObject *canvasWidget = canvas->canvasWidget();
if (!canvasResolver.contains(canvasWidget)) {
canvasResolver.insert(canvasWidget, canvas);
d->q->setupAsEventFilter(canvasWidget);
canvasWidget->installEventFilter(this);
setupFocusThreshold(canvasWidget);
focusSwitchThreshold.setEnabled(false);
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 isInputWidget(QWidget *w)
{
if (!w) return false;
QList<QLatin1String> types;
types << QLatin1String("QAbstractSlider");
types << QLatin1String("QAbstractSpinBox");
types << QLatin1String("QLineEdit");
types << QLatin1String("QTextEdit");
types << QLatin1String("QPlainTextEdit");
types << QLatin1String("QComboBox");
types << QLatin1String("QKeySequenceEdit");
Q_FOREACH (const QLatin1String &type, types) {
if (w->inherits(type.data())) {
return true;
}
}
return false;
}
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);
KisCanvas2 *canvas = canvasResolver.value(object);
if (canvas != d->canvas) {
eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason);
}
d->canvas = canvas;
d->toolProxy = dynamic_cast<KisToolProxy*>(canvas->toolProxy());
d->q->setupAsEventFilter(object);
object->removeEventFilter(this);
object->installEventFilter(this);
setupFocusThreshold(object);
focusSwitchThreshold.setEnabled(false);
QEvent event(QEvent::Enter);
d->q->eventFilter(object, &event);
break;
}
case QEvent::FocusOut: {
focusSwitchThreshold.setEnabled(true);
break;
}
case QEvent::Enter: {
break;
}
case QEvent::Leave: {
focusSwitchThreshold.stop();
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:
focusSwitchThreshold.forceDone();
if (eatOneMouseStroke) {
eatOneMouseStroke--;
return true;
}
break;
case QEvent::MouseButtonDblClick:
focusSwitchThreshold.forceDone();
if (eatOneMouseStroke) {
return true;
}
break;
case QEvent::MouseMove:
case QEvent::TabletMove: {
QWidget *widget = static_cast<QWidget*>(object);
if (!widget->hasFocus()) {
const int delay =
isInputWidget(QApplication::focusWidget()) ?
InputWidgetsThreshold : OtherWidgetsThreshold;
focusSwitchThreshold.setDelayThreshold(delay);
focusSwitchThreshold.start();
}
}
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);
// Tablet proximity events are unreliable AND fake mouse events do not
// necessarily come after tablet events, so this is insufficient.
// d->eventEater.eatOneMousePress();
// Qt sends fake mouse events instead of hover events, so not very useful.
// Don't block mouse events on tablet since tablet move events are not generated until
// after tablet press.
-#ifndef Q_OS_MAC
+#ifndef Q_OS_OSX
d->blockMouseEvents();
#else
// Notify input manager that tablet proximity is entered for Genius tablets.
d->setTabletActive(true);
#endif
break;
case QEvent::TabletLeaveProximity:
d->debugEvent<QEvent, false>(event);
d->allowMouseEvents();
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
d->setTabletActive(false);
#endif
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(QSet<Qt::Key>::fromList(modifiers), QSet<Qt::MouseButton>::fromList(buttonList));
matcher.addShortcut(strokeShortcut);
}
else {
delete 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> allKeys = keys;
Qt::Key key = allKeys.takeLast();
QSet<Qt::Key> modifiers = QSet<Qt::Key>::fromList(allKeys);
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(QSet<Qt::Key>::fromList(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();
Q_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;
}
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()
{
eventEater.activate();
}
void KisInputManager::Private::allowMouseEvents()
{
eventEater.deactivate();
}
void KisInputManager::Private::eatOneMousePress()
{
eventEater.eatOneMousePress();
}
void KisInputManager::Private::setTabletActive(bool value) {
tabletActive = value;
}
void KisInputManager::Private::resetCompressor() {
compressedMoveEvent.reset();
moveEventCompressor.stop();
}
bool KisInputManager::Private::handleCompressedTabletEvent(QEvent *event)
{
bool retval = false;
if (!matcher.pointerMoved(event)) {
toolProxy->forwardHoverEvent(event);
}
retval = true;
event->setAccepted(true);
return retval;
}
diff --git a/libs/ui/input/wintab/kis_tablet_support.h b/libs/ui/input/wintab/kis_tablet_support.h
index 042885faf3..1c1899d79e 100644
--- a/libs/ui/input/wintab/kis_tablet_support.h
+++ b/libs/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
+#ifndef Q_OS_OSX
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))
+#if defined(HAVE_X11) || (defined(Q_OS_OSX) && !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)
+#elif defined(Q_OS_OSX)
quint64 tabletUniqueID;
int tabletDeviceType;
int tabletPointerType;
int capabilityMask;
#endif
// Added by Krita
-#if defined(Q_OS_MAC) || defined(Q_OS_WIN32)
+#if defined(Q_OS_OSX) || 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) {
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
+#ifndef Q_OS_OSX
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/libs/ui/input/wintab/qxcbconnection_xi2.cpp b/libs/ui/input/wintab/qxcbconnection_xi2.cpp
index 36f5a4e8c6..78a9ea1829 100644
--- a/libs/ui/input/wintab/qxcbconnection_xi2.cpp
+++ b/libs/ui/input/wintab/qxcbconnection_xi2.cpp
@@ -1,1035 +1,1041 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** 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 The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. 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.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qxcbconnection_xi2.h"
//#include "qxcbconnection.h"
//#include "qxcbkeyboard.h"
//#include "qxcbscreen.h"
//#include "qxcbwindow.h"
//#include "qtouchdevice.h"
//#include <qpa/qwindowsysteminterface.h>
#include <QDebug>
#include <QApplication>
#include <QDesktopWidget>
#ifdef XCB_USE_XINPUT2
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XI2proto.h>
struct XInput2TouchDeviceData {
XInput2TouchDeviceData()
: xiDeviceInfo(0)
, qtTouchDevice(0)
{
}
XIDeviceInfo *xiDeviceInfo;
QTouchDevice *qtTouchDevice;
QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
// Stuff that is relevant only for touchpads
QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
QSizeF size; // device size in mm
};
void QXcbConnection::initializeXInput2()
{
// TODO Qt 6 (or perhaps earlier): remove these redundant env variables
if (qEnvironmentVariableIsSet("KIS_QT_XCB_DEBUG_XINPUT"))
const_cast<QLoggingCategory&>(lcQpaXInput()).setEnabled(QtDebugMsg, true);
if (qEnvironmentVariableIsSet("KIS_QT_XCB_DEBUG_XINPUT_DEVICES"))
const_cast<QLoggingCategory&>(lcQpaXInputDevices()).setEnabled(QtDebugMsg, true);
Display *xDisplay = static_cast<Display *>(m_xlib_display);
if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
int xiMajor = 2;
m_xi2Minor = 2; // try 2.2 first, needed for TouchBegin/Update/End
if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
m_xi2Minor = 0; // for tablet support 2.0 is enough
m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest;
} else
m_xi2Enabled = true;
} else
m_xi2Enabled = true;
if (m_xi2Enabled) {
#ifdef XCB_USE_XINPUT22
qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor);
#else
qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor);
#endif
}
xi2SetupDevices();
}
}
void QXcbConnection::xi2SetupDevices()
{
#ifndef QT_NO_TABLETEVENT
m_tabletData.clear();
#endif
m_scrollingDevices.clear();
if (!m_xi2Enabled)
return;
Display *xDisplay = static_cast<Display *>(m_xlib_display);
int deviceCount = 0;
XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
for (int i = 0; i < deviceCount; ++i) {
// Only non-master pointing devices are relevant here.
if (devices[i].use != XISlavePointer)
continue;
qCDebug(lcQpaXInputDevices) << "input device "<< devices[i].name;
#ifndef QT_NO_TABLETEVENT
TabletData tabletData;
#endif
ScrollingDevice scrollingDevice;
for (int c = 0; c < devices[i].num_classes; ++c) {
switch (devices[i].classes[c]->type) {
case XIValuatorClass: {
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]);
const int valuatorAtom = qatom(vci->label);
qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
#ifndef QT_NO_TABLETEVENT
if (valuatorAtom < QXcbAtom::NAtoms) {
TabletData::ValuatorClassInfo info;
info.minVal = vci->min;
info.maxVal = vci->max;
info.number = vci->number;
tabletData.valuatorInfo[valuatorAtom] = info;
}
#endif // QT_NO_TABLETEVENT
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
scrollingDevice.lastScrollPosition.setX(vci->value);
else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
scrollingDevice.lastScrollPosition.setY(vci->value);
break;
}
#ifdef XCB_USE_XINPUT21
case XIScrollClass: {
XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]);
if (sci->scroll_type == XIScrollTypeVertical) {
scrollingDevice.orientations |= Qt::Vertical;
scrollingDevice.verticalIndex = sci->number;
scrollingDevice.verticalIncrement = sci->increment;
}
else if (sci->scroll_type == XIScrollTypeHorizontal) {
scrollingDevice.orientations |= Qt::Horizontal;
scrollingDevice.horizontalIndex = sci->number;
scrollingDevice.horizontalIncrement = sci->increment;
}
break;
}
case XIButtonClass: {
XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]);
if (bci->num_buttons >= 5) {
Atom label4 = bci->labels[3];
Atom label5 = bci->labels[4];
// Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
// button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
(!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown))
scrollingDevice.legacyOrientations |= Qt::Vertical;
}
if (bci->num_buttons >= 7) {
Atom label6 = bci->labels[5];
Atom label7 = bci->labels[6];
if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
scrollingDevice.legacyOrientations |= Qt::Horizontal;
}
qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
break;
}
#endif
case XIKeyClass:
qCDebug(lcQpaXInputDevices) << " it's a keyboard";
break;
#ifdef XCB_USE_XINPUT22
case XITouchClass:
// will be handled in deviceForId()
break;
#endif
default:
qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type;
break;
}
}
bool isTablet = false;
#ifndef QT_NO_TABLETEVENT
// If we have found the valuators which we expect a tablet to have, it might be a tablet.
if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure))
isTablet = true;
// But we need to be careful not to take the touch and tablet-button devices as tablets.
QByteArray name = QByteArray(devices[i].name).toLower();
QString dbgType = QLatin1String("UNKNOWN");
if (name.contains("eraser")) {
isTablet = true;
tabletData.pointerType = QTabletEvent::Eraser;
dbgType = QLatin1String("eraser");
} else if (name.contains("cursor")) {
isTablet = true;
tabletData.pointerType = QTabletEvent::Cursor;
dbgType = QLatin1String("cursor");
} else if ((name.contains("pen") || name.contains("stylus")) && isTablet) {
tabletData.pointerType = QTabletEvent::Pen;
dbgType = QLatin1String("pen");
} else if (name.contains("wacom") && isTablet && !name.contains("touch")) {
// combined device (evdev) rather than separate pen/eraser (wacom driver)
tabletData.pointerType = QTabletEvent::Pen;
dbgType = QLatin1String("pen");
} else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
// some "Genius" tablets
isTablet = true;
tabletData.pointerType = QTabletEvent::Pen;
dbgType = QLatin1String("pen");
+ } else if (name.contains("waltop") && name.contains("tablet")) {
+ // other "Genius" tablets
+ // WALTOP International Corp. Slim Tablet
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
} else if (name.contains("uc-logic")) {
isTablet = true;
tabletData.pointerType = QTabletEvent::Pen;
dbgType = QLatin1String("pen");
} else {
isTablet = false;
}
if (isTablet) {
tabletData.deviceId = devices[i].deviceid;
m_tabletData.append(tabletData);
qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
}
#endif // QT_NO_TABLETEVENT
#ifdef XCB_USE_XINPUT21
if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
scrollingDevice.deviceId = devices[i].deviceid;
// Only use legacy wheel button events when we don't have real scroll valuators.
scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
}
#endif
if (!isTablet) {
// touchDeviceForId populates XInput2DeviceData the first time it is called
// with a new deviceId. On subsequent calls it will return the cached object.
XInput2TouchDeviceData *dev = touchDeviceForId(devices[i].deviceid);
if (dev && lcQpaXInputDevices().isDebugEnabled()) {
if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
dev->qtTouchDevice->maximumTouchPoints());
else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
dev->qtTouchDevice->maximumTouchPoints(),
dev->size.width(), dev->size.height());
}
}
}
XIFreeDeviceInfo(devices);
}
void QXcbConnection::finalizeXInput2()
{
foreach (XInput2TouchDeviceData *dev, m_touchDevices) {
if (dev->xiDeviceInfo)
XIFreeDeviceInfo(dev->xiDeviceInfo);
delete dev->qtTouchDevice;
delete dev;
}
}
void QXcbConnection::xi2Select(xcb_window_t window)
{
if (!m_xi2Enabled)
return;
Display *xDisplay = static_cast<Display *>(m_xlib_display);
unsigned int bitMask = 0;
unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
#ifdef XCB_USE_XINPUT22
if (isAtLeastXI22()) {
bitMask |= XI_TouchBeginMask;
bitMask |= XI_TouchUpdateMask;
bitMask |= XI_TouchEndMask;
bitMask |= XI_PropertyEventMask; // for tablets
if (xi2MouseEvents()) {
// We want both mouse and touch through XI2 if touch is supported (>= 2.2).
// The plain xcb press and motion events will not be delivered after this.
bitMask |= XI_ButtonPressMask;
bitMask |= XI_ButtonReleaseMask;
bitMask |= XI_MotionMask;
qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
}
XIEventMask mask;
mask.mask_len = sizeof(bitMask);
mask.mask = xiBitMask;
// When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet
// events will get disabled. This is preferable for touch, as Qt Quick handles touch events
// directly while for others QtGui synthesizes mouse events, not so much for tablets. For
// the latter we will synthesize the events ourselves.
mask.deviceid = XIAllMasterDevices;
Status result = XISelectEvents(xDisplay, window, &mask, 1);
if (result != Success)
qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
}
#endif // XCB_USE_XINPUT22
const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
QSet<int> tabletDevices;
#ifndef QT_NO_TABLETEVENT
if (!m_tabletData.isEmpty()) {
unsigned int tabletBitMask;
unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask);
QVector<XIEventMask> xiEventMask(m_tabletData.count());
tabletBitMask = XI_PropertyEventMask;
if (!pointerSelected)
tabletBitMask |= XI_ButtonPressMask | XI_ButtonReleaseMask | XI_MotionMask;
for (int i = 0; i < m_tabletData.count(); ++i) {
int deviceId = m_tabletData.at(i).deviceId;
tabletDevices.insert(deviceId);
xiEventMask[i].deviceid = deviceId;
xiEventMask[i].mask_len = sizeof(tabletBitMask);
xiEventMask[i].mask = xiTabletBitMask;
}
XISelectEvents(xDisplay, window, xiEventMask.data(), m_tabletData.count());
}
#endif // QT_NO_TABLETEVENT
#ifdef XCB_USE_XINPUT21
// Enable each scroll device
if (!m_scrollingDevices.isEmpty() && !pointerSelected) {
// Only when XI2 mouse events are not enabled, otherwise motion and release are selected already.
QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
unsigned int scrollBitMask;
unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask);
scrollBitMask = XI_MotionMask;
scrollBitMask |= XI_ButtonReleaseMask;
int i=0;
Q_FOREACH (const ScrollingDevice& scrollingDevice, m_scrollingDevices) {
if (tabletDevices.contains(scrollingDevice.deviceId))
continue; // All necessary events are already captured.
xiEventMask[i].deviceid = scrollingDevice.deviceId;
xiEventMask[i].mask_len = sizeof(scrollBitMask);
xiEventMask[i].mask = xiScrollBitMask;
i++;
}
XISelectEvents(xDisplay, window, xiEventMask.data(), i);
}
#else
Q_UNUSED(xiBitMask);
#endif
{
// Listen for hotplug events
XIEventMask xiEventMask;
bitMask = XI_HierarchyChangedMask;
bitMask |= XI_DeviceChangedMask;
xiEventMask.deviceid = XIAllDevices;
xiEventMask.mask_len = sizeof(bitMask);
xiEventMask.mask = xiBitMask;
XISelectEvents(xDisplay, window, &xiEventMask, 1);
}
}
XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
{
XInput2TouchDeviceData *dev = 0;
QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constFind(id);
if (devIt != m_touchDevices.cend()) {
dev = devIt.value();
} else {
int nrDevices = 0;
QTouchDevice::Capabilities caps = 0;
dev = new XInput2TouchDeviceData;
dev->xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), id, &nrDevices);
if (nrDevices <= 0) {
delete dev;
return 0;
}
int type = -1;
int maxTouchPoints = 1;
bool hasRelativeCoords = false;
for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
switch (classinfo->type) {
#ifdef XCB_USE_XINPUT22
case XITouchClass: {
XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
maxTouchPoints = tci->num_touches;
qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
switch (tci->mode) {
case XIDependentTouch:
type = QTouchDevice::TouchPad;
break;
case XIDirectTouch:
type = QTouchDevice::TouchScreen;
break;
}
break;
}
#endif // XCB_USE_XINPUT22
case XIValuatorClass: {
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
if (vci->label == atom(QXcbAtom::AbsMTPositionX))
caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition;
else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor))
caps |= QTouchDevice::Area;
else if (vci->label == atom(QXcbAtom::AbsMTPressure) || vci->label == atom(QXcbAtom::AbsPressure))
caps |= QTouchDevice::Pressure;
else if (vci->label == atom(QXcbAtom::RelX)) {
hasRelativeCoords = true;
dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution);
} else if (vci->label == atom(QXcbAtom::RelY)) {
hasRelativeCoords = true;
dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution);
} else if (vci->label == atom(QXcbAtom::AbsX)) {
dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution);
} else if (vci->label == atom(QXcbAtom::AbsY)) {
dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution);
}
break;
}
default:
break;
}
}
if (type < 0 && caps && hasRelativeCoords) {
type = QTouchDevice::TouchPad;
if (dev->size.width() < 10 || dev->size.height() < 10 ||
dev->size.width() > 10000 || dev->size.height() > 10000)
dev->size = QSizeF(130, 110);
}
// WARNING: Krita edit
// if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
// caps |= QTouchDevice::MouseEmulation;
if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
dev->qtTouchDevice = new QTouchDevice;
dev->qtTouchDevice->setName(QString::fromUtf8(dev->xiDeviceInfo->name));
dev->qtTouchDevice->setType((QTouchDevice::DeviceType)type);
dev->qtTouchDevice->setCapabilities(caps);
dev->qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
if (caps != 0)
QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice);
m_touchDevices[id] = dev;
} else {
XIFreeDeviceInfo(dev->xiDeviceInfo);
delete dev;
dev = 0;
}
}
return dev;
}
#if defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT)
static inline qreal fixed1616ToReal(FP1616 val)
{
return qreal(val) / 0x10000;
}
#endif // defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT)
bool QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
{
if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
int sourceDeviceId = xiEvent->deviceid; // may be the master id
xXIDeviceEvent *xiDeviceEvent = 0;
QWindow *window = 0;
switch (xiEvent->evtype) {
case XI_ButtonPress:
case XI_ButtonRelease:
case XI_Motion:
#ifdef XCB_USE_XINPUT22
case XI_TouchBegin:
case XI_TouchUpdate:
case XI_TouchEnd:
#endif
{
xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
window = windowFromId(xiDeviceEvent->event);
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
break;
}
case XI_HierarchyChanged:
xi2HandleHierachyEvent(xiEvent);
return false;
case XI_DeviceChanged:
xi2HandleDeviceChangedEvent(xiEvent);
return false;
default:
break;
}
#ifndef QT_NO_TABLETEVENT
for (int i = 0; i < m_tabletData.count(); ++i) {
if (m_tabletData.at(i).deviceId == sourceDeviceId) {
if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], window)) {
return true;
}
}
}
#endif // QT_NO_TABLETEVENT
#ifdef XCB_USE_XINPUT21
QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId);
if (device != m_scrollingDevices.end())
xi2HandleScrollEvent(xiEvent, device.value());
// Removed from Qt...
#endif // XCB_USE_XINPUT21
#ifdef XCB_USE_XINPUT22
// Removed from Qt...
#endif // XCB_USE_XINPUT22
}
return false;
}
#ifdef XCB_USE_XINPUT22
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
{
if (grab && !canGrab())
return false;
int num_devices = 0;
Display *xDisplay = static_cast<Display *>(xlib_display());
XIDeviceInfo *info = XIQueryDevice(xDisplay, XIAllMasterDevices, &num_devices);
if (!info)
return false;
XIEventMask evmask;
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
evmask.mask = mask;
evmask.mask_len = sizeof(mask);
memset(mask, 0, sizeof(mask));
evmask.deviceid = XIAllMasterDevices;
XISetMask(mask, XI_ButtonPress);
XISetMask(mask, XI_ButtonRelease);
XISetMask(mask, XI_Motion);
XISetMask(mask, XI_TouchBegin);
XISetMask(mask, XI_TouchUpdate);
XISetMask(mask, XI_TouchEnd);
bool grabbed = true;
for (int i = 0; i < num_devices; i++) {
int id = info[i].deviceid, n = 0;
XIDeviceInfo *deviceInfo = XIQueryDevice(xDisplay, id, &n);
if (deviceInfo) {
const bool grabbable = deviceInfo->use != XIMasterKeyboard;
XIFreeDeviceInfo(deviceInfo);
if (!grabbable)
continue;
}
if (!grab) {
Status result = XIUngrabDevice(xDisplay, id, CurrentTime);
if (result != Success) {
grabbed = false;
qCDebug(lcQpaXInput, "XInput 2.2: failed to ungrab events for device %d (result %d)", id, result);
}
} else {
Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, XIGrabModeAsync,
XIGrabModeAsync, False, &evmask);
if (result != Success) {
grabbed = false;
qCDebug(lcQpaXInput, "XInput 2.2: failed to grab events for device %d on window %x (result %d)", id, w, result);
}
}
}
XIFreeDeviceInfo(info);
m_xiGrab = grabbed;
return grabbed;
}
#endif // XCB_USE_XINPUT22
void QXcbConnection::xi2HandleHierachyEvent(void *event)
{
xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
// We only care about hotplugged devices
if (!(xiEvent->flags & (XISlaveRemoved | XISlaveAdded)))
return;
xi2SetupDevices();
// Reselect events for all event-listening windows.
Q_FOREACH (xcb_window_t window, m_windowMapper.keys()) {
xi2Select(window);
}
}
void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
{
xXIDeviceChangedEvent *xiEvent = reinterpret_cast<xXIDeviceChangedEvent *>(event);
// ### If a slave device changes (XIDeviceChange), we should probably run setup on it again.
if (xiEvent->reason != XISlaveSwitch)
return;
#ifdef XCB_USE_XINPUT21
// This code handles broken scrolling device drivers that reset absolute positions
// when they are made active. Whenever a new slave device is made active the
// primary pointer sends a DeviceChanged event with XISlaveSwitch, and the new
// active slave in sourceid.
QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->sourceid);
if (device == m_scrollingDevices.end())
return;
int nrDevices = 0;
XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), xiEvent->sourceid, &nrDevices);
if (nrDevices <= 0) {
qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", xiEvent->sourceid);
return;
}
updateScrollingDevice(*device, xiDeviceInfo->num_classes, xiDeviceInfo->classes);
XIFreeDeviceInfo(xiDeviceInfo);
#endif
}
void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int num_classes, void *classInfo)
{
#ifdef XCB_USE_XINPUT21
XIAnyClassInfo **classes = reinterpret_cast<XIAnyClassInfo**>(classInfo);
QPointF lastScrollPosition;
if (lcQpaXInput().isDebugEnabled())
lastScrollPosition = scrollingDevice.lastScrollPosition;
for (int c = 0; c < num_classes; ++c) {
if (classes[c]->type == XIValuatorClass) {
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classes[c]);
const int valuatorAtom = qatom(vci->label);
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
scrollingDevice.lastScrollPosition.setX(vci->value);
else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
scrollingDevice.lastScrollPosition.setY(vci->value);
}
}
if (lcQpaXInput().isDebugEnabled() && lastScrollPosition != scrollingDevice.lastScrollPosition)
qCDebug(lcQpaXInput, "scrolling device %d moved from (%f, %f) to (%f, %f)", scrollingDevice.deviceId,
lastScrollPosition.x(), lastScrollPosition.y(),
scrollingDevice.lastScrollPosition.x(),
scrollingDevice.lastScrollPosition.y());
#else
Q_UNUSED(scrollingDevice);
Q_UNUSED(num_classes);
Q_UNUSED(classInfo);
#endif
}
Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
{
switch (b) {
case 1: return Qt::LeftButton;
case 2: return Qt::MiddleButton;
case 3: return Qt::RightButton;
// 4-7 are for scrolling
default: break;
}
if (b >= 8 && b <= Qt::MaxMouseButton)
return static_cast<Qt::MouseButton>(Qt::BackButton << (b - 8));
return Qt::NoButton;
}
Qt::MouseButtons QXcbConnection::xiToQtMouseButtons(xXIDeviceEvent *xiDeviceEvent)
{
/**
* WARNING: we haven't tested this method on different tablets. For basic tablets
* without any button remapping it seems to work. For more complex configurations
* I just don't know. Right now it is only safe to check if buttons is
* equal to Qt::NoButton!
*/
int len = xiDeviceEvent->buttons_len;
const uint32_t *buttons = reinterpret_cast<const quint32*>(&xiDeviceEvent[1]);;
Qt::MouseButtons qtbuttons = Qt::NoButton;
const int numBits = len * 32;
for (int i = 1; i < numBits; i++) {
const int index = (i) / 32;
const int offset = (i) % 32;
const bool isActive = buttons[index] & (1 << offset);
if (isActive) {
qtbuttons |= xiToQtMouseButton(i);
}
}
return qtbuttons;
}
static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
// keep in sync with wacom_intuos_inout() in Linux kernel driver wacom_wac.c
switch (toolId) {
case 0xd12:
case 0x912:
case 0x112:
case 0x913: /* Intuos3 Airbrush */
case 0x91b: /* Intuos3 Airbrush Eraser */
case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
return QTabletEvent::Airbrush;
case 0x007: /* Mouse 4D and 2D */
case 0x09c:
case 0x094:
return QTabletEvent::FourDMouse;
case 0x017: /* Intuos3 2D Mouse */
case 0x806: /* Intuos4 Mouse */
case 0x096: /* Lens cursor */
case 0x097: /* Intuos3 Lens cursor */
case 0x006: /* Intuos4 Lens cursor */
return QTabletEvent::Puck;
case 0x885: /* Intuos3 Art Pen (Marker Pen) */
case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
return QTabletEvent::RotationStylus;
case 0:
return QTabletEvent::NoDevice;
}
return QTabletEvent::Stylus; // Safe default assumption if nonzero
}
#ifndef QT_NO_TABLETEVENT
bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, QWindow *window)
{
bool handled = true;
Display *xDisplay = static_cast<Display *>(m_xlib_display);
xXIGenericDeviceEvent *xiEvent = static_cast<xXIGenericDeviceEvent *>(event);
xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(xiEvent);
/**
* On some systems we can lose tablet button release event if the tablet
* was "closing a popup window by clicking somewhere outside the app
* window". It means that we get a tablet press event, but get absolutely
* no tablet release event. That can cause quite a lot of troubles, so here we
* check if reported button state is consistent and if not, just reset it.
*
* WARNING: We haven't tested xiToQtMouseButtons() functions on all the
* tablet models and configurations, so at the moment we rely only
* on its Qt::NoButton state. If people test it on custom tablet
* button configurations, we can just stop tracking
* tabletData->buttons and use this mapping instead.
*/
if (xiEvent->evtype == XI_Motion ||
xiEvent->evtype == XI_ButtonPress ||
xiEvent->evtype == XI_ButtonRelease) {
Qt::MouseButtons expectedButtons = xiToQtMouseButtons(xiDeviceEvent);
if (expectedButtons != tabletData->buttons) {
if (expectedButtons == Qt::NoButton) {
tabletData->buttons = expectedButtons;
} else {
qWarning() << "===";
qWarning() << "WARNING: Tracked tablet buttons are not consistent!";
qWarning() << " " << ppVar(tabletData->buttons);
qWarning() << " " << ppVar(expectedButtons);
}
}
}
switch (xiEvent->evtype) {
case XI_ButtonPress: {
Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
tabletData->buttons |= b;
xi2ReportTabletEvent(*tabletData, xiEvent);
break;
}
case XI_ButtonRelease: {
Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
tabletData->buttons ^= b;
xi2ReportTabletEvent(*tabletData, xiEvent);
break;
}
case XI_Motion:
xi2ReportTabletEvent(*tabletData, xiEvent);
break;
case XI_PropertyEvent: {
// This is the wacom driver's way of reporting tool proximity.
// The evdev driver doesn't do it this way.
xXIPropertyEvent *ev = reinterpret_cast<xXIPropertyEvent *>(event);
if (ev->what == XIPropertyModified) {
if (ev->property == atom(QXcbAtom::WacomSerialIDs)) {
enum WacomSerialIndex {
_WACSER_USB_ID = 0,
_WACSER_LAST_TOOL_SERIAL,
_WACSER_LAST_TOOL_ID,
_WACSER_TOOL_SERIAL,
_WACSER_TOOL_ID,
_WACSER_COUNT
};
Atom propType;
int propFormat;
unsigned long numItems, bytesAfter;
unsigned char *data;
if (XIGetProperty(xDisplay, tabletData->deviceId, ev->property, 0, 100,
0, AnyPropertyType, &propType, &propFormat,
&numItems, &bytesAfter, &data) == Success) {
if (propType == atom(QXcbAtom::INTEGER) && propFormat == 32 && numItems == _WACSER_COUNT) {
quint32 *ptr = reinterpret_cast<quint32 *>(data);
quint32 tool = ptr[_WACSER_TOOL_ID];
// Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/
// e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1
if (!tool && ptr[_WACSER_TOOL_SERIAL])
tool = ptr[_WACSER_TOOL_SERIAL];
// The property change event informs us which tool is in proximity or which one left proximity.
if (tool) {
tabletData->inProximity = true;
tabletData->tool = toolIdToTabletDevice(tool);
tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_TOOL_SERIAL]);
QWindowSystemInterface::handleTabletEnterProximityEvent(tabletData->tool,
tabletData->pointerType,
tabletData->serialId);
} else {
tabletData->inProximity = false;
tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_ID]);
// Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/
// e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1
if (!tabletData->tool)
tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_SERIAL]);
tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_LAST_TOOL_SERIAL]);
QWindowSystemInterface::handleTabletLeaveProximityEvent(tabletData->tool,
tabletData->pointerType,
tabletData->serialId);
}
// TODO maybe have a hash of tabletData->deviceId to device data so we can
// look up the tablet name here, and distinguish multiple tablets
qCDebug(lcQpaXInput, "XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d",
tabletData->deviceId, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool);
}
XFree(data);
}
}
}
break;
}
default:
handled = false;
break;
}
#ifdef XCB_USE_XINPUT22
// Synthesize mouse events since otherwise there are no mouse events from
// the pen on the XI 2.2+ path.
if (xi2MouseEvents() && window) {
// DK: I have no idea why this line was introduced in Qt5.5!
//eventListener->handleXIMouseEvent(reinterpret_cast<xcb_ge_event_t *>(event));
}
#else
Q_UNUSED(eventListener);
#endif
return handled;
}
inline qreal scaleOneValuator(qreal normValue, qreal screenMin, qreal screenSize)
{
return screenMin + normValue * screenSize;
}
void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event)
{
xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event);
QWindow *window = windowFromId(ev->event);
if (!window) return;
const double scale = 65536.0;
QPointF local(ev->event_x / scale, ev->event_y / scale);
QPointF global(ev->root_x / scale, ev->root_y / scale);
double pressure = 0, rotation = 0, tangentialPressure = 0;
int xTilt = 0, yTilt = 0;
QRect screenArea = qApp->desktop()->rect();
for (QHash<int, TabletData::ValuatorClassInfo>::iterator it = tabletData.valuatorInfo.begin(),
ite = tabletData.valuatorInfo.end(); it != ite; ++it) {
int valuator = it.key();
TabletData::ValuatorClassInfo &classInfo(it.value());
xi2GetValuatorValueIfSet(event, classInfo.number, &classInfo.curVal);
double normalizedValue = (classInfo.curVal - classInfo.minVal) / (classInfo.maxVal - classInfo.minVal);
switch (valuator) {
case QXcbAtom::AbsX: {
const qreal value = scaleOneValuator(normalizedValue, screenArea.x(), screenArea.width());
const qreal offset = local.x() - global.x();
global.rx() = value;
local.rx() = value + offset;
break;
}
case QXcbAtom::AbsY: {
qreal value = scaleOneValuator(normalizedValue, screenArea.y(), screenArea.height());
qreal offset = local.y() - global.y();
global.ry() = value;
local.ry() = value + offset;
break;
}
case QXcbAtom::AbsPressure:
pressure = normalizedValue;
break;
case QXcbAtom::AbsTiltX:
xTilt = classInfo.curVal;
break;
case QXcbAtom::AbsTiltY:
yTilt = classInfo.curVal;
break;
case QXcbAtom::AbsWheel:
switch (tabletData.tool) {
case QTabletEvent::Airbrush:
tangentialPressure = normalizedValue * 2.0 - 1.0; // Convert 0..1 range to -1..+1 range
break;
case QTabletEvent::RotationStylus:
rotation = normalizedValue * 360.0 - 180.0; // Convert 0..1 range to -180..+180 degrees
break;
default: // Other types of styli do not use this valuator
break;
}
break;
default:
break;
}
}
if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf",
tabletData.deviceId, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail,
fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y),
fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y),
(int)tabletData.buttons, pressure, xTilt, yTilt, rotation);
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
QWindowSystemInterface::handleTabletEvent(window, local, global,
tabletData.tool, tabletData.pointerType,
tabletData.buttons, pressure,
xTilt, yTilt, tangentialPressure,
rotation, 0, tabletData.serialId,
modifiers);
}
void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
{
#ifdef XCB_USE_XINPUT21
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
if (xiEvent->evtype == XI_Motion && scrollingDevice.orientations) {
xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
if (QWindow *platformWindow = windowFromId(xiDeviceEvent->event)) {
QPoint rawDelta;
QPoint angleDelta;
double value;
if (scrollingDevice.orientations & Qt::Vertical) {
if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice.verticalIndex, &value)) {
double delta = scrollingDevice.lastScrollPosition.y() - value;
scrollingDevice.lastScrollPosition.setY(value);
angleDelta.setY((delta / scrollingDevice.verticalIncrement) * 120);
// We do not set "pixel" delta if it is only measured in ticks.
if (scrollingDevice.verticalIncrement > 1)
rawDelta.setY(delta);
}
}
if (scrollingDevice.orientations & Qt::Horizontal) {
if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice.horizontalIndex, &value)) {
double delta = scrollingDevice.lastScrollPosition.x() - value;
scrollingDevice.lastScrollPosition.setX(value);
angleDelta.setX((delta / scrollingDevice.horizontalIncrement) * 120);
// We do not set "pixel" delta if it is only measured in ticks.
if (scrollingDevice.horizontalIncrement > 1)
rawDelta.setX(delta);
}
}
if (!angleDelta.isNull()) {
const int dpr = int(platformWindow->devicePixelRatio());
QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr);
QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr);
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
if (modifiers & Qt::AltModifier) {
std::swap(angleDelta.rx(), angleDelta.ry());
std::swap(rawDelta.rx(), rawDelta.ry());
}
QWindowSystemInterface::handleWheelEvent(platformWindow, xiEvent->time, local, global, rawDelta, angleDelta, modifiers);
}
}
} else if (xiEvent->evtype == XI_ButtonRelease && scrollingDevice.legacyOrientations) {
xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
if (QWindow *platformWindow = windowFromId(xiDeviceEvent->event)) {
QPoint angleDelta;
if (scrollingDevice.legacyOrientations & Qt::Vertical) {
if (xiDeviceEvent->detail == 4)
angleDelta.setY(120);
else if (xiDeviceEvent->detail == 5)
angleDelta.setY(-120);
}
if (scrollingDevice.legacyOrientations & Qt::Horizontal) {
if (xiDeviceEvent->detail == 6)
angleDelta.setX(120);
else if (xiDeviceEvent->detail == 7)
angleDelta.setX(-120);
}
if (!angleDelta.isNull()) {
const int dpr = int(platformWindow->devicePixelRatio());
QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr);
QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr);
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
if (modifiers & Qt::AltModifier)
std::swap(angleDelta.rx(), angleDelta.ry());
QWindowSystemInterface::handleWheelEvent(platformWindow, xiEvent->time, local, global, QPoint(), angleDelta, modifiers);
}
}
}
#else
Q_UNUSED(event);
Q_UNUSED(scrollingDevice);
#endif // XCB_USE_XINPUT21
}
#endif // QT_NO_TABLETEVENT
#endif // XCB_USE_XINPUT2
diff --git a/libs/ui/kis_animation_cache_populator.cpp b/libs/ui/kis_animation_cache_populator.cpp
index 6a2c0f6349..5c5d384590 100644
--- a/libs/ui/kis_animation_cache_populator.cpp
+++ b/libs/ui/kis_animation_cache_populator.cpp
@@ -1,403 +1,401 @@
/*
* Copyright (c) 2015 Jouni Pentikäinen <joupent@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_animation_cache_populator.h"
#include <functional>
#include <QTimer>
#include <QMutex>
#include <QtConcurrent>
#include "KisPart.h"
#include "KisDocument.h"
#include "kis_image.h"
#include "kis_image_animation_interface.h"
#include "kis_canvas2.h"
#include "kis_time_range.h"
#include "kis_animation_frame_cache.h"
#include "kis_update_info.h"
#include "kis_signal_auto_connection.h"
#include "kis_idle_watcher.h"
#include "KisViewManager.h"
#include "kis_node_manager.h"
#include "kis_keyframe_channel.h"
struct KisAnimationCachePopulator::Private
{
KisAnimationCachePopulator *q;
KisPart *part;
QTimer timer;
/**
* Counts up the number of subsequent times Krita has been detected idle.
*/
int idleCounter;
static const int IDLE_COUNT_THRESHOLD = 4;
static const int IDLE_CHECK_INTERVAL = 500;
static const int WAITING_FOR_FRAME_TIMEOUT = 10000;
static const int BETWEEN_FRAMES_INTERVAL = 10;
int requestedFrame;
KisAnimationFrameCacheSP requestCache;
KisOpenGLUpdateInfoSP requestInfo;
KisSignalAutoConnectionsStore imageRequestConnections;
QFutureWatcher<void> infoConversionWatcher;
enum State {
NotWaitingForAnything,
WaitingForIdle,
WaitingForFrame,
WaitingForConvertedFrame,
BetweenFrames
};
State state;
QMutex mutex;
Private(KisAnimationCachePopulator *_q, KisPart *_part)
: q(_q),
part(_part),
idleCounter(0),
requestedFrame(-1),
state(WaitingForIdle)
{
timer.setSingleShot(true);
connect(&infoConversionWatcher, SIGNAL(finished()), q, SLOT(slotInfoConverted()));
}
static void processFrameInfo(KisOpenGLUpdateInfoSP info) {
if (info->needsConversion()) {
info->convertColorSpace();
}
}
void frameReceived(int frame)
{
if (frame != requestedFrame) return;
imageRequestConnections.clear();
requestInfo = requestCache->fetchFrameData(frame);
/**
* This method is called from the context of the image worker
* threads, so we cannot modify timers here. Therefore the
* timers reset and the conversion request will be issued in
* the main GUI thread.
*/
emit q->sigPrivateStartWaitingForConvertedFrame();
}
void infoConverted() {
KIS_ASSERT_RECOVER(requestInfo && requestCache) {
enterState(WaitingForIdle);
return;
}
requestCache->addConvertedFrameData(requestInfo, requestedFrame);
requestedFrame = 0;
requestCache = 0;
requestInfo = 0;
enterState(BetweenFrames);
}
void timerTimeout() {
switch (state) {
case WaitingForIdle:
case BetweenFrames:
generateIfIdle();
break;
case WaitingForFrame:
// Request timed out :(
imageRequestConnections.clear();
enterState(WaitingForIdle);
break;
case WaitingForConvertedFrame:
KIS_ASSERT_RECOVER_NOOP(0 && "WaitingForConvertedFrame cannot have a timeout. Just skip this message and report a bug");
break;
case NotWaitingForAnything:
KIS_ASSERT_RECOVER_NOOP(0 && "NotWaitingForAnything cannot have a timeout. Just skip this message and report a bug");
break;
}
}
void generateIfIdle()
{
if (part->idleWatcher()->isIdle()) {
idleCounter++;
if (idleCounter >= IDLE_COUNT_THRESHOLD) {
if (!tryRequestGeneration()) {
enterState(NotWaitingForAnything);
}
return;
}
} else {
idleCounter = 0;
}
enterState(WaitingForIdle);
}
bool tryRequestGeneration()
{
// Prioritize the active document
KisAnimationFrameCacheSP activeDocumentCache = KisAnimationFrameCacheSP(0);
KisMainWindow *activeWindow = part->currentMainwindow();
if (activeWindow && activeWindow->activeView()) {
KisCanvas2 *activeCanvas = activeWindow->activeView()->canvasBase();
if (activeCanvas && activeCanvas->frameCache()) {
activeDocumentCache = activeCanvas->frameCache();
// Let's skip frames affected by changes to the active node (on the active document)
// This avoids constant invalidation and regeneration while drawing
KisNodeSP activeNode = activeCanvas->viewManager()->nodeManager()->activeNode();
KisTimeRange skipRange;
if (activeNode) {
int currentTime = activeCanvas->currentImage()->animationInterface()->currentUITime();
const QList<KisKeyframeChannel*> channels =
activeNode->keyframeChannels();
if (!channels.isEmpty()) {
Q_FOREACH (const KisKeyframeChannel *channel, channels) {
skipRange |= channel->affectedFrames(currentTime);
}
} else {
skipRange = KisTimeRange::infinite(0);
}
}
- if (!skipRange.isInfinite()) {
- bool requested = tryRequestGeneration(activeDocumentCache, skipRange);
- if (requested) return true;
- }
+ bool requested = tryRequestGeneration(activeDocumentCache, skipRange);
+ if (requested) return true;
}
}
QList<KisAnimationFrameCache*> caches = KisAnimationFrameCache::caches();
KisAnimationFrameCache *cache;
Q_FOREACH (cache, caches) {
if (cache == activeDocumentCache.data()) {
// We already handled this one...
continue;
}
bool requested = tryRequestGeneration(cache, KisTimeRange());
if (requested) return true;
}
return false;
}
bool tryRequestGeneration(KisAnimationFrameCacheSP cache, KisTimeRange skipRange)
{
KisImageSP image = cache->image();
if (!image) return false;
KisImageAnimationInterface *animation = image->animationInterface();
KisTimeRange currentRange = animation->fullClipRange();
if (!animation->hasAnimation()) return false;
if (currentRange.isValid()) {
Q_ASSERT(!currentRange.isInfinite());
// TODO: optimize check for fully-cached case
for (int frame = currentRange.start(); frame <= currentRange.end(); frame++) {
if (skipRange.contains(frame)) {
if (skipRange.isInfinite()) {
break;
} else {
frame = skipRange.end();
continue;
}
}
if (cache->frameStatus(frame) != KisAnimationFrameCache::Cached) {
return regenerate(cache, frame);
}
}
}
return false;
}
bool regenerate(KisAnimationFrameCacheSP cache, int frame)
{
if (state == WaitingForFrame || state == WaitingForConvertedFrame) {
// Already busy, deny request
return false;
}
KIS_ASSERT_RECOVER_NOOP(QThread::currentThread() == q->thread());
KisImageSP image = cache->image();
requestCache = cache;
requestedFrame = frame;
imageRequestConnections.clear();
imageRequestConnections.addConnection(
image->animationInterface(), SIGNAL(sigFrameReady(int)),
q, SLOT(slotFrameReady(int)),
Qt::DirectConnection);
imageRequestConnections.addConnection(
image->animationInterface(), SIGNAL(sigFrameCancelled()),
q, SLOT(slotFrameCancelled()),
Qt::AutoConnection);
/**
* We should enter the state before the frame is
* requested. Otherwise the signal may come earlier than we
* enter it.
*/
enterState(WaitingForFrame);
image->animationInterface()->requestFrameRegeneration(frame, image->bounds());
return true;
}
QString debugStateToString(State newState) {
QString str = "<unknown>";
switch (newState) {
case WaitingForIdle:
str = "WaitingForIdle";
break;
case WaitingForFrame:
str = "WaitingForFrame";
break;
case NotWaitingForAnything:
str = "NotWaitingForAnything";
break;
case WaitingForConvertedFrame:
str = "WaitingForConvertedFrame";
break;
case BetweenFrames:
str = "BetweenFrames";
break;
}
return str;
}
void enterState(State newState)
{
state = newState;
int timerTimeout = -1;
switch (state) {
case WaitingForIdle:
timerTimeout = IDLE_CHECK_INTERVAL;
break;
case WaitingForFrame:
timerTimeout = WAITING_FOR_FRAME_TIMEOUT;
break;
case NotWaitingForAnything:
case WaitingForConvertedFrame:
// frame conversion cannot be cancelled,
// so there is no timeout
timerTimeout = -1;
break;
case BetweenFrames:
timerTimeout = BETWEEN_FRAMES_INTERVAL;
break;
}
if (timerTimeout >= 0) {
timer.start(timerTimeout);
} else {
timer.stop();
}
}
};
KisAnimationCachePopulator::KisAnimationCachePopulator(KisPart *part)
: m_d(new Private(this, part))
{
connect(&m_d->timer, SIGNAL(timeout()), this, SLOT(slotTimer()));
connect(this, SIGNAL(sigPrivateStartWaitingForConvertedFrame()), SLOT(slotPrivateStartWaitingForConvertedFrame()));
}
KisAnimationCachePopulator::~KisAnimationCachePopulator()
{}
bool KisAnimationCachePopulator::regenerate(KisAnimationFrameCacheSP cache, int frame)
{
return m_d->regenerate(cache, frame);
}
void KisAnimationCachePopulator::slotStart()
{
m_d->timer.start();
}
void KisAnimationCachePopulator::slotTimer()
{
m_d->timerTimeout();
}
void KisAnimationCachePopulator::slotFrameReady(int frame)
{
m_d->frameReceived(frame);
}
void KisAnimationCachePopulator::slotFrameCancelled()
{
KIS_ASSERT_RECOVER_RETURN(m_d->state == Private::WaitingForFrame);
m_d->timer.stop();
m_d->imageRequestConnections.clear();
m_d->enterState(Private::NotWaitingForAnything);
}
void KisAnimationCachePopulator::slotInfoConverted()
{
m_d->infoConverted();
}
void KisAnimationCachePopulator::slotRequestRegeneration()
{
m_d->enterState(Private::WaitingForIdle);
}
void KisAnimationCachePopulator::slotPrivateStartWaitingForConvertedFrame()
{
KIS_ASSERT_RECOVER_RETURN(m_d->requestInfo);
m_d->enterState(Private::WaitingForConvertedFrame);
QFuture<void> requestFuture =
QtConcurrent::run(
std::bind(&KisAnimationCachePopulator::Private::processFrameInfo,
m_d->requestInfo));
m_d->infoConversionWatcher.setFuture(requestFuture);
}
diff --git a/libs/ui/kis_animation_exporter.cpp b/libs/ui/kis_animation_exporter.cpp
index 16ed459d5f..513a826ec6 100644
--- a/libs/ui/kis_animation_exporter.cpp
+++ b/libs/ui/kis_animation_exporter.cpp
@@ -1,334 +1,334 @@
/*
* Copyright (c) 2015 Jouni Pentikäinen <joupent@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_animation_exporter.h"
#include <QDesktopServices>
#include <QProgressDialog>
#include <KisMimeDatabase.h>
#include <QEventLoop>
#include "KoFileDialog.h"
#include "KisDocument.h"
#include "kis_image.h"
#include "KisImportExportManager.h"
#include "kis_image_animation_interface.h"
#include "KisPart.h"
#include "KisMainWindow.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_time_range.h"
#include "kis_painter.h"
#include "kis_image_lock_hijacker.h"
struct KisAnimationExporterUI::Private
{
QWidget *parentWidget;
KisAnimationExportSaver *exporter;
Private(QWidget *parent)
: parentWidget(parent),
exporter(0)
{}
};
KisAnimationExporterUI::KisAnimationExporterUI(QWidget *parent)
: m_d(new Private(parent))
{
}
KisAnimationExporterUI::~KisAnimationExporterUI()
{
if (m_d->exporter) {
delete m_d->exporter;
}
}
KisImportExportFilter::ConversionStatus KisAnimationExporterUI::exportSequence(KisDocument *document)
{
KoFileDialog dialog(m_d->parentWidget, KoFileDialog::SaveFile, "exportsequence");
dialog.setCaption(i18n("Export sequence"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export));
QString filename = dialog.filename();
// if the user presses cancel, it returns empty
if (filename.isEmpty()) return KisImportExportFilter::UserCancelled;
const KisTimeRange fullClipRange = document->image()->animationInterface()->fullClipRange();
int firstFrame = fullClipRange.start();
int lastFrame = fullClipRange.end();
m_d->exporter = new KisAnimationExportSaver(document, filename, firstFrame, lastFrame);
return m_d->exporter->exportAnimation();
}
struct KisAnimationExporter::Private
{
Private(KisDocument *document, int fromTime, int toTime)
: document(document)
, image(document->image())
, firstFrame(fromTime)
, lastFrame(toTime)
, currentFrame(-1)
, batchMode(document->fileBatchMode())
, isCancelled(false)
, status(KisImportExportFilter::OK)
, tmpDevice(new KisPaintDevice(image->colorSpace()))
{
}
KisDocument *document;
KisImageWSP image;
int firstFrame;
int lastFrame;
int currentFrame;
bool batchMode;
bool isCancelled;
KisImportExportFilter::ConversionStatus status;
SaveFrameCallback saveFrameCallback;
KisPaintDeviceSP tmpDevice;
KisPropertiesConfigurationSP exportConfiguration;
};
KisAnimationExporter::KisAnimationExporter(KisDocument *document, int fromTime, int toTime)
: m_d(new Private(document, fromTime, toTime))
{
connect(m_d->image->animationInterface(), SIGNAL(sigFrameReady(int)),
this, SLOT(frameReadyToCopy(int)), Qt::DirectConnection);
connect(this, SIGNAL(sigFrameReadyToSave()),
this, SLOT(frameReadyToSave()), Qt::QueuedConnection);
}
KisAnimationExporter::~KisAnimationExporter()
{
}
void KisAnimationExporter::setExportConfiguration(KisPropertiesConfigurationSP exportConfiguration)
{
m_d->exportConfiguration = exportConfiguration;
}
void KisAnimationExporter::setSaveFrameCallback(SaveFrameCallback func)
{
m_d->saveFrameCallback = func;
}
KisImportExportFilter::ConversionStatus KisAnimationExporter::exportAnimation()
{
QScopedPointer<QProgressDialog> progress;
if (!m_d->batchMode) {
QString message = i18n("Export frames...");
progress.reset(new QProgressDialog(message, "", 0, 0, KisPart::instance()->currentMainwindow()));
progress->setWindowModality(Qt::ApplicationModal);
progress->setCancelButton(0);
progress->setMinimumDuration(0);
progress->setValue(0);
emit m_d->document->statusBarMessage(message);
emit m_d->document->sigProgress(0);
connect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel()));
}
/**
* HACK ALERT: Here we remove the image lock! We do it in a GUI
* thread under the barrier lock held, so it is
* guaranteed no other stroke will accidentally be
* started by this. And showing an app-modal dialog to
* the user will prevent him from doing anything
* nasty.
*/
KisImageLockHijacker badGuy(m_d->image);
Q_UNUSED(badGuy);
KIS_ASSERT_RECOVER(!m_d->image->locked()) { return KisImportExportFilter::InternalError; }
m_d->status = KisImportExportFilter::OK;
m_d->currentFrame = m_d->firstFrame;
m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds());
QEventLoop loop;
loop.connect(this, SIGNAL(sigFinished()), SLOT(quit()));
loop.exec();
if (!m_d->batchMode) {
disconnect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel()));
emit m_d->document->sigProgress(100);
emit m_d->document->clearStatusBarMessage();
progress.reset();
}
return m_d->status;
}
void KisAnimationExporter::cancel()
{
m_d->isCancelled = true;
}
void KisAnimationExporter::frameReadyToCopy(int time)
{
if (time != m_d->currentFrame) return;
QRect rc = m_d->image->bounds();
KisPainter::copyAreaOptimized(rc.topLeft(), m_d->image->projection(), m_d->tmpDevice, rc);
emit sigFrameReadyToSave();
}
void KisAnimationExporter::frameReadyToSave()
{
KIS_ASSERT_RECOVER(m_d->saveFrameCallback) {
m_d->status = KisImportExportFilter::InternalError;
emit sigFinished();
return;
}
if (m_d->isCancelled) {
m_d->status = KisImportExportFilter::UserCancelled;
emit sigFinished();
return;
}
KisImportExportFilter::ConversionStatus result =
KisImportExportFilter::OK;
int time = m_d->currentFrame;
result = m_d->saveFrameCallback(time, m_d->tmpDevice, m_d->exportConfiguration);
if (!m_d->batchMode) {
emit m_d->document->sigProgress((time - m_d->firstFrame) * 100 /
(m_d->lastFrame - m_d->firstFrame));
}
qDebug() << result << time << m_d->lastFrame;
if (result == KisImportExportFilter::OK && time < m_d->lastFrame) {
m_d->currentFrame = time + 1;
m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds());
} else {
emit sigFinished();
}
}
struct KisAnimationExportSaver::Private
{
Private(KisDocument *document, int fromTime, int toTime, int _sequenceNumberingOffset)
: document(document)
, image(document->image())
, firstFrame(fromTime)
, lastFrame(toTime)
, sequenceNumberingOffset(_sequenceNumberingOffset)
, tmpDoc(KisPart::instance()->createDocument())
, exporter(document, fromTime, toTime)
{
- tmpDoc->setAutoSave(0);
+ tmpDoc->setAutoSaveDelay(0);
tmpImage = new KisImage(tmpDoc->createUndoStore(),
image->bounds().width(),
image->bounds().height(),
image->colorSpace(),
QString());
tmpImage->setResolution(image->xRes(), image->yRes());
tmpDoc->setCurrentImage(tmpImage);
KisPaintLayer* paintLayer = new KisPaintLayer(tmpImage, "paint device", 255);
tmpImage->addNode(paintLayer, tmpImage->rootLayer(), KisLayerSP(0));
tmpDevice = paintLayer->paintDevice();
}
KisDocument *document;
KisImageWSP image;
int firstFrame;
int lastFrame;
int sequenceNumberingOffset;
QScopedPointer<KisDocument> tmpDoc;
KisImageSP tmpImage;
KisPaintDeviceSP tmpDevice;
KisAnimationExporter exporter;
QString filenamePrefix;
QString filenameSuffix;
};
KisAnimationExportSaver::KisAnimationExportSaver(KisDocument *document, const QString &baseFilename, int fromTime, int toTime, int sequenceNumberingOffset)
: m_d(new Private(document, fromTime, toTime, sequenceNumberingOffset))
{
int baseLength = baseFilename.lastIndexOf(".");
if (baseLength > -1) {
m_d->filenamePrefix = baseFilename.left(baseLength);
m_d->filenameSuffix = baseFilename.right(baseFilename.length() - baseLength);
} else {
m_d->filenamePrefix = baseFilename;
}
QString mimefilter = KisMimeDatabase::mimeTypeForFile(baseFilename);
m_d->tmpDoc->setOutputMimeType(mimefilter.toLatin1());
m_d->tmpDoc->setFileBatchMode(true);
using namespace std::placeholders; // For _1 placeholder
m_d->exporter.setSaveFrameCallback(std::bind(&KisAnimationExportSaver::saveFrameCallback, this, _1, _2, _3));
}
KisAnimationExportSaver::~KisAnimationExportSaver()
{
}
KisImportExportFilter::ConversionStatus KisAnimationExportSaver::exportAnimation(KisPropertiesConfigurationSP cfg)
{
m_d->exporter.setExportConfiguration(cfg);
return m_d->exporter.exportAnimation();
}
KisImportExportFilter::ConversionStatus KisAnimationExportSaver::saveFrameCallback(int time, KisPaintDeviceSP frame, KisPropertiesConfigurationSP exportConfiguration)
{
KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK;
QString frameNumber = QString("%1").arg(time + m_d->sequenceNumberingOffset, 4, 10, QChar('0'));
QString filename = m_d->filenamePrefix + frameNumber + m_d->filenameSuffix;
QRect rc = m_d->image->bounds();
KisPainter::copyAreaOptimized(rc.topLeft(), frame, m_d->tmpDevice, rc);
if (!m_d->tmpDoc->exportDocument(QUrl::fromLocalFile(filename), exportConfiguration)) {
status = KisImportExportFilter::InternalError;
}
return status;
}
QString KisAnimationExportSaver::savedFilesMask() const
{
return m_d->filenamePrefix + "%04d" + m_d->filenameSuffix;
}
diff --git a/libs/ui/kis_async_action_feedback.cpp b/libs/ui/kis_async_action_feedback.cpp
deleted file mode 100644
index 99b76aab57..0000000000
--- a/libs/ui/kis_async_action_feedback.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2016 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_action_feedback.h"
-
-#include <QtConcurrent>
-#include <QProgressDialog>
-
-
-struct KisAsyncActionFeedback::Private
-{
- QScopedPointer<QProgressDialog> progress;
-};
-
-KisAsyncActionFeedback::KisAsyncActionFeedback(const QString &message, QWidget *parent)
- : m_d(new Private)
-{
- m_d->progress.reset(new QProgressDialog(message, "", 0, 0, parent));
- m_d->progress->setWindowModality(Qt::ApplicationModal);
- m_d->progress->setCancelButton(0);
- m_d->progress->setMinimumDuration(1000);
- m_d->progress->setValue(0);
-}
-
-KisAsyncActionFeedback::~KisAsyncActionFeedback()
-{
-}
-
-bool KisAsyncActionFeedback::runAction(std::function<bool()> func)
-{
- QFuture<bool> result = QtConcurrent::run(func);
- QFutureWatcher<bool> watcher;
- watcher.setFuture(result);
-
- while (watcher.isRunning()) {
- qApp->processEvents();
- }
-
- watcher.waitForFinished();
- return watcher.result();
-}
diff --git a/libs/ui/kis_async_action_feedback.h b/libs/ui/kis_async_action_feedback.h
deleted file mode 100644
index 5b8b111b75..0000000000
--- a/libs/ui/kis_async_action_feedback.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2016 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_ASYNC_ACTION_FEEDBACK_H
-#define __KIS_ASYNC_ACTION_FEEDBACK_H
-
-#include <QScopedPointer>
-#include <functional>
-
-class QWidget;
-
-class KisAsyncActionFeedback
-{
-public:
- KisAsyncActionFeedback(const QString &message, QWidget *parent);
- ~KisAsyncActionFeedback();
-
- bool runAction(std::function<bool()> func);
-
-private:
- struct Private;
- const QScopedPointer<Private> m_d;
-};
-
-#endif /* __KIS_ASYNC_ACTION_FEEDBACK_H */
diff --git a/libs/ui/kis_canvas_resource_provider.h b/libs/ui/kis_canvas_resource_provider.h
index ad27ef9f4d..c99b65f2cd 100644
--- a/libs/ui/kis_canvas_resource_provider.h
+++ b/libs/ui/kis_canvas_resource_provider.h
@@ -1,245 +1,243 @@
/*
* 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_CANVAS_RESOURCE_PROVIDER_H_
#define KIS_CANVAS_RESOURCE_PROVIDER_H_
#include <QObject>
#include <KoColor.h>
#include <KoID.h>
#include <KoCanvasResourceManager.h>
#include "kis_types.h"
#include "kritaui_export.h"
class KisWorkspaceResource;
class KoColorProfile;
class KoAbstractGradient;
class KoResource;
class KoCanvasBase;
class KisViewManager;
class KoPattern;
class KisFilterConfiguration;
#include <kis_abstract_perspective_grid.h>
/**
* KisCanvasResourceProvider contains the per-window current settings that
* influence painting, like paintop, color, gradients and so on.
*/
class KRITAUI_EXPORT KisCanvasResourceProvider : public QObject
{
Q_OBJECT
public:
enum Resources {
HdrExposure = KoCanvasResourceManager::KritaStart + 1,
CurrentPattern,
CurrentGradient,
CurrentDisplayProfile,
- CurrentImage,
CurrentKritaNode,
CurrentPaintOpPreset,
CurrentGeneratorConfiguration,
CurrentCompositeOp,
CurrentEffectiveCompositeOp,
LodAvailability, ///<-user choice
EraserMode,
MirrorHorizontal,
MirrorVertical,
MirrorHorizontalLock,
MirrorVerticalLock,
MirrorVerticalHideDecorations,
MirrorHorizontalHideDecorations,
- MirrorAxesCenter,
Opacity,
Flow,
Size,
HdrGamma,
GlobalAlphaLock,
DisablePressure,
PreviousPaintOpPreset,
EffectiveZoom, ///<-Used only by painting tools for non-displaying purposes
PresetAllowsLod, ///<-combination of the user choice and thechnical abilities of the preset
SelectionAction,
SelectionMode
};
KisCanvasResourceProvider(KisViewManager * view);
~KisCanvasResourceProvider();
void setResourceManager(KoCanvasResourceManager *resourceManager);
KoCanvasResourceManager* resourceManager();
KoCanvasBase * canvas() const;
KoColor bgColor() const;
void setBGColor(const KoColor& c);
KoColor fgColor() const;
void setFGColor(const KoColor& c);
float HDRExposure() const;
void setHDRExposure(float exposure);
float HDRGamma() const;
void setHDRGamma(float gamma);
bool eraserMode() const;
void setEraserMode(bool value);
KoPattern *currentPattern() const;
KoAbstractGradient *currentGradient() const;
KisImageWSP currentImage() const;
KisNodeSP currentNode() const;
KisPaintOpPresetSP currentPreset() const;
void setPaintOpPreset(const KisPaintOpPresetSP preset);
KisPaintOpPresetSP previousPreset() const;
void setPreviousPaintOpPreset(const KisPaintOpPresetSP preset);
void setCurrentCompositeOp(const QString& compositeOp);
QString currentCompositeOp() const;
QList<QPointer<KisAbstractPerspectiveGrid> > perspectiveGrids() const;
void addPerspectiveGrid(KisAbstractPerspectiveGrid*);
void removePerspectiveGrid(KisAbstractPerspectiveGrid*);
void clearPerspectiveGrids();
void setMirrorHorizontal(bool mirrorHorizontal);
bool mirrorHorizontal() const;
void setMirrorVertical(bool mirrorVertical);
bool mirrorVertical() const;
// options for horizontal and vertical mirror toolbar
void setMirrorHorizontalLock(bool isLocked);
bool mirrorHorizontalLock();
void setMirrorVerticalLock(bool isLocked);
bool mirrorVerticalLock();
void setMirrorVerticalHideDecorations(bool hide);
bool mirrorVerticalHideDecorations();
void setMirrorHorizontalHideDecorations(bool hide);
bool mirrorHorizontalHideDecorations();
void mirrorVerticalMoveCanvasToCenter();
void mirrorHorizontalMoveCanvasToCenter();
void setOpacity(qreal opacity);
qreal opacity() const;
void setFlow(qreal opacity);
qreal flow() const;
void setSize(qreal size);
qreal size() const;
void setGlobalAlphaLock(bool lock);
bool globalAlphaLock() const;
void setDisablePressure(bool value);
bool disablePressure() const;
///Notify that the workspace is saved and settings should be saved to it
void notifySavingWorkspace(KisWorkspaceResource* workspace);
///Notify that the workspace is loaded and settings can be read
void notifyLoadingWorkspace(KisWorkspaceResource* workspace);
int selectionAction();
void setSelectionAction(int action);
int selectionMode();
void setSelectionMode(int mode);
public Q_SLOTS:
void slotSetFGColor(const KoColor& c);
void slotSetBGColor(const KoColor& c);
void slotPatternActivated(KoResource *pattern);
void slotGradientActivated(KoResource *gradient);
void slotNodeActivated(const KisNodeSP node);
void slotPainting();
/**
* Set the image size in pixels. The resource provider will store
* the image size in postscript points.
*/
// FIXME: this slot doesn't catch the case when image resolution is changed
void slotImageSizeChanged();
void slotOnScreenResolutionChanged();
// This is a flag 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
void slotResetEnableFGChange(bool);
private Q_SLOTS:
void slotCanvasResourceChanged(int key, const QVariant & res);
Q_SIGNALS:
void sigFGColorChanged(const KoColor &);
void sigBGColorChanged(const KoColor &);
void sigGradientChanged(KoAbstractGradient *);
void sigPatternChanged(KoPattern *);
void sigNodeChanged(const KisNodeSP);
void sigDisplayProfileChanged(const KoColorProfile *);
void sigFGColorUsed(const KoColor&);
void sigOnScreenResolutionChanged(qreal scaleX, qreal scaleY);
void sigOpacityChanged(qreal);
void sigSavingWorkspace(KisWorkspaceResource* workspace);
void sigLoadingWorkspace(KisWorkspaceResource* workspace);
void sigSelectionActionChanged(const int);
void sigSelectionModeChanged(const int);
void mirrorModeChanged();
void moveMirrorVerticalCenter();
void moveMirrorHorizontalCenter();
private:
KisViewManager * m_view;
KoCanvasResourceManager *m_resourceManager;
bool m_fGChanged;
QList<QPointer<KisAbstractPerspectiveGrid> > m_perspectiveGrids;
// This is a flag 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
bool m_enablefGChange;
};
#endif
diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc
index 1631f19778..820a02ccef 100644
--- a/libs/ui/kis_config.cc
+++ b/libs/ui/kis_config.cc
@@ -1,1755 +1,1755 @@
/*
* 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 <QtGlobal>
#include <QApplication>
#include <QDesktopWidget>
#include <QMutex>
#include <QFont>
#include <QThread>
#include <QStringList>
#include <kconfig.h>
#include <KisDocument.h>
#include <KoColor.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoColorProfile.h>
#include <kis_debug.h>
#include <kis_types.h>
#include "kis_canvas_resource_provider.h"
#include "kis_config_notifier.h"
#include "kis_snap_config.h"
#include <config-ocio.h>
#include <kis_color_manager.h>
KisConfig::KisConfig()
: m_cfg( KSharedConfig::openConfig()->group(""))
{
}
KisConfig::~KisConfig()
{
if (qApp->thread() != QThread::currentThread()) {
//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);
}
qreal KisConfig::stackT1(bool defaultValue) const
{
return (defaultValue ? 5 : m_cfg.readEntry("stackT1",5));
}
void KisConfig::setStackT1(int T1)
{
m_cfg.writeEntry("stackT1", T1);
}
qreal 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));
}
qreal 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(qreal 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();
}
bool KisConfig::useEraserBrushOpacity(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useEraserBrushOpacity",false));
}
void KisConfig::setUseEraserBrushOpacity(bool value)
{
m_cfg.writeEntry("useEraserBrushOpacity",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");
//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)
{
if (screen < 0) return 0;
KisConfig cfg;
QString monitorId;
if (KisColorManager::instance()->devices().size() > screen) {
monitorId = cfg.monitorForScreen(screen, KisColorManager::instance()->devices()[screen]);
}
//dbgKrita << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId;
if (monitorId.isEmpty()) {
return 0;
}
QByteArray bytes = KisColorManager::instance()->displayProfile(monitorId);
//dbgKrita << "\tgetScreenProfile()" << bytes.size();
if (bytes.length() > 0) {
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), bytes);
//dbgKrita << "\tKisConfig::getScreenProfile for screen" << screen << profile->name();
return profile;
}
else {
//dbgKrita << "\tCould not get a system monitor profile";
return 0;
}
}
const KoColorProfile *KisConfig::displayProfile(int screen) const
{
if (screen < 0) return 0;
// 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();
//dbgKrita << "KisConfig::displayProfile(). Override X11:" << override;
const KoColorProfile *profile = 0;
if (override) {
//dbgKrita << "\tGoing to get the screen profile";
profile = KisConfig::getScreenProfile(screen);
}
// if it fails. check the configuration
if (!profile || !profile->isSuitableForDisplay()) {
//dbgKrita << "\tGoing to get the monitor profile";
QString monitorProfileName = monitorProfile(screen);
//dbgKrita << "\t\tmonitorProfileName:" << monitorProfileName;
if (!monitorProfileName.isEmpty()) {
profile = KoColorSpaceRegistry::instance()->profileByName(monitorProfileName);
}
if (profile) {
//dbgKrita << "\t\tsuitable for display" << profile->isSuitableForDisplay();
}
else {
//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()) {
//dbgKrita << "\tnothing worked, going to get sRGB built-in";
profile = KoColorSpaceRegistry::instance()->profileByName("sRGB Built-in");
}
if (profile) {
//dbgKrita << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name();
}
else {
//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);
}
bool KisConfig::rulersTrackMouse(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("rulersTrackMouse", true));
}
void KisConfig::setRulersTrackMouse(bool value) const
{
m_cfg.writeEntry("rulersTrackMouse", value);
}
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 (defaultValue) {
return true;
}
//dbgKrita << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS");
QString canvasState = m_cfg.readEntry("canvasState", "OPENGL_SUCCESS");
return (m_cfg.readEntry("useOpenGL", true) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL"));
}
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::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
{
int v = m_cfg.readEntry("gridmainstyle", 0);
v = qBound(0, 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::guidesLineStyle(bool defaultValue) const
{
int v = m_cfg.readEntry("guidesLineStyle", 0);
v = qBound(0, v, 2);
return (defaultValue ? 0 : v);
}
void KisConfig::setGuidesLineStyle(quint32 v) const
{
m_cfg.writeEntry("guidesLineStyle", v);
}
QColor KisConfig::guidesColor(bool defaultValue) const
{
QColor col(99, 99, 99);
return (defaultValue ? col : m_cfg.readEntry("guidesColor", col));
}
void KisConfig::setGuidesColor(const QColor & v) const
{
m_cfg.writeEntry("guidesColor", v);
}
void KisConfig::loadSnapConfig(KisSnapConfig *config, bool defaultValue) const
{
KisSnapConfig defaultConfig(false);
if (defaultValue) {
*config = defaultConfig;
return;
}
config->setOrthogonal(m_cfg.readEntry("globalSnapOrthogonal", defaultConfig.orthogonal()));
config->setNode(m_cfg.readEntry("globalSnapNode", defaultConfig.node()));
config->setExtension(m_cfg.readEntry("globalSnapExtension", defaultConfig.extension()));
config->setIntersection(m_cfg.readEntry("globalSnapIntersection", defaultConfig.intersection()));
config->setBoundingBox(m_cfg.readEntry("globalSnapBoundingBox", defaultConfig.boundingBox()));
config->setImageBounds(m_cfg.readEntry("globalSnapImageBounds", defaultConfig.imageBounds()));
config->setImageCenter(m_cfg.readEntry("globalSnapImageCenter", defaultConfig.imageCenter()));
}
void KisConfig::saveSnapConfig(const KisSnapConfig &config)
{
m_cfg.writeEntry("globalSnapOrthogonal", config.orthogonal());
m_cfg.writeEntry("globalSnapNode", config.node());
m_cfg.writeEntry("globalSnapExtension", config.extension());
m_cfg.writeEntry("globalSnapIntersection", config.intersection());
m_cfg.writeEntry("globalSnapBoundingBox", config.boundingBox());
m_cfg.writeEntry("globalSnapImageBounds", config.imageBounds());
m_cfg.writeEntry("globalSnapImageCenter", config.imageCenter());
}
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( KSharedConfig::openConfig(), "SplashScreen");
return (defaultValue ? true : cfg.readEntry("HideSplashAfterStartup", true));
}
void KisConfig::setHideSplashScreen(bool hideSplashScreen) const
{
KConfigGroup cfg( KSharedConfig::openConfig(), "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()));
+ return (defaultValue ? 300 : m_cfg.readEntry("AutoSaveInterval", 300));
}
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::showStatusBar(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("showStatusBar", true));
}
void KisConfig::setShowStatusBar(const bool value) const
{
m_cfg.writeEntry("showStatusBar", 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
+void KisConfig::setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const
{
- QString exportConfig = properties.toXML();
+ 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
Q_UNUSED(defaultValue);
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);
}
bool KisConfig::levelOfDetailEnabled(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("levelOfDetailEnabled", false));
}
void KisConfig::setLevelOfDetailEnabled(bool value)
{
m_cfg.writeEntry("levelOfDetailEnabled", 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::shouldEatDriverShortcuts(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("shouldEatDriverShortcuts", false));
}
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 = KSharedConfig::openConfig()->group("advancedColorSelector");
if (defaultValue || cfg.readEntry("useCustomColorSpace", true)) {
KoColorSpaceRegistry* csr = KoColorSpaceRegistry::instance();
QString modelID = cfg.readEntry("customColorSpaceModel", "RGBA");
QString depthID = cfg.readEntry("customColorSpaceDepthID", "U8");
QString profile = cfg.readEntry("customColorSpaceProfile", "sRGB built-in - (lcms internal)");
if (profile == "default") {
// qDebug() << "Falling back to default color profile.";
profile = "sRGB built-in - (lcms internal)";
}
cs = csr->colorSpace(modelID, depthID, profile);
}
return cs;
}
void KisConfig::setCustomColorSelectorColorSpace(const KoColorSpace *cs)
{
KConfigGroup cfg = KSharedConfig::openConfig()->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();
}
bool KisConfig::enableOpenGLDebugging(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("enableOpenGLDebugging", false));
}
void KisConfig::setEnableOpenGLDebugging(bool value) const
{
m_cfg.writeEntry("enableOpenGLDebugging", value);
}
void KisConfig::setEnableAmdVectorizationWorkaround(bool value)
{
m_cfg.writeEntry("amdDisableVectorWorkaround", value);
}
bool KisConfig::enableAmdVectorizationWorkaround(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("amdDisableVectorWorkaround", false));
}
void KisConfig::setAnimationDropFrames(bool value)
{
bool oldValue = animationDropFrames();
if (value == oldValue) return;
m_cfg.writeEntry("animationDropFrames", value);
KisConfigNotifier::instance()->notifyDropFramesModeChanged();
}
bool KisConfig::animationDropFrames(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("animationDropFrames", true));
}
int KisConfig::scribbingUpdatesDelay(bool defaultValue) const
{
return (defaultValue ? 30 : m_cfg.readEntry("scribbingUpdatesDelay", 30));
}
void KisConfig::setScribbingUpdatesDelay(int value)
{
m_cfg.writeEntry("scribbingUpdatesDelay", value);
}
bool KisConfig::switchSelectionCtrlAlt(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("switchSelectionCtrlAlt", false);
}
void KisConfig::setSwitchSelectionCtrlAlt(bool value)
{
m_cfg.writeEntry("switchSelectionCtrlAlt", value);
KisConfigNotifier::instance()->notifyConfigChanged();
}
bool KisConfig::convertToImageColorspaceOnImport(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("ConvertToImageColorSpaceOnImport", false);
}
void KisConfig::setConvertToImageColorspaceOnImport(bool value)
{
m_cfg.writeEntry("ConvertToImageColorSpaceOnImport", value);
}
int KisConfig::stabilizerSampleSize(bool defaultValue) const
{
#ifdef Q_OS_WIN
const int defaultSampleSize = 50;
#else
const int defaultSampleSize = 15;
#endif
return defaultValue ?
defaultSampleSize : m_cfg.readEntry("stabilizerSampleSize", defaultSampleSize);
}
void KisConfig::setStabilizerSampleSize(int value)
{
m_cfg.writeEntry("stabilizerSampleSize", value);
}
QString KisConfig::customFFMpegPath(bool defaultValue) const
{
return defaultValue ? QString() : m_cfg.readEntry("ffmpegExecutablePath", QString());
}
void KisConfig::setCustomFFMpegPath(const QString &value) const
{
m_cfg.writeEntry("ffmpegExecutablePath", value);
}
bool KisConfig::showBrushHud(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("showBrushHud", false);
}
void KisConfig::setShowBrushHud(bool value)
{
m_cfg.writeEntry("showBrushHud", value);
}
QString KisConfig::brushHudSetting(bool defaultValue) const
{
QString defaultDoc = "<!DOCTYPE hud_properties>\n<hud_properties>\n <version value=\"1\" type=\"value\"/>\n <paintbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"angle\" type=\"value\"/>\n </properties_list>\n </paintbrush>\n <colorsmudge>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"smudge_mode\" type=\"value\"/>\n <item_3 value=\"smudge_length\" type=\"value\"/>\n <item_4 value=\"smudge_color_rate\" type=\"value\"/>\n </properties_list>\n </colorsmudge>\n <sketchbrush>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"size\" type=\"value\"/>\n </properties_list>\n </sketchbrush>\n <hairybrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n </properties_list>\n </hairybrush>\n <experimentbrush>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"shape_windingfill\" type=\"value\"/>\n </properties_list>\n </experimentbrush>\n <spraybrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"spray_particlecount\" type=\"value\"/>\n <item_3 value=\"spray_density\" type=\"value\"/>\n </properties_list>\n </spraybrush>\n <hatchingbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"hatching_angle\" type=\"value\"/>\n <item_3 value=\"hatching_thickness\" type=\"value\"/>\n <item_4 value=\"hatching_separation\" type=\"value\"/>\n </properties_list>\n </hatchingbrush>\n <gridbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"grid_divisionlevel\" type=\"value\"/>\n </properties_list>\n </gridbrush>\n <curvebrush>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"curve_historysize\" type=\"value\"/>\n <item_2 value=\"curve_linewidth\" type=\"value\"/>\n <item_3 value=\"curve_lineopacity\" type=\"value\"/>\n <item_4 value=\"curve_connectionline\" type=\"value\"/>\n </properties_list>\n </curvebrush>\n <dynabrush>\n <properties_list type=\"array\">\n <item_0 value=\"dyna_diameter\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"dyna_mass\" type=\"value\"/>\n <item_3 value=\"dyna_drag\" type=\"value\"/>\n </properties_list>\n </dynabrush>\n <particlebrush>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"particle_particles\" type=\"value\"/>\n <item_2 value=\"particle_opecityweight\" type=\"value\"/>\n <item_3 value=\"particle_iterations\" type=\"value\"/>\n </properties_list>\n </particlebrush>\n <duplicate>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"clone_healing\" type=\"value\"/>\n <item_3 value=\"clone_movesource\" type=\"value\"/>\n </properties_list>\n </duplicate>\n <deformbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"deform_amount\" type=\"value\"/>\n <item_3 value=\"deform_mode\" type=\"value\"/>\n </properties_list>\n </deformbrush>\n <tangentnormal>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n </properties_list>\n </tangentnormal>\n <filter>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n </properties_list>\n </filter>\n <chalkbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n </properties_list>\n </chalkbrush>\n <roundmarker>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"size\" type=\"value\"/>\n </properties_list>\n </roundmarker>\n</hud_properties>\n";
return defaultValue ? defaultDoc : m_cfg.readEntry("brushHudSettings", defaultDoc);
}
void KisConfig::setBrushHudSetting(const QString &value) const
{
m_cfg.writeEntry("brushHudSettings", value);
}
#include <QDomDocument>
#include <QDomElement>
void KisConfig::writeKoColor(const QString& name, const KoColor& color) const
{
QDomDocument doc = QDomDocument(name);
QDomElement el = doc.createElement(name);
doc.appendChild(el);
color.toXML(doc, el);
m_cfg.writeEntry(name, doc.toString());
}
//ported from kispropertiesconfig.
KoColor KisConfig::readKoColor(const QString& name, const KoColor& color) const
{
QDomDocument doc;
if (!m_cfg.readEntry(name).isNull()) {
doc.setContent(m_cfg.readEntry(name));
QDomElement e = doc.documentElement().firstChild().toElement();
return KoColor::fromXML(e, Integer16BitsColorDepthID.id(), QHash<QString, QString>());
} else {
QString blackColor = "<!DOCTYPE Color>\n<Color>\n <RGB r=\"0\" space=\"sRGB-elle-V2-srgbtrc.icc\" b=\"0\" g=\"0\"/>\n</Color>\n";
doc.setContent(blackColor);
QDomElement e = doc.documentElement().firstChild().toElement();
return KoColor::fromXML(e, Integer16BitsColorDepthID.id(), QHash<QString, QString>());
}
return color;
}
diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h
index 02949f4181..90d38e0349 100644
--- a/libs/ui/kis_config.h
+++ b/libs/ui/kis_config.h
@@ -1,527 +1,527 @@
/*
* 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.
*/
#ifndef KIS_CONFIG_H_
#define KIS_CONFIG_H_
#include <QString>
#include <QStringList>
#include <QList>
#include <QColor>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include "kis_global.h"
#include "kis_properties_configuration.h"
#include "kritaui_export.h"
class KoColorProfile;
class KoColorSpace;
class KisSnapConfig;
class KRITAUI_EXPORT KisConfig
{
public:
KisConfig();
~KisConfig();
bool disableTouchOnCanvas(bool defaultValue = false) const;
void setDisableTouchOnCanvas(bool value) const;
bool useProjections(bool defaultValue = false) const;
void setUseProjections(bool useProj) const;
bool undoEnabled(bool defaultValue = false) const;
void setUndoEnabled(bool undo) const;
int undoStackLimit(bool defaultValue = false) const;
void setUndoStackLimit(int limit) const;
bool useCumulativeUndoRedo(bool defaultValue = false) const;
void setCumulativeUndoRedo(bool value);
double stackT1(bool defaultValue = false) const;
void setStackT1(int T1);
double stackT2(bool defaultValue = false) const;
void setStackT2(int T2);
int stackN(bool defaultValue = false) const;
void setStackN(int N);
qint32 defImageWidth(bool defaultValue = false) const;
void defImageWidth(qint32 width) const;
qint32 defImageHeight(bool defaultValue = false) const;
void defImageHeight(qint32 height) const;
qreal defImageResolution(bool defaultValue = false) const;
void defImageResolution(qreal res) const;
/**
* @return the id of the default color model used for creating new images.
*/
QString defColorModel(bool defaultValue = false) const;
/**
* set the id of the default color model used for creating new images.
*/
void defColorModel(const QString & model) const;
/**
* @return the id of the default color depth used for creating new images.
*/
QString defaultColorDepth(bool defaultValue = false) const;
/**
* set the id of the default color depth used for creating new images.
*/
void setDefaultColorDepth(const QString & depth) const;
/**
* @return the id of the default color profile used for creating new images.
*/
QString defColorProfile(bool defaultValue = false) const;
/**
* set the id of the default color profile used for creating new images.
*/
void defColorProfile(const QString & depth) const;
CursorStyle newCursorStyle(bool defaultValue = false) const;
void setNewCursorStyle(CursorStyle style);
OutlineStyle newOutlineStyle(bool defaultValue = false) const;
void setNewOutlineStyle(OutlineStyle style);
QRect colorPreviewRect() const;
void setColorPreviewRect(const QRect &rect);
/// get the profile the user has selected for the given screen
QString monitorProfile(int screen) const;
void setMonitorProfile(int screen, const QString & monitorProfile, bool override) const;
QString monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue = true) const;
void setMonitorForScreen(int screen, const QString& monitor);
/// Get the actual profile to be used for the given screen, which is
/// either the screen profile set by the color management system or
/// the custom monitor profile set by the user, depending on the configuration
const KoColorProfile *displayProfile(int screen) const;
QString workingColorSpace(bool defaultValue = false) const;
void setWorkingColorSpace(const QString & workingColorSpace) const;
QString importProfile(bool defaultValue = false) const;
void setImportProfile(const QString & importProfile) const;
QString printerColorSpace(bool defaultValue = false) const;
void setPrinterColorSpace(const QString & printerColorSpace) const;
QString printerProfile(bool defaultValue = false) const;
void setPrinterProfile(const QString & printerProfile) const;
bool useBlackPointCompensation(bool defaultValue = false) const;
void setUseBlackPointCompensation(bool useBlackPointCompensation) const;
bool allowLCMSOptimization(bool defaultValue = false) const;
void setAllowLCMSOptimization(bool allowLCMSOptimization);
void writeKoColor(const QString& name, const KoColor& color) const;
KoColor readKoColor(const QString& name, const KoColor& color = KoColor()) const;
bool showRulers(bool defaultValue = false) const;
void setShowRulers(bool rulers) const;
bool rulersTrackMouse(bool defaultValue = false) const;
void setRulersTrackMouse(bool value) const;
qint32 pasteBehaviour(bool defaultValue = false) const;
void setPasteBehaviour(qint32 behaviour) const;
qint32 monitorRenderIntent(bool defaultValue = false) const;
void setRenderIntent(qint32 monitorRenderIntent) const;
bool useOpenGL(bool defaultValue = false) const;
void setUseOpenGL(bool useOpenGL) const;
int openGLFilteringMode(bool defaultValue = false) const;
void setOpenGLFilteringMode(int filteringMode);
bool useOpenGLTextureBuffer(bool defaultValue = false) const;
void setUseOpenGLTextureBuffer(bool useBuffer);
bool disableVSync(bool defaultValue = false) const;
void setDisableVSync(bool disableVSync);
bool showAdvancedOpenGLSettings(bool defaultValue = false) const;
bool forceOpenGLFenceWorkaround(bool defaultValue = false) const;
int numMipmapLevels(bool defaultValue = false) const;
int openGLTextureSize(bool defaultValue = false) const;
int textureOverlapBorder() const;
qint32 maxNumberOfThreads(bool defaultValue = false) const;
void setMaxNumberOfThreads(qint32 numberOfThreads);
quint32 getGridMainStyle(bool defaultValue = false) const;
void setGridMainStyle(quint32 v) const;
quint32 getGridSubdivisionStyle(bool defaultValue = false) const;
void setGridSubdivisionStyle(quint32 v) const;
QColor getGridMainColor(bool defaultValue = false) const;
void setGridMainColor(const QColor & v) const;
QColor getGridSubdivisionColor(bool defaultValue = false) const;
void setGridSubdivisionColor(const QColor & v) const;
quint32 guidesLineStyle(bool defaultValue = false) const;
void setGuidesLineStyle(quint32 v) const;
QColor guidesColor(bool defaultValue = false) const;
void setGuidesColor(const QColor & v) const;
void loadSnapConfig(KisSnapConfig *config, bool defaultValue = false) const;
void saveSnapConfig(const KisSnapConfig &config);
qint32 checkSize(bool defaultValue = false) const;
void setCheckSize(qint32 checkSize) const;
bool scrollCheckers(bool defaultValue = false) const;
void setScrollingCheckers(bool scollCheckers) const;
QColor checkersColor1(bool defaultValue = false) const;
void setCheckersColor1(const QColor & v) const;
QColor checkersColor2(bool defaultValue = false) const;
void setCheckersColor2(const QColor & v) const;
QColor canvasBorderColor(bool defaultValue = false) const;
void setCanvasBorderColor(const QColor &color) const;
bool hideScrollbars(bool defaultValue = false) const;
void setHideScrollbars(bool value) const;
bool antialiasCurves(bool defaultValue = false) const;
void setAntialiasCurves(bool v) const;
QColor selectionOverlayMaskColor(bool defaultValue = false) const;
void setSelectionOverlayMaskColor(const QColor &color);
bool antialiasSelectionOutline(bool defaultValue = false) const;
void setAntialiasSelectionOutline(bool v) const;
bool showRootLayer(bool defaultValue = false) const;
void setShowRootLayer(bool showRootLayer) const;
bool showGlobalSelection(bool defaultValue = false) const;
void setShowGlobalSelection(bool showGlobalSelection) const;
bool showOutlineWhilePainting(bool defaultValue = false) const;
void setShowOutlineWhilePainting(bool showOutlineWhilePainting) const;
bool hideSplashScreen(bool defaultValue = false) const;
void setHideSplashScreen(bool hideSplashScreen) const;
qreal outlineSizeMinimum(bool defaultValue = false) const;
void setOutlineSizeMinimum(qreal outlineSizeMinimum) const;
int autoSaveInterval(bool defaultValue = false) const;
void setAutoSaveInterval(int seconds) const;
bool backupFile(bool defaultValue = false) const;
void setBackupFile(bool backupFile) const;
bool showFilterGallery(bool defaultValue = false) const;
void setShowFilterGallery(bool showFilterGallery) const;
bool showFilterGalleryLayerMaskDialog(bool defaultValue = false) const;
void setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const;
// OPENGL_SUCCESS, TRY_OPENGL, OPENGL_NOT_TRIED, OPENGL_FAILED
QString canvasState(bool defaultValue = false) const;
void setCanvasState(const QString& state) const;
bool toolOptionsPopupDetached(bool defaultValue = false) const;
void setToolOptionsPopupDetached(bool detached) const;
bool paintopPopupDetached(bool defaultValue = false) const;
void setPaintopPopupDetached(bool detached) const;
QString pressureTabletCurve(bool defaultValue = false) const;
void setPressureTabletCurve(const QString& curveString) const;
qreal vastScrolling(bool defaultValue = false) const;
void setVastScrolling(const qreal factor) const;
int presetChooserViewMode(bool defaultValue = false) const;
void setPresetChooserViewMode(const int mode) const;
bool firstRun(bool defaultValue = false) const;
void setFirstRun(const bool firstRun) const;
bool clicklessSpacePan(bool defaultValue = false) const;
void setClicklessSpacePan(const bool toggle) const;
int horizontalSplitLines(bool defaultValue = false) const;
void setHorizontalSplitLines(const int numberLines) const;
int verticalSplitLines(bool defaultValue = false) const;
void setVerticalSplitLines(const int numberLines) const;
bool hideDockersFullscreen(bool defaultValue = false) const;
void setHideDockersFullscreen(const bool value) const;
bool showDockerTitleBars(bool defaultValue = false) const;
void setShowDockerTitleBars(const bool value) const;
bool showStatusBar(bool defaultValue = false) const;
void setShowStatusBar(const bool value) const;
bool hideMenuFullscreen(bool defaultValue = false) const;
void setHideMenuFullscreen(const bool value) const;
bool hideScrollbarsFullscreen(bool defaultValue = false) const;
void setHideScrollbarsFullscreen(const bool value) const;
bool hideStatusbarFullscreen(bool defaultValue = false) const;
void setHideStatusbarFullscreen(const bool value) const;
bool hideTitlebarFullscreen(bool defaultValue = false) const;
void setHideTitlebarFullscreen(const bool value) const;
bool hideToolbarFullscreen(bool defaultValue = false) const;
void setHideToolbarFullscreen(const bool value) const;
bool fullscreenMode(bool defaultValue = false) const;
void setFullscreenMode(const bool value) const;
QStringList favoriteCompositeOps(bool defaultValue = false) const;
void setFavoriteCompositeOps(const QStringList& compositeOps) const;
QString exportConfiguration(const QString &filterId, bool defaultValue = false) const;
- void setExportConfiguration(const QString &filterId, const KisPropertiesConfiguration &properties) const;
+ void setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const;
bool useOcio(bool defaultValue = false) const;
void setUseOcio(bool useOCIO) const;
int favoritePresets(bool defaultValue = false) const;
void setFavoritePresets(const int value);
bool levelOfDetailEnabled(bool defaultValue = false) const;
void setLevelOfDetailEnabled(bool value);
enum OcioColorManagementMode {
INTERNAL = 0,
OCIO_CONFIG,
OCIO_ENVIRONMENT
};
OcioColorManagementMode ocioColorManagementMode(bool defaultValue = false) const;
void setOcioColorManagementMode(OcioColorManagementMode mode) const;
QString ocioConfigurationPath(bool defaultValue = false) const;
void setOcioConfigurationPath(const QString &path) const;
QString ocioLutPath(bool defaultValue = false) const;
void setOcioLutPath(const QString &path) const;
int ocioLutEdgeSize(bool defaultValue = false) const;
void setOcioLutEdgeSize(int value);
bool ocioLockColorVisualRepresentation(bool defaultValue = false) const;
void setOcioLockColorVisualRepresentation(bool value);
bool useSystemMonitorProfile(bool defaultValue = false) const;
void setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const;
QString defaultPalette(bool defaultValue = false) const;
void setDefaultPalette(const QString& name) const;
QString toolbarSlider(int sliderNumber, bool defaultValue = false) const;
void setToolbarSlider(int sliderNumber, const QString &slider);
bool sliderLabels(bool defaultValue = false) const;
void setSliderLabels(bool enabled);
QString currentInputProfile(bool defaultValue = false) const;
void setCurrentInputProfile(const QString& name);
bool presetStripVisible(bool defaultValue = false) const;
void setPresetStripVisible(bool visible);
bool scratchpadVisible(bool defaultValue = false) const;
void setScratchpadVisible(bool visible);
bool showSingleChannelAsColor(bool defaultValue = false) const;
void setShowSingleChannelAsColor(bool asColor);
bool hidePopups(bool defaultValue = false) const;
void setHidePopups(bool hidepopups);
int numDefaultLayers(bool defaultValue = false) const;
void setNumDefaultLayers(int num);
quint8 defaultBackgroundOpacity(bool defaultValue = false) const;
void setDefaultBackgroundOpacity(quint8 value);
QColor defaultBackgroundColor(bool defaultValue = false) const;
void setDefaultBackgroundColor(QColor value);
enum BackgroundStyle {
LAYER = 0,
PROJECTION = 1
};
BackgroundStyle defaultBackgroundStyle(bool defaultValue = false) const;
void setDefaultBackgroundStyle(BackgroundStyle value);
int lineSmoothingType(bool defaultValue = false) const;
void setLineSmoothingType(int value);
qreal lineSmoothingDistance(bool defaultValue = false) const;
void setLineSmoothingDistance(qreal value);
qreal lineSmoothingTailAggressiveness(bool defaultValue = false) const;
void setLineSmoothingTailAggressiveness(qreal value);
bool lineSmoothingSmoothPressure(bool defaultValue = false) const;
void setLineSmoothingSmoothPressure(bool value);
bool lineSmoothingScalableDistance(bool defaultValue = false) const;
void setLineSmoothingScalableDistance(bool value);
qreal lineSmoothingDelayDistance(bool defaultValue = false) const;
void setLineSmoothingDelayDistance(qreal value);
bool lineSmoothingUseDelayDistance(bool defaultValue = false) const;
void setLineSmoothingUseDelayDistance(bool value);
bool lineSmoothingFinishStabilizedCurve(bool defaultValue = false) const;
void setLineSmoothingFinishStabilizedCurve(bool value);
bool lineSmoothingStabilizeSensors(bool defaultValue = false) const;
void setLineSmoothingStabilizeSensors(bool value);
int paletteDockerPaletteViewSectionSize(bool defaultValue = false) const;
void setPaletteDockerPaletteViewSectionSize(int value) const;
int tabletEventsDelay(bool defaultValue = false) const;
void setTabletEventsDelay(int value);
bool testingAcceptCompressedTabletEvents(bool defaultValue = false) const;
void setTestingAcceptCompressedTabletEvents(bool value);
bool shouldEatDriverShortcuts(bool defaultValue = false) const;
bool testingCompressBrushEvents(bool defaultValue = false) const;
void setTestingCompressBrushEvents(bool value);
const KoColorSpace* customColorSelectorColorSpace(bool defaultValue = false) const;
void setCustomColorSelectorColorSpace(const KoColorSpace *cs);
bool useDirtyPresets(bool defaultValue = false) const;
void setUseDirtyPresets(bool value);
bool useEraserBrushSize(bool defaultValue = false) const;
void setUseEraserBrushSize(bool value);
bool useEraserBrushOpacity(bool defaultValue = false) const;
void setUseEraserBrushOpacity(bool value);
-
+
QColor getMDIBackgroundColor(bool defaultValue = false) const;
void setMDIBackgroundColor(const QColor & v) const;
QString getMDIBackgroundImage(bool defaultValue = false) const;
void setMDIBackgroundImage(const QString & fileName) const;
bool useVerboseOpenGLDebugOutput(bool defaultValue = false) const;
int workaroundX11SmoothPressureSteps(bool defaultValue = false) const;
bool showCanvasMessages(bool defaultValue = false) const;
void setShowCanvasMessages(bool show);
bool compressKra(bool defaultValue = false) const;
void setCompressKra(bool compress);
bool toolOptionsInDocker(bool defaultValue = false) const;
void setToolOptionsInDocker(bool inDocker);
void setEnableOpenGLDebugging(bool value) const;
bool enableOpenGLDebugging(bool defaultValue = false) const;
void setEnableAmdVectorizationWorkaround(bool value);
bool enableAmdVectorizationWorkaround(bool defaultValue = false) const;
bool animationDropFrames(bool defaultValue = false) const;
void setAnimationDropFrames(bool value);
int scribbingUpdatesDelay(bool defaultValue = false) const;
void setScribbingUpdatesDelay(int value);
bool switchSelectionCtrlAlt(bool defaultValue = false) const;
void setSwitchSelectionCtrlAlt(bool value);
bool convertToImageColorspaceOnImport(bool defaultValue = false) const;
void setConvertToImageColorspaceOnImport(bool value);
int stabilizerSampleSize(bool defaultValue = false) const;
void setStabilizerSampleSize(int value);
QString customFFMpegPath(bool defaultValue = false) const;
void setCustomFFMpegPath(const QString &value) const;
bool showBrushHud(bool defaultValue = false) const;
void setShowBrushHud(bool value);
QString brushHudSetting(bool defaultValue = false) const;
void setBrushHudSetting(const QString &value) const;
template<class T>
void writeEntry(const QString& name, const T& value) {
m_cfg.writeEntry(name, value);
}
template<class T>
void writeList(const QString& name, const QList<T>& value) {
m_cfg.writeEntry(name, value);
}
template<class T>
T readEntry(const QString& name, const T& defaultValue=T()) {
return m_cfg.readEntry(name, defaultValue);
}
template<class T>
QList<T> readList(const QString& name, const QList<T>& defaultValue=QList<T>()) {
return m_cfg.readEntry(name, defaultValue);
}
/// get the profile the color managment system has stored for the given screen
static const KoColorProfile* getScreenProfile(int screen);
private:
KisConfig(const KisConfig&);
KisConfig& operator=(const KisConfig&) const;
private:
mutable KConfigGroup m_cfg;
};
#endif // KIS_CONFIG_H_
diff --git a/libs/ui/kis_filter_manager.cc b/libs/ui/kis_filter_manager.cc
index 36712121e9..52b7df81ba 100644
--- a/libs/ui/kis_filter_manager.cc
+++ b/libs/ui/kis_filter_manager.cc
@@ -1,351 +1,348 @@
/*
* 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*, QAction *> filters2Action;
KActionCollection *actionCollection;
KisActionManager *actionManager;
KisViewManager *view;
KisFilterConfigurationSP lastConfiguration;
KisFilterConfigurationSP currentlyAppliedConfiguration;
KisStrokeId currentStrokeId;
QRect initialApplyRect;
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 = d->actionManager->createAction("filter_apply_again");
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
QStringList keys = KisFilterRegistry::instance()->keys();
keys.sort();
Q_FOREACH (const QString &filterName, 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.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->setDefaultShortcut(filter->shortcut());
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*, QAction *>::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 a virtual barrier is added here.
*/
d->view->image()->waitForDone();
Q_ASSERT(d->view);
Q_ASSERT(d->view->activeNode());
KisPaintDeviceSP dev = d->view->activeNode()->paintDevice();
if (!dev) {
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(KisFilterConfigurationSP(filter->defaultConfiguration(d->view->activeNode()->original())));
finish();
}
}
void KisFilterManager::apply(KisFilterConfigurationSP 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();
} else {
image->waitForDone();
d->initialApplyRect = d->view->activeNode()->exactBounds();
}
QRect applyRect = d->initialApplyRect;
KisPaintDeviceSP paintDevice = d->view->activeNode()->paintDevice();
if (paintDevice &&
filter->needsTransparentPixels(filterConfig.data(), paintDevice->colorSpace())) {
applyRect |= image->bounds();
}
- 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,
KisFilterConfigurationSP(filterConfig),
resources));
QRect processRect = filter->changedRect(applyRect, filterConfig.data(), 0);
processRect &= image->bounds();
if (filter->supportsThreading()) {
QSize size = KritaUtils::optimalPatchSize();
QVector<QRect> rects = KritaUtils::splitRectIntoPatches(processRect, size);
Q_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/libs/ui/kis_filters_model.cc b/libs/ui/kis_filters_model.cc
index 4d193d0e49..57af64182d 100644
--- a/libs/ui/kis_filters_model.cc
+++ b/libs/ui/kis_filters_model.cc
@@ -1,191 +1,194 @@
/*
* This file is part of Krita
*
* 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_filters_model.h"
#include <QPixmap>
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include <kis_selection.h>
struct KisFiltersModel::Private {
struct Node {
virtual ~Node() {}
QString name;
QString displayRole() {
return name;
}
virtual int childrenCount() = 0;
};
struct Filter : public Node {
~Filter() override {}
QString id;
QPixmap icon;
KisFilterSP filter;
int childrenCount() override {
return 0;
}
};
struct Category : public Node {
~Category() override {}
QString id;
QList<Filter> filters;
int childrenCount() override {
return filters.count();
}
};
QHash<QString, Category> categories;
QList<QString> categoriesKeys;
KisPaintDeviceSP thumb;
};
KisFiltersModel::KisFiltersModel(bool showAll, KisPaintDeviceSP thumb)
: d(new Private)
{
d->thumb = thumb;
- QList<KisFilterSP> filters = KisFilterRegistry::instance()->values();
- Q_FOREACH (const KisFilterSP filter, filters) {
+ QStringList keys = KisFilterRegistry::instance()->keys();
+ keys.sort();
+
+ Q_FOREACH (const QString &filterName, keys) {
+ KisFilterSP filter = KisFilterRegistry::instance()->get(filterName);
if (!showAll && !filter->supportsAdjustmentLayers()) {
continue;
}
Q_ASSERT(filter);
if (!d->categories.contains(filter->menuCategory().id())) {
Private::Category cat;
cat.id = filter->menuCategory().id();
cat.name = filter->menuCategory().name();
d->categories[ cat.id ] = cat;
d->categoriesKeys.append(cat.id);
}
Private::Filter filt;
filt.id = filter->id();
filt.name = filter->name();
filt.filter = filter;
- d->categories[ filter->menuCategory().id()].filters.append(filt);
+ d->categories[filter->menuCategory().id()].filters.append(filt);
}
qSort(d->categoriesKeys);
}
KisFiltersModel::~KisFiltersModel()
{
delete d;
}
int KisFiltersModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
Private::Node* node = static_cast<Private::Node*>(parent.internalPointer());
return node->childrenCount();
} else {
return d->categoriesKeys.count();
}
}
int KisFiltersModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
QModelIndex KisFiltersModel::indexForFilter(const QString& id)
{
for (int i = 0; i < d->categoriesKeys.size(); i++) {
KisFiltersModel::Private::Category& category = d->categories[ d->categoriesKeys[ i ] ];
for (int j = 0; j < category.filters.size(); j++) {
KisFiltersModel::Private::Filter& filter = category.filters[j];
if (filter.id == id) {
return index(j, i, index(i , 0, QModelIndex()));
}
}
}
return QModelIndex();
}
const KisFilter* KisFiltersModel::indexToFilter(const QModelIndex& idx)
{
Private::Node* node = static_cast<Private::Node*>(idx.internalPointer());
Private::Filter* filter = dynamic_cast<Private::Filter*>(node);
if (filter) {
return filter->filter;
}
return 0;
}
QModelIndex KisFiltersModel::index(int row, int column, const QModelIndex &parent) const
{
// dbgKrita << parent.isValid() << row << endl;
if (parent.isValid()) {
Private::Category* category = static_cast<Private::Category*>(parent.internalPointer());
return createIndex(row, column, &category->filters[row]);
} else {
return createIndex(row, column, &d->categories[ d->categoriesKeys[row] ]);
}
}
QModelIndex KisFiltersModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();
Private::Node* node = static_cast<Private::Node*>(child.internalPointer());
Private::Filter* filter = dynamic_cast<Private::Filter*>(node);
if (filter) {
QString catId = filter->filter->menuCategory().id();
return createIndex(d->categoriesKeys.indexOf(catId) , 0, &d->categories[ catId ]);
}
return QModelIndex(); // categories don't have parents
}
QVariant KisFiltersModel::data(const QModelIndex &index, int role) const
{
if (index.isValid()) {
if (role == Qt::DisplayRole) {
Private::Node* node = static_cast<Private::Node*>(index.internalPointer());
return QVariant(node->displayRole());
}
}
return QVariant();
}
Qt::ItemFlags KisFiltersModel::flags(const QModelIndex & index) const
{
if (!index.isValid()) return 0;
Private::Node* node = static_cast<Private::Node*>(index.internalPointer());
Private::Filter* filter = dynamic_cast<Private::Filter*>(node);
if (filter) {
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
} else {
return Qt::ItemIsEnabled;
}
}
diff --git a/libs/ui/kis_mimedata.cpp b/libs/ui/kis_mimedata.cpp
index 53415f08b0..b6b62fbf90 100644
--- a/libs/ui/kis_mimedata.cpp
+++ b/libs/ui/kis_mimedata.cpp
@@ -1,439 +1,443 @@
/*
* Copyright (c) 2011 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 "kis_mimedata.h"
#include "kis_config.h"
#include "kis_node.h"
#include "kis_paint_device.h"
#include "kis_shared_ptr.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_shape_layer.h"
#include "kis_paint_layer.h"
#include "KisDocument.h"
#include "kis_shape_controller.h"
#include "KisPart.h"
#include "kis_layer_utils.h"
#include "kis_node_insertion_adapter.h"
#include "kis_dummies_facade_base.h"
#include "kis_node_dummies_graph.h"
+#include "KisImportExportManager.h"
#include <KoProperties.h>
#include <KoStore.h>
#include <KoColorProfile.h>
#include <KoColorSpaceRegistry.h>
#include <QApplication>
#include <QImage>
#include <QByteArray>
#include <QBuffer>
#include <QDomDocument>
#include <QDomElement>
#include <QTemporaryFile>
#include <QDesktopWidget>
#include <QDir>
KisMimeData::KisMimeData(QList<KisNodeSP> nodes, bool forceCopy)
: QMimeData()
, m_nodes(nodes)
, m_forceCopy(forceCopy)
{
Q_ASSERT(m_nodes.size() > 0);
m_initialListener = m_nodes.first()->graphListener();
}
void KisMimeData::deepCopyNodes()
{
KisNodeList newNodes;
Q_FOREACH (KisNodeSP node, m_nodes) {
newNodes << node->clone();
}
m_nodes = newNodes;
}
QList<KisNodeSP> KisMimeData::nodes() const
{
return m_nodes;
}
QStringList KisMimeData::formats () const
{
QStringList f = QMimeData::formats();
if (m_nodes.size() > 0) {
f << "application/x-krita-node"
<< "application/x-krita-node-url"
<< "application/x-qt-image"
<< "application/zip"
<< "application/x-krita-node-internal-pointer";
}
return f;
}
KisDocument *createDocument(QList<KisNodeSP> nodes)
{
KisDocument *doc = KisPart::instance()->createDocument();
QRect rc;
Q_FOREACH (KisNodeSP node, nodes) {
rc |= node->exactBounds();
}
KisImageSP image = new KisImage(0, rc.width(), rc.height(), nodes.first()->colorSpace(), nodes.first()->name());
Q_FOREACH (KisNodeSP node, nodes) {
image->addNode(node->clone());
}
doc->setCurrentImage(image);
return doc;
}
QByteArray serializeToByteArray(QList<KisNodeSP> nodes)
{
QByteArray byteArray;
QBuffer buffer(&byteArray);
- KoStore *store = KoStore::createStore(&buffer, KoStore::Write);
- Q_ASSERT(!store->bad());
-
KisDocument *doc = createDocument(nodes);
- doc->saveNativeFormatCalligra(store);
+ KisImportExportFilter *filter = KisImportExportManager::filterForMimeType(doc->nativeFormatMimeType(), KisImportExportManager::Export);
+ filter->setBatchMode(true);
+ filter->setMimeType(doc->nativeFormatMimeType());
+ if (filter->convert(doc, &buffer) != KisImportExportFilter::OK) {
+ qWarning() << "serializeToByteArray():: Could not export to our native format";
+ }
+ delete filter;
delete doc;
-
return byteArray;
}
QVariant KisMimeData::retrieveData(const QString &mimetype, QVariant::Type preferredType) const
{
Q_ASSERT(m_nodes.size() > 0);
if (mimetype == "application/x-qt-image") {
KisConfig cfg;
KisDocument *doc = createDocument(m_nodes);
return doc->image()->projection()->convertToQImage(cfg.displayProfile(QApplication::desktop()->screenNumber(qApp->activeWindow())),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
}
else if (mimetype == "application/x-krita-node" ||
mimetype == "application/zip") {
QByteArray ba = serializeToByteArray(m_nodes);
return ba;
}
else if (mimetype == "application/x-krita-node-url") {
QByteArray ba = serializeToByteArray(m_nodes);
QString temporaryPath =
QDir::tempPath() + QDir::separator() +
QString("krita_tmp_dnd_layer_%1_%2.kra")
.arg(QApplication::applicationPid())
.arg(qrand());
QFile file(temporaryPath);
file.open(QFile::WriteOnly);
file.write(ba);
file.flush();
file.close();
return QUrl::fromLocalFile(temporaryPath).toEncoded();
}
else if (mimetype == "application/x-krita-node-internal-pointer") {
QDomDocument doc("krita_internal_node_pointer");
QDomElement root = doc.createElement("pointer");
root.setAttribute("application_pid", (qint64)QApplication::applicationPid());
root.setAttribute("force_copy", m_forceCopy);
root.setAttribute("listener_pointer_value", (qint64)m_initialListener);
doc.appendChild(root);
Q_FOREACH (KisNodeSP node, m_nodes) {
QDomElement element = doc.createElement("node");
element.setAttribute("pointer_value", (qint64)node.data());
root.appendChild(element);
}
return doc.toByteArray();
}
else {
return QMimeData::retrieveData(mimetype, preferredType);
}
}
-void KisMimeData::initializeExternalNode(KisNodeSP &node,
+void KisMimeData::initializeExternalNode(KisNodeSP *node,
KisImageWSP image,
KisShapeController *shapeController)
{
// layers store a link to the image, so update it
- KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
+ KisLayer *layer = dynamic_cast<KisLayer*>(node->data());
if (layer) {
layer->setImage(image);
}
- KisShapeLayer *shapeLayer = dynamic_cast<KisShapeLayer*>(node.data());
+ KisShapeLayer *shapeLayer = dynamic_cast<KisShapeLayer*>(node->data());
if (shapeLayer) {
// attach the layer to a new shape controller
KisShapeLayer *shapeLayer2 = new KisShapeLayer(*shapeLayer, shapeController);
- node = shapeLayer2;
+ *node = shapeLayer2;
}
}
QList<KisNodeSP> KisMimeData::tryLoadInternalNodes(const QMimeData *data,
KisImageWSP image,
KisShapeController *shapeController,
bool /* IN-OUT */ &copyNode)
{
QList<KisNodeSP> nodes;
bool forceCopy = false;
KisNodeGraphListener *initialListener = 0;
// Qt 4.7 and Qt 5.5 way
const KisMimeData *mimedata = qobject_cast<const KisMimeData*>(data);
if (mimedata) {
nodes = mimedata->nodes();
forceCopy = mimedata->m_forceCopy;
initialListener = mimedata->m_initialListener;
}
// Qt 4.8 way
if (nodes.isEmpty() && data->hasFormat("application/x-krita-node-internal-pointer")) {
QByteArray nodeXml = data->data("application/x-krita-node-internal-pointer");
QDomDocument doc;
doc.setContent(nodeXml);
QDomElement element = doc.documentElement();
qint64 pid = element.attribute("application_pid").toLongLong();
forceCopy = element.attribute("force_copy").toInt();
qint64 listenerPointerValue = element.attribute("listener_pointer_value").toLongLong();
initialListener = reinterpret_cast<KisNodeGraphListener*>(listenerPointerValue);
if (pid == QApplication::applicationPid()) {
QDomNode n = element.firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull()) {
qint64 pointerValue = e.attribute("pointer_value").toLongLong();
if (pointerValue) {
nodes << reinterpret_cast<KisNode*>(pointerValue);
}
}
n = n.nextSibling();
}
}
}
if (!nodes.isEmpty() && (forceCopy || copyNode || nodes.first()->graphListener() != image.data())) {
QList<KisNodeSP> clones;
Q_FOREACH (KisNodeSP node, nodes) {
node = node->clone();
if ((forceCopy || copyNode) && initialListener == image.data()) {
KisLayerUtils::addCopyOfNameTag(node);
}
- initializeExternalNode(node, image, shapeController);
+ initializeExternalNode(&node, image, shapeController);
clones << node;
}
nodes = clones;
copyNode = true;
}
return nodes;
}
QList<KisNodeSP> KisMimeData::loadNodes(const QMimeData *data,
const QRect &imageBounds,
const QPoint &preferredCenter,
bool forceRecenter,
KisImageWSP image,
KisShapeController *shapeController)
{
bool alwaysRecenter = false;
QList<KisNodeSP> nodes;
if (data->hasFormat("application/x-krita-node")) {
- QByteArray ba = data->data("application/x-krita-node");
-
KisDocument *tempDoc = KisPart::instance()->createDocument();
- bool result = tempDoc->loadNativeFormatFromByteArray(ba);
+ QByteArray ba = data->data("application/x-krita-node");
+ QBuffer buf(&ba);
+ KisImportExportFilter *filter = tempDoc->importExportManager()->filterForMimeType(tempDoc->nativeFormatMimeType(), KisImportExportManager::Import);
+ filter->setBatchMode(true);
+ bool result = (filter->convert(tempDoc, &buf) == KisImportExportFilter::OK);
if (result) {
KisImageWSP tempImage = tempDoc->image();
Q_FOREACH (KisNodeSP node, tempImage->root()->childNodes(QStringList(), KoProperties())) {
- nodes << node;
tempImage->removeNode(node);
- initializeExternalNode(node, image, shapeController);
+ initializeExternalNode(&node, image, shapeController);
+ nodes << node;
}
}
+ delete filter;
delete tempDoc;
}
if (nodes.isEmpty() && data->hasFormat("application/x-krita-node-url")) {
QByteArray ba = data->data("application/x-krita-node-url");
- QString localFile = QUrl::fromEncoded(ba).toLocalFile();
-
KisDocument *tempDoc = KisPart::instance()->createDocument();
- bool result = tempDoc->loadNativeFormat(localFile);
+ Q_ASSERT(QUrl::fromEncoded(ba).isLocalFile());
+ bool result = tempDoc->openUrl(QUrl::fromEncoded(ba));
if (result) {
KisImageWSP tempImage = tempDoc->image();
Q_FOREACH (KisNodeSP node, tempImage->root()->childNodes(QStringList(), KoProperties())) {
- nodes << node;
tempImage->removeNode(node);
- initializeExternalNode(node, image, shapeController);
+ initializeExternalNode(&node, image, shapeController);
+ nodes << node;
}
}
delete tempDoc;
-
- QFile::remove(localFile);
+ QFile::remove(QUrl::fromEncoded(ba).toLocalFile());
}
if (nodes.isEmpty() && data->hasImage()) {
QImage qimage = qvariant_cast<QImage>(data->imageData());
KisPaintDeviceSP device = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
device->convertFromQImage(qimage, 0);
nodes << new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, device);
alwaysRecenter = true;
}
if (!nodes.isEmpty()) {
Q_FOREACH (KisNodeSP node, nodes) {
QRect bounds = node->projection()->exactBounds();
if (alwaysRecenter || forceRecenter ||
(!imageBounds.contains(bounds) &&
!imageBounds.intersects(bounds))) {
QPoint pt = preferredCenter - bounds.center();
node->setX(pt.x());
node->setY(pt.y());
}
}
}
return nodes;
}
QMimeData* KisMimeData::mimeForLayers(const KisNodeList &nodes, KisNodeSP imageRoot, bool forceCopy)
{
KisNodeList inputNodes = nodes;
KisNodeList sortedNodes;
KisLayerUtils::sortMergableNodes(imageRoot, inputNodes, sortedNodes);
if (sortedNodes.isEmpty()) return 0;
KisMimeData* data = new KisMimeData(sortedNodes, forceCopy);
return data;
}
QMimeData* KisMimeData::mimeForLayersDeepCopy(const KisNodeList &nodes, KisNodeSP imageRoot, bool forceCopy)
{
KisNodeList inputNodes = nodes;
KisNodeList sortedNodes;
KisLayerUtils::sortMergableNodes(imageRoot, inputNodes, sortedNodes);
if (sortedNodes.isEmpty()) return 0;
KisMimeData* data = new KisMimeData(sortedNodes, forceCopy);
data->deepCopyNodes();
return data;
}
bool nodeAllowsAsChild(KisNodeSP parent, KisNodeList nodes)
{
bool result = true;
Q_FOREACH (KisNodeSP node, nodes) {
if (!parent->allowAsChild(node)) {
result = false;
break;
}
}
return result;
}
bool correctNewNodeLocation(KisNodeList nodes,
KisNodeDummy* &parentDummy,
KisNodeDummy* &aboveThisDummy)
{
KisNodeSP parentNode = parentDummy->node();
bool result = true;
if(!nodeAllowsAsChild(parentDummy->node(), nodes)) {
aboveThisDummy = parentDummy;
parentDummy = parentDummy->parent();
result = (!parentDummy) ? false :
correctNewNodeLocation(nodes, parentDummy, aboveThisDummy);
}
return result;
}
bool KisMimeData::insertMimeLayers(const QMimeData *data,
KisImageSP image,
KisShapeController *shapeController,
KisNodeDummy *parentDummy,
KisNodeDummy *aboveThisDummy,
bool copyNode,
KisNodeInsertionAdapter *nodeInsertionAdapter)
{
QList<KisNodeSP> nodes =
KisMimeData::tryLoadInternalNodes(data,
image,
shapeController,
copyNode /* IN-OUT */);
if (nodes.isEmpty()) {
QRect imageBounds = image->bounds();
nodes = KisMimeData::loadNodes(data,
imageBounds, imageBounds.center(),
false,
image, shapeController);
/**
* Don't try to move a node originating from another image,
* just copy it.
*/
copyNode = true;
}
if (nodes.isEmpty()) return false;
bool result = true;
if (!correctNewNodeLocation(nodes, parentDummy, aboveThisDummy)) {
return false;
}
KIS_ASSERT_RECOVER(nodeInsertionAdapter) { return false; }
Q_ASSERT(parentDummy);
KisNodeSP aboveThisNode = aboveThisDummy ? aboveThisDummy->node() : 0;
if (copyNode) {
nodeInsertionAdapter->addNodes(nodes, parentDummy->node(), aboveThisNode);
}
else {
Q_ASSERT(nodes.first()->graphListener() == image.data());
nodeInsertionAdapter->moveNodes(nodes, parentDummy->node(), aboveThisNode);
}
return result;
}
diff --git a/libs/ui/kis_mimedata.h b/libs/ui/kis_mimedata.h
index ba4a661998..726d4a3a2d 100644
--- a/libs/ui/kis_mimedata.h
+++ b/libs/ui/kis_mimedata.h
@@ -1,118 +1,118 @@
/*
* Copyright (c) 2011 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 KIS_MIMEDATA_H
#define KIS_MIMEDATA_H
#include <QMimeData>
#include <kis_types.h>
#include <kritaui_export.h>
class KisShapeController;
class KisNodeDummy;
class KisNodeInsertionAdapter;
class KisNodeGraphListener;
/**
* KisMimeData implements delayed retrieval of nodes for d&d and copy/paste.
*
* TODO: implement support for the ora format.
*/
class KRITAUI_EXPORT KisMimeData : public QMimeData
{
Q_OBJECT
public:
KisMimeData(QList<KisNodeSP> nodes, bool forceCopy = false);
/// return the node set on this mimedata object -- for internal use
QList<KisNodeSP> nodes() const;
/**
* For Cut/Copy/Paste operations we should detach the contents of
* the mime data from the actual image because the user can modify
* our image between the Copy/Cut and Paste calls. So we just copy
* all our nodes into the internal array.
*
* It also fixes the problem of Cutting group layers. If we don't copy
* the node and all its children, it'll be deleted by the Cut operation
* and we will not be able to paste it correctly later.
*/
void deepCopyNodes();
/**
* KisMimeData provides the following formats if a node has been set:
* <ul>
* <li>application/x-krita-node: requests a whole serialized node. For d&d between instances of Krita.
* <li>application/x-qt-image: fallback for other applications, returns a QImage of the
* current node's paintdevice
* <li>application/zip: allows drop targets that can handle zip files to open the data
* </ul>
*/
QStringList formats() const;
/**
* Loads a node from a mime container
* Supports application/x-krita-node and image types.
*/
static KisNodeList loadNodes(const QMimeData *data,
const QRect &imageBounds,
const QPoint &preferredCenter,
bool forceRecenter,
KisImageWSP image,
KisShapeController *shapeController);
private:
/**
* Try load the node, which belongs to the same Krita instance,
* that is can be fetched without serialization
*/
static KisNodeList tryLoadInternalNodes(const QMimeData *data,
KisImageWSP image,
KisShapeController *shapeController,
bool /* IN-OUT */ &copyNode);
public:
static QMimeData* mimeForLayers(const KisNodeList &nodes, KisNodeSP imageRoot, bool forceCopy = false);
static QMimeData* mimeForLayersDeepCopy(const KisNodeList &nodes, KisNodeSP imageRoot, bool forceCopy);
static bool insertMimeLayers(const QMimeData *data,
KisImageSP image,
KisShapeController *shapeController,
KisNodeDummy *parentDummy,
KisNodeDummy *aboveThisDummy,
bool copyNode,
KisNodeInsertionAdapter *nodeInsertionAdapter);
protected:
QVariant retrieveData(const QString &mimetype, QVariant::Type preferredType) const;
private:
- static void initializeExternalNode(KisNodeSP &nodes,
+ static void initializeExternalNode(KisNodeSP *nodes,
KisImageWSP image,
KisShapeController *shapeController);
private:
QList<KisNodeSP> m_nodes;
bool m_forceCopy;
KisNodeGraphListener *m_initialListener;
};
#endif // KIS_MIMEDATA_H
diff --git a/libs/ui/kis_node_manager.cpp b/libs/ui/kis_node_manager.cpp
index b09ee2e188..aa0b0649c2 100644
--- a/libs/ui/kis_node_manager.cpp
+++ b/libs/ui/kis_node_manager.cpp
@@ -1,1318 +1,1317 @@
/*
* 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 <QSignalMapper>
#include <kactioncollection.h>
#include <QKeySequence>
#include <kis_icon.h>
#include <KoSelection.h>
#include <KoShapeManager.h>
#include <KoShape.h>
#include <KoShapeLayer.h>
#include <KisImportExportManager.h>
#include <KoFileDialog.h>
#include <KoToolManager.h>
#include <KoProperties.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 <KisMimeDatabase.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 "kis_node_selection_adapter.h"
#include "kis_node_insertion_adapter.h"
#include "kis_node_juggler_compressed.h"
#include "kis_clipboard.h"
#include "kis_node_dummies_graph.h"
#include "kis_mimedata.h"
#include "kis_layer_utils.h"
#include "krita_utils.h"
#include "processing/kis_mirror_processing_visitor.h"
#include "KisView.h"
struct KisNodeManager::Private {
Private(KisNodeManager *_q, KisViewManager *v)
: q(_q)
, view(v)
, imageView(0)
, layerManager(v)
, maskManager(v)
, commandsAdapter(v)
, nodeSelectionAdapter(new KisNodeSelectionAdapter(q))
, nodeInsertionAdapter(new KisNodeInsertionAdapter(q))
{
}
KisNodeManager * q;
KisViewManager * view;
QPointer<KisView>imageView;
KisLayerManager layerManager;
KisMaskManager maskManager;
KisNodeCommandsAdapter commandsAdapter;
QScopedPointer<KisNodeSelectionAdapter> nodeSelectionAdapter;
QScopedPointer<KisNodeInsertionAdapter> nodeInsertionAdapter;
KisNodeList selectedNodes;
QPointer<KisNodeJugglerCompressed> nodeJuggler;
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);
KisNodeJugglerCompressed* lazyGetJuggler(const KUndo2MagicString &actionName);
};
bool KisNodeManager::Private::activateNodeImpl(KisNodeSP node)
{
Q_ASSERT(view);
Q_ASSERT(view->canvasBase());
Q_ASSERT(view->canvasBase()->globalShapeManager());
Q_ASSERT(imageView);
if (node && node == q->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, view))
{
connect(&m_d->layerManager, SIGNAL(sigLayerActivated(KisLayerSP)), SIGNAL(sigLayerActivated(KisLayerSP)));
}
KisNodeManager::~KisNodeManager()
{
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()));
connect(m_d->imageView->image(), SIGNAL(sigRequestNodeReselection(KisNodeSP, const KisNodeList&)),this, SLOT(slotImageRequestNodeReselection(KisNodeSP, const KisNodeList&)));
m_d->imageView->resourceProvider()->slotNodeActivated(m_d->imageView->currentNode());
}
}
#define NEW_LAYER_ACTION(id, layerType) \
{ \
action = actionManager->createAction(id); \
m_d->nodeCreationSignalMapper.setMapping(action, layerType); \
connect(action, SIGNAL(triggered()), \
&m_d->nodeCreationSignalMapper, SLOT(map())); \
}
#define CONVERT_NODE_ACTION_2(id, layerType, exclude) \
{ \
action = actionManager->createAction(id); \
action->setExcludedNodeTypes(QStringList(exclude)); \
actionManager->addAction(id, action); \
m_d->nodeConversionSignalMapper.setMapping(action, layerType); \
connect(action, SIGNAL(triggered()), \
&m_d->nodeConversionSignalMapper, SLOT(map())); \
}
#define CONVERT_NODE_ACTION(id, layerType) \
CONVERT_NODE_ACTION_2(id, layerType, layerType)
void KisNodeManager::setup(KActionCollection * actionCollection, KisActionManager* actionManager)
{
m_d->layerManager.setup(actionManager);
m_d->maskManager.setup(actionCollection, actionManager);
KisAction * action = actionManager->createAction("mirrorNodeX");
connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX()));
action = actionManager->createAction("mirrorNodeY");
connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY()));
action = actionManager->createAction("activateNextLayer");
connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode()));
action = actionManager->createAction("activatePreviousLayer");
connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousNode()));
action = actionManager->createAction("save_node_as_image");
connect(action, SIGNAL(triggered()), this, SLOT(saveNodeAsImage()));
action = actionManager->createAction("duplicatelayer");
connect(action, SIGNAL(triggered()), this, SLOT(duplicateActiveNode()));
action = actionManager->createAction("copy_layer_clipboard");
connect(action, SIGNAL(triggered()), this, SLOT(copyLayersToClipboard()));
action = actionManager->createAction("cut_layer_clipboard");
connect(action, SIGNAL(triggered()), this, SLOT(cutLayersToClipboard()));
action = actionManager->createAction("paste_layer_from_clipboard");
connect(action, SIGNAL(triggered()), this, SLOT(pasteLayersFromClipboard()));
action = actionManager->createAction("create_quick_group");
connect(action, SIGNAL(triggered()), this, SLOT(createQuickGroup()));
action = actionManager->createAction("create_quick_clipping_group");
connect(action, SIGNAL(triggered()), this, SLOT(createQuickClippingGroup()));
action = actionManager->createAction("quick_ungroup");
connect(action, SIGNAL(triggered()), this, SLOT(quickUngroup()));
action = actionManager->createAction("select_all_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectAllNodes()));
action = actionManager->createAction("select_visible_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectVisibleNodes()));
action = actionManager->createAction("select_locked_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectLockedNodes()));
action = actionManager->createAction("select_invisible_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectInvisibleNodes()));
action = actionManager->createAction("select_unlocked_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectUnlockedNodes()));
action = actionManager->createAction("new_from_visible");
connect(action, SIGNAL(triggered()), this, SLOT(createFromVisible()));
-
+
NEW_LAYER_ACTION("add_new_paint_layer", "KisPaintLayer");
NEW_LAYER_ACTION("add_new_group_layer", "KisGroupLayer");
NEW_LAYER_ACTION("add_new_clone_layer", "KisCloneLayer");
NEW_LAYER_ACTION("add_new_shape_layer", "KisShapeLayer");
NEW_LAYER_ACTION("add_new_adjustment_layer", "KisAdjustmentLayer");
NEW_LAYER_ACTION("add_new_fill_layer", "KisGeneratorLayer");
NEW_LAYER_ACTION("add_new_file_layer", "KisFileLayer");
NEW_LAYER_ACTION("add_new_transparency_mask", "KisTransparencyMask");
NEW_LAYER_ACTION("add_new_filter_mask", "KisFilterMask");
NEW_LAYER_ACTION("add_new_colorize_mask", "KisColorizeMask");
NEW_LAYER_ACTION("add_new_transform_mask", "KisTransformMask");
NEW_LAYER_ACTION("add_new_selection_mask", "KisSelectionMask");
connect(&m_d->nodeCreationSignalMapper, SIGNAL(mapped(const QString &)),
this, SLOT(createNode(const QString &)));
CONVERT_NODE_ACTION("convert_to_paint_layer", "KisPaintLayer");
CONVERT_NODE_ACTION_2("convert_to_selection_mask", "KisSelectionMask", QStringList() << "KisSelectionMask" << "KisColorizeMask");
CONVERT_NODE_ACTION_2("convert_to_filter_mask", "KisFilterMask", QStringList() << "KisFilterMask" << "KisColorizeMask");
CONVERT_NODE_ACTION_2("convert_to_transparency_mask", "KisTransparencyMask", QStringList() << "KisTransparencyMask" << "KisColorizeMask");
CONVERT_NODE_ACTION("convert_to_animated", "animated");
connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(const QString &)),
this, SLOT(convertNode(const QString &)));
action = actionManager->createAction("isolate_layer");
connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleIsolateMode(bool)));
action = actionManager->createAction("split_alpha_into_mask");
connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask()));
action = actionManager->createAction("split_alpha_write");
connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite()));
// HINT: we can save even when the nodes are not editable
action = actionManager->createAction("split_alpha_save_merged");
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()
{
if (m_d->maskManager.activeDevice()) {
return m_d->maskManager.activeDevice()->colorSpace();
} else {
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::moveNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
{
KUndo2MagicString actionName = kundo2_i18n("Move Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->moveNode(nodes, parent, aboveThis);
}
void KisNodeManager::copyNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
{
KUndo2MagicString actionName = kundo2_i18n("Copy Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->copyNode(nodes, parent, aboveThis);
}
void KisNodeManager::addNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
{
KUndo2MagicString actionName = kundo2_i18n("Add Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->addNode(nodes, 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 and colorize masks don't have pixel data...
if (activeNode->inherits("KisTransformMask") ||
activeNode->inherits("KisColorizeMask")) 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;
this->toggleIsolateMode(true);
}
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 == "KisColorizeMask") {
m_d->maskManager.createColorizeMask(activeNode);
} 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::createFromVisible()
{
KisLayerUtils::newLayerFromVisible(m_d->view->image(), m_d->view->image()->root()->lastChild());
}
KisLayerSP KisNodeManager::createPaintLayer()
{
KisNodeSP activeNode = this->activeNode();
if (!activeNode) {
activeNode = m_d->view->image()->root();
}
return m_d->layerManager.addLayer(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 {
warnKrita << "Unsupported node conversion type:" << nodeType;
}
}
void KisNodeManager::slotSomethingActivatedNodeImpl(KisNodeSP node)
{
KIS_ASSERT_RECOVER_RETURN(node != activeNode());
if (m_d->activateNodeImpl(node)) {
emit sigUiNeedChangeActiveNode(node);
emit sigNodeActivated(node);
nodesUpdated();
if (node) {
bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked();
if (toggled) {
m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
}
}
}
}
void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node)
{
if (node == activeNode()) return;
slotSomethingActivatedNodeImpl(node);
if (node) {
bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked();
if (toggled) {
m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
}
}
}
void KisNodeManager::slotUiActivatedNode(KisNodeSP node)
{
if (node == activeNode()) return;
slotSomethingActivatedNodeImpl(node);
if (node) {
QStringList vectorTools = QStringList()
<< "InteractionTool"
<< "KarbonPatternTool"
<< "KarbonGradientTool"
<< "KarbonCalligraphyTool"
<< "CreateShapesTool"
<< "PathTool";
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 (selectedNodes().size() > 1 || 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::slotImageRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes)
{
if (activeNode) {
slotNonUiActivatedNode(activeNode);
}
if (!selectedNodes.isEmpty()) {
slotSetSelectedNodes(selectedNodes);
}
}
void KisNodeManager::slotSetSelectedNodes(const KisNodeList &nodes)
{
m_d->selectedNodes = nodes;
emit sigUiNeedChangeSelectedNodes(nodes);
}
KisNodeList KisNodeManager::selectedNodes()
{
return m_d->selectedNodes;
}
KisNodeSelectionAdapter* KisNodeManager::nodeSelectionAdapter() const
{
return m_d->nodeSelectionAdapter.data();
}
KisNodeInsertionAdapter* KisNodeManager::nodeInsertionAdapter() const
{
return m_d->nodeInsertionAdapter.data();
}
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()
{
KUndo2MagicString actionName = kundo2_i18n("Duplicate Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->duplicateNode(selectedNodes());
}
KisNodeJugglerCompressed* KisNodeManager::Private::lazyGetJuggler(const KUndo2MagicString &actionName)
{
KisImageWSP image = view->image();
if (!nodeJuggler ||
(nodeJuggler &&
!nodeJuggler->canMergeAction(actionName))) {
nodeJuggler = new KisNodeJugglerCompressed(actionName, image, q, 750);
nodeJuggler->setAutoDelete(true);
}
return nodeJuggler;
}
void KisNodeManager::raiseNode()
{
KUndo2MagicString actionName = kundo2_i18n("Raise Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->raiseNode(selectedNodes());
}
void KisNodeManager::lowerNode()
{
KUndo2MagicString actionName = kundo2_i18n("Lower Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->lowerNode(selectedNodes());
}
void KisNodeManager::removeSingleNode(KisNodeSP node)
{
if (!node || !node->parent()) {
return;
}
KisNodeList nodes;
nodes << node;
removeSelectedNodes(nodes);
}
void KisNodeManager::removeSelectedNodes(KisNodeList nodes)
{
KUndo2MagicString actionName = kundo2_i18n("Remove Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->removeNode(nodes);
}
void KisNodeManager::removeNode()
{
removeSelectedNodes(selectedNodes());
}
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();
while (node && node->childCount() > 0 && node->isEditable()) {
node = node->firstChild();
}
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;
if (activeNode->childCount() > 0 && activeNode->isEditable()) {
node = activeNode->lastChild();
}
else {
node = activeNode->prevSibling();
}
while (!node && activeNode->parent()) {
node = activeNode->parent()->prevSibling();
activeNode = activeNode->parent();
}
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, "savenodeasimage");
dialog.setCaption(i18n("Export \"%1\"", defaultName));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export));
QString filename = dialog.filename();
if (filename.isEmpty()) return;
QUrl url = QUrl::fromLocalFile(filename);
if (url.isEmpty()) return;
QString mimefilter = KisMimeDatabase::mimeTypeForFile(filename);;
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) {
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);
}
void KisNodeManager::cutLayersToClipboard()
{
KisNodeList nodes = this->selectedNodes();
KisNodeSP root = m_d->view->image()->root();
if (nodes.isEmpty()) return;
KisClipboard::instance()->setLayers(nodes, root, false);
KUndo2MagicString actionName = kundo2_i18n("Cut Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->removeNode(nodes);
}
void KisNodeManager::copyLayersToClipboard()
{
KisNodeList nodes = this->selectedNodes();
KisNodeSP root = m_d->view->image()->root();
KisClipboard::instance()->setLayers(nodes, root, true);
}
void KisNodeManager::pasteLayersFromClipboard()
{
const QMimeData *data = KisClipboard::instance()->layersMimeData();
if (!data) return;
KisNodeSP activeNode = this->activeNode();
KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
Q_ASSERT(shapeController);
KisDummiesFacadeBase *dummiesFacade = dynamic_cast<KisDummiesFacadeBase*>(m_d->imageView->document()->shapeController());
Q_ASSERT(dummiesFacade);
const bool copyNode = false;
KisImageSP image = m_d->view->image();
KisNodeDummy *parentDummy = dummiesFacade->dummyForNode(activeNode);
KisNodeDummy *aboveThisDummy = parentDummy ? parentDummy->lastChild() : 0;
KisMimeData::insertMimeLayers(data,
image,
shapeController,
parentDummy,
aboveThisDummy,
copyNode,
nodeInsertionAdapter());
}
void KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
const QString &overrideGroupName,
KisNodeSP *newGroup,
KisNodeSP *newLastChild)
{
KisNodeSP active = activeNode();
if (!active) return;
KisImageSP image = m_d->view->image();
QString groupName = !overrideGroupName.isEmpty() ? overrideGroupName : image->nextLayerName();
KisGroupLayerSP group = new KisGroupLayer(image.data(), groupName, OPACITY_OPAQUE_U8);
KisNodeList nodes1;
nodes1 << group;
KisNodeList nodes2;
nodes2 = KisLayerUtils::sortMergableNodes(image->root(), selectedNodes());
KisLayerUtils::filterMergableNodes(nodes2);
if (KisLayerUtils::checkIsChildOf(active, nodes2)) {
active = nodes2.first();
}
KisNodeSP parent = active->parent();
KisNodeSP aboveThis = active;
juggler->addNode(nodes1, parent, aboveThis);
juggler->moveNode(nodes2, group, 0);
*newGroup = group;
*newLastChild = nodes2.last();
}
void KisNodeManager::createQuickGroup()
{
KUndo2MagicString actionName = kundo2_i18n("Quick Group");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
KisNodeSP parent;
KisNodeSP above;
createQuickGroupImpl(juggler, "", &parent, &above);
}
void KisNodeManager::createQuickClippingGroup()
{
KUndo2MagicString actionName = kundo2_i18n("Quick Clipping Group");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
KisNodeSP parent;
KisNodeSP above;
KisImageSP image = m_d->view->image();
createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above);
KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace());
maskLayer->disableAlphaChannel(true);
juggler->addNode(KisNodeList() << maskLayer, parent, above);
}
void KisNodeManager::quickUngroup()
{
KisNodeSP active = activeNode();
if (!active) return;
KisNodeSP parent = active->parent();
KisNodeSP aboveThis = active;
KUndo2MagicString actionName = kundo2_i18n("Quick Ungroup");
if (parent && dynamic_cast<KisGroupLayer*>(active.data())) {
KisNodeList nodes = active->childNodes(QStringList(), KoProperties());
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->moveNode(nodes, parent, active);
juggler->removeNode(KisNodeList() << active);
} else if (parent && parent->parent()) {
KisNodeSP grandParent = parent->parent();
KisNodeList allChildNodes = parent->childNodes(QStringList(), KoProperties());
KisNodeList allSelectedNodes = selectedNodes();
const bool removeParent = KritaUtils::compareListsUnordered(allChildNodes, allSelectedNodes);
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->moveNode(allSelectedNodes, grandParent, parent);
if (removeParent) {
juggler->removeNode(KisNodeList() << parent);
}
}
}
void KisNodeManager::selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps)
{
KisImageSP image = m_d->view->image();
KisNodeList nodes = KisLayerUtils::findNodesWithProps(image->root(), props, true);
KisNodeList selectedNodes = this->selectedNodes();
if (KritaUtils::compareListsUnordered(nodes, selectedNodes)) {
nodes = KisLayerUtils::findNodesWithProps(image->root(), invertedProps, true);
}
if (!nodes.isEmpty()) {
slotImageRequestNodeReselection(nodes.last(), nodes);
}
}
void KisNodeManager::selectAllNodes()
{
KoProperties props;
selectLayersImpl(props, props);
}
void KisNodeManager::selectVisibleNodes()
{
KoProperties props;
props.setProperty("visible", true);
KoProperties invertedProps;
invertedProps.setProperty("visible", false);
selectLayersImpl(props, invertedProps);
}
void KisNodeManager::selectLockedNodes()
{
KoProperties props;
props.setProperty("locked", true);
KoProperties invertedProps;
invertedProps.setProperty("locked", false);
selectLayersImpl(props, invertedProps);
}
void KisNodeManager::selectInvisibleNodes()
{
KoProperties props;
props.setProperty("visible", false);
KoProperties invertedProps;
invertedProps.setProperty("visible", true);
selectLayersImpl(props, invertedProps);
}
void KisNodeManager::selectUnlockedNodes()
{
KoProperties props;
props.setProperty("locked", false);
KoProperties invertedProps;
invertedProps.setProperty("locked", true);
selectLayersImpl(props, invertedProps);
}
diff --git a/libs/ui/kis_png_converter.cpp b/libs/ui/kis_png_converter.cpp
index 1248587a26..4d92108155 100644
--- a/libs/ui/kis_png_converter.cpp
+++ b/libs/ui/kis_png_converter.cpp
@@ -1,1267 +1,1268 @@
/*
* Copyright (c) 2005-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_png_converter.h"
// A big thank to Glenn Randers-Pehrson for his wonderful
// documentation of libpng available at
// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html
#ifndef PNG_MAX_UINT // Removed in libpng 1.4
#define PNG_MAX_UINT PNG_UINT_31_MAX
#endif
#include <KoConfig.h> // WORDS_BIGENDIAN
#include <KoStore.h>
#include <KoStoreDevice.h>
#include <limits.h>
#include <stdio.h>
#include <zlib.h>
#include <QBuffer>
#include <QFile>
#include <QApplication>
#include <klocalizedstring.h>
#include <QUrl>
#include <KoColorSpace.h>
#include <KoDocumentInfo.h>
#include <KoID.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoColor.h>
#include <KoUnit.h>
#include <kis_config.h>
#include <kis_painter.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_iterator_ng.h>
#include <kis_layer.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <metadata/kis_meta_data_io_backend.h>
#include <metadata/kis_meta_data_store.h>
#include <KoColorModelStandardIds.h>
#include "dialogs/kis_dlg_png_import.h"
#include "kis_clipboard.h"
namespace
{
int getColorTypeforColorSpace(const KoColorSpace * cs , bool alpha)
{
QString id = cs->id();
if (id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16") {
return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY;
}
if (id == "RGBA" || id == "RGBA16") {
return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
}
return -1;
}
bool colorSpaceIdSupported(const QString &id)
{
return id == "RGBA" || id == "RGBA16" ||
id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16";
}
QPair<QString, QString> getColorSpaceForColorType(int color_type, int color_nb_bits)
{
QPair<QString, QString> r;
if (color_type == PNG_COLOR_TYPE_PALETTE) {
r.first = RGBAColorModelID.id();
r.second = Integer8BitsColorDepthID.id();
} else {
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
r.first = GrayAColorModelID.id();
} else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) {
r.first = RGBAColorModelID.id();
}
if (color_nb_bits == 16) {
r.second = Integer16BitsColorDepthID.id();
} else if (color_nb_bits <= 8) {
r.second = Integer8BitsColorDepthID.id();
}
}
return r;
}
void fillText(png_text* p_text, const char* key, QString& text)
{
p_text->compression = PNG_TEXT_COMPRESSION_zTXt;
p_text->key = const_cast<char *>(key);
char* textc = new char[text.length()+1];
strcpy(textc, text.toLatin1());
p_text->text = textc;
p_text->text_length = text.length() + 1;
}
long formatStringList(char *string, const size_t length, const char *format, va_list operands)
{
int n = vsnprintf(string, length, format, operands);
if (n < 0)
string[length-1] = '\0';
return((long) n);
}
long formatString(char *string, const size_t length, const char *format, ...)
{
long n;
va_list operands;
va_start(operands, format);
n = (long) formatStringList(string, length, format, operands);
va_end(operands);
return(n);
}
void writeRawProfile(png_struct *ping, png_info *ping_info, QString profile_type, QByteArray profile_data)
{
png_textp text;
png_uint_32 allocated_length, description_length;
const uchar hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
dbgFile << "Writing Raw profile: type=" << profile_type << ", length=" << profile_data.length() << endl;
text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text));
description_length = profile_type.length();
allocated_length = (png_uint_32)(profile_data.length() * 2 + (profile_data.length() >> 5) + 20 + description_length);
text[0].text = (png_charp) png_malloc(ping, allocated_length);
QString key = QLatin1Literal("Raw profile type ") + profile_type.toLatin1();
QByteArray keyData = key.toLatin1();
text[0].key = keyData.data();
uchar* sp = (uchar*)profile_data.data();
png_charp dp = text[0].text;
*dp++ = '\n';
memcpy(dp, profile_type.toLatin1().constData(), profile_type.length());
dp += description_length;
*dp++ = '\n';
formatString(dp, allocated_length - strlen(text[0].text), "%8lu ", profile_data.length());
dp += 8;
for (long i = 0; i < (long) profile_data.length(); i++) {
if (i % 36 == 0)
*dp++ = '\n';
*(dp++) = (char) hex[((*sp >> 4) & 0x0f)];
*(dp++) = (char) hex[((*sp++) & 0x0f)];
}
*dp++ = '\n';
*dp = '\0';
text[0].text_length = (png_size_t)(dp - text[0].text);
text[0].compression = -1;
if (text[0].text_length <= allocated_length)
png_set_text(ping, ping_info, text, 1);
png_free(ping, text[0].text);
png_free(ping, text);
}
QByteArray png_read_raw_profile(png_textp text)
{
QByteArray profile;
static const unsigned char unhex[103] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12,
13, 14, 15
};
png_charp sp = text[0].text + 1;
/* look for newline */
while (*sp != '\n')
sp++;
/* look for length */
while (*sp == '\0' || *sp == ' ' || *sp == '\n')
sp++;
png_uint_32 length = (png_uint_32) atol(sp);
while (*sp != ' ' && *sp != '\n')
sp++;
if (length == 0) {
return profile;
}
profile.resize(length);
/* copy profile, skipping white space and column 1 "=" signs */
unsigned char *dp = (unsigned char*)profile.data();
png_uint_32 nibbles = length * 2;
for (png_uint_32 i = 0; i < nibbles; i++) {
while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f') {
if (*sp == '\0') {
return QByteArray();
}
sp++;
}
if (i % 2 == 0)
*dp = (unsigned char)(16 * unhex[(int) *sp++]);
else
(*dp++) += unhex[(int) *sp++];
}
return profile;
}
void decode_meta_data(png_textp text, KisMetaData::Store* store, QString type, int headerSize)
{
dbgFile << "Decoding " << type << " " << text[0].key;
KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value(type);
Q_ASSERT(exifIO);
QByteArray rawProfile = png_read_raw_profile(text);
if (headerSize > 0) {
rawProfile.remove(0, headerSize);
}
if (rawProfile.size() > 0) {
QBuffer buffer;
buffer.setData(rawProfile);
exifIO->loadFrom(store, &buffer);
} else {
dbgFile << "Decoding failed";
}
}
}
KisPNGConverter::KisPNGConverter(KisDocument *doc, bool batchMode)
{
// Q_ASSERT(doc);
// Q_ASSERT(adapter);
m_doc = doc;
m_stop = false;
m_max_row = 0;
m_image = 0;
m_batchMode = batchMode;
}
KisPNGConverter::~KisPNGConverter()
{
}
class KisPNGReadStream
{
public:
KisPNGReadStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) {
}
int nextValue() {
if (m_posinc == 0) {
m_posinc = 8;
m_buf++;
}
m_posinc -= m_depth;
return (((*m_buf) >> (m_posinc)) & ((1 << m_depth) - 1));
}
private:
quint32 m_posinc, m_depth;
quint8* m_buf;
};
class KisPNGWriteStream
{
public:
KisPNGWriteStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) {
*m_buf = 0;
}
void setNextValue(int v) {
if (m_posinc == 0) {
m_posinc = 8;
m_buf++;
*m_buf = 0;
}
m_posinc -= m_depth;
*m_buf = (v << m_posinc) | *m_buf;
}
private:
quint32 m_posinc, m_depth;
quint8* m_buf;
};
class KisPNGReaderAbstract
{
public:
KisPNGReaderAbstract(png_structp _png_ptr, int _width, int _height) : png_ptr(_png_ptr), width(_width), height(_height) {}
virtual ~KisPNGReaderAbstract() {}
virtual png_bytep readLine() = 0;
protected:
png_structp png_ptr;
int width, height;
};
class KisPNGReaderLineByLine : public KisPNGReaderAbstract
{
public:
KisPNGReaderLineByLine(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height) {
png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
row_pointer = new png_byte[rowbytes];
}
~KisPNGReaderLineByLine() override {
delete[] row_pointer;
}
png_bytep readLine() override {
png_read_row(png_ptr, row_pointer, 0);
return row_pointer;
}
private:
png_bytep row_pointer;
};
class KisPNGReaderFullImage : public KisPNGReaderAbstract
{
public:
KisPNGReaderFullImage(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height), y(0) {
row_pointers = new png_bytep[height];
png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
for (int i = 0; i < height; i++) {
row_pointers[i] = new png_byte[rowbytes];
}
png_read_image(png_ptr, row_pointers);
}
~KisPNGReaderFullImage() override {
for (int i = 0; i < height; i++) {
delete[] row_pointers[i];
}
delete[] row_pointers;
}
png_bytep readLine() override {
return row_pointers[y++];
}
private:
png_bytepp row_pointers;
int y;
};
static
void _read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
{
QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr);
while (length) {
int nr = in->read((char*)data, length);
if (nr <= 0) {
png_error(png_ptr, "Read Error");
return;
}
length -= nr;
}
}
static
void _write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
{
QIODevice* out = (QIODevice*)png_get_io_ptr(png_ptr);
uint nr = out->write((char*)data, length);
if (nr != length) {
png_error(png_ptr, "Write Error");
return;
}
}
static
void _flush_fn(png_structp png_ptr)
{
Q_UNUSED(png_ptr);
}
KisImageBuilder_Result KisPNGConverter::buildImage(QIODevice* iod)
{
dbgFile << "Start decoding PNG File";
- if (!iod->open(QIODevice::ReadOnly)) {
- dbgFile << "Failed to open PNG File";
- return (KisImageBuilder_RESULT_FAILURE);
- }
png_byte signature[8];
iod->peek((char*)signature, 8);
#if PNG_LIBPNG_VER < 10400
if (!png_check_sig(signature, 8)) {
#else
if (png_sig_cmp(signature, 0, 8) != 0) {
#endif
iod->close();
return (KisImageBuilder_RESULT_BAD_FETCH);
}
// Initialize the internal structures
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!png_ptr) {
iod->close();
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0);
iod->close();
return (KisImageBuilder_RESULT_FAILURE);
}
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info) {
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0);
iod->close();
return (KisImageBuilder_RESULT_FAILURE);
}
// Catch errors
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
iod->close();
return (KisImageBuilder_RESULT_FAILURE);
}
// Initialize the special
png_set_read_fn(png_ptr, iod, _read_fn);
#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
#endif
// read all PNG info up to image data
png_read_info(png_ptr, info_ptr);
if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8) {
png_set_expand(png_ptr);
}
if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE && png_get_bit_depth(png_ptr, info_ptr) < 8) {
png_set_packing(png_ptr);
}
if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE &&
(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) {
png_set_expand(png_ptr);
}
png_read_update_info(png_ptr, info_ptr);
// Read information about the png
png_uint_32 width, height;
int color_nb_bits, color_type, interlace_type;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, 0, 0);
dbgFile << "width = " << width << " height = " << height << " color_nb_bits = " << color_nb_bits << " color_type = " << color_type << " interlace_type = " << interlace_type << endl;
// swap byteorder on little endian machines.
#ifndef WORDS_BIGENDIAN
if (color_nb_bits > 8)
png_set_swap(png_ptr);
#endif
// Determine the colorspace
QPair<QString, QString> csName = getColorSpaceForColorType(color_type, color_nb_bits);
if (csName.first.isEmpty()) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
iod->close();
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA);
// Read image profile
png_charp profile_name;
#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
png_bytep profile_data;
#else
png_charp profile_data;
#endif
int compression_type;
png_uint_32 proflen;
// Get the various optional chunks
// https://www.w3.org/TR/PNG/#11cHRM
#if defined(PNG_cHRM_SUPPORTED)
double whitePointX, whitePointY;
double redX, redY;
double greenX, greenY;
double blueX, blueY;
png_get_cHRM(png_ptr,info_ptr, &whitePointX, &whitePointY, &redX, &redY, &greenX, &greenY, &blueX, &blueY);
qDebug() << "cHRM:" << whitePointX << whitePointY << redX << redY << greenX << greenY << blueX << blueY;
#endif
// https://www.w3.org/TR/PNG/#11gAMA
#if defined(PNG_GAMMA_SUPPORTED)
double gamma;
png_get_gAMA(png_ptr, info_ptr, &gamma);
qDebug() << "gAMA" << gamma;
#endif
// https://www.w3.org/TR/PNG/#11sRGB
#if defined(PNG_sRGB_SUPPORTED)
int sRGBIntent;
png_get_sRGB(png_ptr, info_ptr, &sRGBIntent);
qDebug() << "sRGB" << sRGBIntent;
#endif
bool fromBlender = false;
png_text* text_ptr;
int num_comments;
png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
for (int i = 0; i < num_comments; i++) {
QString key = QString(text_ptr[i].key).toLower();
if (key == "file") {
QString relatedFile = text_ptr[i].text;
if (relatedFile.contains(".blend", Qt::CaseInsensitive)){
fromBlender=true;
}
}
}
const KoColorProfile* profile = 0;
if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) {
QByteArray profile_rawdata;
// XXX: Hardcoded for icc type -- is that correct for us?
profile_rawdata.resize(proflen);
memcpy(profile_rawdata.data(), profile_data, proflen);
profile = KoColorSpaceRegistry::instance()->createColorProfile(csName.first, csName.second, profile_rawdata);
Q_CHECK_PTR(profile);
if (profile) {
// dbgFile << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo();
if (!profile->isSuitableForOutput()) {
dbgFile << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile"; // TODO: in ko2 popup a selection menu to inform the user
}
}
}
else {
dbgFile << "no embedded profile, will use the default profile";
if (color_nb_bits == 16 && !fromBlender && !qAppName().toLower().contains("test") && !m_batchMode) {
KisConfig cfg;
quint32 behaviour = cfg.pasteBehaviour();
if (behaviour == PASTE_ASK) {
KisDlgPngImport dlg(m_path, csName.first, csName.second);
QApplication::restoreOverrideCursor();
dlg.exec();
if (!dlg.profile().isEmpty()) {
QString s = KoColorSpaceRegistry::instance()->colorSpaceId(csName.first, csName.second);
const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s);
if (csf) {
QList<const KoColorProfile *> profileList = KoColorSpaceRegistry::instance()->profilesFor(csf);
Q_FOREACH (const KoColorProfile *p, profileList) {
if (p->name() == dlg.profile()) {
profile = p;
break;
}
}
}
}
QApplication::setOverrideCursor(Qt::WaitCursor);
}
}
dbgFile << "no embedded profile, will use the default profile";
}
// Check that the profile is used by the color space
if (profile && !KoColorSpaceRegistry::instance()->colorSpaceFactory(
KoColorSpaceRegistry::instance()->colorSpaceId(
csName.first, csName.second))->profileIsCompatible(profile)) {
warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << csName.first << " " << csName.second;
profile = 0;
}
// Retrieve a pointer to the colorspace
const KoColorSpace* cs;
if (profile && profile->isSuitableForOutput()) {
dbgFile << "image has embedded profile: " << profile->name() << "\n";
cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile);
}
else {
if (csName.first == RGBAColorModelID.id()) {
cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, "sRGB-elle-V2-srgbtrc.icc");
}
else {
cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, 0);
}
}
if (cs == 0) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
//TODO: two fixes : one tell the user about the problem and ask for a solution, and two once the kocolorspace include KoColorTransformation, use that instead of hacking a lcms transformation
// Create the cmsTransform if needed
KoColorTransformation* transform = 0;
if (profile && !profile->isSuitableForOutput()) {
transform = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
// Creating the KisImageWSP
if (m_image == 0) {
m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, "built image");
Q_CHECK_PTR(m_image);
}
// Read resolution
int unit_type;
png_uint_32 x_resolution, y_resolution;
png_get_pHYs(png_ptr, info_ptr, &x_resolution, &y_resolution, &unit_type);
- if (unit_type == PNG_RESOLUTION_METER) {
+ if (x_resolution > 0 && y_resolution > 0 && unit_type == PNG_RESOLUTION_METER) {
m_image->setResolution((double) POINT_TO_CM(x_resolution) / 100.0, (double) POINT_TO_CM(y_resolution) / 100.0); // It is the "invert" macro because we convert from pointer-per-inchs to points
}
double coeff = quint8_MAX / (double)(pow((double)2, color_nb_bits) - 1);
KisPaintLayerSP layer = new KisPaintLayer(m_image.data(), m_image -> nextLayerName(), UCHAR_MAX);
// Read comments/texts...
png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
if (m_doc) {
KoDocumentInfo * info = m_doc->documentInfo();
dbgFile << "There are " << num_comments << " comments in the text";
for (int i = 0; i < num_comments; i++) {
QString key = QString(text_ptr[i].key).toLower();
dbgFile << "key is |" << text_ptr[i].key << "| containing " << text_ptr[i].text << " " << (key == "Raw profile type exif ");
if (key == "title") {
info->setAboutInfo("title", text_ptr[i].text);
} else if (key == "description") {
info->setAboutInfo("comment", text_ptr[i].text);
} else if (key == "author") {
- qDebug()<<"Author:"<<text_ptr[i].text;
+ qDebug()<<"Author:"<<text_ptr[i].text;
info->setAuthorInfo("creator", text_ptr[i].text);
} else if (key.contains("Raw profile type exif")) {
decode_meta_data(text_ptr + i, layer->metaData(), "exif", 6);
} else if (key.contains("Raw profile type iptc")) {
decode_meta_data(text_ptr + i, layer->metaData(), "iptc", 14);
} else if (key.contains("Raw profile type xmp")) {
decode_meta_data(text_ptr + i, layer->metaData(), "xmp", 0);
} else if (key == "version") {
m_image->addAnnotation(new KisAnnotation("kpp_version", "version", QByteArray(text_ptr[i].text)));
} else if (key == "preset") {
m_image->addAnnotation(new KisAnnotation("kpp_preset", "preset", QByteArray(text_ptr[i].text)));
}
}
}
// Read image data
KisPNGReaderAbstract* reader = 0;
try {
if (interlace_type == PNG_INTERLACE_ADAM7) {
reader = new KisPNGReaderFullImage(png_ptr, info_ptr, width, height);
} else {
reader = new KisPNGReaderLineByLine(png_ptr, info_ptr, width, height);
}
} catch (std::bad_alloc& e) {
// new png_byte[] may raise such an exception if the image
// is invalid / to large.
dbgFile << "bad alloc: " << e.what();
// Free only the already allocated png_byte instances.
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return (KisImageBuilder_RESULT_FAILURE);
}
// Read the palette if the file is indexed
png_colorp palette ;
int num_palette;
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
}
// Read the transparency palette
quint8 palette_alpha[256];
memset(palette_alpha, 255, 256);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_bytep alpha_ptr;
int num_alpha;
png_get_tRNS(png_ptr, info_ptr, &alpha_ptr, &num_alpha, 0);
for (int i = 0; i < num_alpha; ++i) {
palette_alpha[i] = alpha_ptr[i];
}
}
}
for (png_uint_32 y = 0; y < height; y++) {
KisHLineIteratorSP it = layer -> paintDevice() -> createHLineIteratorNG(0, y, width);
png_bytep row_pointer = reader->readLine();
switch (color_type) {
case PNG_COLOR_TYPE_GRAY:
case PNG_COLOR_TYPE_GRAY_ALPHA:
if (color_nb_bits == 16) {
quint16 *src = reinterpret_cast<quint16 *>(row_pointer);
do {
quint16 *d = reinterpret_cast<quint16 *>(it->rawData());
d[0] = *(src++);
if (transform) transform->transform(reinterpret_cast<quint8*>(d), reinterpret_cast<quint8*>(d), 1);
if (hasalpha) {
d[1] = *(src++);
} else {
d[1] = quint16_MAX;
}
} while (it->nextPixel());
} else {
KisPNGReadStream stream(row_pointer, color_nb_bits);
do {
quint8 *d = it->rawData();
d[0] = (quint8)(stream.nextValue() * coeff);
if (transform) transform->transform(d, d, 1);
if (hasalpha) {
d[1] = (quint8)(stream.nextValue() * coeff);
} else {
d[1] = UCHAR_MAX;
}
} while (it->nextPixel());
}
// FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits"
break;
case PNG_COLOR_TYPE_RGB:
case PNG_COLOR_TYPE_RGB_ALPHA:
if (color_nb_bits == 16) {
quint16 *src = reinterpret_cast<quint16 *>(row_pointer);
do {
quint16 *d = reinterpret_cast<quint16 *>(it->rawData());
d[2] = *(src++);
d[1] = *(src++);
d[0] = *(src++);
if (transform) transform->transform(reinterpret_cast<quint8 *>(d), reinterpret_cast<quint8*>(d), 1);
if (hasalpha) d[3] = *(src++);
else d[3] = quint16_MAX;
} while (it->nextPixel());
} else {
KisPNGReadStream stream(row_pointer, color_nb_bits);
do {
quint8 *d = it->rawData();
d[2] = (quint8)(stream.nextValue() * coeff);
d[1] = (quint8)(stream.nextValue() * coeff);
d[0] = (quint8)(stream.nextValue() * coeff);
if (transform) transform->transform(d, d, 1);
if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff);
else d[3] = UCHAR_MAX;
} while (it->nextPixel());
}
break;
case PNG_COLOR_TYPE_PALETTE: {
KisPNGReadStream stream(row_pointer, color_nb_bits);
do {
quint8 *d = it->rawData();
quint8 index = stream.nextValue();
quint8 alpha = palette_alpha[ index ];
if (alpha == 0) {
memset(d, 0, 4);
} else {
png_color c = palette[ index ];
d[2] = c.red;
d[1] = c.green;
d[0] = c.blue;
d[3] = alpha;
}
} while (it->nextPixel());
}
break;
default:
return KisImageBuilder_RESULT_UNSUPPORTED;
}
}
m_image->addNode(layer.data(), m_image->rootLayer().data());
png_read_end(png_ptr, end_info);
iod->close();
// Freeing memory
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete reader;
return KisImageBuilder_RESULT_OK;
}
KisImageBuilder_Result KisPNGConverter::buildImage(const QString &filename)
{
m_path = filename;
QFile fp(filename);
if (fp.exists()) {
+ if (!fp.open(QIODevice::ReadOnly)) {
+ dbgFile << "Failed to open PNG File";
+ return (KisImageBuilder_RESULT_FAILURE);
+ }
+
return buildImage(&fp);
}
return (KisImageBuilder_RESULT_NOT_EXIST);
}
-KisImageWSP KisPNGConverter::image()
+KisImageSP KisPNGConverter::image()
{
return m_image;
}
bool KisPNGConverter::saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store* metaData)
{
if (store->open(filename)) {
KoStoreDevice io(store);
if (!io.open(QIODevice::WriteOnly)) {
dbgFile << "Could not open for writing:" << filename;
return false;
}
KisPNGConverter pngconv(0);
vKisAnnotationSP_it annotIt = 0;
KisMetaData::Store* metaDataStore = 0;
if (metaData) {
metaDataStore = new KisMetaData::Store(*metaData);
}
KisPNGOptions options;
options.compression = 0;
options.interlace = false;
options.tryToSaveAsIndexed = false;
options.alpha = true;
bool success = pngconv.buildFile(&io, imageRect, xRes, yRes, dev, annotIt, annotIt, options, metaDataStore);
if (success != KisImageBuilder_RESULT_OK) {
dbgFile << "Saving PNG failed:" << filename;
delete metaDataStore;
return false;
}
delete metaDataStore;
io.close();
if (!store->close()) {
return false;
}
} else {
dbgFile << "Opening of data file failed :" << filename;
return false;
}
return true;
}
KisImageBuilder_Result KisPNGConverter::buildFile(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData)
{
dbgFile << "Start writing PNG File " << filename;
// Open a QIODevice for writing
QFile fp (filename);
+ if (!fp.open(QIODevice::WriteOnly)) {
+ dbgFile << "Failed to open PNG File for writing";
+ return (KisImageBuilder_RESULT_FAILURE);
+ }
+
KisImageBuilder_Result result = buildFile(&fp, imageRect, xRes, yRes, device, annotationsStart, annotationsEnd, options, metaData);
return result;
}
KisImageBuilder_Result KisPNGConverter::buildFile(QIODevice* iodevice, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData)
{
- if (!iodevice->open(QIODevice::WriteOnly)) {
- dbgFile << "Failed to open PNG File for writing";
- return (KisImageBuilder_RESULT_FAILURE);
- }
-
if (!device)
return KisImageBuilder_RESULT_INVALID_ARG;
if (!options.alpha) {
KisPaintDeviceSP tmp = new KisPaintDevice(device->colorSpace());
KoColor c(options.transparencyFillColor, device->colorSpace());
tmp->fill(imageRect, c);
KisPainter gc(tmp);
gc.bitBlt(imageRect.topLeft(), device, imageRect);
gc.end();
device = tmp;
}
if (options.forceSRGB) {
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), device->colorSpace()->colorDepthId().id(), "sRGB built-in - (lcms internal)");
device = new KisPaintDevice(*device);
device->convertTo(cs);
}
// Initialize structures
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!png_ptr) {
return (KisImageBuilder_RESULT_FAILURE);
}
#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
#endif
#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
png_set_check_for_invalid_index(png_ptr, 0);
#endif
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_write_struct(&png_ptr, (png_infopp)0);
return (KisImageBuilder_RESULT_FAILURE);
}
// If an error occurs during writing, libpng will jump here
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
return (KisImageBuilder_RESULT_FAILURE);
}
// Initialize the writing
// png_init_io(png_ptr, fp);
// Setup the progress function
// XXX: Implement progress updating -- png_set_write_status_fn(png_ptr, progress);"
// setProgressTotalSteps(100/*height*/);
/* set the zlib compression level */
png_set_compression_level(png_ptr, options.compression);
png_set_write_fn(png_ptr, (void*)iodevice, _write_fn, _flush_fn);
/* set other zlib parameters */
png_set_compression_mem_level(png_ptr, 8);
png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
png_set_compression_window_bits(png_ptr, 15);
png_set_compression_method(png_ptr, 8);
png_set_compression_buffer_size(png_ptr, 8192);
int color_nb_bits = 8 * device->pixelSize() / device->channelCount();
int color_type = getColorTypeforColorSpace(device->colorSpace(), options.alpha);
Q_ASSERT(color_type > -1);
// Try to compute a table of color if the colorspace is RGB8f
png_colorp palette = 0;
int num_palette = 0;
if (!options.alpha && options.tryToSaveAsIndexed && KoID(device->colorSpace()->id()) == KoID("RGBA")) { // png doesn't handle indexed images and alpha, and only have indexed for RGB8
palette = new png_color[255];
KisSequentialIterator it(device, imageRect);
bool toomuchcolor = false;
do {
const quint8* c = it.oldRawData();
bool findit = false;
for (int i = 0; i < num_palette; i++) {
if (palette[i].red == c[2] &&
palette[i].green == c[1] &&
palette[i].blue == c[0]) {
findit = true;
break;
}
}
if (!findit) {
if (num_palette == 255) {
toomuchcolor = true;
break;
}
palette[num_palette].red = c[2];
palette[num_palette].green = c[1];
palette[num_palette].blue = c[0];
num_palette++;
}
} while (it.nextPixel());
if (!toomuchcolor) {
dbgFile << "Found a palette of " << num_palette << " colors";
color_type = PNG_COLOR_TYPE_PALETTE;
if (num_palette <= 2) {
color_nb_bits = 1;
} else if (num_palette <= 4) {
color_nb_bits = 2;
} else if (num_palette <= 16) {
color_nb_bits = 4;
} else {
color_nb_bits = 8;
}
} else {
delete [] palette;
}
}
int interlacetype = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
png_set_IHDR(png_ptr, info_ptr,
imageRect.width(),
imageRect.height(),
color_nb_bits,
color_type, interlacetype,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
// set sRGB only if the profile is sRGB -- http://www.w3.org/TR/PNG/#11sRGB says sRGB and iCCP should not both be present
bool sRGB = device->colorSpace()->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive);
/*
* This automatically writes the correct gamma and chroma chunks along with the sRGB chunk, but firefox's
* color management is bugged, so once you give it any incentive to start color managing an sRGB image it
* will turn, for example, a nice desaturated rusty red into bright poppy red. So this is disabled for now.
- */
+ */
/*if (!options.saveSRGBProfile && sRGB) {
- png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
+ png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
}*/
// set the palette
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
}
// Save annotation
vKisAnnotationSP_it it = annotationsStart;
while (it != annotationsEnd) {
if (!(*it) || (*it)->type().isEmpty()) {
dbgFile << "Warning: empty annotation";
it++;
continue;
}
dbgFile << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size();
if ((*it) -> type().startsWith(QString("krita_attribute:"))) { //
// Attribute
// XXX: it should be possible to save krita_attributes in the \"CHUNKs\""
dbgFile << "cannot save this annotation : " << (*it) -> type();
} else if ((*it)->type() == "kpp_version" || (*it)->type() == "kpp_preset" ) {
dbgFile << "Saving preset information " << (*it)->description();
png_textp text = (png_textp) png_malloc(png_ptr, (png_uint_32) sizeof(png_text));
QByteArray keyData = (*it)->description().toLatin1();
text[0].key = keyData.data();
text[0].text = (char*)(*it)->annotation().data();
text[0].text_length = (*it)->annotation().size();
text[0].compression = -1;
png_set_text(png_ptr, info_ptr, text, 1);
png_free(png_ptr, text);
}
it++;
}
// Save the color profile
const KoColorProfile* colorProfile = device->colorSpace()->profile();
QByteArray colorProfileData = colorProfile->rawData();
if (!sRGB || options.saveSRGBProfile) {
#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
png_set_iCCP(png_ptr, info_ptr, (char*)"icc", PNG_COMPRESSION_TYPE_BASE, (const png_bytep)colorProfileData.constData(), colorProfileData . size());
#else
png_set_iCCP(png_ptr, info_ptr, (char*)"icc", PNG_COMPRESSION_TYPE_BASE, (char*)colorProfileData.constData(), colorProfileData . size());
#endif
}
// read comments from the document information
// warning: according to the official png spec, the keys need to be capitalised!
if (m_doc) {
png_text texts[3];
int nbtexts = 0;
KoDocumentInfo * info = m_doc->documentInfo();
QString title = info->aboutInfo("title");
if (!title.isEmpty()) {
fillText(texts + nbtexts, "Title", title);
nbtexts++;
}
QString abstract = info->aboutInfo("comment");
if (!abstract.isEmpty()) {
fillText(texts + nbtexts, "Description", abstract);
nbtexts++;
}
QString author = info->authorInfo("creator");
if (!author.isEmpty()) {
fillText(texts + nbtexts, "Author", author);
nbtexts++;
}
png_set_text(png_ptr, info_ptr, texts, nbtexts);
}
// Save metadata following imagemagick way
// Save exif
if (metaData && !metaData->empty()) {
if (options.exif) {
dbgFile << "Trying to save exif information";
KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif");
Q_ASSERT(exifIO);
QBuffer buffer;
exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
writeRawProfile(png_ptr, info_ptr, "exif", buffer.data());
}
// Save IPTC
if (options.iptc) {
dbgFile << "Trying to save exif information";
KisMetaData::IOBackend* iptcIO = KisMetaData::IOBackendRegistry::instance()->value("iptc");
Q_ASSERT(iptcIO);
QBuffer buffer;
iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
dbgFile << "IPTC information size is" << buffer.data().size();
writeRawProfile(png_ptr, info_ptr, "iptc", buffer.data());
}
// Save XMP
if (options.xmp) {
dbgFile << "Trying to save XMP information";
KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp");
Q_ASSERT(xmpIO);
QBuffer buffer;
xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::NoHeader);
dbgFile << "XMP information size is" << buffer.data().size();
writeRawProfile(png_ptr, info_ptr, "xmp", buffer.data());
}
}
#if 0 // Unimplemented?
// Save resolution
int unit_type;
png_uint_32 x_resolution, y_resolution;
#endif
png_set_pHYs(png_ptr, info_ptr, CM_TO_POINT(xRes) * 100.0, CM_TO_POINT(yRes) * 100.0, PNG_RESOLUTION_METER); // It is the "invert" macro because we convert from pointer-per-inchs to points
// Save the information to the file
png_write_info(png_ptr, info_ptr);
png_write_flush(png_ptr);
// swap byteorder on little endian machines.
#ifndef WORDS_BIGENDIAN
if (color_nb_bits > 8)
png_set_swap(png_ptr);
#endif
// Write the PNG
// png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0);
// Fill the data structure
png_byte** row_pointers = new png_byte*[imageRect.height()];
int row = 0;
for (int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) {
KisHLineConstIteratorSP it = device->createHLineConstIteratorNG(imageRect.x(), y, imageRect.width());
row_pointers[row] = new png_byte[imageRect.width() * device->pixelSize()];
switch (color_type) {
case PNG_COLOR_TYPE_GRAY:
case PNG_COLOR_TYPE_GRAY_ALPHA:
if (color_nb_bits == 16) {
quint16 *dst = reinterpret_cast<quint16 *>(row_pointers[row]);
do {
const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
*(dst++) = d[0];
if (options.alpha) *(dst++) = d[1];
} while (it->nextPixel());
} else {
quint8 *dst = row_pointers[row];
do {
const quint8 *d = it->oldRawData();
*(dst++) = d[0];
if (options.alpha) *(dst++) = d[1];
} while (it->nextPixel());
}
break;
case PNG_COLOR_TYPE_RGB:
case PNG_COLOR_TYPE_RGB_ALPHA:
if (color_nb_bits == 16) {
quint16 *dst = reinterpret_cast<quint16 *>(row_pointers[row]);
do {
const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
*(dst++) = d[2];
*(dst++) = d[1];
*(dst++) = d[0];
if (options.alpha) *(dst++) = d[3];
} while (it->nextPixel());
} else {
quint8 *dst = row_pointers[row];
do {
const quint8 *d = it->oldRawData();
*(dst++) = d[2];
*(dst++) = d[1];
*(dst++) = d[0];
if (options.alpha) *(dst++) = d[3];
} while (it->nextPixel());
}
break;
case PNG_COLOR_TYPE_PALETTE: {
quint8 *dst = row_pointers[row];
KisPNGWriteStream writestream(dst, color_nb_bits);
do {
const quint8 *d = it->oldRawData();
int i;
for (i = 0; i < num_palette; i++) {
if (palette[i].red == d[2] &&
palette[i].green == d[1] &&
palette[i].blue == d[0]) {
break;
}
}
writestream.setNextValue(i);
} while (it->nextPixel());
}
break;
default:
delete[] row_pointers;
return KisImageBuilder_RESULT_UNSUPPORTED;
}
}
png_write_image(png_ptr, row_pointers);
// Writing is over
png_write_end(png_ptr, info_ptr);
// Free memory
png_destroy_write_struct(&png_ptr, &info_ptr);
for (int y = 0; y < imageRect.height(); y++) {
delete[] row_pointers[y];
}
delete[] row_pointers;
if (color_type == PNG_COLOR_TYPE_PALETTE) {
delete [] palette;
}
iodevice->close();
return KisImageBuilder_RESULT_OK;
}
void KisPNGConverter::cancel()
{
m_stop = true;
}
void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass)
{
if (png_ptr == 0 || row_number > PNG_MAX_UINT || pass > 7) return;
// setProgress(row_number);
}
bool KisPNGConverter::isColorSpaceSupported(const KoColorSpace *cs)
{
return colorSpaceIdSupported(cs->id());
}
diff --git a/libs/ui/kis_png_converter.h b/libs/ui/kis_png_converter.h
index 81d735b5c5..b13d89a00d 100644
--- a/libs/ui/kis_png_converter.h
+++ b/libs/ui/kis_png_converter.h
@@ -1,134 +1,134 @@
/*
* Copyright (c) 2005, 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 _KIS_PNG_CONVERTER_H_
#define _KIS_PNG_CONVERTER_H_
#include <png.h>
#include <QColor>
#include <QVector>
#include "kis_types.h"
#include "kis_global.h"
#include "kis_annotation.h"
#include <kritaui_export.h>
#include <KisImageBuilderResult.h>
class KoStore;
class KisDocument;
class KoColorSpace;
namespace KisMetaData
{
class Filter;
class Store;
}
struct KisPNGOptions {
KisPNGOptions()
: compression(0)
, interlace(false)
, alpha(true)
, exif(true)
, iptc(true)
, xmp(true)
, tryToSaveAsIndexed(true)
, saveSRGBProfile(false)
, forceSRGB(false)
, transparencyFillColor(Qt::white)
{}
int compression;
bool interlace;
bool alpha;
bool exif;
bool iptc;
bool xmp;
bool tryToSaveAsIndexed;
bool saveSRGBProfile;
bool forceSRGB;
QList<const KisMetaData::Filter*> filters;
QColor transparencyFillColor;
};
/**
* This class allows to import/export a PNG from either a file or a QIODevice.
*/
// XXX_PROGRESS (pass KoUpdater to the png converter)
class KRITAUI_EXPORT KisPNGConverter : public QObject
{
Q_OBJECT
public:
/**
* Initialize the converter.
* @param doc the KisDocument related to the image, can be null if you don't have a KisDocument
* @param adapter the undo adapter to be used by the image, can be null if you don't want to use an undo adapter
*/
KisPNGConverter(KisDocument *doc, bool batchMode = false);
virtual ~KisPNGConverter();
public:
/**
* Load an image from an URL. If the image is not on a local drive, the image is first downloaded to a
* temporary location.
* @param uri the url of the image
*/
KisImageBuilder_Result buildImage(const QString &filename);
/**
* Load an image from a QIODevice.
* @param iod device to access the data
*/
KisImageBuilder_Result buildImage(QIODevice* iod);
/**
* Save a layer to a PNG
* @param uri the url of the destination file
* @param device the paint device to save
* @param annotationsStart an iterator on the first annotation
* @param annotationsEnd an iterator on the last annotation
* @param compression a number between 0 and 9 to specify the compression rate (9 is most compressed)
* @param interlace set to true if you want to generate an interlaced png
* @param alpha set to true if you want to save the alpha channel
*/
KisImageBuilder_Result buildFile(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData);
KisImageBuilder_Result buildFile(QIODevice*, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData);
/**
* Retrieve the constructed image
*/
- KisImageWSP image();
+ KisImageSP image();
static bool saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store* metaData = 0);
static bool isColorSpaceSupported(const KoColorSpace *cs);
public Q_SLOTS:
virtual void cancel();
private:
void progress(png_structp png_ptr, png_uint_32 row_number, int pass);
private:
png_uint_32 m_max_row;
- KisImageWSP m_image;
+ KisImageSP m_image;
KisDocument *m_doc;
bool m_stop;
bool m_batchMode;
QString m_path;
};
#endif
diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp
index d62b76f956..ca67a329f4 100644
--- a/libs/ui/kis_popup_palette.cpp
+++ b/libs/ui/kis_popup_palette.cpp
@@ -1,634 +1,858 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman <shicmap@gmail.com>
Copyright 2011 Sven Langkamp <sven.langkamp@gmail.com>
+ Copyright 2016 Scott Petrovic <scottpetrovic@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 "kis_icon_utils.h"
#include <brushengine/kis_paintop_preset.h>
#include "kis_resource_server_provider.h"
#include <kis_canvas_resource_provider.h>
#include <KoTriangleColorSelector.h>
#include <kis_visual_color_selector.h>
#include <kis_config_notifier.h>
#include "KoColorSpaceRegistry.h"
#include <kis_types.h>
#include <QtGui>
#include <kis_debug.h>
#include <QQueue>
#include <QMenu>
#include <QWhatsThis>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QSpacerItem>
#include <QDebug>
#include <math.h>
#include "kis_signal_compressor.h"
#include <QApplication>
#include "brushhud/kis_brush_hud.h"
#include "brushhud/kis_round_hud_button.h"
+#include <kis_action.h>
-#define colorInnerRadius 72.0
-#define colorOuterRadius 92.0
-#define maxbrushRadius 42.0
-#define widgetSize (colorOuterRadius*2+maxbrushRadius*4)
-#define widgetMargin 20.0
-#define hudMargin 30.0
-#define _USE_MATH_DEFINES 1
-#include <cmath>
-
-
-class PopupColorTriangle : public KoTriangleColorSelector
-{
-public:
- PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent)
- : KoTriangleColorSelector(displayRenderer, parent)
- , m_dragging(false) {
- }
-
- ~PopupColorTriangle() override {}
-
- void tabletEvent(QTabletEvent* event) override {
- 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, KisCanvasResourceProvider *provider, QWidget *parent)
+KisPopupPalette::KisPopupPalette(KisViewManager* viewManager, KisCoordinatesConverter* coordinatesConverter ,KisFavoriteResourceManager* manager,
+ const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent)
: QWidget(parent, Qt::FramelessWindowHint)
+ , m_hoveredPreset(0)
+ , m_hoveredColor(0)
+ , m_selectedColor(0)
+ , m_coordinatesConverter(coordinatesConverter)
+ , m_actionManager(viewManager->actionManager())
, m_resourceManager(manager)
, m_triangleColorSelector(0)
- , m_timer(0)
, m_displayRenderer(displayRenderer)
, m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE))
+ , m_actionCollection(viewManager->actionCollection())
, m_brushHud(0)
+ , m_popupPaletteSize(385.0)
+ , m_colorHistoryInnerRadius(72.0)
+ , m_colorHistoryOuterRadius(92.0)
+ , m_isOverCanvasRotationIndicator(false)
+ , m_isRotatingCanvasIndicator(false)
{
+ // some UI controls are defined and created based off these variables
const int borderWidth = 3;
- //m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this);
+
m_triangleColorSelector = new KisVisualColorSelector(this);
m_triangleColorSelector->setDisplayRenderer(displayRenderer);
m_triangleColorSelector->setConfig(true,false);
- m_triangleColorSelector->move(widgetSize/2-colorInnerRadius+borderWidth, widgetSize/2-colorInnerRadius+borderWidth);
- m_triangleColorSelector->resize(colorInnerRadius*2-borderWidth*2, colorInnerRadius*2-borderWidth*2);
+ m_triangleColorSelector->move(m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth, m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth);
+ m_triangleColorSelector->resize(m_colorHistoryInnerRadius*2-borderWidth*2, m_colorHistoryInnerRadius*2-borderWidth*2);
m_triangleColorSelector->setVisible(true);
KoColor fgcolor(Qt::black, KoColorSpaceRegistry::instance()->rgb8());
if (m_resourceManager) {
fgcolor = provider->fgColor();
}
m_triangleColorSelector->slotSetColor(fgcolor);
QRegion maskedRegion(0, 0, m_triangleColorSelector->width(), m_triangleColorSelector->height(), QRegion::Ellipse );
m_triangleColorSelector->setMask(maskedRegion);
//setAttribute(Qt::WA_TranslucentBackground, true);
connect(m_triangleColorSelector, SIGNAL(sigNewColor(KoColor)),
m_colorChangeCompressor.data(), SLOT(start()));
connect(m_colorChangeCompressor.data(), SIGNAL(timeout()),
SLOT(slotEmitColorChanged()));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_triangleColorSelector, SLOT(ConfigurationChanged()));
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);
+ m_timer.setSingleShot(true);
connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer()));
- connect(m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor()));
+ connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor()));
connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool)));
setCursor(Qt::ArrowCursor);
setMouseTracking(true);
setHoveredPreset(-1);
setHoveredColor(-1);
setSelectedColor(-1);
m_brushHud = new KisBrushHud(provider, parent);
- m_brushHud->setMaximumHeight(widgetSize);
+ m_brushHud->setMaximumHeight(m_popupPaletteSize);
m_brushHud->setVisible(false);
const int auxButtonSize = 35;
m_settingsButton = new KisRoundHudButton(this);
m_settingsButton->setIcon(KisIconUtils::loadIcon("configure"));
- m_settingsButton->setGeometry(widgetSize - 2.2 * auxButtonSize, widgetSize - auxButtonSize,
+ m_settingsButton->setGeometry(m_popupPaletteSize - 2.2 * auxButtonSize, m_popupPaletteSize - auxButtonSize,
auxButtonSize, auxButtonSize);
connect(m_settingsButton, SIGNAL(clicked()), SLOT(slotShowTagsPopup()));
KisConfig cfg;
m_brushHudButton = new KisRoundHudButton(this);
m_brushHudButton->setCheckable(true);
m_brushHudButton->setOnOffIcons(KisIconUtils::loadIcon("arrow-left"), KisIconUtils::loadIcon("arrow-right"));
- m_brushHudButton->setGeometry(widgetSize - 1.0 * auxButtonSize, widgetSize - auxButtonSize,
+ m_brushHudButton->setGeometry(m_popupPaletteSize - 1.0 * auxButtonSize, m_popupPaletteSize - auxButtonSize,
auxButtonSize, auxButtonSize);
connect(m_brushHudButton, SIGNAL(toggled(bool)), SLOT(showHudWidget(bool)));
m_brushHudButton->setChecked(cfg.showBrushHud());
+
+ // add some stuff below the pop-up palette that will make it easier to use for tablet people
+ QVBoxLayout* vLayout = new QVBoxLayout(this); // main layout
+
+ QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
+ vLayout->addSpacerItem(verticalSpacer); // this should push the box to the bottom
+
+
+ QHBoxLayout* hLayout = new QHBoxLayout();
+
+ vLayout->addLayout(hLayout);
+
+ mirrorMode = new KisHighlightedToolButton(this);
+ mirrorMode->setCheckable(true);
+ mirrorMode->setFixedSize(35, 35);
+ mirrorMode->setIcon(KisIconUtils::loadIcon("symmetry-horizontal"));
+ mirrorMode->setToolTip(i18n("Mirror Canvas"));
+ connect(mirrorMode, SIGNAL(clicked(bool)), this, SLOT(slotmirroModeClicked()));
+
+
+ canvasOnlyButton = new KisHighlightedToolButton(this);
+ canvasOnlyButton->setCheckable(true);
+ canvasOnlyButton->setFixedSize(35, 35);
+ canvasOnlyButton->setIcon(KisIconUtils::loadIcon("document-new"));
+ canvasOnlyButton->setToolTip(i18n("Canvas Only"));
+ connect(canvasOnlyButton, SIGNAL(clicked(bool)), this, SLOT(slotCanvasonlyModeClicked()));
+
+ zoomToOneHundredPercentButton = new QPushButton(this);
+ zoomToOneHundredPercentButton->setText(i18n("100%"));
+ zoomToOneHundredPercentButton->setFixedHeight(35);
+ zoomToOneHundredPercentButton->setIcon(KisIconUtils::loadIcon("zoom-original"));
+ zoomToOneHundredPercentButton->setToolTip(i18n("Zoom to 100%"));
+ connect(zoomToOneHundredPercentButton, SIGNAL(clicked(bool)), this, SLOT(slotZoomToOneHundredPercentClicked()));
+
+ zoomCanvasSlider = new QSlider(Qt::Horizontal, this);
+ zoomCanvasSlider->setRange(10, 200); // 10% to 200 %
+ zoomCanvasSlider->setFixedHeight(35);
+ zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent());
+
+ zoomCanvasSlider->setSingleStep(1);
+ zoomCanvasSlider->setPageStep(1);
+
+ connect(zoomCanvasSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int)));
+
+
+ hLayout->addWidget(mirrorMode);
+ hLayout->addWidget(canvasOnlyButton);
+ hLayout->addWidget(zoomToOneHundredPercentButton);
+ hLayout->addWidget(zoomCanvasSlider);
+
setVisible(true);
setVisible(false);
}
void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color)
{
//m_triangleColorSelector->setRealColor(color);
//hack to get around cmyk for now.
if (color.colorSpace()->colorChannelCount()>3) {
KoColor c(KoColorSpaceRegistry::instance()->rgb8());
c.fromKoColor(color);
m_triangleColorSelector->slotSetColor(c);
} else {
m_triangleColorSelector->slotSetColor(color);
}
}
void KisPopupPalette::slotEmitColorChanged()
{
if (isVisible()) {
update();
emit sigChangefGColor(m_triangleColorSelector->getCurrentColor());
}
}
//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);
+ m_timer.start(750);
}
void KisPopupPalette::slotEnableChangeFGColor()
{
emit sigEnableChangeFGColor(true);
}
+void KisPopupPalette::slotZoomSliderChanged(int zoom) {
+ emit zoomLevelChanged(zoom);
+}
+
void KisPopupPalette::adjustLayout(const QPoint &p)
{
KIS_ASSERT_RECOVER_RETURN(m_brushHud);
if (isVisible() && parentWidget()) {
- const QRect fitRect = kisGrowRect(parentWidget()->rect(), -widgetMargin);
- const QPoint paletteCenterOffset(width() / 2, height() / 2);
+
+ float hudMargin = 30.0;
+ const QRect fitRect = kisGrowRect(parentWidget()->rect(), -20.0); // -20 is widget margin
+ const QPoint paletteCenterOffset(m_popupPaletteSize / 2, m_popupPaletteSize / 2);
QRect paletteRect = rect();
paletteRect.moveTo(p - paletteCenterOffset);
if (m_brushHudButton->isChecked()) {
m_brushHud->updateGeometry();
paletteRect.adjust(0, 0, m_brushHud->width() + hudMargin, 0);
}
paletteRect = kisEnsureInRect(paletteRect, fitRect);
move(paletteRect.topLeft());
- m_brushHud->move(paletteRect.topLeft() + QPoint(widgetSize + hudMargin, 0));
+ m_brushHud->move(paletteRect.topLeft() + QPoint(m_popupPaletteSize + hudMargin, 0));
m_lastCenterPoint = p;
}
}
void KisPopupPalette::showHudWidget(bool visible)
{
KIS_ASSERT_RECOVER_RETURN(m_brushHud);
const bool reallyVisible = visible && m_brushHudButton->isChecked();
if (reallyVisible) {
m_brushHud->updateProperties();
}
m_brushHud->setVisible(reallyVisible);
adjustLayout(m_lastCenterPoint);
KisConfig cfg;
cfg.setShowBrushHud(visible);
}
void KisPopupPalette::showPopupPalette(const QPoint &p)
{
showPopupPalette(!isVisible());
adjustLayout(p);
}
void KisPopupPalette::showPopupPalette(bool show)
{
if (show) {
+ zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); // sync the zoom slider
emit sigEnableChangeFGColor(!show);
} else {
emit sigTriggerTimer();
}
setVisible(show);
m_brushHud->setVisible(show && m_brushHudButton->isChecked());
}
//redefinition of setVariable function to change the scope to private
void KisPopupPalette::setVisible(bool b)
{
QWidget::setVisible(b);
}
void KisPopupPalette::setParent(QWidget *parent) {
m_brushHud->setParent(parent);
QWidget::setParent(parent);
}
QSize KisPopupPalette::sizeHint() const
{
- return QSize(widgetSize, widgetSize);
+ return QSize(m_popupPaletteSize, m_popupPaletteSize + 50); // last number is the space for the toolbar below
}
void KisPopupPalette::resizeEvent(QResizeEvent*)
{
}
void KisPopupPalette::paintEvent(QPaintEvent* e)
{
Q_UNUSED(e);
- float rotationAngle = 0.0;
-
QPainter painter(this);
+
+ QPen pen(palette().color(QPalette::Text));
+ pen.setWidth(3);
+ painter.setPen(pen);
+
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
- painter.translate(width() / 2, height() / 2);
-
- //painting background color
+ //painting background color indicator
QPainterPath bgColor;
- bgColor.addEllipse(QPoint(-width() / 2 + 24, -height() / 2 + 60), 20, 20);
+ bgColor.addEllipse(QPoint( 50, 80), 30, 30);
painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor()));
painter.drawPath(bgColor);
- //painting foreground color
+ //painting foreground color indicator
QPainterPath fgColor;
- fgColor.addEllipse(QPoint(-width() / 2 + 50, -height() / 2 + 32), 30, 30);
+ fgColor.addEllipse(QPoint( 60, 50), 30, 30);
painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->getCurrentColor()));
painter.drawPath(fgColor);
- // create an ellipse for the background that is slightly
- // smaller than the clipping mask. This will prevent aliasing
+ // create a circle background that everything else will go into
QPainterPath backgroundContainer;
- backgroundContainer.addEllipse( -colorOuterRadius, -colorOuterRadius,
- colorOuterRadius*2, colorOuterRadius*2 );
- painter.fillPath(backgroundContainer,palette().brush(QPalette::Window));
+ float shrinkCircleAmount = 3;// helps the circle when the stroke is put around it
+
+ QRectF circleRect(shrinkCircleAmount, shrinkCircleAmount,
+ m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2);
+
+
+ backgroundContainer.addEllipse( circleRect );
+ painter.fillPath(backgroundContainer,palette().brush(QPalette::Background));
+
painter.drawPath(backgroundContainer);
+
+ // create a path slightly inside the container circle. this will create a 'track' to indicate that we can rotate the canvas
+ // with the indicator
+ QPainterPath rotationTrackPath;
+ shrinkCircleAmount = 18;
+ QRectF circleRect2(shrinkCircleAmount, shrinkCircleAmount,
+ m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2);
+
+ rotationTrackPath.addEllipse( circleRect2 );
+ pen.setWidth(1);
+ painter.setPen(pen);
+ painter.drawPath(rotationTrackPath);
+
+
+
+
+ // this thing will help indicate where the starting brush preset is at.
+ // also what direction they go to give sor order to the presets populated
+ /*
+ pen.setWidth(6);
+ pen.setCapStyle(Qt::RoundCap);
+ painter.setPen(pen);
+ painter.drawArc(circleRect, (16*90), (16*-30)); // span angle (last parameter) is in 16th of degrees
+
+ QPainterPath brushDir;
+ brushDir.arcMoveTo(circleRect, 60);
+ brushDir.lineTo(brushDir.currentPosition().x()-5, brushDir.currentPosition().y() - 14);
+ painter.drawPath(brushDir);
+
+ brushDir.lineTo(brushDir.currentPosition().x()-2, brushDir.currentPosition().y() + 6);
+ painter.drawPath(brushDir);
+
+ */
+
+
+ // the following things needs to be based off the center, so let's translate the painter
+ painter.translate(m_popupPaletteSize / 2, m_popupPaletteSize / 2);
+
+
+
+ // create the canvas rotation handle
+ QPainterPath rotationIndicator = drawRotationIndicator(m_coordinatesConverter->rotationAngle(), true);
+
+ painter.fillPath(rotationIndicator,palette().brush(QPalette::Text));
+
+ // hover indicator for the canvas rotation
+ if (m_isOverCanvasRotationIndicator == true) {
+ painter.save();
+
+ QPen pen(palette().color(QPalette::Highlight));
+ pen.setWidth(2);
+ painter.setPen(pen);
+ painter.drawPath(rotationIndicator);
+
+ painter.restore();
+ }
+
+
+
+ // create a reset canvas rotation indicator to bring the canvas back to 0 degrees
+ QPainterPath resetRotationIndicator = drawRotationIndicator(0, false);
+
+ QPen resetPen(palette().color(QPalette::Text));
+ resetPen.setWidth(1);
+ painter.save();
+ painter.setPen(resetPen);
+ painter.drawPath(resetRotationIndicator);
+
+ painter.restore();
+
+
+
//painting favorite brushes
QList<QImage> images(m_resourceManager->favoritePresetImages());
//painting favorite brushes pixmap/icon
- QPainterPath path;
+ QPainterPath presetPath;
for (int pos = 0; pos < numSlots(); pos++) {
painter.save();
- path = pathFromPresetIndex(pos);
+ presetPath = createPathFromPresetIndex(pos);
if (pos < images.size()) {
- painter.setClipPath(path);
+ painter.setClipPath(presetPath);
- QRect bounds = path.boundingRect().toAlignedRect();
+ QRect bounds = presetPath.boundingRect().toAlignedRect();
painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
}
else {
- painter.fillPath(path, palette().brush(QPalette::Window));
+ painter.fillPath(presetPath, palette().brush(QPalette::Window)); // brush slot that has no brush in it
}
QPen pen = painter.pen();
- pen.setWidth(3);
+ pen.setWidth(1);
painter.setPen(pen);
- painter.drawPath(path);
+ painter.drawPath(presetPath);
painter.restore();
}
if (hoveredPreset() > -1) {
- path = pathFromPresetIndex(hoveredPreset());
+ presetPath = createPathFromPresetIndex(hoveredPreset());
QPen pen(palette().color(QPalette::Highlight));
pen.setWidth(3);
painter.setPen(pen);
- painter.drawPath(path);
+ painter.drawPath(presetPath);
}
- //painting recent colors
- painter.setPen(Qt::NoPen);
- rotationAngle = -360.0 / m_resourceManager->recentColorsTotal();
+ // paint recent colors area.
- KoColor kocolor;
+ painter.setPen(Qt::NoPen);
+ float rotationAngle = -360.0 / m_resourceManager->recentColorsTotal();
- for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) {
- QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal()));
+ // there might be no recent colors at the start, so paint a placeholder
+ if (m_resourceManager->recentColorsTotal() == 0) {
+ painter.setBrush(Qt::transparent);
- //accessing recent color of index pos
- kocolor = m_resourceManager->recentColorAt(pos);
- painter.fillPath(path, m_displayRenderer->toQColor(kocolor));
+ QPainterPath emptyRecentColorsPath(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
+ painter.setPen(QPen(palette().color(QPalette::Background).lighter(150), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
+ painter.drawPath(emptyRecentColorsPath);
+ } else {
- painter.drawPath(path);
- painter.rotate(rotationAngle);
- }
+ for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) {
+ QPainterPath recentColorsPath(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal()));
- painter.setBrush(Qt::transparent);
+ //accessing recent color of index pos
+ painter.fillPath(recentColorsPath, m_displayRenderer->toQColor( m_resourceManager->recentColorAt(pos) ));
+ painter.drawPath(recentColorsPath);
+ painter.rotate(rotationAngle);
+ }
- 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));
+ QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
painter.drawPath(path_ColorDonut);
} else {
painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle);
- QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal()));
+ QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, 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));
+ QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
painter.drawPath(path_ColorDonut);
} else {
painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle);
- QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal()));
+ QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal()));
painter.drawPath(path);
painter.rotate(selectedColor() * -1 * rotationAngle);
}
}
}
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;
}
+QPainterPath KisPopupPalette::drawRotationIndicator(qreal rotationAngle, bool canDrag)
+{
+ // used for canvas rotation. This function gets called twice. Once by the canvas rotation indicator,
+ // and another time by the reset canvas position
+
+ float canvasRotationRadians = qDegreesToRadians(rotationAngle - 90); // -90 will make 0 degrees be at the top
+ float rotationDialXPosition = qCos(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); // m_popupPaletteSize/2 = radius
+ float rotationDialYPosition = qSin(canvasRotationRadians) * (m_popupPaletteSize/2 - 10);
+
+ QPainterPath canvasRotationIndicator;
+ int canvasIndicatorSize = 15;
+ float canvasIndicatorMiddle = canvasIndicatorSize/2;
+ QRect indicatorRectangle = QRect( rotationDialXPosition - canvasIndicatorMiddle, rotationDialYPosition - canvasIndicatorMiddle,
+ canvasIndicatorSize, canvasIndicatorSize );
+
+ if (canDrag) {
+ m_canvasRotationIndicatorRect = indicatorRectangle;
+ } else {
+ m_resetCanvasRotationIndicatorRect = indicatorRectangle;
+ }
+
+ canvasRotationIndicator.addEllipse(indicatorRectangle.x(), indicatorRectangle.y(),
+ indicatorRectangle.width(), indicatorRectangle.height() );
+
+ return canvasRotationIndicator;
+
+}
+
+
void KisPopupPalette::mouseMoveEvent(QMouseEvent* event)
{
QPointF point = event->posF();
event->accept();
- QPainterPath pathColor(drawDonutPathFull(width() / 2, height() / 2, colorInnerRadius, colorOuterRadius));
-
- setToolTip("");
+ setToolTip(QString());
setHoveredPreset(-1);
setHoveredColor(-1);
- {
- int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets());
- if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) {
- setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name());
- setHoveredPreset(pos);
- }
+ // calculate if we are over the canvas rotation knob
+ // before we started painting, we moved the painter to the center of the widget, so the X/Y positions are offset. we need to
+ // correct them first before looking for a click event intersection
+
+ float rotationCorrectedXPos = m_canvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2);
+ float rotationCorrectedYPos = m_canvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2);
+ QRect correctedCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos,
+ m_canvasRotationIndicatorRect.width(), m_canvasRotationIndicatorRect.height());
+
+ if (correctedCanvasRotationIndicator.contains(point.x(), point.y())) {
+ m_isOverCanvasRotationIndicator = true;
+ } else {
+ m_isOverCanvasRotationIndicator = false;
}
- if (pathColor.contains(point)) {
- int pos = calculateIndex(point, m_resourceManager->recentColorsTotal());
- if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) {
- setHoveredColor(pos);
+
+ if (m_isRotatingCanvasIndicator) {
+ // we are rotating the canvas, so calculate the rotation angle based off the center
+ // calculate the angle we are at first
+ QPoint widgetCenterPoint = QPoint(m_popupPaletteSize/2, m_popupPaletteSize/2);
+
+ float dX = point.x() - widgetCenterPoint.x();
+ float dY = point.y() - widgetCenterPoint.y();
+
+
+ float finalAngle = qAtan2(dY,dX) * 180 / M_PI; // what we need if we have two points, but don't know the angle
+ finalAngle = finalAngle + 90; // add 90 degrees so 0 degree position points up
+ float angleDifference = finalAngle - m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it out
+ m_coordinatesConverter->rotate(m_coordinatesConverter->widgetCenterPoint(), angleDifference);
+ emit sigUpdateCanvas();
+
+ }
+
+
+ // don't highlight the presets if we are in the middle of rotating the canvas
+ if (m_isRotatingCanvasIndicator == false) {
+ QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
+ {
+ int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets());
+
+ if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) {
+ setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name());
+ setHoveredPreset(pos);
+ }
+ }
+ 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) {
//in favorite brushes area
int pos = calculateIndex(point, m_resourceManager->numFavoritePresets());
if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()
&& isPointInPixmap(point, pos)) {
//setSelectedBrush(pos);
update();
}
+
+ if (m_isOverCanvasRotationIndicator) {
+ m_isRotatingCanvasIndicator = true;
+ }
+
+ // reset the canvas if we are over the reset canvas rotation indicator
+ float rotationCorrectedXPos = m_resetCanvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2);
+ float rotationCorrectedYPos = m_resetCanvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2);
+ QRect correctedResetCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos,
+ m_resetCanvasRotationIndicatorRect.width(), m_resetCanvasRotationIndicatorRect.height());
+
+
+ if (correctedResetCanvasRotationIndicator.contains(point.x(), point.y())) {
+ float angleDifference = -m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it ou
+ m_coordinatesConverter->rotate(m_coordinatesConverter->widgetCenterPoint(), angleDifference);
+ emit sigUpdateCanvas();
+ }
}
}
void KisPopupPalette::slotShowTagsPopup()
{
KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
QStringList tags = rServer->tagNamesList();
qSort(tags);
if (!tags.isEmpty()) {
QMenu menu;
Q_FOREACH (const QString& tag, tags) {
menu.addAction(tag);
}
QAction* action = menu.exec(QCursor::pos());
if (action) {
m_resourceManager->setCurrentTag(action->text());
}
} else {
QWhatsThis::showText(QCursor::pos(),
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::slotmirroModeClicked() {
+ QAction* action = m_actionCollection->action("mirror_canvas");
+
+ if (action) {
+ action->trigger();
+ }
+}
+
+void KisPopupPalette::slotCanvasonlyModeClicked() {
+ QAction* action = m_actionCollection->action("view_show_canvas_only");
+
+ if (action) {
+ action->trigger();
+ }
+}
+
+
+void KisPopupPalette::slotZoomToOneHundredPercentClicked() {
+ QAction* action = m_actionCollection->action("zoom_to_100pct");
+
+ if (action) {
+ action->trigger();
+ }
+}
+
+
+
+
void KisPopupPalette::tabletEvent(QTabletEvent* /*event*/) {
}
void KisPopupPalette::mouseReleaseEvent(QMouseEvent * event)
{
QPointF point = event->posF();
event->accept();
+ m_isOverCanvasRotationIndicator = false;
+ m_isRotatingCanvasIndicator = false;
+
if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) {
- QPainterPath pathColor(drawDonutPathFull(width() / 2, height() / 2, colorInnerRadius, colorOuterRadius));
+ QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
//in favorite brushes area
if (hoveredPreset() > -1) {
//setSelectedBrush(hoveredBrush());
emit sigChangeActivePaintop(hoveredPreset());
}
if (pathColor.contains(point)) {
int pos = calculateIndex(point, m_resourceManager->recentColorsTotal());
if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) {
emit sigUpdateRecentColor(pos);
}
}
}
}
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);
+ point.setX(point.x() - m_popupPaletteSize / 2);
+ point.setY(point.y() - m_popupPaletteSize / 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))) {
+ if (createPathFromPresetIndex(pos).contains(point + QPointF(-m_popupPaletteSize / 2, -m_popupPaletteSize / 2))) {
return true;
}
return false;
}
KisPopupPalette::~KisPopupPalette()
{
}
-QPainterPath KisPopupPalette::pathFromPresetIndex(int index)
+QPainterPath KisPopupPalette::createPathFromPresetIndex(int index)
{
- qreal angleLength = 360.0 / numSlots() / 180 * M_PI;
- qreal angle = index * angleLength;
- qreal r = colorOuterRadius * sin(angleLength/2) / ( 1 - sin(angleLength/2));
+ qreal angleSlice = 360.0 / numSlots() ; // how many degrees each slice will get
+
+ // the starting angle of the slice we need to draw. the negative sign makes us go clockwise.
+ // adding 90 degrees makes us start at the top. otherwise we would start at the right
+ qreal startingAngle = -(index * angleSlice) + 90;
+
+ // the radius will get smaller as the amount of presets shown increases. 10 slots == 41
+ qreal presetRadius = m_colorHistoryOuterRadius * qSin(qDegreesToRadians(angleSlice/2)) / (1-qSin(qDegreesToRadians(angleSlice/2)));
+
QPainterPath path;
- path.addEllipse((colorOuterRadius+r) * cos(angle)-r, -(colorOuterRadius+r) * sin(angle)-r, 2*r, 2*r);
- path.closeSubpath();
+ float pathX = (m_colorHistoryOuterRadius + presetRadius) * qCos(qDegreesToRadians(startingAngle)) - presetRadius;
+ float pathY = -(m_colorHistoryOuterRadius + presetRadius) * qSin(qDegreesToRadians(startingAngle)) - presetRadius;
+ float pathDiameter = 2 * presetRadius; // distance is used to calculate the X/Y in addition to the preset circle size
+ path.addEllipse(pathX, pathY, pathDiameter, pathDiameter);
+
return path;
}
int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/)
{
for(int i = 0; i < numSlots(); i++)
{
- QPointF adujustedPoint = point - QPointF(width()/2, height()/2);
- if(pathFromPresetIndex(i).contains(adujustedPoint))
+ QPointF adujustedPoint = point - QPointF(m_popupPaletteSize/2, m_popupPaletteSize/2);
+ if(createPathFromPresetIndex(i).contains(adujustedPoint))
{
return i;
}
}
return -1;
}
int KisPopupPalette::numSlots()
{
KisConfig config;
return qMax(config.favoritePresets(), 10);
}
diff --git a/libs/ui/kis_popup_palette.h b/libs/ui/kis_popup_palette.h
index 741d610dc7..22b490348c 100644
--- a/libs/ui/kis_popup_palette.h
+++ b/libs/ui/kis_popup_palette.h
@@ -1,140 +1,176 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman <shicmap@gmail.com>
+ Copyright 2016 Scott Petrovic <scottpetrovic@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_POPUP_PALETTE_H
#define KIS_POPUP_PALETTE_H
#include <kis_types.h>
#include <QWidget>
#include <QQueue>
+#include <QPushButton>
+#include <QSlider>
#include <KoColorDisplayRendererInterface.h>
+#include "kis_action_manager.h"
+#include "KisViewManager.h"
+#include "kactioncollection.h"
+#include "kis_coordinates_converter.h"
+#include "kis_tool_button.h"
+#include "kis_highlighted_button.h"
class KisFavoriteResourceManager;
class QWidget;
class KoColor;
class KoTriangleColorSelector;
class KisSignalCompressor;
class KisBrushHud;
class KisRoundHudButton;
class KisCanvasResourceProvider;
class KisVisualColorSelector;
class KisPopupPalette : public QWidget
{
Q_OBJECT
Q_PROPERTY(int hoveredPreset READ hoveredPreset WRITE setHoveredPreset)
Q_PROPERTY(int hoveredColor READ hoveredColor WRITE setHoveredColor)
Q_PROPERTY(int selectedColor READ selectedColor WRITE setSelectedColor)
public:
- KisPopupPalette(KisFavoriteResourceManager*, const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent = 0);
+ KisPopupPalette(KisViewManager*, KisCoordinatesConverter* ,KisFavoriteResourceManager*, const KoColorDisplayRendererInterface *displayRenderer,
+ KisCanvasResourceProvider *provider, QWidget *parent = 0);
~KisPopupPalette();
QSize sizeHint() const;
void showPopupPalette(const QPoint&);
void showPopupPalette(bool b);
//functions to set up selectedBrush
void setSelectedBrush(int x);
int selectedBrush() const;
//functions to set up selectedColor
void setSelectedColor(int x);
int selectedColor() const;
void setParent(QWidget *parent);
virtual void tabletEvent(QTabletEvent * event);
protected:
void paintEvent(QPaintEvent*);
void resizeEvent(QResizeEvent*);
void mouseReleaseEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
void mousePressEvent(QMouseEvent*);
//functions to calculate index of favorite brush or recent color in array
//n is the total number of favorite brushes or recent colors
int calculateIndex(QPointF, int n);
int calculatePresetIndex(QPointF, int n);
//functions to set up hoveredBrush
void setHoveredPreset(int x);
int hoveredPreset() const;
//functions to set up hoveredColor
void setHoveredColor(int x);
int hoveredColor() const;
private:
void setVisible(bool b);
QPainterPath drawDonutPathFull(int, int, int, int);
QPainterPath drawDonutPathAngle(int, int, int);
+ QPainterPath drawRotationIndicator(qreal rotationAngle, bool canDrag);
bool isPointInPixmap(QPointF&, int pos);
- QPainterPath pathFromPresetIndex(int index);
+ QPainterPath createPathFromPresetIndex(int index);
int numSlots();
void adjustLayout(const QPoint &p);
private:
int m_hoveredPreset;
int m_hoveredColor;
int m_selectedColor;
- KisFavoriteResourceManager* m_resourceManager;
- KisVisualColorSelector* m_triangleColorSelector;
- QTimer* m_timer;
+ KisCoordinatesConverter* m_coordinatesConverter;
+ KisActionManager* m_actionManager;
+ KisFavoriteResourceManager* m_resourceManager;
+ KisVisualColorSelector* m_triangleColorSelector;
const KoColorDisplayRendererInterface *m_displayRenderer;
-
QScopedPointer<KisSignalCompressor> m_colorChangeCompressor;
+ KActionCollection* m_actionCollection;
+
+ QTimer m_timer;
+
KisBrushHud *m_brushHud;
+ float m_popupPaletteSize;
+ float m_colorHistoryInnerRadius;
+ float m_colorHistoryOuterRadius;
+
KisRoundHudButton *m_settingsButton;
KisRoundHudButton *m_brushHudButton;
QPoint m_lastCenterPoint;
+ QRect m_canvasRotationIndicatorRect;
+ QRect m_resetCanvasRotationIndicatorRect;
+ bool m_isOverCanvasRotationIndicator;
+ bool m_isRotatingCanvasIndicator;
+
+ KisHighlightedToolButton* mirrorMode;
+ KisHighlightedToolButton* canvasOnlyButton;
+ QPushButton* zoomToOneHundredPercentButton;
+ QSlider* zoomCanvasSlider;
Q_SIGNALS:
void sigChangeActivePaintop(int);
void sigUpdateRecentColor(int);
void sigChangefGColor(const KoColor&);
+ void sigUpdateCanvas();
+ void zoomLevelChanged(int);
// These are 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
void sigEnableChangeFGColor(bool);
void sigTriggerTimer();
private Q_SLOTS:
void slotExternalFgColorChanged(const KoColor &color);
void slotEmitColorChanged();
void slotSetSelectedColor(int x) { setSelectedColor(x); update(); }
void slotTriggerTimer();
void slotEnableChangeFGColor();
void slotUpdate() { update(); }
void slotHide() { showPopupPalette(false); }
void slotShowTagsPopup();
void showHudWidget(bool visible);
+ void slotmirroModeClicked();
+ void slotCanvasonlyModeClicked();
+ void slotZoomToOneHundredPercentClicked();
+ void slotZoomSliderChanged(int zoom);
+
+
};
#endif // KIS_POPUP_PALETTE_H
diff --git a/libs/ui/kis_splash_screen.cpp b/libs/ui/kis_splash_screen.cpp
index 6514b54c53..a194d44dc5 100644
--- a/libs/ui/kis_splash_screen.cpp
+++ b/libs/ui/kis_splash_screen.cpp
@@ -1,201 +1,201 @@
/*
* 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 <kis_debug.h>
#include <QFile>
#include <KisPart.h>
#include <KisApplication.h>
#include <kis_icon.h>
#include <klocalizedstring.h>
#include <kconfig.h>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <QIcon>
KisSplashScreen::KisSplashScreen(const QString &version, const QPixmap &pixmap, bool themed, QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, Qt::SplashScreen | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | f),
m_themed(themed)
{
setupUi(this);
setWindowIcon(KisIconUtils::loadIcon("calligrakrita"));
// Maintain the aspect ratio on high DPI screens when scaling
lblSplash->setPixmap(pixmap);
QString color = colorString();
lblVersion->setText(i18n("Version: %1", version));
lblVersion->setStyleSheet("color:" + color);
bnClose->hide();
connect(bnClose, SIGNAL(clicked()), this, SLOT(close()));
chkShowAtStartup->hide();
connect(chkShowAtStartup, SIGNAL(toggled(bool)), this, SLOT(toggleShowAtStartup(bool)));
KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen");
bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false);
chkShowAtStartup->setChecked(hideSplash);
connect(lblRecent, SIGNAL(linkActivated(QString)), SLOT(linkClicked(QString)));
connect(&m_timer, SIGNAL(timeout()), SLOT(raise()));
// hide these labels by default
lblLinks->setVisible(false);
lblRecent->setVisible(false);
line->setVisible(false);
m_timer.setSingleShot(true);
m_timer.start(10);
}
void KisSplashScreen::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
updateText();
}
void KisSplashScreen::updateText()
{
QString color = colorString();
KConfigGroup cfg2( KSharedConfig::openConfig(), "RecentFiles");
int i = 1;
QString recent = i18n("<html>"
"<head/>"
"<body>"
- "<p align=\"center\"><b><span style=\" color:%1;\">Recent Files</span></b></p>", color);
+ "<p><b><span style=\" color:%1;\">Recent Files</span></b></p>", color);
QString path;
QStringList recentfiles;
QFontMetrics metrics(lblRecent->font());
do {
path = cfg2.readPathEntry(QString("File%1").arg(i), QString());
if (!path.isEmpty()) {
QString name = cfg2.readPathEntry(QString("Name%1").arg(i), QString());
QUrl url(path);
if (name.isEmpty()) {
name = url.fileName();
}
name = metrics.elidedText(name, Qt::ElideMiddle, lblRecent->width());
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);
}
void KisSplashScreen::displayLinks() {
QString color = colorString();
lblLinks->setTextFormat(Qt::RichText);
lblLinks->setText(i18n("<html>"
"<head/>"
"<body>"
- "<p align=\"center\"><span style=\" color:%1;\"><b>Links</b></span></p>"
+ "<p><span style=\" color:%1;\"><b>Links</b></span></p>"
"<p><a href=\"https://krita.org/support-us/\"><span style=\" text-decoration: underline; color:%1;\">Support Krita</span></a></p>"
"<p><a href=\"https://docs.krita.org/Category:Getting_Started\"><span style=\" text-decoration: underline; color:%1;\">Getting Started</span></a></p>"
"<p><a href=\"https://docs.krita.org/\"><span style=\" text-decoration: underline; color:%1;\">Manual</span></a></p>"
"<p><a href=\"https://krita.org/\"><span style=\" text-decoration: underline; color:%1;\">Krita Website</span></a></p>"
"<p><a href=\"https://forum.kde.org/viewforum.php?f=136\"><span style=\" text-decoration: underline; color:%1;\">User Community</span></a></p>"
"<p><a href=\"https://quickgit.kde.org/?p=krita.git\"><span style=\" text-decoration: underline; color:%1;\">Source Code</span></a></p>"
"<p><a href=\"https://store.steampowered.com/app/280680/\"><span style=\" text-decoration: underline; color:%1;\">Krita on Steam</span></a></p>"
"</body>"
"</html>", color));
lblLinks->setVisible(true);
updateText();
}
void KisSplashScreen::displayRecentFiles() {
lblRecent->setVisible(true);
line->setVisible(true);
}
QString KisSplashScreen::colorString() const
{
QString color = "#FFFFFF";
if (m_themed && qApp->palette().background().color().value() > 100) {
color = "#000000";
}
return color;
}
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();
}
m_timer.setSingleShot(true);
m_timer.start(1);
QWidget::show();
}
void KisSplashScreen::toggleShowAtStartup(bool toggle)
{
KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen");
cfg.writeEntry("HideSplashAfterStartup", toggle);
}
void KisSplashScreen::linkClicked(const QString &link)
{
KisPart::instance()->openExistingFile(QUrl::fromLocalFile(link));
if (isTopLevel()) {
close();
}
}
diff --git a/libs/ui/opengl/kis_opengl.cpp b/libs/ui/opengl/kis_opengl.cpp
index 696ed1923d..a76d2c3562 100644
--- a/libs/ui/opengl/kis_opengl.cpp
+++ b/libs/ui/opengl/kis_opengl.cpp
@@ -1,206 +1,208 @@
/*
* Copyright (c) 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.h"
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QApplication>
#include <QDesktopWidget>
#include <QPixmapCache>
#include <QDir>
#include <QFile>
#include <QDesktopServices>
#include <QMessageBox>
#include <QWindow>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kis_config.h>
namespace
{
bool initialized = false;
bool NeedsFenceWorkaround = false;
bool NeedsPixmapCacheWorkaround = false;
int glMajorVersion = 0;
int glMinorVersion = 0;
bool supportsDeprecatedFunctions = false;
QString Renderer;
}
void KisOpenGL::initialize()
{
if (initialized) return;
setDefaultFormat();
// we need a QSurface active to get our GL functions from the context
QWindow surface;
surface.setSurfaceType( QSurface::OpenGLSurface );
surface.create();
QOpenGLContext context;
context.create();
+ if (!context.isValid()) return;
+
context.makeCurrent( &surface );
QOpenGLFunctions *funcs = context.functions();
funcs->initializeOpenGLFunctions();
qDebug() << "OpenGL Info";
qDebug() << " Vendor: " << reinterpret_cast<const char *>(funcs->glGetString(GL_VENDOR));
qDebug() << " Renderer: " << reinterpret_cast<const char *>(funcs->glGetString(GL_RENDERER));
qDebug() << " Version: " << reinterpret_cast<const char *>(funcs->glGetString(GL_VERSION));
qDebug() << " Shading language: " << reinterpret_cast<const char *>(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION));
qDebug() << " Requested format: " << QSurfaceFormat::defaultFormat();
qDebug() << " Current format: " << context.format();
glMajorVersion = context.format().majorVersion();
glMinorVersion = context.format().minorVersion();
supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions);
qDebug() << " Version:" << glMajorVersion << "." << glMinorVersion;
qDebug() << " Supports deprecated functions" << supportsDeprecatedFunctions;
initialized = true;
}
void KisOpenGL::initializeContext(QOpenGLContext *ctx)
{
initialize();
dbgUI << "OpenGL: Opening new context";
// Double check we were given the version we requested
QSurfaceFormat format = ctx->format();
QOpenGLFunctions *f = ctx->functions();
f->initializeOpenGLFunctions();
#ifndef GL_RENDERER
# define GL_RENDERER 0x1F01
#endif
Renderer = QString((const char*)f->glGetString(GL_RENDERER));
QFile log(QDesktopServices::storageLocation(QDesktopServices::TempLocation) + "/krita-opengl.txt");
log.open(QFile::WriteOnly);
QString vendor((const char*)f->glGetString(GL_VENDOR));
log.write(vendor.toLatin1());
log.write(", ");
log.write(Renderer.toLatin1());
log.write(", ");
QString version((const char*)f->glGetString(GL_VERSION));
log.write(version.toLatin1());
log.close();
// Check if we have a bugged driver that needs fence workaround
bool isOnX11 = false;
#ifdef HAVE_X11
isOnX11 = true;
#endif
KisConfig cfg;
if ((isOnX11 && Renderer.startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) {
NeedsFenceWorkaround = true;
}
/**
* NVidia + Qt's openGL don't play well together and one cannot
* draw a pixmap on a widget more than once in one rendering cycle.
*
* It can be workarounded by drawing strictly via QPixmapCache and
* only when the pixmap size in bigger than doubled size of the
* display framebuffer. That is for 8-bit HD display, you should have
* a cache bigger than 16 MiB. Don't ask me why. (DK)
*
* See bug: https://bugs.kde.org/show_bug.cgi?id=361709
*
* TODO: check if this workaround is still needed after merging
* Qt5+openGL3 branch.
*/
if (vendor.toUpper().contains("NVIDIA")) {
NeedsPixmapCacheWorkaround = true;
const QRect screenSize = QApplication::desktop()->screenGeometry();
const int minCacheSize = 20 * 1024;
const int cacheSize = 2048 + 2 * 4 * screenSize.width() * screenSize.height() / 1024; //KiB
QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize));
}
}
// XXX Temporary function to allow LoD on OpenGL3 without triggering
// all of the other 3.2 functionality, can be removed once we move to Qt5.7
bool KisOpenGL::supportsLoD()
{
initialize();
return (glMajorVersion * 100 + glMinorVersion) >= 300;
}
bool KisOpenGL::hasOpenGL3()
{
initialize();
return (glMajorVersion * 100 + glMinorVersion) >= 302;
}
bool KisOpenGL::supportsFenceSync()
{
initialize();
return glMajorVersion >= 3;
}
bool KisOpenGL::needsFenceWorkaround()
{
initialize();
return NeedsFenceWorkaround;
}
bool KisOpenGL::needsPixmapCacheWorkaround()
{
initialize();
return NeedsPixmapCacheWorkaround;
}
void KisOpenGL::setDefaultFormat()
{
QSurfaceFormat format;
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
#else
// XXX This can be removed once we move to Qt5.7
format.setVersion(3, 0);
format.setProfile(QSurfaceFormat::CompatibilityProfile);
format.setOptions(QSurfaceFormat::DeprecatedFunctions);
#endif
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
format.setSwapInterval(0); // Disable vertical refresh syncing
QSurfaceFormat::setDefaultFormat(format);
}
bool KisOpenGL::hasOpenGL()
{
return ((glMajorVersion * 100 + glMinorVersion) >= 201);
//return (glMajorVersion >= 3 && supportsDeprecatedFunctions);
}
diff --git a/libs/ui/opengl/kis_opengl_canvas2.cpp b/libs/ui/opengl/kis_opengl_canvas2.cpp
index 6319f8757a..4ddb1e3240 100644
--- a/libs/ui/opengl/kis_opengl_canvas2.cpp
+++ b/libs/ui/opengl/kis_opengl_canvas2.cpp
@@ -1,793 +1,793 @@
/* 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"
#include "opengl/kis_opengl_canvas2_p.h"
#include "opengl/kis_opengl_shader_loader.h"
#include "opengl/kis_opengl_canvas_debugger.h"
#include "canvas/kis_canvas2.h"
#include "canvas/kis_coordinates_converter.h"
#include "canvas/kis_display_filter.h"
#include "canvas/kis_display_color_converter.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_debug.h"
#include <QPainter>
#include <QPainterPath>
#include <QPointF>
#include <QMatrix>
#include <QTransform>
#include <QThread>
#include <QFile>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QMessageBox>
#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;
typedef void (*kis_glLogicOp)(int);
static kis_glLogicOp ptr_glLogicOp = 0;
struct KisOpenGLCanvas2::Private
{
public:
~Private() {
delete displayShader;
delete checkerShader;
delete cursorShader;
Sync::deleteSync(glSyncObject);
}
bool canvasInitialized{false};
KisOpenGLImageTexturesSP openGLImageTextures;
KisOpenGLShaderLoader shaderLoader;
KisShaderProgram *displayShader{0};
KisShaderProgram *checkerShader{0};
KisShaderProgram *cursorShader{0};
GLfloat checkSizeScale;
bool scrollCheckers;
QSharedPointer<KisDisplayFilter> displayFilter;
KisOpenGL::FilterMode filterMode;
bool proofingConfigIsUpdated=false;
GLsync glSyncObject{0};
bool wrapAroundMode{false};
// Stores a quad for drawing the canvas
QOpenGLVertexArrayObject quadVAO;
QOpenGLBuffer quadBuffers[2];
// Stores data for drawing tool outlines
QOpenGLVertexArrayObject outlineVAO;
QOpenGLBuffer lineBuffer;
QVector3D vertices[6];
QVector2D texCoords[6];
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())
{
KisConfig cfg;
cfg.writeEntry("canvasState", "OPENGL_STARTED");
d->openGLImageTextures =
KisOpenGLImageTextures::getImageTextures(image,
colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
setAcceptDrops(true);
setAutoFillBackground(false);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_NoSystemBackground, true);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
setAttribute(Qt::WA_AcceptTouchEvents, false);
#else
setAttribute(Qt::WA_AcceptTouchEvents, true);
#endif
setAttribute(Qt::WA_InputMethodEnabled, true);
setAttribute(Qt::WA_DontCreateNativeAncestors, true);
setDisplayFilterImpl(colorConverter->displayFilter(), true);
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
slotConfigChanged();
cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
}
KisOpenGLCanvas2::~KisOpenGLCanvas2()
{
delete d;
}
bool KisOpenGLCanvas2::needsFpsDebugging() const
{
return KisOpenglCanvasDebugger::instance()->showFpsOnCanvas();
}
void KisOpenGLCanvas2::setDisplayFilter(QSharedPointer<KisDisplayFilter> displayFilter)
{
setDisplayFilterImpl(displayFilter, false);
}
void KisOpenGLCanvas2::setDisplayFilterImpl(QSharedPointer<KisDisplayFilter> displayFilter, bool initializing)
{
bool needsInternalColorManagement =
!displayFilter || displayFilter->useInternalColorManagement();
bool needsFullRefresh = d->openGLImageTextures->setInternalColorManagementActive(needsInternalColorManagement);
d->displayFilter = displayFilter;
if (d->canvasInitialized) {
d->canvasInitialized = false;
delete d->displayShader;
bool useHiQualityFiltering = d->filterMode == KisOpenGL::HighQualityFiltering;
try {
d->displayShader = d->shaderLoader.loadDisplayShader(d->displayFilter, useHiQualityFiltering);
} catch (const ShaderLoaderException &e) {
reportFailedShaderCompilation(e.what());
}
d->canvasInitialized = true;
}
if (!initializing && needsFullRefresh) {
canvas()->startUpdateInPatches(canvas()->image()->bounds());
}
else if (!initializing) {
canvas()->updateCanvas();
}
}
void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value)
{
d->wrapAroundMode = value;
update();
}
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::initializeGL()
{
KisOpenGL::initializeContext(context());
initializeOpenGLFunctions();
KisConfig cfg;
d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration());
d->openGLImageTextures->initGL(context()->functions());
d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize()));
initializeShaders();
// If we support OpenGL 3.2, then prepare our VAOs and VBOs for drawing
if (KisOpenGL::hasOpenGL3()) {
d->quadVAO.create();
d->quadVAO.bind();
glEnableVertexAttribArray(PROGRAM_VERTEX_ATTRIBUTE);
glEnableVertexAttribArray(PROGRAM_TEXCOORD_ATTRIBUTE);
// Create the vertex buffer object, it has 6 vertices with 3 components
d->quadBuffers[0].create();
d->quadBuffers[0].setUsagePattern(QOpenGLBuffer::StaticDraw);
d->quadBuffers[0].bind();
d->quadBuffers[0].allocate(d->vertices, 6 * 3 * sizeof(float));
glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, 0);
// Create the texture buffer object, it has 6 texture coordinates with 2 components
d->quadBuffers[1].create();
d->quadBuffers[1].setUsagePattern(QOpenGLBuffer::StaticDraw);
d->quadBuffers[1].bind();
d->quadBuffers[1].allocate(d->texCoords, 6 * 2 * sizeof(float));
glVertexAttribPointer(PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, 0);
// Create the outline buffer, this buffer will store the outlines of
// tools and will frequently change data
d->outlineVAO.create();
d->outlineVAO.bind();
glEnableVertexAttribArray(PROGRAM_VERTEX_ATTRIBUTE);
// The outline buffer has a StreamDraw usage pattern, because it changes constantly
d->lineBuffer.create();
d->lineBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
d->lineBuffer.bind();
glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
ptr_glLogicOp = (kis_glLogicOp)(context()->getProcAddress("glLogicOp"));
Sync::init(context());
d->canvasInitialized = true;
}
/**
* Loads all shaders and reports compilation problems
*/
void KisOpenGLCanvas2::initializeShaders()
{
bool useHiQualityFiltering = d->filterMode == KisOpenGL::HighQualityFiltering;
if (!d->canvasInitialized) {
delete d->displayShader;
delete d->checkerShader;
delete d->cursorShader;
try {
d->displayShader = d->shaderLoader.loadDisplayShader(d->displayFilter, useHiQualityFiltering);
d->checkerShader = d->shaderLoader.loadCheckerShader();
d->cursorShader = d->shaderLoader.loadCursorShader();
} catch (const ShaderLoaderException &e) {
reportFailedShaderCompilation(e.what());
}
}
}
/**
* Displays a message box telling the user that
* shader compilation failed and turns off OpenGL.
*/
void KisOpenGLCanvas2::reportFailedShaderCompilation(const QString &context)
{
KisConfig cfg;
if (cfg.useVerboseOpenGLDebugOutput()) {
dbgUI << "GL-log:" << context;
}
qDebug() << "Shader Compilation Failure: " << context;
QMessageBox::critical(this, i18nc("@title:window", "Krita"),
QString(i18n("Krita could not initialize the OpenGL canvas:\n\n%1\n\n Krita will disable OpenGL and close now.")).arg(context),
QMessageBox::Close);
cfg.setUseOpenGL(false);
cfg.setCanvasState("OPENGL_FAILED");
}
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");
}
KisOpenglCanvasDebugger::instance()->nofityPaintRequested();
renderCanvasGL();
if (d->glSyncObject) {
Sync::deleteSync(d->glSyncObject);
}
d->glSyncObject = Sync::getSync();
QPainter gc(this);
renderDecorations(&gc);
gc.end();
if (!OPENGL_SUCCESS) {
KisConfig cfg;
cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
OPENGL_SUCCESS = true;
}
}
void KisOpenGLCanvas2::paintToolOutline(const QPainterPath &path)
{
d->cursorShader->bind();
// setup the mvp transformation
KisCoordinatesConverter *converter = coordinatesConverter();
QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);
// Set view/projection matrices
QMatrix4x4 modelMatrix(converter->flakeToWidgetTransform());
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
d->cursorShader->setUniformValue(d->cursorShader->location(Uniform::ModelViewProjection), modelMatrix);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
// XXX: glLogicOp not in ES 2.0 -- it would be better to use another method.
// It is defined in 3.1 core profile onward.
// Actually, https://www.opengl.org/sdk/docs/man/html/glLogicOp.xhtml says it's in 2.0 onwards,
// only not in ES, but we don't care about ES, so we could use the function directly.
glEnable(GL_COLOR_LOGIC_OP);
if (ptr_glLogicOp) {
ptr_glLogicOp(GL_XOR);
}
// Paint the tool outline
if (KisOpenGL::hasOpenGL3()) {
d->outlineVAO.bind();
d->lineBuffer.bind();
}
// Convert every disjointed subpath to a polygon and draw that polygon
QList<QPolygonF> subPathPolygons = path.toSubpathPolygons();
for (int i = 0; i < subPathPolygons.size(); i++) {
const QPolygonF& polygon = subPathPolygons.at(i);
QVector<QVector3D> vertices;
vertices.resize(polygon.count());
for (int j = 0; j < polygon.count(); j++) {
QPointF p = polygon.at(j);
vertices[j].setX(p.x());
vertices[j].setY(p.y());
}
if (KisOpenGL::hasOpenGL3()) {
d->lineBuffer.allocate(vertices.constData(), 3 * vertices.size() * sizeof(float));
}
else {
d->cursorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
d->cursorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, vertices.constData());
}
glDrawArrays(GL_LINE_STRIP, 0, vertices.size());
}
if (KisOpenGL::hasOpenGL3()) {
d->lineBuffer.release();
d->outlineVAO.release();
}
glDisable(GL_COLOR_LOGIC_OP);
d->cursorShader->release();
}
bool KisOpenGLCanvas2::isBusy() const
{
const bool isBusyStatus = Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled;
KisOpenglCanvasDebugger::instance()->nofitySyncStatus(isBusyStatus);
return isBusyStatus;
}
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, d->scrollCheckers);
textureTransform *= QTransform::fromScale(d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE);
if (!d->checkerShader->bind()) {
qWarning() << "Could not bind checker shader";
return;
}
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->checkerShader->location(Uniform::ModelViewProjection), modelMatrix);
QMatrix4x4 textureMatrix(textureTransform);
d->checkerShader->setUniformValue(d->checkerShader->location(Uniform::TextureMatrix), textureMatrix);
//Setup the geometry for rendering
if (KisOpenGL::hasOpenGL3()) {
rectToVertices(d->vertices, modelRect);
d->quadBuffers[0].bind();
d->quadBuffers[0].write(0, d->vertices, 3 * 6 * sizeof(float));
rectToTexCoords(d->texCoords, textureRect);
d->quadBuffers[1].bind();
d->quadBuffers[1].write(0, d->texCoords, 2 * 6 * sizeof(float));
}
else {
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();
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
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->displayShader->location(Uniform::ModelViewProjection), modelMatrix);
QMatrix4x4 textureMatrix;
textureMatrix.setToIdentity();
d->displayShader->setUniformValue(d->displayShader->location(Uniform::TextureMatrix), 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->displayShader->location(Uniform::ViewportScale), (GLfloat) scaleX);
d->displayShader->setUniformValue(d->displayShader->location(Uniform::TexelSize), (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) {
warnUI << "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
if (KisOpenGL::hasOpenGL3()) {
rectToVertices(d->vertices, modelRect);
d->quadBuffers[0].bind();
d->quadBuffers[0].write(0, d->vertices, 3 * 6 * sizeof(float));
rectToTexCoords(d->texCoords, textureRect);
d->quadBuffers[1].bind();
d->quadBuffers[1].write(0, d->texCoords, 2 * 6 * sizeof(float));
}
else {
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->displayShader->location(Uniform::Texture1), 1);
}
int currentLodPlane = tile->currentLodPlane();
if (d->displayShader->location(Uniform::FixedLodLevel) >= 0) {
d->displayShader->setUniformValue(d->displayShader->location(Uniform::FixedLodLevel),
(GLfloat) currentLodPlane);
}
glActiveTexture(GL_TEXTURE0);
tile->bindToActiveTexture();
if (currentLodPlane > 0) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
} else 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 KisOpenGL::NearestFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
break;
case KisOpenGL::BilinearFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
break;
case KisOpenGL::TrilinearFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case KisOpenGL::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();
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void KisOpenGLCanvas2::slotConfigChanged()
{
KisConfig cfg;
d->checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast<GLfloat>(cfg.checkSize());
d->scrollCheckers = cfg.scrollCheckers();
d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize()));
d->openGLImageTextures->updateConfig(cfg.useOpenGLTextureBuffer(), cfg.numMipmapLevels());
d->filterMode = (KisOpenGL::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);
if (d->displayFilter) {
d->displayFilter->updateShader();
}
if (KisOpenGL::hasOpenGL3()) {
d->quadVAO.bind();
}
drawCheckers();
drawImage();
if (KisOpenGL::hasOpenGL3()) {
d->quadVAO.release();
}
}
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(const QBitArray &channelFlags)
{
d->openGLImageTextures->setChannelFlags(channelFlags);
}
void KisOpenGLCanvas2::finishResizingImage(qint32 w, qint32 h)
{
if (d->canvasInitialized) {
d->openGLImageTextures->slotImageSizeChanged(w, h);
}
}
KisUpdateInfoSP KisOpenGLCanvas2::startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags)
{
d->openGLImageTextures->setChannelFlags(channelFlags);
if (canvas()->proofingConfigUpdated()) {
d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration());
canvas()->setProofingConfigUpdated(false);
}
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);
}
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
/**
* There is a bug on OSX: if we issue frame redraw before the tiles finished
* uploading, the tiles will become corrupted. Depending on the GPU/driver
* version either the tile itself, or its mipmaps will become totally
* transparent.
*/
glFinish();
#endif
return QRect(); // FIXME: Implement dirty rect for OpenGL
}
bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next)
{
return focusNextPrevChild(next);
}
KisOpenGLImageTexturesSP KisOpenGLCanvas2::openGLImageTextures() const
{
return d->openGLImageTextures;
}
diff --git a/libs/ui/opengl/kis_opengl_canvas2_p.h b/libs/ui/opengl/kis_opengl_canvas2_p.h
index 395edaabe7..ba4d83fd9e 100644
--- a/libs/ui/opengl/kis_opengl_canvas2_p.h
+++ b/libs/ui/opengl/kis_opengl_canvas2_p.h
@@ -1,122 +1,122 @@
/*
* 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>
/**
* 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
*/
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) {
#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 || defined Q_OS_MAC
+#elif defined Q_OS_LINUX || defined Q_OS_OSX
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) {
warnUI << "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 // KIS_OPENGL_CANVAS_2_P_H
diff --git a/libs/ui/opengl/kis_opengl_shader_loader.cpp b/libs/ui/opengl/kis_opengl_shader_loader.cpp
index b382c77136..a0294c0892 100644
--- a/libs/ui/opengl/kis_opengl_shader_loader.cpp
+++ b/libs/ui/opengl/kis_opengl_shader_loader.cpp
@@ -1,189 +1,189 @@
/* This file is part of the KDE project
* Copyright (C) Julian Thijssen <julianthijssen@gmail.com>, (C) 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_opengl_shader_loader.h"
#include "opengl/kis_opengl.h"
#include "kis_config.h"
#include <QFile>
#include <QMessageBox>
#include <KLocalizedString>
#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
// Mapping of uniforms to uniform names
std::map<Uniform, const char *> KisShaderProgram::names = {
{ModelViewProjection, "modelViewProjection"},
{TextureMatrix, "textureMatrix"},
{ViewportScale, "viewportScale"},
{TexelSize, "texelSize"},
{Texture0, "texture0"},
{Texture1, "texture1"},
{FixedLodLevel, "fixedLodLevel"}
};
/**
* Generic shader loading function that will compile a shader program given
* a vertex shader and fragment shader resource path. Extra code can be prepended
* to each shader respectively using the header parameters.
*
* @param vertPath Resource path to a vertex shader
* @param fragPath Resource path to a fragment shader
* @param vertHeader Extra code which will be prepended to the vertex shader
* @param fragHeader Extra code which will be prepended to the fragment shader
*/
KisShaderProgram *KisOpenGLShaderLoader::loadShader(QString vertPath, QString fragPath,
QByteArray vertHeader, QByteArray fragHeader)
{
bool result;
KisShaderProgram *shader = new KisShaderProgram();
// Load vertex shader
QByteArray vertSource;
// XXX Check can be removed and set to the MAC version after we move to Qt5.7
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
vertSource.append(KisOpenGL::hasOpenGL3() ? "#version 150 core\n" : "#version 120\n");
#else
vertSource.append(KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\n");
#endif
vertSource.append(vertHeader);
QFile vertexShaderFile(":/" + vertPath);
vertexShaderFile.open(QIODevice::ReadOnly);
vertSource.append(vertexShaderFile.readAll());
result = shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertSource);
if (!result)
throw ShaderLoaderException(QString("%1: %2 - Cause: %3").arg("Failed to add vertex shader source from file", vertPath, shader->log()));
// Load fragment shader
QByteArray fragSource;
// XXX Check can be removed and set to the MAC version after we move to Qt5.7
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
fragSource.append(KisOpenGL::hasOpenGL3() ? "#version 150 core\n" : "#version 120\n");
#else
fragSource.append(KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\n");
#endif
fragSource.append(fragHeader);
QFile fragmentShaderFile(":/" + fragPath);
fragmentShaderFile.open(QIODevice::ReadOnly);
fragSource.append(fragmentShaderFile.readAll());
result = shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragSource);
if (!result)
throw ShaderLoaderException(QString("%1: %2 - Cause: %3").arg("Failed to add fragment shader source from file", fragPath, shader->log()));
// Bind attributes
shader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE);
shader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE);
// Link
result = shader->link();
if (!result)
throw ShaderLoaderException(QString("Failed to link shader: ").append(vertPath));
Q_ASSERT(shader->isLinked());
return shader;
}
/**
* Specific display shader loading function. It adds the appropriate extra code
* to the fragment shader depending on what is available on the target machine.
* Additionally, it picks the appropriate shader files depending on the availability
* of OpenGL3.
*/
KisShaderProgram *KisOpenGLShaderLoader::loadDisplayShader(QSharedPointer<KisDisplayFilter> displayFilter, bool useHiQualityFiltering)
{
QByteArray fragHeader;
if (KisOpenGL::supportsLoD()) {
fragHeader.append("#define DIRECT_LOD_FETCH\n");
if (useHiQualityFiltering) {
fragHeader.append("#define HIGHQ_SCALING\n");
}
}
// If we have an OCIO display filter and it contains a function we add
// it to our shader header which will sit on top of the fragment code.
bool haveDisplayFilter = displayFilter && !displayFilter->program().isEmpty();
if (haveDisplayFilter) {
fragHeader.append("#define USE_OCIO\n");
fragHeader.append(displayFilter->program().toLatin1());
}
QString vertPath, fragPath;
// Select appropriate shader files
if (KisOpenGL::supportsLoD()) {
vertPath = "matrix_transform.vert";
fragPath = "highq_downscale.frag";
} else {
vertPath = "matrix_transform_legacy.vert";
fragPath = "simple_texture_legacy.frag";
}
KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), fragHeader);
return shader;
}
/**
* Specific checker shader loading function. It picks the appropriate shader
* files depending on the availability of OpenGL3 on the target machine.
*/
KisShaderProgram *KisOpenGLShaderLoader::loadCheckerShader()
{
QString vertPath, fragPath;
// Select appropriate shader files
if (KisOpenGL::supportsLoD()) {
vertPath = "matrix_transform.vert";
fragPath = "simple_texture.frag";
} else {
vertPath = "matrix_transform_legacy.vert";
fragPath = "simple_texture_legacy.frag";
}
KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), QByteArray());
return shader;
}
/**
* Specific cursor shader loading function. It picks the appropriate shader
* files depending on the availability of OpenGL3 on the target machine.
*/
KisShaderProgram *KisOpenGLShaderLoader::loadCursorShader()
{
QString vertPath, fragPath;
// Select appropriate shader files
if (KisOpenGL::supportsLoD()) {
vertPath = "cursor.vert";
fragPath = "cursor.frag";
} else {
vertPath = "cursor_legacy.vert";
fragPath = "cursor_legacy.frag";
}
KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), QByteArray());
return shader;
}
diff --git a/libs/ui/tests/CMakeLists.txt b/libs/ui/tests/CMakeLists.txt
index 9755ebe95b..62b3fb2edb 100644
--- a/libs/ui/tests/CMakeLists.txt
+++ b/libs/ui/tests/CMakeLists.txt
@@ -1,186 +1,175 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
#add_subdirectory(scratchpad)
include_directories(${CMAKE_SOURCE_DIR}/libs/image/metadata
${CMAKE_SOURCE_DIR}/sdk/tests )
include(ECMAddTests)
macro_add_unittest_definitions()
ecm_add_tests(
kis_image_view_converter_test.cpp
squeezedcombobox_test.cpp
- kis_kra_savexml_visitor_test.cpp
kis_shape_selection_test.cpp
kis_recorded_action_editor_test.cpp
kis_doc2_test.cpp
kis_coordinates_converter_test.cpp
kis_grid_config_test.cpp
kis_stabilized_events_sampler_test.cpp
kis_derived_resources_test.cpp
kis_brush_hud_properties_config_test.cpp
NAME_PREFIX "krita-ui-"
LINK_LIBRARIES kritaui Qt5::Test
)
ecm_add_tests(
kis_file_layer_test.cpp
kis_multinode_property_test.cpp
NAME_PREFIX "krita-ui-"
LINK_LIBRARIES kritaui kritaimage Qt5::Test
)
ecm_add_test( kis_selection_decoration_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp
TEST_NAME krita-ui-KisSelectionDecorationTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
ecm_add_test( kis_node_dummies_graph_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME krita-ui-KisNodeDummiesGraphTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
ecm_add_test( kis_node_shapes_graph_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME krita-ui-KisNodeShapesGraphTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
ecm_add_test( kis_model_index_converter_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME krita-ui-KisModelIndexConverterTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
ecm_add_test( kis_categorized_list_model_test.cpp modeltest.cpp
TEST_NAME krita-ui-KisCategorizedListModelTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
ecm_add_test( kis_resource_server_provider_test.cpp modeltest.cpp
TEST_NAME krita-ui-KisResourceServerProviderTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
ecm_add_test( kis_node_juggler_compressed_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME krita-image-BaseNodeTest
LINK_LIBRARIES kritaimage kritaui Qt5::Test)
set(kis_node_view_test_SRCS kis_node_view_test.cpp ../../../sdk/tests/testutil.cpp)
qt5_add_resources(kis_node_view_test_SRCS ${krita_QRCS})
ecm_add_test(${kis_node_view_test_SRCS}
TEST_NAME krita-image-kis_node_view_test
LINK_LIBRARIES kritaimage kritaui Qt5::Test)
##### Tests that currently fail and should be fixed #####
include(KritaAddBrokenUnitTest)
krita_add_broken_unit_test(
kis_node_model_test.cpp modeltest.cpp
TEST_NAME krita-ui-kis_node_model_test
LINK_LIBRARIES kritaui Qt5::Test)
krita_add_broken_unit_test(
kis_shape_controller_test.cpp kis_dummies_facade_base_test.cpp
TEST_NAME krita-ui-kis_shape_controller_test
LINK_LIBRARIES kritaimage kritaui Qt5::Test)
krita_add_broken_unit_test(
kis_prescaled_projection_test.cpp
TEST_NAME krita-ui-kis_prescaled_projection_test
LINK_LIBRARIES kritaui Qt5::Test)
-krita_add_broken_unit_test(
- kis_kra_loader_test.cpp
- TEST_NAME krita-ui-KisKraLoaderTest
- LINK_LIBRARIES kritaimage kritaui Qt5::Test)
-
-krita_add_broken_unit_test(
- kis_kra_saver_test.cpp
- TEST_NAME krita-ui-KisKraSaverTest
- LINK_LIBRARIES kritaimage kritaui Qt5::Test)
-
krita_add_broken_unit_test(
kis_exiv2_test.cpp
TEST_NAME krita-ui-KisExiv2Test
LINK_LIBRARIES kritaimage kritaui Qt5::Test)
krita_add_broken_unit_test(
kis_clipboard_test.cpp
TEST_NAME krita-ui-KisClipboardTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
freehand_stroke_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp
TEST_NAME krita-ui-FreehandStrokeTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
fill_processing_visitor_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp
TEST_NAME krita-ui-FillProcessingVisitorTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
filter_stroke_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp
TEST_NAME krita-ui-FilterStrokeTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
kis_selection_manager_test.cpp
TEST_NAME krita-ui-KisSelectionManagerTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
#set_tests_properties(krita-ui-KisSelectionManagerTest PROPERTIES TIMEOUT 300)
krita_add_broken_unit_test(
kis_node_manager_test.cpp
TEST_NAME krita-ui-KisNodeManagerTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
kis_dummies_facade_test.cpp kis_dummies_facade_base_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME krita-ui-KisDummiesFacadeTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
kis_zoom_and_pan_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME krita-ui-KisZoomAndPanTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
#set_tests_properties(krita-ui-KisZoomAndPanTest PROPERTIES TIMEOUT 300)
krita_add_broken_unit_test(
kis_action_manager_test.cpp
TEST_NAME krita-ui-KisActionManagerTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
kis_categories_mapper_test.cpp testing_categories_mapper.cpp
TEST_NAME krita-ui-KisCategoriesMapperTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
kis_asl_layer_style_serializer_test.cpp
TEST_NAME krita-ui-KisAslLayerStyleSerializerTest
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
kis_animation_exporter_test.cpp
TEST_NAME kritaui-animation_exporter_test
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
kis_animation_importer_test.cpp
TEST_NAME kritaui-animation_importer_test
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
kis_animation_frame_cache_test.cpp
TEST_NAME kritaui-animation_frame_cache_test
LINK_LIBRARIES kritaui kritaimage Qt5::Test)
krita_add_broken_unit_test(
ResourceBundleTest.cpp
TEST_NAME krita-resourcemanager-ResourceBundleTest
LINK_LIBRARIES kritaui kritalibbrush kritalibpaintop Qt5::Test)
# FIXME this test doesn't compile
#ecm_add_test(
# kis_input_manager_test.cpp ../../../sdk/tests/testutil.cpp
# TEST_NAME krita-ui-KisInputManagerTest
# LINK_LIBRARIES kritaui kritaimage Qt5::Test)
diff --git a/libs/ui/tests/fill_processing_visitor_test.cpp b/libs/ui/tests/fill_processing_visitor_test.cpp
index 45fe6ce2e4..9851ea9696 100644
--- a/libs/ui/tests/fill_processing_visitor_test.cpp
+++ b/libs/ui/tests/fill_processing_visitor_test.cpp
@@ -1,146 +1,145 @@
/*
* 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 "fill_processing_visitor_test.h"
#include <QTest>
#include "kis_undo_stores.h"
#include "kis_processing_applicator.h"
#include "testutil.h"
#include "qimage_based_test.h"
#include "stroke_testing_utils.h"
#include <resources/KoPattern.h>
#include "kis_canvas_resource_provider.h"
#include <processing/fill_processing_visitor.h>
class FillProcessingVisitorTester : public TestUtil::QImageBasedTest
{
public:
FillProcessingVisitorTester()
: QImageBasedTest("fill_processing")
{
}
void test(const QString &testname, bool haveSelection, bool usePattern, bool selectionOnly) {
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
KisImageSP image = createImage(undoStore);
if (haveSelection) {
addGlobalSelection(image);
}
image->initialRefreshGraph();
QVERIFY(checkLayersInitial(image));
KisNodeSP fillNode = findNode(image->root(), "paint1");
KoCanvasResourceManager *manager = utils::createResourceManager(image, fillNode);
KoPattern *newPattern = new KoPattern(TestUtil::fetchDataFileLazy("HR_SketchPaper_01.pat"));
newPattern->load();
Q_ASSERT(newPattern->valid());
QVariant v;
v.setValue(static_cast<void*>(newPattern));
manager->setResource(KisCanvasResourceProvider::CurrentPattern, v);
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
fillNode,
- image->postExecutionUndoAdapter(),
manager);
KisProcessingVisitorSP visitor =
new FillProcessingVisitor(QPoint(100,100),
image->globalSelection(),
resources,
false, // useFastMode
usePattern,
selectionOnly,
10, 10, 10, true, false);
KisProcessingApplicator applicator(image, fillNode,
KisProcessingApplicator::NONE);
applicator.applyVisitor(visitor);
applicator.end();
image->waitForDone();
QVERIFY(checkOneLayer(image, fillNode, testname));
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayersInitial(image));
}
};
void FillProcessingVisitorTest::testFillColorNoSelection()
{
FillProcessingVisitorTester tester;
tester.test("fill_color_no_selection", false, false, false);
}
void FillProcessingVisitorTest::testFillPatternNoSelection()
{
FillProcessingVisitorTester tester;
tester.test("fill_pattern_no_selection", false, true, false);
}
void FillProcessingVisitorTest::testFillColorHaveSelection()
{
FillProcessingVisitorTester tester;
tester.test("fill_color_have_selection", true, false, false);
}
void FillProcessingVisitorTest::testFillPatternHaveSelection()
{
FillProcessingVisitorTester tester;
tester.test("fill_pattern_have_selection", true, true, false);
}
void FillProcessingVisitorTest::testFillColorNoSelectionSelectionOnly()
{
FillProcessingVisitorTester tester;
tester.test("fill_color_no_selection_selection_only", false, false, true);
}
void FillProcessingVisitorTest::testFillPatternNoSelectionSelectionOnly()
{
FillProcessingVisitorTester tester;
tester.test("fill_pattern_no_selection_selection_only", false, true, true);
}
void FillProcessingVisitorTest::testFillColorHaveSelectionSelectionOnly()
{
FillProcessingVisitorTester tester;
tester.test("fill_color_have_selection_selection_only", true, false, true);
}
void FillProcessingVisitorTest::testFillPatternHaveSelectionSelectionOnly()
{
FillProcessingVisitorTester tester;
tester.test("fill_pattern_have_selection_selection_only", true, true, true);
}
QTEST_MAIN(FillProcessingVisitorTest)
diff --git a/libs/ui/tests/util.h b/libs/ui/tests/util.h
index 63f6c69040..14745d203f 100644
--- a/libs/ui/tests/util.h
+++ b/libs/ui/tests/util.h
@@ -1,226 +1,234 @@
/*
* 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 _UTIL_H_
#define _UTIL_H_
#include <QTest>
#include <QTest>
#include <QBitArray>
#include <KisDocument.h>
#include <KoDocumentInfo.h>
#include <KoColorSpaceRegistry.h>
#include <KoShapeContainer.h>
#include <KoColorSpace.h>
#include <KoPathShape.h>
#include <kis_count_visitor.h>
#include "kis_types.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "KisDocument.h"
#include "KisPart.h"
#include "kis_image.h"
#include "kis_pixel_selection.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_shape_layer.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_shape_selection.h"
#include "kis_default_bounds.h"
#include "kis_transform_mask_params_interface.h"
KisSelectionSP createPixelSelection(KisPaintDeviceSP paintDevice)
{
KisSelectionSP pixelSelection = new KisSelection(new KisSelectionDefaultBounds(paintDevice));
KisFillPainter gc(pixelSelection->pixelSelection());
gc.fillRect(10, 10, 200, 200, KoColor(gc.device()->colorSpace()));
gc.fillRect(150, 150, 200, 200, KoColor(QColor(100, 100, 100, 100), gc.device()->colorSpace()));
gc.end();
return pixelSelection;
}
KisSelectionSP createVectorSelection(KisPaintDeviceSP paintDevice, KisImageWSP image)
{
KisSelectionSP vectorSelection = new KisSelection(new KisSelectionDefaultBounds(paintDevice));
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF(10, 10) + QPointF(100, 0));
path->lineTo(QPointF(100, 100));
path->lineTo(QPointF(10, 10) + QPointF(0, 100));
path->close();
path->normalize();
KisShapeSelection* shapeSelection = new KisShapeSelection(image, vectorSelection);
shapeSelection->addShape(path);
vectorSelection->setShapeSelection(shapeSelection);
return vectorSelection;
}
QTransform createTestingTransform() {
return QTransform(1,2,3,4,5,6,7,8,9);
}
-KisDocument* createCompleteDocument()
+KisDocument* createCompleteDocument(bool shouldMaskToShapeLayer = false)
{
KisImageWSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
KisGroupLayerSP group1 = new KisGroupLayer(image, "group1", 50);
KisGroupLayerSP group2 = new KisGroupLayer(image, "group2", 100);
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paintlayer1", OPACITY_OPAQUE_U8);
paintLayer1->setUserLocked(true);
QBitArray channelFlags(4);
channelFlags[0] = true;
channelFlags[2] = true;
paintLayer1->setChannelFlags(channelFlags);
{
KisFillPainter gc(paintLayer1->paintDevice());
gc.fillRect(10, 10, 200, 200, KoColor(Qt::red, paintLayer1->paintDevice()->colorSpace()));
gc.end();
}
KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paintlayer2", OPACITY_TRANSPARENT_U8, KoColorSpaceRegistry::instance()->lab16());
paintLayer2->setVisible(false);
{
KisFillPainter gc(paintLayer2->paintDevice());
gc.fillRect(0, 0, 900, 1024, KoColor(QColor(10, 20, 30), paintLayer2->paintDevice()->colorSpace()));
gc.end();
}
KisCloneLayerSP cloneLayer1 = new KisCloneLayer(group1, image, "clonelayer1", 150);
cloneLayer1->setX(100);
cloneLayer1->setY(100);
KisSelectionSP pixelSelection = createPixelSelection(paintLayer1->paintDevice());
KisFilterConfigurationSP kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(group2->projection());
Q_ASSERT(kfc);
KisAdjustmentLayerSP adjustmentLayer1 = new KisAdjustmentLayer(image, "adjustmentLayer1", kfc, pixelSelection);
kfc = 0; // kfc cannot be shared!
KisSelectionSP vectorSelection = createVectorSelection(paintLayer2->paintDevice(), image);
kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(group2->projection());
KisAdjustmentLayerSP adjustmentLayer2 = new KisAdjustmentLayer(image, "adjustmentLayer2", kfc, vectorSelection);
kfc = 0; // kfc cannot be shared!
image->addNode(paintLayer1);
image->addNode(group1);
image->addNode(paintLayer2, group1);
image->addNode(group2);
image->addNode(cloneLayer1, group2);
image->addNode(adjustmentLayer1, group2);
// KoShapeContainer * parentContainer =
// dynamic_cast<KoShapeContainer*>(doc->shapeForNode(group1));
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF(10, 10) + QPointF(100, 0));
path->lineTo(QPointF(100, 100));
path->lineTo(QPointF(10, 10) + QPointF(0, 100));
path->close();
path->normalize();
KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), image, "shapeLayer1", 75);
shapeLayer->addShape(path);
image->addNode(shapeLayer, group1);
image->addNode(adjustmentLayer2, group1);
KisFilterMaskSP filterMask1 = new KisFilterMask();
filterMask1->setName("filterMask1");
kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(group2->projection());
filterMask1->setFilter(kfc);
kfc = 0; // kfc cannot be shared!
filterMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(filterMask1, paintLayer1);
KisFilterMaskSP filterMask2 = new KisFilterMask();
filterMask2->setName("filterMask2");
kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(group2->projection());
filterMask2->setFilter(kfc);
kfc = 0; // kfc cannot be shared!
filterMask2->setSelection(createVectorSelection(paintLayer2->paintDevice(), image));
image->addNode(filterMask2, paintLayer2);
KisTransparencyMaskSP transparencyMask1 = new KisTransparencyMask();
transparencyMask1->setName("transparencyMask1");
transparencyMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(transparencyMask1, group1);
KisTransparencyMaskSP transparencyMask2 = new KisTransparencyMask();
transparencyMask2->setName("transparencyMask2");
transparencyMask2->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(transparencyMask2, group2);
KisSelectionMaskSP selectionMask1 = new KisSelectionMask(image);
image->addNode(selectionMask1, paintLayer1);
selectionMask1->setName("selectionMask1");
selectionMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
KisSelectionMaskSP selectionMask2 = new KisSelectionMask(image);
selectionMask2->setName("selectionMask2");
selectionMask2->setSelection(createPixelSelection(paintLayer2->paintDevice()));
image->addNode(selectionMask2, paintLayer2);
KisTransformMaskSP transformMask = new KisTransformMask();
transformMask->setName("testTransformMask");
transformMask->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(createTestingTransform())));
image->addNode(transformMask, paintLayer2);
+ if (shouldMaskToShapeLayer) {
+ // add all-visible transparency mask to crash a shape layer
+ KisTransparencyMaskSP transparencyMask3 = new KisTransparencyMask();
+ transparencyMask3->setName("crashy-transparency-mask");
+ transparencyMask3->initSelection(shapeLayer);
+ image->addNode(transparencyMask3, shapeLayer);
+ }
+
return doc;
}
KisDocument *createEmptyDocument()
{
KisImageWSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
return doc;
}
#endif
diff --git a/libs/ui/thememanager.cpp b/libs/ui/thememanager.cpp
index 2917171871..8fd658026a 100644
--- a/libs/ui/thememanager.cpp
+++ b/libs/ui/thememanager.cpp
@@ -1,310 +1,310 @@
/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2004-08-02
* Description : theme manager
*
* Copyright (C) 2006-2011 by Gilles Caulier <caulier dot gilles at gmail dot 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, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT 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 "thememanager.h"
// Qt includes
#include <QStringList>
#include <QFileInfo>
#include <QFile>
#include <QApplication>
#include <QPalette>
#include <QColor>
#include <QActionGroup>
#include <QBitmap>
#include <QPainter>
#include <QPixmap>
#include <QDate>
#include <QDesktopWidget>
#include <QApplication>
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>
#include <QDebug>
#include <QStandardPaths>
// KDE includes
#include <QMessageBox>
#include <klocalizedstring.h>
#include <kcolorscheme.h>
#include <kactioncollection.h>
#include <KoResourcePaths.h>
#include <kactionmenu.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <QAction>
// Calligra
#include <kis_icon.h>
#ifdef __APPLE__
#include <QStyle>
#endif
namespace Digikam
{
// ---------------------------------------------------------------
class ThemeManager::ThemeManagerPriv
{
public:
ThemeManagerPriv()
: themeMenuActionGroup(0)
, themeMenuAction(0)
{
}
QString currentThemeName;
QMap<QString, QString> themeMap; // map<theme name, theme config path>
QActionGroup* themeMenuActionGroup;
KActionMenu* themeMenuAction;
};
ThemeManager::ThemeManager(const QString &theme, QObject *parent)
: QObject(parent)
, d(new ThemeManagerPriv)
{
//qDebug() << "Creating theme manager with theme" << theme;
d->currentThemeName = theme;
populateThemeMap();
}
ThemeManager::~ThemeManager()
{
delete d;
}
QString ThemeManager::currentThemeName() const
{
//qDebug() << "getting current themename";
QString themeName;
if (d->themeMenuAction && d->themeMenuActionGroup) {
QAction* action = d->themeMenuActionGroup->checkedAction();
if (action) {
themeName = action->text().remove('&');
}
//qDebug() << "\tthemename from action" << themeName;
}
else if (!d->currentThemeName.isEmpty()) {
//qDebug() << "\tcurrent themename" << d->currentThemeName;
themeName = d->currentThemeName;
}
else {
//qDebug() << "\tfallback";
themeName = "Krita dark";
}
//qDebug() << "\tresult" << themeName;
return themeName;
}
void ThemeManager::setCurrentTheme(const QString& name)
{
//qDebug() << "setCurrentTheme();" << d->currentThemeName << "to" << name;
d->currentThemeName = name;
if (d->themeMenuAction && d->themeMenuActionGroup) {
QList<QAction*> list = d->themeMenuActionGroup->actions();
Q_FOREACH (QAction* action, list) {
if (action->text().remove('&') == name) {
action->setChecked(true);
}
}
}
slotChangePalette();
}
void ThemeManager::slotChangePalette()
{
//qDebug() << "slotChangePalette" << sender();
QString theme(currentThemeName());
QString filename = d->themeMap.value(theme);
KSharedConfigPtr config = KSharedConfig::openConfig(filename);
QPalette palette = qApp->palette();
QPalette::ColorGroup states[3] = { QPalette::Active, QPalette::Inactive, QPalette::Disabled };
// TT thinks tooltips shouldn't use active, so we use our active colors for all states
KColorScheme schemeTooltip(QPalette::Active, KColorScheme::Tooltip, config);
for ( int i = 0; i < 3 ; ++i ) {
QPalette::ColorGroup state = states[i];
KColorScheme schemeView(state, KColorScheme::View, config);
KColorScheme schemeWindow(state, KColorScheme::Window, config);
KColorScheme schemeButton(state, KColorScheme::Button, config);
KColorScheme schemeSelection(state, KColorScheme::Selection, config);
palette.setBrush(state, QPalette::WindowText, schemeWindow.foreground());
palette.setBrush(state, QPalette::Window, schemeWindow.background());
palette.setBrush(state, QPalette::Base, schemeView.background());
palette.setBrush(state, QPalette::Text, schemeView.foreground());
palette.setBrush(state, QPalette::Button, schemeButton.background());
palette.setBrush(state, QPalette::ButtonText, schemeButton.foreground());
palette.setBrush(state, QPalette::Highlight, schemeSelection.background());
palette.setBrush(state, QPalette::HighlightedText, schemeSelection.foreground());
palette.setBrush(state, QPalette::ToolTipBase, schemeTooltip.background());
palette.setBrush(state, QPalette::ToolTipText, schemeTooltip.foreground());
palette.setColor(state, QPalette::Light, schemeWindow.shade(KColorScheme::LightShade));
palette.setColor(state, QPalette::Midlight, schemeWindow.shade(KColorScheme::MidlightShade));
palette.setColor(state, QPalette::Mid, schemeWindow.shade(KColorScheme::MidShade));
palette.setColor(state, QPalette::Dark, schemeWindow.shade(KColorScheme::DarkShade));
palette.setColor(state, QPalette::Shadow, schemeWindow.shade(KColorScheme::ShadowShade));
palette.setBrush(state, QPalette::AlternateBase, schemeView.background(KColorScheme::AlternateBackground));
palette.setBrush(state, QPalette::Link, schemeView.foreground(KColorScheme::LinkText));
palette.setBrush(state, QPalette::LinkVisited, schemeView.foreground(KColorScheme::VisitedText));
}
//qDebug() << ">>>>>>>>>>>>>>>>>> going to set palette on app" << theme;
qApp->setPalette(palette);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
if (theme == "Krita bright" || theme.isEmpty()) {
qApp->setStyle("Macintosh");
qApp->style()->polish(qApp);
} else {
qApp->setStyle("Fusion");
qApp->style()->polish(qApp);
}
#endif
emit signalThemeChanged();
}
void ThemeManager::setThemeMenuAction(KActionMenu* const action)
{
d->themeMenuAction = action;
populateThemeMenu();
}
void ThemeManager::registerThemeActions(KActionCollection *actionCollection)
{
if (!d->themeMenuAction) return;
actionCollection->addAction("theme_menu", d->themeMenuAction);
}
void ThemeManager::populateThemeMenu()
{
if (!d->themeMenuAction) return;
d->themeMenuAction->menu()->clear();
delete d->themeMenuActionGroup;
d->themeMenuActionGroup = new QActionGroup(d->themeMenuAction);
connect(d->themeMenuActionGroup, SIGNAL(triggered(QAction*)),
this, SLOT(slotChangePalette()));
QAction * action;
const QStringList schemeFiles = KoResourcePaths::findAllResources("data", "color-schemes/*.colors");
QMap<QString, QAction*> actionMap;
for (int i = 0; i < schemeFiles.size(); ++i) {
const QString filename = schemeFiles.at(i);
const QFileInfo info(filename);
KSharedConfigPtr config = KSharedConfig::openConfig(filename);
QIcon icon = createSchemePreviewIcon(config);
KConfigGroup group(config, "General");
const QString name = group.readEntry("Name", info.baseName());
action = new QAction(name, d->themeMenuActionGroup);
action->setIcon(icon);
action->setCheckable(true);
actionMap.insert(name, action);
}
// sort the list
QStringList actionMapKeys = actionMap.keys();
actionMapKeys.sort();
Q_FOREACH (const QString& name, actionMapKeys) {
if ( name == currentThemeName()) {
actionMap.value(name)->setChecked(true);
}
d->themeMenuAction->addAction(actionMap.value(name));
}
}
QPixmap ThemeManager::createSchemePreviewIcon(const KSharedConfigPtr& config)
{
// code taken from kdebase/workspace/kcontrol/colors/colorscm.cpp
const uchar bits1[] = { 0xff, 0xff, 0xff, 0x2c, 0x16, 0x0b };
const uchar bits2[] = { 0x68, 0x34, 0x1a, 0xff, 0xff, 0xff };
const QSize bitsSize(24, 2);
const QBitmap b1 = QBitmap::fromData(bitsSize, bits1);
const QBitmap b2 = QBitmap::fromData(bitsSize, bits2);
QPixmap pixmap(23, 16);
pixmap.fill(Qt::black); // FIXME use some color other than black for borders?
KConfigGroup group(config, "WM");
QPainter p(&pixmap);
KColorScheme windowScheme(QPalette::Active, KColorScheme::Window, config);
p.fillRect(1, 1, 7, 7, windowScheme.background());
p.fillRect(2, 2, 5, 2, QBrush(windowScheme.foreground().color(), b1));
KColorScheme buttonScheme(QPalette::Active, KColorScheme::Button, config);
p.fillRect(8, 1, 7, 7, buttonScheme.background());
p.fillRect(9, 2, 5, 2, QBrush(buttonScheme.foreground().color(), b1));
p.fillRect(15, 1, 7, 7, group.readEntry("activeBackground", QColor(96, 148, 207)));
p.fillRect(16, 2, 5, 2, QBrush(group.readEntry("activeForeground", QColor(255, 255, 255)), b1));
KColorScheme viewScheme(QPalette::Active, KColorScheme::View, config);
p.fillRect(1, 8, 7, 7, viewScheme.background());
p.fillRect(2, 12, 5, 2, QBrush(viewScheme.foreground().color(), b2));
KColorScheme selectionScheme(QPalette::Active, KColorScheme::Selection, config);
p.fillRect(8, 8, 7, 7, selectionScheme.background());
p.fillRect(9, 12, 5, 2, QBrush(selectionScheme.foreground().color(), b2));
p.fillRect(15, 8, 7, 7, group.readEntry("inactiveBackground", QColor(224, 223, 222)));
p.fillRect(16, 12, 5, 2, QBrush(group.readEntry("inactiveForeground", QColor(20, 19, 18)), b2));
p.end();
return pixmap;
}
void ThemeManager::populateThemeMap()
{
const QStringList schemeFiles = KoResourcePaths::findAllResources("data", "color-schemes/*.colors");
for (int i = 0; i < schemeFiles.size(); ++i) {
const QString filename = schemeFiles.at(i);
const QFileInfo info(filename);
KSharedConfigPtr config = KSharedConfig::openConfig(filename);
KConfigGroup group(config, "General");
const QString name = group.readEntry("Name", info.baseName());
d->themeMap.insert(name, filename);
}
}
} // namespace Digikam
diff --git a/libs/ui/tool/kis_figure_painting_tool_helper.cpp b/libs/ui/tool/kis_figure_painting_tool_helper.cpp
index d7a989e655..96880b689f 100644
--- a/libs/ui/tool/kis_figure_painting_tool_helper.cpp
+++ b/libs/ui/tool/kis_figure_painting_tool_helper.cpp
@@ -1,152 +1,151 @@
/*
* 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_figure_painting_tool_helper.h"
#include <KoCanvasResourceManager.h>
#include "kis_resources_snapshot.h"
#include <kis_distance_information.h>
#include "kis_image.h"
#include "kis_painter.h"
KisFigurePaintingToolHelper::KisFigurePaintingToolHelper(const KUndo2MagicString &name,
KisImageWSP image,
KisNodeSP currentNode,
KoCanvasResourceManager *resourceManager,
KisPainter::StrokeStyle strokeStyle,
KisPainter::FillStyle fillStyle)
{
m_strokesFacade = image.data();
m_resources =
new KisResourcesSnapshot(image,
currentNode,
- image->postExecutionUndoAdapter(),
resourceManager);
m_resources->setStrokeStyle(strokeStyle);
m_resources->setFillStyle(fillStyle);
PainterInfo *painterInfo = new PainterInfo();
KisStrokeStrategy *stroke =
new FreehandStrokeStrategy(m_resources->needsIndirectPainting(),
m_resources->indirectPaintingCompositeOp(),
m_resources, painterInfo, name);
m_strokeId = m_strokesFacade->startStroke(stroke);
}
KisFigurePaintingToolHelper::~KisFigurePaintingToolHelper()
{
m_strokesFacade->endStroke(m_strokeId);
}
void KisFigurePaintingToolHelper::paintLine(const KisPaintInformation &pi0,
const KisPaintInformation &pi1)
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::Data(m_resources->currentNode(),
0,
pi0, pi1));
}
void KisFigurePaintingToolHelper::paintPolyline(const vQPointF &points)
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::Data(m_resources->currentNode(),
0,
FreehandStrokeStrategy::Data::POLYLINE,
points));
}
void KisFigurePaintingToolHelper::paintPolygon(const vQPointF &points)
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::Data(m_resources->currentNode(),
0,
FreehandStrokeStrategy::Data::POLYGON,
points));
}
void KisFigurePaintingToolHelper::paintRect(const QRectF &rect)
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::Data(m_resources->currentNode(),
0,
FreehandStrokeStrategy::Data::RECT,
rect));
}
void KisFigurePaintingToolHelper::paintEllipse(const QRectF &rect)
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::Data(m_resources->currentNode(),
0,
FreehandStrokeStrategy::Data::ELLIPSE,
rect));
}
void KisFigurePaintingToolHelper::paintPainterPath(const QPainterPath &path)
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::Data(m_resources->currentNode(),
0,
FreehandStrokeStrategy::Data::PAINTER_PATH,
path));
}
void KisFigurePaintingToolHelper::setFGColorOverride(const KoColor &color)
{
m_resources->setFGColorOverride(color);
}
void KisFigurePaintingToolHelper::setBGColorOverride(const KoColor &color)
{
m_resources->setBGColorOverride(color);
}
void KisFigurePaintingToolHelper::setSelectionOverride(KisSelectionSP m_selection)
{
m_resources->setSelectionOverride(m_selection);
}
void KisFigurePaintingToolHelper::setBrush(const KisPaintOpPresetSP &brush)
{
m_resources->setBrush(brush);
}
void KisFigurePaintingToolHelper::paintPainterPathQPen(const QPainterPath path, const QPen &pen, const KoColor &color)
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::Data(m_resources->currentNode(),
0,
FreehandStrokeStrategy::Data::QPAINTER_PATH,
path, pen, color));
}
void KisFigurePaintingToolHelper::paintPainterPathQPenFill(const QPainterPath path, const QPen &pen, const KoColor &color)
{
m_strokesFacade->addJob(m_strokeId,
new FreehandStrokeStrategy::Data(m_resources->currentNode(),
0,
FreehandStrokeStrategy::Data::QPAINTER_PATH_FILL,
path, pen, color));
}
diff --git a/libs/ui/tool/kis_resources_snapshot.cpp b/libs/ui/tool/kis_resources_snapshot.cpp
index 1b8966f736..f758268e6b 100644
--- a/libs/ui/tool/kis_resources_snapshot.cpp
+++ b/libs/ui/tool/kis_resources_snapshot.cpp
@@ -1,356 +1,356 @@
/*
* 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_resources_snapshot.h"
#include <KoColor.h>
#include <resources/KoAbstractGradient.h>
#include <KoCompositeOpRegistry.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include <brushengine/kis_paintop_registry.h>
#include <kis_threaded_text_rendering_workaround.h>
#include <resources/KoPattern.h>
#include "kis_canvas_resource_provider.h"
#include "filter/kis_filter_configuration.h"
#include "kis_image.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "recorder/kis_recorded_paint_action.h"
#include "kis_selection.h"
#include "kis_selection_mask.h"
+#include "kis_algebra_2d.h"
struct KisResourcesSnapshot::Private {
Private()
: currentPattern(0)
, currentGradient(0)
, currentGenerator(0)
, compositeOp(0)
{
}
KisImageSP image;
KisDefaultBoundsBaseSP bounds;
- KisPostExecutionUndoAdapter *undoAdapter;
KoColor currentFgColor;
KoColor currentBgColor;
KoPattern *currentPattern;
KoAbstractGradient *currentGradient;
KisPaintOpPresetSP currentPaintOpPreset;
KisNodeSP currentNode;
qreal currentExposure;
KisFilterConfigurationSP currentGenerator;
QPointF axesCenter;
bool mirrorMaskHorizontal;
bool mirrorMaskVertical;
quint8 opacity;
QString compositeOpId;
const KoCompositeOp *compositeOp;
KisPainter::StrokeStyle strokeStyle;
KisPainter::FillStyle fillStyle;
bool globalAlphaLock;
qreal effectiveZoom;
bool presetAllowsLod;
KisSelectionSP selectionOverride;
};
-KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KisPostExecutionUndoAdapter *undoAdapter, KoCanvasResourceManager *resourceManager, KisDefaultBoundsBaseSP bounds)
+KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KoCanvasResourceManager *resourceManager, KisDefaultBoundsBaseSP bounds)
: m_d(new Private())
{
m_d->image = image;
if (!bounds) {
bounds = new KisDefaultBounds(m_d->image);
}
m_d->bounds = bounds;
- m_d->undoAdapter = undoAdapter;
m_d->currentFgColor = resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value<KoColor>();
m_d->currentBgColor = resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value<KoColor>();
m_d->currentPattern = resourceManager->resource(KisCanvasResourceProvider::CurrentPattern).value<KoPattern*>();
m_d->currentGradient = resourceManager->resource(KisCanvasResourceProvider::CurrentGradient).value<KoAbstractGradient*>();
/**
* We should deep-copy the preset, so that long-runnign actions
* will have correct brush parameters. Theoretically this cloniong
* can be expensive, but according to measurements, it takes
* something like 0.1 ms for an average preset.
*/
m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>()->clone();
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset);
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
m_d->currentExposure = resourceManager->resource(KisCanvasResourceProvider::HdrExposure).toDouble();
m_d->currentGenerator = resourceManager->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value<KisFilterConfiguration*>();
- m_d->axesCenter = resourceManager->resource(KisCanvasResourceProvider::MirrorAxesCenter).toPointF();
- if (m_d->axesCenter.isNull()){
- QRect bounds = m_d->bounds->bounds();
- m_d->axesCenter = QPointF(0.5 * bounds.width(), 0.5 * bounds.height());
+
+ QPointF relativeAxesCenter(0.5, 0.5);
+ if (m_d->image) {
+ relativeAxesCenter = m_d->image->mirrorAxesCenter();
}
+ m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds());
m_d->mirrorMaskHorizontal = resourceManager->resource(KisCanvasResourceProvider::MirrorHorizontal).toBool();
m_d->mirrorMaskVertical = resourceManager->resource(KisCanvasResourceProvider::MirrorVertical).toBool();
qreal normOpacity = resourceManager->resource(KisCanvasResourceProvider::Opacity).toDouble();
m_d->opacity = quint8(normOpacity * OPACITY_OPAQUE_U8);
m_d->compositeOpId = resourceManager->resource(KisCanvasResourceProvider::CurrentEffectiveCompositeOp).toString();
setCurrentNode(currentNode);
/**
* Fill and Stroke styles are not a part of the resource manager
* so the tools should set them manually
* TODO: port stroke and fill styles to be a part
* of the resource manager
*/
m_d->strokeStyle = KisPainter::StrokeStyleBrush;
m_d->fillStyle = KisPainter::FillStyleNone;
m_d->globalAlphaLock = resourceManager->resource(KisCanvasResourceProvider::GlobalAlphaLock).toBool();
m_d->effectiveZoom = resourceManager->resource(KisCanvasResourceProvider::EffectiveZoom).toDouble();
m_d->presetAllowsLod = resourceManager->resource(KisCanvasResourceProvider::PresetAllowsLod).toBool();
}
KisResourcesSnapshot::~KisResourcesSnapshot()
{
delete m_d;
}
void KisResourcesSnapshot::setupPainter(KisPainter* painter)
{
painter->setPaintColor(m_d->currentFgColor);
painter->setBackgroundColor(m_d->currentBgColor);
painter->setGenerator(m_d->currentGenerator);
painter->setPattern(m_d->currentPattern);
painter->setGradient(m_d->currentGradient);
QBitArray lockflags = channelLockFlags();
if (lockflags.size() > 0) {
painter->setChannelFlags(lockflags);
}
painter->setOpacity(m_d->opacity);
painter->setCompositeOp(m_d->compositeOp);
painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical);
painter->setStrokeStyle(m_d->strokeStyle);
painter->setFillStyle(m_d->fillStyle);
/**
* The paintOp should be initialized the last, because it may
* ask the painter for some options while initialization
*/
painter->setPaintOpPreset(m_d->currentPaintOpPreset, m_d->currentNode, m_d->image);
}
void KisResourcesSnapshot::setupPaintAction(KisRecordedPaintAction *action)
{
action->setPaintOpPreset(m_d->currentPaintOpPreset);
action->setPaintIncremental(!needsIndirectPainting());
action->setPaintColor(m_d->currentFgColor);
action->setBackgroundColor(m_d->currentBgColor);
action->setGenerator(m_d->currentGenerator);
action->setGradient(m_d->currentGradient);
action->setPattern(m_d->currentPattern);
action->setOpacity(m_d->opacity / qreal(OPACITY_OPAQUE_U8));
action->setCompositeOp(m_d->compositeOp->id());
action->setStrokeStyle(m_d->strokeStyle);
action->setFillStyle(m_d->fillStyle);
}
KisPostExecutionUndoAdapter* KisResourcesSnapshot::postExecutionUndoAdapter() const
{
- return m_d->undoAdapter;
+ return m_d->image ? m_d->image->postExecutionUndoAdapter() : 0;
}
void KisResourcesSnapshot::setCurrentNode(KisNodeSP node)
{
m_d->currentNode = node;
KisPaintDeviceSP device;
if(m_d->currentNode && (device = m_d->currentNode->paintDevice())) {
m_d->compositeOp = device->colorSpace()->compositeOp(m_d->compositeOpId);
if(!m_d->compositeOp) {
m_d->compositeOp = device->colorSpace()->compositeOp(COMPOSITE_OVER);
}
}
}
void KisResourcesSnapshot::setStrokeStyle(KisPainter::StrokeStyle strokeStyle)
{
m_d->strokeStyle = strokeStyle;
}
void KisResourcesSnapshot::setFillStyle(KisPainter::FillStyle fillStyle)
{
m_d->fillStyle = fillStyle;
}
KisNodeSP KisResourcesSnapshot::currentNode() const
{
return m_d->currentNode;
}
KisImageSP KisResourcesSnapshot::image() const
{
return m_d->image;
}
bool KisResourcesSnapshot::needsIndirectPainting() const
{
return !m_d->currentPaintOpPreset->settings()->paintIncremental();
}
QString KisResourcesSnapshot::indirectPaintingCompositeOp() const
{
return m_d->currentPaintOpPreset->settings()->indirectPaintingCompositeOp();
}
KisSelectionSP KisResourcesSnapshot::activeSelection() const
{
/**
* It is possible to have/use the snapshot without the image. Such
* usecase is present for example in the scratchpad.
*/
if (m_d->selectionOverride) {
return m_d->selectionOverride;
}
KisSelectionSP selection = m_d->image ? m_d->image->globalSelection() : 0;
KisLayerSP layer = dynamic_cast<KisLayer*>(m_d->currentNode.data());
KisSelectionMaskSP mask;
if((layer = dynamic_cast<KisLayer*>(m_d->currentNode.data()))) {
selection = layer->selection();
} else if ((mask = dynamic_cast<KisSelectionMask*>(m_d->currentNode.data())) &&
mask->selection() == selection) {
selection = 0;
}
return selection;
}
bool KisResourcesSnapshot::needsAirbrushing() const
{
return m_d->currentPaintOpPreset->settings()->isAirbrushing();
}
int KisResourcesSnapshot::airbrushingRate() const
{
return m_d->currentPaintOpPreset->settings()->rate();
}
void KisResourcesSnapshot::setOpacity(qreal opacity)
{
m_d->opacity = opacity * OPACITY_OPAQUE_U8;
}
quint8 KisResourcesSnapshot::opacity() const
{
return m_d->opacity;
}
const KoCompositeOp* KisResourcesSnapshot::compositeOp() const
{
return m_d->compositeOp;
}
QString KisResourcesSnapshot::compositeOpId() const
{
return m_d->compositeOpId;
}
KoPattern* KisResourcesSnapshot::currentPattern() const
{
return m_d->currentPattern;
}
KoColor KisResourcesSnapshot::currentFgColor() const
{
return m_d->currentFgColor;
}
KoColor KisResourcesSnapshot::currentBgColor() const
{
return m_d->currentBgColor;
}
KisPaintOpPresetSP KisResourcesSnapshot::currentPaintOpPreset() const
{
return m_d->currentPaintOpPreset;
}
QBitArray KisResourcesSnapshot::channelLockFlags() const
{
QBitArray channelFlags;
KisPaintLayer *paintLayer;
if ((paintLayer = dynamic_cast<KisPaintLayer*>(m_d->currentNode.data()))) {
channelFlags = paintLayer->channelLockFlags();
if (m_d->globalAlphaLock) {
if (channelFlags.isEmpty()) {
channelFlags = paintLayer->colorSpace()->channelFlags(true, true);
}
channelFlags &= paintLayer->colorSpace()->channelFlags(true, false);
}
}
return channelFlags;
}
qreal KisResourcesSnapshot::effectiveZoom() const
{
return m_d->effectiveZoom;
}
bool KisResourcesSnapshot::presetAllowsLod() const
{
return m_d->presetAllowsLod;
}
void KisResourcesSnapshot::setFGColorOverride(const KoColor &color)
{
m_d->currentFgColor = color;
}
void KisResourcesSnapshot::setBGColorOverride(const KoColor &color)
{
m_d->currentBgColor = color;
}
void KisResourcesSnapshot::setSelectionOverride(KisSelectionSP selection)
{
m_d->selectionOverride = selection;
}
void KisResourcesSnapshot::setBrush(const KisPaintOpPresetSP &brush)
{
m_d->currentPaintOpPreset = brush;
}
diff --git a/libs/ui/tool/kis_resources_snapshot.h b/libs/ui/tool/kis_resources_snapshot.h
index ca9ca141a9..aefc95e1e7 100644
--- a/libs/ui/tool/kis_resources_snapshot.h
+++ b/libs/ui/tool/kis_resources_snapshot.h
@@ -1,103 +1,103 @@
/*
* 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_RESOURCES_SNAPSHOT_H
#define __KIS_RESOURCES_SNAPSHOT_H
#include "kis_shared.h"
#include "kis_shared_ptr.h"
#include "kis_types.h"
#include "kritaui_export.h"
#include "kis_painter.h"
#include "kis_default_bounds.h"
class KoCanvasResourceManager;
class KoCompositeOp;
class KisPainter;
class KisPostExecutionUndoAdapter;
class KisRecordedPaintAction;
class KoPattern;
/**
* @brief The KisResourcesSnapshot class takes a snapshot of the various resources
* like colors and settings used at the begin of a stroke or a recording so subsequent
* changes don't impact the running stroke. The main reason for the snapshot is that the
* user can *change* the options while the stroke is being executed in the background.
*/
class KRITAUI_EXPORT KisResourcesSnapshot : public KisShared
{
public:
- KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KisPostExecutionUndoAdapter *undoAdapter, KoCanvasResourceManager *resourceManager, KisDefaultBoundsBaseSP bounds = 0);
+ KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KoCanvasResourceManager *resourceManager, KisDefaultBoundsBaseSP bounds = 0);
~KisResourcesSnapshot();
void setupPainter(KisPainter *painter);
// XXX: This was marked as KDE_DEPRECATED, but no althernative was
// given in the apidox.
void setupPaintAction(KisRecordedPaintAction *action);
KisPostExecutionUndoAdapter* postExecutionUndoAdapter() const;
void setCurrentNode(KisNodeSP node);
void setStrokeStyle(KisPainter::StrokeStyle strokeStyle);
void setFillStyle(KisPainter::FillStyle fillStyle);
KisNodeSP currentNode() const;
KisImageSP image() const;
bool needsIndirectPainting() const;
QString indirectPaintingCompositeOp() const;
/**
* \return currently active selection. Note that it will return
* null if current node *is* the current selection. This
* is done to avoid recursive selection application when
* painting on selectgion masks.
*/
KisSelectionSP activeSelection() const;
bool needsAirbrushing() const;
int airbrushingRate() const;
void setOpacity(qreal opacity);
quint8 opacity() const;
const KoCompositeOp* compositeOp() const;
QString compositeOpId() const;
KoPattern* currentPattern() const;
KoColor currentFgColor() const;
KoColor currentBgColor() const;
KisPaintOpPresetSP currentPaintOpPreset() const;
/// @return the channel lock flags of the current node with the global override applied
QBitArray channelLockFlags() const;
qreal effectiveZoom() const;
bool presetAllowsLod() const;
void setFGColorOverride(const KoColor &color);
void setBGColorOverride(const KoColor &color);
void setSelectionOverride(KisSelectionSP selection);
void setBrush(const KisPaintOpPresetSP &brush);
private:
struct Private;
Private * const m_d;
};
typedef KisSharedPtr<KisResourcesSnapshot> KisResourcesSnapshotSP;
#endif /* __KIS_RESOURCES_SNAPSHOT_H */
diff --git a/libs/ui/tool/kis_smoothing_options.cpp b/libs/ui/tool/kis_smoothing_options.cpp
index 4c675c70fe..bc9fa62029 100644
--- a/libs/ui/tool/kis_smoothing_options.cpp
+++ b/libs/ui/tool/kis_smoothing_options.cpp
@@ -1,142 +1,142 @@
/*
* 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 "kis_smoothing_options.h"
#include "kis_config.h"
-KisSmoothingOptions::KisSmoothingOptions()
+KisSmoothingOptions::KisSmoothingOptions(bool useSavedSmoothing)
{
KisConfig cfg;
- m_smoothingType = (SmoothingType)cfg.lineSmoothingType();
- m_smoothnessDistance = cfg.lineSmoothingDistance();
- m_tailAggressiveness = cfg.lineSmoothingTailAggressiveness();
- m_smoothPressure = cfg.lineSmoothingSmoothPressure();
- m_useScalableDistance = cfg.lineSmoothingScalableDistance();
- m_delayDistance = cfg.lineSmoothingDelayDistance();
- m_useDelayDistance = cfg.lineSmoothingUseDelayDistance();
- m_finishStabilizedCurve = cfg.lineSmoothingFinishStabilizedCurve();
- m_stabilizeSensors = cfg.lineSmoothingStabilizeSensors();
+ m_smoothingType = (SmoothingType)cfg.lineSmoothingType(!useSavedSmoothing);
+ m_smoothnessDistance = cfg.lineSmoothingDistance(!useSavedSmoothing);
+ m_tailAggressiveness = cfg.lineSmoothingTailAggressiveness(!useSavedSmoothing);
+ m_smoothPressure = cfg.lineSmoothingSmoothPressure(!useSavedSmoothing);
+ m_useScalableDistance = cfg.lineSmoothingScalableDistance(!useSavedSmoothing);
+ m_delayDistance = cfg.lineSmoothingDelayDistance(!useSavedSmoothing);
+ m_useDelayDistance = cfg.lineSmoothingUseDelayDistance(!useSavedSmoothing);
+ m_finishStabilizedCurve = cfg.lineSmoothingFinishStabilizedCurve(!useSavedSmoothing);
+ m_stabilizeSensors = cfg.lineSmoothingStabilizeSensors(!useSavedSmoothing);
}
KisSmoothingOptions::SmoothingType KisSmoothingOptions::smoothingType() const
{
return m_smoothingType;
}
void KisSmoothingOptions::setSmoothingType(KisSmoothingOptions::SmoothingType value)
{
KisConfig cfg;
cfg.setLineSmoothingType(value);
m_smoothingType = value;
}
qreal KisSmoothingOptions::smoothnessDistance() const
{
return m_smoothnessDistance;
}
void KisSmoothingOptions::setSmoothnessDistance(qreal value)
{
KisConfig cfg;
cfg.setLineSmoothingDistance(value);
m_smoothnessDistance = value;
}
qreal KisSmoothingOptions::tailAggressiveness() const
{
return m_tailAggressiveness;
}
void KisSmoothingOptions::setTailAggressiveness(qreal value)
{
KisConfig cfg;
cfg.setLineSmoothingTailAggressiveness(value);
m_tailAggressiveness = value;
}
bool KisSmoothingOptions::smoothPressure() const
{
return m_smoothPressure;
}
void KisSmoothingOptions::setSmoothPressure(bool value)
{
KisConfig cfg;
cfg.setLineSmoothingSmoothPressure(value);
m_smoothPressure = value;
}
bool KisSmoothingOptions::useScalableDistance() const
{
return m_useScalableDistance;
}
void KisSmoothingOptions::setUseScalableDistance(bool value)
{
KisConfig cfg;
cfg.setLineSmoothingScalableDistance(value);
m_useScalableDistance = value;
}
qreal KisSmoothingOptions::delayDistance() const
{
return m_delayDistance;
}
void KisSmoothingOptions::setDelayDistance(qreal value)
{
KisConfig cfg;
cfg.setLineSmoothingDelayDistance(value);
m_delayDistance = value;
}
bool KisSmoothingOptions::useDelayDistance() const
{
return m_useDelayDistance;
}
void KisSmoothingOptions::setUseDelayDistance(bool value)
{
KisConfig cfg;
cfg.setLineSmoothingUseDelayDistance(value);
m_useDelayDistance = value;
}
void KisSmoothingOptions::setFinishStabilizedCurve(bool value)
{
KisConfig cfg;
cfg.setLineSmoothingFinishStabilizedCurve(value);
m_finishStabilizedCurve = value;
}
bool KisSmoothingOptions::finishStabilizedCurve() const
{
return m_finishStabilizedCurve;
}
void KisSmoothingOptions::setStabilizeSensors(bool value)
{
KisConfig cfg;
cfg.setLineSmoothingStabilizeSensors(value);
m_stabilizeSensors = value;
}
bool KisSmoothingOptions::stabilizeSensors() const
{
return m_stabilizeSensors;
}
diff --git a/libs/ui/tool/kis_smoothing_options.h b/libs/ui/tool/kis_smoothing_options.h
index 66b597f278..c513665b48 100644
--- a/libs/ui/tool/kis_smoothing_options.h
+++ b/libs/ui/tool/kis_smoothing_options.h
@@ -1,82 +1,82 @@
/*
* 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.
*/
#ifndef KIS_SMOOTHING_OPTIONS_H
#define KIS_SMOOTHING_OPTIONS_H
#include <qglobal.h>
#include <QSharedPointer>
#include <kritaui_export.h>
class KRITAUI_EXPORT KisSmoothingOptions
{
public:
enum SmoothingType {
NO_SMOOTHING = 0,
SIMPLE_SMOOTHING,
WEIGHTED_SMOOTHING,
STABILIZER
};
public:
- KisSmoothingOptions();
+ KisSmoothingOptions(bool useSavedSmoothing = true);
SmoothingType smoothingType() const;
void setSmoothingType(SmoothingType value);
qreal smoothnessDistance() const;
void setSmoothnessDistance(qreal value);
qreal tailAggressiveness() const;
void setTailAggressiveness(qreal value);
bool smoothPressure() const;
void setSmoothPressure(bool value);
bool useScalableDistance() const;
void setUseScalableDistance(bool value);
qreal delayDistance() const;
void setDelayDistance(qreal value);
void setUseDelayDistance(bool value);
bool useDelayDistance() const;
void setFinishStabilizedCurve(bool value);
bool finishStabilizedCurve() const;
void setStabilizeSensors(bool value);
bool stabilizeSensors() const;
private:
SmoothingType m_smoothingType;
qreal m_smoothnessDistance;
qreal m_tailAggressiveness;
bool m_smoothPressure;
bool m_useScalableDistance;
qreal m_delayDistance;
bool m_useDelayDistance;
bool m_finishStabilizedCurve;
bool m_stabilizeSensors;
};
typedef QSharedPointer<KisSmoothingOptions> KisSmoothingOptionsSP;
#endif // KIS_SMOOTHING_OPTIONS_H
diff --git a/libs/ui/tool/kis_tool.cc b/libs/ui/tool/kis_tool.cc
index 1369151f78..45b3814c78 100644
--- a/libs/ui/tool/kis_tool.cc
+++ b/libs/ui/tool/kis_tool.cc
@@ -1,694 +1,694 @@
/*
* 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 <QCursor>
#include <QLabel>
#include <QWidget>
#include <QPolygonF>
#include <QTransform>
#include <klocalizedstring.h>
#include <QAction>
#include <kactioncollection.h>
#include <kis_icon.h>
#include <KoConfig.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 <resources/KoAbstractGradient.h>
#include <KoSnapGuide.h>
#include <KisViewManager.h>
#include "kis_node_manager.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 <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include <resources/KoPattern.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>
#include "kis_action_registry.h"
#include "kis_tool_utils.h"
struct Q_DECL_HIDDEN KisTool::Private {
QCursor cursor; // the cursor that should be shown on tool activation.
// From the canvas resources
KoPattern* currentPattern{0};
KoAbstractGradient* currentGradient{0};
KoColor currentFgColor;
KoColor currentBgColor;
float currentExposure{1.0};
KisFilterConfigurationSP currentGenerator;
QWidget* optionWidget{0};
ToolMode m_mode{HOVER_MODE};
bool m_isActive{false};
};
KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor)
: KoToolBase(canvas)
, d(new Private)
{
d->cursor = cursor;
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")) {
QAction *toggleFgBg = KisActionRegistry::instance()->makeQAction("toggle_fg_bg", collection);
collection->addAction("toggle_fg_bg", toggleFgBg);
}
if (!collection->action("reset_fg_bg")) {
QAction *toggleFgBg = KisActionRegistry::instance()->makeQAction("reset_fg_bg", collection);
collection->addAction("reset_fg_bg", toggleFgBg);
}
addAction("toggle_fg_bg", dynamic_cast<QAction *>(collection->action("toggle_fg_bg")));
addAction("reset_fg_bg", dynamic_cast<QAction *>(collection->action("reset_fg_bg")));
}
KisTool::~KisTool()
{
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);
d->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) {
warnKrita << "WARNING: KisTool::deactivate() failed to disconnect"
<< "some signal connections. Your actions might be executed twice!";
}
d->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);
}
QPointF KisTool::convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset, bool useModifiers)
{
if (!image())
return e->point;
KoSnapGuide *snapGuide = canvas()->snapGuide();
QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier);
return image()->documentToPixel(pos);
}
QPointF KisTool::convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset)
{
if (!image())
return pt;
KoSnapGuide *snapGuide = canvas()->snapGuide();
QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier);
return image()->documentToPixel(pos);
}
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() const
{
KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value<KisNodeWSP>();
return node;
}
KisNodeList KisTool::selectedNodes() const
{
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
return viewManager->nodeManager()->selectedNodes();
}
KoColor KisTool::currentFgColor()
{
return d->currentFgColor;
}
KoColor KisTool::currentBgColor()
{
return d->currentBgColor;
}
KisImageWSP KisTool::currentImage()
{
return image();
}
KisFilterConfigurationSP KisTool::currentGenerator()
{
return d->currentGenerator;
}
void KisTool::setMode(ToolMode mode) {
d->m_mode = mode;
}
KisTool::ToolMode KisTool::mode() const {
return d->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());
+ new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
if (!KisToolUtils::clearImage(image(), resources->currentNode(), resources->activeSelection())) {
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)
{
KisOpenGLCanvas2 *canvasWidget = dynamic_cast<KisOpenGLCanvas2 *>(canvas()->canvasWidget());
if (canvasWidget) {
painter->beginNativePainting();
canvasWidget->paintToolOutline(path);
painter->endNativePainting();
}
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 d->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;
}
bool nodeEditable = node->isEditable();
if (!nodeEditable) {
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, KisIconUtils::loadIcon("object-locked"));
}
return nodeEditable;
}
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."), KisIconUtils::loadIcon("object-locked"));
}
return editable;
}
void KisTool::listenToModifiers(bool listen)
{
Q_UNUSED(listen);
}
bool KisTool::listeningToModifiers()
{
return false;
}
diff --git a/libs/ui/tool/kis_tool_freehand.cc b/libs/ui/tool/kis_tool_freehand.cc
index c1968c9948..82410ca9ce 100644
--- a/libs/ui/tool/kis_tool_freehand.cc
+++ b/libs/ui/tool/kis_tool_freehand.cc
@@ -1,460 +1,459 @@
/*
* kis_tool_freehand.cc - part of Krita
*
* Copyright (c) 2003-2007 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
* Copyright (c) 2007,2008,2010 Cyrille Berger <cberger@cberger.net>
* 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_tool_freehand.h"
#include <QPainter>
#include <QRect>
#include <QThreadPool>
#include <QApplication>
#include <QDesktopWidget>
#include <kis_icon.h>
#include <KoPointerEvent.h>
#include <KoViewConverter.h>
#include <KoCanvasController.h>
//pop up palette
#include <kis_canvas_resource_provider.h>
// Krita/image
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <kis_painter.h>
#include <brushengine/kis_paintop.h>
#include <kis_selection.h>
#include <brushengine/kis_paintop_preset.h>
// Krita/ui
#include "kis_abstract_perspective_grid.h"
#include "kis_config.h"
#include "canvas/kis_canvas2.h"
#include "kis_cursor.h"
#include <KisViewManager.h>
#include <kis_painting_assistants_decoration.h>
#include "kis_painting_information_builder.h"
#include "kis_tool_freehand_helper.h"
#include "kis_recording_adapter.h"
#include "strokes/freehand_stroke.h"
using namespace std::placeholders; // For _1 placeholder
KisToolFreehand::KisToolFreehand(KoCanvasBase * canvas, const QCursor & cursor, const KUndo2MagicString &transactionText)
: KisToolPaint(canvas, cursor),
m_paintopBasedPickingInAction(false),
m_brushResizeCompressor(200, std::bind(&KisToolFreehand::slotDoResizeBrush, this, _1))
{
m_assistant = false;
m_magnetism = 1.0;
m_only_one_assistant = true;
setSupportOutline(true);
setMaskSyntheticEvents(true); // Disallow mouse events from finger presses.
m_infoBuilder = new KisToolFreehandPaintingInformationBuilder(this);
m_recordingAdapter = new KisRecordingAdapter();
m_helper = new KisToolFreehandHelper(m_infoBuilder, transactionText, m_recordingAdapter);
connect(m_helper, SIGNAL(requestExplicitUpdateOutline()),
SLOT(explicitUpdateOutline()));
}
KisToolFreehand::~KisToolFreehand()
{
delete m_helper;
delete m_recordingAdapter;
delete m_infoBuilder;
}
KisSmoothingOptionsSP KisToolFreehand::smoothingOptions() const
{
return m_helper->smoothingOptions();
}
void KisToolFreehand::resetCursorStyle()
{
KisConfig cfg;
switch (cfg.newCursorStyle()) {
case CURSOR_STYLE_NO_CURSOR:
useCursor(KisCursor::blankCursor());
break;
case CURSOR_STYLE_POINTER:
useCursor(KisCursor::arrowCursor());
break;
case CURSOR_STYLE_SMALL_ROUND:
useCursor(KisCursor::roundCursor());
break;
case CURSOR_STYLE_CROSSHAIR:
useCursor(KisCursor::crossCursor());
break;
case CURSOR_STYLE_TRIANGLE_RIGHTHANDED:
useCursor(KisCursor::triangleRightHandedCursor());
break;
case CURSOR_STYLE_TRIANGLE_LEFTHANDED:
useCursor(KisCursor::triangleLeftHandedCursor());
break;
case CURSOR_STYLE_BLACK_PIXEL:
useCursor(KisCursor::pixelBlackCursor());
break;
case CURSOR_STYLE_WHITE_PIXEL:
useCursor(KisCursor::pixelWhiteCursor());
break;
case CURSOR_STYLE_TOOLICON:
default:
KisToolPaint::resetCursorStyle();
break;
}
}
KisPaintingInformationBuilder* KisToolFreehand::paintingInformationBuilder() const
{
return m_infoBuilder;
}
KisRecordingAdapter* KisToolFreehand::recordingAdapter() const
{
return m_recordingAdapter;
}
void KisToolFreehand::resetHelper(KisToolFreehandHelper *helper)
{
delete m_helper;
m_helper = helper;
}
int KisToolFreehand::flags() const
{
return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP|KisTool::FLAG_USES_CUSTOM_PRESET;
}
void KisToolFreehand::activate(ToolActivation activation, const QSet<KoShape*> &shapes)
{
KisToolPaint::activate(activation, shapes);
}
void KisToolFreehand::deactivate()
{
if (mode() == PAINT_MODE) {
endStroke();
setMode(KisTool::HOVER_MODE);
}
KisToolPaint::deactivate();
}
void KisToolFreehand::initStroke(KoPointerEvent *event)
{
setCurrentNodeLocked(true);
m_helper->initPaint(event, canvas()->resourceManager(),
image(),
currentNode(),
- image().data(),
- image()->postExecutionUndoAdapter());
+ image().data());
}
void KisToolFreehand::doStroke(KoPointerEvent *event)
{
//set canvas information here?//
KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2 *>(canvas());
if (canvas2) {
m_helper->setCanvasHorizontalMirrorState(canvas2->xAxisMirrored());
m_helper->setCanvasRotation(canvas2->rotationAngle());
}
m_helper->paint(event);
}
void KisToolFreehand::endStroke()
{
m_helper->endPaint();
setCurrentNodeLocked(false);
}
bool KisToolFreehand::primaryActionSupportsHiResEvents() const
{
return true;
}
void KisToolFreehand::beginPrimaryAction(KoPointerEvent *event)
{
// FIXME: workaround for the Duplicate Op
tryPickByPaintOp(event, PickFgImage);
requestUpdateOutline(event->point, event);
NodePaintAbility paintability = nodePaintAbility();
if (!nodeEditable() || paintability != PAINT) {
if(paintability == KisToolPaint::VECTOR){
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
QString message = i18n("The brush tool cannot paint on this layer. Please select a paint layer or mask.");
kiscanvas->viewManager()->showFloatingMessage(message, koIcon("object-locked"));
}
event->ignore();
return;
}
setMode(KisTool::PAINT_MODE);
KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2 *>(canvas());
if (canvas2) {
canvas2->viewManager()->disableControls();
}
initStroke(event);
}
void KisToolFreehand::continuePrimaryAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
requestUpdateOutline(event->point, event);
/**
* Actual painting
*/
doStroke(event);
}
void KisToolFreehand::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
endStroke();
if (m_assistant && static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()) {
static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()->endStroke();
}
notifyModified();
KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2 *>(canvas());
if (canvas2) {
canvas2->viewManager()->enableControls();
}
setMode(KisTool::HOVER_MODE);
}
bool KisToolFreehand::tryPickByPaintOp(KoPointerEvent *event, AlternateAction action)
{
if (action != PickFgNode && action != PickFgImage) return false;
/**
* FIXME: we need some better way to implement modifiers
* for a paintop level. This method is used in DuplicateOp only!
*/
QPointF pos = adjustPosition(event->point, event->point);
qreal perspective = 1.0;
Q_FOREACH (const QPointer<KisAbstractPerspectiveGrid> grid, static_cast<KisCanvas2*>(canvas())->viewManager()->resourceProvider()->perspectiveGrids()) {
if (grid && grid->contains(pos)) {
perspective = grid->distance(pos);
break;
}
}
if (!currentPaintOpPreset()) {
return false;
}
bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()->
mousePressEvent(KisPaintInformation(convertToPixelCoord(event->point),
pressureToCurve(event->pressure()),
event->xTilt(), event->yTilt(),
event->rotation(),
event->tangentialPressure(),
perspective, 0, 0),
event->modifiers(),
currentNode());
return !paintOpIgnoredEvent;
}
void KisToolFreehand::activateAlternateAction(AlternateAction action)
{
if (action != ChangeSize) {
KisToolPaint::activateAlternateAction(action);
return;
}
useCursor(KisCursor::blankCursor());
setOutlineEnabled(true);
}
void KisToolFreehand::deactivateAlternateAction(AlternateAction action)
{
if (action != ChangeSize) {
KisToolPaint::deactivateAlternateAction(action);
return;
}
resetCursorStyle();
setOutlineEnabled(false);
}
void KisToolFreehand::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (tryPickByPaintOp(event, action)) {
m_paintopBasedPickingInAction = true;
return;
}
if (action != ChangeSize) {
KisToolPaint::beginAlternateAction(event, action);
return;
}
setMode(GESTURE_MODE);
m_initialGestureDocPoint = event->point;
m_initialGestureGlobalPoint = QCursor::pos();
m_lastDocumentPoint = event->point;
m_lastPaintOpSize = currentPaintOpPreset()->settings()->paintOpSize();
}
void KisToolFreehand::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (tryPickByPaintOp(event, action) || m_paintopBasedPickingInAction) return;
if (action != ChangeSize) {
KisToolPaint::continueAlternateAction(event, action);
return;
}
QPointF lastWidgetPosition = convertDocumentToWidget(m_lastDocumentPoint);
QPointF actualWidgetPosition = convertDocumentToWidget(event->point);
QPointF offset = actualWidgetPosition - lastWidgetPosition;
KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2 *>(canvas());
QRect screenRect = QApplication::desktop()->screenGeometry();
qreal scaleX = 0;
qreal scaleY = 0;
canvas2->coordinatesConverter()->imageScale(&scaleX, &scaleY);
// we have no centralized knowledge of the maximum brush size!
const qreal maxBrushSize = 1000.0;
const qreal effectiveMaxDragSize = 0.5 * screenRect.width();
const qreal effectiveMaxBrushSize = qMin(maxBrushSize, effectiveMaxDragSize / scaleX);
const qreal scaleCoeff = effectiveMaxBrushSize / effectiveMaxDragSize;
const qreal sizeDiff = scaleCoeff * offset.x() ;
if (qAbs(sizeDiff) > 0.01) {
KisPaintOpSettingsSP settings = currentPaintOpPreset()->settings();
const qreal newSize = qBound(0.01, m_lastPaintOpSize + sizeDiff, maxBrushSize);
settings->setPaintOpSize(newSize);
requestUpdateOutline(m_initialGestureDocPoint, 0);
//m_brushResizeCompressor.start(newSize);
m_lastDocumentPoint = event->point;
m_lastPaintOpSize = newSize;
}
}
void KisToolFreehand::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (tryPickByPaintOp(event, action) || m_paintopBasedPickingInAction) {
m_paintopBasedPickingInAction = false;
return;
}
if (action != ChangeSize) {
KisToolPaint::endAlternateAction(event, action);
return;
}
QCursor::setPos(m_initialGestureGlobalPoint);
requestUpdateOutline(m_initialGestureDocPoint, 0);
setMode(HOVER_MODE);
}
bool KisToolFreehand::wantsAutoScroll() const
{
return false;
}
void KisToolFreehand::setAssistant(bool assistant)
{
m_assistant = assistant;
}
void KisToolFreehand::setOnlyOneAssistantSnap(bool assistant)
{
m_only_one_assistant = assistant;
}
void KisToolFreehand::slotDoResizeBrush(qreal newSize)
{
KisPaintOpSettingsSP settings = currentPaintOpPreset()->settings();
settings->setPaintOpSize(newSize);
requestUpdateOutline(m_initialGestureDocPoint, 0);
}
QPointF KisToolFreehand::adjustPosition(const QPointF& point, const QPointF& strokeBegin)
{
if (m_assistant && static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()) {
static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()->setOnlyOneAssistantSnap(m_only_one_assistant);
QPointF ap = static_cast<KisCanvas2*>(canvas())->paintingAssistantsDecoration()->adjustPosition(point, strokeBegin);
return (1.0 - m_magnetism) * point + m_magnetism * ap;
}
return point;
}
qreal KisToolFreehand::calculatePerspective(const QPointF &documentPoint)
{
qreal perspective = 1.0;
Q_FOREACH (const QPointer<KisAbstractPerspectiveGrid> grid, static_cast<KisCanvas2*>(canvas())->viewManager()->resourceProvider()->perspectiveGrids()) {
if (grid && grid->contains(documentPoint)) {
perspective = grid->distance(documentPoint);
break;
}
}
return perspective;
}
void KisToolFreehand::explicitUpdateOutline()
{
requestUpdateOutline(m_outlineDocPoint, 0);
}
QPainterPath KisToolFreehand::getOutlinePath(const QPointF &documentPos,
const KoPointerEvent *event,
KisPaintOpSettings::OutlineMode outlineMode)
{
QPointF imagePos = currentImage()->documentToPixel(documentPos);
if (currentPaintOpPreset())
return m_helper->paintOpOutline(imagePos,
event,
currentPaintOpPreset()->settings(),
outlineMode);
else
return QPainterPath();
}
diff --git a/libs/ui/tool/kis_tool_freehand_helper.cpp b/libs/ui/tool/kis_tool_freehand_helper.cpp
index 41b8b70e89..ac1749dfc5 100644
--- a/libs/ui/tool/kis_tool_freehand_helper.cpp
+++ b/libs/ui/tool/kis_tool_freehand_helper.cpp
@@ -1,855 +1,853 @@
/*
* 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 <klocalizedstring.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 <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_utils.h>
#include "kis_update_time_monitor.h"
#include "kis_stabilized_events_sampler.h"
#include "kis_config.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;
QTimer stabilizerPollTimer;
KisStabilizedEventsSampler stabilizedSampler;
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)
+ KisRecordingAdapter *recordingAdapter,
+ KisSmoothingOptions *smoothingOptions)
: m_d(new Private())
{
m_d->infoBuilder = infoBuilder;
m_d->recordingAdapter = recordingAdapter;
m_d->transactionText = transactionText;
- m_d->smoothingOptions = KisSmoothingOptionsSP(new KisSmoothingOptions());
+ m_d->smoothingOptions = KisSmoothingOptionsSP(
+ smoothingOptions ? smoothingOptions : 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 KisPaintOpSettingsSP globalSettings,
KisPaintOpSettings::OutlineMode mode) const
{
KisPaintOpSettingsSP 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;
/**
* When LoD mode is active it may happen that the helper has
* already started a stroke, but it painted noting, because
* all the work is being calculated by the scaled-down LodN
* stroke. So at first we try to fetch the data from the lodN
* stroke ("buddy") and then check if there is at least
* something has been painted with this distance information
* object.
*/
KisDistanceInformation *buddyDistance =
m_d->painterInfos.first()->buddyDragDistance();
if (buddyDistance) {
/**
* Tiny hack alert: here we fetch the distance information
* directly from the LodN stroke. Ideally, we should
* upscale its data, but here we just override it with our
* local copy of the coordinates.
*/
distanceInfo = *buddyDistance;
distanceInfo.overrideLastValues(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0);
} else if (m_d->painterInfos.first()->dragDistance->isStarted()) {
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(), resourceManager);
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());
// 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;
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->stabilizedSampler.addEvent(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);
}
// Poll and draw regularly
KisConfig cfg;
m_d->stabilizerPollTimer.setInterval(cfg.stabilizerSampleSize());
m_d->stabilizerPollTimer.start();
m_d->stabilizedSampler.clear();
m_d->stabilizedSampler.addEvent(firstPaintInfo);
}
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()
{
KisStabilizedEventsSampler::iterator it;
KisStabilizedEventsSampler::iterator end;
std::tie(it, end) = m_d->stabilizedSampler.range();
for (; it != end; ++it) {
KisPaintInformation sampledInfo = *it;
bool canPaint = true;
if (m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos();
qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
canPaint = dx > R;
}
if (canPaint) {
KisPaintInformation newInfo =
m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, sampledInfo);
paintLine(m_d->previousPaintInformation, newInfo);
m_d->previousPaintInformation = newInfo;
// Push the new entry through the queue
m_d->stabilizerDeque.dequeue();
m_d->stabilizerDeque.enqueue(sampledInfo);
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;
}
}
}
m_d->stabilizedSampler.clear();
}
void KisToolFreehandHelper::stabilizerEnd()
{
// Stop the timer
m_d->stabilizerPollTimer.stop();
// Finish the line
if (m_d->smoothingOptions->finishStabilizedCurve()) {
// 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.
m_d->stabilizedSampler.addFinishingEvent(m_d->stabilizerDeque.size());
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(int painterInfoId,
const KisPaintInformation &pi)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
painterInfoId, pi));
if(m_d->recordingAdapter) {
m_d->recordingAdapter->addPoint(pi);
}
}
void KisToolFreehandHelper::paintLine(int painterInfoId,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
painterInfoId, pi1, pi2));
if(m_d->recordingAdapter) {
m_d->recordingAdapter->addLine(pi1, pi2);
}
}
void KisToolFreehandHelper::paintBezierCurve(int painterInfoId,
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(),
painterInfoId,
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(lastPosition, lastTime);
}
void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi)
{
paintAt(0, pi);
}
void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
paintLine(0, pi1, pi2);
}
void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
paintBezierCurve(0, 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/libs/ui/tool/kis_tool_freehand_helper.h b/libs/ui/tool/kis_tool_freehand_helper.h
index cd3ae67678..b08c8d4869 100644
--- a/libs/ui/tool/kis_tool_freehand_helper.h
+++ b/libs/ui/tool/kis_tool_freehand_helper.h
@@ -1,153 +1,152 @@
/*
* 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_FREEHAND_HELPER_H
#define __KIS_TOOL_FREEHAND_HELPER_H
#include <QObject>
#include "kis_types.h"
#include "kritaui_export.h"
#include <brushengine/kis_paint_information.h>
#include "kis_default_bounds.h"
#include <brushengine/kis_paintop_settings.h>
#include "kis_smoothing_options.h"
#include "strokes/freehand_stroke.h"
class KoPointerEvent;
class KoCanvasResourceManager;
class KisPaintingInformationBuilder;
class KisRecordingAdapter;
class KisStrokesFacade;
class KisPostExecutionUndoAdapter;
class KisPaintOp;
class KRITAUI_EXPORT KisToolFreehandHelper : public QObject
{
Q_OBJECT
protected:
typedef FreehandStrokeStrategy::PainterInfo PainterInfo;
public:
KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText = KUndo2MagicString(),
- KisRecordingAdapter *recordingAdapter = 0);
+ KisRecordingAdapter *recordingAdapter = 0,
+ KisSmoothingOptions *smoothingOptions = 0);
~KisToolFreehandHelper();
void setSmoothness(KisSmoothingOptionsSP smoothingOptions);
KisSmoothingOptionsSP smoothingOptions() const;
bool isRunning() const;
void initPaint(KoPointerEvent *event,
KoCanvasResourceManager *resourceManager,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
- KisPostExecutionUndoAdapter *undoAdapter,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0);
void paint(KoPointerEvent *event);
void endPaint();
const KisPaintOp* currentPaintOp() const;
QPainterPath paintOpOutline(const QPointF &savedCursorPos,
const KoPointerEvent *event,
const KisPaintOpSettingsSP globalSettings,
KisPaintOpSettings::OutlineMode mode) const;
int canvasRotation();
void setCanvasRotation(int rotation = 0);
bool canvasMirroredH();
void setCanvasHorizontalMirrorState (bool mirrored = false);
Q_SIGNALS:
/**
* The signal is emitted when the outline should be updated
* explicitly by the tool. Used by Stabilizer option, because it
* paints on internal timer events instead of the on every paint()
* event
*/
void requestExplicitUpdateOutline();
protected:
void cancelPaint();
int elapsedStrokeTime() const;
void initPaintImpl(const KisPaintInformation &previousPaintInformation,
KoCanvasResourceManager *resourceManager,
KisImageWSP image,
KisNodeSP node,
KisStrokesFacade *strokesFacade,
- KisPostExecutionUndoAdapter *undoAdapter,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0);
protected:
virtual void createPainters(QVector<PainterInfo*> &painterInfos,
const QPointF &lastPosition,
int lastTime);
// lo-level methods for painting primitives
void paintAt(int painterInfoId, const KisPaintInformation &pi);
void paintLine(int painterInfoId,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2);
void paintBezierCurve(int painterInfoId,
const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2);
// hi-level methods for painting primitives
virtual void paintAt(const KisPaintInformation &pi);
virtual void paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2);
virtual void paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2);
private:
void paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
QPointF tangent1, QPointF tangent2);
void stabilizerStart(KisPaintInformation firstPaintInfo);
void stabilizerEnd();
private Q_SLOTS:
void finishStroke();
void doAirbrushing();
void stabilizerPollAndPaint();
private:
struct Private;
Private * const m_d;
};
#endif /* __KIS_TOOL_FREEHAND_HELPER_H */
diff --git a/libs/ui/tool/kis_tool_paint.cc b/libs/ui/tool/kis_tool_paint.cc
index 27147a937c..d5d4bfb8f7 100644
--- a/libs/ui/tool/kis_tool_paint.cc
+++ b/libs/ui/tool/kis_tool_paint.cc
@@ -1,772 +1,772 @@
/*
* 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 <kis_debug.h>
#include <QPoint>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <kactioncollection.h>
#include <QAction>
#include <kis_icon.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 <brushengine/kis_paintop.h>
#include <brushengine/kis_paintop_preset.h>
#include <kis_action_manager.h>
#include <kis_action.h>
#include "strokes/kis_color_picker_stroke_strategy.h"
KisToolPaint::KisToolPaint(KoCanvasBase * canvas, const QCursor & cursor)
: KisTool(canvas, cursor),
m_showColorPreview(false),
m_colorPreviewShowComparePlate(false),
m_colorPickerDelayTimer(),
m_isOutlineEnabled(true)
{
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);
}
KisCanvas2 * kiscanvas = dynamic_cast<KisCanvas2*>(canvas);
KisActionManager *actionManager = kiscanvas->viewManager()->actionManager();
// XXX: Perhaps a better place for these?
if (!actionManager->actionByName("increase_brush_size")) {
KisAction *increaseBrushSize = new KisAction(i18n("Increase Brush Size"));
increaseBrushSize->setShortcut(Qt::Key_BracketRight);
actionManager->addAction("increase_brush_size", increaseBrushSize);
}
if (!actionManager->actionByName("decrease_brush_size")) {
KisAction *decreaseBrushSize = new KisAction(i18n("Decrease Brush Size"));
decreaseBrushSize->setShortcut(Qt::Key_BracketLeft);
actionManager->addAction("decrease_brush_size", decreaseBrushSize);
}
addAction("increase_brush_size", dynamic_cast<QAction *>(actionManager->actionByName("increase_brush_size")));
addAction("decrease_brush_size", dynamic_cast<QAction *>(actionManager->actionByName("decrease_brush_size")));
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()));
using namespace std::placeholders; // For _1 placeholder
std::function<void(PickingJob)> callback =
std::bind(&KisToolPaint::addPickerJob, this, _1);
m_colorPickingCompressor.reset(
new PickingCompressor(100, callback, KisSignalCompressor::FIRST_ACTIVE));
}
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)
{
if (currentPaintOpPreset()) emit statusTextChanged(currentPaintOpPreset()->name());
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();
}
bool KisToolPaint::isPickingAction(AlternateAction action) {
return action == PickFgNode ||
action == PickBgNode ||
action == PickFgImage ||
action == PickBgImage;
}
void KisToolPaint::deactivateAlternateAction(AlternateAction action)
{
if (!isPickingAction(action)) {
KisTool::deactivateAlternateAction(action);
return;
}
delayedAction = KisTool::NONE;
m_colorPickerDelayTimer.stop();
resetCursorStyle();
deactivatePickColor(action);
}
void KisToolPaint::addPickerJob(const PickingJob &pickingJob)
{
/**
* The actual picking is delayed by a compressor, so we can get this
* event when the stroke is already closed
*/
if (!m_pickerStrokeId) return;
KIS_ASSERT_RECOVER_RETURN(isPickingAction(pickingJob.action));
const QPoint imagePoint = image()->documentToIntPixel(pickingJob.documentPixel);
const bool fromCurrentNode = pickingJob.action == PickFgNode || pickingJob.action == PickBgNode;
m_pickingResource = colorPreviewResourceId(pickingJob.action);
KisPaintDeviceSP device = fromCurrentNode ?
currentNode()->projection() : image()->projection();
image()->addJob(m_pickerStrokeId,
new KisColorPickerStrokeStrategy::Data(device, imagePoint));
}
void KisToolPaint::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (isPickingAction(action)) {
KIS_ASSERT_RECOVER_RETURN(!m_pickerStrokeId);
setMode(SECONDARY_PAINT_MODE);
KisColorPickerStrokeStrategy *strategy = new KisColorPickerStrokeStrategy();
connect(strategy, &KisColorPickerStrokeStrategy::sigColorUpdated,
this, &KisToolPaint::slotColorPickingFinished);
m_pickerStrokeId = image()->startStroke(strategy);
m_colorPickingCompressor->start(PickingJob(event->point, action));
requestUpdateOutline(event->point, event);
} else {
KisTool::beginAlternateAction(event, action);
}
}
void KisToolPaint::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (isPickingAction(action)) {
KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId);
m_colorPickingCompressor->start(PickingJob(event->point, action));
requestUpdateOutline(event->point, event);
} else {
KisTool::continueAlternateAction(event, action);
}
}
void KisToolPaint::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (isPickingAction(action)) {
KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId);
image()->endStroke(m_pickerStrokeId);
m_pickerStrokeId.clear();
requestUpdateOutline(event->point, event);
setMode(HOVER_MODE);
} 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;
}
void KisToolPaint::slotColorPickingFinished(const KoColor &color)
{
canvas()->resourceManager()->setResource(m_pickingResource, color);
if (!m_showColorPreview) return;
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_ASSERT_RECOVER_RETURN(kisCanvas);
QColor previewColor = kisCanvas->displayColorConverter()->toQColor(color);
m_colorPreviewShowComparePlate = true;
m_colorPreviewCurrentColor = previewColor;
requestUpdateOutline(m_outlineDocPoint, 0);
}
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);
+ verticalLayout->setSpacing(5);
// 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);
+ m_optionsWidgetLayout->setSpacing(5);
if (!quickHelp().isEmpty()) {
QPushButton* push = new QPushButton(KisIconUtils::loadIcon("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();
std::vector<int>::iterator result =
std::upper_bound(m_standardBrushSizes.begin(),
m_standardBrushSizes.end(),
qRound(paintopSize));
int newValue = result != m_standardBrushSizes.end() ? *result : m_standardBrushSizes.back();
currentPaintOpPreset()->settings()->setPaintOpSize(newValue);
requestUpdateOutline(m_outlineDocPoint, 0);
}
void KisToolPaint::decreaseBrushSize()
{
qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize();
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();
currentPaintOpPreset()->settings()->setPaintOpSize(newValue);
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/libs/ui/tool/kis_tool_utils.cpp b/libs/ui/tool/kis_tool_utils.cpp
index 2346f662bd..7ce23fe20d 100644
--- a/libs/ui/tool/kis_tool_utils.cpp
+++ b/libs/ui/tool/kis_tool_utils.cpp
@@ -1,110 +1,189 @@
/*
* 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_tool_utils.h>
#include <KoColorSpace.h>
+#include <KoMixColorsOp.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_wrapped_rect.h>
#include <kis_image.h>
#include <kis_transaction.h>
-
+#include <kis_sequential_iterator.h>
+#include <kis_properties_configuration.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
namespace KisToolUtils {
- bool pick(KisPaintDeviceSP dev, const QPoint& pos, KoColor *color)
+ bool pick(KisPaintDeviceSP dev, const QPoint& pos, KoColor *color, int radius)
{
KIS_ASSERT(dev);
KoColor pickedColor;
- dev->pixel(pos.x(), pos.y(), &pickedColor);
+ if (radius <= 1) {
+ dev->pixel(pos.x(), pos.y(), &pickedColor);
+ } else {
+ const KoColorSpace* cs = dev->colorSpace();
+ pickedColor = KoColor(Qt::transparent, cs);
+
+ QVector<const quint8*> pixels;
+
+ const int effectiveRadius = radius - 1;
+
+ const QRect pickRect(pos.x() - effectiveRadius, pos.y() - effectiveRadius,
+ 2 * effectiveRadius + 1, 2 * effectiveRadius + 1);
+ KisSequentialConstIterator it(dev, pickRect);
+
+ const int radiusSq = pow2(effectiveRadius);
+
+ do {
+ const QPoint realPos(it.x(), it.y());
+ const QPoint pt = realPos - pos;
+ if (pow2(pt.x()) + pow2(pt.y()) < radiusSq) {
+ pixels << it.oldRawData();
+ }
+ } while (it.nextPixel());
+
+ const quint8** cpixels = const_cast<const quint8**>(pixels.constData());
+ cs->mixColorsOp()->mixColors(cpixels, pixels.size(), pickedColor.data());
+ }
+
pickedColor.convertTo(dev->compositionSourceColorSpace());
bool validColorPicked =
pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8;
if (validColorPicked) {
pickedColor.setOpacity(OPACITY_OPAQUE_U8);
*color = pickedColor;
}
return validColorPicked;
}
KisNodeSP findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly)
{
KisNodeSP foundNode = 0;
while (node) {
KisLayerSP layer = dynamic_cast<KisLayer*>(node.data());
if (!layer || !layer->isEditable()) {
node = node->prevSibling();
continue;
}
KoColor color(layer->projection()->colorSpace());
layer->projection()->pixel(point.x(), point.y(), &color);
KisGroupLayerSP group = dynamic_cast<KisGroupLayer*>(layer.data());
if ((group && group->passThroughMode()) || color.opacityU8() != OPACITY_TRANSPARENT_U8) {
if (layer->inherits("KisGroupLayer") && (!editableOnly || layer->isEditable())) {
// if this is a group and the pixel is transparent, don't even enter it
foundNode = findNode(node->lastChild(), point, wholeGroup, editableOnly);
}
else {
foundNode = !wholeGroup ? node : node->parent();
}
}
if (foundNode) break;
node = node->prevSibling();
}
return foundNode;
}
bool clearImage(KisImageSP image, KisNodeSP node, KisSelectionSP selection)
{
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();
return true;
}
return false;
}
+
+ const QString ColorPickerConfig::CONFIG_GROUP_NAME = "tool_color_picker";
+
+ ColorPickerConfig::ColorPickerConfig()
+ : toForegroundColor(true)
+ , updateColor(true)
+ , addPalette(false)
+ , normaliseValues(false)
+ , sampleMerged(true)
+ , radius(1)
+ {
+ }
+
+ inline QString getConfigKey(bool defaultActivation) {
+ return defaultActivation ?
+ "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation";
+ }
+
+ void ColorPickerConfig::save(bool defaultActivation) const
+ {
+ KisPropertiesConfiguration props;
+ props.setProperty("toForegroundColor", toForegroundColor);
+ props.setProperty("updateColor", updateColor);
+ props.setProperty("addPalette", addPalette);
+ props.setProperty("normaliseValues", normaliseValues);
+ props.setProperty("sampleMerged", sampleMerged);
+ props.setProperty("radius", radius);
+
+ KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME);
+
+ config.writeEntry(getConfigKey(defaultActivation), props.toXML());
+ }
+
+ void ColorPickerConfig::load(bool defaultActivation)
+ {
+ KisPropertiesConfiguration props;
+
+ KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME);
+ props.fromXML(config.readEntry(getConfigKey(defaultActivation)));
+
+ toForegroundColor = props.getBool("toForegroundColor", true);
+ updateColor = props.getBool("updateColor", true);
+ addPalette = props.getBool("addPalette", false);
+ normaliseValues = props.getBool("normaliseValues", false);
+ sampleMerged = props.getBool("sampleMerged", !defaultActivation ? false : true);
+ radius = props.getInt("radius", 1);
+ }
+
}
diff --git a/libs/ui/tool/kis_tool_utils.h b/libs/ui/tool/kis_tool_utils.h
index 9236f4f9dc..f78b2cf8ee 100644
--- a/libs/ui/tool/kis_tool_utils.h
+++ b/libs/ui/tool/kis_tool_utils.h
@@ -1,48 +1,64 @@
/*
* 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 KIS_TOOL_UTILS_H
#define KIS_TOOL_UTILS_H
#include <QPoint>
#include <KoColor.h>
#include <kis_types.h>
#include <kritaui_export.h>
#include <kis_selection.h>
namespace KisToolUtils {
+struct KRITAUI_EXPORT ColorPickerConfig {
+ ColorPickerConfig();
+
+ bool toForegroundColor;
+ bool updateColor;
+ bool addPalette;
+ bool normaliseValues;
+ bool sampleMerged;
+ int radius;
+
+ void save(bool defaultActivation = true) const;
+ void load(bool defaultActivation = true);
+private:
+ static const QString CONFIG_GROUP_NAME;
+};
+
/**
* return the color at the given position on the given paint device.
*/
-bool KRITAUI_EXPORT pick(KisPaintDeviceSP dev, const QPoint& pos, KoColor *color);
+bool KRITAUI_EXPORT pick(KisPaintDeviceSP dev, const QPoint& pos, KoColor *color, int radius = 1);
/**
* Recursively search a node with a non-transparent pixel
*/
KisNodeSP KRITAUI_EXPORT findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly = true);
/**
* return true if success
* Clears the image. Selection is optional, use 0 to clear everything.
*/
bool KRITAUI_EXPORT clearImage(KisImageSP image, KisNodeSP node, KisSelectionSP selection);
}
#endif // KIS_TOOL_UTILS_H
diff --git a/libs/ui/tool/strokes/freehand_stroke.cpp b/libs/ui/tool/strokes/freehand_stroke.cpp
index 186c517ec4..2ca96405bc 100644
--- a/libs/ui/tool/strokes/freehand_stroke.cpp
+++ b/libs/ui/tool/strokes/freehand_stroke.cpp
@@ -1,150 +1,149 @@
/*
* 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 "freehand_stroke.h"
#include "kis_canvas_resource_provider.h"
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include "kis_painter.h"
#include "kis_update_time_monitor.h"
#include <brushengine/kis_stroke_random_source.h>
struct FreehandStrokeStrategy::Private
{
Private(KisResourcesSnapshotSP _resources) : resources(_resources) {}
KisStrokeRandomSource randomSource;
KisResourcesSnapshotSP resources;
};
FreehandStrokeStrategy::FreehandStrokeStrategy(bool needsIndirectPainting,
const QString &indirectPaintingCompositeOp,
KisResourcesSnapshotSP resources,
PainterInfo *painterInfo,
const KUndo2MagicString &name)
: KisPainterBasedStrokeStrategy("FREEHAND_STROKE", name,
resources, painterInfo),
m_d(new Private(resources))
{
init(needsIndirectPainting, indirectPaintingCompositeOp);
}
FreehandStrokeStrategy::FreehandStrokeStrategy(bool needsIndirectPainting,
const QString &indirectPaintingCompositeOp,
KisResourcesSnapshotSP resources,
QVector<PainterInfo*> painterInfos,
const KUndo2MagicString &name)
: KisPainterBasedStrokeStrategy("FREEHAND_STROKE", name,
resources, painterInfos),
m_d(new Private(resources))
{
init(needsIndirectPainting, indirectPaintingCompositeOp);
}
FreehandStrokeStrategy::FreehandStrokeStrategy(const FreehandStrokeStrategy &rhs, int levelOfDetail)
: KisPainterBasedStrokeStrategy(rhs, levelOfDetail),
m_d(new Private(*rhs.m_d))
{
m_d->randomSource.setLevelOfDetail(levelOfDetail);
}
FreehandStrokeStrategy::~FreehandStrokeStrategy()
{
KisUpdateTimeMonitor::instance()->endStrokeMeasure();
}
void FreehandStrokeStrategy::init(bool needsIndirectPainting,
const QString &indirectPaintingCompositeOp)
{
setNeedsIndirectPainting(needsIndirectPainting);
setIndirectPaintingCompositeOp(indirectPaintingCompositeOp);
setSupportsWrapAroundMode(true);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
KisUpdateTimeMonitor::instance()->startStrokeMeasure();
}
void FreehandStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
Data *d = dynamic_cast<Data*>(data);
PainterInfo *info = painterInfos()[d->painterInfoId];
KisUpdateTimeMonitor::instance()->reportPaintOpPreset(info->painter->preset());
KisRandomSourceSP rnd = m_d->randomSource.source();
switch(d->type) {
case Data::POINT:
d->pi1.setRandomSource(rnd);
info->painter->paintAt(d->pi1, info->dragDistance);
break;
case Data::LINE:
d->pi1.setRandomSource(rnd);
d->pi2.setRandomSource(rnd);
info->painter->paintLine(d->pi1, d->pi2, info->dragDistance);
break;
case Data::CURVE:
d->pi1.setRandomSource(rnd);
d->pi2.setRandomSource(rnd);
info->painter->paintBezierCurve(d->pi1,
d->control1,
d->control2,
d->pi2,
info->dragDistance);
break;
case Data::POLYLINE:
info->painter->paintPolyline(d->points, 0, d->points.size());
break;
case Data::POLYGON:
info->painter->paintPolygon(d->points);
break;
case Data::RECT:
info->painter->paintRect(d->rect);
break;
case Data::ELLIPSE:
info->painter->paintEllipse(d->rect);
break;
case Data::PAINTER_PATH:
info->painter->paintPainterPath(d->path);
break;
case Data::QPAINTER_PATH:
info->painter->drawPainterPath(d->path, d->pen);
break;
case Data::QPAINTER_PATH_FILL: {
info->painter->setBackgroundColor(d->customColor);
info->painter->fillPainterPath(d->path);}
info->painter->drawPainterPath(d->path, d->pen);
break;
};
QVector<QRect> dirtyRects = info->painter->takeDirtyRegion();
KisUpdateTimeMonitor::instance()->reportJobFinished(data, dirtyRects);
d->node->setDirty(dirtyRects);
}
KisStrokeStrategy* FreehandStrokeStrategy::createLodClone(int levelOfDetail)
{
if (!m_d->resources->presetAllowsLod()) return 0;
FreehandStrokeStrategy *clone = new FreehandStrokeStrategy(*this, levelOfDetail);
- clone->setUndoEnabled(false);
return clone;
}
diff --git a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp
index 8ede8a1535..5ddf177f52 100644
--- a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp
+++ b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp
@@ -1,75 +1,79 @@
/*
* Copyright (c) 2016 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_color_picker_stroke_strategy.h"
#include <KoColor.h>
#include "kis_debug.h"
#include "kis_tool_utils.h"
#include <kis_wrapped_rect.h>
#include "kis_default_bounds.h"
#include "kis_paint_device.h"
struct KisColorPickerStrokeStrategy::Private
{
Private() : shouldSkipWork(false) {}
bool shouldSkipWork;
+ int radius = 1;
};
-KisColorPickerStrokeStrategy::KisColorPickerStrokeStrategy()
+KisColorPickerStrokeStrategy::KisColorPickerStrokeStrategy(int lod)
: m_d(new Private)
{
setSupportsWrapAroundMode(true);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
+
+ KisToolUtils::ColorPickerConfig config;
+ config.load();
+
+ m_d->radius = qMax(1, qRound(config.radius * KisLodTransform::lodToScale(lod)));
}
KisColorPickerStrokeStrategy::~KisColorPickerStrokeStrategy()
{
}
void KisColorPickerStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
if (m_d->shouldSkipWork) return;
Data *d = dynamic_cast<Data*>(data);
KIS_ASSERT_RECOVER_RETURN(d);
KoColor color;
- bool result = KisToolUtils::pick(d->dev, d->pt, &color);
+ bool result = KisToolUtils::pick(d->dev, d->pt, &color, m_d->radius);
Q_UNUSED(result);
emit sigColorUpdated(color);
}
KisStrokeStrategy* KisColorPickerStrokeStrategy::createLodClone(int levelOfDetail)
{
- Q_UNUSED(levelOfDetail);
-
m_d->shouldSkipWork = true;
- KisColorPickerStrokeStrategy *lodStrategy = new KisColorPickerStrokeStrategy();
+ KisColorPickerStrokeStrategy *lodStrategy = new KisColorPickerStrokeStrategy(levelOfDetail);
connect(lodStrategy, &KisColorPickerStrokeStrategy::sigColorUpdated,
this, &KisColorPickerStrokeStrategy::sigColorUpdated,
Qt::DirectConnection);
return lodStrategy;
}
diff --git a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.h b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.h
index 532b150dcd..339d320a01 100644
--- a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.h
+++ b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.h
@@ -1,65 +1,65 @@
/*
* Copyright (c) 2016 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_COLOR_PICKER_STROKE_STRATEGY_H
#define __KIS_COLOR_PICKER_STROKE_STRATEGY_H
#include <QObject>
#include <QScopedPointer>
#include "kis_simple_stroke_strategy.h"
#include "kis_lod_transform.h"
class KoColor;
class KisColorPickerStrokeStrategy : public QObject, public KisSimpleStrokeStrategy
{
Q_OBJECT
public:
class Data : public KisStrokeJobData {
public:
Data(KisPaintDeviceSP _dev, const QPoint _pt)
: dev(_dev), pt(_pt)
{}
KisStrokeJobData* createLodClone(int levelOfDetail) {
KisLodTransform t(levelOfDetail);
const QPoint realPoint = t.map(pt);
return new Data(dev, realPoint);
}
KisPaintDeviceSP dev;
QPoint pt;
};
public:
- KisColorPickerStrokeStrategy();
+ KisColorPickerStrokeStrategy(int lod = 0);
~KisColorPickerStrokeStrategy();
void doStrokeCallback(KisStrokeJobData *data);
KisStrokeStrategy* createLodClone(int levelOfDetail);
Q_SIGNALS:
void sigColorUpdated(const KoColor &color);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_COLOR_PICKER_STROKE_STRATEGY_H */
diff --git a/libs/ui/tool/strokes/kis_filter_stroke_strategy.cpp b/libs/ui/tool/strokes/kis_filter_stroke_strategy.cpp
index 5fd75634c7..d662879dea 100644
--- a/libs/ui/tool/strokes/kis_filter_stroke_strategy.cpp
+++ b/libs/ui/tool/strokes/kis_filter_stroke_strategy.cpp
@@ -1,209 +1,208 @@
/*
* 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_filter_stroke_strategy.h"
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_transaction.h>
#include <KoCompositeOpRegistry.h>
struct KisFilterStrokeStrategy::Private {
Private()
: updatesFacade(0),
cancelSilently(false),
secondaryTransaction(0),
levelOfDetail(0)
{
}
Private(const Private &rhs)
: filter(rhs.filter),
filterConfig(rhs.filterConfig),
node(rhs.node),
updatesFacade(rhs.updatesFacade),
cancelSilently(rhs.cancelSilently),
filterDevice(),
filterDeviceBounds(),
secondaryTransaction(0),
progressHelper(),
levelOfDetail(0)
{
KIS_ASSERT_RECOVER_RETURN(!rhs.filterDevice);
KIS_ASSERT_RECOVER_RETURN(rhs.filterDeviceBounds.isEmpty());
KIS_ASSERT_RECOVER_RETURN(!rhs.secondaryTransaction);
KIS_ASSERT_RECOVER_RETURN(!rhs.progressHelper);
KIS_ASSERT_RECOVER_RETURN(!rhs.levelOfDetail);
}
KisFilterSP filter;
KisFilterConfigurationSP filterConfig;
KisNodeSP node;
KisUpdatesFacade *updatesFacade;
bool cancelSilently;
KisPaintDeviceSP filterDevice;
QRect filterDeviceBounds;
KisTransaction *secondaryTransaction;
QScopedPointer<KisProcessingVisitor::ProgressHelper> progressHelper;
int levelOfDetail;
};
KisFilterStrokeStrategy::KisFilterStrokeStrategy(KisFilterSP filter,
KisFilterConfigurationSP filterConfig,
KisResourcesSnapshotSP resources)
: KisPainterBasedStrokeStrategy("FILTER_STROKE",
kundo2_i18n("Filter \"%1\"", filter->name()),
resources,
QVector<PainterInfo*>(),false),
m_d(new Private())
{
m_d->filter = filter;
m_d->filterConfig = filterConfig;
m_d->node = resources->currentNode();
m_d->updatesFacade = resources->image().data();
m_d->cancelSilently = false;
m_d->secondaryTransaction = 0;
m_d->levelOfDetail = 0;
setSupportsWrapAroundMode(true);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
}
KisFilterStrokeStrategy::KisFilterStrokeStrategy(const KisFilterStrokeStrategy &rhs, int levelOfDetail)
: KisPainterBasedStrokeStrategy(rhs, levelOfDetail),
m_d(new Private(*rhs.m_d))
{
// only non-started transaction are allowed
KIS_ASSERT_RECOVER_NOOP(!m_d->secondaryTransaction);
m_d->levelOfDetail = levelOfDetail;
}
KisFilterStrokeStrategy::~KisFilterStrokeStrategy()
{
delete m_d;
}
void KisFilterStrokeStrategy::initStrokeCallback()
{
KisPainterBasedStrokeStrategy::initStrokeCallback();
KisPaintDeviceSP dev = targetDevice();
m_d->filterDeviceBounds = dev->extent();
if (activeSelection() ||
(dev->colorSpace() != dev->compositionSourceColorSpace() &&
*dev->colorSpace() != *dev->compositionSourceColorSpace())) {
m_d->filterDevice = dev->createCompositionSourceDevice(dev);
m_d->secondaryTransaction = new KisTransaction(m_d->filterDevice);
if (activeSelection()) {
m_d->filterDeviceBounds &= activeSelection()->selectedRect();
}
} else {
m_d->filterDevice = dev;
}
m_d->progressHelper.reset(new KisProcessingVisitor::ProgressHelper(m_d->node));
}
void KisFilterStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
Data *d = dynamic_cast<Data*>(data);
CancelSilentlyMarker *cancelJob =
dynamic_cast<CancelSilentlyMarker*>(data);
if (d) {
const QRect rc = d->processRect;
if (!m_d->filterDeviceBounds.intersects(
m_d->filter->neededRect(rc, m_d->filterConfig.data(), m_d->levelOfDetail))) {
return;
}
m_d->filter->processImpl(m_d->filterDevice, rc,
m_d->filterConfig.data(),
m_d->progressHelper->updater());
if (m_d->secondaryTransaction) {
KisPainter::copyAreaOptimized(rc.topLeft(), m_d->filterDevice, targetDevice(), rc, activeSelection());
// Free memory
m_d->filterDevice->clear(rc);
}
m_d->node->setDirty(rc);
} else if (cancelJob) {
m_d->cancelSilently = true;
} else {
qFatal("KisFilterStrokeStrategy: job type is not known");
}
}
void KisFilterStrokeStrategy::cancelStrokeCallback()
{
delete m_d->secondaryTransaction;
m_d->filterDevice = 0;
KisProjectionUpdatesFilterSP prevUpdatesFilter;
if (m_d->cancelSilently) {
/**
* TODO: Projection updates filter is not recursive, please
* redesign this part
*/
prevUpdatesFilter = m_d->updatesFacade->projectionUpdatesFilter();
if (prevUpdatesFilter) {
m_d->updatesFacade->setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP());
}
m_d->updatesFacade->disableDirtyRequests();
}
KisPainterBasedStrokeStrategy::cancelStrokeCallback();
if (m_d->cancelSilently) {
m_d->updatesFacade->enableDirtyRequests();
if (prevUpdatesFilter) {
m_d->updatesFacade->setProjectionUpdatesFilter(prevUpdatesFilter);
prevUpdatesFilter.clear();
}
}
}
void KisFilterStrokeStrategy::finishStrokeCallback()
{
delete m_d->secondaryTransaction;
m_d->filterDevice = 0;
KisPainterBasedStrokeStrategy::finishStrokeCallback();
}
KisStrokeStrategy* KisFilterStrokeStrategy::createLodClone(int levelOfDetail)
{
if (!m_d->filter->supportsLevelOfDetail(m_d->filterConfig.data(), levelOfDetail)) return 0;
KisFilterStrokeStrategy *clone = new KisFilterStrokeStrategy(*this, levelOfDetail);
- clone->setUndoEnabled(false);
return clone;
}
diff --git a/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp b/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
index abe3b5f667..8a05c53142 100644
--- a/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
+++ b/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
@@ -1,324 +1,316 @@
/*
* 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_painter_based_stroke_strategy.h"
#include <KoColorSpace.h>
#include <KoColor.h>
#include <KoCompositeOp.h>
#include "kis_painter.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_transaction.h"
#include "kis_image.h"
#include <kis_distance_information.h>
#include "kis_undo_stores.h"
KisPainterBasedStrokeStrategy::PainterInfo::PainterInfo()
: painter(new KisPainter()),
dragDistance(new KisDistanceInformation()),
m_parentPainterInfo(0),
m_childPainterInfo(0)
{
}
KisPainterBasedStrokeStrategy::PainterInfo::PainterInfo(const QPointF &lastPosition, int lastTime)
: painter(new KisPainter()),
dragDistance(new KisDistanceInformation(lastPosition, lastTime)),
m_parentPainterInfo(0),
m_childPainterInfo(0)
{
}
KisPainterBasedStrokeStrategy::PainterInfo::PainterInfo(PainterInfo *rhs, int levelOfDetail)
: painter(new KisPainter()),
dragDistance(new KisDistanceInformation(*rhs->dragDistance, levelOfDetail)),
m_parentPainterInfo(rhs)
{
rhs->m_childPainterInfo = this;
}
KisPainterBasedStrokeStrategy::PainterInfo::~PainterInfo()
{
if (m_parentPainterInfo) {
m_parentPainterInfo->m_childPainterInfo = 0;
}
delete(painter);
delete(dragDistance);
}
KisDistanceInformation* KisPainterBasedStrokeStrategy::PainterInfo::buddyDragDistance()
{
return m_childPainterInfo ? m_childPainterInfo->dragDistance : 0;
}
KisPainterBasedStrokeStrategy::KisPainterBasedStrokeStrategy(const QString &id,
const KUndo2MagicString &name,
KisResourcesSnapshotSP resources,
QVector<PainterInfo*> painterInfos,bool useMergeID)
: KisSimpleStrokeStrategy(id, name),
m_resources(resources),
m_painterInfos(painterInfos),
m_transaction(0),
- m_undoEnabled(true),
m_useMergeID(useMergeID)
{
init();
}
KisPainterBasedStrokeStrategy::KisPainterBasedStrokeStrategy(const QString &id,
const KUndo2MagicString &name,
KisResourcesSnapshotSP resources,
PainterInfo *painterInfo,bool useMergeID)
: KisSimpleStrokeStrategy(id, name),
m_resources(resources),
m_painterInfos(QVector<PainterInfo*>() << painterInfo),
m_transaction(0),
- m_undoEnabled(true),
m_useMergeID(useMergeID)
{
init();
}
void KisPainterBasedStrokeStrategy::init()
{
enableJob(KisSimpleStrokeStrategy::JOB_INIT);
enableJob(KisSimpleStrokeStrategy::JOB_FINISH);
enableJob(KisSimpleStrokeStrategy::JOB_CANCEL, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
enableJob(KisSimpleStrokeStrategy::JOB_SUSPEND);
enableJob(KisSimpleStrokeStrategy::JOB_RESUME);
}
KisPainterBasedStrokeStrategy::KisPainterBasedStrokeStrategy(const KisPainterBasedStrokeStrategy &rhs, int levelOfDetail)
: KisSimpleStrokeStrategy(rhs),
m_resources(rhs.m_resources),
m_transaction(rhs.m_transaction),
- m_undoEnabled(true),
m_useMergeID(rhs.m_useMergeID)
{
Q_FOREACH (PainterInfo *info, rhs.m_painterInfos) {
m_painterInfos.append(new PainterInfo(info, levelOfDetail));
}
KIS_ASSERT_RECOVER_NOOP(
!rhs.m_transaction &&
!rhs.m_targetDevice &&
!rhs.m_activeSelection &&
"After the stroke has been started, no copying must happen");
}
KisPaintDeviceSP KisPainterBasedStrokeStrategy::targetDevice() const
{
return m_targetDevice;
}
KisSelectionSP KisPainterBasedStrokeStrategy::activeSelection() const
{
return m_activeSelection;
}
const QVector<KisPainterBasedStrokeStrategy::PainterInfo*>
KisPainterBasedStrokeStrategy::painterInfos() const
{
return m_painterInfos;
}
-void KisPainterBasedStrokeStrategy::setUndoEnabled(bool value)
-{
- m_undoEnabled = value;
-}
-
void KisPainterBasedStrokeStrategy::initPainters(KisPaintDeviceSP targetDevice,
KisSelectionSP selection,
bool hasIndirectPainting,
const QString &indirectPaintingCompositeOp)
{
Q_FOREACH (PainterInfo *info, m_painterInfos) {
KisPainter *painter = info->painter;
painter->begin(targetDevice, !hasIndirectPainting ? selection : 0);
m_resources->setupPainter(painter);
if(hasIndirectPainting) {
painter->setCompositeOp(targetDevice->colorSpace()->compositeOp(indirectPaintingCompositeOp));
painter->setOpacity(OPACITY_OPAQUE_U8);
painter->setChannelFlags(QBitArray());
}
}
}
void KisPainterBasedStrokeStrategy::deletePainters()
{
Q_FOREACH (PainterInfo *info, m_painterInfos) {
delete info;
}
m_painterInfos.clear();
}
void KisPainterBasedStrokeStrategy::initStrokeCallback()
{
KisNodeSP node = m_resources->currentNode();
KisPaintDeviceSP paintDevice = node->paintDevice();
KisPaintDeviceSP targetDevice = paintDevice;
bool hasIndirectPainting = needsIndirectPainting();
KisSelectionSP selection = m_resources->activeSelection();
if (hasIndirectPainting) {
KisIndirectPaintingSupport *indirect =
dynamic_cast<KisIndirectPaintingSupport*>(node.data());
if (indirect) {
targetDevice = paintDevice->createCompositionSourceDevice();
targetDevice->setParentNode(node);
indirect->setCurrentColor(m_resources->currentFgColor());
indirect->setTemporaryTarget(targetDevice);
indirect->setTemporaryCompositeOp(m_resources->compositeOpId());
indirect->setTemporaryOpacity(m_resources->opacity());
indirect->setTemporarySelection(selection);
QBitArray channelLockFlags = m_resources->channelLockFlags();
indirect->setTemporaryChannelFlags(channelLockFlags);
}
else {
hasIndirectPainting = false;
}
}
if(m_useMergeID){
m_transaction = new KisTransaction(name(), targetDevice,0,timedID(this->id()));
}
else{
m_transaction = new KisTransaction(name(), targetDevice);
}
initPainters(targetDevice, selection, hasIndirectPainting, indirectPaintingCompositeOp());
m_targetDevice = targetDevice;
m_activeSelection = selection;
// sanity check: selection should be applied only once
if (selection && !m_painterInfos.isEmpty()) {
KisIndirectPaintingSupport *indirect =
dynamic_cast<KisIndirectPaintingSupport*>(node.data());
KIS_ASSERT_RECOVER_RETURN(hasIndirectPainting || m_painterInfos.first()->painter->selection());
KIS_ASSERT_RECOVER_RETURN(!hasIndirectPainting || !indirect->temporarySelection() || !m_painterInfos.first()->painter->selection());
}
}
void KisPainterBasedStrokeStrategy::finishStrokeCallback()
{
KisNodeSP node = m_resources->currentNode();
KisIndirectPaintingSupport *indirect =
dynamic_cast<KisIndirectPaintingSupport*>(node.data());
KisPostExecutionUndoAdapter *undoAdapter =
m_resources->postExecutionUndoAdapter();
QScopedPointer<KisPostExecutionUndoAdapter> dumbUndoAdapter;
QScopedPointer<KisUndoStore> dumbUndoStore;
-
- if (!m_undoEnabled) {
+ if (!undoAdapter) {
dumbUndoStore.reset(new KisDumbUndoStore());
dumbUndoAdapter.reset(new KisPostExecutionUndoAdapter(dumbUndoStore.data(), 0));
undoAdapter = dumbUndoAdapter.data();
}
+
if (indirect && indirect->hasTemporaryTarget()) {
KUndo2MagicString transactionText = m_transaction->text();
m_transaction->end();
if(m_useMergeID){
indirect->mergeToLayer(node,
undoAdapter,
transactionText,timedID(this->id()));
}
else{
indirect->mergeToLayer(node,
undoAdapter,
transactionText);
}
}
else {
m_transaction->commit(undoAdapter);
}
delete m_transaction;
deletePainters();
}
void KisPainterBasedStrokeStrategy::cancelStrokeCallback()
{
KisNodeSP node = m_resources->currentNode();
KisIndirectPaintingSupport *indirect =
dynamic_cast<KisIndirectPaintingSupport*>(node.data());
bool revert = true;
if (indirect) {
KisPaintDeviceSP t = indirect->temporaryTarget();
if (t) {
delete m_transaction;
deletePainters();
QRegion region = t->region();
indirect->setTemporaryTarget(0);
node->setDirty(region);
revert = false;
}
}
if (revert) {
m_transaction->revert();
delete m_transaction;
deletePainters();
}
}
void KisPainterBasedStrokeStrategy::suspendStrokeCallback()
{
KisNodeSP node = m_resources->currentNode();
KisIndirectPaintingSupport *indirect =
dynamic_cast<KisIndirectPaintingSupport*>(node.data());
if(indirect && indirect->hasTemporaryTarget()) {
indirect->setTemporaryTarget(0);
}
}
void KisPainterBasedStrokeStrategy::resumeStrokeCallback()
{
KisNodeSP node = m_resources->currentNode();
KisIndirectPaintingSupport *indirect =
dynamic_cast<KisIndirectPaintingSupport*>(node.data());
if(indirect) {
// todo: don't ask about paint device
if (node->paintDevice() != m_targetDevice) {
indirect->setTemporaryTarget(m_targetDevice);
indirect->setTemporaryCompositeOp(m_resources->compositeOpId());
indirect->setTemporaryOpacity(m_resources->opacity());
indirect->setTemporarySelection(m_activeSelection);
}
}
}
diff --git a/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.h b/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.h
index 09fa6939f3..f8942d7c89 100644
--- a/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.h
+++ b/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.h
@@ -1,111 +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.
*/
#ifndef __KIS_PAINTER_BASED_STROKE_STRATEGY_H
#define __KIS_PAINTER_BASED_STROKE_STRATEGY_H
#include "kis_simple_stroke_strategy.h"
#include "kis_resources_snapshot.h"
#include "kis_selection.h"
class KisPainter;
class KisDistanceInformation;
class KisTransaction;
class KRITAUI_EXPORT KisPainterBasedStrokeStrategy : public KisSimpleStrokeStrategy
{
public:
/**
* The distance information should be associated with each
* painter individually, so we strore and manipulate with
* them together using the structure PainterInfo
*/
class KRITAUI_EXPORT PainterInfo {
public:
PainterInfo();
PainterInfo(const QPointF &lastPosition, int lastTime);
PainterInfo(PainterInfo *rhs, int levelOfDetail);
~PainterInfo();
KisPainter *painter;
KisDistanceInformation *dragDistance;
/**
* The distance inforametion of the associated LodN
* stroke. Returns zero if LodN stroke has already finished
* execution or does not exist.
*/
KisDistanceInformation* buddyDragDistance();
private:
PainterInfo *m_parentPainterInfo;
PainterInfo *m_childPainterInfo;
};
public:
KisPainterBasedStrokeStrategy(const QString &id,
const KUndo2MagicString &name,
KisResourcesSnapshotSP resources,
QVector<PainterInfo*> painterInfos,bool useMergeID = false);
KisPainterBasedStrokeStrategy(const QString &id,
const KUndo2MagicString &name,
KisResourcesSnapshotSP resources,
PainterInfo *painterInfo,bool useMergeID = false);
void initStrokeCallback();
void finishStrokeCallback();
void cancelStrokeCallback();
void suspendStrokeCallback();
void resumeStrokeCallback();
protected:
KisPaintDeviceSP targetDevice() const;
KisSelectionSP activeSelection() const;
const QVector<PainterInfo*> painterInfos() const;
void setUndoEnabled(bool value);
protected:
KisPainterBasedStrokeStrategy(const KisPainterBasedStrokeStrategy &rhs, int levelOfDetail);
private:
void init();
void initPainters(KisPaintDeviceSP targetDevice,
KisSelectionSP selection,
bool hasIndirectPainting,
const QString &indirectPaintingCompositeOp);
void deletePainters();
inline int timedID(const QString &id){
return int(qHash(id));
}
private:
KisResourcesSnapshotSP m_resources;
QVector<PainterInfo*> m_painterInfos;
KisTransaction *m_transaction;
KisPaintDeviceSP m_targetDevice;
KisSelectionSP m_activeSelection;
- bool m_undoEnabled;
bool m_useMergeID;
};
#endif /* __KIS_PAINTER_BASED_STROKE_STRATEGY_H */
diff --git a/libs/ui/wdgsplash.ui b/libs/ui/wdgsplash.ui
index 9a3de54f9c..5c2369b3a9 100644
--- a/libs/ui/wdgsplash.ui
+++ b/libs/ui/wdgsplash.ui
@@ -1,915 +1,957 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgSplash</class>
<widget class="QWidget" name="WdgSplash">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>458</width>
- <height>469</height>
+ <height>439</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>201</red>
<green>201</green>
<blue>201</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>64</red>
<green>64</green>
<blue>64</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>34</red>
<green>32</green>
<blue>31</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>20</red>
<green>19</green>
<blue>18</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>201</red>
<green>201</green>
<blue>201</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>38</red>
<green>38</green>
<blue>38</blue>
</color>
</brush>
</colorrole>
<colorrole role="Highlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>153</red>
<green>204</green>
<blue>139</blue>
</color>
</brush>
</colorrole>
<colorrole role="Link">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>233</red>
<green>233</green>
<blue>233</blue>
</color>
</brush>
</colorrole>
<colorrole role="LinkVisited">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>202</red>
<green>107</green>
<blue>122</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>201</red>
<green>201</green>
<blue>201</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>64</red>
<green>64</green>
<blue>64</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>34</red>
<green>32</green>
<blue>31</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>20</red>
<green>19</green>
<blue>18</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>201</red>
<green>201</green>
<blue>201</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>38</red>
<green>38</green>
<blue>38</blue>
</color>
</brush>
</colorrole>
<colorrole role="Highlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>153</red>
<green>204</green>
<blue>139</blue>
</color>
</brush>
</colorrole>
<colorrole role="Link">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>233</red>
<green>233</green>
<blue>233</blue>
</color>
</brush>
</colorrole>
<colorrole role="LinkVisited">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>202</red>
<green>107</green>
<blue>122</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>34</red>
<green>32</green>
<blue>31</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>64</red>
<green>64</green>
<blue>64</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>34</red>
<green>32</green>
<blue>31</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>34</red>
<green>32</green>
<blue>31</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>34</red>
<green>32</green>
<blue>31</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>38</red>
<green>38</green>
<blue>38</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>38</red>
<green>38</green>
<blue>38</blue>
</color>
</brush>
</colorrole>
<colorrole role="Highlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>209</red>
<green>204</green>
<blue>202</blue>
</color>
</brush>
</colorrole>
<colorrole role="Link">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>233</red>
<green>233</green>
<blue>233</blue>
</color>
</brush>
</colorrole>
<colorrole role="LinkVisited">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>202</red>
<green>107</green>
<blue>122</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="windowTitle">
<string>Krita</string>
</property>
<property name="windowOpacity">
<double>1.000000000000000</double>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>2</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
<item>
<widget class="QLabel" name="lblSplash">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>440</width>
<height>286</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>../../2.8/krita/data/splash/splash_screen.xpm</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="indent">
<number>0</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="leftMargin">
+ <number>10</number>
+ </property>
+ <property name="rightMargin">
+ <number>10</number>
+ </property>
+ <property name="bottomMargin">
+ <number>10</number>
+ </property>
<item>
<widget class="QLabel" name="lblVersion">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="margin">
<number>4</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="loadingLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
+ <property name="spacing">
+ <number>20</number>
+ </property>
<property name="leftMargin">
- <number>10</number>
+ <number>20</number>
</property>
<property name="rightMargin">
- <number>10</number>
+ <number>20</number>
</property>
<property name="bottomMargin">
- <number>2</number>
+ <number>20</number>
</property>
<item>
<widget class="QLabel" name="lblLinks">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;left&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Links&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblRecent">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;left&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Recent files&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="openExternalLinks">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<widget class="QCheckBox" name="chkShowAtStartup">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Hide after startup.</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bnClose">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>78</red>
<green>78</green>
<blue>78</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>117</red>
<green>117</green>
<blue>117</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>97</red>
<green>97</green>
<blue>97</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>39</red>
<green>39</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>52</red>
<green>52</green>
<blue>52</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="BrightText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>78</red>
<green>78</green>
<blue>78</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>39</red>
<green>39</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>220</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>78</red>
<green>78</green>
<blue>78</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>117</red>
<green>117</green>
<blue>117</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>97</red>
<green>97</green>
<blue>97</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>39</red>
<green>39</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>52</red>
<green>52</green>
<blue>52</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="BrightText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>78</red>
<green>78</green>
<blue>78</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>39</red>
<green>39</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>220</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>39</red>
<green>39</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>78</red>
<green>78</green>
<blue>78</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>117</red>
<green>117</green>
<blue>117</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>97</red>
<green>97</green>
<blue>97</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>39</red>
<green>39</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>52</red>
<green>52</green>
<blue>52</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>39</red>
<green>39</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="BrightText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>39</red>
<green>39</green>
<blue>39</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>78</red>
<green>78</green>
<blue>78</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>78</red>
<green>78</green>
<blue>78</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>78</red>
<green>78</green>
<blue>78</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>220</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="text">
<string>&amp;Close</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/widgets/kis_paintop_presets_popup.cpp b/libs/ui/widgets/kis_paintop_presets_popup.cpp
index 94260f55d9..f92e47ab48 100644
--- a/libs/ui/widgets/kis_paintop_presets_popup.cpp
+++ b/libs/ui/widgets/kis_paintop_presets_popup.cpp
@@ -1,438 +1,440 @@
/* This file is part of the KDE project
* Copyright (C) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2010 Lukáš Tvrdý <lukast.dev@gmail.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 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 "widgets/kis_paintop_presets_popup.h"
#include <QList>
#include <QComboBox>
#include <QHBoxLayout>
#include <QToolButton>
#include <QGridLayout>
#include <QFont>
#include <QMenu>
#include <QAction>
#include <QShowEvent>
#include <QFontDatabase>
#include <kconfig.h>
#include <klocalizedstring.h>
#include <KoDockRegistry.h>
#include <kis_icon.h>
#include <kis_icon.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_config_widget.h>
#include <kis_canvas_resource_provider.h>
#include <widgets/kis_preset_chooser.h>
#include <widgets/kis_preset_selector_strip.h>
#include <ui_wdgpaintopsettings.h>
#include <kis_node.h>
#include "kis_config.h"
#include "kis_resource_server_provider.h"
#include "kis_lod_availability_widget.h"
#include "kis_signal_auto_connection.h"
struct KisPaintOpPresetsPopup::Private
{
public:
Ui_WdgPaintOpSettings uiWdgPaintOpPresetSettings;
QGridLayout *layout;
KisPaintOpConfigWidget *settingsWidget;
QFont smallFont;
KisCanvasResourceProvider *resourceProvider;
bool detached;
bool ignoreHideEvents;
QSize minimumSettingsWidgetSize;
QRect detachedGeometry;
KisSignalAutoConnectionsStore widgetConnections;
};
KisPaintOpPresetsPopup::KisPaintOpPresetsPopup(KisCanvasResourceProvider * resourceProvider, QWidget * parent)
: QWidget(parent)
, m_d(new Private())
{
setObjectName("KisPaintOpPresetsPopup");
setFont(KoDockRegistry::dockFont());
m_d->resourceProvider = resourceProvider;
m_d->uiWdgPaintOpPresetSettings.setupUi(this);
m_d->layout = new QGridLayout(m_d->uiWdgPaintOpPresetSettings.frmOptionWidgetContainer);
m_d->layout->setSizeConstraint(QLayout::SetFixedSize);
m_d->uiWdgPaintOpPresetSettings.scratchPad->setupScratchPad(resourceProvider, Qt::white);
m_d->uiWdgPaintOpPresetSettings.scratchPad->setCutoutOverlayRect(QRect(25, 25, 200, 200));
m_d->uiWdgPaintOpPresetSettings.fillLayer->setIcon(KisIconUtils::loadIcon("document-new"));
m_d->uiWdgPaintOpPresetSettings.fillLayer->hide();
m_d->uiWdgPaintOpPresetSettings.fillGradient->setIcon(KisIconUtils::loadIcon("krita_tool_gradient"));
m_d->uiWdgPaintOpPresetSettings.fillSolid->setIcon(KisIconUtils::loadIcon("krita_tool_color_fill"));
m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setIcon(KisIconUtils::loadIcon("edit-delete"));
m_d->uiWdgPaintOpPresetSettings.paintPresetIcon->setIcon(KisIconUtils::loadIcon("krita_tool_freehand"));
connect(m_d->uiWdgPaintOpPresetSettings.eraseScratchPad, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillDefault()));
connect(m_d->uiWdgPaintOpPresetSettings.fillLayer, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillLayer()));
connect(m_d->uiWdgPaintOpPresetSettings.fillGradient, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillGradient()));
connect(m_d->uiWdgPaintOpPresetSettings.fillSolid, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillBackground()));
connect(m_d->uiWdgPaintOpPresetSettings.paintPresetIcon, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(paintPresetImage()));
m_d->settingsWidget = 0;
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
connect(m_d->uiWdgPaintOpPresetSettings.bnSave, SIGNAL(clicked()),
this, SIGNAL(savePresetClicked()));
connect(m_d->uiWdgPaintOpPresetSettings.reload, SIGNAL(clicked()),
this, SIGNAL(reloadPresetClicked()));
connect(m_d->uiWdgPaintOpPresetSettings.bnDefaultPreset, SIGNAL(clicked()),
this, SIGNAL(defaultPresetClicked()));
connect(m_d->uiWdgPaintOpPresetSettings.dirtyPresetCheckBox, SIGNAL(toggled(bool)),
this, SIGNAL(dirtyPresetToggled(bool)));
connect(m_d->uiWdgPaintOpPresetSettings.eraserBrushSizeCheckBox, SIGNAL(toggled(bool)),
this, SIGNAL(eraserBrushSizeToggled(bool)));
connect(m_d->uiWdgPaintOpPresetSettings.eraserBrushOpacityCheckBox, SIGNAL(toggled(bool)),
this, SIGNAL(eraserBrushOpacityToggled(bool)));
connect(m_d->uiWdgPaintOpPresetSettings.bnDefaultPreset, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.txtPreset, SLOT(clear()));
connect(m_d->uiWdgPaintOpPresetSettings.txtPreset, SIGNAL(textChanged(QString)),
SLOT(slotWatchPresetNameLineEdit()));
connect(m_d->uiWdgPaintOpPresetSettings.paintopList, SIGNAL(activated(QString)),
this, SIGNAL(paintopActivated(QString)));
connect(m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SIGNAL(resourceSelected(KoResource*)),
this, SIGNAL(signalResourceSelected(KoResource*)));
connect(m_d->uiWdgPaintOpPresetSettings.bnSave, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SLOT(updateViewSettings()));
connect(m_d->uiWdgPaintOpPresetSettings.reload, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SLOT(updateViewSettings()));
KisConfig cfg;
m_d->detached = !cfg.paintopPopupDetached();
m_d->ignoreHideEvents = false;
m_d->minimumSettingsWidgetSize = QSize(0, 0);
m_d->uiWdgPaintOpPresetSettings.presetWidget->setVisible(cfg.presetStripVisible());
m_d->uiWdgPaintOpPresetSettings.scratchpadControls->setVisible(cfg.scratchpadVisible());
m_d->detachedGeometry = QRect(100, 100, 0, 0);
m_d->uiWdgPaintOpPresetSettings.dirtyPresetCheckBox->setChecked(cfg.useDirtyPresets());
m_d->uiWdgPaintOpPresetSettings.eraserBrushSizeCheckBox->setChecked(cfg.useEraserBrushSize());
m_d->uiWdgPaintOpPresetSettings.eraserBrushOpacityCheckBox->setChecked(cfg.useEraserBrushOpacity());
m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->setCanvasResourceManager(resourceProvider->resourceManager());
connect(resourceProvider->resourceManager(),
SIGNAL(canvasResourceChanged(int,QVariant)),
SLOT(slotResourceChanged(int, QVariant)));
connect(m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability,
SIGNAL(sigUserChangedLodAvailability(bool)),
SLOT(slotLodAvailabilityChanged(bool)));
slotResourceChanged(KisCanvasResourceProvider::LodAvailability,
resourceProvider->resourceManager()->
resource(KisCanvasResourceProvider::LodAvailability));
}
void KisPaintOpPresetsPopup::slotResourceChanged(int key, const QVariant &value)
{
if (key == KisCanvasResourceProvider::LodAvailability) {
m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->slotUserChangedLodAvailability(value.toBool());
}
}
void KisPaintOpPresetsPopup::slotLodAvailabilityChanged(bool value)
{
m_d->resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::LodAvailability, QVariant(value));
}
KisPaintOpPresetsPopup::~KisPaintOpPresetsPopup()
{
if (m_d->settingsWidget) {
m_d->layout->removeWidget(m_d->settingsWidget);
m_d->settingsWidget->hide();
m_d->settingsWidget->setParent(0);
m_d->settingsWidget = 0;
}
delete m_d;
}
void KisPaintOpPresetsPopup::setPaintOpSettingsWidget(QWidget * widget)
{
if (m_d->settingsWidget) {
m_d->layout->removeWidget(m_d->settingsWidget);
m_d->uiWdgPaintOpPresetSettings.frmOptionWidgetContainer->updateGeometry();
}
m_d->layout->update();
updateGeometry();
m_d->widgetConnections.clear();
m_d->settingsWidget = 0;
if (widget) {
m_d->settingsWidget = dynamic_cast<KisPaintOpConfigWidget*>(widget);
KIS_ASSERT_RECOVER_RETURN(m_d->settingsWidget);
if (m_d->settingsWidget->supportScratchBox()) {
showScratchPad();
} else {
hideScratchPad();
}
m_d->widgetConnections.addConnection(m_d->settingsWidget, SIGNAL(sigConfigurationItemChanged()),
this, SLOT(slotUpdateLodAvailability()));
widget->setFont(m_d->smallFont);
QSize hint = widget->sizeHint();
m_d->minimumSettingsWidgetSize = QSize(qMax(hint.width(), m_d->minimumSettingsWidgetSize.width()),
qMax(hint.height(), m_d->minimumSettingsWidgetSize.height()));
widget->setMinimumSize(m_d->minimumSettingsWidgetSize);
m_d->layout->addWidget(widget);
m_d->layout->update();
widget->show();
}
+
+ slotUpdateLodAvailability();
}
void KisPaintOpPresetsPopup::slotUpdateLodAvailability()
{
if (!m_d->settingsWidget) return;
KisPaintopLodLimitations l = m_d->settingsWidget->lodLimitations();
m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->setLimitations(l);
}
void KisPaintOpPresetsPopup::slotWatchPresetNameLineEdit()
{
QString text = m_d->uiWdgPaintOpPresetSettings.txtPreset->text();
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
bool overwrite = rServer->resourceByName(text) != 0;
KisPaintOpPresetSP preset = m_d->resourceProvider->currentPreset();
bool btnSaveAvailable = preset->valid() &&
(preset->isPresetDirty() | !overwrite);
QString btnText = overwrite ? i18n("Overwrite Preset") : i18n("Save to Presets");
m_d->uiWdgPaintOpPresetSettings.bnSave->setText(btnText);
m_d->uiWdgPaintOpPresetSettings.bnSave->setEnabled(btnSaveAvailable);
m_d->uiWdgPaintOpPresetSettings.reload->setVisible(true);
m_d->uiWdgPaintOpPresetSettings.reload->setEnabled(btnSaveAvailable && overwrite);
QFont font = m_d->uiWdgPaintOpPresetSettings.txtPreset->font();
font.setItalic(btnSaveAvailable);
m_d->uiWdgPaintOpPresetSettings.txtPreset->setFont(font);
}
QString KisPaintOpPresetsPopup::getPresetName() const
{
return m_d->uiWdgPaintOpPresetSettings.txtPreset->text();
}
QImage KisPaintOpPresetsPopup::cutOutOverlay()
{
return m_d->uiWdgPaintOpPresetSettings.scratchPad->cutoutOverlay();
}
void KisPaintOpPresetsPopup::contextMenuEvent(QContextMenuEvent *e)
{
Q_UNUSED(e);
#if 0
QMenu menu(this);
QAction* action = menu.addAction(m_d->detached ? i18n("Attach to Toolbar") : i18n("Detach from Toolbar"));
connect(action, SIGNAL(triggered()), this, SLOT(switchDetached()));
QAction* showPresetStrip = menu.addAction(i18n("Show Preset Strip"));
showPresetStrip->setCheckable(true);
showPresetStrip->setChecked(m_d->uiWdgPaintOpPresetSettings.presetWidget->isVisible());
connect(showPresetStrip, SIGNAL(triggered(bool)), this, SLOT(slotSwitchPresetStrip(bool)));
QAction* showScratchPad = menu.addAction(i18n("Show Scratchpad"));
showScratchPad->setCheckable(true);
showScratchPad->setChecked(m_d->uiWdgPaintOpPresetSettings.scratchPad->isVisible());
connect(showScratchPad, SIGNAL(triggered(bool)), this, SLOT(slotSwitchScratchpad(bool)));
menu.exec(e->globalPos());
#endif
}
void KisPaintOpPresetsPopup::switchDetached(bool show)
{
if (parentWidget()) {
m_d->detached = !m_d->detached;
if (m_d->detached) {
m_d->ignoreHideEvents = true;
parentWidget()->setWindowFlags(Qt::Tool);
m_d->uiWdgPaintOpPresetSettings.scratchpadControls->setVisible(false);
if (show) {
parentWidget()->show();
}
m_d->ignoreHideEvents = false;
}
else {
parentWidget()->setWindowFlags(Qt::Popup);
KisConfig cfg;
m_d->uiWdgPaintOpPresetSettings.scratchpadControls->setVisible(cfg.scratchpadVisible());
parentWidget()->hide();
}
KisConfig cfg;
cfg.setPaintopPopupDetached(m_d->detached);
}
}
void KisPaintOpPresetsPopup::hideScratchPad()
{
m_d->uiWdgPaintOpPresetSettings.scratchPad->setEnabled(false);
m_d->uiWdgPaintOpPresetSettings.fillGradient->setEnabled(false);
m_d->uiWdgPaintOpPresetSettings.fillSolid->setEnabled(false);
m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setEnabled(false);
}
void KisPaintOpPresetsPopup::showScratchPad()
{
m_d->uiWdgPaintOpPresetSettings.scratchPad->setEnabled(true);
m_d->uiWdgPaintOpPresetSettings.fillGradient->setEnabled(true);
m_d->uiWdgPaintOpPresetSettings.fillSolid->setEnabled(true);
m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setEnabled(true);
}
void KisPaintOpPresetsPopup::resourceSelected(KoResource* resource)
{
m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->setCurrentResource(resource);
m_d->uiWdgPaintOpPresetSettings.txtPreset->setText(resource->name());
slotWatchPresetNameLineEdit();
}
void KisPaintOpPresetsPopup::setPaintOpList(const QList< KisPaintOpFactory* >& list)
{
m_d->uiWdgPaintOpPresetSettings.paintopList->setPaintOpList(list);
}
void KisPaintOpPresetsPopup::setCurrentPaintOp(const QString& paintOpId)
{
m_d->uiWdgPaintOpPresetSettings.paintopList->setCurrent(paintOpId);
m_d->uiWdgPaintOpPresetSettings.presetWidget->setPresetFilter(paintOpId);
}
QString KisPaintOpPresetsPopup::currentPaintOp()
{
return m_d->uiWdgPaintOpPresetSettings.paintopList->currentItem();
}
void KisPaintOpPresetsPopup::setPresetImage(const QImage& image)
{
m_d->uiWdgPaintOpPresetSettings.scratchPad->setPresetImage(image);
}
void KisPaintOpPresetsPopup::hideEvent(QHideEvent *event)
{
if (m_d->ignoreHideEvents) {
return;
}
if (m_d->detached) {
m_d->detachedGeometry = window()->geometry();
}
QWidget::hideEvent(event);
}
void KisPaintOpPresetsPopup::showEvent(QShowEvent *)
{
if (m_d->detached) {
window()->setGeometry(m_d->detachedGeometry);
}
emit brushEditorShown();
}
void KisPaintOpPresetsPopup::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
emit sizeChanged();
}
bool KisPaintOpPresetsPopup::detached() const
{
return m_d->detached;
}
void KisPaintOpPresetsPopup::slotSwitchPresetStrip(bool visible)
{
m_d->uiWdgPaintOpPresetSettings.presetWidget->setVisible(visible);
KisConfig cfg;
cfg.setPresetStripVisible(visible);
}
void KisPaintOpPresetsPopup::slotSwitchScratchpad(bool visible)
{
m_d->uiWdgPaintOpPresetSettings.scratchpadControls->setVisible(visible);
KisConfig cfg;
cfg.setScratchpadVisible(visible);
}
void KisPaintOpPresetsPopup::updateViewSettings()
{
m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->updateViewSettings();
}
void KisPaintOpPresetsPopup::currentPresetChanged(KisPaintOpPresetSP preset)
{
m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->setCurrentResource(preset.data());
}
void KisPaintOpPresetsPopup::updateThemedIcons()
{
m_d->uiWdgPaintOpPresetSettings.fillLayer->setIcon(KisIconUtils::loadIcon("document-new"));
m_d->uiWdgPaintOpPresetSettings.fillLayer->hide();
m_d->uiWdgPaintOpPresetSettings.fillGradient->setIcon(KisIconUtils::loadIcon("krita_tool_gradient"));
m_d->uiWdgPaintOpPresetSettings.fillSolid->setIcon(KisIconUtils::loadIcon("krita_tool_color_fill"));
m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setIcon(KisIconUtils::loadIcon("edit-delete"));
m_d->uiWdgPaintOpPresetSettings.paintPresetIcon->setIcon(KisIconUtils::loadIcon("krita_tool_freehand"));
}
diff --git a/libs/ui/widgets/kis_scratch_pad.cpp b/libs/ui/widgets/kis_scratch_pad.cpp
index a019db785a..86ef4cc00f 100644
--- a/libs/ui/widgets/kis_scratch_pad.cpp
+++ b/libs/ui/widgets/kis_scratch_pad.cpp
@@ -1,470 +1,469 @@
/* This file is part of the KDE project
* Copyright 2010 (C) Boudewijn Rempt <boud@valdyas.org>
* Copyright 2011 (C) Dmitry Kazakov <dimula73@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_scratch_pad.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QMutex>
#include <KoColorSpace.h>
#include <KoColorProfile.h>
#include <KoColorSpaceRegistry.h>
#include <KoPointerEvent.h>
#include <resources/KoAbstractGradient.h>
#include <kis_cursor.h>
#include <kis_tool_utils.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_gradient_painter.h>
#include <kis_default_bounds.h>
#include <kis_canvas_resource_provider.h>
#include "kis_config.h"
#include "kis_image.h"
#include "kis_undo_stores.h"
#include "kis_update_scheduler.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_scratch_pad_event_filter.h"
#include "kis_painting_information_builder.h"
#include "kis_tool_freehand_helper.h"
#include "kis_image_patch.h"
#include "kis_canvas_widget_base.h"
#include "kis_layer_projection_plane.h"
#include "kis_node_graph_listener.h"
class KisScratchPadNodeListener : public KisNodeGraphListener
{
public:
KisScratchPadNodeListener(KisScratchPad *scratchPad)
: m_scratchPad(scratchPad)
{
}
void requestProjectionUpdate(KisNode *node, const QRect& rect) override {
KisNodeGraphListener::requestProjectionUpdate(node, rect);
QMutexLocker locker(&m_lock);
m_scratchPad->imageUpdated(rect);
}
private:
KisScratchPad *m_scratchPad;
QMutex m_lock;
};
class KisScratchPadDefaultBounds : public KisDefaultBounds
{
public:
KisScratchPadDefaultBounds(KisScratchPad *scratchPad)
: m_scratchPad(scratchPad)
{
}
~KisScratchPadDefaultBounds() override {}
QRect bounds() const override {
return m_scratchPad->imageBounds();
}
private:
Q_DISABLE_COPY(KisScratchPadDefaultBounds)
KisScratchPad *m_scratchPad;
};
KisScratchPad::KisScratchPad(QWidget *parent)
: QWidget(parent)
, m_toolMode(HOVERING)
, m_paintLayer(0)
, m_displayProfile(0)
, m_resourceProvider(0)
{
setAutoFillBackground(false);
m_cursor = KisCursor::load("tool_freehand_cursor.png", 5, 5);
setCursor(m_cursor);
KisConfig cfg;
QImage checkImage = KisCanvasWidgetBase::createCheckersImage(cfg.checkSize());
m_checkBrush = QBrush(checkImage);
// We are not supposed to use updates here,
// so just set the listener to null
m_updateScheduler = new KisUpdateScheduler(0);
m_undoStore = new KisSurrogateUndoStore();
m_undoAdapter = new KisPostExecutionUndoAdapter(m_undoStore, m_updateScheduler);
m_nodeListener = new KisScratchPadNodeListener(this);
connect(this, SIGNAL(sigUpdateCanvas(QRect)), SLOT(slotUpdateCanvas(QRect)), Qt::QueuedConnection);
// filter will be deleted by the QObject hierarchy
m_eventFilter = new KisScratchPadEventFilter(this);
m_infoBuilder = new KisPaintingInformationBuilder();
m_helper = new KisToolFreehandHelper(m_infoBuilder);
m_scaleBorderWidth = 1;
}
KisScratchPad::~KisScratchPad() {
delete m_helper;
delete m_infoBuilder;
delete m_undoAdapter;
delete m_undoStore;
delete m_updateScheduler;
delete m_nodeListener;
}
KisScratchPad::Mode KisScratchPad::modeFromButton(Qt::MouseButton button) const
{
return
button == Qt::NoButton ? HOVERING :
button == Qt::MidButton ? PANNING :
button == Qt::RightButton ? PICKING :
PAINTING;
}
void KisScratchPad::pointerPress(KoPointerEvent *event)
{
if (m_toolMode != HOVERING) return;
m_toolMode = modeFromButton(event->button());
if (m_toolMode == PAINTING) {
beginStroke(event);
event->accept();
}
else if (m_toolMode == PANNING) {
beginPan(event);
event->accept();
}
else if (m_toolMode == PICKING) {
pick(event);
event->accept();
}
}
void KisScratchPad::pointerRelease(KoPointerEvent *event)
{
if (modeFromButton(event->button()) != m_toolMode) return;
if (m_toolMode == PAINTING) {
endStroke(event);
m_toolMode = HOVERING;
event->accept();
}
else if (m_toolMode == PANNING) {
endPan(event);
m_toolMode = HOVERING;
event->accept();
}
else if (m_toolMode == PICKING) {
event->accept();
m_toolMode = HOVERING;
}
}
void KisScratchPad::pointerMove(KoPointerEvent *event)
{
if (m_toolMode == PAINTING) {
doStroke(event);
event->accept();
}
else if (m_toolMode == PANNING) {
doPan(event);
event->accept();
}
else if (m_toolMode == PICKING) {
pick(event);
event->accept();
}
}
void KisScratchPad::beginStroke(KoPointerEvent *event)
{
KoCanvasResourceManager *resourceManager = m_resourceProvider->resourceManager();
m_helper->initPaint(event,
resourceManager,
0,
0,
m_updateScheduler,
- m_undoAdapter,
m_paintLayer,
m_paintLayer->paintDevice()->defaultBounds());
}
void KisScratchPad::doStroke(KoPointerEvent *event)
{
m_helper->paint(event);
}
void KisScratchPad::endStroke(KoPointerEvent *event)
{
Q_UNUSED(event);
m_helper->endPaint();
}
void KisScratchPad::beginPan(KoPointerEvent *event)
{
setCursor(QCursor(Qt::ClosedHandCursor));
m_panDocPoint = event->point;
}
void KisScratchPad::doPan(KoPointerEvent *event)
{
QPointF docOffset = event->point - m_panDocPoint;
m_translateTransform.translate(-docOffset.x(), -docOffset.y());
updateTransformations();
update();
}
void KisScratchPad::endPan(KoPointerEvent *event)
{
Q_UNUSED(event);
setCursor(m_cursor);
}
void KisScratchPad::pick(KoPointerEvent *event)
{
KoColor color;
if (KisToolUtils::pick(m_paintLayer->projection(), event->point.toPoint(), &color)) {
emit colorSelected(color);
}
}
void KisScratchPad::setOnScreenResolution(qreal scaleX, qreal scaleY)
{
m_scaleBorderWidth = BORDER_SIZE(qMax(scaleX, scaleY));
m_scaleTransform = QTransform::fromScale(scaleX, scaleY);
updateTransformations();
update();
}
QTransform KisScratchPad::documentToWidget() const
{
return m_translateTransform.inverted() * m_scaleTransform;
}
QTransform KisScratchPad::widgetToDocument() const
{
return m_scaleTransform.inverted() * m_translateTransform;
}
void KisScratchPad::updateTransformations()
{
m_eventFilter->setWidgetToDocumentTransform(widgetToDocument());
}
QRect KisScratchPad::imageBounds() const
{
return widgetToDocument().mapRect(rect());
}
void KisScratchPad::imageUpdated(const QRect &rect)
{
emit sigUpdateCanvas(documentToWidget().mapRect(QRectF(rect)).toAlignedRect());
}
void KisScratchPad::slotUpdateCanvas(const QRect &rect)
{
update(rect);
}
void KisScratchPad::paintEvent ( QPaintEvent * event ) {
if(!m_paintLayer) return;
QRectF imageRect = widgetToDocument().mapRect(QRectF(event->rect()));
QRect alignedImageRect =
imageRect.adjusted(-m_scaleBorderWidth, -m_scaleBorderWidth,
m_scaleBorderWidth, m_scaleBorderWidth).toAlignedRect();
QPointF offset = alignedImageRect.topLeft();
m_paintLayer->projectionPlane()->recalculate(alignedImageRect, m_paintLayer);
KisPaintDeviceSP projection = m_paintLayer->projection();
QImage image = projection->convertToQImage(m_displayProfile,
alignedImageRect.x(),
alignedImageRect.y(),
alignedImageRect.width(),
alignedImageRect.height(),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
QPainter gc(this);
gc.fillRect(event->rect(), m_checkBrush);
gc.setRenderHints(QPainter::SmoothPixmapTransform);
gc.drawImage(QRectF(event->rect()), image, imageRect.translated(-offset));
QBrush brush(Qt::lightGray);
QPen pen(brush, 1, Qt::DotLine);
gc.setPen(pen);
if (m_cutoutOverlay.isValid()) {
gc.drawRect(m_cutoutOverlay);
}
if(!isEnabled()) {
QColor color(Qt::lightGray);
color.setAlphaF(0.5);
QBrush disabledBrush(color);
gc.fillRect(event->rect(), disabledBrush);
}
gc.end();
}
void KisScratchPad::setupScratchPad(KisCanvasResourceProvider* resourceProvider,
const QColor &defaultColor)
{
m_resourceProvider = resourceProvider;
KisConfig cfg;
setDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this)));
connect(m_resourceProvider, SIGNAL(sigDisplayProfileChanged(const KoColorProfile*)),
SLOT(setDisplayProfile(const KoColorProfile*)));
connect(m_resourceProvider, SIGNAL(sigOnScreenResolutionChanged(qreal,qreal)),
SLOT(setOnScreenResolution(qreal,qreal)));
connect(this, SIGNAL(colorSelected(const KoColor&)),
m_resourceProvider, SLOT(slotSetFGColor(const KoColor&)));
m_defaultColor = KoColor(defaultColor, KoColorSpaceRegistry::instance()->rgb8());
KisPaintDeviceSP paintDevice =
new KisPaintDevice(m_defaultColor.colorSpace(), "scratchpad");
m_paintLayer = new KisPaintLayer(0, "ScratchPad", OPACITY_OPAQUE_U8, paintDevice);
m_paintLayer->setGraphListener(m_nodeListener);
m_paintLayer->paintDevice()->setDefaultBounds(new KisScratchPadDefaultBounds(this));
fillDefault();
}
void KisScratchPad::setCutoutOverlayRect(const QRect& rc)
{
m_cutoutOverlay = rc;
}
QImage KisScratchPad::cutoutOverlay() const
{
if(!m_paintLayer) return QImage();
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect rc = widgetToDocument().mapRect(m_cutoutOverlay);
QImage rawImage = paintDevice->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
QImage scaledImage = rawImage.scaled(m_cutoutOverlay.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
return scaledImage;
}
void KisScratchPad::setPresetImage(const QImage& image)
{
m_presetImage = image;
}
void KisScratchPad::paintPresetImage()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay);
QRect imageRect(QPoint(), overlayRect.size());
QImage scaledImage = m_presetImage.scaled(overlayRect.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
device->convertFromQImage(scaledImage, 0);
KisPainter painter(paintDevice);
painter.bitBlt(overlayRect.topLeft(), device, imageRect);
update();
}
void KisScratchPad::setDisplayProfile(const KoColorProfile *colorProfile)
{
if (colorProfile) {
m_displayProfile = colorProfile;
QWidget::update();
}
}
void KisScratchPad::fillDefault()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
paintDevice->setDefaultPixel(m_defaultColor);
paintDevice->clear();
update();
}
void KisScratchPad::fillGradient()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
KoAbstractGradient* gradient = m_resourceProvider->currentGradient();
QRect gradientRect = widgetToDocument().mapRect(rect());
paintDevice->clear();
KisGradientPainter painter(paintDevice);
painter.setGradient(gradient);
painter.setGradientShape(KisGradientPainter::GradientShapeLinear);
painter.paintGradient(gradientRect.topLeft(),
gradientRect.bottomRight(),
KisGradientPainter::GradientRepeatNone,
0.2, false,
gradientRect.left(), gradientRect.top(),
gradientRect.width(), gradientRect.height());
update();
}
void KisScratchPad::fillBackground()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
paintDevice->setDefaultPixel(m_resourceProvider->bgColor());
paintDevice->clear();
update();
}
void KisScratchPad::fillLayer()
{
// TODO
}
diff --git a/libs/ui/widgets/kis_spinbox_color_selector.cpp b/libs/ui/widgets/kis_spinbox_color_selector.cpp
index 7dc72ea649..8e11202f56 100644
--- a/libs/ui/widgets/kis_spinbox_color_selector.cpp
+++ b/libs/ui/widgets/kis_spinbox_color_selector.cpp
@@ -1,269 +1,259 @@
/*
* Copyright (C) Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>, (C) 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_spinbox_color_selector.h"
#include <QFormLayout>
#include <QLabel>
#include "kis_double_parse_spin_box.h"
#include "kis_int_parse_spin_box.h"
#include "kis_signal_compressor.h"
#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <KoChannelInfo.h>
#include <KoColorSpaceTraits.h>
#include <KoColorSpaceMaths.h>
#include <KoColorSpaceRegistry.h>
struct KisSpinboxColorSelector::Private
{
QList <KisIntParseSpinBox*> spinBoxList;
QList <KisDoubleParseSpinBox*> doubleSpinBoxList;
KoColor color;
const KoColorSpace *cs {0};
bool chooseAlpha {false};
};
KisSpinboxColorSelector::KisSpinboxColorSelector(QWidget *parent)
: QWidget(parent)
, m_d(new Private)
{
this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
KoColor color = KoColor();
m_d->color = color;
slotSetColorSpace(m_d->color.colorSpace());
}
KisSpinboxColorSelector::~KisSpinboxColorSelector()
{
}
void KisSpinboxColorSelector::slotSetColor(KoColor color)
{
m_d->color = color;
if (m_d->color.colorSpace() != m_d->cs) {
slotSetColorSpace(m_d->color.colorSpace());
}
updateSpinboxesWithNewValues();
}
void KisSpinboxColorSelector::slotSetColorSpace(const KoColorSpace *cs)
{
if (cs == m_d->cs) {
return;
}
m_d->cs = KoColorSpaceRegistry::instance()->colorSpace(cs->colorModelId().id(), cs->colorDepthId().id(), cs->profile());
//remake spinboxes
if (this->layout()) {
qDeleteAll(this->children());
}
m_d->spinBoxList.clear();
m_d->doubleSpinBoxList.clear();
QFormLayout *layout = new QFormLayout(this);
QList<KoChannelInfo *> channels = KoChannelInfo::displayOrderSorted(m_d->cs->channels());
Q_FOREACH (KoChannelInfo* channel, channels) {
QString inputLabel = channel->name();
- QLabel *inlb = new QLabel;
+ QLabel *inlb = new QLabel(this);
inlb->setText(inputLabel);
switch (channel->channelValueType()) {
case KoChannelInfo::UINT8: {
KisIntParseSpinBox *input = new KisIntParseSpinBox(this);
input->setMinimum(0);
input->setMaximum(0xFF);
m_d->spinBoxList.append(input);
- layout->addRow(inlb,input);
- if (input) {
- connect(input, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateFromSpinBoxes()));
- }
- if (channel->channelType()==KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
+ layout->addRow(inlb, input);
+ connect(input, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateFromSpinBoxes()));
+ if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
inlb->setVisible(false);
input->setVisible(false);
input->blockSignals(true);
}
}
break;
case KoChannelInfo::UINT16: {
KisIntParseSpinBox *input = new KisIntParseSpinBox(this);
input->setMinimum(0);
input->setMaximum(0xFFFF);
m_d->spinBoxList.append(input);
layout->addRow(inlb,input);
- if (input) {
- connect(input, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateFromSpinBoxes()));
- }
- if (channel->channelType()==KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
+ connect(input, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateFromSpinBoxes()));
+ if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
inlb->setVisible(false);
input->setVisible(false);
input->blockSignals(true);
}
}
break;
case KoChannelInfo::UINT32: {
KisIntParseSpinBox *input = new KisIntParseSpinBox(this);
input->setMinimum(0);
input->setMaximum(0xFFFFFFFF);
m_d->spinBoxList.append(input);
layout->addRow(inlb,input);
- if (input) {
- connect(input, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateFromSpinBoxes()));
- }
- if (channel->channelType()==KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
+ connect(input, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateFromSpinBoxes()));
+ if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
inlb->setVisible(false);
input->setVisible(false);
input->blockSignals(true);
}
}
break;
#ifdef HAVE_OPENEXR
case KoChannelInfo::FLOAT16: {
KisDoubleParseSpinBox *input = new KisDoubleParseSpinBox(this);
input->setMinimum(0);
input->setMaximum(KoColorSpaceMathsTraits<half>::max);
input->setSingleStep(0.1);
m_d->doubleSpinBoxList.append(input);
layout->addRow(inlb,input);
- if (input) {
- connect(input, SIGNAL(valueChanged(double)), this, SLOT(slotUpdateFromSpinBoxes()));
- }
- if (channel->channelType()==KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
+ connect(input, SIGNAL(valueChanged(double)), this, SLOT(slotUpdateFromSpinBoxes()));
+ if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
inlb->setVisible(false);
input->setVisible(false);
input->blockSignals(true);
}
}
break;
#endif
case KoChannelInfo::FLOAT32: {
KisDoubleParseSpinBox *input = new KisDoubleParseSpinBox(this);
input->setMinimum(0);
input->setMaximum(KoColorSpaceMathsTraits<float>::max);
input->setSingleStep(0.1);
m_d->doubleSpinBoxList.append(input);
layout->addRow(inlb,input);
- if (input) {
- connect(input, SIGNAL(valueChanged(double)), this, SLOT(slotUpdateFromSpinBoxes()));
- }
- if (channel->channelType()==KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
+ connect(input, SIGNAL(valueChanged(double)), this, SLOT(slotUpdateFromSpinBoxes()));
+ if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
inlb->setVisible(false);
input->setVisible(false);
input->blockSignals(true);
}
}
break;
default:
Q_ASSERT(false);
}
}
this->setLayout(layout);
}
void KisSpinboxColorSelector::createColorFromSpinboxValues()
{
KoColor newColor;
int channelcount = m_d->cs->channelCount();
quint8 *data = new quint8[m_d->cs->pixelSize()];
QVector <float> channelValues(channelcount);
channelValues.fill(1.0);
QList<KoChannelInfo *> channels = KoChannelInfo::displayOrderSorted(m_d->cs->channels());
for (int i = 0; i < (int)qAbs(m_d->cs->colorChannelCount()); i++) {
int channelposition = KoChannelInfo::displayPositionToChannelIndex(i, m_d->cs->channels());
if (channels.at(i)->channelValueType()==KoChannelInfo::UINT8 && m_d->spinBoxList.at(i)){
int value = m_d->spinBoxList.at(i)->value();
channelValues[channelposition] = KoColorSpaceMaths<quint8,float>::scaleToA(value);
} else if (channels.at(i)->channelValueType()==KoChannelInfo::UINT16 && m_d->spinBoxList.at(i)){
channelValues[channelposition] = KoColorSpaceMaths<quint16,float>::scaleToA(m_d->spinBoxList.at(i)->value());
} else if ((channels.at(i)->channelValueType()==KoChannelInfo::FLOAT16 ||
channels.at(i)->channelValueType()==KoChannelInfo::FLOAT32 ||
channels.at(i)->channelValueType()==KoChannelInfo::FLOAT64) && m_d->doubleSpinBoxList.at(i)) {
channelValues[channelposition] = m_d->doubleSpinBoxList.at(i)->value();
}
}
m_d->cs->fromNormalisedChannelsValue(data, channelValues);
newColor.setColor(data, m_d->cs);
newColor.setOpacity(m_d->color.opacityU8());
m_d->color = newColor;
}
void KisSpinboxColorSelector::slotUpdateFromSpinBoxes()
{
createColorFromSpinboxValues();
emit sigNewColor(m_d->color);
}
void KisSpinboxColorSelector::updateSpinboxesWithNewValues()
{
int channelcount = m_d->cs->channelCount();
QVector <float> channelValues(channelcount);
channelValues.fill(1.0);
m_d->cs->normalisedChannelsValue(m_d->color.data(), channelValues);
QList<KoChannelInfo *> channels = KoChannelInfo::displayOrderSorted(m_d->cs->channels());
int i;
/*while (QLayoutItem *item = this->layout()->takeAt(0))
{
item->widget()->blockSignals(true);
}*/
for (i=0; i<m_d->spinBoxList.size(); i++) {
m_d->spinBoxList.at(i)->blockSignals(true);
}
for (i=0; i<m_d->doubleSpinBoxList.size(); i++) {
m_d->doubleSpinBoxList.at(i)->blockSignals(true);
}
for (i = 0; i < (int)qAbs(m_d->cs->colorChannelCount()); i++) {
int channelposition = KoChannelInfo::displayPositionToChannelIndex(i, m_d->cs->channels());
if (channels.at(i)->channelValueType() == KoChannelInfo::UINT8 && m_d->spinBoxList.at(i)) {
int value = KoColorSpaceMaths<float, quint8>::scaleToA(channelValues[channelposition]);
m_d->spinBoxList.at(i)->setValue(value);
} else if (channels.at(i)->channelValueType() == KoChannelInfo::UINT16 && m_d->spinBoxList.at(i)) {
m_d->spinBoxList.at(i)->setValue(KoColorSpaceMaths<float, quint16>::scaleToA(channelValues[channelposition]));
} else if ((channels.at(i)->channelValueType()==KoChannelInfo::FLOAT16 ||
channels.at(i)->channelValueType()==KoChannelInfo::FLOAT32 ||
channels.at(i)->channelValueType()==KoChannelInfo::FLOAT64) && m_d->doubleSpinBoxList.at(i)) {
m_d->doubleSpinBoxList.at(i)->setValue(channelValues[channelposition]);
}
}
for (i=0; i<m_d->spinBoxList.size(); i++) {
m_d->spinBoxList.at(i)->blockSignals(false);
}
for (i=0; i<m_d->doubleSpinBoxList.size(); i++) {
m_d->doubleSpinBoxList.at(i)->blockSignals(false);
}
/*while (QLayoutItem *item = this->layout()->takeAt(0))
{
item->widget()->blockSignals(false);
}*/
}
diff --git a/libs/widgets/KoZoomInput.cpp b/libs/widgets/KoZoomInput.cpp
index 1e384614d2..1716c227e8 100644
--- a/libs/widgets/KoZoomInput.cpp
+++ b/libs/widgets/KoZoomInput.cpp
@@ -1,146 +1,146 @@
/* Copyright 2008 Peter Simonsson <peter.simonsson@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 "KoZoomInput.h"
#include <WidgetsDebug.h>
#include <klocalizedstring.h>
#include <QComboBox>
#include <QLabel>
#include <QHBoxLayout>
#include <QStyle>
#include <QStyleOption>
#include <QPainter>
#include <QPalette>
#include <QAbstractItemView>
#include <QEvent>
#include <QKeyEvent>
#include <QLineEdit>
class KoZoomInput::Private
{
public:
QComboBox* combo;
QLabel* label;
bool inside;
};
KoZoomInput::KoZoomInput(QWidget* parent)
: QStackedWidget(parent), d(new Private)
{
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
setAttribute(Qt::WA_MacMiniSize, true);
#endif
QWidget* first = new QWidget(this);
QHBoxLayout* layout = new QHBoxLayout(first);
layout->setSpacing(0);
layout->setMargin(0);
d->label = new QLabel(first);
d->label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
layout->addWidget(d->label, 10);
QLabel* icon = new QLabel(first);
QStyleOption option;
option.state = QStyle::State_Enabled;
QPixmap pixmap(16, 16);
pixmap.fill(QColor(255, 255, 255, 0));
QPainter painter(&pixmap);
painter.translate(8, 8);
style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, &painter);
icon->setPixmap(pixmap);
layout->addWidget(icon);
addWidget(first);
d->combo = new QComboBox(this);
d->combo->setMaxVisibleItems(15);
d->combo->setEditable(true);
d->combo->installEventFilter(this);
addWidget(d->combo);
d->inside = false;
connect(d->combo, SIGNAL(activated(const QString&)), this, SIGNAL(zoomLevelChanged(const QString&)));
}
KoZoomInput::~KoZoomInput()
{
delete d;
}
void KoZoomInput::enterEvent(QEvent* event)
{
Q_UNUSED(event);
d->inside = true;
if (d->combo->view())
{
d->combo->view()->removeEventFilter(this);
}
setCurrentIndex(1);
}
void KoZoomInput::leaveEvent(QEvent* event)
{
Q_UNUSED(event);
d->inside = false;
if(d->combo->view() && d->combo->view()->isVisible())
{
d->combo->view()->installEventFilter(this);
return;
}
if(!d->combo->hasFocus())
setCurrentIndex(0);
}
void KoZoomInput::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Return ||
event->key() == Qt::Key_Enter)
{
focusNextChild();
}
}
void KoZoomInput::setZoomLevels(const QStringList& levels)
{
d->combo->clear();
d->combo->addItems(levels);
}
void KoZoomInput::setCurrentZoomLevel(const QString& level)
{
d->combo->setCurrentIndex(d->combo->findText(level));
d->label->setText(level);
}
bool KoZoomInput::eventFilter(QObject* watched, QEvent* event)
{
if(watched == d->combo->view() && event->type() == QEvent::Hide)
{
focusNextChild();
setCurrentIndex(0);
}
else if (watched == d->combo && event->type() == QEvent::FocusOut &&
(d->combo->view() && !d->combo->view()->hasFocus()) && !d->inside)
{
setCurrentIndex(0);
}
return false;
}
diff --git a/libs/widgetutils/KoResourcePaths.cpp b/libs/widgetutils/KoResourcePaths.cpp
index 7b5b5e9955..f4bfb97bd0 100644
--- a/libs/widgetutils/KoResourcePaths.cpp
+++ b/libs/widgetutils/KoResourcePaths.cpp
@@ -1,531 +1,536 @@
/*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "KoResourcePaths.h"
#include <QGlobalStatic>
#include <QString>
#include <QStringList>
#include <QMap>
#include <QStandardPaths>
#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include <QApplication>
#include <QMutex>
#include "kis_debug.h"
#include "WidgetUtilsDebug.h"
Q_GLOBAL_STATIC(KoResourcePaths, s_instance);
static QString cleanup(const QString &path)
{
return QDir::cleanPath(path);
}
static QStringList cleanup(const QStringList &pathList)
{
QStringList cleanedPathList;
Q_FOREACH(const QString &path, pathList) {
cleanedPathList << cleanup(path);
}
return cleanedPathList;
}
static QString cleanupDirs(const QString &path)
{
return QDir::cleanPath(path) + QDir::separator();
}
static QStringList cleanupDirs(const QStringList &pathList)
{
QStringList cleanedPathList;
Q_FOREACH(const QString &path, pathList) {
cleanedPathList << cleanupDirs(path);
}
return cleanedPathList;
}
void appendResources(QStringList *dst, const QStringList &src, bool eliminateDuplicates)
{
Q_FOREACH (const QString &resource, src) {
QString realPath = QDir::cleanPath(resource);
if (!eliminateDuplicates || !dst->contains(realPath)) {
*dst << realPath;
}
}
}
#ifdef Q_OS_WIN
static const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
#else
static const Qt::CaseSensitivity cs = Qt::CaseSensitive;
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
#include <ApplicationServices/ApplicationServices.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#endif
QString getInstallationPrefix() {
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
QString appPath = qApp->applicationDirPath();
debugWidgetUtils << "1" << appPath;
appPath.chop(QString("MacOS/").length());
debugWidgetUtils << "2" << appPath;
bool makeInstall = QDir(appPath + "/../../../share/kritaplugins").exists();
bool inBundle = QDir(appPath + "/Resources/kritaplugins").exists();
debugWidgetUtils << "3. After make install" << makeInstall;
debugWidgetUtils << "4. In Bundle" << inBundle;
QString bundlePath;
if (inBundle) {
bundlePath = appPath + "/";
}
else if (makeInstall) {
appPath.chop(QString("Contents/").length());
bundlePath = appPath + "/../../";
}
else {
qFatal("Cannot calculate the bundle path from the app path");
}
debugWidgetUtils << ">>>>>>>>>>>" << bundlePath;
return bundlePath;
#else
return qApp->applicationDirPath() + "/../";
#endif
}
class Q_DECL_HIDDEN KoResourcePaths::Private {
public:
QMap<QString, QStringList> absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global
QMap<QString, QStringList> relatives; // Same with relative paths
QMutex relativesMutex;
QMutex absolutesMutex;
QStringList aliases(const QString &type)
{
QStringList r;
QStringList a;
relativesMutex.lock();
if (relatives.contains(type)) {
r += relatives[type];
}
relativesMutex.unlock();
debugWidgetUtils << "\trelatives" << r;
absolutesMutex.lock();
if (absolutes.contains(type)) {
a += absolutes[type];
}
debugWidgetUtils << "\tabsolutes" << a;
absolutesMutex.unlock();
return r + a;
}
QStandardPaths::StandardLocation mapTypeToQStandardPaths(const QString &type)
{
if (type == "tmp") {
return QStandardPaths::TempLocation;
}
else if (type == "appdata") {
return QStandardPaths::AppDataLocation;
}
else if (type == "data") {
return QStandardPaths::AppDataLocation;
}
else if (type == "cache") {
return QStandardPaths::CacheLocation;
}
else if (type == "locale") {
return QStandardPaths::AppDataLocation;
}
else {
return QStandardPaths::AppDataLocation;
}
}
};
KoResourcePaths::KoResourcePaths()
: d(new Private)
{
}
KoResourcePaths::~KoResourcePaths()
{
}
QString KoResourcePaths::getApplicationRoot()
{
return getInstallationPrefix();
}
void KoResourcePaths::addResourceType(const char *type, const char *basetype,
const QString &relativeName, bool priority)
{
s_instance->addResourceTypeInternal(QString::fromLatin1(type), QString::fromLatin1(basetype), relativeName, priority);
}
void KoResourcePaths::addResourceDir(const char *type, const QString &dir, bool priority)
{
s_instance->addResourceDirInternal(QString::fromLatin1(type), dir, priority);
}
QString KoResourcePaths::findResource(const char *type, const QString &fileName)
{
return cleanup(s_instance->findResourceInternal(QString::fromLatin1(type), fileName));
}
QStringList KoResourcePaths::findDirs(const char *type)
{
return cleanupDirs(s_instance->findDirsInternal(QString::fromLatin1(type)));
}
QStringList KoResourcePaths::findAllResources(const char *type,
const QString &filter,
SearchOptions options)
{
return cleanup(s_instance->findAllResourcesInternal(QString::fromLatin1(type), filter, options));
}
QStringList KoResourcePaths::resourceDirs(const char *type)
{
return cleanupDirs(s_instance->resourceDirsInternal(QString::fromLatin1(type)));
}
QString KoResourcePaths::saveLocation(const char *type, const QString &suffix, bool create)
{
return cleanupDirs(s_instance->saveLocationInternal(QString::fromLatin1(type), suffix, create));
}
QString KoResourcePaths::locate(const char *type, const QString &filename)
{
return cleanup(s_instance->locateInternal(QString::fromLatin1(type), filename));
}
QString KoResourcePaths::locateLocal(const char *type, const QString &filename, bool createDir)
{
return cleanup(s_instance->locateLocalInternal(QString::fromLatin1(type), filename, createDir));
}
void KoResourcePaths::addResourceTypeInternal(const QString &type, const QString &basetype,
const QString &relativename,
bool priority)
{
Q_UNUSED(basetype);
if (relativename.isEmpty()) return;
QString copy = relativename;
Q_ASSERT(basetype == "data");
if (!copy.endsWith(QLatin1Char('/'))) {
copy += QLatin1Char('/');
}
d->relativesMutex.lock();
QStringList &rels = d->relatives[type]; // find or insert
if (!rels.contains(copy, cs)) {
if (priority) {
rels.prepend(copy);
} else {
rels.append(copy);
}
}
d->relativesMutex.unlock();
debugWidgetUtils << "addResourceType: type" << type << "basetype" << basetype << "relativename" << relativename << "priority" << priority << d->relatives[type];
}
void KoResourcePaths::addResourceDirInternal(const QString &type, const QString &absdir, bool priority)
{
if (absdir.isEmpty() || type.isEmpty()) return;
// find or insert entry in the map
QString copy = absdir;
if (copy.at(copy.length() - 1) != QLatin1Char('/')) {
copy += QLatin1Char('/');
}
d->absolutesMutex.lock();
QStringList &paths = d->absolutes[type];
if (!paths.contains(copy, cs)) {
if (priority) {
paths.prepend(copy);
} else {
paths.append(copy);
}
}
d->absolutesMutex.unlock();
debugWidgetUtils << "addResourceDir: type" << type << "absdir" << absdir << "priority" << priority << d->absolutes[type];
}
QString KoResourcePaths::findResourceInternal(const QString &type, const QString &fileName)
{
QStringList aliases = d->aliases(type);
debugWidgetUtils << "aliases" << aliases << getApplicationRoot();
QString resource = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName, QStandardPaths::LocateFile);
if (resource.isEmpty()) {
Q_FOREACH (const QString &alias, aliases) {
resource = QStandardPaths::locate(d->mapTypeToQStandardPaths(type), alias + '/' + fileName, QStandardPaths::LocateFile);
debugWidgetUtils << "\t1" << resource;
if (QFile::exists(resource)) {
continue;
}
}
}
if (resource.isEmpty() || !QFile::exists(resource)) {
QString approot = getApplicationRoot();
Q_FOREACH (const QString &alias, aliases) {
resource = approot + "/share/" + alias + '/' + fileName;
debugWidgetUtils << "\t1" << resource;
if (QFile::exists(resource)) {
continue;
}
}
}
if (resource.isEmpty() || !QFile::exists(resource)) {
QString approot = getApplicationRoot();
Q_FOREACH (const QString &alias, aliases) {
resource = approot + "/share/krita/" + alias + '/' + fileName;
debugWidgetUtils << "\t1" << resource;
if (QFile::exists(resource)) {
continue;
}
}
}
debugWidgetUtils << "findResource: type" << type << "filename" << fileName << "resource" << resource;
Q_ASSERT(!resource.isEmpty());
return resource;
}
QStringList KoResourcePaths::findDirsInternal(const QString &type)
{
QStringList aliases = d->aliases(type);
debugWidgetUtils << type << aliases << d->mapTypeToQStandardPaths(type);
QStringList dirs;
QStringList standardDirs =
QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), "", QStandardPaths::LocateDirectory);
appendResources(&dirs, standardDirs, true);
Q_FOREACH (const QString &alias, aliases) {
QStringList aliasDirs =
QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias + '/', QStandardPaths::LocateDirectory);
appendResources(&dirs, aliasDirs, true);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_OSX
debugWidgetUtils << "MAC:" << getApplicationRoot();
QStringList bundlePaths;
bundlePaths << getApplicationRoot() + "/share/krita/" + alias;
bundlePaths << getApplicationRoot() + "/../share/krita/" + alias;
debugWidgetUtils << "bundlePaths" << bundlePaths;
appendResources(&dirs, bundlePaths, true);
Q_ASSERT(!dirs.isEmpty());
#endif
QStringList fallbackPaths;
fallbackPaths << getApplicationRoot() + "/share/" + alias;
fallbackPaths << getApplicationRoot() + "/share/krita/" + alias;
appendResources(&dirs, fallbackPaths, true);
}
debugWidgetUtils << "findDirs: type" << type << "resource" << dirs;
return dirs;
}
QStringList filesInDir(const QString &startdir, const QString & filter, bool recursive)
{
debugWidgetUtils << "filesInDir: startdir" << startdir << "filter" << filter << "recursive" << recursive;
QStringList result;
// First the entries in this path
QStringList nameFilters;
nameFilters << filter;
const QStringList fileNames = QDir(startdir).entryList(nameFilters, QDir::Files | QDir::CaseSensitive, QDir::Name);
debugWidgetUtils << "\tFound:" << fileNames.size() << ":" << fileNames;
Q_FOREACH (const QString &fileName, fileNames) {
QString file = startdir + '/' + fileName;
result << file;
}
// And then everything underneath, if recursive is specified
if (recursive) {
const QStringList entries = QDir(startdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
Q_FOREACH (const QString &subdir, entries) {
debugWidgetUtils << "\tGoing to look in subdir" << subdir << "of" << startdir;
result << filesInDir(startdir + '/' + subdir, filter, recursive);
}
}
return result;
}
QStringList KoResourcePaths::findAllResourcesInternal(const QString &type,
const QString &_filter,
SearchOptions options) const
{
debugWidgetUtils << "=====================================================";
debugWidgetUtils << type << _filter << QStandardPaths::standardLocations(d->mapTypeToQStandardPaths(type));
bool recursive = options & KoResourcePaths::Recursive;
debugWidgetUtils << "findAllResources: type" << type << "filter" << _filter << "recursive" << recursive;
QStringList aliases = d->aliases(type);
QString filter = _filter;
// In cases where the filter is like "color-schemes/*.colors" instead of "*.kpp", used with unregistered resource types
if (filter.indexOf('*') > 0) {
aliases << filter.split('*').first();
filter = '*' + filter.split('*')[1];
debugWidgetUtils << "Split up alias" << aliases << "filter" << filter;
}
QStringList resources;
if (aliases.isEmpty()) {
QStringList standardResources =
QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type),
filter, QStandardPaths::LocateFile);
appendResources(&resources, standardResources, true);
}
debugWidgetUtils << "\tresources from qstandardpaths:" << resources.size();
Q_FOREACH (const QString &alias, aliases) {
debugWidgetUtils << "\t\talias:" << alias;
QStringList dirs;
- dirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory)
- << getInstallationPrefix() + "share/" + alias + "/"
- << getInstallationPrefix() + "share/krita/" + alias + "/";
+ QFileInfo dirInfo(alias);
+ if (dirInfo.exists() && dirInfo.isDir() && dirInfo.isAbsolute()) {
+ dirs << alias;
+ } else {
+ dirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory)
+ << getInstallationPrefix() + "share/" + alias + "/"
+ << getInstallationPrefix() + "share/krita/" + alias + "/";
+ }
Q_FOREACH (const QString &dir, dirs) {
appendResources(&resources,
filesInDir(dir, filter, recursive),
true);
}
}
debugWidgetUtils << "\tresources also from aliases:" << resources.size();
QFileInfo fi(filter);
QStringList prefixResources;
prefixResources << filesInDir(getInstallationPrefix() + "share/" + fi.path(), fi.fileName(), false);
prefixResources << filesInDir(getInstallationPrefix() + "share/krita/" + fi.path(), fi.fileName(), false);
appendResources(&resources, prefixResources, true);
debugWidgetUtils << "\tresources from installation:" << resources.size();
debugWidgetUtils << "=====================================================";
return resources;
}
QStringList KoResourcePaths::resourceDirsInternal(const QString &type)
{
QStringList resourceDirs;
QStringList aliases = d->aliases(type);
Q_FOREACH (const QString &alias, aliases) {
QStringList aliasDirs;
aliasDirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
aliasDirs << getInstallationPrefix() + "share/" + alias + "/"
<< QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
aliasDirs << getInstallationPrefix() + "share/krita/" + alias + "/"
<< QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
appendResources(&resourceDirs, aliasDirs, true);
}
debugWidgetUtils << "resourceDirs: type" << type << resourceDirs;
return resourceDirs;
}
QString KoResourcePaths::saveLocationInternal(const QString &type, const QString &suffix, bool create)
{
QStringList aliases = d->aliases(type);
QString path;
if (aliases.size() > 0) {
path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)) + '/' + aliases.first();
}
else {
path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type));
if (!path.endsWith("krita")) {
path += "/krita";
}
if (!suffix.isEmpty()) {
path += "/" + suffix;
}
}
QDir d(path);
if (!d.exists() && create) {
d.mkpath(path);
}
debugWidgetUtils << "saveLocation: type" << type << "suffix" << suffix << "create" << create << "path" << path;
return path;
}
QString KoResourcePaths::locateInternal(const QString &type, const QString &filename)
{
QStringList aliases = d->aliases(type);
QStringList locations;
if (aliases.isEmpty()) {
locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), filename, QStandardPaths::LocateFile);
}
Q_FOREACH (const QString &alias, aliases) {
locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type),
(alias.endsWith('/') ? alias : alias + '/') + filename, QStandardPaths::LocateFile);
}
debugWidgetUtils << "locate: type" << type << "filename" << filename << "locations" << locations;
if (locations.size() > 0) {
return locations.first();
}
else {
return "";
}
}
QString KoResourcePaths::locateLocalInternal(const QString &type, const QString &filename, bool createDir)
{
QString path = saveLocationInternal(type, "", createDir);
debugWidgetUtils << "locateLocal: type" << type << "filename" << filename << "CreateDir" << createDir << "path" << path;
return path + '/' + filename;
}
diff --git a/libs/widgetutils/kis_action_registry.cpp b/libs/widgetutils/kis_action_registry.cpp
index ce7340ec0e..c6c8a7356f 100644
--- a/libs/widgetutils/kis_action_registry.cpp
+++ b/libs/widgetutils/kis_action_registry.cpp
@@ -1,482 +1,431 @@
/*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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 <QHash>
#include <QGlobalStatic>
#include <QFile>
#include <QDomElement>
#include <KSharedConfig>
#include <klocalizedstring.h>
#include <KisShortcutsDialog.h>
#include <KConfigGroup>
#include "kis_debug.h"
#include "KoResourcePaths.h"
#include "kis_icon_utils.h"
#include "kactioncollection.h"
#include "kactioncategory.h"
-
#include "kis_action_registry.h"
#include "kshortcutschemeshelper_p.h"
namespace {
/**
* We associate several pieces of information with each shortcut. The first
* piece of information is a QDomElement, containing the raw data from the
* .action XML file. The second and third are QKeySequences, the first of
* which is the default shortcut, the last of which is any custom shortcut.
* The last two are the KActionCollection and KActionCategory used to
* organize the shortcut editor.
*/
struct ActionInfoItem {
QDomElement xmlData;
QList<QKeySequence> defaultShortcuts;
QList<QKeySequence> customShortcuts;
QString collectionName;
QString categoryName;
};
// Convenience macros to extract text of a child node.
QString getChildContent(QDomElement xml, QString node) {
return xml.firstChildElement(node).text();
};
ActionInfoItem emptyActionInfo; // Used as default return value
// Use Krita debug logging categories instead of KDE's default qDebug() for
// harmless empty strings and translations
QString quietlyTranslate(const QString &s) {
if (s.isEmpty()) {
return s;
}
QString translatedString = i18nc("action", s.toUtf8());
if (translatedString == s) {
translatedString = i18n(s.toUtf8());
}
if (translatedString.isEmpty()) {
dbgAction << "No translation found for" << s;
return s;
}
return translatedString;
};
QList<QKeySequence> preferredShortcuts(ActionInfoItem action) {
if (action.customShortcuts.isEmpty()) {
return action.defaultShortcuts;
} else {
return action.customShortcuts;
}
};
};
class Q_DECL_HIDDEN KisActionRegistry::Private
{
public:
Private(KisActionRegistry *_q) : q(_q) {};
// This is the main place containing ActionInfoItems.
QMap<QString, ActionInfoItem> actionInfoList;
void loadActionFiles();
void loadCustomShortcuts(QString filename = QStringLiteral("kritashortcutsrc"));
ActionInfoItem &actionInfo(const QString &name) {
if (!actionInfoList.contains(name)) {
dbgAction << "Tried to look up info for unknown action" << name;
}
return actionInfoList[name];
};
KisActionRegistry *q;
QMap<QString, KActionCollection*> actionCollections;
};
Q_GLOBAL_STATIC(KisActionRegistry, s_instance);
KisActionRegistry *KisActionRegistry::instance()
{
return s_instance;
};
KisActionRegistry::KisActionRegistry()
: d(new KisActionRegistry::Private(this))
{
d->loadActionFiles();
KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes");
QString schemeName = cg.readEntry("Current Scheme", "Default");
loadShortcutScheme(schemeName);
loadCustomShortcuts();
}
-QList<QKeySequence> KisActionRegistry::getCustomShortcut(const QString &name)
-{
- return d->actionInfo(name).customShortcuts;
-};
-
-QList<QKeySequence> KisActionRegistry::getPreferredShortcut(const QString &name)
-{
- return preferredShortcuts(d->actionInfo(name));
-};
-
-QString KisActionRegistry::getCategory(const QString &name)
-{
- return d->actionInfo(name).categoryName;
-};
-
-QStringList KisActionRegistry::allActions()
-{
- return d->actionInfoList.keys();
-};
-
-KActionCollection * KisActionRegistry::getDefaultCollection()
-{
- return d->actionCollections.value("Krita");
-};
-
void KisActionRegistry::addAction(const QString &name, QAction *a)
{
auto info = d->actionInfo(name);
KActionCollection *collection = d->actionCollections.value(info.collectionName);
if (!collection) {
dbgAction << "No collection found for action" << name;
return;
}
if (collection->action(name)) {
dbgAction << "duplicate action" << name << "in collection" << collection->componentName();
}
else {
}
collection->addCategorizedAction(name, a, info.categoryName);
};
void KisActionRegistry::notifySettingsUpdated()
{
d->loadCustomShortcuts();
};
void KisActionRegistry::loadCustomShortcuts(const QString &path)
{
if (path.isEmpty()) {
d->loadCustomShortcuts();
} else {
d->loadCustomShortcuts(path);
}
};
void KisActionRegistry::loadShortcutScheme(const QString &schemeName)
{
// Load scheme file
if (schemeName != QStringLiteral("Default")) {
QString schemeFileName = KShortcutSchemesHelper::schemeFileLocations().value(schemeName);
if (schemeFileName.isEmpty()) {
return;
}
KConfig schemeConfig(schemeFileName, KConfig::SimpleConfig);
applyShortcutScheme(&schemeConfig);
} else {
// Apply default scheme, updating KisActionRegistry data
applyShortcutScheme();
}
}
QAction * KisActionRegistry::makeQAction(const QString &name, QObject *parent)
{
QAction * a = new QAction(parent);
if (!d->actionInfoList.contains(name)) {
dbgAction << "Warning: requested data for unknown action" << name;
return a;
}
propertizeAction(name, a);
return a;
};
void KisActionRegistry::setupDialog(KisShortcutsDialog *dlg)
{
for (auto i = d->actionCollections.constBegin(); i != d->actionCollections.constEnd(); i++ ) {
dlg->addCollection(i.value(), i.key());
}
}
void KisActionRegistry::settingsPageSaved()
{
// For now, custom shortcuts are dealt with by writing to file and reloading.
loadCustomShortcuts();
// Announce UI should reload current shortcuts.
emit shortcutsUpdated();
}
void KisActionRegistry::applyShortcutScheme(const KConfigBase *config)
{
// First, update the things in KisActionRegistry
if (config == 0) {
// Use default shortcut scheme. Simplest just to reload everything.
d->actionInfoList.clear();
d->loadActionFiles();
loadCustomShortcuts();
} else {
const auto schemeEntries = config->group(QStringLiteral("Shortcuts")).entryMap();
// Load info item for each shortcut, reset custom shortcuts
auto it = schemeEntries.constBegin();
while (it != schemeEntries.end()) {
ActionInfoItem &info = d->actionInfo(it.key());
if (!it.value().isEmpty())
info.defaultShortcuts = QKeySequence::listFromString(it.value());
it++;
}
}
}
void KisActionRegistry::updateShortcut(const QString &name, QAction *action)
{
const ActionInfoItem &info = d->actionInfo(name);
action->setShortcuts(preferredShortcuts(info));
action->setProperty("defaultShortcuts", qVariantFromValue(info.defaultShortcuts));
}
bool KisActionRegistry::propertizeAction(const QString &name, QAction * a)
{
const ActionInfoItem info = d->actionInfo(name);
QDomElement actionXml = info.xmlData;
if (actionXml.text().isEmpty()) {
dbgAction << "No XML data found for action" << name;
return false;
}
// i18n requires converting format from QString.
auto getChildContent_i18n = [=](QString node){return quietlyTranslate(getChildContent(actionXml, node));};
// Note: the fields in the .action documents marked for translation are determined by extractrc.
QString icon = getChildContent(actionXml, "icon");
QString text = getChildContent_i18n("text");
QString whatsthis = getChildContent_i18n("whatsThis");
QString toolTip = getChildContent_i18n("toolTip");
QString statusTip = getChildContent_i18n("statusTip");
QString iconText = getChildContent_i18n("iconText");
bool isCheckable = getChildContent(actionXml, "isCheckable") == QString("true");
a->setObjectName(name); // This is helpful, should be added more places in Krita
a->setIcon(KisIconUtils::loadIcon(icon.toLatin1()));
a->setText(text);
a->setObjectName(name);
a->setWhatsThis(whatsthis);
a->setToolTip(toolTip);
a->setStatusTip(statusTip);
a->setIconText(iconText);
a->setCheckable(isCheckable);
updateShortcut(name, a);
// TODO: check for colliding shortcuts in .action files either here or in loading code
#if 0
QMap<QKeySequence, QAction*> existingShortcuts;
Q_FOREACH (QAction* action, actionCollection->actions()) {
if(action->shortcut() == QKeySequence(0)) {
continue;
}
if (existingShortcuts.contains(action->shortcut())) {
dbgAction << QString("Actions %1 and %2 have the same shortcut: %3") \
.arg(action->text()) \
.arg(existingShortcuts[action->shortcut()]->text()) \
.arg(action->shortcut());
}
else {
existingShortcuts[action->shortcut()] = action;
}
}
#endif
return true;
}
QString KisActionRegistry::getActionProperty(const QString &name, const QString &property)
{
ActionInfoItem info = d->actionInfo(name);
QDomElement actionXml = info.xmlData;
if (actionXml.text().isEmpty()) {
dbgAction << "No XML data found for action" << name;
return QString();
}
return getChildContent(actionXml, property);
}
-void KisActionRegistry::writeCustomShortcuts(KConfigBase *config) const
-{
-
- KConfigGroup cg;
- if (config == 0) {
- cg = KConfigGroup(KSharedConfig::openConfig("kritashortcutsrc"),
- QStringLiteral("Shortcuts"));
- } else {
- cg = KConfigGroup(config, QStringLiteral("Shortcuts"));
- }
-
- for (auto it = d->actionInfoList.constBegin();
- it != d->actionInfoList.constEnd(); ++it) {
-
- QString actionName = it.key();
- QString s = QKeySequence::listToString(it.value().customShortcuts);
- if (s.isEmpty()) {
- cg.deleteEntry(actionName, KConfigGroup::Persistent);
- } else {
- cg.writeEntry(actionName, s, KConfigGroup::Persistent);
- }
- }
- cg.sync();
-}
-
void KisActionRegistry::Private::loadActionFiles()
{
QStringList actionDefinitions =
KoResourcePaths::findAllResources("kis_actions", "*.action", KoResourcePaths::Recursive);
// Extract actions all XML .action files.
Q_FOREACH (const QString &actionDefinition, actionDefinitions) {
QDomDocument doc;
QFile f(actionDefinition);
f.open(QFile::ReadOnly);
doc.setContent(f.readAll());
QDomElement base = doc.documentElement(); // "ActionCollection" outer group
QString collectionName = base.attribute("name");
QString version = base.attribute("version");
if (version != "2") {
errAction << ".action XML file" << actionDefinition << "has incorrect version; skipping.";
continue;
}
KActionCollection *actionCollection;
if (!actionCollections.contains(collectionName)) {
actionCollection = new KActionCollection(q, collectionName);
actionCollections.insert(collectionName, actionCollection);
dbgAction << "Adding a new action collection " << collectionName;
} else {
actionCollection = actionCollections.value(collectionName);
}
// Loop over <Actions> nodes. Each of these corresponds to a
// KActionCategory, producing a group of actions in the shortcut dialog.
QDomElement actions = base.firstChild().toElement();
while (!actions.isNull()) {
// <text> field
QDomElement categoryTextNode = actions.firstChild().toElement();
QString categoryName = quietlyTranslate(categoryTextNode.text());
// KActionCategory *category = actionCollection->getCategory(categoryName);
// dbgAction << "Using category" << categoryName;
// <action></action> tags
QDomElement actionXml = categoryTextNode.nextSiblingElement();
// Loop over individual actions
while (!actionXml.isNull()) {
if (actionXml.tagName() == "Action") {
// Read name from format <Action name="save">
QString name = actionXml.attribute("name");
// Bad things
if (name.isEmpty()) {
errAction << "Unnamed action in definitions file " << actionDefinition;
}
else if (actionInfoList.contains(name)) {
// errAction << "NOT COOL: Duplicated action name from xml data: " << name;
}
else {
ActionInfoItem info;
info.xmlData = actionXml;
// Use empty list to signify no shortcut
QString shortcutText = getChildContent(actionXml, "shortcut");
if (!shortcutText.isEmpty())
info.defaultShortcuts << QKeySequence(shortcutText);
info.categoryName = categoryName;
info.collectionName = collectionName;
// dbgAction << "default shortcut for" << name << " - " << info.defaultShortcut;
actionInfoList.insert(name,info);
}
}
actionXml = actionXml.nextSiblingElement();
}
actions = actions.nextSiblingElement();
}
}
};
void KisActionRegistry::Private::loadCustomShortcuts(QString filename)
{
const KConfigGroup localShortcuts(KSharedConfig::openConfig(filename),
QStringLiteral("Shortcuts"));
if (!localShortcuts.exists()) {
return;
}
// Distinguish between two "null" states for custom shortcuts.
for (auto i = actionInfoList.begin(); i != actionInfoList.end(); ++i) {
if (localShortcuts.hasKey(i.key())) {
QString entry = localShortcuts.readEntry(i.key(), QString());
if (entry == QStringLiteral("none")) {
// A shortcut list with a single entry "" means the user has disabled the shortcut.
// This occurs after stealing the shortcut without assigning a new one.
i.value().customShortcuts = QList<QKeySequence>();
} else {
i.value().customShortcuts = QKeySequence::listFromString(entry);
}
} else {
// An empty shortcut list means no custom shortcut has been set.
i.value().customShortcuts = QList<QKeySequence>();
}
}
};
diff --git a/libs/widgetutils/kis_action_registry.h b/libs/widgetutils/kis_action_registry.h
index 7da8b0c391..f5303c9877 100644
--- a/libs/widgetutils/kis_action_registry.h
+++ b/libs/widgetutils/kis_action_registry.h
@@ -1,165 +1,137 @@
/*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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 <QKeySequence>
#include <QDomElement>
#include <QAction>
#include "kritawidgetutils_export.h"
class KActionCollection;
class QDomElement;
class KConfigBase;
class KisShortcutsDialog;
/**
* KisActionRegistry is intended to manage the global action configuration data
* for Krita. The data come from four sources:
* - .action files, containing static action configuration data in XML format,
* - .rc configuration files, originally from XMLGUI and now in WidgetUtils,
* - kritashortcutsrc, containing temporary shortcut configuration, and
* - .shortcuts scheme files providing sets of default shortcuts, also from XMLGUI
*
* This class can be used as a factory by calling makeQAction. It can be used to
* add standard properties such as default shortcuts and default tooltip to an
* existing action with propertizeAction. If you have a custom action class
* which needs to add other properties, you can use propertizeAction to add any
* sort of data you wish to the .action configuration file.
*
* This class is also in charge of displaying the shortcut configuration dialog.
* The interplay between this class, KActionCollection, KisShortcutsEditor and
* so on can be complex, and is sometimes synchronized by file I/O by reading
* and writing the configuration files mentioned above.
*
* It is a global static. Grab an ::instance().
*/
class KRITAWIDGETUTILS_EXPORT KisActionRegistry : public QObject
{
Q_OBJECT
public:
static KisActionRegistry *instance();
-
- /**
- * Get shortcut for an action
- */
- QList<QKeySequence> getPreferredShortcut(const QString &name);
-
- /**
- * Get shortcut for an action
- */
- QList<QKeySequence> getDefaultShortcut(const QString &name);
-
- /**
- * Get custom shortcut for an action
- */
- QList<QKeySequence> getCustomShortcut(const QString &name);
-
- /**
- * Get category name
- */
- QString getCategory(const QString &name);
-
/**
* @return value @p property for an action @p name.
*
* Allow flexible info structure for KisActions, etc.
*/
QString getActionProperty(const QString &name, const QString &property);
/**
* Saves action in a category. Note that this grabs ownership of the action.
*/
void addAction(const QString &name, QAction *a);
/**
* Produces a new QAction based on the .action data files.
*
* N.B. this action will not be saved in the registry.
*/
QAction * makeQAction(const QString &name, QObject *parent);
/**
* Fills the standard QAction properties of an action.
*
* @return true if the action was loaded successfully.
*/
bool propertizeAction(const QString &name, QAction *a);
- /**
- * @return list of actions with data available.
- */
- QStringList allActions();
-
/**
* Setup the shortcut configuration widget.
*/
void setupDialog(KisShortcutsDialog *dlg);
/**
* Called when "OK" button is pressed in settings dialog.
*/
void settingsPageSaved();
/**
* Reload custom shortcuts from kritashortcutsrc
*/
void loadCustomShortcuts(const QString &path = QString());
-
- /**
- * Write custom shortcuts to a specific file
- */
- void writeCustomShortcuts(KConfigBase *config) const;
-
-
/**
* Call after settings are changed.
*/
void notifySettingsUpdated();
+ // If config == 0, reload defaults
+ void applyShortcutScheme(const KConfigBase *config = 0);
+
/**
* Constructor. Please don't touch!
*/
KisActionRegistry();
- // Undocumented
- void updateShortcut(const QString &name, QAction *ac);
- KActionCollection * getDefaultCollection();
-
+ /**
+ * @brief loadShortcutScheme
+ * @param schemeName
+ */
void loadShortcutScheme(const QString &schemeName);
- // If config == 0, reload defaults
- void applyShortcutScheme(const KConfigBase *config = 0);
+
+ // Undocumented
+ void updateShortcut(const QString &name, QAction *ac);
Q_SIGNALS:
void shortcutsUpdated();
private:
+
class Private;
Private * const d;
};
diff --git a/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp b/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp
index 55b6e82485..fe2e517b19 100644
--- a/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp
+++ b/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp
@@ -1,321 +1,322 @@
/* 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 "KisShortcutsEditor.h"
#include "KisShortcutsEditor_p.h"
#include "kshortcutschemeshelper_p.h"
#include "config-xmlgui.h"
#include "kis_action_registry.h"
// The following is needed for KisShortcutsEditorPrivate and QTreeWidgetHack
// #include "KisShortcutsDialog_p.h"
#include <QAction>
#include <QList>
#include <QObject>
#include <QTimer>
#include <QTextDocument>
#include <QTextTable>
#include <QTextCursor>
#include <QTextTableFormat>
#include <QPrinter>
#include <QDebug>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include <kmessagebox.h>
#include "kactioncollection.h"
#include "kactioncategory.h"
#include <ktreewidgetsearchline.h>
//---------------------------------------------------------------------
// KisShortcutsEditor
//---------------------------------------------------------------------
Q_DECLARE_METATYPE(KisShortcutsEditorItem *)
KisShortcutsEditor::KisShortcutsEditor(QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts)
: QWidget(parent)
, d(new KisShortcutsEditorPrivate(this))
{
d->initGUI(actionType, allowLetterShortcuts);
}
KisShortcutsEditor::~KisShortcutsEditor()
{
delete d;
}
bool KisShortcutsEditor::isModified() const
{
// Iterate over all items
QTreeWidgetItemIterator it(d->ui.list, QTreeWidgetItemIterator::NoChildren);
for (; (*it); ++it) {
KisShortcutsEditorItem *item = dynamic_cast<KisShortcutsEditorItem *>(*it);
if (item && item->isModified()) {
return true;
}
}
return false;
}
void KisShortcutsEditor::clearCollections()
{
d->delegate->contractAll();
d->ui.list->clear();
d->actionCollections.clear();
QTimer::singleShot(0, this, SLOT(resizeColumns()));
}
void KisShortcutsEditor::clearSearch()
{
d->ui.searchFilter->searchLine()->clear();
}
void KisShortcutsEditor::addCollection(KActionCollection *collection, const QString &title)
{
// KXmlGui add action collections unconditionally. If some plugin doesn't
// provide actions we don't want to create empty subgroups.
if (collection->isEmpty()) {
return;
}
// Pause updating.
setUpdatesEnabled(false);
/**
* Forward this actioncollection to the delegate which will do conflict checking.
* This _replaces_ existing collections in the delegate.
*/
d->actionCollections.append(collection);
d->delegate->setCheckActionCollections(d->actionCollections);
// Determine how we should label this collection in the widget.
QString collectionTitle;
if (!title.isEmpty()) {
collectionTitle = title;
} else {
// Use the programName (Translated).
collectionTitle = collection->componentDisplayName();
}
// Create the collection root node.
QTreeWidgetItem *hierarchy[3];
hierarchy[KisShortcutsEditorPrivate::Root] = d->ui.list->invisibleRootItem();
hierarchy[KisShortcutsEditorPrivate::Program] =
d->findOrMakeItem(hierarchy[KisShortcutsEditorPrivate::Root], collectionTitle);
hierarchy[KisShortcutsEditorPrivate::Action] = 0;
// Remember which actions we have seen. We will be adding categorized
// actions first, so this will help us keep track of which actions haven't
// been categorized yet, so we can add them as uncategorized at the end.
QSet<QAction *> actionsSeen;
// Add a subtree for each category? Perhaps easier to think that this
// doesn't exist. Basically you add KActionCategory as a QObject child of
// KActionCollection, and then tag objects as belonging to the category.
foreach (KActionCategory *category, collection->categories()) {
// Don't display empty categories.
if (category->actions().isEmpty()) {
continue;
}
hierarchy[KisShortcutsEditorPrivate::Action] =
d->findOrMakeItem(hierarchy[KisShortcutsEditorPrivate::Program], category->text());
// Add every item from the category.
foreach (QAction *action, category->actions()) {
actionsSeen.insert(action);
d->addAction(action, hierarchy, KisShortcutsEditorPrivate::Action);
}
// Fold in each KActionCategory by default.
hierarchy[KisShortcutsEditorPrivate::Action]->setExpanded(false);
}
// Finally, tack on any uncategorized actions.
foreach (QAction *action, collection->actions()) {
if (!actionsSeen.contains(action)) {
d->addAction(action, hierarchy, KisShortcutsEditorPrivate::Program);
}
}
// sort the list
d->ui.list->sortItems(Name, Qt::AscendingOrder);
// Now turn on updating again.
setUpdatesEnabled(true);
QTimer::singleShot(0, this, SLOT(resizeColumns()));
}
void KisShortcutsEditor::clearConfiguration()
{
d->clearConfiguration();
}
void KisShortcutsEditor::importConfiguration(KConfigBase *config, bool isScheme)
{
Q_ASSERT(config);
if (!config) {
return;
}
// If this is a shortcut scheme, apply it
if (isScheme) {
KisActionRegistry::instance()->applyShortcutScheme(config);
}
// Update the dialog entry items
const KConfigGroup schemeShortcuts(config, QStringLiteral("Shortcuts"));
for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
if (!(*it)->parent()) {
continue;
}
KisShortcutsEditorItem *item = static_cast<KisShortcutsEditorItem *>(*it);
const QString actionId = item->data(Id).toString();
if (!schemeShortcuts.hasKey(actionId))
continue;
QList<QKeySequence> sc = QKeySequence::listFromString(schemeShortcuts.readEntry(actionId, QString()));
d->changeKeyShortcut(item, LocalPrimary, primarySequence(sc));
d->changeKeyShortcut(item, LocalAlternate, alternateSequence(sc));
}
}
void KisShortcutsEditor::exportConfiguration(KConfigBase *config) const
{
Q_ASSERT(config);
if (!config) {
return;
}
if (d->actionTypes) {
KConfigGroup group(config,QStringLiteral("Shortcuts"));
foreach (KActionCollection *collection, d->actionCollections) {
collection->writeSettings(&group, true);
}
}
KisActionRegistry::instance()->notifySettingsUpdated();
}
void KisShortcutsEditor::saveShortcuts(KConfigGroup *config) const
{
+ qDebug() << "Saving shortcuts";
// This is a horrible mess with pointers...
KConfigGroup cg;
if (config == 0) {
cg = KConfigGroup(KSharedConfig::openConfig("kritashortcutsrc"),
QStringLiteral("Shortcuts"));
config = &cg;
}
// Clear and reset temporary shortcuts
config->deleteGroup();
foreach (KActionCollection *collection, d->actionCollections) {
collection->writeSettings(config, false);
}
KisActionRegistry::instance()->notifySettingsUpdated();
}
//slot
void KisShortcutsEditor::resizeColumns()
{
for (int i = 0; i < d->ui.list->columnCount(); i++) {
d->ui.list->resizeColumnToContents(i);
}
}
void KisShortcutsEditor::commit()
{
for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
if (KisShortcutsEditorItem *item = dynamic_cast<KisShortcutsEditorItem *>(*it)) {
item->commit();
}
}
}
void KisShortcutsEditor::save()
{
saveShortcuts();
commit(); // Not doing this would be bad
}
void KisShortcutsEditor::undo()
{
// TODO: is this working?
for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
if (KisShortcutsEditorItem *item = dynamic_cast<KisShortcutsEditorItem *>(*it)) {
item->undo();
}
}
}
void KisShortcutsEditor::allDefault()
{
d->allDefault();
}
void KisShortcutsEditor::printShortcuts() const
{
d->printShortcuts();
}
void KisShortcutsEditor::searchUpdated(QString s)
{
if (s.isEmpty()) {
// Reset the tree area
d->ui.list->collapseAll();
d->ui.list->expandToDepth(0);
} else {
d->ui.list->expandAll();
}
}
KisShortcutsEditor::ActionTypes KisShortcutsEditor::actionTypes() const
{
return d->actionTypes;
}
void KisShortcutsEditor::setActionTypes(ActionTypes actionTypes)
{
d->setActionTypes(actionTypes);
}
#include "moc_KisShortcutsEditor.cpp"
diff --git a/libs/widgetutils/xmlgui/kkeysequencewidget.cpp b/libs/widgetutils/xmlgui/kkeysequencewidget.cpp
index 9d724cfa47..a222ec85ca 100644
--- a/libs/widgetutils/xmlgui/kkeysequencewidget.cpp
+++ b/libs/widgetutils/xmlgui/kkeysequencewidget.cpp
@@ -1,786 +1,786 @@
/* This file is part of the KDE libraries
Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
Copyright (C) 2001 Ellis Whitehead <ellis@kde.org>
Copyright (C) 2007 Andreas Hartmetz <ahartmetz@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 "kkeysequencewidget.h"
#include "kkeysequencewidget_p.h"
#include "config-xmlgui.h"
#include <QAction>
#include <QKeyEvent>
#include <QTimer>
#include <QtCore/QHash>
#include <QHBoxLayout>
#include <QToolButton>
#include <QApplication>
#include <QDebug>
#include <klocalizedstring.h>
#include <kmessagebox.h>
#include <kkeyserver.h>
#include "kactioncollection.h"
#include <kis_icon_utils.h>
uint qHash(const QKeySequence &seq)
{
return qHash(seq.toString());
}
class KKeySequenceWidgetPrivate
{
public:
KKeySequenceWidgetPrivate(KKeySequenceWidget *q);
void init();
static QKeySequence appendToSequence(const QKeySequence &seq, int keyQt);
static bool isOkWhenModifierless(int keyQt);
void updateShortcutDisplay();
void startRecording();
/**
* Conflicts the key sequence @a seq with a current standard
* shortcut?
*
* Pops up a dialog asking overriding the conflict is OK.
*/
bool conflictWithStandardShortcuts(const QKeySequence &seq);
/**
* Conflicts the key sequence @a seq with a current local
* shortcut?
*/
bool conflictWithLocalShortcuts(const QKeySequence &seq);
/**
* Conflicts the key sequence @a seq conflict with Windows shortcut keys?
*/
bool conflictWithGlobalShortcuts(const QKeySequence &seq);
/**
* Get permission to steal the shortcut @seq from the standard shortcut @a std.
*/
bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq);
bool checkAgainstStandardShortcuts() const
{
return checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts;
}
bool checkAgainstGlobalShortcuts() const
{
return checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts;
}
bool checkAgainstLocalShortcuts() const
{
return checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts;
}
void controlModifierlessTimout()
{
if (nKey != 0 && !modifierKeys) {
// No modifier key pressed currently. Start the timout
modifierlessTimeout.start(600);
} else {
// A modifier is pressed. Stop the timeout
modifierlessTimeout.stop();
}
}
void cancelRecording()
{
keySequence = oldKeySequence;
doneRecording();
}
//private slot
void doneRecording(bool validate = true);
//members
KKeySequenceWidget *const q;
QHBoxLayout *layout;
KKeySequenceButton *keyButton;
QToolButton *clearButton;
QKeySequence keySequence;
QKeySequence oldKeySequence;
QTimer modifierlessTimeout;
bool allowModifierless;
uint nKey;
uint modifierKeys;
bool isRecording;
bool multiKeyShortcutsAllowed;
QString componentName;
//! Check the key sequence against KStandardShortcut::find()
KKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes;
/**
* The list of action to check against for conflict shortcut
*/
QList<QAction *> checkList; // deprecated
/**
* The list of action collections to check against for conflict shortcut
*/
QList<KActionCollection *> checkActionCollections;
/**
* The action to steal the shortcut from.
*/
QList<QAction *> stealActions;
bool stealShortcuts(const QList<QAction *> &actions, const QKeySequence &seq);
void wontStealShortcut(QAction *item, const QKeySequence &seq);
};
KKeySequenceWidgetPrivate::KKeySequenceWidgetPrivate(KKeySequenceWidget *q)
: q(q)
, layout(0)
, keyButton(0)
, clearButton(0)
, allowModifierless(false)
, nKey(0)
, modifierKeys(0)
, isRecording(false)
, multiKeyShortcutsAllowed(true)
, componentName()
, checkAgainstShortcutTypes(KKeySequenceWidget::LocalShortcuts | KKeySequenceWidget::GlobalShortcuts)
, stealActions()
{}
bool KKeySequenceWidgetPrivate::stealShortcuts(
const QList<QAction *> &actions,
const QKeySequence &seq)
{
const int listSize = actions.size();
QString title = i18ncp("%1 is the number of conflicts", "Shortcut Conflict", "Shortcut Conflicts", listSize);
QString conflictingShortcuts;
Q_FOREACH (const QAction *action, actions) {
conflictingShortcuts += i18n("Shortcut '%1' for action '%2'\n",
action->shortcut().toString(QKeySequence::NativeText),
KLocalizedString::removeAcceleratorMarker(action->text()));
}
QString message = i18ncp("%1 is the number of ambigious shortcut clashes (hidden)",
"The \"%2\" shortcut is ambiguous with the following shortcut.\n"
"Do you want to assign an empty shortcut to this action?\n"
"%3",
"The \"%2\" shortcut is ambiguous with the following shortcuts.\n"
"Do you want to assign an empty shortcut to these actions?\n"
"%3",
listSize,
seq.toString(QKeySequence::NativeText),
conflictingShortcuts);
if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) {
return false;
}
return true;
}
void KKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq)
{
QString title(i18n("Shortcut conflict"));
QString msg(i18n("<qt>The '%1' key combination is already used by the <b>%2</b> action.<br>"
"Please select a different one.</qt>", seq.toString(QKeySequence::NativeText),
KLocalizedString::removeAcceleratorMarker(item->text())));
KMessageBox::sorry(q, msg, title);
}
KKeySequenceWidget::KKeySequenceWidget(QWidget *parent)
: QWidget(parent),
d(new KKeySequenceWidgetPrivate(this))
{
d->init();
setFocusProxy(d->keyButton);
connect(d->keyButton, SIGNAL(clicked()), this, SLOT(captureKeySequence()));
connect(d->clearButton, SIGNAL(clicked()), this, SLOT(clearKeySequence()));
connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording()));
//TODO: how to adopt style changes at runtime?
/*QFont modFont = d->clearButton->font();
modFont.setStyleHint(QFont::TypeWriter);
d->clearButton->setFont(modFont);*/
d->updateShortcutDisplay();
}
void KKeySequenceWidgetPrivate::init()
{
layout = new QHBoxLayout(q);
layout->setMargin(0);
keyButton = new KKeySequenceButton(this, q);
keyButton->setFocusPolicy(Qt::StrongFocus);
keyButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("configure")));
keyButton->setToolTip(i18n("Click on the button, then enter the shortcut like you would in the program.\nExample for Ctrl+A: hold the Ctrl key and press A."));
layout->addWidget(keyButton);
clearButton = new QToolButton(q);
layout->addWidget(clearButton);
if (qApp->isLeftToRight()) {
clearButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("edit-clear-locationbar-rtl")));
} else {
clearButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("edit-clear-locationbar-ltr")));
}
}
KKeySequenceWidget::~KKeySequenceWidget()
{
delete d;
}
KKeySequenceWidget::ShortcutTypes KKeySequenceWidget::checkForConflictsAgainst() const
{
return d->checkAgainstShortcutTypes;
}
void KKeySequenceWidget::setComponentName(const QString &componentName)
{
d->componentName = componentName;
}
bool KKeySequenceWidget::multiKeyShortcutsAllowed() const
{
return d->multiKeyShortcutsAllowed;
}
void KKeySequenceWidget::setMultiKeyShortcutsAllowed(bool allowed)
{
d->multiKeyShortcutsAllowed = allowed;
}
void KKeySequenceWidget::setCheckForConflictsAgainst(ShortcutTypes types)
{
d->checkAgainstShortcutTypes = types;
}
void KKeySequenceWidget::setModifierlessAllowed(bool allow)
{
d->allowModifierless = allow;
}
bool KKeySequenceWidget::isKeySequenceAvailable(const QKeySequence &keySequence) const
{
if (keySequence.isEmpty()) {
// qDebug() << "Key sequence" << keySequence.toString() << "is empty and available.";
return true;
}
bool hasConflict = (d->conflictWithLocalShortcuts(keySequence)
|| d->conflictWithGlobalShortcuts(keySequence)
|| d->conflictWithStandardShortcuts(keySequence));
if (hasConflict) {
/* qInfo() << "Key sequence" << keySequence.toString() << "has an unresolvable conflict." <<
QString("Local conflict: %1. Windows conflict: %2. Standard Shortcut conflict: %3") \
.arg(d->conflictWithLocalShortcuts(keySequence)) \
.arg(d->conflictWithGlobalShortcuts(keySequence)) \
.arg(d->conflictWithStandardShortcuts(keySequence)); */
}
return !(hasConflict);
}
bool KKeySequenceWidget::isModifierlessAllowed()
{
return d->allowModifierless;
}
void KKeySequenceWidget::setClearButtonShown(bool show)
{
d->clearButton->setVisible(show);
}
void KKeySequenceWidget::setCheckActionCollections(const QList<KActionCollection *> &actionCollections)
{
d->checkActionCollections = actionCollections;
}
//slot
void KKeySequenceWidget::captureKeySequence()
{
d->startRecording();
}
QKeySequence KKeySequenceWidget::keySequence() const
{
return d->keySequence;
}
//slot
void KKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate)
{
// oldKeySequence holds the key sequence before recording started, if setKeySequence()
// is called while not recording then set oldKeySequence to the existing sequence so
// that the keySequenceChanged() signal is emitted if the new and previous key
// sequences are different
if (!d->isRecording) {
d->oldKeySequence = d->keySequence;
}
d->keySequence = seq;
d->doneRecording(validate == Validate);
}
//slot
void KKeySequenceWidget::clearKeySequence()
{
setKeySequence(QKeySequence());
}
//slot
void KKeySequenceWidget::applyStealShortcut()
{
QSet<KActionCollection *> changedCollections;
Q_FOREACH (QAction *stealAction, d->stealActions) {
// Stealing a shortcut means setting it to an empty one.
stealAction->setShortcuts(QList<QKeySequence>());
// The following code will find the action we are about to
// steal from and save it's actioncollection.
KActionCollection *parentCollection = 0;
foreach (KActionCollection *collection, d->checkActionCollections) {
if (collection->actions().contains(stealAction)) {
parentCollection = collection;
break;
}
}
// Remember the changed collection
if (parentCollection) {
changedCollections.insert(parentCollection);
}
}
Q_FOREACH (KActionCollection *col, changedCollections) {
col->writeSettings();
}
d->stealActions.clear();
}
void KKeySequenceWidgetPrivate::startRecording()
{
nKey = 0;
modifierKeys = 0;
oldKeySequence = keySequence;
keySequence = QKeySequence();
isRecording = true;
keyButton->grabKeyboard();
if (!QWidget::keyboardGrabber()) {
qWarning() << "Failed to grab the keyboard! Most likely qt's nograb option is active";
}
keyButton->setDown(true);
updateShortcutDisplay();
}
void KKeySequenceWidgetPrivate::doneRecording(bool validate)
{
modifierlessTimeout.stop();
isRecording = false;
keyButton->releaseKeyboard();
keyButton->setDown(false);
stealActions.clear();
if (keySequence == oldKeySequence) {
// The sequence hasn't changed
updateShortcutDisplay();
return;
}
if (validate && !q->isKeySequenceAvailable(keySequence)) {
// The sequence had conflicts and the user said no to stealing it
keySequence = oldKeySequence;
} else {
emit q->keySequenceChanged(keySequence);
}
updateShortcutDisplay();
}
bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence)
{
// This could hold some OS-specific stuff, or it could be linked back with
// the KDE global shortcut code at some point in the future.
#ifdef Q_OS_WIN
#else
#endif
Q_UNUSED(keySequence);
return false;
}
bool shortcutsConflictWith(const QList<QKeySequence> &shortcuts, const QKeySequence &needle)
{
if (needle.isEmpty() || needle.toString(QKeySequence::NativeText).isEmpty()) {
return false;
}
foreach (const QKeySequence &sequence, shortcuts) {
if (sequence.isEmpty()) {
continue;
}
if (sequence.matches(needle) != QKeySequence::NoMatch
|| needle.matches(sequence) != QKeySequence::NoMatch) {
return true;
}
}
return false;
}
bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(const QKeySequence &keySequence)
{
if (!(checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts)) {
return false;
}
// We have actions both in the deprecated checkList and the
// checkActionCollections list. Add all the actions to a single list to
// be able to process them in a single loop below.
// Note that this can't be done in setCheckActionCollections(), because we
// keep pointers to the action collections, and between the call to
// setCheckActionCollections() and this function some actions might already be
// removed from the collection again.
QList<QAction *> allActions;
allActions += checkList;
foreach (KActionCollection *collection, checkActionCollections) {
allActions += collection->actions();
}
// Because of multikey shortcuts we can have clashes with many shortcuts.
//
// Example 1:
//
// Application currently uses 'CTRL-X,a', 'CTRL-X,f' and 'CTRL-X,CTRL-F'
// and the user wants to use 'CTRL-X'. 'CTRL-X' will only trigger as
// 'activatedAmbiguously()' for obvious reasons.
//
// Example 2:
//
// Application currently uses 'CTRL-X'. User wants to use 'CTRL-X,CTRL-F'.
// This will shadow 'CTRL-X' for the same reason as above.
//
// Example 3:
//
// Some weird combination of Example 1 and 2 with three shortcuts using
// 1/2/3 key shortcuts. I think you can imagine.
QList<QAction *> conflictingActions;
//find conflicting shortcuts with existing actions
foreach (QAction *qaction, allActions) {
if (shortcutsConflictWith(qaction->shortcuts(), keySequence)) {
// A conflict with a KAction. If that action is configurable
// ask the user what to do. If not reject this keySequence.
if (checkActionCollections.first()->isShortcutsConfigurable(qaction)) {
conflictingActions.append(qaction);
} else {
wontStealShortcut(qaction, keySequence);
return true;
}
}
}
if (conflictingActions.isEmpty()) {
// No conflicting shortcuts found.
return false;
}
if (stealShortcuts(conflictingActions, keySequence)) {
stealActions = conflictingActions;
// Announce that the user agreed to override the other shortcut
Q_FOREACH (QAction *stealAction, stealActions) {
emit q->stealShortcut(
keySequence,
stealAction);
}
return false;
} else {
return true;
}
}
bool KKeySequenceWidgetPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence)
{
if (!(checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts)) {
return false;
}
KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence);
if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) {
return true;
}
return false;
}
bool KKeySequenceWidgetPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq)
{
QString title = i18n("Conflict with Standard Application Shortcut");
QString message = i18n("The '%1' key combination is also used for the standard action "
"\"%2\" that some applications use.\n"
"Do you really want to use it as a global shortcut as well?",
seq.toString(QKeySequence::NativeText), KStandardShortcut::label(std));
if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) {
return false;
}
return true;
}
void KKeySequenceWidgetPrivate::updateShortcutDisplay()
{
//empty string if no non-modifier was pressed
QString s = keySequence.toString(QKeySequence::NativeText);
s.replace(QLatin1Char('&'), QStringLiteral("&&"));
if (isRecording) {
if (modifierKeys) {
if (!s.isEmpty()) {
s.append(QLatin1Char(','));
}
if (modifierKeys & Qt::META) {
s += KKeyServer::modToStringUser(Qt::META) + QLatin1Char('+');
}
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_OSX)
if (modifierKeys & Qt::ALT) {
s += KKeyServer::modToStringUser(Qt::ALT) + QLatin1Char('+');
}
if (modifierKeys & Qt::CTRL) {
s += KKeyServer::modToStringUser(Qt::CTRL) + QLatin1Char('+');
}
#else
if (modifierKeys & Qt::CTRL) {
s += KKeyServer::modToStringUser(Qt::CTRL) + QLatin1Char('+');
}
if (modifierKeys & Qt::ALT) {
s += KKeyServer::modToStringUser(Qt::ALT) + QLatin1Char('+');
}
#endif
if (modifierKeys & Qt::SHIFT) {
s += KKeyServer::modToStringUser(Qt::SHIFT) + QLatin1Char('+');
}
} else if (nKey == 0) {
s = i18nc("What the user inputs now will be taken as the new shortcut", "Input");
}
//make it clear that input is still going on
s.append(QStringLiteral(" ..."));
}
if (s.isEmpty()) {
s = i18nc("No shortcut defined", "None");
}
s.prepend(QLatin1Char(' '));
s.append(QLatin1Char(' '));
keyButton->setText(s);
}
KKeySequenceButton::~KKeySequenceButton()
{
}
//prevent Qt from special casing Tab and Backtab
bool KKeySequenceButton::event(QEvent *e)
{
if (d->isRecording && e->type() == QEvent::KeyPress) {
keyPressEvent(static_cast<QKeyEvent *>(e));
return true;
}
// The shortcut 'alt+c' ( or any other dialog local action shortcut )
// ended the recording and triggered the action associated with the
// action. In case of 'alt+c' ending the dialog. It seems that those
// ShortcutOverride events get sent even if grabKeyboard() is active.
if (d->isRecording && e->type() == QEvent::ShortcutOverride) {
e->accept();
return true;
}
if (d->isRecording && e->type() == QEvent::ContextMenu) {
// is caused by Qt::Key_Menu
e->accept();
return true;
}
return QPushButton::event(e);
}
void KKeySequenceButton::keyPressEvent(QKeyEvent *e)
{
int keyQt = e->key();
if (keyQt == -1) {
// Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key.
// We cannot do anything useful with those (several keys have -1, indistinguishable)
// and QKeySequence.toString() will also yield a garbage string.
KMessageBox::sorry(this,
i18n("The key you just pressed is not supported by Qt."),
i18n("Unsupported Key"));
return d->cancelRecording();
}
uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
//don't have the return or space key appear as first key of the sequence when they
//were pressed to start editing - catch and them and imitate their effect
if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) {
d->startRecording();
d->modifierKeys = newModifiers;
d->updateShortcutDisplay();
return;
}
// We get events even if recording isn't active.
if (!d->isRecording) {
return QPushButton::keyPressEvent(e);
}
e->accept();
d->modifierKeys = newModifiers;
switch (keyQt) {
case Qt::Key_AltGr: //or else we get unicode salad
return;
case Qt::Key_Shift:
case Qt::Key_Control:
case Qt::Key_Alt:
case Qt::Key_Meta:
case Qt::Key_Super_L:
case Qt::Key_Super_R:
d->controlModifierlessTimout();
d->updateShortcutDisplay();
break;
default:
if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) {
// It's the first key and no modifier pressed. Check if this is
// allowed
if (!(KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt)
|| d->allowModifierless)) {
// No it's not
return;
}
}
// We now have a valid key press.
if (keyQt) {
if ((keyQt == Qt::Key_Backtab) && (d->modifierKeys & Qt::SHIFT)) {
keyQt = Qt::Key_Tab | d->modifierKeys;
} else if (KKeyServer::isShiftAsModifierAllowed(keyQt)) {
keyQt |= d->modifierKeys;
} else {
keyQt |= (d->modifierKeys & ~Qt::SHIFT);
}
if (d->nKey == 0) {
d->keySequence = QKeySequence(keyQt);
} else {
d->keySequence =
KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt);
}
d->nKey++;
if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) {
d->doneRecording();
return;
}
d->controlModifierlessTimout();
d->updateShortcutDisplay();
}
}
}
void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e)
{
if (e->key() == -1) {
// ignore garbage, see keyPressEvent()
return;
}
if (!d->isRecording) {
return QPushButton::keyReleaseEvent(e);
}
e->accept();
uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
//if a modifier that belongs to the shortcut was released...
if ((newModifiers & d->modifierKeys) < d->modifierKeys) {
d->modifierKeys = newModifiers;
d->controlModifierlessTimout();
d->updateShortcutDisplay();
}
}
//static
QKeySequence KKeySequenceWidgetPrivate::appendToSequence(const QKeySequence &seq, int keyQt)
{
switch (seq.count()) {
case 0:
return QKeySequence(keyQt);
case 1:
return QKeySequence(seq[0], keyQt);
case 2:
return QKeySequence(seq[0], seq[1], keyQt);
case 3:
return QKeySequence(seq[0], seq[1], seq[2], keyQt);
default:
return seq;
}
}
//static
bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt)
{
//this whole function is a hack, but especially the first line of code
if (QKeySequence(keyQt).toString().length() == 1) {
return false;
}
switch (keyQt) {
case Qt::Key_Return:
case Qt::Key_Space:
case Qt::Key_Tab:
case Qt::Key_Backtab: //does this ever happen?
case Qt::Key_Backspace:
case Qt::Key_Delete:
return false;
default:
return true;
}
}
#include "moc_kkeysequencewidget.cpp"
#include "moc_kkeysequencewidget_p.cpp"
diff --git a/packaging/linux/appimage/build-deps.sh b/packaging/linux/appimage/build-deps.sh
index f36978029b..a106d5ed9b 100644
--- a/packaging/linux/appimage/build-deps.sh
+++ b/packaging/linux/appimage/build-deps.sh
@@ -1,122 +1,123 @@
#!/bin/bash
# Enter a CentOS 6 chroot (you could use other methods)
# git clone https://github.com/probonopd/AppImageKit.git
# ./AppImageKit/build.sh
# sudo ./AppImageKit/AppImageAssistant.AppDir/testappimage /isodevice/boot/iso/CentOS-6.5-x86_64-LiveCD.iso bash
# Halt on errors
set -e
# Be verbose
set -x
# Now we are inside CentOS 6
grep -r "CentOS release 6" /etc/redhat-release || exit 1
# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. That's
# not always set correctly in CentOS 6.7
export LC_ALL=en_US.UTF-8
export LANG=en_us.UTF-8
# Determine which architecture should be built
if [[ "$(arch)" = "i686" || "$(arch)" = "x86_64" ]] ; then
ARCH=$(arch)
else
echo "Architecture could not be determined"
exit 1
fi
# if the library path doesn't point to our usr/lib, linking will be broken and we won't find all deps either
export LD_LIBRARY_PATH=/usr/lib64/:/usr/lib:/krita.appdir/usr/lib
git_pull_rebase_helper()
{
git reset --hard HEAD
git pull
}
yum -y install epel-release
# we need to be up to date in order to install the xcb-keysyms dependency
yum -y update
# base dependencies and Qt5.
yum -y install wget tar bzip2 git libtool which fuse fuse-devel libpng-devel automake libtool mesa-libEGL cppunit-devel cmake3 glibc-headers libstdc++-devel gcc-c++ freetype-devel fontconfig-devel libxml2-devel libstdc++-devel libXrender-devel patch xcb-util-keysyms-devel libXi-devel mesa-libGL-devel mesa-libGLU-devel libxcb libxcb-devel xcb-util xcb-util-devel glibc-devel xkeyboard-config
# Newer compiler than what comes with CentOS 6
yum -y install centos-release-scl-rh
yum -y install devtoolset-3-gcc devtoolset-3-gcc-c++
. /opt/rh/devtoolset-3/enable
# Make sure we build from the /, parts of this script depends on that. We also need to run as root...
cd /
# Build AppImageKit
if [ ! -d AppImageKit ] ; then
git clone --depth 1 https://github.com/probonopd/AppImageKit.git /AppImageKit
fi
cd /AppImageKit/
git_pull_rebase_helper
+git checkout stable/v1.0
./build.sh
cd /
# Workaround for: On CentOS 6, .pc files in /usr/lib/pkgconfig are not recognized
# However, this is where .pc files get installed when bulding libraries... (FIXME)
# I found this by comparing the output of librevenge's "make install" command
# between Ubuntu and CentOS 6
ln -sf /usr/share/pkgconfig /usr/lib/pkgconfig
# A krita build layout looks like this:
# krita/ -- the source directory
# krita/3rdparty -- the cmake3 definitions for the dependencies
# d -- downloads of the dependencies from files.kde.org
# b -- build directory for the dependencies
# krita_build -- build directory for krita itself
# krita.appdir -- install directory for krita and the dependencies
# Get Krita
if [ ! -d /krita ] ; then
git clone --depth 1 https://github.com/KDE/krita.git /krita
fi
cd /krita/
git_pull_rebase_helper
# Create the build dir for the 3rdparty deps
if [ ! -d /b ] ; then
mkdir /b
fi
if [ ! -d /d ] ; then
mkdir /d
fi
# start building the deps
cd /b
rm -rf /b/* || true
cmake3 /krita/3rdparty \
-DCMAKE_INSTALL_PREFIX:PATH=/usr \
-DINSTALL_ROOT=/usr \
-DEXTERNALS_DOWNLOAD_DIR=/d
cmake3 --build . --config RelWithDebInfo --target ext_qt
cmake3 --build . --config RelWithDebInfo --target ext_boost
cmake3 --build . --config RelWithDebInfo --target ext_eigen3
cmake3 --build . --config RelWithDebInfo --target ext_exiv2
cmake3 --build . --config RelWithDebInfo --target ext_fftw3
cmake3 --build . --config RelWithDebInfo --target ext_lcms2
cmake3 --build . --config RelWithDebInfo --target ext_ocio
cmake3 --build . --config RelWithDebInfo --target ext_openexr
cmake3 --build . --config RelWithDebInfo --target ext_vc
#cmake3 --build . --config RelWithDebInfo --target ext_png
cmake3 --build . --config RelWithDebInfo --target ext_tiff
cmake3 --build . --config RelWithDebInfo --target ext_jpeg
cmake3 --build . --config RelWithDebInfo --target ext_libraw
cmake3 --build . --config RelWithDebInfo --target ext_kcrash
cmake3 --build . --config RelWithDebInfo --target ext_poppler
cmake3 --build . --config RelWithDebInfo --target ext_gsl
diff --git a/packaging/linux/appimage/build-image.sh b/packaging/linux/appimage/build-image.sh
index 1940f18e69..24fab41948 100644
--- a/packaging/linux/appimage/build-image.sh
+++ b/packaging/linux/appimage/build-image.sh
@@ -1,255 +1,244 @@
#!/bin/bash
# Enter a CentOS 6 chroot (you could use other methods)
# git clone https://github.com/probonopd/AppImageKit.git
# ./AppImageKit/build.sh
# sudo ./AppImageKit/AppImageAssistant.AppDir/testappimage /isodevice/boot/iso/CentOS-6.5-x86_64-LiveCD.iso bash
# Halt on errors
set -e
# Be verbose
set -x
# Now we are inside CentOS 6
grep -r "CentOS release 6" /etc/redhat-release || exit 1
# If we are running inside Travis CI, then we want to build Krita
# and we remove the $DO_NOT_BUILD_KRITA environment variable
# that was used in the process of generating the Docker image.
# Also we do not want to download and build the dependencies every
# time we build Krita on travis in the docker image. In order to
# use newer dependencies, we re-build the docker image instead.
#unset DO_NOT_BUILD_KRITA
#NO_DOWNLOAD=1
# clean up
-rm -f krita-3.0-l10n-win-current.tar.gz
rm -rf /out/*
rm -rf /krita.appdir
# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. That's
# not always set correctly in CentOS 6.7
export LC_ALL=en_US.UTF-8
export LANG=en_us.UTF-8
# Determine which architecture should be built
if [[ "$(arch)" = "i686" || "$(arch)" = "x86_64" ]] ; then
ARCH=$(arch)
else
echo "Architecture could not be determined"
exit 1
fi
# if the library path doesn't point to our usr/lib, linking will be broken and we won't find all deps either
export LD_LIBRARY_PATH=/usr/lib64/:/usr/lib:/krita.appdir/usr/lib
cd /
# Prepare the install location
rm -rf /krita.appdir/ || true
mkdir -p /krita.appdir/usr/bin
# make sure lib and lib64 are the same thing
mkdir -p /krita.appdir/usr/lib
cd /krita.appdir/usr
ln -s lib lib64
cd /krita_build
make -j4 install
cd /krita.appdir
# FIXME: How to find out which subset of plugins is really needed? I used strace when running the binary
cp -r /usr/plugins ./usr/bin/
# copy the Qt translation
cp -r /usr/translations ./usr
cp $(ldconfig -p | grep libsasl2.so.2 | cut -d ">" -f 2 | xargs) ./usr/lib/
cp $(ldconfig -p | grep libGL.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ # otherwise segfaults!?
cp $(ldconfig -p | grep libGLU.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ # otherwise segfaults!?
# Fedora 23 seemed to be missing SOMETHING from the Centos 6.7. The only message was:
# This application failed to start because it could not find or load the Qt platform plugin "xcb".
# Setting export QT_DEBUG_PLUGINS=1 revealed the cause.
# QLibraryPrivate::loadPlugin failed on "/usr/lib64/qt5/plugins/platforms/libqxcb.so" :
# "Cannot load library /usr/lib64/qt5/plugins/platforms/libqxcb.so: (/lib64/libEGL.so.1: undefined symbol: drmGetNodeTypeFromFd)"
# Which means that we have to copy libEGL.so.1 in too
cp $(ldconfig -p | grep libEGL.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ # Otherwise F23 cannot load the Qt platform plugin "xcb"
# let's not copy xcb itself, that breaks on dri3 systems https://bugs.kde.org/show_bug.cgi?id=360552
#cp $(ldconfig -p | grep libxcb.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/
cp $(ldconfig -p | grep libfreetype.so.6 | cut -d ">" -f 2 | xargs) ./usr/lib/ # For Fedora 20
ldd usr/bin/krita | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true
#ldd usr/lib64/krita/*.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true
#ldd usr/lib64/plugins/imageformats/*.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true
ldd usr/bin/plugins/platforms/libqxcb.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true
# Copy in the indirect dependencies
FILES=$(find . -type f -executable)
for FILE in $FILES ; do
ldd "${FILE}" | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' usr/lib || true
done
#DEPS=""
#for FILE in $FILES ; do
# ldd "${FILE}" | grep "=>" | awk '{print $3}' | xargs -I '{}' echo '{}' > DEPSFILE
#done
#DEPS=$(cat DEPSFILE |sort | uniq)
#for FILE in $DEPS ; do
# if [ -f $FILE ] ; then
# echo $FILE
# cp --parents -rfL $FILE ./
# fi
#done
#rm -f DEPSFILE
# The following are assumed to be part of the base system
rm -f usr/lib/libcom_err.so.2 || true
rm -f usr/lib/libcrypt.so.1 || true
rm -f usr/lib/libdl.so.2 || true
rm -f usr/lib/libexpat.so.1 || true
#rm -f usr/lib/libfontconfig.so.1 || true
rm -f usr/lib/libgcc_s.so.1 || true
rm -f usr/lib/libglib-2.0.so.0 || true
rm -f usr/lib/libgpg-error.so.0 || true
rm -f usr/lib/libgssapi_krb5.so.2 || true
rm -f usr/lib/libgssapi.so.3 || true
rm -f usr/lib/libhcrypto.so.4 || true
rm -f usr/lib/libheimbase.so.1 || true
rm -f usr/lib/libheimntlm.so.0 || true
rm -f usr/lib/libhx509.so.5 || true
rm -f usr/lib/libICE.so.6 || true
rm -f usr/lib/libidn.so.11 || true
rm -f usr/lib/libk5crypto.so.3 || true
rm -f usr/lib/libkeyutils.so.1 || true
rm -f usr/lib/libkrb5.so.26 || true
rm -f usr/lib/libkrb5.so.3 || true
rm -f usr/lib/libkrb5support.so.0 || true
# rm -f usr/lib/liblber-2.4.so.2 || true # needed for debian wheezy
# rm -f usr/lib/libldap_r-2.4.so.2 || true # needed for debian wheezy
rm -f usr/lib/libm.so.6 || true
rm -f usr/lib/libp11-kit.so.0 || true
rm -f usr/lib/libpcre.so.3 || true
rm -f usr/lib/libpthread.so.0 || true
rm -f usr/lib/libresolv.so.2 || true
rm -f usr/lib/libroken.so.18 || true
rm -f usr/lib/librt.so.1 || true
rm -f usr/lib/libsasl2.so.2 || true
rm -f usr/lib/libSM.so.6 || true
rm -f usr/lib/libusb-1.0.so.0 || true
rm -f usr/lib/libuuid.so.1 || true
rm -f usr/lib/libwind.so.0 || true
rm -f usr/lib/libfontconfig.so.* || true
# Remove these libraries, we need to use the system versions; this means 11.04 is not supported (12.04 is our baseline)
rm -f usr/lib/libGL.so.* || true
rm -f usr/lib/libdrm.so.* || true
rm -f usr/lib/libX11.so.* || true
#rm -f usr/lib/libz.so.1 || true
# These seem to be available on most systems but not Ubuntu 11.04
# rm -f usr/lib/libffi.so.6 usr/lib/libGL.so.1 usr/lib/libglapi.so.0 usr/lib/libxcb.so.1 usr/lib/libxcb-glx.so.0 || true
# Delete potentially dangerous libraries
rm -f usr/lib/libstdc* usr/lib/libgobject* usr/lib/libc.so.* || true
rm -f usr/lib/libxcb.so.1
# Do NOT delete libX* because otherwise on Ubuntu 11.04:
# loaded library "Xcursor" malloc.c:3096: sYSMALLOc: Assertion (...) Aborted
# We don't bundle the developer stuff
rm -rf usr/include || true
rm -rf usr/lib/cmake3 || true
rm -rf usr/lib/pkgconfig || true
rm -rf usr/share/ECM/ || true
rm -rf usr/share/gettext || true
rm -rf usr/share/pkgconfig || true
strip usr/lib/kritaplugins/* usr/bin/* usr/lib/* || true
# Since we set /krita.appdir as the prefix, we need to patch it away too (FIXME)
# Probably it would be better to use /app as a prefix because it has the same length for all apps
cd usr/ ; find . -type f -exec sed -i -e 's|/krita.appdir/usr/|./././././././././|g' {} \; ; cd ..
# On openSUSE Qt is picking up the wrong libqxcb.so
# (the one from the system when in fact it should use the bundled one) - is this a Qt bug?
# Also, Krita has a hardcoded /usr which we patch away
cd usr/ ; find . -type f -exec sed -i -e 's|/usr|././|g' {} \; ; cd ..
# We do not bundle this, so let's not search that inside the AppImage.
# Fixes "Qt: Failed to create XKB context!" and lets us enter text
sed -i -e 's|././/share/X11/|/usr/share/X11/|g' ./usr/bin/plugins/platforminputcontexts/libcomposeplatforminputcontextplugin.so
sed -i -e 's|././/share/X11/|/usr/share/X11/|g' ./usr/lib/libQt5XcbQpa.so.5
# Workaround for:
# D-Bus library appears to be incorrectly set up;
# failed to read machine uuid: Failed to open
# The file is more commonly in /etc/machine-id
# sed -i -e 's|/var/lib/dbus/machine-id|//././././etc/machine-id|g' ./usr/lib/libdbus-1.so.3
# or
rm -f ./usr/lib/libdbus-1.so.3 || true
cp ../AppImageKit/AppRun .
cp ./usr/share/applications/org.kde.krita.desktop krita.desktop
cp /krita/krita/pics/app/64-apps-calligrakrita.png calligrakrita.png
-#
-# Fetch and install the translations
-#
-cd /
-rm -f krita-3.0-l10-win-current.tar.gz || true
-wget http://files.kde.org/krita/build/krita-3.0-l10n-win-current.tar.gz
-tar -xf krita-3.0-l10n-win-current.tar.gz
-cd /krita.appdir/usr/share
-tar -xf /krita-3.0-l10n-win-current.tar.gz
-
# replace krita with the lib-checking startup script.
#cd /krita.appdir/usr/bin
#mv krita krita.real
#wget https://raw.githubusercontent.com/boudewijnrempt/AppImages/master/recipes/krita/krita
#chmod a+rx krita
cd /
APP=krita
# Source functions
wget -q https://github.com/probonopd/AppImages/raw/master/functions.sh -O ./functions.sh
. ./functions.sh
# Install desktopintegration in usr/bin/krita.wrapper -- feel free to edit it
cd /krita.appdir
get_desktopintegration krita
cd /
VER=$(grep "#define KRITA_VERSION_STRING" krita_build/libs/version/kritaversion.h | cut -d '"' -f 2)
cd /krita
REVISION=$(git rev-parse --short HEAD)
cd ..
VERSION=$VER-$REVISION
VERSION="$(sed s/\ /-/g <<<$VERSION)"
echo $VERSION
if [[ "$ARCH" = "x86_64" ]] ; then
APPIMAGE=$APP"-"$VERSION"-x86_64.appimage"
fi
if [[ "$ARCH" = "i686" ]] ; then
APPIMAGE=$APP"-"$VERSION"-i386.appimage"
fi
echo $APPIMAGE
mkdir -p /out
rm -f /out/*.AppImage || true
AppImageKit/AppImageAssistant.AppDir/package /krita.appdir/ /out/$APPIMAGE
chmod a+rwx /out/$APPIMAGE # So that we can edit the AppImage outside of the Docker container
cd /krita.appdir
mv AppRun krita
cd /
mv krita.appdir "$APP"-"$VERSION"-x86_64
tar -czf "$APP"-"$VERSION"-x86_64.tgz "$APP"-"$VERSION"-x86_64
diff --git a/plugins/dockers/animation/kis_animation_curves_view.cpp b/plugins/dockers/animation/kis_animation_curves_view.cpp
index 71c3620dc4..d57cfe5bcf 100644
--- a/plugins/dockers/animation/kis_animation_curves_view.cpp
+++ b/plugins/dockers/animation/kis_animation_curves_view.cpp
@@ -1,735 +1,736 @@
/*
* Copyright (c) 2016 Jouni Pentikäinen <joupent@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_animation_curves_view.h"
#include <QPaintEvent>
#include <QMouseEvent>
#include <QApplication>
#include <QScrollBar>
#include <qpainter.h>
#include <QtMath>
#include "kis_animation_curves_model.h"
#include "timeline_ruler_header.h"
#include "kis_animation_curves_value_ruler.h"
#include "kis_animation_curves_keyframe_delegate.h"
#include "kis_scalar_keyframe_channel.h"
#include "kis_zoom_button.h"
#include "kis_custom_modifiers_catcher.h"
const int VERTICAL_PADDING = 30;
struct KisAnimationCurvesView::Private
{
Private()
: model(0)
, isDraggingKeyframe(false)
, isAdjustingHandle(false)
, panning(false)
{}
KisAnimationCurvesModel *model;
TimelineRulerHeader *horizontalHeader;
KisAnimationCurvesValueRuler *verticalHeader;
KisAnimationCurvesKeyframeDelegate *itemDelegate;
KisZoomButton *horizontalZoomButton;
KisZoomButton *verticalZoomButton;
KisCustomModifiersCatcher *modifiersCatcher;
bool isDraggingKeyframe;
bool isAdjustingHandle;
int adjustedHandle; // 0 = left, 1 = right
QPoint dragStart;
QPoint dragOffset;
int horizontalZoomStillPointIndex;
int horizontalZoomStillPointOriginalOffset;
qreal verticalZoomStillPoint;
qreal verticalZoomStillPointOriginalOffset;
bool panning;
QPoint panStartOffset;
};
KisAnimationCurvesView::KisAnimationCurvesView(QWidget *parent)
: QAbstractItemView(parent)
, m_d(new Private())
{
m_d->horizontalHeader = new TimelineRulerHeader(this);
m_d->verticalHeader = new KisAnimationCurvesValueRuler(this);
m_d->itemDelegate = new KisAnimationCurvesKeyframeDelegate(m_d->horizontalHeader, m_d->verticalHeader, this);
m_d->modifiersCatcher = new KisCustomModifiersCatcher(this);
m_d->modifiersCatcher->addModifier("pan-zoom", Qt::Key_Space);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
}
KisAnimationCurvesView::~KisAnimationCurvesView()
{}
void KisAnimationCurvesView::setModel(QAbstractItemModel *model)
{
m_d->model = dynamic_cast<KisAnimationCurvesModel*>(model);
QAbstractItemView::setModel(model);
m_d->horizontalHeader->setModel(model);
connect(model, &QAbstractItemModel::rowsInserted,
this, &KisAnimationCurvesView::slotRowsChanged);
connect(model, &QAbstractItemModel::rowsRemoved,
this, &KisAnimationCurvesView::slotRowsChanged);
connect(model, &QAbstractItemModel::dataChanged,
this, &KisAnimationCurvesView::slotDataChanged);
connect(model, &QAbstractItemModel::headerDataChanged,
this, &KisAnimationCurvesView::slotHeaderDataChanged);
}
void KisAnimationCurvesView::setZoomButtons(KisZoomButton *horizontal, KisZoomButton *vertical)
{
m_d->horizontalZoomButton = horizontal;
m_d->verticalZoomButton = vertical;
connect(horizontal, &KisZoomButton::zoomStarted, this, &KisAnimationCurvesView::slotHorizontalZoomStarted);
connect(horizontal, &KisZoomButton::zoomLevelChanged, this, &KisAnimationCurvesView::slotHorizontalZoomLevelChanged);
connect(vertical, &KisZoomButton::zoomStarted, this, &KisAnimationCurvesView::slotVerticalZoomStarted);
connect(vertical, &KisZoomButton::zoomLevelChanged, this, &KisAnimationCurvesView::slotVerticalZoomLevelChanged);
}
QRect KisAnimationCurvesView::visualRect(const QModelIndex &index) const
{
return m_d->itemDelegate->itemRect(index);
}
void KisAnimationCurvesView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint)
{
// TODO
Q_UNUSED(index);
Q_UNUSED(hint);
}
QModelIndex KisAnimationCurvesView::indexAt(const QPoint &point) const
{
if (!model()) return QModelIndex();
int time = m_d->horizontalHeader->logicalIndexAt(point.x());
int rows = model()->rowCount();
for (int row=0; row < rows; row++) {
QModelIndex index = model()->index(row, time);
if (index.data(KisTimeBasedItemModel::SpecialKeyframeExists).toBool()) {
QRect nodePos = m_d->itemDelegate->itemRect(index);
if (nodePos.contains(point)) {
return index;
}
}
}
return QModelIndex();
}
void KisAnimationCurvesView::paintEvent(QPaintEvent *e)
{
QPainter painter(viewport());
QRect r = e->rect();
r.translate(dirtyRegionOffset());
int firstFrame = m_d->horizontalHeader->logicalIndexAt(r.left());
int lastFrame = m_d->horizontalHeader->logicalIndexAt(r.right());
if (lastFrame == -1) lastFrame = model()->columnCount();
paintCurves(painter, firstFrame, lastFrame);
paintKeyframes(painter, firstFrame, lastFrame);
}
void KisAnimationCurvesView::paintCurves(QPainter &painter, int firstFrame, int lastFrame)
{
int channels = model()->rowCount();
for (int channel = 0; channel < channels; channel++) {
QModelIndex index0 = model()->index(channel, 0);
if (!isIndexHidden(index0)) {
QColor color = index0.data(KisAnimationCurvesModel::CurveColorRole).value<QColor>();
painter.setPen(QPen(color, 1));
paintCurve(channel, firstFrame, lastFrame, painter);
}
}
}
void KisAnimationCurvesView::paintCurve(int channel, int firstFrame, int lastFrame, QPainter &painter)
{
int selectionOffset = m_d->isDraggingKeyframe ? (m_d->dragOffset.x() / m_d->horizontalHeader->defaultSectionSize()) : 0;
QModelIndex index = findNextKeyframeIndex(channel, firstFrame+1, selectionOffset, true);
if (!index.isValid()) {
index = findNextKeyframeIndex(channel, firstFrame, selectionOffset, false);
}
if (!index.isValid()) return;
QPointF previousKeyPos = m_d->itemDelegate->nodeCenter(index, selectionModel()->isSelected(index));
QPointF rightTangent = m_d->itemDelegate->rightHandle(index, index == currentIndex());
while(index.column() <= lastFrame) {
int interpolationMode = index.data(KisAnimationCurvesModel::InterpolationModeRole).toInt();
int time = (m_d->isDraggingKeyframe && selectionModel()->isSelected(index)) ? index.column() + selectionOffset : index.column();
index = findNextKeyframeIndex(channel, time, selectionOffset, false);
if (!index.isValid()) return;
bool active = (index == currentIndex());
QPointF nextKeyPos = m_d->itemDelegate->nodeCenter(index, selectionModel()->isSelected(index));
QPointF leftTangent = m_d->itemDelegate->leftHandle(index, active);
if (interpolationMode == KisKeyframe::Constant) {
painter.drawLine(previousKeyPos, QPointF(nextKeyPos.x(), previousKeyPos.y()));
} else if (interpolationMode == KisKeyframe::Linear) {
painter.drawLine(previousKeyPos, nextKeyPos);
} else {
paintCurveSegment(painter, previousKeyPos, rightTangent, leftTangent, nextKeyPos);
}
previousKeyPos = nextKeyPos;
rightTangent = m_d->itemDelegate->rightHandle(index, active);
}
}
void KisAnimationCurvesView::paintCurveSegment(QPainter &painter, QPointF pos1, QPointF rightTangent, QPointF leftTangent, QPointF pos2) {
const int steps = 16;
QPointF previousPos;
for (int step = 0; step <= steps; step++) {
qreal t = 1.0 * step / steps;
QPointF nextPos = KisScalarKeyframeChannel::interpolate(pos1, rightTangent, leftTangent, pos2, t);
if (step > 0) {
painter.drawLine(previousPos, nextPos);
}
previousPos = nextPos;
}
}
void KisAnimationCurvesView::paintKeyframes(QPainter &painter, int firstFrame, int lastFrame)
{
int channels = model()->rowCount();
for (int channel = 0; channel < channels; channel++) {
for (int time=firstFrame; time <= lastFrame; time++) {
QModelIndex index = model()->index(channel, time);
bool keyframeExists = model()->data(index, KisAnimationCurvesModel::SpecialKeyframeExists).toReal();
if (keyframeExists && !isIndexHidden(index)) {
QStyleOptionViewItem opt;
if (selectionModel()->isSelected(index)) {
opt.state |= QStyle::State_Selected;
}
if (index == selectionModel()->currentIndex()) {
opt.state |= QStyle::State_HasFocus;
}
m_d->itemDelegate->paint(&painter, opt, index);
}
}
}
}
QModelIndex KisAnimationCurvesView::findNextKeyframeIndex(int channel, int time, int selectionOffset, bool backward)
{
KisAnimationCurvesModel::ItemDataRole role =
backward ? KisAnimationCurvesModel::PreviousKeyframeTime : KisAnimationCurvesModel::NextKeyframeTime;
QModelIndex currentIndex = model()->index(channel, time);
if (!selectionOffset) {
QVariant next = currentIndex.data(role);
return (next.isValid()) ? model()->index(channel, next.toInt()) : QModelIndex();
} else {
// Find the next unselected index
QModelIndex nextIndex = currentIndex;
do {
QVariant next = nextIndex.data(role);
nextIndex = (next.isValid()) ? model()->index(channel, next.toInt()) : QModelIndex();
} while(nextIndex.isValid() && selectionModel()->isSelected(nextIndex));
// Find the next selected index, accounting for D&D offset
QModelIndex draggedIndex = model()->index(channel, qMax(0, time - selectionOffset));
do {
QVariant next = draggedIndex.data(role);
draggedIndex = (next.isValid()) ? model()->index(channel, next.toInt()) : QModelIndex();
} while(draggedIndex.isValid() && !selectionModel()->isSelected(draggedIndex));
// Choose the earlier of the two
if (draggedIndex.isValid() && nextIndex.isValid()) {
if (draggedIndex.column() + selectionOffset <= nextIndex.column()) {
return draggedIndex;
} else {
return nextIndex;
}
} else if (draggedIndex.isValid()) {
return draggedIndex;
} else {
return nextIndex;
}
}
}
void KisAnimationCurvesView::findExtremes(qreal *minimum, qreal *maximum)
{
if (!model()) return;
qreal min = qInf();
qreal max = -qInf();
int rows = model()->rowCount();
for (int row = 0; row < rows; row++) {
QModelIndex index = model()->index(row, 0);
if (isIndexHidden(index)) continue;
QVariant nextTime;
do {
qreal value = index.data(KisAnimationCurvesModel::ScalarValueRole).toReal();
if (value < min) min = value;
if (value > max) max = value;
nextTime = index.data(KisAnimationCurvesModel::NextKeyframeTime);
if (nextTime.isValid()) index = model()->index(row, nextTime.toInt());
} while (nextTime.isValid());
}
if (qIsFinite(min)) *minimum = min;
if (qIsFinite(max)) *maximum = max;
}
void KisAnimationCurvesView::updateVerticalRange()
{
if (!model()) return;
- qreal minimum, maximum;
+ qreal minimum = 0;
+ qreal maximum = 0;
findExtremes(&minimum, &maximum);
int viewMin = maximum * m_d->verticalHeader->scaleFactor();
int viewMax = minimum * m_d->verticalHeader->scaleFactor();
viewMin -= VERTICAL_PADDING;
viewMax += VERTICAL_PADDING;
verticalScrollBar()->setRange(viewMin, viewMax - viewport()->height());
}
void KisAnimationCurvesView::startPan(QPoint mousePos)
{
m_d->dragStart = mousePos;
m_d->panStartOffset = QPoint(horizontalOffset(), verticalOffset());
m_d->panning = true;
}
QModelIndex KisAnimationCurvesView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
// TODO
Q_UNUSED(cursorAction);
Q_UNUSED(modifiers);
return QModelIndex();
}
int KisAnimationCurvesView::horizontalOffset() const
{
return m_d->horizontalHeader->offset();
}
int KisAnimationCurvesView::verticalOffset() const
{
return m_d->verticalHeader->offset();
}
bool KisAnimationCurvesView::isIndexHidden(const QModelIndex &index) const
{
return !index.data(KisAnimationCurvesModel::CurveVisibleRole).toBool();
}
void KisAnimationCurvesView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
{
int timeFrom = m_d->horizontalHeader->logicalIndexAt(rect.left());
int timeTo = m_d->horizontalHeader->logicalIndexAt(rect.right());
QItemSelection selection;
int rows = model()->rowCount();
for (int row=0; row < rows; row++) {
for (int time = timeFrom; time <= timeTo; time++) {
QModelIndex index = model()->index(row, time);
if (index.data(KisTimeBasedItemModel::SpecialKeyframeExists).toBool()) {
QRect itemRect = m_d->itemDelegate->itemRect(index);
if (itemRect.intersects(rect)) {
selection.select(index, index);
}
}
}
}
selectionModel()->select(selection, command);
}
QRegion KisAnimationCurvesView::visualRegionForSelection(const QItemSelection &selection) const
{
QRegion region;
Q_FOREACH(QModelIndex index, selection.indexes()) {
region += m_d->itemDelegate->visualRect(index);
}
return region;
}
void KisAnimationCurvesView::mousePressEvent(QMouseEvent *e)
{
if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) {
if (e->button() == Qt::LeftButton) {
startPan(e->pos());
} else {
qreal horizontalStaticPoint = m_d->horizontalHeader->logicalIndexAt(e->pos().x());
qreal verticalStaticPoint = m_d->verticalHeader->mapViewToValue(e->pos().y());
m_d->horizontalZoomButton->beginZoom(QPoint(e->pos().x(), 0), horizontalStaticPoint);
m_d->verticalZoomButton->beginZoom(QPoint(0, e->pos().y()), verticalStaticPoint);
}
} else if (e->button() == Qt::LeftButton) {
m_d->dragStart = e->pos();
Q_FOREACH(QModelIndex index, selectedIndexes()) {
QPointF center = m_d->itemDelegate->nodeCenter(index, false);
bool hasLeftHandle = m_d->itemDelegate->hasHandle(index, 0);
bool hasRightHandle = m_d->itemDelegate->hasHandle(index, 1);
QPointF leftHandle = center + m_d->itemDelegate->leftHandle(index, false);
QPointF rightHandle = center + m_d->itemDelegate->rightHandle(index, false);
if (hasLeftHandle && (e->localPos() - leftHandle).manhattanLength() < 8) {
m_d->isAdjustingHandle = true;
m_d->adjustedHandle = 0;
setCurrentIndex(index);
return;
} else if (hasRightHandle && (e->localPos() - rightHandle).manhattanLength() < 8) {
m_d->isAdjustingHandle = true;
m_d->adjustedHandle = 1;
setCurrentIndex(index);
return;
}
}
}
QAbstractItemView::mousePressEvent(e);
}
void KisAnimationCurvesView::mouseMoveEvent(QMouseEvent *e)
{
if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) {
if (e->buttons() & Qt::LeftButton) {
if (!m_d->panning) startPan(e->pos());
QPoint diff = e->pos() - m_d->dragStart;
QPoint newOffset = m_d->panStartOffset - diff;
horizontalScrollBar()->setValue(newOffset.x());
verticalScrollBar()->setValue(newOffset.y());
m_d->verticalHeader->setOffset(newOffset.y());
viewport()->update();
} else {
m_d->horizontalZoomButton->continueZoom(QPoint(e->pos().x(), 0));
m_d->verticalZoomButton->continueZoom(QPoint(0, e->pos().y()));
}
} else if (e->buttons() & Qt::LeftButton) {
m_d->dragOffset = e->pos() - m_d->dragStart;
if (m_d->isAdjustingHandle) {
m_d->itemDelegate->setHandleAdjustment(m_d->dragOffset, m_d->adjustedHandle);
viewport()->update();
return;
} else if (m_d->isDraggingKeyframe) {
m_d->itemDelegate->setSelectedItemVisualOffset(m_d->dragOffset);
viewport()->update();
return;
} else if (selectionModel()->hasSelection()) {
if ((e->pos() - m_d->dragStart).manhattanLength() > QApplication::startDragDistance()) {
m_d->isDraggingKeyframe = true;
}
}
}
QAbstractItemView::mouseMoveEvent(e);
}
void KisAnimationCurvesView::mouseReleaseEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton) {
m_d->panning = false;
if (m_d->isDraggingKeyframe) {
QModelIndexList indexes = selectedIndexes();
int timeOffset = m_d->dragOffset.x() / m_d->horizontalHeader->defaultSectionSize();
qreal valueOffset = m_d->dragOffset.y() / m_d->verticalHeader->scaleFactor();
KisAnimationCurvesModel *curvesModel = dynamic_cast<KisAnimationCurvesModel*>(model());
curvesModel->adjustKeyframes(indexes, timeOffset, valueOffset);
m_d->isDraggingKeyframe = false;
m_d->itemDelegate->setSelectedItemVisualOffset(QPointF());
viewport()->update();
} else if (m_d->isAdjustingHandle) {
QModelIndex index = currentIndex();
int mode = index.data(KisAnimationCurvesModel::TangentsModeRole).toInt();
m_d->model->beginCommand(kundo2_i18n("Adjust tangent"));
if (mode == KisKeyframe::Smooth) {
QPointF leftHandle = m_d->itemDelegate->leftHandle(index, true);
QPointF rightHandle = m_d->itemDelegate->rightHandle(index, true);
QPointF leftTangent = m_d->itemDelegate->unscaledTangent(leftHandle);
QPointF rightTangent = m_d->itemDelegate->unscaledTangent(rightHandle);
model()->setData(index, leftTangent, KisAnimationCurvesModel::LeftTangentRole);
model()->setData(index, rightTangent, KisAnimationCurvesModel::RightTangentRole);
} else {
if (m_d->adjustedHandle == 0) {
QPointF leftHandle = m_d->itemDelegate->leftHandle(index, true);
model()->setData(index, m_d->itemDelegate->unscaledTangent(leftHandle), KisAnimationCurvesModel::LeftTangentRole);
} else {
QPointF rightHandle = m_d->itemDelegate->rightHandle(index, true);
model()->setData(index, m_d->itemDelegate->unscaledTangent(rightHandle), KisAnimationCurvesModel::RightTangentRole);
}
}
m_d->model->endCommand();
m_d->isAdjustingHandle = false;
m_d->itemDelegate->setHandleAdjustment(QPointF(), m_d->adjustedHandle);
}
}
QAbstractItemView::mouseReleaseEvent(e);
}
void KisAnimationCurvesView::scrollContentsBy(int dx, int dy)
{
m_d->horizontalHeader->setOffset(horizontalScrollBar()->value());
m_d->verticalHeader->setOffset(verticalScrollBar()->value());
scrollDirtyRegion(dx, dy);
viewport()->scroll(dx, dy);
}
void KisAnimationCurvesView::updateGeometries()
{
int topMargin = qMax(m_d->horizontalHeader->minimumHeight(),
m_d->horizontalHeader->sizeHint().height());
int leftMargin = m_d->verticalHeader->sizeHint().width();
setViewportMargins(leftMargin, topMargin, 0, 0);
QRect viewRect = viewport()->geometry();
m_d->horizontalHeader->setGeometry(leftMargin, 0, viewRect.width(), topMargin);
m_d->verticalHeader->setGeometry(0, topMargin, leftMargin, viewRect.height());
horizontalScrollBar()->setRange(0, m_d->horizontalHeader->length() - viewport()->width());
updateVerticalRange();
QAbstractItemView::updateGeometries();
}
void KisAnimationCurvesView::slotRowsChanged(const QModelIndex &parentIndex, int first, int last)
{
Q_UNUSED(parentIndex);
Q_UNUSED(first);
Q_UNUSED(last);
updateVerticalRange();
viewport()->update();
}
void KisAnimationCurvesView::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
Q_UNUSED(topLeft);
Q_UNUSED(bottomRight);
updateVerticalRange();
viewport()->update();
}
void KisAnimationCurvesView::slotHeaderDataChanged(Qt::Orientation orientation, int first, int last)
{
Q_UNUSED(orientation);
Q_UNUSED(first);
Q_UNUSED(last);
viewport()->update();
}
void KisAnimationCurvesView::slotHorizontalZoomStarted(qreal staticPoint)
{
m_d->horizontalZoomStillPointIndex =
qIsNaN(staticPoint) ? currentIndex().column() : staticPoint;
const int w = m_d->horizontalHeader->defaultSectionSize();
m_d->horizontalZoomStillPointOriginalOffset =
w * m_d->horizontalZoomStillPointIndex -
horizontalScrollBar()->value();
}
void KisAnimationCurvesView::slotHorizontalZoomLevelChanged(qreal zoomLevel)
{
if (m_d->horizontalHeader->setZoom(zoomLevel)) {
const int w = m_d->horizontalHeader->defaultSectionSize();
horizontalScrollBar()->setValue(w * m_d->horizontalZoomStillPointIndex - m_d->horizontalZoomStillPointOriginalOffset);
viewport()->update();
}
}
void KisAnimationCurvesView::slotVerticalZoomStarted(qreal staticPoint)
{
m_d->verticalZoomStillPoint = qIsNaN(staticPoint) ? 0 : staticPoint;
const float scale = m_d->verticalHeader->scaleFactor();
m_d->verticalZoomStillPointOriginalOffset =
scale * m_d->verticalZoomStillPoint - m_d->verticalHeader->offset();
}
void KisAnimationCurvesView::slotVerticalZoomLevelChanged(qreal zoomLevel)
{
if (!qFuzzyCompare((float)zoomLevel, m_d->verticalHeader->scaleFactor())) {
m_d->verticalHeader->setScale(zoomLevel);
m_d->verticalHeader->setOffset(-zoomLevel * m_d->verticalZoomStillPoint - m_d->verticalZoomStillPointOriginalOffset);
verticalScrollBar()->setValue(m_d->verticalHeader->offset());
viewport()->update();
}
}
void KisAnimationCurvesView::applyConstantMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
m_d->model->setData(index, KisKeyframe::Constant, KisAnimationCurvesModel::InterpolationModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::applyLinearMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
m_d->model->setData(index, KisKeyframe::Linear, KisAnimationCurvesModel::InterpolationModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::applyBezierMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
m_d->model->setData(index, KisKeyframe::Bezier, KisAnimationCurvesModel::InterpolationModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::applySmoothMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
QVector2D leftVisualTangent(m_d->itemDelegate->leftHandle(index, false));
QVector2D rightVisualTangent(m_d->itemDelegate->rightHandle(index, false));
if (leftVisualTangent.lengthSquared() > 0 && rightVisualTangent.lengthSquared() > 0) {
float leftAngle = qAtan2(-leftVisualTangent.y(), -leftVisualTangent.x());
float rightAngle = qAtan2(rightVisualTangent.y(), rightVisualTangent.x());
float angle = (leftAngle + rightAngle) / 2;
QVector2D unit(qCos(angle), qSin(angle));
leftVisualTangent = -unit * QVector2D(leftVisualTangent).length();
rightVisualTangent = unit * QVector2D(rightVisualTangent).length();
QPointF leftTangent = m_d->itemDelegate->unscaledTangent(leftVisualTangent.toPointF());
QPointF rightTangent = m_d->itemDelegate->unscaledTangent(rightVisualTangent.toPointF());
model()->setData(index, leftTangent, KisAnimationCurvesModel::LeftTangentRole);
model()->setData(index, rightTangent, KisAnimationCurvesModel::RightTangentRole);
}
model()->setData(index, KisKeyframe::Smooth, KisAnimationCurvesModel::TangentsModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::applySharpMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
model()->setData(index, KisKeyframe::Sharp, KisAnimationCurvesModel::TangentsModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::createKeyframe()
{
QModelIndex active = currentIndex();
int channel = active.isValid() ? active.row() : 0;
int time = m_d->model->currentTime();
QModelIndex index = m_d->model->index(channel, time);
qreal value = index.data(KisAnimationCurvesModel::ScalarValueRole).toReal();
m_d->model->setData(index, value, KisAnimationCurvesModel::ScalarValueRole);
}
void KisAnimationCurvesView::removeKeyframes()
{
m_d->model->removeFrames(selectedIndexes());
}
void KisAnimationCurvesView::zoomToFit()
{
if (!model()) return;
qreal minimum, maximum;
findExtremes(&minimum, &maximum);
if (minimum == maximum) return;
qreal zoomLevel = (viewport()->height() - 2 * VERTICAL_PADDING) / (maximum - minimum);
qreal offset = -VERTICAL_PADDING - zoomLevel * maximum;
m_d->verticalHeader->setScale(zoomLevel);
m_d->verticalHeader->setOffset(offset);
verticalScrollBar()->setValue(offset);
viewport()->update();
}
diff --git a/plugins/dockers/compositiondocker/compositiondocker_dock.cpp b/plugins/dockers/compositiondocker/compositiondocker_dock.cpp
index 29fa6bb5fc..a76ff5de48 100644
--- a/plugins/dockers/compositiondocker/compositiondocker_dock.cpp
+++ b/plugins/dockers/compositiondocker/compositiondocker_dock.cpp
@@ -1,298 +1,296 @@
/*
* 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 <klocalizedstring.h>
#include <kactioncollection.h>
#include <kis_icon.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(KisIconUtils::loadIcon("edit-delete"));
saveButton->setIcon(KisIconUtils::loadIcon("list-add"));
exportButton->setIcon(KisIconUtils::loadIcon("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)
{
if (m_canvas && m_canvas->viewManager()) {
Q_FOREACH (KisAction *action, m_actions) {
m_canvas->viewManager()->actionManager()->takeAction(action);
}
}
unsetCanvas();
setEnabled(canvas != 0);
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
if (m_canvas && m_canvas->viewManager()) {
Q_FOREACH (KisAction *action, m_actions) {
m_canvas->viewManager()->actionManager()->addAction(action->objectName(), action);
}
updateModel();
}
}
void CompositionDockerDock::unsetCanvas()
{
setEnabled(false);
m_canvas = 0;
m_model->setCompositions(QList<KisLayerCompositionSP>());
}
void CompositionDockerDock::activated(const QModelIndex& index)
{
KisLayerCompositionSP composition = m_model->compositionFromIndex(index);
composition->apply();
}
void CompositionDockerDock::deleteClicked()
{
QModelIndex index = compositionView->currentIndex();
if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) {
KisLayerCompositionSP composition = m_model->compositionFromIndex(index);
m_canvas->viewManager()->image()->removeComposition(composition);
updateModel();
}
}
void CompositionDockerDock::saveClicked()
{
KisImageWSP image = m_canvas->viewManager()->image();
if (!image) return;
// 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;
Q_FOREACH (KisLayerCompositionSP composition, m_canvas->viewManager()->image()->compositions()) {
if (composition->name() == name) {
found = true;
break;
}
}
i++;
} while(found && i < 1000);
}
KisLayerCompositionSP 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()
{
if (m_model && m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image()) {
m_model->setCompositions(m_canvas->viewManager()->image()->compositions());
}
}
void CompositionDockerDock::exportClicked()
{
if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image()) {
QString path;
KoFileDialog dialog(0, KoFileDialog::OpenDirectory, "compositiondockerdock");
dialog.setCaption(i18n("Select a Directory"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
path = dialog.filename();
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() + '_';
}
Q_FOREACH (KisLayerCompositionSP 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->setFileBatchMode(true);
d->exportDocument(QUrl::fromLocalFile(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 (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) {
KisLayerCompositionSP composition = m_model->compositionFromIndex(index);
composition->store();
m_canvas->image()->setModified();
}
}
void CompositionDockerDock::renameComposition()
{
dbgKrita << "rename";
QModelIndex index = compositionView->currentIndex();
if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) {
KisLayerCompositionSP 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/plugins/dockers/griddocker/grid_config_widget.cpp b/plugins/dockers/griddocker/grid_config_widget.cpp
index 49cece4bb2..015c07e112 100644
--- a/plugins/dockers/griddocker/grid_config_widget.cpp
+++ b/plugins/dockers/griddocker/grid_config_widget.cpp
@@ -1,238 +1,332 @@
/*
* Copyright (c) 2016 Dmitry Kazakov <dimula73@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 "grid_config_widget.h"
#include "ui_grid_config_widget.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "kis_debug.h"
#include "kis_aspect_ratio_locker.h"
#include "kis_int_parse_spin_box.h"
struct GridConfigWidget::Private
{
Private() : guiSignalsBlocked(false) {}
KisGridConfig gridConfig;
KisGuidesConfig guidesConfig;
bool guiSignalsBlocked;
};
GridConfigWidget::GridConfigWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::GridConfigWidget),
m_d(new Private)
{
ui->setupUi(this);
ui->colorMain->setAlphaChannelEnabled(true);
ui->colorSubdivision->setAlphaChannelEnabled(true);
ui->colorGuides->setAlphaChannelEnabled(true);
+
+ ui->angleLeftSpinbox->setSuffix(QChar(Qt::Key_degree));
+ ui->angleRightSpinbox->setSuffix(QChar(Qt::Key_degree));
+ ui->cellSpacingSpinbox->setSuffix(i18n(" px"));
+
+
+ ui->gridTypeCombobox->addItem(i18n("Rectangle"));
+ ui->gridTypeCombobox->addItem(i18n("Isometric"));
+ ui->gridTypeCombobox->setCurrentIndex(0); // set to rectangle by default
+ slotGridTypeChanged(); // update the UI to hide any elements we don't need
+
+
+ connect(ui->gridTypeCombobox, SIGNAL(currentIndexChanged(int)), SLOT(slotGridTypeChanged()));
+
+
+ m_isGridEnabled = false;
+
setGridConfig(m_d->gridConfig);
setGuidesConfig(m_d->guidesConfig);
+ // hide offset UI elements if offset is disabled
connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->lblXOffset, SLOT(setVisible(bool)));
connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->lblYOffset, SLOT(setVisible(bool)));
connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->intXOffset, SLOT(setVisible(bool)));
connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->intYOffset, SLOT(setVisible(bool)));
connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->offsetAspectButton, SLOT(setVisible(bool)));
ui->lblXOffset->setVisible(false);
ui->lblYOffset->setVisible(false);
ui->intXOffset->setVisible(false);
ui->intYOffset->setVisible(false);
ui->offsetAspectButton->setVisible(false);
connect(ui->chkShowGrid, SIGNAL(stateChanged(int)), SLOT(slotGridGuiChanged()));
connect(ui->chkSnapToGrid, SIGNAL(stateChanged(int)), SLOT(slotGridGuiChanged()));
connect(ui->chkShowGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged()));
connect(ui->chkSnapToGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged()));
connect(ui->chkLockGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged()));
connect(ui->intSubdivision, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged()));
+ connect(ui->angleLeftSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged()));
+ connect(ui->angleRightSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged()));
+ connect(ui->cellSpacingSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged()));
+
+
connect(ui->selectMainStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGridGuiChanged()));
connect(ui->colorMain, SIGNAL(changed(const QColor&)), SLOT(slotGridGuiChanged()));
connect(ui->selectSubdivisionStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGridGuiChanged()));
connect(ui->colorSubdivision, SIGNAL(changed(const QColor&)), SLOT(slotGridGuiChanged()));
connect(ui->selectGuidesStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGuidesGuiChanged()));
connect(ui->colorGuides, SIGNAL(changed(const QColor&)), SLOT(slotGuidesGuiChanged()));
ui->chkOffset->setChecked(false);
KisAspectRatioLocker *offsetLocker = new KisAspectRatioLocker(this);
offsetLocker->connectSpinBoxes(ui->intXOffset, ui->intYOffset, ui->offsetAspectButton);
KisAspectRatioLocker *spacingLocker = new KisAspectRatioLocker(this);
spacingLocker->connectSpinBoxes(ui->intHSpacing, ui->intVSpacing, ui->spacingAspectButton);
connect(offsetLocker, SIGNAL(sliderValueChanged()), SLOT(slotGridGuiChanged()));
connect(offsetLocker, SIGNAL(aspectButtonChanged()), SLOT(slotGridGuiChanged()));
connect(spacingLocker, SIGNAL(sliderValueChanged()), SLOT(slotGridGuiChanged()));
connect(spacingLocker, SIGNAL(aspectButtonChanged()), SLOT(slotGridGuiChanged()));
}
GridConfigWidget::~GridConfigWidget()
{
delete ui;
}
void GridConfigWidget::setGridConfig(const KisGridConfig &value)
{
KisGridConfig currentConfig = fetchGuiGridConfig();
if (currentConfig == value) return;
setGridConfigImpl(value);
}
void GridConfigWidget::setGuidesConfig(const KisGuidesConfig &value)
{
KisGuidesConfig currentConfig = fetchGuiGuidesConfig();
if (currentConfig == value) return;
setGuidesConfigImpl(value);
}
void GridConfigWidget::setGridConfigImpl(const KisGridConfig &value)
{
m_d->gridConfig = value;
m_d->guiSignalsBlocked = true;
ui->offsetAspectButton->setKeepAspectRatio(m_d->gridConfig.offsetAspectLocked());
ui->spacingAspectButton->setKeepAspectRatio(m_d->gridConfig.spacingAspectLocked());
ui->chkShowGrid->setChecked(m_d->gridConfig.showGrid());
- ui->chkSnapToGrid->setChecked(m_d->gridConfig.snapToGrid());
ui->intHSpacing->setValue(m_d->gridConfig.spacing().x());
ui->intVSpacing->setValue(m_d->gridConfig.spacing().y());
ui->intXOffset->setValue(m_d->gridConfig.offset().x());
ui->intYOffset->setValue(m_d->gridConfig.offset().y());
ui->intSubdivision->setValue(m_d->gridConfig.subdivision());
+ ui->chkSnapToGrid->setChecked(m_d->gridConfig.snapToGrid());
+ ui->angleLeftSpinbox->setValue(m_d->gridConfig.angleLeft());
+ ui->angleRightSpinbox->setValue(m_d->gridConfig.angleRight());
+ ui->cellSpacingSpinbox->setValue(m_d->gridConfig.cellSpacing());
+
ui->selectMainStyle->setCurrentIndex(int(m_d->gridConfig.lineTypeMain()));
ui->selectSubdivisionStyle->setCurrentIndex(int(m_d->gridConfig.lineTypeSubdivision()));
+ ui->gridTypeCombobox->setCurrentIndex(m_d->gridConfig.gridType());
ui->colorMain->setColor(m_d->gridConfig.colorMain());
ui->colorSubdivision->setColor(m_d->gridConfig.colorSubdivision());
m_d->guiSignalsBlocked = false;
+
+
+
+
emit gridValueChanged();
}
void GridConfigWidget::setGuidesConfigImpl(const KisGuidesConfig &value)
{
m_d->guidesConfig = value;
m_d->guiSignalsBlocked = true;
ui->chkShowGuides->setChecked(m_d->guidesConfig.showGuides());
ui->chkSnapToGuides->setChecked(m_d->guidesConfig.snapToGuides());
ui->chkLockGuides->setChecked(m_d->guidesConfig.lockGuides());
ui->selectGuidesStyle->setCurrentIndex(int(m_d->guidesConfig.guidesLineType()));
ui->colorGuides->setColor(m_d->guidesConfig.guidesColor());
m_d->guiSignalsBlocked = false;
emit guidesValueChanged();
}
KisGridConfig GridConfigWidget::gridConfig() const
{
return m_d->gridConfig;
}
KisGuidesConfig GridConfigWidget::guidesConfig() const
{
return m_d->guidesConfig;
}
void GridConfigWidget::setGridDivision(int w, int h)
{
ui->intHSpacing->setMaximum(w);
ui->intVSpacing->setMaximum(h);
}
KisGridConfig GridConfigWidget::fetchGuiGridConfig() const
{
KisGridConfig config;
config.setShowGrid(ui->chkShowGrid->isChecked());
config.setSnapToGrid(ui->chkSnapToGrid->isChecked());
QPoint pt;
pt.rx() = ui->intHSpacing->value();
pt.ry() = ui->intVSpacing->value();
config.setSpacing(pt);
pt.rx() = ui->intXOffset->value();
pt.ry() = ui->intYOffset->value();
config.setOffset(pt);
config.setSubdivision(ui->intSubdivision->value());
+ config.setAngleLeft(ui->angleLeftSpinbox->value());
+ config.setAngleRight(ui->angleRightSpinbox->value());
+ config.setCellSpacing(ui->cellSpacingSpinbox->value());
+ config.setGridType(ui->gridTypeCombobox->currentIndex());
config.setOffsetAspectLocked(ui->offsetAspectButton->keepAspectRatio());
config.setSpacingAspectLocked(ui->spacingAspectButton->keepAspectRatio());
config.setLineTypeMain(KisGridConfig::LineTypeInternal(ui->selectMainStyle->currentIndex()));
config.setLineTypeSubdivision(KisGridConfig::LineTypeInternal(ui->selectSubdivisionStyle->currentIndex()));
config.setColorMain(ui->colorMain->color());
config.setColorSubdivision(ui->colorSubdivision->color());
return config;
}
KisGuidesConfig GridConfigWidget::fetchGuiGuidesConfig() const
{
KisGuidesConfig config = m_d->guidesConfig;
config.setShowGuides(ui->chkShowGuides->isChecked());
config.setSnapToGuides(ui->chkSnapToGuides->isChecked());
config.setLockGuides(ui->chkLockGuides->isChecked());
config.setGuidesLineType(KisGuidesConfig::LineTypeInternal(ui->selectGuidesStyle->currentIndex()));
config.setGuidesColor(ui->colorGuides->color());
return config;
}
void GridConfigWidget::slotGridGuiChanged()
{
if (m_d->guiSignalsBlocked) return;
KisGridConfig currentConfig = fetchGuiGridConfig();
if (currentConfig == m_d->gridConfig) return;
setGridConfigImpl(currentConfig);
}
void GridConfigWidget::slotGuidesGuiChanged()
{
if (m_d->guiSignalsBlocked) return;
KisGuidesConfig currentConfig = fetchGuiGuidesConfig();
if (currentConfig == m_d->guidesConfig) return;
setGuidesConfigImpl(currentConfig);
}
+
+void GridConfigWidget::slotGridTypeChanged() {
+
+
+ bool showRectangleControls = ui->gridTypeCombobox->currentIndex() == 0;
+
+
+ // specific rectangle UI controls
+ ui->lblXSpacing->setVisible(showRectangleControls);
+ ui->lblYSpacing->setVisible(showRectangleControls);
+ ui->intHSpacing->setVisible(showRectangleControls);
+ ui->intVSpacing->setVisible(showRectangleControls);
+ ui->spacingAspectButton->setVisible(showRectangleControls);
+
+ ui->lblSubdivision->setVisible(showRectangleControls);
+ ui->intSubdivision->setVisible(showRectangleControls);
+ ui->lblSubdivisionStyle->setVisible(showRectangleControls);
+ ui->selectSubdivisionStyle->setVisible(showRectangleControls);
+ ui->colorSubdivision->setVisible(showRectangleControls);
+
+
+ // specific isometric UI controls
+ ui->leftAngleLabel->setVisible(!showRectangleControls);
+ ui->rightAngleLabel->setVisible(!showRectangleControls);
+ ui->angleLeftSpinbox->setVisible(!showRectangleControls);
+ ui->angleRightSpinbox->setVisible(!showRectangleControls);
+ ui->cellSpacingLabel->setVisible(!showRectangleControls);
+ ui->cellSpacingSpinbox->setVisible(!showRectangleControls);
+
+
+
+ // disable snapping for isometric grid type for now
+ // remember if we had snapping enabled if it was on the rectangule mode
+ if (!showRectangleControls) {
+ m_isGridEnabled = ui->chkSnapToGrid->isChecked();
+ ui->chkSnapToGrid->setEnabled(false);
+ ui->chkSnapToGrid->setChecked(false);
+ } else {
+ ui->chkSnapToGrid->setEnabled(true);
+ ui->chkSnapToGrid->setChecked(m_isGridEnabled);
+ }
+
+
+
+
+
+
+
+
+
+
+ slotGridGuiChanged();
+}
+
+
+
+
+
diff --git a/plugins/dockers/griddocker/grid_config_widget.h b/plugins/dockers/griddocker/grid_config_widget.h
index 0b5f6d34af..931d20ce6c 100644
--- a/plugins/dockers/griddocker/grid_config_widget.h
+++ b/plugins/dockers/griddocker/grid_config_widget.h
@@ -1,69 +1,72 @@
/*
* Copyright (c) 2016 Dmitry Kazakov <dimula73@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.
*/
#ifndef GRID_CONFIG_WIDGET_H
#define GRID_CONFIG_WIDGET_H
#include <QWidget>
#include <QScopedPointer>
namespace Ui {
class GridConfigWidget;
}
class KisGridConfig;
class KisGuidesConfig;
class GridConfigWidget : public QWidget
{
Q_OBJECT
public:
explicit GridConfigWidget(QWidget *parent = 0);
~GridConfigWidget();
void setGridConfig(const KisGridConfig &value);
KisGridConfig gridConfig() const;
void setGuidesConfig(const KisGuidesConfig &value);
KisGuidesConfig guidesConfig() const;
void setGridDivision(int w, int h);
private Q_SLOTS:
void slotGridGuiChanged();
void slotGuidesGuiChanged();
+ void slotGridTypeChanged();
Q_SIGNALS:
void gridValueChanged();
void guidesValueChanged();
private:
KisGridConfig fetchGuiGridConfig() const;
void setGridConfigImpl(const KisGridConfig &value);
KisGuidesConfig fetchGuiGuidesConfig() const;
void setGuidesConfigImpl(const KisGuidesConfig &value);
+
private:
Ui::GridConfigWidget *ui;
struct Private;
const QScopedPointer<Private> m_d;
+ bool m_isGridEnabled;
};
#endif // GRID_CONFIG_WIDGET_H
diff --git a/plugins/dockers/griddocker/grid_config_widget.ui b/plugins/dockers/griddocker/grid_config_widget.ui
index 86863a1b26..bee6e42556 100644
--- a/plugins/dockers/griddocker/grid_config_widget.ui
+++ b/plugins/dockers/griddocker/grid_config_widget.ui
@@ -1,509 +1,716 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GridConfigWidget</class>
<widget class="QWidget" name="GridConfigWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>441</width>
- <height>342</height>
+ <width>258</width>
+ <height>527</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QTabWidget" name="tabWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="minimumSize">
<size>
<width>187</width>
<height>280</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Grid</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
<item>
<widget class="QCheckBox" name="chkShowGrid">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="text">
<string>Show grid</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkSnapToGrid">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="text">
<string>Snap to grid</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
- <layout class="QGridLayout" name="gridLayout_2">
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="gridTypeCombobox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="isometricControlsLayout" columnstretch="0,0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="cellSpacingSpinbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string/>
+ </property>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>1000</number>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="angleRightSpinbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>89</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="cellSpacingLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Cell Spacing:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="angleLeftSpinbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>89</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="rightAngleLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Right Angle:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="leftAngleLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Left Angle:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="rectangleGridLayout">
<item row="0" column="0">
- <widget class="QLabel" name="lblXSpacing">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>X spacing:</string>
- </property>
- </widget>
- </item>
+ <widget class="QLabel" name="lblXSpacing">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>X spacing:</string>
+ </property>
+ </widget>
+ </item>
<item row="0" column="1">
<widget class="KisIntParseSpinBox" name="intHSpacing">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>500</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="0" column="2" rowspan="2">
<widget class="KoAspectButton" name="spacingAspectButton" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblYSpacing">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Y spacing:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisIntParseSpinBox" name="intVSpacing">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>500</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblSubdivision">
<property name="text">
<string>Subdivision:</string>
</property>
</widget>
</item>
- <item row="2" column="1" colspan="2">
+ <item row="2" column="1">
<widget class="KisIntParseSpinBox" name="intSubdivision">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
- <item row="3" column="0" colspan="3">
- <widget class="QCheckBox" name="chkOffset">
- <property name="text">
- <string>Grid Offset</string>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="2">
+ <widget class="KColorButton" name="colorSubdivision">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="checked">
- <bool>false</bool>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="color" stdset="0">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="defaultColor" stdset="0">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
</property>
</widget>
</item>
- <item row="4" column="0">
- <widget class="QLabel" name="lblXOffset">
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblSubdivisionStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>X offset:</string>
+ <string>Div Style:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="4" column="1">
- <widget class="KisIntParseSpinBox" name="intXOffset">
- <property name="suffix">
- <string> px</string>
- </property>
- <property name="maximum">
- <number>500</number>
- </property>
- <property name="value">
- <number>0</number>
+ <item row="1" column="1">
+ <widget class="KComboBox" name="selectSubdivisionStyle">
+ <property name="currentIndex">
+ <number>1</number>
</property>
+ <item>
+ <property name="text">
+ <string>Lines</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dashed</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dots</string>
+ </property>
+ </item>
</widget>
</item>
- <item row="4" column="2" rowspan="2">
- <widget class="KoAspectButton" name="offsetAspectButton" native="true"/>
- </item>
- <item row="5" column="0">
- <widget class="QLabel" name="lblYOffset">
+ <item row="0" column="2">
+ <widget class="KColorButton" name="colorMain">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="text">
- <string>Y offset:</string>
+ <string/>
</property>
</widget>
</item>
- <item row="5" column="1">
- <widget class="KisIntParseSpinBox" name="intYOffset">
- <property name="suffix">
- <string> px</string>
- </property>
- <property name="maximum">
- <number>500</number>
- </property>
- <property name="value">
+ <item row="0" column="1">
+ <widget class="KComboBox" name="selectMainStyle">
+ <property name="currentIndex">
<number>0</number>
</property>
+ <item>
+ <property name="text">
+ <string>Lines</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dashed</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dots</string>
+ </property>
+ </item>
</widget>
</item>
- <item row="6" column="0">
+ <item row="0" column="0">
<widget class="QLabel" name="lblMainStyle">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Main Style:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="6" column="1" colspan="2">
- <layout class="QHBoxLayout" name="horizontalLayout_4">
- <item>
- <widget class="KComboBox" name="selectMainStyle">
- <property name="currentIndex">
+ </layout>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="chkOffset">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>107</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Grid Offset</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="1">
+ <widget class="KisIntParseSpinBox" name="intXOffset">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="maximum">
+ <number>500</number>
+ </property>
+ <property name="value">
<number>0</number>
</property>
- <item>
- <property name="text">
- <string>Lines</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Dashed</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Dots</string>
- </property>
- </item>
</widget>
</item>
- <item>
- <widget class="KColorButton" name="colorMain">
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblXOffset">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>30</width>
- <height>0</height>
- </size>
- </property>
<property name="text">
- <string/>
+ <string>X offset:</string>
</property>
</widget>
</item>
- </layout>
- </item>
- <item row="7" column="0">
- <widget class="QLabel" name="lblSubdivisionStyle">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Div Style:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="7" column="1" colspan="2">
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="KComboBox" name="selectSubdivisionStyle">
- <property name="currentIndex">
- <number>1</number>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblYOffset">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Y offset:</string>
</property>
- <item>
- <property name="text">
- <string>Lines</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Dashed</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Dots</string>
- </property>
- </item>
</widget>
</item>
- <item>
- <widget class="KColorButton" name="colorSubdivision">
+ <item row="1" column="1">
+ <widget class="KisIntParseSpinBox" name="intYOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>30</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string/>
+ <property name="suffix">
+ <string> px</string>
</property>
- <property name="color" stdset="0">
- <color>
- <red>0</red>
- <green>0</green>
- <blue>0</blue>
- </color>
+ <property name="maximum">
+ <number>500</number>
</property>
- <property name="defaultColor" stdset="0">
- <color>
- <red>0</red>
- <green>0</green>
- <blue>0</blue>
- </color>
+ <property name="value">
+ <number>0</number>
</property>
</widget>
</item>
</layout>
</item>
+ <item>
+ <widget class="KoAspectButton" name="offsetAspectButton" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>24</width>
+ <height>24</height>
+ </size>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item>
- <spacer name="verticalSpacer_3">
+ <spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
- <height>13</height>
+ <height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Guides</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QCheckBox" name="chkShowGuides">
<property name="text">
<string>Show guides</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="chkSnapToGuides">
<property name="text">
<string>Snap to guides</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="chkLockGuides">
<property name="text">
<string>Lock guides</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="lblGuidesStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Guides:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="KComboBox" name="selectGuidesStyle">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Lines</string>
</property>
</item>
<item>
<property name="text">
<string>Dashed</string>
</property>
</item>
<item>
<property name="text">
<string>Dots</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="KColorButton" name="colorGuides">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="color" stdset="0">
<color>
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</property>
<property name="defaultColor" stdset="0">
<color>
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</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>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KoAspectButton</class>
<extends>QWidget</extends>
<header>KoAspectButton.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisIntParseSpinBox</class>
<extends>QSpinBox</extends>
<header>kis_int_parse_spin_box.h</header>
</customwidget>
<customwidget>
<class>KColorButton</class>
<extends>QPushButton</extends>
<header>kcolorbutton.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KComboBox</class>
<extends>QComboBox</extends>
<header>kcombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/plugins/extensions/animationrenderer/AnimationRenderer.cpp b/plugins/extensions/animationrenderer/AnimationRenderer.cpp
index ec3eb696f9..8a8edb6709 100644
--- a/plugins/extensions/animationrenderer/AnimationRenderer.cpp
+++ b/plugins/extensions/animationrenderer/AnimationRenderer.cpp
@@ -1,172 +1,177 @@
/*
* Copyright (c) 2016 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 "AnimationRenderer.h"
#include <QMessageBox>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <kis_image.h>
#include <KisViewManager.h>
#include <kis_node_manager.h>
#include <kis_image_manager.h>
#include <kis_action.h>
#include <kis_image_animation_interface.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include <kis_animation_exporter.h>
#include <KisDocument.h>
#include <KisMimeDatabase.h>
#include <kis_time_range.h>
#include <KisImportExportManager.h>
-#include <KisFilterChain.h>
#include "DlgAnimationRenderer.h"
K_PLUGIN_FACTORY_WITH_JSON(AnimaterionRendererFactory, "kritaanimationrenderer.json", registerPlugin<AnimaterionRenderer>();)
AnimaterionRenderer::AnimaterionRenderer(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
{
// Shows the big dialog
KisAction *action = createAction("render_animation");
action->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
connect(action, SIGNAL(triggered()), this, SLOT(slotRenderAnimation()));
// Re-renders the image sequence as defined in the last render
action = createAction("render_image_sequence_again");
action->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
connect(action, SIGNAL(triggered()), this, SLOT(slotRenderSequenceAgain()));
}
AnimaterionRenderer::~AnimaterionRenderer()
{
}
void AnimaterionRenderer::slotRenderAnimation()
{
KisImageWSP image = m_view->image();
if (!image) return;
if (!image->animationInterface()->hasAnimation()) return;
KisDocument *doc = m_view->document();
doc->setFileProgressProxy();
doc->setFileProgressUpdater(i18n("Export frames"));
DlgAnimationRenderer dlgAnimationRenderer(doc, m_view->mainWindow());
dlgAnimationRenderer.setCaption(i18n("Render Animation"));
KisConfig kisConfig;
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->fromXML(kisConfig.exportConfiguration("IMAGESEQUENCE"));
// Override the saved start/end with the ones from the image in case of using gui.
cfg->setProperty("first_frame", image->animationInterface()->playbackRange().start());
cfg->setProperty("last_frame", image->animationInterface()->playbackRange().end());
dlgAnimationRenderer.setSequenceConfiguration(cfg);
cfg->clearProperties();
cfg->fromXML(kisConfig.exportConfiguration("ANIMATION_RENDERER"));
dlgAnimationRenderer.setVideoConfiguration(cfg);
cfg->clearProperties();
cfg->fromXML(kisConfig.exportConfiguration("FFMPEG_CONFIG"));
dlgAnimationRenderer.setEncoderConfiguration(cfg);
if (dlgAnimationRenderer.exec() == QDialog::Accepted) {
KisPropertiesConfigurationSP sequenceConfig = dlgAnimationRenderer.getSequenceConfiguration();
- kisConfig.setExportConfiguration("IMAGESEQUENCE", *sequenceConfig.data());
+ kisConfig.setExportConfiguration("IMAGESEQUENCE", sequenceConfig);
QString mimetype = sequenceConfig->getString("mimetype");
QString extension = KisMimeDatabase::suffixesForMimeType(mimetype).first();
QString baseFileName = QString("%1/%2.%3").arg(sequenceConfig->getString("directory"))
.arg(sequenceConfig->getString("basename"))
.arg(extension);
KisAnimationExportSaver exporter(doc, baseFileName, sequenceConfig->getInt("first_frame"), sequenceConfig->getInt("last_frame"), sequenceConfig->getInt("sequence_start"));
bool success = exporter.exportAnimation(dlgAnimationRenderer.getFrameExportConfiguration());
Q_ASSERT(success);
QString savedFilesMask = exporter.savedFilesMask();
KisPropertiesConfigurationSP videoConfig = dlgAnimationRenderer.getVideoConfiguration();
if (videoConfig) {
- kisConfig.setExportConfiguration("ANIMATION_RENDERER", *videoConfig.data());
+ kisConfig.setExportConfiguration("ANIMATION_RENDERER", videoConfig);
KisPropertiesConfigurationSP encoderConfig = dlgAnimationRenderer.getEncoderConfiguration();
if (encoderConfig) {
- kisConfig.setExportConfiguration("FFMPEG_CONFIG", *encoderConfig.data());
+ kisConfig.setExportConfiguration("FFMPEG_CONFIG", encoderConfig);
encoderConfig->setProperty("savedFilesMask", savedFilesMask);
}
QSharedPointer<KisImportExportFilter> encoder = dlgAnimationRenderer.encoderFilter();
- KisFilterChainSP chain(new KisFilterChain(doc->importExportManager()));
- chain->setOutputFile(videoConfig->getString("filename"));
- encoder->setChain(chain);
- KisImportExportFilter::ConversionStatus res = encoder->convert(KisDocument::nativeFormatMimeType(), encoderConfig->getString("mimetype").toLatin1(), encoderConfig);
+ encoder->setMimeType(mimetype.toLatin1());
+ QFile fi(videoConfig->getString("filename"));
+ KisImportExportFilter::ConversionStatus res;
+ if (!fi.open(QIODevice::WriteOnly)) {
+ qWarning() << "Could not open" << fi.fileName() << "for writing!";
+ res = KisImportExportFilter::CreationError;
+ }
+ else {
+ res = encoder->convert(doc, &fi, encoderConfig);
+ fi.close();
+ }
if (res != KisImportExportFilter::OK) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not render animation:\n%1", doc->errorMessage()));
}
-
if (videoConfig->getBool("delete_sequence", false)) {
QDir d(sequenceConfig->getString("directory"));
QStringList sequenceFiles = d.entryList(QStringList() << sequenceConfig->getString("basename") + "*." + extension, QDir::Files);
Q_FOREACH(const QString &f, sequenceFiles) {
d.remove(f);
}
}
}
}
doc->clearFileProgressUpdater();
doc->clearFileProgressProxy();
}
void AnimaterionRenderer::slotRenderSequenceAgain()
{
KisImageWSP image = m_view->image();
if (!image) return;
if (!image->animationInterface()->hasAnimation()) return;
KisDocument *doc = m_view->document();
doc->setFileProgressProxy();
doc->setFileProgressUpdater(i18n("Export frames"));
KisConfig kisConfig;
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->fromXML(kisConfig.exportConfiguration("IMAGESEQUENCE"));
QString mimetype = cfg->getString("mimetype");
QString extension = KisMimeDatabase::suffixesForMimeType(mimetype).first();
QString baseFileName = QString("%1/%2.%3").arg(cfg->getString("directory"))
.arg(cfg->getString("basename"))
.arg(extension);
KisAnimationExportSaver exporter(doc, baseFileName, cfg->getInt("first_frame"), cfg->getInt("last_frame"), cfg->getInt("sequence_start"));
bool success = exporter.exportAnimation();
Q_ASSERT(success);
doc->clearFileProgressUpdater();
doc->clearFileProgressProxy();
}
#include "AnimationRenderer.moc"
diff --git a/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp b/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp
index 429a90ce5f..2fe95527dc 100644
--- a/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp
+++ b/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp
@@ -1,405 +1,404 @@
/*
* Copyright (c) 2016 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 "DlgAnimationRenderer.h"
#include <QStandardPaths>
#include <QPluginLoader>
#include <QJsonObject>
#include <QMessageBox>
#include <QStringList>
#include <QProcess>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <KoResourcePaths.h>
#include <kis_properties_configuration.h>
#include <kis_debug.h>
#include <KisMimeDatabase.h>
#include <KoJsonTrader.h>
#include <KisImportExportFilter.h>
#include <kis_image.h>
#include <kis_image_animation_interface.h>
#include <kis_time_range.h>
#include <KisImportExportManager.h>
#include <kis_config_widget.h>
#include <KisDocument.h>
#include <QHBoxLayout>
#include <KisImportExportFilter.h>
#include <kis_config.h>
#include <kis_file_name_requester.h>
#include <KisDocument.h>
#include <KoDialog.h>
DlgAnimationRenderer::DlgAnimationRenderer(KisDocument *doc, QWidget *parent)
: KoDialog(parent)
, m_image(doc->image())
, m_defaultFileName(QFileInfo(doc->url().toLocalFile()).completeBaseName())
{
KisConfig cfg;
setCaption(i18n("Render Animation"));
setButtons(Ok | Cancel);
setDefaultButton(Ok);
m_page = new WdgAnimaterionRenderer(this);
m_page->layout()->setMargin(0);
m_page->dirRequester->setMode(KoFileDialog::OpenDirectory);
QString lastLocation = cfg.readEntry<QString>("AnimationRenderer/last_sequence_export_location", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
m_page->dirRequester->setFileName(lastLocation);
m_page->intStart->setMinimum(doc->image()->animationInterface()->fullClipRange().start());
m_page->intStart->setMaximum(doc->image()->animationInterface()->fullClipRange().end());
m_page->intStart->setValue(doc->image()->animationInterface()->playbackRange().start());
m_page->intEnd->setMinimum(doc->image()->animationInterface()->fullClipRange().start());
m_page->intEnd->setMaximum(doc->image()->animationInterface()->fullClipRange().end());
m_page->intEnd->setValue(doc->image()->animationInterface()->playbackRange().end());
QStringList mimes = KisImportExportManager::mimeFilter(KisImportExportManager::Export);
mimes.sort();
Q_FOREACH(const QString &mime, mimes) {
QString description = KisMimeDatabase::descriptionForMimeType(mime);
if (description.isEmpty()) {
description = mime;
}
m_page->cmbMimetype->addItem(description, mime);
if (mime == "image/png") {
m_page->cmbMimetype->setCurrentIndex(m_page->cmbMimetype->count() - 1);
}
}
setMainWidget(m_page);
resize(m_page->sizeHint());
KoJsonTrader trader;
QList<QPluginLoader *>list = trader.query("Krita/AnimationExporter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
QStringList mimetypes = json.value("X-KDE-Export").toString().split(",");
Q_FOREACH(const QString &mime, mimetypes) {
KLibFactory *factory = qobject_cast<KLibFactory *>(loader->instance());
if (!factory) {
warnUI << loader->errorString();
continue;
}
QObject* obj = factory->create<KisImportExportFilter>(0);
if (!obj || !obj->inherits("KisImportExportFilter")) {
delete obj;
continue;
}
QSharedPointer<KisImportExportFilter>filter(static_cast<KisImportExportFilter*>(obj));
if (!filter) {
delete obj;
continue;
}
m_renderFilters.append(filter);
QString description = KisMimeDatabase::descriptionForMimeType(mime);
if (description.isEmpty()) {
description = mime;
}
m_page->cmbRenderType->addItem(description, mime);
}
}
m_page->videoFilename->setMode(KoFileDialog::SaveFile);
m_page->videoFilename->setStartDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
qDeleteAll(list);
connect(m_page->grpRender, SIGNAL(toggled(bool)), this, SLOT(toggleSequenceType(bool)));
connect(m_page->bnExportOptions, SIGNAL(clicked()), this, SLOT(sequenceMimeTypeSelected()));
connect(m_page->bnRenderOptions, SIGNAL(clicked()), this, SLOT(selectRenderType()));
m_page->ffmpegLocation->setFileName(findFFMpeg());
m_page->ffmpegLocation->setMode(KoFileDialog::OpenFile);
connect(m_page->ffmpegLocation, SIGNAL(fileSelected(QString)), this, SLOT(ffmpegLocationChanged(QString)));
m_page->grpRender->setChecked(cfg.readEntry<bool>("AnimationRenderer/render_animation", false));
m_page->chkDeleteSequence->setChecked(cfg.readEntry<bool>("AnimationRenderer/delete_sequence", false));
m_page->cmbRenderType->setCurrentIndex(cfg.readEntry<int>("AnimationRenderer/render_type", 0));
}
DlgAnimationRenderer::~DlgAnimationRenderer()
{
KisConfig cfg;
cfg.writeEntry<bool>("AnimationRenderer/render_animation", m_page->grpRender->isChecked());
cfg.writeEntry<QString>("AnimationRenderer/last_sequence_export_location", m_page->dirRequester->fileName());
cfg.writeEntry<int>("AnimationRenderer/render_type", m_page->cmbRenderType->currentIndex());
cfg.writeEntry<bool>("AnimationRenderer/delete_sequence", m_page->chkDeleteSequence->isChecked());
cfg.setCustomFFMpegPath(m_page->ffmpegLocation->fileName());
if (m_encoderConfigWidget) {
m_encoderConfigWidget->setParent(0);
m_encoderConfigWidget->deleteLater();
}
if (m_frameExportConfigWidget) {
m_frameExportConfigWidget->setParent(0);
m_frameExportConfigWidget->deleteLater();
}
delete m_page;
}
KisPropertiesConfigurationSP DlgAnimationRenderer::getSequenceConfiguration() const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("basename", m_page->txtBasename->text());
cfg->setProperty("directory", m_page->dirRequester->fileName());
cfg->setProperty("first_frame", m_page->intStart->value());
cfg->setProperty("last_frame", m_page->intEnd->value());
cfg->setProperty("sequence_start", m_page->sequenceStart->value());
cfg->setProperty("mimetype", m_page->cmbMimetype->currentData().toString());
return cfg;
}
void DlgAnimationRenderer::setSequenceConfiguration(KisPropertiesConfigurationSP cfg)
{
m_page->txtBasename->setText(cfg->getString("basename", "frame"));
m_page->dirRequester->setFileName(cfg->getString("directory", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)));
m_page->intStart->setValue(cfg->getInt("first_frame", m_image->animationInterface()->playbackRange().start()));
m_page->intEnd->setValue(cfg->getInt("last_frame", m_image->animationInterface()->playbackRange().end()));
m_page->sequenceStart->setValue(cfg->getInt("sequence_start", m_image->animationInterface()->playbackRange().start()));
QString mimetype = cfg->getString("mimetype");
for (int i = 0; i < m_page->cmbMimetype->count(); ++i) {
if (m_page->cmbMimetype->itemData(i).toString() == mimetype) {
m_page->cmbMimetype->setCurrentIndex(i);
break;
}
}
}
KisPropertiesConfigurationSP DlgAnimationRenderer::getFrameExportConfiguration() const
{
if (m_frameExportConfigWidget) {
KisPropertiesConfigurationSP cfg = m_frameExportConfigWidget->configuration();
cfg->setProperty("basename", m_page->txtBasename->text());
cfg->setProperty("directory", m_page->dirRequester->fileName());
cfg->setProperty("first_frame", m_page->intStart->value());
cfg->setProperty("last_frame", m_page->intEnd->value());
cfg->setProperty("sequence_start", m_page->sequenceStart->value());
return m_frameExportConfigWidget->configuration();
}
return 0;
}
bool DlgAnimationRenderer::renderToVideo() const
{
return m_page->grpRender->isChecked();
}
KisPropertiesConfigurationSP DlgAnimationRenderer::getVideoConfiguration() const
{
if (!m_page->grpRender->isChecked()) {
return 0;
}
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("filename", m_page->videoFilename->fileName());
cfg->setProperty("delete_sequence", m_page->chkDeleteSequence->isChecked());
return cfg;
}
void DlgAnimationRenderer::setVideoConfiguration(KisPropertiesConfigurationSP /*cfg*/)
{
}
KisPropertiesConfigurationSP DlgAnimationRenderer::getEncoderConfiguration() const
{
if (!m_page->grpRender->isChecked()) {
return 0;
}
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
if (m_encoderConfigWidget) {
cfg = m_encoderConfigWidget->configuration();
}
cfg->setProperty("mimetype", m_page->cmbRenderType->currentData().toString());
cfg->setProperty("directory", m_page->dirRequester->fileName());
cfg->setProperty("first_frame", m_page->intStart->value());
cfg->setProperty("last_frame", m_page->intEnd->value());
cfg->setProperty("sequence_start", m_page->sequenceStart->value());
return cfg;
}
void DlgAnimationRenderer::setEncoderConfiguration(KisPropertiesConfigurationSP /*cfg*/)
{
}
QSharedPointer<KisImportExportFilter> DlgAnimationRenderer::encoderFilter() const
{
if (m_page->cmbRenderType->currentIndex() < m_renderFilters.size()) {
return m_renderFilters[m_page->cmbRenderType->currentIndex()];
}
return QSharedPointer<KisImportExportFilter>(0);
}
void DlgAnimationRenderer::selectRenderType()
{
int index = m_page->cmbRenderType->currentIndex();
if (m_encoderConfigWidget) {
m_encoderConfigWidget->deleteLater();
m_encoderConfigWidget = 0;
}
if (index >= m_renderFilters.size()) return;
QSharedPointer<KisImportExportFilter> filter = m_renderFilters[index];
QString mimetype = m_page->cmbRenderType->itemData(index).toString();
if (!m_page->videoFilename->fileName().isEmpty() && QFileInfo(m_page->videoFilename->fileName()).completeBaseName() != m_defaultFileName) {
m_defaultFileName = QFileInfo(m_page->videoFilename->fileName()).completeBaseName();
}
m_page->videoFilename->setMimeTypeFilters(QStringList() << mimetype, mimetype);
m_page->videoFilename->setFileName(m_defaultFileName + "." + KisMimeDatabase::suffixesForMimeType(mimetype).first());
if (filter) {
m_encoderConfigWidget = filter->createConfigurationWidget(0, KisDocument::nativeFormatMimeType(), mimetype.toLatin1());
if (m_encoderConfigWidget) {
m_encoderConfigWidget->setConfiguration(filter->lastSavedConfiguration("", mimetype.toLatin1()));
KoDialog dlg(this);
dlg.setMainWidget(m_encoderConfigWidget);
dlg.setButtons(KoDialog::Ok | KoDialog::Cancel);
if (!dlg.exec()) {
m_encoderConfigWidget->setConfiguration(filter->lastSavedConfiguration());
}
dlg.setMainWidget(0);
m_encoderConfigWidget->hide();
m_encoderConfigWidget->setParent(0);
}
}
else {
m_encoderConfigWidget = 0;
}
}
void DlgAnimationRenderer::toggleSequenceType(bool toggle)
{
m_page->cmbMimetype->setEnabled(!toggle);
for (int i = 0; i < m_page->cmbMimetype->count(); ++i) {
if (m_page->cmbMimetype->itemData(i).toString() == "image/png") {
m_page->cmbMimetype->setCurrentIndex(i);
break;
}
}
}
void DlgAnimationRenderer::sequenceMimeTypeSelected()
{
int index = m_page->cmbMimetype->currentIndex();
if (m_frameExportConfigWidget) {
m_frameExportConfigWidget->deleteLater();
m_frameExportConfigWidget = 0;
}
QString mimetype = m_page->cmbMimetype->itemData(index).toString();
- KisImportExportFilter *filter = KisImportExportManager::filterForMimeType(mimetype, KisImportExportManager::Export);
+ QSharedPointer<KisImportExportFilter> filter(KisImportExportManager::filterForMimeType(mimetype, KisImportExportManager::Export));
if (filter) {
m_frameExportConfigWidget = filter->createConfigurationWidget(0, KisDocument::nativeFormatMimeType(), mimetype.toLatin1());
if (m_frameExportConfigWidget) {
m_frameExportConfigWidget->setConfiguration(filter->lastSavedConfiguration("", mimetype.toLatin1()));
KoDialog dlg(this);
dlg.setMainWidget(m_frameExportConfigWidget);
dlg.setButtons(KoDialog::Ok | KoDialog::Cancel);
if (!dlg.exec()) {
m_frameExportConfigWidget->setConfiguration(filter->lastSavedConfiguration());
}
m_frameExportConfigWidget->hide();
m_frameExportConfigWidget->setParent(0);
dlg.setMainWidget(0);
}
- delete filter;
}
}
void DlgAnimationRenderer::ffmpegLocationChanged(const QString &s)
{
KisConfig cfg;
cfg.setCustomFFMpegPath(s);
}
void DlgAnimationRenderer::slotButtonClicked(int button)
{
if (button == KoDialog::Ok && m_page->grpRender->isChecked()) {
QString ffmpeg = m_page->ffmpegLocation->fileName();
if (m_page->videoFilename->fileName().isEmpty()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("Please enter a file name to render to."));
return;
}
else if (ffmpeg.isEmpty()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The location of FFmpeg is unknown. Please install FFmpeg first: Krita cannot render animations without FFmpeg. (<a href=\"https://www.ffmpeg.org\">www.ffmpeg.org</a>)"));
return;
}
else {
QFileInfo fi(ffmpeg);
if (!fi.exists()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The location of FFmpeg is invalid. Please select the correct location of the FFmpeg executable on your system."));
return;
}
}
}
KoDialog::slotButtonClicked(button);
}
QString DlgAnimationRenderer::findFFMpeg()
{
QString result;
QStringList proposedPaths;
QString customPath = KisConfig().customFFMpegPath();
proposedPaths << customPath;
proposedPaths << customPath + QDir::separator() + "ffmpeg";
proposedPaths << "ffmpeg";
proposedPaths << KoResourcePaths::getApplicationRoot() +
QDir::separator() + "bin" + QDir::separator() + "ffmpeg";
Q_FOREACH (const QString &path, proposedPaths) {
if (path.isEmpty()) continue;
QProcess testProcess;
testProcess.start(path, QStringList() << "-version");
testProcess.waitForFinished(1000);
const bool successfulStart =
testProcess.state() == QProcess::NotRunning &&
testProcess.error() == QProcess::UnknownError;
if (successfulStart) {
result = path;
break;
}
}
return result;
}
diff --git a/plugins/extensions/colorspaceconversion/colorspaceconversion.cc b/plugins/extensions/colorspaceconversion/colorspaceconversion.cc
index 81367b85a0..dcfd10912f 100644
--- a/plugins/extensions/colorspaceconversion/colorspaceconversion.cc
+++ b/plugins/extensions/colorspaceconversion/colorspaceconversion.cc
@@ -1,137 +1,134 @@
/*
* colorspaceconversion.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 "colorspaceconversion.h"
#include <QApplication>
#include <QCursor>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <KoColorSpace.h>
#include <kis_undo_adapter.h>
#include <kis_transaction.h>
#include <kis_annotation.h>
#include <kis_config.h>
#include <kis_cursor.h>
#include <kis_global.h>
#include <kis_image.h>
#include <kis_node_manager.h>
#include <kis_layer.h>
#include <kis_types.h>
#include <kis_colorspace_convert_visitor.h>
#include <KisViewManager.h>
#include <kis_paint_device.h>
#include <kis_action.h>
#include <kis_group_layer.h>
#include "dlg_colorspaceconversion.h"
#include "kis_action_manager.h"
K_PLUGIN_FACTORY_WITH_JSON(ColorSpaceConversionFactory, "kritacolorspaceconversion.json", registerPlugin<ColorSpaceConversion>();)
ColorSpaceConversion::ColorSpaceConversion(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
{
KisAction *action = m_view->actionManager()->createAction("imagecolorspaceconversion");
connect(action, SIGNAL(triggered()), this, SLOT(slotImageColorSpaceConversion()));
action = m_view->actionManager()->createAction("layercolorspaceconversion");
connect(action, SIGNAL(triggered()), this, SLOT(slotLayerColorSpaceConversion()));
}
ColorSpaceConversion::~ColorSpaceConversion()
{
}
void ColorSpaceConversion::slotImageColorSpaceConversion()
{
- KisImageWSP image = m_view->image();
-
+ KisImageSP image = m_view->image().toStrongRef();
if (!image) return;
-
DlgColorSpaceConversion * dlgColorSpaceConversion = new DlgColorSpaceConversion(m_view->mainWindow(), "ColorSpaceConversion");
bool allowLCMSOptimization = KisConfig().allowLCMSOptimization();
dlgColorSpaceConversion->m_page->chkAllowLCMSOptimization->setChecked(allowLCMSOptimization);
Q_CHECK_PTR(dlgColorSpaceConversion);
dlgColorSpaceConversion->setCaption(i18n("Convert All Layers From ") + image->colorSpace()->name());
dlgColorSpaceConversion->setInitialColorSpace(image->colorSpace());
if (dlgColorSpaceConversion->exec() == QDialog::Accepted) {
const KoColorSpace * cs = dlgColorSpaceConversion->m_page->colorSpaceSelector->currentColorSpace();
if (cs) {
QApplication::setOverrideCursor(KisCursor::waitCursor());
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::HighQuality;
if (dlgColorSpaceConversion->m_page->chkBlackpointCompensation->isChecked()) conversionFlags |= KoColorConversionTransformation::BlackpointCompensation;
if (!dlgColorSpaceConversion->m_page->chkAllowLCMSOptimization->isChecked()) conversionFlags |= KoColorConversionTransformation::NoOptimization;
image->convertImageColorSpace(cs, (KoColorConversionTransformation::Intent)dlgColorSpaceConversion->m_intentButtonGroup.checkedId(), conversionFlags);
QApplication::restoreOverrideCursor();
}
}
delete dlgColorSpaceConversion;
}
void ColorSpaceConversion::slotLayerColorSpaceConversion()
{
-
- KisImageWSP image = m_view->image();
+ KisImageSP image = m_view->image().toStrongRef();
if (!image) return;
KisLayerSP layer = m_view->activeLayer();
if (!layer) return;
DlgColorSpaceConversion * dlgColorSpaceConversion = new DlgColorSpaceConversion(m_view->mainWindow(), "ColorSpaceConversion");
Q_CHECK_PTR(dlgColorSpaceConversion);
dlgColorSpaceConversion->setCaption(i18n("Convert Current Layer From") + layer->colorSpace()->name());
dlgColorSpaceConversion->setInitialColorSpace(layer->colorSpace());
if (dlgColorSpaceConversion->exec() == QDialog::Accepted) {
const KoColorSpace * cs = dlgColorSpaceConversion->m_page->colorSpaceSelector->currentColorSpace();
if (cs) {
QApplication::setOverrideCursor(KisCursor::waitCursor());
image->undoAdapter()->beginMacro(kundo2_i18n("Convert Layer Type"));
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::HighQuality;
if (dlgColorSpaceConversion->m_page->chkBlackpointCompensation->isChecked()) conversionFlags |= KoColorConversionTransformation::BlackpointCompensation;
if (!dlgColorSpaceConversion->m_page->chkAllowLCMSOptimization->isChecked()) conversionFlags |= KoColorConversionTransformation::NoOptimization;
KisColorSpaceConvertVisitor visitor(image, layer->colorSpace(), cs, (KoColorConversionTransformation::Intent)dlgColorSpaceConversion->m_intentButtonGroup.checkedId(), conversionFlags);
layer->accept(visitor);
image->undoAdapter()->endMacro();
QApplication::restoreOverrideCursor();
m_view->nodeManager()->nodesUpdated();
}
}
delete dlgColorSpaceConversion;
}
#include "colorspaceconversion.moc"
diff --git a/plugins/extensions/gmic/CMakeLists.txt b/plugins/extensions/gmic/CMakeLists.txt
index b8344d2128..c486a07c60 100644
--- a/plugins/extensions/gmic/CMakeLists.txt
+++ b/plugins/extensions/gmic/CMakeLists.txt
@@ -1,201 +1,203 @@
##
# CMake for gmic
##
if (CMAKE_COMPILER_IS_GNUCXX)
add_compile_options("-Wno-misleading-indentation")
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ add_compile_options("-Wno-cast-align")
endif()
set(GMIC_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/gmic")
include_directories( ${GMIC_SOURCE_DIR})
if(NOT MSVC)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -fPIC")
endif()
# turn off O2 and Ob1 or Ob2 for g'mic
if (MSVC)
# RelWithDebInfo
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Oi /Ot /Oy /Gs /GF /Gy")
string(REPLACE "/O2" "/Od" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
string(REPLACE "/Ob1" "/Ob0" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
#Release
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /Ot /Oy /Gs /GF /Gy")
string(REPLACE "/O2" "/Od" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
string(REPLACE "/Ob2" "/Ob0" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
endif()
set(gmic_sources_SRCS
${GMIC_SOURCE_DIR}/gmic.cpp
)
set(gmic_headers_SRCS
${GMIC_SOURCE_DIR}/CImg.h
${GMIC_SOURCE_DIR}/gmic_def.h
${GMIC_SOURCE_DIR}/gmic.h
)
# Mandatory flags
add_definitions(-Dgmic_gimp)
add_definitions(-Dgmic_build)
add_definitions(-Dgmic_float_only)
add_definitions(-Dcimg_use_vt100 )
# PARALLEL
if (Threads_FOUND)
message("G'Mic: using pthreads")
add_definitions(-Dgmic_is_parallel)
add_definitions(-Dcimg_use_rng)
endif()
#OpenMP
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8.3 AND OPENMP_FOUND)
message("G'Mic: using OpenMP")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
add_definitions(-Dcimg_use_openmp)
add_definitions(-fopenmp)
endif()
#FFTW
if(FFTW3_FOUND)
message("G'Mic: using FFTW3")
add_definitions(-Dcimg_use_fftw3 )
add_definitions(-Dcimg_use_fftw3_singlethread )
include_directories(${FFTW3_INCLUDE_DIR})
endif()
# PNG
if (PNG_FOUND)
add_definitions(${PNG_DEFINITIONS})
add_definitions(-Dcimg_use_png)
include_directories(SYSTEM ${PNG_INCLUDE_DIR})
endif()
# ZLIB
if (ZLIB_FOUND)
add_definitions(-Dcimg_use_zlib)
include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS} )
endif()
# CURL
if (CURL_FOUND)
message("G'Mic: using Curl")
add_definitions(-Dcimg_use_curl)
include_directories(SYSTEM ${CURL_INCLUDE_DIRS} )
endif()
# X11
if(X11_FOUND)
add_definitions(-Dcimg_display=1)
add_definitions(-Dcimg_appname="gmic")
elseif (WIN32)
# CMake for MSVC automatically links and finds headers for gdi32
add_definitions(-Dcimg_OS=2)
add_definitions(-Dcimg_display=2)
add_definitions(-Dcimg_appname="gmic")
else()
add_definitions(-Dcimg_display=0)
endif()
# Extra for Krita
if(APPLE)
add_definitions(-D_APPLE)
endif()
add_library(gmic STATIC ${gmic_sources_SRCS} ${gmic_headers_SRCS})
if(FFTW3_FOUND)
target_link_libraries(gmic ${FFTW3_LIBRARIES})
endif()
if (X11_FOUND)
target_link_libraries(gmic ${X11_LIBRARIES} KF5::WindowSystem Qt5::Core)
endif()
if(CURL_FOUND)
target_link_libraries(gmic ${CURL_LIBRARIES})
endif()
# Only build gmic with gcc; msvc is broken, and now dtschump tells is that on OSX
# gcc should be used as well because clang is broken.
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8.3 AND OPENMP_FOUND)
target_link_libraries(gmic ${OPENMP_LIBRARIES})
endif()
target_link_libraries(gmic ${ZLIB_LIBRARIES})
target_link_libraries(gmic ${PNG_LIBRARIES})
if(Threads_FOUND)
target_link_libraries(gmic ${CMAKE_THREAD_LIBS_INIT})
endif()
##
# compile Krita plug-in and link static library in
##
set(kritagmic_shared_SOURCES
kis_gmic_parser.cpp
kis_gmic_blacklister.cpp
kis_gmic_filter_model.cpp
kis_gmic_filter_settings.cpp
Component.cpp
Category.cpp
Command.cpp
Parameter.cpp
kis_input_output_mapper.cpp
kis_html_delegate.cpp
kis_gmic_settings_widget.cpp
kis_gmic_input_output_widget.cpp
kis_gmic_filter_proxy_model.cpp
kis_gmic_widget.cpp
kis_gmic_updater.cpp
kis_filter_preview_widget.cpp
)
ki18n_wrap_ui(kritagmic_shared_SOURCES wdg_gmic.ui wdg_gmic_input_output.ui)
set(kritagmic_SOURCES
kis_gmic_simple_convertor.cpp
kis_export_gmic_processing_visitor.cpp
kis_gmic_applicator.cpp
kis_gmic_command.cpp
kis_gmic_data.cpp
kis_gmic_synchronize_layers_command.cpp
kis_gmic_synchronize_image_size_command.cpp
kis_import_gmic_processing_visitor.cpp
kis_gmic_progress_manager.cpp
kis_gmic_small_applicator.cpp
kis_gmic_plugin.cpp ${kritagmic_shared_SOURCES}
)
add_library(kritagmic MODULE ${kritagmic_SOURCES})
target_link_libraries(kritagmic kritaui gmic Qt5::Xml Qt5::Network ${ZLIB_LIBRARIES})
# gmicparser for debugging purposes
set(gmicparser_SOURCES gmicparser.cpp ${kritagmic_shared_SOURCES})
add_executable(gmicparser ${gmicparser_SOURCES})
target_link_libraries(gmicparser Qt5::Core Qt5::Gui Qt5::Network kritaui gmic ${ZLIB_LIBRARIES})
########### install files ###############
set(GMIC_INSTALL_DIR ${DATA_INSTALL_DIR}/krita/gmic)
install(TARGETS kritagmic DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install(TARGETS gmicparser ${INSTALL_TARGETS_DEFAULT_ARGS})
install( FILES
#${GMIC_SOURCE_DIR}/gmic_def.gmic
#${GMIC_SOURCE_DIR}/update1610.gmic
${GMIC_SOURCE_DIR}/gimp_update169.gmic
gmic_def.gmic.blacklist
DESTINATION ${GMIC_INSTALL_DIR} )
# tests, currently broken on OSX due to fftw linking problem
if(NOT APPLE)
#add_subdirectory(tests)
endif()
diff --git a/plugins/extensions/gmic/kis_gmic_plugin.cpp b/plugins/extensions/gmic/kis_gmic_plugin.cpp
index 79be76ffe6..3fe9df7e67 100644
--- a/plugins/extensions/gmic/kis_gmic_plugin.cpp
+++ b/plugins/extensions/gmic/kis_gmic_plugin.cpp
@@ -1,576 +1,577 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2013-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_plugin.h"
#include <QApplication>
#include <QTimer>
#include <klocalizedstring.h>
#include <QMessageBox>
#include <KoResourcePaths.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <QTime>
#include <QFileInfo>
#include <KisViewManager.h>
#include <kis_action.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <kis_selection.h>
#include <kis_paint_layer.h>
#include "kis_statusbar.h"
#include "widgets/kis_progress_widget.h"
#include <kis_config.h>
#include "kis_gmic_parser.h"
#include "Component.h"
#include "kis_gmic_filter_model.h"
#include "kis_gmic_widget.h"
#include "kis_gmic_blacklister.h"
#include "kis_input_output_mapper.h"
#include "kis_gmic_simple_convertor.h"
#include "kis_gmic_applicator.h"
#include "kis_export_gmic_processing_visitor.h"
#include "kis_gmic_command.h"
#include "kis_import_gmic_processing_visitor.h"
#include "kis_gmic_synchronize_layers_command.h"
#include "KoColorSpaceConstants.h"
#include "kis_gmic_progress_manager.h"
#include <kis_processing_visitor.h>
#include <kis_gmic_small_applicator.h>
#include "gmic.h"
K_PLUGIN_FACTORY_WITH_JSON(KisGmicPluginFactory, "kritagmic.json", registerPlugin<KisGmicPlugin>();)
const QString STANDARD_GMIC_DEFINITION = "gmic_def.gmic";
KisGmicPlugin::KisGmicPlugin(QObject *parent, const QVariantList &)
: KisViewPlugin(parent),
m_gmicWidget(0),
m_gmicApplicator(0),
m_smallApplicator(0),
m_progressManager(0),
m_currentActivity(INIT),
m_requestFinishAndClose(false),
m_smallPreviewRequestCounter(0),
m_onCanvasPreviewRequestCounter(0),
m_filteringIsRunning(false)
{
KisAction *action = createAction("gmic");
+ action->setActivationFlags(KisAction::ACTIVE_DEVICE);
connect(action, SIGNAL(triggered()), this, SLOT(slotShowGmicDialog()));
m_blacklistPath = KoResourcePaths::findResource("gmic_definitions", STANDARD_GMIC_DEFINITION + ".blacklist");
dumpCompiletimeFeatures();
}
KisGmicPlugin::~KisGmicPlugin()
{
delete m_gmicWidget;
}
void KisGmicPlugin::dumpCompiletimeFeatures()
{
dbgPlugins << "<features>";
#ifdef gmic_is_parallel
dbgPlugins << "PTHREADS ON";
#endif
#ifdef cimg_use_fftw3
dbgPlugins << "FFTW3 ON";
#endif
#ifdef cimg_use_png
dbgPlugins << "PNG ON";
#endif
#ifdef cimg_use_zlib
dbgPlugins << "ZLIB ON";
#endif
#ifdef cimg_use_curl
dbgPlugins << "CURL ON";
#endif
#ifdef cimg_display
#if cimg_display == 1
dbgPlugins << "Display:X11";
#elif cimg_display == 2
dbgPlugins << "Display:GDI";
#elif cimg_display == 3
dbgPlugins << "Display:NONE";
#endif
#endif
dbgPlugins << "</features>";
}
void KisGmicPlugin::setupDefinitionPaths()
{
m_definitionFilePaths = KoResourcePaths::findAllResources("gmic_definitions", "*.gmic");
QMutableStringListIterator it(m_definitionFilePaths);
// remove all instances of gmic_def.gmic and updateXXXX.gmic, they cause problems when merged/mixed
QRegExp rx("update\\d\\d\\d\\d.gmic");
while (it.hasNext())
{
QFileInfo fi(it.next());
if (fi.fileName() == STANDARD_GMIC_DEFINITION)
{
it.remove();
}
else if ( rx.exactMatch( fi.fileName() ) )
{
it.remove();
}
}
// if we don't have updateXXXX.gmic for current version, prepend standard gmic_def.gmic
int gmicVersion = gmic_version;
QString updateFileName = "update" + QString::number(gmicVersion) + ".gmic";
QString updatedGmicDefinitionFilePath = KoResourcePaths::findResource("gmic_definitions", updateFileName);
if (updatedGmicDefinitionFilePath.isEmpty())
{
QString standardGmicDefinitionFilePath = KoResourcePaths::findResource("gmic_definitions", STANDARD_GMIC_DEFINITION);
m_definitionFilePaths.prepend(standardGmicDefinitionFilePath);
}
else
{
m_definitionFilePaths.prepend(updatedGmicDefinitionFilePath);
}
Q_FOREACH (const QString item, m_definitionFilePaths)
{
dbgPlugins << "registered gmic file: " << item;
}
}
void KisGmicPlugin::slotShowGmicDialog()
{
if (m_gmicWidget)
{
// restart here?
slotCloseGmicDialog();
}
// init everything here
KisImageWSP image = m_view->image();
if (!image) return;
KisLayerSP layer = m_view->activeLayer();
if (!layer) return;
m_progressManager = new KisGmicProgressManager(m_view);
connect(m_progressManager, SIGNAL(sigProgress()), this, SLOT(slotUpdateProgress()));
m_gmicApplicator = new KisGmicApplicator();
connect(m_gmicApplicator, SIGNAL(gmicFinished(bool, int, QString)), this, SLOT(slotGmicFinished(bool, int, QString)));
m_smallApplicator = new KisGmicSmallApplicator;
connect(m_smallApplicator , SIGNAL(gmicFinished(bool,int,QString)), this, SLOT(slotGmicFinished(bool,int,QString)));
connect(m_smallApplicator , SIGNAL(previewFinished(bool)), this, SLOT(slotViewportPreviewFinished(bool)));
setupDefinitionPaths();
parseGmicCommandDefinitions(m_definitionFilePaths);
KisGmicParser parser(m_definitionFilePaths);
Component * root = parser.createFilterTree();
KisGmicFilterModel * model = new KisGmicFilterModel(root); // filter mode takes ownership of root
KisGmicBlacklister * blacklister = new KisGmicBlacklister(m_blacklistPath);
model->setBlacklister(blacklister);
KisConfig cfg;
QString gmicUpdatesUrl = cfg.readEntry<QString>("gmic_updates_url", "http://www.gmic.eu/");
QString updateUrl = gmicUpdatesUrl + QString("update") + QString::number(gmic_version) + ".gmic";
m_gmicWidget = new KisGmicWidget(model, updateUrl);
// preview filter
connect(m_gmicWidget, SIGNAL(sigPreviewFilterCommand(KisGmicFilterSetting*)),this, SLOT(slotPreviewGmicCommand(KisGmicFilterSetting*)));
// finish filter
connect(m_gmicWidget, SIGNAL(sigAcceptOnCanvasPreview()), this, SLOT(slotAcceptOnCanvasPreview()));
// cancel filter
connect(m_gmicWidget, SIGNAL(sigCancelOnCanvasPreview()), this, SLOT(slotCancelOnCanvasPreview()));
// cancel
connect(m_gmicWidget, SIGNAL(sigClose()),this, SLOT(slotCloseGmicDialog()));
// filter current image with current settings
connect(m_gmicWidget, SIGNAL(sigFilterCurrentImage(KisGmicFilterSetting*)), this, SLOT(slotFilterCurrentImage(KisGmicFilterSetting*)));
// show active layer in preview viewport
connect(m_gmicWidget, SIGNAL(sigPreviewActiveLayer()), this, SLOT(slotPreviewActiveLayer()));
connect(m_gmicWidget, SIGNAL(sigRequestFinishAndClose()), this, SLOT(slotRequestFinishAndClose()));
QString version = QString("%0.%1.%2.%3").arg(gmic_version/1000).arg((gmic_version/100)%10).arg((gmic_version/10)%10).arg(gmic_version%10);
QString pluginName = i18n("G'MIC for Krita");
m_gmicWidget->setWindowTitle(QString("%0 %1").arg(pluginName).arg(version));
m_gmicWidget->show();
slotPreviewActiveLayer();
}
void KisGmicPlugin::slotCloseGmicDialog()
{
dbgPlugins << "progress manager: " << m_progressManager;
- // reset state
+ // reset state
{
m_gmicWidget = 0;
if (m_gmicApplicator)
{
m_gmicApplicator->cancel();
}
delete m_progressManager;
m_progressManager = 0;
delete m_gmicApplicator;
m_gmicApplicator = 0;
dbgPlugins << "Deleting " << m_smallApplicator;
delete m_smallApplicator;
m_smallApplicator = 0;
setActivity(INIT);
m_requestFinishAndClose = false;
m_smallPreviewRequestCounter = 0;
m_onCanvasPreviewRequestCounter = 0;
m_filteringIsRunning = false;
m_gmicCustomCommands.clear();
}
}
void KisGmicPlugin::slotPreviewGmicCommand(KisGmicFilterSetting* setting)
{
// Use '_none_' as a special command or preview_command to tell the plug-in that the entry requires no G'MIC call.
if (setting->previewGmicCommand().startsWith("-_none_ "))
{
return;
}
dbgPlugins << "Preview Request, preview size: " << setting->previewSize();
KisInputOutputMapper mapper(m_view->image(), m_view->activeNode());
KisNodeListSP layers = mapper.inputNodes(setting->inputLayerMode());
if (!checkSettingsValidity(layers, setting))
{
dbgPlugins << "Failed, some feature not implemented";
return;
}
if (setting->previewSize() != ON_CANVAS)
{
renderViewportPreview(layers, setting);
}
else
{
startOnCanvasPreview(layers, setting, PREVIEWING);
}
}
void KisGmicPlugin::slotPreviewActiveLayer()
{
slotPreviewSmallWindow(m_view->activeNode()->paintDevice());
}
void KisGmicPlugin::slotPreviewSmallWindow(KisPaintDeviceSP device)
{
if (!device) return;
QRect deviceRect = device->exactBounds();
qreal aspectRatio = (qreal)deviceRect.width()/deviceRect.height();
int dstWidth = m_gmicWidget->previewWidget()->size().width();
int dstHeight = dstWidth / aspectRatio;
QImage previewImage = device->createThumbnail(dstWidth, dstHeight, deviceRect);
m_gmicWidget->previewWidget()->setImage(previewImage);
}
void KisGmicPlugin::slotAcceptOnCanvasPreview()
{
m_gmicApplicator->finish();
}
void KisGmicPlugin::slotCancelOnCanvasPreview()
{
m_gmicApplicator->cancel();
}
void KisGmicPlugin::slotFilterCurrentImage(KisGmicFilterSetting* setting)
{
if (setting->gmicCommand().startsWith("-_none_ "))
{
dbgPlugins << "_none_ command does not involve g'mic call";
return;
}
dbgPlugins << "Filtering image on canvas!";
KisInputOutputMapper mapper(m_view->image(), m_view->activeNode());
KisNodeListSP layers = mapper.inputNodes(setting->inputLayerMode());
if (checkSettingsValidity(layers, setting))
{
startOnCanvasPreview(layers, setting, FILTERING);
// wait, so that next request to strokes for on-canvas preview is possible
// m_view->image()->waitForDone();
}
else
{
dbgPlugins << "Failed to filter image, some feature not implemented";
}
}
void KisGmicPlugin::parseGmicCommandDefinitions(const QStringList& gmicDefinitionFilePaths)
{
Q_FOREACH (const QString filePath, gmicDefinitionFilePaths)
{
QByteArray gmicCommands = KisGmicParser::extractGmicCommandsOnly(filePath);
m_gmicCustomCommands.append(gmicCommands);
}
}
void KisGmicPlugin::renderViewportPreview(KisNodeListSP layers, KisGmicFilterSetting* setting)
{
if (m_filteringIsRunning)
{
dbgPlugins << "Filtering is running...";
waitForFilterFinish();
}
QSize previewSize;
if (m_gmicWidget && m_gmicWidget->previewWidget()) {
previewSize = m_gmicWidget->previewWidget()->size();
}
else
{
return;
}
m_smallPreviewRequestCounter++;
dbgPlugins << "Current number of small preview requests: " << m_smallPreviewRequestCounter;
setActivity(SMALL_PREVIEW);
m_view->image()->lock();
QRect canvasRect = m_view->image()->bounds();
m_view->image()->unlock();
dbgPlugins << "Unlocked image...";
m_smallApplicator->render(canvasRect,previewSize, layers, setting, m_gmicCustomCommands);
startProgressReporting();
}
void KisGmicPlugin::startOnCanvasPreview(KisNodeListSP layers, KisGmicFilterSetting* setting, Activity activity)
{
m_onCanvasPreviewRequestCounter++;
m_filteringIsRunning = true;
setActivity(activity);
KUndo2MagicString actionName = kundo2_i18n("Gmic filter");
KisNodeSP rootNode = m_view->image()->root();
m_gmicApplicator->setProperties(m_view->image(), rootNode, actionName, layers, setting->gmicCommand(), m_gmicCustomCommands);
m_gmicApplicator->preview();
// Note: do not call KisImage::waitForDone(): strokes are not finished or cancelled, it's just preview!
// waitForDone would cause infinite hang
dbgPlugins << valueToQString(m_currentActivity);
startProgressReporting();
}
void KisGmicPlugin::startProgressReporting()
{
if (m_progressManager->inProgress())
{
m_progressManager->finishProgress();
}
m_progressManager->initProgress();
}
bool KisGmicPlugin::checkSettingsValidity(KisNodeListSP layers, const KisGmicFilterSetting* setting)
{
if (setting->isBlacklisted())
{
QMessageBox::warning(m_gmicWidget, i18nc("@title:window", "Krita"), i18n("Sorry, this filter is crashing Krita and is turned off."));
return false;
}
if (setting->outputMode() != IN_PLACE) {
QMessageBox::warning(m_gmicWidget, i18nc("@title:window", "Krita"), i18n("Sorry, this output mode is not implemented"));
return false;
}
if (layers->isEmpty()) {
QMessageBox::warning(m_gmicWidget, i18nc("@title:window", "Krita"), i18n("Sorry, this input mode is not implemented"));
return false;
}
return true;
}
void KisGmicPlugin::slotUpdateProgress()
{
float progress = 0;
if (m_currentActivity == SMALL_PREVIEW)
{
if (!m_smallApplicator)
{
dbgPlugins << "WARNING: small applicator already deleted!!!";
return;
}
progress = m_smallApplicator->progress();
}
else
{
if (!m_gmicApplicator)
{
dbgPlugins << "WARNING: gmic applicator already deleted!!!";
return;
}
progress = m_gmicApplicator->getProgress();
}
m_progressManager->updateProgress(progress);
}
void KisGmicPlugin::slotGmicFinished(bool successfully, int miliseconds, const QString& msg)
{
dbgPlugins << "GMIC_FINISHED : activity " << valueToQString(m_currentActivity);
dbgPlugins << ppVar(m_smallPreviewRequestCounter) << " " << ppVar(m_onCanvasPreviewRequestCounter);
m_progressManager->finishProgress();
if (successfully)
{
gmicFinished(miliseconds);
}
else
{
gmicFailed(msg);
}
if (m_currentActivity == FILTERING || m_currentActivity == PREVIEWING)
{
m_filteringIsRunning = false;
emit filteringFinished();
}
if (m_requestFinishAndClose)
{
slotRequestFinishAndClose();
}
}
void KisGmicPlugin::gmicFinished(int miliseconds)
{
if (m_currentActivity == FILTERING)
{
slotAcceptOnCanvasPreview();
}
m_gmicWidget->setWindowTitle(QString("Filtering took ") + QString::number(miliseconds * 0.001) + QString(" seconds"));
}
void KisGmicPlugin::gmicFailed(const QString& msg)
{
dbgPlugins << "G'Mic for activity " << valueToQString(m_currentActivity) << "failed with message: " << msg;
if ((m_currentActivity == PREVIEWING) || (m_currentActivity == FILTERING))
{
slotCancelOnCanvasPreview();
}
QMessageBox::warning(m_gmicWidget, i18nc("@title:window", "Krita"), i18n("Sorry! G'Mic failed, reason:") + msg);
}
void KisGmicPlugin::slotRequestFinishAndClose()
{
if (m_progressManager->inProgress())
{
m_requestFinishAndClose = true;
}
else
{
m_gmicWidget->close();
}
}
void KisGmicPlugin::slotViewportPreviewFinished(bool successfully)
{
if (m_smallApplicator)
{
if (successfully)
{
slotPreviewSmallWindow(m_smallApplicator->preview());
}
else
{
dbgPlugins << "Viewport preview generation failed!";
}
setActivity(INIT);
}
}
void KisGmicPlugin::setActivity(KisGmicPlugin::Activity activity)
{
dbgPlugins << "Changing activity from " << valueToQString(m_currentActivity) << " to " << valueToQString(activity);
m_currentActivity = activity;
}
void KisGmicPlugin::waitForFilterFinish()
{
dbgPlugins << "Starting local event loop!";
QEventLoop localEventLoop;
connect(this, SIGNAL(filteringFinished()), &localEventLoop, SLOT(quit()));
localEventLoop.exec();
dbgPlugins << "Done";
}
QLatin1String KisGmicPlugin::valueToQString(KisGmicPlugin::Activity activity)
{
const QMetaObject & mo = KisGmicPlugin::staticMetaObject;
QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("Activity"));
return QLatin1String(me.valueToKey(activity));
}
#include "kis_gmic_plugin.moc"
diff --git a/plugins/extensions/imagesize/imagesize.cc b/plugins/extensions/imagesize/imagesize.cc
index 3734ee6e17..ca21339e1a 100644
--- a/plugins/extensions/imagesize/imagesize.cc
+++ b/plugins/extensions/imagesize/imagesize.cc
@@ -1,164 +1,166 @@
/*
* imagesize.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 "imagesize.h"
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_global.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include <kis_image_manager.h>
#include <kis_node_manager.h>
#include <kis_group_layer.h>
#include <kis_selection_mask.h>
#include <kis_selection.h>
#include "dlg_imagesize.h"
#include "dlg_canvassize.h"
#include "dlg_layersize.h"
#include "kis_filter_strategy.h"
#include "kis_action.h"
#include "kis_action_manager.h"
K_PLUGIN_FACTORY_WITH_JSON(ImageSizeFactory, "kritaimagesize.json", registerPlugin<ImageSize>();)
ImageSize::ImageSize(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
{
KisAction *action = createAction("imagesize");
connect(action, SIGNAL(triggered()), this, SLOT(slotImageSize()));
action = createAction("canvassize");
connect(action, SIGNAL(triggered()), this, SLOT(slotCanvasSize()));
action = createAction("layersize");
connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSize()));
action = createAction("selectionscale");
connect(action, SIGNAL(triggered()), this, SLOT(slotSelectionScale()));
}
ImageSize::~ImageSize()
{
}
void ImageSize::slotImageSize()
{
- KisImageWSP image = m_view->image();
-
+ KisImageSP image = m_view->image().toStrongRef();
if (!image) return;
DlgImageSize * dlgImageSize = new DlgImageSize(m_view->mainWindow(), image->width(), image->height(), image->yRes());
Q_CHECK_PTR(dlgImageSize);
dlgImageSize->setObjectName("ImageSize");
if (dlgImageSize->exec() == QDialog::Accepted) {
qint32 w = dlgImageSize->width();
qint32 h = dlgImageSize->height();
double res = dlgImageSize->resolution();
m_view->imageManager()->scaleCurrentImage(QSize(w, h), res, res, dlgImageSize->filterType());
}
delete dlgImageSize;
}
void ImageSize::slotCanvasSize()
{
KisImageWSP image = m_view->image();
if (!image) return;
DlgCanvasSize * dlgCanvasSize = new DlgCanvasSize(m_view->mainWindow(), image->width(), image->height(), image->yRes());
Q_CHECK_PTR(dlgCanvasSize);
if (dlgCanvasSize->exec() == QDialog::Accepted) {
qint32 width = dlgCanvasSize->width();
qint32 height = dlgCanvasSize->height();
qint32 xOffset = dlgCanvasSize->xOffset();
qint32 yOffset = dlgCanvasSize->yOffset();
m_view->imageManager()->resizeCurrentImage(width, height, xOffset, yOffset);
}
delete dlgCanvasSize;
}
void ImageSize::slotLayerSize()
{
KisImageWSP image = m_view->image();
if (!image) return;
KisPaintDeviceSP dev = m_view->activeLayer()->projection();
Q_ASSERT(dev);
QRect rc = dev->exactBounds();
DlgLayerSize * dlgLayerSize = new DlgLayerSize(m_view->mainWindow(), "LayerSize", rc.width(), rc.height(), image->yRes());
Q_CHECK_PTR(dlgLayerSize);
dlgLayerSize->setCaption(i18n("Resize Layer"));
if (dlgLayerSize->exec() == QDialog::Accepted) {
qint32 w = dlgLayerSize->width();
qint32 h = dlgLayerSize->height();
m_view->nodeManager()->scale((double)w / ((double)(rc.width())),
(double)h / ((double)(rc.height())),
dlgLayerSize->filterType());
}
delete dlgLayerSize;
}
void ImageSize::slotSelectionScale()
{
- KisImageWSP image = m_view->image();
+ KisImageSP image = m_view->image();
+ if (!image) {
+ return;
+ }
KisLayerSP layer = m_view->activeLayer();
KIS_ASSERT_RECOVER_RETURN(image && layer);
KisSelectionMaskSP selectionMask = layer->selectionMask();
if (!selectionMask) {
selectionMask = image->rootLayer()->selectionMask();
}
KIS_ASSERT_RECOVER_RETURN(selectionMask);
QRect rc = selectionMask->selection()->selectedExactRect();
DlgLayerSize * dlgSize = new DlgLayerSize(m_view->mainWindow(), "SelectionScale", rc.width(), rc.height(), image->yRes());
dlgSize->setCaption(i18n("Scale Selection"));
if (dlgSize->exec() == QDialog::Accepted) {
qint32 w = dlgSize->width();
qint32 h = dlgSize->height();
image->scaleNode(selectionMask,
qreal(w) / rc.width(),
qreal(h) / rc.height(),
dlgSize->filterType());
}
delete dlgSize;
}
#include "imagesize.moc"
diff --git a/plugins/extensions/imagesplit/imagesplit.cpp b/plugins/extensions/imagesplit/imagesplit.cpp
index 73b04e31e4..2984046621 100644
--- a/plugins/extensions/imagesplit/imagesplit.cpp
+++ b/plugins/extensions/imagesplit/imagesplit.cpp
@@ -1,174 +1,173 @@
/*
* 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 <klocalizedstring.h>
#include <kpluginfactory.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 <KisMimeDatabase.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 = createAction("imagesplit");
connect(action, SIGNAL(triggered()), this, SLOT(slotImagesplit()));
}
Imagesplit::~Imagesplit()
{
}
void Imagesplit::saveAsImage(const QRect &imgSize, const QString &mimeType, const QString &url)
{
KisImageWSP image = m_view->image();
KisDocument *document = KisPart::instance()->createDocument();
- document->prepareForImport();
KisImageWSP dst = new KisImage(document->createUndoStore(), imgSize.width(), imgSize.height(), image->colorSpace(), image->objectName());
dst->setResolution(image->xRes(), image->yRes());
document->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();
document->setFileBatchMode(true);
document->setOutputMimeType(mimeType.toLatin1());
document->exportDocument(QUrl::fromLocalFile(url));
delete document;
}
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(KisImportExportManager::Export);
listMimeFilter.sort();
QStringList filteredMimeTypes;
QStringList listFileType;
Q_FOREACH (const QString & mimeType, listMimeFilter) {
listFileType.append(KisMimeDatabase::descriptionForMimeType(mimeType));
filteredMimeTypes.append(mimeType);
}
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()) {
KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenDirectory, "OpenDocument");
dialog.setCaption(i18n("Save Image on Split"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
QUrl directory = QUrl::fromUserInput(dialog.filename());
if (directory.isEmpty())
return;
for (int i = 0, k = 1; i < (numVerticalLines + 1); i++) {
for (int j = 0; j < (numHorizontalLines + 1); j++, k++) {
QString mimeTypeSelected = listMimeFilter.at(dlgImagesplit->cmbIndex);
QString homepath = directory.toLocalFile();
QString suffix = KisMimeDatabase::suffixesForMimeType(mimeTypeSelected).first();
qDebug() << "suffix" << suffix;
if (suffix.startsWith("*.")) {
suffix = suffix.remove(0, 1);
}
qDebug() << "\tsuffix" << suffix;
if (!suffix.startsWith(".")) {
suffix = suffix.prepend('.');
}
qDebug() << "\tsuffix" << suffix;
QString fileName = dlgImagesplit->suffix() + '_' + QString::number(k) + suffix;
QString url = homepath + '/' + fileName;
saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), listMimeFilter.at(dlgImagesplit->cmbIndex), url);
}
}
}
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);
QUrl url = QUrl::fromUserInput(dialog.filename());
QString mimefilter = KisMimeDatabase::mimeTypeForFile(url.toLocalFile());
if (url.isEmpty())
return;
saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), mimefilter, url.toLocalFile());
}
}
}
}
delete dlgImagesplit;
}
#include "imagesplit.moc"
diff --git a/plugins/extensions/layergroupswitcher/layergroupswitcher.cpp b/plugins/extensions/layergroupswitcher/layergroupswitcher.cpp
index 7b988fabc6..6a316987f0 100644
--- a/plugins/extensions/layergroupswitcher/layergroupswitcher.cpp
+++ b/plugins/extensions/layergroupswitcher/layergroupswitcher.cpp
@@ -1,132 +1,137 @@
/*
* layergroupswitcher.cpp -- Part of Krita
*
* 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 "layergroupswitcher.h"
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_node.h>
#include <kis_node_manager.h>
#include <kis_global.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include "kis_action.h"
K_PLUGIN_FACTORY_WITH_JSON(LayerGroupSwitcherFactory, "kritalayergroupswitcher.json", registerPlugin<LayerGroupSwitcher>();)
LayerGroupSwitcher::LayerGroupSwitcher(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
{
KisAction *action = new KisAction(i18n("Move into previous group"), this);
addAction("LayerGroupSwitcher/previous", action);
connect(action, SIGNAL(triggered()), this, SLOT(moveIntoPreviousGroup()));
action = new KisAction(i18n("Move into next group"), this);
addAction("LayerGroupSwitcher/next", action);
connect(action, SIGNAL(triggered()), this, SLOT(moveIntoNextGroup()));
-
}
LayerGroupSwitcher::~LayerGroupSwitcher()
{
}
void LayerGroupSwitcher::moveIntoNextGroup()
{
- KisImageWSP image = m_view->image();
+ KisImageSP image = m_view->image().toStrongRef();
+ if (!image) {
+ return;
+ }
KisNodeManager *nodeManager = m_view->nodeManager();
KisLayerSP active = nodeManager->activeLayer();
if (!active) {
return;
}
if (active->parentLayer().data() == image->rootLayer().data()) {
active->setVisible(false);
nodeManager->activateNextNode();
active = nodeManager->activeLayer();
if (active) {
active->setVisible(true);
}
}
else if (active->parent()) {
KisNodeSP parent = active->parent();
if (parent) {
int indexInGroup = active->parent()->index(active);
nodeManager->slotNonUiActivatedNode(parent);
parent->setVisible(false);
nodeManager->activateNextNode();
active = nodeManager->activeLayer();
if (active) {
active->setVisible(true);
}
KisNodeSP child = active->at(indexInGroup);
if (child) {
nodeManager->slotNonUiActivatedNode(child);
child->setVisible(true);
}
}
}
image->refreshGraph();
}
void LayerGroupSwitcher::moveIntoPreviousGroup()
{
- KisImageWSP image = m_view->image();
+ KisImageSP image = m_view->image().toStrongRef();
+ if (!image) {
+ return;
+ }
KisNodeManager *nodeManager = m_view->nodeManager();
KisLayerSP active = nodeManager->activeLayer();
if (!active) {
return;
}
if (active->parentLayer().data() == image->rootLayer().data()) {
active->setVisible(false);
nodeManager->activatePreviousNode();
active = nodeManager->activeLayer();
if (active) {
active->setVisible(true);
}
}
else if (active->parent()) {
KisNodeSP parent = active->parent();
if (parent) {
int indexInGroup = active->parent()->index(active);
nodeManager->slotNonUiActivatedNode(parent);
parent->setVisible(false);
nodeManager->activatePreviousNode();
active = nodeManager->activeLayer();
if (active) {
active->setVisible(true);
}
KisNodeSP child = active->at(indexInGroup);
if (child) {
nodeManager->slotNonUiActivatedNode(child);
child->setVisible(true);
}
}
}
image->refreshGraph();
}
#include "layergroupswitcher.moc"
diff --git a/plugins/extensions/offsetimage/offsetimage.cpp b/plugins/extensions/offsetimage/offsetimage.cpp
index c62d5fa817..8bc975bbb2 100644
--- a/plugins/extensions/offsetimage/offsetimage.cpp
+++ b/plugins/extensions/offsetimage/offsetimage.cpp
@@ -1,141 +1,143 @@
/*
* 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 <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <kis_icon.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 = createAction("offsetimage");
connect(action, SIGNAL(triggered()), this, SLOT(slotOffsetImage()));
action = createAction("offsetlayer");
connect(action, SIGNAL(triggered()), this, SLOT(slotOffsetLayer()));
}
OffsetImage::~OffsetImage()
{
}
void OffsetImage::slotOffsetImage()
{
- KisImageWSP image = m_view->image();
+ KisImageSP image = m_view->image().toStrongRef();
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
{
dbgKrita << "KisImage not available";
}
}
void OffsetImage::slotOffsetLayer()
{
- KisImageWSP image = m_view->image();
+ KisImageSP image = m_view->image().toStrongRef();
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"));
+ DlgOffsetImage * dlgOffsetImage = new DlgOffsetImage(m_view->mainWindow(), "OffsetLayer", offsetWrapRect().size());
+ Q_CHECK_PTR(dlgOffsetImage);
- if (dlgOffsetImage->exec() == QDialog::Accepted) {
- QPoint offsetPoint = QPoint(dlgOffsetImage->offsetX(), dlgOffsetImage->offsetY());
- KisNodeSP activeNode = m_view->activeNode();
- offsetImpl(actionName, activeNode, offsetPoint);
- }
- delete 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
{
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();
+ KisImageSP image = m_view->image().toStrongRef();
+ if (image) {
+ offsetWrapRect = image->bounds();
+ }
}
return offsetWrapRect;
}
#include "offsetimage.moc"
diff --git a/plugins/extensions/separate_channels/kis_channel_separator.cc b/plugins/extensions/separate_channels/kis_channel_separator.cc
index 9ca70aaee7..0748064cd1 100644
--- a/plugins/extensions/separate_channels/kis_channel_separator.cc
+++ b/plugins/extensions/separate_channels/kis_channel_separator.cc
@@ -1,275 +1,273 @@
/*
* This file is part of Krita
*
* Copyright (c) 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
*
* ported from Gimp, Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
* original pixelize.c for GIMP 0.54 by Tracy Scott
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_channel_separator.h"
#include <limits.h>
#include <stdlib.h>
#include <vector>
#include <QDesktopServices>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <KisImportExportManager.h>
#include <KoUpdater.h>
#include <KoFileDialog.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoChannelInfo.h>
#include <KoColorModelStandardIds.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_transaction.h>
#include <kis_undo_adapter.h>
#include <kis_global.h>
#include <kis_types.h>
#include "kis_iterator_ng.h"
#include <KisPart.h>
#include <KisViewManager.h>
#include <kis_paint_device.h>
#include <kis_node_manager.h>
#include <kis_node_commands_adapter.h>
#include <KisMimeDatabase.h>
KisChannelSeparator::KisChannelSeparator(KisViewManager * view)
: m_view(view)
{
}
void KisChannelSeparator::separate(KoUpdater * progressUpdater, enumSepAlphaOptions alphaOps, enumSepSource sourceOps, enumSepOutput outputOps, bool downscale, bool toColor)
{
- KisImageWSP image = m_view->image();
+ KisImageSP image = m_view->image();
if (!image) return;
KisPaintDeviceSP src;
// Use the flattened image, if required
switch (sourceOps) {
case ALL_LAYERS:
// the content will be locked later
src = image->projection();
break;
case CURRENT_LAYER:
src = m_view->activeDevice();
break;
default:
break;
}
if (!src) return;
progressUpdater->setProgress(1);
const KoColorSpace * dstCs = 0;
quint32 numberOfChannels = src->channelCount();
const KoColorSpace * srcCs = src->colorSpace();
QList<KoChannelInfo *> channels = srcCs->channels();
vKisPaintDeviceSP layers;
QList<KoChannelInfo *>::const_iterator begin = channels.constBegin();
QList<KoChannelInfo *>::const_iterator end = channels.constEnd();
QRect rect = src->exactBounds();
- m_view->image()->lock();
+ image->lock();
int i = 0;
for (QList<KoChannelInfo *>::const_iterator it = begin; it != end; ++it) {
KoChannelInfo * ch = (*it);
if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) {
continue;
}
qint32 channelSize = ch->size();
qint32 channelPos = ch->pos();
qint32 destSize = 1;
KisPaintDeviceSP dev;
if (toColor) {
// We don't downscale if we separate to color channels
dev = new KisPaintDevice(srcCs);
} else {
if (channelSize == 1 || downscale) {
dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), 0));
} else {
dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), 0));
destSize = 2;
}
}
dstCs = dev->colorSpace();
layers.push_back(dev);
KisHLineConstIteratorSP srcIt = src->createHLineConstIteratorNG(rect.x(), rect.y(), rect.width());
KisHLineIteratorSP dstIt = dev->createHLineIteratorNG(rect.x(), rect.y(), rect.width());
for (qint32 row = 0; row < rect.height(); ++row) {
do {
if (toColor) {
dstCs->singleChannelPixel(dstIt->rawData(), srcIt->oldRawData(), channelPos);
if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) {
//dstCs->setAlpha(dstIt->rawData(), srcIt->oldRawData()[srcAlphaPos], 1);
dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1);
} else {
dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
}
} else {
// To grayscale
// Decide whether we need downscaling
if (channelSize == 1 && destSize == 1) {
// Both 8-bit channels
dstIt->rawData()[0] = srcIt->oldRawData()[channelPos];
if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) {
dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1);
} else {
dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
}
} else if (channelSize == 2 && destSize == 2) {
// Both 16-bit
dstIt->rawData()[0] = srcIt->oldRawData()[channelPos];
dstIt->rawData()[1] = srcIt->oldRawData()[channelPos + 1];
if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) {
dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1);
} else {
dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
}
} else if (channelSize != 1 && destSize == 1) {
// Downscale
memset(dstIt->rawData(), srcCs->scaleToU8(srcIt->oldRawData(), channelPos), 1);
// XXX: Do alpha
dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
} else if (channelSize != 2 && destSize == 2) {
// Upscale
dstIt->rawData()[0] = srcCs->scaleToU8(srcIt->oldRawData(), channelPos);
// XXX: Do alpha
dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
}
}
} while (dstIt->nextPixel() && srcIt->nextPixel());
dstIt->nextRow();
srcIt->nextRow();
}
++i;
progressUpdater->setProgress((i * 100) / numberOfChannels);
if (progressUpdater->interrupted()) {
break;
}
}
vKisPaintDeviceSP_it deviceIt = layers.begin();
progressUpdater->setProgress(100);
if (!progressUpdater->interrupted()) {
KisUndoAdapter * undo = image->undoAdapter();
if (outputOps == TO_LAYERS) {
undo->beginMacro(kundo2_i18n("Separate Image"));
}
// Flatten the image if required
switch (sourceOps) {
case(ALL_LAYERS):
image->flatten();
break;
default:
break;
}
KisNodeCommandsAdapter adapter(m_view);
for (QList<KoChannelInfo *>::const_iterator it = begin; it != end; ++it) {
KoChannelInfo * ch = (*it);
if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) {
// Don't make an separate separation of the alpha channel if the user didn't ask for it.
continue;
}
if (outputOps == TO_LAYERS) {
KisPaintLayerSP l = KisPaintLayerSP(new KisPaintLayer(image.data(), ch->name(), OPACITY_OPAQUE_U8, *deviceIt));
adapter.addNode(l.data(), image->rootLayer(), 0);
}
else {
KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::SaveFile, "OpenDocument");
dialog.setCaption(i18n("Export Layer") + '(' + ch->name() + ')');
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export));
QUrl url = QUrl::fromUserInput(dialog.filename());
if (url.isEmpty())
return;
QString mimefilter = KisMimeDatabase::mimeTypeForFile(url.toLocalFile());
KisPaintLayerSP l = KisPaintLayerSP(new KisPaintLayer(image.data(), ch->name(), OPACITY_OPAQUE_U8, *deviceIt));
QRect r = l->exactBounds();
KisDocument *d = KisPart::instance()->createDocument();
- d->prepareForImport();
KisImageWSP dst = KisImageWSP(new KisImage(d->createUndoStore(), r.width(), r.height(), (*deviceIt)->colorSpace(), l->name()));
d->setCurrentImage(dst);
dst->addNode(l->clone().data(), dst->rootLayer());
d->setOutputMimeType(mimefilter.toLatin1());
d->exportDocument(url);
delete d;
}
++deviceIt;
}
if (outputOps == TO_LAYERS) {
undo->endMacro();
}
- m_view->image()->unlock();
+ image->unlock();
image->setModified();
}
-
}
diff --git a/plugins/extensions/separate_channels/kis_separate_channels_plugin.cc b/plugins/extensions/separate_channels/kis_separate_channels_plugin.cc
index 16713b202c..e09d46b578 100644
--- a/plugins/extensions/separate_channels/kis_separate_channels_plugin.cc
+++ b/plugins/extensions/separate_channels/kis_separate_channels_plugin.cc
@@ -1,103 +1,103 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.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_separate_channels_plugin.h"
#include <QApplication>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <KoProgressUpdater.h>
#include <KoUpdater.h>
#include <KoColorSpace.h>
#include <KisViewManager.h>
#include <kis_types.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <kis_statusbar.h>
#include <kis_node_manager.h>
#include <widgets/kis_progress_widget.h>
#include <kis_action.h>
#include "kis_channel_separator.h"
#include "dlg_separate.h"
K_PLUGIN_FACTORY_WITH_JSON(KisSeparateChannelsPluginFactory, "kritaseparatechannels.json", registerPlugin<KisSeparateChannelsPlugin>();)
KisSeparateChannelsPlugin::KisSeparateChannelsPlugin(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
{
KisAction *action = createAction("separate");
connect(action, SIGNAL(triggered(bool)), SLOT(slotSeparate()));
}
KisSeparateChannelsPlugin::~KisSeparateChannelsPlugin()
{
}
void KisSeparateChannelsPlugin::slotSeparate()
{
- KisImageWSP image = m_view->image();
+ KisImageSP image = m_view->image();
if (!image) return;
KisLayerSP l = m_view->nodeManager()->activeLayer();
if (!l) return;
KisPaintDeviceSP dev = l->paintDevice();
if (!dev) return;
DlgSeparate * dlgSeparate = new DlgSeparate(dev->colorSpace()->name(),
image->colorSpace()->name(), m_view->mainWindow(), "Separate");
Q_CHECK_PTR(dlgSeparate);
dlgSeparate->setCaption(i18n("Separate Image"));
// If we're 8-bits, disable the downscale option
if (dev->pixelSize() == dev->channelCount()) {
dlgSeparate->enableDownscale(false);
}
if (dlgSeparate->exec() == QDialog::Accepted) {
QApplication::setOverrideCursor(Qt::BusyCursor);
KoProgressUpdater* pu = m_view->createProgressUpdater(KoProgressUpdater::Unthreaded);
pu->start(100, i18n("Separate Image"));
QPointer<KoUpdater> u = pu->startSubtask();
KisChannelSeparator separator(m_view);
separator.separate(u,
dlgSeparate->getAlphaOptions(),
dlgSeparate->getSource(),
dlgSeparate->getOutput(),
dlgSeparate->getDownscale(),
dlgSeparate->getToColor());
pu->deleteLater();
QApplication::restoreOverrideCursor();
}
delete dlgSeparate;
}
#include "kis_separate_channels_plugin.moc"
diff --git a/plugins/filters/colors/kis_wdg_color_to_alpha.cpp b/plugins/filters/colors/kis_wdg_color_to_alpha.cpp
index a8b0b1450c..03d233207d 100644
--- a/plugins/filters/colors/kis_wdg_color_to_alpha.cpp
+++ b/plugins/filters/colors/kis_wdg_color_to_alpha.cpp
@@ -1,126 +1,127 @@
/*
* 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_wdg_color_to_alpha.h"
#include <QCheckBox>
#include <QLayout>
#include <QSpinBox>
#include <KoColor.h>
#include <KoToolManager.h>
#include <KisViewManager.h>
#include <kis_canvas_resource_provider.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include <KoColorSpaceRegistry.h>
#include "ui_wdgcolortoalphabase.h"
KisWdgColorToAlpha::KisWdgColorToAlpha(QWidget * parent)
: KisConfigWidget(parent),
m_view(0)
{
m_widget = new Ui_WdgColorToAlphaBase();
m_widget->setupUi(this);
m_widget->textLabel1->hide();
m_widget->intThreshold->setRange(1, 255, 0);
connect(m_widget->colorSelector, SIGNAL(sigNewColor(KoColor)), SLOT(slotColorSelectorChanged(const KoColor&)));
connect(m_widget->intThreshold, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_widget->btnCustomColor, SIGNAL(changed(const KoColor)), SLOT(slotCustomColorSelected(const KoColor&)));
KoColor c(Qt::white, KoColorSpaceRegistry::instance()->rgb8());
m_widget->btnCustomColor->setColor(c);
}
KisWdgColorToAlpha::~KisWdgColorToAlpha()
{
delete m_widget;
}
void KisWdgColorToAlpha::setView(KisViewManager *view)
{
m_view = view;
}
void KisWdgColorToAlpha::slotFgColorChanged(const KoColor &color)
{
m_widget->btnCustomColor->setColor(color);
}
void KisWdgColorToAlpha::slotColorSelectorChanged(const KoColor &color)
{
m_widget->btnCustomColor->setColor(color);
}
void KisWdgColorToAlpha::slotCustomColorSelected(const KoColor &color)
{
+ KoColor c(color, KoColorSpaceRegistry::instance()->rgb8());
m_widget->colorSelector->slotSetColor(color);
emit sigConfigurationItemChanged();
}
void KisWdgColorToAlpha::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("targetcolor", value)) {
KoColor c;
if (value.value<QColor>().isValid()) {
- c.fromQColor(value.value<QColor>());
+ c = KoColor(value.value<QColor>(), KoColorSpaceRegistry::instance()->rgb8());
} else {
c = value.value<KoColor>();
}
m_widget->colorSelector->slotSetColor(c);
}
if (config->getProperty("threshold", value)) {
m_widget->intThreshold->setValue(value.toInt());
}
}
KisPropertiesConfigurationSP KisWdgColorToAlpha::configuration() const
{
KisFilterConfigurationSP config = new KisFilterConfiguration("colortoalpha", 1);
config->setProperty("targetcolor", widget()->colorSelector->getCurrentColor().toQColor());
config->setProperty("threshold", widget()->intThreshold->value());
return config;
}
void KisWdgColorToAlpha::hideEvent(QHideEvent *)
{
if (m_view) {
disconnect(m_view->resourceProvider(), SIGNAL(sigFGColorChanged(const KoColor&)), this, SLOT(slotFgColorChanged(const KoColor&)));
KoToolManager::instance()->switchBackRequested();
}
}
void KisWdgColorToAlpha::showEvent(QShowEvent *)
{
if (m_view) {
connect(m_view->resourceProvider(), SIGNAL(sigFGColorChanged(const KoColor&)), this, SLOT(slotFgColorChanged(const KoColor&)));
KoToolManager::instance()->switchToolTemporaryRequested("KritaSelected/KisToolColorPicker");
}
}
diff --git a/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp b/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp
index e0fa53f8cf..8da8bcfeae 100644
--- a/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp
+++ b/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp
@@ -1,147 +1,146 @@
/*
* 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_wdg_fastcolortransfer.h"
#include <QLayout>
#include <KisImportExportFilter.h>
#include <KisImportExportManager.h>
#include <filter/kis_filter_configuration.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <kis_image.h>
#include <kis_iterator_ng.h>
#include <kis_paint_device.h>
#include <kundo2command.h>
#include <KoColorSpaceRegistry.h>
#include <KisImportExportManager.h>
#include <kis_file_name_requester.h>
#include "ui_wdgfastcolortransfer.h"
KisWdgFastColorTransfer::KisWdgFastColorTransfer(QWidget * parent) : KisConfigWidget(parent)
{
m_widget = new Ui_WdgFastColorTransfer();
m_widget->setupUi(this);
m_widget->fileNameURLRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import));
connect(m_widget->fileNameURLRequester, SIGNAL(textChanged(const QString&)), this, SIGNAL(sigConfigurationItemChanged()));
}
KisWdgFastColorTransfer::~KisWdgFastColorTransfer()
{
delete m_widget;
}
void KisWdgFastColorTransfer::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("filename", value)) {
widget()->fileNameURLRequester->setFileName(value.toString());
}
}
KisPropertiesConfigurationSP KisWdgFastColorTransfer::configuration() const
{
KisFilterConfigurationSP config = new KisFilterConfiguration("colortransfer", 1);
QString fileName = this->widget()->fileNameURLRequester->fileName();
if (fileName.isEmpty()) return config;
KisPaintDeviceSP ref;
dbgPlugins << "Use as reference file : " << fileName;
KisDocument *d = KisPart::instance()->createDocument();
KisImportExportManager manager(d);
- KisImportExportFilter::ConversionStatus status;
- QString s = manager.importDocument(fileName, QString(), status);
- dbgPlugins << "import returned" << s << "and status" << status;
+ KisImportExportFilter::ConversionStatus status = manager.importDocument(fileName, QString());
+ dbgPlugins << "import returned status" << status;
KisImageWSP importedImage = d->image();
if (importedImage) {
ref = importedImage->projection();
}
if (!ref) {
dbgPlugins << "No reference image was specified.";
delete d;
return config;
}
// Convert ref to LAB
const KoColorSpace* labCS = KoColorSpaceRegistry::instance()->lab16();
if (!labCS) {
dbgPlugins << "The LAB colorspace is not available.";
delete d;
return config;
}
dbgPlugins << "convert ref to lab";
KUndo2Command* cmd = ref->convertTo(labCS, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
delete cmd;
// Compute the means and sigmas of ref
double meanL_ref = 0., meanA_ref = 0., meanB_ref = 0.;
double sigmaL_ref = 0., sigmaA_ref = 0., sigmaB_ref = 0.;
KisSequentialConstIterator refIt(ref, importedImage->bounds());
do {
const quint16* data = reinterpret_cast<const quint16*>(refIt.oldRawData());
quint32 L = data[0];
quint32 A = data[1];
quint32 B = data[2];
meanL_ref += L;
meanA_ref += A;
meanB_ref += B;
sigmaL_ref += L * L;
sigmaA_ref += A * A;
sigmaB_ref += B * B;
} while (refIt.nextPixel());
double totalSize = 1. / (importedImage->width() * importedImage->height());
meanL_ref *= totalSize;
meanA_ref *= totalSize;
meanB_ref *= totalSize;
sigmaL_ref *= totalSize;
sigmaA_ref *= totalSize;
sigmaB_ref *= totalSize;
dbgPlugins << totalSize << "" << meanL_ref << "" << meanA_ref << "" << meanB_ref << "" << sigmaL_ref << "" << sigmaA_ref << "" << sigmaB_ref;
config->setProperty("filename", fileName);
config->setProperty("meanL", meanL_ref);
config->setProperty("meanA", meanA_ref);
config->setProperty("meanB", meanB_ref);
config->setProperty("sigmaL", sigmaL_ref);
config->setProperty("sigmaA", sigmaA_ref);
config->setProperty("sigmaB", sigmaB_ref);
delete d;
return config;
}
diff --git a/plugins/flake/textshape/TextTool.cpp b/plugins/flake/textshape/TextTool.cpp
index 34aae87885..6a013741d9 100644
--- a/plugins/flake/textshape/TextTool.cpp
+++ b/plugins/flake/textshape/TextTool.cpp
@@ -1,3162 +1,3162 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
* Copyright (C) 2008, 2012 Pierre Stirnweiss <pstirnweiss@googlemail.org>
* Copyright (C) 2009 KO GmbH <cbo@kogmbh.com>
* Copyright (C) 2011 Mojtaba Shahi Senobari <mojtaba.shahi3000@gmail.com>
* Copyright (C) 2014 Denis Kuplyakov <dener.kup@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 "TextTool.h"
#include "TextEditingPluginContainer.h"
#include "dialogs/SimpleCharacterWidget.h"
#include "dialogs/SimpleParagraphWidget.h"
#include "dialogs/SimpleTableWidget.h"
#include "dialogs/SimpleInsertWidget.h"
#include "dialogs/ParagraphSettingsDialog.h"
#include "dialogs/StyleManagerDialog.h"
#include "dialogs/InsertCharacter.h"
#include "dialogs/FontDia.h"
#include "dialogs/TableDialog.h"
#include "dialogs/SectionFormatDialog.h"
#include "dialogs/SectionsSplitDialog.h"
#include "dialogs/SimpleTableWidget.h"
#include "commands/AutoResizeCommand.h"
#include "commands/ChangeListLevelCommand.h"
#include "FontSizeAction.h"
#include "FontFamilyAction.h"
#include <KoOdf.h>
#include <KoCanvasBase.h>
#include <KoShapeController.h>
#include <KoCanvasController.h>
#include <KoCanvasResourceManager.h>
#include <KoSelection.h>
#include <KoShapeManager.h>
#include <KoPointerEvent.h>
#include <KoColor.h>
#include <KoColorBackground.h>
#include <KoColorPopupAction.h>
#include <KoTextDocumentLayout.h>
#include <KoParagraphStyle.h>
#include <KoToolSelection.h>
#include <KoTextEditingPlugin.h>
#include <KoTextEditingRegistry.h>
#include <KoInlineTextObjectManager.h>
#include <KoTextRangeManager.h>
#include <KoStyleManager.h>
#include <KoTextOdfSaveHelper.h>
#include <KoTextDrag.h>
#include <KoTextDocument.h>
#include <KoTextEditor.h>
#include <KoChangeTracker.h>
#include <KoChangeTrackerElement.h>
#include <KoInlineNote.h>
#include <KoBookmark.h>
#include <KoBookmarkManager.h>
#include <KoListLevelProperties.h>
#include <KoTextLayoutRootArea.h>
//#include <ResizeTableCommand.h>
#include <KoIcon.h>
#include <KoViewConverter.h>
#include <KoShapeFactoryBase.h>
#include "kis_action_registry.h"
#include <QDebug>
#include <kstandardshortcut.h>
#include <kactionmenu.h>
#include <kstandardaction.h>
#include <ksharedconfig.h>
#include <QDesktopServices>
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QTextTable>
#include <QTextList>
#include <QTabWidget>
#include <QTextDocumentFragment>
#include <QToolTip>
#include <QSignalMapper>
#include <QLinearGradient>
#include <QBitmap>
#include <QDrag>
#include <QDragLeaveEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QMimeData>
#include "AnnotationTextShape.h"
#define AnnotationShape_SHAPEID "AnnotationTextShapeID"
#include "KoShapeBasedDocumentBase.h"
#include <KoAnnotation.h>
#include <KoShapeRegistry.h>
#include <kuser.h>
#include <KoDocumentRdfBase.h>
class TextToolSelection : public KoToolSelection
{
public:
TextToolSelection(QWeakPointer<KoTextEditor> editor)
: KoToolSelection(0)
, m_editor(editor)
{
}
bool hasSelection() override
{
if (!m_editor.isNull()) {
return m_editor.data()->hasSelection();
}
return false;
}
QWeakPointer<KoTextEditor> m_editor;
};
static bool hit(const QKeySequence &input, KStandardShortcut::StandardShortcut shortcut)
{
foreach (const QKeySequence &ks, KStandardShortcut::shortcut(shortcut)) {
if (input == ks) {
return true;
}
}
return false;
}
TextTool::TextTool(KoCanvasBase *canvas)
: KoToolBase(canvas)
, m_textShape(0)
, m_textShapeData(0)
, m_changeTracker(0)
, m_allowActions(true)
, m_allowAddUndoCommand(true)
, m_allowResourceManagerUpdates(true)
, m_prevCursorPosition(-1)
, m_caretTimer(this)
, m_caretTimerState(true)
, m_currentCommand(0)
, m_currentCommandHasChildren(false)
, m_specialCharacterDocker(0)
, m_textTyping(false)
, m_textDeleting(false)
, m_editTipTimer(this)
, m_delayedEnsureVisible(false)
, m_toolSelection(0)
, m_tableDraggedOnce(false)
, m_tablePenMode(false)
, m_lastImMicroFocus(QRectF(0, 0, 0, 0))
, m_drag(0)
{
setTextMode(true);
createActions();
m_unit = canvas->resourceManager()->unitResource(KoCanvasResourceManager::Unit);
foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) {
connect(plugin, SIGNAL(startMacro(QString)),
this, SLOT(startMacro(QString)));
connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro()));
QHash<QString, QAction *> actions = plugin->actions();
QHash<QString, QAction *>::iterator i = actions.begin();
while (i != actions.end()) {
addAction(i.key(), i.value());
++i;
}
}
// setup the context list.
QSignalMapper *signalMapper = new QSignalMapper(this);
connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(startTextEditingPlugin(QString)));
QList<QAction *> list;
list.append(this->action("format_font"));
foreach (const QString &key, KoTextEditingRegistry::instance()->keys()) {
KoTextEditingFactory *factory = KoTextEditingRegistry::instance()->value(key);
if (factory->showInMenu()) {
QAction *a = new QAction(factory->title(), this);
connect(a, SIGNAL(triggered()), signalMapper, SLOT(map()));
signalMapper->setMapping(a, factory->id());
list.append(a);
addAction(QString("apply_%1").arg(factory->id()), a);
}
}
setPopupActionList(list);
connect(canvas->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SLOT(shapeAddedToCanvas()));
m_caretTimer.setInterval(500);
connect(&m_caretTimer, SIGNAL(timeout()), this, SLOT(blinkCaret()));
m_editTipTimer.setInterval(500);
m_editTipTimer.setSingleShot(true);
connect(&m_editTipTimer, SIGNAL(timeout()), this, SLOT(showEditTip()));
}
void TextTool::createActions()
{
bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
& KoCanvasResourceManager::NoAdvancedText);
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
// FIXME: find new icons for these
m_actionConfigureSection = actionRegistry->makeQAction("configure_section", this);
addAction("configure_section", m_actionConfigureSection);
connect(m_actionConfigureSection, SIGNAL(triggered(bool)), this, SLOT(configureSection()));
m_actionInsertSection = actionRegistry->makeQAction("insert_section", this);
addAction("insert_section", m_actionInsertSection);
connect(m_actionInsertSection, SIGNAL(triggered(bool)), this, SLOT(insertNewSection()));
m_actionSplitSections = actionRegistry->makeQAction("split_sections", this);
addAction("split_sections", m_actionSplitSections);
connect(m_actionSplitSections, SIGNAL(triggered(bool)), this, SLOT(splitSections()));
m_actionPasteAsText = actionRegistry->makeQAction("edit_paste_text", this);
addAction("edit_paste_text", m_actionPasteAsText);
connect(m_actionPasteAsText, SIGNAL(triggered(bool)), this, SLOT(pasteAsText()));
m_actionFormatBold = actionRegistry->makeQAction("format_bold", this);
addAction("format_bold", m_actionFormatBold);
m_actionFormatBold->setCheckable(true);
connect(m_actionFormatBold, SIGNAL(triggered(bool)), this, SLOT(bold(bool)));
m_actionFormatItalic = actionRegistry->makeQAction("format_italic", this);
m_actionFormatItalic->setCheckable(true);
addAction("format_italic", m_actionFormatItalic);
connect(m_actionFormatItalic, SIGNAL(triggered(bool)), this, SLOT(italic(bool)));
m_actionFormatUnderline = actionRegistry->makeQAction("format_underline", this);
m_actionFormatUnderline->setCheckable(true);
addAction("format_underline", m_actionFormatUnderline);
connect(m_actionFormatUnderline, SIGNAL(triggered(bool)), this, SLOT(underline(bool)));
m_actionFormatStrikeOut = actionRegistry->makeQAction("format_strike", this);
m_actionFormatStrikeOut->setCheckable(true);
addAction("format_strike", m_actionFormatStrikeOut);
connect(m_actionFormatStrikeOut, SIGNAL(triggered(bool)), this, SLOT(strikeOut(bool)));
QActionGroup *alignmentGroup = new QActionGroup(this);
m_actionAlignLeft = actionRegistry->makeQAction("format_alignleft", this);
m_actionAlignLeft->setCheckable(true);
alignmentGroup->addAction(m_actionAlignLeft);
addAction("format_alignleft", m_actionAlignLeft);
connect(m_actionAlignLeft, SIGNAL(triggered(bool)), this, SLOT(alignLeft()));
m_actionAlignRight = actionRegistry->makeQAction("format_alignright", this);
m_actionAlignRight->setCheckable(true);
alignmentGroup->addAction(m_actionAlignRight);
addAction("format_alignright", m_actionAlignRight);
connect(m_actionAlignRight, SIGNAL(triggered(bool)), this, SLOT(alignRight()));
m_actionAlignCenter = actionRegistry->makeQAction("format_aligncenter", this);
m_actionAlignCenter->setCheckable(true);
addAction("format_aligncenter", m_actionAlignCenter);
alignmentGroup->addAction(m_actionAlignCenter);
connect(m_actionAlignCenter, SIGNAL(triggered(bool)), this, SLOT(alignCenter()));
m_actionAlignBlock = actionRegistry->makeQAction("format_alignblock", this);
m_actionAlignBlock->setCheckable(true);
alignmentGroup->addAction(m_actionAlignBlock);
addAction("format_alignblock", m_actionAlignBlock);
connect(m_actionAlignBlock, SIGNAL(triggered(bool)), this, SLOT(alignBlock()));
m_actionChangeDirection = actionRegistry->makeQAction("change_text_direction", this);
m_actionChangeDirection->setCheckable(true);
addAction("change_text_direction", m_actionChangeDirection);
connect(m_actionChangeDirection, SIGNAL(triggered()), this, SLOT(textDirectionChanged()));
m_actionFormatSuper = actionRegistry->makeQAction("format_super", this);
m_actionFormatSuper->setCheckable(true);
addAction("format_super", m_actionFormatSuper);
connect(m_actionFormatSuper, SIGNAL(triggered(bool)), this, SLOT(superScript(bool)));
m_actionFormatSub = actionRegistry->makeQAction("format_sub", this);
m_actionFormatSub->setCheckable(true);
addAction("format_sub", m_actionFormatSub);
connect(m_actionFormatSub, SIGNAL(triggered(bool)), this, SLOT(subScript(bool)));
// TODO: check these rtl-things work properly
m_actionFormatIncreaseIndent = actionRegistry->makeQAction("format_increaseindent", this);
addAction("format_increaseindent", m_actionFormatIncreaseIndent);
connect(m_actionFormatIncreaseIndent, SIGNAL(triggered()), this, SLOT(increaseIndent()));
m_actionFormatDecreaseIndent = actionRegistry->makeQAction("format_decreaseindent", this);
addAction("format_decreaseindent", m_actionFormatDecreaseIndent);
connect(m_actionFormatDecreaseIndent, SIGNAL(triggered()), this, SLOT(decreaseIndent()));
const char *const increaseIndentActionIconName =
QApplication::isRightToLeft() ? koIconNameCStr("format-indent-less") : koIconNameCStr("format-indent-more");
m_actionFormatIncreaseIndent->setIcon(koIcon(increaseIndentActionIconName));
const char *const decreaseIndentActionIconName =
QApplication::isRightToLeft() ? koIconNameCStr("format_decreaseindent") : koIconNameCStr("format-indent-less");
m_actionFormatIncreaseIndent->setIcon(koIcon(decreaseIndentActionIconName));
QAction *action = actionRegistry->makeQAction("format_bulletlist", this);
addAction("format_bulletlist", action);
action = actionRegistry->makeQAction("format_numberlist", this);
addAction("format_numberlist", action);
action = actionRegistry->makeQAction("fontsizeup", this);
addAction("fontsizeup", action);
connect(action, SIGNAL(triggered()), this, SLOT(increaseFontSize()));
action = actionRegistry->makeQAction("fontsizedown", this);
addAction("fontsizedown", action);
connect(action, SIGNAL(triggered()), this, SLOT(decreaseFontSize()));
m_actionFormatFontFamily = new KoFontFamilyAction(this);
m_actionFormatFontFamily->setText(i18n("Font Family"));
addAction("format_fontfamily", m_actionFormatFontFamily);
connect(m_actionFormatFontFamily, SIGNAL(triggered(QString)),
this, SLOT(setFontFamily(QString)));
m_variableMenu = new KActionMenu(i18n("Variable"), this);
addAction("insert_variable", m_variableMenu);
// ------------------- Actions with a key binding and no GUI item
action = actionRegistry->makeQAction("nonbreaking_space", this);
addAction("nonbreaking_space", action);
connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingSpace()));
action = actionRegistry->makeQAction("nonbreaking_hyphen", this);
addAction("nonbreaking_hyphen", action);
connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingHyphen()));
action = actionRegistry->makeQAction("insert_index", this);
addAction("insert_index", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertIndexMarker()));
action = actionRegistry->makeQAction("soft_hyphen", this);
// TODO: double check this one works, conflicts with "zoom out"
addAction("soft_hyphen", action);
connect(action, SIGNAL(triggered()), this, SLOT(softHyphen()));
if (useAdvancedText) {
action = actionRegistry->makeQAction("line_break", this);
addAction("line_break", action);
connect(action, SIGNAL(triggered()), this, SLOT(lineBreak()));
action = actionRegistry->makeQAction("insert_framebreak", this);
addAction("insert_framebreak", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertFrameBreak()));
}
action = actionRegistry->makeQAction("format_font", this);
addAction("format_font", action);
connect(action, SIGNAL(triggered()), this, SLOT(selectFont()));
m_actionFormatFontSize = new FontSizeAction(i18n("Font Size"), this);
addAction("format_fontsize", m_actionFormatFontSize);
connect(m_actionFormatFontSize, SIGNAL(fontSizeChanged(qreal)), this, SLOT(setFontSize(qreal)));
m_actionFormatTextColor = new KoColorPopupAction(this);
addAction("format_textcolor", m_actionFormatTextColor);
connect(m_actionFormatTextColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setTextColor(KoColor)));
m_actionFormatBackgroundColor = new KoColorPopupAction(this);
addAction("format_backgroundcolor", m_actionFormatBackgroundColor);
connect(m_actionFormatBackgroundColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setBackgroundColor(KoColor)));
m_growWidthAction = actionRegistry->makeQAction("grow_to_fit_width", this);
addAction("grow_to_fit_width", m_growWidthAction);
connect(m_growWidthAction, SIGNAL(triggered(bool)), this, SLOT(setGrowWidthToFit(bool)));
m_growHeightAction = actionRegistry->makeQAction("grow_to_fit_height", this);
addAction("grow_to_fit_height", m_growHeightAction);
connect(m_growHeightAction, SIGNAL(triggered(bool)), this, SLOT(setGrowHeightToFit(bool)));
m_shrinkToFitAction = actionRegistry->makeQAction("shrink_to_fit", this);
addAction("shrink_to_fit", m_shrinkToFitAction);
connect(m_shrinkToFitAction, SIGNAL(triggered(bool)), this, SLOT(setShrinkToFit(bool)));
if (useAdvancedText) {
action = actionRegistry->makeQAction("insert_table", this);
addAction("insert_table", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertTable()));
action = actionRegistry->makeQAction("insert_tablerow_above", this);
addAction("insert_tablerow_above", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowAbove()));
action = actionRegistry->makeQAction("insert_tablerow_below", this);
addAction("insert_tablerow_below", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowBelow()));
action = actionRegistry->makeQAction("insert_tablecolumn_left", this);
addAction("insert_tablecolumn_left", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnLeft()));
action = actionRegistry->makeQAction("insert_tablecolumn_right", this);
addAction("insert_tablecolumn_right", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnRight()));
action = actionRegistry->makeQAction("delete_tablecolumn", this);
addAction("delete_tablecolumn", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableColumn()));
action = actionRegistry->makeQAction("delete_tablerow", this);
addAction("delete_tablerow", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableRow()));
action = actionRegistry->makeQAction("merge_tablecells", this);
addAction("merge_tablecells", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(mergeTableCells()));
action = actionRegistry->makeQAction("split_tablecells", this);
addAction("split_tablecells", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(splitTableCells()));
action = actionRegistry->makeQAction("activate_borderpainter", this);
addAction("activate_borderpainter", action);
}
action = actionRegistry->makeQAction("format_paragraph", this);
addAction("format_paragraph", action);
connect(action, SIGNAL(triggered()), this, SLOT(formatParagraph()));
action = actionRegistry->makeQAction("format_stylist", this);
addAction("format_stylist", action);
connect(action, SIGNAL(triggered()), this, SLOT(showStyleManager()));
action = KStandardAction::selectAll(this, SLOT(selectAll()), this);
addAction("edit_select_all", action);
action = actionRegistry->makeQAction("insert_specialchar", this);
addAction("insert_specialchar", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertSpecialCharacter()));
action = actionRegistry->makeQAction("repaint", this);
addAction("repaint", action);
connect(action, SIGNAL(triggered()), this, SLOT(relayoutContent()));
action = actionRegistry->makeQAction("insert_annotation", this);
addAction("insert_annotation", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertAnnotation()));
#ifndef NDEBUG
action = actionRegistry->makeQAction("detailed_debug_paragraphs", this);
addAction("detailed_debug_paragraphs", action);
connect(action, SIGNAL(triggered()), this, SLOT(debugTextDocument()));
action = actionRegistry->makeQAction("detailed_debug_styles", this);
addAction("detailed_debug_styles", action);
connect(action, SIGNAL(triggered()), this, SLOT(debugTextStyles()));
#endif
}
#ifndef NDEBUG
#include "tests/MockShapes.h"
#include <kundo2stack.h>
#include <KisMimeDatabase.h>
TextTool::TextTool(MockCanvas *canvas) // constructor for our unit tests;
: KoToolBase(canvas),
m_textShape(0),
m_textShapeData(0),
m_changeTracker(0),
m_allowActions(true),
m_allowAddUndoCommand(true),
m_allowResourceManagerUpdates(true),
m_prevCursorPosition(-1),
m_caretTimer(this),
m_caretTimerState(true),
m_currentCommand(0),
m_currentCommandHasChildren(false),
m_specialCharacterDocker(0),
m_textEditingPlugins(0)
, m_editTipTimer(this)
, m_delayedEnsureVisible(false)
, m_tableDraggedOnce(false)
, m_tablePenMode(false)
{
// we could init some vars here, but we probably don't have to
QLocale::setDefault(QLocale("en"));
QTextDocument *document = new QTextDocument(); // this document is leaked
KoInlineTextObjectManager *inlineManager = new KoInlineTextObjectManager();
KoTextDocument(document).setInlineTextObjectManager(inlineManager);
KoTextRangeManager *locationManager = new KoTextRangeManager();
KoTextDocument(document).setTextRangeManager(locationManager);
m_textEditor = new KoTextEditor(document);
KoTextDocument(document).setTextEditor(m_textEditor.data());
m_toolSelection = new TextToolSelection(m_textEditor);
m_changeTracker = new KoChangeTracker();
KoTextDocument(document).setChangeTracker(m_changeTracker);
KoTextDocument(document).setUndoStack(new KUndo2Stack());
}
#endif
TextTool::~TextTool()
{
delete m_toolSelection;
}
void TextTool::showEditTip()
{
if (!m_textShapeData || m_editTipPointedAt.position == -1) {
return;
}
QTextCursor c(m_textShapeData->document());
c.setPosition(m_editTipPointedAt.position);
QString text = "<p align=center style=\'white-space:pre\' >";
int toolTipWidth = 0;
if (m_changeTracker && m_changeTracker->containsInlineChanges(c.charFormat())
&& m_changeTracker->displayChanges()) {
KoChangeTrackerElement *element = m_changeTracker->elementById(c.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt());
if (element->isEnabled()) {
QString changeType;
if (element->getChangeType() == KoGenChange::InsertChange) {
changeType = i18n("Insertion");
} else if (element->getChangeType() == KoGenChange::DeleteChange) {
changeType = i18n("Deletion");
} else {
changeType = i18n("Formatting");
}
text += "<b>" + changeType + "</b><br/>";
QString date = element->getDate();
//Remove the T which separates the Data and Time.
date[10] = QLatin1Char(' ');
date = element->getCreator() + QLatin1Char(' ') + date;
text += date + "</p>";
toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(date).width();
}
}
if (m_editTipPointedAt.bookmark || !m_editTipPointedAt.externalHRef.isEmpty()) {
QString help = i18n("Ctrl+click to go to link ");
help += m_editTipPointedAt.externalHRef;
text += help + "</p>";
toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
}
if (m_editTipPointedAt.note) {
QString help = i18n("Ctrl+click to go to the note ");
text += help + "</p>";
toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
}
if (m_editTipPointedAt.noteReference > 0) {
QString help = i18n("Ctrl+click to go to the note reference");
text += help + "</p>";
toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
}
QToolTip::hideText();
if (toolTipWidth) {
QRect keepRect(m_editTipPos - QPoint(3, 3), QSize(6, 6));
QToolTip::showText(m_editTipPos - QPoint(toolTipWidth / 2, 0), text, canvas()->canvasWidget(), keepRect);
}
}
void TextTool::blinkCaret()
{
if (!(canvas()->canvasWidget() && canvas()->canvasWidget()->hasFocus())) {
m_caretTimer.stop();
m_caretTimerState = false; // not visible.
} else {
m_caretTimerState = !m_caretTimerState;
}
repaintCaret();
}
void TextTool::relayoutContent()
{
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout *>(m_textShapeData->document()->documentLayout());
Q_ASSERT(lay);
foreach (KoTextLayoutRootArea *rootArea, lay->rootAreas()) {
rootArea->setDirty();
}
lay->emitLayoutIsDirty();
}
void TextTool::paint(QPainter &painter, const KoViewConverter &converter)
{
if (m_textEditor.isNull()) {
return;
}
if (canvas()
&& (canvas()->canvasWidget() && canvas()->canvasWidget()->hasFocus())
&& !m_caretTimer.isActive()) { // make sure we blink
m_caretTimer.start();
m_caretTimerState = true;
}
if (!m_caretTimerState) {
m_caretTimer.setInterval(500); // we set it lower during typing, so set it back to normal
}
if (!m_textShapeData) {
return;
}
if (m_textShapeData->isDirty()) {
return;
}
qreal zoomX, zoomY;
converter.zoom(&zoomX, &zoomY);
painter.save();
QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter);
shapeMatrix.scale(zoomX, zoomY);
shapeMatrix.translate(0, -m_textShapeData->documentOffset());
// Possibly draw table dragging visual cues
const qreal boxHeight = 20;
if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) {
QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(m_dx, 0.0);
if (m_tableDragInfo.tableColumnDivider > 0) {
//let's draw left
qreal w = m_tableDragInfo.tableLeadSize - m_dx;
QRectF rect(anchorPos - QPointF(w, 0.0), QSizeF(w, 0.0));
QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
drawRect.setHeight(boxHeight);
drawRect.moveTop(drawRect.top() - 1.5 * boxHeight);
QString label = m_unit.toUserStringValue(w);
int labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
painter.fillRect(drawRect, QColor(64, 255, 64, 196));
painter.setPen(QColor(0, 0, 0, 196));
if (labelWidth + 10 < drawRect.width()) {
QPointF centerLeft(drawRect.left(), drawRect.center().y());
QPointF centerRight(drawRect.right(), drawRect.center().y());
painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth / 2 + 5, 0.0));
painter.drawLine(centerLeft, centerLeft + QPointF(7, -5));
painter.drawLine(centerLeft, centerLeft + QPointF(7, 5));
painter.drawLine(drawRect.center() + QPointF(labelWidth / 2 + 5, 0.0), centerRight);
painter.drawLine(centerRight, centerRight + QPointF(-7, -5));
painter.drawLine(centerRight, centerRight + QPointF(-7, 5));
painter.drawText(drawRect, Qt::AlignCenter, label);
}
}
if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) {
//let's draw right
qreal w = m_tableDragInfo.tableTrailSize + m_dx;
QRectF rect(anchorPos, QSizeF(w, 0.0));
QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
drawRect.setHeight(boxHeight);
drawRect.moveTop(drawRect.top() - 1.5 * boxHeight);
QString label;
int labelWidth;
if (m_tableDragWithShift) {
label = i18n("follows along");
labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
drawRect.setWidth(2 * labelWidth);
QLinearGradient g(drawRect.topLeft(), drawRect.topRight());
g.setColorAt(0.6, QColor(255, 64, 64, 196));
g.setColorAt(1.0, QColor(255, 64, 64, 0));
QBrush brush(g);
painter.fillRect(drawRect, brush);
} else {
label = m_unit.toUserStringValue(w);
labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
drawRect.setHeight(boxHeight);
painter.fillRect(drawRect, QColor(64, 255, 64, 196));
}
painter.setPen(QColor(0, 0, 0, 196));
if (labelWidth + 10 < drawRect.width()) {
QPointF centerLeft(drawRect.left(), drawRect.center().y());
QPointF centerRight(drawRect.right(), drawRect.center().y());
painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth / 2 + 5, 0.0));
painter.drawLine(centerLeft, centerLeft + QPointF(7, -5));
painter.drawLine(centerLeft, centerLeft + QPointF(7, 5));
if (!m_tableDragWithShift) {
painter.drawLine(drawRect.center() + QPointF(labelWidth / 2 + 5, 0.0), centerRight);
painter.drawLine(centerRight, centerRight + QPointF(-7, -5));
painter.drawLine(centerRight, centerRight + QPointF(-7, 5));
}
painter.drawText(drawRect, Qt::AlignCenter, label);
}
if (!m_tableDragWithShift) {
// let's draw a helper text too
label = i18n("Press shift to not resize this");
labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
labelWidth += 10;
//if (labelWidth < drawRect.width())
{
drawRect.moveTop(drawRect.top() + boxHeight);
drawRect.moveLeft(drawRect.left() + (drawRect.width() - labelWidth) / 2);
drawRect.setWidth(labelWidth);
painter.fillRect(drawRect, QColor(64, 255, 64, 196));
painter.drawText(drawRect, Qt::AlignCenter, label);
}
}
}
}
// Possibly draw table dragging visual cues
if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) {
QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(0.0, m_dy);
if (m_tableDragInfo.tableRowDivider > 0) {
qreal h = m_tableDragInfo.tableLeadSize - m_dy;
QRectF rect(anchorPos - QPointF(0.0, h), QSizeF(0.0, h));
QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
drawRect.setWidth(boxHeight);
drawRect.moveLeft(drawRect.left() - 1.5 * boxHeight);
QString label = m_unit.toUserStringValue(h);
QRectF labelRect = QFontMetrics(QToolTip::font()).boundingRect(label);
labelRect.setHeight(boxHeight);
labelRect.setWidth(labelRect.width() + 10);
labelRect.moveTopLeft(drawRect.center() - QPointF(labelRect.width(), labelRect.height()) / 2);
painter.fillRect(drawRect, QColor(64, 255, 64, 196));
painter.fillRect(labelRect, QColor(64, 255, 64, 196));
painter.setPen(QColor(0, 0, 0, 196));
if (labelRect.height() + 10 < drawRect.height()) {
QPointF centerTop(drawRect.center().x(), drawRect.top());
QPointF centerBottom(drawRect.center().x(), drawRect.bottom());
painter.drawLine(centerTop, drawRect.center() - QPointF(0.0, labelRect.height() / 2 + 5));
painter.drawLine(centerTop, centerTop + QPointF(-5, 7));
painter.drawLine(centerTop, centerTop + QPointF(5, 7));
painter.drawLine(drawRect.center() + QPointF(0.0, labelRect.height() / 2 + 5), centerBottom);
painter.drawLine(centerBottom, centerBottom + QPointF(-5, -7));
painter.drawLine(centerBottom, centerBottom + QPointF(5, -7));
}
painter.drawText(labelRect, Qt::AlignCenter, label);
}
}
if (m_caretTimerState) {
// Lets draw the caret ourselves, as the Qt method doesn't take cursor
// charFormat into consideration.
QTextBlock block = m_textEditor.data()->block();
if (block.isValid()) {
int posInParag = m_textEditor.data()->position() - block.position();
if (posInParag <= block.layout()->preeditAreaPosition()) {
posInParag += block.layout()->preeditAreaText().length();
}
QTextLine tl = block.layout()->lineForTextPosition(m_textEditor.data()->position() - block.position());
if (tl.isValid()) {
painter.setRenderHint(QPainter::Antialiasing, false);
QRectF rect = caretRect(m_textEditor.data()->cursor());
QPointF baselinePoint;
if (tl.ascent() > 0) {
QFontMetricsF fm(m_textEditor.data()->charFormat().font(), painter.device());
rect.setY(rect.y() + tl.ascent() - qMin(tl.ascent(), fm.ascent()));
rect.setHeight(qMin(tl.ascent(), fm.ascent()) + qMin(tl.descent(), fm.descent()));
baselinePoint = QPoint(rect.x(), rect.y() + tl.ascent());
} else {
//line only filled with characters-without-size (eg anchors)
// layout will make sure line has height of block font
QFontMetricsF fm(block.charFormat().font(), painter.device());
rect.setHeight(fm.ascent() + fm.descent());
baselinePoint = QPoint(rect.x(), rect.y() + fm.ascent());
}
QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomLeft()));
drawRect.setWidth(2);
painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
if (m_textEditor.data()->isEditProtected(true)) {
QRectF circleRect(shapeMatrix.map(baselinePoint), QSizeF(14, 14));
circleRect.translate(-6.5, -6.5);
QPen pen(QColor(16, 255, 255));
pen.setWidthF(2.0);
painter.setPen(pen);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.drawEllipse(circleRect);
painter.drawLine(circleRect.topLeft() + QPointF(4.5, 4.5),
circleRect.bottomRight() - QPointF(4.5, 4.5));
} else {
painter.fillRect(drawRect, QColor(128, 255, 128));
}
}
}
}
painter.restore();
}
void TextTool::updateSelectedShape(const QPointF &point, bool noDocumentChange)
{
QRectF area(point, QSizeF(1, 1));
if (m_textEditor.data()->hasSelection()) {
repaintSelection();
} else {
repaintCaret();
}
QList<KoShape *> sortedShapes = canvas()->shapeManager()->shapesAt(area, true);
qSort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
for (int count = sortedShapes.count() - 1; count >= 0; count--) {
KoShape *shape = sortedShapes.at(count);
if (shape->isContentProtected()) {
continue;
}
TextShape *textShape = dynamic_cast<TextShape *>(shape);
if (textShape) {
if (textShape != m_textShape) {
if (static_cast<KoTextShapeData *>(textShape->userData())->document() != m_textShapeData->document()) {
//we should only change to another document if allowed
if (noDocumentChange) {
return;
}
// if we change to another textdocument we need to remove selection in old document
// or it would continue to be painted etc
m_textEditor.data()->setPosition(m_textEditor.data()->position());
}
m_textShape = textShape;
setShapeData(static_cast<KoTextShapeData *>(m_textShape->userData()));
// This is how we inform the rulers of the active range
// For now we will not consider table cells, but just give the shape dimensions
QVariant v;
QRectF rect(QPoint(), m_textShape->size());
rect = m_textShape->absoluteTransformation(0).mapRect(rect);
v.setValue(rect);
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, v);
}
return;
}
}
}
void TextTool::mousePressEvent(KoPointerEvent *event)
{
if (m_textEditor.isNull()) {
return;
}
// request the software keyboard, if any
if (event->button() == Qt::LeftButton && qApp->autoSipEnabled()) {
QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(qApp->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
// the two following bools just make it all a lot easier to read in the following if()
// basically, we require a widget for this to work (passing 0 to QApplication::sendEvent
// crashes) and there are three tests any one of which can be true to trigger the event
const bool hasWidget = canvas()->canvasWidget();
if ((behavior == QStyle::RSIP_OnMouseClick && hasWidget) ||
(hasWidget && canvas()->canvasWidget()->hasFocus())) {
QEvent event(QEvent::RequestSoftwareInputPanel);
if (hasWidget) {
QApplication::sendEvent(canvas()->canvasWidget(), &event);
}
}
}
bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
updateSelectedShape(event->point, shiftPressed);
KoSelection *selection = canvas()->shapeManager()->selection();
if (m_textShape && !selection->isSelected(m_textShape) && m_textShape->isSelectable()) {
selection->deselectAll();
selection->select(m_textShape);
}
KoPointedAt pointedAt = hitTest(event->point);
m_tableDraggedOnce = false;
m_clickWithinSelection = false;
if (pointedAt.position != -1) {
m_tablePenMode = false;
if ((event->button() == Qt::LeftButton) && !shiftPressed && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position)) {
m_clickWithinSelection = true;
m_draggingOrigin = event->pos(); //we store the pixel pos
} else if (!(event->button() == Qt::RightButton && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position))) {
m_textEditor.data()->setPosition(pointedAt.position, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
useCursor(Qt::IBeamCursor);
}
m_tableDragInfo.tableHit = KoPointedAt::None;
if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
m_caretTimer.stop();
m_caretTimer.setInterval(50);
m_caretTimer.start();
m_caretTimerState = true; // turn caret instantly on on click
}
} else {
if (event->button() == Qt::RightButton) {
m_tablePenMode = false;
KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck();
if (plugin) {
plugin->setCurrentCursorPosition(m_textShapeData->document(), -1);
}
event->ignore();
} else if (m_tablePenMode) {
m_textEditor.data()->beginEditBlock(kundo2_i18n("Change Border Formatting"));
if (pointedAt.tableHit == KoPointedAt::ColumnDivider) {
if (pointedAt.tableColumnDivider < pointedAt.table->columns()) {
m_textEditor.data()->setTableBorderData(pointedAt.table,
pointedAt.tableRowDivider, pointedAt.tableColumnDivider,
KoBorder::LeftBorder, m_tablePenBorderData);
}
if (pointedAt.tableColumnDivider > 0) {
m_textEditor.data()->setTableBorderData(pointedAt.table,
pointedAt.tableRowDivider, pointedAt.tableColumnDivider - 1,
KoBorder::RightBorder, m_tablePenBorderData);
}
} else if (pointedAt.tableHit == KoPointedAt::RowDivider) {
if (pointedAt.tableRowDivider < pointedAt.table->rows()) {
m_textEditor.data()->setTableBorderData(pointedAt.table,
pointedAt.tableRowDivider, pointedAt.tableColumnDivider,
KoBorder::TopBorder, m_tablePenBorderData);
}
if (pointedAt.tableRowDivider > 0) {
m_textEditor.data()->setTableBorderData(pointedAt.table,
pointedAt.tableRowDivider - 1, pointedAt.tableColumnDivider,
KoBorder::BottomBorder, m_tablePenBorderData);
}
}
m_textEditor.data()->endEditBlock();
} else {
m_tableDragInfo = pointedAt;
m_tablePenMode = false;
}
return;
}
if (shiftPressed) { // altered selection.
repaintSelection();
} else {
repaintCaret();
}
updateSelectionHandler();
updateStyleManager();
updateActions();
//activate context-menu for spelling-suggestions
if (event->button() == Qt::RightButton) {
KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck();
if (plugin) {
plugin->setCurrentCursorPosition(m_textShapeData->document(), m_textEditor.data()->position());
}
event->ignore();
}
if (event->button() == Qt::MidButton) { // Paste
const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Selection);
// on windows we do not have data if we try to paste this selection
if (data) {
m_prevCursorPosition = m_textEditor.data()->position();
m_textEditor.data()->paste(canvas(), data, canvas()->resourceManager());
editingPluginEvents();
}
}
}
void TextTool::setShapeData(KoTextShapeData *data)
{
bool docChanged = !data || !m_textShapeData || m_textShapeData->document() != data->document();
if (m_textShapeData) {
disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
}
m_textShapeData = data;
if (!m_textShapeData) {
return;
}
connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
if (docChanged) {
if (!m_textEditor.isNull()) {
disconnect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions()));
}
m_textEditor = KoTextDocument(m_textShapeData->document()).textEditor();
Q_ASSERT(m_textEditor.data());
if (!m_toolSelection) {
m_toolSelection = new TextToolSelection(m_textEditor.data());
} else {
m_toolSelection->m_editor = m_textEditor.data();
}
m_variableMenu->menu()->clear();
KoTextDocument document(m_textShapeData->document());
foreach (QAction *action, document.inlineTextObjectManager()->createInsertVariableActions(canvas())) {
m_variableMenu->addAction(action);
connect(action, SIGNAL(triggered()), this, SLOT(returnFocusToCanvas()));
}
connect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions()));
updateActions();
}
}
void TextTool::updateSelectionHandler()
{
if (m_textEditor) {
emit selectionChanged(m_textEditor.data()->hasSelection());
if (m_textEditor.data()->hasSelection()) {
QClipboard *clipboard = QApplication::clipboard();
if (clipboard->supportsSelection()) {
clipboard->setText(m_textEditor.data()->selectedText(), QClipboard::Selection);
}
}
}
KoCanvasResourceManager *p = canvas()->resourceManager();
m_allowResourceManagerUpdates = false;
if (m_textEditor && m_textShapeData) {
p->setResource(KoText::CurrentTextPosition, m_textEditor.data()->position());
p->setResource(KoText::CurrentTextAnchor, m_textEditor.data()->anchor());
QVariant variant;
variant.setValue<void *>(m_textShapeData->document());
p->setResource(KoText::CurrentTextDocument, variant);
} else {
p->clearResource(KoText::CurrentTextPosition);
p->clearResource(KoText::CurrentTextAnchor);
p->clearResource(KoText::CurrentTextDocument);
}
m_allowResourceManagerUpdates = true;
}
QMimeData *TextTool::generateMimeData() const
{
if (!m_textShapeData || m_textEditor.isNull() || !m_textEditor.data()->hasSelection()) {
return 0;
}
int from = m_textEditor.data()->position();
int to = m_textEditor.data()->anchor();
KoTextOdfSaveHelper saveHelper(m_textShapeData->document(), from, to);
KoTextDrag drag;
#ifdef SHOULD_BUILD_RDF
KoDocumentResourceManager *rm = 0;
if (canvas()->shapeController()) {
rm = canvas()->shapeController()->resourceManager();
}
if (rm && rm->hasResource(KoText::DocumentRdf)) {
KoDocumentRdfBase *rdf = qobject_cast<KoDocumentRdfBase *>(rm->resource(KoText::DocumentRdf).value<QObject *>());
if (rdf) {
saveHelper.setRdfModel(rdf->model());
}
}
#endif
drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
QTextDocumentFragment fragment = m_textEditor.data()->selection();
drag.setData("text/html", fragment.toHtml("utf-8").toUtf8());
drag.setData("text/plain", fragment.toPlainText().toUtf8());
return drag.takeMimeData();
}
TextEditingPluginContainer *TextTool::textEditingPluginContainer()
{
m_textEditingPlugins = canvas()->resourceManager()->
resource(TextEditingPluginContainer::ResourceId).value<TextEditingPluginContainer *>();
if (m_textEditingPlugins == 0) {
m_textEditingPlugins = new TextEditingPluginContainer(canvas()->resourceManager());
QVariant variant;
variant.setValue(m_textEditingPlugins.data());
canvas()->resourceManager()->setResource(TextEditingPluginContainer::ResourceId, variant);
foreach (KoTextEditingPlugin *plugin, m_textEditingPlugins->values()) {
connect(plugin, SIGNAL(startMacro(QString)),
this, SLOT(startMacro(QString)));
connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro()));
QHash<QString, QAction *> actions = plugin->actions();
QHash<QString, QAction *>::iterator i = actions.begin();
while (i != actions.end()) {
addAction(i.key(), i.value());
++i;
}
}
}
return m_textEditingPlugins;
}
void TextTool::copy() const
{
QMimeData *mimeData = generateMimeData();
if (mimeData) {
QApplication::clipboard()->setMimeData(mimeData);
}
}
void TextTool::deleteSelection()
{
m_textEditor.data()->deleteChar();
editingPluginEvents();
}
bool TextTool::paste()
{
const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
// on windows we do not have data if we try to paste the selection
if (!data) {
return false;
}
// since this is not paste-as-text we will not paste in urls, but instead let KoToolProxy solve it
if (data->hasUrls()) {
return false;
}
if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))
|| data->hasText()) {
m_prevCursorPosition = m_textEditor.data()->position();
m_textEditor.data()->paste(canvas(), data);
editingPluginEvents();
return true;
}
return false;
}
void TextTool::cut()
{
if (m_textEditor.data()->hasSelection()) {
copy();
KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Cut"));
m_textEditor.data()->deleteChar(false, topCmd);
m_textEditor.data()->endEditBlock();
}
}
QStringList TextTool::supportedPasteMimeTypes() const
{
QStringList list;
list << "text/plain" << "text/html" << "application/vnd.oasis.opendocument.text";
return list;
}
void TextTool::dragMoveEvent(QDragMoveEvent *event, const QPointF &point)
{
if (event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::Text))
|| event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::OpenOfficeClipboard))
|| event->mimeData()->hasText()) {
if (m_drag) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else if (event->proposedAction() == Qt::CopyAction) {
event->acceptProposedAction();
} else {
event->ignore();
return;
}
KoPointedAt pointedAt = hitTest(point);
if (pointedAt.position == -1) {
event->ignore();
}
if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
m_caretTimer.stop();
m_caretTimer.setInterval(50);
m_caretTimer.start();
m_caretTimerState = true; // turn caret instantly on on click
}
if (m_preDragSelection.cursor.isNull()) {
repaintSelection();
m_preDragSelection.cursor = QTextCursor(*m_textEditor.data()->cursor());
if (m_drag) {
// Make a selection that looks like the current cursor selection
// so we can move the real carent around freely
QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
m_preDragSelection.format = QTextCharFormat();
m_preDragSelection.format.setBackground(qApp->palette().brush(QPalette::Highlight));
m_preDragSelection.format.setForeground(qApp->palette().brush(QPalette::HighlightedText));
sels.append(m_preDragSelection);
KoTextDocument(m_textShapeData->document()).setSelections(sels);
} // else we wantt the selection ot disappaear
}
repaintCaret(); // will erase caret
m_textEditor.data()->setPosition(pointedAt.position);
repaintCaret(); // will paint caret in new spot
// Selection has visually not appeared at a new spot so no need to repaint it
}
}
void TextTool::dragLeaveEvent(QDragLeaveEvent *event)
{
if (m_drag) {
// restore the old selections
QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
sels.pop_back();
KoTextDocument(m_textShapeData->document()).setSelections(sels);
}
repaintCaret(); // will erase caret in old spot
m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor());
m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor);
repaintCaret(); // will paint caret in new spot
if (!m_drag) {
repaintSelection(); // will paint selection again
}
// mark that we now are back to normal selection
m_preDragSelection.cursor = QTextCursor();
event->accept();
}
void TextTool::dropEvent(QDropEvent *event, const QPointF &)
{
if (m_drag) {
// restore the old selections
QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
sels.pop_back();
KoTextDocument(m_textShapeData->document()).setSelections(sels);
}
QTextCursor insertCursor(*m_textEditor.data()->cursor());
m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor());
m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor);
repaintSelection(); // will erase the selection in new spot
if (m_drag) {
m_textEditor.data()->deleteChar();
}
m_prevCursorPosition = insertCursor.position();
m_textEditor.data()->setPosition(m_prevCursorPosition);
m_textEditor.data()->paste(canvas(), event->mimeData());
m_textEditor.data()->setPosition(m_prevCursorPosition);
//since the paste made insertCursor we can now use that for the end position
m_textEditor.data()->setPosition(insertCursor.position(), QTextCursor::KeepAnchor);
// mark that we no are back to normal selection
m_preDragSelection.cursor = QTextCursor();
event->accept();
}
KoPointedAt TextTool::hitTest(const QPointF &point) const
{
if (!m_textShape || !m_textShapeData) {
return KoPointedAt();
}
QPointF p = m_textShape->convertScreenPos(point);
KoTextLayoutRootArea *rootArea = m_textShapeData->rootArea();
return rootArea ? rootArea->hitTest(p, Qt::FuzzyHit) : KoPointedAt();
}
void TextTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) {
event->ignore(); // allow the event to be used by another
return;
}
if (event->modifiers() & Qt::ShiftModifier) {
// When whift is pressed we behave as a single press
return mousePressEvent(event);
}
m_textEditor.data()->select(QTextCursor::WordUnderCursor);
m_clickWithinSelection = false;
repaintSelection();
updateSelectionHandler();
}
void TextTool::mouseTripleClickEvent(KoPointerEvent *event)
{
if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) {
event->ignore(); // allow the event to be used by another
return;
}
if (event->modifiers() & Qt::ShiftModifier) {
// When whift is pressed we behave as a single press
return mousePressEvent(event);
}
m_textEditor.data()->clearSelection();
m_textEditor.data()->movePosition(QTextCursor::StartOfBlock);
m_textEditor.data()->movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
m_clickWithinSelection = false;
repaintSelection();
updateSelectionHandler();
}
void TextTool::mouseMoveEvent(KoPointerEvent *event)
{
m_editTipPos = event->globalPos();
if (event->buttons()) {
updateSelectedShape(event->point, true);
}
m_editTipTimer.stop();
if (QToolTip::isVisible()) {
QToolTip::hideText();
}
KoPointedAt pointedAt = hitTest(event->point);
if (event->buttons() == Qt::NoButton) {
if (m_tablePenMode) {
if (pointedAt.tableHit == KoPointedAt::ColumnDivider || pointedAt.tableHit == KoPointedAt::RowDivider) {
useTableBorderCursor();
} else {
useCursor(Qt::IBeamCursor);
}
// do nothing else
return;
}
if (!m_textShapeData || pointedAt.position < 0) {
if (pointedAt.tableHit == KoPointedAt::ColumnDivider) {
useCursor(Qt::SplitHCursor);
m_draggingOrigin = event->point;
} else if (pointedAt.tableHit == KoPointedAt::RowDivider) {
if (pointedAt.tableRowDivider > 0) {
useCursor(Qt::SplitVCursor);
m_draggingOrigin = event->point;
} else {
useCursor(Qt::IBeamCursor);
}
} else {
useCursor(Qt::IBeamCursor);
}
return;
}
QTextCursor mouseOver(m_textShapeData->document());
mouseOver.setPosition(pointedAt.position);
if (m_changeTracker && m_changeTracker->containsInlineChanges(mouseOver.charFormat())) {
m_editTipPointedAt = pointedAt;
if (QToolTip::isVisible()) {
QTimer::singleShot(0, this, SLOT(showEditTip()));
} else {
m_editTipTimer.start();
}
}
if ((pointedAt.bookmark || !pointedAt.externalHRef.isEmpty()) || pointedAt.note || (pointedAt.noteReference > 0)) {
if (event->modifiers() & Qt::ControlModifier) {
useCursor(Qt::PointingHandCursor);
}
m_editTipPointedAt = pointedAt;
if (QToolTip::isVisible()) {
QTimer::singleShot(0, this, SLOT(showEditTip()));
} else {
m_editTipTimer.start();
}
return;
}
// check if mouse pointer is over shape with hyperlink
KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point);
if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) {
useCursor(Qt::PointingHandCursor);
return;
}
useCursor(Qt::IBeamCursor);
// Set Arrow Cursor when mouse is on top of annotation shape.
if (selectedShape) {
if (selectedShape->shapeId() == "AnnotationTextShapeID") {
QPointF point(event->point);
if (point.y() <= (selectedShape->position().y() + 25)) {
useCursor(Qt::ArrowCursor);
}
}
}
return;
} else {
if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) {
m_tableDragWithShift = event->modifiers() & Qt::ShiftModifier;
if (m_tableDraggedOnce) {
canvas()->shapeController()->resourceManager()->undoStack()->undo();
}
KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Column Width"));
m_dx = m_draggingOrigin.x() - event->point.x();
if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()
&& m_tableDragInfo.tableTrailSize + m_dx < 0) {
m_dx = -m_tableDragInfo.tableTrailSize;
}
if (m_tableDragInfo.tableColumnDivider > 0) {
if (m_tableDragInfo.tableLeadSize - m_dx < 0) {
m_dx = m_tableDragInfo.tableLeadSize;
}
m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table,
m_tableDragInfo.tableColumnDivider - 1,
m_tableDragInfo.tableLeadSize - m_dx, topCmd);
} else {
m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, -m_dx, 0.0);
}
if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) {
if (!m_tableDragWithShift) {
m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table,
m_tableDragInfo.tableColumnDivider,
m_tableDragInfo.tableTrailSize + m_dx, topCmd);
}
} else {
m_tableDragWithShift = true; // act like shift pressed
}
if (m_tableDragWithShift) {
m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, 0.0, m_dx);
}
m_textEditor.data()->endEditBlock();
m_tableDragInfo.tableDividerPos.setY(m_textShape->convertScreenPos(event->point).y());
if (m_tableDraggedOnce) {
//we need to redraw like this so we update outside the textshape too
if (canvas()->canvasWidget()) {
canvas()->canvasWidget()->update();
}
}
m_tableDraggedOnce = true;
} else if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) {
if (m_tableDraggedOnce) {
canvas()->shapeController()->resourceManager()->undoStack()->undo();
}
if (m_tableDragInfo.tableRowDivider > 0) {
KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Row Height"));
m_dy = m_draggingOrigin.y() - event->point.y();
if (m_tableDragInfo.tableLeadSize - m_dy < 0) {
m_dy = m_tableDragInfo.tableLeadSize;
}
m_textEditor.data()->adjustTableRowHeight(m_tableDragInfo.table,
m_tableDragInfo.tableRowDivider - 1,
m_tableDragInfo.tableLeadSize - m_dy, topCmd);
m_textEditor.data()->endEditBlock();
m_tableDragInfo.tableDividerPos.setX(m_textShape->convertScreenPos(event->point).x());
if (m_tableDraggedOnce) {
//we need to redraw like this so we update outside the textshape too
if (canvas()->canvasWidget()) {
canvas()->canvasWidget()->update();
}
}
m_tableDraggedOnce = true;
}
} else if (m_tablePenMode) {
// do nothing
} else if (m_clickWithinSelection) {
if (!m_drag && (event->pos() - m_draggingOrigin).manhattanLength()
>= QApplication::startDragDistance()) {
QMimeData *mimeData = generateMimeData();
if (mimeData) {
m_drag = new QDrag(canvas()->canvasWidget());
m_drag->setMimeData(mimeData);
m_drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction);
m_drag = 0;
}
}
} else {
useCursor(Qt::IBeamCursor);
if (pointedAt.position == m_textEditor.data()->position()) {
return;
}
if (pointedAt.position >= 0) {
if (m_textEditor.data()->hasSelection()) {
repaintSelection(); // will erase selection
} else {
repaintCaret();
}
m_textEditor.data()->setPosition(pointedAt.position, QTextCursor::KeepAnchor);
if (m_textEditor.data()->hasSelection()) {
repaintSelection();
} else {
repaintCaret();
}
}
}
updateSelectionHandler();
}
}
void TextTool::mouseReleaseEvent(KoPointerEvent *event)
{
event->ignore();
editingPluginEvents();
m_tableDragInfo.tableHit = KoPointedAt::None;
if (m_tableDraggedOnce) {
m_tableDraggedOnce = false;
//we need to redraw like this so we update outside the textshape too
if (canvas()->canvasWidget()) {
canvas()->canvasWidget()->update();
}
}
if (!m_textShapeData) {
return;
}
// check if mouse pointer is not over some shape with hyperlink
KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point);
if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) {
QString url = selectedShape->hyperLink();
runUrl(event, url);
return;
}
KoPointedAt pointedAt = hitTest(event->point);
if (m_clickWithinSelection && !m_drag) {
if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
m_caretTimer.stop();
m_caretTimer.setInterval(50);
m_caretTimer.start();
m_caretTimerState = true; // turn caret instantly on on click
}
repaintCaret(); // will erase caret
repaintSelection(); // will erase selection
m_textEditor.data()->setPosition(pointedAt.position);
repaintCaret(); // will paint caret in new spot
}
// Is there an anchor here ?
if ((event->modifiers() & Qt::ControlModifier) && !m_textEditor.data()->hasSelection()) {
if (pointedAt.bookmark) {
m_textEditor.data()->setPosition(pointedAt.bookmark->rangeStart());
ensureCursorVisible();
event->accept();
return;
}
if (pointedAt.note) {
m_textEditor.data()->setPosition(pointedAt.note->textFrame()->firstPosition());
ensureCursorVisible();
event->accept();
return;
}
if (pointedAt.noteReference > 0) {
m_textEditor.data()->setPosition(pointedAt.noteReference);
ensureCursorVisible();
event->accept();
return;
}
if (!pointedAt.externalHRef.isEmpty()) {
runUrl(event, pointedAt.externalHRef);
}
}
}
void TextTool::keyPressEvent(QKeyEvent *event)
{
int destinationPosition = -1; // for those cases where the moveOperation is not relevant;
QTextCursor::MoveOperation moveOperation = QTextCursor::NoMove;
KoTextEditor *textEditor = m_textEditor.data();
m_tablePenMode = false; // keypress always stops the table (border) pen mode
Q_ASSERT(textEditor);
if (event->key() == Qt::Key_Backspace) {
if (!textEditor->hasSelection() && textEditor->block().textList()
&& (textEditor->position() == textEditor->block().position())
&& !(m_changeTracker && m_changeTracker->recordChanges())) {
if (!textEditor->blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) {
// backspace at beginning of numbered list item, makes it unnumbered
textEditor->toggleListNumbering(false);
} else {
KoListLevelProperties llp;
llp.setStyle(KoListStyle::None);
llp.setLevel(0);
// backspace on numbered, empty parag, removes numbering.
textEditor->setListProperties(llp);
}
} else if (textEditor->position() > 0 || textEditor->hasSelection()) {
if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) { // delete prev word.
textEditor->movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
}
textEditor->deletePreviousChar();
editingPluginEvents();
}
} else if ((event->key() == Qt::Key_Tab)
&& ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList()) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1);
textEditor->addCommand(cll);
editingPluginEvents();
} else if ((event->key() == Qt::Key_Backtab)
&& ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList() && !(m_changeTracker && m_changeTracker->recordChanges())) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1);
textEditor->addCommand(cll);
editingPluginEvents();
} else if (event->key() == Qt::Key_Delete) {
if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) {// delete next word.
textEditor->movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
}
// the event only gets through when the Del is not used in the app
// if the app forwards Del then deleteSelection is used
textEditor->deleteChar();
editingPluginEvents();
} else if ((event->key() == Qt::Key_Left) && (event->modifiers() & Qt::ControlModifier) == 0) {
moveOperation = QTextCursor::Left;
} else if ((event->key() == Qt::Key_Right) && (event->modifiers() & Qt::ControlModifier) == 0) {
moveOperation = QTextCursor::Right;
} else if ((event->key() == Qt::Key_Up) && (event->modifiers() & Qt::ControlModifier) == 0) {
moveOperation = QTextCursor::Up;
} else if ((event->key() == Qt::Key_Down) && (event->modifiers() & Qt::ControlModifier) == 0) {
moveOperation = QTextCursor::Down;
} else {
// check for shortcuts.
QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers()));
if (hit(item, KStandardShortcut::Begin))
// Goto beginning of the document. Default: Ctrl-Home
{
destinationPosition = 0;
} else if (hit(item, KStandardShortcut::End)) {
// Goto end of the document. Default: Ctrl-End
if (m_textShapeData) {
QTextBlock last = m_textShapeData->document()->lastBlock();
destinationPosition = last.position() + last.length() - 1;
}
} else if (hit(item, KStandardShortcut::Prior)) { // page up
// Scroll up one page. Default: Prior
event->ignore(); // let app level actions handle it
return;
} else if (hit(item, KStandardShortcut::Next)) {
// Scroll down one page. Default: Next
event->ignore(); // let app level actions handle it
return;
} else if (hit(item, KStandardShortcut::BeginningOfLine))
// Goto beginning of current line. Default: Home
{
moveOperation = QTextCursor::StartOfLine;
} else if (hit(item, KStandardShortcut::EndOfLine))
// Goto end of current line. Default: End
{
moveOperation = QTextCursor::EndOfLine;
} else if (hit(item, KStandardShortcut::BackwardWord)) {
moveOperation = QTextCursor::WordLeft;
} else if (hit(item, KStandardShortcut::ForwardWord)) {
moveOperation = QTextCursor::WordRight;
}
-#ifdef Q_WS_MAC
+#ifdef Q_OS_OSX
// Don't reject "alt" key, it may be used for typing text on Mac OS
else if ((event->modifiers() & Qt::ControlModifier) || event->text().length() == 0) {
#else
else if ((event->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) || event->text().length() == 0) {
#endif
event->ignore();
return;
} else if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) {
m_prevCursorPosition = textEditor->position();
textEditor->newLine();
updateActions();
editingPluginEvents();
} else if ((event->key() == Qt::Key_Tab || !(event->text().length() == 1 && !event->text().at(0).isPrint()))) { // insert the text
m_prevCursorPosition = textEditor->position();
startingSimpleEdit(); //signal editing plugins that this is a simple edit
textEditor->insertText(event->text());
editingPluginEvents();
}
}
if (moveOperation != QTextCursor::NoMove || destinationPosition != -1) {
useCursor(Qt::BlankCursor);
bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
if (textEditor->hasSelection()) {
repaintSelection(); // will erase selection
} else {
repaintCaret();
}
QTextBlockFormat format = textEditor->blockFormat();
KoText::Direction dir = static_cast<KoText::Direction>(format.intProperty(KoParagraphStyle::TextProgressionDirection));
bool isRtl;
if (dir == KoText::AutoDirection) {
isRtl = textEditor->block().text().isRightToLeft();
} else {
isRtl = dir == KoText::RightLeftTopBottom;
}
if (isRtl) { // if RTL toggle direction of cursor movement.
switch (moveOperation) {
case QTextCursor::Left: moveOperation = QTextCursor::Right; break;
case QTextCursor::Right: moveOperation = QTextCursor::Left; break;
case QTextCursor::WordRight: moveOperation = QTextCursor::WordLeft; break;
case QTextCursor::WordLeft: moveOperation = QTextCursor::WordRight; break;
default: break;
}
}
int prevPosition = textEditor->position();
if (moveOperation != QTextCursor::NoMove)
textEditor->movePosition(moveOperation,
shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
else
textEditor->setPosition(destinationPosition,
shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
if (moveOperation == QTextCursor::Down && prevPosition == textEditor->position()) {
// change behavior a little big from Qt; at the bottom of the doc we go to the end of the doc
textEditor->movePosition(QTextCursor::End,
shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
}
if (shiftPressed) { // altered selection.
repaintSelection();
} else {
repaintCaret();
}
updateActions();
editingPluginEvents();
}
if (m_caretTimer.isActive()) { // make the caret not blink but decide on the action if its visible or not.
m_caretTimer.stop();
m_caretTimer.setInterval(50);
m_caretTimer.start();
m_caretTimerState = true; // turn caret on while typing
}
if (moveOperation != QTextCursor::NoMove)
// this difference in handling is need to prevent leaving a trail of old cursors onscreen
{
ensureCursorVisible();
} else {
m_delayedEnsureVisible = true;
}
updateActions();
updateSelectionHandler();
}
QVariant TextTool::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor || !m_textShapeData) {
return QVariant();
}
switch (query) {
case Qt::ImMicroFocus: {
// The rectangle covering the area of the input cursor in widget coordinates.
QRectF rect = caretRect(textEditor->cursor());
rect.moveTop(rect.top() - m_textShapeData->documentOffset());
QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter);
qreal zoomX, zoomY;
converter.zoom(&zoomX, &zoomY);
shapeMatrix.scale(zoomX, zoomY);
rect = shapeMatrix.mapRect(rect);
return rect.toRect();
}
case Qt::ImFont:
// The currently used font for text input.
return textEditor->charFormat().font();
case Qt::ImCursorPosition:
// The logical position of the cursor within the text surrounding the input area (see ImSurroundingText).
return textEditor->position() - textEditor->block().position();
case Qt::ImSurroundingText:
// The plain text around the input area, for example the current paragraph.
return textEditor->block().text();
case Qt::ImCurrentSelection:
// The currently selected text.
return textEditor->selectedText();
default:
; // Qt 4.6 adds ImMaximumTextLength and ImAnchorPosition
}
return QVariant();
}
void TextTool::inputMethodEvent(QInputMethodEvent *event)
{
KoTextEditor *textEditor = m_textEditor.data();
if (textEditor == 0) {
return;
}
if (event->replacementLength() > 0) {
textEditor->setPosition(textEditor->position() + event->replacementStart());
for (int i = event->replacementLength(); i > 0; --i) {
textEditor->deleteChar();
}
}
if (!event->commitString().isEmpty()) {
QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString());
keyPressEvent(&ke);
// The cursor may reside in a different block before vs. after keyPressEvent.
QTextBlock block = textEditor->block();
QTextLayout *layout = block.layout();
Q_ASSERT(layout);
layout->setPreeditArea(-1, QString());
} else {
QTextBlock block = textEditor->block();
QTextLayout *layout = block.layout();
Q_ASSERT(layout);
layout->setPreeditArea(textEditor->position() - block.position(), event->preeditString());
const_cast<QTextDocument *>(textEditor->document())->markContentsDirty(textEditor->position(), event->preeditString().length());
}
event->accept();
}
void TextTool::ensureCursorVisible(bool moveView)
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor || !m_textShapeData) {
return;
}
bool upToDate;
QRectF cRect = caretRect(textEditor->cursor(), &upToDate);
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout *>(m_textShapeData->document()->documentLayout());
Q_ASSERT(lay);
KoTextLayoutRootArea *rootArea = lay->rootAreaForPoint(cRect.center());
if (rootArea && rootArea->associatedShape() && m_textShapeData->rootArea() != rootArea) {
// If we have changed root area we need to update m_textShape and m_textShapeData
m_textShape = static_cast<TextShape *>(rootArea->associatedShape());
Q_ASSERT(m_textShape);
disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
m_textShapeData = static_cast<KoTextShapeData *>(m_textShape->userData());
Q_ASSERT(m_textShapeData);
connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
}
if (!moveView) {
return;
}
if (!upToDate) { // paragraph is not yet layouted.
// The number one usecase for this is when the user pressed enter.
// try to do it on next caret blink
m_delayedEnsureVisible = true;
return; // we shouldn't move to an obsolete position
}
cRect.moveTop(cRect.top() - m_textShapeData->documentOffset());
canvas()->ensureVisible(m_textShape->absoluteTransformation(0).mapRect(cRect));
}
void TextTool::keyReleaseEvent(QKeyEvent *event)
{
event->accept();
}
void TextTool::updateActions()
{
bool notInAnnotation = !dynamic_cast<AnnotationTextShape *>(m_textShape);
KoTextEditor *textEditor = m_textEditor.data();
if (textEditor == 0) {
return;
}
m_allowActions = false;
//Update the characterStyle related GUI elements
QTextCharFormat cf = textEditor->charFormat();
m_actionFormatBold->setChecked(cf.fontWeight() > QFont::Normal);
m_actionFormatItalic->setChecked(cf.fontItalic());
m_actionFormatUnderline->setChecked(cf.intProperty(KoCharacterStyle::UnderlineType) != KoCharacterStyle::NoLineType);
m_actionFormatStrikeOut->setChecked(cf.intProperty(KoCharacterStyle::StrikeOutType) != KoCharacterStyle::NoLineType);
bool super = false, sub = false;
switch (cf.verticalAlignment()) {
case QTextCharFormat::AlignSuperScript:
super = true;
break;
case QTextCharFormat::AlignSubScript:
sub = true;
break;
default:;
}
m_actionFormatSuper->setChecked(super);
m_actionFormatSub->setChecked(sub);
m_actionFormatFontSize->setFontSize(cf.font().pointSizeF());
m_actionFormatFontFamily->setFont(cf.font().family());
KoTextShapeData::ResizeMethod resizemethod = KoTextShapeData::AutoResize;
if (m_textShapeData) {
resizemethod = m_textShapeData->resizeMethod();
}
m_shrinkToFitAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
m_shrinkToFitAction->setChecked(resizemethod == KoTextShapeData::ShrinkToFitResize);
m_growWidthAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
m_growWidthAction->setChecked(resizemethod == KoTextShapeData::AutoGrowWidth || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight);
m_growHeightAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
m_growHeightAction->setChecked(resizemethod == KoTextShapeData::AutoGrowHeight || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight);
//update paragraphStyle GUI element
QTextBlockFormat bf = textEditor->blockFormat();
if (bf.hasProperty(KoParagraphStyle::TextProgressionDirection)) {
switch (bf.intProperty(KoParagraphStyle::TextProgressionDirection)) {
case KoText::RightLeftTopBottom:
m_actionChangeDirection->setChecked(true);
break;
case KoText::LeftRightTopBottom:
default:
m_actionChangeDirection->setChecked(false);
break;
}
} else {
m_actionChangeDirection->setChecked(textEditor->block().text().isRightToLeft());
}
if (bf.alignment() == Qt::AlignLeading || bf.alignment() == Qt::AlignTrailing) {
bool revert = (textEditor->block().layout()->textOption().textDirection() == Qt::RightToLeft);
if ((bf.alignment() == Qt::AlignLeading) ^ revert) {
m_actionAlignLeft->setChecked(true);
} else {
m_actionAlignRight->setChecked(true);
}
} else if (bf.alignment() == Qt::AlignHCenter) {
m_actionAlignCenter->setChecked(true);
}
if (bf.alignment() == Qt::AlignJustify) {
m_actionAlignBlock->setChecked(true);
} else if (bf.alignment() == (Qt::AlignLeft | Qt::AlignAbsolute)) {
m_actionAlignLeft->setChecked(true);
} else if (bf.alignment() == (Qt::AlignRight | Qt::AlignAbsolute)) {
m_actionAlignRight->setChecked(true);
}
if (textEditor->block().textList()) {
QTextListFormat listFormat = textEditor->block().textList()->format();
if (listFormat.intProperty(KoListStyle::Level) > 1) {
m_actionFormatDecreaseIndent->setEnabled(true);
} else {
m_actionFormatDecreaseIndent->setEnabled(false);
}
if (listFormat.intProperty(KoListStyle::Level) < 10) {
m_actionFormatIncreaseIndent->setEnabled(true);
} else {
m_actionFormatIncreaseIndent->setEnabled(false);
}
} else {
m_actionFormatDecreaseIndent->setEnabled(textEditor->blockFormat().leftMargin() > 0.);
}
m_allowActions = true;
bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
& KoCanvasResourceManager::NoAdvancedText);
if (useAdvancedText) {
action("insert_table")->setEnabled(notInAnnotation);
bool hasTable = textEditor->currentTable();
action("insert_tablerow_above")->setEnabled(hasTable && notInAnnotation);
action("insert_tablerow_below")->setEnabled(hasTable && notInAnnotation);
action("insert_tablecolumn_left")->setEnabled(hasTable && notInAnnotation);
action("insert_tablecolumn_right")->setEnabled(hasTable && notInAnnotation);
action("delete_tablerow")->setEnabled(hasTable && notInAnnotation);
action("delete_tablecolumn")->setEnabled(hasTable && notInAnnotation);
action("merge_tablecells")->setEnabled(hasTable && notInAnnotation);
action("split_tablecells")->setEnabled(hasTable && notInAnnotation);
action("activate_borderpainter")->setEnabled(hasTable && notInAnnotation);
}
action("insert_annotation")->setEnabled(notInAnnotation);
///TODO if selection contains several different format
emit blockChanged(textEditor->block());
emit charFormatChanged(cf, textEditor->blockCharFormat());
emit blockFormatChanged(bf);
}
void TextTool::updateStyleManager()
{
if (!m_textShapeData) {
return;
}
KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager();
emit styleManagerChanged(styleManager);
//TODO move this to its own method
m_changeTracker = KoTextDocument(m_textShapeData->document()).changeTracker();
}
void TextTool::activate(ToolActivation toolActivation, const QSet<KoShape *> &shapes)
{
Q_UNUSED(toolActivation);
m_caretTimer.start();
m_caretTimerState = true;
foreach (KoShape *shape, shapes) {
m_textShape = dynamic_cast<TextShape *>(shape);
if (m_textShape) {
break;
}
}
if (!m_textShape) { // none found
emit done();
// This is how we inform the rulers of the active range
// No shape means no active range
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, QVariant(QRectF()));
return;
}
// This is how we inform the rulers of the active range
// For now we will not consider table cells, but just give the shape dimensions
QVariant v;
QRectF rect(QPoint(), m_textShape->size());
rect = m_textShape->absoluteTransformation(0).mapRect(rect);
v.setValue(rect);
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, v);
if ((!m_oldTextEditor.isNull()) && m_oldTextEditor.data()->document() != static_cast<KoTextShapeData *>(m_textShape->userData())->document()) {
m_oldTextEditor.data()->setPosition(m_oldTextEditor.data()->position());
//we need to redraw like this so we update the old textshape whereever it may be
if (canvas()->canvasWidget()) {
canvas()->canvasWidget()->update();
}
}
setShapeData(static_cast<KoTextShapeData *>(m_textShape->userData()));
useCursor(Qt::IBeamCursor);
updateStyleManager();
repaintSelection();
updateSelectionHandler();
updateActions();
if (m_specialCharacterDocker) {
m_specialCharacterDocker->setEnabled(true);
}
}
void TextTool::deactivate()
{
m_caretTimer.stop();
m_caretTimerState = false;
repaintCaret();
m_textShape = 0;
// This is how we inform the rulers of the active range
// No shape means no active range
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, QVariant(QRectF()));
m_oldTextEditor = m_textEditor;
setShapeData(0);
updateSelectionHandler();
if (m_specialCharacterDocker) {
m_specialCharacterDocker->setEnabled(false);
m_specialCharacterDocker->setVisible(false);
}
}
void TextTool::repaintDecorations()
{
if (m_textShapeData) {
repaintSelection();
}
}
void TextTool::repaintCaret()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor || !m_textShapeData) {
return;
}
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout *>(m_textShapeData->document()->documentLayout());
Q_ASSERT(lay); Q_UNUSED(lay);
// If we have changed root area we need to update m_textShape and m_textShapeData
if (m_delayedEnsureVisible) {
m_delayedEnsureVisible = false;
ensureCursorVisible();
return;
}
ensureCursorVisible(false); // ensures the various vars are updated
bool upToDate;
QRectF repaintRect = caretRect(textEditor->cursor(), &upToDate);
repaintRect.moveTop(repaintRect.top() - m_textShapeData->documentOffset());
if (repaintRect.isValid()) {
repaintRect = m_textShape->absoluteTransformation(0).mapRect(repaintRect);
// Make sure there is enough space to show an icon
QRectF iconSize = canvas()->viewConverter()->viewToDocument(QRect(0, 0, 18, 18));
repaintRect.setX(repaintRect.x() - iconSize.width() / 2);
repaintRect.setRight(repaintRect.right() + iconSize.width() / 2);
repaintRect.setTop(repaintRect.y() - iconSize.height() / 2);
repaintRect.setBottom(repaintRect.bottom() + iconSize.height() / 2);
canvas()->updateCanvas(repaintRect);
}
}
void TextTool::repaintSelection()
{
KoTextEditor *textEditor = m_textEditor.data();
if (textEditor == 0) {
return;
}
QTextCursor cursor = *textEditor->cursor();
QList<TextShape *> shapes;
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout *>(textEditor->document()->documentLayout());
Q_ASSERT(lay);
foreach (KoShape *shape, lay->shapes()) {
TextShape *textShape = dynamic_cast<TextShape *>(shape);
if (textShape == 0) { // when the shape is being deleted its no longer a TextShape but a KoShape
continue;
}
//Q_ASSERT(!shapes.contains(textShape));
if (!shapes.contains(textShape)) {
shapes.append(textShape);
}
}
// loop over all shapes that contain the text and update per shape.
QRectF repaintRect = textRect(cursor);
foreach (TextShape *ts, shapes) {
QRectF rect = repaintRect;
rect.moveTop(rect.y() - ts->textShapeData()->documentOffset());
rect = ts->absoluteTransformation(0).mapRect(rect);
QRectF r = ts->boundingRect().intersected(rect);
canvas()->updateCanvas(r);
}
}
QRectF TextTool::caretRect(QTextCursor *cursor, bool *upToDate) const
{
QTextCursor tmpCursor(*cursor);
tmpCursor.setPosition(cursor->position()); // looses the anchor
QRectF rect = textRect(tmpCursor);
if (rect.size() == QSizeF(0, 0)) {
if (upToDate) {
*upToDate = false;
}
rect = m_lastImMicroFocus; // prevent block changed but layout not done
} else {
if (upToDate) {
*upToDate = true;
}
m_lastImMicroFocus = rect;
}
return rect;
}
QRectF TextTool::textRect(QTextCursor &cursor) const
{
if (!m_textShapeData) {
return QRectF();
}
KoTextEditor *textEditor = m_textEditor.data();
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout *>(textEditor->document()->documentLayout());
return lay->selectionBoundingBox(cursor);
}
KoToolSelection *TextTool::selection()
{
return m_toolSelection;
}
QList<QPointer<QWidget> > TextTool::createOptionWidgets()
{
QList<QPointer<QWidget> > widgets;
SimpleCharacterWidget *scw = new SimpleCharacterWidget(this, 0);
SimpleParagraphWidget *spw = new SimpleParagraphWidget(this, 0);
if (m_textEditor.data()) {
// connect(m_textEditor.data(), SIGNAL(paragraphStyleApplied(KoParagraphStyle*)), spw, SLOT(slotParagraphStyleApplied(KoParagraphStyle*)));
// connect(m_textEditor.data(), SIGNAL(characterStyleApplied(KoCharacterStyle*)), scw, SLOT(slotCharacterStyleApplied(KoCharacterStyle*)));
//initialise the char- and par- widgets with the current block and formats.
scw->setCurrentBlockFormat(m_textEditor.data()->blockFormat());
scw->setCurrentFormat(m_textEditor.data()->charFormat(), m_textEditor.data()-> blockCharFormat());
spw->setCurrentBlock(m_textEditor.data()->block());
spw->setCurrentFormat(m_textEditor.data()->blockFormat());
}
SimpleTableWidget *stw = new SimpleTableWidget(this, 0);
SimpleInsertWidget *siw = new SimpleInsertWidget(this, 0);
/* We do not use these for now. Let's see if they become useful at a certain point in time. If not, we can remove the whole chain (SimpleCharWidget, SimpleParWidget, DockerStyleComboModel)
if (m_textShapeData && KoTextDocument(m_textShapeData->document()).styleManager()) {
scw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedCharacterStyles());
spw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedParagraphStyles());
}
*/
// Connect to/with simple character widget (docker)
connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), scw, SLOT(setStyleManager(KoStyleManager*)));
connect(this, SIGNAL(charFormatChanged(QTextCharFormat,QTextCharFormat)), scw, SLOT(setCurrentFormat(QTextCharFormat,QTextCharFormat)));
connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), scw, SLOT(setCurrentBlockFormat(QTextBlockFormat)));
connect(scw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
connect(scw, SIGNAL(characterStyleSelected(KoCharacterStyle*)), this, SLOT(setStyle(KoCharacterStyle*)));
connect(scw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentCharFormat(QString)));
connect(scw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int)));
// Connect to/with simple paragraph widget (docker)
connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), spw, SLOT(setStyleManager(KoStyleManager*)));
connect(this, SIGNAL(blockChanged(QTextBlock)), spw, SLOT(setCurrentBlock(QTextBlock)));
connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), spw, SLOT(setCurrentFormat(QTextBlockFormat)));
connect(spw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
connect(spw, SIGNAL(paragraphStyleSelected(KoParagraphStyle*)), this, SLOT(setStyle(KoParagraphStyle*)));
connect(spw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentBlockFormat(QString)));
connect(spw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int)));
// Connect to/with simple table widget (docker)
connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), stw, SLOT(setStyleManager(KoStyleManager*)));
connect(stw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
connect(stw, SIGNAL(tableBorderDataUpdated(KoBorder::BorderData)), this, SLOT(setTableBorderData(KoBorder::BorderData)));
// Connect to/with simple insert widget (docker)
connect(siw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
connect(siw, SIGNAL(insertTableQuick(int,int)), this, SLOT(insertTableQuick(int,int)));
updateStyleManager();
if (m_textShape) {
updateActions();
}
scw->setWindowTitle(i18n("Character"));
widgets.append(scw);
spw->setWindowTitle(i18n("Paragraph"));
widgets.append(spw);
bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
& KoCanvasResourceManager::NoAdvancedText);
if (useAdvancedText) {
stw->setWindowTitle(i18n("Table"));
widgets.append(stw);
siw->setWindowTitle(i18n("Insert"));
widgets.append(siw);
}
return widgets;
}
void TextTool::returnFocusToCanvas()
{
canvas()->canvasWidget()->setFocus();
}
void TextTool::startEditing(KUndo2Command *command)
{
m_currentCommand = command;
m_currentCommandHasChildren = true;
}
void TextTool::stopEditing()
{
m_currentCommand = 0;
m_currentCommandHasChildren = false;
}
void TextTool::insertNewSection()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor) {
return;
}
textEditor->newSection();
}
void TextTool::configureSection()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor) {
return;
}
SectionFormatDialog *dia = new SectionFormatDialog(0, m_textEditor.data());
dia->exec();
delete dia;
returnFocusToCanvas();
updateActions();
}
void TextTool::splitSections()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor) {
return;
}
SectionsSplitDialog *dia = new SectionsSplitDialog(0, m_textEditor.data());
dia->exec();
delete dia;
returnFocusToCanvas();
updateActions();
}
void TextTool::pasteAsText()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor) {
return;
}
const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
// on windows we do not have data if we try to paste this selection
if (!data) {
return;
}
if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))
|| data->hasText()) {
m_prevCursorPosition = m_textEditor.data()->position();
m_textEditor.data()->paste(canvas(), data, true);
editingPluginEvents();
}
}
void TextTool::bold(bool bold)
{
m_textEditor.data()->bold(bold);
}
void TextTool::italic(bool italic)
{
m_textEditor.data()->italic(italic);
}
void TextTool::underline(bool underline)
{
m_textEditor.data()->underline(underline);
}
void TextTool::strikeOut(bool strikeOut)
{
m_textEditor.data()->strikeOut(strikeOut);
}
void TextTool::nonbreakingSpace()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->insertText(QString(QChar(Qt::Key_nobreakspace)));
}
void TextTool::nonbreakingHyphen()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->insertText(QString(QChar(0x2013)));
}
void TextTool::softHyphen()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->insertText(QString(QChar(Qt::Key_hyphen)));
}
void TextTool::lineBreak()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->insertText(QString(QChar(0x2028)));
}
void TextTool::alignLeft()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignLeft | Qt::AlignAbsolute);
}
void TextTool::alignRight()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignRight | Qt::AlignAbsolute);
}
void TextTool::alignCenter()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignHCenter);
}
void TextTool::alignBlock()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignJustify);
}
void TextTool::superScript(bool on)
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
if (on) {
m_actionFormatSub->setChecked(false);
}
m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignTop : Qt::AlignVCenter);
}
void TextTool::subScript(bool on)
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
if (on) {
m_actionFormatSuper->setChecked(false);
}
m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignBottom : Qt::AlignVCenter);
}
void TextTool::increaseIndent()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
if (m_textEditor.data()->block().textList()) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1);
m_textEditor.data()->addCommand(cll);
editingPluginEvents();
} else {
m_textEditor.data()->increaseIndent();
}
updateActions();
}
void TextTool::decreaseIndent()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
if (m_textEditor.data()->block().textList()) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1);
m_textEditor.data()->addCommand(cll);
editingPluginEvents();
} else {
m_textEditor.data()->decreaseIndent();
}
updateActions();
}
void TextTool::decreaseFontSize()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->decreaseFontSize();
}
void TextTool::increaseFontSize()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->increaseFontSize();
}
void TextTool::setFontFamily(const QString &font)
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setFontFamily(font);
}
void TextTool::setFontSize(qreal size)
{
if (!m_allowActions || !m_textEditor.data() || m_textEditor.isNull()) {
return;
}
m_textEditor.data()->setFontSize(size);
}
void TextTool::insertIndexMarker()
{
// TODO handle result when we figure out how to report errors from a tool.
m_textEditor.data()->insertIndexMarker();
}
void TextTool::insertFrameBreak()
{
m_textEditor.data()->insertFrameBreak();
ensureCursorVisible();
m_delayedEnsureVisible = true;
}
void TextTool::setStyle(KoCharacterStyle *style)
{
KoCharacterStyle *charStyle = style;
//if the given KoCharacterStyle is null, set the KoParagraphStyle character properties
if (!charStyle) {
charStyle = static_cast<KoCharacterStyle *>(KoTextDocument(m_textShapeData->document()).styleManager()->paragraphStyle(m_textEditor.data()->blockFormat().intProperty(KoParagraphStyle::StyleId)));
}
if (charStyle) {
m_textEditor.data()->setStyle(charStyle);
updateActions();
}
}
void TextTool::setStyle(KoParagraphStyle *style)
{
m_textEditor.data()->setStyle(style);
updateActions();
}
void TextTool::insertTable()
{
TableDialog *dia = new TableDialog(0);
if (dia->exec() == TableDialog::Accepted) {
m_textEditor.data()->insertTable(dia->rows(), dia->columns());
}
delete dia;
updateActions();
}
void TextTool::insertTableQuick(int rows, int columns)
{
m_textEditor.data()->insertTable(rows, columns);
updateActions();
}
void TextTool::insertTableRowAbove()
{
m_textEditor.data()->insertTableRowAbove();
}
void TextTool::insertTableRowBelow()
{
m_textEditor.data()->insertTableRowBelow();
}
void TextTool::insertTableColumnLeft()
{
m_textEditor.data()->insertTableColumnLeft();
}
void TextTool::insertTableColumnRight()
{
m_textEditor.data()->insertTableColumnRight();
}
void TextTool::deleteTableColumn()
{
m_textEditor.data()->deleteTableColumn();
}
void TextTool::deleteTableRow()
{
m_textEditor.data()->deleteTableRow();
}
void TextTool::mergeTableCells()
{
m_textEditor.data()->mergeTableCells();
}
void TextTool::splitTableCells()
{
m_textEditor.data()->splitTableCells();
}
void TextTool::useTableBorderCursor()
{
static const unsigned char data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0xfd, 0x00,
0x00, 0x80, 0x7e, 0x00, 0x00, 0x40, 0x3f, 0x00, 0x00, 0xa0, 0x1f, 0x00,
0x00, 0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x03, 0x00,
0x00, 0xe4, 0x01, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00,
0x40, 0x32, 0x00, 0x00, 0xa0, 0x0f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00,
0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00,
0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
QBitmap result(32, 32);
result.fill(Qt::color0);
QPainter painter(&result);
painter.drawPixmap(0, 0, QBitmap::fromData(QSize(25, 23), data));
QBitmap brushMask = result.createHeuristicMask(false);
useCursor(QCursor(result, brushMask, 1, 21));
}
void TextTool::setTableBorderData(const KoBorder::BorderData &data)
{
m_tablePenMode = true;
m_tablePenBorderData = data;
}
void TextTool::formatParagraph()
{
ParagraphSettingsDialog *dia = new ParagraphSettingsDialog(this, m_textEditor.data());
dia->setUnit(canvas()->unit());
dia->setImageCollection(m_textShape->imageCollection());
dia->exec();
delete dia;
returnFocusToCanvas();
}
void TextTool::testSlot(bool on)
{
qDebug() << "signal received. bool:" << on;
}
void TextTool::selectAll()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor || !m_textShapeData) {
return;
}
const int selectionLength = qAbs(textEditor->position() - textEditor->anchor());
textEditor->movePosition(QTextCursor::End);
textEditor->setPosition(0, QTextCursor::KeepAnchor);
repaintSelection();
if (selectionLength != qAbs(textEditor->position() - textEditor->anchor())) { // it actually changed
emit selectionChanged(true);
}
}
void TextTool::startMacro(const QString &title)
{
if (title != i18n("Key Press") && title != i18n("Autocorrection")) { //dirty hack while waiting for refactor of text editing
m_textTyping = false;
} else {
m_textTyping = true;
}
if (title != i18n("Delete") && title != i18n("Autocorrection")) { //same dirty hack as above
m_textDeleting = false;
} else {
m_textDeleting = true;
}
if (m_currentCommand) {
return;
}
class MacroCommand : public KUndo2Command
{
public:
MacroCommand(const KUndo2MagicString &title) : KUndo2Command(title), m_first(true) {}
void redo() override
{
if (!m_first) {
KUndo2Command::redo();
}
m_first = false;
}
bool mergeWith(const KUndo2Command *) override
{
return false;
}
bool m_first;
};
/**
* FIXME: The messages genearted by the Text Tool might not be
* properly translated, since we don't control it in
* type-safe way.
*
* The title is already translated string, we just don't
* have any type control over it.
*/
KUndo2MagicString title_workaround = kundo2_noi18n(title);
m_currentCommand = new MacroCommand(title_workaround);
m_currentCommandHasChildren = false;
}
void TextTool::stopMacro()
{
if (!m_currentCommand) {
return;
}
if (!m_currentCommandHasChildren) {
delete m_currentCommand;
}
m_currentCommand = 0;
}
void TextTool::showStyleManager(int styleId)
{
if (!m_textShapeData) {
return;
}
KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager();
Q_ASSERT(styleManager);
if (!styleManager) {
return; //don't crash
}
StyleManagerDialog *dia = new StyleManagerDialog(canvas()->canvasWidget());
dia->setStyleManager(styleManager);
dia->setUnit(canvas()->unit());
KoParagraphStyle *paragraphStyle = styleManager->paragraphStyle(styleId);
if (paragraphStyle) {
dia->setParagraphStyle(paragraphStyle);
}
KoCharacterStyle *characterStyle = styleManager->characterStyle(styleId);
if (characterStyle) {
dia->setCharacterStyle(characterStyle);
}
dia->show();
}
void TextTool::startTextEditingPlugin(const QString &pluginId)
{
KoTextEditingPlugin *plugin = textEditingPluginContainer()->plugin(pluginId);
if (plugin) {
if (m_textEditor.data()->hasSelection()) {
plugin->checkSection(m_textShapeData->document(), m_textEditor.data()->selectionStart(), m_textEditor.data()->selectionEnd());
} else {
plugin->finishedWord(m_textShapeData->document(), m_textEditor.data()->position());
}
}
}
void TextTool::canvasResourceChanged(int key, const QVariant &var)
{
if (m_textEditor.isNull()) {
return;
}
if (!m_textShapeData) {
return;
}
if (m_allowResourceManagerUpdates == false) {
return;
}
if (key == KoText::CurrentTextPosition) {
repaintSelection();
m_textEditor.data()->setPosition(var.toInt());
ensureCursorVisible();
} else if (key == KoText::CurrentTextAnchor) {
repaintSelection();
int pos = m_textEditor.data()->position();
m_textEditor.data()->setPosition(var.toInt());
m_textEditor.data()->setPosition(pos, QTextCursor::KeepAnchor);
} else if (key == KoCanvasResourceManager::Unit) {
m_unit = var.value<KoUnit>();
} else {
return;
}
repaintSelection();
}
void TextTool::insertSpecialCharacter()
{
if (m_specialCharacterDocker == 0) {
m_specialCharacterDocker = new InsertCharacter(canvas()->canvasWidget());
connect(m_specialCharacterDocker, SIGNAL(insertCharacter(QString)),
this, SLOT(insertString(QString)));
}
m_specialCharacterDocker->show();
}
void TextTool::insertString(const QString &string)
{
m_textEditor.data()->insertText(string);
returnFocusToCanvas();
}
void TextTool::selectFont()
{
FontDia *fontDlg = new FontDia(m_textEditor.data());
fontDlg->exec();
delete fontDlg;
returnFocusToCanvas();
}
void TextTool::shapeAddedToCanvas()
{
qDebug();
if (m_textShape) {
KoSelection *selection = canvas()->shapeManager()->selection();
KoShape *shape = selection->firstSelectedShape();
if (shape != m_textShape && canvas()->shapeManager()->shapes().contains(m_textShape)) {
// this situation applies when someone, not us, changed the selection by selecting another
// text shape. Possibly by adding one.
// Deselect the new shape again, so we can keep editing what we were already editing
selection->select(m_textShape);
selection->deselect(shape);
}
}
}
void TextTool::shapeDataRemoved()
{
m_textShapeData = 0;
m_textShape = 0;
if (!m_textEditor.isNull() && !m_textEditor.data()->cursor()->isNull()) {
const QTextDocument *doc = m_textEditor.data()->document();
Q_ASSERT(doc);
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout *>(doc->documentLayout());
if (!lay || lay->shapes().isEmpty()) {
emit done();
return;
}
m_textShape = static_cast<TextShape *>(lay->shapes().first());
m_textShapeData = static_cast<KoTextShapeData *>(m_textShape->userData());
connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
}
}
void TextTool::createStyleFromCurrentBlockFormat(const QString &name)
{
KoTextDocument document(m_textShapeData->document());
KoStyleManager *styleManager = document.styleManager();
KoParagraphStyle *paragraphStyle = new KoParagraphStyle(m_textEditor.data()->blockFormat(), m_textEditor.data()->charFormat());
paragraphStyle->setName(name);
styleManager->add(paragraphStyle);
m_textEditor.data()->setStyle(paragraphStyle);
emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
emit blockFormatChanged(m_textEditor.data()->blockFormat());
}
void TextTool::createStyleFromCurrentCharFormat(const QString &name)
{
KoTextDocument document(m_textShapeData->document());
KoStyleManager *styleManager = document.styleManager();
KoCharacterStyle *originalCharStyle = styleManager->characterStyle(m_textEditor.data()->charFormat().intProperty(KoCharacterStyle::StyleId));
KoCharacterStyle *autoStyle;
if (!originalCharStyle) {
KoCharacterStyle blankStyle;
originalCharStyle = &blankStyle;
autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
autoStyle->setParentStyle(0);
} else {
autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
}
autoStyle->setName(name);
styleManager->add(autoStyle);
m_textEditor.data()->setStyle(autoStyle);
emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
}
// ---------- editing plugins methods.
void TextTool::editingPluginEvents()
{
if (m_prevCursorPosition == -1 || m_prevCursorPosition == m_textEditor.data()->position()) {
qDebug() << "m_prevCursorPosition=" << m_prevCursorPosition << "m_textEditor.data()->position()=" << m_textEditor.data()->position();
return;
}
QTextBlock block = m_textEditor.data()->block();
if (!block.contains(m_prevCursorPosition)) {
qDebug() << "m_prevCursorPosition=" << m_prevCursorPosition;
finishedWord();
finishedParagraph();
m_prevCursorPosition = -1;
} else {
int from = m_prevCursorPosition;
int to = m_textEditor.data()->position();
if (from > to) {
qSwap(from, to);
}
QString section = block.text().mid(from - block.position(), to - from);
qDebug() << "from=" << from << "to=" << to;
if (section.contains(' ')) {
finishedWord();
m_prevCursorPosition = -1;
}
}
}
void TextTool::finishedWord()
{
if (m_textShapeData && textEditingPluginContainer()) {
foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) {
plugin->finishedWord(m_textShapeData->document(), m_prevCursorPosition);
}
}
}
void TextTool::finishedParagraph()
{
if (m_textShapeData && textEditingPluginContainer()) {
foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) {
plugin->finishedParagraph(m_textShapeData->document(), m_prevCursorPosition);
}
}
}
void TextTool::startingSimpleEdit()
{
if (m_textShapeData && textEditingPluginContainer()) {
foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) {
plugin->startingSimpleEdit(m_textShapeData->document(), m_prevCursorPosition);
}
}
}
void TextTool::setTextColor(const KoColor &color)
{
m_textEditor.data()->setTextColor(color.toQColor());
}
void TextTool::setBackgroundColor(const KoColor &color)
{
m_textEditor.data()->setTextBackgroundColor(color.toQColor());
}
void TextTool::setGrowWidthToFit(bool enabled)
{
m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowWidth, enabled));
updateActions();
}
void TextTool::setGrowHeightToFit(bool enabled)
{
m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowHeight, enabled));
updateActions();
}
void TextTool::setShrinkToFit(bool enabled)
{
m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::ShrinkToFitResize, enabled));
updateActions();
}
void TextTool::runUrl(KoPointerEvent *event, QString &url)
{
QUrl _url = QUrl::fromUserInput(url);
if (!_url.isLocalFile()) {
QDesktopServices::openUrl(_url);
}
event->accept();
}
void TextTool::debugTextDocument()
{
#ifndef NDEBUG
if (!m_textShapeData) {
return;
}
const int CHARSPERLINE = 80; // TODO Make configurable using ENV var?
const int CHARPOSITION = 278301935;
KoTextDocument document(m_textShapeData->document());
KoStyleManager *styleManager = document.styleManager();
KoInlineTextObjectManager *inlineManager = document.inlineTextObjectManager();
QTextBlock block = m_textShapeData->document()->begin();
for (; block.isValid(); block = block.next()) {
QVariant var = block.blockFormat().property(KoParagraphStyle::StyleId);
if (!var.isNull()) {
KoParagraphStyle *ps = styleManager->paragraphStyle(var.toInt());
qDebug() << "--- Paragraph Style:" << (ps ? ps->name() : QString()) << var.toInt();
}
var = block.charFormat().property(KoCharacterStyle::StyleId);
if (!var.isNull()) {
KoCharacterStyle *cs = styleManager->characterStyle(var.toInt());
qDebug() << "--- Character Style:" << (cs ? cs->name() : QString()) << var.toInt();
}
int lastPrintedChar = -1;
QTextBlock::iterator it;
QString fragmentText;
QList<QTextCharFormat> inlineCharacters;
for (it = block.begin(); !it.atEnd(); ++it) {
QTextFragment fragment = it.fragment();
if (!fragment.isValid()) {
continue;
}
QTextCharFormat fmt = fragment.charFormat();
qDebug() << "changeId: " << fmt.property(KoCharacterStyle::ChangeTrackerId);
const int fragmentStart = fragment.position() - block.position();
for (int i = fragmentStart; i < fragmentStart + fragment.length(); i += CHARSPERLINE) {
if (lastPrintedChar == fragmentStart - 1) {
fragmentText += '|';
}
if (lastPrintedChar < fragmentStart || i > fragmentStart) {
QString debug = block.text().mid(lastPrintedChar, CHARSPERLINE);
lastPrintedChar += CHARSPERLINE;
if (lastPrintedChar > block.length()) {
debug += "\\n";
}
qDebug() << debug;
}
var = fmt.property(KoCharacterStyle::StyleId);
QString charStyleLong, charStyleShort;
if (!var.isNull()) { // named style
charStyleShort = QString::number(var.toInt());
KoCharacterStyle *cs = styleManager->characterStyle(var.toInt());
if (cs) {
charStyleLong = cs->name();
}
}
if (inlineManager && fmt.hasProperty(KoCharacterStyle::InlineInstanceId)) {
QTextCharFormat inlineFmt = fmt;
inlineFmt.setProperty(CHARPOSITION, fragmentStart);
inlineCharacters << inlineFmt;
}
if (fragment.length() > charStyleLong.length()) {
fragmentText += charStyleLong;
} else if (fragment.length() > charStyleShort.length()) {
fragmentText += charStyleShort;
} else if (fragment.length() >= 2) {
fragmentText += QChar(8230); // elipses
}
int rest = fragmentStart - (lastPrintedChar - CHARSPERLINE) + fragment.length() - fragmentText.length();
rest = qMin(rest, CHARSPERLINE - fragmentText.length());
if (rest >= 2) {
fragmentText = QString("%1%2").arg(fragmentText).arg(' ', rest);
}
if (rest >= 0) {
fragmentText += '|';
}
if (fragmentText.length() >= CHARSPERLINE) {
qDebug() << fragmentText;
fragmentText.clear();
}
}
}
if (!fragmentText.isEmpty()) {
qDebug() << fragmentText;
} else if (block.length() == 1) { // no actual tet
qDebug() << "\\n";
}
foreach (const QTextCharFormat &cf, inlineCharacters) {
KoInlineObject *object = inlineManager->inlineTextObject(cf);
qDebug() << "At pos:" << cf.intProperty(CHARPOSITION) << object;
// qDebug() << "-> id:" << cf.intProperty(577297549);
}
QTextList *list = block.textList();
if (list) {
if (list->format().hasProperty(KoListStyle::StyleId)) {
KoListStyle *ls = styleManager->listStyle(list->format().intProperty(KoListStyle::StyleId));
qDebug() << " List style applied:" << ls->styleId() << ls->name();
} else {
qDebug() << " +- is a list..." << list;
}
}
}
#endif
}
void TextTool::debugTextStyles()
{
#ifndef NDEBUG
if (!m_textShapeData) {
return;
}
KoTextDocument document(m_textShapeData->document());
KoStyleManager *styleManager = document.styleManager();
QSet<int> seenStyles;
foreach (KoParagraphStyle *style, styleManager->paragraphStyles()) {
qDebug() << style->styleId() << style->name() << (styleManager->defaultParagraphStyle() == style ? "[Default]" : "");
KoListStyle *ls = style->listStyle();
if (ls) { // optional ;)
qDebug() << " +- ListStyle: " << ls->styleId() << ls->name()
<< (ls == styleManager->defaultListStyle() ? "[Default]" : "");
foreach (int level, ls->listLevels()) {
KoListLevelProperties llp = ls->levelProperties(level);
qDebug() << " | level" << llp.level() << " style (enum):" << llp.style();
if (llp.bulletCharacter().unicode() != 0) {
qDebug() << " | bullet" << llp.bulletCharacter();
}
}
seenStyles << ls->styleId();
}
}
bool first = true;
foreach (KoCharacterStyle *style, styleManager->characterStyles()) {
if (seenStyles.contains(style->styleId())) {
continue;
}
if (first) {
qDebug() << "--- Character styles ---";
first = false;
}
qDebug() << style->styleId() << style->name();
qDebug() << style->font();
}
first = true;
foreach (KoListStyle *style, styleManager->listStyles()) {
if (seenStyles.contains(style->styleId())) {
continue;
}
if (first) {
qDebug() << "--- List styles ---";
first = false;
}
qDebug() << style->styleId() << style->name()
<< (style == styleManager->defaultListStyle() ? "[Default]" : "");
}
#endif
}
void TextTool::textDirectionChanged()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
QTextBlockFormat blockFormat;
if (m_actionChangeDirection->isChecked()) {
blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::RightLeftTopBottom);
} else {
blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::LeftRightTopBottom);
}
m_textEditor.data()->mergeBlockFormat(blockFormat);
}
void TextTool::setListLevel(int level)
{
if (level < 1 || level > 10) {
return;
}
KoTextEditor *textEditor = m_textEditor.data();
if (textEditor->block().textList()) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::SetLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, level);
textEditor->addCommand(cll);
editingPluginEvents();
}
}
void TextTool::insertAnnotation()
{
AnnotationTextShape *shape = (AnnotationTextShape *)KoShapeRegistry::instance()->value(AnnotationShape_SHAPEID)->createDefaultShape(canvas()->shapeController()->resourceManager());
textEditor()->addAnnotation(shape);
// Set annotation creator.
KConfig cfg("kritarc");
cfg.reparseConfiguration();
KConfigGroup authorGroup(&cfg, "Author");
QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
KSharedConfig::openConfig()->reparseConfiguration();
KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
QString profile = appAuthorGroup.readEntry("active-profile", "");
KConfigGroup cgs(&authorGroup, "Author-" + profile);
if (profiles.contains(profile)) {
KConfigGroup cgs(&authorGroup, "Author-" + profile);
shape->setCreator(cgs.readEntry("creator"));
} else {
if (profile == "anonymous") {
shape->setCreator("Anonymous");
} else {
KUser user(KUser::UseRealUserID);
shape->setCreator(user.property(KUser::FullName).toString());
}
}
// Set Annotation creation date.
shape->setDate(QDate::currentDate().toString(Qt::ISODate));
}
diff --git a/plugins/impex/CMakeLists.txt b/plugins/impex/CMakeLists.txt
index 7571851366..34f7156aac 100644
--- a/plugins/impex/CMakeLists.txt
+++ b/plugins/impex/CMakeLists.txt
@@ -1,43 +1,46 @@
project(kritafilters)
+add_subdirectory(libkra)
+
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
add_definitions( -DCPU_32_BITS )
endif()
if(JPEG_FOUND AND HAVE_LCMS2)
add_subdirectory(jpeg)
endif()
if(TIFF_FOUND)
add_subdirectory(tiff)
endif()
if(PNG_FOUND)
add_subdirectory(png)
add_subdirectory(csv)
endif()
if(OPENEXR_FOUND)
add_subdirectory(exr)
endif()
if(POPPLER_FOUND)
add_subdirectory(pdf)
endif()
if(LIBRAW_FOUND)
add_subdirectory(raw)
endif()
add_subdirectory(bmp)
add_subdirectory(ora)
add_subdirectory(ppm)
add_subdirectory(xcf)
add_subdirectory(psd)
add_subdirectory(odg)
add_subdirectory(qml)
add_subdirectory(tga)
add_subdirectory(heightmap)
add_subdirectory(brush)
add_subdirectory(spriter)
add_subdirectory(video)
+add_subdirectory(kra)
diff --git a/plugins/impex/bmp/CMakeLists.txt b/plugins/impex/bmp/CMakeLists.txt
index fc613c4fb7..02ec25b984 100644
--- a/plugins/impex/bmp/CMakeLists.txt
+++ b/plugins/impex/bmp/CMakeLists.txt
@@ -1,25 +1,25 @@
set(kritabmpexport_SOURCES
kis_bmp_export.cpp
)
ki18n_wrap_ui(kritabmpexport_SOURCES )
add_library(kritabmpexport MODULE ${kritabmpexport_SOURCES})
-target_link_libraries(kritabmpexport kritaui)
+target_link_libraries(kritabmpexport kritaui kritaimpex)
install(TARGETS kritabmpexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritabmpimport_SOURCES
kis_bmp_import.cpp
)
ki18n_wrap_ui(kritabmpimport_SOURCES )
add_library(kritabmpimport MODULE ${kritabmpimport_SOURCES})
target_link_libraries(kritabmpimport kritaui)
install(TARGETS kritabmpimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_bmp.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/bmp/kis_bmp_export.cpp b/plugins/impex/bmp/kis_bmp_export.cpp
index a449f92489..a8b5761372 100644
--- a/plugins/impex/bmp/kis_bmp_export.cpp
+++ b/plugins/impex/bmp/kis_bmp_export.cpp
@@ -1,72 +1,65 @@
/*
* 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_bmp_export.h"
#include <QCheckBox>
#include <QSlider>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <QApplication>
-#include <KisFilterChain.h>
-
+#include <KisMimeDatabase.h>
+#include <KisExportCheckRegistry.h>
#include <kis_paint_device.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
K_PLUGIN_FACTORY_WITH_JSON(KisBMPExportFactory, "krita_bmp_export.json", registerPlugin<KisBMPExport>();)
KisBMPExport::KisBMPExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisBMPExport::~KisBMPExport()
{
}
-KisImportExportFilter::ConversionStatus KisBMPExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisBMPExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
-
- dbgFile << "BMP export! From:" << from << ", To:" << to << "";
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
+ QRect rc = document->image()->bounds();
+ QImage image = document->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
+ image.save(io, QFileInfo(filename()).suffix().toLatin1());
+ return KisImportExportFilter::OK;
+}
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
+void KisBMPExport::initializeCapabilities()
+{
- QRect rc = input->image()->bounds();
- // the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(input->image()->locked());
- QImage image = input->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
- image.save(filename);
- return KisImportExportFilter::OK;
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, KisMimeDatabase::descriptionForMimeType(mimeType()));
+ addCapability(KisExportCheckRegistry::instance()->get("ColorModelPerLayerCheck/" + RGBAColorModelID.id() + "/" + Integer8BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED));
}
#include "kis_bmp_export.moc"
diff --git a/plugins/impex/bmp/kis_bmp_export.h b/plugins/impex/bmp/kis_bmp_export.h
index 9cc799fae1..62f7151e22 100644
--- a/plugins/impex/bmp/kis_bmp_export.h
+++ b/plugins/impex/bmp/kis_bmp_export.h
@@ -1,37 +1,38 @@
/*
* 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 _KIS_BMP_EXPORT_H_
#define _KIS_BMP_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisBMPExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisBMPExport(QObject *parent, const QVariantList &);
virtual ~KisBMPExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/bmp/kis_bmp_import.cpp b/plugins/impex/bmp/kis_bmp_import.cpp
index baefa400ee..a675f67dd5 100644
--- a/plugins/impex/bmp/kis_bmp_import.cpp
+++ b/plugins/impex/bmp/kis_bmp_import.cpp
@@ -1,95 +1,77 @@
/*
* 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_bmp_import.h"
#include <QCheckBox>
#include <QSlider>
#include <QApplication>
#include <QFileInfo>
+#include <QImageReader>
#include <kpluginfactory.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>
K_PLUGIN_FACTORY_WITH_JSON(KisBMPImportFactory, "krita_bmp_import.json", registerPlugin<KisBMPImport>();)
KisBMPImport::KisBMPImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisBMPImport::~KisBMPImport()
{
}
-KisImportExportFilter::ConversionStatus KisBMPImport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisBMPImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
-
- dbgFile << "BMP import! From:" << from << ", To:" << to << 0;
-
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- doc->prepareForImport();
-
- if (!filename.isEmpty()) {
-
- QFileInfo fi(filename);
- if (!fi.exists()) {
- return KisImportExportFilter::FileNotFound;
- }
+ QFileInfo fi(filename());
+ if (!QImageReader::supportedImageFormats().contains(fi.suffix().toLatin1())) {
+ return KisImportExportFilter::InvalidFormat;
+ }
- QImage img(filename);
+ QImage img;
+ if (!img.loadFromData(io->readAll(), fi.suffix().toLatin1())) {
+ return KisImportExportFilter::InvalidFormat;
+ }
- const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
- KisImageSP image = new KisImage(doc->createUndoStore(), img.width(), img.height(), colorSpace, "imported from bmp");
+ const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
+ KisImageSP image = new KisImage(document->createUndoStore(), img.width(), img.height(), colorSpace, i18n("Imported Image"));
- KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255);
- layer->paintDevice()->convertFromQImage(img, 0, 0, 0);
- image->addNode(layer.data(), image->rootLayer().data());
+ 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;
+ document->setCurrentImage(image);
+ return KisImportExportFilter::OK;
}
#include "kis_bmp_import.moc"
diff --git a/plugins/impex/bmp/kis_bmp_import.h b/plugins/impex/bmp/kis_bmp_import.h
index 6fa514f3f8..978c29836d 100644
--- a/plugins/impex/bmp/kis_bmp_import.h
+++ b/plugins/impex/bmp/kis_bmp_import.h
@@ -1,37 +1,37 @@
/*
* 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 _KIS_BMP_IMPORT_H_
#define _KIS_BMP_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisBMPImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisBMPImport(QObject *parent, const QVariantList &);
virtual ~KisBMPImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/bmp/krita_bmp_export.json b/plugins/impex/bmp/krita_bmp_export.json
index f4787438a0..124dcbb1e8 100644
--- a/plugins/impex/bmp/krita_bmp_export.json
+++ b/plugins/impex/bmp/krita_bmp_export.json
@@ -1,13 +1,12 @@
{
"Id": "Krita BMP Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/bmp,image/x-xpixmap,image/x-xbitmap,image/vnd.microsoft.icon,image/webp",
- "X-KDE-Import": "application/x-krita",
"X-KDE-Library": "kritabmpexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "bmp,xpm,xbm,ico,webp"
}
diff --git a/plugins/impex/bmp/krita_bmp_import.json b/plugins/impex/bmp/krita_bmp_import.json
index 1d8b49f681..931605e5d5 100644
--- a/plugins/impex/bmp/krita_bmp_import.json
+++ b/plugins/impex/bmp/krita_bmp_import.json
@@ -1,13 +1,12 @@
{
"Id": "Krita BMP Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
"X-KDE-Import": "image/bmp,image/x-xpixmap,image/gif,image/x-xbitmap,image/webp",
"X-KDE-Library": "kritabmpimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "bmp,gif,webp,xbm"
}
diff --git a/plugins/impex/brush/CMakeLists.txt b/plugins/impex/brush/CMakeLists.txt
index bd4c339b81..ffce44e4d0 100644
--- a/plugins/impex/brush/CMakeLists.txt
+++ b/plugins/impex/brush/CMakeLists.txt
@@ -1,26 +1,26 @@
set(kritabrushexport_PART_SRCS
kis_brush_export.cpp
KisAnimatedBrushAnnotation.cpp
)
ki18n_wrap_ui(kritabrushexport_PART_SRCS wdg_export_gih.ui)
add_library(kritabrushexport MODULE ${kritabrushexport_PART_SRCS})
-target_link_libraries(kritabrushexport kritalibbrush kritalibpaintop kritaui)
+target_link_libraries(kritabrushexport kritalibbrush kritalibpaintop kritaui kritaimpex)
install(TARGETS kritabrushexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritabrushimport_PART_SRCS
kis_brush_import.cpp
KisAnimatedBrushAnnotation.cpp
)
ki18n_wrap_ui(kritabrushimport_PART_SRCS )
add_library(kritabrushimport MODULE ${kritabrushimport_PART_SRCS})
target_link_libraries(kritabrushimport kritalibbrush kritaui)
install(TARGETS kritabrushimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_brush.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/brush/KisAnimatedBrushAnnotation.h b/plugins/impex/brush/KisAnimatedBrushAnnotation.h
index 64fa5cda40..7e57e625c7 100644
--- a/plugins/impex/brush/KisAnimatedBrushAnnotation.h
+++ b/plugins/impex/brush/KisAnimatedBrushAnnotation.h
@@ -1,33 +1,36 @@
/*
* This file is part of the KDE project
*
* 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.
*/
#ifndef KISANIMATEDBRUSHANNOTATION_H
#define KISANIMATEDBRUSHANNOTATION_H
#include <kis_annotation.h>
class KisPipeBrushParasite;
class KisAnimatedBrushAnnotation : public KisAnnotation
{
public:
KisAnimatedBrushAnnotation(const KisPipeBrushParasite &parasite);
+ virtual KisAnnotation* clone() const Q_DECL_OVERRIDE {
+ return new KisAnimatedBrushAnnotation(*this);
+ }
};
#endif // KISANIMATEDBRUSHANNOTATION_H
diff --git a/plugins/impex/brush/kis_brush_export.cpp b/plugins/impex/brush/kis_brush_export.cpp
index 99faa90a5a..cc709505ff 100644
--- a/plugins/impex/brush/kis_brush_export.cpp
+++ b/plugins/impex/brush/kis_brush_export.cpp
@@ -1,213 +1,235 @@
/*
* Copyright (c) 2016 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_brush_export.h"
#include <QCheckBox>
#include <QSlider>
#include <QBuffer>
#include <KoProperties.h>
#include <KoDialog.h>
#include <kpluginfactory.h>
#include <QFileInfo>
-#include <KisFilterChain.h>
-
+#include <KisExportCheckRegistry.h>
#include <kis_paint_device.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_spacing_selection_widget.h>
#include <kis_gbr_brush.h>
#include <kis_imagepipe_brush.h>
#include <kis_pipebrush_parasite.h>
#include <KisAnimatedBrushAnnotation.h>
#include <KisImportExportManager.h>
-
-#include <ui_wdg_export_gih.h>
+#include <kis_config.h>
struct KisBrushExportOptions {
qreal spacing;
bool mask;
int brushStyle;
int selectionMode;
QString name;
};
K_PLUGIN_FACTORY_WITH_JSON(KisBrushExportFactory, "krita_brush_export.json", registerPlugin<KisBrushExport>();)
KisBrushExport::KisBrushExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisBrushExport::~KisBrushExport()
{
}
-KisImportExportFilter::ConversionStatus KisBrushExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisBrushExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
- Q_UNUSED(configuration);
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisAnnotationSP annotation = input->image()->annotation("ImagePipe Parasite");
- KisPipeBrushParasite parasite;
- if (annotation) {
- QBuffer buf(const_cast<QByteArray*>(&annotation->annotation()));
- buf.open(QBuffer::ReadOnly);
- //parasite.loadFromDevice(&buf);
- buf.close();
- }
+// XXX: Loading the parasite itself was commented out -- needs investigation
+// KisAnnotationSP annotation = document->image()->annotation("ImagePipe Parasite");
+// KisPipeBrushParasite parasite;
+// if (annotation) {
+// QBuffer buf(const_cast<QByteArray*>(&annotation->annotation()));
+// buf.open(QBuffer::ReadOnly);
+// parasite.loadFromDevice(&buf);
+// buf.close();
+// }
KisBrushExportOptions exportOptions;
- exportOptions.spacing = 1.0;
- exportOptions.name = input->image()->objectName();
- exportOptions.mask = true;
- exportOptions.selectionMode = 0;
- exportOptions.brushStyle = 0;
-
-
- if (input->image()->dynamicPropertyNames().contains("brushspacing")) {
- exportOptions.spacing = input->image()->property("brushspacing").toFloat();
+ if (document->image()->dynamicPropertyNames().contains("brushspacing")) {
+ exportOptions.spacing = document->image()->property("brushspacing").toFloat();
}
- KisGbrBrush *brush = 0;
-
- if (!getBatchMode()) {
-
- KoDialog* dlgBrushExportOptions = new KoDialog(0);
- dlgBrushExportOptions->setWindowTitle(i18n("Brush Tip Export Options"));
- dlgBrushExportOptions->setButtons(KoDialog::Ok | KoDialog::Cancel);
-
- Ui::WdgExportGih wdgUi;
- QWidget* wdg = new QWidget(dlgBrushExportOptions);
- wdgUi.setupUi(wdg);
- wdgUi.spacingWidget->setSpacing(false, exportOptions.spacing);
- wdgUi.nameLineEdit->setText(exportOptions.name);
- dlgBrushExportOptions->setMainWidget(wdg);
-
-
- if (to == "image/x-gimp-brush") {
- brush = new KisGbrBrush(filename);
- wdgUi.groupBox->setVisible(false);
- }
- else if (to == "image/x-gimp-brush-animated") {
- brush = new KisImagePipeBrush(filename);
- wdgUi.groupBox->setVisible(true);
- }
- else {
- delete dlgBrushExportOptions;
- return KisImportExportFilter::BadMimeType;
- }
+ else {
+ exportOptions.spacing = configuration->getInt("spacing");
+ }
+ if (!configuration->getString("name").isEmpty()) {
+ exportOptions.name = configuration->getString("name");
+ }
+ else {
+ exportOptions.name = document->image()->objectName();
+ }
+ exportOptions.mask = configuration->getBool("mask");
+ exportOptions.selectionMode = configuration->getInt("selectionMode");
+ exportOptions.brushStyle = configuration->getInt("brushStyle");
- if (dlgBrushExportOptions->exec() == QDialog::Rejected) {
- delete dlgBrushExportOptions;
- return KisImportExportFilter::UserCancelled;
- }
- else {
- exportOptions.spacing = wdgUi.spacingWidget->spacing();
- exportOptions.name = wdgUi.brushNameLbl->text();
- exportOptions.mask = wdgUi.colorAsMask->isChecked();
- exportOptions.brushStyle = wdgUi.brushStyle->currentIndex();
- exportOptions.selectionMode = wdgUi.cmbSelectionMode->currentIndex();
- delete dlgBrushExportOptions;
- }
+ KisGbrBrush *brush = 0;
+ if (mimeType() == "image/x-gimp-brush") {
+ brush = new KisGbrBrush(filename());
+ }
+ else if (mimeType() == "image/x-gimp-brush-animated") {
+ brush = new KisImagePipeBrush(filename());
}
else {
- qApp->processEvents(); // For vector layers to be updated
+ return KisImportExportFilter::BadMimeType;
}
+ qApp->processEvents(); // For vector layers to be updated
+
// the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(input->image()->locked());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
- QRect rc = input->image()->bounds();
+ QRect rc = document->image()->bounds();
brush->setName(exportOptions.name);
brush->setSpacing(exportOptions.spacing);
brush->setUseColorAsMask(exportOptions.mask);
- int w = input->image()->width();
- int h = input->image()->height();
+ int w = document->image()->width();
+ int h = document->image()->height();
KisImagePipeBrush *pipeBrush = dynamic_cast<KisImagePipeBrush*>(brush);
if (pipeBrush) {
// Create parasite. XXX: share with KisCustomBrushWidget
QVector< QVector<KisPaintDevice*> > devices;
devices.push_back(QVector<KisPaintDevice*>());
KoProperties properties;
properties.setProperty("visible", true);
- QList<KisNodeSP> layers = input->image()->root()->childNodes(QStringList("KisLayer"), properties);
- KisNodeSP node;
+ QList<KisNodeSP> layers = document->image()->root()->childNodes(QStringList("KisLayer"), properties);
+
Q_FOREACH (KisNodeSP node, layers) {
devices[0].push_back(node->projection().data());
}
-
QVector<KisParasite::SelectionMode > modes;
switch (exportOptions.selectionMode) {
case 0: modes.push_back(KisParasite::Constant); break;
case 1: modes.push_back(KisParasite::Random); break;
case 2: modes.push_back(KisParasite::Incremental); break;
case 3: modes.push_back(KisParasite::Pressure); break;
case 4: modes.push_back(KisParasite::Angular); break;
default: modes.push_back(KisParasite::Incremental);
}
KisPipeBrushParasite parasite;
// XXX: share code with KisImagePipeBrush, when we figure out how to support more gih features
parasite.dim = devices.count();
// XXX Change for multidim! :
parasite.ncells = devices.at(0).count();
parasite.rank[0] = parasite.ncells; // ### This can mask some bugs, be careful here in the future
parasite.selection[0] = modes.at(0);
// XXX needsmovement!
parasite.setBrushesCount();
pipeBrush->setParasite(parasite);
pipeBrush->setDevices(devices, w, h);
}
else {
- QImage image = input->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
+ QImage image = document->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
brush->setImage(image);
}
brush->setWidth(w);
brush->setHeight(h);
- QFile f(filename);
- f.open(QIODevice::WriteOnly);
- brush->saveToDevice(&f);
- f.close();
+ brush->saveToDevice(io);
return KisImportExportFilter::OK;
}
+KisPropertiesConfigurationSP KisBrushExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
+{
+ KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
+ cfg->setProperty("spacing", 1.0);
+ cfg->setProperty("name", "");
+ cfg->setProperty("mask", true);
+ cfg->setProperty("selectionMode", 0);
+ cfg->setProperty("brushStyle", 0);
+ return cfg;
+}
+
+KisPropertiesConfigurationSP KisBrushExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
+{
+ KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
+ QString filterConfig = KisConfig().exportConfiguration(mimeType());
+ cfg->fromXML(filterConfig, false);
+ return cfg;
+}
+
+KisConfigWidget *KisBrushExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &to) const
+{
+ KisWdgOptionsBrush *wdg = new KisWdgOptionsBrush(parent);
+ if (to == "image/x-gimp-brush") {
+ wdg->groupBox->setVisible(false);
+ }
+ else if (to == "image/x-gimp-brush-animated") {
+ wdg->groupBox->setVisible(true);
+ }
+ return wdg;
+}
+
+void KisBrushExport::initializeCapabilities()
+{
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "Gimp Brushes");
+ if (mimeType() == "image/x-gimp-brush-animated") {
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+ }
+}
+
+
+void KisWdgOptionsBrush::setConfiguration(const KisPropertiesConfigurationSP cfg)
+{
+ spacingWidget->setSpacing(false, cfg->getBool("spacing"));
+ nameLineEdit->setText(cfg->getString("name"));
+ brushNameLbl->setText(cfg->getString("name"));
+ colorAsMask->setChecked(cfg->getBool("mask"));
+ brushStyle->setCurrentIndex(cfg->getInt("selectionMode"));
+ cmbSelectionMode->setCurrentIndex(cfg->getInt("brushStyle"));
+}
+
+KisPropertiesConfigurationSP KisWdgOptionsBrush::configuration() const
+{
+ KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
+ cfg->setProperty("spacing", spacingWidget->spacing());
+ cfg->setProperty("name", brushNameLbl->text());
+ cfg->setProperty("mask", colorAsMask->isChecked());
+ cfg->setProperty("selectionMode", brushStyle->currentIndex());
+ cfg->setProperty("brushStyle", cmbSelectionMode->currentIndex());
+ return cfg;
+}
+
+
#include "kis_brush_export.moc"
diff --git a/plugins/impex/brush/kis_brush_export.h b/plugins/impex/brush/kis_brush_export.h
index 47b3bb27a3..2e3d262cbc 100644
--- a/plugins/impex/brush/kis_brush_export.h
+++ b/plugins/impex/brush/kis_brush_export.h
@@ -1,37 +1,61 @@
/*
* Copyright (c) 2016 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_Brush_EXPORT_H_
#define _KIS_Brush_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
+#include <ui_wdg_export_gih.h>
+#include <kis_config_widget.h>
+#include <kis_properties_configuration.h>
+
+class KisWdgOptionsBrush : public KisConfigWidget, public Ui::WdgExportGih
+{
+ Q_OBJECT
+
+public:
+ KisWdgOptionsBrush(QWidget *parent)
+ : KisConfigWidget(parent)
+ {
+ setupUi(this);
+ }
+
+ void setConfiguration(const KisPropertiesConfigurationSP cfg);
+ KisPropertiesConfigurationSP configuration() const;
+};
+
class KisBrushExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisBrushExport(QObject *parent, const QVariantList &);
virtual ~KisBrushExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const;
+ KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const;
+ KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
+
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/brush/kis_brush_import.cpp b/plugins/impex/brush/kis_brush_import.cpp
index 6c0abc4382..fb681bf25e 100644
--- a/plugins/impex/brush/kis_brush_import.cpp
+++ b/plugins/impex/brush/kis_brush_import.cpp
@@ -1,151 +1,123 @@
/*
* Copyright (c) 2016 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_brush_import.h"
#include <QCheckBox>
#include <QBuffer>
#include <QSlider>
#include <QApplication>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
-#include <KisFilterChain.h>
#include <KisDocument.h>
#include <kis_transaction.h>
#include <kis_paint_device.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_node.h>
#include <kis_group_layer.h>
#include <kis_gbr_brush.h>
#include <kis_imagepipe_brush.h>
#include <KisAnimatedBrushAnnotation.h>
K_PLUGIN_FACTORY_WITH_JSON(KisBrushImportFactory, "krita_brush_import.json", registerPlugin<KisBrushImport>();)
KisBrushImport::KisBrushImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisBrushImport::~KisBrushImport()
{
}
-KisImportExportFilter::ConversionStatus KisBrushImport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisBrushImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
+ KisBrush *brush;
- if (to != "application/x-krita")
+ if (mimeType() == "image/x-gimp-brush") {
+ brush = new KisGbrBrush(filename());
+ }
+ else if (mimeType() == "image/x-gimp-brush-animated") {
+ brush = new KisImagePipeBrush(filename());
+ }
+ else {
return KisImportExportFilter::BadMimeType;
+ }
- QString filename = inputFile();
-
- if (!filename.isEmpty()) {
-
- if (!QFile(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
- }
-
-
- KisBrush *brush;
-
- if (from == "image/x-gimp-brush") {
- brush = new KisGbrBrush(filename);
- }
- else if (from == "image/x-gimp-brush-animated") {
- brush = new KisImagePipeBrush(filename);
- }
- else {
- return KisImportExportFilter::BadMimeType;
- }
-
-
- if (!brush->load()) {
- delete brush;
- return KisImportExportFilter::InvalidFormat;
- }
-
- if (!brush->valid()) {
- delete brush;
- return KisImportExportFilter::InvalidFormat;
- }
-
- KisDocument * doc = outputDocument();
-
- if (!doc) {
- delete brush;
- return KisImportExportFilter::NoDocumentCreated;
- }
+ if (!brush->loadFromDevice(io)) {
+ delete brush;
+ return KisImportExportFilter::InvalidFormat;
+ }
- doc->prepareForImport();
+ if (!brush->valid()) {
+ delete brush;
+ return KisImportExportFilter::InvalidFormat;
+ }
- const KoColorSpace *colorSpace = 0;
- if (brush->hasColor()) {
- colorSpace = KoColorSpaceRegistry::instance()->rgb8();
- }
- else {
- colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
- }
+ const KoColorSpace *colorSpace = 0;
+ if (brush->hasColor()) {
+ colorSpace = KoColorSpaceRegistry::instance()->rgb8();
+ }
+ else {
+ colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
+ }
- KisImageWSP image = new KisImage(doc->createUndoStore(), brush->width(), brush->height(), colorSpace, brush->name());
- image->setProperty("brushspacing", brush->spacing());
-
- KisImagePipeBrush *pipeBrush = dynamic_cast<KisImagePipeBrush*>(brush);
- if (pipeBrush) {
- QVector<KisGbrBrush*> brushes = pipeBrush->brushes();
- for(int i = brushes.size(); i > 0; i--) {
- KisGbrBrush *subbrush = brushes.at(i - 1);
- const KoColorSpace *subColorSpace = 0;
- if (brush->hasColor()) {
- subColorSpace = KoColorSpaceRegistry::instance()->rgb8();
- }
- else {
- subColorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
- }
- KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255, subColorSpace);
- layer->paintDevice()->convertFromQImage(subbrush->brushTipImage(), 0, 0, 0);
- image->addNode(layer, image->rootLayer());
+ KisImageSP image = new KisImage(document->createUndoStore(), brush->width(), brush->height(), colorSpace, brush->name());
+ image->setProperty("brushspacing", brush->spacing());
+
+ KisImagePipeBrush *pipeBrush = dynamic_cast<KisImagePipeBrush*>(brush);
+ if (pipeBrush) {
+ QVector<KisGbrBrush*> brushes = pipeBrush->brushes();
+ for(int i = brushes.size(); i > 0; i--) {
+ KisGbrBrush *subbrush = brushes.at(i - 1);
+ const KoColorSpace *subColorSpace = 0;
+ if (brush->hasColor()) {
+ subColorSpace = KoColorSpaceRegistry::instance()->rgb8();
}
- KisAnnotationSP ann = new KisAnimatedBrushAnnotation(pipeBrush->parasite());
- image->addAnnotation(ann);
- }
- else {
- KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255, colorSpace);
- layer->paintDevice()->convertFromQImage(brush->brushTipImage(), 0, 0, 0);
- image->addNode(layer, image->rootLayer(), 0);
+ else {
+ subColorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
+ }
+ KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255, subColorSpace);
+ layer->paintDevice()->convertFromQImage(subbrush->brushTipImage(), 0, 0, 0);
+ image->addNode(layer, image->rootLayer());
}
-
- doc->setCurrentImage(image);
- delete brush;
- return KisImportExportFilter::OK;
+ KisAnnotationSP ann = new KisAnimatedBrushAnnotation(pipeBrush->parasite());
+ image->addAnnotation(ann);
+ }
+ else {
+ KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255, colorSpace);
+ layer->paintDevice()->convertFromQImage(brush->brushTipImage(), 0, 0, 0);
+ image->addNode(layer, image->rootLayer(), 0);
}
- return KisImportExportFilter::StorageCreationError;
+ document->setCurrentImage(image);
+ delete brush;
+ return KisImportExportFilter::OK;
}
#include "kis_brush_import.moc"
diff --git a/plugins/impex/brush/kis_brush_import.h b/plugins/impex/brush/kis_brush_import.h
index 290fee192e..6d084b5d22 100644
--- a/plugins/impex/brush/kis_brush_import.h
+++ b/plugins/impex/brush/kis_brush_import.h
@@ -1,37 +1,37 @@
/*
* Copyright (c) 2016 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_Brush_IMPORT_H_
#define _KIS_Brush_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisBrushImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisBrushImport(QObject *parent, const QVariantList &);
virtual ~KisBrushImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/brush/krita_brush_export.json b/plugins/impex/brush/krita_brush_export.json
index 3cca14e7c6..245085ca32 100644
--- a/plugins/impex/brush/krita_brush_export.json
+++ b/plugins/impex/brush/krita_brush_export.json
@@ -1,11 +1,11 @@
{
"Id": "Krita Brush Export Filter",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"NoDisplay": "true",
"X-KDE-Export": "image/x-gimp-brush,image/x-gimp-brush-animated",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "gih,gbr"
}
diff --git a/plugins/impex/brush/krita_brush_import.json b/plugins/impex/brush/krita_brush_import.json
index 05ac641a60..e647123f21 100644
--- a/plugins/impex/brush/krita_brush_import.json
+++ b/plugins/impex/brush/krita_brush_import.json
@@ -1,11 +1,11 @@
{
"Id": "Krita Brush Import Filter",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"NoDisplay": "true",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/x-gimp-brush,image/x-gimp-brush-animated",
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "gih,gbr"
}
diff --git a/plugins/impex/csv/CMakeLists.txt b/plugins/impex/csv/CMakeLists.txt
index da860efbe1..6cd719e02f 100644
--- a/plugins/impex/csv/CMakeLists.txt
+++ b/plugins/impex/csv/CMakeLists.txt
@@ -1,36 +1,36 @@
add_subdirectory(tests)
include_directories(SYSTEM
${Boost_INCLUDE_DIRS}
)
# import
set(kritacsvimport_SOURCES
kis_csv_import.cpp
csv_loader.cpp
csv_read_line.cpp
csv_layer_record.cpp
)
add_library(kritacsvimport MODULE ${kritacsvimport_SOURCES})
target_link_libraries(kritacsvimport kritaui )
install(TARGETS kritacsvimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
# export
set(kritacsvexport_SOURCES
kis_csv_export.cpp
csv_saver.cpp
csv_layer_record.cpp
)
add_library(kritacsvexport MODULE ${kritacsvexport_SOURCES})
-target_link_libraries(kritacsvexport kritaui )
+target_link_libraries(kritacsvexport kritaui kritaimpex)
install(TARGETS kritacsvexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_csv.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/csv/csv_layer_record.h b/plugins/impex/csv/csv_layer_record.h
index bbfb36d9eb..24a47838ec 100644
--- a/plugins/impex/csv/csv_layer_record.h
+++ b/plugins/impex/csv/csv_layer_record.h
@@ -1,45 +1,45 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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 CSV_LAYER_RECORD_H_
#define CSV_LAYER_RECORD_H_
#include <QString>
#include "kis_raster_keyframe_channel.h"
class CSVLayerRecord
{
public:
CSVLayerRecord();
virtual ~CSVLayerRecord();
QString name;
QString blending;
float density;
int visible;
- KisPaintLayer* layer;
+ KisLayer* layer;
KisRasterKeyframeChannel *channel;
QString last;
QString path;
int frame;
};
#endif
diff --git a/plugins/impex/csv/csv_loader.cpp b/plugins/impex/csv/csv_loader.cpp
index 584edb27f1..4cc3f017a9 100644
--- a/plugins/impex/csv/csv_loader.cpp
+++ b/plugins/impex/csv/csv_loader.cpp
@@ -1,494 +1,486 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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 "csv_loader.h"
#include <QDebug>
#include <QApplication>
#include <QFile>
#include <QVector>
#include <QIODevice>
#include <QStatusBar>
#include <QFileInfo>
#include <KisPart.h>
#include <KisView.h>
#include <KisDocument.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <kis_debug.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_raster_keyframe_channel.h>
#include <kis_image_animation_interface.h>
#include <kis_time_range.h>
#include "csv_read_line.h"
#include "csv_layer_record.h"
CSVLoader::CSVLoader(KisDocument *doc, bool batchMode)
: m_image(0)
, m_doc(doc)
, m_batchMode(batchMode)
, m_stop(false)
{
}
CSVLoader::~CSVLoader()
{
}
-KisImageBuilder_Result CSVLoader::decode(const QString &filename)
+KisImageBuilder_Result CSVLoader::decode(QIODevice *io, const QString &filename)
{
QString field;
int idx;
int frame = 0;
QString projName;
int width = 0;
int height = 0;
int frameCount = 1;
float framerate = 24.0;
float pixelRatio = 1.0;
int projNameIdx = -1;
int widthIdx = -1;
int heightIdx = -1;
int frameCountIdx = -1;
int framerateIdx = -1;
int pixelRatioIdx = -1;
QVector<CSVLayerRecord*> layers;
- // open the csv file
- QFile f(filename);
- if (!f.exists())
- return KisImageBuilder_RESULT_NOT_EXIST;
-
- if (!f.open(QIODevice::ReadOnly))
- return KisImageBuilder_RESULT_NOT_EXIST;
-
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- idx= filename.lastIndexOf(QRegExp("[\\/]"));
+ idx = filename.lastIndexOf(QRegExp("[\\/]"));
QString base = (idx == -1) ? QString() : filename.left(idx + 1); //include separator
QString path = filename;
if (path.right(4).toUpper() == ".CSV")
path = path.left(path.size() - 4);
//according to the QT docs, the slash is a universal directory separator
path.append(".frames/");
KisImageBuilder_Result retval = KisImageBuilder_RESULT_OK;
- dbgFile << "pos:" << f.pos();
+ dbgFile << "pos:" << io->pos();
CSVReadLine readLine;
QScopedPointer<KisDocument> importDoc(KisPart::instance()->createDocument());
- importDoc->setAutoSave(0);
+ importDoc->setAutoSaveDelay(0);
importDoc->setFileBatchMode(true);
- KisView* setView(0);
+ KisView *setView(0);
if (!m_batchMode) {
//show the statusbar message even if no view
Q_FOREACH (KisView* view, KisPart::instance()->views()) {
if (view && view->document() == m_doc) {
- setView= view;
+ setView = view;
break;
}
}
if (!setView) {
QStatusBar *sb = KisPart::instance()->currentMainwindow()->statusBar();
if (sb) {
sb->showMessage(i18n("Loading CSV file..."));
}
} else {
emit m_doc->statusBarMessage(i18n("Loading CSV file..."));
}
emit m_doc->sigProgress(0);
connect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel()));
}
int step = 0;
do {
qApp->processEvents();
if (m_stop) {
retval = KisImageBuilder_RESULT_CANCEL;
break;
}
- if ((idx = readLine.nextLine(&f)) <= 0) {
+ if ((idx = readLine.nextLine(io)) <= 0) {
if ((idx < 0) ||(step < 5))
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
field = readLine.nextField(); //first field of the line
if (field.isNull()) continue; //empty row
switch (step) {
case 0 : //skip first row
step = 1;
break;
case 1 : //scene header names
step = 2;
for (idx = 0; !field.isNull(); idx++) {
if (field == "Project Name") {
projNameIdx = idx;
} else if (field == "Width") {
widthIdx = idx;
} else if (field == "Height") {
heightIdx = idx;
} else if (field == "Frame Count") {
frameCountIdx = idx;
} else if (field == "Frame Rate") {
framerateIdx = idx;
} else if (field == "Pixel Aspect Ratio") {
pixelRatioIdx = idx;
}
field= readLine.nextField();
}
break;
case 2 : //scene header values
step= 3;
for (idx= 0; !field.isNull(); idx++) {
if (idx == projNameIdx) {
projName = field;
} else if (idx == widthIdx) {
width = field.toInt();
} else if (idx == heightIdx) {
height = field.toInt();
} else if (idx == frameCountIdx) {
frameCount = field.toInt();
if (frameCount < 1) frameCount= 1;
} else if (idx == framerateIdx) {
framerate = field.toFloat();
} else if (idx == pixelRatioIdx) {
pixelRatio = field.toFloat();
}
field= readLine.nextField();
}
if ((width < 1) || (height < 1)) {
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
retval = createNewImage(width, height, pixelRatio, projName.isNull() ? filename : projName);
break;
case 3 : //create level headers
if (field[0] != '#') break;
for (; !(field = readLine.nextField()).isNull(); ) {
CSVLayerRecord* layerRecord = new CSVLayerRecord();
layers.append(layerRecord);
}
readLine.rewind();
field = readLine.nextField();
step = 4;
//no break!
case 4 : //level header
if (field == "#Layers") {
//layer name
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->name = field;
break;
}
if (field == "#Density") {
//layer opacity
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->density = field.toFloat();
break;
}
if (field == "#Blending") {
//layer blending mode
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->blending = field;
break;
}
if (field == "#Visible") {
//layer visibility
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->visible = field.toInt();
break;
}
if (field == "#Folder") {
//CSV 1.1 folder location
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->path = validPath(field, base);
break;
}
if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break;
step = 5;
//no break!
case 5 : //frames
if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break;
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) {
CSVLayerRecord* layer = layers.at(idx);
if (layer->last != field) {
if (!m_batchMode) {
emit m_doc->sigProgress((frame * layers.size() + idx) * 100 /
(frameCount * layers.size()));
}
retval = setLayer(layer, importDoc.data(), path);
layer->last = field;
layer->frame = frame;
}
}
frame++;
break;
}
} while (retval == KisImageBuilder_RESULT_OK);
//finish the layers
if (retval == KisImageBuilder_RESULT_OK) {
if (m_image) {
KisImageAnimationInterface *animation = m_image->animationInterface();
if (frame > frameCount)
frameCount = frame;
animation->setFullClipRange(KisTimeRange::fromTime(0,frameCount - 1));
animation->setFramerate((int)framerate);
}
for (idx = 0; idx < layers.size(); idx++) {
CSVLayerRecord* layer = layers.at(idx);
//empty layers without any pictures are dropped
if ((layer->frame > 0) || !layer->last.isEmpty()) {
retval = setLayer(layer, importDoc.data(), path);
if (retval != KisImageBuilder_RESULT_OK)
break;
}
}
}
if (m_image) {
//insert the existing layers by the right order
for (idx = layers.size() - 1; idx >= 0; idx--) {
CSVLayerRecord* layer = layers.at(idx);
if (layer->layer) {
m_image->addNode(layer->layer, m_image->root());
}
}
m_image->unlock();
}
qDeleteAll(layers);
- f.close();
+ io->close();
if (!m_batchMode) {
disconnect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel()));
emit m_doc->sigProgress(100);
if (!setView) {
QStatusBar *sb = KisPart::instance()->currentMainwindow()->statusBar();
if (sb) {
sb->clearMessage();
}
} else {
emit m_doc->clearStatusBarMessage();
}
}
QApplication::restoreOverrideCursor();
return retval;
}
QString CSVLoader::convertBlending(const QString &blending)
{
if (blending == "Color") return COMPOSITE_OVER;
if (blending == "Behind") return COMPOSITE_BEHIND;
if (blending == "Erase") return COMPOSITE_ERASE;
// "Shade"
if (blending == "Light") return COMPOSITE_LINEAR_LIGHT;
if (blending == "Colorize") return COMPOSITE_COLORIZE;
if (blending == "Hue") return COMPOSITE_HUE;
if (blending == "Add") return COMPOSITE_ADD;
if (blending == "Sub") return COMPOSITE_INVERSE_SUBTRACT;
if (blending == "Multiply") return COMPOSITE_MULT;
if (blending == "Screen") return COMPOSITE_SCREEN;
// "Replace"
// "Subtitute"
if (blending == "Difference") return COMPOSITE_DIFF;
if (blending == "Divide") return COMPOSITE_DIVIDE;
if (blending == "Overlay") return COMPOSITE_OVERLAY;
if (blending == "Light2") return COMPOSITE_DODGE;
if (blending == "Shade2") return COMPOSITE_BURN;
if (blending == "HardLight") return COMPOSITE_HARD_LIGHT;
if (blending == "SoftLight") return COMPOSITE_SOFT_LIGHT_PHOTOSHOP;
if (blending == "GrainExtract") return COMPOSITE_GRAIN_EXTRACT;
if (blending == "GrainMerge") return COMPOSITE_GRAIN_MERGE;
if (blending == "Sub2") return COMPOSITE_SUBTRACT;
if (blending == "Darken") return COMPOSITE_DARKEN;
if (blending == "Lighten") return COMPOSITE_LIGHTEN;
if (blending == "Saturation") return COMPOSITE_SATURATION;
return COMPOSITE_OVER;
}
QString CSVLoader::validPath(const QString &path,const QString &base)
{
//replace Windows directory separators with the universal /
QString tryPath= QString(path).replace(QString("\\"), QString("/"));
int i = tryPath.lastIndexOf("/");
if (i == (tryPath.size() - 1))
tryPath= tryPath.left(i); //remove the ending separator if exists
if (QFileInfo(tryPath).isDir())
return tryPath.append("/");
QString scan(tryPath);
i = -1;
while ((i= (scan.lastIndexOf("/",i) - 1)) > 0) {
//avoid testing if the next level will be the default xxxx.layers folder
if ((i >= 6) && (scan.mid(i - 6, 7) == ".layers")) continue;
tryPath= QString(base).append(scan.mid(i + 2)); //base already ending with a /
if (QFileInfo(tryPath).isDir())
return tryPath.append("/");
}
return QString(); //NULL string
}
KisImageBuilder_Result CSVLoader::setLayer(CSVLayerRecord* layer, KisDocument *importDoc, const QString &path)
{
bool result = true;
if (layer->channel == 0) {
//create a new document layer
float opacity = layer->density;
if (opacity > 1.0)
opacity = 1.0;
else if (opacity < 0.0)
opacity = 0.0;
const KoColorSpace* cs = m_image->colorSpace();
const QString layerName = (layer->name).isEmpty() ? m_image->nextLayerName() : layer->name;
KisPaintLayer* paintLayer = new KisPaintLayer(m_image, layerName,
(quint8)(opacity * OPACITY_OPAQUE_U8), cs);
paintLayer->setCompositeOpId(convertBlending(layer->blending));
paintLayer->setVisible(layer->visible);
paintLayer->enableAnimation();
layer->layer = paintLayer;
layer->channel = qobject_cast<KisRasterKeyframeChannel*>
(paintLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true));
}
if (!layer->last.isEmpty()) {
//png image
QString filename = layer->path.isNull() ? path : layer->path;
filename.append(layer->last);
result = importDoc->openUrl(QUrl::fromLocalFile(filename),
KisDocument::OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES);
if (result)
layer->channel->importFrame(layer->frame, importDoc->image()->projection(), 0);
} else {
//blank
layer->channel->addKeyframe(layer->frame);
}
return (result) ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE;
}
KisImageBuilder_Result CSVLoader::createNewImage(int width, int height, float ratio, const QString &name)
{
//the CSV is RGBA 8bits, sRGB
if (!m_image) {
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(
RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), 0);
if (cs) m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, name);
if (!m_image) return KisImageBuilder_RESULT_FAILURE;
m_image->setResolution(ratio, 1.0);
m_image->lock();
}
return KisImageBuilder_RESULT_OK;
}
-KisImageBuilder_Result CSVLoader::buildAnimation(QString &filename)
+KisImageBuilder_Result CSVLoader::buildAnimation(QIODevice *io, const QString &filename)
{
- return decode(filename);
+ return decode(io, filename);
}
-KisImageWSP CSVLoader::image()
+KisImageSP CSVLoader::image()
{
return m_image;
}
void CSVLoader::cancel()
{
m_stop = true;
}
diff --git a/plugins/impex/csv/csv_loader.h b/plugins/impex/csv/csv_loader.h
index 9a7d87155b..941c8232c2 100644
--- a/plugins/impex/csv/csv_loader.h
+++ b/plugins/impex/csv/csv_loader.h
@@ -1,62 +1,62 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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 CSV_LOADER_H_
#define CSV_LOADER_H_
#include <QObject>
#include <QFileInfo>
#include "kis_image.h"
#include "kritaui_export.h"
#include <KisImageBuilderResult.h>
class KisDocument;
#include "csv_layer_record.h"
class CSVLoader : public QObject {
Q_OBJECT
public:
CSVLoader(KisDocument* doc, bool batchMode);
virtual ~CSVLoader();
- KisImageBuilder_Result buildAnimation(QString &filename);
+ KisImageBuilder_Result buildAnimation(QIODevice *io, const QString &filename);
- KisImageWSP image();
+ KisImageSP image();
private:
- KisImageBuilder_Result decode(const QString &);
+ KisImageBuilder_Result decode(QIODevice *io, const QString &filename);
KisImageBuilder_Result setLayer(CSVLayerRecord* , KisDocument* ,const QString &);
KisImageBuilder_Result createNewImage(int, int, float, const QString &);
QString convertBlending(const QString &);
QString validPath(const QString &, const QString &);
private Q_SLOTS:
void cancel();
private:
- KisImageWSP m_image;
+ KisImageSP m_image;
KisDocument* m_doc;
bool m_batchMode;
bool m_stop;
};
#endif
diff --git a/plugins/impex/csv/csv_saver.cpp b/plugins/impex/csv/csv_saver.cpp
index d1858cae6f..de4c4c2e58 100644
--- a/plugins/impex/csv/csv_saver.cpp
+++ b/plugins/impex/csv/csv_saver.cpp
@@ -1,481 +1,474 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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 "csv_saver.h"
#include <QDebug>
#include <QApplication>
#include <QFileInfo>
#include <QFile>
#include <QDir>
#include <QVector>
#include <QIODevice>
#include <QRect>
#include <KisMimeDatabase.h>
#include <KisPart.h>
#include <KisDocument.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <kis_annotation.h>
#include <kis_types.h>
#include <kis_debug.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_raster_keyframe_channel.h>
#include <kis_image_animation_interface.h>
#include <kis_time_range.h>
#include <kis_iterator_ng.h>
#include "csv_layer_record.h"
CSVSaver::CSVSaver(KisDocument *doc, bool batchMode)
: m_image(doc->image())
, m_doc(doc)
, m_batchMode(batchMode)
, m_stop(false)
{
}
CSVSaver::~CSVSaver()
{
}
-KisImageWSP CSVSaver::image()
+KisImageSP CSVSaver::image()
{
return m_image;
}
-KisImageBuilder_Result CSVSaver::encode(const QString &filename)
+KisImageBuilder_Result CSVSaver::encode(QIODevice *io)
{
int idx;
int start, end;
KisNodeSP node;
QByteArray ba;
KisKeyframeSP keyframe;
QVector<CSVLayerRecord*> layers;
KisImageAnimationInterface *animation = m_image->animationInterface();
- //open the csv file for writing
- QFile f(filename);
- if (!f.open(QIODevice::WriteOnly)) {
- return KisImageBuilder_RESULT_NOT_LOCAL;
- }
-
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- //DataStream instead of TextStream for correct line endings
- QDataStream stream(&f);
+// XXX: Stream was unused?
+// //DataStream instead of TextStream for correct line endings
+// QDataStream stream(&f);
//Using the original local path
QString path = m_doc->localFilePath();
if (path.right(4).toUpper() == ".CSV")
path = path.left(path.size() - 4);
else {
- //something is wrong: the local file name is not .csv!
- //trying the given (probably temporary) filename as well
-
- path= filename;
-
+ // something is wrong: the local file name is not .csv!
+ // trying the given (probably temporary) filename as well
+ // XXX: unbreak this!
if (path.right(4).toUpper() == ".CSV")
path = path.left(path.size() - 4);
}
path.append(".frames");
//create directory
QDir dir(path);
if (!dir.exists()) {
dir.mkpath(".");
}
//according to the QT docs, the slash is a universal directory separator
path.append("/");
node = m_image->rootLayer()->firstChild();
//TODO: correct handling of the layer tree.
//for now, only top level paint layers are saved
idx = 0;
while (node) {
- if (node->inherits("KisPaintLayer")) {
- KisPaintLayer* paintLayer = dynamic_cast<KisPaintLayer*>(node.data());
+ if (node->inherits("KisLayer")) {
+ KisLayer* paintLayer = dynamic_cast<KisLayer*>(node.data());
CSVLayerRecord* layerRecord = new CSVLayerRecord();
layers.prepend(layerRecord); //reverse order!
layerRecord->name = paintLayer->name();
layerRecord->name.replace(QRegExp("[\"\\r\\n]"), "_");
if (layerRecord->name.isEmpty())
layerRecord->name= QString("Unnamed-%1").arg(idx);
layerRecord->visible = (paintLayer->visible()) ? 1 : 0;
layerRecord->density = (float)(paintLayer->opacity()) / OPACITY_OPAQUE_U8;
layerRecord->blending = convertToBlending(paintLayer->compositeOpId());
layerRecord->layer = paintLayer;
layerRecord->channel = paintLayer->projection()->keyframeChannel();
layerRecord->last = "";
layerRecord->frame = 0;
idx++;
}
node = node->nextSibling();
}
KisTimeRange range = animation->fullClipRange();
start = (range.isValid()) ? range.start() : 0;
if (!range.isInfinite()) {
end = range.end();
if (end < start) end = start;
} else {
//undefined length, searching for the last keyframe
end = start;
for (idx = 0; idx < layers.size(); idx++) {
KisRasterKeyframeChannel *channel = layers.at(idx)->channel;
if (channel) {
keyframe = channel->lastKeyframe();
if ( (!keyframe.isNull()) && (keyframe->time() > end) )
end = keyframe->time();
}
}
}
//create temporary doc for exporting
QScopedPointer<KisDocument> exportDoc(KisPart::instance()->createDocument());
createTempImage(exportDoc.data());
KisImageBuilder_Result retval= KisImageBuilder_RESULT_OK;
if (!m_batchMode) {
emit m_doc->statusBarMessage(i18n("Saving CSV file..."));
emit m_doc->sigProgress(0);
connect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel()));
}
int frame = start;
int step = 0;
do {
qApp->processEvents();
if (m_stop) {
retval = KisImageBuilder_RESULT_CANCEL;
break;
}
switch(step) {
case 0 : //first row
- if (f.write("UTF-8, TVPaint, \"CSV 1.0\"\r\n") < 0) {
+ if (io->write("UTF-8, TVPaint, \"CSV 1.0\"\r\n") < 0) {
retval = KisImageBuilder_RESULT_FAILURE;
}
break;
case 1 : //scene header names
- if (f.write("Project Name, Width, Height, Frame Count, Layer Count, Frame Rate, Pixel Aspect Ratio, Field Mode\r\n") < 0) {
+ if (io->write("Project Name, Width, Height, Frame Count, Layer Count, Frame Rate, Pixel Aspect Ratio, Field Mode\r\n") < 0) {
retval = KisImageBuilder_RESULT_FAILURE;
}
break;
case 2 : //scene header values
ba = QString("\"%1\", ").arg(m_image->objectName()).toUtf8();
- if (f.write(ba.data()) < 0) {
+ if (io->write(ba.data()) < 0) {
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
ba = QString("%1, %2, ").arg(m_image->width()).arg(m_image->height()).toUtf8();
- if (f.write(ba.data()) < 0) {
+ if (io->write(ba.data()) < 0) {
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
ba = QString("%1, %2, ").arg(end - start + 1).arg(layers.size()).toUtf8();
- if (f.write(ba.data()) < 0) {
+ if (io->write(ba.data()) < 0) {
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
//the framerate is an integer here
ba = QString("%1, ").arg((double)(animation->framerate()),0,'f',6).toUtf8();
- if (f.write(ba.data()) < 0) {
+ if (io->write(ba.data()) < 0) {
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
ba = QString("%1, Progressive\r\n").arg((double)(m_image->xRes() / m_image->yRes()),0,'f',6).toUtf8();
- if (f.write(ba.data()) < 0) {
+ if (io->write(ba.data()) < 0) {
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
break;
case 3 : //layer header values
- if (f.write("#Layers") < 0) { //Layers
+ if (io->write("#Layers") < 0) { //Layers
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
for (idx = 0; idx < layers.size(); idx++) {
ba = QString(", \"%1\"").arg(layers.at(idx)->name).toUtf8();
- if (f.write(ba.data()) < 0)
+ if (io->write(ba.data()) < 0)
break;
}
break;
case 4 :
- if (f.write("\r\n#Density") < 0) { //Density
+ if (io->write("\r\n#Density") < 0) { //Density
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
for (idx = 0; idx < layers.size(); idx++) {
ba = QString(", %1").arg((double)(layers.at(idx)->density), 0, 'f', 6).toUtf8();
- if (f.write(ba.data()) < 0)
+ if (io->write(ba.data()) < 0)
break;
}
break;
case 5 :
- if (f.write("\r\n#Blending") < 0) { //Blending
+ if (io->write("\r\n#Blending") < 0) { //Blending
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
for (idx = 0; idx < layers.size(); idx++) {
ba = QString(", \"%1\"").arg(layers.at(idx)->blending).toUtf8();
- if (f.write(ba.data()) < 0)
+ if (io->write(ba.data()) < 0)
break;
}
break;
case 6 :
- if (f.write("\r\n#Visible") < 0) { //Visible
+ if (io->write("\r\n#Visible") < 0) { //Visible
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
for (idx = 0; idx < layers.size(); idx++) {
ba = QString(", %1").arg(layers.at(idx)->visible).toUtf8();
- if (f.write(ba.data()) < 0)
+ if (io->write(ba.data()) < 0)
break;
}
if (idx < layers.size()) {
retval = KisImageBuilder_RESULT_FAILURE;
}
break;
default : //frames
if (frame > end) {
- if (f.write("\r\n") < 0)
+ if (io->write("\r\n") < 0)
retval = KisImageBuilder_RESULT_FAILURE;
step = 8;
break;
}
ba = QString("\r\n#%1").arg(frame, 5, 10, QChar('0')).toUtf8();
- if (f.write(ba.data()) < 0) {
+ if (io->write(ba.data()) < 0) {
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
for (idx = 0; idx < layers.size(); idx++) {
CSVLayerRecord *layer = layers.at(idx);
KisRasterKeyframeChannel *channel = layer->channel;
if (channel) {
if (frame == start) {
keyframe = channel->activeKeyframeAt(frame);
} else {
keyframe = channel->keyframeAt(frame);
}
} else {
keyframe.clear(); // without animation
}
if ( !keyframe.isNull() || (frame == start) ) {
if (!m_batchMode) {
emit m_doc->sigProgress(((frame - start) * layers.size() + idx) * 100 /
((end - start) * layers.size()));
}
retval = getLayer(layer, exportDoc.data(), keyframe, path, frame, idx);
if (retval != KisImageBuilder_RESULT_OK)
break;
}
ba = QString(", \"%1\"").arg(layer->last).toUtf8();
- if (f.write(ba.data()) < 0)
+ if (io->write(ba.data()) < 0)
break;
}
if (idx < layers.size())
retval = KisImageBuilder_RESULT_FAILURE;
frame++;
step = 6; //keep step here
break;
}
step++;
} while((retval == KisImageBuilder_RESULT_OK) && (step < 8));
qDeleteAll(layers);
- f.close();
+ io->close();
if (!m_batchMode) {
disconnect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel()));
emit m_doc->sigProgress(100);
emit m_doc->clearStatusBarMessage();
}
QApplication::restoreOverrideCursor();
return retval;
}
QString CSVSaver::convertToBlending(const QString &opid)
{
if (opid == COMPOSITE_OVER) return "Color";
if (opid == COMPOSITE_BEHIND) return "Behind";
if (opid == COMPOSITE_ERASE) return "Erase";
// "Shade"
if (opid == COMPOSITE_LINEAR_LIGHT) return "Light";
if (opid == COMPOSITE_COLORIZE) return "Colorize";
if (opid == COMPOSITE_HUE) return "Hue";
if ((opid == COMPOSITE_ADD) ||
(opid == COMPOSITE_LINEAR_DODGE)) return "Add";
if (opid == COMPOSITE_INVERSE_SUBTRACT) return "Sub";
if (opid == COMPOSITE_MULT) return "Multiply";
if (opid == COMPOSITE_SCREEN) return "Screen";
// "Replace"
// "Subtitute"
if (opid == COMPOSITE_DIFF) return "Difference";
if (opid == COMPOSITE_DIVIDE) return "Divide";
if (opid == COMPOSITE_OVERLAY) return "Overlay";
if (opid == COMPOSITE_DODGE) return "Light2";
if (opid == COMPOSITE_BURN) return "Shade2";
if (opid == COMPOSITE_HARD_LIGHT) return "HardLight";
if ((opid == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) ||
(opid == COMPOSITE_SOFT_LIGHT_SVG)) return "SoftLight";
if (opid == COMPOSITE_GRAIN_EXTRACT) return "GrainExtract";
if (opid == COMPOSITE_GRAIN_MERGE) return "GrainMerge";
if (opid == COMPOSITE_SUBTRACT) return "Sub2";
if (opid == COMPOSITE_DARKEN) return "Darken";
if (opid == COMPOSITE_LIGHTEN) return "Lighten";
if (opid == COMPOSITE_SATURATION) return "Saturation";
return "Color";
}
KisImageBuilder_Result CSVSaver::getLayer(CSVLayerRecord* layer, KisDocument* exportDoc, KisKeyframeSP keyframe, const QString &path, int frame, int idx)
{
//render to the temp layer
- KisImageWSP image = exportDoc->image();
+ KisImageSP image = exportDoc->image();
KisPaintDeviceSP device = image->rootLayer()->firstChild()->projection();
if (!keyframe.isNull()) {
layer->channel->fetchFrame(keyframe, device);
} else {
device->makeCloneFrom(layer->layer->projection(),image->bounds()); // without animation
}
QRect bounds = device->exactBounds();
if (bounds.isEmpty()) {
layer->last = ""; //empty frame
return KisImageBuilder_RESULT_OK;
}
layer->last = QString("frame%1-%2.png").arg(idx + 1,5,10,QChar('0')).arg(frame,5,10,QChar('0'));
QString filename = path;
filename.append(layer->last);
//save to PNG
-
KisSequentialConstIterator it(device, image->bounds());
const KoColorSpace* cs = device->colorSpace();
bool isThereAlpha = false;
do {
if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) {
isThereAlpha = true;
break;
}
} while (it.nextPixel());
if (!KisPNGConverter::isColorSpaceSupported(cs)) {
device = new KisPaintDevice(*device.data());
KUndo2Command *cmd= device->convertTo(KoColorSpaceRegistry::instance()->rgb8());
delete cmd;
}
KisPNGOptions options;
options.alpha = isThereAlpha;
options.interlace = false;
options.compression = 8;
options.tryToSaveAsIndexed = false;
options.transparencyFillColor = QColor(0,0,0);
options.saveSRGBProfile = true; //TVPaint can use only sRGB
options.forceSRGB = false;
KisPNGConverter kpc(exportDoc);
KisImageBuilder_Result result = kpc.buildFile(filename, image->bounds(),
image->xRes(), image->yRes(), device,
image->beginAnnotations(), image->endAnnotations(),
options, (KisMetaData::Store* )0 );
return result;
}
void CSVSaver::createTempImage(KisDocument* exportDoc)
{
- exportDoc->setAutoSave(0);
+ exportDoc->setAutoSaveDelay(0);
exportDoc->setOutputMimeType("image/png");
exportDoc->setFileBatchMode(true);
- KisImageWSP exportImage = new KisImage(exportDoc->createUndoStore(),
+ KisImageSP exportImage = new KisImage(exportDoc->createUndoStore(),
m_image->width(), m_image->height(), m_image->colorSpace(),
QString());
exportImage->setResolution(m_image->xRes(), m_image->yRes());
exportDoc->setCurrentImage(exportImage);
KisPaintLayer* paintLayer = new KisPaintLayer(exportImage, "paint device", OPACITY_OPAQUE_U8);
exportImage->addNode(paintLayer, exportImage->rootLayer(), KisLayerSP(0));
}
-KisImageBuilder_Result CSVSaver::buildAnimation(const QString &filename)
+KisImageBuilder_Result CSVSaver::buildAnimation(QIODevice *io)
{
- if (!m_image)
+ if (!m_image) {
return KisImageBuilder_RESULT_EMPTY;
- return encode(filename);
+ }
+ return encode(io);
}
void CSVSaver::cancel()
{
m_stop = true;
}
diff --git a/plugins/impex/csv/csv_saver.h b/plugins/impex/csv/csv_saver.h
index c5119533bd..edfeb37efe 100644
--- a/plugins/impex/csv/csv_saver.h
+++ b/plugins/impex/csv/csv_saver.h
@@ -1,61 +1,61 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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 CSV_SAVER_H_
#define CSV_SAVER_H_
#include <QObject>
-
+#include <QIODevice>
#include "kis_types.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_png_converter.h"
/* The KisImageBuilder_Result definitions come from kis_png_converter.h here */
#include "csv_layer_record.h"
class KisDocument;
class CSVSaver : public QObject {
Q_OBJECT
public:
CSVSaver(KisDocument* doc, bool batchMode);
virtual ~CSVSaver();
- KisImageBuilder_Result buildAnimation(const QString &filename);
- KisImageWSP image();
+ KisImageBuilder_Result buildAnimation(QIODevice *io);
+ KisImageSP image();
private:
- KisImageBuilder_Result encode(const QString &filename);
+ KisImageBuilder_Result encode(QIODevice *io);
KisImageBuilder_Result getLayer(CSVLayerRecord* , KisDocument* , KisKeyframeSP, const QString &, int, int);
void createTempImage(KisDocument* );
QString convertToBlending(const QString &);
private Q_SLOTS:
void cancel();
private:
- KisImageWSP m_image;
+ KisImageSP m_image;
KisDocument* m_doc;
bool m_batchMode;
bool m_stop;
};
#endif
diff --git a/plugins/impex/csv/kis_csv_export.cpp b/plugins/impex/csv/kis_csv_export.cpp
index 6d6986364a..402dc70614 100644
--- a/plugins/impex/csv/kis_csv_export.cpp
+++ b/plugins/impex/csv/kis_csv_export.cpp
@@ -1,109 +1,80 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_csv_export.h"
#include <QCheckBox>
#include <QSlider>
-#include <QMessageBox>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <QApplication>
+#include <KisExportCheckRegistry.h>
#include <KisImportExportManager.h>
-#include <KisFilterChain.h>
#include <KoColorSpaceConstants.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include "csv_saver.h"
K_PLUGIN_FACTORY_WITH_JSON(KisCSVExportFactory, "krita_csv_export.json", registerPlugin<KisCSVExport>();)
-bool checkHomogenity(KisNodeSP root)
-{
- bool res = true;
- KisNodeSP child = root->firstChild();
-
- while (child) {
- if (child->childCount() > 0) {
- res= false;
- break;
- }
- child = child->nextSibling();
- }
- return res;
-}
-
KisCSVExport::KisCSVExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisCSVExport::~KisCSVExport()
{
}
-KisImportExportFilter::ConversionStatus KisCSVExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisCSVExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
-
- dbgFile << "CSV export! From:" << from << ", To:" << to << "";
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument* input = inputDocument();
- QString filename = outputFile();
+ CSVSaver kpc(document, batchMode());
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
-
- if (!checkHomogenity(input->image()->rootLayer())) {
- if (!getBatchMode()) {
- QMessageBox::critical(0,
- i18nc("@title:window", "CSV Export Error"),
- i18n("Unable to save to the CSV format.\n"
- "The CSV format not supports layer groups or masked layers."));
- }
- return KisImportExportFilter::InvalidFormat;
- }
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- CSVSaver kpc(input, getBatchMode());
KisImageBuilder_Result res;
- if ((res = kpc.buildAnimation(filename)) == KisImageBuilder_RESULT_OK) {
- dbgFile <<"success !";
+ if ((res = kpc.buildAnimation(io)) == KisImageBuilder_RESULT_OK) {
+ dbgFile <<"success!";
return KisImportExportFilter::OK;
}
dbgFile <<" Result =" << res;
if (res == KisImageBuilder_RESULT_CANCEL)
return KisImportExportFilter::ProgressCancelled;
return KisImportExportFilter::InternalError;
}
+void KisCSVExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("AnimationCheck")->create(KisExportCheckBase::SUPPORTED));
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "CSV");
+ addCapability(KisExportCheckRegistry::instance()->get("ColorModelPerLayerCheck/" + RGBAColorModelID.id() + "/" + Integer8BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED));
+}
+
#include "kis_csv_export.moc"
diff --git a/plugins/impex/csv/kis_csv_export.h b/plugins/impex/csv/kis_csv_export.h
index 71ffff081c..401af7e3ba 100644
--- a/plugins/impex/csv/kis_csv_export.h
+++ b/plugins/impex/csv/kis_csv_export.h
@@ -1,37 +1,38 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_CSV_EXPORT_H_
#define _KIS_CSV_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisCSVExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisCSVExport(QObject *parent, const QVariantList &);
virtual ~KisCSVExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/csv/kis_csv_import.cpp b/plugins/impex/csv/kis_csv_import.cpp
index 591dbcd5ab..4f39d5eb0d 100644
--- a/plugins/impex/csv/kis_csv_import.cpp
+++ b/plugins/impex/csv/kis_csv_import.cpp
@@ -1,96 +1,77 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_csv_import.h"
#include <kpluginfactory.h>
#include <QDebug>
#include <QFileInfo>
-#include <KisFilterChain.h>
#include <KisImportExportManager.h>
#include <KisDocument.h>
#include <kis_image.h>
#include "csv_loader.h"
K_PLUGIN_FACTORY_WITH_JSON(CSVImportFactory, "krita_csv_import.json", registerPlugin<KisCSVImport>();)
KisCSVImport::KisCSVImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisCSVImport::~KisCSVImport()
{
}
-KisImportExportFilter::ConversionStatus KisCSVImport::convert(const QByteArray&, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisCSVImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile << "Importing using CSVImport!";
+ CSVLoader ib(document, batchMode());
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- doc -> prepareForImport();
+ KisImageBuilder_Result result = ib.buildAnimation(io, filename());
- if (!filename.isEmpty() && QFileInfo(filename).exists()) {
-
- CSVLoader ib(doc, getBatchMode());
-
- KisImageBuilder_Result result = ib.buildAnimation(filename);
-
- switch (result) {
- case KisImageBuilder_RESULT_UNSUPPORTED:
- return KisImportExportFilter::NotImplemented;
- case KisImageBuilder_RESULT_INVALID_ARG:
- return KisImportExportFilter::BadMimeType;
- case KisImageBuilder_RESULT_NO_URI:
- case KisImageBuilder_RESULT_NOT_EXIST:
- case KisImageBuilder_RESULT_NOT_LOCAL:
- qDebug() << "ib returned KisImageBuilder_RESULT_NOT_LOCAL";
- return KisImportExportFilter::FileNotFound;
- case KisImageBuilder_RESULT_BAD_FETCH:
- case KisImageBuilder_RESULT_EMPTY:
- return KisImportExportFilter::ParsingError;
- case KisImageBuilder_RESULT_FAILURE:
- return KisImportExportFilter::InternalError;
- case KisImageBuilder_RESULT_CANCEL:
- return KisImportExportFilter::ProgressCancelled;
- case KisImageBuilder_RESULT_OK:
- doc -> setCurrentImage( ib.image());
- return KisImportExportFilter::OK;
- default:
- return KisImportExportFilter::StorageCreationError;
- }
+ switch (result) {
+ case KisImageBuilder_RESULT_UNSUPPORTED:
+ return KisImportExportFilter::NotImplemented;
+ case KisImageBuilder_RESULT_INVALID_ARG:
+ return KisImportExportFilter::BadMimeType;
+ case KisImageBuilder_RESULT_NO_URI:
+ case KisImageBuilder_RESULT_NOT_EXIST:
+ case KisImageBuilder_RESULT_NOT_LOCAL:
+ qDebug() << "ib returned KisImageBuilder_RESULT_NOT_LOCAL";
+ return KisImportExportFilter::FileNotFound;
+ case KisImageBuilder_RESULT_BAD_FETCH:
+ case KisImageBuilder_RESULT_EMPTY:
+ return KisImportExportFilter::ParsingError;
+ case KisImageBuilder_RESULT_FAILURE:
+ return KisImportExportFilter::InternalError;
+ case KisImageBuilder_RESULT_CANCEL:
+ return KisImportExportFilter::ProgressCancelled;
+ case KisImageBuilder_RESULT_OK:
+ document -> setCurrentImage( ib.image());
+ return KisImportExportFilter::OK;
+ default:
+ return KisImportExportFilter::StorageCreationError;
}
- return KisImportExportFilter::StorageCreationError;
+ return KisImportExportFilter::InternalError;
}
#include <kis_csv_import.moc>
diff --git a/plugins/impex/csv/kis_csv_import.h b/plugins/impex/csv/kis_csv_import.h
index c80e93bb88..d945a5a45f 100644
--- a/plugins/impex/csv/kis_csv_import.h
+++ b/plugins/impex/csv/kis_csv_import.h
@@ -1,36 +1,36 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_CSV_IMPORT_H_
#define _KIS_CSV_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisCSVImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisCSVImport(QObject *parent, const QVariantList &);
virtual ~KisCSVImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/csv/krita_csv_export.json b/plugins/impex/csv/krita_csv_export.json
index 6e7ec06861..5b16df83d1 100644
--- a/plugins/impex/csv/krita_csv_export.json
+++ b/plugins/impex/csv/krita_csv_export.json
@@ -1,13 +1,13 @@
{
"Id": "Krita CSV Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "text/csv",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Library": "kritacsvexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "csv"
}
diff --git a/plugins/impex/csv/krita_csv_import.json b/plugins/impex/csv/krita_csv_import.json
index 70eabe7a6a..9ee07170fe 100644
--- a/plugins/impex/csv/krita_csv_import.json
+++ b/plugins/impex/csv/krita_csv_import.json
@@ -1,13 +1,13 @@
{
"Id": "Krita CSV Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "text/csv",
"X-KDE-Library": "kritacsvimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "csv"
}
diff --git a/plugins/impex/exr/CMakeLists.txt b/plugins/impex/exr/CMakeLists.txt
index 9eb79c0e2b..04a46ea845 100644
--- a/plugins/impex/exr/CMakeLists.txt
+++ b/plugins/impex/exr/CMakeLists.txt
@@ -1,41 +1,41 @@
add_subdirectory(tests)
include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR} )
add_definitions(${OPENEXR_DEFINITIONS})
set(libkritaconverter_LIB_SRCS
exr_converter.cc
kis_exr_layers_sorter.cpp
exr_extra_tags.cpp
)
set(kritaexrimport_SOURCES
exr_import.cc
${libkritaconverter_LIB_SRCS}
)
add_library(kritaexrimport MODULE ${kritaexrimport_SOURCES})
-target_link_libraries(kritaexrimport kritaui ${OPENEXR_LIBRARIES} )
+target_link_libraries(kritaexrimport kritaui kritalibkra ${OPENEXR_LIBRARIES} )
install(TARGETS kritaexrimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritaexrexport_SOURCES
exr_export.cc
${libkritaconverter_LIB_SRCS}
)
ki18n_wrap_ui(kritaexrexport_SOURCES exr_export_widget.ui )
add_library(kritaexrexport MODULE ${kritaexrexport_SOURCES})
-target_link_libraries(kritaexrexport kritaui ${OPENEXR_LIBRARIES} )
+target_link_libraries(kritaexrexport kritaui kritalibkra kritaimpex ${OPENEXR_LIBRARIES} )
install(TARGETS kritaexrexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( FILES
exrchannels.schema
DESTINATION ${DATA_INSTALL_DIR}/krita/metadata/schemas)
install( PROGRAMS krita_exr.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/exr/exr_converter.cc b/plugins/impex/exr/exr_converter.cc
index 32f0d17290..59d01b6fe8 100644
--- a/plugins/impex/exr/exr_converter.cc
+++ b/plugins/impex/exr/exr_converter.cc
@@ -1,1305 +1,1312 @@
/*
* 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 <QDomDocument>
#include <QFileInfo>
#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>
+#include "kis_kra_savexml_visitor.h"
+
// Do not translate!
#define HDR_LAYER "HDR Layer"
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) {}
+struct EXRConverter::Private {
+ Private()
+ : doc(0)
+ , warnedAboutChangedAlpha(false)
+ , showNotifications(false)
+ {}
KisImageSP image;
KisDocument *doc;
bool warnedAboutChangedAlpha;
bool showNotifications;
+ QString errorMessage;
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)
+EXRConverter::EXRConverter(KisDocument *doc, bool showNotifications)
+ : d(new Private)
{
- m_d->doc = doc;
- m_d->showNotifications = showNotifications;
+ d->doc = doc;
+ d->showNotifications = showNotifications;
}
-exrConverter::~exrConverter()
+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 abs(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)
+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 <i>not</i> "
"be reverted on saving the image back."
"<br/><br/>"
"This will hardly make any visual difference just keep it in mind."
"<br/><br/>"
"<note>Modified alpha will have a range from %1 to %2</note>",
alphaEpsilon<channel_type>(),
alphaNoiseThreshold<channel_type>());
if (this->showNotifications) {
QMessageBox::information(0, i18nc("@title:window", "EXR image will be modified"), msg);
} else {
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)
{
if (alphaPos >= 0) {
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)
+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)
+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)
+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)
+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; };
QString layerName = el.attribute(EXR_NAME).toUtf8();
if (layerName != QString(HDR_LAYER)) {
extraInfoLayers.insert(el.attribute(EXR_NAME).toUtf8().constData());
}
el = el.nextSiblingElement();
}
bool result = (extraInfoLayers == exrLayerNames);
if (!result) {
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();
for (; it1 != end1; ++it1, ++it2) {
dbgKrita << it1->c_str() << it2->c_str();
}
}
return result;
}
-KisImageBuilder_Result exrConverter::decode(const QString &filename)
+KisImageBuilder_Result EXRConverter::decode(const QString &filename)
{
Imf::InputFile file(QFile::encodeName(filename));
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());
+ QDomDocument extraLayersInfo = d->loadExtraLayersInfo(file.header());
// Construct 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)) {
+ !d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
// it is inconsistent anyway
extraLayersInfo = QDomDocument();
}
// Check if there are A, R, G, B channels
dbgFile << "Checking for ARGB channels, they can occur in single-layer _or_ multi-layer images:";
ExrPaintLayerInfo info;
bool topLevelRGBFound = false;
info.name = 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;
QString qname = i.name();
QStringList topLevelChannelNames = QStringList() << "A" << "R" << "G" << "B" << ".A" << ".R" << ".G" << ".B" << "A." << "R." << "G." << "B.";;
if (topLevelChannelNames.contains(qname)) {
topLevelRGBFound = true;
dbgFile << "Found top-level channel" << qname;
info.channelMap[qname] = qname;
info.updateImageType(imfTypeToKisType(channel.type));
}
// Channel names that don't contain a "." or that contain a
// "." only at the beginning or at the end are not considered
// to be part of any layer.
else if (!qname.contains('.')
|| !qname.mid(1).contains('.')
|| !qname.left(qname.size() - 1).contains('.')) {
warnFile << "Found a top-level channel that is not part of the rendered image" << qname << ". Krita will not load this channel.";
}
}
if (topLevelRGBFound) {
dbgFile << "Toplevel layer" << info.name << ":Image type:" << imageType << "Layer type" << info.imageType;
informationObjects.push_back(info);
imageType = info.imageType;
}
dbgFile << "Extra layers:" << layerNames.size();
for (std::set<std::string>::const_iterator i = layerNames.begin();i != layerNames.end(); ++i) {
info = ExrPaintLayerInfo();
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() << "layer(s)";
// 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>::const_iterator it = info.channelMap.constBegin();
QMap<QString,QString>::const_iterator end = info.channelMap.constEnd();
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;
}
}
else {
dbgFile << info.name << "has" << info.channelMap.size() << "channels, and we don't know what to do.";
}
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, "");
+ d->image = new KisImage(d->doc->createUndoStore(), width, height, colorSpace, "");
- if (!m_d->image) {
+ if (!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));
+ 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);
+ KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : d->image->rootLayer();
+ info.groupLayer = new KisGroupLayer(d->image, info.name, OPACITY_OPAQUE_U8);
+ d->image->addNode(info.groupLayer, groupLayerParent);
}
// Load the layers
for (int i = informationObjects.size() - 1; i >= 0; --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);
+ KisPaintLayerSP layer = new KisPaintLayer(d->image, info.name, OPACITY_OPAQUE_U8, info.colorSpace);
layer->setCompositeOpId(COMPOSITE_OVER);
if (!layer) {
return KisImageBuilder_RESULT_FAILURE;
}
switch (info.channelMap.size()) {
case 1:
case 2:
// Decode the data
switch (info.imageType) {
case IT_FLOAT16:
- m_d->decodeData1<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
+ 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);
+ 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 (info.imageType) {
case IT_FLOAT16:
- m_d->decodeData4<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
+ 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);
+ 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;
Q_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);
+ KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : d->image->rootLayer();
+ 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);
+ KisExrLayersSorter sorter(extraLayersInfo, d->image);
}
return KisImageBuilder_RESULT_OK;
}
-KisImageBuilder_Result exrConverter::buildImage(const QString &filename)
+KisImageBuilder_Result EXRConverter::buildImage(const QString &filename)
{
return decode(filename);
}
-KisImageWSP exrConverter::image()
+KisImageSP EXRConverter::image()
+{
+ return d->image;
+}
+
+QString EXRConverter::errorMessage() const
{
- return m_d->image;
+ return d->errorMessage;
}
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) {}
~EncoderImpl() override {}
void prepareFrameBuffer(Imf::FrameBuffer*, int line) override;
void encodeData(int line) override;
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)
{
dbgFile << "Create encoder for" << info.layer->name() << info.channels << info.layer->colorSpace()->channelCount();
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;
Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) {
encoders.push_back(encoder(file, info, width));
}
for (int y = 0; y < height; ++y) {
Imf::FrameBuffer frameBuffer;
Q_FOREACH (Encoder* encoder, encoders) {
encoder->prepareFrameBuffer(&frameBuffer, y);
}
file.setFrameBuffer(frameBuffer);
Q_FOREACH (Encoder* encoder, encoders) {
encoder->encodeData(y);
}
file.writePixels(1);
}
qDeleteAll(encoders);
}
-KisImageBuilder_Result exrConverter::buildFile(const QString &filename, KisPaintLayerSP layer)
+KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisPaintLayerSP layer)
{
if (!layer)
return KisImageBuilder_RESULT_INVALID_ARG;
- KisImageWSP image = layer->image();
+ KisImageSP image = layer->image();
if (!image)
return KisImageBuilder_RESULT_EMPTY;
// 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(filename), 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)
+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);
}
}
Q_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)
+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();
Q_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 (info.name == QString(HDR_LAYER) + ".") {
info.channels.push_back("R");
info.channels.push_back("G");
info.channels.push_back("B");
info.channels.push_back("A");
}
else {
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) {
+ }
+
+ 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) {
dbgFile << "Going to save layer" << info.name;
informationObjects.push_back(info);
}
else {
warnFile << "Will not save layer" << info.name;
layersNotSaved << node;
}
}
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)
+void EXRConverter::Private::reportLayersNotSaved(const QSet<KisNodeSP> &layersNotSaved)
{
QString layersList;
QTextStream textStream(&layersList);
textStream.setCodec("UTF-8");
Q_FOREACH (KisNodeSP node, layersNotSaved) {
- textStream << "<item>" << i18nc("@item:unsupported-node-message", "%1 (type: \"%2\")", node->name(), node->metaObject()->className()) << "</item>";
+ textStream << "<li>" << i18nc("@item:unsupported-node-message", "%1 (type: \"%2\")", node->name(), node->metaObject()->className()) << "</li>";
}
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 {
- warnKrita << "WARNING:" << msg;
- }
+ "<p>The following layers have a type that is not supported by EXR format:</p>"
+ "<r><ul>%1</ul></p>"
+ "<p><warning>these layers have <b>not</b> been saved to the final EXR file</warning></p>", layersList);
+ errorMessage = msg;
}
-QString exrConverter::Private::fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo>& informationObjects)
+QString EXRConverter::Private::fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo>& informationObjects)
{
KIS_ASSERT_RECOVER_NOOP(!informationObjects.isEmpty());
if (informationObjects.size() == 1 && informationObjects[0].name == QString(HDR_LAYER) + ".") {
return QString();
}
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 QString &filename, KisGroupLayerSP layer)
+KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisGroupLayerSP layer)
{
if (!layer)
return KisImageBuilder_RESULT_INVALID_ARG;
- KisImageWSP image = layer->image();
+ KisImageSP image = layer->image();
if (!image)
return KisImageBuilder_RESULT_EMPTY;
qint32 height = image->height();
qint32 width = image->width();
Imf::Header header(width, height);
QList<ExrPaintLayerSaveInfo> informationObjects;
- m_d->recBuildPaintLayerSaveInfo(informationObjects, "", layer);
+ d->recBuildPaintLayerSaveInfo(informationObjects, "", layer);
if(informationObjects.isEmpty()) {
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
- m_d->makeLayerNamesUnique(informationObjects);
+ d->makeLayerNamesUnique(informationObjects);
- QByteArray extraLayersInfo = m_d->fetchExtraLayersInfo(informationObjects).toUtf8();
+ QByteArray extraLayersInfo = d->fetchExtraLayersInfo(informationObjects).toUtf8();
if (!extraLayersInfo.isNull()) {
header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData()));
}
dbgFile << informationObjects.size() << " layers to save";
Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) {
if (info.pixelType < Imf::NUM_PIXELTYPES) {
Q_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(filename), header);
encodeData(file, informationObjects, width, height);
return KisImageBuilder_RESULT_OK;
}
-void exrConverter::cancel()
+void EXRConverter::cancel()
{
warnKrita << "WARNING: Cancelling of an EXR loading is not supported!";
}
diff --git a/plugins/impex/exr/exr_converter.h b/plugins/impex/exr/exr_converter.h
index 16ab12f695..b92d2ebd96 100644
--- a/plugins/impex/exr/exr_converter.h
+++ b/plugins/impex/exr/exr_converter.h
@@ -1,54 +1,56 @@
/*
* 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.
*/
#ifndef _EXR_CONVERTER_H_
#define _EXR_CONVERTER_H_
#include <stdio.h>
#include <QObject>
#include "kis_types.h"
#include <KisImageBuilderResult.h>
class KisDocument;
-class exrConverter : public QObject
+class EXRConverter : public QObject
{
Q_OBJECT
public:
- exrConverter(KisDocument *doc, bool showNotifications);
- virtual ~exrConverter();
+ EXRConverter(KisDocument *doc, bool showNotifications);
+ virtual ~EXRConverter();
public:
KisImageBuilder_Result buildImage(const QString &filename);
KisImageBuilder_Result buildFile(const QString &filename, KisPaintLayerSP layer);
KisImageBuilder_Result buildFile(const QString &filename, KisGroupLayerSP layer);
/**
* Retrieve the constructed image
*/
- KisImageWSP image();
+ KisImageSP image();
+ QString errorMessage() const;
private:
KisImageBuilder_Result decode(const QString &filename);
+
public Q_SLOTS:
virtual void cancel();
private:
struct Private;
- const QScopedPointer<Private> m_d;
+ const QScopedPointer<Private> d;
};
#endif
diff --git a/plugins/impex/exr/exr_export.cc b/plugins/impex/exr/exr_export.cc
index 237ea7117d..c4642f5769 100644
--- a/plugins/impex/exr/exr_export.cc
+++ b/plugins/impex/exr/exr_export.cc
@@ -1,186 +1,169 @@
/*
* 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_export.h"
#include <QCheckBox>
#include <QSlider>
#include <QApplication>
-#include <KoDialog.h>
#include <kpluginfactory.h>
#include <QFileInfo>
-#include <KisFilterChain.h>
+#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceConstants.h>
#include <KisImportExportManager.h>
+#include <KisExportCheckRegistry.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include "exr_converter.h"
class KisExternalLayer;
-K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_exr_export.json", registerPlugin<exrExport>();)
+K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_exr_export.json", registerPlugin<EXRExport>();)
-exrExport::exrExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
+EXRExport::EXRExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
-exrExport::~exrExport()
+EXRExport::~EXRExport()
{
}
-KisPropertiesConfigurationSP exrExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
+KisPropertiesConfigurationSP EXRExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("flatten", false);
return cfg;
}
-KisPropertiesConfigurationSP exrExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
+KisPropertiesConfigurationSP EXRExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
{
KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
- QString filterConfig = KisConfig().exportConfiguration("EXR");
+ QString filterConfig = KisConfig().exportConfiguration(mimeType());
cfg->fromXML(filterConfig, false);
return cfg;
}
-KisConfigWidget *exrExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
+KisConfigWidget *EXRExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
return new KisWdgOptionsExr(parent);
}
-KisImportExportFilter::ConversionStatus exrExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus EXRExport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP configuration)
{
- dbgFile << "EXR export! From:" << from << ", To:" << to << "";
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *input = inputDocument();
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
- KisImageWSP image = input->image();
- Q_CHECK_PTR(image);
-
- KoDialog kdb;
- kdb.setWindowTitle(i18n("OpenEXR Export Options"));
- kdb.setButtons(KoDialog::Ok | KoDialog::Cancel);
- KisConfigWidget *wdg = createConfigurationWidget(&kdb, from, to);
- kdb.setMainWidget(wdg);
- kdb.resize(kdb.minimumSize());
-
- // If a configuration object was passed to the convert method, we use that, otherwise we load from the settings
- KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
- if (configuration) {
- cfg->fromXML(configuration->toXML());
- }
- else {
- cfg = lastSavedConfiguration(from, to);
- }
- wdg->setConfiguration(cfg);
-
- if (!getBatchMode() ) {
- QApplication::restoreOverrideCursor();
- if (kdb.exec() == QDialog::Rejected) {
- return KisImportExportFilter::UserCancelled;
- }
- cfg = wdg->configuration();
- KisConfig().setExportConfiguration("EXR", *cfg.data());
- }
-
- QString filename = outputFile();
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
+ KisImageSP image = document->image();
- exrConverter kpc(input, !getBatchMode());
+ EXRConverter exrConverter(document, !batchMode());
KisImageBuilder_Result res;
- if (cfg->getBool("flatten")) {
+ if (configuration->getBool("flatten")) {
// the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(input->image()->locked());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
- res = kpc.buildFile(filename, l);
+ res = exrConverter.buildFile(filename(), l);
}
else {
// the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(input->image()->locked());
- res = kpc.buildFile(filename, image->rootLayer());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
+ res = exrConverter.buildFile(filename(), image->rootLayer());
}
dbgFile << " Result =" << res;
switch (res) {
case KisImageBuilder_RESULT_INVALID_ARG:
- input->setErrorMessage(i18n("This layer cannot be saved to EXR."));
+ document->setErrorMessage(i18n("This layer cannot be saved to EXR."));
return KisImportExportFilter::WrongFormat;
case KisImageBuilder_RESULT_EMPTY:
- input->setErrorMessage(i18n("The layer does not have an image associated with it."));
+ document->setErrorMessage(i18n("The layer does not have an image associated with it."));
return KisImportExportFilter::WrongFormat;
case KisImageBuilder_RESULT_NO_URI:
- input->setErrorMessage(i18n("The filename is empty."));
+ document->setErrorMessage(i18n("The filename is empty."));
return KisImportExportFilter::CreationError;
case KisImageBuilder_RESULT_NOT_LOCAL:
- input->setErrorMessage(i18n("EXR images cannot be saved remotely."));
+ document->setErrorMessage(i18n("EXR images cannot be saved remotely."));
return KisImportExportFilter::InternalError;
case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
- input->setErrorMessage(i18n("Colorspace not supported: EXR images must be 16 or 32 bits floating point RGB."));
+ document->setErrorMessage(i18n("Colorspace not supported: EXR images must be 16 or 32 bits floating point RGB."));
return KisImportExportFilter::WrongFormat;
case KisImageBuilder_RESULT_OK:
+ document->setErrorMessage(exrConverter.errorMessage());
return KisImportExportFilter::OK;
default:
break;
}
- input->setErrorMessage(i18n("Internal Error"));
+ document->setErrorMessage(i18n("Internal Error"));
return KisImportExportFilter::InternalError;
}
+void EXRExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("NodeTypeCheck/KisGroupLayer")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
+
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Float16BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Float32BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Float16BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Float32BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayColorModelID, Float16BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayColorModelID, Float32BitsColorDepthID)
+ << QPair<KoID, KoID>(XYZAColorModelID, Float16BitsColorDepthID)
+ << QPair<KoID, KoID>(XYZAColorModelID, Float32BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "TIFF");
+}
+
+
+
void KisWdgOptionsExr::setConfiguration(const KisPropertiesConfigurationSP cfg)
{
chkFlatten->setChecked(cfg->getBool("flatten", false));
}
KisPropertiesConfigurationSP KisWdgOptionsExr::configuration() const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("flatten", chkFlatten->isChecked());
return cfg;
}
-
#include <exr_export.moc>
diff --git a/plugins/impex/exr/exr_export.h b/plugins/impex/exr/exr_export.h
index 96e8fbc9e8..a1950b9a9a 100644
--- a/plugins/impex/exr/exr_export.h
+++ b/plugins/impex/exr/exr_export.h
@@ -1,58 +1,59 @@
/*
* 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.
*/
#ifndef _EXR_EXPORT_H_
#define _EXR_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
#include <kis_config_widget.h>
#include "ui_exr_export_widget.h"
class KisWdgOptionsExr : public KisConfigWidget, public Ui::ExrExportWidget
{
Q_OBJECT
public:
KisWdgOptionsExr(QWidget *parent)
: KisConfigWidget(parent)
{
setupUi(this);
}
void setConfiguration(const KisPropertiesConfigurationSP cfg);
KisPropertiesConfigurationSP configuration() const;
};
-class exrExport : public KisImportExportFilter
+class EXRExport : public KisImportExportFilter
{
Q_OBJECT
public:
- exrExport(QObject *parent, const QVariantList &);
- virtual ~exrExport();
+ EXRExport(QObject *parent, const QVariantList &);
+ virtual ~EXRExport();
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const;
KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
-public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
+
};
#endif
diff --git a/plugins/impex/exr/exr_import.cc b/plugins/impex/exr/exr_import.cc
index f04cb71a05..dace67bee2 100644
--- a/plugins/impex/exr/exr_import.cc
+++ b/plugins/impex/exr/exr_import.cc
@@ -1,107 +1,83 @@
/*
* 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_import.h"
#include <kpluginfactory.h>
#include <QFileInfo>
-#include <KisFilterChain.h>
#include <KisImportExportManager.h>
#include <KisDocument.h>
#include <kis_image.h>
#include "exr_converter.h"
K_PLUGIN_FACTORY_WITH_JSON(ImportFactory, "krita_exr_import.json", registerPlugin<exrImport>();)
exrImport::exrImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
exrImport::~exrImport()
{
}
-KisImportExportFilter::ConversionStatus exrImport::convert(const QByteArray&, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus exrImport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile << "Importing using EXRImport!";
+ EXRConverter ib(document, !batchMode());
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- doc->prepareForImport();
-
- if (!filename.isEmpty()) {
-
- if (!QFileInfo(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
- }
+ switch (ib.buildImage(filename())) {
+ case KisImageBuilder_RESULT_UNSUPPORTED:
+ case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
+ document->setErrorMessage(i18n("Krita does support this type of EXR file."));
+ return KisImportExportFilter::NotImplemented;
- exrConverter ib(doc, !getBatchMode());
-
-
- switch (ib.buildImage(filename)) {
- case KisImageBuilder_RESULT_UNSUPPORTED:
- case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
- doc->setErrorMessage(i18n("Krita does support this type of EXR file."));
- return KisImportExportFilter::NotImplemented;
-
- case KisImageBuilder_RESULT_INVALID_ARG:
- doc->setErrorMessage(i18n("This is not an EXR file."));
- return KisImportExportFilter::BadMimeType;
-
- case KisImageBuilder_RESULT_NO_URI:
- case KisImageBuilder_RESULT_NOT_LOCAL:
- doc->setErrorMessage(i18n("The EXR file does not exist."));
- return KisImportExportFilter::FileNotFound;
+ case KisImageBuilder_RESULT_INVALID_ARG:
+ document->setErrorMessage(i18n("This is not an EXR file."));
+ return KisImportExportFilter::BadMimeType;
- case KisImageBuilder_RESULT_BAD_FETCH:
- case KisImageBuilder_RESULT_EMPTY:
- doc->setErrorMessage(i18n("The EXR is corrupted."));
- return KisImportExportFilter::ParsingError;
+ case KisImageBuilder_RESULT_NO_URI:
+ case KisImageBuilder_RESULT_NOT_LOCAL:
+ document->setErrorMessage(i18n("The EXR file does not exist."));
+ return KisImportExportFilter::FileNotFound;
- case KisImageBuilder_RESULT_FAILURE:
- doc->setErrorMessage(i18n("Krita could not create a new image."));
- return KisImportExportFilter::InternalError;
+ case KisImageBuilder_RESULT_BAD_FETCH:
+ case KisImageBuilder_RESULT_EMPTY:
+ document->setErrorMessage(i18n("The EXR is corrupted."));
+ return KisImportExportFilter::ParsingError;
- case KisImageBuilder_RESULT_OK:
- Q_ASSERT(ib.image());
- doc -> setCurrentImage(ib.image());
- return KisImportExportFilter::OK;
+ case KisImageBuilder_RESULT_FAILURE:
+ document->setErrorMessage(i18n("Krita could not create a new image."));
+ return KisImportExportFilter::InternalError;
- default:
- break;
- }
+ case KisImageBuilder_RESULT_OK:
+ Q_ASSERT(ib.image());
+ document -> setCurrentImage(ib.image());
+ return KisImportExportFilter::OK;
+ default:
+ break;
}
+
return KisImportExportFilter::StorageCreationError;
}
#include <exr_import.moc>
diff --git a/plugins/impex/exr/exr_import.h b/plugins/impex/exr/exr_import.h
index 646804c10b..eb6ed2cb5f 100644
--- a/plugins/impex/exr/exr_import.h
+++ b/plugins/impex/exr/exr_import.h
@@ -1,37 +1,37 @@
/*
* 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.
*/
#ifndef EXR_IMPORT_H_
#define EXR_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class exrImport : public KisImportExportFilter
{
Q_OBJECT
public:
exrImport(QObject *parent, const QVariantList &);
virtual ~exrImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/exr/kis_exr_layers_sorter.cpp b/plugins/impex/exr/kis_exr_layers_sorter.cpp
index a72e9a191a..a127c467ef 100644
--- a/plugins/impex/exr/kis_exr_layers_sorter.cpp
+++ b/plugins/impex/exr/kis_exr_layers_sorter.cpp
@@ -1,176 +1,176 @@
/*
* 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_exr_layers_sorter.h"
#include <QDomDocument>
#include <QDomElement>
#include "kis_image.h"
#include "exr_extra_tags.h"
-#include "kra/kis_kra_savexml_visitor.h"
+#include "kis_kra_savexml_visitor.h"
#include "kis_paint_layer.h"
struct KisExrLayersSorter::Private
{
- Private(const QDomDocument &_extraData, KisImageWSP _image)
+ Private(const QDomDocument &_extraData, KisImageSP _image)
: extraData(_extraData), image(_image) {}
const QDomDocument &extraData;
- KisImageWSP image;
+ KisImageSP image;
QMap<QString, QDomElement> pathToElementMap;
QMap<QString, int> pathToOrderingMap;
QMap<KisNodeSP, int> nodeToOrderingMap;
void createOrderingMap();
void processLayers(KisNodeSP root);
void sortLayers(KisNodeSP root);
};
QString getNodePath(KisNodeSP node) {
KIS_ASSERT_RECOVER(node) { return "UNDEFINED"; }
QString path;
KisNodeSP parentNode = node->parent();
while(parentNode) {
if (!path.isEmpty()) {
path.prepend(".");
}
path.prepend(node->name());
node = parentNode;
parentNode = node->parent();
}
return path;
}
void KisExrLayersSorter::Private::createOrderingMap()
{
int index = 0;
QDomElement el = extraData.documentElement().firstChildElement();
while (!el.isNull()) {
QString path = el.attribute(EXR_NAME);
pathToElementMap.insert(path, el);
pathToOrderingMap.insert(path, index);
el = el.nextSiblingElement();
index++;
}
}
template <typename T>
T fetchMapValueLazy(const QMap<QString, T> &map, QString path)
{
if (map.contains(path)) return map[path];
typename QMap<QString, T>::const_iterator it = map.constBegin();
typename QMap<QString, T>::const_iterator end = map.constEnd();
for (; it != end; ++it) {
if (it.key().startsWith(path)) {
return it.value();
}
}
return T();
}
void KisExrLayersSorter::Private::processLayers(KisNodeSP root)
{
if (root && root->parent()) {
QString path = getNodePath(root);
nodeToOrderingMap.insert(root, fetchMapValueLazy(pathToOrderingMap, path));
if (KisPaintLayer *paintLayer = dynamic_cast<KisPaintLayer*>(root.data())) {
KisSaveXmlVisitor::loadPaintLayerAttributes(pathToElementMap[path], paintLayer);
}
}
KisNodeSP child = root->firstChild();
while (child) {
processLayers(child);
child = child->nextSibling();
}
}
struct CompareNodesFunctor
{
CompareNodesFunctor(const QMap<KisNodeSP, int> &map)
: m_nodeToOrderingMap(map) {}
bool operator() (KisNodeSP lhs, KisNodeSP rhs) {
return m_nodeToOrderingMap[lhs] < m_nodeToOrderingMap[rhs];
}
private:
const QMap<KisNodeSP, int> &m_nodeToOrderingMap;
};
void KisExrLayersSorter::Private::sortLayers(KisNodeSP root)
{
QList<KisNodeSP> childNodes;
// first move all the children to the list
KisNodeSP child = root->firstChild();
while (child) {
KisNodeSP lastChild = child;
child = child->nextSibling();
childNodes.append(lastChild);
image->removeNode(lastChild);
}
// sort the list
qStableSort(childNodes.begin(), childNodes.end(), CompareNodesFunctor(nodeToOrderingMap));
// put the children back
Q_FOREACH (KisNodeSP node, childNodes) {
image->addNode(node, root, root->childCount());
}
// recursive calls
child = root->firstChild();
while (child) {
sortLayers(child);
child = child->nextSibling();
}
}
-KisExrLayersSorter::KisExrLayersSorter(const QDomDocument &extraData, KisImageWSP image)
+KisExrLayersSorter::KisExrLayersSorter(const QDomDocument &extraData, KisImageSP image)
: m_d(new Private(extraData, image))
{
KIS_ASSERT_RECOVER_RETURN(!extraData.isNull());
m_d->createOrderingMap();
m_d->processLayers(image->root());
m_d->sortLayers(image->root());
}
KisExrLayersSorter::~KisExrLayersSorter()
{
}
diff --git a/plugins/impex/exr/kis_exr_layers_sorter.h b/plugins/impex/exr/kis_exr_layers_sorter.h
index 76dbf4c04b..b00876799c 100644
--- a/plugins/impex/exr/kis_exr_layers_sorter.h
+++ b/plugins/impex/exr/kis_exr_layers_sorter.h
@@ -1,39 +1,39 @@
/*
* 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_EXR_LAYERS_SORTER_H
#define __KIS_EXR_LAYERS_SORTER_H
#include "kis_types.h"
#include <QScopedPointer>
class QDomDocument;
class KisExrLayersSorter
{
public:
- KisExrLayersSorter(const QDomDocument &extraData, KisImageWSP image);
+ KisExrLayersSorter(const QDomDocument &extraData, KisImageSP image);
~KisExrLayersSorter();
private:
struct Private;
QScopedPointer<Private> m_d;
};
#endif /* __KIS_EXR_LAYERS_SORTER_H */
diff --git a/plugins/impex/exr/krita_exr_export.json b/plugins/impex/exr/krita_exr_export.json
index 4cfd43a888..fda26ce137 100644
--- a/plugins/impex/exr/krita_exr_export.json
+++ b/plugins/impex/exr/krita_exr_export.json
@@ -1,13 +1,12 @@
{
"Icon": "",
"Id": "Krita exr Export Filter",
"Type": "Service",
"X-KDE-Export": "image/x-exr,application/x-extension-exr",
- "X-KDE-Import": "application/x-krita",
"X-KDE-Library": "kritaexrexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "exr"
}
diff --git a/plugins/impex/exr/krita_exr_import.json b/plugins/impex/exr/krita_exr_import.json
index 0f75567f9f..f95e1ec32c 100644
--- a/plugins/impex/exr/krita_exr_import.json
+++ b/plugins/impex/exr/krita_exr_import.json
@@ -1,13 +1,12 @@
{
"Icon": "",
"Id": "Krita exr Import Filter",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
"X-KDE-Import": "image/x-exr,application/x-extension-exr",
"X-KDE-Library": "kritaexrimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "exr"
}
diff --git a/plugins/impex/exr/tests/kis_exr_test.cpp b/plugins/impex/exr/tests/kis_exr_test.cpp
index dbf211212e..9b6d082dd5 100644
--- a/plugins/impex/exr/tests/kis_exr_test.cpp
+++ b/plugins/impex/exr/tests/kis_exr_test.cpp
@@ -1,97 +1,95 @@
/*
* 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_exr_test.h"
#include <QTest>
#include <QCoreApplication>
#include <QTest>
#include <half.h>
#include <KisMimeDatabase.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
void KisExrTest::testFiles()
{
TestUtil::testFiles(QString(FILES_DATA_DIR) + "/sources", QStringList(), QString(), 1);
}
void KisExrTest::testRoundTrip()
{
QString inputFileName(TestUtil::fetchDataFileLazy("CandleGlass.exr"));
KisDocument *doc1 = KisPart::instance()->createDocument();
KisImportExportManager manager(doc1);
manager.setBatchMode(true);
- KisImportExportFilter::ConversionStatus status;
- QString s = manager.importDocument(inputFileName, QString(),
- status);
+ KisImportExportFilter::ConversionStatus status = manager.importDocument(inputFileName, QString());
QCOMPARE(status, KisImportExportFilter::OK);
QVERIFY(doc1->image());
QTemporaryFile savedFile(QDir::tempPath() + QLatin1String("/krita_XXXXXX") + QLatin1String(".exr"));
savedFile.setAutoRemove(false);
savedFile.open();
QString savedFileName(savedFile.fileName());
QString typeName = KisMimeDatabase::mimeTypeForFile(savedFileName);
QByteArray mimeType(typeName.toLatin1());
status = manager.exportDocument(savedFileName, mimeType);
QVERIFY(QFileInfo(savedFileName).exists());
{
KisDocument *doc2 = KisPart::instance()->createDocument();
KisImportExportManager manager(doc2);
manager.setBatchMode(true);
- s = manager.importDocument(savedFileName, QString(), status);
+ status = manager.importDocument(savedFileName, QString());
QCOMPARE(status, KisImportExportFilter::OK);
QVERIFY(doc2->image());
QVERIFY(TestUtil::comparePaintDevicesClever<half>(
doc1->image()->root()->firstChild()->paintDevice(),
doc2->image()->root()->firstChild()->paintDevice(),
0.01 /* meaningless alpha */));
delete doc2;
}
savedFile.close();
delete doc1;
}
QTEST_MAIN(KisExrTest)
diff --git a/plugins/impex/heightmap/CMakeLists.txt b/plugins/impex/heightmap/CMakeLists.txt
index 30376ce509..db02dddacd 100644
--- a/plugins/impex/heightmap/CMakeLists.txt
+++ b/plugins/impex/heightmap/CMakeLists.txt
@@ -1,26 +1,26 @@
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set(kritaheightmapimport_SOURCES
kis_heightmap_import.cpp
)
ki18n_wrap_ui(kritaheightmapimport_SOURCES kis_wdg_options_heightmap.ui )
add_library(kritaheightmapimport MODULE ${kritaheightmapimport_SOURCES})
target_link_libraries(kritaheightmapimport kritaui )
install(TARGETS kritaheightmapimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritaheightmapexport_SOURCES
kis_heightmap_export.cpp
)
ki18n_wrap_ui(kritaheightmapexport_SOURCES kis_wdg_options_heightmap.ui )
add_library(kritaheightmapexport MODULE ${kritaheightmapexport_SOURCES})
-target_link_libraries(kritaheightmapexport kritaui )
+target_link_libraries(kritaheightmapexport kritaui kritaimpex)
install(TARGETS kritaheightmapexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_heightmap.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/heightmap/kis_heightmap_export.cpp b/plugins/impex/heightmap/kis_heightmap_export.cpp
index f55e15ec7f..c25f451362 100644
--- a/plugins/impex/heightmap/kis_heightmap_export.cpp
+++ b/plugins/impex/heightmap/kis_heightmap_export.cpp
@@ -1,193 +1,150 @@
/*
* 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 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_heightmap_export.h"
#include <qendian.h>
#include <QDataStream>
#include <QApplication>
-#include <QMessageBox>
#include <kpluginfactory.h>
#include <KoColorSpace.h>
#include <KoColorSpaceConstants.h>
-#include <KisFilterChain.h>
-#include <KisImportExportManager.h>
#include <KoColorSpaceTraits.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
-#include <KoDialog.h>
+#include <KisImportExportManager.h>
+#include <KisExportCheckRegistry.h>
#include <kis_debug.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include <kis_iterator_ng.h>
#include <kis_random_accessor_ng.h>
#include <kis_config_widget.h>
K_PLUGIN_FACTORY_WITH_JSON(KisHeightMapExportFactory, "krita_heightmap_export.json", registerPlugin<KisHeightMapExport>();)
KisHeightMapExport::KisHeightMapExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisHeightMapExport::~KisHeightMapExport()
{
}
KisPropertiesConfigurationSP KisHeightMapExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("endianness", 0);
return cfg;
}
KisPropertiesConfigurationSP KisHeightMapExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
{
KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
QString filterConfig = KisConfig().exportConfiguration("HeightMap");
cfg->fromXML(filterConfig, false);
return cfg;
}
KisConfigWidget *KisHeightMapExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
return new KisWdgOptionsHeightmap(parent);
}
-KisImportExportFilter::ConversionStatus KisHeightMapExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+void KisHeightMapExport::initializeCapabilities()
{
- dbgFile << "HeightMap export! From:" << from << ", To:" << to;
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *inputDoc = inputDocument();
- QString filename = outputFile();
-
- if (!inputDoc)
- return KisImportExportFilter::NoDocumentCreated;
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- KisImageWSP image = inputDoc->image();
- Q_CHECK_PTR(image);
-
- if (inputDoc->image()->width() != inputDoc->image()->height()) {
- inputDoc->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not square"));
- return KisImportExportFilter::WrongFormat;
+ if (mimeType() == "image/x-r8") {
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "R8 Heightmap");
}
-
- if (inputDoc->image()->colorSpace()->colorModelId() != GrayAColorModelID) {
- inputDoc->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not grayscale"));
- return KisImportExportFilter::WrongFormat;
+ else if (mimeType() == "image/x-r16") {
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer16BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "R16 Heightmap");
}
+}
- KoDialog kdb;
- kdb.setWindowTitle(i18n("HeightMap Export Options"));
- kdb.setButtons(KoDialog::Ok | KoDialog::Cancel);
- KisConfigWidget *wdg = createConfigurationWidget(&kdb, from, to);
- kdb.setMainWidget(wdg);
-
- QApplication::restoreOverrideCursor();
+KisImportExportFilter::ConversionStatus KisHeightMapExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
+{
+ KisImageSP image = document->image();
- // If a configuration object was passed to the convert method, we use that, otherwise we load from the settings
- KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
- if (configuration) {
- cfg->fromXML(configuration->toXML());
- }
- else {
- cfg = lastSavedConfiguration(from, to);
+ if (document->image()->width() != document->image()->height()) {
+ document->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not square"));
+ return KisImportExportFilter::WrongFormat;
}
- cfg->setProperty("width", image->width());
- wdg->setConfiguration(cfg);
- if (!getBatchMode()) {
- if (kdb.exec() == QDialog::Rejected) {
- return KisImportExportFilter::UserCancelled;
- }
- cfg = wdg->configuration();
- KisConfig().setExportConfiguration("HeightMap", *cfg.data());
- }
+ configuration->setProperty("width", image->width());
- QDataStream::ByteOrder bo = cfg->getInt("endianness", 0) ? QDataStream::BigEndian : QDataStream::LittleEndian;
+ QDataStream::ByteOrder bo = configuration->getInt("endianness", 0) ? QDataStream::BigEndian : QDataStream::LittleEndian;
bool downscale = false;
- if (to == "image/x-r8" && image->colorSpace()->colorDepthId() == Integer16BitsColorDepthID) {
-
- downscale = (QMessageBox::question(0,
- i18nc("@title:window", "Downscale Image"),
- i18n("You specified the .r8 extension for a 16 bit/channel image. Do you want to save as 8 bit? Your image data will not be changed."),
- QMessageBox::Yes | QMessageBox::No)
- == QMessageBox::Yes);
- }
-
// the image must be locked at the higher levels
KIS_SAFE_ASSERT_RECOVER_NOOP(image->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
- QFile f(filename);
- f.open(QIODevice::WriteOnly);
- QDataStream s(&f);
+ QDataStream s(io);
s.setByteOrder(bo);
KisRandomConstAccessorSP it = pd->createRandomConstAccessorNG(0, 0);
bool r16 = ((image->colorSpace()->colorDepthId() == Integer16BitsColorDepthID) && !downscale);
for (int i = 0; i < image->height(); ++i) {
for (int j = 0; j < image->width(); ++j) {
it->moveTo(i, j);
if (r16) {
s << KoGrayU16Traits::gray(const_cast<quint8*>(it->rawDataConst()));
}
else {
s << KoGrayU8Traits::gray(const_cast<quint8*>(it->rawDataConst()));
}
}
}
-
- f.close();
return KisImportExportFilter::OK;
}
void KisWdgOptionsHeightmap::setConfiguration(const KisPropertiesConfigurationSP cfg)
{
intSize->setValue(cfg->getInt("width"));
int endianness = cfg->getInt("endianness", 0);
radioMac->setChecked(endianness == 0);
}
KisPropertiesConfigurationSP KisWdgOptionsHeightmap::configuration() const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
if (radioMac->isChecked()) {
cfg->setProperty("endianness", 0);
}
else {
cfg->setProperty("endianness", 1);
}
return cfg;
}
#include "kis_heightmap_export.moc"
diff --git a/plugins/impex/heightmap/kis_heightmap_export.h b/plugins/impex/heightmap/kis_heightmap_export.h
index 97bd128722..1bfd9f0e45 100644
--- a/plugins/impex/heightmap/kis_heightmap_export.h
+++ b/plugins/impex/heightmap/kis_heightmap_export.h
@@ -1,57 +1,58 @@
/*
* 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 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.
*/
#ifndef _KIS_HeightMap_EXPORT_H_
#define _KIS_HeightMap_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
#include <kis_config_widget.h>
#include "ui_kis_wdg_options_heightmap.h"
class KisWdgOptionsHeightmap : public KisConfigWidget, public Ui::WdgOptionsHeightMap
{
Q_OBJECT
public:
KisWdgOptionsHeightmap(QWidget *parent)
: KisConfigWidget(parent)
{
setupUi(this);
}
void setConfiguration(const KisPropertiesConfigurationSP cfg);
KisPropertiesConfigurationSP configuration() const;
};
class KisHeightMapExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisHeightMapExport(QObject *parent, const QVariantList &);
virtual ~KisHeightMapExport();
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const;
KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
-public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
+
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/heightmap/kis_heightmap_import.cpp b/plugins/impex/heightmap/kis_heightmap_import.cpp
index 96ca8ccbc8..67acab26c7 100644
--- a/plugins/impex/heightmap/kis_heightmap_import.cpp
+++ b/plugins/impex/heightmap/kis_heightmap_import.cpp
@@ -1,199 +1,160 @@
/*
* 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 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_heightmap_import.h"
#include <ctype.h>
#include <QApplication>
#include <QFile>
#include <qendian.h>
#include <QCursor>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <KoDialog.h>
#include <KisImportExportManager.h>
#include <KoColorSpaceRegistry.h>
-#include <KisFilterChain.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <kis_debug.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
#include <kis_iterator_ng.h>
#include <kis_random_accessor_ng.h>
#include <kis_config.h>
#include "ui_kis_wdg_options_heightmap.h"
K_PLUGIN_FACTORY_WITH_JSON(HeightMapImportFactory, "krita_heightmap_import.json", registerPlugin<KisHeightMapImport>();)
KisHeightMapImport::KisHeightMapImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisHeightMapImport::~KisHeightMapImport()
{
}
-KisImportExportFilter::ConversionStatus KisHeightMapImport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisHeightMapImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
Q_UNUSED(configuration);
-
- KisDocument * doc = outputDocument();
-
- if (!doc) {
- return KisImportExportFilter::NoDocumentCreated;
- }
-
KoID depthId;
- if (from == "image/x-r8") {
+ if (mimeType() == "image/x-r8") {
depthId = Integer8BitsColorDepthID;
}
- else if (from == "image/x-r16") {
+ else if (mimeType() == "image/x-r16") {
depthId = Integer16BitsColorDepthID;
}
else {
- doc->setErrorMessage(i18n("The file is not 8 or 16 bits raw"));
+ document->setErrorMessage(i18n("The file is not 8 or 16 bits raw"));
return KisImportExportFilter::WrongFormat;
}
- dbgFile << "Importing using HeightMapImport!";
-
- if (to != "application/x-krita") {
- return KisImportExportFilter::BadMimeType;
- }
-
- QString filename = inputFile();
-
- if (filename.isEmpty()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- QFileInfo fi(filename);
- if (!fi.exists()) {
- return KisImportExportFilter::FileNotFound;
- }
-
QApplication::restoreOverrideCursor();
KoDialog* kdb = new KoDialog(0);
kdb->setWindowTitle(i18n("R16 HeightMap Import Options"));
kdb->setButtons(KoDialog::Ok | KoDialog::Cancel);
Ui::WdgOptionsHeightMap optionsHeightMap;
QWidget* wdg = new QWidget(kdb);
optionsHeightMap.setupUi(wdg);
kdb->setMainWidget(wdg);
QString filterConfig = KisConfig().exportConfiguration("HeightMap");
- KisPropertiesConfiguration cfg;
- cfg.fromXML(filterConfig);
-
- QFile f(filename);
+ KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration);
+ cfg->fromXML(filterConfig);
int w = 0;
int h = 0;
- if (!f.exists()) {
- doc->setErrorMessage(i18n("File does not exist."));
- return KisImportExportFilter::CreationError;
- }
-
- if (!f.isOpen()) {
- f.open(QFile::ReadOnly);
- }
-
optionsHeightMap.intSize->setValue(0);
- int endianness = cfg.getInt("endianness", 0);
+ int endianness = cfg->getInt("endianness", 0);
if (endianness == 0) {
optionsHeightMap.radioMac->setChecked(true);
}
else {
optionsHeightMap.radioPC->setChecked(true);
}
- if (!getBatchMode()) {
+ if (!batchMode()) {
if (kdb->exec() == QDialog::Rejected) {
return KisImportExportFilter::UserCancelled;
}
}
w = h = optionsHeightMap.intSize->value();
- if ((w * h * (from == "image/x-r16" ? 2 : 1)) != f.size()) {
- doc->setErrorMessage(i18n("Source file is not the right size for the specified width and height."));
- return KisImportExportFilter::WrongFormat;
- }
-
+// if ((w * h * (mimeType() == "image/x-r16" ? 2 : 1)) != f.size()) {
+// document->setErrorMessage(i18n("Source file is not the right size for the specified width and height."));
+// return KisImportExportFilter::WrongFormat;
+// }
QDataStream::ByteOrder bo = QDataStream::LittleEndian;
- cfg.setProperty("endianness", 1);
+ cfg->setProperty("endianness", 1);
if (optionsHeightMap.radioMac->isChecked()) {
bo = QDataStream::BigEndian;
- cfg.setProperty("endianness", 0);
+ cfg->setProperty("endianness", 0);
}
- KisConfig().setExportConfiguration("HeightMap", cfg);
-
- doc->prepareForImport();
+ KisConfig().setExportConfiguration(mimeType(), cfg);
- QDataStream s(&f);
+ QDataStream s(io);
s.setByteOrder(bo);
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), depthId.id(), 0);
- KisImageSP image = new KisImage(doc->createUndoStore(), w, h, colorSpace, "imported heightmap");
+ KisImageSP image = new KisImage(document->createUndoStore(), w, h, colorSpace, "imported heightmap");
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255);
KisRandomAccessorSP it = layer->paintDevice()->createRandomAccessorNG(0, 0);
bool r16 = (depthId == Integer16BitsColorDepthID);
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
it->moveTo(i, j);
if (r16) {
quint16 pixel;
s >> pixel;
KoGrayU16Traits::setGray(it->rawData(), pixel);
KoGrayU16Traits::setOpacity(it->rawData(), OPACITY_OPAQUE_F, 1);
}
else {
quint8 pixel;
s >> pixel;
KoGrayU8Traits::setGray(it->rawData(), pixel);
KoGrayU8Traits::setOpacity(it->rawData(), OPACITY_OPAQUE_F, 1);
}
}
}
image->addNode(layer.data(), image->rootLayer().data());
- doc->setCurrentImage(image);
+ document->setCurrentImage(image);
return KisImportExportFilter::OK;
}
#include "kis_heightmap_import.moc"
diff --git a/plugins/impex/heightmap/kis_heightmap_import.h b/plugins/impex/heightmap/kis_heightmap_import.h
index 9770f6b1d2..64b5bb7fb0 100644
--- a/plugins/impex/heightmap/kis_heightmap_import.h
+++ b/plugins/impex/heightmap/kis_heightmap_import.h
@@ -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 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_HeightMap_IMPORT_H_
#define _KIS_HeightMap_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisHeightMapImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisHeightMapImport(QObject *parent, const QVariantList &);
virtual ~KisHeightMapImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/heightmap/krita_heightmap_export.json b/plugins/impex/heightmap/krita_heightmap_export.json
index d8548bea02..fe1b861c89 100644
--- a/plugins/impex/heightmap/krita_heightmap_export.json
+++ b/plugins/impex/heightmap/krita_heightmap_export.json
@@ -1,14 +1,14 @@
{
"Icon": "",
"Id": "Krita HeightMap Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/x-r16,image/x-r8",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Library": "kritaheightmapexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "r16,r8"
}
diff --git a/plugins/impex/heightmap/krita_heightmap_import.json b/plugins/impex/heightmap/krita_heightmap_import.json
index f67f08866a..03ae647d17 100644
--- a/plugins/impex/heightmap/krita_heightmap_import.json
+++ b/plugins/impex/heightmap/krita_heightmap_import.json
@@ -1,14 +1,14 @@
{
"Icon": "",
"Id": "Krita HeightMap Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/x-r16,image/x-r8",
"X-KDE-Library": "kritaheightmapimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "r16,r8"
}
diff --git a/plugins/impex/jpeg/CMakeLists.txt b/plugins/impex/jpeg/CMakeLists.txt
index d9322ae181..347e46dbe3 100644
--- a/plugins/impex/jpeg/CMakeLists.txt
+++ b/plugins/impex/jpeg/CMakeLists.txt
@@ -1,44 +1,44 @@
add_subdirectory(tests)
set(ICCJPEG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/lcms")
include_directories(
${ICCJPEG_SOURCE_DIR}
${EXIV2_INCLUDE_DIR}
)
include_directories(SYSTEM
${LCMS2_INCLUDE_DIR}
)
set(libkritaconverter_LIB_SRCS
kis_jpeg_converter.cc
kis_jpeg_source.cpp
kis_jpeg_destination.cpp
${ICCJPEG_SOURCE_DIR}/iccjpeg.c
)
set(kritajpegimport_SOURCES
kis_jpeg_import.cc
${libkritaconverter_LIB_SRCS}
)
add_library(kritajpegimport MODULE ${kritajpegimport_SOURCES})
target_link_libraries(kritajpegimport kritaui ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} ${EXIV2_LIBRARIES} )
install(TARGETS kritajpegimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritajpegexport_SOURCES
kis_jpeg_export.cc
${libkritaconverter_LIB_SRCS}
)
ki18n_wrap_ui(kritajpegexport_SOURCES kis_wdg_options_jpeg.ui )
add_library(kritajpegexport MODULE ${kritajpegexport_SOURCES})
-target_link_libraries(kritajpegexport kritaui ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} ${EXIV2_LIBRARIES} )
+target_link_libraries(kritajpegexport kritaui kritaimpex ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} ${EXIV2_LIBRARIES} )
install(TARGETS kritajpegexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_jpeg.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/jpeg/kis_jpeg_converter.cc b/plugins/impex/jpeg/kis_jpeg_converter.cc
index a3d5976060..f6ddbb7c46 100644
--- a/plugins/impex/jpeg/kis_jpeg_converter.cc
+++ b/plugins/impex/jpeg/kis_jpeg_converter.cc
@@ -1,756 +1,731 @@
/*
* 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_jpeg_converter.h"
#include <stdio.h>
#include <stdint.h>
#include <KoConfig.h>
#ifdef HAVE_LCMS2
# include <lcms2.h>
#else
# include <lcms.h>
#endif
extern "C" {
#include <iccjpeg.h>
}
#include <exiv2/jpgimage.hpp>
#include <QFile>
#include <QBuffer>
#include <QApplication>
-#include <QMessageBox>
-
#include <klocalizedstring.h>
#include <QFileInfo>
#include <KoDocumentInfo.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoColor.h>
#include <KoUnit.h>
#include "KoColorModelStandardIds.h"
#include <kis_painter.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_transaction.h>
#include <kis_group_layer.h>
#include <metadata/kis_meta_data_entry.h>
#include <metadata/kis_meta_data_value.h>
#include <metadata/kis_meta_data_store.h>
#include <metadata/kis_meta_data_io_backend.h>
#include <kis_paint_device.h>
#include <kis_transform_worker.h>
#include <kis_jpeg_source.h>
#include <kis_jpeg_destination.h>
#include "kis_iterator_ng.h"
#include <KoColorModelStandardIds.h>
#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
const char photoshopMarker[] = "Photoshop 3.0\0";
//const char photoshopBimId_[] = "8BIM";
const uint16_t photoshopIptc = 0x0404;
const char xmpMarker[] = "http://ns.adobe.com/xap/1.0/\0";
const QByteArray photoshopIptc_((char*)&photoshopIptc, 2);
namespace
{
void jpegErrorExit ( j_common_ptr cinfo )
{
char jpegLastErrorMsg[JMSG_LENGTH_MAX];
/* Create the message */
( *( cinfo->err->format_message ) ) ( cinfo, jpegLastErrorMsg );
/* Jump to the setjmp point */
throw std::runtime_error( jpegLastErrorMsg ); // or your preffered exception ...
}
J_COLOR_SPACE getColorTypeforColorSpace(const KoColorSpace * cs)
{
if (KoID(cs->id()) == KoID("GRAYA") || cs->id() == "GRAYAU16" || cs->id() == "GRAYA16") {
return JCS_GRAYSCALE;
}
if (KoID(cs->id()) == KoID("RGBA") || KoID(cs->id()) == KoID("RGBA16")) {
return JCS_RGB;
}
if (KoID(cs->id()) == KoID("CMYK") || KoID(cs->id()) == KoID("CMYKAU16")) {
return JCS_CMYK;
}
return JCS_UNKNOWN;
}
QString getColorSpaceModelForColorType(J_COLOR_SPACE color_type)
{
dbgFile << "color_type =" << color_type;
if (color_type == JCS_GRAYSCALE) {
return GrayAColorModelID.id();
} else if (color_type == JCS_RGB) {
return RGBAColorModelID.id();
} else if (color_type == JCS_CMYK) {
return CMYKAColorModelID.id();
}
return "";
}
}
struct KisJPEGConverter::Private
{
Private(KisDocument *doc, bool batchMode)
: doc(doc),
stop(false),
batchMode(batchMode)
{}
KisImageSP image;
KisDocument *doc;
bool stop;
bool batchMode;
};
KisJPEGConverter::KisJPEGConverter(KisDocument *doc, bool batchMode)
: m_d(new Private(doc, batchMode))
{
}
KisJPEGConverter::~KisJPEGConverter()
{
}
-KisImageBuilder_Result KisJPEGConverter::decode(const QString &filename)
+KisImageBuilder_Result KisJPEGConverter::decode(QIODevice *io)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = jpegErrorExit;
try {
jpeg_create_decompress(&cinfo);
- // open the file
- QFile file(filename);
- if (!file.exists()) {
- return (KisImageBuilder_RESULT_NOT_EXIST);
- }
- if (!file.open(QIODevice::ReadOnly)) {
- return (KisImageBuilder_RESULT_BAD_FETCH);
- }
- KisJPEGSource::setSource(&cinfo, &file);
+ KisJPEGSource::setSource(&cinfo, io);
jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
/* Save APP0..APP15 markers */
for (int m = 0; m < 16; m++)
jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF);
// setup_read_icc_profile(&cinfo);
// read header
jpeg_read_header(&cinfo, (boolean)true);
// start reading
jpeg_start_decompress(&cinfo);
// Get the colorspace
QString modelId = getColorSpaceModelForColorType(cinfo.out_color_space);
if (modelId.isEmpty()) {
dbgFile << "unsupported colorspace :" << cinfo.out_color_space;
jpeg_destroy_decompress(&cinfo);
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
uchar* profile_data;
uint profile_len;
const KoColorProfile* profile = 0;
QByteArray profile_rawdata;
if (read_icc_profile(&cinfo, &profile_data, &profile_len)) {
profile_rawdata.resize(profile_len);
memcpy(profile_rawdata.data(), profile_data, profile_len);
cmsHPROFILE hProfile = cmsOpenProfileFromMem(profile_data, profile_len);
if (hProfile != (cmsHPROFILE) 0) {
profile = KoColorSpaceRegistry::instance()->createColorProfile(modelId, Integer8BitsColorDepthID.id(), profile_rawdata);
Q_CHECK_PTR(profile);
dbgFile <<"profile name:" << profile->name() <<" product information:" << profile->info();
if (!profile->isSuitableForOutput()) {
dbgFile << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile"; // TODO: in ko2 popup a selection menu to inform the user
}
}
}
// Check that the profile is used by the color space
if (profile && !KoColorSpaceRegistry::instance()->colorSpaceFactory(
KoColorSpaceRegistry::instance()->colorSpaceId(
modelId, Integer8BitsColorDepthID.id()))->profileIsCompatible(profile)) {
warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << modelId;
profile = 0;
}
// Retrieve a pointer to the colorspace
const KoColorSpace* cs;
if (profile && profile->isSuitableForOutput()) {
dbgFile << "image has embedded profile:" << profile -> name() << "";
cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, Integer8BitsColorDepthID.id(), profile);
} else
cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, Integer8BitsColorDepthID.id(), "");
if (cs == 0) {
dbgFile << "unknown colorspace";
jpeg_destroy_decompress(&cinfo);
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
// TODO fixit
// Create the cmsTransform if needed
KoColorTransformation* transform = 0;
if (profile && !profile->isSuitableForOutput()) {
transform = KoColorSpaceRegistry::instance()->colorSpace(modelId, Integer8BitsColorDepthID.id(), profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
// Apparently an invalid transform was created from the profile. See bug https://bugs.kde.org/show_bug.cgi?id=255451.
// After 2.3: warn the user!
if (transform && !transform->isValid()) {
delete transform;
transform = 0;
}
// Creating the KisImageSP
if (!m_d->image) {
m_d->image = new KisImage(m_d->doc->createUndoStore(), cinfo.image_width, cinfo.image_height, cs, "built image");
Q_CHECK_PTR(m_d->image);
}
// Set resolution
double xres = 72, yres = 72;
if (cinfo.density_unit == 1) {
xres = cinfo.X_density;
yres = cinfo.Y_density;
} else if (cinfo.density_unit == 2) {
xres = cinfo.X_density * 2.54;
yres = cinfo.Y_density * 2.54;
}
if (xres < 72) {
xres = 72;
}
if (yres < 72) {
yres = 72;
}
m_d->image->setResolution(POINT_TO_INCH(xres), POINT_TO_INCH(yres)); // It is the "invert" macro because we convert from pointer-per-inchs to points
// Create layer
KisPaintLayerSP layer = KisPaintLayerSP(new KisPaintLayer(m_d->image.data(), m_d->image -> nextLayerName(), quint8_MAX));
// Read data
JSAMPROW row_pointer = new JSAMPLE[cinfo.image_width*cinfo.num_components];
for (; cinfo.output_scanline < cinfo.image_height;) {
KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, cinfo.output_scanline, cinfo.image_width);
jpeg_read_scanlines(&cinfo, &row_pointer, 1);
quint8 *src = row_pointer;
switch (cinfo.out_color_space) {
case JCS_GRAYSCALE:
do {
quint8 *d = it->rawData();
d[0] = *(src++);
if (transform) transform->transform(d, d, 1);
d[1] = quint8_MAX;
} while (it->nextPixel());
break;
case JCS_RGB:
do {
quint8 *d = it->rawData();
d[2] = *(src++);
d[1] = *(src++);
d[0] = *(src++);
if (transform) transform->transform(d, d, 1);
d[3] = quint8_MAX;
} while (it->nextPixel());
break;
case JCS_CMYK:
do {
quint8 *d = it->rawData();
d[0] = quint8_MAX - *(src++);
d[1] = quint8_MAX - *(src++);
d[2] = quint8_MAX - *(src++);
d[3] = quint8_MAX - *(src++);
if (transform) transform->transform(d, d, 1);
d[4] = quint8_MAX;
} while (it->nextPixel());
break;
default:
return KisImageBuilder_RESULT_UNSUPPORTED;
}
}
m_d->image->addNode(KisNodeSP(layer.data()), m_d->image->rootLayer().data());
// Read exif information
dbgFile << "Looking for exif information";
for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) {
dbgFile << "Marker is" << marker->marker;
if (marker->marker != (JOCTET)(JPEG_APP0 + 1)
|| marker->data_length < 14) {
continue; /* Exif data is in an APP1 marker of at least 14 octets */
}
if (GETJOCTET(marker->data[0]) != (JOCTET) 0x45 ||
GETJOCTET(marker->data[1]) != (JOCTET) 0x78 ||
GETJOCTET(marker->data[2]) != (JOCTET) 0x69 ||
GETJOCTET(marker->data[3]) != (JOCTET) 0x66 ||
GETJOCTET(marker->data[4]) != (JOCTET) 0x00 ||
GETJOCTET(marker->data[5]) != (JOCTET) 0x00)
continue; /* no Exif header */
dbgFile << "Found exif information of length :" << marker->data_length;
KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif");
Q_ASSERT(exifIO);
QByteArray byteArray((const char*)marker->data + 6, marker->data_length - 6);
QBuffer buf(&byteArray);
exifIO->loadFrom(layer->metaData(), &buf);
// Interpret orientation tag
if (layer->metaData()->containsEntry("http://ns.adobe.com/tiff/1.0/", "Orientation")) {
KisMetaData::Entry& entry = layer->metaData()->getEntry("http://ns.adobe.com/tiff/1.0/", "Orientation");
if (entry.value().type() == KisMetaData::Value::Variant) {
switch (entry.value().asVariant().toInt()) {
case 2:
KisTransformWorker::mirrorY(layer->paintDevice());
break;
case 3:
image()->rotateImage(M_PI);
break;
case 4:
KisTransformWorker::mirrorX(layer->paintDevice());
break;
case 5:
image()->rotateImage(M_PI / 2);
KisTransformWorker::mirrorY(layer->paintDevice());
break;
case 6:
image()->rotateImage(M_PI / 2);
break;
case 7:
image()->rotateImage(M_PI / 2);
KisTransformWorker::mirrorX(layer->paintDevice());
break;
case 8:
image()->rotateImage(-M_PI / 2 + M_PI*2);
break;
default:
break;
}
}
entry.value().setVariant(1);
}
break;
}
dbgFile << "Looking for IPTC information";
for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) {
dbgFile << "Marker is" << marker->marker;
if (marker->marker != (JOCTET)(JPEG_APP0 + 13) || marker->data_length < 14) {
continue; /* IPTC data is in an APP13 marker of at least 16 octets */
}
if (memcmp(marker->data, photoshopMarker, 14) != 0) {
for (int i = 0; i < 14; i++) {
dbgFile << (int)(*(marker->data + i)) << "" << (int)(photoshopMarker[i]);
}
dbgFile << "No photoshop marker";
continue; /* No IPTC Header */
}
dbgFile << "Found Photoshop information of length :" << marker->data_length;
KisMetaData::IOBackend* iptcIO = KisMetaData::IOBackendRegistry::instance()->value("iptc");
Q_ASSERT(iptcIO);
const Exiv2::byte *record = 0;
uint32_t sizeIptc = 0;
uint32_t sizeHdr = 0;
// Find actual Iptc data within the APP13 segment
if (!Exiv2::Photoshop::locateIptcIrb((Exiv2::byte*)(marker->data + 14),
marker->data_length - 14, &record, &sizeHdr, &sizeIptc)) {
if (sizeIptc) {
// Decode the IPTC data
QByteArray byteArray((const char*)(record + sizeHdr), sizeIptc);
iptcIO->loadFrom(layer->metaData(), new QBuffer(&byteArray));
} else {
dbgFile << "IPTC Not found in Photoshop marker";
}
}
break;
}
dbgFile << "Looking for XMP information";
for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) {
dbgFile << "Marker is" << marker->marker;
if (marker->marker != (JOCTET)(JPEG_APP0 + 1) || marker->data_length < 31) {
continue; /* XMP data is in an APP1 marker of at least 31 octets */
}
if (memcmp(marker->data, xmpMarker, 29) != 0) {
dbgFile << "Not XMP marker";
continue; /* No xmp Header */
}
dbgFile << "Found XMP Marker of length " << marker->data_length;
QByteArray byteArray((const char*)marker->data + 29, marker->data_length - 29);
KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp");
Q_ASSERT(xmpIO);
xmpIO->loadFrom(layer->metaData(), new QBuffer(&byteArray));
break;
}
// Dump loaded metadata
layer->metaData()->debugDump();
// Check whether the metadata has resolution info, too...
if (cinfo.density_unit == 0 && layer->metaData()->containsEntry("tiff:XResolution") && layer->metaData()->containsEntry("tiff:YResolution")) {
double xres = layer->metaData()->getEntry("tiff:XResolution").value().asDouble();
double yres = layer->metaData()->getEntry("tiff:YResolution").value().asDouble();
if (xres != 0 && yres != 0) {
m_d->image->setResolution(POINT_TO_INCH(xres), POINT_TO_INCH(yres)); // It is the "invert" macro because we convert from pointer-per-inchs to points
}
}
// Finish decompression
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
delete [] row_pointer;
return KisImageBuilder_RESULT_OK;
}
catch( std::runtime_error &e) {
jpeg_destroy_decompress(&cinfo);
return KisImageBuilder_RESULT_FAILURE;
}
}
-KisImageBuilder_Result KisJPEGConverter::buildImage(const QString &filename)
+KisImageBuilder_Result KisJPEGConverter::buildImage(QIODevice *io)
{
- return decode(filename);
+ return decode(io);
}
KisImageSP KisJPEGConverter::image()
{
return m_d->image;
}
-KisImageBuilder_Result KisJPEGConverter::buildFile(const QString &filename, KisPaintLayerSP layer, vKisAnnotationSP_it /*annotationsStart*/, vKisAnnotationSP_it /*annotationsEnd*/, KisJPEGOptions options, KisMetaData::Store* metaData)
+KisImageBuilder_Result KisJPEGConverter::buildFile(QIODevice *io, KisPaintLayerSP layer, KisJPEGOptions options, KisMetaData::Store* metaData)
{
if (!layer)
return KisImageBuilder_RESULT_INVALID_ARG;
- KisImageWSP image = KisImageWSP(layer->image());
+ KisImageSP image = KisImageSP(layer->image());
if (!image)
return KisImageBuilder_RESULT_EMPTY;
const KoColorSpace * cs = layer->colorSpace();
J_COLOR_SPACE color_type = getColorTypeforColorSpace(cs);
- if (!m_d->batchMode && cs->colorDepthId() != Integer8BitsColorDepthID) {
- QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Warning: JPEG only supports 8 bits per channel. Your image uses: %1. Krita will save your image as 8 bits per channel.", cs->name()));
- }
-
if (color_type == JCS_UNKNOWN) {
- if (!m_d->batchMode) {
- QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot export images in %1.\nWill save as RGB.", cs->name()));
- }
KUndo2Command *tmp = layer->paintDevice()->convertTo(KoColorSpaceRegistry::instance()->rgb8(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
delete tmp;
cs = KoColorSpaceRegistry::instance()->rgb8();
color_type = JCS_RGB;
}
if (options.forceSRGB) {
const KoColorSpace* dst = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), layer->colorSpace()->colorDepthId().id(), "sRGB built-in - (lcms internal)");
KUndo2Command *tmp = layer->paintDevice()->convertTo(dst);
delete tmp;
cs = dst;
color_type = JCS_RGB;
}
-
- // Open file for writing
- QFile file(filename);
- if (!file.open(QIODevice::WriteOnly)) {
- return (KisImageBuilder_RESULT_FAILURE);
- }
-
uint height = image->height();
uint width = image->width();
// Initialize structure
struct jpeg_compress_struct cinfo;
// Initialize error output
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
// Initialize output stream
- KisJPEGDestination::setDestination(&cinfo, &file);
+ KisJPEGDestination::setDestination(&cinfo, io);
cinfo.image_width = width; // image width and height, in pixels
cinfo.image_height = height;
cinfo.input_components = cs->colorChannelCount(); // number of color channels per pixel */
cinfo.in_color_space = color_type; // colorspace of input image
// Set default compression parameters
jpeg_set_defaults(&cinfo);
// Customize them
jpeg_set_quality(&cinfo, options.quality, (boolean)options.baseLineJPEG);
if (options.progressive) {
jpeg_simple_progression(&cinfo);
}
// Optimize ?
cinfo.optimize_coding = (boolean)options.optimize;
// Smoothing
cinfo.smoothing_factor = (boolean)options.smooth;
// Subsampling
switch (options.subsampling) {
default:
case 0: {
cinfo.comp_info[0].h_samp_factor = 2;
cinfo.comp_info[0].v_samp_factor = 2;
cinfo.comp_info[1].h_samp_factor = 1;
cinfo.comp_info[1].v_samp_factor = 1;
cinfo.comp_info[2].h_samp_factor = 1;
cinfo.comp_info[2].v_samp_factor = 1;
}
break;
case 1: {
cinfo.comp_info[0].h_samp_factor = 2;
cinfo.comp_info[0].v_samp_factor = 1;
cinfo.comp_info[1].h_samp_factor = 1;
cinfo.comp_info[1].v_samp_factor = 1;
cinfo.comp_info[2].h_samp_factor = 1;
cinfo.comp_info[2].v_samp_factor = 1;
}
break;
case 2: {
cinfo.comp_info[0].h_samp_factor = 1;
cinfo.comp_info[0].v_samp_factor = 2;
cinfo.comp_info[1].h_samp_factor = 1;
cinfo.comp_info[1].v_samp_factor = 1;
cinfo.comp_info[2].h_samp_factor = 1;
cinfo.comp_info[2].v_samp_factor = 1;
}
break;
case 3: {
cinfo.comp_info[0].h_samp_factor = 1;
cinfo.comp_info[0].v_samp_factor = 1;
cinfo.comp_info[1].h_samp_factor = 1;
cinfo.comp_info[1].v_samp_factor = 1;
cinfo.comp_info[2].h_samp_factor = 1;
cinfo.comp_info[2].v_samp_factor = 1;
}
break;
}
// Save resolution
cinfo.X_density = INCH_TO_POINT(image->xRes()); // It is the "invert" macro because we convert from pointer-per-inchs to points
cinfo.Y_density = INCH_TO_POINT(image->yRes()); // It is the "invert" macro because we convert from pointer-per-inchs to points
cinfo.density_unit = 1;
cinfo.write_JFIF_header = (boolean)true;
// Start compression
jpeg_start_compress(&cinfo, (boolean)true);
// Save exif and iptc information if any available
if (metaData && !metaData->empty()) {
metaData->applyFilters(options.filters);
// Save EXIF
if (options.exif) {
dbgFile << "Trying to save exif information";
KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif");
Q_ASSERT(exifIO);
QBuffer buffer;
exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
dbgFile << "Exif information size is" << buffer.data().size();
QByteArray data = buffer.data();
if (data.size() < MAX_DATA_BYTES_IN_MARKER) {
jpeg_write_marker(&cinfo, JPEG_APP0 + 1, (const JOCTET*)data.data(), data.size());
} else {
dbgFile << "EXIF information could not be saved."; // TODO: warn the user ?
}
}
// Save IPTC
if (options.iptc) {
dbgFile << "Trying to save exif information";
KisMetaData::IOBackend* iptcIO = KisMetaData::IOBackendRegistry::instance()->value("iptc");
Q_ASSERT(iptcIO);
QBuffer buffer;
iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
dbgFile << "IPTC information size is" << buffer.data().size();
QByteArray data = buffer.data();
if (data.size() < MAX_DATA_BYTES_IN_MARKER) {
jpeg_write_marker(&cinfo, JPEG_APP0 + 13, (const JOCTET*)data.data(), data.size());
} else {
dbgFile << "IPTC information could not be saved."; // TODO: warn the user ?
}
}
// Save XMP
if (options.xmp) {
dbgFile << "Trying to save XMP information";
KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp");
Q_ASSERT(xmpIO);
QBuffer buffer;
xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
dbgFile << "XMP information size is" << buffer.data().size();
QByteArray data = buffer.data();
if (data.size() < MAX_DATA_BYTES_IN_MARKER) {
jpeg_write_marker(&cinfo, JPEG_APP0 + 14, (const JOCTET*)data.data(), data.size());
} else {
dbgFile << "XMP information could not be saved."; // TODO: warn the user ?
}
}
}
KisPaintDeviceSP dev = new KisPaintDevice(layer->colorSpace());
KoColor c(options.transparencyFillColor, layer->colorSpace());
dev->fill(QRect(0, 0, width, height), c);
KisPainter gc(dev);
gc.bitBlt(QPoint(0, 0), layer->paintDevice(), QRect(0, 0, width, height));
gc.end();
if (options.saveProfile) {
const KoColorProfile* colorProfile = layer->colorSpace()->profile();
QByteArray colorProfileData = colorProfile->rawData();
write_icc_profile(& cinfo, (uchar*) colorProfileData.data(), colorProfileData.size());
}
// Write data information
JSAMPROW row_pointer = new JSAMPLE[width*cinfo.input_components];
int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->channelCount();
for (; cinfo.next_scanline < height;) {
KisHLineConstIteratorSP it = dev->createHLineConstIteratorNG(0, cinfo.next_scanline, width);
quint8 *dst = row_pointer;
switch (color_type) {
case JCS_GRAYSCALE:
if (color_nb_bits == 16) {
do {
//const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
const quint8 *d = it->oldRawData();
*(dst++) = cs->scaleToU8(d, 0);//d[0] / quint8_MAX;
} while (it->nextPixel());
} else {
do {
const quint8 *d = it->oldRawData();
*(dst++) = d[0];
} while (it->nextPixel());
}
break;
case JCS_RGB:
if (color_nb_bits == 16) {
do {
//const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
const quint8 *d = it->oldRawData();
*(dst++) = cs->scaleToU8(d, 2); //d[2] / quint8_MAX;
*(dst++) = cs->scaleToU8(d, 1); //d[1] / quint8_MAX;
*(dst++) = cs->scaleToU8(d, 0); //d[0] / quint8_MAX;
} while (it->nextPixel());
} else {
do {
const quint8 *d = it->oldRawData();
*(dst++) = d[2];
*(dst++) = d[1];
*(dst++) = d[0];
} while (it->nextPixel());
}
break;
case JCS_CMYK:
if (color_nb_bits == 16) {
do {
//const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
const quint8 *d = it->oldRawData();
*(dst++) = quint8_MAX - cs->scaleToU8(d, 0);//quint8_MAX - d[0] / quint8_MAX;
*(dst++) = quint8_MAX - cs->scaleToU8(d, 1);//quint8_MAX - d[1] / quint8_MAX;
*(dst++) = quint8_MAX - cs->scaleToU8(d, 2);//quint8_MAX - d[2] / quint8_MAX;
*(dst++) = quint8_MAX - cs->scaleToU8(d, 3);//quint8_MAX - d[3] / quint8_MAX;
} while (it->nextPixel());
} else {
do {
const quint8 *d = it->oldRawData();
*(dst++) = quint8_MAX - d[0];
*(dst++) = quint8_MAX - d[1];
*(dst++) = quint8_MAX - d[2];
*(dst++) = quint8_MAX - d[3];
} while (it->nextPixel());
}
break;
default:
delete [] row_pointer;
jpeg_destroy_compress(&cinfo);
return KisImageBuilder_RESULT_UNSUPPORTED;
}
jpeg_write_scanlines(&cinfo, &row_pointer, 1);
}
// Writing is over
jpeg_finish_compress(&cinfo);
- file.close();
delete [] row_pointer;
// Free memory
jpeg_destroy_compress(&cinfo);
return KisImageBuilder_RESULT_OK;
}
void KisJPEGConverter::cancel()
{
m_d->stop = true;
}
diff --git a/plugins/impex/jpeg/kis_jpeg_converter.h b/plugins/impex/jpeg/kis_jpeg_converter.h
index c606edcbcf..d11648017b 100644
--- a/plugins/impex/jpeg/kis_jpeg_converter.h
+++ b/plugins/impex/jpeg/kis_jpeg_converter.h
@@ -1,85 +1,85 @@
/*
* 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.
*/
#ifndef _KIS_JPEG_CONVERTER_H_
#define _KIS_JPEG_CONVERTER_H_
#include <stdio.h>
extern "C" {
#include <jpeglib.h>
}
#include <QColor>
#include <QVector>
#include <QColor>
#include "kis_types.h"
#include "kis_annotation.h"
#include <KisImageBuilderResult.h>
class KisDocument;
namespace KisMetaData
{
class Filter;
}
struct KisJPEGOptions {
int quality;
bool progressive;
bool optimize;
int smooth;
bool baseLineJPEG;
int subsampling;
bool exif;
bool iptc;
bool xmp;
QList<const KisMetaData::Filter*> filters;
QColor transparencyFillColor;
bool forceSRGB;
bool saveProfile;
};
namespace KisMetaData
{
class Store;
}
class KisJPEGConverter : public QObject
{
Q_OBJECT
public:
KisJPEGConverter(KisDocument *doc, bool batchMode = false);
virtual ~KisJPEGConverter();
public:
- KisImageBuilder_Result buildImage(const QString &filename);
- KisImageBuilder_Result buildFile(const QString &filename, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisJPEGOptions options, KisMetaData::Store* metaData);
+ KisImageBuilder_Result buildImage(QIODevice *io);
+ KisImageBuilder_Result buildFile(QIODevice *io, KisPaintLayerSP layer, KisJPEGOptions options, KisMetaData::Store* metaData);
/** Retrieve the constructed image
*/
KisImageSP image();
public Q_SLOTS:
virtual void cancel();
private:
- KisImageBuilder_Result decode(const QString &filename);
+ KisImageBuilder_Result decode(QIODevice *io);
private:
struct Private;
QScopedPointer<Private> m_d;
};
#endif
diff --git a/plugins/impex/jpeg/kis_jpeg_export.cc b/plugins/impex/jpeg/kis_jpeg_export.cc
index c34b14dd46..b8a2013a00 100644
--- a/plugins/impex/jpeg/kis_jpeg_export.cc
+++ b/plugins/impex/jpeg/kis_jpeg_export.cc
@@ -1,264 +1,232 @@
/*
* 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_jpeg_export.h"
#include <QCheckBox>
#include <QSlider>
#include <QColor>
#include <QString>
#include <QStringList>
#include <QApplication>
+#include <QFileInfo>
-#include <KoDialog.h>
#include <kpluginfactory.h>
-#include <QFileInfo>
#include <KoColorSpace.h>
#include <KoColorProfile.h>
-#include <KisImportExportManager.h>
-#include <KisFilterChain.h>
#include <KoColorSpaceConstants.h>
#include <KoColorSpaceRegistry.h>
+#include <KisImportExportManager.h>
#include <kis_slider_spin_box.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include <metadata/kis_meta_data_store.h>
#include <metadata/kis_meta_data_filter_registry_model.h>
#include <metadata/kis_exif_info_visitor.h>
#include <generator/kis_generator_layer.h>
-#include "kis_jpeg_converter.h"
#include <KisImportExportManager.h>
-
+#include <KisExportCheckRegistry.h>
+#include "kis_jpeg_converter.h"
class KisExternalLayer;
K_PLUGIN_FACTORY_WITH_JSON(KisJPEGExportFactory, "krita_jpeg_export.json", registerPlugin<KisJPEGExport>();)
KisJPEGExport::KisJPEGExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisJPEGExport::~KisJPEGExport()
{
}
-KisImportExportFilter::ConversionStatus KisJPEGExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisJPEGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
- dbgFile << "JPEG export! From:" << from << ", To:" << to << "";
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *input = inputDocument();
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
-
- KisImageWSP image = input->image();
+ KisImageSP image = document->image();
Q_CHECK_PTR(image);
- KoDialog kdb;
- kdb.setWindowTitle(i18n("JPEG Export Options"));
- kdb.setButtons(KoDialog::Ok | KoDialog::Cancel);
- KisConfigWidget *wdg = createConfigurationWidget(&kdb, from, to);
- kdb.setMainWidget(wdg);
- kdb.resize(kdb.minimumSize());
-
- // If a configuration object was passed to the convert method, we use that, otherwise we load from the settings
- KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
- if (configuration) {
- cfg->fromXML(configuration->toXML());
- }
- else {
- cfg = lastSavedConfiguration(from, to);
- }
-
// An extra option to pass to the config widget to set the state correctly, this isn't saved
const KoColorSpace* cs = image->projection()->colorSpace();
bool sRGB = cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive);
- cfg->setProperty("is_sRGB", sRGB);
-
- wdg->setConfiguration(cfg);
-
-
- QApplication::restoreOverrideCursor();
-
- if (!getBatchMode()) {
- if (kdb.exec() == QDialog::Rejected) {
- return KisImportExportFilter::UserCancelled;
- }
- cfg = wdg->configuration();
- KisConfig().setExportConfiguration("JPEG", *cfg.data());
-
- }
+ configuration->setProperty("is_sRGB", sRGB);
KisJPEGOptions options;
- options.progressive = cfg->getBool("progressive", false);
- options.quality = cfg->getInt("quality", 80);
- options.forceSRGB = cfg->getBool("forceSRGB", false);
- options.saveProfile = cfg->getBool("saveProfile", true);
- options.optimize = cfg->getBool("optimize", true);
- options.smooth = cfg->getInt("smoothing", 0);
- options.baseLineJPEG = cfg->getBool("baseline", true);
- options.subsampling = cfg->getInt("subsampling", 0);
- options.exif = cfg->getBool("exif", true);
- options.iptc = cfg->getBool("iptc", true);
- options.xmp = cfg->getBool("xmp", true);
- options.transparencyFillColor = cfg->getColor("transparencyFillcolor").toQColor();
+ options.progressive = configuration->getBool("progressive", false);
+ options.quality = configuration->getInt("quality", 80);
+ options.forceSRGB = configuration->getBool("forceSRGB", false);
+ options.saveProfile = configuration->getBool("saveProfile", true);
+ options.optimize = configuration->getBool("optimize", true);
+ options.smooth = configuration->getInt("smoothing", 0);
+ options.baseLineJPEG = configuration->getBool("baseline", true);
+ options.subsampling = configuration->getInt("subsampling", 0);
+ options.exif = configuration->getBool("exif", true);
+ options.iptc = configuration->getBool("iptc", true);
+ options.xmp = configuration->getBool("xmp", true);
+ options.transparencyFillColor = configuration->getColor("transparencyFillcolor").toQColor();
KisMetaData::FilterRegistryModel m;
- m.setEnabledFilters(cfg->getString("filters").split(","));
+ m.setEnabledFilters(configuration->getString("filters").split(","));
options.filters = m.enabledFilters();
- QString filename = outputFile();
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
// the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(input->image()->locked());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
- KisJPEGConverter kpc(input, getBatchMode());
+ KisJPEGConverter kpc(document, batchMode());
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
- vKisAnnotationSP_it beginIt = image->beginAnnotations();
- vKisAnnotationSP_it endIt = image->endAnnotations();
- KisImageBuilder_Result res;
-
KisExifInfoVisitor eIV;
eIV.visit(image->rootLayer().data());
KisMetaData::Store* eI = 0;
- if (eIV.countPaintLayer() == 1)
+ if (eIV.countPaintLayer() == 1) {
eI = eIV.exifInfo();
+ }
if (eI) {
KisMetaData::Store* copy = new KisMetaData::Store(*eI);
eI = copy;
}
- if ((res = kpc.buildFile(filename, l, beginIt, endIt, options, eI)) == KisImageBuilder_RESULT_OK) {
- dbgFile << "success !";
+
+ KisImageBuilder_Result res = kpc.buildFile(io, l, options, eI);
+
+ if (res == KisImageBuilder_RESULT_OK) {
delete eI;
return KisImportExportFilter::OK;
}
delete eI;
dbgFile << " Result =" << res;
return KisImportExportFilter::InternalError;
}
KisPropertiesConfigurationSP KisJPEGExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("progressive", false);
cfg->setProperty("quality", 80);
cfg->setProperty("forceSRGB", false);
cfg->setProperty("saveProfile", true);
cfg->setProperty("optimize", true);
cfg->setProperty("smoothing", 0);
cfg->setProperty("baseline", true);
cfg->setProperty("subsampling", 0);
cfg->setProperty("exif", true);
cfg->setProperty("iptc", true);
cfg->setProperty("xmp", true);
cfg->setProperty("transparencyFillcolor", QString("255,255,255"));
cfg->setProperty("filters", "");
return cfg;
}
KisPropertiesConfigurationSP KisJPEGExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
{
KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
- QString filterConfig = KisConfig().exportConfiguration("JPEG");
+ QString filterConfig = KisConfig().exportConfiguration(to);
cfg->fromXML(filterConfig, false);
return cfg;
}
KisConfigWidget *KisJPEGExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
return new KisWdgOptionsJPEG(parent);
}
+void KisJPEGExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("ExifCheck")->create(KisExportCheckBase::SUPPORTED));
+
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(CMYKAColorModelID, Integer8BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "JPEG");
+}
+
+
KisWdgOptionsJPEG::KisWdgOptionsJPEG(QWidget *parent)
: KisConfigWidget(parent)
{
setupUi(this);
metaDataFilters->setModel(&m_filterRegistryModel);
qualityLevel->setRange(0, 100, 0);
qualityLevel->setSuffix("%");
smoothLevel->setRange(0, 100, 0);
smoothLevel->setSuffix("%");
}
void KisWdgOptionsJPEG::setConfiguration(const KisPropertiesConfigurationSP cfg)
{
progressive->setChecked(cfg->getBool("progressive", false));
qualityLevel->setValue(cfg->getInt("quality", 80));
optimize->setChecked(cfg->getBool("optimize", true));
smoothLevel->setValue(cfg->getInt("smoothing", 0));
baseLineJPEG->setChecked(cfg->getBool("baseline", true));
subsampling->setCurrentIndex(cfg->getInt("subsampling", 0));
exif->setChecked(cfg->getBool("exif", true));
iptc->setChecked(cfg->getBool("iptc", true));
xmp->setChecked(cfg->getBool("xmp", true));
chkForceSRGB->setVisible(cfg->getBool("is_sRGB"));
chkForceSRGB->setChecked(cfg->getBool("forceSRGB", false));
chkSaveProfile->setChecked(cfg->getBool("saveProfile", true));
QStringList rgb = cfg->getString("transparencyFillcolor", "255,255,255").split(',');
KoColor background(KoColorSpaceRegistry::instance()->rgb8());
background.fromQColor(Qt::white);
bnTransparencyFillColor->setDefaultColor(background);
background.fromQColor(QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()));
bnTransparencyFillColor->setColor(background);
m_filterRegistryModel.setEnabledFilters(cfg->getString("filters").split(','));
}
KisPropertiesConfigurationSP KisWdgOptionsJPEG::configuration() const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("progressive", progressive->isChecked());
cfg->setProperty("quality", (int)qualityLevel->value());
cfg->setProperty("forceSRGB", chkForceSRGB->isChecked());
cfg->setProperty("saveProfile", chkSaveProfile->isChecked());
cfg->setProperty("optimize", optimize->isChecked());
cfg->setProperty("smoothing", (int)smoothLevel->value());
cfg->setProperty("baseline", baseLineJPEG->isChecked());
cfg->setProperty("subsampling", subsampling->currentIndex());
cfg->setProperty("exif", exif->isChecked());
cfg->setProperty("iptc", iptc->isChecked());
cfg->setProperty("xmp", xmp->isChecked());
QColor c = bnTransparencyFillColor->color().toQColor();
cfg->setProperty("transparencyFillcolor", QString("%1,%2,%3").arg(c.red()).arg(c.green()).arg(c.blue()));
QString enabledFilters;
Q_FOREACH (const KisMetaData::Filter* filter, m_filterRegistryModel.enabledFilters()) {
enabledFilters = enabledFilters + filter->id() + ',';
}
cfg->setProperty("filters", enabledFilters);
return cfg;
}
#include <kis_jpeg_export.moc>
diff --git a/plugins/impex/jpeg/kis_jpeg_export.h b/plugins/impex/jpeg/kis_jpeg_export.h
index 89148feed3..c7687f5485 100644
--- a/plugins/impex/jpeg/kis_jpeg_export.h
+++ b/plugins/impex/jpeg/kis_jpeg_export.h
@@ -1,58 +1,59 @@
/*
* 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.
*/
#ifndef _KIS_JPEG_EXPORT_H_
#define _KIS_JPEG_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
#include <kis_config_widget.h>
#include "ui_kis_wdg_options_jpeg.h"
#include <metadata/kis_meta_data_store.h>
#include <metadata/kis_meta_data_filter_registry_model.h>
class KisWdgOptionsJPEG : public KisConfigWidget, public Ui::WdgOptionsJPEG
{
Q_OBJECT
public:
KisWdgOptionsJPEG(QWidget *parent);
void setConfiguration(const KisPropertiesConfigurationSP cfg);
KisPropertiesConfigurationSP configuration() const;
private:
KisMetaData::FilterRegistryModel m_filterRegistryModel;
};
class KisJPEGExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisJPEGExport(QObject *parent, const QVariantList &);
virtual ~KisJPEGExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const;
KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/jpeg/kis_jpeg_import.cc b/plugins/impex/jpeg/kis_jpeg_import.cc
index bada591e38..4a80e3dc53 100644
--- a/plugins/impex/jpeg/kis_jpeg_import.cc
+++ b/plugins/impex/jpeg/kis_jpeg_import.cc
@@ -1,103 +1,80 @@
/*
* 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_jpeg_import.h"
#include <QFileInfo>
#include <kpluginfactory.h>
-#include <KisFilterChain.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <KisViewManager.h>
#include <KisImportExportManager.h>
#include "kis_jpeg_converter.h"
K_PLUGIN_FACTORY_WITH_JSON(JPEGImportFactory, "krita_jpeg_import.json", registerPlugin<KisJPEGImport>();)
KisJPEGImport::KisJPEGImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisJPEGImport::~KisJPEGImport()
{
}
-KisImportExportFilter::ConversionStatus KisJPEGImport::convert(const QByteArray&, const QByteArray& to, KisPropertiesConfigurationSP /*configuration*/)
+KisImportExportFilter::ConversionStatus KisJPEGImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- dbgFile << "Importing using JPEGImport!";
- if (to != "application/x-krita")
+ KisJPEGConverter ib(document, batchMode());
+
+ switch (ib.buildImage(io)) {
+ case KisImageBuilder_RESULT_UNSUPPORTED:
+ case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
+ return KisImportExportFilter::NotImplemented;
+ break;
+ case KisImageBuilder_RESULT_INVALID_ARG:
return KisImportExportFilter::BadMimeType;
+ break;
+ case KisImageBuilder_RESULT_NO_URI:
+ case KisImageBuilder_RESULT_NOT_LOCAL:
+ return KisImportExportFilter::FileNotFound;
+ break;
+ case KisImageBuilder_RESULT_BAD_FETCH:
+ case KisImageBuilder_RESULT_EMPTY:
+ return KisImportExportFilter::ParsingError;
+ break;
+ case KisImageBuilder_RESULT_FAILURE:
+ return KisImportExportFilter::InternalError;
+ break;
+ case KisImageBuilder_RESULT_OK:
+ document->setCurrentImage(ib.image());
+ return KisImportExportFilter::OK;
+ default:
+ break;
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- doc->prepareForImport();
-
- if (!filename.isEmpty()) {
-
- if (!QFileInfo(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- KisJPEGConverter ib(doc, getBatchMode());
-
-// if (view != 0)
-// view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true);
-
- switch (ib.buildImage(filename)) {
- case KisImageBuilder_RESULT_UNSUPPORTED:
- case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
- return KisImportExportFilter::NotImplemented;
- break;
- case KisImageBuilder_RESULT_INVALID_ARG:
- return KisImportExportFilter::BadMimeType;
- break;
- case KisImageBuilder_RESULT_NO_URI:
- case KisImageBuilder_RESULT_NOT_LOCAL:
- return KisImportExportFilter::FileNotFound;
- break;
- case KisImageBuilder_RESULT_BAD_FETCH:
- case KisImageBuilder_RESULT_EMPTY:
- return KisImportExportFilter::ParsingError;
- break;
- case KisImageBuilder_RESULT_FAILURE:
- return KisImportExportFilter::InternalError;
- break;
- case KisImageBuilder_RESULT_OK:
- doc->setCurrentImage(ib.image());
- return KisImportExportFilter::OK;
- default:
- break;
- }
+ }
+ return KisImportExportFilter::InternalError;
- }
- return KisImportExportFilter::StorageCreationError;
}
#include <kis_jpeg_import.moc>
diff --git a/plugins/impex/jpeg/kis_jpeg_import.h b/plugins/impex/jpeg/kis_jpeg_import.h
index 72a800e329..f4f0cc9291 100644
--- a/plugins/impex/jpeg/kis_jpeg_import.h
+++ b/plugins/impex/jpeg/kis_jpeg_import.h
@@ -1,36 +1,36 @@
/*
* 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.
*/
#ifndef _KIS_JPEG_IMPORT_H_
#define _KIS_JPEG_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisJPEGImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisJPEGImport(QObject *parent, const QVariantList &);
virtual ~KisJPEGImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/jpeg/krita_jpeg_export.json b/plugins/impex/jpeg/krita_jpeg_export.json
index 69655fa939..93164f526d 100644
--- a/plugins/impex/jpeg/krita_jpeg_export.json
+++ b/plugins/impex/jpeg/krita_jpeg_export.json
@@ -1,14 +1,14 @@
{
"Icon": "",
"Id": "Krita JPEG Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/jpeg",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Library": "kritajpegexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "jpeg,jpg"
}
diff --git a/plugins/impex/jpeg/krita_jpeg_import.json b/plugins/impex/jpeg/krita_jpeg_import.json
index dcc91c06b2..fafc5571e2 100644
--- a/plugins/impex/jpeg/krita_jpeg_import.json
+++ b/plugins/impex/jpeg/krita_jpeg_import.json
@@ -1,14 +1,14 @@
{
"Icon": "",
"Id": "Krita JPEG Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/jpeg",
"X-KDE-Library": "kritajpegimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "jpg,jpeg"
}
diff --git a/plugins/impex/kra/CMakeLists.txt b/plugins/impex/kra/CMakeLists.txt
new file mode 100644
index 0000000000..6dadc41399
--- /dev/null
+++ b/plugins/impex/kra/CMakeLists.txt
@@ -0,0 +1,28 @@
+set(kritakraimport_SOURCES
+ kra_import.cpp
+ kra_converter.cpp
+)
+
+add_library(kritakraimport MODULE ${kritakraimport_SOURCES})
+
+target_link_libraries(kritakraimport kritaui kritalibkra)
+
+install(TARGETS kritakraimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
+
+set(kritakraexport_SOURCES
+ kra_export.cpp
+ kra_converter.cpp
+)
+
+add_library(kritakraexport MODULE ${kritakraexport_SOURCES})
+
+target_link_libraries(kritakraexport kritaui kritalibkra kritaimpex)
+
+install(TARGETS kritakraexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
+
+install( PROGRAMS krita_kra.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
+
+if(SHOULD_BUILD_FILEMANAGER_THUMBNAIL)
+ install( FILES krita_kra_thumbnail.desktop DESTINATION ${SERVICES_INSTALL_DIR})
+endif()
+
diff --git a/plugins/impex/kra/kra_converter.cpp b/plugins/impex/kra/kra_converter.cpp
new file mode 100644
index 0000000000..798cb937a8
--- /dev/null
+++ b/plugins/impex/kra/kra_converter.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2016 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 "kra_converter.h"
+
+#include <QApplication>
+#include <QFileInfo>
+#include <QScopedPointer>
+#include <QUrl>
+
+#include <KoStore.h>
+#include <KoStoreDevice.h>
+#include <KoColorSpaceRegistry.h>
+#include <KoDocumentInfo.h>
+#include <KoXmlWriter.h>
+#include <KoXmlReader.h>
+
+#include <KritaVersionWrapper.h>
+#include <kis_group_layer.h>
+#include <kis_image.h>
+#include <kis_paint_layer.h>
+#include <kis_png_converter.h>
+#include <KisDocument.h>
+
+static const char CURRENT_DTD_VERSION[] = "2.0";
+
+KraConverter::KraConverter(KisDocument *doc)
+ : m_doc(doc)
+ , m_image(doc->image())
+{
+}
+
+KraConverter::~KraConverter()
+{
+ delete m_store;
+ delete m_kraSaver;
+ delete m_kraLoader;
+}
+
+KisImageBuilder_Result KraConverter::buildImage(QIODevice *io)
+{
+ m_store = KoStore::createStore(io, KoStore::Read, "", KoStore::Zip);
+
+ if (m_store->bad()) {
+ m_doc->setErrorMessage(i18n("Not a valid Krita file"));
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+
+ bool success;
+ {
+ if (m_store->hasFile("root") || m_store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml)
+ KoXmlDocument doc = KoXmlDocument(true);
+
+ bool ok = oldLoadAndParse(m_store, "root", doc);
+ if (ok)
+ ok = loadXML(doc, m_store);
+ if (!ok) {
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+
+ } else {
+ errUI << "ERROR: No maindoc.xml" << endl;
+ m_doc->setErrorMessage(i18n("Invalid document: no file 'maindoc.xml'."));
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+
+ if (m_store->hasFile("documentinfo.xml")) {
+ KoXmlDocument doc = KoXmlDocument(true);
+ if (oldLoadAndParse(m_store, "documentinfo.xml", doc)) {
+ m_doc->documentInfo()->load(doc);
+ }
+ }
+ success = completeLoading(m_store);
+ }
+
+ return success ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE;
+}
+
+KisImageSP KraConverter::image()
+{
+ return m_image;
+}
+
+vKisNodeSP KraConverter::activeNodes()
+{
+ return m_activeNodes;
+}
+
+QList<KisPaintingAssistantSP> KraConverter::assistants()
+{
+ return m_assistants;
+}
+
+KisImageBuilder_Result KraConverter::buildFile(QIODevice *io)
+{
+ m_store = KoStore::createStore(io, KoStore::Write, m_doc->nativeFormatMimeType(), KoStore::Zip);
+
+ if (m_store->bad()) {
+ m_doc->setErrorMessage(i18n("Could not create the file for saving"));
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+
+ bool result = false;
+
+ m_kraSaver = new KisKraSaver(m_doc);
+
+ result = saveRootDocuments(m_store);
+
+ if (!result) {
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+
+ result = m_kraSaver->saveKeyframes(m_store, m_doc->url().toLocalFile(), true);
+ if (!result) {
+ qWarning() << "saving key frames failed";
+ }
+ result = m_kraSaver->saveBinaryData(m_store, m_image, m_doc->url().toLocalFile(), true, m_doc->isAutosaving());
+ if (!result) {
+ qWarning() << "saving binary data failed";
+ }
+
+ if (!m_store->finalize()) {
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+
+ if (!m_kraSaver->errorMessages().isEmpty()) {
+ m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n"));
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+ return KisImageBuilder_RESULT_OK;
+}
+
+bool KraConverter::saveRootDocuments(KoStore *store)
+{
+ dbgFile << "Saving root";
+ if (store->open("root")) {
+ KoStoreDevice dev(store);
+ if (!saveToStream(&dev) || !store->close()) {
+ dbgUI << "saveToStream failed";
+ return false;
+ }
+ } else {
+ m_doc->setErrorMessage(i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml")));
+ return false;
+ }
+ bool success = false;
+ if (store->open("documentinfo.xml")) {
+ QDomDocument doc = KisDocument::createDomDocument("document-info"
+ /*DTD name*/, "document-info" /*tag name*/, "1.1");
+ doc = m_doc->documentInfo()->save(doc);
+ KoStoreDevice dev(store);
+ QByteArray s = doc.toByteArray(); // this is already Utf8!
+ success = dev.write(s.data(), s.size());
+ store->close();
+ }
+
+ if (store->open("preview.png")) {
+ // ### TODO: missing error checking (The partition could be full!)
+ savePreview(store);
+ (void)store->close();
+ }
+
+
+ dbgUI << "Saving done of url:" << m_doc->url().toLocalFile();
+ // Success
+ return success;
+}
+
+bool KraConverter::saveToStream(QIODevice *dev)
+{
+ QDomDocument doc = createDomDocument();
+ // 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()) {
+ warnUI << "wrote " << nwritten << "- expected" << s.size();
+ }
+ return nwritten == (int)s.size();
+}
+
+QDomDocument KraConverter::createDomDocument()
+{
+ QDomDocument doc = m_doc->createDomDocument("DOC", CURRENT_DTD_VERSION);
+ QDomElement root = doc.documentElement();
+
+ root.setAttribute("editor", "Krita");
+ root.setAttribute("syntaxVersion", "2");
+ root.setAttribute("kritaVersion", KritaVersionWrapper::versionString(false));
+
+ root.appendChild(m_kraSaver->saveXML(doc, m_image));
+
+ if (!m_kraSaver->errorMessages().isEmpty()) {
+ m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n"));
+ }
+ return doc;
+}
+
+bool KraConverter::savePreview(KoStore *store)
+{
+ QPixmap pix = m_doc->generatePreview(QSize(256, 256));
+ QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly));
+ if (preview.size() == QSize(0,0)) {
+ QSize newSize = m_doc->image()->bounds().size();
+ newSize.scale(QSize(256, 256), Qt::KeepAspectRatio);
+ preview = QImage(newSize, QImage::Format_ARGB32);
+ preview.fill(QColor(0, 0, 0, 0));
+ }
+
+ KoStoreDevice io(store);
+ if (!io.open(QIODevice::WriteOnly)) {
+ return false;
+ }
+ bool ret = preview.save(&io, "PNG");
+ io.close();
+ return ret;
+}
+
+
+bool KraConverter::oldLoadAndParse(KoStore *store, const QString &filename, KoXmlDocument &xmldoc)
+{
+ //dbgUI <<"Trying to open" << filename;
+
+ if (!store->open(filename)) {
+ warnUI << "Entry " << filename << " not found!";
+ m_doc->setErrorMessage(i18n("Could not find %1", filename));
+ return false;
+ }
+ // Error variables for QDomDocument::setContent
+ QString errorMsg;
+ int errorLine, errorColumn;
+ bool ok = xmldoc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn);
+ store->close();
+ if (!ok) {
+ errUI << "Parsing error in " << filename << "! Aborting!" << endl
+ << " In line: " << errorLine << ", column: " << errorColumn << endl
+ << " Error message: " << errorMsg << endl;
+ m_doc->setErrorMessage(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;
+ }
+ dbgUI << "File" << filename << " loaded and parsed";
+ return true;
+}
+
+bool KraConverter::loadXML(const KoXmlDocument &doc, KoStore *store)
+{
+ Q_UNUSED(store);
+
+ KoXmlElement root;
+ KoXmlNode node;
+
+ if (doc.doctype().name() != "DOC") {
+ m_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) {
+ m_doc->setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion));
+ return false;
+ }
+
+ if (!root.hasChildNodes()) {
+ m_doc->setErrorMessage(i18n("The file has no layers."));
+ return false;
+ }
+
+ m_kraLoader = new KisKraLoader(m_doc, 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 (!(m_image = m_kraLoader->loadXML(elem))) {
+ if (m_kraLoader->errorMessages().isEmpty()) {
+ m_doc->setErrorMessage(i18n("Unknown error."));
+ }
+ else {
+ m_doc->setErrorMessage(m_kraLoader->errorMessages().join(".\n"));
+ }
+ return false;
+ }
+ return true;
+ }
+ else {
+ if (m_kraLoader->errorMessages().isEmpty()) {
+ m_doc->setErrorMessage(i18n("The file does not contain an image."));
+ }
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+bool KraConverter::completeLoading(KoStore* store)
+{
+ if (!m_image) {
+ if (m_kraLoader->errorMessages().isEmpty()) {
+ m_doc->setErrorMessage(i18n("Unknown error."));
+ }
+ else {
+ m_doc->setErrorMessage(m_kraLoader->errorMessages().join(".\n"));
+ }
+ return false;
+ }
+
+ m_image->blockUpdates();
+
+ m_kraLoader->loadBinaryData(store, m_image, m_doc->localFilePath(), true);
+
+ m_image->unblockUpdates();
+
+ bool retval = true;
+ if (!m_kraLoader->errorMessages().isEmpty()) {
+ m_doc->setErrorMessage(m_kraLoader->errorMessages().join(".\n"));
+ retval = false;
+ }
+ if (retval) {
+ m_activeNodes = m_kraLoader->selectedNodes();
+ m_assistants = m_kraLoader->assistants();
+ }
+
+ return retval;
+}
+
+void KraConverter::cancel()
+{
+ m_stop = true;
+}
+
+
diff --git a/plugins/impex/kra/kra_converter.h b/plugins/impex/kra/kra_converter.h
new file mode 100644
index 0000000000..75bb789758
--- /dev/null
+++ b/plugins/impex/kra/kra_converter.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef _KRA_CONVERTER_H_
+#define _KRA_CONVERTER_H_
+
+#include <stdio.h>
+
+#include <QObject>
+#include <QDomDocument>
+
+#include <KoStore.h>
+#include <kis_png_converter.h>
+#include <kis_types.h>
+#include <kis_kra_saver.h>
+#include <kis_kra_loader.h>
+
+class KisDocument;
+
+class KraConverter : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ KraConverter(KisDocument *doc);
+ virtual ~KraConverter();
+
+ KisImageBuilder_Result buildImage(QIODevice *io);
+ KisImageBuilder_Result buildFile(QIODevice *io);
+ /**
+ * Retrieve the constructed image
+ */
+ KisImageSP image();
+ vKisNodeSP activeNodes();
+ QList<KisPaintingAssistantSP> assistants();
+
+public Q_SLOTS:
+
+ virtual void cancel();
+
+private:
+
+ bool saveRootDocuments(KoStore *store);
+ bool saveToStream(QIODevice *dev);
+ QDomDocument createDomDocument();
+ bool savePreview(KoStore *store);
+ bool oldLoadAndParse(KoStore *store, const QString &filename, KoXmlDocument &xmldoc);
+ bool loadXML(const KoXmlDocument &doc, KoStore *store);
+ bool completeLoading(KoStore *store);
+
+ KisDocument *m_doc {0};
+ KisImageSP m_image;
+
+ vKisNodeSP m_activeNodes;
+ QList<KisPaintingAssistantSP> m_assistants;
+ bool m_stop {false};
+
+ KoStore *m_store {0};
+ KisKraSaver *m_kraSaver {0};
+ KisKraLoader *m_kraLoader {0};
+};
+
+#endif
diff --git a/plugins/impex/kra/kra_export.cpp b/plugins/impex/kra/kra_export.cpp
new file mode 100644
index 0000000000..9354b56148
--- /dev/null
+++ b/plugins/impex/kra/kra_export.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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 "kra_export.h"
+
+#include <QCheckBox>
+#include <QSlider>
+
+#include <kpluginfactory.h>
+#include <QFileInfo>
+#include <QApplication>
+
+#include <KisImportExportManager.h>
+#include <KoColorModelStandardIds.h>
+#include <KoColorSpace.h>
+
+#include <KisExportCheckRegistry.h>
+#include <KisDocument.h>
+#include <kis_image.h>
+#include <kis_node.h>
+#include <kis_group_layer.h>
+#include <kis_paint_layer.h>
+#include <kis_shape_layer.h>
+#include <KoProperties.h>
+
+#include "kra_converter.h"
+
+class KisExternalLayer;
+
+K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_kra_export.json", registerPlugin<KraExport>();)
+
+KraExport::KraExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
+{
+}
+
+KraExport::~KraExport()
+{
+}
+
+KisImportExportFilter::ConversionStatus KraExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
+{
+ KisImageSP image = document->image();
+ Q_CHECK_PTR(image);
+
+ KisPaintDeviceSP pd = image->projection();
+ KraConverter kraConverter(document);
+ KisImageBuilder_Result res = kraConverter.buildFile(io);
+
+ if (res == KisImageBuilder_RESULT_OK) {
+ dbgFile << "success !";
+ return KisImportExportFilter::OK;
+ }
+ dbgFile << " Result =" << res;
+ return KisImportExportFilter::InternalError;
+}
+
+void KraExport::initializeCapabilities()
+{
+ // Kra supports everything, by definition
+ KisExportCheckFactory *factory = 0;
+ Q_FOREACH(const QString &id, KisExportCheckRegistry::instance()->keys()) {
+ factory = KisExportCheckRegistry::instance()->get(id);
+ addCapability(factory->create(KisExportCheckBase::SUPPORTED));
+ }
+}
+
+#include <kra_export.moc>
+
diff --git a/libs/ui/kra/kis_kra_utils.h b/plugins/impex/kra/kra_export.h
similarity index 60%
copy from libs/ui/kra/kis_kra_utils.h
copy to plugins/impex/kra/kra_export.h
index 052900495f..349f68b0b6 100644
--- a/libs/ui/kra/kis_kra_utils.h
+++ b/plugins/impex/kra/kra_export.h
@@ -1,32 +1,39 @@
-/* This file is part of the KDE project
- * Copyright 2011 (C) Silvio Heinrich <plassy@web.de>
+/*
+ * Copyright (C) 2016 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.
*/
-#ifndef _KIS_KRA_UTILS_
-#define _KIS_KRA_UTILS_
-#include <QString>
-#include <QBitArray>
+#ifndef _KRA_EXPORT_H_
+#define _KRA_EXPORT_H_
-namespace KRA {
+#include <QVariant>
-QString flagsToString(const QBitArray& flags, int size=-1, char trueToken='1', char falseToken='0', bool defaultTrue=true);
-QBitArray stringToFlags(const QString& string, int size=-1, char token='0', bool defaultTrue=true);
+#include <KisImportExportFilter.h>
-}
+class KraExport : public KisImportExportFilter
+{
+ Q_OBJECT
+public:
+ KraExport(QObject *parent, const QVariantList &);
+ virtual ~KraExport();
+public:
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
-#endif // _KIS_KRA_UTILS_
+};
+
+#endif
diff --git a/plugins/impex/kra/kra_import.cpp b/plugins/impex/kra/kra_import.cpp
new file mode 100644
index 0000000000..7f24f5dc67
--- /dev/null
+++ b/plugins/impex/kra/kra_import.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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 "kra_import.h"
+
+#include <kpluginfactory.h>
+#include <QFileInfo>
+
+#include <KisDocument.h>
+#include <kis_image.h>
+
+#include "kra_converter.h"
+
+K_PLUGIN_FACTORY_WITH_JSON(ImportFactory, "krita_kra_import.json", registerPlugin<KraImport>();)
+
+KraImport::KraImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
+{
+}
+
+KraImport::~KraImport()
+{
+}
+
+KisImportExportFilter::ConversionStatus KraImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
+{
+ KraConverter kraConverter(document);
+ switch (kraConverter.buildImage(io)) {
+ case KisImageBuilder_RESULT_UNSUPPORTED:
+ return KisImportExportFilter::NotImplemented;
+ break;
+ case KisImageBuilder_RESULT_INVALID_ARG:
+ return KisImportExportFilter::BadMimeType;
+ break;
+ case KisImageBuilder_RESULT_NO_URI:
+ case KisImageBuilder_RESULT_NOT_LOCAL:
+ return KisImportExportFilter::FileNotFound;
+ break;
+ case KisImageBuilder_RESULT_BAD_FETCH:
+ case KisImageBuilder_RESULT_EMPTY:
+ return KisImportExportFilter::ParsingError;
+ break;
+ case KisImageBuilder_RESULT_FAILURE:
+ return KisImportExportFilter::InternalError;
+ break;
+ case KisImageBuilder_RESULT_OK:
+ document->setCurrentImage(kraConverter.image());
+ if (kraConverter.activeNodes().size() > 0) {
+ document->setPreActivatedNode(kraConverter.activeNodes()[0]);
+ }
+ if (kraConverter.assistants().size() > 0) {
+ document->setAssistants(kraConverter.assistants());
+ }
+ return KisImportExportFilter::OK;
+ default:
+ break;
+ }
+ return KisImportExportFilter::InternalError;
+}
+
+#include <kra_import.moc>
+
diff --git a/libs/ui/kra/kis_kra_utils.h b/plugins/impex/kra/kra_import.h
similarity index 62%
copy from libs/ui/kra/kis_kra_utils.h
copy to plugins/impex/kra/kra_import.h
index 052900495f..377bd42f32 100644
--- a/libs/ui/kra/kis_kra_utils.h
+++ b/plugins/impex/kra/kra_import.h
@@ -1,32 +1,37 @@
-/* This file is part of the KDE project
- * Copyright 2011 (C) Silvio Heinrich <plassy@web.de>
+/*
+ * Copyright (C) 2016 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.
*/
-#ifndef _KIS_KRA_UTILS_
-#define _KIS_KRA_UTILS_
-#include <QString>
-#include <QBitArray>
+#ifndef KRA_IMPORT_H_
+#define KRA_IMPORT_H_
-namespace KRA {
+#include <QVariant>
-QString flagsToString(const QBitArray& flags, int size=-1, char trueToken='1', char falseToken='0', bool defaultTrue=true);
-QBitArray stringToFlags(const QString& string, int size=-1, char token='0', bool defaultTrue=true);
+#include <KisImportExportFilter.h>
-}
+class KraImport : public KisImportExportFilter
+{
+ Q_OBJECT
+public:
+ KraImport(QObject *parent, const QVariantList &);
+ virtual ~KraImport();
+public:
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+};
-#endif // _KIS_KRA_UTILS_
+#endif
diff --git a/plugins/impex/kra/krita_kra.desktop b/plugins/impex/kra/krita_kra.desktop
new file mode 100644
index 0000000000..08bb8f13b1
--- /dev/null
+++ b/plugins/impex/kra/krita_kra.desktop
@@ -0,0 +1,73 @@
+[Desktop Entry]
+Categories=Qt;KDE;Office;Graphics;
+Comment=
+Exec=krita %u
+Icon=calligrakrita
+MimeType=application/x-krita;
+Name=Krita
+Name[af]=Krita
+Name[bg]=Krita
+Name[br]=Krita
+Name[bs]=Krita
+Name[ca]=Krita
+Name[ca@valencia]=Krita
+Name[cs]=Krita
+Name[cy]=Krita
+Name[da]=Krita
+Name[de]=Krita
+Name[el]=Krita
+Name[en_GB]=Krita
+Name[eo]=Krita
+Name[es]=Krita
+Name[et]=Krita
+Name[eu]=Krita
+Name[fi]=Krita
+Name[fr]=Krita
+Name[fy]=Krita
+Name[ga]=Krita
+Name[gl]=Krita
+Name[he]=Krita
+Name[hi]=केरिता
+Name[hne]=केरिता
+Name[hr]=Krita
+Name[hu]=Krita
+Name[ia]=Krita
+Name[is]=Krita
+Name[it]=Krita
+Name[kk]=Krita
+Name[ko]=Krita
+Name[lt]=Krita
+Name[lv]=Krita
+Name[mr]=क्रिटा
+Name[ms]=Krita
+Name[nb]=Krita
+Name[nds]=Krita
+Name[ne]=क्रिता
+Name[nl]=Krita
+Name[pl]=Krita
+Name[pt]=Krita
+Name[pt_BR]=Krita
+Name[ro]=Krita
+Name[ru]=Krita
+Name[se]=Krita
+Name[sk]=Krita
+Name[sl]=Krita
+Name[sv]=Krita
+Name[ta]=கிரிட்டா
+Name[tg]=Krita
+Name[tr]=Krita
+Name[ug]=Krita
+Name[uk]=Krita
+Name[uz]=Krita
+Name[uz@cyrillic]=Krita
+Name[wa]=Krita
+Name[xh]=Krita
+Name[x-test]=xxKritaxx
+Name[zh_CN]=Krita
+Name[zh_TW]=繪圖_Krita
+StartupNotify=true
+Terminal=false
+Type=Application
+X-KDE-SubstituteUID=false
+X-KDE-Username=
+NoDisplay=true
diff --git a/plugins/impex/ora/krita_ora_import.json b/plugins/impex/kra/krita_kra_export.json
similarity index 57%
copy from plugins/impex/ora/krita_ora_import.json
copy to plugins/impex/kra/krita_kra_export.json
index a2d4a69348..fbdc44f287 100644
--- a/plugins/impex/ora/krita_ora_import.json
+++ b/plugins/impex/kra/krita_kra_export.json
@@ -1,14 +1,13 @@
{
"Icon": "",
- "Id": "Krita ora Import Filter",
+ "Id": "Krita Native Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "application/x-krita",
- "X-KDE-Import": "image/openraster",
- "X-KDE-Library": "kritaoraimport",
+ "X-KDE-Library": "kritakraexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
- "X-KDE-Extensions" : "ora"
+ "X-KDE-Extensions" : "kra"
}
diff --git a/plugins/impex/qml/krita_qml_export.json b/plugins/impex/kra/krita_kra_import.json
similarity index 58%
copy from plugins/impex/qml/krita_qml_export.json
copy to plugins/impex/kra/krita_kra_import.json
index cac04141f8..b56cd63604 100644
--- a/plugins/impex/qml/krita_qml_export.json
+++ b/plugins/impex/kra/krita_kra_import.json
@@ -1,14 +1,13 @@
{
"Icon": "",
- "Id": "Krita QML Export Filter",
+ "Id": "Krita Native Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "text/x-qml",
"X-KDE-Import": "application/x-krita",
- "X-KDE-Library": "kritaqmlexport",
+ "X-KDE-Library": "kritakraimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
- "X-KDE-Extensions" : "qml"
+ "X-KDE-Extensions" : "kra"
}
diff --git a/plugins/impex/libkra/CMakeLists.txt b/plugins/impex/libkra/CMakeLists.txt
new file mode 100644
index 0000000000..d127d20d4d
--- /dev/null
+++ b/plugins/impex/libkra/CMakeLists.txt
@@ -0,0 +1,29 @@
+#add_subdirectory(tests)
+
+set(kritalibkra_LIB_SRCS
+ kis_colorize_dom_utils.cpp
+ kis_colorize_dom_utils.h
+ kis_kra_loader.cpp
+ kis_kra_loader.h
+ kis_kra_load_visitor.cpp
+ kis_kra_load_visitor.h
+ kis_kra_saver.cpp
+ kis_kra_saver.h
+ kis_kra_save_visitor.cpp
+ kis_kra_save_visitor.h
+ kis_kra_savexml_visitor.cpp
+ kis_kra_savexml_visitor.h
+ kis_kra_tags.h
+ kis_kra_utils.cpp
+ kis_kra_utils.h
+)
+
+add_library(kritalibkra SHARED ${kritalibkra_LIB_SRCS})
+target_link_libraries(kritalibkra kritaui)
+generate_export_header(kritalibkra BASE_NAME kritalibkra)
+
+set_target_properties(kritalibkra PROPERTIES
+ VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
+)
+install(TARGETS kritalibkra ${INSTALL_TARGETS_DEFAULT_ARGS} )
+
diff --git a/libs/ui/kra/kis_colorize_dom_utils.cpp b/plugins/impex/libkra/kis_colorize_dom_utils.cpp
similarity index 100%
rename from libs/ui/kra/kis_colorize_dom_utils.cpp
rename to plugins/impex/libkra/kis_colorize_dom_utils.cpp
diff --git a/libs/ui/kra/kis_colorize_dom_utils.h b/plugins/impex/libkra/kis_colorize_dom_utils.h
similarity index 78%
rename from libs/ui/kra/kis_colorize_dom_utils.h
rename to plugins/impex/libkra/kis_colorize_dom_utils.h
index bfc67fd487..d5f5886437 100644
--- a/libs/ui/kra/kis_colorize_dom_utils.h
+++ b/plugins/impex/libkra/kis_colorize_dom_utils.h
@@ -1,34 +1,33 @@
/*
* Copyright (c) 2016 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_COLORIZE_DOM_UTILS_H
#define __KIS_COLORIZE_DOM_UTILS_H
#include <KoColorSpaceRegistry.h>
-#include "kritaui_export.h"
-
+#include "kritalibkra_export.h"
namespace KisLazyFillTools {
struct KeyStroke;
}
namespace KisDomUtils {
- void KRITAUI_EXPORT saveValue(QDomElement *parent, const QString &tag, const KisLazyFillTools::KeyStroke &stroke);
- bool KRITAUI_EXPORT loadValue(const QDomElement &e, KisLazyFillTools::KeyStroke *stroke, const KoColorSpace *colorSpace);
+ void KRITALIBKRA_EXPORT saveValue(QDomElement *parent, const QString &tag, const KisLazyFillTools::KeyStroke &stroke);
+ bool KRITALIBKRA_EXPORT loadValue(const QDomElement &e, KisLazyFillTools::KeyStroke *stroke, const KoColorSpace *colorSpace);
}
#endif /* __KIS_COLORIZE_DOM_UTILS_H */
diff --git a/libs/ui/kra/kis_kra_load_visitor.cpp b/plugins/impex/libkra/kis_kra_load_visitor.cpp
similarity index 99%
rename from libs/ui/kra/kis_kra_load_visitor.cpp
rename to plugins/impex/libkra/kis_kra_load_visitor.cpp
index 1cc44697e6..ac46affb93 100644
--- a/libs/ui/kra/kis_kra_load_visitor.cpp
+++ b/plugins/impex/libkra/kis_kra_load_visitor.cpp
@@ -1,656 +1,656 @@
/*
* 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_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 <lazybrush/kis_colorize_mask.h>
#include <lazybrush/kis_lazy_fill_tools.h>
#include "kis_shape_selection.h"
#include "kis_colorize_dom_utils.h"
#include "kis_dom_utils.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_paint_device_frames_interface.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,
+KisKraLoadVisitor::KisKraLoadVisitor(KisImageSP image,
KoStore *store,
QMap<KisNode *, QString> &layerFilenames,
QMap<KisNode *, QString> &keyframeFilenames,
const QString & name,
int syntaxVersion) :
KisNodeVisitor(),
m_layerFilenames(layerFilenames),
m_keyframeFilenames(keyframeFilenames)
{
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();
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)
{
loadNodeKeyframes(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 (*layer->colorSpace() != *m_image->colorSpace()) {
layer->resetCache(m_image->colorSpace());
}
if (!loadMetaData(layer)) {
return false;
}
bool result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisAdjustmentLayer* layer)
{
loadNodeKeyframes(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;
loadNodeKeyframes(layer);
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);
loadNodeKeyframes(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);
loadNodeKeyframes(mask);
params->clearChangedFlag();
return true;
}
}
return false;
}
bool KisKraLoadVisitor::visit(KisTransparencyMask *mask)
{
initSelectionForMask(mask);
loadNodeKeyframes(mask);
return loadSelection(getLocation(mask), mask->selection());
}
bool KisKraLoadVisitor::visit(KisSelectionMask *mask)
{
initSelectionForMask(mask);
return loadSelection(getLocation(mask), mask->selection());
}
bool KisKraLoadVisitor::visit(KisColorizeMask *mask)
{
m_store->pushDirectory();
QString location = getLocation(mask, DOT_COLORIZE_MASK);
m_store->enterDirectory(location) ;
QByteArray data;
if (!m_store->extractFile("content.xml", data))
return false;
QDomDocument doc;
if (!doc.setContent(data))
return false;
QVector<KisLazyFillTools::KeyStroke> strokes;
if (!KisDomUtils::loadValue(doc.documentElement(), COLORIZE_KEYSTROKES_SECTION, &strokes, mask->colorSpace()))
return false;
int i = 0;
Q_FOREACH (const KisLazyFillTools::KeyStroke &stroke, strokes) {
const QString fileName = QString("%1_%2").arg(COLORIZE_KEYSTROKE).arg(i++);
loadPaintDevice(stroke.dev, fileName);
}
mask->setKeyStrokesDirect(QList<KisLazyFillTools::KeyStroke>::fromVector(strokes));
loadPaintDevice(mask->coloringProjection(), COLORIZE_COLORING_DEVICE);
m_store->popDirectory();
return true;
}
QStringList KisKraLoadVisitor::errorMessages() const
{
return m_errorMessages;
}
struct SimpleDevicePolicy
{
bool read(KisPaintDeviceSP dev, QIODevice *stream) {
return dev->read(stream);
}
void setDefaultPixel(KisPaintDeviceSP dev, const KoColor &defaultPixel) const {
return dev->setDefaultPixel(defaultPixel);
}
};
struct FramedDevicePolicy
{
FramedDevicePolicy(int frameId)
: m_frameId(frameId) {}
bool read(KisPaintDeviceSP dev, QIODevice *stream) {
return dev->framesInterface()->readFrame(stream, m_frameId);
}
void setDefaultPixel(KisPaintDeviceSP dev, const KoColor &defaultPixel) const {
return dev->framesInterface()->setFrameDefaultPixel(defaultPixel, m_frameId);
}
int m_frameId;
};
bool KisKraLoadVisitor::loadPaintDevice(KisPaintDeviceSP device, const QString& location)
{
// Layer data
KisPaintDeviceFramesInterface *frameInterface = device->framesInterface();
QList<int> frames;
if (frameInterface) {
frames = device->framesInterface()->frames();
}
if (!frameInterface || frames.count() <= 1) {
return loadPaintDeviceFrame(device, location, SimpleDevicePolicy());
} else {
KisRasterKeyframeChannel *keyframeChannel = device->keyframeChannel();
for (int i = 0; i < frames.count(); i++) {
int id = frames[i];
QString frameFilename = getLocation(keyframeChannel->frameFilename(id));
Q_ASSERT(!frameFilename.isEmpty());
if (!loadPaintDeviceFrame(device, frameFilename, FramedDevicePolicy(id))) {
return false;
}
}
}
return true;
}
template<class DevicePolicy>
bool KisKraLoadVisitor::loadPaintDeviceFrame(KisPaintDeviceSP device, const QString &location, DevicePolicy policy)
{
if (m_store->open(location)) {
if (!policy.read(device, 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) {
KoColor color(Qt::transparent, device->colorSpace());
m_store->read((char*)color.data(), pixelSize);
policy.setDefaultPixel(device, color);
}
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);
if (device->setProfile(profile)) {
return true;
}
}
m_errorMessages << i18n("Could not load profile %1.", location);
return false;
}
bool KisKraLoadVisitor::loadFilterConfiguration(KisFilterConfigurationSP 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)
{
return getLocation(m_layerFilenames[node], suffix);
}
QString KisKraLoadVisitor::getLocation(const QString &filename, const QString& suffix)
{
QString location = m_external ? QString() : m_uri;
location += m_name + LAYER_PATH + filename + suffix;
return location;
}
void KisKraLoadVisitor::loadNodeKeyframes(KisNode *node)
{
if (!m_keyframeFilenames.contains(node)) return;
node->enableAnimation();
const QString &location = getLocation(m_keyframeFilenames[node]);
if (!m_store->open(location)) {
m_errorMessages << i18n("Could not load keyframes from %1.", location);
return;
}
QString errorMsg;
int errorLine;
int errorColumn;
KoXmlDocument doc = KoXmlDocument(true);
bool ok = doc.setContent(m_store->device(), &errorMsg, &errorLine, &errorColumn);
m_store->close();
if (!ok) {
m_errorMessages << i18n("parsing error in the keyframe file %1 at line %2, column %3\nError message: %4", location, errorLine, errorColumn, i18n(errorMsg.toUtf8()));
return;
}
QDomDocument dom;
KoXml::asQDomElement(dom, doc.documentElement());
QDomElement root = dom.firstChildElement();
for (QDomElement child = root.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) {
if (child.nodeName().toUpper() == "CHANNEL") {
QString id = child.attribute("name");
KisKeyframeChannel *channel = node->getKeyframeChannel(id, true);
if (!channel) {
m_errorMessages << i18n("unknown keyframe channel type: %1 in %2", id, location);
continue;
}
channel->loadXML(child);
}
}
}
diff --git a/libs/ui/kra/kis_kra_load_visitor.h b/plugins/impex/libkra/kis_kra_load_visitor.h
similarity index 94%
rename from libs/ui/kra/kis_kra_load_visitor.h
rename to plugins/impex/libkra/kis_kra_load_visitor.h
index 719f452756..9f08fc046d 100644
--- a/libs/ui/kra/kis_kra_load_visitor.h
+++ b/plugins/impex/libkra/kis_kra_load_visitor.h
@@ -1,94 +1,96 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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.
*/
#ifndef KIS_KRA_LOAD_VISITOR_H_
#define KIS_KRA_LOAD_VISITOR_H_
#include <QRect>
#include <QStringList>
// kritaimage
#include "kis_types.h"
#include "kis_node_visitor.h"
+#include "kritalibkra_export.h"
+
class KisFilterConfiguration;
class KoStore;
-class KisKraLoadVisitor : public KisNodeVisitor
+class KRITALIBKRA_EXPORT KisKraLoadVisitor : public KisNodeVisitor
{
public:
- KisKraLoadVisitor(KisImageWSP image,
+ KisKraLoadVisitor(KisImageSP image,
KoStore *store,
QMap<KisNode *, QString> &layerFilenames,
QMap<KisNode *, QString> &keyframeFilenames,
const QString & name,
int syntaxVersion);
public:
void setExternalUri(const QString &uri);
bool visit(KisNode*) {
return true;
}
bool visit(KisExternalLayer *);
bool visit(KisPaintLayer *layer);
bool visit(KisGroupLayer *layer);
bool visit(KisAdjustmentLayer* layer);
bool visit(KisGeneratorLayer* layer);
bool visit(KisCloneLayer *layer);
bool visit(KisFilterMask *mask);
bool visit(KisTransformMask *mask);
bool visit(KisTransparencyMask *mask);
bool visit(KisSelectionMask *mask);
bool visit(KisColorizeMask *mask);
QStringList errorMessages() const;
private:
bool loadPaintDevice(KisPaintDeviceSP device, const QString& location);
template<class DevicePolicy>
bool loadPaintDeviceFrame(KisPaintDeviceSP device, const QString &location, DevicePolicy policy);
bool loadProfile(KisPaintDeviceSP device, const QString& location);
bool loadFilterConfiguration(KisFilterConfigurationSP kfc, const QString& location);
bool loadMetaData(KisNode* node);
void initSelectionForMask(KisMask *mask);
bool loadSelection(const QString& location, KisSelectionSP dstSelection);
QString getLocation(KisNode* node, const QString& suffix = QString());
QString getLocation(const QString &filename, const QString &suffix = QString());
void loadNodeKeyframes(KisNode *node);
private:
- KisImageWSP m_image;
+ KisImageSP m_image;
KoStore *m_store;
bool m_external;
QString m_uri;
QMap<KisNode *, QString> m_layerFilenames;
QMap<KisNode *, QString> m_keyframeFilenames;
QString m_name;
int m_syntaxVersion;
QStringList m_errorMessages;
};
#endif // KIS_KRA_LOAD_VISITOR_H_
diff --git a/libs/ui/kra/kis_kra_loader.cpp b/plugins/impex/libkra/kis_kra_loader.cpp
similarity index 99%
rename from libs/ui/kra/kis_kra_loader.cpp
rename to plugins/impex/libkra/kis_kra_loader.cpp
index 590acf52a5..b076d719bb 100644
--- a/libs/ui/kra/kis_kra_loader.cpp
+++ b/plugins/impex/libkra/kis_kra_loader.cpp
@@ -1,1104 +1,1105 @@
/* 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 "kis_kra_loader.h"
#include <QApplication>
#include <QStringList>
#include <QMessageBox>
#include <QUrl>
#include <QBuffer>
#include <KoStore.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceEngine.h>
#include <KoColorProfile.h>
#include <KoDocumentInfo.h>
#include <KoFileDialog.h>
#include <KisImportExportManager.h>
#include <KoXmlReader.h>
+#include <KoStoreDevice.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 "lazybrush/kis_colorize_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 "kis_keyframe_channel.h"
#include <kis_filter_configuration.h>
#include "KisDocument.h"
#include "kis_config.h"
#include "kis_kra_tags.h"
#include "kis_kra_utils.h"
#include "kis_kra_load_visitor.h"
#include "kis_dom_utils.h"
#include "kis_image_animation_interface.h"
#include "kis_time_range.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "kis_image_config.h"
#include "KisProofingConfiguration.h"
#include "kis_layer_properties_icons.h"
#include "kis_node_view_color_scheme.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<KisPaintingAssistantSP> assistants;
QMap<KisNode*, QString> keyframeFilenames;
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;
}
KisImageSP KisKraLoader::loadXML(const KoXmlElement& element)
{
QString attr;
KisImageSP 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 KisImageSP(0);
}
if ((attr = element.attribute(WIDTH)).isNull()) {
m_d->errorMessages << i18n("Image does not specify a width.");
return KisImageSP(0);
}
width = KisDomUtils::toInt(attr);
if ((attr = element.attribute(HEIGHT)).isNull()) {
m_d->errorMessages << i18n("Image does not specify a height.");
return KisImageSP(0);
}
height = KisDomUtils::toInt(attr);
m_d->imageComment = element.attribute(DESCRIPTION);
xres = 100.0 / 72.0;
if (!(attr = element.attribute(X_RESOLUTION)).isNull()) {
qreal value = KisDomUtils::toDouble(attr);
if (value > 1.0) {
xres = value / 72.0;
}
}
yres = 100.0 / 72.0;
if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) {
qreal value = KisDomUtils::toDouble(attr);
if (value > 1.0) {
yres = value / 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 KisImageSP(0);
}
}
KisImageConfig cfgImage;
KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration();
if (!(attr = element.attribute(PROOFINGPROFILENAME)).isNull()) {
proofingConfig->proofingProfile = attr;
}
if (!(attr = element.attribute(PROOFINGMODEL)).isNull()) {
proofingConfig->proofingModel = attr;
}
if (!(attr = element.attribute(PROOFINGDEPTH)).isNull()) {
proofingConfig->proofingDepth = attr;
}
if (!(attr = element.attribute(PROOFINGINTENT)).isNull()) {
proofingConfig->intent = (KoColorConversionTransformation::Intent) KisDomUtils::toInt(attr);
}
if (!(attr = element.attribute(PROOFINGADAPTATIONSTATE)).isNull()) {
proofingConfig->adaptationState = KisDomUtils::toDouble(attr);
}
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() == CANVASPROJECTIONCOLOR) {
if (e.hasAttribute(COLORBYTEDATA)) {
QByteArray colorData = QByteArray::fromBase64(e.attribute(COLORBYTEDATA).toLatin1());
KoColor color((const quint8*)colorData.data(), image->colorSpace());
image->setDefaultProjectionColor(color);
}
}
if(e.tagName()== PROOFINGWARNINGCOLOR) {
QDomDocument dom;
KoXml::asQDomElement(dom, e);
QDomElement eq = dom.firstChildElement();
proofingConfig->warningColor = KoColor::fromXML(eq.firstChildElement(), Integer8BitsColorDepthID.id(), QHash<QString, QString>());
}
if (e.tagName().toLower() == "animation") {
loadAnimationMetadata(e, image);
}
}
image->setProofingConfiguration(proofingConfig);
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() == "grid") {
loadGrid(e);
} else if (e.tagName() == "guides") {
loadGuides(e);
} else if (e.tagName() == "assistants") {
loadAssistantsList(e);
}
}
return image;
}
void KisKraLoader::loadBinaryData(KoStore * store, KisImageSP 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 embed proofing profile, it only needs to be loaded into Krita, not assigned.
location = external ? QString() : uri;
location += m_d->imageName + ICC_PROOFING_PATH;
if (store->hasFile(location)) {
if (store->open(location)) {
QByteArray proofingData;
proofingData.resize(store->size());
bool proofingProfileRes = (store->read(proofingData.data(), store->size())>-1);
store->close();
if (proofingProfileRes)
{
const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->createColorProfile(image->proofingConfiguration()->proofingModel, image->proofingConfiguration()->proofingDepth, proofingData);
if (proofingProfile->valid()){
//if (KoColorSpaceEngineRegistry::instance()->get("icc")) {
// KoColorSpaceEngineRegistry::instance()->get("icc")->addProfile(proofingProfile->fileName());
//}
KoColorSpaceRegistry::instance()->addProfile(proofingProfile);
}
}
}
}
// 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->keyframeFilenames, 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 {
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<KisPaintingAssistantSP> 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(toQShared(assistant));
}
}
loadedAssistant++;
}
}
void KisKraLoader::loadAnimationMetadata(const KoXmlElement &element, KisImageSP image)
{
QDomDocument qDom;
KoXml::asQDomElement(qDom, element);
QDomElement qElement = qDom.firstChildElement();
float framerate;
KisTimeRange range;
int currentTime;
KisImageAnimationInterface *animation = image->animationInterface();
if (KisDomUtils::loadValue(qElement, "framerate", &framerate)) {
animation->setFramerate(framerate);
}
if (KisDomUtils::loadValue(qElement, "range", &range)) {
animation->setFullClipRange(range);
}
if (KisDomUtils::loadValue(qElement, "currentTime", &currentTime)) {
animation->switchCurrentTimeAsync(currentTime);
}
}
KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageSP 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, KisImageSP 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;
}
}
const bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true;
const bool locked = element.attribute(LOCKED, "0") == "0" ? false : true;
const bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true;
int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt();
QVector<QColor> labels = KisNodeViewColorScheme::instance()->allColorLabels();
if (colorLabelIndex >= labels.size()) {
colorLabelIndex = labels.size() - 1;
}
// 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 == COLORIZE_MASK)
node = loadColorizeMask(image, element, parent, colorSpace);
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->setColorLabelIndex(colorLabelIndex);
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") || node->inherits("KisColorizeMask")) {
QString compositeOpName = element.attribute(COMPOSITE_OP, "normal");
node->setCompositeOpId(compositeOpName);
}
if (node->inherits("KisLayer")) {
KisLayer* layer = qobject_cast<KisLayer*>(node.data());
QBitArray channelFlags = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount());
layer->setChannelFlags(channelFlags);
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 {
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);
bool onionEnabled = element.attribute(ONION_SKIN_ENABLED, "0") == "0" ? false : true;
layer->setOnionSkinEnabled(onionEnabled);
bool timelineEnabled = element.attribute(VISIBLE_IN_TIMELINE, "0") == "0" ? false : true;
layer->setUseInTimeline(timelineEnabled);
}
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);
}
if (element.hasAttribute(KEYFRAME_FILE)) {
m_d->keyframeFilenames.insert(node.data(), element.attribute(KEYFRAME_FILE));
}
return node;
}
KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageSP 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, KisImageSP 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(KisImportExportManager::Import));
dialog.setDefaultDir(basePath);
QString url = dialog.filename();
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, KisImageSP 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, KisImageSP 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!
}
KisFilterConfigurationSP 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, KisImageSP 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, KisImageSP 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!
}
KisFilterConfigurationSP 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, KisImageSP 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!
}
KisFilterConfigurationSP 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(KisImageSP 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;
}
KisNodeSP KisKraLoader::loadColorizeMask(KisImageSP image, const KoXmlElement& element, KisNodeSP parent, const KoColorSpace *colorSpace)
{
Q_UNUSED(parent);
KisColorizeMaskSP mask = new KisColorizeMask();
bool editKeystrokes = element.attribute(COLORIZE_EDIT_KEYSTROKES, "1") == "0" ? false : true;
bool showColoring = element.attribute(COLORIZE_SHOW_COLORING, "1") == "0" ? false : true;
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, editKeystrokes, image);
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, showColoring, image);
delete mask->setColorSpace(colorSpace);
mask->setImage(image);
return mask;
}
void KisKraLoader::loadCompositions(const KoXmlElement& elem, KisImageSP 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;
KisLayerCompositionSP 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++;
}
}
void KisKraLoader::loadGrid(const KoXmlElement& elem)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement domElement = dom.firstChildElement();
KisGridConfig config;
config.loadDynamicDataFromXml(domElement);
config.loadStaticData();
m_d->document->setGridConfig(config);
}
void KisKraLoader::loadGuides(const KoXmlElement& elem)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement domElement = dom.firstChildElement();
KisGuidesConfig guides;
guides.loadFromXml(domElement);
m_d->document->setGuidesConfig(guides);
}
diff --git a/libs/ui/kra/kis_kra_loader.h b/plugins/impex/libkra/kis_kra_loader.h
similarity index 98%
rename from libs/ui/kra/kis_kra_loader.h
rename to plugins/impex/libkra/kis_kra_loader.h
index a4e756aacc..168dc243ad 100644
--- a/libs/ui/kra/kis_kra_loader.h
+++ b/plugins/impex/libkra/kis_kra_loader.h
@@ -1,113 +1,113 @@
/* 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.
*/
#ifndef KIS_KRA_LOADER_H
#define KIS_KRA_LOADER_H
class QString;
class QStringList;
#include "KoXmlReaderForward.h"
class KoStore;
class KisDocument;
class KoColorSpace;
class KisPaintingAssistant;
#include <kis_types.h>
-#include <kritaui_export.h>
+#include "kritalibkra_export.h"
/**
* Load old-style 1.x .kra files. Updated for 2.0, let's try to stay
* compatible. But 2.0 won't be able to save 1.x .kra files unless we
* implement an export filter.
*/
-class KRITAUI_EXPORT KisKraLoader
+class KRITALIBKRA_EXPORT KisKraLoader
{
public:
KisKraLoader(KisDocument * document, int syntaxVersion);
~KisKraLoader();
/**
* Loading is done in two steps: first all xml is loaded, then, in finishLoading,
* the actual layer data is loaded.
*/
KisImageSP loadXML(const KoXmlElement& elem);
void loadBinaryData(KoStore* store, KisImageSP image, const QString & uri, bool external);
vKisNodeSP selectedNodes() const;
// it's neater to follow the same design as with selectedNodes, so let's have a getter here
QList<KisPaintingAssistantSP> assistants() const;
/// if empty, loading didn't fail...
QStringList errorMessages() const;
private:
// this needs to be private, for neatness sake
void loadAssistants(KoStore* store, const QString & uri, bool external);
void loadAnimationMetadata(const KoXmlElement& element, KisImageSP image);
KisNodeSP loadNodes(const KoXmlElement& element, KisImageSP image, KisNodeSP parent);
KisNodeSP loadNode(const KoXmlElement& elem, KisImageSP image, KisNodeSP parent);
KisNodeSP loadPaintLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity);
KisNodeSP loadGroupLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity);
KisNodeSP loadAdjustmentLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity);
KisNodeSP loadShapeLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity);
KisNodeSP loadGeneratorLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity);
KisNodeSP loadCloneLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity);
KisNodeSP loadFilterMask(const KoXmlElement& elem, KisNodeSP parent);
KisNodeSP loadTransformMask(const KoXmlElement& elem, KisNodeSP parent);
KisNodeSP loadTransparencyMask(const KoXmlElement& elem, KisNodeSP parent);
KisNodeSP loadSelectionMask(KisImageSP image, const KoXmlElement& elem, KisNodeSP parent);
KisNodeSP loadColorizeMask(KisImageSP image, const KoXmlElement& elem, KisNodeSP parent, const KoColorSpace *colorSpace);
KisNodeSP loadFileLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, quint32 opacity);
void loadNodeKeyframes(KoStore *store, const QString &location, KisNodeSP node);
void loadCompositions(const KoXmlElement& elem, KisImageSP image);
void loadAssistantsList(const KoXmlElement& elem);
void loadGrid(const KoXmlElement& elem);
void loadGuides(const KoXmlElement& elem);
private:
struct Private;
Private * const m_d;
};
#endif
diff --git a/libs/ui/kra/kis_kra_save_visitor.cpp b/plugins/impex/libkra/kis_kra_save_visitor.cpp
similarity index 99%
rename from libs/ui/kra/kis_kra_save_visitor.cpp
rename to plugins/impex/libkra/kis_kra_save_visitor.cpp
index 4bc56c4385..60a6f95153 100644
--- a/libs/ui/kra/kis_kra_save_visitor.cpp
+++ b/plugins/impex/libkra/kis_kra_save_visitor.cpp
@@ -1,535 +1,535 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (c) 2007-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 "kra/kis_kra_save_visitor.h"
-#include "kra/kis_kra_tags.h"
+#include "kis_kra_save_visitor.h"
+#include "kis_kra_tags.h"
#include <QBuffer>
#include <QByteArray>
#include <KoColorProfile.h>
#include <KoStore.h>
#include <KoColorSpace.h>
#include <filter/kis_filter_configuration.h>
#include <generator/kis_generator_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_annotation.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <kis_selection.h>
#include <kis_shape_layer.h>
#include <kis_file_layer.h>
#include <kis_clone_layer.h>
#include <kis_mask.h>
#include <kis_filter_mask.h>
#include <kis_transform_mask.h>
#include <kis_transform_mask_params_interface.h>
#include <kis_transparency_mask.h>
#include <kis_selection_mask.h>
#include "lazybrush/kis_colorize_mask.h"
#include <kis_selection_component.h>
#include <kis_pixel_selection.h>
#include <metadata/kis_meta_data_store.h>
#include <metadata/kis_meta_data_io_backend.h>
#include "kis_config.h"
#include "kis_store_paintdevice_writer.h"
#include "flake/kis_shape_selection.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_paint_device_frames_interface.h"
#include "lazybrush/kis_lazy_fill_tools.h"
#include <KoStoreDevice.h>
#include "kis_colorize_dom_utils.h"
#include "kis_dom_utils.h"
using namespace KRA;
KisKraSaveVisitor::KisKraSaveVisitor(KoStore *store, const QString & name, QMap<const KisNode*, QString> nodeFileNames)
: KisNodeVisitor()
, m_store(store)
, m_external(false)
, m_name(name)
, m_nodeFileNames(nodeFileNames)
, m_writer(new KisStorePaintDeviceWriter(store))
{
}
KisKraSaveVisitor::~KisKraSaveVisitor()
{
delete m_writer;
}
void KisKraSaveVisitor::setExternalUri(const QString &uri)
{
m_external = true;
m_uri = uri;
}
bool KisKraSaveVisitor::visit(KisExternalLayer * layer)
{
bool result = false;
if (KisShapeLayer* shapeLayer = dynamic_cast<KisShapeLayer*>(layer)) {
if (!saveMetaData(layer)) {
m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
return false;
}
m_store->pushDirectory();
QString location = getLocation(layer, DOT_SHAPE_LAYER);
result = m_store->enterDirectory(location);
if (!result) {
m_errorMessages << i18n("Failed to open %1.", location);
}
else {
result = shapeLayer->saveLayer(m_store);
m_store->popDirectory();
}
}
else if (KisFileLayer *fileLayer = dynamic_cast<KisFileLayer*>(layer)) {
Q_UNUSED(fileLayer); // We don't save data for file layers, but we still want to save the masks.
result = true;
}
return result && visitAllInverse(layer);
}
bool KisKraSaveVisitor::visit(KisPaintLayer *layer)
{
if (!savePaintDevice(layer->paintDevice(), getLocation(layer))) {
m_errorMessages << i18n("Failed to save the pixel data for layer %1.", layer->name());
return false;
}
if (!saveAnnotations(layer)) {
m_errorMessages << i18n("Failed to save the annotations for layer %1.", layer->name());
return false;
}
if (!saveMetaData(layer)) {
m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
return false;
}
return visitAllInverse(layer);
}
bool KisKraSaveVisitor::visit(KisGroupLayer *layer)
{
if (!saveMetaData(layer)) {
m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
return false;
}
return visitAllInverse(layer);
}
bool KisKraSaveVisitor::visit(KisAdjustmentLayer* layer)
{
if (!layer->filter()) {
m_errorMessages << i18n("Failed to save the filter layer %1: it has no filter.", layer->name());
return false;
}
if (!saveSelection(layer)) {
m_errorMessages << i18n("Failed to save the selection for filter layer %1.", layer->name());
return false;
}
if (!saveFilterConfiguration(layer)) {
m_errorMessages << i18n("Failed to save the filter configuration for filter layer %1.", layer->name());
return false;
}
if (!saveMetaData(layer)) {
m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
return false;
}
return visitAllInverse(layer);
}
bool KisKraSaveVisitor::visit(KisGeneratorLayer * layer)
{
if (!saveSelection(layer)) {
m_errorMessages << i18n("Failed to save the selection for layer %1.", layer->name());
return false;
}
if (!saveFilterConfiguration(layer)) {
m_errorMessages << i18n("Failed to save the generator configuration for layer %1.", layer->name());
return false;
}
if (!saveMetaData(layer)) {
m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
return false;
}
return visitAllInverse(layer);
}
bool KisKraSaveVisitor::visit(KisCloneLayer *layer)
{
// Clone layers do not have a profile
if (!saveMetaData(layer)) {
m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
return false;
}
return visitAllInverse(layer);
}
bool KisKraSaveVisitor::visit(KisFilterMask *mask)
{
if (!mask->filter()) {
m_errorMessages << i18n("Failed to save filter mask %1. It has no filter.", mask->name());
return false;
}
if (!saveSelection(mask)) {
m_errorMessages << i18n("Failed to save the selection for filter mask %1.", mask->name());
return false;
}
if (!saveFilterConfiguration(mask)) {
m_errorMessages << i18n("Failed to save the filter configuration for filter mask %1.", mask->name());
return false;
}
return true;
}
bool KisKraSaveVisitor::visit(KisTransformMask *mask)
{
QDomDocument doc("transform_params");
QDomElement root = doc.createElement("transform_params");
QDomElement main = doc.createElement("main");
main.setAttribute("id", mask->transformParams()->id());
QDomElement data = doc.createElement("data");
mask->transformParams()->toXML(&data);
doc.appendChild(root);
root.appendChild(main);
root.appendChild(data);
QString location = getLocation(mask, DOT_TRANSFORMCONFIG);
if (m_store->open(location)) {
QByteArray a = doc.toByteArray();
bool retval = true;
retval = (m_store->write(a) == a.size());
if (!retval) {
warnFile << "Could not write transform mask configuration";
}
if (!m_store->close()) {
warnFile << "Could not close store after writing transform mask configuration";
retval = false;
}
return retval;
}
return false;
}
bool KisKraSaveVisitor::visit(KisTransparencyMask *mask)
{
if (!saveSelection(mask)) {
m_errorMessages << i18n("Failed to save the selection for transparency mask %1.", mask->name());
return false;
}
return true;
}
bool KisKraSaveVisitor::visit(KisSelectionMask *mask)
{
if (!saveSelection(mask)) {
m_errorMessages << i18n("Failed to save the selection for local selection %1.", mask->name());
return false;
}
return true;
}
bool KisKraSaveVisitor::visit(KisColorizeMask *mask)
{
m_store->pushDirectory();
QString location = getLocation(mask, DOT_COLORIZE_MASK);
bool result = m_store->enterDirectory(location);
if (!result) {
m_errorMessages << i18n("Failed to open %1.", location);
return false;
}
if (!m_store->open("content.xml"))
return false;
KoStoreDevice storeDev(m_store);
QDomDocument doc("doc");
QDomElement root = doc.createElement("colorize");
doc.appendChild(root);
KisDomUtils::saveValue(&root, COLORIZE_KEYSTROKES_SECTION, QVector<KisLazyFillTools::KeyStroke>::fromList(mask->fetchKeyStrokesDirect()));
QTextStream stream(&storeDev);
stream << doc;
if (!m_store->close())
return false;
int i = 0;
Q_FOREACH (const KisLazyFillTools::KeyStroke &stroke, mask->fetchKeyStrokesDirect()) {
const QString fileName = QString("%1_%2").arg(COLORIZE_KEYSTROKE).arg(i++);
savePaintDevice(stroke.dev, fileName);
}
savePaintDevice(mask->coloringProjection(), COLORIZE_COLORING_DEVICE);
m_store->popDirectory();
return true;
}
QStringList KisKraSaveVisitor::errorMessages() const
{
return m_errorMessages;
}
struct SimpleDevicePolicy
{
bool write(KisPaintDeviceSP dev, KisPaintDeviceWriter &store) {
return dev->write(store);
}
KoColor defaultPixel(KisPaintDeviceSP dev) const {
return dev->defaultPixel();
}
};
struct FramedDevicePolicy
{
FramedDevicePolicy(int frameId)
: m_frameId(frameId) {}
bool write(KisPaintDeviceSP dev, KisPaintDeviceWriter &store) {
return dev->framesInterface()->writeFrame(store, m_frameId);
}
KoColor defaultPixel(KisPaintDeviceSP dev) const {
return dev->framesInterface()->frameDefaultPixel(m_frameId);
}
int m_frameId;
};
bool KisKraSaveVisitor::savePaintDevice(KisPaintDeviceSP device,
QString location)
{
// Layer data
KisConfig cfg;
m_store->setCompressionEnabled(cfg.compressKra());
KisPaintDeviceFramesInterface *frameInterface = device->framesInterface();
QList<int> frames;
if (frameInterface) {
frames = frameInterface->frames();
}
if (!frameInterface || frames.count() <= 1) {
savePaintDeviceFrame(device, location, SimpleDevicePolicy());
} else {
KisRasterKeyframeChannel *keyframeChannel = device->keyframeChannel();
for (int i = 0; i < frames.count(); i++) {
int id = frames[i];
QString frameFilename = getLocation(keyframeChannel->frameFilename(id));
Q_ASSERT(!frameFilename.isEmpty());
if (!savePaintDeviceFrame(device, frameFilename, FramedDevicePolicy(id))) {
return false;
}
}
}
m_store->setCompressionEnabled(true);
return true;
}
template<class DevicePolicy>
bool KisKraSaveVisitor::savePaintDeviceFrame(KisPaintDeviceSP device, QString location, DevicePolicy policy)
{
if (m_store->open(location)) {
if (!policy.write(device, *m_writer)) {
device->disconnect();
m_store->close();
return false;
}
m_store->close();
}
if (m_store->open(location + ".defaultpixel")) {
m_store->write((char*)policy.defaultPixel(device).data(), device->colorSpace()->pixelSize());
m_store->close();
}
return true;
}
bool KisKraSaveVisitor::saveAnnotations(KisLayer* layer)
{
if (!layer) return false;
if (!layer->paintDevice()) return false;
if (!layer->paintDevice()->colorSpace()) return false;
if (layer->paintDevice()->colorSpace()->profile()) {
const KoColorProfile *profile = layer->paintDevice()->colorSpace()->profile();
KisAnnotationSP annotation;
if (profile) {
QByteArray profileRawData = profile->rawData();
if (!profileRawData.isEmpty()) {
if (profile->type() == "icc") {
annotation = new KisAnnotation(ICC, profile->name(), profile->rawData());
} else {
annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData());
}
}
}
if (annotation) {
// save layer profile
if (m_store->open(getLocation(layer, DOT_ICC))) {
m_store->write(annotation->annotation());
m_store->close();
} else {
return false;
}
}
}
return true;
}
bool KisKraSaveVisitor::saveSelection(KisNode* node)
{
KisSelectionSP selection;
if (node->inherits("KisMask")) {
selection = static_cast<KisMask*>(node)->selection();
} else if (node->inherits("KisAdjustmentLayer")) {
selection = static_cast<KisAdjustmentLayer*>(node)->internalSelection();
} else if (node->inherits("KisGeneratorLayer")) {
selection = static_cast<KisGeneratorLayer*>(node)->internalSelection();
} else {
return false;
}
bool retval = true;
if (selection->hasPixelSelection()) {
KisPaintDeviceSP dev = selection->pixelSelection();
if (!savePaintDevice(dev, getLocation(node, DOT_PIXEL_SELECTION))) {
m_errorMessages << i18n("Failed to save the pixel selection data for layer %1.", node->name());
retval = false;
}
}
if (selection->hasShapeSelection()) {
m_store->pushDirectory();
retval = m_store->enterDirectory(getLocation(node, DOT_SHAPE_SELECTION));
if (retval) {
KisShapeSelection* shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
if (!shapeSelection) {
retval = false;
}
if (retval && !shapeSelection->saveSelection(m_store)) {
m_errorMessages << i18n("Failed to save the vector selection data for layer %1.", node->name());
retval = false;
}
}
m_store->popDirectory();
}
return retval;
}
bool KisKraSaveVisitor::saveFilterConfiguration(KisNode* node)
{
KisNodeFilterInterface *filterInterface =
dynamic_cast<KisNodeFilterInterface*>(node);
KisFilterConfigurationSP filter;
if (filterInterface) {
filter = filterInterface->filter();
}
bool retval = false;
if (filter) {
QString location = getLocation(node, DOT_FILTERCONFIG);
if (m_store->open(location)) {
QString s = filter->toXML();
retval = (m_store->write(s.toUtf8(), qstrlen(s.toUtf8())) == qstrlen(s.toUtf8())); m_store->close();
}
}
return retval;
}
bool KisKraSaveVisitor::saveMetaData(KisNode* node)
{
if (!node->inherits("KisLayer")) return true;
KisMetaData::Store* metadata = (static_cast<KisLayer*>(node))->metaData();
if (metadata->isEmpty()) return true;
// Serialize all the types of metadata there are
KisMetaData::IOBackend* backend = KisMetaData::IOBackendRegistry::instance()->get("xmp");
if (!backend->supportSaving()) {
dbgFile << "Backend " << backend->id() << " does not support saving.";
return false;
}
QString location = getLocation(node, QString(".") + backend->id() + DOT_METADATA);
dbgFile << "going to save " << backend->id() << ", " << backend->name() << " to " << location;
QBuffer buffer;
// not that the metadata backends every return anything but true...
bool retval = backend->saveTo(metadata, &buffer);
if (!retval) {
m_errorMessages << i18n("The metadata backend failed to save the metadata for %1", node->name());
}
else {
QByteArray data = buffer.data();
dbgFile << "\t information size is" << data.size();
if (data.size() > 0 && m_store->open(location)) {
retval = m_store->write(data, data.size());
m_store->close();
}
if (!retval) {
m_errorMessages << i18n("Could not write for %1 metadata to the file.", node->name());
}
}
return retval;
}
QString KisKraSaveVisitor::getLocation(KisNode* node, const QString& suffix)
{
Q_ASSERT(m_nodeFileNames.contains(node));
return getLocation(m_nodeFileNames[node], suffix);
}
QString KisKraSaveVisitor::getLocation(const QString &filename, const QString& suffix)
{
QString location = m_external ? QString() : m_uri;
location += m_name + LAYER_PATH + filename + suffix;
return location;
}
diff --git a/libs/ui/kra/kis_kra_save_visitor.h b/plugins/impex/libkra/kis_kra_save_visitor.h
similarity index 96%
rename from libs/ui/kra/kis_kra_save_visitor.h
rename to plugins/impex/libkra/kis_kra_save_visitor.h
index bd9af1485a..93163722aa 100644
--- a/libs/ui/kra/kis_kra_save_visitor.h
+++ b/plugins/impex/libkra/kis_kra_save_visitor.h
@@ -1,99 +1,99 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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.
*/
#ifndef KIS_KRA_SAVE_VISITOR_H_
#define KIS_KRA_SAVE_VISITOR_H_
#include <QRect>
#include <QStringList>
#include "kis_types.h"
#include "kis_node_visitor.h"
#include "kis_image.h"
-
+#include "kritalibkra_export.h"
class KisPaintDeviceWriter;
class KoStore;
-class KisKraSaveVisitor : public KisNodeVisitor
+class KRITALIBKRA_EXPORT KisKraSaveVisitor : public KisNodeVisitor
{
public:
KisKraSaveVisitor(KoStore *store, const QString & name, QMap<const KisNode*, QString> nodeFileNames);
virtual ~KisKraSaveVisitor();
using KisNodeVisitor::visit;
public:
void setExternalUri(const QString &uri);
bool visit(KisNode*) {
return true;
}
bool visit(KisExternalLayer *);
bool visit(KisPaintLayer *layer);
bool visit(KisGroupLayer *layer);
bool visit(KisAdjustmentLayer* layer);
bool visit(KisGeneratorLayer * layer);
bool visit(KisCloneLayer *layer);
bool visit(KisFilterMask *mask);
bool visit(KisTransformMask *mask);
bool visit(KisTransparencyMask *mask);
bool visit(KisSelectionMask *mask);
bool visit(KisColorizeMask *mask);
/// @return a list with everything that went wrong while saving
QStringList errorMessages() const;
private:
bool savePaintDevice(KisPaintDeviceSP device, QString location);
template<class DevicePolicy>
bool savePaintDeviceFrame(KisPaintDeviceSP device, QString location, DevicePolicy policy);
bool saveAnnotations(KisLayer* layer);
bool saveSelection(KisNode* node);
bool saveFilterConfiguration(KisNode* node);
bool saveMetaData(KisNode* node);
QString getLocation(KisNode* node, const QString& suffix = QString());
QString getLocation(const QString &filename, const QString &suffix = QString());
private:
KoStore *m_store;
bool m_external;
QString m_uri;
QString m_name;
QMap<const KisNode*, QString> m_nodeFileNames;
KisPaintDeviceWriter *m_writer;
QStringList m_errorMessages;
};
#endif // KIS_KRA_SAVE_VISITOR_H_
diff --git a/libs/ui/kra/kis_kra_saver.cpp b/plugins/impex/libkra/kis_kra_saver.cpp
similarity index 97%
rename from libs/ui/kra/kis_kra_saver.cpp
rename to plugins/impex/libkra/kis_kra_saver.cpp
index 149b080d66..e74571800b 100644
--- a/libs/ui/kra/kis_kra_saver.cpp
+++ b/plugins/impex/libkra/kis_kra_saver.cpp
@@ -1,426 +1,426 @@
/* This file is part of the KDE project
* Copyright 2008 (C) 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 "kis_kra_saver.h"
#include "kis_kra_tags.h"
#include "kis_kra_save_visitor.h"
#include "kis_kra_savexml_visitor.h"
#include <QDomDocument>
#include <QDomElement>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QBuffer>
#include <KoDocumentInfo.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include <KoColorProfile.h>
#include <KoColor.h>
#include <KoStore.h>
#include <KoStoreDevice.h>
#include <kis_annotation.h>
#include <kis_image.h>
#include <kis_image_animation_interface.h>
#include <kis_group_layer.h>
#include <kis_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_layer_composition.h>
#include <kis_painting_assistants_decoration.h>
#include <kis_psd_layer_style_resource.h>
#include "kis_png_converter.h"
#include "kis_keyframe_channel.h"
#include <kis_time_range.h>
#include "KisDocument.h"
#include <string>
#include "kis_dom_utils.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "KisProofingConfiguration.h"
using namespace KRA;
struct KisKraSaver::Private
{
public:
KisDocument* doc;
QMap<const KisNode*, QString> nodeFileNames;
QMap<const KisNode*, QString> keyframeFilenames;
QString imageName;
QStringList errorMessages;
};
KisKraSaver::KisKraSaver(KisDocument* document)
: m_d(new Private)
{
m_d->doc = document;
m_d->imageName = m_d->doc->documentInfo()->aboutInfo("title");
if (m_d->imageName.isEmpty()) {
m_d->imageName = i18n("Unnamed");
}
}
KisKraSaver::~KisKraSaver()
{
delete m_d;
}
-QDomElement KisKraSaver::saveXML(QDomDocument& doc, KisImageWSP image)
+QDomElement KisKraSaver::saveXML(QDomDocument& doc, KisImageSP image)
{
QDomElement imageElement = doc.createElement("IMAGE"); // Legacy!
Q_ASSERT(image);
imageElement.setAttribute(NAME, m_d->imageName);
imageElement.setAttribute(MIME, NATIVE_MIMETYPE);
imageElement.setAttribute(WIDTH, KisDomUtils::toString(image->width()));
imageElement.setAttribute(HEIGHT, KisDomUtils::toString(image->height()));
imageElement.setAttribute(COLORSPACE_NAME, image->colorSpace()->id());
imageElement.setAttribute(DESCRIPTION, m_d->doc->documentInfo()->aboutInfo("comment"));
// XXX: Save profile as blob inside the image, instead of the product name.
if (image->profile() && image->profile()-> valid()) {
imageElement.setAttribute(PROFILE, image->profile()->name());
}
imageElement.setAttribute(X_RESOLUTION, KisDomUtils::toString(image->xRes()*72.0));
imageElement.setAttribute(Y_RESOLUTION, KisDomUtils::toString(image->yRes()*72.0));
//now the proofing options:
imageElement.setAttribute(PROOFINGPROFILENAME, KisDomUtils::toString(image->proofingConfiguration()->proofingProfile));
imageElement.setAttribute(PROOFINGMODEL, KisDomUtils::toString(image->proofingConfiguration()->proofingModel));
imageElement.setAttribute(PROOFINGDEPTH, KisDomUtils::toString(image->proofingConfiguration()->proofingDepth));
imageElement.setAttribute(PROOFINGINTENT, KisDomUtils::toString(image->proofingConfiguration()->intent));
imageElement.setAttribute(PROOFINGADAPTATIONSTATE, KisDomUtils::toString(image->proofingConfiguration()->adaptationState));
quint32 count = 1; // We don't save the root layer, but it does count
KisSaveXmlVisitor visitor(doc, imageElement, count, m_d->doc->url().toLocalFile(), true);
visitor.setSelectedNodes(m_d->doc->activeNodes());
image->rootLayer()->accept(visitor);
m_d->errorMessages.append(visitor.errorMessages());
m_d->nodeFileNames = visitor.nodeFileNames();
m_d->keyframeFilenames = visitor.keyframeFileNames();
saveBackgroundColor(doc, imageElement, image);
saveWarningColor(doc, imageElement, image);
saveCompositions(doc, imageElement, image);
- saveAssistantsList(doc,imageElement);
+ saveAssistantsList(doc, imageElement);
saveGrid(doc,imageElement);
saveGuides(doc,imageElement);
QDomElement animationElement = doc.createElement("animation");
KisDomUtils::saveValue(&animationElement, "framerate", image->animationInterface()->framerate());
KisDomUtils::saveValue(&animationElement, "range", image->animationInterface()->fullClipRange());
KisDomUtils::saveValue(&animationElement, "currentTime", image->animationInterface()->currentUITime());
imageElement.appendChild(animationElement);
return imageElement;
}
bool KisKraSaver::saveKeyframes(KoStore *store, const QString &uri, bool external)
{
QMap<const KisNode*, QString>::iterator it;
for (it = m_d->keyframeFilenames.begin(); it != m_d->keyframeFilenames.end(); it++) {
const KisNode *node = it.key();
QString filename = it.value();
QString location =
(external ? QString() : uri)
+ m_d->imageName + LAYER_PATH + filename;
if (!saveNodeKeyframes(store, location, node)) {
return false;
}
}
return true;
}
bool KisKraSaver::saveNodeKeyframes(KoStore *store, QString location, const KisNode *node)
{
QDomDocument doc = KisDocument::createDomDocument("krita-keyframes", "keyframes", "1.0");
QDomElement root = doc.documentElement();
KisKeyframeChannel *channel;
Q_FOREACH (channel, node->keyframeChannels()) {
QDomElement element = channel->toXML(doc, m_d->nodeFileNames[node]);
root.appendChild(element);
}
if (store->open(location)) {
QByteArray xml = doc.toByteArray();
store->write(xml);
store->close();
} else {
m_d->errorMessages << i18n("could not save keyframes");
return false;
}
return true;
}
-bool KisKraSaver::saveBinaryData(KoStore* store, KisImageWSP image, const QString & uri, bool external, bool autosave)
+bool KisKraSaver::saveBinaryData(KoStore* store, KisImageSP image, const QString &uri, bool external, bool autosave)
{
QString location;
// Save the layers data
KisKraSaveVisitor visitor(store, m_d->imageName, m_d->nodeFileNames);
if (external)
visitor.setExternalUri(uri);
image->rootLayer()->accept(visitor);
m_d->errorMessages.append(visitor.errorMessages());
if (!m_d->errorMessages.isEmpty()) {
return false;
}
// saving annotations
// XXX this only saves EXIF and ICC info. This would probably need
// a redesign of the dtd of the krita file to do this more generally correct
// e.g. have <ANNOTATION> tags or so.
KisAnnotationSP annotation = image->annotation("exif");
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + EXIF_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
if (image->profile()) {
const KoColorProfile *profile = image->profile();
KisAnnotationSP annotation;
if (profile) {
QByteArray profileRawData = profile->rawData();
if (!profileRawData.isEmpty()) {
if (profile->type() == "icc") {
annotation = new KisAnnotation(ICC, profile->name(), profile->rawData());
} else {
annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData());
}
}
}
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + ICC_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
}
//This'll embed the profile used for proofing into the kra file.
if (image->proofingConfiguration()) {
const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->profileByName(image->proofingConfiguration()->proofingProfile);
if (proofingProfile && proofingProfile->valid()) {
QByteArray proofingProfileRaw = proofingProfile->rawData();
if (!proofingProfileRaw.isEmpty()) {
annotation = new KisAnnotation(ICCPROOFINGPROFILE, proofingProfile->name(), proofingProfile->rawData());
}
}
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + ICC_PROOFING_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
}
{
KisPSDLayerStyleCollectionResource collection("not-nexists.asl");
KIS_ASSERT_RECOVER_NOOP(!collection.valid());
collection.collectAllLayerStyles(image->root());
if (collection.valid()) {
location = external ? QString() : uri;
location += m_d->imageName + LAYER_STYLES_PATH;
if (store->open(location)) {
QBuffer aslBuffer;
aslBuffer.open(QIODevice::WriteOnly);
collection.saveToDevice(&aslBuffer);
aslBuffer.close();
store->write(aslBuffer.buffer());
store->close();
}
}
}
if (!autosave) {
KisPaintDeviceSP dev = image->projection();
if (!KisPNGConverter::isColorSpaceSupported(dev->colorSpace())) {
dev = new KisPaintDevice(*dev.data());
KUndo2Command *cmd = dev->convertTo(KoColorSpaceRegistry::instance()->rgb8());
delete cmd;
}
KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store);
}
saveAssistants(store, uri,external);
return true;
}
QStringList KisKraSaver::errorMessages() const
{
return m_d->errorMessages;
}
-void KisKraSaver::saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageWSP image)
+void KisKraSaver::saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
QDomElement e = doc.createElement(CANVASPROJECTIONCOLOR);
KoColor color = image->defaultProjectionColor();
QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize());
e.setAttribute(COLORBYTEDATA, QString(colorData.toBase64()));
element.appendChild(e);
}
-void KisKraSaver::saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageWSP image)
+void KisKraSaver::saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
if (image->proofingConfiguration()) {
QDomElement e = doc.createElement(PROOFINGWARNINGCOLOR);
KoColor color = image->proofingConfiguration()->warningColor;
color.toXML(doc, e);
//QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize());
//e.setAttribute("ColorData", QString(colorData.toBase64()));
element.appendChild(e);
}
}
-void KisKraSaver::saveCompositions(QDomDocument& doc, QDomElement& element, KisImageWSP image)
+void KisKraSaver::saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
if (!image->compositions().isEmpty()) {
QDomElement e = doc.createElement("compositions");
Q_FOREACH (KisLayerCompositionSP composition, image->compositions()) {
composition->save(doc, e);
}
element.appendChild(e);
}
}
bool KisKraSaver::saveAssistants(KoStore* store, QString uri, bool external)
{
QString location;
QMap<QString, int> assistantcounters;
QByteArray data;
QList<KisPaintingAssistantSP> assistants = m_d->doc->assistants();
QMap<KisPaintingAssistantHandleSP, int> handlemap;
if (!assistants.isEmpty()) {
Q_FOREACH (KisPaintingAssistantSP assist, assistants){
if (!assistantcounters.contains(assist->id())){
assistantcounters.insert(assist->id(),0);
}
location = external ? QString() : uri;
location += m_d->imageName + ASSISTANTS_PATH;
location += QString(assist->id()+"%1.assistant").arg(assistantcounters[assist->id()]);
data = assist->saveXml(handlemap);
store->open(location);
store->write(data);
store->close();
assistantcounters[assist->id()]++;
}
}
return true;
}
bool KisKraSaver::saveAssistantsList(QDomDocument& doc, QDomElement& element)
{
int count_ellipse = 0, count_perspective = 0, count_ruler = 0, count_vanishingpoint = 0,count_infiniteruler = 0, count_parallelruler = 0, count_concentricellipse = 0, count_fisheyepoint = 0, count_spline = 0;
QList<KisPaintingAssistantSP> assistants = m_d->doc->assistants();
if (!assistants.isEmpty()) {
QDomElement assistantsElement = doc.createElement("assistants");
Q_FOREACH (KisPaintingAssistantSP assist, assistants){
if (assist->id() == "ellipse"){
assist->saveXmlList(doc, assistantsElement, count_ellipse);
count_ellipse++;
}
else if (assist->id() == "spline"){
assist->saveXmlList(doc, assistantsElement, count_spline);
count_spline++;
}
else if (assist->id() == "perspective"){
assist->saveXmlList(doc, assistantsElement, count_perspective);
count_perspective++;
}
else if (assist->id() == "vanishing point"){
assist->saveXmlList(doc, assistantsElement, count_vanishingpoint);
count_vanishingpoint++;
}
else if (assist->id() == "infinite ruler"){
assist->saveXmlList(doc, assistantsElement, count_infiniteruler);
count_infiniteruler++;
}
else if (assist->id() == "parallel ruler"){
assist->saveXmlList(doc, assistantsElement, count_parallelruler);
count_parallelruler++;
}
else if (assist->id() == "concentric ellipse"){
assist->saveXmlList(doc, assistantsElement, count_concentricellipse);
count_concentricellipse++;
}
else if (assist->id() == "fisheye-point"){
assist->saveXmlList(doc, assistantsElement, count_fisheyepoint);
count_fisheyepoint++;
}
else if (assist->id() == "ruler"){
assist->saveXmlList(doc, assistantsElement, count_ruler);
count_ruler++;
}
}
element.appendChild(assistantsElement);
}
return true;
}
bool KisKraSaver::saveGrid(QDomDocument& doc, QDomElement& element)
{
KisGridConfig config = m_d->doc->gridConfig();
if (!config.isDefault()) {
QDomElement gridElement = config.saveDynamicDataToXml(doc, "grid");
element.appendChild(gridElement);
}
return true;
}
bool KisKraSaver::saveGuides(QDomDocument& doc, QDomElement& element)
{
KisGuidesConfig guides = m_d->doc->guidesConfig();
if (guides.hasGuides()) {
QDomElement guidesElement = guides.saveToXml(doc, "guides");
element.appendChild(guidesElement);
}
return true;
}
diff --git a/libs/ui/kra/kis_kra_saver.h b/plugins/impex/libkra/kis_kra_saver.h
similarity index 85%
rename from libs/ui/kra/kis_kra_saver.h
rename to plugins/impex/libkra/kis_kra_saver.h
index 2f8ef5b791..810abcfd7c 100644
--- a/libs/ui/kra/kis_kra_saver.h
+++ b/plugins/impex/libkra/kis_kra_saver.h
@@ -1,61 +1,63 @@
/* This file is part of the KDE project
* Copyright 2008 (C) 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.
*/
#ifndef KIS_KRA_SAVER
#define KIS_KRA_SAVER
#include <kis_types.h>
class KisDocument;
class QDomElement;
class QDomDocument;
class KoStore;
class QString;
class QStringList;
-class KisKraSaver
+#include "kritalibkra_export.h"
+
+class KRITALIBKRA_EXPORT KisKraSaver
{
public:
KisKraSaver(KisDocument* document);
~KisKraSaver();
- QDomElement saveXML(QDomDocument& doc, KisImageWSP image);
+ QDomElement saveXML(QDomDocument& doc, KisImageSP image);
bool saveKeyframes(KoStore *store, const QString &uri, bool external);
- bool saveBinaryData(KoStore* store, KisImageWSP image, const QString & uri, bool external, bool includeMerge);
+ bool saveBinaryData(KoStore* store, KisImageSP image, const QString & uri, bool external, bool includeMerge);
/// @return a list with everthing that went wrong while saving
QStringList errorMessages() const;
private:
- void saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageWSP image);
- void saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageWSP image);
- void saveCompositions(QDomDocument& doc, QDomElement& element, KisImageWSP image);
+ void saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image);
+ void saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image);
+ void saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image);
bool saveAssistants(KoStore *store,QString uri, bool external);
bool saveAssistantsList(QDomDocument& doc, QDomElement& element);
bool saveGrid(QDomDocument& doc, QDomElement& element);
bool saveGuides(QDomDocument& doc, QDomElement& element);
bool saveNodeKeyframes(KoStore *store, QString location, const KisNode *node);
struct Private;
Private * const m_d;
};
#endif
diff --git a/libs/ui/kra/kis_kra_savexml_visitor.cpp b/plugins/impex/libkra/kis_kra_savexml_visitor.cpp
similarity index 98%
rename from libs/ui/kra/kis_kra_savexml_visitor.cpp
rename to plugins/impex/libkra/kis_kra_savexml_visitor.cpp
index ae758be052..d1a49f3095 100644
--- a/libs/ui/kra/kis_kra_savexml_visitor.cpp
+++ b/plugins/impex/libkra/kis_kra_savexml_visitor.cpp
@@ -1,457 +1,455 @@
/*
* 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_savexml_visitor.h"
#include "kis_kra_tags.h"
#include "kis_kra_utils.h"
#include "kis_layer_properties_icons.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 <lazybrush/kis_colorize_mask.h>
#include <kis_file_layer.h>
#include <kis_psd_layer_style.h>
#include "kis_keyframe_channel.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());
- if (layer->isAnimated()) {
- element.setAttribute(ONION_SKIN_ENABLED, layer->onionSkinEnabled());
- element.setAttribute(VISIBLE_IN_TIMELINE, layer->useInTimeline());
- }
+ element.setAttribute(ONION_SKIN_ENABLED, layer->onionSkinEnabled());
+ element.setAttribute(VISIBLE_IN_TIMELINE, layer->useInTimeline());
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();
}
i = QMapIterator<const KisNode*, QString>(visitor.keyframeFileNames());
while (i.hasNext()) {
i.next();
m_keyframeFileNames[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(KisColorizeMask *mask)
{
Q_ASSERT(mask);
QDomElement el = m_doc.createElement(MASK);
saveMask(el, COLORIZE_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->setCompositeOpId(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(COLOR_LABEL)) {
layer->setColorLabelIndex(el.attribute(COLOR_LABEL).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 {
warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
}
}
}
void KisSaveXmlVisitor::saveNodeKeyframes(const KisNode* node, QString nodeFilename, QDomElement& nodeElement)
{
if (node->isAnimated()) {
QString keyframeFile = nodeFilename + ".keyframes.xml";
m_keyframeFileNames[node] = keyframeFile;
nodeElement.setAttribute(KEYFRAME_FILE, keyframeFile);
}
}
void KisSaveXmlVisitor::saveLayer(QDomElement & el, const QString & layerType, const KisLayer * layer)
{
QString filename = LAYER + QString::number(m_count);
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, filename);
el.setAttribute(X, layer->x());
el.setAttribute(Y, layer->y());
el.setAttribute(UUID, layer->uuid().toString());
el.setAttribute(COLLAPSED, layer->collapsed());
el.setAttribute(COLOR_LABEL, layer->colorLabelIndex());
if (layer->layerStyle()) {
el.setAttribute(LAYER_STYLE_UUID, layer->layerStyle()->uuid().toString());
}
Q_FOREACH (KisNodeSP node, m_selectedNodes) {
if (node.data() == layer) {
el.setAttribute("selected", "true");
break;
}
}
saveNodeKeyframes(layer, filename, el);
m_nodeFileNames[layer] = filename;
dbgFile << "Saved layer "
<< layer->name()
<< " of type " << layerType
<< " with filename " << LAYER + QString::number(m_count);
}
void KisSaveXmlVisitor::saveMask(QDomElement & el, const QString & maskType, const KisMaskSP mask)
{
QString filename = MASK + QString::number(m_count);
el.setAttribute(NAME, mask->name());
el.setAttribute(VISIBLE, mask->visible());
el.setAttribute(LOCKED, mask->userLocked());
el.setAttribute(NODE_TYPE, maskType);
el.setAttribute(FILE_NAME, filename);
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"));
} else if (maskType == COLORIZE_MASK) {
el.setAttribute(COLORSPACE_NAME, mask->colorSpace()->id());
el.setAttribute(COMPOSITE_OP, mask->compositeOpId());
el.setAttribute(COLORIZE_EDIT_KEYSTROKES, KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool());
el.setAttribute(COLORIZE_SHOW_COLORING, KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool());
}
saveNodeKeyframes(mask, filename, el);
m_nodeFileNames[mask] = filename;
dbgFile << "Saved mask "
<< mask->name()
<< " of type " << maskType
<< " with filename " << filename;
}
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();
}
i = QMapIterator<const KisNode*, QString>(visitor.keyframeFileNames());
while (i.hasNext()) {
i.next();
m_keyframeFileNames[i.key()] = i.value();
}
return success;
}
return true;
}
diff --git a/libs/ui/kra/kis_kra_savexml_visitor.h b/plugins/impex/libkra/kis_kra_savexml_visitor.h
similarity index 96%
rename from libs/ui/kra/kis_kra_savexml_visitor.h
rename to plugins/impex/libkra/kis_kra_savexml_visitor.h
index d3e80f61fb..4bb3272c5e 100644
--- a/libs/ui/kra/kis_kra_savexml_visitor.h
+++ b/plugins/impex/libkra/kis_kra_savexml_visitor.h
@@ -1,96 +1,96 @@
/*
* 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.
*/
#ifndef KIS_KRA_SAVEXML_VISITOR_H_
#define KIS_KRA_SAVEXML_VISITOR_H_
#include <QDomDocument>
#include <QDomElement>
#include <QStringList>
#include "kis_node_visitor.h"
#include "kis_types.h"
-#include "kritaui_export.h"
+#include "kritalibkra_export.h"
-class KRITAUI_EXPORT KisSaveXmlVisitor : public KisNodeVisitor
+class KRITALIBKRA_EXPORT KisSaveXmlVisitor : public KisNodeVisitor
{
public:
KisSaveXmlVisitor(QDomDocument doc, const QDomElement & element, quint32 &count, const QString &url, bool root);
void setSelectedNodes(vKisNodeSP selectedNodes);
using KisNodeVisitor::visit;
QStringList errorMessages() const;
public:
bool visit(KisNode*) {
return true;
}
bool visit(KisExternalLayer *);
bool visit(KisPaintLayer *layer);
bool visit(KisGroupLayer *layer);
bool visit(KisAdjustmentLayer* layer);
bool visit(KisGeneratorLayer *layer);
bool visit(KisCloneLayer *layer);
bool visit(KisFilterMask *mask);
bool visit(KisTransformMask *mask);
bool visit(KisTransparencyMask *mask);
bool visit(KisSelectionMask *mask);
bool visit(KisColorizeMask *mask);
QMap<const KisNode*, QString> nodeFileNames() {
return m_nodeFileNames;
}
QMap<const KisNode*, QString> keyframeFileNames() {
return m_keyframeFileNames;
}
public:
QDomElement savePaintLayerAttributes(KisPaintLayer *layer, QDomDocument &doc);
// used by EXR to save properties of Krita layers inside .exr
static void loadPaintLayerAttributes(const QDomElement &el, KisPaintLayer *layer);
private:
static void loadLayerAttributes(const QDomElement &el, KisLayer *layer);
private:
void saveLayer(QDomElement & el, const QString & layerType, const KisLayer * layer);
void saveMask(QDomElement & el, const QString & maskType, const KisMaskSP mask);
bool saveMasks(KisNode * node, QDomElement & layerElement);
void saveNodeKeyframes(const KisNode *node, QString filename, QDomElement& el);
friend class KisKraSaveXmlVisitorTest;
vKisNodeSP m_selectedNodes;
QMap<const KisNode*, QString> m_nodeFileNames;
QMap<const KisNode*, QString> m_keyframeFileNames;
QDomDocument m_doc;
QDomElement m_elem;
quint32 &m_count;
QString m_url;
bool m_root;
QStringList m_errorMessages;
};
#endif
diff --git a/libs/ui/kra/kis_kra_tags.h b/plugins/impex/libkra/kis_kra_tags.h
similarity index 99%
rename from libs/ui/kra/kis_kra_tags.h
rename to plugins/impex/libkra/kis_kra_tags.h
index 8c8d1e7d56..4335df5547 100644
--- a/libs/ui/kra/kis_kra_tags.h
+++ b/plugins/impex/libkra/kis_kra_tags.h
@@ -1,135 +1,134 @@
/* This file is part of the KDE project
* Copyright 2008 (C) 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.
*/
#ifndef KIS_KRA_TAGS
#define KIS_KRA_TAGS
#include <QString>
-
/**
* Tag definitions for our xml file format
*/
namespace KRA
{
// mimetype
const QString NATIVE_MIMETYPE = "application/x-kra";
// xml tags
const QString SEPARATOR = "/";
const QString SHAPE_LAYER_PATH = "/shapelayers/";
const QString EXIF_PATH = "/annotations/exif";
const QString ICC_PATH = "/annotations/icc";
const QString ICC_PROOFING_PATH = "/annotations/proofing/icc";
const QString LAYER_STYLES_PATH = "/annotations/layerstyles.asl";
const QString ASSISTANTS_PATH = "/assistants/";
const QString LAYER_PATH = "/layers/";
const QString ADJUSTMENT_LAYER = "adjustmentlayer";
const QString CHANNEL_FLAGS = "channelflags";
const QString CHANNEL_LOCK_FLAGS = "channellockflags";
const QString CLONE_FROM = "clonefrom";
const QString CLONE_FROM_UUID = "clonefromuuid";
const QString CLONE_LAYER = "clonelayer";
const QString CLONE_TYPE = "clonetype";
const QString COLORSPACE_NAME = "colorspacename";
const QString COMPOSITE_OP = "compositeop";
const QString DESCRIPTION = "description";
const QString ONION_SKIN_ENABLED = "onionskin";
const QString VISIBLE_IN_TIMELINE = "intimeline";
const QString DOT_FILTERCONFIG = ".filterconfig";
const QString DOT_TRANSFORMCONFIG = ".transformconfig";
const QString DOT_ICC = ".icc";
const QString DOT_PIXEL_SELECTION = ".pixelselection";
const QString DOT_SHAPE_SELECTION = ".shapeselection";
const QString DOT_SHAPE_LAYER = ".shapelayer";
const QString DOT_COLORIZE_MASK = ".colorizemask";
const QString DOT_METADATA = ".metadata";
const QString FILE_NAME = "filename";
const QString FILTER_MASK = "filtermask";
const QString FILTER_NAME = "filtername";
const QString FILTER_STATEGY = "filter_strategy";
const QString FILTER_VERSION = "filterversion";
const QString GENERATOR_LAYER = "generatorlayer";
const QString GENERATOR_NAME = "generatorname";
const QString GENERATOR_VERSION = "generatorversion";
const QString GROUP_LAYER = "grouplayer";
const QString HEIGHT = "height";
const QString ICC = "icc";
const QString LAYER = "layer";
const QString LAYERS = "layers";
const QString NODE_TYPE = "nodetype";
const QString LOCKED = "locked";
const QString MASK = "mask";
const QString MASKS = "masks";
const QString MIME = "mime";
const QString NAME = "name";
const QString OPACITY = "opacity";
const QString COLLAPSED = "collapsed";
const QString COLOR_LABEL = "colorlabel";
const QString PAINT_LAYER = "paintlayer";
const QString PROFILE = "profile";
const QString ROTATION = "rotation";
const QString SELECTION_MASK = "selectionmask";
const QString SHAPE_LAYER = "shapelayer";
const QString FILE_LAYER = "filelayer";
const QString TRANSPARENCY_MASK = "transparencymask";
const QString COLORIZE_MASK = "colorizemask";
const QString COLORIZE_SHOW_COLORING = "show-coloring";
const QString COLORIZE_EDIT_KEYSTROKES = "edit-keystrokes";
const QString COLORIZE_KEYSTROKE = "keystroke";
const QString COLORIZE_KEYSTROKE_COLOR = "color";
const QString COLORIZE_KEYSTROKE_IS_TRANSPARENT = "is-transparent";
const QString COLORIZE_COLORING_DEVICE = "colorize-coloring";
const QString COLORIZE_KEYSTROKES_SECTION = "keystrokes";
const QString TRANSFORM_MASK = "transformmask";
const QString UUID = "uuid";
const QString VISIBLE = "visible";
const QString WIDTH = "width";
const QString X = "x";
const QString X_RESOLUTION = "x-res";
const QString X_SCALE = "x_scale";
const QString X_SHEAR = "x_shear";
const QString X_TRANSLATION = "x_translation";
const QString Y = "y";
const QString Y_RESOLUTION = "y-res";
const QString Y_SCALE = "y_scale";
const QString Y_SHEAR = "y_shear";
const QString Y_TRANSLATION = "y_translation";
const QString ACTIVE = "active";
const QString LAYER_STYLE_UUID = "layerstyle";
const QString PASS_THROUGH_MODE = "passthrough";
const QString KEYFRAME_FILE = "keyframes";
const QString PROOFINGPROFILENAME = "proofing-profile-name";
const QString PROOFINGMODEL = "proofing-model";
const QString PROOFINGDEPTH = "proofing-depth";
const QString PROOFINGINTENT = "proofing-intent";
const QString PROOFINGWARNINGCOLOR ="ProofingWarningColor";
const QString PROOFINGADAPTATIONSTATE = "proofing-adaptation-state";
const QString ICCPROOFINGPROFILE ="icc-proofing-profile";
const QString CANVASPROJECTIONCOLOR = "ProjectionBackgroundColor";
const QString COLORBYTEDATA = "ColorData";
}
#endif
diff --git a/libs/ui/kra/kis_kra_utils.cpp b/plugins/impex/libkra/kis_kra_utils.cpp
similarity index 93%
rename from libs/ui/kra/kis_kra_utils.cpp
rename to plugins/impex/libkra/kis_kra_utils.cpp
index 86fbd64826..e32090592b 100644
--- a/libs/ui/kra/kis_kra_utils.cpp
+++ b/plugins/impex/libkra/kis_kra_utils.cpp
@@ -1,44 +1,44 @@
/* This file is part of the KDE project
* Copyright 2011 (C) 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_kra_utils.h"
QString KRA::flagsToString(const QBitArray& flags, int size, char trueToken, char falseToken, bool defaultTrue)
{
size = (size < 0) ? flags.count() : size;
QString string(size, defaultTrue ? trueToken : falseToken);
- for(int i=0; i<qMin(size, flags.count()); ++i)
+ for(int i=0; i < qMin(size, flags.count()); ++i)
string[i] = flags[i] ? trueToken : falseToken;
return string;
}
QBitArray KRA::stringToFlags(const QString& string, int size, char token, bool defaultTrue)
{
size = (size < 0) ? string.length() : size;
QBitArray flags(size, defaultTrue);
- for(int i=0; i<qMin(size, string.length()); ++i)
+ for(int i=0; i < qMin(size, string.length()); ++i)
flags[i] = (string[i] == token) ? !defaultTrue : defaultTrue;
return flags;
}
diff --git a/libs/ui/kra/kis_kra_utils.h b/plugins/impex/libkra/kis_kra_utils.h
similarity index 100%
rename from libs/ui/kra/kis_kra_utils.h
rename to plugins/impex/libkra/kis_kra_utils.h
diff --git a/plugins/impex/libkra/tests/CMakeLists.txt b/plugins/impex/libkra/tests/CMakeLists.txt
new file mode 100644
index 0000000000..04fcd783d9
--- /dev/null
+++ b/plugins/impex/libkra/tests/CMakeLists.txt
@@ -0,0 +1,18 @@
+set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
+
+include_directories( ${CMAKE_SOURCE_DIR}/sdk/tests )
+
+macro_add_unittest_definitions()
+
+set(kis_kra_loader_test_SRCS kis_kra_loader_test.cpp )
+kde4_add_broken_unit_test(KisKraLoaderTest TESTNAME krita-ui-KisKraLoaderTest ${kis_kra_loader_test_SRCS})
+target_link_libraries(KisKraLoaderTest kritaimage kritaui Qt5::Test kritalibkra)
+
+set(kis_kra_saver_test_SRCS kis_kra_saver_test.cpp )
+kde4_add_broken_unit_test(KisKraSaverTest TESTNAME krita-ui-KisKraSaverTest ${kis_kra_saver_test_SRCS})
+target_link_libraries(KisKraSaverTest kritaimage kritaui Qt5::Test kritalibkra)
+
+set(kis_kra_savexml_visitor_test_SRCS kis_kra_savexml_visitor_test.cpp )
+kde4_add_unit_test(KisKraSaveXmlVisitorTest TESTNAME krita-ui-KisKraSaveXmlVisitorTest ${kis_kra_savexml_visitor_test_SRCS})
+target_link_libraries(KisKraSaveXmlVisitorTest kritaimage kritaui Qt5::Test kritalibkra)
+
diff --git a/libs/ui/tests/data/load_test.kra b/plugins/impex/libkra/tests/data/load_test.kra
similarity index 100%
rename from libs/ui/tests/data/load_test.kra
rename to plugins/impex/libkra/tests/data/load_test.kra
diff --git a/libs/ui/tests/data/load_test_animation.kra b/plugins/impex/libkra/tests/data/load_test_animation.kra
similarity index 100%
rename from libs/ui/tests/data/load_test_animation.kra
rename to plugins/impex/libkra/tests/data/load_test_animation.kra
diff --git a/libs/ui/tests/kis_kra_loader_test.cpp b/plugins/impex/libkra/tests/kis_kra_loader_test.cpp
similarity index 97%
rename from libs/ui/tests/kis_kra_loader_test.cpp
rename to plugins/impex/libkra/tests/kis_kra_loader_test.cpp
index 67eddefd0f..c279c74a54 100644
--- a/libs/ui/tests/kis_kra_loader_test.cpp
+++ b/plugins/impex/libkra/tests/kis_kra_loader_test.cpp
@@ -1,172 +1,172 @@
/*
* 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_kra_loader_test.h"
#include <QTest>
#include <KisDocument.h>
#include <KoDocumentInfo.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include <KoColor.h>
#include "KisDocument.h"
#include "kis_image.h"
#include "testutil.h"
#include "KisPart.h"
#include <filter/kis_filter_registry.h>
#include <generator/kis_generator_registry.h>
#include "kis_image_animation_interface.h"
#include "kis_keyframe_channel.h"
#include "kis_time_range.h"
void KisKraLoaderTest::initTestCase()
{
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
}
void KisKraLoaderTest::testLoading()
{
KisDocument *doc = KisPart::instance()->createDocument();
doc->loadNativeFormat(QString(FILES_DATA_DIR) + QDir::separator() + "load_test.kra");
- KisImageWSP image = doc->image();
+ KisImageSP image = doc->image();
image->lock();
QCOMPARE(image->nlayers(), 12);
QCOMPARE(doc->documentInfo()->aboutInfo("title"), QString("test image for loading"));
QCOMPARE(image->height(), 753);
QCOMPARE(image->width(), 1000);
QCOMPARE(image->colorSpace()->id(), KoColorSpaceRegistry::instance()->rgb8()->id());
KisNodeSP node = image->root()->firstChild();
QVERIFY(node);
QCOMPARE(node->name(), QString("Background"));
QVERIFY(node->inherits("KisPaintLayer"));
node = node->nextSibling();
QVERIFY(node);
QCOMPARE(node->name(), QString("Group 1"));
QVERIFY(node->inherits("KisGroupLayer"));
QCOMPARE((int) node->childCount(), 2);
delete doc;
}
void testObligeSingleChildImpl(bool transpDefaultPixel)
{
QString id = !transpDefaultPixel ?
"single_layer_no_channel_flags_nontransp_def_pixel.kra" :
"single_layer_no_channel_flags_transp_def_pixel.kra";
QString fileName = TestUtil::fetchDataFileLazy(id);
KisDocument *doc = KisPart::instance()->createDocument();
doc->loadNativeFormat(fileName);
- KisImageWSP image = doc->image();
+ KisImageSP image = doc->image();
QVERIFY(image);
QCOMPARE(image->nlayers(), 2);
KisNodeSP root = image->root();
KisNodeSP child = root->firstChild();
QVERIFY(child);
QCOMPARE(root->original(), root->projection());
if (transpDefaultPixel) {
QCOMPARE(root->original(), child->projection());
} else {
QVERIFY(root->original() != child->projection());
}
delete doc;
}
void KisKraLoaderTest::testObligeSingleChild()
{
testObligeSingleChildImpl(true);
}
void KisKraLoaderTest::testObligeSingleChildNonTranspPixel()
{
testObligeSingleChildImpl(false);
}
void KisKraLoaderTest::testLoadAnimated()
{
KisDocument *doc = KisPart::instance()->createDocument();
doc->loadNativeFormat(QString(FILES_DATA_DIR) + QDir::separator() + "load_test_animation.kra");
- KisImageWSP image = doc->image();
+ KisImageSP image = doc->image();
KisNodeSP node1 = image->root()->firstChild();
KisNodeSP node2 = node1->nextSibling();
QVERIFY(node1->inherits("KisPaintLayer"));
QVERIFY(node2->inherits("KisPaintLayer"));
KisPaintLayerSP layer1 = qobject_cast<KisPaintLayer*>(node1.data());
KisPaintLayerSP layer2 = qobject_cast<KisPaintLayer*>(node2.data());
KisKeyframeChannel *channel1 = layer1->getKeyframeChannel(KisKeyframeChannel::Content.id());
KisKeyframeChannel *channel2 = layer2->getKeyframeChannel(KisKeyframeChannel::Content.id());
QCOMPARE(channel1->keyframeCount(), 3);
QCOMPARE(channel2->keyframeCount(), 1);
QCOMPARE(image->animationInterface()->framerate(), 17);
QCOMPARE(image->animationInterface()->fullClipRange(), KisTimeRange::fromTime(15, 45));
QCOMPARE(image->animationInterface()->currentTime(), 19);
KisPaintDeviceSP dev = layer1->paintDevice();
const KoColorSpace *cs = dev->colorSpace();
KoColor transparent(Qt::transparent, cs);
KoColor white(Qt::white, cs);
KoColor red(Qt::red, cs);
image->animationInterface()->switchCurrentTimeAsync(0);
image->waitForDone();
QCOMPARE(dev->exactBounds(), QRect(506, 378, 198, 198));
QCOMPARE(dev->x(), -26);
QCOMPARE(dev->y(), -128);
QCOMPARE(dev->defaultPixel(), transparent);
image->animationInterface()->switchCurrentTimeAsync(20);
image->waitForDone();
QCOMPARE(dev->nonDefaultPixelArea(), QRect(615, 416, 129, 129));
QCOMPARE(dev->x(), 502);
QCOMPARE(dev->y(), 224);
QCOMPARE(dev->defaultPixel(), white);
image->animationInterface()->switchCurrentTimeAsync(30);
image->waitForDone();
QCOMPARE(dev->nonDefaultPixelArea(), QRect(729, 452, 45, 44));
QCOMPARE(dev->x(), 645);
QCOMPARE(dev->y(), -10);
QCOMPARE(dev->defaultPixel(), red);
}
QTEST_MAIN(KisKraLoaderTest)
diff --git a/libs/ui/tests/kis_kra_loader_test.h b/plugins/impex/libkra/tests/kis_kra_loader_test.h
similarity index 100%
rename from libs/ui/tests/kis_kra_loader_test.h
rename to plugins/impex/libkra/tests/kis_kra_loader_test.h
diff --git a/libs/ui/tests/kis_kra_saver_test.cpp b/plugins/impex/libkra/tests/kis_kra_saver_test.cpp
similarity index 91%
rename from libs/ui/tests/kis_kra_saver_test.cpp
rename to plugins/impex/libkra/tests/kis_kra_saver_test.cpp
index 667c8446e2..8161df5bd5 100644
--- a/libs/ui/tests/kis_kra_saver_test.cpp
+++ b/plugins/impex/libkra/tests/kis_kra_saver_test.cpp
@@ -1,401 +1,429 @@
/*
* 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_kra_saver_test.h"
#include <QTest>
#include <QBitArray>
#include <KisDocument.h>
#include <KoDocumentInfo.h>
#include <KoShapeContainer.h>
#include <KoPathShape.h>
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "KisDocument.h"
#include "kis_image.h"
#include "kis_pixel_selection.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_shape_layer.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_shape_selection.h"
#include "util.h"
#include "testutil.h"
#include "kis_keyframe_channel.h"
#include "kis_image_animation_interface.h"
#include "kis_layer_properties_icons.h"
#include "kis_transform_mask_params_interface.h"
#include <filter/kis_filter_registry.h>
#include <generator/kis_generator_registry.h>
+#include <KoResourcePaths.h>
+
void KisKraSaverTest::initTestCase()
{
+ KoResourcePaths::addResourceDir("ko_patterns", QString(SYSTEM_RESOURCES_DATA_DIR) + "/patterns");
+
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
}
+void KisKraSaverTest::testCrashyShapeLayer()
+{
+ /**
+ * KisShapeLayer used to call setImage from its destructor and
+ * therefore causing an infinite recursion (when at least one transparency
+ * mask was preset. This testcase just checks that.
+ */
+
+ QScopedPointer<KisDocument> doc(createCompleteDocument(true));
+ Q_UNUSED(doc);
+}
void KisKraSaverTest::testRoundTrip()
{
KisDocument* doc = createCompleteDocument();
KoColor bgColor(Qt::red, doc->image()->colorSpace());
doc->image()->setDefaultProjectionColor(bgColor);
- doc->saveNativeFormat("roundtriptest.kra");
+ doc->exportDocument(QUrl::fromLocalFile("roundtriptest.kra"));
QStringList list;
KisCountVisitor cv1(list, KoProperties());
doc->image()->rootLayer()->accept(cv1);
KisDocument *doc2 = KisPart::instance()->createDocument();
doc2->loadNativeFormat("roundtriptest.kra");
KisCountVisitor cv2(list, KoProperties());
doc2->image()->rootLayer()->accept(cv2);
QCOMPARE(cv1.count(), cv2.count());
// check whether the BG color is saved correctly
QCOMPARE(doc2->image()->defaultProjectionColor(), bgColor);
// test round trip of a transform mask
- KisNodeSP tnode =
- TestUtil::findNode(doc2->image()->rootLayer(), "testTransformMask");
+ KisNode* tnode =
+ TestUtil::findNode(doc2->image()->rootLayer(), "testTransformMask").data();
QVERIFY(tnode);
- KisTransformMask *tmask = dynamic_cast<KisTransformMask*>(tnode.data());
+ KisTransformMask *tmask = dynamic_cast<KisTransformMask*>(tnode);
QVERIFY(tmask);
KisDumbTransformMaskParams *params = dynamic_cast<KisDumbTransformMaskParams*>(tmask->transformParams().data());
QVERIFY(params);
QTransform t = params->testingGetTransform();
QCOMPARE(t, createTestingTransform());
delete doc2;
delete doc;
}
void KisKraSaverTest::testSaveEmpty()
{
KisDocument* doc = createEmptyDocument();
- doc->saveNativeFormat("emptytest.kra");
+ doc->exportDocument(QUrl::fromLocalFile("emptytest.kra"));
QStringList list;
KisCountVisitor cv1(list, KoProperties());
doc->image()->rootLayer()->accept(cv1);
KisDocument *doc2 = KisPart::instance()->createDocument();
doc2->loadNativeFormat("emptytest.kra");
KisCountVisitor cv2(list, KoProperties());
doc2->image()->rootLayer()->accept(cv2);
QCOMPARE(cv1.count(), cv2.count());
delete doc2;
delete doc;
}
#include <filter/kis_filter_configuration.h>
#include "generator/kis_generator_registry.h"
#include <generator/kis_generator.h>
void testRoundTripFillLayerImpl(const QString &testName, KisFilterConfigurationSP config)
{
TestUtil::ExternalImageChecker chk(testName, "fill_layer");
+ chk.setFuzzy(2);
+ QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
+
+ // mask parent should be destructed before the document!
QRect refRect(0,0,512,512);
TestUtil::MaskParent p(refRect);
- QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
doc->setCurrentImage(p.image);
doc->documentInfo()->setAboutInfo("title", p.image->objectName());
KisSelectionSP selection;
KisGeneratorLayerSP glayer = new KisGeneratorLayer(p.image, "glayer", config, selection);
p.image->addNode(glayer, p.image->root(), KisNodeSP());
glayer->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "00_initial_layer_update");
- doc->saveNativeFormat("roundtrip_fill_layer_test.kra");
-
+ doc->exportDocument(QUrl::fromLocalFile("roundtrip_fill_layer_test.kra"));
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_fill_layer_test.kra");
doc2->image()->waitForDone();
chk.checkImage(doc2->image(), "01_fill_layer_round_trip");
QVERIFY(chk.testPassed());
}
void KisKraSaverTest::testRoundTripFillLayerColor()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisGeneratorSP generator = KisGeneratorRegistry::instance()->get("color");
Q_ASSERT(generator);
// warning: we pass null paint device to the default constructed value
KisFilterConfigurationSP config = generator->factoryConfiguration(0);
Q_ASSERT(config);
QVariant v;
v.setValue(KoColor(Qt::red, cs));
config->setProperty("color", v);
testRoundTripFillLayerImpl("fill_layer_color", config);
}
void KisKraSaverTest::testRoundTripFillLayerPattern()
{
KisGeneratorSP generator = KisGeneratorRegistry::instance()->get("pattern");
- Q_ASSERT(generator);
+ QVERIFY(generator);
// warning: we pass null paint device to the default constructed value
KisFilterConfigurationSP config = generator->factoryConfiguration(0);
- Q_ASSERT(config);
+ QVERIFY(config);
QVariant v;
v.setValue(QString("11_drawed_furry.png"));
config->setProperty("pattern", v);
testRoundTripFillLayerImpl("fill_layer_pattern", config);
}
#include "kis_psd_layer_style.h"
void KisKraSaverTest::testRoundTripLayerStyles()
{
TestUtil::ExternalImageChecker chk("kra_saver_test", "layer_styles");
QRect imageRect(0,0,512,512);
+ // the document should be created before the image!
+ QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
+
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image");
KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisPaintLayerSP layer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisPaintLayerSP layer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
image->addNode(layer1);
image->addNode(layer2);
image->addNode(layer3);
- QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
layer1->paintDevice()->fill(QRect(100, 100, 100, 100), KoColor(Qt::red, cs));
layer2->paintDevice()->fill(QRect(200, 200, 100, 100), KoColor(Qt::green, cs));
layer3->paintDevice()->fill(QRect(300, 300, 100, 100), KoColor(Qt::blue, cs));
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->dropShadow()->setEffectEnabled(true);
style->dropShadow()->setAngle(-90);
style->dropShadow()->setUseGlobalLight(false);
layer1->setLayerStyle(style->clone());
style->dropShadow()->setAngle(180);
style->dropShadow()->setUseGlobalLight(true);
layer2->setLayerStyle(style->clone());
style->dropShadow()->setAngle(90);
style->dropShadow()->setUseGlobalLight(false);
layer3->setLayerStyle(style->clone());
image->initialRefreshGraph();
chk.checkImage(image, "00_initial_layers");
- doc->saveNativeFormat("roundtrip_layer_styles.kra");
+ doc->exportDocument(QUrl::fromLocalFile("roundtrip_layer_styles.kra"));
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_layer_styles.kra");
doc2->image()->waitForDone();
chk.checkImage(doc2->image(), "00_initial_layers");
QVERIFY(chk.testPassed());
}
void KisKraSaverTest::testRoundTripAnimation()
{
+ QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
+
QRect imageRect(0,0,512,512);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image");
KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
image->addNode(layer1);
layer1->paintDevice()->fill(QRect(100, 100, 50, 50), KoColor(Qt::black, cs));
layer1->paintDevice()->setDefaultPixel(KoColor(Qt::red, cs));
KUndo2Command parentCommand;
- KisKeyframeChannel *rasterChannel = layer1->getKeyframeChannel(KisKeyframeChannel::Content.id());
+ layer1->enableAnimation();
+ KisKeyframeChannel *rasterChannel = layer1->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
+ QVERIFY(rasterChannel);
rasterChannel->addKeyframe(10, &parentCommand);
image->animationInterface()->switchCurrentTimeAsync(10);
image->waitForDone();
layer1->paintDevice()->fill(QRect(200, 50, 10, 10), KoColor(Qt::black, cs));
layer1->paintDevice()->moveTo(25, 15);
layer1->paintDevice()->setDefaultPixel(KoColor(Qt::green, cs));
rasterChannel->addKeyframe(20, &parentCommand);
image->animationInterface()->switchCurrentTimeAsync(20);
image->waitForDone();
layer1->paintDevice()->fill(QRect(150, 200, 30, 30), KoColor(Qt::black, cs));
layer1->paintDevice()->moveTo(100, 50);
layer1->paintDevice()->setDefaultPixel(KoColor(Qt::blue, cs));
- QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
+ QVERIFY(!layer1->useInTimeline());
+ layer1->setUseInTimeline(true);
+
doc->setCurrentImage(image);
- doc->saveNativeFormat("roundtrip_animation.kra");
+ doc->exportDocument(QUrl::fromLocalFile("roundtrip_animation.kra"));
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_animation.kra");
KisImageSP image2 = doc2->image();
KisNodeSP node = image2->root()->firstChild();
QVERIFY(node->inherits("KisPaintLayer"));
KisPaintLayerSP layer2 = qobject_cast<KisPaintLayer*>(node.data());
cs = layer2->paintDevice()->colorSpace();
QCOMPARE(image2->animationInterface()->currentTime(), 20);
KisKeyframeChannel *channel = layer2->getKeyframeChannel(KisKeyframeChannel::Content.id());
+ QVERIFY(channel);
QCOMPARE(channel->keyframeCount(), 3);
image2->animationInterface()->switchCurrentTimeAsync(0);
image2->waitForDone();
QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(64, 64, 128, 128));
QCOMPARE(layer2->paintDevice()->x(), 0);
QCOMPARE(layer2->paintDevice()->y(), 0);
QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::red, cs));
image2->animationInterface()->switchCurrentTimeAsync(10);
image2->waitForDone();
QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(217, 15, 64, 64));
QCOMPARE(layer2->paintDevice()->x(), 25);
QCOMPARE(layer2->paintDevice()->y(), 15);
QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::green, cs));
image2->animationInterface()->switchCurrentTimeAsync(20);
image2->waitForDone();
QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(228, 242, 64, 64));
QCOMPARE(layer2->paintDevice()->x(), 100);
QCOMPARE(layer2->paintDevice()->y(), 50);
QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::blue, cs));
+ QVERIFY(layer2->useInTimeline());
+
}
#include "lazybrush/kis_lazy_fill_tools.h"
void KisKraSaverTest::testRoundTripColorizeMask()
{
QRect imageRect(0,0,512,512);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->rgb16();
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image");
doc->setCurrentImage(image);
KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, weirdCS);
image->addNode(layer1);
KisColorizeMaskSP mask = new KisColorizeMask();
image->addNode(mask, layer1);
mask->initializeCompositeOp();
delete mask->setColorSpace(layer1->colorSpace());
{
KisPaintDeviceSP key1 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
key1->fill(QRect(50,50,10,20), KoColor(Qt::black, key1->colorSpace()));
mask->testingAddKeyStroke(key1, KoColor(Qt::green, layer1->colorSpace()));
// KIS_DUMP_DEVICE_2(key1, refRect, "key1", "dd");
}
{
KisPaintDeviceSP key2 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
key2->fill(QRect(150,50,10,20), KoColor(Qt::black, key2->colorSpace()));
mask->testingAddKeyStroke(key2, KoColor(Qt::red, layer1->colorSpace()));
// KIS_DUMP_DEVICE_2(key2, refRect, "key2", "dd");
}
{
KisPaintDeviceSP key3 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
key3->fill(QRect(0,0,10,10), KoColor(Qt::black, key3->colorSpace()));
mask->testingAddKeyStroke(key3, KoColor(Qt::blue, layer1->colorSpace()), true);
// KIS_DUMP_DEVICE_2(key3, refRect, "key3", "dd");
}
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, false, image);
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, false, image);
- doc->saveNativeFormat("roundtrip_colorize.kra");
+ doc->exportDocument(QUrl::fromLocalFile("roundtrip_colorize.kra"));
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_colorize.kra");
KisImageSP image2 = doc2->image();
KisNodeSP node = image2->root()->firstChild()->firstChild();
KisColorizeMaskSP mask2 = dynamic_cast<KisColorizeMask*>(node.data());
QVERIFY(mask2);
QCOMPARE(mask2->compositeOpId(), mask->compositeOpId());
QCOMPARE(mask2->colorSpace(), mask->colorSpace());
QCOMPARE(KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool(), false);
QCOMPARE(KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool(), false);
QList<KisLazyFillTools::KeyStroke> strokes = mask->fetchKeyStrokesDirect();
qDebug() << ppVar(strokes.size());
QCOMPARE(strokes[0].dev->exactBounds(), QRect(50,50,10,20));
QCOMPARE(strokes[0].isTransparent, false);
QCOMPARE(strokes[0].color.colorSpace(), weirdCS);
QCOMPARE(strokes[1].dev->exactBounds(), QRect(150,50,10,20));
QCOMPARE(strokes[1].isTransparent, false);
QCOMPARE(strokes[1].color.colorSpace(), weirdCS);
QCOMPARE(strokes[2].dev->exactBounds(), QRect(0,0,10,10));
QCOMPARE(strokes[2].isTransparent, true);
QCOMPARE(strokes[2].color.colorSpace(), weirdCS);
}
QTEST_MAIN(KisKraSaverTest)
diff --git a/libs/ui/tests/kis_kra_saver_test.h b/plugins/impex/libkra/tests/kis_kra_saver_test.h
similarity index 97%
rename from libs/ui/tests/kis_kra_saver_test.h
rename to plugins/impex/libkra/tests/kis_kra_saver_test.h
index 0b8f2c23d5..94aaeb2684 100644
--- a/libs/ui/tests/kis_kra_saver_test.h
+++ b/plugins/impex/libkra/tests/kis_kra_saver_test.h
@@ -1,46 +1,48 @@
/*
* 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 KIS_KRA_SAVER_TEST_H
#define KIS_KRA_SAVER_TEST_H
#include <QtTest>
class KisKraSaverTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
+ void testCrashyShapeLayer();
+
// XXX: Also test roundtripping of metadata
void testRoundTrip();
void testSaveEmpty();
void testRoundTripFillLayerColor();
void testRoundTripFillLayerPattern();
void testRoundTripLayerStyles();
void testRoundTripAnimation();
void testRoundTripColorizeMask();
};
#endif
diff --git a/libs/ui/tests/kis_kra_savexml_visitor_test.cpp b/plugins/impex/libkra/tests/kis_kra_savexml_visitor_test.cpp
similarity index 98%
rename from libs/ui/tests/kis_kra_savexml_visitor_test.cpp
rename to plugins/impex/libkra/tests/kis_kra_savexml_visitor_test.cpp
index 3d36004bbc..b1285ee484 100644
--- a/libs/ui/tests/kis_kra_savexml_visitor_test.cpp
+++ b/plugins/impex/libkra/tests/kis_kra_savexml_visitor_test.cpp
@@ -1,89 +1,89 @@
/*
* 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_kra_savexml_visitor_test.h"
#include <QTest>
#include <QBitArray>
#include <QDomDocument>
#include <KisDocument.h>
#include <KoDocumentInfo.h>
#include <KoShapeContainer.h>
#include <KoPathShape.h>
#include "kis_count_visitor.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "KisDocument.h"
#include "kis_image.h"
#include "kis_pixel_selection.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_shape_layer.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_shape_selection.h"
#include "util.h"
-#include "kra/kis_kra_savexml_visitor.h"
+#include "../kis_kra_savexml_visitor.h"
#include <filter/kis_filter_registry.h>
#include <generator/kis_generator_registry.h>
void KisKraSaveXmlVisitorTest::initTestCase()
{
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
}
void KisKraSaveXmlVisitorTest::testCreateDomDocument()
{
KisDocument* doc = createCompleteDocument();
quint32 count = 0;
QDomDocument dom;
QDomElement image = dom.createElement("IMAGE"); // Legacy!
KisSaveXmlVisitor visitor(dom, image, count, "", true);
Q_ASSERT(doc->image());
QStringList list;
doc->image()->lock();
KisCountVisitor cv(list, KoProperties());
doc->image()->rootLayer()->accept(cv);
doc->image()->rootLayer()->accept(visitor);
QCOMPARE((int)visitor.m_count, (int)cv.count());
//delete doc;
}
QTEST_MAIN(KisKraSaveXmlVisitorTest)
diff --git a/libs/ui/tests/kis_kra_savexml_visitor_test.h b/plugins/impex/libkra/tests/kis_kra_savexml_visitor_test.h
similarity index 100%
rename from libs/ui/tests/kis_kra_savexml_visitor_test.h
rename to plugins/impex/libkra/tests/kis_kra_savexml_visitor_test.h
diff --git a/libs/ui/tests/util.h b/plugins/impex/libkra/tests/util.h
similarity index 97%
copy from libs/ui/tests/util.h
copy to plugins/impex/libkra/tests/util.h
index 63f6c69040..307ad74787 100644
--- a/libs/ui/tests/util.h
+++ b/plugins/impex/libkra/tests/util.h
@@ -1,226 +1,226 @@
/*
* 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 _UTIL_H_
#define _UTIL_H_
#include <QTest>
#include <QTest>
#include <QBitArray>
#include <KisDocument.h>
#include <KoDocumentInfo.h>
#include <KoColorSpaceRegistry.h>
#include <KoShapeContainer.h>
#include <KoColorSpace.h>
#include <KoPathShape.h>
#include <kis_count_visitor.h>
#include "kis_types.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "KisDocument.h"
#include "KisPart.h"
#include "kis_image.h"
#include "kis_pixel_selection.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_shape_layer.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_shape_selection.h"
#include "kis_default_bounds.h"
#include "kis_transform_mask_params_interface.h"
KisSelectionSP createPixelSelection(KisPaintDeviceSP paintDevice)
{
KisSelectionSP pixelSelection = new KisSelection(new KisSelectionDefaultBounds(paintDevice));
KisFillPainter gc(pixelSelection->pixelSelection());
gc.fillRect(10, 10, 200, 200, KoColor(gc.device()->colorSpace()));
gc.fillRect(150, 150, 200, 200, KoColor(QColor(100, 100, 100, 100), gc.device()->colorSpace()));
gc.end();
return pixelSelection;
}
-KisSelectionSP createVectorSelection(KisPaintDeviceSP paintDevice, KisImageWSP image)
+KisSelectionSP createVectorSelection(KisPaintDeviceSP paintDevice, KisImageSP image)
{
KisSelectionSP vectorSelection = new KisSelection(new KisSelectionDefaultBounds(paintDevice));
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF(10, 10) + QPointF(100, 0));
path->lineTo(QPointF(100, 100));
path->lineTo(QPointF(10, 10) + QPointF(0, 100));
path->close();
path->normalize();
KisShapeSelection* shapeSelection = new KisShapeSelection(image, vectorSelection);
shapeSelection->addShape(path);
vectorSelection->setShapeSelection(shapeSelection);
return vectorSelection;
}
QTransform createTestingTransform() {
return QTransform(1,2,3,4,5,6,7,8,9);
}
KisDocument* createCompleteDocument()
{
- KisImageWSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
+ KisImageSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
KisGroupLayerSP group1 = new KisGroupLayer(image, "group1", 50);
KisGroupLayerSP group2 = new KisGroupLayer(image, "group2", 100);
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paintlayer1", OPACITY_OPAQUE_U8);
paintLayer1->setUserLocked(true);
QBitArray channelFlags(4);
channelFlags[0] = true;
channelFlags[2] = true;
paintLayer1->setChannelFlags(channelFlags);
{
KisFillPainter gc(paintLayer1->paintDevice());
gc.fillRect(10, 10, 200, 200, KoColor(Qt::red, paintLayer1->paintDevice()->colorSpace()));
gc.end();
}
KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paintlayer2", OPACITY_TRANSPARENT_U8, KoColorSpaceRegistry::instance()->lab16());
paintLayer2->setVisible(false);
{
KisFillPainter gc(paintLayer2->paintDevice());
gc.fillRect(0, 0, 900, 1024, KoColor(QColor(10, 20, 30), paintLayer2->paintDevice()->colorSpace()));
gc.end();
}
KisCloneLayerSP cloneLayer1 = new KisCloneLayer(group1, image, "clonelayer1", 150);
cloneLayer1->setX(100);
cloneLayer1->setY(100);
KisSelectionSP pixelSelection = createPixelSelection(paintLayer1->paintDevice());
KisFilterConfigurationSP kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(group2->projection());
Q_ASSERT(kfc);
KisAdjustmentLayerSP adjustmentLayer1 = new KisAdjustmentLayer(image, "adjustmentLayer1", kfc, pixelSelection);
kfc = 0; // kfc cannot be shared!
KisSelectionSP vectorSelection = createVectorSelection(paintLayer2->paintDevice(), image);
kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(group2->projection());
KisAdjustmentLayerSP adjustmentLayer2 = new KisAdjustmentLayer(image, "adjustmentLayer2", kfc, vectorSelection);
kfc = 0; // kfc cannot be shared!
image->addNode(paintLayer1);
image->addNode(group1);
image->addNode(paintLayer2, group1);
image->addNode(group2);
image->addNode(cloneLayer1, group2);
image->addNode(adjustmentLayer1, group2);
// KoShapeContainer * parentContainer =
// dynamic_cast<KoShapeContainer*>(doc->shapeForNode(group1));
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF(10, 10) + QPointF(100, 0));
path->lineTo(QPointF(100, 100));
path->lineTo(QPointF(10, 10) + QPointF(0, 100));
path->close();
path->normalize();
KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), image, "shapeLayer1", 75);
shapeLayer->addShape(path);
image->addNode(shapeLayer, group1);
image->addNode(adjustmentLayer2, group1);
KisFilterMaskSP filterMask1 = new KisFilterMask();
filterMask1->setName("filterMask1");
kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(group2->projection());
filterMask1->setFilter(kfc);
kfc = 0; // kfc cannot be shared!
filterMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(filterMask1, paintLayer1);
KisFilterMaskSP filterMask2 = new KisFilterMask();
filterMask2->setName("filterMask2");
kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(group2->projection());
filterMask2->setFilter(kfc);
kfc = 0; // kfc cannot be shared!
filterMask2->setSelection(createVectorSelection(paintLayer2->paintDevice(), image));
image->addNode(filterMask2, paintLayer2);
KisTransparencyMaskSP transparencyMask1 = new KisTransparencyMask();
transparencyMask1->setName("transparencyMask1");
transparencyMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(transparencyMask1, group1);
KisTransparencyMaskSP transparencyMask2 = new KisTransparencyMask();
transparencyMask2->setName("transparencyMask2");
transparencyMask2->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(transparencyMask2, group2);
KisSelectionMaskSP selectionMask1 = new KisSelectionMask(image);
image->addNode(selectionMask1, paintLayer1);
selectionMask1->setName("selectionMask1");
selectionMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
KisSelectionMaskSP selectionMask2 = new KisSelectionMask(image);
selectionMask2->setName("selectionMask2");
selectionMask2->setSelection(createPixelSelection(paintLayer2->paintDevice()));
image->addNode(selectionMask2, paintLayer2);
KisTransformMaskSP transformMask = new KisTransformMask();
transformMask->setName("testTransformMask");
transformMask->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(createTestingTransform())));
image->addNode(transformMask, paintLayer2);
return doc;
}
KisDocument *createEmptyDocument()
{
- KisImageWSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
+ KisImageSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
return doc;
}
#endif
diff --git a/plugins/impex/odg/kis_odg_import.cc b/plugins/impex/odg/kis_odg_import.cc
index 53c3baac7f..a6b2174c10 100644
--- a/plugins/impex/odg/kis_odg_import.cc
+++ b/plugins/impex/odg/kis_odg_import.cc
@@ -1,161 +1,143 @@
/*
* 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 <KoOdfStylesReader.h>
-
+#include <KoStore.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, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisODGImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
- Q_UNUSED(configuration);
- dbgFile << "Import odg";
-
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- KoStore* store = KoStore::createStore(filename, KoStore::Read, from, KoStore::Zip);
+ KoStore* store = KoStore::createStore(io, KoStore::Read, "application/vnd.oasis.opendocument.graphics", 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()));
if (style) {
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());
+ KoShapeLoadingContext shapeContext(context, document->shapeController()->resourceManager());
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
- KisImageWSP image = new KisImage(doc->createUndoStore(), width, height, cs, "built image");
- doc->setCurrentImage(image);
+ KisImageSP image = new KisImage(document->createUndoStore(), width, height, cs, "built image");
+ document->setCurrentImage(image);
KoXmlElement layerElement;
forEachElement(layerElement, KoXml::namedItemNS(page, KoXmlNS::draw, "layer-set")) {
- KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), image,
+ KisShapeLayerSP shapeLayer = new KisShapeLayer(document->shapeController(), image,
i18n("Vector Layer"),
OPACITY_OPAQUE_U8);
if (!shapeLayer->loadOdf(layerElement, shapeContext)) {
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/plugins/impex/odg/kis_odg_import.h b/plugins/impex/odg/kis_odg_import.h
index 6d7a523a33..90a1b98291 100644
--- a/plugins/impex/odg/kis_odg_import.h
+++ b/plugins/impex/odg/kis_odg_import.h
@@ -1,36 +1,36 @@
/*
* 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.
*/
#ifndef _KIS_ODG_IMPORT_H_
#define _KIS_ODG_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisODGImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisODGImport(QObject *parent, const QVariantList &);
virtual ~KisODGImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/odg/krita_odg_import.json b/plugins/impex/odg/krita_odg_import.json
index 7a204d4f50..7f2ae00db8 100644
--- a/plugins/impex/odg/krita_odg_import.json
+++ b/plugins/impex/odg/krita_odg_import.json
@@ -1,13 +1,13 @@
{
"Id": "Krita ODG Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "application/vnd.oasis.opendocument.graphics",
"X-KDE-Library": "kritaodgimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "odf"
}
diff --git a/plugins/impex/ora/CMakeLists.txt b/plugins/impex/ora/CMakeLists.txt
index a12abb7822..76099a24ce 100644
--- a/plugins/impex/ora/CMakeLists.txt
+++ b/plugins/impex/ora/CMakeLists.txt
@@ -1,28 +1,32 @@
set(libkritaconverter_LIB_SRCS
ora_converter.cc
+ ora_load_context.cc
+ ora_save_context.cc
+ kis_open_raster_stack_load_visitor.cpp
+ kis_open_raster_stack_save_visitor.cpp
)
set(kritaoraimport_SOURCES
ora_import.cc
${libkritaconverter_LIB_SRCS}
)
add_library(kritaoraimport MODULE ${kritaoraimport_SOURCES})
target_link_libraries(kritaoraimport kritaui )
install(TARGETS kritaoraimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritaoraexport_SOURCES
ora_export.cc
${libkritaconverter_LIB_SRCS}
)
add_library(kritaoraexport MODULE ${kritaoraexport_SOURCES})
-target_link_libraries(kritaoraexport kritaui )
+target_link_libraries(kritaoraexport kritaui kritaimpex)
install(TARGETS kritaoraexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_ora.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/libs/ui/ora/kis_open_raster_load_context.h b/plugins/impex/ora/kis_open_raster_load_context.h
similarity index 93%
rename from libs/ui/ora/kis_open_raster_load_context.h
rename to plugins/impex/ora/kis_open_raster_load_context.h
index c9f927ef02..b29a3e9bb9 100644
--- a/libs/ui/ora/kis_open_raster_load_context.h
+++ b/plugins/impex/ora/kis_open_raster_load_context.h
@@ -1,36 +1,36 @@
/*
* 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 _KIS_OPEN_RASTER_LOAD_CONTEXT_H_
#define _KIS_OPEN_RASTER_LOAD_CONTEXT_H_
class QString;
class QDomDocument;
#include <kis_types.h>
class KisOpenRasterLoadContext
{
public:
virtual ~KisOpenRasterLoadContext() {}
- virtual KisImageWSP loadDeviceData(const QString & fileName) = 0;
+ virtual KisImageSP loadDeviceData(const QString & fileName) = 0;
virtual QDomDocument loadStack() = 0;
};
#endif
diff --git a/libs/ui/ora/kis_open_raster_save_context.h b/plugins/impex/ora/kis_open_raster_save_context.h
similarity index 100%
rename from libs/ui/ora/kis_open_raster_save_context.h
rename to plugins/impex/ora/kis_open_raster_save_context.h
diff --git a/libs/ui/ora/kis_open_raster_stack_load_visitor.cpp b/plugins/impex/ora/kis_open_raster_stack_load_visitor.cpp
similarity index 84%
rename from libs/ui/ora/kis_open_raster_stack_load_visitor.cpp
rename to plugins/impex/ora/kis_open_raster_stack_load_visitor.cpp
index dacdbc22a6..7e193fc645 100644
--- a/libs/ui/ora/kis_open_raster_stack_load_visitor.cpp
+++ b/plugins/impex/ora/kis_open_raster_stack_load_visitor.cpp
@@ -1,234 +1,254 @@
/*
* Copyright (c) 2006-2007,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_open_raster_stack_load_visitor.h"
#include <QDomElement>
#include <QDomNode>
#include <KoColorSpaceRegistry.h>
// Includes from krita/image
#include <kis_adjustment_layer.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <KoCompositeOpRegistry.h>
#include <kis_filter_configuration.h>
#include <kis_paint_layer.h>
#include <kis_png_converter.h>
#include <kis_selection.h>
#include <kis_dom_utils.h>
#include "KisDocument.h"
#include "kis_open_raster_load_context.h"
struct KisOpenRasterStackLoadVisitor::Private {
- KisImageWSP image;
+ KisImageSP image;
vKisNodeSP activeNodes;
KisUndoStore* undoStore;
KisOpenRasterLoadContext* loadContext;
double xRes;
double yRes;
};
KisOpenRasterStackLoadVisitor::KisOpenRasterStackLoadVisitor(KisUndoStore* undoStore, KisOpenRasterLoadContext* orlc)
: d(new Private)
{
d->undoStore = undoStore;
d->loadContext = orlc;
}
KisOpenRasterStackLoadVisitor::~KisOpenRasterStackLoadVisitor()
{
delete d;
}
-KisImageWSP KisOpenRasterStackLoadVisitor::image()
+KisImageSP KisOpenRasterStackLoadVisitor::image()
{
return d->image;
}
vKisNodeSP KisOpenRasterStackLoadVisitor::activeNodes()
{
return d->activeNodes;
}
void KisOpenRasterStackLoadVisitor::loadImage()
{
QDomDocument doc = d->loadContext->loadStack();
for (QDomNode node = doc.firstChild(); !node.isNull(); node = node.nextSibling()) {
if (node.isElement() && node.nodeName() == "image") { // it's the image root
QDomElement subelem = node.toElement();
int width = 0;
if (!subelem.attribute("w").isNull()) {
width = subelem.attribute("w").toInt();
}
int height = 0;
if (!subelem.attribute("h").isNull()) {
height = subelem.attribute("h").toInt();
}
d->xRes = 75.0/72; // Setting the default value of the X Resolution = 75ppi
if (!subelem.attribute("xres").isNull()){
d->xRes = (KisDomUtils::toDouble(subelem.attribute("xres")) / 72);
}
d->yRes = 75.0/72;
if (!subelem.attribute("yres").isNull()){
d->yRes = (KisDomUtils::toDouble(subelem.attribute("yres")) / 72);
}
dbgFile << ppVar(width) << ppVar(height);
d->image = new KisImage(d->undoStore, width, height, KoColorSpaceRegistry::instance()->rgb8(), "OpenRaster Image (name)");
for (QDomNode node2 = node.firstChild(); !node2.isNull(); node2 = node2.nextSibling()) {
if (node2.isElement() && node2.nodeName() == "stack") { // it's the root layer !
QDomElement subelem2 = node2.toElement();
loadGroupLayer(subelem2, d->image->rootLayer());
break;
}
}
}
}
}
void KisOpenRasterStackLoadVisitor::loadLayerInfo(const QDomElement& elem, KisLayerSP layer)
{
layer->setName(elem.attribute("name"));
layer->setX(elem.attribute("x").toInt());
layer->setY(elem.attribute("y").toInt());
if (elem.attribute("visibility") == "hidden") {
layer->setVisible(false);
} else {
layer->setVisible(true);
}
if (elem.hasAttribute("edit-locked")) {
layer->setUserLocked(elem.attribute("edit-locked") == "true");
}
if (elem.hasAttribute("selected") && elem.attribute("selected") == "true") {
d->activeNodes.append(layer);
}
QString compop = elem.attribute("composite-op");
if (compop.startsWith("svg:")) {
- if (compop == "svg:clear") layer->setCompositeOpId(COMPOSITE_CLEAR);
+ //we don't have a 'composite op clear' despite the registery reserving a string for it, doesn't matter, ora doesn't use it.
+ //if (compop == "svg:clear") layer->setCompositeOpId(COMPOSITE_CLEAR);
if (compop == "svg:src-over") layer->setCompositeOpId(COMPOSITE_OVER);
- if (compop == "svg:add") layer->setCompositeOpId(COMPOSITE_ADD);
+ //not part of the spec.
+ //if (compop == "svg:dst-over") layer->setCompositeOpId(COMPOSITE_BEHIND);
+ //dst-in "The source that overlaps the destination, replaces the destination."
+ if (compop == "svg:dst-in") layer->setCompositeOpId(COMPOSITE_DESTINATION_IN);
+ //dst-out "dst is placed, where it falls outside of the source."
+ if (compop == "svg:dst-out") layer->setCompositeOpId(COMPOSITE_ERASE);
+ //src-atop "Destination which overlaps the source replaces the source. Source is placed elsewhere."
+ //this is basically our alpha-inherit.
+ if (compop == "svg:src-atop") layer->disableAlphaChannel(true);
+ //dst-atop
+ if (compop == "svg:dst-atop") layer->setCompositeOpId(COMPOSITE_DESTINATION_ATOP);
+ //plus is svg standard's way of saying addtion... photoshop calls this linear dodge, btw, maybe make a similar alias?
+ if (compop == "svg:plus") layer->setCompositeOpId(COMPOSITE_ADD);
if (compop == "svg:multiply") layer->setCompositeOpId(COMPOSITE_MULT);
if (compop == "svg:screen") layer->setCompositeOpId(COMPOSITE_SCREEN);
if (compop == "svg:overlay") layer->setCompositeOpId(COMPOSITE_OVERLAY);
if (compop == "svg:darken") layer->setCompositeOpId(COMPOSITE_DARKEN);
if (compop == "svg:lighten") layer->setCompositeOpId(COMPOSITE_LIGHTEN);
if (compop == "svg:color-dodge") layer->setCompositeOpId(COMPOSITE_DODGE);
if (compop == "svg:color-burn") layer->setCompositeOpId(COMPOSITE_BURN);
if (compop == "svg:hard-light") layer->setCompositeOpId(COMPOSITE_HARD_LIGHT);
if (compop == "svg:soft-light") layer->setCompositeOpId(COMPOSITE_SOFT_LIGHT_SVG);
if (compop == "svg:difference") layer->setCompositeOpId(COMPOSITE_DIFF);
if (compop == "svg:color") layer->setCompositeOpId(COMPOSITE_COLOR);
if (compop == "svg:luminosity") layer->setCompositeOpId(COMPOSITE_LUMINIZE);
if (compop == "svg:hue") layer->setCompositeOpId(COMPOSITE_HUE);
if (compop == "svg:saturation") layer->setCompositeOpId(COMPOSITE_SATURATION);
- if (compop == "svg:exclusion") layer->setCompositeOpId(COMPOSITE_EXCLUSION);
+ //Exclusion isn't in the official list.
+ //if (compop == "svg:exclusion") layer->setCompositeOpId(COMPOSITE_EXCLUSION);
}
else if (compop.startsWith("krita:")) {
compop = compop.remove(0, 6);
layer->setCompositeOpId(compop);
}
else {
// to fix old bugs in krita's ora export
if (compop == "color-dodge") layer->setCompositeOpId(COMPOSITE_DODGE);
if (compop == "difference") layer->setCompositeOpId(COMPOSITE_DIFF);
+ if (compop == "svg:add") layer->setCompositeOpId(COMPOSITE_ADD);
}
}
void KisOpenRasterStackLoadVisitor::loadAdjustmentLayer(const QDomElement& elem, KisAdjustmentLayerSP aL)
{
loadLayerInfo(elem, aL);
}
void KisOpenRasterStackLoadVisitor::loadPaintLayer(const QDomElement& elem, KisPaintLayerSP pL)
{
loadLayerInfo(elem, pL);
dbgFile << "Loading was unsuccessful";
}
void KisOpenRasterStackLoadVisitor::loadGroupLayer(const QDomElement& elem, KisGroupLayerSP gL)
{
dbgFile << "Loading group layer";
loadLayerInfo(elem, gL);
for (QDomNode node = elem.firstChild(); !node.isNull(); node = node.nextSibling()) {
if (node.isElement()) {
QDomElement subelem = node.toElement();
if (node.nodeName() == "stack") {
double opacity = 1.0;
if (!subelem.attribute("opacity").isNull()) {
opacity = KisDomUtils::toDouble(subelem.attribute("opacity", "1.0"));
}
KisGroupLayerSP layer = new KisGroupLayer(d->image, "", opacity * 255);
+ bool passThrough = true;
+ if (subelem.attribute("isolation")=="isolate") {
+ passThrough = false;
+ }
+ layer->setPassThroughMode(passThrough);
d->image->addNode(layer.data(), gL.data(), 0);
loadGroupLayer(subelem, layer);
} else if (node.nodeName() == "layer") {
QString filename = subelem.attribute("src");
if (!filename.isNull()) {
double opacity = 1.0;
opacity = KisDomUtils::toDouble(subelem.attribute("opacity", "1.0"));
- KisImageWSP pngImage = d->loadContext->loadDeviceData(filename);
+ KisImageSP pngImage = d->loadContext->loadDeviceData(filename);
if (pngImage) {
// If ORA doesn't have resolution info, load the default value(75 ppi) else fetch from stack.xml
d->image->setResolution(d->xRes, d->yRes);
// now get the device
KisPaintDeviceSP device = pngImage->projection();
delete pngImage.data();
KisPaintLayerSP layer = new KisPaintLayer(gL->image() , "", opacity * 255, device);
d->image->addNode(layer.data(), gL.data(), 0);
loadPaintLayer(subelem, layer);
dbgFile << "Loading was successful";
}
}
} else if (node.nodeName() == "filter") {
QString filterType = subelem.attribute("type");
QStringList filterTypeSplit = filterType.split(':');
KisFilterSP f = 0;
if (filterTypeSplit[0] == "applications" && filterTypeSplit[1] == "krita") {
f = KisFilterRegistry::instance()->value(filterTypeSplit[2]);
}
KisFilterConfigurationSP kfc = f->defaultConfiguration(0);
KisAdjustmentLayerSP layer = new KisAdjustmentLayer(gL->image() , "", kfc, KisSelectionSP(0));
d->image->addNode(layer.data(), gL.data(), 0);
loadAdjustmentLayer(subelem, layer);
} else {
dbgFile << "Unknown element : " << node.nodeName();
}
}
}
}
diff --git a/libs/ui/ora/kis_open_raster_stack_load_visitor.h b/plugins/impex/ora/kis_open_raster_stack_load_visitor.h
similarity index 93%
rename from libs/ui/ora/kis_open_raster_stack_load_visitor.h
rename to plugins/impex/ora/kis_open_raster_stack_load_visitor.h
index c377dd4022..427860558b 100644
--- a/libs/ui/ora/kis_open_raster_stack_load_visitor.h
+++ b/plugins/impex/ora/kis_open_raster_stack_load_visitor.h
@@ -1,53 +1,50 @@
/*
* 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.
*/
#ifndef KIS_OPEN_RASTER_STACK_LOAD_VISITOR_H_
#define KIS_OPEN_RASTER_STACK_LOAD_VISITOR_H_
#include "kis_global.h"
#include "kis_types.h"
-#include <kritaui_export.h>
-
class QDomElement;
-
class KisUndoStore;
class KisOpenRasterLoadContext;
-class KRITAUI_EXPORT KisOpenRasterStackLoadVisitor
+class KisOpenRasterStackLoadVisitor
{
public:
KisOpenRasterStackLoadVisitor(KisUndoStore *undoStore, KisOpenRasterLoadContext* orlc);
virtual ~KisOpenRasterStackLoadVisitor();
public:
void loadImage();
void loadPaintLayer(const QDomElement& elem, KisPaintLayerSP pL);
void loadAdjustmentLayer(const QDomElement& elem, KisAdjustmentLayerSP pL);
void loadGroupLayer(const QDomElement& elem, KisGroupLayerSP gL);
- KisImageWSP image();
+ KisImageSP image();
vKisNodeSP activeNodes();
private:
void loadLayerInfo(const QDomElement& elem, KisLayerSP layer);
struct Private;
Private* const d;
};
#endif // KIS_LAYER_VISITOR_H_
diff --git a/libs/ui/ora/kis_open_raster_stack_save_visitor.cpp b/plugins/impex/ora/kis_open_raster_stack_save_visitor.cpp
similarity index 91%
rename from libs/ui/ora/kis_open_raster_stack_save_visitor.cpp
rename to plugins/impex/ora/kis_open_raster_stack_save_visitor.cpp
index 228254950b..330a6de02a 100644
--- a/libs/ui/ora/kis_open_raster_stack_save_visitor.cpp
+++ b/plugins/impex/ora/kis_open_raster_stack_save_visitor.cpp
@@ -1,164 +1,172 @@
/*
* Copyright (c) 2006-2007,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_open_raster_stack_save_visitor.h"
#include <math.h>
#include <QDomElement>
#include <QImage>
#include <KoCompositeOpRegistry.h>
#include "kis_adjustment_layer.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include <generator/kis_generator_layer.h>
#include "kis_open_raster_save_context.h"
#include <kis_clone_layer.h>
#include <kis_external_layer_iface.h>
struct KisOpenRasterStackSaveVisitor::Private {
Private() : currentElement(0) {}
KisOpenRasterSaveContext* saveContext;
QDomDocument layerStack;
QDomElement* currentElement;
vKisNodeSP activeNodes;
};
KisOpenRasterStackSaveVisitor::KisOpenRasterStackSaveVisitor(KisOpenRasterSaveContext* saveContext, vKisNodeSP activeNodes)
: d(new Private)
{
d->saveContext = saveContext;
d->activeNodes = activeNodes;
}
KisOpenRasterStackSaveVisitor::~KisOpenRasterStackSaveVisitor()
{
delete d;
}
void KisOpenRasterStackSaveVisitor::saveLayerInfo(QDomElement& elt, KisLayer* layer)
{
elt.setAttribute("name", layer->name());
elt.setAttribute("opacity", QString().setNum(layer->opacity() / 255.0));
elt.setAttribute("visibility", layer->visible() ? "visible" : "hidden");
if (layer->userLocked()) {
elt.setAttribute("edit-locked", "true");
}
if (d->activeNodes.contains(layer)) {
elt.setAttribute("selected", "true");
}
QString compop = layer->compositeOpId();
if (layer->compositeOpId() == COMPOSITE_CLEAR) compop = "svg:clear";
else if (layer->compositeOpId() == COMPOSITE_OVER) compop = "svg:src-over";
- else if (layer->compositeOpId() == COMPOSITE_ADD) compop = "svg:add";
+ else if (layer->compositeOpId() == COMPOSITE_ERASE) compop = "svg:dst-out";
+ else if (layer->alphaChannelDisabled()) compop = "svg:src-atop";
+ else if (layer->compositeOpId() == COMPOSITE_DESTINATION_ATOP) compop = "svg:dst-atop";
+ else if (layer->compositeOpId() == COMPOSITE_DESTINATION_IN) compop = "svg:dst-in";
+ else if (layer->compositeOpId() == COMPOSITE_ADD) compop = "svg:plus";
else if (layer->compositeOpId() == COMPOSITE_MULT) compop = "svg:multiply";
else if (layer->compositeOpId() == COMPOSITE_SCREEN) compop = "svg:screen";
else if (layer->compositeOpId() == COMPOSITE_OVERLAY) compop = "svg:overlay";
else if (layer->compositeOpId() == COMPOSITE_DARKEN) compop = "svg:darken";
else if (layer->compositeOpId() == COMPOSITE_LIGHTEN) compop = "svg:lighten";
else if (layer->compositeOpId() == COMPOSITE_DODGE) compop = "svg:color-dodge";
else if (layer->compositeOpId() == COMPOSITE_BURN) compop = "svg:color-burn";
else if (layer->compositeOpId() == COMPOSITE_HARD_LIGHT) compop = "svg:hard-light";
else if (layer->compositeOpId() == COMPOSITE_SOFT_LIGHT_SVG) compop = "svg:soft-light";
else if (layer->compositeOpId() == COMPOSITE_DIFF) compop = "svg:difference";
else if (layer->compositeOpId() == COMPOSITE_COLOR) compop = "svg:color";
else if (layer->compositeOpId() == COMPOSITE_LUMINIZE) compop = "svg:luminosity";
else if (layer->compositeOpId() == COMPOSITE_HUE) compop = "svg:hue";
else if (layer->compositeOpId() == COMPOSITE_SATURATION) compop = "svg:saturation";
- else if (layer->compositeOpId() == COMPOSITE_EXCLUSION) compop = "svg:exclusion";
+ //else if (layer->compositeOpId() == COMPOSITE_EXCLUSION) compop = "svg:exclusion";
else compop = "krita:" + layer->compositeOpId();
elt.setAttribute("composite-op", compop);
}
bool KisOpenRasterStackSaveVisitor::visit(KisPaintLayer *layer)
{
return saveLayer(layer);
}
bool KisOpenRasterStackSaveVisitor::visit(KisGeneratorLayer* layer)
{
return saveLayer(layer);
}
bool KisOpenRasterStackSaveVisitor::visit(KisGroupLayer *layer)
{
QDomElement* previousElt = d->currentElement;
QDomElement elt = d->layerStack.createElement("stack");
d->currentElement = &elt;
saveLayerInfo(elt, layer);
-
+ QString isolate = "isolate";
+ if (layer->passThroughMode()) {
+ isolate = "auto";
+ }
+ elt.setAttribute("isolation", isolate);
visitAll(layer);
if (previousElt) {
previousElt->insertBefore(elt, QDomNode());
d->currentElement = previousElt;
} else {
QDomElement imageElt = d->layerStack.createElement("image");
int width = layer->image()->width();
int height = layer->image()->height();
int xRes = (int)(qRound(layer->image()->xRes() * 72));
int yRes = (int)(qRound(layer->image()->yRes() * 72));
imageElt.setAttribute("version", "0.0.1");
imageElt.setAttribute("w", width);
imageElt.setAttribute("h", height);
imageElt.setAttribute("xres", xRes);
imageElt.setAttribute("yres", yRes);
imageElt.appendChild(elt);
d->layerStack.insertBefore(imageElt, QDomNode());
d->currentElement = 0;
d->saveContext->saveStack(d->layerStack);
}
return true;
}
bool KisOpenRasterStackSaveVisitor::visit(KisAdjustmentLayer *layer)
{
QDomElement elt = d->layerStack.createElement("filter");
saveLayerInfo(elt, layer);
elt.setAttribute("type", "applications:krita:" + layer->filter()->name());
return true;
}
bool KisOpenRasterStackSaveVisitor::visit(KisCloneLayer *layer)
{
return saveLayer(layer);
}
bool KisOpenRasterStackSaveVisitor::visit(KisExternalLayer * layer)
{
return saveLayer(layer);
}
bool KisOpenRasterStackSaveVisitor::saveLayer(KisLayer *layer)
{
QString filename = d->saveContext->saveDeviceData(layer->projection(), layer->metaData(), layer->image()->bounds(), layer->image()->xRes(), layer->image()->yRes());
QDomElement elt = d->layerStack.createElement("layer");
saveLayerInfo(elt, layer);
elt.setAttribute("src", filename);
d->currentElement->insertBefore(elt, QDomNode());
return true;
}
diff --git a/libs/ui/ora/kis_open_raster_stack_save_visitor.h b/plugins/impex/ora/kis_open_raster_stack_save_visitor.h
similarity index 95%
rename from libs/ui/ora/kis_open_raster_stack_save_visitor.h
rename to plugins/impex/ora/kis_open_raster_stack_save_visitor.h
index 21b51b2483..530c28a03f 100644
--- a/libs/ui/ora/kis_open_raster_stack_save_visitor.h
+++ b/plugins/impex/ora/kis_open_raster_stack_save_visitor.h
@@ -1,86 +1,85 @@
/*
* Copyright (c) 2006-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 KIS_OPEN_RASTER_STACK_SAVE_VISITOR_H_
#define KIS_OPEN_RASTER_STACK_SAVE_VISITOR_H_
#include <QSet>
#include "kis_global.h"
#include "kis_types.h"
#include "kis_node_visitor.h"
#include "kis_layer.h"
-#include <kritaui_export.h>
class KisOpenRasterSaveContext;
class KisAdjustmentLayer;
class KisGroupLayer;
class KisPaintLayer;
class KisGeneratorLayer;
class QDomElement;
-class KRITAUI_EXPORT KisOpenRasterStackSaveVisitor : public KisNodeVisitor
+class KisOpenRasterStackSaveVisitor : public KisNodeVisitor
{
public:
KisOpenRasterStackSaveVisitor(KisOpenRasterSaveContext*, vKisNodeSP activeNodes);
virtual ~KisOpenRasterStackSaveVisitor();
using KisNodeVisitor::visit;
public:
bool visit(KisPaintLayer *layer);
bool visit(KisGroupLayer *layer);
bool visit(KisAdjustmentLayer *layer);
bool visit(KisGeneratorLayer * layer);
bool visit(KisNode*) {
return true;
}
bool visit(KisCloneLayer*);
bool visit(KisFilterMask*) {
return true;
}
bool visit(KisTransformMask*) {
return true;
}
bool visit(KisTransparencyMask*) {
return true;
}
bool visit(KisSelectionMask*) {
return true;
}
bool visit(KisColorizeMask*) {
return true;
}
bool visit(KisExternalLayer*);
private:
bool saveLayer(KisLayer *layer);
void saveLayerInfo(QDomElement& elt, KisLayer* layer);
struct Private;
Private* const d;
};
#endif // KIS_LAYER_VISITOR_H_
diff --git a/plugins/impex/ora/krita_ora_export.json b/plugins/impex/ora/krita_ora_export.json
index b05d8d5377..695df98af4 100644
--- a/plugins/impex/ora/krita_ora_export.json
+++ b/plugins/impex/ora/krita_ora_export.json
@@ -1,14 +1,13 @@
{
"Icon": "",
"Id": "Krita ora Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/openraster",
- "X-KDE-Import": "application/x-krita",
"X-KDE-Library": "kritaoraexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "ora"
}
diff --git a/plugins/impex/ora/krita_ora_import.json b/plugins/impex/ora/krita_ora_import.json
index a2d4a69348..bb299e0992 100644
--- a/plugins/impex/ora/krita_ora_import.json
+++ b/plugins/impex/ora/krita_ora_import.json
@@ -1,14 +1,13 @@
{
"Icon": "",
"Id": "Krita ora Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
"X-KDE-Import": "image/openraster",
"X-KDE-Library": "kritaoraimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "ora"
}
diff --git a/plugins/impex/ora/ora_converter.cc b/plugins/impex/ora/ora_converter.cc
index 9ed7087369..2965e819de 100644
--- a/plugins/impex/ora/ora_converter.cc
+++ b/plugins/impex/ora/ora_converter.cc
@@ -1,121 +1,121 @@
/*
* 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 "ora_converter.h"
#include <QApplication>
#include <QFileInfo>
#include <KoStore.h>
#include <KoStoreDevice.h>
#include <KoColorSpaceRegistry.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_open_raster_stack_load_visitor.h>
#include <kis_open_raster_stack_save_visitor.h>
#include <kis_paint_layer.h>
#include "kis_png_converter.h"
#include "ora_load_context.h"
#include "ora_save_context.h"
OraConverter::OraConverter(KisDocument *doc)
: m_doc(doc)
, m_stop(false)
{
}
OraConverter::~OraConverter()
{
}
-KisImageBuilder_Result OraConverter::buildImage(const QString &filename)
+KisImageBuilder_Result OraConverter::buildImage(QIODevice *io)
{
- KoStore* store = KoStore::createStore(filename, KoStore::Read, "image/openraster", KoStore::Zip);
+ KoStore* store = KoStore::createStore(io, KoStore::Read, "image/openraster", KoStore::Zip);
if (!store) {
delete store;
return KisImageBuilder_RESULT_FAILURE;
}
OraLoadContext olc(store);
KisOpenRasterStackLoadVisitor orslv(m_doc->createUndoStore(), &olc);
orslv.loadImage();
m_image = orslv.image();
m_activeNodes = orslv.activeNodes();
delete store;
return KisImageBuilder_RESULT_OK;
}
-KisImageWSP OraConverter::image()
+KisImageSP OraConverter::image()
{
return m_image;
}
vKisNodeSP OraConverter::activeNodes()
{
return m_activeNodes;
}
-KisImageBuilder_Result OraConverter::buildFile(const QString &filename, KisImageWSP image, vKisNodeSP activeNodes)
+KisImageBuilder_Result OraConverter::buildFile(QIODevice *io, KisImageSP image, vKisNodeSP activeNodes)
{
// Open file for writing
- KoStore* store = KoStore::createStore(filename, KoStore::Write, "image/openraster", KoStore::Zip);
+ KoStore* store = KoStore::createStore(io, KoStore::Write, "image/openraster", KoStore::Zip);
if (!store) {
return KisImageBuilder_RESULT_FAILURE;
}
OraSaveContext osc(store);
KisOpenRasterStackSaveVisitor orssv(&osc, activeNodes);
image->rootLayer()->accept(orssv);
if (store->open("Thumbnails/thumbnail.png")) {
QSize previewSize = image->bounds().size();
previewSize.scale(QSize(256,256), Qt::KeepAspectRatio);
QImage preview = image->convertToQImage(previewSize, 0);
KoStoreDevice io(store);
if (io.open(QIODevice::WriteOnly)) {
preview.save(&io, "PNG");
}
io.close();
store->close();
}
KisPaintDeviceSP dev = image->projection();
if (!KisPNGConverter::isColorSpaceSupported(dev->colorSpace())) {
dev = new KisPaintDevice(*dev.data());
KUndo2Command *cmd = dev->convertTo(KoColorSpaceRegistry::instance()->rgb8());
delete cmd;
}
KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store);
delete store;
return KisImageBuilder_RESULT_OK;
}
void OraConverter::cancel()
{
m_stop = true;
}
diff --git a/plugins/impex/ora/ora_converter.h b/plugins/impex/ora/ora_converter.h
index 037e0d36fc..e86599129d 100644
--- a/plugins/impex/ora/ora_converter.h
+++ b/plugins/impex/ora/ora_converter.h
@@ -1,53 +1,53 @@
/*
* 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.
*/
#ifndef _ORA_CONVERTER_H_
#define _ORA_CONVERTER_H_
#include <stdio.h>
#include <QObject>
#include "kis_png_converter.h"
#include "kis_types.h"
class KisDocument;
class OraConverter : public QObject
{
Q_OBJECT
public:
OraConverter(KisDocument *doc);
virtual ~OraConverter();
public:
- KisImageBuilder_Result buildImage(const QString &filename);
- KisImageBuilder_Result buildFile(const QString &filename, KisImageWSP image, vKisNodeSP activeNodes);
+ KisImageBuilder_Result buildImage(QIODevice *io);
+ KisImageBuilder_Result buildFile(QIODevice *io, KisImageSP image, vKisNodeSP activeNodes);
/**
* Retrieve the constructed image
*/
- KisImageWSP image();
+ KisImageSP image();
vKisNodeSP activeNodes();
public Q_SLOTS:
virtual void cancel();
private:
- KisImageWSP m_image;
+ KisImageSP m_image;
KisDocument *m_doc;
vKisNodeSP m_activeNodes;
bool m_stop;
};
#endif
diff --git a/plugins/impex/ora/ora_export.cc b/plugins/impex/ora/ora_export.cc
index bcdaa2af46..5d9593fe87 100644
--- a/plugins/impex/ora/ora_export.cc
+++ b/plugins/impex/ora/ora_export.cc
@@ -1,129 +1,112 @@
/*
* 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 "ora_export.h"
#include <QCheckBox>
#include <QSlider>
-#include <QMessageBox>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <QApplication>
-#include <KisFilterChain.h>
#include <KisImportExportManager.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
+#include <KisExportCheckRegistry.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_node.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_shape_layer.h>
#include <KoProperties.h>
#include "ora_converter.h"
class KisExternalLayer;
K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_ora_export.json", registerPlugin<OraExport>();)
OraExport::OraExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
OraExport::~OraExport()
{
}
bool hasShapeLayerChild(KisNodeSP node)
{
if (!node) return false;
Q_FOREACH (KisNodeSP child, node->childNodes(QStringList(), KoProperties())) {
if (child->inherits("KisShapeLayer")
|| child->inherits("KisGeneratorLayer")
|| child->inherits("KisCloneLayer")) {
return true;
}
else {
if (hasShapeLayerChild(child)) {
return true;
}
}
}
return false;
}
-KisImportExportFilter::ConversionStatus OraExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus OraExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
+ KisImageSP image = document->image();
- dbgFile << "ORA export! From:" << from << ", To:" << to << "";
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- if (!input) {
- return KisImportExportFilter::NoDocumentCreated;
- }
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- KisImageWSP image = input->image();
Q_CHECK_PTR(image);
KisPaintDeviceSP pd = image->projection();
- QStringList supportedColorModelIds;
- supportedColorModelIds << RGBAColorModelID.id() << GrayAColorModelID.id() << GrayColorModelID.id();
- QStringList supportedColorDepthIds;
- supportedColorDepthIds << Integer8BitsColorDepthID.id() << Integer16BitsColorDepthID.id();
- if (!supportedColorModelIds.contains(pd->colorSpace()->colorModelId().id()) ||
- !supportedColorDepthIds.contains(pd->colorSpace()->colorDepthId().id())) {
- if (!getBatchMode()) {
- QMessageBox::critical(0, i18nc("@title:window", "Krita OpenRaster Export"), i18n("Cannot export images in this colorspace or channel depth to OpenRaster"));
- }
- return KisImportExportFilter::UsageError;
- }
-
-
- if (hasShapeLayerChild(image->root()) && !getBatchMode()) {
- QMessageBox::information(0,
- i18nc("@title:window", "Krita:Warning"),
- i18n("This image contains vector, clone or fill layers.\nThese layers will be saved as raster layers."));
- }
-
- OraConverter kpc(input);
+ OraConverter oraConverter(document);
KisImageBuilder_Result res;
- if ((res = kpc.buildFile(filename, image, input->activeNodes())) == KisImageBuilder_RESULT_OK) {
+ if ((res = oraConverter.buildFile(io, image, document->activeNodes())) == KisImageBuilder_RESULT_OK) {
dbgFile << "success !";
return KisImportExportFilter::OK;
}
dbgFile << " Result =" << res;
return KisImportExportFilter::InternalError;
}
+void OraExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("NodeTypeCheck/KisGroupLayer")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("NodeTypeCheck/KisAdjustmentLayer")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("ColorModelHomogenousCheck")->create(KisExportCheckBase::SUPPORTED));
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer16BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "OpenRaster");
+}
+
+
+
#include <ora_export.moc>
diff --git a/plugins/impex/ora/ora_export.h b/plugins/impex/ora/ora_export.h
index e34b28409a..1e15e46563 100644
--- a/plugins/impex/ora/ora_export.h
+++ b/plugins/impex/ora/ora_export.h
@@ -1,35 +1,36 @@
/*
* 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.
*/
#ifndef _ORA_EXPORT_H_
#define _ORA_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class OraExport : public KisImportExportFilter
{
Q_OBJECT
public:
OraExport(QObject *parent, const QVariantList &);
virtual ~OraExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/ora/ora_import.cc b/plugins/impex/ora/ora_import.cc
index 6f73b2a4b9..635b88fe2d 100644
--- a/plugins/impex/ora/ora_import.cc
+++ b/plugins/impex/ora/ora_import.cc
@@ -1,99 +1,76 @@
/*
* 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 "ora_import.h"
#include <kpluginfactory.h>
#include <QFileInfo>
-#include <KisFilterChain.h>
-
#include <KisDocument.h>
#include <kis_image.h>
#include "ora_converter.h"
K_PLUGIN_FACTORY_WITH_JSON(ImportFactory, "krita_ora_import.json", registerPlugin<OraImport>();)
OraImport::OraImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
OraImport::~OraImport()
{
}
-KisImportExportFilter::ConversionStatus OraImport::convert(const QByteArray&, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus OraImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile << "Importing using ORAImport!";
-
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- doc->prepareForImport();
+ OraConverter oraConverter(document);
- if (!filename.isEmpty()) {
- if (!QFileInfo(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
+ switch (oraConverter.buildImage(io)) {
+ case KisImageBuilder_RESULT_UNSUPPORTED:
+ return KisImportExportFilter::NotImplemented;
+ break;
+ case KisImageBuilder_RESULT_INVALID_ARG:
+ return KisImportExportFilter::BadMimeType;
+ break;
+ case KisImageBuilder_RESULT_NO_URI:
+ case KisImageBuilder_RESULT_NOT_LOCAL:
+ return KisImportExportFilter::FileNotFound;
+ break;
+ case KisImageBuilder_RESULT_BAD_FETCH:
+ case KisImageBuilder_RESULT_EMPTY:
+ return KisImportExportFilter::ParsingError;
+ break;
+ case KisImageBuilder_RESULT_FAILURE:
+ return KisImportExportFilter::InternalError;
+ break;
+ case KisImageBuilder_RESULT_OK:
+ document->setCurrentImage(oraConverter.image());
+ if (oraConverter.activeNodes().size() > 0) {
+ document->setPreActivatedNode(oraConverter.activeNodes()[0]);
}
+ return KisImportExportFilter::OK;
+ default:
+ break;
+ }
- OraConverter ib(doc);
-
-
- switch (ib.buildImage(filename)) {
- case KisImageBuilder_RESULT_UNSUPPORTED:
- return KisImportExportFilter::NotImplemented;
- break;
- case KisImageBuilder_RESULT_INVALID_ARG:
- return KisImportExportFilter::BadMimeType;
- break;
- case KisImageBuilder_RESULT_NO_URI:
- case KisImageBuilder_RESULT_NOT_LOCAL:
- return KisImportExportFilter::FileNotFound;
- break;
- case KisImageBuilder_RESULT_BAD_FETCH:
- case KisImageBuilder_RESULT_EMPTY:
- return KisImportExportFilter::ParsingError;
- break;
- case KisImageBuilder_RESULT_FAILURE:
- return KisImportExportFilter::InternalError;
- break;
- case KisImageBuilder_RESULT_OK:
- doc->setCurrentImage(ib.image());
- if (ib.activeNodes().size() > 0) {
- doc->setPreActivatedNode(ib.activeNodes()[0]);
- }
- return KisImportExportFilter::OK;
- default:
- break;
- }
- }
- return KisImportExportFilter::StorageCreationError;
+ return KisImportExportFilter::InternalError;
}
#include <ora_import.moc>
diff --git a/plugins/impex/ora/ora_import.h b/plugins/impex/ora/ora_import.h
index 59593126f3..ed27b63519 100644
--- a/plugins/impex/ora/ora_import.h
+++ b/plugins/impex/ora/ora_import.h
@@ -1,35 +1,35 @@
/*
* 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.
*/
#ifndef ORA_IMPORT_H_
#define ORA_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class OraImport : public KisImportExportFilter
{
Q_OBJECT
public:
OraImport(QObject *parent, const QVariantList &);
virtual ~OraImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/libs/ui/ora/ora_load_context.cc b/plugins/impex/ora/ora_load_context.cc
similarity index 96%
rename from libs/ui/ora/ora_load_context.cc
rename to plugins/impex/ora/ora_load_context.cc
index 66f80f89c3..b6c1b6c8a8 100644
--- a/libs/ui/ora/ora_load_context.cc
+++ b/plugins/impex/ora/ora_load_context.cc
@@ -1,67 +1,67 @@
/*
* 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 "ora_load_context.h"
#include <QDomDocument>
#include <KoStore.h>
#include <KoStoreDevice.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include "kis_png_converter.h"
OraLoadContext::OraLoadContext(KoStore* _store) : m_store(_store)
{
}
OraLoadContext::~OraLoadContext()
{
}
-KisImageWSP OraLoadContext::loadDeviceData(const QString & filename)
+KisImageSP OraLoadContext::loadDeviceData(const QString & filename)
{
if (m_store->open(filename)) {
KoStoreDevice io(m_store);
if (!io.open(QIODevice::ReadOnly)) {
dbgFile << "Could not open for reading:" << filename;
return 0;
}
KisPNGConverter pngConv(0);
pngConv.buildImage(&io);
io.close();
m_store->close();
return pngConv.image();
}
return 0;
}
QDomDocument OraLoadContext::loadStack()
{
m_store->open("stack.xml");
KoStoreDevice io(m_store);
QDomDocument doc;
doc.setContent(&io, false);
io.close();
m_store->close();
return doc;
}
diff --git a/libs/ui/ora/ora_load_context.h b/plugins/impex/ora/ora_load_context.h
similarity index 86%
rename from libs/ui/ora/ora_load_context.h
rename to plugins/impex/ora/ora_load_context.h
index 96bce2939c..fde5f7140c 100644
--- a/libs/ui/ora/ora_load_context.h
+++ b/plugins/impex/ora/ora_load_context.h
@@ -1,40 +1,39 @@
/*
* 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.
*/
#ifndef _ORA_LOAD_CONTEXT_H_
#define _ORA_LOAD_CONTEXT_H_
#include <kis_open_raster_load_context.h>
-#include <kritaui_export.h>
class KoStore;
-class KRITAUI_EXPORT OraLoadContext : public KisOpenRasterLoadContext
+class OraLoadContext : public KisOpenRasterLoadContext
{
public:
OraLoadContext(KoStore* _store);
virtual ~OraLoadContext();
- virtual KisImageWSP loadDeviceData(const QString & fileName);
+ virtual KisImageSP loadDeviceData(const QString & fileName);
virtual QDomDocument loadStack();
private:
KoStore* m_store;
};
#endif
diff --git a/libs/ui/ora/ora_save_context.cc b/plugins/impex/ora/ora_save_context.cc
similarity index 85%
rename from libs/ui/ora/ora_save_context.cc
rename to plugins/impex/ora/ora_save_context.cc
index 37caf9092f..3a624b6563 100644
--- a/libs/ui/ora/ora_save_context.cc
+++ b/plugins/impex/ora/ora_save_context.cc
@@ -1,58 +1,66 @@
/*
* 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 "ora_save_context.h"
#include <QDomDocument>
#include <KoStore.h>
#include <KoStoreDevice.h>
-
+#include <KoColorSpaceRegistry.h>
+#include <kundo2command.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_image.h>
#include <metadata/kis_meta_data_store.h>
#include "kis_png_converter.h"
OraSaveContext::OraSaveContext(KoStore* _store) : m_id(0), m_store(_store)
{
}
QString OraSaveContext::saveDeviceData(KisPaintDeviceSP dev, KisMetaData::Store* metaData, const QRect &imageRect, const qreal xRes, const qreal yRes)
{
QString filename = QString("data/layer%1.png").arg(m_id++);
+
+ if (!KisPNGConverter::isColorSpaceSupported(dev->colorSpace())) {
+ dev = new KisPaintDevice(*dev.data());
+ KUndo2Command *cmd = dev->convertTo(KoColorSpaceRegistry::instance()->rgb8());
+ delete cmd;
+ }
+
if (KisPNGConverter::saveDeviceToStore(filename, imageRect, xRes, yRes, dev, m_store, metaData)) {
return filename;
}
return "";
}
void OraSaveContext::saveStack(const QDomDocument& doc)
{
if (m_store->open("stack.xml")) {
KoStoreDevice io(m_store);
io.write(doc.toByteArray());
io.close();
m_store->close();
} else {
dbgFile << "Opening of the stack.xml file failed :";
}
}
diff --git a/libs/ui/ora/ora_save_context.h b/plugins/impex/ora/ora_save_context.h
similarity index 92%
rename from libs/ui/ora/ora_save_context.h
rename to plugins/impex/ora/ora_save_context.h
index 3baa8de0a2..d7cd62b7cf 100644
--- a/libs/ui/ora/ora_save_context.h
+++ b/plugins/impex/ora/ora_save_context.h
@@ -1,38 +1,38 @@
/*
* 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.
*/
#ifndef _ORA_SAVE_CONTEXT_H_
#define _ORA_SAVE_CONTEXT_H_
class KoStore;
#include <metadata/kis_meta_data_entry.h>
#include "kis_open_raster_save_context.h"
-#include <kritaui_export.h>
-class KRITAUI_EXPORT OraSaveContext : public KisOpenRasterSaveContext
+class OraSaveContext : public KisOpenRasterSaveContext
{
public:
OraSaveContext(KoStore* _store);
+ virtual ~OraSaveContext(){}
virtual QString saveDeviceData(KisPaintDeviceSP dev, KisMetaData::Store *metaData, const QRect &imageRect, const qreal xRes, const qreal yRes);
virtual void saveStack(const QDomDocument& doc);
private:
int m_id;
KoStore* m_store;
};
#endif
diff --git a/plugins/impex/pdf/kis_pdf_import.cpp b/plugins/impex/pdf/kis_pdf_import.cpp
index 822d177d18..99539311fe 100644
--- a/plugins/impex/pdf/kis_pdf_import.cpp
+++ b/plugins/impex/pdf/kis_pdf_import.cpp
@@ -1,165 +1,143 @@
/*
* 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_pdf_import.h"
// poppler's headers
#include <poppler-qt5.h>
// Qt's headers
#include <QFile>
#include <QImage>
#include <QRadioButton>
#include <QApplication>
#include <QFileInfo>
// KDE's headers
#include <kis_debug.h>
#include <kis_paint_device.h>
#include <KoDialog.h>
#include <kpluginfactory.h>
#include <kpassworddialog.h>
#include <QFileInfo>
// calligra's headers
-#include <KisFilterChain.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoProgressUpdater.h>
#include <KoUpdater.h>
// krita's headers
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_transaction.h>
// plugins's headers
#include "kis_pdf_import_widget.h"
K_PLUGIN_FACTORY_WITH_JSON(PDFImportFactory, "krita_pdf_import.json",
registerPlugin<KisPDFImport>();)
KisPDFImport::KisPDFImport(QObject *parent, const QVariantList &)
: KisImportExportFilter(parent)
{
}
KisPDFImport::~KisPDFImport()
{
}
-KisPDFImport::ConversionStatus KisPDFImport::convert(const QByteArray& , const QByteArray&, KisPropertiesConfigurationSP configuration)
+KisPDFImport::ConversionStatus KisPDFImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
+ Poppler::Document* pdoc = Poppler::Document::loadFromData(io->readAll());
- QString filename = inputFile();
- dbgFile << "Importing using PDFImport!" << filename;
-
- if (filename.isEmpty()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- QFileInfo fi(filename);
- if (!fi.exists()) {
- return KisImportExportFilter::FileNotFound;
- }
- Poppler::Document* pdoc = Poppler::Document::load(filename);
if (!pdoc) {
return KisPDFImport::InvalidFormat;
}
pdoc->setRenderHint(Poppler::Document::Antialiasing, true);
pdoc->setRenderHint(Poppler::Document::TextAntialiasing, true);
if (!pdoc) {
dbgFile << "Error when reading the PDF";
return KisImportExportFilter::StorageCreationError;
}
while (pdoc->isLocked()) {
KPasswordDialog dlg(0);
dlg.setPrompt(i18n("A password is required to read that pdf"));
dlg.setWindowTitle(i18n("A password is required to read that pdf"));
if (dlg.exec() != QDialog::Accepted) {
dbgFile << "Password canceled";
return KisImportExportFilter::StorageCreationError;
} else
pdoc->unlock(dlg.password().toLocal8Bit(), dlg.password().toLocal8Bit());
}
KoDialog* kdb = new KoDialog(0);
kdb->setCaption(i18n("PDF Import Options"));
kdb->setModal(false);
KisPDFImportWidget* wdg = new KisPDFImportWidget(pdoc, kdb);
kdb->setMainWidget(wdg);
QApplication::restoreOverrideCursor();
if (kdb->exec() == QDialog::Rejected) {
delete pdoc;
delete kdb;
return KisImportExportFilter::StorageCreationError; // FIXME Cancel doesn't exist :(
}
- // Init kis's doc
- KisDocument * doc = outputDocument();
- if (!doc) {
- delete pdoc;
- delete kdb;
- return KisImportExportFilter::NoDocumentCreated;
- }
-
- doc -> prepareForImport();
// Create the krita image
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
int width = wdg->intWidth->value();
int height = wdg->intHeight->value();
- KisImageWSP image = new KisImage(doc->createUndoStore(), width, height, cs, "built image");
+ KisImageSP image = new KisImage(document->createUndoStore(), width, height, cs, "built image");
image->setResolution(wdg->intResolution->value() / 72.0, wdg->intResolution->value() / 72.0);
// create a layer
QList<int> pages = wdg->pages();
- QPointer<KoUpdater> loadUpdater = outputDocument()->progressUpdater()->startSubtask(1, "load");
+ QPointer<KoUpdater> loadUpdater = document->progressUpdater()->startSubtask(1, "load");
loadUpdater->setRange(0, pages.count());
for (QList<int>::const_iterator it = pages.constBegin(); it != pages.constEnd(); ++it) {
KisPaintLayer* layer = new KisPaintLayer(image.data(),
i18n("Page %1", *it + 1),
quint8_MAX);
Poppler::Page* page = pdoc->page(*it);
QImage img = page->renderToImage(wdg->intResolution->value(), wdg->intResolution->value(), 0, 0, width, height);
layer->paintDevice()->convertFromQImage(img, 0, 0, 0);
delete page;
image->addNode(layer, image->rootLayer(), 0);
loadUpdater->setProgress(*it + 1);
}
- doc->setCurrentImage(image);
+ document->setCurrentImage(image);
delete pdoc;
delete kdb;
return KisImportExportFilter::OK;
}
#include "kis_pdf_import.moc"
diff --git a/plugins/impex/pdf/kis_pdf_import.h b/plugins/impex/pdf/kis_pdf_import.h
index e1b86f2cb9..df387515ff 100644
--- a/plugins/impex/pdf/kis_pdf_import.h
+++ b/plugins/impex/pdf/kis_pdf_import.h
@@ -1,37 +1,37 @@
/*
* 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.
*/
#ifndef KIS_PDF_IMPORT_H
#define KIS_PDF_IMPORT_H
#include <QVariant>
#include <KisImportExportFilter.h>
class KisPDFImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisPDFImport(QObject *parent, const QVariantList &);
virtual ~KisPDFImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/pdf/krita_pdf_import.json b/plugins/impex/pdf/krita_pdf_import.json
index 5f446a4782..ede8ec8f32 100644
--- a/plugins/impex/pdf/krita_pdf_import.json
+++ b/plugins/impex/pdf/krita_pdf_import.json
@@ -1,13 +1,13 @@
{
"Id": "Krita PDF Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "application/pdf",
"X-KDE-Library": "kritapdfimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "pdf"
}
diff --git a/plugins/impex/png/CMakeLists.txt b/plugins/impex/png/CMakeLists.txt
index 7502f9096d..197c523a1f 100644
--- a/plugins/impex/png/CMakeLists.txt
+++ b/plugins/impex/png/CMakeLists.txt
@@ -1,29 +1,29 @@
add_subdirectory(tests)
set(kritapngimport_SOURCES
kis_png_import.cc
)
add_library(kritapngimport MODULE ${kritapngimport_SOURCES})
include_directories(SYSTEM ${PNG_INCLUDE_DIR})
add_definitions(${PNG_DEFINITIONS} ${KDE4_ENABLE_EXCEPTIONS})
target_link_libraries(kritapngimport kritaui ${PNG_LIBRARIES} )
install(TARGETS kritapngimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritapngexport_SOURCES
kis_png_export.cc
)
ki18n_wrap_ui(kritapngexport_SOURCES kis_wdg_options_png.ui )
add_library(kritapngexport MODULE ${kritapngexport_SOURCES})
-target_link_libraries(kritapngexport kritaui ${PNG_LIBRARIES})
+target_link_libraries(kritapngexport kritaui kritaimpex ${PNG_LIBRARIES})
install(TARGETS kritapngexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_png.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/png/kis_png_export.cc b/plugins/impex/png/kis_png_export.cc
index 1f02ad2fab..18ce55610f 100644
--- a/plugins/impex/png/kis_png_export.cc
+++ b/plugins/impex/png/kis_png_export.cc
@@ -1,293 +1,227 @@
/*
* 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_png_export.h"
#include <QCheckBox>
#include <QSlider>
#include <QApplication>
-#include <KoDialog.h>
#include <kpluginfactory.h>
-#include <QMessageBox>
#include <KoColorSpace.h>
-#include <KisFilterChain.h>
#include <KisImportExportManager.h>
#include <KoColorProfile.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpaceRegistry.h>
+#include <KisExportCheckRegistry.h>
+
#include <kis_properties_configuration.h>
#include <kis_paint_device.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_config.h>
#include <kis_properties_configuration.h>
#include <metadata/kis_meta_data_store.h>
#include <metadata/kis_meta_data_filter_registry_model.h>
#include <metadata/kis_exif_info_visitor.h>
#include "kis_png_converter.h"
#include <kis_iterator_ng.h>
K_PLUGIN_FACTORY_WITH_JSON(KisPNGExportFactory, "krita_png_export.json", registerPlugin<KisPNGExport>();)
KisPNGExport::KisPNGExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisPNGExport::~KisPNGExport()
{
}
bool hasVisibleWidgets()
{
QWidgetList wl = QApplication::allWidgets();
Q_FOREACH (QWidget* w, wl) {
if (w->isVisible() && strcmp(w->metaObject()->className(), "QDesktopWidget")) {
dbgFile << "Widget " << w << " " << w->objectName() << " " << w->metaObject()->className() << " is visible";
return true;
}
}
return false;
}
-KisImportExportFilter::ConversionStatus KisPNGExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisPNGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
- dbgFile << "Png export! From:" << from << ", To:" << to << "";
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
-
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
-
-
- KisImageWSP image = input->image();
-
- // the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(image->locked());
- KisPaintDeviceSP pd;
- pd = new KisPaintDevice(*image->projection());
- KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
-
-
- if (!KisPNGConverter::isColorSpaceSupported(pd->colorSpace())) {
- if (!getBatchMode()) {
- QMessageBox::critical(0, i18nc("@title:window", "Krita PNG Export"), i18n("You can only save grayscale and RGB images to PNG. Convert your image before exporting to PNG."));
- }
- return KisImportExportFilter::UsageError;
- }
-
-
- KisSequentialConstIterator it(l->paintDevice(), image->bounds());
- const KoColorSpace* cs = l->paintDevice()->colorSpace();
+ KisImageSP image = document->image();
KisPNGOptions options;
- bool isThereAlpha = false;
- KisPropertiesConfigurationSP cfg = defaultConfiguration();
- do {
- if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) {
- isThereAlpha = true;
- break;
- }
- } while (it.nextPixel());
-
- if (!qApp->applicationName().toLower().contains("test")) {
- KoDialog kdb;
- kdb.setCaption(i18n("PNG Export Options"));
- kdb.setButtons(KoDialog::Ok | KoDialog::Cancel);
- KisConfigWidget *wdg = createConfigurationWidget(&kdb, from, to);
- kdb.setMainWidget(wdg);
-
- // If a configuration object was passed to the convert method, we use that, otherwise we load from the settings
- if (configuration) {
- cfg->fromXML(configuration->toXML());
- }
- else {
- cfg = lastSavedConfiguration(from, to);
- }
-
- cfg->setProperty("ColorModelID", cs->colorModelId().id());
-
- bool sRGB = (cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive)
- && !cs->profile()->name().contains(QLatin1String("g10")));
- cfg->setProperty("sRGB", sRGB);
- cfg->setProperty("isThereAlpha", isThereAlpha);
- wdg->setConfiguration(cfg);
-
- QApplication::restoreOverrideCursor();
- if (hasVisibleWidgets()) {
- if (!getBatchMode()) {
- if (kdb.exec() == QDialog::Rejected) {
- return KisImportExportFilter::UserCancelled;
- }
- cfg = wdg->configuration();
- KisConfig().setExportConfiguration("PNG", *cfg.data());
-
- }
- }
- }
-
- options.alpha = cfg->getBool("alpha", true);
- options.interlace = cfg->getBool("interlaced", false);
- options.compression = cfg->getInt("compression", 0);
- options.tryToSaveAsIndexed = cfg->getBool("indexed", false);
- options.transparencyFillColor = cfg->getColor("transparencyFillColor").toQColor();
- options.saveSRGBProfile = cfg->getBool("saveSRGBProfile", false);
- options.forceSRGB = cfg->getBool("forceSRGB", true);
-
- KisPNGConverter kpc(input);
+ options.alpha = configuration->getBool("alpha", true);
+ options.interlace = configuration->getBool("interlaced", false);
+ options.compression = configuration->getInt("compression", 0);
+ options.tryToSaveAsIndexed = configuration->getBool("indexed", false);
+ options.transparencyFillColor = configuration->getColor("transparencyFillColor").toQColor();
+ options.saveSRGBProfile = configuration->getBool("saveSRGBProfile", false);
+ options.forceSRGB = configuration->getBool("forceSRGB", true);
vKisAnnotationSP_it beginIt = image->beginAnnotations();
vKisAnnotationSP_it endIt = image->endAnnotations();
- KisImageBuilder_Result res;
-
KisExifInfoVisitor eIV;
eIV.visit(image->rootLayer().data());
- KisMetaData::Store* eI = 0;
- if (eIV.countPaintLayer() == 1)
+ KisMetaData::Store *eI = 0;
+ if (eIV.countPaintLayer() == 1) {
eI = eIV.exifInfo();
+ }
if (eI) {
KisMetaData::Store* copy = new KisMetaData::Store(*eI);
eI = copy;
}
- if ((res = kpc.buildFile(filename, image->bounds(), image->xRes(), image->yRes(), l->paintDevice(), beginIt, endIt, options, eI)) == KisImageBuilder_RESULT_OK) {
- dbgFile << "success !";
+
+ KisPNGConverter pngConverter(document);
+
+ KisImageBuilder_Result res = pngConverter.buildFile(io, image->bounds(), image->xRes(), image->yRes(), image->projection(), beginIt, endIt, options, eI);
+
+ if (res == KisImageBuilder_RESULT_OK) {
delete eI;
return KisImportExportFilter::OK;
}
+
delete eI;
dbgFile << " Result =" << res;
return KisImportExportFilter::InternalError;
}
KisPropertiesConfigurationSP KisPNGExport::defaultConfiguration(const QByteArray &, const QByteArray &) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("alpha", true);
cfg->setProperty("indexed", false);
cfg->setProperty("compression", 9);
cfg->setProperty("interlaced", false);
cfg->setProperty("transparencyFillcolor", QString("255,255,255"));
cfg->setProperty("saveSRGBProfile", false);
cfg->setProperty("forceSRGB", true);
return cfg;
}
KisPropertiesConfigurationSP KisPNGExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
{
QString filterConfig = KisConfig().exportConfiguration("PNG");
KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
cfg->fromXML(filterConfig, false);
return cfg;
}
KisConfigWidget *KisPNGExport::createConfigurationWidget(QWidget *parent, const QByteArray &, const QByteArray &) const
{
return new KisWdgOptionsPNG(parent);
}
+void KisPNGExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer16BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "PNG");
+}
+
void KisWdgOptionsPNG::setConfiguration(const KisPropertiesConfigurationSP cfg)
{
bool isThereAlpha = cfg->getBool("isThereAlpha");
alpha->setChecked(cfg->getBool("alpha", isThereAlpha));
if (cfg->getString("ColorModelID") == RGBAColorModelID.id()) {
tryToSaveAsIndexed->setVisible(true);
if (alpha->isChecked()) {
tryToSaveAsIndexed->setChecked(false);
}
else {
tryToSaveAsIndexed->setChecked(cfg->getBool("indexed", false));
}
}
else {
tryToSaveAsIndexed->setVisible(false);
}
interlacing->setChecked(cfg->getBool("interlaced", false));
compressionLevel->setValue(cfg->getInt("compression", 9));
compressionLevel->setRange(1, 9 , 0);
alpha->setEnabled(isThereAlpha);
tryToSaveAsIndexed->setVisible(!isThereAlpha);
bnTransparencyFillColor->setEnabled(!alpha->isChecked());
bool sRGB = cfg->getBool("sRGB", false);
chkSRGB->setEnabled(sRGB);
chkSRGB->setChecked(cfg->getBool("saveSRGBProfile", true));
chkForceSRGB->setEnabled(!sRGB);
chkForceSRGB->setChecked(cfg->getBool("forceSRGB", false));
QStringList rgb = cfg->getString("transparencyFillcolor", "0,0,0").split(',');
KoColor c(KoColorSpaceRegistry::instance()->rgb8());
c.fromQColor(Qt::white);
bnTransparencyFillColor->setDefaultColor(c);
c.fromQColor(QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()));
bnTransparencyFillColor->setColor(c);
}
KisPropertiesConfigurationSP KisWdgOptionsPNG::configuration() const
{
KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
bool alpha = this->alpha->isChecked();
bool interlace = interlacing->isChecked();
int compression = (int)compressionLevel->value();
bool tryToSaveAsIndexed = this->tryToSaveAsIndexed->isChecked();
QColor c = bnTransparencyFillColor->color().toQColor();
bool saveSRGB = chkSRGB->isChecked();
bool forceSRGB = chkForceSRGB->isChecked();
cfg->setProperty("alpha", alpha);
cfg->setProperty("indexed", tryToSaveAsIndexed);
cfg->setProperty("compression", compression);
cfg->setProperty("interlaced", interlace);
cfg->setProperty("transparencyFillcolor", QString("%1,%2,%3").arg(c.red()).arg(c.green()).arg(c.blue()));
cfg->setProperty("saveSRGBProfile", saveSRGB);
cfg->setProperty("forceSRGB", forceSRGB);
return cfg;
}
void KisWdgOptionsPNG::on_alpha_toggled(bool checked)
{
bnTransparencyFillColor->setEnabled(!checked);
}
#include "kis_png_export.moc"
diff --git a/plugins/impex/png/kis_png_export.h b/plugins/impex/png/kis_png_export.h
index e4e15e4eaf..8fcddb1f1d 100644
--- a/plugins/impex/png/kis_png_export.h
+++ b/plugins/impex/png/kis_png_export.h
@@ -1,62 +1,63 @@
/*
* 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.
*/
#ifndef _KIS_PNG_EXPORT_H_
#define _KIS_PNG_EXPORT_H_
#include "ui_kis_wdg_options_png.h"
#include <KisImportExportFilter.h>
#include <kis_config_widget.h>
class KisWdgOptionsPNG : public KisConfigWidget, public Ui::KisWdgOptionsPNG
{
Q_OBJECT
public:
KisWdgOptionsPNG(QWidget *parent)
: KisConfigWidget(parent)
{
setupUi(this);
}
void setConfiguration(const KisPropertiesConfigurationSP config);
KisPropertiesConfigurationSP configuration() const;
private Q_SLOTS:
void on_alpha_toggled(bool checked);
};
class KisPNGExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisPNGExport(QObject *parent, const QVariantList &);
virtual ~KisPNGExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const;
KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/png/kis_png_import.cc b/plugins/impex/png/kis_png_import.cc
index 6b13c0babc..b45fcad441 100644
--- a/plugins/impex/png/kis_png_import.cc
+++ b/plugins/impex/png/kis_png_import.cc
@@ -1,105 +1,79 @@
/*
* 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_png_import.h"
#include <kpluginfactory.h>
#include <QFileInfo>
-#include <KisFilterChain.h>
#include <KisImportExportManager.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <KisViewManager.h>
#include "kis_png_converter.h"
K_PLUGIN_FACTORY_WITH_JSON(PNGImportFactory, "krita_png_import.json", registerPlugin<KisPNGImport>();)
KisPNGImport::KisPNGImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisPNGImport::~KisPNGImport()
{
}
-KisImportExportFilter::ConversionStatus KisPNGImport::convert(const QByteArray&, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisPNGImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile << "Importing using PNGImport!";
+ KisPNGConverter ib(document, batchMode());
- if (to != "application/x-krita")
+ switch (ib.buildImage(io)) {
+ case KisImageBuilder_RESULT_UNSUPPORTED:
+ return KisImportExportFilter::NotImplemented;
+ break;
+ case KisImageBuilder_RESULT_INVALID_ARG:
return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- doc -> prepareForImport();
-
- if (!filename.isEmpty()) {
-
- if (!QFileInfo(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- KisPNGConverter ib(doc, getBatchMode());
-
-// if (view != 0)
-// view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true);
-
- switch (ib.buildImage(filename)) {
- case KisImageBuilder_RESULT_UNSUPPORTED:
- return KisImportExportFilter::NotImplemented;
- break;
- case KisImageBuilder_RESULT_INVALID_ARG:
- return KisImportExportFilter::BadMimeType;
- break;
- case KisImageBuilder_RESULT_NO_URI:
- case KisImageBuilder_RESULT_NOT_EXIST:
- case KisImageBuilder_RESULT_NOT_LOCAL:
- return KisImportExportFilter::FileNotFound;
- break;
- case KisImageBuilder_RESULT_BAD_FETCH:
- case KisImageBuilder_RESULT_EMPTY:
- return KisImportExportFilter::ParsingError;
- break;
- case KisImageBuilder_RESULT_FAILURE:
- return KisImportExportFilter::InternalError;
- break;
- case KisImageBuilder_RESULT_OK:
- doc -> setCurrentImage(ib.image());
- return KisImportExportFilter::OK;
- break;
- default:
- break;
- }
-
+ break;
+ case KisImageBuilder_RESULT_NO_URI:
+ case KisImageBuilder_RESULT_NOT_EXIST:
+ case KisImageBuilder_RESULT_NOT_LOCAL:
+ return KisImportExportFilter::FileNotFound;
+ break;
+ case KisImageBuilder_RESULT_BAD_FETCH:
+ case KisImageBuilder_RESULT_EMPTY:
+ return KisImportExportFilter::ParsingError;
+ break;
+ case KisImageBuilder_RESULT_FAILURE:
+ return KisImportExportFilter::InternalError;
+ break;
+ case KisImageBuilder_RESULT_OK:
+ document -> setCurrentImage(ib.image());
+ return KisImportExportFilter::OK;
+ break;
+ default:
+ break;
}
- return KisImportExportFilter::StorageCreationError;
+ return KisImportExportFilter::InternalError;
+
}
#include <kis_png_import.moc>
diff --git a/plugins/impex/png/kis_png_import.h b/plugins/impex/png/kis_png_import.h
index a256ab910c..1e04cef107 100644
--- a/plugins/impex/png/kis_png_import.h
+++ b/plugins/impex/png/kis_png_import.h
@@ -1,36 +1,36 @@
/*
* 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.
*/
#ifndef _KIS_PNG_IMPORT_H_
#define _KIS_PNG_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisPNGImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisPNGImport(QObject *parent, const QVariantList &);
virtual ~KisPNGImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/png/krita_png_export.json b/plugins/impex/png/krita_png_export.json
index 2f21274b2f..53de1a545a 100644
--- a/plugins/impex/png/krita_png_export.json
+++ b/plugins/impex/png/krita_png_export.json
@@ -1,13 +1,13 @@
{
"Id": "Krita PNG Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/png,application/x-krita-paintoppreset",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Library": "kritapngexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "png,kpp"
}
diff --git a/plugins/impex/png/krita_png_import.json b/plugins/impex/png/krita_png_import.json
index 8206ef5117..0b7d6c3422 100644
--- a/plugins/impex/png/krita_png_import.json
+++ b/plugins/impex/png/krita_png_import.json
@@ -1,13 +1,13 @@
{
"Id": "Krita PNG Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/png,application/x-krita-paintoppreset",
"X-KDE-Library": "kritapngimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "png,kpp"
}
diff --git a/plugins/impex/ppm/CMakeLists.txt b/plugins/impex/ppm/CMakeLists.txt
index 4fdbdb6c5e..dc0bac82cb 100644
--- a/plugins/impex/ppm/CMakeLists.txt
+++ b/plugins/impex/ppm/CMakeLists.txt
@@ -1,27 +1,27 @@
add_subdirectory(tests)
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set(kritappmimport_SOURCES
kis_ppm_import.cpp
)
add_library(kritappmimport MODULE ${kritappmimport_SOURCES})
target_link_libraries(kritappmimport kritaui )
install(TARGETS kritappmimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritappmexport_SOURCES
kis_ppm_export.cpp
)
ki18n_wrap_ui(kritappmexport_SOURCES kis_wdg_options_ppm.ui )
add_library(kritappmexport MODULE ${kritappmexport_SOURCES})
-target_link_libraries(kritappmexport kritaui )
+target_link_libraries(kritappmexport kritaui kritaimpex)
install(TARGETS kritappmexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_ppm.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/ppm/kis_ppm_export.cpp b/plugins/impex/ppm/kis_ppm_export.cpp
index c55113cac4..41bdca78cc 100644
--- a/plugins/impex/ppm/kis_ppm_export.cpp
+++ b/plugins/impex/ppm/kis_ppm_export.cpp
@@ -1,318 +1,294 @@
/*
* Copyright (c) 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; 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_ppm_export.h"
#include <kpluginfactory.h>
#include <KoColorSpace.h>
#include <KoColorSpaceConstants.h>
-#include <KisFilterChain.h>
-#include <KisImportExportManager.h>
-#include <KoDialog.h>
+#include <KisImportExportManager.h>
+#include <KisExportCheckRegistry.h>
#include <kis_debug.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include <qendian.h>
#include <KoColorSpaceTraits.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include "kis_iterator_ng.h"
#include <QApplication>
K_PLUGIN_FACTORY_WITH_JSON(KisPPMExportFactory, "krita_ppm_export.json", registerPlugin<KisPPMExport>();)
KisPPMExport::KisPPMExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisPPMExport::~KisPPMExport()
{
}
class KisPPMFlow
{
public:
KisPPMFlow() {
}
virtual ~KisPPMFlow() {
}
virtual void writeBool(quint8 v) = 0;
virtual void writeBool(quint16 v) = 0;
virtual void writeNumber(quint8 v) = 0;
virtual void writeNumber(quint16 v) = 0;
virtual void flush() = 0;
private:
};
class KisPPMAsciiFlow : public KisPPMFlow
{
public:
KisPPMAsciiFlow(QIODevice* device) : m_device(device) {
}
~KisPPMAsciiFlow() override {
}
void writeBool(quint8 v) override {
if (v > 127) {
m_device->write("1 ");
} else {
m_device->write("0 ");
}
}
void writeBool(quint16 v) override {
writeBool(quint8(v >> 8));
}
void writeNumber(quint8 v) override {
m_device->write(QByteArray::number(v));
m_device->write(" ");
}
void writeNumber(quint16 v) override {
m_device->write(QByteArray::number(v));
m_device->write(" ");
}
void flush() override {
}
private:
QIODevice* m_device;
};
class KisPPMBinaryFlow : public KisPPMFlow
{
public:
KisPPMBinaryFlow(QIODevice* device) : m_device(device), m_pos(0), m_current(0) {
}
~KisPPMBinaryFlow() override {
}
void writeBool(quint8 v) override {
m_current = m_current << 1;
m_current |= (v > 127);
++m_pos;
if (m_pos >= 8) {
m_current = 0;
m_pos = 0;
flush();
}
}
void writeBool(quint16 v) override {
writeBool(quint8(v >> 8));
}
void writeNumber(quint8 v) override {
m_device->write((char*)&v, 1);
}
void writeNumber(quint16 v) override {
quint16 vo = qToBigEndian(v);
m_device->write((char*)&vo, 2);
}
void flush() override {
m_device->write((char*)&m_current, 1);
}
private:
QIODevice* m_device;
int m_pos;
quint8 m_current;
};
-KisImportExportFilter::ConversionStatus KisPPMExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisPPMExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
- dbgFile << "PPM export! From:" << from << ", To:" << to << "";
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- KoDialog kdb;
- kdb.setWindowTitle(i18n("PPM Export Options"));
- kdb.setButtons(KoDialog::Ok | KoDialog::Cancel);
- KisConfigWidget *wdg = createConfigurationWidget(&kdb, from, to);
- kdb.setMainWidget(wdg);
- QApplication::restoreOverrideCursor();
-
- // If a configuration object was passed to the convert method, we use that, otherwise we load from the settings
- KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
- if (configuration) {
- cfg->fromXML(configuration->toXML());
- }
- else {
- cfg = lastSavedConfiguration(from, to);
- }
- wdg->setConfiguration(cfg);
-
- if (!getBatchMode()) {
- if (kdb.exec() == QDialog::Rejected) {
- return KisImportExportFilter::UserCancelled;
- }
- cfg = wdg->configuration();
- KisConfig().setExportConfiguration("PPM", *cfg.data());
- }
-
- bool rgb = (to == "image/x-portable-pixmap");
- bool binary = (cfg->getInt("type") == 0);
+ bool rgb = (mimeType() == "image/x-portable-pixmap");
+ bool binary = (configuration->getInt("type") == 0);
- bool bitmap = (to == "image/x-portable-bitmap");
+ bool bitmap = (mimeType() == "image/x-portable-bitmap");
- KisImageWSP image = input->image();
+ KisImageSP image = document->image();
Q_CHECK_PTR(image);
// the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(input->image()->locked());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
// Test color space
if (((rgb && (pd->colorSpace()->id() != "RGBA" && pd->colorSpace()->id() != "RGBA16"))
|| (!rgb && (pd->colorSpace()->id() != "GRAYA" && pd->colorSpace()->id() != "GRAYA16" && pd->colorSpace()->id() != "GRAYAU16")))) {
if (rgb) {
pd->convertTo(KoColorSpaceRegistry::instance()->rgb8(0), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
else {
pd->convertTo(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), 0), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
}
bool is16bit = pd->colorSpace()->id() == "RGBA16" || pd->colorSpace()->id() == "GRAYAU16";
- // Open the file for writing
- QFile fp(filename);
- fp.open(QIODevice::WriteOnly);
-
// Write the magic
if (rgb) {
- if (binary) fp.write("P6");
- else fp.write("P3");
+ if (binary) io->write("P6");
+ else io->write("P3");
} else if (bitmap) {
- if (binary) fp.write("P4");
- else fp.write("P1");
+ if (binary) io->write("P4");
+ else io->write("P1");
} else {
- if (binary) fp.write("P5");
- else fp.write("P2");
+ if (binary) io->write("P5");
+ else io->write("P2");
}
- fp.write("\n");
+ io->write("\n");
// Write the header
- fp.write(QByteArray::number(image->width()));
- fp.write(" ");
- fp.write(QByteArray::number(image->height()));
+ io->write(QByteArray::number(image->width()));
+ io->write(" ");
+ io->write(QByteArray::number(image->height()));
if (!bitmap) {
- if (is16bit) fp.write(" 65535\n");
- else fp.write(" 255\n");
+ if (is16bit) io->write(" 65535\n");
+ else io->write(" 255\n");
} else {
- fp.write("\n");
+ io->write("\n");
}
// Write the data
KisPPMFlow* flow = 0;
- if (binary) flow = new KisPPMBinaryFlow(&fp);
- else flow = new KisPPMAsciiFlow(&fp);
+ if (binary) flow = new KisPPMBinaryFlow(io);
+ else flow = new KisPPMAsciiFlow(io);
for (int y = 0; y < image->height(); ++y) {
KisHLineIteratorSP it = pd->createHLineIteratorNG(0, y, image->width());
if (is16bit) {
if (rgb) {
do {
flow->writeNumber(KoBgrU16Traits::red(it->rawData()));
flow->writeNumber(KoBgrU16Traits::green(it->rawData()));
flow->writeNumber(KoBgrU16Traits::blue(it->rawData()));
} while (it->nextPixel());
} else if (bitmap) {
do {
flow->writeBool(*reinterpret_cast<quint16*>(it->rawData()));
} while (it->nextPixel());
} else {
do {
flow->writeNumber(*reinterpret_cast<quint16*>(it->rawData()));
} while (it->nextPixel());
}
} else {
if (rgb) {
do {
flow->writeNumber(KoBgrTraits<quint8>::red(it->rawData()));
flow->writeNumber(KoBgrTraits<quint8>::green(it->rawData()));
flow->writeNumber(KoBgrTraits<quint8>::blue(it->rawData()));
} while (it->nextPixel());
} else if (bitmap) {
do {
flow->writeBool(*reinterpret_cast<quint8*>(it->rawData()));
} while (it->nextPixel());
} else {
do {
flow->writeNumber(*reinterpret_cast<quint8*>(it->rawData()));
} while (it->nextPixel());
}
}
}
if (bitmap) {
flow->flush();
}
delete flow;
- fp.close();
return KisImportExportFilter::OK;
}
KisPropertiesConfigurationSP KisPPMExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("type", 0);
return cfg;
}
KisPropertiesConfigurationSP KisPPMExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
{
KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
QString filterConfig = KisConfig().exportConfiguration("PPM");
cfg->fromXML(filterConfig, false);
return cfg;
}
KisConfigWidget *KisPPMExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
return new KisWdgOptionsPPM(parent);
}
void KisWdgOptionsPPM::setConfiguration(const KisPropertiesConfigurationSP cfg)
{
cmbType->setCurrentIndex(cfg->getInt("type", 0));
}
KisPropertiesConfigurationSP KisWdgOptionsPPM::configuration() const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("type", cmbType->currentIndex());
return cfg;
}
+
+void KisPPMExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("ColorModelCheck/" + RGBAColorModelID.id() + "/" + Integer8BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("ColorModelCheck/" + RGBAColorModelID.id() + "/" + Integer16BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("ColorModelCheck/" + GrayAColorModelID.id() + "/" + Integer8BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("ColorModelCheck/" + GrayAColorModelID.id() + "/" + Integer16BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED));
+
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer16BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "PPM");
+
+}
+
+
+
#include "kis_ppm_export.moc"
diff --git a/plugins/impex/ppm/kis_ppm_export.h b/plugins/impex/ppm/kis_ppm_export.h
index b7983dc017..9341b76bb6 100644
--- a/plugins/impex/ppm/kis_ppm_export.h
+++ b/plugins/impex/ppm/kis_ppm_export.h
@@ -1,58 +1,59 @@
/*
* Copyright (c) 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; 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.
*/
#ifndef _KIS_PPM_EXPORT_H_
#define _KIS_PPM_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
#include <kis_config_widget.h>
#include "ui_kis_wdg_options_ppm.h"
class KisWdgOptionsPPM : public KisConfigWidget, public Ui::WdgOptionsPPM
{
Q_OBJECT
public:
KisWdgOptionsPPM(QWidget *parent)
: KisConfigWidget(parent)
{
setupUi(this);
}
void setConfiguration(const KisPropertiesConfigurationSP cfg);
KisPropertiesConfigurationSP configuration() const;
};
class KisPPMExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisPPMExport(QObject *parent, const QVariantList &);
virtual ~KisPPMExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const;
KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/ppm/kis_ppm_import.cpp b/plugins/impex/ppm/kis_ppm_import.cpp
index f1ceadee75..818bf3697c 100644
--- a/plugins/impex/ppm/kis_ppm_import.cpp
+++ b/plugins/impex/ppm/kis_ppm_import.cpp
@@ -1,327 +1,292 @@
/*
* Copyright (c) 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_ppm_import.h"
#include <ctype.h>
#include <QApplication>
#include <QFile>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <KoColorSpaceRegistry.h>
-#include <KisFilterChain.h>
#include <kis_debug.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <KoColorSpaceTraits.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
#include <KoColorSpace.h>
#include <qendian.h>
#include <KoColorModelStandardIds.h>
#include "kis_iterator_ng.h"
K_PLUGIN_FACTORY_WITH_JSON(PPMImportFactory, "krita_ppm_import.json", registerPlugin<KisPPMImport>();)
KisPPMImport::KisPPMImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisPPMImport::~KisPPMImport()
{
}
-KisImportExportFilter::ConversionStatus KisPPMImport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
-{
- Q_UNUSED(from);
- Q_UNUSED(configuration);
- dbgFile << "Importing using PPMImport!";
-
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- if (filename.isEmpty()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- if (!QFileInfo(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
- }
-
-
- QFile fp(filename);
- doc->prepareForImport();
- return loadFromDevice(&fp, doc);
-}
-
int readNumber(QIODevice* device)
{
char c;
int val = 0;
while (true) {
if (!device->getChar(&c)) break; // End of the file
if (isdigit(c)) {
val = 10 * val + c - '0';
} else if (c == '#') {
device->readLine();
break;
} else if (isspace((uchar) c)) {
break;
}
}
return val;
}
class KisPpmFlow
{
public:
KisPpmFlow() {
}
virtual ~KisPpmFlow() {
}
virtual void nextRow() = 0;
virtual bool valid() = 0;
virtual bool nextUint1() = 0;
virtual quint8 nextUint8() = 0;
virtual quint16 nextUint16() = 0;
};
class KisAsciiPpmFlow : public KisPpmFlow
{
public:
KisAsciiPpmFlow(QIODevice* device) : m_device(device) {
}
~KisAsciiPpmFlow() override {
}
void nextRow() override {
}
bool valid() override {
return !m_device->atEnd();
}
bool nextUint1() override {
return readNumber(m_device) == 1;
}
quint8 nextUint8() override {
return readNumber(m_device);
}
quint16 nextUint16() override {
return readNumber(m_device);
}
private:
QIODevice* m_device;
};
class KisBinaryPpmFlow : public KisPpmFlow
{
public:
KisBinaryPpmFlow(QIODevice* device, int lineWidth) : m_pos(0), m_device(device), m_lineWidth(lineWidth) {
}
~KisBinaryPpmFlow() override {
}
void nextRow() override {
m_array = m_device->read(m_lineWidth);
m_ptr = m_array.data();
}
bool valid() override {
return m_array.size() == m_lineWidth;
}
bool nextUint1() override {
if (m_pos == 0) {
m_current = nextUint8();
m_pos = 8;
}
bool v = (m_current & 1) == 1;
--m_pos;
m_current = m_current >> 1;
return v;
}
quint8 nextUint8() override {
quint8 v = *reinterpret_cast<quint8*>(m_ptr);
m_ptr += 1;
return v;
}
quint16 nextUint16() override {
quint16 v = *reinterpret_cast<quint16*>(m_ptr);
m_ptr += 2;
return qFromBigEndian(v);
}
private:
int m_pos;
quint8 m_current;
char* m_ptr;
QIODevice* m_device;
QByteArray m_array;
int m_lineWidth;
};
-KisImportExportFilter::ConversionStatus KisPPMImport::loadFromDevice(QIODevice* device, KisDocument* doc)
-{
- dbgFile << "Start decoding file";
- device->open(QIODevice::ReadOnly);
- if (!device->isOpen()) {
- return KisImportExportFilter::CreationError;
- }
- QByteArray array = device->read(2);
+
+KisImportExportFilter::ConversionStatus KisPPMImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
+{
+ QByteArray array = io->read(2);
if (array.size() < 2) return KisImportExportFilter::CreationError;
// Read the type of the ppm file
enum { Puk, P1, P2, P3, P4, P5, P6 } fileType = Puk; // Puk => unknown
int channels = -1;
bool isAscii = false;
if (array == "P1") {
fileType = P1;
isAscii = true;
channels = 0;
} else if (array == "P2") {
fileType = P2;
channels = 1;
isAscii = true;
} else if (array == "P3") {
fileType = P3;
channels = 3;
isAscii = true;
} else if (array == "P4") {
fileType = P4;
channels = 0;
} else if (array == "P5") { // PGM
fileType = P5;
channels = 1;
} else if (array == "P6") { // PPM
fileType = P6;
channels = 3;
}
Q_ASSERT(channels != -1);
- char c; device->getChar(&c);
+ char c; io->getChar(&c);
if (!isspace(c)) return KisImportExportFilter::CreationError; // Invalid file, it should have a separator now
// Read width
- int width = readNumber(device);
- int height = readNumber(device);
+ int width = readNumber(io);
+ int height = readNumber(io);
int maxval = 1;
if (fileType != P1 && fileType != P4) {
- maxval = readNumber(device);
+ maxval = readNumber(io);
}
dbgFile << "Width = " << width << " height = " << height << "maxval = " << maxval;
// Select the colorspace depending on the maximum value
int pixelsize = -1;
const KoColorSpace* colorSpace = 0;
if (maxval <= 255) {
if (channels == 1 || channels == 0) {
pixelsize = 1;
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), 0);
} else {
pixelsize = 3;
colorSpace = KoColorSpaceRegistry::instance()->rgb8();
}
} else if (maxval <= 65535) {
if (channels == 1 || channels == 0) {
pixelsize = 2;
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), 0);
} else {
pixelsize = 6;
colorSpace = KoColorSpaceRegistry::instance()->rgb16();
}
} else {
dbgFile << "Unknown colorspace";
return KisImportExportFilter::CreationError;
}
- KisImageSP image = new KisImage(doc->createUndoStore(), width, height, colorSpace, "built image");
+ KisImageSP image = new KisImage(document->createUndoStore(), width, height, colorSpace, "built image");
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255);
KisPpmFlow* ppmFlow = 0;
if (isAscii) {
- ppmFlow = new KisAsciiPpmFlow(device);
+ ppmFlow = new KisAsciiPpmFlow(io);
} else {
- ppmFlow = new KisBinaryPpmFlow(device, pixelsize * width);
+ ppmFlow = new KisBinaryPpmFlow(io, pixelsize * width);
}
for (int v = 0; v < height; ++v) {
KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, v, width);
ppmFlow->nextRow();
if (!ppmFlow->valid()) return KisImportExportFilter::CreationError;
if (maxval <= 255) {
if (channels == 3) {
do {
KoBgrTraits<quint8>::setRed(it->rawData(), ppmFlow->nextUint8());
KoBgrTraits<quint8>::setGreen(it->rawData(), ppmFlow->nextUint8());
KoBgrTraits<quint8>::setBlue(it->rawData(), ppmFlow->nextUint8());
colorSpace->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
} while (it->nextPixel());
} else if (channels == 1) {
do {
*reinterpret_cast<quint8*>(it->rawData()) = ppmFlow->nextUint8();
colorSpace->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
} while (it->nextPixel());
} else if (channels == 0) {
do {
if (ppmFlow->nextUint1()) {
*reinterpret_cast<quint8*>(it->rawData()) = 255;
} else {
*reinterpret_cast<quint8*>(it->rawData()) = 0;
}
colorSpace->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
} while (it->nextPixel());
}
} else {
if (channels == 3) {
do {
KoBgrU16Traits::setRed(it->rawData(), ppmFlow->nextUint16());
KoBgrU16Traits::setGreen(it->rawData(), ppmFlow->nextUint16());
KoBgrU16Traits::setBlue(it->rawData(), ppmFlow->nextUint16());
colorSpace->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
} while (it->nextPixel());
} else if (channels == 1) {
do {
*reinterpret_cast<quint16*>(it->rawData()) = ppmFlow->nextUint16();
colorSpace->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
} while (it->nextPixel());
}
}
}
image->addNode(layer.data(), image->rootLayer().data());
- doc->setCurrentImage(image);
+ document->setCurrentImage(image);
return KisImportExportFilter::OK;
}
#include "kis_ppm_import.moc"
diff --git a/plugins/impex/ppm/kis_ppm_import.h b/plugins/impex/ppm/kis_ppm_import.h
index 5c882db742..c3796712c5 100644
--- a/plugins/impex/ppm/kis_ppm_import.h
+++ b/plugins/impex/ppm/kis_ppm_import.h
@@ -1,42 +1,40 @@
/*
* Copyright (c) 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.
*/
#ifndef _KIS_PPM_IMPORT_H_
#define _KIS_PPM_IMPORT_H_
#include <QVariant>
#include <QIODevice>
#include <KisImportExportFilter.h>
class KisDocument;
class KisPPMImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisPPMImport(QObject *parent, const QVariantList &);
virtual ~KisPPMImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
-private:
- KisImportExportFilter::ConversionStatus loadFromDevice(QIODevice* device, KisDocument* doc);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/ppm/krita_ppm_export.json b/plugins/impex/ppm/krita_ppm_export.json
index f1ca50da04..ed1c75b990 100644
--- a/plugins/impex/ppm/krita_ppm_export.json
+++ b/plugins/impex/ppm/krita_ppm_export.json
@@ -1,15 +1,15 @@
{
"Icon": "",
"Id": "Krita PPM Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/x-portable-pixmap,image/x-portable-graymap,image/x-portable-bitmap",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Library": "kritappmexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "ppm"
}
diff --git a/plugins/impex/ppm/krita_ppm_import.json b/plugins/impex/ppm/krita_ppm_import.json
index 9fc2d4f8fd..92e602588f 100644
--- a/plugins/impex/ppm/krita_ppm_import.json
+++ b/plugins/impex/ppm/krita_ppm_import.json
@@ -1,15 +1,15 @@
{
"Icon": "",
"Id": "Krita PPM Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/x-portable-pixmap,image/x-portable-graymap,image/x-portable-bitmap",
"X-KDE-Library": "kritappmimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "ppm"
}
diff --git a/plugins/impex/psd/CMakeLists.txt b/plugins/impex/psd/CMakeLists.txt
index e1a157c78b..3a46d19387 100644
--- a/plugins/impex/psd/CMakeLists.txt
+++ b/plugins/impex/psd/CMakeLists.txt
@@ -1,66 +1,66 @@
if (NOT MSVC AND NOT APPLE)
add_subdirectory(tests)
endif()
configure_file(config_psd.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_psd.h)
include_directories(
${CMAKE_BINARY_DIR}/libs/psd
${CMAKE_SOURCE_DIR}/libs/psd
) #For kispsd_include.h
include_directories(SYSTEM
${ZLIB_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}
)
set(LIB_PSD_SRCS
psd_header.cpp
psd_colormode_block.cpp
psd_resource_section.cpp
psd_resource_block.cpp
psd_layer_section.cpp
psd_layer_record.cpp
psd_image_data.cpp
psd_pixel_utils.cpp
psd_additional_layer_info_block.cpp
)
#
# import
#
set(kritapsdimport_SOURCES
psd_import.cc
psd_loader.cpp
${LIB_PSD_SRCS}
)
add_library(kritapsdimport MODULE ${kritapsdimport_SOURCES})
target_link_libraries(kritapsdimport kritaglobal kritaui kritapsd KF5::I18n ${ZLIB_LIBRARIES})
install(TARGETS kritapsdimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
#
# export
#
set(kritapsdexport_SOURCES
psd_export.cc
psd_saver.cpp
${LIB_PSD_SRCS}
)
add_library(kritapsdexport MODULE ${kritapsdexport_SOURCES})
if (MSVC)
- target_link_libraries(kritapsdexport kritaui kritapsd ${WIN32_PLATFORM_NET_LIBS} ${ZLIB_LIBRARIES})
+ target_link_libraries(kritapsdexport kritaui kritapsd kritaimpex ${WIN32_PLATFORM_NET_LIBS} ${ZLIB_LIBRARIES})
else ()
- target_link_libraries(kritapsdexport kritaui kritapsd ${ZLIB_LIBRARIES})
+ target_link_libraries(kritapsdexport kritaui kritapsd kritaimpex ${ZLIB_LIBRARIES})
endif ()
install(TARGETS kritapsdexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_psd.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/psd/krita_psd_export.json b/plugins/impex/psd/krita_psd_export.json
index ff97d3298c..6f76ba23a9 100644
--- a/plugins/impex/psd/krita_psd_export.json
+++ b/plugins/impex/psd/krita_psd_export.json
@@ -1,14 +1,14 @@
{
"Icon": "",
"Id": "Krita PhotoShop Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/vnd.adobe.photoshop",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Library": "kritapsdexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "psd"
}
diff --git a/plugins/impex/psd/krita_psd_import.json b/plugins/impex/psd/krita_psd_import.json
index 2e3174c686..098fbe4822 100644
--- a/plugins/impex/psd/krita_psd_import.json
+++ b/plugins/impex/psd/krita_psd_import.json
@@ -1,14 +1,14 @@
{
"Icon": "",
"Id": "Krita PhotoShop Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/vnd.adobe.photoshop",
"X-KDE-Library": "kritapsdimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "psd"
}
diff --git a/plugins/impex/psd/psd_export.cc b/plugins/impex/psd/psd_export.cc
index 121dd9f41c..a6d13daaf6 100644
--- a/plugins/impex/psd/psd_export.cc
+++ b/plugins/impex/psd/psd_export.cc
@@ -1,125 +1,120 @@
/*
* 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_export.h"
#include <QCheckBox>
#include <QSlider>
-#include <QMessageBox>
-
-#include <kpluginfactory.h>
#include <QFileInfo>
#include <QApplication>
+#include <kpluginfactory.h>
+
+#include <KisExportCheckRegistry.h>
#include <KisImportExportManager.h>
-#include <KisFilterChain.h>
+#include <ImageSizeCheck.h>
+#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceConstants.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include "psd_saver.h"
class KisExternalLayer;
K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_psd_export.json", registerPlugin<psdExport>();)
bool checkHomogenity(KisNodeSP root, const KoColorSpace* cs)
{
bool res = true;
KisNodeSP child = root->firstChild();
while (child) {
if (child->childCount() > 0) {
res = checkHomogenity(child, cs);
if (res == false) {
break;
}
}
KisLayer *layer = dynamic_cast<KisLayer*>(child.data());
if (layer) {
if (layer->colorSpace() != cs) {
res = false;
break;
}
}
child = child->nextSibling();
}
return res;
}
psdExport::psdExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
psdExport::~psdExport()
{
}
-KisImportExportFilter::ConversionStatus psdExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus psdExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile <<"PSD export! From:" << from <<", To:" << to <<"";
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
-
-
- if (input->image()->width() > 30000 || input->image()->height() > 30000) {
- if (!getBatchMode()) {
- QMessageBox::critical(0,
- i18nc("@title:window", "Photoshop Export Error"),
- i18n("Unable to save to the Photoshop format.\n"
- "The Photoshop format only supports images that are smaller than 30000x3000 pixels."));
- }
- return KisImportExportFilter::InvalidFormat;
- }
-
-
- if (!checkHomogenity(input->image()->rootLayer(), input->image()->colorSpace())) {
- if (!getBatchMode()) {
- QMessageBox::critical(0,
- i18nc("@title:window", "Photoshop Export Error"),
- i18n("Unable to save to the Photoshop format.\n"
- "The Photoshop format only supports images where all layers have the same colorspace as the image."));
- }
- return KisImportExportFilter::InvalidFormat;
- }
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- PSDSaver kpc(input);
+ PSDSaver psdSaver(document);
KisImageBuilder_Result res;
- if ((res = kpc.buildFile(filename)) == KisImageBuilder_RESULT_OK) {
+ if ((res = psdSaver.buildFile(io)) == KisImageBuilder_RESULT_OK) {
dbgFile <<"success !";
return KisImportExportFilter::OK;
}
dbgFile <<" Result =" << res;
return KisImportExportFilter::InternalError;
}
+void psdExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("PSDLayerStyleCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("NodeTypeCheck/KisGroupLayer")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("ColorModelHomogenousCheck")->create(KisExportCheckBase::UNSUPPORTED, i18nc("image conversion warning", "Your image contains one or more layers with a color model that is different from the image.")));
+
+ ImageSizeCheckFactory *factory = dynamic_cast<ImageSizeCheckFactory*>(KisExportCheckRegistry::instance()->get("ImageSizeCheck"));
+ if (factory) {
+ addCapability(factory->create(30000, 30000, KisExportCheckBase::SUPPORTED));
+ }
+
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Float16BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Float32BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(CMYKAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(CMYKAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(LABAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(LABAColorModelID, Integer16BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "PSD");
+
+}
+
#include <psd_export.moc>
diff --git a/plugins/impex/psd/psd_export.h b/plugins/impex/psd/psd_export.h
index 3495fc4f10..2a368771de 100644
--- a/plugins/impex/psd/psd_export.h
+++ b/plugins/impex/psd/psd_export.h
@@ -1,34 +1,35 @@
/*
* 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_EXPORT_H_
#define _PSD_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class psdExport : public KisImportExportFilter {
Q_OBJECT
public:
psdExport(QObject *parent, const QVariantList &);
virtual ~psdExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/psd/psd_import.cc b/plugins/impex/psd/psd_import.cc
index a609245d8b..0d8722f207 100644
--- a/plugins/impex/psd/psd_import.cc
+++ b/plugins/impex/psd/psd_import.cc
@@ -1,94 +1,72 @@
/*
* 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_import.h"
#include <kpluginfactory.h>
#include <QFileInfo>
-#include <KisFilterChain.h>
-
#include <KisDocument.h>
#include <kis_image.h>
#include "psd_loader.h"
K_PLUGIN_FACTORY_WITH_JSON(ImportFactory, "krita_psd_import.json", registerPlugin<psdImport>();)
- psdImport::psdImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
+psdImport::psdImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
psdImport::~psdImport()
{
}
-KisImportExportFilter::ConversionStatus psdImport::convert(const QByteArray&, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus psdImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile <<"Importing using PSDImport!";
-
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
- QString filename = inputFile();
- doc->prepareForImport();
+ PSDLoader ib(document);
- if (!filename.isEmpty()) {
+ KisImageBuilder_Result result = ib.buildImage(io);
- if (!QFileInfo(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- PSDLoader ib(doc);
-
- KisImageBuilder_Result result = ib.buildImage(filename);
-
- switch (result) {
- case KisImageBuilder_RESULT_UNSUPPORTED:
- return KisImportExportFilter::NotImplemented;
- case KisImageBuilder_RESULT_INVALID_ARG:
- return KisImportExportFilter::BadMimeType;
- case KisImageBuilder_RESULT_NO_URI:
- case KisImageBuilder_RESULT_NOT_EXIST:
- case KisImageBuilder_RESULT_NOT_LOCAL:
- qDebug() << "ib returned KisImageBuilder_RESULT_NOT_LOCAL";
- return KisImportExportFilter::FileNotFound;
- case KisImageBuilder_RESULT_BAD_FETCH:
- case KisImageBuilder_RESULT_EMPTY:
- return KisImportExportFilter::ParsingError;
- case KisImageBuilder_RESULT_FAILURE:
- return KisImportExportFilter::InternalError;
- case KisImageBuilder_RESULT_OK:
- doc -> setCurrentImage( ib.image());
- return KisImportExportFilter::OK;
- default:
- return KisImportExportFilter::StorageCreationError;
- //dbgFile << "Result was: " << result;
- }
+ switch (result) {
+ case KisImageBuilder_RESULT_UNSUPPORTED:
+ return KisImportExportFilter::NotImplemented;
+ case KisImageBuilder_RESULT_INVALID_ARG:
+ return KisImportExportFilter::BadMimeType;
+ case KisImageBuilder_RESULT_NO_URI:
+ case KisImageBuilder_RESULT_NOT_EXIST:
+ case KisImageBuilder_RESULT_NOT_LOCAL:
+ qDebug() << "ib returned KisImageBuilder_RESULT_NOT_LOCAL";
+ return KisImportExportFilter::FileNotFound;
+ case KisImageBuilder_RESULT_BAD_FETCH:
+ case KisImageBuilder_RESULT_EMPTY:
+ return KisImportExportFilter::ParsingError;
+ case KisImageBuilder_RESULT_FAILURE:
+ return KisImportExportFilter::InternalError;
+ case KisImageBuilder_RESULT_OK:
+ document -> setCurrentImage( ib.image());
+ return KisImportExportFilter::OK;
+ default:
+ return KisImportExportFilter::StorageCreationError;
+ //dbgFile << "Result was: " << result;
}
- return KisImportExportFilter::StorageCreationError;
+ return KisImportExportFilter::InternalError;
}
#include <psd_import.moc>
diff --git a/plugins/impex/psd/psd_import.h b/plugins/impex/psd/psd_import.h
index 712be6ef7e..88766f5175 100644
--- a/plugins/impex/psd/psd_import.h
+++ b/plugins/impex/psd/psd_import.h
@@ -1,34 +1,34 @@
/*
* 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_IMPORT_H_
#define PSD_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class psdImport : public KisImportExportFilter {
Q_OBJECT
public:
psdImport(QObject *parent, const QVariantList &);
virtual ~psdImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/psd/psd_loader.cpp b/plugins/impex/psd/psd_loader.cpp
index 12f7761b58..aee90777bd 100644
--- a/plugins/impex/psd/psd_loader.cpp
+++ b/plugins/impex/psd/psd_loader.cpp
@@ -1,377 +1,371 @@
/*
* 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 <QFileInfo>
#include <QStack>
#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 QString &filename)
+KisImageBuilder_Result PSDLoader::decode(QIODevice *io)
{
// open the file
- QFile f(filename);
- if (!f.exists()) {
- return KisImageBuilder_RESULT_NOT_EXIST;
- }
- if (!f.open(QIODevice::ReadOnly)) {
- return KisImageBuilder_RESULT_FAILURE;
- }
- dbgFile << "pos:" << f.pos();
+ dbgFile << "pos:" << io->pos();
PSDHeader header;
- if (!header.read(&f)) {
+ if (!header.read( io)) {
dbgFile << "failed reading header: " << header.error;
return KisImageBuilder_RESULT_FAILURE;
}
dbgFile << header;
- dbgFile << "Read header. pos:" << f.pos();
+ dbgFile << "Read header. pos:" << io->pos();
PSDColorModeBlock colorModeBlock(header.colormode);
- if (!colorModeBlock.read(&f)) {
+ if (!colorModeBlock.read(io)) {
dbgFile << "failed reading colormode block: " << colorModeBlock.error;
return KisImageBuilder_RESULT_FAILURE;
}
- dbgFile << "Read color mode block. pos:" << f.pos();
+ dbgFile << "Read color mode block. pos:" << io->pos();
PSDImageResourceSection resourceSection;
- if (!resourceSection.read(&f)) {
+ if (!resourceSection.read(io)) {
dbgFile << "failed image reading resource section: " << resourceSection.error;
return KisImageBuilder_RESULT_FAILURE;
}
- dbgFile << "Read image resource section. pos:" << f.pos();
+ dbgFile << "Read image resource section. pos:" << io->pos();
PSDLayerMaskSection layerSection(header);
- if (!layerSection.read(&f)) {
+ if (!layerSection.read(io)) {
dbgFile << "failed reading layer/mask section: " << layerSection.error;
return KisImageBuilder_RESULT_FAILURE;
}
- dbgFile << "Read layer/mask section. " << layerSection.nLayers << "layers. pos:" << f.pos();
+ dbgFile << "Read layer/mask section. " << layerSection.nLayers << "layers. pos:" << io->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());
+ QString name = dynamic_cast<QFile*>(io) ? dynamic_cast<QFile*>(io)->fileName() : "Imported";
+ m_image = new KisImage(m_doc->createUndoStore(), header.width, header.height, cs, name);
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
Q_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'";
+ dbgFile << "Position" << io->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());
+ imageData.read(io, 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());
/**
* PSD has a weird "optimization": if a group layer has only one
* child layer, it omits it's 'psd_bounding_divider' section. So
* fi you ever see an unbalanced layers group in PSD, most
* probably, it is just a single layered group.
*/
KisNodeSP lastAddedLayer;
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.size() > 1 || (lastAddedLayer && !groupStack.isEmpty()))) {
KisGroupLayerSP groupLayer;
if (groupStack.size() <= 1) {
groupLayer = new KisGroupLayer(m_image, "temp", OPACITY_OPAQUE_U8);
m_image->addNode(groupLayer, groupStack.top());
m_image->moveNode(lastAddedLayer, groupLayer, KisNodeSP());
} else {
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->setCompositeOpId(compositeOp);
newLayer = groupLayer;
} else {
/**
* In some files saved by PS CS6 the group layer sections seem
* to be unbalanced. I don't know why it happens because the
* reporter didn't provide us an example file. So here we just
* check if the new layer was created, and if not, skip the
* initialization of masks.
*
* See bug: 357559
*/
warnKrita << "WARNING: Provided PSD has unbalanced group "
<< "layer markers. Some masks and/or layers can "
<< "be lost while loading this file. Please "
<< "report a bug to Krita developes and attach "
<< "this file to the bugreport\n"
<< " " << ppVar(layerRecord->layerName) << "\n"
<< " " << ppVar(layerRecord->infoBlocks.sectionDividerType) << "\n"
<< " " << ppVar(groupStack.size());
continue;
}
}
else {
KisPaintLayerSP layer = new KisPaintLayer(m_image, layerRecord->layerName, layerRecord->opacity);
layer->setCompositeOpId(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())) {
+ if (!layerRecord->readPixelData(io, 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;
}
Q_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)) {
+ if (!layerRecord->readMask(io, mask->paintDevice(), channelInfo)) {
dbgFile << "failed reading masks for layer: " << layerRecord->layerName << layerRecord->error;
}
m_image->addNode(mask, newLayer);
}
}
lastAddedLayer = newLayer;
}
const QVector<QDomDocument> &embeddedPatterns =
layerSection.globalInfoSection.embeddedPatterns;
KisAslLayerStyleSerializer serializer;
if (!embeddedPatterns.isEmpty()) {
Q_FOREACH (const QDomDocument &doc, embeddedPatterns) {
serializer.registerPSDPattern(doc);
}
}
QVector<KisPSDLayerStyleSP> allStylesForServer;
if (!allStylesXml.isEmpty()) {
Q_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 {
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_SAFE_ASSERT_RECOVER_NOOP(!collection->valid());
collection->setLayerStyles(allStylesForServer);
KIS_SAFE_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 QString &filename)
+KisImageBuilder_Result PSDLoader::buildImage(QIODevice *io)
{
- return decode(filename);
+ return decode(io);
}
-KisImageWSP PSDLoader::image()
+KisImageSP PSDLoader::image()
{
return m_image;
}
void PSDLoader::cancel()
{
m_stop = true;
}
diff --git a/plugins/impex/psd/psd_loader.h b/plugins/impex/psd/psd_loader.h
index 6e40ab6404..0b587a5f43 100644
--- a/plugins/impex/psd/psd_loader.h
+++ b/plugins/impex/psd/psd_loader.h
@@ -1,59 +1,59 @@
/*
* 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_LOADER_H_
#define _PSD_LOADER_H_
#include <stdio.h>
#include <QObject>
#include <QFileInfo>
#include "kis_types.h"
#include <KisImageBuilderResult.h>
class KisDocument;
class PSDLoader : public QObject {
Q_OBJECT
public:
PSDLoader(KisDocument *doc);
virtual ~PSDLoader();
- KisImageBuilder_Result buildImage(const QString &filename);
+ KisImageBuilder_Result buildImage(QIODevice *io);
- KisImageWSP image();
+ KisImageSP image();
public Q_SLOTS:
virtual void cancel();
private:
- KisImageBuilder_Result decode(const QString &filename);
+ KisImageBuilder_Result decode(QIODevice *io);
private:
- KisImageWSP m_image;
+ KisImageSP m_image;
KisDocument *m_doc;
bool m_stop;
};
#endif
diff --git a/plugins/impex/psd/psd_resource_block.cpp b/plugins/impex/psd/psd_resource_block.cpp
index c3e720f32e..5fd0924757 100644
--- a/plugins/impex/psd/psd_resource_block.cpp
+++ b/plugins/impex/psd/psd_resource_block.cpp
@@ -1,399 +1,399 @@
/*
* 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_block.h"
#include <QIODevice>
#include <QBuffer>
#include <QDataStream>
#include <kis_debug.h>
#include "psd_utils.h"
#include "psd_resource_section.h"
PSDResourceBlock::PSDResourceBlock()
: KisAnnotation("PSD Resource Block", "", QByteArray())
, identifier(PSDImageResourceSection::UNKNOWN)
, resource(0)
{
}
bool PSDResourceBlock::read(QIODevice* io)
{
dbgFile << "Reading resource block";
if (io->atEnd()) {
error = "Could not read resource block: no bytes left.";
return false;
}
QByteArray b;
b = io->read(4);
if(b.size() != 4 || QString(b) != "8BIM") {
error = QString("Could not read resource block signature. Got %1.")
.arg(QString(b));
return false;
}
if (!psdread(io, &identifier)) {
error = "Could not read resource block identifier";
return false;
}
dbgFile << "\tresource block identifier" << PSDImageResourceSection::idToString((PSDImageResourceSection::PSDResourceID)identifier) << identifier;
m_type = QString("PSD Resource Block: %1").arg(identifier);
if (!psdread_pascalstring(io, name, 2)) {
error = "Could not read name of resource block";
return false;
}
dbgFile << "\tresource block name" << name;
if (!psdread(io, &dataSize)) {
error = QString("Could not read datasize for resource block with name %1 of type %2").arg(name).arg(identifier);
return false;
}
if ((dataSize & 0x01) != 0) {
dataSize++;
}
dbgFile << "\tresource block size" << dataSize;
m_description = PSDImageResourceSection::idToString((PSDImageResourceSection::PSDResourceID)identifier);
data = io->read(dataSize);
if (data.size() != (int)dataSize) {
error = QString("Could not read data for resource block with name %1 of type %2").arg(name).arg(identifier);
return false;
}
m_annotation = data;
switch (identifier) {
// case PSDImageResourceSection::MAC_PRINT_INFO:
// resource = new MAC_PRINT_INFO_1001;
// break;
case PSDImageResourceSection::RESN_INFO:
resource = new RESN_INFO_1005;
break;
// case PSDImageResourceSection::ALPHA_NAMES:
// resource = new ALPHA_NAMES_1006;
// break;
// case PSDImageResourceSection::DISPLAY_INFO:
// resource = new DISPLAY_INFO_1007;
// break;
// case PSDImageResourceSection::CAPTION:
// resource = new CAPTION_1008;
// break;
// case PSDImageResourceSection::BORDER_INFO:
// resource = new BORDER_INFO_1009;
// break;
// case PSDImageResourceSection::BACKGROUND_COL:
// resource = new BACKGROUND_COL_1010;
// break;
// case PSDImageResourceSection::PRINT_FLAGS:
// resource = new PRINT_FLAGS_1011;
// break;
// case PSDImageResourceSection::GREY_HALFTONE:
// resource = new GREY_HALFTONE_1012;
// break;
// case PSDImageResourceSection::COLOR_HALFTONE:
// resource = new COLOR_HALFTONE_1013;
// break;
// case PSDImageResourceSection::DUOTONE_HALFTONE:
// resource = new DUOTONE_HALFTONE_1014;
// break;
// case PSDImageResourceSection::GREY_XFER:
// resource = new GREY_XFER_1015;
// break;
// case PSDImageResourceSection::COLOR_XFER:
// resource = new COLOR_XFER_1016;
// break;
// case PSDImageResourceSection::DUOTONE_XFER:
// resource = new DUOTONE_XFER_1017;
// break;
// case PSDImageResourceSection::DUOTONE_INFO:
// resource = new DUOTONE_INFO_1018;
// break;
// case PSDImageResourceSection::EFFECTIVE_BW:
// resource = new EFFECTIVE_BW_1019;
// break;
// case PSDImageResourceSection::EPS_OPT:
// resource = new EPS_OPT_1021;
// break;
// case PSDImageResourceSection::QUICK_MASK:
// resource = new QUICK_MASK_1022;
// break;
// case PSDImageResourceSection::LAYER_STATE:
// resource = new LAYER_STATE_1024;
// break;
// case PSDImageResourceSection::WORKING_PATH:
// resource = new WORKING_PATH_1025;
// break;
// case PSDImageResourceSection::LAYER_GROUP:
// resource = new LAYER_GROUP_1026;
// break;
// case PSDImageResourceSection::IPTC_NAA_DATA:
// resource = new IPTC_NAA_DATA_1028;
// break;
// case PSDImageResourceSection::IMAGE_MODE_RAW:
// resource = new IMAGE_MODE_RAW_1029;
// break;
// case PSDImageResourceSection::JPEG_QUAL:
// resource = new JPEG_QUAL_1030;
// break;
// case PSDImageResourceSection::GRID_GUIDE:
// resource = new GRID_GUIDE_1032;
// break;
// case PSDImageResourceSection::THUMB_RES:
// resource = new THUMB_RES_1033;
// break;
// case PSDImageResourceSection::COPYRIGHT_FLG:
// resource = new COPYRIGHT_FLG_1034;
// break;
// case PSDImageResourceSection::URL:
// resource = new URL_1035;
// break;
// case PSDImageResourceSection::THUMB_RES2:
// resource = new THUMB_RES2_1036;
// break;
case PSDImageResourceSection::GLOBAL_ANGLE:
resource = new GLOBAL_ANGLE_1037;
break;
// case PSDImageResourceSection::COLOR_SAMPLER:
// resource = new COLOR_SAMPLER_1038;
// break;
case PSDImageResourceSection::ICC_PROFILE:
resource = new ICC_PROFILE_1039;
break;
// case PSDImageResourceSection::WATERMARK:
// resource = new WATERMARK_1040;
// break;
// case PSDImageResourceSection::ICC_UNTAGGED:
// resource = new ICC_UNTAGGED_1041;
// break;
// case PSDImageResourceSection::EFFECTS_VISIBLE:
// resource = new EFFECTS_VISIBLE_1042;
// break;
// case PSDImageResourceSection::SPOT_HALFTONE:
// resource = new SPOT_HALFTONE_1043;
// break;
// case PSDImageResourceSection::DOC_IDS:
// resource = new DOC_IDS_1044;
// break;
// case PSDImageResourceSection::ALPHA_NAMES_UNI:
// resource = new ALPHA_NAMES_UNI_1045;
// break;
// case PSDImageResourceSection::IDX_COL_TAB_CNT:
// resource = new IDX_COL_TAB_CNT_1046;
// break;
// case PSDImageResourceSection::IDX_TRANSPARENT:
// resource = new IDX_TRANSPARENT_1047;
// break;
case PSDImageResourceSection::GLOBAL_ALT:
resource = new GLOBAL_ALT_1049;
break;
// case PSDImageResourceSection::SLICES:
// resource = new SLICES_1050;
// break;
// case PSDImageResourceSection::WORKFLOW_URL_UNI:
// resource = new WORKFLOW_URL_UNI_1051;
// break;
// case PSDImageResourceSection::JUMP_TO_XPEP:
// resource = new JUMP_TO_XPEP_1052;
// break;
// case PSDImageResourceSection::ALPHA_ID:
// resource = new ALPHA_ID_1053;
// break;
// case PSDImageResourceSection::URL_LIST_UNI:
// resource = new URL_LIST_UNI_1054;
// break;
// case PSDImageResourceSection::VERSION_INFO:
// resource = new VERSION_INFO_1057;
// break;
// case PSDImageResourceSection::EXIF_DATA:
// resource = new EXIF_DATA_1058;
// break;
// case PSDImageResourceSection::XMP_DATA:
// resource = new XMP_DATA_1060;
// break;
// case PSDImageResourceSection::PATH_INFO_FIRST:
// resource = new PATH_INFO_FIRST_2000;
// break;
// case PSDImageResourceSection::PATH_INFO_LAST:
// resource = new PATH_INFO_LAST_2998;
// break;
// case PSDImageResourceSection::CLIPPING_PATH:
// resource = new CLIPPING_PATH_2999;
// break;
// case PSDImageResourceSection::PRINT_FLAGS_2:
// resource = new PRINT_FLAGS_2_10000;
// break;
default:
;
}
if (resource) {
resource->interpretBlock(data);
}
return valid();
}
-bool PSDResourceBlock::write(QIODevice* io)
+bool PSDResourceBlock::write(QIODevice* io) const
{
dbgFile << "Writing Resource Block" << PSDImageResourceSection::idToString((PSDImageResourceSection::PSDResourceID)identifier) << identifier;
if (resource && !resource->valid()) {
error = QString("Cannot write an invalid Resource Block");
return false;
}
if (identifier == PSDImageResourceSection::LAYER_STATE ||
identifier == PSDImageResourceSection::LAYER_GROUP ||
identifier == PSDImageResourceSection::LAYER_COMPS ||
identifier == PSDImageResourceSection::LAYER_GROUP_ENABLED_ID ||
identifier == PSDImageResourceSection::LAYER_SELECTION_ID) {
/**
* We can actually handle LAYER_SELECTION_ID. It consists
* of a number of layers and a list of IDs to select, which
* are retrieved from 'lyid' additional layer block.
*/
dbgFile << "Skip writing resource block" << identifier << displayText();
return true;
}
QByteArray ba;
// createBlock returns true by default but does not change the data.
if (resource && !resource->createBlock(ba)) {
error = resource->error;
return false;
}
else if (!resource) {
// reconstruct from the data
QBuffer buf(&ba);
buf.open(QBuffer::WriteOnly);
buf.write("8BIM", 4);
psdwrite(&buf, identifier);
psdwrite_pascalstring(&buf, name);
psdwrite(&buf, dataSize);
buf.write(data);
buf.close();
}
if (io->write(ba.constData(), ba.size()) != ba.size()) {
error = QString("Could not write complete resource");
return false;
}
return true;
}
bool PSDResourceBlock::valid()
{
if (identifier == PSDImageResourceSection::UNKNOWN) {
error = QString("Unknown ID: %1").arg(identifier);
return false;
}
if (data.size() != (int)dataSize) {
error = QString("Needed %1 bytes, got %2 bytes of data").arg(dataSize).arg(data.length());
return false;
}
return true;
}
bool RESN_INFO_1005::interpretBlock(QByteArray data)
{
dbgFile << "Reading RESN_INFO_1005";
// the resolution we set on the image should be dpi; we can also set the unit on the KisDocument.
QDataStream ds(data);
ds.setByteOrder(QDataStream::BigEndian);
ds >> hRes >> hResUnit >> widthUnit >> vRes >> vResUnit >> heightUnit;
/* Resolution always recorded as pixels / inch in a fixed point implied
decimal int32 with 16 bits before point and 16 after (i.e. cast as
double and divide resolution by 2^16 */
dbgFile << "hres" << hRes << "vres" << vRes;
hRes = hRes / 65536.0;
vRes = vRes / 65536.0;
dbgFile << hRes << hResUnit << widthUnit << vRes << vResUnit << heightUnit;
return ds.atEnd();
}
bool RESN_INFO_1005::createBlock(QByteArray & data)
{
dbgFile << "Writing RESN_INFO_1005";
QBuffer buf(&data);
startBlock(buf, PSDImageResourceSection::RESN_INFO, 16);
// Convert to 16.16 fixed point
Fixed h = hRes * 65536.0 + 0.5;
dbgFile << "h" << h << "hRes" << hRes;
psdwrite(&buf, (quint32)h);
psdwrite(&buf, hResUnit);
psdwrite(&buf, widthUnit);
// Convert to 16.16 fixed point
Fixed v = vRes * 65536.0 + 0.5;
dbgFile << "v" << v << "vRes" << vRes;
psdwrite(&buf, (quint32)v);
psdwrite(&buf, vResUnit);
psdwrite(&buf, heightUnit);
buf.close();
return true;
}
bool ICC_PROFILE_1039::interpretBlock(QByteArray data)
{
dbgFile << "Reading ICC_PROFILE_1039";
icc = data;
return true;
}
bool ICC_PROFILE_1039::createBlock(QByteArray &data)
{
dbgFile << "Writing ICC_PROFILE_1039";
if (icc.size() == 0) {
error = "ICC_PROFILE_1039: Trying to save an empty profile";
return false;
}
QBuffer buf(&data);
startBlock(buf, PSDImageResourceSection::ICC_PROFILE, icc.size());
buf.write(icc.constData(), icc.size());
buf.close();
return true;
}
diff --git a/plugins/impex/psd/psd_resource_block.h b/plugins/impex/psd/psd_resource_block.h
index de0ad084c9..00eb574ab1 100644
--- a/plugins/impex/psd/psd_resource_block.h
+++ b/plugins/impex/psd/psd_resource_block.h
@@ -1,749 +1,761 @@
/*
* 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 <klocalizedstring.h>
#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;
}
+ KisAnnotation* clone() const Q_DECL_OVERRIDE {
+ // HACK ALERT: we are evil! use notmal copying instead!
+
+ PSDResourceBlock *copied = new PSDResourceBlock();
+
+ QBuffer buffer;
+ write(&buffer);
+ copied->read(&buffer);
+
+ return copied;
+ }
+
QString displayText() const {
if (resource) {
return resource->displayText();
}
return i18n("Unparsed Resource Block");
}
bool read(QIODevice* io);
- bool write(QIODevice* io);
+ bool write(QIODevice* io) const;
bool valid();
quint16 identifier;
QString name;
quint32 dataSize;
QByteArray data;
PSDInterpretedResource *resource;
- QString error;
+ mutable 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/plugins/impex/psd/psd_saver.cpp b/plugins/impex/psd/psd_saver.cpp
index 3837715514..49aabe313f 100644
--- a/plugins/impex/psd/psd_saver.cpp
+++ b/plugins/impex/psd/psd_saver.cpp
@@ -1,279 +1,271 @@
/*
* 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_saver.h"
#include <KoColorSpace.h>
#include <KoColorModelStandardIds.h>
#include <KoColorProfile.h>
#include <KoCompositeOp.h>
#include <KoUnit.h>
#include <QFileInfo>
#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_debug.h>
#include <kis_annotation.h>
#include <kis_types.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"
QPair<psd_color_mode, quint16> colormodelid_to_psd_colormode(const QString &colorSpaceId, const QString &colorDepthId)
{
psd_color_mode colorMode = COLORMODE_UNKNOWN;
if (colorSpaceId == RGBAColorModelID.id()) {
colorMode = RGB;
}
else if (colorSpaceId == CMYKAColorModelID.id()) {
colorMode = CMYK;
}
else if (colorSpaceId == GrayAColorModelID.id()) {
colorMode = Grayscale;
}
else if (colorSpaceId == LABAColorModelID.id()) {
colorMode = Lab;
}
quint16 depth = 0;
if (colorDepthId == Integer8BitsColorDepthID.id()) {
depth = 8;
}
else if (colorDepthId == Integer16BitsColorDepthID.id()) {
depth = 16;
}
else if (colorDepthId == Float16BitsColorDepthID.id()) {
depth = 32;
}
else if (colorDepthId == Float32BitsColorDepthID.id()) {
depth = 32;
}
return QPair<psd_color_mode, quint16>(colorMode, depth);
}
PSDSaver::PSDSaver(KisDocument *doc)
: m_image(doc->image())
, m_doc(doc)
, m_stop(false)
{
}
PSDSaver::~PSDSaver()
{
}
-KisImageWSP PSDSaver::image()
+KisImageSP PSDSaver::image()
{
return m_image;
}
#include "kis_sequential_iterator.h"
bool checkIfHasTransparency(KisPaintDeviceSP dev)
{
const QRect deviceBounds = dev->exactBounds();
const QRect imageBounds = dev->defaultBounds()->bounds();
if (deviceBounds.isEmpty() ||
(deviceBounds & imageBounds) != imageBounds) {
return true;
}
const KoColorSpace *cs = dev->colorSpace();
KisSequentialConstIterator it(dev, deviceBounds);
do {
if (cs->opacityU8(it.rawDataConst()) != OPACITY_OPAQUE_U8) {
return true;
}
} while(it.nextPixel());
return false;
}
-KisImageBuilder_Result PSDSaver::buildFile(const QString &filename)
+KisImageBuilder_Result PSDSaver::buildFile(QIODevice *io)
{
if (!m_image)
return KisImageBuilder_RESULT_EMPTY;
- // Open file for writing
- QFile f(filename);
- if (!f.open(QIODevice::WriteOnly)) {
- return KisImageBuilder_RESULT_NOT_LOCAL;
- }
-
const bool haveLayers = m_image->rootLayer()->childCount() > 1 ||
checkIfHasTransparency(m_image->rootLayer()->firstChild()->projection());
// HEADER
PSDHeader header;
header.signature = "8BPS";
header.version = 1;
header.nChannels = haveLayers ?
m_image->colorSpace()->channelCount() :
m_image->colorSpace()->colorChannelCount();
header.width = m_image->width();
header.height = m_image->height();
QPair<psd_color_mode, quint16> colordef = colormodelid_to_psd_colormode(m_image->colorSpace()->colorModelId().id(),
m_image->colorSpace()->colorDepthId().id());
if (colordef.first == COLORMODE_UNKNOWN || colordef.second == 0 || colordef.second == 32) {
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
header.colormode = colordef.first;
header.channelDepth = colordef.second;
- dbgFile << "header" << header << f.pos();
+ dbgFile << "header" << header << io->pos();
- if (!header.write(&f)) {
- dbgFile << "Failed to write header. Error:" << header.error << f.pos();
+ if (!header.write(io)) {
+ dbgFile << "Failed to write header. Error:" << header.error << io->pos();
return KisImageBuilder_RESULT_FAILURE;
}
// COLORMODE BlOCK
PSDColorModeBlock colorModeBlock(header.colormode);
// XXX: check for annotations that contain the duotone spec
KisAnnotationSP annotation = m_image->annotation("DuotoneColormodeBlock");
if (annotation) {
colorModeBlock.duotoneSpecification = annotation->annotation();
}
- dbgFile << "colormode block" << f.pos();
- if (!colorModeBlock.write(&f)) {
- dbgFile << "Failed to write colormode block. Error:" << colorModeBlock.error << f.pos();
+ dbgFile << "colormode block" << io->pos();
+ if (!colorModeBlock.write(io)) {
+ dbgFile << "Failed to write colormode block. Error:" << colorModeBlock.error << io->pos();
return KisImageBuilder_RESULT_FAILURE;
}
// IMAGE RESOURCES SECTION
PSDImageResourceSection resourceSection;
vKisAnnotationSP_it it = m_image->beginAnnotations();
vKisAnnotationSP_it endIt = m_image->endAnnotations();
while (it != endIt) {
KisAnnotationSP annotation = (*it);
if (!annotation || annotation->type().isEmpty()) {
dbgFile << "Warning: empty annotation";
it++;
continue;
}
dbgFile << "Annotation:" << annotation->type() << annotation->description();
if (annotation->type().startsWith(QString("PSD Resource Block:"))) { //
PSDResourceBlock *resourceBlock = dynamic_cast<PSDResourceBlock*>(annotation.data());
if (resourceBlock) {
dbgFile << "Adding PSD Resource Block" << resourceBlock->identifier;
resourceSection.resources[(PSDImageResourceSection::PSDResourceID)resourceBlock->identifier] = resourceBlock;
}
}
it++;
}
// Add resolution block
{
RESN_INFO_1005 *resInfo = new RESN_INFO_1005;
resInfo->hRes = INCH_TO_POINT(m_image->xRes());
resInfo->vRes = INCH_TO_POINT(m_image->yRes());
PSDResourceBlock *block = new PSDResourceBlock;
block->identifier = PSDImageResourceSection::RESN_INFO;
block->resource = resInfo;
resourceSection.resources[PSDImageResourceSection::RESN_INFO] = block;
}
// Add icc block
{
ICC_PROFILE_1039 *profileInfo = new ICC_PROFILE_1039;
profileInfo->icc = m_image->profile()->rawData();
PSDResourceBlock *block = new PSDResourceBlock;
block->identifier = PSDImageResourceSection::ICC_PROFILE;
block->resource = profileInfo;
resourceSection.resources[PSDImageResourceSection::ICC_PROFILE] = block;
}
- dbgFile << "resource section" << f.pos();
- if (!resourceSection.write(&f)) {
- dbgFile << "Failed to write resource section. Error:" << resourceSection.error << f.pos();
+ dbgFile << "resource section" << io->pos();
+ if (!resourceSection.write(io)) {
+ dbgFile << "Failed to write resource section. Error:" << resourceSection.error << io->pos();
return KisImageBuilder_RESULT_FAILURE;
}
// LAYER AND MASK DATA
// Only save layers and masks if there is more than one layer
- dbgFile << "m_image->rootLayer->childCount" << m_image->rootLayer()->childCount() << f.pos();
+ dbgFile << "m_image->rootLayer->childCount" << m_image->rootLayer()->childCount() << io->pos();
if (haveLayers) {
PSDLayerMaskSection layerSection(header);
layerSection.hasTransparency = true;
- if (!layerSection.write(&f, m_image->rootLayer())) {
- dbgFile << "failed to write layer section. Error:" << layerSection.error << f.pos();
+ if (!layerSection.write(io, m_image->rootLayer())) {
+ dbgFile << "failed to write layer section. Error:" << layerSection.error << io->pos();
return KisImageBuilder_RESULT_FAILURE;
}
}
else {
// else write a zero length block
- dbgFile << "No layers, saving empty layers/mask block" << f.pos();
- psdwrite(&f, (quint32)0);
+ dbgFile << "No layers, saving empty layers/mask block" << io->pos();
+ psdwrite(io, (quint32)0);
}
// IMAGE DATA
- dbgFile << "Saving composited image" << f.pos();
+ dbgFile << "Saving composited image" << io->pos();
PSDImageData imagedata(&header);
- if (!imagedata.write(&f, m_image->projection(), haveLayers)) {
+ if (!imagedata.write(io, m_image->projection(), haveLayers)) {
dbgFile << "Failed to write image data. Error:" << imagedata.error;
return KisImageBuilder_RESULT_FAILURE;
}
- f.close();
-
return KisImageBuilder_RESULT_OK;
}
void PSDSaver::cancel()
{
m_stop = true;
}
diff --git a/plugins/impex/psd/psd_saver.h b/plugins/impex/psd/psd_saver.h
index d85a5f5a30..15cceaab22 100644
--- a/plugins/impex/psd/psd_saver.h
+++ b/plugins/impex/psd/psd_saver.h
@@ -1,57 +1,57 @@
/*
* 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_CONVERTER_H_
#define _PSD_CONVERTER_H_
#include <stdio.h>
#include <QObject>
#include <QFileInfo>
#include "kis_types.h"
#include <KisImageBuilderResult.h>
class KisDocument;
class PSDSaver : public QObject {
Q_OBJECT
public:
PSDSaver(KisDocument *doc);
virtual ~PSDSaver();
public:
- KisImageBuilder_Result buildFile(const QString &filename);
+ KisImageBuilder_Result buildFile(QIODevice *io);
- KisImageWSP image();
+ KisImageSP image();
public Q_SLOTS:
virtual void cancel();
private:
- KisImageWSP m_image;
+ KisImageSP m_image;
KisDocument *m_doc;
bool m_stop;
};
#endif
diff --git a/plugins/impex/psd/tests/kis_psd_test.cpp b/plugins/impex/psd/tests/kis_psd_test.cpp
index a3bfca00ec..b507f86122 100644
--- a/plugins/impex/psd/tests/kis_psd_test.cpp
+++ b/plugins/impex/psd/tests/kis_psd_test.cpp
@@ -1,341 +1,336 @@
/*
* 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>
#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 <resources/KoPattern.h>
#include "kis_group_layer.h"
#include "kis_psd_layer_style.h"
#include "kis_paint_device_debug_utils.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);
- dbgKrita << s;
+ KisImportExportFilter::ConversionStatus status = manager.importDocument(sourceFileInfo.absoluteFilePath(), QString());
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);
+ KisImportExportFilter::ConversionStatus status = manager.importDocument(fileInfo.absoluteFilePath(), QString());
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(QUrl::fromLocalFile(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(QUrl::fromLocalFile(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);
Q_FOREACH (QFileInfo sourceFileInfo, dirSources.entryInfoList()) {
Q_ASSERT(sourceFileInfo.exists());
if (sourceFileInfo.isHidden() || sourceFileInfo.isDir()) {
continue;
}
if (sourceFileInfo.fileName() != "ml_cmyk_16b.psd") {
//continue;
}
//dbgKrita << "Opening" << ppVar(sourceFileInfo.fileName());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
if (!doc->image()) {
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);
Q_FOREACH (QFileInfo sourceFileInfo, dirSources.entryInfoList()) {
Q_ASSERT(sourceFileInfo.exists());
if (sourceFileInfo.isHidden() || sourceFileInfo.isDir()) {
continue;
}
if (sourceFileInfo.fileName() != "sl_rgb_8b.psd") {
//continue;
}
dbgKrita << "Opening" << ppVar(sourceFileInfo.fileName());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
if (!doc->image()) {
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);
dbgKrita << "Saving" << ppVar(dstFileInfo.fileName());
bool retval = doc->saveAs(QUrl::fromLocalFile(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_MAIN(KisPSDTest)
diff --git a/plugins/impex/qml/CMakeLists.txt b/plugins/impex/qml/CMakeLists.txt
index e62187f12a..2f3826d5d1 100644
--- a/plugins/impex/qml/CMakeLists.txt
+++ b/plugins/impex/qml/CMakeLists.txt
@@ -1,10 +1,10 @@
set(kritaqmlexport_SOURCES
qml_converter.cc
qml_export.cc
)
add_library(kritaqmlexport MODULE ${kritaqmlexport_SOURCES})
-target_link_libraries(kritaqmlexport kritaui )
+target_link_libraries(kritaqmlexport kritaui kritaimpex)
install(TARGETS kritaqmlexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
diff --git a/plugins/impex/qml/krita_qml_export.json b/plugins/impex/qml/krita_qml_export.json
index cac04141f8..d0b5ed4d7f 100644
--- a/plugins/impex/qml/krita_qml_export.json
+++ b/plugins/impex/qml/krita_qml_export.json
@@ -1,14 +1,13 @@
{
"Icon": "",
"Id": "Krita QML Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "text/x-qml",
- "X-KDE-Import": "application/x-krita",
"X-KDE-Library": "kritaqmlexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "qml"
}
diff --git a/plugins/impex/qml/qml_converter.cc b/plugins/impex/qml/qml_converter.cc
index 725f526713..99c18a89d8 100644
--- a/plugins/impex/qml/qml_converter.cc
+++ b/plugins/impex/qml/qml_converter.cc
@@ -1,100 +1,92 @@
/*
* 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_converter.h"
#include <QFileInfo>
#include <QDir>
#include <QFileInfo>
#include <kis_image.h>
#include <kis_group_layer.h>
#define SPACE " "
QMLConverter::QMLConverter()
{
}
QMLConverter::~QMLConverter()
{
}
-KisImageBuilder_Result QMLConverter::buildFile(const QString &filename, KisImageWSP image)
+KisImageBuilder_Result QMLConverter::buildFile(const QString &filename, QIODevice *io, KisImageSP image)
{
- QFile file(filename);
- if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- return KisImageBuilder_RESULT_FAILURE;
- }
-
- QTextStream out(&file);
+ QTextStream out(io);
out.setCodec("UTF-8");
out << "import QtQuick 1.1" << "\n\n";
out << "Rectangle {\n";
writeInt(out, 1, "width", image->width());
writeInt(out, 1, "height", image->height());
out << "\n";
- QFileInfo info(file);
+ QFileInfo info(filename);
KisNodeSP node = image->rootLayer()->firstChild();
QString imageDir = info.baseName() + "_images";
QString imagePath = info.absolutePath() + '/' + imageDir;
if (node) {
QDir dir;
dir.mkpath(imagePath);
}
dbgFile << "Saving images to " << imagePath;
while(node) {
KisPaintDeviceSP projection = node->projection();
QRect rect = projection->exactBounds();
QImage qmlImage = projection->convertToQImage(0, rect.x(), rect.y(), rect.width(), rect.height());
QString name = node->name().replace(' ', '_').toLower();
QString fileName = name + ".png";
qmlImage.save(imagePath +'/'+ fileName);
out << SPACE << "Image {\n";
writeString(out, 2, "id", name);
writeInt(out, 2, "x", rect.x());
writeInt(out, 2, "y", rect.y());
writeInt(out, 2, "width", rect.width());
writeInt(out, 2, "height", rect.height());
writeString(out, 2, "source", "\"" + imageDir + '/' + fileName + "\"" );
writeString(out, 2, "opacity", QString().setNum(node->opacity()/255.0));
out << SPACE << "}\n";
node = node->nextSibling();
}
out << "}\n";
-
- file.close();
-
return KisImageBuilder_RESULT_OK;
}
void QMLConverter::writeString(QTextStream& out, int spacing, const QString& setting, const QString& value) {
for (int space = 0; space < spacing; space++) {
out << SPACE;
}
out << setting << ": " << value << "\n";
}
void QMLConverter::writeInt(QTextStream& out, int spacing, const QString& setting, int value) {
writeString(out, spacing, setting, QString::number(value));
}
diff --git a/plugins/impex/qml/qml_converter.h b/plugins/impex/qml/qml_converter.h
index 79b46762d1..9d20405d9f 100644
--- a/plugins/impex/qml/qml_converter.h
+++ b/plugins/impex/qml/qml_converter.h
@@ -1,44 +1,44 @@
/*
* 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.
*/
#ifndef _QML_CONVERTER_H_
#define _QML_CONVERTER_H_
#include <stdio.h>
#include <QObject>
#include <QFileInfo>
#include "kis_types.h"
#include <KisImageBuilderResult.h>
class QMLConverter : public QObject
{
Q_OBJECT
public:
QMLConverter();
virtual ~QMLConverter();
public:
- KisImageBuilder_Result buildFile(const QString &filename, KisImageWSP image);
+ KisImageBuilder_Result buildFile(const QString &filename, QIODevice *io, KisImageSP image);
private:
void writeString(QTextStream& out, int spacing, const QString& setting, const QString& value);
void writeInt(QTextStream& out, int spacing, const QString& setting, int value);
};
#endif
diff --git a/plugins/impex/qml/qml_export.cc b/plugins/impex/qml/qml_export.cc
index 2665c595bf..45d7fdf9b0 100644
--- a/plugins/impex/qml/qml_export.cc
+++ b/plugins/impex/qml/qml_export.cc
@@ -1,81 +1,73 @@
/*
* 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 <QFileInfo>
#include <QApplication>
-#include <KisFilterChain.h>
-
+#include <KisExportCheckRegistry.h>
#include <KisDocument.h>
#include <kis_image.h>
#include "qml_converter.h"
+#include <KoColorModelStandardIds.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, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus QMLExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- Q_UNUSED(from);
- Q_UNUSED(to);
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- dbgKrita << "input " << input;
- if (!input) {
- return KisImportExportFilter::NoDocumentCreated;
- }
-
- dbgKrita << "filename " << input;
-
- if (filename.isEmpty()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- KisImageWSP image = input->image();
+ KisImageSP image = document->image();
Q_CHECK_PTR(image);
QMLConverter converter;
- KisImageBuilder_Result result = converter.buildFile(filename, image);
+ KisImageBuilder_Result result = converter.buildFile(filename(), io, image);
if (result == KisImageBuilder_RESULT_OK) {
dbgFile << "success !";
return KisImportExportFilter::OK;
}
dbgFile << " Result =" << result;
return KisImportExportFilter::InternalError;
}
+void QMLExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "QML");
+ }
+
+
+
#include <qml_export.moc>
diff --git a/plugins/impex/qml/qml_export.h b/plugins/impex/qml/qml_export.h
index 56292b5e4e..3fddabd1f5 100644
--- a/plugins/impex/qml/qml_export.h
+++ b/plugins/impex/qml/qml_export.h
@@ -1,35 +1,36 @@
/*
* 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.
*/
#ifndef _QML_EXPORT_H_
#define _QML_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class QMLExport : public KisImportExportFilter
{
Q_OBJECT
public:
QMLExport(QObject *parent, const QVariantList &);
virtual ~QMLExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/raw/kis_raw_import.cpp b/plugins/impex/raw/kis_raw_import.cpp
index 194bb4b6a7..41cdd12c8c 100644
--- a/plugins/impex/raw/kis_raw_import.cpp
+++ b/plugins/impex/raw/kis_raw_import.cpp
@@ -1,215 +1,192 @@
/*
* 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_raw_import.h"
#include <kpluginfactory.h>
#include <KoDialog.h>
-#include <KisFilterChain.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceTraits.h>
#include "kis_debug.h"
#include "KisDocument.h"
#include "kis_image.h"
#include "kis_paint_device.h"
#include "kis_transaction.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_iterator_ng.h"
#include <kdcraw.h>
#include <libkdcraw_version.h>
using namespace KDcrawIface;
K_PLUGIN_FACTORY_WITH_JSON(KisRawImportFactory, "krita_raw_import.json",
registerPlugin<KisRawImport>();)
KisRawImport::KisRawImport(QObject *parent, const QVariantList &)
: KisImportExportFilter(parent)
{
m_dialog = new KoDialog();
m_dialog->enableButtonApply(false);
QWidget* widget = new QWidget;
m_rawWidget.setupUi(widget);
m_dialog->setMainWidget(widget);
connect(m_rawWidget.pushButtonUpdate, SIGNAL(clicked()), this, SLOT(slotUpdatePreview()));
}
KisRawImport::~KisRawImport()
{
delete m_dialog;
}
inline quint16 correctIndian(quint16 v)
{
#if KDCRAW_VERSION < 0x000400
return ((v & 0x00FF) << 8) | ((v & 0xFF00 >> 8));
#else
return v;
#endif
}
-KisImportExportFilter::ConversionStatus KisRawImport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisRawImport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile << from << " " << to << "";
- if (/*from != "image/x-raw" || */to != "application/x-krita") { // too many from to check, and I don't think it can happen an unsupported from
- return KisImportExportFilter::NotImplemented;
- }
-
- dbgFile << "Krita importing from Raw";
-
- KisDocument * doc = outputDocument();
- if (!doc) {
- return KisImportExportFilter::NoDocumentCreated;
- }
-
- doc -> prepareForImport();
-
- QString filename = inputFile();
-
- if (filename.isEmpty()) {
- return KisImportExportFilter::FileNotFound;
- }
-
-
// Show dialog
m_dialog->setCursor(Qt::ArrowCursor);
QApplication::setOverrideCursor(Qt::ArrowCursor);
#if KDCRAW_VERSION < 0x010200
m_rawWidget.rawSettings->setDefaultSettings();
#else
m_rawWidget.rawSettings->resetToDefault();
#endif
slotUpdatePreview();
if (m_dialog->exec() == QDialog::Accepted) {
QApplication::setOverrideCursor(Qt::WaitCursor);
// Do the decoding
// TODO: it would probably be better done in a thread, while an other thread simulate that the application is still living (or even better if libkdcraw was giving us some progress report
QByteArray imageData;
RawDecodingSettings settings = rawDecodingSettings();
settings.sixteenBitsImage = true;
int width, height, rgbmax;
KDcraw dcraw;
- if (!dcraw.decodeRAWImage(inputFile(), settings, imageData, width, height, rgbmax)) return KisImportExportFilter::CreationError;
+ if (!dcraw.decodeRAWImage(filename(), settings, imageData, width, height, rgbmax)) return KisImportExportFilter::CreationError;
QApplication::restoreOverrideCursor();
// Init the image
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb16();
- KisImageWSP image = new KisImage(doc->createUndoStore(), width, height, cs, filename);
+ KisImageSP image = new KisImage(document->createUndoStore(), width, height, cs, filename());
if (image.isNull()) return KisImportExportFilter::CreationError;
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), quint8_MAX);
image->addNode(layer, image->rootLayer());
if (layer.isNull()) return KisImportExportFilter::CreationError;
KisPaintDeviceSP device = layer->paintDevice();
if (device.isNull()) return KisImportExportFilter::CreationError;
// Copy the data
KisHLineIteratorSP it = device->createHLineIteratorNG(0, 0, width);
for (int y = 0; y < height; ++y) {
do {
KoBgrU16Traits::Pixel* pixel = reinterpret_cast<KoBgrU16Traits::Pixel*>(it->rawData());
- quint16* ptr = ((quint16*)imageData.constData()) + (y * width + it->x()) * 3;
+ quint16* ptr = ((quint16*)imageData.data()) + (y * width + it->x()) * 3;
#if KDCRAW_VERSION < 0x000400
pixel->red = correctIndian(ptr[2]);
pixel->green = correctIndian(ptr[1]);
pixel->blue = correctIndian(ptr[0]);
#else
pixel->red = correctIndian(ptr[0]);
pixel->green = correctIndian(ptr[1]);
pixel->blue = correctIndian(ptr[2]);
#endif
pixel->alpha = 0xFFFF;
} while (it->nextPixel());
it->nextRow();
}
QApplication::restoreOverrideCursor();
- doc->setCurrentImage(image);
+ document->setCurrentImage(image);
return KisImportExportFilter::OK;
}
QApplication::restoreOverrideCursor();
return KisImportExportFilter::UserCancelled;
}
void KisRawImport::slotUpdatePreview()
{
QByteArray imageData;
RawDecodingSettings settings = rawDecodingSettings();
settings.sixteenBitsImage = false;
int width, height, rgbmax;
KDcraw dcraw;
- if (dcraw.decodeHalfRAWImage(inputFile(), settings, imageData, width, height, rgbmax)) {
+ if (dcraw.decodeHalfRAWImage(filename(), settings, imageData, width, height, rgbmax)) {
QImage image(width, height, QImage::Format_RGB32);
for (int y = 0; y < height; ++y) {
QRgb *pixel= reinterpret_cast<QRgb *>(image.scanLine(y));
for (int x = 0; x < width; ++x) {
- quint8* ptr = ((quint8*)imageData.constData()) + (y * width + x) * 3;
+ quint8* ptr = ((quint8*)imageData.data()) + (y * width + x) * 3;
pixel[x] = qRgb(ptr[0], ptr[1], ptr[2]);
}
}
m_rawWidget.preview->setPixmap(QPixmap::fromImage(image));
}
}
RawDecodingSettings KisRawImport::rawDecodingSettings()
{
#if KDCRAW_VERSION < 0x010200
RawDecodingSettings settings;
settings.sixteenBitsImage = true;
settings.brightness = m_rawWidget.rawSettings->brightness();
settings.RAWQuality = m_rawWidget.rawSettings->quality();
settings.outputColorSpace = m_rawWidget.rawSettings->outputColorSpace();
settings.RGBInterpolate4Colors = m_rawWidget.rawSettings->useFourColor();
settings.DontStretchPixels = m_rawWidget.rawSettings->useDontStretchPixels();
settings.unclipColors = m_rawWidget.rawSettings->unclipColor();
settings.whiteBalance = m_rawWidget.rawSettings->whiteBalance();
settings.customWhiteBalance = m_rawWidget.rawSettings->customWhiteBalance();
settings.customWhiteBalanceGreen = m_rawWidget.rawSettings->customWhiteBalanceGreen();
settings.enableBlackPoint = m_rawWidget.rawSettings->useBlackPoint();
settings.blackPoint = m_rawWidget.rawSettings->blackPoint();
settings.enableNoiseReduction = m_rawWidget.rawSettings->useNoiseReduction();
settings.NRThreshold = m_rawWidget.rawSettings->NRThreshold();
settings.enableCACorrection = m_rawWidget.rawSettings->useCACorrection();
settings.caMultiplier[0] = m_rawWidget.rawSettings->caRedMultiplier();
settings.caMultiplier[1] = m_rawWidget.rawSettings->caBlueMultiplier();
return settings;
#else
return m_rawWidget.rawSettings->settings();
#endif
}
#include "kis_raw_import.moc"
diff --git a/plugins/impex/raw/kis_raw_import.h b/plugins/impex/raw/kis_raw_import.h
index ed56323937..3686c157eb 100644
--- a/plugins/impex/raw/kis_raw_import.h
+++ b/plugins/impex/raw/kis_raw_import.h
@@ -1,58 +1,58 @@
/*
* 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_RAW_IMPORT_H_
#define KIS_RAW_IMPORT_H_
#include <KisImportExportFilter.h>
#include "ui_wdgrawimport.h"
class KoDialog;
class WdgRawImport;
namespace KDcrawIface
{
class RawDecodingSettings;
}
class KisRawImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisRawImport(QObject *parent, const QVariantList &);
virtual ~KisRawImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
private Q_SLOTS:
void slotUpdatePreview();
private:
KDcrawIface::RawDecodingSettings rawDecodingSettings();
private:
Ui::WdgRawImport m_rawWidget;
KoDialog* m_dialog;
};
#endif // KIS_RAW_IMPORT_H_
diff --git a/plugins/impex/raw/krita_raw_import.json b/plugins/impex/raw/krita_raw_import.json
index 440b26d88c..e818ef5114 100644
--- a/plugins/impex/raw/krita_raw_import.json
+++ b/plugins/impex/raw/krita_raw_import.json
@@ -1,13 +1,12 @@
{
"Id": "Krita RAW Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
"X-KDE-Import": "image/x-krita-raw",
"X-KDE-Library": "krita_raw_import",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "nef,cr2,sr2,crw,pef,x3f,kdc,mrw,arw,k25,dcr,orf,raw,raw,raf,srf,dng"
}
diff --git a/plugins/impex/spriter/CMakeLists.txt b/plugins/impex/spriter/CMakeLists.txt
index fbc037b8d6..40f20764ef 100644
--- a/plugins/impex/spriter/CMakeLists.txt
+++ b/plugins/impex/spriter/CMakeLists.txt
@@ -1,17 +1,17 @@
include_directories(SYSTEM
${Boost_INCLUDE_DIRS}
)
# export
set(kritaspriterexport_SOURCES
kis_spriter_export.cpp
)
add_library(kritaspriterexport MODULE ${kritaspriterexport_SOURCES})
-target_link_libraries(kritaspriterexport kritaui )
+target_link_libraries(kritaspriterexport kritaui kritaimpex)
install(TARGETS kritaspriterexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_spriter.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/spriter/kis_spriter_export.cpp b/plugins/impex/spriter/kis_spriter_export.cpp
index c9ba1a1401..3f26f7cbe1 100644
--- a/plugins/impex/spriter/kis_spriter_export.cpp
+++ b/plugins/impex/spriter/kis_spriter_export.cpp
@@ -1,627 +1,609 @@
/*
* Copyright (c) 2016 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_spriter_export.h"
#include <QApplication>
#include <QCheckBox>
#include <QDomDocument>
#include <QFileInfo>
-#include <QMessageBox>
#include <QSlider>
#include <QFileInfo>
#include <QDir>
#include <QFileInfo>
#include <QApplication>
#include <kpluginfactory.h>
#include <KoColorSpaceConstants.h>
#include <KoColorSpaceRegistry.h>
+#include <KisExportCheckRegistry.h>
+#include <KisImportExportManager.h>
+
#include <KisDocument.h>
-#include <KisFilterChain.h>
#include <kis_group_layer.h>
#include <kis_image.h>
-#include <KisImportExportManager.h>
#include <kis_layer.h>
#include <kis_node.h>
#include <kis_painter.h>
#include <kis_paint_layer.h>
#include <kis_shape_layer.h>
#include <kis_file_layer.h>
#include <kis_clone_layer.h>
#include <kis_generator_layer.h>
#include <kis_adjustment_layer.h>
#include <KisPart.h>
#include <kis_types.h>
#include <kis_png_converter.h>
#include <kis_global.h> // for KisDegreesToRadians
#include <kis_fast_math.h>
#include <math.h>
#include <kis_dom_utils.h>
K_PLUGIN_FACTORY_WITH_JSON(KisSpriterExportFactory, "krita_spriter_export.json", registerPlugin<KisSpriterExport>();)
KisSpriterExport::KisSpriterExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisSpriterExport::~KisSpriterExport()
{
}
bool KisSpriterExport::savePaintDevice(KisPaintDeviceSP dev, const QString &fileName)
{
QFileInfo fi(fileName);
QDir d = fi.absoluteDir();
d.mkpath(d.path());
QRect rc = m_image->bounds().intersected(dev->exactBounds());
if (!KisPNGConverter::isColorSpaceSupported(dev->colorSpace())) {
dev = new KisPaintDevice(*dev.data());
KUndo2Command *cmd = dev->convertTo(KoColorSpaceRegistry::instance()->rgb8());
delete cmd;
}
- QFile fp(fileName);
-
KisPNGOptions options;
options.forceSRGB = true;
vKisAnnotationSP_it beginIt = m_image->beginAnnotations();
vKisAnnotationSP_it endIt = m_image->endAnnotations();
KisPNGConverter converter(0);
- KisImageBuilder_Result res = converter.buildFile(&fp, rc, m_image->xRes(), m_image->yRes(), dev, beginIt, endIt, options, 0);
+ KisImageBuilder_Result res = converter.buildFile(fileName, rc, m_image->xRes(), m_image->yRes(), dev, beginIt, endIt, options, 0);
return (res == KisImageBuilder_RESULT_OK);
}
void KisSpriterExport::parseFolder(KisGroupLayerSP parentGroup, const QString &folderName, const QString &basePath)
{
// qDebug() << "parseFolder: parent" << parentGroup->name()
// << "folderName" << folderName
// << "basepath" << basePath;
static int folderId = 0;
QString pathName;
if (!folderName.isEmpty()) {
pathName = folderName + "/";
}
KisNodeSP child = parentGroup->lastChild();
while (child) {
if (child->visible() && child->inherits("KisGroupLayer")) {
parseFolder(qobject_cast<KisGroupLayer*>(child.data()), child->name().split(" ").first(), basePath + "/" + pathName);
}
child = child->prevSibling();
}
Folder folder;
folder.id = folderId;
folder.name = folderName;
folder.groupName = parentGroup->name();
int fileId = 0;
child = parentGroup->lastChild();
while (child) {
if (child->visible() && !child->inherits("KisGroupLayer") && !child->inherits("KisMask")) {
QRectF rc = m_image->bounds().intersected(child->exactBounds());
QString layerBaseName = child->name().split(" ").first();
SpriterFile file;
file.id = fileId++;
file.pathName = pathName;
file.baseName = layerBaseName;
file.layerName = child->name();
file.name = folderName + "/" + layerBaseName + ".png";
qreal xmin = rc.left();
qreal ymin = rc.top();
qreal xmax = rc.right();
qreal ymax = rc.bottom();
file.width = xmax - xmin;
file.height = ymax - ymin;
file.x = xmin;
file.y = ymin;
//qDebug() << "Created file" << file.id << file.name << file.pathName << file.baseName << file.width << file.height << file.layerName;
savePaintDevice(child->projection(), basePath + file.name);
folder.files.append(file);
}
child = child->prevSibling();
}
if (folder.files.size() > 0) {
//qDebug() << "Adding folder" << folder.id << folder.name << folder.groupName << folder.files.length();
m_folders.append(folder);
folderId++;
}
}
Bone *KisSpriterExport::parseBone(const Bone *parent, KisGroupLayerSP groupLayer)
{
static int boneId = 0;
QString groupBaseName = groupLayer->name().split(" ").first();
Bone *bone = new Bone;
bone->id = boneId++;
bone->parentBone = parent;
bone->name = groupBaseName;
if (m_boneLayer) {
QRectF rc = m_image->bounds().intersected(m_boneLayer->exactBounds());
qreal xmin = rc.left();
qreal ymin = rc.top();
qreal xmax = rc.right();
qreal ymax = rc.bottom();
bone->x = (xmin + xmax) / 2;
bone->y = -(ymin + ymax) / 2;
bone->width = xmax - xmin;
bone->height = ymax - ymin;
}
else {
bone->x = 0.0;
bone->y = 0.0;
bone->width = 0.0;
bone->height = 0.0;
}
if (parent) {
bone->localX = bone->x - parent->x;
bone->localY = bone->y - parent->y;
}
else {
bone->localX = bone->x;
bone->localY = bone->y;
}
bone->localAngle = 0.0;
bone->localScaleX = 1.0;
bone->localScaleY = 1.0;
KisNodeSP child = groupLayer->lastChild();
while (child) {
if (child->visible() && child->inherits("KisGroupLayer")) {
bone->bones.append(parseBone(bone, qobject_cast<KisGroupLayer*>(child.data())));
}
child = child->prevSibling();
}
//qDebug() << "Created bone" << bone->id << "with" << bone->bones.size() << "bones";
return bone;
}
void copyBone(Bone *startBone)
{
startBone->fixLocalX = startBone->localX;
startBone->fixLocalY = startBone->localY;
startBone->fixLocalAngle = startBone->localAngle;
startBone->fixLocalScaleX= startBone->localScaleX;
startBone->fixLocalScaleY= startBone->localScaleY;
Q_FOREACH(Bone *child, startBone->bones) {
copyBone(child);
}
}
void KisSpriterExport::fixBone(Bone *bone)
{
qreal boneLocalAngle = 0;
qreal boneLocalScaleX = 1;
if (bone->bones.length() >= 1) {
// if a bone has one or more children, point at first child
Bone *childBone = bone->bones[0];
qreal dx = childBone->x - bone->x;
qreal dy = childBone->y - bone->y;
if (qAbs(dx) > 0 || qAbs(dy) > 0) {
boneLocalAngle = KisFastMath::atan2(dy, dx);
boneLocalScaleX = sqrt(dx * dx + dy * dy) / 200;
}
}
else if (bone->parentBone) {
// else, if bone has parent, point away from parent
qreal dx = bone->x - bone->parentBone->x;
qreal dy = bone->y - bone->parentBone->y;
if (qAbs(dx) > 0 || qAbs(dy) > 0) {
boneLocalAngle = KisFastMath::atan2(dy, dx);
boneLocalScaleX = sqrt(dx * dx + dy * dy) / 200;
}
}
// adjust bone angle
bone->fixLocalAngle += boneLocalAngle;
bone->fixLocalScaleX *= boneLocalScaleX;
// rotate all the child bones back to world position
for (int i = 0; i < bone->bones.length(); ++i) {
Bone *childBone = bone->bones[i];
qreal tx = childBone->fixLocalX;
qreal ty = childBone->fixLocalY;
childBone->fixLocalX = tx * cos(-boneLocalAngle) - ty * sin(-boneLocalAngle);
childBone->fixLocalY = tx * sin(-boneLocalAngle) + ty * cos(-boneLocalAngle);
childBone->fixLocalX /= boneLocalScaleX;
childBone->fixLocalAngle -= boneLocalAngle;
childBone->fixLocalScaleX /= boneLocalScaleX;
}
// rotate all the child objects back to world position
for (int i = 0; i < m_objects.length(); ++i) {
if (m_objects[i].bone == bone) {
m_objects[i].fixLocalAngle -= boneLocalAngle;
m_objects[i].fixLocalScaleX /= boneLocalScaleX;
}
}
// process all child bones
for (int i = 0; i < bone->bones.length(); ++i) {
fixBone(bone->bones[i]);
}
}
void KisSpriterExport::writeBoneRef(const Bone *bone, QDomElement &key, QDomDocument &scml)
{
if (!bone) return;
QDomElement boneRef = scml.createElement("bone_ref");
key.appendChild(boneRef);
boneRef.setAttribute("id", bone->id);
if (bone->parentBone) {
boneRef.setAttribute("parent", bone->parentBone->id);
}
boneRef.setAttribute("timeline", m_timelineid++);
boneRef.setAttribute("key", "0");
Q_FOREACH(const Bone *childBone, bone->bones) {
writeBoneRef(childBone, key, scml);
}
}
void KisSpriterExport::writeBone(const Bone *bone, QDomElement &animation, QDomDocument &scml)
{
if (!bone) return;
QDomElement timeline = scml.createElement("timeline");
animation.appendChild(timeline);
timeline.setAttribute("id", m_timelineid);
timeline.setAttribute("name", bone->name);
timeline.setAttribute("object_type", "bone");
QDomElement key = scml.createElement("key");
timeline.appendChild(key);
key.setAttribute("id", "0");
key.setAttribute("spin", 0);
QDomElement boneEl = scml.createElement("bone");
key.appendChild(boneEl);
boneEl.setAttribute("x", QString::number(bone->fixLocalX, 'f', 2));
boneEl.setAttribute("y", QString::number(bone->fixLocalY, 'f', 2));
boneEl.setAttribute("angle", QString::number(bone->fixLocalAngle, 'f', 2));
boneEl.setAttribute("scale_x", QString::number(bone->fixLocalScaleX, 'f', 2));
boneEl.setAttribute("scale_y", QString::number(bone->fixLocalScaleY, 'f', 2));
m_timelineid++;
Q_FOREACH(const Bone *childBone, bone->bones) {
writeBone(childBone, animation, scml);
}
}
void KisSpriterExport::fillScml(QDomDocument &scml, const QString &entityName)
{
//qDebug() << "Creating scml" << entityName;
QDomElement root = scml.createElement("spriter_data");
scml.appendChild(root);
root.setAttribute("scml_version", 1);
root.setAttribute("generator", "krita");
root.setAttribute("generator_version", qApp->applicationVersion());
Q_FOREACH(const Folder &folder, m_folders) {
QDomElement fe = scml.createElement("folder");
root.appendChild(fe);
fe.setAttribute("id", folder.id);
fe.setAttribute("name", folder.name);
Q_FOREACH(const SpriterFile &file, folder.files) {
QDomElement fileElement = scml.createElement("file");
fe.appendChild(fileElement);
fileElement.setAttribute("id", file.id);
fileElement.setAttribute("name", file.name);
fileElement.setAttribute("width", QString::number(file.width, 'f', 2));
fileElement.setAttribute("height", QString::number(file.height, 'f', 2));
}
}
// entity
QDomElement entity = scml.createElement("entity");
root.appendChild(entity);
entity.setAttribute("id", "0");
entity.setAttribute("name", entityName);
// entity/animation
QDomElement animation = scml.createElement("animation");
entity.appendChild(animation);
animation.setAttribute("id", "0");
animation.setAttribute("name", "default");
animation.setAttribute("length", "1000");
animation.setAttribute("looping", "false");
// entity/animation/mainline
QDomElement mainline = scml.createElement("mainline");
animation.appendChild(mainline);
QDomElement key = scml.createElement("key");
mainline.appendChild(key);
key.setAttribute("id", "0");
m_timelineid = 0;
writeBoneRef(m_rootBone, key, scml);
Q_FOREACH(const SpriterObject &object, m_objects) {
QDomElement oe = scml.createElement("object_ref");
key.appendChild(oe);
oe.setAttribute("id", object.id);
if (object.bone) {
oe.setAttribute("parent", object.bone->id);
}
oe.setAttribute("timeline", m_timelineid++);
oe.setAttribute("key", "0");
oe.setAttribute("z_index", object.id);
}
// entity/animation/timeline
m_timelineid = 0;
if (m_rootBone) {
writeBone(m_rootBone, animation, scml);
}
Q_FOREACH(const SpriterObject &object, m_objects) {
Folder folder;
Q_FOREACH(const Folder & f, m_folders) {
if (f.id == object.folderId) {
folder = f;
break;
}
}
SpriterFile file;
file.id = -1;
Q_FOREACH(const SpriterFile &f, folder.files) {
if (f.id == object.fileId) {
file = f;
break;
}
}
Q_ASSERT(file.id >= 0);
QString objectName = "object-" + file.baseName;
qreal pivotX = (0.0 -(object.fixLocalX / file.width));
qreal pivotY = (1.0 -(object.fixLocalY / file.height));
QDomElement timeline = scml.createElement("timeline");
animation.appendChild(timeline);
timeline.setAttribute("id", m_timelineid++);
timeline.setAttribute("name", objectName);
QDomElement key = scml.createElement("key");
timeline.appendChild(key);
key.setAttribute("id", "0");
key.setAttribute("spin", "0");
QDomElement objectEl = scml.createElement("object");
key.appendChild(objectEl);
objectEl.setAttribute("folder", object.folderId);
objectEl.setAttribute("file", object.fileId);
objectEl.setAttribute("x", "0");
objectEl.setAttribute("y", "0");
objectEl.setAttribute("pivot_x", QString::number(pivotX, 'f', 2));
objectEl.setAttribute("pivot_y", QString::number(pivotY, 'f', 2));
objectEl.setAttribute("angle", QString::number(kisRadiansToDegrees(object.fixLocalAngle), 'f', 2));
objectEl.setAttribute("scale_x", QString::number(object.fixLocalScaleX, 'f', 2));
objectEl.setAttribute("scale_y", QString::number(object.fixLocalScaleY, 'f', 2));
}
}
Bone *findBoneByName(Bone *startBone, const QString &name)
{
if (!startBone) return 0;
//qDebug() << "findBoneByName" << name << "starting with" << startBone->name;
if (startBone->name == name) {
return startBone;
}
Q_FOREACH(Bone *child, startBone->bones) {
//qDebug() << "looking for" << name << "found" << child->name;
if (child->name == name) {
return child;
}
Bone *grandChild = findBoneByName(child, name);
if (grandChild){
return grandChild;
}
}
return 0;
}
-KisImportExportFilter::ConversionStatus KisSpriterExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisSpriterExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile << "Spriter export! From:" << from << ", To:" << to << "" << outputFile();
-
- if (from != "application/x-krita") {
- return KisImportExportFilter::NotImplemented;
- }
-
- KisDocument *input = inputDocument();
+ QFileInfo fi(filename());
- QString filename = outputFile();
- if (filename.isEmpty()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- if (!input) {
- return KisImportExportFilter::NoDocumentCreated;
- }
-
- QFileInfo fi(filename);
- //qDebug() << "filename" << filename << fi.absolutePath() << fi.baseName();
-
- m_image = input->image();
+ m_image = document->image();
if (m_image->rootLayer()->childCount() == 0) {
return KisImportExportFilter::UsageError;
}
- KisGroupLayerSP root = input->image()->rootLayer();
+ KisGroupLayerSP root = document->image()->rootLayer();
m_boneLayer = qobject_cast<KisLayer*>(root->findChildByName("bone").data());
//qDebug() << "Found boneLayer" << m_boneLayer;
m_rootLayer= qobject_cast<KisGroupLayer*>(root->findChildByName("root").data());
//qDebug() << "Fond rootLayer" << m_rootLayer;
- parseFolder(input->image()->rootLayer(), "", fi.absolutePath());
+ parseFolder(document->image()->rootLayer(), "", fi.absolutePath());
m_rootBone = 0;
if (m_rootLayer) {
m_rootBone = parseBone(0, m_rootLayer);
}
// Generate objects
int objectId = 0;
for (int folderIndex = 0, folderCount = m_folders.size(); folderIndex < folderCount; ++folderIndex) {
Folder folder = m_folders[folderCount - 1 - folderIndex];
for (int fileIndex = 0, fileCount = folder.files.size(); fileIndex < fileCount; ++ fileIndex) {
SpriterFile file = folder.files[fileCount - 1 - fileIndex];
SpriterObject spriterObject;
spriterObject.id = objectId++;
spriterObject.folderId = folder.id;
spriterObject.fileId = file.id;
spriterObject.x = file.x;
spriterObject.y = -file.y;
Bone *bone = 0;
//qDebug() << "file layername" << file.layerName;
// layer.name format: "base_name bone(bone_name) slot(slot_name)"
if (file.layerName.contains("bone(")) {
int start = file.layerName.indexOf("bone(") + 5;
int end = file.layerName.indexOf(')', start);
QString boneName = file.layerName.mid(start, end - start);
bone = findBoneByName(m_rootBone, boneName);
}
// layer.name format: "base_name"
if (!bone && m_rootBone) {
bone = findBoneByName(m_rootBone, file.layerName);
}
// group.name format: "base_name bone(bone_name)"
if (!bone && m_rootBone) {
if (folder.groupName.contains("bone(")) {
int start = folder.groupName.indexOf("bone(") + 5;
int end = folder.groupName.indexOf(')', start);
QString boneName = folder.groupName.mid(start, end - start);
bone = findBoneByName(m_rootBone, boneName);
}
// group.name format: "base_name"
if (!bone) {
bone = findBoneByName(m_rootBone, folder.groupName);
}
}
if (!bone) {
bone = m_rootBone;
}
if (bone) {
spriterObject.bone = bone;
spriterObject.localX = spriterObject.x - bone->x;
spriterObject.localY = spriterObject.y - bone->y;
}
else {
spriterObject.bone = 0;
spriterObject.localX = spriterObject.x;
spriterObject.localY = spriterObject.y;
}
spriterObject.localAngle = 0;
spriterObject.localScaleX = 1.0;
spriterObject.localScaleY = 1.0;
SpriterSlot *slot = 0;
// layer.name format: "base_name bone(bone_name) slot(slot_name)"
if (file.layerName.contains("slot(")) {
int start = file.layerName.indexOf("slot(") + 5;
int end = file.layerName.indexOf(')', start);
slot->name = file.layerName.mid(start, end - start);
slot->defaultAttachmentFlag = file.layerName.contains("*");
}
spriterObject.slot = slot;
// qDebug() << "Created object" << spriterObject.id << spriterObject.folderId
// << spriterObject.fileId << spriterObject.x << spriterObject.y
// << spriterObject.localX << spriterObject.localY;
m_objects.append(spriterObject);
}
}
// Copy object transforms
for (int i = 0; i < m_objects.size(); ++i) {
m_objects[i].fixLocalX = m_objects[i].localX;
m_objects[i].fixLocalY = m_objects[i].localY;
m_objects[i].fixLocalAngle = m_objects[i].localAngle;
m_objects[i].fixLocalScaleX = m_objects[i].localScaleX;
m_objects[i].fixLocalScaleY = m_objects[i].localScaleY;
}
// Calculate bone angles
if (m_rootBone) {
copyBone(m_rootBone);
fixBone(m_rootBone);
}
// Generate scml
QDomDocument scml;
fillScml(scml, fi.baseName());
- QFile f(filename);
- //qDebug() << "Writing xml to" << f.fileName();
- bool r = f.open(QFile::WriteOnly);
- Q_ASSERT(r);
- f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- f.write(scml.toString(4).toUtf8());
- f.flush();
- f.close();
-
-
+ io->write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ io->write(scml.toString(4).toUtf8());
delete m_rootBone;
return KisImportExportFilter::OK;
}
+void KisSpriterExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "Spriter");
+}
+
+
+
#include "kis_spriter_export.moc"
diff --git a/plugins/impex/spriter/kis_spriter_export.h b/plugins/impex/spriter/kis_spriter_export.h
index 42f279decb..86daebf519 100644
--- a/plugins/impex/spriter/kis_spriter_export.h
+++ b/plugins/impex/spriter/kis_spriter_export.h
@@ -1,134 +1,135 @@
/*
* Copyright (c) 2016 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_SPRITER_EXPORT_H_
#define _KIS_SPRITER_EXPORT_H_
#include <QVariant>
#include <QDomDocument>
#include <QList>
#include <KisImportExportFilter.h>
#include <kis_types.h>
struct SpriterFile {
qreal id;
QString name;
QString pathName;
QString baseName;
QString layerName;
qreal width;
qreal height;
qreal x;
qreal y;
};
struct Folder {
qreal id;
QString name;
QString pathName;
QString baseName;
QString groupName;
QList<SpriterFile> files;
};
struct Bone {
qreal id;
const Bone *parentBone;
QString name;
qreal x;
qreal y;
qreal width;
qreal height;
qreal localX;
qreal localY;
qreal localAngle;
qreal localScaleX;
qreal localScaleY;
qreal fixLocalX;
qreal fixLocalY;
qreal fixLocalAngle;
qreal fixLocalScaleX;
qreal fixLocalScaleY;
QList<Bone*> bones;
~Bone() {
qDeleteAll(bones);
bones.clear();;
}
};
struct SpriterSlot {
QString name;
bool defaultAttachmentFlag;
};
struct SpriterObject {
qreal id;
qreal folderId;
qreal fileId;
Bone *bone;
SpriterSlot *slot;
qreal x;
qreal y;
qreal localX;
qreal localY;
qreal localAngle;
qreal localScaleX;
qreal localScaleY;
qreal fixLocalX;
qreal fixLocalY;
qreal fixLocalAngle;
qreal fixLocalScaleX;
qreal fixLocalScaleY;
~SpriterObject() {
delete slot;
}
};
class KisSpriterExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisSpriterExport(QObject *parent, const QVariantList &);
virtual ~KisSpriterExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
private:
bool savePaintDevice(KisPaintDeviceSP dev, const QString &fileName);
void parseFolder(KisGroupLayerSP parentGroup, const QString &folderName, const QString &basePath);
Bone *parseBone(const Bone *parent, KisGroupLayerSP groupLayer);
void fixBone(Bone *bone);
void fillScml(QDomDocument &scml, const QString &entityName);
void writeBoneRef(const Bone *bone, QDomElement &mainline, QDomDocument &scml);
void writeBone(const Bone *bone, QDomElement &timeline, QDomDocument &scml);
- KisImageWSP m_image;
+ KisImageSP m_image;
qreal m_timelineid;
QList<Folder> m_folders;
Bone *m_rootBone;
QList<SpriterObject> m_objects;
KisGroupLayerSP m_rootLayer; // Not the image's root later, but the one that is named "root"
KisLayerSP m_boneLayer;
};
#endif
diff --git a/plugins/impex/spriter/krita_spriter_export.json b/plugins/impex/spriter/krita_spriter_export.json
index 98e9830fae..8281ec5251 100644
--- a/plugins/impex/spriter/krita_spriter_export.json
+++ b/plugins/impex/spriter/krita_spriter_export.json
@@ -1,13 +1,12 @@
{
"Id": "Krita Spriter Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "application/x-spriter",
- "X-KDE-Import": "application/x-krita",
"X-KDE-Library": "kritaspriterexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "scml"
}
diff --git a/plugins/impex/tga/CMakeLists.txt b/plugins/impex/tga/CMakeLists.txt
index 68aa577524..a9546c3326 100644
--- a/plugins/impex/tga/CMakeLists.txt
+++ b/plugins/impex/tga/CMakeLists.txt
@@ -1,24 +1,24 @@
set(kritatgaexport_SOURCES
kis_tga_export.cpp
)
ki18n_wrap_ui(kritatgaexport_SOURCES )
add_library(kritatgaexport MODULE ${kritatgaexport_SOURCES})
-target_link_libraries(kritatgaexport kritaui)
+target_link_libraries(kritatgaexport kritaui kritaimpex)
install(TARGETS kritatgaexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritatgaimport_SOURCES
kis_tga_import.cpp
)
ki18n_wrap_ui(kritatgaimport_SOURCES )
add_library(kritatgaimport MODULE ${kritatgaimport_SOURCES})
-target_link_libraries(kritatgaimport kritaui )
+target_link_libraries(kritatgaimport kritaui)
install(TARGETS kritatgaimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_tga.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/tga/kis_tga_export.cpp b/plugins/impex/tga/kis_tga_export.cpp
index 8f5f37d09d..a2f7aee5a8 100644
--- a/plugins/impex/tga/kis_tga_export.cpp
+++ b/plugins/impex/tga/kis_tga_export.cpp
@@ -1,102 +1,94 @@
/*
* 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_export.h"
#include <QCheckBox>
#include <QSlider>
-#include <KoDialog.h>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <QApplication>
-
-#include <KisFilterChain.h>
-
+#include <KoColorModelStandardIds.h>
+#include <KisExportCheckRegistry.h>
+#include <KisImportExportManager.h>
#include <kis_paint_device.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include "tga.h"
K_PLUGIN_FACTORY_WITH_JSON(KisTGAExportFactory, "krita_tga_export.json", registerPlugin<KisTGAExport>();)
KisTGAExport::KisTGAExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisTGAExport::~KisTGAExport()
{
}
-KisImportExportFilter::ConversionStatus KisTGAExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisTGAExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
Q_UNUSED(configuration);
- dbgFile << "TGA export! From:" << from << ", To:" << to << "";
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- // the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(input->image()->locked());
+ QRect rc = document->image()->bounds();
+ QImage image = document->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
- QRect rc = input->image()->bounds();
- QImage image = input->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
-
- QFile f(filename);
- f.open(QIODevice::WriteOnly);
- QDataStream s(&f);
+ QDataStream s(io);
s.setByteOrder(QDataStream::LittleEndian);
const QImage& img = image;
const bool hasAlpha = (img.format() == QImage::Format_ARGB32);
for (int i = 0; i < 12; i++)
s << targaMagic[i];
// write header
s << quint16(img.width()); // width
s << quint16(img.height()); // height
s << quint8(hasAlpha ? 32 : 24); // depth (24 bit RGB + 8 bit alpha)
s << quint8(hasAlpha ? 0x24 : 0x20); // top left image (0x20) + 8 bit alpha (0x4)
for (int y = 0; y < img.height(); y++) {
for (int x = 0; x < img.width(); x++) {
const QRgb color = img.pixel(x, y);
s << quint8(qBlue(color));
s << quint8(qGreen(color));
s << quint8(qRed(color));
if (hasAlpha)
s << quint8(qAlpha(color));
}
}
return KisImportExportFilter::OK;
}
+void KisTGAExport::initializeCapabilities()
+{
+
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "TGA");
+}
+
+
+
#include "kis_tga_export.moc"
diff --git a/plugins/impex/tga/kis_tga_export.h b/plugins/impex/tga/kis_tga_export.h
index 126e1609f2..d625cfa7b9 100644
--- a/plugins/impex/tga/kis_tga_export.h
+++ b/plugins/impex/tga/kis_tga_export.h
@@ -1,37 +1,38 @@
/*
* 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 _KIS_TGA_EXPORT_H_
#define _KIS_TGA_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisTGAExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisTGAExport(QObject *parent, const QVariantList &);
virtual ~KisTGAExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/tga/kis_tga_import.cpp b/plugins/impex/tga/kis_tga_import.cpp
index c33c9843b2..e04e32569d 100644
--- a/plugins/impex/tga/kis_tga_import.cpp
+++ b/plugins/impex/tga/kis_tga_import.cpp
@@ -1,306 +1,281 @@
/*
* 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 <QFileInfo>
#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;
/*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);
// 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) {
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++) {
const uchar alpha = src[3];
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, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisTGAImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
Q_UNUSED(configuration);
- dbgFile << "TGA import! From:" << from << ", To:" << to << 0;
+ QDataStream s(io);
+ s.setByteOrder(QDataStream::LittleEndian);
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
+ TgaHeader tga;
+ s >> tga;
+ s.device()->seek(TgaHeader::SIZE + tga.id_length);
- KisDocument * doc = outputDocument();
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- doc->prepareForImport();
-
- if (!filename.isEmpty()) {
-
- if (!QFileInfo(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- QFile f(filename);
- 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 image file format.
+ if (s.atEnd()) {
+ return KisImportExportFilter::InvalidFormat;
+ }
- // Check supported file types.
- if (!isSupported(tga)) {
- return KisImportExportFilter::InvalidFormat;
- }
+ // Check supported file types.
+ if (!isSupported(tga)) {
+ return KisImportExportFilter::InvalidFormat;
+ }
- QImage img;
- bool result = loadTGA(s, tga, img);
+ QImage img;
+ bool result = loadTGA(s, tga, img);
- if (result == false) {
- return KisImportExportFilter::CreationError;
- }
+ 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");
+ const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
+ KisImageSP image = new KisImage(document->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());
+ 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;
+ document->setCurrentImage(image);
+ return KisImportExportFilter::OK;
}
#include "kis_tga_import.moc"
diff --git a/plugins/impex/tga/kis_tga_import.h b/plugins/impex/tga/kis_tga_import.h
index f7828581af..0fc03e3fe9 100644
--- a/plugins/impex/tga/kis_tga_import.h
+++ b/plugins/impex/tga/kis_tga_import.h
@@ -1,37 +1,37 @@
/*
* 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 _KIS_TGA_IMPORT_H_
#define _KIS_TGA_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisTGAImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisTGAImport(QObject *parent, const QVariantList &);
virtual ~KisTGAImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/tga/krita_tga_export.json b/plugins/impex/tga/krita_tga_export.json
index e11d9e5473..ac93c159b8 100644
--- a/plugins/impex/tga/krita_tga_export.json
+++ b/plugins/impex/tga/krita_tga_export.json
@@ -1,13 +1,13 @@
{
"Id": "Krita TGA Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/x-tga",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Library": "kritatgaexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "tga"
}
diff --git a/plugins/impex/tga/krita_tga_import.json b/plugins/impex/tga/krita_tga_import.json
index f9c513c5d5..8346082926 100644
--- a/plugins/impex/tga/krita_tga_import.json
+++ b/plugins/impex/tga/krita_tga_import.json
@@ -1,13 +1,13 @@
{
"Id": "Krita TGA Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/x-tga",
"X-KDE-Library": "kritatgaimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "tga"
}
diff --git a/plugins/impex/tiff/CMakeLists.txt b/plugins/impex/tiff/CMakeLists.txt
index 86e7ea0f59..8e57566510 100644
--- a/plugins/impex/tiff/CMakeLists.txt
+++ b/plugins/impex/tiff/CMakeLists.txt
@@ -1,37 +1,37 @@
add_subdirectory(tests)
set(libkritatiffconverter_LIB_SRCS
kis_tiff_converter.cc
kis_tiff_writer_visitor.cpp
kis_tiff_reader.cc
kis_tiff_ycbcr_reader.cc
kis_buffer_stream.cc
)
set(kritatiffimport_SOURCES
${libkritatiffconverter_LIB_SRCS}
kis_tiff_import.cc
)
add_library(kritatiffimport MODULE ${kritatiffimport_SOURCES})
target_link_libraries(kritatiffimport kritaui ${TIFF_LIBRARIES})
install(TARGETS kritatiffimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritatiffexport_SOURCES
${libkritatiffconverter_LIB_SRCS}
kis_tiff_export.cc
kis_dlg_options_tiff.cpp
)
-ki18n_wrap_ui(kritatiffexport_SOURCES kis_wdg_options_tiff.ui )
+ki18n_wrap_ui(kritatiffexport_SOURCES kis_wdg_options_tiff.ui)
add_library(kritatiffexport MODULE ${kritatiffexport_SOURCES})
-target_link_libraries(kritatiffexport kritaui ${TIFF_LIBRARIES})
+target_link_libraries(kritatiffexport kritaui kritaimpex ${TIFF_LIBRARIES})
install(TARGETS kritatiffexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_tiff.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/tiff/kis_dlg_options_tiff.h b/plugins/impex/tiff/kis_dlg_options_tiff.h
index 62a78db5f1..ddd6cf01d9 100644
--- a/plugins/impex/tiff/kis_dlg_options_tiff.h
+++ b/plugins/impex/tiff/kis_dlg_options_tiff.h
@@ -1,47 +1,47 @@
/*
* Copyright (c) 2005-2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2016 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_DLG_OPTIONS_TIFF_H
#define KIS_DLG_OPTIONS_TIFF_H
#include <kis_tiff_converter.h>
#include <kis_config_widget.h>
#include "ui_kis_wdg_options_tiff.h"
class Ui_KisWdgOptionsTIFF;
/**
@author Cyrille Berger <cberger@cberger.net>
*/
class KisTIFFOptionsWidget : public KisConfigWidget, public Ui::KisWdgOptionsTIFF
{
Q_OBJECT
public:
KisTIFFOptionsWidget(QWidget *parent = 0);
void setConfiguration(const KisPropertiesConfigurationSP cfg) override;
KisPropertiesConfigurationSP configuration() const override;
-public Q_SLOTS:
+private Q_SLOTS:
void activated(int index);
void flattenToggled(bool);
KisTIFFOptions options() const;
};
#endif
diff --git a/plugins/impex/tiff/kis_tiff_converter.cc b/plugins/impex/tiff/kis_tiff_converter.cc
index c9d57ada49..a2ab89f984 100644
--- a/plugins/impex/tiff/kis_tiff_converter.cc
+++ b/plugins/impex/tiff/kis_tiff_converter.cc
@@ -1,682 +1,682 @@
/*
* Copyright (c) 2005-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_converter.h"
#include <stdio.h>
#include <QFile>
#include <QApplication>
#include <QFileInfo>
#include <KoDocumentInfo.h>
#include <KoUnit.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include <KoColorModelStandardIds.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <KoColorProfile.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_transaction.h>
#include "kis_tiff_reader.h"
#include "kis_tiff_ycbcr_reader.h"
#include "kis_buffer_stream.h"
#include "kis_tiff_writer_visitor.h"
#if TIFFLIB_VERSION < 20111221
typedef size_t tmsize_t;
#endif
namespace
{
QPair<QString, QString> getColorSpaceForColorType(uint16 sampletype, uint16 color_type, uint16 color_nb_bits, TIFF *image, uint16 &nbchannels, uint16 &extrasamplescount, uint8 &destDepth)
{
if (color_type == PHOTOMETRIC_MINISWHITE || color_type == PHOTOMETRIC_MINISBLACK) {
if (nbchannels == 0) nbchannels = 1;
extrasamplescount = nbchannels - 1; // FIX the extrasamples count in case of
if (sampletype == SAMPLEFORMAT_IEEEFP) {
if (color_nb_bits == 16) {
destDepth = 16;
return QPair<QString, QString>(GrayAColorModelID.id(), Float16BitsColorDepthID.id());
}
else if (color_nb_bits == 32) {
destDepth = 32;
return QPair<QString, QString>(GrayAColorModelID.id(), Float32BitsColorDepthID.id());
}
}
if (color_nb_bits <= 8) {
destDepth = 8;
return QPair<QString, QString>(GrayAColorModelID.id(), Integer8BitsColorDepthID.id());
}
else {
destDepth = 16;
return QPair<QString, QString>(GrayAColorModelID.id(), Integer16BitsColorDepthID.id());
}
} else if (color_type == PHOTOMETRIC_RGB /*|| color_type == */) {
if (nbchannels == 0) nbchannels = 3;
extrasamplescount = nbchannels - 3; // FIX the extrasamples count in case of
if (sampletype == SAMPLEFORMAT_IEEEFP) {
if (color_nb_bits == 16) {
destDepth = 16;
return QPair<QString, QString>(RGBAColorModelID.id(), Float16BitsColorDepthID.id());
}
else if (color_nb_bits == 32) {
destDepth = 32;
return QPair<QString, QString>(RGBAColorModelID.id(), Float32BitsColorDepthID.id());
}
return QPair<QString, QString>();
}
else {
if (color_nb_bits <= 8) {
destDepth = 8;
return QPair<QString, QString>(RGBAColorModelID.id(), Integer8BitsColorDepthID.id());
}
else {
destDepth = 16;
return QPair<QString, QString>(RGBAColorModelID.id(), Integer16BitsColorDepthID.id());
}
}
} else if (color_type == PHOTOMETRIC_YCBCR) {
if (nbchannels == 0) nbchannels = 3;
extrasamplescount = nbchannels - 3; // FIX the extrasamples count in case of
if (color_nb_bits <= 8) {
destDepth = 8;
return QPair<QString, QString>(YCbCrAColorModelID.id(), Integer8BitsColorDepthID.id());
}
else {
destDepth = 16;
return QPair<QString, QString>(YCbCrAColorModelID.id(), Integer16BitsColorDepthID.id());
}
}
else if (color_type == PHOTOMETRIC_SEPARATED) {
if (nbchannels == 0) nbchannels = 4;
// SEPARATED is in general CMYK but not always, so we check
uint16 inkset;
if ((TIFFGetField(image, TIFFTAG_INKSET, &inkset) == 0)) {
dbgFile << "Image does not define the inkset.";
inkset = 2;
}
if (inkset != INKSET_CMYK) {
dbgFile << "Unsupported inkset (right now, only CMYK is supported)";
char** ink_names;
uint16 numberofinks;
if (TIFFGetField(image, TIFFTAG_INKNAMES, &ink_names) == 1 && TIFFGetField(image, TIFFTAG_NUMBEROFINKS, &numberofinks) == 1) {
dbgFile << "Inks are :";
for (uint i = 0; i < numberofinks; i++) {
dbgFile << ink_names[i];
}
}
else {
dbgFile << "inknames are not defined !";
// To be able to read stupid adobe files, if there are no information about inks and four channels, then it's a CMYK file :
if (nbchannels - extrasamplescount != 4) {
return QPair<QString, QString>();
}
}
}
if (color_nb_bits <= 8) {
destDepth = 8;
return QPair<QString, QString>(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id());
}
else {
destDepth = 16;
return QPair<QString, QString>(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id());
}
}
else if (color_type == PHOTOMETRIC_CIELAB || color_type == PHOTOMETRIC_ICCLAB) {
destDepth = 16;
if (nbchannels == 0) nbchannels = 3;
extrasamplescount = nbchannels - 3; // FIX the extrasamples count
return QPair<QString, QString>(LABAColorModelID.id(), Integer16BitsColorDepthID.id());
}
else if (color_type == PHOTOMETRIC_PALETTE) {
destDepth = 16;
if (nbchannels == 0) nbchannels = 2;
extrasamplescount = nbchannels - 2; // FIX the extrasamples count
// <-- we will convert the index image to RGBA16 as the palette is always on 16bits colors
return QPair<QString, QString>(RGBAColorModelID.id(), Integer16BitsColorDepthID.id());
}
return QPair<QString, QString>();
}
}
KisTIFFConverter::KisTIFFConverter(KisDocument *doc)
{
m_doc = doc;
m_stop = false;
TIFFSetWarningHandler(0);
TIFFSetErrorHandler(0);
}
KisTIFFConverter::~KisTIFFConverter()
{
}
KisImageBuilder_Result KisTIFFConverter::decode(const QString &filename)
{
dbgFile << "Start decoding TIFF File";
// Opent the TIFF file
TIFF *image = 0;
if ((image = TIFFOpen(QFile::encodeName(filename), "r")) == 0) {
dbgFile << "Could not open the file, either it does not exist, either it is not a TIFF :" << filename;
return (KisImageBuilder_RESULT_BAD_FETCH);
}
do {
dbgFile << "Read new sub-image";
KisImageBuilder_Result result = readTIFFDirectory(image);
if (result != KisImageBuilder_RESULT_OK) {
return result;
}
} while (TIFFReadDirectory(image));
// Freeing memory
TIFFClose(image);
return KisImageBuilder_RESULT_OK;
}
KisImageBuilder_Result KisTIFFConverter::readTIFFDirectory(TIFF* image)
{
// Read information about the tiff
uint32 width, height;
if (TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width) == 0) {
dbgFile << "Image does not define its width";
TIFFClose(image);
return KisImageBuilder_RESULT_INVALID_ARG;
}
if (TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height) == 0) {
dbgFile << "Image does not define its height";
TIFFClose(image);
return KisImageBuilder_RESULT_INVALID_ARG;
}
float xres;
if (TIFFGetField(image, TIFFTAG_XRESOLUTION, &xres) == 0) {
dbgFile << "Image does not define x resolution";
// but we don't stop
xres = 100;
}
float yres;
if (TIFFGetField(image, TIFFTAG_YRESOLUTION, &yres) == 0) {
dbgFile << "Image does not define y resolution";
// but we don't stop
yres = 100;
}
uint16 depth;
if ((TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &depth) == 0)) {
dbgFile << "Image does not define its depth";
depth = 1;
}
uint16 sampletype;
if ((TIFFGetField(image, TIFFTAG_SAMPLEFORMAT, &sampletype) == 0)) {
dbgFile << "Image does not define its sample type";
sampletype = SAMPLEFORMAT_UINT;
}
// Determine the number of channels (useful to know if a file has an alpha or not
uint16 nbchannels;
if (TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &nbchannels) == 0) {
dbgFile << "Image has an undefined number of samples per pixel";
nbchannels = 0;
}
// Get the number of extrasamples and information about them
uint16 *sampleinfo = 0, extrasamplescount;
if (TIFFGetField(image, TIFFTAG_EXTRASAMPLES, &extrasamplescount, &sampleinfo) == 0) {
extrasamplescount = 0;
}
// Determine the colorspace
uint16 color_type;
if (TIFFGetField(image, TIFFTAG_PHOTOMETRIC, &color_type) == 0) {
dbgFile << "Image has an undefined photometric interpretation";
color_type = PHOTOMETRIC_MINISWHITE;
}
uint8 dstDepth = 0;
QPair<QString, QString> colorSpaceId = getColorSpaceForColorType(sampletype, color_type, depth, image, nbchannels, extrasamplescount, dstDepth);
if (colorSpaceId.first.isEmpty()) {
dbgFile << "Image has an unsupported colorspace :" << color_type << " for this depth :" << depth;
TIFFClose(image);
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
dbgFile << "Colorspace is :" << colorSpaceId.first << colorSpaceId.second << " with a depth of" << depth << " and with a nb of channels of" << nbchannels;
// Read image profile
dbgFile << "Reading profile";
const KoColorProfile* profile = 0;
quint32 EmbedLen;
quint8* EmbedBuffer;
if (TIFFGetField(image, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer) == 1) {
dbgFile << "Profile found";
QByteArray rawdata;
rawdata.resize(EmbedLen);
memcpy(rawdata.data(), EmbedBuffer, EmbedLen);
profile = KoColorSpaceRegistry::instance()->createColorProfile(colorSpaceId.first, colorSpaceId.second, rawdata);
}
else {
dbgFile << "No Profile found";
}
// Check that the profile is used by the color space
if (profile && !KoColorSpaceRegistry::instance()->colorSpaceFactory(KoColorSpaceRegistry::instance()->colorSpaceId(colorSpaceId.first, colorSpaceId.second))->profileIsCompatible(profile)) {
warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << colorSpaceId.first << " " << colorSpaceId.second;
profile = 0;
}
// Retrieve a pointer to the colorspace
const KoColorSpace* cs = 0;
if (profile && profile->isSuitableForOutput()) {
dbgFile << "image has embedded profile:" << profile -> name() << "";
cs = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, profile);
}
else {
cs = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, 0);
}
if (cs == 0) {
dbgFile << "Colorspace" << colorSpaceId.first << colorSpaceId.second << " is not available, please check your installation.";
TIFFClose(image);
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
// Create the cmsTransform if needed
KoColorTransformation* transform = 0;
if (profile && !profile->isSuitableForOutput()) {
dbgFile << "The profile can't be used in krita, need conversion";
transform = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
// Check if there is an alpha channel
int8 alphapos = -1; // <- no alpha
// Check which extra is alpha if any
dbgFile << "There are" << nbchannels << " channels and" << extrasamplescount << " extra channels";
if (sampleinfo) { // index images don't have any sampleinfo, and therefor sampleinfo == 0
for (int i = 0; i < extrasamplescount; i ++) {
dbgFile << i << "" << extrasamplescount << "" << (cs->colorChannelCount()) << nbchannels << "" << sampleinfo[i];
if (sampleinfo[i] == EXTRASAMPLE_ASSOCALPHA) {
// XXX: dangelo: the color values are already multiplied with
// the alpha value. This needs to be reversed later (postprocessor?)
alphapos = i;
}
if (sampleinfo[i] == EXTRASAMPLE_UNASSALPHA) {
// color values are not premultiplied with alpha, and can be used as they are.
alphapos = i;
}
}
}
dbgFile << "Alpha pos:" << alphapos;
// Read META Information
KoDocumentInfo * info = m_doc->documentInfo();
char* text;
if (TIFFGetField(image, TIFFTAG_ARTIST, &text) == 1) {
info->setAuthorInfo("creator", text);
}
if (TIFFGetField(image, TIFFTAG_DOCUMENTNAME, &text) == 1) {
info->setAboutInfo("title", text);
}
if (TIFFGetField(image, TIFFTAG_IMAGEDESCRIPTION, &text) == 1) {
info->setAboutInfo("description", text);
}
// Get the planar configuration
uint16 planarconfig;
if (TIFFGetField(image, TIFFTAG_PLANARCONFIG, &planarconfig) == 0) {
dbgFile << "Plannar configuration is not define";
TIFFClose(image);
return KisImageBuilder_RESULT_INVALID_ARG;
}
- // Creating the KisImageWSP
+ // Creating the KisImageSP
if (! m_image) {
m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, "built image");
m_image->setResolution( POINT_TO_INCH(xres), POINT_TO_INCH(yres )); // It is the "invert" macro because we convert from pointer-per-inchs to points
Q_CHECK_PTR(m_image);
}
else {
if (m_image->width() < (qint32)width || m_image->height() < (qint32)height) {
quint32 newwidth = (m_image->width() < (qint32)width) ? width : m_image->width();
quint32 newheight = (m_image->height() < (qint32)height) ? height : m_image->height();
m_image->resizeImage(QRect(0,0,newwidth, newheight));
}
}
KisPaintLayer* layer = new KisPaintLayer(m_image.data(), m_image -> nextLayerName(), quint8_MAX);
tdata_t buf = 0;
tdata_t* ps_buf = 0; // used only for planar configuration separated
KisBufferStreamBase* tiffstream;
KisTIFFReaderBase* tiffReader = 0;
quint8 poses[5];
KisTIFFPostProcessor* postprocessor = 0;
// Configure poses
uint8 nbcolorsamples = nbchannels - extrasamplescount;
switch (color_type) {
case PHOTOMETRIC_MINISWHITE: {
poses[0] = 0; poses[1] = 1;
postprocessor = new KisTIFFPostProcessorInvert(nbcolorsamples);
}
break;
case PHOTOMETRIC_MINISBLACK: {
poses[0] = 0; poses[1] = 1;
postprocessor = new KisTIFFPostProcessor(nbcolorsamples);
}
break;
case PHOTOMETRIC_CIELAB: {
poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3;
postprocessor = new KisTIFFPostProcessorCIELABtoICCLAB(nbcolorsamples);
}
break;
case PHOTOMETRIC_ICCLAB: {
poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3;
postprocessor = new KisTIFFPostProcessor(nbcolorsamples);
}
break;
case PHOTOMETRIC_RGB: {
if (sampletype == 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;
}
postprocessor = new KisTIFFPostProcessor(nbcolorsamples);
}
break;
case PHOTOMETRIC_SEPARATED: {
poses[0] = 0; poses[1] = 1; poses[2] = 2; poses[3] = 3; poses[4] = 4;
postprocessor = new KisTIFFPostProcessor(nbcolorsamples);
}
break;
default:
break;
}
// Initisalize tiffReader
uint16 * lineSizeCoeffs = new uint16[nbchannels];
uint16 vsubsampling = 1;
uint16 hsubsampling = 1;
for (uint i = 0; i < nbchannels; i++) {
lineSizeCoeffs[i] = 1;
}
if (color_type == PHOTOMETRIC_PALETTE) {
uint16 *red; // No need to free them they are free by libtiff
uint16 *green;
uint16 *blue;
if ((TIFFGetField(image, TIFFTAG_COLORMAP, &red, &green, &blue)) == 0) {
dbgFile << "Indexed image does not define a palette";
TIFFClose(image);
delete [] lineSizeCoeffs;
return KisImageBuilder_RESULT_INVALID_ARG;
}
tiffReader = new KisTIFFReaderFromPalette(layer->paintDevice(), red, green, blue, poses, alphapos, depth, sampletype, nbcolorsamples, extrasamplescount, transform, postprocessor);
} else if (color_type == PHOTOMETRIC_YCBCR) {
TIFFGetFieldDefaulted(image, TIFFTAG_YCBCRSUBSAMPLING, &hsubsampling, &vsubsampling);
lineSizeCoeffs[1] = hsubsampling;
lineSizeCoeffs[2] = hsubsampling;
uint16 position;
TIFFGetFieldDefaulted(image, TIFFTAG_YCBCRPOSITIONING, &position);
if (dstDepth == 8) {
tiffReader = new KisTIFFYCbCrReaderTarget8Bit(layer->paintDevice(), layer->image()->width(), layer->image()->height(), poses, alphapos, depth, sampletype, nbcolorsamples, extrasamplescount, transform, postprocessor, hsubsampling, vsubsampling, (KisTIFFYCbCr::Position)position);
}
else if (dstDepth == 16) {
tiffReader = new KisTIFFYCbCrReaderTarget16Bit(layer->paintDevice(), layer->image()->width(), layer->image()->height(), poses, alphapos, depth, sampletype, nbcolorsamples, extrasamplescount, transform, postprocessor, hsubsampling, vsubsampling, (KisTIFFYCbCr::Position)position);
}
}
else if (dstDepth == 8) {
tiffReader = new KisTIFFReaderTarget8bit(layer->paintDevice(), poses, alphapos, depth, sampletype, nbcolorsamples, extrasamplescount, transform, postprocessor);
}
else if (dstDepth == 16) {
uint16 alphaValue;
if (sampletype == SAMPLEFORMAT_IEEEFP)
{
alphaValue = 15360; // representation of 1.0 in half
} else {
alphaValue = quint16_MAX;
}
tiffReader = new KisTIFFReaderTarget16bit(layer->paintDevice(), poses, alphapos, depth, sampletype, nbcolorsamples, extrasamplescount, transform, postprocessor, alphaValue);
}
else if (dstDepth == 32) {
union {
float f;
uint32 i;
} alphaValue;
if (sampletype == SAMPLEFORMAT_IEEEFP)
{
alphaValue.f = 1.0f;
} else {
alphaValue.i = quint32_MAX;
}
tiffReader = new KisTIFFReaderTarget32bit(layer->paintDevice(), poses, alphapos, depth, sampletype, nbcolorsamples, extrasamplescount, transform, postprocessor, alphaValue.i);
}
if (!tiffReader) {
delete postprocessor;
delete[] lineSizeCoeffs;
TIFFClose(image);
dbgFile << "Image has an invalid/unsupported color type: " << color_type;
return KisImageBuilder_RESULT_INVALID_ARG;
}
if (TIFFIsTiled(image)) {
dbgFile << "tiled image";
uint32 tileWidth, tileHeight;
uint32 x, y;
TIFFGetField(image, TIFFTAG_TILEWIDTH, &tileWidth);
TIFFGetField(image, TIFFTAG_TILELENGTH, &tileHeight);
uint32 linewidth = (tileWidth * depth * nbchannels) / 8;
if (planarconfig == PLANARCONFIG_CONTIG) {
buf = _TIFFmalloc(TIFFTileSize(image));
if (depth < 16) {
tiffstream = new KisBufferStreamContigBelow16((uint8*)buf, depth, linewidth);
}
else if (depth < 32) {
tiffstream = new KisBufferStreamContigBelow32((uint8*)buf, depth, linewidth);
}
else {
tiffstream = new KisBufferStreamContigAbove32((uint8*)buf, depth, linewidth);
}
}
else {
ps_buf = new tdata_t[nbchannels];
uint32 * lineSizes = new uint32[nbchannels];
tmsize_t baseSize = TIFFTileSize(image) / nbchannels;
for (uint i = 0; i < nbchannels; i++) {
ps_buf[i] = _TIFFmalloc(baseSize);
lineSizes[i] = tileWidth; // baseSize / lineSizeCoeffs[i];
}
tiffstream = new KisBufferStreamSeperate((uint8**) ps_buf, nbchannels, depth, lineSizes);
delete [] lineSizes;
}
dbgFile << linewidth << "" << nbchannels << "" << layer->paintDevice()->colorSpace()->colorChannelCount();
for (y = 0; y < height; y += tileHeight) {
for (x = 0; x < width; x += tileWidth) {
dbgFile << "Reading tile x =" << x << " y =" << y;
if (planarconfig == PLANARCONFIG_CONTIG) {
TIFFReadTile(image, buf, x, y, 0, (tsample_t) - 1);
}
else {
for (uint i = 0; i < nbchannels; i++) {
TIFFReadTile(image, ps_buf[i], x, y, 0, i);
}
}
uint32 realTileWidth = (x + tileWidth) < width ? tileWidth : width - x;
for (uint yintile = 0; y + yintile < height && yintile < tileHeight / vsubsampling;) {
tiffReader->copyDataToChannels(x, y + yintile , realTileWidth, tiffstream);
yintile += 1;
tiffstream->moveToLine(yintile);
}
tiffstream->restart();
}
}
}
else {
dbgFile << "striped image";
tsize_t stripsize = TIFFStripSize(image);
uint32 rowsPerStrip;
TIFFGetFieldDefaulted(image, TIFFTAG_ROWSPERSTRIP, &rowsPerStrip);
dbgFile << rowsPerStrip << "" << height;
rowsPerStrip = qMin(rowsPerStrip, height); // when TIFFNumberOfStrips(image) == 1 it might happen that rowsPerStrip is incorrectly set
if (planarconfig == PLANARCONFIG_CONTIG) {
buf = _TIFFmalloc(stripsize);
if (depth < 16) {
tiffstream = new KisBufferStreamContigBelow16((uint8*)buf, depth, stripsize / rowsPerStrip);
}
else if (depth < 32) {
tiffstream = new KisBufferStreamContigBelow32((uint8*)buf, depth, stripsize / rowsPerStrip);
}
else {
tiffstream = new KisBufferStreamContigAbove32((uint8*)buf, depth, stripsize / rowsPerStrip);
}
}
else {
ps_buf = new tdata_t[nbchannels];
uint32 scanLineSize = stripsize / rowsPerStrip;
dbgFile << " scanLineSize for each plan =" << scanLineSize;
uint32 * lineSizes = new uint32[nbchannels];
for (uint i = 0; i < nbchannels; i++) {
ps_buf[i] = _TIFFmalloc(stripsize);
lineSizes[i] = scanLineSize / lineSizeCoeffs[i];
}
tiffstream = new KisBufferStreamSeperate((uint8**) ps_buf, nbchannels, depth, lineSizes);
delete [] lineSizes;
}
dbgFile << "Scanline size =" << TIFFRasterScanlineSize(image) << " / strip size =" << TIFFStripSize(image) << " / rowsPerStrip =" << rowsPerStrip << " stripsize/rowsPerStrip =" << stripsize / rowsPerStrip;
uint32 y = 0;
dbgFile << " NbOfStrips =" << TIFFNumberOfStrips(image) << " rowsPerStrip =" << rowsPerStrip << " stripsize =" << stripsize;
for (uint32 strip = 0; y < height; strip++) {
if (planarconfig == PLANARCONFIG_CONTIG) {
TIFFReadEncodedStrip(image, TIFFComputeStrip(image, y, 0) , buf, (tsize_t) - 1);
}
else {
for (uint i = 0; i < nbchannels; i++) {
TIFFReadEncodedStrip(image, TIFFComputeStrip(image, y, i), ps_buf[i], (tsize_t) - 1);
}
}
for (uint32 yinstrip = 0 ; yinstrip < rowsPerStrip && y < height ;) {
uint linesread = tiffReader->copyDataToChannels(0, y, width, tiffstream);
y += linesread;
yinstrip += linesread;
tiffstream->moveToLine(yinstrip);
}
tiffstream->restart();
}
}
tiffReader->finalize();
delete[] lineSizeCoeffs;
delete tiffReader;
delete tiffstream;
if (planarconfig == PLANARCONFIG_CONTIG) {
_TIFFfree(buf);
} else {
for (uint i = 0; i < nbchannels; i++) {
_TIFFfree(ps_buf[i]);
}
delete[] ps_buf;
}
m_image->addNode(KisNodeSP(layer), m_image->rootLayer().data());
return KisImageBuilder_RESULT_OK;
}
KisImageBuilder_Result KisTIFFConverter::buildImage(const QString &filename)
{
return decode(filename);
}
-KisImageWSP KisTIFFConverter::image()
+KisImageSP KisTIFFConverter::image()
{
return m_image;
}
-KisImageBuilder_Result KisTIFFConverter::buildFile(const QString &filename, KisImageWSP kisimage, KisTIFFOptions options)
+KisImageBuilder_Result KisTIFFConverter::buildFile(const QString &filename, KisImageSP kisimage, KisTIFFOptions options)
{
dbgFile << "Start writing TIFF File";
if (!kisimage)
return KisImageBuilder_RESULT_EMPTY;
// Open file for writing
TIFF *image;
if ((image = TIFFOpen(QFile::encodeName(filename), "w")) == 0) {
dbgFile << "Could not open the file for writing" << filename;
TIFFClose(image);
return (KisImageBuilder_RESULT_FAILURE);
}
// Set the document information
KoDocumentInfo * info = m_doc->documentInfo();
QString title = info->aboutInfo("title");
if (!title.isEmpty()) {
TIFFSetField(image, TIFFTAG_DOCUMENTNAME, title.toLatin1().constData());
}
QString abstract = info->aboutInfo("description");
if (!abstract.isEmpty()) {
TIFFSetField(image, TIFFTAG_IMAGEDESCRIPTION, abstract.toLatin1().constData());
}
QString author = info->authorInfo("creator");
if (!author.isEmpty()) {
TIFFSetField(image, TIFFTAG_ARTIST, author.toLatin1().constData());
}
dbgFile << "xres: " << INCH_TO_POINT(kisimage->xRes()) << " yres: " << INCH_TO_POINT(kisimage->yRes());
TIFFSetField(image, TIFFTAG_XRESOLUTION, INCH_TO_POINT(kisimage->xRes())); // It is the "invert" macro because we convert from pointer-per-inchs to points
TIFFSetField(image, TIFFTAG_YRESOLUTION, INCH_TO_POINT(kisimage->yRes()));
KisGroupLayer* root = dynamic_cast<KisGroupLayer*>(kisimage->rootLayer().data());
if (root == 0) {
TIFFClose(image);
return KisImageBuilder_RESULT_FAILURE;
}
KisTIFFWriterVisitor* visitor = new KisTIFFWriterVisitor(image, &options);
if (!visitor->visit(root)) {
TIFFClose(image);
return KisImageBuilder_RESULT_FAILURE;
}
TIFFClose(image);
return KisImageBuilder_RESULT_OK;
}
void KisTIFFConverter::cancel()
{
m_stop = true;
}
diff --git a/plugins/impex/tiff/kis_tiff_converter.h b/plugins/impex/tiff/kis_tiff_converter.h
index b4f77f97f1..88c7d63e63 100644
--- a/plugins/impex/tiff/kis_tiff_converter.h
+++ b/plugins/impex/tiff/kis_tiff_converter.h
@@ -1,69 +1,69 @@
/*
* Copyright (c) 2005-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.
*/
#ifndef _KIS_TIFF_CONVERTER_H_
#define _KIS_TIFF_CONVERTER_H_
#include <stdio.h>
#include <tiffio.h>
#include <QVector>
#include "kis_types.h"
#include "kis_global.h"
#include "kis_annotation.h"
#include <KisImageBuilderResult.h>
class KisDocument;
struct KisTIFFOptions {
quint16 compressionType;
quint16 predictor;
bool alpha;
bool flatten;
quint16 jpegQuality;
quint16 deflateCompress;
quint16 faxMode;
quint16 pixarLogCompress;
bool saveProfile;
};
class KisTIFFConverter : public QObject
{
Q_OBJECT
public:
KisTIFFConverter(KisDocument *doc);
virtual ~KisTIFFConverter();
public:
KisImageBuilder_Result buildImage(const QString &filename);
- KisImageBuilder_Result buildFile(const QString &filename, KisImageWSP layer, KisTIFFOptions);
+ KisImageBuilder_Result buildFile(const QString &filename, KisImageSP layer, KisTIFFOptions);
/** Retrieve the constructed image
*/
- KisImageWSP image();
+ KisImageSP image();
public Q_SLOTS:
virtual void cancel();
private:
KisImageBuilder_Result decode(const QString &filename);
KisImageBuilder_Result readTIFFDirectory(TIFF* image);
private:
- KisImageWSP m_image;
+ KisImageSP m_image;
KisDocument *m_doc;
bool m_stop;
};
#endif
diff --git a/plugins/impex/tiff/kis_tiff_export.cc b/plugins/impex/tiff/kis_tiff_export.cc
index d1d9c0ca2c..df119b32ec 100644
--- a/plugins/impex/tiff/kis_tiff_export.cc
+++ b/plugins/impex/tiff/kis_tiff_export.cc
@@ -1,168 +1,194 @@
/*
* 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_tiff_export.h"
#include <QCheckBox>
#include <QSlider>
#include <kpluginfactory.h>
#include <QFileInfo>
-#include <KoDialog.h>
-#include <KisFilterChain.h>
+#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include <KoChannelInfo.h>
#include <KoColorModelStandardIds.h>
#include <KisImportExportManager.h>
-
+#include <KisExportCheckRegistry.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_config.h>
#include "kis_tiff_converter.h"
#include "kis_dlg_options_tiff.h"
#include "ui_kis_wdg_options_tiff.h"
K_PLUGIN_FACTORY_WITH_JSON(KisTIFFExportFactory, "krita_tiff_export.json", registerPlugin<KisTIFFExport>();)
KisTIFFExport::KisTIFFExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisTIFFExport::~KisTIFFExport()
{
}
-KisImportExportFilter::ConversionStatus KisTIFFExport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisTIFFExport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP configuration)
{
- dbgFile << "Tiff export! From:" << from << ", To:" << to << "";
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *input = inputDocument();
- if (!input) {
- return KisImportExportFilter::NoDocumentCreated;
- }
-
- KoDialog kdb;
- kdb.setWindowTitle(i18n("TIFF Export Options"));
- kdb.setButtons(KoDialog::Ok | KoDialog::Cancel);
- KisTIFFOptionsWidget *wdg = static_cast<KisTIFFOptionsWidget*>(createConfigurationWidget(&kdb, from, to));
- kdb.setMainWidget(wdg);
- kdb.resize(kdb.minimumSize());
-
// If a configuration object was passed to the convert method, we use that, otherwise we load from the settings
KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
if (configuration) {
cfg->fromXML(configuration->toXML());
}
else {
- cfg = lastSavedConfiguration(from, to);
+ cfg = lastSavedConfiguration(KisDocument::nativeFormatMimeType(), "image/tiff");
}
- const KoColorSpace* cs = input->image()->colorSpace();
+ const KoColorSpace* cs = document->image()->colorSpace();
cfg->setProperty("type", (int)cs->channels()[0]->channelValueType());
cfg->setProperty("isCMYK", (cs->colorModelId() == CMYKAColorModelID));
- wdg->setConfiguration(cfg);
-
- if (!getBatchMode()) {
- if (kdb.exec() == QDialog::Rejected) {
- return KisImportExportFilter::UserCancelled;
- }
- cfg = wdg->configuration();
- KisConfig().setExportConfiguration("TIFF", *cfg.data());
+ KisTIFFOptions options;
+ switch (configuration->getInt("compressiontype")) {
+ 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;
}
-
- KisTIFFOptions options = wdg->options();
+ options.predictor = configuration->getInt("predictor");
+ options.alpha = configuration->getBool("alpha");
+ options.flatten = configuration->getBool("flatten");
+ options.jpegQuality = configuration->getInt("quality");
+ options.deflateCompress = configuration->getInt("deflate");
+ options.faxMode = configuration->getInt("faxmode");
+ options.pixarLogCompress = configuration->getInt("pixarlog");
+ options.saveProfile = configuration->getBool("saveProfile");
if ((cs->channels()[0]->channelValueType() == KoChannelInfo::FLOAT16
|| cs->channels()[0]->channelValueType() == KoChannelInfo::FLOAT32) && options.predictor == 2) {
// FIXME THIS IS AN HACK FIX THAT IN 2.0 !! (62456a7b47636548c6507593df3e2bdf440f7544, BUG:135649)
options.predictor = 3;
}
- QString filename = outputFile();
-
- if (filename.isEmpty()) {
- return KisImportExportFilter::FileNotFound;
- }
-
KisImageSP image;
if (options.flatten) {
- image = new KisImage(0, input->image()->width(), input->image()->height(), input->image()->colorSpace(), "");
- image->setResolution(input->image()->xRes(), input->image()->yRes());
- KisPaintDeviceSP pd = KisPaintDeviceSP(new KisPaintDevice(*input->image()->projection()));
+ image = new KisImage(0, document->image()->width(), document->image()->height(), document->image()->colorSpace(), "");
+ image->setResolution(document->image()->xRes(), document->image()->yRes());
+ KisPaintDeviceSP pd = KisPaintDeviceSP(new KisPaintDevice(*document->image()->projection()));
KisPaintLayerSP l = KisPaintLayerSP(new KisPaintLayer(image.data(), "projection", OPACITY_OPAQUE_U8, pd));
image->addNode(KisNodeSP(l.data()), image->rootLayer().data());
} else {
- image = input->image();
+ image = document->image();
}
// the image must be locked at the higher levels
- KIS_SAFE_ASSERT_RECOVER_NOOP(input->image()->locked());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
- KisTIFFConverter ktc(input);
+ KisTIFFConverter tiffConverter(document);
KisImageBuilder_Result res;
- if ((res = ktc.buildFile(filename, image, options)) == KisImageBuilder_RESULT_OK) {
+ if ((res = tiffConverter.buildFile(filename(), image, options)) == KisImageBuilder_RESULT_OK) {
dbgFile << "success !";
return KisImportExportFilter::OK;
}
dbgFile << " Result =" << res;
return KisImportExportFilter::InternalError;
}
KisPropertiesConfigurationSP KisTIFFExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("compressiontype", 0);
cfg->setProperty("predictor", 0);
cfg->setProperty("alpha", true);
cfg->setProperty("flatten", true);
cfg->setProperty("quality", 80);
cfg->setProperty("deflate", 6);
cfg->setProperty("faxmode", 0);
cfg->setProperty("pixarlog", 6);
cfg->setProperty("saveProfile", true);
return cfg;
}
KisPropertiesConfigurationSP KisTIFFExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
{
- QString filterConfig = KisConfig().exportConfiguration("TIFF");
+ QString filterConfig = KisConfig().exportConfiguration(to);
KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
cfg->fromXML(filterConfig, false);
return cfg;
}
KisConfigWidget *KisTIFFExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
return new KisTIFFOptionsWidget(parent);
}
+void KisTIFFExport::initializeCapabilities()
+{
+ addCapability(KisExportCheckRegistry::instance()->get("NodeTypeCheck/KisGroupLayer")->create(KisExportCheckBase::UNSUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
+ addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
+
+ QList<QPair<KoID, KoID> > supportedColorModels;
+ supportedColorModels << QPair<KoID, KoID>()
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Float16BitsColorDepthID)
+ << QPair<KoID, KoID>(RGBAColorModelID, Float32BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(GrayAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(CMYKAColorModelID, Integer8BitsColorDepthID)
+ << QPair<KoID, KoID>(CMYKAColorModelID, Integer16BitsColorDepthID)
+ << QPair<KoID, KoID>(LABAColorModelID, Integer16BitsColorDepthID);
+ addSupportedColorModels(supportedColorModels, "TIFF");
+
+}
+
#include <kis_tiff_export.moc>
diff --git a/plugins/impex/tiff/kis_tiff_export.h b/plugins/impex/tiff/kis_tiff_export.h
index 839f395af2..4bd4517bbd 100644
--- a/plugins/impex/tiff/kis_tiff_export.h
+++ b/plugins/impex/tiff/kis_tiff_export.h
@@ -1,41 +1,42 @@
/*
* 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.
*/
#ifndef _KIS_TIFF_EXPORT_H_
#define _KIS_TIFF_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
#include <kis_config_widget.h>
class KisTIFFExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisTIFFExport(QObject *parent, const QVariantList &);
virtual ~KisTIFFExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const;
KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
+ void initializeCapabilities();
};
#endif
diff --git a/plugins/impex/tiff/kis_tiff_import.cc b/plugins/impex/tiff/kis_tiff_import.cc
index 0900838cee..c3d795dc07 100644
--- a/plugins/impex/tiff/kis_tiff_import.cc
+++ b/plugins/impex/tiff/kis_tiff_import.cc
@@ -1,100 +1,72 @@
/*
* 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_tiff_import.h"
#include <QFileInfo>
#include <kpluginfactory.h>
-#include <KisFilterChain.h>
-
#include <KisDocument.h>
#include <kis_image.h>
#include <KisViewManager.h>
#include "kis_tiff_converter.h"
K_PLUGIN_FACTORY_WITH_JSON(TIFFImportFactory, "krita_tiff_import.json", registerPlugin<KisTIFFImport>();)
KisTIFFImport::KisTIFFImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisTIFFImport::~KisTIFFImport()
{
}
-KisImportExportFilter::ConversionStatus KisTIFFImport::convert(const QByteArray&, const QByteArray& to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisTIFFImport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP /*configuration*/)
{
- Q_UNUSED(configuration);
- dbgFile << "Importing using TIFFImport!";
+ KisTIFFConverter tiffConverter(document);
- if (to != "application/x-krita")
+ switch (tiffConverter.buildImage(filename())) {
+ case KisImageBuilder_RESULT_UNSUPPORTED:
+ return KisImportExportFilter::NotImplemented;
+ case KisImageBuilder_RESULT_INVALID_ARG:
return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- doc -> prepareForImport();
-
- if (!filename.isEmpty()) {
-
- if (!QFileInfo(filename).exists()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- KisTIFFConverter ib(doc);
-
-// if (view != 0)
-// view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true);
-
- switch (ib.buildImage(filename)) {
- case KisImageBuilder_RESULT_UNSUPPORTED:
- return KisImportExportFilter::NotImplemented;
- case KisImageBuilder_RESULT_INVALID_ARG:
- return KisImportExportFilter::BadMimeType;
- case KisImageBuilder_RESULT_NO_URI:
- case KisImageBuilder_RESULT_NOT_LOCAL:
- return KisImportExportFilter::FileNotFound;
- case KisImageBuilder_RESULT_BAD_FETCH:
- case KisImageBuilder_RESULT_EMPTY:
- return KisImportExportFilter::ParsingError;
- case KisImageBuilder_RESULT_FAILURE:
- return KisImportExportFilter::InternalError;
- case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
- return KisImportExportFilter::WrongFormat;
- case KisImageBuilder_RESULT_OK:
- doc -> setCurrentImage(ib.image());
- return KisImportExportFilter::OK;
- default:
- break;
- }
-
+ case KisImageBuilder_RESULT_NO_URI:
+ case KisImageBuilder_RESULT_NOT_LOCAL:
+ return KisImportExportFilter::FileNotFound;
+ case KisImageBuilder_RESULT_BAD_FETCH:
+ case KisImageBuilder_RESULT_EMPTY:
+ return KisImportExportFilter::ParsingError;
+ case KisImageBuilder_RESULT_FAILURE:
+ return KisImportExportFilter::InternalError;
+ case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
+ return KisImportExportFilter::WrongFormat;
+ case KisImageBuilder_RESULT_OK:
+ document -> setCurrentImage(tiffConverter.image());
+ return KisImportExportFilter::OK;
+ default:
+ break;
}
- return KisImportExportFilter::StorageCreationError;
+ return KisImportExportFilter::InternalError;
}
#include <kis_tiff_import.moc>
diff --git a/plugins/impex/tiff/kis_tiff_import.h b/plugins/impex/tiff/kis_tiff_import.h
index 54b4e2abed..99e04c4a38 100644
--- a/plugins/impex/tiff/kis_tiff_import.h
+++ b/plugins/impex/tiff/kis_tiff_import.h
@@ -1,36 +1,36 @@
/*
* 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.
*/
#ifndef _KIS_TIFF_IMPORT_H_
#define _KIS_TIFF_IMPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisTIFFImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisTIFFImport(QObject *parent, const QVariantList &);
virtual ~KisTIFFImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/tiff/kis_tiff_writer_visitor.cpp b/plugins/impex/tiff/kis_tiff_writer_visitor.cpp
index fb06427172..c8ef4af556 100644
--- a/plugins/impex/tiff/kis_tiff_writer_visitor.cpp
+++ b/plugins/impex/tiff/kis_tiff_writer_visitor.cpp
@@ -1,268 +1,225 @@
/*
* 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 <klocalizedstring.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)
{
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_ICCLAB;
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::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 (m_options->alpha) *(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 (m_options->alpha) *(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 (m_options->alpha) *(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 (m_options->alpha) *(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)
+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 (m_options->alpha) {
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
if (m_options->saveProfile) {
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.constData());
}
}
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_ICCLAB: {
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/plugins/impex/tiff/kis_tiff_writer_visitor.h b/plugins/impex/tiff/kis_tiff_writer_visitor.h
index b874d86b44..a41c9f14ea 100644
--- a/plugins/impex/tiff/kis_tiff_writer_visitor.h
+++ b/plugins/impex/tiff/kis_tiff_writer_visitor.h
@@ -1,86 +1,120 @@
/*
* 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.
*/
#ifndef KIS_TIFF_WRITER_VISITOR_H
#define KIS_TIFF_WRITER_VISITOR_H
#include <kis_node_visitor.h>
#include "kis_types.h"
#include <tiffio.h>
+#include <kis_annotation.h>
+#include <kis_paint_device.h>
+#include <kis_group_layer.h>
+#include <kis_generator_layer.h>
+#include <kis_clone_layer.h>
+#include <kis_external_layer_iface.h>
+#include <kis_adjustment_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>
+
struct KisTIFFOptions;
/**
@author Cyrille Berger <cberger@cberger.net>
*/
class KisTIFFWriterVisitor : public KisNodeVisitor
{
public:
using KisNodeVisitor::visit;
KisTIFFWriterVisitor(TIFF*image, KisTIFFOptions* options);
~KisTIFFWriterVisitor();
public:
- bool visit(KisPaintLayer *layer);
- bool visit(KisGroupLayer *layer);
- bool visit(KisGeneratorLayer*);
-
bool visit(KisNode*) {
return true;
}
- bool visit(KisCloneLayer*) {
- return true;
+
+ bool visit(KisPaintLayer *layer) {
+ return saveLayerProjection(layer);
+ }
+
+ bool visit(KisGroupLayer *layer) {
+ dbgFile << "Visiting on grouplayer" << layer->name() << "";
+ return visitAll(layer, true);
}
+
+ bool visit(KisGeneratorLayer *layer) {
+ return saveLayerProjection(layer);
+ }
+
+ bool visit(KisCloneLayer *layer) {
+ return saveLayerProjection(layer);
+ }
+
+ bool visit(KisExternalLayer *layer) {
+ return saveLayerProjection(layer);
+ }
+
+ bool visit(KisAdjustmentLayer *layer) {
+ return saveLayerProjection(layer);
+ }
+
bool visit(KisFilterMask*) {
return true;
}
+
bool visit(KisTransformMask*) {
return true;
}
+
bool visit(KisTransparencyMask*) {
return true;
}
+
bool visit(KisSelectionMask*) {
return true;
}
+
bool visit(KisColorizeMask*) {
return true;
}
- bool visit(KisExternalLayer*);
- bool visit(KisAdjustmentLayer*) {
- return true;
- }
private:
inline TIFF* image() {
return m_image;
}
bool copyDataToStrips(KisHLineConstIteratorSP it, tdata_t buff, uint8 depth, uint16 sample_format, uint8 nbcolorssamples, quint8* poses);
bool saveLayerProjection(KisLayer *);
private:
TIFF* m_image;
KisTIFFOptions* m_options;
};
#endif
diff --git a/plugins/impex/tiff/krita_tiff_export.json b/plugins/impex/tiff/krita_tiff_export.json
index 4ad46931f0..5d763e7b91 100644
--- a/plugins/impex/tiff/krita_tiff_export.json
+++ b/plugins/impex/tiff/krita_tiff_export.json
@@ -1,14 +1,14 @@
{
"Icon": "",
"Id": "Krita TIFF Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "image/tiff",
- "X-KDE-Import": "application/x-krita",
+
"X-KDE-Library": "kritatiffexport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "tif,tiff"
}
diff --git a/plugins/impex/tiff/krita_tiff_import.json b/plugins/impex/tiff/krita_tiff_import.json
index 8ed8b2a5ff..6e728a9189 100644
--- a/plugins/impex/tiff/krita_tiff_import.json
+++ b/plugins/impex/tiff/krita_tiff_import.json
@@ -1,14 +1,14 @@
{
"Icon": "",
"Id": "Krita TIFF Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/tiff",
"X-KDE-Library": "kritatiffimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "tif,tiff"
}
diff --git a/plugins/impex/video/kis_video_export.cpp b/plugins/impex/video/kis_video_export.cpp
index 48e85dfedc..0110df08eb 100644
--- a/plugins/impex/video/kis_video_export.cpp
+++ b/plugins/impex/video/kis_video_export.cpp
@@ -1,147 +1,133 @@
/*
* Copyright (c) 2016 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_video_export.h"
#include <QCheckBox>
#include <QSlider>
#include <QMessageBox>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <QApplication>
#include <kis_debug.h>
#include <KisImportExportManager.h>
-#include <KisFilterChain.h>
#include <KoColorSpaceConstants.h>
#include <KoDialog.h>
#include "KisPart.h"
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_config.h>
#include <kis_cursor_override_hijacker.h>
#include "video_saver.h"
#include "video_export_options_dialog.h"
K_PLUGIN_FACTORY_WITH_JSON(KisVideoExportFactory, "krita_video_export.json", registerPlugin<KisVideoExport>();)
KisVideoExport::KisVideoExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisVideoExport::~KisVideoExport()
{
}
-KisImportExportFilter::ConversionStatus KisVideoExport::convert(const QByteArray &from, const QByteArray &to, KisPropertiesConfigurationSP configuration)
+KisImportExportFilter::ConversionStatus KisVideoExport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP configuration)
{
- Q_UNUSED(to);
-
- if (from != "application/x-krita")
- return KisImportExportFilter::NotImplemented;
-
- KisDocument *input = inputDocument();
- QString filename = outputFile();
-
- if (!input)
- return KisImportExportFilter::NoDocumentCreated;
-
- if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
-
- VideoSaver videoSaver(input, getBatchMode());
+ VideoSaver videoSaver(document, batchMode());
if (!videoSaver.hasFFMpeg()) {
const QString warningMessage =
i18n("Could not find \'ffmpeg\' binary. Saving to video formats is impossible.");
- if (!getBatchMode()) {
+ if (!batchMode()) {
QMessageBox::critical(KisPart::instance()->currentMainwindow(),
i18n("Video Export Error"),
warningMessage);
}
return KisImportExportFilter::UsageError;
}
- KisImageBuilder_Result res = videoSaver.encode(filename, configuration);
+ KisImageBuilder_Result res = videoSaver.encode(filename(), configuration);
if (res == KisImageBuilder_RESULT_OK) {
return KisImportExportFilter::OK;
}
else if (res == KisImageBuilder_RESULT_CANCEL) {
return KisImportExportFilter::ProgressCancelled;
}
else {
- input->setErrorMessage(i18n("FFMpeg failed to convert the image sequence. Check the logfile in your output directory for more information."));
+ document->setErrorMessage(i18n("FFMpeg failed to convert the image sequence. Check the logfile in your output directory for more information."));
}
return KisImportExportFilter::InternalError;
}
KisPropertiesConfigurationSP KisVideoExport::defaultConfiguration(const QByteArray &from, const QByteArray &to) const
{
Q_UNUSED(from);
Q_ASSERT(!to.isEmpty());
KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
cfg->setProperty("h264PresetIndex", 5);
cfg->setProperty("h264ConstantRateFactor", 23);
cfg->setProperty("h264ProfileIndex", 4);
cfg->setProperty("h264TuneIndex", 1);
cfg->setProperty("TheoraBitrate", 5000);
cfg->setProperty("CustomLineValue", "");
if (to == "video/ogg") {
cfg->setProperty("CodecIndex", VideoExportOptionsDialog::CODEC_THEORA);
}
else if (to == "video/x-matroska" || to == "video/mp4") {
cfg->setProperty("CodecIndex", VideoExportOptionsDialog::CODEC_H264);
}
cfg->setProperty("mimetype", to);
return cfg;
}
KisPropertiesConfigurationSP KisVideoExport::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
{
KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
QString filterConfig = KisConfig().exportConfiguration("FFMPEG_CONFIG");
cfg->fromXML(filterConfig, false);
return cfg;
}
KisConfigWidget *KisVideoExport::createConfigurationWidget(QWidget *parent, const QByteArray &from, const QByteArray &to) const
{
Q_UNUSED(from);
KisConfigWidget *w = 0;
if (to != "image/gif") {
w = new VideoExportOptionsDialog(parent);
}
return w;
}
#include "kis_video_export.moc"
diff --git a/plugins/impex/video/kis_video_export.h b/plugins/impex/video/kis_video_export.h
index 9f6d28edc0..4c1edf0c4a 100644
--- a/plugins/impex/video/kis_video_export.h
+++ b/plugins/impex/video/kis_video_export.h
@@ -1,41 +1,42 @@
/*
* Copyright (c) 2016 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_VIDEO_EXPORT_H_
#define _KIS_VIDEO_EXPORT_H_
#include <QVariant>
#include <KisImportExportFilter.h>
class KisVideoExport : public KisImportExportFilter
{
Q_OBJECT
public:
KisVideoExport(QObject *parent, const QVariantList &);
virtual ~KisVideoExport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from, const QByteArray& to) const;
KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const;
+
};
#endif
diff --git a/plugins/impex/video/krita_video_export.json b/plugins/impex/video/krita_video_export.json
index b6a2a16ede..3186eae0ae 100644
--- a/plugins/impex/video/krita_video_export.json
+++ b/plugins/impex/video/krita_video_export.json
@@ -1,13 +1,12 @@
{
"Id": "Krita Video Export Filter",
"NoDisplay": "true",
"Type": "Service",
"X-KDE-Export": "video/x-matroska,image/gif,video/ogg,video/mp4",
- "X-KDE-Import": "application/x-krita",
"X-KDE-Library": "kritavideoexport",
"X-KDE-ServiceTypes": [
"Krita/AnimationExporter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "mkv,gif,ogg,mp4"
}
diff --git a/plugins/impex/video/video_saver.cpp b/plugins/impex/video/video_saver.cpp
index f70b0ac6cc..6e808e7053 100644
--- a/plugins/impex/video/video_saver.cpp
+++ b/plugins/impex/video/video_saver.cpp
@@ -1,321 +1,322 @@
/*
* Copyright (c) 2016 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 "video_saver.h"
#include <QDebug>
#include <QFileInfo>
#include <KisDocument.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoResourcePaths.h>
#include <kis_image.h>
#include <kis_image_animation_interface.h>
#include <kis_time_range.h>
#include "kis_config.h"
#include "kis_animation_exporter.h"
#include <QFileSystemWatcher>
#include <QProcess>
#include <QProgressDialog>
#include <QEventLoop>
#include <QTemporaryFile>
#include <QTemporaryDir>
#include "KisPart.h"
class KisFFMpegProgressWatcher : public QObject {
Q_OBJECT
public:
KisFFMpegProgressWatcher(QFile &progressFile, int totalFrames)
: m_progressFile(progressFile),
m_totalFrames(totalFrames)
{
connect(&m_progressWatcher, SIGNAL(fileChanged(QString)), SLOT(slotFileChanged()));
m_progressWatcher.addPath(m_progressFile.fileName());
}
private Q_SLOTS:
void slotFileChanged() {
int currentFrame = -1;
bool isEnded = false;
while(!m_progressFile.atEnd()) {
QString line = QString(m_progressFile.readLine()).remove(QChar('\n'));
QStringList var = line.split("=");
if (var[0] == "frame") {
currentFrame = var[1].toInt();
} else if (var[0] == "progress") {
isEnded = var[1] == "end";
}
}
if (isEnded) {
emit sigProgressChanged(100);
emit sigProcessingFinished();
} else {
+
emit sigProgressChanged(100 * currentFrame / m_totalFrames);
}
}
Q_SIGNALS:
void sigProgressChanged(int percent);
void sigProcessingFinished();
private:
QFileSystemWatcher m_progressWatcher;
QFile &m_progressFile;
int m_totalFrames;
};
class KisFFMpegRunner
{
public:
KisFFMpegRunner(const QString &ffmpegPath)
: m_cancelled(false),
m_ffmpegPath(ffmpegPath) {}
public:
KisImageBuilder_Result runFFMpeg(const QStringList &specialArgs,
const QString &actionName,
const QString &logPath,
int totalFrames)
{
dbgFile << "runFFMpeg: specialArgs" << specialArgs
<< "actionName" << actionName
<< "logPath" << logPath
<< "totalFrames" << totalFrames;
QTemporaryFile progressFile("KritaFFmpegProgress.XXXXXX");
progressFile.open();
m_process.setStandardOutputFile(logPath);
m_process.setProcessChannelMode(QProcess::MergedChannels);
QStringList args;
args << "-v" << "debug"
<< "-nostdin"
<< "-progress" << progressFile.fileName()
<< specialArgs;
m_cancelled = false;
m_process.start(m_ffmpegPath, args);
return waitForFFMpegProcess(actionName, progressFile, m_process, totalFrames);
}
void cancel() {
m_cancelled = true;
m_process.kill();
}
private:
KisImageBuilder_Result waitForFFMpegProcess(const QString &message,
QFile &progressFile,
QProcess &ffmpegProcess,
int totalFrames)
{
KisFFMpegProgressWatcher watcher(progressFile, totalFrames);
QProgressDialog progress(message, "", 0, 0, KisPart::instance()->currentMainwindow());
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.setRange(0,100);
QEventLoop loop;
loop.connect(&watcher, SIGNAL(sigProcessingFinished()), SLOT(quit()));
loop.connect(&ffmpegProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(quit()));
loop.connect(&watcher, SIGNAL(sigProgressChanged(int)), &progress, SLOT(setValue(int)));
loop.exec();
// wait for some errorneous case
ffmpegProcess.waitForFinished(5000);
KisImageBuilder_Result retval = KisImageBuilder_RESULT_OK;
if (ffmpegProcess.state() != QProcess::NotRunning) {
// sorry...
ffmpegProcess.kill();
retval = KisImageBuilder_RESULT_FAILURE;
} else if (m_cancelled) {
retval = KisImageBuilder_RESULT_CANCEL;
} else if (ffmpegProcess.exitCode()) {
retval = KisImageBuilder_RESULT_FAILURE;
}
return retval;
}
private:
QProcess m_process;
bool m_cancelled;
QString m_ffmpegPath;
};
VideoSaver::VideoSaver(KisDocument *doc, bool batchMode)
: m_image(doc->image())
, m_doc(doc)
, m_batchMode(batchMode)
, m_ffmpegPath(findFFMpeg())
, m_runner(new KisFFMpegRunner(m_ffmpegPath))
{
}
VideoSaver::~VideoSaver()
{
}
KisImageSP VideoSaver::image()
{
return m_image;
}
bool VideoSaver::hasFFMpeg() const
{
return !m_ffmpegPath.isEmpty();
}
QString VideoSaver::findFFMpeg()
{
QString result;
QStringList proposedPaths;
QString customPath = KisConfig().customFFMpegPath();
proposedPaths << customPath;
proposedPaths << customPath + QDir::separator() + "ffmpeg";
proposedPaths << "ffmpeg";
proposedPaths << KoResourcePaths::getApplicationRoot() +
QDir::separator() + "bin" + QDir::separator() + "ffmpeg";
Q_FOREACH (const QString &path, proposedPaths) {
if (path.isEmpty()) continue;
QProcess testProcess;
testProcess.start(path, QStringList() << "-version");
testProcess.waitForFinished(1000);
const bool successfulStart =
testProcess.state() == QProcess::NotRunning &&
testProcess.error() == QProcess::UnknownError;
if (successfulStart) {
result = path;
break;
}
}
return result;
}
KisImageBuilder_Result VideoSaver::encode(const QString &filename, KisPropertiesConfigurationSP configuration)
{
//qDebug() << "ffmpeg" << m_ffmpegPath << "filename" << filename << "configuration" << configuration->toXML();
if (m_ffmpegPath.isEmpty()) return KisImageBuilder_RESULT_FAILURE;
KisImageBuilder_Result result = KisImageBuilder_RESULT_OK;
KisImageAnimationInterface *animation = m_image->animationInterface();
const KisTimeRange fullRange = animation->fullClipRange();
const KisTimeRange clipRange(configuration->getInt("firstframe", fullRange.start()), configuration->getInt("lastFrame"), fullRange.end());
const int frameRate = animation->framerate();
const QDir framesDir(configuration->getString("directory"));
const QString resultFile = framesDir.absolutePath() + "/" + filename;
const QFileInfo info(resultFile);
const QString suffix = info.suffix().toLower();
const QString palettePath = framesDir.filePath("palette.png");
const QString savedFilesMask = configuration->getString("savedFilesMask");
const QStringList additionalOptionsList = configuration->getString("customUserOptions").split(' ', QString::SkipEmptyParts);
if (suffix == "gif") {
{
QStringList args;
args << "-r" << QString::number(frameRate)
<< "-i" << savedFilesMask
<< "-vf" << "palettegen"
<< "-y" << palettePath;
KisImageBuilder_Result result =
m_runner->runFFMpeg(args, i18n("Fetching palette..."),
framesDir.filePath("log_palettegen.log"),
- clipRange.duration());
+ clipRange.duration() + clipRange.start());
if (result) {
return result;
}
}
{
QStringList args;
args << "-r" << QString::number(frameRate)
<< "-i" << savedFilesMask
<< "-i" << palettePath
<< "-lavfi" << "[0:v][1:v] paletteuse"
<< additionalOptionsList
<< "-y" << resultFile;
KisImageBuilder_Result result =
m_runner->runFFMpeg(args, i18n("Encoding frames..."),
framesDir.filePath("log_paletteuse.log"),
- clipRange.duration());
+ clipRange.duration() + clipRange.start());
if (result) {
return result;
}
}
} else {
QStringList args;
args << "-r" << QString::number(frameRate)
<< "-i" << savedFilesMask
<< additionalOptionsList
<< "-y" << resultFile;
result = m_runner->runFFMpeg(args, i18n("Encoding frames..."),
framesDir.filePath("log_encode.log"),
- clipRange.duration());
+ clipRange.duration() + clipRange.start());
}
return result;
}
void VideoSaver::cancel()
{
m_runner->cancel();
}
#include "video_saver.moc"
diff --git a/plugins/impex/xcf/kis_xcf_import.cpp b/plugins/impex/xcf/kis_xcf_import.cpp
index edab140bad..a98c4ea0c7 100644
--- a/plugins/impex/xcf/kis_xcf_import.cpp
+++ b/plugins/impex/xcf/kis_xcf_import.cpp
@@ -1,350 +1,318 @@
/*
* Copyright (c) 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_xcf_import.h"
#include <ctype.h>
#include <QApplication>
#include <QFile>
#include <qendian.h>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceTraits.h>
#include <KoCompositeOpRegistry.h>
-#include <KisFilterChain.h>
-
#include <kis_debug.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
#include <kis_paint_layer.h>
#include <kis_transparency_mask.h>
#include "kis_iterator_ng.h"
#include "kis_types.h"
#include <KoColorModelStandardIds.h>
extern "C" {
#include "xcftools.h"
#include "pixels.h"
#define GET_RED(x) (x >> RED_SHIFT)
#define GET_GREEN(x) (x >> GREEN_SHIFT)
#define GET_BLUE(x) (x >> BLUE_SHIFT)
#define GET_ALPHA(x) (x >> ALPHA_SHIFT)
}
-struct Layer {
- KisLayerSP layer;
- int depth;
- KisMaskSP mask;
-};
-
-KisGroupLayerSP findGroup(const QVector<Layer> &layers, const Layer& layer, int i)
-{
- for (; i < layers.size(); ++i) {
- KisGroupLayerSP group = dynamic_cast<KisGroupLayer*>(const_cast<KisLayer*>(layers[i].layer.data()));
- if (group && (layers[i].depth == layer.depth -1)) {
- return group;
- }
- }
- return 0;
-}
-
-void addLayers(const QVector<Layer> &layers, KisImageSP image, int depth)
-{
- for(int i = 0; i < layers.size(); i++) {
- const Layer &layer = layers[i];
- if (layer.depth == depth) {
- KisGroupLayerSP group = (depth == 0 ? image->rootLayer() : findGroup(layers, layer, i));
- image->addNode(layer.layer, group);
- if (layer.mask) {
- image->addNode(layer.mask, layer.layer);
- }
- }
- }
-}
-
-K_PLUGIN_FACTORY_WITH_JSON(XCFImportFactory, "krita_xcf_import.json", registerPlugin<KisXCFImport>();)
-
-KisXCFImport::KisXCFImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
-{
-}
-
-KisXCFImport::~KisXCFImport()
-{
-}
-
-KisImportExportFilter::ConversionStatus KisXCFImport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration)
-{
- Q_UNUSED(from);
- Q_UNUSED(configuration);
- dbgFile << "Importing using XCFImport!";
-
- if (to != "application/x-krita")
- return KisImportExportFilter::BadMimeType;
-
- KisDocument * doc = outputDocument();
-
- if (!doc)
- return KisImportExportFilter::NoDocumentCreated;
-
- QString filename = inputFile();
-
- if (filename.isEmpty()) {
- return KisImportExportFilter::FileNotFound;
- }
-
- QFile fp(filename);
- if (fp.exists()) {
- doc->prepareForImport();
- return loadFromDevice(&fp, doc);
- }
-
- return KisImportExportFilter::CreationError;
-}
-
QString layerModeG2K(GimpLayerModeEffects mode)
{
switch (mode) {
case GIMP_NORMAL_MODE:
return COMPOSITE_OVER;
case GIMP_DISSOLVE_MODE:
return COMPOSITE_DISSOLVE;
case GIMP_MULTIPLY_MODE:
return COMPOSITE_MULT;
case GIMP_SCREEN_MODE:
return COMPOSITE_SCREEN;
case GIMP_OVERLAY_MODE:
case GIMP_SOFTLIGHT_MODE:
return COMPOSITE_OVERLAY;
case GIMP_DIFFERENCE_MODE:
return COMPOSITE_DIFF;
case GIMP_ADDITION_MODE:
return COMPOSITE_ADD;
case GIMP_SUBTRACT_MODE:
return COMPOSITE_SUBTRACT;
case GIMP_DARKEN_ONLY_MODE:
return COMPOSITE_DARKEN;
case GIMP_LIGHTEN_ONLY_MODE:
return COMPOSITE_LIGHTEN;
case GIMP_HUE_MODE:
return COMPOSITE_HUE_HSL;
case GIMP_SATURATION_MODE:
return COMPOSITE_SATURATION_HSV;
case GIMP_COLOR_MODE:
return COMPOSITE_COLOR_HSL;
case GIMP_VALUE_MODE:
return COMPOSITE_VALUE;
case GIMP_DIVIDE_MODE:
return COMPOSITE_DIVIDE;
case GIMP_DODGE_MODE:
return COMPOSITE_DODGE;
case GIMP_BURN_MODE:
return COMPOSITE_BURN;
case GIMP_ERASE_MODE:
return COMPOSITE_ERASE;
case GIMP_REPLACE_MODE:
return COMPOSITE_COPY;
case GIMP_HARDLIGHT_MODE:
return COMPOSITE_HARD_LIGHT;
case GIMP_COLOR_ERASE_MODE:
case GIMP_NORMAL_NOPARTIAL_MODE:
case GIMP_ANTI_ERASE_MODE:
case GIMP_GRAIN_EXTRACT_MODE:
return COMPOSITE_GRAIN_EXTRACT;
case GIMP_GRAIN_MERGE_MODE:
return COMPOSITE_GRAIN_MERGE;
case GIMP_BEHIND_MODE:
break;
}
dbgFile << "Unknown mode: " << mode;
return COMPOSITE_OVER;
}
-KisImportExportFilter::ConversionStatus KisXCFImport::loadFromDevice(QIODevice* device, KisDocument* doc)
+struct Layer {
+ KisLayerSP layer;
+ int depth;
+ KisMaskSP mask;
+};
+
+KisGroupLayerSP findGroup(const QVector<Layer> &layers, const Layer& layer, int i)
+{
+ for (; i < layers.size(); ++i) {
+ KisGroupLayerSP group = dynamic_cast<KisGroupLayer*>(const_cast<KisLayer*>(layers[i].layer.data()));
+ if (group && (layers[i].depth == layer.depth -1)) {
+ return group;
+ }
+ }
+ return 0;
+}
+
+void addLayers(const QVector<Layer> &layers, KisImageSP image, int depth)
+{
+ for(int i = 0; i < layers.size(); i++) {
+ const Layer &layer = layers[i];
+ if (layer.depth == depth) {
+ KisGroupLayerSP group = (depth == 0 ? image->rootLayer() : findGroup(layers, layer, i));
+ image->addNode(layer.layer, group);
+ if (layer.mask) {
+ image->addNode(layer.mask, layer.layer);
+ }
+ }
+ }
+}
+
+K_PLUGIN_FACTORY_WITH_JSON(XCFImportFactory, "krita_xcf_import.json", registerPlugin<KisXCFImport>();)
+
+KisXCFImport::KisXCFImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
+{
+}
+
+KisXCFImport::~KisXCFImport()
+{
+}
+
+KisImportExportFilter::ConversionStatus KisXCFImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
dbgFile << "Start decoding file";
- // Read the file into memory
- device->open(QIODevice::ReadOnly);
- QByteArray data = device->readAll();
+ QByteArray data = io->readAll();
xcf_file = (uint8_t*)data.data();
xcf_length = data.size();
- device->close();
+ io->close();
// Decode the data
getBasicXcfInfo() ;
if (XCF.version < 0 || XCF.version > 3) {
- doc->setErrorMessage(i18n("This XCF file is too new; Krita cannot support XCF files written by GIMP 2.9 or newer."));
+ document->setErrorMessage(i18n("This XCF file is too new; Krita cannot support XCF files written by GIMP 2.9 or newer."));
return KisImportExportFilter::UnsupportedVersion;
}
initColormap();
dbgFile << XCF.version << "width = " << XCF.width << "height = " << XCF.height << "layers = " << XCF.numLayers;
// Create the image
- KisImageSP image = new KisImage(doc->createUndoStore(), XCF.width, XCF.height, KoColorSpaceRegistry::instance()->rgb8(), "built image");
+ KisImageSP image = new KisImage(document->createUndoStore(), XCF.width, XCF.height, KoColorSpaceRegistry::instance()->rgb8(), "built image");
QVector<Layer> layers;
uint maxDepth = 0;
// Read layers
for (int i = 0; i < XCF.numLayers; ++i) {
Layer layer;
xcfLayer& xcflayer = XCF.layers[i];
dbgFile << i << " name = " << xcflayer.name << " opacity = " << xcflayer.opacity << "group:" << xcflayer.isGroup << xcflayer.pathLength;
dbgFile << ppVar(xcflayer.dim.width) << ppVar(xcflayer.dim.height) << ppVar(xcflayer.dim.tilesx) << ppVar(xcflayer.dim.tilesy) << ppVar(xcflayer.dim.ntiles) << ppVar(xcflayer.dim.c.t) << ppVar(xcflayer.dim.c.l) << ppVar(xcflayer.dim.c.r) << ppVar(xcflayer.dim.c.b);
maxDepth = qMax(maxDepth, xcflayer.pathLength);
bool isRgbA = false;
// Select the color space
const KoColorSpace* colorSpace = 0;
switch (xcflayer.type) {
case GIMP_INDEXED_IMAGE:
case GIMP_INDEXEDA_IMAGE:
case GIMP_RGB_IMAGE:
case GIMP_RGBA_IMAGE:
colorSpace = KoColorSpaceRegistry::instance()->rgb8();
isRgbA = true;
break;
case GIMP_GRAY_IMAGE:
case GIMP_GRAYA_IMAGE:
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
isRgbA = false;
break;
}
// Create the layer
KisLayerSP kisLayer;
if (xcflayer.isGroup) {
kisLayer = new KisGroupLayer(image, QString::fromUtf8(xcflayer.name), xcflayer.opacity);
}
else {
kisLayer = new KisPaintLayer(image, QString::fromUtf8(xcflayer.name), xcflayer.opacity, colorSpace);
}
// Set some properties
kisLayer->setCompositeOpId(layerModeG2K(xcflayer.mode));
kisLayer->setVisible(xcflayer.isVisible);
kisLayer->disableAlphaChannel(xcflayer.mode != GIMP_NORMAL_MODE);
layer.layer = kisLayer;
layer.depth = xcflayer.pathLength;
// Copy the data in the image
initLayer(&xcflayer);
int left = xcflayer.dim.c.l;
int top = xcflayer.dim.c.t;
if (!xcflayer.isGroup) {
// Copy the data;
for (unsigned int x = 0; x < xcflayer.dim.width; x += TILE_WIDTH) {
for (unsigned int y = 0; y < xcflayer.dim.height; y += TILE_HEIGHT) {
rect want;
want.l = x + left;
want.t = y + top;
want.b = want.t + TILE_HEIGHT;
want.r = want.l + TILE_WIDTH;
Tile* tile = getMaskOrLayerTile(&xcflayer.dim, &xcflayer.pixels, want);
KisHLineIteratorSP it = kisLayer->paintDevice()->createHLineIteratorNG(x, y, TILE_WIDTH);
rgba* data = tile->pixels;
for (int v = 0; v < TILE_HEIGHT; ++v) {
if (isRgbA) {
// RGB image
do {
KoBgrTraits<quint8>::setRed(it->rawData(), GET_RED(*data));
KoBgrTraits<quint8>::setGreen(it->rawData(), GET_GREEN(*data));
KoBgrTraits<quint8>::setBlue(it->rawData(), GET_BLUE(*data));
KoBgrTraits<quint8>::setOpacity(it->rawData(), quint8(GET_ALPHA(*data)), 1);
++data;
} while (it->nextPixel());
} else {
// Grayscale image
do {
it->rawData()[0] = GET_RED(*data);
it->rawData()[1] = GET_ALPHA(*data);
++data;
} while (it->nextPixel());
}
it->nextRow();
}
}
}
// Move the layer to its position
kisLayer->paintDevice()->setX(left);
kisLayer->paintDevice()->setY(top);
}
// Create the mask
if (xcflayer.hasMask) {
KisTransparencyMaskSP mask = new KisTransparencyMask();
layer.mask = mask;
mask->initSelection(kisLayer);
for (unsigned int x = 0; x < xcflayer.dim.width; x += TILE_WIDTH) {
for (unsigned int y = 0; y < xcflayer.dim.height; y += TILE_HEIGHT) {
rect want;
want.l = x + left;
want.t = y + top;
want.b = want.t + TILE_HEIGHT;
want.r = want.l + TILE_WIDTH;
Tile* tile = getMaskOrLayerTile(&xcflayer.dim, &xcflayer.mask, want);
KisHLineIteratorSP it = mask->paintDevice()->createHLineIteratorNG(x, y, TILE_WIDTH);
rgba* data = tile->pixels;
for (int v = 0; v < TILE_HEIGHT; ++v) {
do {
it->rawData()[0] = GET_ALPHA(*data);
++data;
} while (it->nextPixel());
it->nextRow();
}
}
}
mask->paintDevice()->setX(left);
mask->paintDevice()->setY(top);
image->addNode(mask, kisLayer);
}
dbgFile << xcflayer.pixels.tileptrs;
layers.append(layer);
}
for (uint i = 0; i <= maxDepth; ++i) {
addLayers(layers, image, i);
}
- doc->setCurrentImage(image);
+ document->setCurrentImage(image);
return KisImportExportFilter::OK;
+
}
#include "kis_xcf_import.moc"
diff --git a/plugins/impex/xcf/kis_xcf_import.h b/plugins/impex/xcf/kis_xcf_import.h
index 2c6ae03a50..978a69dc33 100644
--- a/plugins/impex/xcf/kis_xcf_import.h
+++ b/plugins/impex/xcf/kis_xcf_import.h
@@ -1,42 +1,40 @@
/*
* Copyright (c) 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.
*/
#ifndef _KIS_XCF_IMPORT_H_
#define _KIS_XCF_IMPORT_H_
#include <QVariant>
#include <QIODevice>
#include <KisImportExportFilter.h>
class KisDocument;
class KisXCFImport : public KisImportExportFilter
{
Q_OBJECT
public:
KisXCFImport(QObject *parent, const QVariantList &);
virtual ~KisXCFImport();
public:
- virtual KisImportExportFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0);
-private:
- KisImportExportFilter::ConversionStatus loadFromDevice(QIODevice* device, KisDocument* doc);
+ virtual KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0);
};
#endif
diff --git a/plugins/impex/xcf/krita_xcf_import.json b/plugins/impex/xcf/krita_xcf_import.json
index d46bcacf02..398183114e 100644
--- a/plugins/impex/xcf/krita_xcf_import.json
+++ b/plugins/impex/xcf/krita_xcf_import.json
@@ -1,13 +1,13 @@
{
"Id": "Krita Gimp Import Filter",
"NoDisplay": "true",
"Type": "Service",
- "X-KDE-Export": "application/x-krita",
+
"X-KDE-Import": "image/x-xcf",
"X-KDE-Library": "kritaxcfimport",
"X-KDE-ServiceTypes": [
"Krita/FileFilter"
],
"X-KDE-Weight": "1",
"X-KDE-Extensions" : "xcf"
}
diff --git a/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp b/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp
index 1d589d0a5a..c8c54ec49b 100644
--- a/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp
+++ b/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp
@@ -1,238 +1,237 @@
/*
* 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_brushop_test.h"
#include <QTest>
#include <qimage_based_test.h>
#include <stroke_testing_utils.h>
#include <brushengine/kis_paint_information.h>
#include <kis_canvas_resource_provider.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include <kis_pressure_mirror_option.h>
#include <kis_pressure_rotation_option.h>
class TestBrushOp : public TestUtil::QImageBasedTest
{
public:
TestBrushOp(const QString &presetFileName, const QString &prefix = "simple")
: QImageBasedTest("brushop") {
m_presetFileName = presetFileName;
m_prefix = prefix;
}
virtual ~TestBrushOp() {}
void test() {
test(false, false, 0.0);
test(false, false, 10.0);
test(false, false, 20.0);
test(true, false, 0.0);
test(true, false, 10.0);
test(true, false, 20.0);
test(false, true, 0.0);
test(false, true, 10.0);
test(false, true, 20.0);
test(true, true, 0.0);
test(true, true, 10.0);
test(true, true, 20.0);
}
void test(bool mirrorX, bool mirrorY, qreal rotation) {
test(mirrorX, mirrorY, rotation, false, false);
test(mirrorX, mirrorY, rotation, true, false);
test(mirrorX, mirrorY, rotation, false, true);
test(mirrorX, mirrorY, rotation, true, true);
}
void test(bool mirrorX, bool mirrorY, qreal rotation, bool mirrorDabX, bool mirrorDabY) {
test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 0.0);
test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 360.0 - 10.0);
test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 360.0 - 20.0);
}
void test(bool mirrorX, bool mirrorY, qreal rotation, bool mirrorDabX, bool mirrorDabY, qreal dabRotation) {
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
KisImageSP image = createTrivialImage(undoStore);
image->initialRefreshGraph();
KisNodeSP paint1 = findNode(image->root(), "paint1");
QVERIFY(paint1->extent().isEmpty());
KisPainter gc(paint1->paintDevice());
QScopedPointer<KoCanvasResourceManager> manager(
utils::createResourceManager(image, 0, m_presetFileName));
KisPaintOpPresetSP preset =
manager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
preset->settings()->setCanvasRotation(rotation);
preset->settings()->setCanvasMirroring(mirrorY, mirrorX);
if (mirrorDabX || mirrorDabY) {
KisPaintOpSettingsSP settings = preset->settings()->clone();
KisPressureMirrorOption mirrorOption;
mirrorOption.readOptionSetting(settings);
mirrorOption.setChecked(true);
mirrorOption.setCurveUsed(false);
mirrorOption.enableHorizontalMirror(mirrorDabX);
mirrorOption.enableVerticalMirror(mirrorDabY);
mirrorOption.writeOptionSetting(settings.data());
preset->setSettings(settings);
}
if (dabRotation != 0.0) {
KisPaintOpSettingsSP settings = preset->settings()->clone();
KisPressureRotationOption rotationOption;
rotationOption.readOptionSetting(settings);
rotationOption.setChecked(true);
rotationOption.setCurveUsed(false);
rotationOption.setValue(dabRotation / 360.0);
rotationOption.writeOptionSetting(settings.data());
preset->setSettings(settings);
}
QString testName =
QString("%7_cmY_%1_cmX_%2_cR_%3_dmX_%4_dmY_%5_dR_%6")
.arg(mirrorY)
.arg(mirrorX)
.arg(rotation)
.arg(mirrorDabX)
.arg(mirrorDabY)
.arg(std::fmod(360.0 - dabRotation, 360.0))
.arg(m_prefix);
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
paint1,
- image->postExecutionUndoAdapter(),
manager.data());
resources->setupPainter(&gc);
doPaint(gc);
checkOneLayer(image, paint1, testName);
}
virtual void doPaint(KisPainter &gc) {
KisPaintInformation pi(QPointF(100, 100), 1.0);
KisDistanceInformation dist;
gc.paintAt(pi, &dist);
}
QString m_presetFileName;
QString m_prefix;
};
class TestBrushOpLines : public TestBrushOp
{
public:
TestBrushOpLines(const QString &presetFileName)
: TestBrushOp(presetFileName) {
}
void doPaint(KisPainter &gc) override {
QVector<KisPaintInformation> vector;
vector << KisPaintInformation(QPointF(100, 100));
vector << KisPaintInformation(QPointF(200, 150));
vector << KisPaintInformation(QPointF(100, 350));
KisDistanceInformation dist;
for (int i = 1; i < vector.size(); i++) {
gc.paintLine(vector[i - 1], vector[i], &dist);
}
}
};
class TestBrushOpPressureLines : public TestBrushOp
{
public:
TestBrushOpPressureLines(const QString &presetFileName, const QString &prefix)
: TestBrushOp(presetFileName, prefix) {
}
void doPaint(KisPainter &gc) override {
QVector<KisPaintInformation> vector;
vector << KisPaintInformation(QPointF(0, 0), 0.2);
vector << KisPaintInformation(QPointF(200, 50), 1.0);
vector << KisPaintInformation(QPointF(100, 250), 0.0);
vector << KisPaintInformation(QPointF(200, 150), 1.0);
vector << KisPaintInformation(QPointF(100, 350), 1.0);
KisDistanceInformation dist;
for (int i = 1; i < vector.size(); i++) {
gc.paintLine(vector[i - 1], vector[i], &dist);
}
}
};
void KisBrushOpTest::testRotationMirroring()
{
TestBrushOp t("LR_simple.kpp");
t.test();
}
void KisBrushOpTest::testRotationMirroringDrawingAngle()
{
TestBrushOpLines t("LR_drawing_angle.kpp");
t.test();
}
void KisBrushOpTest::testMagicSeven()
{
/**
* A special preset that forces Qt to bug:
* mask size: 56
* brush size: 7
* therefore scale is: 0.125
* which causes QTransform work as a pure Translate in the mipmap
*/
TestBrushOpPressureLines t("magic_seven.kpp", "magicseven");
t.test();
}
QTEST_MAIN(KisBrushOpTest)
diff --git a/plugins/tools/basictools/CMakeLists.txt b/plugins/tools/basictools/CMakeLists.txt
index 0d6420ff77..83a57a26f3 100644
--- a/plugins/tools/basictools/CMakeLists.txt
+++ b/plugins/tools/basictools/CMakeLists.txt
@@ -1,45 +1,46 @@
if (NOT APPLE)
add_subdirectory(tests)
endif ()
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
set(kritadefaulttools_SOURCES
default_tools.cc
kis_tool_colorpicker.cc
kis_tool_brush.cc
kis_tool_line.cc
kis_tool_line_helper.cpp
kis_tool_fill.cc
kis_tool_rectangle.cc
kis_tool_ellipse.cc
kis_tool_gradient.cc
kis_tool_measure.cc
kis_tool_path.cc
kis_tool_move.cc
kis_tool_movetooloptionswidget.cpp
strokes/move_stroke_strategy.cpp
strokes/move_selection_stroke_strategy.cpp
kis_tool_multihand.cpp
+ kis_tool_multihand_config.cpp
kis_tool_pencil.cc
)
-ki18n_wrap_ui(kritadefaulttools_SOURCES wdgcolorpicker.ui wdgmovetool.ui)
+ki18n_wrap_ui(kritadefaulttools_SOURCES wdgcolorpicker.ui wdgmovetool.ui wdgmultihandtool.ui)
qt5_add_resources(kritadefaulttools_SOURCES defaulttools.qrc )
add_library(kritadefaulttools MODULE ${kritadefaulttools_SOURCES})
generate_export_header(kritadefaulttools BASE_NAME kritadefaulttools)
target_link_libraries(kritadefaulttools kritaui kritabasicflakes)
target_link_libraries(kritadefaulttools ${Boost_SYSTEM_LIBRARY})
install(TARGETS kritadefaulttools DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
########### install files ###############
install( FILES
KisToolPath.action
KisToolPencil.action
DESTINATION ${DATA_INSTALL_DIR}/krita/actions)
diff --git a/plugins/tools/basictools/kis_tool_brush.cc b/plugins/tools/basictools/kis_tool_brush.cc
index be588f5838..b25a8fd2f5 100644
--- a/plugins/tools/basictools/kis_tool_brush.cc
+++ b/plugins/tools/basictools/kis_tool_brush.cc
@@ -1,443 +1,466 @@
/*
* kis_tool_brush.cc - part of Krita
*
* Copyright (c) 2003-2004 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_brush.h"
#include <QCheckBox>
#include <QComboBox>
#include <klocalizedstring.h>
#include <QAction>
#include <QLabel>
#include <kactioncollection.h>
#include <KoCanvasBase.h>
#include <KoCanvasController.h>
#include "kis_cursor.h"
#include "kis_config.h"
#include "kis_slider_spin_box.h"
#include "kundo2magicstring.h"
#define MAXIMUM_SMOOTHNESS_DISTANCE 1000.0 // 0..1000.0 == weight in gui
#define MAXIMUM_MAGNETISM 1000
void KisToolBrush::addSmoothingAction(int enumId, const QString &id, const QString &name, KActionCollection *globalCollection)
{
/**
* KisToolBrush is the base of several tools, but the actions
* should be unique, so let's be careful with them
*/
if (!globalCollection->action(id)) {
QAction *action = new QAction(name, globalCollection);
globalCollection->addAction(id, action);
}
QAction *action = dynamic_cast<QAction*>(globalCollection->action(id));
addAction(id, action);
connect(action, SIGNAL(triggered()), &m_signalMapper, SLOT(map()));
m_signalMapper.setMapping(action, enumId);
}
KisToolBrush::KisToolBrush(KoCanvasBase * canvas)
: KisToolFreehand(canvas,
KisCursor::load("tool_freehand_cursor.png", 5, 5),
kundo2_i18n("Freehand Brush Stroke"))
{
setObjectName("tool_brush");
connect(this, SIGNAL(smoothingTypeChanged()), this, SLOT(resetCursorStyle()));
KActionCollection *collection = this->canvas()->canvasController()->actionCollection();
addSmoothingAction(KisSmoothingOptions::NO_SMOOTHING, "set_no_brush_smoothing", i18nc("@action", "Brush Smoothing: Disabled"), collection);
addSmoothingAction(KisSmoothingOptions::SIMPLE_SMOOTHING, "set_simple_brush_smoothing", i18nc("@action", "Brush Smoothing: Basic"), collection);
addSmoothingAction(KisSmoothingOptions::WEIGHTED_SMOOTHING, "set_weighted_brush_smoothing", i18nc("@action", "Brush Smoothing: Weighted"), collection);
addSmoothingAction(KisSmoothingOptions::STABILIZER, "set_stabilizer_brush_smoothing", i18nc("@action", "Brush Smoothing: Stabilizer"), collection);
}
KisToolBrush::~KisToolBrush()
{
}
void KisToolBrush::activate(ToolActivation activation, const QSet<KoShape*> &shapes)
{
KisToolFreehand::activate(activation, shapes);
connect(&m_signalMapper, SIGNAL(mapped(int)), SLOT(slotSetSmoothingType(int)), Qt::UniqueConnection);
m_configGroup = KSharedConfig::openConfig()->group(toolId());
}
void KisToolBrush::deactivate()
{
disconnect(&m_signalMapper, 0, this, 0);
KisToolFreehand::deactivate();
}
int KisToolBrush::smoothingType() const
{
return smoothingOptions()->smoothingType();
}
bool KisToolBrush::smoothPressure() const
{
return smoothingOptions()->smoothPressure();
}
int KisToolBrush::smoothnessQuality() const
{
return smoothingOptions()->smoothnessDistance();
}
qreal KisToolBrush::smoothnessFactor() const
{
return smoothingOptions()->tailAggressiveness();
}
void KisToolBrush::slotSetSmoothingType(int index)
{
/**
* The slot can also be called from smoothing-type-switching
* action that would mean the combo box will not be synchronized
*/
if (m_cmbSmoothingType->currentIndex() != index) {
m_cmbSmoothingType->setCurrentIndex(index);
}
switch (index) {
case 0:
smoothingOptions()->setSmoothingType(KisSmoothingOptions::NO_SMOOTHING);
showControl(m_sliderSmoothnessDistance, false);
showControl(m_sliderTailAggressiveness, false);
showControl(m_chkSmoothPressure, false);
showControl(m_chkUseScalableDistance, false);
showControl(m_sliderDelayDistance, false);
showControl(m_chkFinishStabilizedCurve, false);
showControl(m_chkStabilizeSensors, false);
break;
case 1:
smoothingOptions()->setSmoothingType(KisSmoothingOptions::SIMPLE_SMOOTHING);
showControl(m_sliderSmoothnessDistance, false);
showControl(m_sliderTailAggressiveness, false);
showControl(m_chkSmoothPressure, false);
showControl(m_chkUseScalableDistance, false);
showControl(m_sliderDelayDistance, false);
showControl(m_chkFinishStabilizedCurve, false);
showControl(m_chkStabilizeSensors, false);
break;
case 2:
smoothingOptions()->setSmoothingType(KisSmoothingOptions::WEIGHTED_SMOOTHING);
showControl(m_sliderSmoothnessDistance, true);
showControl(m_sliderTailAggressiveness, true);
showControl(m_chkSmoothPressure, true);
showControl(m_chkUseScalableDistance, true);
showControl(m_sliderDelayDistance, false);
showControl(m_chkFinishStabilizedCurve, false);
showControl(m_chkStabilizeSensors, false);
break;
case 3:
default:
smoothingOptions()->setSmoothingType(KisSmoothingOptions::STABILIZER);
showControl(m_sliderSmoothnessDistance, true);
showControl(m_sliderTailAggressiveness, false);
showControl(m_chkSmoothPressure, false);
showControl(m_chkUseScalableDistance, true);
showControl(m_sliderDelayDistance, true);
showControl(m_chkFinishStabilizedCurve, true);
showControl(m_chkStabilizeSensors, true);
}
emit smoothingTypeChanged();
}
void KisToolBrush::slotSetSmoothnessDistance(qreal distance)
{
smoothingOptions()->setSmoothnessDistance(distance);
emit smoothnessQualityChanged();
}
void KisToolBrush::slotSetTailAgressiveness(qreal argh_rhhrr)
{
smoothingOptions()->setTailAggressiveness(argh_rhhrr);
emit smoothnessFactorChanged();
}
// used with weighted smoothing
void KisToolBrush::setSmoothPressure(bool value)
{
smoothingOptions()->setSmoothPressure(value);
}
void KisToolBrush::slotSetMagnetism(int magnetism)
{
m_magnetism = expf(magnetism / (double)MAXIMUM_MAGNETISM) / expf(1.0);
}
bool KisToolBrush::useScalableDistance() const
{
return smoothingOptions()->useScalableDistance();
}
// used with weighted smoothing
void KisToolBrush::setUseScalableDistance(bool value)
{
smoothingOptions()->setUseScalableDistance(value);
emit useScalableDistanceChanged();
}
void KisToolBrush::resetCursorStyle()
{
KisConfig cfg;
CursorStyle cursorStyle = cfg.newCursorStyle();
// When the stabilizer is in use, we avoid using the brush outline cursor,
// because it would hide the real position of the cursor to the user,
// yielding unexpected results.
if (smoothingOptions()->smoothingType() == KisSmoothingOptions::STABILIZER &&
smoothingOptions()->useDelayDistance() &&
cursorStyle == CURSOR_STYLE_NO_CURSOR) {
useCursor(KisCursor::roundCursor());
} else {
KisToolFreehand::resetCursorStyle();
}
overrideCursorIfNotEditable();
}
// stabilizer brush settings
bool KisToolBrush::useDelayDistance() const
{
return smoothingOptions()->useDelayDistance();
}
qreal KisToolBrush::delayDistance() const
{
return smoothingOptions()->delayDistance();
}
void KisToolBrush::setUseDelayDistance(bool value)
{
smoothingOptions()->setUseDelayDistance(value);
m_sliderDelayDistance->setEnabled(value);
enableControl(m_chkFinishStabilizedCurve, !value);
emit useDelayDistanceChanged();
}
void KisToolBrush::setDelayDistance(qreal value)
{
smoothingOptions()->setDelayDistance(value);
emit delayDistanceChanged();
}
void KisToolBrush::setFinishStabilizedCurve(bool value)
{
smoothingOptions()->setFinishStabilizedCurve(value);
emit finishStabilizedCurveChanged();
}
bool KisToolBrush::finishStabilizedCurve() const
{
return smoothingOptions()->finishStabilizedCurve();
}
void KisToolBrush::setStabilizeSensors(bool value)
{
smoothingOptions()->setStabilizeSensors(value);
emit stabilizeSensorsChanged();
}
bool KisToolBrush::stabilizeSensors() const
{
return smoothingOptions()->stabilizeSensors();
}
void KisToolBrush::updateSettingsViews()
{
m_cmbSmoothingType->setCurrentIndex(smoothingOptions()->smoothingType());
m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance());
m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance());
m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance());
m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness());
m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure());
m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance());
m_cmbSmoothingType->setCurrentIndex((int)smoothingOptions()->smoothingType());
m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors());
emit smoothnessQualityChanged();
emit smoothnessFactorChanged();
emit smoothPressureChanged();
emit smoothingTypeChanged();
emit useScalableDistanceChanged();
emit useDelayDistanceChanged();
emit delayDistanceChanged();
emit finishStabilizedCurveChanged();
emit stabilizeSensorsChanged();
KisTool::updateSettingsViews();
}
QWidget * KisToolBrush::createOptionWidget()
{
QWidget *optionsWidget = KisToolFreehand::createOptionWidget();
optionsWidget->setObjectName(toolId() + "option widget");
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(optionsWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
optionsWidget->layout()->addWidget(specialSpacer);
// Line smoothing configuration
m_cmbSmoothingType = new QComboBox(optionsWidget);
m_cmbSmoothingType->addItems(QStringList()
- << i18n("No Smoothing")
- << i18n("Basic Smoothing")
- << i18n("Weighted Smoothing")
+ << i18n("None")
+ << i18n("Basic")
+ << i18n("Weighted")
<< i18n("Stabilizer"));
connect(m_cmbSmoothingType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetSmoothingType(int)));
- addOptionWidgetOption(m_cmbSmoothingType);
+ addOptionWidgetOption(m_cmbSmoothingType, new QLabel(i18n("Brush Smoothing:")));
m_sliderSmoothnessDistance = new KisDoubleSliderSpinBox(optionsWidget);
m_sliderSmoothnessDistance->setRange(3.0, MAXIMUM_SMOOTHNESS_DISTANCE, 1);
m_sliderSmoothnessDistance->setEnabled(true);
connect(m_sliderSmoothnessDistance, SIGNAL(valueChanged(qreal)), SLOT(slotSetSmoothnessDistance(qreal)));
m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance());
addOptionWidgetOption(m_sliderSmoothnessDistance, new QLabel(i18n("Distance:")));
// Finish stabilizer curve
m_chkFinishStabilizedCurve = new QCheckBox(optionsWidget);
m_chkFinishStabilizedCurve->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3,
m_chkFinishStabilizedCurve->sizeHint().height()));
connect(m_chkFinishStabilizedCurve, SIGNAL(toggled(bool)), this, SLOT(setFinishStabilizedCurve(bool)));
m_chkFinishStabilizedCurve->setChecked(smoothingOptions()->finishStabilizedCurve());
// Delay Distance for Stabilizer
QWidget* delayWidget = new QWidget(optionsWidget);
QHBoxLayout* delayLayout = new QHBoxLayout(delayWidget);
delayLayout->setContentsMargins(0,0,0,0);
delayLayout->setSpacing(1);
QLabel* delayLabel = new QLabel(i18n("Delay:"), optionsWidget);
delayLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
delayLayout->addWidget(delayLabel);
m_chkDelayDistance = new QCheckBox(optionsWidget);
m_chkDelayDistance->setLayoutDirection(Qt::RightToLeft);
delayWidget->setToolTip(i18n("Delay the brush stroke to make the line smoother"));
connect(m_chkDelayDistance, SIGNAL(toggled(bool)), this, SLOT(setUseDelayDistance(bool)));
delayLayout->addWidget(m_chkDelayDistance);
m_sliderDelayDistance = new KisDoubleSliderSpinBox(optionsWidget);
m_sliderDelayDistance->setToolTip(i18n("Radius where the brush is blocked"));
m_sliderDelayDistance->setRange(0, 500);
m_sliderDelayDistance->setSuffix(i18n(" px"));
connect(m_sliderDelayDistance, SIGNAL(valueChanged(qreal)), SLOT(setDelayDistance(qreal)));
addOptionWidgetOption(m_sliderDelayDistance, delayWidget);
addOptionWidgetOption(m_chkFinishStabilizedCurve, new QLabel(i18n("Finish line:")));
m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance());
m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance());
// if the state is not flipped, then the previous line doesn't generate any signals
setUseDelayDistance(m_chkDelayDistance->isChecked());
// Stabilize sensors
m_chkStabilizeSensors = new QCheckBox(optionsWidget);
m_chkStabilizeSensors->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3,
m_chkStabilizeSensors->sizeHint().height()));
connect(m_chkStabilizeSensors, SIGNAL(toggled(bool)), this, SLOT(setStabilizeSensors(bool)));
m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors());
addOptionWidgetOption(m_chkStabilizeSensors, new QLabel(i18n("Stabilize Sensors:")));
m_sliderTailAggressiveness = new KisDoubleSliderSpinBox(optionsWidget);
m_sliderTailAggressiveness->setRange(0.0, 1.0, 2);
m_sliderTailAggressiveness->setEnabled(true);
connect(m_sliderTailAggressiveness, SIGNAL(valueChanged(qreal)), SLOT(slotSetTailAgressiveness(qreal)));
m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness());
addOptionWidgetOption(m_sliderTailAggressiveness, new QLabel(i18n("Stroke Ending:")));
m_chkSmoothPressure = new QCheckBox(optionsWidget);
m_chkSmoothPressure->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3,
m_chkSmoothPressure->sizeHint().height()));
m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure());
connect(m_chkSmoothPressure, SIGNAL(toggled(bool)), this, SLOT(setSmoothPressure(bool)));
addOptionWidgetOption(m_chkSmoothPressure, new QLabel(QString("%1:").arg(i18n("Smooth Pressure"))));
m_chkUseScalableDistance = new QCheckBox(optionsWidget);
m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance());
m_chkUseScalableDistance->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3,
m_chkUseScalableDistance->sizeHint().height()));
m_chkUseScalableDistance->setToolTip(i18nc("@info:tooltip",
"Scalable distance takes zoom level "
"into account and makes the distance "
"be visually constant whatever zoom "
"level is chosen"));
connect(m_chkUseScalableDistance, SIGNAL(toggled(bool)), this, SLOT(setUseScalableDistance(bool)));
addOptionWidgetOption(m_chkUseScalableDistance, new QLabel(QString("%1:").arg(i18n("Scalable Distance"))));
+
+ // add a line spacer so we know that the next set of options are for different settings
+ QFrame* line = new QFrame(optionsWidget);
+ line->setObjectName(QString::fromUtf8("line"));
+ line->setFrameShape(QFrame::HLine);
+ addOptionWidgetOption(line);
+
+
+
// Drawing assistant configuration
QWidget* assistantWidget = new QWidget(optionsWidget);
- QHBoxLayout* assistantLayout = new QHBoxLayout(assistantWidget);
- assistantLayout->setContentsMargins(0,0,0,0);
- assistantLayout->setSpacing(1);
- QLabel* assistantLabel = new QLabel(i18n("Assistant:"), optionsWidget);
- assistantLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- assistantLayout->addWidget(assistantLabel);
+ QGridLayout* assistantLayout = new QGridLayout(assistantWidget);
+ assistantLayout->setContentsMargins(10,0,0,0);
+ assistantLayout->setSpacing(5);
+
+
m_chkAssistant = new QCheckBox(optionsWidget);
- m_chkAssistant->setLayoutDirection(Qt::RightToLeft);
+ m_chkAssistant->setText(i18n("Snap to Assistants"));
+
assistantWidget->setToolTip(i18n("You need to add Ruler Assistants before this tool will work."));
connect(m_chkAssistant, SIGNAL(toggled(bool)), this, SLOT(setAssistant(bool)));
- assistantLayout->addWidget(m_chkAssistant);
+ assistantLayout->addWidget(m_chkAssistant, 1, 1, 1, 1, Qt::AlignLeft);
m_sliderMagnetism = new KisSliderSpinBox(optionsWidget);
m_sliderMagnetism->setToolTip(i18n("Assistant Magnetism"));
m_sliderMagnetism->setRange(0, MAXIMUM_MAGNETISM);
- m_sliderMagnetism->setEnabled(false);
- connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, SLOT(setEnabled(bool)));
+
m_sliderMagnetism->setValue(m_magnetism * MAXIMUM_MAGNETISM);
connect(m_sliderMagnetism, SIGNAL(valueChanged(int)), SLOT(slotSetMagnetism(int)));
QAction *toggleaction = new QAction(i18n("Toggle Assistant"), this);
addAction("toggle_assistant", toggleaction);
toggleaction->setShortcut(QKeySequence(Qt::ControlModifier + Qt::ShiftModifier + Qt::Key_L));
connect(toggleaction, SIGNAL(triggered(bool)), m_chkAssistant, SLOT(toggle()));
+
addOptionWidgetOption(m_sliderMagnetism, assistantWidget);
-
+
+ QLabel* snapSingleLabel = new QLabel(i18n("Snap Single:"));
+
m_chkOnlyOneAssistant = new QCheckBox(optionsWidget);
m_chkOnlyOneAssistant->setToolTip(i18nc("@info:tooltip","Make it only snap to a single assistant, prevents snapping mess while using the infinite assistants."));
m_chkOnlyOneAssistant->setCheckState(Qt::Checked);//turn on by default.
connect(m_chkOnlyOneAssistant, SIGNAL(toggled(bool)), this, SLOT(setOnlyOneAssistantSnap(bool)));
- addOptionWidgetOption(m_chkOnlyOneAssistant, new QLabel(i18n("Snap single:")));
+ addOptionWidgetOption(m_chkOnlyOneAssistant, snapSingleLabel);
+
+
+ // set the assistant snapping options to hidden by default and toggle their visibility based based off snapping checkbox
+ m_sliderMagnetism->setVisible(false);
+ m_chkOnlyOneAssistant->setVisible(false);
+ snapSingleLabel->setVisible(false);
+
+ connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, SLOT(setVisible(bool)));
+ connect(m_chkAssistant, SIGNAL(toggled(bool)), m_chkOnlyOneAssistant, SLOT(setVisible(bool)));
+ connect(m_chkAssistant, SIGNAL(toggled(bool)), snapSingleLabel, SLOT(setVisible(bool)));
+
+
KisConfig cfg;
slotSetSmoothingType(cfg.lineSmoothingType());
return optionsWidget;
}
diff --git a/plugins/tools/basictools/kis_tool_colorpicker.cc b/plugins/tools/basictools/kis_tool_colorpicker.cc
index 439f8fb099..7e17168594 100644
--- a/plugins/tools/basictools/kis_tool_colorpicker.cc
+++ b/plugins/tools/basictools/kis_tool_colorpicker.cc
@@ -1,446 +1,386 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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 "kis_tool_colorpicker.h"
#include <string.h>
#include <boost/thread/locks.hpp>
#include <QPoint>
#include <QLayout>
#include <QCheckBox>
#include <QComboBox>
#include <QSpinBox>
#include <QListWidget>
#include <QList>
#include <QWidget>
#include <QVector>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <klocalizedstring.h>
#include <QMessageBox>
#include "kis_layer.h"
#include "kis_cursor.h"
#include "kis_image.h"
#include "kis_paint_device.h"
#include "kis_properties_configuration.h"
#include "KoPointerEvent.h"
#include "KoCanvasBase.h"
#include "kis_random_accessor_ng.h"
#include "KoColor.h"
#include "KoResourceServerProvider.h"
#include <resources/KoColorSet.h>
#include <KoChannelInfo.h>
#include <KoMixColorsOp.h>
#include "kis_wrapped_rect.h"
+#include "kis_tool_utils.h"
namespace
{
// The location of the sample all visible layers in the combobox
const int SAMPLE_MERGED = 0;
-const QString CONFIG_GROUP_NAME = "tool_color_picker";
-}
-
-KisToolColorPicker::Configuration::Configuration()
- : toForegroundColor(true)
- , updateColor(true)
- , addPalette(false)
- , normaliseValues(false)
- , sampleMerged(true)
- , radius(1)
-{
-}
-
-inline QString getConfigKey(KisTool::ToolActivation activation) {
- QString configKey;
-
- switch (activation) {
- case KisTool::TemporaryActivation:
- configKey = "ColorPickerTemporaryActivation";
- break;
- case KisTool::DefaultActivation:
- configKey = "ColorPickerDefaultActivation";
- break;
- };
-
- return configKey;
-}
-
-void KisToolColorPicker::Configuration::save(ToolActivation activation) const
-{
- KisPropertiesConfiguration props;
- props.setProperty("toForegroundColor", toForegroundColor);
- props.setProperty("updateColor", updateColor);
- props.setProperty("addPalette", addPalette);
- props.setProperty("normaliseValues", normaliseValues);
- props.setProperty("sampleMerged", sampleMerged);
- props.setProperty("radius", radius);
-
- KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME);
-
- config.writeEntry(getConfigKey(activation), props.toXML());
-}
-
-void KisToolColorPicker::Configuration::load(ToolActivation activation)
-{
- KisPropertiesConfiguration props;
-
- KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME);
- props.fromXML(config.readEntry(getConfigKey(activation)));
-
- toForegroundColor = props.getBool("toForegroundColor", true);
- updateColor = props.getBool("updateColor", true);
- addPalette = props.getBool("addPalette", false);
- normaliseValues = props.getBool("normaliseValues", false);
- sampleMerged = props.getBool("sampleMerged", activation == KisTool::TemporaryActivation ? false : true);
- radius = props.getInt("radius", 1);
}
KisToolColorPicker::KisToolColorPicker(KoCanvasBase* canvas)
- : KisTool(canvas, KisCursor::pickerCursor())
+ : KisTool(canvas, KisCursor::pickerCursor()),
+ m_config(new KisToolUtils::ColorPickerConfig)
{
setObjectName("tool_colorpicker");
m_isActivated = false;
m_optionsWidget = 0;
m_pickedColor = KoColor();
}
KisToolColorPicker::~KisToolColorPicker()
{
if (m_isActivated) {
- m_config.save(m_toolActivationSource);
+ m_config->save(m_toolActivationSource == KisTool::DefaultActivation);
}
}
void KisToolColorPicker::paint(QPainter& gc, const KoViewConverter &converter)
{
Q_UNUSED(gc);
Q_UNUSED(converter);
}
void KisToolColorPicker::activate(ToolActivation activation, const QSet<KoShape*> &shapes)
{
m_isActivated = true;
m_toolActivationSource = activation;
- m_config.load(m_toolActivationSource);
+ m_config->load(m_toolActivationSource == KisTool::DefaultActivation);
updateOptionWidget();
KisTool::activate(activation, shapes);
}
void KisToolColorPicker::deactivate()
{
- m_config.save(m_toolActivationSource);
+ m_config->save(m_toolActivationSource == KisTool::DefaultActivation);
m_isActivated = false;
KisTool::deactivate();
}
void KisToolColorPicker::pickColor(const QPointF& pos)
{
if (m_colorPickerDelayTimer.isActive()) {
return;
}
else {
m_colorPickerDelayTimer.setSingleShot(true);
m_colorPickerDelayTimer.start(100);
}
QScopedPointer<boost::lock_guard<KisImage> > imageLocker;
KisPaintDeviceSP dev;
if (m_optionsWidget->cmbSources->currentIndex() != SAMPLE_MERGED &&
currentNode() && currentNode()->projection()) {
dev = currentNode()->projection();
}
else {
imageLocker.reset(new boost::lock_guard<KisImage>(*currentImage()));
dev = currentImage()->projection();
}
- if (m_config.radius == 1) {
+ if (m_config->radius == 1) {
QPoint realPos = pos.toPoint();
if (currentImage()->wrapAroundModePermitted()) {
realPos = KisWrappedRect::ptToWrappedPt(realPos, currentImage()->bounds());
}
dev->pixel(realPos.x(), realPos.y(), &m_pickedColor);
}
else {
const KoColorSpace* cs = dev->colorSpace();
int pixelSize = cs->pixelSize();
quint8* dstColor = new quint8[pixelSize];
QVector<const quint8*> pixels;
- QVector<qint16> weights;
KisRandomConstAccessorSP accessor = dev->createRandomConstAccessorNG(0, 0);
- for (int y = -m_config.radius; y <= m_config.radius; y++) {
- for (int x = -m_config.radius; x <= m_config.radius; x++) {
- if (((x * x) + (y * y)) < m_config.radius * m_config.radius) {
+ for (int y = -m_config->radius; y <= m_config->radius; y++) {
+ for (int x = -m_config->radius; x <= m_config->radius; x++) {
+ if (((x * x) + (y * y)) < m_config->radius * m_config->radius) {
QPoint realPos(pos.x() + x, pos.y() + y);
if (currentImage()->wrapAroundModePermitted()) {
realPos = KisWrappedRect::ptToWrappedPt(realPos, currentImage()->bounds());
}
accessor->moveTo(realPos.x(), realPos.y());
pixels << accessor->oldRawData();
}
}
}
- weights.fill(255 / pixels.size(), pixels.size());
- // Because the sum of the weights must be 255,
- // we cheat a bit, and weigh the center pixel differently in order
- // to sum to 255 in total
- weights[(weights.size() / 2)] = 255 - (weights.size() -1) * (255 / weights.size());
const quint8** cpixels = const_cast<const quint8**>(pixels.constData());
- cs->mixColorsOp()->mixColors(cpixels, weights.constData(), pixels.size(), dstColor);
+ cs->mixColorsOp()->mixColors(cpixels, pixels.size(), dstColor);
m_pickedColor = KoColor(dstColor, cs);
delete[] dstColor;
}
m_pickedColor.convertTo(dev->compositionSourceColorSpace());
- if (m_config.updateColor &&
+ if (m_config->updateColor &&
m_pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8) {
KoColor publicColor = m_pickedColor;
publicColor.setOpacity(OPACITY_OPAQUE_U8);
- if (m_config.toForegroundColor) {
+ if (m_config->toForegroundColor) {
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ForegroundColor, publicColor);
}
else {
canvas()->resourceManager()->setResource(KoCanvasResourceManager::BackgroundColor, publicColor);
}
}
}
void KisToolColorPicker::beginPrimaryAction(KoPointerEvent *event)
{
bool sampleMerged = m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED;
if (!sampleMerged) {
if (!currentNode()) {
QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as no layer is active."));
event->ignore();
return;
}
if (!currentNode()->visible()) {
QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as the active layer is not visible."));
event->ignore();
return;
}
}
QPoint pos = convertToIntPixelCoord(event);
// the color picking has to start in the visible part of the layer
if (!currentImage()->bounds().contains(pos) &&
!currentImage()->wrapAroundModePermitted()) {
event->ignore();
return;
}
setMode(KisTool::PAINT_MODE);
pickColor(pos);
displayPickedColor();
}
void KisToolColorPicker::continuePrimaryAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
QPoint pos = convertToIntPixelCoord(event);
pickColor(pos);
displayPickedColor();
}
void KisToolColorPicker::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
- if (m_config.addPalette) {
+ if (m_config->addPalette) {
KoColorSetEntry ent;
ent.color = m_pickedColor;
// We don't ask for a name, too intrusive here
KoColorSet* palette = m_palettes.at(m_optionsWidget->cmbPalette->currentIndex());
palette->add(ent);
if (!palette->save()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Cannot write to palette file %1. Maybe it is read-only.", palette->filename()));
}
}
}
struct PickedChannel {
QString name;
QString valueText;
};
void KisToolColorPicker::displayPickedColor()
{
if (m_pickedColor.data() && m_optionsWidget) {
QList<KoChannelInfo *> channels = m_pickedColor.colorSpace()->channels();
m_optionsWidget->listViewChannels->clear();
QVector<PickedChannel> pickedChannels;
for (int i = 0; i < channels.count(); ++i) {
pickedChannels.append(PickedChannel());
}
for (int i = 0; i < channels.count(); ++i) {
PickedChannel pc;
pc.name = channels[i]->name();
- if (m_config.normaliseValues) {
+ if (m_config->normaliseValues) {
pc.valueText = m_pickedColor.colorSpace()->normalisedChannelValueText(m_pickedColor.data(), i);
} else {
pc.valueText = m_pickedColor.colorSpace()->channelValueText(m_pickedColor.data(), i);
}
pickedChannels[channels[i]->displayPosition()] = pc;
}
Q_FOREACH (const PickedChannel &pc, pickedChannels) {
QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels);
item->setText(0, pc.name);
item->setText(1, pc.valueText);
}
}
}
QWidget* KisToolColorPicker::createOptionWidget()
{
m_optionsWidget = new ColorPickerOptionsWidget(0);
m_optionsWidget->setObjectName(toolId() + " option widget");
m_optionsWidget->listViewChannels->setSortingEnabled(false);
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(m_optionsWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
m_optionsWidget->layout()->addWidget(specialSpacer);
updateOptionWidget();
connect(m_optionsWidget->cbUpdateCurrentColor, SIGNAL(toggled(bool)), SLOT(slotSetUpdateColor(bool)));
connect(m_optionsWidget->cbNormaliseValues, SIGNAL(toggled(bool)), SLOT(slotSetNormaliseValues(bool)));
connect(m_optionsWidget->cbPalette, SIGNAL(toggled(bool)),
SLOT(slotSetAddPalette(bool)));
connect(m_optionsWidget->radius, SIGNAL(valueChanged(int)),
SLOT(slotChangeRadius(int)));
connect(m_optionsWidget->cmbSources, SIGNAL(currentIndexChanged(int)),
SLOT(slotSetColorSource(int)));
KoResourceServer<KoColorSet>* srv = KoResourceServerProvider::instance()->paletteServer();
if (!srv) {
return m_optionsWidget;
}
QList<KoColorSet*> palettes = srv->resources();
Q_FOREACH (KoColorSet *palette, palettes) {
if (palette) {
m_optionsWidget->cmbPalette->addSqueezedItem(palette->name());
m_palettes.append(palette);
}
}
return m_optionsWidget;
}
void KisToolColorPicker::updateOptionWidget()
{
if (!m_optionsWidget) return;
- m_optionsWidget->cbNormaliseValues->setChecked(m_config.normaliseValues);
- m_optionsWidget->cbUpdateCurrentColor->setChecked(m_config.updateColor);
- m_optionsWidget->cmbSources->setCurrentIndex(SAMPLE_MERGED + !m_config.sampleMerged);
- m_optionsWidget->cbPalette->setChecked(m_config.addPalette);
- m_optionsWidget->radius->setValue(m_config.radius);
+ m_optionsWidget->cbNormaliseValues->setChecked(m_config->normaliseValues);
+ m_optionsWidget->cbUpdateCurrentColor->setChecked(m_config->updateColor);
+ m_optionsWidget->cmbSources->setCurrentIndex(SAMPLE_MERGED + !m_config->sampleMerged);
+ m_optionsWidget->cbPalette->setChecked(m_config->addPalette);
+ m_optionsWidget->radius->setValue(m_config->radius);
}
void KisToolColorPicker::setToForeground(bool newValue)
{
- m_config.toForegroundColor = newValue;
+ m_config->toForegroundColor = newValue;
emit toForegroundChanged();
}
bool KisToolColorPicker::toForeground() const
{
- return m_config.toForegroundColor;
+ return m_config->toForegroundColor;
}
void KisToolColorPicker::slotSetUpdateColor(bool state)
{
- m_config.updateColor = state;
+ m_config->updateColor = state;
}
void KisToolColorPicker::slotSetNormaliseValues(bool state)
{
- m_config.normaliseValues = state;
+ m_config->normaliseValues = state;
displayPickedColor();
}
void KisToolColorPicker::slotSetAddPalette(bool state)
{
- m_config.addPalette = state;
+ m_config->addPalette = state;
}
void KisToolColorPicker::slotChangeRadius(int value)
{
- m_config.radius = value;
+ m_config->radius = value;
}
void KisToolColorPicker::slotSetColorSource(int value)
{
- m_config.sampleMerged = value == SAMPLE_MERGED;
+ m_config->sampleMerged = value == SAMPLE_MERGED;
}
void KisToolColorPicker::slotAddPalette(KoResource* resource)
{
KoColorSet* palette = dynamic_cast<KoColorSet*>(resource);
if (palette) {
m_optionsWidget->cmbPalette->addSqueezedItem(palette->name());
m_palettes.append(palette);
}
}
diff --git a/plugins/tools/basictools/kis_tool_colorpicker.h b/plugins/tools/basictools/kis_tool_colorpicker.h
index 3c2d176e3a..e95c16376c 100644
--- a/plugins/tools/basictools/kis_tool_colorpicker.h
+++ b/plugins/tools/basictools/kis_tool_colorpicker.h
@@ -1,142 +1,147 @@
/*
* Copyright (c) 1999 Matthias Elter <elter@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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.
*/
#ifndef KIS_TOOL_COLOR_PICKER_H_
#define KIS_TOOL_COLOR_PICKER_H_
#include <QList>
#include <QTimer>
#include "KoToolFactoryBase.h"
#include "ui_wdgcolorpicker.h"
#include "kis_tool.h"
#include <flake/kis_node_shape.h>
#include <KoIcon.h>
#include <kis_icon.h>
#include <QKeySequence>
class KoResource;
class KoColorSet;
+namespace KisToolUtils {
+struct ColorPickerConfig;
+}
+
class ColorPickerOptionsWidget : public QWidget, public Ui::ColorPickerOptionsWidget
{
Q_OBJECT
public:
ColorPickerOptionsWidget(QWidget *parent) : QWidget(parent) {
setupUi(this);
}
};
class KisToolColorPicker : public KisTool
{
Q_OBJECT
Q_PROPERTY(bool toForeground READ toForeground WRITE setToForeground NOTIFY toForegroundChanged)
public:
KisToolColorPicker(KoCanvasBase* canvas);
virtual ~KisToolColorPicker();
public:
struct Configuration {
Configuration();
bool toForegroundColor;
bool updateColor;
bool addPalette;
bool normaliseValues;
bool sampleMerged;
int radius;
void save(ToolActivation activation) const;
void load(ToolActivation activation);
};
public:
virtual QWidget* createOptionWidget();
void beginPrimaryAction(KoPointerEvent *event);
void continuePrimaryAction(KoPointerEvent *event);
void endPrimaryAction(KoPointerEvent *event);
virtual void paint(QPainter& gc, const KoViewConverter &converter);
bool toForeground() const;
Q_SIGNALS:
void toForegroundChanged();
protected:
void activate(ToolActivation activation, const QSet<KoShape*> &);
void deactivate();
public Q_SLOTS:
void setToForeground(bool newValue);
void slotSetUpdateColor(bool);
void slotSetNormaliseValues(bool);
void slotSetAddPalette(bool);
void slotChangeRadius(int);
void slotAddPalette(KoResource* resource);
void slotSetColorSource(int value);
private:
void displayPickedColor();
void pickColor(const QPointF& pos);
void updateOptionWidget();
- Configuration m_config;
+ //Configuration m_config;
+ QScopedPointer<KisToolUtils::ColorPickerConfig> m_config;
ToolActivation m_toolActivationSource;
bool m_isActivated;
KoColor m_pickedColor;
// used to skip some of the tablet events and don't update the colour that often
QTimer m_colorPickerDelayTimer;
ColorPickerOptionsWidget *m_optionsWidget;
QList<KoColorSet*> m_palettes;
};
class KisToolColorPickerFactory : public KoToolFactoryBase
{
public:
KisToolColorPickerFactory()
: KoToolFactoryBase("KritaSelected/KisToolColorPicker") {
setToolTip(i18n("Color Selector Tool"));
setSection(TOOL_TYPE_FILL);
setPriority(2);
setIconName(koIconNameCStr("krita_tool_color_picker"));
setShortcut(QKeySequence(Qt::Key_P));
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
}
virtual ~KisToolColorPickerFactory() {}
virtual KoToolBase * createTool(KoCanvasBase *canvas) {
return new KisToolColorPicker(canvas);
}
};
#endif // KIS_TOOL_COLOR_PICKER_H_
diff --git a/plugins/tools/basictools/kis_tool_fill.cc b/plugins/tools/basictools/kis_tool_fill.cc
index ea087d60f4..f76f8afece 100644
--- a/plugins/tools/basictools/kis_tool_fill.cc
+++ b/plugins/tools/basictools/kis_tool_fill.cc
@@ -1,302 +1,302 @@
/*
* kis_tool_fill.cc - part of Krayon
*
* Copyright (c) 2000 John Califf <jcaliff@compuzone.net>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public 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_fill.h"
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <QLabel>
#include <QLayout>
#include <QCheckBox>
#include <QComboBox>
#include <QVector>
#include <QRect>
#include <QColor>
#include <ksharedconfig.h>
#include <KoCanvasBase.h>
#include <KoPointerEvent.h>
#include <kis_layer.h>
#include <kis_painter.h>
#include <resources/KoPattern.h>
#include <kis_fill_painter.h>
#include <kis_selection.h>
#include <kis_system_locker.h>
#include <KisViewManager.h>
#include <canvas/kis_canvas2.h>
#include <widgets/kis_cmb_composite.h>
#include <widgets/kis_slider_spin_box.h>
#include <kis_cursor.h>
#include <recorder/kis_recorded_fill_paint_action.h>
#include <recorder/kis_node_query_path.h>
#include <recorder/kis_action_recorder.h>
#include "kis_resources_snapshot.h"
#include <processing/fill_processing_visitor.h>
#include <kis_processing_applicator.h>
KisToolFill::KisToolFill(KoCanvasBase * canvas)
: KisToolPaint(canvas, KisCursor::load("tool_fill_cursor.png", 6, 6))
{
setObjectName("tool_fill");
m_feather = 0;
m_sizemod = 0;
m_threshold = 80;
m_usePattern = false;
m_unmerged = false;
m_fillOnlySelection = false;
}
KisToolFill::~KisToolFill()
{
}
void KisToolFill::resetCursorStyle()
{
KisToolPaint::resetCursorStyle();
overrideCursorIfNotEditable();
}
void KisToolFill::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
KisToolPaint::activate(toolActivation, shapes);
m_configGroup = KSharedConfig::openConfig()->group(toolId());
}
void KisToolFill::beginPrimaryAction(KoPointerEvent *event)
{
if (!nodeEditable()) {
event->ignore();
return;
}
setMode(KisTool::PAINT_MODE);
m_startPos = convertToIntPixelCoord(event);
}
void KisToolFill::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
setMode(KisTool::HOVER_MODE);
if (!currentNode() ||
(!image()->wrapAroundModePermitted() &&
!image()->bounds().contains(m_startPos))) {
return;
}
// TODO: remove this block after recording refactoring
if (image()) {
KisNodeSP projectionNode;
if(m_unmerged) {
projectionNode = currentNode();
} else {
projectionNode = image()->root();
}
KisRecordedFillPaintAction paintAction(KisNodeQueryPath::absolutePath(currentNode()), m_startPos, KisNodeQueryPath::absolutePath(projectionNode));
setupPaintAction(&paintAction);
paintAction.setPattern(currentPattern());
if(m_usePattern) {
paintAction.setFillStyle(KisPainter::FillStylePattern);
}
image()->actionRecorder()->addAction(paintAction);
}
bool useFastMode = m_useFastMode->isChecked();
KisProcessingApplicator applicator(currentImage(), currentNode(),
KisProcessingApplicator::SUPPORTS_WRAPAROUND_MODE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Flood Fill"));
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(image(), currentNode(), 0, this->canvas()->resourceManager());
+ new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
KisProcessingVisitorSP visitor =
new FillProcessingVisitor(m_startPos,
resources->activeSelection(),
resources,
useFastMode,
m_usePattern,
m_fillOnlySelection,
m_feather,
m_sizemod,
m_threshold,
m_unmerged,
false);
applicator.applyVisitor(visitor,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.end();
}
QWidget* KisToolFill::createOptionWidget()
{
QWidget *widget = KisToolPaint::createOptionWidget();
widget->setObjectName(toolId() + " option widget");
QLabel *lbl_fastMode = new QLabel(i18n("Fast mode: "), widget);
m_useFastMode = new QCheckBox(QString(), widget);
m_useFastMode->setToolTip(
i18n("Fills area faster, but does not take composition "
"mode into account. Selections and other extended "
"features will also be disabled."));
QLabel *lbl_threshold = new QLabel(i18n("Threshold: "), widget);
m_slThreshold = new KisSliderSpinBox(widget);
m_slThreshold->setObjectName("int_widget");
m_slThreshold->setRange(1, 100);
m_slThreshold->setPageStep(3);
QLabel *lbl_sizemod = new QLabel(i18n("Grow selection: "), widget);
m_sizemodWidget = new KisSliderSpinBox(widget);
m_sizemodWidget->setObjectName("sizemod");
m_sizemodWidget->setRange(-40, 40);
m_sizemodWidget->setSingleStep(1);
m_sizemodWidget->setSuffix(i18n(" px"));
QLabel *lbl_feather = new QLabel(i18n("Feathering radius: "), widget);
m_featherWidget = new KisSliderSpinBox(widget);
m_featherWidget->setObjectName("feather");
m_featherWidget->setRange(0, 40);
m_featherWidget->setSingleStep(1);
m_featherWidget->setSuffix(i18n(" px"));
QLabel *lbl_usePattern = new QLabel(i18n("Use pattern:"), widget);
m_checkUsePattern = new QCheckBox(QString(), widget);
m_checkUsePattern->setToolTip(i18n("When checked do not use the foreground color, but the gradient selected to fill with"));
QLabel *lbl_sampleMerged = new QLabel(i18n("Limit to current layer:"), widget);
m_checkSampleMerged = new QCheckBox(QString(), widget);
QLabel *lbl_fillSelection = new QLabel(i18n("Fill entire selection:"), widget);
m_checkFillSelection = new QCheckBox(QString(), widget);
m_checkFillSelection->setToolTip(i18n("When checked do not look at the current layer colors, but just fill all of the selected area"));
connect (m_useFastMode , SIGNAL(toggled(bool)) , this, SLOT(slotSetUseFastMode(bool)));
connect (m_slThreshold , SIGNAL(valueChanged(int)), this, SLOT(slotSetThreshold(int)));
connect (m_sizemodWidget , SIGNAL(valueChanged(int)), this, SLOT(slotSetSizemod(int)));
connect (m_featherWidget , SIGNAL(valueChanged(int)), this, SLOT(slotSetFeather(int)));
connect (m_checkUsePattern , SIGNAL(toggled(bool)) , this, SLOT(slotSetUsePattern(bool)));
connect (m_checkSampleMerged , SIGNAL(toggled(bool)) , this, SLOT(slotSetSampleMerged(bool)));
connect (m_checkFillSelection, SIGNAL(toggled(bool)) , this, SLOT(slotSetFillSelection(bool)));
addOptionWidgetOption(m_useFastMode, lbl_fastMode);
addOptionWidgetOption(m_slThreshold, lbl_threshold);
addOptionWidgetOption(m_sizemodWidget , lbl_sizemod);
addOptionWidgetOption(m_featherWidget , lbl_feather);
addOptionWidgetOption(m_checkFillSelection, lbl_fillSelection);
addOptionWidgetOption(m_checkSampleMerged, lbl_sampleMerged);
addOptionWidgetOption(m_checkUsePattern, lbl_usePattern);
updateGUI();
widget->setFixedHeight(widget->sizeHint().height());
// load configuration options
m_useFastMode->setChecked(m_configGroup.readEntry("useFastMode", false));
m_slThreshold->setValue(m_configGroup.readEntry("thresholdAmount", 80));
m_sizemodWidget->setValue(m_configGroup.readEntry("growSelection", 0));
m_featherWidget->setValue(m_configGroup.readEntry("featherAmount", 0));
m_checkUsePattern->setChecked(m_configGroup.readEntry("usePattern", false));
m_checkSampleMerged->setChecked(m_configGroup.readEntry("sampleMerged", false));
m_checkFillSelection->setChecked(m_configGroup.readEntry("fillSelection", false));
return widget;
}
void KisToolFill::updateGUI()
{
bool useAdvancedMode = !m_useFastMode->isChecked();
bool selectionOnly = m_checkFillSelection->isChecked();
m_useFastMode->setEnabled(!selectionOnly);
m_slThreshold->setEnabled(!selectionOnly);
m_sizemodWidget->setEnabled(!selectionOnly && useAdvancedMode);
m_featherWidget->setEnabled(!selectionOnly && useAdvancedMode);
m_checkSampleMerged->setEnabled(!selectionOnly && useAdvancedMode);
m_checkUsePattern->setEnabled(useAdvancedMode);
}
void KisToolFill::slotSetUseFastMode(bool value)
{
updateGUI();
m_configGroup.writeEntry("useFastMode", value);
}
void KisToolFill::slotSetThreshold(int threshold)
{
m_threshold = threshold;
m_configGroup.writeEntry("thresholdAmount", threshold);
}
void KisToolFill::slotSetUsePattern(bool state)
{
m_usePattern = state;
m_configGroup.writeEntry("usePattern", state);
}
void KisToolFill::slotSetSampleMerged(bool state)
{
m_unmerged = state;
m_configGroup.writeEntry("sampleMerged", state);
}
void KisToolFill::slotSetFillSelection(bool state)
{
m_fillOnlySelection = state;
m_configGroup.writeEntry("fillSelection", state);
updateGUI();
}
void KisToolFill::slotSetSizemod(int sizemod)
{
m_sizemod = sizemod;
m_configGroup.writeEntry("growSelection", sizemod);
}
void KisToolFill::slotSetFeather(int feather)
{
m_feather = feather;
m_configGroup.writeEntry("featherAmount", feather);
}
diff --git a/plugins/tools/basictools/kis_tool_gradient.cc b/plugins/tools/basictools/kis_tool_gradient.cc
index 91faa63ee4..b6e4062359 100644
--- a/plugins/tools/basictools/kis_tool_gradient.cc
+++ b/plugins/tools/basictools/kis_tool_gradient.cc
@@ -1,304 +1,304 @@
/*
* kis_tool_gradient.cc - part of Krita
*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2003 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004-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 "kis_tool_gradient.h"
#include <cfloat>
#include <QApplication>
#include <QPainter>
#include <QLabel>
#include <QLayout>
#include <QCheckBox>
#include <kis_transaction.h>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <kcombobox.h>
#include <KoPointerEvent.h>
#include <KoCanvasBase.h>
#include <KoViewConverter.h>
#include <KoUpdater.h>
#include <KoProgressUpdater.h>
#include <kis_gradient_painter.h>
#include <kis_painter.h>
#include <kis_canvas_resource_provider.h>
#include <kis_layer.h>
#include <kis_selection.h>
#include <kis_paint_layer.h>
#include <kis_system_locker.h>
#include <canvas/kis_canvas2.h>
#include <KisViewManager.h>
#include <widgets/kis_cmb_composite.h>
#include <widgets/kis_double_widget.h>
#include <widgets/kis_slider_spin_box.h>
#include <kis_cursor.h>
#include <kis_config.h>
#include "kis_resources_snapshot.h"
KisToolGradient::KisToolGradient(KoCanvasBase * canvas)
: KisToolPaint(canvas, KisCursor::load("tool_gradient_cursor.png", 6, 6))
{
setObjectName("tool_gradient");
m_startPos = QPointF(0, 0);
m_endPos = QPointF(0, 0);
m_reverse = false;
m_shape = KisGradientPainter::GradientShapeLinear;
m_repeat = KisGradientPainter::GradientRepeatNone;
m_antiAliasThreshold = 0.2;
}
KisToolGradient::~KisToolGradient()
{
}
void KisToolGradient::resetCursorStyle()
{
KisToolPaint::resetCursorStyle();
overrideCursorIfNotEditable();
}
void KisToolGradient::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
KisToolPaint::activate(toolActivation, shapes);
m_configGroup = KSharedConfig::openConfig()->group(toolId());
}
void KisToolGradient::paint(QPainter &painter, const KoViewConverter &converter)
{
if (mode() == KisTool::PAINT_MODE && m_startPos != m_endPos) {
qreal sx, sy;
converter.zoom(&sx, &sy);
painter.scale(sx / currentImage()->xRes(), sy / currentImage()->yRes());
paintLine(painter);
}
}
void KisToolGradient::beginPrimaryAction(KoPointerEvent *event)
{
if (!nodeEditable()) {
event->ignore();
return;
}
setMode(KisTool::PAINT_MODE);
m_startPos = convertToPixelCoordAndSnap(event, QPointF(), false);
m_endPos = m_startPos;
}
void KisToolGradient::continuePrimaryAction(KoPointerEvent *event)
{
/**
* TODO: The gradient tool is still not in strokes, so the end of
* its action can call processEvent(), which would result in
* nested event hadler calls. Please uncomment this line
* when the tool is ported to strokes.
*/
//CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
QPointF pos = convertToPixelCoordAndSnap(event, QPointF(), false);
QRectF bound(m_startPos, m_endPos);
canvas()->updateCanvas(convertToPt(bound.normalized()));
if (event->modifiers() == Qt::ShiftModifier) {
m_endPos = straightLine(pos);
} else {
m_endPos = pos;
}
bound.setTopLeft(m_startPos);
bound.setBottomRight(m_endPos);
canvas()->updateCanvas(convertToPt(bound.normalized()));
}
void KisToolGradient::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
setMode(KisTool::HOVER_MODE);
if (!currentNode() || currentNode()->systemLocked())
return;
if (m_startPos == m_endPos) {
return;
}
KisSystemLocker locker(currentNode());
KisPaintDeviceSP device;
KisImageSP image = this->image();
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(image, currentNode(), 0, this->canvas()->resourceManager());
+ new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager());
if (image && (device = resources->currentNode()->paintDevice())) {
QApplication::setOverrideCursor(Qt::BusyCursor);
KUndo2MagicString actionName = kundo2_i18n("Gradient");
KisUndoAdapter *undoAdapter = image->undoAdapter();
undoAdapter->beginMacro(actionName);
KisGradientPainter painter(device, resources->activeSelection());
resources->setupPainter(&painter);
painter.beginTransaction();
KisCanvas2 * canvas = dynamic_cast<KisCanvas2 *>(this->canvas());
KoProgressUpdater * updater = canvas->viewManager()->createProgressUpdater(KoProgressUpdater::Unthreaded);
updater->start(100, i18nc("@info:progress", "Gradient..."));
painter.setProgress(updater->startSubtask());
painter.setGradientShape(m_shape);
painter.paintGradient(m_startPos, m_endPos, m_repeat, m_antiAliasThreshold, m_reverse, 0, 0, image->width(), image->height());
painter.endTransaction(undoAdapter);
undoAdapter->endMacro();
QApplication::restoreOverrideCursor();
currentNode()->setDirty();
notifyModified();
delete updater;
}
canvas()->updateCanvas(convertToPt(currentImage()->bounds()));
}
QPointF KisToolGradient::straightLine(QPointF point)
{
QPointF comparison = point - m_startPos;
QPointF result;
if (fabs(comparison.x()) > fabs(comparison.y())) {
result.setX(point.x());
result.setY(m_startPos.y());
} else {
result.setX(m_startPos.x());
result.setY(point.y());
}
return result;
}
void KisToolGradient::paintLine(QPainter& gc)
{
if (canvas()) {
QPen old = gc.pen();
QPen pen(Qt::SolidLine);
gc.setPen(pen);
gc.drawLine(m_startPos, m_endPos);
gc.setPen(old);
}
}
QWidget* KisToolGradient::createOptionWidget()
{
QWidget *widget = KisToolPaint::createOptionWidget();
Q_CHECK_PTR(widget);
widget->setObjectName(toolId() + " option widget");
// Make sure to create the connections last after everything is set up. The initialized values
// won't be loaded from the configuration file if you add the widget before the connection
m_lbShape = new QLabel(i18n("Shape:"), widget);
m_cmbShape = new KComboBox(widget);
m_cmbShape->setObjectName("shape_combo");
m_cmbShape->addItem(i18nc("the gradient will be drawn linearly", "Linear"));
m_cmbShape->addItem(i18nc("the gradient will be drawn bilinearly", "Bi-Linear"));
m_cmbShape->addItem(i18nc("the gradient will be drawn radially", "Radial"));
m_cmbShape->addItem(i18nc("the gradient will be drawn in a square around a centre", "Square"));
m_cmbShape->addItem(i18nc("the gradient will be drawn as an assymmetric cone", "Conical"));
m_cmbShape->addItem(i18nc("the gradient will be drawn as a symmetric cone", "Conical Symmetric"));
m_cmbShape->addItem(i18nc("the gradient will be drawn in a selection outline", "Shaped"));
addOptionWidgetOption(m_cmbShape, m_lbShape);
connect(m_cmbShape, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetShape(int)));
m_lbRepeat = new QLabel(i18n("Repeat:"), widget);
m_cmbRepeat = new KComboBox(widget);
m_cmbRepeat->setObjectName("repeat_combo");
m_cmbRepeat->addItem(i18nc("The gradient will not repeat", "None"));
m_cmbRepeat->addItem(i18nc("The gradient will repeat forwards", "Forwards"));
m_cmbRepeat->addItem(i18nc("The gradient will repeat alternatingly", "Alternating"));
addOptionWidgetOption(m_cmbRepeat, m_lbRepeat);
connect(m_cmbRepeat, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetRepeat(int)));
m_lbAntiAliasThreshold = new QLabel(i18n("Anti-alias threshold:"), widget);
m_slAntiAliasThreshold = new KisDoubleSliderSpinBox(widget);
m_slAntiAliasThreshold->setObjectName("threshold_slider");
m_slAntiAliasThreshold->setRange(0, 1, 3);
addOptionWidgetOption(m_slAntiAliasThreshold, m_lbAntiAliasThreshold);
connect(m_slAntiAliasThreshold, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAntiAliasThreshold(qreal)));
m_ckReverse = new QCheckBox(i18nc("the gradient will be drawn with the color order reversed", "Reverse"), widget);
m_ckReverse->setObjectName("reverse_check");
connect(m_ckReverse, SIGNAL(toggled(bool)), this, SLOT(slotSetReverse(bool)));
addOptionWidgetOption(m_ckReverse);
widget->setFixedHeight(widget->sizeHint().height());
// load configuration settings into widget (updating UI will update internal variables from signals/slots)
m_ckReverse->setChecked((bool)m_configGroup.readEntry("reverse", false));
m_cmbShape->setCurrentIndex((int)m_configGroup.readEntry("shape", 0));
m_cmbRepeat->setCurrentIndex((int)m_configGroup.readEntry("repeat", 0));
m_slAntiAliasThreshold->setValue((qreal)m_configGroup.readEntry("antialiasThreshold", 0.0));
return widget;
}
void KisToolGradient::slotSetShape(int shape)
{
m_shape = static_cast<KisGradientPainter::enumGradientShape>(shape);
m_configGroup.writeEntry("shape", shape);
}
void KisToolGradient::slotSetRepeat(int repeat)
{
m_repeat = static_cast<KisGradientPainter::enumGradientRepeat>(repeat);
m_configGroup.writeEntry("repeat", repeat);
}
void KisToolGradient::slotSetReverse(bool state)
{
m_reverse = state;
m_configGroup.writeEntry("reverse", state);
}
void KisToolGradient::slotSetAntiAliasThreshold(qreal value)
{
m_antiAliasThreshold = value;
m_configGroup.writeEntry("antialiasThreshold", value);
}
diff --git a/plugins/tools/basictools/kis_tool_line.cc b/plugins/tools/basictools/kis_tool_line.cc
index dbc09532ad..34d4dc53dc 100644
--- a/plugins/tools/basictools/kis_tool_line.cc
+++ b/plugins/tools/basictools/kis_tool_line.cc
@@ -1,375 +1,374 @@
/*
* kis_tool_line.cc - part of Krayon
*
* Copyright (c) 2000 John Califf <jwcaliff@compuzone.net>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2003 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2009 Lukáš Tvrdý <lukast.dev@gmail.com>
* 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_tool_line.h"
#include <QPushButton>
#include <ksharedconfig.h>
#include <KoCanvasBase.h>
#include <KoPointerEvent.h>
#include <KoPathShape.h>
#include <KoShapeController.h>
#include <KoShapeStroke.h>
#include <kis_debug.h>
#include <kis_cursor.h>
#include <brushengine/kis_paintop_registry.h>
#include "kis_figure_painting_tool_helper.h"
#include "kis_canvas2.h"
#include <recorder/kis_action_recorder.h>
#include <recorder/kis_recorded_path_paint_action.h>
#include <recorder/kis_node_query_path.h>
#include "kis_painting_information_builder.h"
#include "kis_tool_line_helper.h"
#define ENABLE_RECORDING
const KisCoordinatesConverter* getCoordinatesConverter(KoCanvasBase * canvas)
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas);
return kritaCanvas->coordinatesConverter();
}
KisToolLine::KisToolLine(KoCanvasBase * canvas)
: KisToolPaint(canvas, KisCursor::load("tool_line_cursor.png", 6, 6)),
m_showGuideline(true),
m_strokeIsRunning(false),
m_infoBuilder(new KisConverterPaintingInformationBuilder(getCoordinatesConverter(canvas))),
m_helper(new KisToolLineHelper(m_infoBuilder.data(), kundo2_i18n("Draw Line"))),
m_strokeUpdateCompressor(500, KisSignalCompressor::POSTPONE),
m_longStrokeUpdateCompressor(1000, KisSignalCompressor::FIRST_INACTIVE)
{
setObjectName("tool_line");
setSupportOutline(true);
connect(&m_strokeUpdateCompressor, SIGNAL(timeout()), SLOT(updateStroke()));
connect(&m_longStrokeUpdateCompressor, SIGNAL(timeout()), SLOT(updateStroke()));
}
KisToolLine::~KisToolLine()
{
}
int KisToolLine::flags() const
{
return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP|KisTool::FLAG_USES_CUSTOM_PRESET;
}
void KisToolLine::resetCursorStyle()
{
KisToolPaint::resetCursorStyle();
overrideCursorIfNotEditable();
}
void KisToolLine::activate(ToolActivation activation, const QSet<KoShape*> &shapes)
{
KisToolPaint::activate(activation, shapes);
configGroup = KSharedConfig::openConfig()->group(toolId());
}
void KisToolLine::deactivate()
{
KisToolPaint::deactivate();
cancelStroke();
}
QWidget* KisToolLine::createOptionWidget()
{
QWidget* widget = KisToolPaint::createOptionWidget();
m_chkUseSensors = new QCheckBox(i18n("Use sensors"));
addOptionWidgetOption(m_chkUseSensors);
m_chkShowPreview = new QCheckBox(i18n("Show Preview"));
addOptionWidgetOption(m_chkShowPreview);
m_chkShowGuideline = new QCheckBox(i18n("Show Guideline"));
addOptionWidgetOption(m_chkShowGuideline);
// hook up connections for value changing
connect(m_chkUseSensors, SIGNAL(clicked(bool)), this, SLOT(setUseSensors(bool)) );
connect(m_chkShowPreview, SIGNAL(clicked(bool)), this, SLOT(setShowPreview(bool)) );
connect(m_chkShowGuideline, SIGNAL(clicked(bool)), this, SLOT(setShowGuideline(bool)) );
// read values in from configuration
m_chkUseSensors->setChecked(configGroup.readEntry("useSensors", true));
m_chkShowPreview->setChecked(configGroup.readEntry("showPreview", true));
m_chkShowGuideline->setChecked(configGroup.readEntry("showGuideline", true));
return widget;
}
void KisToolLine::setUseSensors(bool value)
{
configGroup.writeEntry("useSensors", value);
}
void KisToolLine::setShowGuideline(bool value)
{
m_showGuideline = value;
configGroup.writeEntry("showGuideline", value);
}
void KisToolLine::setShowPreview(bool value)
{
configGroup.writeEntry("showPreview", value);
}
void KisToolLine::requestStrokeCancellation()
{
cancelStroke();
}
void KisToolLine::requestStrokeEnd()
{
// Terminate any in-progress strokes
if (nodePaintAbility() == PAINT && m_helper->isRunning()) {
endStroke();
}
}
void KisToolLine::updatePreviewTimer(bool showGuideline)
{
// If the user disables the guideline, we will want to try to draw some
// preview lines even if they're slow, so set the timer to FIRST_ACTIVE.
if (showGuideline) {
m_strokeUpdateCompressor.setMode(KisSignalCompressor::POSTPONE);
} else {
m_strokeUpdateCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
}
}
void KisToolLine::paint(QPainter& gc, const KoViewConverter &converter)
{
Q_UNUSED(converter);
if(mode() == KisTool::PAINT_MODE) {
paintLine(gc,QRect());
}
KisToolPaint::paint(gc,converter);
}
void KisToolLine::beginPrimaryAction(KoPointerEvent *event)
{
NodePaintAbility nodeAbility = nodePaintAbility();
if (nodeAbility == NONE || !nodeEditable()) {
event->ignore();
return;
}
setMode(KisTool::PAINT_MODE);
// Always show guideline on vector layers
m_showGuideline = m_chkShowGuideline->isChecked() || nodeAbility != PAINT;
updatePreviewTimer(m_showGuideline);
m_helper->setEnabled(nodeAbility == PAINT);
m_helper->setUseSensors(m_chkUseSensors->isChecked());
m_helper->start(event, canvas()->resourceManager());
m_startPoint = convertToPixelCoordAndSnap(event);
m_endPoint = m_startPoint;
m_lastUpdatedPoint = m_startPoint;
m_strokeIsRunning = true;
}
void KisToolLine::updateStroke()
{
if (!m_strokeIsRunning) return;
m_helper->repaintLine(canvas()->resourceManager(),
image(),
currentNode(),
- image().data(),
- image()->postExecutionUndoAdapter());
+ image().data());
}
void KisToolLine::continuePrimaryAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
if (!m_strokeIsRunning) return;
// First ensure the old guideline is deleted
updateGuideline();
QPointF pos = convertToPixelCoordAndSnap(event);
if (event->modifiers() == Qt::AltModifier) {
QPointF trans = pos - m_endPoint;
m_helper->translatePoints(trans);
m_startPoint += trans;
m_endPoint += trans;
} else if (event->modifiers() == Qt::ShiftModifier) {
pos = straightLine(pos);
m_helper->addPoint(event, pos);
} else {
m_helper->addPoint(event, pos);
}
m_endPoint = pos;
// Draw preview if requested
if (m_chkShowPreview->isChecked()) {
// If the cursor has moved a significant amount, immediately clear the
// current preview and redraw. Otherwise, do slow redraws periodically.
auto updateDistance = (pixelToView(m_lastUpdatedPoint) - pixelToView(pos)).manhattanLength();
if (updateDistance > 10) {
m_helper->clearPaint();
m_longStrokeUpdateCompressor.stop();
m_strokeUpdateCompressor.start();
m_lastUpdatedPoint = pos;
} else if (updateDistance > 1) {
m_longStrokeUpdateCompressor.start();
}
}
updateGuideline();
KisToolPaint::requestUpdateOutline(event->point, event);
}
void KisToolLine::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
setMode(KisTool::HOVER_MODE);
updateGuideline();
endStroke();
}
void KisToolLine::endStroke()
{
NodePaintAbility nodeAbility = nodePaintAbility();
if (!m_strokeIsRunning || m_startPoint == m_endPoint || nodeAbility == NONE) {
return;
}
if (nodeAbility == PAINT) {
updateStroke();
m_helper->end();
}
else {
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
QTransform resolutionMatrix;
resolutionMatrix.scale(1 / currentImage()->xRes(), 1 / currentImage()->yRes());
path->moveTo(resolutionMatrix.map(m_startPoint));
path->lineTo(resolutionMatrix.map(m_endPoint));
path->normalize();
KoShapeStroke* border = new KoShapeStroke(1.0, currentFgColor().toQColor());
path->setStroke(border);
KUndo2Command * cmd = canvas()->shapeController()->addShape(path);
canvas()->addCommand(cmd);
}
m_strokeIsRunning = false;
m_endPoint = m_startPoint;
}
void KisToolLine::cancelStroke()
{
if (!m_strokeIsRunning) return;
if (m_startPoint == m_endPoint) return;
/**
* The actual stroke is run by the timer so it is a legal
* situation when m_strokeIsRunning is true, but the actual redraw
* stroke is not running.
*/
if (m_helper->isRunning()) {
m_helper->cancel();
}
m_strokeIsRunning = false;
m_endPoint = m_startPoint;
}
QPointF KisToolLine::straightLine(QPointF point)
{
const QPointF lineVector = point - m_startPoint;
qreal lineAngle = std::atan2(lineVector.y(), lineVector.x());
if (lineAngle < 0) {
lineAngle += 2 * M_PI;
}
const qreal ANGLE_BETWEEN_CONSTRAINED_LINES = (2 * M_PI) / 24;
const quint32 constrainedLineIndex = static_cast<quint32>((lineAngle / ANGLE_BETWEEN_CONSTRAINED_LINES) + 0.5);
const qreal constrainedLineAngle = constrainedLineIndex * ANGLE_BETWEEN_CONSTRAINED_LINES;
const qreal lineLength = std::sqrt((lineVector.x() * lineVector.x()) + (lineVector.y() * lineVector.y()));
const QPointF constrainedLineVector(lineLength * std::cos(constrainedLineAngle), lineLength * std::sin(constrainedLineAngle));
const QPointF result = m_startPoint + constrainedLineVector;
return result;
}
void KisToolLine::updateGuideline()
{
if (canvas()) {
QRectF bound(m_startPoint, m_endPoint);
canvas()->updateCanvas(convertToPt(bound.normalized().adjusted(-3, -3, 3, 3)));
}
}
void KisToolLine::paintLine(QPainter& gc, const QRect&)
{
QPointF viewStartPos = pixelToView(m_startPoint);
QPointF viewStartEnd = pixelToView(m_endPoint);
if (m_showGuideline && canvas()) {
QPainterPath path;
path.moveTo(viewStartPos);
path.lineTo(viewStartEnd);
paintToolOutline(&gc, path);
}
}
QString KisToolLine::quickHelp() const
{
return i18n("Alt+Drag will move the origin of the currently displayed line around, Shift+Drag will force you to draw straight lines");
}
diff --git a/plugins/tools/basictools/kis_tool_line_helper.cpp b/plugins/tools/basictools/kis_tool_line_helper.cpp
index be1d75cf1f..c3541f7874 100644
--- a/plugins/tools/basictools/kis_tool_line_helper.cpp
+++ b/plugins/tools/basictools/kis_tool_line_helper.cpp
@@ -1,172 +1,174 @@
/*
* 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_tool_line_helper.h"
#include "kis_painting_information_builder.h"
#include "kis_image.h"
struct KisToolLineHelper::Private
{
Private(KisPaintingInformationBuilder *_infoBuilder)
: infoBuilder(_infoBuilder),
useSensors(true),
enabled(true)
{
}
QVector<KisPaintInformation> linePoints;
KisPaintingInformationBuilder *infoBuilder;
bool useSensors;
bool enabled;
};
KisToolLineHelper::KisToolLineHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText,
KisRecordingAdapter *recordingAdapter)
- : KisToolFreehandHelper(infoBuilder, transactionText, recordingAdapter),
+ : KisToolFreehandHelper(infoBuilder,
+ transactionText,
+ recordingAdapter,
+ new KisSmoothingOptions(false)),
m_d(new Private(infoBuilder))
{
}
KisToolLineHelper::~KisToolLineHelper()
{
delete m_d;
}
void KisToolLineHelper::setEnabled(bool value)
{
m_d->enabled = value;
}
void KisToolLineHelper::setUseSensors(bool value)
{
m_d->useSensors = value;
}
void KisToolLineHelper::repaintLine(KoCanvasResourceManager *resourceManager,
KisImageWSP image, KisNodeSP node,
- KisStrokesFacade *strokesFacade,
- KisPostExecutionUndoAdapter *undoAdapter)
+ KisStrokesFacade *strokesFacade)
{
if (!m_d->enabled) return;
cancelPaint();
if (m_d->linePoints.isEmpty()) return;
QVector<KisPaintInformation>::const_iterator it = m_d->linePoints.constBegin();
QVector<KisPaintInformation>::const_iterator end = m_d->linePoints.constEnd();
- initPaintImpl(*it, resourceManager, image, node, strokesFacade, undoAdapter);
+ initPaintImpl(*it, resourceManager, image, node, strokesFacade);
++it;
while (it != end) {
paintLine(*(it - 1), *it);
++it;
}
}
void KisToolLineHelper::start(KoPointerEvent *event, KoCanvasResourceManager *resourceManager)
{
if (!m_d->enabled) return;
KisPaintInformation pi =
m_d->infoBuilder->startStroke(event, elapsedStrokeTime(), resourceManager);
if (!m_d->useSensors) {
pi = KisPaintInformation(pi.pos());
}
m_d->linePoints.append(pi);
}
void KisToolLineHelper::addPoint(KoPointerEvent *event, const QPointF &overridePos)
{
if (!m_d->enabled) return;
KisPaintInformation pi =
m_d->infoBuilder->continueStroke(event, elapsedStrokeTime());
if (!m_d->useSensors) {
pi = KisPaintInformation(pi.pos());
}
if (!overridePos.isNull()) {
pi.setPos(overridePos);
}
if (m_d->linePoints.size() > 1) {
const QPointF startPos = m_d->linePoints.first().pos();
const QPointF endPos = pi.pos();
const qreal maxDistance = kisDistance(startPos, endPos);
const QPointF unit = (endPos - startPos) / maxDistance;
QVector<KisPaintInformation>::iterator it = m_d->linePoints.begin();
++it;
while (it != m_d->linePoints.end()) {
qreal dist = kisDistance(startPos, it->pos());
if (dist < maxDistance) {
QPointF pos = startPos + unit * dist;
it->setPos(pos);
++it;
} else {
it = m_d->linePoints.erase(it);
}
}
}
m_d->linePoints.append(pi);
}
void KisToolLineHelper::translatePoints(const QPointF &offset)
{
if (!m_d->enabled) return;
QVector<KisPaintInformation>::iterator it = m_d->linePoints.begin();
while (it != m_d->linePoints.end()) {
it->setPos(it->pos() + offset);
++it;
}
}
void KisToolLineHelper::end()
{
if (!m_d->enabled) return;
KIS_ASSERT_RECOVER_RETURN(isRunning());
endPaint();
m_d->linePoints.clear();
}
void KisToolLineHelper::cancel()
{
if (!m_d->enabled) return;
KIS_ASSERT_RECOVER_RETURN(isRunning());
cancelPaint();
m_d->linePoints.clear();
}
void KisToolLineHelper::clearPaint()
{
if (!m_d->enabled) return;
cancelPaint();
}
diff --git a/plugins/tools/basictools/kis_tool_line_helper.h b/plugins/tools/basictools/kis_tool_line_helper.h
index bb17a67f73..581bffbf02 100644
--- a/plugins/tools/basictools/kis_tool_line_helper.h
+++ b/plugins/tools/basictools/kis_tool_line_helper.h
@@ -1,57 +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_TOOL_LINE_HELPER_H
#define __KIS_TOOL_LINE_HELPER_H
#include "kis_tool_freehand_helper.h"
class KisToolLineHelper : private KisToolFreehandHelper
{
public:
KisToolLineHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText,
KisRecordingAdapter *recordingAdapter = 0);
~KisToolLineHelper();
void setEnabled(bool value);
void setUseSensors(bool value);
void repaintLine(KoCanvasResourceManager *resourceManager,
KisImageWSP image,
KisNodeSP node,
- KisStrokesFacade *strokesFacade,
- KisPostExecutionUndoAdapter *undoAdapter);
+ KisStrokesFacade *strokesFacade);
void start(KoPointerEvent *event, KoCanvasResourceManager *resourceManager);
void addPoint(KoPointerEvent *event, const QPointF &overridePos = QPointF());
void translatePoints(const QPointF &offset);
void end();
void cancel();
void clearPaint();
using KisToolFreehandHelper::isRunning;
private:
struct Private;
Private * const m_d;
};
#endif /* __KIS_TOOL_LINE_HELPER_H */
diff --git a/plugins/tools/basictools/kis_tool_move.cc b/plugins/tools/basictools/kis_tool_move.cc
index cbdf3c5959..b75d0bdc12 100644
--- a/plugins/tools/basictools/kis_tool_move.cc
+++ b/plugins/tools/basictools/kis_tool_move.cc
@@ -1,535 +1,534 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* 1999 Michael Koch <koch@kde.org>
* 2002 Patrick Julien <freak@codepimps.org>
* 2004 Boudewijn Rempt <boud@valdyas.org>
* 2016 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_move.h"
#include <QPoint>
#include "kis_cursor.h"
#include "kis_selection.h"
#include "kis_canvas2.h"
#include "kis_image.h"
#include "kis_tool_utils.h"
#include "kis_paint_layer.h"
#include "strokes/move_stroke_strategy.h"
#include "kis_tool_movetooloptionswidget.h"
#include "strokes/move_selection_stroke_strategy.h"
#include "kis_resources_snapshot.h"
#include "kis_action_registry.h"
#include "krita_utils.h"
#include <KisViewManager.h>
#include <KisDocument.h>
#include "kis_node_manager.h"
#include "kis_signals_blocker.h"
KisToolMove::KisToolMove(KoCanvasBase * canvas)
: KisTool(canvas, KisCursor::moveCursor())
{
setObjectName("tool_move");
m_optionsWidget = 0;
m_moveInProgress = false;
QAction *a;
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
a = actionRegistry->makeQAction("movetool-move-up", this);
addAction("movetool-move-up", a);
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, false);});
a = actionRegistry->makeQAction("movetool-move-down", this);
addAction("movetool-move-down", a);
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, false);});
a = actionRegistry->makeQAction("movetool-move-left", this);
addAction("movetool-move-left", a);
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, false);});
a = actionRegistry->makeQAction("movetool-move-right", this);
addAction("movetool-move-right", a);
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, false);});
a = actionRegistry->makeQAction("movetool-move-up-more", this);
addAction("movetool-move-up-more", a);
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, true);});
a = actionRegistry->makeQAction("movetool-move-down-more", this);
addAction("movetool-move-down-more", a);
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, true);});
a = actionRegistry->makeQAction("movetool-move-left-more", this);
addAction("movetool-move-left-more", a);
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, true);});
a = actionRegistry->makeQAction("movetool-move-right-more", this);
addAction("movetool-move-right-more", a);
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, true);});
m_showCoordinatesAction = actionRegistry->makeQAction("movetool-show-coordinates", this);
addAction("movetool-show-coordinates", m_showCoordinatesAction);
}
KisToolMove::~KisToolMove()
{
endStroke();
}
void KisToolMove::resetCursorStyle()
{
KisTool::resetCursorStyle();
overrideCursorIfNotEditable();
}
bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
{
if (!currentNode()->isEditable()) return false;
KisNodeSP node;
KisNodeList nodes;
KisImageSP image = this->image();
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(image, currentNode(), 0, this->canvas()->resourceManager());
+ new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager());
KisSelectionSP selection = resources->activeSelection();
if (mode != MoveSelectedLayer && pos) {
bool wholeGroup = !selection && mode == MoveGroup;
node = KisToolUtils::findNode(image->root(), *pos, wholeGroup);
if (node) {
nodes = {node};
}
}
if (nodes.isEmpty()) {
nodes = this->selectedNodes();
KritaUtils::filterContainer<KisNodeList>(nodes,
[](KisNodeSP node) {
return node->isEditable();
});
}
if (nodes.size() == 1) {
node = nodes.first();
}
if (nodes.isEmpty()) {
return false;
}
/**
* If the target node has changed, the stroke should be
* restarted. Otherwise just continue processing current node.
*/
if (m_strokeId) {
if (KritaUtils::compareListsUnordered(nodes, m_currentlyProcessingNodes)) {
return true;
} else {
endStroke();
}
}
KisStrokeStrategy *strategy;
KisPaintLayerSP paintLayer = node ?
dynamic_cast<KisPaintLayer*>(node.data()) : 0;
if (paintLayer && selection &&
!selection->isTotallyUnselected(image->bounds())) {
strategy =
new MoveSelectionStrokeStrategy(paintLayer,
selection,
image.data(),
- image->postExecutionUndoAdapter());
+ image.data());
} else {
strategy =
- new MoveStrokeStrategy(nodes, image.data(),
- image->postExecutionUndoAdapter());
+ new MoveStrokeStrategy(nodes, image.data(), image.data());
}
m_strokeId = image->startStroke(strategy);
m_currentlyProcessingNodes = nodes;
m_accumulatedOffset = QPoint();
return true;
}
void KisToolMove::moveDiscrete(MoveDirection direction, bool big)
{
if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging
if (!currentNode()->isEditable()) return; // Don't move invisible nodes
if (startStrokeImpl(MoveSelectedLayer, 0)) {
setMode(KisTool::PAINT_MODE);
}
// Larger movement if "shift" key is pressed.
qreal scale = big ? m_optionsWidget->moveScale() : 1.0;
qreal moveStep = m_optionsWidget->moveStep() * scale;
QPoint offset = direction == Up ? QPoint( 0, -moveStep) :
direction == Down ? QPoint( 0, moveStep) :
direction == Left ? QPoint(-moveStep, 0) :
QPoint( moveStep, 0) ;
const bool showCoordinates =
m_optionsWidget ? m_optionsWidget->showCoordinates() : true;
if (showCoordinates) {
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
kisCanvas->viewManager()->
showFloatingMessage(
i18nc("floating message in move tool",
"X: %1 px, Y: %2 px",
(m_startPosition + offset).x(),
(m_startPosition + offset).y()),
QIcon(), 1000, KisFloatingMessage::High);
}
KisSignalsBlocker b(m_optionsWidget);
emit moveInNewPosition(m_startPosition + offset);
m_startPosition += offset;
image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset + offset));
m_accumulatedOffset += offset;
m_moveInProgress = false;
emit moveInProgressChanged();
setMode(KisTool::HOVER_MODE);
}
void KisToolMove::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
KisTool::activate(toolActivation, shapes);
QRect totalBounds;
Q_FOREACH (KisNodeSP node, this->selectedNodes()) {
if (node && node->projection()) {
totalBounds |= node->projection()->nonDefaultPixelArea();
}
}
m_startPosition = totalBounds.topLeft();
if (m_optionsWidget)
{
KisSignalsBlocker b(m_optionsWidget);
m_optionsWidget->slotSetTranslate(m_startPosition);
}
}
void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter)
{
Q_UNUSED(gc);
Q_UNUSED(converter);
}
void KisToolMove::deactivate()
{
endStroke();
KisTool::deactivate();
}
void KisToolMove::requestStrokeEnd()
{
endStroke();
}
void KisToolMove::requestStrokeCancellation()
{
cancelStroke();
}
void KisToolMove::beginPrimaryAction(KoPointerEvent *event)
{
startAction(event, moveToolMode());
}
void KisToolMove::continuePrimaryAction(KoPointerEvent *event)
{
continueAction(event);
}
void KisToolMove::endPrimaryAction(KoPointerEvent *event)
{
endAction(event);
}
void KisToolMove::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
// Ctrl+Right click toggles between moving current layer and moving layer w/ content
if (action == PickFgNode || action == PickBgImage) {
MoveToolMode mode = moveToolMode();
if (mode == MoveSelectedLayer) {
mode = MoveFirstLayer;
} else if (mode == MoveFirstLayer) {
mode = MoveSelectedLayer;
}
startAction(event, mode);
} else {
startAction(event, MoveGroup);
}
}
void KisToolMove::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(action)
continueAction(event);
}
void KisToolMove::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(action)
endAction(event);
}
void KisToolMove::startAction(KoPointerEvent *event, MoveToolMode mode)
{
QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
m_dragStart = pos;
m_moveInProgress = true;
emit moveInProgressChanged();
if (startStrokeImpl(mode, &pos)) {
setMode(KisTool::PAINT_MODE);
} else {
event->ignore();
}
}
void KisToolMove::continueAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
if (!m_strokeId) return;
QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
const bool showCoordinates =
m_optionsWidget ? m_optionsWidget->showCoordinates() : true;
if (showCoordinates) {
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
kisCanvas->viewManager()->
showFloatingMessage(
i18nc("floating message in move tool",
"X: %1 px, Y: %2 px",
(m_startPosition + (pos - m_dragStart)).x(),
(m_startPosition + (pos - m_dragStart)).y()),
QIcon(), 1000, KisFloatingMessage::High);
}
KisSignalsBlocker b(m_optionsWidget);
emit moveInNewPosition(m_startPosition + (pos - m_dragStart));
pos = applyModifiers(event->modifiers(), pos);
drag(pos);
}
void KisToolMove::endAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
setMode(KisTool::HOVER_MODE);
if (!m_strokeId) return;
QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
pos = applyModifiers(event->modifiers(), pos);
drag(pos);
m_startPosition += pos - m_dragStart;
m_accumulatedOffset += pos - m_dragStart;
}
void KisToolMove::drag(const QPoint& newPos)
{
KisImageWSP image = currentImage();
QPoint offset = m_accumulatedOffset + newPos - m_dragStart;
image->addJob(m_strokeId,
new MoveStrokeStrategy::Data(offset));
}
void KisToolMove::endStroke()
{
if (!m_strokeId) return;
KisImageWSP image = currentImage();
image->endStroke(m_strokeId);
m_strokeId.clear();
m_currentlyProcessingNodes.clear();
m_moveInProgress = false;
emit moveInProgressChanged();
}
void KisToolMove::cancelStroke()
{
if (!m_strokeId) return;
KisImageWSP image = currentImage();
image->cancelStroke(m_strokeId);
m_strokeId.clear();
m_currentlyProcessingNodes.clear();
m_moveInProgress = false;
emit moveInProgressChanged();
}
QWidget* KisToolMove::createOptionWidget()
{
if (!currentImage())
return 0;
m_optionsWidget = new MoveToolOptionsWidget(0, currentImage()->xRes(), toolId());
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(m_optionsWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
m_optionsWidget->layout()->addWidget(specialSpacer);
m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height());
connect(m_showCoordinatesAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(setShowCoordinates(bool)));
connect(m_optionsWidget, SIGNAL(showCoordinatesChanged(bool)), m_showCoordinatesAction, SLOT(setChecked(bool)));
m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates());
m_optionsWidget->slotSetTranslate(m_startPosition);
connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int)));
connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int)));
connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint)));
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
connect(kisCanvas->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)),
this, SLOT(slotNodeChanged(KisNodeList)));
return m_optionsWidget;
}
KisToolMove::MoveToolMode KisToolMove::moveToolMode() const
{
if (m_optionsWidget)
return m_optionsWidget->mode();
return MoveSelectedLayer;
}
bool KisToolMove::moveInProgress() const
{
return m_moveInProgress;
}
QPoint KisToolMove::applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos)
{
QPoint move = pos - m_dragStart;
// Snap to axis
if (modifiers & Qt::ShiftModifier) {
move = snapToClosestAxis(move);
}
// "Precision mode" - scale down movement by 1/5
if (modifiers & Qt::AltModifier) {
const qreal SCALE_FACTOR = .2;
move = SCALE_FACTOR * move;
}
return m_dragStart + move;
}
void KisToolMove::moveBySpinX(int newX)
{
if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging
if (!currentNode()->isEditable()) return; // Don't move invisible nodes
if (startStrokeImpl(MoveSelectedLayer, 0)) {
setMode(KisTool::PAINT_MODE);
}
int offsetX = newX - m_startPosition.x();
QPoint offset = QPoint(offsetX, 0);
KisSignalsBlocker b(m_optionsWidget);
emit moveInNewPosition(m_startPosition + offset);
image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset + offset));
m_accumulatedOffset += offset;
m_startPosition += offset;
m_moveInProgress = false;
emit moveInProgressChanged();
setMode(KisTool::HOVER_MODE);
}
void KisToolMove::moveBySpinY(int newY)
{
if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging
if (!currentNode()->isEditable()) return; // Don't move invisible nodes
if (startStrokeImpl(MoveSelectedLayer, 0)) {
setMode(KisTool::PAINT_MODE);
}
int offsetY = newY - m_startPosition.y();
QPoint offset = QPoint(0, offsetY);
KisSignalsBlocker b(m_optionsWidget);
emit moveInNewPosition(m_startPosition + offset);
image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset + offset));
m_accumulatedOffset += offset;
m_startPosition += offset;
m_moveInProgress = false;
emit moveInProgressChanged();
setMode(KisTool::HOVER_MODE);
}
void KisToolMove::slotNodeChanged(KisNodeList nodes)
{
QRect totalBounds;
Q_FOREACH (KisNodeSP node, nodes) {
if (node && node->projection()) {
totalBounds |= node->projection()->nonDefaultPixelArea();
}
}
m_startPosition = totalBounds.topLeft();
if (m_optionsWidget)
{
KisSignalsBlocker b(m_optionsWidget);
m_optionsWidget->slotSetTranslate(m_startPosition);
}
}
diff --git a/plugins/tools/basictools/kis_tool_multihand.cpp b/plugins/tools/basictools/kis_tool_multihand.cpp
index 9db599c512..1b4cdeb39b 100644
--- a/plugins/tools/basictools/kis_tool_multihand.cpp
+++ b/plugins/tools/basictools/kis_tool_multihand.cpp
@@ -1,408 +1,411 @@
/*
* Copyright (c) 2011 Lukáš Tvrdý <lukast.dev@gmail.com>
* 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_multihand.h"
#include <QTransform>
#include <QPushButton>
#include <QComboBox>
#include <QFormLayout>
#include <QStackedWidget>
#include <kis_slider_spin_box.h>
#include <QLabel>
-
#include "kis_canvas2.h"
#include "kis_cursor.h"
#include "kis_tool_multihand_helper.h"
+
static const int MAXIMUM_BRUSHES = 50;
#include <QtGlobal>
#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
KisToolMultihand::KisToolMultihand(KoCanvasBase *canvas)
: KisToolBrush(canvas),
m_transformMode(SYMMETRY),
m_angle(0),
m_handsCount(6),
m_mirrorVertically(false),
m_mirrorHorizontally(false),
m_showAxes(false),
m_translateRadius(100),
m_setupAxesFlag(false)
+ , customUI(0)
{
+
+
m_helper =
new KisToolMultihandHelper(paintingInformationBuilder(),
kundo2_i18n("Multibrush Stroke"),
recordingAdapter());
resetHelper(m_helper);
if (image()) {
m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height());
}
+
}
KisToolMultihand::~KisToolMultihand()
{
}
void KisToolMultihand::beginPrimaryAction(KoPointerEvent *event)
{
if(m_setupAxesFlag) {
setMode(KisTool::OTHER);
m_axesPoint = convertToPixelCoord(event->point);
requestUpdateOutline(event->point, 0);
updateCanvas();
}
else {
initTransformations();
KisToolFreehand::beginPrimaryAction(event);
}
}
void KisToolMultihand::continuePrimaryAction(KoPointerEvent *event)
{
if(mode() == KisTool::OTHER) {
m_axesPoint = convertToPixelCoord(event->point);
requestUpdateOutline(event->point, 0);
updateCanvas();
}
else {
KisToolFreehand::continuePrimaryAction(event);
}
}
void KisToolMultihand::endPrimaryAction(KoPointerEvent *event)
{
if(mode() == KisTool::OTHER) {
setMode(KisTool::HOVER_MODE);
requestUpdateOutline(event->point, 0);
finishAxesSetup();
}
else {
KisToolFreehand::endPrimaryAction(event);
}
}
void KisToolMultihand::paint(QPainter& gc, const KoViewConverter &converter)
{
if(m_setupAxesFlag) {
int diagonal = (currentImage()->height() + currentImage()->width());
QPainterPath path;
path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle));
path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle));
path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2));
path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2));
paintToolOutline(&gc, pixelToView(path));
}
else {
KisToolFreehand::paint(gc, converter);
if(m_showAxes){
int diagonal = (currentImage()->height() + currentImage()->width());
QPainterPath path;
path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle));
path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle));
path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2));
path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2));
paintToolOutline(&gc, pixelToView(path));
}
}
}
void KisToolMultihand::initTransformations()
{
QVector<QTransform> transformations;
QTransform m;
if(m_transformMode == SYMMETRY) {
qreal angle = 0;
qreal angleStep = (2 * M_PI) / m_handsCount;
for(int i = 0; i < m_handsCount; i++) {
m.translate(m_axesPoint.x(), m_axesPoint.y());
m.rotateRadians(angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
angle += angleStep;
}
}
else if(m_transformMode == MIRROR) {
transformations << m;
if (m_mirrorHorizontally) {
m.translate(m_axesPoint.x(),m_axesPoint.y());
m.rotateRadians(m_angle);
m.scale(-1,1);
m.rotateRadians(-m_angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
}
if (m_mirrorVertically) {
m.translate(m_axesPoint.x(),m_axesPoint.y());
m.rotateRadians(m_angle);
m.scale(1,-1);
m.rotateRadians(-m_angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
}
if (m_mirrorVertically && m_mirrorHorizontally){
m.translate(m_axesPoint.x(),m_axesPoint.y());
m.rotateRadians(m_angle);
m.scale(-1,-1);
m.rotateRadians(-m_angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
}
}
else if(m_transformMode == SNOWFLAKE) {
qreal angle = 0;
qreal angleStep = (2 * M_PI) / m_handsCount/4;
for(int i = 0; i < m_handsCount*4; i++) {
if ((i%2)==1) {
m.translate(m_axesPoint.x(), m_axesPoint.y());
m.rotateRadians(m_angle-angleStep);
m.rotateRadians(angle);
m.scale(-1,1);
m.rotateRadians(-m_angle+angleStep);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
angle += angleStep*2;
} else {
m.translate(m_axesPoint.x(), m_axesPoint.y());
m.rotateRadians(m_angle-angleStep);
m.rotateRadians(angle);
m.rotateRadians(-m_angle+angleStep);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
angle += angleStep*2;
}
}
}
else /* if(m_transformationNode == TRANSLATE) */ {
/**
* TODO: currently, the seed is the same for all the
* strokes
*/
for (int i = 0; i < m_handsCount; i++){
qreal angle = drand48() * M_PI * 2;
qreal length = drand48();
// convert the Polar coordinates to Cartesian coordinates
qreal nx = (m_translateRadius * cos(angle) * length);
qreal ny = (m_translateRadius * sin(angle) * length);
m.translate(m_axesPoint.x(),m_axesPoint.y());
m.rotateRadians(m_angle);
m.translate(nx,ny);
m.rotateRadians(-m_angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
}
}
m_helper->setupTransformations(transformations);
}
QWidget* KisToolMultihand::createOptionWidget()
{
QWidget *widget = KisToolBrush::createOptionWidget();
- m_axesChCkBox = new QCheckBox(i18n("Show Axes"));
-
- connect(m_axesChCkBox,SIGNAL(toggled(bool)),this, SLOT(slotSetAxesVisible(bool)));
+ customUI = new KisToolMultiHandConfigWidget();
- m_axesPointBtn = new QPushButton(i18n("Axes point"), widget);
- m_axesPointBtn->setCheckable(true);
- connect(m_axesPointBtn, SIGNAL(clicked(bool)),this, SLOT(activateAxesPointModeSetup()));
- addOptionWidgetOption(m_axesPointBtn, m_axesChCkBox);
+ // brush smoothing option.
+ customUI->layout()->addWidget(widget);
+ customUI->smoothingOptionsLayout->addWidget(widget);
- m_axesAngleSlider = new KisDoubleSliderSpinBox(widget);
- m_axesAngleSlider->setToolTip(i18n("Set axes angle (degrees)"));
- m_axesAngleSlider->setSuffix(QChar(Qt::Key_degree));
- m_axesAngleSlider->setRange(0.0, 90.0,1);
- m_axesAngleSlider->setEnabled(true);
- connect(m_axesAngleSlider, SIGNAL(valueChanged(qreal)),this, SLOT(slotSetAxesAngle(qreal)));
- addOptionWidgetOption(m_axesAngleSlider, new QLabel(i18n("Axes Angle:")));
+ // setup common parameters that all of the modes will see
+ connect(customUI->showAxesCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetAxesVisible(bool)));
+ customUI->showAxesCheckbox->setChecked((bool)m_configGroup.readEntry("showAxes", false));
- m_transformModesComboBox = new QComboBox(widget);
- m_transformModesComboBox->addItem(i18n("Symmetry"),int(SYMMETRY));
- m_transformModesComboBox->addItem(i18n("Mirror"),int(MIRROR));
- m_transformModesComboBox->addItem(i18n("Translate"),int(TRANSLATE));
- m_transformModesComboBox->addItem(i18n("Snowflake"),int(SNOWFLAKE));
- connect(m_transformModesComboBox,SIGNAL(currentIndexChanged(int)),SLOT(slotSetTransformMode(int)));
- addOptionWidgetOption(m_transformModesComboBox);
+ customUI->moveOriginButton->setCheckable(true);
+ connect(customUI->moveOriginButton, SIGNAL(clicked(bool)),this, SLOT(activateAxesPointModeSetup()));
- m_handsCountSlider = new KisSliderSpinBox(widget);
- m_handsCountSlider->setToolTip(i18n("Brush count"));
- m_handsCountSlider->setRange(1, MAXIMUM_BRUSHES);
- m_handsCountSlider->setEnabled(true);
- connect(m_handsCountSlider, SIGNAL(valueChanged(int)),this, SLOT(slotSetHandsCount(int)));
- addOptionWidgetOption(m_handsCountSlider);
+ customUI->multihandTypeCombobox->addItem(i18n("Symmetry"),int(SYMMETRY)); // axis mode
+ customUI->multihandTypeCombobox->addItem(i18n("Mirror"),int(MIRROR));
+ customUI->multihandTypeCombobox->addItem(i18n("Translate"),int(TRANSLATE));
+ customUI->multihandTypeCombobox->addItem(i18n("Snowflake"),int(SNOWFLAKE));
+ connect(customUI->multihandTypeCombobox,SIGNAL(currentIndexChanged(int)),this, SLOT(slotSetTransformMode(int)));
+ customUI->multihandTypeCombobox->setCurrentIndex(m_configGroup.readEntry("transformMode", 0));
+ slotSetTransformMode(customUI->multihandTypeCombobox->currentIndex());
- m_modeCustomOption = new QStackedWidget(widget);
- QWidget * symmetryWidget = new QWidget(m_modeCustomOption);
- m_modeCustomOption->addWidget(symmetryWidget);
+ customUI->axisRotationSpinbox->setSuffix(QChar(Qt::Key_degree)); // origin rotation
+ customUI->axisRotationSpinbox->setRange(0.0, 90.0);
+ customUI->axisRotationSpinbox->setValue(m_configGroup.readEntry("axesAngle", 0.0));
+ connect( customUI->axisRotationSpinbox, SIGNAL(valueChanged(int)),this, SLOT(slotSetAxesAngle(int)));
- QWidget * mirrorWidget = new QWidget(m_modeCustomOption);
- m_mirrorHorizontallyChCkBox = new QCheckBox(i18n("Horizontally"));
- m_mirrorVerticallyChCkBox = new QCheckBox(i18n("Vertically"));
- connect(m_mirrorHorizontallyChCkBox,SIGNAL(toggled(bool)),this, SLOT(slotSetMirrorHorizontally(bool)));
- connect(m_mirrorVerticallyChCkBox,SIGNAL(toggled(bool)),this, SLOT(slotSetMirrorVertically(bool)));
- QGridLayout * mirrorLayout = new QGridLayout(mirrorWidget);
- mirrorLayout->addWidget(m_mirrorHorizontallyChCkBox,0,0);
- mirrorLayout->addWidget(m_mirrorVerticallyChCkBox,0,1);
- mirrorWidget->setLayout(mirrorLayout);
- m_modeCustomOption->addWidget(mirrorWidget);
+ // symmetry mode options
+ customUI->brushCountSpinBox->setRange(1, MAXIMUM_BRUSHES);
+ connect(customUI->brushCountSpinBox, SIGNAL(valueChanged(int)),this, SLOT(slotSetHandsCount(int)));
+ customUI->brushCountSpinBox->setValue(m_configGroup.readEntry("handsCount", 4));
- QWidget * translateWidget = new QWidget(m_modeCustomOption);
- m_translateRadiusSlider = new KisSliderSpinBox(translateWidget);
- m_translateRadiusSlider->setRange(0, 200);
+ // mirror mode specific options
+ connect(customUI->horizontalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorHorizontally(bool)));
+ customUI->horizontalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorHorizontally", false));
- m_translateRadiusSlider->setSuffix(i18n(" px"));
- connect(m_translateRadiusSlider,SIGNAL(valueChanged(int)),this,SLOT(slotSetTranslateRadius(int)));
+ connect(customUI->verticalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorVertically(bool)));
+ customUI->verticalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorVertically", false));
- QFormLayout *radiusLayout = new QFormLayout(translateWidget);
- radiusLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
- radiusLayout->addRow(i18n("Radius"), m_translateRadiusSlider);
- translateWidget->setLayout(radiusLayout);
+ // translate mode options
+ customUI->translationRadiusSpinbox->setRange(0, 200);
+ customUI->translationRadiusSpinbox->setSuffix(i18n(" px"));
+ customUI->translationRadiusSpinbox->setValue(m_configGroup.readEntry("translateRadius", 0));
- m_modeCustomOption->addWidget(translateWidget);
- m_modeCustomOption->setCurrentIndex(m_transformModesComboBox->currentIndex());
+ connect(customUI->translationRadiusSpinbox,SIGNAL(valueChanged(int)),this,SLOT(slotSetTranslateRadius(int)));
- addOptionWidgetOption(m_modeCustomOption);
+ // snowflake re-uses the existing options, so there is no special parameters for that...
- // read values from configuration file
- m_axesChCkBox->setChecked((bool)m_configGroup.readEntry("showAxes", false));
- m_mirrorHorizontallyChCkBox->setChecked((bool)m_configGroup.readEntry("mirrorHorizontally", false));
- m_mirrorVerticallyChCkBox->setChecked((bool)m_configGroup.readEntry("mirrorVertically", false));
- m_axesAngleSlider->setValue(m_configGroup.readEntry("axesAngle", 0.0));
- m_transformModesComboBox->setCurrentIndex(m_configGroup.readEntry("transformMode", 0));
- m_translateRadiusSlider->setValue(m_configGroup.readEntry("translateRadius", 0));
- m_handsCountSlider->setValue(m_configGroup.readEntry("handsCount", 4));
- return widget;
+ return static_cast<QWidget*>(customUI); // keeping it in the native class until the end allows us to access the UI components
}
void KisToolMultihand::activateAxesPointModeSetup()
{
- if (m_axesPointBtn->isChecked()){
+ if (customUI->moveOriginButton->isChecked()){
m_setupAxesFlag = true;
useCursor(KisCursor::crossCursor());
updateCanvas();
} else {
finishAxesSetup();
}
}
void KisToolMultihand::finishAxesSetup()
{
m_setupAxesFlag = false;
- m_axesPointBtn->setChecked(false);
+ customUI->moveOriginButton->setChecked(false);
resetCursorStyle();
updateCanvas();
}
void KisToolMultihand::updateCanvas()
{
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kisCanvas);
kisCanvas->updateCanvas();
}
void KisToolMultihand::slotSetHandsCount(int count)
{
m_handsCount = count;
m_configGroup.writeEntry("handsCount", count);
}
-void KisToolMultihand::slotSetAxesAngle(qreal angle)
+void KisToolMultihand::slotSetAxesAngle(int angle)
{
//negative so axes rotates counter clockwise
m_angle = -angle*M_PI/180;
updateCanvas();
m_configGroup.writeEntry("axesAngle", angle);
}
void KisToolMultihand::slotSetTransformMode(int index)
{
- m_transformMode = enumTransforModes(m_transformModesComboBox->itemData(index).toInt());
- m_modeCustomOption->setCurrentIndex(index);
- m_handsCountSlider->setVisible(m_transformMode != MIRROR);
+ m_transformMode = enumTransforModes(customUI->multihandTypeCombobox->itemData(index).toInt());
m_configGroup.writeEntry("transformMode", index);
+
+
+ // hide all of the UI elements by default
+ customUI->horizontalCheckbox->setVisible(false);
+ customUI->verticalCheckbox->setVisible(false);
+ customUI->translationRadiusSpinbox->setVisible(false);
+ customUI->radiusLabel->setVisible(false);
+ customUI->brushCountSpinBox->setVisible(false);
+ customUI->brushesLabel->setVisible(false);
+
+ // turn on what we need
+ if (index == int(MIRROR)) {
+ customUI->horizontalCheckbox->setVisible(true);
+ customUI->verticalCheckbox->setVisible(true);
+ }
+
+ if (index == int(TRANSLATE)) {
+ customUI->translationRadiusSpinbox->setVisible(true);
+ customUI->radiusLabel->setVisible(true);
+ }
+
+ if (index == int(SYMMETRY) || index == int(SNOWFLAKE) || index == int(TRANSLATE) ) {
+ customUI->brushCountSpinBox->setVisible(true);
+ customUI->brushesLabel->setVisible(true);
+ }
+
}
void KisToolMultihand::slotSetAxesVisible(bool vis)
{
m_showAxes = vis;
updateCanvas();
m_configGroup.writeEntry("showAxes", vis);
}
void KisToolMultihand::slotSetMirrorVertically(bool mirror)
{
m_mirrorVertically = mirror;
m_configGroup.writeEntry("mirrorVertically", mirror);
}
void KisToolMultihand::slotSetMirrorHorizontally(bool mirror)
{
m_mirrorHorizontally = mirror;
m_configGroup.writeEntry("mirrorHorizontally", mirror);
}
void KisToolMultihand::slotSetTranslateRadius(int radius)
{
m_translateRadius = radius;
m_configGroup.writeEntry("translateRadius", radius);
}
diff --git a/plugins/tools/basictools/kis_tool_multihand.h b/plugins/tools/basictools/kis_tool_multihand.h
index 08bcbcd2c6..01e76abd40 100644
--- a/plugins/tools/basictools/kis_tool_multihand.h
+++ b/plugins/tools/basictools/kis_tool_multihand.h
@@ -1,116 +1,120 @@
/*
* Copyright (c) 2011 Lukáš Tvrdý <lukast.dev@gmail.com>
* 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_MULTIHAND_H
#define __KIS_TOOL_MULTIHAND_H
#include "kis_tool_brush.h"
#include <kis_icon.h>
+#include "kis_tool_multihand_config.h"
class QPushButton;
class QCheckBox;
class QComboBox;
class QStackedWidget;
class KisSliderSpinBox;
class KisToolMultihandHelper;
class KisToolMultihand : public KisToolBrush
{
Q_OBJECT
public:
KisToolMultihand(KoCanvasBase *canvas);
~KisToolMultihand();
void beginPrimaryAction(KoPointerEvent *event);
void continuePrimaryAction(KoPointerEvent *event);
void endPrimaryAction(KoPointerEvent *event);
protected:
void paint(QPainter& gc, const KoViewConverter &converter);
QWidget* createOptionWidget();
private:
void initTransformations();
void finishAxesSetup();
void updateCanvas();
private Q_SLOTS:
void activateAxesPointModeSetup();
void slotSetHandsCount(int count);
- void slotSetAxesAngle(qreal angle);
+ void slotSetAxesAngle(int angle);
void slotSetTransformMode(int qcomboboxIndex);
void slotSetAxesVisible(bool vis);
void slotSetMirrorVertically(bool mirror);
void slotSetMirrorHorizontally(bool mirror);
void slotSetTranslateRadius(int radius);
private:
KisToolMultihandHelper *m_helper;
enum enumTransforModes { SYMMETRY, MIRROR, TRANSLATE, SNOWFLAKE };
enumTransforModes m_transformMode;
QPointF m_axesPoint;
qreal m_angle;
int m_handsCount;
bool m_mirrorVertically;
bool m_mirrorHorizontally;
bool m_showAxes;
int m_translateRadius;
bool m_setupAxesFlag;
QComboBox * m_transformModesComboBox;
KisSliderSpinBox *m_handsCountSlider;
KisDoubleSliderSpinBox *m_axesAngleSlider;
QCheckBox *m_axesChCkBox;
QStackedWidget *m_modeCustomOption;
QCheckBox *m_mirrorVerticallyChCkBox;
QCheckBox *m_mirrorHorizontallyChCkBox;
KisSliderSpinBox *m_translateRadiusSlider;
QPushButton *m_axesPointBtn;
+
+
+ KisToolMultiHandConfigWidget* customUI;
};
class KisToolMultiBrushFactory : public KoToolFactoryBase
{
public:
KisToolMultiBrushFactory()
: KoToolFactoryBase("KritaShape/KisToolMultiBrush") {
setToolTip(i18n("Multibrush Tool"));
// Temporarily
setSection(TOOL_TYPE_SHAPE);
setIconName(koIconNameCStr("krita_tool_multihand"));
setShortcut(QKeySequence(Qt::Key_Q));
setPriority(11);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
}
virtual ~KisToolMultiBrushFactory() {}
virtual KoToolBase * createTool(KoCanvasBase *canvas) {
return new KisToolMultihand(canvas);
}
};
#endif /* __KIS_TOOL_MULTIHAND_H */
diff --git a/plugins/tools/basictools/kis_tool_multihand_config.cpp b/plugins/tools/basictools/kis_tool_multihand_config.cpp
new file mode 100644
index 0000000000..c65a01eac5
--- /dev/null
+++ b/plugins/tools/basictools/kis_tool_multihand_config.cpp
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2016 Scott Petrovic <scottpetrovic@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_icon.h>
+#include "kis_tool_multihand_config.h"
+
+KisToolMultiHandConfigWidget::KisToolMultiHandConfigWidget(QWidget* parent)
+ : QWidget(parent)
+{
+ setupUi(this);
+}
+
+
+KisToolMultiHandConfigWidget::~KisToolMultiHandConfigWidget()
+{
+
+}
diff --git a/plugins/tools/basictools/kis_tool_multihand_config.h b/plugins/tools/basictools/kis_tool_multihand_config.h
new file mode 100644
index 0000000000..a917c47100
--- /dev/null
+++ b/plugins/tools/basictools/kis_tool_multihand_config.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 2016 Scott Petrovic <scottpetrovic@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 KISTOOLMULTIHANDCONFIG_H
+#define KISTOOLMULTIHANDCONFIG_H
+
+#include "ui_wdgmultihandtool.h"
+
+
+class KisToolMultiHandConfigWidget : public QWidget, public Ui::WdgMultiHandTool
+{
+ Q_OBJECT
+
+public:
+ KisToolMultiHandConfigWidget(QWidget *parent=0);
+ ~KisToolMultiHandConfigWidget();
+
+//Q_SIGNALS:
+
+
+//public Q_SLOTS:
+ //void cropTypeSelectableChanged();
+
+//private:
+ //KisToolCrop* m_cropTool;
+};
+
+#endif // KISTOOLMULTIHANDCONFIG_H
diff --git a/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp b/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp
index 2c29deb2e2..2fd6584a0c 100644
--- a/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp
+++ b/plugins/tools/basictools/strokes/move_selection_stroke_strategy.cpp
@@ -1,189 +1,173 @@
/*
* 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 "move_selection_stroke_strategy.h"
#include <klocalizedstring.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
#include "kis_image.h"
#include "kis_paint_layer.h"
#include "kis_painter.h"
#include "kis_transaction.h"
#include <commands_new/kis_selection_move_command2.h>
MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(KisPaintLayerSP paintLayer,
KisSelectionSP selection,
KisUpdatesFacade *updatesFacade,
- KisPostExecutionUndoAdapter *undoAdapter)
- : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Move Selection"), false, undoAdapter),
+ KisStrokeUndoFacade *undoFacade)
+ : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Move Selection"), false, undoFacade),
m_paintLayer(paintLayer),
m_selection(selection),
- m_updatesFacade(updatesFacade),
- m_undoEnabled(true)
+ m_updatesFacade(updatesFacade)
{
enableJob(KisSimpleStrokeStrategy::JOB_INIT);
enableJob(KisSimpleStrokeStrategy::JOB_FINISH);
enableJob(KisSimpleStrokeStrategy::JOB_CANCEL);
}
-MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs, bool suppressUndo)
- : KisStrokeStrategyUndoCommandBased(rhs, suppressUndo),
+MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs)
+ : KisStrokeStrategyUndoCommandBased(rhs),
m_paintLayer(rhs.m_paintLayer),
m_selection(rhs.m_selection),
- m_updatesFacade(rhs.m_updatesFacade),
- m_undoEnabled(rhs.m_undoEnabled)
+ m_updatesFacade(rhs.m_updatesFacade)
{
}
void MoveSelectionStrokeStrategy::initStrokeCallback()
{
KisStrokeStrategyUndoCommandBased::initStrokeCallback();
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
KisPaintDeviceSP movedDevice = new KisPaintDevice(m_paintLayer.data(), paintDevice->colorSpace());
QRect copyRect = m_selection->selectedRect();
KisPainter gc(movedDevice);
gc.setSelection(m_selection);
gc.bitBlt(copyRect.topLeft(), paintDevice, copyRect);
gc.end();
KisTransaction cutTransaction(name(), paintDevice);
paintDevice->clearSelection(m_selection);
runAndSaveCommand(KUndo2CommandSP(cutTransaction.endAndTake()),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
KisIndirectPaintingSupport *indirect =
static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data());
indirect->setTemporaryTarget(movedDevice);
indirect->setTemporaryCompositeOp(COMPOSITE_OVER);
indirect->setTemporaryOpacity(OPACITY_OPAQUE_U8);
m_initialDeviceOffset = QPoint(movedDevice->x(), movedDevice->y());
m_selection->setVisible(false);
}
void MoveSelectionStrokeStrategy::finishStrokeCallback()
{
KisIndirectPaintingSupport *indirect =
static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data());
KisTransaction transaction(name(), m_paintLayer->paintDevice());
indirect->mergeToLayer(m_paintLayer, (KisPostExecutionUndoAdapter*)0, KUndo2MagicString());
- if (m_undoEnabled) {
- runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
- KisStrokeJobData::SEQUENTIAL,
- KisStrokeJobData::NORMAL);
- } else {
- transaction.end();
- }
+ runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
+ KisStrokeJobData::SEQUENTIAL,
+ KisStrokeJobData::NORMAL);
indirect->setTemporaryTarget(0);
QPoint selectionOffset(m_selection->x(), m_selection->y());
m_updatesFacade->blockUpdates();
- if (m_undoEnabled) {
- KUndo2CommandSP moveSelectionCommand(
- new KisSelectionMoveCommand2(m_selection, selectionOffset, selectionOffset + m_finalOffset));
+ KUndo2CommandSP moveSelectionCommand(
+ new KisSelectionMoveCommand2(m_selection, selectionOffset, selectionOffset + m_finalOffset));
- runAndSaveCommand(
- moveSelectionCommand,
+ runAndSaveCommand(
+ moveSelectionCommand,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
- } else {
- QPoint offset = selectionOffset + m_finalOffset;
- m_selection->setX(offset.x());
- m_selection->setY(offset.y());
- }
m_updatesFacade->unblockUpdates();
m_selection->setVisible(true);
KisStrokeStrategyUndoCommandBased::finishStrokeCallback();
}
void MoveSelectionStrokeStrategy::cancelStrokeCallback()
{
KisIndirectPaintingSupport *indirect =
static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data());
if (indirect) {
KisPaintDeviceSP t = indirect->temporaryTarget();
if (t) {
QRegion dirtyRegion = t->region();
indirect->setTemporaryTarget(0);
m_selection->setVisible(true);
m_paintLayer->setDirty(dirtyRegion);
}
}
KisStrokeStrategyUndoCommandBased::cancelStrokeCallback();
}
#include "move_stroke_strategy.h"
void MoveSelectionStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
MoveStrokeStrategy::Data *d = dynamic_cast<MoveStrokeStrategy::Data*>(data);
if (d) {
KisIndirectPaintingSupport *indirect =
static_cast<KisIndirectPaintingSupport*>(m_paintLayer.data());
KisPaintDeviceSP movedDevice = indirect->temporaryTarget();
QRegion dirtyRegion = movedDevice->region();
QPoint currentDeviceOffset(movedDevice->x(), movedDevice->y());
QPoint newDeviceOffset(m_initialDeviceOffset + d->offset);
dirtyRegion |= dirtyRegion.translated(newDeviceOffset - currentDeviceOffset);
movedDevice->setX(newDeviceOffset.x());
movedDevice->setY(newDeviceOffset.y());
m_finalOffset = d->offset;
m_paintLayer->setDirty(dirtyRegion);
} else {
KisStrokeStrategyUndoCommandBased::doStrokeCallback(data);
}
}
-void MoveSelectionStrokeStrategy::setUndoEnabled(bool value)
-{
- m_undoEnabled = value;
-}
-
KisStrokeStrategy* MoveSelectionStrokeStrategy::createLodClone(int levelOfDetail)
{
- MoveSelectionStrokeStrategy *clone = new MoveSelectionStrokeStrategy(*this, levelOfDetail > 0);
- clone->setUndoEnabled(false);
+ Q_UNUSED(levelOfDetail);
+
+ MoveSelectionStrokeStrategy *clone = new MoveSelectionStrokeStrategy(*this);
return clone;
}
diff --git a/plugins/tools/basictools/strokes/move_selection_stroke_strategy.h b/plugins/tools/basictools/strokes/move_selection_stroke_strategy.h
index fd77790011..6d44fe0e56 100644
--- a/plugins/tools/basictools/strokes/move_selection_stroke_strategy.h
+++ b/plugins/tools/basictools/strokes/move_selection_stroke_strategy.h
@@ -1,57 +1,55 @@
/*
* 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 __MOVE_SELECTION_STROKE_STRATEGY_H
#define __MOVE_SELECTION_STROKE_STRATEGY_H
#include "kis_stroke_strategy_undo_command_based.h"
#include "kis_types.h"
class KisPostExecutionUndoAdapter;
class KisUpdatesFacade;
class MoveSelectionStrokeStrategy : public KisStrokeStrategyUndoCommandBased
{
public:
MoveSelectionStrokeStrategy(KisPaintLayerSP paintLayer,
KisSelectionSP selection,
KisUpdatesFacade *updatesFacade,
- KisPostExecutionUndoAdapter *undoAdapter);
+ KisStrokeUndoFacade *undoFacade);
void initStrokeCallback();
void finishStrokeCallback();
void cancelStrokeCallback();
void doStrokeCallback(KisStrokeJobData *data);
private:
- MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs, bool suppressUndo);
+ MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs);
- void setUndoEnabled(bool value);
KisStrokeStrategy* createLodClone(int levelOfDetail);
private:
KisPaintLayerSP m_paintLayer;
KisSelectionSP m_selection;
KisUpdatesFacade *m_updatesFacade;
QPoint m_finalOffset;
QPoint m_initialDeviceOffset;
- bool m_undoEnabled;
};
#endif /* __MOVE_SELECTION_STROKE_STRATEGY_H */
diff --git a/plugins/tools/basictools/strokes/move_stroke_strategy.cpp b/plugins/tools/basictools/strokes/move_stroke_strategy.cpp
index bd1685596f..33841c61de 100644
--- a/plugins/tools/basictools/strokes/move_stroke_strategy.cpp
+++ b/plugins/tools/basictools/strokes/move_stroke_strategy.cpp
@@ -1,238 +1,228 @@
/*
* 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 "move_stroke_strategy.h"
#include <klocalizedstring.h>
#include "kis_image_interfaces.h"
#include "kis_node.h"
#include "commands_new/kis_update_command.h"
#include "commands_new/kis_node_move_command2.h"
#include "kis_layer_utils.h"
#include "krita_utils.h"
MoveStrokeStrategy::MoveStrokeStrategy(KisNodeList nodes,
KisUpdatesFacade *updatesFacade,
- KisPostExecutionUndoAdapter *undoAdapter)
- : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Move"), false, undoAdapter),
+ KisStrokeUndoFacade *undoFacade)
+ : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Move"), false, undoFacade),
m_nodes(),
m_updatesFacade(updatesFacade),
- m_undoEnabled(true),
m_updatesEnabled(true)
{
m_nodes = KisLayerUtils::sortAndFilterMergableInternalNodes(nodes, true);
KritaUtils::filterContainer<KisNodeList>(m_nodes,
[this](KisNodeSP node) {
return
!KisLayerUtils::checkIsCloneOf(node, m_nodes) &&
node->isEditable();
});
Q_FOREACH(KisNodeSP subtree, m_nodes) {
KisLayerUtils::recursiveApplyNodes(
subtree,
[this](KisNodeSP node) {
if (KisLayerUtils::checkIsCloneOf(node, m_nodes) ||
!node->isEditable()) {
m_blacklistedNodes.insert(node);
}
});
}
setSupportsWrapAroundMode(true);
}
-MoveStrokeStrategy::MoveStrokeStrategy(const MoveStrokeStrategy &rhs, bool suppressUndo)
- : KisStrokeStrategyUndoCommandBased(rhs, suppressUndo),
+MoveStrokeStrategy::MoveStrokeStrategy(const MoveStrokeStrategy &rhs)
+ : KisStrokeStrategyUndoCommandBased(rhs),
m_nodes(rhs.m_nodes),
m_blacklistedNodes(rhs.m_blacklistedNodes),
m_updatesFacade(rhs.m_updatesFacade),
m_finalOffset(rhs.m_finalOffset),
m_dirtyRect(rhs.m_dirtyRect),
m_dirtyRects(rhs.m_dirtyRects),
- m_undoEnabled(rhs.m_undoEnabled),
m_updatesEnabled(rhs.m_updatesEnabled)
{
}
void MoveStrokeStrategy::saveInitialNodeOffsets(KisNodeSP node)
{
if (!m_blacklistedNodes.contains(node)) {
m_initialNodeOffsets.insert(node, QPoint(node->x(), node->y()));
}
KisNodeSP child = node->firstChild();
while(child) {
saveInitialNodeOffsets(child);
child = child->nextSibling();
}
}
void MoveStrokeStrategy::initStrokeCallback()
{
Q_FOREACH(KisNodeSP node, m_nodes) {
saveInitialNodeOffsets(node);
}
KisStrokeStrategyUndoCommandBased::initStrokeCallback();
}
void MoveStrokeStrategy::finishStrokeCallback()
{
- if (m_undoEnabled) {
- Q_FOREACH (KisNodeSP node, m_nodes) {
- KUndo2Command *updateCommand =
- new KisUpdateCommand(node, m_dirtyRects[node], m_updatesFacade, true);
+ Q_FOREACH (KisNodeSP node, m_nodes) {
+ KUndo2Command *updateCommand =
+ new KisUpdateCommand(node, m_dirtyRects[node], m_updatesFacade, true);
- addMoveCommands(node, updateCommand);
+ addMoveCommands(node, updateCommand);
- notifyCommandDone(KUndo2CommandSP(updateCommand),
- KisStrokeJobData::SEQUENTIAL,
- KisStrokeJobData::EXCLUSIVE);
- }
+ notifyCommandDone(KUndo2CommandSP(updateCommand),
+ KisStrokeJobData::SEQUENTIAL,
+ KisStrokeJobData::EXCLUSIVE);
}
if (!m_updatesEnabled) {
Q_FOREACH (KisNodeSP node, m_nodes) {
m_updatesFacade->refreshGraphAsync(node, m_dirtyRects[node]);
}
}
KisStrokeStrategyUndoCommandBased::finishStrokeCallback();
}
void MoveStrokeStrategy::cancelStrokeCallback()
{
if (!m_nodes.isEmpty()) {
// FIXME: make cancel job exclusive instead
m_updatesFacade->blockUpdates();
moveAndUpdate(QPoint());
m_updatesFacade->unblockUpdates();
}
KisStrokeStrategyUndoCommandBased::cancelStrokeCallback();
}
void MoveStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
Data *d = dynamic_cast<Data*>(data);
if(!m_nodes.isEmpty() && d) {
moveAndUpdate(d->offset);
/**
* NOTE: we do not care about threading here, because
* all our jobs are declared sequential
*/
m_finalOffset = d->offset;
}
else {
KisStrokeStrategyUndoCommandBased::doStrokeCallback(data);
}
}
void MoveStrokeStrategy::moveAndUpdate(QPoint offset)
{
Q_FOREACH (KisNodeSP node, m_nodes) {
QRect dirtyRect = moveNode(node, offset);
m_dirtyRects[node] |= dirtyRect;
if (m_updatesEnabled) {
m_updatesFacade->refreshGraphAsync(node, dirtyRect);
}
}
}
QRect MoveStrokeStrategy::moveNode(KisNodeSP node, QPoint offset)
{
QRect dirtyRect;
if (!m_blacklistedNodes.contains(node)) {
dirtyRect = node->extent();
QPoint newOffset = m_initialNodeOffsets[node] + offset;
/**
* Some layers, e.g. clones need an update to change extent(), so
* calculate the dirty rect manually
*/
QPoint currentOffset(node->x(), node->y());
dirtyRect |= dirtyRect.translated(newOffset - currentOffset);
node->setX(newOffset.x());
node->setY(newOffset.y());
KisNodeMoveCommand2::tryNotifySelection(node);
}
KisNodeSP child = node->firstChild();
while(child) {
dirtyRect |= moveNode(child, offset);
child = child->nextSibling();
}
return dirtyRect;
}
void MoveStrokeStrategy::addMoveCommands(KisNodeSP node, KUndo2Command *parent)
{
if (!m_blacklistedNodes.contains(node)) {
QPoint nodeOffset(node->x(), node->y());
new KisNodeMoveCommand2(node, nodeOffset - m_finalOffset, nodeOffset, parent);
}
KisNodeSP child = node->firstChild();
while(child) {
addMoveCommands(child, parent);
child = child->nextSibling();
}
}
-void MoveStrokeStrategy::setUndoEnabled(bool value)
-{
- m_undoEnabled = value;
-}
-
void MoveStrokeStrategy::setUpdatesEnabled(bool value)
{
m_updatesEnabled = value;
}
bool checkSupportsLodMoves(KisNodeSP subtree)
{
return
!KisLayerUtils::recursiveFindNode(
subtree,
[](KisNodeSP node) -> bool {
return !node->supportsLodMoves();
});
}
KisStrokeStrategy* MoveStrokeStrategy::createLodClone(int levelOfDetail)
{
Q_FOREACH (KisNodeSP node, m_nodes) {
if (!checkSupportsLodMoves(node)) return 0;
}
- MoveStrokeStrategy *clone = new MoveStrokeStrategy(*this, levelOfDetail > 0);
- clone->setUndoEnabled(false);
+ MoveStrokeStrategy *clone = new MoveStrokeStrategy(*this);
this->setUpdatesEnabled(false);
return clone;
}
diff --git a/plugins/tools/basictools/strokes/move_stroke_strategy.h b/plugins/tools/basictools/strokes/move_stroke_strategy.h
index 42547a8f10..d91035c052 100644
--- a/plugins/tools/basictools/strokes/move_stroke_strategy.h
+++ b/plugins/tools/basictools/strokes/move_stroke_strategy.h
@@ -1,92 +1,91 @@
/*
* 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 __MOVE_STROKE_STRATEGY_H
#define __MOVE_STROKE_STRATEGY_H
#include <QHash>
#include "kis_stroke_strategy_undo_command_based.h"
#include "kis_types.h"
#include "kis_lod_transform.h"
class KisUpdatesFacade;
class KisPostExecutionUndoAdapter;
class MoveStrokeStrategy : public KisStrokeStrategyUndoCommandBased
{
public:
class Data : public KisStrokeJobData {
public:
Data(QPoint _offset)
: KisStrokeJobData(SEQUENTIAL, EXCLUSIVE),
offset(_offset)
{
}
KisStrokeJobData* createLodClone(int levelOfDetail) {
return new Data(*this, levelOfDetail);
}
QPoint offset;
private:
Data(const Data &rhs, int levelOfDetail)
: KisStrokeJobData(rhs)
{
KisLodTransform t(levelOfDetail);
offset = t.map(rhs.offset);
}
};
public:
MoveStrokeStrategy(KisNodeList nodes, KisUpdatesFacade *updatesFacade,
- KisPostExecutionUndoAdapter *undoAdapter);
+ KisStrokeUndoFacade *undoFacade);
void initStrokeCallback();
void finishStrokeCallback();
void cancelStrokeCallback();
void doStrokeCallback(KisStrokeJobData *data);
KisStrokeStrategy* createLodClone(int levelOfDetail);
private:
- MoveStrokeStrategy(const MoveStrokeStrategy &rhs, bool suppressUndo);
+ MoveStrokeStrategy(const MoveStrokeStrategy &rhs);
void setUndoEnabled(bool value);
void setUpdatesEnabled(bool value);
private:
void moveAndUpdate(QPoint offset);
QRect moveNode(KisNodeSP node, QPoint offset);
void addMoveCommands(KisNodeSP node, KUndo2Command *parent);
void saveInitialNodeOffsets(KisNodeSP node);
private:
KisNodeList m_nodes;
QSet<KisNodeSP> m_blacklistedNodes;
KisUpdatesFacade *m_updatesFacade;
QPoint m_finalOffset;
QRect m_dirtyRect;
QHash<KisNodeSP, QRect> m_dirtyRects;
- bool m_undoEnabled;
bool m_updatesEnabled;
QHash<KisNodeSP, QPoint> m_initialNodeOffsets;
};
#endif /* __MOVE_STROKE_STRATEGY_H */
diff --git a/plugins/tools/basictools/tests/move_selection_stroke_test.cpp b/plugins/tools/basictools/tests/move_selection_stroke_test.cpp
index c00c9bf1ad..80e5844ed6 100644
--- a/plugins/tools/basictools/tests/move_selection_stroke_test.cpp
+++ b/plugins/tools/basictools/tests/move_selection_stroke_test.cpp
@@ -1,130 +1,129 @@
/*
* 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 "move_selection_stroke_test.h"
#include <QTest>
#include <KoColor.h>
#include "kis_image.h"
#include "strokes/move_selection_stroke_strategy.h"
#include "stroke_testing_utils.h"
#include "kis_selection.h"
#include "commands/kis_selection_commands.h"
#include "strokes/move_stroke_strategy.h"
#include "kis_paint_layer.h"
#include "kis_image_barrier_locker.h"
#include "kis_paint_device_frames_interface.h"
#include "kis_paint_device_debug_utils.h"
KisPaintDeviceSP lodDevice(KisPaintDeviceSP dev)
{
KisPaintDeviceSP tmp = new KisPaintDevice(dev->colorSpace());
dev->tesingFetchLodDevice(tmp);
return tmp;
}
void MoveSelectionStrokeTest::test()
{
const QRect imageRect(0,0,800,800);
KisImageSP image = utils::createImage(0, imageRect.size());
QScopedPointer<KoCanvasResourceManager> manager(
utils::createResourceManager(image));
image->setDesiredLevelOfDetail(2);
image->waitForDone();
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
image->root()->firstChild(),
- image->postExecutionUndoAdapter(),
- manager.data());
+ manager.data());
KisNodeSP currentNode = resources->currentNode();
KisPaintLayerSP currentPaintLayer = dynamic_cast<KisPaintLayer*>(currentNode.data());
Q_ASSERT(currentPaintLayer);
KisPaintDeviceSP device = currentNode->paintDevice();
{
KisImageBarrierLocker locker(image);
device->fill(QRect(0,0,400,400), KoColor(Qt::red, image->colorSpace()));
device->fill(QRect(400,0,400,400), KoColor(Qt::green, image->colorSpace()));
device->fill(QRect(0,400,400,400), KoColor(Qt::blue, image->colorSpace()));
device->fill(QRect(400,400,400,400), KoColor(Qt::yellow, image->colorSpace()));
}
{
KisSelectionSP newSelection = new KisSelection();
newSelection->pixelSelection()->select(QRect(200,200,400,400), OPACITY_OPAQUE_U8);
KisSetGlobalSelectionCommand cmd(image, newSelection);
cmd.redo();
}
KIS_DUMP_DEVICE_2(device, imageRect, "00_0_device", "mm");
KIS_DUMP_DEVICE_2(image->globalSelection()->projection(), imageRect, "01_0_selection", "mm");
{
MoveSelectionStrokeStrategy *strategy =
new MoveSelectionStrokeStrategy(currentPaintLayer,
image->globalSelection(),
image.data(),
- image->postExecutionUndoAdapter());
+ image.data());
KisStrokeId id = image->startStroke(strategy);
image->addJob(id, new MoveStrokeStrategy::Data(QPoint(100,100)));
image->endStroke(id);
image->waitForDone();
KIS_DUMP_DEVICE_2(device, imageRect, "02_0_device", "mm");
KIS_DUMP_DEVICE_2(lodDevice(device), imageRect, "02_1_device_lod", "mm");
KIS_DUMP_DEVICE_2(image->globalSelection()->projection(), imageRect, "03_0_selection", "mm");
KIS_DUMP_DEVICE_2(lodDevice(image->globalSelection()->projection()), imageRect, "03_1_selection_lod", "mm");
}
{
MoveSelectionStrokeStrategy *strategy =
new MoveSelectionStrokeStrategy(currentPaintLayer,
image->globalSelection(),
image.data(),
- image->postExecutionUndoAdapter());
+ image.data());
KisStrokeId id = image->startStroke(strategy);
image->addJob(id, new MoveStrokeStrategy::Data(QPoint(-200,50)));
image->endStroke(id);
image->waitForDone();
KIS_DUMP_DEVICE_2(device, imageRect, "04_0_device", "mm");
KIS_DUMP_DEVICE_2(lodDevice(device), imageRect, "04_1_device_lod", "mm");
KIS_DUMP_DEVICE_2(image->globalSelection()->projection(), imageRect, "05_0_selection", "mm");
KIS_DUMP_DEVICE_2(lodDevice(image->globalSelection()->projection()), imageRect, "05_1_selection_lod", "mm");
}
}
QTEST_GUILESS_MAIN(MoveSelectionStrokeTest)
diff --git a/plugins/tools/basictools/tests/move_stroke_test.cpp b/plugins/tools/basictools/tests/move_stroke_test.cpp
index 1cfcc24fe1..b2cdf7cdeb 100644
--- a/plugins/tools/basictools/tests/move_stroke_test.cpp
+++ b/plugins/tools/basictools/tests/move_stroke_test.cpp
@@ -1,91 +1,91 @@
/*
* 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 "move_stroke_test.h"
#include <QTest>
#include "stroke_testing_utils.h"
#include "kis_image.h"
#include "kis_node.h"
#include "kis_paint_device.h"
#include "strokes/move_stroke_strategy.h"
class MoveStrokeTester : public utils::StrokeTester
{
public:
MoveStrokeTester()
: StrokeTester("move", QSize(512, 512), "")
{
}
protected:
using utils::StrokeTester::initImage;
void initImage(KisImageWSP image, KisNodeSP activeNode) override {
Q_UNUSED(image);
QImage src(QString(FILES_DATA_DIR) + QDir::separator() + "carrot.png");
activeNode->original()->convertFromQImage(src, 0);
}
KisStrokeStrategy* createStroke(bool indirectPainting,
KisResourcesSnapshotSP resources,
KisImageWSP image) override {
Q_UNUSED(indirectPainting);
KisNodeSP node = resources->currentNode();
- return new MoveStrokeStrategy({node}, image.data(), resources->postExecutionUndoAdapter());
+ return new MoveStrokeStrategy({node}, image.data(), image.data());
}
using utils::StrokeTester::addPaintingJobs;
void addPaintingJobs(KisImageWSP image,
KisResourcesSnapshotSP resources) override {
Q_UNUSED(resources);
image->
addJob(strokeId(), new MoveStrokeStrategy::Data(QPoint(100,100)));
image->
addJob(strokeId(), new MoveStrokeStrategy::Data(QPoint(50, 50)));
for (int i = 0; i < 25; i++) {
image->
addJob(strokeId(), new MoveStrokeStrategy::Data(QPoint(50+i,50)));
QTest::qSleep(1);
image->
addJob(strokeId(), new MoveStrokeStrategy::Data(QPoint(50+i,50+i)));
QTest::qSleep(1);
}
}
private:
};
void MoveStrokeTest::testMoveStroke()
{
MoveStrokeTester tester;
tester.test();
}
QTEST_GUILESS_MAIN(MoveStrokeTest)
diff --git a/plugins/tools/basictools/wdgmultihandtool.ui b/plugins/tools/basictools/wdgmultihandtool.ui
new file mode 100644
index 0000000000..f8d2a974f4
--- /dev/null
+++ b/plugins/tools/basictools/wdgmultihandtool.ui
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WdgMultiHandTool</class>
+ <widget class="QWidget" name="WdgMultiHandTool">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>212</width>
+ <height>293</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="smoothingOptionsLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>10</number>
+ </property>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="horizontalSpacing">
+ <number>4</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>7</number>
+ </property>
+ <item row="5" column="0">
+ <widget class="QLabel" name="axisTypeLabel">
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="brushesLabel">
+ <property name="text">
+ <string>Brushes:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="KisSliderSpinBox" name="brushCountSpinBox"/>
+ </item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="radiusLabel">
+ <property name="text">
+ <string>Radius:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <widget class="KisSliderSpinBox" name="translationRadiusSpinbox"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="axisRotationLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Rotation:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QCheckBox" name="verticalCheckbox">
+ <property name="text">
+ <string>Vertical</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QCheckBox" name="horizontalCheckbox">
+ <property name="text">
+ <string>Horizontal</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QPushButton" name="moveOriginButton">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Move Origin</string>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="KisSliderSpinBox" name="axisRotationSpinbox" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>15</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QComboBox" name="multihandTypeCombobox"/>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="showAxesCheckbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Show Origin</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <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>KisSliderSpinBox</class>
+ <extends>QWidget</extends>
+ <header>kis_slider_spin_box.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp
index 5bc5a98aba..eb11570a41 100644
--- a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp
+++ b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp
@@ -1,302 +1,302 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
* Copyright (C) 2007,2011 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 "ShapeResizeStrategy.h"
#include "SelectionDecorator.h"
#include <KoShapeManager.h>
#include <KoPointerEvent.h>
#include <KoCanvasBase.h>
#include <commands/KoShapeSizeCommand.h>
#include <commands/KoShapeTransformCommand.h>
#include <KoSnapGuide.h>
#include <KoToolBase.h>
#include <KoSelection.h>
#include <klocalizedstring.h>
#include <limits>
#include <math.h>
ShapeResizeStrategy::ShapeResizeStrategy(KoToolBase *tool, const QPointF &clicked, KoFlake::SelectionHandle direction)
: KoInteractionStrategy(tool)
, m_lastScale(1.0, 1.0)
{
Q_ASSERT(tool->canvas()->shapeManager()->selection()->count() > 0);
QList<KoShape *> selectedShapes = tool->canvas()->shapeManager()->selection()->selectedShapes(KoFlake::StrippedSelection);
Q_FOREACH (KoShape *shape, selectedShapes) {
if (!shape->isEditable()) {
continue;
}
m_selectedShapes << shape;
m_startPositions << shape->position();
m_oldTransforms << shape->transformation();
m_transformations << QTransform();
m_startSizes << shape->size();
}
m_start = clicked;
- KoShape *shp = 0;
+ KoShape *shape = 0;
if (tool->canvas()->shapeManager()->selection()->count() > 1) {
- shp = tool->canvas()->shapeManager()->selection();
+ shape = tool->canvas()->shapeManager()->selection();
}
if (tool->canvas()->shapeManager()->selection()->count() == 1) {
- shp = tool->canvas()->shapeManager()->selection()->firstSelectedShape();
+ shape = tool->canvas()->shapeManager()->selection()->firstSelectedShape();
}
- if (shp) {
- m_windMatrix = shp->absoluteTransformation(0);
+ if (shape) {
+ m_windMatrix = shape->absoluteTransformation(0);
m_unwindMatrix = m_windMatrix.inverted();
- m_initialSize = shp->size();
+ m_initialSize = shape->size();
m_initialPosition = m_windMatrix.map(QPointF());
- }
- switch (direction) {
- case KoFlake::TopMiddleHandle:
- m_start = 0.5 * (shp->absolutePosition(KoFlake::TopLeftCorner) + shp->absolutePosition(KoFlake::TopRightCorner));
- m_top = true; m_bottom = false; m_left = false; m_right = false; break;
- case KoFlake::TopRightHandle:
- m_start = shp->absolutePosition(KoFlake::TopRightCorner);
- m_top = true; m_bottom = false; m_left = false; m_right = true; break;
- case KoFlake::RightMiddleHandle:
- m_start = 0.5 * (shp->absolutePosition(KoFlake::TopRightCorner) + shp->absolutePosition(KoFlake::BottomRightCorner));
- m_top = false; m_bottom = false; m_left = false; m_right = true; break;
- case KoFlake::BottomRightHandle:
- m_start = shp->absolutePosition(KoFlake::BottomRightCorner);
- m_top = false; m_bottom = true; m_left = false; m_right = true; break;
- case KoFlake::BottomMiddleHandle:
- m_start = 0.5 * (shp->absolutePosition(KoFlake::BottomRightCorner) + shp->absolutePosition(KoFlake::BottomLeftCorner));
- m_top = false; m_bottom = true; m_left = false; m_right = false; break;
- case KoFlake::BottomLeftHandle:
- m_start = shp->absolutePosition(KoFlake::BottomLeftCorner);
- m_top = false; m_bottom = true; m_left = true; m_right = false; break;
- case KoFlake::LeftMiddleHandle:
- m_start = 0.5 * (shp->absolutePosition(KoFlake::BottomLeftCorner) + shp->absolutePosition(KoFlake::TopLeftCorner));
- m_top = false; m_bottom = false; m_left = true; m_right = false; break;
- case KoFlake::TopLeftHandle:
- m_start = shp->absolutePosition(KoFlake::TopLeftCorner);
- m_top = true; m_bottom = false; m_left = true; m_right = false; break;
- default:
- Q_ASSERT(0); // illegal 'corner'
+ switch (direction) {
+ case KoFlake::TopMiddleHandle:
+ m_start = 0.5 * (shape->absolutePosition(KoFlake::TopLeftCorner) + shape->absolutePosition(KoFlake::TopRightCorner));
+ m_top = true; m_bottom = false; m_left = false; m_right = false; break;
+ case KoFlake::TopRightHandle:
+ m_start = shape->absolutePosition(KoFlake::TopRightCorner);
+ m_top = true; m_bottom = false; m_left = false; m_right = true; break;
+ case KoFlake::RightMiddleHandle:
+ m_start = 0.5 * (shape->absolutePosition(KoFlake::TopRightCorner) + shape->absolutePosition(KoFlake::BottomRightCorner));
+ m_top = false; m_bottom = false; m_left = false; m_right = true; break;
+ case KoFlake::BottomRightHandle:
+ m_start = shape->absolutePosition(KoFlake::BottomRightCorner);
+ m_top = false; m_bottom = true; m_left = false; m_right = true; break;
+ case KoFlake::BottomMiddleHandle:
+ m_start = 0.5 * (shape->absolutePosition(KoFlake::BottomRightCorner) + shape->absolutePosition(KoFlake::BottomLeftCorner));
+ m_top = false; m_bottom = true; m_left = false; m_right = false; break;
+ case KoFlake::BottomLeftHandle:
+ m_start = shape->absolutePosition(KoFlake::BottomLeftCorner);
+ m_top = false; m_bottom = true; m_left = true; m_right = false; break;
+ case KoFlake::LeftMiddleHandle:
+ m_start = 0.5 * (shape->absolutePosition(KoFlake::BottomLeftCorner) + shape->absolutePosition(KoFlake::TopLeftCorner));
+ m_top = false; m_bottom = false; m_left = true; m_right = false; break;
+ case KoFlake::TopLeftHandle:
+ m_start = shape->absolutePosition(KoFlake::TopLeftCorner);
+ m_top = true; m_bottom = false; m_left = true; m_right = false; break;
+ default:
+ Q_ASSERT(0); // illegal 'corner'
+ }
}
tool->setStatusText(i18n("Press CTRL to resize from center."));
}
void ShapeResizeStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers)
{
tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect());
QPointF newPos = tool()->canvas()->snapGuide()->snap(point, modifiers);
tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect());
bool keepAspect = modifiers & Qt::ShiftModifier;
Q_FOREACH (KoShape *shape, m_selectedShapes) {
keepAspect = keepAspect || shape->keepAspectRatio();
}
qreal startWidth = m_initialSize.width();
if (startWidth < std::numeric_limits<qreal>::epsilon()) {
startWidth = std::numeric_limits<qreal>::epsilon();
}
qreal startHeight = m_initialSize.height();
if (startHeight < std::numeric_limits<qreal>::epsilon()) {
startHeight = std::numeric_limits<qreal>::epsilon();
}
QPointF distance = m_unwindMatrix.map(newPos) - m_unwindMatrix.map(m_start);
// guard against resizing zero width shapes, which would result in huge zoom factors
if (m_initialSize.width() < std::numeric_limits<qreal>::epsilon()) {
distance.rx() = 0.0;
}
// guard against resizing zero height shapes, which would result in huge zoom factors
if (m_initialSize.height() < std::numeric_limits<qreal>::epsilon()) {
distance.ry() = 0.0;
}
const bool scaleFromCenter = modifiers & Qt::ControlModifier;
if (scaleFromCenter) {
distance *= 2.0;
}
qreal newWidth = startWidth;
qreal newHeight = startHeight;
if (m_left) {
newWidth = startWidth - distance.x();
} else if (m_right) {
newWidth = startWidth + distance.x();
}
if (m_top) {
newHeight = startHeight - distance.y();
} else if (m_bottom) {
newHeight = startHeight + distance.y();
}
/**
* Do not let a shape be less than 1px in size in current view
* coordinates. If the user wants it to be smaller, he can just
* zoom-in a bit.
*/
QSizeF minViewSize(1.0, 1.0);
QSizeF minDocSize = tool()->canvas()->viewConverter()->viewToDocument(minViewSize);
if (qAbs(newWidth) < minDocSize.width()) {
int sign = newWidth >= 0.0 ? 1 : -1; // zero -> '1'
newWidth = sign * minDocSize.width();
}
if (qAbs(newHeight) < minDocSize.height()) {
int sign = newHeight >= 0.0 ? 1 : -1; // zero -> '1'
newHeight = sign * minDocSize.height();
}
qreal zoomX = newWidth / startWidth;
qreal zoomY = newHeight / startHeight;
if (keepAspect) {
const bool cornerUsed = ((m_bottom ? 1 : 0) + (m_top ? 1 : 0) + (m_left ? 1 : 0) + (m_right ? 1 : 0)) == 2;
if ((cornerUsed && startWidth < startHeight) || m_left || m_right) {
zoomY = zoomX;
} else {
zoomX = zoomY;
}
}
QPointF move;
if (scaleFromCenter) {
move = QPointF(startWidth / 2.0, startHeight / 2.0);
} else {
move = QPointF(m_left ? startWidth : 0, m_top ? startHeight : 0);
}
resizeBy(move, zoomX, zoomY);
}
void ShapeResizeStrategy::handleCustomEvent(KoPointerEvent *event)
{
QPointF center = 0.5 * QPointF(m_initialSize.width(), m_initialSize.height());
qreal zoom = pow(1.01, -0.1 * event->z());
m_lastScale *= zoom;
resizeBy(center, m_lastScale.x(), m_lastScale.y());
}
void ShapeResizeStrategy::resizeBy(const QPointF &center, qreal zoomX, qreal zoomY)
{
QTransform matrix;
matrix.translate(center.x(), center.y()); // translate to
matrix.scale(zoomX, zoomY);
matrix.translate(-center.x(), -center.y()); // and back
// that is the transformation we want to apply to the shapes
matrix = m_unwindMatrix * matrix * m_windMatrix;
// the resizing transformation without the mirroring part
QTransform resizeMatrix;
resizeMatrix.translate(center.x(), center.y()); // translate to
resizeMatrix.scale(qAbs(zoomX), qAbs(zoomY));
resizeMatrix.translate(-center.x(), -center.y()); // and back
// the mirroring part of the resizing transformation
QTransform mirrorMatrix;
mirrorMatrix.translate(center.x(), center.y()); // translate to
mirrorMatrix.scale(zoomX < 0 ? -1 : 1, zoomY < 0 ? -1 : 1);
mirrorMatrix.translate(-center.x(), -center.y()); // and back
int i = 0;
Q_FOREACH (KoShape *shape, m_selectedShapes) {
shape->update();
// this uses resize for the zooming part
shape->applyAbsoluteTransformation(m_unwindMatrix);
/*
normally we would just apply the resizeMatrix now and be done with it, but
we want to resize instead of scale, so we have to separate the scaling part
of that transformation which can then be used to resize
*/
// undo the last resize transformation
shape->applyAbsoluteTransformation(m_transformations[i].inverted());
// save the shapes transformation matrix
QTransform shapeMatrix = shape->absoluteTransformation(0);
// calculate the matrix we would apply to the local shape matrix
// that tells us the effective scale values we have to use for the resizing
QTransform localMatrix = shapeMatrix * resizeMatrix * shapeMatrix.inverted();
// save the effective scale values
qreal scaleX = localMatrix.m11();
qreal scaleY = localMatrix.m22();
// calculate the scale matrix which is equivalent to our resizing above
QTransform scaleMatrix = (QTransform().scale(scaleX, scaleY));
scaleMatrix = shapeMatrix.inverted() * scaleMatrix * shapeMatrix;
// calculate the new size of the shape, using the effective scale values
QSizeF size(scaleX * m_startSizes[i].width(), scaleY * m_startSizes[i].height());
// apply the transformation
shape->setSize(size);
// apply the rest of the transformation without the resizing part
shape->applyAbsoluteTransformation(scaleMatrix.inverted() * resizeMatrix);
shape->applyAbsoluteTransformation(mirrorMatrix);
// and remember the applied transformation later for later undoing
m_transformations[i] = shapeMatrix.inverted() * shape->absoluteTransformation(0);
shape->applyAbsoluteTransformation(m_windMatrix);
shape->update();
i++;
}
tool()->canvas()->shapeManager()->selection()->applyAbsoluteTransformation(matrix * m_scaleMatrix.inverted());
m_scaleMatrix = matrix;
}
KUndo2Command *ShapeResizeStrategy::createCommand()
{
tool()->canvas()->snapGuide()->reset();
QList<QSizeF> newSizes;
QList<QTransform> transformations;
const int shapeCount = m_selectedShapes.count();
for (int i = 0; i < shapeCount; ++i) {
newSizes << m_selectedShapes[i]->size();
transformations << m_selectedShapes[i]->transformation();
}
KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Resize"));
new KoShapeSizeCommand(m_selectedShapes, m_startSizes, newSizes, cmd);
new KoShapeTransformCommand(m_selectedShapes, m_oldTransforms, transformations, cmd);
return cmd;
}
void ShapeResizeStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
{
Q_UNUSED(modifiers);
tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect());
}
void ShapeResizeStrategy::paint(QPainter &painter, const KoViewConverter &converter)
{
SelectionDecorator decorator(KoFlake::NoHandle, false, false);
decorator.setSelection(tool()->canvas()->shapeManager()->selection());
decorator.setHandleRadius(handleRadius());
decorator.paint(painter, converter);
}
diff --git a/plugins/tools/tool_crop/kis_tool_crop.cc b/plugins/tools/tool_crop/kis_tool_crop.cc
index 1f90661ee7..57d15c57f7 100644
--- a/plugins/tools/tool_crop/kis_tool_crop.cc
+++ b/plugins/tools/tool_crop/kis_tool_crop.cc
@@ -1,872 +1,872 @@
/*
* kis_tool_crop.cc -- part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (C) 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 "kis_tool_crop.h"
#include "kistoolcropconfigwidget.h"
#include <QCheckBox>
#include <QComboBox>
#include <QObject>
#include <QPainter>
#include <QPen>
#include <QPushButton>
#include <QRect>
#include <QVector>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <ksharedconfig.h>
#include <KoCanvasBase.h>
#include <kis_global.h>
#include <kis_painter.h>
#include <kis_cursor.h>
#include <kis_image.h>
#include <kis_undo_adapter.h>
#include <KoPointerEvent.h>
#include <kis_selection.h>
#include <kis_layer.h>
#include <kis_canvas2.h>
#include <KisViewManager.h>
#include <kis_floating_message.h>
#include <kis_group_layer.h>
#include <kis_resources_snapshot.h>
#include <kundo2command.h>
#include <kis_crop_saved_extra_data.h>
struct DecorationLine
{
QPointF start;
QPointF end;
enum Relation
{
Width,
Height,
Smallest,
Largest
};
Relation startXRelation;
Relation startYRelation;
Relation endXRelation;
Relation endYRelation;
};
DecorationLine decors[20] =
{
//thirds
{QPointF(0.0, 0.3333),QPointF(1.0, 0.3333), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.0, 0.6666),QPointF(1.0, 0.6666), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.3333, 0.0),QPointF(0.3333, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.6666, 0.0),QPointF(0.6666, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
//fifths
{QPointF(0.0, 0.2),QPointF(1.0, 0.2), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.0, 0.4),QPointF(1.0, 0.4), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.0, 0.6),QPointF(1.0, 0.6), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.0, 0.8),QPointF(1.0, 0.8), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.2, 0.0),QPointF(0.2, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.4, 0.0),QPointF(0.4, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.6, 0.0),QPointF(0.6, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.8, 0.0),QPointF(0.8, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
// Passport photo
{QPointF(0.0, 0.45/0.35),QPointF(1.0, 0.45/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
{QPointF(0.2, 0.05/0.35),QPointF(0.8, 0.05/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
{QPointF(0.2, 0.40/0.35),QPointF(0.8, 0.40/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
{QPointF(0.25, 0.07/0.35),QPointF(0.75, 0.07/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
{QPointF(0.25, 0.38/0.35),QPointF(0.75, 0.38/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
{QPointF(0.35/0.45, 0.0),QPointF(0.35/0.45, 1.0), DecorationLine::Height, DecorationLine::Height, DecorationLine::Height, DecorationLine::Height},
//Crosshair
{QPointF(0.0, 0.5),QPointF(1.0, 0.5), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
{QPointF(0.5, 0.0),QPointF(0.5, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height}
};
#define DECORATION_COUNT 5
const int decorsIndex[DECORATION_COUNT] = {0,4,12,18,20};
KisToolCrop::KisToolCrop(KoCanvasBase * canvas)
: KisTool(canvas, KisCursor::load("tool_crop_cursor.png", 6, 6))
{
setObjectName("tool_crop");
m_handleSize = 13;
m_haveCropSelection = false;
m_cropTypeSelectable = false;
m_cropType = ImageCropType;
m_decoration = 1;
connect(&m_finalRect, SIGNAL(sigValuesChanged()), SLOT(slotRectChanged()));
connect(&m_finalRect, SIGNAL(sigLockValuesChanged()), SLOT(slotRectChanged()));
}
KisToolCrop::~KisToolCrop()
{
}
void KisToolCrop::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
KisTool::activate(toolActivation, shapes);
configGroup = KSharedConfig::openConfig()->group(toolId()); // save settings to kritarc
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(image(), currentNode(), 0, this->canvas()->resourceManager());
+ new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
// load settings from configuration
setGrowCenter(configGroup.readEntry("growCenter", false));
setAllowGrow(configGroup.readEntry("allowGrow", false));
// Default: thirds decoration
setDecoration(configGroup.readEntry("decoration", 1));
// Default: crop the entire image
setCropType(configGroup.readEntry("cropType", 1) == 0 ? LayerCropType : ImageCropType);
m_finalRect.setCropRect(image()->bounds());
KisSelectionSP sel = resources->activeSelection();
if (sel) {
m_haveCropSelection = true;
m_finalRect.setRectInitial(sel->selectedExactRect());
}
useCursor(cursor());
//pixel layer
if(resources->currentNode() && resources->currentNode()->paintDevice()) {
setCropTypeSelectable(true);
}
//vector layer
else {
setCropTypeSelectable(false);
}
}
void KisToolCrop::cancelStroke()
{
m_haveCropSelection = false;
doCanvasUpdate(image()->bounds());
}
void KisToolCrop::deactivate()
{
cancelStroke();
KisTool::deactivate();
}
void KisToolCrop::requestStrokeEnd()
{
if (m_haveCropSelection) crop();
}
void KisToolCrop::requestStrokeCancellation()
{
cancelStroke();
}
void KisToolCrop::canvasResourceChanged(int key, const QVariant &res)
{
KisTool::canvasResourceChanged(key, res);
//pixel layer
if(currentNode() && currentNode()->paintDevice()) {
setCropTypeSelectable(true);
}
//vector layer
else {
setCropType(ImageCropType);
setCropTypeSelectable(false);
}
}
void KisToolCrop::paint(QPainter &painter, const KoViewConverter &converter)
{
Q_UNUSED(converter);
paintOutlineWithHandles(painter);
}
void KisToolCrop::beginPrimaryAction(KoPointerEvent *event)
{
m_finalRect.setCropRect(image()->bounds());
setMode(KisTool::PAINT_MODE);
const QPointF imagePoint = convertToPixelCoord(event);
m_mouseOnHandleType = mouseOnHandle(pixelToView(imagePoint));
if (m_mouseOnHandleType != KisConstrainedRect::None) {
QPointF snapPoint = m_finalRect.handleSnapPoint(KisConstrainedRect::HandleType(m_mouseOnHandleType), imagePoint);
QPointF snapDocPoint = image()->pixelToDocument(snapPoint);
m_dragOffsetDoc = snapDocPoint - event->point;
} else {
m_dragOffsetDoc = QPointF();
}
QPointF snappedPoint = convertToPixelCoordAndSnap(event, m_dragOffsetDoc);
m_dragStart = snappedPoint.toPoint();
m_resettingStroke = false;
if (!m_haveCropSelection || m_mouseOnHandleType == None) {
m_lastCanvasUpdateRect = image()->bounds();
const int initialWidth = m_finalRect.widthLocked() ? m_finalRect.rect().width() : 1;
const int initialHeight = m_finalRect.heightLocked() ? m_finalRect.rect().height() : 1;
const QRect initialRect = QRect(m_dragStart, QSize(initialWidth, initialHeight));
m_finalRect.setRectInitial(initialRect);
m_initialDragRect = initialRect;
m_mouseOnHandleType = KisConstrainedRect::Creation;
m_resettingStroke = true;
} else {
m_initialDragRect = m_finalRect.rect();
}
}
void KisToolCrop::continuePrimaryAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
const QPointF pos = convertToPixelCoordAndSnap(event, m_dragOffsetDoc);
const QPoint drag = pos.toPoint() - m_dragStart;
m_finalRect.moveHandle(KisConstrainedRect::HandleType(m_mouseOnHandleType), drag, m_initialDragRect);
}
bool KisToolCrop::tryContinueLastCropAction()
{
bool result = false;
const KUndo2Command *lastCommand = image()->undoAdapter()->presentCommand();
const KisCropSavedExtraData *data = 0;
if ((lastCommand = image()->undoAdapter()->presentCommand()) &&
(data = dynamic_cast<const KisCropSavedExtraData*>(lastCommand->extraData()))) {
bool cropImageConsistent =
m_cropType == ImageCropType &&
(data->type() == KisCropSavedExtraData::CROP_IMAGE ||
data->type() == KisCropSavedExtraData::RESIZE_IMAGE);
bool cropLayerConsistent =
m_cropType == LayerCropType &&
data->type() == KisCropSavedExtraData::CROP_LAYER &&
currentNode() == data->cropNode();
if (cropImageConsistent || cropLayerConsistent) {
image()->undoAdapter()->undoLastCommand();
image()->waitForDone();
m_finalRect.setRectInitial(data->cropRect());
m_haveCropSelection = true;
result = true;
}
}
return result;
}
void KisToolCrop::endPrimaryAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
setMode(KisTool::HOVER_MODE);
QRectF viewCropRect = pixelToView(m_finalRect.rect());
const bool haveValidRect =
viewCropRect.width() > m_handleSize &&
viewCropRect.height() > m_handleSize;
if (!m_haveCropSelection && !haveValidRect) {
if (!tryContinueLastCropAction()) {
m_finalRect.setRectInitial(image()->bounds());
m_haveCropSelection = true;
}
} else if (m_resettingStroke && !haveValidRect) {
m_lastCanvasUpdateRect = image()->bounds();
m_haveCropSelection = false;
} else {
m_haveCropSelection = true;
}
m_finalRect.normalize();
qint32 type = mouseOnHandle(pixelToView(convertToPixelCoordAndSnap(event, m_dragOffsetDoc)));
setMoveResizeCursor(type);
}
void KisToolCrop::mouseMoveEvent(KoPointerEvent *event)
{
QPointF pos = convertToPixelCoordAndSnap(event);
if (m_haveCropSelection) { //if the crop selection is set
//set resize cursor if we are on one of the handles
if(mode() == KisTool::PAINT_MODE) {
//keep the same cursor as the one we clicked with
setMoveResizeCursor(m_mouseOnHandleType);
}else{
//hovering
qint32 type = mouseOnHandle(pixelToView(pos));
setMoveResizeCursor(type);
}
}
}
void KisToolCrop::beginPrimaryDoubleClickAction(KoPointerEvent *event)
{
if (m_haveCropSelection) crop();
// this action will have no continuation
event->ignore();
}
#define BORDER_LINE_WIDTH 0
#define HALF_BORDER_LINE_WIDTH 0
#define HANDLE_BORDER_LINE_WIDTH 1
QRectF KisToolCrop::borderLineRect()
{
QRectF borderRect = pixelToView(m_finalRect.rect());
// Draw the border line right next to the crop rectangle perimeter.
borderRect.adjust(-HALF_BORDER_LINE_WIDTH, -HALF_BORDER_LINE_WIDTH, HALF_BORDER_LINE_WIDTH, HALF_BORDER_LINE_WIDTH);
return borderRect;
}
#define OUTSIDE_CROP_ALPHA 200
void KisToolCrop::paintOutlineWithHandles(QPainter& gc)
{
if (canvas() && (mode() == KisTool::PAINT_MODE || m_haveCropSelection)) {
gc.save();
QRectF wholeImageRect = pixelToView(image()->bounds());
QRectF borderRect = borderLineRect();
QPainterPath path;
path.addRect(wholeImageRect);
path.addRect(borderRect);
gc.setPen(Qt::NoPen);
gc.setBrush(QColor(0, 0, 0, OUTSIDE_CROP_ALPHA));
gc.drawPath(path);
// Handles
QPen pen(Qt::SolidLine);
pen.setWidth(HANDLE_BORDER_LINE_WIDTH);
pen.setColor(Qt::black);
gc.setPen(pen);
gc.setBrush(QColor(200, 200, 200, OUTSIDE_CROP_ALPHA));
gc.drawPath(handlesPath());
gc.setClipRect(borderRect, Qt::IntersectClip);
if (m_decoration > 0) {
for (int i = decorsIndex[m_decoration-1]; i<decorsIndex[m_decoration]; i++) {
drawDecorationLine(&gc, &(decors[i]), borderRect);
}
}
gc.restore();
}
}
void KisToolCrop::crop()
{
KIS_ASSERT_RECOVER_RETURN(currentImage());
if (m_finalRect.rect().isEmpty()) return;
if (m_cropType == LayerCropType) {
//Cropping layer
if (!nodeEditable()) {
return;
}
}
m_haveCropSelection = false;
useCursor(cursor());
QRect cropRect = m_finalRect.rect();
// The visitor adds the undo steps to the macro
if (m_cropType == LayerCropType && currentNode()->paintDevice()) {
currentImage()->cropNode(currentNode(), cropRect);
} else {
currentImage()->cropImage(cropRect);
}
}
void KisToolCrop::setCropTypeLegacy(int cropType)
{
setCropType(cropType == 0 ? LayerCropType : ImageCropType);
}
void KisToolCrop::setCropType(KisToolCrop::CropToolType cropType)
{
if(m_cropType == cropType)
return;
m_cropType = cropType;
// can't save LayerCropType, so have to convert it to int for saving
configGroup.writeEntry("cropType", cropType == LayerCropType ? 0 : 1);
emit cropTypeChanged(m_cropType);
}
KisToolCrop::CropToolType KisToolCrop::cropType() const
{
return m_cropType;
}
void KisToolCrop::setCropTypeSelectable(bool selectable)
{
if(selectable == m_cropTypeSelectable)
return;
m_cropTypeSelectable = selectable;
emit cropTypeSelectableChanged();
}
bool KisToolCrop::cropTypeSelectable() const
{
return m_cropTypeSelectable;
}
int KisToolCrop::decoration() const
{
return m_decoration;
}
void KisToolCrop::setDecoration(int i)
{
// This shouldn't happen, but safety first
if(i < 0 || i > DECORATION_COUNT)
return;
m_decoration = i;
emit decorationChanged(decoration());
updateCanvasViewRect(boundingRect());
configGroup.writeEntry("decoration", i);
}
void KisToolCrop::doCanvasUpdate(const QRect &updateRect)
{
updateCanvasViewRect(updateRect | m_lastCanvasUpdateRect);
m_lastCanvasUpdateRect = updateRect;
}
void KisToolCrop::slotRectChanged()
{
emit cropHeightChanged(cropHeight());
emit cropWidthChanged(cropWidth());
emit cropXChanged(cropX());
emit cropYChanged(cropY());
emit ratioChanged(ratio());
emit forceHeightChanged(forceHeight());
emit forceWidthChanged(forceWidth());
emit forceRatioChanged(forceRatio());
emit canGrowChanged(allowGrow());
emit isCenteredChanged(growCenter());
doCanvasUpdate(boundingRect().toAlignedRect());
}
void KisToolCrop::setCropX(int x)
{
if(x == m_finalRect.rect().x())
return;
if (!m_haveCropSelection) {
m_haveCropSelection = true;
m_finalRect.setRectInitial(image()->bounds());
}
QPoint offset = m_finalRect.rect().topLeft();
offset.setX(x);
m_finalRect.setOffset(offset);
}
int KisToolCrop::cropX() const
{
return m_finalRect.rect().x();
}
void KisToolCrop::setCropY(int y)
{
if(y == m_finalRect.rect().y())
return;
if (!m_haveCropSelection) {
m_haveCropSelection = true;
m_finalRect.setRectInitial(image()->bounds());
}
QPoint offset = m_finalRect.rect().topLeft();
offset.setY(y);
m_finalRect.setOffset(offset);
}
int KisToolCrop::cropY() const
{
return m_finalRect.rect().y();
}
void KisToolCrop::setCropWidth(int w)
{
if(w == m_finalRect.rect().width())
return;
if (!m_haveCropSelection) {
m_haveCropSelection = true;
m_finalRect.setRectInitial(image()->bounds());
}
m_finalRect.setWidth(w);
}
int KisToolCrop::cropWidth() const
{
return m_finalRect.rect().width();
}
void KisToolCrop::setForceWidth(bool force)
{
m_finalRect.setWidthLocked(force);
}
bool KisToolCrop::forceWidth() const
{
return m_finalRect.widthLocked();
}
void KisToolCrop::setCropHeight(int h)
{
if(h == m_finalRect.rect().height())
return;
if (!m_haveCropSelection) {
m_haveCropSelection = true;
m_finalRect.setRectInitial(image()->bounds());
}
m_finalRect.setHeight(h);
}
int KisToolCrop::cropHeight() const
{
return m_finalRect.rect().height();
}
void KisToolCrop::setForceHeight(bool force)
{
m_finalRect.setHeightLocked(force);
}
bool KisToolCrop::forceHeight() const
{
return m_finalRect.heightLocked();
}
void KisToolCrop::setAllowGrow(bool g)
{
m_finalRect.setCanGrow(g);
m_finalRect.setCropRect(image()->bounds());
configGroup.writeEntry("allowGrow", g);
}
bool KisToolCrop::allowGrow() const
{
return m_finalRect.canGrow();
}
void KisToolCrop::setGrowCenter(bool value)
{
m_finalRect.setCentered(value);
configGroup.writeEntry("growCenter", value);
}
bool KisToolCrop::growCenter() const
{
return m_finalRect.centered();
}
void KisToolCrop::setRatio(double ratio)
{
if(ratio == m_finalRect.ratio())
return;
if (!m_haveCropSelection) {
m_haveCropSelection = true;
m_finalRect.setRectInitial(image()->bounds());
}
m_finalRect.setRatio(ratio);
}
double KisToolCrop::ratio() const
{
return m_finalRect.ratio();
}
void KisToolCrop::setForceRatio(bool force)
{
m_finalRect.setRatioLocked(force);
}
bool KisToolCrop::forceRatio() const
{
return m_finalRect.ratioLocked();
}
QWidget* KisToolCrop::createOptionWidget()
{
KisToolCropConfigWidget* optionsWidget = new KisToolCropConfigWidget(0, this);
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(optionsWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
optionsWidget->layout()->addWidget(specialSpacer);
Q_CHECK_PTR(optionsWidget);
optionsWidget->setObjectName(toolId() + " option widget");
connect(optionsWidget->bnCrop, SIGNAL(clicked()), this, SLOT(crop()));
connect(optionsWidget, SIGNAL(cropTypeChanged(int)), this, SLOT(setCropTypeLegacy(int)));
connect(optionsWidget, SIGNAL(cropXChanged(int)), this, SLOT(setCropX(int)));
connect(optionsWidget, SIGNAL(cropYChanged(int)), this, SLOT(setCropY(int)));
connect(optionsWidget, SIGNAL(cropHeightChanged(int)), this, SLOT(setCropHeight(int)));
connect(optionsWidget, SIGNAL(forceHeightChanged(bool)), this, SLOT(setForceHeight(bool)));
connect(optionsWidget, SIGNAL(cropWidthChanged(int)), this, SLOT(setCropWidth(int)));
connect(optionsWidget, SIGNAL(forceWidthChanged(bool)), this, SLOT(setForceWidth(bool)));
connect(optionsWidget, SIGNAL(ratioChanged(double)), this, SLOT(setRatio(double)));
connect(optionsWidget, SIGNAL(forceRatioChanged(bool)), this, SLOT(setForceRatio(bool)));
connect(optionsWidget, SIGNAL(decorationChanged(int)), this, SLOT(setDecoration(int)));
connect(optionsWidget, SIGNAL(allowGrowChanged(bool)), this, SLOT(setAllowGrow(bool)));
connect(optionsWidget, SIGNAL(growCenterChanged(bool)), this, SLOT(setGrowCenter(bool)));
optionsWidget->setFixedHeight(optionsWidget->sizeHint().height());
return optionsWidget;
}
QRectF KisToolCrop::lowerRightHandleRect(QRectF cropBorderRect)
{
return QRectF(cropBorderRect.right() - m_handleSize / 2.0, cropBorderRect.bottom() - m_handleSize / 2.0, m_handleSize, m_handleSize);
}
QRectF KisToolCrop::upperRightHandleRect(QRectF cropBorderRect)
{
return QRectF(cropBorderRect.right() - m_handleSize / 2.0 , cropBorderRect.top() - m_handleSize / 2.0, m_handleSize, m_handleSize);
}
QRectF KisToolCrop::lowerLeftHandleRect(QRectF cropBorderRect)
{
return QRectF(cropBorderRect.left() - m_handleSize / 2.0 , cropBorderRect.bottom() - m_handleSize / 2.0, m_handleSize, m_handleSize);
}
QRectF KisToolCrop::upperLeftHandleRect(QRectF cropBorderRect)
{
return QRectF(cropBorderRect.left() - m_handleSize / 2.0, cropBorderRect.top() - m_handleSize / 2.0, m_handleSize, m_handleSize);
}
QRectF KisToolCrop::lowerHandleRect(QRectF cropBorderRect)
{
return QRectF(cropBorderRect.left() + (cropBorderRect.width() - m_handleSize) / 2.0 , cropBorderRect.bottom() - m_handleSize / 2.0, m_handleSize, m_handleSize);
}
QRectF KisToolCrop::rightHandleRect(QRectF cropBorderRect)
{
return QRectF(cropBorderRect.right() - m_handleSize / 2.0 , cropBorderRect.top() + (cropBorderRect.height() - m_handleSize) / 2.0, m_handleSize, m_handleSize);
}
QRectF KisToolCrop::upperHandleRect(QRectF cropBorderRect)
{
return QRectF(cropBorderRect.left() + (cropBorderRect.width() - m_handleSize) / 2.0 , cropBorderRect.top() - m_handleSize / 2.0, m_handleSize, m_handleSize);
}
QRectF KisToolCrop::leftHandleRect(QRectF cropBorderRect)
{
return QRectF(cropBorderRect.left() - m_handleSize / 2.0, cropBorderRect.top() + (cropBorderRect.height() - m_handleSize) / 2.0, m_handleSize, m_handleSize);
}
QPainterPath KisToolCrop::handlesPath()
{
QRectF cropBorderRect = borderLineRect();
QPainterPath path;
path.addRect(upperLeftHandleRect(cropBorderRect));
path.addRect(upperRightHandleRect(cropBorderRect));
path.addRect(lowerLeftHandleRect(cropBorderRect));
path.addRect(lowerRightHandleRect(cropBorderRect));
path.addRect(upperHandleRect(cropBorderRect));
path.addRect(lowerHandleRect(cropBorderRect));
path.addRect(leftHandleRect(cropBorderRect));
path.addRect(rightHandleRect(cropBorderRect));
return path;
}
qint32 KisToolCrop::mouseOnHandle(QPointF currentViewPoint)
{
QRectF borderRect = borderLineRect();
qint32 handleType = None;
if (!m_haveCropSelection) {
return None;
}
if (upperLeftHandleRect(borderRect).contains(currentViewPoint)) {
handleType = UpperLeft;
} else if (lowerLeftHandleRect(borderRect).contains(currentViewPoint)) {
handleType = LowerLeft;
} else if (upperRightHandleRect(borderRect).contains(currentViewPoint)) {
handleType = UpperRight;
} else if (lowerRightHandleRect(borderRect).contains(currentViewPoint)) {
handleType = LowerRight;
} else if (upperHandleRect(borderRect).contains(currentViewPoint)) {
handleType = Upper;
} else if (lowerHandleRect(borderRect).contains(currentViewPoint)) {
handleType = Lower;
} else if (leftHandleRect(borderRect).contains(currentViewPoint)) {
handleType = Left;
} else if (rightHandleRect(borderRect).contains(currentViewPoint)) {
handleType = Right;
} else if (borderRect.contains(currentViewPoint)) {
handleType = Inside;
}
return handleType;
}
void KisToolCrop::setMoveResizeCursor(qint32 handle)
{
QCursor cursor;
switch (handle) {
case(UpperLeft):
case(LowerRight):
cursor = KisCursor::sizeFDiagCursor();
break;
case(LowerLeft):
case(UpperRight):
cursor = KisCursor::sizeBDiagCursor();
break;
case(Upper):
case(Lower):
cursor = KisCursor::sizeVerCursor();
break;
case(Left):
case(Right):
cursor = KisCursor::sizeHorCursor();
break;
case(Inside):
cursor = KisCursor::sizeAllCursor();
break;
default:
cursor = KisCursor::arrowCursor();
break;
}
useCursor(cursor);
}
QRectF KisToolCrop::boundingRect()
{
QRectF rect = handlesPath().boundingRect();
rect.adjust(-HANDLE_BORDER_LINE_WIDTH, -HANDLE_BORDER_LINE_WIDTH, HANDLE_BORDER_LINE_WIDTH, HANDLE_BORDER_LINE_WIDTH);
return rect;
}
void KisToolCrop::drawDecorationLine(QPainter *p, DecorationLine *decorLine, const QRectF rect)
{
QPointF start = rect.topLeft();
QPointF end = rect.topLeft();
qreal small = qMin(rect.width(), rect.height());
qreal large = qMax(rect.width(), rect.height());
switch (decorLine->startXRelation) {
case DecorationLine::Width:
start.setX(start.x() + decorLine->start.x() * rect.width());
break;
case DecorationLine::Height:
start.setX(start.x() + decorLine->start.x() * rect.height());
break;
case DecorationLine::Smallest:
start.setX(start.x() + decorLine->start.x() * small);
break;
case DecorationLine::Largest:
start.setX(start.x() + decorLine->start.x() * large);
break;
}
switch (decorLine->startYRelation) {
case DecorationLine::Width:
start.setY(start.y() + decorLine->start.y() * rect.width());
break;
case DecorationLine::Height:
start.setY(start.y() + decorLine->start.y() * rect.height());
break;
case DecorationLine::Smallest:
start.setY(start.y() + decorLine->start.y() * small);
break;
case DecorationLine::Largest:
start.setY(start.y() + decorLine->start.y() * large);
break;
}
switch (decorLine->endXRelation) {
case DecorationLine::Width:
end.setX(end.x() + decorLine->end.x() * rect.width());
break;
case DecorationLine::Height:
end.setX(end.x() + decorLine->end.x() * rect.height());
break;
case DecorationLine::Smallest:
end.setX(end.x() + decorLine->end.x() * small);
break;
case DecorationLine::Largest:
end.setX(end.x() + decorLine->end.x() * large);
break;
}
switch (decorLine->endYRelation) {
case DecorationLine::Width:
end.setY(end.y() + decorLine->end.y() * rect.width());
break;
case DecorationLine::Height:
end.setY(end.y() + decorLine->end.y() * rect.height());
break;
case DecorationLine::Smallest:
end.setY(end.y() + decorLine->end.y() * small);
break;
case DecorationLine::Largest:
end.setY(end.y() + decorLine->end.y() * large);
break;
}
p->drawLine(start, end);
}
diff --git a/plugins/tools/tool_transform2/kis_free_transform_strategy.cpp b/plugins/tools/tool_transform2/kis_free_transform_strategy.cpp
index 9f7b1cc6c7..a8492f0cff 100644
--- a/plugins/tools/tool_transform2/kis_free_transform_strategy.cpp
+++ b/plugins/tools/tool_transform2/kis_free_transform_strategy.cpp
@@ -1,697 +1,696 @@
/*
* 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.h"
#include <QPointF>
#include <QPainter>
#include <QMatrix4x4>
#include <KoResourcePaths.h>
#include "kis_coordinates_converter.h"
#include "tool_transform_args.h"
#include "transform_transaction_properties.h"
#include "krita_utils.h"
#include "kis_cursor.h"
#include "kis_transform_utils.h"
#include "kis_free_transform_strategy_gsl_helpers.h"
enum StrokeFunction {
ROTATE = 0,
MOVE,
RIGHTSCALE,
TOPRIGHTSCALE,
TOPSCALE,
TOPLEFTSCALE,
LEFTSCALE,
BOTTOMLEFTSCALE,
BOTTOMSCALE,
BOTTOMRIGHTSCALE,
BOTTOMSHEAR,
RIGHTSHEAR,
TOPSHEAR,
LEFTSHEAR,
MOVECENTER,
PERSPECTIVE
};
struct KisFreeTransformStrategy::Private
{
Private(KisFreeTransformStrategy *_q,
const KisCoordinatesConverter *_converter,
ToolTransformArgs &_currentArgs,
TransformTransactionProperties &_transaction)
: q(_q),
converter(_converter),
currentArgs(_currentArgs),
transaction(_transaction),
imageTooBig(false)
{
scaleCursors[0] = KisCursor::sizeHorCursor();
scaleCursors[1] = KisCursor::sizeFDiagCursor();
scaleCursors[2] = KisCursor::sizeVerCursor();
scaleCursors[3] = KisCursor::sizeBDiagCursor();
scaleCursors[4] = KisCursor::sizeHorCursor();
scaleCursors[5] = KisCursor::sizeFDiagCursor();
scaleCursors[6] = KisCursor::sizeVerCursor();
scaleCursors[7] = KisCursor::sizeBDiagCursor();
- shearCursorPixmap.load(KoResourcePaths::locate("data", "icons/cursor_shear.png"));
-
+ shearCursorPixmap.load(":/shear_cursor.png");
}
KisFreeTransformStrategy *q;
/// standard members ///
const KisCoordinatesConverter *converter;
//////
ToolTransformArgs &currentArgs;
//////
TransformTransactionProperties &transaction;
QTransform thumbToImageTransform;
QImage originalImage;
QTransform paintingTransform;
QPointF paintingOffset;
QTransform handlesTransform;
/// custom members ///
StrokeFunction function;
struct HandlePoints {
QPointF topLeft;
QPointF topMiddle;
QPointF topRight;
QPointF middleLeft;
QPointF rotationCenter;
QPointF middleRight;
QPointF bottomLeft;
QPointF bottomMiddle;
QPointF bottomRight;
};
HandlePoints transformedHandles;
QTransform transform;
QCursor scaleCursors[8]; // cursors for the 8 directions
QPixmap shearCursorPixmap;
bool imageTooBig;
ToolTransformArgs clickArgs;
QPointF clickPos;
QCursor getScaleCursor(const QPointF &handlePt);
QCursor getShearCursor(const QPointF &start, const QPointF &end);
void recalculateTransformations();
void recalculateTransformedHandles();
};
KisFreeTransformStrategy::KisFreeTransformStrategy(const KisCoordinatesConverter *converter,
KoSnapGuide *snapGuide,
ToolTransformArgs &currentArgs,
TransformTransactionProperties &transaction)
: KisSimplifiedActionPolicyStrategy(converter, snapGuide),
m_d(new Private(this, converter, currentArgs, transaction))
{
}
KisFreeTransformStrategy::~KisFreeTransformStrategy()
{
}
void KisFreeTransformStrategy::Private::recalculateTransformedHandles()
{
transformedHandles.topLeft = transform.map(transaction.originalTopLeft());
transformedHandles.topMiddle = transform.map(transaction.originalMiddleTop());
transformedHandles.topRight = transform.map(transaction.originalTopRight());
transformedHandles.middleLeft = transform.map(transaction.originalMiddleLeft());
transformedHandles.rotationCenter = transform.map(currentArgs.originalCenter() + currentArgs.rotationCenterOffset());
transformedHandles.middleRight = transform.map(transaction.originalMiddleRight());
transformedHandles.bottomLeft = transform.map(transaction.originalBottomLeft());
transformedHandles.bottomMiddle = transform.map(transaction.originalMiddleBottom());
transformedHandles.bottomRight = transform.map(transaction.originalBottomRight());
}
void KisFreeTransformStrategy::setTransformFunction(const QPointF &mousePos, bool perspectiveModifierActive)
{
if (perspectiveModifierActive) {
m_d->function = PERSPECTIVE;
return;
}
QPolygonF transformedPolygon = m_d->transform.map(QPolygonF(m_d->transaction.originalRect()));
qreal handleRadius = KisTransformUtils::effectiveHandleGrabRadius(m_d->converter);
qreal rotationHandleRadius = KisTransformUtils::effectiveHandleGrabRadius(m_d->converter);
StrokeFunction defaultFunction =
transformedPolygon.containsPoint(mousePos, Qt::OddEvenFill) ? MOVE : ROTATE;
KisTransformUtils::HandleChooser<StrokeFunction>
handleChooser(mousePos, defaultFunction);
handleChooser.addFunction(m_d->transformedHandles.topMiddle,
handleRadius, TOPSCALE);
handleChooser.addFunction(m_d->transformedHandles.topRight,
handleRadius, TOPRIGHTSCALE);
handleChooser.addFunction(m_d->transformedHandles.middleRight,
handleRadius, RIGHTSCALE);
handleChooser.addFunction(m_d->transformedHandles.bottomRight,
handleRadius, BOTTOMRIGHTSCALE);
handleChooser.addFunction(m_d->transformedHandles.bottomMiddle,
handleRadius, BOTTOMSCALE);
handleChooser.addFunction(m_d->transformedHandles.bottomLeft,
handleRadius, BOTTOMLEFTSCALE);
handleChooser.addFunction(m_d->transformedHandles.middleLeft,
handleRadius, LEFTSCALE);
handleChooser.addFunction(m_d->transformedHandles.topLeft,
handleRadius, TOPLEFTSCALE);
handleChooser.addFunction(m_d->transformedHandles.rotationCenter,
rotationHandleRadius, MOVECENTER);
m_d->function = handleChooser.function();
if (m_d->function == ROTATE || m_d->function == MOVE) {
QRectF originalRect = m_d->transaction.originalRect();
QPointF t = m_d->transform.inverted().map(mousePos);
if (t.x() >= originalRect.left() && t.x() <= originalRect.right()) {
if (fabs(t.y() - originalRect.top()) <= handleRadius)
m_d->function = TOPSHEAR;
if (fabs(t.y() - originalRect.bottom()) <= handleRadius)
m_d->function = BOTTOMSHEAR;
}
if (t.y() >= originalRect.top() && t.y() <= originalRect.bottom()) {
if (fabs(t.x() - originalRect.left()) <= handleRadius)
m_d->function = LEFTSHEAR;
if (fabs(t.x() - originalRect.right()) <= handleRadius)
m_d->function = RIGHTSHEAR;
}
}
}
QCursor KisFreeTransformStrategy::Private::getScaleCursor(const QPointF &handlePt)
{
QPointF handlePtInWidget = converter->imageToWidget(handlePt);
QPointF centerPtInWidget = converter->imageToWidget(currentArgs.transformedCenter());
QPointF direction = handlePtInWidget - centerPtInWidget;
qreal angle = atan2(direction.y(), direction.x());
angle = normalizeAngle(angle);
int octant = qRound(angle * 4. / M_PI) % 8;
return scaleCursors[octant];
}
QCursor KisFreeTransformStrategy::Private::getShearCursor(const QPointF &start, const QPointF &end)
{
QPointF startPtInWidget = converter->imageToWidget(start);
QPointF endPtInWidget = converter->imageToWidget(end);
QPointF direction = endPtInWidget - startPtInWidget;
qreal angle = atan2(-direction.y(), direction.x());
return QCursor(shearCursorPixmap.transformed(QTransform().rotateRadians(-angle)));
}
QCursor KisFreeTransformStrategy::getCurrentCursor() const
{
QCursor cursor;
switch (m_d->function) {
case MOVE:
cursor = KisCursor::moveCursor();
break;
case ROTATE:
cursor = KisCursor::rotateCursor();
break;
case PERSPECTIVE:
//TODO: find another cursor for perspective
cursor = KisCursor::rotateCursor();
break;
case RIGHTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.middleRight);
break;
case TOPSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.topMiddle);
break;
case LEFTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.middleLeft);
break;
case BOTTOMSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomMiddle);
break;
case TOPRIGHTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.topRight);
break;
case BOTTOMLEFTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomLeft);
break;
case TOPLEFTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.topLeft);
break;
case BOTTOMRIGHTSCALE:
cursor = m_d->getScaleCursor(m_d->transformedHandles.bottomRight);
break;
case MOVECENTER:
cursor = KisCursor::handCursor();
break;
case BOTTOMSHEAR:
cursor = m_d->getShearCursor(m_d->transformedHandles.bottomLeft, m_d->transformedHandles.bottomRight);
break;
case RIGHTSHEAR:
cursor = m_d->getShearCursor(m_d->transformedHandles.bottomRight, m_d->transformedHandles.topRight);
break;
case TOPSHEAR:
cursor = m_d->getShearCursor(m_d->transformedHandles.topRight, m_d->transformedHandles.topLeft);
break;
case LEFTSHEAR:
cursor = m_d->getShearCursor(m_d->transformedHandles.topLeft, m_d->transformedHandles.bottomLeft);
break;
}
return cursor;
}
void KisFreeTransformStrategy::paint(QPainter &gc)
{
gc.save();
gc.setOpacity(m_d->transaction.basePreviewOpacity());
gc.setTransform(m_d->paintingTransform, true);
gc.drawImage(m_d->paintingOffset, originalImage());
gc.restore();
// Draw Handles
QRectF handleRect =
KisTransformUtils::handleRect(KisTransformUtils::handleVisualRadius,
m_d->handlesTransform,
m_d->transaction.originalRect(), 0, 0);
qreal rX = 1;
qreal rY = 1;
QRectF rotationCenterRect =
KisTransformUtils::handleRect(KisTransformUtils::rotationHandleVisualRadius,
m_d->handlesTransform,
m_d->transaction.originalRect(),
&rX,
&rY);
QPainterPath handles;
handles.moveTo(m_d->transaction.originalTopLeft());
handles.lineTo(m_d->transaction.originalTopRight());
handles.lineTo(m_d->transaction.originalBottomRight());
handles.lineTo(m_d->transaction.originalBottomLeft());
handles.lineTo(m_d->transaction.originalTopLeft());
handles.addRect(handleRect.translated(m_d->transaction.originalTopLeft()));
handles.addRect(handleRect.translated(m_d->transaction.originalTopRight()));
handles.addRect(handleRect.translated(m_d->transaction.originalBottomLeft()));
handles.addRect(handleRect.translated(m_d->transaction.originalBottomRight()));
handles.addRect(handleRect.translated(m_d->transaction.originalMiddleLeft()));
handles.addRect(handleRect.translated(m_d->transaction.originalMiddleRight()));
handles.addRect(handleRect.translated(m_d->transaction.originalMiddleTop()));
handles.addRect(handleRect.translated(m_d->transaction.originalMiddleBottom()));
QPointF rotationCenter = m_d->currentArgs.originalCenter() + m_d->currentArgs.rotationCenterOffset();
QPointF dx(rX + 3, 0);
QPointF dy(0, rY + 3);
handles.addEllipse(rotationCenterRect.translated(rotationCenter));
handles.moveTo(rotationCenter - dx);
handles.lineTo(rotationCenter + dx);
handles.moveTo(rotationCenter - dy);
handles.lineTo(rotationCenter + dy);
gc.save();
//gc.setTransform(m_d->handlesTransform, true); <-- don't do like this!
QPainterPath mappedHandles = m_d->handlesTransform.map(handles);
QPen pen[2];
pen[0].setWidth(1);
pen[1].setWidth(2);
pen[1].setColor(Qt::lightGray);
for (int i = 1; i >= 0; --i) {
gc.setPen(pen[i]);
gc.drawPath(mappedHandles);
}
gc.restore();
}
void KisFreeTransformStrategy::externalConfigChanged()
{
m_d->recalculateTransformations();
}
bool KisFreeTransformStrategy::beginPrimaryAction(const QPointF &pt)
{
m_d->clickArgs = m_d->currentArgs;
m_d->clickPos = pt;
return true;
}
void KisFreeTransformStrategy::continuePrimaryAction(const QPointF &mousePos, bool specialModifierActive)
{
// Note: "specialModifierActive" just tells us if the shift key is being pressed
switch (m_d->function) {
case MOVE: {
QPointF diff = mousePos - m_d->clickPos;
if (specialModifierActive) {
KisTransformUtils::MatricesPack m(m_d->clickArgs);
QTransform t = m.S * m.projectedP;
QPointF originalDiff = t.inverted().map(diff);
if (qAbs(originalDiff.x()) >= qAbs(originalDiff.y())) {
originalDiff.setY(0);
} else {
originalDiff.setX(0);
}
diff = t.map(originalDiff);
}
m_d->currentArgs.setTransformedCenter(m_d->clickArgs.transformedCenter() + diff);
break;
}
case ROTATE:
{
KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
QTransform clickT = clickM.finalTransform();
QPointF rotationCenter = m_d->clickArgs.originalCenter() + m_d->clickArgs.rotationCenterOffset();
QPointF clickMouseImagePos = clickT.inverted().map(m_d->clickPos) - rotationCenter;
QPointF mouseImagePos = clickT.inverted().map(mousePos) - rotationCenter;
qreal a1 = atan2(clickMouseImagePos.y(), clickMouseImagePos.x());
qreal a2 = atan2(mouseImagePos.y(), mouseImagePos.x());
qreal theta = a2 - a1;
// Snap with shift key
if (specialModifierActive) {
const qreal snapAngle = M_PI_4 / 6.0; // fifteen degrees
qint32 thetaIndex = static_cast<qint32>((theta / snapAngle) + 0.5);
m_d->currentArgs.setAZ(normalizeAngle(thetaIndex * snapAngle));
}
else {
m_d->currentArgs.setAZ(normalizeAngle(m_d->clickArgs.aZ() + theta));
}
KisTransformUtils::MatricesPack m(m_d->currentArgs);
QTransform t = m.finalTransform();
QPointF newRotationCenter = t.map(m_d->currentArgs.originalCenter() + m_d->currentArgs.rotationCenterOffset());
QPointF oldRotationCenter = clickT.map(m_d->clickArgs.originalCenter() + m_d->clickArgs.rotationCenterOffset());
m_d->currentArgs.setTransformedCenter(m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
}
break;
case PERSPECTIVE:
{
QPointF diff = mousePos - m_d->clickPos;
double thetaX = - diff.y() * M_PI / m_d->transaction.originalHalfHeight() / 2 / fabs(m_d->currentArgs.scaleY());
m_d->currentArgs.setAX(normalizeAngle(m_d->clickArgs.aX() + thetaX));
qreal sign = qAbs(m_d->currentArgs.aX() - M_PI) < M_PI / 2 ? -1.0 : 1.0;
double thetaY = sign * diff.x() * M_PI / m_d->transaction.originalHalfWidth() / 2 / fabs(m_d->currentArgs.scaleX());
m_d->currentArgs.setAY(normalizeAngle(m_d->clickArgs.aY() + thetaY));
KisTransformUtils::MatricesPack m(m_d->currentArgs);
QTransform t = m.finalTransform();
QPointF newRotationCenter = t.map(m_d->currentArgs.originalCenter() + m_d->currentArgs.rotationCenterOffset());
KisTransformUtils::MatricesPack clickM(m_d->clickArgs);
QTransform clickT = clickM.finalTransform();
QPointF oldRotationCenter = clickT.map(m_d->clickArgs.originalCenter() + m_d->clickArgs.rotationCenterOffset());
m_d->currentArgs.setTransformedCenter(m_d->currentArgs.transformedCenter() + oldRotationCenter - newRotationCenter);
}
break;
case TOPSCALE:
case BOTTOMSCALE: {
QPointF staticPoint;
QPointF movingPoint;
qreal extraSign;
if (m_d->function == TOPSCALE) {
staticPoint = m_d->transaction.originalMiddleBottom();
movingPoint = m_d->transaction.originalMiddleTop();
extraSign = -1.0;
} else {
staticPoint = m_d->transaction.originalMiddleTop();
movingPoint = m_d->transaction.originalMiddleBottom();
extraSign = 1.0;
}
QPointF mouseImagePos = m_d->transform.inverted().map(mousePos);
qreal sign = mouseImagePos.y() <= staticPoint.y() ? -extraSign : extraSign;
m_d->currentArgs.setScaleY(sign * m_d->currentArgs.scaleY());
QPointF staticPointInView = m_d->transform.map(staticPoint);
qreal dist = kisDistance(staticPointInView, mousePos);
GSL::ScaleResult1D result =
GSL::calculateScaleY(m_d->currentArgs,
staticPoint,
staticPointInView,
movingPoint,
dist);
if (specialModifierActive || m_d->currentArgs.keepAspectRatio()) {
qreal aspectRatio = m_d->clickArgs.scaleX() / m_d->clickArgs.scaleY();
m_d->currentArgs.setScaleX(aspectRatio * result.scale);
}
m_d->currentArgs.setScaleY(result.scale);
m_d->currentArgs.setTransformedCenter(result.transformedCenter);
break;
}
case LEFTSCALE:
case RIGHTSCALE: {
QPointF staticPoint;
QPointF movingPoint;
qreal extraSign;
if (m_d->function == LEFTSCALE) {
staticPoint = m_d->transaction.originalMiddleRight();
movingPoint = m_d->transaction.originalMiddleLeft();
extraSign = -1.0;
} else {
staticPoint = m_d->transaction.originalMiddleLeft();
movingPoint = m_d->transaction.originalMiddleRight();
extraSign = 1.0;
}
QPointF mouseImagePos = m_d->transform.inverted().map(mousePos);
qreal sign = mouseImagePos.x() <= staticPoint.x() ? -extraSign : extraSign;
m_d->currentArgs.setScaleX(sign * m_d->currentArgs.scaleX());
QPointF staticPointInView = m_d->transform.map(staticPoint);
qreal dist = kisDistance(staticPointInView, mousePos);
GSL::ScaleResult1D result =
GSL::calculateScaleX(m_d->currentArgs,
staticPoint,
staticPointInView,
movingPoint,
dist);
if (specialModifierActive || m_d->currentArgs.keepAspectRatio()) {
qreal aspectRatio = m_d->clickArgs.scaleY() / m_d->clickArgs.scaleX();
m_d->currentArgs.setScaleY(aspectRatio * result.scale);
}
m_d->currentArgs.setScaleX(result.scale);
m_d->currentArgs.setTransformedCenter(result.transformedCenter);
break;
}
case TOPRIGHTSCALE:
case BOTTOMRIGHTSCALE:
case TOPLEFTSCALE:
case BOTTOMLEFTSCALE: {
QPointF staticPoint;
QPointF movingPoint;
if (m_d->function == TOPRIGHTSCALE) {
staticPoint = m_d->transaction.originalBottomLeft();
movingPoint = m_d->transaction.originalTopRight();
} else if (m_d->function == BOTTOMRIGHTSCALE) {
staticPoint = m_d->transaction.originalTopLeft();
movingPoint = m_d->transaction.originalBottomRight();
} else if (m_d->function == TOPLEFTSCALE) {
staticPoint = m_d->transaction.originalBottomRight();
movingPoint = m_d->transaction.originalTopLeft();
} else {
staticPoint = m_d->transaction.originalTopRight();
movingPoint = m_d->transaction.originalBottomLeft();
}
QPointF staticPointInView = m_d->transform.map(staticPoint);
QPointF movingPointInView = mousePos;
if (specialModifierActive || m_d->currentArgs.keepAspectRatio()) {
KisTransformUtils::MatricesPack m(m_d->clickArgs);
QTransform t = m.finalTransform();
QPointF refDiff = t.map(movingPoint) - staticPointInView;
QPointF realDiff = mousePos - staticPointInView;
realDiff = kisProjectOnVector(refDiff, realDiff);
movingPointInView = staticPointInView + realDiff;
}
GSL::ScaleResult2D result =
GSL::calculateScale2D(m_d->currentArgs,
staticPoint,
staticPointInView,
movingPoint,
movingPointInView);
m_d->currentArgs.setScaleX(result.scaleX);
m_d->currentArgs.setScaleY(result.scaleY);
m_d->currentArgs.setTransformedCenter(result.transformedCenter);
break;
}
case MOVECENTER: {
QPointF pt = m_d->transform.inverted().map(mousePos);
pt = KisTransformUtils::clipInRect(pt, m_d->transaction.originalRect());
QPointF newRotationCenterOffset = pt - m_d->currentArgs.originalCenter();
if (specialModifierActive) {
if (qAbs(newRotationCenterOffset.x()) > qAbs(newRotationCenterOffset.y())) {
newRotationCenterOffset.ry() = 0;
} else {
newRotationCenterOffset.rx() = 0;
}
}
m_d->currentArgs.setRotationCenterOffset(newRotationCenterOffset);
emit requestResetRotationCenterButtons();
}
break;
case TOPSHEAR:
case BOTTOMSHEAR: {
KisTransformUtils::MatricesPack m(m_d->clickArgs);
QTransform backwardT = (m.S * m.projectedP).inverted();
QPointF diff = backwardT.map(mousePos - m_d->clickPos);
qreal sign = m_d->function == BOTTOMSHEAR ? 1.0 : -1.0;
// get the dx pixels corresponding to the current shearX factor
qreal dx = sign * m_d->clickArgs.shearX() * m_d->clickArgs.scaleY() * m_d->transaction.originalHalfHeight(); // get the dx pixels corresponding to the current shearX factor
dx += diff.x();
// calculate the new shearX factor
m_d->currentArgs.setShearX(sign * dx / m_d->currentArgs.scaleY() / m_d->transaction.originalHalfHeight()); // calculate the new shearX factor
break;
}
case LEFTSHEAR:
case RIGHTSHEAR: {
KisTransformUtils::MatricesPack m(m_d->clickArgs);
QTransform backwardT = (m.S * m.projectedP).inverted();
QPointF diff = backwardT.map(mousePos - m_d->clickPos);
qreal sign = m_d->function == RIGHTSHEAR ? 1.0 : -1.0;
// get the dx pixels corresponding to the current shearX factor
qreal dy = sign * m_d->clickArgs.shearY() * m_d->clickArgs.scaleX() * m_d->transaction.originalHalfWidth();
dy += diff.y();
// calculate the new shearY factor
m_d->currentArgs.setShearY(sign * dy / m_d->clickArgs.scaleX() / m_d->transaction.originalHalfWidth());
break;
}
}
m_d->recalculateTransformations();
}
bool KisFreeTransformStrategy::endPrimaryAction()
{
bool shouldSave = !m_d->imageTooBig;
if (m_d->imageTooBig) {
m_d->currentArgs = m_d->clickArgs;
m_d->recalculateTransformations();
}
return shouldSave;
}
void KisFreeTransformStrategy::Private::recalculateTransformations()
{
KisTransformUtils::MatricesPack m(currentArgs);
QTransform sanityCheckMatrix = m.TS * m.SC * m.S * m.projectedP;
/**
* The center of the original image should still
* stay the origin of CS
*/
KIS_ASSERT_RECOVER_NOOP(sanityCheckMatrix.map(currentArgs.originalCenter()).manhattanLength() < 1e-4);
transform = m.finalTransform();
QTransform viewScaleTransform = converter->imageToDocumentTransform() * converter->documentToFlakeTransform();
handlesTransform = transform * viewScaleTransform;
QTransform tl = QTransform::fromTranslate(transaction.originalTopLeft().x(), transaction.originalTopLeft().y());
paintingTransform = tl.inverted() * q->thumbToImageTransform() * tl * transform * viewScaleTransform;
paintingOffset = transaction.originalTopLeft();
// check whether image is too big to be displayed or not
imageTooBig = KisTransformUtils::checkImageTooBig(transaction.originalRect(), m);
// recalculate cached handles position
recalculateTransformedHandles();
emit q->requestShowImageTooBig(imageTooBig);
}
diff --git a/plugins/tools/tool_transform2/kis_tool_transform.cc b/plugins/tools/tool_transform2/kis_tool_transform.cc
index 354c76815a..ae5fec7812 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform.cc
+++ b/plugins/tools/tool_transform2/kis_tool_transform.cc
@@ -1,1178 +1,1178 @@
/*
* kis_tool_transform.cc -- part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (c) 2010 Marc Pegon <pe.marc@free.fr>
* 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_tool_transform.h"
#include <math.h>
#include <limits>
#include <QPainter>
#include <QPen>
#include <QPushButton>
#include <QObject>
#include <QLabel>
#include <QComboBox>
#include <QApplication>
#include <QMatrix4x4>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <KoPointerEvent.h>
#include <KoID.h>
#include <KoCanvasBase.h>
#include <KoViewConverter.h>
#include <KoSelection.h>
#include <KoCompositeOp.h>
#include <kis_global.h>
#include <canvas/kis_canvas2.h>
#include <KisViewManager.h>
#include <kis_painter.h>
#include <kis_cursor.h>
#include <kis_image.h>
#include <kis_undo_adapter.h>
#include <kis_transaction.h>
#include <kis_selection.h>
#include <kis_filter_strategy.h>
#include <widgets/kis_cmb_idlist.h>
#include <kis_statusbar.h>
#include <kis_transform_worker.h>
#include <kis_perspectivetransform_worker.h>
#include <kis_warptransform_worker.h>
#include <kis_pixel_selection.h>
#include <kis_shape_selection.h>
#include <kis_selection_manager.h>
#include <kis_system_locker.h>
#include <krita_utils.h>
#include <kis_resources_snapshot.h>
#include <KoShapeTransformCommand.h>
#include "widgets/kis_progress_widget.h"
#include "kis_transform_utils.h"
#include "kis_warp_transform_strategy.h"
#include "kis_cage_transform_strategy.h"
#include "kis_liquify_transform_strategy.h"
#include "kis_free_transform_strategy.h"
#include "kis_perspective_transform_strategy.h"
#include "kis_transform_mask.h"
#include "kis_transform_mask_adapter.h"
#include "strokes/transform_stroke_strategy.h"
KisToolTransform::KisToolTransform(KoCanvasBase * canvas)
: KisTool(canvas, KisCursor::rotateCursor())
, m_workRecursively(true)
, m_changesTracker(&m_transaction)
, m_warpStrategy(
new KisWarpTransformStrategy(
dynamic_cast<KisCanvas2*>(canvas)->coordinatesConverter(),
m_currentArgs, m_transaction))
, m_cageStrategy(
new KisCageTransformStrategy(
dynamic_cast<KisCanvas2*>(canvas)->coordinatesConverter(),
m_currentArgs, m_transaction))
, m_liquifyStrategy(
new KisLiquifyTransformStrategy(
dynamic_cast<KisCanvas2*>(canvas)->coordinatesConverter(),
m_currentArgs, m_transaction, canvas->resourceManager()))
, m_freeStrategy(
new KisFreeTransformStrategy(
dynamic_cast<KisCanvas2*>(canvas)->coordinatesConverter(),
dynamic_cast<KisCanvas2*>(canvas)->snapGuide(),
m_currentArgs, m_transaction))
, m_perspectiveStrategy(
new KisPerspectiveTransformStrategy(
dynamic_cast<KisCanvas2*>(canvas)->coordinatesConverter(),
dynamic_cast<KisCanvas2*>(canvas)->snapGuide(),
m_currentArgs, m_transaction))
{
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
Q_ASSERT(m_canvas);
setObjectName("tool_transform");
useCursor(KisCursor::selectCursor());
m_optionsWidget = 0;
connect(m_warpStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_cageStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_liquifyStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_liquifyStrategy.data(), SIGNAL(requestCursorOutlineUpdate(const QPointF&)), SLOT(cursorOutlineUpdateRequested(const QPointF&)));
connect(m_liquifyStrategy.data(), SIGNAL(requestUpdateOptionWidget()), SLOT(updateOptionWidget()));
connect(m_freeStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_freeStrategy.data(), SIGNAL(requestResetRotationCenterButtons()), SLOT(resetRotationCenterButtonsRequested()));
connect(m_freeStrategy.data(), SIGNAL(requestShowImageTooBig(bool)), SLOT(imageTooBigRequested(bool)));
connect(m_perspectiveStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_perspectiveStrategy.data(), SIGNAL(requestShowImageTooBig(bool)), SLOT(imageTooBigRequested(bool)));
connect(&m_changesTracker, SIGNAL(sigConfigChanged()),
this, SLOT(slotTrackerChangedConfig()));
}
KisToolTransform::~KisToolTransform()
{
cancelStroke();
}
void KisToolTransform::outlineChanged()
{
emit freeTransformChanged();
m_canvas->updateCanvas();
}
void KisToolTransform::canvasUpdateRequested()
{
m_canvas->updateCanvas();
}
void KisToolTransform::resetCursorStyle()
{
KisTool::resetCursorStyle();
overrideCursorIfNotEditable();
}
void KisToolTransform::resetRotationCenterButtonsRequested()
{
if (!m_optionsWidget) return;
m_optionsWidget->resetRotationCenterButtons();
}
void KisToolTransform::imageTooBigRequested(bool value)
{
if (!m_optionsWidget) return;
m_optionsWidget->setTooBigLabelVisible(value);
}
KisTransformStrategyBase* KisToolTransform::currentStrategy() const
{
if (m_currentArgs.mode() == ToolTransformArgs::FREE_TRANSFORM) {
return m_freeStrategy.data();
} else if (m_currentArgs.mode() == ToolTransformArgs::WARP) {
return m_warpStrategy.data();
} else if (m_currentArgs.mode() == ToolTransformArgs::CAGE) {
return m_cageStrategy.data();
} else if (m_currentArgs.mode() == ToolTransformArgs::LIQUIFY) {
return m_liquifyStrategy.data();
} else /* if (m_currentArgs.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) */ {
return m_perspectiveStrategy.data();
}
}
void KisToolTransform::paint(QPainter& gc, const KoViewConverter &converter)
{
Q_UNUSED(converter);
if (!m_strokeData.strokeId()) return;
QRectF newRefRect = KisTransformUtils::imageToFlake(m_canvas->coordinatesConverter(), QRectF(0.0,0.0,1.0,1.0));
if (m_refRect != newRefRect) {
m_refRect = newRefRect;
currentStrategy()->externalConfigChanged();
}
gc.save();
if (m_optionsWidget && m_optionsWidget->showDecorations()) {
gc.setOpacity(0.3);
gc.fillPath(m_selectionPath, Qt::black);
}
gc.restore();
currentStrategy()->paint(gc);
if (!m_cursorOutline.isEmpty()) {
QPainterPath mappedOutline =
KisTransformUtils::imageToFlakeTransform(
m_canvas->coordinatesConverter()).map(m_cursorOutline);
paintToolOutline(&gc, mappedOutline);
}
}
void KisToolTransform::setFunctionalCursor()
{
if (overrideCursorIfNotEditable()) {
return;
}
if (!m_strokeData.strokeId()) {
useCursor(KisCursor::pointingHandCursor());
} else {
useCursor(currentStrategy()->getCurrentCursor());
}
}
void KisToolTransform::cursorOutlineUpdateRequested(const QPointF &imagePos)
{
QRect canvasUpdateRect;
if (!m_cursorOutline.isEmpty()) {
canvasUpdateRect = m_canvas->coordinatesConverter()->
imageToDocument(m_cursorOutline.boundingRect()).toAlignedRect();
}
m_cursorOutline = currentStrategy()->
getCursorOutline().translated(imagePos);
if (!m_cursorOutline.isEmpty()) {
canvasUpdateRect |=
m_canvas->coordinatesConverter()->
imageToDocument(m_cursorOutline.boundingRect()).toAlignedRect();
}
if (!canvasUpdateRect.isEmpty()) {
// grow rect a bit to follow interpolation fuzziness
canvasUpdateRect = kisGrowRect(canvasUpdateRect, 2);
m_canvas->updateCanvas(canvasUpdateRect);
}
}
void KisToolTransform::beginActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action)
{
if (!nodeEditable()) {
event->ignore();
return;
}
if (!m_strokeData.strokeId()) {
startStroke(m_currentArgs.mode(), false);
} else {
bool result = false;
if (usePrimaryAction) {
result = currentStrategy()->beginPrimaryAction(event);
} else {
result = currentStrategy()->beginAlternateAction(event, action);
}
if (result) {
setMode(KisTool::PAINT_MODE);
}
}
m_actuallyMoveWhileSelected = false;
outlineChanged();
}
void KisToolTransform::continueActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action)
{
if (mode() != KisTool::PAINT_MODE) return;
m_actuallyMoveWhileSelected = true;
if (usePrimaryAction) {
currentStrategy()->continuePrimaryAction(event);
} else {
currentStrategy()->continueAlternateAction(event, action);
}
updateOptionWidget();
outlineChanged();
}
void KisToolTransform::endActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action)
{
if (mode() != KisTool::PAINT_MODE) return;
setMode(KisTool::HOVER_MODE);
if (m_actuallyMoveWhileSelected ||
currentStrategy()->acceptsClicks()) {
bool result = false;
if (usePrimaryAction) {
result = currentStrategy()->endPrimaryAction(event);
} else {
result = currentStrategy()->endAlternateAction(event, action);
}
if (result) {
commitChanges();
}
outlineChanged();
}
updateOptionWidget();
updateApplyResetAvailability();
}
void KisToolTransform::beginPrimaryAction(KoPointerEvent *event)
{
beginActionImpl(event, true, KisTool::NONE);
}
void KisToolTransform::continuePrimaryAction(KoPointerEvent *event)
{
continueActionImpl(event, true, KisTool::NONE);
}
void KisToolTransform::endPrimaryAction(KoPointerEvent *event)
{
endActionImpl(event, true, KisTool::NONE);
}
void KisToolTransform::activateAlternateAction(AlternateAction action)
{
currentStrategy()->activateAlternateAction(action);
}
void KisToolTransform::deactivateAlternateAction(AlternateAction action)
{
currentStrategy()->deactivateAlternateAction(action);
}
void KisToolTransform::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
beginActionImpl(event, false, action);
}
void KisToolTransform::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
continueActionImpl(event, false, action);
}
void KisToolTransform::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
endActionImpl(event, false, action);
}
void KisToolTransform::mousePressEvent(KoPointerEvent *event)
{
KisTool::mousePressEvent(event);
}
void KisToolTransform::mouseMoveEvent(KoPointerEvent *event)
{
QPointF mousePos = m_canvas->coordinatesConverter()->documentToImage(event->point);
cursorOutlineUpdateRequested(mousePos);
if (!MOVE_CONDITION(event, KisTool::PAINT_MODE)) {
currentStrategy()->hoverActionCommon(event);
setFunctionalCursor();
KisTool::mouseMoveEvent(event);
return;
}
}
void KisToolTransform::mouseReleaseEvent(KoPointerEvent *event)
{
KisTool::mouseReleaseEvent(event);
}
void KisToolTransform::touchEvent( QTouchEvent* event )
{
//Count all moving touch points
int touchCount = 0;
Q_FOREACH ( QTouchEvent::TouchPoint tp, event->touchPoints() ) {
if( tp.state() == Qt::TouchPointMoved ) {
touchCount++;
}
}
//Use the touch point count to determine the gesture
switch( touchCount ) {
case 1: { //Panning
QTouchEvent::TouchPoint tp = event->touchPoints().at( 0 );
QPointF diff = tp.screenPos() - tp.lastScreenPos();
m_currentArgs.setTransformedCenter( m_currentArgs.transformedCenter() + diff );
outlineChanged();
break;
}
case 2: { //Scaling
QTouchEvent::TouchPoint tp1 = event->touchPoints().at( 0 );
QTouchEvent::TouchPoint tp2 = event->touchPoints().at( 1 );
float lastZoom = (tp1.lastScreenPos() - tp2.lastScreenPos()).manhattanLength();
float newZoom = (tp1.screenPos() - tp2.screenPos()).manhattanLength();
float diff = (newZoom - lastZoom) / 100;
m_currentArgs.setScaleX( m_currentArgs.scaleX() + diff );
m_currentArgs.setScaleY( m_currentArgs.scaleY() + diff );
outlineChanged();
break;
}
case 3: { //Rotation
/* TODO: implement touch-based rotation.
Vector2f center;
Q_FOREACH ( const QTouchEvent::TouchPoint &tp, event->touchPoints() ) {
if( tp.state() == Qt::TouchPointMoved ) {
center += Vector2f( tp.screenPos().x(), tp.screenPos().y() );
}
}
center /= touchCount;
QTouchEvent::TouchPoint tp = event->touchPoints().at(0);
Vector2f oldPosition = (Vector2f( tp.lastScreenPos().x(), tp.lastScreenPos().y() ) - center).normalized();
Vector2f newPosition = (Vector2f( tp.screenPos().x(), tp.screenPos().y() ) - center).normalized();
float oldAngle = qAcos( oldPosition.dot( Vector2f( 0.0f, 0.0f ) ) );
float newAngle = qAcos( newPosition.dot( Vector2f( 0.0f, 0.0f ) ) );
float diff = newAngle - oldAngle;
m_currentArgs.setAZ( m_currentArgs.aZ() + diff );
outlineChanged();
*/
break;
}
}
}
void KisToolTransform::applyTransform()
{
slotApplyTransform();
}
KisToolTransform::TransformToolMode KisToolTransform::transformMode() const
{
TransformToolMode mode = FreeTransformMode;
switch (m_currentArgs.mode())
{
case ToolTransformArgs::FREE_TRANSFORM:
mode = FreeTransformMode;
break;
case ToolTransformArgs::WARP:
mode = WarpTransformMode;
break;
case ToolTransformArgs::CAGE:
mode = CageTransformMode;
break;
case ToolTransformArgs::LIQUIFY:
mode = LiquifyTransformMode;
break;
case ToolTransformArgs::PERSPECTIVE_4POINT:
mode = PerspectiveTransformMode;
break;
default:
KIS_ASSERT_RECOVER_NOOP(0 && "unexpected transform mode");
}
return mode;
}
double KisToolTransform::translateX() const
{
return m_currentArgs.transformedCenter().x();
}
double KisToolTransform::translateY() const
{
return m_currentArgs.transformedCenter().y();
}
double KisToolTransform::rotateX() const
{
return m_currentArgs.aX();
}
double KisToolTransform::rotateY() const
{
return m_currentArgs.aY();
}
double KisToolTransform::rotateZ() const
{
return m_currentArgs.aZ();
}
double KisToolTransform::scaleX() const
{
return m_currentArgs.scaleX();
}
double KisToolTransform::scaleY() const
{
return m_currentArgs.scaleY();
}
double KisToolTransform::shearX() const
{
return m_currentArgs.shearX();
}
double KisToolTransform::shearY() const
{
return m_currentArgs.shearY();
}
KisToolTransform::WarpType KisToolTransform::warpType() const
{
switch(m_currentArgs.warpType()) {
case KisWarpTransformWorker::AFFINE_TRANSFORM:
return AffineWarpType;
case KisWarpTransformWorker::RIGID_TRANSFORM:
return RigidWarpType;
case KisWarpTransformWorker::SIMILITUDE_TRANSFORM:
return SimilitudeWarpType;
default:
return RigidWarpType;
}
}
double KisToolTransform::warpFlexibility() const
{
return m_currentArgs.alpha();
}
int KisToolTransform::warpPointDensity() const
{
return m_currentArgs.numPoints();
}
void KisToolTransform::setTransformMode(KisToolTransform::TransformToolMode newMode)
{
ToolTransformArgs::TransformMode mode = ToolTransformArgs::FREE_TRANSFORM;
switch (newMode) {
case FreeTransformMode:
mode = ToolTransformArgs::FREE_TRANSFORM;
break;
case WarpTransformMode:
mode = ToolTransformArgs::WARP;
break;
case CageTransformMode:
mode = ToolTransformArgs::CAGE;
break;
case LiquifyTransformMode:
mode = ToolTransformArgs::LIQUIFY;
break;
case PerspectiveTransformMode:
mode = ToolTransformArgs::PERSPECTIVE_4POINT;
break;
default:
KIS_ASSERT_RECOVER_NOOP(0 && "unexpected transform mode");
}
if( mode != m_currentArgs.mode() ) {
if( newMode == FreeTransformMode ) {
m_optionsWidget->slotSetFreeTransformModeButtonClicked( true );
} else if( newMode == WarpTransformMode ) {
m_optionsWidget->slotSetWarpModeButtonClicked( true );
} else if( newMode == CageTransformMode ) {
m_optionsWidget->slotSetCageModeButtonClicked( true );
} else if( newMode == LiquifyTransformMode ) {
m_optionsWidget->slotSetLiquifyModeButtonClicked( true );
} else if( newMode == PerspectiveTransformMode ) {
m_optionsWidget->slotSetPerspectiveModeButtonClicked( true );
}
emit transformModeChanged();
}
}
void KisToolTransform::setRotateX( double rotation )
{
m_currentArgs.setAX( normalizeAngle(rotation) );
}
void KisToolTransform::setRotateY( double rotation )
{
m_currentArgs.setAY( normalizeAngle(rotation) );
}
void KisToolTransform::setRotateZ( double rotation )
{
m_currentArgs.setAZ( normalizeAngle(rotation) );
}
void KisToolTransform::setWarpType( KisToolTransform::WarpType type )
{
switch( type ) {
case RigidWarpType:
m_currentArgs.setWarpType(KisWarpTransformWorker::RIGID_TRANSFORM);
break;
case AffineWarpType:
m_currentArgs.setWarpType(KisWarpTransformWorker::AFFINE_TRANSFORM);
break;
case SimilitudeWarpType:
m_currentArgs.setWarpType(KisWarpTransformWorker::SIMILITUDE_TRANSFORM);
break;
default:
break;
}
}
void KisToolTransform::setWarpFlexibility( double flexibility )
{
m_currentArgs.setAlpha( flexibility );
}
void KisToolTransform::setWarpPointDensity( int density )
{
m_optionsWidget->slotSetWarpDensity(density);
}
bool KisToolTransform::tryInitTransformModeFromNode(KisNodeSP node)
{
bool result = false;
if (KisTransformMaskSP mask =
dynamic_cast<KisTransformMask*>(node.data())) {
KisTransformMaskParamsInterfaceSP savedParams =
mask->transformParams();
KisTransformMaskAdapter *adapter =
dynamic_cast<KisTransformMaskAdapter*>(savedParams.data());
if (adapter) {
m_currentArgs = adapter->transformArgs();
initGuiAfterTransformMode();
result = true;
}
}
return result;
}
bool KisToolTransform::tryFetchArgsFromCommandAndUndo(ToolTransformArgs *args, ToolTransformArgs::TransformMode mode, KisNodeSP currentNode)
{
bool result = false;
const KUndo2Command *lastCommand = image()->undoAdapter()->presentCommand();
KisNodeSP oldRootNode;
if (lastCommand &&
TransformStrokeStrategy::fetchArgsFromCommand(lastCommand, args, &oldRootNode) &&
args->mode() == mode &&
oldRootNode == currentNode) {
args->saveContinuedState();
image()->undoAdapter()->undoLastCommand();
// FIXME: can we make it async?
image()->waitForDone();
result = true;
}
return result;
}
void KisToolTransform::initTransformMode(ToolTransformArgs::TransformMode mode)
{
// NOTE: we are requesting an old value of m_currentArgs variable
// here, which is global, don't forget about this on higher
// levels.
QString filterId = m_currentArgs.filterId();
m_currentArgs = ToolTransformArgs();
m_currentArgs.setOriginalCenter(m_transaction.originalCenterGeometric());
m_currentArgs.setTransformedCenter(m_transaction.originalCenterGeometric());
if (mode == ToolTransformArgs::FREE_TRANSFORM) {
m_currentArgs.setMode(ToolTransformArgs::FREE_TRANSFORM);
} else if (mode == ToolTransformArgs::WARP) {
m_currentArgs.setMode(ToolTransformArgs::WARP);
m_optionsWidget->setDefaultWarpPoints();
m_currentArgs.setEditingTransformPoints(false);
} else if (mode == ToolTransformArgs::CAGE) {
m_currentArgs.setMode(ToolTransformArgs::CAGE);
m_currentArgs.setEditingTransformPoints(true);
} else if (mode == ToolTransformArgs::LIQUIFY) {
m_currentArgs.setMode(ToolTransformArgs::LIQUIFY);
const QRect srcRect = m_transaction.originalRect().toAlignedRect();
if (!srcRect.isEmpty()) {
m_currentArgs.initLiquifyTransformMode(m_transaction.originalRect().toAlignedRect());
}
} else if (mode == ToolTransformArgs::PERSPECTIVE_4POINT) {
m_currentArgs.setMode(ToolTransformArgs::PERSPECTIVE_4POINT);
}
initGuiAfterTransformMode();
}
void KisToolTransform::initGuiAfterTransformMode()
{
currentStrategy()->externalConfigChanged();
outlineChanged();
updateOptionWidget();
updateApplyResetAvailability();
}
void KisToolTransform::updateSelectionPath()
{
m_selectionPath = QPainterPath();
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(image(), currentNode(), 0, this->canvas()->resourceManager());
+ new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
QPainterPath selectionOutline;
KisSelectionSP selection = resources->activeSelection();
if (selection && selection->outlineCacheValid()) {
selectionOutline = selection->outlineCache();
} else {
selectionOutline.addRect(m_selectedPortionCache->exactBounds());
}
const KisCoordinatesConverter *converter = m_canvas->coordinatesConverter();
QTransform i2f = converter->imageToDocumentTransform() * converter->documentToFlakeTransform();
m_selectionPath = i2f.map(selectionOutline);
}
void KisToolTransform::initThumbnailImage(KisPaintDeviceSP previewDevice)
{
QImage origImg;
m_selectedPortionCache = previewDevice;
QTransform thumbToImageTransform;
const int maxSize = 2000;
QRect srcRect(m_transaction.originalRect().toAlignedRect());
int x, y, w, h;
srcRect.getRect(&x, &y, &w, &h);
if (w > maxSize || h > maxSize) {
qreal scale = qreal(maxSize) / (w > h ? w : h);
QTransform scaleTransform = QTransform::fromScale(scale, scale);
QRect thumbRect = scaleTransform.mapRect(m_transaction.originalRect()).toAlignedRect();
origImg = m_selectedPortionCache->
createThumbnail(thumbRect.width(),
thumbRect.height(),
srcRect, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
thumbToImageTransform = scaleTransform.inverted();
} else {
origImg = m_selectedPortionCache->convertToQImage(0, x, y, w, h,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
thumbToImageTransform = QTransform();
}
// init both strokes since the thumbnail is initialized only once
// during the stroke
m_freeStrategy->setThumbnailImage(origImg, thumbToImageTransform);
m_perspectiveStrategy->setThumbnailImage(origImg, thumbToImageTransform);
m_warpStrategy->setThumbnailImage(origImg, thumbToImageTransform);
m_cageStrategy->setThumbnailImage(origImg, thumbToImageTransform);
m_liquifyStrategy->setThumbnailImage(origImg, thumbToImageTransform);
}
void KisToolTransform::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
KisTool::activate(toolActivation, shapes);
if (currentNode()) {
m_transaction = TransformTransactionProperties(QRectF(), &m_currentArgs, currentNode());
}
startStroke(ToolTransformArgs::FREE_TRANSFORM, false);
}
void KisToolTransform::deactivate()
{
endStroke();
m_canvas->updateCanvas();
KisTool::deactivate();
}
void KisToolTransform::requestUndoDuringStroke()
{
if (!m_strokeData.strokeId()) return;
m_changesTracker.requestUndo();
}
void KisToolTransform::requestStrokeEnd()
{
endStroke();
}
void KisToolTransform::requestStrokeCancellation()
{
cancelStroke();
}
void KisToolTransform::startStroke(ToolTransformArgs::TransformMode mode, bool forceReset)
{
Q_ASSERT(!m_strokeData.strokeId());
KisPaintDeviceSP dev;
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(image(), currentNode(), 0, this->canvas()->resourceManager());
+ new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
KisNodeSP currentNode = resources->currentNode();
if (!currentNode || !currentNode->isEditable()) {
return;
}
/**
* FIXME: The transform tool is not completely asynchronous, it
* needs the content of the layer for creation of the stroke
* strategy. It means that we cannot start a new stroke until the
* previous one is finished. Ideally, we should create the
* m_selectedPortionCache and m_selectionPath somewhere in the
* stroke and pass it to the tool somehow. But currently, we will
* just disable starting a new stroke asynchronously
*/
if (image()->tryBarrierLock()) {
image()->unlock();
} else {
return;
}
ToolTransformArgs fetchedArgs;
bool fetchedFromCommand = false;
if (!forceReset) {
fetchedFromCommand = tryFetchArgsFromCommandAndUndo(&fetchedArgs, mode, currentNode);
}
if (m_optionsWidget) {
m_workRecursively = m_optionsWidget->workRecursively() ||
!currentNode->paintDevice();
}
- TransformStrokeStrategy *strategy = new TransformStrokeStrategy(currentNode, resources->activeSelection(), image()->postExecutionUndoAdapter());
+ TransformStrokeStrategy *strategy = new TransformStrokeStrategy(currentNode, resources->activeSelection(), image().data());
KisPaintDeviceSP previewDevice = strategy->previewDevice();
KisSelectionSP selection = strategy->realSelection();
QRect srcRect = selection ? selection->selectedExactRect() : previewDevice->exactBounds();
if (!selection && resources->activeSelection()) {
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
kisCanvas->viewManager()->
showFloatingMessage(
i18nc("floating message in transformation tool",
"Selections are not used when editing transform masks "),
QIcon(), 4000, KisFloatingMessage::Low);
}
if (srcRect.isEmpty()) {
delete strategy;
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
kisCanvas->viewManager()->
showFloatingMessage(
i18nc("floating message in transformation tool",
"Cannot transform empty layer "),
QIcon(), 1000, KisFloatingMessage::Medium);
return;
}
m_transaction = TransformTransactionProperties(srcRect, &m_currentArgs, currentNode);
initThumbnailImage(previewDevice);
updateSelectionPath();
if (!forceReset && fetchedFromCommand) {
m_currentArgs = fetchedArgs;
initGuiAfterTransformMode();
} else if (forceReset || !tryInitTransformModeFromNode(currentNode)) {
initTransformMode(mode);
}
m_strokeData = StrokeData(image()->startStroke(strategy));
bool haveInvisibleNodes = clearDevices(m_transaction.rootNode(), m_workRecursively);
if (haveInvisibleNodes) {
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
kisCanvas->viewManager()->
showFloatingMessage(
i18nc("floating message in transformation tool",
"Invisible sublayers will also be transformed. Lock layers if you do not want them to be transformed "),
QIcon(), 4000, KisFloatingMessage::Low);
}
Q_ASSERT(m_changesTracker.isEmpty());
commitChanges();
}
void KisToolTransform::endStroke()
{
if (!m_strokeData.strokeId()) return;
if (!m_currentArgs.isIdentity()) {
transformDevices(m_transaction.rootNode(), m_workRecursively);
image()->addJob(m_strokeData.strokeId(),
new TransformStrokeStrategy::TransformData(
TransformStrokeStrategy::TransformData::SELECTION,
m_currentArgs,
m_transaction.rootNode()));
image()->endStroke(m_strokeData.strokeId());
} else {
image()->cancelStroke(m_strokeData.strokeId());
}
m_strokeData.clear();
m_changesTracker.reset();
}
void KisToolTransform::cancelStroke()
{
if (!m_strokeData.strokeId()) return;
if (m_currentArgs.continuedTransform()) {
m_currentArgs.restoreContinuedState();
endStroke();
} else {
image()->cancelStroke(m_strokeData.strokeId());
m_strokeData.clear();
m_changesTracker.reset();
}
}
void KisToolTransform::commitChanges()
{
if (!m_strokeData.strokeId()) return;
m_changesTracker.commitConfig(m_currentArgs);
}
void KisToolTransform::slotTrackerChangedConfig()
{
slotUiChangedConfig();
updateOptionWidget();
}
bool KisToolTransform::clearDevices(KisNodeSP node, bool recursive)
{
bool haveInvisibleNodes = false;
if (!node->isEditable(false)) return haveInvisibleNodes;
haveInvisibleNodes = !node->visible(false);
if (recursive) {
// simple tail-recursive iteration
KisNodeSP prevNode = node->lastChild();
while(prevNode) {
haveInvisibleNodes |= clearDevices(prevNode, recursive);
prevNode = prevNode->prevSibling();
}
}
image()->addJob(m_strokeData.strokeId(),
new TransformStrokeStrategy::ClearSelectionData(node));
/**
* It might happen that the editablity state of the node would
* change during the stroke, so we need to save the set of
* applicable nodes right in the beginning of the processing
*/
m_strokeData.addClearedNode(node);
return haveInvisibleNodes;
}
void KisToolTransform::transformDevices(KisNodeSP node, bool recursive)
{
if (!node->isEditable()) return;
KIS_ASSERT_RECOVER_RETURN(recursive ||
(m_strokeData.clearedNodes().size() == 1 &&
KisNodeSP(m_strokeData.clearedNodes().first()) == node));
Q_FOREACH (KisNodeSP currentNode, m_strokeData.clearedNodes()) {
KIS_ASSERT_RECOVER_RETURN(currentNode);
image()->addJob(m_strokeData.strokeId(),
new TransformStrokeStrategy::TransformData(
TransformStrokeStrategy::TransformData::PAINT_DEVICE,
m_currentArgs,
currentNode));
}
}
QWidget* KisToolTransform::createOptionWidget() {
m_optionsWidget = new KisToolTransformConfigWidget(&m_transaction, m_canvas, m_workRecursively, 0);
Q_CHECK_PTR(m_optionsWidget);
m_optionsWidget->setObjectName(toolId() + " option widget");
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(m_optionsWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
m_optionsWidget->layout()->addWidget(specialSpacer);
connect(m_optionsWidget, SIGNAL(sigConfigChanged()),
this, SLOT(slotUiChangedConfig()));
connect(m_optionsWidget, SIGNAL(sigApplyTransform()),
this, SLOT(slotApplyTransform()));
connect(m_optionsWidget, SIGNAL(sigResetTransform()),
this, SLOT(slotResetTransform()));
connect(m_optionsWidget, SIGNAL(sigRestartTransform()),
this, SLOT(slotRestartTransform()));
connect(m_optionsWidget, SIGNAL(sigEditingFinished()),
this, SLOT(slotEditingFinished()));
updateOptionWidget();
return m_optionsWidget;
}
void KisToolTransform::updateOptionWidget()
{
if (!m_optionsWidget) return;
if (!currentNode()) {
m_optionsWidget->setEnabled(false);
return;
}
else {
m_optionsWidget->setEnabled(true);
m_optionsWidget->updateConfig(m_currentArgs);
}
}
void KisToolTransform::updateApplyResetAvailability()
{
if (m_optionsWidget) {
m_optionsWidget->setApplyResetDisabled(m_currentArgs.isIdentity());
}
}
void KisToolTransform::slotUiChangedConfig()
{
if (mode() == KisTool::PAINT_MODE) return;
currentStrategy()->externalConfigChanged();
if (m_currentArgs.mode() == ToolTransformArgs::LIQUIFY) {
m_currentArgs.saveLiquifyTransformMode();
}
outlineChanged();
updateApplyResetAvailability();
}
void KisToolTransform::slotApplyTransform()
{
QApplication::setOverrideCursor(KisCursor::waitCursor());
endStroke();
QApplication::restoreOverrideCursor();
}
void KisToolTransform::slotResetTransform()
{
if (m_currentArgs.continuedTransform()) {
ToolTransformArgs::TransformMode savedMode = m_currentArgs.mode();
/**
* Our reset transform button can be used for two purposes:
*
* 1) Reset current transform to the initial one, which was
* loaded from the previous user action.
*
* 2) Reset transform frame to infinity when the frame is unchanged
*/
const bool transformDiffers = !m_currentArgs.continuedTransform()->isSameMode(m_currentArgs);
if (transformDiffers &&
m_currentArgs.continuedTransform()->mode() == savedMode) {
m_currentArgs.restoreContinuedState();
initGuiAfterTransformMode();
slotEditingFinished();
} else {
cancelStroke();
image()->waitForDone();
startStroke(savedMode, true);
KIS_ASSERT_RECOVER_NOOP(!m_currentArgs.continuedTransform());
}
} else {
initTransformMode(m_currentArgs.mode());
slotEditingFinished();
}
}
void KisToolTransform::slotRestartTransform()
{
if (!m_strokeData.strokeId()) return;
ToolTransformArgs savedArgs(m_currentArgs);
cancelStroke();
image()->waitForDone();
startStroke(savedArgs.mode(), true);
}
void KisToolTransform::slotEditingFinished()
{
commitChanges();
}
void KisToolTransform::setShearY(double shear)
{
m_optionsWidget->slotSetShearY(shear);
}
void KisToolTransform::setShearX(double shear)
{
m_optionsWidget->slotSetShearX(shear);
}
void KisToolTransform::setScaleY(double scale)
{
m_optionsWidget->slotSetScaleY(scale);
}
void KisToolTransform::setScaleX(double scale)
{
m_optionsWidget->slotSetScaleX(scale);
}
void KisToolTransform::setTranslateY(double translation)
{
m_optionsWidget->slotSetTranslateY(translation);
}
void KisToolTransform::setTranslateX(double translation)
{
m_optionsWidget->slotSetTranslateX(translation);
}
diff --git a/plugins/tools/tool_transform2/shear_cursor.png b/plugins/tools/tool_transform2/shear_cursor.png
new file mode 100644
index 0000000000..ae6dab5b38
Binary files /dev/null and b/plugins/tools/tool_transform2/shear_cursor.png differ
diff --git a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
index 9e7776cd1e..5bcd66f8db 100644
--- a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
+++ b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
@@ -1,318 +1,318 @@
/*
* 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 <klocalizedstring.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_abstract_projection_plane.h"
#include "kis_recalculate_transform_mask_job.h"
#include "kis_projection_leaf.h"
#include "kis_modify_transform_mask_command.h"
TransformStrokeStrategy::TransformStrokeStrategy(KisNodeSP rootNode,
KisSelectionSP selection,
- KisPostExecutionUndoAdapter *undoAdapter)
- : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Transform"), false, undoAdapter),
+ KisStrokeUndoFacade *undoFacade)
+ : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Transform"), false, undoFacade),
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) {
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()) {
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 KisModifyTransformMaskCommand(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 KisModifyTransformMaskCommand(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/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
index fd298a1bf5..27771924a9 100644
--- a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
+++ b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
@@ -1,126 +1,126 @@
/*
* 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 __TRANSFORM_STROKE_STRATEGY_H
#define __TRANSFORM_STROKE_STRATEGY_H
#include <QMutex>
#include <KoUpdater.h>
#include <kis_stroke_strategy_undo_command_based.h>
#include <kis_types.h>
#include "tool_transform_args.h"
#include <kis_processing_visitor.h>
#include <kritatooltransform_export.h>
class KisPostExecutionUndoAdapter;
class TransformStrokeStrategy : public KisStrokeStrategyUndoCommandBased
{
public:
class TransformData : public KisStrokeJobData {
public:
enum Destination {
PAINT_DEVICE,
SELECTION,
};
public:
TransformData(Destination _destination, const ToolTransformArgs &_config, KisNodeSP _node)
: KisStrokeJobData(CONCURRENT, NORMAL),
destination(_destination),
config(_config),
node(_node)
{
}
Destination destination;
ToolTransformArgs config;
KisNodeSP node;
};
class ClearSelectionData : public KisStrokeJobData {
public:
ClearSelectionData(KisNodeSP _node)
: KisStrokeJobData(SEQUENTIAL, NORMAL),
node(_node)
{
}
KisNodeSP node;
};
public:
TransformStrokeStrategy(KisNodeSP rootNode,
KisSelectionSP selection,
- KisPostExecutionUndoAdapter *undoAdapter);
+ KisStrokeUndoFacade *undoFacade);
~TransformStrokeStrategy();
KisPaintDeviceSP previewDevice() const;
KisSelectionSP realSelection() const;
void initStrokeCallback();
void finishStrokeCallback();
void cancelStrokeCallback();
void doStrokeCallback(KisStrokeJobData *data);
static bool fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeSP *rootNode);
protected:
void postProcessToplevelCommand(KUndo2Command *command);
private:
KoUpdaterPtr fetchUpdater(KisNodeSP node);
void transformAndMergeDevice(const ToolTransformArgs &config,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
KisProcessingVisitor::ProgressHelper *helper);
void transformDevice(const ToolTransformArgs &config,
KisPaintDeviceSP device,
KisProcessingVisitor::ProgressHelper *helper);
void clearSelection(KisPaintDeviceSP device);
//void transformDevice(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper);
bool checkBelongsToSelection(KisPaintDeviceSP device) const;
KisPaintDeviceSP createDeviceCache(KisPaintDeviceSP src);
bool haveDeviceInCache(KisPaintDeviceSP src);
void putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache);
KisPaintDeviceSP getDeviceCache(KisPaintDeviceSP src);
private:
KisSelectionSP m_selection;
QMutex m_devicesCacheMutex;
QHash<KisPaintDevice*, KisPaintDeviceSP> m_devicesCacheHash;
KisPaintDeviceSP m_previewDevice;
KisTransformMaskSP writeToTransformMask;
ToolTransformArgs m_savedTransformArgs;
KisNodeSP m_savedRootNode;
};
#endif /* __TRANSFORM_STROKE_STRATEGY_H */
diff --git a/plugins/tools/tool_transform2/tool_transform.qrc b/plugins/tools/tool_transform2/tool_transform.qrc
index 105c2aa7ae..d9d068a502 100644
--- a/plugins/tools/tool_transform2/tool_transform.qrc
+++ b/plugins/tools/tool_transform2/tool_transform.qrc
@@ -1,6 +1,7 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file>rotate_cursor.xpm</file>
+ <file>shear_cursor.png</file>
</qresource>
</RCC>
diff --git a/sdk/tests/filestest.h b/sdk/tests/filestest.h
index 7ae559982c..ebed5e2fec 100644
--- a/sdk/tests/filestest.h
+++ b/sdk/tests/filestest.h
@@ -1,121 +1,119 @@
/*
* 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 <klocalizedstring.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;
Q_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);
- dbgKrita << s;
+ KisImportExportFilter::ConversionStatus status = manager.importDocument(sourceFileInfo.absoluteFilePath(), QString());
+ Q_UNUSED(status);
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(QUrl("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;
}
dbgKrita << "Comparison failures: " << failuresCompare;
dbgKrita << "No image failures: " << failuresDocImage;
dbgKrita << "No comparison image: " << failuresFileInfo;
QFAIL("Failed testing files");
}
}
#endif
diff --git a/sdk/tests/stroke_testing_utils.cpp b/sdk/tests/stroke_testing_utils.cpp
index 4350862d7e..bc47272732 100644
--- a/sdk/tests/stroke_testing_utils.cpp
+++ b/sdk/tests/stroke_testing_utils.cpp
@@ -1,354 +1,350 @@
/*
* 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 <brushengine/kis_paintop_preset.h>
#include <resources/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::testSimpleStroke()
{
testOneStroke(false, true, false, true);
}
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);
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);
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, image);
m_strokeId = image->startStroke(stroke);
addPaintingJobs(image, resources, i);
if(!cancelled) {
image->endStroke(m_strokeId);
}
else {
image->cancelStroke(m_strokeId);
}
image->waitForDone();
currentNode = resources->currentNode();
}
beforeCheckingResult(image, 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,
int iteration)
{
Q_UNUSED(iteration);
addPaintingJobs(image, resources);
}
void utils::StrokeTester::addPaintingJobs(KisImageWSP image,
KisResourcesSnapshotSP resources)
{
Q_UNUSED(image);
Q_UNUSED(resources);
}
void utils::StrokeTester::beforeCheckingResult(KisImageWSP image, KisNodeSP activeNode)
{
Q_UNUSED(image);
Q_UNUSED(activeNode);
}
diff --git a/sdk/tests/testing_nodes.h b/sdk/tests/testing_nodes.h
index ea37de4300..6855a0e84b 100644
--- a/sdk/tests/testing_nodes.h
+++ b/sdk/tests/testing_nodes.h
@@ -1,53 +1,53 @@
/*
* Copyright (c) 2016 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 TESTING_NODES_H
#define TESTING_NODES_H
#include "kis_node.h"
namespace TestUtil {
struct DefaultNode : public KisNode {
KisPaintDeviceSP paintDevice() const {
- return 0;
+ return KisPaintDeviceSP();
}
KisPaintDeviceSP original() const {
- return 0;
+ return KisPaintDeviceSP();
}
KisPaintDeviceSP projection() const {
- return 0;
+ return KisPaintDeviceSP();
}
bool allowAsChild(KisNodeSP) const {
return true;
}
const KoColorSpace * colorSpace() const {
return 0;
}
virtual const KoCompositeOp * compositeOp() const {
return 0;
}
};
}
#endif // TESTING_NODES_H
diff --git a/sdk/tests/testutil.h b/sdk/tests/testutil.h
index a9db0752ac..a850de05fc 100644
--- a/sdk/tests/testutil.h
+++ b/sdk/tests/testutil.h
@@ -1,618 +1,624 @@
/*
* 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 <QProcessEnvironment>
#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"
#include "testing_nodes.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;
+ return KisNodeSP();
}
inline QString fetchExternalDataFileName(const QString relativeFileName)
{
static QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
static QString unittestsDataDirPath = "KRITA_UNITTESTS_DATA_DIR";
QString path;
if (!env.contains(unittestsDataDirPath)) {
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"))
{
dbgKrita << node->name();
KisNodeSP child = node->firstChild();
while (child) {
if (child->childCount() > 0) {
dumpNodeStack(child, prefix + "\t");
} else {
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);
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++;
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;
}
}
}
}
}
// 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();
}
// 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) {
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;
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);
}
if (!QFileInfo(fullPath).exists()) {
fullPath = "";
}
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());
dbgKrita << "--- Saving reference image:" << name << path;
image.save(path);
saveStandardResults = false;
} else {
dbgKrita << "--- External image not found. Skipping..." << name;
}
} else {
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),
- m_maxFailingPixels(100)
+ m_maxFailingPixels(100),
+ m_fuzzy(1)
{
}
void setMaxFailingPixels(int value) {
m_maxFailingPixels = value;
}
+ void setFuzzy(int fuzzy){
+ m_fuzzy = fuzzy;
+ }
+
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, m_maxFailingPixels);
+ caseName, m_fuzzy, m_fuzzy, m_maxFailingPixels);
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;
int m_maxFailingPixels;
+ int m_fuzzy;
};
#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) {
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 DefaultNode
{
Q_OBJECT
public:
KisNodeSP clone() const {
- return new TestNode(*this);
+ return KisNodeSP(new TestNode(*this));
}
};
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);
+ layer = KisPaintLayerSP(new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8));
+ image->addNode(KisNodeSP(layer.data()));
}
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) {
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;
};
QStringList getHierarchy(KisNodeSP root, const QString &prefix = "");
bool checkHierarchy(KisNodeSP root, const QStringList &expected);
}
#endif