9#include <QElapsedTimer>
11#include <QApplication>
74 resources, strokeInfo),
85 resources, strokeInfos),
95 m_d->randomSource.setLevelOfDetail(levelOfDetail);
101 m_d->efficiencyMeasurer.averageRenderingSpeed(),
102 m_d->efficiencyMeasurer.averageFps(),
103 m_d->resources->currentPaintOpPreset());
118 if (
m_d->needsAsynchronousUpdates) {
133 m_d->efficiencyMeasurer.notifyRenderingStarted();
138 m_d->efficiencyMeasurer.notifyRenderingFinished();
151 }
else if (
Data *d =
dynamic_cast<Data*
>(data)) {
160 d->pi1.setRandomSource(rnd);
161 d->pi1.setPerStrokeRandomSource(strokeRnd);
163 m_d->efficiencyMeasurer.addSample(d->pi1.pos());
166 d->pi1.setRandomSource(rnd);
167 d->pi2.setRandomSource(rnd);
168 d->pi1.setPerStrokeRandomSource(strokeRnd);
169 d->pi2.setPerStrokeRandomSource(strokeRnd);
171 m_d->efficiencyMeasurer.addSample(d->pi2.pos());
174 d->pi1.setRandomSource(rnd);
175 d->pi2.setRandomSource(rnd);
176 d->pi1.setPerStrokeRandomSource(strokeRnd);
177 d->pi2.setPerStrokeRandomSource(strokeRnd);
182 m_d->efficiencyMeasurer.addSample(d->pi2.pos());
186 m_d->efficiencyMeasurer.addSamples(d->points);
190 m_d->efficiencyMeasurer.addSamples(d->points);
194 m_d->efficiencyMeasurer.addSample(d->rect.topLeft());
195 m_d->efficiencyMeasurer.addSample(d->rect.topRight());
196 m_d->efficiencyMeasurer.addSample(d->rect.bottomRight());
197 m_d->efficiencyMeasurer.addSample(d->rect.bottomLeft());
222 if (dataWithUpdate) {
231 std::unique_lock<std::mutex> entryLock(
m_d->updateEntryMutex, std::try_to_lock);
232 if (!entryLock.owns_lock())
return;
234 if (
m_d->needsAsynchronousUpdates) {
235 if (forceEnd ||
m_d->timeSinceLastUpdate.elapsed() >
m_d->currentUpdatePeriod) {
236 m_d->timeSinceLastUpdate.restart();
244 bool needsMoreUpdates =
false;
246 std::tie(
m_d->currentUpdatePeriod, needsMoreUpdates) =
249 if (!jobs.isEmpty() ||
251 (forceEnd && needsMoreUpdates)) {
259 if (forceEnd && needsMoreUpdates) {
269 m_d->efficiencyMeasurer.notifyFrameRenderingStarted();
300 for (
auto it = dirtyRects.begin(); it != dirtyRects.end(); ++it) {
303 *it = *it & wrapRect;
308 const int maxPatchSizeForMaskingUpdates = 64;
309 const QRect totalRect =
310 std::accumulate(dirtyRects.constBegin(), dirtyRects.constEnd(), QRect(), std::bit_or<QRect>());
317 [
this, dirtyRects] () {
333 if (!
m_d->resources->presetAllowsLod())
return 0;
334 if (!
m_d->resources->currentNode()->supportsLodPainting())
return 0;
342 m_d->efficiencyMeasurer.notifyCursorMoveStarted();
347 m_d->efficiencyMeasurer.notifyCursorMoveFinished();
void doStrokeCallback(KisStrokeJobData *data) override
void issueSetDirtySignals()
KisStrokeStrategy * createLodClone(int levelOfDetail) override
~FreehandStrokeStrategy() override
@ SupportsContinuedInterstrokeData
void initStrokeCallback() override
const QScopedPointer< Private > m_d
FreehandStrokeStrategy(KisResourcesSnapshotSP resources, KisFreehandStrokeInfo *strokeInfo, const KUndo2MagicString &name, Flags flags=None)
void notifyUserEndedStroke() override
void notifyUserStartedStroke() override
void tryDoUpdate(bool forceEnd=false)
void finishStrokeCallback() override
virtual bool wrapAroundMode() const =0
virtual QRect imageBorderRect() const
std::pair< int, bool > doAsynchronousUpdate(QVector< KisRunnableStrokeJobData * > &jobs)
QVector< QRect > takeDirtyRegion()
void paintEllipse(const QRectF &rect)
void paintPolygon(const QVector< QPointF > &points)
KisPaintOpPresetSP preset() const
void paintPainterPath(const QPainterPath &path)
void paintRect(const QRectF &rect)
void paintAt(const KisPaintInformation &pi)
void drawPainterPath(const QPainterPath &path, const QPen &pen)
void paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2)
bool hasDirtyRegion() const
void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2)
void drawAndFillPainterPath(const QPainterPath &path, const QPen &pen, const KoColor &customColor)
void paintPolyline(const QVector< QPointF > &points, int index=0, int numPoints=-1)
KisDefaultBoundsBaseSP defaultBounds() const
void setSupportsContinuedInterstrokeData(bool value)
bool needsMaskingUpdates() const
void setSupportsMaskingBrush(bool value)
void setSupportsIndirectPainting(bool value)
void setSupportsTimedMergeId(bool value)
KisMaskedFreehandStrokePainter * maskedPainter(int strokeInfoId)
int numMaskedPainters() const
void finishStrokeCallback() override
void initStrokeCallback() override
KisNodeSP targetNode() const
QVector< KisRunnableStrokeJobData * > doMaskingBrushUpdates(const QVector< QRect > &rects)
KisRunnableStrokeJobsInterface * runnableJobsInterface() const
virtual void addRunnableJobs(const QVector< KisRunnableStrokeJobDataBase * > &list)=0
void enableJob(JobType type, bool enable=true, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
virtual void doStrokeCallback(KisStrokeJobData *data)
void setEnabled(bool value)
static KisStrokeSpeedMonitor * instance()
void notifyStrokeFinished(qreal cursorSpeed, qreal renderingSpeed, qreal fps, KisPaintOpPresetSP preset)
void setBalancingRatioOverride(qreal value)
void setSupportsWrapAroundMode(bool value)
#define KIS_SAFE_ASSERT_RECOVER(cond)
QVector< QRect > splitAndFilterDabRect(const QRect &totalRect, const QVector< QRect > &dabRects, int idealPatchSize)
void addJobSequential(QVector< Job * > &jobs, Func func)
KisStrokeRandomSource randomSource
KisResourcesSnapshotSP resources
Private(KisResourcesSnapshotSP _resources)
KisStrokeEfficiencyMeasurer efficiencyMeasurer
std::mutex updateEntryMutex
const bool needsAsynchronousUpdates
Private(const Private &rhs)
QElapsedTimer timeSinceLastUpdate
virtual KisPaintDeviceSP projection() const =0
static KisUpdateTimeMonitor * instance()
void startStrokeMeasure()
void reportPaintOpPreset(KisPaintOpPresetSP preset)