summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/cg/GraphicsContextCG.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/cg/GraphicsContextCG.cpp')
-rw-r--r--WebCore/platform/graphics/cg/GraphicsContextCG.cpp309
1 files changed, 262 insertions, 47 deletions
diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp
index c490dcb..3f0e6e7 100644
--- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp
+++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp
@@ -1,5 +1,6 @@
/*
* 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
@@ -29,11 +30,21 @@
#include "AffineTransform.h"
#include "FloatConversion.h"
+#include "GraphicsContextPrivate.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>
+
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+#define HAVE_CG_INTERPOLATION_MEDIUM 1
+#endif
using namespace std;
@@ -98,6 +109,8 @@ void GraphicsContext::restorePlatformState()
// 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;
@@ -252,14 +265,7 @@ void GraphicsContext::drawEllipse(const IntRect& rect)
CGContextAddArc(context, rect.x() + r, rect.y() + r, r, 0.0f, 2.0f * piFloat, 0);
CGContextClosePath(context);
- if (fillColor().alpha()) {
- if (strokeStyle() != NoStroke)
- // stroke and fill
- CGContextDrawPath(context, kCGPathFillStroke);
- else
- CGContextFillPath(context);
- } else if (strokeStyle() != NoStroke)
- CGContextStrokePath(context);
+ drawPath();
}
@@ -372,29 +378,177 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points
CGContextAddLineToPoint(context, points[i].x(), points[i].y());
CGContextClosePath(context);
- if (fillColor().alpha()) {
- if (strokeStyle() != NoStroke)
- CGContextDrawPath(context, kCGPathEOFillStroke);
- else
- CGContextEOFillPath(context);
- } else
- CGContextStrokePath(context);
+ drawPath();
CGContextRestoreGState(context);
}
-void GraphicsContext::fillRect(const IntRect& rect, const Color& color)
+static void applyStrokePattern(GraphicsContext* context, Pattern* pattern)
+{
+ CGContextRef cgContext = context->platformContext();
+
+ CGPatternRef platformPattern = pattern->createPlatformPattern(context->getCTM());
+ if (!platformPattern)
+ return;
+
+ CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
+ CGContextSetStrokeColorSpace(cgContext, patternSpace);
+ CGColorSpaceRelease(patternSpace);
+
+ const CGFloat patternAlpha = 1;
+ CGContextSetStrokePattern(cgContext, platformPattern, &patternAlpha);
+ CGPatternRelease(platformPattern);
+}
+
+static void applyFillPattern(GraphicsContext* context, Pattern* pattern)
+{
+ CGContextRef cgContext = context->platformContext();
+
+ CGPatternRef platformPattern = pattern->createPlatformPattern(context->getCTM());
+ if (!platformPattern)
+ return;
+
+ CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
+ CGContextSetFillColorSpace(cgContext, patternSpace);
+ CGColorSpaceRelease(patternSpace);
+
+ const CGFloat patternAlpha = 1;
+ CGContextSetFillPattern(cgContext, platformPattern, &patternAlpha);
+ CGPatternRelease(platformPattern);
+}
+
+static inline bool calculateDrawingMode(const GraphicsContextState& state, CGPathDrawingMode& mode)
+{
+ bool shouldFill = state.fillColorSpace == PatternColorSpace || state.fillColor.alpha();
+ bool shouldStroke = state.strokeColorSpace == PatternColorSpace || (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 compain about an uninitialized variable.
+ mode = kCGPathStroke;
+ }
+
+ return shouldFill || shouldStroke;
+}
+
+void GraphicsContext::drawPath()
{
if (paintingDisabled())
return;
- if (color.alpha()) {
- CGContextRef context = platformContext();
- Color oldFillColor = fillColor();
- if (oldFillColor != color)
- setCGFillColor(context, color);
+
+ CGContextRef context = platformContext();
+ const GraphicsContextState& state = m_common->state;
+
+ if (state.fillColorSpace == GradientColorSpace || state.strokeColorSpace == GradientColorSpace) {
+ // We don't have any optimized way to fill & stroke a path using gradients
+ fillPath();
+ strokePath();
+ return;
+ }
+
+ if (state.fillColorSpace == PatternColorSpace)
+ applyFillPattern(this, m_common->state.fillPattern.get());
+ if (state.strokeColorSpace == PatternColorSpace)
+ applyStrokePattern(this, m_common->state.strokePattern.get());
+
+ 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()
+{
+ if (paintingDisabled())
+ return;
+
+ CGContextRef context = platformContext();
+ switch (m_common->state.fillColorSpace) {
+ case SolidColorSpace:
+ if (fillColor().alpha())
+ fillPathWithFillRule(context, fillRule());
+ break;
+ case PatternColorSpace:
+ applyFillPattern(this, m_common->state.fillPattern.get());
+ fillPathWithFillRule(context, fillRule());
+ break;
+ case GradientColorSpace:
+ CGContextSaveGState(context);
+ if (fillRule() == RULE_EVENODD)
+ CGContextEOClip(context);
+ else
+ CGContextClip(context);
+ CGContextDrawShading(context, m_common->state.fillGradient->platformGradient());
+ CGContextRestoreGState(context);
+ break;
+ }
+}
+
+void GraphicsContext::strokePath()
+{
+ if (paintingDisabled())
+ return;
+
+ CGContextRef context = platformContext();
+ switch (m_common->state.strokeColorSpace) {
+ case SolidColorSpace:
+ if (fillColor().alpha())
+ CGContextStrokePath(context);
+ break;
+ case PatternColorSpace:
+ applyStrokePattern(this, m_common->state.strokePattern.get());
+ CGContextStrokePath(context);
+ break;
+ case GradientColorSpace:
+ CGContextSaveGState(context);
+ CGContextReplacePathWithStrokedPath(context);
+ CGContextClip(context);
+ CGContextDrawShading(context, m_common->state.strokeGradient->platformGradient());
+ CGContextRestoreGState(context);
+ break;
+ }
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+ CGContextRef context = platformContext();
+ switch (m_common->state.fillColorSpace) {
+ case SolidColorSpace:
+ if (fillColor().alpha())
+ CGContextFillRect(context, rect);
+ break;
+ case PatternColorSpace:
+ applyFillPattern(this, m_common->state.fillPattern.get());
CGContextFillRect(context, rect);
- if (oldFillColor != color)
- setCGFillColor(context, oldFillColor);
+ break;
+ case GradientColorSpace:
+ CGContextSaveGState(context);
+ CGContextClipToRect(context, rect);
+ CGContextDrawShading(context, m_common->state.fillGradient->platformGradient());
+ CGContextRestoreGState(context);
+ break;
}
}
@@ -424,14 +578,13 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef
setCGFillColor(context, color);
addPath(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
- CGContextFillPath(context);
+ fillPath();
if (oldFillColor != color)
setCGFillColor(context, oldFillColor);
}
-
-void GraphicsContext::clip(const IntRect& rect)
+void GraphicsContext::clip(const FloatRect& rect)
{
if (paintingDisabled())
return;
@@ -478,6 +631,18 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness
CGContextEOClip(context);
}
+void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer* imageBuffer)
+{
+ if (paintingDisabled())
+ return;
+
+ CGContextTranslateCTM(platformContext(), rect.x(), rect.y() + rect.height());
+ CGContextScaleCTM(platformContext(), 1, -1);
+ CGContextClipToMask(platformContext(), FloatRect(FloatPoint(), rect.size()), imageBuffer->image()->getCGImageRef());
+ CGContextScaleCTM(platformContext(), 1, -1);
+ CGContextTranslateCTM(platformContext(), -rect.x(), -rect.y() - rect.height());
+}
+
void GraphicsContext::beginTransparencyLayer(float opacity)
{
if (paintingDisabled())
@@ -501,19 +666,28 @@ void GraphicsContext::endTransparencyLayer()
m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
-void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color)
+void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Color& color)
{
- // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp
- blur = min(blur, 1000);
-
if (paintingDisabled())
return;
CGContextRef context = platformContext();
+ CGAffineTransform transform = CGContextGetCTM(context);
+
+ CGFloat A = transform.a * transform.a + transform.b * transform.b;
+ CGFloat B = transform.a * transform.c + transform.b * transform.d;
+ CGFloat C = B;
+ CGFloat D = transform.c * transform.c + transform.d * transform.d;
- CGFloat width = size.width();
- CGFloat height = size.height();
+ 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
+ CGFloat blurRadius = min(blur * smallEigenvalue, narrowPrecisionToCGFloat(1000.0));
+
+ CGSize sizeInDeviceSpace = CGSizeApplyAffineTransform(size, transform);
+
+ CGFloat width = sizeInDeviceSpace.width;
+ CGFloat height = sizeInDeviceSpace.height;
-#ifdef BUILDING_ON_TIGER
// 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);
@@ -526,23 +700,22 @@ void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& colo
height += extraShadowOffset;
else if (height < 0)
height -= extraShadowOffset;
-#endif
// 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(width, -height), blur); // y is flipped.
+ CGContextSetShadow(context, CGSizeMake(width, height), blurRadius);
else {
CGColorRef colorCG = cgColor(color);
CGContextSetShadowWithColor(context,
- CGSizeMake(width, -height), // y is flipped.
- blur,
+ CGSizeMake(width, height),
+ blurRadius,
colorCG);
CGColorRelease(colorCG);
}
}
-void GraphicsContext::clearShadow()
+void GraphicsContext::clearPlatformShadow()
{
if (paintingDisabled())
return;
@@ -594,6 +767,11 @@ void GraphicsContext::setLineCap(LineCap cap)
}
}
+void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
+{
+ CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size());
+}
+
void GraphicsContext::setLineJoin(LineJoin join)
{
if (paintingDisabled())
@@ -610,7 +788,7 @@ void GraphicsContext::setLineJoin(LineJoin join)
break;
}
}
-
+
void GraphicsContext::beginPath()
{
CGContextBeginPath(platformContext());
@@ -789,20 +967,57 @@ void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
}
}
-void GraphicsContext::setUseLowQualityImageInterpolation(bool lowQualityMode)
+void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
{
if (paintingDisabled())
return;
-
- CGContextSetInterpolationQuality(platformContext(), lowQualityMode ? kCGInterpolationNone : kCGInterpolationDefault);
+
+ 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 available
+ case InterpolationMedium:
+#if HAVE(CG_INTERPOLATION_MEDIUM)
+ quality = kCGInterpolationMedium;
+ break;
+#endif
+ case InterpolationHigh:
+ quality = kCGInterpolationHigh;
+ break;
+ }
+ CGContextSetInterpolationQuality(platformContext(), quality);
}
-bool GraphicsContext::useLowQualityImageInterpolation() const
+InterpolationQuality GraphicsContext::imageInterpolationQuality() const
{
if (paintingDisabled())
- return false;
-
- return CGContextGetInterpolationQuality(platformContext());
+ 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)
+ case kCGInterpolationMedium:
+ return InterpolationMedium;
+#endif
+ case kCGInterpolationHigh:
+ return InterpolationHigh;
+ }
+ return InterpolationDefault;
}
void GraphicsContext::setPlatformTextDrawingMode(int mode)
@@ -924,6 +1139,6 @@ void GraphicsContext::setCompositeOperation(CompositeOperator mode)
CGContextSetBlendMode(platformContext(), target);
}
#endif
-
+
}