Home | History | Annotate | Download | only in win
      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