1/* 2 * Copyright (C) 2003, 2004, 2005, 2006, 2010 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 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#import "config.h" 27#import "GraphicsContext.h" 28 29#import "GraphicsContextCG.h" 30#import "GraphicsContextPlatformPrivateCG.h" 31#import "IntRect.h" 32#if USE(APPKIT) 33#import <AppKit/AppKit.h> 34#endif 35#import <wtf/StdLibExtras.h> 36 37#if PLATFORM(IOS) 38#import "Color.h" 39#import "WKGraphics.h" 40#endif 41 42#if !PLATFORM(IOS) 43#import "LocalCurrentGraphicsContext.h" 44#endif 45#import "WebCoreSystemInterface.h" 46 47@class NSColor; 48 49// FIXME: More of this should use CoreGraphics instead of AppKit. 50// FIXME: More of this should move into GraphicsContextCG.cpp. 51 52namespace WebCore { 53 54// NSColor, NSBezierPath, and NSGraphicsContext 55// calls in this file are all exception-safe, so we don't block 56// exceptions for those. 57 58#if !PLATFORM(IOS) 59static void drawFocusRingToContext(CGContextRef context, CGPathRef focusRingPath) 60{ 61 CGContextBeginPath(context); 62 CGContextAddPath(context, focusRingPath); 63 wkDrawFocusRing(context, nullptr, 0); 64} 65 66static bool drawFocusRingToContextAtTime(CGContextRef context, CGPathRef focusRingPath, double timeOffset) 67{ 68 UNUSED_PARAM(timeOffset); 69 CGContextBeginPath(context); 70 CGContextAddPath(context, focusRingPath); 71 return wkDrawFocusRingAtTime(context, std::numeric_limits<double>::max()); 72} 73#endif // !PLATFORM(IOS) 74 75void GraphicsContext::drawFocusRing(const Path& path, int /* width */, int /* offset */, const Color&) 76{ 77#if PLATFORM(MAC) 78 if (paintingDisabled() || path.isNull()) 79 return; 80 81 drawFocusRingToContext(platformContext(), path.platformPath()); 82#else 83 UNUSED_PARAM(path); 84#endif 85} 86 87#if PLATFORM(MAC) 88void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, double timeOffset, bool& needsRedraw) 89{ 90 if (paintingDisabled()) 91 return; 92 93 offset += (width - 1) / 2; 94 95 RetainPtr<CGMutablePathRef> focusRingPath = adoptCF(CGPathCreateMutable()); 96 for (auto& rect : rects) 97 CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rect, -offset, -offset)); 98 99 needsRedraw = drawFocusRingToContextAtTime(platformContext(), focusRingPath.get(), timeOffset); 100} 101#endif 102 103void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color&) 104{ 105#if !PLATFORM(IOS) 106 if (paintingDisabled()) 107 return; 108 109 offset += (width - 1) / 2; 110 111 RetainPtr<CGMutablePathRef> focusRingPath = adoptCF(CGPathCreateMutable()); 112 for (auto& rect : rects) 113 CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rect, -offset, -offset)); 114 115 drawFocusRingToContext(platformContext(), focusRingPath.get()); 116#else 117 UNUSED_PARAM(rects); 118 UNUSED_PARAM(width); 119 UNUSED_PARAM(offset); 120#endif 121} 122 123#if !PLATFORM(IOS) 124static NSColor* makePatternColor(NSString* firstChoiceName, NSString* secondChoiceName, NSColor* defaultColor, bool& usingDot) 125{ 126 // Eventually we should be able to get rid of the secondChoiceName. For the time being we need both to keep 127 // this working on all platforms. 128 NSImage *image = [NSImage imageNamed:firstChoiceName]; 129 if (!image) 130 image = [NSImage imageNamed:secondChoiceName]; 131 ASSERT(image); // if image is not available, we want to know 132 NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil); 133 if (color) 134 usingDot = true; 135 else 136 color = defaultColor; 137 return color; 138} 139#else 140static RetainPtr<CGPatternRef> createDotPattern(bool& usingDot, const char* resourceName) 141{ 142 RetainPtr<CGImageRef> image = adoptCF(WKGraphicsCreateImageFromBundleWithName(resourceName)); 143 ASSERT(image); // if image is not available, we want to know 144 usingDot = true; 145 return adoptCF(WKCreatePatternFromCGImage(image.get())); 146} 147#endif // !PLATFORM(IOS) 148 149static NSColor *spellingPatternColor = nullptr; 150static NSColor *grammarPatternColor = nullptr; 151static NSColor *correctionPatternColor = nullptr; 152 153void GraphicsContext::updateDocumentMarkerResources() 154{ 155 spellingPatternColor = nullptr; 156 grammarPatternColor = nullptr; 157 correctionPatternColor = nullptr; 158} 159 160// WebKit on Mac is a standard platform component, so it must use the standard platform artwork for underline. 161void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& point, float width, DocumentMarkerLineStyle style) 162{ 163 if (paintingDisabled()) 164 return; 165 166 // These are the same for misspelling or bad grammar. 167 int patternHeight = cMisspellingLineThickness; 168 float patternWidth = cMisspellingLinePatternWidth; 169 170 bool usingDot; 171#if !PLATFORM(IOS) 172 NSColor *patternColor; 173#else 174 CGPatternRef dotPattern; 175#endif 176 switch (style) { 177 case DocumentMarkerSpellingLineStyle: 178 { 179 // Constants for spelling pattern color. 180 static bool usingDotForSpelling = false; 181#if !PLATFORM(IOS) 182 if (!spellingPatternColor) 183 spellingPatternColor = [makePatternColor(@"NSSpellingDot", @"SpellingDot", [NSColor redColor], usingDotForSpelling) retain]; 184 usingDot = usingDotForSpelling; 185 patternColor = spellingPatternColor; 186#else 187 static CGPatternRef spellingPattern = createDotPattern(usingDotForSpelling, "SpellingDot").leakRef(); 188 dotPattern = spellingPattern; 189#endif 190 usingDot = usingDotForSpelling; 191 break; 192 } 193 case DocumentMarkerGrammarLineStyle: 194 { 195#if !PLATFORM(IOS) 196 // Constants for grammar pattern color. 197 static bool usingDotForGrammar = false; 198 if (!grammarPatternColor) 199 grammarPatternColor = [makePatternColor(@"NSGrammarDot", @"GrammarDot", [NSColor greenColor], usingDotForGrammar) retain]; 200 usingDot = usingDotForGrammar; 201 patternColor = grammarPatternColor; 202 break; 203#else 204 ASSERT_NOT_REACHED(); 205 return; 206#endif 207 } 208#if PLATFORM(MAC) 209 // To support correction panel. 210 case DocumentMarkerAutocorrectionReplacementLineStyle: 211 case DocumentMarkerDictationAlternativesLineStyle: 212 { 213 // Constants for spelling pattern color. 214 static bool usingDotForSpelling = false; 215 if (!correctionPatternColor) 216 correctionPatternColor = [makePatternColor(@"NSCorrectionDot", @"CorrectionDot", [NSColor blueColor], usingDotForSpelling) retain]; 217 usingDot = usingDotForSpelling; 218 patternColor = correctionPatternColor; 219 break; 220 } 221#endif 222#if PLATFORM(IOS) 223 case TextCheckingDictationPhraseWithAlternativesLineStyle: 224 { 225 static bool usingDotForDictationPhraseWithAlternatives = false; 226 static CGPatternRef dictationPhraseWithAlternativesPattern = createDotPattern(usingDotForDictationPhraseWithAlternatives, "DictationPhraseWithAlternativesDot").leakRef(); 227 dotPattern = dictationPhraseWithAlternativesPattern; 228 usingDot = usingDotForDictationPhraseWithAlternatives; 229 break; 230 } 231#endif // PLATFORM(IOS) 232 default: 233#if PLATFORM(IOS) 234 // FIXME: Should remove default case so we get compile-time errors. 235 ASSERT_NOT_REACHED(); 236#endif // PLATFORM(IOS) 237 return; 238 } 239 240 FloatPoint offsetPoint = point; 241 242 // Make sure to draw only complete dots. 243 if (usingDot) { 244 // allow slightly more considering that the pattern ends with a transparent pixel 245 float widthMod = fmodf(width, patternWidth); 246 if (patternWidth - widthMod > cMisspellingLinePatternGapWidth) { 247 float gapIncludeWidth = 0; 248 if (width > patternWidth) 249 gapIncludeWidth = cMisspellingLinePatternGapWidth; 250 offsetPoint.move(floor((widthMod + gapIncludeWidth) / 2), 0); 251 width -= widthMod; 252 } 253 } 254 255 // FIXME: This code should not use NSGraphicsContext currentContext 256 // In order to remove this requirement we will need to use CGPattern instead of NSColor 257 // FIXME: This code should not be using wkSetPatternPhaseInUserSpace, as this approach is wrong 258 // for transforms. 259 260 // Draw underline. 261#if !PLATFORM(IOS) 262 LocalCurrentGraphicsContext localContext(this); 263 NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; 264 CGContextRef context = (CGContextRef)[currentContext graphicsPort]; 265#else 266 CGContextRef context = platformContext(); 267#endif 268 CGContextSaveGState(context); 269 270#if !PLATFORM(IOS) 271 [patternColor set]; 272#else 273 WKSetPattern(context, dotPattern, YES, YES); 274#endif 275 276 wkSetPatternPhaseInUserSpace(context, offsetPoint); 277 278#if !PLATFORM(IOS) 279 NSRectFillUsingOperation(NSMakeRect(offsetPoint.x(), offsetPoint.y(), width, patternHeight), NSCompositeSourceOver); 280#else 281 WKRectFillUsingOperation(context, CGRectMake(offsetPoint.x(), offsetPoint.y(), width, patternHeight), kCGCompositeSover); 282#endif 283 284 CGContextRestoreGState(context); 285} 286 287#if !PLATFORM(IOS) 288CGColorSpaceRef linearRGBColorSpaceRef() 289{ 290 static CGColorSpaceRef linearSRGBSpace = 0; 291 292 if (linearSRGBSpace) 293 return linearSRGBSpace; 294 295 RetainPtr<NSString> iccProfilePath = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"linearSRGB" ofType:@"icc"]; 296 RetainPtr<NSData> iccProfileData = adoptNS([[NSData alloc] initWithContentsOfFile:iccProfilePath.get()]); 297 298 if (iccProfileData) 299 linearSRGBSpace = CGColorSpaceCreateWithICCProfile((CFDataRef)iccProfileData.get()); 300 301 // If we fail to load the linearized sRGB ICC profile, fall back to DeviceRGB. 302 if (!linearSRGBSpace) 303 return deviceRGBColorSpaceRef(); 304 305 return linearSRGBSpace; 306} 307#endif 308 309} 310