Reviewed by Eric. Rubber stamped by Oliver.
[WebKit-https.git] / WebCore / platform / mac / GraphicsContextMac.mm
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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 #import "SVGResourceImage.h"
29
30 #import "../graphics/cg/GraphicsContextPlatformPrivate.h"
31
32 #import "WebCoreSystemInterface.h"
33
34 // FIXME: More of this should use CoreGraphics instead of AppKit.
35 // FIXME: More of this should move into GraphicsContextCG.cpp.
36
37 namespace WebCore {
38
39 // NSColor, NSBezierPath, and NSGraphicsContext
40 // calls in this file are all exception-safe, so we don't block
41 // exceptions for those.
42
43 void GraphicsContext::drawFocusRing(const Color& color)
44 {
45     if (paintingDisabled())
46         return;
47
48     int radius = (focusRingWidth() - 1) / 2;
49     int offset = radius + focusRingOffset();
50     CGColorRef colorRef = color.isValid() ? cgColor(color) : 0;
51
52     CGMutablePathRef focusRingPath = CGPathCreateMutable();
53     const Vector<IntRect>& rects = focusRingRects();
54     unsigned rectCount = rects.size();
55     for (unsigned i = 0; i < rectCount; i++)
56         CGPathAddRect(focusRingPath, 0, CGRectInset(rects[i], -offset, -offset));
57
58     CGContextRef context = platformContext();
59
60     // FIXME: This works only inside a NSView's drawRect method. The view must be
61     // focused and this context must be the current NSGraphicsContext.
62     ASSERT(context == [[NSGraphicsContext currentContext] graphicsPort]);
63     NSView* view = [NSView focusView];
64     ASSERT(view);
65
66     const NSRect* drawRects;
67 #ifdef __LP64__
68     long count;
69 #else
70     int count;
71 #endif
72     [view getRectsBeingDrawn:&drawRects count:&count];
73
74     // We have to pass in our own clip rectangles here because a bug in CG
75     // seems to inflate the clip (thus allowing the focus ring to paint
76     // slightly outside the clip).
77     NSRect transformedClipRect = [view convertRect:m_data->m_focusRingClip toView:nil];
78     for (int i = 0; i < count; ++i) {
79         NSRect transformedRect = [view convertRect:drawRects[i] toView:nil];
80         NSRect rectToUse = NSIntersectionRect(transformedRect, transformedClipRect);
81         if (!NSIsEmptyRect(rectToUse)) {
82             CGContextBeginPath(context);
83             CGContextAddPath(context, focusRingPath);
84             wkDrawFocusRing(context, *(CGRect *)&rectToUse, colorRef, radius);
85         }
86     }
87
88     CGColorRelease(colorRef);
89
90     CGPathRelease(focusRingPath);
91 }
92
93 void GraphicsContext::setCompositeOperation(CompositeOperator op)
94 {
95     if (paintingDisabled())
96         return;
97     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
98     [[NSGraphicsContext graphicsContextWithGraphicsPort:platformContext() flipped:YES]
99         setCompositingOperation:(NSCompositingOperation)op];
100     [pool release];
101 }
102
103 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& point, int width, bool grammar)
104 {
105     if (paintingDisabled())
106         return;
107         
108     // Constants for spelling pattern color
109     static NSColor *spellingPatternColor = nil;
110     static bool usingDotForSpelling = false;
111
112     // Constants for grammar pattern color
113     static NSColor *grammarPatternColor = nil;
114     static bool usingDotForGrammar = false;
115     
116     // These are the same for misspelling or bad grammar
117     int patternHeight = cMisspellingLineThickness;
118     int patternWidth = cMisspellingLinePatternWidth;
119  
120     // Initialize pattern color if needed
121     if (!grammar && !spellingPatternColor) {
122         NSImage *image = [NSImage imageNamed:@"SpellingDot"];
123         ASSERT(image); // if image is not available, we want to know
124         NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil);
125         if (color)
126             usingDotForSpelling = true;
127         else
128             color = [NSColor redColor];
129         spellingPatternColor = [color retain];
130     }
131     
132     if (grammar && !grammarPatternColor) {
133         NSImage *image = [NSImage imageNamed:@"GrammarDot"];
134         ASSERT(image); // if image is not available, we want to know
135         NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil);
136         if (color)
137             usingDotForGrammar = true;
138         else
139             color = [NSColor greenColor];
140         grammarPatternColor = [color retain];
141     }
142     
143     bool usingDot;
144     NSColor *patternColor;
145     if (grammar) {
146         usingDot = usingDotForGrammar;
147         patternColor = grammarPatternColor;
148     } else {
149         usingDot = usingDotForSpelling;
150         patternColor = spellingPatternColor;
151     }
152
153     // Make sure to draw only complete dots.
154     // NOTE: Code here used to shift the underline to the left and increase the width
155     // to make sure everything gets underlined, but that results in drawing out of
156     // bounds (e.g. when at the edge of a view) and could make it appear that the
157     // space between adjacent misspelled words was underlined.
158     if (usingDot) {
159         // allow slightly more considering that the pattern ends with a transparent pixel
160         int widthMod = width % patternWidth;
161         if (patternWidth - widthMod > cMisspellingLinePatternGapWidth)
162             width -= widthMod;
163     }
164     
165     // FIXME: This code should not use NSGraphicsContext currentContext
166     // In order to remove this requirement we will need to use CGPattern instead of NSColor
167     
168     // Draw underline
169     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
170     CGContextRef context = (CGContextRef)[currentContext graphicsPort];
171     CGContextSaveGState(context);
172
173     [patternColor set];
174
175     wkSetPatternPhaseInUserSpace(context, point);
176
177     NSRectFillUsingOperation(NSMakeRect(point.x(), point.y(), width, patternHeight), NSCompositeSourceOver);
178     
179     CGContextRestoreGState(context);
180 }
181
182 GraphicsContext* contextForImage(SVGResourceImage* image)
183 {
184     CGLayerRef cgLayer = image->cgLayer();
185
186     // FIXME: Using currentContext / graphicsPort here is a TOTAL hack. See SVGMaskElement comment for a possible solution!
187     CGContextRef currentContext = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
188
189     if (!cgLayer) {
190         ASSERT(currentContext);
191
192         // FIXME: we might not get back a layer if this is a loaded image
193         // maybe this logic should go into SVGResourceImage?
194         cgLayer = CGLayerCreateWithContext(currentContext, CGSize(image->size() + IntSize(1,1)), NULL);  // FIXME + 1 is a hack
195         // FIXME: we should composite the original image onto the layer...
196         image->setCGLayer(cgLayer);
197         CGLayerRelease(cgLayer);
198     }
199     return new GraphicsContext(CGLayerGetContext(cgLayer));
200 }
201
202 }