diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/ca/mac/TileCache.mm')
-rw-r--r-- | Source/WebCore/platform/graphics/ca/mac/TileCache.mm | 570 |
1 files changed, 0 insertions, 570 deletions
diff --git a/Source/WebCore/platform/graphics/ca/mac/TileCache.mm b/Source/WebCore/platform/graphics/ca/mac/TileCache.mm deleted file mode 100644 index 233352d57..000000000 --- a/Source/WebCore/platform/graphics/ca/mac/TileCache.mm +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright (C) 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "config.h" -#import "TileCache.h" - -#import "IntRect.h" -#import "PlatformCALayer.h" -#import "Region.h" -#import "LayerPool.h" -#import "WebLayer.h" -#import "WebTileCacheLayer.h" -#import "WebTileLayer.h" -#import <wtf/MainThread.h> -#import <utility> - -using namespace std; - -#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 -@interface CALayer (WebCALayerDetails) -- (void)setAcceleratesDrawing:(BOOL)flag; -@end -#endif - -namespace WebCore { - -static const int defaultTileCacheWidth = 512; -static const int defaultTileCacheHeight = 512; - -PassOwnPtr<TileCache> TileCache::create(WebTileCacheLayer* tileCacheLayer) -{ - return adoptPtr(new TileCache(tileCacheLayer)); -} - -TileCache::TileCache(WebTileCacheLayer* tileCacheLayer) - : m_tileCacheLayer(tileCacheLayer) - , m_tileContainerLayer(adoptCF([[CALayer alloc] init])) - , m_tileSize(defaultTileCacheWidth, defaultTileCacheHeight) - , m_tileRevalidationTimer(this, &TileCache::tileRevalidationTimerFired) - , m_scale(1) - , m_deviceScaleFactor(1) - , m_tileCoverage(CoverageForVisibleArea) - , m_isInWindow(false) - , m_scrollingPerformanceLoggingEnabled(false) - , m_acceleratesDrawing(false) - , m_tilesAreOpaque(false) - , m_tileDebugBorderWidth(0) -{ - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - [m_tileCacheLayer addSublayer:m_tileContainerLayer.get()]; -#ifndef NDEBUG - [m_tileContainerLayer.get() setName:@"TileCache Container Layer"]; -#endif - [CATransaction commit]; -} - -TileCache::~TileCache() -{ - ASSERT(isMainThread()); - - for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { - WebTileLayer* tileLayer = it->value.get(); - [tileLayer setTileCache:0]; - } -} - -void TileCache::tileCacheLayerBoundsChanged() -{ - if (m_tiles.isEmpty()) { - // We must revalidate immediately instead of using a timer when there are - // no tiles to avoid a flash when transitioning from one page to another. - revalidateTiles(); - return; - } - - scheduleTileRevalidation(0); -} - -void TileCache::setNeedsDisplay() -{ - for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) - [it->value.get() setNeedsDisplay]; -} - -void TileCache::setNeedsDisplayInRect(const IntRect& rect) -{ - if (m_tiles.isEmpty()) - return; - - FloatRect scaledRect(rect); - scaledRect.scale(m_scale); - - // Find the tiles that need to be invalidated. - IntRect coveredRect = intersection(enclosingIntRect(scaledRect), m_tileCoverageRect); - if (coveredRect.isEmpty()) - return; - - TileIndex topLeft; - TileIndex bottomRight; - getTileIndexRangeForRect(coveredRect, topLeft, bottomRight); - - for (int y = topLeft.y(); y <= bottomRight.y(); ++y) { - for (int x = topLeft.x(); x <= bottomRight.x(); ++x) { - WebTileLayer* tileLayer = tileLayerAtIndex(TileIndex(x, y)); - if (!tileLayer) - continue; - - CGRect tileRect = [m_tileCacheLayer convertRect:rect toLayer:tileLayer]; - if (CGRectIsEmpty(tileRect)) - continue; - - [tileLayer setNeedsDisplayInRect:tileRect]; - - if (shouldShowRepaintCounters()) { - CGRect bounds = [tileLayer bounds]; - CGRect indicatorRect = CGRectMake(bounds.origin.x, bounds.origin.y, 52, 27); - [tileLayer setNeedsDisplayInRect:indicatorRect]; - } - } - } -} - -void TileCache::drawLayer(WebTileLayer *layer, CGContextRef context) -{ - PlatformCALayer* platformLayer = PlatformCALayer::platformCALayer(m_tileCacheLayer); - if (!platformLayer) - return; - - CGContextSaveGState(context); - - CGPoint layerOrigin = [layer frame].origin; - CGContextTranslateCTM(context, -layerOrigin.x, -layerOrigin.y); - CGContextScaleCTM(context, m_scale, m_scale); - drawLayerContents(context, layer, platformLayer); - - CGContextRestoreGState(context); - - drawRepaintCounter(layer, context); -} - -void TileCache::setScale(CGFloat scale) -{ - PlatformCALayer* platformLayer = PlatformCALayer::platformCALayer(m_tileCacheLayer); - float deviceScaleFactor = platformLayer->owner()->platformCALayerDeviceScaleFactor(); - - // The scale we get is the produce of the page scale factor and device scale factor. - // Divide by the device scale factor so we'll get the page scale factor. - scale /= deviceScaleFactor; - - if (m_scale == scale && m_deviceScaleFactor == deviceScaleFactor) - return; - -#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 - Vector<FloatRect> dirtyRects; - - m_deviceScaleFactor = deviceScaleFactor; - m_scale = scale; - [m_tileContainerLayer.get() setTransform:CATransform3DMakeScale(1 / m_scale, 1 / m_scale, 1)]; - - revalidateTiles(); - - for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { - [it->value.get() setContentsScale:deviceScaleFactor]; - - IntRect tileRect = rectForTileIndex(it->key); - FloatRect scaledTileRect = tileRect; - - scaledTileRect.scale(1 / m_scale); - dirtyRects.append(scaledTileRect); - } - - platformLayer->owner()->platformCALayerDidCreateTiles(dirtyRects); -#endif -} - -void TileCache::setAcceleratesDrawing(bool acceleratesDrawing) -{ -#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 - if (m_acceleratesDrawing == acceleratesDrawing) - return; - - m_acceleratesDrawing = acceleratesDrawing; - - for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) - [it->value.get() setAcceleratesDrawing:m_acceleratesDrawing]; -#else - UNUSED_PARAM(acceleratesDrawing); -#endif -} - -void TileCache::setTilesOpaque(bool opaque) -{ - if (opaque == m_tilesAreOpaque) - return; - - m_tilesAreOpaque = opaque; - - for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { - WebTileLayer* tileLayer = it->value.get(); - [tileLayer setOpaque:opaque]; - } -} - -void TileCache::setVisibleRect(const IntRect& visibleRect) -{ - if (m_visibleRect == visibleRect) - return; - - m_visibleRect = visibleRect; - revalidateTiles(); -} - -void TileCache::setIsInWindow(bool isInWindow) -{ - if (m_isInWindow == isInWindow) - return; - - m_isInWindow = isInWindow; - - if (!m_isInWindow) { - const double tileRevalidationTimeout = 4; - scheduleTileRevalidation(tileRevalidationTimeout); - } -} - -void TileCache::setTileCoverage(TileCoverage coverage) -{ - if (coverage == m_tileCoverage) - return; - - m_tileCoverage = coverage; - scheduleTileRevalidation(0); -} - -void TileCache::forceRepaint() -{ - setNeedsDisplay(); -} - -void TileCache::setTileDebugBorderWidth(float borderWidth) -{ - if (m_tileDebugBorderWidth == borderWidth) - return; - - m_tileDebugBorderWidth = borderWidth; - for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) - [it->value.get() setBorderWidth:m_tileDebugBorderWidth]; -} - -void TileCache::setTileDebugBorderColor(CGColorRef borderColor) -{ - if (m_tileDebugBorderColor == borderColor) - return; - - m_tileDebugBorderColor = borderColor; - for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) - [it->value.get() setBorderColor:m_tileDebugBorderColor.get()]; -} - -IntRect TileCache::bounds() const -{ - return IntRect(IntPoint(), IntSize([m_tileCacheLayer bounds].size)); -} - -IntRect TileCache::rectForTileIndex(const TileIndex& tileIndex) const -{ - IntRect rect(tileIndex.x() * m_tileSize.width(), tileIndex.y() * m_tileSize.height(), m_tileSize.width(), m_tileSize.height()); - IntRect scaledBounds(bounds()); - scaledBounds.scale(m_scale); - - rect.intersect(scaledBounds); - - return rect; -} - -void TileCache::getTileIndexRangeForRect(const IntRect& rect, TileIndex& topLeft, TileIndex& bottomRight) const -{ - IntRect clampedRect = bounds(); - clampedRect.scale(m_scale); - clampedRect.intersect(rect); - - topLeft.setX(max(clampedRect.x() / m_tileSize.width(), 0)); - topLeft.setY(max(clampedRect.y() / m_tileSize.height(), 0)); - - int bottomXRatio = ceil((float)clampedRect.maxX() / m_tileSize.width()); - bottomRight.setX(max(bottomXRatio - 1, 0)); - - int bottomYRatio = ceil((float)clampedRect.maxY() / m_tileSize.height()); - bottomRight.setY(max(bottomYRatio - 1, 0)); -} - -IntRect TileCache::computeTileCoverageRect() const -{ - IntRect tileCoverageRect = m_visibleRect; - - // If the page is not in a window (for example if it's in a background tab), we limit the tile coverage rect to the visible rect. - // Furthermore, if the page can't have scrollbars (for example if its body element has overflow:hidden) it's very unlikely that the - // page will ever be scrolled so we limit the tile coverage rect as well. - if (m_isInWindow && !(m_tileCoverage & CoverageForSlowScrolling)) { - // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height. - // These values were chosen because it's more common to have tall pages and to scroll vertically, - // so we keep more tiles above and below the current area. - if (m_tileCoverage & CoverageForHorizontalScrolling) - tileCoverageRect.inflateX(tileCoverageRect.width() / 2); - - if (m_tileCoverage & CoverageForVerticalScrolling) - tileCoverageRect.inflateY(tileCoverageRect.height()); - } - - return tileCoverageRect; -} - -IntSize TileCache::tileSizeForCoverageRect(const IntRect& coverageRect) const -{ - if (m_tileCoverage & CoverageForSlowScrolling) - return coverageRect.size(); - return IntSize(defaultTileCacheWidth, defaultTileCacheHeight); -} - -void TileCache::scheduleTileRevalidation(double interval) -{ - if (m_tileRevalidationTimer.isActive() && m_tileRevalidationTimer.nextFireInterval() < interval) - return; - - m_tileRevalidationTimer.startOneShot(interval); -} - -void TileCache::tileRevalidationTimerFired(Timer<TileCache>*) -{ - revalidateTiles(); -} - -unsigned TileCache::blankPixelCount() const -{ - WebTileLayerList tiles(m_tiles.size()); - tiles.appendRange(m_tiles.begin().values(), m_tiles.end().values()); - - return blankPixelCountForTiles(tiles, m_visibleRect, IntPoint(0,0)); -} - -unsigned TileCache::blankPixelCountForTiles(const WebTileLayerList& tiles, IntRect visibleRect, IntPoint tileTranslation) -{ - Region paintedVisibleTiles; - - for (WebTileLayerList::const_iterator it = tiles.begin(), end = tiles.end(); it != end; ++it) { - const WebTileLayer* tileLayer = it->get(); - - IntRect visiblePart(CGRectOffset([tileLayer frame], tileTranslation.x(), tileTranslation.y())); - visiblePart.intersect(visibleRect); - - if (!visiblePart.isEmpty() && [tileLayer repaintCount]) - paintedVisibleTiles.unite(visiblePart); - } - - Region uncoveredRegion(visibleRect); - uncoveredRegion.subtract(paintedVisibleTiles); - - return uncoveredRegion.totalArea(); -} - -void TileCache::revalidateTiles() -{ - // If the underlying PlatformLayer has been destroyed, but the WebTileCacheLayer hasn't - // platformLayer will be null here. - PlatformCALayer* platformLayer = PlatformCALayer::platformCALayer(m_tileCacheLayer); - if (!platformLayer) - return; - - if (m_visibleRect.isEmpty() || bounds().isEmpty()) - return; - - IntRect tileCoverageRect = computeTileCoverageRect(); - IntRect coverageRectInTileCoords(tileCoverageRect); - coverageRectInTileCoords.scale(m_scale); - - IntSize oldTileSize = m_tileSize; - m_tileSize = tileSizeForCoverageRect(tileCoverageRect); - bool tileSizeChanged = m_tileSize != oldTileSize; - - Vector<TileIndex> tilesToRemove; - - for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { - const TileIndex& tileIndex = it->key; - - WebTileLayer* tileLayer = it->value.get(); - - if (!rectForTileIndex(tileIndex).intersects(coverageRectInTileCoords) || tileSizeChanged) { - // Remove this layer. - [tileLayer removeFromSuperlayer]; - [tileLayer setTileCache:0]; - - tilesToRemove.append(tileIndex); - } - } - - // FIXME: Be more clever about which tiles to remove. We might not want to remove all - // the tiles that are outside the coverage rect. When we know that we're going to be scrolling up, - // we might want to remove the ones below the coverage rect but keep the ones above. - for (size_t i = 0; i < tilesToRemove.size(); ++i) - LayerPool::sharedPool()->addLayer(m_tiles.take(tilesToRemove[i])); - - TileIndex topLeft; - TileIndex bottomRight; - getTileIndexRangeForRect(coverageRectInTileCoords, topLeft, bottomRight); - - Vector<FloatRect> dirtyRects; - - for (int y = topLeft.y(); y <= bottomRight.y(); ++y) { - for (int x = topLeft.x(); x <= bottomRight.x(); ++x) { - TileIndex tileIndex(x, y); - - IntRect tileRect = rectForTileIndex(tileIndex); - RetainPtr<WebTileLayer>& tileLayer = m_tiles.add(tileIndex, 0).iterator->value; - if (!tileLayer) { - tileLayer = createTileLayer(tileRect); - [m_tileContainerLayer.get() addSublayer:tileLayer.get()]; - } else { - // We already have a layer for this tile. Ensure that its size is correct. - CGSize tileLayerSize = [tileLayer.get() frame].size; - if (tileLayerSize.width >= tileRect.width() && tileLayerSize.height >= tileRect.height()) - continue; - [tileLayer.get() setFrame:tileRect]; - } - - FloatRect scaledTileRect = tileRect; - scaledTileRect.scale(1 / m_scale); - dirtyRects.append(scaledTileRect); - } - } - - m_tileCoverageRect = IntRect(); - for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { - const TileIndex& tileIndex = it->key; - - m_tileCoverageRect.unite(rectForTileIndex(tileIndex)); - } - - if (dirtyRects.isEmpty()) - return; - platformLayer->owner()->platformCALayerDidCreateTiles(dirtyRects); -} - -IntRect TileCache::tileGridExtent() const -{ - TileIndex topLeft; - TileIndex bottomRight; - getTileIndexRangeForRect(m_tileCoverageRect, topLeft, bottomRight); - - // Return index of top, left tile and the number of tiles across and down. - return IntRect(topLeft.x(), topLeft.y(), bottomRight.x() - topLeft.x() + 1, bottomRight.y() - topLeft.y() + 1); -} - -// Return the rect in layer coords, not tile coords. -IntRect TileCache::tileCoverageRect() const -{ - IntRect coverageRectInLayerCoords(m_tileCoverageRect); - coverageRectInLayerCoords.scale(1 / m_scale); - return coverageRectInLayerCoords; -} - -WebTileLayer* TileCache::tileLayerAtIndex(const TileIndex& index) const -{ - return m_tiles.get(index).get(); -} - -RetainPtr<WebTileLayer> TileCache::createTileLayer(const IntRect& tileRect) -{ - RetainPtr<WebTileLayer> layer = LayerPool::sharedPool()->takeLayerWithSize(tileRect.size()); - if (layer) { - // If we were able to restore a layer from the LayerPool, we should call setNeedsDisplay to - // ensure we avoid stale content. - [layer setNeedsDisplay]; - } else - layer = adoptNS([[WebTileLayer alloc] init]); - [layer.get() setAnchorPoint:CGPointZero]; - [layer.get() setFrame:tileRect]; - [layer.get() setTileCache:this]; - [layer.get() setBorderColor:m_tileDebugBorderColor.get()]; - [layer.get() setBorderWidth:m_tileDebugBorderWidth]; - [layer.get() setEdgeAntialiasingMask:0]; - [layer.get() setOpaque:m_tilesAreOpaque]; -#ifndef NDEBUG - [layer.get() setName:@"Tile"]; -#endif - -#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 - [layer.get() setContentsScale:m_deviceScaleFactor]; - [layer.get() setAcceleratesDrawing:m_acceleratesDrawing]; -#endif - - return layer; -} - -bool TileCache::shouldShowRepaintCounters() const -{ - PlatformCALayer* platformLayer = PlatformCALayer::platformCALayer(m_tileCacheLayer); - if (!platformLayer) - return false; - - WebCore::PlatformCALayerClient* layerContents = platformLayer->owner(); - ASSERT(layerContents); - if (!layerContents) - return false; - - return layerContents->platformCALayerShowRepaintCounter(0); -} - -void TileCache::drawRepaintCounter(WebTileLayer *layer, CGContextRef context) -{ - unsigned repaintCount = [layer incrementRepaintCount]; - if (!shouldShowRepaintCounters()) - return; - - // FIXME: Some of this code could be shared with WebLayer. - char text[16]; // that's a lot of repaints - snprintf(text, sizeof(text), "%d", repaintCount); - - CGRect indicatorBox = [layer bounds]; - indicatorBox.size.width = 12 + 10 * strlen(text); - indicatorBox.size.height = 27; - CGContextSaveGState(context); - - CGContextSetAlpha(context, 0.5f); - CGContextBeginTransparencyLayerWithRect(context, indicatorBox, 0); - - CGContextSetFillColorWithColor(context, m_tileDebugBorderColor.get()); - CGContextFillRect(context, indicatorBox); - - PlatformCALayer* platformLayer = PlatformCALayer::platformCALayer(m_tileCacheLayer); - - if (platformLayer->acceleratesDrawing()) - CGContextSetRGBFillColor(context, 1, 0, 0, 1); - else - CGContextSetRGBFillColor(context, 1, 1, 1, 1); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1, -1)); - CGContextSelectFont(context, "Helvetica", 22, kCGEncodingMacRoman); - CGContextShowTextAtPoint(context, indicatorBox.origin.x + 5, indicatorBox.origin.y + 22, text, strlen(text)); -#pragma clang diagnostic pop - - CGContextEndTransparencyLayer(context); - CGContextRestoreGState(context); -} - -} // namespace WebCore |