1/* 2 * Copyright (C) 2011 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 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#include "PlatformCALayerWinInternal.h" 29 30#include "Font.h" 31#include "FontCache.h" 32#include "GraphicsContext.h" 33#include "PlatformCALayer.h" 34#include "TextRun.h" 35#include "TileController.h" 36#include "TiledBacking.h" 37#include <QuartzCore/CACFLayer.h> 38#include <wtf/MainThread.h> 39 40using namespace std; 41using namespace WebCore; 42 43// The width and height of a single tile in a tiled layer. Should be large enough to 44// avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough 45// to keep the overall tile cost low. 46static const int cTiledLayerTileSize = 512; 47 48static bool layerTypeIsTiled(const PlatformCALayer::LayerType layerType) 49{ 50 return layerType == PlatformCALayer::LayerTypeWebTiledLayer 51 || layerType == PlatformCALayer::LayerTypePageTiledBackingLayer 52 || layerType == PlatformCALayer::LayerTypeTiledBackingLayer; 53} 54 55PlatformCALayerWinInternal::PlatformCALayerWinInternal(PlatformCALayer* owner) 56 : m_tileSize(CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize)) 57 , m_constrainedSize(constrainedSize(owner->bounds().size())) 58 , m_owner(owner) 59{ 60 if (layerTypeIsTiled(m_owner->layerType())) { 61 // Tiled layers are placed in a child layer that is always the first child of the TiledLayer 62 m_tileParent = adoptCF(CACFLayerCreate(kCACFLayer)); 63 CACFLayerInsertSublayer(m_owner->platformLayer(), m_tileParent.get(), 0); 64 updateTiles(); 65 } 66} 67 68PlatformCALayerWinInternal::~PlatformCALayerWinInternal() 69{ 70} 71 72void PlatformCALayerWinInternal::displayCallback(CACFLayerRef caLayer, CGContextRef context) 73{ 74 ASSERT(isMainThread()); 75 76 if (!owner() || !owner()->owner()) 77 return; 78 79 CGContextSaveGState(context); 80 81 CGRect layerBounds = owner()->bounds(); 82 if (owner()->owner()->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { 83 CGContextScaleCTM(context, 1, -1); 84 CGContextTranslateCTM(context, 0, -layerBounds.size.height); 85 } 86 87 if (owner()->owner()) { 88 GraphicsContext graphicsContext(context); 89 90 // It's important to get the clip from the context, because it may be significantly 91 // smaller than the layer bounds (e.g. tiled layers) 92 CGRect clipBounds = CGContextGetClipBoundingBox(context); 93 IntRect clip(enclosingIntRect(clipBounds)); 94 owner()->owner()->platformCALayerPaintContents(owner(), graphicsContext, clip); 95 } 96#ifndef NDEBUG 97 else { 98 ASSERT_NOT_REACHED(); 99 100 // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color, 101 // so CA never makes backing store for it (which is what -setNeedsDisplay will do above). 102 CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f); 103 CGContextFillRect(context, layerBounds); 104 } 105#endif 106 107 if (owner()->owner()->platformCALayerShowRepaintCounter(owner())) { 108 FontCachePurgePreventer fontCachePurgePreventer; 109 110 String text = String::number(owner()->owner()->platformCALayerIncrementRepaintCount(owner())); 111 112 CGContextSaveGState(context); 113 114 // Make the background of the counter the same as the border color, 115 // unless there is no border, then make it red 116 float borderWidth = CACFLayerGetBorderWidth(caLayer); 117 if (borderWidth > 0) { 118 CGColorRef borderColor = CACFLayerGetBorderColor(caLayer); 119 const CGFloat* colors = CGColorGetComponents(borderColor); 120 CGContextSetRGBFillColor(context, colors[0], colors[1], colors[2], colors[3]); 121 } else 122 CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); 123 124 CGRect aBounds = layerBounds; 125 126 aBounds.size.width = 10 + 10 * text.length(); 127 aBounds.size.height = 22; 128 CGContextFillRect(context, aBounds); 129 130 FontDescription desc; 131 132 NONCLIENTMETRICS metrics; 133 metrics.cbSize = sizeof(metrics); 134 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); 135 desc.setOneFamily(metrics.lfSmCaptionFont.lfFaceName); 136 137 desc.setComputedSize(18); 138 139 Font font = Font(desc, 0, 0); 140 font.update(0); 141 142 GraphicsContext cg(context); 143 cg.setFillColor(Color::black, ColorSpaceDeviceRGB); 144 cg.drawText(font, TextRun(text), IntPoint(aBounds.origin.x + 5, aBounds.origin.y + 17)); 145 146 CGContextRestoreGState(context); 147 } 148 149 CGContextRestoreGState(context); 150 151 owner()->owner()->platformCALayerLayerDidDisplay(owner()); 152} 153 154void PlatformCALayerWinInternal::internalSetNeedsDisplay(const FloatRect* dirtyRect) 155{ 156 if (dirtyRect) { 157 CGRect rect = *dirtyRect; 158 CACFLayerSetNeedsDisplay(owner()->platformLayer(), &rect); 159 } else 160 CACFLayerSetNeedsDisplay(owner()->platformLayer(), 0); 161} 162 163void PlatformCALayerWinInternal::setNeedsDisplay(const FloatRect* dirtyRect) 164{ 165 if (layerTypeIsTiled(m_owner->layerType())) { 166 // FIXME: Only setNeedsDisplay for tiles that are currently visible 167 int numTileLayers = tileCount(); 168 CGRect rect; 169 if (dirtyRect) 170 rect = *dirtyRect; 171 for (int i = 0; i < numTileLayers; ++i) 172 CACFLayerSetNeedsDisplay(tileAtIndex(i), dirtyRect ? &rect : 0); 173 174 if (m_owner->owner() && m_owner->owner()->platformCALayerShowRepaintCounter(m_owner)) { 175 CGRect layerBounds = m_owner->bounds(); 176 CGRect indicatorRect = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, 80, 25); 177 CACFLayerSetNeedsDisplay(tileAtIndex(0), &indicatorRect); 178 } 179 } else if (owner()->layerType() == PlatformCALayer::LayerTypeWebLayer) { 180 if (owner() && owner()->owner()) { 181 if (owner()->owner()->platformCALayerShowRepaintCounter(owner())) { 182 FloatRect layerBounds = owner()->bounds(); 183 FloatRect repaintCounterRect = layerBounds; 184 185 // We assume a maximum of 4 digits and a font size of 18. 186 repaintCounterRect.setWidth(80); 187 repaintCounterRect.setHeight(22); 188 if (owner()->owner()->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) 189 repaintCounterRect.setY(layerBounds.height() - (layerBounds.y() + repaintCounterRect.height())); 190 internalSetNeedsDisplay(&repaintCounterRect); 191 } 192 if (dirtyRect && owner()->owner()->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { 193 FloatRect flippedDirtyRect = *dirtyRect; 194 flippedDirtyRect.setY(owner()->bounds().height() - (flippedDirtyRect.y() + flippedDirtyRect.height())); 195 internalSetNeedsDisplay(&flippedDirtyRect); 196 return; 197 } 198 } 199 200 internalSetNeedsDisplay(dirtyRect); 201 } 202 owner()->setNeedsCommit(); 203} 204 205void PlatformCALayerWinInternal::setSublayers(const PlatformCALayerList& list) 206{ 207 // Remove all the current sublayers and add the passed layers 208 CACFLayerSetSublayers(owner()->platformLayer(), 0); 209 210 // Perform removeFromSuperLayer in a separate pass. CACF requires superlayer to 211 // be null or CACFLayerInsertSublayer silently fails. 212 for (size_t i = 0; i < list.size(); i++) 213 CACFLayerRemoveFromSuperlayer(list[i]->platformLayer()); 214 215 for (size_t i = 0; i < list.size(); i++) 216 CACFLayerInsertSublayer(owner()->platformLayer(), list[i]->platformLayer(), i); 217 218 owner()->setNeedsCommit(); 219 220 if (layerTypeIsTiled(m_owner->layerType())) { 221 // Preserve the tile parent after set 222 CACFLayerInsertSublayer(owner()->platformLayer(), m_tileParent.get(), 0); 223 } 224} 225 226void PlatformCALayerWinInternal::getSublayers(PlatformCALayerList& list) const 227{ 228 CFArrayRef sublayers = CACFLayerGetSublayers(owner()->platformLayer()); 229 if (!sublayers) { 230 list.clear(); 231 return; 232 } 233 234 size_t count = CFArrayGetCount(sublayers); 235 236 size_t layersToSkip = 0; 237 if (layerTypeIsTiled(m_owner->layerType())) { 238 // Exclude the tile parent layer. 239 layersToSkip = 1; 240 } 241 242 list.resize(count - layersToSkip); 243 for (size_t arrayIndex = layersToSkip; arrayIndex < count; ++arrayIndex) 244 list[arrayIndex - layersToSkip] = PlatformCALayer::platformCALayer(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, arrayIndex))); 245} 246 247void PlatformCALayerWinInternal::removeAllSublayers() 248{ 249 CACFLayerSetSublayers(owner()->platformLayer(), 0); 250 owner()->setNeedsCommit(); 251 252 if (layerTypeIsTiled(m_owner->layerType())) { 253 // Restore the tile parent after removal 254 CACFLayerInsertSublayer(owner()->platformLayer(), m_tileParent.get(), 0); 255 } 256} 257 258void PlatformCALayerWinInternal::insertSublayer(PlatformCALayer* layer, size_t index) 259{ 260 index = min(index, sublayerCount()); 261 if (layerTypeIsTiled(m_owner->layerType())) { 262 // Add 1 to account for the tile parent layer 263 index++; 264 } 265 266 layer->removeFromSuperlayer(); 267 CACFLayerInsertSublayer(owner()->platformLayer(), layer->platformLayer(), index); 268 owner()->setNeedsCommit(); 269} 270 271size_t PlatformCALayerWinInternal::sublayerCount() const 272{ 273 CFArrayRef sublayers = CACFLayerGetSublayers(owner()->platformLayer()); 274 size_t count = sublayers ? CFArrayGetCount(sublayers) : 0; 275 276 if (layerTypeIsTiled(m_owner->layerType())) { 277 // Subtract 1 to account for the tile parent layer 278 ASSERT(count > 0); 279 count--; 280 } 281 282 return count; 283} 284 285int PlatformCALayerWinInternal::indexOfSublayer(const PlatformCALayer* reference) 286{ 287 CACFLayerRef ref = reference->platformLayer(); 288 if (!ref) 289 return -1; 290 291 CFArrayRef sublayers = CACFLayerGetSublayers(owner()->platformLayer()); 292 if (!sublayers) 293 return -1; 294 295 size_t n = CFArrayGetCount(sublayers); 296 297 if (layerTypeIsTiled(m_owner->layerType())) { 298 for (size_t i = 1; i < n; ++i) { 299 if (CFArrayGetValueAtIndex(sublayers, i) == ref) 300 return i - 1; 301 } 302 } else { 303 for (size_t i = 0; i < n; ++i) { 304 if (CFArrayGetValueAtIndex(sublayers, i) == ref) 305 return i; 306 } 307 } 308 309 return -1; 310} 311 312PlatformCALayer* PlatformCALayerWinInternal::sublayerAtIndex(int index) const 313{ 314 if (layerTypeIsTiled(m_owner->layerType())) { 315 // Add 1 to account for the tile parent layer 316 index++; 317 } 318 319 CFArrayRef sublayers = CACFLayerGetSublayers(owner()->platformLayer()); 320 if (!sublayers || index < 0 || CFArrayGetCount(sublayers) <= index) 321 return 0; 322 323 return PlatformCALayer::platformCALayer(static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index)))); 324} 325 326void PlatformCALayerWinInternal::setBounds(const FloatRect& rect) 327{ 328 if (CGRectEqualToRect(rect, owner()->bounds())) 329 return; 330 331 CACFLayerSetBounds(owner()->platformLayer(), rect); 332 owner()->setNeedsCommit(); 333 334 if (layerTypeIsTiled(m_owner->layerType())) { 335 m_constrainedSize = constrainedSize(rect.size()); 336 updateTiles(); 337 } 338} 339 340void PlatformCALayerWinInternal::setFrame(const FloatRect& rect) 341{ 342 CGRect oldFrame = CACFLayerGetFrame(owner()->platformLayer()); 343 if (CGRectEqualToRect(rect, oldFrame)) 344 return; 345 346 CACFLayerSetFrame(owner()->platformLayer(), rect); 347 owner()->setNeedsCommit(); 348 349 if (layerTypeIsTiled(m_owner->layerType())) 350 updateTiles(); 351} 352 353CGSize PlatformCALayerWinInternal::constrainedSize(const CGSize& size) const 354{ 355 const int cMaxTileCount = 512; 356 const float cSqrtMaxTileCount = sqrtf(cMaxTileCount); 357 358 CGSize constrainedSize = size; 359 360 int tileColumns = ceilf(constrainedSize.width / m_tileSize.width); 361 int tileRows = ceilf(constrainedSize.height / m_tileSize.height); 362 363 bool tooManyTiles = tileColumns && numeric_limits<int>::max() / tileColumns < tileRows || tileColumns * tileRows > cMaxTileCount; 364 365 // If number of tiles vertically or horizontally is < sqrt(cMaxTileCount) 366 // just shorten the longer dimension. Otherwise shorten both dimensions 367 // according to the ratio of width to height 368 369 if (tooManyTiles) { 370 if (tileRows < cSqrtMaxTileCount) 371 tileColumns = floorf(cMaxTileCount / tileRows); 372 else if (tileColumns < cSqrtMaxTileCount) 373 tileRows = floorf(cMaxTileCount / tileColumns); 374 else { 375 tileRows = ceilf(sqrtf(cMaxTileCount * constrainedSize.height / constrainedSize.width)); 376 tileColumns = floorf(cMaxTileCount / tileRows); 377 } 378 379 constrainedSize.width = tileColumns * m_tileSize.width; 380 constrainedSize.height = tileRows * m_tileSize.height; 381 } 382 383 return constrainedSize; 384} 385 386void PlatformCALayerWinInternal::tileDisplayCallback(CACFLayerRef layer, CGContextRef context) 387{ 388 static_cast<PlatformCALayerWinInternal*>(CACFLayerGetUserData(layer))->drawTile(layer, context); 389} 390 391void PlatformCALayerWinInternal::addTile() 392{ 393 RetainPtr<CACFLayerRef> newLayer = adoptCF(CACFLayerCreate(kCACFLayer)); 394 CACFLayerSetAnchorPoint(newLayer.get(), CGPointMake(0, 1)); 395 CACFLayerSetUserData(newLayer.get(), this); 396 CACFLayerSetDisplayCallback(newLayer.get(), tileDisplayCallback); 397 398 CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); 399 CACFLayerInsertSublayer(m_tileParent.get(), newLayer.get(), sublayers ? CFArrayGetCount(sublayers) : 0); 400 401 if (owner()->owner()->platformCALayerShowDebugBorders()) { 402 CGColorRef borderColor = CGColorCreateGenericRGB(0.5, 0, 0.5, 0.7); 403 CACFLayerSetBorderColor(newLayer.get(), borderColor); 404 CGColorRelease(borderColor); 405 CACFLayerSetBorderWidth(newLayer.get(), 2); 406 } 407} 408 409void PlatformCALayerWinInternal::removeTile() 410{ 411 CACFLayerRemoveFromSuperlayer(tileAtIndex(tileCount() - 1)); 412} 413 414CACFLayerRef PlatformCALayerWinInternal::tileAtIndex(int index) 415{ 416 CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); 417 if (!sublayers || index < 0 || index >= tileCount()) 418 return 0; 419 420 return static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index))); 421} 422 423int PlatformCALayerWinInternal::tileCount() const 424{ 425 CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get()); 426 return sublayers ? CFArrayGetCount(sublayers) : 0; 427} 428 429void PlatformCALayerWinInternal::updateTiles() 430{ 431 // FIXME: In addition to redoing the number of tiles, we need to only render and have backing 432 // store for visible layers 433 int numTilesHorizontal = ceil(m_constrainedSize.width / m_tileSize.width); 434 int numTilesVertical = ceil(m_constrainedSize.height / m_tileSize.height); 435 int numTilesTotal = numTilesHorizontal * numTilesVertical; 436 ASSERT(!m_constrainedSize.height || !m_constrainedSize.width || numTilesTotal > 0); 437 438 int numTilesToChange = numTilesTotal - tileCount(); 439 if (numTilesToChange >= 0) { 440 // Add new tiles 441 for (int i = 0; i < numTilesToChange; ++i) 442 addTile(); 443 } else { 444 // Remove old tiles 445 numTilesToChange = -numTilesToChange; 446 for (int i = 0; i < numTilesToChange; ++i) 447 removeTile(); 448 } 449 450 // Set coordinates for all tiles 451 CFArrayRef tileArray = CACFLayerGetSublayers(m_tileParent.get()); 452 453 for (int i = 0; i < numTilesHorizontal; ++i) { 454 for (int j = 0; j < numTilesVertical; ++j) { 455 CACFLayerRef tile = static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(tileArray, i * numTilesVertical + j))); 456 CACFLayerSetPosition(tile, CGPointMake(i * m_tileSize.width, j * m_tileSize.height)); 457 int width = min(m_tileSize.width, m_constrainedSize.width - i * m_tileSize.width); 458 int height = min(m_tileSize.height, m_constrainedSize.height - j * m_tileSize.height); 459 CACFLayerSetBounds(tile, CGRectMake(i * m_tileSize.width, j * m_tileSize.height, width, height)); 460 461 // Flip Y to compensate for the flipping that happens during render to match the CG context coordinate space 462 CATransform3D transform = CATransform3DMakeScale(1, -1, 1); 463 CATransform3DTranslate(transform, 0, height, 0); 464 CACFLayerSetTransform(tile, transform); 465 466#ifndef NDEBUG 467 String name = "Tile (" + String::number(i) + "," + String::number(j) + ")"; 468 CACFLayerSetName(tile, name.createCFString().get()); 469#endif 470 } 471 } 472} 473 474void PlatformCALayerWinInternal::drawTile(CACFLayerRef tile, CGContextRef context) 475{ 476 CGPoint tilePosition = CACFLayerGetPosition(tile); 477 CGRect tileBounds = CACFLayerGetBounds(tile); 478 479 CGContextSaveGState(context); 480 481 // Transform context to be at the origin of the parent layer 482 CGContextTranslateCTM(context, -tilePosition.x, -tilePosition.y); 483 484 // Set the context clipping rectangle to the current tile 485 CGContextClipToRect(context, CGRectMake(tilePosition.x, tilePosition.y, tileBounds.size.width, tileBounds.size.height)); 486 487 if (owner()->owner()->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { 488 // If the layer is rendering top-down, it will flip the coordinates in y. Tiled layers are 489 // already flipping, so we need to undo that here. 490 CGContextTranslateCTM(context, 0, owner()->bounds().height()); 491 CGContextScaleCTM(context, 1, -1); 492 } 493 494 // Draw the tile 495 displayCallback(owner()->platformLayer(), context); 496 497 CGContextRestoreGState(context); 498} 499 500TileController* PlatformCALayerWinInternal::createTileController(PlatformCALayer* rootLayer) 501{ 502 ASSERT(!m_tileController); 503 m_tileController = TileController::create(rootLayer); 504 return m_tileController.get(); 505} 506 507TiledBacking* PlatformCALayerWinInternal::tiledBacking() 508{ 509 return m_tileController.get(); 510} 511