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 COMPUTER, 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#import "config.h"
27#import "GraphicsContext.h"
28
29#import "GraphicsContextCG.h"
30#import "GraphicsContextPlatformPrivateCG.h"
31#import <AppKit/AppKit.h>
32#import <wtf/StdLibExtras.h>
33
34#import "LocalCurrentGraphicsContext.h"
35#import "WebCoreSystemInterface.h"
36
37@class NSColor;
38
39// FIXME: More of this should use CoreGraphics instead of AppKit.
40// FIXME: More of this should move into GraphicsContextCG.cpp.
41
42namespace WebCore {
43
44// NSColor, NSBezierPath, and NSGraphicsContext
45// calls in this file are all exception-safe, so we don't block
46// exceptions for those.
47
48static void drawFocusRingToContext(CGContextRef context, CGPathRef focusRingPath, CGColorRef color, int radius)
49{
50    CGContextBeginPath(context);
51    CGContextAddPath(context, focusRingPath);
52    wkDrawFocusRing(context, color, radius);
53}
54
55void GraphicsContext::drawFocusRing(const Path& path, int width, int /*offset*/, const Color& color)
56{
57    // FIXME: Use 'offset' for something? http://webkit.org/b/49909
58
59    if (paintingDisabled() || path.isNull())
60        return;
61
62    int radius = (width - 1) / 2;
63    CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0;
64
65    drawFocusRingToContext(platformContext(), path.platformPath(), colorRef, radius);
66}
67
68void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
69{
70    if (paintingDisabled())
71        return;
72
73    int radius = (width - 1) / 2;
74    offset += radius;
75    CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0;
76
77    RetainPtr<CGMutablePathRef> focusRingPath = adoptCF(CGPathCreateMutable());
78    unsigned rectCount = rects.size();
79    for (unsigned i = 0; i < rectCount; i++)
80        CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rects[i], -offset, -offset));
81
82    drawFocusRingToContext(platformContext(), focusRingPath.get(), colorRef, radius);
83}
84
85
86static NSColor* createPatternColor(NSString* firstChoiceName, NSString* secondChoiceName, NSColor* defaultColor, bool& usingDot)
87{
88    // Eventually we should be able to get rid of the secondChoiceName. For the time being we need both to keep
89    // this working on all platforms.
90    NSImage *image = [NSImage imageNamed:firstChoiceName];
91    if (!image)
92        image = [NSImage imageNamed:secondChoiceName];
93    ASSERT(image); // if image is not available, we want to know
94    NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil);
95    if (color)
96        usingDot = true;
97    else
98        color = defaultColor;
99    return color;
100}
101
102// WebKit on Mac is a standard platform component, so it must use the standard platform artwork for underline.
103void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& point, float width, DocumentMarkerLineStyle style)
104{
105    if (paintingDisabled())
106        return;
107
108    // These are the same for misspelling or bad grammar.
109    int patternHeight = cMisspellingLineThickness;
110    float patternWidth = cMisspellingLinePatternWidth;
111
112    bool usingDot;
113    NSColor *patternColor;
114    switch (style) {
115        case DocumentMarkerSpellingLineStyle:
116        {
117            // Constants for spelling pattern color.
118            static bool usingDotForSpelling = false;
119            DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, spellingPatternColor, (createPatternColor(@"NSSpellingDot", @"SpellingDot", [NSColor redColor], usingDotForSpelling)));
120            usingDot = usingDotForSpelling;
121            patternColor = spellingPatternColor.get();
122            break;
123        }
124        case DocumentMarkerGrammarLineStyle:
125        {
126            // Constants for grammar pattern color.
127            static bool usingDotForGrammar = false;
128            DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, grammarPatternColor, (createPatternColor(@"NSGrammarDot", @"GrammarDot", [NSColor greenColor], usingDotForGrammar)));
129            usingDot = usingDotForGrammar;
130            patternColor = grammarPatternColor.get();
131            break;
132        }
133#if PLATFORM(MAC) && (PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
134        // To support correction panel.
135        case DocumentMarkerAutocorrectionReplacementLineStyle:
136        case DocumentMarkerDictationAlternativesLineStyle:
137        {
138            // Constants for spelling pattern color.
139            static bool usingDotForSpelling = false;
140            DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, spellingPatternColor, (createPatternColor(@"NSCorrectionDot", @"CorrectionDot", [NSColor blueColor], usingDotForSpelling)));
141            usingDot = usingDotForSpelling;
142            patternColor = spellingPatternColor.get();
143            break;
144        }
145#endif
146        default:
147            return;
148    }
149
150    FloatPoint offsetPoint = point;
151
152    // Make sure to draw only complete dots.
153    // NOTE: Code here used to shift the underline to the left and increase the width
154    // to make sure everything gets underlined, but that results in drawing out of
155    // bounds (e.g. when at the edge of a view) and could make it appear that the
156    // space between adjacent misspelled words was underlined.
157    if (usingDot) {
158        // allow slightly more considering that the pattern ends with a transparent pixel
159        float widthMod = fmodf(width, patternWidth);
160        if (patternWidth - widthMod > cMisspellingLinePatternGapWidth) {
161            offsetPoint.move(widthMod / 2, 0);
162            width -= widthMod;
163        }
164    }
165
166    // FIXME: This code should not use NSGraphicsContext currentContext
167    // In order to remove this requirement we will need to use CGPattern instead of NSColor
168    // FIXME: This code should not be using wkSetPatternPhaseInUserSpace, as this approach is wrong
169    // for transforms.
170
171    // Draw underline.
172    LocalCurrentGraphicsContext localContext(this);
173    NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
174    CGContextRef context = (CGContextRef)[currentContext graphicsPort];
175    CGContextSaveGState(context);
176
177    [patternColor set];
178
179    wkSetPatternPhaseInUserSpace(context, offsetPoint);
180
181    NSRectFillUsingOperation(NSMakeRect(offsetPoint.x(), offsetPoint.y(), width, patternHeight), NSCompositeSourceOver);
182
183    CGContextRestoreGState(context);
184}
185
186CGColorSpaceRef linearRGBColorSpaceRef()
187{
188    static CGColorSpaceRef linearSRGBSpace = 0;
189
190    if (linearSRGBSpace)
191        return linearSRGBSpace;
192
193    RetainPtr<NSString> iccProfilePath = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"linearSRGB" ofType:@"icc"];
194    RetainPtr<NSData> iccProfileData = adoptNS([[NSData alloc] initWithContentsOfFile:iccProfilePath.get()]);
195
196    if (iccProfileData)
197        linearSRGBSpace = CGColorSpaceCreateWithICCProfile((CFDataRef)iccProfileData.get());
198
199    // If we fail to load the linearized sRGB ICC profile, fall back to DeviceRGB.
200    if (!linearSRGBSpace)
201        return deviceRGBColorSpaceRef();
202
203    return linearSRGBSpace;
204}
205
206}
207