diff options
Diffstat (limited to 'WebCore/platform/graphics/cg/GraphicsContextCG.cpp')
-rw-r--r-- | WebCore/platform/graphics/cg/GraphicsContextCG.cpp | 1253 |
1 files changed, 0 insertions, 1253 deletions
diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp deleted file mode 100644 index 7898d62..0000000 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ /dev/null @@ -1,1253 +0,0 @@ -/* - * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2008 Eric Seidel <eric@webkit.org> - * - * 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. - */ - -#define _USE_MATH_DEFINES 1 -#include "config.h" -#include "GraphicsContextCG.h" - -#include "AffineTransform.h" -#include "FloatConversion.h" -#include "GraphicsContextPlatformPrivateCG.h" -#include "ImageBuffer.h" -#include "KURL.h" -#include "Path.h" -#include "Pattern.h" - -#include <CoreGraphics/CGBitmapContext.h> -#include <CoreGraphics/CGPDFContext.h> -#include <wtf/MathExtras.h> -#include <wtf/OwnArrayPtr.h> -#include <wtf/RetainPtr.h> -#include <wtf/UnusedParam.h> - -#if PLATFORM(MAC) || PLATFORM(CHROMIUM) -#include "WebCoreSystemInterface.h" -#endif - -#if PLATFORM(WIN) -#include <WebKitSystemInterface/WebKitSystemInterface.h> -#endif - -#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) - -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) -// Building on 10.6 or later: kCGInterpolationMedium is defined in the CGInterpolationQuality enum. -#define HAVE_CG_INTERPOLATION_MEDIUM 1 -#endif - -#if !defined(TARGETING_TIGER) && !defined(TARGETING_LEOPARD) -// Targeting 10.6 or later: use kCGInterpolationMedium. -#define WTF_USE_CG_INTERPOLATION_MEDIUM 1 -#endif - -#endif - -using namespace std; - -namespace WebCore { - -static void setCGFillColor(CGContextRef context, const Color& color, ColorSpace colorSpace) -{ - CGContextSetFillColorWithColor(context, cachedCGColor(color, colorSpace)); -} - -static void setCGStrokeColor(CGContextRef context, const Color& color, ColorSpace colorSpace) -{ - CGContextSetStrokeColorWithColor(context, cachedCGColor(color, colorSpace)); -} - -CGColorSpaceRef deviceRGBColorSpaceRef() -{ - static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB(); - return deviceSpace; -} - -CGColorSpaceRef sRGBColorSpaceRef() -{ - // FIXME: Windows should be able to use kCGColorSpaceSRGB, this is tracked by http://webkit.org/b/31363. -#if PLATFORM(WIN) || defined(BUILDING_ON_TIGER) - return deviceRGBColorSpaceRef(); -#else - static CGColorSpaceRef sRGBSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - return sRGBSpace; -#endif -} - -CGColorSpaceRef linearRGBColorSpaceRef() -{ - // FIXME: Windows should be able to use kCGColorSpaceGenericRGBLinear, this is tracked by http://webkit.org/b/31363. -#if PLATFORM(WIN) || defined(BUILDING_ON_TIGER) - return deviceRGBColorSpaceRef(); -#else - static CGColorSpaceRef linearRGBSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear); - return linearRGBSpace; -#endif -} - -void GraphicsContext::platformInit(CGContextRef cgContext) -{ - m_data = new GraphicsContextPlatformPrivate(cgContext); - setPaintingDisabled(!cgContext); - if (cgContext) { - // Make sure the context starts in sync with our state. - setPlatformFillColor(fillColor(), fillColorSpace()); - setPlatformStrokeColor(strokeColor(), strokeColorSpace()); - } -} - -void GraphicsContext::platformDestroy() -{ - delete m_data; -} - -CGContextRef GraphicsContext::platformContext() const -{ - ASSERT(!paintingDisabled()); - ASSERT(m_data->m_cgContext); - return m_data->m_cgContext.get(); -} - -void GraphicsContext::savePlatformState() -{ - // Note: Do not use this function within this class implementation, since we want to avoid the extra - // save of the secondary context (in GraphicsContextPlatformPrivateCG.h). - CGContextSaveGState(platformContext()); - m_data->save(); -} - -void GraphicsContext::restorePlatformState() -{ - // Note: Do not use this function within this class implementation, since we want to avoid the extra - // restore of the secondary context (in GraphicsContextPlatformPrivateCG.h). - CGContextRestoreGState(platformContext()); - m_data->restore(); - m_data->m_userToDeviceTransformKnownToBeIdentity = false; -} - -// Draws a filled rectangle with a stroked border. -void GraphicsContext::drawRect(const IntRect& rect) -{ - // FIXME: this function does not handle patterns and gradients - // like drawPath does, it probably should. - if (paintingDisabled()) - return; - - CGContextRef context = platformContext(); - - CGContextFillRect(context, rect); - - if (strokeStyle() != NoStroke) { - // We do a fill of four rects to simulate the stroke of a border. - Color oldFillColor = fillColor(); - if (oldFillColor != strokeColor()) - setCGFillColor(context, strokeColor(), strokeColorSpace()); - CGRect rects[4] = { - FloatRect(rect.x(), rect.y(), rect.width(), 1), - FloatRect(rect.x(), rect.bottom() - 1, rect.width(), 1), - FloatRect(rect.x(), rect.y() + 1, 1, rect.height() - 2), - FloatRect(rect.right() - 1, rect.y() + 1, 1, rect.height() - 2) - }; - CGContextFillRects(context, rects, 4); - if (oldFillColor != strokeColor()) - setCGFillColor(context, oldFillColor, fillColorSpace()); - } -} - -// This is only used to draw borders. -void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) -{ - if (paintingDisabled()) - return; - - if (strokeStyle() == NoStroke) - return; - - float width = strokeThickness(); - - FloatPoint p1 = point1; - FloatPoint p2 = point2; - bool isVerticalLine = (p1.x() == p2.x()); - - // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic - // works out. For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g., - // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave - // us a perfect position, but an odd width gave us a position that is off by exactly 0.5. - if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { - if (isVerticalLine) { - p1.move(0, width); - p2.move(0, -width); - } else { - p1.move(width, 0); - p2.move(-width, 0); - } - } - - if (((int)width) % 2) { - if (isVerticalLine) { - // We're a vertical line. Adjust our x. - p1.move(0.5f, 0.0f); - p2.move(0.5f, 0.0f); - } else { - // We're a horizontal line. Adjust our y. - p1.move(0.0f, 0.5f); - p2.move(0.0f, 0.5f); - } - } - - int patWidth = 0; - switch (strokeStyle()) { - case NoStroke: - case SolidStroke: - break; - case DottedStroke: - patWidth = (int)width; - break; - case DashedStroke: - patWidth = 3 * (int)width; - break; - } - - CGContextRef context = platformContext(); - - if (shouldAntialias()) - CGContextSetShouldAntialias(context, false); - - if (patWidth) { - CGContextSaveGState(context); - - // Do a rect fill of our endpoints. This ensures we always have the - // appearance of being a border. We then draw the actual dotted/dashed line. - setCGFillColor(context, strokeColor(), strokeColorSpace()); // The save/restore make it safe to mutate the fill color here without setting it back to the old color. - if (isVerticalLine) { - CGContextFillRect(context, FloatRect(p1.x() - width / 2, p1.y() - width, width, width)); - CGContextFillRect(context, FloatRect(p2.x() - width / 2, p2.y(), width, width)); - } else { - CGContextFillRect(context, FloatRect(p1.x() - width, p1.y() - width / 2, width, width)); - CGContextFillRect(context, FloatRect(p2.x(), p2.y() - width / 2, width, width)); - } - - // Example: 80 pixels with a width of 30 pixels. - // Remainder is 20. The maximum pixels of line we could paint - // will be 50 pixels. - int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width; - int remainder = distance % patWidth; - int coverage = distance - remainder; - int numSegments = coverage / patWidth; - - float patternOffset = 0.0f; - // Special case 1px dotted borders for speed. - if (patWidth == 1) - patternOffset = 1.0f; - else { - bool evenNumberOfSegments = !(numSegments % 2); - if (remainder) - evenNumberOfSegments = !evenNumberOfSegments; - if (evenNumberOfSegments) { - if (remainder) { - patternOffset += patWidth - remainder; - patternOffset += remainder / 2; - } else - patternOffset = patWidth / 2; - } else { - if (remainder) - patternOffset = (patWidth - remainder)/2; - } - } - - const CGFloat dottedLine[2] = { patWidth, patWidth }; - CGContextSetLineDash(context, patternOffset, dottedLine, 2); - } - - CGContextBeginPath(context); - CGContextMoveToPoint(context, p1.x(), p1.y()); - CGContextAddLineToPoint(context, p2.x(), p2.y()); - - CGContextStrokePath(context); - - if (patWidth) - CGContextRestoreGState(context); - - if (shouldAntialias()) - CGContextSetShouldAntialias(context, true); -} - -// This method is only used to draw the little circles used in lists. -void GraphicsContext::drawEllipse(const IntRect& rect) -{ - if (paintingDisabled()) - return; - - Path path; - path.addEllipse(rect); - drawPath(path); -} - - -void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) -{ - if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f) - return; - - CGContextRef context = platformContext(); - CGContextSaveGState(context); - CGContextBeginPath(context); - CGContextSetShouldAntialias(context, false); - - int x = rect.x(); - int y = rect.y(); - float w = (float)rect.width(); - float h = (float)rect.height(); - float scaleFactor = h / w; - float reverseScaleFactor = w / h; - - if (w != h) - scale(FloatSize(1, scaleFactor)); - - float hRadius = w / 2; - float vRadius = h / 2; - float fa = startAngle; - float falen = fa + angleSpan; - float start = -fa * piFloat / 180.0f; - float end = -falen * piFloat / 180.0f; - CGContextAddArc(context, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, start, end, true); - - if (w != h) - scale(FloatSize(1, reverseScaleFactor)); - - float width = strokeThickness(); - int patWidth = 0; - - switch (strokeStyle()) { - case DottedStroke: - patWidth = (int)(width / 2); - break; - case DashedStroke: - patWidth = 3 * (int)(width / 2); - break; - default: - break; - } - - if (patWidth) { - // Example: 80 pixels with a width of 30 pixels. - // Remainder is 20. The maximum pixels of line we could paint - // will be 50 pixels. - int distance; - if (hRadius == vRadius) - distance = static_cast<int>((piFloat * hRadius) / 2.0f); - else // We are elliptical and will have to estimate the distance - distance = static_cast<int>((piFloat * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0f)) / 2.0f); - - int remainder = distance % patWidth; - int coverage = distance - remainder; - int numSegments = coverage / patWidth; - - float patternOffset = 0.0f; - // Special case 1px dotted borders for speed. - if (patWidth == 1) - patternOffset = 1.0f; - else { - bool evenNumberOfSegments = !(numSegments % 2); - if (remainder) - evenNumberOfSegments = !evenNumberOfSegments; - if (evenNumberOfSegments) { - if (remainder) { - patternOffset += patWidth - remainder; - patternOffset += remainder / 2.0f; - } else - patternOffset = patWidth / 2.0f; - } else { - if (remainder) - patternOffset = (patWidth - remainder) / 2.0f; - } - } - - const CGFloat dottedLine[2] = { patWidth, patWidth }; - CGContextSetLineDash(context, patternOffset, dottedLine, 2); - } - - CGContextStrokePath(context); - - CGContextRestoreGState(context); -} - -static void addConvexPolygonToPath(Path& path, size_t numberOfPoints, const FloatPoint* points) -{ - ASSERT(numberOfPoints > 0); - - path.moveTo(points[0]); - for (size_t i = 1; i < numberOfPoints; ++i) - path.addLineTo(points[i]); - path.closeSubpath(); -} - -void GraphicsContext::drawConvexPolygon(size_t numberOfPoints, const FloatPoint* points, bool antialiased) -{ - if (paintingDisabled()) - return; - - if (numberOfPoints <= 1) - return; - - CGContextRef context = platformContext(); - - if (antialiased != shouldAntialias()) - CGContextSetShouldAntialias(context, antialiased); - - Path path; - addConvexPolygonToPath(path, numberOfPoints, points); - drawPath(path); - - if (antialiased != shouldAntialias()) - CGContextSetShouldAntialias(context, shouldAntialias()); -} - -void GraphicsContext::clipConvexPolygon(size_t numberOfPoints, const FloatPoint* points, bool antialias) -{ - if (paintingDisabled()) - return; - - if (numberOfPoints <= 1) - return; - - CGContextRef context = platformContext(); - - if (antialias != shouldAntialias()) - CGContextSetShouldAntialias(context, antialias); - - Path path; - addConvexPolygonToPath(path, numberOfPoints, points); - clipPath(path, RULE_NONZERO); - - if (antialias != shouldAntialias()) - CGContextSetShouldAntialias(context, shouldAntialias()); -} - -void GraphicsContext::applyStrokePattern() -{ - CGContextRef cgContext = platformContext(); - - RetainPtr<CGPatternRef> platformPattern(AdoptCF, m_state.strokePattern->createPlatformPattern(getCTM())); - if (!platformPattern) - return; - - RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0)); - CGContextSetStrokeColorSpace(cgContext, patternSpace.get()); - - const CGFloat patternAlpha = 1; - CGContextSetStrokePattern(cgContext, platformPattern.get(), &patternAlpha); -} - -void GraphicsContext::applyFillPattern() -{ - CGContextRef cgContext = platformContext(); - - RetainPtr<CGPatternRef> platformPattern(AdoptCF, m_state.fillPattern->createPlatformPattern(getCTM())); - if (!platformPattern) - return; - - RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0)); - CGContextSetFillColorSpace(cgContext, patternSpace.get()); - - const CGFloat patternAlpha = 1; - CGContextSetFillPattern(cgContext, platformPattern.get(), &patternAlpha); -} - -static inline bool calculateDrawingMode(const GraphicsContextState& state, CGPathDrawingMode& mode) -{ - bool shouldFill = state.fillPattern || state.fillColor.alpha(); - bool shouldStroke = state.strokePattern || (state.strokeStyle != NoStroke && state.strokeColor.alpha()); - bool useEOFill = state.fillRule == RULE_EVENODD; - - if (shouldFill) { - if (shouldStroke) { - if (useEOFill) - mode = kCGPathEOFillStroke; - else - mode = kCGPathFillStroke; - } else { // fill, no stroke - if (useEOFill) - mode = kCGPathEOFill; - else - mode = kCGPathFill; - } - } else { - // Setting mode to kCGPathStroke even if shouldStroke is false. In that case, we return false and mode will not be used, - // but the compiler will not complain about an uninitialized variable. - mode = kCGPathStroke; - } - - return shouldFill || shouldStroke; -} - -void GraphicsContext::drawPath(const Path& path) -{ - if (paintingDisabled()) - return; - - CGContextRef context = platformContext(); - const GraphicsContextState& state = m_state; - - if (state.fillGradient || state.strokeGradient) { - // We don't have any optimized way to fill & stroke a path using gradients - // FIXME: Be smarter about this. - fillPath(path); - strokePath(path); - return; - } - - CGContextBeginPath(context); - CGContextAddPath(context, path.platformPath()); - - if (state.fillPattern) - applyFillPattern(); - if (state.strokePattern) - applyStrokePattern(); - - CGPathDrawingMode drawingMode; - if (calculateDrawingMode(state, drawingMode)) - CGContextDrawPath(context, drawingMode); -} - -static inline void fillPathWithFillRule(CGContextRef context, WindRule fillRule) -{ - if (fillRule == RULE_EVENODD) - CGContextEOFillPath(context); - else - CGContextFillPath(context); -} - -void GraphicsContext::fillPath(const Path& path) -{ - if (paintingDisabled()) - return; - - CGContextRef context = platformContext(); - - CGContextBeginPath(context); - CGContextAddPath(context, path.platformPath()); - - if (m_state.fillGradient) { - CGContextSaveGState(context); - if (fillRule() == RULE_EVENODD) - CGContextEOClip(context); - else - CGContextClip(context); - CGContextConcatCTM(context, m_state.fillGradient->gradientSpaceTransform()); - m_state.fillGradient->paint(this); - CGContextRestoreGState(context); - return; - } - - if (m_state.fillPattern) - applyFillPattern(); - fillPathWithFillRule(context, fillRule()); -} - -void GraphicsContext::strokePath(const Path& path) -{ - if (paintingDisabled()) - return; - - CGContextRef context = platformContext(); - - CGContextBeginPath(context); - CGContextAddPath(context, path.platformPath()); - - if (m_state.strokeGradient) { - CGContextSaveGState(context); - CGContextReplacePathWithStrokedPath(context); - CGContextClip(context); - CGContextConcatCTM(context, m_state.strokeGradient->gradientSpaceTransform()); - m_state.strokeGradient->paint(this); - CGContextRestoreGState(context); - return; - } - - if (m_state.strokePattern) - applyStrokePattern(); - CGContextStrokePath(context); -} - -void GraphicsContext::fillRect(const FloatRect& rect) -{ - if (paintingDisabled()) - return; - - CGContextRef context = platformContext(); - - if (m_state.fillGradient) { - CGContextSaveGState(context); - CGContextClipToRect(context, rect); - CGContextConcatCTM(context, m_state.fillGradient->gradientSpaceTransform()); - m_state.fillGradient->paint(this); - CGContextRestoreGState(context); - return; - } - - if (m_state.fillPattern) - applyFillPattern(); - CGContextFillRect(context, rect); -} - -void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) -{ - if (paintingDisabled()) - return; - - CGContextRef context = platformContext(); - Color oldFillColor = fillColor(); - ColorSpace oldColorSpace = fillColorSpace(); - - if (oldFillColor != color || oldColorSpace != colorSpace) - setCGFillColor(context, color, colorSpace); - - CGContextFillRect(context, rect); - - if (oldFillColor != color || oldColorSpace != colorSpace) - setCGFillColor(context, oldFillColor, oldColorSpace); -} - -void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace) -{ - if (paintingDisabled()) - return; - - CGContextRef context = platformContext(); - Color oldFillColor = fillColor(); - ColorSpace oldColorSpace = fillColorSpace(); - - if (oldFillColor != color || oldColorSpace != colorSpace) - setCGFillColor(context, color, colorSpace); - - Path path; - path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); - fillPath(path); - - if (oldFillColor != color || oldColorSpace != colorSpace) - setCGFillColor(context, oldFillColor, oldColorSpace); -} - -void GraphicsContext::clip(const FloatRect& rect) -{ - if (paintingDisabled()) - return; - CGContextClipToRect(platformContext(), rect); - m_data->clip(rect); -} - -void GraphicsContext::clipOut(const IntRect& rect) -{ - if (paintingDisabled()) - return; - - CGRect rects[2] = { CGContextGetClipBoundingBox(platformContext()), rect }; - CGContextBeginPath(platformContext()); - CGContextAddRects(platformContext(), rects, 2); - CGContextEOClip(platformContext()); -} - -void GraphicsContext::clipPath(const Path& path, WindRule clipRule) -{ - if (paintingDisabled()) - return; - - if (path.isEmpty()) - return; - - CGContextRef context = platformContext(); - - CGContextBeginPath(platformContext()); - CGContextAddPath(platformContext(), path.platformPath()); - - if (clipRule == RULE_EVENODD) - CGContextEOClip(context); - else - CGContextClip(context); -} - -void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) -{ - if (paintingDisabled()) - return; - - clip(rect); - CGContextRef context = platformContext(); - - // Add outer ellipse - CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height())); - // Add inner ellipse. - CGContextAddEllipseInRect(context, CGRectMake(rect.x() + thickness, rect.y() + thickness, - rect.width() - (thickness * 2), rect.height() - (thickness * 2))); - - CGContextEOClip(context); -} - -void GraphicsContext::beginTransparencyLayer(float opacity) -{ - if (paintingDisabled()) - return; - CGContextRef context = platformContext(); - CGContextSaveGState(context); - CGContextSetAlpha(context, opacity); - CGContextBeginTransparencyLayer(context, 0); - m_data->beginTransparencyLayer(); - m_data->m_userToDeviceTransformKnownToBeIdentity = false; -} - -void GraphicsContext::endTransparencyLayer() -{ - if (paintingDisabled()) - return; - CGContextRef context = platformContext(); - CGContextEndTransparencyLayer(context); - CGContextRestoreGState(context); - m_data->endTransparencyLayer(); - m_data->m_userToDeviceTransformKnownToBeIdentity = false; -} - -void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace) -{ - if (paintingDisabled()) - return; - CGFloat xOffset = offset.width(); - CGFloat yOffset = offset.height(); - CGFloat blurRadius = blur; - CGContextRef context = platformContext(); - - if (!m_state.shadowsIgnoreTransforms) { - CGAffineTransform userToBaseCTM = wkGetUserToBaseCTM(context); - - CGFloat A = userToBaseCTM.a * userToBaseCTM.a + userToBaseCTM.b * userToBaseCTM.b; - CGFloat B = userToBaseCTM.a * userToBaseCTM.c + userToBaseCTM.b * userToBaseCTM.d; - CGFloat C = B; - CGFloat D = userToBaseCTM.c * userToBaseCTM.c + userToBaseCTM.d * userToBaseCTM.d; - - CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D))))); - - // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp - blurRadius = min(blur * smallEigenvalue, narrowPrecisionToCGFloat(1000.0)); - - CGSize offsetInBaseSpace = CGSizeApplyAffineTransform(offset, userToBaseCTM); - - xOffset = offsetInBaseSpace.width; - yOffset = offsetInBaseSpace.height; - } - - // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated - // to the desired integer. - static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128); - if (xOffset > 0) - xOffset += extraShadowOffset; - else if (xOffset < 0) - xOffset -= extraShadowOffset; - - if (yOffset > 0) - yOffset += extraShadowOffset; - else if (yOffset < 0) - yOffset -= extraShadowOffset; - - // Check for an invalid color, as this means that the color was not set for the shadow - // and we should therefore just use the default shadow color. - if (!color.isValid()) - CGContextSetShadow(context, CGSizeMake(xOffset, yOffset), blurRadius); - else - CGContextSetShadowWithColor(context, CGSizeMake(xOffset, yOffset), blurRadius, cachedCGColor(color, colorSpace)); -} - -void GraphicsContext::clearPlatformShadow() -{ - if (paintingDisabled()) - return; - CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0); -} - -void GraphicsContext::setMiterLimit(float limit) -{ - if (paintingDisabled()) - return; - CGContextSetMiterLimit(platformContext(), limit); -} - -void GraphicsContext::setAlpha(float alpha) -{ - if (paintingDisabled()) - return; - CGContextSetAlpha(platformContext(), alpha); -} - -void GraphicsContext::clearRect(const FloatRect& r) -{ - if (paintingDisabled()) - return; - CGContextClearRect(platformContext(), r); -} - -void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth) -{ - if (paintingDisabled()) - return; - - CGContextRef context = platformContext(); - - if (m_state.strokeGradient) { - CGContextSaveGState(context); - setStrokeThickness(lineWidth); - CGContextAddRect(context, r); - CGContextReplacePathWithStrokedPath(context); - CGContextClip(context); - m_state.strokeGradient->paint(this); - CGContextRestoreGState(context); - return; - } - - if (m_state.strokePattern) - applyStrokePattern(); - CGContextStrokeRectWithWidth(context, r, lineWidth); -} - -void GraphicsContext::setLineCap(LineCap cap) -{ - if (paintingDisabled()) - return; - switch (cap) { - case ButtCap: - CGContextSetLineCap(platformContext(), kCGLineCapButt); - break; - case RoundCap: - CGContextSetLineCap(platformContext(), kCGLineCapRound); - break; - case SquareCap: - CGContextSetLineCap(platformContext(), kCGLineCapSquare); - break; - } -} - -void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) -{ - CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size()); -} - -void GraphicsContext::setLineJoin(LineJoin join) -{ - if (paintingDisabled()) - return; - switch (join) { - case MiterJoin: - CGContextSetLineJoin(platformContext(), kCGLineJoinMiter); - break; - case RoundJoin: - CGContextSetLineJoin(platformContext(), kCGLineJoinRound); - break; - case BevelJoin: - CGContextSetLineJoin(platformContext(), kCGLineJoinBevel); - break; - } -} - -void GraphicsContext::clip(const Path& path) -{ - if (paintingDisabled()) - return; - CGContextRef context = platformContext(); - - // CGContextClip does nothing if the path is empty, so in this case, we - // instead clip against a zero rect to reduce the clipping region to - // nothing - which is the intended behavior of clip() if the path is empty. - if (path.isEmpty()) - CGContextClipToRect(context, CGRectZero); - else { - CGContextBeginPath(context); - CGContextAddPath(context, path.platformPath()); - CGContextClip(context); - } - m_data->clip(path); -} - -void GraphicsContext::canvasClip(const Path& path) -{ - clip(path); -} - -void GraphicsContext::clipOut(const Path& path) -{ - if (paintingDisabled()) - return; - - CGContextBeginPath(platformContext()); - CGContextAddRect(platformContext(), CGContextGetClipBoundingBox(platformContext())); - CGContextAddPath(platformContext(), path.platformPath()); - CGContextEOClip(platformContext()); -} - -void GraphicsContext::scale(const FloatSize& size) -{ - if (paintingDisabled()) - return; - CGContextScaleCTM(platformContext(), size.width(), size.height()); - m_data->scale(size); - m_data->m_userToDeviceTransformKnownToBeIdentity = false; -} - -void GraphicsContext::rotate(float angle) -{ - if (paintingDisabled()) - return; - CGContextRotateCTM(platformContext(), angle); - m_data->rotate(angle); - m_data->m_userToDeviceTransformKnownToBeIdentity = false; -} - -void GraphicsContext::translate(float x, float y) -{ - if (paintingDisabled()) - return; - CGContextTranslateCTM(platformContext(), x, y); - m_data->translate(x, y); - m_data->m_userToDeviceTransformKnownToBeIdentity = false; -} - -void GraphicsContext::concatCTM(const AffineTransform& transform) -{ - if (paintingDisabled()) - return; - CGContextConcatCTM(platformContext(), transform); - m_data->concatCTM(transform); - m_data->m_userToDeviceTransformKnownToBeIdentity = false; -} - -AffineTransform GraphicsContext::getCTM() const -{ - CGAffineTransform t = CGContextGetCTM(platformContext()); - return AffineTransform(t.a, t.b, t.c, t.d, t.tx, t.ty); -} - -FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) -{ - // It is not enough just to round to pixels in device space. The rotation part of the - // affine transform matrix to device space can mess with this conversion if we have a - // rotating image like the hands of the world clock widget. We just need the scale, so - // we get the affine transform matrix and extract the scale. - - if (m_data->m_userToDeviceTransformKnownToBeIdentity) - return rect; - - CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext()); - if (CGAffineTransformIsIdentity(deviceMatrix)) { - m_data->m_userToDeviceTransformKnownToBeIdentity = true; - return rect; - } - - float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b); - float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d); - - CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY); - CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX, - (rect.y() + rect.height()) * deviceScaleY); - - deviceOrigin.x = roundf(deviceOrigin.x); - deviceOrigin.y = roundf(deviceOrigin.y); - deviceLowerRight.x = roundf(deviceLowerRight.x); - deviceLowerRight.y = roundf(deviceLowerRight.y); - - // Don't let the height or width round to 0 unless either was originally 0 - if (deviceOrigin.y == deviceLowerRight.y && rect.height()) - deviceLowerRight.y += 1; - if (deviceOrigin.x == deviceLowerRight.x && rect.width()) - deviceLowerRight.x += 1; - - FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY); - FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY); - return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin); -} - -void GraphicsContext::drawLineForText(const IntPoint& point, int width, bool printing) -{ - if (paintingDisabled()) - return; - - if (width <= 0) - return; - - float x = point.x(); - float y = point.y(); - float lineLength = width; - - // Use a minimum thickness of 0.5 in user space. - // See http://bugs.webkit.org/show_bug.cgi?id=4255 for details of why 0.5 is the right minimum thickness to use. - float thickness = max(strokeThickness(), 0.5f); - - bool restoreAntialiasMode = false; - - if (!printing) { - // On screen, use a minimum thickness of 1.0 in user space (later rounded to an integral number in device space). - float adjustedThickness = max(thickness, 1.0f); - - // FIXME: This should be done a better way. - // We try to round all parameters to integer boundaries in device space. If rounding pixels in device space - // makes our thickness more than double, then there must be a shrinking-scale factor and rounding to pixels - // in device space will make the underlines too thick. - CGRect lineRect = roundToDevicePixels(FloatRect(x, y, lineLength, adjustedThickness)); - if (lineRect.size.height < thickness * 2.0) { - x = lineRect.origin.x; - y = lineRect.origin.y; - lineLength = lineRect.size.width; - thickness = lineRect.size.height; - if (shouldAntialias()) { - CGContextSetShouldAntialias(platformContext(), false); - restoreAntialiasMode = true; - } - } - } - - if (fillColor() != strokeColor()) - setCGFillColor(platformContext(), strokeColor(), strokeColorSpace()); - CGContextFillRect(platformContext(), CGRectMake(x, y, lineLength, thickness)); - if (fillColor() != strokeColor()) - setCGFillColor(platformContext(), fillColor(), fillColorSpace()); - - if (restoreAntialiasMode) - CGContextSetShouldAntialias(platformContext(), true); -} - -void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) -{ - if (paintingDisabled()) - return; - - RetainPtr<CFURLRef> urlRef(AdoptCF, link.createCFURL()); - if (!urlRef) - return; - - CGContextRef context = platformContext(); - - // Get the bounding box to handle clipping. - CGRect box = CGContextGetClipBoundingBox(context); - - IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height); - IntRect rect = destRect; - rect.intersect(intBox); - - CGPDFContextSetURLForRect(context, urlRef.get(), - CGRectApplyAffineTransform(rect, CGContextGetCTM(context))); -} - -void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) -{ - if (paintingDisabled()) - return; - - CGInterpolationQuality quality = kCGInterpolationDefault; - switch (mode) { - case InterpolationDefault: - quality = kCGInterpolationDefault; - break; - case InterpolationNone: - quality = kCGInterpolationNone; - break; - case InterpolationLow: - quality = kCGInterpolationLow; - break; - - // Fall through to InterpolationHigh if kCGInterpolationMedium is not usable. - case InterpolationMedium: -#if USE(CG_INTERPOLATION_MEDIUM) - quality = kCGInterpolationMedium; - break; -#endif - case InterpolationHigh: - quality = kCGInterpolationHigh; - break; - } - CGContextSetInterpolationQuality(platformContext(), quality); -} - -InterpolationQuality GraphicsContext::imageInterpolationQuality() const -{ - if (paintingDisabled()) - return InterpolationDefault; - - CGInterpolationQuality quality = CGContextGetInterpolationQuality(platformContext()); - switch (quality) { - case kCGInterpolationDefault: - return InterpolationDefault; - case kCGInterpolationNone: - return InterpolationNone; - case kCGInterpolationLow: - return InterpolationLow; -#if HAVE(CG_INTERPOLATION_MEDIUM) - // kCGInterpolationMedium is known to be present in the CGInterpolationQuality enum. - case kCGInterpolationMedium: -#if USE(CG_INTERPOLATION_MEDIUM) - // Only map to InterpolationMedium if targeting a system that understands it. - return InterpolationMedium; -#else - return InterpolationDefault; -#endif // USE(CG_INTERPOLATION_MEDIUM) -#endif // HAVE(CG_INTERPOLATION_MEDIUM) - case kCGInterpolationHigh: - return InterpolationHigh; - } - return InterpolationDefault; -} - -void GraphicsContext::setAllowsFontSmoothing(bool allowsFontSmoothing) -{ - UNUSED_PARAM(allowsFontSmoothing); -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - CGContextRef context = platformContext(); - CGContextSetAllowsFontSmoothing(context, allowsFontSmoothing); -#endif -} - -void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode) -{ - if (paintingDisabled()) - return; - - // Wow, wish CG had used bits here. - CGContextRef context = platformContext(); - switch (mode) { - case TextModeInvisible: - CGContextSetTextDrawingMode(context, kCGTextInvisible); - break; - case TextModeFill: - CGContextSetTextDrawingMode(context, kCGTextFill); - break; - case TextModeStroke: - CGContextSetTextDrawingMode(context, kCGTextStroke); - break; - case TextModeFill | TextModeStroke: - CGContextSetTextDrawingMode(context, kCGTextFillStroke); - break; - case TextModeClip: - CGContextSetTextDrawingMode(context, kCGTextClip); - break; - case TextModeFill | TextModeClip: - CGContextSetTextDrawingMode(context, kCGTextFillClip); - break; - case TextModeStroke | TextModeClip: - CGContextSetTextDrawingMode(context, kCGTextStrokeClip); - break; - case TextModeFill | TextModeStroke | TextModeClip: - CGContextSetTextDrawingMode(context, kCGTextFillStrokeClip); - break; - default: - break; - } -} - -void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace) -{ - if (paintingDisabled()) - return; - setCGStrokeColor(platformContext(), color, colorSpace); -} - -void GraphicsContext::setPlatformStrokeThickness(float thickness) -{ - if (paintingDisabled()) - return; - CGContextSetLineWidth(platformContext(), thickness); -} - -void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace) -{ - if (paintingDisabled()) - return; - setCGFillColor(platformContext(), color, colorSpace); -} - -void GraphicsContext::setPlatformShouldAntialias(bool enable) -{ - if (paintingDisabled()) - return; - CGContextSetShouldAntialias(platformContext(), enable); -} - -void GraphicsContext::setPlatformShouldSmoothFonts(bool enable) -{ - if (paintingDisabled()) - return; - CGContextSetShouldSmoothFonts(platformContext(), enable); -} - -#ifndef BUILDING_ON_TIGER // Tiger's setPlatformCompositeOperation() is defined in GraphicsContextMac.mm. -void GraphicsContext::setPlatformCompositeOperation(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); -} -#endif - -} |