Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_predefined_brush_factory.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QApplication>
10#include <QThread>
11#include <QDomDocument>
12#include "kis_gbr_brush.h"
13#include "kis_png_brush.h"
14#include "kis_abr_brush.h"
15#include "kis_svg_brush.h"
16#include <kis_dom_utils.h>
18#include "kis_imagepipe_brush.h"
19
21 : m_id(brushType)
22{
23}
24
26{
27 return m_id;
28}
29
31{
32 auto resourceSourceAdapter = resourcesInterface->source<KisBrush>(ResourceType::Brushes);
33 const QString brushFileName = brushData.predefinedBrush.resourceSignature.filename;
34 const QString brushMD5Sum = brushData.predefinedBrush.resourceSignature.md5sum;
35 KisBrushSP brush = resourceSourceAdapter.bestMatch(brushMD5Sum, brushFileName, "");
36 if (!brush) {
37 return KoResourceSignature(ResourceType::Brushes, brushMD5Sum, brushFileName, "");
38 }
39
40 // we always return a copy of the brush!
41 brush = brush->clone().dynamicCast<KisBrush>();
42
43 brush->setSpacing(brushData.common.spacing);
44 brush->setAutoSpacing(brushData.common.useAutoSpacing, brushData.common.autoSpacingCoeff);
45 brush->setAngle(brushData.common.angle);
46 brush->setScale(brushData.predefinedBrush.scale);
47
48 KisColorfulBrush *colorfulBrush = dynamic_cast<KisColorfulBrush*>(brush.data());
49 if (colorfulBrush) {
54 }
55
56 brush->setBrushApplication(brushData.predefinedBrush.application);
57
58 return brush;
59}
60
61KoResourceLoadResult KisPredefinedBrushFactory::createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface)
62{
63 auto data = createBrushModelImpl(brushDefinition, resourcesInterface);
64
65 if (std::holds_alternative<KisBrushModel::BrushData>(data)) {
66 return createBrush(std::get<KisBrushModel::BrushData>(data), resourcesInterface);
67 } else if (std::holds_alternative<KoResourceSignature>(data)) {
68 return std::get<KoResourceSignature>(data);
69 }
70
71 // fallback, should never reach!
73}
74
75std::optional<KisBrushModel::BrushData> KisPredefinedBrushFactory::createBrushModel(const QDomElement &element, KisResourcesInterfaceSP resourcesInterface)
76{
77 auto data = createBrushModelImpl(element, resourcesInterface);
78 return std::holds_alternative<KisBrushModel::BrushData>(data) ?
79 std::make_optional(std::get<KisBrushModel::BrushData>(data)) :
80 std::nullopt;
81}
82
83std::variant<KisBrushModel::BrushData, KoResourceSignature> KisPredefinedBrushFactory::createBrushModelImpl(const QDomElement &element, KisResourcesInterfaceSP resourcesInterface)
84{
86
87 auto resourceSourceAdapter = resourcesInterface->source<KisBrush>(ResourceType::Brushes);
88 const QString brushFileName = element.attribute("filename", "");
89 const QString brushMD5Sum = element.attribute("md5sum", "");
90 KisBrushSP brushResource = resourceSourceAdapter.bestMatch(brushMD5Sum, brushFileName, "");
91 if (!brushResource) {
92 return KoResourceSignature(ResourceType::Brushes, brushMD5Sum, brushFileName, "");
93 }
94
96
99 loadFromBrushResource(brush.common, brush.predefinedBrush, brushResource);
100
101 brush.common.angle = KisDomUtils::toDouble(element.attribute("angle", "0.0"));
102 brush.common.spacing = KisDomUtils::toDouble(element.attribute("spacing", "0.25"));
103 brush.common.useAutoSpacing = KisDomUtils::toInt(element.attribute("useAutoSpacing", "0"));
104 brush.common.autoSpacingCoeff = KisDomUtils::toDouble(element.attribute("autoSpacingCoeff", "1.0"));
105
106 brush.predefinedBrush.scale = KisDomUtils::toDouble(element.attribute("scale", "1.0"));
107
108 // legacy support...
109 if (element.attribute("BrushVersion", "1") == "1") {
110 brush.predefinedBrush.scale *= 2.0;
111 }
112
113 KisColorfulBrush *colorfulBrush = dynamic_cast<KisColorfulBrush*>(brushResource.data());
114 if (colorfulBrush) {
115 quint8 adjustmentMidPoint = element.attribute("AdjustmentMidPoint", "127").toInt();
116 qreal brightnessAdjustment = element.attribute("BrightnessAdjustment").toDouble();
117 qreal contrastAdjustment = element.attribute("ContrastAdjustment").toDouble();
118
119 const int adjustmentVersion = element.attribute("AdjustmentVersion", "1").toInt();
120 const bool autoAdjustMidPoint = element.attribute("AutoAdjustMidPoint", "0").toInt();
121 const bool hasAutoAdjustMidPoint = element.hasAttribute("AutoAdjustMidPoint");
122
138 if (adjustmentVersion < 2 && !hasAutoAdjustMidPoint) {
139 adjustmentMidPoint = qBound(0, 127 + (int(adjustmentMidPoint) - 127) * 2, 255);
140 brightnessAdjustment *= 2.0;
141 contrastAdjustment *= 2.0;
142
147 if (contrastAdjustment < 0) {
148 contrastAdjustment = 1.0 / (1.0 - contrastAdjustment) - 1.0;
149 }
150 }
151
152 brush.predefinedBrush.adjustmentMidPoint = adjustmentMidPoint;
153 brush.predefinedBrush.brightnessAdjustment = brightnessAdjustment;
154 brush.predefinedBrush.contrastAdjustment = contrastAdjustment;
155 brush.predefinedBrush.autoAdjustMidPoint = autoAdjustMidPoint;
157 }
158
159 auto legacyBrushApplication = [] (KisColorfulBrush *colorfulBrush, bool forceColorToAlpha) {
166 return (colorfulBrush && colorfulBrush->hasColorAndTransparency() && !forceColorToAlpha) ? IMAGESTAMP : ALPHAMASK;
167 };
168
169
170 if (element.hasAttribute("preserveLightness")) {
171 const int preserveLightness = KisDomUtils::toInt(element.attribute("preserveLightness", "0"));
172 const bool useColorAsMask = (bool)element.attribute("ColorAsMask", "1").toInt();
173 brush.predefinedBrush.application = preserveLightness ? LIGHTNESSMAP : legacyBrushApplication(colorfulBrush, useColorAsMask);
174 }
175 else if (element.hasAttribute("brushApplication")) {
176 enumBrushApplication brushApplication = static_cast<enumBrushApplication>(KisDomUtils::toInt(element.attribute("brushApplication", "0")));
177 brush.predefinedBrush.application = brushApplication;
178 }
179 else if (element.hasAttribute("ColorAsMask")) {
180 KIS_SAFE_ASSERT_RECOVER_NOOP(colorfulBrush);
181
182 const bool useColorAsMask = (bool)element.attribute("ColorAsMask", "1").toInt();
183 brush.predefinedBrush.application =legacyBrushApplication(colorfulBrush, useColorAsMask);
184 }
185 else {
191 brush.predefinedBrush.application = legacyBrushApplication(colorfulBrush, false);
192 }
193
194 return {brush};
195}
196
198{
199 commonData.spacing = brushResource->spacing();
200 commonData.useAutoSpacing = brushResource->autoSpacingActive();
201 commonData.autoSpacingCoeff = brushResource->autoSpacingCoeff();
202 predefinedBrushData.resourceSignature = brushResource->signature();
203 predefinedBrushData.brushType = brushResource->brushType();
204 predefinedBrushData.baseSize = { brushResource->width(), brushResource->height() };
205
206 if (brushResource.dynamicCast<KisGbrBrush>()) {
207 predefinedBrushData.subtype = "gbr_brush";
208 } else if (brushResource.dynamicCast<KisAbrBrush>()) {
209 predefinedBrushData.subtype = "abr_brush";
210 } else if (brushResource.dynamicCast<KisPngBrush>()) {
211 predefinedBrushData.subtype = "png_brush";
212 } else if (brushResource.dynamicCast<KisSvgBrush>()) {
213 predefinedBrushData.subtype = "svg_brush";
214 } else {
215 KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "unknown brush type");
216 }
217
218 KisImagePipeBrushSP imagepipeBrush = brushResource.dynamicCast<KisImagePipeBrush>();
219 if (imagepipeBrush) {
220 predefinedBrushData.parasiteSelection = imagepipeBrush->parasiteSelection();
221 }
222}
223
224void KisPredefinedBrushFactory::toXML(QDomDocument &doc, QDomElement &e, const KisBrushModel::BrushData &model)
225{
226 Q_UNUSED(doc);
227
228 e.setAttribute("type", id());
229 e.setAttribute("BrushVersion", "2");
230
231 e.setAttribute("filename", model.predefinedBrush.resourceSignature.filename);
232 e.setAttribute("md5sum", model.predefinedBrush.resourceSignature.md5sum);
233 e.setAttribute("spacing", QString::number(model.common.spacing));
234 e.setAttribute("useAutoSpacing", QString::number(model.common.useAutoSpacing));
235 e.setAttribute("autoSpacingCoeff", QString::number(model.common.autoSpacingCoeff));
236 e.setAttribute("angle", QString::number(model.common.angle));
237 e.setAttribute("scale", QString::number(model.predefinedBrush.scale));
238 e.setAttribute("brushApplication", QString::number((int)model.predefinedBrush.application));
239
240 if (id() == "abr_brush") {
241 e.setAttribute("name", model.predefinedBrush.resourceSignature.name);
242
243 } else {
244 // all other brushes are derived from KisColorfulBrush
245
246 // legacy setting, now 'brushApplication' is used instead
247 e.setAttribute("ColorAsMask", QString::number((int)(model.predefinedBrush.application != IMAGESTAMP)));
248
249 e.setAttribute("AdjustmentMidPoint", QString::number(model.predefinedBrush.adjustmentMidPoint));
250 e.setAttribute("BrightnessAdjustment", QString::number(model.predefinedBrush.brightnessAdjustment));
251 e.setAttribute("ContrastAdjustment", QString::number(model.predefinedBrush.contrastAdjustment));
252 e.setAttribute("AutoAdjustMidPoint", QString::number(model.predefinedBrush.autoAdjustMidPoint));
253 e.setAttribute("AdjustmentVersion", QString::number(2));
254 }
255}
virtual void setAdjustmentMidPoint(quint8 value)
bool hasColorAndTransparency() const
virtual void setContrastAdjustment(qreal value)
virtual void setBrightnessAdjustment(qreal value)
virtual void setAutoAdjustMidPoint(bool autoAdjustMidPoint)
static void loadFromBrushResource(KisBrushModel::CommonData &commonData, KisBrushModel::PredefinedBrushData &predefinedBrushData, KisBrushSP brushResource)
void toXML(QDomDocument &doc, QDomElement &element, const KisBrushModel::BrushData &model) override
std::variant< KisBrushModel::BrushData, KoResourceSignature > createBrushModelImpl(const QDomElement &element, KisResourcesInterfaceSP resourcesInterface)
KisPredefinedBrushFactory(const QString &brushType)
std::optional< KisBrushModel::BrushData > createBrushModel(const QDomElement &element, KisResourcesInterfaceSP resourcesInterface) override
KoResourceLoadResult createBrush(const KisBrushModel::BrushData &brushData, KisResourcesInterfaceSP resourcesInterface) override
A simple wrapper object for the main information about the resource.
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
enumBrushApplication
Definition kis_brush.h:38
@ ALPHAMASK
Definition kis_brush.h:39
@ IMAGESTAMP
Definition kis_brush.h:40
@ LIGHTNESSMAP
Definition kis_brush.h:41
double toDouble(const QString &str, bool *ok=nullptr)
int toInt(const QString &str, bool *ok=nullptr)
const QString Brushes
PredefinedBrushData predefinedBrush
KoResourceSignature resourceSignature