afbfea005a4c39d057e84c187cc2e57ee536a5cd
[WebKit-https.git] / Source / WebCore / platform / graphics / win / GraphicsContextCGWin.cpp
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2013 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 #include "config.h"
27 #include "GraphicsContextCG.h"
28
29 #if USE(CG)
30
31 #include "AffineTransform.h"
32 #include "GraphicsContextImpl.h"
33 #include "GraphicsContextPlatformPrivateCG.h"
34 #include "Path.h"
35
36 #include <CoreGraphics/CGBitmapContext.h>
37 #include <WebKitSystemInterface/WebKitSystemInterface.h>
38 #include <wtf/win/GDIObject.h>
39
40
41 namespace WebCore {
42 using namespace std;
43
44 static CGContextRef CGContextWithHDC(HDC hdc, bool hasAlpha)
45 {
46     HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
47
48     DIBPixelData pixelData(bitmap);
49
50     // FIXME: We can get here because we asked for a bitmap that is too big
51     // when we have a tiled layer and we're compositing. In that case 
52     // bmBitsPixel will be 0. This seems to be benign, so for now we will
53     // exit gracefully and look at it later:
54     //  https://bugs.webkit.org/show_bug.cgi?id=52041   
55     // ASSERT(bitmapBits.bitsPerPixel() == 32);
56     if (pixelData.bitsPerPixel() != 32)
57         return 0;
58
59     CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | (hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst);
60     CGContextRef context = CGBitmapContextCreate(pixelData.buffer(), pixelData.size().width(), pixelData.size().height(), 8,
61                                                  pixelData.bytesPerRow(), deviceRGBColorSpaceRef(), bitmapInfo);
62
63     // Flip coords
64     CGContextTranslateCTM(context, 0, pixelData.size().height());
65     CGContextScaleCTM(context, 1, -1);
66     
67     // Put the HDC In advanced mode so it will honor affine transforms.
68     SetGraphicsMode(hdc, GM_ADVANCED);
69     
70     return context;
71 }
72
73 GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha)
74 {
75     platformInit(hdc, hasAlpha);
76 }
77
78 void GraphicsContext::platformInit(HDC hdc, bool hasAlpha)
79 {
80     if (!hdc)
81         return;
82
83     m_data = new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc, hasAlpha));
84     CGContextRelease(m_data->m_cgContext.get());
85     m_data->m_hdc = hdc;
86     if (m_data->m_cgContext) {
87         // Make sure the context starts in sync with our state.
88         setPlatformFillColor(fillColor());
89         setPlatformStrokeColor(strokeColor());
90     }
91 }
92
93 // FIXME: Is it possible to merge getWindowsContext and createWindowsBitmap into a single API
94 // suitable for all clients?
95 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
96 {
97     bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || isInTransparencyLayer());
98     if (!createdBitmap) {
99         m_data->restore();
100         return;
101     }
102
103     if (dstRect.isEmpty())
104         return;
105
106     auto bitmap = adoptGDIObject(static_cast<HBITMAP>(::GetCurrentObject(hdc, OBJ_BITMAP)));
107
108     DIBPixelData pixelData(bitmap.get());
109
110     ASSERT(pixelData.bitsPerPixel() == 32);
111
112     CGContextRef bitmapContext = CGBitmapContextCreate(pixelData.buffer(), pixelData.size().width(), pixelData.size().height(), 8,
113                                                        pixelData.bytesPerRow(), deviceRGBColorSpaceRef(), kCGBitmapByteOrder32Little |
114                                                        (supportAlphaBlend ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst));
115
116     CGImageRef image = CGBitmapContextCreateImage(bitmapContext);
117     CGContextDrawImage(m_data->m_cgContext.get(), dstRect, image);
118     
119     // Delete all our junk.
120     CGImageRelease(image);
121     CGContextRelease(bitmapContext);
122     ::DeleteDC(hdc);
123 }
124
125 void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point)
126 {
127     // FIXME: Creating CFData is non-optimal, but needed to avoid crashing when printing.  Ideally we should 
128     // make a custom CGDataProvider that controls the WindowsBitmap lifetime.  see <rdar://6394455>
129     RetainPtr<CFDataRef> imageData = adoptCF(CFDataCreate(kCFAllocatorDefault, image->buffer(), image->bufferLength()));
130     RetainPtr<CGDataProviderRef> dataProvider = adoptCF(CGDataProviderCreateWithCFData(imageData.get()));
131     RetainPtr<CGImageRef> cgImage = adoptCF(CGImageCreate(image->size().width(), image->size().height(), 8, 32, image->bytesPerRow(), deviceRGBColorSpaceRef(),
132                                                          kCGBitmapByteOrder32Little | kCGImageAlphaFirst, dataProvider.get(), 0, true, kCGRenderingIntentDefault));
133     CGContextDrawImage(m_data->m_cgContext.get(), CGRectMake(point.x(), point.y(), image->size().width(), image->size().height()), cgImage.get());   
134 }
135
136 void GraphicsContext::drawFocusRing(const Path& path, float width, float offset, const Color& color)
137 {
138     // FIXME: implement
139 }
140
141 // FIXME: This is nearly identical to the GraphicsContext::drawFocusRing function in GraphicsContextMac.mm.
142 // The code could move to GraphicsContextCG.cpp and be shared.
143 void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
144 {
145     if (paintingDisabled())
146         return;
147
148     float radius = (width - 1) / 2.0f;
149     offset += radius;
150     CGColorRef colorRef = color.isValid() ? cachedCGColor(color) : nullptr;
151
152     CGMutablePathRef focusRingPath = CGPathCreateMutable();
153     unsigned rectCount = rects.size();
154     for (unsigned i = 0; i < rectCount; i++)
155         CGPathAddRect(focusRingPath, 0, CGRectInset(rects[i], -offset, -offset));
156
157     CGContextRef context = platformContext();
158     CGContextSaveGState(context);
159
160     CGContextBeginPath(context);
161     CGContextAddPath(context, focusRingPath);
162
163     wkDrawFocusRing(context, colorRef, radius);
164
165     CGPathRelease(focusRingPath);
166
167     CGContextRestoreGState(context);
168 }
169
170 // Pulled from GraphicsContextCG
171 static void setCGStrokeColor(CGContextRef context, const Color& color)
172 {
173     CGFloat red, green, blue, alpha;
174     color.getRGBA(red, green, blue, alpha);
175     CGContextSetRGBStrokeColor(context, red, green, blue, alpha);
176 }
177
178 static const Color& spellingPatternColor() {
179     static const Color spellingColor(255, 0, 0);
180     return spellingColor;
181 }
182
183 static const Color& grammarPatternColor() {
184     static const Color grammarColor(0, 128, 0);
185     return grammarColor;
186 }
187
188 void GraphicsContext::updateDocumentMarkerResources()
189 {
190     // Unnecessary, since our document markers don't use resources.
191 }
192
193 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& point, float width, DocumentMarkerLineStyle style)
194 {
195     if (paintingDisabled())
196         return;
197
198     if (style != DocumentMarkerSpellingLineStyle && style != DocumentMarkerGrammarLineStyle)
199         return;
200
201     // These are the same for misspelling or bad grammar
202     const int patternHeight = 3; // 3 rows
203     ASSERT(cMisspellingLineThickness == patternHeight);
204     const int patternWidth = 4; // 4 pixels
205     ASSERT(patternWidth == cMisspellingLinePatternWidth);
206
207     // Make sure to draw only complete dots.
208     // NOTE: Code here used to shift the underline to the left and increase the width
209     // to make sure everything gets underlined, but that results in drawing out of
210     // bounds (e.g. when at the edge of a view) and could make it appear that the
211     // space between adjacent misspelled words was underlined.
212     // allow slightly more considering that the pattern ends with a transparent pixel
213     float widthMod = fmodf(width, patternWidth);
214     if (patternWidth - widthMod > cMisspellingLinePatternGapWidth)
215         width -= widthMod;
216       
217     // Draw the underline
218     CGContextRef context = platformContext();
219     CGContextSaveGState(context);
220
221     const Color& patternColor = style == DocumentMarkerGrammarLineStyle ? grammarPatternColor() : spellingPatternColor();
222     setCGStrokeColor(context, patternColor);
223
224     wkSetPatternPhaseInUserSpace(context, point);
225     CGContextSetBlendMode(context, kCGBlendModeNormal);
226     
227     // 3 rows, each offset by half a pixel for blending purposes
228     const CGPoint upperPoints [] = {{point.x(), point.y() + patternHeight - 2.5 }, {point.x() + width, point.y() + patternHeight - 2.5}};
229     const CGPoint middlePoints [] = {{point.x(), point.y() + patternHeight - 1.5 }, {point.x() + width, point.y() + patternHeight - 1.5}};
230     const CGPoint lowerPoints [] = {{point.x(), point.y() + patternHeight - 0.5 }, {point.x() + width, point.y() + patternHeight - 0.5 }};
231     
232     // Dash lengths for the top and bottom of the error underline are the same.
233     // These are magic.
234     static const CGFloat edge_dash_lengths[] = {2.0f, 2.0f};
235     static const CGFloat middle_dash_lengths[] = { 2.76f, 1.24f };
236     static const CGFloat edge_offset = -(edge_dash_lengths[1] - 1.0f) / 2.0f;
237     static const CGFloat middle_offset = -(middle_dash_lengths[1] - 1.0f) / 2.0f;
238
239     // Line opacities.  Once again, these are magic.
240     const float upperOpacity = 0.33f;
241     const float middleOpacity = 0.75f;
242     const float lowerOpacity = 0.88f;
243
244     //Top line
245     CGContextSetLineDash(context, edge_offset, edge_dash_lengths, WTF_ARRAY_LENGTH(edge_dash_lengths));
246     CGContextSetAlpha(context, upperOpacity);
247     CGContextStrokeLineSegments(context, upperPoints, 2);
248  
249     // Middle line
250     CGContextSetLineDash(context, middle_offset, middle_dash_lengths, WTF_ARRAY_LENGTH(middle_dash_lengths));
251     CGContextSetAlpha(context, middleOpacity);
252     CGContextStrokeLineSegments(context, middlePoints, 2);
253     
254     // Bottom line
255     CGContextSetLineDash(context, edge_offset, edge_dash_lengths, WTF_ARRAY_LENGTH(edge_dash_lengths));
256     CGContextSetAlpha(context, lowerOpacity);
257     CGContextStrokeLineSegments(context, lowerPoints, 2);
258
259     CGContextRestoreGState(context);
260 }
261
262 void GraphicsContextPlatformPrivate::flush()
263 {
264     CGContextFlush(m_cgContext.get());
265 }
266
267 }
268 #endif