1 /*
2  *  Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "freehand_stroke.h"
20 
21 #include <QElapsedTimer>
22 
23 #include "kis_canvas_resource_provider.h"
24 #include <brushengine/kis_paintop_preset.h>
25 #include <brushengine/kis_paintop_settings.h>
26 #include "kis_painter.h"
27 #include "kis_paintop.h"
28 
29 #include "kis_update_time_monitor.h"
30 
31 #include <brushengine/kis_stroke_random_source.h>
32 #include <KisRunnableStrokeJobsInterface.h>
33 #include "FreehandStrokeRunnableJobDataWithUpdate.h"
34 #include <mutex>
35 
36 #include "KisStrokeEfficiencyMeasurer.h"
37 #include <KisStrokeSpeedMonitor.h>
38 #include <strokes/KisFreehandStrokeInfo.h>
39 #include <strokes/KisMaskedFreehandStrokePainter.h>
40 
41 #include "brushengine/kis_paintop_utils.h"
42 #include "KisAsyncronousStrokeUpdateHelper.h"
43 
44 struct FreehandStrokeStrategy::Private
45 {
PrivateFreehandStrokeStrategy::Private46     Private(KisResourcesSnapshotSP _resources)
47         : resources(_resources),
48           needsAsynchronousUpdates(_resources->presetNeedsAsynchronousUpdates())
49     {
50         if (needsAsynchronousUpdates) {
51             timeSinceLastUpdate.start();
52         }
53     }
54 
PrivateFreehandStrokeStrategy::Private55     Private(const Private &rhs)
56         : randomSource(rhs.randomSource),
57           resources(rhs.resources),
58           needsAsynchronousUpdates(rhs.needsAsynchronousUpdates)
59     {
60         if (needsAsynchronousUpdates) {
61             timeSinceLastUpdate.start();
62         }
63     }
64 
65     KisStrokeRandomSource randomSource;
66     KisResourcesSnapshotSP resources;
67 
68     KisStrokeEfficiencyMeasurer efficiencyMeasurer;
69 
70     QElapsedTimer timeSinceLastUpdate;
71     int currentUpdatePeriod = 40;
72 
73     const bool needsAsynchronousUpdates = false;
74     std::mutex updateEntryMutex;
75 };
76 
FreehandStrokeStrategy(KisResourcesSnapshotSP resources,KisFreehandStrokeInfo * strokeInfo,const KUndo2MagicString & name)77 FreehandStrokeStrategy::FreehandStrokeStrategy(KisResourcesSnapshotSP resources,
78                                                KisFreehandStrokeInfo *strokeInfo,
79                                                const KUndo2MagicString &name)
80     : KisPainterBasedStrokeStrategy(QLatin1String("FREEHAND_STROKE"), name,
81                                     resources, strokeInfo),
82       m_d(new Private(resources))
83 {
84     init();
85 }
86 
FreehandStrokeStrategy(KisResourcesSnapshotSP resources,QVector<KisFreehandStrokeInfo * > strokeInfos,const KUndo2MagicString & name)87 FreehandStrokeStrategy::FreehandStrokeStrategy(KisResourcesSnapshotSP resources,
88                                                QVector<KisFreehandStrokeInfo*> strokeInfos,
89                                                const KUndo2MagicString &name)
90     : KisPainterBasedStrokeStrategy(QLatin1String("FREEHAND_STROKE"), name,
91                                     resources, strokeInfos),
92       m_d(new Private(resources))
93 {
94     init();
95 }
96 
FreehandStrokeStrategy(const FreehandStrokeStrategy & rhs,int levelOfDetail)97 FreehandStrokeStrategy::FreehandStrokeStrategy(const FreehandStrokeStrategy &rhs, int levelOfDetail)
98     : KisPainterBasedStrokeStrategy(rhs, levelOfDetail),
99       m_d(new Private(*rhs.m_d))
100 {
101     m_d->randomSource.setLevelOfDetail(levelOfDetail);
102 }
103 
~FreehandStrokeStrategy()104 FreehandStrokeStrategy::~FreehandStrokeStrategy()
105 {
106     KisStrokeSpeedMonitor::instance()->notifyStrokeFinished(m_d->efficiencyMeasurer.averageCursorSpeed(),
107                                                             m_d->efficiencyMeasurer.averageRenderingSpeed(),
108                                                             m_d->efficiencyMeasurer.averageFps(),
109                                                             m_d->resources->currentPaintOpPreset());
110 
111     KisUpdateTimeMonitor::instance()->endStrokeMeasure();
112 }
113 
init()114 void FreehandStrokeStrategy::init()
115 {
116     setSupportsWrapAroundMode(true);
117     setSupportsMaskingBrush(true);
118     setSupportsIndirectPainting(true);
119     enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
120 
121     if (m_d->needsAsynchronousUpdates) {
122         /**
123          * In case the paintop uses asynchronous updates, we should set priority to it,
124          * because FPS is controlled separately, not by the queue's merging algorithm.
125          */
126         setBalancingRatioOverride(0.01); // set priority to updates
127     }
128 
129     KisUpdateTimeMonitor::instance()->startStrokeMeasure();
130     m_d->efficiencyMeasurer.setEnabled(KisStrokeSpeedMonitor::instance()->haveStrokeSpeedMeasurement());
131 }
132 
initStrokeCallback()133 void FreehandStrokeStrategy::initStrokeCallback()
134 {
135     KisPainterBasedStrokeStrategy::initStrokeCallback();
136     m_d->efficiencyMeasurer.notifyRenderingStarted();
137 }
138 
finishStrokeCallback()139 void FreehandStrokeStrategy::finishStrokeCallback()
140 {
141     m_d->efficiencyMeasurer.notifyRenderingFinished();
142     KisPainterBasedStrokeStrategy::finishStrokeCallback();
143 }
144 
145 
doStrokeCallback(KisStrokeJobData * data)146 void FreehandStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
147 {
148     if (KisAsyncronousStrokeUpdateHelper::UpdateData *d =
149             dynamic_cast<KisAsyncronousStrokeUpdateHelper::UpdateData*>(data)) {
150 
151         // this job is lod-clonable in contrast to FreehandStrokeRunnableJobDataWithUpdate!
152         tryDoUpdate(d->forceUpdate);
153 
154     } else if (Data *d = dynamic_cast<Data*>(data)) {
155         KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(d->strokeInfoId);
156 
157         KisUpdateTimeMonitor::instance()->reportPaintOpPreset(maskedPainter->preset());
158         KisRandomSourceSP rnd = m_d->randomSource.source();
159         KisPerStrokeRandomSourceSP strokeRnd = m_d->randomSource.perStrokeSource();
160 
161         switch(d->type) {
162         case Data::POINT:
163             d->pi1.setRandomSource(rnd);
164             d->pi1.setPerStrokeRandomSource(strokeRnd);
165             maskedPainter->paintAt(d->pi1);
166             m_d->efficiencyMeasurer.addSample(d->pi1.pos());
167             break;
168         case Data::LINE:
169             d->pi1.setRandomSource(rnd);
170             d->pi2.setRandomSource(rnd);
171             d->pi1.setPerStrokeRandomSource(strokeRnd);
172             d->pi2.setPerStrokeRandomSource(strokeRnd);
173             maskedPainter->paintLine(d->pi1, d->pi2);
174             m_d->efficiencyMeasurer.addSample(d->pi2.pos());
175             break;
176         case Data::CURVE:
177             d->pi1.setRandomSource(rnd);
178             d->pi2.setRandomSource(rnd);
179             d->pi1.setPerStrokeRandomSource(strokeRnd);
180             d->pi2.setPerStrokeRandomSource(strokeRnd);
181             maskedPainter->paintBezierCurve(d->pi1,
182                                          d->control1,
183                                          d->control2,
184                                          d->pi2);
185             m_d->efficiencyMeasurer.addSample(d->pi2.pos());
186             break;
187         case Data::POLYLINE:
188             maskedPainter->paintPolyline(d->points, 0, d->points.size());
189             m_d->efficiencyMeasurer.addSamples(d->points);
190             break;
191         case Data::POLYGON:
192             maskedPainter->paintPolygon(d->points);
193             m_d->efficiencyMeasurer.addSamples(d->points);
194             break;
195         case Data::RECT:
196             maskedPainter->paintRect(d->rect);
197             m_d->efficiencyMeasurer.addSample(d->rect.topLeft());
198             m_d->efficiencyMeasurer.addSample(d->rect.topRight());
199             m_d->efficiencyMeasurer.addSample(d->rect.bottomRight());
200             m_d->efficiencyMeasurer.addSample(d->rect.bottomLeft());
201             break;
202         case Data::ELLIPSE:
203             maskedPainter->paintEllipse(d->rect);
204             // TODO: add speed measures
205             break;
206         case Data::PAINTER_PATH:
207             maskedPainter->paintPainterPath(d->path);
208             // TODO: add speed measures
209             break;
210         case Data::QPAINTER_PATH:
211             maskedPainter->drawPainterPath(d->path, d->pen);
212             break;
213         case Data::QPAINTER_PATH_FILL:
214             maskedPainter->drawAndFillPainterPath(d->path, d->pen, d->customColor);
215             break;
216         };
217 
218         tryDoUpdate();
219     } else {
220         KisPainterBasedStrokeStrategy::doStrokeCallback(data);
221 
222         FreehandStrokeRunnableJobDataWithUpdate *dataWithUpdate =
223             dynamic_cast<FreehandStrokeRunnableJobDataWithUpdate*>(data);
224 
225         if (dataWithUpdate) {
226             tryDoUpdate();
227         }
228     }
229 }
230 
tryDoUpdate(bool forceEnd)231 void FreehandStrokeStrategy::tryDoUpdate(bool forceEnd)
232 {
233     // we should enter this function only once!
234     std::unique_lock<std::mutex> entryLock(m_d->updateEntryMutex, std::try_to_lock);
235     if (!entryLock.owns_lock()) return;
236 
237     if (m_d->needsAsynchronousUpdates) {
238         if (forceEnd || m_d->timeSinceLastUpdate.elapsed() > m_d->currentUpdatePeriod) {
239             m_d->timeSinceLastUpdate.restart();
240 
241             for (int i = 0; i < numMaskedPainters(); i++) {
242                 KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(i);
243 
244                 // TODO: well, we should count all N simultaneous painters for FPS rate!
245                 QVector<KisRunnableStrokeJobData*> jobs;
246 
247                 bool needsMoreUpdates = false;
248 
249                 std::tie(m_d->currentUpdatePeriod, needsMoreUpdates) =
250                     maskedPainter->doAsyncronousUpdate(jobs);
251 
252                 if (!jobs.isEmpty() ||
253                     maskedPainter->hasDirtyRegion() ||
254                     (forceEnd && needsMoreUpdates)) {
255 
256                     jobs.append(new KisRunnableStrokeJobData(
257                                     [this] () {
258                                         this->issueSetDirtySignals();
259                                     },
260                                     KisStrokeJobData::SEQUENTIAL));
261 
262                     if (forceEnd && needsMoreUpdates) {
263                         jobs.append(new KisRunnableStrokeJobData(
264                                         [this] () {
265                                             this->tryDoUpdate(true);
266                                         },
267                                         KisStrokeJobData::SEQUENTIAL));
268                     }
269 
270 
271                     runnableJobsInterface()->addRunnableJobs(jobs);
272                     m_d->efficiencyMeasurer.notifyFrameRenderingStarted();
273                 }
274 
275             }
276         }
277     } else {
278         issueSetDirtySignals();
279     }
280 
281 
282 }
283 
issueSetDirtySignals()284 void FreehandStrokeStrategy::issueSetDirtySignals()
285 {
286     QVector<QRect> dirtyRects;
287 
288     for (int i = 0; i < numMaskedPainters(); i++) {
289         KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(i);
290         dirtyRects.append(maskedPainter->takeDirtyRegion());
291     }
292 
293     if (needsMaskingUpdates()) {
294 
295         // optimize the rects so that they would never intersect with each other!
296         // that is a mandatory step for the multithreaded execution of merging jobs
297 
298         // sanity check: updates from the brush should have already been normalized
299         //               to the wrapping rect
300         const KisDefaultBoundsBaseSP defaultBounds = targetNode()->projection()->defaultBounds();
301         if (defaultBounds->wrapAroundMode()) {
302             const QRect wrapRect = defaultBounds->imageBorderRect();
303             for (auto it = dirtyRects.begin(); it != dirtyRects.end(); ++it) {
304                 KIS_SAFE_ASSERT_RECOVER(wrapRect.contains(*it)) {
305                     ENTER_FUNCTION() << ppVar(*it) << ppVar(wrapRect);
306                     *it = *it & wrapRect;
307                 }
308             }
309         }
310 
311         const int maxPatchSizeForMaskingUpdates = 64;
312         const QRect totalRect =
313             std::accumulate(dirtyRects.constBegin(), dirtyRects.constEnd(), QRect(), std::bit_or<QRect>());
314 
315         dirtyRects = KisPaintOpUtils::splitAndFilterDabRect(totalRect, dirtyRects, maxPatchSizeForMaskingUpdates);
316 
317         QVector<KisRunnableStrokeJobData*> jobs = doMaskingBrushUpdates(dirtyRects);
318 
319         jobs.append(new KisRunnableStrokeJobData(
320             [this, dirtyRects] () {
321                 this->targetNode()->setDirty(dirtyRects);
322             },
323             KisStrokeJobData::SEQUENTIAL));
324 
325         runnableJobsInterface()->addRunnableJobs(jobs);
326 
327     } else {
328         targetNode()->setDirty(dirtyRects);
329     }
330 
331     //KisUpdateTimeMonitor::instance()->reportJobFinished(data, dirtyRects);
332 }
333 
createLodClone(int levelOfDetail)334 KisStrokeStrategy* FreehandStrokeStrategy::createLodClone(int levelOfDetail)
335 {
336     if (!m_d->resources->presetAllowsLod()) return 0;
337     if (!m_d->resources->currentNode()->supportsLodPainting()) return 0;
338 
339     FreehandStrokeStrategy *clone = new FreehandStrokeStrategy(*this, levelOfDetail);
340     return clone;
341 }
342 
notifyUserStartedStroke()343 void FreehandStrokeStrategy::notifyUserStartedStroke()
344 {
345     m_d->efficiencyMeasurer.notifyCursorMoveStarted();
346 }
347 
notifyUserEndedStroke()348 void FreehandStrokeStrategy::notifyUserEndedStroke()
349 {
350     m_d->efficiencyMeasurer.notifyCursorMoveFinished();
351 }
352