diff --git a/Messages.sh b/Messages.sh
index f4cd49d0ea..25aea70ad2 100755
--- a/Messages.sh
+++ b/Messages.sh
@@ -1,27 +1,27 @@
#! /bin/sh
-source krita_xgettext.sh
+source kundo2_aware_xgettext.sh
$EXTRACTRC `find . -name \*.ui | grep -v '/tests/'` >> rc.cpp
RCFILES=`find . -name \*.xmlgui \
| grep -v plugins/extensions/metadataeditor/editors/dublincore.xmlgui \
| grep -v plugins/extensions/metadataeditor/editors/exif.xmlgui \
| grep -v krita/sketch/KritaSketchWin.xmlgui \
| grep -v krita/gemini/KritaGeminiWin.xmlgui
`
$EXTRACTRC $RCFILES >> rc.cpp
ACTIONFILES=`find . -name \*.action`
./action_i18n.pl --context=action $ACTIONFILES >> rc.cpp
# extracti18n.pl extracts additional data from brushes, palettes etc.
perl extracti18n.pl > i18ndata
# Ignore sdk/templates which contains templates for writing future plugins.
# Also ignore crashreporter, it has it's own catalog
# None of the placeholder strings inside will be seen by users.
-krita_xgettext krita.pot i18ndata rc.cpp \
+kundo2_aware_xgettext krita.pot i18ndata rc.cpp \
`find . -name \*.cc -o -name \*.h -o -name \*.cpp | \
grep -v '/tests/' | grep -v './sdk/templates' | grep -v './krita/crashreporter/'`
# Clean up
rm -f i18ndata rc.cpp
diff --git a/krita/data/actions/ConnectionTool.action b/krita/data/actions/ConnectionTool.action
index 71d6d26af6..22e2b77af1 100644
--- a/krita/data/actions/ConnectionTool.action
+++ b/krita/data/actions/ConnectionTool.action
@@ -1,156 +1,160 @@
+
diff --git a/krita/data/profiles/elles-icc-profiles/CMakeLists.txt b/krita/data/profiles/elles-icc-profiles/CMakeLists.txt
index f5f1b3ffbc..1b3d466513 100644
--- a/krita/data/profiles/elles-icc-profiles/CMakeLists.txt
+++ b/krita/data/profiles/elles-icc-profiles/CMakeLists.txt
@@ -1,97 +1,28 @@
########### install files ###############
install(FILES
- ACES-elle-V2-g10.icc
- ACES-elle-V4-g10.icc
- ACES-elle-V2-g22.icc
- ACES-elle-V4-g22.icc
- ACES-elle-V2-labl.icc
- ACES-elle-V4-labl.icc
- ACES-elle-V2-srgbtrc.icc
- ACES-elle-V4-srgbtrc.icc
- ACEScg-elle-V2-g10.icc
ACEScg-elle-V4-g10.icc
- ACEScg-elle-V2-labl.icc
- ACEScg-elle-V4-labl.icc
- ACEScg-elle-V2-srgbtrc.icc
- ACEScg-elle-V4-srgbtrc.icc
- AllColorsRGB-elle-V2-g10.icc
- AllColorsRGB-elle-V4-g10.icc
- AllColorsRGB-elle-V2-labl.icc
- AllColorsRGB-elle-V4-labl.icc
- AllColorsRGB-elle-V2-srgbtrc.icc
- AllColorsRGB-elle-V4-srgbtrc.icc
- CIERGB-elle-V2-g10.icc
- CIERGB-elle-V2-g22.icc
- CIERGB-elle-V2-srgbtrc.icc
- CIERGB-elle-V4-g10.icc
- CIERGB-elle-V4-g22.icc
- CIERGB-elle-V4-srgbtrc.icc
- CIERGB-elle-V2-labl.icc
- CIERGB-elle-V4-labl.icc
- ClayRGB-elle-V2-g10.icc
- ClayRGB-elle-V2-g22.icc
- ClayRGB-elle-V2-srgbtrc.icc
- ClayRGB-elle-V4-g10.icc
- ClayRGB-elle-V4-g22.icc
ClayRGB-elle-V4-srgbtrc.icc
- ClayRGB-elle-V2-labl.icc;
- ClayRGB-elle-V4-labl.icc
- CMakeLists.txt
+
Gray-D50-elle-V2-g10.icc
Gray-D50-elle-V2-g18.icc
Gray-D50-elle-V2-g22.icc
Gray-D50-elle-V2-srgbtrc.icc
Gray-D50-elle-V4-g10.icc
Gray-D50-elle-V4-g18.icc
Gray-D50-elle-V4-g22.icc
Gray-D50-elle-V4-srgbtrc.icc
Gray-D50-elle-V2-labl.icc
Gray-D50-elle-V4-labl.icc
Gray-D50-elle-V2-rec709.icc
Gray-D50-elle-V4-rec709.icc
- IdentityRGB-elle-V2-g10.icc
- IdentityRGB-elle-V2-srgbtrc.icc
- IdentityRGB-elle-V4-g10.icc
- IdentityRGB-elle-V4-srgbtrc.icc
- IdentityRGB-elle-V2-labl.icc
- IdentityRGB-elle-V4-labl.icc
+
Lab-D50-Identity-elle-V2.icc
Lab-D50-Identity-elle-V4.icc
- LargeRGB-elle-V2-g10.icc
- LargeRGB-elle-V2-g18.icc
- LargeRGB-elle-V2-srgbtrc.icc
- LargeRGB-elle-V4-g10.icc
- LargeRGB-elle-V4-g18.icc
- LargeRGB-elle-V4-srgbtrc.icc
- LargeRGB-elle-V2-labl.icc
- LargeRGB-elle-V4-labl.icc
- Rec2020-elle-V2-rec709.icc
- Rec2020-elle-V4-rec709.icc
- Rec2020-elle-V2-g10.icc
- Rec2020-elle-V4-g10.icc
- Rec2020-elle-V2-labl.icc
- Rec2020-elle-V4-labl.icc
- Rec2020-elle-V2-srgbtrc.icc
- Rec2020-elle-V4-srgbtrc.icc
+
sRGB-elle-V2-g10.icc
sRGB-elle-V2-srgbtrc.icc
- sRGB-elle-V4-g10.icc
- sRGB-elle-V4-srgbtrc.icc
- sRGB-elle-V2-labl.icc
- sRGB-elle-V4-labl.icc
- #sRGB-elle-V2-rec709.icc
- #sRGB-elle-V4-rec709.icc
- WideRGB-elle-V2-g10.icc
- WideRGB-elle-V2-g22.icc
- WideRGB-elle-V2-srgbtrc.icc
- WideRGB-elle-V4-g10.icc
- WideRGB-elle-V4-g22.icc
- WideRGB-elle-V4-srgbtrc.icc
- WideRGB-elle-V2-srgbtrc.icc
- WideRGB-elle-V4-srgbtrc.icc
- WideRGB-elle-V2-labl.icc
- WideRGB-elle-V4-labl.icc
+
Lab-D50-Identity-elle-V2.icc
Lab-D50-Identity-elle-V4.icc
XYZ-D50-Identity-elle-V4.icc
DESTINATION ${SHARE_INSTALL_PREFIX}/color/icc/krita)
diff --git a/krita/krita.action b/krita/krita.action
index 3f6759344a..c4038c13bb 100644
--- a/krita/krita.action
+++ b/krita/krita.action
@@ -1,2973 +1,2936 @@
GeneralOpen Resources FolderOpens a file browser at the location Krita saves resources such as brushes to.Opens a file browser at the location Krita saves resources such as brushes to.Open Resources Folder00falseCleanup removed files...Cleanup removed filesCleanup removed files00falseC&ascadeCascadeCascade100false&TileTileTile100false
-
-
- Import Resources or Bundles...
-
- Import Resources or Bundles
- Import Resources or Bundles
- 0
- 0
-
- false
-
- Create Resource Bundle...Create Resource BundleCreate Resource Bundle00falseShow File ToolbarShow File ToolbarShow File ToolbarfalseShow color selectorShow color selectorShow color selectorShift+IfalseShow MyPaint shade selectorShow MyPaint shade selectorShow MyPaint shade selectorShift+MfalseShow minimal shade selectorShow minimal shade selectorShow minimal shade selectorShift+NfalseShow color historyShow color historyShow color historyHfalseShow common colorsShow common colorsShow common colorsUfalseShow Tool OptionsShow Tool OptionsShow Tool Options\falseShow Brush EditorShow Brush EditorShow Brush EditorF5falseShow Brush PresetsShow Brush PresetsShow Brush PresetsF6falseToggle Tablet DebuggerToggle Tablet DebuggerToggle Tablet Debugger00Ctrl+Shift+TfalseRename Composition...Rename CompositionRename Composition00false
+
+
+ Update Composition
+
+ Update Composition
+ Update Composition
+ 0
+ 0
+
+ false
+
+ PaintingMake brush color lighterMake brush color lighterMake brush color lighter00LfalseMake brush color darkerMake brush color darkerMake brush color darker00KfalseMake brush color more saturatedMake brush color more saturatedMake brush color more saturatedfalseMake brush color more desaturatedMake brush color more desaturatedMake brush color more desaturatedfalseShift brush color hue clockwiseShift brush color hue clockwiseShift brush color hue clockwisefalseShift brush color hue counter-clockwiseShift brush color hue counter-clockwiseShift brush color hue counter-clockwisefalseMake brush color more redMake brush color more redMake brush color more redfalseMake brush color more greenMake brush color more greenMake brush color more greenfalseMake brush color more blueMake brush color more blueMake brush color more bluefalseMake brush color more yellowMake brush color more yellowMake brush color more yellowfalseIncrease opacityIncrease opacityIncrease opacity00OfalseDecrease opacityDecrease opacityDecrease opacity00Ifalsedraw-eraserSet eraser modeSet eraser modeSet eraser mode100000Etrueview-refreshReload Original PresetReload Original PresetReload Original Preset10000falsetransparency-unlockedPreserve AlphaPreserve AlphaPreserve Alpha10000truetransform_icons_penPressureUse Pen PressureUse Pen PressureUse Pen Pressure10000truesymmetry-horizontalHorizontal Mirror ToolHorizontal Mirror ToolHorizontal Mirror Tool10000truesymmetry-verticalVertical Mirror ToolVertical Mirror ToolVertical Mirror Tool10000truePaste at cursorPaste at cursorPaste at cursor00false&Invert SelectionInvert current selectionInvert Selection10000000000100Ctrl+Shift+IfalseFill with Foreground Color (Opacity)Fill with Foreground Color (Opacity)Fill with Foreground Color (Opacity)100001Ctrl+Shift+BackspacefalseFill with Background Color (Opacity)Fill with Background Color (Opacity)Fill with Background Color (Opacity)100001Ctrl+BackspacefalseFill with Pattern (Opacity)Fill with Pattern (Opacity)Fill with Pattern (Opacity)100001false&Toggle Selection Display ModeToggle Selection Display ModeToggle Selection Display Mode00falseNext Favourite PresetNext Favourite PresetNext Favourite Preset,falsePrevious Favourite PresetPrevious Favourite PresetPrevious Favourite Preset.falseSwitch to Previous PresetSwitch to Previous PresetSwitch to Previous Preset/falseHide Brushes and Stuff ToolbarHide Brushes and Stuff ToolbarHide Brushes and Stuff Toolbartrue
-
- zoom-in
- Zoom &In
-
- Zoom In
- Zoom In
- Ctrl++; Ctrl+=
- false
-
-
-
- zoom-out
- Zoom &Out
-
- Zoom Out
- Zoom Out
- Ctrl+-
- false
-
- Reset Foreground and Background ColorReset Foreground and Background ColorReset Foreground and Background ColorDfalseSwap Foreground and Background ColorSwap Foreground and Background ColorSwap Foreground and Background ColorXfalseBrush Smoothing: WeightedBrush Smoothing: WeightedBrush Smoothing: WeightedfalseBrush Smoothing: DisabledBrush Smoothing: DisabledBrush Smoothing: DisabledfalseBrush Smoothing: StabilizerBrush Smoothing: StabilizerBrush Smoothing: StabilizerfalseDecrease Brush SizeDecrease Brush SizeDecrease Brush Size00[falseBrush Smoothing: BasicBrush Smoothing: BasicBrush Smoothing: BasicfalseIncrease Brush SizeIncrease Brush SizeIncrease Brush Size00]falseToggle AssistantToggle AssistantToggleAssistantCtrl+Shift+LtrueUndo Polygon Selection PointsUndo Polygon Selection PointsUndo Polygon Selection PointsShift+ZfalseFill with Foreground Color (Opacity)Fill with Foreground Color (Opacity)Fill with Foreground Color (Opacity)100001Ctrl+Shift+BackspacefalseFill with Background Color (Opacity)Fill with Background Color (Opacity)Fill with Background Color (Opacity)100001Ctrl+BackspacefalseFill with Pattern (Opacity)Fill with Pattern (Opacity)Fill with Pattern (Opacity)100001falseConvert &to ShapeConvert to ShapeConvert to Shape100000000000false&Select OpaqueSelect OpaqueSelect Opaque100000100false&Show Global Selection MaskShows global selection as a usual selection mask in <interface>Layers</interface> dockerShow Global Selection Mask00trueFilters&Color to Alpha...Color to AlphaColor to Alpha100000false&Top Edge DetectionTop Edge DetectionTop Edge Detection100000false&Index Colors...Index ColorsIndex Colors100000falseEmboss Horizontal &OnlyEmboss Horizontal OnlyEmboss Horizontal Only100000falseD&odgeDodgeDodge100000false&SharpenSharpenSharpen100000falseB&urnBurnBurn100000false&Mean RemovalMean RemovalMean Removal100000false&Gaussian Blur...Gaussian BlurGaussian Blur100000falseEmboss &in All DirectionsEmboss in All DirectionsEmboss in All Directions100000false&Small Tiles...Small TilesSmall Tiles100000false&Levels...LevelsLevels100000Ctrl+Lfalse&Sobel...SobelSobel100000false&Wave...WaveWave100000false&Motion Blur...Motion BlurMotion Blur100000false&Color Adjustment curves...Color Adjustment curvesColor Adjustment curves100000Ctrl+MfalsePi&xelize...PixelizePixelize100000falseEmboss (&Laplacian)Emboss (Laplacian)Emboss (Laplacian)100000false&Left Edge DetectionLeft Edge DetectionLeft Edge Detection100000false&Blur...BlurBlur100000false&Raindrops...RaindropsRaindrops100000false&Bottom Edge DetectionBottom Edge DetectionBottom Edge Detection100000false&Random Noise...Random NoiseRandom Noise100000false&Brightness/Contrast curve...Brightness/Contrast curveBrightness/Contrast curve100000falseColo&r Balance..Color Balance..Color Balance..100000Ctrl+Bfalse
- &PhongBumpmap...
+ &Phong Bumpmap...
- PhongBumpmap
- PhongBumpmap
+ Phong Bumpmap
+ Phong Bumpmap100000false&DesaturateDesaturateDesaturate100000Ctrl+Shift+UfalseColor &Transfer...Color TransferColor Transfer100000falseEmboss &Vertical OnlyEmboss Vertical OnlyEmboss Vertical Only100000false&Lens Blur...Lens BlurLens Blur100000falseM&inimize ChannelMinimize ChannelMinimize Channel100000falseM&aximize ChannelMaximize ChannelMaximize Channel100000false&Oilpaint...OilpaintOilpaint100000false&Right Edge DetectionRight Edge DetectionRight Edge Detection100000false&Auto ContrastAuto ContrastAuto Contrast100000false&Round Corners...Round CornersRound Corners100000false&Unsharp Mask...Unsharp MaskUnsharp Mask100000false&Emboss with Variable Depth...Emboss with Variable DepthEmboss with Variable Depth100000falseEmboss &Horizontal && VerticalEmboss Horizontal & VerticalEmboss Horizontal & Vertical100000falseRandom &Pick...Random PickRandom Pick100000false&Gaussian Noise Reduction...Gaussian Noise ReductionGaussian Noise Reduction100000false&Posterize...PosterizePosterize100000false&Wavelet Noise Reducer...Wavelet Noise ReducerWavelet Noise Reducer100000false&HSV Adjustment...HSV AdjustmentHSV Adjustment100000Ctrl+UfalseTool ShortcutsDynamic Brush ToolDynamic Brush ToolDynamic Brush ToolfalseCrop ToolCrop the image to an areaCrop the image to an areaCfalsePolygon ToolPolygon Tool. Shift-mouseclick ends the polygon.Polygon Tool. Shift-mouseclick ends the polygon.falseReferencesReferencesReferencesfalseRectangle ToolRectangle ToolRectangle ToolfalseMultibrush ToolMultibrush ToolMultibrush ToolQfalseShape Manipulation ToolShape Manipulation ToolShape Manipulation ToolfalseColor PickerSelect a color from the image or current layerSelect a color from the image or current layerPfalseText Editing ToolText editingText editingfalseOutline Selection ToolOutline Selection ToolOutline Selection ToolfalseArtistic Text ToolArtistic text editingArtistic text editingfalseBezier Curve Selection ToolSelect a Bezier Curve Selection ToolfalseSimilar Color Selection ToolSelect a Similar Color Selection ToolfalseFill ToolFill a contiguous area of color with a color, or fill a selection.Fill a contiguous area of color with a color, or fill a selection.FfalseLine ToolLine ToolLine ToolfalseFreehand Path ToolFreehand Path ToolFreehand Path ToolfalseBezier Curve ToolBezier Curve Tool. Shift-mouseclick ends the curve.Bezier Curve Tool. Shift-mouseclick ends the curve.falseEllipse ToolEllipse ToolEllipse ToolfalseFreehand Brush ToolFreehand Brush ToolFreehand Brush ToolBfalseCreate objectCreate objectCreate objectfalseElliptical Selection ToolElliptical Selection ToolElliptical Selection ToolJfalseContiguous Selection ToolContiguous Selection ToolContiguous Selection ToolfalsePattern editingPattern editingPattern editingfalseReviewReviewReviewfalseDraw a gradient.Draw a gradient.Draw a gradient.GfalsePolygonal Selection ToolPolygonal Selection ToolPolygonal Selection ToolfalseMeasurement ToolMeasure the distance between two pointsMeasure the distance between two pointsfalseRectangular Selection ToolRectangular Selection ToolRectangular Selection ToolCtrl+RfalseMove ToolMove a layerMove a layerTfalseVector Image ToolVector Image (EMF/WMF/SVM/SVG) toolVector Image (EMF/WMF/SVM/SVG) toolfalseCalligraphyCalligraphyCalligraphyfalsePath editingPath editingPath editingfalsePolyline ToolPolyline Tool. Shift-mouseclick ends the polyline.Polyline Tool. Shift-mouseclick ends the polyline.falseTransform ToolTransform a layer or a selectionTransform a layer or a selectionCtrl+TfalseRuler assistant editor toolRuler assistant editor toolRuler assistant editor toolfalseText toolText toolText toolfalseGradient Editing ToolGradient editingGradient editingfalse
-
-
- Brush Selection Tool
-
- Brush Selection Tool
- Brush Selection Tool
-
- false
-
- Blending Modes
+
+
Select Normal Blending ModeSelect Normal Blending ModeSelect Normal Blending Mode00Alt+Shift+NfalseSelect Dissolve Blending ModeSelect Dissolve Blending ModeSelect Dissolve Blending Mode00Alt+Shift+IfalseSelect Behind Blending ModeSelect Behind Blending ModeSelect Behind Blending Mode00Alt+Shift+QfalseSelect Clear Blending ModeSelect Clear Blending ModeSelect Clear Blending Mode00Alt+Shift+RfalseSelect Darken Blending ModeSelect Darken Blending ModeSelect Darken Blending Mode00Alt+Shift+KfalseSelect Multiply Blending ModeSelect Multiply Blending ModeSelect Multiply Blending Mode00Alt+Shift+MfalseSelect Color Burn Blending ModeSelect Color Burn Blending ModeSelect Color Burn Blending Mode00Alt+Shift+BfalseSelect Linear Burn Blending ModeSelect Linear Burn Blending ModeSelect Linear Burn Blending Mode00Alt+Shift+AfalseSelect Lighten Blending ModeSelect Lighten Blending ModeSelect Lighten Blending Mode00Alt+Shift+GfalseSelect Screen Blending ModeSelect Screen Blending ModeSelect Screen Blending Mode00Alt+Shift+SfalseSelect Color Dodge Blending ModeSelect Color Dodge Blending ModeSelect Color Dodge Blending Mode00Alt+Shift+DfalseSelect Linear Dodge Blending ModeSelect Linear Dodge Blending ModeSelect Linear Dodge Blending Mode00Alt+Shift+WfalseSelect Overlay Blending ModeSelect Overlay Blending ModeSelect Overlay Blending Mode00Alt+Shift+OfalseSelect Soft Light Blending ModeSelect Soft Light Blending ModeSelect Soft Light Blending Mode00Alt+Shift+FfalseSelect Hard Light Blending ModeSelect Hard Light Blending ModeSelect Hard Light Blending Mode00Alt+Shift+HfalseSelect Vivid Light Blending ModeSelect Vivid Light Blending ModeSelect Vivid Light Blending Mode00Alt+Shift+VfalseSelect Linear Light Blending ModeSelect Linear Light Blending ModeSelect Linear Light Blending Mode00Alt+Shift+JfalseSelect Pin Light Blending ModeSelect Pin Light Blending ModeSelect Pin Light Blending Mode00Alt+Shift+ZfalseSelect Hard Mix Blending ModeSelect Hard Mix Blending ModeSelect Hard Mix Blending Mode00Alt+Shift+LfalseSelect Difference Blending ModeSelect Difference Blending ModeSelect Difference Blending Mode00Alt+Shift+EfalseSelect Exclusion Blending ModeSelect Exclusion Blending ModeSelect Exclusion Blending Mode00Alt+Shift+XfalseSelect Hue Blending ModeSelect Hue Blending ModeSelect Hue Blending Mode00Alt+Shift+UfalseSelect Saturation Blending ModeSelect Saturation Blending ModeSelect Saturation Blending Mode00Alt+Shift+TfalseSelect Color Blending ModeSelect Color Blending ModeSelect Color Blending Mode00Alt+Shift+CfalseSelect Luminosity Blending ModeSelect Luminosity Blending ModeSelect Luminosity Blending Mode00Alt+Shift+YfalseAnimationPrevious frameMove to previous frameMove to previous frame10falseNext frameMove to next frameMove to next frame10falsePlay / pause animationPlay / pause animationPlay / pause animation10falseAdd blank frameAdd blank frameAdd blank frame1000000falseCopy FrameAdd duplicate frameAdd duplicate frame1000000falseToggle onion skinToggle onion skinToggle onion skin1000000falsePrevious KeyframefalseNext KeyframefalseFirst FramefalseLast FramefalseAuto Frame ModetruetrueAdd blank frameAdd blank frameAdd blank frame1000000falseShow in TimelinetrueLayersActivate next layerActivate next layerActivate next layer10000PgUpfalseActivate previous layerActivate previous layerActivate previous layer10000PgDownfalsegroupLayer&Group LayerGroup LayerGroup Layer10000falsecloneLayer&Clone LayerClone LayerClone Layer10000falsevectorLayer&Vector LayerVector LayerVector Layer10000falsefilterLayer&Filter Layer...Filter LayerFilter Layer10000falsefillLayer&Fill Layer...Fill LayerFill Layer10000falsefileLayer&File Layer...File LayerFile Layer10000falsetransparencyMask&Transparency MaskTransparency MaskTransparency Mask1000000falsefilterMask&Filter Mask...Filter MaskFilter Mask1000000falsefilterMask&Colorize MaskColorize MaskColorize Mask1000000falsetransformMask&Transform Mask...Transform MaskTransform Mask1000000falseselectionMask&Local SelectionLocal SelectionLocal Selection1000000falseview-filter&Isolate LayerIsolate LayerIsolate Layer10000truepaintLayer&Paint LayerPaint LayerPaint Layer10000Insertfalse&New Layer From VisibleNew layer from visibleNew layer from visible10000falseduplicatelayer&Duplicate Layer or MaskDuplicate Layer or MaskDuplicate Layer or Mask10000Ctrl+Jfalse&Cut Selection to New LayerCut Selection to New LayerCut Selection to New Layer1000000001Ctrl+Shift+JfalseCopy &Selection to New LayerCopy Selection to New LayerCopy Selection to New Layer1000000000Ctrl+Alt+JfalseCopy LayerCopy layer to clipboardCopy layer to clipboard00falseCut LayerCut layer to clipboardCut layer to clipboard00falsePaste LayerPaste layer from clipboardPaste layer from clipboard1000000000000000falseQuick GroupCreate a group layer containing selected layersQuick Group
- 0
+ 1000000Ctrl+GfalseQuick UngroupRemove grouping of the layers or remove one layer out of the groupQuick Ungroup
- 0
+ 1000000Ctrl+Alt+GfalseQuick Clipping GroupGroup selected layers and add a layer with clipped alpha channelQuick Clipping Group
- 0
+ 1000000Ctrl+Shift+GfalseAll LayersSelect all layersSelect all layers00falseVisible LayersSelect all visible layersSelect all visible layers00falseLocked LayersSelect all locked layersSelect all locked layers00falseInvisible LayersSelect all invisible layersSelect all invisible layers00falseUnlocked LayersSelect all unlocked layersSelect all unlocked layers00falsedocument-save&Save Layer/Mask...Save Layer/MaskSave Layer/Mask10000falsedocument-saveSave &Group Layers...Save Group LayersSave Group Layers1000000falseConvert group to &animated layerConvert child layers into animation framesConvert child layers into animation frames1000000falseI&mport Layer...Import LayerImport Layer1000000falsepaintLayer&as Paint Layer...as Paint Layeras Paint Layer10000falsetransparencyMaskas &Transparency Mask...as Transparency Maskas Transparency Mask10000falsefilterMaskas &Filter Mask...as Filter Maskas Filter Mask10000falseselectionMaskas &Selection Mask...as Selection Maskas Selection Mask10000falsepaintLayerto &Paint Layerto Paint Layerto Paint Layer10000falsetransparencyMaskto &Transparency Maskto Transparency Maskto Transparency Mask10000falsefilterMaskto &Filter Mask...to Filter Maskto Filter Mask10000falseselectionMaskto &Selection Maskto Selection Maskto Selection Mask10000falsetransparencyMask&Alpha into MaskAlpha into MaskAlpha into Mask10000010falsetransparency-enabled&Write as AlphaWrite as AlphaWrite as Alpha10000001falsedocument-save&Save Merged...Save MergedSave Merged10000000falseSplit Layer...Split LayerSplit Layer10000falseWavelet Decompose ...Wavelet DecomposeWavelet Decompose10001falsesymmetry-horizontalMirror Layer Hori&zontallyMirror Layer HorizontallyMirror Layer Horizontally10001falsesymmetry-verticalMirror Layer &VerticallyMirror Layer VerticallyMirror Layer Vertically10001false&Rotate Layer...Rotate LayerRotate Layer1000001falseobject-rotate-rightRotate &Layer 90° to the RightRotate Layer 90° to the RightRotate Layer 90° to the Right1000001falseobject-rotate-leftRotate Layer &90° to the LeftRotate Layer 90° to the LeftRotate Layer 90° to the Left1000001falseRotate Layer &180°Rotate Layer 180°Rotate Layer 180°1000001falseScale &Layer to new Size...Scale Layer to new SizeScale Layer to new Size1000001false&Shear Layer...Shear LayerShear Layer1000001false&Offset Layer...Offset LayerOffset Layer1000001falseClones &Array...Clones ArrayClones Array1000000false&Edit metadata...Edit metadataEdit metadata1000001false&Histogram...HistogramHistogram1000000false&Convert Layer Color Space...Convert Layer Color SpaceConvert Layer Color Space1000001false&Merge with Layer BelowMerge with Layer BelowMerge with Layer Below1000000Ctrl+Efalse&Flatten LayerFlatten LayerFlatten Layer1000000falseRas&terize LayerRasterize LayerRasterize Layer100000001falseFlatten ima&geFlatten imageFlatten image1000000Ctrl+Shift+Efalse
-
-
- &Merge Selected Layers
-
- Merge Selected Layers
- Merge Selected Layers
- Ctrl+Alt+E
- false
-
- La&yer Style...Layer StyleLayer Style1000001falseMove into previous groupMove into previous groupMove into previous group00falseMove into next groupMove into next groupMove into next group00falseRename current layerRename current layerRename current layer1000000F2falsedeletelayer&Remove LayerRemove LayerRemove Layer10001Shift+DeletefalsearrowupblrMove Layer or Mask UpMove Layer or Mask UpCtrl+PgUpfalsearrowdownMove Layer or Mask DownMove Layer or Mask DownCtrl+PgDownfalseproperties&Properties...PropertiesProperties10001F3false
diff --git a/krita/krita.xmlgui b/krita/krita.xmlgui
index 534e356436..cb21d45449 100644
--- a/krita/krita.xmlgui
+++ b/krita/krita.xmlgui
@@ -1,375 +1,375 @@
&Image&Rotate&LayerNew&Import/ExportImport&Convert&Select&Group&Transform&RotateS&plitS&plit Alpha&SelectFilte&r&ToolsRecordingMacrosSetti&ngs&HelpFileBrushes and Stuff
diff --git a/krita/kritamenu.action b/krita/kritamenu.action
index 1e2b17b9d6..48befac8f4 100644
--- a/krita/kritamenu.action
+++ b/krita/kritamenu.action
@@ -1,1718 +1,1720 @@
Filedocument-new&NewCreate new documentNew00Ctrl+Nfalsedocument-open&Open...Open an existing documentOpen00Ctrl+Ofalsedocument-open-recentOpen &RecentOpen a document which was recently openedOpen Recent10falsedocument-save&SaveSaveSave10Ctrl+Sfalsedocument-save-asSave &As...Save document under a new nameSave As10Ctrl+Shift+Sfalse
+
document-importOpen ex&isting Document as Untitled Document...Open existing Document as Untitled DocumentOpen existing Document as Untitled Document00falsedocument-exportE&xport...ExportExport10falseapplication-pdf&Export as PDF...Export as PDFExport as PDF10falseImport animation frames...Import animation framesImport animation frames10false&Render Animation...Render Animation to GIF, Image Sequence or VideoRender Animation10000false&Render Image Sequence AgainRender Animation to Image Sequence AgainRender Animation10000falseSave Incremental &VersionSave Incremental VersionSave Incremental Version10Ctrl+Alt+SfalseSave Incremental &BackupSave Incremental BackupSave Incremental Backup10F4false&Create Template From Image...Create Template From ImageCreate Template From Image10falseCreate Copy &From Current ImageCreate Copy From Current ImageCreate Copy From Current Image10falsedocument-print&Print...Print documentPrint10Ctrl+Pfalsedocument-print-previewPrint Previe&wShow a print preview of documentPrint Preview10falseconfigure&Document InformationDocument InformationDocument Information10false&Close AllClose AllClose All10Ctrl+Shift+WfalseC&loseCloseClose10false&QuitQuit applicationQuit00Ctrl+QfalseEditedit-undoUndoUndo last actionUndo10Ctrl+Zfalse_edit-redoRedoRedo last undone actionRedo10Ctrl+Shift+Zfalseedit-cutCu&tCut selection to clipboardCut00Ctrl+Xfalseedit-copy&CopyCopy selection to clipboardCopy00Ctrl+CfalseC&opy (sharp)Copy (sharp)Copy (sharp)1000000000falseCut (&sharp)Cut (sharp)Cut (sharp)1000000000falseCopy &mergedCopy mergedCopy merged1000000000Ctrl+Shift+Cfalseedit-paste&PastePaste clipboard contentPaste00Ctrl+VfalsePaste into &New ImagePaste into New ImagePaste into New Image00Ctrl+Shift+Nfalseedit-clearC&learClearClear10Delfalse&Fill with Foreground ColorFill with Foreground ColorFill with Foreground Color100001Shift+BackspacefalseFill &with Background ColorFill with Background ColorFill with Background Color100001BackspacefalseF&ill with PatternFill with PatternFill with Pattern100001falseStro&ke selected shapesStroke selected shapesStroke selected shapes10000000000false
- Stroke selec&tion
+ Stroke Selec&tion...Stroke selectionStroke selection100000000000falseDelete keyframeDelete keyframeDelete keyframe1000000falseWindowwindow-new&New WindowNew WindowNew Window00falseN&extNextNext100falsePreviousPreviousPreviousfalseView&Show Canvas OnlyShow just the canvas or the whole windowShow Canvas Only00Tabtrueview-fullscreenF&ull Screen ModeDisplay the window in full screenFull Screen Mode00Ctrl+Shift+Ffalse&Wrap Around ModeWrap Around ModeWrap Around Mode10Wtrue&Instant Preview ModeInstant Preview ModeInstant Preview Mode10Shift+LtrueSoft ProofingTurns on Soft ProofingTurns on Soft ProofingCtrl+YtrueOut of Gamut WarningsTurns on warnings for colors out of proofed gamut, needs soft proofing to be turned on.Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on.Ctrl+Shift+YtrueMirror ViewMirror ViewMirror ViewMfalse&Reset zoomReset zoomReset zoom10Ctrl+0falseZoom &InZoom InZoom In00Ctrl++falseZoom &OutZoom OutZoom Out00Ctrl+-falseRotate &Canvas RightRotate Canvas RightRotate Canvas Right10Ctrl+]falseRotate Canvas &LeftRotate Canvas LeftRotate Canvas Left10Ctrl+[falseReset Canvas RotationReset Canvas RotationReset Canvas Rotation10falseShow &RulersThe rulers show the horizontal and vertical positions of the mouse on the image and can be used to position your mouse at the right place on the canvas. <p>Uncheck this to hide the rulers.</p>Show RulersShow Rulers10trueRulers Track PointerThe rulers will track current mouse position and show it on screen. It can cause suptle performance slowdownRulers Track PointerRulers Track Pointer10trueShow GuidesShow or hide guidesShow Guides10trueLock GuidesLock or unlock guidesLock Guides10trueSnap to GuidesSnap cursor to guides positionSnap to Guides10trueShow Status &BarShow or hide the status barShow Status Bar00trueview-gridShow &GridShow GridShow Grid10000Ctrl+Shift+'trueSnap To GridSnap To GridSnap To Grid1000Ctrl+Shift+;trueShow Snap Options PopupShow Snap Options PopupShow Snap Options Popup1000Shift+sfalseSnap OrthogonalSnap OrthogonalSnap Orthogonal1000trueSnap NodeSnap NodeSnap Node1000trueSnap ExtensionSnap ExtensionSnap Extension1000trueSnap IntersectionSnap IntersectionSnap Intersection1000trueSnap Bounding BoxSnap Bounding BoxSnap Bounding Box1000trueSnap Image BoundsSnap Image BoundsSnap Image Bounds1000trueSnap Image CenterSnap Image CenterSnap Image Center1000trueS&how Painting AssistantsShow Painting AssistantsShow Painting Assistants10000trueShow &Assistant PreviewsShow Assistant PreviewsShow Assistant Previews10000trueImagedocument-properties&Properties...PropertiesProperties10000falseformat-stroke-color&Image Background Color and Transparency...Change the background color of the imageImage Background Color and Transparency10000false&Convert Image Color Space...Convert Image Color SpaceConvert Image Color Space10000falsetrim-to-image&Trim to Image SizeTrim to Image SizeTrim to Image Size10falseTrim to Current &LayerTrim to Current LayerTrim to Current Layer1000000falseTrim to S&electionTrim to SelectionTrim to Selection1000000000false&Rotate Image...Rotate ImageRotate Image10000falseobject-rotate-rightRotate &Image 90° to the RightRotate Image 90° to the RightRotate Image 90° to the Right10000falseobject-rotate-leftRotate Image &90° to the LeftRotate Image 90° to the LeftRotate Image 90° to the Left10000falseRotate Image &180°Rotate Image 180°Rotate Image 180°10000false&Shear Image...Shear ImageShear Image10000falsesymmetry-horizontal&Mirror Image HorizontallyMirror Image HorizontallyMirror Image Horizontally10000falsesymmetry-verticalMirror Image &VerticallyMirror Image VerticallyMirror Image Vertically10000falseScale Image To &New Size...Scale Image To New SizeScale Image To New Size10000Ctrl+Alt+Ifalse&Offset Image...Offset ImageOffset Image10000falseR&esize Canvas...Resize CanvasResize Canvas10000Ctrl+Alt+CfalseIm&age Split Image SplitImage Split10000falseSeparate Ima&ge...Separate ImageSeparate Image10000falseSelectedit-select-allSelect &AllSelect AllSelect All00Ctrl+Afalseedit-select-all&DeselectDeselectDeselect11000000000Ctrl+Shift+Afalse&ReselectReselectReselect00Ctrl+Shift+Dfalse&InvertInvertInvert100000Ctrl+Ifalse&Convert to Vector SelectionConvert to Vector SelectionConvert to Vector Selection100000000000falseConvert Shapes to &Vector SelectionConvert Shapes to Vector SelectionConvert Shapes to Vector Selection10000000000false&Feather Selection...Feather SelectionFeather Selection10000000000100Shift+F6falseDis&play SelectionDisplay SelectionDisplay Selection10000Ctrl+HtrueSca&le...ScaleScale100000000100falseS&elect from Color Range...Select from Color RangeSelect from Color Range10000100falseSelect &OpaqueSelect OpaqueSelect Opaque10000100false&Grow Selection...Grow SelectionGrow Selection10000000000100falseS&hrink Selection...Shrink SelectionShrink Selection10000000000100false&Border Selection...Border SelectionBorder Selection10000000000100falseS&moothSmoothSmooth10000000000100falseFilter&Apply Filter AgainApply Filter AgainApply Filter Again00Ctrl+FfalseAdjustAdjustAdjustfalseArtisticArtisticArtisticfalseBlurBlurBlurfalseColorsColorsColorsfalseEdge DetectionEdge DetectionEdge DetectionfalseEnhanceEnhanceEnhancefalseEmbossEmbossEmbossfalseMapMapMapfalseOtherOtherOtherfalseG'MICApply G'Mic ActionApply G'Mic ActionfalseToolsmedia-record&Start recording macroStart recording macroStart recording macro10000falsemedia-playback-stopStop &recording actionsStop recording actionsStop recording actions10000falsemedia-playback-start&Open and play...Open and playOpen and play00falsedocument-editOpen &and edit...Open and editOpen and edit00falseSettingsconfigure&Configure Krita...Configure KritaConfigure Krita00false&Manage Resources...Manage ResourcesManage Resources00falsepreferences-desktop-localeSwitch Application &Language...Switch Application LanguageSwitch Application Languagefalse&Show DockersShow DockersShow Dockers00trueSho&w Docker TitlebarsShow Docker TitlebarsShow Docker Titlebars00trueconfigureConfigure Tool&bars...Configure ToolbarsConfigure Toolbars00falseDockersDockersDockersfalse&ThemesThemesThemesfalseim-userActive Author ProfileActive Author ProfileActive Author Profileconfigure-shortcutsConfigure S&hortcuts...Configure ShortcutsConfigure Shortcuts00false&WindowWindowWindowfalseHelphelp-contentsKrita &HandbookKrita HandbookKrita HandbookF1falsetools-report-bug&Report Bug...Report BugReport Bugfalsecalligrakrita&About KritaAbout KritaAbout KritafalsekdeAbout &KDEAbout KDEAbout KDEfalseBrushes and Stuff&GradientsGradientsGradientsfalse&PatternsPatternsPatternsfalse&ColorColorColorfalse&Painter's ToolsPainter's ToolsPainter's ToolsfalseBrush compositeBrush compositeBrush compositefalseBrush option slider 1Brush option slider 1Brush option slider 1falseBrush option slider 2Brush option slider 2Brush option slider 2falseBrush option slider 3Brush option slider 3Brush option slider 3falseMirrorMirrorMirrorfalseWorkspacesWorkspacesWorkspacesfalse
diff --git a/krita/pics/misc-dark/dark_pivot-point.svg b/krita/pics/misc-dark/dark_pivot-point.svg
new file mode 100644
index 0000000000..bc0eccbb06
--- /dev/null
+++ b/krita/pics/misc-dark/dark_pivot-point.svg
@@ -0,0 +1,110 @@
+
+
+
+
diff --git a/krita/pics/misc-dark/misc-dark-icons.qrc b/krita/pics/misc-dark/misc-dark-icons.qrc
index fe5d162a0d..9b1007a880 100644
--- a/krita/pics/misc-dark/misc-dark-icons.qrc
+++ b/krita/pics/misc-dark/misc-dark-icons.qrc
@@ -1,31 +1,32 @@
dark_draw-eraser.svgdark_ox16-action-object-align-horizontal-center-calligra.svgdark_ox16-action-object-align-horizontal-left-calligra.svgdark_ox16-action-object-align-horizontal-right-calligra.svgdark_ox16-action-object-align-vertical-bottom-calligra.svgdark_ox16-action-object-align-vertical-center-calligra.svgdark_ox16-action-object-align-vertical-top-calligra.svgdark_ox16-action-object-order-back-calligra.svgdark_ox16-action-object-order-front-calligra.svgdark_ox16-action-object-order-lower-calligra.svgdark_ox16-action-object-order-raise-calligra.svgdark_ox16-action-object-group-calligra.svgdark_ox16-action-object-ungroup-calligra.svgdark_paintop_settings_01.svgdark_paintop_settings_02.svg
+ dark_pivot-point.svgdark_stroke-cap-butt.svgdark_stroke-cap-round.svgdark_stroke-cap-square.svgdark_stroke-join-bevel.svgdark_stroke-join-miter.svgdark_stroke-join-round.svgdark_symmetry-horizontal.svgdark_symmetry-vertical.svgdark_onionOff.svgdark_onionOn.svgdark_onion_skin_options.svg
diff --git a/krita/pics/misc-light/light_pivot-point.svg b/krita/pics/misc-light/light_pivot-point.svg
new file mode 100644
index 0000000000..e69a09588e
--- /dev/null
+++ b/krita/pics/misc-light/light_pivot-point.svg
@@ -0,0 +1,110 @@
+
+
+
+
diff --git a/krita/pics/misc-light/misc-light-icons.qrc b/krita/pics/misc-light/misc-light-icons.qrc
index 28ec376a65..58f156a5da 100644
--- a/krita/pics/misc-light/misc-light-icons.qrc
+++ b/krita/pics/misc-light/misc-light-icons.qrc
@@ -1,31 +1,32 @@
light_draw-eraser.svglight_ox16-action-object-align-horizontal-center-calligra.svglight_ox16-action-object-align-horizontal-left-calligra.svglight_ox16-action-object-align-horizontal-right-calligra.svglight_ox16-action-object-align-vertical-bottom-calligra.svglight_ox16-action-object-align-vertical-center-calligra.svglight_ox16-action-object-align-vertical-top-calligra.svglight_ox16-action-object-order-back-calligra.svglight_ox16-action-object-order-front-calligra.svglight_ox16-action-object-order-lower-calligra.svglight_ox16-action-object-order-raise-calligra.svglight_ox16-action-object-group-calligra.svglight_ox16-action-object-ungroup-calligra.svglight_paintop_settings_01.svglight_paintop_settings_02.svg
+ light_pivot-point.svglight_stroke-cap-butt.svglight_stroke-cap-round.svglight_stroke-cap-square.svglight_stroke-join-bevel.svglight_stroke-join-miter.svglight_stroke-join-round.svglight_symmetry-horizontal.svglight_symmetry-vertical.svglight_onionOff.svglight_onionOn.svglight_onion_skin_options.svg
diff --git a/krita/pics/tool_transform/dark_transform_icons_cage.png b/krita/pics/tool_transform/dark_transform_icons_cage.png
deleted file mode 100644
index 67dfde90ac..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_cage.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_cage.svg b/krita/pics/tool_transform/dark_transform_icons_cage.svg
index d177e0418b..34d2f4ce3e 100644
--- a/krita/pics/tool_transform/dark_transform_icons_cage.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_cage.svg
@@ -1,107 +1,67 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_erase.png b/krita/pics/tool_transform/dark_transform_icons_liquify_erase.png
deleted file mode 100644
index 8f96447a86..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_liquify_erase.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_erase.svg b/krita/pics/tool_transform/dark_transform_icons_liquify_erase.svg
index 6c37808759..5a6af139a1 100644
--- a/krita/pics/tool_transform/dark_transform_icons_liquify_erase.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_liquify_erase.svg
@@ -1,126 +1,67 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_main.png b/krita/pics/tool_transform/dark_transform_icons_liquify_main.png
deleted file mode 100644
index abe0d75c13..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_liquify_main.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_main.svg b/krita/pics/tool_transform/dark_transform_icons_liquify_main.svg
index 2cdb3f1849..90aaa22e71 100644
--- a/krita/pics/tool_transform/dark_transform_icons_liquify_main.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_liquify_main.svg
@@ -1,115 +1,67 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_move.png b/krita/pics/tool_transform/dark_transform_icons_liquify_move.png
deleted file mode 100644
index 5461629a47..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_liquify_move.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_move.svg b/krita/pics/tool_transform/dark_transform_icons_liquify_move.svg
index fd8b9a49a7..33cf35837c 100644
--- a/krita/pics/tool_transform/dark_transform_icons_liquify_move.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_liquify_move.svg
@@ -1,438 +1,67 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_offset.png b/krita/pics/tool_transform/dark_transform_icons_liquify_offset.png
deleted file mode 100644
index a0d34d6574..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_liquify_offset.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_offset.svg b/krita/pics/tool_transform/dark_transform_icons_liquify_offset.svg
index d902515349..b743613be8 100644
--- a/krita/pics/tool_transform/dark_transform_icons_liquify_offset.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_liquify_offset.svg
@@ -1,223 +1,77 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_resize.png b/krita/pics/tool_transform/dark_transform_icons_liquify_resize.png
deleted file mode 100644
index 7f8e3b759c..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_liquify_resize.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_resize.svg b/krita/pics/tool_transform/dark_transform_icons_liquify_resize.svg
index 0a232438b0..a9415605fc 100644
--- a/krita/pics/tool_transform/dark_transform_icons_liquify_resize.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_liquify_resize.svg
@@ -1,127 +1,82 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_rotate.png b/krita/pics/tool_transform/dark_transform_icons_liquify_rotate.png
deleted file mode 100644
index dd55714d53..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_liquify_rotate.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_rotate.svg b/krita/pics/tool_transform/dark_transform_icons_liquify_rotate.svg
index d14e777b83..c552a0df92 100644
--- a/krita/pics/tool_transform/dark_transform_icons_liquify_rotate.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_liquify_rotate.svg
@@ -1,152 +1,73 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_rotateCCW.png b/krita/pics/tool_transform/dark_transform_icons_liquify_rotateCCW.png
deleted file mode 100644
index 17f17fe04a..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_liquify_rotateCCW.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_liquify_rotateCCW.svg b/krita/pics/tool_transform/dark_transform_icons_liquify_rotateCCW.svg
new file mode 100644
index 0000000000..aadd876e95
--- /dev/null
+++ b/krita/pics/tool_transform/dark_transform_icons_liquify_rotateCCW.svg
@@ -0,0 +1,74 @@
+
+
+
+
diff --git a/krita/pics/tool_transform/dark_transform_icons_main.png b/krita/pics/tool_transform/dark_transform_icons_main.png
deleted file mode 100644
index 473eff13a9..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_main.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_main.svg b/krita/pics/tool_transform/dark_transform_icons_main.svg
index d9149e5f9b..fe271ad1bf 100644
--- a/krita/pics/tool_transform/dark_transform_icons_main.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_main.svg
@@ -1,111 +1,67 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_mirror_x.svg b/krita/pics/tool_transform/dark_transform_icons_mirror_x.svg
index 5c856aef1b..c1ca2ab01f 100644
--- a/krita/pics/tool_transform/dark_transform_icons_mirror_x.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_mirror_x.svg
@@ -1,160 +1,78 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_mirror_y.svg b/krita/pics/tool_transform/dark_transform_icons_mirror_y.svg
index fc91d531f3..7e5b19f1f6 100644
--- a/krita/pics/tool_transform/dark_transform_icons_mirror_y.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_mirror_y.svg
@@ -1,160 +1,78 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_penPressure.png b/krita/pics/tool_transform/dark_transform_icons_penPressure.png
deleted file mode 100644
index e343451822..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_penPressure.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_penPressure.svg b/krita/pics/tool_transform/dark_transform_icons_penPressure.svg
index 373d32c5e9..4458747c2b 100644
--- a/krita/pics/tool_transform/dark_transform_icons_penPressure.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_penPressure.svg
@@ -1,167 +1,74 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.png b/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.png
deleted file mode 100644
index f18f35ffec..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.svg b/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.svg
new file mode 100644
index 0000000000..11d2761ccb
--- /dev/null
+++ b/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.svg
@@ -0,0 +1,79 @@
+
+
+
+
diff --git a/krita/pics/tool_transform/dark_transform_icons_perspective.png b/krita/pics/tool_transform/dark_transform_icons_perspective.png
deleted file mode 100644
index 51601de6cf..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_perspective.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_perspective.svg b/krita/pics/tool_transform/dark_transform_icons_perspective.svg
index 6eab6ef54a..b1123f863b 100644
--- a/krita/pics/tool_transform/dark_transform_icons_perspective.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_perspective.svg
@@ -1,167 +1,74 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_rotate_ccw.svg b/krita/pics/tool_transform/dark_transform_icons_rotate_ccw.svg
index 4404fe36bc..df1c870177 100644
--- a/krita/pics/tool_transform/dark_transform_icons_rotate_ccw.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_rotate_ccw.svg
@@ -1,104 +1,79 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_rotate_cw.svg b/krita/pics/tool_transform/dark_transform_icons_rotate_cw.svg
index 951b763f2b..22df4db22a 100644
--- a/krita/pics/tool_transform/dark_transform_icons_rotate_cw.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_rotate_cw.svg
@@ -1,103 +1,80 @@
diff --git a/krita/pics/tool_transform/dark_transform_icons_warp.png b/krita/pics/tool_transform/dark_transform_icons_warp.png
deleted file mode 100644
index acb924013d..0000000000
Binary files a/krita/pics/tool_transform/dark_transform_icons_warp.png and /dev/null differ
diff --git a/krita/pics/tool_transform/dark_transform_icons_warp.svg b/krita/pics/tool_transform/dark_transform_icons_warp.svg
index 6c0948126c..2319ed5382 100644
--- a/krita/pics/tool_transform/dark_transform_icons_warp.svg
+++ b/krita/pics/tool_transform/dark_transform_icons_warp.svg
@@ -1,180 +1,67 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_cage.png b/krita/pics/tool_transform/light_transform_icons_cage.png
deleted file mode 100644
index e9f118ea74..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_cage.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_cage.svg b/krita/pics/tool_transform/light_transform_icons_cage.svg
index 929e493f05..2d2892eb63 100644
--- a/krita/pics/tool_transform/light_transform_icons_cage.svg
+++ b/krita/pics/tool_transform/light_transform_icons_cage.svg
@@ -1,164 +1,67 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_erase.png b/krita/pics/tool_transform/light_transform_icons_liquify_erase.png
deleted file mode 100644
index c2740512fa..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_liquify_erase.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_erase.svg b/krita/pics/tool_transform/light_transform_icons_liquify_erase.svg
index 28e0e0990d..e1383e9610 100644
--- a/krita/pics/tool_transform/light_transform_icons_liquify_erase.svg
+++ b/krita/pics/tool_transform/light_transform_icons_liquify_erase.svg
@@ -1,219 +1,67 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_main.png b/krita/pics/tool_transform/light_transform_icons_liquify_main.png
deleted file mode 100644
index 8ac8ce7397..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_liquify_main.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_main.svg b/krita/pics/tool_transform/light_transform_icons_liquify_main.svg
index 018972d61b..dc360ed7c5 100644
--- a/krita/pics/tool_transform/light_transform_icons_liquify_main.svg
+++ b/krita/pics/tool_transform/light_transform_icons_liquify_main.svg
@@ -1,231 +1,67 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_move.png b/krita/pics/tool_transform/light_transform_icons_liquify_move.png
deleted file mode 100644
index 151414d474..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_liquify_move.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_move.svg b/krita/pics/tool_transform/light_transform_icons_liquify_move.svg
index 9f545e9c1c..b47e2afaf2 100644
--- a/krita/pics/tool_transform/light_transform_icons_liquify_move.svg
+++ b/krita/pics/tool_transform/light_transform_icons_liquify_move.svg
@@ -1,650 +1,67 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_offset.png b/krita/pics/tool_transform/light_transform_icons_liquify_offset.png
deleted file mode 100644
index 142326ebf0..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_liquify_offset.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_offset.svg b/krita/pics/tool_transform/light_transform_icons_liquify_offset.svg
index 9df4f097b1..394295d5af 100644
--- a/krita/pics/tool_transform/light_transform_icons_liquify_offset.svg
+++ b/krita/pics/tool_transform/light_transform_icons_liquify_offset.svg
@@ -1,373 +1,77 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_resize.png b/krita/pics/tool_transform/light_transform_icons_liquify_resize.png
deleted file mode 100644
index 7dd29e20c8..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_liquify_resize.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_resize.svg b/krita/pics/tool_transform/light_transform_icons_liquify_resize.svg
index e0995fa18b..af376bec4e 100644
--- a/krita/pics/tool_transform/light_transform_icons_liquify_resize.svg
+++ b/krita/pics/tool_transform/light_transform_icons_liquify_resize.svg
@@ -1,263 +1,82 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_rotate.png b/krita/pics/tool_transform/light_transform_icons_liquify_rotate.png
deleted file mode 100644
index a9d4d9af8d..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_liquify_rotate.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_rotate.svg b/krita/pics/tool_transform/light_transform_icons_liquify_rotate.svg
index 5585c0a92a..1be1990e77 100644
--- a/krita/pics/tool_transform/light_transform_icons_liquify_rotate.svg
+++ b/krita/pics/tool_transform/light_transform_icons_liquify_rotate.svg
@@ -1,213 +1,73 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_rotateCCW.png b/krita/pics/tool_transform/light_transform_icons_liquify_rotateCCW.png
deleted file mode 100644
index ad7e71875e..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_liquify_rotateCCW.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_liquify_rotateCCW.svg b/krita/pics/tool_transform/light_transform_icons_liquify_rotateCCW.svg
new file mode 100644
index 0000000000..7be9c55d3c
--- /dev/null
+++ b/krita/pics/tool_transform/light_transform_icons_liquify_rotateCCW.svg
@@ -0,0 +1,74 @@
+
+
+
+
diff --git a/krita/pics/tool_transform/light_transform_icons_main.png b/krita/pics/tool_transform/light_transform_icons_main.png
deleted file mode 100644
index 3223442ef3..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_main.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_main.svg b/krita/pics/tool_transform/light_transform_icons_main.svg
index 4b8ebb6310..a2280f7566 100644
--- a/krita/pics/tool_transform/light_transform_icons_main.svg
+++ b/krita/pics/tool_transform/light_transform_icons_main.svg
@@ -1,194 +1,67 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_mirror_x.svg b/krita/pics/tool_transform/light_transform_icons_mirror_x.svg
index e82757f6dc..0ec27b8cac 100644
--- a/krita/pics/tool_transform/light_transform_icons_mirror_x.svg
+++ b/krita/pics/tool_transform/light_transform_icons_mirror_x.svg
@@ -1,160 +1,78 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_mirror_y.svg b/krita/pics/tool_transform/light_transform_icons_mirror_y.svg
index ff64b6ce80..a2e7c85f10 100644
--- a/krita/pics/tool_transform/light_transform_icons_mirror_y.svg
+++ b/krita/pics/tool_transform/light_transform_icons_mirror_y.svg
@@ -1,164 +1,78 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_penPressure.png b/krita/pics/tool_transform/light_transform_icons_penPressure.png
deleted file mode 100644
index 0a2c6fef94..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_penPressure.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_penPressure.svg b/krita/pics/tool_transform/light_transform_icons_penPressure.svg
index 675a981720..800bcd12af 100644
--- a/krita/pics/tool_transform/light_transform_icons_penPressure.svg
+++ b/krita/pics/tool_transform/light_transform_icons_penPressure.svg
@@ -1,119 +1,74 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_penPressure_locked.png b/krita/pics/tool_transform/light_transform_icons_penPressure_locked.png
deleted file mode 100644
index 14b0a62405..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_penPressure_locked.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_penPressure_locked.svg b/krita/pics/tool_transform/light_transform_icons_penPressure_locked.svg
new file mode 100644
index 0000000000..0ec2a50139
--- /dev/null
+++ b/krita/pics/tool_transform/light_transform_icons_penPressure_locked.svg
@@ -0,0 +1,79 @@
+
+
+
+
diff --git a/krita/pics/tool_transform/light_transform_icons_perspective.png b/krita/pics/tool_transform/light_transform_icons_perspective.png
deleted file mode 100644
index f7770bc49f..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_perspective.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_perspective.svg b/krita/pics/tool_transform/light_transform_icons_perspective.svg
index 7f39210d67..a10df17c9a 100644
--- a/krita/pics/tool_transform/light_transform_icons_perspective.svg
+++ b/krita/pics/tool_transform/light_transform_icons_perspective.svg
@@ -1,323 +1,74 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_rotate_ccw.svg b/krita/pics/tool_transform/light_transform_icons_rotate_ccw.svg
index e024d99f07..4f1e7dba84 100644
--- a/krita/pics/tool_transform/light_transform_icons_rotate_ccw.svg
+++ b/krita/pics/tool_transform/light_transform_icons_rotate_ccw.svg
@@ -1,104 +1,79 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_rotate_cw.svg b/krita/pics/tool_transform/light_transform_icons_rotate_cw.svg
index c28bc63983..d8638e06c9 100644
--- a/krita/pics/tool_transform/light_transform_icons_rotate_cw.svg
+++ b/krita/pics/tool_transform/light_transform_icons_rotate_cw.svg
@@ -1,103 +1,80 @@
diff --git a/krita/pics/tool_transform/light_transform_icons_warp.png b/krita/pics/tool_transform/light_transform_icons_warp.png
deleted file mode 100644
index 7006b17c2f..0000000000
Binary files a/krita/pics/tool_transform/light_transform_icons_warp.png and /dev/null differ
diff --git a/krita/pics/tool_transform/light_transform_icons_warp.svg b/krita/pics/tool_transform/light_transform_icons_warp.svg
index 140b472948..6c3e54350f 100644
--- a/krita/pics/tool_transform/light_transform_icons_warp.svg
+++ b/krita/pics/tool_transform/light_transform_icons_warp.svg
@@ -1,389 +1,67 @@
diff --git a/krita/pics/tool_transform/tool-transform-icons.qrc b/krita/pics/tool_transform/tool-transform-icons.qrc
index 023352cce6..f742da059e 100644
--- a/krita/pics/tool_transform/tool-transform-icons.qrc
+++ b/krita/pics/tool_transform/tool-transform-icons.qrc
@@ -1,40 +1,40 @@
- dark_transform_icons_cage.png
- dark_transform_icons_liquify_erase.png
- dark_transform_icons_liquify_main.png
- dark_transform_icons_liquify_move.png
- dark_transform_icons_liquify_offset.png
- dark_transform_icons_liquify_resize.png
- dark_transform_icons_liquify_rotate.png
- dark_transform_icons_liquify_rotateCCW.png
- dark_transform_icons_main.png
- dark_transform_icons_perspective.png
- dark_transform_icons_warp.png
- dark_transform_icons_penPressure.png
+ dark_transform_icons_cage.svg
+ dark_transform_icons_liquify_erase.svg
+ dark_transform_icons_liquify_main.svg
+ dark_transform_icons_liquify_move.svg
+ dark_transform_icons_liquify_offset.svg
+ dark_transform_icons_liquify_resize.svg
+ dark_transform_icons_liquify_rotate.svg
+ dark_transform_icons_liquify_rotateCCW.svg
+ dark_transform_icons_main.svg
+ dark_transform_icons_perspective.svg
+ dark_transform_icons_warp.svg
+ dark_transform_icons_penPressure.svgdark_krita_tool_transform_recursive.pngdark_transform_icons_mirror_x.svgdark_transform_icons_mirror_y.svgdark_transform_icons_rotate_cw.svgdark_transform_icons_rotate_ccw.svg
- light_transform_icons_cage.png
- light_transform_icons_liquify_erase.png
- light_transform_icons_liquify_main.png
- light_transform_icons_liquify_move.png
- light_transform_icons_liquify_offset.png
- light_transform_icons_liquify_resize.png
- light_transform_icons_liquify_rotate.png
- light_transform_icons_liquify_rotateCCW.png
- light_transform_icons_main.png
- light_transform_icons_perspective.png
- light_transform_icons_warp.png
- light_transform_icons_penPressure.png
+ light_transform_icons_cage.svg
+ light_transform_icons_liquify_erase.svg
+ light_transform_icons_liquify_main.svg
+ light_transform_icons_liquify_move.svg
+ light_transform_icons_liquify_offset.svg
+ light_transform_icons_liquify_resize.svg
+ light_transform_icons_liquify_rotate.svg
+ light_transform_icons_liquify_rotateCCW.svg
+ light_transform_icons_main.svg
+ light_transform_icons_perspective.svg
+ light_transform_icons_warp.svg
+ light_transform_icons_penPressure.svglight_krita_tool_transform_recursive.pnglight_transform_icons_mirror_x.svglight_transform_icons_mirror_y.svglight_transform_icons_rotate_cw.svglight_transform_icons_rotate_ccw.svg
- dark_transform_icons_penPressure_locked.png
- light_transform_icons_penPressure_locked.png
+ dark_transform_icons_penPressure_locked.svg
+ light_transform_icons_penPressure_locked.svg
diff --git a/krita/pics/tool_transform/transform_icons_base.svg b/krita/pics/tool_transform/transform_icons_base.svg
deleted file mode 100644
index 0dccecf7f7..0000000000
--- a/krita/pics/tool_transform/transform_icons_base.svg
+++ /dev/null
@@ -1,557 +0,0 @@
-
-
-
-
diff --git a/krita_xgettext.sh b/kundo2_aware_xgettext.sh
similarity index 67%
rename from krita_xgettext.sh
rename to kundo2_aware_xgettext.sh
index 7b0e3467ef..99cb4cd773 100644
--- a/krita_xgettext.sh
+++ b/kundo2_aware_xgettext.sh
@@ -1,96 +1,113 @@
#
-# Helper function for extracting translatable messages from krita source code.
-# Usage: krita_xgettext
+# Helper function for extracting translatable messages from Calligra/Krita/Kexi source code.
+# Usage: kundo2_aware_xgettext
# If there are no messages or the is empty, the pot file is deleted.
#
# Example usage that creates $podir/myapp.pot file:
-# krita_xgettext myapp.pot `find . -name \*.cpp -o -name \*.h`
+# kundo2_aware_xgettext myapp.pot `find . -name \*.cpp -o -name \*.h`
#
-function krita_xgettext() {
+function kundo2_aware_xgettext() {
POTFILE="$podir/$1"
shift
if test -n "$*"; then
- krita_xgettext_internal $* | tee "${POTFILE}" | tail -n1 | grep "^msgstr \"\"\$" > /dev/null \
+ # we rely on last line being a 'msgstr' signaling that strings has been extracted (a header is always present)
+ # normally it ends with 'msgstr ""' but if plural it can end with eg 'msgstr[1] ""'
+ kundo2_aware_xgettext_internal $* | tee "${POTFILE}" | tail -n1 | grep "^msgstr" > /dev/null \
|| rm -f "${POTFILE}" 2> /dev/null
fi
}
# How to unit test:
# export podir=.
# cp init-sample.pot sample.pot
# source krita_xgettext.sh
# add_ctxt_qtundo sample.pot
#
# Then check that all messages in sample.pot have "(qtundo-format)" in msgctxt.
function add_ctxt_qtundo() {
POT_PART_QUNDOFORMAT="$1"
POT_PART_QUNDOFORMAT2="`mktemp $podir/_qundoformat2_XXXXXXXX.pot`"
# Prepend "(qtundo-format)" to existing msgctxt properties of messages
sed -i -e 's/^msgctxt "/msgctxt "(qtundo-format) /' "${POT_PART_QUNDOFORMAT}"
# Add msgctxt "(qtundo-format)" to messages not having msgctxt yet
#
# lastLine != "#, fuzzy" is the check for the .pot header.
+ # If lastLine starts with '"' the msgctxt has been split on several lines and is treated by sed above, so skip it
mv "${POT_PART_QUNDOFORMAT}" "${POT_PART_QUNDOFORMAT2}"
cat "${POT_PART_QUNDOFORMAT2}" | awk '
/^msgid "/ {
- if (lastLine !~ /^msgctxt/ && lastLine !~ /^"/ && lastLine != "#, fuzzy") {
+ if (lastLine !~ /^\"/ && lastLine !~ /^msgctxt/ && lastLine != "#, fuzzy") {
print "msgctxt \"(qtundo-format)\""
}
}
{ print ; lastLine = $0 }' > "${POT_PART_QUNDOFORMAT}"
rm -f "${POT_PART_QUNDOFORMAT2}"
}
-function krita_xgettext_internal() {
+function kundo2_aware_xgettext_internal() {
SRC_FILES="$*"
POT_PART_NORMAL="`mktemp $podir/_normal_XXXXXXXX.pot`"
POT_PART_QUNDOFORMAT="`mktemp $podir/_qundoformat_XXXXXXXX.pot`"
POT_MERGED="`mktemp $podir/_merged_XXXXXXXX.pot`"
$XGETTEXT ${CXG_EXTRA_ARGS} ${SRC_FILES} -o "${POT_PART_NORMAL}" --force-po
- $XGETTEXT_PROGRAM --from-code=UTF-8 -C --kde -kkundo2_i18n:1 -kkundo2_i18np:1,2 -kkundo2_i18nc:1c,2 -kkundo2_i18ncp:1c,2,3 ${CXG_EXTRA_ARGS} ${SRC_FILES} -o "${POT_PART_QUNDOFORMAT}"
+
+ XGETTEXT_FLAGS_KUNDO2="\
+--copyright-holder=This_file_is_part_of_KDE \
+--msgid-bugs-address=http://bugs.kde.org \
+--from-code=UTF-8
+-C -k --kde \
+-kkundo2_i18n:1 -kkundo2_i18np:1,2 -kkundo2_i18nc:1c,2 -kkundo2_i18ncp:1c,2,3 \
+"
+
+ $XGETTEXT_PROGRAM ${XGETTEXT_FLAGS_KUNDO2} ${CXG_EXTRA_ARGS} ${SRC_FILES} -o "${POT_PART_QUNDOFORMAT}"
if [ $(cat ${POT_PART_NORMAL} ${POT_PART_QUNDOFORMAT} | grep -c \(qtundo-format\)) != 0 ]; then
echo "ERROR: Context '(qtundo-format)' should not be added manually. Use kundo2_i18n*() calls instead." 1>&2
exit 17
fi
if [ -s "${POT_PART_QUNDOFORMAT}" ]; then
add_ctxt_qtundo "${POT_PART_QUNDOFORMAT}"
fi
if [ -s "${POT_PART_NORMAL}" -a -s "${POT_PART_QUNDOFORMAT}" ]; then
+ # ensure an empty line or else KDE_HEADER search will fail
+ # in case POT_PART_NORMAL only contains header
+ echo "" >>${POT_PART_NORMAL}
+
${MSGCAT} -F "${POT_PART_NORMAL}" "${POT_PART_QUNDOFORMAT}" > ${POT_MERGED}
MERGED_HEADER_LINE_COUNT=$(cat ${POT_MERGED} | grep "^$" -B 100000 --max-count=1 | wc -l)
-
KDE_HEADER="$(cat ${POT_PART_NORMAL} | grep "^$" -B 100000 --max-count=1)"
MERGED_TAIL="$(cat ${POT_MERGED} | tail -n +$MERGED_HEADER_LINE_COUNT)"
# Print out the resulting .pot
echo "$KDE_HEADER"
echo "$MERGED_TAIL"
elif [ -s "${POT_PART_NORMAL}" ]; then
+ echo "# POT_PART_NORMAL only"
cat "${POT_PART_NORMAL}"
elif [ -s "${POT_PART_QUNDOFORMAT}" ]; then
+ echo "# POT_PART_QUNDOFORMAT only"
cat "${POT_PART_QUNDOFORMAT}"
fi
rm -f "${POT_PART_NORMAL}" "${POT_PART_QUNDOFORMAT}" "${POT_MERGED}"
}
# Sets EXCLUDE variable to excludes compatible with the find(1) command, e.g. '-path a -o -path b'.
# To unconditionally exclude dir (with subdirs) just put an empty file .i18n in it.
-# To exclude dir for all translations but one, e.g. foo.pot, put a single "foo" line into the .i18n file.
+# To disable excluding for given file, e.g. foo.pot, add "foo.pot" line to the .i18n file.
function find_exclude() {
EXCLUDE=""
for f in `find . -name .i18n | sed 's/\/\.i18n$//g' | sort`; do
if ! grep -q "^${1}$" "$f/.i18n" ; then
if [ -n "$EXCLUDE" ] ; then EXCLUDE="$EXCLUDE -o " ; fi
EXCLUDE="$EXCLUDE -path $f"
fi
done
if [ -z "$EXCLUDE" ] ; then EXCLUDE="-path __dummy__" ; fi # needed because -prune in find needs args
}
diff --git a/libs/image/kis_image.cc b/libs/image/kis_image.cc
index 63e2c79e51..cc422bb081 100644
--- a/libs/image/kis_image.cc
+++ b/libs/image/kis_image.cc
@@ -1,1692 +1,1692 @@
/*
* Copyright (c) 2002 Patrick Julien
* Copyright (c) 2007 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_image.h"
#include // WORDS_BIGENDIAN
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "KoColorSpaceRegistry.h"
#include "KoColor.h"
#include "KoColorProfile.h"
#include
#include "KisProofingConfiguration.h"
#include "recorder/kis_action_recorder.h"
#include "kis_adjustment_layer.h"
#include "kis_annotation.h"
#include "kis_change_profile_visitor.h"
#include "kis_colorspace_convert_visitor.h"
#include "kis_count_visitor.h"
#include "kis_filter_strategy.h"
#include "kis_group_layer.h"
#include "commands/kis_image_commands.h"
#include "kis_layer.h"
#include "kis_meta_data_merge_strategy_registry.h"
#include "kis_name_server.h"
#include "kis_paint_layer.h"
#include "kis_painter.h"
#include "kis_selection.h"
#include "kis_transaction.h"
#include "kis_meta_data_merge_strategy.h"
#include "kis_memory_statistics_server.h"
#include "kis_image_config.h"
#include "kis_update_scheduler.h"
#include "kis_image_signal_router.h"
#include "kis_image_animation_interface.h"
#include "kis_stroke_strategy.h"
#include "kis_image_barrier_locker.h"
#include "kis_undo_stores.h"
#include "kis_legacy_undo_adapter.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_transform_worker.h"
#include "kis_processing_applicator.h"
#include "processing/kis_crop_processing_visitor.h"
#include "processing/kis_crop_selections_processing_visitor.h"
#include "processing/kis_transform_processing_visitor.h"
#include "commands_new/kis_image_resize_command.h"
#include "commands_new/kis_image_set_resolution_command.h"
#include "commands_new/kis_activate_selection_mask_command.h"
#include "kis_composite_progress_proxy.h"
#include "kis_layer_composition.h"
#include "kis_wrapped_rect.h"
#include "kis_crop_saved_extra_data.h"
#include "kis_layer_utils.h"
#include "kis_lod_transform.h"
#include "kis_suspend_projection_updates_stroke_strategy.h"
#include "kis_sync_lod_cache_stroke_strategy.h"
#include "kis_projection_updates_filter.h"
#include "kis_layer_projection_plane.h"
#include "kis_update_time_monitor.h"
#include "kis_image_barrier_locker.h"
#include
#include
#include "kis_time_range.h"
// #define SANITY_CHECKS
#ifdef SANITY_CHECKS
#define SANITY_CHECK_LOCKED(name) \
if (!locked()) warnKrita() << "Locking policy failed:" << name \
<< "has been called without the image" \
"being locked";
#else
#define SANITY_CHECK_LOCKED(name)
#endif
struct KisImageSPStaticRegistrar {
KisImageSPStaticRegistrar() {
qRegisterMetaType("KisImageSP");
}
};
static KisImageSPStaticRegistrar __registrar;
class KisImage::KisImagePrivate
{
public:
KisImagePrivate(KisImage *_q, qint32 w, qint32 h,
const KoColorSpace *c,
KisUndoStore *undo,
KisImageAnimationInterface *_animationInterface)
: q(_q)
, lockedForReadOnly(false)
, width(w)
, height(h)
, colorSpace(c ? c : KoColorSpaceRegistry::instance()->rgb8())
, nserver(1)
, undoStore(undo ? undo : new KisDumbUndoStore())
, legacyUndoAdapter(undoStore.data(), _q)
, postExecutionUndoAdapter(undoStore.data(), _q)
, recorder(_q)
, signalRouter(_q)
, animationInterface(_animationInterface)
, scheduler(_q)
, axesCenter(QPointF(0.5, 0.5))
{
{
KisImageConfig cfg;
if (cfg.enableProgressReporting()) {
scheduler.setProgressProxy(&compositeProgressProxy);
}
// Each of these lambdas defines a new factory function.
scheduler.setLod0ToNStrokeStrategyFactory(
[=](bool forgettable) {
return KisLodSyncPair(
new KisSyncLodCacheStrokeStrategy(KisImageWSP(q), forgettable),
KisSyncLodCacheStrokeStrategy::createJobsData(KisImageWSP(q)));
});
scheduler.setSuspendUpdatesStrokeStrategyFactory(
[=]() {
return KisSuspendResumePair(
new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), true),
KisSuspendProjectionUpdatesStrokeStrategy::createSuspendJobsData(KisImageWSP(q)));
});
scheduler.setResumeUpdatesStrokeStrategyFactory(
[=]() {
return KisSuspendResumePair(
new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), false),
KisSuspendProjectionUpdatesStrokeStrategy::createResumeJobsData(KisImageWSP(q)));
});
}
connect(q, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
}
~KisImagePrivate() {
/**
* Stop animation interface. It may use the rootLayer.
*/
delete animationInterface;
/**
* First delete the nodes, while strokes
* and undo are still alive
*/
rootLayer.clear();
}
KisImage *q;
quint32 lockCount = 0;
bool lockedForReadOnly;
qint32 width;
qint32 height;
double xres = 1.0;
double yres = 1.0;
const KoColorSpace * colorSpace;
KisProofingConfigurationSP proofingConfig;
KisSelectionSP deselectedGlobalSelection;
KisGroupLayerSP rootLayer; // The layers are contained in here
QList compositions;
KisNodeSP isolatedRootNode;
bool wrapAroundModePermitted = false;
KisNameServer nserver;
QScopedPointer undoStore;
KisLegacyUndoAdapter legacyUndoAdapter;
KisPostExecutionUndoAdapter postExecutionUndoAdapter;
KisActionRecorder recorder;
vKisAnnotationSP annotations;
QAtomicInt disableUIUpdateSignals;
KisProjectionUpdatesFilterSP projectionUpdatesFilter;
KisImageSignalRouter signalRouter;
KisImageAnimationInterface *animationInterface;
KisUpdateScheduler scheduler;
QAtomicInt disableDirtyRequests;
KisCompositeProgressProxy compositeProgressProxy;
bool blockLevelOfDetail = false;
QPointF axesCenter;
bool tryCancelCurrentStrokeAsync();
void notifyProjectionUpdatedInPatches(const QRect &rc);
};
KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace * colorSpace, const QString& name)
: QObject(0)
, KisShared()
, m_d(new KisImagePrivate(this, width, height,
colorSpace, undoStore,
new KisImageAnimationInterface(this)))
{
setObjectName(name);
setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
}
KisImage::~KisImage()
{
dbgImage << "deleting kisimage" << objectName();
/**
* Request the tools to end currently running strokes
*/
waitForDone();
delete m_d;
disconnect(); // in case Qt gets confused
}
KisImage *KisImage::clone(bool exactCopy)
{
return new KisImage(*this, 0, exactCopy);
}
KisImage::KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy)
: KisNodeFacade(),
KisNodeGraphListener(),
KisShared(),
m_d(new KisImagePrivate(this,
rhs.width(), rhs.height(),
rhs.colorSpace(),
undoStore ? undoStore : new KisDumbUndoStore(),
new KisImageAnimationInterface(*rhs.animationInterface(), this)))
{
setObjectName(rhs.objectName());
m_d->xres = rhs.m_d->xres;
m_d->yres = rhs.m_d->yres;
if (rhs.m_d->proofingConfig) {
m_d->proofingConfig = toQShared(new KisProofingConfiguration(*rhs.m_d->proofingConfig));
}
KisNodeSP newRoot = rhs.root()->clone();
newRoot->setGraphListener(this);
newRoot->setImage(this);
m_d->rootLayer = dynamic_cast(newRoot.data());
setRoot(newRoot);
if (exactCopy) {
QQueue linearizedNodes;
KisLayerUtils::recursiveApplyNodes(rhs.root(),
[&linearizedNodes](KisNodeSP node) {
linearizedNodes.enqueue(node);
});
KisLayerUtils::recursiveApplyNodes(newRoot,
[&linearizedNodes](KisNodeSP node) {
KisNodeSP refNode = linearizedNodes.dequeue();
node->setUuid(refNode->uuid());
});
}
Q_FOREACH (KisLayerCompositionSP comp, rhs.m_d->compositions) {
m_d->compositions << toQShared(new KisLayerComposition(*comp, this));
}
rhs.m_d->nserver = KisNameServer(rhs.m_d->nserver);
vKisAnnotationSP newAnnotations;
Q_FOREACH (KisAnnotationSP annotation, rhs.m_d->annotations) {
newAnnotations << annotation->clone();
}
m_d->annotations = newAnnotations;
KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->projectionUpdatesFilter);
KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableUIUpdateSignals);
KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableDirtyRequests);
m_d->blockLevelOfDetail = rhs.m_d->blockLevelOfDetail;
}
void KisImage::aboutToAddANode(KisNode *parent, int index)
{
KisNodeGraphListener::aboutToAddANode(parent, index);
SANITY_CHECK_LOCKED("aboutToAddANode");
}
void KisImage::nodeHasBeenAdded(KisNode *parent, int index)
{
KisNodeGraphListener::nodeHasBeenAdded(parent, index);
SANITY_CHECK_LOCKED("nodeHasBeenAdded");
m_d->signalRouter.emitNodeHasBeenAdded(parent, index);
KisNodeSP newNode = parent->at(index);
if (!dynamic_cast(newNode.data())) {
stopIsolatedMode();
}
}
void KisImage::aboutToRemoveANode(KisNode *parent, int index)
{
KisNodeSP deletedNode = parent->at(index);
if (!dynamic_cast(deletedNode.data())) {
stopIsolatedMode();
}
KisNodeGraphListener::aboutToRemoveANode(parent, index);
SANITY_CHECK_LOCKED("aboutToRemoveANode");
m_d->signalRouter.emitAboutToRemoveANode(parent, index);
}
void KisImage::nodeChanged(KisNode* node)
{
KisNodeGraphListener::nodeChanged(node);
requestStrokeEnd();
m_d->signalRouter.emitNodeChanged(node);
}
void KisImage::invalidateAllFrames()
{
invalidateFrames(KisTimeRange::infinite(0), QRect());
}
KisSelectionSP KisImage::globalSelection() const
{
KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
if (selectionMask) {
return selectionMask->selection();
} else {
return 0;
}
}
void KisImage::setGlobalSelection(KisSelectionSP globalSelection)
{
KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
if (!globalSelection) {
if (selectionMask) {
removeNode(selectionMask);
}
}
else {
if (!selectionMask) {
selectionMask = new KisSelectionMask(this);
selectionMask->initSelection(m_d->rootLayer);
addNode(selectionMask);
// If we do not set the selection now, the setActive call coming next
// can be very, very expensive, depending on the size of the image.
selectionMask->setSelection(globalSelection);
selectionMask->setActive(true);
}
else {
selectionMask->setSelection(globalSelection);
}
Q_ASSERT(m_d->rootLayer->childCount() > 0);
Q_ASSERT(m_d->rootLayer->selectionMask());
}
m_d->deselectedGlobalSelection = 0;
m_d->legacyUndoAdapter.emitSelectionChanged();
}
void KisImage::deselectGlobalSelection()
{
KisSelectionSP savedSelection = globalSelection();
setGlobalSelection(0);
m_d->deselectedGlobalSelection = savedSelection;
}
bool KisImage::canReselectGlobalSelection()
{
return m_d->deselectedGlobalSelection;
}
void KisImage::reselectGlobalSelection()
{
if(m_d->deselectedGlobalSelection) {
setGlobalSelection(m_d->deselectedGlobalSelection);
}
}
QString KisImage::nextLayerName(const QString &_baseName) const
{
QString baseName = _baseName;
if (m_d->nserver.currentSeed() == 0) {
m_d->nserver.number();
return i18n("background");
}
if (baseName.isEmpty()) {
baseName = i18n("Layer");
}
return QString("%1 %2").arg(baseName).arg(m_d->nserver.number());
}
void KisImage::rollBackLayerName()
{
m_d->nserver.rollback();
}
KisCompositeProgressProxy* KisImage::compositeProgressProxy()
{
return &m_d->compositeProgressProxy;
}
bool KisImage::locked() const
{
return m_d->lockCount != 0;
}
void KisImage::barrierLock(bool readOnly)
{
if (!locked()) {
requestStrokeEnd();
m_d->scheduler.barrierLock();
m_d->lockedForReadOnly = readOnly;
} else {
m_d->lockedForReadOnly &= readOnly;
}
m_d->lockCount++;
}
bool KisImage::tryBarrierLock(bool readOnly)
{
bool result = true;
if (!locked()) {
result = m_d->scheduler.tryBarrierLock();
m_d->lockedForReadOnly = readOnly;
}
if (result) {
m_d->lockCount++;
m_d->lockedForReadOnly &= readOnly;
}
return result;
}
-bool KisImage::isIdle()
+bool KisImage::isIdle(bool allowLocked)
{
- return !locked() && m_d->scheduler.isIdle();
+ return (allowLocked || !locked()) && m_d->scheduler.isIdle();
}
void KisImage::lock()
{
if (!locked()) {
requestStrokeEnd();
m_d->scheduler.lock();
}
m_d->lockCount++;
m_d->lockedForReadOnly = false;
}
void KisImage::unlock()
{
Q_ASSERT(locked());
if (locked()) {
m_d->lockCount--;
if (m_d->lockCount == 0) {
m_d->scheduler.unlock(!m_d->lockedForReadOnly);
}
}
}
void KisImage::blockUpdates()
{
m_d->scheduler.blockUpdates();
}
void KisImage::unblockUpdates()
{
m_d->scheduler.unblockUpdates();
}
void KisImage::setSize(const QSize& size)
{
m_d->width = size.width();
m_d->height = size.height();
}
void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
{
if (newRect == bounds() && !cropLayers) return;
KUndo2MagicString actionName = cropLayers ?
kundo2_i18n("Crop Image") :
kundo2_i18n("Resize Image");
KisImageSignalVector emitSignals;
emitSignals << ComplexSizeChangedSignal(newRect, newRect.size());
emitSignals << ModifiedSignal;
KisCropSavedExtraData *extraData =
new KisCropSavedExtraData(cropLayers ?
KisCropSavedExtraData::CROP_IMAGE :
KisCropSavedExtraData::RESIZE_IMAGE,
newRect);
KisProcessingApplicator applicator(this, m_d->rootLayer,
KisProcessingApplicator::RECURSIVE |
KisProcessingApplicator::NO_UI_UPDATES,
emitSignals, actionName, extraData);
if (cropLayers || !newRect.topLeft().isNull()) {
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(newRect, cropLayers, true);
applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
}
applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
applicator.end();
}
void KisImage::resizeImage(const QRect& newRect)
{
resizeImageImpl(newRect, false);
}
void KisImage::cropImage(const QRect& newRect)
{
resizeImageImpl(newRect, true);
}
void KisImage::cropNode(KisNodeSP node, const QRect& newRect)
{
bool isLayer = qobject_cast(node.data());
KUndo2MagicString actionName = isLayer ?
kundo2_i18n("Crop Layer") :
kundo2_i18n("Crop Mask");
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisCropSavedExtraData *extraData =
new KisCropSavedExtraData(KisCropSavedExtraData::CROP_LAYER,
newRect, node);
KisProcessingApplicator applicator(this, node,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName, extraData);
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(newRect, true, false);
applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
applicator.end();
}
void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
{
bool resolutionChanged = xres != xRes() && yres != yRes();
bool sizeChanged = size != this->size();
if (!resolutionChanged && !sizeChanged) return;
KisImageSignalVector emitSignals;
if (resolutionChanged) emitSignals << ResolutionChangedSignal;
if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), size);
emitSignals << ModifiedSignal;
KUndo2MagicString actionName = sizeChanged ?
kundo2_i18n("Scale Image") :
kundo2_i18n("Change Image Resolution");
KisProcessingApplicator::ProcessingFlags signalFlags =
(resolutionChanged || sizeChanged) ?
KisProcessingApplicator::NO_UI_UPDATES :
KisProcessingApplicator::NONE;
KisProcessingApplicator applicator(this, m_d->rootLayer,
KisProcessingApplicator::RECURSIVE | signalFlags,
emitSignals, actionName);
qreal sx = qreal(size.width()) / this->size().width();
qreal sy = qreal(size.height()) / this->size().height();
QTransform shapesCorrection;
if (resolutionChanged) {
shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres);
}
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(sx, sy,
0, 0,
QPointF(),
0,
0, 0,
filterStrategy,
shapesCorrection);
applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
if (resolutionChanged) {
KUndo2Command *parent =
new KisResetShapesCommand(m_d->rootLayer);
new KisImageSetResolutionCommand(this, xres, yres, parent);
applicator.applyCommand(parent);
}
if (sizeChanged) {
applicator.applyCommand(new KisImageResizeCommand(this, size));
}
applicator.end();
}
void KisImage::scaleNode(KisNodeSP node, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy)
{
KUndo2MagicString actionName(kundo2_i18n("Scale Layer"));
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(this, node,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName);
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(scaleX, scaleY,
0, 0,
QPointF(),
0,
0, 0,
filterStrategy);
applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
applicator.end();
}
void KisImage::rotateImpl(const KUndo2MagicString &actionName,
KisNodeSP rootNode,
bool resizeImage,
double radians)
{
QPointF offset;
QSize newSize;
{
KisTransformWorker worker(0,
1.0, 1.0,
0, 0, 0, 0,
radians,
0, 0, 0, 0);
QTransform transform = worker.transform();
if (resizeImage) {
QRect newRect = transform.mapRect(bounds());
newSize = newRect.size();
offset = -newRect.topLeft();
}
else {
QPointF origin = QRectF(rootNode->exactBounds()).center();
newSize = size();
offset = -(transform.map(origin) - origin);
}
}
bool sizeChanged = resizeImage &&
(newSize.width() != width() || newSize.height() != height());
// These signals will be emitted after processing is done
KisImageSignalVector emitSignals;
if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), newSize);
emitSignals << ModifiedSignal;
// These flags determine whether updates are transferred to the UI during processing
KisProcessingApplicator::ProcessingFlags signalFlags =
sizeChanged ?
KisProcessingApplicator::NO_UI_UPDATES :
KisProcessingApplicator::NONE;
KisProcessingApplicator applicator(this, rootNode,
KisProcessingApplicator::RECURSIVE | signalFlags,
emitSignals, actionName);
KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bicubic");
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
QPointF(),
radians,
offset.x(), offset.y(),
filter);
applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
if (sizeChanged) {
applicator.applyCommand(new KisImageResizeCommand(this, newSize));
}
applicator.end();
}
void KisImage::rotateImage(double radians)
{
rotateImpl(kundo2_i18n("Rotate Image"), root(), true, radians);
}
void KisImage::rotateNode(KisNodeSP node, double radians)
{
rotateImpl(kundo2_i18n("Rotate Layer"), node, false, radians);
}
void KisImage::shearImpl(const KUndo2MagicString &actionName,
KisNodeSP rootNode,
bool resizeImage,
double angleX, double angleY,
const QPointF &origin)
{
//angleX, angleY are in degrees
const qreal pi = 3.1415926535897932385;
const qreal deg2rad = pi / 180.0;
qreal tanX = tan(angleX * deg2rad);
qreal tanY = tan(angleY * deg2rad);
QPointF offset;
QSize newSize;
{
KisTransformWorker worker(0,
1.0, 1.0,
tanX, tanY, origin.x(), origin.y(),
0,
0, 0, 0, 0);
QRect newRect = worker.transform().mapRect(bounds());
newSize = newRect.size();
if (resizeImage) offset = -newRect.topLeft();
}
if (newSize == size()) return;
KisImageSignalVector emitSignals;
if (resizeImage) emitSignals << ComplexSizeChangedSignal(bounds(), newSize);
emitSignals << ModifiedSignal;
KisProcessingApplicator::ProcessingFlags signalFlags =
KisProcessingApplicator::RECURSIVE;
if (resizeImage) signalFlags |= KisProcessingApplicator::NO_UI_UPDATES;
KisProcessingApplicator applicator(this, rootNode,
signalFlags,
emitSignals, actionName);
KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bilinear");
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(1.0, 1.0,
tanX, tanY, origin,
0,
offset.x(), offset.y(),
filter);
applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
if (resizeImage) {
applicator.applyCommand(new KisImageResizeCommand(this, newSize));
}
applicator.end();
}
void KisImage::shearNode(KisNodeSP node, double angleX, double angleY)
{
QPointF shearOrigin = QRectF(bounds()).center();
shearImpl(kundo2_i18n("Shear layer"), node, false,
angleX, angleY, shearOrigin);
}
void KisImage::shear(double angleX, double angleY)
{
shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true,
angleX, angleY, QPointF());
}
void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags)
{
if (!dstColorSpace) return;
const KoColorSpace *srcColorSpace = m_d->colorSpace;
undoAdapter()->beginMacro(kundo2_i18n("Convert Image Color Space"));
undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true));
undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace));
KisColorSpaceConvertVisitor visitor(this, srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
m_d->rootLayer->accept(visitor);
undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false));
undoAdapter()->endMacro();
setModified();
}
bool KisImage::assignImageProfile(const KoColorProfile *profile)
{
if (!profile) return false;
const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
const KoColorSpace *srcCs = colorSpace();
if (!dstCs) return false;
m_d->colorSpace = dstCs;
KisChangeProfileVisitor visitor(srcCs, dstCs);
return m_d->rootLayer->accept(visitor);
}
void KisImage::convertProjectionColorSpace(const KoColorSpace *dstColorSpace)
{
if (*m_d->colorSpace == *dstColorSpace) return;
undoAdapter()->beginMacro(kundo2_i18n("Convert Projection Color Space"));
undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true));
undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace));
undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false));
undoAdapter()->endMacro();
setModified();
}
void KisImage::setProjectionColorSpace(const KoColorSpace * colorSpace)
{
m_d->colorSpace = colorSpace;
m_d->rootLayer->resetCache();
m_d->signalRouter.emitNotification(ColorSpaceChangedSignal);
}
const KoColorSpace * KisImage::colorSpace() const
{
return m_d->colorSpace;
}
const KoColorProfile * KisImage::profile() const
{
return colorSpace()->profile();
}
double KisImage::xRes() const
{
return m_d->xres;
}
double KisImage::yRes() const
{
return m_d->yres;
}
void KisImage::setResolution(double xres, double yres)
{
m_d->xres = xres;
m_d->yres = yres;
m_d->signalRouter.emitNotification(ResolutionChangedSignal);
}
QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
{
return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes());
}
QPoint KisImage::documentToIntPixel(const QPointF &documentCoord) const
{
QPointF pixelCoord = documentToPixel(documentCoord);
return QPoint((int)pixelCoord.x(), (int)pixelCoord.y());
}
QRectF KisImage::documentToPixel(const QRectF &documentRect) const
{
return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight()));
}
QRect KisImage::documentToIntPixel(const QRectF &documentRect) const
{
return documentToPixel(documentRect).toAlignedRect();
}
QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const
{
return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes());
}
QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const
{
return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes());
}
QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
{
return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
}
qint32 KisImage::width() const
{
return m_d->width;
}
qint32 KisImage::height() const
{
return m_d->height;
}
KisGroupLayerSP KisImage::rootLayer() const
{
Q_ASSERT(m_d->rootLayer);
return m_d->rootLayer;
}
KisPaintDeviceSP KisImage::projection() const
{
if (m_d->isolatedRootNode) {
return m_d->isolatedRootNode->projection();
}
Q_ASSERT(m_d->rootLayer);
KisPaintDeviceSP projection = m_d->rootLayer->projection();
Q_ASSERT(projection);
return projection;
}
qint32 KisImage::nlayers() const
{
QStringList list;
list << "KisLayer";
KisCountVisitor visitor(list, KoProperties());
m_d->rootLayer->accept(visitor);
return visitor.count();
}
qint32 KisImage::nHiddenLayers() const
{
QStringList list;
list << "KisLayer";
KoProperties properties;
properties.setProperty("visible", false);
KisCountVisitor visitor(list, properties);
m_d->rootLayer->accept(visitor);
return visitor.count();
}
void KisImage::flatten()
{
KisLayerUtils::flattenImage(this);
}
void KisImage::mergeMultipleLayers(QList mergedNodes, KisNodeSP putAfter)
{
if (!KisLayerUtils::tryMergeSelectionMasks(this, mergedNodes, putAfter)) {
KisLayerUtils::mergeMultipleLayers(this, mergedNodes, putAfter);
}
}
void KisImage::mergeDown(KisLayerSP layer, const KisMetaData::MergeStrategy* strategy)
{
KisLayerUtils::mergeDown(this, layer, strategy);
}
void KisImage::flattenLayer(KisLayerSP layer)
{
KisLayerUtils::flattenLayer(this, layer);
}
void KisImage::setModified()
{
m_d->signalRouter.emitNotification(ModifiedSignal);
}
QImage KisImage::convertToQImage(QRect imageRect,
const KoColorProfile * profile)
{
qint32 x;
qint32 y;
qint32 w;
qint32 h;
imageRect.getRect(&x, &y, &w, &h);
return convertToQImage(x, y, w, h, profile);
}
QImage KisImage::convertToQImage(qint32 x,
qint32 y,
qint32 w,
qint32 h,
const KoColorProfile * profile)
{
KisPaintDeviceSP dev = projection();
if (!dev) return QImage();
QImage image = dev->convertToQImage(const_cast(profile), x, y, w, h,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
return image;
}
QImage KisImage::convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile)
{
if (scaledImageSize.isEmpty()) {
return QImage();
}
KisPaintDeviceSP dev = new KisPaintDevice(colorSpace());
KisPainter gc;
gc.copyAreaOptimized(QPoint(0, 0), projection(), dev, bounds());
gc.end();
double scaleX = qreal(scaledImageSize.width()) / width();
double scaleY = qreal(scaledImageSize.height()) / height();
QPointer updater = new KoDummyUpdater();
KisTransformWorker worker(dev, scaleX, scaleY, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, updater, KisFilterStrategyRegistry::instance()->value("Bicubic"));
worker.run();
delete updater;
return dev->convertToQImage(profile);
}
void KisImage::notifyLayersChanged()
{
m_d->signalRouter.emitNotification(LayersChangedSignal);
}
QRect KisImage::bounds() const
{
return QRect(0, 0, width(), height());
}
QRect KisImage::effectiveLodBounds() const
{
QRect boundRect = bounds();
const int lod = currentLevelOfDetail();
if (lod > 0) {
KisLodTransform t(lod);
boundRect = t.map(boundRect);
}
return boundRect;
}
KisPostExecutionUndoAdapter* KisImage::postExecutionUndoAdapter() const
{
const int lod = currentLevelOfDetail();
return lod > 0 ?
m_d->scheduler.lodNPostExecutionUndoAdapter() :
&m_d->postExecutionUndoAdapter;
}
void KisImage::setUndoStore(KisUndoStore *undoStore)
{
m_d->legacyUndoAdapter.setUndoStore(undoStore);
m_d->postExecutionUndoAdapter.setUndoStore(undoStore);
m_d->undoStore.reset(undoStore);
}
KisUndoStore* KisImage::undoStore()
{
return m_d->undoStore.data();
}
KisUndoAdapter* KisImage::undoAdapter() const
{
return &m_d->legacyUndoAdapter;
}
KisActionRecorder* KisImage::actionRecorder() const
{
return &m_d->recorder;
}
void KisImage::setDefaultProjectionColor(const KoColor &color)
{
KIS_ASSERT_RECOVER_RETURN(m_d->rootLayer);
m_d->rootLayer->setDefaultProjectionColor(color);
}
KoColor KisImage::defaultProjectionColor() const
{
KIS_ASSERT_RECOVER(m_d->rootLayer) {
return KoColor(Qt::transparent, m_d->colorSpace);
}
return m_d->rootLayer->defaultProjectionColor();
}
void KisImage::setRootLayer(KisGroupLayerSP rootLayer)
{
stopIsolatedMode();
KoColor defaultProjectionColor(Qt::transparent, m_d->colorSpace);
if (m_d->rootLayer) {
m_d->rootLayer->setGraphListener(0);
m_d->rootLayer->disconnect();
KisPaintDeviceSP original = m_d->rootLayer->original();
defaultProjectionColor = original->defaultPixel();
}
m_d->rootLayer = rootLayer;
m_d->rootLayer->disconnect();
m_d->rootLayer->setGraphListener(this);
m_d->rootLayer->setImage(this);
KisPaintDeviceSP newOriginal = m_d->rootLayer->original();
newOriginal->setDefaultPixel(defaultProjectionColor);
setRoot(m_d->rootLayer.data());
}
void KisImage::addAnnotation(KisAnnotationSP annotation)
{
// Find the icc annotation, if there is one
vKisAnnotationSP_it it = m_d->annotations.begin();
while (it != m_d->annotations.end()) {
if ((*it)->type() == annotation->type()) {
*it = annotation;
return;
}
++it;
}
m_d->annotations.push_back(annotation);
}
KisAnnotationSP KisImage::annotation(const QString& type)
{
vKisAnnotationSP_it it = m_d->annotations.begin();
while (it != m_d->annotations.end()) {
if ((*it)->type() == type) {
return *it;
}
++it;
}
return KisAnnotationSP(0);
}
void KisImage::removeAnnotation(const QString& type)
{
vKisAnnotationSP_it it = m_d->annotations.begin();
while (it != m_d->annotations.end()) {
if ((*it)->type() == type) {
m_d->annotations.erase(it);
return;
}
++it;
}
}
vKisAnnotationSP_it KisImage::beginAnnotations()
{
return m_d->annotations.begin();
}
vKisAnnotationSP_it KisImage::endAnnotations()
{
return m_d->annotations.end();
}
void KisImage::notifyAboutToBeDeleted()
{
emit sigAboutToBeDeleted();
}
KisImageSignalRouter* KisImage::signalRouter()
{
return &m_d->signalRouter;
}
void KisImage::waitForDone()
{
requestStrokeEnd();
m_d->scheduler.waitForDone();
}
KisStrokeId KisImage::startStroke(KisStrokeStrategy *strokeStrategy)
{
/**
* Ask open strokes to end gracefully. All the strokes clients
* (including the one calling this method right now) will get
* a notification that they should probably end their strokes.
* However this is purely their choice whether to end a stroke
* or not.
*/
if (strokeStrategy->requestsOtherStrokesToEnd()) {
requestStrokeEnd();
}
/**
* Some of the strokes can cancel their work with undoing all the
* changes they did to the paint devices. The problem is that undo
* stack will know nothing about it. Therefore, just notify it
* explicitly
*/
if (strokeStrategy->clearsRedoOnStart()) {
m_d->undoStore->purgeRedoState();
}
return m_d->scheduler.startStroke(strokeStrategy);
}
void KisImage::KisImagePrivate::notifyProjectionUpdatedInPatches(const QRect &rc)
{
KisImageConfig imageConfig;
int patchWidth = imageConfig.updatePatchWidth();
int patchHeight = imageConfig.updatePatchHeight();
for (int y = 0; y < rc.height(); y += patchHeight) {
for (int x = 0; x < rc.width(); x += patchWidth) {
QRect patchRect(x, y, patchWidth, patchHeight);
patchRect &= rc;
QtConcurrent::run(std::bind(&KisImage::notifyProjectionUpdated, q, patchRect));
}
}
}
bool KisImage::startIsolatedMode(KisNodeSP node)
{
if (!tryBarrierLock()) return false;
unlock();
m_d->isolatedRootNode = node;
emit sigIsolatedModeChanged();
// the GUI uses our thread to do the color space conversion so we
// need to emit this signal in multiple threads
m_d->notifyProjectionUpdatedInPatches(bounds());
invalidateAllFrames();
return true;
}
void KisImage::stopIsolatedMode()
{
if (!m_d->isolatedRootNode) return;
KisNodeSP oldRootNode = m_d->isolatedRootNode;
m_d->isolatedRootNode = 0;
emit sigIsolatedModeChanged();
// the GUI uses our thread to do the color space conversion so we
// need to emit this signal in multiple threads
m_d->notifyProjectionUpdatedInPatches(bounds());
invalidateAllFrames();
// TODO: Substitute notifyProjectionUpdated() with this code
// when update optimization is implemented
//
// QRect updateRect = bounds() | oldRootNode->extent();
// oldRootNode->setDirty(updateRect);
}
KisNodeSP KisImage::isolatedModeRoot() const
{
return m_d->isolatedRootNode;
}
void KisImage::addJob(KisStrokeId id, KisStrokeJobData *data)
{
KisUpdateTimeMonitor::instance()->reportJobStarted(data);
m_d->scheduler.addJob(id, data);
}
void KisImage::endStroke(KisStrokeId id)
{
m_d->scheduler.endStroke(id);
}
bool KisImage::cancelStroke(KisStrokeId id)
{
return m_d->scheduler.cancelStroke(id);
}
bool KisImage::KisImagePrivate::tryCancelCurrentStrokeAsync()
{
return scheduler.tryCancelCurrentStrokeAsync();
}
void KisImage::requestUndoDuringStroke()
{
emit sigUndoDuringStrokeRequested();
}
void KisImage::requestStrokeCancellation()
{
if (!m_d->tryCancelCurrentStrokeAsync()) {
emit sigStrokeCancellationRequested();
}
}
UndoResult KisImage::tryUndoUnfinishedLod0Stroke()
{
return m_d->scheduler.tryUndoLastStrokeAsync();
}
void KisImage::requestStrokeEnd()
{
emit sigStrokeEndRequested();
emit sigStrokeEndRequestedActiveNodeFiltered();
}
void KisImage::requestStrokeEndActiveNode()
{
emit sigStrokeEndRequested();
}
void KisImage::refreshGraph(KisNodeSP root)
{
refreshGraph(root, bounds(), bounds());
}
void KisImage::refreshGraph(KisNodeSP root, const QRect &rc, const QRect &cropRect)
{
if (!root) root = m_d->rootLayer;
m_d->animationInterface->notifyNodeChanged(root.data(), rc, true);
m_d->scheduler.fullRefresh(root, rc, cropRect);
}
void KisImage::initialRefreshGraph()
{
/**
* NOTE: Tricky part. We set crop rect to null, so the clones
* will not rely on precalculated projections of their sources
*/
refreshGraphAsync(0, bounds(), QRect());
waitForDone();
}
void KisImage::refreshGraphAsync(KisNodeSP root)
{
refreshGraphAsync(root, bounds(), bounds());
}
void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc)
{
refreshGraphAsync(root, rc, bounds());
}
void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect)
{
if (!root) root = m_d->rootLayer;
m_d->animationInterface->notifyNodeChanged(root.data(), rc, true);
m_d->scheduler.fullRefreshAsync(root, rc, cropRect);
}
void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect)
{
KIS_ASSERT_RECOVER_RETURN(pseudoFilthy);
m_d->animationInterface->notifyNodeChanged(pseudoFilthy.data(), rc, false);
m_d->scheduler.updateProjectionNoFilthy(pseudoFilthy, rc, cropRect);
}
void KisImage::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
{
m_d->scheduler.addSpontaneousJob(spontaneousJob);
}
void KisImage::setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter)
{
// udpate filters are *not* recursive!
KIS_ASSERT_RECOVER_NOOP(!filter || !m_d->projectionUpdatesFilter);
m_d->projectionUpdatesFilter = filter;
}
KisProjectionUpdatesFilterSP KisImage::projectionUpdatesFilter() const
{
return m_d->projectionUpdatesFilter;
}
void KisImage::disableDirtyRequests()
{
setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP(new KisDropAllProjectionUpdatesFilter()));
}
void KisImage::enableDirtyRequests()
{
setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP());
}
void KisImage::disableUIUpdates()
{
m_d->disableUIUpdateSignals.ref();
}
void KisImage::enableUIUpdates()
{
m_d->disableUIUpdateSignals.deref();
}
void KisImage::notifyProjectionUpdated(const QRect &rc)
{
KisUpdateTimeMonitor::instance()->reportUpdateFinished(rc);
if (!m_d->disableUIUpdateSignals) {
int lod = currentLevelOfDetail();
QRect dirtyRect = !lod ? rc : KisLodTransform::upscaledRect(rc, lod);
if (dirtyRect.isEmpty()) return;
emit sigImageUpdated(dirtyRect);
}
}
void KisImage::notifySelectionChanged()
{
/**
* The selection is calculated asynchromously, so it is not
* handled by disableUIUpdates() and other special signals of
* KisImageSignalRouter
*/
m_d->legacyUndoAdapter.emitSelectionChanged();
/**
* Editing of selection masks doesn't necessary produce a
* setDirty() call, so in the end of the stroke we need to request
* direct update of the UI's cache.
*/
if (m_d->isolatedRootNode &&
dynamic_cast(m_d->isolatedRootNode.data())) {
notifyProjectionUpdated(bounds());
}
}
void KisImage::requestProjectionUpdateImpl(KisNode *node,
const QRect &rect,
const QRect &cropRect)
{
if (rect.isEmpty()) return;
KisNodeGraphListener::requestProjectionUpdate(node, rect);
m_d->scheduler.updateProjection(node, rect, cropRect);
}
void KisImage::requestProjectionUpdate(KisNode *node, const QRect& rect)
{
if (m_d->projectionUpdatesFilter
&& m_d->projectionUpdatesFilter->filter(this, node, rect)) {
return;
}
m_d->animationInterface->notifyNodeChanged(node, rect, false);
/**
* Here we use 'permitted' instead of 'active' intentively,
* because the updates may come after the actual stroke has been
* finished. And having some more updates for the stroke not
* supporting the wrap-around mode will not make much harm.
*/
if (m_d->wrapAroundModePermitted) {
const QRect boundRect = effectiveLodBounds();
KisWrappedRect splitRect(rect, boundRect);
Q_FOREACH (const QRect &rc, splitRect) {
requestProjectionUpdateImpl(node, rc, boundRect);
}
} else {
requestProjectionUpdateImpl(node, rect, bounds());
}
}
void KisImage::invalidateFrames(const KisTimeRange &range, const QRect &rect)
{
m_d->animationInterface->invalidateFrames(range, rect);
}
void KisImage::requestTimeSwitch(int time)
{
m_d->animationInterface->requestTimeSwitchNonGUI(time);
}
QList KisImage::compositions()
{
return m_d->compositions;
}
void KisImage::addComposition(KisLayerCompositionSP composition)
{
m_d->compositions.append(composition);
}
void KisImage::removeComposition(KisLayerCompositionSP composition)
{
m_d->compositions.removeAll(composition);
}
bool checkMasksNeedConversion(KisNodeSP root, const QRect &bounds)
{
KisSelectionMask *mask = dynamic_cast(root.data());
if (mask &&
(!bounds.contains(mask->paintDevice()->exactBounds()) ||
mask->selection()->hasShapeSelection())) {
return true;
}
KisNodeSP node = root->firstChild();
while (node) {
if (checkMasksNeedConversion(node, bounds)) {
return true;
}
node = node->nextSibling();
}
return false;
}
void KisImage::setWrapAroundModePermitted(bool value)
{
m_d->wrapAroundModePermitted = value;
if (m_d->wrapAroundModePermitted &&
checkMasksNeedConversion(root(), bounds())) {
KisProcessingApplicator applicator(this, root(),
KisProcessingApplicator::RECURSIVE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Crop Selections"));
KisProcessingVisitorSP visitor =
new KisCropSelectionsProcessingVisitor(bounds());
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
applicator.end();
}
}
bool KisImage::wrapAroundModePermitted() const
{
return m_d->wrapAroundModePermitted;
}
bool KisImage::wrapAroundModeActive() const
{
return m_d->wrapAroundModePermitted &&
m_d->scheduler.wrapAroundModeSupported();
}
void KisImage::setDesiredLevelOfDetail(int lod)
{
if (m_d->blockLevelOfDetail) {
qWarning() << "WARNING: KisImage::setDesiredLevelOfDetail()"
<< "was called while LoD functionality was being blocked!";
return;
}
m_d->scheduler.setDesiredLevelOfDetail(lod);
}
int KisImage::currentLevelOfDetail() const
{
if (m_d->blockLevelOfDetail) {
return 0;
}
return m_d->scheduler.currentLevelOfDetail();
}
void KisImage::setLevelOfDetailBlocked(bool value)
{
KisImageBarrierLockerRaw l(this);
if (value && !m_d->blockLevelOfDetail) {
m_d->scheduler.setDesiredLevelOfDetail(0);
}
m_d->blockLevelOfDetail = value;
}
void KisImage::explicitRegenerateLevelOfDetail()
{
if (!m_d->blockLevelOfDetail) {
m_d->scheduler.explicitRegenerateLevelOfDetail();
}
}
bool KisImage::levelOfDetailBlocked() const
{
return m_d->blockLevelOfDetail;
}
void KisImage::notifyNodeCollpasedChanged()
{
emit sigNodeCollapsedChanged();
}
KisImageAnimationInterface* KisImage::animationInterface() const
{
return m_d->animationInterface;
}
void KisImage::setProofingConfiguration(KisProofingConfigurationSP proofingConfig)
{
m_d->proofingConfig = proofingConfig;
emit sigProofingConfigChanged();
}
KisProofingConfigurationSP KisImage::proofingConfiguration() const
{
if (!m_d->proofingConfig) {
KisImageConfig cfg;
m_d->proofingConfig = cfg.defaultProofingconfiguration();
}
return m_d->proofingConfig;
}
QPointF KisImage::mirrorAxesCenter() const
{
return m_d->axesCenter;
}
void KisImage::setMirrorAxesCenter(const QPointF &value) const
{
m_d->axesCenter = value;
}
diff --git a/libs/image/kis_image.h b/libs/image/kis_image.h
index d420171435..a34ccf6054 100644
--- a/libs/image/kis_image.h
+++ b/libs/image/kis_image.h
@@ -1,984 +1,984 @@
/*
* Copyright (c) 2002 Patrick Julien
* Copyright (c) 2007 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_IMAGE_H_
#define KIS_IMAGE_H_
#include
#include
#include
#include
#include
#include
#include
#include "kis_paint_device.h" // msvc cannot handle forward declarations, so include kis_paint_device here
#include "kis_types.h"
#include "kis_shared.h"
#include "kis_node_graph_listener.h"
#include "kis_node_facade.h"
#include "kis_image_interfaces.h"
#include "kis_strokes_queue_undo_result.h"
#include
class KisDocument;
class KoColorSpace;
class KoColor;
class KisCompositeProgressProxy;
class KisActionRecorder;
class KisUndoStore;
class KisUndoAdapter;
class KisImageSignalRouter;
class KisPostExecutionUndoAdapter;
class KisFilterStrategy;
class KoColorProfile;
class KisLayerComposition;
class KisSpontaneousJob;
class KisImageAnimationInterface;
class KUndo2MagicString;
class KisProofingConfiguration;
namespace KisMetaData
{
class MergeStrategy;
}
/**
* This is the image class, it contains a tree of KisLayer stack and
* meta information about the image. And it also provides some
* functions to manipulate the whole image.
*/
class KRITAIMAGE_EXPORT KisImage : public QObject,
public KisStrokesFacade,
public KisStrokeUndoFacade,
public KisUpdatesFacade,
public KisProjectionUpdateListener,
public KisNodeFacade,
public KisNodeGraphListener,
public KisShared
{
Q_OBJECT
public:
/// @param colorSpace can be null. in that case it will be initialised to a default color space.
KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString& name);
virtual ~KisImage();
public: // KisNodeGraphListener implementation
void aboutToAddANode(KisNode *parent, int index);
void nodeHasBeenAdded(KisNode *parent, int index);
void aboutToRemoveANode(KisNode *parent, int index);
void nodeChanged(KisNode * node);
void invalidateAllFrames();
void notifySelectionChanged();
void requestProjectionUpdate(KisNode *node, const QRect& rect);
void invalidateFrames(const KisTimeRange &range, const QRect &rect);
void requestTimeSwitch(int time);
public: // KisProjectionUpdateListener implementation
void notifyProjectionUpdated(const QRect &rc);
public:
/**
* Makes a copy of the image with all the layers. If possible, shallow
* copies of the layers are made.
*
* \p exactCopy shows if the copied image should look *exactly* the same as
* the other one (according to it's .kra xml representation). It means that
* the layers will have the same UUID keys and, therefore, you are not
* expected to use the copied image anywhere except for saving. Don't use
* this option if you plan to work with the copied image later.
*/
KisImage* clone(bool exactCopy = false);
/**
* Render the projection onto a QImage.
*/
QImage convertToQImage(qint32 x1,
qint32 y1,
qint32 width,
qint32 height,
const KoColorProfile * profile);
/**
* Render the projection onto a QImage.
* (this is an overloaded function)
*/
QImage convertToQImage(QRect imageRect,
const KoColorProfile * profile);
/**
* XXX: docs!
*/
QImage convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile);
/**
* Calls KisUpdateScheduler::lock (XXX: APIDOX -- what does that mean?)
*/
void lock();
/**
* Calls KisUpdateScheduler::unlock (XXX: APIDOX -- what does that mean?)
*/
void unlock();
/**
* Returns true if lock() has been called more often than unlock().
*/
bool locked() const;
/**
* @return the global selection object or 0 if there is none. The
* global selection is always read-write.
*/
KisSelectionSP globalSelection() const;
/**
* Retrieve the next automatic layername (XXX: fix to add option to return Mask X)
*/
QString nextLayerName(const QString &baseName = "") const;
/**
* Set the automatic layer name counter one back.
*/
void rollBackLayerName();
/**
* Resize the image to the specified rect. The resize
* method handles the creating on an undo step itself.
*
* @param newRect the rect describing the new width, height and offset
* of the image
*/
void resizeImage(const QRect& newRect);
/**
* Crop the image to the specified rect. The crop
* method handles the creating on an undo step itself.
*
* @param newRect the rect describing the new width, height and offset
* of the image
*/
void cropImage(const QRect& newRect);
/**
* Crop a node to @newRect. The node will *not* be moved anywhere,
* it just drops some content
*/
void cropNode(KisNodeSP node, const QRect& newRect);
/// XXX: ApiDox
void scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy);
/// XXX: ApiDox
void scaleNode(KisNodeSP node, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy);
/**
* Execute a rotate transform on all layers in this image.
* Image is resized to fit rotated image.
*/
void rotateImage(double radians);
/**
* Execute a rotate transform on on a subtree of this image.
* Image is not resized.
*/
void rotateNode(KisNodeSP node, double radians);
/**
* Execute a shear transform on all layers in this image.
*/
void shear(double angleX, double angleY);
/**
* Shear a node and all its children.
* @param angleX, @param angleY are given in degrees.
*/
void shearNode(KisNodeSP node, double angleX, double angleY);
/**
* Convert the image and all its layers to the dstColorSpace
*/
void convertImageColorSpace(const KoColorSpace *dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
/**
* Set the color space of the projection (and the root layer)
* to dstColorSpace. No conversion is done for other layers,
* their colorspace can differ.
* NOTE: Note conversion is done, only regeneration, so no rendering
* intent needed
*/
void convertProjectionColorSpace(const KoColorSpace *dstColorSpace);
// Get the profile associated with this image
const KoColorProfile * profile() const;
/**
* Set the profile of the image to the new profile and do the same for
* all layers that have the same colorspace and profile of the image.
* It doesn't do any pixel conversion.
*
* This is essential if you have loaded an image that didn't
* have an embedded profile to which you want to attach the right profile.
*
* This does not create an undo action; only call it when creating or
* loading an image.
*
* @returns false if the profile could not be assigned
*/
bool assignImageProfile(const KoColorProfile *profile);
/**
* Returns the current undo adapter. You can add new commands to the
* undo stack using the adapter. This adapter is used for a backward
* compatibility for old commands created before strokes. It blocks
* all the porcessing at the scheduler, waits until it's finished
* adn executes commands exclusively.
*/
KisUndoAdapter* undoAdapter() const;
/**
* This adapter is used by the strokes system. The commands are added
* to it *after* redo() is done (in the scheduler context). They are
* wrapped into a special command and added to the undo stack. redo()
* in not called.
*/
KisPostExecutionUndoAdapter* postExecutionUndoAdapter() const;
/**
* Replace current undo store with the new one. The old store
* will be deleted.
* This method is used by KisDocument for dropping all the commands
* during file loading.
*/
void setUndoStore(KisUndoStore *undoStore);
/**
* Return current undo store of the image
*/
KisUndoStore* undoStore();
/**
* @return the action recorder associated with this image
*/
KisActionRecorder* actionRecorder() const;
/**
* Tell the image it's modified; this emits the sigImageModified
* signal. This happens when the image needs to be saved
*/
void setModified();
/**
* The default colorspace of this image: new layers will have this
* colorspace and the projection will have this colorspace.
*/
const KoColorSpace * colorSpace() const;
/**
* X resolution in pixels per pt
*/
double xRes() const;
/**
* Y resolution in pixels per pt
*/
double yRes() const;
/**
* Set the resolution in pixels per pt.
*/
void setResolution(double xres, double yres);
/**
* Convert a document coordinate to a pixel coordinate.
*
* @param documentCoord PostScript Pt coordinate to convert.
*/
QPointF documentToPixel(const QPointF &documentCoord) const;
/**
* Convert a document coordinate to an integer pixel coordinate.
*
* @param documentCoord PostScript Pt coordinate to convert.
*/
QPoint documentToIntPixel(const QPointF &documentCoord) const;
/**
* Convert a document rectangle to a pixel rectangle.
*
* @param documentRect PostScript Pt rectangle to convert.
*/
QRectF documentToPixel(const QRectF &documentRect) const;
/**
* Convert a document rectangle to an integer pixel rectangle.
*
* @param documentRect PostScript Pt rectangle to convert.
*/
QRect documentToIntPixel(const QRectF &documentRect) const;
/**
* Convert a pixel coordinate to a document coordinate.
*
* @param pixelCoord pixel coordinate to convert.
*/
QPointF pixelToDocument(const QPointF &pixelCoord) const;
/**
* Convert an integer pixel coordinate to a document coordinate.
* The document coordinate is at the centre of the pixel.
*
* @param pixelCoord pixel coordinate to convert.
*/
QPointF pixelToDocument(const QPoint &pixelCoord) const;
/**
* Convert a document rectangle to an integer pixel rectangle.
*
* @param pixelCoord pixel coordinate to convert.
*/
QRectF pixelToDocument(const QRectF &pixelCoord) const;
/**
* Return the width of the image
*/
qint32 width() const;
/**
* Return the height of the image
*/
qint32 height() const;
/**
* Return the size of the image
*/
QSize size() const {
return QSize(width(), height());
}
/**
* @return the root node of the image node graph
*/
KisGroupLayerSP rootLayer() const;
/**
* Return the projection; that is, the complete, composited
* representation of this image.
*/
KisPaintDeviceSP projection() const;
/**
* Return the number of layers (not other nodes) that are in this
* image.
*/
qint32 nlayers() const;
/**
* Return the number of layers (not other node types) that are in
* this image and that are hidden.
*/
qint32 nHiddenLayers() const;
/**
* Merge all visible layers and discard hidden ones.
*/
void flatten();
/**
* Merge the specified layer with the layer
* below this layer, remove the specified layer.
*/
void mergeDown(KisLayerSP l, const KisMetaData::MergeStrategy* strategy);
/**
* flatten the layer: that is, the projection becomes the layer
* and all subnodes are removed. If this is not a paint layer, it will morph
* into a paint layer.
*/
void flattenLayer(KisLayerSP layer);
/**
* Merges layers in \p mergedLayers and creates a new layer above
* \p putAfter
*/
void mergeMultipleLayers(QList mergedLayers, KisNodeSP putAfter);
/// @return the exact bounds of the image in pixel coordinates.
QRect bounds() const;
/**
* Returns the actual bounds of the image, taking LevelOfDetail
* into account. This value is used as a bounds() value of
* KisDefaultBounds object.
*/
QRect effectiveLodBounds() const;
/// use if the layers have changed _completely_ (eg. when flattening)
void notifyLayersChanged();
/**
* Sets the default color of the root layer projection. All the layers
* will be merged on top of this very color
*/
void setDefaultProjectionColor(const KoColor &color);
/**
* \see setDefaultProjectionColor()
*/
KoColor defaultProjectionColor() const;
void setRootLayer(KisGroupLayerSP rootLayer);
/**
* Add an annotation for this image. This can be anything: Gamma, EXIF, etc.
* Note that the "icc" annotation is reserved for the color strategies.
* If the annotation already exists, overwrite it with this one.
*/
void addAnnotation(KisAnnotationSP annotation);
/** get the annotation with the given type, can return 0 */
KisAnnotationSP annotation(const QString& type);
/** delete the annotation, if the image contains it */
void removeAnnotation(const QString& type);
/**
* Start of an iteration over the annotations of this image (including the ICC Profile)
*/
vKisAnnotationSP_it beginAnnotations();
/** end of an iteration over the annotations of this image */
vKisAnnotationSP_it endAnnotations();
/**
* Called before the image is delted and sends the sigAboutToBeDeleted signal
*/
void notifyAboutToBeDeleted();
KisImageSignalRouter* signalRouter();
/**
* Returns whether we can reselect current global selection
*
* \see reselectGlobalSelection()
*/
bool canReselectGlobalSelection();
/**
* Returns the layer compositions for the image
*/
QList compositions();
/**
* Adds a new layer composition, will be saved with the image
*/
void addComposition(KisLayerCompositionSP composition);
/**
* Remove the layer compostion
*/
void removeComposition(KisLayerCompositionSP composition);
/**
* Permit or deny the wrap-around mode for all the paint devices
* of the image. Note that permitting the wraparound mode will not
* necessarily activate it right now. To be activated the wrap
* around mode should be 1) permitted; 2) supported by the
* currently running stroke.
*/
void setWrapAroundModePermitted(bool value);
/**
* \return whether the wrap-around mode is permitted for this
* image. If the wrap around mode is permitted and the
* currently running stroke supports it, the mode will be
* activated for all paint devices of the image.
*
* \see setWrapAroundMode
*/
bool wrapAroundModePermitted() const;
/**
* \return whether the wraparound mode is activated for all the
* devices of the image. The mode is activated when both
* factors are true: the user permitted it and the stroke
* supports it
*/
bool wrapAroundModeActive() const;
/**
* \return curent level of detail which is used when processing the image.
* Current working zoom = 2 ^ (- currentLevelOfDetail()). Default value is
* null, which means we work on the original image.
*/
int currentLevelOfDetail() const;
/**
* Notify KisImage which level of detail should be used in the
* lod-mode. Setting the mode does not guarantee the LOD to be
* used. It will be activated only when the stokes supports it.
*/
void setDesiredLevelOfDetail(int lod);
/**
* Relative position of the mirror axis center
* 0,0 - topleft corner of the image
* 1,1 - bottomright corner of the image
*/
QPointF mirrorAxesCenter() const;
/**
* Sets the relative position of the axes center
* \see mirrorAxesCenter() for details
*/
void setMirrorAxesCenter(const QPointF &value) const;
public Q_SLOTS:
/**
* Explicitly start regeneration of LoD planes of all the devices
* in the image. This call should be performed when the user is idle,
* just to make the quality of image updates better.
*/
void explicitRegenerateLevelOfDetail();
public:
/**
* Blocks usage of level of detail functionality. After this method
* has been called, no new strokes will use LoD.
*/
void setLevelOfDetailBlocked(bool value);
/**
* \see setLevelOfDetailBlocked()
*/
bool levelOfDetailBlocked() const;
/**
* Notifies that the node collapsed state has changed
*/
void notifyNodeCollpasedChanged();
KisImageAnimationInterface *animationInterface() const;
/**
* @brief setProofingConfiguration, this sets the image's proofing configuration, and signals
* the proofingConfiguration has changed.
* @param proofingConfig - the kis proofing config that will be used instead.
*/
void setProofingConfiguration(KisProofingConfigurationSP proofingConfig);
/**
* @brief proofingConfiguration
* @return the proofing configuration of the image.
*/
KisProofingConfigurationSP proofingConfiguration() const;
public:
bool startIsolatedMode(KisNodeSP node);
void stopIsolatedMode();
KisNodeSP isolatedModeRoot() const;
Q_SIGNALS:
/**
* Emitted whenever an action has caused the image to be
* recomposited.
*
* @param rc The rect that has been recomposited.
*/
void sigImageUpdated(const QRect &);
/**
Emitted whenever the image has been modified, so that it
doesn't match with the version saved on disk.
*/
void sigImageModified();
/**
* The signal is emitted when the size of the image is changed.
* \p oldStillPoint and \p newStillPoint give the receiver the
* hint about how the new and old rect of the image correspond to
* each other. They specify the point of the image around which
* the conversion was done. This point will stay still on the
* user's screen. That is the \p newStillPoint of the new image
* will be painted at the same screen position, where \p
* oldStillPoint of the old image was painted.
*
* \param oldStillPoint is a still point represented in *old*
* image coordinates
*
* \param newStillPoint is a still point represented in *new*
* image coordinates
*/
void sigSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint);
void sigProfileChanged(const KoColorProfile * profile);
void sigColorSpaceChanged(const KoColorSpace* cs);
void sigResolutionChanged(double xRes, double yRes);
void sigRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes);
/**
* Inform the model that a node was changed
*/
void sigNodeChanged(KisNodeSP node);
/**
* Inform that the image is going to be deleted
*/
void sigAboutToBeDeleted();
/**
* The signal is emitted right after a node has been connected
* to the graph of the nodes.
*
* WARNING: you must not request any graph-related information
* about the node being run in a not-scheduler thread. If you need
* information about the parent/siblings of the node connect
* with Qt::DirectConnection, get needed information and then
* emit another Qt::AutoConnection signal to pass this information
* to your thread. See details of the implementation
* in KisDummiesfacadeBase.
*/
void sigNodeAddedAsync(KisNodeSP node);
/**
* This signal is emitted right before a node is going to removed
* from the graph of the nodes.
*
* WARNING: you must not request any graph-related information
* about the node being run in a not-scheduler thread.
*
* \see comment in sigNodeAddedAsync()
*/
void sigRemoveNodeAsync(KisNodeSP node);
/**
* Emitted when the root node of the image has changed.
* It happens, e.g. when we flatten the image. When
* this happens the receiver should reload information
* about the image
*/
void sigLayersChangedAsync();
/**
* Emitted when the UI has requested the undo of the last stroke's
* operation. The point is, we cannot deal with the internals of
* the stroke without its creator knowing about it (which most
* probably cause a crash), so we just forward this request from
* the UI to the creator of the stroke.
*
* If your tool supports undoing part of its work, just listen to
* this signal and undo when it comes
*/
void sigUndoDuringStrokeRequested();
/**
* Emitted when the UI has requested the cancellation of
* the stroke. The point is, we cannot cancel the stroke
* without its creator knowing about it (which most probably
* cause a crash), so we just forward this request from the UI
* to the creator of the stroke.
*
* If your tool supports cancelling of its work in the middle
* of operation, just listen to this signal and cancel
* the stroke when it comes
*/
void sigStrokeCancellationRequested();
/**
* Emitted when the image decides that the stroke should better
* be ended. The point is, we cannot just end the stroke
* without its creator knowing about it (which most probably
* cause a crash), so we just forward this request from the UI
* to the creator of the stroke.
*
* If your tool supports long strokes that may involve multiple
* mouse actions in one stroke, just listen to this signal and
* end the stroke when it comes.
*/
void sigStrokeEndRequested();
/**
* Same as sigStrokeEndRequested() but is not emitted when the active node
* is changed.
*/
void sigStrokeEndRequestedActiveNodeFiltered();
/**
* Emitted when the isolated mode status has changed.
*
* Can be used by the receivers to catch a fact of forcefully
* stopping the isolated mode by the image when some complex
* action was requested
*/
void sigIsolatedModeChanged();
/**
* Emitted when one or more nodes changed the collapsed state
*
*/
void sigNodeCollapsedChanged();
/**
* Emitted when the proofing configuration of the image is being changed.
*
*/
void sigProofingConfigChanged();
public Q_SLOTS:
KisCompositeProgressProxy* compositeProgressProxy();
- bool isIdle();
+ bool isIdle(bool allowLocked = false);
/**
* @brief barrierLock APIDOX
* @param readOnly
*/
void barrierLock(bool readOnly = false);
/**
* @brief barrierLock APIDOX
* @param readOnly
*/
bool tryBarrierLock(bool readOnly = false);
/**
* @brief barrierLock APIDOX
* @param readOnly
*/
void waitForDone();
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy);
void addJob(KisStrokeId id, KisStrokeJobData *data);
void endStroke(KisStrokeId id);
bool cancelStroke(KisStrokeId id);
/**
* @brief blockUpdates block updating the image projection
*/
void blockUpdates();
/**
* @brief unblockUpdates unblock updating the image project. This
* only restarts the scheduler and does not schedule a full refresh.
*/
void unblockUpdates();
/**
* Disables notification of the UI about the changes in the image.
* This feature is used by KisProcessingApplicator. It is needed
* when we change the size of the image. In this case, the whole
* image will be reloaded into UI by sigSizeChanged(), so there is
* no need to inform the UI about individual dirty rects.
*/
void disableUIUpdates();
/**
* \see disableUIUpdates
*/
void enableUIUpdates();
/**
* Disables the processing of all the setDirty() requests that
* come to the image. The incoming requests are effectively
* *dropped*.
*
* This feature is used by KisProcessingApplicator. For many cases
* it provides its own updates interface, which recalculates the
* whole subtree of nodes. But while we change any particular
* node, it can ask for an update itself. This method is a way of
* blocking such intermediate (and excessive) requests.
*
* NOTE: this is a convenience function for setProjectionUpdatesFilter()
* that installs a predefined filter that eats everything. Please
* note that these calls are *not* recursive
*/
void disableDirtyRequests();
/**
* \see disableDirtyRequests()
*/
void enableDirtyRequests();
/**
* Installs a filter object that will filter all the incoming projection update
* requests. If the filter return true, the incoming update is dropped.
*
* NOTE: you cannot set filters recursively!
*/
void setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter);
/**
* \see setProjectionUpdatesFilter()
*/
KisProjectionUpdatesFilterSP projectionUpdatesFilter() const;
void refreshGraphAsync(KisNodeSP root = KisNodeSP());
void refreshGraphAsync(KisNodeSP root, const QRect &rc);
void refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect);
/**
* Triggers synchronous recomposition of the projection
*/
void refreshGraph(KisNodeSP root = KisNodeSP());
void refreshGraph(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void initialRefreshGraph();
/**
* Initiate a stack regeneration skipping the recalculation of the
* filthy node's projection.
*
* Works exactly as pseudoFilthy->setDirty() with the only
* exception that pseudoFilthy::updateProjection() will not be
* called. That is used by KisRecalculateTransformMaskJob to avoid
* cyclic dependencies.
*/
void requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect);
/**
* Adds a spontaneous job to the updates queue.
*
* A spontaneous job may do some trivial tasks in the background,
* like updating the outline of selection or purging unused tiles
* from the existing paint devices.
*/
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
/**
* This method is called by the UI (*not* by the creator of the
* stroke) when it thinks the current stroke should undo its last
* action, for example, when the user presses Ctrl+Z while some
* stroke is active.
*
* If the creator of the stroke supports undoing of intermediate
* actions, it will be notified about this request and can undo
* its last action.
*/
void requestUndoDuringStroke();
/**
* This method is called by the UI (*not* by the creator of the
* stroke) when it thinks current stroke should be cancelled. If
* there is a running stroke that has already been detached from
* its creator (ended or cancelled), it will be forcefully
* cancelled and reverted. If there is an open stroke present, and
* if its creator supports cancelling, it will be notified about
* the request and the stroke will be cancelled
*/
void requestStrokeCancellation();
/**
* This method requests the last stroke executed on the image to become undone.
* If the stroke is not ended, or if all the Lod0 strokes are completed, the method
* returns UNDO_FAIL. If the last Lod0 is going to be finished soon, then UNDO_WAIT
* is returned and the caller should just wait for its completion and call global undo
* instead. UNDO_OK means one unfinished stroke has been undone.
*/
UndoResult tryUndoUnfinishedLod0Stroke();
/**
* This method is called when image or some other part of Krita
* (*not* the creator of the stroke) decides that the stroke
* should be ended. If the creator of the stroke supports it, it
* will be notified and the stroke will be cancelled
*/
void requestStrokeEnd();
/**
* Same as requestStrokeEnd() but is called by view manager when
* the current node is changed. Use to dintinguish
* sigStrokeEndRequested() and
* sigStrokeEndRequestedActiveNodeFiltered() which are used by
* KisNodeJugglerCompressed
*/
void requestStrokeEndActiveNode();
private:
KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy);
KisImage& operator=(const KisImage& rhs);
void emitSizeChanged();
void resizeImageImpl(const QRect& newRect, bool cropLayers);
void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode,
bool resizeImage, double radians);
void shearImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode,
bool resizeImage, double angleX, double angleY,
const QPointF &origin);
void safeRemoveTwoNodes(KisNodeSP node1, KisNodeSP node2);
void refreshHiddenArea(KisNodeSP rootNode, const QRect &preparedArea);
void requestProjectionUpdateImpl(KisNode *node,
const QRect& rect,
const QRect &cropRect);
friend class KisImageResizeCommand;
void setSize(const QSize& size);
friend class KisImageSetProjectionColorSpaceCommand;
void setProjectionColorSpace(const KoColorSpace * colorSpace);
friend class KisDeselectGlobalSelectionCommand;
friend class KisReselectGlobalSelectionCommand;
friend class KisSetGlobalSelectionCommand;
friend class KisImageTest;
/**
* Replaces the current global selection with globalSelection. If
* \p globalSelection is empty, removes the selection object, so that
* \ref globalSelection() will return 0 after that.
*/
void setGlobalSelection(KisSelectionSP globalSelection);
/**
* Deselects current global selection.
* \ref globalSelection() will return 0 after that.
*/
void deselectGlobalSelection();
/**
* Reselects current deselected selection
*
* \see deselectGlobalSelection()
*/
void reselectGlobalSelection();
private:
class KisImagePrivate;
KisImagePrivate * m_d;
};
#endif // KIS_IMAGE_H_
diff --git a/libs/image/kis_image_config.cpp b/libs/image/kis_image_config.cpp
index c99542ebda..0258ccc41a 100644
--- a/libs/image/kis_image_config.cpp
+++ b/libs/image/kis_image_config.cpp
@@ -1,455 +1,455 @@
/*
* Copyright (c) 2010 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_image_config.h"
#include
#include
#include
#include
#include
#include "kis_debug.h"
#include
#include
#include
#include
#include "kis_global.h"
#include
#ifdef Q_OS_OSX
#include
#endif
KisImageConfig::KisImageConfig(bool readOnly)
: m_config( KSharedConfig::openConfig()->group(QString())),
m_readOnly(readOnly)
{
}
KisImageConfig::~KisImageConfig()
{
if (m_readOnly) return;
if (qApp->thread() != QThread::currentThread()) {
dbgKrita << "KisImageConfig: requested config synchronization from nonGUI thread! Called from" << kisBacktrace();
return;
}
m_config.sync();
}
bool KisImageConfig::enableProgressReporting(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("enableProgressReporting", true) : true;
}
void KisImageConfig::setEnableProgressReporting(bool value)
{
m_config.writeEntry("enableProgressReporting", value);
}
bool KisImageConfig::enablePerfLog(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("enablePerfLog", false) :false;
}
void KisImageConfig::setEnablePerfLog(bool value)
{
m_config.writeEntry("enablePerfLog", value);
}
qreal KisImageConfig::transformMaskOffBoundsReadArea() const
{
return m_config.readEntry("transformMaskOffBoundsReadArea", 0.5);
}
int KisImageConfig::updatePatchHeight() const
{
return m_config.readEntry("updatePatchHeight", 512);
}
void KisImageConfig::setUpdatePatchHeight(int value)
{
m_config.writeEntry("updatePatchHeight", value);
}
int KisImageConfig::updatePatchWidth() const
{
return m_config.readEntry("updatePatchWidth", 512);
}
void KisImageConfig::setUpdatePatchWidth(int value)
{
m_config.writeEntry("updatePatchWidth", value);
}
qreal KisImageConfig::maxCollectAlpha() const
{
return m_config.readEntry("maxCollectAlpha", 2.5);
}
qreal KisImageConfig::maxMergeAlpha() const
{
return m_config.readEntry("maxMergeAlpha", 1.);
}
qreal KisImageConfig::maxMergeCollectAlpha() const
{
return m_config.readEntry("maxMergeCollectAlpha", 1.5);
}
qreal KisImageConfig::schedulerBalancingRatio() const
{
/**
* updates-queue-size / strokes-queue-size
*/
return m_config.readEntry("schedulerBalancingRatio", 100.);
}
void KisImageConfig::setSchedulerBalancingRatio(qreal value)
{
m_config.writeEntry("schedulerBalancingRatio", value);
}
int KisImageConfig::maxSwapSize(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("maxSwapSize", 4096) : 4096; // in MiB
}
void KisImageConfig::setMaxSwapSize(int value)
{
m_config.writeEntry("maxSwapSize", value);
}
int KisImageConfig::swapSlabSize() const
{
return m_config.readEntry("swapSlabSize", 64); // in MiB
}
void KisImageConfig::setSwapSlabSize(int value)
{
m_config.writeEntry("swapSlabSize", value);
}
int KisImageConfig::swapWindowSize() const
{
return m_config.readEntry("swapWindowSize", 16); // in MiB
}
void KisImageConfig::setSwapWindowSize(int value)
{
m_config.writeEntry("swapWindowSize", value);
}
int KisImageConfig::tilesHardLimit() const
{
qreal hp = qreal(memoryHardLimitPercent()) / 100.0;
qreal pp = qreal(memoryPoolLimitPercent()) / 100.0;
return totalRAM() * hp * (1 - pp);
}
int KisImageConfig::tilesSoftLimit() const
{
qreal sp = qreal(memorySoftLimitPercent()) / 100.0;
return tilesHardLimit() * sp;
}
int KisImageConfig::poolLimit() const
{
qreal hp = qreal(memoryHardLimitPercent()) / 100.0;
qreal pp = qreal(memoryPoolLimitPercent()) / 100.0;
return totalRAM() * hp * pp;
}
qreal KisImageConfig::memoryHardLimitPercent(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("memoryHardLimitPercent", 50.) : 50.;
}
void KisImageConfig::setMemoryHardLimitPercent(qreal value)
{
m_config.writeEntry("memoryHardLimitPercent", value);
}
qreal KisImageConfig::memorySoftLimitPercent(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("memorySoftLimitPercent", 2.) : 2.;
}
void KisImageConfig::setMemorySoftLimitPercent(qreal value)
{
m_config.writeEntry("memorySoftLimitPercent", value);
}
qreal KisImageConfig::memoryPoolLimitPercent(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("memoryPoolLimitPercent", 2.) : 2.;
}
void KisImageConfig::setMemoryPoolLimitPercent(qreal value)
{
m_config.writeEntry("memoryPoolLimitPercent", value);
}
QString KisImageConfig::swapDir(bool requestDefault)
{
QString swap = QDir::tempPath();
return !requestDefault ?
m_config.readEntry("swaplocation", swap) : swap;
}
void KisImageConfig::setSwapDir(const QString &swapDir)
{
m_config.writeEntry("swaplocation", swapDir);
}
int KisImageConfig::numberOfOnionSkins() const
{
return m_config.readEntry("numberOfOnionSkins", 10);
}
void KisImageConfig::setNumberOfOnionSkins(int value)
{
m_config.writeEntry("numberOfOnionSkins", value);
}
int KisImageConfig::onionSkinTintFactor() const
{
return m_config.readEntry("onionSkinTintFactor", 192);
}
void KisImageConfig::setOnionSkinTintFactor(int value)
{
m_config.writeEntry("onionSkinTintFactor", value);
}
int KisImageConfig::onionSkinOpacity(int offset) const
{
int value = m_config.readEntry("onionSkinOpacity_" + QString::number(offset), -1);
if (value < 0) {
const int num = numberOfOnionSkins();
const qreal dx = qreal(qAbs(offset)) / num;
value = 0.7 * exp(-pow2(dx) / 0.5) * 255;
}
return value;
}
void KisImageConfig::setOnionSkinOpacity(int offset, int value)
{
m_config.writeEntry("onionSkinOpacity_" + QString::number(offset), value);
}
bool KisImageConfig::onionSkinState(int offset) const
{
bool enableByDefault = (qAbs(offset) <= 2);
return m_config.readEntry("onionSkinState_" + QString::number(offset), enableByDefault);
}
void KisImageConfig::setOnionSkinState(int offset, bool value)
{
m_config.writeEntry("onionSkinState_" + QString::number(offset), value);
}
QColor KisImageConfig::onionSkinTintColorBackward() const
{
return m_config.readEntry("onionSkinTintColorBackward", QColor(Qt::red));
}
void KisImageConfig::setOnionSkinTintColorBackward(const QColor &value)
{
m_config.writeEntry("onionSkinTintColorBackward", value);
}
QColor KisImageConfig::onionSkinTintColorForward() const
{
return m_config.readEntry("oninSkinTintColorForward", QColor(Qt::green));
}
void KisImageConfig::setOnionSkinTintColorForward(const QColor &value)
{
m_config.writeEntry("oninSkinTintColorForward", value);
}
bool KisImageConfig::lazyFrameCreationEnabled(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("lazyFrameCreationEnabled", true) : true;
}
void KisImageConfig::setLazyFrameCreationEnabled(bool value)
{
m_config.writeEntry("lazyFrameCreationEnabled", value);
}
#if defined Q_OS_LINUX
#include
#elif defined Q_OS_FREEBSD || defined Q_OS_NETBSD || defined Q_OS_OPENBSD
#include
#elif defined Q_OS_WIN
#include
#elif defined Q_OS_OSX
#include
#include
#endif
#include
int KisImageConfig::totalRAM()
{
// let's think that default memory size is 1000MiB
int totalMemory = 1000; // MiB
int error = 1;
#if defined Q_OS_LINUX
struct sysinfo info;
error = sysinfo(&info);
if(!error) {
totalMemory = info.totalram * info.mem_unit / (1UL << 20);
}
#elif defined Q_OS_FREEBSD || defined Q_OS_NETBSD || defined Q_OS_OPENBSD
u_long physmem;
# if defined HW_PHYSMEM64 // NetBSD only
int mib[] = {CTL_HW, HW_PHYSMEM64};
# else
int mib[] = {CTL_HW, HW_PHYSMEM};
# endif
size_t len = sizeof(physmem);
error = sysctl(mib, 2, &physmem, &len, 0, 0);
if(!error) {
totalMemory = physmem >> 20;
}
#elif defined Q_OS_WIN
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
error = !GlobalMemoryStatusEx(&status);
if (!error) {
totalMemory = status.ullTotalPhys >> 20;
}
// For 32 bit windows, the total memory available is at max the 2GB per process memory limit.
# if defined ENV32BIT
totalMemory = qMin(totalMemory, 2000);
# endif
#elif defined Q_OS_OSX
int mib[2] = { CTL_HW, HW_MEMSIZE };
u_int namelen = sizeof(mib) / sizeof(mib[0]);
uint64_t size;
size_t len = sizeof(size);
errno = 0;
if (sysctl(mib, namelen, &size, &len, 0, 0) >= 0) {
totalMemory = size >> 20;
error = 0;
}
else {
dbgKrita << "sysctl(\"hw.memsize\") raised error" << strerror(errno);
}
#endif
if (error) {
warnKrita << "Cannot get the size of your RAM. Using 1 GiB by default.";
}
return totalMemory;
}
bool KisImageConfig::showAdditionalOnionSkinsSettings(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("showAdditionalOnionSkinsSettings", true) : true;
}
void KisImageConfig::setShowAdditionalOnionSkinsSettings(bool value)
{
m_config.writeEntry("showAdditionalOnionSkinsSettings", value);
}
int KisImageConfig::defaultFrameColorLabel() const
{
return m_config.readEntry("defaultFrameColorLabel", 0);
}
void KisImageConfig::setDefaultFrameColorLabel(int label)
{
m_config.writeEntry("defaultFrameColorLabel", label);
}
KisProofingConfigurationSP KisImageConfig::defaultProofingconfiguration()
{
KisProofingConfiguration *proofingConfig= new KisProofingConfiguration();
proofingConfig->proofingProfile = m_config.readEntry("defaultProofingProfileName", "Chemical proof");
proofingConfig->proofingModel = m_config.readEntry("defaultProofingProfileModel", "CMYKA");
proofingConfig->proofingDepth = m_config.readEntry("defaultProofingProfileDepth", "U8");
proofingConfig->intent = (KoColorConversionTransformation::Intent)m_config.readEntry("defaultProofingProfileIntent", 3);
if (m_config.readEntry("defaultProofingBlackpointCompensation", true)) {
proofingConfig->conversionFlags |= KoColorConversionTransformation::ConversionFlag::BlackpointCompensation;
} else {
proofingConfig->conversionFlags = proofingConfig->conversionFlags & ~KoColorConversionTransformation::ConversionFlag::BlackpointCompensation;
}
- QColor def;
- def = m_config.readEntry("defaultProofingGamutwarning", QColor(Qt::gray));
+ QColor def(Qt::green);
+ m_config.readEntry("defaultProofingGamutwarning", def);
KoColor col(KoColorSpaceRegistry::instance()->rgb8());
col.fromQColor(def);
col.setOpacity(1.0);
proofingConfig->warningColor = col;
proofingConfig->adaptationState = (double)m_config.readEntry("defaultProofingAdaptationState", 1.0);
return toQShared(proofingConfig);
}
void KisImageConfig::setDefaultProofingConfig(const KoColorSpace *proofingSpace, int proofingIntent, bool blackPointCompensation, KoColor warningColor, double adaptationState)
{
m_config.writeEntry("defaultProofingProfileName", proofingSpace->profile()->name());
m_config.writeEntry("defaultProofingProfileModel", proofingSpace->colorModelId().id());
m_config.writeEntry("defaultProofingProfileDepth", proofingSpace->colorDepthId().id());
m_config.writeEntry("defaultProofingProfileIntent", proofingIntent);
m_config.writeEntry("defaultProofingBlackpointCompensation", blackPointCompensation);
QColor c;
- warningColor.toQColor(&c);
+ c = warningColor.toQColor();
m_config.writeEntry("defaultProofingGamutwarning", c);
m_config.writeEntry("defaultProofingAdaptationState",adaptationState);
}
bool KisImageConfig::useLodForColorizeMask(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("useLodForColorizeMask", false) : false;
}
void KisImageConfig::setUseLodForColorizeMask(bool value)
{
m_config.writeEntry("useLodForColorizeMask", value);
}
diff --git a/libs/image/kis_layer.cc b/libs/image/kis_layer.cc
index 38a5dafb33..158488433b 100644
--- a/libs/image/kis_layer.cc
+++ b/libs/image/kis_layer.cc
@@ -1,874 +1,876 @@
/*
* Copyright (c) 2002 Patrick Julien
* Copyright (c) 2005 C. Boemann
* Copyright (c) 2009 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_layer.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_debug.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_mask.h"
#include "kis_effect_mask.h"
#include "kis_selection_mask.h"
#include "kis_meta_data_store.h"
#include "kis_selection.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_psd_layer_style.h"
#include "kis_layer_projection_plane.h"
#include "layerstyles/kis_layer_style_projection_plane.h"
#include "krita_utils.h"
#include "kis_layer_properties_icons.h"
#include "kis_layer_utils.h"
class KisSafeProjection {
public:
KisPaintDeviceSP getDeviceLazy(KisPaintDeviceSP prototype) {
QMutexLocker locker(&m_lock);
if (!m_reusablePaintDevice) {
m_reusablePaintDevice = new KisPaintDevice(*prototype);
}
if(!m_projection ||
*m_projection->colorSpace() != *prototype->colorSpace()) {
m_projection = m_reusablePaintDevice;
m_projection->makeCloneFromRough(prototype, prototype->extent());
m_projection->setProjectionDevice(true);
}
return m_projection;
}
void freeDevice() {
QMutexLocker locker(&m_lock);
m_projection = 0;
if(m_reusablePaintDevice) {
m_reusablePaintDevice->clear();
}
}
private:
QMutex m_lock;
KisPaintDeviceSP m_projection;
KisPaintDeviceSP m_reusablePaintDevice;
};
class KisCloneLayersList {
public:
void addClone(KisCloneLayerWSP cloneLayer) {
m_clonesList.append(cloneLayer);
}
void removeClone(KisCloneLayerWSP cloneLayer) {
m_clonesList.removeOne(cloneLayer);
}
void setDirty(const QRect &rect) {
Q_FOREACH (KisCloneLayerSP clone, m_clonesList) {
if (clone) {
clone->setDirtyOriginal(rect);
}
}
}
const QList registeredClones() const {
return m_clonesList;
}
bool hasClones() const {
return !m_clonesList.isEmpty();
}
private:
QList m_clonesList;
};
struct Q_DECL_HIDDEN KisLayer::Private
{
KisImageWSP image;
QBitArray channelFlags;
KisMetaData::Store* metaDataStore;
KisSafeProjection safeProjection;
KisCloneLayersList clonesList;
KisPSDLayerStyleSP layerStyle;
KisAbstractProjectionPlaneSP layerStyleProjectionPlane;
KisAbstractProjectionPlaneSP projectionPlane;
};
KisLayer::KisLayer(KisImageWSP image, const QString &name, quint8 opacity)
: KisNode()
, m_d(new Private)
{
setName(name);
setOpacity(opacity);
m_d->image = image;
m_d->metaDataStore = new KisMetaData::Store();
m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this));
}
KisLayer::KisLayer(const KisLayer& rhs)
: KisNode(rhs)
, m_d(new Private())
{
if (this != &rhs) {
m_d->image = rhs.m_d->image;
m_d->metaDataStore = new KisMetaData::Store(*rhs.m_d->metaDataStore);
m_d->channelFlags = rhs.m_d->channelFlags;
setName(rhs.name());
m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this));
if (rhs.m_d->layerStyle) {
setLayerStyle(rhs.m_d->layerStyle->clone());
}
}
}
KisLayer::~KisLayer()
{
delete m_d->metaDataStore;
delete m_d;
}
const KoColorSpace * KisLayer::colorSpace() const
{
KisImageSP image = m_d->image.toStrongRef();
if (!image) {
return nullptr;
}
return image->colorSpace();
}
const KoCompositeOp * KisLayer::compositeOp() const
{
/**
* FIXME: This function duplicates the same function from
* KisMask. We can't move it to KisBaseNode as it doesn't
* know anything about parent() method of KisNode
* Please think it over...
*/
KisNodeSP parentNode = parent();
if (!parentNode) return 0;
if (!parentNode->colorSpace()) return 0;
const KoCompositeOp* op = parentNode->colorSpace()->compositeOp(compositeOpId());
return op ? op : parentNode->colorSpace()->compositeOp(COMPOSITE_OVER);
}
KisPSDLayerStyleSP KisLayer::layerStyle() const
{
return m_d->layerStyle;
}
void KisLayer::setLayerStyle(KisPSDLayerStyleSP layerStyle)
{
if (layerStyle) {
m_d->layerStyle = layerStyle;
KisAbstractProjectionPlaneSP plane = !layerStyle->isEmpty() ?
KisAbstractProjectionPlaneSP(new KisLayerStyleProjectionPlane(this)) :
KisAbstractProjectionPlaneSP(0);
m_d->layerStyleProjectionPlane = plane;
} else {
m_d->layerStyleProjectionPlane.clear();
m_d->layerStyle.clear();
}
}
KisBaseNode::PropertyList KisLayer::sectionModelProperties() const
{
KisBaseNode::PropertyList l = KisBaseNode::sectionModelProperties();
l << KisBaseNode::Property(KoID("opacity", i18n("Opacity")), i18n("%1%", percentOpacity()));
- if (compositeOp()) {
- l << KisBaseNode::Property(KoID("compositeop", i18n("Composite Mode")), compositeOp()->description());
+ const KoCompositeOp * compositeOp = this->compositeOp();
+
+ if (compositeOp) {
+ l << KisBaseNode::Property(KoID("compositeop", i18n("Composite Mode")), compositeOp->description());
}
if (m_d->layerStyle && !m_d->layerStyle->isEmpty()) {
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::layerStyle, m_d->layerStyle->isEnabled());
}
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::inheritAlpha, alphaChannelDisabled());
return l;
}
void KisLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
{
KisBaseNode::setSectionModelProperties(properties);
Q_FOREACH (const KisBaseNode::Property &property, properties) {
if (property.id == KisLayerPropertiesIcons::inheritAlpha.id()) {
disableAlphaChannel(property.state.toBool());
}
if (property.id == KisLayerPropertiesIcons::layerStyle.id()) {
if (m_d->layerStyle &&
m_d->layerStyle->isEnabled() != property.state.toBool()) {
m_d->layerStyle->setEnabled(property.state.toBool());
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
}
}
}
void KisLayer::disableAlphaChannel(bool disable)
{
QBitArray newChannelFlags = m_d->channelFlags;
if(newChannelFlags.isEmpty())
newChannelFlags = colorSpace()->channelFlags(true, true);
if(disable)
newChannelFlags &= colorSpace()->channelFlags(true, false);
else
newChannelFlags |= colorSpace()->channelFlags(false, true);
setChannelFlags(newChannelFlags);
}
bool KisLayer::alphaChannelDisabled() const
{
QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->channelFlags;
return flags.count(true) == 0 && !m_d->channelFlags.isEmpty();
}
void KisLayer::setChannelFlags(const QBitArray & channelFlags)
{
Q_ASSERT(channelFlags.isEmpty() ||((quint32)channelFlags.count() == colorSpace()->channelCount()));
if (KritaUtils::compareChannelFlags(channelFlags,
this->channelFlags())) {
return;
}
if (!channelFlags.isEmpty() &&
channelFlags == QBitArray(channelFlags.size(), true)) {
m_d->channelFlags.clear();
} else {
m_d->channelFlags = channelFlags;
}
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
QBitArray & KisLayer::channelFlags() const
{
return m_d->channelFlags;
}
bool KisLayer::temporary() const
{
return nodeProperties().boolProperty("temporary", false);
}
void KisLayer::setTemporary(bool t)
{
nodeProperties().setProperty("temporary", t);
}
KisImageWSP KisLayer::image() const
{
return m_d->image;
}
void KisLayer::setImage(KisImageWSP image)
{
m_d->image = image;
KisNodeSP node = firstChild();
while (node) {
KisLayerUtils::recursiveApplyNodes(node,
[image] (KisNodeSP node) {
node->setImage(image);
});
node = node->nextSibling();
}
}
bool KisLayer::canMergeAndKeepBlendOptions(KisLayerSP otherLayer)
{
return
this->compositeOpId() == otherLayer->compositeOpId() &&
this->opacity() == otherLayer->opacity() &&
this->channelFlags() == otherLayer->channelFlags() &&
!this->layerStyle() && !otherLayer->layerStyle() &&
(this->colorSpace() == otherLayer->colorSpace() ||
*this->colorSpace() == *otherLayer->colorSpace());
}
KisLayerSP KisLayer::createMergedLayerTemplate(KisLayerSP prevLayer)
{
const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer);
KisLayerSP newLayer = new KisPaintLayer(image(), prevLayer->name(), OPACITY_OPAQUE_U8);
if (keepBlendingOptions) {
newLayer->setCompositeOpId(compositeOpId());
newLayer->setOpacity(opacity());
newLayer->setChannelFlags(channelFlags());
}
return newLayer;
}
void KisLayer::fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer)
{
const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer);
QRect layerProjectionExtent = this->projection()->extent();
QRect prevLayerProjectionExtent = prevLayer->projection()->extent();
bool alphaDisabled = this->alphaChannelDisabled();
bool prevAlphaDisabled = prevLayer->alphaChannelDisabled();
KisPaintDeviceSP mergedDevice = dstLayer->paintDevice();
if (!keepBlendingOptions) {
KisPainter gc(mergedDevice);
KisImageSP imageSP = image().toStrongRef();
if (!imageSP) {
return;
}
//Copy the pixels of previous layer with their actual alpha value
prevLayer->disableAlphaChannel(false);
prevLayer->projectionPlane()->apply(&gc, prevLayerProjectionExtent | imageSP->bounds());
//Restore the previous prevLayer disableAlpha status for correct undo/redo
prevLayer->disableAlphaChannel(prevAlphaDisabled);
//Paint the pixels of the current layer, using their actual alpha value
if (alphaDisabled == prevAlphaDisabled) {
this->disableAlphaChannel(false);
}
this->projectionPlane()->apply(&gc, layerProjectionExtent | imageSP->bounds());
//Restore the layer disableAlpha status for correct undo/redo
this->disableAlphaChannel(alphaDisabled);
}
else {
//Copy prevLayer
KisPaintDeviceSP srcDev = prevLayer->projection();
mergedDevice->makeCloneFrom(srcDev, srcDev->extent());
//Paint layer on the copy
KisPainter gc(mergedDevice);
gc.bitBlt(layerProjectionExtent.topLeft(), this->projection(), layerProjectionExtent);
}
}
void KisLayer::registerClone(KisCloneLayerWSP clone)
{
m_d->clonesList.addClone(clone);
}
void KisLayer::unregisterClone(KisCloneLayerWSP clone)
{
m_d->clonesList.removeClone(clone);
}
const QList KisLayer::registeredClones() const
{
return m_d->clonesList.registeredClones();
}
bool KisLayer::hasClones() const
{
return m_d->clonesList.hasClones();
}
void KisLayer::updateClones(const QRect &rect)
{
m_d->clonesList.setDirty(rect);
}
KisSelectionMaskSP KisLayer::selectionMask() const
{
KoProperties properties;
properties.setProperty("active", true);
QList masks = childNodes(QStringList("KisSelectionMask"), properties);
// return the first visible mask
Q_FOREACH (KisNodeSP mask, masks) {
if (mask->visible()) {
return dynamic_cast(mask.data());
}
}
return KisSelectionMaskSP();
}
KisSelectionSP KisLayer::selection() const
{
KisSelectionMaskSP mask = selectionMask();
if (mask) {
return mask->selection();
}
KisImageSP image = m_d->image.toStrongRef();
if (image) {
return image->globalSelection();
}
return KisSelectionSP();
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
QList KisLayer::effectMasks(KisNodeSP lastNode) const
{
QList masks;
if (childCount() > 0) {
KoProperties properties;
properties.setProperty("visible", true);
QList nodes = childNodes(QStringList("KisEffectMask"), properties);
Q_FOREACH (const KisNodeSP& node, nodes) {
if (node == lastNode) break;
KisEffectMaskSP mask = dynamic_cast(const_cast(node.data()));
if (mask)
masks.append(mask);
}
}
return masks;
}
bool KisLayer::hasEffectMasks() const
{
if (childCount() == 0) return false;
KisNodeSP node = firstChild();
while (node) {
if (node->inherits("KisEffectMask") && node->visible()) {
return true;
}
node = node->nextSibling();
}
return false;
}
QRect KisLayer::masksChangeRect(const QList &masks,
const QRect &requestedRect,
bool &rectVariesFlag) const
{
rectVariesFlag = false;
QRect prevChangeRect = requestedRect;
/**
* We set default value of the change rect for the case
* when there is no mask at all
*/
QRect changeRect = requestedRect;
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
changeRect = mask->changeRect(prevChangeRect);
if (changeRect != prevChangeRect)
rectVariesFlag = true;
prevChangeRect = changeRect;
}
return changeRect;
}
QRect KisLayer::masksNeedRect(const QList &masks,
const QRect &changeRect,
QStack &applyRects,
bool &rectVariesFlag) const
{
rectVariesFlag = false;
QRect prevNeedRect = changeRect;
QRect needRect;
for (qint32 i = masks.size() - 1; i >= 0; i--) {
applyRects.push(prevNeedRect);
needRect = masks[i]->needRect(prevNeedRect);
if (prevNeedRect != needRect)
rectVariesFlag = true;
prevNeedRect = needRect;
}
return needRect;
}
KisNode::PositionToFilthy calculatePositionToFilthy(KisNodeSP nodeInQuestion,
KisNodeSP filthy,
KisNodeSP parent)
{
if (parent == filthy || parent != filthy->parent()) {
return KisNode::N_ABOVE_FILTHY;
}
if (nodeInQuestion == filthy) {
return KisNode::N_FILTHY;
}
KisNodeSP node = nodeInQuestion->prevSibling();
while (node) {
if (node == filthy) {
return KisNode::N_ABOVE_FILTHY;
}
node = node->prevSibling();
}
return KisNode::N_BELOW_FILTHY;
}
QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
KisPaintDeviceSP destination,
const QRect &requestedRect,
KisNodeSP filthyNode,
KisNodeSP lastNode) const
{
Q_ASSERT(source);
Q_ASSERT(destination);
QList masks = effectMasks(lastNode);
QRect changeRect;
QRect needRect;
if (masks.isEmpty()) {
changeRect = requestedRect;
if (source != destination) {
copyOriginalToProjection(source, destination, requestedRect);
}
} else {
QStack applyRects;
bool changeRectVaries;
bool needRectVaries;
/**
* FIXME: Assume that varying of the changeRect has already
* been taken into account while preparing walkers
*/
changeRectVaries = false;
changeRect = requestedRect;
//changeRect = masksChangeRect(masks, requestedRect,
// changeRectVaries);
needRect = masksNeedRect(masks, changeRect,
applyRects, needRectVaries);
if (!changeRectVaries && !needRectVaries) {
/**
* A bit of optimization:
* All filters will read/write exactly from/to the requested
* rect so we needn't create temporary paint device,
* just apply it onto destination
*/
Q_ASSERT(needRect == requestedRect);
if (source != destination) {
copyOriginalToProjection(source, destination, needRect);
}
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
const QRect maskApplyRect = applyRects.pop();
const QRect maskNeedRect =
applyRects.isEmpty() ? needRect : applyRects.top();
PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast(this));
mask->apply(destination, maskApplyRect, maskNeedRect, maskPosition);
}
Q_ASSERT(applyRects.isEmpty());
} else {
/**
* We can't eliminate additional copy-op
* as filters' behaviour may be quite insane here,
* so let them work on their own paintDevice =)
*/
KisPaintDeviceSP tempDevice = new KisPaintDevice(colorSpace());
tempDevice->prepareClone(source);
copyOriginalToProjection(source, tempDevice, needRect);
QRect maskApplyRect = applyRects.pop();
QRect maskNeedRect = needRect;
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast(this));
mask->apply(tempDevice, maskApplyRect, maskNeedRect, maskPosition);
if (!applyRects.isEmpty()) {
maskNeedRect = maskApplyRect;
maskApplyRect = applyRects.pop();
}
}
Q_ASSERT(applyRects.isEmpty());
KisPainter::copyAreaOptimized(changeRect.topLeft(), tempDevice, destination, changeRect);
}
}
return changeRect;
}
QRect KisLayer::updateProjection(const QRect& rect, KisNodeSP filthyNode)
{
QRect updatedRect = rect;
KisPaintDeviceSP originalDevice = original();
if (!rect.isValid() ||
!visible() ||
!originalDevice) return QRect();
if (!needProjection() && !hasEffectMasks()) {
m_d->safeProjection.freeDevice();
} else {
if (!updatedRect.isEmpty()) {
KisPaintDeviceSP projection =
m_d->safeProjection.getDeviceLazy(originalDevice);
updatedRect = applyMasks(originalDevice, projection,
updatedRect, filthyNode, 0);
}
}
return updatedRect;
}
QRect KisLayer::partialChangeRect(KisNodeSP lastNode, const QRect& rect)
{
bool changeRectVaries = false;
QRect changeRect = outgoingChangeRect(rect);
changeRect = masksChangeRect(effectMasks(lastNode), changeRect,
changeRectVaries);
return changeRect;
}
/**
* \p rect is a dirty rect in layer's original() coordinates!
*/
void KisLayer::buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect)
{
QRect changeRect = partialChangeRect(lastNode, rect);
KisPaintDeviceSP originalDevice = original();
KIS_ASSERT_RECOVER_RETURN(needProjection() || hasEffectMasks());
if (!changeRect.isEmpty()) {
applyMasks(originalDevice, projection,
changeRect, this, lastNode);
}
}
bool KisLayer::needProjection() const
{
return false;
}
void KisLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const
{
KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect);
}
KisAbstractProjectionPlaneSP KisLayer::projectionPlane() const
{
return m_d->layerStyleProjectionPlane ?
m_d->layerStyleProjectionPlane : m_d->projectionPlane;
}
KisAbstractProjectionPlaneSP KisLayer::internalProjectionPlane() const
{
return m_d->projectionPlane;
}
KisPaintDeviceSP KisLayer::projection() const
{
KisPaintDeviceSP originalDevice = original();
return needProjection() || hasEffectMasks() ?
m_d->safeProjection.getDeviceLazy(originalDevice) : originalDevice;
}
QRect KisLayer::changeRect(const QRect &rect, PositionToFilthy pos) const
{
QRect changeRect = rect;
changeRect = incomingChangeRect(changeRect);
if(pos == KisNode::N_FILTHY) {
QRect projectionToBeUpdated = projection()->exactBoundsAmortized() & changeRect;
bool changeRectVaries;
changeRect = outgoingChangeRect(changeRect);
changeRect = masksChangeRect(effectMasks(), changeRect, changeRectVaries);
/**
* If the projection contains some dirty areas we should also
* add them to the change rect, because they might have
* changed. E.g. when a visibility of the mask has chnaged
* while the parent layer was invinisble.
*/
if (!projectionToBeUpdated.isEmpty() &&
!changeRect.contains(projectionToBeUpdated)) {
changeRect |= projectionToBeUpdated;
}
}
// TODO: string comparizon: optimize!
if (pos != KisNode::N_FILTHY &&
pos != KisNode::N_FILTHY_PROJECTION &&
compositeOpId() != COMPOSITE_COPY) {
changeRect |= rect;
}
return changeRect;
}
QRect KisLayer::incomingChangeRect(const QRect &rect) const
{
return rect;
}
QRect KisLayer::outgoingChangeRect(const QRect &rect) const
{
return rect;
}
QImage KisLayer::createThumbnail(qint32 w, qint32 h)
{
KisPaintDeviceSP originalDevice = original();
return originalDevice ?
originalDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()) : QImage();
}
qint32 KisLayer::x() const
{
KisPaintDeviceSP originalDevice = original();
return originalDevice ? originalDevice->x() : 0;
}
qint32 KisLayer::y() const
{
KisPaintDeviceSP originalDevice = original();
return originalDevice ? originalDevice->y() : 0;
}
void KisLayer::setX(qint32 x)
{
KisPaintDeviceSP originalDevice = original();
if (originalDevice)
originalDevice->setX(x);
}
void KisLayer::setY(qint32 y)
{
KisPaintDeviceSP originalDevice = original();
if (originalDevice)
originalDevice->setY(y);
}
QRect KisLayer::layerExtentImpl(bool needExactBounds) const
{
QRect additionalMaskExtent = QRect();
QList effectMasks = this->effectMasks();
Q_FOREACH(KisEffectMaskSP mask, effectMasks) {
additionalMaskExtent |= mask->nonDependentExtent();
}
KisPaintDeviceSP originalDevice = original();
QRect layerExtent;
if (originalDevice) {
layerExtent = needExactBounds ?
originalDevice->exactBounds() :
originalDevice->extent();
}
QRect additionalCompositeOpExtent;
if (compositeOpId() == COMPOSITE_DESTINATION_IN ||
compositeOpId() == COMPOSITE_DESTINATION_ATOP) {
additionalCompositeOpExtent = originalDevice->defaultBounds()->bounds();
}
return layerExtent | additionalMaskExtent | additionalCompositeOpExtent;
}
QRect KisLayer::extent() const
{
return layerExtentImpl(false);
}
QRect KisLayer::exactBounds() const
{
return layerExtentImpl(true);
}
KisLayerSP KisLayer::parentLayer() const
{
return qobject_cast(parent().data());
}
KisMetaData::Store* KisLayer::metaData()
{
return m_d->metaDataStore;
}
diff --git a/libs/image/kis_legacy_undo_adapter.cpp b/libs/image/kis_legacy_undo_adapter.cpp
index 709989fd1d..4d6e2b8e87 100644
--- a/libs/image/kis_legacy_undo_adapter.cpp
+++ b/libs/image/kis_legacy_undo_adapter.cpp
@@ -1,75 +1,77 @@
/*
* Copyright (c) 2011 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_legacy_undo_adapter.h"
#include "kis_image.h"
KisLegacyUndoAdapter::KisLegacyUndoAdapter(KisUndoStore *undoStore,
KisImageWSP image)
: KisUndoAdapter(undoStore),
m_image(image),
m_macroCounter(0)
{
}
const KUndo2Command* KisLegacyUndoAdapter::presentCommand()
{
return undoStore()->presentCommand();
}
void KisLegacyUndoAdapter::undoLastCommand()
{
undoStore()->undoLastCommand();
}
void KisLegacyUndoAdapter::addCommand(KUndo2Command *command)
{
if(!command) return;
if(m_macroCounter) {
undoStore()->addCommand(command);
}
else {
+ // TODO: add feedback
m_image->barrierLock();
undoStore()->addCommand(command);
m_image->unlock();
}
}
void KisLegacyUndoAdapter::beginMacro(const KUndo2MagicString& macroName)
{
if(!m_macroCounter) {
+ // TODO: add feedback
m_image->barrierLock();
}
m_macroCounter++;
undoStore()->beginMacro(macroName);
}
void KisLegacyUndoAdapter::endMacro()
{
m_macroCounter--;
if(!m_macroCounter) {
m_image->unlock();
}
undoStore()->endMacro();
}
diff --git a/libs/image/kis_update_time_monitor.cpp b/libs/image/kis_update_time_monitor.cpp
index 569dc5e779..b76ab09c2d 100644
--- a/libs/image/kis_update_time_monitor.cpp
+++ b/libs/image/kis_update_time_monitor.cpp
@@ -1,256 +1,258 @@
/*
* Copyright (c) 2011 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_update_time_monitor.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_debug.h"
#include "kis_global.h"
#include "kis_image_config.h"
#include
Q_GLOBAL_STATIC(KisUpdateTimeMonitor, s_instance)
struct StrokeTicket
{
StrokeTicket()
: m_jobTime(0)
, m_updateTime(0) {}
QRegion dirtyRegion;
void start() {
m_timer.start();
}
void jobCompleted() {
m_jobTime = m_timer.restart();
}
void updateCompleted() {
m_updateTime = m_timer.restart();
}
qint64 jobTime() const {
return m_jobTime;
}
qint64 updateTime() const {
return m_updateTime;
}
private:
QElapsedTimer m_timer;
qint64 m_jobTime;
qint64 m_updateTime;
};
struct Q_DECL_HIDDEN KisUpdateTimeMonitor::Private
{
Private()
: jobsTime(0),
responseTime(0),
numTickets(0),
numUpdates(0),
mousePath(0.0),
loggingEnabled(false)
{
loggingEnabled = KisImageConfig().enablePerfLog();
}
QHash preliminaryTickets;
QSet finishedTickets;
qint64 jobsTime;
qint64 responseTime;
qint32 numTickets;
qint32 numUpdates;
QMutex mutex;
qreal mousePath;
QPointF lastMousePos;
QElapsedTimer strokeTime;
KisPaintOpPresetSP preset;
bool loggingEnabled;
};
KisUpdateTimeMonitor::KisUpdateTimeMonitor()
: m_d(new Private)
{
if (m_d->loggingEnabled) {
QDir dir;
if (dir.exists("log")) {
dir.remove("log");
}
dir.mkdir("log");
}
}
KisUpdateTimeMonitor::~KisUpdateTimeMonitor()
{
delete m_d;
}
KisUpdateTimeMonitor* KisUpdateTimeMonitor::instance()
{
return s_instance;
}
void KisUpdateTimeMonitor::startStrokeMeasure()
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
m_d->jobsTime = 0;
m_d->responseTime = 0;
m_d->numTickets = 0;
m_d->numUpdates = 0;
m_d->mousePath = 0;
m_d->lastMousePos = QPointF();
m_d->preset = 0;
m_d->strokeTime.start();
}
void KisUpdateTimeMonitor::endStrokeMeasure()
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
if(m_d->numTickets) {
printValues();
}
}
void KisUpdateTimeMonitor::reportPaintOpPreset(KisPaintOpPresetSP preset)
{
if (!m_d->loggingEnabled) return;
m_d->preset = preset;
}
void KisUpdateTimeMonitor::reportMouseMove(const QPointF &pos)
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
if (!m_d->lastMousePos.isNull()) {
qreal distance = kisDistance(m_d->lastMousePos, pos);
m_d->mousePath += distance;
}
m_d->lastMousePos = pos;
}
void KisUpdateTimeMonitor::printValues()
{
qint64 strokeTime = m_d->strokeTime.elapsed();
qreal responseTime = qreal(m_d->responseTime) / m_d->numTickets;
qreal nonUpdateTime = qreal(m_d->jobsTime) / m_d->numTickets;
- qreal jobsPerUpdate = qreal(m_d->numTickets) / m_d->numUpdates;
+ qreal jobsPerUpdate = qreal(m_d->numTickets) / m_d->numUpdates;
qreal mouseSpeed = qreal(m_d->mousePath) / strokeTime;
QString prefix;
if (m_d->preset) {
KisPaintOpPresetSP preset = m_d->preset->clone();
prefix = QString("%1.").arg(preset->name());
preset->setFilename(QString("log/%1.kpp").arg(preset->name()));
preset->save();
}
QFile logFile(QString("log/%1stroke.rdata").arg(prefix));
logFile.open(QIODevice::Append);
QTextStream stream(&logFile);
- stream << mouseSpeed << "\t"
- << jobsPerUpdate << "\t"
- << nonUpdateTime << "\t"
- << responseTime << "\n";
+
+ stream << i18n("Stroke Time:") << strokeTime << "\t"
+ << i18n("Mouse Speed:") << QString::number( mouseSpeed, 'f', 3 ) << "\t"
+ << i18n("Jobs/Update:") << QString::number( jobsPerUpdate, 'f', 3 ) << "\t"
+ << i18n("Non Update Time:") << QString::number( nonUpdateTime, 'f', 3 ) << "\t"
+ << i18n("Response Time:") << responseTime << endl; // 'endl' will use the correct OS line ending
logFile.close();
}
void KisUpdateTimeMonitor::reportJobStarted(void *key)
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
StrokeTicket *ticket = new StrokeTicket();
ticket->start();
m_d->preliminaryTickets.insert(key, ticket);
}
void KisUpdateTimeMonitor::reportJobFinished(void *key, const QVector &rects)
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
StrokeTicket *ticket = m_d->preliminaryTickets.take(key);
if( ticket ){
ticket->jobCompleted();
Q_FOREACH (const QRect &rect, rects) {
ticket->dirtyRegion += rect;
}
m_d->finishedTickets.insert(ticket);
}
}
void KisUpdateTimeMonitor::reportUpdateFinished(const QRect &rect)
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
Q_FOREACH (StrokeTicket *ticket, m_d->finishedTickets) {
ticket->dirtyRegion -= rect;
if(ticket->dirtyRegion.isEmpty()) {
ticket->updateCompleted();
m_d->jobsTime += ticket->jobTime();
m_d->responseTime += ticket->jobTime() + ticket->updateTime();
m_d->numTickets++;
m_d->finishedTickets.remove(ticket);
delete ticket;
}
}
m_d->numUpdates++;
}
diff --git a/libs/pigment/KoColorSpace.h b/libs/pigment/KoColorSpace.h
index 38b4814e32..e8a24ea9c6 100644
--- a/libs/pigment/KoColorSpace.h
+++ b/libs/pigment/KoColorSpace.h
@@ -1,641 +1,641 @@
/*
* Copyright (c) 2005 Boudewijn Rempt
* Copyright (c) 2006-2007 Cyrille Berger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOLORSPACE_H
#define KOCOLORSPACE_H
#include
#include
#include
#include
#include
#include
#include "KoColorSpaceConstants.h"
#include "KoColorConversionTransformation.h"
#include "KoColorProofingConversionTransformation.h"
#include "KoCompositeOp.h"
#include
#include "kritapigment_export.h"
class QDomDocument;
class QDomElement;
class KoChannelInfo;
class KoColorProfile;
class KoColorTransformation;
class QBitArray;
enum Deletability {
OwnedByRegistryDoNotDelete,
OwnedByRegistryRegistryDeletes,
NotOwnedByRegistry
};
enum ColorSpaceIndependence {
FULLY_INDEPENDENT,
TO_LAB16,
TO_RGBA8,
TO_RGBA16
};
class KoMixColorsOp;
class KoConvolutionOp;
/**
* A KoColorSpace is the definition of a certain color space.
*
* A color model and a color space are two related concepts. A color
* model is more general in that it describes the channels involved and
* how they in broad terms combine to describe a color. Examples are
* RGB, HSV, CMYK.
*
* A color space is more specific in that it also describes exactly how
* the channels are combined. So for each color model there can be a
* number of specific color spaces. So RGB is the model and sRGB,
* adobeRGB, etc are colorspaces.
*
* In Pigment KoColorSpace acts as both a color model and a color space.
* You can think of the class definition as the color model, but the
* instance of the class as representing a colorspace.
*
* A third concept is the profile represented by KoColorProfile. It
* represents the info needed to specialize a color model into a color
* space.
*
* KoColorSpace is an abstract class serving as an interface.
*
* Subclasses implement actual color spaces
* Some subclasses implement only some parts and are named Traits
*
*/
class KRITAPIGMENT_EXPORT KoColorSpace : public boost::equality_comparable
{
friend class KoColorSpaceRegistry;
friend class KoColorSpaceFactory;
protected:
/// Only for use by classes that serve as baseclass for real color spaces
KoColorSpace();
public:
/// Should be called by real color spaces
KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp);
virtual bool operator==(const KoColorSpace& rhs) const;
protected:
virtual ~KoColorSpace();
public:
//========== Gamut and other basic info ===================================//
/*
* @returns QPolygonF with 5*channel samples converted to xyY.
* maybe convert to 3d space in future?
*/
QPolygonF gamutXYY() const;
/*
* @returns a polygon with 5 samples per channel converted to xyY, but unlike
* gamutxyY it focuses on the luminance. This then can be used to visualise
* the approximate trc of a given colorspace.
*/
QPolygonF estimatedTRCXYY() const;
QVector colorants() const;
QVector lumaCoefficients() const;
//========== Channels =====================================================//
/// Return a list describing all the channels this color model has. The order
/// of the channels in the list is the order of channels in the pixel. To find
/// out the preferred display position, use KoChannelInfo::displayPosition.
virtual QList channels() const;
/**
* The total number of channels for a single pixel in this color model
*/
virtual quint32 channelCount() const = 0;
/**
* The total number of color channels (excludes alpha) for a single
* pixel in this color model.
*/
virtual quint32 colorChannelCount() const = 0;
/**
* returns a QBitArray that contains true for the specified
* channel types:
*
* @param color if true, set all color channels to true
* @param alpha if true, set all alpha channels to true
*
* The order of channels is the colorspace descriptive order,
* not the pixel order.
*/
QBitArray channelFlags(bool color = true, bool alpha = false) const;
/**
* The size in bytes of a single pixel in this color model
*/
virtual quint32 pixelSize() const = 0;
/**
* Return a string with the channel's value suitable for display in the gui.
*/
virtual QString channelValueText(const quint8 *pixel, quint32 channelIndex) const = 0;
/**
* Return a string with the channel's value with integer
* channels normalised to the floating point range 0 to 1, if
* appropriate.
*/
virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const = 0;
/**
* Return a QVector of floats with channels' values normalized
* to floating point range 0 to 1.
*/
virtual void normalisedChannelsValue(const quint8 *pixel, QVector &channels) const = 0;
/**
* Write in the pixel the value from the normalized vector.
*/
virtual void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) const = 0;
/**
* Convert the value of the channel at the specified position into
* an 8-bit value. The position is not the number of bytes, but
* the position of the channel as defined in the channel info list.
*/
virtual quint8 scaleToU8(const quint8 * srcPixel, qint32 channelPos) const = 0;
/**
* Set dstPixel to the pixel containing only the given channel of srcPixel. The remaining channels
* should be set to whatever makes sense for 'empty' channels of this color space,
* with the intent being that the pixel should look like it only has the given channel.
*/
virtual void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) const = 0;
//========== Identification ===============================================//
/**
* ID for use in files and internally: unchanging name. As the id must be unique
* it is usually the concatenation of the id of the color model and of the color
* depth, for instance "RGBA8" or "CMYKA16" or "XYZA32f".
*/
- virtual QString id() const;
+ QString id() const;
/**
* User visible name which contains the name of the color model and of the color depth.
* For intance "RGBA (8-bits)" or "CMYKA (16-bits)".
*/
- virtual QString name() const;
+ QString name() const;
/**
* @return a string that identify the color model (for instance "RGB" or "CMYK" ...)
* @see KoColorModelStandardIds.h
*/
virtual KoID colorModelId() const = 0;
/**
* @return a string that identify the bit depth (for instance "U8" or "F16" ...)
* @see KoColorModelStandardIds.h
*/
virtual KoID colorDepthId() const = 0;
/**
* @return true if the profile given in argument can be used by this color space
*/
virtual bool profileIsCompatible(const KoColorProfile* profile) const = 0;
/**
* If false, images in this colorspace will degrade considerably by
* functions, tools and filters that have the given measure of colorspace
* independence.
*
* @param independence the measure to which this colorspace will suffer
* from the manipulations of the tool or filter asking
* @return false if no degradation will take place, true if degradation will
* take place
*/
virtual bool willDegrade(ColorSpaceIndependence independence) const = 0;
//========== Capabilities =================================================//
/**
* Tests if the colorspace offers the specific composite op.
*/
virtual bool hasCompositeOp(const QString & id) const;
/**
* Returns the list of user-visible composite ops supported by this colorspace.
*/
virtual QList compositeOps() const;
/**
* Retrieve a single composite op from the ones this colorspace offers.
* If the requeste composite op does not exist, COMPOSITE_OVER is returned.
*/
virtual const KoCompositeOp * compositeOp(const QString & id) const;
/**
* add a composite op to this colorspace.
*/
virtual void addCompositeOp(const KoCompositeOp * op);
/**
* Returns true if the colorspace supports channel values outside the
* (normalised) range 0 to 1.
*/
virtual bool hasHighDynamicRange() const = 0;
//========== Display profiles =============================================//
/**
* Return the profile of this color space.
*/
virtual const KoColorProfile * profile() const = 0;
//================= Conversion functions ==================================//
/**
* The fromQColor methods take a given color defined as an RGB QColor
* and fills a byte array with the corresponding color in the
* the colorspace managed by this strategy.
*
* @param color the QColor that will be used to fill dst
* @param dst a pointer to a pixel
* @param profile the optional profile that describes the color values of QColor
*/
virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const = 0;
/**
* The toQColor methods take a byte array that is at least pixelSize() long
* and converts the contents to a QColor, using the given profile as a source
* profile and the optional profile as a destination profile.
*
* @param src a pointer to the source pixel
* @param c the QColor that will be filled with the color at src
* @param profile the optional profile that describes the color in c, for instance the monitor profile
*/
virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const = 0;
/**
* Convert the pixels in data to (8-bit BGRA) QImage using the specified profiles.
*
* @param data A pointer to a contiguous memory region containing width * height pixels
* @param width in pixels
* @param height in pixels
* @param dstProfile destination profile
* @param renderingIntent the rendering intent
*/
virtual QImage convertToQImage(const quint8 *data, qint32 width, qint32 height,
const KoColorProfile * dstProfile,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/**
* Convert the specified data to Lab (D50). All colorspaces are guaranteed to support this
*
* @param src the source data
* @param dst the destination data
* @param nPixels the number of source pixels
*/
virtual void toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
/**
* Convert the specified data from Lab (D50). to this colorspace. All colorspaces are
* guaranteed to support this.
*
* @param src the pixels in 16 bit lab format
* @param dst the destination data
* @param nPixels the number of pixels in the array
*/
virtual void fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
/**
* Convert the specified data to sRGB 16 bits. All colorspaces are guaranteed to support this
*
* @param src the source data
* @param dst the destination data
* @param nPixels the number of source pixels
*/
virtual void toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
/**
* Convert the specified data from sRGB 16 bits. to this colorspace. All colorspaces are
* guaranteed to support this.
*
* @param src the pixels in 16 bit rgb format
* @param dst the destination data
* @param nPixels the number of pixels in the array
*/
virtual void fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
/**
* Create a color conversion transformation.
*/
virtual KoColorConversionTransformation* createColorConverter(const KoColorSpace * dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/**
* Convert a byte array of srcLen pixels *src to the specified color space
* and put the converted bytes into the prepared byte array *dst.
*
* Returns false if the conversion failed, true if it succeeded
*
* This function is not thread-safe. If you want to apply multiple conversion
* in different threads at the same time, you need to create one color converter
* per-thread using createColorConverter.
*/
virtual bool convertPixelsTo(const quint8 * src,
quint8 * dst, const KoColorSpace * dstColorSpace,
quint32 numPixels,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
virtual KoColorConversionTransformation *createProofingTransform(const KoColorSpace * dstColorSpace,
const KoColorSpace * proofingSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::Intent proofingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags,
quint8 *gamutWarning, double adaptationState) const;
/**
* @brief proofPixelsTo
* @param src
* @param dst
* @param dstColorSpace the colorspace to which we go to.
* @param proofingSpace the proofing space.
* @param numPixels the amount of pixels.
* @param renderingIntent the rendering intent used for rendering.
* @param proofingIntent the intent used for proofing.
* @param conversionFlags the conversion flags.
* @param gamutWarning the data() of a KoColor.
* @param adaptationState the state of adaptation, only affects absolute colorimetric.
* @return
*/
virtual bool proofPixelsTo(const quint8 * src,
quint8 * dst,
quint32 numPixels,
KoColorConversionTransformation *proofingTransform) const;
//============================== Manipulation functions ==========================//
//
// The manipulation functions have default implementations that _convert_ the pixel
// to a QColor and back. Reimplement these methods in your color strategy!
//
/**
* Get the alpha value of the given pixel, downscaled to an 8-bit value.
*/
virtual quint8 opacityU8(const quint8 * pixel) const = 0;
virtual qreal opacityF(const quint8 * pixel) const = 0;
/**
* Set the alpha channel of the given run of pixels to the given value.
*
* pixels -- a pointer to the pixels that will have their alpha set to this value
* alpha -- a downscaled 8-bit value for opacity
* nPixels -- the number of pixels
*
*/
virtual void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0;
virtual void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) const = 0;
/**
* Multiply the alpha channel of the given run of pixels by the given value.
*
* pixels -- a pointer to the pixels that will have their alpha set to this value
* alpha -- a downscaled 8-bit value for opacity
* nPixels -- the number of pixels
*
*/
virtual void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0;
/**
* Applies the specified 8-bit alpha mask to the pixels. We assume that there are just
* as many alpha values as pixels but we do not check this; the alpha values
* are assumed to be 8-bits.
*/
virtual void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0;
/**
* Applies the inverted 8-bit alpha mask to the pixels. We assume that there are just
* as many alpha values as pixels but we do not check this; the alpha values
* are assumed to be 8-bits.
*/
virtual void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0;
/**
* Applies the specified float alpha mask to the pixels. We assume that there are just
* as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0
*/
virtual void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0;
/**
* Applies the inverted specified float alpha mask to the pixels. We assume that there are just
* as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0
*/
virtual void applyInverseNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0;
/**
* Create an adjustment object for adjusting the brightness and contrast
* transferValues is a 256 bins array with values from 0 to 0xFFFF
* This function is thread-safe, but you need to create one KoColorTransformation per thread.
*/
virtual KoColorTransformation *createBrightnessContrastAdjustment(const quint16 *transferValues) const = 0;
/**
* Create an adjustment object for adjusting individual channels
* transferValues is an array of colorChannelCount number of 256 bins array with values from 0 to 0xFFFF
* This function is thread-safe, but you need to create one KoColorTransformation per thread.
*
* The layout of the channels must be the following:
*
* 0..N-2 - color channels of the pixel;
* N-1 - alpha channel of the pixel (if exists)
*/
virtual KoColorTransformation *createPerChannelAdjustment(const quint16 * const* transferValues) const = 0;
/**
* Darken all color channels with the given amount. If compensate is true,
* the compensation factor will be used to limit the darkening.
*
*/
virtual KoColorTransformation *createDarkenAdjustment(qint32 shade, bool compensate, qreal compensation) const = 0;
/**
* Invert color channels of the given pixels
* This function is thread-safe, but you need to create one KoColorTransformation per thread.
*/
virtual KoColorTransformation *createInvertTransformation() const = 0;
/**
* Get the difference between 2 colors, normalized in the range (0,255). Only completely
* opaque and completely transparent are taken into account when computing the different;
* other transparency levels are not regarded when finding the difference.
*/
virtual quint8 difference(const quint8* src1, const quint8* src2) const = 0;
/**
* Get the difference between 2 colors, normalized in the range (0,255). This function
* takes the Alpha channel of the pixel into account. Alpha channel has the same
* weight as Lightness channel.
*/
virtual quint8 differenceA(const quint8* src1, const quint8* src2) const = 0;
/**
* @return the mix color operation of this colorspace (do not delete it locally, it's deleted by the colorspace).
*/
virtual KoMixColorsOp* mixColorsOp() const;
/**
* @return the convolution operation of this colorspace (do not delete it locally, it's deleted by the colorspace).
*/
virtual KoConvolutionOp* convolutionOp() const;
/**
* Calculate the intensity of the given pixel, scaled down to the range 0-255. XXX: Maybe this should be more flexible
*/
virtual quint8 intensity8(const quint8 * src) const = 0;
/*
*increase luminosity by step
*/
virtual void increaseLuminosity(quint8 * pixel, qreal step) const;
virtual void decreaseLuminosity(quint8 * pixel, qreal step) const;
virtual void increaseSaturation(quint8 * pixel, qreal step) const;
virtual void decreaseSaturation(quint8 * pixel, qreal step) const;
virtual void increaseHue(quint8 * pixel, qreal step) const;
virtual void decreaseHue(quint8 * pixel, qreal step) const;
virtual void increaseRed(quint8 * pixel, qreal step) const;
virtual void increaseGreen(quint8 * pixel, qreal step) const;
virtual void increaseBlue(quint8 * pixel, qreal step) const;
virtual void increaseYellow(quint8 * pixel, qreal step) const;
virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const = 0;
virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const = 0;
virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const = 0;
virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const = 0;
/**
* Compose two arrays of pixels together. If source and target
* are not the same color model, the source pixels will be
* converted to the target model. We're "dst" -- "dst" pixels are always in _this_
* colorspace.
*
* @param srcSpace the colorspace of the source pixels that will be composited onto "us"
* @param param the information needed for blitting e.g. the source and destination pixel data,
* the opacity and flow, ...
* @param op the composition operator to use, e.g. COPY_OVER
*
*/
virtual void bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/**
* Serialize this color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
* This function doesn't create the element but rather the ,
* , ... elements. It is assumed that colorElt is the
* element.
*
* @param pixel buffer to serialized
* @param colorElt root element for the serialization, it is assumed that this
* element is
* @param doc is the document containing colorElt
*/
virtual void colorToXML(const quint8* pixel, QDomDocument& doc, QDomElement& colorElt) const = 0;
/**
* Unserialize a color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
* @param pixel buffer where the color will be unserialized
* @param elt the element to unserialize (, , )
* @return the unserialize color, or an empty color object if the function failed
* to unserialize the color
*/
virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const = 0;
KoColorTransformation* createColorTransformation(const QString & id, const QHash & parameters) const;
protected:
/**
* Use this function in the constructor of your colorspace to add the information about a channel.
* @param ci a pointer to the information about a channel
*/
virtual void addChannel(KoChannelInfo * ci);
const KoColorConversionTransformation* toLabA16Converter() const;
const KoColorConversionTransformation* fromLabA16Converter() const;
const KoColorConversionTransformation* toRgbA16Converter() const;
const KoColorConversionTransformation* fromRgbA16Converter() const;
/**
* Returns the thread-local conversion cache. If it doesn't exist
* yet, it is created. If it is currently too small, it is resized.
*/
QVector * threadLocalConversionCache(quint32 size) const;
/**
* This function defines the behavior of the bitBlt function
* when the composition of pixels in different colorspaces is
* requested, that is in case:
*
* srcCS == any
* dstCS == this
*
* 1) preferCompositionInSourceColorSpace() == false,
*
* the source pixels are first converted to *this color space
* and then composition is performed.
*
* 2) preferCompositionInSourceColorSpace() == true,
*
* the destination pixels are first converted into *srcCS color
* space, then the composition is done, and the result is finally
* converted into *this colorspace.
*
* This is used by alpha8() color space mostly, because it has
* weaker representation of the color, so the composition
* should be done in CS with richer functionality.
*/
virtual bool preferCompositionInSourceColorSpace() const;
struct Private;
Private * const d;
};
inline QDebug operator<<(QDebug dbg, const KoColorSpace *cs)
{
dbg.nospace() << cs->name() << " (" << cs->colorModelId().id() << "," << cs->colorDepthId().id() << " )";
return dbg.space();
}
#endif // KOCOLORSPACE_H
diff --git a/libs/pigment/KoLabColorSpaceTraits.h b/libs/pigment/KoLabColorSpaceTraits.h
index 1cc4ce0a6c..43eb0f72a7 100644
--- a/libs/pigment/KoLabColorSpaceTraits.h
+++ b/libs/pigment/KoLabColorSpaceTraits.h
@@ -1,109 +1,400 @@
/*
* Copyright (c) 2006-2007 Cyrille Berger
+ * Copyright (c) 2016 L. E. Segovia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KO_LAB_COLORSPACE_TRAITS_H_
#define _KO_LAB_COLORSPACE_TRAITS_H_
/**
* LAB traits, it provides some convenient functions to
* access LAB channels through an explicit API.
*
* Use this class in conjonction with KoColorSpace::toLabA16 and
* KoColorSpace::fromLabA16 data.
*
* Example:
* quint8* p = KoLabU16Traits::allocate(1);
* oneKoColorSpace->toLabA16(somepointertodata, p, 1);
* KoLabU16Traits::setL( p, KoLabU16Traits::L(p) / 10 );
* oneKoColorSpace->fromLabA16(p, somepointertodata, 1);
*/
template
struct KoLabTraits : public KoColorSpaceTrait<_channels_type_, 4, 3> {
typedef _channels_type_ channels_type;
typedef KoColorSpaceTrait<_channels_type_, 4, 3> parent;
static const qint32 L_pos = 0;
static const qint32 a_pos = 1;
static const qint32 b_pos = 2;
/**
* An Lab pixel
*/
struct Pixel {
channels_type L;
channels_type a;
channels_type b;
channels_type alpha;
};
/// @return the L component
inline static channels_type L(quint8* data) {
channels_type* d = parent::nativeArray(data);
return d[L_pos];
}
/// Set the L component
inline static void setL(quint8* data, channels_type nv) {
channels_type* d = parent::nativeArray(data);
d[L_pos] = nv;
}
/// @return the a component
inline static channels_type a(quint8* data) {
channels_type* d = parent::nativeArray(data);
return d[a_pos];
}
/// Set the a component
inline static void setA(quint8* data, channels_type nv) {
channels_type* d = parent::nativeArray(data);
d[a_pos] = nv;
}
/// @return the b component
inline static channels_type b(quint8* data) {
channels_type* d = parent::nativeArray(data);
return d[b_pos];
}
/// Set the a component
inline static void setB(quint8* data, channels_type nv) {
channels_type* d = parent::nativeArray(data);
d[b_pos] = nv;
}
};
+//For quint* values must range from 0 to 1 - see KoColorSpaceMaths
struct KoLabU8Traits : public KoLabTraits {
+
+ static const quint32 MAX_CHANNEL_L = 100;
+ static const quint32 MAX_CHANNEL_AB = 255;
+ static const quint32 CHANNEL_AB_ZERO_OFFSET = 128;
+
+ inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) {
+ Q_ASSERT((int)channels.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < parent::channels_nb; i++) {
+ c = nativeArray(pixel)[i];
+ switch (i) {
+ case L_pos:
+ channels[i] = ((qreal)c) / MAX_CHANNEL_L;
+ break;
+ case a_pos:
+ channels[i] = (((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB;
+ break;
+ case b_pos:
+ channels[i] = (((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB;
+ break;
+ case 3:
+ channels[i] = ((qreal)c) / UINT16_MAX;
+ break;
+ default:
+ channels[i] = ((qreal)c) / KoColorSpaceMathsTraits::unitValue;
+ break;
+ }
+ }
+ }
+
+ inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) {
+ if (channelIndex > parent::channels_nb) return QString("Error");
+ channels_type c = nativeArray(pixel)[channelIndex];
+ switch (channelIndex) {
+ case L_pos:
+ return QString().setNum(100.0 * ((qreal)c) / MAX_CHANNEL_L);
+ case a_pos:
+ return QString().setNum(100.0 * ((((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB));
+ case b_pos:
+ return QString().setNum(100.0 * ((((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB));
+ case 3:
+ return QString().setNum(100.0 * ((qreal)c) / UINT16_MAX);
+ default:
+ return QString("Error");
+ }
+ }
+
+ inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) {
+ Q_ASSERT((int)values.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < channels_nb; i++) {
+ float b = 0;
+
+ switch (i) {
+ case L_pos:
+ b = qBound((float)0,
+ (float)MAX_CHANNEL_L * values[i],
+ (float)MAX_CHANNEL_L);
+ break;
+ case a_pos:
+ case b_pos:
+ b = qBound((float)0,
+ (float)MAX_CHANNEL_AB * values[i],
+ (float)MAX_CHANNEL_AB);
+ break;
+ default:
+ b = qBound((float)KoColorSpaceMathsTraits::min,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)KoColorSpaceMathsTraits::max);
+ break;
+ }
+ c = (channels_type)b;
+ nativeArray(pixel)[i] = c;
+ }
+ }
};
struct KoLabU16Traits : public KoLabTraits {
+ // https://github.com/mm2/Little-CMS/blob/master/src/cmspcs.c
+ static const quint32 MAX_CHANNEL_L = 0xFF00;
+ static const quint32 MAX_CHANNEL_AB = 0xFFFF;
+ static const quint32 CHANNEL_AB_ZERO_OFFSET = 0x8000;
+
+ inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) {
+ Q_ASSERT((int)channels.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < parent::channels_nb; i++) {
+ c = nativeArray(pixel)[i];
+ switch (i) {
+ case L_pos:
+ channels[i] = ((qreal)c) / MAX_CHANNEL_L;
+ break;
+ case a_pos:
+ channels[i] = (((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB;
+ break;
+ case b_pos:
+ channels[i] = (((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB;
+ break;
+ case 3:
+ channels[i] = ((qreal)c) / UINT16_MAX;
+ break;
+ default:
+ channels[i] = ((qreal)c) / KoColorSpaceMathsTraits::unitValue;
+ break;
+ }
+ }
+ }
+
+ inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) {
+ if (channelIndex > parent::channels_nb) return QString("Error");
+ channels_type c = nativeArray(pixel)[channelIndex];
+ switch (channelIndex) {
+ case L_pos:
+ return QString().setNum(100.0 * ((qreal)c) / MAX_CHANNEL_L);
+ case a_pos:
+ return QString().setNum(100.0 * ((((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB));
+ case b_pos:
+ return QString().setNum(100.0 * ((((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB));
+ case 3:
+ return QString().setNum(100.0 * ((qreal)c) / UINT16_MAX);
+ default:
+ return QString("Error");
+ }
+ }
+
+ inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) {
+ Q_ASSERT((int)values.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < channels_nb; i++) {
+ float b = 0;
+
+ switch (i) {
+ case L_pos:
+ b = qBound((float)0,
+ (float)MAX_CHANNEL_L * values[i],
+ (float)MAX_CHANNEL_L);
+ break;
+ case a_pos:
+ case b_pos:
+ b = qBound((float)0,
+ (float)MAX_CHANNEL_AB * values[i],
+ (float)MAX_CHANNEL_AB);
+ break;
+ default:
+ b = qBound((float)KoColorSpaceMathsTraits::min,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)KoColorSpaceMathsTraits::max);
+ break;
+ }
+ c = (channels_type)b;
+ nativeArray(pixel)[i] = c;
+ }
+ }
};
+// Float values are not normalised
+// XXX: is it really necessary to bind them to these ranges?
+
#include
#ifdef HAVE_OPENEXR
#include
struct KoLabF16Traits : public KoLabTraits {
+ static constexpr float MIN_CHANNEL_L = 0;
+ static constexpr float MAX_CHANNEL_L = 100;
+ static constexpr float MIN_CHANNEL_AB = -128;
+ static constexpr float MAX_CHANNEL_AB = +127;
+
+ // Lab has some... particulars
+ // For instance, float et al. are NOT normalised
+ inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) {
+ return channelValueText(pixel, channelIndex);
+ }
+ inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) {
+ Q_ASSERT((int)channels.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < parent::channels_nb; i++) {
+ c = parent::nativeArray(pixel)[i];
+ channels[i] = (qreal)c;
+ }
+ }
+ inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) {
+ Q_ASSERT((int)values.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < parent::channels_nb; i++) {
+ float b = 0;
+ switch(i) {
+ case L_pos:
+ b = qBound((float)MIN_CHANNEL_L,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)MAX_CHANNEL_L);
+ break;
+ case a_pos:
+ case b_pos:
+ b = qBound((float)MIN_CHANNEL_AB,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)MAX_CHANNEL_AB);
+ break;
+ case 3:
+ b = qBound((float)KoColorSpaceMathsTraits::min,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)KoColorSpaceMathsTraits::max);
+ default:
+ break;
+ }
+ c = (channels_type)b;
+ parent::nativeArray(pixel)[i] = c;
+ }
+ }
};
#endif
struct KoLabF32Traits : public KoLabTraits {
+ static constexpr float MIN_CHANNEL_L = 0;
+ static constexpr float MAX_CHANNEL_L = 100;
+ static constexpr float MIN_CHANNEL_AB = -128;
+ static constexpr float MAX_CHANNEL_AB = +127;
+
+ // Lab has some... particulars
+ inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) {
+ return channelValueText(pixel, channelIndex);
+ }
+ inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) {
+ Q_ASSERT((int)channels.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < parent::channels_nb; i++) {
+ c = parent::nativeArray(pixel)[i];
+ channels[i] = (qreal)c;
+ }
+ }
+ inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) {
+ Q_ASSERT((int)values.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < parent::channels_nb; i++) {
+ float b = 0;
+ switch(i) {
+ case L_pos:
+ b = qBound((float)MIN_CHANNEL_L,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)MAX_CHANNEL_L);
+ break;
+ case a_pos:
+ case b_pos:
+ b = qBound((float)MIN_CHANNEL_AB,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)MAX_CHANNEL_AB);
+ break;
+ case 3:
+ b = qBound((float)KoColorSpaceMathsTraits::min,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)KoColorSpaceMathsTraits::max);
+ default:
+ break;
+ }
+ c = (channels_type)b;
+ parent::nativeArray(pixel)[i] = c;
+ }
+ }
};
struct KoLabF64Traits : public KoLabTraits {
+ static constexpr double MIN_CHANNEL_L = 0;
+ static constexpr double MAX_CHANNEL_L = 100;
+ static constexpr double MIN_CHANNEL_AB = -128;
+ static constexpr double MAX_CHANNEL_AB = +127;
+
+ // Lab has some... particulars
+ inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) {
+ return channelValueText(pixel, channelIndex);
+ }
+ inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) {
+ Q_ASSERT((int)channels.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < parent::channels_nb; i++) {
+ c = parent::nativeArray(pixel)[i];
+ channels[i] = (qreal)c;
+ }
+ }
+ inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) {
+ Q_ASSERT((int)values.count() == (int)parent::channels_nb);
+ channels_type c;
+ for (uint i = 0; i < parent::channels_nb; i++) {
+ float b = 0;
+ switch(i) {
+ case L_pos:
+ b = qBound((float)MIN_CHANNEL_L,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)MAX_CHANNEL_L);
+ break;
+ case a_pos:
+ case b_pos:
+ b = qBound((float)MIN_CHANNEL_AB,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)MAX_CHANNEL_AB);
+ break;
+ case 3:
+ b = qBound((float)KoColorSpaceMathsTraits::min,
+ (float)KoColorSpaceMathsTraits::unitValue * values[i],
+ (float)KoColorSpaceMathsTraits::max);
+ default:
+ break;
+ }
+ c = (channels_type)b;
+ parent::nativeArray(pixel)[i] = c;
+ }
+ }
};
#endif
diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp
index 9b784d4e92..831a329654 100644
--- a/libs/ui/KisApplication.cpp
+++ b/libs/ui/KisApplication.cpp
@@ -1,734 +1,752 @@
- /* This file is part of the KDE project
+/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis
Copyright (C) 2009 Thomas Zander
Copyright (C) 2012 Boudewijn Rempt
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisApplication.h"
#include
#ifdef Q_OS_WIN
#include
#include
#endif
#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 "KoGlobal.h"
#include "KoConfig.h"
#include
#include
#include
#include "thememanager.h"
#include "KisPrintJob.h"
#include "KisDocument.h"
#include "KisMainWindow.h"
#include "KisAutoSaveRecoveryDialog.h"
#include "KisPart.h"
#include
#include "kis_md5_generator.h"
#include "kis_splash_screen.h"
#include "kis_config.h"
#include "flake/kis_shape_selection.h"
#include
#include
#include
#include
#include
#include
#include
#include "kisexiv2/kis_exiv2.h"
#include "KisApplicationArguments.h"
#include
#include "kis_action_registry.h"
#include
#include
#include
#include "kis_image_barrier_locker.h"
#include "opengl/kis_opengl.h"
#include
namespace {
const QTime appStartTime(QTime::currentTime());
}
class KisApplicationPrivate
{
public:
KisApplicationPrivate()
: splashScreen(0)
{}
- KisSplashScreen *splashScreen;
+ QPointer splashScreen;
};
class KisApplication::ResetStarting
{
public:
ResetStarting(KisSplashScreen *splash = 0)
: m_splash(splash)
{
}
~ResetStarting() {
if (m_splash) {
KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen");
bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false);
if (hideSplash) {
m_splash->hide();
}
else {
- m_splash->setWindowFlags(Qt::Tool);
+ m_splash->setWindowFlags(Qt::Dialog);
QRect r(QPoint(), m_splash->size());
m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center());
m_splash->setWindowTitle(qAppName());
- m_splash->setParent(qApp->activeWindow());
+ m_splash->setParent(0);
Q_FOREACH (QObject *o, m_splash->children()) {
QWidget *w = qobject_cast(o);
if (w && w->isHidden()) {
w->setVisible(true);
}
}
-
m_splash->show();
+ m_splash->activateWindow();
}
}
}
- KisSplashScreen *m_splash;
+ QPointer m_splash;
};
KisApplication::KisApplication(const QString &key, int &argc, char **argv)
: QtSingleApplication(key, argc, argv)
, d(new KisApplicationPrivate)
, m_autosaveDialog(0)
, m_mainWindow(0)
, m_batchRun(false)
{
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
setApplicationDisplayName("Krita");
setApplicationName("krita");
// Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird.
-// setOrganizationName("krita");
+ // setOrganizationName("krita");
setOrganizationDomain("krita.org");
QString version = KritaVersionWrapper::versionString(true);
setApplicationVersion(version);
setWindowIcon(KisIconUtils::loadIcon("calligrakrita"));
if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) {
QStringList styles = QStringList() << "breeze" << "fusion" << "plastique";
if (!styles.contains(style()->objectName().toLower())) {
Q_FOREACH (const QString & style, styles) {
if (!setStyle(style)) {
qDebug() << "No" << style << "available.";
}
else {
qDebug() << "Set style" << style;
break;
}
}
}
}
else {
qDebug() << "Style override disabled, using" << style()->objectName();
}
KisOpenGL::initialize();
qDebug() << "krita has opengl" << KisOpenGL::hasOpenGL();
}
#if defined(Q_OS_WIN) && defined(ENV32BIT)
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
LPFN_ISWOW64PROCESS fnIsWow64Process;
BOOL isWow64()
{
BOOL bIsWow64 = FALSE;
//IsWow64Process is not available on all supported versions of Windows.
//Use GetModuleHandle to get a handle to the DLL that contains the function
//and GetProcAddress to get a pointer to the function if available.
fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
if(0 != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
{
//handle error
}
}
return bIsWow64;
}
#endif
void initializeGlobals(const KisApplicationArguments &args)
{
int dpiX = args.dpiX();
int dpiY = args.dpiY();
if (dpiX > 0 && dpiY > 0) {
KoDpi::setDPI(dpiX, dpiY);
}
}
void addResourceTypes()
{
// All Krita's resource types
KoResourcePaths::addResourceType("kis_pics", "data", "/pics/");
KoResourcePaths::addResourceType("kis_images", "data", "/images/");
KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/");
KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/");
KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/");
KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/");
KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/");
KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/");
KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/");
KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl");
KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true);
KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/");
KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true);
KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true);
KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/");
KoResourcePaths::addResourceType("kis_actions", "data", "/actions");
KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc");
KoResourcePaths::addResourceType("ko_effects", "data", "/effects/");
KoResourcePaths::addResourceType("tags", "data", "/tags/");
KoResourcePaths::addResourceType("templates", "data", "/templates");
KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita");
-// // Extra directories to look for create resources. (Does anyone actually use that anymore?)
-// KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp");
-// KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp"));
-// KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp");
-// KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp"));
-// KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp");
-// KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp"));
-// KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches");
-// KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches"));
+ // // Extra directories to look for create resources. (Does anyone actually use that anymore?)
+ // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp");
+ // KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp"));
+ // KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp");
+ // KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp"));
+ // KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp");
+ // KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp"));
+ // KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches");
+ // KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches"));
// Make directories for all resources we can save, and tags
QDir d;
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/");
}
void KisApplication::loadResources()
{
setSplashScreenLoadingText(i18n("Loading Gradients..."));
+ processEvents();
KoResourceServerProvider::instance()->gradientServer(true);
+
// Load base resources
setSplashScreenLoadingText(i18n("Loading Patterns..."));
+ processEvents();
KoResourceServerProvider::instance()->patternServer(true);
setSplashScreenLoadingText(i18n("Loading Palettes..."));
+ processEvents();
KoResourceServerProvider::instance()->paletteServer(false);
setSplashScreenLoadingText(i18n("Loading Brushes..."));
+ processEvents();
KisBrushServer::instance()->brushServer(true);
// load paintop presets
setSplashScreenLoadingText(i18n("Loading Paint Operations..."));
+ processEvents();
KisResourceServerProvider::instance()->paintOpPresetServer(true);
setSplashScreenLoadingText(i18n("Loading Resource Bundles..."));
+ processEvents();
KisResourceServerProvider::instance()->resourceBundleServer();
}
void KisApplication::loadPlugins()
{
KoShapeRegistry* r = KoShapeRegistry::instance();
r->add(new KisShapeSelectionFactory());
KisActionRegistry::instance();
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
KisPaintOpRegistry::instance();
KoColorSpaceRegistry::instance();
// Load the krita-specific tools
setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool..."));
+ processEvents();
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// Load dockers
setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock..."));
+ processEvents();
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// XXX_EXIV: make the exiv io backends real plugins
setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO..."));
+ processEvents();
KisExiv2::initialize();
}
bool KisApplication::start(const KisApplicationArguments &args)
{
#if defined(Q_OS_WIN) || defined (Q_OS_OSX)
#ifdef ENV32BIT
KisConfig cfg;
if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) {
QMessageBox::information(0,
i18nc("@title:window", "Krita: Warning"),
i18n("You are running a 32 bits build on a 64 bits Windows.\n"
"This is not recommended.\n"
"Please download and install the x64 build instead."));
cfg.writeEntry("WarnedAbout32Bits", true);
}
#endif
#endif
setSplashScreenLoadingText(i18n("Initializing Globals"));
+ processEvents();
initializeGlobals(args);
const bool doTemplate = args.doTemplate();
const bool print = args.print();
const bool exportAs = args.exportAs();
const bool exportAsPdf = args.exportAsPdf();
const QString exportFileName = args.exportFileName();
m_batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty());
// print & exportAsPdf do user interaction ATM
const bool needsMainWindow = !exportAs;
// only show the mainWindow when no command-line mode option is passed
// TODO: fix print & exportAsPdf to work without mainwindow shown
const bool showmainWindow = !exportAs; // would be !batchRun;
- const bool showSplashScreen = !m_batchRun && qEnvironmentVariableIsEmpty("NOSPLASH") && qgetenv("XDG_CURRENT_DESKTOP") != "GNOME";
- if (showSplashScreen) {
+ const bool showSplashScreen = !m_batchRun && qEnvironmentVariableIsEmpty("NOSPLASH");// && qgetenv("XDG_CURRENT_DESKTOP") != "GNOME";
+ if (showSplashScreen && d->splashScreen) {
d->splashScreen->show();
d->splashScreen->repaint();
processEvents();
}
KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator());
// Initialize all Krita directories etc.
KoGlobal::initialize();
KConfigGroup group(KSharedConfig::openConfig(), "theme");
Digikam::ThemeManager themeManager;
themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark"));
ResetStarting resetStarting(d->splashScreen); // remove the splash when done
Q_UNUSED(resetStarting);
// Make sure we can save resources and tags
setSplashScreenLoadingText(i18n("Adding resource types"));
+ processEvents();
addResourceTypes();
// Load all resources and tags before the plugins do that
loadResources();
// Load the plugins
loadPlugins();
if (needsMainWindow) {
// show a mainWindow asap, if we want that
setSplashScreenLoadingText(i18n("Loading Main Window..."));
+ processEvents();
m_mainWindow = KisPart::instance()->createMainWindow();
if (showmainWindow) {
m_mainWindow->initializeGeometry();
m_mainWindow->show();
}
}
short int numberOfOpenDocuments = 0; // number of documents open
// Check for autosave files that can be restored, if we're not running a batchrun (test, print, export to pdf)
if (!m_batchRun) {
checkAutosaveFiles();
}
setSplashScreenLoadingText(QString()); // done loading, so clear out label
+ processEvents();
// Get the command line arguments which we have to parse
int argsCount = args.filenames().count();
if (argsCount > 0) {
// Loop through arguments
short int nPrinted = 0;
for (int argNumber = 0; argNumber < argsCount; argNumber++) {
QString fileName = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
// called in mix with batch options? ignore and silently skip
if (m_batchRun) {
continue;
}
if (createNewDocFromTemplate(fileName, m_mainWindow)) {
++numberOfOpenDocuments;
}
// now try to load
}
else {
if (exportAs) {
QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName);
if (outputMimetype == "application/octetstream") {
dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl;
return 1;
}
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
doc->openUrl(QUrl::fromLocalFile(fileName));
qApp->processEvents(); // For vector layers to be updated
doc->setFileBatchMode(true);
doc->setOutputMimeType(outputMimetype.toLatin1());
if (!doc->exportDocument(QUrl::fromLocalFile(exportFileName))) {
dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage();
}
nPrinted++;
QTimer::singleShot(0, this, SLOT(quit()));
}
else if (m_mainWindow) {
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
if (m_mainWindow->openDocumentInternal(QUrl::fromLocalFile(fileName), doc)) {
if (print) {
m_mainWindow->slotFilePrint();
nPrinted++;
// TODO: trigger closing of app once printing is done
}
else if (exportAsPdf) {
KisPrintJob *job = m_mainWindow->exportToPdf(exportFileName);
if (job)
connect (job, SIGNAL(destroyed(QObject*)), m_mainWindow,
SLOT(slotFileQuit()), Qt::QueuedConnection);
nPrinted++;
} else {
// Normal case, success
numberOfOpenDocuments++;
}
} else {
// .... if failed
// delete doc; done by openDocument
}
}
}
}
if (m_batchRun) {
return nPrinted > 0;
}
}
// fixes BUG:369308 - Krita crashing on splash screen when loading.
// trying to open a file before Krita has loaded can cause it to hang and crash
- d->splashScreen->displayLinks();
- d->splashScreen->displayRecentFiles();
+ if (d->splashScreen) {
+ d->splashScreen->displayLinks();
+ d->splashScreen->displayRecentFiles();
+ }
// not calling this before since the program will quit there.
return true;
}
KisApplication::~KisApplication()
{
delete d;
}
void KisApplication::setSplashScreen(QWidget *splashScreen)
{
d->splashScreen = qobject_cast(splashScreen);
}
void KisApplication::setSplashScreenLoadingText(QString textToLoad)
{
- d->splashScreen->loadingLabel->setText(textToLoad);
- d->splashScreen->repaint();
+ if (d->splashScreen) {
+ d->splashScreen->loadingLabel->setText(textToLoad);
+ d->splashScreen->repaint();
+ }
}
void KisApplication::hideSplashScreen()
{
if (d->splashScreen) {
// hide the splashscreen to see the dialog
d->splashScreen->hide();
}
}
bool KisApplication::notify(QObject *receiver, QEvent *event)
{
try {
return QApplication::notify(receiver, event);
} catch (std::exception &e) {
qWarning("Error %s sending event %i to object %s",
e.what(), event->type(), qPrintable(receiver->objectName()));
} catch (...) {
qWarning("Error sending event %i to object %s",
event->type(), qPrintable(receiver->objectName()));
}
return false;
}
void KisApplication::remoteArguments(QByteArray message, QObject *socket)
{
Q_UNUSED(socket);
// check if we have any mainwindow
KisMainWindow *mw = qobject_cast(qApp->activeWindow());
if (!mw) {
mw = KisPart::instance()->mainWindows().first();
}
if (!mw) {
return;
}
KisApplicationArguments args = KisApplicationArguments::deserialize(message);
const bool doTemplate = args.doTemplate();
const int argsCount = args.filenames().count();
if (argsCount > 0) {
// Loop through arguments
for (int argNumber = 0; argNumber < argsCount; ++argNumber) {
QString filename = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
createNewDocFromTemplate(filename, mw);
}
else if (QFile(filename).exists()) {
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
mw->openDocumentInternal(QUrl::fromLocalFile(filename), doc);
}
}
}
}
void KisApplication::fileOpenRequested(const QString &url)
{
KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first();
if (mainWindow) {
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
mainWindow->openDocumentInternal(QUrl::fromLocalFile(url), doc);
}
}
void KisApplication::checkAutosaveFiles()
{
if (m_batchRun) return;
// Check for autosave files from a previous run. There can be several, and
// we want to offer a restore for every one. Including a nice thumbnail!
QStringList filters;
filters << QString(".krita-*-*-autosave.kra");
#ifdef Q_OS_WIN
QDir dir = QDir::temp();
#else
QDir dir = QDir::home();
#endif
// all autosave files for our application
m_autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden);
// Allow the user to make their selection
if (m_autosaveFiles.size() > 0) {
if (d->splashScreen) {
// hide the splashscreen to see the dialog
d->splashScreen->hide();
}
m_autosaveDialog = new KisAutoSaveRecoveryDialog(m_autosaveFiles, activeWindow());
QDialog::DialogCode result = (QDialog::DialogCode) m_autosaveDialog->exec();
if (result == QDialog::Accepted) {
QStringList filesToRecover = m_autosaveDialog->recoverableFiles();
Q_FOREACH (const QString &autosaveFile, m_autosaveFiles) {
if (!filesToRecover.contains(autosaveFile)) {
QFile::remove(dir.absolutePath() + "/" + autosaveFile);
}
}
m_autosaveFiles = filesToRecover;
} else {
m_autosaveFiles.clear();
}
if (m_autosaveFiles.size() > 0) {
QList autosaveUrls;
Q_FOREACH (const QString &autoSaveFile, m_autosaveFiles) {
const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile);
autosaveUrls << url;
}
if (m_mainWindow) {
Q_FOREACH (const QUrl &url, autosaveUrls) {
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
m_mainWindow->openDocumentInternal(url, doc);
}
}
}
// cleanup
delete m_autosaveDialog;
m_autosaveDialog = nullptr;
}
}
bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow)
{
QString templatePath;
const QUrl templateUrl = QUrl::fromLocalFile(fileName);
if (QFile::exists(fileName)) {
templatePath = templateUrl.toLocalFile();
dbgUI << "using full path...";
}
else {
QString desktopName(fileName);
const QString templatesResourcePath = QStringLiteral("templates/");
QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName);
if (paths.isEmpty()) {
paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName);
}
if (paths.isEmpty()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("No template found for: %1", desktopName));
} else if (paths.count() > 1) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Too many templates found for: %1", desktopName));
} else {
templatePath = paths.at(0);
}
}
if (!templatePath.isEmpty()) {
QUrl templateBase;
templateBase.setPath(templatePath);
KDesktopFile templateInfo(templatePath);
QString templateName = templateInfo.readUrl();
QUrl templateURL;
templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName);
KisDocument *doc = KisPart::instance()->createDocument();
doc->setFileBatchMode(m_batchRun);
if (mainWindow->openDocumentInternal(templateURL, doc)) {
doc->resetURL();
doc->setTitleModified();
dbgUI << "Template loaded...";
return true;
}
else {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Template %1 failed to load.", templateURL.toDisplayString()));
}
}
return false;
}
void KisApplication::clearConfig()
{
KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread());
KSharedConfigPtr config = KSharedConfig::openConfig();
// find user settings file
bool createDir = false;
QString kritarcPath = KoResourcePaths::locateLocal("config", "kritarc", createDir);
QFile configFile(kritarcPath);
if (configFile.exists()) {
// clear file
if (configFile.open(QFile::WriteOnly)) {
configFile.close();
}
else {
QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("Failed to clear %1\n\n"
"Please make sure no other program is using the file and try again.",
kritarcPath),
QMessageBox::Ok, QMessageBox::Ok);
}
}
// reload from disk; with the user file settings cleared,
// this should load any default configuration files shipping with the program
config->reparseConfiguration();
config->sync();
}
void KisApplication::askClearConfig()
{
Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers();
bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier);
if (askClearConfig) {
bool ok = QMessageBox::question(0,
i18nc("@title:window", "Krita"),
i18n("Do you want to clear the settings file?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes;
if (ok) {
clearConfig();
}
}
}
diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp
index 0be6549d03..84969166f2 100644
--- a/libs/ui/KisMainWindow.cpp
+++ b/libs/ui/KisMainWindow.cpp
@@ -1,2394 +1,2394 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis
Copyright (C) 2000-2006 David Faure
Copyright (C) 2007, 2009 Thomas zander
Copyright (C) 2010 Benjamin Port
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMainWindow.h"
#include
// qt includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include