Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
76227ff5e8b109a231e11dfa5b430497aaf1205d
[graphlib.git] / DrawingWindow.cpp
1 #include "DrawingWindow.h"
2 #include <QApplication>
3 #include <QBasicTimer>
4 #include <QColor>
5 #include <QImage>
6 #include <QMutex>
7 #include <QPaintEvent>
8 #include <QPainter>
9 #include <QRect>
10 #include <QThread>
11 #include <QTimerEvent>
12 #include <QWaitCondition>
13
14 class DrawingThread: public QThread {
15 public:
16     DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f);
17     void start_once(Priority priority = InheritPriority);
18
19 protected:
20     void run();
21
22 private:
23     DrawingWindow &drawingWindow;
24     DrawingWindow::ThreadFunction threadFunction;
25     bool started_once;
26
27     friend class DrawingWindow;
28     friend class DrawingWindowPrivate;
29 };
30
31 class DrawingWindowPrivate {
32 public:
33     static const int paintInterval = 33;
34
35     DrawingWindow * const q;
36
37     QBasicTimer timer;
38     QMutex imageMutex;
39     QMutex paintMutex;
40     QWaitCondition paintCondition;
41     bool painted;
42     int lockCount;
43
44     QImage *image;
45     QPainter *painter;
46
47     QColor fgColor;
48     QColor bgColor;
49
50     bool dirtyFlag;
51     QRect dirtyRect;
52
53     DrawingThread *thread;
54
55     DrawingWindowPrivate(DrawingWindow *w,
56                          DrawingWindow::ThreadFunction f);
57     ~DrawingWindowPrivate();
58
59     void initialize();
60
61     void safeLock(QMutex &mutex);
62     void safeUnlock(QMutex &mutex);
63
64     void dirty();
65     void dirty(int x, int y);
66     void dirty(int x1, int y1, int x2, int y2);
67     void dirty(const QRect &rect);
68
69 };
70
71 //--- DrawingWindow ----------------------------------------------------
72
73 DrawingWindow::DrawingWindow(ThreadFunction f, int w, int h)
74     : QWidget()
75     , width(w)
76     , height(h)
77     , d(new DrawingWindowPrivate(this, f))
78 {
79     d->initialize();
80 }
81
82 DrawingWindow::DrawingWindow(QWidget *parent,
83                              ThreadFunction f, int w, int h)
84     : QWidget(parent)
85     , width(w)
86     , height(h)
87     , d(new DrawingWindowPrivate(this, f))
88 {
89     d->initialize();
90 }
91
92 DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags,
93                              ThreadFunction f, int w, int h)
94     : QWidget(parent, flags)
95     , width(w)
96     , height(h)
97     , d(new DrawingWindowPrivate(this, f))
98 {
99     d->initialize();
100 }
101
102 DrawingWindow::~DrawingWindow()
103 {
104     delete d;
105 }
106
107 void DrawingWindow::setColor(float red, float green, float blue)
108 {
109     d->fgColor.setRgbF(red, green, blue);
110     QPen pen(d->painter->pen());
111     pen.setColor(d->fgColor);
112     d->painter->setPen(pen);
113 }
114
115 void DrawingWindow::setBgColor(float red, float green, float blue)
116 {
117     d->bgColor.setRgbF(red, green, blue);
118 }
119
120 void DrawingWindow::clearGraph()
121 {
122     d->safeLock(d->imageMutex);
123     d->painter->fillRect(d->image->rect(), d->bgColor);    
124     d->dirty();
125     d->safeUnlock(d->imageMutex);
126 }
127
128 void DrawingWindow::drawPoint(int x, int y)
129 {
130     d->safeLock(d->imageMutex);
131     d->painter->drawPoint(x, y);
132     d->dirty(x, y);
133     d->safeUnlock(d->imageMutex);
134 }
135
136 void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
137 {
138     d->safeLock(d->imageMutex);
139     d->painter->drawLine(x1, y1, x2, y2);
140     d->dirty(x1, y1, x2, y2);
141     d->safeUnlock(d->imageMutex);
142 }
143
144 void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
145 {
146     QRect r;
147     r.setCoords(x1, y1, x2, y2);
148     r = r.normalized();
149     d->safeLock(d->imageMutex);
150     d->painter->drawRect(r);
151     r.adjust(0, 0, 1, 1);
152     d->dirty(r);
153     d->safeUnlock(d->imageMutex);
154 }
155
156 bool DrawingWindow::sync(unsigned long time)
157 {
158     bool ret;
159     d->safeLock(d->paintMutex);
160     d->safeLock(d->imageMutex);
161 #if 1
162     d->dirty();                 // xxx
163 #else
164     QApplication::postEvent(this, new QPaintEvent(this->rect()));
165     d->dirtyFlag = false;
166 #endif
167     d->safeUnlock(d->imageMutex);
168     ret = d->paintCondition.wait(&d->paintMutex, time);
169     d->safeUnlock(d->paintMutex);
170     return ret;
171 }
172
173 void DrawingWindow::sleep(unsigned long secs)
174 {
175     DrawingThread::sleep(secs);
176 }
177
178 void DrawingWindow::msleep(unsigned long msecs)
179 {
180     DrawingThread::msleep(msecs);
181 }
182
183 void DrawingWindow::usleep(unsigned long usecs)
184 {
185     DrawingThread::usleep(usecs);
186 }
187
188 void DrawingWindow::closeEvent(QCloseEvent *ev)
189 {
190     d->timer.stop();
191     d->thread->terminate();
192     d->paintCondition.wakeAll();
193     QWidget::closeEvent(ev);
194     d->thread->wait();
195 }
196
197 void DrawingWindow::paintEvent(QPaintEvent *ev)
198 {
199     QPainter widgetPainter(this);
200     d->imageMutex.lock();
201     QImage imageCopy(*d->image);
202     d->imageMutex.unlock();
203     QRect rect = ev->rect();
204     widgetPainter.drawImage(rect, imageCopy, rect);
205     if (rect == this->rect()) {
206         d->paintMutex.lock();
207         d->paintCondition.wakeAll();
208         d->paintMutex.unlock();
209     }
210 }
211
212 void DrawingWindow::showEvent(QShowEvent *ev)
213 {
214     d->timer.start(d->paintInterval, this);
215     d->thread->start_once(QThread::IdlePriority);
216     QWidget::showEvent(ev);
217 }
218
219 void DrawingWindow::timerEvent(QTimerEvent *ev)
220 {
221     if (ev->timerId() == d->timer.timerId()) {
222         d->imageMutex.lock();
223         if (d->dirtyFlag) {
224             update(d->dirtyRect);
225             d->dirtyFlag = false;
226         }
227         d->imageMutex.unlock();
228         d->timer.start(d->paintInterval, this);
229     } else {
230         QWidget::timerEvent(ev);
231     }
232 }
233
234 //--- DrawingWindowPrivate ---------------------------------------------
235
236 DrawingWindowPrivate::DrawingWindowPrivate(DrawingWindow *w,
237                                            DrawingWindow::ThreadFunction f)
238     : q(w)
239     , lockCount(0)
240     , image(new QImage(q->width, q->height, QImage::Format_RGB32))
241     , painter(new QPainter(image))
242     , thread(new DrawingThread(*q, f))
243 {
244 }
245
246 void DrawingWindowPrivate::initialize()
247 {
248     q->setFocusPolicy(Qt::StrongFocus);
249     q->setFixedSize(image->size());
250     q->setAttribute(Qt::WA_OpaquePaintEvent);
251     q->setFocus();
252
253     q->setColor(0.0, 0.0, 0.0); // black
254     q->setBgColor(1.0, 1.0, 1.0); // white
255     q->clearGraph();
256
257     dirtyFlag = false;
258 }
259
260 DrawingWindowPrivate::~DrawingWindowPrivate()
261 {
262     delete thread;
263     delete painter;
264     delete image;
265 }
266
267 inline
268 void DrawingWindowPrivate::safeLock(QMutex &mutex)
269 {
270     if (lockCount++ == 0)
271         thread->setTerminationEnabled(false);
272     mutex.lock();
273 }
274
275 inline
276 void DrawingWindowPrivate::safeUnlock(QMutex &mutex)
277 {
278     mutex.unlock();
279     if (--lockCount == 0)
280         thread->setTerminationEnabled(true);
281 }
282
283 inline
284 void DrawingWindowPrivate::dirty()
285 {
286     dirtyFlag = true;
287     dirtyRect = image->rect();
288 }
289
290 inline
291 void DrawingWindowPrivate::dirty(int x, int y)
292 {
293     dirty(QRect(x, y, 1, 1));
294 }
295
296 inline
297 void DrawingWindowPrivate::dirty(int x1, int y1, int x2, int y2)
298 {
299     QRect r;
300     r.setCoords(x1, y1, x2, y2);
301     dirty(r.normalized());
302 }
303
304 void DrawingWindowPrivate::dirty(const QRect &rect)
305 {
306     if (dirtyFlag) {
307         dirtyRect |= rect;
308     } else {
309         dirtyFlag = true;
310         dirtyRect = rect;
311     }
312 }
313
314 //--- DrawingThread ----------------------------------------------------
315
316 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
317     : drawingWindow(w)
318     , threadFunction(f)
319     , started_once(false)
320 {
321 }
322
323 void DrawingThread::start_once(Priority priority)
324 {
325     if (!started_once) {
326         started_once = true;
327         start(priority);
328     }
329 }
330
331 void DrawingThread::run()
332 {
333     threadFunction(drawingWindow);
334 }