1/* 2 * Copyright (C) 2008, 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 "GraphicsContext.h" 28 29#include "AffineTransform.h" 30#include "DIBPixelData.h" 31#include "Path.h" 32 33#include <cairo-win32.h> 34#include "GraphicsContextPlatformPrivateCairo.h" 35 36using namespace std; 37 38namespace WebCore { 39 40#if PLATFORM(WIN) 41static cairo_t* createCairoContextWithHDC(HDC hdc, bool hasAlpha) 42{ 43 // Put the HDC In advanced mode so it will honor affine transforms. 44 SetGraphicsMode(hdc, GM_ADVANCED); 45 46 cairo_surface_t* surface = 0; 47 48 HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); 49 50 BITMAP info; 51 if (!GetObject(bitmap, sizeof(info), &info)) 52 surface = cairo_win32_surface_create(hdc); 53 else { 54 ASSERT(info.bmBitsPixel == 32); 55 56 surface = cairo_image_surface_create_for_data((unsigned char*)info.bmBits, 57 CAIRO_FORMAT_ARGB32, 58 info.bmWidth, 59 info.bmHeight, 60 info.bmWidthBytes); 61 } 62 63 cairo_t* context = cairo_create(surface); 64 cairo_surface_destroy(surface); 65 66 return context; 67} 68 69GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha) 70 : m_updatingControlTints(false), 71 m_transparencyCount(0) 72{ 73 platformInit(dc, hasAlpha); 74} 75 76void GraphicsContext::platformInit(HDC dc, bool hasAlpha) 77{ 78 cairo_t* cr = 0; 79 if (dc) 80 cr = createCairoContextWithHDC(dc, hasAlpha); 81 else 82 setPaintingDisabled(true); 83 84 m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr)); 85 m_data->m_hdc = dc; 86 if (platformContext()->cr()) { 87 // Make sure the context starts in sync with our state. 88 setPlatformFillColor(fillColor(), fillColorSpace()); 89 setPlatformStrokeColor(strokeColor(), strokeColorSpace()); 90 } 91 if (cr) 92 cairo_destroy(cr); 93} 94#endif 95 96static void setRGBABitmapAlpha(unsigned char* bytes, size_t length, unsigned char level) 97{ 98 for (size_t i = 0; i < length; i += 4) 99 bytes[i + 3] = level; 100} 101 102static void drawBitmapToContext(GraphicsContextPlatformPrivate* context, cairo_t* cr, const DIBPixelData& pixelData, const IntSize& translate) 103{ 104 // Need to make a cairo_surface_t out of the bitmap's pixel buffer and then draw 105 // it into our context. 106 cairo_surface_t* surface = cairo_image_surface_create_for_data(pixelData.buffer(), 107 CAIRO_FORMAT_ARGB32, 108 pixelData.size().width(), 109 pixelData.size().height(), 110 pixelData.bytesPerRow()); 111 112 // Flip the target surface so that when we set the srcImage as 113 // the surface it will draw right-side-up. 114 cairo_save(cr); 115 cairo_translate(cr, static_cast<double>(translate.width()), static_cast<double>(translate.height())); 116 cairo_scale(cr, 1, -1); 117 cairo_set_source_surface(cr, surface, 0, 0); 118 119 if (context->layers.size()) 120 cairo_paint_with_alpha(cr, context->layers.last()); 121 else 122 cairo_paint(cr); 123 124 // Delete all our junk. 125 cairo_surface_destroy(surface); 126 cairo_restore(cr); 127} 128 129void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) 130{ 131 bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || isInTransparencyLayer()); 132 if (!hdc || !createdBitmap) { 133 m_data->restore(); 134 return; 135 } 136 137 if (dstRect.isEmpty()) 138 return; 139 140 auto bitmap = adoptGDIObject(static_cast<HBITMAP>(::GetCurrentObject(hdc, OBJ_BITMAP))); 141 142 DIBPixelData pixelData(bitmap.get()); 143 ASSERT(pixelData.bitsPerPixel() == 32); 144 145 // If this context does not support alpha blending, then it may have 146 // been drawn with GDI functions which always set the alpha channel 147 // to zero. We need to manually set the bitmap to be fully opaque. 148 unsigned char* bytes = reinterpret_cast<unsigned char*>(pixelData.buffer()); 149 if (!supportAlphaBlend) 150 setRGBABitmapAlpha(bytes, pixelData.size().height() * pixelData.bytesPerRow(), 255); 151 152 drawBitmapToContext(m_data, platformContext()->cr(), pixelData, IntSize(dstRect.x(), dstRect.height() + dstRect.y())); 153 154 ::DeleteDC(hdc); 155} 156 157#if PLATFORM(WIN) 158void GraphicsContext::drawWindowsBitmap(WindowsBitmap* bitmap, const IntPoint& point) 159{ 160 drawBitmapToContext(m_data, platformContext()->cr(), bitmap->windowsDIB(), IntSize(point.x(), bitmap->size().height() + point.y())); 161} 162 163void GraphicsContextPlatformPrivate::syncContext(cairo_t* cr) 164{ 165 if (!cr) 166 return; 167 168 cairo_surface_t* surface = cairo_get_target(cr); 169 m_hdc = cairo_win32_surface_get_dc(surface); 170 171 SetGraphicsMode(m_hdc, GM_ADVANCED); // We need this call for themes to honor world transforms. 172} 173 174void GraphicsContextPlatformPrivate::flush() 175{ 176 cairo_surface_t* surface = cairo_win32_surface_create(m_hdc); 177 cairo_surface_flush(surface); 178 cairo_surface_destroy(surface); 179} 180#endif 181 182} 183