1 /*
2  *  Copyright (c) 2017 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 "KisDabRenderingJob.h"
20 
21 #include <QElapsedTimer>
22 
23 #include <KisRunnableStrokeJobsInterface.h>
24 #include <KisRunnableStrokeJobData.h>
25 
26 #include "KisDabCacheUtils.h"
27 #include "KisDabRenderingQueue.h"
28 
29 #include <tool/strokes/FreehandStrokeRunnableJobDataWithUpdate.h>
30 
31 
KisDabRenderingJob()32 KisDabRenderingJob::KisDabRenderingJob()
33 {
34 }
35 
KisDabRenderingJob(int _seqNo,KisDabCacheUtils::DabGenerationInfo _generationInfo,KisDabRenderingJob::JobType _type)36 KisDabRenderingJob::KisDabRenderingJob(int _seqNo, KisDabCacheUtils::DabGenerationInfo _generationInfo, KisDabRenderingJob::JobType _type)
37     : seqNo(_seqNo),
38       generationInfo(_generationInfo),
39       type(_type)
40 {
41 }
42 
KisDabRenderingJob(const KisDabRenderingJob & rhs)43 KisDabRenderingJob::KisDabRenderingJob(const KisDabRenderingJob &rhs)
44     : seqNo(rhs.seqNo),
45       generationInfo(rhs.generationInfo),
46       type(rhs.type),
47       originalDevice(rhs.originalDevice),
48       postprocessedDevice(rhs.postprocessedDevice),
49       status(rhs.status),
50       opacity(rhs.opacity),
51       flow(rhs.flow)
52 {
53 }
54 
operator =(const KisDabRenderingJob & rhs)55 KisDabRenderingJob &KisDabRenderingJob::operator=(const KisDabRenderingJob &rhs)
56 {
57     seqNo = rhs.seqNo;
58     generationInfo = rhs.generationInfo;
59     type = rhs.type;
60     originalDevice = rhs.originalDevice;
61     postprocessedDevice = rhs.postprocessedDevice;
62     status = rhs.status;
63     opacity = rhs.opacity;
64     flow = rhs.flow;
65 
66     return *this;
67 }
68 
dstDabOffset() const69 QPoint KisDabRenderingJob::dstDabOffset() const
70 {
71     /// Recenter generated low-res dab around the center
72     /// of the idel theoretical dab rect
73     const QPoint p1 = generationInfo.dstDabRect.topLeft();
74     const QPoint s1 = QPoint(generationInfo.dstDabRect.width(),
75                              generationInfo.dstDabRect.height());
76     const QPoint s2 = QPoint(postprocessedDevice->bounds().width(),
77                              postprocessedDevice->bounds().height());
78     return p1 + (s1 - s2) / 2;
79 }
80 
81 
82 
KisDabRenderingJobRunner(KisDabRenderingJobSP job,KisDabRenderingQueue * parentQueue,KisRunnableStrokeJobsInterface * runnableJobsInterface)83 KisDabRenderingJobRunner::KisDabRenderingJobRunner(KisDabRenderingJobSP job,
84                                                    KisDabRenderingQueue *parentQueue,
85                                                    KisRunnableStrokeJobsInterface *runnableJobsInterface)
86     : m_job(job),
87       m_parentQueue(parentQueue),
88       m_runnableJobsInterface(runnableJobsInterface)
89 {
90 }
91 
~KisDabRenderingJobRunner()92 KisDabRenderingJobRunner::~KisDabRenderingJobRunner()
93 {
94 }
95 
executeOneJob(KisDabRenderingJob * job,KisDabCacheUtils::DabRenderingResources * resources,KisDabRenderingQueue * parentQueue)96 int KisDabRenderingJobRunner::executeOneJob(KisDabRenderingJob *job,
97                                             KisDabCacheUtils::DabRenderingResources *resources,
98                                             KisDabRenderingQueue *parentQueue)
99 {
100     using namespace KisDabCacheUtils;
101 
102     KIS_SAFE_ASSERT_RECOVER_NOOP(job->type == KisDabRenderingJob::Dab ||
103                                  job->type == KisDabRenderingJob::Postprocess);
104 
105     QElapsedTimer executionTime;
106     executionTime.start();
107 
108     resources->syncResourcesToSeqNo(job->seqNo, job->generationInfo.info);
109 
110     if (job->type == KisDabRenderingJob::Dab) {
111         // TODO: thing about better interface for the reverse queue link
112         job->originalDevice = parentQueue->fetchCachedPaintDevce();
113 
114         generateDab(job->generationInfo, resources, &job->originalDevice);
115     }
116 
117     // by now the original device should be already prepared
118     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(job->originalDevice, 0);
119 
120     if (job->type == KisDabRenderingJob::Dab ||
121         job->type == KisDabRenderingJob::Postprocess) {
122 
123         if (job->generationInfo.needsPostprocessing) {
124             // TODO: cache postprocessed device
125 
126             if (!job->postprocessedDevice ||
127                 *job->originalDevice->colorSpace() != *job->postprocessedDevice->colorSpace()) {
128 
129                 job->postprocessedDevice = parentQueue->fetchCachedPaintDevce();
130                 *job->postprocessedDevice = *job->originalDevice;
131             } else {
132                 *job->postprocessedDevice = *job->originalDevice;
133             }
134 
135             postProcessDab(job->postprocessedDevice,
136                            job->generationInfo.dstDabRect.topLeft(),
137                            job->generationInfo.info,
138                            resources);
139         } else {
140             job->postprocessedDevice = job->originalDevice;
141         }
142     }
143 
144     return executionTime.nsecsElapsed() / 1000;
145 }
146 
run()147 void KisDabRenderingJobRunner::run()
148 {
149     int executionTime = 0;
150 
151     KisDabCacheUtils::DabRenderingResources *resources = m_parentQueue->fetchResourcesFromCache();
152 
153     executionTime = executeOneJob(m_job.data(), resources, m_parentQueue);
154     QList<KisDabRenderingJobSP> jobs = m_parentQueue->notifyJobFinished(m_job->seqNo, executionTime);
155 
156     while (!jobs.isEmpty()) {
157         QVector<KisRunnableStrokeJobData*> dataList;
158 
159         // start all-but-the-first jobs asynchronously
160         for (int i = 1; i < jobs.size(); i++) {
161             dataList.append(new FreehandStrokeRunnableJobDataWithUpdate(
162                                 new KisDabRenderingJobRunner(jobs[i], m_parentQueue, m_runnableJobsInterface),
163                                 KisStrokeJobData::CONCURRENT));
164         }
165 
166         m_runnableJobsInterface->addRunnableJobs(dataList);
167 
168 
169         // execute the first job in the current thread
170         KisDabRenderingJobSP job = jobs.first();
171         executionTime = executeOneJob(job.data(), resources, m_parentQueue);
172         jobs = m_parentQueue->notifyJobFinished(job->seqNo, executionTime);
173     }
174 
175     m_parentQueue->putResourcesToCache(resources);
176 }
177