--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ginebra2/ContentViews/TiledWebView.cpp Fri Oct 15 17:30:59 2010 -0400
@@ -0,0 +1,1108 @@
+
+#include "TiledWebView.h"
+
+#include <QPainter>
+#include <QPixmap>
+#include <QStyleOptionGraphicsItem>
+#include <QWebFrame>
+#include <QApplication>
+#include <QGraphicsView>
+#include <QGraphicsSceneResizeEvent>
+
+
+
+const int cTileSize = 64;
+const qreal cBigSideTileOverHead = 2;
+const qreal cSmallSideTileOverHead = 2.5;
+const int cTileUpdateTimerTick = 50;
+const int cPaintIdleTimeout = cTileUpdateTimerTick * 2;
+const int cTileRectRecenterTimeout = 500;
+const int cTileScaleUpdateTimeout = 250;
+const int cIdleTileUpdateChunkSize = 4;
+const int cInPaintTileUpdateTimeout = 18;
+
+
+TiledWebView::TiledWebView(QGraphicsItem* parent) : QGraphicsWebView(parent)
+{
+ m_tilesPool.clear();
+ m_tilesField = 0;
+ m_inUpdate = false;
+ m_tilesRectCentered = false;
+ m_tilesFrozen = false;
+ m_needViewportTilesUpdate = false;
+ m_needScaleCommit = false;
+ m_needTilesFieldRebuild = false;
+ m_lastScrollDelta = QPoint(0, 0);
+#ifdef USE_ASSISTANT_ITEM
+ m_assistant = new TiledWebViewAssistant();
+ m_assistant->setParentItem(this);
+ m_assistant->m_master = this;
+
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+ setAttribute(Qt::WA_OpaquePaintEvent, true);
+#endif
+
+ connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateTimeout()));
+ connect(this, SIGNAL(scaleChanged()), this, SLOT(scheduleScaleUpdate()));
+ m_userPaintTS.start();
+
+}
+
+TiledWebView::~TiledWebView()
+{
+ delete[] m_tilesField;
+ m_tilesField = 0;
+ for(int i = 0; i < m_tilesPool.count(); i++) {
+ delete m_tilesPool[i];
+ m_tilesPool[i] = 0;
+ }
+}
+
+void TiledWebView::loadStarted()
+{
+// resetTiles(QRect(QPoint(0, 0), m_tilesDim), false);
+ m_needViewportTilesUpdate = true;
+
+ startUpdateTimer();
+}
+
+void TiledWebView::setPage(QWebPage* page)
+{
+ bool doneOnce = false;
+
+ if(!doneOnce) {
+ doneOnce = true;
+ page->setProperty("_q_RepaintThrottlingPreset", QVariant::fromValue(QString("Medium")));
+ }
+
+ page->setProperty("_q_HTMLTokenizerChunkSize", 1024);
+ page->setProperty("_q_HTMLTokenizerTimeDelay", 0.750);
+ resetTiles(QRect(QPoint(0, 0), m_tilesDim), false);
+ m_needViewportTilesUpdate = true;
+
+ QGraphicsWebView::setPage(page);
+
+ QPalette pal = palette();
+ pal.setColor(QPalette::ButtonText,Qt::black);
+ pal.setBrush(QPalette::Base, Qt::white);
+ setPalette(pal);
+ page->setPalette(pal);
+
+ connect(page, SIGNAL(repaintRequested(QRect)), this, SLOT(repaintRequested(QRect)));
+ connect(page, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
+ m_needViewportTilesUpdate = true;
+ startUpdateTimer();
+}
+
+//#define TILEPOOL_DEBUG
+TiledWebView::Tile::Tile() : img(cTileSize,cTileSize), ready(0), used(0)
+{
+}
+
+TiledWebView::Tile* TiledWebView::tileAt(const QPoint& p) const
+{
+ Q_ASSERT(p.x() < m_tilesDim.width() && p.y() < m_tilesDim.height());
+ Q_ASSERT(m_tilesField);
+
+ return m_tilesField[m_tilesDim.width() * p.y() + p.x()];
+}
+
+void TiledWebView::setTileAt(const QPoint& p,Tile* t)
+{
+ m_tilesField[m_tilesDim.width() * p.y() + p.x()] = t;
+}
+
+static void boundPoint(const QPoint& min, QPoint& p, const QPoint &max)
+{
+ p.setX(qBound(min.x(),p.x(),max.x()));
+ p.setY(qBound(min.y(),p.y(),max.y()));
+}
+
+static QPoint topPoint(const QSize& s)
+{
+ return QPoint(s.width() - 1, s.height() - 1);
+}
+
+TiledWebView::Tile* TiledWebView::createTile(const QPoint& p)
+{
+ int i;
+ for(i = 0; i < m_tilesPool.count(); i++)
+ if(!m_tilesPool[i] || !m_tilesPool[i]->used)
+ break;
+
+ Tile* ret;
+ if(i < m_tilesPool.count()) {
+ if(!m_tilesPool[i]) {
+ m_tilesPool[i] = new Tile();
+ }
+ setTileAt(p, m_tilesPool[i]);
+ ret = m_tilesPool[i];
+ } else {
+ ret = new Tile();
+ m_tilesPool.append(ret);
+ setTileAt(p, ret);
+ }
+
+ ret->used = true;
+
+#ifdef TILEPOOL_DEBUG
+ checkTilesField();
+#endif
+ return ret;
+}
+
+QPoint TiledWebView::tileAtPoint(const QPointF& p) const
+{
+ QPointF tmp = mapToTileCoords(p - m_tilesRect.topLeft());
+ tmp /= cTileSize;
+ QPoint ret((int)tmp.x(),(int)tmp.y());
+ return ret;
+}
+
+QRectF TiledWebView::tileRect(const QPoint& t) const
+{
+ QRectF tRect = mapToTileCoords(m_tilesRect);
+ QRectF ret(tRect.topLeft() + t * cTileSize,QSizeF(cTileSize,cTileSize));
+
+ return ret;
+}
+
+static int calcD(const QPoint p1, QPoint p2)
+{
+ int d1 = p2.x() - p1.x();
+ int d2 = p2.y() - p1.y();
+ return d1 * d1 + d2 * d2;
+}
+QRectF TiledWebView::viewPortRect() const
+{
+ return mapRectFromParent(QRectF(QPointF(0, 0),static_cast<QGraphicsWidget*>(parentItem())->size()));
+}
+
+void TiledWebView::boundTile(QPoint& t) const
+{
+ boundPoint(QPoint(0,0),t,topPoint(m_tilesDim));
+}
+
+QPoint TiledWebView::findTile4Update(bool inView, bool addDirty) const
+{
+ QRectF vpRect = viewPortRect();
+ QRectF updateRect = m_tilesRect;
+ if(inView)
+ updateRect = updateRect.intersect(vpRect);
+ QPoint found(-1, -1);
+ if(updateRect.isEmpty()) return found;
+
+ QPoint topLeft = tileAtPoint(updateRect.topLeft());
+ QPoint bottomRight = tileAtPoint(updateRect.bottomRight());
+ QPoint center = tileAtPoint(vpRect.center());
+
+ bottomRight += QPoint(1, 1);
+ boundTile(topLeft);
+ boundTile(bottomRight);
+ boundPoint(topLeft,center,bottomRight);
+
+ int d = 1000000; // just big enough value;
+ int tmpD;
+ for(int j = topLeft.y(); j <= bottomRight.y(); j++)
+ for(int i = topLeft.x(); i <= bottomRight.x(); i++) {
+ QPoint p(i,j);
+ Tile* t = tileAt(p);
+ if(!t || !t->ready || (addDirty && !t->dirtyRect.isEmpty())) {
+ tmpD = calcD(center, p);
+ if(tmpD < d) {
+ d = tmpD;
+ found = p;
+ }
+ }
+ }
+
+ return found;
+}
+
+QList<QPoint> TiledWebView::findTileLine4Update(bool dirty, bool inView, bool useScrollDirection) const
+{
+ QRectF vpRect = viewPortRect();
+ QRectF updateRect = m_tilesRect;
+ if(inView)
+ updateRect = updateRect.intersect(vpRect);
+ QList<QPoint> ret;
+ if(updateRect.isEmpty()) return ret;
+
+ QPoint topLeft = tileAtPoint(updateRect.topLeft());
+ QPoint bottomRight = tileAtPoint(updateRect.bottomRight());
+ QPoint topVPLeft = tileAtPoint(vpRect.topLeft());
+ QPoint bottomVPRight = tileAtPoint(vpRect.bottomRight());
+ QPoint center = tileAtPoint(vpRect.center());
+
+ bottomRight += QPoint(1, 1);
+ bottomVPRight += QPoint(1, 1);
+ boundTile(topLeft);
+ boundTile(bottomRight);
+ boundTile(topVPLeft);
+ boundTile(bottomVPRight);
+ boundPoint(topLeft,center,bottomRight);
+
+ int maxCount = qAbs(topLeft.x() - center.x());
+ maxCount = qMax(maxCount, qAbs(topLeft.y() - center.y()));
+ maxCount = qMax(maxCount, qAbs(bottomRight.x() - center.x()));
+ maxCount = qMax(maxCount, qAbs(bottomRight.y() - center.y()));
+
+ for(int i = 0; i < maxCount; i++) {
+ int j;
+ if((m_lastScrollDelta.y() > 0 || !useScrollDirection) &&
+ center.y() + i <= bottomRight.y())
+ for(j = topVPLeft.x(); j <= bottomVPRight.x(); j++) {
+ QPoint p(j, center.y() + i);
+ Tile* t = tileAt(p);
+ if(!t || !t->ready || (dirty && !t->dirtyRect.isEmpty())) {
+ ret += p;
+ }
+ }
+ if(!ret.isEmpty())
+ break;
+
+ if((m_lastScrollDelta.y() < 0 || !useScrollDirection) &&
+ center.y() - i >= topLeft.y())
+ for(j = topVPLeft.x(); j <= bottomVPRight.x(); j++) {
+ QPoint p(j, center.y() - i);
+ Tile* t = tileAt(p);
+ if(!t || !t->ready || (dirty && !t->dirtyRect.isEmpty())) {
+ ret += p;
+ }
+ }
+ if(!ret.isEmpty())
+ break;
+
+ if((m_lastScrollDelta.x() > 0 || !useScrollDirection) &&
+ center.x() + i <= bottomRight.x())
+ for(j = topVPLeft.y(); j <= bottomVPRight.y(); j++) {
+ QPoint p(center.x() + i, j);
+ Tile* t = tileAt(p);
+ if(!t || !t->ready || (dirty && !t->dirtyRect.isEmpty())) {
+ ret += p;
+ }
+ }
+ if(!ret.isEmpty())
+ break;
+
+ if((m_lastScrollDelta.x() < 0 || !useScrollDirection) &&
+ center.x() - i >= topLeft.x())
+ for(j = topVPLeft.y(); j <= bottomVPRight.y(); j++) {
+ QPoint p(center.x() - i, j);
+ Tile* t = tileAt(p);
+ if(!t || !t->ready || (dirty && !t->dirtyRect.isEmpty())) {
+ ret += p;
+ }
+ }
+ if(!ret.isEmpty())
+ break;
+ }
+
+ return ret;
+}
+
+QRectF TiledWebView::updateTile(const QPoint& t)
+{
+ m_inUpdate = true;
+ Tile* tile = tileAt(t);
+ if(!tile) tile = createTile(t);
+
+ QPainter p(&(tile->img));
+ QRectF tRect = mapFromTileCoords(tileRect(t));
+ QRectF tDirtyRect = mapFromTileCoords(tile->dirtyRect);
+ QRectF updateRect = !tile->ready || tDirtyRect.isEmpty() ? tRect : tDirtyRect;
+
+
+ //if(!tile->ready)
+ // p.fillRect(0, 0, cTileSize, cTileSize, Qt::white);
+
+ tile->ready = true;
+ tile->dirtyRect = QRectF();
+
+ p.scale(m_tilesScale,m_tilesScale);
+
+ qreal adjust = 2 + m_tilesScale;
+
+ // adjust rect to cover rouding errors
+ updateRect.adjust(-adjust, -adjust, adjust, adjust);
+
+ QRegion clip(updateRect.toRect());
+ p.translate(-tRect.topLeft());
+ p.setClipRegion(clip);
+// p.fillRect(clip.boundingRect(), Qt::white);
+
+// p.setRenderHint(QPainter::SmoothPixmapTransform); // cause assert now
+// p.setRenderHint(QPainter::Antialiasing);
+
+ page()->mainFrame()->render(&p, QWebFrame::ContentsLayer, clip);
+
+ m_inUpdate = false;
+ return updateRect;
+}
+
+void TiledWebView::repaintRequested(QRect r)
+{
+ if(!m_tilesField) return;
+
+ if(r.isEmpty())
+ r = m_tilesRect.adjusted(-1, -1, 1, 1).toRect();
+
+ QPoint topLeftTile = tileAtPoint(r.topLeft());
+ QPoint bottomRightTile = tileAtPoint(r.bottomRight());
+ boundTile(topLeftTile);
+ boundTile(bottomRightTile);
+ QRectF repaintClip = mapToTileCoords(r);
+ bool needUpdate = false;
+ QRectF vpRect = mapToTileCoords(viewPortRect());
+
+ for(int j = topLeftTile.y(); j <= bottomRightTile.y(); j++)
+ for(int i = topLeftTile.x(); i <= bottomRightTile.x(); i++) {
+ QPoint t(i, j);
+ QRectF clip = tileRect(t) & repaintClip;
+ if(!clip.isEmpty()) {
+
+ Tile* tile = tileAt(t);
+ if(tile && tile->ready) {
+ if(!tile->dirtyRect.isEmpty())
+ clip |= tile->dirtyRect;
+ tile->dirtyRect = clip;
+// if(vpRect.intersects(tile->dirtyRect)) {
+// m_needViewportTilesUpdate = true;
+// }
+ }
+ needUpdate = true;
+ }
+ }
+
+ if(needUpdate)
+ startUpdateTimer();
+
+}
+
+QPixmap* TiledWebView::getUnprepPixmap()
+{
+ static const int squareMult = 1;
+ static const int cellSize = 16;
+ static QPixmap pixmap(squareMult * cellSize, squareMult * cellSize);
+ static bool init = false;
+
+ if(!init) {
+ init = true;
+ QPainter p(&pixmap);
+ p.fillRect(pixmap.rect(),Qt::lightGray);
+ // p.setPen(QColor(182,242,255));
+ // p.setPen(QColor(Qt::darkBlue));
+ p.setPen(Qt::darkGray);
+ for(int i = -1; i < squareMult + 1; i++) {
+ int xoffs = i * cellSize;
+ for(int j = -1; j < squareMult + 1; j++) {
+ int yoffs = j * cellSize;
+ p.drawLine(xoffs + 7, yoffs - 4, xoffs + 7, yoffs + 2);
+ p.drawLine(xoffs + 4, yoffs + 7, xoffs + 10, yoffs + 7);
+ p.drawLine(xoffs + 15, yoffs + 4, xoffs + 15, yoffs + 10);
+ p.drawLine(xoffs + 12, yoffs + 15, xoffs + 18, yoffs + 15);
+ }
+ }
+ }
+
+ return &pixmap;
+}
+
+QSize TiledWebView::getTileFieldDim()
+{
+ QSizeF vpSize = static_cast<QGraphicsWidget*>(parentItem())->size();
+ qreal heightMult = cBigSideTileOverHead;
+ qreal widthMult = cSmallSideTileOverHead;
+ if(vpSize.width() > vpSize.height())
+ qSwap(widthMult, heightMult);
+ return QSize((int)((vpSize.width() * widthMult + cTileSize) / cTileSize),
+ (int)((vpSize.height() * heightMult + cTileSize) / cTileSize));
+}
+
+void TiledWebView::createTileField()
+{
+ QRectF vpRect = viewPortRect();
+
+ m_tilesDim = getTileFieldDim();
+ m_tilesPool.reserve(m_tilesDim.width() * m_tilesDim.height());
+
+
+ m_tilesField = new Tile*[m_tilesDim.width() * m_tilesDim.height()];
+ memset(m_tilesField, 0, sizeof(Tile*) * m_tilesDim.width() * m_tilesDim.height());
+ m_tilesScale = scale();
+
+ adjustTilesToViewPort(true);// mapFromTileCoords(QRectF(QPointF(3, 5) * cTileSize, m_tilesDim * cTileSize));
+}
+
+QRectF TiledWebView::validateTileRect(const QRectF& rect, const QSize& dim) const
+{
+ QRectF ret(rect);
+ QRectF vpRect = viewPortRect();
+ qreal tileSize = cTileSize / m_tilesScale;
+
+ if(ret.bottom() > size().height() + tileSize)
+ ret.moveBottom(size().height() + tileSize);
+ if(ret.top() < 0)
+ ret.moveTop(0);
+
+ if(ret.right() > size().width() + tileSize)
+ ret.moveRight(size().width() + tileSize);
+ if(ret.left() < 0)
+ ret.moveLeft(0);
+
+ if(ret.width() < vpRect.width())
+ ret.setLeft(0);
+
+ if(ret.height() < vpRect.height())
+ ret.setTop(0);
+
+ QPointF p = mapToTileCoords(ret.topLeft());
+ // allign coordinates to tile boundary
+ QPoint pp = (p / cTileSize).toPoint();
+ p = QPointF(pp) * cTileSize;
+
+ return mapFromTileCoords(QRectF(p, dim * cTileSize));
+}
+
+QRectF TiledWebView::adjustedTileRect(const QSize& dim) const
+{
+ QRectF ret(m_tilesRect);
+ if(ret.isEmpty())
+ ret = QRectF(QPoint(0,0),mapFromTileCoords(dim * cTileSize));
+ // no repositioning and scaling and tile dropping during scaling
+ if(!qFuzzyCompare(m_tilesScale,scale()))
+ return ret;
+
+ QRectF vpRect = viewPortRect();
+ qreal tileSize = cTileSize / m_tilesScale;
+ if(vpRect.bottom() > ret.bottom())
+ ret.moveTop(vpRect.top() - tileSize);
+ else if(vpRect.top() < ret.top())
+ ret.moveBottom(vpRect.bottom() + tileSize);
+
+ if(vpRect.right() > ret.right())
+ ret.moveLeft(vpRect.left() - tileSize);
+ else if(vpRect.left() < ret.left())
+ ret.moveRight(vpRect.right() + tileSize);
+
+ return validateTileRect(ret, dim);
+}
+
+QRectF TiledWebView::centeredTileRect(const QSize& dim) const
+{
+ QRectF vpRect = viewPortRect();
+ QSizeF tilesSize = mapFromTileCoords(dim * cTileSize);
+ QPoint centerOffset(tilesSize.width() / 2, tilesSize.height() / 2);
+ QRectF centeredRect(vpRect.center() - centerOffset,tilesSize);
+
+ return validateTileRect(centeredRect, dim);
+}
+
+void TiledWebView::adjustTilesToViewPort(bool center)
+{
+ QRectF newTilesRect = center ? centeredTileRect(m_tilesDim) :
+ adjustedTileRect(m_tilesDim);
+ m_tilesRectCentered = center;
+
+ moveTilesRect(newTilesRect);
+}
+
+void TiledWebView::moveTilesRect(const QRectF& newTilesRect)
+{
+ QRectF trNew = mapToTileCoords(newTilesRect);
+ QRectF trOld = mapToTileCoords(m_tilesRect);
+
+ if(trNew == trOld) return;
+
+ if(trNew.intersects(trOld)) {
+ QPoint trDiff = ((trNew.topLeft() - trOld.topLeft()) / cTileSize).toPoint();
+ scrollTileField(-trDiff);
+ } else {
+ resetTiles(QRect(QPoint(0,0), m_tilesDim), false);
+#ifdef TILEPOOL_DEBUG
+ checkTilesField();
+#endif
+ }
+ m_tilesRect = newTilesRect;
+}
+
+void TiledWebView::resetTiles(const QRect& r, bool remove)
+{
+ for(int j = r.top(); j <= r.bottom(); j++)
+ for(int i = r.left(); i <= r.right(); i++) {
+ QPoint t(i, j);
+ Tile* tile = tileAt(t);
+ if(tile) {
+ tile->ready = false;
+ tile->dirtyRect = QRect();
+ if(remove) {
+ tile->used = false;
+ setTileAt(t, 0);
+ }
+ }
+ }
+}
+
+void TiledWebView::scrollTileField(const QPoint& diff)
+{
+#ifdef TILEPOOL_DEBUG
+ checkTilesField();
+#endif
+ if(qAbs(diff.x()) > m_tilesDim.width() || qAbs(diff.y()) > m_tilesDim.height())
+ return;
+
+ if(diff.x() > 0) {
+ resetTiles(QRect(QPoint(m_tilesDim.width() - diff.x(),0),
+ QSize(diff.x(), m_tilesDim.height())), true);
+
+ for(int i = m_tilesDim.width() - diff.x() - 1; i >= 0; i--) {
+ int dstNum = i + diff.x();
+
+ for(int j = 0; j < m_tilesDim.height(); j++) {
+ setTileAt(dstNum, j, tileAt(i, j));
+ setTileAt(i, j, 0);
+ }
+ }
+ } else if(diff.x() < 0) {
+ resetTiles(QRect(QPoint(0,0),
+ QSize(-diff.x(), m_tilesDim.height())), true);
+
+ for(int i = -diff.x(); i < m_tilesDim.width(); i++) {
+ int dstNum = i + diff.x();
+
+ for(int j = 0; j < m_tilesDim.height(); j++) {
+ setTileAt(dstNum, j, tileAt(i, j));
+ setTileAt(i, j, 0);
+ }
+ }
+ }
+
+ if(diff.y() > 0) {
+ resetTiles(QRect(QPoint(0, m_tilesDim.height() - diff.y()),
+ QSize(m_tilesDim.width(), diff.y())), true);
+
+ for(int i = m_tilesDim.height() - diff.y() - 1; i >= 0; i--) {
+ int dstNum = i + diff.y();
+ Tile **srcLine = m_tilesField + m_tilesDim.width() * i;
+ Tile **dstLine = m_tilesField + m_tilesDim.width() * dstNum;
+ memcpy(dstLine, srcLine, m_tilesDim.width() * sizeof(Tile*));
+ memset(srcLine, 0, m_tilesDim.width() * sizeof(Tile*));
+ }
+ } else if(diff.y() < 0) {
+ resetTiles(QRect(QPoint(0, 0),
+ QSize(m_tilesDim.width(), -diff.y())), true);
+
+ for(int i = -diff.y(); i < m_tilesDim.height(); i++) {
+ int dstNum = i + diff.y();
+ Tile **srcLine = m_tilesField + m_tilesDim.width() * i;
+ Tile **dstLine = m_tilesField + m_tilesDim.width() * dstNum;
+ memcpy(dstLine, srcLine, m_tilesDim.width() * sizeof(Tile*));
+ memset(srcLine, 0, m_tilesDim.width() * sizeof(Tile*));
+ }
+ }
+#ifdef TILEPOOL_DEBUG
+ checkTilesField();
+#endif
+}
+
+QList<QRectF> TiledWebView::updateViewportTiles(QList<TileSet> *updatedTiles)
+{
+ QList<QRectF> ret;
+ // update all visible tiles
+ QRectF vpRect = viewPortRect();
+
+ QPoint topLeft = tileAtPoint(vpRect.topLeft());
+ QPoint bottomRight = tileAtPoint(vpRect.bottomRight());
+
+ bottomRight += QPoint(1, 1);
+ boundTile(topLeft);
+ boundTile(bottomRight);
+ for(int j = topLeft.y(); j <= bottomRight.y(); j++)
+ for(int i = topLeft.x(); i <= bottomRight.x(); i++) {
+ QPoint t(i, j);
+ Tile *tile = tileAt(t);
+ if(!tile || !tile->ready || !tile->dirtyRect.isEmpty()) {
+ QRectF r = updateTile(t);
+ ret += r;
+ if(updatedTiles)
+ *updatedTiles += TileSet(t, r);
+ }
+ }
+
+ m_needViewportTilesUpdate = false;
+ return ret;
+}
+
+void TiledWebView::doScaleCommit()
+{
+ m_needScaleCommit = false;
+ if(qFuzzyCompare(m_tilesScale, scale()))
+ return;
+
+ resetTiles(QRect(QPoint(0,0), m_tilesDim), true);
+#ifdef TILEPOOL_DEBUG
+ checkTilesField();
+#endif
+ m_tilesScale = scale();
+ adjustTilesToViewPort(true);
+ m_needViewportTilesUpdate = true;
+}
+
+void TiledWebView::commitZoom()
+{
+ m_needScaleCommit = true;
+
+ startUpdateTimer();
+}
+
+QList<QRectF> TiledWebView::updateScrollAreaTilesChunk(QList<TileSet> *updatedTiles, bool inPaint)
+{
+ QList<QRectF> dirtyRects;
+ QList<QPoint> lst = findTileLine4Update(false, true);
+ if(lst.isEmpty())
+ lst = findTileLine4Update(false, false);
+ if(lst.isEmpty())
+ lst = findTileLine4Update(false, true, false);
+// if(!inPaint) {
+ {
+ if(lst.isEmpty())
+ lst = findTileLine4Update(true, true);
+ if(lst.isEmpty())
+ lst = findTileLine4Update(true, false);
+ if(lst.isEmpty())
+ lst = findTileLine4Update(true, false, false);
+ }
+
+ QTime ts;
+ ts.start();
+ foreach(QPoint t, lst) {
+ QRectF r = updateTile(t);
+ dirtyRects += r;
+ if(updatedTiles)
+ *updatedTiles += TileSet(t, r);
+ if(inPaint && ts.elapsed() > cInPaintTileUpdateTimeout)
+ break;
+ }
+
+ return dirtyRects;
+}
+
+
+void TiledWebView::updateTimeout()
+{
+ if(m_tilesFrozen) return;
+ if(m_inUpdate) return;
+
+ int elapsed = m_userPaintTS.elapsed();
+ QList<QRectF> dirtyTiles;
+
+ if(m_needTilesFieldRebuild) {
+ doTilesFieldRebuild();
+ } else if(m_needScaleCommit) {
+ doScaleCommit();
+ } else if(m_needViewportTilesUpdate) {
+/* just do nothing, because it will update tiles below
+ dirtyTiles += updateViewportTiles(); */
+ } else if(elapsed < cPaintIdleTimeout) {
+// updateSceneRects(updateScrollAreaTilesChunk);
+
+/* QList<QRectF> rects = updateViewportTiles();
+ foreach(QRectF r, rects)
+ update(r); */
+ return;
+ }
+
+ if(elapsed > cTileScaleUpdateTimeout && !qFuzzyCompare(m_tilesScale, scale())) {
+ doScaleCommit();
+ }
+// else if(elapsed > cTileRectRecenterTimeout && !m_tilesRectCentered)
+// adjustTilesToViewPort(true);
+
+ dirtyTiles += updateViewportTiles();
+ if(dirtyTiles.isEmpty())
+ dirtyTiles = updateScrollAreaTilesChunk();
+
+ if(dirtyTiles.isEmpty())
+ for(int i = 0; i < cIdleTileUpdateChunkSize; i++) {
+ // 1st try to paint not ready tiles in view
+ QPoint oneDirtyTile = findTile4Update(true);
+ // 2nd update dirty tiles in view
+ if(oneDirtyTile.x() < 0) oneDirtyTile = findTile4Update(true, true);
+ // 3rd try to paint not ready tiles everywhere else
+ if(oneDirtyTile.x() < 0) oneDirtyTile = findTile4Update(false);
+ // 4th update all other dirty tiles
+ if(oneDirtyTile.x() < 0) oneDirtyTile = findTile4Update(false, true);
+ if(oneDirtyTile.x() >= 0)
+ dirtyTiles += updateTile(oneDirtyTile);
+ else if(/*m_tilesRectCentered && */qFuzzyCompare(m_tilesScale, scale())) {
+ stopUpdateTimer();
+ break;
+ }
+ }
+
+ m_inUpdate = true;
+
+ updateSceneRects(dirtyTiles);
+
+ m_inUpdate = false;
+
+ // restart timer if some of update flags was set during recursive calls from webkit render
+ if(m_needTilesFieldRebuild || m_needScaleCommit || m_needViewportTilesUpdate)
+ startUpdateTimer();
+}
+
+void TiledWebView::updateSceneRects(const QList<QRectF>& dirtyTiles)
+{
+ QGraphicsScene* s = scene();
+ QList<QGraphicsView*> gvList = s->views();
+
+ QRectF vpRect = viewPortRect();
+
+ foreach(QGraphicsView* v, gvList) {
+ QRegion reg;
+ foreach(QRectF r, dirtyTiles) {
+ r = r.intersected(vpRect);
+ update(r);
+ r = mapRectToScene(r);
+ r = v->mapFromScene(r).boundingRect();
+ reg += r.toRect();
+ }
+// v->repaint(reg);
+ }
+}
+
+void TiledWebView::scheduleScaleUpdate()
+{
+ if(!m_tilesField) return;
+
+ startUpdateTimer();
+}
+
+void TiledWebView::checkTilesField()
+{
+ int usedCount1 = 0;
+ for(int j = 0; j < m_tilesDim.height(); j++)
+ for(int i = 0; i < m_tilesDim.width(); i++) {
+ Tile* t = tileAt(i, j);
+ if(t) {
+ Q_ASSERT(t->used);
+ usedCount1++;
+ }
+ }
+
+ int usedCount2 = 0;
+ for(int i = 0; i < m_tilesPool.count(); i++)
+ if(m_tilesPool[i] && m_tilesPool[i]->used)
+ usedCount2++;
+
+ Q_ASSERT(usedCount1 == usedCount2);
+}
+
+void TiledWebView::doTilesFieldRebuild()
+{
+ QSize oldDim = m_tilesDim;
+ QSize newDim = getTileFieldDim();
+
+ if(!qFuzzyCompare(m_tilesScale, scale())) {
+ resetTiles(QRect(QPoint(0,0), m_tilesDim), true);
+ m_tilesScale = scale();
+
+ QRectF newRect = adjustedTileRect(newDim);
+
+ Tile** newField = new Tile*[newDim.width() * newDim.height()];
+ memset(newField, 0, sizeof(Tile*) * newDim.width() * newDim.height());
+ delete[] m_tilesField;
+ m_tilesField = newField;
+ m_tilesDim = newDim;
+ m_tilesRect = newRect;
+
+ m_needViewportTilesUpdate = true;
+
+ } else {
+ Tile** oldField = m_tilesField;
+ QRectF newRect = adjustedTileRect(newDim);
+
+ QRectF trNew = mapToTileCoords(newRect);
+ QRectF trOld = mapToTileCoords(m_tilesRect);
+
+ if(trNew != trOld) {
+
+ Tile** newField = new Tile*[newDim.width() * newDim.height()];
+ memset(newField, 0, sizeof(Tile*) * newDim.width() * newDim.height());
+ QRectF trCommon = trNew.intersect(trOld);
+
+ if(!trCommon.isEmpty()) {
+ QSize copySize = (trCommon.size() / cTileSize).toSize();
+ QPoint oldOffs = ((trCommon.topLeft() - trOld.topLeft()) / cTileSize).toPoint();
+ QPoint newOffs = ((trCommon.topLeft() - trNew.topLeft()) / cTileSize).toPoint();
+ if(trNew.size().width() - newOffs.x() < copySize.width())
+ copySize.setWidth(trNew.size().width() - newOffs.x());
+ if(trNew.size().height() - newOffs.y() < copySize.height())
+ copySize.setHeight(trNew.size().height() - newOffs.y());
+ if(trOld.size().width() - oldOffs.x() < copySize.width())
+ copySize.setWidth(trOld.size().width() - oldOffs.x());
+ if(trOld.size().height() - oldOffs.y() < copySize.height())
+ copySize.setHeight(trOld.size().height() - oldOffs.y());
+
+ for(int j = 0; j < copySize.height(); j++)
+ for(int i = 0; i < copySize.width(); i++) {
+ QPoint cPoint(i, j);
+ QPoint oldPos = cPoint + oldOffs;
+ Tile *tile = tileAt(oldPos);
+ setTileAt(oldPos, 0);
+ QPoint newPos = cPoint + newOffs;
+ Q_ASSERT(newPos.x() >= 0 && newPos.y() >=0 &&
+ newPos.x() < newDim.width() && newPos.y() < newDim.height());
+ *(newField + newPos.y() * newDim.width() + newPos.x()) = tile;
+ }
+ }
+
+ // release remaining tiles in old field
+ resetTiles(QRect(QPoint(0,0), m_tilesDim), true);
+
+ delete[] m_tilesField;
+ m_tilesField = newField;
+ m_tilesDim = newDim;
+ m_tilesRect = newRect;
+
+ int newTileCount = m_tilesDim.height() * m_tilesDim.width();
+ while(m_tilesPool.count() > newTileCount) {
+ bool deleted = false;
+ for(int i = m_tilesPool.count() - 1; i >= 0; i--) {
+ Tile* tile = m_tilesPool[i];
+ if(!tile->used) {
+ deleted = true;
+ if(tile) delete tile;
+ m_tilesPool.remove(i);
+ break;
+ }
+ }
+ Q_ASSERT(deleted);
+ }
+
+ m_needViewportTilesUpdate = true;
+ }
+ }
+
+ m_needTilesFieldRebuild = false;
+}
+
+void TiledWebView::viewportUpdated()
+{
+ if(!m_tilesField) {
+ createTileField();
+ commitZoom();
+ }
+ else {
+ m_needTilesFieldRebuild = true;
+ startUpdateTimer();
+ }
+}
+
+// #define DRAW_TILE_BOUNDS
+void TiledWebView::paintTile(QPainter* painter, const QPoint& t, const QRectF& clipRect, QRegion& dirtyRegion)
+{
+ QRectF tRectOrig = tileRect(t);
+ qreal adjust = 1; // + m_tilesScale;
+ QRectF tRect = tRectOrig.adjusted(-adjust, -adjust, adjust, adjust);
+ QRectF drawRect = clipRect.intersected(tRect);
+ //painter->drawPixmap(tRectOrig,tileAt(t)->img, tRectOrig.translated(-tRectOrig.topLeft()));
+ painter->drawPixmap(tRectOrig.topLeft(),tileAt(t)->img);
+#ifdef DRAW_TILE_BOUNDS
+ painter->setPen(Qt::red);
+ painter->drawRect(tileRect(t));
+#endif // DRAW_TILE_BOUNDS
+ dirtyRegion = dirtyRegion.subtract(QRegion(mapFromTileCoords(drawRect).toRect()));
+}
+
+void TiledWebView::paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget)
+{
+
+ if(!m_tilesField) {
+ QGraphicsWebView::paint(painter, options, widget );
+ return;
+ }
+
+ painter->save();
+ QRectF clipRect = viewPortRect().adjusted(-1, -1, 1, 1);
+ if(options && !options->exposedRect.isEmpty())
+ clipRect &= options->exposedRect;
+
+ QList<QRectF> updatedTileRects;
+ QList<TileSet> updatedTiles;
+ if(!m_inUpdate && !m_tilesFrozen) {
+ QList<QRectF> lst;
+ if(m_userPaintTS.elapsed() > cPaintIdleTimeout || m_needViewportTilesUpdate) {
+ lst = updateViewportTiles(&updatedTiles);
+ m_needViewportTilesUpdate = false;
+ } else
+ lst = updateScrollAreaTilesChunk(&updatedTiles, true);
+ QRectF vpRect = viewPortRect().adjusted(-1, -1, 1, 1);
+ foreach(QRectF r, lst) {
+ r = r.intersected(vpRect);
+ if(!r.isEmpty()) {
+ if(clipRect.contains(r)) {
+ // do nothing, it will be updated in any case
+ } else if(r.contains(clipRect)) {
+ clipRect = r;
+ } else if(clipRect.intersects(r)) {
+ clipRect = clipRect.unite(r);
+ } else {
+ updatedTileRects += r;
+ }
+ }
+ }
+ }
+
+ painter->setBackgroundMode(Qt::OpaqueMode);
+
+ painter->setClipRect(clipRect, Qt::IntersectClip);
+
+ QRectF tileClipRect = mapToTileCoords(clipRect);
+ QRegion dirtyRgn(clipRect.toRect());
+ QPoint topLeftTile = tileAtPoint(clipRect.topLeft());
+ QPoint rightBottomTile = tileAtPoint(clipRect.bottomRight());
+
+ boundPoint(QPoint(0,0),topLeftTile,topPoint(m_tilesDim));
+ boundPoint(QPoint(0,0),rightBottomTile,topPoint(m_tilesDim));
+
+ qreal sc = scale();
+ QPointF p = pos() / sc;
+
+ QRectF scr(clipRect.topLeft() + p, clipRect.size() * sc);
+
+ QRegion notReadyClip(scr.toRect()); //.adjusted(-2, -2, 2, 2).toRect());
+ for(int j = topLeftTile.y(); j <= rightBottomTile.y(); j++)
+ for(int i = topLeftTile.x(); i <= rightBottomTile.x(); i++) {
+ QPoint t(i,j);
+ if(tileAt(t) && tileAt(t)->ready) {
+ QRectF r = mapFromTileCoords(tileRect(t));
+ r = QRectF(r.topLeft() * sc, r.size() * sc);
+ r.translate(pos());
+ notReadyClip = notReadyClip.subtract(r.toRect());
+// painter->setPen(Qt::red);
+// painter->drawRect(r);
+ }
+ }
+
+// painter->setPen(Qt::red);
+// painter->drawRect(scr.adjusted(10,10,-10,-10));
+
+ QVector<QRect> rList = notReadyClip.rects();
+ if(!rList.isEmpty()) {
+ painter->save();
+ painter->translate(-p);
+ painter->scale(1/sc, 1/sc);
+ foreach(QRect r, rList) {
+ painter->fillRect(r, QBrush(*getUnprepPixmap()));
+ // painter->setPen(Qt::red);
+ // painter->drawRect(r.adjusted(10, 10, -10, -10));
+ }
+ painter->restore();
+ }
+
+ painter->scale(1 / m_tilesScale, 1 / m_tilesScale);
+
+ for(int j = topLeftTile.y(); j <= rightBottomTile.y(); j++)
+ for(int i = topLeftTile.x(); i <= rightBottomTile.x(); i++) {
+ QPoint t(i,j);
+ if(tileAt(t) && tileAt(t)->ready)
+ paintTile(painter, t, tileClipRect, dirtyRgn);
+ }
+ QRect clippedRectTiles(topLeftTile,rightBottomTile);
+ foreach(TileSet ts, updatedTiles)
+ if(!clippedRectTiles.contains(ts.t))
+ paintTile(painter, ts.t, ts.r, dirtyRgn);
+
+// if(!m_inUpdate)
+// m_userPaintTS.start();
+
+ if(!m_tilesFrozen && !m_tilesRect.contains(viewPortRect()) && qFuzzyCompare(scale(),m_tilesScale)) {
+ adjustTilesToViewPort();
+ startUpdateTimer();
+ }
+/* painter->setPen(Qt::red);
+ painter->drawLine(0, 0, 100, 100);
+*/
+ painter->restore();
+}
+
+void TiledWebView::setTiledBackingStoreFrozen(bool frozen)
+{
+ m_tilesFrozen = frozen;
+ if(frozen) {
+ if(m_updateTimer.isActive())
+ stopUpdateTimer();
+ } else {
+ if(!qFuzzyCompare(scale(),m_tilesScale))
+ commitZoom();
+ else {
+ // m_needViewportTilesUpdate = true;
+ m_tilesRectCentered = false;
+ }
+
+ startUpdateTimer();
+ }
+}
+
+void TiledWebView::startUpdateTimer()
+{
+ if(!m_updateTimer.isActive() && !m_tilesFrozen) {
+ m_updateTimer.start(cTileUpdateTimerTick);
+ }
+}
+
+void TiledWebView::stopUpdateTimer()
+{
+ m_updateTimer.stop();
+}
+
+void TiledWebView::userActivity()
+{
+ m_userPaintTS.start();
+}
+
+void TiledWebView::viewScrolled(QPoint& scrollPos, QPoint& delta)
+{
+ m_lastScrollDelta = delta;
+
+ userActivity();
+ if(!m_tilesField) return;
+
+ QRectF ret(m_tilesRect);
+ if(ret.isEmpty())
+ ret = QRectF(QPoint(0,0),mapFromTileCoords(m_tilesDim * cTileSize));
+ // no repositioning and scaling and tile dropping during scaling
+ if(!qFuzzyCompare(m_tilesScale,scale()))
+ return;
+
+ QRectF vpRect = viewPortRect();
+ qreal tileSize = cTileSize / m_tilesScale;
+ vpRect.adjust(-tileSize, -tileSize, tileSize, tileSize);
+ if(vpRect.bottom() > ret.bottom() && delta.y() > 0)
+ ret.moveTop(vpRect.top() - tileSize);
+ else if(vpRect.top() < ret.top() && delta.y() < 0)
+ ret.moveBottom(vpRect.bottom() + tileSize);
+
+ if(vpRect.right() > ret.right() && delta.x() > 0)
+ ret.moveLeft(vpRect.left() - tileSize);
+ else if(vpRect.left() < ret.left() & delta.x() < 0)
+ ret.moveRight(vpRect.right() + tileSize);
+
+ ret = validateTileRect(ret, m_tilesDim);
+
+ moveTilesRect(ret);
+}
+
+#ifdef USE_ASSISTANT_ITEM
+
+void TiledWebView::resizeEvent(QGraphicsSceneResizeEvent *event)
+{
+ m_assistant->setGeometry(QRectF(QPoint(0,0), event->newSize()));
+}
+
+void TiledWebViewAssistant::paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget)
+{
+ m_master->paint(painter, options, widget);
+}
+#endif