File indexing completed on 2024-12-22 04:16:10
0001 /* 0002 * SPDX-FileCopyrightText: 2008, 2009 Lukáš Tvrdý <lukast.dev@gmail.com> 0003 * SPDX-FileCopyrightText: 2010 José Luis Vergara <pentalis@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kis_hatching_paintop.h" 0009 #include "kis_hatching_paintop_settings.h" 0010 0011 #include <cmath> 0012 #include <QRect> 0013 0014 #include <KoColor.h> 0015 #include <KoColorSpace.h> 0016 0017 #include <kis_image.h> 0018 #include <kis_debug.h> 0019 0020 #include <kis_global.h> 0021 #include <kis_paint_device.h> 0022 #include <kis_painter.h> 0023 #include <kis_types.h> 0024 #include <brushengine/kis_paintop.h> 0025 #include <kis_brush_based_paintop.h> 0026 #include <brushengine/kis_paint_information.h> 0027 #include <kis_fixed_paint_device.h> 0028 #include <kis_lod_transform.h> 0029 #include <kis_spacing_information.h> 0030 0031 0032 0033 #include <KoColorSpaceRegistry.h> 0034 0035 KisHatchingPaintOp::KisHatchingPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP /*image*/) 0036 : KisBrushBasedPaintOp(settings, painter) 0037 , m_angleOption(settings.data()) 0038 , m_crosshatchingOption(settings.data()) 0039 , m_separationOption(settings.data()) 0040 , m_thicknessOption(settings.data()) 0041 , m_opacityOption(settings.data()) 0042 , m_sizeOption(settings.data()) 0043 { 0044 Q_UNUSED(node); 0045 0046 m_settings = static_cast<KisHatchingPaintOpSettings*>(settings->clone().data()); 0047 static_cast<const KisHatchingPaintOpSettings*>(settings.data())->initializeTwin(m_settings); 0048 0049 m_hatchingBrush = new HatchingBrush(m_settings); 0050 m_hatchingOptions.read(settings.data()); 0051 m_hatchingPreferences.read(settings.data()); 0052 0053 } 0054 0055 KisHatchingPaintOp::~KisHatchingPaintOp() 0056 { 0057 delete m_hatchingBrush; 0058 } 0059 0060 KisSpacingInformation KisHatchingPaintOp::paintAt(const KisPaintInformation& info) 0061 { 0062 //------START SIMPLE ERROR CATCHING------- 0063 if (!painter()->device()) return KisSpacingInformation(1.0); 0064 0065 if (!m_hatchedDab) 0066 m_hatchedDab = source()->createCompositionSourceDevice(); 0067 else 0068 m_hatchedDab->clear(); 0069 0070 //Simple convenience renaming, I'm thinking of removing these inherited quirks 0071 KisBrushSP brush = m_brush; 0072 KisPaintDeviceSP device = painter()->device(); 0073 0074 //Macro to catch errors 0075 Q_ASSERT(brush); 0076 0077 //----------SIMPLE error catching code, maybe it's not even needed------ 0078 if (!brush) return KisSpacingInformation(1.0); 0079 if (!brush->canPaintFor(info)) return KisSpacingInformation(1.0); 0080 0081 //SENSOR-depending settings 0082 m_settings->anglesensorvalue = m_angleOption.apply(info); 0083 m_settings->crosshatchingsensorvalue = m_crosshatchingOption.apply(info); 0084 m_settings->separationsensorvalue = m_separationOption.apply(info); 0085 m_settings->thicknesssensorvalue = m_thicknessOption.apply(info); 0086 0087 const qreal additionalScale = KisLodTransform::lodToScale(painter()->device()); 0088 const double scale = additionalScale * m_sizeOption.apply(info); 0089 if ((scale * brush->width()) <= 0.01 || (scale * brush->height()) <= 0.01) return KisSpacingInformation(1.0); 0090 KisDabShape shape(scale, 1.0, 0.0); 0091 0092 quint8 origOpacity = m_opacityOption.apply(painter(), info); 0093 0094 /*----Fetch the Dab----*/ 0095 static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); 0096 static KoColor color(Qt::black, cs); 0097 0098 QRect dstRect; 0099 KisFixedPaintDeviceSP maskDab = 0100 m_dabCache->fetchDab(cs, color, info.pos(), 0101 shape, 0102 info, 1.0, &dstRect); 0103 0104 // sanity check 0105 KIS_ASSERT_RECOVER_NOOP(dstRect.size() == maskDab->bounds().size()); 0106 0107 /*-----Convenient renaming for the limits of the maskDab, this will be used 0108 to hatch a dab of just the right size------*/ 0109 qint32 x, y, sw, sh; 0110 dstRect.getRect(&x, &y, &sw, &sh); 0111 0112 //------This If_block pre-fills the future m_hatchedDab with a pretty backgroundColor 0113 if (m_hatchingPreferences.useOpaqueBackground) { 0114 KoColor aersh = painter()->backgroundColor(); 0115 m_hatchedDab->fill(0, 0, (sw - 1), (sh - 1), aersh.data()); //this plus yellow background = french fry brush 0116 } 0117 0118 /* If block describing how to stack hatching passes to generate 0119 crosshatching according to user specifications */ 0120 if (m_crosshatchingOption.isChecked()) { 0121 if (m_hatchingOptions.crosshatchingStyle == CrosshatchingType::Perpendicular) { 0122 if (m_settings->crosshatchingsensorvalue > 0.5) 0123 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale); 0124 } 0125 else if (m_hatchingOptions.crosshatchingStyle == CrosshatchingType::MinusThenPlus) { 0126 if (m_settings->crosshatchingsensorvalue > 0.33) 0127 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); 0128 if (m_settings->crosshatchingsensorvalue > 0.67) 0129 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); 0130 } 0131 else if (m_hatchingOptions.crosshatchingStyle == CrosshatchingType::PlusThenMinus) { 0132 if (m_settings->crosshatchingsensorvalue > 0.33) 0133 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); 0134 if (m_settings->crosshatchingsensorvalue > 0.67) 0135 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); 0136 } 0137 else if (m_hatchingOptions.crosshatchingStyle == CrosshatchingType::MoirePattern) { 0138 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->crosshatchingsensorvalue) * 360), painter()->paintColor(), additionalScale); 0139 } 0140 } else { 0141 if (m_hatchingOptions.crosshatchingStyle == CrosshatchingType::Perpendicular) { 0142 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale); 0143 } 0144 else if (m_hatchingOptions.crosshatchingStyle == CrosshatchingType::MinusThenPlus) { 0145 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); 0146 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); 0147 } 0148 else if (m_hatchingOptions.crosshatchingStyle == CrosshatchingType::PlusThenMinus) { 0149 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); 0150 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); 0151 } 0152 else if (m_hatchingOptions.crosshatchingStyle == CrosshatchingType::MoirePattern) { 0153 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-10), painter()->paintColor(), additionalScale); 0154 } 0155 } 0156 0157 if (m_angleOption.isChecked()) 0158 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->anglesensorvalue)*360+m_hatchingOptions.angle), painter()->paintColor(), additionalScale); 0159 0160 // The base hatch... unless moiré or angle 0161 if (m_hatchingOptions.crosshatchingStyle != CrosshatchingType::MoirePattern && !m_angleOption.isChecked()) 0162 m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, m_hatchingOptions.angle, painter()->paintColor(), additionalScale); 0163 0164 0165 // The most important line, the one that paints to the screen. 0166 painter()->bitBltWithFixedSelection(x, y, m_hatchedDab, maskDab, sw, sh); 0167 painter()->renderMirrorMaskSafe(QRect(QPoint(x, y), QSize(sw, sh)), m_hatchedDab, 0, 0, maskDab, 0168 !m_dabCache->needSeparateOriginal()); 0169 painter()->setOpacity(origOpacity); 0170 0171 return effectiveSpacing(scale); 0172 } 0173 0174 KisSpacingInformation KisHatchingPaintOp::updateSpacingImpl(const KisPaintInformation &info) const 0175 { 0176 const qreal scale = KisLodTransform::lodToScale(painter()->device()) * m_sizeOption.apply(info); 0177 return effectiveSpacing(scale); 0178 } 0179 0180 double KisHatchingPaintOp::spinAngle(double spin) 0181 { 0182 double tempangle = m_hatchingOptions.angle + spin; 0183 qint8 factor = 1; 0184 0185 if (tempangle < 0) 0186 factor = -1; 0187 0188 tempangle = fabs(fmod(tempangle, 180)); 0189 0190 if ((tempangle >= 0) && (tempangle <= 90)) 0191 return factor * tempangle; 0192 else if ((tempangle > 90) && (tempangle <= 180)) 0193 return factor * -(180 - tempangle); 0194 0195 return 0; // this should never be executed except if NAN 0196 }