File indexing completed on 2024-04-14 11:05:41

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"