OSDN Git Service

Tweak ring drawing
[android-x86/external-webkit.git] / WebCore / platform / graphics / android / GLWebViewState.cpp
1 /*
2  * Copyright 2010, The Android Open Source Project
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "GLWebViewState.h"
28
29 #if USE(ACCELERATED_COMPOSITING)
30
31 #include "BaseLayerAndroid.h"
32 #include "ClassTracker.h"
33 #include "GLUtils.h"
34 #include "LayerAndroid.h"
35 #include "TilesManager.h"
36 #include <wtf/CurrentTime.h>
37
38 #include <cutils/log.h>
39 #include <wtf/text/CString.h>
40
41 #undef XLOGC
42 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
43
44 #ifdef DEBUG
45
46 #undef XLOG
47 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
48
49 #else
50
51 #undef XLOG
52 #define XLOG(...)
53
54 #endif // DEBUG
55
56 #define FIRST_TILED_PAGE_ID 1
57 #define SECOND_TILED_PAGE_ID 2
58
59 #define FRAMERATE_CAP 0.01666 // We cap at 60 fps
60
61 namespace WebCore {
62
63 using namespace android;
64
65 GLWebViewState::GLWebViewState(android::Mutex* buttonMutex)
66     : m_scaleRequestState(kNoScaleRequest)
67     , m_currentScale(1)
68     , m_futureScale(1)
69     , m_updateTime(-1)
70     , m_transitionTime(-1)
71     , m_baseLayer(0)
72     , m_currentBaseLayer(0)
73     , m_previouslyUsedRoot(0)
74     , m_currentPictureCounter(0)
75     , m_usePageA(true)
76     , m_frameworkInval(0, 0, 0, 0)
77     , m_frameworkLayersInval(0, 0, 0, 0)
78     , m_globalButtonMutex(buttonMutex)
79     , m_baseLayerUpdate(true)
80     , m_backgroundColor(SK_ColorWHITE)
81     , m_prevDrawTime(0)
82     , m_displayRings(false)
83     , m_focusRingTexture(-1)
84 {
85     m_viewport.setEmpty();
86     m_previousViewport.setEmpty();
87     m_futureViewportTileBounds.setEmpty();
88     m_viewportTileBounds.setEmpty();
89     m_preZoomBounds.setEmpty();
90
91     m_tiledPageA = new TiledPage(FIRST_TILED_PAGE_ID, this);
92     m_tiledPageB = new TiledPage(SECOND_TILED_PAGE_ID, this);
93 #ifdef DEBUG_COUNT
94     ClassTracker::instance()->increment("GLWebViewState");
95 #endif
96 #ifdef MEASURES_PERF
97     m_timeCounter = 0;
98     m_totalTimeCounter = 0;
99     m_measurePerfs = false;
100 #endif
101 }
102
103 GLWebViewState::~GLWebViewState()
104 {
105     // We have to destroy the two tiled pages first as their destructor
106     // may depend on the existence of this GLWebViewState and some of its
107     // instance variables in order to complete.
108     // Explicitely, currently we need to have the m_currentBaseLayer around
109     // in order to complete any pending paint operations (the tiled pages
110     // will remove any pending operations, and wait if one is underway).
111     delete m_tiledPageA;
112     delete m_tiledPageB;
113     SkSafeUnref(m_currentBaseLayer);
114     SkSafeUnref(m_baseLayer);
115     m_baseLayer = 0;
116     m_currentBaseLayer = 0;
117 #ifdef DEBUG_COUNT
118     ClassTracker::instance()->decrement("GLWebViewState");
119 #endif
120 }
121
122 void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
123                                   bool showVisualIndicator, bool isPictureAfterFirstLayout)
124 {
125     android::Mutex::Autolock lock(m_baseLayerLock);
126     if (!layer || isPictureAfterFirstLayout) {
127         m_tiledPageA->setUsable(false);
128         m_tiledPageB->setUsable(false);
129     }
130     if (isPictureAfterFirstLayout) {
131         m_baseLayerUpdate = true;
132         m_invalidateRegion.setEmpty();
133     }
134     if (m_baseLayer && layer)
135         m_baseLayer->swapExtra(layer);
136
137     SkSafeRef(layer);
138     SkSafeUnref(m_baseLayer);
139     m_baseLayer = layer;
140     if (m_baseLayer)
141         m_baseLayer->setGLWebViewState(this);
142
143     // We only update the layers if we are not currently
144     // waiting for a tiledPage to be painted
145     if (m_baseLayerUpdate) {
146         SkSafeRef(layer);
147         SkSafeUnref(m_currentBaseLayer);
148         m_currentBaseLayer = layer;
149     }
150     m_displayRings = false;
151     invalRegion(inval);
152
153 #ifdef MEASURES_PERF
154     if (m_measurePerfs && !showVisualIndicator)
155         dumpMeasures();
156     m_measurePerfs = showVisualIndicator;
157 #endif
158
159     TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
160 }
161
162 void GLWebViewState::setRings(Vector<IntRect>& rings)
163 {
164     android::Mutex::Autolock lock(m_baseLayerLock);
165     m_displayRings = true;
166     m_rings = rings;
167 }
168
169 void GLWebViewState::invalRegion(const SkRegion& region)
170 {
171     SkRegion::Iterator iterator(region);
172     while (!iterator.done()) {
173         SkIRect r = iterator.rect();
174         IntRect ir(r.fLeft, r.fTop, r.width(), r.height());
175         inval(ir);
176         iterator.next();
177     }
178 }
179
180 void GLWebViewState::unlockBaseLayerUpdate() {
181     if (m_baseLayerUpdate)
182         return;
183
184     m_baseLayerUpdate = true;
185     android::Mutex::Autolock lock(m_baseLayerLock);
186     SkSafeRef(m_baseLayer);
187     SkSafeUnref(m_currentBaseLayer);
188     m_currentBaseLayer = m_baseLayer;
189
190     invalRegion(m_invalidateRegion);
191     m_invalidateRegion.setEmpty();
192 }
193
194 void GLWebViewState::setExtra(BaseLayerAndroid* layer, SkPicture& picture,
195     const IntRect& rect, bool allowSame)
196 {
197     android::Mutex::Autolock lock(m_baseLayerLock);
198     if (!m_baseLayerUpdate)
199         return;
200
201     layer->setExtra(picture);
202
203     if (!allowSame && m_lastInval == rect)
204         return;
205
206     if (!rect.isEmpty())
207         inval(rect);
208     if (!m_lastInval.isEmpty())
209         inval(m_lastInval);
210     m_lastInval = rect;
211     m_displayRings = false;
212 }
213
214 void GLWebViewState::inval(const IntRect& rect)
215 {
216     if (m_baseLayerUpdate) {
217         m_currentPictureCounter++;
218         if (!rect.isEmpty()) {
219             // find which tiles fall within the invalRect and mark them as dirty
220             m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
221             m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
222             if (m_frameworkInval.isEmpty())
223                 m_frameworkInval = rect;
224             else
225                 m_frameworkInval.unite(rect);
226             XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
227                  m_frameworkInval.y(), m_frameworkInval.right(), m_frameworkInval.bottom(),
228                  rect.x(), rect.y(), rect.right(), rect.bottom());
229         }
230     } else {
231         m_invalidateRegion.op(rect.x(), rect.y(), rect.right(), rect.bottom(), SkRegion::kUnion_Op);
232     }
233 }
234
235 void GLWebViewState::resetRings()
236 {
237     m_displayRings = false;
238 }
239
240 void GLWebViewState::drawFocusRing(IntRect& srcRect)
241 {
242     // TODO: use a 9-patch texture to draw the focus ring
243     // instead of plain colors
244     const int border = 1;
245     const int fuzzyBorder = border * 2;
246     const int padding = 4;
247     const float alpha = 0.3;
248     const float borderAlpha = 0.40;
249
250     const int r = 104;
251     const int g = 153;
252     const int b = 255;
253
254     if (m_focusRingTexture == -1)
255         m_focusRingTexture = GLUtils::createSampleColorTexture(r, g, b);
256
257     SkRect rLeft, rTop, rRight, rBottom, rOverlay;
258
259     IntRect rect(srcRect.x() - padding, srcRect.y() - padding,
260                  srcRect.width() + (padding * 2), srcRect.height() + (padding * 2));
261     rLeft.set(rect.x() - border, rect.y(),
262               rect.x(), rect.y() + rect.height());
263     rTop.set(rect.x() - border, rect.y() - border,
264              rect.x() + rect.width() + border, rect.y());
265     rRight.set(rect.x() + rect.width(), rect.y(),
266                rect.x() + rect.width() + border,
267                rect.y() + rect.height());
268     rBottom.set(rect.x() - border, rect.y() + rect.height(),
269                 rect.x() + rect.width() + border,
270                 rect.y() + rect.height() + border);
271     rOverlay.set(rect.x() - fuzzyBorder, rect.y() - fuzzyBorder,
272               rect.x() + rect.width() + fuzzyBorder,
273               rect.y() + rect.height() + fuzzyBorder);
274
275     TilesManager::instance()->shader()->drawQuad(rLeft, m_focusRingTexture, borderAlpha);
276     TilesManager::instance()->shader()->drawQuad(rTop, m_focusRingTexture, borderAlpha);
277     TilesManager::instance()->shader()->drawQuad(rRight, m_focusRingTexture, borderAlpha);
278     TilesManager::instance()->shader()->drawQuad(rBottom, m_focusRingTexture, borderAlpha);
279     TilesManager::instance()->shader()->drawQuad(rOverlay, m_focusRingTexture, alpha);
280 }
281
282 void GLWebViewState::paintExtras()
283 {
284     if (m_displayRings) {
285         // TODO: handles correctly the multi-rings case
286         for (int i=0; i<m_rings.size(); i++) {
287             IntRect rect = m_rings.at(i);
288             drawFocusRing(rect);
289         }
290     }
291 }
292
293 unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
294 {
295     android::Mutex::Autolock lock(m_baseLayerLock);
296     if (m_currentBaseLayer) {
297         m_globalButtonMutex->lock();
298         m_currentBaseLayer->drawCanvas(canvas);
299         m_globalButtonMutex->unlock();
300     }
301     return m_currentPictureCounter;
302 }
303
304 void GLWebViewState::scheduleUpdate(const double& currentTime,
305                                     const SkIRect& viewport, float scale)
306 {
307     // if no update time, set it
308     if (updateTime() == -1) {
309         m_scaleRequestState = kWillScheduleRequest;
310         setUpdateTime(currentTime + s_updateInitialDelay);
311         setFutureScale(scale);
312         setFutureViewport(viewport);
313         return;
314     }
315
316     if (currentTime < updateTime())
317         return;
318
319     // we reached the scheduled update time, check if we can update
320     if (futureScale() == scale) {
321         // we are still with the previous scale, let's go
322         // with the update
323         m_scaleRequestState = kRequestNewScale;
324         setUpdateTime(-1);
325     } else {
326         // we reached the update time, but the planned update was for
327         // a different scale factor -- meaning the user is still probably
328         // in the process of zooming. Let's push the update time a bit.
329         setUpdateTime(currentTime + s_updateDelay);
330         setFutureScale(scale);
331         setFutureViewport(viewport);
332     }
333 }
334
335 double GLWebViewState::zoomInTransitionTime(double currentTime)
336 {
337     if (m_transitionTime == -1)
338         m_transitionTime = currentTime + s_zoomInTransitionDelay;
339     return m_transitionTime;
340 }
341
342 double GLWebViewState::zoomOutTransitionTime(double currentTime)
343 {
344     if (m_transitionTime == -1)
345         m_transitionTime = currentTime + s_zoomOutTransitionDelay;
346     return m_transitionTime;
347 }
348
349
350 float GLWebViewState::zoomInTransparency(double currentTime)
351 {
352     float t = zoomInTransitionTime(currentTime) - currentTime;
353     t *= s_invZoomInTransitionDelay;
354     return fmin(1, fmax(0, t));
355 }
356
357 float GLWebViewState::zoomOutTransparency(double currentTime)
358 {
359     float t = zoomOutTransitionTime(currentTime) - currentTime;
360     t *= s_invZoomOutTransitionDelay;
361     return fmin(1, fmax(0, t));
362 }
363
364 TiledPage* GLWebViewState::sibling(TiledPage* page)
365 {
366     return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA;
367 }
368
369 TiledPage* GLWebViewState::frontPage()
370 {
371     android::Mutex::Autolock lock(m_tiledPageLock);
372     return m_usePageA ? m_tiledPageA : m_tiledPageB;
373 }
374
375 TiledPage* GLWebViewState::backPage()
376 {
377     android::Mutex::Autolock lock(m_tiledPageLock);
378     return m_usePageA ? m_tiledPageB : m_tiledPageA;
379 }
380
381 void GLWebViewState::swapPages()
382 {
383     android::Mutex::Autolock lock(m_tiledPageLock);
384     m_usePageA ^= true;
385     TiledPage* working = m_usePageA ? m_tiledPageB : m_tiledPageA;
386     if (m_scaleRequestState != kNoScaleRequest)
387         TilesManager::instance()->resetTextureUsage(working);
388
389     m_scaleRequestState = kNoScaleRequest;
390 }
391
392 int GLWebViewState::baseContentWidth()
393 {
394     return m_currentBaseLayer ? m_currentBaseLayer->getWidth() : 0;
395
396 }
397 int GLWebViewState::baseContentHeight()
398 {
399     return m_currentBaseLayer ? m_currentBaseLayer->getHeight() : 0;
400 }
401
402 void GLWebViewState::setViewport(SkRect& viewport, float scale)
403 {
404     m_previousViewport = m_viewport;
405     if ((m_viewport == viewport) &&
406         (m_futureScale == scale))
407         return;
408
409     m_viewport = viewport;
410     XLOG("New VIEWPORT %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f currentScale: %.2f futureScale: %.2f)",
411          m_viewport.fLeft, m_viewport.fTop, m_viewport.fRight, m_viewport.fBottom,
412          m_viewport.width(), m_viewport.height(), scale, m_currentScale, m_futureScale);
413
414     const float invTileContentWidth = scale / TilesManager::tileWidth();
415     const float invTileContentHeight = scale / TilesManager::tileHeight();
416
417     m_viewportTileBounds.set(
418             static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)),
419             static_cast<int>(floorf(viewport.fTop * invTileContentHeight)),
420             static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)),
421             static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight)));
422
423     int maxTextureCount = (m_viewportTileBounds.width() + TilesManager::instance()->expandedTileBoundsX() * 2 + 1) *
424             (m_viewportTileBounds.height() + TilesManager::instance()->expandedTileBoundsY() * 2 + 1) * 2;
425     TilesManager::instance()->setMaxTextureCount(maxTextureCount);
426     m_tiledPageA->updateBaseTileSize();
427     m_tiledPageB->updateBaseTileSize();
428 }
429
430 #ifdef MEASURES_PERF
431 void GLWebViewState::dumpMeasures()
432 {
433     for (int i = 0; i < m_timeCounter; i++) {
434         XLOGC("%d delay: %d ms", m_totalTimeCounter + i,
435              static_cast<int>(m_delayTimes[i]*1000));
436         m_delayTimes[i] = 0;
437     }
438     m_totalTimeCounter += m_timeCounter;
439     m_timeCounter = 0;
440 }
441 #endif // MEASURES_PERF
442
443 void GLWebViewState::resetFrameworkInval()
444 {
445     m_frameworkInval.setX(0);
446     m_frameworkInval.setY(0);
447     m_frameworkInval.setWidth(0);
448     m_frameworkInval.setHeight(0);
449 }
450
451 void GLWebViewState::addDirtyArea(const IntRect& rect)
452 {
453     if (rect.isEmpty())
454         return;
455
456     IntRect inflatedRect = rect;
457     inflatedRect.inflate(8);
458     if (m_frameworkLayersInval.isEmpty())
459         m_frameworkLayersInval = inflatedRect;
460     else
461         m_frameworkLayersInval.unite(inflatedRect);
462 }
463
464 void GLWebViewState::resetLayersDirtyArea()
465 {
466     m_frameworkLayersInval.setX(0);
467     m_frameworkLayersInval.setY(0);
468     m_frameworkLayersInval.setWidth(0);
469     m_frameworkLayersInval.setHeight(0);
470 }
471
472 bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
473                             IntRect& webViewRect, int titleBarHeight,
474                             IntRect& clip, float scale, SkColor color)
475 {
476     glFinish();
477
478     double currentTime = WTF::currentTime();
479     double delta = currentTime - m_prevDrawTime;
480
481     if (delta < FRAMERATE_CAP) {
482         unsigned int usecs = (FRAMERATE_CAP - delta) * 1E6;
483         usleep(usecs);
484     }
485
486     m_prevDrawTime = currentTime;
487
488     m_baseLayerLock.lock();
489     BaseLayerAndroid* baseLayer = m_currentBaseLayer;
490     SkSafeRef(baseLayer);
491     BaseLayerAndroid* baseForComposited = m_baseLayer;
492     SkSafeRef(baseForComposited);
493     m_baseLayerLock.unlock();
494     if (!baseLayer) {
495         SkSafeUnref(baseForComposited);
496         return false;
497     }
498
499     XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
500          rect.x(), rect.y(), rect.right(), rect.bottom(),
501          viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);
502
503     resetLayersDirtyArea();
504
505     if (!baseForComposited ||
506         (baseForComposited && !baseForComposited->countChildren())) {
507         SkSafeRef(baseLayer);
508         SkSafeUnref(baseForComposited);
509         baseForComposited = baseLayer;
510     }
511
512     LayerAndroid* compositedRoot = 0;
513     if (baseForComposited && baseForComposited->countChildren() >= 1)
514         compositedRoot = static_cast<LayerAndroid*>(baseForComposited->getChild(0));
515
516     bool ret = baseLayer->drawGL(compositedRoot, rect, viewport, webViewRect, titleBarHeight, clip, scale, color);
517     m_previouslyUsedRoot = compositedRoot;
518     if (ret) {
519         FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(m_frameworkInval);
520         IntRect inval(frameworkInval.x(), frameworkInval.y(), frameworkInval.width(), frameworkInval.height());
521
522         inval.unite(m_frameworkLayersInval);
523
524         invalRect->setX(inval.x());
525         invalRect->setY(inval.y());
526         invalRect->setWidth(inval.width());
527         invalRect->setHeight(inval.height());
528
529         XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
530              inval.y(), inval.right(), inval.bottom());
531     } else {
532         resetFrameworkInval();
533     }
534
535 #ifdef MEASURES_PERF
536     if (m_measurePerfs) {
537         m_delayTimes[m_timeCounter++] = delta;
538         if (m_timeCounter >= MAX_MEASURES_PERF)
539             dumpMeasures();
540     }
541 #endif
542
543     SkSafeUnref(baseForComposited);
544     SkSafeUnref(baseLayer);
545     return ret;
546 }
547
548 } // namespace WebCore
549
550 #endif // USE(ACCELERATED_COMPOSITING)