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#import "WebLayer.h" 31 32#import "GraphicsContext.h" 33#import "GraphicsLayerCA.h" 34#import "PlatformCALayer.h" 35#import "ThemeMac.h" 36#import "WebCoreSystemInterface.h" 37#import <objc/runtime.h> 38#import <QuartzCore/QuartzCore.h> 39 40@interface CALayer(WebCoreCALayerPrivate) 41- (void)reloadValueForKeyPath:(NSString *)keyPath; 42@end 43 44using namespace WebCore; 45 46@implementation WebLayer 47 48void drawLayerContents(CGContextRef context, CALayer *layer, WebCore::PlatformCALayer* platformLayer) 49{ 50 WebCore::PlatformCALayerClient* layerContents = platformLayer->owner(); 51 if (!layerContents) 52 return; 53 54 CGContextSaveGState(context); 55 56 CGRect layerBounds = [layer bounds]; 57 if (layerContents->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesBottomUp) { 58 CGContextScaleCTM(context, 1, -1); 59 CGContextTranslateCTM(context, 0, -layerBounds.size.height); 60 } 61 62 [NSGraphicsContext saveGraphicsState]; 63 64 // Set up an NSGraphicsContext for the context, so that parts of AppKit that rely on 65 // the current NSGraphicsContext (e.g. NSCell drawing) get the right one. 66 NSGraphicsContext* layerContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]; 67 [NSGraphicsContext setCurrentContext:layerContext]; 68 69 GraphicsContext graphicsContext(context); 70 graphicsContext.setIsCALayerContext(true); 71 graphicsContext.setIsAcceleratedContext(platformLayer->acceleratesDrawing()); 72 73 if (!layerContents->platformCALayerContentsOpaque()) { 74 // Turn off font smoothing to improve the appearance of text rendered onto a transparent background. 75 graphicsContext.setShouldSmoothFonts(false); 76 } 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 FloatRect clipBounds = CGContextGetClipBoundingBox(context); 81 82 FloatRect focusRingClipRect = clipBounds; 83#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1090 84 // Set the focus ring clip rect which needs to be in base coordinates. 85 AffineTransform transform = CGContextGetCTM(context); 86 focusRingClipRect = transform.mapRect(clipBounds); 87#endif 88 ThemeMac::setFocusRingClipRect(focusRingClipRect); 89 90#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 91 const float wastedSpaceThreshold = 0.75f; 92 const unsigned maxRectsToPaint = 5; 93 94 double clipArea = clipBounds.width() * clipBounds.height(); 95 __block double totalRectArea = 0; 96 __block unsigned rectCount = 0; 97 __block Vector<FloatRect, maxRectsToPaint> dirtyRects; 98 99 wkCALayerEnumerateRectsBeingDrawnWithBlock(layer, context, ^(CGRect rect) { 100 if (++rectCount > maxRectsToPaint) 101 return; 102 103 totalRectArea += rect.size.width * rect.size.height; 104 dirtyRects.append(rect); 105 }); 106 107 if (rectCount < maxRectsToPaint && totalRectArea < clipArea * wastedSpaceThreshold) { 108 for (unsigned i = 0; i < rectCount; ++i) { 109 const FloatRect& currentRect = dirtyRects[i]; 110 111 GraphicsContextStateSaver stateSaver(graphicsContext); 112 graphicsContext.clip(currentRect); 113 114 layerContents->platformCALayerPaintContents(graphicsContext, enclosingIntRect(currentRect)); 115 } 116 } else { 117 // CGContextGetClipBoundingBox() gives us the bounds of the dirty region, so clipBounds 118 // encompasses all the dirty rects. 119 layerContents->platformCALayerPaintContents(graphicsContext, enclosingIntRect(clipBounds)); 120 } 121 122#else 123 IntRect clip(enclosingIntRect(clipBounds)); 124 layerContents->platformCALayerPaintContents(graphicsContext, clip); 125#endif 126 127 ThemeMac::setFocusRingClipRect(FloatRect()); 128 129 [NSGraphicsContext restoreGraphicsState]; 130 131 // Re-fetch the layer owner, since <rdar://problem/9125151> indicates that it might have been destroyed during painting. 132 layerContents = platformLayer->owner(); 133 ASSERT(layerContents); 134 135 // Always update the repain count so that it's accurate even if the count itself is not shown. This will be useful 136 // for the Web Inspector feeding this information through the LayerTreeAgent. 137 int repaintCount = layerContents->platformCALayerIncrementRepaintCount(); 138 139 if (!platformLayer->usesTiledBackingLayer() && layerContents && layerContents->platformCALayerShowRepaintCounter(platformLayer)) { 140 bool isTiledLayer = [layer isKindOfClass:[CATiledLayer class]]; 141 142 char text[16]; // that's a lot of repaints 143 snprintf(text, sizeof(text), "%d", repaintCount); 144 145 CGRect indicatorBox = layerBounds; 146 indicatorBox.size.width = 12 + 10 * strlen(text); 147 indicatorBox.size.height = 27; 148 CGContextSaveGState(context); 149 150 CGContextSetAlpha(context, 0.5f); 151 CGContextBeginTransparencyLayerWithRect(context, indicatorBox, 0); 152 153 if (isTiledLayer) 154 CGContextSetRGBFillColor(context, 1, 0.5f, 0, 1); 155 else 156 CGContextSetRGBFillColor(context, 0, 0.5f, 0.25f, 1); 157 158 CGContextFillRect(context, indicatorBox); 159 160 if (platformLayer->acceleratesDrawing()) 161 CGContextSetRGBFillColor(context, 1, 0, 0, 1); 162 else 163 CGContextSetRGBFillColor(context, 1, 1, 1, 1); 164 165#pragma clang diagnostic push 166#pragma clang diagnostic ignored "-Wdeprecated-declarations" 167 CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1, -1)); 168 CGContextSelectFont(context, "Helvetica", 22, kCGEncodingMacRoman); 169 CGContextShowTextAtPoint(context, indicatorBox.origin.x + 5, indicatorBox.origin.y + 22, text, strlen(text)); 170#pragma clang diagnostic pop 171 172 CGContextEndTransparencyLayer(context); 173 CGContextRestoreGState(context); 174 } 175 176 CGContextRestoreGState(context); 177} 178 179 180- (id<CAAction>)actionForKey:(NSString *)key 181{ 182 // Fix for <rdar://problem/9015675>: Force the layer content to be updated when the tree is reparented. 183 if ([key isEqualToString:@"onOrderIn"]) 184 [self reloadValueForKeyPath:@"contents"]; 185 186 return nil; 187} 188 189- (void)setNeedsDisplay 190{ 191 PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); 192 if (layer && layer->owner() && layer->owner()->platformCALayerDrawsContent()) 193 [super setNeedsDisplay]; 194} 195 196- (void)setNeedsDisplayInRect:(CGRect)dirtyRect 197{ 198 PlatformCALayer* platformLayer = PlatformCALayer::platformCALayer(self); 199 if (!platformLayer) { 200 [super setNeedsDisplayInRect:dirtyRect]; 201 return; 202 } 203 204 if (PlatformCALayerClient* layerOwner = platformLayer->owner()) { 205 if (layerOwner->platformCALayerDrawsContent()) { 206 if (layerOwner->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesBottomUp) 207 dirtyRect.origin.y = [self bounds].size.height - dirtyRect.origin.y - dirtyRect.size.height; 208 209 [super setNeedsDisplayInRect:dirtyRect]; 210 211 if (layerOwner->platformCALayerShowRepaintCounter(platformLayer)) { 212 CGRect bounds = [self bounds]; 213 CGRect indicatorRect = CGRectMake(bounds.origin.x, bounds.origin.y, 52, 27); 214 if (layerOwner->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesBottomUp) 215 indicatorRect.origin.y = [self bounds].size.height - indicatorRect.origin.y - indicatorRect.size.height; 216 217 [super setNeedsDisplayInRect:indicatorRect]; 218 } 219 } 220 } 221} 222 223- (void)display 224{ 225 [super display]; 226 PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); 227 if (layer && layer->owner()) 228 layer->owner()->platformCALayerLayerDidDisplay(self); 229} 230 231- (void)drawInContext:(CGContextRef)context 232{ 233 PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); 234 if (layer) 235 drawLayerContents(context, self, layer); 236} 237 238@end // implementation WebLayer 239 240// MARK: - 241 242#ifndef NDEBUG 243 244@implementation CALayer(ExtendedDescription) 245 246- (NSString*)_descriptionWithPrefix:(NSString*)inPrefix 247{ 248 CGRect aBounds = [self bounds]; 249 CGPoint aPos = [self position]; 250 251 NSString* selfString = [NSString stringWithFormat:@"%@<%@ 0x%p> \"%@\" bounds(%.1f, %.1f, %.1f, %.1f) pos(%.1f, %.1f), sublayers=%lu masking=%d", 252 inPrefix, 253 [self class], 254 self, 255 [self name], 256 aBounds.origin.x, aBounds.origin.y, aBounds.size.width, aBounds.size.height, 257 aPos.x, aPos.y, 258 static_cast<unsigned long>([[self sublayers] count]), 259 [self masksToBounds]]; 260 261 NSMutableString* curDesc = [NSMutableString stringWithString:selfString]; 262 263 if ([[self sublayers] count] > 0) 264 [curDesc appendString:@"\n"]; 265 266 NSString* sublayerPrefix = [inPrefix stringByAppendingString:@"\t"]; 267 268 NSEnumerator* sublayersEnum = [[self sublayers] objectEnumerator]; 269 CALayer* curLayer; 270 while ((curLayer = [sublayersEnum nextObject])) 271 [curDesc appendString:[curLayer _descriptionWithPrefix:sublayerPrefix]]; 272 273 if ([[self sublayers] count] == 0) 274 [curDesc appendString:@"\n"]; 275 276 return curDesc; 277} 278 279- (NSString*)extendedDescription 280{ 281 return [self _descriptionWithPrefix:@""]; 282} 283 284@end // implementation WebLayer(ExtendedDescription) 285 286#endif // NDEBUG 287 288#endif // USE(ACCELERATED_COMPOSITING) 289