diff -r 6aeb7a756187 -r 3c88a81ff781 ginebra2/ContentViews/TiledWebView.cpp --- /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 +#include +#include +#include +#include +#include +#include + + + +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(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 TiledWebView::findTileLine4Update(bool dirty, bool inView, bool useScrollDirection) const +{ + QRectF vpRect = viewPortRect(); + QRectF updateRect = m_tilesRect; + if(inView) + updateRect = updateRect.intersect(vpRect); + QList 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(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 TiledWebView::updateViewportTiles(QList *updatedTiles) +{ + QList 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 TiledWebView::updateScrollAreaTilesChunk(QList *updatedTiles, bool inPaint) +{ + QList dirtyRects; + QList 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 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 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& dirtyTiles) +{ + QGraphicsScene* s = scene(); + QList 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 updatedTileRects; + QList updatedTiles; + if(!m_inUpdate && !m_tilesFrozen) { + QList 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 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