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 "WKCACFLayer.h" 31 32 #include "CString.h" 33 #include "WKCACFContextFlusher.h" 34 #include "WKCACFLayerRenderer.h" 35 36 #include <stdio.h> 37 #include <QuartzCore/CACFContext.h> 38 #include <QuartzCore/CARender.h> 39 #include <QuartzCoreInterface/QuartzCoreInterface.h> 40 41 #ifndef NDEBUG 42 #include <wtf/CurrentTime.h> 43 #endif 44 45 #ifdef DEBUG_ALL 46 #pragma comment(lib, "QuartzCore_debug") 47 #pragma comment(lib, "QuartzCoreInterface_debug") 48 #else 49 #pragma comment(lib, "QuartzCore") 50 #pragma comment(lib, "QuartzCoreInterface") 51 #endif 52 53 namespace WebCore { 54 55 using namespace std; 56 57 static void displayInContext(CACFLayerRef layer, CGContextRef context) 58 { 59 ASSERT_ARG(layer, WKCACFLayer::layer(layer)); 60 WKCACFLayer::layer(layer)->display(context); 61 } 62 63 #define STATIC_CACF_STRING(name) \ 64 static CFStringRef name() \ 65 { \ 66 static CFStringRef name = wkqcCFStringRef(wkqc##name); \ 67 return name; \ 68 } 69 70 STATIC_CACF_STRING(kCACFLayer) 71 STATIC_CACF_STRING(kCACFTransformLayer) 72 STATIC_CACF_STRING(kCACFGravityCenter) 73 STATIC_CACF_STRING(kCACFGravityTop) 74 STATIC_CACF_STRING(kCACFGravityBottom) 75 STATIC_CACF_STRING(kCACFGravityLeft) 76 STATIC_CACF_STRING(kCACFGravityRight) 77 STATIC_CACF_STRING(kCACFGravityTopLeft) 78 STATIC_CACF_STRING(kCACFGravityTopRight) 79 STATIC_CACF_STRING(kCACFGravityBottomLeft) 80 STATIC_CACF_STRING(kCACFGravityBottomRight) 81 STATIC_CACF_STRING(kCACFGravityResize) 82 STATIC_CACF_STRING(kCACFGravityResizeAspect) 83 STATIC_CACF_STRING(kCACFGravityResizeAspectFill) 84 STATIC_CACF_STRING(kCACFFilterLinear) 85 STATIC_CACF_STRING(kCACFFilterNearest) 86 STATIC_CACF_STRING(kCACFFilterTrilinear) 87 STATIC_CACF_STRING(kCACFFilterLanczos) 88 89 static CFStringRef toCACFLayerType(WKCACFLayer::LayerType type) 90 { 91 switch (type) { 92 case WKCACFLayer::Layer: return kCACFLayer(); 93 case WKCACFLayer::TransformLayer: return kCACFTransformLayer(); 94 default: return 0; 95 } 96 } 97 98 static CFStringRef toCACFContentsGravityType(WKCACFLayer::ContentsGravityType type) 99 { 100 switch (type) { 101 case WKCACFLayer::Center: return kCACFGravityCenter(); 102 case WKCACFLayer::Top: return kCACFGravityTop(); 103 case WKCACFLayer::Bottom: return kCACFGravityBottom(); 104 case WKCACFLayer::Left: return kCACFGravityLeft(); 105 case WKCACFLayer::Right: return kCACFGravityRight(); 106 case WKCACFLayer::TopLeft: return kCACFGravityTopLeft(); 107 case WKCACFLayer::TopRight: return kCACFGravityTopRight(); 108 case WKCACFLayer::BottomLeft: return kCACFGravityBottomLeft(); 109 case WKCACFLayer::BottomRight: return kCACFGravityBottomRight(); 110 case WKCACFLayer::Resize: return kCACFGravityResize(); 111 case WKCACFLayer::ResizeAspect: return kCACFGravityResizeAspect(); 112 case WKCACFLayer::ResizeAspectFill: return kCACFGravityResizeAspectFill(); 113 default: return 0; 114 } 115 } 116 117 static WKCACFLayer::ContentsGravityType fromCACFContentsGravityType(CFStringRef string) 118 { 119 if (CFEqual(string, kCACFGravityTop())) 120 return WKCACFLayer::Top; 121 122 if (CFEqual(string, kCACFGravityBottom())) 123 return WKCACFLayer::Bottom; 124 125 if (CFEqual(string, kCACFGravityLeft())) 126 return WKCACFLayer::Left; 127 128 if (CFEqual(string, kCACFGravityRight())) 129 return WKCACFLayer::Right; 130 131 if (CFEqual(string, kCACFGravityTopLeft())) 132 return WKCACFLayer::TopLeft; 133 134 if (CFEqual(string, kCACFGravityTopRight())) 135 return WKCACFLayer::TopRight; 136 137 if (CFEqual(string, kCACFGravityBottomLeft())) 138 return WKCACFLayer::BottomLeft; 139 140 if (CFEqual(string, kCACFGravityBottomRight())) 141 return WKCACFLayer::BottomRight; 142 143 if (CFEqual(string, kCACFGravityResize())) 144 return WKCACFLayer::Resize; 145 146 if (CFEqual(string, kCACFGravityResizeAspect())) 147 return WKCACFLayer::ResizeAspect; 148 149 if (CFEqual(string, kCACFGravityResizeAspectFill())) 150 return WKCACFLayer::ResizeAspectFill; 151 152 return WKCACFLayer::Center; 153 } 154 155 static CFStringRef toCACFFilterType(WKCACFLayer::FilterType type) 156 { 157 switch (type) { 158 case WKCACFLayer::Linear: return kCACFFilterLinear(); 159 case WKCACFLayer::Nearest: return kCACFFilterNearest(); 160 case WKCACFLayer::Trilinear: return kCACFFilterTrilinear(); 161 case WKCACFLayer::Lanczos: return kCACFFilterLanczos(); 162 default: return 0; 163 } 164 } 165 166 static WKCACFLayer::FilterType fromCACFFilterType(CFStringRef string) 167 { 168 if (CFEqual(string, kCACFFilterNearest())) 169 return WKCACFLayer::Nearest; 170 171 if (CFEqual(string, kCACFFilterTrilinear())) 172 return WKCACFLayer::Trilinear; 173 174 if (CFEqual(string, kCACFFilterLanczos())) 175 return WKCACFLayer::Lanczos; 176 177 return WKCACFLayer::Linear; 178 } 179 180 PassRefPtr<WKCACFLayer> WKCACFLayer::create(LayerType type, GraphicsLayerCACF* owner) 181 { 182 if (!WKCACFLayerRenderer::acceleratedCompositingAvailable()) 183 return 0; 184 return adoptRef(new WKCACFLayer(type, owner)); 185 } 186 187 // FIXME: It might be good to have a way of ensuring that all WKCACFLayers eventually 188 // get destroyed in debug builds. A static counter could accomplish this pretty easily. 189 190 WKCACFLayer::WKCACFLayer(LayerType type, GraphicsLayerCACF* owner) 191 : m_layer(AdoptCF, CACFLayerCreate(toCACFLayerType(type))) 192 , m_needsDisplayOnBoundsChange(false) 193 , m_owner(owner) 194 { 195 CACFLayerSetUserData(layer(), this); 196 CACFLayerSetDisplayCallback(layer(), displayInContext); 197 } 198 199 WKCACFLayer::~WKCACFLayer() 200 { 201 // Our superlayer should be holding a reference to us, so there should be no way for us to be destroyed while we still have a superlayer. 202 ASSERT(!superlayer()); 203 204 CACFLayerSetUserData(layer(), 0); 205 CACFLayerSetDisplayCallback(layer(), 0); 206 } 207 208 void WKCACFLayer::display(PlatformGraphicsContext* context) 209 { 210 if (!m_owner) 211 return; 212 213 CGContextSaveGState(context); 214 215 CGRect layerBounds = bounds(); 216 if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) { 217 CGContextScaleCTM(context, 1, -1); 218 CGContextTranslateCTM(context, 0, -layerBounds.size.height); 219 } 220 221 if (m_owner->client()) { 222 GraphicsContext graphicsContext(context); 223 224 // It's important to get the clip from the context, because it may be significantly 225 // smaller than the layer bounds (e.g. tiled layers) 226 CGRect clipBounds = CGContextGetClipBoundingBox(context); 227 IntRect clip(enclosingIntRect(clipBounds)); 228 m_owner->paintGraphicsLayerContents(graphicsContext, clip); 229 } 230 #ifndef NDEBUG 231 else { 232 ASSERT_NOT_REACHED(); 233 234 // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color, 235 // so CA never makes backing store for it (which is what -setNeedsDisplay will do above). 236 CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f); 237 CGContextFillRect(context, layerBounds); 238 } 239 #endif 240 241 if (m_owner->showRepaintCounter()) { 242 char text[16]; // that's a lot of repaints 243 _snprintf(text, sizeof(text), "%d", m_owner->incrementRepaintCount()); 244 245 CGContextSaveGState(context); 246 CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); 247 248 CGRect aBounds = layerBounds; 249 250 aBounds.size.width = 10 + 12 * strlen(text); 251 aBounds.size.height = 25; 252 CGContextFillRect(context, aBounds); 253 254 CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 1.0f); 255 256 CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0f, -1.0f)); 257 CGContextSelectFont(context, "Helvetica", 25, kCGEncodingMacRoman); 258 CGContextShowTextAtPoint(context, aBounds.origin.x + 3.0f, aBounds.origin.y + 20.0f, text, strlen(text)); 259 260 CGContextRestoreGState(context); 261 } 262 263 CGContextRestoreGState(context); 264 } 265 266 void WKCACFLayer::becomeRootLayerForContext(CACFContextRef context) 267 { 268 CACFContextSetLayer(context, layer()); 269 setNeedsCommit(); 270 } 271 272 void WKCACFLayer::setNeedsCommit() 273 { 274 CACFContextRef context = CACFLayerGetContext(rootLayer()->layer()); 275 276 // The context might now be set yet. This happens if a property gets set 277 // before placing the layer in the tree. In this case we don't need to 278 // worry about remembering the context because we will when the layer is 279 // added to the tree. 280 if (context) 281 WKCACFContextFlusher::shared().addContext(context); 282 283 // Call notifySyncRequired(), which in this implementation plumbs through to 284 // call setRootLayerNeedsDisplay() on the WebView, which causes the CACFRenderer 285 // to render a frame. 286 if (m_owner) 287 m_owner->notifySyncRequired(); 288 } 289 290 bool WKCACFLayer::isTransformLayer() const 291 { 292 return CACFLayerGetClass(layer()) == kCACFTransformLayer(); 293 } 294 295 void WKCACFLayer::addSublayer(PassRefPtr<WKCACFLayer> sublayer) 296 { 297 insertSublayer(sublayer, numSublayers()); 298 } 299 300 void WKCACFLayer::insertSublayer(PassRefPtr<WKCACFLayer> sublayer, size_t index) 301 { 302 index = min(index, numSublayers()); 303 CACFLayerInsertSublayer(layer(), sublayer->layer(), index); 304 setNeedsCommit(); 305 } 306 307 void WKCACFLayer::insertSublayerAboveLayer(PassRefPtr<WKCACFLayer> sublayer, const WKCACFLayer* reference) 308 { 309 if (!reference) { 310 insertSublayer(sublayer, 0); 311 return; 312 } 313 314 int referenceIndex = indexOfSublayer(reference); 315 if (referenceIndex == -1) { 316 addSublayer(sublayer); 317 return; 318 } 319 320 insertSublayer(sublayer, referenceIndex + 1); 321 } 322 323 void WKCACFLayer::insertSublayerBelowLayer(PassRefPtr<WKCACFLayer> sublayer, const WKCACFLayer* reference) 324 { 325 if (!reference) { 326 insertSublayer(sublayer, 0); 327 return; 328 } 329 330 int referenceIndex = indexOfSublayer(reference); 331 if (referenceIndex == -1) { 332 addSublayer(sublayer); 333 return; 334 } 335 336 insertSublayer(sublayer, referenceIndex); 337 } 338 339 void WKCACFLayer::replaceSublayer(WKCACFLayer* reference, PassRefPtr<WKCACFLayer> newLayer) 340 { 341 ASSERT_ARG(reference, reference); 342 ASSERT_ARG(reference, reference->superlayer() == this); 343 344 if (reference == newLayer) 345 return; 346 347 if (!newLayer) { 348 removeSublayer(reference); 349 return; 350 } 351 352 newLayer->removeFromSuperlayer(); 353 354 int referenceIndex = indexOfSublayer(reference); 355 ASSERT(referenceIndex != -1); 356 if (referenceIndex == -1) 357 return; 358 359 // FIXME: Can we make this more efficient? The current CACF API doesn't seem to give us a way to do so. 360 reference->removeFromSuperlayer(); 361 insertSublayer(newLayer, referenceIndex); 362 } 363 364 void WKCACFLayer::removeFromSuperlayer() 365 { 366 WKCACFLayer* superlayer = this->superlayer(); 367 if (!superlayer) 368 return; 369 370 superlayer->removeSublayer(this); 371 CACFLayerRemoveFromSuperlayer(layer()); 372 superlayer->setNeedsCommit(); 373 } 374 375 void WKCACFLayer::removeSublayer(const WKCACFLayer* sublayer) 376 { 377 int foundIndex = indexOfSublayer(sublayer); 378 if (foundIndex == -1) 379 return; 380 381 CACFLayerRemoveFromSuperlayer(sublayer->layer()); 382 setNeedsCommit(); 383 } 384 385 const WKCACFLayer* WKCACFLayer::sublayerAtIndex(int index) const 386 { 387 CFArrayRef sublayers = CACFLayerGetSublayers(layer()); 388 if (index < 0 || CFArrayGetCount(sublayers) <= index) 389 return 0; 390 391 return layer(static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index)))); 392 } 393 394 int WKCACFLayer::indexOfSublayer(const WKCACFLayer* reference) 395 { 396 CACFLayerRef ref = reference->layer(); 397 if (!ref) 398 return -1; 399 400 CFArrayRef sublayers = CACFLayerGetSublayers(layer()); 401 size_t n = CFArrayGetCount(sublayers); 402 403 for (size_t i = 0; i < n; ++i) 404 if (CFArrayGetValueAtIndex(sublayers, i) == ref) 405 return i; 406 407 return -1; 408 } 409 410 WKCACFLayer* WKCACFLayer::ancestorOrSelfWithSuperlayer(WKCACFLayer* superlayer) const 411 { 412 WKCACFLayer* layer = const_cast<WKCACFLayer*>(this); 413 for (WKCACFLayer* ancestor = this->superlayer(); ancestor; layer = ancestor, ancestor = ancestor->superlayer()) { 414 if (ancestor == superlayer) 415 return layer; 416 } 417 return 0; 418 } 419 420 void WKCACFLayer::setBounds(const CGRect& rect) 421 { 422 if (CGRectEqualToRect(rect, bounds())) 423 return; 424 425 CACFLayerSetBounds(layer(), rect); 426 setNeedsCommit(); 427 428 if (m_needsDisplayOnBoundsChange) 429 setNeedsDisplay(); 430 } 431 432 void WKCACFLayer::setFrame(const CGRect& rect) 433 { 434 CGRect oldFrame = frame(); 435 if (CGRectEqualToRect(rect, oldFrame)) 436 return; 437 438 CACFLayerSetFrame(layer(), rect); 439 setNeedsCommit(); 440 441 if (m_needsDisplayOnBoundsChange) 442 setNeedsDisplay(); 443 } 444 445 void WKCACFLayer::setContentsGravity(ContentsGravityType type) 446 { 447 CACFLayerSetContentsGravity(layer(), toCACFContentsGravityType(type)); 448 setNeedsCommit(); 449 } 450 451 WKCACFLayer::ContentsGravityType WKCACFLayer::contentsGravity() const 452 { 453 return fromCACFContentsGravityType(CACFLayerGetContentsGravity(layer())); 454 } 455 456 void WKCACFLayer::setMagnificationFilter(FilterType type) 457 { 458 CACFLayerSetMagnificationFilter(layer(), toCACFFilterType(type)); 459 setNeedsCommit(); 460 } 461 462 WKCACFLayer::FilterType WKCACFLayer::magnificationFilter() const 463 { 464 return fromCACFFilterType(CACFLayerGetMagnificationFilter(layer())); 465 } 466 467 void WKCACFLayer::setMinificationFilter(FilterType type) 468 { 469 CACFLayerSetMinificationFilter(layer(), toCACFFilterType(type)); 470 setNeedsCommit(); 471 } 472 473 WKCACFLayer::FilterType WKCACFLayer::minificationFilter() const 474 { 475 return fromCACFFilterType(CACFLayerGetMinificationFilter(layer())); 476 } 477 478 WKCACFLayer* WKCACFLayer::rootLayer() const 479 { 480 WKCACFLayer* layer = const_cast<WKCACFLayer*>(this); 481 for (WKCACFLayer* superlayer = layer->superlayer(); superlayer; layer = superlayer, superlayer = superlayer->superlayer()) { } 482 return layer; 483 } 484 485 void WKCACFLayer::removeAllSublayers() 486 { 487 CACFLayerSetSublayers(layer(), 0); 488 setNeedsCommit(); 489 } 490 491 void WKCACFLayer::setSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers) 492 { 493 if (sublayers.isEmpty()) 494 CACFLayerSetSublayers(layer(), 0); 495 else { 496 // Create a vector of CACFLayers. 497 Vector<const void*> layers; 498 for (size_t i = 0; i < sublayers.size(); i++) 499 layers.append(sublayers[i]->layer()); 500 501 RetainPtr<CFArrayRef> layersArray(AdoptCF, CFArrayCreate(0, layers.data(), layers.size(), 0)); 502 CACFLayerSetSublayers(layer(), layersArray.get()); 503 } 504 505 setNeedsCommit(); 506 } 507 508 void WKCACFLayer::moveSublayers(WKCACFLayer* fromLayer, WKCACFLayer* toLayer) 509 { 510 if (!fromLayer || !toLayer) 511 return; 512 513 CACFLayerSetSublayers(toLayer->layer(), CACFLayerGetSublayers(fromLayer->layer())); 514 fromLayer->setNeedsCommit(); 515 toLayer->setNeedsCommit(); 516 } 517 518 WKCACFLayer* WKCACFLayer::superlayer() const 519 { 520 CACFLayerRef super = CACFLayerGetSuperlayer(layer()); 521 if (!super) 522 return 0; 523 return WKCACFLayer::layer(super); 524 } 525 526 void WKCACFLayer::setNeedsDisplay(const CGRect& dirtyRect) 527 { 528 if (m_owner) 529 CACFLayerSetNeedsDisplay(layer(), &dirtyRect); 530 setNeedsCommit(); 531 } 532 533 void WKCACFLayer::setNeedsDisplay() 534 { 535 if (m_owner) 536 CACFLayerSetNeedsDisplay(layer(), 0); 537 setNeedsCommit(); 538 } 539 540 #ifndef NDEBUG 541 static void printIndent(int indent) 542 { 543 for ( ; indent > 0; --indent) 544 fprintf(stderr, " "); 545 } 546 547 static void printTransform(const CATransform3D& transform) 548 { 549 fprintf(stderr, "[%g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g]", 550 transform.m11, transform.m12, transform.m13, transform.m14, 551 transform.m21, transform.m22, transform.m23, transform.m24, 552 transform.m31, transform.m32, transform.m33, transform.m34, 553 transform.m41, transform.m42, transform.m43, transform.m44); 554 } 555 556 void WKCACFLayer::printTree() const 557 { 558 // Print heading info 559 CGRect rootBounds = bounds(); 560 fprintf(stderr, "\n\n** Render tree at time %g (bounds %g, %g %gx%g) **\n\n", 561 currentTime(), rootBounds.origin.x, rootBounds.origin.y, rootBounds.size.width, rootBounds.size.height); 562 563 // Print layer tree from the root 564 printLayer(0); 565 } 566 567 void WKCACFLayer::printLayer(int indent) const 568 { 569 CGPoint layerPosition = position(); 570 CGPoint layerAnchorPoint = anchorPoint(); 571 CGRect layerBounds = bounds(); 572 printIndent(indent); 573 fprintf(stderr, "(%s [%g %g %g] [%g %g %g %g] [%g %g %g]\n", 574 isTransformLayer() ? "transform-layer" : "layer", 575 layerPosition.x, layerPosition.y, zPosition(), 576 layerBounds.origin.x, layerBounds.origin.y, layerBounds.size.width, layerBounds.size.height, 577 layerAnchorPoint.x, layerAnchorPoint.y, anchorPointZ()); 578 579 // Print name if needed 580 String layerName = name(); 581 if (!layerName.isEmpty()) { 582 printIndent(indent + 1); 583 fprintf(stderr, "(name %s)\n", layerName.utf8().data()); 584 } 585 586 // Print masksToBounds if needed 587 bool layerMasksToBounds = masksToBounds(); 588 if (layerMasksToBounds) { 589 printIndent(indent + 1); 590 fprintf(stderr, "(masksToBounds true)\n"); 591 } 592 593 // Print opacity if needed 594 float layerOpacity = opacity(); 595 if (layerOpacity != 1) { 596 printIndent(indent + 1); 597 fprintf(stderr, "(opacity %hf)\n", layerOpacity); 598 } 599 600 // Print sublayerTransform if needed 601 CATransform3D layerTransform = sublayerTransform(); 602 if (!CATransform3DIsIdentity(layerTransform)) { 603 printIndent(indent + 1); 604 fprintf(stderr, "(sublayerTransform "); 605 printTransform(layerTransform); 606 fprintf(stderr, ")\n"); 607 } 608 609 // Print transform if needed 610 layerTransform = transform(); 611 if (!CATransform3DIsIdentity(layerTransform)) { 612 printIndent(indent + 1); 613 fprintf(stderr, "(transform "); 614 printTransform(layerTransform); 615 fprintf(stderr, ")\n"); 616 } 617 618 // Print contents if needed 619 CGImageRef layerContents = contents(); 620 if (layerContents) { 621 printIndent(indent + 1); 622 fprintf(stderr, "(contents (image [%d %d]))\n", 623 CGImageGetWidth(layerContents), CGImageGetHeight(layerContents)); 624 } 625 626 // Print sublayers if needed 627 int n = numSublayers(); 628 if (n > 0) { 629 printIndent(indent + 1); 630 fprintf(stderr, "(sublayers\n"); 631 for (int i = 0; i < n; ++i) 632 sublayerAtIndex(i)->printLayer(indent + 2); 633 634 printIndent(indent + 1); 635 fprintf(stderr, ")\n"); 636 } 637 638 printIndent(indent); 639 fprintf(stderr, ")\n"); 640 } 641 #endif // #ifndef NDEBUG 642 } 643 644 #endif // USE(ACCELERATED_COMPOSITING) 645