diff --git a/krita/krita.action b/krita/krita.action index a20c27192b..6871c863d0 100644 --- a/krita/krita.action +++ b/krita/krita.action @@ -1,3628 +1,3628 @@ General Open Resources Folder Opens 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 Folder 0 0 false C&ascade Cascade Cascade 10 0 false &Tile Tile Tile 10 0 false Create Resource Bundle... Create Resource Bundle Create Resource Bundle 0 0 false Show File Toolbar Show File Toolbar Show File Toolbar false Show color selector Show color selector Show color selector Shift+I false Show MyPaint shade selector Show MyPaint shade selector Show MyPaint shade selector Shift+M false Show minimal shade selector Show minimal shade selector Show minimal shade selector Shift+N false Show color history Show color history Show color history H false Show common colors Show common colors Show common colors U false Show Tool Options Show Tool Options Show Tool Options \ false Show Brush Editor Show Brush Editor Show Brush Editor F5 false Show Brush Presets Show Brush Presets Show Brush Presets F6 false Toggle Tablet Debugger Toggle Tablet Debugger Toggle Tablet Debugger 0 0 Ctrl+Shift+T false Show system information for bug reports. Show system information for bug reports. Show system information for bug reports. false Rename Composition... Rename Composition Rename Composition 0 0 false Update Composition Update Composition Update Composition 0 0 false Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale 1 0 true &Invert Selection Invert current selection Invert Selection 10000000000 100 Ctrl+Shift+I false Create Snapshot Create Snapshot 1 0 false Switch to Selected Snapshot Switch to selected snapshot 1 0 false Remove Selected Snapshot Remove Selected Snapshot 1 0 false Painting lightness-increase Make brush color lighter Make brush color lighter Make brush color lighter 0 0 L false lightness-decrease Make brush color darker Make brush color darker Make brush color darker 0 0 K false Make brush color more saturated Make brush color more saturated Make brush color more saturated false Make brush color more desaturated Make brush color more desaturated Make brush color more desaturated false Shift brush color hue clockwise Shift brush color hue clockwise Shift brush color hue clockwise false Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise false Make brush color more red Make brush color more red Make brush color more red false Make brush color more green Make brush color more green Make brush color more green false Make brush color more blue Make brush color more blue Make brush color more blue false Make brush color more yellow Make brush color more yellow Make brush color more yellow false opacity-increase Increase opacity Increase opacity Increase opacity 0 0 O false opacity-decrease Decrease opacity Decrease opacity Decrease opacity 0 0 I false draw-eraser Set eraser mode Set eraser mode Set eraser mode 10000 0 E true view-refresh Reload Original Preset Reload Original Preset Reload Original Preset 10000 false transparency-unlocked Preserve Alpha Preserve Alpha Preserve Alpha 10000 true transform_icons_penPressure Use Pen Pressure Use Pen Pressure Use Pen Pressure 10000 true symmetry-horizontal Horizontal Mirror Tool Horizontal Mirror Tool Horizontal Mirror Tool 0 true symmetry-vertical Vertical Mirror Tool Vertical Mirror Tool Vertical Mirror Tool 0 true Hide Mirror X Line Hide Mirror X Line Hide Mirror X Line 10000 true Hide Mirror Y Line Hide Mirror Y Line Hide Mirror Y Line 10000 true Lock Lock X Line Lock X Line 10000 true Lock Y Line Lock Y Line Lock Y Line 10000 true - Move to Canvas Center + Move to Canvas Center X Move to Canvas Center X Move to Canvas Center X 10000 false Move to Canvas Center Y Move to Canvas Center Y Move to Canvas Center Y 10000 false &Toggle Selection Display Mode Toggle Selection Display Mode Toggle Selection Display Mode 0 0 false Next Favourite Preset Next Favourite Preset Next Favourite Preset , false Previous Favourite Preset Previous Favourite Preset Previous Favourite Preset . false preset-switcher Switch to Previous Preset Switch to Previous Preset Switch to Previous Preset / false Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar true Reset Foreground and Background Color Reset Foreground and Background Color Reset Foreground and Background Color D false Swap Foreground and Background Color Swap Foreground and Background Color Swap Foreground and Background Color X false Selection Mode: Add Selection Mode: Add Selection Mode: Add A false Selection Mode: Subtract Selection Mode: Subtract Selection Mode: Subtract S false Selection Mode: Intersect Selection Mode: Intersect Selection Mode: Intersect false Selection Mode: Replace Selection Mode: Replace Selection Mode: Replace R false smoothing-weighted Brush Smoothing: Weighted Brush Smoothing: Weighted Brush Smoothing: Weighted false smoothing-no Brush Smoothing: Disabled Brush Smoothing: Disabled Brush Smoothing: Disabled false smoothing-stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer false brushsize-decrease Decrease Brush Size Decrease Brush Size Decrease Brush Size 0 0 [ false smoothing-basic Brush Smoothing: Basic Brush Smoothing: Basic Brush Smoothing: Basic false brushsize-increase Increase Brush Size Increase Brush Size Increase Brush Size 0 0 ] false Toggle Assistant Toggle Assistant ToggleAssistant Ctrl+Shift+L true Undo Polygon Selection Points Undo Polygon Selection Points Undo Polygon Selection Points Shift+Z false Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false Convert &to Shape Convert to Shape Convert to Shape 10000000000 0 false &Show Global Selection Mask Shows global selection as a usual selection mask in <interface>Layers</interface> docker Show Global Selection Mask 100000 100 true Filters color-to-alpha &Color to Alpha... Color to Alpha Color to Alpha 10000 0 false &Top Edge Detection Top Edge Detection Top Edge Detection 10000 0 false &Index Colors... Index Colors Index Colors 10000 0 false Emboss Horizontal &Only Emboss Horizontal Only Emboss Horizontal Only 10000 0 false D&odge Dodge Dodge 10000 0 false &Sharpen Sharpen Sharpen 10000 0 false B&urn Burn Burn 10000 0 false &Mean Removal Mean Removal Mean Removal 10000 0 false &Gaussian Blur... Gaussian Blur Gaussian Blur 10000 0 false Emboss &in All Directions Emboss in All Directions Emboss in All Directions 10000 0 false &Small Tiles... Small Tiles Small Tiles 10000 0 false &Levels... Levels Levels 10000 0 Ctrl+L false &Sobel... Sobel Sobel 10000 0 false &Wave... Wave Wave 10000 0 false &Motion Blur... Motion Blur Motion Blur 10000 0 false &Invert Invert Invert 10000 0 Ctrl+I false &Color Adjustment curves... Color Adjustment curves Color Adjustment curves 10000 0 Ctrl+M false Pi&xelize... Pixelize Pixelize 10000 0 false Emboss (&Laplacian) Emboss (Laplacian) Emboss (Laplacian) 10000 0 false &Left Edge Detection Left Edge Detection Left Edge Detection 10000 0 false &Blur... Blur Blur 10000 0 false &Raindrops... Raindrops Raindrops 10000 0 false &Bottom Edge Detection Bottom Edge Detection Bottom Edge Detection 10000 0 false &Random Noise... Random Noise Random Noise 10000 0 false &Brightness/Contrast curve... Brightness/Contrast curve Brightness/Contrast curve 10000 0 false Colo&r Balance... Color Balance Color Balance 10000 0 Ctrl+B false &Phong Bumpmap... Phong Bumpmap Phong Bumpmap 10000 0 false &Desaturate Desaturate Desaturate 10000 0 Ctrl+Shift+U false Color &Transfer... Color Transfer Color Transfer 10000 0 false Emboss &Vertical Only Emboss Vertical Only Emboss Vertical Only 10000 0 false &Lens Blur... Lens Blur Lens Blur 10000 0 false M&inimize Channel Minimize Channel Minimize Channel 10000 0 false M&aximize Channel Maximize Channel Maximize Channel 10000 0 false &Oilpaint... Oilpaint Oilpaint 10000 0 false &Right Edge Detection Right Edge Detection Right Edge Detection 10000 0 false &Auto Contrast Auto Contrast Auto Contrast 10000 0 false &Round Corners... Round Corners Round Corners 10000 0 false &Unsharp Mask... Unsharp Mask Unsharp Mask 10000 0 false &Emboss with Variable Depth... Emboss with Variable Depth Emboss with Variable Depth 10000 0 false Emboss &Horizontal && Vertical Emboss Horizontal & Vertical Emboss Horizontal & Vertical 10000 0 false Random &Pick... Random Pick Random Pick 10000 0 false &Gaussian Noise Reduction... Gaussian Noise Reduction Gaussian Noise Reduction 10000 0 false &Posterize... Posterize Posterize 10000 0 false &Wavelet Noise Reducer... Wavelet Noise Reducer Wavelet Noise Reducer 10000 0 false &HSV Adjustment... HSV Adjustment HSV Adjustment 10000 0 Ctrl+U false Tool Shortcuts Dynamic Brush Tool Dynamic Brush Tool Dynamic Brush Tool false Crop Tool Crop the image to an area Crop the image to an area C false Polygon Tool Polygon Tool. Shift-mouseclick ends the polygon. Polygon Tool. Shift-mouseclick ends the polygon. false Rectangle Tool Rectangle Tool Rectangle Tool false Multibrush Tool Multibrush Tool Multibrush Tool Q false Colorize Mask Tool Colorize Mask Tool Colorize Mask Tool Smart Patch Tool Smart Patch Tool Smart Patch Tool Pan Tool Pan Tool Pan Tool Select Shapes Tool Select Shapes Tool Select Shapes Tool false Color Picker Select a color from the image or current layer Select a color from the image or current layer P false Outline Selection Tool Outline Selection Tool Outline Selection Tool false Bezier Curve Selection Tool - Select a + Bezier Curve Selection Tool Bezier Curve Selection Tool false Similar Color Selection Tool - Select a + Similar Color Selection Tool Similar Color Selection Tool false Fill Tool Fill 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. F false Line Tool Line Tool Line Tool false Freehand Path Tool Freehand Path Tool Freehand Path Tool false Bezier Curve Tool Bezier Curve Tool. Shift-mouseclick or double-click ends the curve. Bezier Curve Tool. Shift-mouseclick or double-click ends the curve. false Ellipse Tool Ellipse Tool Ellipse Tool false Freehand Brush Tool Freehand Brush Tool Freehand Brush Tool B false Create object Create object Create object false Elliptical Selection Tool Elliptical Selection Tool Elliptical Selection Tool J false Contiguous Selection Tool Contiguous Selection Tool Contiguous Selection Tool false Pattern editing Pattern editing Pattern editing false Review Review Review false Draw a gradient. Draw a gradient. Draw a gradient. G false Polygonal Selection Tool Polygonal Selection Tool Polygonal Selection Tool false Measurement Tool Measure the distance between two points Measure the distance between two points false Rectangular Selection Tool Rectangular Selection Tool Rectangular Selection Tool Ctrl+R false Move Tool Move a layer Move a layer T false Vector Image Tool Vector Image (EMF/WMF/SVM/SVG) tool Vector Image (EMF/WMF/SVM/SVG) tool false Calligraphy Calligraphy Calligraphy false Path editing Path editing Path editing false Zoom Tool Zoom Tool Zoom Tool false Polyline Tool Polyline Tool. Shift-mouseclick ends the polyline. Polyline Tool. Shift-mouseclick ends the polyline. false Transform Tool Transform a layer or a selection Transform a layer or a selection Ctrl+T false Assistant Tool Assistant Tool Assistant Tool false Gradient Editing Tool Gradient editing Gradient editing false Reference Images Tool Reference Images Tool Reference Images Tool false Blending Modes Select Normal Blending Mode Select Normal Blending Mode Select Normal Blending Mode 0 0 Alt+Shift+N false Select Dissolve Blending Mode Select Dissolve Blending Mode Select Dissolve Blending Mode 0 0 Alt+Shift+I false Select Behind Blending Mode Select Behind Blending Mode Select Behind Blending Mode 0 0 Alt+Shift+Q false Select Clear Blending Mode Select Clear Blending Mode Select Clear Blending Mode 0 0 Alt+Shift+R false Select Darken Blending Mode Select Darken Blending Mode Select Darken Blending Mode 0 0 Alt+Shift+K false Select Multiply Blending Mode Select Multiply Blending Mode Select Multiply Blending Mode 0 0 Alt+Shift+M false Select Color Burn Blending Mode Select Color Burn Blending Mode Select Color Burn Blending Mode 0 0 Alt+Shift+B false Select Linear Burn Blending Mode Select Linear Burn Blending Mode Select Linear Burn Blending Mode 0 0 Alt+Shift+A false Select Lighten Blending Mode Select Lighten Blending Mode Select Lighten Blending Mode 0 0 Alt+Shift+G false Select Screen Blending Mode Select Screen Blending Mode Select Screen Blending Mode 0 0 Alt+Shift+S false Select Color Dodge Blending Mode Select Color Dodge Blending Mode Select Color Dodge Blending Mode 0 0 Alt+Shift+D false Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode 0 0 Alt+Shift+W false Select Overlay Blending Mode Select Overlay Blending Mode Select Overlay Blending Mode 0 0 Alt+Shift+O false Select Hard Overlay Blending Mode Select Hard Overlay Blending Mode Select Hard Overlay Blending Mode 0 0 Alt+Shift+P false Select Soft Light Blending Mode Select Soft Light Blending Mode Select Soft Light Blending Mode 0 0 Alt+Shift+F false Select Hard Light Blending Mode Select Hard Light Blending Mode Select Hard Light Blending Mode 0 0 Alt+Shift+H false Select Vivid Light Blending Mode Select Vivid Light Blending Mode Select Vivid Light Blending Mode 0 0 Alt+Shift+V false Select Linear Light Blending Mode Select Linear Light Blending Mode Select Linear Light Blending Mode 0 0 Alt+Shift+J false Select Pin Light Blending Mode Select Pin Light Blending Mode Select Pin Light Blending Mode 0 0 Alt+Shift+Z false Select Hard Mix Blending Mode Select Hard Mix Blending Mode Select Hard Mix Blending Mode 0 0 Alt+Shift+L false Select Difference Blending Mode Select Difference Blending Mode Select Difference Blending Mode 0 0 Alt+Shift+E false Select Exclusion Blending Mode Select Exclusion Blending Mode Select Exclusion Blending Mode 0 0 Alt+Shift+X false Select Hue Blending Mode Select Hue Blending Mode Select Hue Blending Mode 0 0 Alt+Shift+U false Select Saturation Blending Mode Select Saturation Blending Mode Select Saturation Blending Mode 0 0 Alt+Shift+T false Select Color Blending Mode Select Color Blending Mode Select Color Blending Mode 0 0 Alt+Shift+C false Select Luminosity Blending Mode Select Luminosity Blending Mode Select Luminosity Blending Mode 0 0 Alt+Shift+Y false Animation Previous frame Move to previous frame Move to previous frame 1 0 false Next frame Move to next frame Move to next frame 1 0 false Play / pause animation Play / pause animation Play / pause animation 1 0 false addblankframe Create Blank Frame Add blank frame Add blank frame 100000 0 false addduplicateframe Create Duplicate Frame Add duplicate frame Add duplicate frame 100000 0 false Toggle onion skin Toggle onion skin Toggle onion skin 100000 0 false Previous Keyframe false Next Keyframe false First Frame false Last Frame false Auto Frame Mode true true Show in Timeline true Insert Keyframe Left Insert keyframes to the left of selection, moving the tail of animation to the right. 100000 0 false Insert Keyframe Right Insert keyframes to the right of selection, moving the tail of animation to the right. 100000 0 false Insert Multiple Keyframes Insert several keyframes based on user parameters. 100000 0 false Remove Frame and Pull Remove keyframes moving the tail of animation to the left 100000 0 false deletekeyframe Remove Keyframe Remove keyframes without moving anything around 100000 0 false Insert Column Left Insert column to the left of selection, moving the tail of animation to the right 100000 0 false Insert Column Right Insert column to the right of selection, moving the tail of animation to the right 100000 0 false Insert Multiple Columns Insert several columns based on user parameters. 100000 0 false Remove Column and Pull Remove columns moving the tail of animation to the left 100000 0 false Remove Column Remove columns without moving anything around 100000 0 false Insert Hold Frame Insert a hold frame after every keyframe 100000 0 false Insert Multiple Hold Frames Insert N hold frames after every keyframe 100000 0 false Remove Hold Frame Remove a hold frame after every keyframe 100000 0 false Remove Multiple Hold Frames Remove N hold frames after every keyframe 100000 0 false Insert Hold Column Insert a hold column into the frame at the current position 100000 0 false Insert Multiple Hold Columns Insert N hold columns into the frame at the current position 100000 0 false Remove Hold Column Remove a hold column from the frame at the current position 100000 0 false Remove Multiple Hold Columns Remove N hold columns from the frame at the current position 100000 0 false Mirror Frames Mirror frames' position 100000 0 false Mirror Columns Mirror columns' position 100000 0 false Copy to Clipboard Copy frames to clipboard 100000 0 false Cut to Clipboard Cut frames to clipboard 100000 0 false Paste from Clipboard Paste frames from clipboard 100000 0 false Copy Columns to Clipboard Copy columns to clipboard 100000 0 false Cut Columns to Clipboard Cut columns to clipboard 100000 0 false Paste Columns from Clipboard Paste columns from clipboard 100000 0 false Set Start Time 100000 0 false Set End Time 100000 0 false Update Playback Range 100000 0 false Layers Activate next layer Activate next layer Activate next layer 1000 0 PgUp false Activate previous layer Activate previous layer Activate previous layer 1000 0 PgDown false Activate previously selected layer Activate previously selected layer Activate previously selected layer 1000 0 ; false groupLayer &Group Layer Group Layer Group Layer 1000 0 false cloneLayer &Clone Layer Clone Layer Clone Layer 1000 0 false vectorLayer &Vector Layer Vector Layer Vector Layer 1000 0 false filterLayer &Filter Layer... Filter Layer Filter Layer 1000 0 false fillLayer &Fill Layer... Fill Layer Fill Layer 1000 0 false fileLayer &File Layer... File Layer File Layer 1000 0 false transparencyMask &Transparency Mask Transparency Mask Transparency Mask 100000 0 false filterMask &Filter Mask... Filter Mask Filter Mask 100000 0 false filterMask &Colorize Mask Colorize Mask Colorize Mask 100000 0 false transformMask &Transform Mask... Transform Mask Transform Mask 100000 0 false selectionMask &Local Selection Local Selection Local Selection 100000 0 false view-filter &Isolate Layer Isolate Layer Isolate Layer 1000 0 true layer-locked &Toggle layer lock Toggle layer lock Toggle layer lock 1000 0 false visible Toggle layer &visibility Toggle layer visibility Toggle layer visibility 1000 0 false transparency-locked Toggle layer &alpha Toggle layer alpha Toggle layer alpha 1000 0 false transparency-enabled Toggle layer alpha &inheritance Toggle layer alpha inheritance Toggle layer alpha inheritance 1000 0 false paintLayer &Paint Layer Paint Layer Paint Layer 1000 0 Insert false &New Layer From Visible New layer from visible New layer from visible 1000 0 false duplicatelayer &Duplicate Layer or Mask Duplicate Layer or Mask Duplicate Layer or Mask 1000 0 Ctrl+J false &Cut Selection to New Layer Cut Selection to New Layer Cut Selection to New Layer 100000000 1 Ctrl+Shift+J false Copy &Selection to New Layer Copy Selection to New Layer Copy Selection to New Layer 100000000 0 Ctrl+Alt+J false Copy Layer Copy layer to clipboard Copy layer to clipboard 1000 0 false Cut Layer Cut layer to clipboard Cut layer to clipboard 1000 0 false Paste Layer Paste layer from clipboard Paste layer from clipboard 1000 0 false Quick Group Create a group layer containing selected layers Quick Group 1000 0 Ctrl+G false Quick Ungroup Remove grouping of the layers or remove one layer out of the group Quick Ungroup 100000 0 Ctrl+Alt+G false Quick Clipping Group Group selected layers and add a layer with clipped alpha channel Quick Clipping Group 100000 0 Ctrl+Shift+G false All Layers Select all layers Select all layers 10000 0 false Visible Layers Select all visible layers Select all visible layers 10000 0 false Locked Layers Select all locked layers Select all locked layers 10000 0 false Invisible Layers Select all invisible layers Select all invisible layers 10000 0 false Unlocked Layers Select all unlocked layers Select all unlocked layers 10000 0 false document-save &Save Layer/Mask... Save Layer/Mask Save Layer/Mask 1000 0 false document-save Save Vector Layer as SVG... Save Vector Layer as SVG Save Vector Layer as SVG 1000 0 false document-save Save &Group Layers... Save Group Layers Save Group Layers 100000 0 false Convert group to &animated layer Convert child layers into animation frames Convert child layers into animation frames 100000 0 false fileLayer to &File Layer Saves out the layers into a new image and then references that image. Convert to File Layer 100000 0 false I&mport Layer... Import Layer Import Layer 100000 0 false paintLayer &as Paint Layer... as Paint Layer as Paint Layer 1000 0 false transparencyMask as &Transparency Mask... as Transparency Mask as Transparency Mask 1000 0 false filterMask as &Filter Mask... as Filter Mask as Filter Mask 1000 0 false selectionMask as &Selection Mask... as Selection Mask as Selection Mask 1000 0 false paintLayer to &Paint Layer to Paint Layer to Paint Layer 1000 0 false transparencyMask to &Transparency Mask to Transparency Mask to Transparency Mask 1000 0 false filterMask to &Filter Mask... to Filter Mask to Filter Mask 1000 0 false selectionMask to &Selection Mask to Selection Mask to Selection Mask 1000 0 false transparencyMask &Alpha into Mask Alpha into Mask Alpha into Mask 100000 10 false transparency-enabled &Write as Alpha Write as Alpha Write as Alpha 1000000 1 false document-save &Save Merged... Save Merged Save Merged 1000000 0 false split-layer Split Layer... Split Layer Split Layer 1000 0 false Wavelet Decompose ... Wavelet Decompose Wavelet Decompose 1000 1 false symmetry-horizontal Mirror Layer Hori&zontally Mirror Layer Horizontally Mirror Layer Horizontally 1000 1 false symmetry-vertical Mirror Layer &Vertically Mirror Layer Vertically Mirror Layer Vertically 1000 1 false &Rotate Layer... Rotate Layer Rotate Layer 1000 1 false object-rotate-right Rotate &Layer 90° to the Right Rotate Layer 90° to the Right Rotate Layer 90° to the Right 1000 1 false object-rotate-left Rotate Layer &90° to the Left Rotate Layer 90° to the Left Rotate Layer 90° to the Left 1000 1 false Rotate Layer &180° Rotate Layer 180° Rotate Layer 180° 1000 1 false Scale &Layer to new Size... Scale Layer to new Size Scale Layer to new Size 100000 1 false &Shear Layer... Shear Layer Shear Layer 1000 1 false symmetry-horizontal Mirror All Layers Hori&zontally Mirror All Layers Horizontally Mirror All Layers Horizontally 1000 1 false symmetry-vertical Mirror All Layers &Vertically Mirror All Layers Vertically Mirror All Layers Vertically 1000 1 false &Rotate All Layers... Rotate All Layers Rotate All Layers 1000 1 false object-rotate-right Rotate All &Layers 90° to the Right Rotate All Layers 90° to the Right Rotate All Layers 90° to the Right 1000 1 false object-rotate-left Rotate All Layers &90° to the Left Rotate All Layers 90° to the Left Rotate All Layers 90° to the Left 1000 1 false Rotate All Layers &180° Rotate All Layers 180° Rotate All Layers 180° 1000 1 false Scale All &Layers to new Size... Scale All Layers to new Size Scale All Layers to new Size 100000 1 false &Shear All Layers... Shear All Layers Shear All Layers 1000 1 false &Offset Layer... Offset Layer Offset Layer 100000 1 false Clones &Array... Clones Array Clones Array 100000 0 false &Edit metadata... Edit metadata Edit metadata 100000 1 false &Histogram... Histogram Histogram 100000 0 false &Convert Layer Color Space... Convert Layer Color Space Convert Layer Color Space 100000 1 false merge-layer-below &Merge with Layer Below Merge with Layer Below Merge with Layer Below 100000 0 Ctrl+E false &Flatten Layer Flatten Layer Flatten Layer 100000 0 false Ras&terize Layer Rasterize Layer Rasterize Layer 10000000 1 false Flatten ima&ge Flatten image Flatten image 100000 0 Ctrl+Shift+E false La&yer Style... Layer Style Layer Style 100000 1 false Move into previous group Move into previous group Move into previous group 0 0 false Move into next group Move into next group Move into next group 0 0 false Rename current layer Rename current layer Rename current layer 100000 0 F2 false deletelayer &Remove Layer Remove Layer Remove Layer 1000 1 Shift+Delete false arrowupblr Move Layer or Mask Up Move Layer or Mask Up Ctrl+PgUp false arrowdown Move Layer or Mask Down Move Layer or Mask Down Ctrl+PgDown false properties &Properties... Properties Properties 1000 1 F3 false Set Copy F&rom... Set the source for the selected clone layer(s). Set Copy From 1000 1 false diff --git a/libs/image/brushengine/kis_paint_information.cc b/libs/image/brushengine/kis_paint_information.cc index 80edf3bcd7..07a3f85bc7 100644 --- a/libs/image/brushengine/kis_paint_information.cc +++ b/libs/image/brushengine/kis_paint_information.cc @@ -1,643 +1,655 @@ /* * Copyright (c) 2007,2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "kis_paintop.h" #include "kis_algebra_2d.h" #include "kis_lod_transform.h" #include "kis_spacing_information.h" #include struct KisPaintInformation::Private { Private(const QPointF & pos_, qreal pressure_, qreal xTilt_, qreal yTilt_, qreal rotation_, qreal tangentialPressure_, qreal perspective_, qreal time_, qreal speed_, bool isHoveringMode_) : pos(pos_), pressure(pressure_), xTilt(xTilt_), yTilt(yTilt_), rotation(rotation_), tangentialPressure(tangentialPressure_), perspective(perspective_), time(time_), speed(speed_), isHoveringMode(isHoveringMode_), randomSource(0), perStrokeRandomSource(0), levelOfDetail(0) { } ~Private() { KIS_ASSERT_RECOVER_NOOP(!sanityIsRegistered); } Private(const Private &rhs) { copy(rhs); } Private& operator=(const Private &rhs) { copy(rhs); return *this; } void copy(const Private &rhs) { pos = rhs.pos; pressure = rhs.pressure; xTilt = rhs.xTilt; yTilt = rhs.yTilt; rotation = rhs.rotation; tangentialPressure = rhs.tangentialPressure; perspective = rhs.perspective; time = rhs.time; speed = rhs.speed; isHoveringMode = rhs.isHoveringMode; randomSource = rhs.randomSource; perStrokeRandomSource = rhs.perStrokeRandomSource; sanityIsRegistered = false; // HINT: we do not copy registration mark! directionHistoryInfo = rhs.directionHistoryInfo; canvasRotation = rhs.canvasRotation; canvasMirroredH = rhs.canvasMirroredH; + canvasMirroredV = rhs.canvasMirroredV; + if (rhs.drawingAngleOverride) { drawingAngleOverride = *rhs.drawingAngleOverride; } levelOfDetail = rhs.levelOfDetail; } QPointF pos; qreal pressure; qreal xTilt; qreal yTilt; qreal rotation; qreal tangentialPressure; qreal perspective; qreal time; qreal speed; bool isHoveringMode; KisRandomSourceSP randomSource; KisPerStrokeRandomSourceSP perStrokeRandomSource; - int canvasRotation {0}; + qreal canvasRotation {0}; bool canvasMirroredH {false}; + bool canvasMirroredV {false}; boost::optional drawingAngleOverride; bool sanityIsRegistered = false; struct DirectionHistoryInfo { DirectionHistoryInfo() {} DirectionHistoryInfo(qreal _totalDistance, int _currentDabSeqNo, qreal _lastAngle, QPointF _lastPosition, boost::optional _lockedDrawingAngle) : totalStrokeLength(_totalDistance), currentDabSeqNo(_currentDabSeqNo), lastAngle(_lastAngle), lastPosition(_lastPosition), lockedDrawingAngle(_lockedDrawingAngle) { } qreal totalStrokeLength = 0.0; int currentDabSeqNo = 0; qreal lastAngle = 0.0; QPointF lastPosition; boost::optional lockedDrawingAngle; }; boost::optional directionHistoryInfo; int levelOfDetail; void registerDistanceInfo(KisDistanceInformation *di) { directionHistoryInfo = DirectionHistoryInfo(di->scalarDistanceApprox(), di->currentDabSeqNo(), di->lastDrawingAngle(), di->lastPosition(), di->lockedDrawingAngleOptional()); KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityIsRegistered); sanityIsRegistered = true; } void unregisterDistanceInfo() { sanityIsRegistered = false; } }; KisPaintInformation::DistanceInformationRegistrar:: DistanceInformationRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo) : p(_p) { p->d->registerDistanceInfo(distanceInfo); } KisPaintInformation::DistanceInformationRegistrar::DistanceInformationRegistrar(KisPaintInformation::DistanceInformationRegistrar &&rhs) : p(0) { std::swap(p, rhs.p); } KisPaintInformation::DistanceInformationRegistrar:: ~DistanceInformationRegistrar() { if (p) { p->d->unregisterDistanceInfo(); } } KisPaintInformation::KisPaintInformation(const QPointF & pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal tangentialPressure, qreal perspective, qreal time, qreal speed) : d(new Private(pos, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed, false)) { } KisPaintInformation::KisPaintInformation(const QPointF & pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation) : d(new Private(pos, pressure, xTilt, yTilt, rotation, 0.0, 1.0, 0.0, 0.0, false)) { } KisPaintInformation::KisPaintInformation(const QPointF &pos, qreal pressure) : d(new Private(pos, pressure, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, false)) { } KisPaintInformation::KisPaintInformation(const KisPaintInformation& rhs) : d(new Private(*rhs.d)) { } void KisPaintInformation::operator=(const KisPaintInformation & rhs) { *d = *rhs.d; } KisPaintInformation::~KisPaintInformation() { delete d; } bool KisPaintInformation::isHoveringMode() const { return d->isHoveringMode; } KisPaintInformation KisPaintInformation::createHoveringModeInfo(const QPointF &pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal tangentialPressure, qreal perspective, qreal speed, - int canvasrotation, - bool canvasMirroredH) + qreal canvasrotation, + bool canvasMirroredH, + bool canvasMirroredV) { KisPaintInformation info(pos, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, 0, speed); info.d->isHoveringMode = true; info.d->canvasRotation = canvasrotation; info.d->canvasMirroredH = canvasMirroredH; + info.d->canvasMirroredV = canvasMirroredV; return info; } -int KisPaintInformation::canvasRotation() const +qreal KisPaintInformation::canvasRotation() const { return d->canvasRotation; } -void KisPaintInformation::setCanvasRotation(int rotation) +void KisPaintInformation::setCanvasRotation(qreal rotation) { - if (rotation < 0) { - d->canvasRotation= 360- abs(rotation % 360); - } else { - d->canvasRotation= rotation % 360; - } - + d->canvasRotation = normalizeAngleDegrees(rotation); } bool KisPaintInformation::canvasMirroredH() const { return d->canvasMirroredH; } -void KisPaintInformation::setCanvasHorizontalMirrorState(bool mir) +void KisPaintInformation::setCanvasMirroredH(bool value) { - d->canvasMirroredH = mir; + d->canvasMirroredH = value; +} +bool KisPaintInformation::canvasMirroredV() const +{ + return d->canvasMirroredV; +} + +void KisPaintInformation::setCanvasMirroredV(bool value) +{ + d->canvasMirroredV = value; } void KisPaintInformation::toXML(QDomDocument&, QDomElement& e) const { // hovering mode infos are not supposed to be saved KIS_ASSERT_RECOVER_NOOP(!d->isHoveringMode); e.setAttribute("pointX", QString::number(pos().x(), 'g', 15)); e.setAttribute("pointY", QString::number(pos().y(), 'g', 15)); e.setAttribute("pressure", QString::number(pressure(), 'g', 15)); e.setAttribute("xTilt", QString::number(xTilt(), 'g', 15)); e.setAttribute("yTilt", QString::number(yTilt(), 'g', 15)); e.setAttribute("rotation", QString::number(rotation(), 'g', 15)); e.setAttribute("tangentialPressure", QString::number(tangentialPressure(), 'g', 15)); e.setAttribute("perspective", QString::number(perspective(), 'g', 15)); e.setAttribute("time", QString::number(d->time, 'g', 15)); e.setAttribute("speed", QString::number(d->speed, 'g', 15)); } KisPaintInformation KisPaintInformation::fromXML(const QDomElement& e) { qreal pointX = qreal(KisDomUtils::toDouble(e.attribute("pointX", "0.0"))); qreal pointY = qreal(KisDomUtils::toDouble(e.attribute("pointY", "0.0"))); qreal pressure = qreal(KisDomUtils::toDouble(e.attribute("pressure", "0.0"))); qreal rotation = qreal(KisDomUtils::toDouble(e.attribute("rotation", "0.0"))); qreal tangentialPressure = qreal(KisDomUtils::toDouble(e.attribute("tangentialPressure", "0.0"))); qreal perspective = qreal(KisDomUtils::toDouble(e.attribute("perspective", "0.0"))); qreal xTilt = qreal(KisDomUtils::toDouble(e.attribute("xTilt", "0.0"))); qreal yTilt = qreal(KisDomUtils::toDouble(e.attribute("yTilt", "0.0"))); qreal time = KisDomUtils::toDouble(e.attribute("time", "0")); qreal speed = KisDomUtils::toDouble(e.attribute("speed", "0")); return KisPaintInformation(QPointF(pointX, pointY), pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed); } const QPointF& KisPaintInformation::pos() const { return d->pos; } void KisPaintInformation::setPos(const QPointF& p) { d->pos = p; } qreal KisPaintInformation::pressure() const { return d->pressure; } void KisPaintInformation::setPressure(qreal p) { d->pressure = p; } qreal KisPaintInformation::xTilt() const { return d->xTilt; } qreal KisPaintInformation::yTilt() const { return d->yTilt; } void KisPaintInformation::overrideDrawingAngle(qreal angle) { d->drawingAngleOverride = angle; } qreal KisPaintInformation::drawingAngleSafe(const KisDistanceInformation &distance) const { KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!d->directionHistoryInfo, 0.0); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(distance.hasLastDabInformation(), 0.0); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!d->drawingAngleOverride, 0.0); return KisAlgebra2D::directionBetweenPoints(distance.lastPosition(), pos(), distance.lastDrawingAngle()); } KisPaintInformation::DistanceInformationRegistrar KisPaintInformation::registerDistanceInformation(KisDistanceInformation *distance) { return DistanceInformationRegistrar(this, distance); } qreal KisPaintInformation::drawingAngle(bool considerLockedAngle) const { if (d->drawingAngleOverride) return *d->drawingAngleOverride; if (!d->directionHistoryInfo) { warnKrita << "KisPaintInformation::drawingAngleSafe()" << "DirectionHistoryInfo object is not available"; return 0.0; } if (considerLockedAngle && d->directionHistoryInfo->lockedDrawingAngle) { return *d->directionHistoryInfo->lockedDrawingAngle; } // If the start and end positions are the same, we can't compute an angle. In that case, use the // provided default. return KisAlgebra2D::directionBetweenPoints(d->directionHistoryInfo->lastPosition, pos(), d->directionHistoryInfo->lastAngle); } QPointF KisPaintInformation::drawingDirectionVector() const { const qreal angle = drawingAngle(false); return QPointF(cos(angle), sin(angle)); } qreal KisPaintInformation::drawingDistance() const { if (!d->directionHistoryInfo) { warnKrita << "KisPaintInformation::drawingDistance()" << "DirectionHistoryInfo object is not available"; return 1.0; } QVector2D diff(pos() - d->directionHistoryInfo->lastPosition); qreal length = diff.length(); if (d->levelOfDetail) { length *= KisLodTransform::lodToInvScale(d->levelOfDetail); } return length; } qreal KisPaintInformation::drawingSpeed() const { return d->speed; } qreal KisPaintInformation::rotation() const { return d->rotation; } qreal KisPaintInformation::tangentialPressure() const { return d->tangentialPressure; } qreal KisPaintInformation::perspective() const { return d->perspective; } qreal KisPaintInformation::currentTime() const { return d->time; } int KisPaintInformation::currentDabSeqNo() const { if (!d->directionHistoryInfo) { warnKrita << "KisPaintInformation::currentDabSeqNo()" << "DirectionHistoryInfo object is not available"; return 0; } return d->directionHistoryInfo->currentDabSeqNo; } qreal KisPaintInformation::totalStrokeLength() const { if (!d->directionHistoryInfo) { warnKrita << "KisPaintInformation::totalStrokeLength()" << "DirectionHistoryInfo object is not available"; return 0; } return d->directionHistoryInfo->totalStrokeLength; } KisRandomSourceSP KisPaintInformation::randomSource() const { if (!d->randomSource) { qWarning() << "Accessing uninitialized random source!"; d->randomSource = new KisRandomSource(); } return d->randomSource; } void KisPaintInformation::setRandomSource(KisRandomSourceSP value) { d->randomSource = value; } KisPerStrokeRandomSourceSP KisPaintInformation::perStrokeRandomSource() const { if (!d->perStrokeRandomSource) { qWarning() << "Accessing uninitialized per stroke random source!"; d->perStrokeRandomSource = new KisPerStrokeRandomSource(); } return d->perStrokeRandomSource; } void KisPaintInformation::setPerStrokeRandomSource(KisPerStrokeRandomSourceSP value) { d->perStrokeRandomSource = value; } void KisPaintInformation::setLevelOfDetail(int levelOfDetail) { d->levelOfDetail = levelOfDetail; } QDebug operator<<(QDebug dbg, const KisPaintInformation &info) { #ifdef NDEBUG Q_UNUSED(info); #else dbg.nospace() << "Position: " << info.pos(); dbg.nospace() << ", Pressure: " << info.pressure(); dbg.nospace() << ", X Tilt: " << info.xTilt(); dbg.nospace() << ", Y Tilt: " << info.yTilt(); dbg.nospace() << ", Rotation: " << info.rotation(); dbg.nospace() << ", Tangential Pressure: " << info.tangentialPressure(); dbg.nospace() << ", Perspective: " << info.perspective(); dbg.nospace() << ", Drawing Angle: " << info.drawingAngle(); dbg.nospace() << ", Drawing Speed: " << info.drawingSpeed(); dbg.nospace() << ", Drawing Distance: " << info.drawingDistance(); dbg.nospace() << ", Time: " << info.currentTime(); #endif return dbg.space(); } KisPaintInformation KisPaintInformation::mixOnlyPosition(qreal t, const KisPaintInformation& mixedPi, const KisPaintInformation& basePi) { QPointF pt = (1 - t) * mixedPi.pos() + t * basePi.pos(); return mixImpl(pt, t, mixedPi, basePi, true, false); } KisPaintInformation KisPaintInformation::mix(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) { QPointF pt = (1 - t) * pi1.pos() + t * pi2.pos(); return mix(pt, t, pi1, pi2); } KisPaintInformation KisPaintInformation::mix(const QPointF& p, qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) { return mixImpl(p, t, pi1, pi2, false, true); } KisPaintInformation KisPaintInformation::mixWithoutTime(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) { QPointF pt = (1 - t) * pi1.pos() + t * pi2.pos(); return mixWithoutTime(pt, t, pi1, pi2); } KisPaintInformation KisPaintInformation::mixWithoutTime(const QPointF& p, qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) { return mixImpl(p, t, pi1, pi2, false, false); } void KisPaintInformation::mixOtherOnlyPosition(qreal t, const KisPaintInformation& other) { QPointF pt = (1 - t) * other.pos() + t * this->pos(); this->mixOtherImpl(pt, t, other, true, false); } void KisPaintInformation::mixOtherWithoutTime(qreal t, const KisPaintInformation& other) { QPointF pt = (1 - t) * other.pos() + t * this->pos(); this->mixOtherImpl(pt, t, other, false, false); } KisPaintInformation KisPaintInformation::mixImpl(const QPointF &p, qreal t, const KisPaintInformation &pi1, const KisPaintInformation &pi2, bool posOnly, bool mixTime) { KisPaintInformation result(pi2); result.mixOtherImpl(p, t, pi1, posOnly, mixTime); return result; } void KisPaintInformation::mixOtherImpl(const QPointF &p, qreal t, const KisPaintInformation &other, bool posOnly, bool mixTime) { if (posOnly) { this->d->pos = p; this->d->isHoveringMode = false; this->d->levelOfDetail = 0; return; } else { qreal pressure = (1 - t) * other.pressure() + t * this->pressure(); qreal xTilt = (1 - t) * other.xTilt() + t * this->xTilt(); qreal yTilt = (1 - t) * other.yTilt() + t * this->yTilt(); qreal rotation = other.rotation(); if (other.rotation() != this->rotation()) { qreal a1 = kisDegreesToRadians(other.rotation()); qreal a2 = kisDegreesToRadians(this->rotation()); qreal distance = shortestAngularDistance(a2, a1); rotation = kisRadiansToDegrees(incrementInDirection(a1, t * distance, a2)); } qreal tangentialPressure = (1 - t) * other.tangentialPressure() + t * this->tangentialPressure(); qreal perspective = (1 - t) * other.perspective() + t * this->perspective(); qreal time = mixTime ? ((1 - t) * other.currentTime() + t * this->currentTime()) : this->currentTime(); qreal speed = (1 - t) * other.drawingSpeed() + t * this->drawingSpeed(); KIS_ASSERT_RECOVER_NOOP(other.isHoveringMode() == this->isHoveringMode()); *(this->d) = Private(p, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed, other.isHoveringMode()); + this->d->canvasRotation = other.d->canvasRotation; + this->d->canvasMirroredH = other.d->canvasMirroredH; + this->d->canvasMirroredV = other.d->canvasMirroredV; this->d->randomSource = other.d->randomSource; this->d->perStrokeRandomSource = other.d->perStrokeRandomSource; // this->d->isHoveringMode = other.isHoveringMode(); this->d->levelOfDetail = other.d->levelOfDetail; } } qreal KisPaintInformation::tiltDirection(const KisPaintInformation& info, bool normalize) { qreal xTilt = info.xTilt(); qreal yTilt = info.yTilt(); // radians -PI, PI qreal tiltDirection = atan2(-xTilt, yTilt); // if normalize is true map to 0.0..1.0 return normalize ? (tiltDirection / (2 * M_PI) + 0.5) : tiltDirection; } qreal KisPaintInformation::tiltElevation(const KisPaintInformation& info, qreal maxTiltX, qreal maxTiltY, bool normalize) { qreal xTilt = qBound(qreal(-1.0), info.xTilt() / maxTiltX , qreal(1.0)); qreal yTilt = qBound(qreal(-1.0), info.yTilt() / maxTiltY , qreal(1.0)); qreal e; if (fabs(xTilt) > fabs(yTilt)) { e = sqrt(qreal(1.0) + yTilt * yTilt); } else { e = sqrt(qreal(1.0) + xTilt * xTilt); } qreal cosAlpha = sqrt(xTilt * xTilt + yTilt * yTilt) / e; qreal tiltElevation = acos(cosAlpha); // in radians in [0, 0.5 * PI] // mapping to 0.0..1.0 if normalize is true return normalize ? (tiltElevation / (M_PI * qreal(0.5))) : tiltElevation; } diff --git a/libs/image/brushengine/kis_paint_information.h b/libs/image/brushengine/kis_paint_information.h index bc1067d38d..baa1bc0f0b 100644 --- a/libs/image/brushengine/kis_paint_information.h +++ b/libs/image/brushengine/kis_paint_information.h @@ -1,311 +1,324 @@ /* * Copyright (c) 2004 Cyrille Berger * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_PAINT_INFORMATION_ #define _KIS_PAINT_INFORMATION_ #include #include #include "kis_global.h" #include "kritaimage_export.h" #include #include "kis_random_source.h" #include "KisPerStrokeRandomSource.h" #include "kis_spacing_information.h" #include "kis_timing_information.h" class QDomDocument; class QDomElement; class KisDistanceInformation; /** * KisPaintInformation contains information about the input event that * causes the brush action to happen to the brush engine's paint * methods. * * XXX: we directly pass the KoPointerEvent x and y tilt to * KisPaintInformation, and their range is -60 to +60! * * @param pos: the position of the paint event in subpixel accuracy * @param pressure: the pressure of the stylus * @param xTilt: the angle between the device (a pen, for example) and * the perpendicular in the direction of the x axis. Positive values * are towards the bottom of the tablet. The angle is within the range * 0 to 1 * @param yTilt: the angle between the device (a pen, for example) and * the perpendicular in the direction of the y axis. Positive values * are towards the bottom of the tablet. The angle is within the range * 0 to . * @param movement: current position minus the last position of the call to paintAt * @param rotation * @param tangentialPressure * @param perspective **/ class KRITAIMAGE_EXPORT KisPaintInformation { public: /** * Note, that this class is relied on the compiler optimization * of the return value. So if it doesn't work for some reason, * please implement a proper copy c-tor */ class KRITAIMAGE_EXPORT DistanceInformationRegistrar { public: DistanceInformationRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo); DistanceInformationRegistrar(const DistanceInformationRegistrar &rhs) = delete; DistanceInformationRegistrar(DistanceInformationRegistrar &&rhs); ~DistanceInformationRegistrar(); private: KisPaintInformation *p; }; public: /** * Create a new KisPaintInformation object. */ KisPaintInformation(const QPointF & pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal tangentialPressure, qreal perspective, qreal time, qreal speed); KisPaintInformation(const QPointF & pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation); KisPaintInformation(const QPointF & pos = QPointF(), qreal pressure = PRESSURE_DEFAULT); KisPaintInformation(const KisPaintInformation& rhs); void operator=(const KisPaintInformation& rhs); ~KisPaintInformation(); template void paintAt(PaintOp &op, KisDistanceInformation *distanceInfo) { KisSpacingInformation spacingInfo; KisTimingInformation timingInfo; { DistanceInformationRegistrar r = registerDistanceInformation(distanceInfo); spacingInfo = op.paintAt(*this); timingInfo = op.updateTimingImpl(*this); // Initiate the process of locking the drawing angle. The locked value will // always be present in the internals, but it will be requested but the users // with a special parameter of drawingAngle() only. if (!this->isHoveringMode()) { distanceInfo->lockCurrentDrawingAngle(*this); } } distanceInfo->registerPaintedDab(*this, spacingInfo, timingInfo); } const QPointF& pos() const; void setPos(const QPointF& p); /// The pressure of the value (from 0.0 to 1.0) qreal pressure() const; /// Set the pressure void setPressure(qreal p); /// The tilt of the pen on the horizontal axis (from 0.0 to 1.0) qreal xTilt() const; /// The tilt of the pen on the vertical axis (from 0.0 to 1.0) qreal yTilt() const; /// XXX !!! :-| Please add dox! void overrideDrawingAngle(qreal angle); /// XXX !!! :-| Please add dox! qreal drawingAngleSafe(const KisDistanceInformation &distance) const; /** * Causes the specified distance information to be temporarily registered with this * KisPaintInformation object, so that the KisPaintInformation can compute certain values that * may be needed at painting time, such as the drawing direction. When the returned object is * destroyed, the KisDistanceInformation will be unregistered. At most one * KisDistanceInformation can be registered with a given KisPaintInformation at a time. */ DistanceInformationRegistrar registerDistanceInformation(KisDistanceInformation *distance); /** * Current brush direction computed from the cursor movement * * WARNING: this method is available *only* inside paintAt() call, * that is when the distance information is registered. */ qreal drawingAngle(bool considerLockedAngle = false) const; /** * Current brush direction vector computed from the cursor movement * * WARNING: this method is available *only* inside paintAt() call, * that is when the distance information is registered. */ QPointF drawingDirectionVector() const; /** * Current brush speed computed from the cursor movement * * WARNING: this method is available *only* inside paintAt() call, * that is when the distance information is registered. */ qreal drawingSpeed() const; /** * Current distance from the previous dab * * WARNING: this method is available *only* inside paintAt() call, * that is when the distance information is registered. */ qreal drawingDistance() const; /// rotation as given by the tablet event qreal rotation() const; /// tangential pressure (i.e., rate for an airbrush device) qreal tangentialPressure() const; /// reciprocal of distance on the perspective grid qreal perspective() const; /// Number of ms since the beginning of the stroke qreal currentTime() const; /// Number of dabs painted since the beginning of the stroke int currentDabSeqNo() const; /// The length of the stroke **before** painting the current dab qreal totalStrokeLength() const; // random source for generating in-stroke effects KisRandomSourceSP randomSource() const; // the stroke should initialize random source of all the used // paint info objects, otherwise it shows a warning void setRandomSource(KisRandomSourceSP value); // random source for generating in-stroke effects, generates one(!) value per stroke KisPerStrokeRandomSourceSP perStrokeRandomSource() const; // the stroke should initialize per stroke random source of all the used // paint info objects, otherwise it shows a warning void setPerStrokeRandomSource(KisPerStrokeRandomSourceSP value); // set level of detail which info object has been generated for void setLevelOfDetail(int levelOfDetail); /** * The paint information may be generated not only during real * stroke when the actual painting is happening, but also when the * cursor is hovering the canvas. In this mode some of the sensors * work a bit differently. The most outstanding example is Fuzzy * sensor, which returns unit value in this mode, otherwise it is * too irritating for a user. * * This value is true only for paint information objects created with * createHoveringModeInfo() constructor. * * \see createHoveringModeInfo() */ bool isHoveringMode() const; /** * Create a fake info object with isHoveringMode() property set to * true. * * \see isHoveringMode() */ static KisPaintInformation createHoveringModeInfo(const QPointF &pos, qreal pressure = PRESSURE_DEFAULT, qreal xTilt = 0.0, qreal yTilt = 0.0, qreal rotation = 0.0, qreal tangentialPressure = 0.0, qreal perspective = 1.0, qreal speed = 0.0, - int canvasrotation = 0, - bool canvasMirroredH = false); + qreal canvasrotation = 0, + bool canvasMirroredH = false, + bool canvasMirroredV = false); + /** - *Returns the canvas rotation if that has been given to the kispaintinformation. + * Returns the canvas rotation if that has been given to the kispaintinformation. */ - int canvasRotation() const; + qreal canvasRotation() const; + /** - *set the canvas rotation. + * Set the canvas rotation. */ - void setCanvasRotation(int rotation); + void setCanvasRotation(qreal rotation); - /* - *Whether the canvas is mirrored for the paint-operation. + /** + * Whether the canvas is horizontally mirrored for the paint-operation. */ bool canvasMirroredH() const; - /* - *Set whether the canvas is mirrored for the paint-operation. + /** + * Set whether the canvas is horizontally mirrored for the paint-operation. + */ + void setCanvasMirroredH(bool value); + + /** + * Whether the canvas is vertically mirrored for the paint-operation. + */ + bool canvasMirroredV() const; + + /** + * Set whether the canvas is vertically mirrored for the paint-operation. */ - void setCanvasHorizontalMirrorState(bool mir); + void setCanvasMirroredV(bool value); void toXML(QDomDocument&, QDomElement&) const; static KisPaintInformation fromXML(const QDomElement&); // TODO: Refactor the static mix functions to non-static in-place mutation // versions like mixOtherOnlyPosition and mixOtherWithoutTime. // Heap allocation on Windows is awfully slow and will fragment the memory // badly. Since KisPaintInformation allocates on the heap, we should re-use // existing instance whenever possible, especially in loops. // Ref: https://phabricator.kde.org/D6578 /// (1-t) * p1 + t * p2 static KisPaintInformation mixOnlyPosition(qreal t, const KisPaintInformation& mixedPi, const KisPaintInformation& basePi); static KisPaintInformation mix(const QPointF& p, qreal t, const KisPaintInformation& p1, const KisPaintInformation& p2); static KisPaintInformation mix(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2); static KisPaintInformation mixWithoutTime(const QPointF &p, qreal t, const KisPaintInformation &p1, const KisPaintInformation &p2); static KisPaintInformation mixWithoutTime(qreal t, const KisPaintInformation &pi1, const KisPaintInformation &pi2); void mixOtherOnlyPosition(qreal t, const KisPaintInformation& other); void mixOtherWithoutTime(qreal t, const KisPaintInformation& other); static qreal tiltDirection(const KisPaintInformation& info, bool normalize = true); static qreal tiltElevation(const KisPaintInformation& info, qreal maxTiltX = 60.0, qreal maxTiltY = 60.0, bool normalize = true); private: static KisPaintInformation mixImpl(const QPointF &p, qreal t, const KisPaintInformation &p1, const KisPaintInformation &p2, bool posOnly, bool mixTime); void mixOtherImpl(const QPointF &p, qreal t, const KisPaintInformation &other, bool posOnly, bool mixTime); private: struct Private; Private* const d; }; KRITAIMAGE_EXPORT QDebug operator<<(QDebug debug, const KisPaintInformation& info); #endif diff --git a/libs/image/brushengine/kis_paintop_settings.cpp b/libs/image/brushengine/kis_paintop_settings.cpp index cd5686f13e..0fad1af607 100644 --- a/libs/image/brushengine/kis_paintop_settings.cpp +++ b/libs/image/brushengine/kis_paintop_settings.cpp @@ -1,518 +1,499 @@ /* * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2008 Lukáš Tvrdý * Copyright (c) 2014 Mohit Goyal * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "kis_paintop_preset.h" #include "kis_paint_layer.h" #include "kis_image.h" #include "kis_painter.h" #include "kis_paint_device.h" #include "kis_paintop_registry.h" #include "kis_timing_information.h" #include #include "kis_paintop_config_widget.h" #include #include "kis_paintop_settings_update_proxy.h" #include #include #include #include #include #include "KisPaintopSettingsIds.h" struct Q_DECL_HIDDEN KisPaintOpSettings::Private { Private() : disableDirtyNotifications(false) {} QPointer settingsWidget; QString modelName; QPointer updateProxy; QList uniformProperties; bool disableDirtyNotifications; class DirtyNotificationsLocker { public: DirtyNotificationsLocker(KisPaintOpSettings::Private *d) : m_d(d), m_oldNotificationsState(d->disableDirtyNotifications) { m_d->disableDirtyNotifications = true; } ~DirtyNotificationsLocker() { m_d->disableDirtyNotifications = m_oldNotificationsState; } private: KisPaintOpSettings::Private *m_d; bool m_oldNotificationsState; Q_DISABLE_COPY(DirtyNotificationsLocker) }; }; KisPaintOpSettings::KisPaintOpSettings() : d(new Private) { d->updateProxy = 0; } KisPaintOpSettings::~KisPaintOpSettings() { } KisPaintOpSettings::KisPaintOpSettings(const KisPaintOpSettings &rhs) : KisPropertiesConfiguration(rhs) , d(new Private) { d->settingsWidget = 0; d->updateProxy = rhs.updateProxy(); d->modelName = rhs.modelName(); } void KisPaintOpSettings::setOptionsWidget(KisPaintOpConfigWidget* widget) { d->settingsWidget = widget; } void KisPaintOpSettings::setUpdateProxy(const QPointer proxy) { d->updateProxy = proxy; } QPointer KisPaintOpSettings::updateProxy() const { return d->updateProxy; } bool KisPaintOpSettings::mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode) { Q_UNUSED(modifiers); Q_UNUSED(currentNode); setRandomOffset(paintInformation); return true; // ignore the event by default } bool KisPaintOpSettings::mouseReleaseEvent() { return true; // ignore the event by default } void KisPaintOpSettings::setRandomOffset(const KisPaintInformation &paintInformation) { if (getBool("Texture/Pattern/Enabled")) { if (getBool("Texture/Pattern/isRandomOffsetX")) { setProperty("Texture/Pattern/OffsetX", paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetX"))); } if (getBool("Texture/Pattern/isRandomOffsetY")) { setProperty("Texture/Pattern/OffsetY", paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetY"))); } } } bool KisPaintOpSettings::hasMaskingSettings() const { return getBool(KisPaintOpUtils::MaskingBrushEnabledTag, false); } KisPaintOpSettingsSP KisPaintOpSettings::createMaskingSettings() const { if (!hasMaskingSettings()) return KisPaintOpSettingsSP(); const KoID pixelBrushId(KisPaintOpUtils::MaskingBrushPaintOpId, QString()); KisPaintOpSettingsSP maskingSettings = KisPaintOpRegistry::instance()->createSettings(pixelBrushId); this->getPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, maskingSettings); const bool useMasterSize = this->getBool(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, true); if (useMasterSize) { const qreal masterSizeCoeff = getDouble(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, 1.0); maskingSettings->setPaintOpSize(masterSizeCoeff * paintOpSize()); } return maskingSettings; } QString KisPaintOpSettings::maskingBrushCompositeOp() const { return getString(KisPaintOpUtils::MaskingBrushCompositeOpTag, COMPOSITE_MULT); } KisPaintOpSettingsSP KisPaintOpSettings::clone() const { QString paintopID = getString("paintop"); if (paintopID.isEmpty()) return 0; KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->createSettings(KoID(paintopID)); QMapIterator i(getProperties()); while (i.hasNext()) { i.next(); settings->setProperty(i.key(), QVariant(i.value())); } settings->setUpdateProxy(this->updateProxy()); return settings; } void KisPaintOpSettings::resetSettings(const QStringList &preserveProperties) { QStringList allKeys = preserveProperties; allKeys << "paintop"; QHash preserved; Q_FOREACH (const QString &key, allKeys) { if (hasProperty(key)) { preserved[key] = getProperty(key); } } clearProperties(); for (auto it = preserved.constBegin(); it != preserved.constEnd(); ++it) { setProperty(it.key(), it.value()); } } void KisPaintOpSettings::activate() { } void KisPaintOpSettings::setPaintOpOpacity(qreal value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("OpacityValue", value); } void KisPaintOpSettings::setPaintOpFlow(qreal value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("FlowValue", value); } void KisPaintOpSettings::setPaintOpCompositeOp(const QString &value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("CompositeOp", value); } qreal KisPaintOpSettings::paintOpOpacity() { KisLockedPropertiesProxySP proxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this); return proxy->getDouble("OpacityValue", 1.0); } qreal KisPaintOpSettings::paintOpFlow() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getDouble("FlowValue", 1.0); } QString KisPaintOpSettings::paintOpCompositeOp() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getString("CompositeOp", COMPOSITE_OVER); } void KisPaintOpSettings::setEraserMode(bool value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("EraserMode", value); } bool KisPaintOpSettings::eraserMode() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getBool("EraserMode", false); } QString KisPaintOpSettings::effectivePaintOpCompositeOp() { return !eraserMode() ? paintOpCompositeOp() : COMPOSITE_ERASE; } qreal KisPaintOpSettings::savedEraserSize() const { return getDouble("SavedEraserSize", 0.0); } void KisPaintOpSettings::setSavedEraserSize(qreal value) { setProperty("SavedEraserSize", value); setPropertyNotSaved("SavedEraserSize"); } qreal KisPaintOpSettings::savedBrushSize() const { return getDouble("SavedBrushSize", 0.0); } void KisPaintOpSettings::setSavedBrushSize(qreal value) { setProperty("SavedBrushSize", value); setPropertyNotSaved("SavedBrushSize"); } qreal KisPaintOpSettings::savedEraserOpacity() const { return getDouble("SavedEraserOpacity", 0.0); } void KisPaintOpSettings::setSavedEraserOpacity(qreal value) { setProperty("SavedEraserOpacity", value); setPropertyNotSaved("SavedEraserOpacity"); } qreal KisPaintOpSettings::savedBrushOpacity() const { return getDouble("SavedBrushOpacity", 0.0); } void KisPaintOpSettings::setSavedBrushOpacity(qreal value) { setProperty("SavedBrushOpacity", value); setPropertyNotSaved("SavedBrushOpacity"); } QString KisPaintOpSettings::modelName() const { return d->modelName; } void KisPaintOpSettings::setModelName(const QString & modelName) { d->modelName = modelName; } KisPaintOpConfigWidget* KisPaintOpSettings::optionsWidget() const { if (d->settingsWidget.isNull()) return 0; return d->settingsWidget.data(); } bool KisPaintOpSettings::isValid() const { return true; } bool KisPaintOpSettings::isLoadable() { return isValid(); } QString KisPaintOpSettings::indirectPaintingCompositeOp() const { return COMPOSITE_ALPHA_DARKEN; } bool KisPaintOpSettings::isAirbrushing() const { return getBool(AIRBRUSH_ENABLED, false); } qreal KisPaintOpSettings::airbrushInterval() const { qreal rate = getDouble(AIRBRUSH_RATE, 1.0); if (rate == 0.0) { return LONG_TIME; } else { return 1000.0 / rate; } } bool KisPaintOpSettings::useSpacingUpdates() const { return getBool(SPACING_USE_UPDATES, false); } bool KisPaintOpSettings::needsAsynchronousUpdates() const { return false; } QPainterPath KisPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode) { QPainterPath path; if (mode.isVisible) { path = ellipseOutline(10, 10, 1.0, 0); if (mode.showTiltDecoration) { path.addPath(makeTiltIndicator(info, QPointF(0.0, 0.0), 0.0, 2.0)); } path.translate(info.pos()); } return path; } QPainterPath KisPaintOpSettings::ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation) { QPainterPath path; QRectF ellipse(0, 0, width * scale, height * scale); ellipse.translate(-ellipse.center()); path.addEllipse(ellipse); QTransform m; m.reset(); m.rotate(rotation); path = m.map(path); return path; } QPainterPath KisPaintOpSettings::makeTiltIndicator(KisPaintInformation const& info, QPointF const& start, qreal maxLength, qreal angle) { if (maxLength == 0.0) maxLength = 50.0; maxLength = qMax(maxLength, 50.0); qreal const length = maxLength * (1 - info.tiltElevation(info, 60.0, 60.0, true)); qreal const baseAngle = 360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0); QLineF guideLine = QLineF::fromPolar(length, baseAngle + angle); guideLine.translate(start); QPainterPath ret; ret.moveTo(guideLine.p1()); ret.lineTo(guideLine.p2()); guideLine.setAngle(baseAngle - angle); ret.lineTo(guideLine.p2()); ret.lineTo(guideLine.p1()); return ret; } -void KisPaintOpSettings::setCanvasRotation(qreal angle) -{ - Private::DirtyNotificationsLocker locker(d.data()); - - setProperty("runtimeCanvasRotation", angle); - setPropertyNotSaved("runtimeCanvasRotation"); -} - -void KisPaintOpSettings::setCanvasMirroring(bool xAxisMirrored, bool yAxisMirrored) -{ - Private::DirtyNotificationsLocker locker(d.data()); - - setProperty("runtimeCanvasMirroredX", xAxisMirrored); - setPropertyNotSaved("runtimeCanvasMirroredX"); - - setProperty("runtimeCanvasMirroredY", yAxisMirrored); - setPropertyNotSaved("runtimeCanvasMirroredY"); -} - void KisPaintOpSettings::setProperty(const QString & name, const QVariant & value) { if (value != KisPropertiesConfiguration::getProperty(name) && !d->disableDirtyNotifications) { if (d->updateProxy) { d->updateProxy->setDirty(true); } } KisPropertiesConfiguration::setProperty(name, value); onPropertyChanged(); } void KisPaintOpSettings::onPropertyChanged() { if (d->updateProxy) { d->updateProxy->notifySettingsChanged(); } } bool KisPaintOpSettings::isLodUserAllowed(const KisPropertiesConfigurationSP config) { return config->getBool("lodUserAllowed", true); } void KisPaintOpSettings::setLodUserAllowed(KisPropertiesConfigurationSP config, bool value) { config->setProperty("lodUserAllowed", value); } bool KisPaintOpSettings::lodSizeThresholdSupported() const { return true; } qreal KisPaintOpSettings::lodSizeThreshold() const { return getDouble("lodSizeThreshold", 100.0); } void KisPaintOpSettings::setLodSizeThreshold(qreal value) { setProperty("lodSizeThreshold", value); } #include "kis_standard_uniform_properties_factory.h" QList KisPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(d->uniformProperties); if (props.isEmpty()) { using namespace KisStandardUniformPropertiesFactory; props.append(createProperty(opacity, settings, d->updateProxy)); props.append(createProperty(size, settings, d->updateProxy)); props.append(createProperty(flow, settings, d->updateProxy)); d->uniformProperties = listStrongToWeak(props); } return props; } diff --git a/libs/image/brushengine/kis_paintop_settings.h b/libs/image/brushengine/kis_paintop_settings.h index 1fee734533..64f839f3f2 100644 --- a/libs/image/brushengine/kis_paintop_settings.h +++ b/libs/image/brushengine/kis_paintop_settings.h @@ -1,358 +1,349 @@ /* * 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_PAINTOP_SETTINGS_H_ #define KIS_PAINTOP_SETTINGS_H_ #include "kis_types.h" #include "kritaimage_export.h" #include #include #include "kis_properties_configuration.h" #include #include class KisPaintOpConfigWidget; class KisPaintopSettingsUpdateProxy; /** * Configuration property used to control whether airbrushing is enabled. */ const QString AIRBRUSH_ENABLED = "PaintOpSettings/isAirbrushing"; /** * Configuration property used to control airbrushing rate. The value should be in dabs per second. */ const QString AIRBRUSH_RATE = "PaintOpSettings/rate"; /** * Configuration property used to control whether airbrushing is configured to ignore distance-based * spacing. */ const QString AIRBRUSH_IGNORE_SPACING = "PaintOpSettings/ignoreSpacing"; /** * Configuration property used to control whether the spacing settings can be updated between * painted dabs. */ const QString SPACING_USE_UPDATES = "PaintOpSettings/updateSpacingBetweenDabs"; /** * This class is used to cache the settings for a paintop * between two creations. There is one KisPaintOpSettings per input device (mouse, tablet, * etc...). * * The settings may be stored in a preset. Note that if your * paintop's settings subclass has data that is not stored as a property, that data is not * saved and restored. * * The object also contains a pointer to its parent KisPaintOpPreset object.This is to control the DirtyPreset * property of KisPaintOpPreset. Whenever the settings are changed/modified from the original -- the preset is * set to dirty. */ class KRITAIMAGE_EXPORT KisPaintOpSettings : public KisPropertiesConfiguration { public: KisPaintOpSettings(); ~KisPaintOpSettings() override; KisPaintOpSettings(const KisPaintOpSettings &rhs); /** * */ void setOptionsWidget(KisPaintOpConfigWidget* widget); /** * This function is called by a tool when the mouse is pressed. It's useful if * the paintop needs mouse interaction for instance in the case of the clone op. * If the tool is supposed to ignore the event, the paint op should return true * and if the tool is supposed to use the event, return false. * See kis_tool_freehand:tryPickByPaintOp() */ virtual bool mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode); /** * This function is called by a tool when the mouse is released. It's useful if * the paintop needs mouse interaction for instance in the case of the clone op. * If the tool is supposed to ignore the event, the paint op should return true * and if the tool is supposed to use the event, return false. */ virtual bool mouseReleaseEvent(); /** * Clone the current settings object. Override this if your settings instance doesn't * store everything as properties. */ virtual KisPaintOpSettingsSP clone() const; /** * Removes all the settings from the object while keeping the paintop id, * which is loaded to the object by the factory */ void resetSettings(const QStringList &preserveProperties = QStringList()); /** * @return the node the paintop is working on. */ KisNodeSP node() const; /** * Call this function when the paint op is selected or the tool is activated */ virtual void activate(); /** * XXX: Remove this after 2.0, when the paint operation (incremental/non incremental) will * be completely handled in the paintop, not in the tool. This is a filthy hack to move * the option to the right place, at least. * @return true if we paint incrementally, false if we paint like Photoshop. By default, paintops * do not support non-incremental. */ virtual bool paintIncremental() { return true; } /** * @return the composite op it to which the indirect painting device * should be initialized to. This is used by clone op to reset * the composite op to COMPOSITE_COPY */ virtual QString indirectPaintingCompositeOp() const; /** * Whether this paintop wants to deposit paint even when not moving, i.e. the tool needs to * activate its timer. If this is true, painting updates need to be generated at regular * intervals even in the absence of input device events, e.g. when the cursor is not moving. * * The default implementation checks the property AIRBRUSH_ENABLED, defaulting to false if the * property is not found. This should be suitable for most paintops. */ virtual bool isAirbrushing() const; /** * Indicates the minimum time interval that might be needed between airbrush dabs, in * milliseconds. A lower value means painting updates need to happen more frequently. This value * should be ignored if isAirbrushing() is false. * * The default implementation uses the property AIRBRUSH_RATE, defaulting to an interval of * one second if the property is not found. This should be suitable for most paintops. */ virtual qreal airbrushInterval() const; /** * Indicates whether this configuration allows spacing information to be updated between painted * dabs during a stroke. */ virtual bool useSpacingUpdates() const; /** * Indicates if the tool should call paintOp->doAsynchronousUpdate() inbetween * paintAt() calls to do the asynchronous rendering */ virtual bool needsAsynchronousUpdates() const; /** * This structure defines the current mode for painting an outline. */ struct OutlineMode { bool isVisible = false; bool forceCircle = false; bool showTiltDecoration = false; bool forceFullSize = false; }; /** * Returns the brush outline in pixel coordinates. Tool is responsible for conversion into view coordinates. * Outline mode has to be passed to the paintop which builds the outline as some paintops have to paint outline * always like clone paintop indicating the duplicate position */ virtual QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode); /** * Helpers for drawing the brush outline */ static QPainterPath ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation); /** * Helper for drawing a triangle representing the tilt of the stylus. * * @param start is the offset from the brush's outline's bounding box * @param lengthScale is used for deciding the size of the triangle. * Brush diameter or width are common choices for this. * @param angle is the angle between the two sides of the triangle. */ static QPainterPath makeTiltIndicator(KisPaintInformation const& info, QPointF const& start, qreal lengthScale, qreal angle); /** * Set paintop opacity directly in the properties */ void setPaintOpOpacity(qreal value); /** * Set paintop flow directly in the properties */ void setPaintOpFlow(qreal value); /** * Set paintop composite mode directly in the properties */ void setPaintOpCompositeOp(const QString &value); /** * @return opacity saved in the properties */ qreal paintOpOpacity(); /** * @return flow saved in the properties */ qreal paintOpFlow(); /** * @return composite mode saved in the properties */ QString paintOpCompositeOp(); /** * Set paintop size directly in the properties */ virtual void setPaintOpSize(qreal value) = 0; /** * @return size saved in the properties */ virtual qreal paintOpSize() const = 0; void setEraserMode(bool value); bool eraserMode(); qreal savedEraserSize() const; void setSavedEraserSize(qreal value); qreal savedBrushSize() const; void setSavedBrushSize(qreal value); qreal savedEraserOpacity() const; void setSavedEraserOpacity(qreal value); qreal savedBrushOpacity() const; void setSavedBrushOpacity(qreal value); QString effectivePaintOpCompositeOp(); void setUpdateProxy(const QPointer proxy); QPointer updateProxy() const; /** * @return filename of the 3D brush model, empty if no brush is set */ virtual QString modelName() const; /** * Set filename of 3D brush model. By default no brush is set */ void setModelName(const QString & modelName); /// Check if the settings are valid, setting might be invalid through missing brushes etc /// Overwrite if the settings of a paintop can be invalid /// @return state of the settings, default implementation is true virtual bool isValid() const; /// Check if the settings are loadable, that might the case if we can fallback to something /// Overwrite if the settings can do some kind of fallback /// @return loadable state of the settings, by default implementation return the same as isValid() virtual bool isLoadable(); - /** - * These methods are populating properties with runtime - * information about canvas rotation/mirroring. This information - * is set directly by KisToolFreehand. Later the data is accessed - * by the pressure options to make a final decision. - */ - void setCanvasRotation(qreal angle); - void setCanvasMirroring(bool xAxisMirrored, bool yAxisMirrored); - /** * Overrides the method in KisPropertiesCofiguration to allow * onPropertyChanged() callback */ void setProperty(const QString & name, const QVariant & value) override; virtual QList uniformProperties(KisPaintOpSettingsSP settings); static bool isLodUserAllowed(const KisPropertiesConfigurationSP config); static void setLodUserAllowed(KisPropertiesConfigurationSP config, bool value); virtual bool lodSizeThresholdSupported() const; qreal lodSizeThreshold() const; void setLodSizeThreshold(qreal value); /** * @return the option widget of the paintop (can be 0 is no option widgets is set) */ KisPaintOpConfigWidget* optionsWidget() const; /** * This function is called to set random offsets to the brush whenever the mouse is clicked. It is * specific to when the pattern option is set. * */ virtual void setRandomOffset(const KisPaintInformation &paintInformation); /** * @return true if this preset demands a secondary masked brush running * alongside it */ bool hasMaskingSettings() const; /** * @return a newly created settings object representing a preset of the masking * brush that should be run alongside the current brush */ KisPaintOpSettingsSP createMaskingSettings() const; /** * @return a composite op id of the masked brush rendering algorithm. * * Please take into account that the brush itself always paints in alpha- * darken mode, but the final result is combined with this composite op. */ QString maskingBrushCompositeOp() const; protected: /** * The callback is called every time when a property changes */ virtual void onPropertyChanged(); private: struct Private; const QScopedPointer d; }; #endif diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index fe6e1fe794..64ba9e8ffc 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,613 +1,612 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) find_library(APPKIT_LIBRARY AppKit) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp - canvas/kis_paintop_transformation_connector.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp canvas/KisSnapPointStrategy.cpp canvas/KisSnapPixelStrategy.cpp canvas/KisMirrorAxisConfig.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_stroke_selection_properties.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp dialogs/KisSessionManagerDialog.cpp dialogs/KisNewWindowLayoutDialog.cpp dialogs/KisDlgChangeCloneSource.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp brushhud/kis_uniform_paintop_property_widget.cpp brushhud/kis_brush_hud.cpp brushhud/kis_round_hud_button.cpp brushhud/kis_dlg_brush_hud_config.cpp brushhud/kis_brush_hud_properties_list.cpp brushhud/kis_brush_hud_properties_config.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc KisPaintopPropertiesBase.cpp kis_canvas_resource_provider.cpp kis_derived_resources.cpp kis_categories_mapper.cpp kis_categorized_list_model.cpp kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc KisOcioConfiguration.cpp kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp kis_cursor.cc kis_cursor_cache.cpp kis_custom_pattern.cc kis_file_layer.cpp kis_change_file_layer_command.h kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc KisImageBarrierLockerWithFeedback.cpp kis_image_manager.cc kis_image_view_converter.cpp kis_import_catcher.cc kis_layer_manager.cc kis_mask_manager.cc kis_mimedata.cpp kis_node_commands_adapter.cpp kis_node_manager.cpp kis_node_juggler_compressed.cpp kis_node_selection_adapter.cpp kis_node_insertion_adapter.cpp KisNodeDisplayModeAdapter.cpp kis_node_model.cpp kis_node_filter_proxy_model.cpp kis_model_index_converter_base.cpp kis_model_index_converter.cpp kis_model_index_converter_show_all.cpp kis_painting_assistant.cc kis_painting_assistants_decoration.cpp KisDecorationsManager.cpp kis_paintop_box.cc kis_paintop_option.cpp kis_paintop_options_model.cpp kis_paintop_settings_widget.cpp kis_popup_palette.cpp kis_png_converter.cpp kis_preference_set_registry.cpp KisResourceServerProvider.cpp KisSelectedShapesProxy.cpp kis_selection_decoration.cc kis_selection_manager.cc KisSelectionActionsAdapter.cpp kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp KisActionPlugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_stopgradient_editor.cpp KisWelcomePageWidget.cpp KisChangeCloneLayersCommand.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp kisexiv2/kis_iptc_io.cpp kisexiv2/kis_xmp_io.cpp opengl/kis_opengl.cpp opengl/kis_opengl_canvas2.cpp opengl/kis_opengl_canvas_debugger.cpp opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_opengl_shader_loader.cpp opengl/kis_texture_tile_info_pool.cpp opengl/KisOpenGLUpdateInfoBuilder.cpp opengl/KisOpenGLModeProber.cpp opengl/KisScreenInformationAdapter.cpp kis_fps_decoration.cpp tool/KisToolChangesTracker.cpp tool/KisToolChangesTrackerData.cpp tool/kis_selection_tool_helper.cpp tool/kis_selection_tool_config_widget_helper.cpp tool/kis_rectangle_constraint_widget.cpp tool/kis_shape_tool_helper.cpp tool/kis_tool.cc tool/kis_delegated_tool_policies.cpp tool/kis_tool_freehand.cc tool/kis_speed_smoother.cpp tool/kis_painting_information_builder.cpp tool/kis_stabilized_events_sampler.cpp tool/kis_tool_freehand_helper.cpp tool/kis_tool_multihand_helper.cpp tool/kis_figure_painting_tool_helper.cpp tool/kis_tool_paint.cc tool/kis_tool_shape.cc tool/kis_tool_ellipse_base.cpp tool/kis_tool_rectangle_base.cpp tool/kis_tool_polyline_base.cpp tool/kis_tool_utils.cpp tool/kis_resources_snapshot.cpp tool/kis_smoothing_options.cpp tool/KisStabilizerDelayedPaintHelper.cpp tool/KisStrokeSpeedMonitor.cpp tool/strokes/freehand_stroke.cpp tool/strokes/KisStrokeEfficiencyMeasurer.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp tool/strokes/KisFreehandStrokeInfo.cpp tool/strokes/KisMaskedFreehandStrokePainter.cpp tool/strokes/KisMaskingBrushRenderer.cpp tool/strokes/KisMaskingBrushCompositeOpFactory.cpp tool/strokes/move_stroke_strategy.cpp tool/KisSelectionToolFactoryBase.cpp tool/KisToolPaintFactoryBase.cpp widgets/kis_cmb_composite.cc widgets/kis_cmb_contour.cpp widgets/kis_cmb_gradient.cpp widgets/kis_paintop_list_widget.cpp widgets/kis_cmb_idlist.cc widgets/kis_color_space_selector.cc widgets/kis_advanced_color_space_selector.cc widgets/kis_cie_tongue_widget.cpp widgets/kis_tone_curve_widget.cpp widgets/kis_curve_widget.cpp widgets/kis_custom_image_widget.cc widgets/kis_image_from_clipboard_widget.cpp widgets/kis_double_widget.cc widgets/kis_filter_selector_widget.cc widgets/kis_gradient_chooser.cc widgets/kis_iconwidget.cc widgets/kis_mask_widgets.cpp widgets/kis_meta_data_merge_strategy_chooser_widget.cc widgets/kis_multi_bool_filter_widget.cc widgets/kis_multi_double_filter_widget.cc widgets/kis_multi_integer_filter_widget.cc widgets/kis_multipliers_double_slider_spinbox.cpp widgets/kis_paintop_presets_popup.cpp widgets/kis_tool_options_popup.cpp widgets/kis_paintop_presets_chooser_popup.cpp widgets/kis_paintop_presets_save.cpp widgets/kis_paintop_preset_icon_library.cpp widgets/kis_pattern_chooser.cc widgets/kis_preset_chooser.cpp widgets/kis_progress_widget.cpp widgets/kis_selection_options.cc widgets/kis_scratch_pad.cpp widgets/kis_scratch_pad_event_filter.cpp widgets/kis_preset_selector_strip.cpp widgets/kis_slider_spin_box.cpp widgets/KisSelectionPropertySlider.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.cpp widgets/kis_categorized_list_view.cpp widgets/kis_widget_chooser.cpp widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp widgets/kis_lod_availability_widget.cpp widgets/kis_color_label_selector_widget.cpp widgets/kis_color_filter_combo.cpp widgets/kis_elided_label.cpp widgets/kis_stopgradient_slider_widget.cpp widgets/kis_preset_live_preview_view.cpp widgets/KisScreenColorPicker.cpp widgets/KoDualColorButton.cpp widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp widgets/KisLayerStyleAngleSelector.cpp widgets/KisMemoryReportButton.cpp widgets/KisDitherWidget.cpp KisPaletteEditor.cpp dialogs/KisDlgPaletteEditor.cpp widgets/KisNewsWidget.cpp widgets/KisGamutMaskToolbar.cpp utils/kis_document_aware_spin_box_unit_manager.cpp utils/KisSpinBoxSplineUnitConverter.cpp utils/KisClipboardUtil.cpp utils/KisDitherUtil.cpp input/kis_input_manager.cpp input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp input/kis_pan_action.cpp input/kis_alternate_invocation_action.cpp input/kis_rotate_canvas_action.cpp input/kis_zoom_action.cpp input/kis_change_frame_action.cpp input/kis_gamma_exposure_action.cpp input/kis_show_palette_action.cpp input/kis_change_primary_setting_action.cpp input/kis_abstract_shortcut.cpp input/kis_native_gesture_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp input/KisQtWidgetsTweaker.cpp input/KisInputActionGroup.cpp input/kis_zoom_and_rotate_action.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp actions/KisPasteActionFactories.cpp actions/KisTransformToolActivationCommand.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp input/kis_input_profile_manager.cpp input/kis_input_profile.cpp input/kis_shortcut_configuration.cpp input/config/kis_input_configuration_page.cpp input/config/kis_edit_profiles_dialog.cpp input/config/kis_input_profile_model.cpp input/config/kis_input_configuration_page_item.cpp input/config/kis_action_shortcuts_model.cpp input/config/kis_input_type_delegate.cpp input/config/kis_input_mode_delegate.cpp input/config/kis_input_button.cpp input/config/kis_input_editor_delegate.cpp input/config/kis_mouse_input_editor.cpp input/config/kis_wheel_input_editor.cpp input/config/kis_key_input_editor.cpp processing/fill_processing_visitor.cpp kis_asl_layer_style_serializer.cpp kis_psd_layer_style_resource.cpp canvas/kis_mirror_axis.cpp kis_abstract_perspective_grid.cpp KisApplication.cpp KisAutoSaveRecoveryDialog.cpp KisDetailsPane.cpp KisDocument.cpp KisCloneDocumentStroke.cpp kis_node_view_color_scheme.cpp KisImportExportFilter.cpp KisImportExportManager.cpp KisImportExportUtils.cpp kis_async_action_feedback.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoActionsUpdateManager.cpp KisView.cpp KisImportExportErrorCode.cpp KisImportExportAdditionalChecks.cpp thememanager.cpp kis_mainwindow_observer.cpp KisViewManager.cpp kis_mirror_manager.cpp qtlockedfile/qtlockedfile.cpp qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp KisResourceBundle.cpp KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp KisNetworkAccessManager.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisSaveGroupVisitor.cpp KisWindowLayoutResource.cpp KisWindowLayoutManager.cpp KisSessionResource.cpp KisReferenceImagesDecoration.cpp KisReferenceImage.cpp flake/KisReferenceImagesLayer.cpp flake/KisReferenceImagesLayer.h KisMouseClickEater.cpp ) if(WIN32) # Private headers are needed for: # * KisDlgCustomTabletResolution # * KisScreenInformationAdapter include_directories(SYSTEM ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} qtlockedfile/qtlockedfile_win.cpp ) if (NOT USE_QT_TABLET_WINDOWS) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp input/wintab/kis_tablet_support_win8.cpp ) else() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} dialogs/KisDlgCustomTabletResolution.cpp ) endif() endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp KisAsyncAnimationRendererBase.cpp KisAsyncAnimationCacheRenderer.cpp KisAsyncAnimationFramesSavingRenderer.cpp dialogs/KisAsyncAnimationRenderDialogBase.cpp dialogs/KisAsyncAnimationCacheRenderDialog.cpp dialogs/KisAsyncAnimationFramesSaveDialog.cpp canvas/kis_animation_player.cpp kis_animation_importer.cpp KisSyncedAudioPlayback.cpp KisFrameDataSerializer.cpp KisFrameCacheStore.cpp KisFrameCacheSwapper.cpp KisAbstractFrameCacheSwapper.cpp KisInMemoryFrameCacheSwapper.cpp input/wintab/drawpile_tablettester/tablettester.cpp input/wintab/drawpile_tablettester/tablettest.cpp ) if (UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} qtlockedfile/qtlockedfile_unix.cpp ) endif() if(APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} osx.mm ) endif() ki18n_wrap_ui(kritaui_LIB_SRCS widgets/KoFillConfigWidget.ui widgets/KoStrokeConfigWidget.ui widgets/KisDitherWidget.ui forms/wdgdlgpngimport.ui forms/wdgfullscreensettings.ui forms/wdgautogradient.ui forms/wdggeneralsettings.ui forms/wdgperformancesettings.ui forms/wdggenerators.ui forms/wdgbookmarkedconfigurationseditor.ui forms/wdgapplyprofile.ui forms/wdgcustompattern.ui forms/wdglayerproperties.ui forms/wdgcolorsettings.ui forms/wdgtabletsettings.ui forms/wdgcolorspaceselector.ui forms/wdgcolorspaceselectoradvanced.ui forms/wdgdisplaysettings.ui forms/kis_previewwidgetbase.ui forms/kis_matrix_widget.ui forms/wdgselectionoptions.ui forms/wdggeometryoptions.ui forms/wdgnewimage.ui forms/wdgimageproperties.ui forms/wdgmaskfromselection.ui forms/wdgmasksource.ui forms/wdgfilterdialog.ui forms/wdgmetadatamergestrategychooser.ui forms/wdgpaintoppresets.ui forms/wdgpaintopsettings.ui forms/wdgdlggeneratorlayer.ui forms/wdgdlgfilelayer.ui forms/wdgfilterselector.ui forms/wdgfilternodecreation.ui forms/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgsavebrushpreset.ui forms/wdgpreseticonlibrary.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui forms/wdgsessionmanager.ui forms/wdgnewwindowlayout.ui forms/KisWelcomePage.ui forms/WdgDlgPaletteEditor.ui forms/KisNewsPage.ui forms/wdgGamutMaskToolbar.ui forms/wdgchangeclonesource.ui brushhud/kis_dlg_brush_hud_config.ui dialogs/kis_delayed_save_dialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui layerstyles/wdgKisLayerStyleAngleSelector.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui input/wintab/drawpile_tablettester/tablettest.ui ) if(WIN32) if(USE_QT_TABLET_WINDOWS) ki18n_wrap_ui(kritaui_LIB_SRCS dialogs/KisDlgCustomTabletResolution.ui ) else() ki18n_wrap_ui(kritaui_LIB_SRCS input/wintab/kis_screen_size_choice_dialog.ui ) endif() endif() add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils kritaresources ${PNG_LIBRARIES} LibExiv2::LibExiv2 ) if (HAVE_QT_MULTIMEDIA) target_link_libraries(kritaui Qt5::Multimedia) endif() if (NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB} ${XCB_LIBRARIES}) endif() if(APPLE) target_link_libraries(kritaui ${FOUNDATION_LIBRARY}) target_link_libraries(kritaui ${APPKIT_LIBRARY}) endif () target_link_libraries(kritaui ${OPENEXR_LIBRARIES}) # Add VSync disable workaround if(NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras) endif() if(X11_FOUND) target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES}) endif() target_include_directories(kritaui PUBLIC $ $ $ $ $ $ $ ) set_target_properties(kritaui PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS}) if (APPLE) install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita) endif () diff --git a/libs/ui/KisMultiFeedRSSModel.cpp b/libs/ui/KisMultiFeedRSSModel.cpp index 9c058053bb..9c6c446fbd 100644 --- a/libs/ui/KisMultiFeedRSSModel.cpp +++ b/libs/ui/KisMultiFeedRSSModel.cpp @@ -1,221 +1,219 @@ /************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "KisMultiFeedRSSModel.h" #include #include #include #include #include #include #include #include QString shortenHtml(QString html) { html.replace(QLatin1String("")); uint firstParaEndHtml = (uint) html.indexOf(QLatin1String("

"), html.indexOf(QLatin1String("

"))+1); uint firstParaEndBr = (uint) html.indexOf(QLatin1String("request().url(); requestUrl = source.toString(); streamReader.setDevice(reply); RssItemList list; while (!streamReader.atEnd()) { switch (streamReader.readNext()) { case QXmlStreamReader::StartElement: if (streamReader.name() == QLatin1String("item")) list.append(parseItem()); else if (streamReader.name() == QLatin1String("title")) blogName = streamReader.readElementText(); else if (streamReader.name() == QLatin1String("link")) { if (!streamReader.namespaceUri().isEmpty()) break; QString favIconString(streamReader.readElementText()); QUrl favIconUrl(favIconString); favIconUrl.setPath(QLatin1String("favicon.ico")); blogIcon = favIconUrl.toString(); blogIcon = QString(); // XXX: fix the favicon on krita.org! } break; default: break; } } return list; } private: QXmlStreamReader streamReader; QString requestUrl; QString blogIcon; QString blogName; }; MultiFeedRssModel::MultiFeedRssModel(QObject *parent) : QAbstractListModel(parent), m_networkAccessManager(new KisNetworkAccessManager), m_articleCount(0) { connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(appendFeedData(QNetworkReply*)), Qt::QueuedConnection); } MultiFeedRssModel::~MultiFeedRssModel() { } QHash MultiFeedRssModel::roleNames() const { QHash roleNames; roleNames[TitleRole] = "title"; roleNames[DescriptionRole] = "description"; roleNames[PubDateRole] = "pubDate"; roleNames[LinkRole] = "link"; roleNames[BlogNameRole] = "blogName"; roleNames[BlogIconRole] = "blogIcon"; return roleNames; } void MultiFeedRssModel::addFeed(const QString& feed) { const QUrl feedUrl(feed); QMetaObject::invokeMethod(m_networkAccessManager, "getUrl", Qt::QueuedConnection, Q_ARG(QUrl, feedUrl)); } bool sortForPubDate(const RssItem& item1, const RssItem& item2) { return item1.pubDate > item2.pubDate; } void MultiFeedRssModel::appendFeedData(QNetworkReply *reply) { RssReader reader; m_aggregatedFeed.append(reader.parse(reply)); std::sort(m_aggregatedFeed.begin(), m_aggregatedFeed.end(), sortForPubDate); setArticleCount(m_aggregatedFeed.size()); beginResetModel(); endResetModel(); } void MultiFeedRssModel::removeFeed(const QString &feed) { QMutableListIterator it(m_aggregatedFeed); while (it.hasNext()) { RssItem item = it.next(); if (item.source == feed) it.remove(); } setArticleCount(m_aggregatedFeed.size()); } int MultiFeedRssModel::rowCount(const QModelIndex &) const { return m_aggregatedFeed.size(); } QVariant MultiFeedRssModel::data(const QModelIndex &index, int role) const { RssItem item = m_aggregatedFeed.at(index.row()); switch (role) { case Qt::DisplayRole: { return QString("" + item.title + "" - "
(" + item.pubDate.toString("MMMM d, yyyy") + ") " + "
(" + item.pubDate.toLocalTime().toString(Qt::DefaultLocaleShortDate) + ") " + item.description.left(90).append("...") + "


"); } case TitleRole: return item.title; case DescriptionRole: return item.description; case PubDateRole: return item.pubDate.toString("dd-MM-yyyy hh:mm"); case LinkRole: return item.link; case BlogNameRole: return item.blogName; case BlogIconRole: return item.blogIcon; } return QVariant(); } diff --git a/libs/ui/canvas/kis_canvas_controller.cpp b/libs/ui/canvas/kis_canvas_controller.cpp index 2ab25dec3b..7af9689b70 100644 --- a/libs/ui/canvas/kis_canvas_controller.cpp +++ b/libs/ui/canvas/kis_canvas_controller.cpp @@ -1,398 +1,386 @@ /* * 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_canvas_controller.h" #include #include #include #include #include #include "kis_canvas_decoration.h" -#include "kis_paintop_transformation_connector.h" #include "kis_coordinates_converter.h" #include "kis_canvas2.h" #include "opengl/kis_opengl_canvas2.h" #include "KisDocument.h" #include "kis_image.h" #include "KisViewManager.h" #include "KisView.h" #include "krita_utils.h" #include "kis_config.h" #include "kis_signal_compressor_with_param.h" #include "kis_config_notifier.h" static const int gRulersUpdateDelay = 80 /* ms */; struct KisCanvasController::Private { Private(KisCanvasController *qq) - : q(qq), - paintOpTransformationConnector(0) + : q(qq) { using namespace std::placeholders; std::function callback( std::bind(&KisCanvasController::Private::emitPointerPositionChangedSignals, this, _1)); mousePositionCompressor.reset( new KisSignalCompressorWithParam( gRulersUpdateDelay, callback, KisSignalCompressor::FIRST_ACTIVE)); } QPointer view; KisCoordinatesConverter *coordinatesConverter; KisCanvasController *q; - KisPaintopTransformationConnector *paintOpTransformationConnector; QScopedPointer > mousePositionCompressor; void emitPointerPositionChangedSignals(QPoint pointerPos); void updateDocumentSizeAfterTransform(); void showRotationValueOnCanvas(); void showMirrorStateOnCanvas(); }; void KisCanvasController::Private::emitPointerPositionChangedSignals(QPoint pointerPos) { if (!coordinatesConverter) return; QPointF documentPos = coordinatesConverter->widgetToDocument(pointerPos); q->proxyObject->emitDocumentMousePositionChanged(documentPos); q->proxyObject->emitCanvasMousePositionChanged(pointerPos); } void KisCanvasController::Private::updateDocumentSizeAfterTransform() { // round the size of the area to the nearest integer instead of getting aligned rect QSize widgetSize = coordinatesConverter->imageRectInWidgetPixels().toRect().size(); q->updateDocumentSize(widgetSize, true); KisCanvas2 *kritaCanvas = dynamic_cast(q->canvas()); Q_ASSERT(kritaCanvas); kritaCanvas->notifyZoomChanged(); } KisCanvasController::KisCanvasController(QPointerparent, KActionCollection * actionCollection) : KoCanvasControllerWidget(actionCollection, parent), m_d(new Private(this)) { m_d->view = parent; } KisCanvasController::~KisCanvasController() { delete m_d; } void KisCanvasController::setCanvas(KoCanvasBase *canvas) { if (canvas) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas); KIS_SAFE_ASSERT_RECOVER_RETURN(kritaCanvas); m_d->coordinatesConverter = const_cast(kritaCanvas->coordinatesConverter()); - - m_d->paintOpTransformationConnector = - new KisPaintopTransformationConnector(kritaCanvas, this); } else { m_d->coordinatesConverter = 0; - - delete m_d->paintOpTransformationConnector; - m_d->paintOpTransformationConnector = 0; } KoCanvasControllerWidget::setCanvas(canvas); } void KisCanvasController::activate() { KoCanvasControllerWidget::activate(); } QPointF KisCanvasController::currentCursorPosition() const { KoCanvasBase *canvas = m_d->view->canvasBase(); QWidget *canvasWidget = canvas->canvasWidget(); const QPointF cursorPosWidget = canvasWidget->mapFromGlobal(QCursor::pos()); return m_d->coordinatesConverter->widgetToDocument(cursorPosWidget); } void KisCanvasController::keyPressEvent(QKeyEvent *event) { /** * Dirty Hack Alert: * Do not call the KoCanvasControllerWidget::keyPressEvent() * to avoid activation of Pan and Default tool activation shortcuts */ Q_UNUSED(event); } void KisCanvasController::wheelEvent(QWheelEvent *event) { /** * Dirty Hack Alert: * Do not call the KoCanvasControllerWidget::wheelEvent() * to disable the default behavior of KoCanvasControllerWidget and QAbstractScrollArea */ Q_UNUSED(event); } bool KisCanvasController::eventFilter(QObject *watched, QEvent *event) { KoCanvasBase *canvas = this->canvas(); if (!canvas || !canvas->canvasWidget() || canvas->canvasWidget() != watched) return false; if (event->type() == QEvent::MouseMove) { QMouseEvent *mevent = static_cast(event); m_d->mousePositionCompressor->start(mevent->pos()); } else if (event->type() == QEvent::TabletMove) { QTabletEvent *tevent = static_cast(event); m_d->mousePositionCompressor->start(tevent->pos()); } else if (event->type() == QEvent::FocusIn) { m_d->view->syncLastActiveNodeToDocument(); } return false; } void KisCanvasController::updateDocumentSize(const QSizeF &sz, bool recalculateCenter) { KoCanvasControllerWidget::updateDocumentSize(sz, recalculateCenter); emit documentSizeChanged(); } void KisCanvasController::Private::showMirrorStateOnCanvas() { bool isXMirrored = coordinatesConverter->xAxisMirrored(); view->viewManager()-> showFloatingMessage( i18nc("floating message about mirroring", "Horizontal mirroring: %1 ", isXMirrored ? i18n("ON") : i18n("OFF")), QIcon(), 500, KisFloatingMessage::Low); } void KisCanvasController::mirrorCanvas(bool enable) { QPoint newOffset = m_d->coordinatesConverter->mirror(m_d->coordinatesConverter->widgetCenterPoint(), enable, false); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); - m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showMirrorStateOnCanvas(); } void KisCanvasController::Private::showRotationValueOnCanvas() { qreal rotationAngle = coordinatesConverter->rotationAngle(); view->viewManager()-> showFloatingMessage( i18nc("floating message about rotation", "Rotation: %1° ", KritaUtils::prettyFormatReal(rotationAngle)), QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter); } void KisCanvasController::rotateCanvas(qreal angle, const QPointF ¢er) { QPoint newOffset = m_d->coordinatesConverter->rotate(center, angle); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); - m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showRotationValueOnCanvas(); } void KisCanvasController::rotateCanvas(qreal angle) { rotateCanvas(angle, m_d->coordinatesConverter->widgetCenterPoint()); } void KisCanvasController::rotateCanvasRight15() { rotateCanvas(15.0); } void KisCanvasController::rotateCanvasLeft15() { rotateCanvas(-15.0); } qreal KisCanvasController::rotation() const { return m_d->coordinatesConverter->rotationAngle(); } void KisCanvasController::resetCanvasRotation() { QPoint newOffset = m_d->coordinatesConverter->resetRotation(m_d->coordinatesConverter->widgetCenterPoint()); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); - m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showRotationValueOnCanvas(); } void KisCanvasController::slotToggleWrapAroundMode(bool value) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); if (!canvas()->canvasIsOpenGL() && value) { m_d->view->viewManager()->showFloatingMessage(i18n("You are activating wrap-around mode, but have not enabled OpenGL.\n" "To visualize wrap-around mode, enable OpenGL."), QIcon()); } kritaCanvas->setWrapAroundViewingMode(value); kritaCanvas->image()->setWrapAroundModePermitted(value); } bool KisCanvasController::wrapAroundMode() const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->wrapAroundViewingMode(); } void KisCanvasController::slotTogglePixelGrid(bool value) { KisConfig cfg(false); cfg.enablePixelGrid(value); KisConfigNotifier::instance()->notifyPixelGridModeChanged(); } void KisCanvasController::slotToggleLevelOfDetailMode(bool value) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); kritaCanvas->setLodAllowedInCanvas(value); bool result = levelOfDetailMode(); if (!value || result) { m_d->view->viewManager()->showFloatingMessage( i18n("Instant Preview Mode: %1", result ? i18n("ON") : i18n("OFF")), QIcon(), 500, KisFloatingMessage::Low); } else { QString reason; if (!kritaCanvas->canvasIsOpenGL()) { reason = i18n("Instant Preview is only supported with OpenGL activated"); } else if (kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode || kritaCanvas->openGLFilterMode() == KisOpenGL::NearestFilterMode) { QString filteringMode = kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode ? i18n("Bilinear") : i18n("Nearest Neighbour"); reason = i18n("Instant Preview is supported\n in Trilinear or High Quality filtering modes.\nCurrent mode is %1", filteringMode); } m_d->view->viewManager()->showFloatingMessage( i18n("Failed activating Instant Preview mode!\n\n%1", reason), QIcon(), 5000, KisFloatingMessage::Low); } } bool KisCanvasController::levelOfDetailMode() const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->lodAllowedInCanvas(); } void KisCanvasController::saveCanvasState(KisPropertiesConfiguration &config) const { const QPointF ¢er = preferredCenter(); config.setProperty("panX", center.x()); config.setProperty("panY", center.y()); config.setProperty("rotation", rotation()); config.setProperty("mirror", m_d->coordinatesConverter->xAxisMirrored()); config.setProperty("wrapAround", wrapAroundMode()); config.setProperty("enableInstantPreview", levelOfDetailMode()); } void KisCanvasController::restoreCanvasState(const KisPropertiesConfiguration &config) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); mirrorCanvas(config.getBool("mirror", false)); rotateCanvas(config.getFloat("rotation", 0.0f)); const QPointF ¢er = preferredCenter(); float panX = config.getFloat("panX", center.x()); float panY = config.getFloat("panY", center.y()); setPreferredCenter(QPointF(panX, panY)); slotToggleWrapAroundMode(config.getBool("wrapAround", false)); kritaCanvas->setLodAllowedInCanvas(config.getBool("enableInstantPreview", false)); } void KisCanvasController::resetScrollBars() { // The scrollbar value always points at the top-left corner of the // bit of image we paint. KisDocument *doc = m_d->view->document(); if (!doc) return; QRectF documentBounds = doc->documentBounds(); QRectF viewRect = m_d->coordinatesConverter->imageToWidget(documentBounds); // Cancel out any existing pan const QRectF imageBounds = m_d->view->image()->bounds(); const QRectF imageBB = m_d->coordinatesConverter->imageToWidget(imageBounds); QPointF pan = imageBB.topLeft(); viewRect.translate(-pan); int drawH = viewport()->height(); int drawW = viewport()->width(); qreal horizontalReserve = vastScrollingFactor() * drawW; qreal verticalReserve = vastScrollingFactor() * drawH; qreal xMin = viewRect.left() - horizontalReserve; qreal yMin = viewRect.top() - verticalReserve; qreal xMax = viewRect.right() - drawW + horizontalReserve; qreal yMax = viewRect.bottom() - drawH + verticalReserve; QScrollBar *hScroll = horizontalScrollBar(); QScrollBar *vScroll = verticalScrollBar(); hScroll->setRange(static_cast(xMin), static_cast(xMax)); vScroll->setRange(static_cast(yMin), static_cast(yMax)); int fontHeight = QFontMetrics(font()).height(); vScroll->setPageStep(drawH); vScroll->setSingleStep(fontHeight); hScroll->setPageStep(drawW); hScroll->setSingleStep(fontHeight); } diff --git a/libs/ui/canvas/kis_paintop_transformation_connector.cpp b/libs/ui/canvas/kis_paintop_transformation_connector.cpp deleted file mode 100644 index bf93986495..0000000000 --- a/libs/ui/canvas/kis_paintop_transformation_connector.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2013 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_paintop_transformation_connector.h" - -#include "kis_canvas_resource_provider.h" -#include "kis_canvas2.h" -#include "kis_coordinates_converter.h" -#include "brushengine/kis_paintop_preset.h" -#include "brushengine/kis_paintop_settings.h" - - -KisPaintopTransformationConnector::KisPaintopTransformationConnector(KisCanvas2 *canvas, QObject *parent) - : QObject(parent), - m_canvas(canvas) -{ - connect(m_canvas->resourceManager(), - SIGNAL(canvasResourceChanged(int,QVariant)), - SLOT(slotCanvasResourceChanged(int,QVariant))); -} - -void KisPaintopTransformationConnector::notifyTransformationChanged() -{ - KisPaintOpPresetSP preset = - m_canvas->resourceManager()-> - resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); - - if (preset) { - const KisCoordinatesConverter *converter = m_canvas->coordinatesConverter(); - preset->settings()->setCanvasRotation(converter->rotationAngle()); - preset->settings()->setCanvasMirroring(converter->xAxisMirrored(), - converter->yAxisMirrored()); - } -} - -void KisPaintopTransformationConnector::slotCanvasResourceChanged(int key, const QVariant &resource) -{ - Q_UNUSED(resource); - - if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) { - notifyTransformationChanged(); - } -} diff --git a/libs/ui/canvas/kis_paintop_transformation_connector.h b/libs/ui/canvas/kis_paintop_transformation_connector.h deleted file mode 100644 index 68c751ee0a..0000000000 --- a/libs/ui/canvas/kis_paintop_transformation_connector.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2013 Dmitry Kazakov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __KIS_PAINTOP_TRANSFORMATION_CONNECTOR_H -#define __KIS_PAINTOP_TRANSFORMATION_CONNECTOR_H - -#include -#include - -#include - -class KisPaintopTransformationConnector : public QObject -{ - Q_OBJECT -public: - KisPaintopTransformationConnector(KisCanvas2 *canvas, QObject *parent); - -public: - void notifyTransformationChanged(); - -public Q_SLOTS: - void slotCanvasResourceChanged(int key, const QVariant &resource); - -private: - QPointer m_canvas; -}; - -#endif /* __KIS_PAINTOP_TRANSFORMATION_CONNECTOR_H */ diff --git a/libs/ui/tool/kis_painting_information_builder.cpp b/libs/ui/tool/kis_painting_information_builder.cpp index e52259105f..b005a4a67b 100644 --- a/libs/ui/tool/kis_painting_information_builder.cpp +++ b/libs/ui/tool/kis_painting_information_builder.cpp @@ -1,198 +1,259 @@ /* * 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_painting_information_builder.h" #include #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_cubic_curve.h" #include "kis_speed_smoother.h" #include #include "kis_canvas_resource_provider.h" /***********************************************************************/ /* KisPaintingInformationBuilder */ /***********************************************************************/ const int KisPaintingInformationBuilder::LEVEL_OF_PRESSURE_RESOLUTION = 1024; KisPaintingInformationBuilder::KisPaintingInformationBuilder() : m_speedSmoother(new KisSpeedSmoother()), m_pressureDisabled(false) { connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(updateSettings())); updateSettings(); } KisPaintingInformationBuilder::~KisPaintingInformationBuilder() { } void KisPaintingInformationBuilder::updateSettings() { KisConfig cfg(true); KisCubicCurve curve; curve.fromString(cfg.pressureTabletCurve()); m_pressureSamples = curve.floatTransfer(LEVEL_OF_PRESSURE_RESOLUTION + 1); } KisPaintInformation KisPaintingInformationBuilder::startStroke(KoPointerEvent *event, int timeElapsed, const KoCanvasResourceProvider *manager) { if (manager) { m_pressureDisabled = manager->resource(KisCanvasResourceProvider::DisablePressure).toBool(); } m_startPoint = event->point; return createPaintingInformation(event, timeElapsed); } KisPaintInformation KisPaintingInformationBuilder::continueStroke(KoPointerEvent *event, int timeElapsed) { return createPaintingInformation(event, timeElapsed); } QPointF KisPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &/*startPoint*/) { return point; } QPointF KisPaintingInformationBuilder::documentToImage(const QPointF &point) { return point; } QPointF KisPaintingInformationBuilder::imageToView(const QPointF &point) { return point; } qreal KisPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint) { Q_UNUSED(documentPoint); return 1.0; } +qreal KisPaintingInformationBuilder::canvasRotation() const +{ + return 0; +} + +bool KisPaintingInformationBuilder::canvasMirroredX() const +{ + return false; +} + +bool KisPaintingInformationBuilder::canvasMirroredY() const +{ + return false; +} KisPaintInformation KisPaintingInformationBuilder::createPaintingInformation(KoPointerEvent *event, int timeElapsed) { QPointF adjusted = adjustDocumentPoint(event->point, m_startPoint); QPointF imagePoint = documentToImage(adjusted); qreal perspective = calculatePerspective(adjusted); qreal speed = m_speedSmoother->getNextSpeed(imageToView(imagePoint)); - return KisPaintInformation(imagePoint, + KisPaintInformation pi(imagePoint, !m_pressureDisabled ? 1.0 : pressureToCurve(event->pressure()), event->xTilt(), event->yTilt(), event->rotation(), event->tangentialPressure(), perspective, timeElapsed, speed); + + pi.setCanvasRotation(canvasRotation()); + pi.setCanvasMirroredH(canvasMirroredX()); + pi.setCanvasMirroredV(canvasMirroredY()); + + return pi; } KisPaintInformation KisPaintingInformationBuilder::hover(const QPointF &imagePoint, const KoPointerEvent *event) { qreal perspective = calculatePerspective(imagePoint); qreal speed = m_speedSmoother->getNextSpeed(imageToView(imagePoint)); if (event) { return KisPaintInformation::createHoveringModeInfo(imagePoint, PRESSURE_DEFAULT, event->xTilt(), event->yTilt(), event->rotation(), event->tangentialPressure(), perspective, - speed); + speed, + canvasRotation(), + canvasMirroredX(), + canvasMirroredY()); } else { - return KisPaintInformation::createHoveringModeInfo(imagePoint); + KisPaintInformation pi = KisPaintInformation::createHoveringModeInfo(imagePoint); + pi.setCanvasRotation(canvasRotation()); + pi.setCanvasMirroredH(canvasMirroredX()); + pi.setCanvasMirroredV(canvasMirroredY()); + return pi; } } qreal KisPaintingInformationBuilder::pressureToCurve(qreal pressure) { return KisCubicCurve::interpolateLinear(pressure, m_pressureSamples); } /***********************************************************************/ /* KisConverterPaintingInformationBuilder */ /***********************************************************************/ #include "kis_coordinates_converter.h" KisConverterPaintingInformationBuilder::KisConverterPaintingInformationBuilder(const KisCoordinatesConverter *converter) : m_converter(converter) { } QPointF KisConverterPaintingInformationBuilder::documentToImage(const QPointF &point) { return m_converter->documentToImage(point); } QPointF KisConverterPaintingInformationBuilder::imageToView(const QPointF &point) { return m_converter->documentToWidget(point); } +qreal KisConverterPaintingInformationBuilder::canvasRotation() const +{ + return m_converter->rotationAngle(); +} + +bool KisConverterPaintingInformationBuilder::canvasMirroredX() const +{ + return m_converter->xAxisMirrored(); +} + +bool KisConverterPaintingInformationBuilder::canvasMirroredY() const +{ + return m_converter->yAxisMirrored(); +} + /***********************************************************************/ -/* KisToolFreehandPaintingInformationBuilder */ +/* KisToolFreehandPaintingInformationBuilder */ /***********************************************************************/ #include "kis_tool_freehand.h" +#include "kis_canvas2.h" KisToolFreehandPaintingInformationBuilder::KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool) : m_tool(tool) { } QPointF KisToolFreehandPaintingInformationBuilder::documentToImage(const QPointF &point) { return m_tool->convertToPixelCoord(point); } QPointF KisToolFreehandPaintingInformationBuilder::imageToView(const QPointF &point) { return m_tool->pixelToView(point); } QPointF KisToolFreehandPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &startPoint) { return m_tool->adjustPosition(point, startPoint); } qreal KisToolFreehandPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint) { return m_tool->calculatePerspective(documentPoint); } + +qreal KisToolFreehandPaintingInformationBuilder::canvasRotation() const +{ + KisCanvas2 *canvas = dynamic_cast(m_tool->canvas()); + return canvas->coordinatesConverter()->rotationAngle(); +} + +bool KisToolFreehandPaintingInformationBuilder::canvasMirroredX() const +{ + KisCanvas2 *canvas = dynamic_cast(m_tool->canvas()); + return canvas->coordinatesConverter()->xAxisMirrored(); +} + +bool KisToolFreehandPaintingInformationBuilder::canvasMirroredY() const +{ + KisCanvas2 *canvas = dynamic_cast(m_tool->canvas()); + return canvas->coordinatesConverter()->yAxisMirrored(); +} diff --git a/libs/ui/tool/kis_painting_information_builder.h b/libs/ui/tool/kis_painting_information_builder.h index 42e9820a9d..c5c2e77651 100644 --- a/libs/ui/tool/kis_painting_information_builder.h +++ b/libs/ui/tool/kis_painting_information_builder.h @@ -1,112 +1,124 @@ /* * 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. */ #ifndef __KIS_PAINTING_INFORMATION_BUILDER_H #define __KIS_PAINTING_INFORMATION_BUILDER_H #include #include #include "kis_types.h" #include "kritaui_export.h" #include class KoPointerEvent; class KisToolFreehand; class KisCoordinatesConverter; class KisSpeedSmoother; class KoCanvasResourceProvider; class KRITAUI_EXPORT KisPaintingInformationBuilder : public QObject { Q_OBJECT public: KisPaintingInformationBuilder(); ~KisPaintingInformationBuilder() override; KisPaintInformation startStroke(KoPointerEvent *event, int timeElapsed, const KoCanvasResourceProvider *manager); KisPaintInformation continueStroke(KoPointerEvent *event, int timeElapsed); KisPaintInformation hover(const QPointF &imagePoint, const KoPointerEvent *event); qreal pressureToCurve(qreal pressure); protected Q_SLOTS: void updateSettings(); protected: virtual QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint); virtual QPointF documentToImage(const QPointF &point); virtual QPointF imageToView(const QPointF &point); virtual qreal calculatePerspective(const QPointF &documentPoint); + virtual qreal canvasRotation() const; + virtual bool canvasMirroredX() const; + virtual bool canvasMirroredY() const; + private: KisPaintInformation createPaintingInformation(KoPointerEvent *event, int timeElapsed); /** * Defines how many discrete samples are stored in a precomputed array * of different pressures. */ static const int LEVEL_OF_PRESSURE_RESOLUTION; private: QVector m_pressureSamples; QPointF m_startPoint; QScopedPointer m_speedSmoother; bool m_pressureDisabled; }; class KRITAUI_EXPORT KisConverterPaintingInformationBuilder : public KisPaintingInformationBuilder { Q_OBJECT public: KisConverterPaintingInformationBuilder(const KisCoordinatesConverter *converter); protected: QPointF documentToImage(const QPointF &point) override; QPointF imageToView(const QPointF &point) override; + qreal canvasRotation() const override; + bool canvasMirroredX() const override; + bool canvasMirroredY() const override; + private: const KisCoordinatesConverter *m_converter; }; class KRITAUI_EXPORT KisToolFreehandPaintingInformationBuilder : public KisPaintingInformationBuilder { Q_OBJECT public: KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool); protected: QPointF documentToImage(const QPointF &point) override; QPointF imageToView(const QPointF &point) override; QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint) override; qreal calculatePerspective(const QPointF &documentPoint) override; + qreal canvasRotation() const override; + bool canvasMirroredX() const override; + bool canvasMirroredY() const override; + private: KisToolFreehand *m_tool; }; #endif /* __KIS_PAINTING_INFORMATION_BUILDER_H */ diff --git a/libs/ui/tool/kis_tool.cc b/libs/ui/tool/kis_tool.cc index 40622c62f0..eafcf146d9 100644 --- a/libs/ui/tool/kis_tool.cc +++ b/libs/ui/tool/kis_tool.cc @@ -1,667 +1,668 @@ /* * Copyright (c) 2006, 2010 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_tool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_node_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include "opengl/kis_opengl_canvas2.h" #include "kis_canvas_resource_provider.h" #include "canvas/kis_canvas2.h" #include "kis_coordinates_converter.h" #include "filter/kis_filter_configuration.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_cursor.h" #include #include "kis_resources_snapshot.h" #include #include "kis_action_registry.h" #include "kis_tool_utils.h" struct Q_DECL_HIDDEN KisTool::Private { QCursor cursor; // the cursor that should be shown on tool activation. // From the canvas resources KoPatternSP currentPattern; KoAbstractGradientSP currentGradient; KoColor currentFgColor; KoColor currentBgColor; float currentExposure{1.0}; KisFilterConfigurationSP currentGenerator; QWidget* optionWidget{0}; ToolMode m_mode{HOVER_MODE}; bool m_isActive{false}; }; KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor) : KoToolBase(canvas) , d(new Private) { d->cursor = cursor; connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle())); connect(this, SIGNAL(isActiveChanged(bool)), SLOT(resetCursorStyle())); } KisTool::~KisTool() { delete d; } void KisTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); resetCursorStyle(); if (!canvas()) return; if (!canvas()->resourceManager()) return; d->currentFgColor = canvas()->resourceManager()->resource(KoCanvasResourceProvider::ForegroundColor).value(); d->currentBgColor = canvas()->resourceManager()->resource(KoCanvasResourceProvider::BackgroundColor).value(); if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentPattern)) { d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value(); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGradient)) { d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value(); } KisPaintOpPresetSP preset = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset && preset->settings()) { preset->settings()->activate(); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::HdrExposure)) { d->currentExposure = static_cast(canvas()->resourceManager()->resource(KisCanvasResourceProvider::HdrExposure).toDouble()); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration)) { d->currentGenerator = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); } d->m_isActive = true; emit isActiveChanged(true); } void KisTool::deactivate() { d->m_isActive = false; emit isActiveChanged(false); KoToolBase::deactivate(); } void KisTool::canvasResourceChanged(int key, const QVariant & v) { QString formattedBrushName; if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) { formattedBrushName = v.value()->name().replace("_", " "); } switch (key) { case(KoCanvasResourceProvider::ForegroundColor): d->currentFgColor = v.value(); break; case(KoCanvasResourceProvider::BackgroundColor): d->currentBgColor = v.value(); break; case(KisCanvasResourceProvider::CurrentPattern): d->currentPattern = v.value(); break; case(KisCanvasResourceProvider::CurrentGradient): d->currentGradient = v.value(); break; case(KisCanvasResourceProvider::HdrExposure): d->currentExposure = static_cast(v.toDouble()); break; case(KisCanvasResourceProvider::CurrentGeneratorConfiguration): d->currentGenerator = static_cast(v.value()); break; case(KisCanvasResourceProvider::CurrentPaintOpPreset): emit statusTextChanged(formattedBrushName); break; case(KisCanvasResourceProvider::CurrentKritaNode): resetCursorStyle(); break; default: break; // Do nothing }; } void KisTool::updateSettingsViews() { } QPointF KisTool::widgetCenterInWidgetPixels() { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter(); return converter->flakeToWidget(converter->flakeCenterPoint()); } QPointF KisTool::convertDocumentToWidget(const QPointF& pt) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->coordinatesConverter()->documentToWidget(pt); } QPointF KisTool::convertToPixelCoord(KoPointerEvent *e) { if (!image()) return e->point; return image()->documentToPixel(e->point); } QPointF KisTool::convertToPixelCoord(const QPointF& pt) { if (!image()) return pt; return image()->documentToPixel(pt); } QPointF KisTool::convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset, bool useModifiers) { if (!image()) return e->point; KoSnapGuide *snapGuide = canvas()->snapGuide(); QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier); return image()->documentToPixel(pos); } QPointF KisTool::convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset) { if (!image()) return pt; KoSnapGuide *snapGuide = canvas()->snapGuide(); QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier); return image()->documentToPixel(pos); } QPoint KisTool::convertToImagePixelCoordFloored(KoPointerEvent *e) { if (!image()) return e->point.toPoint(); return image()->documentToImagePixelFloored(e->point); } QPointF KisTool::viewToPixel(const QPointF &viewCoord) const { if (!image()) return viewCoord; return image()->documentToPixel(canvas()->viewConverter()->viewToDocument(viewCoord)); } QRectF KisTool::convertToPt(const QRectF &rect) { if (!image()) return rect; QRectF r; //We add 1 in the following to the extreme coords because a pixel always has size r.setCoords(int(rect.left()) / image()->xRes(), int(rect.top()) / image()->yRes(), int(rect.right()) / image()->xRes(), int( rect.bottom()) / image()->yRes()); return r; } qreal KisTool::convertToPt(qreal value) { const qreal avgResolution = 0.5 * (image()->xRes() + image()->yRes()); return value / avgResolution; } QPointF KisTool::pixelToView(const QPoint &pixelCoord) const { if (!image()) return pixelCoord; QPointF documentCoord = image()->pixelToDocument(pixelCoord); return canvas()->viewConverter()->documentToView(documentCoord); } QPointF KisTool::pixelToView(const QPointF &pixelCoord) const { if (!image()) return pixelCoord; QPointF documentCoord = image()->pixelToDocument(pixelCoord); return canvas()->viewConverter()->documentToView(documentCoord); } QRectF KisTool::pixelToView(const QRectF &pixelRect) const { - if (!image()) + if (!image()) { return pixelRect; + } QPointF topLeft = pixelToView(pixelRect.topLeft()); QPointF bottomRight = pixelToView(pixelRect.bottomRight()); - return QRectF(topLeft, bottomRight); + return {topLeft, bottomRight}; } QPainterPath KisTool::pixelToView(const QPainterPath &pixelPolygon) const { QTransform matrix; qreal zoomX, zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes()); return matrix.map(pixelPolygon); } QPolygonF KisTool::pixelToView(const QPolygonF &pixelPath) const { QTransform matrix; qreal zoomX, zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes()); return matrix.map(pixelPath); } void KisTool::updateCanvasPixelRect(const QRectF &pixelRect) { canvas()->updateCanvas(convertToPt(pixelRect)); } void KisTool::updateCanvasViewRect(const QRectF &viewRect) { canvas()->updateCanvas(canvas()->viewConverter()->viewToDocument(viewRect)); } KisImageWSP KisTool::image() const { // For now, krita tools only work in krita, not for a krita shape. Krita shapes are for 2.1 KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (kisCanvas) { return kisCanvas->currentImage(); } return 0; } QCursor KisTool::cursor() const { return d->cursor; } KoPatternSP KisTool::currentPattern() { return d->currentPattern; } KoAbstractGradientSP KisTool::currentGradient() { return d->currentGradient; } KisPaintOpPresetSP KisTool::currentPaintOpPreset() { return canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); } KisNodeSP KisTool::currentNode() const { KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); return node; } KisNodeList KisTool::selectedNodes() const { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); return viewManager->nodeManager()->selectedNodes(); } KoColor KisTool::currentFgColor() { return d->currentFgColor; } KoColor KisTool::currentBgColor() { return d->currentBgColor; } KisImageWSP KisTool::currentImage() { return image(); } KisFilterConfigurationSP KisTool::currentGenerator() { return d->currentGenerator; } void KisTool::setMode(ToolMode mode) { d->m_mode = mode; } KisTool::ToolMode KisTool::mode() const { return d->m_mode; } void KisTool::setCursor(const QCursor &cursor) { d->cursor = cursor; } KisTool::AlternateAction KisTool::actionToAlternateAction(ToolAction action) { KIS_ASSERT_RECOVER_RETURN_VALUE(action != Primary, Secondary); return (AlternateAction)action; } void KisTool::activatePrimaryAction() { resetCursorStyle(); } void KisTool::deactivatePrimaryAction() { resetCursorStyle(); } void KisTool::beginPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::beginPrimaryDoubleClickAction(KoPointerEvent *event) { beginPrimaryAction(event); } void KisTool::continuePrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } bool KisTool::primaryActionSupportsHiResEvents() const { return false; } void KisTool::activateAlternateAction(AlternateAction action) { Q_UNUSED(action); } void KisTool::deactivateAlternateAction(AlternateAction action) { Q_UNUSED(action); } void KisTool::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action) { beginAlternateAction(event, action); } void KisTool::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::endAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::mouseDoubleClickEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseTripleClickEvent(KoPointerEvent *event) { mouseDoubleClickEvent(event); } void KisTool::mousePressEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseReleaseEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseMoveEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::deleteSelection() { KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager()); if (!blockUntilOperationsFinished()) { return; } if (!KisToolUtils::clearImage(image(), resources->currentNode(), resources->activeSelection())) { KoToolBase::deleteSelection(); } } KisTool::NodePaintAbility KisTool::nodePaintAbility() { KisNodeSP node = currentNode(); if (!node) { return NodePaintAbility::UNPAINTABLE; } if (node->inherits("KisShapeLayer")) { return NodePaintAbility::VECTOR; } if (node->inherits("KisCloneLayer")) { return NodePaintAbility::CLONE; } if (node->paintDevice()) { return NodePaintAbility::PAINT; } return NodePaintAbility::UNPAINTABLE; } QWidget* KisTool::createOptionWidget() { d->optionWidget = new QLabel(i18n("No options")); d->optionWidget->setObjectName("SpecialSpacer"); return d->optionWidget; } #define NEAR_VAL -1000.0 #define FAR_VAL 1000.0 #define PROGRAM_VERTEX_ATTRIBUTE 0 void KisTool::paintToolOutline(QPainter* painter, const QPainterPath &path) { KisOpenGLCanvas2 *canvasWidget = dynamic_cast(canvas()->canvasWidget()); if (canvasWidget) { painter->beginNativePainting(); canvasWidget->paintToolOutline(path); painter->endNativePainting(); } else { painter->save(); painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination); painter->setPen(QColor(128, 255, 128)); painter->drawPath(path); painter->restore(); } } void KisTool::resetCursorStyle() { useCursor(d->cursor); } bool KisTool::overrideCursorIfNotEditable() { // override cursor for canvas iff this tool is active // and we can't paint on the active layer if (isActive()) { KisNodeSP node = currentNode(); if (node && !node->isEditable()) { canvas()->setCursor(Qt::ForbiddenCursor); return true; } } return false; } bool KisTool::blockUntilOperationsFinished() { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); return viewManager->blockUntilOperationsFinished(image()); } void KisTool::blockUntilOperationsFinishedForced() { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); viewManager->blockUntilOperationsFinishedForced(image()); } bool KisTool::isActive() const { return d->m_isActive; } bool KisTool::nodeEditable() { KisNodeSP node = currentNode(); if (!node) { return false; } bool blockedNoIndirectPainting = false; const bool presetUsesIndirectPainting = !currentPaintOpPreset()->settings()->paintIncremental(); if (!presetUsesIndirectPainting) { const KisIndirectPaintingSupport *indirectPaintingLayer = dynamic_cast(node.data()); if (indirectPaintingLayer) { blockedNoIndirectPainting = !indirectPaintingLayer->supportsNonIndirectPainting(); } } bool nodeEditable = node->isEditable() && !blockedNoIndirectPainting; if (!nodeEditable) { KisCanvas2 * kiscanvas = static_cast(canvas()); QString message; if (!node->visible() && node->userLocked()) { message = i18n("Layer is locked and invisible."); } else if (node->userLocked()) { message = i18n("Layer is locked."); } else if(!node->visible()) { message = i18n("Layer is invisible."); } else if (blockedNoIndirectPainting) { message = i18n("Layer can be painted in Wash Mode only."); } else { message = i18n("Group not editable."); } kiscanvas->viewManager()->showFloatingMessage(message, KisIconUtils::loadIcon("object-locked")); } return nodeEditable; } bool KisTool::selectionEditable() { KisCanvas2 * kisCanvas = static_cast(canvas()); KisViewManager * view = kisCanvas->viewManager(); bool editable = view->selectionEditable(); if (!editable) { KisCanvas2 * kiscanvas = static_cast(canvas()); kiscanvas->viewManager()->showFloatingMessage(i18n("Local selection is locked."), KisIconUtils::loadIcon("object-locked")); } return editable; } void KisTool::listenToModifiers(bool listen) { Q_UNUSED(listen); } bool KisTool::listeningToModifiers() { return false; } diff --git a/libs/ui/tool/kis_tool_freehand.cc b/libs/ui/tool/kis_tool_freehand.cc index 44bfc63df7..284869d934 100644 --- a/libs/ui/tool/kis_tool_freehand.cc +++ b/libs/ui/tool/kis_tool_freehand.cc @@ -1,461 +1,455 @@ /* * kis_tool_freehand.cc - part of Krita * * Copyright (c) 2003-2007 Boudewijn Rempt * Copyright (c) 2004 Bart Coppens * Copyright (c) 2007,2008,2010 Cyrille Berger * Copyright (c) 2009 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_freehand.h" #include #include #include #include #include #include #include #include #include #include //pop up palette #include // Krita/image #include #include #include #include #include #include // Krita/ui #include "kis_abstract_perspective_grid.h" #include "kis_config.h" #include "canvas/kis_canvas2.h" #include "kis_cursor.h" #include #include #include "kis_painting_information_builder.h" #include "kis_tool_freehand_helper.h" #include "strokes/freehand_stroke.h" using namespace std::placeholders; // For _1 placeholder KisToolFreehand::KisToolFreehand(KoCanvasBase * canvas, const QCursor & cursor, const KUndo2MagicString &transactionText) : KisToolPaint(canvas, cursor), m_paintopBasedPickingInAction(false), m_brushResizeCompressor(200, std::bind(&KisToolFreehand::slotDoResizeBrush, this, _1)) { m_assistant = false; m_magnetism = 1.0; m_only_one_assistant = true; setSupportOutline(true); setMaskSyntheticEvents(KisConfig(true).disableTouchOnCanvas()); // Disallow mouse events from finger presses unless enabled m_infoBuilder = new KisToolFreehandPaintingInformationBuilder(this); m_helper = new KisToolFreehandHelper(m_infoBuilder, transactionText); connect(m_helper, SIGNAL(requestExplicitUpdateOutline()), SLOT(explicitUpdateOutline())); } KisToolFreehand::~KisToolFreehand() { delete m_helper; delete m_infoBuilder; } void KisToolFreehand::mouseMoveEvent(KoPointerEvent *event) { KisToolPaint::mouseMoveEvent(event); m_helper->cursorMoved(convertToPixelCoord(event)); } KisSmoothingOptionsSP KisToolFreehand::smoothingOptions() const { return m_helper->smoothingOptions(); } void KisToolFreehand::resetCursorStyle() { KisConfig cfg(true); switch (cfg.newCursorStyle()) { case CURSOR_STYLE_NO_CURSOR: useCursor(KisCursor::blankCursor()); break; case CURSOR_STYLE_POINTER: useCursor(KisCursor::arrowCursor()); break; case CURSOR_STYLE_SMALL_ROUND: useCursor(KisCursor::roundCursor()); break; case CURSOR_STYLE_CROSSHAIR: useCursor(KisCursor::crossCursor()); break; case CURSOR_STYLE_TRIANGLE_RIGHTHANDED: useCursor(KisCursor::triangleRightHandedCursor()); break; case CURSOR_STYLE_TRIANGLE_LEFTHANDED: useCursor(KisCursor::triangleLeftHandedCursor()); break; case CURSOR_STYLE_BLACK_PIXEL: useCursor(KisCursor::pixelBlackCursor()); break; case CURSOR_STYLE_WHITE_PIXEL: useCursor(KisCursor::pixelWhiteCursor()); break; case CURSOR_STYLE_TOOLICON: default: KisToolPaint::resetCursorStyle(); break; } } KisPaintingInformationBuilder* KisToolFreehand::paintingInformationBuilder() const { return m_infoBuilder; } void KisToolFreehand::resetHelper(KisToolFreehandHelper *helper) { delete m_helper; m_helper = helper; } int KisToolFreehand::flags() const { return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP|KisTool::FLAG_USES_CUSTOM_PRESET |KisTool::FLAG_USES_CUSTOM_SIZE; } void KisToolFreehand::activate(ToolActivation activation, const QSet &shapes) { KisToolPaint::activate(activation, shapes); } void KisToolFreehand::deactivate() { if (mode() == PAINT_MODE) { endStroke(); setMode(KisTool::HOVER_MODE); } KisToolPaint::deactivate(); } void KisToolFreehand::initStroke(KoPointerEvent *event) { m_helper->initPaint(event, convertToPixelCoord(event), canvas()->resourceManager(), image(), currentNode(), image().data()); } void KisToolFreehand::doStroke(KoPointerEvent *event) { - //set canvas information here?// - KisCanvas2 *canvas2 = dynamic_cast(canvas()); - if (canvas2) { - m_helper->setCanvasHorizontalMirrorState(canvas2->xAxisMirrored()); - m_helper->setCanvasRotation(canvas2->rotationAngle()); - } m_helper->paintEvent(event); } void KisToolFreehand::endStroke() { m_helper->endPaint(); bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()->mouseReleaseEvent(); Q_UNUSED(paintOpIgnoredEvent); } bool KisToolFreehand::primaryActionSupportsHiResEvents() const { return true; } void KisToolFreehand::beginPrimaryAction(KoPointerEvent *event) { // FIXME: workaround for the Duplicate Op tryPickByPaintOp(event, PickFgImage); requestUpdateOutline(event->point, event); NodePaintAbility paintability = nodePaintAbility(); // XXX: move this to KisTool and make it work properly for clone layers: for clone layers, the shape paint tools don't work either if (!nodeEditable() || paintability != PAINT) { if (paintability == KisToolPaint::VECTOR || paintability == KisToolPaint::CLONE){ KisCanvas2 * kiscanvas = static_cast(canvas()); QString message = i18n("The brush tool cannot paint on this layer. Please select a paint layer or mask."); kiscanvas->viewManager()->showFloatingMessage(message, koIcon("object-locked")); } event->ignore(); return; } setMode(KisTool::PAINT_MODE); KisCanvas2 *canvas2 = dynamic_cast(canvas()); if (canvas2) { canvas2->viewManager()->disableControls(); } initStroke(event); } void KisToolFreehand::continuePrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); requestUpdateOutline(event->point, event); /** * Actual painting */ doStroke(event); } void KisToolFreehand::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); endStroke(); if (m_assistant && static_cast(canvas())->paintingAssistantsDecoration()) { static_cast(canvas())->paintingAssistantsDecoration()->endStroke(); } KisCanvas2 *canvas2 = dynamic_cast(canvas()); if (canvas2) { canvas2->viewManager()->enableControls(); } setMode(KisTool::HOVER_MODE); } bool KisToolFreehand::tryPickByPaintOp(KoPointerEvent *event, AlternateAction action) { if (action != PickFgNode && action != PickFgImage) return false; /** * FIXME: we need some better way to implement modifiers * for a paintop level. This method is used in DuplicateOp only! */ QPointF pos = adjustPosition(event->point, event->point); qreal perspective = 1.0; Q_FOREACH (const QPointer grid, static_cast(canvas())->viewManager()->canvasResourceProvider()->perspectiveGrids()) { if (grid && grid->contains(pos)) { perspective = grid->distance(pos); break; } } if (!currentPaintOpPreset()) { return false; } bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()-> mousePressEvent(KisPaintInformation(convertToPixelCoord(event->point), m_infoBuilder->pressureToCurve(event->pressure()), event->xTilt(), event->yTilt(), event->rotation(), event->tangentialPressure(), perspective, 0, 0), event->modifiers(), currentNode()); // DuplicateOP during the picking of new source point (origin) // is the only paintop that returns "false" here return !paintOpIgnoredEvent; } void KisToolFreehand::activateAlternateAction(AlternateAction action) { if (action != ChangeSize) { KisToolPaint::activateAlternateAction(action); return; } useCursor(KisCursor::blankCursor()); setOutlineEnabled(true); } void KisToolFreehand::deactivateAlternateAction(AlternateAction action) { if (action != ChangeSize) { KisToolPaint::deactivateAlternateAction(action); return; } resetCursorStyle(); setOutlineEnabled(false); } void KisToolFreehand::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { if (tryPickByPaintOp(event, action)) { m_paintopBasedPickingInAction = true; return; } if (action != ChangeSize) { KisToolPaint::beginAlternateAction(event, action); return; } setMode(GESTURE_MODE); m_initialGestureDocPoint = event->point; m_initialGestureGlobalPoint = QCursor::pos(); m_lastDocumentPoint = event->point; m_lastPaintOpSize = currentPaintOpPreset()->settings()->paintOpSize(); } void KisToolFreehand::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { if (tryPickByPaintOp(event, action) || m_paintopBasedPickingInAction) return; if (action != ChangeSize) { KisToolPaint::continueAlternateAction(event, action); return; } QPointF lastWidgetPosition = convertDocumentToWidget(m_lastDocumentPoint); QPointF actualWidgetPosition = convertDocumentToWidget(event->point); QPointF offset = actualWidgetPosition - lastWidgetPosition; KisCanvas2 *canvas2 = dynamic_cast(canvas()); QRect screenRect = QApplication::desktop()->screenGeometry(); qreal scaleX = 0; qreal scaleY = 0; canvas2->coordinatesConverter()->imageScale(&scaleX, &scaleY); const qreal maxBrushSize = KisConfig(true).readEntry("maximumBrushSize", 1000); const qreal effectiveMaxDragSize = 0.5 * screenRect.width(); const qreal effectiveMaxBrushSize = qMin(maxBrushSize, effectiveMaxDragSize / scaleX); const qreal scaleCoeff = effectiveMaxBrushSize / effectiveMaxDragSize; const qreal sizeDiff = scaleCoeff * offset.x() ; if (qAbs(sizeDiff) > 0.01) { KisPaintOpSettingsSP settings = currentPaintOpPreset()->settings(); const qreal newSize = qBound(0.01, m_lastPaintOpSize + sizeDiff, maxBrushSize); settings->setPaintOpSize(newSize); requestUpdateOutline(m_initialGestureDocPoint, 0); //m_brushResizeCompressor.start(newSize); m_lastDocumentPoint = event->point; m_lastPaintOpSize = newSize; } } void KisToolFreehand::endAlternateAction(KoPointerEvent *event, AlternateAction action) { if (tryPickByPaintOp(event, action) || m_paintopBasedPickingInAction) { m_paintopBasedPickingInAction = false; return; } if (action != ChangeSize) { KisToolPaint::endAlternateAction(event, action); return; } QCursor::setPos(m_initialGestureGlobalPoint); requestUpdateOutline(m_initialGestureDocPoint, 0); setMode(HOVER_MODE); } bool KisToolFreehand::wantsAutoScroll() const { return false; } void KisToolFreehand::setAssistant(bool assistant) { m_assistant = assistant; } void KisToolFreehand::setOnlyOneAssistantSnap(bool assistant) { m_only_one_assistant = assistant; } void KisToolFreehand::slotDoResizeBrush(qreal newSize) { KisPaintOpSettingsSP settings = currentPaintOpPreset()->settings(); settings->setPaintOpSize(newSize); requestUpdateOutline(m_initialGestureDocPoint, 0); } QPointF KisToolFreehand::adjustPosition(const QPointF& point, const QPointF& strokeBegin) { if (m_assistant && static_cast(canvas())->paintingAssistantsDecoration()) { static_cast(canvas())->paintingAssistantsDecoration()->setOnlyOneAssistantSnap(m_only_one_assistant); QPointF ap = static_cast(canvas())->paintingAssistantsDecoration()->adjustPosition(point, strokeBegin); return (1.0 - m_magnetism) * point + m_magnetism * ap; } return point; } qreal KisToolFreehand::calculatePerspective(const QPointF &documentPoint) { qreal perspective = 1.0; Q_FOREACH (const QPointer grid, static_cast(canvas())->viewManager()->canvasResourceProvider()->perspectiveGrids()) { if (grid && grid->contains(documentPoint)) { perspective = grid->distance(documentPoint); break; } } return perspective; } void KisToolFreehand::explicitUpdateOutline() { requestUpdateOutline(m_outlineDocPoint, 0); } QPainterPath KisToolFreehand::getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode) { QPointF imagePos = convertToPixelCoord(documentPos); if (currentPaintOpPreset()) return m_helper->paintOpOutline(imagePos, event, currentPaintOpPreset()->settings(), outlineMode); else return QPainterPath(); } diff --git a/libs/ui/tool/kis_tool_freehand_helper.cpp b/libs/ui/tool/kis_tool_freehand_helper.cpp index 7bcae1f1b8..42e12ffda4 100644 --- a/libs/ui/tool/kis_tool_freehand_helper.cpp +++ b/libs/ui/tool/kis_tool_freehand_helper.cpp @@ -1,1003 +1,975 @@ /* * 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_tool_freehand_helper.h" #include #include #include #include #include #include "kis_algebra_2d.h" #include "kis_distance_information.h" #include "kis_painting_information_builder.h" #include "kis_image.h" #include "kis_painter.h" #include #include #include "kis_update_time_monitor.h" #include "kis_stabilized_events_sampler.h" #include "KisStabilizerDelayedPaintHelper.h" #include "kis_config.h" #include "kis_random_source.h" #include "KisPerStrokeRandomSource.h" #include "strokes/freehand_stroke.h" #include "strokes/KisFreehandStrokeInfo.h" #include //#define DEBUG_BEZIER_CURVES // Factor by which to scale the airbrush timer's interval, relative to the actual airbrushing rate. // Setting this less than 1 makes the timer-generated pseudo-events happen faster than the desired // airbrush rate, which can improve responsiveness. const qreal AIRBRUSH_INTERVAL_FACTOR = 0.5; // The amount of time, in milliseconds, to allow between updates of the spacing information. Only // used when spacing updates between dabs are enabled. const qreal SPACING_UPDATE_INTERVAL = 50.0; // The amount of time, in milliseconds, to allow between updates of the timing information. Only // used when airbrushing. const qreal TIMING_UPDATE_INTERVAL = 50.0; struct KisToolFreehandHelper::Private { KisPaintingInformationBuilder *infoBuilder; KisStrokesFacade *strokesFacade; KUndo2MagicString transactionText; bool haveTangent; QPointF previousTangent; bool hasPaintAtLeastOnce; QTime strokeTime; QTimer strokeTimeoutTimer; QVector strokeInfos; KisResourcesSnapshotSP resources; KisStrokeId strokeId; KisPaintInformation previousPaintInformation; KisPaintInformation olderPaintInformation; KisSmoothingOptionsSP smoothingOptions; // fake random sources for hovering outline *only* KisRandomSourceSP fakeDabRandomSource; KisPerStrokeRandomSourceSP fakeStrokeRandomSource; // Timer used to generate paint updates periodically even without input events. This is only // used for paintops that depend on timely updates even when the cursor is not moving, e.g. for // airbrushing effects. QTimer airbrushingTimer; QList history; QList distanceHistory; // Keeps track of past cursor positions. This is used to determine the drawing angle when // drawing the brush outline or starting a stroke. KisPaintOpUtils::PositionHistory lastCursorPos; // Stabilizer data bool usingStabilizer; QQueue stabilizerDeque; QTimer stabilizerPollTimer; KisStabilizedEventsSampler stabilizedSampler; KisStabilizerDelayedPaintHelper stabilizerDelayedPaintHelper; QTimer asynchronousUpdatesThresholdTimer; - int canvasRotation; - bool canvasMirroredH; - qreal effectiveSmoothnessDistance() const; }; KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText, KisSmoothingOptions *smoothingOptions) : m_d(new Private()) { m_d->infoBuilder = infoBuilder; m_d->transactionText = transactionText; m_d->smoothingOptions = KisSmoothingOptionsSP( smoothingOptions ? smoothingOptions : new KisSmoothingOptions()); - m_d->canvasRotation = 0; m_d->fakeDabRandomSource = new KisRandomSource(); m_d->fakeStrokeRandomSource = new KisPerStrokeRandomSource(); m_d->strokeTimeoutTimer.setSingleShot(true); connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke())); connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing())); connect(&m_d->asynchronousUpdatesThresholdTimer, SIGNAL(timeout()), SLOT(doAsynchronousUpdate())); connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint())); connect(m_d->smoothingOptions.data(), SIGNAL(sigSmoothingTypeChanged()), SLOT(slotSmoothingTypeChanged())); m_d->stabilizerDelayedPaintHelper.setPaintLineCallback( [this](const KisPaintInformation &pi1, const KisPaintInformation &pi2) { paintLine(pi1, pi2); }); m_d->stabilizerDelayedPaintHelper.setUpdateOutlineCallback( [this]() { emit requestExplicitUpdateOutline(); }); } KisToolFreehandHelper::~KisToolFreehandHelper() { delete m_d; } void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions) { m_d->smoothingOptions = smoothingOptions; } KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const { return m_d->smoothingOptions; } QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos, const KoPointerEvent *event, const KisPaintOpSettingsSP globalSettings, KisPaintOpSettings::OutlineMode mode) const { KisPaintOpSettingsSP settings = globalSettings; KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event); QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(savedCursorPos); qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, savedCursorPos, 0); - info.setCanvasRotation(m_d->canvasRotation); - info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH ); KisDistanceInformation distanceInfo(prevPoint, startAngle); if (!m_d->strokeInfos.isEmpty()) { settings = m_d->resources->currentPaintOpPreset()->settings(); if (m_d->stabilizerDelayedPaintHelper.running() && m_d->stabilizerDelayedPaintHelper.hasLastPaintInformation()) { info = m_d->stabilizerDelayedPaintHelper.lastPaintInformation(); } else { info = m_d->previousPaintInformation; } /** * When LoD mode is active it may happen that the helper has * already started a stroke, but it painted noting, because * all the work is being calculated by the scaled-down LodN * stroke. So at first we try to fetch the data from the lodN * stroke ("buddy") and then check if there is at least * something has been painted with this distance information * object. */ KisDistanceInformation *buddyDistance = m_d->strokeInfos.first()->buddyDragDistance(); if (buddyDistance) { /** * Tiny hack alert: here we fetch the distance information * directly from the LodN stroke. Ideally, we should * upscale its data, but here we just override it with our * local copy of the coordinates. */ distanceInfo = *buddyDistance; distanceInfo.overrideLastValues(prevPoint, startAngle); } else if (m_d->strokeInfos.first()->dragDistance->isStarted()) { distanceInfo = *m_d->strokeInfos.first()->dragDistance; } } KisPaintInformation::DistanceInformationRegistrar registrar = info.registerDistanceInformation(&distanceInfo); info.setRandomSource(m_d->fakeDabRandomSource); info.setPerStrokeRandomSource(m_d->fakeStrokeRandomSource); QPainterPath outline = settings->brushOutline(info, mode); if (m_d->resources && m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER && m_d->smoothingOptions->useDelayDistance()) { const qreal R = m_d->smoothingOptions->delayDistance() / m_d->resources->effectiveZoom(); outline.addEllipse(info.pos(), R, R); } return outline; } void KisToolFreehandHelper::cursorMoved(const QPointF &cursorPos) { m_d->lastCursorPos.pushThroughHistory(cursorPos); } void KisToolFreehandHelper::initPaint(KoPointerEvent *event, const QPointF &pixelCoords, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode, KisDefaultBoundsBaseSP bounds) { QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(pixelCoords); m_d->strokeTime.start(); KisPaintInformation pi = m_d->infoBuilder->startStroke(event, elapsedStrokeTime(), resourceManager); qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, pixelCoords, 0.0); initPaintImpl(startAngle, pi, resourceManager, image, currentNode, strokesFacade, overrideNode, bounds); } bool KisToolFreehandHelper::isRunning() const { return m_d->strokeId; } void KisToolFreehandHelper::initPaintImpl(qreal startAngle, const KisPaintInformation &pi, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode, KisDefaultBoundsBaseSP bounds) { m_d->strokesFacade = strokesFacade; m_d->haveTangent = false; m_d->previousTangent = QPointF(); m_d->hasPaintAtLeastOnce = false; m_d->previousPaintInformation = pi; m_d->resources = new KisResourcesSnapshot(image, currentNode, resourceManager, bounds); if(overrideNode) { m_d->resources->setCurrentNode(overrideNode); } const bool airbrushing = m_d->resources->needsAirbrushing(); const bool useSpacingUpdates = m_d->resources->needsSpacingUpdates(); KisDistanceInitInfo startDistInfo(m_d->previousPaintInformation.pos(), startAngle, useSpacingUpdates ? SPACING_UPDATE_INTERVAL : LONG_TIME, airbrushing ? TIMING_UPDATE_INTERVAL : LONG_TIME, 0); KisDistanceInformation startDist = startDistInfo.makeDistInfo(); createPainters(m_d->strokeInfos, startDist); KisStrokeStrategy *stroke = new FreehandStrokeStrategy(m_d->resources, m_d->strokeInfos, m_d->transactionText); m_d->strokeId = m_d->strokesFacade->startStroke(stroke); m_d->history.clear(); m_d->distanceHistory.clear(); if (airbrushing) { m_d->airbrushingTimer.setInterval(computeAirbrushTimerInterval()); m_d->airbrushingTimer.start(); } else if (m_d->resources->presetNeedsAsynchronousUpdates()) { m_d->asynchronousUpdatesThresholdTimer.setInterval(80 /* msec */); m_d->asynchronousUpdatesThresholdTimer.start(); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { stabilizerStart(m_d->previousPaintInformation); } // If airbrushing, paint an initial dab immediately. This is a workaround for an issue where // some paintops (Dyna, Particle, Sketch) might never initialize their spacing/timing // information until paintAt is called. if (airbrushing) { paintAt(pi); } } void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2, QPointF tangent1, QPointF tangent2) { if (tangent1.isNull() || tangent2.isNull()) return; const qreal maxSanePoint = 1e6; QPointF controlTarget1; QPointF controlTarget2; // Shows the direction in which control points go QPointF controlDirection1 = pi1.pos() + tangent1; QPointF controlDirection2 = pi2.pos() - tangent2; // Lines in the direction of the control points QLineF line1(pi1.pos(), controlDirection1); QLineF line2(pi2.pos(), controlDirection2); // Lines to check whether the control points lay on the opposite // side of the line QLineF line3(controlDirection1, controlDirection2); QLineF line4(pi1.pos(), pi2.pos()); QPointF intersection; if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) { qreal controlLength = line4.length() / 2; line1.setLength(controlLength); line2.setLength(controlLength); controlTarget1 = line1.p2(); controlTarget2 = line2.p2(); } else { QLineF::IntersectType type = line1.intersect(line2, &intersection); if (type == QLineF::NoIntersection || intersection.manhattanLength() > maxSanePoint) { intersection = 0.5 * (pi1.pos() + pi2.pos()); // dbgKrita << "WARNING: there is no intersection point " // << "in the basic smoothing algorithms"; } controlTarget1 = intersection; controlTarget2 = intersection; } // shows how near to the controlTarget the value raises qreal coeff = 0.8; qreal velocity1 = QLineF(QPointF(), tangent1).length(); qreal velocity2 = QLineF(QPointF(), tangent2).length(); if (velocity1 == 0.0 || velocity2 == 0.0) { velocity1 = 1e-6; velocity2 = 1e-6; warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2); } qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1); // the controls should not differ more than 50% similarity = qMax(similarity, qreal(0.5)); // when the controls are symmetric, their size should be smaller // to avoid corner-like curves coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8)); Q_ASSERT(coeff > 0); QPointF control1; QPointF control2; if (velocity1 > velocity2) { control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1; coeff *= similarity; control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2; } else { control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2; coeff *= similarity; control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1; } paintBezierCurve(pi1, control1, control2, pi2); } qreal KisToolFreehandHelper::Private::effectiveSmoothnessDistance() const { const qreal effectiveSmoothnessDistance = !smoothingOptions->useScalableDistance() ? smoothingOptions->smoothnessDistance() : smoothingOptions->smoothnessDistance() / resources->effectiveZoom(); return effectiveSmoothnessDistance; } void KisToolFreehandHelper::paintEvent(KoPointerEvent *event) { KisPaintInformation info = m_d->infoBuilder->continueStroke(event, elapsedStrokeTime()); - info.setCanvasRotation( m_d->canvasRotation ); - info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH ); - KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos()); paint(info); } void KisToolFreehandHelper::paint(KisPaintInformation &info) { /** * Smooth the coordinates out using the history and the * distance. This is a heavily modified version of an algo used in * Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and * http://www24.atwiki.jp/sigetch_2007/pages/17.html. The main * differences are: * * 1) It uses 'distance' instead of 'velocity', since time * measurements are too unstable in realworld environment * * 2) There is no 'Quality' parameter, since the number of samples * is calculated automatically * * 3) 'Tail Aggressiveness' is used for controlling the end of the * stroke * * 4) The formila is a little bit different: 'Distance' parameter * stands for $3 \Sigma$ */ if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING && m_d->smoothingOptions->smoothnessDistance() > 0.0) { { // initialize current distance QPointF prevPos; if (!m_d->history.isEmpty()) { const KisPaintInformation &prevPi = m_d->history.last(); prevPos = prevPi.pos(); } else { prevPos = m_d->previousPaintInformation.pos(); } qreal currentDistance = QVector2D(info.pos() - prevPos).length(); m_d->distanceHistory.append(currentDistance); } m_d->history.append(info); qreal x = 0.0; qreal y = 0.0; if (m_d->history.size() > 3) { const qreal sigma = m_d->effectiveSmoothnessDistance() / 3.0; // '3.0' for (3 * sigma) range qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma); qreal gaussianWeight2 = sigma * sigma; qreal distanceSum = 0.0; qreal scaleSum = 0.0; qreal pressure = 0.0; qreal baseRate = 0.0; Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size()); for (int i = m_d->history.size() - 1; i >= 0; i--) { qreal rate = 0.0; const KisPaintInformation nextInfo = m_d->history.at(i); double distance = m_d->distanceHistory.at(i); Q_ASSERT(distance >= 0.0); qreal pressureGrad = 0.0; if (i < m_d->history.size() - 1) { pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure(); const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness(); if (pressureGrad > 0.0 ) { pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure()); distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region } } if (gaussianWeight2 != 0.0) { distanceSum += distance; rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2)); } if (m_d->history.size() - i == 1) { baseRate = rate; } else if (baseRate / rate > 100) { break; } scaleSum += rate; x += rate * nextInfo.pos().x(); y += rate * nextInfo.pos().y(); if (m_d->smoothingOptions->smoothPressure()) { pressure += rate * nextInfo.pressure(); } } if (scaleSum != 0.0) { x /= scaleSum; y /= scaleSum; if (m_d->smoothingOptions->smoothPressure()) { pressure /= scaleSum; } } if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) { info.setPos(QPointF(x, y)); if (m_d->smoothingOptions->smoothPressure()) { info.setPressure(pressure); } m_d->history.last() = info; } } } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING || m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING) { // Now paint between the coordinates, using the bezier curve interpolation if (!m_d->haveTangent) { m_d->haveTangent = true; m_d->previousTangent = (info.pos() - m_d->previousPaintInformation.pos()) / qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime()); } else { QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) / qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime()); if (newTangent.isNull() || m_d->previousTangent.isNull()) { paintLine(m_d->previousPaintInformation, info); } else { paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation, m_d->previousTangent, newTangent); } m_d->previousTangent = newTangent; } m_d->olderPaintInformation = m_d->previousPaintInformation; // Enable stroke timeout only when not airbrushing. if (!m_d->airbrushingTimer.isActive()) { m_d->strokeTimeoutTimer.start(100); } } else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){ paintLine(m_d->previousPaintInformation, info); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { m_d->stabilizedSampler.addEvent(info); if (m_d->stabilizerDelayedPaintHelper.running()) { // Paint here so we don't have to rely on the timer // This is just a tricky source for a relatively stable 7ms "timer" m_d->stabilizerDelayedPaintHelper.paintSome(); } } else { m_d->previousPaintInformation = info; } if(m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.start(); } } void KisToolFreehandHelper::endPaint() { if (!m_d->hasPaintAtLeastOnce) { paintAt(m_d->previousPaintInformation); } else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) { finishStroke(); } m_d->strokeTimeoutTimer.stop(); if(m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.stop(); } if (m_d->asynchronousUpdatesThresholdTimer.isActive()) { m_d->asynchronousUpdatesThresholdTimer.stop(); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { stabilizerEnd(); } /** * There might be some timer events still pending, so * we should cancel them. Use this flag for the purpose. * Please note that we are not in MT here, so no mutex * is needed */ m_d->strokeInfos.clear(); // last update to complete rendering if there is still something pending doAsynchronousUpdate(true); m_d->strokesFacade->endStroke(m_d->strokeId); m_d->strokeId.clear(); } void KisToolFreehandHelper::cancelPaint() { if (!m_d->strokeId) return; m_d->strokeTimeoutTimer.stop(); if (m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.stop(); } if (m_d->asynchronousUpdatesThresholdTimer.isActive()) { m_d->asynchronousUpdatesThresholdTimer.stop(); } if (m_d->stabilizerPollTimer.isActive()) { m_d->stabilizerPollTimer.stop(); } if (m_d->stabilizerDelayedPaintHelper.running()) { m_d->stabilizerDelayedPaintHelper.cancel(); } // see a comment in endPaint() m_d->strokeInfos.clear(); m_d->strokesFacade->cancelStroke(m_d->strokeId); m_d->strokeId.clear(); } int KisToolFreehandHelper::elapsedStrokeTime() const { return m_d->strokeTime.elapsed(); } void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo) { m_d->usingStabilizer = true; // FIXME: Ugly hack, this is no a "distance" in any way int sampleSize = qRound(m_d->effectiveSmoothnessDistance()); sampleSize = qMax(3, sampleSize); // Fill the deque with the current value repeated until filling the sample m_d->stabilizerDeque.clear(); for (int i = sampleSize; i > 0; i--) { m_d->stabilizerDeque.enqueue(firstPaintInfo); } // Poll and draw regularly KisConfig cfg(true); int stabilizerSampleSize = cfg.stabilizerSampleSize(); m_d->stabilizerPollTimer.setInterval(stabilizerSampleSize); m_d->stabilizerPollTimer.start(); bool delayedPaintEnabled = cfg.stabilizerDelayedPaint(); if (delayedPaintEnabled) { m_d->stabilizerDelayedPaintHelper.start(firstPaintInfo); } m_d->stabilizedSampler.clear(); m_d->stabilizedSampler.addEvent(firstPaintInfo); } KisPaintInformation KisToolFreehandHelper::getStabilizedPaintInfo(const QQueue &queue, const KisPaintInformation &lastPaintInfo) { KisPaintInformation result(lastPaintInfo.pos(), lastPaintInfo.pressure(), lastPaintInfo.xTilt(), lastPaintInfo.yTilt(), lastPaintInfo.rotation(), lastPaintInfo.tangentialPressure(), lastPaintInfo.perspective(), elapsedStrokeTime(), lastPaintInfo.drawingSpeed()); if (queue.size() > 1) { QQueue::const_iterator it = queue.constBegin(); QQueue::const_iterator end = queue.constEnd(); /** * The first point is going to be overridden by lastPaintInfo, skip it. */ it++; int i = 2; if (m_d->smoothingOptions->stabilizeSensors()) { while (it != end) { qreal k = qreal(i - 1) / i; // coeff for uniform averaging result.KisPaintInformation::mixOtherWithoutTime(k, *it); it++; i++; } } else{ while (it != end) { qreal k = qreal(i - 1) / i; // coeff for uniform averaging result.KisPaintInformation::mixOtherOnlyPosition(k, *it); it++; i++; } } } return result; } void KisToolFreehandHelper::stabilizerPollAndPaint() { KisStabilizedEventsSampler::iterator it; KisStabilizedEventsSampler::iterator end; std::tie(it, end) = m_d->stabilizedSampler.range(); QVector delayedPaintTodoItems; for (; it != end; ++it) { KisPaintInformation sampledInfo = *it; bool canPaint = true; if (m_d->smoothingOptions->useDelayDistance()) { const qreal R = m_d->smoothingOptions->delayDistance() / m_d->resources->effectiveZoom(); QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos(); qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y())); if (!(dx > R)) { if (m_d->resources->needsAirbrushing()) { sampledInfo.setPos(m_d->previousPaintInformation.pos()); } else { canPaint = false; } } } if (canPaint) { KisPaintInformation newInfo = getStabilizedPaintInfo(m_d->stabilizerDeque, sampledInfo); if (m_d->stabilizerDelayedPaintHelper.running()) { delayedPaintTodoItems.append(newInfo); } else { paintLine(m_d->previousPaintInformation, newInfo); } m_d->previousPaintInformation = newInfo; // Push the new entry through the queue m_d->stabilizerDeque.dequeue(); m_d->stabilizerDeque.enqueue(sampledInfo); } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) { QQueue::iterator it = m_d->stabilizerDeque.begin(); QQueue::iterator end = m_d->stabilizerDeque.end(); while (it != end) { *it = m_d->previousPaintInformation; ++it; } } } m_d->stabilizedSampler.clear(); if (m_d->stabilizerDelayedPaintHelper.running()) { m_d->stabilizerDelayedPaintHelper.update(delayedPaintTodoItems); } else { emit requestExplicitUpdateOutline(); } } void KisToolFreehandHelper::stabilizerEnd() { // Stop the timer m_d->stabilizerPollTimer.stop(); // Finish the line if (m_d->smoothingOptions->finishStabilizedCurve()) { // Process all the existing events first stabilizerPollAndPaint(); // Draw the finish line with pending events and a time override m_d->stabilizedSampler.addFinishingEvent(m_d->stabilizerDeque.size()); stabilizerPollAndPaint(); } if (m_d->stabilizerDelayedPaintHelper.running()) { m_d->stabilizerDelayedPaintHelper.end(); } m_d->usingStabilizer = false; } void KisToolFreehandHelper::slotSmoothingTypeChanged() { if (!isRunning()) { return; } KisSmoothingOptions::SmoothingType currentSmoothingType = m_d->smoothingOptions->smoothingType(); if (m_d->usingStabilizer && (currentSmoothingType != KisSmoothingOptions::STABILIZER)) { stabilizerEnd(); } else if (!m_d->usingStabilizer && (currentSmoothingType == KisSmoothingOptions::STABILIZER)) { stabilizerStart(m_d->previousPaintInformation); } } void KisToolFreehandHelper::finishStroke() { if (m_d->haveTangent) { m_d->haveTangent = false; QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) / (m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime()); paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation, m_d->previousTangent, newTangent); } } void KisToolFreehandHelper::doAirbrushing() { // Check that the stroke hasn't ended. if (!m_d->strokeInfos.isEmpty()) { // Add a new painting update at a point identical to the previous one, except for the time // and speed information. const KisPaintInformation &prevPaint = m_d->previousPaintInformation; KisPaintInformation nextPaint(prevPaint.pos(), prevPaint.pressure(), prevPaint.xTilt(), prevPaint.yTilt(), prevPaint.rotation(), prevPaint.tangentialPressure(), prevPaint.perspective(), elapsedStrokeTime(), 0.0); paint(nextPaint); } } void KisToolFreehandHelper::doAsynchronousUpdate(bool forceUpdate) { m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::UpdateData(forceUpdate)); } int KisToolFreehandHelper::computeAirbrushTimerInterval() const { qreal realInterval = m_d->resources->airbrushingInterval() * AIRBRUSH_INTERVAL_FACTOR; return qMax(1, qFloor(realInterval)); } void KisToolFreehandHelper::paintAt(int strokeInfoId, const KisPaintInformation &pi) { m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(strokeInfoId, pi)); } void KisToolFreehandHelper::paintLine(int strokeInfoId, const KisPaintInformation &pi1, const KisPaintInformation &pi2) { m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(strokeInfoId, pi1, pi2)); } void KisToolFreehandHelper::paintBezierCurve(int strokeInfoId, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2) { #ifdef DEBUG_BEZIER_CURVES KisPaintInformation tpi1; KisPaintInformation tpi2; tpi1 = pi1; tpi2 = pi2; tpi1.setPressure(0.3); tpi2.setPressure(0.3); paintLine(tpi1, tpi2); tpi1.setPressure(0.6); tpi2.setPressure(0.3); tpi1.setPos(pi1.pos()); tpi2.setPos(control1); paintLine(tpi1, tpi2); tpi1.setPos(pi2.pos()); tpi2.setPos(control2); paintLine(tpi1, tpi2); #endif m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(strokeInfoId, pi1, control1, control2, pi2)); } void KisToolFreehandHelper::createPainters(QVector &strokeInfos, const KisDistanceInformation &startDist) { strokeInfos << new KisFreehandStrokeInfo(startDist); } void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi) { paintAt(0, pi); } void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2) { paintLine(0, pi1, pi2); } void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2) { paintBezierCurve(0, pi1, control1, control2, pi2); } - -int KisToolFreehandHelper::canvasRotation() -{ - return m_d->canvasRotation; -} - -void KisToolFreehandHelper::setCanvasRotation(int rotation) -{ - m_d->canvasRotation = rotation; -} -bool KisToolFreehandHelper::canvasMirroredH() -{ - return m_d->canvasMirroredH; -} - -void KisToolFreehandHelper::setCanvasHorizontalMirrorState(bool mirrored) -{ - m_d->canvasMirroredH = mirrored; -} diff --git a/libs/ui/tool/kis_tool_freehand_helper.h b/libs/ui/tool/kis_tool_freehand_helper.h index 39c3c56b9d..6323952380 100644 --- a/libs/ui/tool/kis_tool_freehand_helper.h +++ b/libs/ui/tool/kis_tool_freehand_helper.h @@ -1,166 +1,163 @@ /* * 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. */ #ifndef __KIS_TOOL_FREEHAND_HELPER_H #define __KIS_TOOL_FREEHAND_HELPER_H #include #include #include "kis_types.h" #include "kritaui_export.h" #include #include "kis_default_bounds.h" #include #include "kis_smoothing_options.h" #include "kundo2magicstring.h" class KoPointerEvent; class KoCanvasResourceProvider; class KisPaintingInformationBuilder; class KisStrokesFacade; class KisPostExecutionUndoAdapter; class KisPaintOp; class KisFreehandStrokeInfo; class KRITAUI_EXPORT KisToolFreehandHelper : public QObject { Q_OBJECT public: KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText = KUndo2MagicString(), KisSmoothingOptions *smoothingOptions = 0); ~KisToolFreehandHelper() override; void setSmoothness(KisSmoothingOptionsSP smoothingOptions); KisSmoothingOptionsSP smoothingOptions() const; bool isRunning() const; void cursorMoved(const QPointF &cursorPos); /** * @param event The event * @param pixelCoords The position of the KoPointerEvent, in pixel coordinates. * @param resourceManager The canvas resource manager * @param image The image * @param currentNode The current node * @param strokesFacade The strokes facade * @param overrideNode The override node * @param bounds The bounds */ void initPaint(KoPointerEvent *event, const QPointF &pixelCoords, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode = 0, KisDefaultBoundsBaseSP bounds = 0); void paintEvent(KoPointerEvent *event); void endPaint(); QPainterPath paintOpOutline(const QPointF &savedCursorPos, const KoPointerEvent *event, const KisPaintOpSettingsSP globalSettings, KisPaintOpSettings::OutlineMode mode) const; - int canvasRotation(); - void setCanvasRotation(int rotation = 0); - bool canvasMirroredH(); - void setCanvasHorizontalMirrorState (bool mirrored = false); + Q_SIGNALS: /** * The signal is emitted when the outline should be updated * explicitly by the tool. Used by Stabilizer option, because it * paints on internal timer events instead of the on every paint() * event */ void requestExplicitUpdateOutline(); protected: void cancelPaint(); int elapsedStrokeTime() const; void initPaintImpl(qreal startAngle, const KisPaintInformation &pi, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP node, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode = 0, KisDefaultBoundsBaseSP bounds = 0); protected: virtual void createPainters(QVector &strokeInfos, const KisDistanceInformation &startDist); // lo-level methods for painting primitives void paintAt(int strokeInfoId, const KisPaintInformation &pi); void paintLine(int strokeInfoId, const KisPaintInformation &pi1, const KisPaintInformation &pi2); void paintBezierCurve(int strokeInfoId, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2); // hi-level methods for painting primitives virtual void paintAt(const KisPaintInformation &pi); virtual void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2); virtual void paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2); private: void paint(KisPaintInformation &info); void paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2, QPointF tangent1, QPointF tangent2); void stabilizerStart(KisPaintInformation firstPaintInfo); void stabilizerEnd(); KisPaintInformation getStabilizedPaintInfo(const QQueue &queue, const KisPaintInformation &lastPaintInfo); int computeAirbrushTimerInterval() const; private Q_SLOTS: void finishStroke(); void doAirbrushing(); void doAsynchronousUpdate(bool forceUpdate = false); void stabilizerPollAndPaint(); void slotSmoothingTypeChanged(); private: struct Private; Private * const m_d; }; #endif /* __KIS_TOOL_FREEHAND_HELPER_H */ diff --git a/libs/ui/tool/kis_tool_multihand_helper.cpp b/libs/ui/tool/kis_tool_multihand_helper.cpp index f34368f99f..b99c713be0 100644 --- a/libs/ui/tool/kis_tool_multihand_helper.cpp +++ b/libs/ui/tool/kis_tool_multihand_helper.cpp @@ -1,154 +1,134 @@ /* * 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_tool_multihand_helper.h" #include #include "kis_painter.h" #include +#include "kis_algebra_2d.h" struct KisToolMultihandHelper::Private { QVector transformations; }; KisToolMultihandHelper::KisToolMultihandHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText) : KisToolFreehandHelper(infoBuilder, transactionText) , d(new Private) { } KisToolMultihandHelper::~KisToolMultihandHelper() { delete d; } void KisToolMultihandHelper::setupTransformations(const QVector &transformations) { d->transformations = transformations; } void KisToolMultihandHelper::createPainters(QVector &strokeInfos, const KisDistanceInformation &startDist) { for (int i = 0; i < d->transformations.size(); i++) { const QTransform &transform = d->transformations[i]; KisDistanceInitInfo __startDistInfo(transform.map(startDist.lastPosition()), startDist.lastDrawingAngle(), startDist.getSpacingInterval(), startDist.getTimingUpdateInterval(), 0); KisDistanceInformation __startDist = __startDistInfo.makeDistInfo(); strokeInfos << new KisFreehandStrokeInfo(__startDist); } } +void adjustPointInformationRotation(KisPaintInformation &pi, const QTransform &t) +{ + KisAlgebra2D::DecomposedMatix d(t); + + qreal rotation = d.angle; + const bool mirrorX = KisAlgebra2D::signPZ(d.scaleX) < 0; + const bool mirrorY = KisAlgebra2D::signPZ(d.scaleY) < 0; + + pi.setCanvasMirroredH(pi.canvasMirroredH() ^ mirrorX); + pi.setCanvasMirroredV(pi.canvasMirroredV() ^ mirrorY); + + if (pi.canvasMirroredH()!= pi.canvasMirroredV()) { + rotation = normalizeAngleDegrees(360.0 - rotation); + } + + pi.setCanvasRotation(normalizeAngleDegrees(pi.canvasRotation() - rotation)); +} + + void KisToolMultihandHelper::paintAt(const KisPaintInformation &pi) { for (int i = 0; i < d->transformations.size(); i++) { const QTransform &transform = d->transformations[i]; - KisPaintInformation __pi = pi; - QLineF rotateme(QPointF (0.0,0.0), QPointF (10.0,10.0)); - rotateme.setAngle(__pi.canvasRotation()); - QLineF rotated = transform.map(rotateme); - __pi.setPos(transform.map(__pi.pos())); - __pi.setCanvasRotation(rotated.angle()); - - if (__pi.canvasMirroredH()) { - __pi.setCanvasRotation(180-__pi.canvasRotation()); - __pi.setCanvasRotation(__pi.canvasRotation()+180); - } - + adjustPointInformationRotation(__pi, transform); paintAt(i, __pi); } } void KisToolMultihandHelper::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2) { for (int i = 0; i < d->transformations.size(); i++) { const QTransform &transform = d->transformations[i]; KisPaintInformation __pi1 = pi1; KisPaintInformation __pi2 = pi2; __pi1.setPos(transform.map(__pi1.pos())); __pi2.setPos(transform.map(__pi2.pos())); - QLineF rotateme(QPointF (0.0,0.0), QPointF (10.0,10.0)); - rotateme.setAngle(__pi1.canvasRotation()); - QLineF rotated = transform.map(rotateme); - __pi1.setCanvasRotation(rotated.angle()); - - rotateme.setAngle(__pi2.canvasRotation()); - rotated = transform.map(rotateme); - __pi2.setCanvasRotation(rotated.angle()); - - //check mirroring - if (__pi2.canvasMirroredH()) { - __pi1.setCanvasRotation(180-__pi1.canvasRotation()); - __pi1.setCanvasRotation(__pi1.canvasRotation()+180); - __pi2.setCanvasRotation(180-__pi2.canvasRotation()); - __pi2.setCanvasRotation(__pi2.canvasRotation()+180); - } - + adjustPointInformationRotation(__pi1, transform); + adjustPointInformationRotation(__pi2, transform); paintLine(i, __pi1, __pi2); } } void KisToolMultihandHelper::paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2) { for (int i = 0; i < d->transformations.size(); i++) { const QTransform &transform = d->transformations[i]; KisPaintInformation __pi1 = pi1; KisPaintInformation __pi2 = pi2; __pi1.setPos(transform.map(__pi1.pos())); __pi2.setPos(transform.map(__pi2.pos())); - QLineF rotateme(QPointF (0.0,0.0), QPointF (10.0,10.0)); - rotateme.setAngle(__pi1.canvasRotation()); - QLineF rotated = transform.map(rotateme); - __pi1.setCanvasRotation(rotated.angle()); - - rotateme.setAngle(__pi2.canvasRotation()); - rotated = transform.map(rotateme); - __pi2.setCanvasRotation(rotated.angle()); - - if (__pi2.canvasMirroredH()) { - __pi1.setCanvasRotation(180-__pi1.canvasRotation()); - __pi1.setCanvasRotation(__pi1.canvasRotation()+180); - __pi2.setCanvasRotation(180-__pi2.canvasRotation()); - __pi2.setCanvasRotation(__pi2.canvasRotation()+180); - } - + adjustPointInformationRotation(__pi1, transform); + adjustPointInformationRotation(__pi2, transform); QPointF __control1 = transform.map(control1); QPointF __control2 = transform.map(control2); paintBezierCurve(i, __pi1, __control1, __control2, __pi2); } } diff --git a/plugins/impex/gif/qgiflibhandler.cpp b/plugins/impex/gif/qgiflibhandler.cpp index ed3f8227ca..fec916c424 100644 --- a/plugins/impex/gif/qgiflibhandler.cpp +++ b/plugins/impex/gif/qgiflibhandler.cpp @@ -1,368 +1,367 @@ /* * Copyright (C) 2009 Shawn T. Rutledge (shawn.t.rutledge@gmail.com) * Copyright (c) 2018 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 * */ #include "qgiflibhandler.h" #include #include #include #include // memset #include extern int _GifError; static const int InterlacedOffset[] = { 0, 4, 2, 1 }; /* The way Interlaced image should */ static const int InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */ int doOutput(GifFileType* gif, const GifByteType * data, int i) { QIODevice* out = (QIODevice*)gif->UserData; // qDebug("given %d bytes to write; device is writeable? %d", i, out->isWritable()); return out->write((const char*)data, i); } int doInput(GifFileType* gif, GifByteType* data, int i) { QIODevice* in = (QIODevice*)gif->UserData; return in->read((char*)data, i); } QGIFLibHandler::QGIFLibHandler() : QImageIOHandler() { } bool QGIFLibHandler::canRead () const { if (canRead(device())) { setFormat("gif"); return true; } return false; } bool QGIFLibHandler::read ( QImage * image ) { // The contents of this function are based on gif2rgb.c, from the giflib source. // qDebug("QGIFLibHandler::read into image with size %d x %d", image->size().width(), image->size().height()); int err; GifFileType* gifFile = DGifOpen(device(), doInput, &err); if (!gifFile) { qWarning() << "Received error code" << err; return false; } // qDebug("dimensions %d x %d", gifFile->SWidth, gifFile->SHeight); *image = QImage(gifFile->SWidth, gifFile->SHeight, QImage::Format_Indexed8); GifRecordType recordType; ColorMapObject* ColorMap; - int i, row, imageNum = 0, topRow, leftCol, width, height; + int i, row, imageNum = 0, topRow, width, height; int transColor = -1; do { DGifGetRecordType(gifFile, &recordType); switch (recordType) { case IMAGE_DESC_RECORD_TYPE: if (DGifGetImageDesc(gifFile) == GIF_ERROR) { qWarning("QGIFLibHandler::read: error %d", gifFile->Error); return false; } topRow = gifFile->Image.Top; /* Image Position relative to Screen. */ - leftCol = gifFile->Image.Left; width = gifFile->Image.Width; height = gifFile->Image.Height; - //qDebug("Image %d at (%d, %d) [%dx%d]", ++imageNum, leftCol, topRow, width, height); + //qDebug("Image %d at (%d, %d) [%dx%d]", ++imageNum, gifFile->Image.Left, topRow, width, height); if (gifFile->Image.Left + width > gifFile->SWidth || gifFile->Image.Top + height > gifFile->SHeight) { qWarning("Image %d is not confined to screen dimension, aborted.", imageNum); return false; } // Pre-fill with background color // qDebug("background color is at index %d", gifFile->SBackGroundColor); image->fill(gifFile->SBackGroundColor); // Now read the image data if (gifFile->Image.Interlace) { /* Need to perform 4 passes on the images: */ for (i = 0; i < 4; i++) for (row = topRow + InterlacedOffset[i]; row < topRow + height; row += InterlacedJumps[i]) { if (DGifGetLine(gifFile, image->scanLine(row), width) == GIF_ERROR) { qWarning("QGIFLibHandler::read: error %d", gifFile->Error); return false; } // else // qDebug("got row %d: %d %d %d %d %d %d %d %d ...", row, // image->scanLine(row)[0], image->scanLine(row)[1], image->scanLine(row)[2], image->scanLine(row)[3], // image->scanLine(row)[4], image->scanLine(row)[5], image->scanLine(row)[6], image->scanLine(row)[7]); } } else { for (row = 0; row < height; row++) { if (DGifGetLine(gifFile, image->scanLine(row), width) == GIF_ERROR) { qWarning("QGIFLibHandler::read: error %d", gifFile->Error); return false; } // else // qDebug("got row %d: %d %d %d %d %d %d %d %d ...", row, // image->scanLine(row)[0], image->scanLine(row)[1], image->scanLine(row)[2], image->scanLine(row)[3], // image->scanLine(row)[4], image->scanLine(row)[5], image->scanLine(row)[6], image->scanLine(row)[7]); } } break; case EXTENSION_RECORD_TYPE: { int extCode; GifByteType* extData; /* Skip any extension blocks in file: */ if (DGifGetExtension(gifFile, &extCode, &extData) == GIF_ERROR) { qWarning("QGIFLibHandler::read: error %d", gifFile->Error); return false; } while (extData != NULL) { int len = extData[0]; switch (extCode) { case GRAPHICS_EXT_FUNC_CODE: // Graphics control extension // qDebug("graphics control: %x %x %x %x %x", extData[0], extData[1], extData[2], extData[3], extData[4]); // Should be block size, packed fields, delay time, // transparent color, block terminator // see doc/gif89.txt in libgif source package // If the trans bit is set in packed fields, // then set the trans color to the one given if (extData[1] & 0x01) { transColor = extData[3]; // qDebug("transparent color is at index %d", transColor); /// @todo is it correct to override default fill color? // image->fill(transColor); } break; case COMMENT_EXT_FUNC_CODE: { QByteArray comment((char*)(extData + 1), len); // qDebug("comment of len %d: \"%s\"", len, comment.constData()); image->setText("Description", comment); } break; case PLAINTEXT_EXT_FUNC_CODE: break; } if (DGifGetExtensionNext(gifFile, &extData) == GIF_ERROR) { qWarning("QGIFLibHandler::read: error %d", gifFile->Error); return false; } } } break; case TERMINATE_RECORD_TYPE: break; default: break; } } while (recordType != TERMINATE_RECORD_TYPE); // BackGround = gifFile->SBackGroundColor; ColorMap = (gifFile->Image.ColorMap ? gifFile->Image.ColorMap : gifFile->SColorMap); if (!ColorMap) { qWarning("QGIFLibHandler::read: Image does not have a colormap"); return false; } int ccount = ColorMap->ColorCount; image->setColorCount(ccount); for (i = 0; i < ccount; ++i) { GifColorType gifColor = ColorMap->Colors[i]; QRgb color = gifColor.Blue | (gifColor.Green << 8) | (gifColor.Red << 16); // If this is not the transparent color, // set the alpha to opaque. if (i != transColor) color |= 0xff << 24; // qDebug("color %d: 0x%X", i, color); image->setColor(i, color); } return true; } bool QGIFLibHandler::canRead(QIODevice *device) { if (!device) { qWarning("QGIFLibHandler::canRead() called with no device"); return false; } char head[6]; if (device->peek(head, sizeof(head)) == sizeof(head)) return qstrncmp(head, "GIF87a", 6) == 0 || qstrncmp(head, "GIF89a", 6) == 0; return false; } bool QGIFLibHandler::write ( const QImage & image ) { QImage toWrite(image); /// @todo how to specify dithering method if (toWrite.colorCount() == 0 || toWrite.colorCount() > 256) toWrite = image.convertToFormat(QImage::Format_Indexed8); QVector colorTable = toWrite.colorTable(); ColorMapObject cmap; // colorCount must be a power of 2 int colorCount = 1 << GifBitSize(toWrite.colorCount()); cmap.ColorCount = colorCount; cmap.BitsPerPixel = 8; /// @todo based on colorCount (or not? we did ask for Format_Indexed8, so the data is always 8-bit, right?) GifColorType* colorValues = (GifColorType*)malloc(cmap.ColorCount * sizeof(GifColorType)); cmap.Colors = colorValues; int c = 0; for(; c < toWrite.colorCount(); ++c) { // qDebug("color %d has %02X%02X%02X", c, qRed(colorTable[c]), qGreen(colorTable[c]), qBlue(colorTable[c])); colorValues[c].Red = qRed(colorTable[c]); colorValues[c].Green = qGreen(colorTable[c]); colorValues[c].Blue = qBlue(colorTable[c]); } // In case we had an actual number of colors that's not a power of 2, // fill the rest with something (black perhaps). for (; c < colorCount; ++c) { colorValues[c].Red = 0; colorValues[c].Green = 0; colorValues[c].Blue = 0; } /// @todo transparent GIFs (use alpha?) /// @todo write to m_device int err; GifFileType *gif = EGifOpen(device(), doOutput, &err); /// @todo how to specify which version, or decide based on features in use // Because of this call, libgif is not re-entrant EGifSetGifVersion(gif, true); /// @todo how to specify background if (EGifPutScreenDesc(gif, toWrite.width(), toWrite.height(), colorCount, 0, &cmap) == GIF_ERROR) { qWarning("EGifPutScreenDesc returned error %d", gif->Error); } QVariant descText = option(QImageIOHandler::Description); if (descText.type() == QVariant::String) { QString comment = descText.toString(); // Will be something like "Description: actual text" or just // ": actual text", so remove everything leading up to and // including the first colon and the space following it. int idx = comment.indexOf(": "); if (idx >= 0) comment.remove(0, idx + 2); // qDebug() << "comment:" << comment; if (!comment.isEmpty()) EGifPutComment(gif, comment.toUtf8().constData()); } // else // qDebug("description is of qvariant type %d", descText.type()); /// @todo foreach of multiple images in an animation... if (EGifPutImageDesc(gif, 0, 0, toWrite.width(), toWrite.height(), 0, &cmap) == GIF_ERROR) qWarning("EGifPutImageDesc returned error %d", gif->Error); int lc = toWrite.height(); // NOTE: we suppose that the pixel size is exactly 1 byte, right now we // cannot save anything else int llen = toWrite.width(); // qDebug("will write %d lines, %d bytes each", lc, llen); for (int l = 0; l < lc; ++l) { uchar* line = toWrite.scanLine(l); if (EGifPutLine(gif, (GifPixelType*)line, llen) == GIF_ERROR) { int i = gif->Error; qWarning("EGifPutLine returned error %d", i); } } EGifCloseFile(gif, &err); free(colorValues); return true; } bool QGIFLibHandler::supportsOption ( ImageOption option ) const { // qDebug("supportsOption %d", option); switch (option) { // These are relevant only for reading case QImageIOHandler::ImageFormat: case QImageIOHandler::Size: // This is relevant for both reading and writing case QImageIOHandler::Description: return true; break; default: return false; } } void QGIFLibHandler::setOption ( ImageOption option, const QVariant & value ) { // qDebug("setOption given option %d, variant of type %d", option, value.type()); if (option == QImageIOHandler::Description) m_description = value.toString(); } QVariant QGIFLibHandler::option( ImageOption option ) const { switch (option) { case QImageIOHandler::ImageFormat: return QVariant(); /// @todo break; case QImageIOHandler::Size: return QVariant(); /// @todo break; case QImageIOHandler::Description: return QVariant(m_description); break; default: return QVariant(); } } diff --git a/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp b/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp index 46b44aa39b..7389cbc8f5 100644 --- a/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp +++ b/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp @@ -1,242 +1,254 @@ /* * Copyright (c) 2013 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_brushop_test.h" #include #include #include #include #include #include #include #include #include class TestBrushOp : public TestUtil::QImageBasedTest { public: TestBrushOp(const QString &presetFileName, const QString &prefix = "simple") : QImageBasedTest("brushop") { m_presetFileName = presetFileName; m_prefix = prefix; } virtual ~TestBrushOp() {} void test() { test(false, false, 0.0); test(false, false, 10.0); test(false, false, 20.0); test(true, false, 0.0); test(true, false, 10.0); test(true, false, 20.0); test(false, true, 0.0); test(false, true, 10.0); test(false, true, 20.0); test(true, true, 0.0); test(true, true, 10.0); test(true, true, 20.0); } void test(bool mirrorX, bool mirrorY, qreal rotation) { test(mirrorX, mirrorY, rotation, false, false); test(mirrorX, mirrorY, rotation, true, false); test(mirrorX, mirrorY, rotation, false, true); test(mirrorX, mirrorY, rotation, true, true); } void test(bool mirrorX, bool mirrorY, qreal rotation, bool mirrorDabX, bool mirrorDabY) { test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 0.0); test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 360.0 - 10.0); test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 360.0 - 20.0); } void test(bool mirrorX, bool mirrorY, qreal rotation, bool mirrorDabX, bool mirrorDabY, qreal dabRotation) { KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore(); KisImageSP image = createTrivialImage(undoStore); image->initialRefreshGraph(); KisNodeSP paint1 = findNode(image->root(), "paint1"); QVERIFY(paint1->extent().isEmpty()); KisPainter gc(paint1->paintDevice()); QScopedPointer manager( utils::createResourceManager(image, 0, m_presetFileName)); KisPaintOpPresetSP preset = manager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); - preset->settings()->setCanvasRotation(rotation); - preset->settings()->setCanvasMirroring(mirrorY, mirrorX); - - if (mirrorDabX || mirrorDabY) { KisPaintOpSettingsSP settings = preset->settings()->clone(); KisPressureMirrorOption mirrorOption; mirrorOption.readOptionSetting(settings); mirrorOption.setChecked(true); mirrorOption.setCurveUsed(false); mirrorOption.enableHorizontalMirror(mirrorDabX); mirrorOption.enableVerticalMirror(mirrorDabY); mirrorOption.writeOptionSetting(settings.data()); preset->setSettings(settings); } if (dabRotation != 0.0) { KisPaintOpSettingsSP settings = preset->settings()->clone(); KisPressureRotationOption rotationOption; rotationOption.readOptionSetting(settings); rotationOption.setChecked(true); rotationOption.setCurveUsed(false); rotationOption.setValue(dabRotation / 360.0); rotationOption.writeOptionSetting(settings.data()); preset->setSettings(settings); } QString testName = QString("%7_cmY_%1_cmX_%2_cR_%3_dmX_%4_dmY_%5_dR_%6") .arg(mirrorY) .arg(mirrorX) .arg(rotation) .arg(mirrorDabX) .arg(mirrorDabY) .arg(std::fmod(360.0 - dabRotation, 360.0)) .arg(m_prefix); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, paint1, manager.data()); resources->setupPainter(&gc); - doPaint(gc); + doPaint(gc, rotation, mirrorX, mirrorY); checkOneLayer(image, paint1, testName); } - virtual void doPaint(KisPainter &gc) { + virtual void doPaint(KisPainter &gc, qreal rotation, bool mirrorX, bool mirrorY) { KisPaintInformation pi(QPointF(100, 100), 1.0); + pi.setCanvasRotation(rotation); + pi.setCanvasMirroredH(mirrorX); + pi.setCanvasMirroredV(mirrorY); KisDistanceInformation dist; gc.paintAt(pi, &dist); } QString m_presetFileName; QString m_prefix; }; class TestBrushOpLines : public TestBrushOp { public: TestBrushOpLines(const QString &presetFileName) : TestBrushOp(presetFileName) { } - void doPaint(KisPainter &gc) override { + void doPaint(KisPainter &gc, qreal rotation, bool mirrorX, bool mirrorY) override { QVector vector; vector << KisPaintInformation(QPointF(100, 100)); vector << KisPaintInformation(QPointF(200, 150)); vector << KisPaintInformation(QPointF(100, 350)); + + for (auto pi : vector) { + pi.setCanvasRotation(rotation); + pi.setCanvasMirroredH(mirrorX); + pi.setCanvasMirroredV(mirrorY); + } + KisDistanceInformation dist; for (int i = 1; i < vector.size(); i++) { gc.paintLine(vector[i - 1], vector[i], &dist); } } }; class TestBrushOpPressureLines : public TestBrushOp { public: TestBrushOpPressureLines(const QString &presetFileName, const QString &prefix) : TestBrushOp(presetFileName, prefix) { } - void doPaint(KisPainter &gc) override { + void doPaint(KisPainter &gc, qreal rotation, bool mirrorX, bool mirrorY) override { QVector vector; vector << KisPaintInformation(QPointF(0, 0), 0.2); vector << KisPaintInformation(QPointF(200, 50), 1.0); vector << KisPaintInformation(QPointF(100, 250), 0.0); vector << KisPaintInformation(QPointF(200, 150), 1.0); vector << KisPaintInformation(QPointF(100, 350), 1.0); + for (auto pi : vector) { + pi.setCanvasRotation(rotation); + pi.setCanvasMirroredH(mirrorX); + pi.setCanvasMirroredV(mirrorY); + } + KisDistanceInformation dist; for (int i = 1; i < vector.size(); i++) { gc.paintLine(vector[i - 1], vector[i], &dist); } } }; #include void KisBrushOpTest::initTestCase() { KoResourcePaths::addResourceDir(ResourceType::Brushes, QString(SYSTEM_RESOURCES_DATA_DIR) + "/brushes"); } void KisBrushOpTest::testRotationMirroring() { TestBrushOp t("LR_simple.kpp"); t.test(); } void KisBrushOpTest::testRotationMirroringDrawingAngle() { TestBrushOpLines t("LR_drawing_angle.kpp"); t.test(); } void KisBrushOpTest::testMagicSeven() { /** * A special preset that forces Qt to bug: * mask size: 56 * brush size: 7 * therefore scale is: 0.125 * which causes QTransform work as a pure Translate in the mipmap */ TestBrushOpPressureLines t("magic_seven.kpp", "magicseven"); t.test(); } QTEST_MAIN(KisBrushOpTest) diff --git a/plugins/paintops/hairy/kis_hairy_paintop.cpp b/plugins/paintops/hairy/kis_hairy_paintop.cpp index 2927af5fae..bd53194983 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop.cpp +++ b/plugins/paintops/hairy/kis_hairy_paintop.cpp @@ -1,172 +1,170 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_hairy_paintop.h" #include "kis_hairy_paintop_settings.h" #include #include #include #include #include "kis_paint_device.h" #include "kis_painter.h" #include #include #include #include #include #include #include #include #include "kis_brush.h" KisHairyPaintOp::KisHairyPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image) Q_ASSERT(settings); m_dev = node ? node->paintDevice() : 0; KisBrushOptionProperties brushOption; brushOption.readOptionSetting(settings); KisBrushSP brush = brushOption.brush(); KisFixedPaintDeviceSP dab = cachedDab(painter->device()->compositionSourceColorSpace()); // properly initialize fake paint information to avoid warnings KisPaintInformation fakePaintInformation; fakePaintInformation.setRandomSource(new KisRandomSource()); fakePaintInformation.setPerStrokeRandomSource(new KisPerStrokeRandomSource()); if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) { dab = brush->paintDevice(source()->colorSpace(), KisDabShape(), fakePaintInformation); } else { brush->mask(dab, painter->paintColor(), KisDabShape(), fakePaintInformation); } m_brush.fromDabWithDensity(dab, settings->getDouble(HAIRY_BRISTLE_DENSITY) * 0.01); m_brush.setInkColor(painter->paintColor()); loadSettings(static_cast(settings.data())); m_brush.setProperties(&m_properties); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); - - bool mirrorXAxis = settings->getBool("runtimeCanvasMirroredX", false); - bool mirrorYAxis = settings->getBool("runtimeCanvasMirroredY", false); - m_mirrorFlip = mirrorXAxis != mirrorYAxis; } void KisHairyPaintOp::loadSettings(const KisBrushBasedPaintOpSettings *settings) { m_properties.inkAmount = settings->getInt(HAIRY_INK_AMOUNT); //TODO: wait for the transfer function with variable size m_properties.inkDepletionCurve = settings->getCubicCurve(HAIRY_INK_DEPLETION_CURVE).floatTransfer(m_properties.inkAmount); m_properties.inkDepletionEnabled = settings->getBool(HAIRY_INK_DEPLETION_ENABLED); m_properties.useSaturation = settings->getBool(HAIRY_INK_USE_SATURATION); m_properties.useOpacity = settings->getBool(HAIRY_INK_USE_OPACITY); m_properties.useWeights = settings->getBool(HAIRY_INK_USE_WEIGHTS); m_properties.pressureWeight = settings->getDouble(HAIRY_INK_PRESSURE_WEIGHT) / 100.0; m_properties.bristleLengthWeight = settings->getDouble(HAIRY_INK_BRISTLE_LENGTH_WEIGHT) / 100.0; m_properties.bristleInkAmountWeight = settings->getDouble(HAIRY_INK_BRISTLE_INK_AMOUNT_WEIGHT) / 100.0; m_properties.inkDepletionWeight = settings->getDouble(HAIRY_INK_DEPLETION_WEIGHT); m_properties.useSoakInk = settings->getBool(HAIRY_INK_SOAK); m_properties.useMousePressure = settings->getBool(HAIRY_BRISTLE_USE_MOUSEPRESSURE); m_properties.shearFactor = settings->getDouble(HAIRY_BRISTLE_SHEAR); m_properties.randomFactor = settings->getDouble(HAIRY_BRISTLE_RANDOM); m_properties.scaleFactor = settings->getDouble(HAIRY_BRISTLE_SCALE); m_properties.threshold = settings->getBool(HAIRY_BRISTLE_THRESHOLD); m_properties.antialias = settings->getBool(HAIRY_BRISTLE_ANTI_ALIASING); m_properties.useCompositing = settings->getBool(HAIRY_BRISTLE_USE_COMPOSITING); m_properties.connectedPath = settings->getBool(HAIRY_BRISTLE_CONNECTED); } KisSpacingInformation KisHairyPaintOp::paintAt(const KisPaintInformation& info) { return updateSpacingImpl(info); } KisSpacingInformation KisHairyPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { Q_UNUSED(info); return KisSpacingInformation(0.5); } void KisHairyPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { Q_UNUSED(currentDistance); if (!painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } /** * Even though we don't use spacing in hairy brush, we should still * initialize its distance information to ensure drawing angle and * other history-based sensors work fine. */ KisPaintInformation pi(pi2); KisPaintInformation::DistanceInformationRegistrar r = pi.registerDistanceInformation(currentDistance); // Hairy Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(pi); scale *= KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(pi); quint8 origOpacity = m_opacityOption.apply(painter(), pi); + const bool mirrorFlip = pi1.canvasMirroredH() != pi1.canvasMirroredV(); + // we don't use spacing here (the brush itself is used only once // during initialization), so we should just skip the distance info // update - m_brush.paintLine(m_dab, m_dev, pi1, pi, scale * m_properties.scaleFactor, m_mirrorFlip? -rotation : rotation); + m_brush.paintLine(m_dab, m_dev, pi1, pi, scale * m_properties.scaleFactor, mirrorFlip ? -rotation : rotation); //QRect rc = m_dab->exactBounds(); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); // we don't use spacing in hairy brush, but history is // still important for us currentDistance->registerPaintedDab(pi, KisSpacingInformation(), KisTimingInformation()); } diff --git a/plugins/paintops/hairy/kis_hairy_paintop.h b/plugins/paintops/hairy/kis_hairy_paintop.h index 9bda32cbe2..e9de44d51d 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop.h +++ b/plugins/paintops/hairy/kis_hairy_paintop.h @@ -1,65 +1,63 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_HAIRYPAINTOP_H_ #define KIS_HAIRYPAINTOP_H_ #include #include #include #include #include "hairy_brush.h" #include #include #include class KisPainter; class KisBrushBasedPaintOpSettings; class KisHairyPaintOp : public KisPaintOp { public: KisHairyPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image); void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override; protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; private: KisHairyProperties m_properties; KisPaintDeviceSP m_dab; KisPaintDeviceSP m_dev; HairyBrush m_brush; KisPressureRotationOption m_rotationOption; KisPressureSizeOption m_sizeOption; KisPressureOpacityOption m_opacityOption; - bool m_mirrorFlip; - void loadSettings(const KisBrushBasedPaintOpSettings* settings); }; #endif // KIS_HAIRYPAINTOP_H_ diff --git a/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp b/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp index 599a46d649..fee3928f66 100644 --- a/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp +++ b/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp @@ -1,162 +1,162 @@ /* * Copyright (c) 2013 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_current_outline_fetcher.h" #include "kis_pressure_size_option.h" #include "kis_pressure_rotation_option.h" #include "kis_pressure_mirror_option.h" #include #include #include "kis_paintop_settings.h" #include #define NOISY_UPDATE_SPEED 50 // Time in ms for outline updates to noisy brushes struct KisCurrentOutlineFetcher::Private { Private(Options optionsAvailable) : isDirty(true) { if (optionsAvailable & SIZE_OPTION) { sizeOption.reset(new KisPressureSizeOption()); } if (optionsAvailable & ROTATION_OPTION) { rotationOption.reset(new KisPressureRotationOption()); } if (optionsAvailable & MIRROR_OPTION) { mirrorOption.reset(new KisPressureMirrorOption()); } } QScopedPointer sizeOption; QScopedPointer rotationOption; QScopedPointer mirrorOption; bool isDirty; QElapsedTimer lastUpdateTime; qreal lastRotationApplied; qreal lastSizeApplied; MirrorProperties lastMirrorApplied; }; KisCurrentOutlineFetcher::KisCurrentOutlineFetcher(Options optionsAvailable) : d(new Private(optionsAvailable)) { d->lastUpdateTime.start(); } KisCurrentOutlineFetcher::~KisCurrentOutlineFetcher() { } void KisCurrentOutlineFetcher::setDirty() { d->isDirty = true; } QPainterPath KisCurrentOutlineFetcher::fetchOutline(const KisPaintInformation &info, const KisPaintOpSettingsSP settings, const QPainterPath &originalOutline, const KisPaintOpSettings::OutlineMode &mode, qreal additionalScale, qreal additionalRotation, bool tilt, qreal tiltcenterx, qreal tiltcentery) const { if (d->isDirty) { if (d->sizeOption) { d->sizeOption->readOptionSetting(settings); d->sizeOption->resetAllSensors(); } if (d->rotationOption) { d->rotationOption->readOptionSetting(settings); d->rotationOption->resetAllSensors(); } if (d->mirrorOption) { d->mirrorOption->readOptionSetting(settings); d->mirrorOption->resetAllSensors(); } d->isDirty = false; } qreal scale = additionalScale; qreal rotation = additionalRotation; bool needsUpdate = false; // Randomized rotation at full speed looks noisy, so slow it down if (d->lastUpdateTime.elapsed() > NOISY_UPDATE_SPEED) { needsUpdate = true; d->lastUpdateTime.restart(); } if (d->sizeOption && !tilt && !mode.forceFullSize) { if (!d->sizeOption->isRandom() || needsUpdate) { d->lastSizeApplied = d->sizeOption->apply(info); } scale *= d->lastSizeApplied; } if (d->rotationOption && !tilt) { if (!d->rotationOption->isRandom() || needsUpdate) { d->lastRotationApplied = d->rotationOption->apply(info); } rotation += d->lastRotationApplied; } else if (d->rotationOption && tilt) { - rotation += settings->getDouble("runtimeCanvasRotation", 0.0) * M_PI / 180.0; + rotation += info.canvasRotation() * M_PI / 180.0; } qreal xFlip = 1.0; qreal yFlip = 1.0; if (d->mirrorOption) { if (!d->mirrorOption->isRandom() || needsUpdate) { d->lastMirrorApplied = d->mirrorOption->apply(info); } if (d->lastMirrorApplied.coordinateSystemFlipped) { rotation = 2 * M_PI - rotation; } if (d->lastMirrorApplied.horizontalMirror) { xFlip = -1.0; } if (d->lastMirrorApplied.verticalMirror) { yFlip = -1.0; } } QTransform rot; rot.rotateRadians(-rotation); QPointF hotSpot = originalOutline.boundingRect().center(); if (tilt) { hotSpot.setX(tiltcenterx); hotSpot.setY(tiltcentery); } QTransform T1 = QTransform::fromTranslate(-hotSpot.x(), -hotSpot.y()); QTransform T2 = QTransform::fromTranslate(info.pos().x(), info.pos().y()); QTransform S = QTransform::fromScale(xFlip * scale, yFlip * scale); return (T1 * rot * S * T2).map(originalOutline); } diff --git a/plugins/paintops/libpaintop/kis_pressure_mirror_option.cpp b/plugins/paintops/libpaintop/kis_pressure_mirror_option.cpp index f32d6158b4..c026ba12bf 100644 --- a/plugins/paintops/libpaintop/kis_pressure_mirror_option.cpp +++ b/plugins/paintops/libpaintop/kis_pressure_mirror_option.cpp @@ -1,101 +1,98 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_pressure_mirror_option.h" #include #include #include #include KisPressureMirrorOption::KisPressureMirrorOption() : KisCurveOption("Mirror", KisPaintOpOption::GENERAL, false) { m_enableHorizontalMirror = false; m_enableVerticalMirror = false; } void KisPressureMirrorOption::enableHorizontalMirror(bool mirror) { m_enableHorizontalMirror = mirror; } void KisPressureMirrorOption::enableVerticalMirror(bool mirror) { m_enableVerticalMirror = mirror; } bool KisPressureMirrorOption::isHorizontalMirrorEnabled() { return m_enableHorizontalMirror; } bool KisPressureMirrorOption::isVerticalMirrorEnabled() { return m_enableVerticalMirror; } void KisPressureMirrorOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { KisCurveOption::writeOptionSetting(setting); setting->setProperty(MIRROR_HORIZONTAL_ENABLED, m_enableHorizontalMirror); setting->setProperty(MIRROR_VERTICAL_ENABLED, m_enableVerticalMirror); } void KisPressureMirrorOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { KisCurveOption::readOptionSetting(setting); m_enableHorizontalMirror = setting->getBool(MIRROR_HORIZONTAL_ENABLED, false); m_enableVerticalMirror = setting->getBool(MIRROR_VERTICAL_ENABLED, false); - - m_canvasAxisXMirrored = setting->getBool("runtimeCanvasMirroredX", false); - m_canvasAxisYMirrored = setting->getBool("runtimeCanvasMirroredY", false); } MirrorProperties KisPressureMirrorOption::apply(const KisPaintInformation& info) const { - int mirrorXIncrement = m_canvasAxisXMirrored; - int mirrorYIncrement = m_canvasAxisYMirrored; + int mirrorXIncrement = info.canvasMirroredH(); + int mirrorYIncrement = info.canvasMirroredV(); bool coordinateSystemFlipped = false; if (isChecked() && (m_enableHorizontalMirror || m_enableVerticalMirror)) { qreal sensorResult = computeSizeLikeValue(info); bool result = (sensorResult >= 0.5); mirrorXIncrement += result && m_enableHorizontalMirror; mirrorYIncrement += result && m_enableVerticalMirror; coordinateSystemFlipped = result && (m_enableHorizontalMirror != m_enableVerticalMirror); } MirrorProperties mirrors; mirrors.verticalMirror = mirrorYIncrement % 2; mirrors.horizontalMirror = mirrorXIncrement % 2; mirrors.coordinateSystemFlipped = coordinateSystemFlipped; return mirrors; } diff --git a/plugins/paintops/libpaintop/kis_pressure_mirror_option.h b/plugins/paintops/libpaintop/kis_pressure_mirror_option.h index 5bcf75e837..14edf66872 100644 --- a/plugins/paintops/libpaintop/kis_pressure_mirror_option.h +++ b/plugins/paintops/libpaintop/kis_pressure_mirror_option.h @@ -1,76 +1,74 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_PRESSURE_MIRROR_OPTION_H #define KIS_PRESSURE_MIRROR_OPTION_H #include "kis_curve_option.h" #include #include #include struct MirrorProperties { MirrorProperties() : horizontalMirror(false), verticalMirror(false), coordinateSystemFlipped(false) {} bool horizontalMirror; bool verticalMirror; bool coordinateSystemFlipped; bool isEmpty() const { return !horizontalMirror && !verticalMirror; } }; const QString MIRROR_HORIZONTAL_ENABLED = "HorizontalMirrorEnabled"; const QString MIRROR_VERTICAL_ENABLED = "VerticalMirrorEnabled"; /** * If the sensor value is higher then 0.5, then the related mirror option is true, false otherwise */ class PAINTOP_EXPORT KisPressureMirrorOption : public KisCurveOption { public: KisPressureMirrorOption(); /** * Set the */ MirrorProperties apply(const KisPaintInformation& info) const; void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; void enableVerticalMirror(bool mirror); void enableHorizontalMirror(bool mirror); bool isVerticalMirrorEnabled(); bool isHorizontalMirrorEnabled(); private: bool m_enableVerticalMirror; bool m_enableHorizontalMirror; - bool m_canvasAxisXMirrored; - bool m_canvasAxisYMirrored; }; #endif diff --git a/plugins/paintops/libpaintop/kis_pressure_rotation_option.cpp b/plugins/paintops/libpaintop/kis_pressure_rotation_option.cpp index 9421a53d9d..ea6f60a72c 100644 --- a/plugins/paintops/libpaintop/kis_pressure_rotation_option.cpp +++ b/plugins/paintops/libpaintop/kis_pressure_rotation_option.cpp @@ -1,86 +1,80 @@ /* This file is part of the KDE project * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_pressure_rotation_option.h" #include "kis_pressure_opacity_option.h" #include "sensors/kis_dynamic_sensor_drawing_angle.h" #include "sensors/kis_dynamic_sensor_fuzzy.h" #include #include #include #include KisPressureRotationOption::KisPressureRotationOption() - : KisCurveOption("Rotation", KisPaintOpOption::GENERAL, false), - m_defaultAngle(0.0), - m_canvasAxisXMirrored(false), - m_canvasAxisYMirrored(false) + : KisCurveOption("Rotation", KisPaintOpOption::GENERAL, false) { } double KisPressureRotationOption::apply(const KisPaintInformation & info) const { - if (!isChecked()) return m_defaultAngle; + if (!isChecked()) return kisDegreesToRadians(info.canvasRotation()); - const bool absoluteAxesFlipped = m_canvasAxisXMirrored != m_canvasAxisYMirrored; - const qreal normalizedBaseAngle = -m_defaultAngle / (2 * M_PI); + const bool absoluteAxesFlipped = info.canvasMirroredH() != info.canvasMirroredV(); + + const qreal normalizedBaseAngle = info.canvasRotation() / 360.0; qreal value = computeRotationLikeValue(info, normalizedBaseAngle, absoluteAxesFlipped); // flip to conform global legacy code value = 1.0 - value; return normalizeAngle(value * M_PI); } void KisPressureRotationOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { - m_defaultAngle = setting->getDouble("runtimeCanvasRotation", 0.0) * M_PI / 180.0; KisCurveOption::readOptionSetting(setting); - - m_canvasAxisXMirrored = setting->getBool("runtimeCanvasMirroredX", false); - m_canvasAxisYMirrored = setting->getBool("runtimeCanvasMirroredY", false); } void KisPressureRotationOption::applyFanCornersInfo(KisPaintOp *op) { KisDynamicSensorDrawingAngle *sensor = dynamic_cast(this->sensor(ANGLE, true).data()); /** * A special case for the Drawing Angle sensor, because it * changes the behavior of KisPaintOp::paintLine() */ if (sensor) { op->setFanCornersInfo(sensor->fanCornersEnabled(), qreal(sensor->fanCornersStep()) * M_PI / 180.0); } } int KisPressureRotationOption::intMinValue() const { return -180; } int KisPressureRotationOption::intMaxValue() const { return 180; } QString KisPressureRotationOption::valueSuffix() const { return i18n("°"); } diff --git a/plugins/paintops/libpaintop/kis_pressure_rotation_option.h b/plugins/paintops/libpaintop/kis_pressure_rotation_option.h index f52e6e8c0a..52396d501c 100644 --- a/plugins/paintops/libpaintop/kis_pressure_rotation_option.h +++ b/plugins/paintops/libpaintop/kis_pressure_rotation_option.h @@ -1,56 +1,49 @@ /* This file is part of the KDE project * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_PRESSURE_ROTATION_OPTION_H #define KIS_PRESSURE_ROTATION_OPTION_H #include "kis_curve_option.h" #include #include /** * The pressure opacity option defines a curve that is used to * calculate the effect of pressure on the size of the dab */ class PAINTOP_EXPORT KisPressureRotationOption : public KisCurveOption { public: KisPressureRotationOption(); double apply(const KisPaintInformation & info) const; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; void applyFanCornersInfo(KisPaintOp *op); // KisCurveOption interface public: int intMinValue() const override; int intMaxValue() const override; QString valueSuffix() const override; private: - qreal m_defaultAngle; - bool m_canvasAxisXMirrored; - bool m_canvasAxisYMirrored; - - - - }; #endif diff --git a/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp b/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp index b6edb053b0..b0888bf6bf 100644 --- a/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp +++ b/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp @@ -1,269 +1,260 @@ /* This file is part of the KDE project * * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_tangent_tilt_option.h" #include #include #include #include "ui_wdgtangenttiltoption.h" #include "kis_global.h" class KisTangentTiltOptionWidget: public QWidget, public Ui::WdgTangentTiltOptions { public: KisTangentTiltOptionWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); } }; KisTangentTiltOption::KisTangentTiltOption() -: KisPaintOpOption(KisPaintOpOption::GENERAL, false), - m_canvasAngle(0.0), - m_canvasAxisXMirrored(false), - m_canvasAxisYMirrored(false) +: KisPaintOpOption(KisPaintOpOption::GENERAL, false) { m_checkable = false; m_options = new KisTangentTiltOptionWidget(); //Setup tangent tilt. m_options->comboRed->setCurrentIndex(0); m_options->comboGreen->setCurrentIndex(2); m_options->comboBlue->setCurrentIndex(4); m_options->sliderElevationSensitivity->setRange(0, 100, 0); m_options->sliderElevationSensitivity->setValue(100); m_options->sliderElevationSensitivity->setSuffix("%"); m_options->sliderMixValue->setRange(0, 100, 0); m_options->sliderMixValue->setValue(50); m_options->sliderMixValue->setSuffix("%"); connect(m_options->comboRed, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_options->comboGreen, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_options->comboBlue, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_options->comboRed, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setRedChannel(int))); connect(m_options->comboGreen, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setGreenChannel(int))); connect(m_options->comboBlue, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setBlueChannel(int))); connect(m_options->optionTilt, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->optionDirection, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->optionRotation, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->optionMix, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->sliderElevationSensitivity, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->sliderMixValue, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); m_options->sliderMixValue->setVisible(false); setConfigurationPage(m_options); } KisTangentTiltOption::~KisTangentTiltOption() { delete m_options; } //options int KisTangentTiltOption::redChannel() const { return m_options->comboRed->currentIndex(); } int KisTangentTiltOption::greenChannel() const { return m_options->comboGreen->currentIndex(); } int KisTangentTiltOption::blueChannel() const { return m_options->comboBlue->currentIndex(); } int KisTangentTiltOption::directionType() const { int type=0; if (m_options->optionTilt->isChecked()==true) { type=0; } else if (m_options->optionDirection->isChecked()==true) { type=1; } else if (m_options->optionRotation->isChecked()==true) { type=2; } else if (m_options->optionMix->isChecked()==true) { type=3; } else { warnKrita<<"There's something odd with the radio buttons. We'll use Tilt"; } return type; } double KisTangentTiltOption::elevationSensitivity() const { return m_options->sliderElevationSensitivity->value(); } double KisTangentTiltOption::mixValue() const { return m_options->sliderMixValue->value(); } void KisTangentTiltOption::swizzleAssign(qreal const horizontal, qreal const vertical, qreal const depth, qreal *component, int index, qreal maxvalue) { switch(index) { case 0: *component = horizontal; break; case 1: *component = maxvalue-horizontal; break; case 2: *component = vertical; break; case 3: *component = maxvalue-vertical; break; case 4: *component = depth; break; case 5: *component = maxvalue-depth; break; } } void KisTangentTiltOption::apply(const KisPaintInformation& info,qreal *r,qreal *g,qreal *b) { //formula based on http://www.cerebralmeltdown.com/programming_projects/Altitude%20and%20Azimuth%20to%20Vector/index.html /* It doesn't make sense of have higher than 8bit color depth. * Instead we make sure in the paintAt function of kis_tangent_normal_paintop to pick an 8bit space of the current * color space if the space is an RGB space. If not, it'll pick sRGB 8bit. */ qreal halfvalue = 0.5; qreal maxvalue = 1.0; //have the azimuth and altitude in degrees. qreal direction = KisPaintInformation::tiltDirection(info, true)*360.0; qreal elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0); - if (directionType()==0) { + + if (directionType() == 0) { direction = KisPaintInformation::tiltDirection(info, true)*360.0; - elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0); - } else if (directionType()==1) { + elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0); + } else if (directionType() == 1) { direction = (0.75 + info.drawingAngle() / (2.0 * M_PI))*360.0; - elevation= 0;//turns out that tablets that don't support tilt just return 90 degrees for elevation. - } else if (directionType()==2) { + elevation= 0;//turns out that tablets that don't support tilt just return 90 degrees for elevation. + } else if (directionType() == 2) { direction = info.rotation(); - elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0);//artpens have tilt-recognition, so this should work. - } else if (directionType()==3) {//mix of tilt+direction - qreal mixamount = mixValue()/100.0; + elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0);//artpens have tilt-recognition, so this should work. + } else if (directionType() == 3) {//mix of tilt+direction + qreal mixamount = mixValue()/100.0; direction = (KisPaintInformation::tiltDirection(info, true)*360.0*(1.0-mixamount))+((0.75 + info.drawingAngle() / (2.0 * M_PI))*360.0*(mixamount)); - elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0); + elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0); } - //subtract/add the rotation of the canvas. + const qreal oldDirection = direction; - if (info.canvasRotation()!=m_canvasAngle && info.canvasMirroredH()==m_canvasAxisXMirrored) { - m_canvasAngle=info.canvasRotation(); - } - if (directionType()!=1) { - direction = direction-m_canvasAngle; + //subtract/add the rotation of the canvas. + if (directionType() != 1) { + direction = normalizeAngleDegrees(direction - info.canvasRotation()); } //limit the direction/elevation //qreal elevationMax = (elevationSensitivity()*90.0)/100.0; qreal elevationT = elevation*(elevationSensitivity()/100.0)+(90-(elevationSensitivity()*90.0)/100.0); elevation = static_cast(elevationT); //convert to radians. // Convert this to kis_global's radian function. direction = kisDegreesToRadians(direction); elevation = kisDegreesToRadians(elevation); //make variables for axes for easy switching later on. qreal horizontal, vertical, depth; //spherical coordinates always center themselves around the origin, leading to values. We need to work around those... horizontal = cos(elevation)*sin(direction); if (horizontal>0.0) { horizontal= halfvalue+(fabs(horizontal)*halfvalue); } else { horizontal= halfvalue-(fabs(horizontal)*halfvalue); } vertical = cos(elevation)*cos(direction); if (vertical>0.0) { vertical = halfvalue+(fabs(vertical)*halfvalue); } else { vertical = halfvalue-(fabs(vertical)*halfvalue); } - if (m_canvasAxisXMirrored && info.canvasMirroredH()) {horizontal = maxvalue-horizontal;} - if (m_canvasAxisYMirrored && info.canvasMirroredH()) {vertical = maxvalue-vertical;} + if (info.canvasMirroredH()) {horizontal = maxvalue - horizontal;} + if (info.canvasMirroredV()) {vertical = maxvalue - vertical;} depth = sin(elevation)*maxvalue; //assign right components to correct axes. swizzleAssign(horizontal, vertical, depth, r, redChannel(), maxvalue); swizzleAssign(horizontal, vertical, depth, g, greenChannel(), maxvalue); swizzleAssign(horizontal, vertical, depth, b, blueChannel(), maxvalue); } /*settings*/ void KisTangentTiltOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { setting->setProperty(TANGENT_RED, redChannel()); setting->setProperty(TANGENT_GREEN, greenChannel()); setting->setProperty(TANGENT_BLUE, blueChannel()); setting->setProperty(TANGENT_TYPE, directionType()); setting->setProperty(TANGENT_EV_SEN, elevationSensitivity()); setting->setProperty(TANGENT_MIX_VAL, mixValue()); } void KisTangentTiltOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { m_options->comboRed->setCurrentIndex(setting->getInt(TANGENT_RED, 0)); m_options->comboGreen->setCurrentIndex(setting->getInt(TANGENT_GREEN, 2)); m_options->comboBlue->setCurrentIndex(setting->getInt(TANGENT_BLUE, 4)); //The comboboxes are connected to the TangentTiltPreview, so that gets automatically updated by them. if (setting->getInt(TANGENT_TYPE)== 0){ m_options->optionTilt->setChecked(true); - m_options->sliderMixValue->setVisible(false); + m_options->sliderMixValue->setVisible(false); } else if (setting->getInt(TANGENT_TYPE)== 1) { m_options->optionDirection->setChecked(true); - m_options->sliderMixValue->setVisible(false); + m_options->sliderMixValue->setVisible(false); } else if (setting->getInt(TANGENT_TYPE)== 2) { m_options->optionRotation->setChecked(true); - m_options->sliderMixValue->setVisible(false); + m_options->sliderMixValue->setVisible(false); } else if (setting->getInt(TANGENT_TYPE)== 3) { m_options->optionMix->setChecked(true); - m_options->sliderMixValue->setVisible(true); + m_options->sliderMixValue->setVisible(true); } - m_canvasAngle = setting->getDouble("runtimeCanvasRotation", 0.0);//in degrees please. - m_canvasAxisXMirrored = setting->getBool("runtimeCanvasMirroredX", false); - m_canvasAxisYMirrored = setting->getBool("runtimeCanvasMirroredY", false); - m_options->sliderElevationSensitivity->setValue(setting->getDouble(TANGENT_EV_SEN, 100)); m_options->sliderMixValue->setValue(setting->getDouble(TANGENT_MIX_VAL, 50)); - } diff --git a/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h b/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h index 53f84cb31b..0a7fbad56e 100644 --- a/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h +++ b/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h @@ -1,68 +1,65 @@ /* This file is part of the KDE project * * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_TANGENT_TILT_OPTION_H #define KIS_TANGENT_TILT_OPTION_H #include #include #include const QString TANGENT_RED = "Tangent/swizzleRed"; const QString TANGENT_GREEN = "Tangent/swizzleGreen"; const QString TANGENT_BLUE = "Tangent/swizzleBlue"; const QString TANGENT_TYPE = "Tangent/directionType"; const QString TANGENT_EV_SEN = "Tangent/elevationSensitivity"; const QString TANGENT_MIX_VAL = "Tangent/mixValue"; //const QString TANGENT_DIR_MIN = "Tangent/directionMinimum"; //const QString TANGENT_DIR_MAX = "Tangent/directionMaximum"; class KisPropertiesConfiguration; class KisTangentTiltOptionWidget; class KisTangentTiltOption: public KisPaintOpOption //not really// { public: KisTangentTiltOption(); ~KisTangentTiltOption() override; /*These three give away which the index of the combobox for a given channel*/ int redChannel() const; int greenChannel() const; int blueChannel() const; int directionType() const; double elevationSensitivity() const; double mixValue() const; - qreal m_canvasAngle; - bool m_canvasAxisXMirrored; - bool m_canvasAxisYMirrored; /*This assigns the right axis to the component, based on index and maximum value*/ void swizzleAssign(qreal const horizontal, qreal const vertical, qreal const depth, qreal *component, int index, qreal maxvalue); //takes the RGB values and will deform them depending on tilt. void apply(const KisPaintInformation& info, qreal *r, qreal *g, qreal *b); void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; private: KisTangentTiltOptionWidget * m_options; }; #endif // KIS_TANGENT_TILT_OPTION_H diff --git a/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop b/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop index 835e341b20..544eda162d 100644 --- a/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop +++ b/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop @@ -1,57 +1,57 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=assignprofiledialog X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Assign Profile to Image Name[ar]=إسناد اللاحات إلى الصور Name[ca]=Assigna un perfil a una imatge Name[ca@valencia]=Assigna un perfil a una imatge Name[cs]=Přiřadit obrázku profil Name[el]=Αντιστοίχιση προφίλ σε εικόνα Name[en_GB]=Assign Profile to Image Name[es]=Asignar perfil a imagen Name[eu]=Esleitu profila irudiari Name[fi]=Liitä kuvaan profiili Name[fr]=Attribuer un profil à l'image Name[gl]=Asignar un perfil á imaxe Name[is]=Úthluta litasniði á myndina Name[it]=Assegna profilo a immagine Name[ko]=이미지에 프로필 할당 Name[nl]=Profiel aan afbeelding toewijzen Name[nn]=Tildel profil til bilete Name[pl]=Przypisz profil do obrazu Name[pt]=Atribuir um Perfil à Imagem Name[pt_BR]=Atribuir perfil a imagem Name[sv]=Tilldela profil till bild Name[tr]=Görüntüye Profil Ata Name[uk]=Призначити профіль до зображення Name[x-test]=xxAssign Profile to Imagexx Name[zh_CN]=为图像指定特性文件 Name[zh_TW]=指定設定檔到圖像 Comment=Assign a profile to an image without converting it. Comment[ar]=أسنِد لاحة إلى صورة دون تحويلها. Comment[ca]=Assigna un perfil a una imatge sense convertir-la. Comment[ca@valencia]=Assigna un perfil a una imatge sense convertir-la. Comment[el]=Αντιστοιχίζει ένα προφίλ σε μια εικόνα χωρίς μετατροπή. Comment[en_GB]=Assign a profile to an image without converting it. Comment[es]=Asignar un perfil a una imagen sin convertirla. Comment[eu]=Esleitu profil bat irudi bati hura bihurtu gabe. Comment[fi]=Liitä kuvaan profiili muuntamatta kuvaa Comment[fr]=Attribuer un profil à une image sans la convertir. Comment[gl]=Asignar un perfil a unha imaxe sen convertela. Comment[is]=Úthluta litasniði á myndina án þess að umbreyta henni. Comment[it]=Assegna un profilo a un'immagine senza convertirla. Comment[ko]=프로필을 변환하지 않고 이미지에 할당합니다. Comment[nl]=Een profiel aan een afbeelding toewijzen zonder het te converteren. -Comment[nn]=Tildel profil til eit bilete utan å konvertera det +Comment[nn]=Tildel fargeprofil til eit bilete utan å konvertera det til profilen Comment[pl]=Przypisz profil do obrazu bez jego przekształcania. Comment[pt]=Atribui um perfil à imagem sem a converter. Comment[pt_BR]=Atribui um perfil para uma imagem sem convertê-la. Comment[sv]=Tilldela en profil till en bild utan att konvertera den. Comment[tr]=Bir görüntüye, görüntüyü değiştirmeden bir profil ata. Comment[uk]=Призначити профіль до зображення без його перетворення. Comment[x-test]=xxAssign a profile to an image without converting it.xx Comment[zh_CN]=仅为图像指定特性文件,不转换其色彩空间 Comment[zh_TW]=將設定檔指定給圖像,而不進行轉換。 diff --git a/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop b/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop index df0266eec2..f72ea33d5a 100644 --- a/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop +++ b/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop @@ -1,58 +1,58 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=comics_project_management_tools X-Krita-Manual=README.html X-Python-2-Compatible=false Name=Comics Project Management Tools Name[ar]=أدوات إدارة المشاريع الهزليّة Name[ca]=Eines per a la gestió dels projectes de còmics Name[ca@valencia]=Eines per a la gestió dels projectes de còmics Name[cs]=Nástroje pro správu projektů komixů Name[el]=Εργαλεία διαχείρισης έργων ιστοριών σε εικόνες Name[en_GB]=Comics Project Management Tools Name[es]=Herramientas de gestión de proyectos de cómics Name[eu]=Komikien proiektuak kudeatzeko tresnak Name[fi]=Sarjakuvaprojektien hallintatyökalut Name[fr]=Outils de gestion d'un projet de bande dessinée Name[gl]=Ferramentas de xestión de proxectos de cómics Name[is]=Verkefnisstjórn teiknimyndasögu Name[it]=Strumenti per la gestione dei progetti di fumetti Name[ko]=만화 프로젝트 관리 도구 Name[nl]=Hulpmiddelen voor projectbeheer van strips -Name[nn]=Prosjekthandsamingsverktøy for teikneseriar +Name[nn]=Prosjekthandsaming for teikneseriar Name[pl]=Narzędzia do zarządzania projektami komiksów Name[pt]=Ferramentas de Gestão de Projectos de Banda Desenhada Name[pt_BR]=Ferramentas de gerenciamento de projeto de quadrinhos Name[sv]=Projekthanteringsverktyg för tecknade serier Name[tr]=Çizgi Roman Projesi Yönetimi Araçları Name[uk]=Інструменти для керування проєктами коміксів Name[x-test]=xxComics Project Management Toolsxx Name[zh_CN]=漫画项目管理工具 Name[zh_TW]=漫畫專案管理工具 Comment=Tools for managing comics. Comment[ar]=أدوات لإدارة الهزليّات. Comment[ca]=Eines per a gestionar els còmics. Comment[ca@valencia]=Eines per a gestionar els còmics. Comment[cs]=Nástroje pro správu komixů. Comment[el]=Εργαλεία για τη διαχείριση ιστοριών σε εικόνες. Comment[en_GB]=Tools for managing comics. Comment[es]=Herramientas para gestionar cómics. Comment[eu]=Komikiak kudeatzeko tresnak. Comment[fi]=Sarjakuvien hallintatyökalut. Comment[fr]=Outils pour gérer les bandes dessinées. Comment[gl]=Ferramentas para xestionar cómics. Comment[is]=Verkfæri til að stýra gerð teiknimyndasögu. Comment[it]=Strumenti per la gestione dei fumetti. Comment[ko]=만화 관리 도구입니다. Comment[nl]=Hulpmiddelen voor beheer van strips. Comment[nn]=Verktøy for handsaming av teikneseriar Comment[pl]=Narzędzie do zarządzania komiksami. Comment[pt]=Ferramentas para gerir bandas desenhadas. Comment[pt_BR]=Ferramentas para gerenciar quadrinhos. Comment[sv]=Verktyg för att hantera tecknade serier. Comment[tr]=Çizgi romanları yönetmek için araçlar. Comment[uk]=Інструменти для керування коміксами Comment[x-test]=xxTools for managing comics.xx Comment[zh_CN]=用于管理漫画的工具。 Comment[zh_TW]=管理漫畫的工具。 diff --git a/plugins/python/documenttools/kritapykrita_documenttools.desktop b/plugins/python/documenttools/kritapykrita_documenttools.desktop index 260b23215b..c34c388ee2 100644 --- a/plugins/python/documenttools/kritapykrita_documenttools.desktop +++ b/plugins/python/documenttools/kritapykrita_documenttools.desktop @@ -1,56 +1,56 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=documenttools X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Document Tools Name[ar]=أدوات المستندات Name[ca]=Eines de document Name[ca@valencia]=Eines de document Name[cs]=Dokumentové nástroje Name[el]=Εργαλεία για έγγραφα Name[en_GB]=Document Tools Name[es]=Herramientas de documentos Name[eu]=Dokumentuen tresnak Name[fi]=Tiedostotyökalut Name[fr]=Outil Document Name[gl]=Ferramentas de documentos Name[it]=Strumenti per i documenti Name[ko]=문서 도구 Name[nl]=Documenthulpmiddelen Name[nn]=Dokumentverktøy Name[pl]=Narzędzia dokumentu Name[pt]=Ferramentas de Documentos Name[pt_BR]=Ferramentas de documento Name[sv]=Dokumentverktyg Name[tr]=Belge Araçları Name[uk]=Засоби документа Name[x-test]=xxDocument Toolsxx Name[zh_CN]=文档工具 Name[zh_TW]=檔案工具 Comment=Plugin to manipulate properties of selected documents Comment[ar]=ملحقة لتعديل خصائص المستندات المحددة Comment[ca]=Un connector per a manipular les propietats dels documents seleccionats Comment[ca@valencia]=Un connector per a manipular les propietats dels documents seleccionats Comment[cs]=Modul pro správu vlastností vybraných dokumentů Comment[el]=Πρόσθετο χειρισμού ιδιοτήτων σε επιλεγμένα έγγραφα Comment[en_GB]=Plugin to manipulate properties of selected documents Comment[es]=Complemento para manipular las propiedades de los documentos seleccionados Comment[eu]=Hautatutako dokumentuen propietateak manipulatzeko plugina Comment[fi]=Liitännäinen valittujen tiedostojen ominaisuuksien käsittelemiseksi Comment[fr]=Module externe de gestion des propriétés des documents sélectionnés Comment[gl]=Complemento para manipular as propiedades dos documentos seleccionados. Comment[it]=Estensione per manipolare le proprietà dei documenti selezionati Comment[ko]=선택한 문서의 속성을 변경하는 플러그인 Comment[nl]=Plug-in om eigenschappen van geselecteerde documenten te manipuleren -Comment[nn]=Programtillegg for endra eigenskapar på utvalde dokument +Comment[nn]=Programtillegg for å endra eigenskapar på utvalde dokument Comment[pl]=Wtyczka do zmiany właściwości wybranych dokumentów Comment[pt]='Plugin' para manipular as propriedades dos documentos seleccionados Comment[pt_BR]=Plugin para manipular as propriedades de documentos selecionados Comment[sv]=Insticksprogram för att ändra egenskaper för valda dokument Comment[tr]=Seçili belgelerin özelliklerini değiştirmek için eklenti Comment[uk]=Додаток для керування властивостями позначених документів Comment[x-test]=xxPlugin to manipulate properties of selected documentsxx Comment[zh_CN]=用于编辑选定文档属性的插件 Comment[zh_TW]=用於修改所選檔案屬性的外掛程式 diff --git a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop index 3d70df7b0c..6a13606e33 100644 --- a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop +++ b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop @@ -1,45 +1,45 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=krita_script_starter X-Python-2-Compatible=false X-Krita-Manual=Manual.html Name=Krita Script Starter Name[ca]=Iniciador de scripts del Krita Name[ca@valencia]=Iniciador de scripts del Krita Name[cs]=Spouštěč skriptů Krita Name[en_GB]=Krita Script Starter Name[es]=Iniciador de guiones de Krita Name[eu]=Krita-ren script abiarazlea Name[gl]=Iniciador de scripts de Krita Name[it]=Iniziatore di script per Krita Name[ko]=Krita 스크립트 시작 도구 Name[nl]=Script-starter van Krita Name[nn]=Krita skriptbyggjar Name[pl]=Starter skryptów Krity Name[pt]=Inicialização do Programa do Krita Name[sv]=Krita skriptstart Name[tr]=Krita Betik Başlatıcı Name[uk]=Створення скрипту Krita Name[x-test]=xxKrita Script Starterxx Name[zh_CN]=Krita 空脚本生成器 Name[zh_TW]=Krita 指令啟動器 Comment=Create the metadata and file structure for a new Krita script Comment[ca]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita Comment[ca@valencia]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita Comment[en_GB]=Create the metadata and file structure for a new Krita script Comment[es]=Crear los metadatos y la estructura de archivos para un nuevo guion de Krita Comment[eu]=Sortu Krita-script berri baterako meta-datuak eta fitxategi egitura Comment[gl]=Crear os metadatos e a estrutura de ficheiros para un novo script de Krita. Comment[it]=Crea i metadati e la struttura dei file per un nuovo script di Krita Comment[ko]=새 Krita 스크립트에 대한 메타데이터 및 파일 구조 생성 Comment[nl]=Maak de metagegevens en bestandsstructuur voor een nieuw Krita-script -Comment[nn]=Generer metadata og filstruktur for nytt Krita-skript +Comment[nn]=Generer metadata og filstruktur for nye Krita-skript Comment[pl]=Utwórz metadane i strukturę plików dla nowego skryptu Krity Comment[pt]=Cria os meta-dados e a estrutura de ficheiros para um novo programa do Krita Comment[sv]=Skapa metadata och filstruktur för ett nytt Krita-skript Comment[tr]=Yeni Krita betiği için üstveri ve dosya yapısı oluştur Comment[uk]=Створення метаданих і структури файлів для нового скрипту Krita Comment[x-test]=xxCreate the metadata and file structure for a new Krita scriptxx Comment[zh_CN]=为新的 Krita 脚本创建元数据及文件结构 Comment[zh_TW]=為新的 Krita 指令稿建立中繼資料和檔案建構體 diff --git a/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop b/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop index 5b54ae897f..846e94d779 100644 --- a/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop +++ b/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop @@ -1,52 +1,52 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=lastdocumentsdocker X-Python-2-Compatible=false X-Krita-Manual=Manual.html Name=Last Documents Docker Name[ar]=رصيف بآخر المستندات Name[ca]=Acoblador Darrers documents Name[ca@valencia]=Acoblador Darrers documents Name[el]=Προσάρτηση τελευταίων εγγράφοων Name[en_GB]=Last Documents Docker Name[es]=Panel de últimos documentos Name[eu]=Azken dokumentuen panela Name[fi]=Viimeisimpien tiedostojen telakka Name[fr]=Récemment ouverts Name[gl]=Doca dos últimos documentos Name[it]=Area di aggancio Ultimi documenti Name[ko]=마지막 문서 도킹 패널 Name[nl]=Laatste documenten verankering -Name[nn]=Dokk for nyleg brukte dokument +Name[nn]=Dokk for nyleg opna dokument Name[pl]=Dok ostatnich dokumentów Name[pt]=Área dos Últimos Documentos Name[sv]=Dockningsfönster för senaste dokument Name[tr]=Son Belgeler Doku Name[uk]=Бічна панель останніх документів Name[x-test]=xxLast Documents Dockerxx Name[zh_CN]=最近文档工具面板 Name[zh_TW]=「最後檔案」面板 Comment=A Python-based docker for show thumbnails to last ten documents Comment[ar]=رصيف بِ‍«پيثون» لعرض مصغّرات آخر ١٠ مستندات مفتوحة Comment[ca]=Un acoblador basat en Python per a mostrar miniatures dels darrers deu documents Comment[ca@valencia]=Un acoblador basat en Python per a mostrar miniatures dels darrers deu documents Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την εμφάνιση επισκοπήσεων των δέκα τελευταίων εγγράφων Comment[en_GB]=A Python-based docker for show thumbnails to last ten documents Comment[es]=Un panel basado en Python para mostrar miniaturas de los últimos diez documentos Comment[eu]=Azken hamar dokumentuen koadro-txikiak erakusteko Python-oinarridun panel bat Comment[fi]=Python-pohjainen telakka viimeisimpien kymmenen tiedoston pienoiskuvien näyttämiseen Comment[fr]=Panneau en Python pour afficher les vignettes des dix derniers documents Comment[gl]=Unha doca baseada en Python para mostrar as miniaturas dos últimos dez documentos. Comment[it]=Un'area di aggancio basata su Python per mostrare miniature degli ultimi dieci documenti. Comment[ko]=10개의 문서를 표시할 수 있는 Python 기반 도킹 패널 Comment[nl]=Een op Python gebaseerde vastzetter om miniaturen te tonen naar de laatste tien documenten. Comment[nn]=Python-basert dokk for vising av miniatyrbilete av dei siste ti dokumenta Comment[pl]=Dok oparty na pythonie do wyświetlania miniatur ostatnich dziesięciu dokumentów Comment[pt]=Uma área acoplável, feita em Python, para mostrar as miniaturas dos últimos dez documentos Comment[sv]=Ett Python-baserat dockningsfönster för att visa miniatyrbilder för de tio senaste dokumenten Comment[tr]=Son on belgenin küçük resmini göstermek için Python-tabanlı bir dok Comment[uk]=Бічна панель на основі Python для показу мініатюр останніх десяти документів Comment[x-test]=xxA Python-based docker for show thumbnails to last ten documentsxx Comment[zh_CN]=基于 Python 的工具面板,显示最近十个文档的缩略图 Comment[zh_TW]=基於 Python 的面板,用於顯示最後 10 個檔案縮圖 diff --git a/plugins/python/scripter/kritapykrita_scripter.desktop b/plugins/python/scripter/kritapykrita_scripter.desktop index 7ad09b506f..e79dde80b6 100644 --- a/plugins/python/scripter/kritapykrita_scripter.desktop +++ b/plugins/python/scripter/kritapykrita_scripter.desktop @@ -1,49 +1,49 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=scripter X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Scripter Name[ca]=Scripter Name[ca@valencia]=Scripter Name[de]=Scripter Name[el]=Σενάρια Name[en_GB]=Scripter Name[es]=Guionador Name[eu]=Script egilea Name[fr]=Scripter Name[gl]=Executor de scripts Name[it]=Scripter Name[ko]=스크립트 도구 Name[nl]=Scriptmaker Name[nn]=Skriptkøyrer Name[pl]=Skrypter Name[pt]=Programador Name[sv]=Skriptgenerator Name[tr]=Betik yazarı Name[uk]=Скриптер Name[x-test]=xxScripterxx Name[zh_CN]=脚本工具 Name[zh_TW]=指令稿編寫者 Comment=Plugin to execute ad-hoc Python code Comment[ca]=Connector per executar codi «ad hoc» en Python Comment[ca@valencia]=Connector per executar codi «ad hoc» en Python Comment[el]=Πρόσθετο για την εκτέλεση συγκεκριμένου κώδικα Python Comment[en_GB]=Plugin to execute ad-hoc Python code Comment[es]=Complemento para ejecutar código Python a medida Comment[eu]=Berariaz egindako Python kodea exekutatzeko plugina Comment[fi]=Liitännäinen satunnaisen Python-koodin suorittamiseksi Comment[gl]=Complemento para executar código de Python escrito no momento. Comment[it]=Estensione per eseguire ad-hoc codice Python Comment[ko]=즉석에서 Python 코드를 실행하는 플러그인 Comment[nl]=Plug-in om ad-hoc Python code uit te voeren -Comment[nn]=Programtillegg for køyring av ad hoc-køyring av Python-kode +Comment[nn]=Programtillegg for køyring av ad hoc Python-kode Comment[pl]=Wtyczka do wykonywania kodu Pythona ad-hoc Comment[pt]='Plugin' para executar código em Python arbitrário Comment[sv]=Insticksprogram för att köra godtycklig Python-kod Comment[tr]=Geçici Python kodu çalıştırmak için eklenti Comment[uk]=Додаток для виконання апріорного коду Python Comment[x-test]=xxPlugin to execute ad-hoc Python codexx Comment[zh_CN]=用于执行当场编写的 Python 代码的插件 Comment[zh_TW]=外掛程式,用於執行特定 Python 程式碼 diff --git a/plugins/python/tenscripts/kritapykrita_tenscripts.desktop b/plugins/python/tenscripts/kritapykrita_tenscripts.desktop index 0550f859b4..abde5797ff 100644 --- a/plugins/python/tenscripts/kritapykrita_tenscripts.desktop +++ b/plugins/python/tenscripts/kritapykrita_tenscripts.desktop @@ -1,51 +1,51 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=tenscripts X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Ten Scripts Name[ar]=عشرُ سكربتات Name[ca]=Deu scripts Name[ca@valencia]=Deu scripts Name[en_GB]=Ten Scripts Name[es]=Diez guiones Name[eu]=Hamar script Name[fi]=Kymmenen skriptiä Name[fr]=Raccourcis des scripts Name[gl]=Dez scripts Name[is]=Tíu skriftur Name[it]=Dieci script Name[ko]=10개의 스크립트 Name[nl]=Tien scripts Name[nn]=Ti skript Name[pl]=Skrypty Ten Name[pt]=Dez Programas Name[sv]=Tio skript Name[tr]=On Betik Name[uk]=Десять скриптів Name[x-test]=xxTen Scriptsxx Name[zh_CN]=十大脚本 Name[zh_TW]=10 個指令稿 Comment=A Python-based plugin for creating ten actions and assign them to Python scripts Comment[ar]=ملحقة بِ‍«پيثون» لإنشاء ١٠ إجراءات وإسنادها إلى سكربتات «پيثون» Comment[ca]=Un connector basat en Python per a crear deu accions i assignar-les a scripts en Python Comment[ca@valencia]=Un connector basat en Python per a crear deu accions i assignar-les a scripts en Python Comment[en_GB]=A Python-based plugin for creating ten actions and assign them to Python scripts Comment[es]=Un complemento basado en Python para crear diez acciones y asignarlas a guiones de Python Comment[eu]=Hamar ekintza sortu eta haiek Python-scriptei esleitzeko Python-oinarridun plugin bat Comment[fi]=Python-pohjainen liitännäinen kymmenen toiminnon luomiseksi kytkemiseksi Python-skripteihin Comment[fr]=Module externe basé sur Python pour créer dix actions et les assigner à des scripts Python Comment[gl]=Un complemento escrito en Python para crear dez accións e asignalas a scripts escritos en Python. Comment[it]=Un'estensione basata su Python per creare dieci azioni e assegnarle a script Python Comment[ko]=10개의 작업을 생성하고 이를 Python 스크립트에 할당하는 Python 기반 플러그인 Comment[nl]=Een op Python gebaseerde plug-in voor aanmaken van tien acties en ze dan toewijzen aan Python-scripts -Comment[nn]=Python-basert tillegg for å leggja til ti handlingar og tildela dei til Python-skript +Comment[nn]=Python-basert tillegg som legg til ti handlingar du kan tildela til Python-skript Comment[pl]=Wtyczka oparta na Pythonie do tworzenia działań i przypisywanie ich skryptom Pythona Comment[pt]=Um 'plugin' feito em Python para criar dez acções e atribuí-las a programas em Python Comment[sv]=Ett Python-baserat insticksprogram för att skapa tio åtgärder och tilldela dem till Python-skript Comment[tr]=On eylem oluşturmak ve Python betiklerine atamak için Python tabanlı bir eklenti Comment[uk]=Скрипт на основі Python для створення десяти дій і прив'язування до них скриптів Python Comment[x-test]=xxA Python-based plugin for creating ten actions and assign them to Python scriptsxx Comment[zh_CN]=基于 Python 的用于创建十个操作并将它们指定到特定 Python 脚本的插件 Comment[zh_TW]=基於 Python 的外掛程式,用於建立 10 個動作並且將它們指定給 Python 指令稿 diff --git a/plugins/tools/basictools/kis_tool_move.cc b/plugins/tools/basictools/kis_tool_move.cc index 3abda4d635..88fd6cb471 100644 --- a/plugins/tools/basictools/kis_tool_move.cc +++ b/plugins/tools/basictools/kis_tool_move.cc @@ -1,699 +1,699 @@ /* * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 2002 Patrick Julien * 2004 Boudewijn Rempt * 2016 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_move.h" #include #include "kis_cursor.h" #include "kis_selection.h" #include "kis_canvas2.h" #include "kis_image.h" #include "kis_tool_utils.h" #include "kis_paint_layer.h" #include "strokes/move_stroke_strategy.h" #include "kis_tool_movetooloptionswidget.h" #include "strokes/move_selection_stroke_strategy.h" #include "kis_resources_snapshot.h" #include "kis_action_registry.h" #include "krita_utils.h" #include #include #include "kis_node_manager.h" #include "kis_selection_manager.h" #include "kis_signals_blocker.h" #include #include "KisMoveBoundsCalculationJob.h" struct KisToolMoveState : KisToolChangesTrackerData, boost::equality_comparable { KisToolMoveState(QPoint _accumulatedOffset) : accumulatedOffset(_accumulatedOffset) {} - KisToolChangesTrackerData* clone() const { return new KisToolMoveState(*this); } + KisToolChangesTrackerData* clone() const override { return new KisToolMoveState(*this); } bool operator ==(const KisToolMoveState &rhs) { return accumulatedOffset == rhs.accumulatedOffset; } QPoint accumulatedOffset; }; KisToolMove::KisToolMove(KoCanvasBase *canvas) : KisTool(canvas, KisCursor::moveCursor()) , m_updateCursorCompressor(100, KisSignalCompressor::FIRST_ACTIVE) { setObjectName("tool_move"); m_showCoordinatesAction = action("movetool-show-coordinates"); m_showCoordinatesAction = action("movetool-show-coordinates"); connect(&m_updateCursorCompressor, SIGNAL(timeout()), this, SLOT(resetCursorStyle())); m_optionsWidget = new MoveToolOptionsWidget(0, currentImage()->xRes(), toolId()); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height()); m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates()); m_optionsWidget->slotSetTranslate(m_handlesRect.topLeft() + currentOffset()); connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int)), Qt::UniqueConnection); connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int)), Qt::UniqueConnection); connect(m_optionsWidget, SIGNAL(sigRequestCommitOffsetChanges()), this, SLOT(commitChanges()), Qt::UniqueConnection); connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint)), Qt::UniqueConnection); connect(qobject_cast(canvas)->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)), this, SLOT(slotNodeChanged(KisNodeList)), Qt::UniqueConnection); connect(qobject_cast(canvas)->viewManager()->selectionManager(), SIGNAL(currentSelectionChanged()), this, SLOT(slotSelectionChanged()), Qt::UniqueConnection); } KisToolMove::~KisToolMove() { endStroke(); } void KisToolMove::resetCursorStyle() { KisTool::resetCursorStyle(); if (!isActive()) return; KisImageSP image = this->image(); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, currentNode(), canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeList nodes = fetchSelectedNodes(moveToolMode(), &m_lastCursorPos, selection); if (nodes.isEmpty()) { canvas()->setCursor(Qt::ForbiddenCursor); } } KisNodeList KisToolMove::fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection) { KisNodeList nodes; KisImageSP image = this->image(); if (mode != MoveSelectedLayer && pixelPoint) { const bool wholeGroup = !selection && mode == MoveGroup; KisNodeSP node = KisToolUtils::findNode(image->root(), *pixelPoint, wholeGroup); if (node) { nodes = {node}; } } if (nodes.isEmpty()) { nodes = this->selectedNodes(); KritaUtils::filterContainer(nodes, [](KisNodeSP node) { return node->isEditable(); }); } return nodes; } bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos) { KisNodeSP node; KisImageSP image = this->image(); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, currentNode(), canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeList nodes = fetchSelectedNodes(mode, pos, selection); if (nodes.size() == 1) { node = nodes.first(); } if (nodes.isEmpty()) { return false; } /** * If the target node has changed, the stroke should be * restarted. Otherwise just continue processing current node. */ if (m_strokeId && !tryEndPreviousStroke(nodes)) { return true; } KisStrokeStrategy *strategy; KisPaintLayerSP paintLayer = node ? dynamic_cast(node.data()) : 0; if (paintLayer && selection && (!selection->selectedRect().isEmpty() && !selection->selectedExactRect().isEmpty())) { MoveSelectionStrokeStrategy *moveStrategy = new MoveSelectionStrokeStrategy(paintLayer, selection, image.data(), image.data()); connect(moveStrategy, SIGNAL(sigHandlesRectCalculated(const QRect&)), SLOT(slotHandlesRectCalculated(const QRect&))); strategy = moveStrategy; } else { MoveStrokeStrategy *moveStrategy = new MoveStrokeStrategy(nodes, image.data(), image.data()); connect(moveStrategy, SIGNAL(sigHandlesRectCalculated(const QRect&)), SLOT(slotHandlesRectCalculated(const QRect&))); strategy = moveStrategy; } // disable outline feedback until the stroke calcualtes // correct bounding rect m_handlesRect = QRect(); m_strokeId = image->startStroke(strategy); m_currentlyProcessingNodes = nodes; m_accumulatedOffset = QPoint(); KIS_SAFE_ASSERT_RECOVER(m_changesTracker.isEmpty()) { m_changesTracker.reset(); } commitChanges(); return true; } QPoint KisToolMove::currentOffset() const { return m_accumulatedOffset + m_dragPos - m_dragStart; } void KisToolMove::notifyGuiAfterMove(bool showFloatingMessage) { if (!m_optionsWidget) return; if (m_handlesRect.isEmpty()) return; const QPoint currentTopLeft = m_handlesRect.topLeft() + currentOffset(); KisSignalsBlocker b(m_optionsWidget); emit moveInNewPosition(currentTopLeft); // TODO: fetch this info not from options widget, but from config const bool showCoordinates = m_optionsWidget->showCoordinates(); if (showCoordinates && showFloatingMessage) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in move tool", "X: %1 px, Y: %2 px", currentTopLeft.x(), currentTopLeft.y()), QIcon(), 1000, KisFloatingMessage::High); } } -bool KisToolMove::tryEndPreviousStroke(KisNodeList nodes) +bool KisToolMove::tryEndPreviousStroke(const KisNodeList &nodes) { if (!m_strokeId) return false; bool strokeEnded = false; if (!KritaUtils::compareListsUnordered(nodes, m_currentlyProcessingNodes)) { endStroke(); strokeEnded = true; } return strokeEnded; } void KisToolMove::commitChanges() { KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId); QSharedPointer newState(new KisToolMoveState(m_accumulatedOffset)); KisToolMoveState *lastState = dynamic_cast(m_changesTracker.lastState().data()); if (lastState && *lastState == *newState) return; m_changesTracker.commitConfig(newState); } void KisToolMove::slotHandlesRectCalculated(const QRect &handlesRect) { m_handlesRect = handlesRect; notifyGuiAfterMove(false); } void KisToolMove::moveDiscrete(MoveDirection direction, bool big) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()) return; if (!image()) return; if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } // Larger movement if "shift" key is pressed. qreal scale = big ? m_optionsWidget->moveScale() : 1.0; qreal moveStep = m_optionsWidget->moveStep() * scale; const QPoint offset = direction == Up ? QPoint( 0, -moveStep) : direction == Down ? QPoint( 0, moveStep) : direction == Left ? QPoint(-moveStep, 0) : QPoint( moveStep, 0) ; m_accumulatedOffset += offset; image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(); commitChanges(); setMode(KisTool::HOVER_MODE); } void KisToolMove::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); m_actionConnections.addConnection(action("movetool-move-up"), SIGNAL(triggered(bool)), this, SLOT(slotMoveDiscreteUp())); m_actionConnections.addConnection(action("movetool-move-down"), SIGNAL(triggered(bool)), this, SLOT(slotMoveDiscreteDown())); m_actionConnections.addConnection(action("movetool-move-left"), SIGNAL(triggered(bool)), this, SLOT(slotMoveDiscreteLeft())); m_actionConnections.addConnection(action("movetool-move-right"), SIGNAL(triggered(bool)), this, SLOT(slotMoveDiscreteRight())); m_actionConnections.addConnection(action("movetool-move-up-more"), SIGNAL(triggered(bool)), this, SLOT(slotMoveDiscreteUpMore())); m_actionConnections.addConnection(action("movetool-move-down-more"), SIGNAL(triggered(bool)), this, SLOT(slotMoveDiscreteDownMore())); m_actionConnections.addConnection(action("movetool-move-left-more"), SIGNAL(triggered(bool)), this, SLOT(slotMoveDiscreteLeftMore())); m_actionConnections.addConnection(action("movetool-move-right-more"), SIGNAL(triggered(bool)), this, SLOT(slotMoveDiscreteRightMore())); connect(m_showCoordinatesAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(setShowCoordinates(bool)), Qt::UniqueConnection); connect(m_optionsWidget, SIGNAL(showCoordinatesChanged(bool)), m_showCoordinatesAction, SLOT(setChecked(bool)), Qt::UniqueConnection); connect(&m_changesTracker, SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)), SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP))); slotNodeChanged(this->selectedNodes()); } void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if (m_strokeId && !m_handlesRect.isEmpty()) { QPainterPath handles; handles.addRect(m_handlesRect.translated(currentOffset())); QPainterPath path = pixelToView(handles); paintToolOutline(&gc, path); } } void KisToolMove::deactivate() { m_actionConnections.clear(); disconnect(m_showCoordinatesAction, 0, this, 0); disconnect(m_optionsWidget, 0, this, 0); endStroke(); KisTool::deactivate(); } void KisToolMove::requestStrokeEnd() { endStroke(); } void KisToolMove::requestStrokeCancellation() { cancelStroke(); } void KisToolMove::requestUndoDuringStroke() { if (!m_strokeId) return; if (m_changesTracker.isEmpty()) { cancelStroke(); } else { m_changesTracker.requestUndo(); } } void KisToolMove::beginPrimaryAction(KoPointerEvent *event) { startAction(event, moveToolMode()); } void KisToolMove::continuePrimaryAction(KoPointerEvent *event) { continueAction(event); } void KisToolMove::endPrimaryAction(KoPointerEvent *event) { endAction(event); } void KisToolMove::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { // Ctrl+Right click toggles between moving current layer and moving layer w/ content if (action == PickFgNode || action == PickBgImage) { MoveToolMode mode = moveToolMode(); if (mode == MoveSelectedLayer) { mode = MoveFirstLayer; } else if (mode == MoveFirstLayer) { mode = MoveSelectedLayer; } startAction(event, mode); } else { startAction(event, MoveGroup); } } void KisToolMove::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(action) continueAction(event); } void KisToolMove::endAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(action) endAction(event); } void KisToolMove::mouseMoveEvent(KoPointerEvent *event) { m_lastCursorPos = convertToPixelCoord(event).toPoint(); KisTool::mouseMoveEvent(event); if (moveToolMode() == MoveFirstLayer) { m_updateCursorCompressor.start(); } } void KisToolMove::startAction(KoPointerEvent *event, MoveToolMode mode) { QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); m_dragStart = pos; m_dragPos = pos; if (startStrokeImpl(mode, &pos)) { setMode(KisTool::PAINT_MODE); } else { event->ignore(); m_dragPos = QPoint(); m_dragStart = QPoint(); } qobject_cast(canvas())->updateCanvas(); } void KisToolMove::continueAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); if (!m_strokeId) return; QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); pos = applyModifiers(event->modifiers(), pos); m_dragPos = pos; drag(pos); notifyGuiAfterMove(); qobject_cast(canvas())->updateCanvas(); } void KisToolMove::endAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); setMode(KisTool::HOVER_MODE); if (!m_strokeId) return; QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); pos = applyModifiers(event->modifiers(), pos); drag(pos); m_accumulatedOffset += pos - m_dragStart; m_dragStart = QPoint(); m_dragPos = QPoint(); commitChanges(); notifyGuiAfterMove(); qobject_cast(canvas())->updateCanvas(); } void KisToolMove::drag(const QPoint& newPos) { KisImageWSP image = currentImage(); QPoint offset = m_accumulatedOffset + newPos - m_dragStart; image->addJob(m_strokeId, new MoveStrokeStrategy::Data(offset)); } void KisToolMove::endStroke() { if (!m_strokeId) return; KisImageSP image = currentImage(); image->endStroke(m_strokeId); m_strokeId.clear(); m_changesTracker.reset(); m_currentlyProcessingNodes.clear(); m_accumulatedOffset = QPoint(); qobject_cast(canvas())->updateCanvas(); } void KisToolMove::slotTrackerChangedConfig(KisToolChangesTrackerDataSP state) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId); KisToolMoveState *newState = dynamic_cast(state.data()); KIS_SAFE_ASSERT_RECOVER_RETURN(newState); if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging m_accumulatedOffset = newState->accumulatedOffset; image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(); } void KisToolMove::slotMoveDiscreteLeft() { moveDiscrete(MoveDirection::Left, false); } void KisToolMove::slotMoveDiscreteRight() { moveDiscrete(MoveDirection::Right, false); } void KisToolMove::slotMoveDiscreteUp() { moveDiscrete(MoveDirection::Up, false); } void KisToolMove::slotMoveDiscreteDown() { moveDiscrete(MoveDirection::Down, false); } void KisToolMove::slotMoveDiscreteLeftMore() { moveDiscrete(MoveDirection::Left, true); } void KisToolMove::slotMoveDiscreteRightMore() { moveDiscrete(MoveDirection::Right, true); } void KisToolMove::slotMoveDiscreteUpMore() { moveDiscrete(MoveDirection::Up, true); } void KisToolMove::slotMoveDiscreteDownMore() { moveDiscrete(MoveDirection::Down, true); } void KisToolMove::cancelStroke() { if (!m_strokeId) return; KisImageSP image = currentImage(); image->cancelStroke(m_strokeId); m_strokeId.clear(); m_changesTracker.reset(); m_currentlyProcessingNodes.clear(); m_accumulatedOffset = QPoint(); notifyGuiAfterMove(); qobject_cast(canvas())->updateCanvas(); } QWidget* KisToolMove::createOptionWidget() { return m_optionsWidget; } KisToolMove::MoveToolMode KisToolMove::moveToolMode() const { if (m_optionsWidget) return m_optionsWidget->mode(); return MoveSelectedLayer; } QPoint KisToolMove::applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos) { QPoint move = pos - m_dragStart; // Snap to axis if (modifiers & Qt::ShiftModifier) { move = snapToClosestAxis(move); } // "Precision mode" - scale down movement by 1/5 if (modifiers & Qt::AltModifier) { const qreal SCALE_FACTOR = .2; move = SCALE_FACTOR * move; } return m_dragStart + move; } void KisToolMove::moveBySpinX(int newX) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } m_accumulatedOffset.rx() = newX - m_handlesRect.x(); image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(false); setMode(KisTool::HOVER_MODE); } void KisToolMove::moveBySpinY(int newY) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } m_accumulatedOffset.ry() = newY - m_handlesRect.y(); image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(false); setMode(KisTool::HOVER_MODE); } void KisToolMove::requestHandlesRectUpdate() { KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image(), currentNode(), canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisMoveBoundsCalculationJob *job = new KisMoveBoundsCalculationJob(this->selectedNodes(), selection, this); connect(job, SIGNAL(sigCalcualtionFinished(const QRect&)), SLOT(slotHandlesRectCalculated(const QRect &))); KisImageSP image = this->image(); image->addSpontaneousJob(job); notifyGuiAfterMove(false); } -void KisToolMove::slotNodeChanged(KisNodeList nodes) +void KisToolMove::slotNodeChanged(const KisNodeList &nodes) { if (m_strokeId && !tryEndPreviousStroke(nodes)) { return; } requestHandlesRectUpdate(); } void KisToolMove::slotSelectionChanged() { if (m_strokeId) return; requestHandlesRectUpdate(); } QList KisToolMoveFactory::createActionsImpl() { KisActionRegistry *actionRegistry = KisActionRegistry::instance(); QList actions = KisToolPaintFactoryBase::createActionsImpl(); actions << actionRegistry->makeQAction("movetool-move-up"); actions << actionRegistry->makeQAction("movetool-move-down"); actions << actionRegistry->makeQAction("movetool-move-left"); actions << actionRegistry->makeQAction("movetool-move-right"); actions << actionRegistry->makeQAction("movetool-move-up-more"); actions << actionRegistry->makeQAction("movetool-move-down-more"); actions << actionRegistry->makeQAction("movetool-move-left-more"); actions << actionRegistry->makeQAction("movetool-move-right-more"); actions << actionRegistry->makeQAction("movetool-show-coordinates"); return actions; } diff --git a/plugins/tools/basictools/kis_tool_move.h b/plugins/tools/basictools/kis_tool_move.h index d71ccaa9c2..040f31a879 100644 --- a/plugins/tools/basictools/kis_tool_move.h +++ b/plugins/tools/basictools/kis_tool_move.h @@ -1,205 +1,205 @@ /* * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 2003 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_MOVE_H_ #define KIS_TOOL_MOVE_H_ #include #include #include #include #include #include #include #include #include #include "KisToolChangesTracker.h" #include "kis_signal_compressor.h" #include "kis_signal_auto_connection.h" #include "kis_canvas2.h" class KoCanvasBase; class MoveToolOptionsWidget; class KisDocument; class KisToolMove : public KisTool { Q_OBJECT Q_ENUMS(MoveToolMode); public: KisToolMove(KoCanvasBase * canvas); ~KisToolMove() override; /** * @brief wantsAutoScroll * reimplemented from KoToolBase * there's an issue where autoscrolling with this tool never makes the * stroke end, so we return false here so that users don't get stuck with * the tool. See bug 362659 * @return false */ bool wantsAutoScroll() const override { return false; } public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; public Q_SLOTS: void requestStrokeEnd() override; void requestStrokeCancellation() override; void requestUndoDuringStroke() override; protected Q_SLOTS: void resetCursorStyle() override; public: enum MoveToolMode { MoveSelectedLayer, MoveFirstLayer, MoveGroup }; enum MoveDirection { Up, Down, Left, Right }; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override; void endAlternateAction(KoPointerEvent *event, AlternateAction action) override; void mouseMoveEvent(KoPointerEvent *event) override; void startAction(KoPointerEvent *event, MoveToolMode mode); void continueAction(KoPointerEvent *event); void endAction(KoPointerEvent *event); void paint(QPainter& gc, const KoViewConverter &converter) override; QWidget *createOptionWidget() override; void updateUIUnit(int newUnit); MoveToolMode moveToolMode() const; void setShowCoordinates(bool value); public Q_SLOTS: void moveDiscrete(MoveDirection direction, bool big); void moveBySpinX(int newX); void moveBySpinY(int newY); - void slotNodeChanged(KisNodeList nodes); + void slotNodeChanged(const KisNodeList &nodes); void slotSelectionChanged(); void commitChanges(); void slotHandlesRectCalculated(const QRect &handlesRect); Q_SIGNALS: void moveToolModeChanged(); void moveInNewPosition(QPoint); private: void drag(const QPoint& newPos); void cancelStroke(); QPoint applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos); bool startStrokeImpl(MoveToolMode mode, const QPoint *pos); QPoint currentOffset() const; void notifyGuiAfterMove(bool showFloatingMessage = true); - bool tryEndPreviousStroke(KisNodeList nodes); + bool tryEndPreviousStroke(const KisNodeList &nodes); KisNodeList fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection); void requestHandlesRectUpdate(); private Q_SLOTS: void endStroke(); void slotTrackerChangedConfig(KisToolChangesTrackerDataSP state); void slotMoveDiscreteLeft(); void slotMoveDiscreteRight(); void slotMoveDiscreteUp(); void slotMoveDiscreteDown(); void slotMoveDiscreteLeftMore(); void slotMoveDiscreteRightMore(); void slotMoveDiscreteUpMore(); void slotMoveDiscreteDownMore(); private: MoveToolOptionsWidget* m_optionsWidget {0}; QPoint m_dragStart; ///< Point where current cursor dragging began QPoint m_accumulatedOffset; ///< Total offset including multiple clicks, up/down/left/right keys, etc. added together KisStrokeId m_strokeId; KisNodeList m_currentlyProcessingNodes; int m_resolution; QAction *m_showCoordinatesAction {0}; QPoint m_dragPos; QRect m_handlesRect; KisToolChangesTracker m_changesTracker; QPoint m_lastCursorPos; KisSignalCompressor m_updateCursorCompressor; KisSignalAutoConnectionsStore m_actionConnections; }; class KisToolMoveFactory : public KisToolPaintFactoryBase { public: KisToolMoveFactory() : KisToolPaintFactoryBase("KritaTransform/KisToolMove") { setToolTip(i18n("Move Tool")); setSection(TOOL_TYPE_TRANSFORM); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setPriority(3); setIconName(koIconNameCStr("krita_tool_move")); setShortcut(QKeySequence(Qt::Key_T)); } ~KisToolMoveFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolMove(canvas); } QList createActionsImpl() override; }; #endif // KIS_TOOL_MOVE_H_