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