File indexing completed on 2024-06-16 04:16:40
0001 /* 0002 * KDE. Krita Project. 0003 * 0004 * SPDX-FileCopyrightText: 2020 Deif Lou <ginoba@gmail.com> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include <QHash> 0010 0011 #include <kpluginfactory.h> 0012 #include <kis_filter_registry.h> 0013 #include <filter/kis_filter_category_ids.h> 0014 #include <KoUpdater.h> 0015 #include <kis_filter_configuration.h> 0016 #include <generator/kis_generator.h> 0017 #include <generator/kis_generator_registry.h> 0018 #include <KisGlobalResourcesInterface.h> 0019 #include <KisSequentialIteratorProgress.h> 0020 #include <kis_processing_information.h> 0021 #include <kis_selection.h> 0022 #include <kis_painter.h> 0023 #include <KoCompositeOpRegistry.h> 0024 #include <KoColorModelStandardIds.h> 0025 #include <KoColorSpaceRegistry.h> 0026 #include <KoColorProfile.h> 0027 0028 #include "KisHalftoneFilter.h" 0029 #include "KisHalftoneConfigWidget.h" 0030 0031 K_PLUGIN_FACTORY_WITH_JSON(KritaHalftoneFactory, "KritaHalftone.json", registerPlugin<KritaHalftone>();) 0032 0033 KritaHalftone::KritaHalftone(QObject *parent, const QVariantList &) 0034 : QObject(parent) 0035 { 0036 KisFilterRegistry::instance()->add(new KisHalftoneFilter()); 0037 } 0038 0039 KritaHalftone::~KritaHalftone() 0040 {} 0041 0042 KisHalftoneFilter::KisHalftoneFilter() 0043 : KisFilter(id(), FiltersCategoryArtisticId, i18n("&Halftone...")) 0044 { 0045 setSupportsPainting(true); 0046 } 0047 0048 void KisHalftoneFilter::processImpl(KisPaintDeviceSP device, 0049 const QRect &applyRect, 0050 const KisFilterConfigurationSP config, 0051 KoUpdater *progressUpdater) const 0052 { 0053 const KisHalftoneFilterConfiguration *filterConfig = 0054 dynamic_cast<const KisHalftoneFilterConfiguration*>(config.data()); 0055 0056 Q_ASSERT(device); 0057 KIS_SAFE_ASSERT_RECOVER_RETURN(filterConfig); 0058 KIS_SAFE_ASSERT_RECOVER_NOOP(filterConfig->hasLocalResourcesSnapshot()); 0059 0060 const QString mode = filterConfig->mode(); 0061 KIS_SAFE_ASSERT_RECOVER_RETURN( 0062 mode == KisHalftoneFilterConfiguration::HalftoneMode_Intensity || 0063 mode == KisHalftoneFilterConfiguration::HalftoneMode_IndependentChannels || 0064 mode == KisHalftoneFilterConfiguration::HalftoneMode_Alpha 0065 ); 0066 0067 KIS_SAFE_ASSERT_RECOVER_RETURN( 0068 (device->colorSpace()->colorModelId().id() == AlphaColorModelID.id() && 0069 mode == KisHalftoneFilterConfiguration::HalftoneMode_Alpha) || 0070 (device->colorSpace()->colorModelId().id() == GrayColorModelID.id() && 0071 mode == KisHalftoneFilterConfiguration::HalftoneMode_Intensity) || 0072 (device->colorSpace()->colorModelId().id() == GrayAColorModelID.id() && 0073 (mode == KisHalftoneFilterConfiguration::HalftoneMode_Intensity || 0074 mode == KisHalftoneFilterConfiguration::HalftoneMode_Alpha)) || 0075 ((device->colorSpace()->colorModelId().id() == RGBAColorModelID.id() || 0076 device->colorSpace()->colorModelId().id() == XYZAColorModelID.id() || 0077 device->colorSpace()->colorModelId().id() == LABAColorModelID.id() || 0078 device->colorSpace()->colorModelId().id() == CMYKAColorModelID.id() || 0079 device->colorSpace()->colorModelId().id() == YCbCrAColorModelID.id()) && 0080 (mode == KisHalftoneFilterConfiguration::HalftoneMode_Intensity || 0081 mode == KisHalftoneFilterConfiguration::HalftoneMode_IndependentChannels || 0082 mode == KisHalftoneFilterConfiguration::HalftoneMode_Alpha)) 0083 ); 0084 0085 if (mode == KisHalftoneFilterConfiguration::HalftoneMode_Intensity) { 0086 KIS_SAFE_ASSERT_RECOVER_RETURN( 0087 device->colorSpace()->colorModelId().id() != AlphaColorModelID.id() 0088 ); 0089 processIntensity(device, applyRect, filterConfig, progressUpdater); 0090 } else if (mode == KisHalftoneFilterConfiguration::HalftoneMode_IndependentChannels) { 0091 KIS_SAFE_ASSERT_RECOVER_RETURN( 0092 device->colorSpace()->colorModelId().id() != AlphaColorModelID.id() && 0093 device->colorSpace()->colorModelId().id() != GrayColorModelID.id() && 0094 device->colorSpace()->colorModelId().id() != GrayAColorModelID.id() 0095 ); 0096 processChannels(device, applyRect, filterConfig, progressUpdater); 0097 } else { 0098 KIS_SAFE_ASSERT_RECOVER_RETURN( 0099 device->colorSpace()->colorModelId().id() != GrayColorModelID.id() 0100 ); 0101 if (filterConfig->colorModelId() == AlphaColorModelID.id()) 0102 { 0103 // This an Alpha layer (mask layer) but the device passed 0104 // here has the composition color space (GrayA) 0105 processMask(device, applyRect, filterConfig, progressUpdater); 0106 } else { 0107 // process the alpha channel of a multichannel device 0108 processAlpha(device, applyRect, filterConfig, progressUpdater); 0109 } 0110 } 0111 } 0112 0113 QVector<quint8> KisHalftoneFilter::makeHardnessLut(qreal hardness) 0114 { 0115 QVector<quint8> hardnessLut(256); 0116 if (qFuzzyCompare(hardness, 1.0)) { 0117 for (int i = 0; i < 256; ++i) { 0118 hardnessLut[i] = i < 128 ? 0 : 255; 0119 } 0120 } else { 0121 qreal m = 1.0 / (1.0 - hardness); 0122 qreal b = -m * (hardness / 2.0); 0123 for (int i = 0; i < 256; ++i) { 0124 hardnessLut[i] = qBound(0, static_cast<int>(qRound((m * (i / 255.0) + b) * 255.0)), 255); 0125 } 0126 } 0127 return hardnessLut; 0128 } 0129 0130 QVector<quint8> KisHalftoneFilter::makeNoiseWeightLut(qreal hardness) 0131 { 0132 QVector<quint8> noiseWeightLut(256); 0133 hardness *= 0.99; 0134 for (int i = 0; i < 256; ++i) { 0135 qreal iNorm = i / 255.0; 0136 qreal weight = (2.0 - std::abs(4.0 * iNorm - 2.0)) + hardness; 0137 noiseWeightLut[i] = qBound(0, static_cast<int>(qRound(weight * 255.0)), 255); 0138 } 0139 return noiseWeightLut; 0140 } 0141 0142 KisPaintDeviceSP KisHalftoneFilter::makeGeneratorPaintDevice(KisPaintDeviceSP prototype, 0143 const QString & prefix, 0144 const QRect &applyRect, 0145 const KisHalftoneFilterConfiguration *config, 0146 KoUpdater *progressUpdater) const 0147 { 0148 const QString generatorId = config->generatorId(prefix); 0149 if (generatorId.isEmpty()) { 0150 return nullptr; 0151 } 0152 0153 KisGeneratorSP generator = KisGeneratorRegistry::instance()->get(generatorId); 0154 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(generator, nullptr); 0155 0156 KisFilterConfigurationSP generatorConfiguration = config->generatorConfiguration(prefix); 0157 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(generatorConfiguration, nullptr); 0158 0159 // Fill the generator device 0160 KisPaintDeviceSP generatorDevice = m_grayDevicesCache.getDevice(prototype, KoColorSpaceRegistry::instance()->graya8()); 0161 0162 KisProcessingInformation(generatorDevice, applyRect.topLeft(), KisSelectionSP()); 0163 generator->generate( 0164 KisProcessingInformation(generatorDevice, applyRect.topLeft(),KisSelectionSP()), 0165 applyRect.size(), 0166 generatorConfiguration, 0167 progressUpdater 0168 ); 0169 0170 return generatorDevice; 0171 } 0172 0173 bool KisHalftoneFilter::checkUpdaterInterruptedAndSetPercent(KoUpdater *progressUpdater, int percent) const 0174 { 0175 // The updater is null so return false to keep going 0176 // with the computations 0177 if (!progressUpdater) { 0178 return false; 0179 } 0180 0181 if (progressUpdater->interrupted()) { 0182 return true; 0183 } 0184 0185 progressUpdater->setProgress(percent); 0186 return false; 0187 } 0188 0189 void KisHalftoneFilter::processIntensity(KisPaintDeviceSP device, 0190 const QRect &applyRect, 0191 const KisHalftoneFilterConfiguration *config, 0192 KoUpdater *progressUpdater) const 0193 { 0194 const QString prefix = "intensity_"; 0195 0196 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 0)) { 0197 return; 0198 } 0199 0200 // Make the generator device 0201 KisPaintDeviceSP generatorDevice = makeGeneratorPaintDevice(device, prefix, applyRect, config, nullptr); 0202 if (!generatorDevice) { 0203 return; 0204 } 0205 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 25)) { 0206 return; 0207 } 0208 0209 // Make the hardness and the noise weight LUT 0210 const qreal hardness = config->hardness(prefix) / 100.0; 0211 const QVector<quint8> hardnessLut = makeHardnessLut(hardness); 0212 const QVector<quint8> noiseWeightLut = makeNoiseWeightLut(hardness); 0213 0214 // Fill the mask device 0215 KisSelectionSP maskDevice = m_selectionsCache.getSelection(); 0216 0217 { 0218 const bool invert = config->invert(prefix); 0219 0220 KisSequentialIterator maskIterator(maskDevice->pixelSelection(), applyRect); 0221 KisSequentialIterator dstIterator(device, applyRect); 0222 KisSequentialIterator srcIterator(generatorDevice, applyRect); 0223 0224 if (!invert) { 0225 while (maskIterator.nextPixel() && dstIterator.nextPixel() && srcIterator.nextPixel()) { 0226 int dstGray = device->colorSpace()->intensity8(dstIterator.rawData()); 0227 int srcGray = srcIterator.rawData()[0]; 0228 int srcAlpha = srcIterator.rawData()[1]; 0229 0230 // Combine pixels 0231 int result = qBound(0, dstGray + (srcGray - 128) * noiseWeightLut[dstGray] * srcAlpha / 0xFE01, 255); 0232 0233 // Apply hardness 0234 result = hardnessLut[result]; 0235 0236 *maskIterator.rawData() = 255 - result; 0237 } 0238 } else { 0239 while (maskIterator.nextPixel() && dstIterator.nextPixel() && srcIterator.nextPixel()) { 0240 int dstGray = device->colorSpace()->intensity8(dstIterator.rawData()); 0241 int srcGray = srcIterator.rawData()[0]; 0242 int srcAlpha = srcIterator.rawData()[1]; 0243 0244 // Combine pixels 0245 int result = qBound(0, dstGray + (srcGray - 128) * noiseWeightLut[dstGray] * srcAlpha / 0xFE01, 255); 0246 0247 // Apply hardness 0248 result = hardnessLut[result]; 0249 0250 *maskIterator.rawData() = result; 0251 } 0252 } 0253 m_grayDevicesCache.putDevice(generatorDevice); 0254 } 0255 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 50)) { 0256 return; 0257 } 0258 0259 // Make the halftone image 0260 const KoColorSpace *colorSpace; 0261 if (device->colorSpace()->profile()->isLinear()) { 0262 colorSpace = KoColorSpaceRegistry::instance()->rgb8(); 0263 } else { 0264 colorSpace = device->colorSpace(); 0265 } 0266 KisPaintDeviceSP halftoneDevice = m_genericDevicesCache.getDevice(device, colorSpace); 0267 0268 { 0269 KisPaintDeviceSP foregroundDevice = m_genericDevicesCache.getDevice(device, colorSpace); 0270 KoColor foregroundColor = config->foregroundColor(prefix); 0271 KoColor backgroundColor = config->backgroundColor(prefix); 0272 const qreal foregroundOpacity = config->foregroundOpacity(prefix) / 100.0; 0273 const qreal backgroundOpacity = config->backgroundOpacity(prefix) / 100.0; 0274 foregroundColor.convertTo(colorSpace); 0275 backgroundColor.convertTo(colorSpace); 0276 foregroundColor.setOpacity(foregroundOpacity); 0277 backgroundColor.setOpacity(backgroundOpacity); 0278 0279 foregroundDevice->fill(applyRect, foregroundColor); 0280 halftoneDevice->fill(applyRect, backgroundColor); 0281 0282 KisPainter painter(halftoneDevice, maskDevice); 0283 painter.setCompositeOpId(COMPOSITE_OVER); 0284 painter.bitBlt(applyRect.topLeft(), foregroundDevice, applyRect); 0285 0286 m_genericDevicesCache.putDevice(foregroundDevice); 0287 m_selectionsCache.putSelection(maskDevice); 0288 } 0289 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 75)) { 0290 return; 0291 } 0292 0293 // Make the final image 0294 { 0295 KisPainter painter(halftoneDevice); 0296 painter.setCompositeOpId(COMPOSITE_DESTINATION_IN); 0297 painter.bitBlt(applyRect.topLeft(), device, applyRect); 0298 } 0299 { 0300 KisPainter painter(device); 0301 painter.setCompositeOpId(COMPOSITE_COPY); 0302 painter.bitBlt(applyRect.topLeft(), halftoneDevice, applyRect); 0303 } 0304 m_genericDevicesCache.putDevice(halftoneDevice); 0305 0306 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 100)) { 0307 return; 0308 } 0309 } 0310 0311 template <typename ChannelType> 0312 void KisHalftoneFilter::processChannel(KisPaintDeviceSP device, 0313 KisPaintDeviceSP generatorDevice, 0314 const QRect &applyRect, 0315 const KisHalftoneFilterConfiguration *config, 0316 const QString & prefix, 0317 KoChannelInfo * channelInfo) const 0318 { 0319 const int channelPos = channelInfo->pos() / sizeof(ChannelType); 0320 // Make the hardness and the noise weight LUT 0321 const qreal hardness = config->hardness(prefix) / 100.0; 0322 const QVector<quint8> hardnessLut = makeHardnessLut(hardness); 0323 const QVector<quint8> noiseWeightLut = makeNoiseWeightLut(hardness); 0324 0325 // Fill the device 0326 const bool invert = config->invert(prefix); 0327 KisSequentialIterator dstIterator(device, applyRect); 0328 KisSequentialIterator srcIterator(generatorDevice, applyRect); 0329 0330 const KoColorSpace *colorSpace = device->colorSpace(); 0331 0332 if (colorSpace->profile()->isLinear()) { 0333 if (!invert) { 0334 while (dstIterator.nextPixel() && srcIterator.nextPixel()) { 0335 int dst = 255 - device->colorSpace()->scaleToU8(dstIterator.rawData(), channelPos); 0336 int src = srcIterator.rawData()[0]; 0337 int srcAlpha = srcIterator.rawData()[1]; 0338 KoColor c(QColor(src, src, src, srcAlpha), device->colorSpace()); 0339 src = device->colorSpace()->scaleToU8(c.data(), 0); 0340 srcAlpha = device->colorSpace()->scaleToU8(c.data(), device->colorSpace()->alphaPos()); 0341 0342 // Combine pixels 0343 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255); 0344 0345 // Apply hardness 0346 result = hardnessLut[result]; 0347 0348 ChannelType *dstPixel = reinterpret_cast<ChannelType*>(dstIterator.rawData()); 0349 ChannelType channelMin = static_cast<ChannelType>(channelInfo->getUIMin()); 0350 ChannelType channelMax = static_cast<ChannelType>(channelInfo->getUIMax()); 0351 dstPixel[channelPos] = static_cast<ChannelType>(mapU8ToRange(255 - result, channelMin, channelMax)); 0352 } 0353 } else { 0354 while (dstIterator.nextPixel() && srcIterator.nextPixel()) { 0355 int dst = device->colorSpace()->scaleToU8(dstIterator.rawData(), channelPos); 0356 int src = srcIterator.rawData()[0]; 0357 int srcAlpha = srcIterator.rawData()[1]; 0358 KoColor c(QColor(src, src, src, srcAlpha), device->colorSpace()); 0359 src = device->colorSpace()->scaleToU8(c.data(), 0); 0360 srcAlpha = device->colorSpace()->scaleToU8(c.data(), device->colorSpace()->alphaPos()); 0361 0362 // Combine pixels 0363 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255); 0364 0365 // Apply hardness 0366 result = hardnessLut[result]; 0367 0368 ChannelType *dstPixel = reinterpret_cast<ChannelType*>(dstIterator.rawData()); 0369 ChannelType channelMin = static_cast<ChannelType>(channelInfo->getUIMin()); 0370 ChannelType channelMax = static_cast<ChannelType>(channelInfo->getUIMax()); 0371 dstPixel[channelPos] = static_cast<ChannelType>(mapU8ToRange(result, channelMin, channelMax)); 0372 } 0373 } 0374 } else { 0375 if (!invert) { 0376 while (dstIterator.nextPixel() && srcIterator.nextPixel()) { 0377 int dst = 255 - device->colorSpace()->scaleToU8(dstIterator.rawData(), channelPos); 0378 int src = srcIterator.rawData()[0]; 0379 int srcAlpha = srcIterator.rawData()[1]; 0380 0381 // Combine pixels 0382 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255); 0383 0384 // Apply hardness 0385 result = hardnessLut[result]; 0386 0387 ChannelType *dstPixel = reinterpret_cast<ChannelType*>(dstIterator.rawData()); 0388 ChannelType channelMin = static_cast<ChannelType>(channelInfo->getUIMin()); 0389 ChannelType channelMax = static_cast<ChannelType>(channelInfo->getUIMax()); 0390 dstPixel[channelPos] = static_cast<ChannelType>(mapU8ToRange(255 - result, channelMin, channelMax)); 0391 } 0392 } else { 0393 while (dstIterator.nextPixel() && srcIterator.nextPixel()) { 0394 int dst = device->colorSpace()->scaleToU8(dstIterator.rawData(), channelPos); 0395 int src = srcIterator.rawData()[0]; 0396 int srcAlpha = srcIterator.rawData()[1]; 0397 0398 // Combine pixels 0399 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255); 0400 0401 // Apply hardness 0402 result = hardnessLut[result]; 0403 0404 ChannelType *dstPixel = reinterpret_cast<ChannelType*>(dstIterator.rawData()); 0405 ChannelType channelMin = static_cast<ChannelType>(channelInfo->getUIMin()); 0406 ChannelType channelMax = static_cast<ChannelType>(channelInfo->getUIMax()); 0407 dstPixel[channelPos] = static_cast<ChannelType>(mapU8ToRange(result, channelMin, channelMax)); 0408 } 0409 } 0410 } 0411 } 0412 0413 void KisHalftoneFilter::processChannels(KisPaintDeviceSP device, 0414 const QRect &applyRect, 0415 const KisHalftoneFilterConfiguration *config, 0416 KoUpdater *progressUpdater) const 0417 { 0418 const QList<KoChannelInfo *> channels = device->colorSpace()->channels(); 0419 const int progressStep = 100 / (channels.count() * 2); 0420 0421 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 0)) { 0422 return; 0423 } 0424 0425 // Make the generator devices 0426 QVector<KisPaintDeviceSP> generatorDevices(channels.count()); 0427 for (int i = 0; i < channels.count(); ++i) { 0428 if (channels.at(i)->channelType() == KoChannelInfo::ALPHA) { 0429 generatorDevices[i] = nullptr; 0430 } else { 0431 const QString prefix = 0432 device->colorSpace()->colorModelId().id() + "_channel" + QString::number(i) + "_"; 0433 KisPaintDeviceSP generatorDevice = makeGeneratorPaintDevice(device, prefix, applyRect, config, nullptr); 0434 if (generatorDevice) { 0435 generatorDevices[i] = generatorDevice; 0436 } else { 0437 generatorDevices[i] = nullptr; 0438 } 0439 } 0440 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, progressUpdater->progress() + progressStep)) { 0441 return; 0442 } 0443 } 0444 0445 // process channels 0446 for (int i = 0; i < channels.count(); ++i) { 0447 if (!generatorDevices[i]) { 0448 progressUpdater->setProgress(progressUpdater->progress() + progressStep); 0449 continue; 0450 } 0451 0452 const QString prefix = device->colorSpace()->colorModelId().id() + "_channel" + QString::number(i) + "_"; 0453 0454 switch (channels.at(i)->channelValueType()) { 0455 case KoChannelInfo::UINT8: { 0456 processChannel<quint8>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i)); 0457 break; 0458 } case KoChannelInfo::UINT16: { 0459 processChannel<quint16>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i)); 0460 break; 0461 } case KoChannelInfo::UINT32: { 0462 processChannel<quint32>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i)); 0463 break; 0464 } case KoChannelInfo::FLOAT16: { 0465 #ifdef HAVE_OPENEXR 0466 processChannel<half>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i)); 0467 #endif 0468 break; 0469 } case KoChannelInfo::FLOAT32: { 0470 processChannel<float>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i)); 0471 break; 0472 } case KoChannelInfo::FLOAT64: { 0473 processChannel<double>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i)); 0474 break; 0475 } case KoChannelInfo::INT8: { 0476 processChannel<qint8>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i)); 0477 break; 0478 } case KoChannelInfo::INT16: { 0479 processChannel<qint16>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i)); 0480 break; 0481 } default: { 0482 break; 0483 } 0484 } 0485 0486 m_grayDevicesCache.putDevice(generatorDevices[i]); 0487 0488 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, progressUpdater->progress() + progressStep)) { 0489 return; 0490 } 0491 } 0492 0493 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 100)) { 0494 return; 0495 } 0496 } 0497 0498 void KisHalftoneFilter::processAlpha(KisPaintDeviceSP device, 0499 const QRect& applyRect, 0500 const KisHalftoneFilterConfiguration *config, 0501 KoUpdater *progressUpdater) const 0502 { 0503 const QString prefix = "alpha_"; 0504 0505 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 0)) { 0506 return; 0507 } 0508 0509 // Make the generator device 0510 KisPaintDeviceSP generatorDevice = makeGeneratorPaintDevice(device, prefix, applyRect, config, nullptr); 0511 if (!generatorDevice) { 0512 return; 0513 } 0514 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 50)) { 0515 return; 0516 } 0517 0518 // Make the hardness and the noise weight LUT 0519 const qreal hardness = config->hardness(prefix) / 100.0; 0520 const QVector<quint8> hardnessLut = makeHardnessLut(hardness); 0521 const QVector<quint8> noiseWeightLut = makeNoiseWeightLut(hardness); 0522 0523 // Fill the device 0524 const bool invert = config->invert(prefix); 0525 KisSequentialIterator dstIterator(device, applyRect); 0526 KisSequentialIterator srcIterator(generatorDevice, applyRect); 0527 0528 if (!invert) { 0529 while (dstIterator.nextPixel() && srcIterator.nextPixel()) { 0530 int dst = 255 - device->colorSpace()->opacityU8(dstIterator.rawData()); 0531 int src = srcIterator.rawData()[0]; 0532 int srcAlpha = srcIterator.rawData()[1]; 0533 0534 // Combine pixels 0535 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255); 0536 0537 // Apply hardness 0538 result = hardnessLut[result]; 0539 0540 device->colorSpace()->setOpacity(dstIterator.rawData(), static_cast<quint8>(255 - result), 1); 0541 } 0542 } else { 0543 while (dstIterator.nextPixel() && srcIterator.nextPixel()) { 0544 int dst = device->colorSpace()->opacityU8(dstIterator.rawData()); 0545 int src = srcIterator.rawData()[0]; 0546 int srcAlpha = srcIterator.rawData()[1]; 0547 0548 // Combine pixels 0549 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255); 0550 0551 // Apply hardness 0552 result = hardnessLut[result]; 0553 0554 device->colorSpace()->setOpacity(dstIterator.rawData(), static_cast<quint8>(result), 1); 0555 } 0556 } 0557 m_grayDevicesCache.putDevice(generatorDevice); 0558 0559 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 100)) { 0560 return; 0561 } 0562 } 0563 0564 void KisHalftoneFilter::processMask(KisPaintDeviceSP device, 0565 const QRect& applyRect, 0566 const KisHalftoneFilterConfiguration *config, 0567 KoUpdater *progressUpdater) const 0568 { 0569 const QString prefix = "alpha_"; 0570 0571 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 0)) { 0572 return; 0573 } 0574 0575 // Make the generator device 0576 KisPaintDeviceSP generatorDevice = makeGeneratorPaintDevice(device, prefix, applyRect, config, nullptr); 0577 if (!generatorDevice) { 0578 return; 0579 } 0580 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 50)) { 0581 return; 0582 } 0583 0584 // Make the hardness and the noise weight LUT 0585 const qreal hardness = config->hardness(prefix) / 100.0; 0586 const QVector<quint8> hardnessLut = makeHardnessLut(hardness); 0587 const QVector<quint8> noiseWeightLut = makeNoiseWeightLut(hardness); 0588 0589 // Fill the device 0590 const bool invert = config->invert(prefix); 0591 KisSequentialIterator dstIterator(device, applyRect); 0592 KisSequentialIterator srcIterator(generatorDevice, applyRect); 0593 0594 if (!invert) { 0595 while (dstIterator.nextPixel() && srcIterator.nextPixel()) { 0596 int dst = 255 - *dstIterator.rawData(); 0597 int src = *srcIterator.rawData(); 0598 0599 // Combine pixels 0600 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] / 0xFF, 255); 0601 0602 // Apply hardness 0603 result = hardnessLut[result]; 0604 0605 *dstIterator.rawData() = static_cast<quint8>(255 - result); 0606 } 0607 } else { 0608 while (dstIterator.nextPixel() && srcIterator.nextPixel()) { 0609 int dst = *dstIterator.rawData(); 0610 int src = *srcIterator.rawData(); 0611 0612 // Combine pixels 0613 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] / 0xFF, 255); 0614 0615 // Apply hardness 0616 result = hardnessLut[result]; 0617 0618 *dstIterator.rawData() = static_cast<quint8>(result); 0619 } 0620 } 0621 m_grayDevicesCache.putDevice(generatorDevice); 0622 0623 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 100)) { 0624 return; 0625 } 0626 } 0627 0628 KisFilterConfigurationSP KisHalftoneFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const 0629 { 0630 KisHalftoneFilterConfigurationSP filterConfig = 0631 dynamic_cast<KisHalftoneFilterConfiguration*>(factoryConfiguration(resourcesInterface).data()); 0632 0633 filterConfig->setMode(KisHalftoneFilterConfiguration::defaultMode()); 0634 0635 QString defaultGeneratorId = KisHalftoneFilterConfiguration::defaultGeneratorId(); 0636 KisGeneratorSP defaultGenerator = KisGeneratorRegistry::instance()->get(defaultGeneratorId); 0637 0638 // intensity 0639 filterConfig->setGeneratorId("intensity_", defaultGeneratorId); 0640 if (defaultGenerator) { 0641 KisFilterConfigurationSP defaultGeneratorConfiguration = 0642 defaultGenerator->defaultConfiguration(resourcesInterface); 0643 if (defaultGeneratorId == "screentone") { 0644 defaultGeneratorConfiguration->setProperty("rotation", 45.0); 0645 defaultGeneratorConfiguration->setProperty("contrast", 50.0); 0646 } 0647 filterConfig->setGeneratorConfiguration("intensity_", defaultGeneratorConfiguration); 0648 } 0649 filterConfig->setHardness("intensity_", KisHalftoneFilterConfiguration::defaultHardness()); 0650 filterConfig->setInvert("intensity_", KisHalftoneFilterConfiguration::defaultInvert()); 0651 filterConfig->setForegroundColor("intensity_", KisHalftoneFilterConfiguration::defaultForegroundColor()); 0652 filterConfig->setForegroundOpacity("intensity_", KisHalftoneFilterConfiguration::defaultForegroundOpacity()); 0653 filterConfig->setBackgroundColor("intensity_", KisHalftoneFilterConfiguration::defaultBackgroundColor()); 0654 filterConfig->setBackgroundOpacity("intensity_", KisHalftoneFilterConfiguration::defaultBackgroundOpacity()); 0655 0656 // Alpha 0657 filterConfig->setGeneratorId("alpha_", defaultGeneratorId); 0658 if (defaultGenerator) { 0659 KisFilterConfigurationSP defaultGeneratorConfiguration = 0660 defaultGenerator->defaultConfiguration(resourcesInterface); 0661 if (defaultGeneratorId == "screentone") { 0662 defaultGeneratorConfiguration->setProperty("rotation", 45.0); 0663 defaultGeneratorConfiguration->setProperty("contrast", 50.0); 0664 } 0665 filterConfig->setGeneratorConfiguration("alpha_", defaultGeneratorConfiguration); 0666 } 0667 filterConfig->setHardness("alpha_", KisHalftoneFilterConfiguration::defaultHardness()); 0668 filterConfig->setInvert("alpha_", KisHalftoneFilterConfiguration::defaultInvert()); 0669 0670 // The channels only use default generator if it is screentone 0671 // because there are predefined ways of presenting the patterns (screen angles) 0672 0673 // Map channel prefixes to rotation angle 0674 QHash<QString, qreal> channelDict; 0675 channelDict.insert("RGBA_channel0_", 15.0); 0676 channelDict.insert("RGBA_channel1_", 45.0); 0677 channelDict.insert("RGBA_channel2_", 75.0); 0678 channelDict.insert("CMYKA_channel0_", 15.0); 0679 channelDict.insert("CMYKA_channel1_", 75.0); 0680 channelDict.insert("CMYKA_channel2_", 0.0); 0681 channelDict.insert("CMYKA_channel3_", 45.0); 0682 0683 for (auto i = channelDict.constBegin(); i != channelDict.constEnd(); ++i) { 0684 if (defaultGenerator && defaultGeneratorId == "screentone") { 0685 filterConfig->setGeneratorId(i.key(), "screentone"); 0686 KisFilterConfigurationSP defaultGeneratorConfiguration = 0687 defaultGenerator->defaultConfiguration(resourcesInterface); 0688 defaultGeneratorConfiguration->setProperty("rotation", i.value()); 0689 defaultGeneratorConfiguration->setProperty("contrast", 50.0); 0690 filterConfig->setGeneratorConfiguration(i.key(), defaultGeneratorConfiguration); 0691 } else { 0692 filterConfig->setGeneratorId(i.key(), ""); 0693 } 0694 filterConfig->setHardness(i.key(), KisHalftoneFilterConfiguration::defaultHardness()); 0695 filterConfig->setInvert(i.key(), KisHalftoneFilterConfiguration::defaultInvert()); 0696 } 0697 0698 return filterConfig; 0699 } 0700 0701 KisFilterConfigurationSP KisHalftoneFilter::factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const 0702 { 0703 return new KisHalftoneFilterConfiguration("halftone", 1, resourcesInterface); 0704 } 0705 0706 KisConfigWidget *KisHalftoneFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const 0707 { 0708 Q_UNUSED(useForMasks); 0709 return new KisHalftoneConfigWidget(parent, dev); 0710 } 0711 0712 #include "KisHalftoneFilter.moc"