diff --git a/krita/data/aboutdata/credits.txt b/krita/data/aboutdata/credits.txt index a3b6f1df68..5b9efe7042 100644 --- a/krita/data/aboutdata/credits.txt +++ b/krita/data/aboutdata/credits.txt @@ -1,6 +1,8 @@ Peter Sikking:Project Vision Ramon Miranda:Artist, Muses author, brush and preset creator David Revoy:Brushes and Palettes Ilya Portnov:MyPaint shade selector Martin Renold:MyPaint shade selector +Saisho Kazuki:Japanese Animation template Pasquale D'Antuono:Donor + diff --git a/krita/data/templates/CMakeLists.txt b/krita/data/templates/CMakeLists.txt index d139bf84ee..26ed3a8713 100644 --- a/krita/data/templates/CMakeLists.txt +++ b/krita/data/templates/CMakeLists.txt @@ -1,4 +1,5 @@ +add_subdirectory( animation ) add_subdirectory( comics ) add_subdirectory( design ) add_subdirectory( dslr ) add_subdirectory( texture ) diff --git a/krita/data/templates/animation/Anim-Jp-EN.desktop b/krita/data/templates/animation/Anim-Jp-EN.desktop new file mode 100644 index 0000000000..235bc65c98 --- /dev/null +++ b/krita/data/templates/animation/Anim-Jp-EN.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Link +URL=.source/Anim-Jp-EN.kra +Icon=template_animation +Name=Animation-Japanese-En +Name[ca]=Animació-Japonès-EN +Name[es]=Animación-Japonés-En +Name[nl]=Animatie-Japans-En +Name[pl]=Animacja-Japońska-En +Name[pt]=Animação-Japonês-EN +Name[sv]=Animering-japanska-en +Name[uk]=Японська анімація (англійською) +Name[x-test]=xxAnimation-Japanese-Enxx diff --git a/krita/data/templates/animation/Anim-Jp-EN.kra b/krita/data/templates/animation/Anim-Jp-EN.kra new file mode 100644 index 0000000000..122a0e7af2 Binary files /dev/null and b/krita/data/templates/animation/Anim-Jp-EN.kra differ diff --git a/krita/data/templates/animation/Anim-Jp-JP.desktop b/krita/data/templates/animation/Anim-Jp-JP.desktop new file mode 100644 index 0000000000..bc1fb3284b --- /dev/null +++ b/krita/data/templates/animation/Anim-Jp-JP.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Link +URL=.source/Anim-Jp-JP.kra +Icon=template_animation +Name=Animation-Japanese-JP +Name[ca]=Animació-Japonès-JP +Name[es]=Animación-Japonés-JP +Name[nl]=Animatie-Japans-JP +Name[pl]=Animacja-Japońska-JP +Name[pt]=Animação-Japonês-JP +Name[sv]=Animering-japanska-jp +Name[uk]=Японська анімація (японською) +Name[x-test]=xxAnimation-Japanese-JPxx diff --git a/krita/data/templates/animation/Anim-Jp-JP.kra b/krita/data/templates/animation/Anim-Jp-JP.kra new file mode 100644 index 0000000000..87847d73db Binary files /dev/null and b/krita/data/templates/animation/Anim-Jp-JP.kra differ diff --git a/krita/data/templates/animation/CMakeLists.txt b/krita/data/templates/animation/CMakeLists.txt new file mode 100644 index 0000000000..5f1622220b --- /dev/null +++ b/krita/data/templates/animation/CMakeLists.txt @@ -0,0 +1,15 @@ +install( FILES +template_animation.png +DESTINATION +${DATA_INSTALL_DIR}/krita/pics +) + +install( FILES + Anim-Jp-EN.kra + Anim-Jp-JP.kra +DESTINATION ${DATA_INSTALL_DIR}/krita/templates/animation/.source) + +install( FILES + Anim-Jp-EN.desktop + Anim-Jp-JP.desktop +DESTINATION ${DATA_INSTALL_DIR}/krita/templates/animation) diff --git a/krita/data/templates/animation/template_animation.png b/krita/data/templates/animation/template_animation.png new file mode 100644 index 0000000000..1c359f732f Binary files /dev/null and b/krita/data/templates/animation/template_animation.png differ diff --git a/krita/data/templates/animation/template_animation.svg b/krita/data/templates/animation/template_animation.svg new file mode 100644 index 0000000000..4f19375fb8 --- /dev/null +++ b/krita/data/templates/animation/template_animation.svg @@ -0,0 +1,167 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/data/themes/BreezeDark.colors b/krita/data/themes/BreezeDark.colors new file mode 100644 index 0000000000..e3e3a401dd --- /dev/null +++ b/krita/data/themes/BreezeDark.colors @@ -0,0 +1,119 @@ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.1 +IntensityEffect=2 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color=112,111,110 +ColorAmount=0.025 +ColorEffect=2 +ContrastAmount=0.1 +ContrastEffect=2 +Enable=false +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=77,77,77 +BackgroundNormal=49,54,59 +DecorationFocus=61,174,233 +DecorationHover=61,174,233 +ForegroundActive=61,174,233 +ForegroundInactive=189,195,199 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=239,240,241 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[Colors:Selection] +BackgroundAlternate=29,153,243 +BackgroundNormal=61,174,233 +DecorationFocus=61,174,233 +DecorationHover=61,174,233 +ForegroundActive=252,252,252 +ForegroundInactive=239,240,241 +ForegroundLink=253,188,75 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=239,240,241 +ForegroundPositive=39,174,96 +ForegroundVisited=189,195,199 + +[Colors:Tooltip] +BackgroundAlternate=77,77,77 +BackgroundNormal=49,54,59 +DecorationFocus=61,174,233 +DecorationHover=61,174,233 +ForegroundActive=61,174,233 +ForegroundInactive=189,195,199 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=239,240,241 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[Colors:View] +BackgroundAlternate=49,54,59 +BackgroundNormal=35,38,41 +DecorationFocus=61,174,233 +DecorationHover=61,174,233 +ForegroundActive=61,174,233 +ForegroundInactive=189,195,199 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=239,240,241 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[Colors:Window] +BackgroundAlternate=77,77,77 +BackgroundNormal=49,54,59 +DecorationFocus=61,174,233 +DecorationHover=61,174,233 +ForegroundActive=61,174,233 +ForegroundInactive=189,195,199 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=239,240,241 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[Colors:Complementary] +BackgroundAlternate=59,64,69 +BackgroundNormal=49,54,59 +DecorationFocus=30,146,255 +DecorationHover=61,174,230 +ForegroundActive=246,116,0 +ForegroundInactive=175,176,179 +ForegroundLink=61,174,230 +ForegroundNegative=237,21,21 +ForegroundNeutral=201,206,59 +ForegroundNormal=239,240,241 +ForegroundPositive=17,209,22 +ForegroundVisited=61,174,230 + +[General] +ColorScheme=Breeze Dark +Name=Breeze Dark +shadeSortColumn=true + +[KDE] +contrast=4 + +[WM] +activeBackground=49,54,59 +activeBlend=255,255,255 +activeForeground=239,240,241 +inactiveBackground=49,54,59 +inactiveBlend=75,71,67 +inactiveForeground=127,140,141 diff --git a/krita/data/themes/BreezeHighContrast.colors b/krita/data/themes/BreezeHighContrast.colors new file mode 100644 index 0000000000..758f1e9048 --- /dev/null +++ b/krita/data/themes/BreezeHighContrast.colors @@ -0,0 +1,119 @@ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.1 +IntensityEffect=2 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color=112,111,110 +ColorAmount=0.025 +ColorEffect=2 +ContrastAmount=0.1 +ContrastEffect=2 +Enable=false +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=189,195,199 +BackgroundNormal=77,77,77 +DecorationFocus=29,153,243 +DecorationHover=77,77,77 +ForegroundActive=61,174,233 +ForegroundInactive=127,140,141 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=252,252,252 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[Colors:Selection] +BackgroundAlternate=29,153,243 +BackgroundNormal=52,73,94 +DecorationFocus=29,153,243 +DecorationHover=77,77,77 +ForegroundActive=252,252,252 +ForegroundInactive=252,252,252 +ForegroundLink=253,188,75 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=239,240,241 +ForegroundPositive=39,174,96 +ForegroundVisited=189,195,199 + +[Colors:Tooltip] +BackgroundAlternate=77,77,77 +BackgroundNormal=29,153,243 +DecorationFocus=29,153,243 +DecorationHover=77,77,77 +ForegroundActive=61,174,233 +ForegroundInactive=127,140,141 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=252,252,252 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[Colors:View] +BackgroundAlternate=41,44,48 +BackgroundNormal=35,38,41 +DecorationFocus=29,153,243 +DecorationHover=77,77,77 +ForegroundActive=61,174,233 +ForegroundInactive=127,140,141 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=252,252,252 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[Colors:Window] +BackgroundAlternate=189,195,199 +BackgroundNormal=49,54,59 +DecorationFocus=29,153,243 +DecorationHover=77,77,77 +ForegroundActive=61,174,233 +ForegroundInactive=127,140,141 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=252,252,252 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[Colors:Complementary] +BackgroundAlternate=41,44,48 +BackgroundNormal=35,38,41 +DecorationFocus=29,153,243 +DecorationHover=77,77,77 +ForegroundActive=61,174,233 +ForegroundInactive=127,140,141 +ForegroundLink=41,128,185 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundNormal=252,252,252 +ForegroundPositive=39,174,96 +ForegroundVisited=127,140,141 + +[General] +ColorScheme=Breeze High Conttrast +Name=Breeze High Contrast +shadeSortColumn=true + +[KDE] +contrast=9 + +[WM] +activeBackground=29,153,243 +activeBlend=29,153,243 +activeForeground=252,252,252 +inactiveBackground=49,54,59 +inactiveBlend=26,188,156 +inactiveForeground=252,252,252 diff --git a/krita/data/themes/BreezeLight.colors b/krita/data/themes/BreezeLight.colors new file mode 100644 index 0000000000..aada764375 --- /dev/null +++ b/krita/data/themes/BreezeLight.colors @@ -0,0 +1,119 @@ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.1 +IntensityEffect=2 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color=112,111,110 +ColorAmount=0.025 +ColorEffect=2 +ContrastAmount=0.1 +ContrastEffect=2 +Enable=false +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate=224,223,222 +BackgroundNormal=239,240,241 +DecorationFocus=61,174,233 +DecorationHover=142,203,233 +ForegroundActive=255,128,224 +ForegroundInactive=136,135,134 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=49,54,59 +ForegroundPositive=0,110,40 +ForegroundVisited=69,40,134 + +[Colors:Selection] +BackgroundAlternate=62,138,204 +BackgroundNormal=61,174,233 +DecorationFocus=61,174,233 +DecorationHover=142,203,233 +ForegroundActive=255,128,224 +ForegroundInactive=142,203,233 +ForegroundLink=0,49,110 +ForegroundNegative=156,14,14 +ForegroundNeutral=255,221,0 +ForegroundNormal=252,252,252 +ForegroundPositive=128,255,128 +ForegroundVisited=69,40,134 + +[Colors:Tooltip] +BackgroundAlternate=196,224,255 +BackgroundNormal=252,252,252 +DecorationFocus=61,174,233 +DecorationHover=142,203,233 +ForegroundActive=255,128,224 +ForegroundInactive=96,112,128 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=49,54,59 +ForegroundPositive=0,110,40 +ForegroundVisited=69,40,134 + +[Colors:View] +BackgroundAlternate=248,247,246 +BackgroundNormal=252,252,252 +DecorationFocus=61,174,233 +DecorationHover=142,203,233 +ForegroundActive=255,128,224 +ForegroundInactive=136,135,134 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=49,54,59 +ForegroundPositive=0,110,40 +ForegroundVisited=69,40,134 + +[Colors:Window] +BackgroundAlternate=218,217,216 +BackgroundNormal=239,240,241 +DecorationFocus=61,174,233 +DecorationHover=142,203,233 +ForegroundActive=255,128,224 +ForegroundInactive=136,135,134 +ForegroundLink=0,87,174 +ForegroundNegative=191,3,3 +ForegroundNeutral=176,128,0 +ForegroundNormal=49,54,59 +ForegroundPositive=0,110,40 +ForegroundVisited=69,40,134 + +[Colors:Complementary] +BackgroundAlternate=59,64,69 +BackgroundNormal=49,54,59 +DecorationFocus=30,146,255 +DecorationHover=61,174,230 +ForegroundActive=246,116,0 +ForegroundInactive=175,176,179 +ForegroundLink=61,174,230 +ForegroundNegative=237,21,21 +ForegroundNeutral=201,206,59 +ForegroundNormal=239,240,241 +ForegroundPositive=17,209,22 +ForegroundVisited=61,174,230 + +[General] +ColorScheme=Breeze Light +Name=Breeze Light +shadeSortColumn=true + +[KDE] +contrast=4 + +[WM] +activeBackground=239,240,241 +activeBlend=255,255,255 +activeForeground=49,54,59 +inactiveBackground=239,240,241 +inactiveBlend=75,71,67 +inactiveForeground=137,142,146 diff --git a/krita/data/themes/CMakeLists.txt b/krita/data/themes/CMakeLists.txt index 0a8b08d9c5..4a5b3fa495 100644 --- a/krita/data/themes/CMakeLists.txt +++ b/krita/data/themes/CMakeLists.txt @@ -1,10 +1,13 @@ ########### install files ############### install(FILES +BreezeDark.colors +BreezeHighContrast.colors +BreezeLight.colors KritaNeutral.colors KritaBlender.colors KritaBright.colors KritaDark.colors ${schemefiles} DESTINATION ${DATA_INSTALL_DIR}/color-schemes ) diff --git a/krita/pics/Breeze-dark/breeze-dark-icons.qrc b/krita/pics/Breeze-dark/breeze-dark-icons.qrc index 345a8a9c2d..396a806e69 100644 --- a/krita/pics/Breeze-dark/breeze-dark-icons.qrc +++ b/krita/pics/Breeze-dark/breeze-dark-icons.qrc @@ -1,89 +1,89 @@ dark_application-exit.svg dark_application-pdf.svg dark_applications-system.svg dark_arrow-down.svg dark_arrow-downright.svg dark_arrow-downleft.svg dark_arrow-topright.svg dark_arrow-topleft.svg dark_arrow-left.svg dark_arrow-right.svg dark_arrow-up.svg dark_bookmarks.svg dark_configure-shortcuts.svg dark_configure.svg dark_configure-toolbars.svg dark_dialog-cancel.svg dark_dialog-close.svg dark_dialog-ok.svg dark_dialog-warning.svg dark_document-edit.svg dark_document-export.svg dark_document-import.svg dark_document-new.svg dark_document-open-recent.svg dark_document-open.svg dark_document-print-preview.svg - dark_document-print.svg + dark_document-properties.svg dark_document-save-as.svg dark_document-save.svg dark_download.svg dark_drive-harddisk.svg dark_edit-clear.svg dark_edit-copy.svg dark_edit-cut.svg dark_edit-delete.svg dark_edit-paste.svg dark_edit-redo.svg dark_edit-undo.svg dark_folder-documents.svg dark_folder-pictures.svg dark_folder.svg dark_format-list-unordered.svg dark_go-home.svg dark_go-next.svg dark_go-previous.svg dark_go-up.svg dark_help-contents.svg dark_im-user.svg dark_kde.svg dark_layer-visible-off.svg dark_link.svg dark_list-add.svg dark_locked.svg dark_media-playback-start.svg dark_media-playback-stop.svg dark_media-record.svg dark_object-locked.svg dark_object-rotate-left.svg dark_object-rotate-right.svg dark_object-unlocked.svg dark_preferences-desktop-color.svg dark_preferences-desktop-display.svg dark_preferences-desktop-locale.svg dark_process-stop.svg dark_select-all.svg dark_select-clear.svg dark_system-help.svg dark_tools-report-bug.svg dark_tools-wizard.svg dark_unlocked.svg dark_view-choose.svg dark_view-filter.svg dark_view-fullscreen.svg dark_view-grid.svg dark_view-list-details.svg dark_view-list-text.svg dark_view-preview.svg dark_view-refresh.svg dark_window-close.svg dark_window-new.svg dark_zoom-in.svg dark_zoom-original.svg dark_zoom-out.svg diff --git a/krita/pics/Breeze-dark/dark_document-properties.svg b/krita/pics/Breeze-dark/dark_document-properties.svg new file mode 120000 index 0000000000..489d15bce0 --- /dev/null +++ b/krita/pics/Breeze-dark/dark_document-properties.svg @@ -0,0 +1 @@ +dark_configure.svg \ No newline at end of file diff --git a/krita/pics/Breeze-light/breeze-light-icons.qrc b/krita/pics/Breeze-light/breeze-light-icons.qrc index ea891c07fc..3cfa9133d2 100644 --- a/krita/pics/Breeze-light/breeze-light-icons.qrc +++ b/krita/pics/Breeze-light/breeze-light-icons.qrc @@ -1,89 +1,90 @@ light_application-exit.svg light_application-pdf.svg light_applications-system.svg light_arrow-down.svg light_arrow-downright.svg light_arrow-downleft.svg light_arrow-topright.svg light_arrow-topleft.svg light_arrow-left.svg light_arrow-right.svg light_arrow-up.svg light_bookmarks.svg light_configure-shortcuts.svg light_configure.svg light_configure-toolbars.svg light_dialog-cancel.svg light_dialog-close.svg light_dialog-ok.svg light_dialog-warning.svg light_document-edit.svg light_document-export.svg light_document-import.svg light_document-new.svg light_document-open-recent.svg light_document-open.svg light_document-print-preview.svg light_document-print.svg + light_document-properties.svg light_document-save-as.svg light_document-save.svg light_download.svg light_drive-harddisk.svg light_edit-clear.svg light_edit-copy.svg light_edit-cut.svg light_edit-delete.svg light_edit-paste.svg light_edit-redo.svg light_edit-undo.svg light_folder-documents.svg light_folder-pictures.svg light_folder.svg light_format-list-unordered.svg light_go-home.svg light_go-next.svg light_go-previous.svg light_go-up.svg light_help-contents.svg light_im-user.svg light_kde.svg light_layer-visible-off.svg light_link.svg light_list-add.svg light_locked.svg light_media-playback-start.svg light_media-playback-stop.svg light_media-record.svg light_object-locked.svg light_object-rotate-left.svg light_object-rotate-right.svg light_object-unlocked.svg light_preferences-desktop-color.svg light_preferences-desktop-display.svg light_preferences-desktop-locale.svg light_process-stop.svg light_select-all.svg light_select-clear.svg light_system-help.svg light_tools-report-bug.svg light_tools-wizard.svg light_unlocked.svg light_view-choose.svg light_view-filter.svg light_view-fullscreen.svg light_view-grid.svg light_view-list-details.svg light_view-list-text.svg light_view-preview.svg light_view-refresh.svg light_window-close.svg light_window-new.svg light_zoom-in.svg light_zoom-original.svg light_zoom-out.svg diff --git a/krita/pics/Breeze-light/light_document-properties.svg b/krita/pics/Breeze-light/light_document-properties.svg new file mode 120000 index 0000000000..b0c09418e0 --- /dev/null +++ b/krita/pics/Breeze-light/light_document-properties.svg @@ -0,0 +1 @@ +light_configure.svg \ No newline at end of file diff --git a/krita/pics/app/sc-apps-calligrakrita.svgz b/krita/pics/app/sc-apps-calligrakrita.svgz index 494468ec7c..a2ad31fcfd 100644 Binary files a/krita/pics/app/sc-apps-calligrakrita.svgz and b/krita/pics/app/sc-apps-calligrakrita.svgz differ diff --git a/krita/pics/mimetypes/128/application-x-krita.png b/krita/pics/mimetypes/128/application-x-krita.png new file mode 100644 index 0000000000..07479934d1 Binary files /dev/null and b/krita/pics/mimetypes/128/application-x-krita.png differ diff --git a/krita/pics/mimetypes/16/application-x-krita.png b/krita/pics/mimetypes/16/application-x-krita.png new file mode 100644 index 0000000000..53e73f45dc Binary files /dev/null and b/krita/pics/mimetypes/16/application-x-krita.png differ diff --git a/krita/pics/mimetypes/22/application-x-krita.png b/krita/pics/mimetypes/22/application-x-krita.png new file mode 100644 index 0000000000..2ee2bfce3a Binary files /dev/null and b/krita/pics/mimetypes/22/application-x-krita.png differ diff --git a/krita/pics/mimetypes/32/application-x-krita.png b/krita/pics/mimetypes/32/application-x-krita.png new file mode 100644 index 0000000000..0960a9ea11 Binary files /dev/null and b/krita/pics/mimetypes/32/application-x-krita.png differ diff --git a/krita/pics/mimetypes/48/application-x-krita.png b/krita/pics/mimetypes/48/application-x-krita.png new file mode 100644 index 0000000000..3d03c0827b Binary files /dev/null and b/krita/pics/mimetypes/48/application-x-krita.png differ diff --git a/krita/pics/mimetypes/64/application-x-krita.png b/krita/pics/mimetypes/64/application-x-krita.png new file mode 100644 index 0000000000..3c7b37c9a4 Binary files /dev/null and b/krita/pics/mimetypes/64/application-x-krita.png differ diff --git a/krita/pics/mimetypes/sources/application-x-krita-16.svg b/krita/pics/mimetypes/sources/application-x-krita-16.svg new file mode 100644 index 0000000000..b5aaeee97e --- /dev/null +++ b/krita/pics/mimetypes/sources/application-x-krita-16.svg @@ -0,0 +1,1628 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/mimetypes/sources/application-x-krita-22.svg b/krita/pics/mimetypes/sources/application-x-krita-22.svg new file mode 100644 index 0000000000..0881839f7f --- /dev/null +++ b/krita/pics/mimetypes/sources/application-x-krita-22.svg @@ -0,0 +1,1727 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/krita/pics/mimetypes/sources/application-x-krita.svg b/krita/pics/mimetypes/sources/application-x-krita.svg new file mode 100644 index 0000000000..7cc04e6f86 --- /dev/null +++ b/krita/pics/mimetypes/sources/application-x-krita.svg @@ -0,0 +1,1762 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/flake/KoTosContainer.cpp b/libs/flake/KoTosContainer.cpp index 697a489323..78909eeee5 100644 --- a/libs/flake/KoTosContainer.cpp +++ b/libs/flake/KoTosContainer.cpp @@ -1,352 +1,352 @@ /* This file is part of the KDE project * Copyright (C) 2010 Thomas Zander * Copyright (C) 2010 KO GmbH * Copyright (C) 2010 Thorsten Zachmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoTosContainer.h" #include "KoTosContainer_p.h" #include "KoShapeRegistry.h" #include "KoShapeFactoryBase.h" #include "KoShapeLoadingContext.h" #include "KoTextShapeDataBase.h" #include "KoTosContainerModel.h" #include "KoStyleStack.h" #include "KoOdfLoadingContext.h" #include "KoXmlNS.h" #include "KoGenStyle.h" #include #include #include KoTosContainerPrivate::KoTosContainerPrivate(KoShapeContainer *q) : KoShapeContainerPrivate(q) , resizeBehavior(KoTosContainer::IndependentSizes) { } KoTosContainerPrivate::~KoTosContainerPrivate() { } KoTosContainer::KoTosContainer() : KoShapeContainer(*(new KoTosContainerPrivate(this))) { } KoTosContainer::~KoTosContainer() { delete textShape(); } KoTosContainer::KoTosContainer(KoTosContainerPrivate &dd) : KoShapeContainer(dd) { } void KoTosContainer::paintComponent(QPainter &, const KoViewConverter &, KoShapePaintingContext &) { } bool KoTosContainer::loadText(const KoXmlElement &element, KoShapeLoadingContext &context) { Q_D(const KoTosContainer); KoXmlElement child; forEachElement(child, element) { // only recreate the text shape if there's something to be loaded if (child.localName() == "p" || child.localName() == "list") { KoShape *textShape = createTextShape(context.documentResourceManager()); if (!textShape) { return false; } //apply the style properties to the loaded text setTextAlignment(d->alignment); // In the case of text on shape, we cannot ask the text shape to load // the odf, since it expects a complete document with style info and // everything, so we have to use the KoTextShapeData object instead. KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); Q_ASSERT(shapeData); shapeData->loadStyle(element, context); bool loadOdf = shapeData->loadOdf(element, context); return loadOdf; } } return true; } void KoTosContainer::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context) { Q_D(KoTosContainer); KoShapeContainer::loadStyle(element, context); KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); styleStack.setTypeProperties("graphic"); QString verticalAlign(styleStack.property(KoXmlNS::draw, "textarea-vertical-align")); Qt::Alignment vAlignment(Qt::AlignTop); if (verticalAlign == "bottom") { vAlignment = Qt::AlignBottom; } else if (verticalAlign == "justify") { // not yet supported vAlignment = Qt::AlignVCenter; } else if (verticalAlign == "middle") { vAlignment = Qt::AlignVCenter; } QString horizontalAlign(styleStack.property(KoXmlNS::draw, "textarea-horizontal-align")); Qt::Alignment hAlignment(Qt::AlignLeft); if (horizontalAlign == "center") { hAlignment = Qt::AlignCenter; } else if (horizontalAlign == "justify") { // not yet supported hAlignment = Qt::AlignCenter; } else if (horizontalAlign == "right") { hAlignment = Qt::AlignRight; } d->alignment = vAlignment | hAlignment; } QString KoTosContainer::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const { Qt::Alignment alignment = textAlignment(); QString verticalAlignment = "top"; Qt::Alignment vAlignment(alignment & Qt::AlignVertical_Mask); if (vAlignment == Qt::AlignBottom) { verticalAlignment = "bottom"; } else if (vAlignment == Qt::AlignVCenter || vAlignment == Qt::AlignCenter) { verticalAlignment = "middle"; } style.addProperty("draw:textarea-vertical-align", verticalAlignment); QString horizontalAlignment = "left"; Qt::Alignment hAlignment(alignment & Qt::AlignHorizontal_Mask); if (hAlignment == Qt::AlignCenter || hAlignment == Qt::AlignHCenter) { horizontalAlignment = "center"; } else if (hAlignment == Qt::AlignJustify) { horizontalAlignment = "justify"; } else if (hAlignment == Qt::AlignRight) { horizontalAlignment = "right"; } style.addProperty("draw:textarea-horizontal-align", horizontalAlignment); return KoShapeContainer::saveStyle(style, context); } void KoTosContainer::saveText(KoShapeSavingContext &context) const { KoShape *textShape = this->textShape(); if (!textShape) { return; } // In the case of text on shape, we cannot ask the text shape to save // the odf, since it would save all the frame information as well, which // is wrong. // Only save the text shape if it has content. KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); if (shapeData && !shapeData->document()->isEmpty()) { shapeData->saveOdf(context); } } void KoTosContainer::setPlainText(const QString &text) { KoShape *textShape = this->textShape(); if (textShape == 0) { warnFlake << "No text shape present in KoTosContainer"; return; } KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); Q_ASSERT(shapeData->document()); shapeData->document()->setPlainText(text); } void KoTosContainer::setResizeBehavior(ResizeBehavior resizeBehavior) { Q_D(KoTosContainer); if (d->resizeBehavior == resizeBehavior) { return; } d->resizeBehavior = resizeBehavior; if (d->model) { d->model->containerChanged(this, KoShape::SizeChanged); } } KoTosContainer::ResizeBehavior KoTosContainer::resizeBehavior() const { Q_D(const KoTosContainer); return d->resizeBehavior; } void KoTosContainer::setTextAlignment(Qt::Alignment alignment) { Q_D(KoTosContainer); KoShape *textShape = this->textShape(); if (textShape == 0) { warnFlake << "No text shape present in KoTosContainer"; return; } // vertical KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); shapeData->setVerticalAlignment(alignment); // horizontal Q_ASSERT(shapeData->document()); QTextBlockFormat bf; bf.setAlignment(alignment & Qt::AlignHorizontal_Mask); QTextCursor cursor(shapeData->document()); - cursor.setPosition(QTextCursor::End, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); cursor.mergeBlockFormat(bf); d->alignment = alignment; } Qt::Alignment KoTosContainer::textAlignment() const { KoShape *textShape = this->textShape(); if (textShape == 0) { warnFlake << "No text shape present in KoTosContainer"; return Qt::AlignTop; } // vertical KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); // the model makes sure it contains a shape that has a KoTextShapeDataBase set so no need to check that Qt::Alignment answer = shapeData->verticalAlignment() & Qt::AlignVertical_Mask; // horizontal Q_ASSERT(shapeData->document()); QTextCursor cursor(shapeData->document()); answer = answer | (cursor.blockFormat().alignment() & Qt::AlignHorizontal_Mask); return answer; } void KoTosContainer::setPreferredTextRect(const QRectF &rect) { Q_D(KoTosContainer); d->preferredTextRect = rect; KoShape *textShape = this->textShape(); //debugFlake << rect << textShape << d->resizeBehavior; if (d->resizeBehavior == TextFollowsPreferredTextRect && textShape) { //debugFlake << rect; textShape->setPosition(rect.topLeft()); textShape->setSize(rect.size()); } } QRectF KoTosContainer::preferredTextRect() const { Q_D(const KoTosContainer); return d->preferredTextRect; } KoShape *KoTosContainer::createTextShape(KoDocumentResourceManager *documentResources) { if (!documentResources) { warnFlake << "KoDocumentResourceManager not found"; return 0; } Q_D(KoTosContainer); delete textShape(); delete d->model; d->model = new KoTosContainerModel(); QSet delegates; delegates << this; KoShape *textShape = 0; KoShapeFactoryBase *factory = KoShapeRegistry::instance()->get("TextShapeID"); if (factory) { // not installed, thats too bad, but allowed textShape = factory->createDefaultShape(documentResources); Q_ASSERT(textShape); // would be a bug in the text shape; if (d->resizeBehavior == TextFollowsPreferredTextRect) { textShape->setSize(d->preferredTextRect.size()); } else { textShape->setSize(size()); } if (d->resizeBehavior == TextFollowsPreferredTextRect) { textShape->setPosition(d->preferredTextRect.topLeft()); } else { textShape->setPosition(QPointF(0, 0)); } textShape->setSelectable(false); textShape->setRunThrough(runThrough()); KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); Q_ASSERT(shapeData); // would be a bug in kotext // TODO check if that is correct depending on the resize mode shapeData->setVerticalAlignment(Qt::AlignVCenter); addShape(textShape); // textShape->setZIndex(zIndex() + 1); // not needed as there as the text shape is the only sub shape delegates << textShape; } else { warnFlake << "Text shape factory not found"; } setToolDelegates(delegates); return textShape; } KoShape *KoTosContainer::textShape() const { const QList subShapes = shapes(); return subShapes.isEmpty() ? 0 : subShapes.at(0); } void KoTosContainer::shapeChanged(ChangeType type, KoShape *shape) { Q_UNUSED(shape); Q_D(KoTosContainer); if (d->model == 0) { return; } if (type == SizeChanged || type == ContentChanged) { d->model->containerChanged(this, type); } // TODO is this needed? #if 0 Q_FOREACH (KoShape *shape, d->model->shapes()) shape->notifyChanged(); #endif } void KoTosContainer::setRunThrough(short int runThrough) { KoShape::setRunThrough(runThrough); KoShape *textShape = this->textShape(); if (textShape) { textShape->setRunThrough(runThrough); } } diff --git a/libs/image/brushengine/kis_paintop.cc b/libs/image/brushengine/kis_paintop.cc index cd25d953a4..072dbbea2a 100644 --- a/libs/image/brushengine/kis_paintop.cc +++ b/libs/image/brushengine/kis_paintop.cc @@ -1,196 +1,171 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004,2007,2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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.h" #include #include #include #include #include "kis_painter.h" #include "kis_layer.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_datamanager.h" #include #include #include "kis_vec.h" #include "kis_perspective_math.h" #include "kis_fixed_paint_device.h" #include "kis_paintop_utils.h" #define BEZIER_FLATNESS_THRESHOLD 0.5 #include #include struct Q_DECL_HIDDEN KisPaintOp::Private { Private(KisPaintOp *_q) : q(_q), dab(0), - currentScale(1.0), - currentRotation(0), fanCornersEnabled(false), fanCornersStep(1.0) {} KisPaintOp *q; KisFixedPaintDeviceSP dab; KisPainter* painter; - qreal currentScale; - qreal currentRotation; bool fanCornersEnabled; qreal fanCornersStep; }; KisPaintOp::KisPaintOp(KisPainter * painter) : d(new Private(this)) { d->painter = painter; } KisPaintOp::~KisPaintOp() { d->dab.clear(); delete d; } KisFixedPaintDeviceSP KisPaintOp::cachedDab() { return cachedDab(d->painter->device()->colorSpace()); } KisFixedPaintDeviceSP KisPaintOp::cachedDab(const KoColorSpace *cs) { if (!d->dab || !(*d->dab->colorSpace() == *cs)) { d->dab = new KisFixedPaintDevice(cs); } return d->dab; } void KisPaintOp::setFanCornersInfo(bool fanCornersEnabled, qreal fanCornersStep) { d->fanCornersEnabled = fanCornersEnabled; d->fanCornersStep = fanCornersStep; } void KisPaintOp::splitCoordinate(qreal coordinate, qint32 *whole, qreal *fraction) { const qint32 i = std::floor(coordinate); const qreal f = coordinate - i; *whole = i; *fraction = f; } static void paintBezierCurve(KisPaintOp *paintOp, const KisPaintInformation &pi1, const KisVector2D &control1, const KisVector2D &control2, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { LineEquation line = LineEquation::Through(toKisVector2D(pi1.pos()), toKisVector2D(pi2.pos())); qreal d1 = line.absDistance(control1); qreal d2 = line.absDistance(control2); if ((d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) || qIsNaN(d1) || qIsNaN(d2)) { paintOp->paintLine(pi1, pi2, currentDistance); } else { // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 KisVector2D l2 = (toKisVector2D(pi1.pos()) + control1) / 2; KisVector2D h = (control1 + control2) / 2; KisVector2D l3 = (l2 + h) / 2; KisVector2D r3 = (control2 + toKisVector2D(pi2.pos())) / 2; KisVector2D r2 = (h + r3) / 2; KisVector2D l4 = (l3 + r2) / 2; KisPaintInformation middlePI = KisPaintInformation::mix(toQPointF(l4), 0.5, pi1, pi2); paintBezierCurve(paintOp, pi1, l2, l3, middlePI, currentDistance); paintBezierCurve(paintOp, middlePI, r2, r3, pi2, currentDistance); } } void KisPaintOp::paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { return ::paintBezierCurve(this, pi1, toKisVector2D(control1), toKisVector2D(control2), pi2, currentDistance); } void KisPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { KisPaintOpUtils::paintLine(*this, pi1, pi2, currentDistance, d->fanCornersEnabled, d->fanCornersStep); } void KisPaintOp::paintAt(const KisPaintInformation& info, KisDistanceInformation *currentDistance) { Q_ASSERT(currentDistance); KisPaintInformation pi(info); pi.paintAt(*this, currentDistance); } KisPainter* KisPaintOp::painter() const { return d->painter; } KisPaintDeviceSP KisPaintOp::source() const { return d->painter->device(); } - -qreal KisPaintOp::currentRotation() const -{ - return d->currentRotation; -} - -qreal KisPaintOp::currentScale() const -{ - return d->currentScale; -} - -void KisPaintOp::setCurrentRotation(qreal rotation) -{ - d->currentRotation = rotation; -} - -void KisPaintOp::setCurrentScale(qreal scale) -{ - d->currentScale = scale; -} - diff --git a/libs/image/brushengine/kis_paintop.h b/libs/image/brushengine/kis_paintop.h index f12d8e77bd..adec15f84f 100644 --- a/libs/image/brushengine/kis_paintop.h +++ b/libs/image/brushengine/kis_paintop.h @@ -1,143 +1,129 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004,2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTOP_H_ #define KIS_PAINTOP_H_ #include #include "kis_shared.h" #include "kis_types.h" #include class QPointF; class KoColorSpace; class KisPainter; class KisPaintInformation; /** * KisPaintOp are use by tools to draw on a paint device. A paintop takes settings * and input information, like pressure, tilt or motion and uses that to draw pixels */ class KRITAIMAGE_EXPORT KisPaintOp : public KisShared { struct Private; public: KisPaintOp(KisPainter * painter); virtual ~KisPaintOp(); /** * Paint at the subpixel point pos using the specified paint * information.. * * The distance between two calls of the paintAt is always * specified by spacing, which is automatically saved into the * current distance information object */ void paintAt(const KisPaintInformation& info, KisDistanceInformation *currentDistance); /** * Draw a line between pos1 and pos2 using the currently set brush and color. * If savedDist is less than zero, the brush is painted at pos1 before being * painted along the line using the spacing setting. * * @return the drag distance, that is the remains of the distance * between p1 and p2 not covered because the currenlty set brush * has a spacing greater than that distance. */ virtual void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance); /** * Draw a Bezier curve between pos1 and pos2 using control points 1 and 2. * If savedDist is less than zero, the brush is painted at pos1 before being * painted along the curve using the spacing setting. * @return the drag distance, that is the remains of the distance between p1 and p2 not covered * because the currenlty set brush has a spacing greater than that distance. */ virtual void paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance); /** * Whether this paintop can paint. Can be false in case that some setting isn't read correctly. * @return if paintop is ready for painting, default is true */ virtual bool canPaint() const { return true; } /** * Split the coordinate into whole + fraction, where fraction is always >= 0. */ static void splitCoordinate(qreal coordinate, qint32 *whole, qreal *fraction); - /** - * returns the scale and rotation dynamically computed for the single dab - * rotation is in radians - * This is used e.g. for dynamic brush outline - */ - qreal currentScale() const; - qreal currentRotation() const; - protected: friend class KisPaintInformation; /** * The implementation of painting of a dab */ virtual KisSpacingInformation paintAt(const KisPaintInformation& info) = 0; - void setCurrentScale(qreal scale); - /** - * Set rotation in radians - */ - void setCurrentRotation(qreal rotation); - KisFixedPaintDeviceSP cachedDab(); KisFixedPaintDeviceSP cachedDab(const KoColorSpace *cs); /** * Return the painter this paintop is owned by */ KisPainter* painter() const; /** * Return the paintdevice the painter this paintop is owned by */ KisPaintDeviceSP source() const; private: friend class KisPressureRotationOption; void setFanCornersInfo(bool fanCornersEnabled, qreal fanCornersStep); private: Private* const d; }; #endif // KIS_PAINTOP_H_ diff --git a/libs/image/kis_image_lock_hijacker.h b/libs/image/kis_image_lock_hijacker.h new file mode 100644 index 0000000000..e2f928ecc7 --- /dev/null +++ b/libs/image/kis_image_lock_hijacker.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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_LOCK_HIJACKER_H +#define __KIS_IMAGE_LOCK_HIJACKER_H + +#include +#include + + +#include "kis_image.h" +#include "kis_debug.h" + +/** + * This class removes all the locks from the image on construction and + * resets them back on destruction. Never use this class unless you + * really know what you are doing! + * + * Never! + */ + +class KisImageLockHijacker +{ +public: + KisImageLockHijacker(KisImageSP image) + : m_image(image), + m_count(0) + { + QThread *currentThread = QThread::currentThread(); + KIS_ASSERT_RECOVER_RETURN(image->thread() == currentThread); + KIS_ASSERT_RECOVER_RETURN(qApp->thread() == currentThread); + + while(image->locked()) { + m_count++; + m_image->unlock(); + } + } + + ~KisImageLockHijacker() { + while (m_count--) { + m_image->barrierLock(); + } + } + +private: + KisImageSP m_image; + int m_count; +}; + +#endif /* __KIS_IMAGE_LOCK_HIJACKER_H */ diff --git a/libs/pigment/KoBasicHistogramProducers.cpp b/libs/pigment/KoBasicHistogramProducers.cpp index deab2d5ab1..2b5d1431cc 100644 --- a/libs/pigment/KoBasicHistogramProducers.cpp +++ b/libs/pigment/KoBasicHistogramProducers.cpp @@ -1,517 +1,525 @@ /* * Copyright (c) 2005 Bart Coppens * * 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 "KoBasicHistogramProducers.h" #include #include #include #ifdef HAVE_OPENEXR #include #endif // #include "Ko_global.h" #include "KoIntegerMaths.h" #include "KoChannelInfo.h" static const KoColorSpace* m_labCs = 0; KoBasicHistogramProducer::KoBasicHistogramProducer(const KoID& id, int channelCount, int nrOfBins) : m_channels(channelCount) , m_nrOfBins(nrOfBins) , m_colorSpace(0) , m_id(id) { m_bins.resize(m_channels); for (int i = 0; i < m_channels; i++) m_bins[i].resize(m_nrOfBins); m_outLeft.resize(m_channels); m_outRight.resize(m_channels); m_count = 0; m_from = 0.0; m_width = 1.0; } KoBasicHistogramProducer::KoBasicHistogramProducer(const KoID& id, int nrOfBins, const KoColorSpace *cs) : m_nrOfBins(nrOfBins), m_colorSpace(cs), m_id(id) { Q_ASSERT(cs); m_channels = cs->channelCount(); m_bins.resize(m_channels); for (int i = 0; i < m_channels; i++) m_bins[i].resize(m_nrOfBins); m_outLeft.resize(m_channels); m_outRight.resize(m_channels); m_count = 0; m_from = 0.0; m_width = 1.0; } void KoBasicHistogramProducer::clear() { m_count = 0; for (int i = 0; i < m_channels; i++) { for (int j = 0; j < m_nrOfBins; j++) { m_bins[i][j] = 0; } m_outRight[i] = 0; m_outLeft[i] = 0; } } void KoBasicHistogramProducer::makeExternalToInternal() { // This function assumes that the pixel is has no 'gaps'. That is to say: if we start // at byte 0, we can get to the end of the pixel by adding consecutive size()s of // the channels QList c = channels(); uint count = c.count(); int currentPos = 0; for (uint i = 0; i < count; i++) { for (uint j = 0; j < count; j++) { if (c[j]->pos() == currentPos) { m_external.append(j); break; } } currentPos += c.at(m_external.at(m_external.count() - 1))->size(); } } // ------------ U8 --------------------- KoBasicU8HistogramProducer::KoBasicU8HistogramProducer(const KoID& id, const KoColorSpace *cs) : KoBasicHistogramProducer(id, 256, cs) { } QString KoBasicU8HistogramProducer::positionToString(qreal pos) const { return QString("%1").arg(static_cast(pos * UINT8_MAX)); } void KoBasicU8HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) { - qint32 pSize = cs->pixelSize(); + quint32 dstPixelSize = m_colorSpace->pixelSize(); + quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; + cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); if (selectionMask) { + quint8 *dst = dstPixels; while (nPixels > 0) { - if (!(m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { + if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - m_bins[i][pixels[i]]++; + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + m_bins[i][m_colorSpace->scaleToU8(dst,i)]++; } m_count++; - } - - pixels += pSize; + dst += dstPixelSize; selectionMask++; nPixels--; } } else { + quint8 *dst = dstPixels; while (nPixels > 0) { if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - m_bins[i][pixels[i]]++; + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + m_bins[i][m_colorSpace->scaleToU8(dst,i)]++; } m_count++; - } - - pixels += pSize; + dst += dstPixelSize; nPixels--; } } } // ------------ U16 --------------------- KoBasicU16HistogramProducer::KoBasicU16HistogramProducer(const KoID& id, const KoColorSpace *cs) : KoBasicHistogramProducer(id, 256, cs) { } QString KoBasicU16HistogramProducer::positionToString(qreal pos) const { return QString("%1").arg(static_cast(pos * UINT8_MAX)); } qreal KoBasicU16HistogramProducer::maximalZoom() const { return 1.0 / 255.0; } void KoBasicU16HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) { // The view quint16 from = static_cast(m_from * UINT16_MAX); quint16 width = static_cast(m_width * UINT16_MAX + 0.5); // We include the end quint16 to = from + width; qreal factor = 255.0 / width; - qint32 pSize = cs->pixelSize(); + quint32 dstPixelSize = m_colorSpace->pixelSize(); + quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; + cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); + quint8 *dst = dstPixels; + QVector channels(m_colorSpace->channelCount()); if (selectionMask) { - const quint16* pixel = reinterpret_cast(pixels); while (nPixels > 0) { if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { - for (int i = 0; i < m_channels; i++) { - quint16 value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + quint16 value = channels[i]*UINT16_MAX; if (value > to) m_outRight[i]++; else if (value < from) m_outLeft[i]++; else m_bins[i][static_cast((value - from) * factor)]++; } m_count++; } - pixels += pSize; + dst += dstPixelSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { - const quint16* pixel = reinterpret_cast(pixels); - if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - quint16 value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + quint16 value = channels[i]*UINT16_MAX; + if (value > to) m_outRight[i]++; else if (value < from) m_outLeft[i]++; else m_bins[i][static_cast((value - from) * factor)]++; } m_count++; } - pixels += pSize; + dst += dstPixelSize; nPixels--; - } } } // ------------ Float32 --------------------- KoBasicF32HistogramProducer::KoBasicF32HistogramProducer(const KoID& id, const KoColorSpace *cs) : KoBasicHistogramProducer(id, 256, cs) { } QString KoBasicF32HistogramProducer::positionToString(qreal pos) const { return QString("%1").arg(static_cast(pos)); // XXX I doubt this is correct! } qreal KoBasicF32HistogramProducer::maximalZoom() const { // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromis for the moment return 1.0 / 255.0; } void KoBasicF32HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) { // The view float from = static_cast(m_from); float width = static_cast(m_width); float to = from + width; float factor = 255.0 / width; - qint32 pSize = cs->pixelSize(); + quint32 dstPixelSize = m_colorSpace->pixelSize(); + quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; + cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); + quint8 *dst = dstPixels; + QVector channels(m_colorSpace->channelCount()); if (selectionMask) { while (nPixels > 0) { - - const float* pixel = reinterpret_cast(pixels); if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { - for (int i = 0; i < m_channels; i++) { - float value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + float value = channels[i]; if (value > to) m_outRight[i]++; else if (value < from) m_outLeft[i]++; else m_bins[i][static_cast((value - from) * factor)]++; } m_count++; } - - pixels += pSize; + dst += dstPixelSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { - - const float* pixel = reinterpret_cast(pixels); if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - float value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + float value = channels[i]; if (value > to) m_outRight[i]++; else if (value < from) m_outLeft[i]++; else m_bins[i][static_cast((value - from) * factor)]++; } m_count++; } - - pixels += pSize; + dst += dstPixelSize; nPixels--; } } } #ifdef HAVE_OPENEXR // ------------ Float16 Half --------------------- KoBasicF16HalfHistogramProducer::KoBasicF16HalfHistogramProducer(const KoID& id, const KoColorSpace *cs) : KoBasicHistogramProducer(id, 256, cs) { } QString KoBasicF16HalfHistogramProducer::positionToString(qreal pos) const { return QString("%1").arg(static_cast(pos)); // XXX I doubt this is correct! } qreal KoBasicF16HalfHistogramProducer::maximalZoom() const { // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromis for the moment return 1.0 / 255.0; } void KoBasicF16HalfHistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) { // The view float from = static_cast(m_from); float width = static_cast(m_width); float to = from + width; float factor = 255.0 / width; - qint32 pSize = cs->pixelSize(); + quint32 dstPixelSize = m_colorSpace->pixelSize(); + quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; + cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); + quint8 *dst = dstPixels; + QVector channels(m_colorSpace->channelCount()); + if (selectionMask) { while (nPixels > 0) { - const half* pixel = reinterpret_cast(pixels); if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { - for (int i = 0; i < m_channels; i++) { - float value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + float value = channels[i]; if (value > to) m_outRight[i]++; else if (value < from) m_outLeft[i]++; else m_bins[i][static_cast((value - from) * factor)]++; } m_count++; } - pixels += pSize; + dst += dstPixelSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { - const half* pixel = reinterpret_cast(pixels); if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - float value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + float value = channels[i]; if (value > to) m_outRight[i]++; else if (value < from) m_outLeft[i]++; else m_bins[i][static_cast((value - from) * factor)]++; } m_count++; } - pixels += pSize; + dst += dstPixelSize; nPixels--; } } } #endif // ------------ Generic RGB --------------------- KoGenericRGBHistogramProducer::KoGenericRGBHistogramProducer() : KoBasicHistogramProducer(KoID("GENRGBHISTO", i18n("Generic RGB Histogram")), 3, 256) { /* we set 0 as colorspece, because we are not based on a specific colorspace. This is no problem for the superclass since we override channels() */ m_channelsList.append(new KoChannelInfo(i18n("R"), 0, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0))); m_channelsList.append(new KoChannelInfo(i18n("G"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0))); m_channelsList.append(new KoChannelInfo(i18n("B"), 2, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255))); } QList KoGenericRGBHistogramProducer::channels() { return m_channelsList; } QString KoGenericRGBHistogramProducer::positionToString(qreal pos) const { return QString("%1").arg(static_cast(pos * UINT8_MAX)); } qreal KoGenericRGBHistogramProducer::maximalZoom() const { return 1.0; } void KoGenericRGBHistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) { for (int i = 0; i < m_channels; i++) { m_outRight[i] = 0; m_outLeft[i] = 0; } QColor c; qint32 pSize = cs->pixelSize(); if (selectionMask) { while (nPixels > 0) { if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { cs->toQColor(pixels, &c); m_bins[0][c.red()]++; m_bins[1][c.green()]++; m_bins[2][c.blue()]++; m_count++; } pixels += pSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { cs->toQColor(pixels, &c); m_bins[0][c.red()]++; m_bins[1][c.green()]++; m_bins[2][c.blue()]++; m_count++; } pixels += pSize; nPixels--; } } } KoGenericRGBHistogramProducerFactory::KoGenericRGBHistogramProducerFactory() : KoHistogramProducerFactory(KoID("GENRGBHISTO", i18n("Generic RGB Histogram"))) { } // ------------ Generic L*a*b* --------------------- KoGenericLabHistogramProducer::KoGenericLabHistogramProducer() : KoBasicHistogramProducer(KoID("GENLABHISTO", i18n("L*a*b* Histogram")), 3, 256) { /* we set 0 as colorspace, because we are not based on a specific colorspace. This is no problem for the superclass since we override channels() */ m_channelsList.append(new KoChannelInfo(i18n("L*"), 0, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8)); m_channelsList.append(new KoChannelInfo(i18n("a*"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8)); m_channelsList.append(new KoChannelInfo(i18n("b*"), 2, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8)); if (!m_labCs) { m_labCs = KoColorSpaceRegistry::instance()->lab16(); } m_colorSpace = m_labCs; } KoGenericLabHistogramProducer::~KoGenericLabHistogramProducer() { delete m_channelsList[0]; delete m_channelsList[1]; delete m_channelsList[2]; } QList KoGenericLabHistogramProducer::channels() { return m_channelsList; } QString KoGenericLabHistogramProducer::positionToString(qreal pos) const { return QString("%1").arg(static_cast(pos * UINT16_MAX)); } qreal KoGenericLabHistogramProducer::maximalZoom() const { return 1.0; } void KoGenericLabHistogramProducer::addRegionToBin(const quint8 *pixels, const quint8 *selectionMask, quint32 nPixels, const KoColorSpace *cs) { for (int i = 0; i < m_channels; i++) { m_outRight[i] = 0; m_outLeft[i] = 0; } qint32 dstPixelSize = m_colorSpace->pixelSize(); quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); qint32 pSize = cs->pixelSize(); if (selectionMask) { while (nPixels > 0) { if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { m_count++; } pixels += pSize; selectionMask++; nPixels--; } } else { quint8 *dst = dstPixels; while (nPixels > 0) { if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { m_bins[0][m_colorSpace->scaleToU8(dst, 0)]++; m_bins[1][m_colorSpace->scaleToU8(dst, 1)]++; m_bins[2][m_colorSpace->scaleToU8(dst, 2)]++; m_count++; } dst+= dstPixelSize; nPixels--; } } delete[] dstPixels; } KoGenericLabHistogramProducerFactory::KoGenericLabHistogramProducerFactory() : KoHistogramProducerFactory(KoID("GENLABHISTO", i18n("Generic L*a*b* Histogram"))) { } diff --git a/libs/pigment/KoBasicHistogramProducers.h b/libs/pigment/KoBasicHistogramProducers.h index 9f89ddae5e..d62675a977 100644 --- a/libs/pigment/KoBasicHistogramProducers.h +++ b/libs/pigment/KoBasicHistogramProducers.h @@ -1,257 +1,262 @@ /* * Copyright (c) 2005 Bart Coppens * * 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 _Ko_BASIC_HISTOGRAM_PRODUCERS_ #define _Ko_BASIC_HISTOGRAM_PRODUCERS_ #include "KoHistogramProducer.h" #include #include #include "KoColorSpace.h" #include "KoID.h" #include "kritapigment_export.h" #include "KoColorSpaceRegistry.h" class KRITAPIGMENT_EXPORT KoBasicHistogramProducer : public KoHistogramProducer { public: explicit KoBasicHistogramProducer(const KoID& id, int channelCount, int nrOfBins); explicit KoBasicHistogramProducer(const KoID& id, int nrOfBins, const KoColorSpace *colorSpace); virtual ~KoBasicHistogramProducer() {} virtual void clear(); virtual void setView(qreal from, qreal size) { m_from = from; m_width = size; } virtual const KoID& id() const { return m_id; } virtual QList channels() { return m_colorSpace->channels(); } virtual qint32 numberOfBins() { return m_nrOfBins; } virtual qreal viewFrom() const { return m_from; } virtual qreal viewWidth() const { return m_width; } virtual qint32 count() { return m_count; } virtual qint32 getBinAt(int channel, int position) { return m_bins.at(externalToInternal(channel)).at(position); } virtual qint32 outOfViewLeft(int channel) { return m_outLeft.at(externalToInternal(channel)); } virtual qint32 outOfViewRight(int channel) { return m_outRight.at(externalToInternal(channel)); } protected: /** * The order in which channels() returns is not the same as the internal representation, * that of the pixel internally. This method converts external usage to internal usage. * This method uses some basic assumtpions about the layout of the pixel, so _extremely_ * exotic spaces might want to override this (see makeExternalToInternal source for * those assumptions) **/ virtual int externalToInternal(int ext) { if (channels().count() > 0 && m_external.count() == 0) // Set up the translation table makeExternalToInternal(); return m_external.at(ext); } // not virtual since that is useless: we call it from constructor void makeExternalToInternal(); typedef QVector vBins; QVector m_bins; vBins m_outLeft, m_outRight; qreal m_from, m_width; qint32 m_count; int m_channels, m_nrOfBins; const KoColorSpace *m_colorSpace; KoID m_id; QVector m_external; }; class KRITAPIGMENT_EXPORT KoBasicU8HistogramProducer : public KoBasicHistogramProducer { public: KoBasicU8HistogramProducer(const KoID& id, const KoColorSpace *colorSpace); virtual void addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *colorSpace); virtual QString positionToString(qreal pos) const; virtual qreal maximalZoom() const { return 1.0; } }; class KRITAPIGMENT_EXPORT KoBasicU16HistogramProducer : public KoBasicHistogramProducer { public: KoBasicU16HistogramProducer(const KoID& id, const KoColorSpace *colorSpace); virtual void addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *colorSpace); virtual QString positionToString(qreal pos) const; virtual qreal maximalZoom() const; }; class KRITAPIGMENT_EXPORT KoBasicF32HistogramProducer : public KoBasicHistogramProducer { public: KoBasicF32HistogramProducer(const KoID& id, const KoColorSpace *colorSpace); virtual void addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *colorSpace); virtual QString positionToString(qreal pos) const; virtual qreal maximalZoom() const; }; #ifdef HAVE_OPENEXR class KRITAPIGMENT_EXPORT KoBasicF16HalfHistogramProducer : public KoBasicHistogramProducer { public: KoBasicF16HalfHistogramProducer(const KoID& id, const KoColorSpace *colorSpace); virtual void addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *colorSpace); virtual QString positionToString(qreal pos) const; virtual qreal maximalZoom() const; }; #endif /** * Parametrized on a specific KoHistogramProducer. Its generated producers * will have the same KoID as the factory's. This is acceptable because we can't mix * Factories with Producers in the code because they are incompatible types, and * in the GUI we actually only need a producer's name, not a factory's. */ template class KoBasicHistogramProducerFactory : public KoHistogramProducerFactory { public: KoBasicHistogramProducerFactory(const KoID& id, const QString& modelId, const QString& depthId ) : KoHistogramProducerFactory(id), m_modelId(modelId), m_depthId(depthId) { } virtual ~KoBasicHistogramProducerFactory() {} virtual KoHistogramProducer *generate() { KoHistogramProducer *producer = 0; const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(m_modelId, m_depthId, 0); if (cs) { producer = new T(KoID(id(), name()), cs); } return producer; } - virtual bool isCompatibleWith(const KoColorSpace* colorSpace) const { + virtual bool isCompatibleWith(const KoColorSpace* colorSpace, bool strict = false) const { + if( strict ){ + return colorSpace->colorDepthId().id() == m_depthId; + } return colorSpace->colorModelId().id() == m_modelId || colorSpace->colorDepthId().id() == m_depthId; } virtual float preferrednessLevelWith(const KoColorSpace* colorSpace) const { return 0.5 * ( (colorSpace->colorModelId().id() == m_modelId) + (colorSpace->colorDepthId().id() == m_depthId) ); } protected: QString m_modelId, m_depthId; }; /** * This is a Producer (with associated factory) that converts the pixels of the colorspace * to RGB8 with toQColor, and then does its counting on RGB. This is NOT registered with the * Registry, because it isCompatibleWith all colorspaces, and should only be used in extreme * cases (like no other producer being available **/ class KRITAPIGMENT_EXPORT KoGenericRGBHistogramProducer : public KoBasicHistogramProducer { public: KoGenericRGBHistogramProducer(); virtual void addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *colorSpace); virtual QString positionToString(qreal pos) const; virtual qreal maximalZoom() const; virtual QList channels(); protected: QList m_channelsList; }; /** KoGenericRGBHistogramProducer his special Factory that isCompatibleWith everything. */ class KRITAPIGMENT_EXPORT KoGenericRGBHistogramProducerFactory : public KoHistogramProducerFactory { public: KoGenericRGBHistogramProducerFactory(); virtual ~KoGenericRGBHistogramProducerFactory() {} virtual KoHistogramProducer *generate() { return new KoGenericRGBHistogramProducer(); } - virtual bool isCompatibleWith(const KoColorSpace*) const { + virtual bool isCompatibleWith(const KoColorSpace*, bool strict = false) const { + Q_UNUSED(strict); return true; } virtual float preferrednessLevelWith(const KoColorSpace*) const { return 0.0; } }; /** * This is a Producer (with associated factory) that converts the pixels of the colorspace * to L*a*b*, and then does its counting. * It isCompatibleWith all colorspaces **/ class KRITAPIGMENT_EXPORT KoGenericLabHistogramProducer : public KoBasicHistogramProducer { public: KoGenericLabHistogramProducer(); virtual ~KoGenericLabHistogramProducer(); virtual void addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *colorSpace); virtual QString positionToString(qreal pos) const; virtual qreal maximalZoom() const; virtual QList channels(); protected: QList m_channelsList; }; /** KoGenericLabHistogramProducer his special Factory that isCompatibleWith everything. */ class /*KRITAPIGMENT_EXPORT*/ KoGenericLabHistogramProducerFactory : public KoHistogramProducerFactory { public: KoGenericLabHistogramProducerFactory(); virtual ~KoGenericLabHistogramProducerFactory() {} virtual KoHistogramProducer *generate() { return new KoGenericLabHistogramProducer(); } - virtual bool isCompatibleWith(const KoColorSpace*) const { + virtual bool isCompatibleWith(const KoColorSpace*, bool strict = false) const { + Q_UNUSED(strict); return true; } virtual float preferrednessLevelWith(const KoColorSpace*) const { return 0.0; } }; #endif // _Ko_BASIC_HISTOGRAM_PRODUCERS_ diff --git a/libs/pigment/KoHistogramProducer.cpp b/libs/pigment/KoHistogramProducer.cpp index 5534e1f381..0d30a3e1c1 100644 --- a/libs/pigment/KoHistogramProducer.cpp +++ b/libs/pigment/KoHistogramProducer.cpp @@ -1,69 +1,70 @@ /* * Copyright (c) 2005 Bart Coppens * * 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 "KoHistogramProducer.h" #include #include #include #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" Q_GLOBAL_STATIC(KoHistogramProducerFactoryRegistry, s_instance) KoHistogramProducerFactoryRegistry::KoHistogramProducerFactoryRegistry() { } KoHistogramProducerFactoryRegistry::~KoHistogramProducerFactoryRegistry() { qDeleteAll(values()); } KoHistogramProducerFactoryRegistry* KoHistogramProducerFactoryRegistry::instance() { return s_instance; } -QList KoHistogramProducerFactoryRegistry::keysCompatibleWith(const KoColorSpace* colorSpace) const +QList KoHistogramProducerFactoryRegistry::keysCompatibleWith(const KoColorSpace* colorSpace, bool isStrict) const { QList list; QList preferredList; Q_FOREACH (const QString &id, keys()) { KoHistogramProducerFactory *f = value(id); - if (f->isCompatibleWith(colorSpace)) { + + if (f->isCompatibleWith(colorSpace, isStrict)) { float preferred = f->preferrednessLevelWith(colorSpace); QList::iterator pit = preferredList.begin(); QList::iterator pend = preferredList.end(); QList::iterator lit = list.begin(); while (pit != pend && preferred <= *pit) { ++pit; ++lit; } list.insert(lit, id); preferredList.insert(pit, preferred); } } return list; } diff --git a/libs/pigment/KoHistogramProducer.h b/libs/pigment/KoHistogramProducer.h index b97a334f1f..fc9956dcf3 100644 --- a/libs/pigment/KoHistogramProducer.h +++ b/libs/pigment/KoHistogramProducer.h @@ -1,143 +1,143 @@ /* * Copyright (c) 2005 Bart Coppens * * 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 _KO_HISTOGRAM_PRODUCER_ #define _KO_HISTOGRAM_PRODUCER_ #include #include "kritapigment_export.h" #include #include class QString; class KoChannelInfo; class KoColorSpace; /** * This class is an interface used in the generation of a histogram. It is a container of * data, all mathematically interesting things will calculated by an histogram. * * The default view will be the entire range each color can be in. And don't let the * numberOfBins return anything else then 256 unless you have a very good reason for it. * * About the views: a view is a zoom combined with a start level: the entire * range of a channel is 0.0 - 1.0: this is the position. Combined with a zoom, we can * calculate what part of a channel will fall in a bin. This gives us an interface to * that the views that is not dependent of the actual colorspace of the histogram. * The 'size' value is the size, again from 0.0 to 1.0 of the displayed range. * * For comfort of the GUI, and because it is logical, channels are accessed in the order * in which they are found in the channels() method. This is potentially different from * the order in which they are internally ordered! **/ class KRITAPIGMENT_EXPORT KoHistogramProducer { public: KoHistogramProducer() : m_skipTransparent(true), m_skipUnselected(true) {} virtual ~KoHistogramProducer() {} // Methods to change the bins /** Clears the data in this producer, but keeps its other settings */ virtual void clear() = 0; /** * Adds the values from the specified array of pixels to the bins -- does not * reset anything. * * @param pixels A pointer an array of pixeldata in the given colorspace * @param selectionMask a pointer to an array of bytes, where 0 is unselected and 1-255 is degree of selectedness. The array * must be just as long as the array of pixels. * @param nPixels The number of pixels * @param colorSpace the colorspace that can decode the pixel data. */ virtual void addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace* colorSpace) = 0; // Methods to set what exactly is being added to the bins virtual void setView(qreal from, qreal width) = 0; virtual void setSkipTransparent(bool set) { m_skipTransparent = set; } virtual void setSkipUnselected(bool set) { m_skipUnselected = set; } // Methods with general information about this specific producer virtual const KoID& id() const = 0; virtual QList channels() = 0; virtual qint32 numberOfBins() = 0; virtual QString positionToString(qreal pos) const = 0; virtual qreal viewFrom() const = 0; virtual qreal viewWidth() const = 0; virtual qreal maximalZoom() const = 0; // Methods to get information on the data we have seen virtual qint32 count() = 0; virtual qint32 getBinAt(qint32 channel, qint32 position) = 0; virtual qint32 outOfViewLeft(qint32 channel) = 0; virtual qint32 outOfViewRight(qint32 channel) = 0; protected: bool m_skipTransparent; bool m_skipUnselected; }; class KRITAPIGMENT_EXPORT KoHistogramProducerFactory { public: explicit KoHistogramProducerFactory(const KoID &id) : m_id(id) {} virtual ~KoHistogramProducerFactory() {} /// Factory method, generates a new KoHistogramProducer virtual KoHistogramProducer *generate() = 0; /// Returns if a colorspace can be used with this producer - virtual bool isCompatibleWith(const KoColorSpace* colorSpace) const = 0; + virtual bool isCompatibleWith(const KoColorSpace* colorSpace, bool strict = false) const = 0; /// Returns a float in the [0.0, 1.0] range, 0.0 means this is a very generic method virtual float preferrednessLevelWith(const KoColorSpace* colorSpace) const = 0; virtual QString id() const { return m_id.id(); } virtual QString name() const { return m_id.name(); } protected: KoID m_id; }; class KRITAPIGMENT_EXPORT KoHistogramProducerFactoryRegistry : public KoGenericRegistry { public: KoHistogramProducerFactoryRegistry(); virtual ~KoHistogramProducerFactoryRegistry(); static KoHistogramProducerFactoryRegistry* instance(); /// returns a list, sorted by preferrence: higher preferance comes first - QList keysCompatibleWith(const KoColorSpace* colorSpace) const; + QList keysCompatibleWith(const KoColorSpace* colorSpace, bool isStrict=false) const; private: KoHistogramProducerFactoryRegistry(const KoHistogramProducerFactoryRegistry&); KoHistogramProducerFactoryRegistry operator=(const KoHistogramProducerFactoryRegistry&); }; #endif // _KO_HISTOGRAM_PRODUCER diff --git a/libs/ui/KisTemplateCreateDia.cpp b/libs/ui/KisTemplateCreateDia.cpp index b501f3da89..e0886e3875 100644 --- a/libs/ui/KisTemplateCreateDia.cpp +++ b/libs/ui/KisTemplateCreateDia.cpp @@ -1,523 +1,517 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Reginald Stadlbauer 2000 Werner Trobin Copyright (C) 2004 Nicolas GOUTTE 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 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->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())); + 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) { - QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/krita_XXXXXX") + QLatin1String(QLatin1String(suffix))); - //Check that creation of temp file was successful - if (!tempFile->open()) { - delete tempFile; - qWarning("Creation of temporary file to store template failed."); + 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); + if (!retval) { + qWarning("Could not save template"); return; } - const QString fileName = tempFile->fileName(); - tempFile->close(); // need to close on Windows before we can open it again to save - delete tempFile; // now the file has disappeared and we can create a new file with the generated name - - document->saveNativeFormat(fileName); - const QPixmap thumbnail = document->generatePreview(QSize(thumbnailExtent, thumbnailExtent)); - KisTemplateCreateDia *dia = new KisTemplateCreateDia(templatesResourcePath, 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/"; + 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 file = KisTemplates::trimmed(d->m_name->text()); + QString tmpIcon = ".icon/"+file; + tmpIcon += ".png"; QString icon=iconDir+file; - icon+=".png"; + icon += ".png"; - // try to find the extension for the template file :P - const int pos = d->m_filePath.lastIndexOf(QLatin1Char('.')); - QString ext; - if ( pos > -1 ) - ext = d->m_filePath.mid(pos); - else - warnUI << "Template extension not found!"; + QString ext = ".kra"; - QUrl dest; - dest.setPath(templateDir+file+ext); - if (QFile::exists(dest.toLocalFile())) { + QString dest = templateDir + file + ext; + if (QFile::exists(dest)) { do { - file.prepend( '_' ); - dest.setPath( templateDir + file + ext ); + file = file.prepend( '_' ); + dest = templateDir + file + ext; tmpIcon=".icon/" + file + ".png"; icon=iconDir + file + ".png"; } - while (QFile(dest.toLocalFile()).exists()); + while (QFile(dest).exists()); } bool ignore = false; - dbgUI <<"Trying to create template:" << d->m_name->text() <<"URL=" <<".source/"+file+ext <<" ICON=" << tmpIcon; - KisTemplate *t=new KisTemplate(d->m_name->text(), QString(), ".source/"+file+ext, tmpIcon, "", "", false, true); + 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; } - QUrl orig; - orig.setPath(d->m_filePath); + QString orig; + orig = d->m_filePath; // don't overwrite the hidden template file with a new non-hidden one - if ( !ignore ) - { - QFile::copy(d->m_filePath, dest.toLocalFile()); + if (!ignore) { + if (!QFile::copy(d->m_filePath, dest)) { + qWarning() << QString("Could not copy %1 to %2.").arg(d->m_filePath).arg(dest).toUtf8(); + } + // 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.setPath( (*it)+".directory" ); + orig = (*it) + ".directory"; // Check if we can read the file - if (QFile(orig.toLocalFile()).exists()) { - dest.setPath(dir + "/.directory"); + if (QFile(orig).exists()) { + dest = dir + "/.directory"; // We copy the file with overwrite - if (!QFile(orig.toLocalFile()).copy(dest.toLocalFile())) { - warnKrita << "Failed to copy from" << orig.toLocalFile() << "to" << dest.toLocalFile(); + 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 groups = d->m_tree.groups(); QList::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/canvas/kis_mirror_axis.cpp b/libs/ui/canvas/kis_mirror_axis.cpp index 468dbf4ad8..8a0d6ca48a 100644 --- a/libs/ui/canvas/kis_mirror_axis.cpp +++ b/libs/ui/canvas/kis_mirror_axis.cpp @@ -1,359 +1,406 @@ /* * Copyright (c) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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 #include #include #include #include #include #include #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, QPointerparent) : 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); - int imageWidth = parent->canvasBase()->image()->width(); - int imageHeight = parent->canvasBase()->image()->height(); + + 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) { + 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(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); - 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); + // 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) { + 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(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); - // gc.drawEllipse(verticalIndicator); - // gc.drawPixmap(verticalIndicator.adjusted(5, 5, -5, -5).toRect(), d->verticalIcon); - 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); + // 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(event); QTabletEvent *te = dynamic_cast(event); QPoint pos = me ? me->pos() : (te ? te->pos() : QPoint(77,77)); - if(d->mirrorHorizontal && d->horizontalHandle.contains(pos)) { + + + + 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)) { + 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(event); QTabletEvent *te = dynamic_cast(event); QPoint pos = me ? me->pos() : (te ? te->pos() : QPoint(77,77)); if(d->xActive) { float axisX = view()->viewConverter()->widgetToImage(pos).x(); d->setAxisPosition(axisX, d->axisPosition.y()); d->horizontalHandlePosition = KisAlgebra2D::dotProduct(pos - d->horizontalAxis.p1(), d->horizontalAxis.unitVector().p2() - d->horizontalAxis.p1()); event->accept(); return true; } if(d->yActive) { float axisY = view()->viewConverter()->widgetToImage(pos).y(); d->setAxisPosition(d->axisPosition.x(), axisY); d->verticalHandlePosition = KisAlgebra2D::dotProduct(pos - d->verticalAxis.p1(), d->verticalAxis.unitVector().p2() - d->verticalAxis.p1()); event->accept(); return true; } - if(d->mirrorHorizontal) { - if(d->horizontalHandle.contains(pos)) { + 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) { - if(d->verticalHandle.contains(pos)) { + 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); q->view()->canvasBase()->updateCanvas(); } + void KisMirrorAxis::Private::recomputeVisibleAxes(QRect viewport) { KisCoordinatesConverter *converter = q->view()->viewConverter(); axisPosition = resourceProvider->resourceManager()->resource(KisCanvasResourceProvider::MirrorAxesCenter).toPointF(); QPointF samplePt1 = converter->imageToWidget(axisPosition); QPointF samplePt2 = converter->imageToWidget(QPointF(axisPosition.x(), axisPosition.y() - 100)); horizontalAxis = QLineF(samplePt1, samplePt2); if (!KisAlgebra2D::intersectLineRect(horizontalAxis, viewport)) horizontalAxis = QLineF(); samplePt2 = converter->imageToWidget(QPointF(axisPosition.x() - 100, axisPosition.y())); verticalAxis = QLineF(samplePt1, samplePt2); if (!KisAlgebra2D::intersectLineRect(verticalAxis, viewport)) verticalAxis = QLineF(); } diff --git a/libs/ui/canvas/kis_mirror_axis.h b/libs/ui/canvas/kis_mirror_axis.h index 99eda39cf6..5c5a3b0078 100644 --- a/libs/ui/canvas/kis_mirror_axis.h +++ b/libs/ui/canvas/kis_mirror_axis.h @@ -1,56 +1,58 @@ /* * Copyright (c) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 KISMIRRORAXIS_H #define KISMIRRORAXIS_H #include "kis_canvas_decoration.h" class KisView; class KisCanvasResourceProvider; class KisMirrorAxis : public KisCanvasDecoration { Q_OBJECT Q_PROPERTY(float handleSize READ handleSize WRITE setHandleSize NOTIFY handleSizeChanged) public: KisMirrorAxis(KisCanvasResourceProvider* provider, QPointer parent); ~KisMirrorAxis(); float handleSize() const; void setHandleSize(float newSize); void setVisible(bool v); Q_SIGNALS: void handleSizeChanged(); protected: virtual void drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter* converter, KisCanvas2* canvas); virtual bool eventFilter(QObject* target, QEvent* event); private: class Private; Private * const d; private Q_SLOTS: void mirrorModeChanged(); + void moveHorizontalAxisToCenter(); + void moveVerticalAxisToCenter(); }; #endif // KISMIRRORAXIS_H diff --git a/libs/ui/kis_animation_exporter.cpp b/libs/ui/kis_animation_exporter.cpp index 7ca3530bea..e8a102a259 100644 --- a/libs/ui/kis_animation_exporter.cpp +++ b/libs/ui/kis_animation_exporter.cpp @@ -1,227 +1,326 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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 -#include +#include #include #include #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; - KisAnimationExporter *exporter; + 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 KisAnimationExporter(document, filename, firstFrame, lastFrame); + m_d->exporter = new KisAnimationExportSaver(document, filename, firstFrame, lastFrame); return m_d->exporter->exportAnimation(); } struct KisAnimationExporter::Private { KisDocument *document; KisImageWSP image; - QString filenamePrefix; - QString filenameSuffix; - int firstFrame; - int currentFrame; int lastFrame; + int currentFrame; - QScopedPointer tmpDoc; - KisImageWSP tmpImage; - KisPaintDeviceSP tmpDevice; - - bool exporting; bool batchMode; + bool isCancelled; + KisImportExportFilter::ConversionStatus status; - QMutex mutex; - QWaitCondition exportFinished; + SaveFrameCallback saveFrameCallback; + + KisPaintDeviceSP tmpDevice; Private(KisDocument *document, int fromTime, int toTime) : document(document), image(document->image()), firstFrame(fromTime), lastFrame(toTime), - tmpDoc(KisPart::instance()->createDocument()), - exporting(false), - batchMode(false) + currentFrame(-1), + batchMode(document->fileBatchMode()), + isCancelled(false), + status(KisImportExportFilter::OK), + tmpDevice(new KisPaintDevice(image->colorSpace())) { - tmpDoc->setAutoSave(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(); } }; -KisAnimationExporter::KisAnimationExporter(KisDocument *document, const QString &baseFilename, int fromTime, int toTime) +KisAnimationExporter::KisAnimationExporter(KisDocument *document, int fromTime, int toTime) : m_d(new Private(document, fromTime, toTime)) { - 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; - } - m_d->batchMode = document->fileBatchMode(); - - QString mimefilter = KisMimeDatabase::mimeTypeForFile(baseFilename); - m_d->tmpDoc->setOutputMimeType(mimefilter.toLatin1()); - m_d->tmpDoc->setFileBatchMode(true); + connect(m_d->image->animationInterface(), SIGNAL(sigFrameReady(int)), + this, SLOT(frameReadyToCopy(int)), Qt::DirectConnection); - connect(this, SIGNAL(sigFrameReadyToSave()), this, SLOT(frameReadyToSave()), Qt::QueuedConnection); + connect(this, SIGNAL(sigFrameReadyToSave()), + this, SLOT(frameReadyToSave()), Qt::QueuedConnection); } KisAnimationExporter::~KisAnimationExporter() { } +void KisAnimationExporter::setSaveFrameCallback(SaveFrameCallback func) +{ + m_d->saveFrameCallback = func; +} + KisImportExportFilter::ConversionStatus KisAnimationExporter::exportAnimation() { + QScopedPointer progress; if (!m_d->batchMode) { - emit m_d->document->statusBarMessage(i18n("Export frames")); + 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); + + KIS_ASSERT_RECOVER(!m_d->image->locked()) { return KisImportExportFilter::InternalError; } + m_d->status = KisImportExportFilter::OK; - m_d->exporting = true; m_d->currentFrame = m_d->firstFrame; - connect(m_d->image->animationInterface(), SIGNAL(sigFrameReady(int)), this, SLOT(frameReadyToCopy(int)), Qt::DirectConnection); m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); QEventLoop loop; loop.connect(this, SIGNAL(sigFinished()), SLOT(quit())); loop.exec(); - return m_d->status; -} - -void KisAnimationExporter::stopExport() -{ - if (!m_d->exporting) return; - - m_d->exporting = false; - - disconnect(m_d->image->animationInterface(), 0, this, 0); - 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(); } - emit sigFinished(); + + return m_d->status; } void KisAnimationExporter::cancel() { - m_d->status = KisImportExportFilter::ProgressCancelled; - stopExport(); + 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() { - QString frameNumber = QString("%1").arg(m_d->currentFrame, 4, 10, QChar('0')); - QString filename = m_d->filenamePrefix + frameNumber + m_d->filenameSuffix; + KIS_ASSERT_RECOVER(m_d->saveFrameCallback) { + m_d->status = KisImportExportFilter::InternalError; + emit sigFinished(); + return; + } - if (m_d->tmpDoc->exportDocument(QUrl::fromLocalFile(filename))) { - - if (m_d->exporting && m_d->currentFrame < m_d->lastFrame) { - if (!m_d->batchMode) { - emit m_d->document->sigProgress((m_d->currentFrame - m_d->firstFrame) * 100 / - (m_d->lastFrame - m_d->firstFrame)); - } - m_d->currentFrame++; - m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); - return; //continue - } + 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); + + if (!m_d->batchMode) { + emit m_d->document->sigProgress((time - m_d->firstFrame) * 100 / + (m_d->lastFrame - m_d->firstFrame)); + } + + 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 { - //error - m_d->status = KisImportExportFilter::InternalError; + emit sigFinished(); } - stopExport(); //finish +} + +struct KisAnimationExportSaver::Private +{ + Private(KisDocument *document, int fromTime, int toTime) + : document(document), + image(document->image()), + firstFrame(fromTime), + lastFrame(toTime), + tmpDoc(KisPart::instance()->createDocument()), + exporter(document, fromTime, toTime) + { + tmpDoc->setAutoSave(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; + + QScopedPointer tmpDoc; + KisImageSP tmpImage; + KisPaintDeviceSP tmpDevice; + + KisAnimationExporter exporter; + + QString filenamePrefix; + QString filenameSuffix; +}; + +KisAnimationExportSaver::KisAnimationExportSaver(KisDocument *document, const QString &baseFilename, int fromTime, int toTime) + : m_d(new Private(document, fromTime, toTime)) +{ + 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)); +} + +KisAnimationExportSaver::~KisAnimationExportSaver() +{ +} + +KisImportExportFilter::ConversionStatus KisAnimationExportSaver::exportAnimation() +{ + return m_d->exporter.exportAnimation(); +} + +KisImportExportFilter::ConversionStatus KisAnimationExportSaver::saveFrameCallback(int time, KisPaintDeviceSP frame) +{ + KisImportExportFilter::ConversionStatus status = + KisImportExportFilter::OK; + + QString frameNumber = QString("%1").arg(time, 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))) { + status = KisImportExportFilter::InternalError; + } + + return status; +} + +QString KisAnimationExportSaver::savedFilesMask() const +{ + return m_d->filenamePrefix + "%04d" + m_d->filenameSuffix; } diff --git a/libs/ui/kis_animation_exporter.h b/libs/ui/kis_animation_exporter.h index 544e65ad35..1ba139571b 100644 --- a/libs/ui/kis_animation_exporter.h +++ b/libs/ui/kis_animation_exporter.h @@ -1,70 +1,94 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_ANIMATION_EXPORTER_H #define KIS_ANIMATION_EXPORTER_H #include "kis_types.h" #include "kritaui_export.h" #include +#include + + class KisDocument; class KRITAUI_EXPORT KisAnimationExporterUI : public QObject { Q_OBJECT public: KisAnimationExporterUI(QWidget *parent); ~KisAnimationExporterUI(); KisImportExportFilter::ConversionStatus exportSequence(KisDocument *document); private: struct Private; QScopedPointer m_d; }; class KRITAUI_EXPORT KisAnimationExporter : public QObject { Q_OBJECT - public: - KisAnimationExporter(KisDocument *document, const QString &baseFilename, int fromTime, int toTime); + typedef std::function SaveFrameCallback; +public: + KisAnimationExporter(KisDocument *document, int fromTime, int toTime); ~KisAnimationExporter(); KisImportExportFilter::ConversionStatus exportAnimation(); - void stopExport(); + + void setSaveFrameCallback(SaveFrameCallback func); Q_SIGNALS: // Internal, used for getting back to main thread void sigFrameReadyToSave(); void sigFinished(); private Q_SLOTS: void frameReadyToCopy(int time); void frameReadyToSave(); void cancel(); private: struct Private; QScopedPointer m_d; }; +class KRITAUI_EXPORT KisAnimationExportSaver : public QObject +{ + Q_OBJECT +public: + KisAnimationExportSaver(KisDocument *document, const QString &baseFilename, int fromTime, int toTime); + ~KisAnimationExportSaver(); + + KisImportExportFilter::ConversionStatus exportAnimation(); + + QString savedFilesMask() const; + +private: + KisImportExportFilter::ConversionStatus saveFrameCallback(int time, KisPaintDeviceSP frame); + +private: + struct Private; + const QScopedPointer m_d; +}; + #endif diff --git a/libs/ui/kis_canvas_resource_provider.cpp b/libs/ui/kis_canvas_resource_provider.cpp index 2a879552a5..08646bd49e 100644 --- a/libs/ui/kis_canvas_resource_provider.cpp +++ b/libs/ui/kis_canvas_resource_provider.cpp @@ -1,447 +1,501 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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_resource_provider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_favorite_resource_manager.h" #include "kis_config.h" #include "KisViewManager.h" #include "canvas/kis_canvas2.h" KisCanvasResourceProvider::KisCanvasResourceProvider(KisViewManager * view) : m_view(view) { m_fGChanged = true; m_enablefGChange = true; // default to true, so that colour history is working without popup palette } KisCanvasResourceProvider::~KisCanvasResourceProvider() { disconnect(); // in case Qt gets confused } KoCanvasResourceManager* KisCanvasResourceProvider::resourceManager() { return m_resourceManager; } void KisCanvasResourceProvider::setResourceManager(KoCanvasResourceManager *resourceManager) { m_resourceManager = resourceManager; QVariant v; v.setValue(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); m_resourceManager->setResource(KoCanvasResourceManager::ForegroundColor, v); v.setValue(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8())); m_resourceManager->setResource(KoCanvasResourceManager::BackgroundColor, v); setCurrentCompositeOp(COMPOSITE_OVER); setMirrorHorizontal(false); setMirrorVertical(false); m_resourceManager->setResource(HdrExposure, 0.0); m_resourceManager->setResource(HdrGamma, 1.0); m_resourceManager->setResource(EffectiveZoom, 1.0); connect(m_resourceManager, SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(slotCanvasResourceChanged(int,QVariant))); m_resourceManager->setResource(KoCanvasResourceManager::ApplicationSpeciality, KoCanvasResourceManager::NoAdvancedText); } KoCanvasBase * KisCanvasResourceProvider::canvas() const { return m_view->canvasBase(); } KoColor KisCanvasResourceProvider::bgColor() const { return m_resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value(); } KoColor KisCanvasResourceProvider::fgColor() const { return m_resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value(); } float KisCanvasResourceProvider::HDRExposure() const { return static_cast(m_resourceManager->resource(HdrExposure).toDouble()); } void KisCanvasResourceProvider::setHDRExposure(float exposure) { m_resourceManager->setResource(HdrExposure, static_cast(exposure)); } float KisCanvasResourceProvider::HDRGamma() const { return static_cast(m_resourceManager->resource(HdrGamma).toDouble()); } void KisCanvasResourceProvider::setHDRGamma(float gamma) { m_resourceManager->setResource(HdrGamma, static_cast(gamma)); } KoPattern * KisCanvasResourceProvider::currentPattern() const { if (m_resourceManager->hasResource(CurrentPattern)) { return m_resourceManager->resource(CurrentPattern).value(); } else { return 0; } } KisFilterConfiguration * KisCanvasResourceProvider::currentGeneratorConfiguration() const { return m_resourceManager->resource(CurrentGeneratorConfiguration).value(); } KoAbstractGradient* KisCanvasResourceProvider::currentGradient() const { if (m_resourceManager->hasResource(CurrentGradient)) { return m_resourceManager->resource(CurrentGradient).value(); } else { return 0; } } KisImageWSP KisCanvasResourceProvider::currentImage() const { return m_view->image(); } KisNodeSP KisCanvasResourceProvider::currentNode() const { return m_view->activeNode(); } KisPaintOpPresetSP KisCanvasResourceProvider::currentPreset() const { KisPaintOpPresetSP preset = m_resourceManager->resource(CurrentPaintOpPreset).value(); return preset; } void KisCanvasResourceProvider::setPaintOpPreset(const KisPaintOpPresetSP preset) { Q_ASSERT(preset->valid()); Q_ASSERT(!preset->paintOp().id().isEmpty()); Q_ASSERT(preset->settings()); if (!preset) return; dbgUI << "setPaintOpPreset" << preset->paintOp(); QVariant v; v.setValue(preset); m_resourceManager->setResource(CurrentPaintOpPreset, v); } KisPaintOpPresetSP KisCanvasResourceProvider::previousPreset() const { KisPaintOpPresetSP preset = m_resourceManager->resource(PreviousPaintOpPreset).value(); return preset; } void KisCanvasResourceProvider::setPreviousPaintOpPreset(const KisPaintOpPresetSP preset) { Q_ASSERT(preset->valid()); Q_ASSERT(!preset->paintOp().id().isEmpty()); Q_ASSERT(preset->settings()); if (!preset) return; dbgUI << "setPreviousPaintOpPreset" << preset->paintOp(); QVariant v; v.setValue(preset); m_resourceManager->setResource(PreviousPaintOpPreset, v); } void KisCanvasResourceProvider::slotPatternActivated(KoResource * res) { KoPattern *pattern = dynamic_cast(res); QVariant v; v.setValue(pattern); m_resourceManager->setResource(CurrentPattern, v); emit sigPatternChanged(pattern); } void KisCanvasResourceProvider::slotGeneratorConfigurationActivated(KisFilterConfiguration * res) { KisFilterConfiguration * generatorConfiguration = dynamic_cast(res); QVariant v; v.setValue(generatorConfiguration); m_resourceManager->setResource(CurrentGeneratorConfiguration, v); } void KisCanvasResourceProvider::slotGradientActivated(KoResource *res) { KoAbstractGradient * gradient = dynamic_cast(res); QVariant v; v.setValue(gradient); m_resourceManager->setResource(CurrentGradient, v); emit sigGradientChanged(gradient); } void KisCanvasResourceProvider::setBGColor(const KoColor& c) { QVariant v; v.setValue(c); m_resourceManager->setResource(KoCanvasResourceManager::BackgroundColor, v); emit sigBGColorChanged(c); } void KisCanvasResourceProvider::setFGColor(const KoColor& c) { m_fGChanged = true; QVariant v; v.setValue(c); m_resourceManager->setResource(KoCanvasResourceManager::ForegroundColor, v); emit sigFGColorChanged(c); } void KisCanvasResourceProvider::slotSetFGColor(const KoColor& c) { setFGColor(c); } void KisCanvasResourceProvider::slotSetBGColor(const KoColor& c) { setBGColor(c); } void KisCanvasResourceProvider::slotNodeActivated(const KisNodeSP node) { QVariant v; v.setValue(KisNodeWSP(node)); m_resourceManager->setResource(CurrentKritaNode, v); emit sigNodeChanged(currentNode()); } void KisCanvasResourceProvider::slotImageSizeChanged() { if (KisImageWSP image = m_view->image()) { float fw = image->width() / image->xRes(); float fh = image->height() / image->yRes(); QSizeF postscriptSize(fw, fh); m_resourceManager->setResource(KoCanvasResourceManager::PageSize, postscriptSize); } } void KisCanvasResourceProvider::slotOnScreenResolutionChanged() { KisImageWSP image = m_view->image(); KisCanvas2 *canvas = m_view->canvasBase(); if(!image || !canvas) return; qreal zoomX, zoomY; canvas->coordinatesConverter()->zoom(&zoomX, &zoomY); qreal scaleX = zoomX / image->xRes(); qreal scaleY = zoomY / image->yRes(); emit sigOnScreenResolutionChanged(scaleX, scaleY); } void KisCanvasResourceProvider::slotCanvasResourceChanged(int key, const QVariant & res) { if(key == KoCanvasResourceManager::ForegroundColor || key == KoCanvasResourceManager::BackgroundColor) { KoAbstractGradient* resource = KoResourceServerProvider::instance()->gradientServer()->resources()[0]; KoStopGradient* stopGradient = dynamic_cast(resource); if(stopGradient) { QList stops; stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), fgColor().colorSpace())); stopGradient->setStops(stops); KoResourceServerProvider::instance()->gradientServer()->updateResource(resource); } resource = KoResourceServerProvider::instance()->gradientServer()->resources()[1]; stopGradient = dynamic_cast(resource); if(stopGradient) { QList stops; stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, bgColor()); stopGradient->setStops(stops); KoResourceServerProvider::instance()->gradientServer()->updateResource(resource); } } switch (key) { case(KoCanvasResourceManager::ForegroundColor): m_fGChanged = true; emit sigFGColorChanged(res.value()); break; case(KoCanvasResourceManager::BackgroundColor): emit sigBGColorChanged(res.value()); break; case(CurrentPattern): emit sigPatternChanged(static_cast(res.value())); break; case(CurrentGradient): emit sigGradientChanged(static_cast(res.value())); break; case(CurrentKritaNode) : emit sigNodeChanged(currentNode()); break; case (Opacity): { emit sigOpacityChanged(res.toDouble()); } default: ; // Do nothing }; } void KisCanvasResourceProvider::setCurrentCompositeOp(const QString& compositeOp) { m_resourceManager->setResource(CurrentCompositeOp, QVariant::fromValue(compositeOp)); } QString KisCanvasResourceProvider::currentCompositeOp() const { return m_resourceManager->resource(CurrentCompositeOp).value(); } bool KisCanvasResourceProvider::eraserMode() const { return m_resourceManager->resource(EraserMode).toBool(); } void KisCanvasResourceProvider::setEraserMode(bool value) { m_resourceManager->setResource(EraserMode, QVariant::fromValue(value)); } void KisCanvasResourceProvider::slotPainting() { if (m_fGChanged && m_enablefGChange) { emit sigFGColorUsed(fgColor()); m_fGChanged = false; } } void KisCanvasResourceProvider::slotResetEnableFGChange(bool b) { m_enablefGChange = b; } QList > KisCanvasResourceProvider::perspectiveGrids() const { return m_perspectiveGrids; } void KisCanvasResourceProvider::addPerspectiveGrid(KisAbstractPerspectiveGrid* grid) { m_perspectiveGrids.append(grid); } void KisCanvasResourceProvider::removePerspectiveGrid(KisAbstractPerspectiveGrid* grid) { m_perspectiveGrids.removeOne(grid); } void KisCanvasResourceProvider::clearPerspectiveGrids() { m_perspectiveGrids.clear(); } void KisCanvasResourceProvider::setMirrorHorizontal(bool mirrorHorizontal) { m_resourceManager->setResource(MirrorHorizontal, mirrorHorizontal); emit mirrorModeChanged(); } bool KisCanvasResourceProvider::mirrorHorizontal() const { return m_resourceManager->resource(MirrorHorizontal).toBool(); } void KisCanvasResourceProvider::setMirrorVertical(bool mirrorVertical) { m_resourceManager->setResource(MirrorVertical, mirrorVertical); emit mirrorModeChanged(); } bool KisCanvasResourceProvider::mirrorVertical() const { return m_resourceManager->resource(MirrorVertical).toBool(); } +void KisCanvasResourceProvider::setMirrorHorizontalLock(bool isLocked) +{ + m_resourceManager->setResource(MirrorHorizontalLock, isLocked); + emit mirrorModeChanged(); +} + +bool KisCanvasResourceProvider::mirrorHorizontalLock() { + return m_resourceManager->resource(MirrorHorizontalLock).toBool(); +} + +void KisCanvasResourceProvider::setMirrorVerticalLock(bool isLocked) +{ + m_resourceManager->setResource(MirrorVerticalLock, isLocked); + emit mirrorModeChanged(); +} + + + +bool KisCanvasResourceProvider::mirrorVerticalHideDecorations() { + return m_resourceManager->resource(MirrorVerticalHideDecorations).toBool(); +} + +void KisCanvasResourceProvider::setMirrorVerticalHideDecorations(bool hide) +{ + m_resourceManager->setResource(MirrorVerticalHideDecorations, hide); + emit mirrorModeChanged(); +} + + +bool KisCanvasResourceProvider::mirrorHorizontalHideDecorations() { + return m_resourceManager->resource(MirrorHorizontalHideDecorations).toBool(); +} + +void KisCanvasResourceProvider::setMirrorHorizontalHideDecorations(bool hide) +{ + m_resourceManager->setResource(MirrorHorizontalHideDecorations, hide); + emit mirrorModeChanged(); +} + + +bool KisCanvasResourceProvider::mirrorVerticalLock() { + return m_resourceManager->resource(MirrorVerticalLock).toBool(); +} + +void KisCanvasResourceProvider::mirrorVerticalMoveCanvasToCenter() { + emit moveMirrorVerticalCenter(); +} + +void KisCanvasResourceProvider::mirrorHorizontalMoveCanvasToCenter() { + emit moveMirrorHorizontalCenter(); +} + + + void KisCanvasResourceProvider::setOpacity(qreal opacity) { m_resourceManager->setResource(Opacity, opacity); } qreal KisCanvasResourceProvider::opacity() const { return m_resourceManager->resource(Opacity).toReal(); } void KisCanvasResourceProvider::setGlobalAlphaLock(bool lock) { m_resourceManager->setResource(GlobalAlphaLock, lock); } bool KisCanvasResourceProvider::globalAlphaLock() const { return m_resourceManager->resource(GlobalAlphaLock).toBool(); } void KisCanvasResourceProvider::notifyLoadingWorkspace(KisWorkspaceResource* workspace) { emit sigLoadingWorkspace(workspace); } void KisCanvasResourceProvider::notifySavingWorkspace(KisWorkspaceResource* workspace) { emit sigSavingWorkspace(workspace); } diff --git a/libs/ui/kis_canvas_resource_provider.h b/libs/ui/kis_canvas_resource_provider.h index 8a0878ca45..27bea92f16 100644 --- a/libs/ui/kis_canvas_resource_provider.h +++ b/libs/ui/kis_canvas_resource_provider.h @@ -1,205 +1,229 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #include #include #include #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 /** * 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, EraserMode, MirrorHorizontal, MirrorVertical, + MirrorHorizontalLock, + MirrorVerticalLock, + MirrorVerticalHideDecorations, + MirrorHorizontalHideDecorations, MirrorAxesCenter, Opacity, HdrGamma, GlobalAlphaLock, PreviousPaintOpPreset, EffectiveZoom, ///<-Used only by painting tools for non-displaying purposes PresetAllowsLod }; 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); KisFilterConfiguration* currentGeneratorConfiguration() const; void setCurrentCompositeOp(const QString& compositeOp); QString currentCompositeOp() const; QList > 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 setGlobalAlphaLock(bool lock); bool globalAlphaLock() 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); 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 slotGeneratorConfigurationActivated(KisFilterConfiguration * generatorConfiguration); 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 mirrorModeChanged(); + void moveMirrorVerticalCenter(); + void moveMirrorHorizontalCenter(); + private: KisViewManager * m_view; KoCanvasResourceManager *m_resourceManager; bool m_fGChanged; QList > 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 ecdf9be771..dde6d49266 100644 --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -1,1684 +1,1694 @@ /* * Copyright (c) 2002 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" #include "kis_config_notifier.h" #include "kis_snap_config.h" #include #include 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(); } 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 && screen > 0) { 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())); } 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 { #ifdef Q_OS_WIN return false; #else return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true)); #endif } void KisConfig::setHideTitlebarFullscreen(const bool value) const { m_cfg.writeEntry("hideTitleBarFullscreen", value); } bool KisConfig::hideToolbarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideToolbarFullscreen", true)); } void KisConfig::setHideToolbarFullscreen(const bool value) const { m_cfg.writeEntry("hideToolbarFullscreen", value); } bool KisConfig::fullscreenMode(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("fullscreenMode", true)); } void KisConfig::setFullscreenMode(const bool value) const { m_cfg.writeEntry("fullscreenMode", value); } QStringList KisConfig::favoriteCompositeOps(bool defaultValue) const { return (defaultValue ? QStringList() : m_cfg.readEntry("favoriteCompositeOps", QStringList())); } void KisConfig::setFavoriteCompositeOps(const QStringList& compositeOps) const { m_cfg.writeEntry("favoriteCompositeOps", compositeOps); } QString KisConfig::exportConfiguration(const QString &filterId, bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("ExportConfiguration-" + filterId, QString())); } void KisConfig::setExportConfiguration(const QString &filterId, const KisPropertiesConfiguration &properties) const { QString exportConfig = properties.toXML(); m_cfg.writeEntry("ExportConfiguration-" + filterId, exportConfig); } bool KisConfig::useOcio(bool defaultValue) const { #ifdef HAVE_OCIO return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/UseOcio", false)); #else 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); +} diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h index fc02b3146a..51810c6b96 100644 --- a/libs/ui/kis_config.h +++ b/libs/ui/kis_config.h @@ -1,513 +1,516 @@ /* * Copyright (c) 2002 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #include #include #include #include #include #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); 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; 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); 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; + template void writeEntry(const QString& name, const T& value) { m_cfg.writeEntry(name, value); } template void writeList(const QString& name, const QList& value) { m_cfg.writeEntry(name, value); } template T readEntry(const QString& name, const T& defaultValue=T()) { return m_cfg.readEntry(name, defaultValue); } template QList readList(const QString& name, const QList& defaultValue=QList()) { 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_cursor_override_hijacker.h b/libs/ui/kis_cursor_override_hijacker.h new file mode 100644 index 0000000000..08a323018d --- /dev/null +++ b/libs/ui/kis_cursor_override_hijacker.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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_CURSOR_OVERRIDE_HIJACKER_H +#define __KIS_CURSOR_OVERRIDE_HIJACKER_H + +#include +#include +#include + + +class KisCursorOverrideHijacker +{ +public: + KisCursorOverrideHijacker() { + while (qApp->overrideCursor()) { + m_cursorStack.push(*qApp->overrideCursor()); + qApp->restoreOverrideCursor(); + } + } + + ~KisCursorOverrideHijacker() { + while (!m_cursorStack.isEmpty()) { + qApp->setOverrideCursor(m_cursorStack.pop()); + } + } + +private: + QStack m_cursorStack; +}; + +#endif /* __KIS_CURSOR_OVERRIDE_HIJACKER_H */ diff --git a/libs/ui/kis_histogram_view.cc b/libs/ui/kis_histogram_view.cc index 327ffdf689..9a9b478b5c 100644 --- a/libs/ui/kis_histogram_view.cc +++ b/libs/ui/kis_histogram_view.cc @@ -1,379 +1,239 @@ /* * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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_view.h" #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include "KoChannelInfo.h" #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" #include "kis_global.h" #include "kis_layer.h" - +#include #include "kis_paint_device.h" KisHistogramView::KisHistogramView(QWidget *parent, const char *name, Qt::WFlags f) - : QLabel(parent, f) + : QLabel(parent, f), + m_currentDev(nullptr), m_currentProducer(nullptr), + m_smoothHistogram(false),m_histogram_type(LINEAR) { setObjectName(name); - // This is needed until we can computationally scale it well. Until then, this is needed - // And when we have it, it won't hurt to have it around - setScaledContents(true); - setFrameShape(QFrame::Box); // Draw a box around ourselves } KisHistogramView::~KisHistogramView() { } -void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds) -{ - m_cs = dev->colorSpace(); - - setChannels(); // Sets m_currentProducer to the first in the list - - if (!m_currentProducer) - return; - - m_from = m_currentProducer->viewFrom(); - m_width = m_currentProducer->viewWidth(); - - m_histogram = new KisHistogram(dev, bounds, m_currentProducer, LINEAR); - - updateHistogram(); -} - -void KisHistogramView::setHistogram(KisHistogramSP histogram) -{ - if (!histogram) return; - m_cs = 0; - m_histogram = histogram; - m_currentProducer = m_histogram->producer(); - m_from = m_currentProducer->viewFrom(); - m_width = m_currentProducer->viewWidth(); - - m_comboInfo.clear(); - m_channelStrings.clear(); - m_channels.clear(); - m_channelToOffset.clear(); - - addProducerChannels(m_currentProducer); - - // Set the currently viewed channel: - m_color = false; - m_channels.append(m_comboInfo.at(1).channel); - m_channelToOffset.append(0); - - updateHistogram(); -} - -void KisHistogramView::setView(double from, double size) -{ - m_from = from; - m_width = size; - if (m_from + m_width > 1.0) - m_from = 1.0 - m_width; - m_histogram->producer()->setView(m_from, m_width); - - m_histogram->updateHistogram(); - updateHistogram(); -} KoHistogramProducer *KisHistogramView::currentProducer() { return m_currentProducer; } -QStringList KisHistogramView::channelStrings() +void KisHistogramView::startUpdateCanvasProjection() { - return m_channelStrings; + updateHistogramCalculation(); } -QList KisHistogramView::producers() +void KisHistogramView::setChannels(QList & channels) { - if (m_cs) - return KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs); - return QList(); + m_channels = channels; + updateHistogramCalculation(); } -void KisHistogramView::setCurrentChannels(const KoID& producerID, QList channels) +void KisHistogramView::setProducer(KoHistogramProducer* producer) { - setCurrentChannels( - KoHistogramProducerFactoryRegistry::instance()->value(producerID.id())->generate(), - channels); + m_currentProducer = producer; + m_channels = m_currentProducer->channels(); + if( !m_histogram.isNull() ){ + m_histogram->setProducer( m_currentProducer ); + } + updateHistogramCalculation(); } -void KisHistogramView::setCurrentChannels(KoHistogramProducer *producer, QList channels) +void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer* producer, const QRect &bounds) { m_currentProducer = producer; - m_currentProducer->setView(m_from, m_width); - m_histogram->setProducer(m_currentProducer); - m_histogram->updateHistogram(); - m_histogram->setChannel(0); // Set a default channel, just being nice - - m_channels.clear(); - m_channelToOffset.clear(); - - if (channels.count() == 0) { - updateHistogram(); - return; - } + m_channels = m_currentProducer->channels(); + m_currentDev = dev; + m_currentBounds = bounds; + m_histogram = new KisHistogram(m_currentDev, m_currentBounds, m_currentProducer, m_histogram_type); - QList producerChannels = m_currentProducer->channels(); - - for (int i = 0; i < channels.count(); i++) { - // Also makes sure the channel is actually in the producer's list - for (int j = 0; j < producerChannels.count(); j++) { - if (channels.at(i)->name() == producerChannels.at(j)->name()) { - m_channelToOffset.append(m_channels.count()); // The first we append maps to 0 - m_channels.append(channels.at(i)); - } - } - } + updateHistogramCalculation(); +} - updateHistogram(); +void KisHistogramView::setView(double from, double size) +{ + double m_from = from; + double m_width = size; + if (m_from + m_width > 1.0) + m_from = 1.0 - m_width; + m_histogram->producer()->setView(m_from, m_width); + updateHistogramCalculation(); } bool KisHistogramView::hasColor() { return m_color; } void KisHistogramView::setColor(bool set) { if (set != m_color) { m_color = set; - updateHistogram(); } + update(); } -void KisHistogramView::setActiveChannel(int channel) +void KisHistogramView::setHistogramType(enumHistogramType type) { - ComboboxInfo info = m_comboInfo.at(channel); - if (info.producer != m_currentProducer) { - m_currentProducer = info.producer; - m_currentProducer->setView(m_from, m_width); - m_histogram->setProducer(m_currentProducer); - m_histogram->updateHistogram(); - } - - m_channels.clear(); - m_channelToOffset.clear(); + m_histogram_type = type; + updateHistogramCalculation(); +} - if (!m_currentProducer) { - updateHistogram(); +void KisHistogramView::updateHistogramCalculation() +{ + if (!m_currentProducer || m_currentDev.isNull() || m_histogram.isNull() ) { // Something's very wrong: not initialized return; } - - if (info.isProducer) { - m_color = true; - m_channels = m_currentProducer->channels(); - for (int i = 0; i < m_channels.count(); i++) - m_channelToOffset.append(i); - m_histogram->setChannel(0); // Set a default channel, just being nice - } else { - m_color = false; - QList channels = m_currentProducer->channels(); - for (int i = 0; i < channels.count(); i++) { - KoChannelInfo* channel = channels.at(i); - if (channel->name() == info.channel->name()) { - m_channels.append(channel); - m_channelToOffset.append(i); - break; - } - } - } - - updateHistogram(); + m_histogram->updateHistogram(); + update(); } -void KisHistogramView::setHistogramType(enumHistogramType type) +void KisHistogramView::mousePressEvent(QMouseEvent * e) { - m_histogram->setHistogramType(type); - updateHistogram(); + if (e->button() == Qt::RightButton) + emit rightClicked(e->globalPos()); + else + QLabel::mousePressEvent(e); } -void KisHistogramView::setChannels() +void KisHistogramView::setSmoothHistogram(bool smoothHistogram) { - m_comboInfo.clear(); - m_channelStrings.clear(); - m_channels.clear(); - m_channelToOffset.clear(); - - QList list = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs); - - if (list.count() == 0) { - // XXX: No native histogram for this colorspace. Using converted RGB. We should have a warning - KoGenericRGBHistogramProducerFactory f; - addProducerChannels(f.generate()); - } else { - Q_FOREACH (const QString &id, list) { - KoHistogramProducer *producer = KoHistogramProducerFactoryRegistry::instance()->value(id)->generate(); - if (producer) { - addProducerChannels(producer); - } - } - } - - m_currentProducer = m_comboInfo.at(0).producer; - m_color = false; - // The currently displayed channel and its offset - m_channels.append(m_comboInfo.at(1).channel); - m_channelToOffset.append(0); + m_smoothHistogram = smoothHistogram; } -void KisHistogramView::addProducerChannels(KoHistogramProducer *producer) -{ - if (!producer) return; - - ComboboxInfo info; - info.isProducer = true; - info.producer = producer; - // channel not used for a producer - QList channels = info.producer->channels(); - int count = channels.count(); - m_comboInfo.append(info); - m_channelStrings.append(producer->id() . name()); - for (int j = 0; j < count; j++) { - info.isProducer = false; - info.channel = channels.at(j); - m_comboInfo.append(info); - m_channelStrings.append(QString(" ").append(info.channel->name())); - } -} -void KisHistogramView::updateHistogram() +void KisHistogramView::paintEvent(QPaintEvent *event) { - quint32 height = this->height(); - int selFrom, selTo; // from - to in bins - - if (!m_currentProducer) { // Something's very wrong: no producer for this colorspace to update histogram with! - return; - } + QLabel::paintEvent(event); - qint32 bins = m_histogram->producer()->numberOfBins(); - - m_pix = QPixmap(bins, height); - m_pix.fill(); - - QPainter p(&m_pix); - p.setBrush(Qt::black); - - // Draw the box of the selection, if any - if (m_histogram->hasSelection()) { - double width = m_histogram->selectionTo() - m_histogram->selectionFrom(); - double factor = static_cast(bins) / m_histogram->producer()->viewWidth(); - selFrom = static_cast(m_histogram->selectionFrom() * factor); - selTo = selFrom + static_cast(width * factor); - p.drawRect(selFrom, 0, selTo - selFrom, height); - } else { - // We don't want the drawing to think we're in a selected area - selFrom = -1; - selTo = 2; - } + if( this->height() > 0 && this->width()>0 && !m_histogram.isNull() ){ + qint32 height = this->height(); + int selFrom, selTo; // from - to in bins - qint32 i = 0; - double highest = 0; - bool blackOnBlack = false; + qint32 bins = m_histogram->producer()->numberOfBins(); - // First we iterate once, so that we have the overall maximum. This is a bit inefficient, - // but not too much since the histogram caches the calculations - for (int chan = 0; chan < m_channels.count(); chan++) { - m_histogram->setChannel(m_channelToOffset.at(chan)); - if ((double)m_histogram->calculations().getHighest() > highest) - highest = (double)m_histogram->calculations().getHighest(); - } + QPainter painter(this); + painter.setPen(this->palette().light().color()); - QPen pen(Qt::white); + const int NGRID = 4; + for(int i=0; i<=NGRID; ++i){ + painter.drawLine(this->width()*i/NGRID,0.,this->width()*i/NGRID,this->height()); + painter.drawLine(0.,this->height()*i/NGRID,this->width(),this->height()*i/NGRID); + } - for (int chan = 0; chan < m_channels.count(); chan++) { - QColor color; - m_histogram->setChannel(m_channelToOffset.at(chan)); - if (m_color) { - color = m_channels.at(chan)->color(); - QLinearGradient gradient; - gradient.setColorAt(0, Qt::white); - gradient.setColorAt(1, color); - QBrush brush(gradient); - pen = QPen(brush, 0); + // Draw the box of the selection, if any + if (m_histogram->hasSelection()) { + double width = m_histogram->selectionTo() - m_histogram->selectionFrom(); + double factor = static_cast(bins) / m_histogram->producer()->viewWidth(); + selFrom = static_cast(m_histogram->selectionFrom() * factor); + selTo = selFrom + static_cast(width * factor); + painter.drawRect(selFrom, 0, selTo - selFrom, height); } else { - color = Qt::black; + // We don't want the drawing to think we're in a selected area + selFrom = -1; + selTo = 2; } - blackOnBlack = (color == Qt::black); - - if (m_histogram->getHistogramType() == LINEAR) { + float highest = 0; - double factor = (double)height / highest; - for (i = 0; i < bins; ++i) { - // So that we get a good view even with a selection box with - // black colors on background of black selection - if (i >= selFrom && i < selTo && blackOnBlack) { - p.setPen(Qt::white); - } else { - p.setPen(pen); - } - p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor)); + if(m_channels.count()==0){ + m_channels=m_currentProducer->channels(); + } + int nChannels = m_channels.size(); + + // First we iterate once, so that we have the overall maximum. This is a bit inefficient, + // but not too much since the histogram caches the calculations + for (int chan = 0; chan < m_channels.size(); chan++) { + if( m_channels.at(chan)->channelType() != KoChannelInfo::ALPHA ){ + m_histogram->setChannel(chan); + if ((float)m_histogram->calculations().getHighest() > highest) + highest = (float)m_histogram->calculations().getHighest(); } - } else { - - double factor = (double)height / (double)log(highest); - for (i = 0; i < bins; ++i) { - // Same as above - if (i >= selFrom && i < selTo && blackOnBlack) { - p.setPen(Qt::white); - } else { - p.setPen(pen); + } + highest = (m_histogram_type==LINEAR)? highest: std::log2(highest); + + painter.setWindow(QRect(-1,0,bins+1,highest)); + painter.setCompositionMode(QPainter::CompositionMode_Plus); + + for (int chan = 0; chan < nChannels; chan++) { + if( m_channels.at(chan)->channelType() != KoChannelInfo::ALPHA ){ + auto color = m_channels.at(chan)->color(); + auto fill_color = color; + fill_color.setAlphaF(.25); + painter.setBrush(fill_color); + auto pen = QPen(color); + pen.setWidth(0); + painter.setPen(pen); + + if (m_smoothHistogram){ + QPainterPath path; + + m_histogram->setChannel(chan); + path.moveTo(QPointF(-1,highest)); + for (qint32 i = 0; i < bins; ++i) { + float v = (m_histogram_type==LINEAR)? highest-m_histogram->getValue(i): highest-std::log2(m_histogram->getValue(i)); + path.lineTo(QPointF(i,v)); + + } + path.lineTo(QPointF(bins+1,highest)); + path.closeSubpath(); + painter.drawPath(path); } - if (m_histogram->getValue(i) > 0) { // Don't try to calculate log(0) - p.drawLine(i, height, i, - height - int(log((double)m_histogram->getValue(i)) * factor)); + else { + pen.setWidth(1); + painter.setPen(pen); + m_histogram->setChannel(chan); + for (qint32 i = 0; i < bins; ++i) { + float v = (m_histogram_type==LINEAR)? highest-m_histogram->getValue(i): highest-std::log2(m_histogram->getValue(i)); + painter.drawLine(QPointF(i,highest),QPointF(i,v)); + } } } } } - - setPixmap(m_pix); } - -void KisHistogramView::mousePressEvent(QMouseEvent * e) -{ - if (e->button() == Qt::RightButton) - emit rightClicked(e->globalPos()); - else - QLabel::mousePressEvent(e); -} - - diff --git a/libs/ui/kis_histogram_view.h b/libs/ui/kis_histogram_view.h index 3ceca8ce69..a5d266df55 100644 --- a/libs/ui/kis_histogram_view.h +++ b/libs/ui/kis_histogram_view.h @@ -1,133 +1,106 @@ /* * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_HISTOGRAM_VIEW_ #define _KIS_HISTOGRAM_VIEW_ #include #include #include #include #include +#include #include "kis_types.h" -#include "KoHistogramProducer.h" #include "kis_histogram.h" #include class KoChannelInfo; /** * This class displays a histogram. It has a list of channels it can * select. The easy way is to display channelStrings() to the user, * and then use a setActiveChannel with the integer the same as the * one the selected string in that stringlist has. If the selected one * is a producer, the histogram will automatically display all its * channels, and color them if that is possible. * * You can also set the channels manually, just don't forget that the * displayed channels all need to belong to the same producer! If you * set them manually, don't forget to set the (non)usage of color as * well. * * You can either set this to use a specific layer, or use a specific * histogram. With the latter, some functionality will disappear, like * listProducers(). Setting a histogram will discard info on the * layer, and setting a layer will discard info on the histogram. * - * XXX: make beautiful, for instance by using alpha and a bit of a - * gradient! **/ class KRITAUI_EXPORT KisHistogramView : public QLabel { Q_OBJECT public: KisHistogramView(QWidget *parent = 0, const char *name = 0, Qt::WFlags f = 0); virtual ~KisHistogramView(); - void setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds); - - void setHistogram(KisHistogramSP histogram); + void setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer *producer, const QRect &bounds); void setView(double from, double size); KoHistogramProducer *currentProducer(); - QStringList channelStrings(); - - /** Lists all producers currently available */ - QList producers(); - - /** Sets the currently displayed channels to channels of the producer with producerID as ID*/ - void setCurrentChannels(const KoID& producerID, QList channels); - - /** Be careful, producer will be modified */ - void setCurrentChannels(KoHistogramProducer *producer, QList channels); - bool hasColor(); - void setColor(bool set); + void setProducer(KoHistogramProducer* producer); + void setChannels(QList & channels); + virtual void paintEvent(QPaintEvent* event); + virtual void updateHistogramCalculation(); + void setSmoothHistogram(bool smoothHistogram); public Q_SLOTS: - - void setActiveChannel(int channel); - - void setHistogramType(enumHistogramType type); - - void updateHistogram(); + virtual void setHistogramType(enumHistogramType type); + virtual void startUpdateCanvasProjection(); Q_SIGNALS: - void rightClicked(const QPoint& pos); protected: virtual void mousePressEvent(QMouseEvent * e); private: - void setChannels(); + void setChannels(void); void addProducerChannels(KoHistogramProducer *producer); - - typedef struct { - bool isProducer; - KoHistogramProducer *producer; - KoChannelInfo * channel; - } ComboboxInfo; - - QVector m_comboInfo; - QPixmap m_pix; KisHistogramSP m_histogram; - const KoColorSpace* m_cs; + KisPaintDeviceSP m_currentDev; + QRect m_currentBounds; KoHistogramProducer *m_currentProducer; QList m_channels; - // Maps the channels in m_channels to a real channel offset in the producer->channels() - QVector m_channelToOffset; - QStringList m_channelStrings; bool m_color; - double m_from; - double m_width; + bool m_smoothHistogram; + enumHistogramType m_histogram_type; }; #endif // _KIS_HISTOGRAM_VIEW_ diff --git a/libs/ui/kis_paintop_box.cc b/libs/ui/kis_paintop_box.cc index bb2a10a2f4..7087df2b11 100644 --- a/libs/ui/kis_paintop_box.cc +++ b/libs/ui/kis_paintop_box.cc @@ -1,1109 +1,1205 @@ /* * kis_paintop_box.cc - part of KImageShop/Krayon/Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * Copyright (c) 2009-2011 Sven Langkamp (sven.langkamp@gmail.com) * Copyright (c) 2010 Lukáš Tvrdý * Copyright (C) 2011 Silvio Heinrich * Copyright (C) 2011 Srikanth Tiyyagura * Copyright (c) 2014 Mohit Goyal * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_paintop_box.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "kis_node_manager.h" #include "KisViewManager.h" #include "kis_canvas_resource_provider.h" #include "kis_resource_server_provider.h" #include "kis_favorite_resource_manager.h" #include "kis_config.h" #include "widgets/kis_popup_button.h" #include "widgets/kis_tool_options_popup.h" #include "widgets/kis_paintop_presets_popup.h" #include "widgets/kis_tool_options_popup.h" #include "widgets/kis_paintop_presets_chooser_popup.h" #include "widgets/kis_workspace_chooser.h" #include "widgets/kis_paintop_list_widget.h" #include "widgets/kis_slider_spin_box.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_widget_chooser.h" #include "tool/kis_tool.h" #include "kis_signals_blocker.h" #include "kis_action_manager.h" #include "kis_highlighted_button.h" typedef KoResourceServerSimpleConstruction > KisPaintOpPresetResourceServer; typedef KoResourceServerAdapter > KisPaintOpPresetResourceServerAdapter; KisPaintopBox::KisPaintopBox(KisViewManager *view, QWidget *parent, const char *name) : QWidget(parent) , m_resourceProvider(view->resourceProvider()) , m_optionWidget(0) , m_toolOptionsPopupButton(0) , m_brushEditorPopupButton(0) , m_presetSelectorPopupButton(0) , m_toolOptionsPopup(0) , m_viewManager(view) , m_previousNode(0) , m_currTabletToolID(KoInputDevice::invalid()) , m_presetsEnabled(true) , m_blockUpdate(false) , m_dirtyPresetsEnabled(false) , m_eraserBrushSizeEnabled(false) { Q_ASSERT(view != 0); setObjectName(name); KisConfig cfg; m_dirtyPresetsEnabled = cfg.useDirtyPresets(); m_eraserBrushSizeEnabled = cfg.useEraserBrushSize(); KAcceleratorManager::setNoAccel(this); setWindowTitle(i18n("Painter's Toolchest")); KConfigGroup grp = KSharedConfig::openConfig()->group("krita").group("Toolbar BrushesAndStuff"); int iconsize = grp.readEntry("IconSize", 32); if (!cfg.toolOptionsInDocker()) { m_toolOptionsPopupButton = new KisPopupButton(this); m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure")); m_toolOptionsPopupButton->setToolTip(i18n("Tool Settings")); m_toolOptionsPopupButton->setFixedSize(iconsize, iconsize); } m_brushEditorPopupButton = new KisPopupButton(this); m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02")); m_brushEditorPopupButton->setToolTip(i18n("Edit brush settings")); m_brushEditorPopupButton->setFixedSize(iconsize, iconsize); m_presetSelectorPopupButton = new KisPopupButton(this); m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01")); m_presetSelectorPopupButton->setToolTip(i18n("Choose brush preset")); m_presetSelectorPopupButton->setFixedSize(iconsize, iconsize); m_eraseModeButton = new KisHighlightedToolButton(this); m_eraseModeButton->setFixedSize(iconsize, iconsize); m_eraseModeButton->setCheckable(true); m_eraseAction = m_viewManager->actionManager()->createAction("erase_action"); m_eraseModeButton->setDefaultAction(m_eraseAction); m_reloadButton = new QToolButton(this); m_reloadButton->setFixedSize(iconsize, iconsize); m_reloadAction = m_viewManager->actionManager()->createAction("reload_preset_action"); m_reloadButton->setDefaultAction(m_reloadAction); m_alphaLockButton = new KisHighlightedToolButton(this); m_alphaLockButton->setFixedSize(iconsize, iconsize); m_alphaLockButton->setCheckable(true); KisAction* alphaLockAction = m_viewManager->actionManager()->createAction("preserve_alpha"); m_alphaLockButton->setDefaultAction(alphaLockAction); + + + + // horizontal and vertical mirror toolbar buttons + + // mirror tool options for the X Mirror + QMenu *toolbarMenuXMirror = new QMenu(); + + KisAction* hideCanvasDecorationsX = m_viewManager->actionManager()->createAction("mirrorX-hideDecorations"); + hideCanvasDecorationsX->setCheckable(true); + hideCanvasDecorationsX->setText(i18n("Hide Mirror Line")); + toolbarMenuXMirror->addAction(hideCanvasDecorationsX); + + KisAction* lockActionX = m_viewManager->actionManager()->createAction("mirrorX-lock"); + lockActionX->setText(i18n("Lock")); + lockActionX->setCheckable(true); + toolbarMenuXMirror->addAction(lockActionX); + + KisAction* moveToCenterActionX = m_viewManager->actionManager()->createAction("mirrorX-moveToCenter"); + moveToCenterActionX->setCheckable(false); + moveToCenterActionX->setText(i18n("Move to Canvas Center")); + toolbarMenuXMirror->addAction(moveToCenterActionX); + + + + // mirror tool options for the Y Mirror + QMenu *toolbarMenuYMirror = new QMenu(); + + KisAction* hideCanvasDecorationsY = m_viewManager->actionManager()->createAction("mirrorY-hideDecorations"); + hideCanvasDecorationsY->setCheckable(true); + hideCanvasDecorationsY->setText(i18n("Hide Mirror Line")); + toolbarMenuYMirror->addAction(hideCanvasDecorationsY); + + + KisAction* lockActionY = m_viewManager->actionManager()->createAction("mirrorY-lock"); + lockActionY->setText(i18n("Lock")); + lockActionY->setCheckable(true); + toolbarMenuYMirror->addAction(lockActionY); + + KisAction* moveToCenterActionY = m_viewManager->actionManager()->createAction("mirrorY-moveToCenter"); + moveToCenterActionY->setCheckable(false); + moveToCenterActionY->setText(i18n("Move to Canvas Center")); + toolbarMenuYMirror->addAction(moveToCenterActionY); + + + + + // create horizontal and vertical mirror buttons + m_hMirrorButton = new KisHighlightedToolButton(this); - m_hMirrorButton->setFixedSize(iconsize, iconsize); + int menuPadding = 10; + m_hMirrorButton->setFixedSize(iconsize + menuPadding, iconsize); m_hMirrorButton->setCheckable(true); - m_hMirrorAction = m_viewManager->actionManager()->createAction("hmirror_action"); m_hMirrorButton->setDefaultAction(m_hMirrorAction); + m_hMirrorButton->setMenu(toolbarMenuXMirror); + m_hMirrorButton->setPopupMode(QToolButton::MenuButtonPopup); m_vMirrorButton = new KisHighlightedToolButton(this); - m_vMirrorButton->setFixedSize(iconsize, iconsize); + m_vMirrorButton->setFixedSize(iconsize + menuPadding, iconsize); m_vMirrorButton->setCheckable(true); - m_vMirrorAction = m_viewManager->actionManager()->createAction("vmirror_action"); m_vMirrorButton->setDefaultAction(m_vMirrorAction); + m_vMirrorButton->setMenu(toolbarMenuYMirror); + m_vMirrorButton->setPopupMode(QToolButton::MenuButtonPopup); + + + // add connections for horizontal and mirrror buttons + connect(lockActionX, SIGNAL(toggled(bool)), this, SLOT(slotLockXMirrorToggle(bool))); + connect(lockActionY, SIGNAL(toggled(bool)), this, SLOT(slotLockYMirrorToggle(bool))); + + connect(moveToCenterActionX, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorX())); + connect(moveToCenterActionY, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorY())); + + connect(hideCanvasDecorationsX, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorX(bool))); + connect(hideCanvasDecorationsY, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorY(bool))); const bool sliderLabels = cfg.sliderLabels(); int sliderWidth; if (sliderLabels) { sliderWidth = 150 * logicalDpiX() / 96; } else { sliderWidth = 120 * logicalDpiX() / 96; } for (int i = 0; i < 3; ++i) { m_sliderChooser[i] = new KisWidgetChooser(i + 1); KisDoubleSliderSpinBox* slOpacity; KisDoubleSliderSpinBox* slFlow; KisDoubleSliderSpinBox* slSize; if (sliderLabels) { slOpacity = m_sliderChooser[i]->addWidget("opacity"); slFlow = m_sliderChooser[i]->addWidget("flow"); slSize = m_sliderChooser[i]->addWidget("size"); slOpacity->setPrefix(QString("%1 ").arg(i18n("Opacity:"))); slFlow->setPrefix(QString("%1 ").arg(i18n("Flow:"))); slSize->setPrefix(QString("%1 ").arg(i18n("Size:"))); } else { slOpacity = m_sliderChooser[i]->addWidget("opacity", i18n("Opacity:")); slFlow = m_sliderChooser[i]->addWidget("flow", i18n("Flow:")); slSize = m_sliderChooser[i]->addWidget("size", i18n("Size:")); } slOpacity->setRange(0.0, 1.0, 2); slOpacity->setValue(1.0); slOpacity->setSingleStep(0.05); slOpacity->setMinimumWidth(qMax(sliderWidth, slOpacity->sizeHint().width())); slOpacity->setFixedHeight(iconsize); slOpacity->setBlockUpdateSignalOnDrag(true); slFlow->setRange(0.0, 1.0, 2); slFlow->setValue(1.0); slFlow->setSingleStep(0.05); slFlow->setMinimumWidth(qMax(sliderWidth, slFlow->sizeHint().width())); slFlow->setFixedHeight(iconsize); slFlow->setBlockUpdateSignalOnDrag(true); slSize->setRange(0, 1000, 2); slSize->setValue(100); slSize->setSingleStep(1); slSize->setExponentRatio(3.0); slSize->setSuffix(i18n(" px")); slSize->setMinimumWidth(qMax(sliderWidth, slSize->sizeHint().width())); slSize->setFixedHeight(iconsize); slSize->setBlockUpdateSignalOnDrag(true); m_sliderChooser[i]->chooseWidget(cfg.toolbarSlider(i + 1)); } m_cmbCompositeOp = new KisCompositeOpComboBox(); m_cmbCompositeOp->setFixedHeight(iconsize); Q_FOREACH (KisAction * a, m_cmbCompositeOp->blendmodeActions()) { m_viewManager->actionManager()->addAction(a->text(), a); } m_workspaceWidget = new KisPopupButton(this); m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose")); m_workspaceWidget->setToolTip(i18n("Choose workspace")); m_workspaceWidget->setFixedSize(iconsize, iconsize); m_workspaceWidget->setPopupWidget(new KisWorkspaceChooser(view)); QHBoxLayout* baseLayout = new QHBoxLayout(this); m_paintopWidget = new QWidget(this); baseLayout->addWidget(m_paintopWidget); baseLayout->setSpacing(4); baseLayout->setContentsMargins(0, 0, 0, 0); m_layout = new QHBoxLayout(m_paintopWidget); if (!cfg.toolOptionsInDocker()) { m_layout->addWidget(m_toolOptionsPopupButton); } m_layout->addWidget(m_brushEditorPopupButton); m_layout->addWidget(m_presetSelectorPopupButton); m_layout->setSpacing(4); m_layout->setContentsMargins(0, 0, 0, 0); QWidget* compositeActions = new QWidget(this); QHBoxLayout* compositeLayout = new QHBoxLayout(compositeActions); compositeLayout->addWidget(m_cmbCompositeOp); compositeLayout->addWidget(m_eraseModeButton); compositeLayout->addWidget(m_alphaLockButton); compositeLayout->setSpacing(4); compositeLayout->setContentsMargins(0, 0, 0, 0); compositeLayout->addWidget(m_reloadButton); QWidgetAction * action; action = new QWidgetAction(this); view->actionCollection()->addAction("composite_actions", action); action->setDefaultWidget(compositeActions); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("brushslider1", action); view->actionCollection()->addAction("brushslider1", action); action->setDefaultWidget(m_sliderChooser[0]); connect(action, SIGNAL(triggered()), m_sliderChooser[0], SLOT(showPopupWidget())); connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[0], SLOT(updateThemedIcons())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("brushslider2", action); view->actionCollection()->addAction("brushslider2", action); action->setDefaultWidget(m_sliderChooser[1]); connect(action, SIGNAL(triggered()), m_sliderChooser[1], SLOT(showPopupWidget())); connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[1], SLOT(updateThemedIcons())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("brushslider3", action); view->actionCollection()->addAction("brushslider3", action); action->setDefaultWidget(m_sliderChooser[2]); connect(action, SIGNAL(triggered()), m_sliderChooser[2], SLOT(showPopupWidget())); connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[2], SLOT(updateThemedIcons())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("next_favorite_preset", action); view->actionCollection()->addAction("next_favorite_preset", action); connect(action, SIGNAL(triggered()), this, SLOT(slotNextFavoritePreset())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("previous_favorite_preset", action); view->actionCollection()->addAction("previous_favorite_preset", action); connect(action, SIGNAL(triggered()), this, SLOT(slotPreviousFavoritePreset())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("previous_preset", action); view->actionCollection()->addAction("previous_preset", action); connect(action, SIGNAL(triggered()), this, SLOT(slotSwitchToPreviousPreset())); if (!cfg.toolOptionsInDocker()) { action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("show_tool_options", action); view->actionCollection()->addAction("show_tool_options", action); connect(action, SIGNAL(triggered()), m_toolOptionsPopupButton, SLOT(showPopupWidget())); } action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("show_brush_editor", action); view->actionCollection()->addAction("show_brush_editor", action); connect(action, SIGNAL(triggered()), m_brushEditorPopupButton, SLOT(showPopupWidget())); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("show_brush_presets", action); view->actionCollection()->addAction("show_brush_presets", action); connect(action, SIGNAL(triggered()), m_presetSelectorPopupButton, SLOT(showPopupWidget())); QWidget* mirrorActions = new QWidget(this); QHBoxLayout* mirrorLayout = new QHBoxLayout(mirrorActions); mirrorLayout->addWidget(m_hMirrorButton); mirrorLayout->addWidget(m_vMirrorButton); mirrorLayout->setSpacing(4); mirrorLayout->setContentsMargins(0, 0, 0, 0); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("mirror_actions", action); action->setDefaultWidget(mirrorActions); view->actionCollection()->addAction("mirror_actions", action); action = new QWidgetAction(this); KisActionRegistry::instance()->propertizeAction("workspaces", action); view->actionCollection()->addAction("workspaces", action); action->setDefaultWidget(m_workspaceWidget); if (!cfg.toolOptionsInDocker()) { m_toolOptionsPopup = new KisToolOptionsPopup(); m_toolOptionsPopupButton->setPopupWidget(m_toolOptionsPopup); m_toolOptionsPopup->switchDetached(false); } m_presetsPopup = new KisPaintOpPresetsPopup(m_resourceProvider); m_brushEditorPopupButton->setPopupWidget(m_presetsPopup); m_presetsPopup->switchDetached(false); connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_presetsPopup, SLOT(updateThemedIcons())); m_presetsChooserPopup = new KisPaintOpPresetsChooserPopup(); m_presetsChooserPopup->setFixedSize(500, 600); m_presetSelectorPopupButton->setPopupWidget(m_presetsChooserPopup); m_currCompositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id(); slotNodeChanged(view->activeNode()); // Get all the paintops QList keys = KisPaintOpRegistry::instance()->keys(); QList factoryList; Q_FOREACH (const QString & paintopId, keys) { factoryList.append(KisPaintOpRegistry::instance()->get(paintopId)); } m_presetsPopup->setPaintOpList(factoryList); connect(m_presetsPopup , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString))); connect(m_presetsPopup , SIGNAL(savePresetClicked()) , SLOT(slotSaveActivePreset())); connect(m_presetsPopup , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset())); connect(m_presetsPopup , SIGNAL(signalResourceSelected(KoResource*)), SLOT(resourceSelected(KoResource*))); connect(m_presetsPopup , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset())); connect(m_presetsPopup , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool))); connect(m_presetsPopup , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool))); connect(m_presetsChooserPopup, SIGNAL(resourceSelected(KoResource*)) , SLOT(resourceSelected(KoResource*))); connect(m_resourceProvider , SIGNAL(sigNodeChanged(const KisNodeSP)) , SLOT(slotNodeChanged(const KisNodeSP))); connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int))); connect(m_eraseAction , SIGNAL(toggled(bool)) , SLOT(slotToggleEraseMode(bool))); connect(alphaLockAction , SIGNAL(toggled(bool)) , SLOT(slotToggleAlphaLockMode(bool))); + + connect(m_hMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotHorizontalMirrorChanged(bool))); connect(m_vMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotVerticalMirrorChanged(bool))); + + + + connect(m_reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset())); connect(m_sliderChooser[0]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed())); connect(m_sliderChooser[0]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed())); connect(m_sliderChooser[0]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed())); connect(m_sliderChooser[1]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed())); connect(m_sliderChooser[1]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed())); connect(m_sliderChooser[1]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed())); connect(m_sliderChooser[2]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed())); connect(m_sliderChooser[2]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed())); connect(m_sliderChooser[2]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed())); //Needed to connect canvas to favorite resource manager connect(m_viewManager->resourceProvider(), SIGNAL(sigOpacityChanged(qreal)), SLOT(slotOpacityChanged(qreal))); connect(m_viewManager->resourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), SLOT(slotUnsetEraseMode())); m_favoriteResourceManager = new KisFavoriteResourceManager(this); connect(m_resourceProvider, SIGNAL(sigFGColorUsed(KoColor)), m_favoriteResourceManager, SLOT(slotAddRecentColor(KoColor))); connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotChangeFGColorSelector(KoColor))); connect(m_resourceProvider, SIGNAL(sigBGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotSetBGColor(KoColor))); // cold initialization m_favoriteResourceManager->slotChangeFGColorSelector(m_resourceProvider->fgColor()); m_favoriteResourceManager->slotSetBGColor(m_resourceProvider->bgColor()); connect(m_favoriteResourceManager, SIGNAL(sigSetFGColor(KoColor)), m_resourceProvider, SLOT(slotSetFGColor(KoColor))); connect(m_favoriteResourceManager, SIGNAL(sigSetBGColor(KoColor)), m_resourceProvider, SLOT(slotSetBGColor(KoColor))); connect(m_favoriteResourceManager, SIGNAL(sigEnableChangeColor(bool)), m_resourceProvider, SLOT(slotResetEnableFGChange(bool))); connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateSelectionIcon())); slotInputDeviceChanged(KoToolManager::instance()->currentInputDevice()); } KisPaintopBox::~KisPaintopBox() { KisConfig cfg; QMapIterator iter(m_tabletToolMap); while (iter.hasNext()) { iter.next(); if ((iter.key().pointer) == QTabletEvent::Eraser) { cfg.writeEntry(QString("LastEraser_%1").arg(iter.key().uniqueID) , iter.value().preset->name()); } else { cfg.writeEntry(QString("LastPreset_%1").arg(iter.key().uniqueID) , iter.value().preset->name()); } } // Do not delete the widget, since it it is global to the application, not owned by the view m_presetsPopup->setPaintOpSettingsWidget(0); qDeleteAll(m_paintopOptionWidgets); delete m_favoriteResourceManager; for (int i = 0; i < 3; ++i) { delete m_sliderChooser[i]; } } void KisPaintopBox::restoreResource(KoResource* resource) { KisPaintOpPreset* preset = dynamic_cast(resource); if (preset) { setCurrentPaintop(preset->paintOp(), preset); m_presetsPopup->setPresetImage(preset->image()); m_presetsPopup->resourceSelected(resource); } } void KisPaintopBox::newOptionWidgets(const QList > &optionWidgetList) { if (m_toolOptionsPopup) { m_toolOptionsPopup->newOptionWidgets(optionWidgetList); } } void KisPaintopBox::resourceSelected(KoResource* resource) { KisPaintOpPreset* preset = dynamic_cast(resource); if (preset) { if (!preset->settings()->isLoadable()) return; setCurrentPaintopAndReload(preset->paintOp(), preset); m_presetsPopup->setPresetImage(preset->image()); m_presetsPopup->resourceSelected(resource); } } void KisPaintopBox::setCurrentPaintopAndReload(const KoID& paintop, KisPaintOpPresetSP preset) { if (!m_dirtyPresetsEnabled) { KisSignalsBlocker blocker(m_optionWidget); if (!preset->load()) { warnKrita << "failed to load the preset."; } } setCurrentPaintop(paintop, preset); } void KisPaintopBox::setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP preset) { if (m_resourceProvider->currentPreset()) { m_resourceProvider->setPreviousPaintOpPreset(m_resourceProvider->currentPreset()); if (m_optionWidget) { m_optionWidget->disconnect(this); m_optionWidget->hide(); } m_paintOpPresetMap[m_resourceProvider->currentPreset()->paintOp()] = m_resourceProvider->currentPreset(); m_tabletToolMap[m_currTabletToolID].preset = m_resourceProvider->currentPreset(); m_tabletToolMap[m_currTabletToolID].paintOpID = m_resourceProvider->currentPreset()->paintOp(); } preset = (!preset) ? activePreset(paintop) : preset; Q_ASSERT(preset && preset->settings()); m_resourceProvider->setPaintOpPreset(preset); if (!m_paintopOptionWidgets.contains(paintop)) m_paintopOptionWidgets[paintop] = KisPaintOpRegistry::instance()->get(paintop.id())->createConfigWidget(this); m_optionWidget = m_paintopOptionWidgets[paintop]; preset->settings()->setOptionsWidget(m_optionWidget); m_optionWidget->setImage(m_viewManager->image()); m_optionWidget->setNode(m_viewManager->activeNode()); m_optionWidget->setConfiguration(preset->settings()); m_presetsPopup->setPaintOpSettingsWidget(m_optionWidget); Q_ASSERT(m_optionWidget && m_presetSelectorPopupButton); connect(m_optionWidget, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotGuiChangedCurrentPreset())); connect(m_optionWidget, SIGNAL(sigSaveLockedConfig(KisPropertiesConfiguration*)), this, SLOT(slotSaveLockedOptionToPreset(KisPropertiesConfiguration*))); connect(m_optionWidget, SIGNAL(sigDropLockedConfig(KisPropertiesConfiguration*)), this, SLOT(slotDropLockedOption(KisPropertiesConfiguration*))); // load the current brush engine icon for the brush editor toolbar button KisPaintOpFactory* paintOp = KisPaintOpRegistry::instance()->get(paintop.id()); QString pixFilename = KoResourcePaths::findResource("kis_images", paintOp->pixmap()); m_brushEditorPopupButton->setIcon(QIcon(pixFilename)); m_presetsPopup->setCurrentPaintOp(paintop.id()); if (m_presetsPopup->currentPaintOp() != paintop.id()) { // Must change the paintop as the current one is not supported // by the new colorspace. dbgKrita << "current paintop " << paintop.name() << " was not set, not supported by colorspace"; } /** * We will get more update signals from the configuration widgets * but they might be delayed by some internal deferring timers, * so just call the slot directly */ slotUpdatePreset(); } KisPaintOpPresetSP KisPaintopBox::defaultPreset(const KoID& paintOp) { QString defaultName = paintOp.id() + ".kpp"; QString path = KoResourcePaths::findResource("kis_defaultpresets", defaultName); KisPaintOpPresetSP preset = new KisPaintOpPreset(path); if (!preset->load()) { preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp); } Q_ASSERT(preset); Q_ASSERT(preset->valid()); return preset; } KisPaintOpPresetSP KisPaintopBox::activePreset(const KoID& paintOp) { if (m_paintOpPresetMap[paintOp] == 0) { m_paintOpPresetMap[paintOp] = defaultPreset(paintOp); } return m_paintOpPresetMap[paintOp]; } void KisPaintopBox::updateCompositeOp(QString compositeOpID) { if (!m_optionWidget) return; KisSignalsBlocker blocker(m_optionWidget); KisNodeSP node = m_resourceProvider->currentNode(); if (node && node->paintDevice()) { if (!node->paintDevice()->colorSpace()->hasCompositeOp(compositeOpID)) compositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id(); m_cmbCompositeOp->blockSignals(true); m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID)); m_cmbCompositeOp->blockSignals(false); if (compositeOpID != m_currCompositeOpID) { m_resourceProvider->currentPreset()->settings()->setPaintOpCompositeOp(compositeOpID); m_optionWidget->setConfiguration(m_resourceProvider->currentPreset()->settings().data()); m_resourceProvider->setCurrentCompositeOp(compositeOpID); m_currCompositeOpID = compositeOpID; } } } void KisPaintopBox::setWidgetState(int flags) { if (flags & (ENABLE_COMPOSITEOP | DISABLE_COMPOSITEOP)) { m_cmbCompositeOp->setEnabled(flags & ENABLE_COMPOSITEOP); m_eraseModeButton->setEnabled(flags & ENABLE_COMPOSITEOP); } if (flags & (ENABLE_PRESETS | DISABLE_PRESETS)) { m_presetSelectorPopupButton->setEnabled(flags & ENABLE_PRESETS); m_brushEditorPopupButton->setEnabled(flags & ENABLE_PRESETS); } for (int i = 0; i < 3; ++i) { if (flags & (ENABLE_OPACITY | DISABLE_OPACITY)) m_sliderChooser[i]->getWidget("opacity")->setEnabled(flags & ENABLE_OPACITY); if (flags & (ENABLE_FLOW | DISABLE_FLOW)) m_sliderChooser[i]->getWidget("flow")->setEnabled(flags & ENABLE_FLOW); if (flags & (ENABLE_SIZE | DISABLE_SIZE)) m_sliderChooser[i]->getWidget("size")->setEnabled(flags & ENABLE_SIZE); } } void KisPaintopBox::setSliderValue(const QString& sliderID, qreal value) { for (int i = 0; i < 3; ++i) { KisDoubleSliderSpinBox* slider = m_sliderChooser[i]->getWidget(sliderID); slider->blockSignals(true); slider->setValue(value); slider->blockSignals(false); } } void KisPaintopBox::slotSetPaintop(const QString& paintOpId) { if (KisPaintOpRegistry::instance()->get(paintOpId) != 0) { KoID id(paintOpId, KisPaintOpRegistry::instance()->get(paintOpId)->name()); setCurrentPaintop(id); } } void KisPaintopBox::slotInputDeviceChanged(const KoInputDevice& inputDevice) { TabletToolMap::iterator toolData = m_tabletToolMap.find(inputDevice); if (toolData == m_tabletToolMap.end()) { KisConfig cfg; KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer(false); KisPaintOpPresetSP preset; if (inputDevice.pointer() == QTabletEvent::Eraser) { preset = rserver->resourceByName(cfg.readEntry(QString("LastEraser_%1").arg(inputDevice.uniqueTabletId()), "Eraser_circle")); } else { preset = rserver->resourceByName(cfg.readEntry(QString("LastPreset_%1").arg(inputDevice.uniqueTabletId()), "Basic_tip_default")); } if (!preset) { preset = rserver->resourceByName("Basic_tip_default"); } if (preset) { setCurrentPaintop(preset->paintOp(), preset); } } else { setCurrentPaintop(toolData->paintOpID, toolData->preset); } m_currTabletToolID = TabletToolID(inputDevice); } void KisPaintopBox::slotCanvasResourceChanged(int key, const QVariant &value) { if (m_viewManager) { sender()->blockSignals(true); KisPaintOpPresetSP preset = m_viewManager->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset && m_resourceProvider->currentPreset()->name() != preset->name()) { QString compositeOp = preset->settings()->getString("CompositeOp"); updateCompositeOp(compositeOp); resourceSelected(preset.data()); } m_presetsChooserPopup->canvasResourceChanged(preset.data(), preset); if (m_resourceProvider->currentCompositeOp() != m_currCompositeOpID) { updateCompositeOp(m_resourceProvider->currentCompositeOp()); } if (key == KisCanvasResourceProvider::EraserMode) { m_eraseAction->setChecked(value.toBool()); } sender()->blockSignals(false); } } void KisPaintopBox::slotSaveActivePreset() { KisPaintOpPresetSP curPreset = m_resourceProvider->currentPreset(); if (!curPreset) return; m_favoriteResourceManager->setBlockUpdates(true); KisPaintOpPresetSP newPreset = curPreset->clone(); KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QString saveLocation = rServer->saveLocation(); QString presetName = m_presetsPopup->getPresetName(); QString presetFilename = saveLocation + presetName + newPreset->defaultFileExtension(); QStringList tags; KisPaintOpPresetSP resource = rServer->resourceByName(presetName); if (resource) { tags = rServer->assignedTagsList(resource.data()); rServer->removeResourceAndBlacklist(resource); } newPreset->setImage(m_presetsPopup->cutOutOverlay()); newPreset->setFilename(presetFilename); newPreset->setName(presetName); newPreset->setPresetDirty(false); rServer->addResource(newPreset); Q_FOREACH (const QString & tag, tags) { rServer->addTag(newPreset.data(), tag); } // HACK ALERT! the server does not notify the observers // automatically, so we need to call theupdate manually! rServer->tagCategoryMembersChanged(); restoreResource(newPreset.data()); m_favoriteResourceManager->setBlockUpdates(false); } void KisPaintopBox::slotUpdatePreset() { if (!m_resourceProvider->currentPreset()) return; // block updates of avoid some over updating of the option widget m_blockUpdate = true; setSliderValue("size", m_resourceProvider->currentPreset()->settings()->paintOpSize().width()); { qreal opacity = m_resourceProvider->currentPreset()->settings()->paintOpOpacity(); m_resourceProvider->setOpacity(opacity); setSliderValue("opacity", opacity); setWidgetState(ENABLE_OPACITY); } { setSliderValue("flow", m_resourceProvider->currentPreset()->settings()->paintOpFlow()); setWidgetState(ENABLE_FLOW); } { updateCompositeOp(m_resourceProvider->currentPreset()->settings()->paintOpCompositeOp()); setWidgetState(ENABLE_COMPOSITEOP); } { const bool newEraseMode = m_resourceProvider->eraserMode(); if (m_eraseAction->isChecked() != newEraseMode) { KisSignalsBlocker b(m_eraseAction, m_eraseModeButton); m_eraseAction->setChecked(newEraseMode); m_eraseModeButton->setChecked(newEraseMode); } } m_blockUpdate = false; } void KisPaintopBox::slotSetupDefaultPreset() { KisPaintOpPresetSP preset = defaultPreset(m_resourceProvider->currentPreset()->paintOp()); preset->settings()->setOptionsWidget(m_optionWidget); m_optionWidget->setConfiguration(preset->settings()); m_optionWidget->writeConfiguration(const_cast(preset->settings().data())); } void KisPaintopBox::slotNodeChanged(const KisNodeSP node) { if (m_previousNode.isValid() && m_previousNode->paintDevice()) disconnect(m_previousNode->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*))); // Reconnect colorspace change of node if (node && node->paintDevice()) { connect(node->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*))); m_resourceProvider->setCurrentCompositeOp(m_currCompositeOpID); m_previousNode = node; slotColorSpaceChanged(node->colorSpace()); } if (m_optionWidget) { m_optionWidget->setNode(node); } } void KisPaintopBox::slotColorSpaceChanged(const KoColorSpace* colorSpace) { m_cmbCompositeOp->validate(colorSpace); } void KisPaintopBox::slotToggleEraseMode(bool checked) { const bool oldEraserMode = m_resourceProvider->eraserMode(); m_resourceProvider->setEraserMode(checked); KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings(); m_optionWidget->setConfiguration(settings.data()); if (oldEraserMode != checked && m_eraserBrushSizeEnabled) { const qreal currentSize = m_sliderChooser[0]->getWidget("size")->value(); // remember brush size. set the eraser size to the normal brush size if not set if (checked) { settings->setSavedBrushSize(currentSize); if (qFuzzyIsNull(settings->savedEraserSize())) { settings->setSavedEraserSize(currentSize); } } else { settings->setSavedEraserSize(currentSize); if (qFuzzyIsNull(settings->savedBrushSize())) { settings->setSavedBrushSize(currentSize); } } //update value in UI (this is the main place the value is 'stored' in memory) qreal updateSize = checked ? settings->savedEraserSize() : settings->savedBrushSize(); m_sliderChooser[0]->getWidget("size")->setValue(updateSize); m_sliderChooser[1]->getWidget("size")->setValue(updateSize); m_sliderChooser[2]->getWidget("size")->setValue(updateSize); } } void KisPaintopBox::slotSetCompositeMode(int index) { Q_UNUSED(index); if (m_resourceProvider->currentPreset()->settings()->hasProperty("CompositeOp")) { QString compositeOp = m_cmbCompositeOp->selectedCompositeOp().id(); updateCompositeOp(compositeOp); } } void KisPaintopBox::slotHorizontalMirrorChanged(bool value) { m_resourceProvider->setMirrorHorizontal(value); } void KisPaintopBox::slotVerticalMirrorChanged(bool value) { m_resourceProvider->setMirrorVertical(value); } void KisPaintopBox::sliderChanged(int n) { if (!m_optionWidget) // widget will not exist if the are no documents open return; KisSignalsBlocker blocker(m_optionWidget); m_optionWidget->writeConfiguration(const_cast(m_resourceProvider->currentPreset()->settings().data())); qreal opacity = m_sliderChooser[n]->getWidget("opacity")->value(); qreal flow = m_sliderChooser[n]->getWidget("flow")->value(); qreal size = m_sliderChooser[n]->getWidget("size")->value(); setSliderValue("opacity", opacity); setSliderValue("flow" , flow); setSliderValue("size" , size); if (m_presetsEnabled) { // IMPORTANT: set the PaintOp size before setting the other properties // it wont work the other way qreal sizeDiff = size - m_resourceProvider->currentPreset()->settings()->paintOpSize().width(); m_resourceProvider->currentPreset()->settings()->changePaintOpSize(sizeDiff, 0); m_resourceProvider->currentPreset()->settings()->setPaintOpOpacity(opacity); m_resourceProvider->currentPreset()->settings()->setPaintOpFlow(flow); KisLockedPropertiesProxy *propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(m_resourceProvider->currentPreset()->settings()); propertiesProxy->setProperty("OpacityValue", opacity); propertiesProxy->setProperty("FlowValue", flow); delete propertiesProxy; m_optionWidget->setConfiguration(m_resourceProvider->currentPreset()->settings().data()); } m_resourceProvider->setOpacity(opacity); m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data()); } void KisPaintopBox::slotSlider1Changed() { sliderChanged(0); } void KisPaintopBox::slotSlider2Changed() { sliderChanged(1); } void KisPaintopBox::slotSlider3Changed() { sliderChanged(2); } void KisPaintopBox::slotToolChanged(KoCanvasController* canvas, int toolId) { Q_UNUSED(canvas); Q_UNUSED(toolId); if (!m_viewManager->canvasBase()) return; QString id = KoToolManager::instance()->activeToolId(); KisTool* tool = dynamic_cast(KoToolManager::instance()->toolById(m_viewManager->canvasBase(), id)); if (tool) { int flags = tool->flags(); if (flags & KisTool::FLAG_USES_CUSTOM_COMPOSITEOP) { setWidgetState(ENABLE_COMPOSITEOP | ENABLE_OPACITY); } else { setWidgetState(DISABLE_COMPOSITEOP | DISABLE_OPACITY); } if (flags & KisTool::FLAG_USES_CUSTOM_PRESET) { setWidgetState(ENABLE_PRESETS | ENABLE_SIZE | ENABLE_FLOW); slotUpdatePreset(); m_presetsEnabled = true; } else { setWidgetState(DISABLE_PRESETS | DISABLE_SIZE | DISABLE_FLOW); m_presetsEnabled = false; } } else setWidgetState(DISABLE_ALL); } void KisPaintopBox::slotOpacityChanged(qreal opacity) { if (m_blockUpdate || !m_optionWidget) return; m_blockUpdate = true; for (int i = 0; i < 3; ++i) { KisDoubleSliderSpinBox *opacitySlider = m_sliderChooser[i]->getWidget("opacity"); opacitySlider->blockSignals(true); opacitySlider->setValue(opacity); opacitySlider->blockSignals(false); } if (m_presetsEnabled) { m_resourceProvider->currentPreset()->settings()->setPaintOpOpacity(opacity); m_optionWidget->setConfiguration(m_resourceProvider->currentPreset()->settings().data()); } m_blockUpdate = false; } void KisPaintopBox::slotPreviousFavoritePreset() { if (!m_favoriteResourceManager) return; int i = 0; Q_FOREACH (KisPaintOpPresetSP preset, m_favoriteResourceManager->favoritePresetList()) { if (m_resourceProvider->currentPreset() && m_resourceProvider->currentPreset()->name() == preset->name()) { if (i > 0) { m_favoriteResourceManager->slotChangeActivePaintop(i - 1); } else { m_favoriteResourceManager->slotChangeActivePaintop(m_favoriteResourceManager->numFavoritePresets() - 1); } return; } i++; } } void KisPaintopBox::slotNextFavoritePreset() { if (!m_favoriteResourceManager) return; int i = 0; Q_FOREACH (KisPaintOpPresetSP preset, m_favoriteResourceManager->favoritePresetList()) { if (m_resourceProvider->currentPreset()->name() == preset->name()) { if (i < m_favoriteResourceManager->numFavoritePresets() - 1) { m_favoriteResourceManager->slotChangeActivePaintop(i + 1); } else { m_favoriteResourceManager->slotChangeActivePaintop(0); } return; } i++; } } void KisPaintopBox::slotSwitchToPreviousPreset() { if (m_resourceProvider->previousPreset()) { setCurrentPaintop(m_resourceProvider->previousPreset()->paintOp(), m_resourceProvider->previousPreset()); } } void KisPaintopBox::slotUnsetEraseMode() { m_eraseAction->setChecked(false); } void KisPaintopBox::slotToggleAlphaLockMode(bool checked) { if (checked) { m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-locked")); } else { m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-unlocked")); } m_resourceProvider->setGlobalAlphaLock(checked); } void KisPaintopBox::slotReloadPreset() { KisSignalsBlocker blocker(m_optionWidget); //Here using the name and fetching the preset from the server was the only way the load was working. Otherwise it was not loading. KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); KisPaintOpPresetSP preset = rserver->resourceByName(m_resourceProvider->currentPreset()->name()); if (preset) { preset->load(); preset->settings()->setOptionsWidget(m_optionWidget); m_optionWidget->setConfiguration(preset->settings()); m_presetsPopup->resourceSelected(preset.data()); } slotUpdatePreset(); } void KisPaintopBox::slotGuiChangedCurrentPreset() // Called only when UI is changed and not when preset is changed { m_optionWidget->writeConfiguration(const_cast(m_resourceProvider->currentPreset()->settings().data())); slotUpdatePreset(); m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data()); m_presetsPopup->updateViewSettings(); } void KisPaintopBox::slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p) { QMapIterator i(p->getProperties()); while (i.hasNext()) { i.next(); m_resourceProvider->currentPreset()->settings()->setProperty(i.key(), QVariant(i.value())); if (m_resourceProvider->currentPreset()->settings()->hasProperty(i.key() + "_previous")) { m_resourceProvider->currentPreset()->settings()->removeProperty(i.key() + "_previous"); } } slotGuiChangedCurrentPreset(); } void KisPaintopBox::slotDropLockedOption(KisPropertiesConfiguration* p) { KisSignalsBlocker blocker(m_optionWidget); KisPaintOpPresetSP preset = m_resourceProvider->currentPreset(); { KisPaintOpPreset::DirtyStateSaver dirtySaver(preset.data()); QMapIterator i(p->getProperties()); while (i.hasNext()) { i.next(); if (preset->settings()->hasProperty(i.key() + "_previous")) { preset->settings()->setProperty(i.key(), preset->settings()->getProperty(i.key() + "_previous")); preset->settings()->removeProperty(i.key() + "_previous"); } } m_optionWidget->setConfiguration(preset->settings()); } slotUpdatePreset(); } void KisPaintopBox::slotDirtyPresetToggled(bool value) { if (!value) { slotReloadPreset(); m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data()); m_presetsPopup->updateViewSettings(); } m_dirtyPresetsEnabled = value; KisConfig cfg; cfg.setUseDirtyPresets(m_dirtyPresetsEnabled); } void KisPaintopBox::slotEraserBrushSizeToggled(bool value) { m_eraserBrushSizeEnabled = value; KisConfig cfg; cfg.setUseEraserBrushSize(m_eraserBrushSizeEnabled); } void KisPaintopBox::slotUpdateSelectionIcon() { m_hMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-horizontal")); m_vMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-vertical")); KisConfig cfg; if (!cfg.toolOptionsInDocker()) { m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure")); } m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01")); m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02")); m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose")); m_eraseAction->setIcon(KisIconUtils::loadIcon("draw-eraser")); m_reloadAction->setIcon(KisIconUtils::loadIcon("view-refresh")); } + +void KisPaintopBox::slotLockXMirrorToggle(bool toggleLock) { + m_resourceProvider->setMirrorHorizontalLock(toggleLock); +} + +void KisPaintopBox::slotLockYMirrorToggle(bool toggleLock) { + m_resourceProvider->setMirrorVerticalLock(toggleLock); +} + +void KisPaintopBox::slotHideDecorationMirrorX(bool toggled) { + m_resourceProvider->setMirrorHorizontalHideDecorations(toggled); +} + +void KisPaintopBox::slotHideDecorationMirrorY(bool toggled) { + m_resourceProvider->setMirrorVerticalHideDecorations(toggled); +} + + + +void KisPaintopBox::slotMoveToCenterMirrorX() { + m_resourceProvider->mirrorHorizontalMoveCanvasToCenter(); +} + +void KisPaintopBox::slotMoveToCenterMirrorY() { + m_resourceProvider->mirrorVerticalMoveCanvasToCenter(); +} diff --git a/libs/ui/kis_paintop_box.h b/libs/ui/kis_paintop_box.h index b589556def..3442814726 100644 --- a/libs/ui/kis_paintop_box.h +++ b/libs/ui/kis_paintop_box.h @@ -1,237 +1,246 @@ /* * kis_paintop_box.h - part of KImageShop/Krayon/Krita * * Copyright (c) 2004-2008 Boudewijn Rempt (boud@valdyas.org) * Copyright (C) 2011 Silvio Heinrich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTOP_BOX_H_ #define KIS_PAINTOP_BOX_H_ #include #include #include #include #include #include #include #include #include #include #include "kritaui_export.h" class QToolButton; class QString; class QHBoxLayout; class KoColorSpace; class KoResource; class KoCanvasController; class KisViewManager; class KisCanvasResourceProvider; class KisPopupButton; class KisToolOptionsPopup; class KisPaintOpPresetsPopup; class KisPaintOpPresetsChooserPopup; class KisPaintOpConfigWidget; class KisCompositeOpComboBox; class KisWidgetChooser; class KisFavoriteResourceManager; class KisAction; /** * This widget presents all paintops that a user can paint with. * Paintops represent real-world tools or the well-known Shoup * computer equivalents that do nothing but change color. * * To incorporate the dirty preset functionality and locked settings * the following slots are added * void slotReloadPreset(); void slotGuiChangedCurrentPreset(); void slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p); void slotDropLockedOption(KisPropertiesConfiguration* p); void slotDirtyPresetToggled(bool); Everytime a value is changed in a preset, the preset is made dirty through the onChange() function. For Locked Settings however, a changed Locked Setting will not cause a preset to become dirty. That is becuase it borrows its values from the KisLockedPropertiesServer. Hence the dirty state of the Preset is kept consistent before and after a writeConfiguration operation in most cases. * XXX: When we have a lot of paintops, replace the listbox * with a table, and for every category a combobox. * * XXX: instead of text, use pretty pictures. */ class KRITAUI_EXPORT KisPaintopBox : public QWidget { Q_OBJECT enum { ENABLE_PRESETS = 0x0001, DISABLE_PRESETS = 0x0002, ENABLE_COMPOSITEOP = 0x0004, DISABLE_COMPOSITEOP = 0x0008, ENABLE_OPACITY = 0x0010, DISABLE_OPACITY = 0x0020, ENABLE_FLOW = 0x0040, DISABLE_FLOW = 0x0080, ENABLE_SIZE = 0x0100, DISABLE_SIZE = 0x0200, ENABLE_ALL = 0x5555, DISABLE_ALL = 0xAAAA }; public: KisPaintopBox(KisViewManager* view, QWidget* parent, const char* name); ~KisPaintopBox(); void restoreResource(KoResource* resource); /** * Update the option widgets to the argument ones, removing the currently set widgets. */ void newOptionWidgets(const QList > & optionWidgetList); KisFavoriteResourceManager *favoriteResourcesManager() { return m_favoriteResourceManager; } public Q_SLOTS: void slotColorSpaceChanged(const KoColorSpace* colorSpace); void slotInputDeviceChanged(const KoInputDevice & inputDevice); void slotCanvasResourceChanged(int key, const QVariant& v); void resourceSelected(KoResource* resource); private: void setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP preset = 0); void setCurrentPaintopAndReload(const KoID& paintop, KisPaintOpPresetSP preset); KisPaintOpPresetSP defaultPreset(const KoID& paintOp); KisPaintOpPresetSP activePreset(const KoID& paintOp); void updateCompositeOp(QString compositeOpID); void setWidgetState(int flags); void setSliderValue(const QString& sliderID, qreal value); void sliderChanged(int n); private Q_SLOTS: void slotSaveActivePreset(); void slotUpdatePreset(); void slotSetupDefaultPreset(); void slotNodeChanged(const KisNodeSP node); void slotToggleEraseMode(bool checked); void slotSetCompositeMode(int index); void slotSetPaintop(const QString& paintOpId); void slotHorizontalMirrorChanged(bool value); void slotVerticalMirrorChanged(bool value); void slotSlider1Changed(); void slotSlider2Changed(); void slotSlider3Changed(); void slotToolChanged(KoCanvasController* canvas, int toolId); void slotOpacityChanged(qreal); void slotPreviousFavoritePreset(); void slotNextFavoritePreset(); void slotSwitchToPreviousPreset(); void slotUnsetEraseMode(); void slotToggleAlphaLockMode(bool); void slotReloadPreset(); void slotGuiChangedCurrentPreset(); void slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p); void slotDropLockedOption(KisPropertiesConfiguration* p); void slotDirtyPresetToggled(bool); void slotEraserBrushSizeToggled(bool); void slotUpdateSelectionIcon(); + void slotLockXMirrorToggle(bool); + void slotLockYMirrorToggle(bool); + void slotMoveToCenterMirrorX(); + void slotMoveToCenterMirrorY(); + void slotHideDecorationMirrorX(bool); + void slotHideDecorationMirrorY(bool); + + + private: KisCanvasResourceProvider* m_resourceProvider; QHBoxLayout* m_layout; QWidget* m_paintopWidget; KisPaintOpConfigWidget* m_optionWidget; KisPopupButton* m_toolOptionsPopupButton; KisPopupButton* m_brushEditorPopupButton; KisPopupButton* m_presetSelectorPopupButton; KisCompositeOpComboBox* m_cmbCompositeOp; QToolButton* m_eraseModeButton; QToolButton* m_alphaLockButton; QToolButton* m_hMirrorButton; QToolButton* m_vMirrorButton; KisToolOptionsPopup* m_toolOptionsPopup; KisPaintOpPresetsPopup* m_presetsPopup; KisPaintOpPresetsChooserPopup* m_presetsChooserPopup; KisViewManager* m_viewManager; KisPopupButton* m_workspaceWidget; KisWidgetChooser* m_sliderChooser[3]; QMap m_paintopOptionWidgets; KisFavoriteResourceManager* m_favoriteResourceManager; QToolButton* m_reloadButton; KisAction* m_eraseAction; KisAction* m_reloadAction; QString m_currCompositeOpID; KisNodeWSP m_previousNode; KisAction* m_hMirrorAction; KisAction* m_vMirrorAction; struct TabletToolID { TabletToolID(const KoInputDevice& dev) { uniqueID = dev.uniqueTabletId(); // Only the eraser is special, and we don't look at Cursor pointer = QTabletEvent::Pen; if (dev.pointer() == QTabletEvent::Eraser) { pointer = QTabletEvent::Eraser; } } bool operator == (const TabletToolID& id) const { return pointer == id.pointer && uniqueID == id.uniqueID; } bool operator < (const TabletToolID& id) const { if (uniqueID == id.uniqueID) return pointer < id.pointer; return uniqueID < id.uniqueID; } QTabletEvent::PointerType pointer; qint64 uniqueID; }; struct TabletToolData { KoID paintOpID; KisPaintOpPresetSP preset; }; typedef QMap TabletToolMap; typedef QMap PaintOpPresetMap; TabletToolMap m_tabletToolMap; PaintOpPresetMap m_paintOpPresetMap; TabletToolID m_currTabletToolID; bool m_presetsEnabled; bool m_blockUpdate; bool m_dirtyPresetsEnabled; bool m_eraserBrushSizeEnabled; }; #endif //KIS_PAINTOP_BOX_H_ diff --git a/libs/ui/tests/kis_animation_exporter_test.cpp b/libs/ui/tests/kis_animation_exporter_test.cpp index c46dc966a9..81d5dd14e2 100644 --- a/libs/ui/tests/kis_animation_exporter_test.cpp +++ b/libs/ui/tests/kis_animation_exporter_test.cpp @@ -1,98 +1,99 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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_test.h" #include "kis_animation_exporter.h" #include #include #include "KisPart.h" #include "kis_image.h" #include "KisDocument.h" #include "kis_image_animation_interface.h" #include "KoColor.h" #include "kis_time_range.h" #include "kis_keyframe_channel.h" void KisAnimationExporterTest::testAnimationExport() { KisDocument *document = KisPart::instance()->createDocument(); QRect rect(0,0,512,512); + QRect fillRect(10,0,502,512); TestUtil::MaskParent p(rect); document->setCurrentImage(p.image); const KoColorSpace *cs = p.image->colorSpace(); KUndo2Command parentCommand; + p.layer->enableAnimation(); KisKeyframeChannel *rasterChannel = p.layer->getKeyframeChannel(KisKeyframeChannel::Content.id()); rasterChannel->addKeyframe(1, &parentCommand); rasterChannel->addKeyframe(2, &parentCommand); p.image->animationInterface()->setFullClipRange(KisTimeRange::fromTime(0, 2)); KisPaintDeviceSP dev = p.layer->paintDevice(); - dev->fill(rect, KoColor(Qt::red, cs)); - QImage frame0 = dev->createThumbnail(50, 50); + dev->fill(fillRect, KoColor(Qt::red, cs)); + QImage frame0 = dev->convertToQImage(0, rect); p.image->animationInterface()->switchCurrentTimeAsync(1); p.image->waitForDone(); - dev->fill(rect, KoColor(Qt::green, cs)); - QImage frame1 = dev->createThumbnail(50, 50); + dev->fill(fillRect, KoColor(Qt::green, cs)); + QImage frame1 = dev->convertToQImage(0, rect); p.image->animationInterface()->switchCurrentTimeAsync(2); p.image->waitForDone(); - dev->fill(rect, KoColor(Qt::blue, cs)); - QImage frame2 = dev->createThumbnail(50, 50); - - KisAnimationExporter exporter(document, "export-test.png", 0, 2); - QSignalSpy spy(&exporter, SIGNAL(sigExportProgress(int))); + dev->fill(fillRect, KoColor(Qt::blue, cs)); + QImage frame2 = dev->convertToQImage(0, rect); + KisAnimationExportSaver exporter(document, "export-test.png", 0, 2); + QSignalSpy spy(document, SIGNAL(sigProgress(int))); QVERIFY(spy.isValid()); exporter.exportAnimation(); // If we had Qt5 already: // spy.wait(100); // spy.wait(100); // spy.wait(100); // But in the meanwhile... QTest::qWait(1000); - QCOMPARE(spy.count(), 3); - QCOMPARE(spy.at(0).at(0).value(), 0); - QCOMPARE(spy.at(1).at(0).value(), 1); - QCOMPARE(spy.at(2).at(0).value(), 2); + //QCOMPARE(spy.count(), 3); + //QCOMPARE(spy.at(0).at(0).value(), 0); + //QCOMPARE(spy.at(1).at(0).value(), 1); + //QCOMPARE(spy.at(2).at(0).value(), 2); // FIXME: Export doesn't seem to work from unit tests - /* + QImage exported; - exported.load("export-test0.png"); - QVERIFY(exported == frame0); - exported.load("export-test1.png"); - QVERIFY(exported == frame1); + exported.load("export-test0000.png"); + QCOMPARE(exported, frame0); + + exported.load("export-test0001.png"); + QCOMPARE(exported, frame1); - exported.load("export-test2.png"); - QVERIFY(exported == frame2); - */ + exported.load("export-test0002.png"); + QCOMPARE(exported, frame2); } QTEST_MAIN(KisAnimationExporterTest) diff --git a/libs/vectorimage/libwmf/WmfPainterBackend.cpp b/libs/vectorimage/libwmf/WmfPainterBackend.cpp index a65b7dc2f4..6e95058aa4 100644 --- a/libs/vectorimage/libwmf/WmfPainterBackend.cpp +++ b/libs/vectorimage/libwmf/WmfPainterBackend.cpp @@ -1,844 +1,836 @@ /* This file is part of the KDE libraries * * Copyright (c) 2003 thierry lorthiois (lorthioist@wanadoo.fr) * 2009-2011 Inge Wallin * * 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 "WmfPainterBackend.h" #include #include #include #include #include "WmfEnums.h" #include "WmfParser.h" #define DEBUG_WMFPAINT 0 /** Namespace for Windows Metafile (WMF) classes */ namespace Libwmf { WmfPainterBackend::WmfPainterBackend(QPainter *painter, const QSizeF &outputSize) : WmfAbstractBackend() , mPainter(painter) , mOutputSize(outputSize) , mSaveCount(0) { mTarget = painter->device(); mIsInternalPainter = false; mWorldTransform = QTransform(); } WmfPainterBackend::~WmfPainterBackend() { } #if 0 bool WmfPainterBackend::play(QPaintDevice& target) { if (!mPainter) mPainter = new QPainter(&target); mIsInternalPainter = true; if (mPainter->isActive()) return false; mTarget = ⌖ // Play the wmf file mSaveCount = 0; bool ret = WmfAbstractBackend::play(); // Make sure that the painter is in the same state as before WmfParser::play() for (; mSaveCount > 0; mSaveCount--) restore(); return ret; } #endif bool WmfPainterBackend::play() { - // If there is already a painter and it's owned by us, then delete it. - if (mPainter && mIsInternalPainter) - delete mPainter; - - mTarget = mPainter->device(); - // Play the wmf file - bool ret = m_parser->play(this); - - return ret; + return m_parser->play(this); } //----------------------------------------------------------------------------- // Virtual Painter bool WmfPainterBackend::begin(const QRect &boundingBox) { // If the painter is our own, we have to call begin() on it. // If it's external, we assume that it's already done for us. if (mIsInternalPainter) { if (!mPainter->begin(mTarget)) return false; } // For calculations of window / viewport during the painting mWindowOrg = QPoint(0, 0); mViewportOrg = QPoint(0, 0); mWindowExtIsSet = false; mViewportExtIsSet = false; mOutputTransform = mPainter->transform(); mWorldTransform = QTransform(); //mPainter->setBrush(QBrush(Qt::NoBrush)); #if DEBUG_WMFPAINT - debugVectorImage << "Using QPainter: " << mPainter->pen() << mPainter->brush() + debugVectorImage << "Using QPainter: " << mPainter->pen() << mPainter->brush() << "Background: " << mPainter->background() << " " << mPainter->backgroundMode(); #endif qreal scaleX = mOutputSize.width() / boundingBox.width(); qreal scaleY = mOutputSize.height() / boundingBox.height(); // Transform the WMF object so that it fits in the shape as much // as possible. The topleft will be the top left of the shape. mPainter->scale( scaleX, scaleY ); mOutputTransform = mPainter->transform(); mPainter->setRenderHint(QPainter::Antialiasing); mPainter->setRenderHint(QPainter::TextAntialiasing); mSaveCount = 0; return true; } bool WmfPainterBackend::end() { // Make sure that the painter is in the same state as before calling play above(). for (; mSaveCount > 0; mSaveCount--) restore(); bool ret = true; if (mIsInternalPainter) ret = mPainter->end(); return ret; } void WmfPainterBackend::save() { // A little trick here: Save the worldTransform in the painter. // If we didn't do this, we would have to create a separate stack // for these. // // FIXME: We should collect all the parts of the DC that are not // stored in the painter and save them separately. QTransform savedTransform = mPainter->worldTransform(); mPainter->setWorldTransform(mWorldTransform); mPainter->save(); ++mSaveCount; mPainter->setWorldTransform(savedTransform); } void WmfPainterBackend::restore() { if (mSaveCount > 0) { mPainter->restore(); mSaveCount--; } else { debugVectorImage << "restore(): try to restore painter without save"; } // We used a trick in save() and stored the worldTransform in // the painter. Now restore the full transformation. mWorldTransform = mPainter->worldTransform(); recalculateWorldTransform(); } void WmfPainterBackend::setCompositionMode(QPainter::CompositionMode mode) { #if DEBUG_WMFPAINT debugVectorImage << mode << "(ignored)"; #endif // FIXME: This doesn't work. I don't understand why, but when I // enable this all backgrounds become black. /iw Q_UNUSED(mode); //mPainter->setCompositionMode(mode); } // --------------------------------------------------------------------- // World Transform, Window and Viewport // General note about coordinate spaces and transforms: // // There are several coordinate spaces in use when drawing an WMF file: // 1. The object space, in which the objects' coordinates are expressed inside the WMF. // In general there are several of these. // 2. The page space, which is where they end up being painted in the WMF picture. // The union of these form the bounding box of the WMF. // 3. (possibly) the output space, where the WMF picture itself is placed // and/or scaled, rotated, etc // // The transform between spaces 1. and 2. is called the World Transform. // The world transform can be changed either through calls to change // the window or viewport or through calls to setWorldTransform() or // modifyWorldTransform(). // // The transform between spaces 2. and 3. is the transform that the QPainter // already contains when it is given to us. We need to save this and reapply // it after the world transform has changed. We call this transform the Output // Transform in lack of a better word. (Some sources call it the Device Transform.) // // FIXME: // To change those functions it's better to have // a large set of WMF files. WMF special case includes : // - without call to setWindowOrg and setWindowExt // - change the origin or the scale in the middle of the drawing // - negative width or height // and relative/absolute coordinate void WmfPainterBackend::recalculateWorldTransform() { mWorldTransform = QTransform(); if (!mWindowExtIsSet && !mViewportExtIsSet) return; // FIXME: Check windowExt == 0 in any direction qreal windowViewportScaleX; qreal windowViewportScaleY; if (mWindowExtIsSet && mViewportExtIsSet) { // Both window and viewport are set. windowViewportScaleX = qreal(mViewportExt.width()) / qreal(mWindowExt.width()); windowViewportScaleY = qreal(mViewportExt.height()) / qreal(mWindowExt.height()); #if DEBUG_WMFPAINT debugVectorImage << "Scale for Window -> Viewport" << windowViewportScaleX << windowViewportScaleY; #endif } else { // At most one of window and viewport ext is set: Use same width for window and viewport windowViewportScaleX = qreal(1.0); windowViewportScaleY = qreal(1.0); #if DEBUG_WMFPAINT debugVectorImage << "Only one of Window or Viewport set: scale window -> viewport = 1"; #endif } // Negative window extensions mean flip the picture. Handle this here. bool flip = false; qreal midpointX = 0.0; qreal midpointY = 0.0; qreal scaleX = 1.0; qreal scaleY = 1.0; if (mWindowExt.width() < 0) { midpointX = (mWindowOrg.x() + mWindowExt.width()) / qreal(2.0); scaleX = -1.0; flip = true; } if (mWindowExt.height() < 0) { midpointY = (mWindowOrg.y() + mWindowExt.height()) / qreal(2.0); scaleY = -1.0; flip = true; } if (flip) { //debugVectorImage << "Flipping round midpoint" << midpointX << midpointY << scaleX << scaleY; mWorldTransform.translate(midpointX, midpointY); mWorldTransform.scale(scaleX, scaleY); mWorldTransform.translate(-midpointX, -midpointY); //debugVectorImage << "After flipping for window" << mWorldTransform; } // Calculate the world transform. mWorldTransform.translate(-mWindowOrg.x(), -mWindowOrg.y()); mWorldTransform.scale(windowViewportScaleX, windowViewportScaleY); if (mViewportExtIsSet) { mWorldTransform.translate(mViewportOrg.x(), mViewportOrg.y()); - } + } else { // If viewport is not set, but window is *and* the window // width/height is negative, then we must compensate for this. // If the width/height is positive, we already did it with the // first translate before the scale() above. - if (mWindowExt.width() < 0) + if (mWindowExt.width() < 0) mWorldTransform.translate(mWindowOrg.x() + mWindowExt.width(), qreal(0.0)); - if (mWindowExt.height() < 0) + if (mWindowExt.height() < 0) mWorldTransform.translate(qreal(0.0), mWindowOrg.y() + mWindowExt.height()); } //debugVectorImage << "After window viewport calculation" << mWorldTransform; // FIXME: also handle negative viewport extensions? If so, do it here. // Apply the world transform to the painter. mPainter->setWorldTransform(mWorldTransform); // Apply the output transform. QTransform currentMatrix = mPainter->worldTransform(); QTransform newMatrix = currentMatrix * mOutputTransform; //debugVectorImage << "Output transform" << mOutputTransform; //debugVectorImage << "Total transform" << newMatrix; mPainter->setWorldTransform( newMatrix ); } void WmfPainterBackend::setWindowOrg(int left, int top) { #if DEBUG_WMFPAINT debugVectorImage << left << " " << top; #endif // Only set WindowOrg if it changes. See the Unanswered Question in libemf. if (mWindowOrg == QPoint(left, top)) return; mWindowOrg = QPoint(left, top); recalculateWorldTransform(); #if 0 // Debug code. Draw a rectangle with some decoration to show see // if all the transformations work. mPainter->save(); // Paint a black rectangle around the current window. mPainter->setPen(Qt::green); QRect windowRect = QRect(mWindowOrg, mWindowExt); mPainter->drawRect(windowRect); #if 0 // Paint a black line from the Window origin to (0, 0) mPainter->drawLine(mWindowOrg, QPoint(0, 0)); mPainter->setPen(Qt::red); mPainter->drawRect(boundingRect()); mPainter->drawLine(boundingRect().topLeft(), QPoint(0, 0)); #endif mPainter->restore(); debugVectorImage << "Window rect: " << windowRect; debugVectorImage << "Bounding rect: " << boundingRect(); #endif } void WmfPainterBackend::setWindowExt(int width, int height) { #if DEBUG_WMFPAINT debugVectorImage << width << " " << height; #endif // Only set WindowExt if it changes. See the Unanswered Question in libemf. if (mWindowExt == QSize(width, height)) return; mWindowExt = QSize(width, height); mWindowExtIsSet = true; recalculateWorldTransform(); #if 0 // Debug code. Draw a rectangle with some decoration to show see // if all the transformations work. mPainter->save(); // Paint a red rectangle around the current window. mPainter->setPen(Qt::red); QRect windowRect = QRect(mWindowOrg, mWindowExt); mPainter->drawRect(windowRect); // Paint a line from the Window origin to (0, 0) mPainter->drawLine(mWindowOrg, QPoint(0, 0)); mPainter->setPen(Qt::black); mPainter->drawRect(boundingRect()); mPainter->drawLine(boundingRect().topLeft(), QPoint(0, 0)); mPainter->restore(); debugVectorImage << "Window rect: " << windowRect; debugVectorImage << "Bounding rect: " << boundingRect(); #endif } void WmfPainterBackend::setViewportOrg( int left, int top ) { #if DEBUG_WMFPAINT debugVectorImage << left << top; #endif // Only set ViewportOrg if it changes. See the Unanswered Question in libemf. if (mViewportOrg == QPoint(left, top)) return; mViewportOrg = QPoint(left, top); recalculateWorldTransform(); } void WmfPainterBackend::setViewportExt( int width, int height ) { #if DEBUG_WMFPAINT debugVectorImage << width << height; #endif // Only set ViewportOrg if it changes. See the Unanswered Question in libemf. if (mViewportExt == QSize(width, height)) return; mViewportExt = QSize(width, height); mViewportExtIsSet = true; recalculateWorldTransform(); } void WmfPainterBackend::setMatrix(WmfDeviceContext &context, const QMatrix &wm, bool combine) { Q_UNUSED(context); #if DEBUG_WMFPAINT debugVectorImage << wm << " " << combine; #endif mPainter->setMatrix(wm, combine); recalculateWorldTransform(); } // ---------------------------------------------------------------- // Drawing void WmfPainterBackend::setPixel(WmfDeviceContext &context, int x, int y, QColor color) { #if DEBUG_WMFPAINT debugVectorImage << x << y << color; #endif updateFromDeviceContext(context); QPen oldPen = mPainter->pen(); QPen pen = oldPen; pen.setColor(color); mPainter->setPen(pen); mPainter->drawLine(x, y, x, y); mPainter->setPen(oldPen); } void WmfPainterBackend::lineTo(WmfDeviceContext &context, int x, int y) { updateFromDeviceContext(context); #if DEBUG_WMFPAINT debugVectorImage << x << ", " << y << " using " << mPainter->pen(); #endif QPoint newPoint(x, y); mPainter->drawLine(context.currentPosition, newPoint); context.currentPosition = newPoint; } void WmfPainterBackend::drawRect(WmfDeviceContext &context, int x, int y, int w, int h) { updateFromDeviceContext(context); #if DEBUG_WMFPAINT debugVectorImage << x << ", " << y << ", " << w << ", " << h; debugVectorImage << "Using QPainter: " << mPainter->pen() << mPainter->brush(); #endif mPainter->drawRect(x, y, w, h); } void WmfPainterBackend::drawRoundRect(WmfDeviceContext &context, int x, int y, int w, int h, int roudw, int roudh) { #if DEBUG_WMFPAINT debugVectorImage << x << ", " << y << ", " << w << ", " << h; #endif updateFromDeviceContext(context); mPainter->drawRoundRect(x, y, w, h, roudw, roudh); } void WmfPainterBackend::drawEllipse(WmfDeviceContext &context, int x, int y, int w, int h) { #if DEBUG_WMFPAINT debugVectorImage << x << ", " << y << ", " << w << ", " << h; #endif updateFromDeviceContext(context); mPainter->drawEllipse(x, y, w, h); } void WmfPainterBackend::drawArc(WmfDeviceContext &context, int x, int y, int w, int h, int a, int alen) { #if DEBUG_WMFPAINT debugVectorImage << x << ", " << y << ", " << w << ", " << h; #endif updateFromDeviceContext(context); mPainter->drawArc(x, y, w, h, a, alen); } void WmfPainterBackend::drawPie(WmfDeviceContext &context, int x, int y, int w, int h, int a, int alen) { #if DEBUG_WMFPAINT debugVectorImage << x << ", " << y << ", " << w << ", " << h; #endif updateFromDeviceContext(context); mPainter->drawPie(x, y, w, h, a, alen); } void WmfPainterBackend::drawChord(WmfDeviceContext &context, int x, int y, int w, int h, int a, int alen) { #if DEBUG_WMFPAINT debugVectorImage << x << ", " << y << ", " << w << ", " << h << ", " << a << ", " << alen; #endif updateFromDeviceContext(context); mPainter->drawChord(x, y, w, h, a, alen); } void WmfPainterBackend::drawPolyline(WmfDeviceContext &context, const QPolygon &pa) { #if DEBUG_WMFPAINT debugVectorImage << pa; #endif updateFromDeviceContext(context); mPainter->drawPolyline(pa); } void WmfPainterBackend::drawPolygon(WmfDeviceContext &context, const QPolygon &pa) { #if DEBUG_WMFPAINT debugVectorImage << pa; debugVectorImage << "Using QPainter: " << mPainter->pen() << mPainter->brush(); #endif updateFromDeviceContext(context); if (context.polyFillMode) mPainter->drawPolygon(pa, Qt::WindingFill); else mPainter->drawPolygon(pa, Qt::OddEvenFill); } void WmfPainterBackend::drawPolyPolygon(WmfDeviceContext &context, QList& listPa) { #if DEBUG_WMFPAINT debugVectorImage; #endif updateFromDeviceContext(context); mPainter->save(); QBrush brush = mPainter->brush(); // define clipping region QRegion region; Q_FOREACH (const QPolygon & pa, listPa) { region = region.xored(pa); } mPainter->setClipRegion(region); // fill polygons if (brush != Qt::NoBrush) { //debugVectorImage << "Filling polygon with " << brush; mPainter->fillRect(region.boundingRect(), brush); } // draw polygon's border mPainter->setClipping(false); if (mPainter->pen().style() != Qt::NoPen) { mPainter->setBrush(Qt::NoBrush); Q_FOREACH (const QPolygon & pa, listPa) { #if DEBUG_WMFPAINT debugVectorImage << pa; #endif if (context.polyFillMode == WINDING) mPainter->drawPolygon(pa, Qt::WindingFill); else mPainter->drawPolygon(pa, Qt::OddEvenFill); } } // restore previous state mPainter->restore(); } void WmfPainterBackend::drawImage(WmfDeviceContext &context, int x, int y, const QImage &img, int sx, int sy, int sw, int sh) { #if DEBUG_WMFPAINT debugVectorImage << x << " " << y << " " << sx << " " << sy << " " << sw << " " << sh; #endif updateFromDeviceContext(context); mPainter->drawImage(x, y, img, sx, sy, sw, sh); } void WmfPainterBackend::patBlt(WmfDeviceContext &context, int x, int y, int width, int height, quint32 rasterOperation) { #if DEBUG_WMFPAINT debugVectorImage << x << y << width << height << hex << rasterOperation << dec; #endif updateFromDeviceContext(context); // 0x00f00021 is the PatCopy raster operation which just fills a rectangle with a brush. // This seems to be the most common one. // // FIXME: Implement the rest of the raster operations. if (rasterOperation == 0x00f00021) { // Would have been nice if we didn't have to pull out the // brush to use it with fillRect()... QBrush brush = mPainter->brush(); mPainter->fillRect(x, y, width, height, brush); } } void WmfPainterBackend::drawText(WmfDeviceContext &context, int x, int y, const QString& text) { #if DEBUG_WMFPAINT debugVectorImage << x << y << hex << dec << text; #endif updateFromDeviceContext(context); // The TA_UPDATECP flag tells us to use the current position if (context.textAlign & TA_UPDATECP) { // (left, top) position = current logical position x = context.currentPosition.x(); y = context.currentPosition.y(); #if DEBUG_WMFPAINT debugVectorImage << "Using current position:" << x << y; #endif } QFontMetrics fm(mPainter->font(), mTarget); int width = fm.width(text) + fm.descent(); // fm.width(text) isn't right with Italic text int height = fm.height(); // Horizontal align. These flags are supposed to be mutually exclusive. if ((context.textAlign & TA_CENTER) == TA_CENTER) x -= (width / 2); else if ((context.textAlign & TA_RIGHT) == TA_RIGHT) x -= width; - // Vertical align. + // Vertical align. if ((context.textAlign & TA_BASELINE) == TA_BASELINE) y -= fm.ascent(); // (height - fm.descent()) is used in qwmf. This should be the same. else if ((context.textAlign & TA_BOTTOM) == TA_BOTTOM) { y -= height; } #if DEBUG_WMFPAINT debugVectorImage << "font = " << mPainter->font() << " pointSize = " << mPainter->font().pointSize() << "ascent = " << fm.ascent() << " height = " << fm.height() << "leading = " << fm.leading(); #endif // Use the special pen defined by the foregroundTextColor in the device context for text. mPainter->save(); mPainter->setPen(context.foregroundTextColor); // If the actual height is < 0, we should use device units. This // means that if the text is currently upside-down due to some // transformations, we should un-upside-down it before painting. //debugVectorImage << "fontheight:" << context.height << "height:" << height << "y" << y; if (context.height < 0 && mPainter->worldTransform().m22() < 0) { mPainter->translate(0, -(y - height / 2)); mPainter->scale(qreal(1.0), qreal(-1.0)); mPainter->translate(0, +(y - height / 2)); // This is necessary to get drawText(x, y, ...) right below. y = -3 * y; } mPainter->translate(x, y); if (context.escapement != 0) { mPainter->rotate(qreal(context.escapement) / qreal(-10.0)); } mPainter->drawText(0, 0, width, height, Qt::AlignLeft|Qt::AlignTop, text); mPainter->restore(); } // ---------------------------------------------------------------- // Private functions // If anything has changed in the device context that is relevant to // the QPainter, then update the painter with the corresponding data. // void WmfPainterBackend::updateFromDeviceContext(WmfDeviceContext &context) { // Graphic objects if (context.changedItems & DCBrush) { mPainter->setBrush(context.brush); #if DEBUG_WMFPAINT debugVectorImage << "*** Setting fill brush to" << context.brush; #endif } // FIXME: context.image if (context.changedItems & DCFont) { mPainter->setFont(context.font); #if DEBUG_WMFPAINT debugVectorImage << "*** Setting font to" << context.font; #endif } if (context.changedItems & DCPalette) { // NYI #if DEBUG_WMFPAINT debugVectorImage << "*** Setting palette (NYI)"; #endif } if (context.changedItems & DCPen) { QPen p = context.pen; int width = p.width(); if (dynamic_cast(mTarget)) { width = 0; } else if (width == 1) // I'm unsure of this, but it seems that WMF uses line // width == 1 as cosmetic pen. Or it could just be that // any line width < 1 should be drawn as width == 1. The // WMF spec doesn't mention the term "cosmetic pen" // anywhere so we don't get any clue there. // // For an example where this is shown clearly, see // wmf_tests.doc, in the colored rectangles and the polypolygon. width = 0; #if 0 else { // WMF spec: width of pen in logical coordinate // => width of pen proportional with device context width QRect rec = mPainter->window(); // QPainter documentation says this is equivalent of xFormDev, but it doesn't compile. Bug reported. QRect devRec = rec * mPainter->matrix(); if (rec.width() != 0) width = (width * devRec.width()) / rec.width() ; else width = 0; } #endif p.setWidth(width); mPainter->setPen(p); #if DEBUG_WMFPAINT debugVectorImage << "*** Setting pen to" << p; #endif } if (context.changedItems & DCClipRegion) { // Not used until SETCLIPREGION is used #if DEBUG_WMFPAINT //debugVectorImage << "*** region changed to" << context.region; #endif } // Structure objects if (context.changedItems & DCBgTextColor) { mPainter->setBackground(QBrush(context.backgroundColor)); #if DEBUG_WMFPAINT debugVectorImage << "*** Setting background text color to" << context.backgroundColor; #endif } //---------------------------------------------------------------- // Output surface not supported //DCViewportExt //DCViewportorg - //DCWindowExt - //DCWindoworg + //DCWindowExt + //DCWindoworg //---------------------------------------------------------------- // Graphic Properties if (context.changedItems & DCBgMixMode) { // FIXME: Check the default value for this. mPainter->setBackgroundMode(context.bgMixMode == TRANSPARENT ? Qt::TransparentMode : Qt::OpaqueMode); #if DEBUG_WMFPAINT debugVectorImage << "*** Setting background mode to" << context.bgMixMode; #endif } //Break extra space NYI //Font mapping mode NYI if (context.changedItems & DCFgMixMode) { // FIXME: Check the default value for this. QPainter::CompositionMode compMode = QPainter::CompositionMode_Source; if (context.rop < 17) compMode = koWmfOpTab16[context.rop]; mPainter->setCompositionMode(compMode); #if DEBUG_WMFPAINT debugVectorImage << "*** Setting composition mode to" << context.rop; #endif } //layoutMode not necessary to handle here //Mapping mode NYI //PolyFillMode not necessary to handle here //Stretchblt mode NYI //textAlign not necessary to handle here //Text extra space NYI // Reset all changes until next time. context.changedItems = 0; } } diff --git a/libs/widgetutils/KoFileDialog.cpp b/libs/widgetutils/KoFileDialog.cpp index 5a4b336f18..4e2c6db9a0 100644 --- a/libs/widgetutils/KoFileDialog.cpp +++ b/libs/widgetutils/KoFileDialog.cpp @@ -1,401 +1,405 @@ /* This file is part of the KDE project Copyright (C) 2013 - 2014 Yue Liu 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 "KoFileDialog.h" #include #include #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KoFileDialog::Private { public: Private(QWidget *parent_, KoFileDialog::DialogType dialogType_, const QString caption_, const QString defaultDir_, const QString dialogName_) : parent(parent_) , type(dialogType_) , dialogName(dialogName_) , caption(caption_) , defaultDirectory(defaultDir_) , filterList(QStringList()) , defaultFilter(QString()) , swapExtensionOrder(false) { } ~Private() { } QWidget *parent; KoFileDialog::DialogType type; QString dialogName; QString caption; QString defaultDirectory; QStringList filterList; QString defaultFilter; QScopedPointer fileDialog; QString mimeType; bool swapExtensionOrder; }; KoFileDialog::KoFileDialog(QWidget *parent, KoFileDialog::DialogType type, const QString &dialogName) : d(new Private(parent, type, "", getUsedDir(dialogName), dialogName)) { if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { // The GTK file dialog interferes with the Qt clipboard; so disable that QClipboard *cb = QApplication::clipboard(); cb->blockSignals(true); } } KoFileDialog::~KoFileDialog() { if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { // And re-enable the clipboard. QClipboard *cb = QApplication::clipboard(); cb->blockSignals(false); } delete d; } void KoFileDialog::setCaption(const QString &caption) { d->caption = caption; } void KoFileDialog::setDefaultDir(const QString &defaultDir, bool override) { if (override || d->defaultDirectory.isEmpty() || !QFile(d->defaultDirectory).exists()) { QFileInfo f(defaultDir); d->defaultDirectory = f.absoluteFilePath(); } } void KoFileDialog::setOverrideDir(const QString &overrideDir) { d->defaultDirectory = overrideDir; } void KoFileDialog::setImageFilters() { QStringList imageFilters; // add filters for all formats supported by QImage Q_FOREACH (const QByteArray &format, QImageReader::supportedImageFormats()) { imageFilters << QLatin1String("image/") + format; } setMimeTypeFilters(imageFilters); } void KoFileDialog::setMimeTypeFilters(const QStringList &filterList, QString defaultFilter) { d->filterList = getFilterStringListFromMime(filterList, true); if (!defaultFilter.isEmpty()) { QStringList defaultFilters = getFilterStringListFromMime(QStringList() << defaultFilter, false); if (defaultFilters.size() > 0) { defaultFilter = defaultFilters.first(); } } d->defaultFilter = defaultFilter; } QString KoFileDialog::selectedNameFilter() const { return d->fileDialog->selectedNameFilter(); } QString KoFileDialog::selectedMimeType() const { return d->mimeType; } void KoFileDialog::createFileDialog() { //qDebug() << "createFIleDialog. Parent:" << d->parent << "Caption:" << d->caption << "Default directory:" << d->defaultDirectory << "Default iflter:" << d->defaultFilter; d->fileDialog.reset(new QFileDialog(d->parent, d->caption, d->defaultDirectory)); d->fileDialog->setOption(QFileDialog::DontUseNativeDialog, false); d->fileDialog->setOption(QFileDialog::DontConfirmOverwrite, false); d->fileDialog->setOption(QFileDialog::HideNameFilterDetails, true); if (d->type == SaveFile) { d->fileDialog->setAcceptMode(QFileDialog::AcceptSave); d->fileDialog->setFileMode(QFileDialog::AnyFile); } else { // open / import d->fileDialog->setAcceptMode(QFileDialog::AcceptOpen); if (d->type == ImportDirectory || d->type == OpenDirectory){ d->fileDialog->setFileMode(QFileDialog::Directory); d->fileDialog->setOption(QFileDialog::ShowDirsOnly, true); } else { // open / import file(s) if (d->type == OpenFile || d->type == ImportFile) { d->fileDialog->setFileMode(QFileDialog::ExistingFile); } else { // files d->fileDialog->setFileMode(QFileDialog::ExistingFiles); } } } d->fileDialog->setNameFilters(d->filterList); if (!QFileInfo(d->defaultDirectory).isDir()) { //qDebug() << "Finding the right mimetype for the given file" << d->defaultDirectory; QString mime = KisMimeDatabase::mimeTypeForFile(d->defaultDirectory); QString description = KisMimeDatabase::descriptionForMimeType(mime); Q_FOREACH(const QString &filter, d->filterList) { //qDebug() << "\tConsidering" << filter; if (filter.startsWith(description)) { d->fileDialog->selectNameFilter(filter); break; } } } else if (!d->defaultFilter.isEmpty()) { d->fileDialog->selectNameFilter(d->defaultFilter); } if (d->type == ImportDirectory || d->type == ImportFile || d->type == ImportFiles || d->type == SaveFile) { d->fileDialog->setWindowModality(Qt::WindowModal); } } QString KoFileDialog::filename() { QString url; createFileDialog(); if (d->fileDialog->exec() == QDialog::Accepted) { url = d->fileDialog->selectedFiles().first(); } if (!url.isEmpty()) { if (d->type == SaveFile && QFileInfo(url).suffix().isEmpty()) { QString selectedFilter; // index 0 is all supported; if that is chosen, saveDocument will automatically make it .kra for (int i = 1; i < d->filterList.size(); ++i) { if (d->filterList[i].startsWith(d->fileDialog->selectedNameFilter())) { selectedFilter = d->filterList[i]; break; } } int start = selectedFilter.indexOf("*.") + 1; int end = selectedFilter.indexOf(" ", start); int n = end - start; QString extension = selectedFilter.mid(start, n); - url = url + "." + extension; + if (!(extension.contains(".") || url.endsWith("."))) { + extension = "." + extension; + } + url = url + extension; + } d->mimeType = KisMimeDatabase::mimeTypeForFile(url); saveUsedDir(url, d->dialogName); } return url; } QStringList KoFileDialog::filenames() { QStringList urls; createFileDialog(); if (d->fileDialog->exec() == QDialog::Accepted) { urls = d->fileDialog->selectedFiles(); } if (urls.size() > 0) { saveUsedDir(urls.first(), d->dialogName); } return urls; } QStringList KoFileDialog::splitNameFilter(const QString &nameFilter, QStringList *mimeList) { Q_ASSERT(mimeList); QStringList filters; QString description; if (nameFilter.contains("(")) { description = nameFilter.left(nameFilter.indexOf("(") -1).trimmed(); } QStringList entries = nameFilter.mid(nameFilter.indexOf("(") + 1).split(" ",QString::SkipEmptyParts ); entries.sort(); Q_FOREACH (QString entry, entries) { entry = entry.remove("*"); entry = entry.remove(")"); QString mimeType = KisMimeDatabase::mimeTypeForSuffix(entry); if (mimeType != "application/octet-stream") { if (!mimeList->contains(mimeType)) { mimeList->append(mimeType); filters.append(KisMimeDatabase::descriptionForMimeType(mimeType) + " ( *" + entry + " )"); } } else { filters.append(entry.remove(".").toUpper() + " " + description + " ( *." + entry + " )"); } } return filters; } const QStringList KoFileDialog::getFilterStringListFromMime(const QStringList &_mimeList, bool withAllSupportedEntry) { QStringList mimeSeen; // 1 QString allSupported; // 2 QString kritaNative; // 3 QString ora; QStringList ret; QStringList mimeList = _mimeList; mimeList.sort(); Q_FOREACH(const QString &mimeType, mimeList) { if (!mimeSeen.contains(mimeType)) { QString description = KisMimeDatabase::descriptionForMimeType(mimeType); if (description.isEmpty() && !mimeType.isEmpty()) { description = mimeType.split("/")[1]; if (description.startsWith("x-")) { description = description.remove(0, 2); } } QString oneFilter; QStringList patterns = KisMimeDatabase::suffixesForMimeType(mimeType); QStringList globPatterns; Q_FOREACH(const QString &pattern, patterns) { if (pattern.startsWith(".")) { globPatterns << "*" + pattern; } else if (pattern.startsWith("*.")) { globPatterns << pattern; } else { globPatterns << "*." + pattern; } } Q_FOREACH(const QString &glob, globPatterns) { if (d->swapExtensionOrder) { oneFilter.prepend(glob + " "); if (withAllSupportedEntry) { allSupported.prepend(glob + " "); } #ifdef Q_OS_LINUX if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { oneFilter.prepend(glob.toUpper() + " "); if (withAllSupportedEntry) { allSupported.prepend(glob.toUpper() + " "); } } #endif } else { oneFilter.append(glob + " "); if (withAllSupportedEntry) { allSupported.append(glob + " "); } #ifdef Q_OS_LINUX if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { oneFilter.append(glob.toUpper() + " "); if (withAllSupportedEntry) { allSupported.append(glob.toUpper() + " "); } } #endif } } Q_ASSERT(!description.isEmpty()); oneFilter = description + " ( " + oneFilter + ")"; if (mimeType == "application/x-krita") { kritaNative = oneFilter; continue; } if (mimeType == "image/openraster") { ora = oneFilter; continue; } else { ret << oneFilter; } mimeSeen << mimeType; } } ret.sort(); ret.removeDuplicates(); if (!ora.isEmpty()) ret.prepend(ora); if (!kritaNative.isEmpty()) ret.prepend(kritaNative); if (!allSupported.isEmpty()) ret.prepend(i18n("All supported formats") + " ( " + allSupported + (")")); return ret; } QString KoFileDialog::getUsedDir(const QString &dialogName) { if (dialogName.isEmpty()) return ""; KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString dir = group.readEntry(dialogName); return dir; } void KoFileDialog::saveUsedDir(const QString &fileName, const QString &dialogName) { if (dialogName.isEmpty()) return; QFileInfo fileInfo(fileName); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); group.writeEntry(dialogName, fileInfo.absolutePath()); } diff --git a/packaging/linux/snap/setup/gui/krita.desktop b/packaging/linux/snap/setup/gui/krita.desktop index e40e8d6ec7..6dc91c8186 100755 --- a/packaging/linux/snap/setup/gui/krita.desktop +++ b/packaging/linux/snap/setup/gui/krita.desktop @@ -1,115 +1,116 @@ [Desktop Entry] 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 Exec=krita %U GenericName=Digital Painting GenericName[bs]=Digitalno Bojenje GenericName[ca]=Dibuix digital GenericName[ca@valencia]=Dibuix digital GenericName[da]=Digital tegning GenericName[de]=Digitales Malen GenericName[el]=Ψηφιακή ζωγραφική GenericName[en_GB]=Digital Painting GenericName[es]=Pintura digital GenericName[et]=Digitaalne joonistamine GenericName[eu]=Pintura digitala GenericName[fi]=Digitaalimaalaus GenericName[fr]=Peinture numérique GenericName[gl]=Debuxo dixital GenericName[hu]=Digitális festészet GenericName[ia]=Pintura Digital GenericName[it]=Pittura digitale GenericName[kk]=Цифрлық сурет салу GenericName[lt]=Skaitmeninis piešimas GenericName[mr]=डिजिटल पेंटिंग GenericName[nb]=Digital maling GenericName[nl]=Digitaal schilderen GenericName[pl]=Cyfrowe malowanie GenericName[pt]=Pintura Digital GenericName[pt_BR]=Pintura digital GenericName[ru]=Цифровая живопись GenericName[sk]=Digitálne maľovanie GenericName[sl]=Digitalno slikanje GenericName[sv]=Digital målning GenericName[tr]=Sayısal Boyama GenericName[ug]=سىفىرلىق رەسىم سىزغۇ GenericName[uk]=Цифрове малювання GenericName[x-test]=xxDigital Paintingxx MimeType=application/x-krita;image/openraster;application/x-krita-paintoppreset; Comment=Pixel-based image manipulation program for the Calligra Suite Comment[ca]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra Comment[ca@valencia]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra Comment[de]=Pixelbasiertes Bildbearbeitungsprogramm für die Calligra-Suite Comment[es]=Programa de manipulación de imágenes basado en píxeles para la suite Calligra +Comment[it]=Programma di manipolazione delle immagini basato su pixel per Calligra Suite Comment[nl]=Afbeeldingsbewerkingsprogramma gebaseerd op pixels voor de Calligra Suite Comment[pl]=Program do obróbki obrazów na poziomie pikseli dla Pakietu Calligra Comment[pt]='Plugin' de manipulação de imagens em pixels para o Calligra Stage Comment[sv]=Bildpunktsbaserat bildbehandlingsprogram för Calligra-sviten Comment[uk]=Програма для роботи із растровими зображеннями для комплексу програм Calligra Comment[x-test]=xxPixel-based image manipulation program for the Calligra Suitexx Type=Application Icon=${SNAP}/meta/gui/calligrakrita.png Categories=Qt;KDE;Graphics; X-KDE-NativeMimeType=application/x-krita X-KDE-ExtraNativeMimeTypes= StartupNotify=true X-Krita-Version=28 diff --git a/plugins/dockers/CMakeLists.txt b/plugins/dockers/CMakeLists.txt index e3a7a3b436..a2c02809e8 100644 --- a/plugins/dockers/CMakeLists.txt +++ b/plugins/dockers/CMakeLists.txt @@ -1,23 +1,24 @@ add_subdirectory(defaultdockers) add_subdirectory(smallcolorselector) add_subdirectory(specificcolorselector) add_subdirectory(digitalmixer) add_subdirectory(advancedcolorselector) add_subdirectory(presetdocker) add_subdirectory(historydocker) add_subdirectory(channeldocker) add_subdirectory(imagedocker) add_subdirectory(artisticcolorselector) add_subdirectory(tasksetdocker) add_subdirectory(compositiondocker) add_subdirectory(patterndocker) add_subdirectory(griddocker) if(HAVE_OCIO) add_subdirectory(lut) endif() add_subdirectory(overview) add_subdirectory(palettedocker) add_subdirectory(colorslider) add_subdirectory(animation) add_subdirectory(presethistory) add_subdirectory(shapedockers) +add_subdirectory(histogram) diff --git a/plugins/dockers/colorslider/kis_color_slider_widget.h b/plugins/dockers/colorslider/kis_color_slider_widget.h index 6c2cbb2cde..92c7f33e91 100644 --- a/plugins/dockers/colorslider/kis_color_slider_widget.h +++ b/plugins/dockers/colorslider/kis_color_slider_widget.h @@ -1,84 +1,84 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2014 Wolthera van Hövell * * 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_COLOR_SLIDER_WIDGET_H_ #define _KIS_COLOR_SLIDER_WIDGET_H_ #include #include "kis_canvas2.h" #include class KoColorSpace; class KoColorDisplayRendererInterface; class QVBoxLayout; class KisColorSliderInput; class KisSignalCompressor; class QBitArray; class KisColorSliderWidget : public QWidget { Q_OBJECT public: KisColorSliderWidget(KoColorDisplayRendererInterface *displayRenderer, QWidget* parent, KisCanvas2* canvas, QBitArray SlidersConfigArray); ~KisColorSliderWidget(); //bool customColorSpaceUsed(); public Q_SLOTS: //void setColorSpace(const KoColorSpace*); void setColor(const KoColor&); void slotConfigChanged(); private Q_SLOTS: void update(); void updateTimeout(); void hueUpdate(int h); void satUpdate(int s, int type); void toneUpdate(int l, int type); void setSlidersVisible(QBitArray SlidersConfigArray); void setConfig(); //void setCustomColorSpace(const KoColorSpace *); Q_SIGNALS: void colorChanged(const KoColor&); void updated(); void hueUpdated(int); void satUpdated(int, int); void toneUpdated(int, int); private: QList m_inputs; - const KoColorSpace* m_colorSpace; +// const KoColorSpace* m_colorSpace; QVBoxLayout *m_layout; KoColor m_color; bool m_updateAllowed; KisSignalCompressor *m_updateCompressor; KisSignalCompressor *m_configCompressor; KoColorDisplayRendererInterface *m_displayRenderer; KisCanvas2 *m_canvas; KisColorSliderInput* hsvH; KisColorSliderInput* hsvS; KisColorSliderInput* hsvV; KisColorSliderInput* hslH; KisColorSliderInput* hslS; KisColorSliderInput* hslL; KisColorSliderInput* hsiH; KisColorSliderInput* hsiS; KisColorSliderInput* hsiI; KisColorSliderInput* hsyH; KisColorSliderInput* hsyS; KisColorSliderInput* hsyY; }; #endif diff --git a/plugins/dockers/histogram/CMakeLists.txt b/plugins/dockers/histogram/CMakeLists.txt new file mode 100644 index 0000000000..929f7da944 --- /dev/null +++ b/plugins/dockers/histogram/CMakeLists.txt @@ -0,0 +1,4 @@ +set(KRITA_HISTOGRAMDOCKER_SOURCES histogramdocker.cpp histogramdocker_dock.cpp ) +add_library(kritahistogramdocker MODULE ${KRITA_HISTOGRAMDOCKER_SOURCES}) +target_link_libraries(kritahistogramdocker kritaui) +install(TARGETS kritahistogramdocker DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/histogram/histogramdocker.cpp b/plugins/dockers/histogram/histogramdocker.cpp new file mode 100644 index 0000000000..e3953ff44b --- /dev/null +++ b/plugins/dockers/histogram/histogramdocker.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 Eugene Ingerman + * + * 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 "histogramdocker.h" + +#include + +#include + + +#include +#include +#include + +#include + +#include "kis_config.h" +#include "kis_cursor.h" +#include "kis_global.h" +#include "kis_types.h" +#include "KisViewManager.h" + +#include "histogramdocker_dock.h" +#include + +K_PLUGIN_FACTORY_WITH_JSON(HistogramDockerPluginFactory, "krita_histogramdocker.json", registerPlugin();) + +class HistogramDockerDockFactory : public KoDockFactoryBase { +public: + HistogramDockerDockFactory() + { + } + + virtual QString id() const + { + return QString( "HistogramDocker" ); + } + + virtual Qt::DockWidgetArea defaultDockWidgetArea() const + { + return Qt::RightDockWidgetArea; + } + + virtual QDockWidget* createDockWidget() + { + HistogramDockerDock * dockWidget = new HistogramDockerDock(); + dockWidget->setObjectName(id()); + + return dockWidget; + } + + DockPosition defaultDockPosition() const + { + return DockRight; + } +private: + + +}; + + +HistogramDockerPlugin::HistogramDockerPlugin(QObject *parent, const QVariantList &) + : QObject(parent) +{ + KoDockRegistry::instance()->add(new HistogramDockerDockFactory()); +} + +HistogramDockerPlugin::~HistogramDockerPlugin() +{ + m_view = 0; +} + +#include "histogramdocker.moc" diff --git a/plugins/dockers/histogram/histogramdocker.h b/plugins/dockers/histogram/histogramdocker.h new file mode 100644 index 0000000000..650eaa5323 --- /dev/null +++ b/plugins/dockers/histogram/histogramdocker.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Eugene Ingerman + * + * 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 _HISTOGRAM_DOCKER_H_ +#define _HISTOGRAM_DOCKER_H_ + +#include +#include + +class KisViewManager; + +/** + * Template of view plugin + */ +class HistogramDockerPlugin : public QObject +{ + Q_OBJECT + public: + HistogramDockerPlugin(QObject *parent, const QVariantList &); + virtual ~HistogramDockerPlugin(); + private: + KisViewManager* m_view; +}; + +#endif diff --git a/plugins/dockers/histogram/histogramdocker_dock.cpp b/plugins/dockers/histogram/histogramdocker_dock.cpp new file mode 100644 index 0000000000..7744a8b62f --- /dev/null +++ b/plugins/dockers/histogram/histogramdocker_dock.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 Eugene Ingerman + * + * 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 "histogramdocker_dock.h" + +#include +#include +#include + +#include "kis_canvas2.h" +#include +#include +#include "kis_image.h" +#include "kis_paint_device.h" +#include "kis_signal_compressor.h" +#include "kis_histogram_view.h" + +HistogramDockerDock::HistogramDockerDock( ) + : QDockWidget(i18n("Histogram")), + m_compressor(new KisSignalCompressor(200, KisSignalCompressor::POSTPONE, this)), + m_canvas(0), m_producer(nullptr) +{ + QWidget *page = new QWidget(this); + m_layout = new QVBoxLayout(page); + + m_histogramWidget = new KisHistogramView(this); + m_histogramWidget->setMinimumHeight(50); + m_histogramWidget->setSmoothHistogram(true); + m_layout->addWidget(m_histogramWidget, 1); + setWidget(page); + connect(m_compressor,SIGNAL(timeout()),SLOT(startUpdateCanvasProjection())); +} + + +void HistogramDockerDock::setCanvas(KoCanvasBase * canvas) +{ + if(m_canvas == canvas) + return; + + setEnabled(canvas != 0); + + if (m_canvas) { + m_canvas->disconnectCanvasObserver(this); + m_canvas->image()->disconnect(this); + } + m_canvas = dynamic_cast(canvas); + if (m_canvas && m_canvas->imageView() && m_canvas->imageView()->image() ) { + + KisPaintDeviceSP dev = m_canvas->image()->projection(); + auto cs = m_canvas->image()->colorSpace(); + + QList producers = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(cs,true); + m_producer = KoHistogramProducerFactoryRegistry::instance()->get(producers.at(0))->generate(); + m_histogramWidget->setPaintDevice( dev, m_producer, m_canvas->image()->bounds() ); + + connect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)), m_compressor, SLOT(start()), Qt::UniqueConnection); + connect(m_canvas->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), this, SLOT(sigColorSpaceChanged(const KoColorSpace*)), Qt::UniqueConnection); + + m_compressor->start(); + } +} + +void HistogramDockerDock::unsetCanvas() +{ + setEnabled(false); + m_canvas = 0; +} + +void HistogramDockerDock::startUpdateCanvasProjection() +{ + m_histogramWidget->startUpdateCanvasProjection(); +} + +void HistogramDockerDock::sigColorSpaceChanged(const KoColorSpace *cs) +{ + QList producers = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(cs,true); + m_producer = KoHistogramProducerFactoryRegistry::instance()->get(producers.at(0))->generate(); + m_histogramWidget->setProducer(m_producer); +} + diff --git a/plugins/dockers/histogram/histogramdocker_dock.h b/plugins/dockers/histogram/histogramdocker_dock.h new file mode 100644 index 0000000000..31150553fa --- /dev/null +++ b/plugins/dockers/histogram/histogramdocker_dock.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 Eugene Ingerman + * + * 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 _HISTOGRAM_DOCK_H_ +#define _HISTOGRAM_DOCK_H_ + +#include +#include +#include + +class QVBoxLayout; +class KisCanvas2; +class KisHistogramView; +class KisSignalCompressor; +class KoHistogramProducer; + +class HistogramDockerDock : public QDockWidget, public KoCanvasObserverBase { + Q_OBJECT +public: + HistogramDockerDock(); + + QString observerName() { return "HistogramDockerDock"; } + virtual void setCanvas(KoCanvasBase *canvas); + virtual void unsetCanvas(); + +public Q_SLOTS: + virtual void startUpdateCanvasProjection(); + //virtual void sigProfileChanged(const KoColorProfile* cp); + virtual void sigColorSpaceChanged(const KoColorSpace* cs); + +private: + QVBoxLayout *m_layout; + KisSignalCompressor *m_compressor; + KisHistogramView *m_histogramWidget; + KisCanvas2 *m_canvas; + KoHistogramProducer *m_producer; +}; + + +#endif diff --git a/plugins/dockers/histogram/krita_histogramdocker.json b/plugins/dockers/histogram/krita_histogramdocker.json new file mode 100644 index 0000000000..7108567064 --- /dev/null +++ b/plugins/dockers/histogram/krita_histogramdocker.json @@ -0,0 +1,9 @@ +{ + "Id": "Histogram Docker", + "Type": "Service", + "X-KDE-Library": "kritahistogramdocker", + "X-KDE-ServiceTypes": [ + "Krita/Dock" + ], + "X-Krita-Version": "28" +} diff --git a/plugins/extensions/histogram/kis_histogram_widget.cc b/plugins/extensions/histogram/kis_histogram_widget.cc index 61dff53efb..393b5b2b5d 100644 --- a/plugins/extensions/histogram/kis_histogram_widget.cc +++ b/plugins/extensions/histogram/kis_histogram_widget.cc @@ -1,147 +1,268 @@ /* * Copyright (c) 2004 Boudewijn Rempt * (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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_widget.h" #include #include #include #include #include #include #include "KoChannelInfo.h" +#include "KoBasicHistogramProducers.h" #include "kis_histogram_view.h" #include "kis_histogram.h" #include "kis_global.h" #include "kis_types.h" #include "kis_layer.h" #include "kis_paint_device.h" KisHistogramWidget::KisHistogramWidget(QWidget *parent, const char *name) : WdgHistogram(parent) { setObjectName(name); m_from = 0.0; m_width = 0.0; } KisHistogramWidget::~KisHistogramWidget() { } +QList KisHistogramWidget::producers() +{ + if (m_cs) + return KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs,true); + return QList(); +} + + +void KisHistogramWidget::setChannels(void) +{ + m_comboInfo.clear(); + m_channelStrings.clear(); + m_channels.clear(); + m_channelToOffset.clear(); + + QList list = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs,false); + + if (list.count() == 0) { + // XXX: No native histogram for this colorspace. Using converted RGB. We should have a warning + KoGenericRGBHistogramProducerFactory f; + addProducerChannels(f.generate()); + } else { + Q_FOREACH (const QString &id, list) { + KoHistogramProducer *producer = KoHistogramProducerFactoryRegistry::instance()->value(id)->generate(); + if (producer) { + addProducerChannels(producer); + } + } + } + + m_currentProducer = m_comboInfo.at(0).producer; + m_color = false; + // The currently displayed channel and its offset + m_channels.append(m_comboInfo.at(1).channel); + m_channelToOffset.append(0); +} + +void KisHistogramWidget::setCurrentChannels(const KoID& producerID, QList channels) +{ + setCurrentChannels( + KoHistogramProducerFactoryRegistry::instance()->value(producerID.id())->generate(), + channels); +} + +void KisHistogramWidget::setCurrentChannels(KoHistogramProducer *producer, QList channels) +{ + m_currentProducer = producer; + m_currentProducer->setView(m_from, m_width); + m_histogramView->setProducer(m_currentProducer); + //m_histogram->setChannel(0); // Set a default channel, just being nice + + m_channels.clear(); + m_channelToOffset.clear(); + + if (channels.count() == 0) { + return; + } + + QList producerChannels = m_currentProducer->channels(); + + for (int i = 0; i < channels.count(); i++) { + // Also makes sure the channel is actually in the producer's list + for (int j = 0; j < producerChannels.count(); j++) { + if (channels.at(i)->name() == producerChannels.at(j)->name()) { + m_channelToOffset.append(m_channels.count()); // The first we append maps to 0 + m_channels.append(channels.at(i)); + } + } + } + m_histogramView->setChannels(m_channels); +} + +void KisHistogramWidget::addProducerChannels(KoHistogramProducer *producer) +{ + if (!producer) return; + + ComboboxInfo info; + info.isProducer = true; + info.producer = producer; + m_comboInfo.append(info); + m_channelStrings.append(producer->id().name()); +} + +void KisHistogramWidget::setActiveChannel(int channel) +{ + ComboboxInfo info = m_comboInfo.at(channel); + if (info.producer != m_currentProducer) { + m_currentProducer = info.producer; + m_currentProducer->setView(m_from, m_width); + m_histogramView->setProducer(m_currentProducer); + } + + m_channels.clear(); + m_channelToOffset.clear(); + + if (info.isProducer) { + m_color = true; + m_channels = m_currentProducer->channels(); + for (int i = 0; i < m_channels.count(); i++) + m_channelToOffset.append(i); + //setChannel(0); // Set a default channel, just being nice + } else { + m_color = false; + QList channels = m_currentProducer->channels(); + for (int i = 0; i < channels.count(); i++) { + KoChannelInfo* channel = channels.at(i); + if (channel->name() == info.channel->name()) { + m_channels.append(channel); + m_channelToOffset.append(i); + break; + } + } + } + updateEnabled(); +} + +QStringList KisHistogramWidget::channelStrings() +{ + return m_channelStrings; +} + void KisHistogramWidget::setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds) { - grpType->disconnect(this); + radioLinear->disconnect(this); + radioLog->disconnect(this); cmbChannel->disconnect(this); - m_histogramView->setPaintDevice(dev, bounds); + m_cs = dev->colorSpace(); + + setChannels(); // Sets m_currentProducer to the first in the list + m_histogramView->setPaintDevice(dev, m_currentProducer, bounds); setActiveChannel(0); // So we have the colored one if there are colors // The channels cmbChannel->clear(); - cmbChannel->addItems(m_histogramView->channelStrings()); + cmbChannel->addItems(this->channelStrings()); cmbChannel->setCurrentIndex(0); // View display currentView->setMinimum(0); currentView->setMaximum(100); updateEnabled(); m_from = m_histogramView->currentProducer()->viewFrom(); m_width = m_histogramView->currentProducer()->viewWidth(); connect(radioLinear,SIGNAL(clicked()), this, SLOT(slotTypeSwitched())); connect(radioLog,SIGNAL(clicked()), this, SLOT(slotTypeSwitched())); connect(cmbChannel, SIGNAL(activated(int)), this, SLOT(setActiveChannel(int))); connect(zoomIn, SIGNAL(clicked()), this, SLOT(slotZoomIn())); connect(zoomOut, SIGNAL(clicked()), this, SLOT(slotZoomOut())); connect(currentView, SIGNAL(valueChanged(int)), this, SLOT(slide(int))); } -void KisHistogramWidget::setActiveChannel(int channel) -{ - m_histogramView->setActiveChannel(channel); - updateEnabled(); -} void KisHistogramWidget::slotTypeSwitched(void) { m_histogramView->setHistogramType( radioLinear->isChecked()?LINEAR:LOGARITHMIC ); } void KisHistogramWidget::setView(double from, double size) { m_from = from; m_width = size; if (m_from + m_width > 1.0) m_from = 1.0 - m_width; m_histogramView->setView(m_from, m_width); updateEnabled(); } void KisHistogramWidget::slotZoomIn() { if ((m_width / 2) >= m_histogramView->currentProducer()->maximalZoom()) { setView(m_from, m_width / 2); } } void KisHistogramWidget::slotZoomOut() { if (m_width * 2 <= 1.0) { setView(m_from, m_width * 2); } } void KisHistogramWidget::slide(int val) { // Beware: at the END (e.g. 100), we want to still view m_width: setView((static_cast(val) / 100.0) *(1.0 - m_width), m_width); } void KisHistogramWidget::updateEnabled() { if (m_histogramView->currentProducer()->maximalZoom() < 1.0) { if ((m_width / 2) >= m_histogramView->currentProducer()->maximalZoom()) { zoomIn->setEnabled(true); } else { zoomIn->setEnabled(false); } if (m_width * 2 <= 1.0) { zoomOut->setEnabled(true); } else { zoomOut->setEnabled(false); } if (m_width < 1.0) currentView->setEnabled(true); else currentView->setEnabled(false); } else { zoomIn->setEnabled(false); zoomOut->setEnabled(false); currentView->setEnabled(false); } } diff --git a/plugins/extensions/histogram/kis_histogram_widget.h b/plugins/extensions/histogram/kis_histogram_widget.h index 452cafa506..fc13628567 100644 --- a/plugins/extensions/histogram/kis_histogram_widget.h +++ b/plugins/extensions/histogram/kis_histogram_widget.h @@ -1,64 +1,92 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_HISTOGRAM_WIDGET_ #define KIS_HISTOGRAM_WIDGET_ #include "kis_types.h" #include "ui_wdghistogram.h" - +#include "KoHistogramProducer.h" class WdgHistogram : public QWidget, public Ui::WdgHistogram { Q_OBJECT public: WdgHistogram(QWidget *parent) : QWidget(parent) { setupUi(this); } }; /** * The histogram widget takes a paint device or an image and * draws a histogram for the given KisHistogram. */ class KisHistogramWidget : public WdgHistogram { Q_OBJECT public: KisHistogramWidget(QWidget *parent, const char *name); virtual ~KisHistogramWidget(); void setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds); + /** Sets the currently displayed channels to channels of the producer with producerID as ID*/ + void setCurrentChannels(const KoID& producerID, QList channels); + + /** Be careful, producer will be modified */ + void setCurrentChannels(KoHistogramProducer *producer, QList channels); + private Q_SLOTS: void setActiveChannel(int channel); void slotTypeSwitched(void); void slotZoomIn(); void slotZoomOut(); void slide(int val); private: + void setChannels(void); + void addProducerChannels(KoHistogramProducer *producer); + + typedef struct { + bool isProducer; + KoHistogramProducer *producer; + KoChannelInfo * channel; + } ComboboxInfo; + + QVector m_comboInfo; + // Maps the channels in m_channels to a real channel offset in the producer->channels() + QVector m_channelToOffset; + QStringList m_channelStrings; + QList m_channels; + const KoColorSpace* m_cs; + + QStringList channelStrings(); + /** Lists all producers currently available */ + QList producers(); + void setView(double from, double size); void updateEnabled(); double m_from, m_width; + KoHistogramProducer* m_currentProducer; + bool m_color; }; #endif // KIS_HISTOGRAM_WIDGET_ diff --git a/plugins/impex/CMakeLists.txt b/plugins/impex/CMakeLists.txt index 501ea8cf09..9b44d8c1a6 100644 --- a/plugins/impex/CMakeLists.txt +++ b/plugins/impex/CMakeLists.txt @@ -1,46 +1,47 @@ project(kritafilters) 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(OPENJPEG_FOUND) add_subdirectory(jp2) 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) diff --git a/plugins/impex/png/kis_png_export.cc b/plugins/impex/png/kis_png_export.cc index 13151a79f7..87837ccbc4 100644 --- a/plugins/impex/png/kis_png_export.cc +++ b/plugins/impex/png/kis_png_export.cc @@ -1,248 +1,248 @@ /* * Copyright (c) 2005 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_png_converter.h" #include K_PLUGIN_FACTORY_WITH_JSON(KisPNGExportFactory, "krita_png_export.json", registerPlugin();) 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) { 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; KoDialog* kdb = new KoDialog(0); kdb->setCaption(i18n("PNG Export Options")); kdb->setModal(false); kdb->setButtons(KoDialog::Ok | KoDialog::Cancel); 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(); KisPNGOptions options; bool isThereAlpha = false; do { if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) { isThereAlpha = true; break; } } while (it.nextPixel()); - if (qApp->applicationName() != "qttest") { + if (!qApp->applicationName().toLower().contains("test")) { bool sRGB = (cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive) && !cs->profile()->name().contains(QLatin1String("g10"))); KisWdgOptionsPNG* wdg = new KisWdgOptionsPNG(kdb); QString filterConfig = KisConfig().exportConfiguration("PNG"); KisPropertiesConfiguration cfg; cfg.fromXML(filterConfig); wdg->alpha->setChecked(cfg.getBool("alpha", isThereAlpha)); if (cs->colorModelId() == RGBAColorModelID) { wdg->tryToSaveAsIndexed->setVisible(true); if (wdg->alpha->isChecked()) { wdg->tryToSaveAsIndexed->setChecked(false); } else { wdg->tryToSaveAsIndexed->setChecked(cfg.getBool("indexed", false)); } } else { wdg->tryToSaveAsIndexed->setVisible(false); } wdg->interlacing->setChecked(cfg.getBool("interlaced", false)); wdg->compressionLevel->setValue(cfg.getInt("compression", 9)); wdg->compressionLevel->setRange(1, 9 , 0); wdg->alpha->setEnabled(isThereAlpha); wdg->tryToSaveAsIndexed->setVisible(!isThereAlpha); wdg->bnTransparencyFillColor->setEnabled(!wdg->alpha->isChecked()); wdg->chkSRGB->setEnabled(sRGB); wdg->chkSRGB->setChecked(cfg.getBool("saveSRGBProfile", true)); wdg->chkForceSRGB->setEnabled(!sRGB); wdg->chkForceSRGB->setChecked(cfg.getBool("forceSRGB", false)); QStringList rgb = cfg.getString("transparencyFillcolor", "0,0,0").split(','); wdg->bnTransparencyFillColor->setDefaultColor(Qt::white); wdg->bnTransparencyFillColor->setColor(QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt())); kdb->setMainWidget(wdg); QApplication::restoreOverrideCursor(); if (hasVisibleWidgets()) { if (!getBatchMode()) { if (kdb->exec() == QDialog::Rejected) { return KisImportExportFilter::UserCancelled; } } } bool alpha = wdg->alpha->isChecked(); bool interlace = wdg->interlacing->isChecked(); int compression = (int)wdg->compressionLevel->value(); bool tryToSaveAsIndexed = wdg->tryToSaveAsIndexed->isChecked(); QColor c = wdg->bnTransparencyFillColor->color(); bool saveSRGB = wdg->chkSRGB->isChecked(); bool forceSRGB = wdg->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); KisConfig().setExportConfiguration("PNG", cfg); options.alpha = alpha; options.interlace = interlace; options.compression = compression; options.tryToSaveAsIndexed = tryToSaveAsIndexed; options.transparencyFillColor = c; options.saveSRGBProfile = saveSRGB; options.forceSRGB = forceSRGB; } else { - options.alpha = isThereAlpha; + options.alpha = true; options.interlace = false; options.compression = 9; options.tryToSaveAsIndexed = false; options.transparencyFillColor = QColor(0,0,0); options.saveSRGBProfile = false; options.forceSRGB = false; } delete kdb; KisPNGConverter kpc(input); 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) 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 !"; delete eI; return KisImportExportFilter::OK; } delete eI; dbgFile << " Result =" << res; return KisImportExportFilter::InternalError; } #include "kis_png_export.moc" void KisWdgOptionsPNG::on_alpha_toggled(bool checked) { bnTransparencyFillColor->setEnabled(!checked); } diff --git a/plugins/impex/psd/psd_layer_section.cpp b/plugins/impex/psd/psd_layer_section.cpp index 7310ee24af..114b48d870 100644 --- a/plugins/impex/psd/psd_layer_section.cpp +++ b/plugins/impex/psd/psd_layer_section.cpp @@ -1,591 +1,591 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "psd_layer_section.h" #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include "psd_header.h" #include "psd_utils.h" #include "compression.h" #include #include #include #include PSDLayerMaskSection::PSDLayerMaskSection(const PSDHeader& header) : globalInfoSection(header), m_header(header) { hasTransparency = false; layerMaskBlockSize = 0; nLayers = 0; } PSDLayerMaskSection::~PSDLayerMaskSection() { qDeleteAll(layers); } bool PSDLayerMaskSection::read(QIODevice* io) { bool retval = true; // be optimistic! <:-) try { retval = readImpl(io); } catch (KisAslReaderUtils::ASLParseException &e) { warnKrita << "WARNING: PSD (emb. pattern):" << e.what(); retval = false; } return retval; } bool PSDLayerMaskSection::readLayerInfoImpl(QIODevice* io) { quint32 layerInfoSectionSize = 0; SAFE_READ_EX(io, layerInfoSectionSize); if (layerInfoSectionSize & 0x1) { warnKrita << "WARNING: layerInfoSectionSize is NOT even! Fixing..."; layerInfoSectionSize++; } { SETUP_OFFSET_VERIFIER(layerInfoSectionTag, io, layerInfoSectionSize, 0); dbgFile << "Layer info block size" << layerInfoSectionSize; if (layerInfoSectionSize > 0 ) { if (!psdread(io, &nLayers) || nLayers == 0) { error = QString("Could not read read number of layers or no layers in image. %1").arg(nLayers); return false; } hasTransparency = nLayers < 0; // first alpha channel is the alpha channel of the projection. nLayers = qAbs(nLayers); dbgFile << "Number of layers:" << nLayers; dbgFile << "Has separate projection transparency:" << hasTransparency; for (int i = 0; i < nLayers; ++i) { dbgFile << "Going to read layer" << i << "pos" << io->pos(); dbgFile << "== Enter PSDLayerRecord"; PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header); if (!layerRecord->read(io)) { error = QString("Could not load layer %1: %2").arg(i).arg(layerRecord->error); return false; } dbgFile << "== Leave PSDLayerRecord"; dbgFile << "Finished reading layer" << i << layerRecord->layerName << "blending mode" << layerRecord->blendModeKey << io->pos() << "Number of channels:" << layerRecord->channelInfoRecords.size(); layers << layerRecord; } } // get the positions for the channels belonging to each layer for (int i = 0; i < nLayers; ++i) { dbgFile << "Going to seek channel positions for layer" << i << "pos" << io->pos(); if (i > layers.size()) { error = QString("Expected layer %1, but only have %2 layers").arg(i).arg(layers.size()); return false; } PSDLayerRecord *layerRecord = layers.at(i); for (int j = 0; j < layerRecord->nChannels; ++j) { // save the current location so we can jump beyond this block later on. quint64 channelStartPos = io->pos(); dbgFile << "\tReading channel image data for channel" << j << "from pos" << io->pos(); KIS_ASSERT_RECOVER(j < layerRecord->channelInfoRecords.size()) { return false; } ChannelInfo* channelInfo = layerRecord->channelInfoRecords.at(j); quint16 compressionType; if (!psdread(io, &compressionType)) { error = "Could not read compression type for channel"; return false; } channelInfo->compressionType = (Compression::CompressionType)compressionType; dbgFile << "\t\tChannel" << j << "has compression type" << compressionType; QRect channelRect = layerRecord->channelRect(channelInfo); // read the rle row lengths; if (channelInfo->compressionType == Compression::RLE) { for(qint64 row = 0; row < channelRect.height(); ++row) { //dbgFile << "Reading the RLE bytecount position of row" << row << "at pos" << io->pos(); quint32 byteCount; if (m_header.version == 1) { quint16 _byteCount; if (!psdread(io, &_byteCount)) { error = QString("Could not read byteCount for rle-encoded channel"); return 0; } byteCount = _byteCount; } else { if (!psdread(io, &byteCount)) { error = QString("Could not read byteCount for rle-encoded channel"); return 0; } } ////dbgFile << "rle byte count" << byteCount; channelInfo->rleRowLengths << byteCount; } } // we're beyond all the length bytes, rle bytes and whatever, this is the // location of the real pixel data channelInfo->channelDataStart = io->pos(); dbgFile << "\t\tstart" << channelStartPos << "data start" << channelInfo->channelDataStart << "data length" << channelInfo->channelDataLength << "pos" << io->pos(); // make sure we are at the start of the next channel data block io->seek(channelStartPos + channelInfo->channelDataLength); // this is the length of the actual channel data bytes channelInfo->channelDataLength = channelInfo->channelDataLength - (channelInfo->channelDataStart - channelStartPos); dbgFile << "\t\tchannel record" << j << "for layer" << i << "with id" << channelInfo->channelId << "starting postion" << channelInfo->channelDataStart << "with length" << channelInfo->channelDataLength << "and has compression type" << channelInfo->compressionType; } } } return true; } bool PSDLayerMaskSection::readImpl(QIODevice* io) { dbgFile << "reading layer section. Pos:" << io->pos() << "bytes left:" << io->bytesAvailable(); layerMaskBlockSize = 0; if (m_header.version == 1) { quint32 _layerMaskBlockSize = 0; if (!psdread(io, &_layerMaskBlockSize) || _layerMaskBlockSize > (quint64)io->bytesAvailable()) { error = QString("Could not read layer + mask block size. Got %1. Bytes left %2") .arg(_layerMaskBlockSize).arg(io->bytesAvailable()); return false; } layerMaskBlockSize = _layerMaskBlockSize; } else if (m_header.version == 2) { if (!psdread(io, &layerMaskBlockSize) || layerMaskBlockSize > (quint64)io->bytesAvailable()) { error = QString("Could not read layer + mask block size. Got %1. Bytes left %2") .arg(layerMaskBlockSize).arg(io->bytesAvailable()); return false; } } quint64 start = io->pos(); dbgFile << "layer + mask section size" << layerMaskBlockSize; if (layerMaskBlockSize == 0) { dbgFile << "No layer + mask info, so no layers, only a background layer"; return true; } KIS_ASSERT_RECOVER(m_header.version == 1) { return false; } if (!readLayerInfoImpl(io)) { return false; } quint32 globalMaskBlockLength; if (!psdread(io, &globalMaskBlockLength)) { error = "Could not read global mask info block"; return false; } if (globalMaskBlockLength > 0) { if (!psdread(io, &globalLayerMaskInfo.overlayColorSpace)) { error = "Could not read global mask info overlay colorspace"; return false; } for (int i = 0; i < 4; ++i) { if (!psdread(io, &globalLayerMaskInfo.colorComponents[i])) { error = QString("Could not read mask info visualizaion color component %1").arg(i); return false; } } if (!psdread(io, &globalLayerMaskInfo.opacity)) { error = "Could not read global mask info visualization opacity"; return false; } if (!psdread(io, &globalLayerMaskInfo.kind)) { error = "Could not read global mask info visualization type"; return false; } } // global additional sections /** * Newer versions of PSD have layers info block wrapped into * 'Lr16' or 'Lr32' additional section, while the main block is * absent. * * Here we pass the callback which should be used when such * additional section is recognized. */ globalInfoSection.setExtraLayerInfoBlockHandler(std::bind(&PSDLayerMaskSection::readLayerInfoImpl, this, std::placeholders::_1)); globalInfoSection.read(io); /* put us after this section so reading the next section will work even if we mess up */ io->seek(start + layerMaskBlockSize); return true; } struct FlattenedNode { FlattenedNode() : type(RASTER_LAYER) {} KisNodeSP node; enum Type { RASTER_LAYER, FOLDER_OPEN, FOLDER_CLOSED, SECTION_DIVIDER }; Type type; }; void addBackgroundIfNeeded(KisNodeSP root, QList &nodes) { KisGroupLayer *group = dynamic_cast(root.data()); if (!group) return; KoColor projectionColor = group->defaultProjectionColor(); if (projectionColor.opacityU8() == OPACITY_TRANSPARENT_U8) return; KisPaintLayerSP layer = new KisPaintLayer(group->image(), i18nc("Automatically created layer name when saving into PSD", "Background"), OPACITY_OPAQUE_U8); projectionColor.convertTo(layer->paintDevice()->colorSpace()); layer->paintDevice()->setDefaultPixel(projectionColor.data()); { FlattenedNode item; item.node = layer; item.type = FlattenedNode::RASTER_LAYER; nodes << item; } } void flattenNodes(KisNodeSP node, QList &nodes) { KisNodeSP child = node->firstChild(); while (child) { bool isGroupLayer = child->inherits("KisGroupLayer"); bool isRasterLayer = child->inherits("KisPaintLayer") || child->inherits("KisShapeLayer"); if (isGroupLayer) { { FlattenedNode item; item.node = child; item.type = FlattenedNode::SECTION_DIVIDER; nodes << item; } flattenNodes(child, nodes); { FlattenedNode item; item.node = child; item.type = FlattenedNode::FOLDER_OPEN; nodes << item; } } else if (isRasterLayer) { FlattenedNode item; item.node = child; item.type = FlattenedNode::RASTER_LAYER; nodes << item; } child = child->nextSibling(); } } KisNodeSP findOnlyTransparencyMask(KisNodeSP node, FlattenedNode::Type type) { if (type != FlattenedNode::FOLDER_OPEN && type != FlattenedNode::FOLDER_CLOSED && type != FlattenedNode::RASTER_LAYER) { return 0; } KisLayer *layer = dynamic_cast(node.data()); QList masks = layer->effectMasks(); if (masks.size() != 1) return 0; KisEffectMaskSP onlyMask = masks.first(); return onlyMask->inherits("KisTransparencyMask") ? onlyMask : 0; } QDomDocument fetchLayerStyleXmlData(KisNodeSP node) { const KisLayer *layer = qobject_cast(node.data()); KisPSDLayerStyleSP layerStyle = layer->layerStyle(); if (!layerStyle) return QDomDocument(); KisAslLayerStyleSerializer serializer; serializer.setStyles(QVector() << layerStyle); return serializer.formPsdXmlDocument(); } inline QDomNode findNodeByKey(const QString &key, QDomNode parent) { return KisDomUtils::findElementByAttibute(parent, "node", "key", key); } void mergePatternsXMLSection(const QDomDocument &src, QDomDocument &dst) { QDomNode srcPatternsNode = findNodeByKey("Patterns", src.documentElement()); QDomNode dstPatternsNode = findNodeByKey("Patterns", dst.documentElement()); if (srcPatternsNode.isNull()) return; if (dstPatternsNode.isNull()) { dst = src; return; } KIS_ASSERT_RECOVER_RETURN(!srcPatternsNode.isNull()); KIS_ASSERT_RECOVER_RETURN(!dstPatternsNode.isNull()); QDomNode node = srcPatternsNode.firstChild(); while(!node.isNull()) { QDomNode importedNode = dst.importNode(node, true); KIS_ASSERT_RECOVER_RETURN(!importedNode.isNull()); dstPatternsNode.appendChild(importedNode); node = node.nextSibling(); } } bool PSDLayerMaskSection::write(QIODevice* io, KisNodeSP rootLayer) { bool retval = true; try { writeImpl(io, rootLayer); } catch (KisAslWriterUtils::ASLWriteException &e) { error = PREPEND_METHOD(e.what()); retval = false; } return retval; } void PSDLayerMaskSection::writeImpl(QIODevice* io, KisNodeSP rootLayer) { dbgFile << "Writing layer layer section"; // Build the whole layer structure QList nodes; addBackgroundIfNeeded(rootLayer, nodes); flattenNodes(rootLayer, nodes); if (nodes.isEmpty()) { throw KisAslWriterUtils::ASLWriteException("Could not find paint layers to save"); } { KisAslWriterUtils::OffsetStreamPusher layerAndMaskSectionSizeTag(io, 2); QDomDocument mergedPatternsXmlDoc; { KisAslWriterUtils::OffsetStreamPusher layerInfoSizeTag(io, 4); { // number of layers (negative, because krita always has alpha) const qint16 layersSize = -nodes.size(); SAFE_WRITE_EX(io, layersSize); dbgFile << "Number of layers" << layersSize << "at" << io->pos(); } // Layer records section Q_FOREACH (const FlattenedNode &item, nodes) { KisNodeSP node = item.node; PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header); layers.append(layerRecord); KisNodeSP onlyTransparencyMask = findOnlyTransparencyMask(node, item.type); const QRect maskRect = onlyTransparencyMask ? onlyTransparencyMask->paintDevice()->exactBounds() : QRect(); const bool nodeVisible = node->visible(); const KoColorSpace *colorSpace = node->colorSpace(); const quint8 nodeOpacity = node->opacity(); const quint8 nodeClipping = 0; const KisPaintLayer *paintLayer = qobject_cast(node.data()); const bool alphaLocked = (paintLayer && paintLayer->alphaLocked()); const QString nodeCompositeOp = node->compositeOpId(); const KisGroupLayer *groupLayer = qobject_cast(node.data()); const bool nodeIsPassThrough = groupLayer && groupLayer->passThroughMode(); QDomDocument stylesXmlDoc = fetchLayerStyleXmlData(node); if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) { mergedPatternsXmlDoc = stylesXmlDoc; } else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) { mergePatternsXMLSection(stylesXmlDoc, mergedPatternsXmlDoc); } bool nodeIrrelevant = false; QString nodeName; KisPaintDeviceSP layerContentDevice; psd_section_type sectionType; if (item.type == FlattenedNode::RASTER_LAYER) { nodeIrrelevant = false; nodeName = node->name(); layerContentDevice = onlyTransparencyMask ? node->original() : node->projection(); sectionType = psd_other; } else { nodeIrrelevant = true; nodeName = item.type == FlattenedNode::SECTION_DIVIDER ? QString("") : node->name(); layerContentDevice = 0; sectionType = item.type == FlattenedNode::SECTION_DIVIDER ? psd_bounding_divider : item.type == FlattenedNode::FOLDER_OPEN ? psd_open_folder : psd_closed_folder; } // === no access to node anymore QRect layerRect; if (layerContentDevice) { - QRect rc = layerContentDevice->extent(); + QRect rc = layerContentDevice->exactBounds(); rc = rc.normalized(); // keep to the max of photoshop's capabilities if (rc.width() > 30000) rc.setWidth(30000); if (rc.height() > 30000) rc.setHeight(30000); layerRect = rc; } layerRecord->top = layerRect.y(); layerRecord->left = layerRect.x(); layerRecord->bottom = layerRect.y() + layerRect.height(); layerRecord->right = layerRect.x() + layerRect.width(); // colors + alpha channel // note: transparency mask not included layerRecord->nChannels = colorSpace->colorChannelCount() + 1; ChannelInfo *info = new ChannelInfo; info->channelId = -1; // For the alpha channel, which we always have in Krita, and should be saved first in layerRecord->channelInfoRecords << info; // the rest is in display order: rgb, cmyk, lab... for (int i = 0; i < (int)colorSpace->colorChannelCount(); ++i) { info = new ChannelInfo; info->channelId = i; // 0 for red, 1 = green, etc layerRecord->channelInfoRecords << info; } layerRecord->blendModeKey = composite_op_to_psd_blendmode(nodeCompositeOp); layerRecord->isPassThrough = nodeIsPassThrough; layerRecord->opacity = nodeOpacity; layerRecord->clipping = nodeClipping; layerRecord->transparencyProtected = alphaLocked; layerRecord->visible = nodeVisible; layerRecord->irrelevant = nodeIrrelevant; layerRecord->layerName = nodeName; layerRecord->write(io, layerContentDevice, onlyTransparencyMask, maskRect, sectionType, stylesXmlDoc); } dbgFile << "start writing layer pixel data" << io->pos(); // Now save the pixel data Q_FOREACH (PSDLayerRecord *layerRecord, layers) { layerRecord->writePixelData(io); } } { // write the global layer mask info -- which is empty const quint32 globalMaskSize = 0; SAFE_WRITE_EX(io, globalMaskSize); } { PsdAdditionalLayerInfoBlock globalInfoSection(m_header); globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc); } } } diff --git a/plugins/impex/video/CMakeLists.txt b/plugins/impex/video/CMakeLists.txt new file mode 100644 index 0000000000..768cfed851 --- /dev/null +++ b/plugins/impex/video/CMakeLists.txt @@ -0,0 +1,19 @@ +add_subdirectory(tests) + +# export + +set(kritavideoexport_SOURCES + kis_video_export.cpp + video_saver.cpp + video_export_options_dialog.cpp + ) + +ki18n_wrap_ui(kritavideoexport_SOURCES video_export_options_dialog.ui ) + +add_library(kritavideoexport MODULE ${kritavideoexport_SOURCES}) + +generate_export_header(kritavideoexport BASE_NAME kritavideoexport) + +target_link_libraries(kritavideoexport kritaui) + +install(TARGETS kritavideoexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/impex/video/kis_video_export.cpp b/plugins/impex/video/kis_video_export.cpp new file mode 100644 index 0000000000..26b63dcbdf --- /dev/null +++ b/plugins/impex/video/kis_video_export.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "KisPart.h" +#include +#include +#include +#include +#include + +#include "video_saver.h" +#include "video_export_options_dialog.h" + +#include "kis_cursor_override_hijacker.h" + + +K_PLUGIN_FACTORY_WITH_JSON(KisVideoExportFactory, "krita_video_export.json", registerPlugin();) + +KisVideoExport::KisVideoExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) +{ +} + +KisVideoExport::~KisVideoExport() +{ +} + +KisImportExportFilter::ConversionStatus KisVideoExport::convert(const QByteArray& from, const QByteArray& to) +{ + 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; + + bool askForOptions = false; + + const QFileInfo fileInfo(filename); + const QString suffix = fileInfo.suffix().toLower(); + + VideoExportOptionsDialog::CodecIndex codecIndex = + VideoExportOptionsDialog::CODEC_H264; + + if (suffix == "mkv" || suffix == "mp4") { + codecIndex = VideoExportOptionsDialog::CODEC_H264; + askForOptions = true; + } else if (suffix == "ogv") { + codecIndex = VideoExportOptionsDialog::CODEC_THEORA; + askForOptions = true; + } + + QStringList additionalOptionsList; + + askForOptions &= + !qApp->applicationName().toLower().contains("test") & + !getBatchMode(); + + if (askForOptions) { + KisCursorOverrideHijacker badGuy; + + VideoExportOptionsDialog dlg; + dlg.setCodec(codecIndex); + + if (dlg.exec() == QDialog::Accepted) { + additionalOptionsList = dlg.customUserOptions(); + } else { + return KisImportExportFilter::UserCancelled; + } + } + + VideoSaver kpc(input, getBatchMode()); + + if (!kpc.hasFFMpeg()) { + const QString warningMessage = + i18n("Couldn not find \'ffmpeg\' binary. Saving to video formats is impossible."); + + if (askForOptions) { + QMessageBox::critical(KisPart::instance()->currentMainwindow(), + i18n("Video Export Error"), + warningMessage); + } else { + qWarning() << "WARNING:" << warningMessage; + } + + return KisImportExportFilter::UsageError; + } + + KisImageBuilder_Result res = kpc.encode(filename, additionalOptionsList); + + if (res == KisImageBuilder_RESULT_OK) { + return KisImportExportFilter::OK; + } else if (res == KisImageBuilder_RESULT_CANCEL) { + return KisImportExportFilter::ProgressCancelled; + } + + return KisImportExportFilter::InternalError; +} + +#include "kis_video_export.moc" diff --git a/plugins/impex/video/kis_video_export.h b/plugins/impex/video/kis_video_export.h new file mode 100644 index 0000000000..db6cf2ee7e --- /dev/null +++ b/plugins/impex/video/kis_video_export.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +#include + +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); +}; + +#endif diff --git a/plugins/impex/video/krita_video_export.json b/plugins/impex/video/krita_video_export.json new file mode 100644 index 0000000000..dc949e0615 --- /dev/null +++ b/plugins/impex/video/krita_video_export.json @@ -0,0 +1,13 @@ +{ + "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/FileFilter" + ], + "X-KDE-Weight": "1", + "X-KDE-Extensions" : "mkv,gif,ogg,mp4" +} diff --git a/plugins/impex/video/tests/CMakeLists.txt b/plugins/impex/video/tests/CMakeLists.txt new file mode 100644 index 0000000000..cb551bf389 --- /dev/null +++ b/plugins/impex/video/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) +include_directories( ${CMAKE_SOURCE_DIR}/sdk/tests ) + +macro_add_unittest_definitions() + +########### next target ############### +set(kis_video_plugin_test_SRCS kis_video_plugin_test.cpp ) + +kde4_add_unit_test(kis_video_plugin_test TESTNAME krita-plugins-formats-video_test ${kis_video_plugin_test_SRCS}) + +target_link_libraries(kis_video_plugin_test kritavideoexport kritaui Qt5::Test) diff --git a/plugins/impex/video/tests/data/test_animation_small.kra b/plugins/impex/video/tests/data/test_animation_small.kra new file mode 100644 index 0000000000..1cd9b66f68 Binary files /dev/null and b/plugins/impex/video/tests/data/test_animation_small.kra differ diff --git a/plugins/impex/video/tests/kis_video_plugin_test.cpp b/plugins/impex/video/tests/kis_video_plugin_test.cpp new file mode 100644 index 0000000000..925355678d --- /dev/null +++ b/plugins/impex/video/tests/kis_video_plugin_test.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_plugin_test.h" + + +#include +#include + +#include + +#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 "../video_saver.h" + + +void KisVideoPluginTest::testFiles() +{ + QString fname = QString(FILES_DATA_DIR) + QDir::separator() + "test_animation_small.kra"; + + KisDocument *doc = KisPart::instance()->createDocument(); + doc->loadNativeFormat(fname); + + + VideoSaver saver(doc, false); + + KisImageBuilder_Result result = + //saver.encode("testfile.gif"); + saver.encode("testfile.ogg"); + + QCOMPARE(result, KisImageBuilder_RESULT_OK); +} +QTEST_MAIN(KisVideoPluginTest) + diff --git a/plugins/impex/video/tests/kis_video_plugin_test.h b/plugins/impex/video/tests/kis_video_plugin_test.h new file mode 100644 index 0000000000..8f08f43cbd --- /dev/null +++ b/plugins/impex/video/tests/kis_video_plugin_test.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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_PLUGIN_TEST_H_ +#define _KIS_VIDEO_PLUGIN_TEST_H_ + +#include + +class KisVideoPluginTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testFiles(); +}; + +#endif diff --git a/plugins/impex/video/video_export_options_dialog.cpp b/plugins/impex/video/video_export_options_dialog.cpp new file mode 100644 index 0000000000..4f466ac189 --- /dev/null +++ b/plugins/impex/video/video_export_options_dialog.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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_export_options_dialog.h" +#include "ui_video_export_options_dialog.h" + +#include + +#include +#include + + +struct VideoExportOptionsDialog::Private +{ + Private() { + presets << KoID("ultrafast", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "ultrafast")); + presets << KoID("superfast", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "superfast")); + presets << KoID("veryfast", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "veryfast")); + presets << KoID("faster", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "faster")); + presets << KoID("fast", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "fast")); + presets << KoID("medium", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "medium")); + presets << KoID("slow", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "slow")); + presets << KoID("slower", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "slower")); + presets << KoID("veryslow", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "veryslow")); + presets << KoID("placebo", i18nc("h264 preset name, check simplescreenrecorder for standard translations", "placebo")); + + profiles << KoID("baseline", i18nc("h264 profile name, check simplescreenrecorder for standard translations", "baseline")); + profiles << KoID("main", i18nc("h264 profile name, check simplescreenrecorder for standard translations", "main")); + profiles << KoID("high", i18nc("h264 profile name, check simplescreenrecorder for standard translations", "high")); + profiles << KoID("high10", i18nc("h264 profile name, check simplescreenrecorder for standard translations", "high10")); + profiles << KoID("high422", i18nc("h264 profile name, check simplescreenrecorder for standard translations", "high422")); + profiles << KoID("high444", i18nc("h264 profile name, check simplescreenrecorder for standard translations", "high444")); + + tunes << KoID("film", i18nc("h264 tune option name, check simplescreenrecorder for standard translations", "film")); + tunes << KoID("animation", i18nc("h264 tune option name, check simplescreenrecorder for standard translations", "animation")); + tunes << KoID("grain", i18nc("h264 tune option name, check simplescreenrecorder for standard translations", "grain")); + tunes << KoID("stillimage", i18nc("h264 tune option name, check simplescreenrecorder for standard translations", "stillimage")); + tunes << KoID("psnr", i18nc("h264 tune option name, check simplescreenrecorder for standard translations", "psnr")); + tunes << KoID("ssim", i18nc("h264 tune option name, check simplescreenrecorder for standard translations", "ssim")); + tunes << KoID("fastdecode", i18nc("h264 tune option name, check simplescreenrecorder for standard translations", "fastdecode")); + tunes << KoID("zerolatency", i18nc("h264 tune option name, check simplescreenrecorder for standard translations", "zerolatency")); + + + KConfigGroup cfg = KSharedConfig::openConfig()->group("VideoExportPlugin"); + + defaultPreset = cfg.readEntry("h264PresetIndex", 5); + defaultConstantRateFactor = cfg.readEntry("h264ConstantRateFactor", 23); + defaultProfile = cfg.readEntry("h264ProfileIndex", 4); + defaultTune = cfg.readEntry("h264TuneIndex", 1); + defaultBitrate = cfg.readEntry("TheoraBitrate", 5000); + defaultCustomLine = cfg.readEntry("CustomLineValue", ""); + } + + void updateDefaultCustomLine() { + KConfigGroup cfg = KSharedConfig::openConfig()->group("VideoExportPlugin"); + defaultCustomLine = cfg.readEntry("CustomLineValue", ""); + } + + QVector presets; + int defaultPreset; + int defaultBitrate; + int defaultConstantRateFactor; + + QVector profiles; + int defaultProfile; + + QVector tunes; + int defaultTune; + + QString defaultCustomLine; +}; + + +VideoExportOptionsDialog::VideoExportOptionsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::VideoExportOptionsDialog), + m_d(new Private) +{ + ui->setupUi(this); + + ui->intConstantRateFactor->setRange(0, 51); + ui->intConstantRateFactor->setValue(m_d->defaultConstantRateFactor); + + Q_FOREACH (const KoID &preset, m_d->presets) { + ui->cmbPreset->insertItem(ui->cmbPreset->count(), preset.name()); + } + ui->cmbPreset->setCurrentIndex(m_d->defaultPreset); + + Q_FOREACH (const KoID &profile, m_d->profiles) { + ui->cmbProfile->insertItem(ui->cmbProfile->count(), profile.name()); + } + ui->cmbProfile->setCurrentIndex(m_d->defaultProfile); + + Q_FOREACH (const KoID &tune, m_d->tunes) { + ui->cmbTune->insertItem(ui->cmbTune->count(), tune.name()); + } + ui->cmbTune->setCurrentIndex(m_d->defaultTune); + + ui->intBitrate->setRange(10, 50000); + ui->intBitrate->setValue(5000); + ui->intBitrate->setSuffix(i18nc("kilo-bits-per-second, video bitrate suffix", "kbps")); + + connect(ui->cmbCodec, SIGNAL(currentIndexChanged(int)), + ui->stackedWidget, SLOT(setCurrentIndex(int))); + + ui->cmbCodec->setCurrentIndex(0); + ui->cmbCodec->setEnabled(false); + + // TODO: temporarily hidden! Some combinations of 'tune' and + // 'profile' options make ffmpeg generate empty file. + // We should not let the user shoot into his own foot! + ui->cmbTune->setVisible(false); + ui->lblTune->setVisible(false); + + setModal(true); + connect(this, SIGNAL(accepted()), SLOT(slotAccepted())); + + ui->chkCustomLine->setChecked(!m_d->defaultCustomLine.isEmpty()); + slotCustomLineToggled(!m_d->defaultCustomLine.isEmpty()); + connect(ui->chkCustomLine, SIGNAL(toggled(bool)), SLOT(slotCustomLineToggled(bool))); + + connect(ui->txtCustomLine, SIGNAL(editingFinished()), SLOT(slotSaveCustomLine())); + connect(ui->btnResetCustomLine, SIGNAL(clicked()), SLOT(slotResetCustomLine())); +} + +VideoExportOptionsDialog::~VideoExportOptionsDialog() +{ + delete ui; +} + +void VideoExportOptionsDialog::slotAccepted() +{ + KConfigGroup cfg = KSharedConfig::openConfig()->group("VideoExportPlugin"); + + cfg.writeEntry("h264PresetIndex", ui->cmbPreset->currentIndex()); + cfg.writeEntry("h264ConstantRateFactor", ui->intConstantRateFactor->value()); + cfg.writeEntry("h264ProfileIndex", ui->cmbProfile->currentIndex()); + cfg.writeEntry("h264TuneIndex", ui->cmbTune->currentIndex()); + cfg.writeEntry("TheoraBitrate", ui->intBitrate->value()); + slotSaveCustomLine(); +} + +void VideoExportOptionsDialog::slotCustomLineToggled(bool value) +{ + m_d->updateDefaultCustomLine(); + QString customLine = m_d->defaultCustomLine; + + if (customLine.isEmpty() && value) { + customLine = generateCustomLine().join(" "); + } else if (!value) { + customLine = ""; + } + + ui->txtCustomLine->setText(customLine); + + ui->stackedWidget->setEnabled(!value); + ui->txtCustomLine->setEnabled(value); + ui->btnResetCustomLine->setEnabled(value); +} + +void VideoExportOptionsDialog::slotSaveCustomLine() +{ + KConfigGroup cfg = KSharedConfig::openConfig()->group("VideoExportPlugin"); + cfg.writeEntry("CustomLineValue", ui->txtCustomLine->text()); +} + +void VideoExportOptionsDialog::slotResetCustomLine() +{ + ui->txtCustomLine->setText(generateCustomLine().join(" ")); + slotSaveCustomLine(); +} + +void VideoExportOptionsDialog::setCodec(CodecIndex index) +{ + ui->cmbCodec->setCurrentIndex(int(index)); +} + +QStringList VideoExportOptionsDialog::customUserOptions() const +{ + return ui->chkCustomLine->isChecked() ? + ui->txtCustomLine->text().split(" ", QString::SkipEmptyParts) : + generateCustomLine(); +} + +QStringList VideoExportOptionsDialog::generateCustomLine() const +{ + QStringList options; + + if (ui->cmbCodec->currentIndex() == int(CODEC_H264)) { + options << "-crf" << QString::number(ui->intConstantRateFactor->value()); + + const int presetIndex = ui->cmbPreset->currentIndex(); + options << "-preset" << m_d->presets[presetIndex].id(); + + const int profileIndex = ui->cmbProfile->currentIndex(); + options << "-profile" << m_d->profiles[profileIndex].id(); + + if (m_d->profiles[profileIndex].id() == "high422") { + options << "-pix_fmt" << "yuv422p"; + } else if (m_d->profiles[profileIndex].id() == "high444") { + options << "-pix_fmt" << "yuv444p"; + } else { + options << "-pix_fmt" << "yuv420p"; + } + + // Disabled! see the comment in c-tor! + //const int tuneIndex = ui->cmbTune->currentIndex(); + //options << "-tune" << m_d->tunes[tuneIndex].id(); + + } else if (ui->cmbCodec->currentIndex() == int(CODEC_THEORA)) { + options << "-b" << QString::number(ui->intBitrate->value()) + "k"; + } + + return options; +} diff --git a/plugins/impex/video/video_export_options_dialog.h b/plugins/impex/video/video_export_options_dialog.h new file mode 100644 index 0000000000..0c59b0f649 --- /dev/null +++ b/plugins/impex/video/video_export_options_dialog.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 VIDEO_EXPORT_OPTIONS_DIALOG_H +#define VIDEO_EXPORT_OPTIONS_DIALOG_H + +#include +#include "video_saver.h" + +#include + +namespace Ui { +class VideoExportOptionsDialog; +} + +class VideoExportOptionsDialog : public QDialog +{ + Q_OBJECT + +public: + enum CodecIndex { + CODEC_H264 = 0, + CODEC_THEORA + }; + +public: + explicit VideoExportOptionsDialog(QWidget *parent = 0); + ~VideoExportOptionsDialog(); + + void setCodec(CodecIndex index); + + QStringList customUserOptions() const; + +private Q_SLOTS: + void slotAccepted(); + void slotCustomLineToggled(bool value); + void slotSaveCustomLine(); + void slotResetCustomLine(); + +private: + Ui::VideoExportOptionsDialog *ui; + +private: + QStringList generateCustomLine() const; + +private: + struct Private; + const QScopedPointer m_d; +}; + +#endif // VIDEO_EXPORT_OPTIONS_DIALOG_H diff --git a/plugins/impex/video/video_export_options_dialog.ui b/plugins/impex/video/video_export_options_dialog.ui new file mode 100644 index 0000000000..21a85e0c63 --- /dev/null +++ b/plugins/impex/video/video_export_options_dialog.ui @@ -0,0 +1,209 @@ + + + VideoExportOptionsDialog + + + + 0 + 0 + 505 + 333 + + + + Dialog + + + + + + + H.264, MPEG-4 Part 10 + + + + + Theora + + + + + + + + 0 + + + + + + + Constant Rate Factor: + + + + + + + + 0 + 0 + + + + + + + + Preset: + + + + + + + + + + Profile: + + + + + + + + + + Tune: + + + + + + + + + + + + + + Bitrate: + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Custom Options: + + + + + + + + + Add any ffmpeg filtering options here. It will be put between source and destination streams of the ffmpeg + + + + + + + Reset + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 44 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + KisSliderSpinBox + QWidget +
kis_slider_spin_box.h
+
+
+ + + + buttonBox + accepted() + VideoExportOptionsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + VideoExportOptionsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/plugins/impex/video/video_saver.cpp b/plugins/impex/video/video_saver.cpp new file mode 100644 index 0000000000..c491779210 --- /dev/null +++ b/plugins/impex/video/video_saver.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 + +#include + +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include "kis_config.h" +#include "kis_animation_exporter.h" + +#include +#include +#include +#include +#include +#include + +#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() { + //qDebug() << "=== progress changed ==="; + + 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"; + } + //qDebug() << var; + } + + 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) { + + 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, const QStringList &additionalOptionsList) +{ + if (m_ffmpegPath.isEmpty()) return KisImageBuilder_RESULT_FAILURE; + + KisImageBuilder_Result retval= KisImageBuilder_RESULT_OK; + + KisImageAnimationInterface *animation = m_image->animationInterface(); + const KisTimeRange clipRange = animation->fullClipRange(); + const int frameRate = animation->framerate(); + + const QString resultFile = filename; + const bool removeGeneratedFiles = + !qEnvironmentVariableIsSet("KRITA_KEEP_FRAMES"); + + const QFileInfo info(resultFile); + const QString suffix = info.suffix().toLower(); + const QString baseDirectory = info.absolutePath(); + const QString frameDirectoryTemplate = baseDirectory + QDir::separator() + "frames.XXXXXX"; + + QTemporaryDir framesDirImpl(frameDirectoryTemplate); + framesDirImpl.setAutoRemove(removeGeneratedFiles); + + const QDir framesDir(framesDirImpl.path()); + + const QString framesPath = framesDir.path(); + const QString framesBasePath = framesDir.filePath("frame.png"); + const QString palettePath = framesDir.filePath("palette.png"); + + KisAnimationExportSaver saver(m_doc, framesBasePath, clipRange.start(), clipRange.end()); + + KisImportExportFilter::ConversionStatus status = + saver.exportAnimation(); + + if (status == KisImportExportFilter::UserCancelled) { + return KisImageBuilder_RESULT_CANCEL; + } else if (status != KisImportExportFilter::OK) { + return KisImageBuilder_RESULT_FAILURE; + } + + if (suffix == "gif") { + { + QStringList args; + args << "-r" << QString::number(frameRate) + << "-i" << saver.savedFilesMask() + << "-vf" << "palettegen" + << "-y" << palettePath; + + KisImageBuilder_Result result = + m_runner->runFFMpeg(args, i18n("Fetching palette..."), + framesDir.filePath("log_palettegen.log"), + clipRange.duration()); + + if (result) { + return result; + } + } + + { + QStringList args; + args << "-r" << QString::number(frameRate) + << "-i" << saver.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()); + + if (result) { + return result; + } + } + } else { + QStringList args; + args << "-r" << QString::number(frameRate) + << "-i" << saver.savedFilesMask() + << additionalOptionsList + << "-y" << resultFile; + + KisImageBuilder_Result result = + m_runner->runFFMpeg(args, i18n("Encoding frames..."), + framesDir.filePath("log_encode.log"), + clipRange.duration()); + + if (result) { + return result; + } + } + + return retval; +} + +void VideoSaver::cancel() +{ + m_runner->cancel(); +} + +#include "video_saver.moc" diff --git a/plugins/impex/video/video_saver.h b/plugins/impex/video/video_saver.h new file mode 100644 index 0000000000..70ae221335 --- /dev/null +++ b/plugins/impex/video/video_saver.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 VIDEO_SAVER_H_ +#define VIDEO_SAVER_H_ + +#include + +#include "kis_types.h" + +#include "KisImageBuilderResult.h" +#include "kritavideoexport_export.h" + +class KisFFMpegRunner; + +/* The KisImageBuilder_Result definitions come from kis_png_converter.h here */ + +class KisDocument; + +class KRITAVIDEOEXPORT_EXPORT VideoSaver : public QObject { + + Q_OBJECT +public: + VideoSaver(KisDocument* doc, bool batchMode); + virtual ~VideoSaver(); + + KisImageSP image(); + KisImageBuilder_Result encode(const QString &filename, const QStringList &additionalOptionsList = QStringList()); + + bool hasFFMpeg() const; + +private Q_SLOTS: + void cancel(); + +private: + static QString findFFMpeg(); + +private: + KisImageSP m_image; + KisDocument* m_doc; + bool m_batchMode; + QString m_ffmpegPath; + QScopedPointer m_runner; +}; + +#endif diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop.cpp b/plugins/paintops/colorsmudge/kis_colorsmudgeop.cpp index f9644b2e57..ae45a1bdda 100644 --- a/plugins/paintops/colorsmudge/kis_colorsmudgeop.cpp +++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop.cpp @@ -1,291 +1,289 @@ /* * Copyright (C) 2011 Silvio Heinrich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_colorsmudgeop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisColorSmudgeOp::KisColorSmudgeOp(const KisBrushBasedPaintOpSettings* settings, KisPainter* painter, KisNodeSP node, KisImageSP image) : KisBrushBasedPaintOp(settings, painter) , m_firstRun(true) , m_image(image) , m_tempDev(painter->device()->createCompositionSourceDevice()) , m_backgroundPainter(new KisPainter(m_tempDev)) , m_smudgePainter(new KisPainter(m_tempDev)) , m_colorRatePainter(new KisPainter(m_tempDev)) , m_smudgeRateOption() , m_colorRateOption("ColorRate", KisPaintOpOption::GENERAL, false) , m_smudgeRadiusOption() { Q_UNUSED(node); Q_ASSERT(settings); Q_ASSERT(painter); m_sizeOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_spacingOption.readOptionSetting(settings); m_smudgeRateOption.readOptionSetting(settings); m_colorRateOption.readOptionSetting(settings); m_smudgeRadiusOption.readOptionSetting(settings); m_overlayModeOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_scatterOption.readOptionSetting(settings); m_gradientOption.readOptionSetting(settings); m_sizeOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_spacingOption.resetAllSensors(); m_smudgeRateOption.resetAllSensors(); m_colorRateOption.resetAllSensors(); m_smudgeRadiusOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_scatterOption.resetAllSensors(); m_gradientOption.resetAllSensors(); m_gradient = painter->gradient(); m_backgroundPainter->setCompositeOp(COMPOSITE_COPY); // Smudge Painter works in default COMPOSITE_OVER mode m_colorRatePainter->setCompositeOp(painter->compositeOp()->id()); m_rotationOption.applyFanCornersInfo(this); } KisColorSmudgeOp::~KisColorSmudgeOp() { delete m_backgroundPainter; delete m_colorRatePainter; delete m_smudgePainter; } void KisColorSmudgeOp::updateMask(const KisPaintInformation& info, double scale, double rotation, const QPointF &cursorPoint) { static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); static KoColor color(Qt::black, cs); m_maskDab = m_dabCache->fetchDab(cs, color, cursorPoint, scale, scale, rotation, info, 1.0, &m_dstDabRect); // sanity check KIS_ASSERT_RECOVER_NOOP(m_dstDabRect.size() == m_maskDab->bounds().size()); } inline void KisColorSmudgeOp::getTopLeftAligned(const QPointF &pos, const QPointF &hotSpot, qint32 *x, qint32 *y) { QPointF topLeft = pos - hotSpot; qreal xFraction, yFraction; // will not be used splitCoordinate(topLeft.x(), x, &xFraction); splitCoordinate(topLeft.y(), y, &yFraction); } KisSpacingInformation KisColorSmudgeOp::paintAt(const KisPaintInformation& info) { KisBrushSP brush = m_brush; // Simple error catching if (!painter()->device() || !brush || !brush->canPaintFor(info)) { return KisSpacingInformation(1.0); } if (m_smudgeRateOption.getMode() == KisSmudgeOption::SMEARING_MODE) { /** * Disable handling of the subpixel precision. In the smudge op we * should read from the aligned areas of the image, so having * additional internal offsets, created by the subpixel precision, * will worsen the quality (at least because * QRectF(m_dstDabRect).center() will not point to the real center * of the brush anymore). * Of course, this only really matters with smearing_mode (bug:327235), * and you only notice the lack of subpixel precision in the dulling methods. */ m_dabCache->disableSubpixelPrecision(); } #if 0 //if precision KoColor colorSpaceChanger = painter()->paintColor(); const KoColorSpace* preciseColorSpace = colorSpaceChanger.colorSpace(); if (colorSpaceChanger.colorSpace()->colorDepthId().id() == "U8") { preciseColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceChanger.colorSpace()->colorModelId().id(), "U16", colorSpaceChanger.profile() ); colorSpaceChanger.convertTo(preciseColorSpace); } painter()->setPaintColor(colorSpaceChanger); #endif // get the scaling factor calculated by the size option qreal scale = m_sizeOption.apply(info); scale *= KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(info); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); - setCurrentScale(scale); - setCurrentRotation(rotation); QPointF scatteredPos = m_scatterOption.apply(info, brush->maskWidth(scale, rotation, 0, 0, info), brush->maskHeight(scale, rotation, 0, 0, info)); QPointF hotSpot = brush->hotSpot(scale, scale, rotation, info); /** * Update the brush mask. * * Upon leaving the function: * o m_maskDab stores the new mask * o m_maskBounds stores the extents of the mask paint device * o m_dstDabRect stores the destination rect where the mask is going * to be written to */ updateMask(info, scale, rotation, scatteredPos); QPointF newCenterPos = QRectF(m_dstDabRect).center(); /** * Save the center of the current dab to know where to read the * data during the next pass. We do not save scatteredPos here, * because it may differ slightly from the real center of the * brush (due to rounding effects), which will result in a * really weird quality. */ QRect srcDabRect = m_dstDabRect.translated((m_lastPaintPos - newCenterPos).toPoint()); m_lastPaintPos = newCenterPos; KisSpacingInformation spacingInfo = effectiveSpacing(scale, rotation, m_spacingOption, info); if (m_firstRun) { m_firstRun = false; return spacingInfo; } // save the old opacity value and composite mode quint8 oldOpacity = painter()->opacity(); QString oldCompositeOpId = painter()->compositeOp()->id(); qreal fpOpacity = (qreal(oldOpacity) / 255.0) * m_opacityOption.getOpacityf(info); if (m_image && m_overlayModeOption.isChecked()) { m_image->blockUpdates(); m_backgroundPainter->bitBlt(QPoint(), m_image->projection(), srcDabRect); m_image->unblockUpdates(); } else { // IMPORTANT: clear the temporary painting device to color black with zero opacity: // it will only clear the extents of the brush. m_tempDev->clear(QRect(QPoint(), m_dstDabRect.size())); } if (m_smudgeRateOption.getMode() == KisSmudgeOption::SMEARING_MODE) { m_smudgePainter->bitBlt(QPoint(), painter()->device(), srcDabRect); } else { QPoint pt = (srcDabRect.topLeft() + hotSpot).toPoint(); if (m_smudgeRadiusOption.isChecked()) { qreal effectiveSize = 0.5 * (m_dstDabRect.width() + m_dstDabRect.height()); m_smudgeRadiusOption.apply(*m_smudgePainter, info, effectiveSize, pt.x(), pt.y(), painter()->device()); KoColor color2 = m_smudgePainter->paintColor(); m_smudgePainter->fill(0, 0, m_dstDabRect.width(), m_dstDabRect.height(), color2); } else { KoColor color = painter()->paintColor(); // get the pixel on the canvas that lies beneath the hot spot // of the dab and fill the temporary paint device with that color KisCrossDeviceColorPickerInt colorPicker(painter()->device(), color); colorPicker.pickColor(pt.x(), pt.y(), color.data()); m_smudgePainter->fill(0, 0, m_dstDabRect.width(), m_dstDabRect.height(), color); } } // if the user selected the color smudge option, // we will mix some color into the temporary painting device (m_tempDev) if (m_colorRateOption.isChecked()) { // this will apply the opacity (selected by the user) to copyPainter // (but fit the rate inbetween the range 0.0 to (1.0-SmudgeRate)) qreal maxColorRate = qMax(1.0 - m_smudgeRateOption.getRate(), 0.2); m_colorRateOption.apply(*m_colorRatePainter, info, 0.0, maxColorRate, fpOpacity); // paint a rectangle with the current color (foreground color) // or a gradient color (if enabled) // into the temporary painting device and use the user selected // composite mode KoColor color = painter()->paintColor(); m_gradientOption.apply(color, m_gradient, info); m_colorRatePainter->fill(0, 0, m_dstDabRect.width(), m_dstDabRect.height(), color); } // if color is disabled (only smudge) and "overlay mode" is enabled // then first blit the region under the brush from the image projection // to the painting device to prevent a rapid build up of alpha value // if the color to be smudged is semi transparent. if (m_image && m_overlayModeOption.isChecked() && !m_colorRateOption.isChecked()) { painter()->setCompositeOp(COMPOSITE_COPY); painter()->setOpacity(OPACITY_OPAQUE_U8); m_image->blockUpdates(); painter()->bitBlt(m_dstDabRect.topLeft(), m_image->projection(), m_dstDabRect); m_image->unblockUpdates(); } // set opacity calculated by the rate option m_smudgeRateOption.apply(*painter(), info, 0.0, 1.0, fpOpacity); // then blit the temporary painting device on the canvas at the current brush position // the alpha mask (maskDab) will be used here to only blit the pixels that are in the area (shape) of the brush painter()->setCompositeOp(COMPOSITE_COPY); painter()->bitBltWithFixedSelection(m_dstDabRect.x(), m_dstDabRect.y(), m_tempDev, m_maskDab, m_dstDabRect.width(), m_dstDabRect.height()); painter()->renderMirrorMaskSafe(m_dstDabRect, m_tempDev, 0, 0, m_maskDab, !m_dabCache->needSeparateOriginal()); // restore orginal opacy and composite mode values painter()->setOpacity(oldOpacity); painter()->setCompositeOp(oldCompositeOpId); return spacingInfo; } diff --git a/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp b/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp index bb0a4ff88e..40334edefd 100644 --- a/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp +++ b/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp @@ -1,191 +1,189 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along 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.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisBrushOp::KisBrushOp(const KisBrushBasedPaintOpSettings *settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisBrushBasedPaintOp(settings, painter), m_opacityOption(node), m_hsvTransformation(0) { Q_UNUSED(image); Q_ASSERT(settings); KisColorSourceOption colorSourceOption; colorSourceOption.readOptionSetting(settings); m_colorSource = colorSourceOption.createColorSource(painter); m_hsvOptions.append(KisPressureHSVOption::createHueOption()); m_hsvOptions.append(KisPressureHSVOption::createSaturationOption()); m_hsvOptions.append(KisPressureHSVOption::createValueOption()); Q_FOREACH (KisPressureHSVOption * option, m_hsvOptions) { option->readOptionSetting(settings); option->resetAllSensors(); if (option->isChecked() && !m_hsvTransformation) { m_hsvTransformation = painter->backgroundColor().colorSpace()->createColorTransformation("hsv_adjustment", QHash()); } } m_opacityOption.readOptionSetting(settings); m_flowOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_spacingOption.readOptionSetting(settings); m_softnessOption.readOptionSetting(settings); m_sharpnessOption.readOptionSetting(settings); m_darkenOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_mixOption.readOptionSetting(settings); m_scatterOption.readOptionSetting(settings); m_opacityOption.resetAllSensors(); m_flowOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_softnessOption.resetAllSensors(); m_sharpnessOption.resetAllSensors(); m_darkenOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_scatterOption.resetAllSensors(); m_dabCache->setSharpnessPostprocessing(&m_sharpnessOption); m_rotationOption.applyFanCornersInfo(this); } KisBrushOp::~KisBrushOp() { qDeleteAll(m_hsvOptions); delete m_colorSource; delete m_hsvTransformation; } KisSpacingInformation KisBrushOp::paintAt(const KisPaintInformation& info) { if (!painter()->device()) return KisSpacingInformation(1.0); KisBrushSP brush = m_brush; Q_ASSERT(brush); if (!brush) return KisSpacingInformation(1.0); if (!brush->canPaintFor(info)) return KisSpacingInformation(1.0); qreal scale = m_sizeOption.apply(info); scale *= KisLodTransform::lodToScale(painter()->device()); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); KisPaintDeviceSP device = painter()->device(); qreal rotation = m_rotationOption.apply(info); - setCurrentScale(scale); - setCurrentRotation(rotation); QPointF cursorPos = m_scatterOption.apply(info, brush->maskWidth(scale, rotation, 0, 0, info), brush->maskHeight(scale, rotation, 0, 0, info)); quint8 origOpacity = painter()->opacity(); m_opacityOption.setFlow(m_flowOption.apply(info)); m_opacityOption.apply(painter(), info); m_colorSource->selectColor(m_mixOption.apply(info), info); m_darkenOption.apply(m_colorSource, info); if (m_hsvTransformation) { Q_FOREACH (KisPressureHSVOption * option, m_hsvOptions) { option->apply(m_hsvTransformation, info); } m_colorSource->applyColorTransformation(m_hsvTransformation); } QRect dabRect; KisFixedPaintDeviceSP dab = m_dabCache->fetchDab(device->compositionSourceColorSpace(), m_colorSource, cursorPos, scale, scale, rotation, info, m_softnessOption.apply(info), &dabRect); // sanity check for the size calculation code if (dab->bounds().size() != dabRect.size()) { warnKrita << "KisBrushOp: dab bounds is not dab rect. See bug 327156" << dab->bounds().size() << dabRect.size(); } painter()->bltFixed(dabRect.topLeft(), dab, dab->bounds()); painter()->renderMirrorMaskSafe(dabRect, dab, !m_dabCache->needSeparateOriginal()); painter()->setOpacity(origOpacity); return effectiveSpacing(scale, rotation, m_spacingOption, info); } void KisBrushOp::paintLine(const KisPaintInformation& pi1, const KisPaintInformation& pi2, KisDistanceInformation *currentDistance) { if (m_sharpnessOption.isChecked() && m_brush && (m_brush->width() == 1) && (m_brush->height() == 1)) { if (!m_lineCacheDevice) { m_lineCacheDevice = source()->createCompositionSourceDevice(); } else { m_lineCacheDevice->clear(); } KisPainter p(m_lineCacheDevice); p.setPaintColor(painter()->paintColor()); p.drawDDALine(pi1.pos(), pi2.pos()); QRect rc = m_lineCacheDevice->extent(); painter()->bitBlt(rc.x(), rc.y(), m_lineCacheDevice, rc.x(), rc.y(), rc.width(), rc.height()); //fixes Bug 338011 painter()->renderMirrorMask(rc, m_lineCacheDevice); } else { KisPaintOp::paintLine(pi1, pi2, currentDistance); } } diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop.cpp b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop.cpp index 2caf8381c0..26c6b3f40c 100644 --- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop.cpp +++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop.cpp @@ -1,283 +1,282 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004,2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_duplicateop.h" #include "kis_duplicateop_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_duplicateop_settings.h" #include "kis_duplicateop_settings_widget.h" #include "kis_duplicateop_option.h" KisDuplicateOp::KisDuplicateOp(const KisDuplicateOpSettings *settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisBrushBasedPaintOp(settings, painter) , m_image(image) , m_node(node) , m_settings(settings) { Q_ASSERT(settings); Q_ASSERT(painter); m_sizeOption.readOptionSetting(settings); m_healing = settings->getBool(DUPLICATE_HEALING); m_perspectiveCorrection = settings->getBool(DUPLICATE_CORRECT_PERSPECTIVE); m_moveSourcePoint = settings->getBool(DUPLICATE_MOVE_SOURCE_POINT); m_cloneFromProjection = settings->getBool(DUPLICATE_CLONE_FROM_PROJECTION); m_srcdev = source()->createCompositionSourceDevice(); } KisDuplicateOp::~KisDuplicateOp() { } #define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x))) KisSpacingInformation KisDuplicateOp::paintAt(const KisPaintInformation& info) { if (!painter()->device()) return KisSpacingInformation(1.0); KisBrushSP brush = m_brush; if (!brush) return KisSpacingInformation(1.0); if (!brush->canPaintFor(info)) return KisSpacingInformation(1.0); if (!m_duplicateStartIsSet) { m_duplicateStartIsSet = true; m_duplicateStart = info.pos(); } KisPaintDeviceSP realSourceDevice; if (m_cloneFromProjection && m_image) { realSourceDevice = m_image->projection(); } else { KisNodeSP externalSourceNode = m_settings->sourceNode(); /** * The saved layer might have been deleted by then, so check if it * still belongs to a graph */ if (!externalSourceNode || !externalSourceNode->graphListener()) { externalSourceNode = m_node; } realSourceDevice = externalSourceNode->projection(); } qreal scale = m_sizeOption.apply(info); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); - setCurrentScale(scale); static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); static KoColor color(Qt::black, cs); QRect dstRect; KisFixedPaintDeviceSP dab = m_dabCache->fetchDab(cs, color, info.pos(), scale, scale, 0.0, info, 1.0, &dstRect); if (dstRect.isEmpty()) return KisSpacingInformation(1.0); QPoint srcPoint; if (m_moveSourcePoint) { srcPoint = (dstRect.topLeft() - m_settings->offset()).toPoint(); } else { QPointF hotSpot = brush->hotSpot(scale, scale, 0, info); srcPoint = (m_settings->position() - hotSpot).toPoint(); } qint32 sw = dstRect.width(); qint32 sh = dstRect.height(); // Perspective correction ? // if (m_perspectiveCorrection && m_image && m_image->perspectiveGrid()->countSubGrids() == 1) { // Matrix3qreal startM = Matrix3qreal::Identity(); // Matrix3qreal endM = Matrix3qreal::Identity(); // // First look for the grid corresponding to the start point // KisSubPerspectiveGrid* subGridStart = *m_image->perspectiveGrid()->begin(); // QRect r = QRect(0, 0, m_image->width(), m_image->height()); // if (subGridStart) { // startM = KisPerspectiveMath::computeMatrixTransfoFromPerspective(r, *subGridStart->topLeft(), *subGridStart->topRight(), *subGridStart->bottomLeft(), *subGridStart->bottomRight()); // } // // Second look for the grid corresponding to the end point // KisSubPerspectiveGrid* subGridEnd = *m_image->perspectiveGrid()->begin(); // if (subGridEnd) { // endM = KisPerspectiveMath::computeMatrixTransfoToPerspective(*subGridEnd->topLeft(), *subGridEnd->topRight(), *subGridEnd->bottomLeft(), *subGridEnd->bottomRight(), r); // } // // Compute the translation in the perspective transformation space: // QPointF positionStartPaintingT = KisPerspectiveMath::matProd(endM, QPointF(m_duplicateStart)); // QPointF duplicateStartPositionT = KisPerspectiveMath::matProd(endM, QPointF(m_duplicateStart) - QPointF(m_settings->offset())); // QPointF translat = duplicateStartPositionT - positionStartPaintingT; // KisSequentialIterator dstIt(m_srcdev, QRect(0, 0, sw, sh)); // KisRandomSubAccessorSP srcAcc = realSourceDevice->createRandomSubAccessor(); // //Action // do { // QPointF p = KisPerspectiveMath::matProd(startM, KisPerspectiveMath::matProd(endM, QPointF(dstIt.x() + dstRect.x(), dstIt.y() + dstRect.y())) + translat); // srcAcc->moveTo(p); // srcAcc->sampledOldRawData(dstIt.rawData()); // } while (dstIt.nextPixel()); // } // else { KisPainter copyPainter(m_srcdev); copyPainter.setCompositeOp(COMPOSITE_COPY); copyPainter.bitBltOldData(0, 0, realSourceDevice, srcPoint.x(), srcPoint.y(), sw, sh); copyPainter.end(); } // heal ? if (m_healing) { QRect healRect(dstRect); const bool smallWidth = healRect.width() < 3; const bool smallHeight = healRect.height() < 3; if (smallWidth || smallHeight) { healRect.adjust(-smallWidth, -smallHeight, smallWidth, smallHeight); } const int healSW = healRect.width(); const int healSH = healRect.height(); quint16 srcData[4]; quint16 tmpData[4]; QScopedArrayPointer matrix(new qreal[ 3 * healSW * healSH ]); // First divide const KoColorSpace* srcCs = realSourceDevice->colorSpace(); const KoColorSpace* tmpCs = m_srcdev->colorSpace(); KisHLineConstIteratorSP srcIt = realSourceDevice->createHLineConstIteratorNG(healRect.x(), healRect.y() , healSW); KisHLineIteratorSP tmpIt = m_srcdev->createHLineIteratorNG(0, 0, healSW); qreal* matrixIt = matrix.data(); for (int j = 0; j < healSH; j++) { for (int i = 0; i < healSW; i++) { srcCs->toLabA16(srcIt->oldRawData(), (quint8*)srcData, 1); tmpCs->toLabA16(tmpIt->rawData(), (quint8*)tmpData, 1); // Division for (int k = 0; k < 3; k++) { matrixIt[k] = srcData[k] / (qreal)qMax((int)tmpData [k], 1); } srcIt->nextPixel(); tmpIt->nextPixel(); matrixIt += 3; } srcIt->nextRow(); tmpIt->nextRow(); } // Minimize energy { int iter = 0; qreal err; QScopedArrayPointer solution(new qreal[ 3 * healSW * healSH ]); do { err = DuplicateOpUtils::minimizeEnergy(matrix.data(), solution.data(), healSW, healSH); solution.swap(matrix); iter++; } while (err > 0.00001 && iter < 100); } // Finaly multiply KisHLineIteratorSP tmpIt2 = m_srcdev->createHLineIteratorNG(0, 0, healSW); matrixIt = &matrix[0]; for (int j = 0; j < healSH; j++) { for (int i = 0; i < healSW; i++) { tmpCs->toLabA16(tmpIt2->rawData(), (quint8*)tmpData, 1); // Multiplication for (int k = 0; k < 3; k++) { tmpData[k] = (int)CLAMP(matrixIt[k] * qMax((int) tmpData[k], 1), 0, 65535); } tmpCs->fromLabA16((quint8*)tmpData, tmpIt2->rawData(), 1); tmpIt2->nextPixel(); matrixIt += 3; } tmpIt2->nextRow(); } } painter()->bitBltWithFixedSelection(dstRect.x(), dstRect.y(), m_srcdev, dab, dstRect.width(), dstRect.height()); painter()->renderMirrorMaskSafe(dstRect, m_srcdev, 0, 0, dab, !m_dabCache->needSeparateOriginal()); return effectiveSpacing(scale, 0.0); } diff --git a/plugins/paintops/deform/CMakeLists.txt b/plugins/paintops/deform/CMakeLists.txt index b5cf407fc4..8ea628adc7 100644 --- a/plugins/paintops/deform/CMakeLists.txt +++ b/plugins/paintops/deform/CMakeLists.txt @@ -1,17 +1,18 @@ set(kritadeformpaintop_SOURCES deform_brush.cpp deform_paintop_plugin.cpp kis_deform_paintop.cpp kis_deform_paintop_settings.cpp kis_deform_option.cpp kis_deform_paintop_settings_widget.cpp + kis_brush_size_option.cpp ) ki18n_wrap_ui(kritadeformpaintop_SOURCES wdgdeformoptions.ui ) add_library(kritadeformpaintop MODULE ${kritadeformpaintop_SOURCES}) target_link_libraries(kritadeformpaintop kritaui kritalibpaintop) install(TARGETS kritadeformpaintop DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( FILES krita-deform.png DESTINATION ${DATA_INSTALL_DIR}/krita/images ) diff --git a/plugins/paintops/libpaintop/kis_brush_size_option.cpp b/plugins/paintops/deform/kis_brush_size_option.cpp similarity index 100% rename from plugins/paintops/libpaintop/kis_brush_size_option.cpp rename to plugins/paintops/deform/kis_brush_size_option.cpp diff --git a/plugins/paintops/libpaintop/kis_brush_size_option.h b/plugins/paintops/deform/kis_brush_size_option.h similarity index 95% rename from plugins/paintops/libpaintop/kis_brush_size_option.h rename to plugins/paintops/deform/kis_brush_size_option.h index cf7de86831..3727dc205d 100644 --- a/plugins/paintops/libpaintop/kis_brush_size_option.h +++ b/plugins/paintops/deform/kis_brush_size_option.h @@ -1,96 +1,95 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_SIZE_OPTION_H_ #define KIS_SIZE_OPTION_H_ #include #include #include -#include class KisBrushSizeOptionsWidget; const QString BRUSH_SHAPE = "Brush/shape"; const QString BRUSH_DIAMETER = "Brush/diameter"; const QString BRUSH_ASPECT = "Brush/aspect"; const QString BRUSH_SCALE = "Brush/scale"; const QString BRUSH_ROTATION = "Brush/rotation"; const QString BRUSH_SPACING = "Brush/spacing"; const QString BRUSH_DENSITY = "Brush/density"; const QString BRUSH_JITTER_MOVEMENT = "Brush/jitterMovement"; const QString BRUSH_JITTER_MOVEMENT_ENABLED = "Brush/jitterMovementEnabled"; -class PAINTOP_EXPORT KisBrushSizeOption : public KisPaintOpOption +class KisBrushSizeOption : public KisPaintOpOption { public: KisBrushSizeOption(); ~KisBrushSizeOption(); int diameter() const; void setDiameter(int diameter); void setSpacing(qreal spacing); qreal spacing() const; qreal brushAspect() const; void writeOptionSetting(KisPropertiesConfiguration* setting) const; void readOptionSetting(const KisPropertiesConfiguration* setting); private: KisBrushSizeOptionsWidget * m_options; }; -class PAINTOP_EXPORT KisBrushSizeProperties +class KisBrushSizeProperties { public: quint16 shape; quint16 diameter; qreal aspect; qreal scale; qreal rotation; qreal spacing; qreal density; qreal jitterMovementAmount; bool jitterEnabled; public: void readOptionSetting(const KisPropertiesConfiguration * settings) { //TODO: shape shape = 0; diameter = quint16(settings->getDouble(BRUSH_DIAMETER)); aspect = settings->getDouble(BRUSH_ASPECT); rotation = settings->getDouble(BRUSH_ROTATION) * (M_PI / 180.0); scale = settings->getDouble(BRUSH_SCALE); density = settings->getDouble(BRUSH_DENSITY) * 0.01; spacing = settings->getDouble(BRUSH_SPACING); if ((jitterEnabled = settings->getBool(BRUSH_JITTER_MOVEMENT_ENABLED))) { jitterMovementAmount = settings->getDouble(BRUSH_JITTER_MOVEMENT); } else { jitterMovementAmount = 0.0; } } }; #endif diff --git a/plugins/paintops/deform/kis_deform_paintop.cpp b/plugins/paintops/deform/kis_deform_paintop.cpp index 89bf7cc237..2c3a9cb593 100644 --- a/plugins/paintops/deform/kis_deform_paintop.cpp +++ b/plugins/paintops/deform/kis_deform_paintop.cpp @@ -1,152 +1,150 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_deform_paintop.h" #include "kis_deform_paintop_settings.h" #include #include #include #include #include #include "kis_global.h" #include "kis_paint_device.h" #include "kis_painter.h" #include "kis_selection.h" #include "kis_random_accessor_ng.h" #include #include "kis_deform_option.h" #include "kis_brush_size_option.h" #include #include #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(qrand()) / static_cast(RAND_MAX)) #endif KisDeformPaintOp::KisDeformPaintOp(const KisDeformPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); Q_ASSERT(settings); m_sizeProperties.readOptionSetting(settings); // sensors m_sizeOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_sizeOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_properties.action = settings->getInt(DEFORM_ACTION); m_properties.deformAmount = settings->getDouble(DEFORM_AMOUNT); m_properties.useBilinear = settings->getBool(DEFORM_USE_BILINEAR); m_properties.useCounter = settings->getBool(DEFORM_USE_COUNTER); m_properties.useOldData = settings->getBool(DEFORM_USE_OLD_DATA); m_deformBrush.setProperties(&m_properties); m_deformBrush.setSizeProperties(&m_sizeProperties); m_deformBrush.initDeformAction(); m_dev = source(); if ((m_sizeProperties.diameter * 0.5) > 1) { m_ySpacing = m_xSpacing = m_sizeProperties.diameter * 0.5 * m_sizeProperties.spacing; } else { m_ySpacing = m_xSpacing = 1.0; } m_spacing = m_xSpacing; } KisDeformPaintOp::~KisDeformPaintOp() { } KisSpacingInformation KisDeformPaintOp::paintAt(const KisPaintInformation& info) { if (!painter()) return KisSpacingInformation(m_spacing); if (!m_dev) return KisSpacingInformation(m_spacing); KisFixedPaintDeviceSP dab = cachedDab(source()->compositionSourceColorSpace()); qint32 x; qreal subPixelX; qint32 y; qreal subPixelY; QPointF pt = info.pos(); if (m_sizeProperties.jitterEnabled) { pt.setX(pt.x() + ((m_sizeProperties.diameter * drand48()) - m_sizeProperties.diameter * 0.5) * m_sizeProperties.jitterMovementAmount); pt.setY(pt.y() + ((m_sizeProperties.diameter * drand48()) - m_sizeProperties.diameter * 0.5) * m_sizeProperties.jitterMovementAmount); } qreal rotation = m_rotationOption.apply(info); // Deform Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(info); - setCurrentRotation(rotation); - setCurrentScale(scale); rotation += m_sizeProperties.rotation; scale *= m_sizeProperties.scale; QPointF pos = pt - m_deformBrush.hotSpot(scale, rotation); splitCoordinate(pos.x(), &x, &subPixelX); splitCoordinate(pos.y(), &y, &subPixelY); KisFixedPaintDeviceSP mask = m_deformBrush.paintMask(dab, m_dev, scale, rotation, info.pos(), subPixelX, subPixelY, x, y ); // this happens for the first dab of the move mode, we need more information for being able to move if (!mask) { return KisSpacingInformation(m_spacing); } quint8 origOpacity = m_opacityOption.apply(painter(), info); painter()->bltFixedWithFixedSelection(x, y, dab, mask, mask->bounds().width() , mask->bounds().height()); painter()->renderMirrorMask(QRect(QPoint(x, y), QSize(mask->bounds().width() , mask->bounds().height())), dab, mask); painter()->setOpacity(origOpacity); return KisSpacingInformation(m_spacing); } diff --git a/plugins/paintops/filterop/kis_filterop.cpp b/plugins/paintops/filterop/kis_filterop.cpp index 05d0b03416..bae2059083 100644 --- a/plugins/paintops/filterop/kis_filterop.cpp +++ b/plugins/paintops/filterop/kis_filterop.cpp @@ -1,148 +1,147 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_filterop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisFilterOp::KisFilterOp(const KisFilterOpSettings *settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisBrushBasedPaintOp(settings, painter) , m_filterConfiguration(0) { Q_UNUSED(node); Q_UNUSED(image); Q_ASSERT(settings); Q_ASSERT(painter); m_tmpDevice = source()->createCompositionSourceDevice(); m_sizeOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_sizeOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_filter = KisFilterRegistry::instance()->get(settings->getString(FILTER_ID)); m_filterConfiguration = settings->filterConfig(); m_smudgeMode = settings->getBool(FILTER_SMUDGE_MODE); m_rotationOption.applyFanCornersInfo(this); } KisFilterOp::~KisFilterOp() { } KisSpacingInformation KisFilterOp::paintAt(const KisPaintInformation& info) { if (!painter()) { return KisSpacingInformation(1.0); } if (!m_filter) { return KisSpacingInformation(1.0); } if (!source()) { return KisSpacingInformation(1.0); } KisBrushSP brush = m_brush; if (!brush) return KisSpacingInformation(1.0); if (! brush->canPaintFor(info)) return KisSpacingInformation(1.0); qreal scale = m_sizeOption.apply(info); scale *= KisLodTransform::lodToScale(painter()->device()); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); - setCurrentScale(scale); qreal rotation = m_rotationOption.apply(info); static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); static KoColor color(Qt::black, cs); QRect dstRect; KisFixedPaintDeviceSP dab = m_dabCache->fetchDab(cs, color, info.pos(), scale, scale, rotation, info, 1.0, &dstRect); if (dstRect.isEmpty()) return KisSpacingInformation(1.0); QRect dabRect = dab->bounds(); // sanity check Q_ASSERT(dstRect.size() == dabRect.size()); // Filter the paint device QRect neededRect = m_filter->neededRect(dstRect, m_filterConfiguration, painter()->device()->defaultBounds()->currentLevelOfDetail()); KisPainter p(m_tmpDevice); if (!m_smudgeMode) { p.setCompositeOp(COMPOSITE_COPY); } p.bitBltOldData(neededRect.topLeft() - dstRect.topLeft(), source(), neededRect); KisTransaction transaction(m_tmpDevice); m_filter->process(m_tmpDevice, dabRect, m_filterConfiguration, 0); transaction.end(); painter()-> bitBltWithFixedSelection(dstRect.x(), dstRect.y(), m_tmpDevice, dab, 0, 0, dabRect.x(), dabRect.y(), dabRect.width(), dabRect.height()); painter()->renderMirrorMaskSafe(dstRect, m_tmpDevice, 0, 0, dab, !m_dabCache->needSeparateOriginal()); return effectiveSpacing(scale, rotation); } diff --git a/plugins/paintops/hairy/kis_hairy_paintop.cpp b/plugins/paintops/hairy/kis_hairy_paintop.cpp index 57daf72b1f..d49edf3aa0 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop.cpp +++ b/plugins/paintops/hairy/kis_hairy_paintop.cpp @@ -1,141 +1,139 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_hairy_paintop.h" #include "kis_hairy_paintop_settings.h" #include #include #include #include #include "kis_paint_device.h" #include "kis_painter.h" #include #include #include #include #include #include #include #include #include "kis_brush.h" KisHairyPaintOp::KisHairyPaintOp(const KisBrushBasedPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image) Q_ASSERT(settings); m_dev = node ? node->paintDevice() : 0; KisBrushOption brushOption; brushOption.readOptionSetting(settings, true); KisBrushSP brush = brushOption.brush(); KisFixedPaintDeviceSP dab = cachedDab(painter->device()->compositionSourceColorSpace()); if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) { dab = brush->paintDevice(source()->colorSpace(), 1.0, 0.0, KisPaintInformation()); } else { brush->mask(dab, painter->paintColor(), 1.0, 1.0, 0.0, KisPaintInformation()); } m_brush.fromDabWithDensity(dab, settings->getDouble(HAIRY_BRISTLE_DENSITY) * 0.01); m_brush.setInkColor(painter->paintColor()); loadSettings(settings); m_brush.setProperties(&m_properties); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); } void KisHairyPaintOp::loadSettings(const KisBrushBasedPaintOpSettings* settings) { m_properties.inkAmount = settings->getInt(HAIRY_INK_AMOUNT); //TODO: wait for the transfer function with variable size m_properties.inkDepletionCurve = settings->getCubicCurve(HAIRY_INK_DEPLETION_CURVE).floatTransfer(m_properties.inkAmount); m_properties.inkDepletionEnabled = settings->getBool(HAIRY_INK_DEPLETION_ENABLED); m_properties.useSaturation = settings->getBool(HAIRY_INK_USE_SATURATION); m_properties.useOpacity = settings->getBool(HAIRY_INK_USE_OPACITY); m_properties.useWeights = settings->getBool(HAIRY_INK_USE_WEIGHTS); m_properties.pressureWeight = settings->getDouble(HAIRY_INK_PRESSURE_WEIGHT) / 100.0; m_properties.bristleLengthWeight = settings->getDouble(HAIRY_INK_BRISTLE_LENGTH_WEIGHT) / 100.0; m_properties.bristleInkAmountWeight = settings->getDouble(HAIRY_INK_BRISTLE_INK_AMOUNT_WEIGHT) / 100.0; m_properties.inkDepletionWeight = settings->getDouble(HAIRY_INK_DEPLETION_WEIGHT); m_properties.useSoakInk = settings->getBool(HAIRY_INK_SOAK); m_properties.useMousePressure = settings->getBool(HAIRY_BRISTLE_USE_MOUSEPRESSURE); m_properties.shearFactor = settings->getDouble(HAIRY_BRISTLE_SHEAR); m_properties.randomFactor = settings->getDouble(HAIRY_BRISTLE_RANDOM); m_properties.scaleFactor = settings->getDouble(HAIRY_BRISTLE_SCALE); m_properties.threshold = settings->getBool(HAIRY_BRISTLE_THRESHOLD); m_properties.antialias = settings->getBool(HAIRY_BRISTLE_ANTI_ALIASING); m_properties.useCompositing = settings->getBool(HAIRY_BRISTLE_USE_COMPOSITING); m_properties.connectedPath = settings->getBool(HAIRY_BRISTLE_CONNECTED); } KisSpacingInformation KisHairyPaintOp::paintAt(const KisPaintInformation& info) { Q_UNUSED(info); return KisSpacingInformation(0.5); } void KisHairyPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { Q_UNUSED(currentDistance); if (!painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } // Hairy Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(pi2); scale *= KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(pi2); quint8 origOpacity = m_opacityOption.apply(painter(), pi2); - setCurrentScale(scale); - setCurrentRotation(rotation); m_brush.paintLine(m_dab, m_dev, pi1, pi2, scale * m_properties.scaleFactor, rotation); //QRect rc = m_dab->exactBounds(); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); } diff --git a/plugins/paintops/hatching/kis_hatching_paintop.cpp b/plugins/paintops/hatching/kis_hatching_paintop.cpp index b8a75cd133..578e011b5f 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop.cpp +++ b/plugins/paintops/hatching/kis_hatching_paintop.cpp @@ -1,204 +1,203 @@ /* * Copyright (c) 2008,2009 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_hatching_paintop.h" #include "kis_hatching_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisHatchingPaintOp::KisHatchingPaintOp(const KisHatchingPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisBrushBasedPaintOp(settings, painter) , m_image(image) { Q_UNUSED(node); m_settings = new KisHatchingPaintOpSettings(); settings->initializeTwin(m_settings); m_hatchingBrush = new HatchingBrush(m_settings); m_crosshatchingOption.readOptionSetting(settings); m_separationOption.readOptionSetting(settings); m_thicknessOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_crosshatchingOption.resetAllSensors(); m_separationOption.resetAllSensors(); m_thicknessOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); } KisHatchingPaintOp::~KisHatchingPaintOp() { delete m_hatchingBrush; } KisSpacingInformation KisHatchingPaintOp::paintAt(const KisPaintInformation& info) { //------START SIMPLE ERROR CATCHING------- if (!painter()->device()) return KisSpacingInformation(1.0); if (!m_hatchedDab) m_hatchedDab = source()->createCompositionSourceDevice(); else m_hatchedDab->clear(); //Simple convenience renaming, I'm thinking of removing these inherited quirks KisBrushSP brush = m_brush; KisPaintDeviceSP device = painter()->device(); //Macro to catch errors Q_ASSERT(brush); //----------SIMPLE error catching code, maybe it's not even needed------ if (!brush) return KisSpacingInformation(1.0); if (!brush->canPaintFor(info)) return KisSpacingInformation(1.0); //SENSOR-depending settings m_settings->crosshatchingsensorvalue = m_crosshatchingOption.apply(info); m_settings->separationsensorvalue = m_separationOption.apply(info); m_settings->thicknesssensorvalue = m_thicknessOption.apply(info); const qreal additionalScale = KisLodTransform::lodToScale(painter()->device()); const double scale = additionalScale * m_sizeOption.apply(info); if ((scale * brush->width()) <= 0.01 || (scale * brush->height()) <= 0.01) return KisSpacingInformation(1.0); - setCurrentScale(scale); quint8 origOpacity = m_opacityOption.apply(painter(), info); /*----Fetch the Dab----*/ static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); static KoColor color(Qt::black, cs); QRect dstRect; KisFixedPaintDeviceSP maskDab = m_dabCache->fetchDab(cs, color, info.pos(), scale, scale, 0.0, info, 1.0, &dstRect); // sanity check KIS_ASSERT_RECOVER_NOOP(dstRect.size() == maskDab->bounds().size()); /*-----Convenient renaming for the limits of the maskDab, this will be used to hatch a dab of just the right size------*/ qint32 x, y, sw, sh; dstRect.getRect(&x, &y, &sw, &sh); //------This If_block pre-fills the future m_hatchedDab with a pretty backgroundColor if (m_settings->opaquebackground) { KoColor aersh = painter()->backgroundColor(); m_hatchedDab->fill(0, 0, (sw - 1), (sh - 1), aersh.data()); //this plus yellow background = french fry brush } // Trick for moire pattern to look better bool donotbasehatch = false; /* If block describing how to stack hatching passes to generate crosshatching according to user specifications */ if (m_settings->enabledcurvecrosshatching) { if (m_settings->perpendicular) { if (m_settings->crosshatchingsensorvalue > 0.5) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale); } else if (m_settings->minusthenplus) { if (m_settings->crosshatchingsensorvalue > 0.33) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); if (m_settings->crosshatchingsensorvalue > 0.67) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); } else if (m_settings->plusthenminus) { if (m_settings->crosshatchingsensorvalue > 0.33) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); if (m_settings->crosshatchingsensorvalue > 0.67) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); } else if (m_settings->moirepattern) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->crosshatchingsensorvalue) * 180), painter()->paintColor(), additionalScale); donotbasehatch = true; } } else { if (m_settings->perpendicular) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale); } else if (m_settings->minusthenplus) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); } else if (m_settings->plusthenminus) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); } else if (m_settings->moirepattern) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-10), painter()->paintColor(), additionalScale); } } if (!donotbasehatch) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, m_settings->angle, painter()->paintColor(), additionalScale); // The most important line, the one that paints to the screen. painter()->bitBltWithFixedSelection(x, y, m_hatchedDab, maskDab, sw, sh); painter()->renderMirrorMaskSafe(QRect(QPoint(x, y), QSize(sw, sh)), m_hatchedDab, 0, 0, maskDab, !m_dabCache->needSeparateOriginal()); painter()->setOpacity(origOpacity); return effectiveSpacing(scale, 0.0); } double KisHatchingPaintOp::spinAngle(double spin) { double tempangle = m_settings->angle + spin; qint8 factor = 1; if (tempangle < 0) factor = -1; tempangle = fabs(fmod(tempangle, 180)); if ((tempangle >= 0) && (tempangle <= 90)) return factor * tempangle; else if ((tempangle > 90) && (tempangle <= 180)) return factor * -(180 - tempangle); return 0; // this should never be executed except if NAN } diff --git a/plugins/paintops/libpaintop/CMakeLists.txt b/plugins/paintops/libpaintop/CMakeLists.txt index 6275b75e88..37c63befbd 100644 --- a/plugins/paintops/libpaintop/CMakeLists.txt +++ b/plugins/paintops/libpaintop/CMakeLists.txt @@ -1,97 +1,96 @@ set(kritalibpaintop_LIB_SRCS kis_airbrush_option.cpp kis_auto_brush_widget.cpp kis_spacing_selection_widget.cpp kis_bidirectional_mixing_option.cpp kis_bidirectional_mixing_option_widget.cpp kis_brush_based_paintop.cpp kis_brush_chooser.cpp kis_brush_option_widget.cpp - kis_brush_size_option.cpp kis_brush_option.cpp kis_brush_selection_widget.cpp kis_color_option.cpp kis_color_source.cpp kis_color_source_option.cpp kis_color_source_option_widget.cpp kis_curve_option.cpp kis_curve_option_widget.cpp kis_custom_brush_widget.cpp kis_clipboard_brush_widget.cpp kis_dynamic_sensor.cc kis_dab_cache.cpp kis_filter_option.cpp kis_multi_sensors_model_p.cpp kis_multi_sensors_selector.cpp kis_paint_action_type_option.cpp kis_precision_option.cpp kis_pressure_darken_option.cpp kis_pressure_hsv_option.cpp kis_pressure_opacity_option.cpp kis_pressure_flow_option.cpp kis_pressure_mirror_option.cpp kis_pressure_scatter_option.cpp kis_pressure_scatter_option_widget.cpp kis_pressure_sharpness_option.cpp kis_pressure_sharpness_option_widget.cpp kis_pressure_mirror_option_widget.cpp kis_pressure_rotation_option.cpp kis_pressure_size_option.cpp kis_pressure_spacing_option.cpp kis_pressure_softness_option.cpp kis_pressure_mix_option.cpp kis_pressure_gradient_option.cpp kis_pressure_flow_opacity_option.cpp kis_pressure_flow_opacity_option_widget.cpp kis_pressure_spacing_option_widget.cpp kis_current_outline_fetcher.cpp kis_text_brush_chooser.cpp kis_brush_based_paintop_options_widget.cpp kis_brush_based_paintop_settings.cpp kis_compositeop_option.cpp kis_texture_option.cpp kis_pressure_texture_strength_option.cpp kis_embedded_pattern_manager.cpp sensors/kis_dynamic_sensors.cc sensors/kis_dynamic_sensor_drawing_angle.cpp sensors/kis_dynamic_sensor_distance.cc sensors/kis_dynamic_sensor_time.cc sensors/kis_dynamic_sensor_fade.cpp sensors/kis_dynamic_sensor_fuzzy.cpp ) ki18n_wrap_ui(kritalibpaintop_LIB_SRCS forms/wdgautobrush.ui forms/wdgBrushSizeOptions.ui forms/wdgcurveoption.ui forms/wdgcustombrush.ui forms/wdgclipboardbrush.ui forms/wdgtextbrush.ui forms/wdgincremental.ui forms/wdgmultisensorsselector.ui forms/wdgairbrush.ui forms/wdgfilteroption.ui forms/wdgcoloroptions.ui forms/wdgbrushchooser.ui forms/wdgCompositeOpOption.ui forms/wdgflowopacityoption.ui sensors/SensorDistanceConfiguration.ui sensors/SensorTimeConfiguration.ui sensors/SensorFadeConfiguration.ui ) add_library(kritalibpaintop SHARED ${kritalibpaintop_LIB_SRCS} ) generate_export_header(kritalibpaintop BASE_NAME kritapaintop EXPORT_MACRO_NAME PAINTOP_EXPORT) target_link_libraries(kritalibpaintop kritaui kritalibbrush kritawidgetutils) target_link_libraries(kritalibpaintop LINK_INTERFACE_LIBRARIES kritaui kritalibbrush) set_target_properties(kritalibpaintop PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritalibpaintop ${INSTALL_TARGETS_DEFAULT_ARGS}) add_subdirectory(tests) diff --git a/plugins/paintops/libpaintop/kis_pressure_ratio_option.cpp b/plugins/paintops/libpaintop/kis_pressure_ratio_option.cpp new file mode 100644 index 0000000000..f2ac852317 --- /dev/null +++ b/plugins/paintops/libpaintop/kis_pressure_ratio_option.cpp @@ -0,0 +1,32 @@ +/* This file is part of the KDE project + * Copyright (C) Nishant Rodrigues , (C) 2016 + * + * 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_pressure_ratio_option.h" + +KisPressureRatioOption::KisPressureRatioOption() + : KisCurveOption("Ratio", KisPaintOpOption::GENERAL, true) +{ +} + +double KisPressureRatioOption::apply(const KisPaintInformation & info) const +{ + if (!isChecked()) return 1.0; + return computeValue(info); +} + diff --git a/plugins/paintops/libpaintop/kis_pressure_ratio_option.h b/plugins/paintops/libpaintop/kis_pressure_ratio_option.h new file mode 100644 index 0000000000..bac334d8c5 --- /dev/null +++ b/plugins/paintops/libpaintop/kis_pressure_ratio_option.h @@ -0,0 +1,31 @@ +/* This file is part of the KDE project + * Copyright (C) Nishant Rodrigues , (C) 2016 + * + * 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. + */ + +#pragma once + +#include "kis_curve_option.h" +#include + +/** Calculates effect of pressure on aspect ratio of brush tip */ +class PAINTOP_EXPORT KisPressureRatioOption : public KisCurveOption +{ +public: + KisPressureRatioOption(); + double apply(const KisPaintInformation & info) const; +}; diff --git a/plugins/paintops/sketch/kis_sketch_paintop.cpp b/plugins/paintops/sketch/kis_sketch_paintop.cpp index 9a6ca52713..e7cac4d3d0 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop.cpp +++ b/plugins/paintops/sketch/kis_sketch_paintop.cpp @@ -1,302 +1,300 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 Ricardo Cabello * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_sketch_paintop.h" #include "kis_sketch_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_lod_transform.h" #include /* * Based on Harmony project http://github.com/mrdoob/harmony/ */ // chrome : diff 0.2, sketchy : 0.3, fur: 0.5 // fur : distance / thresholdDistance // shaded: opacity per line :/ // ((1 - (d / 1000)) * 0.1 * BRUSH_PRESSURE), offset == 0 // chrome: color per line :/ //this.context.strokeStyle = "rgba(" + Math.floor(Math.random() * COLOR[0]) + ", " + Math.floor(Math.random() * COLOR[1]) + ", " + Math.floor(Math.random() * COLOR[2]) + ", " + 0.1 * BRUSH_PRESSURE + " )"; // long fur // from: count + offset * -random // to: i point - (offset * -random) + random * 2 // probability distance / thresholdDistnace // shaded: probabity : paint always - 0.0 density KisSketchPaintOp::KisSketchPaintOp(const KisSketchPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_sketchProperties.readOptionSetting(settings); m_brushOption.readOptionSetting(settings, true); m_densityOption.readOptionSetting(settings); m_lineWidthOption.readOptionSetting(settings); m_offsetScaleOption.readOptionSetting(settings); m_brush = m_brushOption.brush(); m_dabCache = new KisDabCache(m_brush); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_painter = 0; m_count = 0; } KisSketchPaintOp::~KisSketchPaintOp() { delete m_painter; delete m_dabCache; } void KisSketchPaintOp::drawConnection(const QPointF& start, const QPointF& end, double lineWidth) { if (lineWidth == 1.0) { m_painter->drawThickLine(start, end, lineWidth, lineWidth); } else { m_painter->drawLine(start, end, lineWidth, true); } } void KisSketchPaintOp::updateBrushMask(const KisPaintInformation& info, qreal scale, qreal rotation) { QRect dstRect; m_maskDab = m_dabCache->fetchDab(m_dab->colorSpace(), painter()->paintColor(), info.pos(), scale, scale, rotation, info, 1.0, &dstRect); m_brushBoundingBox = dstRect; m_hotSpot = QPointF(0.5 * m_brushBoundingBox.width(), 0.5 * m_brushBoundingBox.height()); } void KisSketchPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { Q_UNUSED(currentDistance); if (!m_brush || !painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); m_painter = new KisPainter(m_dab); m_painter->setPaintColor(painter()->paintColor()); } else { m_dab->clear(); } QPointF prevMouse = pi1.pos(); QPointF mousePosition = pi2.pos(); m_points.append(mousePosition); const qreal lodAdditionalScale = KisLodTransform::lodToScale(painter()->device()); const qreal scale = lodAdditionalScale * m_sizeOption.apply(pi2); if ((scale * m_brush->width()) <= 0.01 || (scale * m_brush->height()) <= 0.01) return; const qreal currentLineWidth = qMax(0.9, lodAdditionalScale * m_lineWidthOption.apply(pi2, m_sketchProperties.lineWidth)); const qreal currentOffsetScale = m_offsetScaleOption.apply(pi2, m_sketchProperties.offset); const double rotation = m_rotationOption.apply(pi2); const double currentProbability = m_densityOption.apply(pi2, m_sketchProperties.probability); // shaded: does not draw this line, chrome does, fur does if (m_sketchProperties.makeConnection) { drawConnection(prevMouse, mousePosition, currentLineWidth); } - setCurrentScale(scale); - setCurrentRotation(rotation); qreal thresholdDistance = 0.0; // update the mask for simple mode only once // determine the radius if (m_count == 0 && m_sketchProperties.simpleMode) { updateBrushMask(pi2, 1.0, 0.0); //m_radius = qMax(m_maskDab->bounds().width(),m_maskDab->bounds().height()) * 0.5; m_radius = 0.5 * qMax(m_brush->width(), m_brush->height()); } if (!m_sketchProperties.simpleMode) { updateBrushMask(pi2, scale, rotation); m_radius = qMax(m_maskDab->bounds().width(), m_maskDab->bounds().height()) * 0.5; thresholdDistance = pow(m_radius, 2); } if (m_sketchProperties.simpleMode) { // update the radius according scale in simple mode thresholdDistance = pow(m_radius * scale, 2); } // determine density const qreal density = thresholdDistance * currentProbability; // probability behaviour qreal probability = 1.0 - currentProbability; QColor painterColor = painter()->paintColor().toQColor(); QColor randomColor; KoColor color(m_dab->colorSpace()); int w = m_maskDab->bounds().width(); quint8 opacityU8 = 0; quint8 * pixel; qreal distance; QPoint positionInMask; QPointF diff; int size = m_points.size(); // MAIN LOOP for (int i = 0; i < size; i++) { diff = m_points.at(i) - mousePosition; distance = diff.x() * diff.x() + diff.y() * diff.y(); // circle test bool makeConnection = false; if (m_sketchProperties.simpleMode) { if (distance < thresholdDistance) { makeConnection = true; } // mask test } else { if (m_brushBoundingBox.contains(m_points.at(i))) { positionInMask = (diff + m_hotSpot).toPoint(); uint pos = ((positionInMask.y() * w + positionInMask.x()) * m_maskDab->pixelSize()); if (pos < m_maskDab->allocatedPixels() * m_maskDab->pixelSize()) { pixel = m_maskDab->data() + pos; opacityU8 = m_maskDab->colorSpace()->opacityU8(pixel); if (opacityU8 != 0) { makeConnection = true; } } } } if (!makeConnection) { // check next point continue; } if (m_sketchProperties.distanceDensity) { probability = distance / density; } KisRandomSourceSP randomSource = pi2.randomSource(); // density check if (randomSource->generateNormalized() >= probability) { QPointF offsetPt = diff * currentOffsetScale; if (m_sketchProperties.randomRGB) { /** * Since the order of calculation of function * parameters is not defined by C++ standard, we * should generate values in an external code snippet * which has a definite order of execution. */ qreal r1 = randomSource->generateNormalized(); qreal r2 = randomSource->generateNormalized(); qreal r3 = randomSource->generateNormalized(); // some color transformation per line goes here randomColor.setRgbF(r1 * painterColor.redF(), r2 * painterColor.greenF(), r3 * painterColor.blueF()); color.fromQColor(randomColor); m_painter->setPaintColor(color); } // distance based opacity quint8 opacity = OPACITY_OPAQUE_U8; if (m_sketchProperties.distanceOpacity) { opacity *= qRound((1.0 - (distance / thresholdDistance))); } if (m_sketchProperties.randomOpacity) { opacity *= randomSource->generateNormalized(); } m_painter->setOpacity(opacity); if (m_sketchProperties.magnetify) { drawConnection(mousePosition + offsetPt, m_points.at(i) - offsetPt, currentLineWidth); } else { drawConnection(mousePosition + offsetPt, mousePosition - offsetPt, currentLineWidth); } } }// end of MAIN LOOP m_count++; QRect rc = m_dab->extent(); quint8 origOpacity = m_opacityOption.apply(painter(), pi2); painter()->bitBlt(rc.x(), rc.y(), m_dab, rc.x(), rc.y(), rc.width(), rc.height()); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); } KisSpacingInformation KisSketchPaintOp::paintAt(const KisPaintInformation& info) { KisDistanceInformation di; paintLine(info, info, &di); return di.currentSpacing(); } diff --git a/plugins/paintops/spray/kis_spray_paintop.cpp b/plugins/paintops/spray/kis_spray_paintop.cpp index f01eafa759..3905ef3013 100644 --- a/plugins/paintops/spray/kis_spray_paintop.cpp +++ b/plugins/paintops/spray/kis_spray_paintop.cpp @@ -1,133 +1,131 @@ /* * Copyright (c) 2008-2012 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_spray_paintop.h" #include "kis_spray_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisSprayPaintOp::KisSprayPaintOp(const KisSprayPaintOpSettings *settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) , m_settings(settings) , m_isPresetValid(true) , m_node(node) { Q_ASSERT(settings); Q_ASSERT(painter); Q_UNUSED(image); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_brushOption.readOptionSetting(settings, true); m_colorProperties.fillProperties(settings); m_properties.loadSettings(settings); // first load tip properties as shape properties are dependent on diameter/scale/aspect m_shapeProperties.loadSettings(settings, m_properties.diameter * m_properties.scale, m_properties.diameter * m_properties.aspect * m_properties.scale); // TODO: what to do with proportional sizes? m_shapeDynamicsProperties.loadSettings(settings); if (!m_shapeProperties.enabled && !m_brushOption.brush()) { // in case the preset does not contain the definition for KisBrush m_isPresetValid = false; dbgKrita << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset."; } m_sprayBrush.setProperties(&m_properties, &m_colorProperties, &m_shapeProperties, &m_shapeDynamicsProperties, m_brushOption.brush()); m_sprayBrush.setFixedDab(cachedDab()); // spacing if ((m_properties.diameter * 0.5) > 1) { m_ySpacing = m_xSpacing = m_properties.diameter * 0.5 * m_properties.spacing; } else { m_ySpacing = m_xSpacing = 1.0; } m_spacing = m_xSpacing; } KisSprayPaintOp::~KisSprayPaintOp() { } KisSpacingInformation KisSprayPaintOp::paintAt(const KisPaintInformation& info) { if (!painter() || !m_isPresetValid) { return KisSpacingInformation(m_spacing); } if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } qreal rotation = m_rotationOption.apply(info); quint8 origOpacity = m_opacityOption.apply(painter(), info); // Spray Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed const qreal scale = m_sizeOption.apply(info); const qreal additionalScale = KisLodTransform::lodToScale(painter()->device()); - setCurrentRotation(rotation); - setCurrentScale(scale * additionalScale); m_sprayBrush.paint(m_dab, m_node->paintDevice(), info, rotation, scale, additionalScale, painter()->paintColor(), painter()->backgroundColor()); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); return KisSpacingInformation(m_spacing * additionalScale); } diff --git a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.cpp b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.cpp index 49551b8cd5..d038b34696 100644 --- a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.cpp +++ b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.cpp @@ -1,235 +1,233 @@ /* * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tangent_normal_paintop.h" #include #include #include #include #include #include #include #include #include #include #include KisTangentNormalPaintOp::KisTangentNormalPaintOp(const KisBrushBasedPaintOpSettings* settings, KisPainter* painter, KisNodeSP node, KisImageSP image): KisBrushBasedPaintOp(settings, painter), m_opacityOption(node), m_tempDev(painter->device()->createCompositionSourceDevice()) { Q_UNUSED(image); //Init, read settings, etc// m_tangentTiltOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_flowOption.readOptionSetting(settings); m_spacingOption.readOptionSetting(settings); m_softnessOption.readOptionSetting(settings); m_sharpnessOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_scatterOption.readOptionSetting(settings); m_sizeOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_flowOption.resetAllSensors(); m_spacingOption.resetAllSensors(); m_softnessOption.resetAllSensors(); m_sharpnessOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_scatterOption.resetAllSensors(); m_dabCache->setSharpnessPostprocessing(&m_sharpnessOption); m_rotationOption.applyFanCornersInfo(this); } KisTangentNormalPaintOp::~KisTangentNormalPaintOp() { //destroy things here// } KisSpacingInformation KisTangentNormalPaintOp::paintAt(const KisPaintInformation& info) { /* * For the color, the precision of tilt is only 60x60, and the precision of direction and rotation are 360 and 360*90. * You can't get more precise than 8bit. Therefore, we will check if the current space is RGB, * if so we request a profile with that space and 8bit bit depth, if not, just sRGB */ KoColor currentColor = painter()->paintColor(); QString currentSpace = currentColor.colorSpace()->colorModelId().id(); const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); if (currentSpace != "RGBA") { rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); } else { rgbColorSpace = currentColor.colorSpace(); } QVector channelValues(4); qreal r, g, b; if (currentColor.colorSpace()->colorDepthId().id()=="F16" || currentColor.colorSpace()->colorDepthId().id()=="F32"){ channelValues[0] = 0.5;//red channelValues[1] = 0.5;//green channelValues[2] = 1.0;//blue channelValues[3] = 1.0;//alpha, leave alone. m_tangentTiltOption.apply(info, &r, &g, &b); channelValues[0] = r;//red channelValues[1] = g;//green channelValues[2] = b;//blue } else { channelValues[0] = 1.0;//blue channelValues[1] = 0.5;//green channelValues[2] = 0.5;//red channelValues[3] = 1.0;//alpha, leave alone. m_tangentTiltOption.apply(info, &r, &g, &b); channelValues[0] = b;//blue channelValues[1] = g;//green channelValues[2] = r;//red } quint8 data[4]; rgbColorSpace->fromNormalisedChannelsValue(data, channelValues); KoColor color(data, rgbColorSpace);//Should be default RGB(0.5,0.5,1.0) //draw stuff here, return kisspacinginformation. KisBrushSP brush = m_brush; if (!painter()->device() || !brush || !brush->canPaintFor(info)) { return KisSpacingInformation(1.0); } qreal scale = m_sizeOption.apply(info); scale *= KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(info); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); - setCurrentScale(scale); - setCurrentRotation(rotation); QPointF cursorPos = m_scatterOption.apply(info, brush->maskWidth(scale, rotation, 0, 0, info), brush->maskHeight(scale, rotation, 0, 0, info)); m_maskDab = m_dabCache->fetchDab(rgbColorSpace, color, cursorPos, scale, scale, rotation, info, m_softnessOption.apply(info), &m_dstDabRect); if (m_dstDabRect.isEmpty()) return KisSpacingInformation(1.0); QRect dabRect = m_maskDab->bounds(); // sanity check Q_ASSERT(m_dstDabRect.size() == dabRect.size()); Q_UNUSED(dabRect); quint8 oldOpacity = painter()->opacity(); QString oldCompositeOpId = painter()->compositeOp()->id(); m_opacityOption.setFlow(m_flowOption.apply(info)); m_opacityOption.apply(painter(), info); //paint with the default color? Copied this from color smudge.// //painter()->setCompositeOp(COMPOSITE_COPY); //painter()->fill(0, 0, m_dstDabRect.width(), m_dstDabRect.height(), color); painter()->bltFixed(m_dstDabRect.topLeft(), m_maskDab, m_maskDab->bounds()); painter()->renderMirrorMaskSafe(m_dstDabRect, m_maskDab, !m_dabCache->needSeparateOriginal()); // restore orginal opacity and composite mode values painter()->setOpacity(oldOpacity); painter()->setCompositeOp(oldCompositeOpId); return effectiveSpacing(scale, rotation, m_spacingOption, info); } void KisTangentNormalPaintOp::paintLine(const KisPaintInformation& pi1, const KisPaintInformation& pi2, KisDistanceInformation *currentDistance) { if (m_sharpnessOption.isChecked() && m_brush && (m_brush->width() == 1) && (m_brush->height() == 1)) { if (!m_lineCacheDevice) { m_lineCacheDevice = m_tempDev; } else { m_lineCacheDevice->clear(); } KisPainter p(m_lineCacheDevice); KoColor currentColor = painter()->paintColor(); QString currentSpace = currentColor.colorSpace()->colorModelId().id(); const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); if (currentSpace != "RGBA") { rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); } else { rgbColorSpace = currentColor.colorSpace(); } QVector channelValues(4); qreal r, g, b; if (currentColor.colorSpace()->colorDepthId().id()=="F16" || currentColor.colorSpace()->colorDepthId().id()=="F32"){ channelValues[0] = 0.5;//red channelValues[1] = 0.5;//green channelValues[2] = 1.0;//blue channelValues[3] = 1.0;//alpha, leave alone. m_tangentTiltOption.apply(pi2, &r, &g, &b); channelValues[0] = r;//red channelValues[1] = g;//green channelValues[2] = b;//blue } else { channelValues[0] = 1.0;//blue channelValues[1] = 0.5;//green channelValues[2] = 0.5;//red channelValues[3] = 1.0;//alpha, leave alone. m_tangentTiltOption.apply(pi2, &r, &g, &b); channelValues[0] = b;//blue channelValues[1] = g;//green channelValues[2] = r;//red } quint8 data[4]; rgbColorSpace->fromNormalisedChannelsValue(data, channelValues); KoColor color(data, rgbColorSpace); p.setPaintColor(color); p.drawDDALine(pi1.pos(), pi2.pos()); QRect rc = m_lineCacheDevice->extent(); painter()->bitBlt(rc.x(), rc.y(), m_lineCacheDevice, rc.x(), rc.y(), rc.width(), rc.height()); painter()->renderMirrorMask(rc, m_lineCacheDevice); } else { KisPaintOp::paintLine(pi1, pi2, currentDistance); } }