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