Krita Source Code Documentation
Loading...
Searching...
No Matches
highpass.py
Go to the documentation of this file.
1# SPDX-License-Identifier: CC0-1.0
2
3try:
4 from PyQt6.QtCore import Qt
5 from PyQt6.QtWidgets import (
6 QCheckBox,
7 QComboBox,
8 QDialog,
9 QDialogButtonBox,
10 QFormLayout,
11 QMessageBox,
12 QSpinBox,
13 QVBoxLayout,
14 )
15except:
16 from PyQt5.QtCore import Qt
17 from PyQt5.QtWidgets import (
18 QCheckBox,
19 QComboBox,
20 QDialog,
21 QDialogButtonBox,
22 QFormLayout,
23 QMessageBox,
24 QSpinBox,
25 QVBoxLayout,
26 )
27from krita import Extension
28from builtins import i18n, i18nc, Application
29
30
31class HighpassExtension(Extension):
32
33 def __init__(self, parent):
34 super().__init__(parent)
35
36 def setup(self):
37 pass
38
39 def createActions(self, window):
40 action = window.createAction("high_pass_filter", i18n("High Pass"))
41 action.triggered.connect(self.showDialogshowDialog)
42
43 def showDialog(self):
44 doc = Application.activeDocument()
45 if doc is None:
46 QMessageBox.information(
47 Application.activeWindow().qwindow(),
48 i18n("High Pass Filter"),
49 i18n("There is no active image."))
50 return
51
52 self.dialog = QDialog(Application.activeWindow().qwindow())
53
54 self.intRadius = QSpinBox()
55 self.intRadius.setValue(10)
56 self.intRadius.setRange(2, 200)
57
58 self.cmbMode = QComboBox()
59 self.cmbMode.addItems(
60 ["Color", "Preserve DC", "Greyscale",
61 "Greyscale, Apply Chroma", "Redrobes"]
62 )
63 self.keepOriginal = QCheckBox(i18n("Keep original layer"))
64 self.keepOriginal.setChecked(True)
65 form = QFormLayout()
66 form.addRow(i18nc("Filter radius in Highpass filter settings", "Filter radius:"), self.intRadius)
67 form.addRow(i18n("Mode:"), self.cmbMode)
68 form.addRow("", self.keepOriginal)
69
70 self.buttonBox = QDialogButtonBox(self.dialog)
71 self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
72 self.buttonBox.setStandardButtons(
73 QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
74 self.buttonBox.accepted.connect(self.dialog.accept)
75 self.buttonBox.accepted.connect(self.highpasshighpass)
76 self.buttonBox.rejected.connect(self.dialog.reject)
77
78 vbox = QVBoxLayout(self.dialog)
79 vbox.addLayout(form)
80 vbox.addWidget(self.buttonBox)
81
82 self.dialog.show()
83 self.dialog.activateWindow()
84 self.dialog.exec()
85
86 def highpass(self):
87 # XXX: Start undo macro
88 image = Application.activeDocument()
89 original = image.activeNode()
90 working_layer = original
91
92 # We can only highpass on paint layers
93 if self.keepOriginal.isChecked() or original.type() != "paintlayer":
94 working_layer = image.createNode("working", "paintlayer")
95 working_layer.setColorSpace(
96 original.colorModel(),
97 original.colorSpace(),
98 original.profile())
99 working_layer.writeBytes(
100 original.readBytes(0, 0, image.width(), image.height()),
101 0, 0, image.width(), image.height())
102
103 # XXX: Unimplemented:
104 original.parentNode().addChildNode(working_layer, original)
105
106 image.setActiveNode(working_layer)
107 colors_layer = None
108
109 # if keeping colors
110 if (self.cmbMode.currentIndex() == 1
111 or self.cmbMode.currentIndex() == 3):
112 # XXX: Unimplemented:
113 colors_layer = working_layer.duplicate()
114 colors_layer.setName("colors")
115 # XXX: Unimplemented:
116 original.parentNode().addChildNode(working_layer, colors_layer)
117
118 # if greyscale, desature
119 if (self.cmbMode.currentIndex() == 2
120 or self.cmbMode.currentIndex() == 3):
121 filter = Application.filter("desaturate")
122 filter.apply(working_layer, 0, 0, image.width(), image.height())
123
124 # Duplicate on top and blur
125 blur_layer = working_layer.duplicate()
126 blur_layer.setName("blur")
127 # XXX: Unimplemented:
128 original.parentNode().addChildNode(blur_layer, working_layer)
129
130 # blur
131 filter = Application.filter("gaussian blur")
132 filter_configuration = filter.configuration()
133 filter_configuration.setProperty("horizRadius", self.intRadius.value())
134 filter_configuration.setProperty("vertRadius", self.intRadius.value())
135 filter_configuration.setProperty("lockAspect", True)
136 filter.setConfiguration(filter_configuration)
137 filter.apply(blur_layer, 0, 0, image.width(), image.height())
138
139 if self.cmbMode.currentIndex() <= 3:
140 blur_layer.setBlendingMode("grain_extract")
141 working_layer = image.mergeDown(blur_layer)
142
143 # if preserve chroma, change set the mode to value and
144 # merge down with the layer we kept earlier.
145 if self.cmbMode.currentIndex() == 3:
146 working_layer.setBlendingMode("value")
147 working_layer = image.mergeDown(working_layer)
148
149 # if preserve DC, change set the mode to overlay and merge
150 # down with the average color of the layer we kept
151 # earlier.
152 if self.cmbMode.currentIndex() == 1:
153 # get the average color of the entire image
154 # clear the colors layer to the given color
155 working_layer = image.mergeDown(working_layer)
156
157 else: # Mode == 4, RedRobes
158 image.setActiveNode(blur_layer)
159 # Get the average color of the input layer
160 # copy the solid color layer
161 # copy the blurred layer
162 # XXX: End undo macro
float value(const T *src, size_t ch)