diff options
Diffstat (limited to 'WebCore/platform/graphics')
190 files changed, 14195 insertions, 2024 deletions
diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index 4b21de0..45b32ab 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -30,22 +30,18 @@ #include "FloatRect.h" #include "ImageObserver.h" #include "IntRect.h" +#include "MIMETypeRegistry.h" #include "PlatformString.h" -#include "SystemTime.h" #include "Timer.h" +#include <wtf/CurrentTime.h> #include <wtf/Vector.h> -#include "MIMETypeRegistry.h" namespace WebCore { -// Animated images >5MB are considered large enough that we'll only hang on to -// one frame at a time. -const unsigned cLargeAnimationCutoff = 5242880; - -// When an animated image is more than five minutes out of date, don't try to -// resync on repaint, so we don't waste CPU cycles on an edge case the user -// doesn't care about. -const double cAnimationResyncCutoff = 5 * 60; +static int frameBytes(const IntSize& frameSize) +{ + return frameSize.width() * frameSize.height() * 4; +} BitmapImage::BitmapImage(ImageObserver* observer) : Image(observer) @@ -75,40 +71,42 @@ BitmapImage::~BitmapImage() stopAnimation(); } -void BitmapImage::destroyDecodedData(bool incremental, bool preserveNearbyFrames) +void BitmapImage::destroyDecodedData(bool destroyAll) { - // Destroy the cached images and release them. - if (m_frames.size()) { - int sizeChange = 0; - int frameSize = m_size.width() * m_size.height() * 4; - const size_t nextFrame = (preserveNearbyFrames && frameCount()) ? ((m_currentFrame + 1) % frameCount()) : 0; - for (unsigned i = incremental ? m_frames.size() - 1 : 0; i < m_frames.size(); i++) { - if (m_frames[i].m_frame && (!preserveNearbyFrames || (i != m_currentFrame && i != nextFrame))) { - sizeChange -= frameSize; - m_frames[i].clear(); - } - } - - // We just always invalidate our platform data, even in the incremental case. - // This could be better, but it's not a big deal. - m_isSolidColor = false; - invalidatePlatformData(); - - if (sizeChange) { - m_decodedSize += sizeChange; - if (imageObserver()) - imageObserver()->decodedSizeChanged(this, sizeChange); - } - - if (!incremental) { - // Reset the image source, since Image I/O has an underlying cache that it uses - // while animating that it seems to never clear. -#if !PLATFORM(SGL) - m_source.clear(); - m_source.setData(m_data.get(), m_allDataReceived); -#endif - } + int framesCleared = 0; + const size_t clearBeforeFrame = destroyAll ? m_frames.size() : m_currentFrame; + for (size_t i = 0; i < clearBeforeFrame; ++i) { + // The underlying frame isn't actually changing (we're just trying to + // save the memory for the framebuffer data), so we don't need to clear + // the metadata. + if (m_frames[i].clear(false)) + ++framesCleared; } + + destroyMetadataAndNotify(framesCleared); + + m_source.clear(destroyAll, clearBeforeFrame, m_data.get(), m_allDataReceived); + return; +} + +void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll) +{ + // Animated images >5MB are considered large enough that we'll only hang on + // to one frame at a time. + static const unsigned cLargeAnimationCutoff = 5242880; + if (frameCount() * frameBytes(m_size) > cLargeAnimationCutoff) + destroyDecodedData(destroyAll); +} + +void BitmapImage::destroyMetadataAndNotify(int framesCleared) +{ + m_isSolidColor = false; + invalidatePlatformData(); + + const int deltaBytes = framesCleared * -frameBytes(m_size); + m_decodedSize += deltaBytes; + if (deltaBytes && imageObserver()) + imageObserver()->decodedSizeChanged(this, deltaBytes); } void BitmapImage::cacheFrame(size_t index) @@ -129,19 +127,14 @@ void BitmapImage::cacheFrame(size_t index) m_frames[index].m_duration = m_source.frameDurationAtIndex(index); m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index); - int sizeChange; - if (index) { - IntSize frameSize = m_source.frameSizeAtIndex(index); - if (frameSize != m_size) - m_hasUniformFrameSize = false; - sizeChange = m_frames[index].m_frame ? frameSize.width() * frameSize.height() * 4 : 0; - } else - sizeChange = m_frames[index].m_frame ? m_size.width() * m_size.height() * 4 : 0; - - if (sizeChange) { - m_decodedSize += sizeChange; + const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size); + if (frameSize != m_size) + m_hasUniformFrameSize = false; + if (m_frames[index].m_frame) { + const int deltaBytes = frameBytes(frameSize); + m_decodedSize += deltaBytes; if (imageObserver()) - imageObserver()->decodedSizeChanged(this, sizeChange); + imageObserver()->decodedSizeChanged(this, deltaBytes); } } @@ -163,7 +156,9 @@ IntSize BitmapImage::currentFrameSize() const bool BitmapImage::dataChanged(bool allDataReceived) { - destroyDecodedData(true); + // Because we're modifying the current frame, clear its (now possibly + // inaccurate) metadata as well. + destroyMetadataAndNotify((!m_frames.isEmpty() && m_frames[m_frames.size() - 1].clear(true)) ? 1 : 0); // Feed all the data we've seen so far to the image decoder. m_allDataReceived = allDataReceived; @@ -179,6 +174,11 @@ bool BitmapImage::dataChanged(bool allDataReceived) return isSizeAvailable(); } +String BitmapImage::filenameExtension() const +{ + return m_source.filenameExtension(); +} + size_t BitmapImage::frameCount() { if (!m_haveFrameCount) { @@ -274,16 +274,18 @@ void BitmapImage::startAnimation(bool catchUpIfNecessary) m_desiredFrameStartTime = time + currentDuration; } else { m_desiredFrameStartTime += currentDuration; - // If we're too far behind, the user probably doesn't care about - // resyncing and we could burn a lot of time looping through frames - // below. Just reset the timings. + + // When an animated image is more than five minutes out of date, the + // user probably doesn't care about resyncing and we could burn a lot of + // time looping through frames below. Just reset the timings. + const double cAnimationResyncCutoff = 5 * 60; if ((time - m_desiredFrameStartTime) > cAnimationResyncCutoff) m_desiredFrameStartTime = time + currentDuration; } // Don't advance the animation to an incomplete frame. size_t nextFrame = (m_currentFrame + 1) % frameCount(); - if (!frameIsCompleteAtIndex(nextFrame)) + if (!m_allDataReceived && !frameIsCompleteAtIndex(nextFrame)) return; // Don't advance past the last frame if we haven't decoded the whole image @@ -369,14 +371,12 @@ void BitmapImage::resetAnimation() m_repetitionsComplete = 0; m_desiredFrameStartTime = 0; m_animationFinished = false; - int frameSize = m_size.width() * m_size.height() * 4; - + // For extremely large animations, when the animation is reset, we just throw everything away. - if (frameCount() * frameSize > cLargeAnimationCutoff) - destroyDecodedData(); + destroyDecodedDataIfNecessary(true); } -void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer) +void BitmapImage::advanceAnimation(Timer<BitmapImage>*) { internalAdvanceAnimation(false); // At this point the image region has been marked dirty, and if it's @@ -394,45 +394,32 @@ bool BitmapImage::internalAdvanceAnimation(bool skippingFrames) if (!skippingFrames && imageObserver()->shouldPauseAnimation(this)) return false; - m_currentFrame++; + ++m_currentFrame; + bool advancedAnimation = true; + bool destroyAll = false; if (m_currentFrame >= frameCount()) { ++m_repetitionsComplete; + // Get the repetition count again. If we weren't able to get a // repetition count before, we should have decoded the whole image by // now, so it should now be available. if (repetitionCount(true) && m_repetitionsComplete >= m_repetitionCount) { m_animationFinished = true; m_desiredFrameStartTime = 0; - m_currentFrame--; - if (skippingFrames) { - // Uh oh. We tried to skip past the end of the animation. We'd - // better draw this last frame. - notifyObserverAndTrimDecodedData(); - } - return false; + --m_currentFrame; + advancedAnimation = false; + } else { + m_currentFrame = 0; + destroyAll = true; } - m_currentFrame = 0; } + destroyDecodedDataIfNecessary(destroyAll); - if (!skippingFrames) - notifyObserverAndTrimDecodedData(); - - return true; -} - -void BitmapImage::notifyObserverAndTrimDecodedData() -{ - // Notify our observer that the animation has advanced. - imageObserver()->animationAdvanced(this); - - // For large animated images, go ahead and throw away frames as we go to - // save footprint. - int frameSize = m_size.width() * m_size.height() * 4; - if (frameCount() * frameSize > cLargeAnimationCutoff) { - // Destroy all of our frames and just redecode every time. We save the - // current frame since we'll need it in draw() anyway. - destroyDecodedData(false, true); - } + // We need to draw this frame if we advanced to it while not skipping, or if + // while trying to skip frames we hit the last frame and thus had to stop. + if (skippingFrames != advancedAnimation) + imageObserver()->animationAdvanced(this); + return advancedAnimation; } } diff --git a/WebCore/platform/graphics/BitmapImage.h b/WebCore/platform/graphics/BitmapImage.h index c5f2a72..110aec4 100644 --- a/WebCore/platform/graphics/BitmapImage.h +++ b/WebCore/platform/graphics/BitmapImage.h @@ -80,10 +80,12 @@ struct FrameData : Noncopyable { ~FrameData() { - clear(); + clear(true); } - void clear(); + // Clear the cached image data on the frame, and (optionally) the metadata. + // Returns whether there was cached image data to clear. + bool clear(bool clearMetadata); NativeImagePtr m_frame; bool m_haveMetadata; @@ -118,6 +120,7 @@ public: IntSize currentFrameSize() const; virtual bool dataChanged(bool allDataReceived); + virtual String filenameExtension() const; // It may look unusual that there is no start animation call as public API. This is because // we start and stop animating lazily. Animation begins whenever someone draws the image. It will @@ -164,7 +167,7 @@ protected: #endif virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); #if PLATFORM(QT) || PLATFORM(WX) - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, + virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); #endif size_t currentFrame() const { return m_currentFrame; } @@ -177,18 +180,22 @@ protected: // Decodes and caches a frame. Never accessed except internally. void cacheFrame(size_t index); - // Called to invalidate all our cached data. If an image is loading - // incrementally, we only invalidate the last cached frame. For large - // animated images, where we throw away the decoded data after every frame, - // |preserveNearbyFrames| can be set to preserve the current frame's data - // and eliminate some unnecessary duplicated decoding work. This also - // preserves the next frame's data, if available. In most cases this has no - // effect; either that frame isn't decoded yet, or it's already been - // destroyed by a previous call. But when we fall behind on the very first - // animation loop and startAnimation() needs to "catch up" one or more - // frames, this briefly preserves some of that decoding work, to ease CPU - // load and make it less likely that we'll keep falling behind. - virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false); + // Called to invalidate cached data. When |destroyAll| is true, we wipe out + // the entire frame buffer cache and tell the image source to destroy + // everything; this is used when e.g. we want to free some room in the image + // cache. If |destroyAll| is false, we only delete frames up to the current + // one; this is used while animating large images to keep memory footprint + // low without redecoding the whole image on every frame. + virtual void destroyDecodedData(bool destroyAll = true); + + // If the image is large enough, calls destroyDecodedData() and passes + // |destroyAll| along. + void destroyDecodedDataIfNecessary(bool destroyAll); + + // Generally called by destroyDecodedData(), destroys whole-image metadata + // and notifies observers that the memory footprint has (hopefully) + // decreased by |framesCleared| times the size (in bytes) of a frame. + void destroyMetadataAndNotify(int framesCleared); // Whether or not size is available yet. bool isSizeAvailable(); @@ -206,9 +213,6 @@ protected: // Returns whether the animation was advanced. bool internalAdvanceAnimation(bool skippingFrames); - // Helper for internalAdvanceAnimation(). - void notifyObserverAndTrimDecodedData(); - // Handle platform-specific data void initPlatformData(); void invalidatePlatformData(); diff --git a/WebCore/platform/graphics/Color.cpp b/WebCore/platform/graphics/Color.cpp index 3ff589d..c7e11ee 100644 --- a/WebCore/platform/graphics/Color.cpp +++ b/WebCore/platform/graphics/Color.cpp @@ -38,8 +38,17 @@ using namespace WTF; namespace WebCore { -const RGBA32 lightenedBlack = 0xFF545454; -const RGBA32 darkenedWhite = 0xFFABABAB; +#if !COMPILER(MSVC) +const RGBA32 Color::black; +const RGBA32 Color::white; +const RGBA32 Color::darkGray; +const RGBA32 Color::gray; +const RGBA32 Color::lightGray; +const RGBA32 Color::transparent; +#endif + +static const RGBA32 lightenedBlack = 0xFF545454; +static const RGBA32 darkenedWhite = 0xFFABABAB; RGBA32 makeRGB(int r, int g, int b) { @@ -51,7 +60,7 @@ RGBA32 makeRGBA(int r, int g, int b, int a) return max(0, min(a, 255)) << 24 | max(0, min(r, 255)) << 16 | max(0, min(g, 255)) << 8 | max(0, min(b, 255)); } -int colorFloatToRGBAByte(float f) +static int colorFloatToRGBAByte(float f) { // We use lroundf and 255 instead of nextafterf(256, 0) to match CG's rounding return max(0, min(static_cast<int>(lroundf(255.0f * f)), 255)); diff --git a/WebCore/platform/graphics/FloatPoint.cpp b/WebCore/platform/graphics/FloatPoint.cpp index 3ca0361..564ea86 100644 --- a/WebCore/platform/graphics/FloatPoint.cpp +++ b/WebCore/platform/graphics/FloatPoint.cpp @@ -27,7 +27,7 @@ #include "config.h" #include "FloatPoint.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatConversion.h" #include "IntPoint.h" @@ -37,7 +37,7 @@ FloatPoint::FloatPoint(const IntPoint& p) : m_x(p.x()), m_y(p.y()) { } -FloatPoint FloatPoint::matrixTransform(const AffineTransform& transform) const +FloatPoint FloatPoint::matrixTransform(const TransformationMatrix& transform) const { double newX, newY; transform.map(static_cast<double>(m_x), static_cast<double>(m_y), &newX, &newY); diff --git a/WebCore/platform/graphics/FloatPoint.h b/WebCore/platform/graphics/FloatPoint.h index 6b3c769..35b3036 100644 --- a/WebCore/platform/graphics/FloatPoint.h +++ b/WebCore/platform/graphics/FloatPoint.h @@ -28,6 +28,8 @@ #define FloatPoint_h #include "FloatSize.h" +#include "IntPoint.h" +#include <wtf/MathExtras.h> #include <wtf/Platform.h> #if PLATFORM(CG) @@ -59,7 +61,7 @@ struct SkPoint; namespace WebCore { -class AffineTransform; +class TransformationMatrix; class IntPoint; class FloatPoint { @@ -102,7 +104,7 @@ public: FloatPoint(const SkPoint&); #endif - FloatPoint matrixTransform(const AffineTransform&) const; + FloatPoint matrixTransform(const TransformationMatrix&) const; private: float m_x, m_y; @@ -146,6 +148,11 @@ inline bool operator!=(const FloatPoint& a, const FloatPoint& b) return a.x() != b.x() || a.y() != b.y(); } +inline IntPoint roundedIntPoint(const FloatPoint& p) +{ + return IntPoint(static_cast<int>(roundf(p.x())), static_cast<int>(roundf(p.y()))); +} + } #endif diff --git a/WebCore/platform/graphics/FloatPoint3D.cpp b/WebCore/platform/graphics/FloatPoint3D.cpp index ec52d40..e3ba422 100644 --- a/WebCore/platform/graphics/FloatPoint3D.cpp +++ b/WebCore/platform/graphics/FloatPoint3D.cpp @@ -23,14 +23,15 @@ #if ENABLE(SVG) #include <math.h> +#include "FloatPoint.h" #include "FloatPoint3D.h" namespace WebCore { FloatPoint3D::FloatPoint3D() - : m_x(0.f) - , m_y(0.f) - , m_z(0.f) + : m_x(0) + , m_y(0) + , m_z(0) { } @@ -41,43 +42,22 @@ FloatPoint3D::FloatPoint3D(float x, float y, float z) { } -float FloatPoint3D::x() const +FloatPoint3D::FloatPoint3D(const FloatPoint& p) + : m_x(p.x()) + , m_y(p.y()) + , m_z(0) { - return m_x; -} - -void FloatPoint3D::setX(float x) -{ - m_x = x; -} - -float FloatPoint3D::y() const -{ - return m_y; -} - -void FloatPoint3D::setY(float y) -{ - m_y = y; -} - -float FloatPoint3D::z() const -{ - return m_z; -} - -void FloatPoint3D::setZ(float z) -{ - m_z = z; } void FloatPoint3D::normalize() { float length = sqrtf(m_x * m_x + m_y * m_y + m_z * m_z); - m_x /= length; - m_y /= length; - m_z /= length; + if (length != 0) { + m_x /= length; + m_y /= length; + m_z /= length; + } } } // namespace WebCore diff --git a/WebCore/platform/graphics/FloatPoint3D.h b/WebCore/platform/graphics/FloatPoint3D.h index 55f70e7..184e914 100644 --- a/WebCore/platform/graphics/FloatPoint3D.h +++ b/WebCore/platform/graphics/FloatPoint3D.h @@ -26,19 +26,22 @@ namespace WebCore { +class FloatPoint; + class FloatPoint3D { public: FloatPoint3D(); FloatPoint3D(float x, float y, float z); + FloatPoint3D(const FloatPoint&); - float x() const; - void setX(float x); + float x() const { return m_x; } + void setX(float x) { m_x = x; } - float y() const; - void setY(float y); + float y() const { return m_y; } + void setY(float y) { m_y = y; } - float z() const; - void setZ(float z); + float z() const { return m_z; } + void setZ(float z) { m_z = z; } void normalize(); diff --git a/WebCore/platform/graphics/FloatQuad.cpp b/WebCore/platform/graphics/FloatQuad.cpp new file mode 100644 index 0000000..a32d8ab --- /dev/null +++ b/WebCore/platform/graphics/FloatQuad.cpp @@ -0,0 +1,60 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "FloatQuad.h" + +#include <algorithm> + +using std::max; +using std::min; + +namespace WebCore { + +static inline float min4(float a, float b, float c, float d) +{ + return min(min(a, b), min(c, d)); +} + +static inline float max4(float a, float b, float c, float d) +{ + return max(max(a, b), max(c, d)); +} + +FloatRect FloatQuad::boundingBox() const +{ + float left = min4(m_p1.x(), m_p2.x(), m_p3.x(), m_p4.x()); + float top = min4(m_p1.y(), m_p2.y(), m_p3.y(), m_p4.y()); + + float right = max4(m_p1.x(), m_p2.x(), m_p3.x(), m_p4.x()); + float bottom = max4(m_p1.y(), m_p2.y(), m_p3.y(), m_p4.y()); + + return FloatRect(left, top, right - left, bottom - top); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/FloatQuad.h b/WebCore/platform/graphics/FloatQuad.h new file mode 100644 index 0000000..e05b27d --- /dev/null +++ b/WebCore/platform/graphics/FloatQuad.h @@ -0,0 +1,138 @@ +/* + * 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 + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 FloatQuad_h +#define FloatQuad_h + +#include "FloatPoint.h" +#include "FloatRect.h" +#include "IntRect.h" + +namespace WebCore { + +// A FloatQuad is a collection of 4 points, often representing the result of +// mapping a rectangle through transforms. When initialized from a rect, the +// points are in clockwise order from top left. +class FloatQuad { +public: + FloatQuad() + { + } + + FloatQuad(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3, const FloatPoint& p4) + : m_p1(p1) + , m_p2(p2) + , m_p3(p3) + , m_p4(p4) + { + } + + FloatQuad(const FloatRect& inRect) + : m_p1(inRect.location()) + , m_p2(inRect.right(), inRect.y()) + , m_p3(inRect.right(), inRect.bottom()) + , m_p4(inRect.x(), inRect.bottom()) + { + } + + FloatPoint p1() const { return m_p1; } + FloatPoint p2() const { return m_p2; } + FloatPoint p3() const { return m_p3; } + FloatPoint p4() const { return m_p4; } + + void setP1(const FloatPoint& p) { m_p1 = p; } + void setP2(const FloatPoint& p) { m_p2 = p; } + void setP3(const FloatPoint& p) { m_p3 = p; } + void setP4(const FloatPoint& p) { m_p4 = p; } + + // isEmpty tests that the bounding box is empty. This will not identify + // "slanted" empty quads. + bool isEmpty() const { return boundingBox().isEmpty(); } + + FloatRect boundingBox() const; + IntRect enclosingBoundingBox() const + { + return enclosingIntRect(boundingBox()); + } + + void move(const FloatSize& offset) + { + m_p1 += offset; + m_p2 += offset; + m_p3 += offset; + m_p4 += offset; + } + + void move(float dx, float dy) + { + m_p1.move(dx, dy); + m_p2.move(dx, dy); + m_p3.move(dx, dy); + m_p4.move(dx, dy); + } + +private: + FloatPoint m_p1; + FloatPoint m_p2; + FloatPoint m_p3; + FloatPoint m_p4; +}; + +inline FloatQuad& operator+=(FloatQuad& a, const FloatSize& b) +{ + a.move(b); + return a; +} + +inline FloatQuad& operator-=(FloatQuad& a, const FloatSize& b) +{ + a.move(-b.width(), -b.height()); + return a; +} + +inline bool operator==(const FloatQuad& a, const FloatQuad& b) +{ + return a.p1() == b.p1() && + a.p2() == b.p2() && + a.p3() == b.p3() && + a.p4() == b.p4(); +} + +inline bool operator!=(const FloatQuad& a, const FloatQuad& b) +{ + return a.p1() != b.p1() || + a.p2() != b.p2() || + a.p3() != b.p3() || + a.p4() != b.p4(); +} + +} // namespace WebCore + + +#endif // FloatQuad_h + diff --git a/WebCore/platform/graphics/FloatRect.cpp b/WebCore/platform/graphics/FloatRect.cpp index ec7b3fa..532f719 100644 --- a/WebCore/platform/graphics/FloatRect.cpp +++ b/WebCore/platform/graphics/FloatRect.cpp @@ -119,4 +119,16 @@ IntRect enclosingIntRect(const FloatRect& rect) return IntRect(l, t, r - l, b - t); } +FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect) +{ + if (srcRect.width() == 0 || srcRect.height() == 0) + return FloatRect(); + + float widthScale = destRect.width() / srcRect.width(); + float heightScale = destRect.height() / srcRect.height(); + return FloatRect(destRect.x() + (r.x() - srcRect.x()) * widthScale, + destRect.y() + (r.y() - srcRect.y()) * heightScale, + r.width() * widthScale, r.height() * heightScale); +} + } diff --git a/WebCore/platform/graphics/FloatRect.h b/WebCore/platform/graphics/FloatRect.h index 11e3791..a87c949 100644 --- a/WebCore/platform/graphics/FloatRect.h +++ b/WebCore/platform/graphics/FloatRect.h @@ -167,6 +167,7 @@ inline FloatRect unionRect(const FloatRect& a, const FloatRect& b) return c; } + inline bool operator==(const FloatRect& a, const FloatRect& b) { return a.location() == b.location() && a.size() == b.size(); @@ -179,6 +180,9 @@ inline bool operator!=(const FloatRect& a, const FloatRect& b) IntRect enclosingIntRect(const FloatRect&); +// Map rect r from srcRect to an equivalent rect in destRect. +FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect); + } #endif diff --git a/WebCore/platform/graphics/FloatSize.h b/WebCore/platform/graphics/FloatSize.h index cf1e1c5..6e792b6 100644 --- a/WebCore/platform/graphics/FloatSize.h +++ b/WebCore/platform/graphics/FloatSize.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2005 Nokia. All rights reserved. + * 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 @@ -67,6 +68,12 @@ public: m_height > other.m_height ? m_height : other.m_height); } + FloatSize shrunkTo(const FloatSize& other) const + { + return FloatSize(m_width < other.m_width ? m_width : other.m_width, + m_height < other.m_height ? m_height : other.m_height); + } + #if PLATFORM(CG) explicit FloatSize(const CGSize&); // don't do this implicitly since it's lossy operator CGSize() const; diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp index 138e322..f8bec82 100644 --- a/WebCore/platform/graphics/Font.cpp +++ b/WebCore/platform/graphics/Font.cpp @@ -38,6 +38,7 @@ using namespace Unicode; namespace WebCore { +#if USE(FONT_FAST_PATH) const uint8_t Font::gRoundingHackCharacterTable[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/, @@ -50,6 +51,7 @@ const uint8_t Font::gRoundingHackCharacterTable[256] = { }; Font::CodePath Font::s_codePath = Auto; +#endif // ============================================================================================ // Font Implementation (Cross-Platform Portion) @@ -133,132 +135,6 @@ bool Font::operator==(const Font& other) const && (m_fontList ? m_fontList->generation() : 0) == (other.m_fontList ? other.m_fontList->generation() : 0); } -const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCaps) const -{ - bool useSmallCapsFont = forceSmallCaps; - if (m_fontDescription.smallCaps()) { - UChar32 upperC = Unicode::toUpper(c); - if (upperC != c) { - c = upperC; - useSmallCapsFont = true; - } - } - - if (mirror) - c = mirroredChar(c); - - unsigned pageNumber = (c / GlyphPage::size); - - GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero; - if (!node) { - node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); - if (pageNumber) - m_pages.set(pageNumber, node); - else - m_pageZero = node; - } - - GlyphPage* page; - if (!useSmallCapsFont) { - // Fastest loop, for the common case (not small caps). - while (true) { - page = node->page(); - if (page) { - const GlyphData& data = page->glyphDataForCharacter(c); - if (data.fontData) - return data; - if (node->isSystemFallback()) - break; - } - - // Proceed with the fallback list. - node = node->getChild(fontDataAt(node->level()), pageNumber); - if (pageNumber) - m_pages.set(pageNumber, node); - else - m_pageZero = node; - } - } else { - while (true) { - page = node->page(); - if (page) { - const GlyphData& data = page->glyphDataForCharacter(c); - if (data.fontData) { - // The smallCapsFontData function should not normally return 0. - // But if it does, we will just render the capital letter big. - const SimpleFontData* smallCapsFontData = data.fontData->smallCapsFontData(m_fontDescription); - if (!smallCapsFontData) - return data; - - GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber); - const GlyphPage* smallCapsPage = smallCapsNode->page(); - if (smallCapsPage) { - const GlyphData& data = smallCapsPage->glyphDataForCharacter(c); - if (data.fontData) - return data; - } - - // Do not attempt system fallback off the smallCapsFontData. This is the very unlikely case that - // a font has the lowercase character but the small caps font does not have its uppercase version. - return smallCapsFontData->missingGlyphData(); - } - - if (node->isSystemFallback()) - break; - } - - // Proceed with the fallback list. - node = node->getChild(fontDataAt(node->level()), pageNumber); - if (pageNumber) - m_pages.set(pageNumber, node); - else - m_pageZero = node; - } - } - - ASSERT(page); - ASSERT(node->isSystemFallback()); - - // System fallback is character-dependent. When we get here, we - // know that the character in question isn't in the system fallback - // font's glyph page. Try to lazily create it here. - UChar codeUnits[2]; - int codeUnitsLength; - if (c <= 0xFFFF) { - UChar c16 = c; - if (Font::treatAsSpace(c16)) - codeUnits[0] = ' '; - else if (Font::treatAsZeroWidthSpace(c16)) - codeUnits[0] = zeroWidthSpace; - else - codeUnits[0] = c16; - codeUnitsLength = 1; - } else { - codeUnits[0] = U16_LEAD(c); - codeUnits[1] = U16_TRAIL(c); - codeUnitsLength = 2; - } - const SimpleFontData* characterFontData = FontCache::getFontDataForCharacters(*this, codeUnits, codeUnitsLength); - if (useSmallCapsFont) - characterFontData = characterFontData->smallCapsFontData(m_fontDescription); - if (characterFontData) { - // Got the fallback glyph and font. - GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page(); - const GlyphData& data = fallbackPage && fallbackPage->glyphDataForCharacter(c).fontData ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); - // Cache it so we don't have to do system fallback again next time. - if (!useSmallCapsFont) - page->setGlyphDataForCharacter(c, data.glyph, data.fontData); - return data; - } - - // Even system fallback can fail; use the missing glyph in that case. - // FIXME: It would be nicer to use the missing glyph from the last resort font instead. - const GlyphData& data = primaryFont()->missingGlyphData(); - if (!useSmallCapsFont) - page->setGlyphDataForCharacter(c, data.glyph, data.fontData); - return data; -} - void Font::cachePrimaryFont() const { ASSERT(m_fontList); @@ -293,184 +169,12 @@ void Font::update(PassRefPtr<FontSelector> fontSelector) const m_pages.clear(); } -int Font::width(const TextRun& run) const -{ - return lroundf(floatWidth(run)); -} - -int Font::ascent() const -{ - return primaryFont()->ascent(); -} - -int Font::descent() const -{ - return primaryFont()->descent(); -} - -int Font::lineSpacing() const -{ - return primaryFont()->lineSpacing(); -} - -int Font::lineGap() const -{ - return primaryFont()->lineGap(); -} - -float Font::xHeight() const -{ - return primaryFont()->xHeight(); -} - -unsigned Font::unitsPerEm() const -{ - return primaryFont()->unitsPerEm(); -} - -int Font::spaceWidth() const -{ - return (int)ceilf(primaryFont()->m_adjustedSpaceWidth + m_letterSpacing); -} - bool Font::isFixedPitch() const { ASSERT(m_fontList); return m_fontList->isFixedPitch(this); } -void Font::setCodePath(CodePath p) -{ - s_codePath = p; -} - -Font::CodePath Font::codePath() -{ - return s_codePath; -} - -bool Font::canUseGlyphCache(const TextRun& run) const -{ - switch (s_codePath) { - case Auto: - break; - case Simple: - return true; - case Complex: - return false; - } - - // Start from 0 since drawing and highlighting also measure the characters before run->from - for (int i = 0; i < run.length(); i++) { - const UChar c = run[i]; - if (c < 0x300) // U+0300 through U+036F Combining diacritical marks - continue; - if (c <= 0x36F) - return false; - - if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha - continue; - if (c <= 0x05CF) - return false; - - if (c < 0x0600) // U+0600 through U+1059 Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar - continue; - if (c <= 0x1059) - return false; - - if (c < 0x1100) // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; Modern Korean will be precomposed as a result of step A) - continue; - if (c <= 0x11FF) - return false; - - if (c < 0x1780) // U+1780 through U+18AF Khmer, Mongolian - continue; - if (c <= 0x18AF) - return false; - - if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0) - continue; - if (c <= 0x194F) - return false; - - if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols - continue; - if (c <= 0x20FF) - return false; - - if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks - continue; - if (c <= 0xFE2F) - return false; - } - - return true; - -} - -void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const -{ - // This glyph buffer holds our glyphs+advances+font data for each glyph. - GlyphBuffer glyphBuffer; - - float startX = point.x(); - WidthIterator it(this, run); - it.advance(from); - float beforeWidth = it.m_runWidthSoFar; - it.advance(to, &glyphBuffer); - - // We couldn't generate any glyphs for the run. Give up. - if (glyphBuffer.isEmpty()) - return; - - float afterWidth = it.m_runWidthSoFar; - - if (run.rtl()) { - float finalRoundingWidth = it.m_finalRoundingWidth; - it.advance(run.length()); - startX += finalRoundingWidth + it.m_runWidthSoFar - afterWidth; - } else - startX += beforeWidth; - - // Swap the order of the glyphs if right-to-left. - if (run.rtl()) - for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) - glyphBuffer.swap(i, end); - - // Calculate the starting point of the glyphs to be displayed by adding - // all the advances up to the first glyph. - FloatPoint startPoint(startX, point.y()); - drawGlyphBuffer(context, glyphBuffer, run, startPoint); -} - -void Font::drawGlyphBuffer(GraphicsContext* context, const GlyphBuffer& glyphBuffer, - const TextRun& run, const FloatPoint& point) const -{ - // Draw each contiguous run of glyphs that use the same font data. - const SimpleFontData* fontData = glyphBuffer.fontDataAt(0); - FloatSize offset = glyphBuffer.offsetAt(0); - FloatPoint startPoint(point); - float nextX = startPoint.x(); - int lastFrom = 0; - int nextGlyph = 0; - while (nextGlyph < glyphBuffer.size()) { - const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph); - FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph); - if (nextFontData != fontData || nextOffset != offset) { - drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); - - lastFrom = nextGlyph; - fontData = nextFontData; - offset = nextOffset; - startPoint.setX(nextX); - } - nextX += glyphBuffer.advanceAt(nextGlyph); - nextGlyph++; - } - - drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); -} - void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { // Don't draw anything while we are using custom fonts that are in the process of loading. @@ -486,10 +190,12 @@ void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoi } #endif +#if USE(FONT_FAST_PATH) if (canUseGlyphCache(run)) - drawSimpleText(context, run, point, from, to); - else - drawComplexText(context, run, point, from, to); + return drawSimpleText(context, run, point, from, to); +#endif + + return drawComplexText(context, run, point, from, to); } float Font::floatWidth(const TextRun& run) const @@ -499,8 +205,11 @@ float Font::floatWidth(const TextRun& run) const return floatWidthUsingSVGFont(run); #endif +#if USE(FONT_FAST_PATH) if (canUseGlyphCache(run)) return floatWidthForSimpleText(run, 0); +#endif + return floatWidthForComplexText(run); } @@ -513,16 +222,13 @@ float Font::floatWidth(const TextRun& run, int extraCharsAvailable, int& charsCo charsConsumed = run.length(); glyphName = ""; + +#if USE(FONT_FAST_PATH) if (canUseGlyphCache(run)) return floatWidthForSimpleText(run, 0); - return floatWidthForComplexText(run); -} +#endif -float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer) const -{ - WidthIterator it(this, run); - it.advance(run.length(), glyphBuffer); - return it.m_runWidthSoFar; + return floatWidthForComplexText(run); } FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& point, int h, int from, int to) const @@ -533,27 +239,13 @@ FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& point, #endif to = (to == -1 ? run.length() : to); + +#if USE(FONT_FAST_PATH) if (canUseGlyphCache(run)) return selectionRectForSimpleText(run, point, h, from, to); - return selectionRectForComplexText(run, point, h, from, to); -} +#endif -FloatRect Font::selectionRectForSimpleText(const TextRun& run, const IntPoint& point, int h, int from, int to) const -{ - WidthIterator it(this, run); - it.advance(from); - float beforeWidth = it.m_runWidthSoFar; - it.advance(to); - float afterWidth = it.m_runWidthSoFar; - - // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning - if (run.rtl()) { - it.advance(run.length()); - float totalWidth = it.m_runWidthSoFar; - return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); - } else { - return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); - } + return selectionRectForComplexText(run, point, h, from, to); } int Font::offsetForPosition(const TextRun& run, int x, bool includePartialGlyphs) const @@ -563,52 +255,12 @@ int Font::offsetForPosition(const TextRun& run, int x, bool includePartialGlyphs return offsetForPositionForTextUsingSVGFont(run, x, includePartialGlyphs); #endif +#if USE(FONT_FAST_PATH) if (canUseGlyphCache(run)) return offsetForPositionForSimpleText(run, x, includePartialGlyphs); - return offsetForPositionForComplexText(run, x, includePartialGlyphs); -} - -int Font::offsetForPositionForSimpleText(const TextRun& run, int x, bool includePartialGlyphs) const -{ - float delta = (float)x; - - WidthIterator it(this, run); - GlyphBuffer localGlyphBuffer; - unsigned offset; - if (run.rtl()) { - delta -= floatWidthForSimpleText(run, 0); - while (1) { - offset = it.m_currentCharacter; - float w; - if (!it.advanceOneCharacter(w, &localGlyphBuffer)) - break; - delta += w; - if (includePartialGlyphs) { - if (delta - w / 2 >= 0) - break; - } else { - if (delta >= 0) - break; - } - } - } else { - while (1) { - offset = it.m_currentCharacter; - float w; - if (!it.advanceOneCharacter(w, &localGlyphBuffer)) - break; - delta -= w; - if (includePartialGlyphs) { - if (delta + w / 2 <= 0) - break; - } else { - if (delta <= 0) - break; - } - } - } +#endif - return offset; + return offsetForPositionForComplexText(run, x, includePartialGlyphs); } #if ENABLE(SVG_FONTS) diff --git a/WebCore/platform/graphics/Font.h b/WebCore/platform/graphics/Font.h index a99ce12..1bfee8f 100644 --- a/WebCore/platform/graphics/Font.h +++ b/WebCore/platform/graphics/Font.h @@ -3,6 +3,7 @@ * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) * Copyright (C) 2003, 2006, 2007 Apple Computer, Inc. + * Copyright (C) 2008 Holger Hans Peter Freyther * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,11 +27,12 @@ #include "TextRun.h" #include "FontDescription.h" +#include "SimpleFontData.h" #include <wtf/HashMap.h> +#include <wtf/MathExtras.h> #if PLATFORM(QT) -#include <QtGui/qfont.h> -#include <QtGui/qfontmetrics.h> +#include <QFont> #endif namespace WebCore { @@ -45,18 +47,18 @@ class GlyphBuffer; class GlyphPageTreeNode; class GraphicsContext; class IntPoint; -class SimpleFontData; class SVGFontElement; struct GlyphData; +const unsigned defaultUnitsPerEm = 1000; + class Font { public: Font(); Font(const FontDescription&, short letterSpacing, short wordSpacing); -#if !PLATFORM(QT) - Font(const FontPlatformData&, bool isPrinting); // This constructor is only used if the platform wants to start with a native font. -#endif + // This constructor is only used if the platform wants to start with a native font. + Font(const FontPlatformData&, bool isPrinting); ~Font(); Font(const Font&); @@ -76,7 +78,7 @@ public: void drawText(GraphicsContext*, const TextRun&, const FloatPoint&, int from = 0, int to = -1) const; - int width(const TextRun&) const; + int width(const TextRun& run) const { return lroundf(floatWidth(run)); } float floatWidth(const TextRun&) const; float floatWidth(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const; @@ -87,13 +89,8 @@ public: short wordSpacing() const { return m_wordSpacing; } short letterSpacing() const { return m_letterSpacing; } -#if !PLATFORM(QT) void setWordSpacing(short s) { m_wordSpacing = s; } void setLetterSpacing(short s) { m_letterSpacing = s; } -#else - void setWordSpacing(short s); - void setLetterSpacing(short s); -#endif bool isFixedPitch() const; bool isPrinterFont() const { return m_fontDescription.usePrinterFont(); } @@ -105,27 +102,19 @@ public: bool italic() const { return m_fontDescription.italic(); } FontWeight weight() const { return m_fontDescription.weight(); } -#if !PLATFORM(QT) bool isPlatformFont() const { return m_isPlatformFont; } -#endif - -#if PLATFORM(QT) - inline const QFont &font() const { return m_font; } - inline const QFont &scFont() const { return m_scFont; } -#endif // Metrics that we query the FontFallbackList for. - int ascent() const; - int descent() const; + int ascent() const { return primaryFont()->ascent(); } + int descent() const { return primaryFont()->descent(); } int height() const { return ascent() + descent(); } - int lineSpacing() const; - int lineGap() const; - float xHeight() const; - unsigned unitsPerEm() const; - int spaceWidth() const; + int lineSpacing() const { return primaryFont()->lineSpacing(); } + int lineGap() const { return primaryFont()->lineGap(); } + float xHeight() const { return primaryFont()->xHeight(); } + unsigned unitsPerEm() const { return primaryFont()->unitsPerEm(); } + int spaceWidth() const { return (int)ceilf(primaryFont()->m_adjustedSpaceWidth + m_letterSpacing); } int tabWidth() const { return 8 * spaceWidth(); } -#if !PLATFORM(QT) const SimpleFontData* primaryFont() const { if (!m_cachedPrimaryFont) cachePrimaryFont(); @@ -137,9 +126,11 @@ public: // Used for complex text, and does not utilize the glyph map cache. const FontData* fontDataForCharacters(const UChar*, int length) const; +#if PLATFORM(QT) + QFont font() const; +#endif + private: - bool canUseGlyphCache(const TextRun&) const; - void drawSimpleText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const; #if ENABLE(SVG_FONTS) void drawTextUsingSVGFont(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const; float floatWidthUsingSVGFont(const TextRun&) const; @@ -147,24 +138,28 @@ private: FloatRect selectionRectForTextUsingSVGFont(const TextRun&, const IntPoint&, int h, int from, int to) const; int offsetForPositionForTextUsingSVGFont(const TextRun&, int position, bool includePartialGlyphs) const; #endif + +#if USE(FONT_FAST_PATH) + bool canUseGlyphCache(const TextRun&) const; + void drawSimpleText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const; void drawGlyphs(GraphicsContext*, const SimpleFontData*, const GlyphBuffer&, int from, int to, const FloatPoint&) const; void drawGlyphBuffer(GraphicsContext*, const GlyphBuffer&, const TextRun&, const FloatPoint&) const; - void drawComplexText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const; float floatWidthForSimpleText(const TextRun&, GlyphBuffer*) const; - float floatWidthForComplexText(const TextRun&) const; int offsetForPositionForSimpleText(const TextRun&, int position, bool includePartialGlyphs) const; - int offsetForPositionForComplexText(const TextRun&, int position, bool includePartialGlyphs) const; FloatRect selectionRectForSimpleText(const TextRun&, const IntPoint&, int h, int from, int to) const; +#endif + + void drawComplexText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const; + float floatWidthForComplexText(const TextRun&) const; + int offsetForPositionForComplexText(const TextRun&, int position, bool includePartialGlyphs) const; FloatRect selectionRectForComplexText(const TextRun&, const IntPoint&, int h, int from, int to) const; void cachePrimaryFont() const; -#endif + friend struct WidthIterator; public: -#if PLATFORM(QT) - FontSelector* fontSelector() const { return 0; } -#else // Useful for debugging the different font rendering code paths. +#if USE(FONT_FAST_PATH) enum CodePath { Auto, Simple, Complex }; static void setCodePath(CodePath); static CodePath codePath(); @@ -175,9 +170,9 @@ public: { return (((c & ~0xFF) == 0 && gRoundingHackCharacterTable[c])); } +#endif FontSelector* fontSelector() const; -#endif static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; } static bool treatAsZeroWidthSpace(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e) || c == 0xFFFC; } @@ -188,21 +183,13 @@ public: private: FontDescription m_fontDescription; -#if !PLATFORM(QT) mutable RefPtr<FontFallbackList> m_fontList; mutable HashMap<int, GlyphPageTreeNode*> m_pages; mutable GlyphPageTreeNode* m_pageZero; mutable const SimpleFontData* m_cachedPrimaryFont; -#endif short m_letterSpacing; short m_wordSpacing; -#if !PLATFORM(QT) bool m_isPlatformFont; -#else - QFont m_font; - QFont m_scFont; - int m_spaceWidth; -#endif }; } diff --git a/WebCore/platform/graphics/FontCache.cpp b/WebCore/platform/graphics/FontCache.cpp index 1c5a987..2d219be 100644 --- a/WebCore/platform/graphics/FontCache.cpp +++ b/WebCore/platform/graphics/FontCache.cpp @@ -37,11 +37,22 @@ #include "StringHash.h" #include <wtf/HashMap.h> #include <wtf/ListHashSet.h> +#include <wtf/StdLibExtras.h> using namespace WTF; namespace WebCore { +FontCache* fontCache() +{ + DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); + return &globalFontCache; +} + +FontCache::FontCache() +{ +} + struct FontPlatformDataCacheKey { FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false, bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode) @@ -104,7 +115,7 @@ struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataC static const bool emptyValueIsZero = true; static const FontPlatformDataCacheKey& emptyValue() { - static FontPlatformDataCacheKey key(nullAtom); + DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (nullAtom)); return key; } static void constructDeletedValue(FontPlatformDataCacheKey& slot) @@ -124,21 +135,24 @@ static FontPlatformDataCache* gFontPlatformDataCache = 0; static const AtomicString& alternateFamilyName(const AtomicString& familyName) { // Alias Courier <-> Courier New - static AtomicString courier("Courier"), courierNew("Courier New"); + DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier")); + DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New")); if (equalIgnoringCase(familyName, courier)) return courierNew; if (equalIgnoringCase(familyName, courierNew)) return courier; // Alias Times and Times New Roman. - static AtomicString times("Times"), timesNewRoman("Times New Roman"); + DEFINE_STATIC_LOCAL(AtomicString, times, ("Times")); + DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman")); if (equalIgnoringCase(familyName, times)) return timesNewRoman; if (equalIgnoringCase(familyName, timesNewRoman)) return times; // Alias Arial and Helvetica - static AtomicString arial("Arial"), helvetica("Helvetica"); + DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial")); + DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica")); if (equalIgnoringCase(familyName, arial)) return helvetica; if (equalIgnoringCase(familyName, helvetica)) @@ -202,7 +216,7 @@ struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { static const bool needsDestruction = true; static const FontPlatformData& emptyValue() { - static FontPlatformData key; + DEFINE_STATIC_LOCAL(FontPlatformData, key, ()); return key; } static void constructDeletedValue(FontPlatformData& slot) diff --git a/WebCore/platform/graphics/FontCache.h b/WebCore/platform/graphics/FontCache.h index 816fe64..8820045 100644 --- a/WebCore/platform/graphics/FontCache.h +++ b/WebCore/platform/graphics/FontCache.h @@ -51,45 +51,51 @@ class SimpleFontData; class FontCache { public: - static const FontData* getFontData(const Font&, int& familyIndex, FontSelector*); - static void releaseFontData(const SimpleFontData*); + friend FontCache* fontCache(); + + const FontData* getFontData(const Font&, int& familyIndex, FontSelector*); + void releaseFontData(const SimpleFontData*); // This method is implemented by the platform. // FIXME: Font data returned by this method never go inactive because callers don't track and release them. - static const SimpleFontData* getFontDataForCharacters(const Font&, const UChar* characters, int length); + const SimpleFontData* getFontDataForCharacters(const Font&, const UChar* characters, int length); // Also implemented by the platform. - static void platformInit(); + void platformInit(); #if PLATFORM(WIN) - static IMLangFontLink2* getFontLinkInterface(); + IMLangFontLink2* getFontLinkInterface(); #endif - static void getTraitsInFamily(const AtomicString&, Vector<unsigned>&); + void getTraitsInFamily(const AtomicString&, Vector<unsigned>&); - static FontPlatformData* getCachedFontPlatformData(const FontDescription&, const AtomicString& family, bool checkingAlternateName = false); - static SimpleFontData* getCachedFontData(const FontPlatformData*); - static FontPlatformData* getLastResortFallbackFont(const FontDescription&); + FontPlatformData* getCachedFontPlatformData(const FontDescription&, const AtomicString& family, bool checkingAlternateName = false); + SimpleFontData* getCachedFontData(const FontPlatformData*); + FontPlatformData* getLastResortFallbackFont(const FontDescription&); - static void addClient(FontSelector*); - static void removeClient(FontSelector*); + void addClient(FontSelector*); + void removeClient(FontSelector*); - static unsigned generation(); - static void invalidate(); + unsigned generation(); + void invalidate(); - static size_t fontDataCount(); - static size_t inactiveFontDataCount(); - static void purgeInactiveFontData(int count = INT_MAX); + size_t fontDataCount(); + size_t inactiveFontDataCount(); + void purgeInactiveFontData(int count = INT_MAX); private: + FontCache(); + // These methods are implemented by each platform. - static FontPlatformData* getSimilarFontPlatformData(const Font&); - static FontPlatformData* createFontPlatformData(const FontDescription&, const AtomicString& family); + FontPlatformData* getSimilarFontPlatformData(const Font&); + FontPlatformData* createFontPlatformData(const FontDescription&, const AtomicString& family); friend class SimpleFontData; friend class FontFallbackList; }; +// Get the global fontCache. +FontCache* fontCache(); } #endif diff --git a/WebCore/platform/graphics/FontFallbackList.cpp b/WebCore/platform/graphics/FontFallbackList.cpp index ef59c2f..06d52d7 100644 --- a/WebCore/platform/graphics/FontFallbackList.cpp +++ b/WebCore/platform/graphics/FontFallbackList.cpp @@ -40,7 +40,7 @@ FontFallbackList::FontFallbackList() , m_pitch(UnknownPitch) , m_loadingCustomFonts(false) , m_fontSelector(0) - , m_generation(FontCache::generation()) + , m_generation(fontCache()->generation()) { } @@ -52,7 +52,7 @@ void FontFallbackList::invalidate(PassRefPtr<FontSelector> fontSelector) m_pitch = UnknownPitch; m_loadingCustomFonts = false; m_fontSelector = fontSelector; - m_generation = FontCache::generation(); + m_generation = fontCache()->generation(); } void FontFallbackList::releaseFontData() @@ -61,7 +61,7 @@ void FontFallbackList::releaseFontData() for (unsigned i = 0; i < numFonts; ++i) { if (!m_fontList[i].second) { ASSERT(!m_fontList[i].first->isSegmented()); - FontCache::releaseFontData(static_cast<const SimpleFontData*>(m_fontList[i].first)); + fontCache()->releaseFontData(static_cast<const SimpleFontData*>(m_fontList[i].first)); } } } @@ -96,8 +96,8 @@ const FontData* FontFallbackList::fontDataAt(const Font* font, unsigned realized // We are obtaining this font for the first time. We keep track of the families we've looked at before // in |m_familyIndex|, so that we never scan the same spot in the list twice. getFontData will adjust our // |m_familyIndex| as it scans for the right font to make. - ASSERT(FontCache::generation() == m_generation); - const FontData* result = FontCache::getFontData(*font, m_familyIndex, m_fontSelector.get()); + ASSERT(fontCache()->generation() == m_generation); + const FontData* result = fontCache()->getFontData(*font, m_familyIndex, m_fontSelector.get()); if (result) { m_fontList.append(pair<const FontData*, bool>(result, result->isCustomFont())); if (result->isLoading()) @@ -116,8 +116,8 @@ const FontData* FontFallbackList::fontDataForCharacters(const Font* font, const fontData = fontDataAt(font, ++realizedFontIndex); if (!fontData) { - ASSERT(FontCache::generation() == m_generation); - fontData = FontCache::getFontDataForCharacters(*font, characters, length); + ASSERT(fontCache()->generation() == m_generation); + fontData = fontCache()->getFontDataForCharacters(*font, characters, length); } return fontData; @@ -126,8 +126,8 @@ const FontData* FontFallbackList::fontDataForCharacters(const Font* font, const void FontFallbackList::setPlatformFont(const FontPlatformData& platformData) { m_familyIndex = cAllFamiliesScanned; - ASSERT(FontCache::generation() == m_generation); - const FontData* fontData = FontCache::getCachedFontData(&platformData); + ASSERT(fontCache()->generation() == m_generation); + const FontData* fontData = fontCache()->getCachedFontData(&platformData); m_fontList.append(pair<const FontData*, bool>(fontData, fontData->isCustomFont())); } diff --git a/WebCore/platform/graphics/FontFastPath.cpp b/WebCore/platform/graphics/FontFastPath.cpp new file mode 100644 index 0000000..635aba9 --- /dev/null +++ b/WebCore/platform/graphics/FontFastPath.cpp @@ -0,0 +1,367 @@ +/** + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Font.h" + +#include "CharacterNames.h" +#include "FontCache.h" +#include "FloatRect.h" +#include "GlyphBuffer.h" +#include "GlyphPageTreeNode.h" +#include "IntPoint.h" +#include "SimpleFontData.h" +#include "WidthIterator.h" + +#include <wtf/unicode/Unicode.h> +#include <wtf/MathExtras.h> + +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCaps) const +{ + bool useSmallCapsFont = forceSmallCaps; + if (m_fontDescription.smallCaps()) { + UChar32 upperC = Unicode::toUpper(c); + if (upperC != c) { + c = upperC; + useSmallCapsFont = true; + } + } + + if (mirror) + c = mirroredChar(c); + + unsigned pageNumber = (c / GlyphPage::size); + + GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero; + if (!node) { + node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); + if (pageNumber) + m_pages.set(pageNumber, node); + else + m_pageZero = node; + } + + GlyphPage* page; + if (!useSmallCapsFont) { + // Fastest loop, for the common case (not small caps). + while (true) { + page = node->page(); + if (page) { + const GlyphData& data = page->glyphDataForCharacter(c); + if (data.fontData) + return data; + if (node->isSystemFallback()) + break; + } + + // Proceed with the fallback list. + node = node->getChild(fontDataAt(node->level()), pageNumber); + if (pageNumber) + m_pages.set(pageNumber, node); + else + m_pageZero = node; + } + } else { + while (true) { + page = node->page(); + if (page) { + const GlyphData& data = page->glyphDataForCharacter(c); + if (data.fontData) { + // The smallCapsFontData function should not normally return 0. + // But if it does, we will just render the capital letter big. + const SimpleFontData* smallCapsFontData = data.fontData->smallCapsFontData(m_fontDescription); + if (!smallCapsFontData) + return data; + + GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber); + const GlyphPage* smallCapsPage = smallCapsNode->page(); + if (smallCapsPage) { + const GlyphData& data = smallCapsPage->glyphDataForCharacter(c); + if (data.fontData) + return data; + } + + // Do not attempt system fallback off the smallCapsFontData. This is the very unlikely case that + // a font has the lowercase character but the small caps font does not have its uppercase version. + return smallCapsFontData->missingGlyphData(); + } + + if (node->isSystemFallback()) + break; + } + + // Proceed with the fallback list. + node = node->getChild(fontDataAt(node->level()), pageNumber); + if (pageNumber) + m_pages.set(pageNumber, node); + else + m_pageZero = node; + } + } + + ASSERT(page); + ASSERT(node->isSystemFallback()); + + // System fallback is character-dependent. When we get here, we + // know that the character in question isn't in the system fallback + // font's glyph page. Try to lazily create it here. + UChar codeUnits[2]; + int codeUnitsLength; + if (c <= 0xFFFF) { + UChar c16 = c; + if (Font::treatAsSpace(c16)) + codeUnits[0] = ' '; + else if (Font::treatAsZeroWidthSpace(c16)) + codeUnits[0] = zeroWidthSpace; + else + codeUnits[0] = c16; + codeUnitsLength = 1; + } else { + codeUnits[0] = U16_LEAD(c); + codeUnits[1] = U16_TRAIL(c); + codeUnitsLength = 2; + } + const SimpleFontData* characterFontData = fontCache()->getFontDataForCharacters(*this, codeUnits, codeUnitsLength); + if (useSmallCapsFont && characterFontData) + characterFontData = characterFontData->smallCapsFontData(m_fontDescription); + if (characterFontData) { + // Got the fallback glyph and font. + GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page(); + const GlyphData& data = fallbackPage && fallbackPage->glyphDataForCharacter(c).fontData ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); + // Cache it so we don't have to do system fallback again next time. + if (!useSmallCapsFont) + page->setGlyphDataForCharacter(c, data.glyph, data.fontData); + return data; + } + + // Even system fallback can fail; use the missing glyph in that case. + // FIXME: It would be nicer to use the missing glyph from the last resort font instead. + const GlyphData& data = primaryFont()->missingGlyphData(); + if (!useSmallCapsFont) + page->setGlyphDataForCharacter(c, data.glyph, data.fontData); + return data; +} + +void Font::setCodePath(CodePath p) +{ + s_codePath = p; +} + +Font::CodePath Font::codePath() +{ + return s_codePath; +} + +bool Font::canUseGlyphCache(const TextRun& run) const +{ + switch (s_codePath) { + case Auto: + break; + case Simple: + return true; + case Complex: + return false; + } + + // Start from 0 since drawing and highlighting also measure the characters before run->from + for (int i = 0; i < run.length(); i++) { + const UChar c = run[i]; + if (c < 0x300) // U+0300 through U+036F Combining diacritical marks + continue; + if (c <= 0x36F) + return false; + + if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha + continue; + if (c <= 0x05CF) + return false; + + if (c < 0x0600) // U+0600 through U+1059 Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar + continue; + if (c <= 0x1059) + return false; + + if (c < 0x1100) // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; Modern Korean will be precomposed as a result of step A) + continue; + if (c <= 0x11FF) + return false; + + if (c < 0x1780) // U+1780 through U+18AF Khmer, Mongolian + continue; + if (c <= 0x18AF) + return false; + + if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0) + continue; + if (c <= 0x194F) + return false; + + if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols + continue; + if (c <= 0x20FF) + return false; + + if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks + continue; + if (c <= 0xFE2F) + return false; + } + + return true; + +} + +void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const +{ + // This glyph buffer holds our glyphs+advances+font data for each glyph. + GlyphBuffer glyphBuffer; + + float startX = point.x(); + WidthIterator it(this, run); + it.advance(from); + float beforeWidth = it.m_runWidthSoFar; + it.advance(to, &glyphBuffer); + + // We couldn't generate any glyphs for the run. Give up. + if (glyphBuffer.isEmpty()) + return; + + float afterWidth = it.m_runWidthSoFar; + + if (run.rtl()) { + float finalRoundingWidth = it.m_finalRoundingWidth; + it.advance(run.length()); + startX += finalRoundingWidth + it.m_runWidthSoFar - afterWidth; + } else + startX += beforeWidth; + + // Swap the order of the glyphs if right-to-left. + if (run.rtl()) + for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) + glyphBuffer.swap(i, end); + + // Calculate the starting point of the glyphs to be displayed by adding + // all the advances up to the first glyph. + FloatPoint startPoint(startX, point.y()); + drawGlyphBuffer(context, glyphBuffer, run, startPoint); +} + +void Font::drawGlyphBuffer(GraphicsContext* context, const GlyphBuffer& glyphBuffer, const TextRun&, const FloatPoint& point) const +{ + // Draw each contiguous run of glyphs that use the same font data. + const SimpleFontData* fontData = glyphBuffer.fontDataAt(0); + FloatSize offset = glyphBuffer.offsetAt(0); + FloatPoint startPoint(point); + float nextX = startPoint.x(); + int lastFrom = 0; + int nextGlyph = 0; + while (nextGlyph < glyphBuffer.size()) { + const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph); + FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph); + if (nextFontData != fontData || nextOffset != offset) { + drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); + + lastFrom = nextGlyph; + fontData = nextFontData; + offset = nextOffset; + startPoint.setX(nextX); + } + nextX += glyphBuffer.advanceAt(nextGlyph); + nextGlyph++; + } + + drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); +} + +float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer) const +{ + WidthIterator it(this, run); + it.advance(run.length(), glyphBuffer); + return it.m_runWidthSoFar; +} + +FloatRect Font::selectionRectForSimpleText(const TextRun& run, const IntPoint& point, int h, int from, int to) const +{ + WidthIterator it(this, run); + it.advance(from); + float beforeWidth = it.m_runWidthSoFar; + it.advance(to); + float afterWidth = it.m_runWidthSoFar; + + // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning + if (run.rtl()) { + it.advance(run.length()); + float totalWidth = it.m_runWidthSoFar; + return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); + } else { + return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); + } +} + +int Font::offsetForPositionForSimpleText(const TextRun& run, int x, bool includePartialGlyphs) const +{ + float delta = (float)x; + + WidthIterator it(this, run); + GlyphBuffer localGlyphBuffer; + unsigned offset; + if (run.rtl()) { + delta -= floatWidthForSimpleText(run, 0); + while (1) { + offset = it.m_currentCharacter; + float w; + if (!it.advanceOneCharacter(w, &localGlyphBuffer)) + break; + delta += w; + if (includePartialGlyphs) { + if (delta - w / 2 >= 0) + break; + } else { + if (delta >= 0) + break; + } + } + } else { + while (1) { + offset = it.m_currentCharacter; + float w; + if (!it.advanceOneCharacter(w, &localGlyphBuffer)) + break; + delta -= w; + if (includePartialGlyphs) { + if (delta + w / 2 <= 0) + break; + } else { + if (delta <= 0) + break; + } + } + } + + return offset; +} + +} diff --git a/WebCore/platform/graphics/GeneratedImage.cpp b/WebCore/platform/graphics/GeneratedImage.cpp index 5e50959..15e27d7 100644 --- a/WebCore/platform/graphics/GeneratedImage.cpp +++ b/WebCore/platform/graphics/GeneratedImage.cpp @@ -47,7 +47,7 @@ void GeneratedImage::draw(GraphicsContext* context, const FloatRect& dstRect, co context->restore(); } -void GeneratedImage::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, +void GeneratedImage::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect) { // Create a BitmapImage and call drawPattern on it. diff --git a/WebCore/platform/graphics/GeneratedImage.h b/WebCore/platform/graphics/GeneratedImage.h index fb0661b..dea0c54 100644 --- a/WebCore/platform/graphics/GeneratedImage.h +++ b/WebCore/platform/graphics/GeneratedImage.h @@ -53,15 +53,14 @@ public: virtual IntSize size() const { return m_size; } // Assume that generated content has no decoded data we need to worry about - virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) { } + virtual void destroyDecodedData(bool /*destroyAll*/ = true) { } virtual unsigned decodedSize() const { return 0; } protected: virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, + virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); -protected: GeneratedImage(PassRefPtr<Generator> generator, const IntSize& size) : m_generator(generator) , m_size(size) diff --git a/WebCore/platform/graphics/GlyphBuffer.h b/WebCore/platform/graphics/GlyphBuffer.h index 18957d5..fdb306f 100644 --- a/WebCore/platform/graphics/GlyphBuffer.h +++ b/WebCore/platform/graphics/GlyphBuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,14 +30,16 @@ #define GlyphBuffer_h #include "FloatSize.h" +#include <wtf/UnusedParam.h> +#include <wtf/Vector.h> #if PLATFORM(CG) #include <ApplicationServices/ApplicationServices.h> -#elif PLATFORM(CAIRO) -#include <cairo.h> #endif -#include <wtf/Vector.h> +#if PLATFORM(CAIRO) +#include <cairo.h> +#endif namespace WebCore { @@ -125,6 +127,7 @@ public: #if PLATFORM(WIN) return m_offsets[index]; #else + UNUSED_PARAM(index); return FloatSize(); #endif } @@ -132,6 +135,7 @@ public: void add(Glyph glyph, const SimpleFontData* font, float width, const FloatSize* offset = 0) { m_fontData.append(font); + #if PLATFORM(CAIRO) cairo_glyph_t cairoGlyph; cairoGlyph.index = glyph; @@ -152,6 +156,8 @@ public: m_offsets.append(*offset); else m_offsets.append(FloatSize()); +#else + UNUSED_PARAM(offset); #endif } diff --git a/WebCore/platform/graphics/GraphicsContext.cpp b/WebCore/platform/graphics/GraphicsContext.cpp index 9cd2969..8426011 100644 --- a/WebCore/platform/graphics/GraphicsContext.cpp +++ b/WebCore/platform/graphics/GraphicsContext.cpp @@ -109,17 +109,6 @@ void GraphicsContext::restore() restorePlatformState(); } -const Font& GraphicsContext::font() const -{ - return m_common->state.font; -} - -void GraphicsContext::setFont(const Font& aFont) -{ - m_common->state.font = aFont; - setPlatformFont(aFont); -} - void GraphicsContext::setStrokeThickness(float thickness) { m_common->state.strokeThickness = thickness; @@ -211,6 +200,17 @@ Color GraphicsContext::fillColor() const return m_common->state.fillColor; } +void GraphicsContext::setShouldAntialias(bool b) +{ + m_common->state.shouldAntialias = b; + setPlatformShouldAntialias(b); +} + +bool GraphicsContext::shouldAntialias() const +{ + return m_common->state.shouldAntialias; +} + void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) { ASSERT(pattern); @@ -255,6 +255,11 @@ void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) m_common->state.fillGradient = gradient; } +void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms) +{ + m_common->state.shadowsIgnoreTransforms = ignoreTransforms; +} + bool GraphicsContext::updatingControlTints() const { return m_common->m_updatingControlTints; @@ -296,15 +301,15 @@ void GraphicsContext::drawImage(Image* image, const IntRect& dest, const IntRect drawImage(image, FloatRect(dest), srcRect, op, useLowQualityScale); } -void GraphicsContext::drawText(const TextRun& run, const IntPoint& point, int from, int to) +void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to) { if (paintingDisabled()) return; - font().drawText(this, run, point, from, to); + font.drawText(this, run, point, from, to); } -void GraphicsContext::drawBidiText(const TextRun& run, const FloatPoint& point) +void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point) { if (paintingDisabled()) return; @@ -329,23 +334,23 @@ void GraphicsContext::drawBidiText(const TextRun& run, const FloatPoint& point) subrun.setRTL(bidiRun->level() % 2); subrun.setDirectionalOverride(bidiRun->dirOverride(false)); - font().drawText(this, subrun, currPoint); + font.drawText(this, subrun, currPoint); bidiRun = bidiRun->next(); // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. if (bidiRun) - currPoint.move(font().floatWidth(subrun), 0.f); + currPoint.move(font.floatWidth(subrun), 0.f); } bidiResolver.deleteRuns(); } -void GraphicsContext::drawHighlightForText(const TextRun& run, const IntPoint& point, int h, const Color& backgroundColor, int from, int to) +void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const IntPoint& point, int h, const Color& backgroundColor, int from, int to) { if (paintingDisabled()) return; - fillRect(font().selectionRectForText(run, point, h, from, to), backgroundColor); + fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor); } void GraphicsContext::initFocusRing(int width, int offset) @@ -498,10 +503,4 @@ void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&) } #endif -#if !PLATFORM(QT) -void GraphicsContext::setPlatformFont(const Font&) -{ -} -#endif - } diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index 95bdc90..c27f38f 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -98,7 +98,7 @@ namespace WebCore { const int cMisspellingLinePatternWidth = 4; const int cMisspellingLinePatternGapWidth = 1; - class AffineTransform; + class TransformationMatrix; class Font; class Generator; class Gradient; @@ -145,9 +145,6 @@ namespace WebCore { ~GraphicsContext(); PlatformGraphicsContext* platformContext() const; - - const Font& font() const; - void setFont(const Font&); float strokeThickness() const; void setStrokeThickness(float); @@ -166,6 +163,15 @@ namespace WebCore { void setFillColor(const Color&); void setFillPattern(PassRefPtr<Pattern>); void setFillGradient(PassRefPtr<Gradient>); + void setShadowsIgnoreTransforms(bool); + + void setShouldAntialias(bool); + bool shouldAntialias() const; + +#if PLATFORM(CG) + void applyStrokePattern(); + void applyFillPattern(); +#endif #if PLATFORM(SGL) /* these should be pused to apple. needed for CanvasStyle.cpp */ @@ -183,6 +189,10 @@ namespace WebCore { bool willFill() const; // returns true if there is a valid (non-transparent) stroke color bool willStroke() const; + + // may return NULL, since we lazily allocate the path. This is the path + // that is drawn by drawPath() + const SkPath* getCurrPath() const; /** platform-specific factory method to return a bitmap graphicscontext, called by <canvas> when we need to draw offscreen. Caller is responsible for @@ -239,14 +249,15 @@ namespace WebCore { void clipOut(const IntRect&); void clipOutEllipseInRect(const IntRect&); void clipOutRoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight); + void clipPath(WindRule); void clipToImageBuffer(const FloatRect&, const ImageBuffer*); int textDrawingMode(); void setTextDrawingMode(int); - void drawText(const TextRun&, const IntPoint&, int from = 0, int to = -1); - void drawBidiText(const TextRun&, const FloatPoint&); - void drawHighlightForText(const TextRun&, const IntPoint&, int h, const Color& backgroundColor, int from = 0, int to = -1); + void drawText(const Font&, const TextRun&, const IntPoint&, int from = 0, int to = -1); + void drawBidiText(const Font&, const TextRun&, const FloatPoint&); + void drawHighlightForText(const Font&, const TextRun&, const IntPoint&, int h, const Color& backgroundColor, int from = 0, int to = -1); FloatRect roundToDevicePixels(const FloatRect&); @@ -297,10 +308,8 @@ namespace WebCore { void setURLForRect(const KURL&, const IntRect&); - void concatCTM(const AffineTransform&); - AffineTransform getCTM() const; - - void setUseAntialiasing(bool = true); + void concatCTM(const TransformationMatrix&); + TransformationMatrix getCTM() const; #if PLATFORM(WIN) GraphicsContext(HDC, bool hasAlpha = false); // FIXME: To be removed. @@ -308,6 +317,17 @@ namespace WebCore { HDC getWindowsContext(const IntRect&, bool supportAlphaBlend = true, bool mayCreateBitmap = true); // The passed in rect is used to create a bitmap for compositing inside transparency layers. void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend = true, bool mayCreateBitmap = true); // The passed in HDC should be the one handed back by getWindowsContext. + // When set to true, child windows should be rendered into this context + // rather than allowing them just to render to the screen. Defaults to + // false. + // FIXME: This is a layering violation. GraphicsContext shouldn't know + // what a "window" is. It would be much more appropriate for this flag + // to be passed as a parameter alongside the GraphicsContext, but doing + // that would require lots of changes in cross-platform code that we + // aren't sure we want to make. + void setShouldIncludeChildWindows(bool); + bool shouldIncludeChildWindows() const; + class WindowsBitmap : public Noncopyable { public: WindowsBitmap(HDC, IntSize); @@ -341,6 +361,7 @@ namespace WebCore { #if PLATFORM(QT) bool inTransparencyLayer() const; PlatformPath* currentPath(); + QPen pen(); #endif #if PLATFORM(GTK) @@ -362,6 +383,8 @@ namespace WebCore { void setPlatformFillColor(const Color&); + void setPlatformShouldAntialias(bool b); + void setPlatformShadow(const IntSize&, int blur, const Color&); void clearPlatformShadow(); diff --git a/WebCore/platform/graphics/GraphicsContextPrivate.h b/WebCore/platform/graphics/GraphicsContextPrivate.h index de94527..87123eb 100644 --- a/WebCore/platform/graphics/GraphicsContextPrivate.h +++ b/WebCore/platform/graphics/GraphicsContextPrivate.h @@ -26,7 +26,7 @@ #ifndef GraphicsContextPrivate_h #define GraphicsContextPrivate_h -#include "Font.h" +#include "TransformationMatrix.h" #include "Gradient.h" #include "GraphicsContext.h" #include "Pattern.h" @@ -58,18 +58,21 @@ namespace WebCore { , fillRule(RULE_NONZERO) , fillColorSpace(SolidColorSpace) , fillColor(Color::black) + , shouldAntialias(true) , paintingDisabled(false) , shadowBlur(0) + , shadowsIgnoreTransforms(false) { } - Font font; int textDrawingMode; StrokeStyle strokeStyle; float strokeThickness; #if PLATFORM(CAIRO) float globalAlpha; +#elif PLATFORM(QT) + TransformationMatrix pathTransform; #endif ColorSpace strokeColorSpace; Color strokeColor; @@ -83,11 +86,15 @@ namespace WebCore { RefPtr<Gradient> fillGradient; RefPtr<Pattern> fillPattern; + bool shouldAntialias; + bool paintingDisabled; IntSize shadowSize; unsigned shadowBlur; Color shadowColor; + + bool shadowsIgnoreTransforms; }; class GraphicsContextPrivate { diff --git a/WebCore/platform/graphics/Image.cpp b/WebCore/platform/graphics/Image.cpp index ca6954e..49961e1 100644 --- a/WebCore/platform/graphics/Image.cpp +++ b/WebCore/platform/graphics/Image.cpp @@ -27,11 +27,12 @@ #include "config.h" #include "Image.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "BitmapImage.h" #include "GraphicsContext.h" #include "IntRect.h" #include "MIMETypeRegistry.h" +#include <wtf/StdLibExtras.h> #include <math.h> @@ -52,7 +53,7 @@ Image::~Image() Image* Image::nullImage() { - static RefPtr<Image> nullImage = BitmapImage::create(); + DEFINE_STATIC_LOCAL(RefPtr<Image>, nullImage, (BitmapImage::create()));; return nullImage.get(); } @@ -61,11 +62,6 @@ bool Image::supportsType(const String& type) return MIMETypeRegistry::isSupportedImageResourceMIMEType(type); } -bool Image::isNull() const -{ - return size().isEmpty(); -} - bool Image::setData(PassRefPtr<SharedBuffer> data, bool allDataReceived) { m_data = data; @@ -79,21 +75,6 @@ bool Image::setData(PassRefPtr<SharedBuffer> data, bool allDataReceived) return dataChanged(allDataReceived); } -IntRect Image::rect() const -{ - return IntRect(IntPoint(), size()); -} - -int Image::width() const -{ - return size().width(); -} - -int Image::height() const -{ - return size().height(); -} - void Image::fillWithSolidColor(GraphicsContext* ctxt, const FloatRect& dstRect, const Color& color, CompositeOperator op) { if (color.alpha() <= 0) @@ -138,7 +119,7 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); - AffineTransform patternTransform = AffineTransform().scale(scale.width(), scale.height()); + TransformationMatrix patternTransform = TransformationMatrix().scale(scale.width(), scale.height()); FloatRect oneTileRect; oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), scaledTileSize.width()) - scaledTileSize.width(), scaledTileSize.width())); @@ -177,7 +158,7 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& dstRect, const Flo vRule = RepeatTile; FloatSize scale = calculatePatternScale(dstRect, srcRect, hRule, vRule); - AffineTransform patternTransform = AffineTransform().scale(scale.width(), scale.height()); + TransformationMatrix patternTransform = TransformationMatrix().scale(scale.width(), scale.height()); // We want to construct the phase such that the pattern is centered (when stretch is not // set for a particular rule). diff --git a/WebCore/platform/graphics/Image.h b/WebCore/platform/graphics/Image.h index 1419b2d..c3cf2e7 100644 --- a/WebCore/platform/graphics/Image.h +++ b/WebCore/platform/graphics/Image.h @@ -30,6 +30,7 @@ #include "Color.h" #include "GraphicsTypes.h" #include "ImageSource.h" +#include "IntRect.h" #include <wtf/RefPtr.h> #include <wtf/PassRefPtr.h> #include "SharedBuffer.h" @@ -66,13 +67,11 @@ class SkBitmapRef; namespace WebCore { -class AffineTransform; +class TransformationMatrix; class FloatPoint; class FloatRect; class FloatSize; class GraphicsContext; -class IntRect; -class IntSize; class SharedBuffer; class String; @@ -96,7 +95,7 @@ public: virtual bool hasSingleSecurityOrigin() const { return false; } static Image* nullImage(); - bool isNull() const; + bool isNull() const { return size().isEmpty(); } // These are only used for SVGImage right now virtual void setContainerSize(const IntSize&) { } @@ -105,14 +104,16 @@ public: virtual bool hasRelativeHeight() const { return false; } virtual IntSize size() const = 0; - IntRect rect() const; - int width() const; - int height() const; + IntRect rect() const { return IntRect(IntPoint(), size()); } + int width() const { return size().width(); } + int height() const { return size().height(); } bool setData(PassRefPtr<SharedBuffer> data, bool allDataReceived); - virtual bool dataChanged(bool allDataReceived) { return false; } + virtual bool dataChanged(bool /*allDataReceived*/) { return false; } + + virtual String filenameExtension() const { return String(); } // null string if unknown - virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) = 0; + virtual void destroyDecodedData(bool destroyAll = true) = 0; virtual unsigned decodedSize() const = 0; SharedBuffer* data() { return m_data.get(); } @@ -166,9 +167,9 @@ protected: virtual bool mayFillWithSolidColor() const { return false; } virtual Color solidColor() const { return Color(); } - virtual void startAnimation() { } + virtual void startAnimation(bool /*catchUpIfNecessary*/ = true) { } - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, + virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); #if PLATFORM(CG) // These are private to CG. Ideally they would be only in the .cpp file, but the callback requires access diff --git a/WebCore/platform/graphics/ImageBuffer.h b/WebCore/platform/graphics/ImageBuffer.h index 7c68fc8..14f7461 100644 --- a/WebCore/platform/graphics/ImageBuffer.h +++ b/WebCore/platform/graphics/ImageBuffer.h @@ -27,6 +27,7 @@ #ifndef ImageBuffer_h #define ImageBuffer_h +#include "TransformationMatrix.h" #include "Image.h" #include "IntSize.h" #include "ImageBufferData.h" @@ -67,7 +68,11 @@ namespace WebCore { void putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint); String toDataURL(const String& mimeType) const; - +#if !PLATFORM(CG) + TransformationMatrix baseTransform() const { return TransformationMatrix(); } +#else + TransformationMatrix baseTransform() const { return TransformationMatrix(1, 0, 0, -1, 0, m_size.height()); } +#endif private: ImageBufferData m_data; diff --git a/WebCore/platform/graphics/ImageObserver.h b/WebCore/platform/graphics/ImageObserver.h index 4be83bd..8b693d9 100644 --- a/WebCore/platform/graphics/ImageObserver.h +++ b/WebCore/platform/graphics/ImageObserver.h @@ -29,6 +29,7 @@ namespace WebCore { class Image; +class IntRect; // Interface for notification about changes to an image, including decoding, // drawing, and animating. @@ -41,6 +42,8 @@ public: virtual bool shouldPauseAnimation(const Image*) = 0; virtual void animationAdvanced(const Image*) = 0; + + virtual void changedInRect(const Image*, const IntRect&) = 0; }; } diff --git a/WebCore/platform/graphics/ImageSource.h b/WebCore/platform/graphics/ImageSource.h index a9f346d..55e0c5a 100644 --- a/WebCore/platform/graphics/ImageSource.h +++ b/WebCore/platform/graphics/ImageSource.h @@ -55,6 +55,7 @@ namespace WebCore { class IntSize; class SharedBuffer; +class String; #if PLATFORM(WX) class ImageDecoder; @@ -94,22 +95,49 @@ public: ImageSource(); ~ImageSource(); - void clear(); + // Tells the ImageSource that the Image no longer cares about decoded frame + // data -- at all (if |destroyAll| is true), or before frame + // |clearBeforeFrame| (if |destroyAll| is false). The ImageSource should + // delete cached decoded data for these frames where possible to keep memory + // usage low. When |destroyAll| is true, the ImageSource should also reset + // any local state so that decoding can begin again. + // + // Implementations that delete less than what's specified above waste + // memory. Implementations that delete more may burn CPU re-decoding frames + // that could otherwise have been cached, or encounter errors if they're + // asked to decode frames they can't decode due to the loss of previous + // decoded frames. + // + // Callers should not call clear(false, n) and subsequently call + // createFrameAtIndex(m) with m < n, unless they first call clear(true). + // This ensures that stateful ImageSources/decoders will work properly. + // + // The |data| and |allDataReceived| parameters should be supplied by callers + // who set |destroyAll| to true if they wish to be able to continue using + // the ImageSource. This way implementations which choose to destroy their + // decoders in some cases can reconstruct them correctly. + void clear(bool destroyAll, + size_t clearBeforeFrame = 0, + SharedBuffer* data = NULL, + bool allDataReceived = false); bool initialized() const; - + void setData(SharedBuffer* data, bool allDataReceived); + String filenameExtension() const; bool isSizeAvailable(); IntSize size() const; IntSize frameSizeAtIndex(size_t) const; int repetitionCount(); - + size_t frameCount() const; - + + // Callers should not call this after calling clear() with a higher index; + // see comments on clear() above. NativeImagePtr createFrameAtIndex(size_t); - + float frameDurationAtIndex(size_t); bool frameHasAlphaAtIndex(size_t); // Whether or not the frame actually used any alpha. bool frameIsCompleteAtIndex(size_t); // Whether or not the frame is completely decoded. @@ -119,6 +147,9 @@ public: void setURL(const String& url); #endif private: + // FIXME: This is protected only to allow ImageSourceSkia to set ICO decoder + // with a preferred size. See ImageSourceSkia.h for discussion. +protected: NativeImageSourcePtr m_decoder; }; diff --git a/WebCore/platform/graphics/IntSize.h b/WebCore/platform/graphics/IntSize.h index 7245408..4d36545 100644 --- a/WebCore/platform/graphics/IntSize.h +++ b/WebCore/platform/graphics/IntSize.h @@ -67,6 +67,12 @@ public: bool isEmpty() const { return m_width <= 0 || m_height <= 0; } + void expand(int width, int height) + { + m_width += width; + m_height += height; + } + IntSize expandedTo(const IntSize& other) const { return IntSize(m_width > other.m_width ? m_width : other.m_width, diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 1beab95..203f299 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -67,6 +67,9 @@ public: void setFrameView(FrameView* frameView) { m_frameView = frameView; } bool inMediaDocument(); + // FIXME: it would be better to just have a getter and setter for size. + // This is currently an absolute rect, which is not appropriate for + // content with transforms IntRect rect() const { return m_rect; } void setRect(const IntRect& r); diff --git a/WebCore/platform/graphics/Path.h b/WebCore/platform/graphics/Path.h index 06e6ee4..2b0a7d1 100644 --- a/WebCore/platform/graphics/Path.h +++ b/WebCore/platform/graphics/Path.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. - * 2006 Rob Buis <buis@kde.org> + * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved. + * 2006 Rob Buis <buis@kde.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,6 +27,8 @@ #ifndef Path_h #define Path_h +#include <algorithm> + #if PLATFORM(CG) typedef struct CGPath PlatformPath; #elif PLATFORM(QT) @@ -55,11 +57,13 @@ typedef void PlatformPath; namespace WebCore { - class AffineTransform; class FloatPoint; - class FloatSize; class FloatRect; + class FloatSize; + class GraphicsContext; class String; + class StrokeStyleApplier; + class TransformationMatrix; enum WindRule { RULE_NONZERO = 0, @@ -79,7 +83,7 @@ namespace WebCore { FloatPoint* points; }; - typedef void (*PathApplierFunction) (void* info, const PathElement*); + typedef void (*PathApplierFunction)(void* info, const PathElement*); class Path { public: @@ -89,8 +93,12 @@ namespace WebCore { Path(const Path&); Path& operator=(const Path&); + void swap(Path& other) { std::swap(m_path, other.m_path); } + bool contains(const FloatPoint&, WindRule rule = RULE_NONZERO) const; + bool strokeContains(StrokeStyleApplier*, const FloatPoint&) const; FloatRect boundingRect() const; + FloatRect strokeBoundingRect(StrokeStyleApplier* = 0); float length(); FloatPoint pointAtLength(float length, bool& ok); @@ -124,7 +132,7 @@ namespace WebCore { static Path createLine(const FloatPoint&, const FloatPoint&); void apply(void* info, PathApplierFunction) const; - void transform(const AffineTransform&); + void transform(const TransformationMatrix&); private: PlatformPath* m_path; diff --git a/WebCore/platform/graphics/Pattern.h b/WebCore/platform/graphics/Pattern.h index 985c7c0..716a645 100644 --- a/WebCore/platform/graphics/Pattern.h +++ b/WebCore/platform/graphics/Pattern.h @@ -54,7 +54,7 @@ typedef wxBrush* PlatformPatternPtr; #endif namespace WebCore { - class AffineTransform; + class TransformationMatrix; class Image; class Pattern : public RefCounted<Pattern> { @@ -67,7 +67,7 @@ namespace WebCore { Image* tileImage() const { return m_tileImage.get(); } - PlatformPatternPtr createPlatformPattern(const AffineTransform& patternTransform) const; + PlatformPatternPtr createPlatformPattern(const TransformationMatrix& patternTransform) const; private: Pattern(Image*, bool repeatX, bool repeatY); diff --git a/WebCore/platform/graphics/SegmentedFontData.cpp b/WebCore/platform/graphics/SegmentedFontData.cpp index ceefe4f..1731d16 100644 --- a/WebCore/platform/graphics/SegmentedFontData.cpp +++ b/WebCore/platform/graphics/SegmentedFontData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -45,16 +45,27 @@ const SimpleFontData* SegmentedFontData::fontDataForCharacter(UChar32 c) const return m_ranges[0].fontData(); } -bool SegmentedFontData::containsCharacters(const UChar* characters, int length) const +bool SegmentedFontData::containsCharacter(UChar32 c) const { Vector<FontDataRange>::const_iterator end = m_ranges.end(); for (Vector<FontDataRange>::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (it->from() <= characters[0] && it->to() >= characters[0]) + if (c >= it->from() && c <= it->to()) return true; } return false; } +bool SegmentedFontData::containsCharacters(const UChar* characters, int length) const +{ + UChar32 c; + for (int i = 0; i < length; ) { + U16_NEXT(characters, i, length, c) + if (!containsCharacter(c)) + return false; + } + return true; +} + bool SegmentedFontData::isCustomFont() const { // All segmented fonts are custom fonts. diff --git a/WebCore/platform/graphics/SegmentedFontData.h b/WebCore/platform/graphics/SegmentedFontData.h index 1adec15..0a78321 100644 --- a/WebCore/platform/graphics/SegmentedFontData.h +++ b/WebCore/platform/graphics/SegmentedFontData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -55,6 +55,11 @@ class SegmentedFontData : public FontData { public: virtual ~SegmentedFontData(); + void appendRange(const FontDataRange& range) { m_ranges.append(range); } + unsigned numRanges() const { return m_ranges.size(); } + const FontDataRange& rangeAt(unsigned i) const { return m_ranges[i]; } + +private: virtual const SimpleFontData* fontDataForCharacter(UChar32) const; virtual bool containsCharacters(const UChar*, int length) const; @@ -62,11 +67,8 @@ public: virtual bool isLoading() const; virtual bool isSegmented() const; - void appendRange(const FontDataRange& range) { m_ranges.append(range); } - unsigned numRanges() const { return m_ranges.size(); } - const FontDataRange& rangeAt(unsigned i) const { return m_ranges[i]; } + bool containsCharacter(UChar32) const; -private: Vector<FontDataRange, 1> m_ranges; }; diff --git a/WebCore/platform/graphics/SimpleFontData.cpp b/WebCore/platform/graphics/SimpleFontData.cpp index 372fcc8..9670b55 100644 --- a/WebCore/platform/graphics/SimpleFontData.cpp +++ b/WebCore/platform/graphics/SimpleFontData.cpp @@ -30,6 +30,7 @@ #include "config.h" #include "SimpleFontData.h" +#include "Font.h" #include "FontCache.h" #if ENABLE(SVG_FONTS) #include "SVGFontData.h" @@ -41,7 +42,8 @@ namespace WebCore { SimpleFontData::SimpleFontData(const FontPlatformData& f, bool customFont, bool loading, SVGFontData* svgFontData) - : m_font(f) + : m_unitsPerEm(defaultUnitsPerEm) + , m_font(f) , m_treatAsFixedPitch(false) #if ENABLE(SVG_FONTS) , m_svgFontData(svgFontData) @@ -50,7 +52,7 @@ SimpleFontData::SimpleFontData(const FontPlatformData& f, bool customFont, bool , m_isLoading(loading) , m_smallCapsFontData(0) { -#if ENABLE(SVG_FONTS) && !PLATFORM(QT) +#if ENABLE(SVG_FONTS) if (SVGFontFaceElement* svgFontFaceElement = svgFontData ? svgFontData->svgFontFaceElement() : 0) { m_unitsPerEm = svgFontFaceElement->unitsPerEm(); @@ -75,7 +77,12 @@ SimpleFontData::SimpleFontData(const FontPlatformData& f, bool customFont, bool #endif platformInit(); + platformGlyphInit(); +} +#if !PLATFORM(QT) +void SimpleFontData::platformGlyphInit() +{ GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (!glyphPageZero) { LOG_ERROR("Failed to get glyph page zero."); @@ -113,21 +120,23 @@ SimpleFontData::SimpleFontData(const FontPlatformData& f, bool customFont, bool m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; } +#endif SimpleFontData::~SimpleFontData() { +#if ENABLE(SVG_FONTS) + if (!m_svgFontData || !m_svgFontData->svgFontFaceElement()) +#endif + platformDestroy(); + if (!isCustomFont()) { if (m_smallCapsFontData) - FontCache::releaseFontData(m_smallCapsFontData); + fontCache()->releaseFontData(m_smallCapsFontData); GlyphPageTreeNode::pruneTreeFontData(this); } - -#if ENABLE(SVG_FONTS) && !PLATFORM(QT) - if (!m_svgFontData || !m_svgFontData->svgFontFaceElement()) -#endif - platformDestroy(); } +#if !PLATFORM(QT) float SimpleFontData::widthForGlyph(Glyph glyph) const { float width = m_glyphToWidthMap.widthForGlyph(glyph); @@ -139,6 +148,7 @@ float SimpleFontData::widthForGlyph(Glyph glyph) const return width; } +#endif const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const { diff --git a/WebCore/platform/graphics/SimpleFontData.h b/WebCore/platform/graphics/SimpleFontData.h index 5f26cbf..e572e30 100644 --- a/WebCore/platform/graphics/SimpleFontData.h +++ b/WebCore/platform/graphics/SimpleFontData.h @@ -41,6 +41,10 @@ typedef struct OpaqueATSUStyle* ATSUStyle; #include <cairo.h> #endif +#if PLATFORM(QT) +#include <QFont> +#endif + namespace WebCore { class FontDescription; @@ -109,6 +113,10 @@ public: } #endif +#if PLATFORM(QT) + QFont getQtFont() const { return m_font.font(); } +#endif + #if PLATFORM(WIN) bool isSystemFont() const { return m_isSystemFont; } SCRIPT_FONTPROPERTIES* scriptFontProperties() const; @@ -128,6 +136,7 @@ public: private: void platformInit(); + void platformGlyphInit(); void platformDestroy(); void commonInit(); diff --git a/WebCore/platform/graphics/StrokeStyleApplier.h b/WebCore/platform/graphics/StrokeStyleApplier.h new file mode 100644 index 0000000..e40d3d1 --- /dev/null +++ b/WebCore/platform/graphics/StrokeStyleApplier.h @@ -0,0 +1,38 @@ +/* + Copyright (C) 2008 Dirk Schulze <krit@webkit.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef StrokeStyleApplier_h +#define StrokeStyleApplier_h + +namespace WebCore { + + class GraphicsContext; + + class StrokeStyleApplier { + public: + virtual void strokeStyle(GraphicsContext*) = 0; + + protected: + StrokeStyleApplier() {} + virtual ~StrokeStyleApplier() {} + }; +} + +#endif + diff --git a/WebCore/platform/graphics/android/FontCacheAndroid.cpp b/WebCore/platform/graphics/android/FontCacheAndroid.cpp index c257348..0b3655f 100644 --- a/WebCore/platform/graphics/android/FontCacheAndroid.cpp +++ b/WebCore/platform/graphics/android/FontCacheAndroid.cpp @@ -113,7 +113,7 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD if (fontDescription.italic()) style |= SkTypeface::kItalic; - SkTypeface* tf = SkTypeface::Create(name, (SkTypeface::Style)style); + SkTypeface* tf = SkTypeface::CreateFromName(name, (SkTypeface::Style)style); FontPlatformData* result = new FontPlatformData(tf, fontDescription.computedSize(), diff --git a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp index c2e0f02..40d98ec 100644 --- a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp +++ b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp @@ -40,7 +40,7 @@ #include "SkPaint.h" #include "SkPorterDuff.h" #include "PlatformGraphicsContext.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "android_graphics.h" #include "SkGradientShader.h" @@ -320,7 +320,7 @@ static void extactShader(SkPaint* paint, ColorSpace cs, Pattern* pat, case PatternColorSpace: // createPlatformPattern() returns a new inst paint->setShader(pat->createPlatformPattern( - AffineTransform()))->safeUnref(); + TransformationMatrix()))->safeUnref(); break; case GradientColorSpace: { // grad->getShader() returns a cached obj @@ -389,6 +389,10 @@ bool GraphicsContext::willStroke() const { return m_data->mState->mStrokeColor != 0; } +const SkPath* GraphicsContext::getCurrPath() const { + return m_data->mState->mPath; +} + // Draws a filled rectangle with a stroked border. void GraphicsContext::drawRect(const IntRect& rect) { @@ -771,19 +775,21 @@ KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext() } #endif +/* These are the flags we need when we call saveLayer for transparency. + Since it does not appear that webkit intends this to also save/restore + the matrix or clip, I do not give those flags (for performance) + */ +#define TRANSPARENCY_SAVEFLAGS \ + (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | \ + SkCanvas::kFullColorLayer_SaveFlag) + void GraphicsContext::beginTransparencyLayer(float opacity) { if (paintingDisabled()) return; SkCanvas* canvas = GC2Canvas(this); - - if (opacity < 1) - { - canvas->saveLayerAlpha(NULL, (int)(opacity * 255), SkCanvas::kHasAlphaLayer_SaveFlag); - } - else - canvas->save(); + canvas->saveLayerAlpha(NULL, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS); } void GraphicsContext::endTransparencyLayer() @@ -976,7 +982,7 @@ void GraphicsContext::translate(float x, float y) GC2Canvas(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y)); } -void GraphicsContext::concatCTM(const AffineTransform& xform) +void GraphicsContext::concatCTM(const TransformationMatrix& xform) { if (paintingDisabled()) return; @@ -1026,32 +1032,38 @@ if (urlRef) { #endif } -void GraphicsContext::setUseAntialiasing(bool useAA) { +void GraphicsContext::setPlatformShouldAntialias(bool useAA) +{ if (paintingDisabled()) return; m_data->mState->mUseAA = useAA; } -AffineTransform GraphicsContext::getCTM() const { - return AffineTransform(GC2Canvas(this)->getTotalMatrix()); +TransformationMatrix GraphicsContext::getCTM() const +{ + return TransformationMatrix(GC2Canvas(this)->getTotalMatrix()); } /////////////////////////////////////////////////////////////////////////////// -void GraphicsContext::beginPath() { +void GraphicsContext::beginPath() +{ m_data->beginPath(); } -void GraphicsContext::addPath(const Path& p) { +void GraphicsContext::addPath(const Path& p) +{ m_data->addPath(*p.platformPath()); } -void GraphicsContext::drawPath() { +void GraphicsContext::drawPath() +{ this->fillPath(); this->strokePath(); } -void GraphicsContext::fillPath() { +void GraphicsContext::fillPath() +{ SkPath* path = m_data->getPath(); if (paintingDisabled() || !path) return; @@ -1075,7 +1087,8 @@ void GraphicsContext::fillPath() { GC2Canvas(this)->drawPath(*path, paint); } -void GraphicsContext::strokePath() { +void GraphicsContext::strokePath() +{ const SkPath* path = m_data->getPath(); if (paintingDisabled() || !path || strokeStyle() == NoStroke) return; @@ -1112,7 +1125,8 @@ void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) /////////////////////////////////////////////////////////////////////////////// -SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc) { +SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc) +{ return gc->platformContext()->mCanvas; } diff --git a/WebCore/platform/graphics/android/ImageAndroid.cpp b/WebCore/platform/graphics/android/ImageAndroid.cpp index 04235d5..da52d67 100644 --- a/WebCore/platform/graphics/android/ImageAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageAndroid.cpp @@ -25,7 +25,7 @@ */ #include "config.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "BitmapImage.h" #include "Image.h" #include "FloatRect.h" @@ -59,14 +59,19 @@ android::AssetManager* globalAssetManager() { namespace WebCore { -void FrameData::clear() +bool FrameData::clear(bool clearMetadata) { + if (clearMetadata) + m_haveMetadata = false; + if (m_frame) { m_frame->unref(); m_frame = 0; m_duration = 0.; m_hasAlpha = true; + return true; } + return false; } BitmapImage::BitmapImage(SkBitmapRef* ref, ImageObserver* observer) @@ -211,7 +216,7 @@ void BitmapImage::setURL(const String& str) /////////////////////////////////////////////////////////////////////////////// void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, - const AffineTransform& patternTransform, + const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect) { diff --git a/WebCore/platform/graphics/android/ImageBufferAndroid.cpp b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp index de88b33..7b3e8e8 100644 --- a/WebCore/platform/graphics/android/ImageBufferAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp @@ -99,8 +99,9 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const return 0; } - PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); - unsigned char* data = result->data()->data().data(); + // ! Can't use PassRefPtr<>, otherwise the second access will cause crash. + RefPtr<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()); @@ -190,7 +191,7 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, con unsigned srcBytesPerRow = 4 * source->width(); unsigned dstPixelsPerRow = dst.rowBytesAsPixels(); - unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; + unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4; SkPMColor* dstRows = dst.getAddr32(destx, desty); for (int y = 0; y < numRows; ++y) { for (int x = 0; x < numColumns; x++) { diff --git a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp index a6bf6c6..c8fe8dd 100644 --- a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp @@ -347,7 +347,7 @@ bool ImageSource::frameIsCompleteAtIndex(size_t index) return m_decoder.m_image && m_decoder.m_image->fAllDataReceived; } -void ImageSource::clear() +void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) { // do nothing, since the cache is managed elsewhere } @@ -358,4 +358,10 @@ IntSize ImageSource::frameSizeAtIndex(size_t index) const return this->size(); } +String ImageSource::filenameExtension() const +{ + // FIXME: need to add virtual to our decoders to return "jpg/png/gif/..." + return String(); +} + } diff --git a/WebCore/platform/graphics/android/PathAndroid.cpp b/WebCore/platform/graphics/android/PathAndroid.cpp index 819173b..cfcd2bc 100644 --- a/WebCore/platform/graphics/android/PathAndroid.cpp +++ b/WebCore/platform/graphics/android/PathAndroid.cpp @@ -26,7 +26,10 @@ #include "config.h" #include "Path.h" #include "FloatRect.h" -#include "AffineTransform.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "StrokeStyleApplier.h" +#include "TransformationMatrix.h" #include "SkPath.h" #include "SkRegion.h" @@ -260,9 +263,54 @@ void Path::apply(void* info, PathApplierFunction function) const } } -void Path::transform(const AffineTransform& xform) +void Path::transform(const TransformationMatrix& xform) { m_path->transform(xform); } + +/////////////////////////////////////////////////////////////////////////////// + +// Computes the bounding box for the stroke and style currently selected into +// the given bounding box. This also takes into account the stroke width. +static FloatRect boundingBoxForCurrentStroke(GraphicsContext* context) +{ + const SkPath* path = context->getCurrPath(); + if (NULL == path) { + return FloatRect(); + } + + SkPaint paint; + context->setupStrokePaint(&paint); + SkPath fillPath; + paint.getFillPath(*path, &fillPath); + SkRect r; + fillPath.computeBounds(&r, SkPath::kExact_BoundsType); + return FloatRect(r.fLeft, r.fTop, r.width(), r.height()); +} + +static GraphicsContext* scratchContext() +{ + static ImageBuffer* scratch = 0; + if (!scratch) + scratch = ImageBuffer::create(IntSize(1, 1), false).release(); + // We don't bother checking for failure creating the ImageBuffer, since our + // ImageBuffer initializer won't fail. + return scratch->context(); +} + +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + GraphicsContext* scratch = scratchContext(); + scratch->save(); + scratch->beginPath(); + scratch->addPath(*this); + + if (applier) + applier->strokeStyle(scratch); + + FloatRect r = boundingBoxForCurrentStroke(scratch); + scratch->restore(); + return r; +} } diff --git a/WebCore/platform/graphics/android/PatternAndroid.cpp b/WebCore/platform/graphics/android/PatternAndroid.cpp index 2840faa..ffdbbb1 100644 --- a/WebCore/platform/graphics/android/PatternAndroid.cpp +++ b/WebCore/platform/graphics/android/PatternAndroid.cpp @@ -40,7 +40,7 @@ static SkShader::TileMode toTileMode(bool doRepeat) { return doRepeat ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; } -SkShader* Pattern::createPlatformPattern(const AffineTransform& transform) const +SkShader* Pattern::createPlatformPattern(const TransformationMatrix& transform) const { SkBitmapRef* ref = tileImage()->nativeImageForCurrentFrame(); SkShader* s = SkShader::CreateBitmapShader(ref->bitmap(), diff --git a/WebCore/platform/graphics/android/AffineTransformAndroid.cpp b/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp index c01d078..154d4f3 100644 --- a/WebCore/platform/graphics/android/AffineTransformAndroid.cpp +++ b/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp @@ -24,7 +24,7 @@ */ #include "config.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatRect.h" #include "IntRect.h" @@ -35,14 +35,14 @@ namespace WebCore { static const double deg2rad = 0.017453292519943295769; // pi/180 -AffineTransform::AffineTransform() +TransformationMatrix::TransformationMatrix() { m_transform.reset(); } -AffineTransform::AffineTransform(const SkMatrix& mat) : m_transform(mat) {} +TransformationMatrix::TransformationMatrix(const SkMatrix& mat) : m_transform(mat) {} -AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) +TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) { m_transform.reset(); @@ -55,7 +55,7 @@ AffineTransform::AffineTransform(double a, double b, double c, double d, double m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(ty)); } -void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) +void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) { m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); @@ -66,150 +66,164 @@ void AffineTransform::setMatrix(double a, double b, double c, double d, double t m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(ty)); } -void AffineTransform::map(double x, double y, double *x2, double *y2) const +void TransformationMatrix::map(double x, double y, double *x2, double *y2) const { - SkPoint src, dst; - src.set(SkDoubleToScalar(x), SkDoubleToScalar(y)); - m_transform.mapPoints(&dst, &src, 1); + SkPoint pt; - *x2 = SkScalarToDouble(dst.fX); - *y2 = SkScalarToDouble(dst.fY); + m_transform.mapXY(SkDoubleToScalar(x), SkDoubleToScalar(y), &pt); + *x2 = SkScalarToDouble(pt.fX); + *y2 = SkScalarToDouble(pt.fY); } -IntRect AffineTransform::mapRect(const IntRect &rect) const +IntRect TransformationMatrix::mapRect(const IntRect &rect) const { SkRect src, dst; SkIRect ir; android_setrect(&src, rect); m_transform.mapRect(&dst, src); - dst.round(&ir); + // we round out to mimic enclosingIntRect() + dst.roundOut(&ir); return IntRect(ir.fLeft, ir.fTop, ir.width(), ir.height()); } -FloatRect AffineTransform::mapRect(const FloatRect &rect) const +FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const { - SkRect src, dst; - SkIRect ir; + SkRect r; - android_setrect(&src, rect); - m_transform.mapRect(&dst, src); - dst.round(&ir); + android_setrect(&r, rect); + m_transform.mapRect(&r); - return IntRect(ir.fLeft, ir.fTop, ir.width(), ir.height()); + return FloatRect(r.fLeft, r.fTop, r.width(), r.height()); } -bool AffineTransform::isIdentity() const +bool TransformationMatrix::isIdentity() const { return m_transform.isIdentity(); } -void AffineTransform::reset() +void TransformationMatrix::reset() { m_transform.reset(); } - double AffineTransform::a() const { - return SkScalarToDouble(m_transform[0]); - } - void AffineTransform::setA(double a) { - m_transform.set(0, SkDoubleToScalar(a)); - } +double TransformationMatrix::a() const +{ + return SkScalarToDouble(m_transform[0]); +} - double AffineTransform::b() const { - return SkScalarToDouble(m_transform[1]); - } - void AffineTransform::setB(double b) { - m_transform.set(1, SkDoubleToScalar(b)); - } - - double AffineTransform::c() const { - return SkScalarToDouble(m_transform[3]); - } - void AffineTransform::setC(double c) { - m_transform.set(3, SkDoubleToScalar(c)); - } - - double AffineTransform::d() const { - return SkScalarToDouble(m_transform[4]); - } - void AffineTransform::setD(double d) { - m_transform.set(4, SkDoubleToScalar(d)); - } - - double AffineTransform::e() const { - return SkScalarToDouble(m_transform[2]); - } - void AffineTransform::setE(double e) { - m_transform.set(2, SkDoubleToScalar(e)); - } - - double AffineTransform::f() const { - return SkScalarToDouble(m_transform[5]); - } - void AffineTransform::setF(double f) { - m_transform.set(5, SkDoubleToScalar(f)); - } +void TransformationMatrix::setA(double a) +{ + m_transform.set(0, SkDoubleToScalar(a)); +} + +double TransformationMatrix::b() const +{ + return SkScalarToDouble(m_transform[1]); +} + +void TransformationMatrix::setB(double b) +{ + m_transform.set(1, SkDoubleToScalar(b)); +} + +double TransformationMatrix::c() const +{ + return SkScalarToDouble(m_transform[3]); +} + +void TransformationMatrix::setC(double c) +{ + m_transform.set(3, SkDoubleToScalar(c)); +} + +double TransformationMatrix::d() const { + return SkScalarToDouble(m_transform[4]); +} + +void TransformationMatrix::setD(double d) +{ + m_transform.set(4, SkDoubleToScalar(d)); +} + +double TransformationMatrix::e() const +{ + return SkScalarToDouble(m_transform[2]); +} + +void TransformationMatrix::setE(double e) +{ + m_transform.set(2, SkDoubleToScalar(e)); +} + +double TransformationMatrix::f() const { + return SkScalarToDouble(m_transform[5]); +} +void TransformationMatrix::setF(double f) { + m_transform.set(5, SkDoubleToScalar(f)); +} -AffineTransform &AffineTransform::scale(double sx, double sy) +TransformationMatrix &TransformationMatrix::scale(double sx, double sy) { m_transform.preScale(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); return *this; } -AffineTransform &AffineTransform::rotate(double d) +TransformationMatrix &TransformationMatrix::rotate(double d) { m_transform.preRotate(SkDoubleToScalar(d)); return *this; } -AffineTransform &AffineTransform::translate(double tx, double ty) +TransformationMatrix &TransformationMatrix::translate(double tx, double ty) { m_transform.preTranslate(SkDoubleToScalar(tx), SkDoubleToScalar(ty)); return *this; } -AffineTransform &AffineTransform::shear(double sx, double sy) +TransformationMatrix &TransformationMatrix::shear(double sx, double sy) { m_transform.preSkew(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); return *this; } -double AffineTransform::det() const +double TransformationMatrix::det() const { return SkScalarToDouble(m_transform[SkMatrix::kMScaleX]) * SkScalarToDouble(m_transform[SkMatrix::kMScaleY]) - SkScalarToDouble(m_transform[SkMatrix::kMSkewX]) * SkScalarToDouble(m_transform[SkMatrix::kMSkewY]); } -AffineTransform AffineTransform::inverse() const +TransformationMatrix TransformationMatrix::inverse() const { - AffineTransform inverse; + // the constructor initializes inverse to the identity + TransformationMatrix inverse; + // if we are not invertible, inverse will stay identity m_transform.invert(&inverse.m_transform); return inverse; } -AffineTransform::operator SkMatrix() const +TransformationMatrix::operator SkMatrix() const { return m_transform; } -bool AffineTransform::operator==(const AffineTransform &m2) const +bool TransformationMatrix::operator==(const TransformationMatrix &m2) const { return m_transform == m2.m_transform; } -AffineTransform &AffineTransform::operator*= (const AffineTransform &m2) +TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) { m_transform.setConcat(m2.m_transform, m_transform); return *this; } -AffineTransform AffineTransform::operator* (const AffineTransform &m2) +TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) { - AffineTransform cat; + TransformationMatrix cat; cat.m_transform.setConcat(m2.m_transform, m_transform); return cat; diff --git a/WebCore/platform/graphics/android/android_graphics.cpp b/WebCore/platform/graphics/android/android_graphics.cpp index 2bc0c8f..1decfd8 100644 --- a/WebCore/platform/graphics/android/android_graphics.cpp +++ b/WebCore/platform/graphics/android/android_graphics.cpp @@ -98,7 +98,7 @@ static const struct CompositOpToPorterDuffMode { uint8_t mPorterDuffMode; } gMapCompositOpsToPorterDuffModes[] = { { WebCore::CompositeClear, SkPorterDuff::kClear_Mode }, - { WebCore::CompositeCopy, SkPorterDuff::kSrcOver_Mode }, // TODO + { WebCore::CompositeCopy, SkPorterDuff::kSrc_Mode }, { WebCore::CompositeSourceOver, SkPorterDuff::kSrcOver_Mode }, { WebCore::CompositeSourceIn, SkPorterDuff::kSrcIn_Mode }, { WebCore::CompositeSourceOut, SkPorterDuff::kSrcOut_Mode }, diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index c403f44..ef748cf 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -31,7 +31,7 @@ #if PLATFORM(CAIRO) -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "CairoPath.h" #include "FloatRect.h" #include "Font.h" @@ -62,6 +62,15 @@ namespace WebCore { +static const unsigned aquaFocusRingColor = 0xFF7DADD9; + +Color focusRingColor() +{ + static Color focusRingColor = aquaFocusRingColor; + + return focusRingColor; +} + static inline void setColor(cairo_t* cr, const Color& col) { float red, green, blue, alpha; @@ -90,9 +99,6 @@ static inline cairo_pattern_t* applySpreadMethod(cairo_pattern_t* pattern, Gradi case SpreadMethodRepeat: cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); break; - default: - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE); - break; } return pattern; } @@ -111,7 +117,7 @@ GraphicsContext::~GraphicsContext() delete m_data; } -AffineTransform GraphicsContext::getCTM() const +TransformationMatrix GraphicsContext::getCTM() const { cairo_t* cr = platformContext(); cairo_matrix_t m; @@ -444,19 +450,19 @@ void GraphicsContext::fillPath() cairo_set_fill_rule(cr, fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); switch (m_common->state.fillColorSpace) { case SolidColorSpace: - if (fillColor().alpha()) { - setColor(cr, fillColor()); - cairo_clip(cr); - cairo_paint_with_alpha(cr, m_common->state.globalAlpha); - } + setColor(cr, fillColor()); + cairo_clip(cr); + cairo_paint_with_alpha(cr, m_common->state.globalAlpha); break; - case PatternColorSpace: - cairo_set_source(cr, m_common->state.fillPattern.get()->createPlatformPattern(getCTM())); + case PatternColorSpace: { + TransformationMatrix affine; + cairo_set_source(cr, m_common->state.fillPattern->createPlatformPattern(affine)); cairo_clip(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); break; + } case GradientColorSpace: - cairo_pattern_t* pattern = m_common->state.fillGradient.get()->platformGradient(); + cairo_pattern_t* pattern = m_common->state.fillGradient->platformGradient(); pattern = applySpreadMethod(pattern, spreadMethod()); cairo_set_source(cr, pattern); cairo_clip(cr); @@ -475,18 +481,16 @@ void GraphicsContext::strokePath() cairo_save(cr); switch (m_common->state.strokeColorSpace) { case SolidColorSpace: - if (strokeColor().alpha()) { - setColor(cr, strokeColor()); - if (m_common->state.globalAlpha < 1.0f) { - cairo_push_group(cr); - cairo_paint_with_alpha(cr, m_common->state.globalAlpha); - cairo_pop_group_to_source(cr); - } - cairo_stroke(cr); - } + float red, green, blue, alpha; + strokeColor().getRGBA(red, green, blue, alpha); + if (m_common->state.globalAlpha < 1.0f) + alpha *= m_common->state.globalAlpha; + cairo_set_source_rgba(cr, red, green, blue, alpha); + cairo_stroke(cr); break; - case PatternColorSpace: - cairo_set_source(cr, m_common->state.strokePattern.get()->createPlatformPattern(getCTM())); + case PatternColorSpace: { + TransformationMatrix affine; + cairo_set_source(cr, m_common->state.strokePattern->createPlatformPattern(affine)); if (m_common->state.globalAlpha < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); @@ -494,8 +498,9 @@ void GraphicsContext::strokePath() } cairo_stroke(cr); break; + } case GradientColorSpace: - cairo_pattern_t* pattern = m_common->state.strokeGradient.get()->platformGradient(); + cairo_pattern_t* pattern = m_common->state.strokeGradient->platformGradient(); pattern = applySpreadMethod(pattern, spreadMethod()); cairo_set_source(cr, pattern); if (m_common->state.globalAlpha < 1.0f) { @@ -548,6 +553,16 @@ void GraphicsContext::clip(const FloatRect& rect) m_data->clip(rect); } +void GraphicsContext::clipPath(WindRule clipRule) +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); + cairo_clip(cr); +} + void GraphicsContext::drawFocusRing(const Color& color) { if (paintingDisabled()) @@ -729,7 +744,7 @@ void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) notImplemented(); } -void GraphicsContext::concatCTM(const AffineTransform& transform) +void GraphicsContext::concatCTM(const TransformationMatrix& transform) { if (paintingDisabled()) return; @@ -1079,7 +1094,7 @@ GdkDrawable* GraphicsContext::gdkDrawable() const } #endif -void GraphicsContext::setUseAntialiasing(bool enable) +void GraphicsContext::setPlatformShouldAntialias(bool enable) { if (paintingDisabled()) return; diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index 9a14555..535f70d 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -69,7 +69,7 @@ public: void scale(const FloatSize&); void rotate(float); void translate(float, float); - void concatCTM(const AffineTransform&); + void concatCTM(const TransformationMatrix&); void beginTransparencyLayer() { m_transparencyCount++; } void endTransparencyLayer() { m_transparencyCount--; } #else @@ -81,7 +81,7 @@ public: void scale(const FloatSize&) {} void rotate(float) {} void translate(float, float) {} - void concatCTM(const AffineTransform&) {} + void concatCTM(const TransformationMatrix&) {} void beginTransparencyLayer() {} void endTransparencyLayer() {} #endif diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 5f65ed2..3e06669 100644 --- a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -94,7 +94,7 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface); - unsigned char* dataDst = result->data()->data().data(); + unsigned char* dataDst = 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(dataSrc, 0, result->data()->length()); @@ -179,7 +179,7 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, con unsigned srcBytesPerRow = 4 * source->width(); int stride = cairo_image_surface_get_stride(m_data.m_surface); - unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; + unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4; for (int y = 0; y < numRows; ++y) { unsigned char *row = dataDst + stride * (y + desty); for (int x = 0; x < numColumns; x++) { diff --git a/WebCore/platform/graphics/cairo/ImageCairo.cpp b/WebCore/platform/graphics/cairo/ImageCairo.cpp index 0a35cf2..2850488 100644 --- a/WebCore/platform/graphics/cairo/ImageCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageCairo.cpp @@ -29,7 +29,7 @@ #if PLATFORM(CAIRO) -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatRect.h" #include "GraphicsContext.h" #include "ImageObserver.h" @@ -38,15 +38,17 @@ namespace WebCore { -void FrameData::clear() +bool FrameData::clear(bool clearMetadata) { + if (clearMetadata) + m_haveMetadata = false; + if (m_frame) { cairo_surface_destroy(m_frame); m_frame = 0; - // 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. + return true; } + return false; } BitmapImage::BitmapImage(cairo_surface_t* surface, ImageObserver* observer) @@ -139,7 +141,7 @@ void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const Flo imageObserver()->didDraw(this); } -void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const AffineTransform& patternTransform, +void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { cairo_surface_t* image = nativeImageForCurrentFrame(); diff --git a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp index b7a4cbb..6841599 100644 --- a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp @@ -97,13 +97,21 @@ ImageSource::ImageSource() ImageSource::~ImageSource() { - clear(); + clear(true); } -void ImageSource::clear() +void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) { + if (!destroyAll) { + if (m_decoder) + m_decoder->clearFrameBufferCache(clearBeforeFrame); + return; + } + delete m_decoder; m_decoder = 0; + if (data) + setData(data, allDataReceived); } bool ImageSource::initialized() const @@ -126,6 +134,14 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) m_decoder->setData(data, allDataReceived); } +String ImageSource::filenameExtension() const +{ + if (!m_decoder) + return String(); + + return m_decoder->filenameExtension(); +} + bool ImageSource::isSizeAvailable() { if (!m_decoder) diff --git a/WebCore/platform/graphics/cairo/PathCairo.cpp b/WebCore/platform/graphics/cairo/PathCairo.cpp index 3f8d588..24354d2 100644 --- a/WebCore/platform/graphics/cairo/PathCairo.cpp +++ b/WebCore/platform/graphics/cairo/PathCairo.cpp @@ -4,6 +4,7 @@ 2004, 2005, 2006 Rob Buis <buis@kde.org> 2005, 2007 Apple Inc. All Rights reserved. 2007 Alp Toker <alp@atoker.com> + 2008 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -24,11 +25,13 @@ #include "config.h" #include "Path.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "CairoPath.h" #include "FloatRect.h" +#include "GraphicsContext.h" #include "NotImplemented.h" #include "PlatformString.h" +#include "StrokeStyleApplier.h" #include <cairo.h> #include <math.h> @@ -153,9 +156,78 @@ void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlo void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) { - // FIXME: cairo_arc_to not yet in cairo see cairo.h - // cairo_arc_to(m_cr, p1.x(), p1.y(), p2.x(), p2.y()); - notImplemented(); + if (isEmpty()) + return; + + cairo_t* cr = platformPath()->m_cr; + + double x0, y0; + cairo_get_current_point(cr, &x0, &y0); + FloatPoint p0(x0, y0); + if ((p1.x() == p0.x() && p1.y() == p0.y()) || (p1.x() == p2.x() && p1.y() == p2.y()) || radius == 0.f) { + cairo_line_to(cr, p1.x(), p1.y()); + return; + } + + FloatPoint p1p0((p0.x() - p1.x()),(p0.y() - p1.y())); + FloatPoint p1p2((p2.x() - p1.x()),(p2.y() - p1.y())); + float p1p0_length = sqrtf(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); + float p1p2_length = sqrtf(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); + + double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); + // all points on a line logic + if (cos_phi == -1) { + cairo_line_to(cr, p1.x(), p1.y()); + return; + } + if (cos_phi == 1) { + // add infinite far away point + unsigned int max_length = 65535; + double factor_max = max_length / p1p0_length; + FloatPoint ep((p0.x() + factor_max * p1p0.x()), (p0.y() + factor_max * p1p0.y())); + cairo_line_to(cr, ep.x(), ep.y()); + return; + } + + float tangent = radius / tan(acos(cos_phi) / 2); + float factor_p1p0 = tangent / p1p0_length; + FloatPoint t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); + + FloatPoint orth_p1p0(p1p0.y(), -p1p0.x()); + float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); + float factor_ra = radius / orth_p1p0_length; + + // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 + double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); + if (cos_alpha < 0.f) + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + + FloatPoint p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); + + // calculate angles for addArc + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + float sa = acos(orth_p1p0.x() / orth_p1p0_length); + if (orth_p1p0.y() < 0.f) + sa = 2 * piDouble - sa; + + // anticlockwise logic + bool anticlockwise = false; + + float factor_p1p2 = tangent / p1p2_length; + FloatPoint t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); + FloatPoint orth_p1p2((t_p1p2.x() - p.x()),(t_p1p2.y() - p.y())); + float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); + float ea = acos(orth_p1p2.x() / orth_p1p2_length); + if (orth_p1p2.y() < 0) + ea = 2 * piDouble - ea; + if ((sa > ea) && ((sa - ea) < piDouble)) + anticlockwise = true; + if ((sa < ea) && ((ea - sa) > piDouble)) + anticlockwise = true; + + cairo_line_to(cr, t_p1p0.x(), t_p1p0.y()); + + addArc(p, radius, sa, ea, anticlockwise); } void Path::addEllipse(const FloatRect& rect) @@ -188,6 +260,19 @@ FloatRect Path::boundingRect() const return FloatRect(x0, y0, x1 - x0, y1 - y0); } +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + cairo_t* cr = platformPath()->m_cr; + if (applier) { + GraphicsContext gc(cr); + applier->strokeStyle(&gc); + } + + double x0, x1, y0, y1; + cairo_stroke_extents(cr, &x0, &y0, &x1, &y1); + return FloatRect(x0, y0, x1 - x0, y1 - y0); +} + bool Path::contains(const FloatPoint& point, WindRule rule) const { if (!boundingRect().contains(point)) @@ -201,6 +286,16 @@ bool Path::contains(const FloatPoint& point, WindRule rule) const return contains; } +bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const +{ + ASSERT(applier); + cairo_t* cr = platformPath()->m_cr; + GraphicsContext gc(cr); + applier->strokeStyle(&gc); + + return cairo_in_stroke(cr, point.x(), point.y()); +} + void Path::apply(void* info, PathApplierFunction function) const { cairo_t* cr = platformPath()->m_cr; @@ -239,7 +334,7 @@ void Path::apply(void* info, PathApplierFunction function) const cairo_path_destroy(path); } -void Path::transform(const AffineTransform& trans) +void Path::transform(const TransformationMatrix& trans) { cairo_t* m_cr = platformPath()->m_cr; cairo_matrix_t c_matrix = cairo_matrix_t(trans); @@ -249,37 +344,39 @@ void Path::transform(const AffineTransform& trans) String Path::debugString() const { - String string = ""; + if (isEmpty()) + return String(); + + String pathString; cairo_path_t* path = cairo_copy_path(platformPath()->m_cr); cairo_path_data_t* data; - if (!path->num_data ) - string = "EMPTY"; - for (int i = 0; i < path->num_data; i += path->data[i].header.length) { data = &path->data[i]; switch (data->header.type) { case CAIRO_PATH_MOVE_TO: - string += String::format("M %.2f,%.2f", + if (i < (path->num_data - path->data[i].header.length)) + pathString += String::format("M%.2f,%.2f ", data[1].point.x, data[1].point.y); break; case CAIRO_PATH_LINE_TO: - string += String::format("L %.2f,%.2f", + pathString += String::format("L%.2f,%.2f ", data[1].point.x, data[1].point.y); break; case CAIRO_PATH_CURVE_TO: - string += String::format("C %.2f,%.2f,%.2f,%.2f,%.2f,%.2f", + pathString += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ", data[1].point.x, data[1].point.y, data[2].point.x, data[2].point.y, data[3].point.x, data[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: - string += "X"; + pathString += "Z "; break; } } + cairo_path_destroy(path); - return string; + return pathString.simplifyWhiteSpace(); } } // namespace WebCore diff --git a/WebCore/platform/graphics/cairo/PatternCairo.cpp b/WebCore/platform/graphics/cairo/PatternCairo.cpp index 16cebf8..7d75db3 100644 --- a/WebCore/platform/graphics/cairo/PatternCairo.cpp +++ b/WebCore/platform/graphics/cairo/PatternCairo.cpp @@ -26,21 +26,22 @@ #include "config.h" #include "Pattern.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "GraphicsContext.h" #include <cairo.h> namespace WebCore { -cairo_pattern_t* Pattern::createPlatformPattern(const AffineTransform& patternTransform) const +cairo_pattern_t* Pattern::createPlatformPattern(const TransformationMatrix& patternTransform) const { cairo_surface_t* surface = tileImage()->nativeImageForCurrentFrame(); if (!surface) return 0; cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface); - const cairo_matrix_t* pattern_matrix = reinterpret_cast<const cairo_matrix_t*>(&patternTransform); + const TransformationMatrix& inverse = patternTransform.inverse(); + const cairo_matrix_t* pattern_matrix = reinterpret_cast<const cairo_matrix_t*>(&inverse); cairo_pattern_set_matrix(pattern, pattern_matrix); if (m_repeatX || m_repeatY) cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); diff --git a/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp b/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp index 0f2fccd..b78620f 100644 --- a/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp +++ b/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp @@ -23,7 +23,7 @@ */ #include "config.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "IntRect.h" #include "FloatRect.h" @@ -34,34 +34,34 @@ namespace WebCore { static const double deg2rad = 0.017453292519943295769; // pi/180 -AffineTransform::AffineTransform() +TransformationMatrix::TransformationMatrix() { cairo_matrix_init_identity(&m_transform); } -AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) +TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) { cairo_matrix_init(&m_transform, a, b, c, d, tx, ty); } -AffineTransform::AffineTransform(const PlatformAffineTransform& matrix) +TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) { m_transform = matrix; } -void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) +void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) { cairo_matrix_init(&m_transform, a, b, c, d, tx, ty); } -void AffineTransform::map(double x, double y, double* x2, double* y2) const +void TransformationMatrix::map(double x, double y, double* x2, double* y2) const { *x2 = x; *y2 = y; cairo_matrix_transform_point(&m_transform, x2, y2); } -IntRect AffineTransform::mapRect(const IntRect &rect) const +IntRect TransformationMatrix::mapRect(const IntRect &rect) const { FloatRect floatRect(rect); FloatRect enclosingFloatRect = this->mapRect(floatRect); @@ -69,7 +69,7 @@ IntRect AffineTransform::mapRect(const IntRect &rect) const return enclosingIntRect(enclosingFloatRect); } -FloatRect AffineTransform::mapRect(const FloatRect &rect) const +FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const { double rectMinX = rect.x(); double rectMaxX = rect.x() + rect.width(); @@ -128,97 +128,97 @@ FloatRect AffineTransform::mapRect(const FloatRect &rect) const return FloatRect(enclosingRectMinX, enclosingRectMinY, enclosingRectWidth, enclosingRectHeight); } -bool AffineTransform::isIdentity() const +bool TransformationMatrix::isIdentity() const { return ((m_transform.xx == 1) && (m_transform.yy == 1) && (m_transform.xy == 0) && (m_transform.yx == 0) && (m_transform.x0 == 0) && (m_transform.y0 == 0)); } -double AffineTransform::a() const +double TransformationMatrix::a() const { return m_transform.xx; } -void AffineTransform::setA(double a) +void TransformationMatrix::setA(double a) { m_transform.xx = a; } -double AffineTransform::b() const +double TransformationMatrix::b() const { return m_transform.yx; } -void AffineTransform::setB(double b) +void TransformationMatrix::setB(double b) { m_transform.yx = b; } -double AffineTransform::c() const +double TransformationMatrix::c() const { return m_transform.xy; } -void AffineTransform::setC(double c) +void TransformationMatrix::setC(double c) { m_transform.xy = c; } -double AffineTransform::d() const +double TransformationMatrix::d() const { return m_transform.yy; } -void AffineTransform::setD(double d) +void TransformationMatrix::setD(double d) { m_transform.yy = d; } -double AffineTransform::e() const +double TransformationMatrix::e() const { return m_transform.x0; } -void AffineTransform::setE(double e) +void TransformationMatrix::setE(double e) { m_transform.x0 = e; } -double AffineTransform::f() const +double TransformationMatrix::f() const { return m_transform.y0; } -void AffineTransform::setF(double f) +void TransformationMatrix::setF(double f) { m_transform.y0 = f; } -void AffineTransform::reset() +void TransformationMatrix::reset() { cairo_matrix_init_identity(&m_transform); } -AffineTransform &AffineTransform::scale(double sx, double sy) +TransformationMatrix &TransformationMatrix::scale(double sx, double sy) { cairo_matrix_scale(&m_transform, sx, sy); return *this; } -AffineTransform &AffineTransform::rotate(double d) +TransformationMatrix &TransformationMatrix::rotate(double d) { cairo_matrix_rotate(&m_transform, d * deg2rad); return *this; } -AffineTransform &AffineTransform::translate(double tx, double ty) +TransformationMatrix &TransformationMatrix::translate(double tx, double ty) { cairo_matrix_translate(&m_transform, tx, ty); return *this; } -AffineTransform &AffineTransform::shear(double sx, double sy) +TransformationMatrix &TransformationMatrix::shear(double sx, double sy) { cairo_matrix_t shear; cairo_matrix_init(&shear, 1, sy, sx, 1, 0, 0); @@ -230,26 +230,26 @@ AffineTransform &AffineTransform::shear(double sx, double sy) return *this; } -double AffineTransform::det() const +double TransformationMatrix::det() const { return m_transform.xx * m_transform.yy - m_transform.xy * m_transform.yx; } -AffineTransform AffineTransform::inverse() const +TransformationMatrix TransformationMatrix::inverse() const { - if (!isInvertible()) return AffineTransform(); + if (!isInvertible()) return TransformationMatrix(); cairo_matrix_t result = m_transform; cairo_matrix_invert(&result); - return AffineTransform(result); + return TransformationMatrix(result); } -AffineTransform::operator cairo_matrix_t() const +TransformationMatrix::operator cairo_matrix_t() const { return m_transform; } -bool AffineTransform::operator== (const AffineTransform &m2) const +bool TransformationMatrix::operator== (const TransformationMatrix &m2) const { return ((m_transform.xx == m2.m_transform.xx) && (m_transform.yy == m2.m_transform.yy) @@ -260,7 +260,7 @@ bool AffineTransform::operator== (const AffineTransform &m2) const } -AffineTransform &AffineTransform::operator*= (const AffineTransform &m2) +TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) { cairo_matrix_t result; cairo_matrix_multiply(&result, &m_transform, &m2.m_transform); @@ -269,7 +269,7 @@ AffineTransform &AffineTransform::operator*= (const AffineTransform &m2) return *this; } -AffineTransform AffineTransform::operator* (const AffineTransform &m2) +TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) { cairo_matrix_t result; cairo_matrix_multiply(&result, &m_transform, &m2.m_transform); diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index 3f0e6e7..1cc55a4 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -28,7 +28,7 @@ #include "config.h" #include "GraphicsContext.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatConversion.h" #include "GraphicsContextPrivate.h" #include "GraphicsContextPlatformPrivateCG.h" @@ -191,11 +191,13 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) } CGContextRef context = platformContext(); - CGContextSaveGState(context); - - CGContextSetShouldAntialias(context, false); + + 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()); // The save/restore make it safe to mutate the fill color here without setting it back to the old color. @@ -245,7 +247,11 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) CGContextStrokePath(context); - CGContextRestoreGState(context); + if (patWidth) + CGContextRestoreGState(context); + + if (shouldAntialias()) + CGContextSetShouldAntialias(context, true); } // This method is only used to draw the little circles used in lists. @@ -358,7 +364,7 @@ void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSp CGContextRestoreGState(context); } -void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) +void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool antialiased) { if (paintingDisabled() || !fillColor().alpha() && (strokeThickness() <= 0 || strokeStyle() == NoStroke)) return; @@ -368,9 +374,8 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points CGContextRef context = platformContext(); - CGContextSaveGState(context); - - CGContextSetShouldAntialias(context, shouldAntialias); + if (antialiased != shouldAntialias()) + CGContextSetShouldAntialias(context, antialiased); CGContextBeginPath(context); CGContextMoveToPoint(context, points[0].x(), points[0].y()); @@ -379,15 +384,16 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points CGContextClosePath(context); drawPath(); - - CGContextRestoreGState(context); + + if (antialiased != shouldAntialias()) + CGContextSetShouldAntialias(context, shouldAntialias()); } -static void applyStrokePattern(GraphicsContext* context, Pattern* pattern) +void GraphicsContext::applyStrokePattern() { - CGContextRef cgContext = context->platformContext(); + CGContextRef cgContext = platformContext(); - CGPatternRef platformPattern = pattern->createPlatformPattern(context->getCTM()); + CGPatternRef platformPattern = m_common->state.strokePattern.get()->createPlatformPattern(getCTM()); if (!platformPattern) return; @@ -400,11 +406,11 @@ static void applyStrokePattern(GraphicsContext* context, Pattern* pattern) CGPatternRelease(platformPattern); } -static void applyFillPattern(GraphicsContext* context, Pattern* pattern) +void GraphicsContext::applyFillPattern() { - CGContextRef cgContext = context->platformContext(); + CGContextRef cgContext = platformContext(); - CGPatternRef platformPattern = pattern->createPlatformPattern(context->getCTM()); + CGPatternRef platformPattern = m_common->state.fillPattern.get()->createPlatformPattern(getCTM()); if (!platformPattern) return; @@ -460,9 +466,9 @@ void GraphicsContext::drawPath() } if (state.fillColorSpace == PatternColorSpace) - applyFillPattern(this, m_common->state.fillPattern.get()); + applyFillPattern(); if (state.strokeColorSpace == PatternColorSpace) - applyStrokePattern(this, m_common->state.strokePattern.get()); + applyStrokePattern(); CGPathDrawingMode drawingMode; if (calculateDrawingMode(state, drawingMode)) @@ -489,7 +495,7 @@ void GraphicsContext::fillPath() fillPathWithFillRule(context, fillRule()); break; case PatternColorSpace: - applyFillPattern(this, m_common->state.fillPattern.get()); + applyFillPattern(); fillPathWithFillRule(context, fillRule()); break; case GradientColorSpace: @@ -512,11 +518,11 @@ void GraphicsContext::strokePath() CGContextRef context = platformContext(); switch (m_common->state.strokeColorSpace) { case SolidColorSpace: - if (fillColor().alpha()) + if (strokeColor().alpha()) CGContextStrokePath(context); break; case PatternColorSpace: - applyStrokePattern(this, m_common->state.strokePattern.get()); + applyStrokePattern(); CGContextStrokePath(context); break; case GradientColorSpace: @@ -540,7 +546,7 @@ void GraphicsContext::fillRect(const FloatRect& rect) CGContextFillRect(context, rect); break; case PatternColorSpace: - applyFillPattern(this, m_common->state.fillPattern.get()); + applyFillPattern(); CGContextFillRect(context, rect); break; case GradientColorSpace: @@ -614,6 +620,21 @@ void GraphicsContext::clipOutEllipseInRect(const IntRect& rect) CGContextEOClip(platformContext()); } +void GraphicsContext::clipPath(WindRule clipRule) +{ + if (paintingDisabled()) + return; + + CGContextRef context = platformContext(); + + if (!CGContextIsPathEmpty(context)) { + if (clipRule == RULE_EVENODD) + CGContextEOClip(context); + else + CGContextClip(context); + } +} + void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) { if (paintingDisabled()) @@ -670,23 +691,30 @@ void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Col { if (paintingDisabled()) return; + CGFloat width = size.width(); + CGFloat height = size.height(); + CGFloat blurRadius = blur; 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; + if (!m_common->state.shadowsIgnoreTransforms) { + CGAffineTransform transform = CGContextGetCTM(context); - CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D))))); + 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; - // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp - CGFloat blurRadius = min(blur * smallEigenvalue, narrowPrecisionToCGFloat(1000.0)); + CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D))))); - CGSize sizeInDeviceSpace = CGSizeApplyAffineTransform(size, transform); + // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp + blurRadius = min(blur * smallEigenvalue, narrowPrecisionToCGFloat(1000.0)); - CGFloat width = sizeInDeviceSpace.width; - CGFloat height = sizeInDeviceSpace.height; + CGSize sizeInDeviceSpace = CGSizeApplyAffineTransform(size, transform); + + width = sizeInDeviceSpace.width; + height = sizeInDeviceSpace.height; + + } // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated // to the desired integer. @@ -747,7 +775,27 @@ void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth) { if (paintingDisabled()) return; - CGContextStrokeRectWithWidth(platformContext(), r, lineWidth); + + CGContextRef context = platformContext(); + switch (m_common->state.strokeColorSpace) { + case SolidColorSpace: + if (strokeColor().alpha()) + CGContextStrokeRectWithWidth(context, r, lineWidth); + break; + case PatternColorSpace: + applyStrokePattern(); + CGContextStrokeRectWithWidth(context, r, lineWidth); + break; + case GradientColorSpace: + CGContextSaveGState(context); + setStrokeThickness(lineWidth); + CGContextAddRect(context, r); + CGContextReplacePathWithStrokedPath(context); + CGContextClip(context); + CGContextDrawShading(context, m_common->state.strokeGradient->platformGradient()); + CGContextRestoreGState(context); + break; + } } void GraphicsContext::setLineCap(LineCap cap) @@ -848,7 +896,7 @@ void GraphicsContext::translate(float x, float y) m_data->m_userToDeviceTransformKnownToBeIdentity = false; } -void GraphicsContext::concatCTM(const AffineTransform& transform) +void GraphicsContext::concatCTM(const TransformationMatrix& transform) { if (paintingDisabled()) return; @@ -857,7 +905,7 @@ void GraphicsContext::concatCTM(const AffineTransform& transform) m_data->m_userToDeviceTransformKnownToBeIdentity = false; } -AffineTransform GraphicsContext::getCTM() const +TransformationMatrix GraphicsContext::getCTM() const { return CGContextGetCTM(platformContext()); } @@ -909,8 +957,6 @@ void GraphicsContext::drawLineForText(const IntPoint& point, int width, bool pri if (width <= 0) return; - CGContextSaveGState(platformContext()); - float x = point.x(); float y = point.y(); float lineLength = width; @@ -919,6 +965,8 @@ void GraphicsContext::drawLineForText(const IntPoint& point, int width, bool pri // 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); @@ -933,15 +981,21 @@ void GraphicsContext::drawLineForText(const IntPoint& point, int width, bool pri y = lineRect.origin.y; lineLength = lineRect.size.width; thickness = lineRect.size.height; - CGContextSetShouldAntialias(platformContext(), false); + if (shouldAntialias()) { + CGContextSetShouldAntialias(platformContext(), false); + restoreAntialiasMode = true; + } } } if (fillColor() != strokeColor()) setCGFillColor(platformContext(), strokeColor()); CGContextFillRect(platformContext(), CGRectMake(x, y, lineLength, thickness)); - - CGContextRestoreGState(platformContext()); + if (fillColor() != strokeColor()) + setCGFillColor(platformContext(), fillColor()); + + if (restoreAntialiasMode) + CGContextSetShouldAntialias(platformContext(), true); } void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) @@ -1078,7 +1132,7 @@ void GraphicsContext::setPlatformFillColor(const Color& color) setCGFillColor(platformContext(), color); } -void GraphicsContext::setUseAntialiasing(bool enable) +void GraphicsContext::setPlatformShouldAntialias(bool enable) { if (paintingDisabled()) return; diff --git a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h index 8827ff7..beee660 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h +++ b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h @@ -34,6 +34,7 @@ public: #if PLATFORM(WIN) , m_hdc(0) , m_transparencyCount(0) + , m_shouldIncludeChildWindows(false) #endif , m_userToDeviceTransformKnownToBeIdentity(false) { @@ -54,7 +55,7 @@ public: void scale(const FloatSize&) {} void rotate(float) {} void translate(float, float) {} - void concatCTM(const AffineTransform&) {} + void concatCTM(const TransformationMatrix&) {} void beginTransparencyLayer() {} void endTransparencyLayer() {} #endif @@ -68,12 +69,13 @@ public: void scale(const FloatSize&); void rotate(float); void translate(float, float); - void concatCTM(const AffineTransform&); + void concatCTM(const TransformationMatrix&); void beginTransparencyLayer() { m_transparencyCount++; } void endTransparencyLayer() { m_transparencyCount--; } HDC m_hdc; unsigned m_transparencyCount; + bool m_shouldIncludeChildWindows; #endif CGContextRef m_cgContext; diff --git a/WebCore/platform/graphics/cg/ImageBufferCG.cpp b/WebCore/platform/graphics/cg/ImageBufferCG.cpp index 502313b..96e5604 100644 --- a/WebCore/platform/graphics/cg/ImageBufferCG.cpp +++ b/WebCore/platform/graphics/cg/ImageBufferCG.cpp @@ -107,7 +107,7 @@ Image* ImageBuffer::image() const PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const { PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); - unsigned char* data = result->data()->data().data(); + 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()); @@ -188,7 +188,7 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, con unsigned srcBytesPerRow = 4 * source->width(); unsigned destBytesPerRow = 4 * m_size.width(); - unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; + 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++) { diff --git a/WebCore/platform/graphics/cg/ImageCG.cpp b/WebCore/platform/graphics/cg/ImageCG.cpp index 8609c46..13c8c07 100644 --- a/WebCore/platform/graphics/cg/ImageCG.cpp +++ b/WebCore/platform/graphics/cg/ImageCG.cpp @@ -28,7 +28,7 @@ #if PLATFORM(CG) -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatConversion.h" #include "FloatRect.h" #include "GraphicsContext.h" @@ -47,15 +47,17 @@ namespace WebCore { -void FrameData::clear() +bool FrameData::clear(bool clearMetadata) { + if (clearMetadata) + m_haveMetadata = false; + if (m_frame) { CGImageRelease(m_frame); m_frame = 0; - // 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. + return true; } + return false; } // ================================================ @@ -207,7 +209,7 @@ void Image::drawPatternCallback(void* info, CGContextRef context) CGContextDrawImage(context, GraphicsContext(context).roundToDevicePixels(FloatRect(0, 0, CGImageGetWidth(image), CGImageGetHeight(image))), image); } -void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, +void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { if (!nativeImageForCurrentFrame()) @@ -251,9 +253,14 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const // its buffer is the same size as the overall image. Because a partially decoded CGImageRef with a smaller width or height than the // overall image buffer needs to tile with "gaps", we can't use the optimized tiling call in that case. // FIXME: Could create WebKitSystemInterface SPI for CGCreatePatternWithImage2 and probably make Tiger tile faster as well. + // FIXME: We cannot use CGContextDrawTiledImage with scaled tiles on Leopard, because it suffers from rounding errors. Snow Leopard is ok. float scaledTileWidth = tileRect.width() * narrowPrecisionToFloat(patternTransform.a()); float w = CGImageGetWidth(tileImage); +#ifdef BUILDING_ON_LEOPARD + if (w == size().width() && h == size().height() && scaledTileWidth == tileRect.width() && scaledTileHeight == tileRect.height()) +#else if (w == size().width() && h == size().height()) +#endif CGContextDrawTiledImage(context, FloatRect(adjustedX, adjustedY, scaledTileWidth, scaledTileHeight), subImage); else { #endif diff --git a/WebCore/platform/graphics/cg/ImageSourceCG.cpp b/WebCore/platform/graphics/cg/ImageSourceCG.cpp index 73907c9..0b276cc 100644 --- a/WebCore/platform/graphics/cg/ImageSourceCG.cpp +++ b/WebCore/platform/graphics/cg/ImageSourceCG.cpp @@ -27,8 +27,10 @@ #include "ImageSource.h" #if PLATFORM(CG) +#include "ImageSourceCG.h" #include "IntSize.h" +#include "MIMETypeRegistry.h" #include "SharedBuffer.h" #include <ApplicationServices/ApplicationServices.h> @@ -43,18 +45,23 @@ ImageSource::ImageSource() ImageSource::~ImageSource() { - clear(); + clear(true); } -void ImageSource::clear() +void ImageSource::clear(bool, size_t, SharedBuffer* data, bool allDataReceived) { + // We always destroy the decoder, because there is no API to get it to + // selectively release some of the frames it's holding, and if we don't + // release any of them, we use too much memory on large images. if (m_decoder) { CFRelease(m_decoder); m_decoder = 0; } + if (data) + setData(data, allDataReceived); } -CFDictionaryRef imageSourceOptions() +static CFDictionaryRef imageSourceOptions() { static CFDictionaryRef options; @@ -89,6 +96,14 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) CFRelease(cfData); } +String ImageSource::filenameExtension() const +{ + if (!m_decoder) + return String(); + CFStringRef imageSourceType = CGImageSourceGetType(m_decoder); + return WebCore::preferredExtensionForImageSourceType(imageSourceType); +} + bool ImageSource::isSizeAvailable() { bool result = false; @@ -210,7 +225,7 @@ float ImageSource::frameDurationAtIndex(size_t index) return duration; } -bool ImageSource::frameHasAlphaAtIndex(size_t index) +bool ImageSource::frameHasAlphaAtIndex(size_t) { // Might be interesting to do this optimization on Mac some day, but for now we're just using this // for the Cairo source, since it uses our decoders, and our decoders can answer this question. diff --git a/WebCore/platform/graphics/cg/ImageSourceCG.h b/WebCore/platform/graphics/cg/ImageSourceCG.h new file mode 100644 index 0000000..d5b4b5a --- /dev/null +++ b/WebCore/platform/graphics/cg/ImageSourceCG.h @@ -0,0 +1,41 @@ +/* + * 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 + * 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 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 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 ImageSourceCG_h +#define ImageSourceCG_h + +#include "ImageSource.h" + +namespace WebCore { + +class String; + +String preferredExtensionForImageSourceType(const String& type); + +String MIMETypeForImageSourceType(const String& type); + +} + +#endif // ImageSourceCG_h diff --git a/WebCore/platform/graphics/cg/ImageSourceCGMac.mm b/WebCore/platform/graphics/cg/ImageSourceCGMac.mm new file mode 100644 index 0000000..297e30a --- /dev/null +++ b/WebCore/platform/graphics/cg/ImageSourceCGMac.mm @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 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. + */ + +#import "config.h" +#import "ImageSourceCG.h" + +#import "PlatformString.h" +#import "wtf/RetainPtr.h" + +namespace WebCore { + +String MIMETypeForImageSourceType(const String& uti) +{ + RetainPtr<CFStringRef> utiref(AdoptCF, uti.createCFString()); + RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(utiref.get(), kUTTagClassMIMEType)); + return mime.get(); +} + +String preferredExtensionForImageSourceType(const String& uti) +{ + RetainPtr<CFStringRef> type(AdoptCF, uti.createCFString()); + RetainPtr<CFStringRef> extension(AdoptCF, UTTypeCopyPreferredTagWithClass(type.get(), kUTTagClassFilenameExtension)); + return extension.get(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/cg/ImageSourceCGWin.cpp b/WebCore/platform/graphics/cg/ImageSourceCGWin.cpp new file mode 100644 index 0000000..c7d9a0b --- /dev/null +++ b/WebCore/platform/graphics/cg/ImageSourceCGWin.cpp @@ -0,0 +1,84 @@ +/* + * 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 + * 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 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 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 "ImageSourceCG.h" + +#include "StdLibExtras.h" +#include "StringHash.h" +#include <wtf/HashMap.h> + +namespace WebCore { + +String MIMETypeForImageSourceType(const String& type) +{ + String mimeType; + // FIXME: This approach of taking a UTI like public.type and giving back + // a MIME type like image/type will work for common image UTIs like jpeg, + // png, tiff, gif but won't work for UTIs like: public.jpeg-2000, + // public.xbitmap-image, com.apple.quicktime-image, and others. + if (int dotLocation = type.reverseFind('.')) + mimeType = "image/" + type.substring(dotLocation + 1); + return mimeType; +} + +String preferredExtensionForImageSourceType(const String& type) +{ + if (type.isEmpty()) + return String(); + + typedef HashMap<String, String> StringMap; + DEFINE_STATIC_LOCAL(StringMap, UTIMap, ()); + if (UTIMap.isEmpty()) { + UTIMap.add("public.html", "html"); + UTIMap.add("public.jpeg", "jpeg"); + UTIMap.add("public.jpeg-2000", "jp2"); + UTIMap.add("public.plain-text", "txt"); + UTIMap.add("public.png", "png"); + UTIMap.add("public.tiff", "tiff"); + UTIMap.add("public.xbitmap-image", "xbm"); + UTIMap.add("public.xml", "xml"); + UTIMap.add("com.adobe.illustrator.ai-image", "ai"); + UTIMap.add("com.adobe.pdf", "pdf"); + UTIMap.add("com.adobe.photoshop-image", "psd"); + UTIMap.add("com.adobe.postscript", "ps"); + UTIMap.add("com.apple.icns", "icns"); + UTIMap.add("com.apple.macpaint-image", "pntg"); + UTIMap.add("com.apple.pict", "pict"); + UTIMap.add("com.apple.quicktime-image", "qtif"); + UTIMap.add("com.apple.webarchive", "webarchive"); + UTIMap.add("com.compuserve.gif", "gif"); + UTIMap.add("com.ilm.openexr-image", "exr"); + UTIMap.add("com.kodak.flashpix-image", "fpx"); + UTIMap.add("com.microsoft.bmp", "bmp"); + UTIMap.add("com.microsoft.ico", "ico"); + UTIMap.add("com.netscape.javascript-source", "js"); + UTIMap.add("com.sgi.sgi-image", "sgi"); + UTIMap.add("com.truevision.tga-image", "tga"); + } + return UTIMap.get(type); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/cg/PDFDocumentImage.h b/WebCore/platform/graphics/cg/PDFDocumentImage.h index 5c9d4e1..130c12c 100644 --- a/WebCore/platform/graphics/cg/PDFDocumentImage.h +++ b/WebCore/platform/graphics/cg/PDFDocumentImage.h @@ -42,7 +42,9 @@ namespace WebCore { { return adoptRef(new PDFDocumentImage); } - ~PDFDocumentImage(); + + private: + virtual ~PDFDocumentImage(); virtual bool hasSingleSecurityOrigin() const { return true; } @@ -50,12 +52,11 @@ namespace WebCore { // 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 void destroyDecodedData(bool /*destroyAll*/ = true) { } virtual unsigned decodedSize() const { return 0; } virtual IntSize size() const; - private: PDFDocumentImage(); virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); diff --git a/WebCore/platform/graphics/cg/PathCG.cpp b/WebCore/platform/graphics/cg/PathCG.cpp index 1382589..ebd0359 100644 --- a/WebCore/platform/graphics/cg/PathCG.cpp +++ b/WebCore/platform/graphics/cg/PathCG.cpp @@ -29,16 +29,43 @@ #if PLATFORM(CG) -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include <ApplicationServices/ApplicationServices.h> #include "FloatRect.h" +#include "GraphicsContext.h" #include "IntRect.h" #include "PlatformString.h" +#include "StrokeStyleApplier.h" #include <wtf/MathExtras.h> namespace WebCore { +static size_t putBytesNowhere(void*, const void*, size_t count) +{ + return count; +} + +static CGContextRef createScratchContext() +{ + CGDataConsumerCallbacks callbacks = { putBytesNowhere, 0 }; + CGDataConsumerRef consumer = CGDataConsumerCreate(0, &callbacks); + CGContextRef context = CGPDFContextCreate(consumer, 0, 0); + CGDataConsumerRelease(consumer); + + CGFloat black[4] = { 0, 0, 0, 1 }; + CGContextSetFillColor(context, black); + CGContextSetStrokeColor(context, black); + + return context; +} + +static inline CGContextRef scratchContext() +{ + static CGContextRef context = createScratchContext(); + return context; +} + Path::Path() : m_path(CGPathCreateMutable()) { @@ -62,7 +89,6 @@ Path& Path::operator=(const Path& other) return *this; } - static void copyClosingSubpathsApplierFunction(void* info, const CGPathElement* element) { CGMutablePathRef path = static_cast<CGMutablePathRef>(info); @@ -109,6 +135,25 @@ bool Path::contains(const FloatPoint &point, WindRule rule) const return ret; } +bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const +{ + ASSERT(applier); + + CGContextRef context = scratchContext(); + + CGContextSaveGState(context); + CGContextBeginPath(context); + CGContextAddPath(context, platformPath()); + + GraphicsContext gc(context); + applier->strokeStyle(&gc); + + bool hitSuccess = CGContextPathContainsPoint(context, point, kCGPathStroke); + CGContextRestoreGState(context); + + return hitSuccess; +} + void Path::translate(const FloatSize& size) { CGAffineTransform translation = CGAffineTransformMake(1, 0, 0, 1, size.width(), size.height()); @@ -123,6 +168,26 @@ FloatRect Path::boundingRect() const return CGPathGetBoundingBox(m_path); } +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + CGContextRef context = scratchContext(); + + CGContextSaveGState(context); + CGContextBeginPath(context); + CGContextAddPath(context, platformPath()); + + if (applier) { + GraphicsContext graphicsContext(context); + applier->strokeStyle(&graphicsContext); + } + + CGContextReplacePathWithStrokedPath(context); + CGRect box = CGContextIsPathEmpty(context) ? CGRectZero : CGContextGetPathBoundingBox(context); + CGContextRestoreGState(context); + + return box; +} + void Path::moveTo(const FloatPoint& point) { CGPathMoveToPoint(m_path, 0, point.x(), point.y()); @@ -184,8 +249,8 @@ bool Path::isEmpty() const static void CGPathToCFStringApplierFunction(void* info, const CGPathElement *element) { - CFMutableStringRef string = (CFMutableStringRef)info; - CFStringRef typeString = CFSTR(""); + CFMutableStringRef string = static_cast<CFMutableStringRef>(info); + CGPoint* points = element->points; switch (element->type) { case kCGPathElementMoveToPoint: @@ -204,7 +269,8 @@ static void CGPathToCFStringApplierFunction(void* info, const CGPathElement *ele points[2].x, points[2].y); break; case kCGPathElementCloseSubpath: - typeString = CFSTR("X"); break; + CFStringAppendFormat(string, 0, CFSTR("Z ")); + break; } } @@ -241,7 +307,7 @@ struct PathApplierInfo { PathApplierFunction function; }; -void CGPathApplierToPathApplier(void *info, const CGPathElement *element) +static void CGPathApplierToPathApplier(void *info, const CGPathElement *element) { PathApplierInfo* pinfo = (PathApplierInfo*)info; FloatPoint points[3]; @@ -277,7 +343,7 @@ void Path::apply(void* info, PathApplierFunction function) const CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier); } -void Path::transform(const AffineTransform& transform) +void Path::transform(const TransformationMatrix& transform) { CGMutablePathRef path = CGPathCreateMutable(); CGAffineTransform transformCG = transform; diff --git a/WebCore/platform/graphics/cg/PatternCG.cpp b/WebCore/platform/graphics/cg/PatternCG.cpp index e1f7a69..2b9c12f 100644 --- a/WebCore/platform/graphics/cg/PatternCG.cpp +++ b/WebCore/platform/graphics/cg/PatternCG.cpp @@ -27,7 +27,7 @@ #include "config.h" #include "Pattern.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "GraphicsContext.h" #include <ApplicationServices/ApplicationServices.h> @@ -50,11 +50,11 @@ static void patternReleaseCallback(void* info) static_cast<Image*>(info)->deref(); } -CGPatternRef Pattern::createPlatformPattern(const AffineTransform& transform) const +CGPatternRef Pattern::createPlatformPattern(const TransformationMatrix& transform) const { IntRect tileRect = tileImage()->rect(); - AffineTransform patternTransform = transform; + TransformationMatrix patternTransform = transform; patternTransform.scale(1, -1); patternTransform.translate(0, -tileRect.height()); diff --git a/WebCore/platform/graphics/cg/AffineTransformCG.cpp b/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp index 4f0bca0..9b3181a 100644 --- a/WebCore/platform/graphics/cg/AffineTransformCG.cpp +++ b/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp @@ -24,7 +24,7 @@ */ #include "config.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #if PLATFORM(CG) @@ -36,12 +36,12 @@ namespace WebCore { -AffineTransform::AffineTransform() +TransformationMatrix::TransformationMatrix() : m_transform(CGAffineTransformIdentity) { } -AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) +TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) { m_transform = CGAffineTransformMake(narrowPrecisionToCGFloat(a), narrowPrecisionToCGFloat(b), @@ -51,12 +51,12 @@ AffineTransform::AffineTransform(double a, double b, double c, double d, double narrowPrecisionToCGFloat(ty)); } -AffineTransform::AffineTransform(const PlatformAffineTransform& t) +TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& t) : m_transform(t) { } -void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) +void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) { m_transform = CGAffineTransformMake(narrowPrecisionToCGFloat(a), narrowPrecisionToCGFloat(b), @@ -66,147 +66,147 @@ void AffineTransform::setMatrix(double a, double b, double c, double d, double t narrowPrecisionToCGFloat(ty)); } -void AffineTransform::map(double x, double y, double *x2, double *y2) const +void TransformationMatrix::map(double x, double y, double *x2, double *y2) const { CGPoint result = CGPointApplyAffineTransform(CGPointMake(narrowPrecisionToCGFloat(x), narrowPrecisionToCGFloat(y)), m_transform); *x2 = result.x; *y2 = result.y; } -IntRect AffineTransform::mapRect(const IntRect &rect) const +IntRect TransformationMatrix::mapRect(const IntRect &rect) const { return enclosingIntRect(CGRectApplyAffineTransform(CGRect(rect), m_transform)); } -FloatRect AffineTransform::mapRect(const FloatRect &rect) const +FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const { return FloatRect(CGRectApplyAffineTransform(CGRect(rect), m_transform)); } -bool AffineTransform::isIdentity() const +bool TransformationMatrix::isIdentity() const { return CGAffineTransformIsIdentity(m_transform); } -double AffineTransform::a() const +double TransformationMatrix::a() const { return m_transform.a; } -void AffineTransform::setA(double a) +void TransformationMatrix::setA(double a) { m_transform.a = narrowPrecisionToCGFloat(a); } -double AffineTransform::b() const +double TransformationMatrix::b() const { return m_transform.b; } -void AffineTransform::setB(double b) +void TransformationMatrix::setB(double b) { m_transform.b = narrowPrecisionToCGFloat(b); } -double AffineTransform::c() const +double TransformationMatrix::c() const { return m_transform.c; } -void AffineTransform::setC(double c) +void TransformationMatrix::setC(double c) { m_transform.c = narrowPrecisionToCGFloat(c); } -double AffineTransform::d() const +double TransformationMatrix::d() const { return m_transform.d; } -void AffineTransform::setD(double d) +void TransformationMatrix::setD(double d) { m_transform.d = narrowPrecisionToCGFloat(d); } -double AffineTransform::e() const +double TransformationMatrix::e() const { return m_transform.tx; } -void AffineTransform::setE(double e) +void TransformationMatrix::setE(double e) { m_transform.tx = narrowPrecisionToCGFloat(e); } -double AffineTransform::f() const +double TransformationMatrix::f() const { return m_transform.ty; } -void AffineTransform::setF(double f) +void TransformationMatrix::setF(double f) { m_transform.ty = narrowPrecisionToCGFloat(f); } -void AffineTransform::reset() +void TransformationMatrix::reset() { m_transform = CGAffineTransformIdentity; } -AffineTransform &AffineTransform::scale(double sx, double sy) +TransformationMatrix &TransformationMatrix::scale(double sx, double sy) { m_transform = CGAffineTransformScale(m_transform, narrowPrecisionToCGFloat(sx), narrowPrecisionToCGFloat(sy)); return *this; } -AffineTransform &AffineTransform::rotate(double d) +TransformationMatrix &TransformationMatrix::rotate(double d) { m_transform = CGAffineTransformRotate(m_transform, narrowPrecisionToCGFloat(deg2rad(d))); return *this; } -AffineTransform &AffineTransform::translate(double tx, double ty) +TransformationMatrix &TransformationMatrix::translate(double tx, double ty) { m_transform = CGAffineTransformTranslate(m_transform, narrowPrecisionToCGFloat(tx), narrowPrecisionToCGFloat(ty)); return *this; } -AffineTransform &AffineTransform::shear(double sx, double sy) +TransformationMatrix &TransformationMatrix::shear(double sx, double sy) { CGAffineTransform shear = CGAffineTransformMake(1.0f, narrowPrecisionToCGFloat(sy), narrowPrecisionToCGFloat(sx), 1.0f, 0.0f, 0.0f); m_transform = CGAffineTransformConcat(shear, m_transform); return *this; } -double AffineTransform::det() const +double TransformationMatrix::det() const { return m_transform.a * m_transform.d - m_transform.b * m_transform.c; } -AffineTransform AffineTransform::inverse() const +TransformationMatrix TransformationMatrix::inverse() const { if (isInvertible()) - return AffineTransform(CGAffineTransformInvert(m_transform)); - return AffineTransform(); + return TransformationMatrix(CGAffineTransformInvert(m_transform)); + return TransformationMatrix(); } -AffineTransform::operator PlatformAffineTransform() const +TransformationMatrix::operator PlatformTransformationMatrix() const { return m_transform; } -bool AffineTransform::operator== (const AffineTransform &m2) const +bool TransformationMatrix::operator== (const TransformationMatrix &m2) const { return CGAffineTransformEqualToTransform(m_transform, CGAffineTransform(m2)); } -AffineTransform &AffineTransform::operator*= (const AffineTransform &m2) +TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) { m_transform = CGAffineTransformConcat(m_transform, CGAffineTransform(m2)); return *this; } -AffineTransform AffineTransform::operator* (const AffineTransform &m2) +TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) { return CGAffineTransformConcat(m_transform, CGAffineTransform(m2)); } diff --git a/WebCore/platform/graphics/chromium/ColorChromium.cpp b/WebCore/platform/graphics/chromium/ColorChromium.cpp new file mode 100644 index 0000000..16ca17d --- /dev/null +++ b/WebCore/platform/graphics/chromium/ColorChromium.cpp @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#include "config.h" +#include "Color.h" + +namespace WebCore { + +Color focusRingColor() +{ +// FIXME: This should be split up to ColorChromiumWin and ColorChromiumMac. +#if PLATFORM(DARWIN) + // To avoid the Mac Chromium build having to rebasline 500+ layout tests and + // continue to do this w/ new tests that get landed in WebKit, we want to + // run the layout tests w/ the same color that stock WebKit uses. + // + // TODO: For now we've hard coded the color that WebKit uses for layout + // tests. We need to revisit this and do either of the following: + // A. Fully honor the color from the UI, which means collecting the color + // (and change notifications) in the browser process, and messaging the + // color to the render process. + // B. Adding a "layout tests" flag, to control the orage vs. blue colors + // depending if we're running layout tests. + // To see the WebKit implementation of using the UI color and/or a flag for + // layout tests see WebKit/WebCore/platform/graphics/mac/ColorMac.mm. + // (Reality is we just need an api to override the focus color and both + // of the above are covered for what this file needs to provide, the + // two options would be details that happen in other places.) + + // From WebKit: + // static RGBA32 oldAquaFocusRingColorRGBA = 0xFF7DADD9; + static Color oldAquaFocusRingColor(0x7D, 0xAD, 0xD9, 0xFF); + return oldAquaFocusRingColor; +#else + static Color focusRingColor(229, 151, 0, 255); + return focusRingColor; +#endif +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp new file mode 100644 index 0000000..03583a0 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "FontCache.h" + +#include "ChromiumBridge.h" +#include "Font.h" +#include "FontUtilsChromiumWin.h" +#include "HashMap.h" +#include "HashSet.h" +#include "SimpleFontData.h" +#include "StringHash.h" +#include <unicode/uniset.h> + +#include <windows.h> +#include <objidl.h> +#include <mlang.h> + +using std::min; + +namespace WebCore +{ + +void FontCache::platformInit() +{ + // Not needed on Windows. +} + +// FIXME: consider adding to WebKit String class +static bool charactersAreAllASCII(const String& s) +{ + return charactersAreAllASCII(s.characters(), s.length()); +} + +// When asked for a CJK font with a native name under a non-CJK locale or +// asked for a CJK font with a Romanized name under a CJK locale, +// |GetTextFace| (after |CreateFont*|) returns a 'bogus' value (e.g. Arial). +// This is not consistent with what MSDN says !! +// Therefore, before we call |CreateFont*|, we have to map a Romanized name to +// the corresponding native name under a CJK locale and vice versa +// under a non-CJK locale. +// See the corresponding gecko bugs at +// https://bugzilla.mozilla.org/show_bug.cgi?id=373952 +// https://bugzilla.mozilla.org/show_bug.cgi?id=231426 +static bool LookupAltName(const String& name, String& altName) +{ + struct FontCodepage { + WCHAR* name; + int codePage; + }; + + struct NamePair { + WCHAR* name; + FontCodepage altNameCodepage; + }; + + const int japaneseCodepage = 932; + const int simplifiedChineseCodepage = 936; + const int koreanCodepage = 949; + const int traditionalChineseCodepage = 950; + + // FIXME(jungshik) : This list probably covers 99% of cases. + // To cover the remaining 1% and cut down the file size, + // consider accessing 'NAME' table of a truetype font + // using |GetFontData| and caching the mapping. + // In the table below, the ASCII keys are all lower-cased for + // case-insensitive matching. + static const NamePair namePairs[] = { + // MS Pゴシック, MS PGothic + {L"\xFF2D\xFF33 \xFF30\x30B4\x30B7\x30C3\x30AF", {L"MS PGothic", japaneseCodepage}}, + {L"ms pgothic", {L"\xFF2D\xFF33 \xFF30\x30B4\x30B7\x30C3\x30AF", japaneseCodepage}}, + // MS P明朝, MS PMincho + {L"\xFF2D\xFF33 \xFF30\x660E\x671D", {L"MS PMincho", japaneseCodepage}}, + {L"ms pmincho", {L"\xFF2D\xFF33 \xFF30\x660E\x671D", japaneseCodepage}}, + // MSゴシック, MS Gothic + {L"\xFF2D\xFF33 \x30B4\x30B7\x30C3\x30AF", {L"MS Gothic", japaneseCodepage}}, + {L"ms gothic", {L"\xFF2D\xFF33 \x30B4\x30B7\x30C3\x30AF", japaneseCodepage}}, + // MS 明朝, MS Mincho + {L"\xFF2D\xFF33 \x660E\x671D", {L"MS Mincho", japaneseCodepage}}, + {L"ms mincho", {L"\xFF2D\xFF33 \x660E\x671D", japaneseCodepage}}, + // メイリオ, Meiryo + {L"\x30E1\x30A4\x30EA\x30AA", {L"Meiryo", japaneseCodepage}}, + {L"meiryo", {L"\x30E1\x30A4\x30EA\x30AA", japaneseCodepage}}, + // 바탕, Batang + {L"\xBC14\xD0D5", {L"Batang", koreanCodepage}}, + {L"batang", {L"\xBC14\xD0D5", koreanCodepage}}, + // 바탕체, Batangche + {L"\xBC14\xD0D5\xCCB4", {L"Batangche", koreanCodepage}}, + {L"batangche", {L"\xBC14\xD0D5\xCCB4", koreanCodepage}}, + // 굴림, Gulim + {L"\xAD74\xB9BC", {L"Gulim", koreanCodepage}}, + {L"gulim", {L"\xAD74\xB9BC", koreanCodepage}}, + // 굴림체, Gulimche + {L"\xAD74\xB9BC\xCCB4", {L"Gulimche", koreanCodepage}}, + {L"gulimche", {L"\xAD74\xB9BC\xCCB4", koreanCodepage}}, + // 돋움, Dotum + {L"\xB3CB\xC6C0", {L"Dotum", koreanCodepage}}, + {L"dotum", {L"\xB3CB\xC6C0", koreanCodepage}}, + // 돋움체, Dotumche + {L"\xB3CB\xC6C0\xCCB4", {L"Dotumche", koreanCodepage}}, + {L"dotumche", {L"\xB3CB\xC6C0\xCCB4", koreanCodepage}}, + // 궁서, Gungsuh + {L"\xAD81\xC11C", {L"Gungsuh", koreanCodepage}}, + {L"gungsuh", {L"\xAD81\xC11C", koreanCodepage}}, + // 궁서체, Gungsuhche + {L"\xAD81\xC11C\xCCB4", {L"Gungsuhche", koreanCodepage}}, + {L"gungsuhche", {L"\xAD81\xC11C\xCCB4", koreanCodepage}}, + // 맑은 고딕, Malgun Gothic + {L"\xB9D1\xC740 \xACE0\xB515", {L"Malgun Gothic", koreanCodepage}}, + {L"malgun gothic", {L"\xB9D1\xC740 \xACE0\xB515", koreanCodepage}}, + // 宋体, SimSun + {L"\x5B8B\x4F53", {L"SimSun", simplifiedChineseCodepage}}, + {L"simsun", {L"\x5B8B\x4F53", simplifiedChineseCodepage}}, + // 黑体, SimHei + {L"\x9ED1\x4F53", {L"SimHei", simplifiedChineseCodepage}}, + {L"simhei", {L"\x9ED1\x4F53", simplifiedChineseCodepage}}, + // 新宋体, NSimSun + {L"\x65B0\x5B8B\x4F53", {L"NSimSun", simplifiedChineseCodepage}}, + {L"nsimsun", {L"\x65B0\x5B8B\x4F53", simplifiedChineseCodepage}}, + // 微软雅黑, Microsoft Yahei + {L"\x5FAE\x8F6F\x96C5\x9ED1", {L"Microsoft Yahei", simplifiedChineseCodepage}}, + {L"microsoft yahei", {L"\x5FAE\x8F6F\x96C5\x9ED1", simplifiedChineseCodepage}}, + // 仿宋, FangSong + {L"\x4EFF\x5B8B", {L"FangSong", simplifiedChineseCodepage}}, + {L"fangsong", {L"\x4EFF\x5B8B", simplifiedChineseCodepage}}, + // 楷体, KaiTi + {L"\x6977\x4F53", {L"KaiTi", simplifiedChineseCodepage}}, + {L"kaiti", {L"\x6977\x4F53", simplifiedChineseCodepage}}, + // 仿宋_GB2312, FangSong_GB2312 + {L"\x4EFF\x5B8B_GB2312", {L"FangSong_GB2312", simplifiedChineseCodepage}}, + {L"fangsong_gb2312", {L"\x4EFF\x5B8B_gb2312", simplifiedChineseCodepage}}, + // 楷体_GB2312, KaiTi_GB2312 + {L"\x6977\x4F53", {L"KaiTi_GB2312", simplifiedChineseCodepage}}, + {L"kaiti_gb2312", {L"\x6977\x4F53_gb2312", simplifiedChineseCodepage}}, + // 新細明體, PMingLiu + {L"\x65B0\x7D30\x660E\x9AD4", {L"PMingLiu", traditionalChineseCodepage}}, + {L"pmingliu", {L"\x65B0\x7D30\x660E\x9AD4", traditionalChineseCodepage}}, + // 細明體, MingLiu + {L"\x7D30\x660E\x9AD4", {L"MingLiu", traditionalChineseCodepage}}, + {L"mingliu", {L"\x7D30\x660E\x9AD4", traditionalChineseCodepage}}, + // 微軟正黑體, Microsoft JhengHei + {L"\x5FAE\x8EDF\x6B63\x9ED1\x9AD4", {L"Microsoft JhengHei", traditionalChineseCodepage}}, + {L"microsoft jhengHei", {L"\x5FAE\x8EDF\x6B63\x9ED1\x9AD4", traditionalChineseCodepage}}, + // 標楷體, DFKai-SB + {L"\x6A19\x6977\x9AD4", {L"DFKai-SB", traditionalChineseCodepage}}, + {L"dfkai-sb", {L"\x6A19\x6977\x9AD4", traditionalChineseCodepage}}, + // WenQuanYi Zen Hei + {L"\x6587\x6cc9\x9a5b\x6b63\x9ed1", {L"WenQuanYi Zen Hei", traditionalChineseCodepage}}, + {L"wenquanyi zen hei", {L"\x6587\x6cc9\x9a5b\x6b63\x9ed1", traditionalChineseCodepage}}, + // WenQuanYi Zen Hei + {L"\x6587\x6cc9\x9a7f\x6b63\x9ed1", {L"WenQuanYi Zen Hei", simplifiedChineseCodepage}}, + {L"wenquanyi zen hei", {L"\x6587\x6cc9\x9a7f\x6b63\x9ed1", simplifiedChineseCodepage}}, + // AR PL ShanHeiSun Uni, + {L"\x6587\x9f0e\x0050\x004c\x7d30\x4e0a\x6d77\x5b8b\x0055\x006e\x0069", + {L"AR PL ShanHeiSun Uni", traditionalChineseCodepage}}, + {L"ar pl shanheisun uni", + {L"\x6587\x9f0e\x0050\x004c\x7d30\x4e0a\x6d77\x5b8b\x0055\x006e\x0069", traditionalChineseCodepage}}, + // AR PL ShanHeiSun Uni, + {L"\x6587\x9f0e\x0050\x004c\x7ec6\x4e0a\x6d77\x5b8b\x0055\x006e\x0069", + {L"AR PL ShanHeiSun Uni", simplifiedChineseCodepage}}, + {L"ar pl shanheisun uni", + {L"\x6587\x9f0e\x0050\x004c\x7ec6\x4e0a\x6d77\x5b8b\x0055\x006e\x0069", simplifiedChineseCodepage}}, + // AR PL ZenKai Uni + // Traditional Chinese and Simplified Chinese names are + // identical. + {L"\x6587\x0050\x004C\x4E2D\x6977\x0055\x006E\x0069", {L"AR PL ZenKai Uni", traditionalChineseCodepage}}, + {L"ar pl zenkai uni", {L"\x6587\x0050\x004C\x4E2D\x6977\x0055\x006E\x0069", traditionalChineseCodepage}}, + {L"\x6587\x0050\x004C\x4E2D\x6977\x0055\x006E\x0069", {L"AR PL ZenKai Uni", simplifiedChineseCodepage}}, + {L"ar pl zenkai uni", {L"\x6587\x0050\x004C\x4E2D\x6977\x0055\x006E\x0069", simplifiedChineseCodepage}}, + }; + + typedef HashMap<String, const FontCodepage*> NameMap; + static NameMap* fontNameMap = 0; + + if (!fontNameMap) { + size_t numElements = sizeof(namePairs) / sizeof(NamePair); + fontNameMap = new NameMap; + for (size_t i = 0; i < numElements; ++i) + fontNameMap->set(String(namePairs[i].name), &(namePairs[i].altNameCodepage)); + } + + bool isAscii = false; + String n; + // use |lower| only for ASCII names + // For non-ASCII names, we don't want to invoke an expensive + // and unnecessary |lower|. + if (charactersAreAllASCII(name)) { + isAscii = true; + n = name.lower(); + } else + n = name; + + NameMap::iterator iter = fontNameMap->find(n); + if (iter == fontNameMap->end()) + return false; + + static int systemCp = ::GetACP(); + int fontCp = iter->second->codePage; + + if ((isAscii && systemCp == fontCp) || (!isAscii && systemCp != fontCp)) { + altName = String(iter->second->name); + return true; + } + + return false; +} + +static HFONT createFontIndirectAndGetWinName(const String& family, LOGFONT* winfont, String* winName) +{ + int len = min(static_cast<int>(family.length()), LF_FACESIZE - 1); + memcpy(winfont->lfFaceName, family.characters(), len * sizeof(WORD)); + winfont->lfFaceName[len] = '\0'; + + HFONT hfont = CreateFontIndirect(winfont); + if (!hfont) + return 0; + + HDC dc = GetDC(0); + HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(dc, hfont)); + WCHAR name[LF_FACESIZE]; + unsigned resultLength = GetTextFace(dc, LF_FACESIZE, name); + if (resultLength > 0) + resultLength--; // ignore the null terminator + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + *winName = String(name, resultLength); + return hfont; +} + +// This maps font family names to their repertoires of supported Unicode +// characters. Because it's family names rather than font faces we use +// as keys, there might be edge cases where one face of a font family +// has a different repertoire from another face of the same family. +typedef HashMap<const wchar_t*, UnicodeSet*> FontCmapCache; + +static bool fontContainsCharacter(const FontPlatformData* fontData, + const wchar_t* family, UChar32 character) +{ + // FIXME: For non-BMP characters, GetFontUnicodeRanges is of + // no use. We have to read directly from the cmap table of a font. + // Return true for now. + if (character > 0xFFFF) + return true; + + // This cache is just leaked on shutdown. + static FontCmapCache* fontCmapCache = 0; + if (!fontCmapCache) + fontCmapCache = new FontCmapCache; + + HashMap<const wchar_t*, UnicodeSet*>::iterator it = fontCmapCache->find(family); + if (it != fontCmapCache->end()) + return it->second->contains(character); + + HFONT hfont = fontData->hfont(); + HDC hdc = GetDC(0); + HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(hdc, hfont)); + int count = GetFontUnicodeRanges(hdc, 0); + if (count == 0 && ChromiumBridge::ensureFontLoaded(hfont)) + count = GetFontUnicodeRanges(hdc, 0); + if (count == 0) { + ASSERT_NOT_REACHED(); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + return true; + } + + static Vector<char, 512> glyphsetBuffer; + glyphsetBuffer.resize(GetFontUnicodeRanges(hdc, 0)); + GLYPHSET* glyphset = reinterpret_cast<GLYPHSET*>(glyphsetBuffer.data()); + // In addition, refering to the OS/2 table and converting the codepage list + // to the coverage map might be faster. + count = GetFontUnicodeRanges(hdc, glyphset); + ASSERT(count > 0); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + + // FIXME: consider doing either of the following two: + // 1) port back ICU 4.0's faster look-up code for UnicodeSet + // 2) port Mozilla's CompressedCharMap or gfxSparseBitset + unsigned i = 0; + UnicodeSet* cmap = new UnicodeSet; + while (i < glyphset->cRanges) { + WCHAR start = glyphset->ranges[i].wcLow; + cmap->add(start, start + glyphset->ranges[i].cGlyphs - 1); + i++; + } + cmap->freeze(); + // We don't lowercase |family| because all of them are under our control + // and they're already lowercased. + fontCmapCache->set(family, cmap); + return cmap->contains(character); +} + +// Given the desired base font, this will create a SimpleFontData for a specific +// font that can be used to render the given range of characters. +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) +{ + // FIXME: Consider passing fontDescription.dominantScript() + // to GetFallbackFamily here. + FontDescription fontDescription = font.fontDescription(); + UChar32 c; + UScriptCode script; + const wchar_t* family = getFallbackFamily(characters, length, + fontDescription.genericFamily(), &c, &script); + FontPlatformData* data = 0; + if (family) + data = getCachedFontPlatformData(font.fontDescription(), AtomicString(family, wcslen(family)), false); + + // Last resort font list : PanUnicode. CJK fonts have a pretty + // large repertoire. Eventually, we need to scan all the fonts + // on the system to have a Firefox-like coverage. + // Make sure that all of them are lowercased. + const static wchar_t* const cjkFonts[] = { + L"arial unicode ms", + L"ms pgothic", + L"simsun", + L"gulim", + L"pmingliu", + L"wenquanyi zen hei", // partial CJK Ext. A coverage but more + // widely known to Chinese users. + L"ar pl shanheisun uni", + L"ar pl zenkai uni", + L"han nom a", // Complete CJK Ext. A coverage + L"code2000", // Complete CJK Ext. A coverage + // CJK Ext. B fonts are not listed here because it's of no use + // with our current non-BMP character handling because we use + // Uniscribe for it and that code path does not go through here. + }; + + const static wchar_t* const commonFonts[] = { + L"tahoma", + L"arial unicode ms", + L"lucida sans unicode", + L"microsoft sans serif", + L"palatino linotype", + // Four fonts below (and code2000 at the end) are not from MS, but + // once installed, cover a very wide range of characters. + L"freeserif", + L"freesans", + L"gentium", + L"gentiumalt", + L"ms pgothic", + L"simsun", + L"gulim", + L"pmingliu", + L"code2000", + }; + + const wchar_t* const* panUniFonts = 0; + int numFonts = 0; + if (script == USCRIPT_HAN) { + panUniFonts = cjkFonts; + numFonts = ARRAYSIZE(cjkFonts); + } else { + panUniFonts = commonFonts; + numFonts = ARRAYSIZE(commonFonts); + } + // Font returned from GetFallbackFamily may not cover |characters| + // because it's based on script to font mapping. This problem is + // critical enough for non-Latin scripts (especially Han) to + // warrant an additional (real coverage) check with fontCotainsCharacter. + int i; + for (i = 0; (!data || !fontContainsCharacter(data, family, c)) && i < numFonts; ++i) { + family = panUniFonts[i]; + data = getCachedFontPlatformData(font.fontDescription(), AtomicString(family, wcslen(family))); + } + if (i < numFonts) // we found the font that covers this character ! + return getCachedFontData(data); + + return 0; + +} + +const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName) +{ + // Note that mapping to Courier is removed because + // because it's a bitmap font on Windows. + // Alias Courier -> Courier New + static AtomicString courier("Courier"), courierNew("Courier New"); + if (equalIgnoringCase(familyName, courier)) + return courierNew; + + // Alias Times <-> Times New Roman. + static AtomicString times("Times"), timesNewRoman("Times New Roman"); + if (equalIgnoringCase(familyName, times)) + return timesNewRoman; + if (equalIgnoringCase(familyName, timesNewRoman)) + return times; + + // Alias Helvetica <-> Arial + static AtomicString arial("Arial"), helvetica("Helvetica"); + if (equalIgnoringCase(familyName, helvetica)) + return arial; + if (equalIgnoringCase(familyName, arial)) + return helvetica; + + // We block bitmap fonts altogether so that we have to + // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) + static AtomicString msSans("MS Sans Serif"); + static AtomicString microsoftSans("Microsoft Sans Serif"); + if (equalIgnoringCase(familyName, msSans)) + return microsoftSans; + + // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no + // 'Microsoft Sans Serif-equivalent' for Serif. + static AtomicString msSerif("MS Serif"); + if (equalIgnoringCase(familyName, msSerif)) + return timesNewRoman; + + // FIXME: should we map 'system' to something ('Tahoma') ? + return emptyAtom; +} + +FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& description) +{ + FontDescription::GenericFamilyType generic = description.genericFamily(); + // FIXME: Mapping webkit generic to GenericFamilyType needs to + // be more intelligent. + // This spot rarely gets reached. GetFontDataForCharacters() gets hit a lot + // more often (see FIXME comment there). + const wchar_t* family = getFontFamilyForScript(description.dominantScript(), generic); + + if (family) + return getCachedFontPlatformData(description, AtomicString(family, wcslen(family))); + + // FIXME: Would be even better to somehow get the user's default font here. + // For now we'll pick the default that the user would get without changing + // any prefs. + static AtomicString timesStr("Times New Roman"); + static AtomicString courierStr("Courier New"); + static AtomicString arialStr("Arial"); + + AtomicString& fontStr = timesStr; + if (generic == FontDescription::SansSerifFamily) + fontStr = arialStr; + else if (generic == FontDescription::MonospaceFamily) + fontStr = courierStr; + + return getCachedFontPlatformData(description, fontStr); +} + +static LONG toGDIFontWeight(FontWeight fontWeight) +{ + static LONG gdiFontWeights[] = { + FW_THIN, // FontWeight100 + FW_EXTRALIGHT, // FontWeight200 + FW_LIGHT, // FontWeight300 + FW_NORMAL, // FontWeight400 + FW_MEDIUM, // FontWeight500 + FW_SEMIBOLD, // FontWeight600 + FW_BOLD, // FontWeight700 + FW_EXTRABOLD, // FontWeight800 + FW_HEAVY // FontWeight900 + }; + return gdiFontWeights[fontWeight]; +} + +// FIXME: This may not be the best place to put this function +AtomicString FontCache::getGenericFontForScript(UScriptCode script, const FontDescription& description) +{ + const wchar_t* scriptFont = getFontFamilyForScript( script, description.genericFamily()); + return scriptFont ? AtomicString(scriptFont, wcslen(scriptFont)) : emptyAtom; +} + +static void FillLogFont(const FontDescription& fontDescription, LOGFONT* winfont) +{ + // The size here looks unusual. The negative number is intentional. + // Unlike WebKit trunk, we don't multiply the size by 32. That seems to be + // some kind of artifact of their CG backend, or something. + winfont->lfHeight = -fontDescription.computedPixelSize(); + winfont->lfWidth = 0; + winfont->lfEscapement = 0; + winfont->lfOrientation = 0; + winfont->lfUnderline = false; + winfont->lfStrikeOut = false; + winfont->lfCharSet = DEFAULT_CHARSET; + winfont->lfOutPrecision = OUT_TT_ONLY_PRECIS; + winfont->lfQuality = ChromiumBridge::layoutTestMode() ? NONANTIALIASED_QUALITY : DEFAULT_QUALITY; // Honor user's desktop settings. + winfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + winfont->lfItalic = fontDescription.italic(); + winfont->lfWeight = toGDIFontWeight(fontDescription.weight()); +} + +struct TraitsInFamilyProcData { + TraitsInFamilyProcData(const AtomicString& familyName) + : m_familyName(familyName) + { + } + + const AtomicString& m_familyName; + HashSet<unsigned> m_traitsMasks; +}; + +static int CALLBACK traitsInFamilyEnumProc(CONST LOGFONT* logFont, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) +{ + TraitsInFamilyProcData* procData = reinterpret_cast<TraitsInFamilyProcData*>(lParam); + + unsigned traitsMask = 0; + traitsMask |= logFont->lfItalic ? FontStyleItalicMask : FontStyleNormalMask; + traitsMask |= FontVariantNormalMask; + LONG weight = logFont->lfWeight; + traitsMask |= weight == FW_THIN ? FontWeight100Mask : + weight == FW_EXTRALIGHT ? FontWeight200Mask : + weight == FW_LIGHT ? FontWeight300Mask : + weight == FW_NORMAL ? FontWeight400Mask : + weight == FW_MEDIUM ? FontWeight500Mask : + weight == FW_SEMIBOLD ? FontWeight600Mask : + weight == FW_BOLD ? FontWeight700Mask : + weight == FW_EXTRABOLD ? FontWeight800Mask : + FontWeight900Mask; + procData->m_traitsMasks.add(traitsMask); + return 1; +} + +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) +{ + HDC hdc = GetDC(0); + + LOGFONT logFont; + logFont.lfCharSet = DEFAULT_CHARSET; + unsigned familyLength = min(familyName.length(), static_cast<unsigned>(LF_FACESIZE - 1)); + memcpy(logFont.lfFaceName, familyName.characters(), familyLength * sizeof(UChar)); + logFont.lfFaceName[familyLength] = 0; + logFont.lfPitchAndFamily = 0; + + TraitsInFamilyProcData procData(familyName); + EnumFontFamiliesEx(hdc, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0); + copyToVector(procData.m_traitsMasks, traitsMasks); + + ReleaseDC(0, hdc); +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) +{ + LOGFONT winfont = {0}; + FillLogFont(fontDescription, &winfont); + + // Windows will always give us a valid pointer here, even if the face name + // is non-existent. We have to double-check and see if the family name was + // really used. + String winName; + HFONT hfont = createFontIndirectAndGetWinName(family, &winfont, &winName); + if (!hfont) + return 0; + + // FIXME: Do we need to use predefined fonts "guaranteed" to exist + // when we're running in layout-test mode? + if (!equalIgnoringCase(family, winName)) { + // For CJK fonts with both English and native names, + // GetTextFace returns a native name under the font's "locale" + // and an English name under other locales regardless of + // lfFaceName field of LOGFONT. As a result, we need to check + // if a font has an alternate name. If there is, we need to + // compare it with what's requested in the first place. + String altName; + if (!LookupAltName(family, altName) || + !equalIgnoringCase(altName, winName)) { + DeleteObject(hfont); + return 0; + } + } + + return new FontPlatformData(hfont, + fontDescription.computedPixelSize()); +} + +} diff --git a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp new file mode 100644 index 0000000..f187c55 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "FontCache.h" + +#include <fontconfig/fontconfig.h> + +#include "AtomicString.h" +#include "CString.h" +#include "Font.h" +#include "FontDescription.h" +#include "FontPlatformData.h" +#include "Logging.h" +#include "NotImplemented.h" +#include "SimpleFontData.h" + +#include "SkPaint.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +namespace WebCore { + +void FontCache::platformInit() +{ +} + +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, + const UChar* characters, + int length) +{ + FcCharSet* cset = FcCharSetCreate(); + for (int i = 0; i < length; ++i) + FcCharSetAddChar(cset, characters[i]); + + FcPattern* pattern = FcPatternCreate(); + + FcValue fcvalue; + fcvalue.type = FcTypeCharSet; + fcvalue.u.c = cset; + FcPatternAdd(pattern, FC_CHARSET, fcvalue, 0); + + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + FcResult result; + FcPattern* match = FcFontMatch(0, pattern, &result); + FcPatternDestroy(pattern); + + SimpleFontData* ret = 0; + + if (match) { + FcChar8* family; + if (FcPatternGetString(match, FC_FAMILY, 0, &family) == FcResultMatch) { + FontPlatformData* fpd = + createFontPlatformData(font.fontDescription(), AtomicString((char*) family)); + ret = new SimpleFontData(*fpd); + } + FcPatternDestroy(match); + } + + FcCharSetDestroy(cset); + + return ret; +} + +const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName) +{ + notImplemented(); + + // This is just to stop GCC emitting a warning about returning a reference + // to a temporary variable + static AtomicString a; + return a; +} + +FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& description) +{ + static AtomicString arialStr("Arial"); + return getCachedFontPlatformData(description, arialStr); +} + +void FontCache::getTraitsInFamily(const AtomicString& familyName, + Vector<unsigned>& traitsMasks) +{ + notImplemented(); +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, + const AtomicString& family) +{ + const char* name = 0; + CString s; + + if (family.length() == 0) { + static const struct { + FontDescription::GenericFamilyType mType; + const char* mName; + } fontDescriptions[] = { + { FontDescription::SerifFamily, "serif" }, + { FontDescription::SansSerifFamily, "sans-serif" }, + { FontDescription::MonospaceFamily, "monospace" }, + { FontDescription::CursiveFamily, "cursive" }, + { FontDescription::FantasyFamily, "fantasy" } + }; + + FontDescription::GenericFamilyType type = fontDescription.genericFamily(); + for (unsigned i = 0; i < SK_ARRAY_COUNT(fontDescriptions); i++) { + if (type == fontDescriptions[i].mType) { + name = fontDescriptions[i].mName; + break; + } + } + // if we fall out of the loop, it's ok for name to still be 0 + } + else { // convert the name to utf8 + s = family.string().utf8(); + name = s.data(); + } + + int style = SkTypeface::kNormal; + if (fontDescription.weight() >= FontWeightBold) + style |= SkTypeface::kBold; + if (fontDescription.italic()) + style |= SkTypeface::kItalic; + + SkTypeface* tf = SkTypeface::Create(name, static_cast<SkTypeface::Style>(style)); + if (!tf) + return 0; + + FontPlatformData* result = + new FontPlatformData(tf, + fontDescription.computedSize(), + (style & SkTypeface::kBold) && !tf->isBold(), + (style & SkTypeface::kItalic) && !tf->isItalic()); + tf->unref(); + return result; +} + +AtomicString FontCache::getGenericFontForScript(UScriptCode script, + const FontDescription& descript) +{ + notImplemented(); + return AtomicString(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp new file mode 100644 index 0000000..3cf18a6 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "Font.h" + +#include "TransformationMatrix.h" +#include "ChromiumBridge.h" +#include "FontFallbackList.h" +#include "GlyphBuffer.h" +#include "PlatformContextSkia.h" +#include "SimpleFontData.h" +#include "SkiaFontWin.h" +#include "SkiaUtils.h" +#include "UniscribeHelperTextRun.h" + +#include "skia/ext/platform_canvas_win.h" +#include "skia/ext/skia_utils_win.h" // FIXME: remove this dependency. + +#include <windows.h> + +namespace WebCore { + +static bool windowsCanHandleTextDrawing(GraphicsContext* context) +{ + // Check for non-translation transforms. Sometimes zooms will look better in + // Skia, and sometimes better in Windows. The main problem is that zooming + // in using Skia will show you the hinted outlines for the smaller size, + // which look weird. All else being equal, it's better to use Windows' text + // drawing, so we don't check for zooms. + const TransformationMatrix& matrix = context->getCTM(); + if (matrix.b() != 0 || matrix.c() != 0) // Check for skew. + return false; + + // Check for stroke effects. + if (context->platformContext()->getTextDrawingMode() != cTextFill) + return false; + + // Check for shadow effects. + if (context->platformContext()->getDrawLooper()) + return false; + + return true; +} + +// Skia equivalents to Windows text drawing functions. They +// will get the outlines from Windows and draw then using Skia using the given +// parameters in the paint arguments. This allows more complex effects and +// transforms to be drawn than Windows allows. +// +// These functions will be significantly slower than Windows GDI, and the text +// will look different (no ClearType), so use only when necessary. +// +// When you call a Skia* text drawing function, various glyph outlines will be +// cached. As a result, you should call SkiaWinOutlineCache::removePathsForFont +// when the font is destroyed so that the cache does not outlive the font (since +// the HFONTs are recycled). + +// Analog of the Windows GDI function DrawText, except using the given SkPaint +// attributes for the text. See above for more. +// +// Returns true of the text was drawn successfully. False indicates an error +// from Windows. +static bool skiaDrawText(HFONT hfont, + SkCanvas* canvas, + const SkPoint& point, + SkPaint* paint, + const WORD* glyphs, + const int* advances, + int numGlyphs) +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont); + + canvas->save(); + canvas->translate(point.fX, point.fY); + + for (int i = 0; i < numGlyphs; i++) { + const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); + if (!path) + return false; + canvas->drawPath(*path, *paint); + canvas->translate(advances[i], 0); + } + + canvas->restore(); + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + return true; +} + +static bool paintSkiaText(PlatformContextSkia* platformContext, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const SkPoint& origin) +{ + int textMode = platformContext->getTextDrawingMode(); + + // Filling (if necessary). This is the common case. + SkPaint paint; + platformContext->setupPaintForFilling(&paint); + paint.setFlags(SkPaint::kAntiAlias_Flag); + bool didFill = false; + if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) { + if (!skiaDrawText(hfont, platformContext->canvas(), origin, &paint, &glyphs[0], &advances[0], numGlyphs)) + return false; + didFill = true; + } + + // Stroking on top (if necessary). + if ((textMode & WebCore::cTextStroke) + && platformContext->getStrokeStyle() != NoStroke + && platformContext->getStrokeThickness() > 0) { + + paint.reset(); + platformContext->setupPaintForStroking(&paint, 0, 0); + paint.setFlags(SkPaint::kAntiAlias_Flag); + if (didFill) { + // If there is a shadow and we filled above, there will already be + // a shadow. We don't want to draw it again or it will be too dark + // and it will go on top of the fill. + // + // Note that this isn't strictly correct, since the stroke could be + // very thick and the shadow wouldn't account for this. The "right" + // thing would be to draw to a new layer and then draw that layer + // with a shadow. But this is a lot of extra work for something + // that isn't normally an issue. + paint.setLooper(0)->safeUnref(); + } + + if (!skiaDrawText(hfont, platformContext->canvas(), origin, &paint, &glyphs[0], &advances[0], numGlyphs)) + return false; + } + return true; +} + +void Font::drawGlyphs(GraphicsContext* graphicsContext, + const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, + int from, + int numGlyphs, + const FloatPoint& point) const +{ + PlatformGraphicsContext* context = graphicsContext->platformContext(); + + // Max buffer length passed to the underlying windows API. + const int kMaxBufferLength = 1024; + // Default size for the buffer. It should be enough for most of cases. + const int kDefaultBufferLength = 256; + + SkColor color = context->fillColor(); + unsigned char alpha = SkColorGetA(color); + // Skip 100% transparent text; no need to draw anything. + if (!alpha && context->getStrokeStyle() == NoStroke) + return; + + // Set up our graphics context. + HDC hdc = context->canvas()->beginPlatformPaint(); + HGDIOBJ oldFont = SelectObject(hdc, font->platformData().hfont()); + + // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. + // Enforce non-transparent color. + color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); + SetTextColor(hdc, skia::SkColorToCOLORREF(color)); + SetBkMode(hdc, TRANSPARENT); + + // Windows needs the characters and the advances in nice contiguous + // buffers, which we build here. + Vector<WORD, kDefaultBufferLength> glyphs; + Vector<int, kDefaultBufferLength> advances; + + // Compute the coordinate. The 'origin' represents the baseline, so we need + // to move it up to the top of the bounding square. + int x = static_cast<int>(point.x()); + int lineTop = static_cast<int>(point.y()) - font->ascent(); + + bool canUseGDI = windowsCanHandleTextDrawing(graphicsContext); + + // We draw the glyphs in chunks to avoid having to do a heap allocation for + // the arrays of characters and advances. Since ExtTextOut is the + // lowest-level text output function on Windows, there should be little + // penalty for splitting up the text. On the other hand, the buffer cannot + // be bigger than 4094 or the function will fail. + int glyphIndex = 0; + while (glyphIndex < numGlyphs) { + // how many chars will be in this chunk? + int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); + + glyphs.resize(curLen); + advances.resize(curLen); + + int curWidth = 0; + for (int i = 0; i < curLen; ++i, ++glyphIndex) { + glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); + advances[i] = static_cast<int>(glyphBuffer.advanceAt(from + glyphIndex)); + curWidth += advances[i]; + } + + bool success = false; + for (int executions = 0; executions < 2; ++executions) { + if (canUseGDI) + success = !!ExtTextOut(hdc, x, lineTop, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), curLen, &advances[0]); + else { + // Skia's text draing origin is the baseline, like WebKit, not + // the top, like Windows. + SkPoint origin = { x, point.y() }; + success = paintSkiaText(context, font->platformData().hfont(), numGlyphs, reinterpret_cast<const WORD*>(&glyphs[0]), &advances[0], origin); + } + + if (!success && executions == 0) { + // Ask the browser to load the font for us and retry. + ChromiumBridge::ensureFontLoaded(font->platformData().hfont()); + continue; + } + break; + } + + ASSERT(success); + + x += curWidth; + } + + SelectObject(hdc, oldFont); + context->canvas()->endPlatformPaint(); +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, + const IntPoint& point, + int h, + int from, + int to) const +{ + UniscribeHelperTextRun state(run, *this); + float left = static_cast<float>(point.x() + state.characterToX(from)); + float right = static_cast<float>(point.x() + state.characterToX(to)); + + // If the text is RTL, left will actually be after right. + if (left < right) + return FloatRect(left, static_cast<float>(point.y()), + right - left, static_cast<float>(h)); + + return FloatRect(right, static_cast<float>(point.y()), + left - right, static_cast<float>(h)); +} + +void Font::drawComplexText(GraphicsContext* graphicsContext, + const TextRun& run, + const FloatPoint& point, + int from, + int to) const +{ + PlatformGraphicsContext* context = graphicsContext->platformContext(); + UniscribeHelperTextRun state(run, *this); + + SkColor color = context->fillColor(); + unsigned char alpha = SkColorGetA(color); + // Skip 100% transparent text; no need to draw anything. + if (!alpha) + return; + + HDC hdc = context->canvas()->beginPlatformPaint(); + + // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. + // Enforce non-transparent color. + color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); + SetTextColor(hdc, skia::SkColorToCOLORREF(color)); + SetBkMode(hdc, TRANSPARENT); + + // Uniscribe counts the coordinates from the upper left, while WebKit uses + // the baseline, so we have to subtract off the ascent. + state.draw(hdc, static_cast<int>(point.x()), static_cast<int>(point.y() - ascent()), from, to); + context->canvas()->endPlatformPaint(); +} + +float Font::floatWidthForComplexText(const TextRun& run) const +{ + UniscribeHelperTextRun state(run, *this); + return static_cast<float>(state.width()); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int x, + bool includePartialGlyphs) const +{ + // Mac code ignores includePartialGlyphs, and they don't know what it's + // supposed to do, so we just ignore it as well. + UniscribeHelperTextRun state(run, *this); + int charIndex = state.xToCharacter(x); + + // XToCharacter will return -1 if the position is before the first + // character (we get called like this sometimes). + if (charIndex < 0) + charIndex = 0; + return charIndex; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp new file mode 100644 index 0000000..8f8df88 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (c) 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "FontCustomPlatformData.h" + +#if PLATFORM(WIN_OS) +#include "Base64.h" +#include "ChromiumBridge.h" +#include "OpenTypeUtilities.h" +#endif + +#include "FontPlatformData.h" +#include "NotImplemented.h" +#include "SharedBuffer.h" + +#if PLATFORM(WIN_OS) +#include <objbase.h> +#include <t2embapi.h> +#pragma comment(lib, "t2embed") +#endif + +namespace WebCore { + +FontCustomPlatformData::~FontCustomPlatformData() +{ +#if PLATFORM(WIN_OS) + if (m_fontReference) { + if (m_name.isNull()) { + ULONG status; + TTDeleteEmbeddedFont(m_fontReference, 0, &status); + } else + RemoveFontMemResourceEx(m_fontReference); + } +#endif +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode mode) +{ +#if PLATFORM(WIN_OS) + ASSERT(m_fontReference); + + LOGFONT logFont; + if (m_name.isNull()) + TTGetNewFontName(&m_fontReference, logFont.lfFaceName, LF_FACESIZE, 0, 0); + else { + // m_name comes from createUniqueFontName, which, in turn, gets + // it from base64-encoded uuid (128-bit). So, m_name + // can never be longer than LF_FACESIZE (32). + if (m_name.length() + 1 >= LF_FACESIZE) { + ASSERT_NOT_REACHED(); + return FontPlatformData(); + } + memcpy(logFont.lfFaceName, m_name.charactersWithNullTermination(), + sizeof(logFont.lfFaceName[0]) * (1 + m_name.length())); + } + + // FIXME: almost identical to FillLogFont in FontCacheWin.cpp. + // Need to refactor. + logFont.lfHeight = -size; + logFont.lfWidth = 0; + logFont.lfEscapement = 0; + logFont.lfOrientation = 0; + logFont.lfUnderline = false; + logFont.lfStrikeOut = false; + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfOutPrecision = OUT_TT_ONLY_PRECIS; + logFont.lfQuality = ChromiumBridge::layoutTestMode() ? + NONANTIALIASED_QUALITY : + DEFAULT_QUALITY; // Honor user's desktop settings. + logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + logFont.lfItalic = italic; + logFont.lfWeight = bold ? 700 : 400; + + HFONT hfont = CreateFontIndirect(&logFont); + return FontPlatformData(hfont, size); +#else + notImplemented(); + return FontPlatformData(); +#endif +} + +#if PLATFORM(WIN_OS) +// FIXME: EOTStream class and static functions in this #if block are +// duplicated from platform/graphics/win/FontCustomPlatformData.cpp +// and need to be shared. + +// Streams the concatenation of a header and font data. +class EOTStream { +public: + EOTStream(const Vector<UInt8, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) + : m_eotHeader(eotHeader) + , m_fontData(fontData) + , m_overlayDst(overlayDst) + , m_overlaySrc(overlaySrc) + , m_overlayLength(overlayLength) + , m_offset(0) + , m_inHeader(true) + { + } + + size_t read(void* buffer, size_t count); + +private: + const Vector<UInt8, 512>& m_eotHeader; + const SharedBuffer* m_fontData; + size_t m_overlayDst; + size_t m_overlaySrc; + size_t m_overlayLength; + size_t m_offset; + bool m_inHeader; +}; + +size_t EOTStream::read(void* buffer, size_t count) +{ + size_t bytesToRead = count; + if (m_inHeader) { + size_t bytesFromHeader = std::min(m_eotHeader.size() - m_offset, count); + memcpy(buffer, m_eotHeader.data() + m_offset, bytesFromHeader); + m_offset += bytesFromHeader; + bytesToRead -= bytesFromHeader; + if (m_offset == m_eotHeader.size()) { + m_inHeader = false; + m_offset = 0; + } + } + if (bytesToRead && !m_inHeader) { + size_t bytesFromData = std::min(m_fontData->size() - m_offset, bytesToRead); + memcpy(buffer, m_fontData->data() + m_offset, bytesFromData); + if (m_offset < m_overlayDst + m_overlayLength && m_offset + bytesFromData >= m_overlayDst) { + size_t dstOffset = std::max<int>(m_overlayDst - m_offset, 0); + size_t srcOffset = std::max<int>(0, m_offset - m_overlayDst); + size_t bytesToCopy = std::min(bytesFromData - dstOffset, m_overlayLength - srcOffset); + memcpy(reinterpret_cast<char*>(buffer) + dstOffset, m_fontData->data() + m_overlaySrc + srcOffset, bytesToCopy); + } + m_offset += bytesFromData; + bytesToRead -= bytesFromData; + } + return count - bytesToRead; +} + +static unsigned long WINAPIV readEmbedProc(void* stream, void* buffer, unsigned long length) +{ + return static_cast<EOTStream*>(stream)->read(buffer, length); +} + +// Creates a unique and unpredictable font name, in order to avoid collisions and to +// not allow access from CSS. +static String createUniqueFontName() +{ + Vector<char> fontUuid(sizeof(GUID)); + CoCreateGuid(reinterpret_cast<GUID*>(fontUuid.data())); + + Vector<char> fontNameVector; + base64Encode(fontUuid, fontNameVector); + ASSERT(fontNameVector.size() < LF_FACESIZE); + return String(fontNameVector.data(), fontNameVector.size()); +} +#endif + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) +{ + ASSERT_ARG(buffer, buffer); + +#if PLATFORM(WIN_OS) + // Introduce the font to GDI. AddFontMemResourceEx cannot be used, because it will pollute the process's + // font namespace (Windows has no API for creating an HFONT from data without exposing the font to the + // entire process first). TTLoadEmbeddedFont lets us override the font family name, so using a unique name + // we avoid namespace collisions. + + String fontName = createUniqueFontName(); + + // TTLoadEmbeddedFont works only with Embedded OpenType (.eot) data, + // so we need to create an EOT header and prepend it to the font data. + Vector<UInt8, 512> eotHeader; + size_t overlayDst; + size_t overlaySrc; + size_t overlayLength; + + if (!getEOTHeader(buffer, eotHeader, overlayDst, overlaySrc, overlayLength)) + return 0; + + HANDLE fontReference; + ULONG privStatus; + ULONG status; + EOTStream eotStream(eotHeader, buffer, overlayDst, overlaySrc, overlayLength); + + LONG loadEmbeddedFontResult = TTLoadEmbeddedFont(&fontReference, TTLOAD_PRIVATE, &privStatus, LICENSE_PREVIEWPRINT, &status, readEmbedProc, &eotStream, const_cast<LPWSTR>(fontName.charactersWithNullTermination()), 0, 0); + if (loadEmbeddedFontResult == E_NONE) + fontName = String(); + else { + fontReference = renameAndActivateFont(buffer, fontName); + if (!fontReference) + return 0; + } + + return new FontCustomPlatformData(fontReference, fontName); +#else + notImplemented();; + return 0; +#endif +} + +} diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h b/WebCore/platform/graphics/chromium/FontCustomPlatformData.h new file mode 100644 index 0000000..2f1a597 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (c) 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 FontCustomPlatformData_h +#define FontCustomPlatformData_h + +#include <wtf/Noncopyable.h> + +#if PLATFORM(WIN_OS) +#include "FontRenderingMode.h" +#include "PlatformString.h" +#include <windows.h> +#endif + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformData : Noncopyable { +#if PLATFORM(WIN_OS) + FontCustomPlatformData(HANDLE fontReference, const String& name) + : m_fontReference(fontReference) + , m_name(name) + {} +#endif + + ~FontCustomPlatformData(); + + FontPlatformData fontPlatformData(int size, bool bold, bool italic, + FontRenderingMode = NormalRenderingMode); + +#if PLATFORM(WIN_OS) + HANDLE m_fontReference; + String m_name; +#endif +}; + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); +} + +#endif // FontCustomPlatformData_h diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp new file mode 100644 index 0000000..7a3e614 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2007, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "Font.h" + +#include "FloatRect.h" +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "SimpleFontData.h" + +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkTemplates.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +namespace WebCore { + +void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, int from, int numGlyphs, + const FloatPoint& point) const { + SkCanvas* canvas = gc->platformContext()->canvas(); + SkPaint paint; + + gc->platformContext()->setupPaintCommon(&paint); + font->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->fillColor().rgb()); + + SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert + + const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); + SkScalar x = SkFloatToScalar(point.x()); + SkScalar y = SkFloatToScalar(point.y()); + + // FIXME: text rendering speed: + // Android has code in their WebCore fork to special case when the + // GlyphBuffer has no advances other than the defaults. In that case the + // text drawing can proceed faster. However, it's unclear when those + // patches may be upstreamed to WebKit so we always use the slower path + // here. + const GlyphBufferAdvance* adv = glyphBuffer.advances(from); + SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); + SkPoint* pos = storage.get(); + + for (int i = 0; i < numGlyphs; i++) { + pos[i].set(x, y); + x += SkFloatToScalar(adv[i].width()); + y += SkFloatToScalar(adv[i].height()); + } + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); +} + +void Font::drawComplexText(GraphicsContext* context, const TextRun& run, + const FloatPoint& point, int from, int to) const +{ + notImplemented(); +} + +float Font::floatWidthForComplexText(const TextRun& run) const +{ + notImplemented(); + return 0; +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int x, + bool includePartialGlyphs) const +{ + notImplemented(); + return 0; +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, + const IntPoint& point, int h, + int from, int to) const +{ + notImplemented(); + return FloatRect(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontPlatformData.h b/WebCore/platform/graphics/chromium/FontPlatformData.h new file mode 100644 index 0000000..c6f1912 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontPlatformData.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2007, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 FontPlatformData_h +#define FontPlatformData_h + +#if PLATFORM(WIN_OS) +#include "FontPlatformDataChromiumWin.h" +#elif defined(__linux__) +#include "FontPlatformDataLinux.h" +#endif + +#endif // FontPlatformData_h diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp new file mode 100644 index 0000000..767fe76 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "FontPlatformData.h" + +#include <windows.h> +#include <objidl.h> +#include <mlang.h> + +#include "ChromiumBridge.h" +#include "SkiaFontWin.h" + +namespace WebCore { + +FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType) + : m_font(hashTableDeletedFontValue()) + , m_size(-1) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +FontPlatformData::FontPlatformData() + : m_font(0) + , m_size(0) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +FontPlatformData::FontPlatformData(HFONT font, float size) + : m_font(RefCountedHFONT::create(font)) + , m_size(size) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +// FIXME: this constructor is needed for SVG fonts but doesn't seem to do much +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) + : m_font(0) + , m_size(size) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +FontPlatformData::FontPlatformData(const FontPlatformData& data) + : m_font(data.m_font) + , m_size(data.m_size) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& data) +{ + if (this != &data) { + m_font = data.m_font; + m_size = data.m_size; + + // The following fields will get re-computed if necessary. + ScriptFreeCache(&m_scriptCache); + m_scriptCache = 0; + + delete m_scriptFontProperties; + m_scriptFontProperties = 0; + } + return *this; +} + +FontPlatformData::~FontPlatformData() +{ + ScriptFreeCache(&m_scriptCache); + m_scriptCache = 0; + + delete m_scriptFontProperties; + m_scriptFontProperties = 0; +} + +FontPlatformData::RefCountedHFONT::~RefCountedHFONT() +{ + if (m_hfont != reinterpret_cast<HFONT>(-1)) { + SkiaWinOutlineCache::removePathsForFont(m_hfont); + DeleteObject(m_hfont); + } +} + +FontPlatformData::RefCountedHFONT* FontPlatformData::hashTableDeletedFontValue() +{ + static RefPtr<RefCountedHFONT> deletedValue = + RefCountedHFONT::create(reinterpret_cast<HFONT>(-1)); + return deletedValue.get(); +} + +SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const +{ + if (!m_scriptFontProperties) { + m_scriptFontProperties = new SCRIPT_FONTPROPERTIES; + memset(m_scriptFontProperties, 0, sizeof(SCRIPT_FONTPROPERTIES)); + m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES); + HRESULT result = ScriptGetFontProperties(0, scriptCache(), + m_scriptFontProperties); + if (result == E_PENDING) { + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont()); + HRESULT hr = ScriptGetFontProperties(dc, scriptCache(), + m_scriptFontProperties); + if (S_OK != hr) { + if (ChromiumBridge::ensureFontLoaded(hfont())) { + // FIXME: Handle gracefully the error if this call also fails. + hr = ScriptGetFontProperties(dc, scriptCache(), + m_scriptFontProperties); + if (S_OK != hr) { + ASSERT_NOT_REACHED(); + } + } + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + } + } + return m_scriptFontProperties; +} + +} diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h new file mode 100644 index 0000000..ce15a93 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 FontPlatformDataWin_h +#define FontPlatformDataWin_h + +#include "config.h" + +#include "StringImpl.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +#include <usp10.h> + +typedef struct HFONT__ *HFONT; + +namespace WebCore { + +class FontDescription; + +class FontPlatformData { +public: + // Used for deleted values in the font cache's hash tables. The hash table + // will create us with this structure, and it will compare other values + // to this "Deleted" one. It expects the Deleted one to be differentiable + // from the NULL one (created with the empty constructor), so we can't just + // set everything to NULL. + FontPlatformData(WTF::HashTableDeletedValueType); + FontPlatformData(); + FontPlatformData(HFONT, float size); + FontPlatformData(float size, bool bold, bool oblique); + FontPlatformData(const FontPlatformData&); + + FontPlatformData& operator=(const FontPlatformData&); + + bool isHashTableDeletedValue() const { return m_font == hashTableDeletedFontValue(); } + + ~FontPlatformData(); + + HFONT hfont() const { return m_font ? m_font->hfont() : 0; } + float size() const { return m_size; } + + unsigned hash() const + { + return m_font ? m_font->hash() : NULL; + } + + bool operator==(const FontPlatformData& other) const + { + return m_font == other.m_font && m_size == other.m_size; + } + + SCRIPT_FONTPROPERTIES* scriptFontProperties() const; + SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; } + +private: + // We refcount the internal HFONT so that FontPlatformData can be + // efficiently copied. WebKit depends on being able to copy it, and we + // don't really want to re-create the HFONT. + class RefCountedHFONT : public RefCounted<RefCountedHFONT> { + public: + static PassRefPtr<RefCountedHFONT> create(HFONT hfont) + { + return adoptRef(new RefCountedHFONT(hfont)); + } + + ~RefCountedHFONT(); + + HFONT hfont() const { return m_hfont; } + unsigned hash() const + { + return StringImpl::computeHash(reinterpret_cast<const UChar*>(&m_hfont), sizeof(HFONT) / sizeof(UChar)); + } + + bool operator==(const RefCountedHFONT& other) const + { + return m_hfont == other.m_hfont; + } + + private: + // The create() function assumes there is already a refcount of one + // so it can do adoptRef. + RefCountedHFONT(HFONT hfont) : m_hfont(hfont) + { + } + + HFONT m_hfont; + }; + + static RefCountedHFONT* hashTableDeletedFontValue(); + + RefPtr<RefCountedHFONT> m_font; + float m_size; // Point size of the font in pixels. + + mutable SCRIPT_CACHE m_scriptCache; + mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties; +}; + +} // WebCore + +#endif // FontPlatformDataWin_h diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp new file mode 100644 index 0000000..86f96ee --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2006, 2007, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "FontPlatformData.h" + +#include "StringImpl.h" +#include "NotImplemented.h" + +#include "SkPaint.h" +#include "SkTypeface.h" + +namespace WebCore { + +FontPlatformData::FontPlatformData(const FontPlatformData& src) + : m_typeface(src.m_typeface) + , m_textSize(src.m_textSize) + , m_fakeBold(src.m_fakeBold) + , m_fakeItalic(src.m_fakeItalic) +{ + m_typeface->safeRef(); +} + +FontPlatformData::FontPlatformData(SkTypeface* tf, float textSize, bool fakeBold, bool fakeItalic) + : m_typeface(tf) + , m_textSize(textSize) + , m_fakeBold(fakeBold) + , m_fakeItalic(fakeItalic) +{ + m_typeface->safeRef(); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) + : m_typeface(src.m_typeface) + , m_textSize(textSize) + , m_fakeBold(src.m_fakeBold) + , m_fakeItalic(src.m_fakeItalic) +{ + m_typeface->safeRef(); +} + +FontPlatformData::~FontPlatformData() +{ + m_typeface->safeUnref(); +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) +{ + SkRefCnt_SafeAssign(m_typeface, src.m_typeface); + + m_textSize = src.m_textSize; + m_fakeBold = src.m_fakeBold; + m_fakeItalic = src.m_fakeItalic; + + return *this; +} + +void FontPlatformData::setupPaint(SkPaint* paint) const +{ + const float ts = m_textSize > 0 ? m_textSize : 12; + + paint->setAntiAlias(false); + paint->setSubpixelText(false); + paint->setTextSize(SkFloatToScalar(ts)); + paint->setTypeface(m_typeface); + paint->setFakeBoldText(m_fakeBold); + paint->setTextSkewX(m_fakeItalic ? -SK_Scalar1 / 4 : 0); + paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); +} + +bool FontPlatformData::operator==(const FontPlatformData& a) const +{ + // If either of the typeface pointers are invalid (either NULL or the + // special deleted value) then we test for pointer equality. Otherwise, we + // call SkTypeface::Equal on the valid pointers. + bool typefacesEqual; + if (m_typeface == hashTableDeletedFontValue() + || a.m_typeface == hashTableDeletedFontValue() + || !m_typeface + || !a.m_typeface) + typefacesEqual = m_typeface == a.m_typeface; + else + typefacesEqual = SkTypeface::Equal(m_typeface, a.m_typeface); + + return typefacesEqual + && m_textSize == a.m_textSize + && m_fakeBold == a.m_fakeBold + && m_fakeItalic == a.m_fakeItalic; +} + +unsigned FontPlatformData::hash() const +{ + unsigned h = SkTypeface::UniqueID(m_typeface); + h ^= 0x01010101 * ((static_cast<int>(m_fakeBold) << 1) | static_cast<int>(m_fakeItalic)); + + // This memcpy is to avoid a reinterpret_cast that breaks strict-aliasing + // rules. Memcpy is generally optimized enough so that performance doesn't + // matter here. + uint32_t textSizeBytes; + memcpy(&textSizeBytes, &m_textSize, sizeof(uint32_t)); + h ^= textSizeBytes; + + return h; +} + +bool FontPlatformData::isFixedPitch() const +{ + notImplemented(); + return false; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h new file mode 100644 index 0000000..ec7d837 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2006, 2007, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 FontPlatformDataLinux_h +#define FontPlatformDataLinux_h + +#include "StringImpl.h" +#include <wtf/RefPtr.h> + +class SkPaint; +class SkTypeface; + +namespace WebCore { + +class FontDescription; + +// ----------------------------------------------------------------------------- +// FontPlatformData is the handle which WebKit has on a specific face. A face +// is the tuple of (font, size, ...etc). Here we are just wrapping a Skia +// SkTypeface pointer and dealing with the reference counting etc. +// ----------------------------------------------------------------------------- +class FontPlatformData { +public: + // Used for deleted values in the font cache's hash tables. The hash table + // will create us with this structure, and it will compare other values + // to this "Deleted" one. It expects the Deleted one to be differentiable + // from the NULL one (created with the empty constructor), so we can't just + // set everything to NULL. + FontPlatformData(WTF::HashTableDeletedValueType) + : m_typeface(hashTableDeletedFontValue()) + , m_textSize(0) + , m_fakeBold(false) + , m_fakeItalic(false) + { } + + FontPlatformData() + : m_typeface(0) + , m_textSize(0) + , m_fakeBold(false) + , m_fakeItalic(false) + { } + + FontPlatformData(float textSize, bool fakeBold, bool fakeItalic) + : m_typeface(0) + , m_textSize(textSize) + , m_fakeBold(fakeBold) + , m_fakeItalic(fakeItalic) + { } + + FontPlatformData(const FontPlatformData&); + FontPlatformData(SkTypeface*, float textSize, bool fakeBold, bool fakeItalic); + FontPlatformData(const FontPlatformData& src, float textSize); + ~FontPlatformData(); + + // ------------------------------------------------------------------------- + // Return true iff this font is monospaced (i.e. every glyph has an equal x + // advance) + // ------------------------------------------------------------------------- + bool isFixedPitch() const; + + // ------------------------------------------------------------------------- + // Setup a Skia painting context to use this font. + // ------------------------------------------------------------------------- + void setupPaint(SkPaint*) const; + + unsigned hash() const; + float size() const { return m_textSize; } + + bool operator==(const FontPlatformData&) const; + FontPlatformData& operator=(const FontPlatformData&); + bool isHashTableDeletedValue() const { return m_typeface == hashTableDeletedFontValue(); } + +private: + // FIXME: Could SkAutoUnref be used here? + SkTypeface* m_typeface; + float m_textSize; + bool m_fakeBold; + bool m_fakeItalic; + + SkTypeface* hashTableDeletedFontValue() const { return reinterpret_cast<SkTypeface*>(-1); } +}; + +} // namespace WebCore + +#endif // ifdef FontPlatformData_h diff --git a/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.cpp new file mode 100644 index 0000000..ed326c8 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2006, 2007, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "FontUtilsChromiumWin.h" + +#include <limits> + +#include "PlatformString.h" +#include "StringHash.h" +#include "UniscribeHelper.h" +#include <unicode/locid.h> +#include <unicode/uchar.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +namespace { + +// A simple mapping from UScriptCode to family name. This is a sparse array, +// which works well since the range of UScriptCode values is small. +typedef const UChar* ScriptToFontMap[USCRIPT_CODE_LIMIT]; + +void initializeScriptFontMap(ScriptToFontMap& scriptFontMap) +{ + struct FontMap { + UScriptCode script; + const UChar* family; + }; + + const static FontMap fontMap[] = { + {USCRIPT_LATIN, L"times new roman"}, + {USCRIPT_GREEK, L"times new roman"}, + {USCRIPT_CYRILLIC, L"times new roman"}, + {USCRIPT_SIMPLIFIED_HAN, L"simsun"}, + {USCRIPT_HIRAGANA, L"ms pgothic"}, + {USCRIPT_KATAKANA, L"ms pgothic"}, + {USCRIPT_KATAKANA_OR_HIRAGANA, L"ms pgothic"}, + {USCRIPT_HANGUL, L"gulim"}, + {USCRIPT_THAI, L"tahoma"}, + {USCRIPT_HEBREW, L"david"}, + {USCRIPT_ARABIC, L"tahoma"}, + {USCRIPT_DEVANAGARI, L"mangal"}, + {USCRIPT_BENGALI, L"vrinda"}, + {USCRIPT_GURMUKHI, L"raavi"}, + {USCRIPT_GUJARATI, L"shruti"}, + {USCRIPT_ORIYA, L"kalinga"}, + {USCRIPT_TAMIL, L"latha"}, + {USCRIPT_TELUGU, L"gautami"}, + {USCRIPT_KANNADA, L"tunga"}, + {USCRIPT_MALAYALAM, L"kartika"}, + {USCRIPT_LAO, L"dokchampa"}, + {USCRIPT_TIBETAN, L"microsoft himalaya"}, + {USCRIPT_GEORGIAN, L"sylfaen"}, + {USCRIPT_ARMENIAN, L"sylfaen"}, + {USCRIPT_ETHIOPIC, L"nyala"}, + {USCRIPT_CANADIAN_ABORIGINAL, L"euphemia"}, + {USCRIPT_CHEROKEE, L"plantagenet cherokee"}, + {USCRIPT_YI, L"microsoft yi balti"}, + {USCRIPT_SINHALA, L"iskoola pota"}, + {USCRIPT_SYRIAC, L"estrangelo edessa"}, + {USCRIPT_KHMER, L"daunpenh"}, + {USCRIPT_THAANA, L"mv boli"}, + {USCRIPT_MONGOLIAN, L"mongolian balti"}, + {USCRIPT_MYANMAR, L"padauk"}, + // For USCRIPT_COMMON, we map blocks to scripts when + // that makes sense. + }; + + for (int i = 0; i < sizeof(fontMap) / sizeof(fontMap[0]); ++i) + scriptFontMap[fontMap[i].script] = fontMap[i].family; + + // Initialize the locale-dependent mapping. + // Since Chrome synchronizes the ICU default locale with its UI locale, + // this ICU locale tells the current UI locale of Chrome. + Locale locale = Locale::getDefault(); + const UChar* localeFamily = 0; + if (locale == Locale::getJapanese()) + localeFamily = scriptFontMap[USCRIPT_HIRAGANA]; + else if (locale == Locale::getKorean()) + localeFamily = scriptFontMap[USCRIPT_HANGUL]; + else { + // Use Simplified Chinese font for all other locales including + // Traditional Chinese because Simsun (SC font) has a wider + // coverage (covering both SC and TC) than PMingLiu (TC font). + // Note that |fontMap| does not have a separate entry for + // USCRIPT_TRADITIONAL_HAN for that reason. + // This also speeds up the TC version of Chrome when rendering SC + // pages. + localeFamily = scriptFontMap[USCRIPT_SIMPLIFIED_HAN]; + } + if (localeFamily) + scriptFontMap[USCRIPT_HAN] = localeFamily; +} + +const int kUndefinedAscent = std::numeric_limits<int>::min(); + +// Given an HFONT, return the ascent. If GetTextMetrics fails, +// kUndefinedAscent is returned, instead. +int getAscent(HFONT hfont) +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont); + TEXTMETRIC tm; + BOOL gotMetrics = GetTextMetrics(dc, &tm); + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + return gotMetrics ? tm.tmAscent : kUndefinedAscent; +} + +struct FontData { + FontData() + : hfont(0) + , ascent(kUndefinedAscent) + , scriptCache(0) + { + } + + HFONT hfont; + int ascent; + mutable SCRIPT_CACHE scriptCache; +}; + +// Again, using hash_map does not earn us much here. page_cycler_test intl2 +// gave us a 'better' result with map than with hash_map even though they're +// well-within 1-sigma of each other so that the difference is not significant. +// On the other hand, some pages in intl2 seem to take longer to load with map +// in the 1st pass. Need to experiment further. +typedef HashMap<String, FontData> FontDataCache; + +} // namespace + +// FIXME: this is font fallback code version 0.1 +// - Cover all the scripts +// - Get the default font for each script/generic family from the +// preference instead of hardcoding in the source. +// (at least, read values from the registry for IE font settings). +// - Support generic families (from FontDescription) +// - If the default font for a script is not available, +// try some more fonts known to support it. Finally, we can +// use EnumFontFamilies or similar APIs to come up with a list of +// fonts supporting the script and cache the result. +// - Consider using UnicodeSet (or UnicodeMap) converted from +// GLYPHSET (BMP) or directly read from truetype cmap tables to +// keep track of which character is supported by which font +// - Update script_font_cache in response to WM_FONTCHANGE + +const UChar* getFontFamilyForScript(UScriptCode script, + FontDescription::GenericFamilyType generic) +{ + static ScriptToFontMap scriptFontMap; + static bool initialized = false; + if (!initialized) { + initializeScriptFontMap(scriptFontMap); + initialized = true; + } + if (script == USCRIPT_INVALID_CODE) + return 0; + ASSERT(script < USCRIPT_CODE_LIMIT); + return scriptFontMap[script]; +} + +// FIXME: +// - Handle 'Inherited', 'Common' and 'Unknown' +// (see http://www.unicode.org/reports/tr24/#Usage_Model ) +// For 'Inherited' and 'Common', perhaps we need to +// accept another parameter indicating the previous family +// and just return it. +// - All the characters (or characters up to the point a single +// font can cover) need to be taken into account +const UChar* getFallbackFamily(const UChar* characters, + int length, + FontDescription::GenericFamilyType generic, + UChar32* charChecked, + UScriptCode* scriptChecked) +{ + ASSERT(characters && characters[0] && length > 0); + UScriptCode script = USCRIPT_COMMON; + + // Sometimes characters common to script (e.g. space) is at + // the beginning of a string so that we need to skip them + // to get a font required to render the string. + int i = 0; + UChar32 ucs4 = 0; + while (i < length && script == USCRIPT_COMMON || script == USCRIPT_INVALID_CODE) { + U16_NEXT(characters, i, length, ucs4); + UErrorCode err = U_ZERO_ERROR; + script = uscript_getScript(ucs4, &err); + // silently ignore the error + } + + // For the full-width ASCII characters (U+FF00 - U+FF5E), use the font for + // Han (determined in a locale-dependent way above). Full-width ASCII + // characters are rather widely used in Japanese and Chinese documents and + // they're fully covered by Chinese, Japanese and Korean fonts. + if (0xFF00 < ucs4 && ucs4 < 0xFF5F) + script = USCRIPT_HAN; + + // There are a lot of characters in USCRIPT_COMMON that can be covered + // by fonts for scripts closely related to them. See + // http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:Script=Common:] + // FIXME: make this more efficient with a wider coverage + if (script == USCRIPT_COMMON || script == USCRIPT_INHERITED) { + UBlockCode block = ublock_getCode(ucs4); + switch (block) { + case UBLOCK_BASIC_LATIN: + script = USCRIPT_LATIN; + break; + case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION: + script = USCRIPT_HAN; + break; + case UBLOCK_HIRAGANA: + case UBLOCK_KATAKANA: + script = USCRIPT_HIRAGANA; + break; + case UBLOCK_ARABIC: + script = USCRIPT_ARABIC; + break; + case UBLOCK_GREEK: + script = USCRIPT_GREEK; + break; + case UBLOCK_DEVANAGARI: + // For Danda and Double Danda (U+0964, U+0965), use a Devanagari + // font for now although they're used by other scripts as well. + // Without a context, we can't do any better. + script = USCRIPT_DEVANAGARI; + break; + case UBLOCK_ARMENIAN: + script = USCRIPT_ARMENIAN; + break; + case UBLOCK_GEORGIAN: + script = USCRIPT_GEORGIAN; + break; + case UBLOCK_KANNADA: + script = USCRIPT_KANNADA; + break; + } + } + + // Another lame work-around to cover non-BMP characters. + const UChar* family = getFontFamilyForScript(script, generic); + if (!family) { + int plane = ucs4 >> 16; + switch (plane) { + case 1: + family = L"code2001"; + break; + case 2: + family = L"simsun-extb"; + break; + default: + family = L"lucida sans unicode"; + } + } + + if (charChecked) + *charChecked = ucs4; + if (scriptChecked) + *scriptChecked = script; + return family; +} + +// Be aware that this is not thread-safe. +bool getDerivedFontData(const UChar* family, + int style, + LOGFONT* logfont, + int* ascent, + HFONT* hfont, + SCRIPT_CACHE** scriptCache) +{ + ASSERT(logfont); + ASSERT(family); + ASSERT(*family); + + // It does not matter that we leak font data when we exit. + static FontDataCache fontDataCache; + + // FIXME: This comes up pretty high in the profile so that + // we need to measure whether using SHA256 (after coercing all the + // fields to char*) is faster than String::format. + String fontKey = String::format("%1d:%d:%ls", style, logfont->lfHeight, family); + FontDataCache::iterator iter = fontDataCache.find(fontKey); + FontData* derived; + if (iter == fontDataCache.end()) { + ASSERT(wcslen(family) < LF_FACESIZE); + wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family); + // FIXME: CreateFontIndirect always comes up with + // a font even if there's no font matching the name. Need to + // check it against what we actually want (as is done in + // FontCacheWin.cpp) + pair<FontDataCache::iterator, bool> entry = fontDataCache.add(fontKey, FontData()); + derived = &entry.first->second; + derived->hfont = CreateFontIndirect(logfont); + // GetAscent may return kUndefinedAscent, but we still want to + // cache it so that we won't have to call CreateFontIndirect once + // more for HFONT next time. + derived->ascent = getAscent(derived->hfont); + } else { + derived = &iter->second; + // Last time, GetAscent failed so that only HFONT was + // cached. Try once more assuming that TryPreloadFont + // was called by a caller between calls. + if (kUndefinedAscent == derived->ascent) + derived->ascent = getAscent(derived->hfont); + } + *hfont = derived->hfont; + *ascent = derived->ascent; + *scriptCache = &(derived->scriptCache); + return *ascent != kUndefinedAscent; +} + +int getStyleFromLogfont(const LOGFONT* logfont) +{ + // FIXME: consider defining UNDEFINED or INVALID for style and + // returning it when logfont is 0 + if (!logfont) { + ASSERT_NOT_REACHED(); + return FontStyleNormal; + } + return (logfont->lfItalic ? FontStyleItalic : FontStyleNormal) | + (logfont->lfUnderline ? FontStyleUnderlined : FontStyleNormal) | + (logfont->lfWeight >= 700 ? FontStyleBold : FontStyleNormal); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.h b/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.h new file mode 100644 index 0000000..6a964c4 --- /dev/null +++ b/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2006, 2007, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +// A collection of utilities for font handling. + +// FIXME: Move all methods to the files that have their callsites and remove this file. +// *Utils files are not very WebKit-ty. + +#ifndef FontUtilsWin_h +#define FontUtilsWin_h + +#include <usp10.h> +#include <wchar.h> +#include <windows.h> + +#include "FontDescription.h" +#include <unicode/uscript.h> + +namespace WebCore { + +// Return a font family that supports a script and belongs to |generic| font +// family. It can return NULL and a caller has to implement its own fallback. +const UChar* getFontFamilyForScript(UScriptCode, FontDescription::GenericFamilyType); + +// Return a font family that can render |characters| based on +// what script characters belong to. When char_checked is non-NULL, +// it's filled with the character used to determine the script. +// When script_checked is non-NULL, the script used to determine +// the family is returned. +// FIXME: This function needs a total overhaul. +const UChar* getFallbackFamily(const UChar* characters, int length, + FontDescription::GenericFamilyType, + UChar32* charChecked, + UScriptCode* scriptChecked); + +// Derive a new HFONT by replacing lfFaceName of LOGFONT with |family|, +// calculate the ascent for the derived HFONT, and initialize SCRIPT_CACHE +// in FontData. +// |style| is only used for cache key generation. |style| is +// bit-wise OR of BOLD(1), UNDERLINED(2) and ITALIC(4) and +// should match what's contained in LOGFONT. It should be calculated +// by calling GetStyleFromLogFont. +// Returns false if the font is not accessible, in which case |ascent| field +// of |fontdata| is set to kUndefinedAscent. +// Be aware that this is not thread-safe. +// FIXME: Instead of having three out params, we'd better have one +// (|*FontData|), but somehow it mysteriously messes up the layout for +// certain complex script pages (e.g. hi.wikipedia.org) and also crashes +// at the start-up if recently visited page list includes pages with complex +// scripts in their title. Moreover, somehow the very first-pass of +// intl2 page-cycler test is noticeably slower with one out param than +// the current version although the subsequent 9 passes take about the +// same time. +bool getDerivedFontData(const UChar* family, int style, LOGFONT*, int* ascent, HFONT*, SCRIPT_CACHE**); + +enum { + FontStyleNormal = 0, + FontStyleBold = 1, + FontStyleItalic = 2, + FontStyleUnderlined = 4 +}; + +// Derive style (bit-wise OR of FONT_STYLE_BOLD, FONT_STYLE_UNDERLINED, and +// FONT_STYLE_ITALIC) from LOGFONT. Returns 0 if |*logfont| is NULL. +int getStyleFromLogfont(const LOGFONT*); + +} // namespace WebCore + +#endif // FontUtilsWin_h diff --git a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp new file mode 100644 index 0000000..4c5cf7b --- /dev/null +++ b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2008, 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 <windows.h> +#include <vector> + +#include "ChromiumBridge.h" +#include "Font.h" +#include "GlyphPageTreeNode.h" +#include "SimpleFontData.h" +#include "UniscribeHelperTextRun.h" +#include "WindowsVersion.h" + +namespace WebCore { + +// Fills one page of font data pointers with 0 to indicate that there +// are no glyphs for the characters. +static void fillEmptyGlyphs(GlyphPage* page) +{ + for (int i = 0; i < GlyphPage::size; ++i) + page->setGlyphDataForIndex(i, 0, 0); +} + +// Lazily initializes space glyph +static Glyph initSpaceGlyph(HDC dc, Glyph* spaceGlyph) +{ + if (*spaceGlyph) + return *spaceGlyph; + + static wchar_t space = ' '; + GetGlyphIndices(dc, &space, 1, spaceGlyph, 0); + return *spaceGlyph; +} + +// Fills |length| glyphs starting at |offset| in a |page| in the Basic +// Multilingual Plane (<= U+FFFF). The input buffer size should be the +// same as |length|. We can use the standard Windows GDI functions here. +// Returns true if any glyphs were found. +static bool fillBMPGlyphs(unsigned offset, + unsigned length, + UChar* buffer, + GlyphPage* page, + const SimpleFontData* fontData, + bool recurse) +{ + HDC dc = GetDC((HWND)0); + HGDIOBJ oldFont = SelectObject(dc, fontData->m_font.hfont()); + + TEXTMETRIC tm = {0}; + if (!GetTextMetrics(dc, &tm)) { + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + if (recurse) { + if (ChromiumBridge::ensureFontLoaded(fontData->m_font.hfont())) + return fillBMPGlyphs(offset, length, buffer, page, fontData, false); + else { + fillEmptyGlyphs(page); + return false; + } + } else { + // FIXME: This should never happen. We want to crash the + // process and receive a crash dump. We should revisit this code later. + // See http://crbug.com/6401 + ASSERT_NOT_REACHED(); + fillEmptyGlyphs(page); + return false; + } + } + + // FIXME: GetGlyphIndices() sets each item of localGlyphBuffer[] + // with the one of the values listed below. + // * With the GGI_MARK_NONEXISTING_GLYPHS flag + // + If the font has a glyph available for the character, + // localGlyphBuffer[i] > 0x0. + // + If the font does not have glyphs available for the character, + // localGlyphBuffer[i] = 0x1F (TrueType Collection?) or + // 0xFFFF (OpenType?). + // * Without the GGI_MARK_NONEXISTING_GLYPHS flag + // + If the font has a glyph available for the character, + // localGlyphBuffer[i] > 0x0. + // + If the font does not have glyphs available for the character, + // localGlyphBuffer[i] = 0x80. + // (Windows automatically assigns the glyph for a box character to + // prevent ExtTextOut() from returning errors.) + // To avoid from hurting the rendering performance, this code just + // tells WebKit whether or not the all glyph indices for the given + // characters are 0x80 (i.e. a possibly-invalid glyph) and let it + // use alternative fonts for the characters. + // Although this may cause a problem, it seems to work fine as far as I + // have tested. (Obviously, I need more tests.) + WORD localGlyphBuffer[GlyphPage::size]; + + // FIXME: I find some Chinese characters can not be correctly displayed + // when call GetGlyphIndices without flag GGI_MARK_NONEXISTING_GLYPHS, + // because the corresponding glyph index is set as 0x20 when current font + // does not have glyphs available for the character. According a blog post + // http://blogs.msdn.com/michkap/archive/2006/06/28/649791.aspx + // I think we should switch to the way about calling GetGlyphIndices with + // flag GGI_MARK_NONEXISTING_GLYPHS, it should be OK according the + // description of MSDN. + // Also according to Jungshik and Hironori's suggestion and modification + // we treat turetype and raster Font as different way when windows version + // is less than Vista. + GetGlyphIndices(dc, buffer, length, localGlyphBuffer, GGI_MARK_NONEXISTING_GLYPHS); + + // Copy the output to the GlyphPage + bool haveGlyphs = false; + int invalidGlyph = 0xFFFF; + if (!isVistaOrNewer() && !(tm.tmPitchAndFamily & TMPF_TRUETYPE)) + invalidGlyph = 0x1F; + + Glyph spaceGlyph = 0; // Glyph for a space. Lazily filled. + + for (unsigned i = 0; i < length; i++) { + UChar c = buffer[i]; + Glyph glyph = localGlyphBuffer[i]; + const SimpleFontData* glyphFontData = fontData; + // When this character should be a space, we ignore whatever the font + // says and use a space. Otherwise, if fonts don't map one of these + // space or zero width glyphs, we will get a box. + if (Font::treatAsSpace(c)) + // Hard code the glyph indices for characters that should be + // treated like spaces. + glyph = initSpaceGlyph(dc, &spaceGlyph); + else if (Font::treatAsZeroWidthSpace(c) || c == 0x200B) { + // FIXME: change Font::treatAsZeroWidthSpace to use + // u_hasBinaryProperty, per jungshik's comment here: + // https://bugs.webkit.org/show_bug.cgi?id=20237#c6. + // Then the additional OR above won't be necessary. + glyph = initSpaceGlyph(dc, &spaceGlyph); + glyphFontData = fontData->zeroWidthFontData(); + } else if (glyph == invalidGlyph) { + // WebKit expects both the glyph index and FontData + // pointer to be 0 if the glyph is not present + glyph = 0; + glyphFontData = 0; + } else { + if (SimpleFontData::isCJKCodePoint(c)) + glyphFontData = fontData->cjkWidthFontData(); + haveGlyphs = true; + } + page->setGlyphDataForCharacter(offset + i, glyph, glyphFontData); + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + return haveGlyphs; +} + +// For non-BMP characters, each is two words (UTF-16) and the input buffer +// size is 2 * |length|. Since GDI doesn't know how to handle non-BMP +// characters, we must use Uniscribe to tell us the glyph indices. +// +// We don't want to call this in the case of "regular" characters since some +// fonts may not have the correct combining rules for accents. See the notes +// at the bottom of ScriptGetCMap. We can't use ScriptGetCMap, though, since +// it doesn't seem to support UTF-16, despite what this blog post says: +// http://blogs.msdn.com/michkap/archive/2006/06/29/650680.aspx +// +// So we fire up the full Uniscribe doohicky, give it our string, and it will +// correctly handle the UTF-16 for us. The hard part is taking this and getting +// the glyph indices back out that correspond to the correct input characters, +// since they may be missing. +// +// Returns true if any glyphs were found. +static bool fillNonBMPGlyphs(unsigned offset, + unsigned length, + UChar* buffer, + GlyphPage* page, + const SimpleFontData* fontData) +{ + bool haveGlyphs = false; + + UniscribeHelperTextRun state(buffer, length * 2, false, + fontData->m_font.hfont(), + fontData->m_font.scriptCache(), + fontData->m_font.scriptFontProperties()); + state.setInhibitLigate(true); + state.init(); + + for (unsigned i = 0; i < length; i++) { + // Each character in this input buffer is a surrogate pair, which + // consists of two UChars. So, the offset for its i-th character is + // (i * 2). + WORD glyph = state.firstGlyphForCharacter(i * 2); + if (glyph) { + haveGlyphs = true; + page->setGlyphDataForIndex(offset + i, glyph, fontData); + } else + // Clear both glyph and fontData fields. + page->setGlyphDataForIndex(offset + i, 0, 0); + } + return haveGlyphs; +} + +// We're supposed to return true if there are any glyphs in the range +// specified by |offset| and |length| in our font, +// false if there are none. +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* characterBuffer, + unsigned bufferLength, const SimpleFontData* fontData) +{ + // We have to handle BMP and non-BMP characters differently. + // FIXME: Add assertions to make sure that buffer is entirely in BMP + // or entirely in non-BMP. + if (bufferLength == length) + return fillBMPGlyphs(offset, length, characterBuffer, this, fontData, true); + + if (bufferLength == 2 * length) { + // A non-BMP input buffer will be twice as long as output glyph buffer + // because each character in the non-BMP input buffer will be + // represented by a surrogate pair (two UChar's). + return fillNonBMPGlyphs(offset, length, characterBuffer, this, fontData); + } + + ASSERT_NOT_REACHED(); + return false; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp new file mode 100644 index 0000000..6024d43 --- /dev/null +++ b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2008, 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "GlyphPageTreeNode.h" + +#include "Font.h" +#include "SimpleFontData.h" + +#include "SkTemplates.h" +#include "SkPaint.h" +#include "SkUtils.h" + +namespace WebCore { + +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + if (SkUTF16_IsHighSurrogate(buffer[bufferLength-1])) { + SkDebugf("%s last char is high-surrogate", __FUNCTION__); + return false; + } + + SkPaint paint; + fontData->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + SkAutoSTMalloc <GlyphPage::size, uint16_t> glyphStorage(length); + uint16_t* glyphs = glyphStorage.get(); + // textToGlyphs takes a byte count, not a glyph count so we multiply by two. + unsigned count = paint.textToGlyphs(buffer, bufferLength * 2, glyphs); + if (count != length) { + SkDebugf("%s count != length\n", __FUNCTION__); + return false; + } + + unsigned allGlyphs = 0; // track if any of the glyphIDs are non-zero + for (unsigned i = 0; i < length; i++) { + setGlyphDataForIndex(offset + i, glyphs[i], glyphs[i] ? fontData : NULL); + allGlyphs |= glyphs[i]; + } + + return allGlyphs != 0; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/IconChromiumLinux.cpp b/WebCore/platform/graphics/chromium/IconChromiumLinux.cpp new file mode 100644 index 0000000..a5a6e1f --- /dev/null +++ b/WebCore/platform/graphics/chromium/IconChromiumLinux.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2008, 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "Icon.h" + +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "PlatformString.h" + +namespace WebCore { + +Icon::Icon(const PlatformIcon& icon) + : m_icon(icon) +{ +} + +Icon::~Icon() +{ +} + +PassRefPtr<Icon> Icon::createIconForFile(const String&) +{ + notImplemented(); + return 0; +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>&) +{ + notImplemented(); + return 0; +} + +void Icon::paint(GraphicsContext*, const IntRect&) +{ + notImplemented(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/IconChromiumMac.cpp b/WebCore/platform/graphics/chromium/IconChromiumMac.cpp new file mode 100644 index 0000000..93e36ba --- /dev/null +++ b/WebCore/platform/graphics/chromium/IconChromiumMac.cpp @@ -0,0 +1,60 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "Icon.h" + +#include "PassRefPtr.h" + +// FIXME: These are temporary stubs, we need real implementations which +// may come in the form of IconChromium.cpp. The Windows Chromium +// implementation is currently in IconWin.cpp. + +namespace WebCore { + +PassRefPtr<Icon> Icon::createIconForFile(const String&) +{ + return 0; +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>&) +{ + return 0; +} + +Icon::~Icon() +{ +} + +void Icon::paint(GraphicsContext*, const IntRect&) +{ +} + +} diff --git a/WebCore/platform/graphics/chromium/IconChromiumWin.cpp b/WebCore/platform/graphics/chromium/IconChromiumWin.cpp new file mode 100644 index 0000000..b419e6f --- /dev/null +++ b/WebCore/platform/graphics/chromium/IconChromiumWin.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "Icon.h" + +#include <windows.h> +#include <shellapi.h> + +#include "GraphicsContext.h" +#include "PlatformContextSkia.h" +#include "PlatformString.h" +#include "SkiaUtils.h" + +namespace WebCore { + +Icon::Icon(const PlatformIcon& icon) + : m_icon(icon) +{ +} + +Icon::~Icon() +{ + if (m_icon) + DestroyIcon(m_icon); +} + +PassRefPtr<Icon> Icon::createIconForFile(const String& filename) +{ + SHFILEINFO sfi; + memset(&sfi, 0, sizeof(sfi)); + + String tmpFilename = filename; + if (!SHGetFileInfo(tmpFilename.charactersWithNullTermination(), 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SHELLICONSIZE | SHGFI_SMALLICON)) + return 0; + + return adoptRef(new Icon(sfi.hIcon)); +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +{ + // FIXME: support multiple files. + // http://code.google.com/p/chromium/issues/detail?id=4092 + if (!filenames.size()) + return 0; + + return createIconForFile(filenames[0]); +} + +void Icon::paint(GraphicsContext* context, const IntRect& rect) +{ + if (context->paintingDisabled()) + return; + + HDC hdc = context->platformContext()->canvas()->beginPlatformPaint(); + DrawIconEx(hdc, rect.x(), rect.y(), m_icon, rect.width(), rect.height(), 0, 0, DI_NORMAL); + context->platformContext()->canvas()->endPlatformPaint(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/ImageBufferData.h b/WebCore/platform/graphics/chromium/ImageBufferData.h new file mode 100644 index 0000000..504b893 --- /dev/null +++ b/WebCore/platform/graphics/chromium/ImageBufferData.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 + +#include "PlatformContextSkia.h" + +#include "skia/ext/platform_canvas.h" + +namespace WebCore { + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); + + skia::PlatformCanvas m_canvas; + PlatformContextSkia m_platformContext; +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/WebCore/platform/graphics/chromium/ImageChromiumMac.mm b/WebCore/platform/graphics/chromium/ImageChromiumMac.mm new file mode 100644 index 0000000..073a409 --- /dev/null +++ b/WebCore/platform/graphics/chromium/ImageChromiumMac.mm @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +// A wrapper around Uniscribe that provides a reasonable API. + +#include "config.h" +#include "BitmapImage.h" + +#include "ChromiumBridge.h" +#include "Image.h" + +namespace WebCore { + +PassRefPtr<Image> Image::loadPlatformResource(const char* name) +{ + return ChromiumBridge::loadPlatformImageResource(name); +} + +// FIXME: These are temporary stubs, we need real implementations which +// may come in the form of ImageChromium.cpp. The Windows Chromium +// implementation is currently in ImageSkia.cpp. + +void BitmapImage::initPlatformData() +{ +} + +void BitmapImage::invalidatePlatformData() +{ +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h b/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h new file mode 100644 index 0000000..959147a --- /dev/null +++ b/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 MediaPlayerPrivateChromium_h +#define MediaPlayerPrivateChromium_h + +#if ENABLE(VIDEO) + +#include "MediaPlayer.h" + +namespace WebCore { + +class MediaPlayerPrivate : public Noncopyable { +public: + MediaPlayerPrivate(MediaPlayer*); + ~MediaPlayerPrivate(); + + IntSize naturalSize() const; + bool hasVideo() const; + + void load(const String& url); + void cancelLoad(); + + void play(); + void pause(); + + bool paused() const; + bool seeking() const; + + float duration() const; + float currentTime() const; + void seek(float time); + void setEndTime(float); + + void setRate(float); + void setVolume(float); + + int dataRate() const; + + MediaPlayer::NetworkState networkState() const; + MediaPlayer::ReadyState readyState() const; + + float maxTimeBuffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + bool totalBytesKnown() const; + unsigned totalBytes() const; + + void setVisible(bool); + void setRect(const IntRect&); + + void paint(GraphicsContext*, const IntRect&); + + static void getSupportedTypes(HashSet<String>&); + static bool isAvailable(); + + // Public methods to be called by WebMediaPlayer + FrameView* frameView(); + void networkStateChanged(); + void readyStateChanged(); + void timeChanged(); + void volumeChanged(); + void repaint(); + +private: + MediaPlayer* m_player; + void* m_data; +}; + +} // namespace WebCore + +#endif + +#endif // MediaPlayerPrivateChromium_h diff --git a/WebCore/platform/graphics/chromium/PlatformIcon.h b/WebCore/platform/graphics/chromium/PlatformIcon.h new file mode 100644 index 0000000..51613b8 --- /dev/null +++ b/WebCore/platform/graphics/chromium/PlatformIcon.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 PlatformIcon_h +#define PlatformIcon_h + +typedef struct HICON__* HICON; + +namespace WebCore { + +typedef HICON PlatformIcon; + +} // namespace WebCore + +#endif // PlatformIcon_h diff --git a/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp b/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp new file mode 100644 index 0000000..06e997f --- /dev/null +++ b/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All Rights Reserved. + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "SimpleFontData.h" + +#include "ChromiumBridge.h" +#include "Font.h" +#include "FontCache.h" +#include "FloatRect.h" +#include "FontDescription.h" +#include <wtf/MathExtras.h> + +#include <unicode/uchar.h> +#include <unicode/unorm.h> +#include <objidl.h> +#include <mlang.h> + +namespace WebCore { + +static inline float scaleEmToUnits(float x, int unitsPerEm) +{ + return unitsPerEm ? x / static_cast<float>(unitsPerEm) : x; +} + +void SimpleFontData::platformInit() +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, m_font.hfont()); + + TEXTMETRIC textMetric = {0}; + if (!GetTextMetrics(dc, &textMetric)) { + if (ChromiumBridge::ensureFontLoaded(m_font.hfont())) { + // Retry GetTextMetrics. + // FIXME: Handle gracefully the error if this call also fails. + // See http://crbug.com/6401. + if (!GetTextMetrics(dc, &textMetric)) + ASSERT_NOT_REACHED(); + } + } + + m_avgCharWidth = textMetric.tmAveCharWidth; + m_maxCharWidth = textMetric.tmMaxCharWidth; + + m_ascent = textMetric.tmAscent; + m_descent = textMetric.tmDescent; + m_lineGap = textMetric.tmExternalLeading; + m_xHeight = m_ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. + + OUTLINETEXTMETRIC outlineTextMetric; + if (GetOutlineTextMetrics(dc, sizeof(outlineTextMetric), &outlineTextMetric) > 0) { + // This is a TrueType font. We might be able to get an accurate xHeight. + GLYPHMETRICS glyphMetrics = {0}; + MAT2 identityMatrix = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; + DWORD len = GetGlyphOutlineW(dc, 'x', GGO_METRICS, &glyphMetrics, 0, 0, &identityMatrix); + if (len != GDI_ERROR && glyphMetrics.gmBlackBoxY > 0) + m_xHeight = static_cast<float>(glyphMetrics.gmBlackBoxY); + } + + m_lineSpacing = m_ascent + m_descent + m_lineGap; + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); +} + +void SimpleFontData::platformDestroy() +{ + // We don't hash this on Win32, so it's effectively owned by us. + delete m_smallCapsFontData; + m_smallCapsFontData = 0; +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_smallCapsFontData) { + LOGFONT winFont; + GetObject(m_font.hfont(), sizeof(LOGFONT), &winFont); + float smallCapsSize = 0.70f * fontDescription.computedSize(); + // Unlike WebKit trunk, we don't multiply the size by 32. That seems + // to be some kind of artifact of their CG backend, or something. + winFont.lfHeight = -lroundf(smallCapsSize); + HFONT hfont = CreateFontIndirect(&winFont); + m_smallCapsFontData = + new SimpleFontData(FontPlatformData(hfont, smallCapsSize)); + } + return m_smallCapsFontData; +} + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + // This used to be implemented with IMLangFontLink2, but since that code has + // been disabled, this would always return false anyway. + return false; +} + +void SimpleFontData::determinePitch() +{ + // TEXTMETRICS have this. Set m_treatAsFixedPitch based off that. + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, m_font.hfont()); + + // Yes, this looks backwards, but the fixed pitch bit is actually set if the font + // is *not* fixed pitch. Unbelievable but true. + TEXTMETRIC textMetric = {0}; + if (!GetTextMetrics(dc, &textMetric)) { + if (ChromiumBridge::ensureFontLoaded(m_font.hfont())) { + // Retry GetTextMetrics. + // FIXME: Handle gracefully the error if this call also fails. + // See http://crbug.com/6401. + if (!GetTextMetrics(dc, &textMetric)) + ASSERT_NOT_REACHED(); + } + } + + m_treatAsFixedPitch = ((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0); + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, m_font.hfont()); + + int width = 0; + if (!GetCharWidthI(dc, glyph, 1, 0, &width)) { + // Ask the browser to preload the font and retry. + if (ChromiumBridge::ensureFontLoaded(m_font.hfont())) { + // FIXME: Handle gracefully the error if this call also fails. + // See http://crbug.com/6401. + if (!GetCharWidthI(dc, glyph, 1, 0, &width)) + ASSERT_NOT_REACHED(); + } + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + return static_cast<float>(width); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp b/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp new file mode 100644 index 0000000..8200175 --- /dev/null +++ b/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "SimpleFontData.h" + +#include "Font.h" +#include "FontCache.h" +#include "FloatRect.h" +#include "FontDescription.h" +#include "Logging.h" +#include "NotImplemented.h" + +#include "SkPaint.h" +#include "SkTypeface.h" +#include "SkTime.h" + +namespace WebCore { + +// Smallcaps versions of fonts are 70% the size of the normal font. +static const float smallCapsFraction = 0.7f; + +void SimpleFontData::platformInit() +{ + SkPaint paint; + SkPaint::FontMetrics metrics; + + m_font.setupPaint(&paint); + paint.getFontMetrics(&metrics); + + // Beware those who step here: This code is designed to match Win32 font + // metrics *exactly*. + if (metrics.fVDMXMetricsValid) { + m_ascent = metrics.fVDMXAscent; + m_descent = metrics.fVDMXDescent; + } else { + m_ascent = SkScalarRound(-metrics.fAscent); + m_descent = SkScalarRound(metrics.fHeight) - m_ascent; + } + + if (metrics.fXHeight) + m_xHeight = metrics.fXHeight; + else { + // hack taken from the Windows port + m_xHeight = static_cast<float>(m_ascent) * 0.56; + } + + m_lineGap = SkScalarRound(metrics.fLeading); + m_lineSpacing = m_ascent + m_descent + m_lineGap; + + // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is + // calculated for us, but we need to calculate m_maxCharWidth and + // m_avgCharWidth in order for text entry widgets to be sized correctly. + + m_maxCharWidth = SkScalarRound(metrics.fXRange * SkScalarRound(m_font.size())); + + if (metrics.fAvgCharWidth) + m_avgCharWidth = SkScalarRound(metrics.fAvgCharWidth); + else { + m_avgCharWidth = m_xHeight; + + GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); + + if (glyphPageZero) { + static const UChar32 x_char = 'x'; + const Glyph xGlyph = glyphPageZero->glyphDataForCharacter(x_char).glyph; + + if (xGlyph) + m_avgCharWidth = widthForGlyph(xGlyph); + } + } +} + +void SimpleFontData::platformDestroy() +{ + delete m_smallCapsFontData; + m_smallCapsFontData = 0; +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_smallCapsFontData) { + const float smallCapsSize = lroundf(fontDescription.computedSize() * smallCapsFraction); + m_smallCapsFontData = new SimpleFontData(FontPlatformData(m_font, smallCapsSize)); + } + + return m_smallCapsFontData; +} + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + SkPaint paint; + static const unsigned maxBufferCount = 64; + uint16_t glyphs[maxBufferCount]; + + m_font.setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + while (length > 0) { + int n = SkMin32(length, SK_ARRAY_COUNT(glyphs)); + + // textToGlyphs takes a byte count so we double the character count. + int count = paint.textToGlyphs(characters, n * 2, glyphs); + for (int i = 0; i < count; i++) { + if (0 == glyphs[i]) + return false; // missing glyph + } + + characters += n; + length -= n; + } + + return true; +} + +void SimpleFontData::determinePitch() +{ + m_treatAsFixedPitch = platformData().isFixedPitch(); +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + SkASSERT(sizeof(glyph) == 2); // compile-time assert + + SkPaint paint; + + m_font.setupPaint(&paint); + + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + SkScalar width = paint.measureText(&glyph, 2); + + return SkScalarToFloat(width); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp new file mode 100644 index 0000000..798ee32 --- /dev/null +++ b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "ThemeHelperChromiumWin.h" + +#include "FloatRect.h" +#include "GraphicsContext.h" + +namespace WebCore { + +ThemeHelperWin::ThemeHelperWin(GraphicsContext* context, const IntRect& rect) + : m_orgContext(context) + , m_orgMatrix(context->getCTM()) + , m_orgRect(rect) +{ + if (m_orgMatrix.b() != 0 || m_orgMatrix.c() != 0) { // Check for skew. + // Complicated effects, make a copy and draw the bitmap there. + m_type = COPY; + m_rect.setSize(rect.size()); + + m_newBuffer.set(ImageBuffer::create(rect.size(), false).release()); + + // Theme drawing messes with the transparency. + // FIXME: Ideally, we would leave this transparent, but I was + // having problems with button drawing, so we fill with white. Buttons + // looked good with transparent here and no fixing up of the alpha + // later, but text areas didn't. This makes text areas look good but + // gives buttons a white halo. Is there a way to fix this? I think + // buttons actually have antialised edges which is just not possible + // to handle on a transparent background given that it messes with the + // alpha channel. + FloatRect newContextRect(0, 0, rect.width(), rect.height()); + GraphicsContext* newContext = m_newBuffer->context(); + newContext->setFillColor(Color::white); + newContext->fillRect(newContextRect); + + return; + } + + if (m_orgMatrix.a() != 1.0 || m_orgMatrix.d() != 1.0) { // Check for scale. + // Only a scaling is applied. + m_type = SCALE; + + // Save the transformed coordinates to draw. + m_rect = m_orgMatrix.mapRect(rect); + + m_orgContext->save(); + m_orgContext->concatCTM(m_orgContext->getCTM().inverse()); + return; + } + + // Nothing interesting. + m_rect = rect; + m_type = ORIGINAL; +} + +ThemeHelperWin::~ThemeHelperWin() +{ + switch (m_type) { + case SCALE: + m_orgContext->restore(); + break; + case COPY: { + // Copy the duplicate bitmap with our control to the original canvas. + FloatRect destRect(m_orgRect); + m_newBuffer->context()->platformContext()->canvas()-> + getTopPlatformDevice().fixupAlphaBeforeCompositing(); + m_orgContext->drawImage(m_newBuffer->image(), destRect); + break; + } + case ORIGINAL: + break; + } +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h new file mode 100644 index 0000000..1771fb4 --- /dev/null +++ b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 ThemeHelperWin_h +#define ThemeHelperWin_h + +#include "TransformationMatrix.h" +#include "ImageBuffer.h" +#include "IntRect.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class GraphicsContext; +class IntRect; + +// Helps drawing theme elements like buttons and scroll bars. This will handle +// translations and scalings that Windows might not, by either making Windows +// draw the appropriate sized control, or by rendering it into an off-screen +// context and transforming it ourselves. +class ThemeHelperWin { + enum Type { + // Use the original canvas with no changes. This is the normal mode. + ORIGINAL, + + // Use the original canvas but scale the rectangle of the control so + // that it will be the correct size, undoing any scale already on the + // canvas. This will have the effect of just drawing the control bigger + // or smaller and not actually expanding or contracting the pixels in + // it. This usually looks better. + SCALE, + + // Make a copy of the control and then transform it ourselves after + // Windows draws it. This allows us to get complex effects. + COPY, + }; + +public: + // Prepares drawing a control with the given rect to the given context. + ThemeHelperWin(GraphicsContext* context, const IntRect& rect); + ~ThemeHelperWin(); + + // Returns the context to draw the control into, which may be the original + // or the copy, depending on the mode. + GraphicsContext* context() + { + return m_newBuffer.get() ? m_newBuffer->context() : m_orgContext; + } + + // Returns the rectangle in which to draw into the canvas() by Windows. + const IntRect& rect() { return m_rect; } + +private: + Type m_type; + + // The original canvas to wrote to. Not owned by this class. + GraphicsContext* m_orgContext; + TransformationMatrix m_orgMatrix; + IntRect m_orgRect; + + // When m_type == COPY, this will be a new surface owned by this class that + // represents the copy. + OwnPtr<ImageBuffer> m_newBuffer; + + // The control rectangle in the coordinate space of canvas(). + IntRect m_rect; +}; + +} // namespace WebCore + +#endif // ThemeHelperWin_h diff --git a/WebCore/platform/graphics/chromium/UniscribeHelper.cpp b/WebCore/platform/graphics/chromium/UniscribeHelper.cpp new file mode 100644 index 0000000..caeb959 --- /dev/null +++ b/WebCore/platform/graphics/chromium/UniscribeHelper.cpp @@ -0,0 +1,902 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "UniscribeHelper.h" + +#include <windows.h> + +#include "FontUtilsChromiumWin.h" +#include <wtf/Assertions.h> + +namespace WebCore { + +// This function is used to see where word spacing should be applied inside +// runs. Note that this must match Font::treatAsSpace so we all agree where +// and how much space this is, so we don't want to do more general Unicode +// "is this a word break" thing. +static bool treatAsSpace(UChar c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; +} + +// SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid +// and blank glyphs. Just because ScriptShape succeeds does not mean +// that a text run is rendered correctly. Some characters may be rendered +// with default/invalid/blank glyphs. Therefore, we need to check if the glyph +// array returned by ScriptShape contains any of those glyphs to make +// sure that the text run is rendered successfully. +static bool containsMissingGlyphs(WORD *glyphs, + int length, + SCRIPT_FONTPROPERTIES* properties) +{ + for (int i = 0; i < length; ++i) { + if (glyphs[i] == properties->wgDefault + || (glyphs[i] == properties->wgInvalid + && glyphs[i] != properties->wgBlank)) + return true; + } + + return false; +} + +// HFONT is the 'incarnation' of 'everything' about font, but it's an opaque +// handle and we can't directly query it to make a new HFONT sharing +// its characteristics (height, style, etc) except for family name. +// This function uses GetObject to convert HFONT back to LOGFONT, +// resets the fields of LOGFONT and calculates style to use later +// for the creation of a font identical to HFONT other than family name. +static void setLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style) +{ + ASSERT(hfont && logfont); + if (!hfont || !logfont) + return; + + GetObject(hfont, sizeof(LOGFONT), logfont); + // We reset these fields to values appropriate for CreateFontIndirect. + // while keeping lfHeight, which is the most important value in creating + // a new font similar to hfont. + logfont->lfWidth = 0; + logfont->lfEscapement = 0; + logfont->lfOrientation = 0; + logfont->lfCharSet = DEFAULT_CHARSET; + logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS; + logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings. + logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + if (style) + *style = getStyleFromLogfont(logfont); +} + +UniscribeHelper::UniscribeHelper(const UChar* input, + int inputLength, + bool isRtl, + HFONT hfont, + SCRIPT_CACHE* scriptCache, + SCRIPT_FONTPROPERTIES* fontProperties) + : m_input(input) + , m_inputLength(inputLength) + , m_isRtl(isRtl) + , m_hfont(hfont) + , m_scriptCache(scriptCache) + , m_fontProperties(fontProperties) + , m_directionalOverride(false) + , m_inhibitLigate(false) + , m_letterSpacing(0) + , m_spaceWidth(0) + , m_wordSpacing(0) + , m_ascent(0) +{ + m_logfont.lfFaceName[0] = 0; +} + +UniscribeHelper::~UniscribeHelper() +{ +} + +void UniscribeHelper::initWithOptionalLengthProtection(bool lengthProtection) +{ + // We cap the input length and just don't do anything. We'll allocate a lot + // of things of the size of the number of characters, so the allocated + // memory will be several times the input length. Plus shaping such a large + // buffer may be a form of denial of service. No legitimate text should be + // this long. It also appears that Uniscribe flatly rejects very long + // strings, so we don't lose anything by doing this. + // + // The input length protection may be disabled by the unit tests to cause + // an error condition. + static const int kMaxInputLength = 65535; + if (m_inputLength == 0 || (lengthProtection && m_inputLength > kMaxInputLength)) + return; + + fillRuns(); + fillShapes(); + fillScreenOrder(); +} + +int UniscribeHelper::width() const +{ + int width = 0; + for (int itemIndex = 0; itemIndex < static_cast<int>(m_runs.size()); itemIndex++) + width += advanceForItem(itemIndex); + return width; +} + +void UniscribeHelper::justify(int additionalSpace) +{ + // Count the total number of glyphs we have so we know how big to make the + // buffers below. + int totalGlyphs = 0; + for (size_t run = 0; run < m_runs.size(); run++) { + int runIndex = m_screenOrder[run]; + totalGlyphs += static_cast<int>(m_shapes[runIndex].glyphLength()); + } + if (totalGlyphs == 0) + return; // Nothing to do. + + // We make one big buffer in screen order of all the glyphs we are drawing + // across runs so that the justification function will adjust evenly across + // all glyphs. + Vector<SCRIPT_VISATTR, 64> visualAttributes; + visualAttributes.resize(totalGlyphs); + Vector<int, 64> advances; + advances.resize(totalGlyphs); + Vector<int, 64> justify; + justify.resize(totalGlyphs); + + // Build the packed input. + int destIndex = 0; + for (size_t run = 0; run < m_runs.size(); run++) { + int runIndex = m_screenOrder[run]; + const Shaping& shaping = m_shapes[runIndex]; + + for (int i = 0; i < shaping.glyphLength(); i++, destIndex++) { + memcpy(&visualAttributes[destIndex], &shaping.m_visualAttributes[i], + sizeof(SCRIPT_VISATTR)); + advances[destIndex] = shaping.m_advance[i]; + } + } + + // The documentation for Scriptjustify is wrong, the parameter is the space + // to add and not the width of the column you want. + const int minKashida = 1; // How do we decide what this should be? + ScriptJustify(&visualAttributes[0], &advances[0], totalGlyphs, + additionalSpace, minKashida, &justify[0]); + + // Now we have to unpack the justification amounts back into the runs so + // the glyph indices match. + int globalGlyphIndex = 0; + for (size_t run = 0; run < m_runs.size(); run++) { + int runIndex = m_screenOrder[run]; + Shaping& shaping = m_shapes[runIndex]; + + shaping.m_justify.resize(shaping.glyphLength()); + for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++) + shaping.m_justify[i] = justify[globalGlyphIndex]; + } +} + +int UniscribeHelper::characterToX(int offset) const +{ + HRESULT hr; + ASSERT(offset <= m_inputLength); + + // Our algorithm is to traverse the items in screen order from left to + // right, adding in each item's screen width until we find the item with + // the requested character in it. + int width = 0; + for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { + // Compute the length of this run. + int itemIndex = m_screenOrder[screenIndex]; + const SCRIPT_ITEM& item = m_runs[itemIndex]; + const Shaping& shaping = m_shapes[itemIndex]; + int itemLength = shaping.charLength(); + + if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) { + // Character offset is in this run. + int charLength = offset - item.iCharPos; + + int curX = 0; + hr = ScriptCPtoX(charLength, FALSE, itemLength, + shaping.glyphLength(), + &shaping.m_logs[0], &shaping.m_visualAttributes[0], + shaping.effectiveAdvances(), &item.a, &curX); + if (FAILED(hr)) + return 0; + + width += curX + shaping.m_prePadding; + ASSERT(width >= 0); + return width; + } + + // Move to the next item. + width += advanceForItem(itemIndex); + } + ASSERT(width >= 0); + return width; +} + +int UniscribeHelper::xToCharacter(int x) const +{ + // We iterate in screen order until we find the item with the given pixel + // position in it. When we find that guy, we ask Uniscribe for the + // character index. + HRESULT hr; + for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { + int itemIndex = m_screenOrder[screenIndex]; + int itemAdvance = advanceForItem(itemIndex); + + // Note that the run may be empty if shaping failed, so we want to skip + // over it. + const Shaping& shaping = m_shapes[itemIndex]; + int itemLength = shaping.charLength(); + if (x <= itemAdvance && itemLength > 0) { + // The requested offset is within this item. + const SCRIPT_ITEM& item = m_runs[itemIndex]; + + // Account for the leading space we've added to this run that + // Uniscribe doesn't know about. + x -= shaping.m_prePadding; + + int charX = 0; + int trailing; + hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(), + &shaping.m_logs[0], &shaping.m_visualAttributes[0], + shaping.effectiveAdvances(), &item.a, &charX, + &trailing); + + // The character offset is within the item. We need to add the + // item's offset to transform it into the space of the TextRun + return charX + item.iCharPos; + } + + // The offset is beyond this item, account for its length and move on. + x -= itemAdvance; + } + + // Error condition, we don't know what to do if we don't have that X + // position in any of our items. + return 0; +} + +void UniscribeHelper::draw(HDC dc, int x, int y, int from, int to) +{ + HGDIOBJ oldFont = 0; + int curX = x; + bool firstRun = true; + + for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { + int itemIndex = m_screenOrder[screenIndex]; + const SCRIPT_ITEM& item = m_runs[itemIndex]; + const Shaping& shaping = m_shapes[itemIndex]; + + // Character offsets within this run. THESE MAY NOT BE IN RANGE and may + // be negative, etc. The code below handles this. + int fromChar = from - item.iCharPos; + int toChar = to - item.iCharPos; + + // See if we need to draw any characters in this item. + if (shaping.charLength() == 0 || + fromChar >= shaping.charLength() || toChar <= 0) { + // No chars in this item to display. + curX += advanceForItem(itemIndex); + continue; + } + + // Compute the starting glyph within this span. |from| and |to| are + // global offsets that may intersect arbitrarily with our local run. + int fromGlyph, afterGlyph; + if (item.a.fRTL) { + // To compute the first glyph when going RTL, we use |to|. + if (toChar >= shaping.charLength()) + // The end of the text is after (to the left) of us. + fromGlyph = 0; + else { + // Since |to| is exclusive, the first character we draw on the + // left is actually the one right before (to the right) of + // |to|. + fromGlyph = shaping.m_logs[toChar - 1]; + } + + // The last glyph is actually the first character in the range. + if (fromChar <= 0) { + // The first character to draw is before (to the right) of this + // span, so draw all the way to the end. + afterGlyph = shaping.glyphLength(); + } else { + // We want to draw everything up until the character to the + // right of |from|. To the right is - 1, so we look that up + // (remember our character could be more than one glyph, so we + // can't look up our glyph and add one). + afterGlyph = shaping.m_logs[fromChar - 1]; + } + } else { + // Easy case, everybody agrees about directions. We only need to + // handle boundary conditions to get a range inclusive at the + // beginning, and exclusive at the ending. We have to do some + // computation to see the glyph one past the end. + fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar]; + if (toChar >= shaping.charLength()) + afterGlyph = shaping.glyphLength(); + else + afterGlyph = shaping.m_logs[toChar]; + } + + // Account for the characters that were skipped in this run. When + // WebKit asks us to draw a subset of the run, it actually tells us + // to draw at the X offset of the beginning of the run, since it + // doesn't know the internal position of any of our characters. + const int* effectiveAdvances = shaping.effectiveAdvances(); + int innerOffset = 0; + for (int i = 0; i < fromGlyph; i++) + innerOffset += effectiveAdvances[i]; + + // Actually draw the glyphs we found. + int glyphCount = afterGlyph - fromGlyph; + if (fromGlyph >= 0 && glyphCount > 0) { + // Account for the preceeding space we need to add to this run. We + // don't need to count for the following space because that will be + // counted in advanceForItem below when we move to the next run. + innerOffset += shaping.m_prePadding; + + // Pass 0 in when there is no justification. + const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_justify[fromGlyph]; + + if (firstRun) { + oldFont = SelectObject(dc, shaping.m_hfont); + firstRun = false; + } else + SelectObject(dc, shaping.m_hfont); + + // Fonts with different ascents can be used to render different + // runs. 'Across-runs' y-coordinate correction needs to be + // adjusted for each font. + HRESULT hr = S_FALSE; + for (int executions = 0; executions < 2; ++executions) { + hr = ScriptTextOut(dc, shaping.m_scriptCache, + curX + innerOffset, + y - shaping.m_ascentOffset, + 0, 0, &item.a, 0, 0, + &shaping.m_glyphs[fromGlyph], + glyphCount, + &shaping.m_advance[fromGlyph], + justify, + &shaping.m_offsets[fromGlyph]); + if (S_OK != hr && 0 == executions) { + // If this ScriptTextOut is called from the renderer it + // might fail because the sandbox is preventing it from + // opening the font files. If we are running in the + // renderer, TryToPreloadFont is overridden to ask the + // browser to preload the font for us so we can access it. + tryToPreloadFont(shaping.m_hfont); + continue; + } + break; + } + + ASSERT(S_OK == hr); + } + + curX += advanceForItem(itemIndex); + } + + if (oldFont) + SelectObject(dc, oldFont); +} + +WORD UniscribeHelper::firstGlyphForCharacter(int charOffset) const +{ + // Find the run for the given character. + for (int i = 0; i < static_cast<int>(m_runs.size()); i++) { + int firstChar = m_runs[i].iCharPos; + const Shaping& shaping = m_shapes[i]; + int localOffset = charOffset - firstChar; + if (localOffset >= 0 && localOffset < shaping.charLength()) { + // The character is in this run, return the first glyph for it + // (should generally be the only glyph). It seems Uniscribe gives + // glyph 0 for empty, which is what we want to return in the + // "missing" case. + size_t glyphIndex = shaping.m_logs[localOffset]; + if (glyphIndex >= shaping.m_glyphs.size()) { + // The glyph should be in this run, but the run has too few + // actual characters. This can happen when shaping the run + // fails, in which case, we should have no data in the logs at + // all. + ASSERT(shaping.m_glyphs.size() == 0); + return 0; + } + return shaping.m_glyphs[glyphIndex]; + } + } + + return 0; +} + +void UniscribeHelper::fillRuns() +{ + HRESULT hr; + m_runs.resize(UNISCRIBE_HELPER_STACK_RUNS); + + SCRIPT_STATE inputState; + inputState.uBidiLevel = m_isRtl; + inputState.fOverrideDirection = m_directionalOverride; + inputState.fInhibitSymSwap = false; + inputState.fCharShape = false; // Not implemented in Uniscribe + inputState.fDigitSubstitute = false; // Do we want this for Arabic? + inputState.fInhibitLigate = m_inhibitLigate; + inputState.fDisplayZWG = false; // Don't draw control characters. + inputState.fArabicNumContext = m_isRtl; // Do we want this for Arabic? + inputState.fGcpClusters = false; + inputState.fReserved = 0; + inputState.fEngineReserved = 0; + // The psControl argument to ScriptItemize should be non-0 for RTL text, + // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a + // SCRIPT_CONTROL that is set to all zeros. Zero as a locale ID means the + // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx + static SCRIPT_CONTROL inputControl = {0, // uDefaultLanguage :16; + 0, // fContextDigits :1; + 0, // fInvertPreBoundDir :1; + 0, // fInvertPostBoundDir :1; + 0, // fLinkStringBefore :1; + 0, // fLinkStringAfter :1; + 0, // fNeutralOverride :1; + 0, // fNumericOverride :1; + 0, // fLegacyBidiClass :1; + 0, // fMergeNeutralItems :1; + 0};// fReserved :7; + // Calling ScriptApplyDigitSubstitution( 0, &inputControl, &inputState) + // here would be appropriate if we wanted to set the language ID, and get + // local digit substitution behavior. For now, don't do it. + + while (true) { + int numberOfItems = 0; + + // Ideally, we would have a way to know the runs before and after this + // one, and put them into the control parameter of ScriptItemize. This + // would allow us to shape characters properly that cross style + // boundaries (WebKit bug 6148). + // + // We tell ScriptItemize that the output list of items is one smaller + // than it actually is. According to Mozilla bug 366643, if there is + // not enough room in the array on pre-SP2 systems, ScriptItemize will + // write one past the end of the buffer. + // + // ScriptItemize is very strange. It will often require a much larger + // ITEM buffer internally than it will give us as output. For example, + // it will say a 16-item buffer is not big enough, and will write + // interesting numbers into all those items. But when we give it a 32 + // item buffer and it succeeds, it only has one item output. + // + // It seems to be doing at least two passes, the first where it puts a + // lot of intermediate data into our items, and the second where it + // collates them. + hr = ScriptItemize(m_input, m_inputLength, + static_cast<int>(m_runs.size()) - 1, &inputControl, + &inputState, + &m_runs[0], &numberOfItems); + if (SUCCEEDED(hr)) { + m_runs.resize(numberOfItems); + break; + } + if (hr != E_OUTOFMEMORY) { + // Some kind of unexpected error. + m_runs.resize(0); + break; + } + // There was not enough items for it to write into, expand. + m_runs.resize(m_runs.size() * 2); + } +} + +bool UniscribeHelper::shape(const UChar* input, + int itemLength, + int numGlyphs, + SCRIPT_ITEM& run, + Shaping& shaping) +{ + HFONT hfont = m_hfont; + SCRIPT_CACHE* scriptCache = m_scriptCache; + SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties; + int ascent = m_ascent; + HDC tempDC = 0; + HGDIOBJ oldFont = 0; + HRESULT hr; + bool lastFallbackTried = false; + bool result; + + int generatedGlyphs = 0; + + // In case HFONT passed in ctor cannot render this run, we have to scan + // other fonts from the beginning of the font list. + resetFontIndex(); + + // Compute shapes. + while (true) { + shaping.m_logs.resize(itemLength); + shaping.m_glyphs.resize(numGlyphs); + shaping.m_visualAttributes.resize(numGlyphs); + +#ifdef PURIFY + // http://code.google.com/p/chromium/issues/detail?id=5309 + // Purify isn't able to track the assignments that ScriptShape makes to + // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it + // writes, will be considered un-initialized data. + // + // This hack avoid the false-positive UMRs by marking the buffer as + // initialized. + // + // FIXME: A better solution would be to use Purify's API and mark only + // the populated range as initialized: + // + // PurifyMarkAsInitialized( + // &shaping.m_glyphs[0], + // sizeof(shaping.m_glyphs[0] * generatedGlyphs); + + ZeroMemory(&shaping.m_glyphs[0], + sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size()); +#endif + + // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true + // here. Is that what we want? It will display control characters. + hr = ScriptShape(tempDC, scriptCache, input, itemLength, + numGlyphs, &run.a, + &shaping.m_glyphs[0], &shaping.m_logs[0], + &shaping.m_visualAttributes[0], &generatedGlyphs); + if (hr == E_PENDING) { + // Allocate the DC. + tempDC = GetDC(0); + oldFont = SelectObject(tempDC, hfont); + continue; + } else if (hr == E_OUTOFMEMORY) { + numGlyphs *= 2; + continue; + } else if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(&shaping.m_glyphs[0], generatedGlyphs, fontProperties))) + break; + + // The current font can't render this run. clear DC and try + // next font. + if (tempDC) { + SelectObject(tempDC, oldFont); + ReleaseDC(0, tempDC); + tempDC = 0; + } + + if (nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) { + // The primary font does not support this run. Try next font. + // In case of web page rendering, they come from fonts specified in + // CSS stylesheets. + continue; + } else if (!lastFallbackTried) { + lastFallbackTried = true; + + // Generate a last fallback font based on the script of + // a character to draw while inheriting size and styles + // from the primary font + if (!m_logfont.lfFaceName[0]) + setLogFontAndStyle(m_hfont, &m_logfont, &m_style); + + // TODO(jungshik): generic type should come from webkit for + // UniscribeHelperTextRun (a derived class used in webkit). + const UChar *family = getFallbackFamily(input, itemLength, + FontDescription::StandardFamily, 0, 0); + bool fontOk = getDerivedFontData(family, m_style, &m_logfont, + &ascent, &hfont, &scriptCache); + + if (!fontOk) { + // If this GetDerivedFontData is called from the renderer it + // might fail because the sandbox is preventing it from opening + // the font files. If we are running in the renderer, + // TryToPreloadFont is overridden to ask the browser to preload + // the font for us so we can access it. + tryToPreloadFont(hfont); + + // Try again. + fontOk = getDerivedFontData(family, m_style, &m_logfont, + &ascent, &hfont, &scriptCache); + ASSERT(fontOk); + } + + // TODO(jungshik) : Currently GetDerivedHFont always returns a + // a valid HFONT, but in the future, I may change it to return 0. + ASSERT(hfont); + + // We don't need a font_properties for the last resort fallback font + // because we don't have anything more to try and are forced to + // accept empty glyph boxes. If we tried a series of fonts as + // 'last-resort fallback', we'd need it, but currently, we don't. + continue; + } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { + run.a.eScript = SCRIPT_UNDEFINED; + continue; + } else if (FAILED(hr)) { + // Error shaping. + generatedGlyphs = 0; + result = false; + goto cleanup; + } + } + + // Sets Windows font data for this run to those corresponding to + // a font supporting this run. we don't need to store font_properties + // because it's not used elsewhere. + shaping.m_hfont = hfont; + shaping.m_scriptCache = scriptCache; + + // The ascent of a font for this run can be different from + // that of the primary font so that we need to keep track of + // the difference per run and take that into account when calling + // ScriptTextOut in |draw|. Otherwise, different runs rendered by + // different fonts would not be aligned vertically. + shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0; + result = true; + + cleanup: + shaping.m_glyphs.resize(generatedGlyphs); + shaping.m_visualAttributes.resize(generatedGlyphs); + shaping.m_advance.resize(generatedGlyphs); + shaping.m_offsets.resize(generatedGlyphs); + if (tempDC) { + SelectObject(tempDC, oldFont); + ReleaseDC(0, tempDC); + } + // On failure, our logs don't mean anything, so zero those out. + if (!result) + shaping.m_logs.clear(); + + return result; +} + +void UniscribeHelper::fillShapes() +{ + m_shapes.resize(m_runs.size()); + for (size_t i = 0; i < m_runs.size(); i++) { + int startItem = m_runs[i].iCharPos; + int itemLength = m_inputLength - startItem; + if (i < m_runs.size() - 1) + itemLength = m_runs[i + 1].iCharPos - startItem; + + int numGlyphs; + if (itemLength < UNISCRIBE_HELPER_STACK_CHARS) { + // We'll start our buffer sizes with the current stack space + // available in our buffers if the current input fits. As long as + // it doesn't expand past that we'll save a lot of time mallocing. + numGlyphs = UNISCRIBE_HELPER_STACK_CHARS; + } else { + // When the input doesn't fit, give up with the stack since it will + // almost surely not be enough room (unless the input actually + // shrinks, which is unlikely) and just start with the length + // recommended by the Uniscribe documentation as a "usually fits" + // size. + numGlyphs = itemLength * 3 / 2 + 16; + } + + // Convert a string to a glyph string trying the primary font, fonts in + // the fallback list and then script-specific last resort font. + Shaping& shaping = m_shapes[i]; + if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], shaping)) + continue; + + // Compute placements. Note that offsets is documented incorrectly + // and is actually an array. + + // DC that we lazily create if Uniscribe commands us to. + // (this does not happen often because scriptCache is already + // updated when calling ScriptShape). + HDC tempDC = 0; + HGDIOBJ oldFont = 0; + HRESULT hr; + while (true) { + shaping.m_prePadding = 0; + hr = ScriptPlace(tempDC, shaping.m_scriptCache, + &shaping.m_glyphs[0], + static_cast<int>(shaping.m_glyphs.size()), + &shaping.m_visualAttributes[0], &m_runs[i].a, + &shaping.m_advance[0], &shaping.m_offsets[0], + &shaping.m_abc); + if (hr != E_PENDING) + break; + + // Allocate the DC and run the loop again. + tempDC = GetDC(0); + oldFont = SelectObject(tempDC, shaping.m_hfont); + } + + if (FAILED(hr)) { + // Some error we don't know how to handle. Nuke all of our data + // since we can't deal with partially valid data later. + m_runs.clear(); + m_shapes.clear(); + m_screenOrder.clear(); + } + + if (tempDC) { + SelectObject(tempDC, oldFont); + ReleaseDC(0, tempDC); + } + } + + adjustSpaceAdvances(); + + if (m_letterSpacing != 0 || m_wordSpacing != 0) + applySpacing(); +} + +void UniscribeHelper::fillScreenOrder() +{ + m_screenOrder.resize(m_runs.size()); + + // We assume that the input has only one text direction in it. + // TODO(brettw) are we sure we want to keep this restriction? + if (m_isRtl) { + for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++) + m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i; + } else { + for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++) + m_screenOrder[i] = i; + } +} + +void UniscribeHelper::adjustSpaceAdvances() +{ + if (m_spaceWidth == 0) + return; + + int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing; + + // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem. + for (size_t run = 0; run < m_runs.size(); run++) { + Shaping& shaping = m_shapes[run]; + + for (int i = 0; i < shaping.charLength(); i++) { + if (!treatAsSpace(m_input[m_runs[run].iCharPos + i])) + continue; + + int glyphIndex = shaping.m_logs[i]; + int currentAdvance = shaping.m_advance[glyphIndex]; + // Don't give zero-width spaces a width. + if (!currentAdvance) + continue; + + // currentAdvance does not include additional letter-spacing, but + // space_width does. Here we find out how off we are from the + // correct width for the space not including letter-spacing, then + // just subtract that diff. + int diff = currentAdvance - spaceWidthWithoutLetterSpacing; + // The shaping can consist of a run of text, so only subtract the + // difference in the width of the glyph. + shaping.m_advance[glyphIndex] -= diff; + shaping.m_abc.abcB -= diff; + } + } +} + +void UniscribeHelper::applySpacing() +{ + for (size_t run = 0; run < m_runs.size(); run++) { + Shaping& shaping = m_shapes[run]; + bool isRtl = m_runs[run].a.fRTL; + + if (m_letterSpacing != 0) { + // RTL text gets padded to the left of each character. We increment + // the run's advance to make this happen. This will be balanced out + // by NOT adding additional advance to the last glyph in the run. + if (isRtl) + shaping.m_prePadding += m_letterSpacing; + + // Go through all the glyphs in this run and increase the "advance" + // to account for letter spacing. We adjust letter spacing only on + // cluster boundaries. + // + // This works for most scripts, but may have problems with some + // indic scripts. This behavior is better than Firefox or IE for + // Hebrew. + for (int i = 0; i < shaping.glyphLength(); i++) { + if (shaping.m_visualAttributes[i].fClusterStart) { + // Ick, we need to assign the extra space so that the glyph + // comes first, then is followed by the space. This is + // opposite for RTL. + if (isRtl) { + if (i != shaping.glyphLength() - 1) { + // All but the last character just get the spacing + // applied to their advance. The last character + // doesn't get anything, + shaping.m_advance[i] += m_letterSpacing; + shaping.m_abc.abcB += m_letterSpacing; + } + } else { + // LTR case is easier, we just add to the advance. + shaping.m_advance[i] += m_letterSpacing; + shaping.m_abc.abcB += m_letterSpacing; + } + } + } + } + + // Go through all the characters to find whitespace and insert the + // extra wordspacing amount for the glyphs they correspond to. + if (m_wordSpacing != 0) { + for (int i = 0; i < shaping.charLength(); i++) { + if (!treatAsSpace(m_input[m_runs[run].iCharPos + i])) + continue; + + // The char in question is a word separator... + int glyphIndex = shaping.m_logs[i]; + + // Spaces will not have a glyph in Uniscribe, it will just add + // additional advance to the character to the left of the + // space. The space's corresponding glyph will be the character + // following it in reading order. + if (isRtl) { + // In RTL, the glyph to the left of the space is the same + // as the first glyph of the following character, so we can + // just increment it. + shaping.m_advance[glyphIndex] += m_wordSpacing; + shaping.m_abc.abcB += m_wordSpacing; + } else { + // LTR is actually more complex here, we apply it to the + // previous character if there is one, otherwise we have to + // apply it to the leading space of the run. + if (glyphIndex == 0) + shaping.m_prePadding += m_wordSpacing; + else { + shaping.m_advance[glyphIndex - 1] += m_wordSpacing; + shaping.m_abc.abcB += m_wordSpacing; + } + } + } + } // m_wordSpacing != 0 + + // Loop for next run... + } +} + +// The advance is the ABC width of the run +int UniscribeHelper::advanceForItem(int itemIndex) const +{ + int accum = 0; + const Shaping& shaping = m_shapes[itemIndex]; + + if (shaping.m_justify.size() == 0) { + // Easy case with no justification, the width is just the ABC width of + // the run. (The ABC width is the sum of the advances). + return shaping.m_abc.abcA + shaping.m_abc.abcB + + shaping.m_abc.abcC + shaping.m_prePadding; + } + + // With justification, we use the justified amounts instead. The + // justification array contains both the advance and the extra space + // added for justification, so is the width we want. + int justification = 0; + for (size_t i = 0; i < shaping.m_justify.size(); i++) + justification += shaping.m_justify[i]; + + return shaping.m_prePadding + justification; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/UniscribeHelper.h b/WebCore/platform/graphics/chromium/UniscribeHelper.h new file mode 100644 index 0000000..d291105 --- /dev/null +++ b/WebCore/platform/graphics/chromium/UniscribeHelper.h @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +// A wrapper around Uniscribe that provides a reasonable API. + +#ifndef UniscribeHelper_h +#define UniscribeHelper_h + +#include <windows.h> +#include <usp10.h> +#include <map> + +#include <unicode/uchar.h> +#include <wtf/Vector.h> + +class UniscribeTest_TooBig_Test; // A gunit test for UniscribeHelper. + +namespace WebCore { + +#define UNISCRIBE_HELPER_STACK_RUNS 8 +#define UNISCRIBE_HELPER_STACK_CHARS 32 + +// This object should be safe to create & destroy frequently, as long as the +// caller preserves the script_cache when possible (this data may be slow to +// compute). +// +// This object is "kind of large" (~1K) because it reserves a lot of space for +// working with to avoid expensive heap operations. Therefore, not only should +// you not worry about creating and destroying it, you should try to not keep +// them around. +class UniscribeHelper { +public: + // Initializes this Uniscribe run with the text pointed to by |run| with + // |length|. The input is NOT null terminated. + // + // The is_rtl flag should be set if the input script is RTL. It is assumed + // that the caller has already divided up the input text (using ICU, for + // example) into runs of the same direction of script. This avoids + // disagreements between the caller and Uniscribe later (see FillItems). + // + // A script cache should be provided by the caller that is initialized to + // NULL. When the caller is done with the cache (it may be stored between + // runs as long as it is used consistently with the same HFONT), it should + // call ScriptFreeCache(). + UniscribeHelper(const UChar* input, + int inputLength, + bool isRtl, + HFONT, + SCRIPT_CACHE*, + SCRIPT_FONTPROPERTIES*); + + virtual ~UniscribeHelper(); + + // Sets Uniscribe's directional override flag. False by default. + bool directionalOverride() const + { + return m_directionalOverride; + } + void setDirectionalOverride(bool override) + { + m_directionalOverride = override; + } + + // Set's Uniscribe's no-ligate override flag. False by default. + bool inhibitLigate() const + { + return m_inhibitLigate; + } + void setInhibitLigate(bool inhibit) + { + m_inhibitLigate = inhibit; + } + + // Set letter spacing. We will try to insert this much space between + // graphemes (one or more glyphs perceived as a single unit by ordinary + // users of a script). Positive values increase letter spacing, negative + // values decrease it. 0 by default. + int letterSpacing() const + { + return m_letterSpacing; + } + void setLetterSpacing(int letterSpacing) + { + m_letterSpacing = letterSpacing; + } + + // Set the width of a standard space character. We use this to normalize + // space widths. Windows will make spaces after Hindi characters larger than + // other spaces. A space_width of 0 means to use the default space width. + // + // Must be set before Init() is called. + int spaceWidth() const + { + return m_spaceWidth; + } + void setSpaceWidth(int spaceWidth) + { + m_spaceWidth = spaceWidth; + } + + // Set word spacing. We will try to insert this much extra space between + // each word in the input (beyond whatever whitespace character separates + // words). Positive values lead to increased letter spacing, negative values + // decrease it. 0 by default. + // + // Must be set before Init() is called. + int wordSpacing() const + { + return m_wordSpacing; + } + void setWordSpacing(int wordSpacing) + { + m_wordSpacing = wordSpacing; + } + + void setAscent(int ascent) + { + m_ascent = ascent; + } + + // You must call this after setting any options but before doing any + // other calls like asking for widths or drawing. + void init() + { + initWithOptionalLengthProtection(true); + } + + // Returns the total width in pixels of the text run. + int width() const; + + // Call to justify the text, with the amount of space that should be ADDED + // to get the desired width that the column should be justified to. + // Normally, spaces are inserted, but for Arabic there will be kashidas + // (extra strokes) inserted instead. + // + // This function MUST be called AFTER Init(). + void justify(int additionalSpace); + + // Computes the given character offset into a pixel offset of the beginning + // of that character. + int characterToX(int offset) const; + + // Converts the given pixel X position into a logical character offset into + // the run. For positions appearing before the first character, this will + // return -1. + int xToCharacter(int x) const; + + // Draws the given characters to (x, y) in the given DC. The font will be + // handled by this function, but the font color and other attributes should + // be pre-set. + // + // The y position is the upper left corner, NOT the baseline. + void draw(HDC, int x, int y, int from, int to); + + // Returns the first glyph assigned to the character at the given offset. + // This function is used to retrieve glyph information when Uniscribe is + // being used to generate glyphs for non-complex, non-BMP (above U+FFFF) + // characters. These characters are not otherwise special and have no + // complex shaping rules, so we don't otherwise need Uniscribe, except + // Uniscribe is the only way to get glyphs for non-BMP characters. + // + // Returns 0 if there is no glyph for the given character. + WORD firstGlyphForCharacter(int charOffset) const; + +protected: + // Backend for init. The flag allows the unit test to specify whether we + // should fail early for very long strings like normal, or try to pass the + // long string to Uniscribe. The latter provides a way to force failure of + // shaping. + void initWithOptionalLengthProtection(bool lengthProtection); + + // Tries to preload the font when the it is not accessible. + // This is the default implementation and it does not do anything. + virtual void tryToPreloadFont(HFONT) {} + +private: + friend class UniscribeTest_TooBig_Test; + + // An array corresponding to each item in runs_ containing information + // on each of the glyphs that were generated. Like runs_, this is in + // reading order. However, for rtl text, the characters within each + // item will be reversed. + struct Shaping { + Shaping() + : m_prePadding(0) + , m_hfont(NULL) + , m_scriptCache(NULL) + , m_ascentOffset(0) { + m_abc.abcA = 0; + m_abc.abcB = 0; + m_abc.abcC = 0; + } + + // Returns the number of glyphs (which will be drawn to the screen) + // in this run. + int glyphLength() const + { + return static_cast<int>(m_glyphs.size()); + } + + // Returns the number of characters (that we started with) in this run. + int charLength() const + { + return static_cast<int>(m_logs.size()); + } + + // Returns the advance array that should be used when measuring glyphs. + // The returned pointer will indicate an array with glyph_length() + // elements and the advance that should be used for each one. This is + // either the real advance, or the justified advances if there is one, + // and is the array we want to use for measurement. + const int* effectiveAdvances() const + { + if (m_advance.size() == 0) + return 0; + if (m_justify.size() == 0) + return &m_advance[0]; + return &m_justify[0]; + } + + // This is the advance amount of space that we have added to the + // beginning of the run. It is like the ABC's |A| advance but one that + // we create and must handle internally whenever computing with pixel + // offsets. + int m_prePadding; + + // Glyph indices in the font used to display this item. These indices + // are in screen order. + Vector<WORD, UNISCRIBE_HELPER_STACK_CHARS> m_glyphs; + + // For each input character, this tells us the first glyph index it + // generated. This is the only array with size of the input chars. + // + // All offsets are from the beginning of this run. Multiple characters + // can generate one glyph, in which case there will be adjacent + // duplicates in this list. One character can also generate multiple + // glyphs, in which case there will be skipped indices in this list. + Vector<WORD, UNISCRIBE_HELPER_STACK_CHARS> m_logs; + + // Flags and such for each glyph. + Vector<SCRIPT_VISATTR, UNISCRIBE_HELPER_STACK_CHARS> m_visualAttributes; + + // Horizontal advances for each glyph listed above, this is basically + // how wide each glyph is. + Vector<int, UNISCRIBE_HELPER_STACK_CHARS> m_advance; + + // This contains glyph offsets, from the nominal position of a glyph. + // It is used to adjust the positions of multiple combining characters + // around/above/below base characters in a context-sensitive manner so + // that they don't bump against each other and the base character. + Vector<GOFFSET, UNISCRIBE_HELPER_STACK_CHARS> m_offsets; + + // Filled by a call to Justify, this is empty for nonjustified text. + // If nonempty, this contains the array of justify characters for each + // character as returned by ScriptJustify. + // + // This is the same as the advance array, but with extra space added + // for some characters. The difference between a glyph's |justify| + // width and it's |advance| width is the extra space added. + Vector<int, UNISCRIBE_HELPER_STACK_CHARS> m_justify; + + // Sizing information for this run. This treats the entire run as a + // character with a preceeding advance, width, and ending advance. The + // B width is the sum of the |advance| array, and the A and C widths + // are any extra spacing applied to each end. + // + // It is unclear from the documentation what this actually means. From + // experimentation, it seems that the sum of the character advances is + // always the sum of the ABC values, and I'm not sure what you're + // supposed to do with the ABC values. + ABC m_abc; + + // Pointers to windows font data used to render this run. + HFONT m_hfont; + SCRIPT_CACHE* m_scriptCache; + + // Ascent offset between the ascent of the primary font + // and that of the fallback font. The offset needs to be applied, + // when drawing a string, to align multiple runs rendered with + // different fonts. + int m_ascentOffset; + }; + + // Computes the runs_ array from the text run. + void fillRuns(); + + // Computes the shapes_ array given an runs_ array already filled in. + void fillShapes(); + + // Fills in the screen_order_ array (see below). + void fillScreenOrder(); + + // Called to update the glyph positions based on the current spacing + // options that are set. + void applySpacing(); + + // Normalizes all advances for spaces to the same width. This keeps windows + // from making spaces after Hindi characters larger, which is then + // inconsistent with our meaure of the width since WebKit doesn't include + // spaces in text-runs sent to uniscribe unless white-space:pre. + void adjustSpaceAdvances(); + + // Returns the total width of a single item. + int advanceForItem(int) const; + + // Shapes a run (pointed to by |input|) using |hfont| first. + // Tries a series of fonts specified retrieved with NextWinFontData + // and finally a font covering characters in |*input|. A string pointed + // by |input| comes from ScriptItemize and is supposed to contain + // characters belonging to a single script aside from characters common to + // all scripts (e.g. space). + bool shape(const UChar* input, int itemLength, int numGlyphs, SCRIPT_ITEM& run, Shaping&); + + // Gets Windows font data for the next best font to try in the list + // of fonts. When there's no more font available, returns false + // without touching any of out params. Need to call ResetFontIndex + // to start scanning of the font list from the beginning. + virtual bool nextWinFontData(HFONT*, SCRIPT_CACHE**, SCRIPT_FONTPROPERTIES**, int* ascent) + { + return false; + } + + // Resets the font index to the first in the list of fonts to try after the + // primaryFont turns out not to work. With fontIndex reset, + // NextWinFontData scans fallback fonts from the beginning. + virtual void resetFontIndex() {} + + // The input data for this run of Uniscribe. See the constructor. + const UChar* m_input; + const int m_inputLength; + const bool m_isRtl; + + // Windows font data for the primary font. In a sense, m_logfont and m_style + // are redundant because m_hfont contains all the information. However, + // invoking GetObject, everytime we need the height and the style, is rather + // expensive so that we cache them. Would it be better to add getter and + // (virtual) setter for the height and the style of the primary font, + // instead of m_logfont? Then, a derived class ctor can set m_ascent, + // m_height and m_style if they're known. Getters for them would have to + // 'infer' their values from m_hfont ONLY when they're not set. + HFONT m_hfont; + SCRIPT_CACHE* m_scriptCache; + SCRIPT_FONTPROPERTIES* m_fontProperties; + int m_ascent; + LOGFONT m_logfont; + int m_style; + + // Options, see the getters/setters above. + bool m_directionalOverride; + bool m_inhibitLigate; + int m_letterSpacing; + int m_spaceWidth; + int m_wordSpacing; + + // Uniscribe breaks the text into Runs. These are one length of text that is + // in one script and one direction. This array is in reading order. + Vector<SCRIPT_ITEM, UNISCRIBE_HELPER_STACK_RUNS> m_runs; + + Vector<Shaping, UNISCRIBE_HELPER_STACK_RUNS> m_shapes; + + // This is a mapping between reading order and screen order for the items. + // Uniscribe's items array are in reading order. For right-to-left text, + // or mixed (although WebKit's |TextRun| should really be only one + // direction), this makes it very difficult to compute character offsets + // and positions. This list is in screen order from left to right, and + // gives the index into the |m_runs| and |m_shapes| arrays of each + // subsequent item. + Vector<int, UNISCRIBE_HELPER_STACK_RUNS> m_screenOrder; +}; + +} // namespace WebCore + +#endif // UniscribeHelper_h diff --git a/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp b/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp new file mode 100644 index 0000000..f801c13 --- /dev/null +++ b/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "UniscribeHelperTextRun.h" + +#include "ChromiumBridge.h" +#include "Font.h" +#include "SimpleFontData.h" + +namespace WebCore { + +UniscribeHelperTextRun::UniscribeHelperTextRun(const TextRun& run, + const Font& font) + : UniscribeHelper(run.characters(), run.length(), run.rtl(), + font.primaryFont()->platformData().hfont(), + font.primaryFont()->platformData().scriptCache(), + font.primaryFont()->platformData().scriptFontProperties()) + , m_font(&font) + , m_fontIndex(0) +{ + setDirectionalOverride(run.directionalOverride()); + setLetterSpacing(font.letterSpacing()); + setSpaceWidth(font.spaceWidth()); + setWordSpacing(font.wordSpacing()); + setAscent(font.primaryFont()->ascent()); + + init(); + + // Padding is the amount to add to make justification happen. This + // should be done after Init() so all the runs are already measured. + if (run.padding() > 0) + justify(run.padding()); +} + +UniscribeHelperTextRun::UniscribeHelperTextRun( + const wchar_t* input, + int inputLength, + bool isRtl, + HFONT hfont, + SCRIPT_CACHE* scriptCache, + SCRIPT_FONTPROPERTIES* fontProperties) + : UniscribeHelper(input, inputLength, isRtl, hfont, + scriptCache, fontProperties) + , m_font(0) + , m_fontIndex(-1) +{ +} + +void UniscribeHelperTextRun::tryToPreloadFont(HFONT font) +{ + // Ask the browser to get the font metrics for this font. + // That will preload the font and it should now be accessible + // from the renderer. + ChromiumBridge::ensureFontLoaded(font); +} + +bool UniscribeHelperTextRun::nextWinFontData( + HFONT* hfont, + SCRIPT_CACHE** scriptCache, + SCRIPT_FONTPROPERTIES** fontProperties, + int* ascent) +{ + // This check is necessary because NextWinFontData can be called again + // after we already ran out of fonts. fontDataAt behaves in a strange + // manner when the difference between param passed and # of fonts stored in + // WebKit::Font is larger than one. We can avoid this check by setting + // font_index_ to # of elements in hfonts_ when we run out of font. In that + // case, we'd have to go through a couple of more checks before returning + // false. + if (m_fontIndex == -1 || !m_font) + return false; + + // If the font data for a fallback font requested is not yet retrieved, add + // them to our vectors. Note that '>' rather than '>=' is used to test that + // condition. primaryFont is not stored in hfonts_, and friends so that + // indices for fontDataAt and our vectors for font data are 1 off from each + // other. That is, when fully populated, hfonts_ and friends have one font + // fewer than what's contained in font_. + if (static_cast<size_t>(++m_fontIndex) > m_hfonts.size()) { + const FontData *fontData = m_font->fontDataAt(m_fontIndex); + if (!fontData) { + // Ran out of fonts. + m_fontIndex = -1; + return false; + } + + // FIXME: this won't work for SegmentedFontData + // http://crbug.com/6425 + const SimpleFontData* simpleFontData = + fontData->fontDataForCharacter(' '); + + m_hfonts.append(simpleFontData->platformData().hfont()); + m_scriptCaches.append(simpleFontData->platformData().scriptCache()); + m_fontProperties.append(simpleFontData->platformData().scriptFontProperties()); + m_ascents.append(simpleFontData->ascent()); + } + + *hfont = m_hfonts[m_fontIndex - 1]; + *scriptCache = m_scriptCaches[m_fontIndex - 1]; + *fontProperties = m_fontProperties[m_fontIndex - 1]; + *ascent = m_ascents[m_fontIndex - 1]; + return true; +} + +void UniscribeHelperTextRun::resetFontIndex() +{ + m_fontIndex = 0; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h b/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h new file mode 100644 index 0000000..b5c54a0 --- /dev/null +++ b/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 UniscribeHelperTextRun_h +#define UniscribeHelperTextRun_h + +#include "UniscribeHelper.h" + +namespace WebCore { + +class Font; +class TextRun; + +// Wrapper around the Uniscribe helper that automatically sets it up with the +// WebKit types we supply. +class UniscribeHelperTextRun : public UniscribeHelper { +public: + // Regular constructor used for WebCore text run processing. + UniscribeHelperTextRun(const TextRun&, const Font&); + + // Constructor with the same interface as the gfx::UniscribeState. Using + // this constructor will not give you font fallback, but it will provide + // the ability to load fonts that may not be in the OS cache + // ("TryToPreloadFont") if the caller does not have a TextRun/Font. + UniscribeHelperTextRun(const wchar_t* input, + int inputLength, + bool isRtl, + HFONT hfont, + SCRIPT_CACHE*, + SCRIPT_FONTPROPERTIES*); + +protected: + virtual void tryToPreloadFont(HFONT); + +private: + // This function retrieves the Windows font data (HFONT, etc) for the next + // WebKit font in the list. If the font data corresponding to font_index_ + // has been obtained before, returns the values stored in our internal + // vectors (hfonts_, etc). Otherwise, it gets next SimpleFontData from + // WebKit and adds them to in hfonts_ and friends so that font data can be + // returned quickly next time they're requested. + virtual bool nextWinFontData(HFONT*, SCRIPT_CACHE**, SCRIPT_FONTPROPERTIES**, int* ascent); + virtual void resetFontIndex(); + + // Reference to WebKit::Font that contains all the information about fonts + // we can use to render this input run of text. It is used in + // NextWinFontData to retrieve Windows font data for a series of + // non-primary fonts. + // + // This pointer can be NULL for no font fallback handling. + const Font* m_font; + + // It's rare that many fonts are listed in stylesheets. + // Four would be large enough in most cases. + const static size_t kNumberOfFonts = 4; + + // These vectors are used to store Windows font data for non-primary fonts. + Vector<HFONT, kNumberOfFonts> m_hfonts; + Vector<SCRIPT_CACHE*, kNumberOfFonts> m_scriptCaches; + Vector<SCRIPT_FONTPROPERTIES*, kNumberOfFonts> m_fontProperties; + Vector<int, kNumberOfFonts> m_ascents; + + // Index of the fallback font we're currently using for NextWinFontData. + int m_fontIndex; +}; + +} // namespace WebCore + +#endif // UniscribeHelperTextRun_h diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp index 1ca3e95..d076cb6 100644 --- a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp @@ -63,6 +63,7 @@ void SimpleFontData::platformInit() void SimpleFontData::platformDestroy() { delete m_smallCapsFontData; + m_smallCapsFontData = 0; if (isCustomFont()) return; diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp index 8621865..db8dd3b 100644 --- a/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp +++ b/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp @@ -80,6 +80,7 @@ void SimpleFontData::platformDestroy() } delete m_smallCapsFontData; + m_smallCapsFontData = 0; } SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const @@ -97,25 +98,27 @@ bool SimpleFontData::containsCharacters(const UChar* characters, int length) con { bool result = true; - PangoCoverage* requested = pango_coverage_from_bytes((guchar*)characters, length); - PangoCoverage* available = pango_font_get_coverage(m_font.m_font, pango_language_get_default()); - pango_coverage_max(requested, available); + PangoCoverage* coverage = pango_font_get_coverage(m_font.m_font, pango_language_get_default()); for (int i = 0; i < length; i++) { - if (PANGO_COVERAGE_NONE == pango_coverage_get(requested, i)) { + if (PANGO_COVERAGE_NONE == pango_coverage_get(coverage, characters[i])) { result = false; break; } } - pango_coverage_unref(requested); - pango_coverage_unref(available); + pango_coverage_unref(coverage); return result; } void SimpleFontData::determinePitch() { + if (isCustomFont()) { + m_treatAsFixedPitch = false; + return; + } + m_treatAsFixedPitch = m_font.isFixedPitch(); } diff --git a/WebCore/platform/graphics/mac/ColorMac.h b/WebCore/platform/graphics/mac/ColorMac.h index 3be9094..830e9d9 100644 --- a/WebCore/platform/graphics/mac/ColorMac.h +++ b/WebCore/platform/graphics/mac/ColorMac.h @@ -39,6 +39,7 @@ class NSColor; namespace WebCore { + // These functions assume NSColors are in DeviceRGB colorspace Color colorFromNSColor(NSColor *); NSColor* nsColor(const Color&); diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm index 96fdc39..9b0f770 100644 --- a/WebCore/platform/graphics/mac/ColorMac.mm +++ b/WebCore/platform/graphics/mac/ColorMac.mm @@ -28,6 +28,7 @@ #import "ColorMac.h" #import <wtf/Assertions.h> +#import <wtf/StdLibExtras.h> #import <wtf/RetainPtr.h> @interface WebCoreControlTintObserver : NSObject @@ -59,37 +60,30 @@ NSColor* nsColor(const Color& color) switch (c) { case 0: { // Need this to avoid returning nil because cachedRGBAValues will default to 0. - static RetainPtr<NSColor> clearColor = [NSColor clearColor]; + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, clearColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:0.0f])); return clearColor.get(); } case Color::black: { - static RetainPtr<NSColor> blackColor = [NSColor blackColor]; + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, blackColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:1.0f])); return blackColor.get(); } case Color::white: { - static RetainPtr<NSColor> whiteColor = [NSColor whiteColor]; + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, whiteColor, ([NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f])); return whiteColor.get(); } default: { const int cacheSize = 32; static unsigned cachedRGBAValues[cacheSize]; - static RetainPtr<NSColor> cachedColors[cacheSize]; + static RetainPtr<NSColor>* cachedColors = new RetainPtr<NSColor>[cacheSize]; for (int i = 0; i != cacheSize; ++i) if (cachedRGBAValues[i] == c) return cachedColors[i].get(); -#ifdef COLORMATCH_EVERYTHING - NSColor* result = [NSColor colorWithCalibratedRed:color.red() / 255.0f - green:color.green() / 255.0f - blue:color.blue() / 255.0f - alpha:color.alpha() /255.0f]; -#else NSColor* result = [NSColor colorWithDeviceRed:color.red() / 255.0f green:color.green() / 255.0f blue:color.blue() / 255.0f alpha:color.alpha() /255.0f]; -#endif static int cursor; cachedRGBAValues[cursor] = c; @@ -158,12 +152,8 @@ void setUsesTestModeFocusRingColor(bool newValue) + (void)controlTintDidChange { -#ifdef COLORMATCH_EVERYTHING -#error Not yet implemented. -#else NSColor* color = [[NSColor keyboardFocusIndicatorColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; WebCore::systemFocusRingColor = WebCore::makeRGBAFromNSColor(color); -#endif } @end diff --git a/WebCore/platform/graphics/mac/CoreTextController.cpp b/WebCore/platform/graphics/mac/CoreTextController.cpp index 171a7ec..49e83c4 100644 --- a/WebCore/platform/graphics/mac/CoreTextController.cpp +++ b/WebCore/platform/graphics/mac/CoreTextController.cpp @@ -369,9 +369,9 @@ void CoreTextController::collectCoreTextRunsForCharacters(const UChar* cp, unsig static const void* optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel }; static const void* ltrOptionValues[] = { kCFBooleanFalse }; static const void* rtlOptionValues[] = { kCFBooleanTrue }; - static RetainPtr<CFDictionaryRef> ltrTypesetterOptions(AdoptCF, CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); - static RetainPtr<CFDictionaryRef> rtlTypesetterOptions(AdoptCF, CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); - typesetter.adoptCF(CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions.get() : rtlTypesetterOptions.get())); + static CFDictionaryRef ltrTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + static CFDictionaryRef rtlTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + typesetter.adoptCF(CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions)); } else typesetter.adoptCF(CTTypesetterCreateWithAttributedString(attributedString.get())); diff --git a/WebCore/platform/graphics/mac/FontCacheMac.mm b/WebCore/platform/graphics/mac/FontCacheMac.mm index e7cda66..26d84cc 100644 --- a/WebCore/platform/graphics/mac/FontCacheMac.mm +++ b/WebCore/platform/graphics/mac/FontCacheMac.mm @@ -35,6 +35,7 @@ #import "FontPlatformData.h" #import "WebCoreSystemInterface.h" #import "WebFontCache.h" +#include <wtf/StdLibExtras.h> #ifdef BUILDING_ON_TIGER typedef int NSInteger; @@ -44,7 +45,7 @@ namespace WebCore { static void fontCacheATSNotificationCallback(ATSFontNotificationInfoRef, void*) { - FontCache::invalidate(); + fontCache()->invalidate(); } void FontCache::platformInit() @@ -139,10 +140,10 @@ FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) const FontFamily* currFamily = &font.fontDescription().family(); while (currFamily && !platformData) { if (currFamily->family().length()) { - static String matchWords[3] = { String("Arabic"), String("Pashto"), String("Urdu") }; - static AtomicString geezaStr("Geeza Pro"); + static String* matchWords[3] = { new String("Arabic"), new String("Pashto"), new String("Urdu") }; + DEFINE_STATIC_LOCAL(AtomicString, geezaStr, ("Geeza Pro")); for (int j = 0; j < 3 && !platformData; ++j) - if (currFamily->family().contains(matchWords[j], false)) + if (currFamily->family().contains(*matchWords[j], false)) platformData = getCachedFontPlatformData(font.fontDescription(), geezaStr); } currFamily = currFamily->next(); @@ -153,8 +154,8 @@ FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) { - static AtomicString timesStr("Times"); - static AtomicString lucidaGrandeStr("Lucida Grande"); + DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times")); + DEFINE_STATIC_LOCAL(AtomicString, lucidaGrandeStr, ("Lucida Grande")); // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick // the default that the user would get without changing any prefs. diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp index 1fb144c..9aa4997 100644 --- a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp @@ -68,7 +68,7 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) CGFontRef cgFontRef = CGFontCreateWithPlatformFont(&fontRef); #ifndef BUILDING_ON_TIGER // Workaround for <rdar://problem/5675504>. - if (!CGFontGetNumberOfGlyphs(cgFontRef)) { + if (cgFontRef && !CGFontGetNumberOfGlyphs(cgFontRef)) { CFRelease(cgFontRef); cgFontRef = 0; } diff --git a/WebCore/platform/graphics/mac/FontMacATSUI.mm b/WebCore/platform/graphics/mac/FontMacATSUI.mm index 9a45c5a..52493e7 100644 --- a/WebCore/platform/graphics/mac/FontMacATSUI.mm +++ b/WebCore/platform/graphics/mac/FontMacATSUI.mm @@ -154,8 +154,7 @@ static void initializeATSUStyle(const SimpleFontData* fontData) fontData->m_ATSUStyleInitialized = true; } -static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, URefCon iRefCon, - void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus) +static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector, ATSULineRef iLineRef, URefCon iRefCon, void*, ATSULayoutOperationCallbackStatus* oCallbackStatus) { ATSULayoutParameters* params = reinterpret_cast<ATSULayoutParameters*>(iRefCon); OSStatus status; @@ -592,7 +591,7 @@ float Font::floatWidthForComplexText(const TextRun& run) const MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x)); } -int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const +int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool /*includePartialGlyphs*/) const { OwnArrayPtr<UChar> charactersWithOverride; TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); diff --git a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm index 15e573d..7cd9ab6 100644 --- a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm +++ b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm @@ -34,7 +34,7 @@ FontPlatformData::FontPlatformData(NSFont *f, bool b , bool o) CFRetain(f); m_size = f ? [f pointSize] : 0.0f; #ifndef BUILDING_ON_TIGER - m_cgFont = CTFontCopyGraphicsFont(toCTFontRef(f), 0); + m_cgFont.adoptCF(CTFontCopyGraphicsFont(toCTFontRef(f), 0)); m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(f), 0); #else m_cgFont = wkGetCGFontFromNSFont(f); @@ -86,7 +86,7 @@ void FontPlatformData::setFont(NSFont *font) m_font = font; m_size = font ? [font pointSize] : 0.0f; #ifndef BUILDING_ON_TIGER - m_cgFont = CTFontCopyGraphicsFont(toCTFontRef(font), 0); + m_cgFont.adoptCF(CTFontCopyGraphicsFont(toCTFontRef(font), 0)); m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(font), 0); #else m_cgFont = wkGetCGFontFromNSFont(font); diff --git a/WebCore/platform/graphics/mac/GraphicsContextMac.mm b/WebCore/platform/graphics/mac/GraphicsContextMac.mm index 3f9176c..ae829e2 100644 --- a/WebCore/platform/graphics/mac/GraphicsContextMac.mm +++ b/WebCore/platform/graphics/mac/GraphicsContextMac.mm @@ -27,6 +27,7 @@ #import "GraphicsContext.h" #import "../cg/GraphicsContextPlatformPrivateCG.h" +#import <wtf/StdLibExtras.h> #import "WebCoreSystemInterface.h" @@ -80,53 +81,42 @@ void GraphicsContext::setCompositeOperation(CompositeOperator op) [pool drain]; } #endif - + +static NSColor* createPatternColor(NSString* name, NSColor* defaultColor, bool& usingDot) +{ + NSImage *image = [NSImage imageNamed:name]; + ASSERT(image); // if image is not available, we want to know + NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil); + if (color) + usingDot = true; + else + color = defaultColor; + return color; +} + void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& point, int width, bool grammar) { if (paintingDisabled()) return; - // Constants for spelling pattern color - static RetainPtr<NSColor> spellingPatternColor = nil; - static bool usingDotForSpelling = false; - - // Constants for grammar pattern color - static RetainPtr<NSColor> grammarPatternColor = nil; - static bool usingDotForGrammar = false; - // These are the same for misspelling or bad grammar int patternHeight = cMisspellingLineThickness; int patternWidth = cMisspellingLinePatternWidth; - // Initialize pattern color if needed - if (!grammar && !spellingPatternColor) { - NSImage *image = [NSImage imageNamed:@"SpellingDot"]; - ASSERT(image); // if image is not available, we want to know - NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil); - if (color) - usingDotForSpelling = true; - else - color = [NSColor redColor]; - spellingPatternColor = color; - } - - if (grammar && !grammarPatternColor) { - NSImage *image = [NSImage imageNamed:@"GrammarDot"]; - ASSERT(image); // if image is not available, we want to know - NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil); - if (color) - usingDotForGrammar = true; - else - color = [NSColor greenColor]; - grammarPatternColor = color; - } - bool usingDot; NSColor *patternColor; if (grammar) { + // Constants for grammar pattern color + static bool usingDotForGrammar = false; + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, grammarPatternColor, (createPatternColor(@"GrammarDot", [NSColor greenColor], usingDotForGrammar))); + usingDot = usingDotForGrammar; patternColor = grammarPatternColor.get(); } else { + // Constants for spelling pattern color + static bool usingDotForSpelling = false; + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, spellingPatternColor, (createPatternColor(@"SpellingDot", [NSColor redColor], usingDotForSpelling))); + usingDot = usingDotForSpelling; patternColor = spellingPatternColor.get(); } diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index 0ec56d6..a33c8d2 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -29,14 +29,19 @@ #import "MediaPlayerPrivateQTKit.h" +#ifdef BUILDING_ON_TIGER +#import "AutodrainedPool.h" +#endif + #import "BlockExceptions.h" +#import "FrameView.h" #import "GraphicsContext.h" #import "KURL.h" -#import "FrameView.h" #import "SoftLinking.h" #import "WebCoreSystemInterface.h" #import <QTKit/QTKit.h> #import <objc/objc-runtime.h> +#import <wtf/UnusedParam.h> #if DRAW_FRAME_RATE #import "Font.h" @@ -239,7 +244,7 @@ void MediaPlayerPrivate::createQTMovie(const String& url) object:m_qtMovie.get()]; } -static void mainThreadSetNeedsDisplay(id self, SEL _cmd) +static void mainThreadSetNeedsDisplay(id self, SEL) { id movieView = [self superview]; ASSERT(!movieView || [movieView isKindOfClass:[QTMovieView class]]); @@ -772,6 +777,10 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) context->scale(FloatSize(1.0f, -1.0f)); context->setImageInterpolationQuality(InterpolationLow); IntRect paintRect(IntPoint(0, 0), IntSize(r.width(), r.height())); + +#ifdef BUILDING_ON_TIGER + AutodrainedPool pool; +#endif NSGraphicsContext* newContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context->platformContext() flipped:NO]; // draw the current video frame @@ -969,48 +978,54 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) m_callback->repaint(); } -- (void)loadStateChanged:(NSNotification *)notification +- (void)loadStateChanged:(NSNotification *)unusedNotification { + UNUSED_PARAM(unusedNotification); if (m_delayCallbacks) [self performSelector:_cmd withObject:nil afterDelay:0]; else m_callback->loadStateChanged(); } -- (void)rateChanged:(NSNotification *)notification +- (void)rateChanged:(NSNotification *)unusedNotification { + UNUSED_PARAM(unusedNotification); if (m_delayCallbacks) [self performSelector:_cmd withObject:nil afterDelay:0]; else m_callback->rateChanged(); } -- (void)sizeChanged:(NSNotification *)notification +- (void)sizeChanged:(NSNotification *)unusedNotification { + UNUSED_PARAM(unusedNotification); if (m_delayCallbacks) [self performSelector:_cmd withObject:nil afterDelay:0]; else m_callback->sizeChanged(); } -- (void)timeChanged:(NSNotification *)notification +- (void)timeChanged:(NSNotification *)unusedNotification { + UNUSED_PARAM(unusedNotification); if (m_delayCallbacks) [self performSelector:_cmd withObject:nil afterDelay:0]; else m_callback->timeChanged(); } -- (void)didEnd:(NSNotification *)notification +- (void)didEnd:(NSNotification *)unusedNotification { + UNUSED_PARAM(unusedNotification); if (m_delayCallbacks) [self performSelector:_cmd withObject:nil afterDelay:0]; else m_callback->didEnd(); } -- (void)newImageAvailable:(NSNotification *)notification +- (void)newImageAvailable:(NSNotification *)unusedNotification { + UNUSED_PARAM(unusedNotification); [self repaint]; } diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index 4ee5933..30dbf97 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -42,6 +42,7 @@ #import <float.h> #import <unicode/uchar.h> #import <wtf/Assertions.h> +#import <wtf/StdLibExtras.h> #import <wtf/RetainPtr.h> @interface NSFont (WebAppKitSecretAPI) @@ -54,7 +55,7 @@ const float smallCapsFontSizeMultiplier = 0.7f; const float contextDPI = 72.0f; static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x * (contextDPI / (contextDPI * unitsPerEm)); } -bool initFontData(SimpleFontData* fontData) +static bool initFontData(SimpleFontData* fontData) { if (!fontData->m_font.cgFont()) return false; @@ -92,9 +93,7 @@ bool initFontData(SimpleFontData* fontData) static NSString *webFallbackFontFamily(void) { - static RetainPtr<NSString> webFallbackFontFamily = nil; - if (!webFallbackFontFamily) - webFallbackFontFamily = [[NSFont systemFontOfSize:16.0f] familyName]; + DEFINE_STATIC_LOCAL(RetainPtr<NSString>, webFallbackFontFamily, ([[NSFont systemFontOfSize:16.0f] familyName])); return webFallbackFontFamily.get(); } @@ -315,7 +314,7 @@ SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDes smallCapsFont.m_syntheticBold = (fontTraits & NSBoldFontMask) && !(smallCapsFontTraits & NSBoldFontMask); smallCapsFont.m_syntheticOblique = (fontTraits & NSItalicFontMask) && !(smallCapsFontTraits & NSItalicFontMask); - m_smallCapsFontData = FontCache::getCachedFontData(&smallCapsFont); + m_smallCapsFontData = fontCache()->getCachedFontData(&smallCapsFont); } END_BLOCK_OBJC_EXCEPTIONS; } @@ -325,7 +324,7 @@ SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDes bool SimpleFontData::containsCharacters(const UChar* characters, int length) const { - NSString *string = [[NSString alloc] initWithCharactersNoCopy:(UniChar*)characters length:length freeWhenDone:NO]; + NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<unichar*>(characters) length:length freeWhenDone:NO]; NSCharacterSet *set = [[m_font.font() coveredCharacterSet] invertedSet]; bool result = set && [string rangeOfCharacterFromSet:set].location == NSNotFound; [string release]; diff --git a/WebCore/platform/graphics/qt/FontCacheQt.cpp b/WebCore/platform/graphics/qt/FontCacheQt.cpp index be31d96..114f073 100644 --- a/WebCore/platform/graphics/qt/FontCacheQt.cpp +++ b/WebCore/platform/graphics/qt/FontCacheQt.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -21,18 +22,31 @@ */ #include "config.h" #include "FontCache.h" + #include "FontDescription.h" +#include "FontPlatformData.h" #include "Font.h" +#include <wtf/StdLibExtras.h> namespace WebCore { +FontCache* fontCache() +{ + DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); + return &globalFontCache; +} + +FontCache::FontCache() +{ +} + void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) { } -FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription&, const AtomicString& family, bool checkingAlternateName) +FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& description, const AtomicString& family, bool checkingAlternateName) { - return 0; + return new FontPlatformData(description); } SimpleFontData* FontCache::getCachedFontData(const FontPlatformData*) @@ -45,6 +59,10 @@ FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription&) return 0; } +void FontCache::releaseFontData(const WebCore::SimpleFontData*) +{ +} + void FontCache::addClient(FontSelector*) { } diff --git a/WebCore/platform/graphics/qt/FontCustomPlatformData.cpp b/WebCore/platform/graphics/qt/FontCustomPlatformData.cpp index 8fc3ea0..a19464e 100644 --- a/WebCore/platform/graphics/qt/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/qt/FontCustomPlatformData.cpp @@ -25,19 +25,25 @@ #include "FontPlatformData.h" #include "SharedBuffer.h" #include <QFontDatabase> +#include <QStringList> namespace WebCore { FontCustomPlatformData::~FontCustomPlatformData() { - QFontDatabase::removeApplicationFont(handle); + QFontDatabase::removeApplicationFont(m_handle); } FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode) { - FontPlatformData result; - result.handle = handle; - return result; + QFont font; + font.setFamily(QFontDatabase::applicationFontFamilies(m_handle)[0]); + font.setPixelSize(size); + if (bold) + font.setWeight(QFont::Bold); + font.setItalic(italic); + + return FontPlatformData(font, bold); } FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) @@ -47,8 +53,11 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) int id = QFontDatabase::addApplicationFontFromData(QByteArray(buffer->data(), buffer->size())); if (id == -1) return 0; + + Q_ASSERT(QFontDatabase::applicationFontFamilies(id).size() > 0); + FontCustomPlatformData *data = new FontCustomPlatformData; - data->handle = id; + data->m_handle = id; return data; } diff --git a/WebCore/platform/graphics/qt/FontCustomPlatformData.h b/WebCore/platform/graphics/qt/FontCustomPlatformData.h index da5159d..4305b87 100644 --- a/WebCore/platform/graphics/qt/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/qt/FontCustomPlatformData.h @@ -33,7 +33,8 @@ class FontPlatformData; struct FontCustomPlatformData : Noncopyable { ~FontCustomPlatformData(); - int handle; // for use with QFontDatabase::addApplicationFont/removeApplicationFont + // for use with QFontDatabase::addApplicationFont/removeApplicationFont + int m_handle; FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); }; diff --git a/WebCore/platform/graphics/qt/FontFallbackListQt.cpp b/WebCore/platform/graphics/qt/FontFallbackListQt.cpp new file mode 100644 index 0000000..22ae205 --- /dev/null +++ b/WebCore/platform/graphics/qt/FontFallbackListQt.cpp @@ -0,0 +1,106 @@ +/* + Copyright (C) 2008 Holger Hans Peter Freyther + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + Replacement of the stock FontFallbackList as Qt is going to find us a + replacement font, will do caching and the other stuff we implement in + WebKit. +*/ + +#include "config.h" +#include "FontFallbackList.h" + +#include "Font.h" +#include "SegmentedFontData.h" + +#include <QDebug> + +namespace WebCore { + +FontFallbackList::FontFallbackList() + : m_familyIndex(0) + , m_pitch(UnknownPitch) + , m_loadingCustomFonts(false) + , m_fontSelector(0) + , m_generation(0) +{ +} + +void FontFallbackList::invalidate(WTF::PassRefPtr<WebCore::FontSelector> fontSelector) +{ + releaseFontData(); + m_fontList.clear(); + m_familyIndex = 0; + m_pitch = UnknownPitch; + m_loadingCustomFonts = false; + m_fontSelector = fontSelector; + m_generation = 0; +} + +void FontFallbackList::releaseFontData() +{ +} + +void FontFallbackList::determinePitch(const WebCore::Font* font) const +{ + const FontData* fontData = primaryFont(font); + if (!fontData->isSegmented()) + m_pitch = static_cast<const SimpleFontData*>(fontData)->pitch(); + else { + const SegmentedFontData* segmentedFontData = static_cast<const SegmentedFontData*>(fontData); + unsigned numRanges = segmentedFontData->numRanges(); + if (numRanges == 1) + m_pitch = segmentedFontData->rangeAt(0).fontData()->pitch(); + else + m_pitch = VariablePitch; + } +} + +const FontData* FontFallbackList::fontDataAt(const WebCore::Font* _font, unsigned index) const +{ + if (index != 0) + return 0; + + // Use the FontSelector to get a WebCore font and then fallback to Qt + const FontDescription& description = _font->fontDescription(); + const FontFamily* family = &description.family(); + while (family) { + if (m_fontSelector) { + FontData* data = m_fontSelector->getFontData(description, family->family()); + if (data) { + if (data->isLoading()) + m_loadingCustomFonts = true; + return data; + } + } + family = family->next(); + } + + return new SimpleFontData(FontPlatformData(description), _font->wordSpacing(), _font->letterSpacing()); +} + +const FontData* FontFallbackList::fontDataForCharacters(const WebCore::Font* font, const UChar*, int) const +{ + return primaryFont(font); +} + +void FontFallbackList::setPlatformFont(const WebCore::FontPlatformData& platformData) +{ + m_familyIndex = cAllFamiliesScanned; +} + +} diff --git a/WebCore/platform/graphics/qt/FontPlatformData.h b/WebCore/platform/graphics/qt/FontPlatformData.h index e4363be..5e97678 100644 --- a/WebCore/platform/graphics/qt/FontPlatformData.h +++ b/WebCore/platform/graphics/qt/FontPlatformData.h @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -22,14 +23,29 @@ #ifndef FontPlatformData_h #define FontPlatformData_h +#include "FontDescription.h" + +#include <QFont> + namespace WebCore { class FontPlatformData { public: - // this is only used for custom loaded fonts and represents the id handle passed to - // QFontDatabase::addApplicationFont/removeApplicationFont - int handle; +#if ENABLE(SVG_FONTS) + FontPlatformData(float size, bool bold, bool oblique); +#endif + FontPlatformData(); + FontPlatformData(const FontDescription&, int wordSpacing = 0, int letterSpacing = 0); + FontPlatformData(const QFont&, bool bold); + + QFont font() const { return m_font; } + float size() const { return m_size; } + + float m_size; + bool m_bold; + bool m_oblique; + QFont m_font; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp new file mode 100644 index 0000000..ea51fe8 --- /dev/null +++ b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2008 Holger Hans Peter Freyther + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "config.h" +#include "FontPlatformData.h" + +namespace WebCore { + +FontPlatformData::FontPlatformData(const FontDescription& description, int wordSpacing, int letterSpacing) + : m_size(0.0f) + , m_bold(false) + , m_oblique(false) +{ + QString familyName; + const FontFamily* family = &description.family(); + while (family) { + familyName += family->family(); + family = family->next(); + if (family) + familyName += QLatin1Char(','); + } + + m_font.setFamily(familyName); + m_font.setPixelSize(qRound(description.computedSize())); + m_font.setItalic(description.italic()); + // FIXME: Map all FontWeight values to QFont weights. + if (description.weight() >= FontWeight600) + m_font.setWeight(QFont::Bold); + else + m_font.setWeight(QFont::Normal); + + bool smallCaps = description.smallCaps(); + m_font.setCapitalization(smallCaps ? QFont::SmallCaps : QFont::MixedCase); + m_font.setWordSpacing(wordSpacing); + m_font.setLetterSpacing(QFont::AbsoluteSpacing, letterSpacing); + m_size = m_font.pointSize(); +} + +FontPlatformData::FontPlatformData(const QFont& font, bool bold) + : m_size(font.pointSize()) + , m_bold(bold) + , m_oblique(false) + , m_font(font) +{ +} + +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) + : m_size(size) + , m_bold(bold) + , m_oblique(oblique) +{ +} + +FontPlatformData::FontPlatformData() + : m_size(0.0f) + , m_bold(false) + , m_oblique(false) +{ +} + +} diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp index e2ef605..deeea99 100644 --- a/WebCore/platform/graphics/qt/FontQt.cpp +++ b/WebCore/platform/graphics/qt/FontQt.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -20,6 +21,7 @@ #include "config.h" #include "Font.h" #include "FontDescription.h" +#include "FontFallbackList.h" #include "FontSelector.h" #include "GraphicsContext.h" @@ -31,66 +33,9 @@ #include <qdebug.h> #include <limits.h> -namespace WebCore { #if QT_VERSION >= 0x040400 - -Font::Font() - : m_letterSpacing(0) - , m_wordSpacing(0) - , m_font() - , m_scFont() -{ - QFontMetrics metrics(m_font); - m_spaceWidth = metrics.width(QLatin1Char(' ')); -} - -Font::Font(const FontDescription& description, short letterSpacing, short wordSpacing) - : m_fontDescription(description) - , m_letterSpacing(letterSpacing) - , m_wordSpacing(wordSpacing) -{ - const FontFamily* family = &description.family(); - QString familyName; - while (family) { - familyName += family->family(); - family = family->next(); - if (family) - familyName += QLatin1Char(','); - } - - m_font.setFamily(familyName); - m_font.setPixelSize(qRound(description.computedSize())); - m_font.setItalic(description.italic()); - // FIXME: Map all FontWeight values to QFont weights. - if (description.weight() >= FontWeight600) - m_font.setWeight(QFont::Bold); - else - m_font.setWeight(QFont::Normal); - - bool smallCaps = description.smallCaps(); - m_font.setCapitalization(smallCaps ? QFont::SmallCaps : QFont::MixedCase); - - QFontMetrics metrics = QFontMetrics(m_font); - m_spaceWidth = metrics.width(QLatin1Char(' ')); - - if (wordSpacing) - m_font.setWordSpacing(wordSpacing); - if (letterSpacing) - m_font.setLetterSpacing(QFont::AbsoluteSpacing, letterSpacing); -} - -void Font::setWordSpacing(short s) -{ - m_font.setWordSpacing(s); - m_wordSpacing = s; -} -void Font::setLetterSpacing(short s) -{ - m_font.setLetterSpacing(QFont::AbsoluteSpacing, s); - m_letterSpacing = s; -} - +namespace WebCore { static QString qstring(const TextRun& run) { @@ -121,10 +66,11 @@ static QTextLine setupLayout(QTextLayout* layout, const TextRun& style) return line; } -void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const +void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const { if (to < 0) to = run.length(); + QPainter *p = ctx->platformContext(); Color color = ctx->fillColor(); p->setPen(QColor(color)); @@ -138,14 +84,14 @@ void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& bool hasShadow = ctx->textDrawingMode() == cTextFill && ctx->getShadow(shadowSize, shadowBlur, shadowColor); if (from > 0 || to < run.length()) { - QTextLayout layout(string, m_font); + QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); float x1 = line.cursorToX(from); float x2 = line.cursorToX(to); if (x2 < x1) qSwap(x1, x2); - QFontMetrics fm(m_font); + QFontMetrics fm(font()); int ascent = fm.ascent(); QRectF clip(point.x() + x1, point.y() - ascent, x2 - x1, fm.height()); @@ -179,7 +125,7 @@ void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& return; } - p->setFont(m_font); + p->setFont(font()); QPointF pt(point.x(), point.y()); int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; @@ -194,12 +140,12 @@ void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& p->drawText(pt, string, flags, run.padding()); } -int Font::width(const TextRun& run) const +float Font::floatWidthForComplexText(const TextRun& run) const { if (!run.length()) return 0; QString string = qstring(run); - QTextLayout layout(string, m_font); + QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); int w = int(line.naturalTextWidth()); // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does) @@ -209,30 +155,18 @@ int Font::width(const TextRun& run) const return w + run.padding(); } -float Font::floatWidth(const TextRun& run) const -{ - return width(run); -} - -float Font::floatWidth(const TextRun& run, int /*extraCharsAvailable*/, int& charsConsumed, String& glyphName) const -{ - charsConsumed = run.length(); - glyphName = ""; - return width(run); -} - -int Font::offsetForPosition(const TextRun& run, int position, bool /*includePartialGlyphs*/) const +int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool includePartialGlyphs) const { QString string = qstring(run); - QTextLayout layout(string, m_font); + QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); return line.xToCursor(position); } -FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& pt, int h, int from, int to) const +FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& pt, int h, int from, int to) const { QString string = qstring(run); - QTextLayout layout(string, m_font); + QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); float x1 = line.cursorToX(from); @@ -243,464 +177,12 @@ FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& pt, int return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h); } -#else - - -struct TextRunComponent { - TextRunComponent() : font(0) {} - TextRunComponent(const UChar *start, int length, bool rtl, const QFont *font, int offset, bool sc = false); - TextRunComponent(int spaces, bool rtl, const QFont *font, int offset); - - inline bool isSpace() const { return spaces != 0; } - - QString string; - const QFont *font; - int width; - int offset; - int spaces; -}; - -TextRunComponent::TextRunComponent(const UChar *start, int length, bool rtl, const QFont *f, int o, bool sc) - : string(reinterpret_cast<const QChar*>(start), length) - , font(f) - , offset(o) - , spaces(0) -{ - if (sc) - string = string.toUpper(); - string.prepend(rtl ? QChar(0x202e) : QChar(0x202d)); - width = QFontMetrics(*font).width(string); -} - -TextRunComponent::TextRunComponent(int s, bool rtl, const QFont *f, int o) - : string(s, QLatin1Char(' ')) - , font(f) - , offset(o) - , spaces(s) -{ - string.prepend(rtl ? QChar(0x202e) : QChar(0x202d)); - width = spaces * QFontMetrics(*font).width(QLatin1Char(' ')); -} - - -Font::Font() - : m_letterSpacing(0) - , m_wordSpacing(0) - , m_font() - , m_scFont() -{ - QFontMetrics metrics(m_font); - m_spaceWidth = metrics.width(QLatin1Char(' ')); - qreal pointsize = m_font.pointSizeF(); - if (pointsize > 0) - m_scFont.setPointSizeF(pointsize*0.7); - else - m_scFont.setPixelSize(qRound(m_font.pixelSize()*.7)); -} - -Font::Font(const FontDescription& description, short letterSpacing, short wordSpacing) - : m_fontDescription(description) - , m_letterSpacing(letterSpacing) - , m_wordSpacing(wordSpacing) -{ - const FontFamily* family = &description.family(); - QString familyName; - while (family) { - familyName += family->family(); - family = family->next(); - if (family) - familyName += QLatin1Char(','); - } - - m_font.setFamily(familyName); - m_font.setPixelSize(qRound(description.computedSize())); - m_font.setItalic(description.italic()); - // FIXME: Map all FontWeight values to QFont weights. - if (description.weight() >= FontWeight600) - m_font.setWeight(QFont::Bold); - else - m_font.setWeight(QFont::Normal); - - QFontMetrics metrics = QFontMetrics(m_font); - m_spaceWidth = metrics.width(QLatin1Char(' ')); - m_scFont = m_font; - m_scFont.setPixelSize(qRound(description.computedSize()*.7)); -} - -void Font::setWordSpacing(short s) -{ - m_wordSpacing = s; -} -void Font::setLetterSpacing(short s) -{ - m_letterSpacing = s; -} - -static int generateComponents(Vector<TextRunComponent, 1024>* components, const Font &font, const TextRun &run) -{ -// qDebug() << "generateComponents" << QString((const QChar *)run.characters(), run.length()); - int letterSpacing = font.letterSpacing(); - int wordSpacing = font.wordSpacing(); - bool smallCaps = font.fontDescription().smallCaps(); - int padding = run.padding(); - int numSpaces = 0; - if (padding) { - for (int i = 0; i < run.length(); i++) - if (Font::treatAsSpace(run[i])) - ++numSpaces; - } - - int offset = 0; - const QFont *f = &font.font(); - if (letterSpacing || smallCaps) { - // need to draw every letter on it's own - int start = 0; - if (Font::treatAsSpace(run[0])) { - int add = 0; - if (numSpaces) { - add = padding/numSpaces; - padding -= add; - --numSpaces; - } - components->append(TextRunComponent(1, run.rtl(), &font.font(), offset)); - offset += add + letterSpacing + components->last().width; - start = 1; -// qDebug() << "space at 0" << offset; - } else if (smallCaps) { - f = (QChar::category(run[0]) == QChar::Letter_Lowercase ? &font.scFont() : &font.font()); - } - for (int i = 1; i < run.length(); ++i) { - uint ch = run[i]; - if (QChar(ch).isHighSurrogate() && QChar(run[i-1]).isLowSurrogate()) - ch = QChar::surrogateToUcs4(ch, run[i-1]); - if (QChar(ch).isLowSurrogate() || QChar::category(ch) == QChar::Mark_NonSpacing) - continue; - if (Font::treatAsSpace(run[i])) { - int add = 0; -// qDebug() << " treatAsSpace:" << i << start; - if (i - start > 0) { - components->append(TextRunComponent(run.characters() + start, i - start, - run.rtl(), - f, offset, f == &font.scFont())); - offset += components->last().width + letterSpacing; -// qDebug() << " appending(1) " << components->last().string << components->last().width; - } - if (numSpaces) { - add = padding/numSpaces; - padding -= add; - --numSpaces; - } - components->append(TextRunComponent(1, run.rtl(), &font.font(), offset)); - offset += wordSpacing + add + components->last().width + letterSpacing; - start = i + 1; - continue; - } else if (!letterSpacing) { -// qDebug() << i << char(run[i]) << (QChar::category(ch) == QChar::Letter_Lowercase) << -// QFontInfo(*f).pointSizeF(); - if (QChar::category(ch) == QChar::Letter_Lowercase) { - if (f == &font.scFont()) - continue; - } else { - if (f == &font.font()) - continue; - } - } - if (i - start > 0) { - components->append(TextRunComponent(run.characters() + start, i - start, - run.rtl(), - f, offset, f == &font.scFont())); - offset += components->last().width + letterSpacing; -// qDebug() << " appending(2) " << components->last().string << components->last().width; - } - if (smallCaps) - f = (QChar::category(ch) == QChar::Letter_Lowercase ? &font.scFont() : &font.font()); - start = i; - } - if (run.length() - start > 0) { - components->append(TextRunComponent(run.characters() + start, run.length() - start, - run.rtl(), - f, offset, f == &font.scFont())); - offset += components->last().width; -// qDebug() << " appending(3) " << components->last().string << components->last().width; - } - offset += letterSpacing; - } else { - int start = 0; - for (int i = 0; i < run.length(); ++i) { - if (Font::treatAsSpace(run[i])) { - if (i - start > 0) { - components->append(TextRunComponent(run.characters() + start, i - start, - run.rtl(), - f, offset)); - offset += components->last().width; - } - int add = 0; - if (numSpaces) { - add = padding/numSpaces; - padding -= add; - --numSpaces; - } - components->append(TextRunComponent(1, run.rtl(), &font.font(), offset)); - offset += add + components->last().width; - if (i) - offset += wordSpacing; - start = i + 1; - } - } - if (run.length() - start > 0) { - components->append(TextRunComponent(run.characters() + start, run.length() - start, - run.rtl(), - f, offset)); - offset += components->last().width; - } - } - return offset; -} - -void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const -{ - if (to < 0) - to = run.length(); - QPainter *p = ctx->platformContext(); - Color color = ctx->fillColor(); - p->setPen(QColor(color)); - - Vector<TextRunComponent, 1024> components; - int w = generateComponents(&components, *this, run); - - if (from > 0 || to < run.length()) { - FloatRect clip = selectionRectForText(run, - IntPoint(qRound(point.x()), qRound(point.y())), - QFontMetrics(m_font).height(), from, to); - QRectF rect(clip.x(), clip.y() - ascent(), clip.width(), clip.height()); - p->save(); - p->setClipRect(rect.toRect()); - } - - if (run.rtl()) { - for (int i = 0; i < components.size(); ++i) { - if (!components.at(i).isSpace()) { - p->setFont(*components.at(i).font); - QPointF pt(point.x() + w - components.at(i).offset - components.at(i).width, point.y()); - p->drawText(pt, components.at(i).string); - } - } - } else { - for (int i = 0; i < components.size(); ++i) { - if (!components.at(i).isSpace()) { - p->setFont(*components.at(i).font); - QPointF pt(point.x() + components.at(i).offset, point.y()); - p->drawText(pt, components.at(i).string); - } - } - } - if (from > 0 || to < run.length()) - p->restore(); -} - -int Font::width(const TextRun& run) const -{ - Vector<TextRunComponent, 1024> components; - int w = generateComponents(&components, *this, run); - -// qDebug() << " width=" << w; - return w; -} - -float Font::floatWidth(const TextRun& run) const -{ - return width(run); -} - -float Font::floatWidth(const TextRun& run, int /*extraCharsAvailable*/, int& charsConsumed, String& glyphName) const +QFont Font::font() const { - charsConsumed = run.length(); - glyphName = ""; - return width(run); + return primaryFont()->getQtFont(); } -int Font::offsetForPosition(const TextRun& run, int position, bool includePartialGlyphs) const -{ - Vector<TextRunComponent, 1024> components; - int w = generateComponents(&components, *this, run); - - int offset = 0; - if (run.rtl()) { - for (int i = 0; i < components.size(); ++i) { - int xe = w - components.at(i).offset; - int xs = xe - components.at(i).width; - if (position >= xs) { - QTextLayout layout(components.at(i).string, *components.at(i).font); - layout.beginLayout(); - QTextLine l = layout.createLine(); - if (!l.isValid()) - return offset; - - l.setLineWidth(INT_MAX/256); - layout.endLayout(); - - if (position - xs >= l.width()) - return offset; - int cursor = l.xToCursor(position - xs); - if (cursor > 1) - --cursor; - return offset + cursor; - } else { - offset += components.at(i).string.length() - 1; - } - } - } else { - for (int i = 0; i < components.size(); ++i) { - int xs = components.at(i).offset; - int xe = xs + components.at(i).width; - if (position <= xe) { - QTextLayout layout(components.at(i).string, *components.at(i).font); - layout.beginLayout(); - QTextLine l = layout.createLine(); - if (!l.isValid()) - return offset; - - l.setLineWidth(INT_MAX/256); - layout.endLayout(); - - if (position - xs >= l.width()) - return offset + components.at(i).string.length() - 1; - int cursor = l.xToCursor(position - xs); - if (cursor > 1) - --cursor; - return offset + cursor; - } else { - offset += components.at(i).string.length() - 1; - } - } - } - return run.length(); } -static float cursorToX(const Vector<TextRunComponent, 1024>& components, int width, bool rtl, int cursor) -{ - int start = 0; - for (int i = 0; i < components.size(); ++i) { - if (start + components.at(i).string.length() - 1 < cursor) { - start += components.at(i).string.length() - 1; - continue; - } - int xs = components.at(i).offset; - if (rtl) - xs = width - xs - components.at(i).width; - QTextLayout layout(components.at(i).string, *components.at(i).font); - layout.beginLayout(); - QTextLine l = layout.createLine(); - if (!l.isValid()) - return 0; - - l.setLineWidth(INT_MAX/256); - layout.endLayout(); - - return xs + l.cursorToX(cursor - start + 1); - } - return width; -} - -FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& pt, - int h, int from, int to) const -{ - Vector<TextRunComponent, 1024> components; - int w = generateComponents(&components, *this, run); - - if (from == 0 && to == run.length()) - return FloatRect(pt.x(), pt.y(), w, h); - - float x1 = cursorToX(components, w, run.rtl(), from); - float x2 = cursorToX(components, w, run.rtl(), to); - if (x2 < x1) - qSwap(x1, x2); - - return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h); -} #endif - -Font::~Font() -{ -} - -Font::Font(const Font& other) - : m_fontDescription(other.m_fontDescription) - , m_letterSpacing(other.m_letterSpacing) - , m_wordSpacing(other.m_wordSpacing) - , m_font(other.m_font) - , m_scFont(other.m_scFont) - , m_spaceWidth(other.m_spaceWidth) -{ -} - -Font& Font::operator=(const Font& other) -{ - m_fontDescription = other.m_fontDescription; - m_letterSpacing = other.m_letterSpacing; - m_wordSpacing = other.m_wordSpacing; - m_font = other.m_font; - m_scFont = other.m_scFont; - m_spaceWidth = other.m_spaceWidth; - return *this; -} - -bool Font::operator==(const Font& other) const -{ - return m_fontDescription == other.m_fontDescription - && m_letterSpacing == other.m_letterSpacing - && m_wordSpacing == other.m_wordSpacing - && m_font == other.m_font - && m_scFont == other.m_scFont - && m_spaceWidth == other.m_spaceWidth; -} - -void Font::update(PassRefPtr<FontSelector>) const -{ - // don't think we need this -} - - -bool Font::isFixedPitch() const -{ - return QFontInfo(m_font).fixedPitch(); -} - -// Metrics that we query the FontFallbackList for. -int Font::ascent() const -{ - return QFontMetrics(m_font).ascent(); -} - -int Font::descent() const -{ - return QFontMetrics(m_font).descent(); -} - -int Font::lineSpacing() const -{ - return QFontMetrics(m_font).lineSpacing(); -} - -int Font::lineGap() const -{ - return QFontMetrics(m_font).leading(); -} - -float Font::xHeight() const -{ - return QFontMetrics(m_font).xHeight(); -} - -unsigned Font::unitsPerEm() const -{ - return 1; // FIXME! -} - -int Font::spaceWidth() const -{ - return m_spaceWidth; -} - -} diff --git a/WebCore/platform/graphics/qt/FontQt43.cpp b/WebCore/platform/graphics/qt/FontQt43.cpp new file mode 100644 index 0000000..137b7c9 --- /dev/null +++ b/WebCore/platform/graphics/qt/FontQt43.cpp @@ -0,0 +1,356 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "Font.h" +#include "FontDescription.h" +#include "FontFallbackList.h" +#include "FontSelector.h" + +#include "GraphicsContext.h" +#include <QTextLayout> +#include <QPainter> +#include <QFontMetrics> +#include <QFontInfo> +#include <qalgorithms.h> +#include <qdebug.h> + +#include <limits.h> + +#if QT_VERSION < 0x040400 + +namespace WebCore { + +struct TextRunComponent { + TextRunComponent() : font(0) {} + TextRunComponent(const UChar *start, int length, bool rtl, const QFont *font, int offset, bool sc = false); + TextRunComponent(int spaces, bool rtl, const QFont *font, int offset); + + inline bool isSpace() const { return spaces != 0; } + + QString string; + const QFont *font; + int width; + int offset; + int spaces; +}; + +TextRunComponent::TextRunComponent(const UChar *start, int length, bool rtl, const QFont *f, int o, bool sc) + : string(reinterpret_cast<const QChar*>(start), length) + , font(f) + , offset(o) + , spaces(0) +{ + if (sc) + string = string.toUpper(); + string.prepend(rtl ? QChar(0x202e) : QChar(0x202d)); + width = QFontMetrics(*font).width(string); +} + +TextRunComponent::TextRunComponent(int s, bool rtl, const QFont *f, int o) + : string(s, QLatin1Char(' ')) + , font(f) + , offset(o) + , spaces(s) +{ + string.prepend(rtl ? QChar(0x202e) : QChar(0x202d)); + width = spaces * QFontMetrics(*font).width(QLatin1Char(' ')); +} + + +static int generateComponents(Vector<TextRunComponent, 1024>* components, const Font &font, const TextRun &run) +{ +// qDebug() << "generateComponents" << QString((const QChar *)run.characters(), run.length()); + int letterSpacing = font.letterSpacing(); + int wordSpacing = font.wordSpacing(); + bool smallCaps = font.fontDescription().smallCaps(); + int padding = run.padding(); + int numSpaces = 0; + if (padding) { + for (int i = 0; i < run.length(); i++) + if (Font::treatAsSpace(run[i])) + ++numSpaces; + } + + int offset = 0; + const QFont *f = &font.font(); + if (letterSpacing || smallCaps) { + // need to draw every letter on it's own + int start = 0; + if (Font::treatAsSpace(run[0])) { + int add = 0; + if (numSpaces) { + add = padding/numSpaces; + padding -= add; + --numSpaces; + } + components->append(TextRunComponent(1, run.rtl(), &font.font(), offset)); + offset += add + letterSpacing + components->last().width; + start = 1; +// qDebug() << "space at 0" << offset; + } else if (smallCaps) { + f = (QChar::category(run[0]) == QChar::Letter_Lowercase ? &font.scFont() : &font.font()); + } + for (int i = 1; i < run.length(); ++i) { + uint ch = run[i]; + if (QChar(ch).isHighSurrogate() && QChar(run[i-1]).isLowSurrogate()) + ch = QChar::surrogateToUcs4(ch, run[i-1]); + if (QChar(ch).isLowSurrogate() || QChar::category(ch) == QChar::Mark_NonSpacing) + continue; + if (Font::treatAsSpace(run[i])) { + int add = 0; +// qDebug() << " treatAsSpace:" << i << start; + if (i - start > 0) { + components->append(TextRunComponent(run.characters() + start, i - start, + run.rtl(), + f, offset, f == &font.scFont())); + offset += components->last().width + letterSpacing; +// qDebug() << " appending(1) " << components->last().string << components->last().width; + } + if (numSpaces) { + add = padding/numSpaces; + padding -= add; + --numSpaces; + } + components->append(TextRunComponent(1, run.rtl(), &font.font(), offset)); + offset += wordSpacing + add + components->last().width + letterSpacing; + start = i + 1; + continue; + } else if (!letterSpacing) { +// qDebug() << i << char(run[i]) << (QChar::category(ch) == QChar::Letter_Lowercase) << +// QFontInfo(*f).pointSizeF(); + if (QChar::category(ch) == QChar::Letter_Lowercase) { + if (f == &font.scFont()) + continue; + } else { + if (f == &font.font()) + continue; + } + } + if (i - start > 0) { + components->append(TextRunComponent(run.characters() + start, i - start, + run.rtl(), + f, offset, f == &font.scFont())); + offset += components->last().width + letterSpacing; +// qDebug() << " appending(2) " << components->last().string << components->last().width; + } + if (smallCaps) + f = (QChar::category(ch) == QChar::Letter_Lowercase ? &font.scFont() : &font.font()); + start = i; + } + if (run.length() - start > 0) { + components->append(TextRunComponent(run.characters() + start, run.length() - start, + run.rtl(), + f, offset, f == &font.scFont())); + offset += components->last().width; +// qDebug() << " appending(3) " << components->last().string << components->last().width; + } + offset += letterSpacing; + } else { + int start = 0; + for (int i = 0; i < run.length(); ++i) { + if (Font::treatAsSpace(run[i])) { + if (i - start > 0) { + components->append(TextRunComponent(run.characters() + start, i - start, + run.rtl(), + f, offset)); + offset += components->last().width; + } + int add = 0; + if (numSpaces) { + add = padding/numSpaces; + padding -= add; + --numSpaces; + } + components->append(TextRunComponent(1, run.rtl(), &font.font(), offset)); + offset += add + components->last().width; + if (i) + offset += wordSpacing; + start = i + 1; + } + } + if (run.length() - start > 0) { + components->append(TextRunComponent(run.characters() + start, run.length() - start, + run.rtl(), + f, offset)); + offset += components->last().width; + } + } + return offset; +} + +void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const +{ + if (to < 0) + to = run.length(); + + QPainter *p = ctx->platformContext(); + Color color = ctx->fillColor(); + p->setPen(QColor(color)); + + Vector<TextRunComponent, 1024> components; + int w = generateComponents(&components, *this, run); + + if (from > 0 || to < run.length()) { + FloatRect clip = selectionRectForComplexText(run, + IntPoint(qRound(point.x()), qRound(point.y())), + QFontMetrics(font()).height(), from, to); + QRectF rect(clip.x(), clip.y() - ascent(), clip.width(), clip.height()); + p->save(); + p->setClipRect(rect.toRect()); + } + + if (run.rtl()) { + for (int i = 0; i < components.size(); ++i) { + if (!components.at(i).isSpace()) { + p->setFont(*components.at(i).font); + QPointF pt(point.x() + w - components.at(i).offset - components.at(i).width, point.y()); + p->drawText(pt, components.at(i).string); + } + } + } else { + for (int i = 0; i < components.size(); ++i) { + if (!components.at(i).isSpace()) { + p->setFont(*components.at(i).font); + QPointF pt(point.x() + components.at(i).offset, point.y()); + p->drawText(pt, components.at(i).string); + } + } + } + if (from > 0 || to < run.length()) + p->restore(); +} + +float Font::floatWidthForComplexText(const TextRun& run) const +{ + Vector<TextRunComponent, 1024> components; + int w = generateComponents(&components, *this, run); + + return w; +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool includePartialGlyphs) const +{ + Vector<TextRunComponent, 1024> components; + int w = generateComponents(&components, *this, run); + + int offset = 0; + if (run.rtl()) { + for (int i = 0; i < components.size(); ++i) { + int xe = w - components.at(i).offset; + int xs = xe - components.at(i).width; + if (position >= xs) { + QTextLayout layout(components.at(i).string, *components.at(i).font); + layout.beginLayout(); + QTextLine l = layout.createLine(); + if (!l.isValid()) + return offset; + + l.setLineWidth(INT_MAX/256); + layout.endLayout(); + + if (position - xs >= l.width()) + return offset; + int cursor = l.xToCursor(position - xs); + if (cursor > 1) + --cursor; + return offset + cursor; + } else { + offset += components.at(i).string.length() - 1; + } + } + } else { + for (int i = 0; i < components.size(); ++i) { + int xs = components.at(i).offset; + int xe = xs + components.at(i).width; + if (position <= xe) { + QTextLayout layout(components.at(i).string, *components.at(i).font); + layout.beginLayout(); + QTextLine l = layout.createLine(); + if (!l.isValid()) + return offset; + + l.setLineWidth(INT_MAX/256); + layout.endLayout(); + + if (position - xs >= l.width()) + return offset + components.at(i).string.length() - 1; + int cursor = l.xToCursor(position - xs); + if (cursor > 1) + --cursor; + return offset + cursor; + } else { + offset += components.at(i).string.length() - 1; + } + } + } + return run.length(); +} + +static float cursorToX(const Vector<TextRunComponent, 1024>& components, int width, bool rtl, int cursor) +{ + int start = 0; + for (int i = 0; i < components.size(); ++i) { + if (start + components.at(i).string.length() - 1 < cursor) { + start += components.at(i).string.length() - 1; + continue; + } + int xs = components.at(i).offset; + if (rtl) + xs = width - xs - components.at(i).width; + QTextLayout layout(components.at(i).string, *components.at(i).font); + layout.beginLayout(); + QTextLine l = layout.createLine(); + if (!l.isValid()) + return 0; + + l.setLineWidth(INT_MAX/256); + layout.endLayout(); + + return xs + l.cursorToX(cursor - start + 1); + } + return width; +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& pt, + int h, int from, int to) const +{ + Vector<TextRunComponent, 1024> components; + int w = generateComponents(&components, *this, run); + + if (from == 0 && to == run.length()) + return FloatRect(pt.x(), pt.y(), w, h); + + float x1 = cursorToX(components, w, run.rtl(), from); + float x2 = cursorToX(components, w, run.rtl(), to); + if (x2 < x1) + qSwap(x1, x2); + + return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h); +} + +int Font::lineGap() const +{ + return QFontMetrics(m_font).leading(); +} + +} + +#endif diff --git a/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp b/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp index d32cc63..2121206 100644 --- a/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp +++ b/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -24,7 +25,11 @@ namespace WebCore { -void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData) +void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData*) +{ +} + +void GlyphPageTreeNode::pruneTreeFontData(const WebCore::SimpleFontData*) { } diff --git a/WebCore/platform/graphics/qt/GradientQt.cpp b/WebCore/platform/graphics/qt/GradientQt.cpp index f414efa..a0edf8d 100644 --- a/WebCore/platform/graphics/qt/GradientQt.cpp +++ b/WebCore/platform/graphics/qt/GradientQt.cpp @@ -52,7 +52,7 @@ QGradient* Gradient::platformGradient() QColor stopColor; Vector<ColorStop>::iterator stopIterator = m_stops.begin(); - qreal lastStop; + qreal lastStop(0.0); const qreal lastStopDiff = 0.0000001; while (stopIterator != m_stops.end()) { stopColor.setRgbF(stopIterator->red, stopIterator->green, stopIterator->blue, stopIterator->alpha); diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 600d77c..2e7cdcb 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -39,7 +39,7 @@ #include <windows.h> #endif -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "Color.h" #include "FloatConversion.h" #include "Font.h" @@ -280,7 +280,7 @@ PlatformGraphicsContext* GraphicsContext::platformContext() const return m_data->p(); } -AffineTransform GraphicsContext::getCTM() const +TransformationMatrix GraphicsContext::getCTM() const { return platformContext()->combinedMatrix(); } @@ -293,6 +293,11 @@ void GraphicsContext::savePlatformState() void GraphicsContext::restorePlatformState() { m_data->p()->restore(); + + if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) { + QMatrix matrix = m_common->state.pathTransform; + m_data->currentPath = m_data->currentPath * matrix; + } } /* FIXME: DISABLED WHILE MERGING BACK FROM UNITY @@ -520,6 +525,15 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points p->restore(); } +QPen GraphicsContext::pen() +{ + if (paintingDisabled()) + return QPen(); + + QPainter *p = m_data->p(); + return p->pen(); +} + void GraphicsContext::fillPath() { if (paintingDisabled()) @@ -533,15 +547,18 @@ void GraphicsContext::fillPath() if (fillColor().alpha()) p->fillPath(path, p->brush()); break; - case PatternColorSpace: - p->fillPath(path, QBrush(m_common->state.fillPattern.get()->createPlatformPattern(getCTM()))); + case PatternColorSpace: { + TransformationMatrix affine; + p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine))); break; + } case GradientColorSpace: - QGradient* gradient = m_common->state.fillGradient.get()->platformGradient(); + QGradient* gradient = m_common->state.fillGradient->platformGradient(); *gradient = applySpreadMethod(*gradient, spreadMethod()); p->fillPath(path, QBrush(*gradient)); break; } + m_data->currentPath = QPainterPath(); } void GraphicsContext::strokePath() @@ -559,13 +576,14 @@ void GraphicsContext::strokePath() p->strokePath(path, pen); break; case PatternColorSpace: { - pen.setBrush(QBrush(m_common->state.strokePattern.get()->createPlatformPattern(getCTM()))); + TransformationMatrix affine; + pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine))); p->setPen(pen); p->strokePath(path, pen); break; } case GradientColorSpace: { - QGradient* gradient = m_common->state.strokeGradient.get()->platformGradient(); + QGradient* gradient = m_common->state.strokeGradient->platformGradient(); *gradient = applySpreadMethod(*gradient, spreadMethod()); pen.setBrush(QBrush(*gradient)); p->setPen(pen); @@ -573,6 +591,7 @@ void GraphicsContext::strokePath() break; } } + m_data->currentPath = QPainterPath(); } void GraphicsContext::fillRect(const FloatRect& rect) @@ -587,13 +606,16 @@ void GraphicsContext::fillRect(const FloatRect& rect) if (fillColor().alpha()) p->fillRect(rect, p->brush()); break; - case PatternColorSpace: - p->fillRect(rect, QBrush(m_common->state.fillPattern.get()->createPlatformPattern(getCTM()))); + case PatternColorSpace: { + TransformationMatrix affine; + p->fillRect(rect, QBrush(m_common->state.fillPattern->createPlatformPattern(affine))); break; + } case GradientColorSpace: p->fillRect(rect, QBrush(*(m_common->state.fillGradient.get()->platformGradient()))); break; } + m_data->currentPath = QPainterPath(); } void GraphicsContext::fillRect(const FloatRect& rect, const Color& c) @@ -621,7 +643,9 @@ void GraphicsContext::beginPath() void GraphicsContext::addPath(const Path& path) { - m_data->currentPath = *(path.platformPath()); + QPainterPath newPath = m_data->currentPath; + newPath.addPath(*(path.platformPath())); + m_data->currentPath = newPath; } bool GraphicsContext::inTransparencyLayer() const @@ -645,6 +669,17 @@ void GraphicsContext::clip(const FloatRect& rect) else p->setClipRect(rect, Qt::IntersectClip); } +void GraphicsContext::clipPath(WindRule clipRule) +{ + if (paintingDisabled()) + return; + + QPainter *p = m_data->p(); + QPainterPath newPath = m_data->currentPath; + newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill); + p->setClipPath(newPath); +} + /** * Focus ring handling is not handled here. Qt style in * RenderTheme handles drawing focus on widgets which @@ -823,8 +858,9 @@ void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) if (dashLength % 2) count *= 2; + float penWidth = narrowPrecisionToFloat(double(pen.widthF())); for (unsigned i = 0; i < count; i++) - pattern.append(dashes[i % dashLength] / narrowPrecisionToFloat(pen.widthF())); + pattern.append(dashes[i % dashLength] / penWidth); pen.setDashPattern(pattern); pen.setDashOffset(dashOffset); @@ -901,6 +937,12 @@ void GraphicsContext::translate(float x, float y) return; m_data->p()->translate(x, y); + + if (!m_data->currentPath.isEmpty()) { + QMatrix matrix; + m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y); + m_common->state.pathTransform.translate(x, y); + } } IntPoint GraphicsContext::origin() @@ -917,6 +959,12 @@ void GraphicsContext::rotate(float radians) return; m_data->p()->rotate(180/M_PI*radians); + + if (!m_data->currentPath.isEmpty()) { + QMatrix matrix; + m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians); + m_common->state.pathTransform.rotate(radians); + } } void GraphicsContext::scale(const FloatSize& s) @@ -925,6 +973,12 @@ void GraphicsContext::scale(const FloatSize& s) return; m_data->p()->scale(s.width(), s.height()); + + if (!m_data->currentPath.isEmpty()) { + QMatrix matrix; + m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height()); + m_common->state.pathTransform.scale(s.width(), s.height()); + } } void GraphicsContext::clipOut(const IntRect& rect) @@ -982,12 +1036,20 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, m_data->p()->setClipPath(path, Qt::IntersectClip); } -void GraphicsContext::concatCTM(const AffineTransform& transform) +void GraphicsContext::concatCTM(const TransformationMatrix& transform) { if (paintingDisabled()) return; m_data->p()->setMatrix(transform, true); + + // Transformations to the context shouldn't transform the currentPath. + // We have to undo every change made to the context from the currentPath to avoid wrong drawings. + if (!m_data->currentPath.isEmpty() && transform.isInvertible()) { + QMatrix matrix = transform.inverse(); + m_data->currentPath = m_data->currentPath * matrix; + m_common->state.pathTransform.multiply(transform); + } } void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) @@ -995,13 +1057,6 @@ void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) notImplemented(); } -void GraphicsContext::setPlatformFont(const Font& aFont) -{ - if (paintingDisabled()) - return; - m_data->p()->setFont(aFont.font()); -} - void GraphicsContext::setPlatformStrokeColor(const Color& color) { if (paintingDisabled()) @@ -1039,7 +1094,7 @@ void GraphicsContext::setPlatformFillColor(const Color& color) m_data->p()->setBrush(QBrush(color)); } -void GraphicsContext::setUseAntialiasing(bool enable) +void GraphicsContext::setPlatformShouldAntialias(bool enable) { if (paintingDisabled()) return; @@ -1051,8 +1106,8 @@ void GraphicsContext::setUseAntialiasing(bool enable) HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { - // painting through native HDC is only supported for plugin, where mayCreateBitmap is always TRUE - Q_ASSERT(mayCreateBitmap == TRUE); + // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true + Q_ASSERT(mayCreateBitmap); if (dstRect.isEmpty()) return 0; @@ -1090,6 +1145,7 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha memset(bmpInfo.bmBits, 0, bufferSize); } +#if !PLATFORM(WIN_CE) // Make sure we can do world transforms. SetGraphicsMode(bitmapDC, GM_ADVANCED); @@ -1102,15 +1158,15 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha xform.eDx = -dstRect.x(); xform.eDy = -dstRect.y(); ::SetWorldTransform(bitmapDC, &xform); - +#endif return bitmapDC; } void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { - // painting through native HDC is only supported for plugin, where mayCreateBitmap is always TRUE - Q_ASSERT(mayCreateBitmap == TRUE); + // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true + Q_ASSERT(mayCreateBitmap); if (hdc) { diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp index e3b00a1..394c7a7 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -178,9 +178,26 @@ ImageDecoderQt::ReadContext::IncrementalReadResult return IncrementalReadComplete; } +ImageDecoderQt* ImageDecoderQt::create(const SharedBuffer& data) +{ + // We need at least 4 bytes to figure out what kind of image we're dealing with. + if (data.size() < 4) + return 0; + + QByteArray bytes = QByteArray::fromRawData(data.data(), data.size()); + QBuffer buffer(&bytes); + if (!buffer.open(QBuffer::ReadOnly)) + return 0; + + QString imageFormat = QString::fromLatin1(QImageReader::imageFormat(&buffer).toLower()); + if (imageFormat.isEmpty()) + return 0; // Image format not supported + + return new ImageDecoderQt(imageFormat); +} -// ImageDecoderQt -ImageDecoderQt::ImageDecoderQt( ) +ImageDecoderQt::ImageDecoderQt(const QString &imageFormat) + : m_imageFormat(imageFormat) { } @@ -254,7 +271,6 @@ int ImageDecoderQt::frameCount() const return m_imageList.size(); } - int ImageDecoderQt::repetitionCount() const { if (debugImageDecoderQt) @@ -262,7 +278,6 @@ int ImageDecoderQt::repetitionCount() const return m_loopCount; } - bool ImageDecoderQt::supportsAlpha() const { return hasFirstImageHeader() && m_imageList[0].m_image.hasAlphaChannel(); @@ -275,6 +290,13 @@ int ImageDecoderQt::duration(size_t index) const return m_imageList[index].m_duration; } +String ImageDecoderQt::filenameExtension() const +{ + if (debugImageDecoderQt) + qDebug() << " ImageDecoderQt::filenameExtension() returns" << m_imageFormat; + return m_imageFormat; +}; + RGBA32Buffer* ImageDecoderQt::frameBufferAtIndex(size_t index) { Q_ASSERT("use imageAtIndex instead"); diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.h b/WebCore/platform/graphics/qt/ImageDecoderQt.h index 3573dd0..a2eb6aa 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.h +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.h @@ -38,34 +38,30 @@ namespace WebCore { class ImageDecoderQt : public ImageDecoder { - ImageDecoderQt(const ImageDecoderQt&); - ImageDecoderQt &operator=(const ImageDecoderQt&); public: - ImageDecoderQt(); + static ImageDecoderQt* create(const SharedBuffer& data); ~ImageDecoderQt(); typedef Vector<char> IncomingData; virtual void setData(const IncomingData& data, bool allDataReceived); - virtual bool isSizeAvailable() const; - virtual int frameCount() const; - - virtual int repetitionCount() const; - - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); QPixmap* imageAtIndex(size_t index) const; - virtual bool supportsAlpha() const; - int duration(size_t index) const; + virtual String filenameExtension() const; void clearFrame(size_t index); + private: + ImageDecoderQt(const QString &imageFormat); + ImageDecoderQt(const ImageDecoderQt&); + ImageDecoderQt &operator=(const ImageDecoderQt&); + class ReadContext; void reset(); bool hasFirstImageHeader() const; @@ -89,6 +85,7 @@ private: ImageList m_imageList; mutable QHash<int, QPixmap> m_pixmapCache; int m_loopCount; + QString m_imageFormat; }; diff --git a/WebCore/platform/graphics/qt/ImageQt.cpp b/WebCore/platform/graphics/qt/ImageQt.cpp index 9234c69..99062f9 100644 --- a/WebCore/platform/graphics/qt/ImageQt.cpp +++ b/WebCore/platform/graphics/qt/ImageQt.cpp @@ -34,7 +34,7 @@ #include "FloatRect.h" #include "PlatformString.h" #include "GraphicsContext.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "NotImplemented.h" #include "StillImageQt.h" #include "qwebsettings.h" @@ -69,14 +69,16 @@ static QPixmap loadResourcePixmap(const char *name) namespace WebCore { -void FrameData::clear() +bool FrameData::clear(bool clearMetadata) { + if (clearMetadata) + m_haveMetadata = false; + if (m_frame) { m_frame = 0; - // 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. + return true; } + return false; } @@ -91,7 +93,7 @@ PassRefPtr<Image> Image::loadPlatformResource(const char* name) } -void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, +void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { notImplemented(); @@ -136,7 +138,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, ctxt->restore(); } -void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, +void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { QPixmap* framePixmap = nativeImageForCurrentFrame(); diff --git a/WebCore/platform/graphics/qt/ImageSourceQt.cpp b/WebCore/platform/graphics/qt/ImageSourceQt.cpp index 1d14f9d..d62acc3 100644 --- a/WebCore/platform/graphics/qt/ImageSourceQt.cpp +++ b/WebCore/platform/graphics/qt/ImageSourceQt.cpp @@ -29,6 +29,7 @@ #include "config.h" #include "ImageSource.h" #include "ImageDecoderQt.h" +#include "NotImplemented.h" #include "SharedBuffer.h" #include <QBuffer> @@ -36,25 +37,6 @@ #include <QImageReader> namespace WebCore { -static bool canHandleImage(const SharedBuffer& _data) -{ - // We need at least 4 bytes to figure out what kind of image we're dealing with. - if (_data.size() < 4) - return false; - - QByteArray data = QByteArray::fromRawData(_data.data(), _data.size()); - QBuffer buffer(&data); - if (!buffer.open(QBuffer::ReadOnly)) - return false; - - return !QImageReader::imageFormat(&buffer).isEmpty(); -} - -ImageDecoderQt* createDecoder(const SharedBuffer& data) { - if (!canHandleImage(data)) - return 0; - return new ImageDecoderQt(); -} ImageSource::ImageSource() : m_decoder(0) @@ -63,7 +45,7 @@ ImageSource::ImageSource() ImageSource::~ImageSource() { - delete m_decoder; + clear(true); } bool ImageSource::initialized() const @@ -78,7 +60,7 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) // If insufficient bytes are available to determine the image type, no decoder plugin will be // made. if (!m_decoder) - m_decoder = createDecoder(*data); + m_decoder = ImageDecoderQt::create(*data); if (!m_decoder) return; @@ -86,6 +68,14 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) m_decoder->setData(data->buffer(), allDataReceived); } +String ImageSource::filenameExtension() const +{ + if (!m_decoder) + return String(); + + return m_decoder->filenameExtension(); +} + bool ImageSource::isSizeAvailable() { if (!m_decoder) @@ -162,13 +152,20 @@ bool ImageSource::frameIsCompleteAtIndex(size_t index) return (m_decoder && m_decoder->imageAtIndex(index) != 0); } -void ImageSource::clear() +void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) { - delete m_decoder; + if (!destroyAll) { + if (m_decoder) + m_decoder->clearFrameBufferCache(clearBeforeFrame); + return; + } + + delete m_decoder; m_decoder = 0; + if (data) + setData(data, allDataReceived); } - } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp index 431e68e..b1a48fb 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -84,6 +84,9 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) { // Hint to Phonon to disable overlay painting m_videoWidget->setAttribute(Qt::WA_DontShowOnScreen); +#if QT_VERSION < 0x040500 + m_videoWidget->setAttribute(Qt::WA_QuitOnClose, false); +#endif createPath(m_mediaObject, m_videoWidget); createPath(m_mediaObject, m_audioOutput); @@ -96,7 +99,6 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged(Phonon::State, Phonon::State))); - connect(m_mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64))); connect(m_mediaObject, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged())); connect(m_mediaObject, SIGNAL(seekableChanged(bool)), this, SLOT(seekableChanged(bool))); connect(m_mediaObject, SIGNAL(hasVideoChanged(bool)), this, SLOT(hasVideoChanged(bool))); @@ -105,7 +107,6 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) connect(m_mediaObject, SIGNAL(currentSourceChanged(const Phonon::MediaSource&)), this, SLOT(currentSourceChanged(const Phonon::MediaSource&))); connect(m_mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish())); - connect(m_mediaObject, SIGNAL(prefinishMarkReached(qint32)), this, SLOT(prefinishMarkReached(qint32))); connect(m_mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); } @@ -314,7 +315,7 @@ void MediaPlayerPrivate::updateStates() m_mediaObject->pause(); } } else if (phononState == Phonon::PausedState) { - m_networkState = MediaPlayer::LoadedFirstFrame; + m_networkState = MediaPlayer::Loaded; m_readyState = MediaPlayer::CanPlayThrough; } else if (phononState == Phonon::ErrorState) { if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) { @@ -371,42 +372,6 @@ void MediaPlayerPrivate::setRect(const IntRect& newRect) m_videoWidget->resize(newRect.width(), newRect.height()); } - -void MediaPlayerPrivate::loadStateChanged() -{ - notImplemented(); -} - -void MediaPlayerPrivate::rateChanged() -{ - notImplemented(); -} - -void MediaPlayerPrivate::sizeChanged() -{ - notImplemented(); -} - -void MediaPlayerPrivate::timeChanged() -{ - notImplemented(); -} - -void MediaPlayerPrivate::volumeChanged() -{ - notImplemented(); -} - -void MediaPlayerPrivate::didEnd() -{ - notImplemented(); -} - -void MediaPlayerPrivate::loadingFailed() -{ - notImplemented(); -} - IntSize MediaPlayerPrivate::naturalSize() const { if (!hasVideo()) { @@ -430,17 +395,12 @@ IntSize MediaPlayerPrivate::naturalSize() const bool MediaPlayerPrivate::eventFilter(QObject* obj, QEvent* event) { - if (event->type() == QEvent::Paint) + if (event->type() == QEvent::UpdateRequest) m_player->repaint(); return QObject::eventFilter(obj, event); } -void MediaPlayerPrivate::repaint() -{ - m_player->repaint(); -} - void MediaPlayerPrivate::paint(GraphicsContext* graphicsContect, const IntRect& rect) { if (graphicsContect->paintingDisabled()) @@ -469,12 +429,6 @@ void MediaPlayerPrivate::stateChanged(Phonon::State newState, Phonon::State oldS updateStates(); } -void MediaPlayerPrivate::tick(qint64) -{ - updateStates(); - m_player->timeChanged(); -} - void MediaPlayerPrivate::metaDataChanged() { LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()"); @@ -516,12 +470,6 @@ void MediaPlayerPrivate::aboutToFinish() LOG_MEDIAOBJECT(); } -void MediaPlayerPrivate::prefinishMarkReached(qint32) -{ - notImplemented(); - LOG_MEDIAOBJECT(); -} - void MediaPlayerPrivate::totalTimeChanged(qint64 totalTime) { LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%d)", totalTime); diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h index 5eb2a09..1b20a84 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h @@ -111,15 +111,6 @@ namespace WebCore { void setVisible(bool); void setRect(const IntRect&); - void loadStateChanged(); - void rateChanged(); - void sizeChanged(); - void timeChanged(); - void volumeChanged(); - void didEnd(); - void loadingFailed(); - - void repaint(); void paint(GraphicsContext*, const IntRect&); static void getSupportedTypes(HashSet<String>&); static bool isAvailable() { return true; } @@ -129,7 +120,6 @@ namespace WebCore { private slots: void stateChanged(Phonon::State, Phonon::State); - void tick(qint64); void metaDataChanged(); void seekableChanged(bool); void hasVideoChanged(bool); @@ -137,7 +127,6 @@ namespace WebCore { void finished(); void currentSourceChanged(const Phonon::MediaSource&); void aboutToFinish(); - void prefinishMarkReached(qint32); void totalTimeChanged(qint64); private: diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index 76f375c..bd0192c 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -29,9 +29,12 @@ #include "config.h" #include "Path.h" +#include "TransformationMatrix.h" #include "FloatRect.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" #include "PlatformString.h" -#include "AffineTransform.h" +#include "StrokeStyleApplier.h" #include <QPainterPath> #include <QMatrix> #include <QString> @@ -39,6 +42,10 @@ #define _USE_MATH_DEFINES #include <math.h> +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + namespace WebCore { Path::Path() @@ -77,6 +84,28 @@ bool Path::contains(const FloatPoint& point, WindRule rule) const return contains; } +bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const +{ + ASSERT(applier); + + // FIXME: We should try to use a 'shared Context' instead of creating a new ImageBuffer + // on each call. + std::auto_ptr<ImageBuffer> scratchImage = ImageBuffer::create(IntSize(1, 1), false); + GraphicsContext* gc = scratchImage->context(); + QPainterPathStroker stroke; + applier->strokeStyle(gc); + + QPen pen = gc->pen(); + stroke.setWidth(pen.widthF()); + stroke.setCapStyle(pen.capStyle()); + stroke.setJoinStyle(pen.joinStyle()); + stroke.setMiterLimit(pen.miterLimit()); + stroke.setDashPattern(pen.dashPattern()); + stroke.setDashOffset(pen.dashOffset()); + + return (stroke.createStroke(*platformPath())).contains(point); +} + void Path::translate(const FloatSize& size) { QMatrix matrix; @@ -89,6 +118,27 @@ FloatRect Path::boundingRect() const return m_path->boundingRect(); } +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + // FIXME: We should try to use a 'shared Context' instead of creating a new ImageBuffer + // on each call. + std::auto_ptr<ImageBuffer> scratchImage = ImageBuffer::create(IntSize(1, 1), false); + GraphicsContext* gc = scratchImage->context(); + QPainterPathStroker stroke; + if (applier) { + applier->strokeStyle(gc); + + QPen pen = gc->pen(); + stroke.setWidth(pen.widthF()); + stroke.setCapStyle(pen.capStyle()); + stroke.setJoinStyle(pen.joinStyle()); + stroke.setMiterLimit(pen.miterLimit()); + stroke.setDashPattern(pen.dashPattern()); + stroke.setDashOffset(pen.dashOffset()); + } + return (stroke.createStroke(*platformPath())).boundingRect(); +} + void Path::moveTo(const FloatPoint& point) { m_path->moveTo(point); @@ -263,7 +313,7 @@ void Path::apply(void* info, PathApplierFunction function) const } } -void Path::transform(const AffineTransform& transform) +void Path::transform(const TransformationMatrix& transform) { if (m_path) { QMatrix mat = transform; diff --git a/WebCore/platform/graphics/qt/PatternQt.cpp b/WebCore/platform/graphics/qt/PatternQt.cpp index 883a258..5b76841 100644 --- a/WebCore/platform/graphics/qt/PatternQt.cpp +++ b/WebCore/platform/graphics/qt/PatternQt.cpp @@ -26,12 +26,12 @@ #include "config.h" #include "Pattern.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "GraphicsContext.h" namespace WebCore { -QBrush Pattern::createPlatformPattern(const AffineTransform& transform) const +QBrush Pattern::createPlatformPattern(const TransformationMatrix& transform) const { QPixmap* pixmap = tileImage()->nativeImageForCurrentFrame(); if (!pixmap) diff --git a/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp b/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp index 1ffce33..6cf4e55 100644 --- a/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp +++ b/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -19,37 +20,47 @@ This class provides all functionality needed for loading images, style sheets and html pages from the web. It has a memory cache for these objects. */ + #include "config.h" #include "SimpleFontData.h" -#include "SVGFontData.h" +#include <QFontMetrics> namespace WebCore { -SimpleFontData::SimpleFontData(const FontPlatformData& font, bool customFont, bool loading, SVGFontData*) - : m_font(font) - , m_isCustomFont(customFont) - , m_isLoading(loading) +void SimpleFontData::determinePitch() { + m_treatAsFixedPitch = m_font.font().fixedPitch(); } -SimpleFontData::~SimpleFontData() +bool SimpleFontData::containsCharacters(const UChar*, int length) const { + return true; } -bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +void SimpleFontData::platformInit() { - return true; + QFontMetrics fm(m_font.font()); + + m_ascent = fm.ascent(); + m_descent = fm.descent(); + m_lineSpacing = fm.lineSpacing(); + m_xHeight = fm.xHeight(); + m_spaceWidth = fm.width(QLatin1Char(' ')); + m_lineGap = fm.leading(); } -const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const +void SimpleFontData::platformGlyphInit() { - return this; + m_spaceGlyph = 0; + m_adjustedSpaceWidth = m_spaceWidth; + determinePitch(); + m_missingGlyphData.fontData = this; + m_missingGlyphData.glyph = 0; } -bool SimpleFontData::isSegmented() const +void SimpleFontData::platformDestroy() { - return false; } } diff --git a/WebCore/platform/graphics/qt/StillImageQt.h b/WebCore/platform/graphics/qt/StillImageQt.h index 37b8b2c..2b2c1f7 100644 --- a/WebCore/platform/graphics/qt/StillImageQt.h +++ b/WebCore/platform/graphics/qt/StillImageQt.h @@ -41,7 +41,7 @@ namespace WebCore { // FIXME: StillImages 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 void destroyDecodedData(bool destroyAll = true) { } virtual unsigned decodedSize() const { return 0; } virtual IntSize size() const; diff --git a/WebCore/platform/graphics/qt/AffineTransformQt.cpp b/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp index 2793043..47abd17 100644 --- a/WebCore/platform/graphics/qt/AffineTransformQt.cpp +++ b/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp @@ -24,34 +24,34 @@ */ #include "config.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "IntRect.h" #include "FloatRect.h" namespace WebCore { -AffineTransform::AffineTransform() +TransformationMatrix::TransformationMatrix() : m_transform() { } -AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) +TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) : m_transform(a, b, c, d, tx, ty) { } -AffineTransform::AffineTransform(const PlatformAffineTransform& matrix) +TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) : m_transform(matrix) { } -void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) +void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) { m_transform.setMatrix(a, b, c, d, tx, ty); } -void AffineTransform::map(double x, double y, double* x2, double* y2) const +void TransformationMatrix::map(double x, double y, double* x2, double* y2) const { qreal tx2, ty2; m_transform.map(qreal(x), qreal(y), &tx2, &ty2); @@ -59,140 +59,140 @@ void AffineTransform::map(double x, double y, double* x2, double* y2) const *y2 = ty2; } -IntRect AffineTransform::mapRect(const IntRect& rect) const +IntRect TransformationMatrix::mapRect(const IntRect& rect) const { return m_transform.mapRect(rect); } -FloatRect AffineTransform::mapRect(const FloatRect& rect) const +FloatRect TransformationMatrix::mapRect(const FloatRect& rect) const { return m_transform.mapRect(rect); } -bool AffineTransform::isIdentity() const +bool TransformationMatrix::isIdentity() const { return m_transform.isIdentity(); } -double AffineTransform::a() const +double TransformationMatrix::a() const { return m_transform.m11(); } -void AffineTransform::setA(double a) +void TransformationMatrix::setA(double a) { m_transform.setMatrix(a, b(), c(), d(), e(), f()); } -double AffineTransform::b() const +double TransformationMatrix::b() const { return m_transform.m12(); } -void AffineTransform::setB(double b) +void TransformationMatrix::setB(double b) { m_transform.setMatrix(a(), b, c(), d(), e(), f()); } -double AffineTransform::c() const +double TransformationMatrix::c() const { return m_transform.m21(); } -void AffineTransform::setC(double c) +void TransformationMatrix::setC(double c) { m_transform.setMatrix(a(), b(), c, d(), e(), f()); } -double AffineTransform::d() const +double TransformationMatrix::d() const { return m_transform.m22(); } -void AffineTransform::setD(double d) +void TransformationMatrix::setD(double d) { m_transform.setMatrix(a(), b(), c(), d, e(), f()); } -double AffineTransform::e() const +double TransformationMatrix::e() const { return m_transform.dx(); } -void AffineTransform::setE(double e) +void TransformationMatrix::setE(double e) { m_transform.setMatrix(a(), b(), c(), d(), e, f()); } -double AffineTransform::f() const +double TransformationMatrix::f() const { return m_transform.dy(); } -void AffineTransform::setF(double f) +void TransformationMatrix::setF(double f) { m_transform.setMatrix(a(), b(), c(), d(), e(), f); } -void AffineTransform::reset() +void TransformationMatrix::reset() { m_transform.reset(); } -AffineTransform& AffineTransform::scale(double sx, double sy) +TransformationMatrix& TransformationMatrix::scale(double sx, double sy) { m_transform.scale(sx, sy); return *this; } -AffineTransform& AffineTransform::rotate(double d) +TransformationMatrix& TransformationMatrix::rotate(double d) { m_transform.rotate(d); return *this; } -AffineTransform& AffineTransform::translate(double tx, double ty) +TransformationMatrix& TransformationMatrix::translate(double tx, double ty) { m_transform.translate(tx, ty); return *this; } -AffineTransform& AffineTransform::shear(double sx, double sy) +TransformationMatrix& TransformationMatrix::shear(double sx, double sy) { m_transform.shear(sx, sy); return *this; } -double AffineTransform::det() const +double TransformationMatrix::det() const { return m_transform.det(); } -AffineTransform AffineTransform::inverse() const +TransformationMatrix TransformationMatrix::inverse() const { if(!isInvertible()) - return AffineTransform(); + return TransformationMatrix(); return m_transform.inverted(); } -AffineTransform::operator QMatrix() const +TransformationMatrix::operator QMatrix() const { return m_transform; } -bool AffineTransform::operator==(const AffineTransform& other) const +bool TransformationMatrix::operator==(const TransformationMatrix& other) const { return m_transform == other.m_transform; } -AffineTransform& AffineTransform::operator*=(const AffineTransform& other) +TransformationMatrix& TransformationMatrix::operator*=(const TransformationMatrix& other) { m_transform *= other.m_transform; return *this; } -AffineTransform AffineTransform::operator*(const AffineTransform& other) +TransformationMatrix TransformationMatrix::operator*(const TransformationMatrix& other) { return m_transform * other.m_transform; } diff --git a/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h b/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h new file mode 100644 index 0000000..5d85652 --- /dev/null +++ b/WebCore/platform/graphics/skia/BitmapImageSingleFrameSkia.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2006,2007,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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 BitmapImageSingleFrameSkia_h +#define BitmapImageSingleFrameSkia_h + +#include "Image.h" +#include "NativeImageSkia.h" + +namespace WebCore { + +// This image class can be used in places which need an Image, but have +// raw pixel data rather than undecoded image data. +// The Image is simpler than a BitmapImage, as it does not have image +// observers, animation, multiple frames, or non-decoded data. +// Therefore trimming the decoded data (destroyDecodedData()) has no effect. +// +// The difficulty with putting this in BitmapImage::create(NativeImagePtr) +// is that NativeImagePtr = NativeImageSkia, yet callers have SkBitmap. +class BitmapImageSingleFrameSkia : public Image { +public: + // Creates a new Image, by copying the pixel values out of |bitmap|. + // If creation failed, returns null. + static PassRefPtr<BitmapImageSingleFrameSkia> create(const SkBitmap&); + + virtual bool isBitmapImage() const { return true; } + + virtual IntSize size() const + { + return IntSize(m_nativeImage.width(), m_nativeImage.height()); + } + + // Do nothing, as we only have the one representation of data (decoded). + virtual void destroyDecodedData(bool destroyAll = true) { } + + virtual unsigned decodedSize() const + { + return m_nativeImage.decodedSize(); + } + + // We only have a single frame. + virtual NativeImagePtr nativeImageForCurrentFrame() + { + return &m_nativeImage; + } + +protected: + virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); + +private: + NativeImageSkia m_nativeImage; + + // Use create(). + BitmapImageSingleFrameSkia() { } +}; + +} // namespace WebCore + +#endif // BitmapImageSingleFrameSkia_h diff --git a/WebCore/platform/graphics/skia/FloatPointSkia.cpp b/WebCore/platform/graphics/skia/FloatPointSkia.cpp new file mode 100644 index 0000000..054a772 --- /dev/null +++ b/WebCore/platform/graphics/skia/FloatPointSkia.cpp @@ -0,0 +1,51 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "FloatPoint.h" + +#include "SkPoint.h" +#include "SkiaUtils.h" + +namespace WebCore { + +FloatPoint::FloatPoint(const SkPoint& p) + : m_x(p.fX) + , m_y(p.fY) +{ +} + +FloatPoint::operator SkPoint() const +{ + SkPoint p = { WebCoreFloatToSkScalar(m_x), WebCoreFloatToSkScalar(m_y) }; + return p; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/FloatRectSkia.cpp b/WebCore/platform/graphics/skia/FloatRectSkia.cpp new file mode 100644 index 0000000..a10371f --- /dev/null +++ b/WebCore/platform/graphics/skia/FloatRectSkia.cpp @@ -0,0 +1,50 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "FloatRect.h" + +#include "SkRect.h" + +namespace WebCore { + +FloatRect::FloatRect(const SkRect& r) + : m_location(r.fLeft, r.fTop) + , m_size(r.width(), r.height()) +{ +} + +FloatRect::operator SkRect() const +{ + SkRect rect = { x(), y(), right(), bottom() }; + return rect; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/GradientSkia.cpp b/WebCore/platform/graphics/skia/GradientSkia.cpp new file mode 100644 index 0000000..eff7c66 --- /dev/null +++ b/WebCore/platform/graphics/skia/GradientSkia.cpp @@ -0,0 +1,164 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "SkGradientShader.h" +#include "SkiaUtils.h" + +namespace WebCore { + +void Gradient::platformDestroy() +{ + if (m_gradient) + m_gradient->safeUnref(); + m_gradient = 0; +} + +static inline U8CPU F2B(float x) +{ + return static_cast<int>(x * 255); +} + +static SkColor makeSkColor(float a, float r, float g, float b) +{ + return SkColorSetARGB(F2B(a), F2B(r), F2B(g), F2B(b)); +} + +// Determine the total number of stops needed, including pseudo-stops at the +// ends as necessary. +static size_t totalStopsNeeded(const Gradient::ColorStop* stopData, size_t count) +{ + const Gradient::ColorStop* stop = stopData; + size_t countUsed = count; + if (count < 1 || stop->stop > 0.0) + countUsed++; + stop += count - 1; + if (count < 2 || stop->stop < 1.0) + countUsed++; + return countUsed; +} + +// Collect sorted stop position and color information into the pos and colors +// buffers, ensuring stops at both 0.0 and 1.0. The buffers must be large +// enough to hold information for all stops, including the new endpoints if +// stops at 0.0 and 1.0 aren't already included. +static void fillStops(const Gradient::ColorStop* stopData, + size_t count, SkScalar* pos, SkColor* colors) +{ + const Gradient::ColorStop* stop = stopData; + size_t start = 0; + if (count < 1) { + // A gradient with no stops must be transparent black. + pos[0] = WebCoreFloatToSkScalar(0.0); + colors[0] = makeSkColor(0.0, 0.0, 0.0, 0.0); + start = 1; + } else if (stop->stop > 0.0) { + // Copy the first stop to 0.0. The first stop position may have a slight + // rounding error, but we don't care in this float comparison, since + // 0.0 comes through cleanly and people aren't likely to want a gradient + // with a stop at (0 + epsilon). + pos[0] = WebCoreFloatToSkScalar(0.0); + colors[0] = makeSkColor(stop->alpha, stop->red, stop->green, stop->blue); + start = 1; + } + + for (size_t i = start; i < start + count; i++) { + pos[i] = WebCoreFloatToSkScalar(stop->stop); + colors[i] = makeSkColor(stop->alpha, stop->red, stop->green, stop->blue); + ++stop; + } + + // Copy the last stop to 1.0 if needed. See comment above about this float + // comparison. + if (count < 1 || (--stop)->stop < 1.0) { + pos[start + count] = WebCoreFloatToSkScalar(1.0); + colors[start + count] = colors[start + count - 1]; + } +} + +static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b) +{ + return a.stop < b.stop; +} + +SkShader* Gradient::platformGradient() +{ + if (m_gradient) + return m_gradient; + + // FIXME: This and compareStops() are also in Gradient.cpp and + // CSSGradientValue.cpp; probably should refactor in WebKit. + if (!m_stopsSorted) { + if (m_stops.size()) + std::stable_sort(m_stops.begin(), m_stops.end(), compareStops); + m_stopsSorted = true; + } + size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size()); + ASSERT(countUsed >= 2); + ASSERT(countUsed >= m_stops.size()); + + // FIXME: Why is all this manual pointer math needed?! + SkAutoMalloc storage(countUsed * (sizeof(SkColor) + sizeof(SkScalar))); + SkColor* colors = (SkColor*)storage.get(); + SkScalar* pos = (SkScalar*)(colors + countUsed); + + fillStops(m_stops.data(), m_stops.size(), pos, colors); + + if (m_radial) { + // FIXME: CSS radial Gradients allow an offset focal point (the + // "start circle"), but skia doesn't seem to support that, so this just + // ignores m_p0/m_r0 and draws the gradient centered in the "end + // circle" (m_p1/m_r1). + // See http://webkit.org/blog/175/introducing-css-gradients/ for a + // description of the expected behavior. + m_gradient = SkGradientShader::CreateRadial(m_p1, + WebCoreFloatToSkScalar(m_r1), colors, pos, + static_cast<int>(countUsed), SkShader::kClamp_TileMode); + } else { + SkPoint pts[2] = { m_p0, m_p1 }; + m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, + static_cast<int>(countUsed), SkShader::kClamp_TileMode); + } + + return m_gradient; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + context->setFillGradient(this); + context->fillRect(rect); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h b/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h new file mode 100644 index 0000000..29738f4 --- /dev/null +++ b/WebCore/platform/graphics/skia/GraphicsContextPlatformPrivate.h @@ -0,0 +1,55 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 GraphicsContextPlatformPrivate_h +#define GraphicsContextPlatformPrivate_h + +#include <wtf/Noncopyable.h> + +class PlatformContextSkia; + +namespace WebCore { + +// This class just holds onto a PlatformContextSkia for GraphicsContext. +class GraphicsContextPlatformPrivate : Noncopyable { +public: + GraphicsContextPlatformPrivate(PlatformContextSkia* platformContext) + : m_context(platformContext) { } + + PlatformContextSkia* context() { return m_context; } + +private: + // Non-owning pointer to the PlatformContext. + PlatformContextSkia* m_context; +}; + +} // namespace WebCore + +#endif // GraphicsContextPlatformPrivate_h diff --git a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp new file mode 100644 index 0000000..e6c7783 --- /dev/null +++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -0,0 +1,1122 @@ +/* + * Copyright (c) 2006, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GraphicsContext.h" + +#include "GraphicsContextPlatformPrivate.h" +#include "GraphicsContextPrivate.h" +#include "Color.h" +#include "FloatRect.h" +#include "Gradient.h" +#include "IntRect.h" +#include "NativeImageSkia.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "TransformationMatrix.h" + +#include "SkBitmap.h" +#include "SkBlurDrawLooper.h" +#include "SkCornerPathEffect.h" +#include "skia/ext/platform_canvas.h" +#include "SkiaUtils.h" +#include "SkShader.h" + +#include <math.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +namespace { + +// "Seatbelt" functions ------------------------------------------------------ +// +// These functions check certain graphics primitives for being "safe". +// Skia has historically crashed when sent crazy data. These functions do +// additional checking to prevent crashes. +// +// Ideally, all of these would be fixed in the graphics layer and we would not +// have to do any checking. You can uncomment the ENSURE_VALUE_SAFETY_FOR_SKIA +// flag to check the graphics layer. +#define ENSURE_VALUE_SAFETY_FOR_SKIA + +static bool isCoordinateSkiaSafe(float coord) +{ +#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA + // First check for valid floats. +#if defined(_MSC_VER) + if (!_finite(coord)) +#else + if (!finite(coord)) +#endif + return false; + + // Skia uses 16.16 fixed point and 26.6 fixed point in various places. If + // the transformed point exceeds 15 bits, we just declare that it's + // unreasonable to catch both of these cases. + static const int maxPointMagnitude = 32767; + if (coord > maxPointMagnitude || coord < -maxPointMagnitude) + return false; + + return true; +#else + return true; +#endif +} + +static bool isPointSkiaSafe(const SkMatrix& transform, const SkPoint& pt) +{ +#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA + // Now check for points that will overflow. We check the *transformed* + // points since this is what will be rasterized. + SkPoint xPt; + transform.mapPoints(&xPt, &pt, 1); + return isCoordinateSkiaSafe(xPt.fX) && isCoordinateSkiaSafe(xPt.fY); +#else + return true; +#endif +} + +static bool isRectSkiaSafe(const SkMatrix& transform, const SkRect& rc) +{ +#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA + SkPoint topleft = {rc.fLeft, rc.fTop}; + SkPoint bottomright = {rc.fRight, rc.fBottom}; + return isPointSkiaSafe(transform, topleft) && isPointSkiaSafe(transform, bottomright); +#else + return true; +#endif +} + +bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path) +{ +#ifdef ENSURE_VALUE_SAFETY_FOR_SKIA + SkPoint current_points[4]; + SkPath::Iter iter(path, false); + for (SkPath::Verb verb = iter.next(current_points); + verb != SkPath::kDone_Verb; + verb = iter.next(current_points)) { + switch (verb) { + case SkPath::kMove_Verb: + // This move will be duplicated in the next verb, so we can ignore. + break; + case SkPath::kLine_Verb: + // iter.next returns 2 points. + if (!isPointSkiaSafe(transform, current_points[0]) + || !isPointSkiaSafe(transform, current_points[1])) + return false; + break; + case SkPath::kQuad_Verb: + // iter.next returns 3 points. + if (!isPointSkiaSafe(transform, current_points[0]) + || !isPointSkiaSafe(transform, current_points[1]) + || !isPointSkiaSafe(transform, current_points[2])) + return false; + break; + case SkPath::kCubic_Verb: + // iter.next returns 4 points. + if (!isPointSkiaSafe(transform, current_points[0]) + || !isPointSkiaSafe(transform, current_points[1]) + || !isPointSkiaSafe(transform, current_points[2]) + || !isPointSkiaSafe(transform, current_points[3])) + return false; + break; + case SkPath::kClose_Verb: + case SkPath::kDone_Verb: + default: + break; + } + } + return true; +#else + return true; +#endif +} + +// Local helper functions ------------------------------------------------------ + +void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle) +{ + SkIRect ir; + int rx = SkMin32(SkScalarRound(rect.width()), size.width()); + int ry = SkMin32(SkScalarRound(rect.height()), size.height()); + + ir.set(-rx, -ry, rx, ry); + switch (startAngle) { + case 0: + ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom); + break; + case 90: + ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom); + break; + case 180: + ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop); + break; + case 270: + ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop); + break; + default: + ASSERT(0); + } + + SkRect r; + r.set(ir); + path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false); +} + +inline int fastMod(int value, int max) +{ + int sign = SkExtractSign(value); + + value = SkApplySign(value, sign); + if (value >= max) + value %= max; + return SkApplySign(value, sign); +} + +inline float square(float n) +{ + return n * n; +} + +} // namespace + +// ----------------------------------------------------------------------------- + +// This may be called with a NULL pointer to create a graphics context that has +// no painting. +GraphicsContext::GraphicsContext(PlatformGraphicsContext* gc) + : m_common(createGraphicsContextPrivate()) + , m_data(new GraphicsContextPlatformPrivate(gc)) +{ + setPaintingDisabled(!gc || !platformContext()->canvas()); +} + +GraphicsContext::~GraphicsContext() +{ + delete m_data; + this->destroyGraphicsContextPrivate(m_common); +} + +PlatformGraphicsContext* GraphicsContext::platformContext() const +{ + ASSERT(!paintingDisabled()); + return m_data->context(); +} + +// State saving ---------------------------------------------------------------- + +void GraphicsContext::savePlatformState() +{ + if (paintingDisabled()) + return; + + // Save our private State. + platformContext()->save(); +} + +void GraphicsContext::restorePlatformState() +{ + if (paintingDisabled()) + return; + + // Restore our private State. + platformContext()->restore(); +} + +void GraphicsContext::beginTransparencyLayer(float opacity) +{ + if (paintingDisabled()) + return; + + // We need the "alpha" layer flag here because the base layer is opaque + // (the surface of the page) but layers on top may have transparent parts. + // Without explicitly setting the alpha flag, the layer will inherit the + // opaque setting of the base and some things won't work properly. + platformContext()->canvas()->saveLayerAlpha( + 0, + static_cast<unsigned char>(opacity * 255), + static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | + SkCanvas::kFullColorLayer_SaveFlag)); +} + +void GraphicsContext::endTransparencyLayer() +{ + if (paintingDisabled()) + return; + +#if PLATFORM(WIN_OS) + platformContext()->canvas()->getTopPlatformDevice(). + fixupAlphaBeforeCompositing(); +#endif + platformContext()->canvas()->restore(); +} + +// Graphics primitives --------------------------------------------------------- + +void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) +{ + if (paintingDisabled()) + return; + + SkRect r(rect); + if (!isRectSkiaSafe(getCTM(), r)) + return; + + SkPath path; + path.addOval(r, SkPath::kCW_Direction); + // only perform the inset if we won't invert r + if (2 * thickness < rect.width() && 2 * thickness < rect.height()) { + r.inset(SkIntToScalar(thickness) ,SkIntToScalar(thickness)); + path.addOval(r, SkPath::kCCW_Direction); + } + platformContext()->canvas()->clipPath(path); +} + +void GraphicsContext::addPath(const Path& path) +{ + if (paintingDisabled()) + return; + platformContext()->addPath(*path.platformPath()); +} + +void GraphicsContext::beginPath() +{ + if (paintingDisabled()) + return; + platformContext()->beginPath(); +} + +void GraphicsContext::clearPlatformShadow() +{ + if (paintingDisabled()) + return; + platformContext()->setDrawLooper(0); +} + +void GraphicsContext::clearRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + paint.setPorterDuffXfermode(SkPorterDuff::kClear_Mode); + platformContext()->canvas()->drawRect(r, paint); +} + +void GraphicsContext::clip(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r(rect); + if (!isRectSkiaSafe(getCTM(), r)) + return; + + platformContext()->canvas()->clipRect(r); +} + +void GraphicsContext::clip(const Path& path) +{ + if (paintingDisabled()) + return; + + const SkPath& p = *path.platformPath(); + if (!isPathSkiaSafe(getCTM(), p)) + return; + + platformContext()->canvas()->clipPath(p); +} + +void GraphicsContext::clipOut(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r(rect); + if (!isRectSkiaSafe(getCTM(), r)) + return; + + platformContext()->canvas()->clipRect(r, SkRegion::kDifference_Op); +} + +void GraphicsContext::clipOut(const Path& p) +{ + if (paintingDisabled()) + return; + + const SkPath& path = *p.platformPath(); + if (!isPathSkiaSafe(getCTM(), path)) + return; + + platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op); +} + +void GraphicsContext::clipOutEllipseInRect(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect oval(rect); + if (!isRectSkiaSafe(getCTM(), oval)) + return; + + SkPath path; + path.addOval(oval, SkPath::kCCW_Direction); + platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op); +} + +void GraphicsContext::clipPath(WindRule clipRule) +{ + if (paintingDisabled()) + return; + + const SkPath* oldPath = platformContext()->currentPath(); + SkPath path(*oldPath); + path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); + platformContext()->canvas()->clipPath(path); +} + +void GraphicsContext::clipToImageBuffer(const FloatRect& rect, + const ImageBuffer* imageBuffer) +{ + if (paintingDisabled()) + return; + + // FIXME: This is needed for image masking and complex text fills. + notImplemented(); +} + +void GraphicsContext::concatCTM(const TransformationMatrix& xform) +{ + if (paintingDisabled()) + return; + platformContext()->canvas()->concat(xform); +} + +void GraphicsContext::drawConvexPolygon(size_t numPoints, + const FloatPoint* points, + bool shouldAntialias) +{ + if (paintingDisabled()) + return; + + if (numPoints <= 1) + return; + + SkPath path; + + path.incReserve(numPoints); + path.moveTo(WebCoreFloatToSkScalar(points[0].x()), + WebCoreFloatToSkScalar(points[0].y())); + for (size_t i = 1; i < numPoints; i++) { + path.lineTo(WebCoreFloatToSkScalar(points[i].x()), + WebCoreFloatToSkScalar(points[i].y())); + } + + if (!isPathSkiaSafe(getCTM(), path)) + return; + + SkPaint paint; + if (fillColor().alpha() > 0) { + platformContext()->setupPaintForFilling(&paint); + platformContext()->canvas()->drawPath(path, paint); + } + + if (strokeStyle() != NoStroke) { + paint.reset(); + platformContext()->setupPaintForStroking(&paint, 0, 0); + platformContext()->canvas()->drawPath(path, paint); + } +} + +// This method is only used to draw the little circles used in lists. +void GraphicsContext::drawEllipse(const IntRect& elipseRect) +{ + if (paintingDisabled()) + return; + + SkRect rect = elipseRect; + if (!isRectSkiaSafe(getCTM(), rect)) + return; + + SkPaint paint; + if (fillColor().alpha() > 0) { + platformContext()->setupPaintForFilling(&paint); + platformContext()->canvas()->drawOval(rect, paint); + } + + if (strokeStyle() != NoStroke) { + paint.reset(); + platformContext()->setupPaintForStroking(&paint, &rect, 0); + platformContext()->canvas()->drawOval(rect, paint); + } +} + +void GraphicsContext::drawFocusRing(const Color& color) +{ + if (paintingDisabled()) + return; + + const Vector<IntRect>& rects = focusRingRects(); + unsigned rectCount = rects.size(); + if (0 == rectCount) + return; + + SkRegion focusRingRegion; + const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.5); + for (unsigned i = 0; i < rectCount; i++) { + SkIRect r = rects[i]; + r.inset(-focusRingOutset, -focusRingOutset); + focusRingRegion.op(r, SkRegion::kUnion_Op); + } + + SkPath path; + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + + paint.setColor(focusRingColor().rgb()); + paint.setStrokeWidth(focusRingOutset * 2); + paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref(); + focusRingRegion.getBoundaryPath(&path); + platformContext()->canvas()->drawPath(path, paint); +} + +// This is only used to draw borders. +void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) +{ + if (paintingDisabled()) + return; + + StrokeStyle penStyle = strokeStyle(); + if (penStyle == NoStroke) + return; + + SkPaint paint; + SkPoint pts[2] = { (SkPoint)point1, (SkPoint)point2 }; + if (!isPointSkiaSafe(getCTM(), pts[0]) || !isPointSkiaSafe(getCTM(), pts[1])) + return; + + // We know these are vertical or horizontal lines, so the length will just + // be the sum of the displacement component vectors give or take 1 - + // probably worth the speed up of no square root, which also won't be exact. + SkPoint disp = pts[1] - pts[0]; + int length = SkScalarRound(disp.fX + disp.fY); + int width = roundf( + platformContext()->setupPaintForStroking(&paint, 0, length)); + + // "Borrowed" this comment and idea from GraphicsContextCG.cpp + // 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. + bool isVerticalLine = pts[0].fX == pts[1].fX; + + if (width & 1) { // Odd. + if (isVerticalLine) { + pts[0].fX = pts[0].fX + SK_ScalarHalf; + pts[1].fX = pts[0].fX; + } else { // Horizontal line + pts[0].fY = pts[0].fY + SK_ScalarHalf; + pts[1].fY = pts[0].fY; + } + } + platformContext()->canvas()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint); +} + +void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& pt, + int width, + bool grammar) +{ + if (paintingDisabled()) + return; + + // Create the pattern we'll use to draw the underline. + static SkBitmap* misspellBitmap = 0; + if (!misspellBitmap) { + // We use a 2-pixel-high misspelling indicator because that seems to be + // what WebKit is designed for, and how much room there is in a typical + // page for it. + const int rowPixels = 32; // Must be multiple of 4 for pattern below. + const int colPixels = 2; + misspellBitmap = new SkBitmap; + misspellBitmap->setConfig(SkBitmap::kARGB_8888_Config, + rowPixels, colPixels); + misspellBitmap->allocPixels(); + + misspellBitmap->eraseARGB(0, 0, 0, 0); + const uint32_t lineColor = 0xFFFF0000; // Opaque red. + const uint32_t antiColor = 0x60600000; // Semitransparent red. + + // Pattern: X o o X o o X + // o X o o X o + uint32_t* row1 = misspellBitmap->getAddr32(0, 0); + uint32_t* row2 = misspellBitmap->getAddr32(0, 1); + for (int x = 0; x < rowPixels; x++) { + switch (x % 4) { + case 0: + row1[x] = lineColor; + break; + case 1: + row1[x] = antiColor; + row2[x] = antiColor; + break; + case 2: + row2[x] = lineColor; + break; + case 3: + row1[x] = antiColor; + row2[x] = antiColor; + break; + } + } + } + + // Offset it vertically by 1 so that there's some space under the text. + SkScalar originX = SkIntToScalar(pt.x()); + SkScalar originY = SkIntToScalar(pt.y()) + 1; + + // Make a shader for the bitmap with an origin of the box we'll draw. This + // shader is refcounted and will have an initial refcount of 1. + SkShader* shader = SkShader::CreateBitmapShader( + *misspellBitmap, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + SkMatrix matrix; + matrix.reset(); + matrix.postTranslate(originX, originY); + shader->setLocalMatrix(matrix); + + // Assign the shader to the paint & release our reference. The paint will + // now own the shader and the shader will be destroyed when the paint goes + // out of scope. + SkPaint paint; + paint.setShader(shader); + shader->unref(); + + SkRect rect; + rect.set(originX, + originY, + originX + SkIntToScalar(width), + originY + SkIntToScalar(misspellBitmap->height())); + platformContext()->canvas()->drawRect(rect, paint); +} + +void GraphicsContext::drawLineForText(const IntPoint& pt, + int width, + bool printing) +{ + if (paintingDisabled()) + return; + + int thickness = SkMax32(static_cast<int>(strokeThickness()), 1); + SkRect r; + r.fLeft = SkIntToScalar(pt.x()); + r.fTop = SkIntToScalar(pt.y()); + r.fRight = r.fLeft + SkIntToScalar(width); + r.fBottom = r.fTop + SkIntToScalar(thickness); + + SkPaint paint; + paint.setColor(strokeColor().rgb()); + platformContext()->canvas()->drawRect(r, paint); +} + +// Draws a filled rectangle with a stroked border. +void GraphicsContext::drawRect(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) + // See the fillRect below. + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + + platformContext()->drawRect(r); +} + +void GraphicsContext::fillPath() +{ + if (paintingDisabled()) + return; + + const SkPath& path = *platformContext()->currentPath(); + if (!isPathSkiaSafe(getCTM(), path)) + return; + + const GraphicsContextState& state = m_common->state; + ColorSpace colorSpace = state.fillColorSpace; + + if (colorSpace == SolidColorSpace && !fillColor().alpha()) + return; + + platformContext()->setFillRule(state.fillRule == RULE_EVENODD ? + SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + + if (colorSpace == PatternColorSpace) { + SkShader* pat = state.fillPattern->createPlatformPattern(getCTM()); + paint.setShader(pat); + pat->unref(); + } else if (colorSpace == GradientColorSpace) + paint.setShader(state.fillGradient->platformGradient()); + + platformContext()->canvas()->drawPath(path, paint); +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) + // See the other version of fillRect below. + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + + const GraphicsContextState& state = m_common->state; + ColorSpace colorSpace = state.fillColorSpace; + + if (colorSpace == SolidColorSpace && !fillColor().alpha()) + return; + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + + if (colorSpace == PatternColorSpace) { + SkShader* pat = state.fillPattern->createPlatformPattern(getCTM()); + paint.setShader(pat); + pat->unref(); + } else if (colorSpace == GradientColorSpace) + paint.setShader(state.fillGradient->platformGradient()); + + platformContext()->canvas()->drawRect(r, paint); +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) +{ + if (paintingDisabled()) + return; + + if (!color.alpha()) + return; + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) { + // Special case when the rectangle overflows fixed point. This is a + // workaround to fix bug 1212844. When the input rectangle is very + // large, it can overflow Skia's internal fixed point rect. This + // should be fixable in Skia (since the output bitmap isn't that + // large), but until that is fixed, we try to handle it ourselves. + // + // We manually clip the rectangle to the current clip rect. This + // will prevent overflow. The rectangle will be transformed to the + // canvas' coordinate space before it is converted to fixed point + // so we are guaranteed not to overflow after doing this. + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } + + SkPaint paint; + platformContext()->setupPaintCommon(&paint); + paint.setColor(color.rgb()); + platformContext()->canvas()->drawRect(r, paint); +} + +void GraphicsContext::fillRoundedRect(const IntRect& rect, + const IntSize& topLeft, + const IntSize& topRight, + const IntSize& bottomLeft, + const IntSize& bottomRight, + const Color& color) +{ + if (paintingDisabled()) + return; + + SkRect r = rect; + if (!isRectSkiaSafe(getCTM(), r)) + // See fillRect(). + ClipRectToCanvas(*platformContext()->canvas(), r, &r); + + SkPath path; + addCornerArc(&path, r, topRight, 270); + addCornerArc(&path, r, bottomRight, 0); + addCornerArc(&path, r, bottomLeft, 90); + addCornerArc(&path, r, topLeft, 180); + + SkPaint paint; + platformContext()->setupPaintForFilling(&paint); + platformContext()->canvas()->drawPath(path, paint); + return fillRect(rect, color); +} + +TransformationMatrix GraphicsContext::getCTM() const +{ + return platformContext()->canvas()->getTotalMatrix(); +} + +FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) +{ + // This logic is copied from GraphicsContextCG, eseidel 5/05/08 + + // 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. + + const SkMatrix& deviceMatrix = platformContext()->canvas()->getTotalMatrix(); + if (deviceMatrix.isIdentity()) + return rect; + + float deviceScaleX = sqrtf(square(deviceMatrix.getScaleX()) + + square(deviceMatrix.getSkewY())); + float deviceScaleY = sqrtf(square(deviceMatrix.getSkewX()) + + square(deviceMatrix.getScaleY())); + + FloatPoint deviceOrigin(rect.x() * deviceScaleX, rect.y() * deviceScaleY); + FloatPoint deviceLowerRight((rect.x() + rect.width()) * deviceScaleX, + (rect.y() + rect.height()) * deviceScaleY); + + deviceOrigin.setX(roundf(deviceOrigin.x())); + deviceOrigin.setY(roundf(deviceOrigin.y())); + deviceLowerRight.setX(roundf(deviceLowerRight.x())); + deviceLowerRight.setY(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() != 0) + deviceLowerRight.move(0, 1); + if (deviceOrigin.x() == deviceLowerRight.x() && rect.width() != 0) + deviceLowerRight.move(1, 0); + + FloatPoint roundedOrigin(deviceOrigin.x() / deviceScaleX, + deviceOrigin.y() / deviceScaleY); + FloatPoint roundedLowerRight(deviceLowerRight.x() / deviceScaleX, + deviceLowerRight.y() / deviceScaleY); + return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin); +} + +void GraphicsContext::scale(const FloatSize& size) +{ + if (paintingDisabled()) + return; + platformContext()->canvas()->scale(WebCoreFloatToSkScalar(size.width()), + WebCoreFloatToSkScalar(size.height())); +} + +void GraphicsContext::setAlpha(float alpha) +{ + if (paintingDisabled()) + return; + platformContext()->setAlpha(alpha); +} + +void GraphicsContext::setCompositeOperation(CompositeOperator op) +{ + if (paintingDisabled()) + return; + platformContext()->setPorterDuffMode(WebCoreCompositeToSkiaComposite(op)); +} + +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) +{ + notImplemented(); +} + +void GraphicsContext::setLineCap(LineCap cap) +{ + if (paintingDisabled()) + return; + switch (cap) { + case ButtCap: + platformContext()->setLineCap(SkPaint::kButt_Cap); + break; + case RoundCap: + platformContext()->setLineCap(SkPaint::kRound_Cap); + break; + case SquareCap: + platformContext()->setLineCap(SkPaint::kSquare_Cap); + break; + default: + ASSERT(0); + break; + } +} + +void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) +{ + if (paintingDisabled()) + return; + + // FIXME: This is lifted directly off SkiaSupport, lines 49-74 + // so it is not guaranteed to work correctly. + size_t dashLength = dashes.size(); + if (!dashLength) + return; + + size_t count = (dashLength % 2) == 0 ? dashLength : dashLength * 2; + SkScalar* intervals = new SkScalar[count]; + + for (unsigned int i = 0; i < count; i++) + intervals[i] = dashes[i % dashLength]; + + platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset)); + + delete[] intervals; +} + +void GraphicsContext::setLineJoin(LineJoin join) +{ + if (paintingDisabled()) + return; + switch (join) { + case MiterJoin: + platformContext()->setLineJoin(SkPaint::kMiter_Join); + break; + case RoundJoin: + platformContext()->setLineJoin(SkPaint::kRound_Join); + break; + case BevelJoin: + platformContext()->setLineJoin(SkPaint::kBevel_Join); + break; + default: + ASSERT(0); + break; + } +} + +void GraphicsContext::setMiterLimit(float limit) +{ + if (paintingDisabled()) + return; + platformContext()->setMiterLimit(limit); +} + +void GraphicsContext::setPlatformFillColor(const Color& color) +{ + if (paintingDisabled()) + return; + platformContext()->setFillColor(color.rgb()); +} + +void GraphicsContext::setPlatformShadow(const IntSize& size, + int blurInt, + const Color& color) +{ + if (paintingDisabled()) + return; + + double width = size.width(); + double height = size.height(); + double blur = blurInt; + + // TODO(tc): This still does not address the issue that shadows + // within canvas elements should ignore transforms. + if (m_common->state.shadowsIgnoreTransforms) { + // Currently only the GraphicsContext associated with the + // CanvasRenderingContext for HTMLCanvasElement have shadows ignore + // Transforms. So with this flag set, we know this state is associated + // with a CanvasRenderingContext. + // CG uses natural orientation for Y axis, but the HTML5 canvas spec + // does not. + // So we now flip the height since it was flipped in + // CanvasRenderingContext in order to work with CG. + height = -height; + } + + SkColor c; + if (color.isValid()) + c = color.rgb(); + else + c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" apple shadow color. + + // TODO(tc): Should we have a max value for the blur? CG clamps at 1000.0 + // for perf reasons. + SkDrawLooper* dl = new SkBlurDrawLooper(blur / 2, width, height, c); + platformContext()->setDrawLooper(dl); + dl->unref(); +} + +void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeColor(strokecolor.rgb()); +} + +void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& stroke) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeStyle(stroke); +} + +void GraphicsContext::setPlatformStrokeThickness(float thickness) +{ + if (paintingDisabled()) + return; + + platformContext()->setStrokeThickness(thickness); +} + +void GraphicsContext::setPlatformTextDrawingMode(int mode) +{ + if (paintingDisabled()) + return; + + platformContext()->setTextDrawingMode(mode); +} + +void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) +{ +} + +void GraphicsContext::setPlatformShouldAntialias(bool enable) +{ + if (paintingDisabled()) + return; + + platformContext()->setUseAntialiasing(enable); +} + +void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect oval = r; + if (strokeStyle() == NoStroke) { + // Stroke using the fill color. + // TODO(brettw) is this really correct? It seems unreasonable. + platformContext()->setupPaintForFilling(&paint); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(WebCoreFloatToSkScalar(strokeThickness())); + } else + platformContext()->setupPaintForStroking(&paint, 0, 0); + + // We do this before converting to scalar, so we don't overflow SkFixed. + startAngle = fastMod(startAngle, 360); + angleSpan = fastMod(angleSpan, 360); + + SkPath path; + path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan)); + if (!isPathSkiaSafe(getCTM(), path)) + return; + platformContext()->canvas()->drawPath(path, paint); +} + +void GraphicsContext::strokePath() +{ + if (paintingDisabled()) + return; + + const SkPath& path = *platformContext()->currentPath(); + if (!isPathSkiaSafe(getCTM(), path)) + return; + + const GraphicsContextState& state = m_common->state; + ColorSpace colorSpace = state.strokeColorSpace; + + if (colorSpace == SolidColorSpace && !strokeColor().alpha()) + return; + + SkPaint paint; + platformContext()->setupPaintForStroking(&paint, 0, 0); + + if (colorSpace == PatternColorSpace) { + SkShader* pat = state.strokePattern->createPlatformPattern(getCTM()); + paint.setShader(pat); + pat->unref(); + } else if (colorSpace == GradientColorSpace) + paint.setShader(state.strokeGradient->platformGradient()); + + platformContext()->canvas()->drawPath(path, paint); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) +{ + if (paintingDisabled()) + return; + + if (!isRectSkiaSafe(getCTM(), rect)) + return; + + const GraphicsContextState& state = m_common->state; + ColorSpace colorSpace = state.strokeColorSpace; + + if (colorSpace == SolidColorSpace && !strokeColor().alpha()) + return; + + SkPaint paint; + platformContext()->setupPaintForStroking(&paint, 0, 0); + paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); + + if (colorSpace == PatternColorSpace) { + SkShader* pat = state.strokePattern->createPlatformPattern(getCTM()); + paint.setShader(pat); + pat->unref(); + } else if (colorSpace == GradientColorSpace) + paint.setShader(state.strokeGradient->platformGradient()); + + platformContext()->canvas()->drawRect(rect, paint); +} + +void GraphicsContext::rotate(float angleInRadians) +{ + if (paintingDisabled()) + return; + + platformContext()->canvas()->rotate(WebCoreFloatToSkScalar( + angleInRadians * (180.0f / 3.14159265f))); +} + +void GraphicsContext::translate(float w, float h) +{ + if (paintingDisabled()) + return; + + platformContext()->canvas()->translate(WebCoreFloatToSkScalar(w), + WebCoreFloatToSkScalar(h)); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp new file mode 100644 index 0000000..fdfcb85 --- /dev/null +++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -0,0 +1,210 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "ImageBuffer.h" + +#include "BitmapImage.h" +#include "BitmapImageSingleFrameSkia.h" +#include "GraphicsContext.h" +#include "ImageData.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "SkiaUtils.h" + +using namespace std; + +namespace WebCore { + +// We pass a technically-uninitialized canvas to the platform context here since +// the canvas initialization completes in ImageBuffer::ImageBuffer. But +// PlatformContext doesn't actually need to use the object, and this makes all +// the ownership easier to manage. +ImageBufferData::ImageBufferData(const IntSize& size) + : m_platformContext(0) // Canvas is set in ImageBuffer constructor. +{ +} + +ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) + : m_data(size) + , m_size(size) +{ + if (!m_data.m_canvas.initialize(size.width(), size.height(), false)) { + success = false; + return; + } + + m_data.m_platformContext.setCanvas(&m_data.m_canvas); + m_context.set(new GraphicsContext(&m_data.m_platformContext)); + + // Make the background transparent. It would be nice if this wasn't + // required, but the canvas is currently filled with the magic transparency + // color. Can we have another way to manage this? + m_data.m_canvas.drawARGB(0, 0, 0, 0, SkPorterDuff::kClear_Mode); + success = true; +} + +ImageBuffer::~ImageBuffer() +{ +} + +GraphicsContext* ImageBuffer::context() const +{ + return m_context.get(); +} + +Image* ImageBuffer::image() const +{ + if (!m_image) { + // This creates a COPY of the image and will cache that copy. This means + // that if subsequent operations take place on the context, neither the + // currently-returned image, nor the results of future image() calls, + // will contain that operation. + // + // This seems silly, but is the way the CG port works: image() is + // intended to be used only when rendering is "complete." + m_image = BitmapImageSingleFrameSkia::create( + *m_data.m_platformContext.bitmap()); + } + return m_image.get(); +} + +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const +{ + ASSERT(context()); + + RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); + unsigned char* data = result->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; + + const SkBitmap& bitmap = *context()->platformContext()->bitmap(); + ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); + SkAutoLockPixels bitmapLock(bitmap); + + unsigned destBytesPerRow = 4 * rect.width(); + unsigned char* destRow = data + destY * destBytesPerRow + destX * 4; + + for (int y = 0; y < numRows; ++y) { + uint32_t* srcRow = bitmap.getAddr32(originX, originY + y); + for (int x = 0; x < numColumns; ++x) { + SkColor color = SkPMColorToColor(srcRow[x]); + unsigned char* destPixel = &destRow[x * 4]; + destPixel[0] = SkColorGetR(color); + destPixel[1] = SkColorGetG(color); + destPixel[2] = SkColorGetB(color); + destPixel[3] = SkColorGetA(color); + } + destRow += 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; + + const SkBitmap& bitmap = *context()->platformContext()->bitmap(); + ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); + SkAutoLockPixels bitmapLock(bitmap); + + unsigned srcBytesPerRow = 4 * source->width(); + + const unsigned char* srcRow = source->data()->data() + originY * srcBytesPerRow + originX * 4; + + for (int y = 0; y < numRows; ++y) { + uint32_t* destRow = bitmap.getAddr32(destX, destY + y); + for (int x = 0; x < numColumns; ++x) { + const unsigned char* srcPixel = &srcRow[x * 4]; + destRow[x] = SkPreMultiplyARGB(srcPixel[3], srcPixel[0], + srcPixel[1], srcPixel[2]); + } + srcRow += srcBytesPerRow; + } +} + +String ImageBuffer::toDataURL(const String&) const +{ + notImplemented(); + return String(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageSkia.cpp b/WebCore/platform/graphics/skia/ImageSkia.cpp new file mode 100644 index 0000000..1123fe9 --- /dev/null +++ b/WebCore/platform/graphics/skia/ImageSkia.cpp @@ -0,0 +1,462 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "BitmapImage.h" +#include "BitmapImageSingleFrameSkia.h" +#include "ChromiumBridge.h" +#include "FloatConversion.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "Logging.h" +#include "NativeImageSkia.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "PlatformString.h" +#include "SkiaUtils.h" +#include "SkShader.h" +#include "TransformationMatrix.h" + +#include "skia/ext/image_operations.h" +#include "skia/ext/platform_canvas.h" + +namespace WebCore { + +// Used by computeResamplingMode to tell how bitmaps should be resampled. +enum ResamplingMode { + // Nearest neighbor resampling. Used when we detect that the page is + // trying to make a pattern by stretching a small bitmap very large. + RESAMPLE_NONE, + + // Default skia resampling. Used for large growing of images where high + // quality resampling doesn't get us very much except a slowdown. + RESAMPLE_LINEAR, + + // High quality resampling. + RESAMPLE_AWESOME, +}; + +static ResamplingMode computeResamplingMode(const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight) +{ + int destIWidth = static_cast<int>(destWidth); + int destIHeight = static_cast<int>(destHeight); + + // The percent change below which we will not resample. This usually means + // an off-by-one error on the web page, and just doing nearest neighbor + // sampling is usually good enough. + const float kFractionalChangeThreshold = 0.025f; + + // Images smaller than this in either direction are considered "small" and + // are not resampled ever (see below). + const int kSmallImageSizeThreshold = 8; + + // The amount an image can be stretched in a single direction before we + // say that it is being stretched so much that it must be a line or + // background that doesn't need resampling. + const float kLargeStretch = 3.0f; + + // Figure out if we should resample this image. We try to prune out some + // common cases where resampling won't give us anything, since it is much + // slower than drawing stretched. + if (srcWidth == destIWidth && srcHeight == destIHeight) { + // We don't need to resample if the source and destination are the same. + return RESAMPLE_NONE; + } + + if (srcWidth <= kSmallImageSizeThreshold + || srcHeight <= kSmallImageSizeThreshold + || destWidth <= kSmallImageSizeThreshold + || destHeight <= kSmallImageSizeThreshold) { + // Never resample small images. These are often used for borders and + // rules (think 1x1 images used to make lines). + return RESAMPLE_NONE; + } + + if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) { + // Large image detected. + + // Don't resample if it is being stretched a lot in only one direction. + // This is trying to catch cases where somebody has created a border + // (which might be large) and then is stretching it to fill some part + // of the page. + if (srcWidth == destWidth || srcHeight == destHeight) + return RESAMPLE_NONE; + + // The image is growing a lot and in more than one direction. Resampling + // is slow and doesn't give us very much when growing a lot. + return RESAMPLE_LINEAR; + } + + if ((fabs(destWidth - srcWidth) / srcWidth < kFractionalChangeThreshold) + && (fabs(destHeight - srcHeight) / srcHeight < kFractionalChangeThreshold)) { + // It is disappointingly common on the web for image sizes to be off by + // one or two pixels. We don't bother resampling if the size difference + // is a small fraction of the original size. + return RESAMPLE_NONE; + } + + // When the image is not yet done loading, use linear. We don't cache the + // partially resampled images, and as they come in incrementally, it causes + // us to have to resample the whole thing every time. + if (!bitmap.isDataComplete()) + return RESAMPLE_LINEAR; + + // Everything else gets resampled. + return RESAMPLE_AWESOME; +} + +// Draws the given bitmap to the given canvas. The subset of the source bitmap +// identified by src_rect is drawn to the given destination rect. The bitmap +// will be resampled to resample_width * resample_height (this is the size of +// the whole image, not the subset). See shouldResampleBitmap for more. +// +// This does a lot of computation to resample only the portion of the bitmap +// that will only be drawn. This is critical for performance since when we are +// scrolling, for example, we are only drawing a small strip of the image. +// Resampling the whole image every time is very slow, so this speeds up things +// dramatically. +static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) +{ + // First get the subset we need. This is efficient and does not copy pixels. + SkBitmap subset; + bitmap.extractSubset(&subset, srcIRect); + SkRect srcRect; + srcRect.set(srcIRect); + + // Whether we're doing a subset or using the full source image. + bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0 + && srcIRect.width() == bitmap.width() + && srcIRect.height() == bitmap.height(); + + // We will always draw in integer sizes, so round the destination rect. + SkIRect destRectRounded; + destRect.round(&destRectRounded); + SkIRect resizedImageRect; // Represents the size of the resized image. + resizedImageRect.set(0, 0, destRectRounded.width(), destRectRounded.height()); + + if (srcIsFull && bitmap.hasResizedBitmap(destRectRounded.width(), destRectRounded.height())) { + // Yay, this bitmap frame already has a resized version. + SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height()); + canvas.drawBitmapRect(resampled, 0, destRect, &paint); + return; + } + + // Compute the visible portion of our rect. + SkRect destBitmapSubsetSk; + ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); + destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); + + // The matrix inverting, etc. could have introduced rounding error which + // causes the bounds to be outside of the resized bitmap. We round outward + // so we always lean toward it being larger rather than smaller than we + // need, and then clamp to the bitmap bounds so we don't get any invalid + // data. + SkIRect destBitmapSubsetSkI; + destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); + if (!destBitmapSubsetSkI.intersect(resizedImageRect)) + return; // Resized image does not intersect. + + if (srcIsFull && bitmap.shouldCacheResampling( + resizedImageRect.width(), + resizedImageRect.height(), + destBitmapSubsetSkI.width(), + destBitmapSubsetSkI.height())) { + // We're supposed to resize the entire image and cache it, even though + // we don't need all of it. + SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), + destRectRounded.height()); + canvas.drawBitmapRect(resampled, 0, destRect, &paint); + } else { + // We should only resize the exposed part of the bitmap to do the + // minimal possible work. + gfx::Rect destBitmapSubset(destBitmapSubsetSkI.fLeft, + destBitmapSubsetSkI.fTop, + destBitmapSubsetSkI.width(), + destBitmapSubsetSkI.height()); + + // Resample the needed part of the image. + SkBitmap resampled = skia::ImageOperations::Resize(subset, + skia::ImageOperations::RESIZE_LANCZOS3, + destRectRounded.width(), destRectRounded.height(), + destBitmapSubset); + + // Compute where the new bitmap should be drawn. Since our new bitmap + // may be smaller than the original, we have to shift it over by the + // same amount that we cut off the top and left. + SkRect offsetDestRect = { + destBitmapSubset.x() + destRect.fLeft, + destBitmapSubset.y() + destRect.fTop, + destBitmapSubset.right() + destRect.fLeft, + destBitmapSubset.bottom() + destRect.fTop }; + + canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint); + } +} + +static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkPorterDuff::Mode& compOp) +{ + SkPaint paint; + paint.setPorterDuffXfermode(compOp); + + skia::PlatformCanvas* canvas = platformContext->canvas(); + + ResamplingMode resampling = platformContext->isPrinting() ? RESAMPLE_NONE : + computeResamplingMode(bitmap, srcRect.width(), srcRect.height(), + SkScalarToFloat(destRect.width()), + SkScalarToFloat(destRect.height())); + if (resampling == RESAMPLE_AWESOME) { + paint.setFilterBitmap(false); + drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); + } else { + // No resampling necessary, we can just draw the bitmap. We want to + // filter it if we decided to do linear interpolation above, or if there + // is something interesting going on with the matrix (like a rotation). + // Note: for serialization, we will want to subset the bitmap first so + // we don't send extra pixels. + paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); + canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint); + } +} + +// Transforms the given dimensions with the given matrix. Used to see how big +// images will be once transformed. +static void TransformDimensions(const SkMatrix& matrix, float srcWidth, float srcHeight, float* destWidth, float* destHeight) { + // Transform 3 points to see how long each side of the bitmap will be. + SkPoint src_points[3]; // (0, 0), (width, 0), (0, height). + src_points[0].set(0, 0); + src_points[1].set(SkFloatToScalar(srcWidth), 0); + src_points[2].set(0, SkFloatToScalar(srcHeight)); + + // Now measure the length of the two transformed vectors relative to the + // transformed origin to see how big the bitmap will be. Note: for skews, + // this isn't the best thing, but we don't have skews. + SkPoint dest_points[3]; + matrix.mapPoints(dest_points, src_points, 3); + *destWidth = SkScalarToFloat((dest_points[1] - dest_points[0]).length()); + *destHeight = SkScalarToFloat((dest_points[2] - dest_points[0]).length()); +} + +// A helper method for translating negative width and height values. +static FloatRect normalizeRect(const FloatRect& rect) +{ + FloatRect norm = rect; + if (norm.width() < 0) { + norm.setX(norm.x() + norm.width()); + norm.setWidth(-norm.width()); + } + if (norm.height() < 0) { + norm.setY(norm.y() + norm.height()); + norm.setHeight(-norm.height()); + } + return norm; +} + +bool FrameData::clear(bool clearMetadata) +{ + if (clearMetadata) + m_haveMetadata = false; + + if (m_frame) { + // ImageSource::createFrameAtIndex() allocated |m_frame| and passed + // ownership to BitmapImage; we must delete it here. + delete m_frame; + m_frame = 0; + return true; + } + return false; +} + +PassRefPtr<Image> Image::loadPlatformResource(const char *name) +{ + return ChromiumBridge::loadPlatformImageResource(name); +} + +void Image::drawPattern(GraphicsContext* context, + const FloatRect& floatSrcRect, + const TransformationMatrix& patternTransform, + const FloatPoint& phase, + CompositeOperator compositeOp, + const FloatRect& destRect) +{ + if (destRect.isEmpty() || floatSrcRect.isEmpty()) + return; // nothing to draw + + NativeImageSkia* bitmap = nativeImageForCurrentFrame(); + if (!bitmap) + return; + + // This is a very inexpensive operation. It will generate a new bitmap but + // it will internally reference the old bitmap's pixels, adjusting the row + // stride so the extra pixels appear as padding to the subsetted bitmap. + SkBitmap srcSubset; + SkIRect srcRect = enclosingIntRect(floatSrcRect); + bitmap->extractSubset(&srcSubset, srcRect); + + SkBitmap resampled; + SkShader* shader; + + // Figure out what size the bitmap will be in the destination. The + // destination rect is the bounds of the pattern, we need to use the + // matrix to see how bit it will be. + float destBitmapWidth, destBitmapHeight; + TransformDimensions(patternTransform, srcRect.width(), srcRect.height(), + &destBitmapWidth, &destBitmapHeight); + + // Compute the resampling mode. + ResamplingMode resampling; + if (context->platformContext()->isPrinting()) + resampling = RESAMPLE_LINEAR; + else { + resampling = computeResamplingMode(*bitmap, + srcRect.width(), srcRect.height(), + destBitmapWidth, destBitmapHeight); + } + + // Load the transform WebKit requested. + SkMatrix matrix(patternTransform); + + if (resampling == RESAMPLE_AWESOME) { + // Do nice resampling. + SkBitmap resampled = skia::ImageOperations::Resize(srcSubset, + skia::ImageOperations::RESIZE_LANCZOS3, + static_cast<int>(destBitmapWidth), + static_cast<int>(destBitmapHeight)); + shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + + // Since we just resized the bitmap, we need to undo the scale set in + // the image transform. + matrix.setScaleX(SkIntToScalar(1)); + matrix.setScaleY(SkIntToScalar(1)); + } else { + // No need to do nice resampling. + shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + } + + // We also need to translate it such that the origin of the pattern is the + // origin of the destination rect, which is what WebKit expects. Skia uses + // the coordinate system origin as the base for the patter. If WebKit wants + // a shifted image, it will shift it from there using the patternTransform. + float adjustedX = phase.x() + floatSrcRect.x() * + narrowPrecisionToFloat(patternTransform.a()); + float adjustedY = phase.y() + floatSrcRect.y() * + narrowPrecisionToFloat(patternTransform.d()); + matrix.postTranslate(SkFloatToScalar(adjustedX), + SkFloatToScalar(adjustedY)); + shader->setLocalMatrix(matrix); + + SkPaint paint; + paint.setShader(shader)->unref(); + paint.setPorterDuffXfermode(WebCoreCompositeToSkiaComposite(compositeOp)); + paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); + + context->platformContext()->paintSkPaint(destRect, paint); +} + +// ================================================ +// BitmapImage Class +// ================================================ + +// FIXME: These should go to BitmapImageSkia.cpp + +void BitmapImage::initPlatformData() +{ + // This is not used. On Mac, the "platform" data is a cache of some OS + // specific versions of the image that are created is some cases. These + // aren't normally used, it is equivalent to getHBITMAP on Windows, and + // the platform data is the cache. +} + +void BitmapImage::invalidatePlatformData() +{ + // See initPlatformData above. +} + +void BitmapImage::checkForSolidColor() +{ +} + +void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, + const FloatRect& srcRect, CompositeOperator compositeOp) +{ + if (!m_source.initialized()) + return; + + // Spin the animation to the correct frame before we try to draw it, so we + // don't draw an old frame and then immediately need to draw a newer one, + // causing flicker and wasting CPU. + startAnimation(); + + const NativeImageSkia* bm = nativeImageForCurrentFrame(); + if (!bm) + return; // It's too early and we don't have an image yet. + + FloatRect normDstRect = normalizeRect(dstRect); + FloatRect normSrcRect = normalizeRect(srcRect); + + if (normSrcRect.isEmpty() || normDstRect.isEmpty()) + return; // Nothing to draw. + + paintSkBitmap(ctxt->platformContext(), + *bm, + enclosingIntRect(normSrcRect), + enclosingIntRect(normDstRect), + WebCoreCompositeToSkiaComposite(compositeOp)); +} + +// FIXME: These should go into BitmapImageSingleFrameSkia.cpp + +void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt, + const FloatRect& dstRect, + const FloatRect& srcRect, + CompositeOperator compositeOp) +{ + FloatRect normDstRect = normalizeRect(dstRect); + FloatRect normSrcRect = normalizeRect(srcRect); + + if (normSrcRect.isEmpty() || normDstRect.isEmpty()) + return; // Nothing to draw. + + paintSkBitmap(ctxt->platformContext(), + m_nativeImage, + enclosingIntRect(normSrcRect), + enclosingIntRect(normDstRect), + WebCoreCompositeToSkiaComposite(compositeOp)); +} + +PassRefPtr<BitmapImageSingleFrameSkia> BitmapImageSingleFrameSkia::create(const SkBitmap& bitmap) +{ + RefPtr<BitmapImageSingleFrameSkia> image(adoptRef(new BitmapImageSingleFrameSkia())); + if (!bitmap.copyTo(&image->m_nativeImage, bitmap.config())) + return 0; + return image.release(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp new file mode 100644 index 0000000..f77620b --- /dev/null +++ b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp @@ -0,0 +1,247 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "ImageSourceSkia.h" +#include "SharedBuffer.h" + +#include "GIFImageDecoder.h" +#include "JPEGImageDecoder.h" +#include "PNGImageDecoder.h" +#include "BMPImageDecoder.h" +#include "XBMImageDecoder.h" +#include "ICOImageDecoder.h" + +#include "SkBitmap.h" + +namespace WebCore { + +ImageDecoder* createDecoder(const Vector<char>& data, const IntSize& preferredIconSize) +{ + // We need at least 4 bytes to figure out what kind of image we're dealing with. + int length = data.size(); + if (length < 4) + return 0; + + const unsigned char* uContents = (const unsigned char*)data.data(); + const char* contents = data.data(); + + // GIFs begin with GIF8(7 or 9). + if (strncmp(contents, "GIF8", 4) == 0) + return new GIFImageDecoder(); + + // Test for PNG. + if (uContents[0]==0x89 && + uContents[1]==0x50 && + uContents[2]==0x4E && + uContents[3]==0x47) + return new PNGImageDecoder(); + + // JPEG + if (uContents[0]==0xFF && + uContents[1]==0xD8 && + uContents[2]==0xFF) + return new JPEGImageDecoder(); + + // BMP + if (strncmp(contents, "BM", 2) == 0) + return new BMPImageDecoder(); + + // ICOs always begin with a 2-byte 0 followed by a 2-byte 1. + // CURs begin with 2-byte 0 followed by 2-byte 2. + if (!memcmp(contents, "\000\000\001\000", 4) || + !memcmp(contents, "\000\000\002\000", 4)) + return new ICOImageDecoder(preferredIconSize); + + // XBMs require 8 bytes of info. + if (length >= 8 && strncmp(contents, "#define ", 8) == 0) + return new XBMImageDecoder(); + + // Give up. We don't know what the heck this is. + return 0; +} + +ImageSource::ImageSource() + : m_decoder(0) +{} + +ImageSource::~ImageSource() +{ + clear(true); +} + +void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) +{ + // TODO(darin): Figure out what to do with the |data| and |allDataReceived| params. + + if (destroyAll) { + delete m_decoder; + m_decoder = 0; + return; + } + + if (m_decoder) + m_decoder->clearFrameBufferCache(clearBeforeFrame); +} + +bool ImageSource::initialized() const +{ + return m_decoder; +} + +void ImageSource::setData(SharedBuffer* data, bool allDataReceived) +{ + // Make the decoder by sniffing the bytes. + // This method will examine the data and instantiate an instance of the appropriate decoder plugin. + // If insufficient bytes are available to determine the image type, no decoder plugin will be + // made. + if (!m_decoder) + m_decoder = createDecoder(data->buffer(), IntSize()); + + // CreateDecoder will return NULL if the decoder could not be created. Plus, + // we should not send more data to a decoder which has already decided it + // has failed. + if (!m_decoder || m_decoder->failed()) + return; + m_decoder->setData(data, allDataReceived); +} + +bool ImageSource::isSizeAvailable() +{ + if (!m_decoder) + return false; + + return m_decoder->isSizeAvailable(); +} + +IntSize ImageSource::size() const +{ + if (!m_decoder) + return IntSize(); + + return m_decoder->size(); +} + +IntSize ImageSource::frameSizeAtIndex(size_t) const +{ + // TODO(brettw) do we need anything here? + return size(); +} + +int ImageSource::repetitionCount() +{ + if (!m_decoder) + return cAnimationNone; + + return m_decoder->repetitionCount(); +} + +size_t ImageSource::frameCount() const +{ + if (!m_decoder) + return 0; + return m_decoder->failed() ? 0 : m_decoder->frameCount(); +} + +NativeImagePtr ImageSource::createFrameAtIndex(size_t index) +{ + if (!m_decoder) + return 0; + + // Note that the buffer can have NULL bytes even when it is marked as + // non-empty. It seems "FrameEmpty" is only set before the frame has been + // initialized. If it is decoded and it happens to be empty, it will be + // marked as "FrameComplete" but will still have NULL bytes. + RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); + if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) + return 0; + + // Copy the bitmap. The pixel data is refcounted internally by SkBitmap, so + // this doesn't cost much. This pointer will be owned by the BitmapImage + // and freed in FrameData::clear(). + return new NativeImageSkia(buffer->bitmap()); +} + +bool ImageSource::frameIsCompleteAtIndex(size_t index) +{ + if (!m_decoder) + return false; + + RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); + return buffer && buffer->status() == RGBA32Buffer::FrameComplete; +} + +float ImageSource::frameDurationAtIndex(size_t index) +{ + if (!m_decoder) + return 0; + + RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); + if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) + return 0; + + // Many annoying ads specify a 0 duration to make an image flash as quickly + // as possible. We follow WinIE's behavior and use a duration of 100 ms + // for any frames that specify a duration of <= 50 ms. See + // <http://bugs.webkit.org/show_bug.cgi?id=14413> or Radar 4051389 for + // more. + const float duration = buffer->duration() / 1000.0f; + return (duration < 0.051f) ? 0.100f : duration; +} + +bool ImageSource::frameHasAlphaAtIndex(size_t index) +{ + if (!m_decoder || !m_decoder->supportsAlpha()) + return false; + + RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); + if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) + return false; + + return buffer->hasAlpha(); +} + +void ImageSourceSkia::setData(SharedBuffer* data, + bool allDataReceived, + const IntSize& preferredIconSize) +{ + if (!m_decoder) + m_decoder = createDecoder(data->buffer(), preferredIconSize); + + ImageSource::setData(data, allDataReceived); +} + +String ImageSource::filenameExtension() const +{ + return m_decoder ? m_decoder->filenameExtension() : String(); +} + +} diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.h b/WebCore/platform/graphics/skia/ImageSourceSkia.h new file mode 100644 index 0000000..9cb4a95 --- /dev/null +++ b/WebCore/platform/graphics/skia/ImageSourceSkia.h @@ -0,0 +1,60 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "ImageSource.h" + +namespace WebCore { + +class ImageSourceSkia : public ImageSource { +public: + // FIXME: This class is a hack to support Chromium's ICO decoder + // Currently our ICO decoder decodes all data during setData() instead of + // being lazy. In addition, it only decodes one frame (closest to the size + // passed to the decoder during createDecoder, called from setData) and + // discards all other data in the file. + // + // To fix this will require fixing the ICO decoder to be lazy, or to decode + // all frames. Apple's decoders (ImageIO) decode all frames, and return + // them all sorted in decreasing size. WebCore always draws the first frame. + // + // This is a special-purpose routine for the favicon decoder, which is used + // to specify a particular icon size for the ICOImageDecoder to prefer + // decoding. Note that not all favicons are ICOs, so this won't + // necessarily do anything differently than ImageSource::setData(). + // + // Passing an empty IntSize for |preferredIconSize| here is exactly + // equivalent to just calling ImageSource::setData(). See also comments in + // ICOImageDecoder.cpp. + void setData(SharedBuffer* data, + bool allDataReceived, + const IntSize& preferredIconSize); +}; + +} diff --git a/WebCore/platform/graphics/skia/IntPointSkia.cpp b/WebCore/platform/graphics/skia/IntPointSkia.cpp new file mode 100644 index 0000000..fd9a6fd --- /dev/null +++ b/WebCore/platform/graphics/skia/IntPointSkia.cpp @@ -0,0 +1,56 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "IntPoint.h" + +#include "SkPoint.h" + +namespace WebCore { + +IntPoint::IntPoint(const SkIPoint& p) + : m_x(p.fX) + , m_y(p.fY) +{ +} + +IntPoint::operator SkIPoint() const +{ + SkIPoint p = { m_x, m_y }; + return p; +} + +IntPoint::operator SkPoint() const +{ + SkPoint p = { SkIntToScalar(m_x), SkIntToScalar(m_y) }; + return p; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/IntRectSkia.cpp b/WebCore/platform/graphics/skia/IntRectSkia.cpp new file mode 100644 index 0000000..ea138ee --- /dev/null +++ b/WebCore/platform/graphics/skia/IntRectSkia.cpp @@ -0,0 +1,57 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "IntRect.h" + +#include "SkRect.h" + +namespace WebCore { + +IntRect::operator SkIRect() const +{ + SkIRect rect = { x(), y(), right(), bottom() }; + return rect; +} + +IntRect::operator SkRect() const +{ + SkRect rect; + rect.set(SkIntToScalar(x()), SkIntToScalar(y()), SkIntToScalar(right()), SkIntToScalar(bottom())); + return rect; +} + +IntRect::IntRect(const SkIRect& r) + : m_location(r.fLeft, r.fTop) + , m_size(r.width(), r.height()) +{ +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/NativeImageSkia.cpp b/WebCore/platform/graphics/skia/NativeImageSkia.cpp new file mode 100644 index 0000000..e59d1e2 --- /dev/null +++ b/WebCore/platform/graphics/skia/NativeImageSkia.cpp @@ -0,0 +1,109 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "skia/ext/image_operations.h" + +#include "NativeImageSkia.h" +#include "SkiaUtils.h" + +NativeImageSkia::NativeImageSkia() + : m_isDataComplete(false), + m_lastRequestSize(0, 0), + m_resizeRequests(0) +{ +} + +int NativeImageSkia::decodedSize() const +{ + return getSize() + m_resizedImage.getSize(); +} + +bool NativeImageSkia::hasResizedBitmap(int w, int h) const +{ + if (m_lastRequestSize.width() == w && m_lastRequestSize.height() == h) + m_resizeRequests++; + else { + m_lastRequestSize = WebCore::IntSize(w, h); + m_resizeRequests = 0; + } + + return m_resizedImage.width() == w && m_resizedImage.height() == h; +} + +// FIXME: don't cache when image is in-progress. + +SkBitmap NativeImageSkia::resizedBitmap(int w, int h) const +{ + if (m_resizedImage.width() != w || m_resizedImage.height() != h) + m_resizedImage = skia::ImageOperations::Resize(*this, skia::ImageOperations::RESIZE_LANCZOS3, w, h); + + return m_resizedImage; +} + +bool NativeImageSkia::shouldCacheResampling(int destWidth, + int destHeight, + int destSubsetWidth, + int destSubsetHeight) const +{ + // We can not cache incomplete frames. This might be a good optimization in + // the future, were we know how much of the frame has been decoded, so when + // we incrementally draw more of the image, we only have to resample the + // parts that are changed. + if (!m_isDataComplete) + return false; + + // If the destination bitmap is small, we'll always allow caching, since + // there is not very much penalty for computing it and it may come in handy. + static const int kSmallBitmapSize = 4096; + if (destWidth * destHeight <= kSmallBitmapSize) + return true; + + // If "too many" requests have been made for this bitmap, we assume that + // many more will be made as well, and we'll go ahead and cache it. + static const int kManyRequestThreshold = 4; + if (m_lastRequestSize.width() == destWidth && + m_lastRequestSize.height() == destHeight) { + if (m_resizeRequests >= kManyRequestThreshold) + return true; + } else { + // When a different size is being requested, count this as a query + // (hasResizedBitmap) and reset the counter. + m_lastRequestSize = WebCore::IntSize(destWidth, destHeight); + m_resizeRequests = 0; + } + + // Otherwise, use the heuristic that if more than 1/4 of the image is + // requested, it's worth caching. + int destSize = destWidth * destHeight; + int destSubsetSize = destSubsetWidth * destSubsetHeight; + return destSize / 4 < destSubsetSize; +} diff --git a/WebCore/platform/graphics/skia/NativeImageSkia.h b/WebCore/platform/graphics/skia/NativeImageSkia.h new file mode 100644 index 0000000..5947238 --- /dev/null +++ b/WebCore/platform/graphics/skia/NativeImageSkia.h @@ -0,0 +1,104 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 NativeImageSkia_h +#define NativeImageSkia_h + +#include "SkBitmap.h" +#include "IntSize.h" + +// This object is used as the "native image" in our port. When WebKit uses +// "NativeImagePtr", it is a pointer to this type. It is an SkBitmap, but also +// stores a cached resized image. +class NativeImageSkia : public SkBitmap { +public: + NativeImageSkia(); + + // Returns the number of bytes of image data. This includes the cached + // resized version if there is one. + int decodedSize() const; + + // Sets the data complete flag. This is called by the image decoder when + // all data is complete, and used by us to know whether we can cache + // resized images. + void setDataComplete() { m_isDataComplete = true; } + + // Returns true if the entire image has been decoded. + bool isDataComplete() const { return m_isDataComplete; } + + // We can keep a resized version of the bitmap cached on this object. + // This function will return true if there is a cached version of the + // given image subset with the given dimensions. + bool hasResizedBitmap(int width, int height) const; + + // This will return an existing resized image, or generate a new one of + // the specified size and store it in the cache. Subsetted images can not + // be cached unless the subset is the entire bitmap. + SkBitmap resizedBitmap(int width, int height) const; + + // Returns true if the given resize operation should either resize the whole + // image and cache it, or resize just the part it needs and throw the result + // away. + // + // On the one hand, if only a small subset is desired, then we will waste a + // lot of time resampling the entire thing, so we only want to do exactly + // what's required. On the other hand, resampling the entire bitmap is + // better if we're going to be using it more than once (like a bitmap + // scrolling on and off the screen. Since we only cache when doing the + // entire thing, it's best to just do it up front. + bool shouldCacheResampling(int destWidth, + int destHeight, + int destSubsetWidth, + int destSubsetHeight) const; + +private: + // Set to true when the data is complete. Before the entire image has + // loaded, we do not want to cache a resize. + bool m_isDataComplete; + + // The cached bitmap. This will be empty() if there is no cached image. + mutable SkBitmap m_resizedImage; + + // References how many times that the image size has been requested for + // the last size. + // + // Every time we get a request, if it matches the m_lastRequestSize, we'll + // increment the counter, and if not, we'll reset the counter and save the + // size. + // + // This allows us to see if many requests have been made for the same + // resized image, we know that we should probably cache it, even if all of + // those requests individually are small and would not otherwise be cached. + mutable WebCore::IntSize m_lastRequestSize; + mutable int m_resizeRequests; +}; + +#endif // NativeImageSkia_h + diff --git a/WebCore/platform/graphics/skia/PathSkia.cpp b/WebCore/platform/graphics/skia/PathSkia.cpp new file mode 100644 index 0000000..ca99322 --- /dev/null +++ b/WebCore/platform/graphics/skia/PathSkia.cpp @@ -0,0 +1,316 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "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 THE COPYRIGHT +// OWNER 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 "Path.h" + +#include "FloatRect.h" +#include "ImageBuffer.h" +#include "StrokeStyleApplier.h" + +#include "SkPath.h" +#include "SkRegion.h" +#include "SkiaUtils.h" + +#include <wtf/MathExtras.h> + +namespace WebCore { + +Path::Path() +{ + m_path = new SkPath; +} + +Path::Path(const Path& other) +{ + m_path = new SkPath(*other.m_path); +} + +Path::~Path() +{ + delete m_path; +} + +Path& Path::operator=(const Path& other) +{ + *m_path = *other.m_path; + return *this; +} + +bool Path::isEmpty() const +{ + return m_path->isEmpty(); +} + +bool Path::contains(const FloatPoint& point, WindRule rule) const +{ + return SkPathContainsPoint(m_path, point, + rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType); +} + +void Path::translate(const FloatSize& size) +{ + m_path->offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height())); +} + +FloatRect Path::boundingRect() const +{ + SkRect rect; + m_path->computeBounds(&rect, SkPath::kExact_BoundsType); + return rect; +} + +void Path::moveTo(const FloatPoint& point) +{ + m_path->moveTo(point); +} + +void Path::addLineTo(const FloatPoint& point) +{ + m_path->lineTo(point); +} + +void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) +{ + m_path->quadTo(cp, ep); +} + +void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep) +{ + m_path->cubicTo(p1, p2, ep); +} + +void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) +{ + m_path->arcTo(p1, p2, WebCoreFloatToSkScalar(radius)); +} + +void Path::closeSubpath() +{ + m_path->close(); +} + +void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlockwise) { + SkScalar cx = WebCoreFloatToSkScalar(p.x()); + SkScalar cy = WebCoreFloatToSkScalar(p.y()); + SkScalar radius = WebCoreFloatToSkScalar(r); + + SkRect oval; + oval.set(cx - radius, cy - radius, cx + radius, cy + radius); + + float sweep = ea - sa; + // check for a circle + if (sweep >= 2 * piFloat || sweep <= -2 * piFloat) + m_path->addOval(oval); + else { + SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat); + SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); + + // Counterclockwise arcs should be drawn with negative sweeps, while + // clockwise arcs should be drawn with positive sweeps. Check to see + // if the situation is reversed and correct it by adding or subtracting + // a full circle + if (anticlockwise && sweepDegrees > 0) { + sweepDegrees -= SkIntToScalar(360); + } else if (!anticlockwise && sweepDegrees < 0) { + sweepDegrees += SkIntToScalar(360); + } + + m_path->arcTo(oval, startDegrees, sweepDegrees, false); + } +} + +void Path::addRect(const FloatRect& rect) +{ + m_path->addRect(rect); +} + +void Path::addEllipse(const FloatRect& rect) +{ + m_path->addOval(rect); +} + +void Path::clear() +{ + m_path->reset(); +} + +static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count) +{ + for (int i = 0; i < count; i++) { + dst[i].setX(SkScalarToFloat(src[i].fX)); + dst[i].setY(SkScalarToFloat(src[i].fY)); + } + return dst; +} + +void Path::apply(void* info, PathApplierFunction function) const +{ + SkPath::Iter iter(*m_path, false); + SkPoint pts[4]; + PathElement pathElement; + FloatPoint pathPoints[3]; + + for (;;) { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + pathElement.type = PathElementMoveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[0], 1); + break; + case SkPath::kLine_Verb: + pathElement.type = PathElementAddLineToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 1); + break; + case SkPath::kQuad_Verb: + pathElement.type = PathElementAddQuadCurveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 2); + break; + case SkPath::kCubic_Verb: + pathElement.type = PathElementAddCurveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 3); + break; + case SkPath::kClose_Verb: + pathElement.type = PathElementCloseSubpath; + pathElement.points = convertPathPoints(pathPoints, 0, 0); + break; + case SkPath::kDone_Verb: + return; + } + function(info, &pathElement); + } +} + +void Path::transform(const TransformationMatrix& xform) +{ + m_path->transform(xform); +} + +String Path::debugString() const +{ + String result; + + SkPath::Iter iter(*m_path, false); + SkPoint pts[4]; + + int numPoints = m_path->getPoints(0, 0); + SkPath::Verb verb; + + do { + verb = iter.next(pts); + switch (verb) { + case SkPath::kMove_Verb: + result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY); + numPoints -= 1; + break; + case SkPath::kLine_Verb: + if (!iter.isCloseLine()) { + result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY); + numPoints -= 1; + } + break; + case SkPath::kQuad_Verb: + result += String::format("Q%.2f,%.2f,%.2f,%.2f ", + pts[1].fX, pts[1].fY, + pts[2].fX, pts[2].fY); + numPoints -= 2; + break; + case SkPath::kCubic_Verb: + result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ", + pts[1].fX, pts[1].fY, + pts[2].fX, pts[2].fY, + pts[3].fX, pts[3].fY); + numPoints -= 3; + break; + case SkPath::kClose_Verb: + result += "Z "; + break; + case SkPath::kDone_Verb: + break; + } + } while (verb != SkPath::kDone_Verb); + + // If you have a path that ends with an M, Skia will not iterate the + // trailing M. That's nice of it, but Apple's paths output the trailing M + // and we want out layout dumps to look like theirs + if (numPoints) { + ASSERT(numPoints==1); + m_path->getLastPt(pts); + result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY); + } + + return result.stripWhiteSpace(); +} + +// Computes the bounding box for the stroke and style currently selected into +// the given bounding box. This also takes into account the stroke width. +static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context) +{ + SkPaint paint; + context->platformContext()->setupPaintForStroking(&paint, 0, 0); + SkPath boundingPath; + paint.getFillPath(context->platformContext()->currentPath(), &boundingPath); + SkRect r; + boundingPath.computeBounds(&r, SkPath::kExact_BoundsType); + return r; +} + +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + GraphicsContext* scratch = scratchContext(); + scratch->save(); + scratch->beginPath(); + scratch->addPath(*this); + + if (applier) + applier->strokeStyle(scratch); + + FloatRect r = boundingBoxForCurrentStroke(scratch); + scratch->restore(); + return r; +} + +bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const +{ + ASSERT(applier); + GraphicsContext* scratch = scratchContext(); + scratch->save(); + + applier->strokeStyle(scratch); + + SkPaint paint; + scratch->platformContext()->setupPaintForStroking(&paint, 0, 0); + SkPath strokePath; + paint.getFillPath(*platformPath(), &strokePath); + bool contains = SkPathContainsPoint(&strokePath, point, + SkPath::kWinding_FillType); + + scratch->restore(); + return contains; +} +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/PatternSkia.cpp b/WebCore/platform/graphics/skia/PatternSkia.cpp new file mode 100644 index 0000000..be8eb8a --- /dev/null +++ b/WebCore/platform/graphics/skia/PatternSkia.cpp @@ -0,0 +1,77 @@ +/* + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "Image.h" +#include "NativeImageSkia.h" +#include "TransformationMatrix.h" + +#include "SkShader.h" +#include "SkCanvas.h" + +namespace WebCore { + +PlatformPatternPtr Pattern::createPlatformPattern(const TransformationMatrix& patternTransform) const +{ + // Note: patternTransform is ignored since it seems to be applied elsewhere + // (when the pattern is used?). Applying it to the pattern (i.e. + // shader->setLocalMatrix) results in a double transformation. This can be + // seen, for instance, as an extra offset in: + // LayoutTests/fast/canvas/patternfill-repeat.html + // and expanded scale and skew in: + // LayoutTests/svg/W3C-SVG-1.1/pservers-grad-06-b.svg + + SkBitmap* bm = m_tileImage->nativeImageForCurrentFrame(); + if (m_repeatX && m_repeatY) + return SkShader::CreateBitmapShader(*bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + + // Skia does not have a "draw the tile only once" option. Clamp_TileMode + // repeats the last line of the image after drawing one tile. To avoid + // filling the space with arbitrary pixels, this workaround forces the + // image to have a line of transparent pixels on the "repeated" edge(s), + // thus causing extra space to be transparent filled. + SkShader::TileMode tileModeX = m_repeatX ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; + SkShader::TileMode tileModeY = m_repeatY ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; + int expandW = m_repeatX ? 0 : 1; + int expandH = m_repeatY ? 0 : 1; + + // Create a transparent bitmap 1 pixel wider and/or taller than the + // original, then copy the orignal into it. + // FIXME: Is there a better way to pad (not scale) an image in skia? + SkBitmap bm2; + bm2.setConfig(bm->config(), bm->width() + expandW, bm->height() + expandH); + bm2.allocPixels(); + bm2.eraseARGB(0x00, 0x00, 0x00, 0x00); + SkCanvas canvas(bm2); + canvas.drawBitmap(*bm, 0, 0); + return SkShader::CreateBitmapShader(bm2, tileModeX, tileModeY); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp new file mode 100644 index 0000000..60dbbe0 --- /dev/null +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -0,0 +1,427 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "GraphicsContext.h" +#include "NativeImageSkia.h" +#include "PlatformContextSkia.h" +#include "SkiaUtils.h" + +#include "skia/ext/image_operations.h" +#include "skia/ext/platform_canvas.h" + +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkDashPathEffect.h" + +#include <wtf/MathExtras.h> + +#if defined(__linux__) +#include "GdkSkia.h" +#endif + +// State ----------------------------------------------------------------------- + +// Encapsulates the additional painting state information we store for each +// pushed graphics state. +struct PlatformContextSkia::State { + State(); + State(const State&); + ~State(); + + // Common shader state. + float m_alpha; + SkPorterDuff::Mode m_porterDuffMode; + SkShader* m_gradient; + SkShader* m_pattern; + bool m_useAntialiasing; + SkDrawLooper* m_looper; + + // Fill. + SkColor m_fillColor; + + // Stroke. + WebCore::StrokeStyle m_strokeStyle; + SkColor m_strokeColor; + float m_strokeThickness; + int m_dashRatio; // Ratio of the length of a dash to its width. + float m_miterLimit; + SkPaint::Cap m_lineCap; + SkPaint::Join m_lineJoin; + SkDashPathEffect* m_dash; + + // Text. (See cTextFill & friends in GraphicsContext.h.) + int m_textDrawingMode; + + // Helper function for applying the state's alpha value to the given input + // color to produce a new output color. + SkColor applyAlpha(SkColor) const; + +private: + // Not supported. + void operator=(const State&); +}; + +// Note: Keep theses default values in sync with GraphicsContextState. +PlatformContextSkia::State::State() + : m_alpha(1) + , m_porterDuffMode(SkPorterDuff::kSrcOver_Mode) + , m_gradient(0) + , m_pattern(0) + , m_useAntialiasing(true) + , m_looper(0) + , m_fillColor(0xFF000000) + , m_strokeStyle(WebCore::SolidStroke) + , m_strokeColor(WebCore::Color::black) + , m_strokeThickness(0) + , m_dashRatio(3) + , m_miterLimit(4) + , m_lineCap(SkPaint::kDefault_Cap) + , m_lineJoin(SkPaint::kDefault_Join) + , m_dash(0) + , m_textDrawingMode(WebCore::cTextFill) +{ +} + +PlatformContextSkia::State::State(const State& other) +{ + memcpy(this, &other, sizeof(State)); + + m_looper->safeRef(); + m_dash->safeRef(); + m_gradient->safeRef(); + m_pattern->safeRef(); +} + +PlatformContextSkia::State::~State() +{ + m_looper->safeUnref(); + m_dash->safeUnref(); + m_gradient->safeUnref(); + m_pattern->safeUnref(); +} + +SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const +{ + int s = roundf(m_alpha * 256); + if (s >= 256) + return c; + if (s < 0) + return 0; + + int a = SkAlphaMul(SkColorGetA(c), s); + return (c & 0x00FFFFFF) | (a << 24); +} + +// PlatformContextSkia --------------------------------------------------------- + +// Danger: canvas can be NULL. +PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) + : m_canvas(canvas) + , m_stateStack(sizeof(State)) +{ + m_stateStack.append(State()); + m_state = &m_stateStack.last(); +#if defined(OS_LINUX) + m_gdkskia = m_canvas ? gdk_skia_new(m_canvas) : 0; +#endif +} + +PlatformContextSkia::~PlatformContextSkia() +{ +#if defined(OS_LINUX) + if (m_gdkskia) { + g_object_unref(m_gdkskia); + m_gdkskia = 0; + } +#endif +} + +void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) +{ + m_canvas = canvas; +} + +void PlatformContextSkia::save() +{ + m_stateStack.append(*m_state); + m_state = &m_stateStack.last(); + + // Save our native canvas. + canvas()->save(); +} + +void PlatformContextSkia::restore() +{ + m_stateStack.removeLast(); + m_state = &m_stateStack.last(); + + // Restore our native canvas. + canvas()->restore(); +} + +void PlatformContextSkia::drawRect(SkRect rect) +{ + SkPaint paint; + int fillcolorNotTransparent = m_state->m_fillColor & 0xFF000000; + if (fillcolorNotTransparent) { + setupPaintForFilling(&paint); + canvas()->drawRect(rect, paint); + } + + if (m_state->m_strokeStyle != WebCore::NoStroke && + (m_state->m_strokeColor & 0xFF000000)) { + if (fillcolorNotTransparent) { + // This call is expensive so don't call it unnecessarily. + paint.reset(); + } + setupPaintForStroking(&paint, &rect, 0); + canvas()->drawRect(rect, paint); + } +} + +void PlatformContextSkia::setupPaintCommon(SkPaint* paint) const +{ +#ifdef SK_DEBUGx + { + SkPaint defaultPaint; + SkASSERT(*paint == defaultPaint); + } +#endif + + paint->setAntiAlias(m_state->m_useAntialiasing); + paint->setPorterDuffXfermode(m_state->m_porterDuffMode); + paint->setLooper(m_state->m_looper); + + if (m_state->m_gradient) + paint->setShader(m_state->m_gradient); + else if (m_state->m_pattern) + paint->setShader(m_state->m_pattern); +} + +void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const +{ + setupPaintCommon(paint); + paint->setColor(m_state->applyAlpha(m_state->m_fillColor)); +} + +float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const +{ + setupPaintCommon(paint); + float width = m_state->m_strokeThickness; + + // This allows dashing and dotting to work properly for hairline strokes. + if (width == 0) + width = 1; + + paint->setColor(m_state->applyAlpha(m_state->m_strokeColor)); + paint->setStyle(SkPaint::kStroke_Style); + paint->setStrokeWidth(SkFloatToScalar(width)); + paint->setStrokeCap(m_state->m_lineCap); + paint->setStrokeJoin(m_state->m_lineJoin); + paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit)); + + if (rect != 0 && (static_cast<int>(roundf(width)) & 1)) + rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); + + if (m_state->m_dash) + paint->setPathEffect(m_state->m_dash); + else { + switch (m_state->m_strokeStyle) { + case WebCore::NoStroke: + case WebCore::SolidStroke: + break; + case WebCore::DashedStroke: + width = m_state->m_dashRatio * width; + // Fall through. + case WebCore::DottedStroke: + SkScalar dashLength; + if (length) { + // Determine about how many dashes or dots we should have. + int numDashes = length / roundf(width); + if (!(numDashes & 1)) + numDashes++; // Make it odd so we end on a dash/dot. + // Use the number of dashes to determine the length of a + // dash/dot, which will be approximately width + dashLength = SkScalarDiv(SkIntToScalar(length), SkIntToScalar(numDashes)); + } else + dashLength = SkFloatToScalar(width); + SkScalar intervals[2] = { dashLength, dashLength }; + paint->setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref(); + } + } + + return width; +} + +void PlatformContextSkia::setDrawLooper(SkDrawLooper* dl) +{ + SkRefCnt_SafeAssign(m_state->m_looper, dl); +} + +void PlatformContextSkia::setMiterLimit(float ml) +{ + m_state->m_miterLimit = ml; +} + +void PlatformContextSkia::setAlpha(float alpha) +{ + m_state->m_alpha = alpha; +} + +void PlatformContextSkia::setLineCap(SkPaint::Cap lc) +{ + m_state->m_lineCap = lc; +} + +void PlatformContextSkia::setLineJoin(SkPaint::Join lj) +{ + m_state->m_lineJoin = lj; +} + +void PlatformContextSkia::setPorterDuffMode(SkPorterDuff::Mode pdm) +{ + m_state->m_porterDuffMode = pdm; +} + +void PlatformContextSkia::setFillColor(SkColor color) +{ + m_state->m_fillColor = color; +} + +SkDrawLooper* PlatformContextSkia::getDrawLooper() const +{ + return m_state->m_looper; +} + +WebCore::StrokeStyle PlatformContextSkia::getStrokeStyle() const +{ + return m_state->m_strokeStyle; +} + +void PlatformContextSkia::setStrokeStyle(WebCore::StrokeStyle strokeStyle) +{ + m_state->m_strokeStyle = strokeStyle; +} + +void PlatformContextSkia::setStrokeColor(SkColor strokeColor) +{ + m_state->m_strokeColor = strokeColor; +} + +float PlatformContextSkia::getStrokeThickness() const +{ + return m_state->m_strokeThickness; +} + +void PlatformContextSkia::setStrokeThickness(float thickness) +{ + m_state->m_strokeThickness = thickness; +} + +int PlatformContextSkia::getTextDrawingMode() const +{ + return m_state->m_textDrawingMode; +} + +void PlatformContextSkia::setTextDrawingMode(int mode) +{ + // cTextClip is never used, so we assert that it isn't set: + // https://bugs.webkit.org/show_bug.cgi?id=21898 + ASSERT((mode & WebCore::cTextClip) == 0); + m_state->m_textDrawingMode = mode; +} + +void PlatformContextSkia::setUseAntialiasing(bool enable) +{ + m_state->m_useAntialiasing = enable; +} + +SkColor PlatformContextSkia::fillColor() const +{ + return m_state->m_fillColor; +} + +void PlatformContextSkia::beginPath() +{ + m_path.reset(); +} + +void PlatformContextSkia::addPath(const SkPath& path) +{ + m_path.addPath(path); +} + +void PlatformContextSkia::setFillRule(SkPath::FillType fr) +{ + m_path.setFillType(fr); +} + +void PlatformContextSkia::setGradient(SkShader* gradient) +{ + if (gradient != m_state->m_gradient) { + m_state->m_gradient->safeUnref(); + m_state->m_gradient = gradient; + } +} + +void PlatformContextSkia::setPattern(SkShader* pattern) +{ + if (pattern != m_state->m_pattern) { + m_state->m_pattern->safeUnref(); + m_state->m_pattern = pattern; + } +} + +void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash) +{ + if (dash != m_state->m_dash) { + m_state->m_dash->safeUnref(); + m_state->m_dash = dash; + } +} + +void PlatformContextSkia::paintSkPaint(const SkRect& rect, + const SkPaint& paint) +{ + m_canvas->drawRect(rect, paint); +} + +const SkBitmap* PlatformContextSkia::bitmap() const +{ + return &m_canvas->getDevice()->accessBitmap(false); +} + +bool PlatformContextSkia::isPrinting() +{ + return m_canvas->getTopPlatformDevice().IsVectorial(); +} diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h new file mode 100644 index 0000000..78e9a19 --- /dev/null +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -0,0 +1,173 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 PlatformContextSkia_h +#define PlatformContextSkia_h + +#include "GraphicsContext.h" +#include "Noncopyable.h" + +#include "SkDashPathEffect.h" +#include "SkDrawLooper.h" +#include "SkDeque.h" +#include "skia/ext/platform_canvas.h" +#include "SkPaint.h" +#include "SkPath.h" + +#include <wtf/Vector.h> + +typedef struct _GdkDrawable GdkSkia; + +// This class holds the platform-specific state for GraphicsContext. We put +// most of our Skia wrappers on this class. In theory, a lot of this stuff could +// be moved to GraphicsContext directly, except that some code external to this +// would like to poke at our graphics layer as well (like the Image and Font +// stuff, which needs some amount of our wrappers and state around SkCanvas). +// +// So in general, this class uses just Skia types except when there's no easy +// conversion. GraphicsContext is responsible for converting the WebKit types to +// Skia types and setting up the eventual call to the Skia functions. +// +// This class then keeps track of all the current Skia state. WebKit expects +// that the graphics state that is pushed and popped by save() and restore() +// includes things like colors and pen styles. Skia does this differently, where +// push and pop only includes transforms and bitmaps, and the application is +// responsible for managing the painting state which is store in separate +// SkPaint objects. This class provides the adaptor that allows the painting +// state to be pushed and popped along with the bitmap. +class PlatformContextSkia : Noncopyable { +public: + // For printing, there shouldn't be any canvas. canvas can be NULL. If you + // supply a NULL canvas, you can also call setCanvas later. + PlatformContextSkia(skia::PlatformCanvas*); + ~PlatformContextSkia(); + + // Sets the canvas associated with this context. Use when supplying NULL + // to the constructor. + void setCanvas(skia::PlatformCanvas*); + + void save(); + void restore(); + + // Sets up the common flags on a paint for antialiasing, effects, etc. + // This is implicitly called by setupPaintFill and setupPaintStroke, but + // you may wish to call it directly sometimes if you don't want that other + // behavior. + void setupPaintCommon(SkPaint*) const; + + // Sets up the paint for the current fill style. + void setupPaintForFilling(SkPaint*) const; + + // Sets up the paint for stroking. Returns an int representing the width of + // the pen, or 1 if the pen's width is 0 if a non-zero length is provided, + // the number of dashes/dots on a dashed/dotted line will be adjusted to + // start and end that length with a dash/dot. + float setupPaintForStroking(SkPaint*, SkRect*, int length) const; + + // State setting functions. + void setDrawLooper(SkDrawLooper*); // Note: takes an additional ref. + void setMiterLimit(float); + void setAlpha(float); + void setLineCap(SkPaint::Cap); + void setLineJoin(SkPaint::Join); + void setFillRule(SkPath::FillType); + void setPorterDuffMode(SkPorterDuff::Mode); + void setFillColor(SkColor); + void setStrokeStyle(WebCore::StrokeStyle); + void setStrokeColor(SkColor); + void setStrokeThickness(float thickness); + void setTextDrawingMode(int mode); + void setUseAntialiasing(bool enable); + void setGradient(SkShader*); + void setPattern(SkShader*); + void setDashPathEffect(SkDashPathEffect*); + + SkDrawLooper* getDrawLooper() const; + WebCore::StrokeStyle getStrokeStyle() const; + float getStrokeThickness() const; + int getTextDrawingMode() const; + + void beginPath(); + void addPath(const SkPath&); + const SkPath* currentPath() const { return &m_path; } + + SkColor fillColor() const; + + skia::PlatformCanvas* canvas() { return m_canvas; } + + // FIXME: This should be pushed down to GraphicsContext. + void drawRect(SkRect rect); + + // FIXME: I'm still unsure how I will serialize this call. + void paintSkPaint(const SkRect&, const SkPaint&); + + const SkBitmap* bitmap() const; + + // Returns the canvas used for painting, NOT guaranteed to be non-NULL. + // + // Warning: This function is deprecated so the users are reminded that they + // should use this layer of indirection instead of using the canvas + // directly. This is to help with the eventual serialization. + skia::PlatformCanvas* canvas() const; + + // Returns if the context is a printing context instead of a display + // context. Bitmap shouldn't be resampled when printing to keep the best + // possible quality. + bool isPrinting(); + +#if defined(__linux__) + // FIXME: should be camelCase. + GdkSkia* gdk_skia() const { return m_gdkskia; } +#endif + +private: + // Defines drawing style. + struct State; + + // NULL indicates painting is disabled. Never delete this object. + skia::PlatformCanvas* m_canvas; + + // States stack. Enables local drawing state change with save()/restore() + // calls. + WTF::Vector<State> m_stateStack; + // Pointer to the current drawing state. This is a cached value of + // mStateStack.back(). + State* m_state; + + // Current path. + SkPath m_path; + +#if defined(__linux__) + // A pointer to a GDK Drawable wrapping of this Skia canvas + GdkSkia* m_gdkskia; +#endif +}; + +#endif // PlatformContextSkia_h diff --git a/WebCore/platform/graphics/skia/PlatformGraphics.h b/WebCore/platform/graphics/skia/PlatformGraphics.h new file mode 100644 index 0000000..4ae8835 --- /dev/null +++ b/WebCore/platform/graphics/skia/PlatformGraphics.h @@ -0,0 +1,37 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 PlatformGraphics_h +#define PlatformGraphics_h + +typedef class SkShader* PlatformGradient; +typedef class SkShader* PlatformPattern; + +#endif // PlatformGraphics_h diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.cpp b/WebCore/platform/graphics/skia/SkiaFontWin.cpp new file mode 100644 index 0000000..6e79a7e --- /dev/null +++ b/WebCore/platform/graphics/skia/SkiaFontWin.cpp @@ -0,0 +1,218 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "SkiaFontWin.h" + +#include "SkCanvas.h" +#include "SkPaint.h" + +#include <wtf/ListHashSet.h> +#include <wtf/Vector.h> + +namespace WebCore { + +struct CachedOutlineKey { + CachedOutlineKey() : font(0), glyph(0), path(0) {} + CachedOutlineKey(HFONT f, WORD g) : font(f), glyph(g), path(0) {} + + HFONT font; + WORD glyph; + + // The lifetime of this pointer is managed externally to this class. Be sure + // to call DeleteOutline to remove items. + SkPath* path; +}; + +const bool operator==(const CachedOutlineKey& a, const CachedOutlineKey& b) +{ + return a.font == b.font && a.glyph == b.glyph; +} + +struct CachedOutlineKeyHash { + static unsigned hash(const CachedOutlineKey& key) + { + unsigned keyBytes; + memcpy(&keyBytes, &key.font, sizeof(unsigned)); + return keyBytes + key.glyph; + } + + static unsigned equal(const CachedOutlineKey& a, const CachedOutlineKey& b) + { + return a.font == b.font && a.glyph == b.glyph; + } + + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +typedef ListHashSet<CachedOutlineKey, CachedOutlineKeyHash> OutlineCache; + +// FIXME: Convert from static constructor to accessor function. WebCore tries to +// avoid global constructors to save on start-up time. +static OutlineCache outlineCache; + +// The global number of glyph outlines we'll cache. +static const int outlineCacheSize = 256; + +static SkScalar FIXEDToSkScalar(FIXED fixed) +{ + SkFixed skFixed; + memcpy(&skFixed, &fixed, sizeof(SkFixed)); + return SkFixedToScalar(skFixed); +} + +// Removes the given key from the cached outlines, also deleting the path. +static void deleteOutline(OutlineCache::iterator deleteMe) +{ + delete deleteMe->path; + outlineCache.remove(deleteMe); +} + +static void addPolyCurveToPath(const TTPOLYCURVE* polyCurve, SkPath* path) +{ + switch (polyCurve->wType) { + case TT_PRIM_LINE: + for (WORD i = 0; i < polyCurve->cpfx; i++) { + path->lineTo(FIXEDToSkScalar(polyCurve->apfx[i].x), -FIXEDToSkScalar(polyCurve->apfx[i].y)); + } + break; + + case TT_PRIM_QSPLINE: + // FIXME: doesn't this duplicate points if we do the loop > once? + for (WORD i = 0; i < polyCurve->cpfx - 1; i++) { + SkScalar bx = FIXEDToSkScalar(polyCurve->apfx[i].x); + SkScalar by = FIXEDToSkScalar(polyCurve->apfx[i].y); + + SkScalar cx = FIXEDToSkScalar(polyCurve->apfx[i + 1].x); + SkScalar cy = FIXEDToSkScalar(polyCurve->apfx[i + 1].y); + if (i < polyCurve->cpfx - 2) { + // We're not the last point, compute C. + cx = SkScalarAve(bx, cx); + cy = SkScalarAve(by, cy); + } + + // Need to flip the y coordinates since the font's coordinate system is + // flipped from ours vertically. + path->quadTo(bx, -by, cx, -cy); + } + break; + + case TT_PRIM_CSPLINE: + // FIXME + break; + } +} + +// The size of the glyph path buffer. +static const int glyphPathBufferSize = 4096; + +// Fills the given SkPath with the outline for the given glyph index. The font +// currently selected into the given DC is used. Returns true on success. +static bool getPathForGlyph(HDC dc, WORD glyph, SkPath* path) +{ + char buffer[glyphPathBufferSize]; + GLYPHMETRICS gm; + MAT2 mat = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; // Each one is (fract,value). + + DWORD totalSize = GetGlyphOutlineW(dc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, + &gm, glyphPathBufferSize, buffer, &mat); + if (totalSize == GDI_ERROR) + return false; + + const char* curGlyph = buffer; + const char* endGlyph = &buffer[totalSize]; + while (curGlyph < endGlyph) { + const TTPOLYGONHEADER* polyHeader = + reinterpret_cast<const TTPOLYGONHEADER*>(curGlyph); + path->moveTo(FIXEDToSkScalar(polyHeader->pfxStart.x), + -FIXEDToSkScalar(polyHeader->pfxStart.y)); + + const char* curPoly = curGlyph + sizeof(TTPOLYGONHEADER); + const char* endPoly = curGlyph + polyHeader->cb; + while (curPoly < endPoly) { + const TTPOLYCURVE* polyCurve = + reinterpret_cast<const TTPOLYCURVE*>(curPoly); + addPolyCurveToPath(polyCurve, path); + curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx; + } + curGlyph += polyHeader->cb; + } + + path->close(); + return true; +} + +// Returns a SkPath corresponding to the give glyph in the given font. The font +// should be selected into the given DC. The returned path is owned by the +// hashtable. Returns 0 on error. +const SkPath* SkiaWinOutlineCache::lookupOrCreatePathForGlyph(HDC hdc, HFONT font, WORD glyph) +{ + CachedOutlineKey key(font, glyph); + OutlineCache::iterator found = outlineCache.find(key); + if (found != outlineCache.end()) { + // Keep in MRU order by removing & reinserting the value. + key = *found; + outlineCache.remove(found); + outlineCache.add(key); + return key.path; + } + + key.path = new SkPath; + if (!getPathForGlyph(hdc, glyph, key.path)) + return 0; + + if (outlineCache.size() > outlineCacheSize) + // The cache is too big, find the oldest value (first in the list). + deleteOutline(outlineCache.begin()); + + outlineCache.add(key); + return key.path; +} + + +void SkiaWinOutlineCache::removePathsForFont(HFONT hfont) +{ + // ListHashSet isn't the greatest structure for deleting stuff out of, but + // removing entries will be relatively rare (we don't remove fonts much, nor + // do we draw out own glyphs using these routines much either). + // + // We keep a list of all glyphs we're removing which we do in a separate + // pass. + Vector<CachedOutlineKey> outlinesToDelete; + for (OutlineCache::iterator i = outlineCache.begin(); + i != outlineCache.end(); ++i) + outlinesToDelete.append(*i); + + for (Vector<CachedOutlineKey>::iterator i = outlinesToDelete.begin(); + i != outlinesToDelete.end(); ++i) + deleteOutline(outlineCache.find(*i)); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.h b/WebCore/platform/graphics/skia/SkiaFontWin.h new file mode 100644 index 0000000..2adab39 --- /dev/null +++ b/WebCore/platform/graphics/skia/SkiaFontWin.h @@ -0,0 +1,54 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 SkiaWinOutlineCache_h +#define SkiaWinOutlineCache_h + +#include <windows.h> + +class SkPath; + +namespace WebCore { + +// FIXME: Rename file to SkiaWinOutlineCache +class SkiaWinOutlineCache { +public: + static const SkPath* lookupOrCreatePathForGlyph(HDC, HFONT, WORD); + // Removes any cached glyphs from the outline cache corresponding to the + // given font handle. + static void removePathsForFont(HFONT); + +private: + SkiaWinOutlineCache(); +}; + +} // namespace WebCore + +#endif // SkiaWinOutlineCache_h diff --git a/WebCore/platform/graphics/skia/SkiaUtils.cpp b/WebCore/platform/graphics/skia/SkiaUtils.cpp new file mode 100644 index 0000000..6d9ffe2 --- /dev/null +++ b/WebCore/platform/graphics/skia/SkiaUtils.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2006,2007,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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "SkiaUtils.h" + +#include "ImageBuffer.h" +#include "SharedBuffer.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkMatrix.h" +#include "SkRegion.h" + +namespace WebCore { + +static const struct CompositOpToPorterDuffMode { + uint8_t mCompositOp; + uint8_t mPorterDuffMode; +} gMapCompositOpsToPorterDuffModes[] = { + { CompositeClear, SkPorterDuff::kClear_Mode }, + { CompositeCopy, SkPorterDuff::kSrcOver_Mode }, // TODO + { CompositeSourceOver, SkPorterDuff::kSrcOver_Mode }, + { CompositeSourceIn, SkPorterDuff::kSrcIn_Mode }, + { CompositeSourceOut, SkPorterDuff::kSrcOut_Mode }, + { CompositeSourceAtop, SkPorterDuff::kSrcATop_Mode }, + { CompositeDestinationOver, SkPorterDuff::kDstOver_Mode }, + { CompositeDestinationIn, SkPorterDuff::kDstIn_Mode }, + { CompositeDestinationOut, SkPorterDuff::kDstOut_Mode }, + { CompositeDestinationAtop, SkPorterDuff::kDstATop_Mode }, + { CompositeXOR, SkPorterDuff::kXor_Mode }, + { CompositePlusDarker, SkPorterDuff::kDarken_Mode }, + { CompositeHighlight, SkPorterDuff::kSrcOver_Mode }, // TODO + { CompositePlusLighter, SkPorterDuff::kLighten_Mode } +}; + +SkPorterDuff::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op) +{ + const CompositOpToPorterDuffMode* table = gMapCompositOpsToPorterDuffModes; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToPorterDuffModes); i++) { + if (table[i].mCompositOp == op) + return (SkPorterDuff::Mode)table[i].mPorterDuffMode; + } + + SkDEBUGF(("GraphicsContext::setCompositeOperation uknown CompositeOperator %d\n", op)); + return SkPorterDuff::kSrcOver_Mode; // fall-back +} + +static U8CPU InvScaleByte(U8CPU component, uint32_t scale) +{ + SkASSERT(component == (uint8_t)component); + return (component * scale + 0x8000) >> 16; +} + +SkColor SkPMColorToColor(SkPMColor pm) +{ + if (0 == pm) + return 0; + + unsigned a = SkGetPackedA32(pm); + uint32_t scale = (255 << 16) / a; + + return SkColorSetARGB(a, + InvScaleByte(SkGetPackedR32(pm), scale), + InvScaleByte(SkGetPackedG32(pm), scale), + InvScaleByte(SkGetPackedB32(pm), scale)); +} + +Color SkPMColorToWebCoreColor(SkPMColor pm) +{ + return SkPMColorToColor(pm); +} + +void IntersectRectAndRegion(const SkRegion& region, const SkRect& srcRect, SkRect* destRect) { + // The cliperator requires an int rect, so we round out. + SkIRect srcRectRounded; + srcRect.roundOut(&srcRectRounded); + + // The Cliperator will iterate over a bunch of rects where our transformed + // rect and the clipping region (which may be non-square) overlap. + SkRegion::Cliperator cliperator(region, srcRectRounded); + if (cliperator.done()) { + destRect->setEmpty(); + return; + } + + // Get the union of all visible rects in the clip that overlap our bitmap. + SkIRect currentVisibleRect = cliperator.rect(); + cliperator.next(); + while (!cliperator.done()) { + currentVisibleRect.join(cliperator.rect()); + cliperator.next(); + } + + destRect->set(currentVisibleRect); +} + +void ClipRectToCanvas(const SkCanvas& canvas, const SkRect& srcRect, SkRect* destRect) { + // Translate into the canvas' coordinate space. This is where the clipping + // region applies. + SkRect transformedSrc; + canvas.getTotalMatrix().mapRect(&transformedSrc, srcRect); + + // Do the intersection. + SkRect transformedDest; + IntersectRectAndRegion(canvas.getTotalClip(), transformedSrc, &transformedDest); + + // Now transform it back into world space. + SkMatrix inverseTransform; + canvas.getTotalMatrix().invert(&inverseTransform); + inverseTransform.mapRect(destRect, transformedDest); +} + +bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath::FillType ft) +{ + SkRegion rgn; + SkRegion clip; + + SkPath::FillType originalFillType = originalPath->getFillType(); + + const SkPath* path = originalPath; + SkPath scaledPath; + int scale = 1; + + SkRect bounds; + originalPath->computeBounds(&bounds, SkPath::kFast_BoundsType); + + // We can immediately return false if the point is outside the bounding rect + if (!bounds.contains(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()))) + return false; + + originalPath->setFillType(ft); + + // Skia has trouble with coordinates close to the max signed 16-bit values + // If we have those, we need to scale. + // + // TODO: remove this code once Skia is patched to work properly with large + // values + const SkScalar kMaxCoordinate = SkIntToScalar(1<<15); + SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); + + if (biggestCoord > kMaxCoordinate) { + scale = SkScalarCeil(SkScalarDiv(biggestCoord, kMaxCoordinate)); + + SkMatrix m; + m.setScale(SkScalarInvert(SkIntToScalar(scale)), SkScalarInvert(SkIntToScalar(scale))); + originalPath->transform(m, &scaledPath); + path = &scaledPath; + } + + int x = static_cast<int>(floorf(point.x() / scale)); + int y = static_cast<int>(floorf(point.y() / scale)); + clip.setRect(x, y, x + 1, y + 1); + + bool contains = rgn.setPath(*path, clip); + + originalPath->setFillType(originalFillType); + return contains; +} + +GraphicsContext* scratchContext() +{ + static ImageBuffer* scratch = 0; + if (!scratch) + scratch = ImageBuffer::create(IntSize(1, 1), false).release(); + // We don't bother checking for failure creating the ImageBuffer, since our + // ImageBuffer initializer won't fail. + return scratch->context(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/skia/SkiaUtils.h b/WebCore/platform/graphics/skia/SkiaUtils.h new file mode 100644 index 0000000..0e68574 --- /dev/null +++ b/WebCore/platform/graphics/skia/SkiaUtils.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006,2007,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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +// All of the functions in this file should move to new homes and this file should be deleted. + +#ifndef SkiaUtils_h +#define SkiaUtils_h + +#include <wtf/MathExtras.h> +#include "GraphicsContext.h" +#include "SkPath.h" +#include "SkPorterDuff.h" + +class SkCanvas; +class SkRegion; + +namespace WebCore { + +SkPorterDuff::Mode WebCoreCompositeToSkiaComposite(CompositeOperator); + +// move this guy into SkColor.h +SkColor SkPMColorToColor(SkPMColor); + +// This should be an operator on Color +Color SkPMColorToWebCoreColor(SkPMColor); + +// Skia has problems when passed infinite, etc floats, filter them to 0. +inline SkScalar WebCoreFloatToSkScalar(float f) +{ + return SkFloatToScalar(isfinite(f) ? f : 0); +} + +inline SkScalar WebCoreDoubleToSkScalar(double d) +{ + return SkDoubleToScalar(isfinite(d) ? d : 0); +} + +// Computes the smallest rectangle that, which when drawn to the given canvas, +// will cover the same area as the source rectangle. It will clip to the canvas' +// clip, doing the necessary coordinate transforms. +// +// srcRect and destRect can be the same. +void ClipRectToCanvas(const SkCanvas&, const SkRect& srcRect, SkRect* destRect); + +// Determine if a given WebKit point is contained in a path +bool SkPathContainsPoint(SkPath*, const FloatPoint&, SkPath::FillType); + +// Returns a statically allocated 1x1 GraphicsContext intended for temporary +// operations. Please save() the state and restore() it when you're done with +// the context. +GraphicsContext* scratchContext(); + +} // namespace WebCore + +#endif // SkiaUtils_h diff --git a/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp new file mode 100644 index 0000000..1e2a194 --- /dev/null +++ b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp @@ -0,0 +1,222 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "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 THE COPYRIGHT +// OWNER 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 "TransformationMatrix.h" + +#include "FloatRect.h" +#include "IntRect.h" + +#include "SkiaUtils.h" + +namespace WebCore { + +TransformationMatrix::TransformationMatrix() +{ + m_transform.reset(); +} + +TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double e, double f) +{ + setMatrix(a, b, c, d, e, f); +} + +TransformationMatrix::TransformationMatrix(const SkMatrix& matrix) + : m_transform(matrix) +{ +} + +void TransformationMatrix::setMatrix(double a, double b, double c, double d, double e, double f) +{ + m_transform.reset(); + + m_transform.setScaleX(WebCoreDoubleToSkScalar(a)); + m_transform.setSkewX(WebCoreDoubleToSkScalar(c)); + m_transform.setTranslateX(WebCoreDoubleToSkScalar(e)); + + m_transform.setScaleY(WebCoreDoubleToSkScalar(d)); + m_transform.setSkewY(WebCoreDoubleToSkScalar(b)); + m_transform.setTranslateY(WebCoreDoubleToSkScalar(f)); +} + +void TransformationMatrix::map(double x, double y, double* x2, double* y2) const +{ + SkPoint src, dst; + src.set(WebCoreDoubleToSkScalar(x), WebCoreDoubleToSkScalar(y)); + m_transform.mapPoints(&dst, &src, 1); + + *x2 = SkScalarToDouble(dst.fX); + *y2 = SkScalarToDouble(dst.fY); +} + +IntRect TransformationMatrix::mapRect(const IntRect& src) const +{ + SkRect dst; + m_transform.mapRect(&dst, src); + return enclosingIntRect(dst); +} + +FloatRect TransformationMatrix::mapRect(const FloatRect& src) const +{ + SkRect dst; + m_transform.mapRect(&dst, src); + return dst; +} + +bool TransformationMatrix::isIdentity() const +{ + return m_transform.isIdentity(); +} + +void TransformationMatrix::reset() +{ + m_transform.reset(); +} + +TransformationMatrix &TransformationMatrix::scale(double sx, double sy) +{ + m_transform.preScale(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0); + return *this; +} + +TransformationMatrix &TransformationMatrix::rotate(double d) +{ + m_transform.preRotate(WebCoreDoubleToSkScalar(d), 0, 0); + return *this; +} + +TransformationMatrix &TransformationMatrix::translate(double tx, double ty) +{ + m_transform.preTranslate(WebCoreDoubleToSkScalar(tx), WebCoreDoubleToSkScalar(ty)); + return *this; +} + +TransformationMatrix &TransformationMatrix::shear(double sx, double sy) +{ + m_transform.preSkew(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0); + return *this; +} + +double TransformationMatrix::det() const +{ + return SkScalarToDouble(m_transform.getScaleX()) * SkScalarToDouble(m_transform.getScaleY()) - + SkScalarToDouble(m_transform.getSkewY()) * SkScalarToDouble(m_transform.getSkewX()); +} + +TransformationMatrix TransformationMatrix::inverse() const +{ + TransformationMatrix inverse; + m_transform.invert(&inverse.m_transform); + return inverse; +} + +TransformationMatrix::operator SkMatrix() const +{ + return m_transform; +} + +bool TransformationMatrix::operator==(const TransformationMatrix& m2) const +{ + return m_transform == m2.m_transform; +} + +TransformationMatrix &TransformationMatrix::operator*=(const TransformationMatrix& m2) +{ + m_transform.setConcat(m2.m_transform, m_transform); + return *this; +} + +TransformationMatrix TransformationMatrix::operator*(const TransformationMatrix& m2) +{ + TransformationMatrix cat; + cat.m_transform.setConcat(m2.m_transform, m_transform); + return cat; +} + +double TransformationMatrix::a() const +{ + return SkScalarToDouble(m_transform.getScaleX()); +} + +void TransformationMatrix::setA(double a) +{ + m_transform.setScaleX(WebCoreDoubleToSkScalar(a)); +} + +double TransformationMatrix::b() const +{ + return SkScalarToDouble(m_transform.getSkewY()); +} + +void TransformationMatrix::setB(double b) +{ + m_transform.setSkewY(WebCoreDoubleToSkScalar(b)); +} + +double TransformationMatrix::c() const +{ + return SkScalarToDouble(m_transform.getSkewX()); +} + +void TransformationMatrix::setC(double c) +{ + m_transform.setSkewX(WebCoreDoubleToSkScalar(c)); +} + +double TransformationMatrix::d() const +{ + return SkScalarToDouble(m_transform.getScaleY()); +} + +void TransformationMatrix::setD(double d) +{ + m_transform.setScaleY(WebCoreDoubleToSkScalar(d)); +} + +double TransformationMatrix::e() const +{ + return SkScalarToDouble(m_transform.getTranslateX()); +} + +void TransformationMatrix::setE(double e) +{ + m_transform.setTranslateX(WebCoreDoubleToSkScalar(e)); +} + +double TransformationMatrix::f() const +{ + return SkScalarToDouble(m_transform.getTranslateY()); +} + +void TransformationMatrix::setF(double f) +{ + m_transform.setTranslateY(WebCoreDoubleToSkScalar(f)); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/IdentityTransformOperation.h b/WebCore/platform/graphics/transforms/IdentityTransformOperation.h new file mode 100644 index 0000000..347737c --- /dev/null +++ b/WebCore/platform/graphics/transforms/IdentityTransformOperation.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef IdentityTransformOperation_h +#define IdentityTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class IdentityTransformOperation : public TransformOperation { +public: + static PassRefPtr<IdentityTransformOperation> create() + { + return adoptRef(new IdentityTransformOperation()); + } + +private: + virtual bool isIdentity() const { return true; } + virtual OperationType getOperationType() const { return IDENTITY; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == IDENTITY; } + + virtual bool operator==(const TransformOperation& o) const + { + return isSameType(o); + } + + virtual bool apply(TransformationMatrix&, const IntSize&) const + { + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation*, double, bool = false) + { + return this; + } + + IdentityTransformOperation() + { + } + +}; + +} // namespace WebCore + +#endif // IdentityTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp b/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp new file mode 100644 index 0000000..153d96d --- /dev/null +++ b/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "MatrixTransformOperation.h" + +#include <algorithm> + +namespace WebCore { + +PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + // convert the TransformOperations into matrices + IntSize size; + TransformationMatrix fromT; + TransformationMatrix toT(m_a, m_b, m_c, m_d, m_e, m_f); + if (from) { + const MatrixTransformOperation* m = static_cast<const MatrixTransformOperation*>(from); + fromT.setMatrix(m->m_a, m->m_b, m->m_c, m->m_d, m->m_e, m->m_f); + } + + if (blendToIdentity) + std::swap(fromT, toT); + + toT.blend(fromT, progress); + return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f()); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/MatrixTransformOperation.h b/WebCore/platform/graphics/transforms/MatrixTransformOperation.h new file mode 100644 index 0000000..d272229 --- /dev/null +++ b/WebCore/platform/graphics/transforms/MatrixTransformOperation.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef MatrixTransformOperation_h +#define MatrixTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class MatrixTransformOperation : public TransformOperation { +public: + static PassRefPtr<MatrixTransformOperation> create(double a, double b, double c, double d, double e, double f) + { + return adoptRef(new MatrixTransformOperation(a, b, c, d, e, f)); + } + +private: + virtual bool isIdentity() const { return m_a == 1 && m_b == 0 && m_c == 0 && m_d == 1 && m_e == 0 && m_f == 0; } + virtual OperationType getOperationType() const { return MATRIX; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == MATRIX; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + + const MatrixTransformOperation* m = static_cast<const MatrixTransformOperation*>(&o); + return m_a == m->m_a && m_b == m->m_b && m_c == m->m_c && m_d == m->m_d && m_e == m->m_e && m_f == m->m_f; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize&) const + { + TransformationMatrix matrix(m_a, m_b, m_c, m_d, m_e, m_f); + transform = matrix * transform; + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + + MatrixTransformOperation(double a, double b, double c, double d, double e, double f) + : m_a(a) + , m_b(b) + , m_c(c) + , m_d(d) + , m_e(e) + , m_f(f) + { + } + + double m_a; + double m_b; + double m_c; + double m_d; + double m_e; + double m_f; +}; + +} // namespace WebCore + +#endif // MatrixTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp b/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp new file mode 100644 index 0000000..4887cee --- /dev/null +++ b/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RotateTransformOperation.h" + +namespace WebCore { + +PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + if (blendToIdentity) + return RotateTransformOperation::create(m_angle - m_angle * progress, m_type); + + const RotateTransformOperation* fromOp = static_cast<const RotateTransformOperation*>(from); + double fromAngle = fromOp ? fromOp->m_angle : 0; + return RotateTransformOperation::create(fromAngle + (m_angle - fromAngle) * progress, m_type); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/RotateTransformOperation.h b/WebCore/platform/graphics/transforms/RotateTransformOperation.h new file mode 100644 index 0000000..adc6c4c --- /dev/null +++ b/WebCore/platform/graphics/transforms/RotateTransformOperation.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RotateTransformOperation_h +#define RotateTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class RotateTransformOperation : public TransformOperation { +public: + static PassRefPtr<RotateTransformOperation> create(double angle, OperationType type) + { + return adoptRef(new RotateTransformOperation(angle, type)); + } + + virtual bool isIdentity() const { return m_angle == 0; } + virtual OperationType getOperationType() const { return m_type; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + const RotateTransformOperation* r = static_cast<const RotateTransformOperation*>(&o); + return m_angle == r->m_angle; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize& /*borderBoxSize*/) const + { + transform.rotate(m_angle); + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + + double angle() const { return m_angle; } + +private: + RotateTransformOperation(double angle, OperationType type) + : m_angle(angle) + , m_type(type) + { + } + + double m_angle; + OperationType m_type; +}; + +} // namespace WebCore + +#endif // RotateTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp b/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp new file mode 100644 index 0000000..49a8fd8 --- /dev/null +++ b/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "ScaleTransformOperation.h" + +namespace WebCore { + +PassRefPtr<TransformOperation> ScaleTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + if (blendToIdentity) + return ScaleTransformOperation::create(m_x + (1. - m_x) * progress, m_y + (1. - m_y) * progress, m_type); + + const ScaleTransformOperation* fromOp = static_cast<const ScaleTransformOperation*>(from); + double fromX = fromOp ? fromOp->m_x : 1.; + double fromY = fromOp ? fromOp->m_y : 1.; + return ScaleTransformOperation::create(fromX + (m_x - fromX) * progress, fromY + (m_y - fromY) * progress, m_type); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/ScaleTransformOperation.h b/WebCore/platform/graphics/transforms/ScaleTransformOperation.h new file mode 100644 index 0000000..289f8a1 --- /dev/null +++ b/WebCore/platform/graphics/transforms/ScaleTransformOperation.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef ScaleTransformOperation_h +#define ScaleTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class ScaleTransformOperation : public TransformOperation { +public: + static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, OperationType type) + { + return adoptRef(new ScaleTransformOperation(sx, sy, type)); + } + +private: + virtual bool isIdentity() const { return m_x == 1 && m_y == 1; } + virtual OperationType getOperationType() const { return m_type; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + const ScaleTransformOperation* s = static_cast<const ScaleTransformOperation*>(&o); + return m_x == s->m_x && m_y == s->m_y; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize&) const + { + transform.scale(m_x, m_y); + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + + ScaleTransformOperation(double sx, double sy, OperationType type) + : m_x(sx) + , m_y(sy) + , m_type(type) + { + } + + double m_x; + double m_y; + OperationType m_type; +}; + +} // namespace WebCore + +#endif // ScaleTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp b/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp new file mode 100644 index 0000000..2a430e9 --- /dev/null +++ b/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "SkewTransformOperation.h" + +namespace WebCore { + +PassRefPtr<TransformOperation> SkewTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + if (blendToIdentity) + return SkewTransformOperation::create(m_angleX - m_angleX * progress, m_angleY - m_angleY * progress, m_type); + + const SkewTransformOperation* fromOp = static_cast<const SkewTransformOperation*>(from); + double fromAngleX = fromOp ? fromOp->m_angleX : 0; + double fromAngleY = fromOp ? fromOp->m_angleY : 0; + return SkewTransformOperation::create(fromAngleX + (m_angleX - fromAngleX) * progress, fromAngleY + (m_angleY - fromAngleY) * progress, m_type); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/SkewTransformOperation.h b/WebCore/platform/graphics/transforms/SkewTransformOperation.h new file mode 100644 index 0000000..6343710 --- /dev/null +++ b/WebCore/platform/graphics/transforms/SkewTransformOperation.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef SkewTransformOperation_h +#define SkewTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class SkewTransformOperation : public TransformOperation { +public: + static PassRefPtr<SkewTransformOperation> create(double angleX, double angleY, OperationType type) + { + return adoptRef(new SkewTransformOperation(angleX, angleY, type)); + } + +private: + virtual bool isIdentity() const { return m_angleX == 0 && m_angleY == 0; } + virtual OperationType getOperationType() const { return m_type; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + const SkewTransformOperation* s = static_cast<const SkewTransformOperation*>(&o); + return m_angleX == s->m_angleX && m_angleY == s->m_angleY; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize&) const + { + transform.skew(m_angleX, m_angleY); + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + + SkewTransformOperation(double angleX, double angleY, OperationType type) + : m_angleX(angleX) + , m_angleY(angleY) + , m_type(type) + { + } + + double m_angleX; + double m_angleY; + OperationType m_type; +}; + +} // namespace WebCore + +#endif // SkewTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/TransformOperation.h b/WebCore/platform/graphics/transforms/TransformOperation.h new file mode 100644 index 0000000..65a0def --- /dev/null +++ b/WebCore/platform/graphics/transforms/TransformOperation.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef TransformOperation_h +#define TransformOperation_h + +#include "TransformationMatrix.h" +#include "IntSize.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +// CSS Transforms (may become part of CSS3) + +class TransformOperation : public RefCounted<TransformOperation> { +public: + enum OperationType { + SCALE_X, SCALE_Y, SCALE, + TRANSLATE_X, TRANSLATE_Y, TRANSLATE, + ROTATE, + SKEW_X, SKEW_Y, SKEW, + MATRIX, IDENTITY, NONE + }; + + virtual ~TransformOperation() { } + + virtual bool operator==(const TransformOperation&) const = 0; + bool operator!=(const TransformOperation& o) const { return !(*this == o); } + + virtual bool isIdentity() const = 0; + + virtual bool apply(TransformationMatrix&, const IntSize& borderBoxSize) const = 0; + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) = 0; + + virtual OperationType getOperationType() const = 0; + virtual bool isSameType(const TransformOperation&) const { return false; } +}; + +} // namespace WebCore + +#endif // TransformOperation_h diff --git a/WebCore/platform/graphics/transforms/TransformOperations.cpp b/WebCore/platform/graphics/transforms/TransformOperations.cpp new file mode 100644 index 0000000..3d71480 --- /dev/null +++ b/WebCore/platform/graphics/transforms/TransformOperations.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "TransformOperations.h" + +#include "IdentityTransformOperation.h" + +namespace WebCore { + +TransformOperations::TransformOperations(bool makeIdentity) +{ + if (makeIdentity) + m_operations.append(IdentityTransformOperation::create()); +} + +bool TransformOperations::operator==(const TransformOperations& o) const +{ + if (m_operations.size() != o.m_operations.size()) + return false; + + unsigned s = m_operations.size(); + for (unsigned i = 0; i < s; i++) { + if (*m_operations[i] != *o.m_operations[i]) + return false; + } + + return true; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/TransformOperations.h b/WebCore/platform/graphics/transforms/TransformOperations.h new file mode 100644 index 0000000..f929417 --- /dev/null +++ b/WebCore/platform/graphics/transforms/TransformOperations.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef TransformOperations_h +#define TransformOperations_h + +#include "TransformOperation.h" +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class TransformOperations { +public: + TransformOperations(bool makeIdentity = false); + + bool operator==(const TransformOperations& o) const; + bool operator!=(const TransformOperations& o) const + { + return !(*this == o); + } + + void apply(const IntSize& sz, TransformationMatrix& t) const + { + for (unsigned i = 0; i < m_operations.size(); ++i) + m_operations[i]->apply(t, sz); + } + + Vector<RefPtr<TransformOperation> >& operations() { return m_operations; } + const Vector<RefPtr<TransformOperation> >& operations() const { return m_operations; } + +private: + Vector<RefPtr<TransformOperation> > m_operations; +}; + +} // namespace WebCore + +#endif // TransformOperations_h diff --git a/WebCore/platform/graphics/AffineTransform.cpp b/WebCore/platform/graphics/transforms/TransformationMatrix.cpp index fdeba44..b48d572 100644 --- a/WebCore/platform/graphics/AffineTransform.cpp +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.cpp @@ -24,18 +24,19 @@ */ #include "config.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatRect.h" +#include "FloatQuad.h" #include "IntRect.h" #include <wtf/MathExtras.h> namespace WebCore { -static void affineTransformDecompose(const AffineTransform& matrix, double sr[9]) +static void affineTransformDecompose(const TransformationMatrix& matrix, double sr[9]) { - AffineTransform m(matrix); + TransformationMatrix m(matrix); // Compute scaling factors double sx = sqrt(m.a() * m.a() + m.b() * m.b()); @@ -73,7 +74,7 @@ static void affineTransformDecompose(const AffineTransform& matrix, double sr[9] sr[7] = m.e(); sr[8] = m.f(); } -static void affineTransformCompose(AffineTransform& m, const double sr[9]) +static void affineTransformCompose(TransformationMatrix& m, const double sr[9]) { m.setA(sr[3]); m.setB(sr[4]); @@ -85,74 +86,74 @@ static void affineTransformCompose(AffineTransform& m, const double sr[9]) m.scale(sr[0], sr[1]); } -bool AffineTransform::isInvertible() const +bool TransformationMatrix::isInvertible() const { return det() != 0.0; } -AffineTransform& AffineTransform::multiply(const AffineTransform& other) +TransformationMatrix& TransformationMatrix::multiply(const TransformationMatrix& other) { return (*this) *= other; } -AffineTransform& AffineTransform::scale(double s) +TransformationMatrix& TransformationMatrix::scale(double s) { return scale(s, s); } -AffineTransform& AffineTransform::scaleNonUniform(double sx, double sy) +TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy) { return scale(sx, sy); } -AffineTransform& AffineTransform::rotateFromVector(double x, double y) +TransformationMatrix& TransformationMatrix::rotateFromVector(double x, double y) { return rotate(rad2deg(atan2(y, x))); } -AffineTransform& AffineTransform::flipX() +TransformationMatrix& TransformationMatrix::flipX() { return scale(-1.0f, 1.0f); } -AffineTransform& AffineTransform::flipY() +TransformationMatrix& TransformationMatrix::flipY() { return scale(1.0f, -1.0f); } -AffineTransform& AffineTransform::skew(double angleX, double angleY) +TransformationMatrix& TransformationMatrix::skew(double angleX, double angleY) { return shear(tan(deg2rad(angleX)), tan(deg2rad(angleY))); } -AffineTransform& AffineTransform::skewX(double angle) +TransformationMatrix& TransformationMatrix::skewX(double angle) { return shear(tan(deg2rad(angle)), 0.0f); } -AffineTransform& AffineTransform::skewY(double angle) +TransformationMatrix& TransformationMatrix::skewY(double angle) { return shear(0.0f, tan(deg2rad(angle))); } -AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest) +TransformationMatrix makeMapBetweenRects(const FloatRect& source, const FloatRect& dest) { - AffineTransform transform; + TransformationMatrix transform; transform.translate(dest.x() - source.x(), dest.y() - source.y()); transform.scale(dest.width() / source.width(), dest.height() / source.height()); return transform; } -IntPoint AffineTransform::mapPoint(const IntPoint& point) const +IntPoint TransformationMatrix::mapPoint(const IntPoint& point) const { double x2, y2; map(point.x(), point.y(), &x2, &y2); - + // Round the point. return IntPoint(lround(x2), lround(y2)); } -FloatPoint AffineTransform::mapPoint(const FloatPoint& point) const +FloatPoint TransformationMatrix::mapPoint(const FloatPoint& point) const { double x2, y2; map(point.x(), point.y(), &x2, &y2); @@ -160,7 +161,17 @@ FloatPoint AffineTransform::mapPoint(const FloatPoint& point) const return FloatPoint(static_cast<float>(x2), static_cast<float>(y2)); } -void AffineTransform::blend(const AffineTransform& from, double progress) +FloatQuad TransformationMatrix::mapQuad(const FloatQuad& quad) const +{ + // FIXME: avoid 4 seperate library calls. Point mapping really needs + // to be platform-independent code. + return FloatQuad(mapPoint(quad.p1()), + mapPoint(quad.p2()), + mapPoint(quad.p3()), + mapPoint(quad.p4())); +} + +void TransformationMatrix::blend(const TransformationMatrix& from, double progress) { double srA[9], srB[9]; diff --git a/WebCore/platform/graphics/AffineTransform.h b/WebCore/platform/graphics/transforms/TransformationMatrix.h index 37c6033..db121d1 100644 --- a/WebCore/platform/graphics/AffineTransform.h +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.h @@ -20,31 +20,28 @@ * 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. */ -#ifndef AffineTransform_h -#define AffineTransform_h +#ifndef TransformationMatrix_h +#define TransformationMatrix_h #if PLATFORM(CG) #include <CoreGraphics/CGAffineTransform.h> -typedef CGAffineTransform PlatformAffineTransform; +typedef CGAffineTransform PlatformTransformationMatrix; #elif PLATFORM(QT) #include <QMatrix> -typedef QMatrix PlatformAffineTransform; +typedef QMatrix PlatformTransformationMatrix; #elif PLATFORM(CAIRO) #include <cairo.h> -typedef cairo_matrix_t PlatformAffineTransform; -#elif PLATFORM(SGL) +typedef cairo_matrix_t PlatformTransformationMatrix; +#elif PLATFORM(SKIA) || PLATFORM(SGL) #include "SkMatrix.h" -typedef SkMatrix PlatformAffineTransform; -#elif PLATFORM(SKIA) -#include "SkMatrix.h" -typedef SkMatrix PlatformAffineTransform; +typedef SkMatrix PlatformTransformationMatrix; #elif PLATFORM(WX) && USE(WXGC) #include <wx/defs.h> #include <wx/graphics.h> -typedef wxGraphicsMatrix PlatformAffineTransform; +typedef wxGraphicsMatrix PlatformTransformationMatrix; #endif namespace WebCore { @@ -53,13 +50,14 @@ class IntPoint; class IntRect; class FloatPoint; class FloatRect; +class FloatQuad; -class AffineTransform { +class TransformationMatrix { public: - AffineTransform(); - AffineTransform(double a, double b, double c, double d, double e, double f); + TransformationMatrix(); + TransformationMatrix(double a, double b, double c, double d, double e, double f); #if !PLATFORM(WX) || USE(WXGC) - AffineTransform(const PlatformAffineTransform&); + TransformationMatrix(const PlatformTransformationMatrix&); #endif void setMatrix(double a, double b, double c, double d, double e, double f); @@ -76,6 +74,8 @@ public: FloatRect mapRect(const FloatRect&) const; + FloatQuad mapQuad(const FloatQuad&) const; + bool isIdentity() const; double a() const; @@ -98,43 +98,43 @@ public: void reset(); - AffineTransform& multiply(const AffineTransform&); - AffineTransform& scale(double); - AffineTransform& scale(double sx, double sy); - AffineTransform& scaleNonUniform(double sx, double sy); - AffineTransform& rotate(double d); - AffineTransform& rotateFromVector(double x, double y); - AffineTransform& translate(double tx, double ty); - AffineTransform& shear(double sx, double sy); - AffineTransform& flipX(); - AffineTransform& flipY(); - AffineTransform& skew(double angleX, double angleY); - AffineTransform& skewX(double angle); - AffineTransform& skewY(double angle); - + TransformationMatrix& multiply(const TransformationMatrix&); + TransformationMatrix& scale(double); + TransformationMatrix& scale(double sx, double sy); + TransformationMatrix& scaleNonUniform(double sx, double sy); + TransformationMatrix& rotate(double d); + TransformationMatrix& rotateFromVector(double x, double y); + TransformationMatrix& translate(double tx, double ty); + TransformationMatrix& shear(double sx, double sy); + TransformationMatrix& flipX(); + TransformationMatrix& flipY(); + TransformationMatrix& skew(double angleX, double angleY); + TransformationMatrix& skewX(double angle); + TransformationMatrix& skewY(double angle); + double det() const; bool isInvertible() const; - AffineTransform inverse() const; + TransformationMatrix inverse() const; - void blend(const AffineTransform& from, double progress); + void blend(const TransformationMatrix& from, double progress); #if !PLATFORM(WX) || USE(WXGC) - operator PlatformAffineTransform() const; + operator PlatformTransformationMatrix() const; #endif - bool operator==(const AffineTransform&) const; - bool operator!=(const AffineTransform& other) const { return !(*this == other); } - AffineTransform& operator*=(const AffineTransform&); - AffineTransform operator*(const AffineTransform&); - + bool operator==(const TransformationMatrix&) const; + bool operator!=(const TransformationMatrix& other) const { return !(*this == other); } + TransformationMatrix& operator*=(const TransformationMatrix&); + TransformationMatrix operator*(const TransformationMatrix&); + private: #if !PLATFORM(WX) || USE(WXGC) - PlatformAffineTransform m_transform; + PlatformTransformationMatrix m_transform; #endif }; -AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest); +TransformationMatrix makeMapBetweenRects(const FloatRect& source, const FloatRect& dest); } // namespace WebCore -#endif // AffineTransform_h +#endif // TransformationMatrix_h diff --git a/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp b/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp new file mode 100644 index 0000000..47471c4 --- /dev/null +++ b/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "TranslateTransformOperation.h" + +namespace WebCore { + +PassRefPtr<TransformOperation> TranslateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + if (blendToIdentity) + return TranslateTransformOperation::create(Length(m_x.type()).blend(m_x, progress), Length(m_y.type()).blend(m_y, progress), m_type); + + const TranslateTransformOperation* fromOp = static_cast<const TranslateTransformOperation*>(from); + Length fromX = fromOp ? fromOp->m_x : Length(m_x.type()); + Length fromY = fromOp ? fromOp->m_y : Length(m_y.type()); + return TranslateTransformOperation::create(m_x.blend(fromX, progress), m_y.blend(fromY, progress), m_type); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/TranslateTransformOperation.h b/WebCore/platform/graphics/transforms/TranslateTransformOperation.h new file mode 100644 index 0000000..61ccbcc --- /dev/null +++ b/WebCore/platform/graphics/transforms/TranslateTransformOperation.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef TranslateTransformOperation_h +#define TranslateTransformOperation_h + +#include "Length.h" +#include "TransformOperation.h" + +namespace WebCore { + +class TranslateTransformOperation : public TransformOperation { +public: + static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, OperationType type) + { + return adoptRef(new TranslateTransformOperation(tx, ty, type)); + } + + virtual bool isIdentity() const { return m_x.calcFloatValue(1) == 0 && m_y.calcFloatValue(1) == 0; } + virtual OperationType getOperationType() const { return m_type; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + const TranslateTransformOperation* t = static_cast<const TranslateTransformOperation*>(&o); + return m_x == t->m_x && m_y == t->m_y; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize& borderBoxSize) const + { + transform.translate(m_x.calcFloatValue(borderBoxSize.width()), m_y.calcFloatValue(borderBoxSize.height())); + return m_x.type() == Percent || m_y.type() == Percent; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + +private: + TranslateTransformOperation(const Length& tx, const Length& ty, OperationType type) + : m_x(tx) + , m_y(ty) + , m_type(type) + { + } + + Length m_x; + Length m_y; + OperationType m_type; +}; + +} // namespace WebCore + +#endif // TranslateTransformOperation_h diff --git a/WebCore/platform/graphics/win/FontCGWin.cpp b/WebCore/platform/graphics/win/FontCGWin.cpp index 1766cd9..9ca95f3 100644 --- a/WebCore/platform/graphics/win/FontCGWin.cpp +++ b/WebCore/platform/graphics/win/FontCGWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ #include "config.h" #include "Font.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatConversion.h" #include "GlyphBuffer.h" #include "GraphicsContext.h" @@ -227,7 +227,7 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData XFORM xform; GetWorldTransform(hdc, &xform); - AffineTransform hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy); + TransformationMatrix hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy); CGAffineTransform initialGlyphTransform = hdcTransform.isInvertible() ? hdcTransform.inverse() : CGAffineTransformIdentity; if (font->platformData().syntheticOblique()) initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0)); @@ -294,14 +294,18 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { - if (font->m_font.useGDI()) { - drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); - return; - } - CGContextRef cgContext = graphicsContext->platformContext(); + bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); + + if (font->platformData().useGDI()) { + static bool canUsePlatformNativeGlyphs = wkCanUsePlatformNativeGlyphs(); + if (!canUsePlatformNativeGlyphs || !shouldUseFontSmoothing || (graphicsContext->textDrawingMode() & cTextStroke)) { + drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); + return; + } + } - uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, WebCoreShouldUseFontSmoothing()); + uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); const FontPlatformData& platformData = font->platformData(); @@ -322,7 +326,7 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo FloatSize translation = glyphBuffer.offsetAt(from); CGContextSetFontSize(cgContext, platformData.size()); - wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false); + wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI()); IntSize shadowSize; int shadowBlur; diff --git a/WebCore/platform/graphics/win/FontCacheWin.cpp b/WebCore/platform/graphics/win/FontCacheWin.cpp index 49b3d76..9acc5a0 100644 --- a/WebCore/platform/graphics/win/FontCacheWin.cpp +++ b/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -134,7 +134,7 @@ static const Vector<DWORD, 4>& getCJKCodePageMasks() static bool initialized; if (!initialized) { initialized = true; - IMLangFontLink2* langFontLink = FontCache::getFontLinkInterface(); + IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface(); if (!langFontLink) return codePageMasks; diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp index 26fceba..ba8afe7 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,6 +27,7 @@ #include "SharedBuffer.h" #include "SoftLinking.h" #include <ApplicationServices/ApplicationServices.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> #include <wtf/RetainPtr.h> // From t2embapi.h, which is missing from the Microsoft Platform SDK. @@ -64,7 +65,10 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b ASSERT(m_fontReference); ASSERT(T2embedLibrary()); - LOGFONT logFont; + static bool canUsePlatformNativeGlyphs = wkCanUsePlatformNativeGlyphs(); + LOGFONT _logFont; + + LOGFONT& logFont = canUsePlatformNativeGlyphs ? *static_cast<LOGFONT*>(malloc(sizeof(LOGFONT))) : _logFont; if (m_name.isNull()) TTGetNewFontName(&m_fontReference, logFont.lfFaceName, LF_FACESIZE, 0, 0); else @@ -86,6 +90,8 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b logFont.lfWeight = bold ? 700 : 400; HFONT hfont = CreateFontIndirect(&logFont); + if (canUsePlatformNativeGlyphs) + wkSetFontPlatformInfo(m_cgFont, &logFont, free); return FontPlatformData(hfont, m_cgFont, size, bold, italic, renderingMode == AlternateRenderingMode); } diff --git a/WebCore/platform/graphics/win/FontPlatformData.h b/WebCore/platform/graphics/win/FontPlatformData.h index d61afa8..09a8b55 100644 --- a/WebCore/platform/graphics/win/FontPlatformData.h +++ b/WebCore/platform/graphics/win/FontPlatformData.h @@ -45,6 +45,7 @@ public: FontPlatformData() #if PLATFORM(CAIRO) : m_fontFace(0) + , m_scaledFont(0) , #else : diff --git a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp index bbfdb9f..c7e59ab 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp @@ -2,7 +2,7 @@ * This file is part of the internal font implementation. It should not be included by anyone other than * FontMac.cpp, FontWin.cpp and Font.cpp. * - * Copyright (C) 2006, 2007, 2008 Apple Inc. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,6 +27,7 @@ #include "PlatformString.h" #include "StringHash.h" #include <ApplicationServices/ApplicationServices.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> #include <wtf/HashMap.h> #include <wtf/RetainPtr.h> #include <wtf/Vector.h> @@ -119,6 +120,11 @@ void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* ASSERT(m_cgFont); } } + if (m_useGDI && wkCanUsePlatformNativeGlyphs()) { + LOGFONT* logfont = static_cast<LOGFONT*>(malloc(sizeof(LOGFONT))); + GetObject(font, sizeof(*logfont), logfont); + wkSetFontPlatformInfo(m_cgFont.get(), logfont, free); + } } FontPlatformData::FontPlatformData(HFONT hfont, CGFontRef font, float size, bool bold, bool oblique, bool useGDI) diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index 5a4279a..dccbe6c 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "GraphicsContext.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "NotImplemented.h" #include "Path.h" @@ -173,6 +173,16 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo m_data->restore(); } +void GraphicsContext::setShouldIncludeChildWindows(bool include) +{ + m_data->m_shouldIncludeChildWindows = include; +} + +bool GraphicsContext::shouldIncludeChildWindows() const +{ + return m_data->m_shouldIncludeChildWindows; +} + GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size) : m_hdc(0) , m_size(size) @@ -222,14 +232,16 @@ GraphicsContext::WindowsBitmap* GraphicsContext::createWindowsBitmap(IntSize siz void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point) { RetainPtr<CGColorSpaceRef> deviceRGB(AdoptCF, CGColorSpaceCreateDeviceRGB()); - RetainPtr<CFDataRef> imageData(AdoptCF, CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, image->buffer(), image->bufferLength(), kCFAllocatorNull)); + // FIXME: Creating CFData is non-optimal, but needed to avoid crashing when printing. Ideally we should + // make a custom CGDataProvider that controls the WindowsBitmap lifetime. see <rdar://6394455> + RetainPtr<CFDataRef> imageData(AdoptCF, CFDataCreate(kCFAllocatorDefault, image->buffer(), image->bufferLength())); RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(imageData.get())); RetainPtr<CGImageRef> cgImage(AdoptCF, CGImageCreate(image->size().width(), image->size().height(), 8, 32, image->bytesPerRow(), deviceRGB.get(), kCGBitmapByteOrder32Little | kCGImageAlphaFirst, dataProvider.get(), 0, true, kCGRenderingIntentDefault)); CGContextDrawImage(m_data->m_cgContext, CGRectMake(point.x(), point.y(), image->size().width(), image->size().height()), cgImage.get()); } -void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) +void GraphicsContextPlatformPrivate::concatCTM(const TransformationMatrix& transform) { if (!m_hdc) return; diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index 3dcf6ba..892d24a 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "GraphicsContext.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "NotImplemented.h" #include "Path.h" @@ -102,7 +102,7 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo cairo_surface_mark_dirty(surface); } -void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) +void GraphicsContextPlatformPrivate::concatCTM(const TransformationMatrix& transform) { cairo_surface_t* surface = cairo_get_target(cr); HDC hdc = cairo_win32_surface_get_dc(surface); diff --git a/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/WebCore/platform/graphics/win/GraphicsContextWin.cpp index dbf9fad..b3ebcb0 100644 --- a/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -32,7 +32,7 @@ #include "GraphicsContextPlatformPrivateCairo.h" #endif -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "NotImplemented.h" #include "Path.h" #include <wtf/MathExtras.h> diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index cef4217..1b09e1f 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -239,8 +239,9 @@ IntSize MediaPlayerPrivate::naturalSize() const bool MediaPlayerPrivate::hasVideo() const { - // This is not used at the moment - return true; + if (!m_qtMovie) + return false; + return m_qtMovie->hasVideo(); } void MediaPlayerPrivate::setVolume(float volume) diff --git a/WebCore/platform/graphics/win/QTMovieWin.cpp b/WebCore/platform/graphics/win/QTMovieWin.cpp index 8eee41b..32aecd3 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.cpp +++ b/WebCore/platform/graphics/win/QTMovieWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -710,6 +710,14 @@ void QTMovieWin::disableUnsupportedTracks(unsigned& enabledTrackCount) } } + +bool QTMovieWin::hasVideo() const +{ + if (!m_private->m_movie) + return false; + return (GetMovieIndTrackType(m_private->m_movie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly)); +} + pascal OSErr movieDrawingCompleteProc(Movie movie, long data) { UppParam param; diff --git a/WebCore/platform/graphics/win/QTMovieWin.h b/WebCore/platform/graphics/win/QTMovieWin.h index e31780a..2186974 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.h +++ b/WebCore/platform/graphics/win/QTMovieWin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -86,6 +86,8 @@ public: void disableUnsupportedTracks(unsigned& enabledTrackCount); + bool hasVideo() const; + static unsigned countSupportedTypes(); static void getSupportedType(unsigned index, const UChar*& str, unsigned& len); diff --git a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp index 0e9f9fb..9d5c3b9 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp @@ -91,6 +91,7 @@ void SimpleFontData::platformCommonDestroy() { // We don't hash this on Win32, so it's effectively owned by us. delete m_smallCapsFontData; + m_smallCapsFontData = 0; ScriptFreeCache(&m_scriptCache); delete m_scriptFontProperties; @@ -124,7 +125,7 @@ bool SimpleFontData::containsCharacters(const UChar* characters, int length) con // FIXME: Microsoft documentation seems to imply that characters can be output using a given font and DC // merely by testing code page intersection. This seems suspect though. Can't a font only partially // cover a given code page? - IMLangFontLink2* langFontLink = FontCache::getFontLinkInterface(); + IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface(); if (!langFontLink) return false; @@ -176,6 +177,7 @@ void SimpleFontData::determinePitch() float SimpleFontData::widthForGDIGlyph(Glyph glyph) const { HDC hdc = GetDC(0); + SetGraphicsMode(hdc, GM_ADVANCED); HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); int width; GetCharWidthI(hdc, glyph, 1, 0, &width); diff --git a/WebCore/platform/graphics/wx/FontPlatformData.h b/WebCore/platform/graphics/wx/FontPlatformData.h index e3a3cce..d2394dc 100644 --- a/WebCore/platform/graphics/wx/FontPlatformData.h +++ b/WebCore/platform/graphics/wx/FontPlatformData.h @@ -81,7 +81,7 @@ public: bool operator==(const FontPlatformData& other) const { if (m_fontState == VALID) - return other.m_fontState == VALID && m_font.Ok() && other.m_font.Ok() && m_font.IsSameAs(other.m_font); + return other.m_fontState == VALID && m_font.IsOk() && other.m_font.IsOk() && m_font.IsSameAs(other.m_font); else return m_fontState == other.m_fontState; } @@ -89,7 +89,7 @@ public: bool isHashTableDeletedValue() const { return m_fontState == DELETED; } unsigned computeHash() const { - ASSERT(m_font.Ok()); + ASSERT(m_font.IsOk()); // make a hash that is unique for this font, but not globally unique - that is, // a font whose properties are equal should generate the same hash diff --git a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp index 435e7ce..59e388e 100644 --- a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp +++ b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "GraphicsContext.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatRect.h" #include "Font.h" #include "IntRect.h" @@ -284,7 +284,7 @@ void GraphicsContext::clip(const FloatRect& r) wxPoint pos(0, 0); if (windc) { -#ifndef __WXGTK__ +#if !defined(__WXGTK__) || wxCHECK_VERSION(2,9,0) wxWindow* window = windc->GetWindow(); #else wxWindow* window = windc->m_owner; @@ -349,10 +349,10 @@ void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) notImplemented(); } -AffineTransform GraphicsContext::getCTM() const +TransformationMatrix GraphicsContext::getCTM() const { notImplemented(); - return AffineTransform(); + return TransformationMatrix(); } void GraphicsContext::translate(float tx, float ty) @@ -414,7 +414,13 @@ void GraphicsContext::setURLForRect(const KURL&, const IntRect&) void GraphicsContext::setCompositeOperation(CompositeOperator op) { if (m_data->context) + { +#if wxCHECK_VERSION(2,9,0) + m_data->context->SetLogicalFunction(static_cast<wxRasterOperationMode>(getWxCompositingOperation(op, false))); +#else m_data->context->SetLogicalFunction(getWxCompositingOperation(op, false)); +#endif + } } void GraphicsContext::beginPath() @@ -455,7 +461,7 @@ void GraphicsContext::setPlatformFillColor(const Color& color) m_data->context->SetBrush(wxBrush(color)); } -void GraphicsContext::concatCTM(const AffineTransform& transform) +void GraphicsContext::concatCTM(const TransformationMatrix& transform) { if (paintingDisabled()) return; @@ -464,7 +470,7 @@ void GraphicsContext::concatCTM(const AffineTransform& transform) return; } -void GraphicsContext::setUseAntialiasing(bool enable) +void GraphicsContext::setPlatformShouldAntialias(bool enable) { if (paintingDisabled()) return; diff --git a/WebCore/platform/graphics/wx/ImageSourceWx.cpp b/WebCore/platform/graphics/wx/ImageSourceWx.cpp index 3ce4f2a..d523354 100644 --- a/WebCore/platform/graphics/wx/ImageSourceWx.cpp +++ b/WebCore/platform/graphics/wx/ImageSourceWx.cpp @@ -30,6 +30,7 @@ #include "GIFImageDecoder.h" #include "ICOImageDecoder.h" #include "JPEGImageDecoder.h" +#include "NotImplemented.h" #include "PNGImageDecoder.h" #include "SharedBuffer.h" #include "XBMImageDecoder.h" @@ -92,7 +93,7 @@ ImageSource::ImageSource() ImageSource::~ImageSource() { - delete m_decoder; + clear(true); } bool ImageSource::initialized() const @@ -141,6 +142,12 @@ int ImageSource::repetitionCount() return m_decoder->repetitionCount(); } +String ImageSource::filenameExtension() const +{ + notImplemented(); + return String(); +} + size_t ImageSource::frameCount() const { return m_decoder ? m_decoder->frameCount() : 0; @@ -152,10 +159,18 @@ bool ImageSource::frameIsCompleteAtIndex(size_t index) return (m_decoder && m_decoder->frameBufferAtIndex(index) != 0); } -void ImageSource::clear() +void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) { - delete m_decoder; + if (!destroyAll) { + if (m_decoder) + m_decoder->clearFrameBufferCache(clearBeforeFrame); + return; + } + + delete m_decoder; m_decoder = 0; + if (data) + setData(data, allDataReceived); } NativeImagePtr ImageSource::createFrameAtIndex(size_t index) @@ -205,8 +220,9 @@ NativeImagePtr ImageSource::createFrameAtIndex(size_t index) } } - +#if !wxCHECK_VERSION(2,9,0) bmp->UseAlpha(); +#endif ASSERT(bmp->IsOk()); return bmp; } diff --git a/WebCore/platform/graphics/wx/ImageWx.cpp b/WebCore/platform/graphics/wx/ImageWx.cpp index a05a31f..e52e9ff 100644 --- a/WebCore/platform/graphics/wx/ImageWx.cpp +++ b/WebCore/platform/graphics/wx/ImageWx.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "Image.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "BitmapImage.h" #include "FloatRect.h" #include "GraphicsContext.h" @@ -52,15 +52,17 @@ namespace WebCore { // this is in GraphicsContextWx.cpp int getWxCompositingOperation(CompositeOperator op, bool hasAlpha); -void FrameData::clear() +bool FrameData::clear(bool clearMetadata) { + if (clearMetadata) + m_haveMetadata = false; + if (m_frame) { delete m_frame; m_frame = 0; - // 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. + return true; } + return false; } // ================================================ @@ -95,6 +97,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatR #if USE(WXGC) wxGCDC* context = (wxGCDC*)ctxt->platformContext(); + wxGraphicsContext* gc = context->GetGraphicsContext(); #else wxWindowDC* context = ctxt->platformContext(); #endif @@ -114,7 +117,29 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatR // Set the compositing operation. ctxt->setCompositeOperation(op); + +#if USE(WXGC) + float scaleX = src.width() / dst.width(); + float scaleY = src.height() / dst.height(); + FloatRect adjustedDestRect = dst; + FloatSize selfSize = currentFrameSize(); + + if (src.size() != selfSize) { + adjustedDestRect.setLocation(FloatPoint(dst.x() - src.x() / scaleX, dst.y() - src.y() / scaleY)); + adjustedDestRect.setSize(FloatSize(selfSize.width() / scaleX, selfSize.height() / scaleY)); + } + + // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. + int currHeight = bitmap->GetHeight(); + if (currHeight < selfSize.height()) + adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height()); + + gc->PushState(); + gc->Clip(dst.x(), dst.y(), dst.width(), dst.height()); + gc->DrawBitmap(*bitmap, adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.width(), adjustedDestRect.height()); + gc->PopState(); +#else IntRect srcIntRect(src); IntRect dstIntRect(dst); bool rescaling = false; @@ -124,7 +149,8 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatR wxImage img = bitmap->ConvertToImage(); img.Rescale(dstIntRect.width(), dstIntRect.height()); bitmap = new wxBitmap(img); - } + } + wxMemoryDC mydc; ASSERT(bitmap->GetRefData()); mydc.SelectObject(*bitmap); @@ -143,10 +169,12 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatR delete bitmap; bitmap = NULL; } +#endif + ctxt->restore(); } -void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& dstRect) +void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& dstRect) { if (!m_source.initialized()) return; @@ -169,23 +197,33 @@ void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, c #if USE(WXGC) wxGraphicsContext* gc = context->GetGraphicsContext(); gc->ConcatTransform(patternTransform); -#endif - +#else wxMemoryDC mydc; mydc.SelectObject(*bitmap); +#endif - while ( currentW < dstRect.width() ) { - while ( currentH < dstRect.height() ) { + wxPoint origin(context->GetDeviceOrigin()); + wxSize clientSize(context->GetSize()); + + while ( currentW < dstRect.width() && currentW < clientSize.x - origin.x ) { + while ( currentH < dstRect.height() && currentH < clientSize.y - origin.y) { +#if USE(WXGC) + gc->DrawBitmap(*bitmap, (wxDouble)dstRect.x() + currentW, (wxDouble)dstRect.y() + currentH, (wxDouble)srcRect.width(), (wxDouble)srcRect.height()); +#else context->Blit((wxCoord)dstRect.x() + currentW, (wxCoord)dstRect.y() + currentH, (wxCoord)srcRect.width(), (wxCoord)srcRect.height(), &mydc, (wxCoord)srcRect.x(), (wxCoord)srcRect.y(), wxCOPY, true); +#endif currentH += srcRect.height(); } currentW += srcRect.width(); currentH = 0; } ctxt->restore(); + +#if !USE(WXGC) mydc.SelectObject(wxNullBitmap); +#endif // NB: delete is causing crashes during page load, but not during the deletion // itself. It occurs later on when a valid bitmap created in frameAtIndex diff --git a/WebCore/platform/graphics/wx/PathWx.cpp b/WebCore/platform/graphics/wx/PathWx.cpp index 5ff9914..60c71d5 100644 --- a/WebCore/platform/graphics/wx/PathWx.cpp +++ b/WebCore/platform/graphics/wx/PathWx.cpp @@ -26,10 +26,11 @@ #include "config.h" #include "Path.h" -#include "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatPoint.h" #include "FloatRect.h" #include "NotImplemented.h" +#include "StrokeStyleApplier.h" #include <stdio.h> @@ -64,7 +65,7 @@ Path::Path() } Path::~Path() -{ +{ } Path::Path(const Path& path) @@ -73,7 +74,16 @@ Path::Path(const Path& path) } bool Path::contains(const FloatPoint& point, const WindRule rule) const -{ +{ +#if USE(WXGC) + if (m_path) { +#if wxCHECK_VERSION(2,9,0) + return m_path->Contains(point.x(), point.y(), static_cast<wxPolygonFillMode>(getWxWindRuleForWindRule(rule))); +#else + return m_path->Contains(point.x(), point.y(), getWxWindRuleForWindRule(rule)); +#endif + } +#endif return false; } @@ -93,6 +103,12 @@ FloatRect Path::boundingRect() const return FloatRect(); } +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + notImplemented(); + return FloatRect(); +} + Path& Path::operator=(const Path&) { notImplemented(); @@ -121,60 +137,93 @@ void Path::moveTo(const FloatPoint& point) #endif } -void Path::addLineTo(const FloatPoint&) -{ - notImplemented(); +void Path::addLineTo(const FloatPoint& point) +{ +#if USE(WXGC) + if (m_path) + m_path->AddLineToPoint(point.x(), point.y()); +#endif } -void Path::addQuadCurveTo(const FloatPoint&, const FloatPoint&) -{ - notImplemented(); +void Path::addQuadCurveTo(const FloatPoint& control, const FloatPoint& end) +{ +#if USE(WXGC) + if (m_path) + m_path->AddQuadCurveToPoint(control.x(), control.y(), end.x(), end.y()); +#endif } -void Path::addBezierCurveTo(const FloatPoint&, const FloatPoint&, const FloatPoint&) -{ - notImplemented(); +void Path::addBezierCurveTo(const FloatPoint& control1, const FloatPoint& control2, const FloatPoint& end) +{ +#if USE(WXGC) + if (m_path) + m_path->AddCurveToPoint(control1.x(), control1.y(), control2.x(), control2.y(), end.x(), end.y()); +#endif } -void Path::addArcTo(const FloatPoint&, const FloatPoint&, float) -{ - notImplemented(); +void Path::addArcTo(const FloatPoint& point1, const FloatPoint& point2, float radius) +{ +#if USE(WXGC) + if (m_path) + m_path->AddArcToPoint(point1.x(), point1.y(), point2.x(), point2.y(), radius); +#endif } void Path::closeSubpath() -{ - notImplemented(); +{ +#if USE(WXGC) + if (m_path) + m_path->CloseSubpath(); +#endif } -void Path::addArc(const FloatPoint&, float, float, float, bool) -{ - notImplemented(); +void Path::addArc(const FloatPoint& point, float radius, float startAngle, float endAngle, bool clockwise) +{ +#if USE(WXGC) + if (m_path) + m_path->AddArc(point.x(), point.y(), radius, startAngle, endAngle, clockwise); +#endif } -void Path::addRect(const FloatRect&) -{ - notImplemented(); +void Path::addRect(const FloatRect& rect) +{ +#if USE(WXGC) + if (m_path) + m_path->AddRectangle(rect.x(), rect.y(), rect.width(), rect.height()); +#endif } -void Path::addEllipse(const FloatRect&) -{ - notImplemented(); +void Path::addEllipse(const FloatRect& rect) +{ +#if USE(WXGC) + if (m_path) + m_path->AddEllipse(rect.x(), rect.y(), rect.width(), rect.height()); +#endif } -void Path::transform(const AffineTransform&) -{ - notImplemented(); +void Path::transform(const TransformationMatrix& transform) +{ +#if USE(WXGC) + if (m_path) + m_path->Transform(transform); +#endif } void Path::apply(void* info, PathApplierFunction function) const -{ +{ notImplemented(); } bool Path::isEmpty() const { - notImplemented(); - return false; +#if USE(WXGC) + if (m_path) { + wxDouble width, height; + m_path->GetBox(NULL, NULL, &width, &height); + return (width == 0 && height == 0); + } +#endif + return true; } } diff --git a/WebCore/platform/graphics/wx/AffineTransformWx.cpp b/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp index 12485ae..e6a02b8 100644 --- a/WebCore/platform/graphics/wx/AffineTransformWx.cpp +++ b/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp @@ -20,11 +20,11 @@ * 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 "AffineTransform.h" +#include "TransformationMatrix.h" #include "FloatRect.h" #include "IntRect.h" @@ -37,105 +37,125 @@ namespace WebCore { #if USE(WXGC) -AffineTransform::AffineTransform(const PlatformAffineTransform& matrix) +TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) { m_transform = matrix; } #endif -AffineTransform::AffineTransform(double a, double b, double c, double d, double e, double f) +TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double e, double f) { #if USE(WXGC) - wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); + wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); m_transform = renderer->CreateMatrix(); #endif setMatrix(a, b, c, d, e, f); } -AffineTransform::AffineTransform() -{ +TransformationMatrix::TransformationMatrix() +{ // NB: If we ever support using Cairo backend on Win/Mac, this will need to be // changed somehow (though I'm not sure how as we don't have a reference to the // graphics context here. #if USE(WXGC) - wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); + wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); m_transform = renderer->CreateMatrix(); #endif } -AffineTransform AffineTransform::inverse() const +TransformationMatrix TransformationMatrix::inverse() const { notImplemented(); return *this; } -void AffineTransform::setMatrix(double a, double b, double c, double d, double e, double f) +void TransformationMatrix::setMatrix(double a, double b, double c, double d, double e, double f) { #if USE(WXGC) m_transform.Set(a, b, c, d, e, f); #endif } -void AffineTransform::map(double x, double y, double *x2, double *y2) const -{ +void TransformationMatrix::map(double x, double y, double *x2, double *y2) const +{ notImplemented(); } -IntRect AffineTransform::mapRect(const IntRect &rect) const +IntRect TransformationMatrix::mapRect(const IntRect &rect) const { - notImplemented(); +#if USE(WXGC) + double x, y, width, height; + x = rect.x(); + y = rect.y(); + width = rect.width(); + height = rect.height(); + + m_transform.TransformPoint(&x, &y); + m_transform.TransformDistance(&width, &height); + return IntRect(x, y, width, height); +#endif return IntRect(); } -FloatRect AffineTransform::mapRect(const FloatRect &rect) const +FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const { - notImplemented(); +#if USE(WXGC) + double x, y, width, height; + x = rect.x(); + y = rect.y(); + width = rect.width(); + height = rect.height(); + + m_transform.TransformPoint(&x, &y); + m_transform.TransformDistance(&width, &height); + return FloatRect(x, y, width, height); +#endif return FloatRect(); } -AffineTransform& AffineTransform::scale(double sx, double sy) +TransformationMatrix& TransformationMatrix::scale(double sx, double sy) { #if USE(WXGC) - m_transform.Scale((wxDouble)sx, (wxDouble)sy); + m_transform.Scale((wxDouble)sx, (wxDouble)sy); #endif - return *this; + return *this; } -void AffineTransform::reset() +void TransformationMatrix::reset() { notImplemented(); } -AffineTransform& AffineTransform::rotate(double d) -{ +TransformationMatrix& TransformationMatrix::rotate(double d) +{ #if USE(WXGC) - m_transform.Rotate((wxDouble)d); + m_transform.Rotate((wxDouble)d); #endif - return *this; + return *this; } -AffineTransform& AffineTransform::translate(double tx, double ty) -{ +TransformationMatrix& TransformationMatrix::translate(double tx, double ty) +{ #if USE(WXGC) - m_transform.Translate((wxDouble)tx, (wxDouble)ty); + m_transform.Translate((wxDouble)tx, (wxDouble)ty); #endif - return *this; + return *this; } -AffineTransform& AffineTransform::shear(double sx, double sy) -{ - notImplemented(); - return *this; +TransformationMatrix& TransformationMatrix::shear(double sx, double sy) +{ + notImplemented(); + return *this; } -AffineTransform& AffineTransform::operator*=(const AffineTransform& other) -{ +TransformationMatrix& TransformationMatrix::operator*=(const TransformationMatrix& other) +{ notImplemented(); - return *this; + return *this; } -bool AffineTransform::operator== (const AffineTransform &other) const +bool TransformationMatrix::operator== (const TransformationMatrix &other) const { #if USE(WXGC) return m_transform.IsEqual((wxGraphicsMatrix)other); @@ -145,26 +165,26 @@ bool AffineTransform::operator== (const AffineTransform &other) const #endif } -AffineTransform AffineTransform::operator* (const AffineTransform &other) +TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &other) { notImplemented(); return *this; //m_transform * other.m_transform; } -double AffineTransform::det() const -{ - notImplemented(); +double TransformationMatrix::det() const +{ + notImplemented(); return 0; } #if USE(WXGC) -AffineTransform::operator wxGraphicsMatrix() const +TransformationMatrix::operator wxGraphicsMatrix() const { return m_transform; } #endif -double AffineTransform::a() const +double TransformationMatrix::a() const { double a = 0; #if USE(WXGC) @@ -173,12 +193,12 @@ double AffineTransform::a() const return a; } -void AffineTransform::setA(double a) +void TransformationMatrix::setA(double a) { setMatrix(a, b(), c(), d(), e(), f()); } -double AffineTransform::b() const +double TransformationMatrix::b() const { double b = 0; #if USE(WXGC) @@ -187,12 +207,12 @@ double AffineTransform::b() const return b; } -void AffineTransform::setB(double b) +void TransformationMatrix::setB(double b) { setMatrix(a(), b, c(), d(), e(), f()); } -double AffineTransform::c() const +double TransformationMatrix::c() const { double c = 0; #if USE(WXGC) @@ -201,12 +221,12 @@ double AffineTransform::c() const return c; } -void AffineTransform::setC(double c) +void TransformationMatrix::setC(double c) { setMatrix(a(), b(), c, d(), e(), f()); } -double AffineTransform::d() const +double TransformationMatrix::d() const { double d = 0; #if USE(WXGC) @@ -215,12 +235,12 @@ double AffineTransform::d() const return d; } -void AffineTransform::setD(double d) +void TransformationMatrix::setD(double d) { setMatrix(a(), b(), c(), d, e(), f()); } -double AffineTransform::e() const +double TransformationMatrix::e() const { double e = 0; #if USE(WXGC) @@ -229,12 +249,12 @@ double AffineTransform::e() const return e; } -void AffineTransform::setE(double e) +void TransformationMatrix::setE(double e) { setMatrix(a(), b(), c(), d(), e, f()); } -double AffineTransform::f() const +double TransformationMatrix::f() const { double f = 0; #if USE(WXGC) @@ -243,7 +263,7 @@ double AffineTransform::f() const return f; } -void AffineTransform::setF(double f) +void TransformationMatrix::setF(double f) { setMatrix(a(), b(), c(), d(), e(), f); } |