• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
displayInContext(CACFLayerRef layer,CGContextRef context)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)
STATIC_CACF_STRING(kCACFTransformLayer)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 
toCACFContentsGravityType(WKCACFLayer::ContentsGravityType type)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 
fromCACFContentsGravityType(CFStringRef string)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 
toCACFFilterType(WKCACFLayer::FilterType type)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 
fromCACFFilterType(CFStringRef string)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 
create(LayerType type,GraphicsLayerCACF * owner)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 
WKCACFLayer(LayerType type,GraphicsLayerCACF * owner)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 
~WKCACFLayer()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 
display(PlatformGraphicsContext * context)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 
becomeRootLayerForContext(CACFContextRef context)266 void WKCACFLayer::becomeRootLayerForContext(CACFContextRef context)
267 {
268     CACFContextSetLayer(context, layer());
269     setNeedsCommit();
270 }
271 
setNeedsCommit()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 
isTransformLayer() const290 bool WKCACFLayer::isTransformLayer() const
291 {
292     return CACFLayerGetClass(layer()) == kCACFTransformLayer();
293 }
294 
addSublayer(PassRefPtr<WKCACFLayer> sublayer)295 void WKCACFLayer::addSublayer(PassRefPtr<WKCACFLayer> sublayer)
296 {
297     insertSublayer(sublayer, numSublayers());
298 }
299 
insertSublayer(PassRefPtr<WKCACFLayer> sublayer,size_t index)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 
insertSublayerAboveLayer(PassRefPtr<WKCACFLayer> sublayer,const WKCACFLayer * reference)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 
insertSublayerBelowLayer(PassRefPtr<WKCACFLayer> sublayer,const WKCACFLayer * reference)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 
replaceSublayer(WKCACFLayer * reference,PassRefPtr<WKCACFLayer> newLayer)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 
removeFromSuperlayer()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 
removeSublayer(const WKCACFLayer * sublayer)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 
sublayerAtIndex(int index) const385 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 
indexOfSublayer(const WKCACFLayer * reference)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 
ancestorOrSelfWithSuperlayer(WKCACFLayer * superlayer) const410 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 
setBounds(const CGRect & rect)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 
setFrame(const CGRect & rect)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 
setContentsGravity(ContentsGravityType type)445 void WKCACFLayer::setContentsGravity(ContentsGravityType type)
446 {
447     CACFLayerSetContentsGravity(layer(), toCACFContentsGravityType(type));
448     setNeedsCommit();
449 }
450 
contentsGravity() const451 WKCACFLayer::ContentsGravityType WKCACFLayer::contentsGravity() const
452 {
453     return fromCACFContentsGravityType(CACFLayerGetContentsGravity(layer()));
454 }
455 
setMagnificationFilter(FilterType type)456 void WKCACFLayer::setMagnificationFilter(FilterType type)
457 {
458     CACFLayerSetMagnificationFilter(layer(), toCACFFilterType(type));
459     setNeedsCommit();
460 }
461 
magnificationFilter() const462 WKCACFLayer::FilterType WKCACFLayer::magnificationFilter() const
463 {
464     return fromCACFFilterType(CACFLayerGetMagnificationFilter(layer()));
465 }
466 
setMinificationFilter(FilterType type)467 void WKCACFLayer::setMinificationFilter(FilterType type)
468 {
469     CACFLayerSetMinificationFilter(layer(), toCACFFilterType(type));
470     setNeedsCommit();
471 }
472 
minificationFilter() const473 WKCACFLayer::FilterType WKCACFLayer::minificationFilter() const
474 {
475     return fromCACFFilterType(CACFLayerGetMinificationFilter(layer()));
476 }
477 
rootLayer() const478 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 
removeAllSublayers()485 void WKCACFLayer::removeAllSublayers()
486 {
487     CACFLayerSetSublayers(layer(), 0);
488     setNeedsCommit();
489 }
490 
setSublayers(const Vector<RefPtr<WKCACFLayer>> & sublayers)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 
moveSublayers(WKCACFLayer * fromLayer,WKCACFLayer * toLayer)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 
superlayer() const518 WKCACFLayer* WKCACFLayer::superlayer() const
519 {
520     CACFLayerRef super = CACFLayerGetSuperlayer(layer());
521     if (!super)
522         return 0;
523     return WKCACFLayer::layer(super);
524 }
525 
setNeedsDisplay(const CGRect & dirtyRect)526 void WKCACFLayer::setNeedsDisplay(const CGRect& dirtyRect)
527 {
528     if (m_owner)
529         CACFLayerSetNeedsDisplay(layer(), &dirtyRect);
530     setNeedsCommit();
531 }
532 
setNeedsDisplay()533 void WKCACFLayer::setNeedsDisplay()
534 {
535     if (m_owner)
536         CACFLayerSetNeedsDisplay(layer(), 0);
537     setNeedsCommit();
538 }
539 
540 #ifndef NDEBUG
printIndent(int indent)541 static void printIndent(int indent)
542 {
543     for ( ; indent > 0; --indent)
544         fprintf(stderr, "  ");
545 }
546 
printTransform(const CATransform3D & transform)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 
printTree() const556 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 
printLayer(int indent) const567 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