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; | ||||
172 | } | 112 | } | ||
173 | 113 | | |||
174 | inline QString getConfigKey(bool defaultActivation) { | 114 | KoColor pickedColor(Qt::transparent, sourceDevice->colorSpace()); | ||
175 | return defaultActivation ? | 115 | | ||
176 | "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation"; | 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 | } | ||||
177 | } | 135 | } | ||
178 | 136 | | |||
179 | void ColorPickerConfig::save(bool defaultActivation) const | 137 | // Color picker mixing. | ||
180 | { | 138 | if (!pure && mixColor && mix < 100) { | ||
181 | KisPropertiesConfiguration props; | 139 | //Scale from 0..100% to 0..255 range for mixOp weights. | ||
182 | props.setProperty("toForegroundColor", toForegroundColor); | 140 | quint8 scaledMix = static_cast<quint8>(mix * 2.55f); | ||
183 | props.setProperty("updateColor", updateColor); | 141 | | ||
184 | props.setProperty("addPalette", addPalette); | 142 | const quint8 *colors[2]; | ||
185 | props.setProperty("normaliseValues", normaliseValues); | 143 | colors[0] = mixColor->data(); | ||
186 | props.setProperty("sampleMerged", sampleMerged); | 144 | colors[1] = pickedColor.data(); | ||
187 | props.setProperty("radius", radius); | 145 | qint16 weights[2]; | ||
188 | props.setProperty("blend", blend); | 146 | weights[0] = 255 - scaledMix; | ||
189 | 147 | weights[1] = scaledMix; | |||
190 | KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); | 148 | | ||
191 | 149 | const KoMixColorsOp *mixOp = sourceDevice->colorSpace()->mixColorsOp(); | |||
192 | config.writeEntry(getConfigKey(defaultActivation), props.toXML()); | 150 | mixOp->mixColors(colors, weights, 2, pickedColor.data()); | ||
193 | } | 151 | } | ||
194 | 152 | | |||
195 | void ColorPickerConfig::load(bool defaultActivation) | 153 | pickedColor.convertTo(sourceDevice->compositionSourceColorSpace()); | ||
196 | { | 154 | | ||
197 | KisPropertiesConfiguration props; | 155 | bool validColorPicked = pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8; | ||
198 | 156 | | |||
199 | KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); | 157 | if (validColorPicked) { | ||
200 | props.fromXML(config.readEntry(getConfigKey(defaultActivation))); | 158 | out_color = pickedColor; | ||
201 | 159 | } | |||
202 | toForegroundColor = props.getBool("toForegroundColor", true); | 160 | | ||
203 | updateColor = props.getBool("updateColor", true); | 161 | return validColorPicked; | ||
204 | addPalette = props.getBool("addPalette", false); | 162 | } | ||
205 | normaliseValues = props.getBool("normaliseValues", false); | 163 | | ||
206 | sampleMerged = props.getBool("sampleMerged", !defaultActivation ? false : true); | 164 | void sampleRadius(KoColor &out_color, const KisPaintDeviceSP device, const QPoint position, const int radius) { | ||
207 | radius = props.getInt("radius", 1); | 165 | QVector<const quint8*> pixels; | ||
208 | blend = props.getInt("blend", 100); | 166 | const int effectiveRadius = radius - 1; | ||
167 | | ||||
168 | const QRect pickRect(position.x() - effectiveRadius, position.y() - effectiveRadius, | ||||
169 | 2 * effectiveRadius + 1, 2 * effectiveRadius + 1); | ||||
170 | | ||||
171 | KisSequentialConstIterator it(device, pickRect); | ||||
172 | | ||||
173 | const int radiusSq = pow2(effectiveRadius); | ||||
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 | , normalizeColorData(false) | ||||
219 | { | ||||
220 | } | ||||
221 | | ||||
222 | inline QString getConfigKey(bool defaultActivation) { | ||||
223 | return defaultActivation ? | ||||
224 | "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation"; | ||||
225 | } | ||||
226 | | ||||
227 | void ColorPickerConfig::save(bool defaultActivation) const | ||||
228 | { | ||||
229 | KisPropertiesConfiguration props; | ||||
230 | props.setProperty("update", update); | ||||
231 | props.setProperty("samplingSource", source); | ||||
232 | props.setProperty("radius", radius); | ||||
233 | props.setProperty("mix", mix); | ||||
234 | props.setProperty("normaliseValues", normalizeColorData); | ||||
235 | | ||||
236 | KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); | ||||
237 | | ||||
238 | config.writeEntry(getConfigKey(defaultActivation), props.toXML()); | ||||
239 | } | ||||
240 | | ||||
241 | void ColorPickerConfig::load(bool defaultActivation) | ||||
242 | { | ||||
243 | KisPropertiesConfiguration props; | ||||
244 | | ||||
245 | KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); | ||||
246 | props.fromXML(config.readEntry(getConfigKey(defaultActivation))); | ||||
247 | | ||||
248 | update = props.getBool("update"); | ||||
249 | source = static_cast<SamplingSource>(props.getInt("samplingSource")); | ||||
250 | radius = props.getInt("radius", 1); | ||||
251 | mix = props.getInt("mix", 100); | ||||
252 | normalizeColorData = props.getBool("normaliseValues"); | ||||
253 | } | ||||
254 | } | ||||
Context not available. |