2
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
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.
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.
27
#include "GraphicsContext.h"
29
#include "AffineTransform.h"
30
#include "NotImplemented.h"
33
#include <CoreGraphics/CGBitmapContext.h>
34
#include <WebKitSystemInterface/WebKitSystemInterface.h>
35
#include "GraphicsContextPlatformPrivateCG.h"
41
static CGContextRef CGContextWithHDC(HDC hdc)
43
HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
44
CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB();
47
GetObject(bitmap, sizeof(info), &info);
48
ASSERT(info.bmBitsPixel == 32);
49
CGContextRef context = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8,
50
info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst);
51
CGColorSpaceRelease(deviceRGB);
54
CGContextTranslateCTM(context, 0, info.bmHeight);
55
CGContextScaleCTM(context, 1, -1);
57
// Put the HDC In advanced mode so it will honor affine transforms.
58
SetGraphicsMode(hdc, GM_ADVANCED);
63
GraphicsContext::GraphicsContext(HDC hdc)
64
: m_common(createGraphicsContextPrivate())
65
, m_data(new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc)))
67
CGContextRelease(m_data->m_cgContext);
69
setPaintingDisabled(!m_data->m_cgContext);
70
if (m_data->m_cgContext) {
71
// Make sure the context starts in sync with our state.
72
setPlatformFillColor(fillColor());
73
setPlatformStrokeColor(strokeColor());
77
bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; }
79
// FIXME: Is it possible to merge getWindowsContext and createWindowsBitmap into a single API
80
// suitable for all clients?
81
HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
83
// FIXME: Should a bitmap be created also when a shadow is set?
84
if (mayCreateBitmap && inTransparencyLayer()) {
85
if (dstRect.isEmpty())
88
// Create a bitmap DC in which to draw.
89
BITMAPINFO bitmapInfo;
90
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
91
bitmapInfo.bmiHeader.biWidth = dstRect.width();
92
bitmapInfo.bmiHeader.biHeight = dstRect.height();
93
bitmapInfo.bmiHeader.biPlanes = 1;
94
bitmapInfo.bmiHeader.biBitCount = 32;
95
bitmapInfo.bmiHeader.biCompression = BI_RGB;
96
bitmapInfo.bmiHeader.biSizeImage = 0;
97
bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
98
bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
99
bitmapInfo.bmiHeader.biClrUsed = 0;
100
bitmapInfo.bmiHeader.biClrImportant = 0;
103
HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
107
HDC bitmapDC = ::CreateCompatibleDC(m_data->m_hdc);
108
::SelectObject(bitmapDC, bitmap);
110
// Fill our buffer with clear if we're going to alpha blend.
111
if (supportAlphaBlend) {
113
GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
114
int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
115
memset(bmpInfo.bmBits, 0, bufferSize);
118
// Make sure we can do world transforms.
119
SetGraphicsMode(bitmapDC, GM_ADVANCED);
121
// Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
127
xform.eDx = -dstRect.x();
128
xform.eDy = -dstRect.y();
129
::SetWorldTransform(bitmapDC, &xform);
134
CGContextFlush(platformContext());
136
return m_data->m_hdc;
139
void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
141
if (mayCreateBitmap && hdc && inTransparencyLayer()) {
142
if (dstRect.isEmpty())
145
HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
147
// Need to make a CGImage out of the bitmap's pixel buffer and then draw
148
// it into our context.
150
GetObject(bitmap, sizeof(info), &info);
151
ASSERT(info.bmBitsPixel == 32);
153
CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB();
154
CGContextRef bitmapContext = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8,
155
info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little |
156
(supportAlphaBlend ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst));
157
CGColorSpaceRelease(deviceRGB);
159
CGImageRef image = CGBitmapContextCreateImage(bitmapContext);
160
CGContextDrawImage(m_data->m_cgContext, dstRect, image);
162
// Delete all our junk.
163
CGImageRelease(image);
164
CGContextRelease(bitmapContext);
166
::DeleteObject(bitmap);
174
GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size)
178
BITMAPINFO bitmapInfo;
179
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
180
bitmapInfo.bmiHeader.biWidth = m_size.width();
181
bitmapInfo.bmiHeader.biHeight = m_size.height();
182
bitmapInfo.bmiHeader.biPlanes = 1;
183
bitmapInfo.bmiHeader.biBitCount = 32;
184
bitmapInfo.bmiHeader.biCompression = BI_RGB;
185
bitmapInfo.bmiHeader.biSizeImage = 0;
186
bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
187
bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
188
bitmapInfo.bmiHeader.biClrUsed = 0;
189
bitmapInfo.bmiHeader.biClrImportant = 0;
191
m_bitmap = CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&m_bitmapBuffer), 0, 0);
195
m_hdc = CreateCompatibleDC(hdc);
196
SelectObject(m_hdc, m_bitmap);
199
GetObject(m_bitmap, sizeof(bmpInfo), &bmpInfo);
200
m_bytesPerRow = bmpInfo.bmWidthBytes;
201
m_bitmapBufferLength = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
203
SetGraphicsMode(m_hdc, GM_ADVANCED);
206
GraphicsContext::WindowsBitmap::~WindowsBitmap()
212
DeleteObject(m_bitmap);
215
GraphicsContext::WindowsBitmap* GraphicsContext::createWindowsBitmap(IntSize size)
217
return new WindowsBitmap(m_data->m_hdc, size);
220
void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point)
222
RetainPtr<CGColorSpaceRef> deviceRGB(AdoptCF, CGColorSpaceCreateDeviceRGB());
223
RetainPtr<CFDataRef> imageData(AdoptCF, CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, image->buffer(), image->bufferLength(), kCFAllocatorNull));
224
RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(imageData.get()));
225
RetainPtr<CGImageRef> cgImage(AdoptCF, CGImageCreate(image->size().width(), image->size().height(), 8, 32, image->bytesPerRow(), deviceRGB.get(),
226
kCGBitmapByteOrder32Little | kCGImageAlphaFirst, dataProvider.get(), 0, true, kCGRenderingIntentDefault));
227
CGContextDrawImage(m_data->m_cgContext, CGRectMake(point.x(), point.y(), image->size().width(), image->size().height()), cgImage.get());
230
void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform)
235
CGAffineTransform mat = transform;
244
ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY);
247
void GraphicsContext::drawFocusRing(const Color& color)
249
if (paintingDisabled())
252
float radius = (focusRingWidth() - 1) / 2.0f;
253
int offset = radius + focusRingOffset();
254
CGColorRef colorRef = color.isValid() ? cgColor(color) : 0;
256
CGMutablePathRef focusRingPath = CGPathCreateMutable();
257
const Vector<IntRect>& rects = focusRingRects();
258
unsigned rectCount = rects.size();
259
for (unsigned i = 0; i < rectCount; i++)
260
CGPathAddRect(focusRingPath, 0, CGRectInset(rects[i], -offset, -offset));
262
CGContextRef context = platformContext();
263
CGContextSaveGState(context);
265
CGContextBeginPath(context);
266
CGContextAddPath(context, focusRingPath);
268
wkDrawFocusRing(context, colorRef, radius);
270
CGColorRelease(colorRef);
272
CGPathRelease(focusRingPath);
274
CGContextRestoreGState(context);
277
// Pulled from GraphicsContextCG
278
static void setCGStrokeColor(CGContextRef context, const Color& color)
280
CGFloat red, green, blue, alpha;
281
color.getRGBA(red, green, blue, alpha);
282
CGContextSetRGBStrokeColor(context, red, green, blue, alpha);
285
static const Color& spellingPatternColor() {
286
static const Color spellingColor(255, 0, 0);
287
return spellingColor;
290
static const Color& grammarPatternColor() {
291
static const Color grammarColor(0, 128, 0);
295
void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& point, int width, bool grammar)
297
if (paintingDisabled())
300
// These are the same for misspelling or bad grammar
301
const int patternHeight = 3; // 3 rows
302
ASSERT(cMisspellingLineThickness == patternHeight);
303
const int patternWidth = 4; // 4 pixels
304
ASSERT(patternWidth == cMisspellingLinePatternWidth);
306
// Make sure to draw only complete dots.
307
// NOTE: Code here used to shift the underline to the left and increase the width
308
// to make sure everything gets underlined, but that results in drawing out of
309
// bounds (e.g. when at the edge of a view) and could make it appear that the
310
// space between adjacent misspelled words was underlined.
311
// allow slightly more considering that the pattern ends with a transparent pixel
312
int widthMod = width % patternWidth;
313
if (patternWidth - widthMod > cMisspellingLinePatternGapWidth)
316
// Draw the underline
317
CGContextRef context = platformContext();
318
CGContextSaveGState(context);
320
const Color& patternColor = grammar ? grammarPatternColor() : spellingPatternColor();
321
setCGStrokeColor(context, patternColor);
323
wkSetPatternPhaseInUserSpace(context, point);
324
CGContextSetBlendMode(context, kCGBlendModeNormal);
326
// 3 rows, each offset by half a pixel for blending purposes
327
const CGPoint upperPoints [] = {{point.x(), point.y() + patternHeight - 2.5 }, {point.x() + width, point.y() + patternHeight - 2.5}};
328
const CGPoint middlePoints [] = {{point.x(), point.y() + patternHeight - 1.5 }, {point.x() + width, point.y() + patternHeight - 1.5}};
329
const CGPoint lowerPoints [] = {{point.x(), point.y() + patternHeight - 0.5 }, {point.x() + width, point.y() + patternHeight - 0.5 }};
331
// Dash lengths for the top and bottom of the error underline are the same.
333
static const float edge_dash_lengths[] = {2.0f, 2.0f};
334
static const float middle_dash_lengths[] = {2.76f, 1.24f};
335
static const float edge_offset = -(edge_dash_lengths[1] - 1.0f) / 2.0f;
336
static const float middle_offset = -(middle_dash_lengths[1] - 1.0f) / 2.0f;
338
// Line opacities. Once again, these are magic.
339
const float upperOpacity = 0.33f;
340
const float middleOpacity = 0.75f;
341
const float lowerOpacity = 0.88f;
344
CGContextSetLineDash(context, edge_offset, edge_dash_lengths,
345
sizeof(edge_dash_lengths) / sizeof(edge_dash_lengths[0]));
346
CGContextSetAlpha(context, upperOpacity);
347
CGContextStrokeLineSegments(context, upperPoints, 2);
350
CGContextSetLineDash(context, middle_offset, middle_dash_lengths,
351
sizeof(middle_dash_lengths) / sizeof(middle_dash_lengths[0]));
352
CGContextSetAlpha(context, middleOpacity);
353
CGContextStrokeLineSegments(context, middlePoints, 2);
356
CGContextSetLineDash(context, edge_offset, edge_dash_lengths,
357
sizeof(edge_dash_lengths) / sizeof(edge_dash_lengths[0]));
358
CGContextSetAlpha(context, lowerOpacity);
359
CGContextStrokeLineSegments(context, lowerPoints, 2);
361
CGContextRestoreGState(context);