REGRESSION(r227594) [WinCairo] NULL pointer crash in GraphicsContext::getWindowsContext
[WebKit-https.git] / Source / WebCore / platform / graphics / win / GraphicsContextCairoWin.cpp
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 "GraphicsContextImpl.h"
32 #include "Path.h"
33
34 #include <cairo-win32.h>
35 #include "GraphicsContextPlatformPrivateCairo.h"
36
37
38 namespace WebCore {
39 using namespace std;
40
41 #if PLATFORM(WIN)
42 static cairo_t* createCairoContextWithHDC(HDC hdc, bool hasAlpha)
43 {
44     // Put the HDC In advanced mode so it will honor affine transforms.
45     SetGraphicsMode(hdc, GM_ADVANCED);
46
47     cairo_surface_t* surface = 0;
48
49     HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
50
51     BITMAP info;
52     if (!GetObject(bitmap, sizeof(info), &info))
53         surface = cairo_win32_surface_create(hdc);
54     else {
55         ASSERT(info.bmBitsPixel == 32);
56
57         surface = cairo_image_surface_create_for_data((unsigned char*)info.bmBits,
58                                                CAIRO_FORMAT_ARGB32,
59                                                info.bmWidth,
60                                                info.bmHeight,
61                                                info.bmWidthBytes);
62     }
63
64     cairo_t* context = cairo_create(surface);
65     cairo_surface_destroy(surface);
66
67     return context;
68 }
69
70 GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha)
71 {
72     platformInit(dc, hasAlpha);
73 }
74
75 void GraphicsContext::platformInit(HDC dc, bool hasAlpha)
76 {
77     if (!dc)
78         return;
79
80     cairo_t* cr = createCairoContextWithHDC(dc, hasAlpha);
81
82     m_data = new GraphicsContextPlatformPrivate(std::make_unique<PlatformContextCairo>(cr));
83     m_data->platformContext.setGraphicsContextPrivate(m_data);
84     m_data->m_hdc = dc;
85     if (platformContext()->cr()) {
86         // Make sure the context starts in sync with our state.
87         setPlatformFillColor(fillColor());
88         setPlatformStrokeColor(strokeColor());
89     }
90     if (cr)
91         cairo_destroy(cr);
92 }
93 #endif
94
95 static void setRGBABitmapAlpha(unsigned char* bytes, size_t length, unsigned char level)
96 {
97     for (size_t i = 0; i < length; i += 4)
98         bytes[i + 3] = level;
99 }
100
101 static void drawBitmapToContext(PlatformContextCairo& platformContext, const DIBPixelData& pixelData, const IntSize& translate)
102 {
103     // Need to make a cairo_surface_t out of the bitmap's pixel buffer and then draw
104     // it into our context.
105     cairo_surface_t* surface = cairo_image_surface_create_for_data(pixelData.buffer(),
106                                                                    CAIRO_FORMAT_ARGB32,
107                                                                    pixelData.size().width(),
108                                                                    pixelData.size().height(),
109                                                                    pixelData.bytesPerRow());
110
111     // Flip the target surface so that when we set the srcImage as
112     // the surface it will draw right-side-up.
113     cairo_t* cr = platformContext.cr();
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 (platformContext.layers().size())
120         cairo_paint_with_alpha(cr, platformContext.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
129 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend)
130 {
131     bool createdBitmap = m_impl || !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(*platformContext(), pixelData, IntSize(dstRect.x(), dstRect.height() + dstRect.y()));
153
154     ::DeleteDC(hdc);
155 }
156
157 #if PLATFORM(WIN)
158 void GraphicsContext::drawWindowsBitmap(WindowsBitmap* bitmap, const IntPoint& point)
159 {
160     drawBitmapToContext(*platformContext(), bitmap->windowsDIB(), IntSize(point.x(), bitmap->size().height() + point.y()));
161 }
162
163 void 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
174 void 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 }