KHtml

paintbuffer.cpp
1 /*
2  * This file is part of the KDE libraries
3  *
4  * Copyright (C) 2007 Germain Garand <[email protected]>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "paintbuffer.h"
24 #include <QTimerEvent>
25 
26 using namespace khtml;
27 
28 const int PaintBuffer::maxPixelBuffering = 200 * 200;
29 const int PaintBuffer::leaseTime = 2 * 1000;
30 const int PaintBuffer::cleanupTime = 10 * 1000;
31 const int PaintBuffer::maxBuffers = 10;
32 
33 namespace khtml
34 {
35 
36 class BufferSweeper: public QObject
37 {
38 public:
39  BufferSweeper(): QObject()
40  {
41  m_timer = 0;
42  m_reset = false;
43  }
44 
45  void timerEvent(QTimerEvent *e) override
46  {
47  assert(m_timer == e->timerId());
48  Q_UNUSED(e);
49  if (m_reset) {
50  m_reset = false;
51  return;
52  }
53  if (PaintBuffer::s_avail) {
54  while (PaintBuffer::s_avail->count() > 1) {
55  delete PaintBuffer::s_avail->pop();
56  }
57  if (PaintBuffer::s_avail->count()) {
58  PaintBuffer::s_avail->top()->reset();
59  }
60  }
61  if (!PaintBuffer::s_grabbed) {
62  stop();
63  }
64  }
65  void start()
66  {
67  if (m_timer) {
68  return;
69  }
70  m_timer = startTimer(PaintBuffer::cleanupTime);
71  }
72  void stop()
73  {
74  if (m_timer) {
75  killTimer(m_timer);
76  }
77  m_timer = 0;
78  }
79  void restart()
80  {
81  stop();
82  start();
83  }
84  void reset()
85  {
86  m_reset = true;
87  }
88  bool stopped() const
89  {
90  return !m_timer;
91  }
92  int m_timer;
93  bool m_reset;
94 };
95 
96 }
97 
98 PaintBuffer::PaintBuffer()
99  : m_overflow(false),
100  m_grabbed(false),
101  m_renewTimer(false),
102  m_timer(0),
103  m_resetWidth(0),
104  m_resetHeight(0)
105 {
106 
107 }
108 
109 // static
110 void PaintBuffer::cleanup()
111 {
112  if (s_avail) {
113  qDeleteAll(*s_avail);
114  delete s_avail;
115  s_avail = nullptr;
116  }
117  if (s_grabbed) {
118  qDeleteAll(*s_grabbed);
119  delete s_grabbed;
120  s_grabbed = nullptr;
121  }
122  if (s_full) {
123  qDeleteAll(*s_full);
124  delete s_full;
125  s_full = nullptr;
126  }
127  if (s_sweeper) {
128  s_sweeper->deleteLater();
129  s_sweeper = nullptr;
130  }
131 }
132 
133 // static
134 QPixmap *PaintBuffer::grab(QSize s)
135 {
136  if (!s_avail) {
137  s_avail = new QStack<PaintBuffer *>;
138  s_grabbed = new QStack<PaintBuffer *>;
139  s_sweeper = new BufferSweeper;
140  }
141 
142  if (s_sweeper->stopped()) {
143  s_sweeper->start();
144  } else {
145  s_sweeper->reset();
146  }
147 
148  if (s_grabbed->count() + s_avail->count() >= maxBuffers) {
149  if (!s_full) {
150  s_full = new QStack<QPixmap *>;
151  }
152  s_full->push(new QPixmap(s.width(), s.height()));
153  return s_full->top();
154  }
155 
156  s_grabbed->push(s_avail->count() ? s_avail->pop() : new PaintBuffer);
157  QPixmap *ret = s_grabbed->top()->getBuf(s);
158  //qCDebug(KHTML_LOG) << "requested size:" << s << "real size:" << ret->size();
159  return ret;
160 }
161 
162 // static
163 void PaintBuffer::release(QPixmap *px)
164 {
165  if (s_full && s_full->count()) {
166  assert(px == s_full->top());
167  Q_UNUSED(px);
168  delete s_full->top();
169  s_full->pop();
170  return;
171  }
172  assert(px == &s_grabbed->top()->m_buf);
173  s_grabbed->top()->m_grabbed = false;
174  s_avail->push(s_grabbed->pop());
175 }
176 
177 void PaintBuffer::timerEvent(QTimerEvent *e)
178 {
179  assert(m_timer == e->timerId());
180  Q_UNUSED(e);
181  if (m_grabbed) {
182  return;
183  }
184  if (m_renewTimer) {
185  m_renewTimer = false;
186  return;
187  }
188  m_buf = QPixmap(m_resetWidth, m_resetHeight);
189  m_resetWidth = m_resetHeight = 0;
190  m_overflow = false;
191  killTimer(m_timer);
192  m_timer = 0;
193 }
194 
195 void PaintBuffer::reset()
196 {
197  if (m_grabbed | m_renewTimer) {
198  return;
199  }
200  m_resetWidth = m_resetHeight = 0;
201  m_buf = QPixmap();
202  m_overflow = false;
203  if (m_timer) {
204  killTimer(m_timer);
205  }
206  m_timer = 0;
207 }
208 
209 QPixmap *PaintBuffer::getBuf(QSize s)
210 {
211  assert(!m_grabbed);
212  if (s.isEmpty()) {
213  return nullptr;
214  }
215 
216  m_grabbed = true;
217  bool cur_overflow = false;
218  int nw = qMax(m_buf.width(), s.width());
219  int nh = qMax(m_buf.height(), s.height());
220 
221  if (!m_overflow && (nw * nh > maxPixelBuffering)) {
222  cur_overflow = true;
223  }
224 
225  if (nw != m_buf.width() || nh != m_buf.height()) {
226  m_buf = QPixmap(nw, nh);
227  }
228 
229  if (cur_overflow) {
230  m_overflow = true;
231  m_timer = startTimer(leaseTime);
232  } else if (m_overflow) {
233  int numPx = s.width() * s.height();
234  if (numPx > maxPixelBuffering) {
235  m_renewTimer = true;
236  } else if (numPx > m_resetWidth * m_resetHeight) {
237  m_resetWidth = s.width();
238  m_resetHeight = s.height();
239  }
240  }
241  return &m_buf;
242 }
243 
244 QStack<PaintBuffer *> *PaintBuffer::s_avail = nullptr;
245 QStack<PaintBuffer *> *PaintBuffer::s_grabbed = nullptr;
246 QStack<QPixmap *> *PaintBuffer::s_full = nullptr;
247 BufferSweeper *PaintBuffer::s_sweeper = nullptr;
248 
249 // ### benchark me in release mode
250 #define USE_PIXMAP_CACHE
251 
252 // static
253 BufferedPainter *BufferedPainter::start(QPainter *&p, const QRegion &rr)
254 {
255  if (rr.isEmpty()) {
256  return nullptr;
257  }
258 #ifdef USE_PIXMAP_CACHE
259  QPixmap *pm = PaintBuffer::grab(rr.boundingRect().size());
260 #else
261  QPixmap *pm = new QPixmap(rr.boundingRect().size());
262 #endif
263  if (!pm || pm->isNull()) {
264  return nullptr;
265  }
266  return new BufferedPainter(pm, p, rr, true /*replacePainter*/);
267 }
268 
269 // static
270 void BufferedPainter::end(QPainter *&p, BufferedPainter *bp, float opacity)
271 {
272  bp->transfer(opacity);
273  p = bp->originalPainter();
274 #ifdef USE_PIXMAP_CACHE
275  PaintBuffer::release(bp->buffer());
276 #else
277  delete bp->buffer();
278 #endif
279  delete bp;
280 }
QSize size() const const
int width() const const
bool isEmpty() const const
This file is part of the HTML rendering engine for KDE.
void push(const T &t)
QRect boundingRect() const const
QAction * restart(const QObject *recvr, const char *slot, QObject *parent)
KGuiItem stop()
int startTimer(int interval, Qt::TimerType timerType)
int height() const const
int timerId() const const
bool isEmpty() const const
KGuiItem reset()
void killTimer(int id)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:06 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.