1 /* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. 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 APPLE INC. ``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 APPLE COMPUTER, INC. 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 28 #if USE(ACCELERATED_COMPOSITING) 29 30 #include "WebTiledLayer.h" 31 32 #include "GraphicsLayer.h" 33 #include "WKCACFLayerRenderer.h" 34 35 namespace WebCore { 36 37 using namespace std; 38 39 #ifndef NDEBUG 40 void WebTiledLayer::internalCheckLayerConsistency() 41 { 42 WKCACFLayer::internalCheckLayerConsistency(); 43 44 // Additionally make sure the tiled parent is valid 45 CFArrayRef sublayers = CACFLayerGetSublayers(layer()); 46 47 // Make sure there is a tile parent and it is the same as we remember 48 size_t n = CFArrayGetCount(sublayers); 49 ASSERT(n > 0); 50 const void* element = CFArrayGetValueAtIndex(sublayers, 0); 51 ASSERT(m_tileParent.get() == element); 52 53 // Make sure the tile parent doesn't have user data. If it does, it is probably 54 // a WKCACFLayer in the wrong place. 55 ASSERT(!layer(m_tileParent.get())); 56 } 57 #endif 58 59 void WebTiledLayer::tileDisplayCallback(CACFLayerRef layer, CGContextRef context) 60 { 61 static_cast<WebTiledLayer*>(CACFLayerGetUserData(layer))->drawTile(layer, context); 62 } 63 64 PassRefPtr<WebTiledLayer> WebTiledLayer::create(const CGSize& tileSize, GraphicsLayer* owner) 65 { 66 ASSERT(WKCACFLayerRenderer::acceleratedCompositingAvailable()); 67 return adoptRef(new WebTiledLayer(tileSize, owner)); 68 } 69 70 WebTiledLayer::WebTiledLayer(const CGSize& tileSize, GraphicsLayer* owner) 71 : WebLayer(WKCACFLayer::Layer, owner) 72 , m_tileSize(tileSize) 73 , m_constrainedSize(constrainedSize(bounds().size)) 74 { 75 // Tiled layers are placed in a child layer that is always the first child of the TiledLayer 76 m_tileParent.adoptCF(CACFLayerCreate(kCACFLayer)); 77 CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); 78 79 updateTiles(); 80 } 81 82 WebTiledLayer::~WebTiledLayer() 83 { 84 } 85 86 void WebTiledLayer::setBounds(const CGRect& rect) 87 { 88 if (CGRectEqualToRect(rect, bounds())) 89 return; 90 91 WebLayer::setBounds(rect); 92 m_constrainedSize = constrainedSize(rect.size); 93 updateTiles(); 94 } 95 96 void WebTiledLayer::setFrame(const CGRect& rect) 97 { 98 if (CGRectEqualToRect(rect, frame())) 99 return; 100 101 WebLayer::setFrame(rect); 102 updateTiles(); 103 } 104 105 void WebTiledLayer::internalSetNeedsDisplay(const CGRect* dirtyRect) 106 { 107 // FIXME: Only setNeedsDisplay for tiles that are currently visible 108 int numTileLayers = tileCount(); 109 for (int i = 0; i < numTileLayers; ++i) 110 CACFLayerSetNeedsDisplay(tileAtIndex(i), dirtyRect); 111 112 if (m_owner->showRepaintCounter()) { 113 CGRect layerBounds = bounds(); 114 CGRect indicatorRect = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, 80, 25); 115 CACFLayerSetNeedsDisplay(tileAtIndex(0), &indicatorRect); 116 } 117 } 118 119 size_t WebTiledLayer::internalSublayerCount() const 120 { 121 ASSERT(WebLayer::internalSublayerCount() > 0); 122 123 // Subtract 1 to account for the tile parent layer 124 return WebLayer::internalSublayerCount() - 1; 125 } 126 127 void WebTiledLayer::internalRemoveAllSublayers() 128 { 129 // Restore the tile parent after removal 130 WebLayer::internalRemoveAllSublayers(); 131 CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); 132 } 133 134 void WebTiledLayer::internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) 135 { 136 // Preserve the tile parent after set 137 WebLayer::internalSetSublayers(sublayers); 138 CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0); 139 } 140 141 void WebTiledLayer::internalInsertSublayer(PassRefPtr<WKCACFLayer> layer, size_t index) 142 { 143 // Add 1 to account for the tile parent layer 144 WebLayer::internalInsertSublayer(layer, index + 1); 145 } 146 147 WKCACFLayer* WebTiledLayer::internalSublayerAtIndex(int i) const 148 { 149 // Add 1 to account for the tile parent layer 150 return WebLayer::internalSublayerAtIndex(i + 1); 151 } 152 153 int WebTiledLayer::internalIndexOfSublayer(const WKCACFLayer* layer) 154 { 155 int i = WebLayer::internalIndexOfSublayer(layer); 156 157 // Add 1 to account for the tile parent layer (but be safe about it) 158 return (i > 0) ? i - 1 : -1; 159 } 160 161 CGSize WebTiledLayer::constrainedSize(const CGSize& size) const 162 { 163 const int cMaxTileCount = 512; 164 const float cSqrtMaxTileCount = sqrtf(cMaxTileCount); 165 166 CGSize constrainedSize = size; 167 168 int tileColumns = ceilf(constrainedSize.width / m_tileSize.width); 169 int tileRows = ceilf(constrainedSize.height / m_tileSize.height); 170 int numTiles = tileColumns * tileRows; 171 172 // If number of tiles vertically or horizontally is < sqrt(cMaxTileCount) 173 // just shorten the longer dimension. Otherwise shorten both dimensions 174 // according to the ratio of width to height 175 176 if (numTiles > cMaxTileCount) { 177 if (tileRows < cSqrtMaxTileCount) 178 tileColumns = floorf(cMaxTileCount / tileRows); 179 else if (tileColumns < cSqrtMaxTileCount) 180 tileRows = floorf(cMaxTileCount / tileColumns); 181 else { 182 tileRows = ceilf(sqrtf(cMaxTileCount * constrainedSize.height / constrainedSize.width)); 183 tileColumns = floorf(cMaxTileCount / tileRows); 184 } 185 186 constrainedSize.width = tileColumns * m_tileSize.width; 187 constrainedSize.height = tileRows * m_tileSize.height; 188 } 189 190 return constrainedSize; 191 } 192 193 void WebTiledLayer::addTile() 194 { 195 RetainPtr<CACFLayerRef> newLayer(AdoptCF, CACFLayerCreate(kCACFLayer)); 196 CACFLayerSetAnchorPoint(newLayer.get(), CGPointMake(0, 1)); 197 CACFLayerSetUserData(newLayer.get(), this); 198 CACFLayerSetDisplayCallback(newLayer.get(), tileDisplayCallback); 199 200 CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); 201 CACFLayerInsertSublayer(m_tileParent.get(), newLayer.get(), sublayers ? CFArrayGetCount(sublayers) : 0); 202 203 if (m_owner->showDebugBorders()) { 204 CGColorRef borderColor = CGColorCreateGenericRGB(0.5, 0, 0.5, 0.7); 205 CACFLayerSetBorderColor(newLayer.get(), borderColor); 206 CGColorRelease(borderColor); 207 CACFLayerSetBorderWidth(newLayer.get(), 2); 208 } 209 } 210 211 void WebTiledLayer::removeTile() 212 { 213 CACFLayerRemoveFromSuperlayer(tileAtIndex(tileCount() - 1)); 214 } 215 216 CACFLayerRef WebTiledLayer::tileAtIndex(int index) 217 { 218 CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); 219 if (!sublayers || index < 0 || index >= tileCount() ) 220 return 0; 221 222 return static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index))); 223 } 224 225 int WebTiledLayer::tileCount() const 226 { 227 CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); 228 return sublayers ? CFArrayGetCount(sublayers) : 0; 229 } 230 231 void WebTiledLayer::updateTiles() 232 { 233 // FIXME: In addition to redoing the number of tiles, we need to only render and have backing 234 // store for visible layers 235 int numTilesHorizontal = ceil(m_constrainedSize.width / m_tileSize.width); 236 int numTilesVertical = ceil(m_constrainedSize.height / m_tileSize.height); 237 int numTilesTotal = numTilesHorizontal * numTilesVertical; 238 239 int numTilesToChange = numTilesTotal - tileCount(); 240 if (numTilesToChange >= 0) { 241 // Add new tiles 242 for (int i = 0; i < numTilesToChange; ++i) 243 addTile(); 244 } else { 245 // Remove old tiles 246 numTilesToChange = -numTilesToChange; 247 for (int i = 0; i < numTilesToChange; ++i) 248 removeTile(); 249 } 250 251 // Set coordinates for all tiles 252 CFArrayRef tileArray = CACFLayerGetSublayers(m_tileParent.get()); 253 254 for (int i = 0; i < numTilesHorizontal; ++i) { 255 for (int j = 0; j < numTilesVertical; ++j) { 256 CACFLayerRef tile = static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(tileArray, i * numTilesVertical + j))); 257 CACFLayerSetPosition(tile, CGPointMake(i * m_tileSize.width, j * m_tileSize.height)); 258 int width = min(m_tileSize.width, m_constrainedSize.width - i * m_tileSize.width); 259 int height = min(m_tileSize.height, m_constrainedSize.height - j * m_tileSize.height); 260 CACFLayerSetBounds(tile, CGRectMake(i * m_tileSize.width, j * m_tileSize.height, width, height)); 261 262 // Flip Y to compensate for the flipping that happens during render to match the CG context coordinate space 263 CATransform3D transform = CATransform3DMakeScale(1, -1, 1); 264 CATransform3DTranslate(transform, 0, height, 0); 265 CACFLayerSetTransform(tile, transform); 266 267 #ifndef NDEBUG 268 String name = "Tile (" + String::number(i) + "," + String::number(j) + ")"; 269 CACFLayerSetName(tile, RetainPtr<CFStringRef>(AdoptCF, name.createCFString()).get()); 270 #endif 271 } 272 } 273 } 274 275 void WebTiledLayer::drawTile(CACFLayerRef tile, CGContextRef context) 276 { 277 CGPoint tilePosition = CACFLayerGetPosition(tile); 278 CGRect tileBounds = CACFLayerGetBounds(tile); 279 280 CGContextSaveGState(context); 281 282 // Transform context to be at the origin of the parent layer 283 CGContextTranslateCTM(context, -tilePosition.x, -tilePosition.y); 284 285 // Set the context clipping rectangle to the current tile 286 CGContextClipToRect(context, CGRectMake(tilePosition.x, tilePosition.y, tileBounds.size.width, tileBounds.size.height)); 287 288 if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { 289 // If the layer is rendering top-down, it will flip the coordinates in y. Tiled layers are 290 // already flipping, so we need to undo that here. 291 CGContextTranslateCTM(context, 0, bounds().size.height); 292 CGContextScaleCTM(context, 1, -1); 293 } 294 295 // Draw the tile 296 drawInContext(context); 297 298 CGContextRestoreGState(context); 299 } 300 301 } 302 303 #endif // USE(ACCELERATED_COMPOSITING) 304