File indexing completed on 2023-12-03 07:43:23
0001 /* 0002 * This file is part of the KDE libraries 0003 * 0004 * Copyright (C) 2007 Germain Garand <germain@ebooksfrance.org> 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Library General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Library General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Library General Public License 0017 * along with this library; see the file COPYING.LIB. If not, write to 0018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 * 0021 */ 0022 0023 #include "paintbuffer.h" 0024 #include <QTimerEvent> 0025 0026 using namespace khtml; 0027 0028 const int PaintBuffer::maxPixelBuffering = 200 * 200; 0029 const int PaintBuffer::leaseTime = 2 * 1000; 0030 const int PaintBuffer::cleanupTime = 10 * 1000; 0031 const int PaintBuffer::maxBuffers = 10; 0032 0033 namespace khtml 0034 { 0035 0036 class BufferSweeper: public QObject 0037 { 0038 public: 0039 BufferSweeper(): QObject() 0040 { 0041 m_timer = 0; 0042 m_reset = false; 0043 } 0044 0045 void timerEvent(QTimerEvent *e) override 0046 { 0047 assert(m_timer == e->timerId()); 0048 Q_UNUSED(e); 0049 if (m_reset) { 0050 m_reset = false; 0051 return; 0052 } 0053 if (PaintBuffer::s_avail) { 0054 while (PaintBuffer::s_avail->count() > 1) { 0055 delete PaintBuffer::s_avail->pop(); 0056 } 0057 if (PaintBuffer::s_avail->count()) { 0058 PaintBuffer::s_avail->top()->reset(); 0059 } 0060 } 0061 if (!PaintBuffer::s_grabbed) { 0062 stop(); 0063 } 0064 } 0065 void start() 0066 { 0067 if (m_timer) { 0068 return; 0069 } 0070 m_timer = startTimer(PaintBuffer::cleanupTime); 0071 } 0072 void stop() 0073 { 0074 if (m_timer) { 0075 killTimer(m_timer); 0076 } 0077 m_timer = 0; 0078 } 0079 void restart() 0080 { 0081 stop(); 0082 start(); 0083 } 0084 void reset() 0085 { 0086 m_reset = true; 0087 } 0088 bool stopped() const 0089 { 0090 return !m_timer; 0091 } 0092 int m_timer; 0093 bool m_reset; 0094 }; 0095 0096 } 0097 0098 PaintBuffer::PaintBuffer() 0099 : m_overflow(false), 0100 m_grabbed(false), 0101 m_renewTimer(false), 0102 m_timer(0), 0103 m_resetWidth(0), 0104 m_resetHeight(0) 0105 { 0106 0107 } 0108 0109 // static 0110 void PaintBuffer::cleanup() 0111 { 0112 if (s_avail) { 0113 qDeleteAll(*s_avail); 0114 delete s_avail; 0115 s_avail = nullptr; 0116 } 0117 if (s_grabbed) { 0118 qDeleteAll(*s_grabbed); 0119 delete s_grabbed; 0120 s_grabbed = nullptr; 0121 } 0122 if (s_full) { 0123 qDeleteAll(*s_full); 0124 delete s_full; 0125 s_full = nullptr; 0126 } 0127 if (s_sweeper) { 0128 s_sweeper->deleteLater(); 0129 s_sweeper = nullptr; 0130 } 0131 } 0132 0133 // static 0134 QPixmap *PaintBuffer::grab(QSize s) 0135 { 0136 if (!s_avail) { 0137 s_avail = new QStack<PaintBuffer *>; 0138 s_grabbed = new QStack<PaintBuffer *>; 0139 s_sweeper = new BufferSweeper; 0140 } 0141 0142 if (s_sweeper->stopped()) { 0143 s_sweeper->start(); 0144 } else { 0145 s_sweeper->reset(); 0146 } 0147 0148 if (s_grabbed->count() + s_avail->count() >= maxBuffers) { 0149 if (!s_full) { 0150 s_full = new QStack<QPixmap *>; 0151 } 0152 s_full->push(new QPixmap(s.width(), s.height())); 0153 return s_full->top(); 0154 } 0155 0156 s_grabbed->push(s_avail->count() ? s_avail->pop() : new PaintBuffer); 0157 QPixmap *ret = s_grabbed->top()->getBuf(s); 0158 //qCDebug(KHTML_LOG) << "requested size:" << s << "real size:" << ret->size(); 0159 return ret; 0160 } 0161 0162 // static 0163 void PaintBuffer::release(QPixmap *px) 0164 { 0165 if (s_full && s_full->count()) { 0166 assert(px == s_full->top()); 0167 Q_UNUSED(px); 0168 delete s_full->top(); 0169 s_full->pop(); 0170 return; 0171 } 0172 assert(px == &s_grabbed->top()->m_buf); 0173 s_grabbed->top()->m_grabbed = false; 0174 s_avail->push(s_grabbed->pop()); 0175 } 0176 0177 void PaintBuffer::timerEvent(QTimerEvent *e) 0178 { 0179 assert(m_timer == e->timerId()); 0180 Q_UNUSED(e); 0181 if (m_grabbed) { 0182 return; 0183 } 0184 if (m_renewTimer) { 0185 m_renewTimer = false; 0186 return; 0187 } 0188 m_buf = QPixmap(m_resetWidth, m_resetHeight); 0189 m_resetWidth = m_resetHeight = 0; 0190 m_overflow = false; 0191 killTimer(m_timer); 0192 m_timer = 0; 0193 } 0194 0195 void PaintBuffer::reset() 0196 { 0197 if (m_grabbed | m_renewTimer) { 0198 return; 0199 } 0200 m_resetWidth = m_resetHeight = 0; 0201 m_buf = QPixmap(); 0202 m_overflow = false; 0203 if (m_timer) { 0204 killTimer(m_timer); 0205 } 0206 m_timer = 0; 0207 } 0208 0209 QPixmap *PaintBuffer::getBuf(QSize s) 0210 { 0211 assert(!m_grabbed); 0212 if (s.isEmpty()) { 0213 return nullptr; 0214 } 0215 0216 m_grabbed = true; 0217 bool cur_overflow = false; 0218 int nw = qMax(m_buf.width(), s.width()); 0219 int nh = qMax(m_buf.height(), s.height()); 0220 0221 if (!m_overflow && (nw * nh > maxPixelBuffering)) { 0222 cur_overflow = true; 0223 } 0224 0225 if (nw != m_buf.width() || nh != m_buf.height()) { 0226 m_buf = QPixmap(nw, nh); 0227 } 0228 0229 if (cur_overflow) { 0230 m_overflow = true; 0231 m_timer = startTimer(leaseTime); 0232 } else if (m_overflow) { 0233 int numPx = s.width() * s.height(); 0234 if (numPx > maxPixelBuffering) { 0235 m_renewTimer = true; 0236 } else if (numPx > m_resetWidth * m_resetHeight) { 0237 m_resetWidth = s.width(); 0238 m_resetHeight = s.height(); 0239 } 0240 } 0241 return &m_buf; 0242 } 0243 0244 QStack<PaintBuffer *> *PaintBuffer::s_avail = nullptr; 0245 QStack<PaintBuffer *> *PaintBuffer::s_grabbed = nullptr; 0246 QStack<QPixmap *> *PaintBuffer::s_full = nullptr; 0247 BufferSweeper *PaintBuffer::s_sweeper = nullptr; 0248 0249 // ### benchark me in release mode 0250 #define USE_PIXMAP_CACHE 0251 0252 // static 0253 BufferedPainter *BufferedPainter::start(QPainter *&p, const QRegion &rr) 0254 { 0255 if (rr.isEmpty()) { 0256 return nullptr; 0257 } 0258 #ifdef USE_PIXMAP_CACHE 0259 QPixmap *pm = PaintBuffer::grab(rr.boundingRect().size()); 0260 #else 0261 QPixmap *pm = new QPixmap(rr.boundingRect().size()); 0262 #endif 0263 if (!pm || pm->isNull()) { 0264 return nullptr; 0265 } 0266 return new BufferedPainter(pm, p, rr, true /*replacePainter*/); 0267 } 0268 0269 // static 0270 void BufferedPainter::end(QPainter *&p, BufferedPainter *bp, float opacity) 0271 { 0272 bp->transfer(opacity); 0273 p = bp->originalPainter(); 0274 #ifdef USE_PIXMAP_CACHE 0275 PaintBuffer::release(bp->buffer()); 0276 #else 0277 delete bp->buffer(); 0278 #endif 0279 delete bp; 0280 } 0281 0282 #include "moc_paintbuffer.cpp"