diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
commit | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch) | |
tree | 4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/platform/graphics/cg | |
parent | 9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff) | |
download | external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/platform/graphics/cg')
-rw-r--r-- | WebCore/platform/graphics/cg/AffineTransformCG.cpp | 8 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/GradientCG.cpp | 82 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/GraphicsContextCG.cpp | 309 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h | 8 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/ImageBufferCG.cpp | 244 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/ImageBufferData.h | 42 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/ImageCG.cpp | 147 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/ImageSourceCG.cpp | 48 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/PDFDocumentImage.h | 15 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/PathCG.cpp | 12 | ||||
-rw-r--r-- | WebCore/platform/graphics/cg/PatternCG.cpp | 77 |
11 files changed, 824 insertions, 168 deletions
diff --git a/WebCore/platform/graphics/cg/AffineTransformCG.cpp b/WebCore/platform/graphics/cg/AffineTransformCG.cpp index 8fdd1e6..4f0bca0 100644 --- a/WebCore/platform/graphics/cg/AffineTransformCG.cpp +++ b/WebCore/platform/graphics/cg/AffineTransformCG.cpp @@ -37,8 +37,8 @@ namespace WebCore { AffineTransform::AffineTransform() + : m_transform(CGAffineTransformIdentity) { - m_transform = CGAffineTransformIdentity; } AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) @@ -51,9 +51,9 @@ AffineTransform::AffineTransform(double a, double b, double c, double d, double narrowPrecisionToCGFloat(ty)); } -AffineTransform::AffineTransform(CGAffineTransform t) +AffineTransform::AffineTransform(const PlatformAffineTransform& t) + : m_transform(t) { - m_transform = t; } void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) @@ -190,7 +190,7 @@ AffineTransform AffineTransform::inverse() const return AffineTransform(); } -AffineTransform::operator CGAffineTransform() const +AffineTransform::operator PlatformAffineTransform() const { return m_transform; } diff --git a/WebCore/platform/graphics/cg/GradientCG.cpp b/WebCore/platform/graphics/cg/GradientCG.cpp new file mode 100644 index 0000000..c189fd5 --- /dev/null +++ b/WebCore/platform/graphics/cg/GradientCG.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * 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 "Gradient.h" + +#include "CSSParser.h" +#include "GraphicsContext.h" + +#include <ApplicationServices/ApplicationServices.h> + +namespace WebCore { + +void Gradient::platformDestroy() +{ + CGShadingRelease(m_gradient); + m_gradient = 0; +} + +static void gradientCallback(void* info, const CGFloat* in, CGFloat* out) +{ + float r, g, b, a; + static_cast<const Gradient*>(info)->getColor(*in, &r, &g, &b, &a); + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = a; +} + +CGShadingRef Gradient::platformGradient() +{ + if (m_gradient) + return m_gradient; + + const CGFloat intervalRanges[2] = { 0, 1 }; + const CGFloat colorComponentRanges[4 * 2] = { 0, 1, 0, 1, 0, 1, 0, 1 }; + const CGFunctionCallbacks gradientCallbacks = { 0, gradientCallback, 0 }; + CGFunctionRef colorFunction = CGFunctionCreate(this, 1, intervalRanges, 4, colorComponentRanges, &gradientCallbacks); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + if (m_radial) + m_gradient = CGShadingCreateRadial(colorSpace, m_p0, m_r0, m_p1, m_r1, colorFunction, true, true); + else + m_gradient = CGShadingCreateAxial(colorSpace, m_p0, m_p1, colorFunction, true, true); + + CGColorSpaceRelease(colorSpace); + CGFunctionRelease(colorFunction); + + return m_gradient; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + context->clip(rect); + CGContextDrawShading(context->platformContext(), platformGradient()); +} + +} //namespace 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 - + } diff --git a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h index 937481b..8827ff7 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h +++ b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h @@ -45,11 +45,11 @@ public: CGContextRelease(m_cgContext); } -#if PLATFORM(MAC) +#if PLATFORM(MAC) || PLATFORM(CHROMIUM) // These methods do nothing on Mac. void save() {} void restore() {} - void clip(const IntRect&) {} + void clip(const FloatRect&) {} void clip(const Path&) {} void scale(const FloatSize&) {} void rotate(float) {} @@ -63,7 +63,7 @@ public: // On Windows, we need to update the HDC for form controls to draw in the right place. void save(); void restore(); - void clip(const IntRect&); + void clip(const FloatRect&); void clip(const Path&); void scale(const FloatSize&); void rotate(float); @@ -71,9 +71,7 @@ public: void concatCTM(const AffineTransform&); void beginTransparencyLayer() { m_transparencyCount++; } void endTransparencyLayer() { m_transparencyCount--; } -#endif -#if PLATFORM(WIN) HDC m_hdc; unsigned m_transparencyCount; #endif diff --git a/WebCore/platform/graphics/cg/ImageBufferCG.cpp b/WebCore/platform/graphics/cg/ImageBufferCG.cpp index 2d1ac01..502313b 100644 --- a/WebCore/platform/graphics/cg/ImageBufferCG.cpp +++ b/WebCore/platform/graphics/cg/ImageBufferCG.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,65 +21,69 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ImageBuffer.h" +#include "Base64.h" +#include "BitmapImage.h" +#include "CString.h" #include "GraphicsContext.h" - +#include "ImageData.h" +#include "MIMETypeRegistry.h" +#include "PlatformString.h" #include <ApplicationServices/ApplicationServices.h> #include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/RetainPtr.h> using namespace std; namespace WebCore { -auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool grayScale) +ImageBufferData::ImageBufferData(const IntSize&) + : m_data(0) { +} + +ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) + : m_data(size) + , m_size(size) +{ + success = false; // Make early return mean failure. + unsigned bytesPerRow; if (size.width() < 0 || size.height() < 0) - return auto_ptr<ImageBuffer>(); - unsigned int bytesPerRow = size.width(); + return; + bytesPerRow = size.width(); if (!grayScale) { // Protect against overflow if (bytesPerRow > 0x3FFFFFFF) - return auto_ptr<ImageBuffer>(); + return; bytesPerRow *= 4; } - void* imageBuffer = fastCalloc(size.height(), bytesPerRow); - if (!imageBuffer) - return auto_ptr<ImageBuffer>(); - + m_data.m_data = tryFastCalloc(size.height(), bytesPerRow); + ASSERT((reinterpret_cast<size_t>(m_data.m_data) & 2) == 0); + CGColorSpaceRef colorSpace = grayScale ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB(); - CGContextRef cgContext = CGBitmapContextCreate(imageBuffer, size.width(), size.height(), 8, bytesPerRow, + CGContextRef cgContext = CGBitmapContextCreate(m_data.m_data, size.width(), size.height(), 8, bytesPerRow, colorSpace, grayScale ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpace); - if (!cgContext) { - fastFree(imageBuffer); - return auto_ptr<ImageBuffer>(); - } + if (!cgContext) + return; - auto_ptr<GraphicsContext> context(new GraphicsContext(cgContext)); + m_context.set(new GraphicsContext(cgContext)); + m_context->scale(FloatSize(1, -1)); + m_context->translate(0, -size.height()); CGContextRelease(cgContext); - - return auto_ptr<ImageBuffer>(new ImageBuffer(imageBuffer, size, context)); -} - - -ImageBuffer::ImageBuffer(void* imageData, const IntSize& size, auto_ptr<GraphicsContext> context) - : m_data(imageData) - , m_size(size) - , m_context(context.release()) - , m_cgImage(0) -{ + success = true; } ImageBuffer::~ImageBuffer() { - fastFree(m_data); - CGImageRelease(m_cgImage); + fastFree(m_data.m_data); } GraphicsContext* ImageBuffer::context() const @@ -86,16 +91,185 @@ GraphicsContext* ImageBuffer::context() const return m_context.get(); } -CGImageRef ImageBuffer::cgImage() const +Image* ImageBuffer::image() const { - // It's assumed that if cgImage() is called, the actual rendering to the - // contained GraphicsContext must be done, as we create the CGImageRef here. - if (!m_cgImage) { + if (!m_image) { + // It's assumed that if image() is called, the actual rendering to the + // GraphicsContext must be done. ASSERT(context()); - m_cgImage = CGBitmapContextCreateImage(context()->platformContext()); + CGImageRef cgImage = CGBitmapContextCreateImage(context()->platformContext()); + // BitmapImage will release the passed in CGImage on destruction + m_image = BitmapImage::create(cgImage); + } + return m_image.get(); +} + +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const +{ + PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); + unsigned char* data = result->data()->data().data(); + + if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) + memset(data, 0, result->data()->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.x() + rect.width(); + if (endx > m_size.width()) + endx = m_size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.y() + rect.height(); + if (endy > m_size.height()) + endy = m_size.height(); + int numRows = endy - originy; + + unsigned srcBytesPerRow = 4 * m_size.width(); + unsigned destBytesPerRow = 4 * rect.width(); + + // ::create ensures that all ImageBuffers have valid data, so we don't need to check it here. + unsigned char* srcRows = reinterpret_cast<unsigned char*>(m_data.m_data) + originy * srcBytesPerRow + originx * 4; + unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + if (unsigned char alpha = srcRows[basex + 3]) { + destRows[basex] = (srcRows[basex] * 255) / alpha; + destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha; + destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha; + destRows[basex + 3] = alpha; + } else + reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + } + srcRows += srcBytesPerRow; + destRows += destBytesPerRow; + } + return result; +} + +void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) +{ + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < m_size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= m_size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < m_size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= m_size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * source->width(); + unsigned destBytesPerRow = 4 * m_size.width(); + + unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; + unsigned char* destRows = reinterpret_cast<unsigned char*>(m_data.m_data) + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + unsigned char alpha = srcRows[basex + 3]; + if (alpha != 255) { + destRows[basex] = (srcRows[basex] * alpha + 254) / 255; + destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255; + destRows[basex + 2] = (srcRows[basex + 2] * alpha + 254) / 255; + destRows[basex + 3] = alpha; + } else + reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + } + destRows += destBytesPerRow; + srcRows += srcBytesPerRow; } +} + +static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType) +{ +#if PLATFORM(MAC) + RetainPtr<CFStringRef> mimeTypeCFString(AdoptCF, mimeType.createCFString()); + return RetainPtr<CFStringRef>(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeTypeCFString.get(), 0)); +#else + // FIXME: Add Windows support for all the supported UTIs when a way to convert from MIMEType to UTI reliably is found. + // For now, only support PNG, JPEG, and GIF. See <rdar://problem/6095286>. + static const CFStringRef kUTTypePNG = CFSTR("public.png"); + static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg"); + static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif"); - return m_cgImage; + if (equalIgnoringCase(mimeType, "image/png")) + return kUTTypePNG; + if (equalIgnoringCase(mimeType, "image/jpeg")) + return kUTTypeJPEG; + if (equalIgnoringCase(mimeType, "image/gif")) + return kUTTypeGIF; + + ASSERT_NOT_REACHED(); + return kUTTypePNG; +#endif } +String ImageBuffer::toDataURL(const String& mimeType) const +{ + ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); + + RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context()->platformContext())); + if (!image) + return "data:,"; + + size_t width = CGImageGetWidth(image.get()); + size_t height = CGImageGetHeight(image.get()); + + OwnArrayPtr<uint32_t> imageData(new uint32_t[width * height]); + if (!imageData) + return "data:,"; + + RetainPtr<CGImageRef> transformedImage(AdoptCF, CGBitmapContextCreateImage(context()->platformContext())); + if (!transformedImage) + return "data:,"; + + RetainPtr<CFMutableDataRef> transformedImageData(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0)); + if (!transformedImageData) + return "data:,"; + + RetainPtr<CGImageDestinationRef> imageDestination(AdoptCF, CGImageDestinationCreateWithData(transformedImageData.get(), + utiFromMIMEType(mimeType).get(), 1, 0)); + if (!imageDestination) + return "data:,"; + + CGImageDestinationAddImage(imageDestination.get(), transformedImage.get(), 0); + CGImageDestinationFinalize(imageDestination.get()); + + Vector<char> in; + in.append(CFDataGetBytePtr(transformedImageData.get()), CFDataGetLength(transformedImageData.get())); + + Vector<char> out; + base64Encode(in, out); + out.append('\0'); + + return String::format("data:%s;base64,%s", mimeType.utf8().data(), out.data()); } + +} // namespace WebCore diff --git a/WebCore/platform/graphics/cg/ImageBufferData.h b/WebCore/platform/graphics/cg/ImageBufferData.h new file mode 100644 index 0000000..5e6fc4c --- /dev/null +++ b/WebCore/platform/graphics/cg/ImageBufferData.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Google 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. + */ + +#ifndef ImageBufferData_h +#define ImageBufferData_h + +namespace WebCore { + +class IntSize; + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); + + void* m_data; +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/WebCore/platform/graphics/cg/ImageCG.cpp b/WebCore/platform/graphics/cg/ImageCG.cpp index 5958d86..8609c46 100644 --- a/WebCore/platform/graphics/cg/ImageCG.cpp +++ b/WebCore/platform/graphics/cg/ImageCG.cpp @@ -37,7 +37,7 @@ #include "PlatformString.h" #include <ApplicationServices/ApplicationServices.h> -#if PLATFORM(MAC) +#if PLATFORM(MAC) || PLATFORM(CHROMIUM) #include "WebCoreSystemInterface.h" #endif @@ -52,8 +52,9 @@ void FrameData::clear() if (m_frame) { CGImageRelease(m_frame); m_frame = 0; - m_duration = 0.0f; - m_hasAlpha = true; + // NOTE: We purposefully don't reset metadata here, so that even if we + // throw away previously-decoded data, animation loops can still access + // properties like frame durations without re-decoding. } } @@ -61,6 +62,37 @@ void FrameData::clear() // Image Class // ================================================ +BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer) + : Image(observer) + , m_currentFrame(0) + , m_frames(0) + , m_frameTimer(0) + , m_repetitionCount(cAnimationNone) + , m_repetitionCountStatus(Unknown) + , m_repetitionsComplete(0) + , m_isSolidColor(false) + , m_animationFinished(true) + , m_allDataReceived(true) + , m_haveSize(true) + , m_sizeAvailable(true) + , m_decodedSize(0) + , m_haveFrameCount(true) + , m_frameCount(1) +{ + initPlatformData(); + + CGFloat width = CGImageGetWidth(cgImage); + CGFloat height = CGImageGetHeight(cgImage); + m_decodedSize = width * height * 4; + m_size = IntSize(width, height); + + m_frames.grow(1); + m_frames[0].m_frame = cgImage; + m_frames[0].m_hasAlpha = true; + m_frames[0].m_haveMetadata = true; + checkForSolidColor(); +} + // Drawing Routines void BitmapImage::checkForSolidColor() @@ -97,78 +129,73 @@ CGImageRef BitmapImage::getCGImageRef() return frameAtIndex(0); } -void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp) +void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp) { - if (!m_source.initialized()) - return; - - CGRect fr = ctxt->roundToDevicePixels(srcRect); - CGRect ir = ctxt->roundToDevicePixels(dstRect); + startAnimation(); CGImageRef image = frameAtIndex(m_currentFrame); if (!image) // If it's too early we won't have an image yet. return; if (mayFillWithSolidColor()) { - fillWithSolidColor(ctxt, ir, solidColor(), compositeOp); + fillWithSolidColor(ctxt, destRect, solidColor(), compositeOp); return; } - // Get the height (in adjusted, i.e. scaled, coords) of the portion of the image - // that is currently decoded. This could be less that the actual height. - CGSize selfSize = size(); // full image size, in pixels - float curHeight = CGImageGetHeight(image); // height of loaded portion, in pixels - - CGSize adjustedSize = selfSize; - if (curHeight < selfSize.height) { - adjustedSize.height *= curHeight / selfSize.height; - - // Is the amount of available bands less than what we need to draw? If so, - // we may have to clip 'fr' if it goes outside the available bounds. - if (CGRectGetMaxY(fr) > adjustedSize.height) { - float frHeight = adjustedSize.height - fr.origin.y; // clip fr to available bounds - if (frHeight <= 0) - return; // clipped out entirely - ir.size.height *= (frHeight / fr.size.height); // scale ir proportionally to fr - fr.size.height = frHeight; - } - } + float currHeight = CGImageGetHeight(image); + if (currHeight <= srcRect.y()) + return; CGContextRef context = ctxt->platformContext(); ctxt->save(); + bool shouldUseSubimage = false; + + // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image + // and then set a clip to the portion that we want to display. + FloatRect adjustedDestRect = destRect; + FloatSize selfSize = currentFrameSize(); + if (srcRect.size() != selfSize) { + CGInterpolationQuality interpolationQuality = CGContextGetInterpolationQuality(context); + // When the image is scaled using high-quality interpolation, we create a temporary CGImage + // containing only the portion we want to display. We need to do this because high-quality + // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed + // into the destination rect. See <rdar://problem/6112909>. + shouldUseSubimage = (interpolationQuality == kCGInterpolationHigh || interpolationQuality == kCGInterpolationDefault) && srcRect.size() != destRect.size(); + if (shouldUseSubimage) { + image = CGImageCreateWithImageInRect(image, srcRect); + if (currHeight < srcRect.bottom()) { + ASSERT(CGImageGetHeight(image) == currHeight - CGRectIntegral(srcRect).origin.y); + adjustedDestRect.setHeight(destRect.height() / srcRect.height() * CGImageGetHeight(image)); + } + } else { + float xScale = srcRect.width() / destRect.width(); + float yScale = srcRect.height() / destRect.height(); + + adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale)); + adjustedDestRect.setSize(FloatSize(selfSize.width() / xScale, selfSize.height() / yScale)); + + CGContextClipToRect(context, destRect); + } + } + + // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. + if (!shouldUseSubimage && currHeight < selfSize.height()) + adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height()); + // Flip the coords. ctxt->setCompositeOperation(compositeOp); - CGContextTranslateCTM(context, ir.origin.x, ir.origin.y); + CGContextTranslateCTM(context, adjustedDestRect.x(), adjustedDestRect.bottom()); CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -ir.size.height); - - // Translated to origin, now draw at 0,0. - ir.origin.x = ir.origin.y = 0; - - // If we're drawing a sub portion of the image then create - // a image for the sub portion and draw that. - // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html - if (fr.size.width != adjustedSize.width || fr.size.height != adjustedSize.height) { - // Convert ft to image pixel coords: - float xscale = adjustedSize.width / selfSize.width; - float yscale = adjustedSize.height / curHeight; // yes, curHeight, not selfSize.height! - fr.origin.x /= xscale; - fr.origin.y /= yscale; - fr.size.width /= xscale; - fr.size.height /= yscale; - - image = CGImageCreateWithImageInRect(image, fr); - if (image) { - CGContextDrawImage(context, ir, image); - CFRelease(image); - } - } else // Draw the whole image. - CGContextDrawImage(context, ir, image); - + adjustedDestRect.setLocation(FloatPoint()); + + // Draw the image. + CGContextDrawImage(context, adjustedDestRect, image); + + if (shouldUseSubimage) + CGImageRelease(image); + ctxt->restore(); - - startAnimation(); if (imageObserver()) imageObserver()->didDraw(this); @@ -183,6 +210,9 @@ void Image::drawPatternCallback(void* info, CGContextRef context) void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { + if (!nativeImageForCurrentFrame()) + return; + ASSERT(patternTransform.isInvertible()); if (!patternTransform.isInvertible()) // Avoid a hang under CGContextDrawTiledImage on release builds. @@ -192,9 +222,8 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const ctxt->save(); CGContextClipToRect(context, destRect); ctxt->setCompositeOperation(op); - CGContextTranslateCTM(context, destRect.x(), destRect.y()); + CGContextTranslateCTM(context, destRect.x(), destRect.y() + destRect.height()); CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -destRect.height()); // Compute the scaled tile size. float scaledTileHeight = tileRect.height() * narrowPrecisionToFloat(patternTransform.d()); diff --git a/WebCore/platform/graphics/cg/ImageSourceCG.cpp b/WebCore/platform/graphics/cg/ImageSourceCG.cpp index 2bfc204..73907c9 100644 --- a/WebCore/platform/graphics/cg/ImageSourceCG.cpp +++ b/WebCore/platform/graphics/cg/ImageSourceCG.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,15 +25,17 @@ #include "config.h" #include "ImageSource.h" -#include "SharedBuffer.h" #if PLATFORM(CG) #include "IntSize.h" +#include "SharedBuffer.h" #include <ApplicationServices/ApplicationServices.h> namespace WebCore { +static const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32"); + ImageSource::ImageSource() : m_decoder(0) { @@ -52,15 +54,13 @@ void ImageSource::clear() } } -const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32"); - CFDictionaryRef imageSourceOptions() { static CFDictionaryRef options; if (!options) { - const void *keys[2] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 }; - const void *values[2] = { kCFBooleanTrue, kCFBooleanTrue }; + const void* keys[2] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 }; + const void* values[2] = { kCFBooleanTrue, kCFBooleanTrue }; options = CFDictionaryCreate(NULL, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } @@ -108,10 +108,10 @@ bool ImageSource::isSizeAvailable() return result; } -IntSize ImageSource::size() const +IntSize ImageSource::frameSizeAtIndex(size_t index) const { IntSize result; - CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()); + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions()); if (properties) { int w = 0, h = 0; CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); @@ -120,16 +120,23 @@ IntSize ImageSource::size() const num = (CFNumberRef)CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); if (num) CFNumberGetValue(num, kCFNumberIntType, &h); - result = IntSize(w, h); + result = IntSize(w, h); CFRelease(properties); } return result; } +IntSize ImageSource::size() const +{ + return frameSizeAtIndex(0); +} + int ImageSource::repetitionCount() { int result = cAnimationLoopOnce; // No property means loop once. - + if (!initialized()) + return result; + // A property with value 0 means loop forever. CFDictionaryRef properties = CGImageSourceCopyProperties(m_decoder, imageSourceOptions()); if (properties) { @@ -154,7 +161,23 @@ size_t ImageSource::frameCount() const CGImageRef ImageSource::createFrameAtIndex(size_t index) { - return CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions()); + if (!initialized()) + return 0; + + CGImageRef image = CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions()); + CFStringRef imageUTI = CGImageSourceGetType(m_decoder); + static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image"); + if (!imageUTI || !CFEqual(imageUTI, xbmUTI)) + return image; + + // If it is an xbm image, mask out all the white areas to render them transparent. + const CGFloat maskingColors[6] = {255, 255, 255, 255, 255, 255}; + CGImageRef maskedImage = CGImageCreateWithMaskingColors(image, maskingColors); + if (!maskedImage) + return image; + + CGImageRelease(image); + return maskedImage; } bool ImageSource::frameIsCompleteAtIndex(size_t index) @@ -164,6 +187,9 @@ bool ImageSource::frameIsCompleteAtIndex(size_t index) float ImageSource::frameDurationAtIndex(size_t index) { + if (!initialized()) + return 0; + float duration = 0; CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions()); if (properties) { diff --git a/WebCore/platform/graphics/cg/PDFDocumentImage.h b/WebCore/platform/graphics/cg/PDFDocumentImage.h index caddc05..5c9d4e1 100644 --- a/WebCore/platform/graphics/cg/PDFDocumentImage.h +++ b/WebCore/platform/graphics/cg/PDFDocumentImage.h @@ -38,14 +38,25 @@ namespace WebCore { class PDFDocumentImage : public Image { public: - PDFDocumentImage(); + static PassRefPtr<PDFDocumentImage> create() + { + return adoptRef(new PDFDocumentImage); + } ~PDFDocumentImage(); - + + virtual bool hasSingleSecurityOrigin() const { return true; } + virtual bool dataChanged(bool allDataReceived); + // FIXME: PDF Images are underreporting decoded sizes and will be unable + // to prune because these functions are not implemented yet. + virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) { } + virtual unsigned decodedSize() const { return 0; } + virtual IntSize size() const; private: + PDFDocumentImage(); virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); void setCurrentPage(int); diff --git a/WebCore/platform/graphics/cg/PathCG.cpp b/WebCore/platform/graphics/cg/PathCG.cpp index c0a0caf..1382589 100644 --- a/WebCore/platform/graphics/cg/PathCG.cpp +++ b/WebCore/platform/graphics/cg/PathCG.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. - * 2006 Rob Buis <buis@kde.org> + * 2006, 2008 Rob Buis <buis@kde.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -189,17 +189,17 @@ static void CGPathToCFStringApplierFunction(void* info, const CGPathElement *ele CGPoint* points = element->points; switch (element->type) { case kCGPathElementMoveToPoint: - CFStringAppendFormat(string, 0, CFSTR("M%.2f,%.2f"), points[0].x, points[0].y); + CFStringAppendFormat(string, 0, CFSTR("M%.2f,%.2f "), points[0].x, points[0].y); break; case kCGPathElementAddLineToPoint: - CFStringAppendFormat(string, 0, CFSTR("L%.2f,%.2f"), points[0].x, points[0].y); + CFStringAppendFormat(string, 0, CFSTR("L%.2f,%.2f "), points[0].x, points[0].y); break; case kCGPathElementAddQuadCurveToPoint: - CFStringAppendFormat(string, 0, CFSTR("Q%.2f,%.2f,%.2f,%.2f"), + CFStringAppendFormat(string, 0, CFSTR("Q%.2f,%.2f,%.2f,%.2f "), points[0].x, points[0].y, points[1].x, points[1].y); break; case kCGPathElementAddCurveToPoint: - CFStringAppendFormat(string, 0, CFSTR("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f"), + CFStringAppendFormat(string, 0, CFSTR("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f "), points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y); break; @@ -215,6 +215,8 @@ static CFStringRef CFStringFromCGPath(CGPathRef path) CFMutableStringRef string = CFStringCreateMutable(NULL, 0); CGPathApply(path, string, CGPathToCFStringApplierFunction); + CFStringTrimWhitespace(string); + return string; } diff --git a/WebCore/platform/graphics/cg/PatternCG.cpp b/WebCore/platform/graphics/cg/PatternCG.cpp new file mode 100644 index 0000000..e1f7a69 --- /dev/null +++ b/WebCore/platform/graphics/cg/PatternCG.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, 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. + */ + +#include "config.h" +#include "Pattern.h" + +#include "AffineTransform.h" +#include "GraphicsContext.h" + +#include <ApplicationServices/ApplicationServices.h> + +namespace WebCore { + +static void patternCallback(void* info, CGContextRef context) +{ + CGImageRef platformImage = static_cast<Image*>(info)->getCGImageRef(); + if (!platformImage) + return; + + CGRect rect = GraphicsContext(context).roundToDevicePixels( + FloatRect(0, 0, CGImageGetWidth(platformImage), CGImageGetHeight(platformImage))); + CGContextDrawImage(context, rect, platformImage); +} + +static void patternReleaseCallback(void* info) +{ + static_cast<Image*>(info)->deref(); +} + +CGPatternRef Pattern::createPlatformPattern(const AffineTransform& transform) const +{ + IntRect tileRect = tileImage()->rect(); + + AffineTransform patternTransform = transform; + patternTransform.scale(1, -1); + patternTransform.translate(0, -tileRect.height()); + + // If FLT_MAX should also be used for xStep or yStep, nothing is rendered. Using fractions of FLT_MAX also + // result in nothing being rendered. + // INT_MAX is almost correct, but there seems to be some number wrapping occuring making the fill + // pattern is not filled correctly. + // So, just pick a really large number that works. + float xStep = m_repeatX ? tileRect.width() : (100000000.0f); + float yStep = m_repeatY ? tileRect.height() : (100000000.0f); + + // The pattern will release the tile when it's done rendering in patternReleaseCallback + tileImage()->ref(); + + const CGPatternCallbacks patternCallbacks = { 0, patternCallback, patternReleaseCallback }; + return CGPatternCreate(tileImage(), tileRect, patternTransform, xStep, yStep, + kCGPatternTilingConstantSpacing, TRUE, &patternCallbacks); +} + +} |