1 /*
2 * This file is part of RawTherapee.
3 *
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
5 *
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "bqentryupdater.h"
20 #include <gtkmm.h>
21 #include "guiutils.h"
22
23 BatchQueueEntryUpdater batchQueueEntryUpdater;
24
BatchQueueEntryUpdater()25 BatchQueueEntryUpdater::BatchQueueEntryUpdater ()
26 : tostop(false), stopped(true), thread(nullptr), qMutex(nullptr)
27 {
28 }
29
process(guint8 * oimg,int ow,int oh,int newh,BQEntryUpdateListener * listener,rtengine::ProcParams * pparams,Thumbnail * thumbnail)30 void BatchQueueEntryUpdater::process (guint8* oimg, int ow, int oh, int newh, BQEntryUpdateListener* listener, rtengine::ProcParams* pparams, Thumbnail* thumbnail)
31 {
32 if (!oimg && (!pparams || !thumbnail)) {
33 //printf("WARNING! !oimg && (!pparams || !thumbnail)\n");
34 return;
35 }
36
37 if (!qMutex) {
38 qMutex = new MyMutex ();
39 }
40
41 qMutex->lock ();
42 // look up if an older version is in the queue
43 std::list<Job>::iterator i;
44
45 for (i = jqueue.begin(); i != jqueue.end(); ++i)
46 if (i->oimg == oimg && i->listener == listener) {
47 i->ow = ow;
48 i->oh = oh;
49 i->newh = newh;
50 i->listener = listener;
51 i->pparams = pparams;
52 i->thumbnail = thumbnail;
53 break;
54 }
55
56 // not found, create and append new job
57 if (i == jqueue.end ()) {
58 Job j;
59 j.oimg = oimg;
60 j.ow = ow;
61 j.oh = oh;
62 j.newh = newh;
63 j.listener = listener;
64 j.pparams = pparams;
65 j.thumbnail = thumbnail;
66 jqueue.push_back (j);
67 }
68
69 qMutex->unlock ();
70
71 // Start thread if not running yet
72 if (stopped) {
73 stopped = false;
74 tostop = false;
75
76 #undef THREAD_PRIORITY_LOW
77 thread = Glib::Thread::create(sigc::mem_fun(*this, &BatchQueueEntryUpdater::processThread), (unsigned long int)0, true, true, Glib::THREAD_PRIORITY_LOW);
78 }
79 }
80
processThread()81 void BatchQueueEntryUpdater::processThread ()
82 {
83 // TODO: process visible jobs first
84 bool isEmpty = false;
85
86 while (!tostop && !isEmpty) {
87
88 qMutex->lock ();
89 isEmpty = jqueue.empty (); // do NOT put into while() since it must be within mutex section
90 Job current;
91
92 if (!isEmpty) {
93 current = jqueue.front ();
94 jqueue.pop_front ();
95 }
96
97 qMutex->unlock ();
98
99 if(isEmpty) {
100 break;
101 }
102
103 rtengine::IImage8* img = nullptr;
104 bool newBuffer = false;
105
106 if (current.thumbnail && current.pparams) {
107 // the thumbnail and the pparams are provided, it means that we have to build the original preview image
108 double tmpscale;
109 img = current.thumbnail->processThumbImage (*current.pparams, current.oh, tmpscale);
110
111 //current.thumbnail->decreaseRef (); // WARNING: decreasing refcount (and maybe deleting) thumbnail, with or without processed image
112 if (img) {
113 int prevw = img->getWidth();
114 int prevh = img->getHeight();
115 #ifndef NDEBUG
116
117 if (current.ow != img->getWidth() || current.oh != img->getHeight()) {
118 printf("WARNING! Expected image size: %dx%d ; image size is: %dx%d\n", current.ow, current.oh, img->getWidth(), img->getHeight());
119 }
120
121 assert ((current.ow + 1)*current.oh >= img->getWidth()*img->getHeight());
122 #endif
123 current.ow = prevw;
124 current.oh = prevh;
125
126 if (!current.oimg) {
127 current.oimg = new guint8[prevw * prevh * 3];
128 newBuffer = true;
129 }
130
131 memcpy(current.oimg, img->getData(), prevw * prevh * 3);
132 img->free();
133 }
134 }
135
136 if (current.oimg && !isEmpty && current.listener) {
137 int neww = current.newh * current.ow / current.oh;
138 guint8* img = new guint8 [current.newh * neww * 3];
139 thumbInterp (current.oimg, current.ow, current.oh, img, neww, current.newh);
140 current.listener->updateImage (img, neww, current.newh, current.ow, current.oh, newBuffer ? current.oimg : nullptr);
141 }
142
143 if(current.oimg) {
144 delete[] current.oimg;
145 current.oimg = nullptr;
146 }
147 }
148
149 stopped = true;
150 }
151
152
removeJobs(BQEntryUpdateListener * listener)153 void BatchQueueEntryUpdater::removeJobs (BQEntryUpdateListener* listener)
154 {
155 if (!qMutex) {
156 return;
157 }
158
159 qMutex->lock ();
160 bool ready = false;
161
162 while (!ready) {
163 ready = true;
164 std::list<Job>::iterator i;
165
166 for (i = jqueue.begin(); i != jqueue.end(); ++i)
167 if (i->listener == listener) {
168 jqueue.erase (i);
169 ready = false;
170 break;
171 }
172 }
173
174 qMutex->unlock ();
175 }
176
terminate()177 void BatchQueueEntryUpdater::terminate ()
178 {
179 // never started or currently not running?
180 if (!qMutex || stopped) {
181 return;
182 }
183
184 if (!stopped) {
185 // Yield to currently running thread and wait till it's finished
186 GThreadUnLock lock;
187 tostop = true;
188 Glib::Thread::self()->yield();
189
190 if (!stopped) {
191 thread->join ();
192 }
193 }
194
195 // Remove remaining jobs
196 qMutex->lock ();
197
198 while (!jqueue.empty()) {
199 jqueue.pop_front ();
200 }
201
202 qMutex->unlock ();
203 }
204
205
206