Krita Source Code Documentation
Loading...
Searching...
No Matches
KisHalftoneFilter.cpp
Go to the documentation of this file.
1/*
2 * KDE. Krita Project.
3 *
4 * SPDX-FileCopyrightText: 2020 Deif Lou <ginoba@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include <QHash>
10
11#include <kpluginfactory.h>
12#include <kis_filter_registry.h>
14#include <KoUpdater.h>
21#include <kis_selection.h>
22#include <kis_painter.h>
26#include <KoColorProfile.h>
27
28#include "KisHalftoneFilter.h"
30
31K_PLUGIN_FACTORY_WITH_JSON(KritaHalftoneFactory, "KritaHalftone.json", registerPlugin<KritaHalftone>();)
32
33KritaHalftone::KritaHalftone(QObject *parent, const QVariantList &)
34 : QObject(parent)
35{
37}
38
41
47
49 const QRect &applyRect,
50 const KisFilterConfigurationSP config,
51 KoUpdater *progressUpdater) const
52{
53 const KisHalftoneFilterConfiguration *filterConfig =
54 dynamic_cast<const KisHalftoneFilterConfiguration*>(config.data());
55
56 Q_ASSERT(device);
59
60 const QString mode = filterConfig->mode();
65 );
66
68 (device->colorSpace()->colorModelId().id() == AlphaColorModelID.id() &&
70 (device->colorSpace()->colorModelId().id() == GrayColorModelID.id() &&
72 (device->colorSpace()->colorModelId().id() == GrayAColorModelID.id() &&
75 ((device->colorSpace()->colorModelId().id() == RGBAColorModelID.id() ||
76 device->colorSpace()->colorModelId().id() == XYZAColorModelID.id() ||
77 device->colorSpace()->colorModelId().id() == LABAColorModelID.id() ||
78 device->colorSpace()->colorModelId().id() == CMYKAColorModelID.id() ||
79 device->colorSpace()->colorModelId().id() == YCbCrAColorModelID.id()) &&
83 );
84
88 );
89 processIntensity(device, applyRect, filterConfig, progressUpdater);
92 device->colorSpace()->colorModelId().id() != AlphaColorModelID.id() &&
93 device->colorSpace()->colorModelId().id() != GrayColorModelID.id() &&
95 );
96 processChannels(device, applyRect, filterConfig, progressUpdater);
97 } else {
99 device->colorSpace()->colorModelId().id() != GrayColorModelID.id()
100 );
101 if (filterConfig->colorModelId() == AlphaColorModelID.id())
102 {
103 // This an Alpha layer (mask layer) but the device passed
104 // here has the composition color space (GrayA)
105 processMask(device, applyRect, filterConfig, progressUpdater);
106 } else {
107 // process the alpha channel of a multichannel device
108 processAlpha(device, applyRect, filterConfig, progressUpdater);
109 }
110 }
111}
112
114{
115 QVector<quint8> hardnessLut(256);
116 if (qFuzzyCompare(hardness, 1.0)) {
117 for (int i = 0; i < 256; ++i) {
118 hardnessLut[i] = i < 128 ? 0 : 255;
119 }
120 } else {
121 qreal m = 1.0 / (1.0 - hardness);
122 qreal b = -m * (hardness / 2.0);
123 for (int i = 0; i < 256; ++i) {
124 hardnessLut[i] = qBound(0, static_cast<int>(qRound((m * (i / 255.0) + b) * 255.0)), 255);
125 }
126 }
127 return hardnessLut;
128}
129
131{
132 QVector<quint8> noiseWeightLut(256);
133 hardness *= 0.99;
134 for (int i = 0; i < 256; ++i) {
135 qreal iNorm = i / 255.0;
136 qreal weight = (2.0 - std::abs(4.0 * iNorm - 2.0)) + hardness;
137 noiseWeightLut[i] = qBound(0, static_cast<int>(qRound(weight * 255.0)), 255);
138 }
139 return noiseWeightLut;
140}
141
143 const QString & prefix,
144 const QRect &applyRect,
145 const KisHalftoneFilterConfiguration *config,
146 KoUpdater *progressUpdater) const
147{
148 const QString generatorId = config->generatorId(prefix);
149 if (generatorId.isEmpty()) {
150 return nullptr;
151 }
152
153 KisGeneratorSP generator = KisGeneratorRegistry::instance()->get(generatorId);
154 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(generator, nullptr);
155
156 KisFilterConfigurationSP generatorConfiguration = config->generatorConfiguration(prefix);
157 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(generatorConfiguration, nullptr);
158
159 // Fill the generator device
160 KisPaintDeviceSP generatorDevice = m_grayDevicesCache.getDevice(prototype, KoColorSpaceRegistry::instance()->graya8());
161
162 KisProcessingInformation(generatorDevice, applyRect.topLeft(), KisSelectionSP());
163 generator->generate(
164 KisProcessingInformation(generatorDevice, applyRect.topLeft(),KisSelectionSP()),
165 applyRect.size(),
166 generatorConfiguration,
167 progressUpdater
168 );
169
170 return generatorDevice;
171}
172
174{
175 // The updater is null so return false to keep going
176 // with the computations
177 if (!progressUpdater) {
178 return false;
179 }
180
181 if (progressUpdater->interrupted()) {
182 return true;
183 }
184
185 progressUpdater->setProgress(percent);
186 return false;
187}
188
190 const QRect &applyRect,
191 const KisHalftoneFilterConfiguration *config,
192 KoUpdater *progressUpdater) const
193{
194 const QString prefix = "intensity_";
195
196 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 0)) {
197 return;
198 }
199
200 // Make the generator device
201 KisPaintDeviceSP generatorDevice = makeGeneratorPaintDevice(device, prefix, applyRect, config, nullptr);
202 if (!generatorDevice) {
203 return;
204 }
205 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 25)) {
206 return;
207 }
208
209 // Make the hardness and the noise weight LUT
210 const qreal hardness = config->hardness(prefix) / 100.0;
211 const QVector<quint8> hardnessLut = makeHardnessLut(hardness);
212 const QVector<quint8> noiseWeightLut = makeNoiseWeightLut(hardness);
213
214 // Fill the mask device
216
217 {
218 const bool invert = config->invert(prefix);
219
220 KisSequentialIterator maskIterator(maskDevice->pixelSelection(), applyRect);
221 KisSequentialIterator dstIterator(device, applyRect);
222 KisSequentialIterator srcIterator(generatorDevice, applyRect);
223
224 if (!invert) {
225 while (maskIterator.nextPixel() && dstIterator.nextPixel() && srcIterator.nextPixel()) {
226 int dstGray = device->colorSpace()->intensity8(dstIterator.rawData());
227 int srcGray = srcIterator.rawData()[0];
228 int srcAlpha = srcIterator.rawData()[1];
229
230 // Combine pixels
231 int result = qBound(0, dstGray + (srcGray - 128) * noiseWeightLut[dstGray] * srcAlpha / 0xFE01, 255);
232
233 // Apply hardness
234 result = hardnessLut[result];
235
236 *maskIterator.rawData() = 255 - result;
237 }
238 } else {
239 while (maskIterator.nextPixel() && dstIterator.nextPixel() && srcIterator.nextPixel()) {
240 int dstGray = device->colorSpace()->intensity8(dstIterator.rawData());
241 int srcGray = srcIterator.rawData()[0];
242 int srcAlpha = srcIterator.rawData()[1];
243
244 // Combine pixels
245 int result = qBound(0, dstGray + (srcGray - 128) * noiseWeightLut[dstGray] * srcAlpha / 0xFE01, 255);
246
247 // Apply hardness
248 result = hardnessLut[result];
249
250 *maskIterator.rawData() = result;
251 }
252 }
253 m_grayDevicesCache.putDevice(generatorDevice);
254 }
255 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 50)) {
256 return;
257 }
258
259 // Make the halftone image
260 const KoColorSpace *colorSpace;
261 if (device->colorSpace()->profile()->isLinear()) {
262 colorSpace = KoColorSpaceRegistry::instance()->rgb8();
263 } else {
264 colorSpace = device->colorSpace();
265 }
266 KisPaintDeviceSP halftoneDevice = m_genericDevicesCache.getDevice(device, colorSpace);
267
268 {
269 KisPaintDeviceSP foregroundDevice = m_genericDevicesCache.getDevice(device, colorSpace);
270 KoColor foregroundColor = config->foregroundColor(prefix);
271 KoColor backgroundColor = config->backgroundColor(prefix);
272 const qreal foregroundOpacity = config->foregroundOpacity(prefix) / 100.0;
273 const qreal backgroundOpacity = config->backgroundOpacity(prefix) / 100.0;
274 foregroundColor.convertTo(colorSpace);
275 backgroundColor.convertTo(colorSpace);
276 foregroundColor.setOpacity(foregroundOpacity);
277 backgroundColor.setOpacity(backgroundOpacity);
278
279 foregroundDevice->fill(applyRect, foregroundColor);
280 halftoneDevice->fill(applyRect, backgroundColor);
281
282 KisPainter painter(halftoneDevice, maskDevice);
284 painter.bitBlt(applyRect.topLeft(), foregroundDevice, applyRect);
285
286 m_genericDevicesCache.putDevice(foregroundDevice);
288 }
289 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 75)) {
290 return;
291 }
292
293 // Make the final image
294 {
295 KisPainter painter(halftoneDevice);
297 painter.bitBlt(applyRect.topLeft(), device, applyRect);
298 }
299 {
300 KisPainter painter(device);
302 painter.bitBlt(applyRect.topLeft(), halftoneDevice, applyRect);
303 }
304 m_genericDevicesCache.putDevice(halftoneDevice);
305
306 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 100)) {
307 return;
308 }
309}
310
311template <typename ChannelType>
313 KisPaintDeviceSP generatorDevice,
314 const QRect &applyRect,
315 const KisHalftoneFilterConfiguration *config,
316 const QString & prefix,
317 KoChannelInfo * channelInfo) const
318{
319 const int channelPos = channelInfo->pos() / sizeof(ChannelType);
320 // Make the hardness and the noise weight LUT
321 const qreal hardness = config->hardness(prefix) / 100.0;
322 const QVector<quint8> hardnessLut = makeHardnessLut(hardness);
323 const QVector<quint8> noiseWeightLut = makeNoiseWeightLut(hardness);
324
325 // Fill the device
326 const bool invert = config->invert(prefix);
327 KisSequentialIterator dstIterator(device, applyRect);
328 KisSequentialIterator srcIterator(generatorDevice, applyRect);
329
330 const KoColorSpace *colorSpace = device->colorSpace();
331
332 if (colorSpace->profile()->isLinear()) {
333 if (!invert) {
334 while (dstIterator.nextPixel() && srcIterator.nextPixel()) {
335 int dst = 255 - device->colorSpace()->scaleToU8(dstIterator.rawData(), channelPos);
336 int src = srcIterator.rawData()[0];
337 int srcAlpha = srcIterator.rawData()[1];
338 KoColor c(QColor(src, src, src, srcAlpha), device->colorSpace());
339 src = device->colorSpace()->scaleToU8(c.data(), 0);
340 srcAlpha = device->colorSpace()->scaleToU8(c.data(), device->colorSpace()->alphaPos());
341
342 // Combine pixels
343 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255);
344
345 // Apply hardness
346 result = hardnessLut[result];
347
348 ChannelType *dstPixel = reinterpret_cast<ChannelType*>(dstIterator.rawData());
349 ChannelType channelMin = static_cast<ChannelType>(channelInfo->getUIMin());
350 ChannelType channelMax = static_cast<ChannelType>(channelInfo->getUIMax());
351 dstPixel[channelPos] = static_cast<ChannelType>(mapU8ToRange(255 - result, channelMin, channelMax));
352 }
353 } else {
354 while (dstIterator.nextPixel() && srcIterator.nextPixel()) {
355 int dst = device->colorSpace()->scaleToU8(dstIterator.rawData(), channelPos);
356 int src = srcIterator.rawData()[0];
357 int srcAlpha = srcIterator.rawData()[1];
358 KoColor c(QColor(src, src, src, srcAlpha), device->colorSpace());
359 src = device->colorSpace()->scaleToU8(c.data(), 0);
360 srcAlpha = device->colorSpace()->scaleToU8(c.data(), device->colorSpace()->alphaPos());
361
362 // Combine pixels
363 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255);
364
365 // Apply hardness
366 result = hardnessLut[result];
367
368 ChannelType *dstPixel = reinterpret_cast<ChannelType*>(dstIterator.rawData());
369 ChannelType channelMin = static_cast<ChannelType>(channelInfo->getUIMin());
370 ChannelType channelMax = static_cast<ChannelType>(channelInfo->getUIMax());
371 dstPixel[channelPos] = static_cast<ChannelType>(mapU8ToRange(result, channelMin, channelMax));
372 }
373 }
374 } else {
375 if (!invert) {
376 while (dstIterator.nextPixel() && srcIterator.nextPixel()) {
377 int dst = 255 - device->colorSpace()->scaleToU8(dstIterator.rawData(), channelPos);
378 int src = srcIterator.rawData()[0];
379 int srcAlpha = srcIterator.rawData()[1];
380
381 // Combine pixels
382 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255);
383
384 // Apply hardness
385 result = hardnessLut[result];
386
387 ChannelType *dstPixel = reinterpret_cast<ChannelType*>(dstIterator.rawData());
388 ChannelType channelMin = static_cast<ChannelType>(channelInfo->getUIMin());
389 ChannelType channelMax = static_cast<ChannelType>(channelInfo->getUIMax());
390 dstPixel[channelPos] = static_cast<ChannelType>(mapU8ToRange(255 - result, channelMin, channelMax));
391 }
392 } else {
393 while (dstIterator.nextPixel() && srcIterator.nextPixel()) {
394 int dst = device->colorSpace()->scaleToU8(dstIterator.rawData(), channelPos);
395 int src = srcIterator.rawData()[0];
396 int srcAlpha = srcIterator.rawData()[1];
397
398 // Combine pixels
399 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255);
400
401 // Apply hardness
402 result = hardnessLut[result];
403
404 ChannelType *dstPixel = reinterpret_cast<ChannelType*>(dstIterator.rawData());
405 ChannelType channelMin = static_cast<ChannelType>(channelInfo->getUIMin());
406 ChannelType channelMax = static_cast<ChannelType>(channelInfo->getUIMax());
407 dstPixel[channelPos] = static_cast<ChannelType>(mapU8ToRange(result, channelMin, channelMax));
408 }
409 }
410 }
411}
412
414 const QRect &applyRect,
415 const KisHalftoneFilterConfiguration *config,
416 KoUpdater *progressUpdater) const
417{
418 const QList<KoChannelInfo *> channels = device->colorSpace()->channels();
419 const int progressStep = 100 / (channels.count() * 2);
420
421 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 0)) {
422 return;
423 }
424
425 // Make the generator devices
426 QVector<KisPaintDeviceSP> generatorDevices(channels.count());
427 for (int i = 0; i < channels.count(); ++i) {
428 if (channels.at(i)->channelType() == KoChannelInfo::ALPHA) {
429 generatorDevices[i] = nullptr;
430 } else {
431 const QString prefix =
432 device->colorSpace()->colorModelId().id() + "_channel" + QString::number(i) + "_";
433 KisPaintDeviceSP generatorDevice = makeGeneratorPaintDevice(device, prefix, applyRect, config, nullptr);
434 if (generatorDevice) {
435 generatorDevices[i] = generatorDevice;
436 } else {
437 generatorDevices[i] = nullptr;
438 }
439 }
440 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, progressUpdater->progress() + progressStep)) {
441 return;
442 }
443 }
444
445 // process channels
446 for (int i = 0; i < channels.count(); ++i) {
447 if (!generatorDevices[i]) {
448 progressUpdater->setProgress(progressUpdater->progress() + progressStep);
449 continue;
450 }
451
452 const QString prefix = device->colorSpace()->colorModelId().id() + "_channel" + QString::number(i) + "_";
453
454 switch (channels.at(i)->channelValueType()) {
456 processChannel<quint8>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i));
457 break;
458 } case KoChannelInfo::UINT16: {
459 processChannel<quint16>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i));
460 break;
461 } case KoChannelInfo::UINT32: {
462 processChannel<quint32>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i));
463 break;
464 } case KoChannelInfo::FLOAT16: {
465#ifdef HAVE_OPENEXR
466 processChannel<half>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i));
467#endif
468 break;
469 } case KoChannelInfo::FLOAT32: {
470 processChannel<float>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i));
471 break;
472 } case KoChannelInfo::FLOAT64: {
473 processChannel<double>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i));
474 break;
475 } case KoChannelInfo::INT8: {
476 processChannel<qint8>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i));
477 break;
478 } case KoChannelInfo::INT16: {
479 processChannel<qint16>(device, generatorDevices[i], applyRect, config, prefix, channels.at(i));
480 break;
481 } default: {
482 break;
483 }
484 }
485
486 m_grayDevicesCache.putDevice(generatorDevices[i]);
487
488 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, progressUpdater->progress() + progressStep)) {
489 return;
490 }
491 }
492
493 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 100)) {
494 return;
495 }
496}
497
499 const QRect& applyRect,
500 const KisHalftoneFilterConfiguration *config,
501 KoUpdater *progressUpdater) const
502{
503 const QString prefix = "alpha_";
504
505 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 0)) {
506 return;
507 }
508
509 // Make the generator device
510 KisPaintDeviceSP generatorDevice = makeGeneratorPaintDevice(device, prefix, applyRect, config, nullptr);
511 if (!generatorDevice) {
512 return;
513 }
514 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 50)) {
515 return;
516 }
517
518 // Make the hardness and the noise weight LUT
519 const qreal hardness = config->hardness(prefix) / 100.0;
520 const QVector<quint8> hardnessLut = makeHardnessLut(hardness);
521 const QVector<quint8> noiseWeightLut = makeNoiseWeightLut(hardness);
522
523 // Fill the device
524 const bool invert = config->invert(prefix);
525 KisSequentialIterator dstIterator(device, applyRect);
526 KisSequentialIterator srcIterator(generatorDevice, applyRect);
527
528 if (!invert) {
529 while (dstIterator.nextPixel() && srcIterator.nextPixel()) {
530 int dst = 255 - device->colorSpace()->opacityU8(dstIterator.rawData());
531 int src = srcIterator.rawData()[0];
532 int srcAlpha = srcIterator.rawData()[1];
533
534 // Combine pixels
535 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255);
536
537 // Apply hardness
538 result = hardnessLut[result];
539
540 device->colorSpace()->setOpacity(dstIterator.rawData(), static_cast<quint8>(255 - result), 1);
541 }
542 } else {
543 while (dstIterator.nextPixel() && srcIterator.nextPixel()) {
544 int dst = device->colorSpace()->opacityU8(dstIterator.rawData());
545 int src = srcIterator.rawData()[0];
546 int srcAlpha = srcIterator.rawData()[1];
547
548 // Combine pixels
549 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] * srcAlpha / 0xFE01, 255);
550
551 // Apply hardness
552 result = hardnessLut[result];
553
554 device->colorSpace()->setOpacity(dstIterator.rawData(), static_cast<quint8>(result), 1);
555 }
556 }
557 m_grayDevicesCache.putDevice(generatorDevice);
558
559 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 100)) {
560 return;
561 }
562}
563
565 const QRect& applyRect,
566 const KisHalftoneFilterConfiguration *config,
567 KoUpdater *progressUpdater) const
568{
569 const QString prefix = "alpha_";
570
571 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 0)) {
572 return;
573 }
574
575 // Make the generator device
576 KisPaintDeviceSP generatorDevice = makeGeneratorPaintDevice(device, prefix, applyRect, config, nullptr);
577 if (!generatorDevice) {
578 return;
579 }
580 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 50)) {
581 return;
582 }
583
584 // Make the hardness and the noise weight LUT
585 const qreal hardness = config->hardness(prefix) / 100.0;
586 const QVector<quint8> hardnessLut = makeHardnessLut(hardness);
587 const QVector<quint8> noiseWeightLut = makeNoiseWeightLut(hardness);
588
589 // Fill the device
590 const bool invert = config->invert(prefix);
591 KisSequentialIterator dstIterator(device, applyRect);
592 KisSequentialIterator srcIterator(generatorDevice, applyRect);
593
594 if (!invert) {
595 while (dstIterator.nextPixel() && srcIterator.nextPixel()) {
596 int dst = 255 - *dstIterator.rawData();
597 int src = *srcIterator.rawData();
598
599 // Combine pixels
600 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] / 0xFF, 255);
601
602 // Apply hardness
603 result = hardnessLut[result];
604
605 *dstIterator.rawData() = static_cast<quint8>(255 - result);
606 }
607 } else {
608 while (dstIterator.nextPixel() && srcIterator.nextPixel()) {
609 int dst = *dstIterator.rawData();
610 int src = *srcIterator.rawData();
611
612 // Combine pixels
613 int result = qBound(0, dst + (src - 128) * noiseWeightLut[dst] / 0xFF, 255);
614
615 // Apply hardness
616 result = hardnessLut[result];
617
618 *dstIterator.rawData() = static_cast<quint8>(result);
619 }
620 }
621 m_grayDevicesCache.putDevice(generatorDevice);
622
623 if (checkUpdaterInterruptedAndSetPercent(progressUpdater, 100)) {
624 return;
625 }
626}
627
629{
631 dynamic_cast<KisHalftoneFilterConfiguration*>(factoryConfiguration(resourcesInterface).data());
632
633 filterConfig->setMode(KisHalftoneFilterConfiguration::defaultMode());
634
635 QString defaultGeneratorId = KisHalftoneFilterConfiguration::defaultGeneratorId();
636 KisGeneratorSP defaultGenerator = KisGeneratorRegistry::instance()->get(defaultGeneratorId);
637
638 // intensity
639 filterConfig->setGeneratorId("intensity_", defaultGeneratorId);
640 if (defaultGenerator) {
641 KisFilterConfigurationSP defaultGeneratorConfiguration =
642 defaultGenerator->defaultConfiguration(resourcesInterface);
643 if (defaultGeneratorId == "screentone") {
644 defaultGeneratorConfiguration->setProperty("rotation", 45.0);
645 defaultGeneratorConfiguration->setProperty("contrast", 50.0);
646 }
647 filterConfig->setGeneratorConfiguration("intensity_", defaultGeneratorConfiguration);
648 }
649 filterConfig->setHardness("intensity_", KisHalftoneFilterConfiguration::defaultHardness());
650 filterConfig->setInvert("intensity_", KisHalftoneFilterConfiguration::defaultInvert());
651 filterConfig->setForegroundColor("intensity_", KisHalftoneFilterConfiguration::defaultForegroundColor());
652 filterConfig->setForegroundOpacity("intensity_", KisHalftoneFilterConfiguration::defaultForegroundOpacity());
653 filterConfig->setBackgroundColor("intensity_", KisHalftoneFilterConfiguration::defaultBackgroundColor());
654 filterConfig->setBackgroundOpacity("intensity_", KisHalftoneFilterConfiguration::defaultBackgroundOpacity());
655
656 // Alpha
657 filterConfig->setGeneratorId("alpha_", defaultGeneratorId);
658 if (defaultGenerator) {
659 KisFilterConfigurationSP defaultGeneratorConfiguration =
660 defaultGenerator->defaultConfiguration(resourcesInterface);
661 if (defaultGeneratorId == "screentone") {
662 defaultGeneratorConfiguration->setProperty("rotation", 45.0);
663 defaultGeneratorConfiguration->setProperty("contrast", 50.0);
664 }
665 filterConfig->setGeneratorConfiguration("alpha_", defaultGeneratorConfiguration);
666 }
667 filterConfig->setHardness("alpha_", KisHalftoneFilterConfiguration::defaultHardness());
668 filterConfig->setInvert("alpha_", KisHalftoneFilterConfiguration::defaultInvert());
669
670 // The channels only use default generator if it is screentone
671 // because there are predefined ways of presenting the patterns (screen angles)
672
673 // Map channel prefixes to rotation angle
674 QHash<QString, qreal> channelDict;
675 channelDict.insert("RGBA_channel0_", 15.0);
676 channelDict.insert("RGBA_channel1_", 45.0);
677 channelDict.insert("RGBA_channel2_", 75.0);
678 channelDict.insert("CMYKA_channel0_", 15.0);
679 channelDict.insert("CMYKA_channel1_", 75.0);
680 channelDict.insert("CMYKA_channel2_", 0.0);
681 channelDict.insert("CMYKA_channel3_", 45.0);
682
683 for (auto i = channelDict.constBegin(); i != channelDict.constEnd(); ++i) {
684 if (defaultGenerator && defaultGeneratorId == "screentone") {
685 filterConfig->setGeneratorId(i.key(), "screentone");
686 KisFilterConfigurationSP defaultGeneratorConfiguration =
687 defaultGenerator->defaultConfiguration(resourcesInterface);
688 defaultGeneratorConfiguration->setProperty("rotation", i.value());
689 defaultGeneratorConfiguration->setProperty("contrast", 50.0);
690 filterConfig->setGeneratorConfiguration(i.key(), defaultGeneratorConfiguration);
691 } else {
692 filterConfig->setGeneratorId(i.key(), "");
693 }
694 filterConfig->setHardness(i.key(), KisHalftoneFilterConfiguration::defaultHardness());
695 filterConfig->setInvert(i.key(), KisHalftoneFilterConfiguration::defaultInvert());
696 }
697
698 return filterConfig;
699}
700
702{
703 return new KisHalftoneFilterConfiguration("halftone", 1, resourcesInterface);
704}
705
706KisConfigWidget *KisHalftoneFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const
707{
708 Q_UNUSED(useForMasks);
709 return new KisHalftoneConfigWidget(parent, dev);
710}
711
712#include "KisHalftoneFilter.moc"
const KoID YCbCrAColorModelID("YCbCrA", ki18n("YCbCr/Alpha"))
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID XYZAColorModelID("XYZA", ki18n("XYZ/Alpha"))
const KoID AlphaColorModelID("A", ki18n("Alpha mask"))
const KoID CMYKAColorModelID("CMYKA", ki18n("CMYK/Alpha"))
const KoID LABAColorModelID("LABA", ki18n("L*a*b*/Alpha"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
const KoID GrayColorModelID("GRAY", ki18n("Grayscale (without transparency)"))
const QString COMPOSITE_OVER
const QString COMPOSITE_COPY
const QString COMPOSITE_DESTINATION_IN
KisPaintDeviceSP getDevice(KisPaintDeviceSP prototype)
void putDevice(KisPaintDeviceSP device)
void putSelection(KisSelectionSP selection)
KisSelectionSP getSelection()
void add(KisFilterSP item)
static KisFilterRegistry * instance()
static KisGeneratorRegistry * instance()
virtual void generate(KisProcessingInformation dst, const QSize &size, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const =0
static constexpr const char * HalftoneMode_Alpha
static constexpr const char * HalftoneMode_IndependentChannels
static constexpr const char * HalftoneMode_Intensity
KoColor backgroundColor(const QString &prefix) const
QString generatorId(const QString &prefix) const
int backgroundOpacity(const QString &prefix) const
int foregroundOpacity(const QString &prefix) const
KisFilterConfigurationSP generatorConfiguration(const QString &prefix) const
qreal hardness(const QString &prefix) const
KoColor foregroundColor(const QString &prefix) const
bool invert(const QString &prefix) const
void processIntensity(KisPaintDeviceSP device, const QRect &applyRect, const KisHalftoneFilterConfiguration *config, KoUpdater *progressUpdater) const
KisCachedPaintDevice m_grayDevicesCache
static QVector< quint8 > makeHardnessLut(qreal hardness)
void processImpl(KisPaintDeviceSP device, const QRect &applyRect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const override
KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const override
void processChannels(KisPaintDeviceSP device, const QRect &applyRect, const KisHalftoneFilterConfiguration *config, KoUpdater *progressUpdater) const
KisCachedPaintDevice m_genericDevicesCache
KisCachedSelection m_selectionsCache
void processMask(KisPaintDeviceSP device, const QRect &applyRect, const KisHalftoneFilterConfiguration *config, KoUpdater *progressUpdater) const
void processChannel(KisPaintDeviceSP device, KisPaintDeviceSP generatorDevice, const QRect &applyRect, const KisHalftoneFilterConfiguration *config, const QString &prefix, KoChannelInfo *channelInfo) const
void processAlpha(KisPaintDeviceSP device, const QRect &applyRect, const KisHalftoneFilterConfiguration *config, KoUpdater *progressUpdater) const
KisConfigWidget * createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const override
static quint8 mapU8ToRange(quint8 value, quint8 new_min, quint8 new_max)
KisPaintDeviceSP makeGeneratorPaintDevice(KisPaintDeviceSP prototype, const QString &prefix, const QRect &applyRect, const KisHalftoneFilterConfiguration *config, KoUpdater *progressUpdater) const
bool checkUpdaterInterruptedAndSetPercent(KoUpdater *progressUpdater, int percent) const
static QVector< quint8 > makeNoiseWeightLut(qreal hardness)
KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override
void fill(const QRect &rc, const KoColor &color)
const KoColorSpace * colorSpace() const
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
void setCompositeOpId(const KoCompositeOp *op)
ALWAYS_INLINE quint8 * rawData()
@ ALPHA
The channel represents the opacity of a pixel.
qint32 pos() const
@ UINT8
use this for an unsigned integer 8bits channel
@ UINT16
use this for an integer 16bits channel
@ INT16
use this for an integer 16bits channel
@ INT8
use this for an integer 8bits channel
@ FLOAT32
use this for a float 32bits channel
@ FLOAT16
use this for a float 16bits channel
@ UINT32
use this for an unsigned integer 21bits channel
@ FLOAT64
use this for a float 64bits channel
double getUIMin(void) const
double getUIMax(void) const
virtual quint32 alphaPos() const =0
virtual quint8 intensity8(const quint8 *src) const =0
virtual void setOpacity(quint8 *pixels, quint8 alpha, qint32 nPixels) const =0
QList< KoChannelInfo * > channels
virtual quint8 scaleToU8(const quint8 *srcPixel, qint32 channelPos) const =0
virtual KoID colorModelId() const =0
virtual quint8 opacityU8(const quint8 *pixel) const =0
virtual const KoColorProfile * profile() const =0
void convertTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
Definition KoColor.cpp:136
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
quint8 * data()
Definition KoColor.h:144
T get(const QString &id) const
QString id() const
Definition KoID.cpp:63
bool interrupted() const
Definition KoUpdater.cpp:54
int progress() const
Definition KoUpdater.cpp:48
void setProgress(int percent)
Definition KoUpdater.cpp:38
~KritaHalftone() override
KritaHalftone(QObject *parent, const QVariantList &)
static bool qFuzzyCompare(half p1, half p2)
K_PLUGIN_FACTORY_WITH_JSON(KritaASCCDLFactory, "kritaasccdl.json", registerPlugin< KritaASCCDL >();) KritaASCCDL
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
const KoID FiltersCategoryArtisticId("artistic_filters", ki18nc("The category of artistic filters, like raindrops. Adjective.", "Artistic"))
virtual KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
void setSupportsPainting(bool v)
KisPixelSelectionSP pixelSelection
virtual bool isLinear() const =0
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb8(const QString &profileName=QString())