File indexing completed on 2024-06-16 04:17:22
0001 /* 0002 * SPDX-FileCopyrightText: 2008-2010 Lukáš Tvrdý <lukast.dev@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_hairy_paintop.h" 0008 #include "kis_hairy_paintop_settings.h" 0009 0010 #include <cmath> 0011 #include <QRect> 0012 0013 #include <kis_image.h> 0014 #include <kis_debug.h> 0015 0016 #include "kis_paint_device.h" 0017 #include "kis_painter.h" 0018 #include <kis_vec.h> 0019 0020 #include "KisHairyInkOptionData.h" 0021 #include "KisHairyBristleOptionData.h" 0022 #include <kis_brush_option.h> 0023 #include <kis_brush_based_paintop_settings.h> 0024 #include <kis_fixed_paint_device.h> 0025 #include <kis_lod_transform.h> 0026 #include <kis_spacing_information.h> 0027 #include <KoResourceLoadResult.h> 0028 0029 0030 #include "kis_brush.h" 0031 0032 KisHairyPaintOp::KisHairyPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) 0033 : KisPaintOp(painter) 0034 , m_opacityOption(settings.data()) 0035 , m_sizeOption(settings.data()) 0036 , m_rotationOption(settings.data()) 0037 { 0038 Q_UNUSED(image); 0039 Q_ASSERT(settings); 0040 0041 m_hairyBristleOption.read(settings.data()); 0042 m_hairyInkOption.read(settings.data()); 0043 0044 m_dev = node ? node->paintDevice() : 0; 0045 0046 KisBrushOptionProperties brushOption; 0047 brushOption.readOptionSetting(settings, settings->resourcesInterface(), settings->canvasResourcesInterface()); 0048 KisBrushSP brush = brushOption.brush(); 0049 KisFixedPaintDeviceSP dab = cachedDab(painter->device()->compositionSourceColorSpace()); 0050 0051 // properly initialize fake paint information to avoid warnings 0052 KisPaintInformation fakePaintInformation; 0053 fakePaintInformation.setRandomSource(new KisRandomSource()); 0054 fakePaintInformation.setPerStrokeRandomSource(new KisPerStrokeRandomSource()); 0055 0056 if (brush->brushApplication() == IMAGESTAMP) { 0057 dab = brush->paintDevice(source()->colorSpace(), KisDabShape(), fakePaintInformation); 0058 } else { 0059 brush->mask(dab, painter->paintColor(), KisDabShape(), fakePaintInformation); 0060 } 0061 0062 m_brush.fromDabWithDensity(dab, m_hairyBristleOption.densityFactor * 0.01); 0063 m_brush.setInkColor(painter->paintColor()); 0064 0065 loadSettings(); 0066 m_brush.setProperties(&m_properties); 0067 } 0068 0069 QList<KoResourceLoadResult> KisHairyPaintOp::prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) 0070 { 0071 KisBrushOptionProperties brushOption; 0072 return brushOption.prepareLinkedResources(settings, resourcesInterface); 0073 } 0074 0075 void KisHairyPaintOp::loadSettings() 0076 { 0077 m_properties.inkAmount = m_hairyInkOption.inkAmount; 0078 //TODO: wait for the transfer function with variable size 0079 0080 m_properties.inkDepletionCurve = KisCubicCurve(m_hairyInkOption.inkDepletionCurve).floatTransfer(m_hairyInkOption.inkAmount); 0081 0082 m_properties.inkDepletionEnabled = m_hairyInkOption.inkDepletionEnabled; 0083 m_properties.useSaturation = m_hairyInkOption.useSaturation; 0084 m_properties.useOpacity = m_hairyInkOption.useOpacity; 0085 m_properties.useWeights = m_hairyInkOption.useWeights; 0086 0087 m_properties.pressureWeight = m_hairyInkOption.pressureWeight / 100.0; 0088 m_properties.bristleLengthWeight = m_hairyInkOption.bristleLengthWeight / 100.0; 0089 m_properties.bristleInkAmountWeight = m_hairyInkOption.bristleInkAmountWeight / 100.0; 0090 m_properties.inkDepletionWeight = m_hairyInkOption.inkDepletionWeight; 0091 m_properties.useSoakInk = m_hairyInkOption.useSoakInk; 0092 0093 m_properties.useMousePressure = m_hairyBristleOption.useMousePressure; 0094 m_properties.shearFactor = m_hairyBristleOption.shearFactor; 0095 m_properties.randomFactor = m_hairyBristleOption.randomFactor; 0096 m_properties.scaleFactor = m_hairyBristleOption.scaleFactor; 0097 m_properties.threshold = m_hairyBristleOption.threshold; 0098 m_properties.antialias = m_hairyBristleOption.antialias; 0099 m_properties.useCompositing = m_hairyBristleOption.useCompositing; 0100 m_properties.connectedPath = m_hairyBristleOption.connectedPath; 0101 } 0102 0103 0104 KisSpacingInformation KisHairyPaintOp::paintAt(const KisPaintInformation& info) 0105 { 0106 return updateSpacingImpl(info); 0107 } 0108 0109 KisSpacingInformation KisHairyPaintOp::updateSpacingImpl(const KisPaintInformation &info) const 0110 { 0111 Q_UNUSED(info); 0112 return KisSpacingInformation(0.5); 0113 } 0114 0115 0116 void KisHairyPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) 0117 { 0118 Q_UNUSED(currentDistance); 0119 if (!painter()) return; 0120 0121 if (!m_dab) { 0122 m_dab = source()->createCompositionSourceDevice(); 0123 } 0124 else { 0125 m_dab->clear(); 0126 } 0127 0128 /** 0129 * Even though we don't use spacing in hairy brush, we should still 0130 * initialize its distance information to ensure drawing angle and 0131 * other history-based sensors work fine. 0132 */ 0133 KisPaintInformation pi(pi2); 0134 KisPaintInformation::DistanceInformationRegistrar r = 0135 pi.registerDistanceInformation(currentDistance); 0136 0137 // Hairy Brush is capable of working with zero scale, 0138 // so no additional checks for 'zero'ness are needed 0139 qreal scale = m_sizeOption.apply(pi); 0140 scale *= KisLodTransform::lodToScale(painter()->device()); 0141 qreal rotation = m_rotationOption.apply(pi); 0142 quint8 origOpacity = m_opacityOption.apply(painter(), pi); 0143 0144 const bool mirrorFlip = pi1.canvasMirroredH() != pi1.canvasMirroredV(); 0145 0146 // we don't use spacing here (the brush itself is used only once 0147 // during initialization), so we should just skip the distance info 0148 // update 0149 0150 m_brush.paintLine(m_dab, m_dev, pi1, pi, scale * m_hairyBristleOption.scaleFactor, mirrorFlip ? -rotation : rotation); 0151 0152 //QRect rc = m_dab->exactBounds(); 0153 QRect rc = m_dab->extent(); 0154 painter()->bitBlt(rc.topLeft(), m_dab, rc); 0155 painter()->renderMirrorMask(rc, m_dab); 0156 painter()->setOpacity(origOpacity); 0157 0158 // we don't use spacing in hairy brush, but history is 0159 // still important for us 0160 currentDistance->registerPaintedDab(pi, 0161 KisSpacingInformation(), 0162 KisTimingInformation()); 0163 }