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