/* * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "GraphicsContext.h" #include "AffineTransform.h" #include "NotImplemented.h" #include "Path.h" #include #include #include "GraphicsContextPlatformPrivate.h" #include "WebCoreSystemInterface.h" using namespace std; namespace WebCore { class SVGResourceImage; static CGContextRef CGContextWithHDC(HDC hdc) { HBITMAP bitmap = (HBITMAP)GetCurrentObject(hdc, OBJ_BITMAP); CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); BITMAP info; GetObject(bitmap, sizeof(info), &info); ASSERT(info.bmBitsPixel == 32); CGContextRef context = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst); CGColorSpaceRelease(deviceRGB); // Flip coords CGContextTranslateCTM(context, 0, info.bmHeight); CGContextScaleCTM(context, 1, -1); // Put the HDC In advanced mode so it will honor affine transforms. SetGraphicsMode(hdc, GM_ADVANCED); return context; } GraphicsContext::GraphicsContext(HDC hdc) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc))) { CGContextRelease(m_data->m_cgContext); m_data->m_hdc = hdc; setPaintingDisabled(!m_data->m_cgContext); if (m_data->m_cgContext) { // Make sure the context starts in sync with our state. setPlatformFillColor(fillColor()); setPlatformStrokeColor(strokeColor()); } } HDC GraphicsContext::getWindowsContext(bool supportAlphaBlend, const IntRect* dstRect) { if (m_data->m_transparencyCount) { // We're in a transparency layer. ASSERT(dstRect); if (!dstRect) return 0; // Create a bitmap DC in which to draw. BITMAPINFO bitmapInfo; bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo.bmiHeader.biWidth = dstRect->width(); bitmapInfo.bmiHeader.biHeight = dstRect->height(); bitmapInfo.bmiHeader.biPlanes = 1; bitmapInfo.bmiHeader.biBitCount = 32; bitmapInfo.bmiHeader.biCompression = BI_RGB; bitmapInfo.bmiHeader.biSizeImage = 0; bitmapInfo.bmiHeader.biXPelsPerMeter = 0; bitmapInfo.bmiHeader.biYPelsPerMeter = 0; bitmapInfo.bmiHeader.biClrUsed = 0; bitmapInfo.bmiHeader.biClrImportant = 0; void* pixels = 0; HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); if (!bitmap) return 0; HDC bitmapDC = ::CreateCompatibleDC(m_data->m_hdc); ::SelectObject(bitmapDC, bitmap); // Fill our buffer with clear if we're going to alpha blend. if (supportAlphaBlend) { BITMAP bmpInfo; GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; memset(bmpInfo.bmBits, 0, bufferSize); } // Make sure we can do world transforms. SetGraphicsMode(bitmapDC, GM_ADVANCED); // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap. XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = 0; xform.eM22 = 1.0; xform.eDx = -dstRect->x(); xform.eDy = -dstRect->y(); ::SetWorldTransform(bitmapDC, &xform); return bitmapDC; } CGContextFlush(platformContext()); m_data->save(); return m_data->m_hdc; } void GraphicsContext::releaseWindowsContext(HDC hdc, bool supportAlphaBlend, const IntRect* dstRect) { if (hdc && m_data->m_transparencyCount) { HBITMAP bitmap = (HBITMAP)GetCurrentObject(hdc, OBJ_BITMAP); // Need to make a CGImage out of the bitmap's pixel buffer and then draw // it into our context. BITMAP info; GetObject(bitmap, sizeof(info), &info); ASSERT(info.bmBitsPixel == 32); CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); CGContextRef bitmapContext = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | (supportAlphaBlend ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst)); CGColorSpaceRelease(deviceRGB); CGImageRef image = CGBitmapContextCreateImage(bitmapContext); CGContextDrawImage(m_data->m_cgContext, *dstRect, image); // Delete all our junk. CGImageRelease(image); CGContextRelease(bitmapContext); ::DeleteDC(hdc); ::DeleteObject(bitmap); return; } m_data->restore(); } void GraphicsContextPlatformPrivate::save() { if (!m_hdc) return; SaveDC(m_hdc); } void GraphicsContextPlatformPrivate::restore() { if (!m_hdc) return; RestoreDC(m_hdc, -1); } void GraphicsContextPlatformPrivate::clip(const IntRect& clipRect) { if (!m_hdc) return; IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); } void GraphicsContextPlatformPrivate::scale(const FloatSize& size) { if (!m_hdc) return; XFORM xform; xform.eM11 = size.width(); xform.eM12 = 0; xform.eM21 = 0; xform.eM22 = size.height(); xform.eDx = 0; xform.eDy = 0; ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); } static const double deg2rad = 0.017453292519943295769; // pi/180 void GraphicsContextPlatformPrivate::rotate(float degreesAngle) { float radiansAngle = degreesAngle * deg2rad; float cosAngle = cosf(radiansAngle); float sinAngle = sinf(radiansAngle); XFORM xform; xform.eM11 = cosAngle; xform.eM12 = -sinAngle; xform.eM21 = sinAngle; xform.eM22 = cosAngle; xform.eDx = 0; xform.eDy = 0; ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); } void GraphicsContextPlatformPrivate::translate(float x , float y) { if (!m_hdc) return; XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = 0; xform.eM22 = 1.0; xform.eDx = x; xform.eDy = y; ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); } void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) { if (!m_hdc) return; CGAffineTransform mat = transform; XFORM xform; xform.eM11 = mat.a; xform.eM12 = mat.b; xform.eM21 = mat.c; xform.eM22 = mat.d; xform.eDx = mat.tx; xform.eDy = mat.ty; ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); } void GraphicsContext::setCompositeOperation(CompositeOperator mode) { if (paintingDisabled()) return; CGBlendMode target = kCGBlendModeNormal; switch (mode) { case CompositeClear: target = kCGBlendModeClear; break; case CompositeCopy: target = kCGBlendModeCopy; break; case CompositeSourceOver: //kCGBlendModeNormal break; case CompositeSourceIn: target = kCGBlendModeSourceIn; break; case CompositeSourceOut: target = kCGBlendModeSourceOut; break; case CompositeSourceAtop: target = kCGBlendModeSourceAtop; break; case CompositeDestinationOver: target = kCGBlendModeDestinationOver; break; case CompositeDestinationIn: target = kCGBlendModeDestinationIn; break; case CompositeDestinationOut: target = kCGBlendModeDestinationOut; break; case CompositeDestinationAtop: target = kCGBlendModeDestinationAtop; break; case CompositeXOR: target = kCGBlendModeXOR; break; case CompositePlusDarker: target = kCGBlendModePlusDarker; break; case CompositeHighlight: // currently unsupported break; case CompositePlusLighter: target = kCGBlendModePlusLighter; break; } CGContextSetBlendMode(platformContext(), target); } void GraphicsContext::drawFocusRing(const Color& color) { if (paintingDisabled()) return; float radius = (focusRingWidth() - 1) / 2.0f; int offset = radius + focusRingOffset(); CGColorRef colorRef = color.isValid() ? cgColor(color) : 0; CGMutablePathRef focusRingPath = CGPathCreateMutable(); const Vector& rects = focusRingRects(); unsigned rectCount = rects.size(); for (unsigned i = 0; i < rectCount; i++) CGPathAddRect(focusRingPath, 0, CGRectInset(rects[i], -offset, -offset)); CGContextRef context = platformContext(); CGContextSaveGState(context); CGContextBeginPath(context); CGContextAddPath(context, focusRingPath); wkDrawFocusRing(context, colorRef, radius); CGColorRelease(colorRef); CGPathRelease(focusRingPath); CGContextRestoreGState(context); } static const Color& spellingPatternColor() { static const Color spellingColor(255, 0, 0); return spellingColor; } static const Color& grammarPatternColor() { static const Color grammarColor(0, 128, 0); return grammarColor; } // Pulled from GraphicsContextCG static void setCGStrokeColor(CGContextRef context, const Color& color) { CGFloat red, green, blue, alpha; color.getRGBA(red, green, blue, alpha); CGContextSetRGBStrokeColor(context, red, green, blue, alpha); } void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& point, int width, bool grammar) { if (paintingDisabled()) return; // These are the same for misspelling or bad grammar const int patternHeight = 3; // 3 rows ASSERT(cMisspellingLineThickness == patternHeight); const int patternWidth = 4; // 4 pixels ASSERT(patternWidth == cMisspellingLinePatternWidth); // Make sure to draw only complete dots. // NOTE: Code here used to shift the underline to the left and increase the width // to make sure everything gets underlined, but that results in drawing out of // bounds (e.g. when at the edge of a view) and could make it appear that the // space between adjacent misspelled words was underlined. // allow slightly more considering that the pattern ends with a transparent pixel int widthMod = width % patternWidth; if (patternWidth - widthMod > cMisspellingLinePatternGapWidth) width -= widthMod; // Draw the underline CGContextRef context = platformContext(); CGContextSaveGState(context); const Color& patternColor = grammar ? grammarPatternColor() : spellingPatternColor(); setCGStrokeColor(context, patternColor); wkSetPatternPhaseInUserSpace(context, point); CGContextSetBlendMode(context, kCGBlendModeNormal); // 3 rows, each offset by half a pixel for blending purposes const CGPoint upperPoints [] = {{point.x(), point.y() + patternHeight - 2.5 }, {point.x() + width, point.y() + patternHeight - 2.5}}; const CGPoint middlePoints [] = {{point.x(), point.y() + patternHeight - 1.5 }, {point.x() + width, point.y() + patternHeight - 1.5}}; const CGPoint lowerPoints [] = {{point.x(), point.y() + patternHeight - 0.5 }, {point.x() + width, point.y() + patternHeight - 0.5 }}; // Dash lengths for the top and bottom of the error underline are the same. // These are magic. static const float edge_dash_lengths[] = {2, 2}; static const float middle_dash_lengths[] = {2.76f, 1.24f}; static const float edge_offset = -(edge_dash_lengths[1] - 1.0f) / 2.0f; static const float middle_offset = -(middle_dash_lengths[1] - 1.0f) / 2.0f; // Line opacities. Once again, these are magic. const float upperOpacity = 0.33f; const float middleOpacity = 0.75f; const float lowerOpacity = 0.88f; //Top line CGContextSetLineDash(context, edge_offset, edge_dash_lengths, sizeof(edge_dash_lengths) / sizeof(edge_dash_lengths[0])); CGContextSetAlpha(context, upperOpacity); CGContextStrokeLineSegments(context, upperPoints, 2); // Middle line CGContextSetLineDash(context, middle_offset, middle_dash_lengths, sizeof(middle_dash_lengths) / sizeof(middle_dash_lengths[0])); CGContextSetAlpha(context, middleOpacity); CGContextStrokeLineSegments(context, middlePoints, 2); // Bottom line CGContextSetLineDash(context, edge_offset, edge_dash_lengths, sizeof(edge_dash_lengths) / sizeof(edge_dash_lengths[0])); CGContextSetAlpha(context, lowerOpacity); CGContextStrokeLineSegments(context, lowerPoints, 2); CGContextRestoreGState(context); } #if ENABLE(SVG) GraphicsContext* contextForImage(SVGResourceImage*) { // FIXME: This should go in GraphicsContextCG.cpp notImplemented(); return 0; } #endif }