Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_preset_live_preview_view.cpp
Go to the documentation of this file.
1
2/*
3 * SPDX-FileCopyrightText: 2017 Scott Petrovic <scottpetrovic@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <QEvent>
9
11#include <QDebug>
12#include <QGraphicsPixmapItem>
17#include <kis_brush.h>
19#include "kis_transaction.h"
21
23 : QGraphicsView(parent),
24 m_updateCompressor(100, KisSignalCompressor::FIRST_ACTIVE)
25{
26 connect(&m_updateCompressor, SIGNAL(timeout()), SLOT(updateStroke()));
27}
28
34
36{
37 m_resourceManager = resourceManager;
38
39 // initializing to 0 helps check later if they actually have something in them
42
43 setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff );
44 setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff );
45
46
47 // layer image needs to be big enough to get an entire stroke of data
48 m_canvasSize.setWidth(this->width());
49 m_canvasSize.setHeight(this->height());
50
51 m_canvasCenterPoint.setX(m_canvasSize.width()*0.5);
52 m_canvasCenterPoint.setY(m_canvasSize.height()*0.5);
53
55 m_image = new KisImage(0, m_canvasSize.width(), m_canvasSize.height(), m_colorSpace, "stroke sample image");
56
57
58 m_layer = new KisPaintLayer(m_image, "livePreviewStrokeSample", OPACITY_OPAQUE_U8, m_colorSpace);
59
60 // set scene for the view
61 m_brushPreviewScene = new QGraphicsScene();
62 setScene(m_brushPreviewScene);
63
64}
65
70
75
77{
78 // do not paint a stroke if we are any of these engines (they have some issue currently)
79 if (m_currentPreset->paintOp().id() == "roundmarker" ||
80 m_currentPreset->paintOp().id() == "experimentbrush" ||
81 m_currentPreset->paintOp().id() == "duplicate") {
82
85 return;
86 }
87
91 } else {
93 }
94}
95
97{
99
100 QImage m_temp_image;
101 m_temp_image = m_layer->paintDevice()->convertToQImage(0, m_image->bounds());
102
103 // only add the object once...then just update the pixmap so we can move the preview around
104 if (!m_sceneImageItem) {
105 m_sceneImageItem = m_brushPreviewScene->addPixmap(QPixmap::fromImage(m_temp_image));
106 } else {
107 m_sceneImageItem->setPixmap(QPixmap::fromImage(m_temp_image));
108 }
109}
110
112{
113 // clean up "no preview" text object if it exists. we will add it later if we need it
114 if (m_noPreviewText) {
115 this->scene()->removeItem(m_noPreviewText);
116 m_noPreviewText = 0;
117 }
118
119
120 if (m_currentPreset->paintOp().id() == "colorsmudge" ||
121 m_currentPreset->paintOp().id() == "deformbrush" ||
122 m_currentPreset->paintOp().id() == "filter") {
123
124 // easier to see deformations and smudging with alternating stripes in the background
125 // paint the whole background with alternating stripes
126 // filter engine may or may not show things depending on the filter...but it is better than nothing
127
128 int grayStrips = 20;
129 for (int i=0; i < grayStrips; i++ ) {
130
131 float sectionPercent = 1.0 / (float)grayStrips;
132 bool isAlternating = i % 2;
133 KoColor fillColor(m_layer->paintDevice()->colorSpace());
134
135 if (isAlternating) {
136 fillColor.fromQColor(QColor(80,80,80));
137 } else {
138 fillColor.fromQColor(QColor(140,140,140));
139 }
140
141
142 const QRect fillRect(m_layer->image()->width()*sectionPercent*i,
143 0,
144 m_layer->image()->width()*(sectionPercent*i +sectionPercent),
145 m_layer->image()->height());
146
148 m_layer->paintDevice()->fill(fillRect, fillColor);
149 t.end();
150 }
151
152 m_paintColor = KoColor(Qt::white, m_colorSpace);
153
154 }
155 else if (m_currentPreset->paintOp().id() == "roundmarker" ||
156 m_currentPreset->paintOp().id() == "experimentbrush" ||
157 m_currentPreset->paintOp().id() == "duplicate" ) {
158
159 // cases where we will not show a preview for now
160 // roundbrush (quick) -- this isn't showing anything, disable showing preview
161 // experimentbrush -- this creates artifacts that carry over to other previews and messes up their display
162 // duplicate (clone) brush doesn't have a preview as it doesn't show anything)
163
164 // fill with gray first to clear out what existed from previous preview
166 m_layer->paintDevice()->fill(m_image->bounds(), KoColor(palette().color(QPalette::Window) , m_colorSpace));
167 t.end();
168
169 m_paintColor = KoColor(palette().color(QPalette::Text), m_colorSpace);
170
171 QFont font;
172 font.setPixelSize(14);
173 font.setBold(false);
174
175 m_noPreviewText = this->scene()->addText(i18n("No Preview for this engine"),font);
176 m_noPreviewText->setPos(50, this->height()/4);
177
178 return;
179
180 }
181 else {
182
183 // fill with gray first to clear out what existed from previous preview
185 m_layer->paintDevice()->fill(m_image->bounds(), KoColor(palette().color(QPalette::Window) , m_colorSpace));
186 t.end();
187
188 m_paintColor = KoColor(palette().color(QPalette::Text), m_colorSpace);
189 }
190}
191
192class NotificationStroke : public QObject, public KisSimpleStrokeStrategy
193{
194 Q_OBJECT
195public:
197 : KisSimpleStrokeStrategy(QLatin1String("NotificationStroke"))
198 {
202 }
203
204 void initStrokeCallback() override {
205 Q_EMIT timeout();
206 }
207
208 void cancelStrokeCallback() override {
209 Q_EMIT cancelled();
210 }
211
212Q_SIGNALS:
213 void timeout();
214 void cancelled();
215};
216
218{
219 // limit the brush stroke size. larger brush strokes just don't look good and are CPU intensive
220 // we are making a proxy preset and setting it to the painter...otherwise setting the brush size of the original preset
221 // will fire off signals that make this run in an infinite loop
222 qreal previewSize = qBound(3.0, m_currentPreset->settings()->paintOpSize(), 25.0 ); // constrain live preview brush size
223 //Except for the sketchbrush where it determines the history.
224 if (m_currentPreset->paintOp().id() == "sketchbrush" ||
225 m_currentPreset->paintOp().id() == "spraybrush") {
226 previewSize = qMax(3.0, m_currentPreset->settings()->paintOpSize());
227 }
228
229
230 KisPaintOpPresetSP proxy_preset = m_currentPreset->clone().dynamicCast<KisPaintOpPreset>();
231 KisPaintOpSettingsSP settings = proxy_preset->settings();
232 settings->setPaintOpSize(previewSize);
233
234 int maxTextureSize = 200;
235 int textureOffsetX = settings->getInt("Texture/Pattern/MaximumOffsetX")*2;
236 int textureOffsetY = settings->getInt("Texture/Pattern/MaximumOffsetY")*2;
237 double textureScale = settings->getDouble("Texture/Pattern/Scale");
238 if ( textureOffsetX*textureScale> maxTextureSize || textureOffsetY*textureScale > maxTextureSize) {
239 int maxSize = qMax(textureOffsetX, textureOffsetY);
240 double result = qreal(maxTextureSize) / maxSize;
241 settings->setProperty("Texture/Pattern/Scale", result);
242 }
243 if (proxy_preset->paintOp().id() == "spraybrush") {
244
245 QDomElement element;
246 QDomDocument d;
247 QString brushDefinition = settings->getString("brush_definition");
248 if (!brushDefinition.isEmpty()) {
249 d.setContent(brushDefinition, false);
250 element = d.firstChildElement("Brush");
251
253
254 qreal width = brush->image().width();
255 qreal scale = brush->scale();
256 qreal diameterToBrushRatio = 1.0;
257 qreal diameter = settings->getInt("Spray/diameter");
258 //hack, 1000 being the maximum possible brushsize.
259 if (brush->filename().endsWith(".svg", Qt::CaseInsensitive)) {
260 diameterToBrushRatio = diameter/(1000.0*scale);
261 scale = 25.0 / 1000.0;
262 } else {
263 if (width * scale > 25.0) {
264 diameterToBrushRatio = diameter / (width * scale);
265 scale = 25.0 / width;
266
267 if (!settings->getBool("SprayShape/proportional")) {
268 settings->setProperty("SprayShape/width", qRound(scale * settings->getInt("SprayShape/width")));
269 settings->setProperty("SprayShape/height", qRound(scale * settings->getInt("SprayShape/height")));
270 }
271 }
272 }
273 settings->setProperty("Spray/diameter", int(25.0 * diameterToBrushRatio));
274
275 brush->setScale(scale);
276 d.clear();
277 element = d.createElement("Brush");
278 brush->toXML(d, element);
279 d.appendChild(element);
280 settings->setProperty("brush_definition", d.toString());
281 }
282 }
283
284 proxy_preset->setSettings(settings);
285
286 KisResourcesSnapshotSP resources =
289 0, {},
290 proxy_preset);
291 resources->setOpacity(settings->paintOpOpacity());
292 resources->setMirroring(false, false); // ignore mirroring in toolbar
293
296
297 KisStrokeStrategy *stroke =
298 new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("temp_stroke"));
299
300 KisStrokeId strokeId = m_image->startStroke(stroke);
301
302 if (proxy_preset->paintOp().id() == "mypaintbrush") {
303
306 }
307
308 // paint the stroke. The sketchbrush gets a different shape than the others to show how it works
309 if (proxy_preset->paintOp().id() == "sketchbrush"
310 || proxy_preset->paintOp().id() == "curvebrush"
311 || proxy_preset->paintOp().id() == "particlebrush") {
312 qreal startX = m_canvasCenterPoint.x() - (this->width()*0.4);
313 qreal endX = m_canvasCenterPoint.x() + (this->width()*0.4);
314 qreal middle = m_canvasCenterPoint.y();
315 KisPaintInformation pointOne;
316 pointOne.setPressure(0.0);
317 pointOne.setPos(QPointF(startX, middle));
318 KisPaintInformation pointTwo;
319 pointTwo.setPressure(0.0);
320 pointTwo.setPos(QPointF(startX, middle));
321 int repeats = 8;
322
323 for (int i = 0; i < repeats; i++) {
324 pointOne.setPos(pointTwo.pos());
325 pointOne.setPressure(pointTwo.pressure());
326
327 pointTwo.setPressure((1.0/repeats)*(i+1));
328 qreal xPos = ((1.0/repeats) * (i+1) * (endX-startX) )+startX;
329 pointTwo.setPos(QPointF(xPos, middle));
330
331 qreal offset = (this->height()/(repeats*1.5))*(i+1);
332 qreal handleY = middle + offset;
333 if (i%2 == 0) {
334 handleY = middle - offset;
335 }
336
337 m_image->addJob(strokeId,
339 pointOne,
340 QPointF(pointOne.pos().x(),
341 handleY),
342 QPointF(pointTwo.pos().x(),
343 handleY),
344 pointTwo));
346 }
347
348 } else {
349
350 // paint an S curve
351 m_curvePointPI1.setPos(QPointF(m_canvasCenterPoint.x() - (this->width()*0.45),
352 m_canvasCenterPoint.y() + (this->height()*0.2)));
354
355
356 m_curvePointPI2.setPos(QPointF(m_canvasCenterPoint.x() + (this->width()*0.4),
357 m_canvasCenterPoint.y() - (this->height()*0.2) ));
358
360
361 m_image->addJob(strokeId,
364 QPointF(m_canvasCenterPoint.x(),
365 m_canvasCenterPoint.y()-this->height()),
366 QPointF(m_canvasCenterPoint.x(),
367 m_canvasCenterPoint.y()+this->height()),
370 }
371 m_image->endStroke(strokeId);
372
374
375 NotificationStroke *notificationStroke = new NotificationStroke();
376 connect(notificationStroke, SIGNAL(timeout()), SLOT(slotPreviewGenerationCompleted()));
377 KisStrokeId notificationId = m_image->startStroke(notificationStroke);
378 m_image->endStroke(notificationId);
379
380
381 // TODO: if we don't have any regressions because of it until 4.2.8, then
382 // just remove this code.
383 // even though the brush is cloned, the proxy_preset still has some connection to the original preset which will mess brush sizing
384 // we need to return brush size to normal.The normal brush sends out a lot of extra signals, so keeping the proxy for now
385 //proxy_preset->settings()->setPaintOpSize(originalPresetSize);
386
387}
388
390{
391 QWidget::changeEvent(event);
392 if (event->type() == QEvent::PaletteChange) {
393 if (m_currentPreset) {
395 }
396 }
397}
398
399#include "kis_preset_live_preview_view.moc"
const quint8 OPACITY_OPAQUE_U8
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
static KisBrushSP fromXML(const QDomElement &element, KisResourcesInterfaceSP resourcesInterface)
static KisResourcesInterfaceSP instance()
qint32 width() const
void addJob(KisStrokeId id, KisStrokeJobData *data) override
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override
qint32 height() const
QRect bounds() const override
void endStroke(KisStrokeId id) override
void fill(const QRect &rc, const KoColor &color)
const KoColorSpace * colorSpace() const
QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags()) const
void setPos(const QPointF &p)
const QPointF & pos() const
void setCurrentTime(qreal time) const
void setPressure(qreal p)
Set the pressure.
qreal pressure() const
The pressure of the value (from 0.0 to 1.0)
QGraphicsScene * m_brushPreviewScene
the scene that can add items like text and the brush stroke image
const KoColorSpace * m_colorSpace
internally sets the color space for brush preview
void setupAndPaintStroke()
creates and performs the actual stroke that goes on top of the background this is internally and shou...
KoCanvasResourceProvider * m_resourceManager
internally sets the Resource Provider for brush preview (allowing gradients in preview)
void setCurrentPreset(KisPaintOpPresetSP preset)
set the current preset from resource manager for the live preview to use. Good to call this every str...
QGraphicsTextItem * m_noPreviewText
holds the 'no preview available' text object
KisImageSP m_image
internally sets the image area for brush preview
void paintBackground()
works as both clearing the previous stroke, providing striped backgrounds for smudging brushes,...
QGraphicsPixmapItem * m_sceneImageItem
holds the preview brush stroke data
KisLayerSP m_layer
internally sets the layer area for brush preview
void setup(KoCanvasResourceProvider *resourceManager)
one time setup for initialization of many variables. This live preview might be in a UI file,...
KoColor m_paintColor
the color which is used for rendering the stroke
The KisResourcesSnapshot class takes a snapshot of the various resources like colors and settings use...
void setFGColorOverride(const KoColor &color)
void setMirroring(bool horizontal, bool vertical)
void enableJob(JobType type, bool enable=true, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void setClearsRedoOnStart(bool value)
void fromQColor(const QColor &c)
Convenient function for converting from a QColor.
Definition KoColor.cpp:213
KUndo2MagicString kundo2_noi18n(const QString &text)
rgba palette[MAX_PALETTE]
Definition palette.c:35
KisImageWSP image
virtual KisPaintDeviceSP paintDevice() const =0
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb8(const QString &profileName=QString())