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