Changeset View
Changeset View
Standalone View
Standalone View
libs/ui/tool/kis_tool_utils.cpp
Context not available. | |||||
20 | #include <kis_tool_utils.h> | 20 | #include <kis_tool_utils.h> | ||
---|---|---|---|---|---|
21 | 21 | | |||
22 | #include <KoMixColorsOp.h> | 22 | #include <KoMixColorsOp.h> | ||
23 | #include <kis_canvas2.h> | ||||
24 | #include <KisDocument.h> | ||||
23 | #include <kis_group_layer.h> | 25 | #include <kis_group_layer.h> | ||
24 | #include <kis_transaction.h> | 26 | #include <kis_transaction.h> | ||
25 | #include <kis_sequential_iterator.h> | 27 | #include <kis_sequential_iterator.h> | ||
26 | #include <kis_properties_configuration.h> | 28 | #include <kis_properties_configuration.h> | ||
27 | #include <kconfiggroup.h> | 29 | #include <kconfiggroup.h> | ||
28 | #include <ksharedconfig.h> | 30 | #include <ksharedconfig.h> | ||
31 | #include <KoCanvasResourceProvider.h> | ||||
32 | #include <KisReferenceImagesLayer.h> | ||||
29 | 33 | | |||
30 | namespace KisToolUtils { | 34 | namespace KisToolUtils { | ||
31 | | ||||
32 | bool pickColor(KoColor &out_color, KisPaintDeviceSP dev, const QPoint &pos, | | |||
33 | KoColor const *const blendColor, int radius, int blend, bool pure) | | |||
34 | { | | |||
35 | KIS_ASSERT(dev); | | |||
36 | | ||||
37 | // Bugfix hack forcing pure on first sample to avoid wrong | | |||
38 | // format blendColor on newly initialized Krita. | | |||
39 | static bool firstTime = true; | | |||
40 | if (firstTime == true) { | | |||
41 | pure = true; | | |||
42 | firstTime = false; | | |||
43 | } | | |||
44 | | ||||
45 | const KoColorSpace *cs = dev->colorSpace(); | | |||
46 | KoColor pickedColor(Qt::transparent, cs); | | |||
47 | | ||||
48 | // Sampling radius. | | |||
49 | if (!pure && radius > 1) { | | |||
50 | QVector<const quint8*> pixels; | | |||
51 | const int effectiveRadius = radius - 1; | | |||
52 | | ||||
53 | const QRect pickRect(pos.x() - effectiveRadius, pos.y() - effectiveRadius, | | |||
54 | 2 * effectiveRadius + 1, 2 * effectiveRadius + 1); | | |||
55 | KisSequentialConstIterator it(dev, pickRect); | | |||
56 | | ||||
57 | const int radiusSq = pow2(effectiveRadius); | | |||
58 | | ||||
59 | while (it.nextPixel()) { | | |||
60 | const QPoint realPos(it.x(), it.y()); | | |||
61 | const QPoint pt = realPos - pos; | | |||
62 | if (pow2(pt.x()) + pow2(pt.y()) < radiusSq) { | | |||
63 | pixels << it.oldRawData(); | | |||
64 | } | | |||
65 | } | | |||
66 | | ||||
67 | const quint8 **cpixels = const_cast<const quint8**>(pixels.constData()); | | |||
68 | cs->mixColorsOp()->mixColors(cpixels, pixels.size(), pickedColor.data()); | | |||
69 | } else { | | |||
70 | dev->pixel(pos.x(), pos.y(), &pickedColor); | | |||
71 | } | | |||
72 | | ||||
73 | // Color blending. | | |||
74 | if (!pure && blendColor && blend < 100) { | | |||
75 | //Scale from 0..100% to 0..255 range for mixOp weights. | | |||
76 | quint8 blendScaled = static_cast<quint8>(blend * 2.55f); | | |||
77 | | ||||
78 | const quint8 *colors[2]; | | |||
79 | colors[0] = blendColor->data(); | | |||
80 | colors[1] = pickedColor.data(); | | |||
81 | qint16 weights[2]; | | |||
82 | weights[0] = 255 - blendScaled; | | |||
83 | weights[1] = blendScaled; | | |||
84 | | ||||
85 | const KoMixColorsOp *mixOp = dev->colorSpace()->mixColorsOp(); | | |||
86 | mixOp->mixColors(colors, weights, 2, pickedColor.data()); | | |||
87 | } | | |||
88 | | ||||
89 | pickedColor.convertTo(dev->compositionSourceColorSpace()); | | |||
90 | | ||||
91 | bool validColorPicked = pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8; | | |||
92 | | ||||
93 | if (validColorPicked) { | | |||
94 | out_color = pickedColor; | | |||
95 | } | | |||
96 | | ||||
97 | return validColorPicked; | | |||
98 | } | | |||
99 | | ||||
100 | KisNodeSP findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly) | 35 | KisNodeSP findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly) | ||
101 | { | 36 | { | ||
102 | KisNodeSP foundNode = 0; | 37 | KisNodeSP foundNode = 0; | ||
Context not available. | |||||
157 | } | 92 | } | ||
158 | return false; | 93 | return false; | ||
159 | } | 94 | } | ||
95 | } | ||||
160 | 96 | | |||
161 | const QString ColorPickerConfig::CONFIG_GROUP_NAME = "tool_color_picker"; | 97 | //============================================================================= | ||
162 | 98 | | |||
163 | ColorPickerConfig::ColorPickerConfig() | 99 | namespace KisPickerUtils { // Color picker utility functions. | ||
164 | : toForegroundColor(true) | 100 | | ||
165 | , updateColor(true) | 101 | bool pickColor(KoColor &out_color, KisPaintDeviceSP sourceDevice, KisCanvas2 *canvas, | ||
166 | , addPalette(false) | 102 | const QPoint &pos, KoColor const *const mixColor, int radius, int mix, bool pure) | ||
167 | , normaliseValues(false) | 103 | { | ||
168 | , sampleMerged(true) | 104 | KIS_ASSERT(sourceDevice); | ||
169 | , radius(1) | 105 | | ||
170 | , blend(100) | 106 | // Bugfix hack forcing pure on first sample to avoid wrong | ||
171 | { | 107 | // format mixColor on newly initialized Krita. | ||
108 | static bool firstTime = true; | ||||
109 | if (firstTime == true) { | ||||
110 | pure = true; | ||||
111 | firstTime = false; | ||||
112 | } | ||||
113 | | ||||
114 | KoColor pickedColor(Qt::transparent, sourceDevice->colorSpace()); | ||||
115 | | ||||
116 | if (!pure && radius > 1) { | ||||
117 | if (canvas && !canvas->image()->bounds().contains(pos)) { | ||||
118 | //TODO: Implement proper radius sampling for reference images! | ||||
119 | KisSharedPtr<KisReferenceImagesLayer> referenceImages = canvas->imageView()->document()->referenceImagesLayer(); | ||||
120 | if (referenceImages && canvas->referenceImagesDecoration()->visible()) { | ||||
121 | pickedColor.fromQColor(referenceImages->getPixel(pos)); | ||||
122 | } | ||||
123 | } else { | ||||
124 | sampleRadius(pickedColor, sourceDevice, pos, radius); | ||||
125 | } | ||||
126 | } else { | ||||
127 | if (canvas && !canvas->image()->bounds().contains(pos)) { | ||||
128 | KisSharedPtr<KisReferenceImagesLayer> referenceImages = canvas->imageView()->document()->referenceImagesLayer(); | ||||
129 | if (referenceImages && canvas->referenceImagesDecoration()->visible()) { | ||||
130 | pickedColor.fromQColor(referenceImages->getPixel(pos)); | ||||
131 | } | ||||
132 | } else { | ||||
133 | sourceDevice->pixel(pos.x(), pos.y(), &pickedColor); | ||||
134 | } | ||||
172 | } | 135 | } | ||
173 | 136 | | |||
174 | inline QString getConfigKey(bool defaultActivation) { | 137 | // Color picker mixing. | ||
175 | return defaultActivation ? | 138 | if (!pure && mixColor && mix < 100) { | ||
176 | "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation"; | 139 | //Scale from 0..100% to 0..255 range for mixOp weights. | ||
140 | quint8 scaledMix = static_cast<quint8>(mix * 2.55f); | ||||
141 | | ||||
142 | const quint8 *colors[2]; | ||||
143 | colors[0] = mixColor->data(); | ||||
144 | colors[1] = pickedColor.data(); | ||||
145 | qint16 weights[2]; | ||||
146 | weights[0] = 255 - scaledMix; | ||||
147 | weights[1] = scaledMix; | ||||
148 | | ||||
149 | const KoMixColorsOp *mixOp = sourceDevice->colorSpace()->mixColorsOp(); | ||||
150 | mixOp->mixColors(colors, weights, 2, pickedColor.data()); | ||||
177 | } | 151 | } | ||
178 | 152 | | |||
179 | void ColorPickerConfig::save(bool defaultActivation) const | 153 | pickedColor.convertTo(sourceDevice->compositionSourceColorSpace()); | ||
180 | { | 154 | | ||
181 | KisPropertiesConfiguration props; | 155 | bool validColorPicked = pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8; | ||
182 | props.setProperty("toForegroundColor", toForegroundColor); | 156 | | ||
183 | props.setProperty("updateColor", updateColor); | 157 | if (validColorPicked) { | ||
184 | props.setProperty("addPalette", addPalette); | 158 | out_color = pickedColor; | ||
185 | props.setProperty("normaliseValues", normaliseValues); | | |||
186 | props.setProperty("sampleMerged", sampleMerged); | | |||
187 | props.setProperty("radius", radius); | | |||
188 | props.setProperty("blend", blend); | | |||
189 | | ||||
190 | KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); | | |||
191 | | ||||
192 | config.writeEntry(getConfigKey(defaultActivation), props.toXML()); | | |||
193 | } | 159 | } | ||
194 | 160 | | |||
195 | void ColorPickerConfig::load(bool defaultActivation) | 161 | return validColorPicked; | ||
196 | { | 162 | } | ||
197 | KisPropertiesConfiguration props; | 163 | | ||
198 | 164 | void sampleRadius(KoColor &out_color, const KisPaintDeviceSP device, const QPoint position, const int radius) { | |||
199 | KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); | 165 | QVector<const quint8*> pixels; | ||
200 | props.fromXML(config.readEntry(getConfigKey(defaultActivation))); | 166 | const int effectiveRadius = radius - 1; | ||
201 | 167 | | |||
202 | toForegroundColor = props.getBool("toForegroundColor", true); | 168 | const QRect pickRect(position.x() - effectiveRadius, position.y() - effectiveRadius, | ||
203 | updateColor = props.getBool("updateColor", true); | 169 | 2 * effectiveRadius + 1, 2 * effectiveRadius + 1); | ||
204 | addPalette = props.getBool("addPalette", false); | 170 | | ||
205 | normaliseValues = props.getBool("normaliseValues", false); | 171 | KisSequentialConstIterator it(device, pickRect); | ||
206 | sampleMerged = props.getBool("sampleMerged", !defaultActivation ? false : true); | 172 | | ||
207 | radius = props.getInt("radius", 1); | 173 | const int radiusSq = pow2(effectiveRadius); | ||
208 | blend = props.getInt("blend", 100); | 174 | | ||
175 | while (it.nextPixel()) { | ||||
176 | const QPoint realPos(it.x(), it.y()); | ||||
177 | const QPoint pt = realPos - position; | ||||
178 | if (pow2(pt.x()) + pow2(pt.y()) < radiusSq) { | ||||
179 | pixels << it.oldRawData(); | ||||
180 | } | ||||
181 | } | ||||
182 | | ||||
183 | const quint8 **cpixels = const_cast<const quint8**>(pixels.constData()); | ||||
184 | device->colorSpace()->mixColorsOp()->mixColors(cpixels, pixels.size(), out_color.data()); | ||||
185 | } | ||||
186 | | ||||
187 | void submitColor(KoCanvasResourceProvider &resourceProvider, UpdateTarget target, KoColor &color) | ||||
188 | { | ||||
189 | color.setOpacity(OPACITY_OPAQUE_U8); //Strip unwanted alpha component. | ||||
190 | | ||||
191 | switch (target) { | ||||
192 | case FOREGROUND: | ||||
193 | resourceProvider.setForegroundColor(color); | ||||
194 | break; | ||||
195 | case BACKGROUND: | ||||
196 | resourceProvider.setBackgroundColor(color); | ||||
197 | break; | ||||
198 | case BOTH: | ||||
199 | resourceProvider.setForegroundColor(color); | ||||
200 | resourceProvider.setBackgroundColor(color); | ||||
201 | break; | ||||
202 | case SHIFT: | ||||
203 | KoColor hold = resourceProvider.foregroundColor(); | ||||
204 | resourceProvider.setForegroundColor(color); | ||||
205 | resourceProvider.setBackgroundColor(hold); | ||||
206 | break; | ||||
209 | } | 207 | } | ||
210 | } | 208 | } | ||
209 | | ||||
210 | const QString ColorPickerConfig::CONFIG_GROUP_NAME = "tool_color_picker"; | ||||
211 | | ||||
212 | ColorPickerConfig::ColorPickerConfig() | ||||
213 | : source(MERGED_VISIBLE) | ||||
214 | , resampleRateMS(200) | ||||
215 | , update(true) | ||||
216 | , radius(1) | ||||
217 | , mix(100) | ||||
218 | , addToPalette(false) | ||||
219 | , snapToPalette(false) | ||||
220 | , normalizeColorData(false) | ||||
221 | { | ||||
222 | } | ||||
223 | | ||||
224 | inline QString getConfigKey(bool defaultActivation) { | ||||
225 | return defaultActivation ? | ||||
226 | "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation"; | ||||
227 | } | ||||
228 | | ||||
229 | void ColorPickerConfig::save(bool defaultActivation) const | ||||
230 | { | ||||
231 | KisPropertiesConfiguration props; | ||||
232 | props.setProperty("update", update); | ||||
233 | props.setProperty("samplingSource", source); | ||||
234 | props.setProperty("radius", radius); | ||||
235 | props.setProperty("mix", mix); | ||||
236 | props.setProperty("addPalette", addToPalette); | ||||
237 | props.setProperty("snapToPalette", snapToPalette); | ||||
238 | props.setProperty("normaliseValues", normalizeColorData); | ||||
239 | | ||||
240 | KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); | ||||
241 | | ||||
242 | config.writeEntry(getConfigKey(defaultActivation), props.toXML()); | ||||
243 | } | ||||
244 | | ||||
245 | void ColorPickerConfig::load(bool defaultActivation) | ||||
246 | { | ||||
247 | KisPropertiesConfiguration props; | ||||
248 | | ||||
249 | KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); | ||||
250 | props.fromXML(config.readEntry(getConfigKey(defaultActivation))); | ||||
251 | | ||||
252 | update = props.getBool("update"); | ||||
253 | source = static_cast<SamplingSource>(props.getInt("samplingSource")); | ||||
254 | radius = props.getInt("radius", 1); | ||||
255 | mix = props.getInt("mix", 100); | ||||
256 | addToPalette = props.getBool("addPalette"); | ||||
257 | snapToPalette = props.getBool("snapToPalette"); | ||||
258 | normalizeColorData = props.getBool("normaliseValues"); | ||||
259 | } | ||||
260 | } | ||||
Context not available. |