diff options
Diffstat (limited to 'WebCore/platform/graphics')
202 files changed, 13276 insertions, 3615 deletions
diff --git a/WebCore/platform/graphics/AffineTransform.cpp b/WebCore/platform/graphics/AffineTransform.cpp index 664bf28..fdeba44 100644 --- a/WebCore/platform/graphics/AffineTransform.cpp +++ b/WebCore/platform/graphics/AffineTransform.cpp @@ -33,6 +33,58 @@ namespace WebCore { +static void affineTransformDecompose(const AffineTransform& matrix, double sr[9]) +{ + AffineTransform m(matrix); + + // Compute scaling factors + double sx = sqrt(m.a() * m.a() + m.b() * m.b()); + double sy = sqrt(m.c() * m.c() + m.d() * m.d()); + + /* Compute cross product of transformed unit vectors. If negative, + one axis was flipped. */ + + if (m.a() * m.d() - m.c() * m.b() < 0.0) { + // Flip axis with minimum unit vector dot product + + if (m.a() < m.d()) + sx = -sx; + else + sy = -sy; + } + + // Remove scale from matrix + + m.scale(1.0 / sx, 1.0 / sy); + + // Compute rotation + + double angle = atan2(m.b(), m.a()); + + // Remove rotation from matrix + + m.rotate(rad2deg(-angle)); + + // Return results + + sr[0] = sx; sr[1] = sy; sr[2] = angle; + sr[3] = m.a(); sr[4] = m.b(); + sr[5] = m.c(); sr[6] = m.d(); + sr[7] = m.e(); sr[8] = m.f(); +} + +static void affineTransformCompose(AffineTransform& m, const double sr[9]) +{ + m.setA(sr[3]); + m.setB(sr[4]); + m.setC(sr[5]); + m.setD(sr[6]); + m.setE(sr[7]); + m.setF(sr[8]); + m.rotate(rad2deg(sr[2])); + m.scale(sr[0], sr[1]); +} + bool AffineTransform::isInvertible() const { return det() != 0.0; @@ -83,6 +135,14 @@ AffineTransform& AffineTransform::skewY(double angle) return shear(0.0f, tan(deg2rad(angle))); } +AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest) +{ + AffineTransform 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 { double x2, y2; @@ -100,4 +160,35 @@ 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) +{ + double srA[9], srB[9]; + + affineTransformDecompose(from, srA); + affineTransformDecompose(*this, srB); + + // If x-axis of one is flipped, and y-axis of the other, convert to an unflipped rotation. + if ((srA[0] < 0.0 && srB[1] < 0.0) || (srA[1] < 0.0 && srB[0] < 0.0)) { + srA[0] = -srA[0]; + srA[1] = -srA[1]; + srA[2] += srA[2] < 0 ? piDouble : -piDouble; + } + + // Don't rotate the long way around. + srA[2] = fmod(srA[2], 2.0 * piDouble); + srB[2] = fmod(srB[2], 2.0 * piDouble); + + if (fabs(srA[2] - srB[2]) > piDouble) { + if (srA[2] > srB[2]) + srA[2] -= piDouble * 2.0; + else + srB[2] -= piDouble * 2.0; + } + + for (int i = 0; i < 9; i++) + srA[i] = srA[i] + progress * (srB[i] - srA[i]); + + affineTransformCompose(*this, srA); +} + } diff --git a/WebCore/platform/graphics/AffineTransform.h b/WebCore/platform/graphics/AffineTransform.h index c0dc72f..37c6033 100644 --- a/WebCore/platform/graphics/AffineTransform.h +++ b/WebCore/platform/graphics/AffineTransform.h @@ -28,15 +28,23 @@ #if PLATFORM(CG) #include <CoreGraphics/CGAffineTransform.h> +typedef CGAffineTransform PlatformAffineTransform; #elif PLATFORM(QT) #include <QMatrix> +typedef QMatrix PlatformAffineTransform; #elif PLATFORM(CAIRO) #include <cairo.h> +typedef cairo_matrix_t PlatformAffineTransform; #elif PLATFORM(SGL) #include "SkMatrix.h" +typedef SkMatrix PlatformAffineTransform; +#elif PLATFORM(SKIA) +#include "SkMatrix.h" +typedef SkMatrix PlatformAffineTransform; #elif PLATFORM(WX) && USE(WXGC) #include <wx/defs.h> #include <wx/graphics.h> +typedef wxGraphicsMatrix PlatformAffineTransform; #endif namespace WebCore { @@ -50,25 +58,26 @@ class AffineTransform { public: AffineTransform(); AffineTransform(double a, double b, double c, double d, double e, double f); -#if PLATFORM(CG) - AffineTransform(CGAffineTransform transform); -#elif PLATFORM(QT) - AffineTransform(const QMatrix &matrix); -#elif PLATFORM(CAIRO) - AffineTransform(const cairo_matrix_t &matrix); -#elif PLATFORM(WX) && USE(WXGC) - AffineTransform(const wxGraphicsMatrix &matrix); +#if !PLATFORM(WX) || USE(WXGC) + AffineTransform(const PlatformAffineTransform&); #endif void setMatrix(double a, double b, double c, double d, double e, double f); void map(double x, double y, double *x2, double *y2) const; + + // Rounds the mapped point to the nearest integer value. IntPoint mapPoint(const IntPoint&) const; + FloatPoint mapPoint(const FloatPoint&) const; + + // Rounds the resulting mapped rectangle out. This is helpful for bounding + // box computations but may not be what is wanted in other contexts. IntRect mapRect(const IntRect&) const; + FloatRect mapRect(const FloatRect&) const; - + bool isIdentity() const; - + double a() const; void setA(double a); @@ -107,16 +116,10 @@ public: bool isInvertible() const; AffineTransform inverse() const; -#if PLATFORM(CG) - operator CGAffineTransform() const; -#elif PLATFORM(QT) - operator QMatrix() const; -#elif PLATFORM(CAIRO) - operator cairo_matrix_t() const; -#elif PLATFORM(SGL) - operator SkMatrix() const; -#elif PLATFORM(WX) && USE(WXGC) - operator wxGraphicsMatrix() const; + void blend(const AffineTransform& from, double progress); + +#if !PLATFORM(WX) || USE(WXGC) + operator PlatformAffineTransform() const; #endif bool operator==(const AffineTransform&) const; @@ -125,19 +128,13 @@ public: AffineTransform operator*(const AffineTransform&); private: -#if PLATFORM(CG) - CGAffineTransform m_transform; -#elif PLATFORM(QT) - QMatrix m_transform; -#elif PLATFORM(CAIRO) - cairo_matrix_t m_transform; -#elif PLATFORM(SGL) - SkMatrix m_transform; -#elif PLATFORM(WX) && USE(WXGC) - wxGraphicsMatrix m_transform; +#if !PLATFORM(WX) || USE(WXGC) + PlatformAffineTransform m_transform; #endif }; +AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest); + } // namespace WebCore #endif // AffineTransform_h diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index e731ed6..4b21de0 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -31,6 +31,7 @@ #include "ImageObserver.h" #include "IntRect.h" #include "PlatformString.h" +#include "SystemTime.h" #include "Timer.h" #include <wtf/Vector.h> #include "MIMETypeRegistry.h" @@ -41,20 +42,29 @@ namespace WebCore { // 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; + BitmapImage::BitmapImage(ImageObserver* observer) : Image(observer) , m_currentFrame(0) , m_frames(0) , m_frameTimer(0) - , m_repetitionCount(0) + , m_repetitionCount(cAnimationNone) + , m_repetitionCountStatus(Unknown) , m_repetitionsComplete(0) + , m_desiredFrameStartTime(0) , m_isSolidColor(false) - , m_animatingImageType(true) , m_animationFinished(false) , m_allDataReceived(false) , m_haveSize(false) , m_sizeAvailable(false) + , m_hasUniformFrameSize(true) , m_decodedSize(0) + , m_haveFrameCount(false) + , m_frameCount(0) { initPlatformData(); } @@ -65,14 +75,15 @@ BitmapImage::~BitmapImage() stopAnimation(); } -void BitmapImage::destroyDecodedData(bool incremental) +void BitmapImage::destroyDecodedData(bool incremental, bool preserveNearbyFrames) { // 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) { + if (m_frames[i].m_frame && (!preserveNearbyFrames || (i != m_currentFrame && i != nextFrame))) { sizeChange -= frameSize; m_frames[i].clear(); } @@ -105,13 +116,6 @@ void BitmapImage::cacheFrame(size_t index) size_t numFrames = frameCount(); ASSERT(m_decodedSize == 0 || numFrames > 1); - if (!m_frames.size() && shouldAnimate()) { - // Snag the repetition count. - m_repetitionCount = m_source.repetitionCount(); - if (m_repetitionCount == cAnimationNone) - m_animatingImageType = false; - } - if (m_frames.size() < numFrames) m_frames.grow(numFrames); @@ -119,11 +123,21 @@ void BitmapImage::cacheFrame(size_t index) if (numFrames == 1 && m_frames[index].m_frame) checkForSolidColor(); - if (shouldAnimate()) + m_frames[index].m_haveMetadata = true; + m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index); + if (repetitionCount(false) != cAnimationNone) m_frames[index].m_duration = m_source.frameDurationAtIndex(index); m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index); - - int sizeChange = m_frames[index].m_frame ? m_size.width() * m_size.height() * 4 : 0; + + 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; if (imageObserver()) @@ -140,6 +154,13 @@ IntSize BitmapImage::size() const return m_size; } +IntSize BitmapImage::currentFrameSize() const +{ + if (!m_currentFrame || m_hasUniformFrameSize) + return size(); + return m_source.frameSizeAtIndex(m_currentFrame); +} + bool BitmapImage::dataChanged(bool allDataReceived) { destroyDecodedData(true); @@ -148,6 +169,11 @@ bool BitmapImage::dataChanged(bool allDataReceived) m_allDataReceived = allDataReceived; m_source.setData(m_data.get(), allDataReceived); + // Clear the frame count. + m_haveFrameCount = false; + + m_hasUniformFrameSize = true; + // Image properties will not be available until the first frame of the file // reaches kCGImageStatusIncomplete. return isSizeAvailable(); @@ -155,7 +181,11 @@ bool BitmapImage::dataChanged(bool allDataReceived) size_t BitmapImage::frameCount() { - return m_source.frameCount(); + if (!m_haveFrameCount) { + m_haveFrameCount = true; + m_frameCount = m_source.frameCount(); + } + return m_frameCount; } bool BitmapImage::isSizeAvailable() @@ -179,12 +209,23 @@ NativeImagePtr BitmapImage::frameAtIndex(size_t index) return m_frames[index].m_frame; } +bool BitmapImage::frameIsCompleteAtIndex(size_t index) +{ + if (index >= frameCount()) + return true; + + if (index >= m_frames.size() || !m_frames[index].m_haveMetadata) + cacheFrame(index); + + return m_frames[index].m_isComplete; +} + float BitmapImage::frameDurationAtIndex(size_t index) { if (index >= frameCount()) return 0; - if (index >= m_frames.size() || !m_frames[index].m_frame) + if (index >= m_frames.size() || !m_frames[index].m_haveMetadata) cacheFrame(index); return m_frames[index].m_duration; @@ -193,30 +234,124 @@ float BitmapImage::frameDurationAtIndex(size_t index) bool BitmapImage::frameHasAlphaAtIndex(size_t index) { if (index >= frameCount()) - return 0; + return true; - if (index >= m_frames.size() || !m_frames[index].m_frame) + if (index >= m_frames.size() || !m_frames[index].m_haveMetadata) cacheFrame(index); return m_frames[index].m_hasAlpha; } +int BitmapImage::repetitionCount(bool imageKnownToBeComplete) +{ + if ((m_repetitionCountStatus == Unknown) || ((m_repetitionCountStatus == Uncertain) && imageKnownToBeComplete)) { + // Snag the repetition count. If |imageKnownToBeComplete| is false, the + // repetition count may not be accurate yet for GIFs; in this case the + // decoder will default to cAnimationLoopOnce, and we'll try and read + // the count again once the whole image is decoded. + m_repetitionCount = m_source.repetitionCount(); + m_repetitionCountStatus = (imageKnownToBeComplete || m_repetitionCount == cAnimationNone) ? Certain : Uncertain; + } + return m_repetitionCount; +} + bool BitmapImage::shouldAnimate() { - return (m_animatingImageType && !m_animationFinished && imageObserver()); + return (repetitionCount(false) != cAnimationNone && !m_animationFinished && imageObserver()); } -void BitmapImage::startAnimation() +void BitmapImage::startAnimation(bool catchUpIfNecessary) { if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) return; - // Don't advance the animation until the current frame has completely loaded. - if (!m_source.frameIsCompleteAtIndex(m_currentFrame)) + // Determine time for next frame to start. By ignoring paint and timer lag + // in this calculation, we make the animation appear to run at its desired + // rate regardless of how fast it's being repainted. + const double currentDuration = frameDurationAtIndex(m_currentFrame); + const double time = currentTime(); + if (m_desiredFrameStartTime == 0) { + 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. + 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)) return; - m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimation); - m_frameTimer->startOneShot(frameDurationAtIndex(m_currentFrame)); + // Don't advance past the last frame if we haven't decoded the whole image + // yet and our repetition count is potentially unset. The repetition count + // in a GIF can potentially come after all the rest of the image data, so + // wait on it. + if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1)) + return; + + // The image may load more slowly than it's supposed to animate, so that by + // the time we reach the end of the first repetition, we're well behind. + // Clamp the desired frame start time in this case, so that we don't skip + // frames (or whole iterations) trying to "catch up". This is a tradeoff: + // It guarantees users see the whole animation the second time through and + // don't miss any repetitions, and is closer to what other browsers do; on + // the other hand, it makes animations "less accurate" for pages that try to + // sync an image and some other resource (e.g. audio), especially if users + // switch tabs (and thus stop drawing the animation, which will pause it) + // during that initial loop, then switch back later. + if (nextFrame == 0 && m_repetitionsComplete == 0 && m_desiredFrameStartTime < time) + m_desiredFrameStartTime = time; + + if (!catchUpIfNecessary || time < m_desiredFrameStartTime) { + // Haven't yet reached time for next frame to start; delay until then. + m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimation); + m_frameTimer->startOneShot(std::max(m_desiredFrameStartTime - time, 0.)); + } else { + // We've already reached or passed the time for the next frame to start. + // See if we've also passed the time for frames after that to start, in + // case we need to skip some frames entirely. Remember not to advance + // to an incomplete frame. + for (size_t frameAfterNext = (nextFrame + 1) % frameCount(); frameIsCompleteAtIndex(frameAfterNext); frameAfterNext = (nextFrame + 1) % frameCount()) { + // Should we skip the next frame? + double frameAfterNextStartTime = m_desiredFrameStartTime + frameDurationAtIndex(nextFrame); + if (time < frameAfterNextStartTime) + break; + + // Yes; skip over it without notifying our observers. + if (!internalAdvanceAnimation(true)) + return; + m_desiredFrameStartTime = frameAfterNextStartTime; + nextFrame = frameAfterNext; + } + + // Draw the next frame immediately. Note that m_desiredFrameStartTime + // may be in the past, meaning the next time through this function we'll + // kick off the next advancement sooner than this frame's duration would + // suggest. + if (internalAdvanceAnimation(false)) { + // The image region has been marked dirty, but once we return to our + // caller, draw() will clear it, and nothing will cause the + // animation to advance again. We need to start the timer for the + // next frame running, or the animation can hang. (Compare this + // with when advanceAnimation() is called, and the region is dirtied + // while draw() is not in the callstack, meaning draw() gets called + // to update the region and thus startAnimation() is reached again.) + // NOTE: For large images with slow or heavily-loaded systems, + // throwing away data as we go (see destroyDecodedData()) means we + // can spend so much time re-decoding data above that by the time we + // reach here we're behind again. If we let startAnimation() run + // the catch-up code again, we can get long delays without painting + // as we race the timer, or even infinite recursion. In this + // situation the best we can do is to simply change frames as fast + // as possible, so force startAnimation() to set a zero-delay timer + // and bail out if we're not caught up. + startAnimation(false); + } + } } void BitmapImage::stopAnimation() @@ -232,6 +367,7 @@ void BitmapImage::resetAnimation() stopAnimation(); m_currentFrame = 0; m_repetitionsComplete = 0; + m_desiredFrameStartTime = 0; m_animationFinished = false; int frameSize = m_size.width() * m_size.height() * 4; @@ -242,43 +378,61 @@ void BitmapImage::resetAnimation() void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer) { + internalAdvanceAnimation(false); + // At this point the image region has been marked dirty, and if it's + // onscreen, we'll soon make a call to draw(), which will call + // startAnimation() again to keep the animation moving. +} + +bool BitmapImage::internalAdvanceAnimation(bool skippingFrames) +{ // Stop the animation. stopAnimation(); // See if anyone is still paying attention to this animation. If not, we don't // advance and will remain suspended at the current frame until the animation is resumed. - if (imageObserver()->shouldPauseAnimation(this)) - return; + if (!skippingFrames && imageObserver()->shouldPauseAnimation(this)) + return false; m_currentFrame++; if (m_currentFrame >= frameCount()) { - m_repetitionsComplete += 1; - if (m_repetitionCount && m_repetitionsComplete >= m_repetitionCount) { + ++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--; - return; + 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 = 0; } + 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. + // 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. - destroyDecodedData(); - - // Go ahead and decode the next frame. - frameAtIndex(m_currentFrame); + // 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 do not advance the animation explicitly. We rely on a subsequent draw of the image - // to force a request for the next frame via startAnimation(). This allows images that move offscreen while - // scrolling to stop animating (thus saving memory from additional decoded frames and - // CPU time spent doing the decoding). } } diff --git a/WebCore/platform/graphics/BitmapImage.h b/WebCore/platform/graphics/BitmapImage.h index 154d829..c5f2a72 100644 --- a/WebCore/platform/graphics/BitmapImage.h +++ b/WebCore/platform/graphics/BitmapImage.h @@ -45,7 +45,8 @@ typedef struct HBITMAP__ *HBITMAP; #endif #if PLATFORM(SGL) - class SkBitmapRef; +class SkBitmap; +class SkBitmapRef; #endif namespace WebCore { @@ -70,6 +71,8 @@ template <typename T> class Timer; struct FrameData : Noncopyable { FrameData() : m_frame(0) + , m_haveMetadata(false) + , m_isComplete(false) , m_duration(0) , m_hasAlpha(true) { @@ -83,6 +86,8 @@ struct FrameData : Noncopyable { void clear(); NativeImagePtr m_frame; + bool m_haveMetadata; + bool m_isComplete; float m_duration; bool m_hasAlpha; }; @@ -92,15 +97,25 @@ struct FrameData : Noncopyable { // ================================================= class BitmapImage : public Image { + friend class GeneratedImage; friend class GraphicsContext; public: -#if PLATFORM(QT) - BitmapImage(const QPixmap &pixmap, ImageObserver* = 0); -#endif - BitmapImage(ImageObserver* = 0); + static PassRefPtr<BitmapImage> create(NativeImagePtr nativeImage, ImageObserver* observer = 0) + { + return adoptRef(new BitmapImage(nativeImage, observer)); + } + static PassRefPtr<BitmapImage> create(ImageObserver* observer = 0) + { + return adoptRef(new BitmapImage(observer)); + } ~BitmapImage(); + virtual bool isBitmapImage() const { return true; } + + virtual bool hasSingleSecurityOrigin() const { return true; } + virtual IntSize size() const; + IntSize currentFrameSize() const; virtual bool dataChanged(bool allDataReceived); @@ -122,23 +137,28 @@ public: virtual CGImageRef getCGImageRef(); #endif -#if PLATFORM(QT) - virtual QPixmap* getPixmap() const; -#endif - #if PLATFORM(WIN) virtual bool getHBITMAP(HBITMAP); virtual bool getHBITMAPOfSize(HBITMAP, LPSIZE); #endif #if PLATFORM(SGL) - virtual SkBitmapRef* getBitmap(); +// virtual SkBitmapRef* getBitmap(); virtual void setURL(const String& str); #endif virtual NativeImagePtr nativeImageForCurrentFrame() { return frameAtIndex(currentFrame()); } -private: +protected: + enum RepetitionCountStatus { + Unknown, // We haven't checked the source's repetition count. + Uncertain, // We have a repetition count, but it might be wrong (some GIFs have a count after the image data, and will report "loop once" until all data has been decoded). + Certain, // The repetition count is known to be correct. + }; + + BitmapImage(NativeImagePtr, ImageObserver* = 0); + BitmapImage(ImageObserver* = 0); + #if PLATFORM(WIN) virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator); #endif @@ -150,24 +170,45 @@ private: size_t currentFrame() const { return m_currentFrame; } size_t frameCount(); NativeImagePtr frameAtIndex(size_t); + bool frameIsCompleteAtIndex(size_t); float frameDurationAtIndex(size_t); bool frameHasAlphaAtIndex(size_t); // 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. - virtual void destroyDecodedData(bool incremental = false); + // 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); // Whether or not size is available yet. bool isSizeAvailable(); // Animation. + int repetitionCount(bool imageKnownToBeComplete); // |imageKnownToBeComplete| should be set if the caller knows the entire image has been decoded. bool shouldAnimate(); - virtual void startAnimation(); + virtual void startAnimation(bool catchUpIfNecessary = true); void advanceAnimation(Timer<BitmapImage>*); - + + // Function that does the real work of advancing the animation. When + // skippingFrames is true, we're in the middle of a loop trying to skip over + // a bunch of animation frames, so we should not do things like decode each + // one or notify our observers. + // Returns whether the animation was advanced. + bool internalAdvanceAnimation(bool skippingFrames); + + // Helper for internalAdvanceAnimation(). + void notifyObserverAndTrimDecodedData(); + // Handle platform-specific data void initPlatformData(); void invalidatePlatformData(); @@ -185,34 +226,31 @@ private: Vector<FrameData> m_frames; // An array of the cached frames of the animation. We have to ref frames to pin them in the cache. Timer<BitmapImage>* m_frameTimer; - int m_repetitionCount; // How many total animation loops we should do. + int m_repetitionCount; // How many total animation loops we should do. This will be cAnimationNone if this image type is incapable of animation. + RepetitionCountStatus m_repetitionCountStatus; int m_repetitionsComplete; // How many repetitions we've finished. + double m_desiredFrameStartTime; // The system time at which we hope to see the next call to startAnimation(). #if PLATFORM(MAC) mutable RetainPtr<NSImage> m_nsImage; // A cached NSImage of frame 0. Only built lazily if someone actually queries for one. mutable RetainPtr<CFDataRef> m_tiffRep; // Cached TIFF rep for frame 0. Only built lazily if someone queries for one. #endif -#if PLATFORM(SGL) - SkBitmapRef* m_bitmapRef; -#endif - Color m_solidColor; // If we're a 1x1 solid color, this is the color to use to fill. bool m_isSolidColor; // Whether or not we are a 1x1 solid image. - bool m_animatingImageType; // Whether or not we're an image type that is capable of animating (GIF). bool m_animationFinished; // Whether or not we've completed the entire animation. bool m_allDataReceived; // Whether or not we've received all our data. mutable bool m_haveSize; // Whether or not our |m_size| member variable has the final overall image size yet. bool m_sizeAvailable; // Whether or not we can obtain the size of the first image frame yet from ImageIO. - unsigned m_decodedSize; // The current size of all decoded frames. + mutable bool m_hasUniformFrameSize; -#if PLATFORM(QT) - QPixmap *m_pixmap; -#endif + unsigned m_decodedSize; // The current size of all decoded frames. + mutable bool m_haveFrameCount; + size_t m_frameCount; }; } diff --git a/WebCore/platform/graphics/Color.cpp b/WebCore/platform/graphics/Color.cpp index b0efff1..3ff589d 100644 --- a/WebCore/platform/graphics/Color.cpp +++ b/WebCore/platform/graphics/Color.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,6 @@ #include "config.h" #include "Color.h" -#include "DeprecatedString.h" #include "PlatformString.h" #include <math.h> #include <wtf/Assertions.h> @@ -35,6 +34,7 @@ #include "ColorData.c" using namespace std; +using namespace WTF; namespace WebCore { @@ -51,6 +51,24 @@ 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) +{ + // 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)); +} + +RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a) +{ + return colorFloatToRGBAByte(a) << 24 | colorFloatToRGBAByte(r) << 16 | colorFloatToRGBAByte(g) << 8 | colorFloatToRGBAByte(b); +} + +RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha) +{ + RGBA32 rgbOnly = color & 0x00FFFFFF; + RGBA32 rgba = rgbOnly | colorFloatToRGBAByte(overrideAlpha) << 24; + return rgba; +} + static double calcHue(double temp1, double temp2, double hueVal) { if (hueVal < 0.0) @@ -92,24 +110,26 @@ RGBA32 makeRGBAFromHSLA(double hue, double saturation, double lightness, double // originally moved here from the CSS parser bool Color::parseHexColor(const String& name, RGBA32& rgb) { - int len = name.length(); - if (len == 3 || len == 6) { - bool ok; - int val = name.deprecatedString().toInt(&ok, 16); - if (ok) { - if (len == 6) { - rgb = 0xFF000000 | val; - return true; - } - // #abc converts to #aabbcc according to the specs - rgb = 0xFF000000 - | (val & 0xF00) << 12 | (val & 0xF00) << 8 - | (val & 0xF0) << 8 | (val & 0xF0) << 4 - | (val & 0xF) << 4 | (val & 0xF); - return true; - } + unsigned length = name.length(); + if (length != 3 && length != 6) + return false; + unsigned value = 0; + for (unsigned i = 0; i < length; ++i) { + if (!isASCIIHexDigit(name[i])) + return false; + value <<= 4; + value |= toASCIIHexValue(name[i]); } - return false; + if (length == 6) { + rgb = 0xFF000000 | value; + return true; + } + // #abc converts to #aabbcc + rgb = 0xFF000000 + | (value & 0xF00) << 12 | (value & 0xF00) << 8 + | (value & 0xF0) << 8 | (value & 0xF0) << 4 + | (value & 0xF) << 4 | (value & 0xF); + return true; } int differenceSquared(const Color& c1, const Color& c2) @@ -147,10 +167,25 @@ String Color::name() const return String::format("#%02X%02X%02X", red(), green(), blue()); } +static inline const NamedColor* findNamedColor(const String& name) +{ + char buffer[64]; // easily big enough for the longest color name + unsigned length = name.length(); + if (length > sizeof(buffer) - 1) + return 0; + for (unsigned i = 0; i < length; ++i) { + UChar c = name[i]; + if (!c || c > 0x7F) + return 0; + buffer[i] = toASCIILower(static_cast<char>(c)); + } + buffer[length] = '\0'; + return findColor(buffer, length); +} + void Color::setNamedColor(const String& name) { - DeprecatedString dname = name.deprecatedString(); - const NamedColor* foundColor = dname.isAllASCII() ? findColor(dname.latin1(), dname.length()) : 0; + const NamedColor* foundColor = findNamedColor(name); m_color = foundColor ? foundColor->RGBValue : 0; m_color |= 0xFF000000; m_valid = foundColor; diff --git a/WebCore/platform/graphics/Color.h b/WebCore/platform/graphics/Color.h index 7bbca18..61fc74c 100644 --- a/WebCore/platform/graphics/Color.h +++ b/WebCore/platform/graphics/Color.h @@ -33,7 +33,10 @@ typedef struct CGColor* CGColorRef; #endif #if PLATFORM(QT) +#include <qglobal.h> +QT_BEGIN_NAMESPACE class QColor; +QT_END_NAMESPACE #endif #if PLATFORM(GTK) @@ -53,6 +56,9 @@ typedef unsigned RGBA32; // RGBA quadruplet RGBA32 makeRGB(int r, int g, int b); RGBA32 makeRGBA(int r, int g, int b, int a); + +RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha); +RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a); RGBA32 makeRGBAFromHSLA(double h, double s, double l, double a); int differenceSquared(const Color&, const Color&); @@ -63,6 +69,8 @@ public: Color(RGBA32 col) : m_color(col), m_valid(true) { } Color(int r, int g, int b) : m_color(makeRGB(r, g, b)), m_valid(true) { } Color(int r, int g, int b, int a) : m_color(makeRGBA(r, g, b, a)), m_valid(true) { } + // Color is currently limited to 32bit RGBA, perhaps some day we'll support better colors + Color(float r, float g, float b, float a) : m_color(makeRGBA32FromFloats(r, g, b, a)), m_valid(true) { } explicit Color(const String&); explicit Color(const char*); @@ -124,7 +132,7 @@ public: private: RGBA32 m_color; - bool m_valid : 1; + bool m_valid; }; inline bool operator==(const Color& a, const Color& b) @@ -138,7 +146,6 @@ inline bool operator!=(const Color& a, const Color& b) } Color focusRingColor(); -void setFocusRingColorChangeFunction(void (*)()); #if PLATFORM(CG) CGColorRef cgColor(const Color&); diff --git a/WebCore/platform/graphics/DashArray.h b/WebCore/platform/graphics/DashArray.h new file mode 100644 index 0000000..46b84a4 --- /dev/null +++ b/WebCore/platform/graphics/DashArray.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DashArray_h +#define DashArray_h + +#include <wtf/Vector.h> + +#if PLATFORM(CG) +typedef Vector<CGFloat> DashArray; +#elif PLATFORM(CAIRO) +typedef Vector<double> DashArray; +#else +typedef Vector<float> DashArray; +#endif + +#endif // DashArray_h diff --git a/WebCore/platform/graphics/FloatPoint.h b/WebCore/platform/graphics/FloatPoint.h index 7b8ba1d..6b3c769 100644 --- a/WebCore/platform/graphics/FloatPoint.h +++ b/WebCore/platform/graphics/FloatPoint.h @@ -43,13 +43,20 @@ typedef struct _NSPoint NSPoint; #endif #if PLATFORM(QT) +#include "qglobal.h" +QT_BEGIN_NAMESPACE class QPointF; +QT_END_NAMESPACE #endif #if PLATFORM(SYMBIAN) class TPoint; #endif +#if PLATFORM(SKIA) +struct SkPoint; +#endif + namespace WebCore { class AffineTransform; @@ -87,7 +94,12 @@ public: #if PLATFORM(SYMBIAN) operator TPoint() const; - FloatPoint(const TPoint& ); + FloatPoint(const TPoint&); +#endif + +#if PLATFORM(SKIA) + operator SkPoint() const; + FloatPoint(const SkPoint&); #endif FloatPoint matrixTransform(const AffineTransform&) const; diff --git a/WebCore/platform/graphics/FloatRect.cpp b/WebCore/platform/graphics/FloatRect.cpp index bb604d1..ec7b3fa 100644 --- a/WebCore/platform/graphics/FloatRect.cpp +++ b/WebCore/platform/graphics/FloatRect.cpp @@ -30,6 +30,7 @@ #include "FloatConversion.h" #include "IntRect.h" #include <algorithm> +#include <math.h> using std::max; using std::min; @@ -111,14 +112,10 @@ void FloatRect::scale(float s) IntRect enclosingIntRect(const FloatRect& rect) { - int l = static_cast<int>(rect.x()); - int t = static_cast<int>(rect.y()); - // FIXME: These two need to be a "ceiling" operation, not rounding. - // We changed them to do "+ 0.5f" to compile on Win32 where there's - // no ceilf, but they should be changed back to "ceiling" at some point - // and we should provide an implementation of ceilf for Win32. - int r = static_cast<int>(rect.right() + 0.5f); - int b = static_cast<int>(rect.bottom() + 0.5f); + int l = static_cast<int>(floorf(rect.x())); + int t = static_cast<int>(floorf(rect.y())); + int r = static_cast<int>(ceilf(rect.right())); + int b = static_cast<int>(ceilf(rect.bottom())); return IntRect(l, t, r - l, b - t); } diff --git a/WebCore/platform/graphics/FloatRect.h b/WebCore/platform/graphics/FloatRect.h index 867813d..11e3791 100644 --- a/WebCore/platform/graphics/FloatRect.h +++ b/WebCore/platform/graphics/FloatRect.h @@ -42,13 +42,19 @@ typedef struct _NSRect NSRect; #endif #if PLATFORM(QT) +QT_BEGIN_NAMESPACE class QRectF; +QT_END_NAMESPACE #endif #if PLATFORM(WX) && USE(WXGC) class wxRect2DDouble; #endif +#if PLATFORM(SKIA) +struct SkRect; +#endif + namespace WebCore { class IntRect; @@ -137,6 +143,11 @@ public: operator wxRect2DDouble() const; #endif +#if PLATFORM(SKIA) + FloatRect(const SkRect&); + operator SkRect() const; +#endif + private: FloatPoint m_location; FloatSize m_size; diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp index 82bf3b1..a78d27b 100644 --- a/WebCore/platform/graphics/Font.cpp +++ b/WebCore/platform/graphics/Font.cpp @@ -1,10 +1,8 @@ -/** - * This file is part of the html renderer for KDE. - * +/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2006 Apple Computer, Inc. + * Copyright (C) 2003, 2006 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 @@ -32,21 +30,14 @@ #include "FontFallbackList.h" #include "IntPoint.h" #include "GlyphBuffer.h" -#include <wtf/unicode/Unicode.h> +#include "WidthIterator.h" #include <wtf/MathExtras.h> -#if USE(ICU_UNICODE) -#include <unicode/unorm.h> -#endif - using namespace WTF; using namespace Unicode; namespace WebCore { -// According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values -const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8; - 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 /*?*/, @@ -58,250 +49,7 @@ const uint8_t Font::gRoundingHackCharacterTable[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -Font::CodePath Font::codePath = Auto; - -struct WidthIterator { - WidthIterator(const Font* font, const TextRun& run); - -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - bool -#else - void -#endif - advance(int to, GlyphBuffer* glyphBuffer = 0); - bool advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer = 0); - - const Font* m_font; - - const TextRun& m_run; - int m_end; - - unsigned m_currentCharacter; - float m_runWidthSoFar; - float m_padding; - float m_padPerSpace; - float m_finalRoundingWidth; - -private: - UChar32 normalizeVoicingMarks(int currentCharacter); -}; - -WidthIterator::WidthIterator(const Font* font, const TextRun& run) - : m_font(font) - , m_run(run) - , m_end(run.length()) - , m_currentCharacter(0) - , m_runWidthSoFar(0) - , m_finalRoundingWidth(0) -{ - // If the padding is non-zero, count the number of spaces in the run - // and divide that by the padding for per space addition. - m_padding = m_run.padding(); - if (!m_padding) - m_padPerSpace = 0; - else { - float numSpaces = 0; - for (int i = 0; i < run.length(); i++) - if (Font::treatAsSpace(m_run[i])) - numSpaces++; - - if (numSpaces == 0) - m_padPerSpace = 0; - else - m_padPerSpace = ceilf(m_run.padding() / numSpaces); - } -} - -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS -#define SIGNAL_ADJUSTED_WIDTHS() adjustedWidths = true -bool WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) -#else -#define SIGNAL_ADJUSTED_WIDTHS() -void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) -#endif -{ - if (offset > m_end) - offset = m_end; - - int currentCharacter = m_currentCharacter; - const UChar* cp = m_run.data(currentCharacter); - - bool rtl = m_run.rtl(); - bool hasExtraSpacing = m_font->letterSpacing() || m_font->wordSpacing() || m_padding; - - float runWidthSoFar = m_runWidthSoFar; - float lastRoundingWidth = m_finalRoundingWidth; - -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - bool adjustedWidths = false; -#endif - - while (currentCharacter < offset) { - UChar32 c = *cp; - unsigned clusterLength = 1; - if (c >= 0x3041) { - if (c <= 0x30FE) { - // Deal with Hiragana and Katakana voiced and semi-voiced syllables. - // Normalize into composed form, and then look for glyph with base + combined mark. - // Check above for character range to minimize performance impact. - UChar32 normalized = normalizeVoicingMarks(currentCharacter); - if (normalized) { - c = normalized; - clusterLength = 2; - } - } else if (U16_IS_SURROGATE(c)) { - if (!U16_IS_SURROGATE_LEAD(c)) - break; - - // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) - // code point before glyph lookup. - // Make sure we have another character and it's a low surrogate. - if (currentCharacter + 1 >= m_run.length()) - break; - UChar low = cp[1]; - if (!U16_IS_TRAIL(low)) - break; - c = U16_GET_SUPPLEMENTARY(c, low); - clusterLength = 2; - } - } - - const GlyphData& glyphData = m_font->glyphDataForCharacter(c, rtl); - Glyph glyph = glyphData.glyph; - const SimpleFontData* fontData = glyphData.fontData; - - ASSERT(fontData); - - // Now that we have a glyph and font data, get its width. - float width; - if (c == '\t' && m_run.allowTabs()) { - float tabWidth = m_font->tabWidth(); - width = tabWidth - fmodf(m_run.xPos() + runWidthSoFar, tabWidth); - SIGNAL_ADJUSTED_WIDTHS(); - } else { - width = fontData->widthForGlyph(glyph); -#ifndef ANDROID_NEVER_ROUND_FONT_METRICS - // We special case spaces in two ways when applying word rounding. - // First, we round spaces to an adjusted width in all fonts. - // Second, in fixed-pitch fonts we ensure that all characters that - // match the width of the space character have the same width as the space character. - if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding()) { - width = fontData->m_adjustedSpaceWidth; - SIGNAL_ADJUSTED_WIDTHS(); - } -#endif - } - - if (hasExtraSpacing && !m_run.spacingDisabled()) { - // Account for letter-spacing. - if (width && m_font->letterSpacing()) { - width += m_font->letterSpacing(); - SIGNAL_ADJUSTED_WIDTHS(); - } - - if (Font::treatAsSpace(c)) { - // Account for padding. WebCore uses space padding to justify text. - // We distribute the specified padding over the available spaces in the run. - if (m_padding) { - // Use left over padding if not evenly divisible by number of spaces. - if (m_padding < m_padPerSpace) { - width += m_padding; - m_padding = 0; - } else { - width += m_padPerSpace; - m_padding -= m_padPerSpace; - } - SIGNAL_ADJUSTED_WIDTHS(); - } - - // Account for word spacing. - // We apply additional space between "words" by adding width to the space character. - if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) { - width += m_font->wordSpacing(); - SIGNAL_ADJUSTED_WIDTHS(); - } - } - } - - // Advance past the character we just dealt with. - cp += clusterLength; - currentCharacter += clusterLength; - - // Account for float/integer impedance mismatch between CG and KHTML. "Words" (characters - // followed by a character defined by isRoundingHackCharacter()) are always an integer width. - // We adjust the width of the last character of a "word" to ensure an integer width. - // If we move KHTML to floats we can remove this (and related) hacks. - - float oldWidth = width; - -#ifndef ANDROID_NEVER_ROUND_FONT_METRICS - // Force characters that are used to determine word boundaries for the rounding hack - // to be integer width, so following words will start on an integer boundary. - if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c)) { - width = ceilf(width); - SIGNAL_ADJUSTED_WIDTHS(); - } - - // Check to see if the next character is a "rounding hack character", if so, adjust - // width so that the total run width will be on an integer boundary. - if ((m_run.applyWordRounding() && currentCharacter < m_run.length() && Font::isRoundingHackCharacter(*cp)) - || (m_run.applyRunRounding() && currentCharacter >= m_end)) { - float totalWidth = runWidthSoFar + width; - width += ceilf(totalWidth) - totalWidth; - SIGNAL_ADJUSTED_WIDTHS(); - } -#endif - - runWidthSoFar += width; - - if (glyphBuffer) - glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width)); - - lastRoundingWidth = width - oldWidth; - } - - m_currentCharacter = currentCharacter; - m_runWidthSoFar = runWidthSoFar; - m_finalRoundingWidth = lastRoundingWidth; - -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - return adjustedWidths; -#endif -} - -bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer) -{ - glyphBuffer->clear(); - advance(m_currentCharacter + 1, glyphBuffer); - float w = 0; - for (int i = 0; i < glyphBuffer->size(); ++i) - w += glyphBuffer->advanceAt(i); - width = w; - return !glyphBuffer->isEmpty(); -} - -UChar32 WidthIterator::normalizeVoicingMarks(int currentCharacter) -{ - if (currentCharacter + 1 < m_end) { - if (combiningClass(m_run[currentCharacter + 1]) == hiraganaKatakanaVoicingMarksCombiningClass) { -#if USE(ICU_UNICODE) - // Normalize into composed form using 3.2 rules. - UChar normalizedCharacters[2] = { 0, 0 }; - UErrorCode uStatus = U_ZERO_ERROR; - int32_t resultLength = unorm_normalize(m_run.data(currentCharacter), 2, - UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uStatus); - if (resultLength == 1 && uStatus == 0) - return normalizedCharacters[0]; -#elif USE(QT4_UNICODE) - QString tmp(reinterpret_cast<const QChar*>(m_run.data(currentCharacter)), 2); - QString res = tmp.normalized(QString::NormalizationForm_C, QChar::Unicode_3_2); - if (res.length() == 1) - return res.at(0).unicode(); -#endif - } - } - return 0; -} +Font::CodePath Font::s_codePath = Auto; // ============================================================================================ // Font Implementation (Cross-Platform Portion) @@ -327,7 +75,7 @@ Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) } Font::Font(const FontPlatformData& fontData, bool isPrinterFont) - : m_fontList(new FontFallbackList) + : m_fontList(FontFallbackList::create()) , m_pageZero(0) , m_cachedPrimaryFont(0) , m_letterSpacing(0) @@ -381,7 +129,8 @@ bool Font::operator==(const Font& other) const return first == second && m_fontDescription == other.m_fontDescription && m_letterSpacing == other.m_letterSpacing - && m_wordSpacing == other.m_wordSpacing; + && m_wordSpacing == other.m_wordSpacing + && (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 @@ -442,9 +191,12 @@ const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceS return data; GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber); - const GlyphData& data = smallCapsNode->page()->glyphDataForCharacter(c); - if (data.fontData) - return data; + 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. @@ -534,7 +286,7 @@ void Font::update(PassRefPtr<FontSelector> fontSelector) const // won't stick around long enough to get you in trouble). Still, this is pretty disgusting, // and could eventually be rectified by using RefPtrs for Fonts themselves. if (!m_fontList) - m_fontList = new FontFallbackList(); + m_fontList = FontFallbackList::create(); m_fontList->invalidate(fontSelector); m_cachedPrimaryFont = 0; m_pageZero = 0; @@ -561,6 +313,11 @@ int Font::lineSpacing() const return primaryFont()->lineSpacing(); } +int Font::lineGap() const +{ + return primaryFont()->lineGap(); +} + float Font::xHeight() const { return primaryFont()->xHeight(); @@ -584,12 +341,17 @@ bool Font::isFixedPitch() const void Font::setCodePath(CodePath p) { - codePath = p; + s_codePath = p; +} + +Font::CodePath Font::codePath() +{ + return s_codePath; } bool Font::canUseGlyphCache(const TextRun& run) const { - switch (codePath) { + switch (s_codePath) { case Auto: break; case Simple: @@ -753,6 +515,20 @@ float Font::floatWidth(const TextRun& run) const return floatWidthForComplexText(run); } +float Font::floatWidth(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const +{ +#if ENABLE(SVG_FONTS) + if (primaryFont()->isSVGFont()) + return floatWidthUsingSVGFont(run, extraCharsAvailable, charsConsumed, glyphName); +#endif + + charsConsumed = run.length(); + glyphName = ""; + if (canUseGlyphCache(run)) + return floatWidthForSimpleText(run, 0); + return floatWidthForComplexText(run); +} + float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer) const { WidthIterator it(this, run); @@ -846,6 +622,13 @@ int Font::offsetForPositionForSimpleText(const TextRun& run, int x, bool include return offset; } +#if ENABLE(SVG_FONTS) +bool Font::isSVGFont() const +{ + return primaryFont()->isSVGFont(); +} +#endif + FontSelector* Font::fontSelector() const { return m_fontList ? m_fontList->fontSelector() : 0; diff --git a/WebCore/platform/graphics/Font.h b/WebCore/platform/graphics/Font.h index 84048ed..a99ce12 100644 --- a/WebCore/platform/graphics/Font.h +++ b/WebCore/platform/graphics/Font.h @@ -1,6 +1,4 @@ /* - * This file is part of the html renderer for KDE. - * * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) @@ -26,6 +24,7 @@ #ifndef Font_h #define Font_h +#include "TextRun.h" #include "FontDescription.h" #include <wtf/HashMap.h> @@ -46,102 +45,11 @@ class GlyphBuffer; class GlyphPageTreeNode; class GraphicsContext; class IntPoint; -class RenderObject; class SimpleFontData; -class SVGPaintServer; +class SVGFontElement; struct GlyphData; -class TextRun { -public: - TextRun(const UChar* c, int len, bool allowTabs = false, int xpos = 0, int padding = 0, bool rtl = false, bool directionalOverride = false, - bool applyRunRounding = true, bool applyWordRounding = true) - : m_characters(c) - , m_len(len) - , m_allowTabs(allowTabs) - , m_xpos(xpos) - , m_padding(padding) - , m_rtl(rtl) - , m_directionalOverride(directionalOverride) - , m_applyRunRounding(applyRunRounding) - , m_applyWordRounding(applyWordRounding) - , m_disableSpacing(false) -#if ENABLE(SVG_FONTS) - , m_referencingRenderObject(0) - , m_activePaintServer(0) -#endif - { - } - - TextRun(const String& s, bool allowTabs = false, int xpos = 0, int padding = 0, bool rtl = false, bool directionalOverride = false, - bool applyRunRounding = true, bool applyWordRounding = true) - : m_characters(s.characters()) - , m_len(s.length()) - , m_allowTabs(allowTabs) - , m_xpos(xpos) - , m_padding(padding) - , m_rtl(rtl) - , m_directionalOverride(directionalOverride) - , m_applyRunRounding(applyRunRounding) - , m_applyWordRounding(applyWordRounding) - , m_disableSpacing(false) -#if ENABLE(SVG_FONTS) - , m_referencingRenderObject(0) - , m_activePaintServer(0) -#endif - { - } - - const UChar operator[](int i) const { return m_characters[i]; } - const UChar* data(int i) const { return &m_characters[i]; } - - const UChar* characters() const { return m_characters; } - int length() const { return m_len; } - - void setText(const UChar* c, int len) { m_characters = c; m_len = len; } - - bool allowTabs() const { return m_allowTabs; } - int xPos() const { return m_xpos; } - int padding() const { return m_padding; } - bool rtl() const { return m_rtl; } - bool ltr() const { return !m_rtl; } - bool directionalOverride() const { return m_directionalOverride; } - bool applyRunRounding() const { return m_applyRunRounding; } - bool applyWordRounding() const { return m_applyWordRounding; } - bool spacingDisabled() const { return m_disableSpacing; } - - void disableSpacing() { m_disableSpacing = true; } - void disableRoundingHacks() { m_applyRunRounding = m_applyWordRounding = false; } - void setRTL(bool b) { m_rtl = b; } - void setDirectionalOverride(bool override) { m_directionalOverride = override; } - -#if ENABLE(SVG_FONTS) - RenderObject* referencingRenderObject() const { return m_referencingRenderObject; } - void setReferencingRenderObject(RenderObject* object) { m_referencingRenderObject = object; } - - SVGPaintServer* activePaintServer() const { return m_activePaintServer; } - void setActivePaintServer(SVGPaintServer* object) { m_activePaintServer = object; } -#endif - -private: - const UChar* m_characters; - int m_len; - - bool m_allowTabs; - int m_xpos; - int m_padding; - bool m_rtl; - bool m_directionalOverride; - bool m_applyRunRounding; - bool m_applyWordRounding; - bool m_disableSpacing; - -#if ENABLE(SVG_FONTS) - RenderObject* m_referencingRenderObject; - SVGPaintServer* m_activePaintServer; -#endif -}; - class Font { public: Font(); @@ -170,6 +78,7 @@ public: int width(const TextRun&) const; float floatWidth(const TextRun&) const; + float floatWidth(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const; int offsetForPosition(const TextRun&, int position, bool includePartialGlyphs) const; FloatRect selectionRectForText(const TextRun&, const IntPoint&, int h, int from = 0, int to = -1) const; @@ -194,8 +103,7 @@ public: const FontFamily& family() const { return m_fontDescription.family(); } bool italic() const { return m_fontDescription.italic(); } - unsigned weight() const { return m_fontDescription.weight(); } - bool bold() const { return m_fontDescription.bold(); } + FontWeight weight() const { return m_fontDescription.weight(); } #if !PLATFORM(QT) bool isPlatformFont() const { return m_isPlatformFont; } @@ -211,6 +119,7 @@ public: int descent() const; int height() const { return ascent() + descent(); } int lineSpacing() const; + int lineGap() const; float xHeight() const; unsigned unitsPerEm() const; int spaceWidth() const; @@ -234,6 +143,7 @@ private: #if ENABLE(SVG_FONTS) void drawTextUsingSVGFont(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const; float floatWidthUsingSVGFont(const TextRun&) const; + float floatWidthUsingSVGFont(const TextRun&, int extraCharsAvailable, int& charsConsumed, String& glyphName) const; FloatRect selectionRectForTextUsingSVGFont(const TextRun&, const IntPoint&, int h, int from, int to) const; int offsetForPositionForTextUsingSVGFont(const TextRun&, int position, bool includePartialGlyphs) const; #endif @@ -250,12 +160,15 @@ private: #endif friend struct WidthIterator; - // Useful for debugging the different font rendering code paths. public: -#if !PLATFORM(QT) +#if PLATFORM(QT) + FontSelector* fontSelector() const { return 0; } +#else + // Useful for debugging the different font rendering code paths. enum CodePath { Auto, Simple, Complex }; static void setCodePath(CodePath); - static CodePath codePath; + static CodePath codePath(); + static CodePath s_codePath; static const uint8_t gRoundingHackCharacterTable[256]; static bool isRoundingHackCharacter(UChar32 c) @@ -266,8 +179,13 @@ public: FontSelector* fontSelector() const; #endif static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; } -// ANDROID: extra parentheses around expressions to suppress warnings - static bool treatAsZeroWidthSpace(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e); } + static bool treatAsZeroWidthSpace(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e) || c == 0xFFFC; } + +#if ENABLE(SVG_FONTS) + bool isSVGFont() const; + SVGFontElement* svgFont() const; +#endif + private: FontDescription m_fontDescription; #if !PLATFORM(QT) diff --git a/WebCore/platform/graphics/FontCache.cpp b/WebCore/platform/graphics/FontCache.cpp index 6abbb2e..1c5a987 100644 --- a/WebCore/platform/graphics/FontCache.cpp +++ b/WebCore/platform/graphics/FontCache.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,34 +36,43 @@ #include "FontSelector.h" #include "StringHash.h" #include <wtf/HashMap.h> +#include <wtf/ListHashSet.h> + +using namespace WTF; namespace WebCore { struct FontPlatformDataCacheKey { - FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, bool bold = false, bool italic = false, + FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false, bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode) : m_family(family) , m_size(size) - , m_bold(bold) + , m_weight(weight) , m_italic(italic) , m_printerFont(isPrinterFont) , m_renderingMode(renderingMode) { } + FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { } + bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); } + bool operator==(const FontPlatformDataCacheKey& other) const { return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size && - m_bold == other.m_bold && m_italic == other.m_italic && m_printerFont == other.m_printerFont && + m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont && m_renderingMode == other.m_renderingMode; } - + AtomicString m_family; unsigned m_size; - bool m_bold; + unsigned m_weight; bool m_italic; bool m_printerFont; FontRenderingMode m_renderingMode; + +private: + static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } }; inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) @@ -70,10 +80,10 @@ inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) unsigned hashCodes[4] = { CaseFoldingHash::hash(fontKey.m_family), fontKey.m_size, - static_cast<unsigned>(fontKey.m_bold) << 3 | static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 | - static_cast<unsigned>(fontKey.m_renderingMode) + fontKey.m_weight, + static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 | static_cast<unsigned>(fontKey.m_renderingMode) }; - return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), 4 * sizeof(unsigned) / sizeof(UChar)); + return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); } struct FontPlatformDataCacheKeyHash { @@ -92,16 +102,18 @@ struct FontPlatformDataCacheKeyHash { struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> { static const bool emptyValueIsZero = true; - static const bool needsDestruction = false; - static const FontPlatformDataCacheKey& deletedValue() + static const FontPlatformDataCacheKey& emptyValue() { - static FontPlatformDataCacheKey key(nullAtom, 0xFFFFFFFFU, false, false); + static FontPlatformDataCacheKey key(nullAtom); return key; } - static const FontPlatformDataCacheKey& emptyValue() + static void constructDeletedValue(FontPlatformDataCacheKey& slot) { - static FontPlatformDataCacheKey key(nullAtom, 0, false, false); - return key; + new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue); + } + static bool isDeletedValue(const FontPlatformDataCacheKey& value) + { + return value.isHashTableDeletedValue(); } }; @@ -144,7 +156,7 @@ FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fo platformInit(); } - FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.bold(), fontDescription.italic(), + FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(), fontDescription.usePrinterFont(), fontDescription.renderingMode()); FontPlatformData* result = 0; bool foundResult; @@ -187,38 +199,123 @@ struct FontDataCacheKeyHash { struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { static const bool emptyValueIsZero = true; - static const bool needsDestruction = false; - static const FontPlatformData& deletedValue() - { - static FontPlatformData key = FontPlatformData::Deleted(); - return key; - } + static const bool needsDestruction = true; static const FontPlatformData& emptyValue() { static FontPlatformData key; return key; } + static void constructDeletedValue(FontPlatformData& slot) + { + new (&slot) FontPlatformData(HashTableDeletedValue); + } + static bool isDeletedValue(const FontPlatformData& value) + { + return value.isHashTableDeletedValue(); + } }; -typedef HashMap<FontPlatformData, SimpleFontData*, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache; +typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache; static FontDataCache* gFontDataCache = 0; +const int cMaxInactiveFontData = 120; // Pretty Low Threshold +const float cTargetInactiveFontData = 100; +static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0; + SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData) { if (!platformData) return 0; - if (!gFontDataCache) + if (!gFontDataCache) { gFontDataCache = new FontDataCache; + gInactiveFontData = new ListHashSet<const SimpleFontData*>; + } - SimpleFontData* result = gFontDataCache->get(*platformData); - if (!result) { - result = new SimpleFontData(*platformData); - gFontDataCache->set(*platformData, result); + FontDataCache::iterator result = gFontDataCache->find(*platformData); + if (result == gFontDataCache->end()) { + pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1); + gFontDataCache->set(*platformData, newValue); + return newValue.first; } - - return result; + if (!result.get()->second.second++) { + ASSERT(gInactiveFontData->contains(result.get()->second.first)); + gInactiveFontData->remove(result.get()->second.first); + } + + return result.get()->second.first; +} + +void FontCache::releaseFontData(const SimpleFontData* fontData) +{ + ASSERT(gFontDataCache); + ASSERT(!fontData->isCustomFont()); + + FontDataCache::iterator it = gFontDataCache->find(fontData->platformData()); + ASSERT(it != gFontDataCache->end()); + + if (!--it->second.second) { + gInactiveFontData->add(fontData); + if (gInactiveFontData->size() > cMaxInactiveFontData) + purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData); + } +} + +void FontCache::purgeInactiveFontData(int count) +{ + if (!gInactiveFontData) + return; + + static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData. + if (isPurging) + return; + + isPurging = true; + + ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end(); + ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin(); + for (int i = 0; i < count && it != end; ++it, ++i) { + const SimpleFontData* fontData = *it.get(); + gFontDataCache->remove(fontData->platformData()); + delete fontData; + } + + if (it == end) { + // Removed everything + gInactiveFontData->clear(); + } else { + for (int i = 0; i < count; ++i) + gInactiveFontData->remove(gInactiveFontData->begin()); + } + + Vector<FontPlatformDataCacheKey> keysToRemove; + keysToRemove.reserveCapacity(gFontPlatformDataCache->size()); + FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); + for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { + if (platformData->second && !gFontDataCache->contains(*platformData->second)) + keysToRemove.append(platformData->first); + } + + size_t keysToRemoveCount = keysToRemove.size(); + for (size_t i = 0; i < keysToRemoveCount; ++i) + delete gFontPlatformDataCache->take(keysToRemove[i]); + + isPurging = false; +} + +size_t FontCache::fontDataCount() +{ + if (gFontDataCache) + return gFontDataCache->size(); + return 0; +} + +size_t FontCache::inactiveFontDataCount() +{ + if (gInactiveFontData) + return gInactiveFontData->size(); + return 0; } const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector) @@ -270,4 +367,59 @@ const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontS return getCachedFontData(result); } +static HashSet<FontSelector*>* gClients; + +void FontCache::addClient(FontSelector* client) +{ + if (!gClients) + gClients = new HashSet<FontSelector*>; + + ASSERT(!gClients->contains(client)); + gClients->add(client); +} + +void FontCache::removeClient(FontSelector* client) +{ + ASSERT(gClients); + ASSERT(gClients->contains(client)); + + gClients->remove(client); +} + +static unsigned gGeneration = 0; + +unsigned FontCache::generation() +{ + return gGeneration; +} + +void FontCache::invalidate() +{ + if (!gClients) { + ASSERT(!gFontPlatformDataCache); + return; + } + + if (gFontPlatformDataCache) { + deleteAllValues(*gFontPlatformDataCache); + delete gFontPlatformDataCache; + gFontPlatformDataCache = new FontPlatformDataCache; + } + + gGeneration++; + + Vector<RefPtr<FontSelector> > clients; + size_t numClients = gClients->size(); + clients.reserveCapacity(numClients); + HashSet<FontSelector*>::iterator end = gClients->end(); + for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it) + clients.append(*it); + + ASSERT(numClients == clients.size()); + for (size_t i = 0; i < numClients; ++i) + clients[i]->fontCacheInvalidated(); + + purgeInactiveFontData(); +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/FontCache.h b/WebCore/platform/graphics/FontCache.h index e1a704b..816fe64 100644 --- a/WebCore/platform/graphics/FontCache.h +++ b/WebCore/platform/graphics/FontCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2008 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,6 +29,8 @@ #ifndef FontCache_h #define FontCache_h +#include <limits.h> +#include <wtf/Vector.h> #include <wtf/unicode/Unicode.h> #if PLATFORM(WIN) @@ -50,8 +52,10 @@ class SimpleFontData; class FontCache { public: static const FontData* getFontData(const Font&, int& familyIndex, FontSelector*); + static 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); // Also implemented by the platform. @@ -61,12 +65,22 @@ public: static IMLangFontLink2* getFontLinkInterface(); #endif - static bool fontExists(const FontDescription&, const AtomicString& family); + static 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&); - + + static void addClient(FontSelector*); + static void removeClient(FontSelector*); + + static unsigned generation(); + static void invalidate(); + + static size_t fontDataCount(); + static size_t inactiveFontDataCount(); + static void purgeInactiveFontData(int count = INT_MAX); + private: // These methods are implemented by each platform. static FontPlatformData* getSimilarFontPlatformData(const Font&); diff --git a/WebCore/platform/graphics/FontData.h b/WebCore/platform/graphics/FontData.h index 352d965..cb79919 100644 --- a/WebCore/platform/graphics/FontData.h +++ b/WebCore/platform/graphics/FontData.h @@ -35,6 +35,11 @@ class SimpleFontData; class FontData : Noncopyable { public: + FontData() + : m_maxGlyphPageTreeLevel(0) + { + } + virtual ~FontData(); virtual const SimpleFontData* fontDataForCharacter(UChar32) const = 0; @@ -42,6 +47,12 @@ public: virtual bool isCustomFont() const = 0; virtual bool isLoading() const = 0; virtual bool isSegmented() const = 0; + + void setMaxGlyphPageTreeLevel(unsigned level) const { m_maxGlyphPageTreeLevel = level; } + unsigned maxGlyphPageTreeLevel() const { return m_maxGlyphPageTreeLevel; } + +private: + mutable unsigned m_maxGlyphPageTreeLevel; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/FontDescription.cpp b/WebCore/platform/graphics/FontDescription.cpp new file mode 100644 index 0000000..58ddf81 --- /dev/null +++ b/WebCore/platform/graphics/FontDescription.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2007 Nicholas Shanks <contact@nickshanks.com> + * 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 "FontDescription.h" + +namespace WebCore { + +FontWeight FontDescription::lighterWeight(void) const +{ + // FIXME: Should actually return the CSS weight corresponding to next lightest + // weight of the currently used font family. + switch (m_weight) { + case FontWeight100: + case FontWeight200: + return FontWeight100; + + case FontWeight300: + return FontWeight200; + + case FontWeight400: + case FontWeight500: + return FontWeight300; + + case FontWeight600: + case FontWeight700: + return FontWeight400; + + case FontWeight800: + return FontWeight500; + + case FontWeight900: + return FontWeight700; + } + ASSERT_NOT_REACHED(); + return FontWeightNormal; +} + +FontWeight FontDescription::bolderWeight(void) const +{ + // FIXME: Should actually return the CSS weight corresponding to next heaviest + // weight of the currently used font family. + switch (m_weight) { + case FontWeight100: + case FontWeight200: + return FontWeight300; + + case FontWeight300: + return FontWeight400; + + case FontWeight400: + case FontWeight500: + return FontWeight700; + + case FontWeight600: + case FontWeight700: + return FontWeight800; + + case FontWeight800: + case FontWeight900: + return FontWeight900; + } + ASSERT_NOT_REACHED(); + return FontWeightNormal; +} + +FontTraitsMask FontDescription::traitsMask() const +{ + return static_cast<FontTraitsMask>((m_italic ? FontStyleItalicMask : FontStyleNormalMask) + | (m_smallCaps ? FontVariantSmallCapsMask : FontVariantNormalMask) + | (FontWeight100Mask << (m_weight - FontWeight100))); + +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/FontDescription.h b/WebCore/platform/graphics/FontDescription.h index 6bb232e..d13e86a 100644 --- a/WebCore/platform/graphics/FontDescription.h +++ b/WebCore/platform/graphics/FontDescription.h @@ -2,7 +2,8 @@ * 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-6 Apple Computer, Inc. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,15 +26,24 @@ #define FontDescription_h #include "FontFamily.h" +#include "FontRenderingMode.h" +#include "FontTraitsMask.h" namespace WebCore { -const unsigned cNormalWeight = 50; -const unsigned cBoldWeight = 63; - -// This setting is used to provide ways of switching between multiple rendering modes that may have different -// metrics. It is used to switch between CG and GDI text on Windows. -enum FontRenderingMode { NormalRenderingMode, AlternateRenderingMode }; +enum FontWeight { + FontWeight100, + FontWeight200, + FontWeight300, + FontWeight400, + FontWeight500, + FontWeight600, + FontWeight700, + FontWeight800, + FontWeight900, + FontWeightNormal = FontWeight400, + FontWeightBold = FontWeight700 +}; class FontDescription { public: @@ -41,11 +51,19 @@ public: MonospaceFamily, CursiveFamily, FantasyFamily }; FontDescription() - : m_specifiedSize(0), m_computedSize(0), - m_italic(false), m_smallCaps(false), m_isAbsoluteSize(false), m_weight(cNormalWeight), - m_genericFamily(NoFamily), m_usePrinterFont(false), m_renderingMode(NormalRenderingMode), m_keywordSize(0) - {} - + : m_specifiedSize(0) + , m_computedSize(0) + , m_italic(false) + , m_smallCaps(false) + , m_isAbsoluteSize(false) + , m_weight(FontWeightNormal) + , m_genericFamily(NoFamily) + , m_usePrinterFont(false) + , m_renderingMode(NormalRenderingMode) + , m_keywordSize(0) + { + } + bool operator==(const FontDescription&) const; bool operator!=(const FontDescription& other) const { return !(*this == other); } @@ -54,24 +72,26 @@ public: float specifiedSize() const { return m_specifiedSize; } float computedSize() const { return m_computedSize; } bool italic() const { return m_italic; } - bool bold() const { return weight() == cBoldWeight; } int computedPixelSize() const { return int(m_computedSize + 0.5f); } bool smallCaps() const { return m_smallCaps; } bool isAbsoluteSize() const { return m_isAbsoluteSize; } - unsigned weight() const { return m_weight; } + FontWeight weight() const { return static_cast<FontWeight>(m_weight); } + FontWeight lighterWeight() const; + FontWeight bolderWeight() const; GenericFamilyType genericFamily() const { return static_cast<GenericFamilyType>(m_genericFamily); } bool usePrinterFont() const { return m_usePrinterFont; } FontRenderingMode renderingMode() const { return static_cast<FontRenderingMode>(m_renderingMode); } int keywordSize() const { return m_keywordSize; } + FontTraitsMask traitsMask() const; + void setFamily(const FontFamily& family) { m_familyList = family; } void setComputedSize(float s) { m_computedSize = s; } void setSpecifiedSize(float s) { m_specifiedSize = s; } void setItalic(bool i) { m_italic = i; } - void setBold(bool b) { m_weight = (b ? cBoldWeight : cNormalWeight); } void setSmallCaps(bool c) { m_smallCaps = c; } void setIsAbsoluteSize(bool s) { m_isAbsoluteSize = s; } - void setWeight(unsigned w) { m_weight = w; } + void setWeight(FontWeight w) { m_weight = w; } void setGenericFamily(GenericFamilyType genericFamily) { m_genericFamily = genericFamily; } void setUsePrinterFont(bool p) { m_usePrinterFont = p; } void setRenderingMode(FontRenderingMode mode) { m_renderingMode = mode; } @@ -88,7 +108,7 @@ private: bool m_smallCaps : 1; bool m_isAbsoluteSize : 1; // Whether or not CSS specified an explicit size // (logical sizes like "medium" don't count). - unsigned m_weight : 8; + unsigned m_weight : 8; // FontWeight unsigned m_genericFamily : 3; // GenericFamilyType bool m_usePrinterFont : 1; diff --git a/WebCore/platform/graphics/FontFallbackList.cpp b/WebCore/platform/graphics/FontFallbackList.cpp index 049cf7f..ef59c2f 100644 --- a/WebCore/platform/graphics/FontFallbackList.cpp +++ b/WebCore/platform/graphics/FontFallbackList.cpp @@ -36,20 +36,34 @@ namespace WebCore { FontFallbackList::FontFallbackList() -: m_familyIndex(0) -, m_pitch(UnknownPitch) -, m_loadingCustomFonts(false) -, m_fontSelector(0) + : m_familyIndex(0) + , m_pitch(UnknownPitch) + , m_loadingCustomFonts(false) + , m_fontSelector(0) + , m_generation(FontCache::generation()) { } void FontFallbackList::invalidate(PassRefPtr<FontSelector> fontSelector) { + releaseFontData(); m_fontList.clear(); m_familyIndex = 0; m_pitch = UnknownPitch; m_loadingCustomFonts = false; m_fontSelector = fontSelector; + m_generation = FontCache::generation(); +} + +void FontFallbackList::releaseFontData() +{ + unsigned numFonts = m_fontList.size(); + 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)); + } + } } void FontFallbackList::determinePitch(const Font* font) const @@ -70,7 +84,7 @@ void FontFallbackList::determinePitch(const Font* font) const const FontData* FontFallbackList::fontDataAt(const Font* font, unsigned realizedFontIndex) const { if (realizedFontIndex < m_fontList.size()) - return m_fontList[realizedFontIndex]; // This fallback font is already in our list. + return m_fontList[realizedFontIndex].first; // This fallback font is already in our list. // Make sure we're not passing in some crazy value here. ASSERT(realizedFontIndex == m_fontList.size()); @@ -82,9 +96,10 @@ 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()); if (result) { - m_fontList.append(result); + m_fontList.append(pair<const FontData*, bool>(result, result->isCustomFont())); if (result->isLoading()) m_loadingCustomFonts = true; } @@ -100,8 +115,10 @@ const FontData* FontFallbackList::fontDataForCharacters(const Font* font, const while (fontData && !fontData->containsCharacters(characters, length)) fontData = fontDataAt(font, ++realizedFontIndex); - if (!fontData) + if (!fontData) { + ASSERT(FontCache::generation() == m_generation); fontData = FontCache::getFontDataForCharacters(*font, characters, length); + } return fontData; } @@ -109,7 +126,9 @@ const FontData* FontFallbackList::fontDataForCharacters(const Font* font, const void FontFallbackList::setPlatformFont(const FontPlatformData& platformData) { m_familyIndex = cAllFamiliesScanned; - m_fontList.append(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/FontFallbackList.h b/WebCore/platform/graphics/FontFallbackList.h index 38128a2..a23b32c 100644 --- a/WebCore/platform/graphics/FontFallbackList.h +++ b/WebCore/platform/graphics/FontFallbackList.h @@ -41,8 +41,9 @@ const int cAllFamiliesScanned = -1; class FontFallbackList : public RefCounted<FontFallbackList> { public: - FontFallbackList(); + static PassRefPtr<FontFallbackList> create() { return adoptRef(new FontFallbackList()); } + ~FontFallbackList() { releaseFontData(); } void invalidate(PassRefPtr<FontSelector>); bool isFixedPitch(const Font* f) const { if (m_pitch == UnknownPitch) determinePitch(f); return m_pitch == FixedPitch; }; @@ -51,19 +52,25 @@ public: bool loadingCustomFonts() const { return m_loadingCustomFonts; } FontSelector* fontSelector() const { return m_fontSelector.get(); } + unsigned generation() const { return m_generation; } private: + FontFallbackList(); + const FontData* primaryFont(const Font* f) const { return fontDataAt(f, 0); } const FontData* fontDataAt(const Font*, unsigned index) const; const FontData* fontDataForCharacters(const Font*, const UChar*, int length) const; void setPlatformFont(const FontPlatformData&); - mutable Vector<const FontData*, 1> m_fontList; + void releaseFontData(); + + mutable Vector<pair<const FontData*, bool>, 1> m_fontList; mutable int m_familyIndex; mutable Pitch m_pitch; mutable bool m_loadingCustomFonts; RefPtr<FontSelector> m_fontSelector; + unsigned m_generation; friend class Font; }; diff --git a/WebCore/platform/graphics/FontFamily.cpp b/WebCore/platform/graphics/FontFamily.cpp index a185756..12b59a4 100644 --- a/WebCore/platform/graphics/FontFamily.cpp +++ b/WebCore/platform/graphics/FontFamily.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 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 @@ -29,8 +29,7 @@ namespace WebCore { FontFamily::FontFamily(const FontFamily& other) - : RefCounted<FontFamily>() - , m_family(other.m_family) + : m_family(other.m_family) , m_next(other.m_next) { } @@ -42,14 +41,19 @@ FontFamily& FontFamily::operator=(const FontFamily& other) return *this; } -bool FontFamily::operator==(const FontFamily &compareFontFamily) const +bool operator==(const FontFamily& a, const FontFamily& b) { - if ((!m_next && compareFontFamily.m_next) || - (m_next && !compareFontFamily.m_next) || - ((m_next && compareFontFamily.m_next) && (*m_next != *(compareFontFamily.m_next)))) + if (a.family() != b.family()) return false; - - return m_family == compareFontFamily.m_family; + const FontFamily* ap; + const FontFamily* bp; + for (ap = a.next(), bp = b.next(); ap != bp; ap = ap->next(), bp = bp->next()) { + if (!ap || !bp) + return false; + if (ap->family() != bp->family()) + return false; + } + return true; } } diff --git a/WebCore/platform/graphics/FontFamily.h b/WebCore/platform/graphics/FontFamily.h index 65a64b4..126bd83 100644 --- a/WebCore/platform/graphics/FontFamily.h +++ b/WebCore/platform/graphics/FontFamily.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,14 +28,15 @@ #include "AtomicString.h" #include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> +#include <wtf/ListRefPtr.h> namespace WebCore { -class FontFamily : public RefCounted<FontFamily> { +class SharedFontFamily; + +class FontFamily { public: FontFamily() { } - FontFamily(const FontFamily&); FontFamily& operator=(const FontFamily&); @@ -43,19 +44,45 @@ public: const AtomicString& family() const { return m_family; } bool familyIsEmpty() const { return m_family.isEmpty(); } - FontFamily* next() { return m_next.get(); } - const FontFamily* next() const { return m_next.get(); } + const FontFamily* next() const; - void appendFamily(PassRefPtr<FontFamily> family) { m_next = family; } + void appendFamily(PassRefPtr<SharedFontFamily>); + PassRefPtr<SharedFontFamily> releaseNext(); - bool operator==(const FontFamily&) const; - bool operator!=(const FontFamily& x) const { return !(*this == x); } - private: AtomicString m_family; - RefPtr<FontFamily> m_next; + ListRefPtr<SharedFontFamily> m_next; }; +class SharedFontFamily : public FontFamily, public RefCounted<SharedFontFamily> { +public: + static PassRefPtr<SharedFontFamily> create() + { + return adoptRef(new SharedFontFamily); + } + +private: + SharedFontFamily() { } +}; + +bool operator==(const FontFamily&, const FontFamily&); +inline bool operator!=(const FontFamily& a, const FontFamily& b) { return !(a == b); } + +inline const FontFamily* FontFamily::next() const +{ + return m_next.get(); +} + +inline void FontFamily::appendFamily(PassRefPtr<SharedFontFamily> family) +{ + m_next = family; +} + +inline PassRefPtr<SharedFontFamily> FontFamily::releaseNext() +{ + return m_next.release(); +} + } #endif diff --git a/WebCore/platform/graphics/FontRenderingMode.h b/WebCore/platform/graphics/FontRenderingMode.h new file mode 100644 index 0000000..c1ce497 --- /dev/null +++ b/WebCore/platform/graphics/FontRenderingMode.h @@ -0,0 +1,37 @@ +/* + * 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 COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FontRenderingMode_h +#define FontRenderingMode_h + +namespace WebCore { + +// This setting is used to provide ways of switching between multiple rendering modes that may have different +// metrics. It is used to switch between CG and GDI text on Windows. +enum FontRenderingMode { NormalRenderingMode, AlternateRenderingMode }; + +} // namespace WebCore + +#endif // FontRenderingMode_h diff --git a/WebCore/platform/graphics/FontSelector.h b/WebCore/platform/graphics/FontSelector.h index c5b3651..9b520b9 100644 --- a/WebCore/platform/graphics/FontSelector.h +++ b/WebCore/platform/graphics/FontSelector.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 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 @@ -36,8 +36,10 @@ class FontDescription; class FontSelector : public RefCounted<FontSelector> { public: - virtual ~FontSelector() {}; - virtual FontData* getFontData(const FontDescription& fontDescription, const AtomicString& familyName) = 0; + virtual ~FontSelector() { } + virtual FontData* getFontData(const FontDescription&, const AtomicString& familyName) = 0; + + virtual void fontCacheInvalidated() { } }; } // namespace WebCore diff --git a/WebCore/platform/graphics/FontTraitsMask.h b/WebCore/platform/graphics/FontTraitsMask.h new file mode 100644 index 0000000..686c30c --- /dev/null +++ b/WebCore/platform/graphics/FontTraitsMask.h @@ -0,0 +1,70 @@ +/* + * 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 FontTraitsMask_h +#define FontTraitsMask_h + +namespace WebCore { + + enum { + FontStyleNormalBit = 0, + FontStyleItalicBit, + FontVariantNormalBit, + FontVariantSmallCapsBit, + FontWeight100Bit, + FontWeight200Bit, + FontWeight300Bit, + FontWeight400Bit, + FontWeight500Bit, + FontWeight600Bit, + FontWeight700Bit, + FontWeight800Bit, + FontWeight900Bit, + FontTraitsMaskWidth + }; + + enum FontTraitsMask { + FontStyleNormalMask = 1 << FontStyleNormalBit, + FontStyleItalicMask = 1 << FontStyleItalicBit, + FontStyleMask = FontStyleNormalMask | FontStyleItalicMask, + + FontVariantNormalMask = 1 << FontVariantNormalBit, + FontVariantSmallCapsMask = 1 << FontVariantSmallCapsBit, + FontVariantMask = FontVariantNormalMask | FontVariantSmallCapsMask, + + FontWeight100Mask = 1 << FontWeight100Bit, + FontWeight200Mask = 1 << FontWeight200Bit, + FontWeight300Mask = 1 << FontWeight300Bit, + FontWeight400Mask = 1 << FontWeight400Bit, + FontWeight500Mask = 1 << FontWeight500Bit, + FontWeight600Mask = 1 << FontWeight600Bit, + FontWeight700Mask = 1 << FontWeight700Bit, + FontWeight800Mask = 1 << FontWeight800Bit, + FontWeight900Mask = 1 << FontWeight900Bit, + FontWeightMask = FontWeight100Mask | FontWeight200Mask | FontWeight300Mask | FontWeight400Mask | FontWeight500Mask | FontWeight600Mask | FontWeight700Mask | FontWeight800Mask | FontWeight900Mask + }; + +} // namespace WebCore +#endif // FontTraitsMask_h diff --git a/WebCore/platform/graphics/GeneratedImage.cpp b/WebCore/platform/graphics/GeneratedImage.cpp new file mode 100644 index 0000000..5e50959 --- /dev/null +++ b/WebCore/platform/graphics/GeneratedImage.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 Apple Computer, 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 "GeneratedImage.h" + +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" + +using namespace std; + +namespace WebCore { + +void GeneratedImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp) +{ + context->save(); + context->setCompositeOperation(compositeOp); + context->clip(dstRect); + context->translate(dstRect.x(), dstRect.y()); + if (dstRect.size() != srcRect.size()) + context->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height())); + context->translate(-srcRect.x(), -srcRect.y()); + context->fillRect(FloatRect(FloatPoint(), m_size), *m_generator.get()); + context->restore(); +} + +void GeneratedImage::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect) +{ + // Create a BitmapImage and call drawPattern on it. + auto_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(m_size, false); + ASSERT(imageBuffer.get()); + + // Fill with the gradient. + GraphicsContext* graphicsContext = imageBuffer->context(); + graphicsContext->fillRect(FloatRect(FloatPoint(), m_size), *m_generator.get()); + + // Grab the final image from the image buffer. + Image* bitmap = imageBuffer->image(); + + // Now just call drawTiled on that image. + bitmap->drawPattern(context, srcRect, patternTransform, phase, compositeOp, destRect); +} + +} diff --git a/WebCore/platform/graphics/GeneratedImage.h b/WebCore/platform/graphics/GeneratedImage.h new file mode 100644 index 0000000..fb0661b --- /dev/null +++ b/WebCore/platform/graphics/GeneratedImage.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GeneratedImage_h +#define GeneratedImage_h + +#include "Image.h" + +#include "Generator.h" +#include "IntSize.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class GeneratedImage : public Image { +public: + static PassRefPtr<GeneratedImage> create(PassRefPtr<Generator> generator, const IntSize& size) + { + return adoptRef(new GeneratedImage(generator, size)); + } + virtual ~GeneratedImage() {} + + virtual bool hasSingleSecurityOrigin() const { return true; } + + // These are only used for SVGGeneratedImage right now + virtual void setContainerSize(const IntSize& size) { m_size = size; } + virtual bool usesContainerSize() const { return true; } + virtual bool hasRelativeWidth() const { return true; } + virtual bool hasRelativeHeight() const { return true; } + + 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 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, + const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); + +protected: + GeneratedImage(PassRefPtr<Generator> generator, const IntSize& size) + : m_generator(generator) + , m_size(size) + { + } + + RefPtr<Generator> m_generator; + IntSize m_size; +}; + +} + +#endif diff --git a/WebCore/platform/graphics/Generator.h b/WebCore/platform/graphics/Generator.h new file mode 100644 index 0000000..a0af689 --- /dev/null +++ b/WebCore/platform/graphics/Generator.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Generator_h +#define Generator_h + +#include <wtf/RefCounted.h> + +namespace WebCore { + +class FloatRect; +class GraphicsContext; + +class Generator : public RefCounted<Generator> { +public: + virtual ~Generator() {}; + + virtual void fill(GraphicsContext*, const FloatRect&) = 0; +}; + +} //namespace + +#endif diff --git a/WebCore/platform/graphics/GlyphBuffer.h b/WebCore/platform/graphics/GlyphBuffer.h index c8e308c..110b3c2 100644 --- a/WebCore/platform/graphics/GlyphBuffer.h +++ b/WebCore/platform/graphics/GlyphBuffer.h @@ -44,20 +44,18 @@ namespace WebCore { typedef unsigned short Glyph; class SimpleFontData; -#if PLATFORM(CG) -typedef Glyph GlyphBufferGlyph; -typedef CGSize GlyphBufferAdvance; -#elif PLATFORM(CAIRO) +#if PLATFORM(CAIRO) +// FIXME: Why does Cairo use such a huge struct instead of just an offset into an array? typedef cairo_glyph_t GlyphBufferGlyph; -typedef FloatSize GlyphBufferAdvance; -#elif PLATFORM(WX) -typedef Glyph GlyphBufferGlyph; -typedef FloatSize GlyphBufferAdvance; -#elif PLATFORM(SGL) +#else typedef Glyph GlyphBufferGlyph; -typedef FloatSize GlyphBufferAdvance; -#elif PLATFORM(QT) -typedef unsigned short GlyphBufferGlyph; +#endif + +// CG uses CGSize instead of FloatSize so that the result of advances() +// can be passed directly to CGContextShowGlyphsWithAdvances in FontMac.mm +#if PLATFORM(CG) +typedef CGSize GlyphBufferAdvance; +#else typedef FloatSize GlyphBufferAdvance; #endif @@ -110,10 +108,10 @@ public: Glyph glyphAt(int index) const { -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(WX) || PLATFORM(SGL) - return m_glyphs[index]; -#elif PLATFORM(CAIRO) +#if PLATFORM(CAIRO) return m_glyphs[index].index; +#else + return m_glyphs[index]; #endif } @@ -121,7 +119,7 @@ public: { #if PLATFORM(CG) return m_advances[index].width; -#elif PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX) || PLATFORM(SGL) +#else return m_advances[index].width(); #endif } @@ -138,21 +136,21 @@ public: void add(Glyph glyph, const SimpleFontData* font, float width, const FloatSize* offset = 0) { m_fontData.append(font); -#if PLATFORM(CG) - m_glyphs.append(glyph); - CGSize advance; - advance.width = width; - advance.height = 0; - m_advances.append(advance); -#elif PLATFORM(CAIRO) +#if PLATFORM(CAIRO) cairo_glyph_t cairoGlyph; cairoGlyph.index = glyph; m_glyphs.append(cairoGlyph); - m_advances.append(FloatSize(width, 0)); -#elif PLATFORM(QT) || PLATFORM(WX) || PLATFORM(SGL) +#else m_glyphs.append(glyph); +#endif + +#if PLATFORM(CG) + CGSize advance = { width, 0 }; + m_advances.append(advance); +#else m_advances.append(FloatSize(width, 0)); #endif + #if PLATFORM(WIN) if (offset) m_offsets.append(*offset); @@ -173,6 +171,20 @@ public: bool hasAdjustedWidths() const { return m_hasAdjustedWidths; } #endif + void add(Glyph glyph, const SimpleFontData* font, GlyphBufferAdvance advance) + { + m_fontData.append(font); +#if PLATFORM(CAIRO) + cairo_glyph_t cairoGlyph; + cairoGlyph.index = glyph; + m_glyphs.append(cairoGlyph); +#else + m_glyphs.append(glyph); +#endif + + m_advances.append(advance); + } + private: Vector<const SimpleFontData*, 2048> m_fontData; Vector<GlyphBufferGlyph, 2048> m_glyphs; diff --git a/WebCore/platform/graphics/GlyphPageTreeNode.cpp b/WebCore/platform/graphics/GlyphPageTreeNode.cpp index 53c94b8..6b9d23d 100644 --- a/WebCore/platform/graphics/GlyphPageTreeNode.cpp +++ b/WebCore/platform/graphics/GlyphPageTreeNode.cpp @@ -65,6 +65,31 @@ GlyphPageTreeNode* GlyphPageTreeNode::getRoot(unsigned pageNumber) return node; } +size_t GlyphPageTreeNode::treeGlyphPageCount() +{ + size_t count = 0; + if (roots) { + HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end(); + for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it) + count += it->second->pageCount(); + } + + if (pageZeroRoot) + count += pageZeroRoot->pageCount(); + + return count; +} + +size_t GlyphPageTreeNode::pageCount() const +{ + size_t count = m_page && m_page->owner() == this ? 1 : 0; + HashMap<const FontData*, GlyphPageTreeNode*>::const_iterator end = m_children.end(); + for (HashMap<const FontData*, GlyphPageTreeNode*>::const_iterator it = m_children.begin(); it != end; ++it) + count += it->second->pageCount(); + + return count; +} + void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData) { // Enumerate all the roots and prune any tree that contains our custom font data. @@ -78,6 +103,18 @@ void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData) pageZeroRoot->pruneCustomFontData(fontData); } +void GlyphPageTreeNode::pruneTreeFontData(const SimpleFontData* fontData) +{ + if (roots) { + HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end(); + for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it) + it->second->pruneFontData(fontData); + } + + if (pageZeroRoot) + pageZeroRoot->pruneFontData(fontData); +} + GlyphPageTreeNode::~GlyphPageTreeNode() { deleteAllValues(m_children); @@ -146,7 +183,7 @@ void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNu } } - m_page = new GlyphPage(this); + m_page = GlyphPage::create(this); // Now that we have a buffer full of characters, we want to get back an array // of glyph indices. This part involves calling into the platform-specific @@ -160,19 +197,32 @@ void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNu const SegmentedFontData* segmentedFontData = static_cast<const SegmentedFontData*>(fontData); unsigned numRanges = segmentedFontData->numRanges(); bool zeroFilled = false; + RefPtr<GlyphPage> scratchPage; + GlyphPage* pageToFill = m_page.get(); for (unsigned i = 0; i < numRanges; i++) { const FontDataRange& range = segmentedFontData->rangeAt(i); int from = max(0, range.from() - static_cast<int>(start)); int to = 1 + min(range.to() - static_cast<int>(start), static_cast<int>(GlyphPage::size) - 1); if (from < static_cast<int>(GlyphPage::size) && to > 0) { + if (haveGlyphs && !scratchPage) { + scratchPage = GlyphPage::create(this); + pageToFill = scratchPage.get(); + } + if (!zeroFilled) { if (from > 0 || to < static_cast<int>(GlyphPage::size)) { for (unsigned i = 0; i < GlyphPage::size; i++) - m_page->setGlyphDataForIndex(i, 0, 0); + pageToFill->setGlyphDataForIndex(i, 0, 0); } zeroFilled = true; } - haveGlyphs |= m_page->fill(from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData()); + haveGlyphs |= pageToFill->fill(from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData()); + if (scratchPage) { + for (int j = from; j < to; j++) { + if (!m_page->m_glyphs[j].glyph && pageToFill->m_glyphs[j].glyph) + m_page->m_glyphs[j] = pageToFill->m_glyphs[j]; + } + } } } } else @@ -208,7 +258,7 @@ void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNu m_page = parentPage; } else { // Combine the parent's glyphs and ours to form a new more complete page. - m_page = new GlyphPage(this); + m_page = GlyphPage::create(this); // Overlay the parent page on the fallback page. Check if the fallback font // has added anything. @@ -231,7 +281,7 @@ void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNu } } } else { - m_page = new GlyphPage(this); + m_page = GlyphPage::create(this); // System fallback. Initialized with the parent's page here, as individual // entries may use different fonts depending on character. If the Font // ever finds it needs a glyph out of the system fallback page, it will @@ -264,9 +314,10 @@ GlyphPageTreeNode* GlyphPageTreeNode::getChild(const FontData* fontData, unsigne #ifndef NDEBUG child->m_pageNumber = m_pageNumber; #endif - if (fontData) + if (fontData) { m_children.set(fontData, child); - else { + fontData->setMaxGlyphPageTreeLevel(max(fontData->maxGlyphPageTreeLevel(), child->m_level)); + } else { m_systemFallbackChild = child; child->m_isSystemFallback = true; } @@ -298,4 +349,36 @@ void GlyphPageTreeNode::pruneCustomFontData(const FontData* fontData) it->second->pruneCustomFontData(fontData); } +void GlyphPageTreeNode::pruneFontData(const SimpleFontData* fontData, unsigned level) +{ + ASSERT(fontData); + if (!fontData) + return; + + // Prune any branch that contains this FontData. + HashMap<const FontData*, GlyphPageTreeNode*>::iterator child = m_children.find(fontData); + if (child == m_children.end()) { + // If there is no level-1 node for fontData, then there is no deeper node for it in this tree. + if (!level) + return; + } else { + GlyphPageTreeNode* node = child->second; + m_children.remove(fontData); + unsigned customFontCount = node->m_customFontCount; + delete node; + if (customFontCount) { + for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) + curr->m_customFontCount -= customFontCount; + } + } + + level++; + if (level > fontData->maxGlyphPageTreeLevel()) + return; + + HashMap<const FontData*, GlyphPageTreeNode*>::iterator end = m_children.end(); + for (HashMap<const FontData*, GlyphPageTreeNode*>::iterator it = m_children.begin(); it != end; ++it) + it->second->pruneFontData(fontData, level); +} + } diff --git a/WebCore/platform/graphics/GlyphPageTreeNode.h b/WebCore/platform/graphics/GlyphPageTreeNode.h index 2619888..240b492 100644 --- a/WebCore/platform/graphics/GlyphPageTreeNode.h +++ b/WebCore/platform/graphics/GlyphPageTreeNode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 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 @@ -29,9 +29,10 @@ #ifndef GlyphPageTreeNode_h #define GlyphPageTreeNode_h +#include <wtf/HashMap.h> +#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/unicode/Unicode.h> -#include <wtf/HashMap.h> namespace WebCore { @@ -57,14 +58,9 @@ struct GlyphData { // although multiple nodes may reference it as their "page" if they are supposed // to be overriding the parent's node, but provide no additional information. struct GlyphPage : public RefCounted<GlyphPage> { - GlyphPage() - : m_owner(0) - { - } - - GlyphPage(GlyphPageTreeNode* owner) - : m_owner(owner) + static PassRefPtr<GlyphPage> create(GlyphPageTreeNode* owner) { + return adoptRef(new GlyphPage(owner)); } static const size_t size = 256; // Covers Latin-1 in a single page. @@ -83,8 +79,15 @@ struct GlyphPage : public RefCounted<GlyphPage> { m_glyphs[index].fontData = f; } GlyphPageTreeNode* owner() const { return m_owner; } + // Implemented by the platform. - bool fill(unsigned offset, unsigned length, UChar* characterBuffer, unsigned bufferLength, const SimpleFontData* fontData); + bool fill(unsigned offset, unsigned length, UChar* characterBuffer, unsigned bufferLength, const SimpleFontData*); + +private: + GlyphPage(GlyphPageTreeNode* owner) + : m_owner(owner) + { + } }; // The glyph page tree is a data structure that maps (FontData, glyph page number) @@ -132,8 +135,10 @@ public: } static void pruneTreeCustomFontData(const FontData*); + static void pruneTreeFontData(const SimpleFontData*); void pruneCustomFontData(const FontData*); + void pruneFontData(const SimpleFontData*, unsigned level = 0); GlyphPageTreeNode* parent() const { return m_parent; } GlyphPageTreeNode* getChild(const FontData*, unsigned pageNumber); @@ -147,6 +152,9 @@ public: // The system fallback font has special rules (see above). bool isSystemFallback() const { return m_isSystemFallback; } + static size_t treeGlyphPageCount(); + size_t pageCount() const; + private: static GlyphPageTreeNode* getRoot(unsigned pageNumber); void initializePage(const FontData*, unsigned pageNumber); diff --git a/WebCore/platform/graphics/Gradient.cpp b/WebCore/platform/graphics/Gradient.cpp new file mode 100644 index 0000000..2e6a5d2 --- /dev/null +++ b/WebCore/platform/graphics/Gradient.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Gradient.h" + +#include "Color.h" + +namespace WebCore { + +Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1) + : m_radial(false) + , m_p0(p0) + , m_p1(p1) + , m_r0(0) + , m_r1(0) + , m_stopsSorted(false) + , m_lastStop(0) +{ + platformInit(); +} + +Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1) + : m_radial(true) + , m_p0(p0) + , m_p1(p1) + , m_r0(r0) + , m_r1(r1) + , m_stopsSorted(false) + , m_lastStop(0) +{ + platformInit(); +} + +Gradient::~Gradient() +{ + platformDestroy(); +} + +void Gradient::addColorStop(float value, const Color& color) +{ + float r; + float g; + float b; + float a; + color.getRGBA(r, g, b, a); + m_stops.append(ColorStop(value, r, g, b, a)); + + m_stopsSorted = false; + + platformDestroy(); +} + +static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b) +{ + return a.stop < b.stop; +} + +void Gradient::getColor(float value, float* r, float* g, float* b, float* a) const +{ + ASSERT(value >= 0); + ASSERT(value <= 1); + + if (m_stops.isEmpty()) { + *r = 0; + *g = 0; + *b = 0; + *a = 0; + return; + } + if (!m_stopsSorted) { + if (m_stops.size()) + std::stable_sort(m_stops.begin(), m_stops.end(), compareStops); + m_stopsSorted = true; + } + if (value <= 0 || value <= m_stops.first().stop) { + *r = m_stops.first().red; + *g = m_stops.first().green; + *b = m_stops.first().blue; + *a = m_stops.first().alpha; + return; + } + if (value >= 1 || value >= m_stops.last().stop) { + *r = m_stops.last().red; + *g = m_stops.last().green; + *b = m_stops.last().blue; + *a = m_stops.last().alpha; + return; + } + + // Find stop before and stop after and interpolate. + int stop = findStop(value); + const ColorStop& lastStop = m_stops[stop]; + const ColorStop& nextStop = m_stops[stop + 1]; + float stopFraction = (value - lastStop.stop) / (nextStop.stop - lastStop.stop); + *r = lastStop.red + (nextStop.red - lastStop.red) * stopFraction; + *g = lastStop.green + (nextStop.green - lastStop.green) * stopFraction; + *b = lastStop.blue + (nextStop.blue - lastStop.blue) * stopFraction; + *a = lastStop.alpha + (nextStop.alpha - lastStop.alpha) * stopFraction; +} + +int Gradient::findStop(float value) const +{ + ASSERT(value >= 0); + ASSERT(value <= 1); + ASSERT(m_stopsSorted); + + int numStops = m_stops.size(); + ASSERT(numStops >= 2); + ASSERT(m_lastStop < numStops - 1); + + int i = m_lastStop; + if (value < m_stops[i].stop) + i = 1; + else + i = m_lastStop + 1; + + for (; i < numStops - 1; ++i) + if (value < m_stops[i].stop) + break; + + m_lastStop = i - 1; + return m_lastStop; +} + +} //namespace diff --git a/WebCore/platform/graphics/Gradient.h b/WebCore/platform/graphics/Gradient.h new file mode 100644 index 0000000..00ef2b6 --- /dev/null +++ b/WebCore/platform/graphics/Gradient.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Gradient_h +#define Gradient_h + +#include "FloatPoint.h" +#include "Generator.h" +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +#if PLATFORM(CG) +typedef struct CGShading* CGShadingRef; +typedef CGShadingRef PlatformGradient; +#elif PLATFORM(QT) +QT_BEGIN_NAMESPACE +class QGradient; +QT_END_NAMESPACE +typedef QGradient* PlatformGradient; +#elif PLATFORM(CAIRO) +typedef struct _cairo_pattern cairo_pattern_t; +typedef cairo_pattern_t* PlatformGradient; +#elif PLATFORM(SGL) +#include "SkShader.h" +typedef class PlatformGradientRec* PlatformGradient; +#elif PLATFORM(SKIA) +class SkShader; +typedef class SkShader* PlatformGradient; +typedef class SkShader* PlatformPattern; +#else +typedef void* PlatformGradient; +#endif + +namespace WebCore { + + class Color; + + class Gradient : public Generator { + public: + static PassRefPtr<Gradient> create(const FloatPoint& p0, const FloatPoint& p1) + { + return adoptRef(new Gradient(p0, p1)); + } + static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1) + { + return adoptRef(new Gradient(p0, r0, p1, r1)); + } + virtual ~Gradient(); + + void addColorStop(float, const Color&); + + void getColor(float value, float* r, float* g, float* b, float* a) const; + +#if PLATFORM(SGL) + SkShader* getShader(SkShader::TileMode); +#else + PlatformGradient platformGradient(); +#endif + + struct ColorStop { + float stop; + float red; + float green; + float blue; + float alpha; + + ColorStop() : stop(0), red(0), green(0), blue(0), alpha(0) { } + ColorStop(float s, float r, float g, float b, float a) : stop(s), red(r), green(g), blue(b), alpha(a) { } + }; + + void setStopsSorted(bool s) { m_stopsSorted = s; } + + virtual void fill(GraphicsContext*, const FloatRect&); + + private: + Gradient(const FloatPoint& p0, const FloatPoint& p1); + Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1); + + void platformInit() { m_gradient = 0; } + void platformDestroy(); + + int findStop(float value) const; + + bool m_radial; + FloatPoint m_p0; + FloatPoint m_p1; + float m_r0; + float m_r1; + mutable Vector<ColorStop> m_stops; + mutable bool m_stopsSorted; + mutable int m_lastStop; + + PlatformGradient m_gradient; + }; + +} //namespace + +#endif diff --git a/WebCore/platform/graphics/GraphicsContext.cpp b/WebCore/platform/graphics/GraphicsContext.cpp index 2f2f97e..9cd2969 100644 --- a/WebCore/platform/graphics/GraphicsContext.cpp +++ b/WebCore/platform/graphics/GraphicsContext.cpp @@ -27,7 +27,10 @@ #include "GraphicsContext.h" #include "BidiResolver.h" +#include "Generator.h" +#include "GraphicsContextPrivate.h" #include "Font.h" +#include "NotImplemented.h" using namespace std; @@ -54,7 +57,7 @@ public: } unsigned offset() const { return m_offset; } - void increment(BidiResolver<TextRunIterator, BidiCharacterRun>&) { m_offset++; } + void increment() { m_offset++; } bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); } UChar current() const { return (*m_textRun)[m_offset]; } WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); } @@ -71,44 +74,6 @@ private: int m_offset; }; -struct GraphicsContextState { - GraphicsContextState() - : strokeStyle(SolidStroke) - , strokeThickness(0) - , strokeColor(Color::black) - , fillColor(Color::black) - , textDrawingMode(cTextFill) - , paintingDisabled(false) - {} - - Font font; - StrokeStyle strokeStyle; - float strokeThickness; - Color strokeColor; - Color fillColor; - int textDrawingMode; - bool paintingDisabled; -}; - -class GraphicsContextPrivate { -public: - GraphicsContextPrivate(); - - GraphicsContextState state; - Vector<GraphicsContextState> stack; - Vector<IntRect> m_focusRingRects; - int m_focusRingWidth; - int m_focusRingOffset; - bool m_updatingControlTints; -}; - -GraphicsContextPrivate::GraphicsContextPrivate() - : m_focusRingWidth(0) - , m_focusRingOffset(0) - , m_updatingControlTints(false) -{ -} - GraphicsContextPrivate* GraphicsContext::createGraphicsContextPrivate() { return new GraphicsContextPrivate; @@ -169,10 +134,36 @@ void GraphicsContext::setStrokeStyle(const StrokeStyle& style) void GraphicsContext::setStrokeColor(const Color& color) { + m_common->state.strokeColorSpace = SolidColorSpace; m_common->state.strokeColor = color; setPlatformStrokeColor(color); } +void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color) +{ + m_common->state.shadowSize = size; + m_common->state.shadowBlur = blur; + m_common->state.shadowColor = color; + setPlatformShadow(size, blur, color); +} + +void GraphicsContext::clearShadow() +{ + m_common->state.shadowSize = IntSize(); + m_common->state.shadowBlur = 0; + m_common->state.shadowColor = Color(); + clearPlatformShadow(); +} + +bool GraphicsContext::getShadow(IntSize& size, int& blur, Color& color) const +{ + size = m_common->state.shadowSize; + blur = m_common->state.shadowBlur; + color = m_common->state.shadowColor; + + return color.isValid() && color.alpha() && (blur || size.width() || size.height()); +} + float GraphicsContext::strokeThickness() const { return m_common->state.strokeThickness; @@ -188,8 +179,29 @@ Color GraphicsContext::strokeColor() const return m_common->state.strokeColor; } +WindRule GraphicsContext::fillRule() const +{ + return m_common->state.fillRule; +} + +void GraphicsContext::setFillRule(WindRule fillRule) +{ + m_common->state.fillRule = fillRule; +} + +GradientSpreadMethod GraphicsContext::spreadMethod() const +{ + return m_common->state.spreadMethod; +} + +void GraphicsContext::setSpreadMethod(GradientSpreadMethod spreadMethod) +{ + m_common->state.spreadMethod = spreadMethod; +} + void GraphicsContext::setFillColor(const Color& color) { + m_common->state.fillColorSpace = SolidColorSpace; m_common->state.fillColor = color; setPlatformFillColor(color); } @@ -199,6 +211,50 @@ Color GraphicsContext::fillColor() const return m_common->state.fillColor; } +void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) +{ + ASSERT(pattern); + if (!pattern) { + setStrokeColor(Color::black); + return; + } + m_common->state.strokeColorSpace = PatternColorSpace; + m_common->state.strokePattern = pattern; +} + +void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) +{ + ASSERT(pattern); + if (!pattern) { + setFillColor(Color::black); + return; + } + m_common->state.fillColorSpace = PatternColorSpace; + m_common->state.fillPattern = pattern; +} + +void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) +{ + ASSERT(gradient); + if (!gradient) { + setStrokeColor(Color::black); + return; + } + m_common->state.strokeColorSpace = GradientColorSpace; + m_common->state.strokeGradient = gradient; +} + +void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) +{ + ASSERT(gradient); + if (!gradient) { + setFillColor(Color::black); + return; + } + m_common->state.fillColorSpace = GradientColorSpace; + m_common->state.fillGradient = gradient; +} + bool GraphicsContext::updatingControlTints() const { return m_common->m_updatingControlTints; @@ -248,7 +304,7 @@ void GraphicsContext::drawText(const TextRun& run, const IntPoint& point, int fr font().drawText(this, run, point, from, to); } -void GraphicsContext::drawBidiText(const TextRun& run, const IntPoint& point) +void GraphicsContext::drawBidiText(const TextRun& run, const FloatPoint& point) { if (paintingDisabled()) return; @@ -258,7 +314,8 @@ void GraphicsContext::drawBidiText(const TextRun& run, const IntPoint& point) bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, new BidiContext(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride()))); - bidiResolver.createBidiRunsForLine(TextRunIterator(&run, 0), TextRunIterator(&run, run.length())); + bidiResolver.setPosition(TextRunIterator(&run, 0)); + bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); if (!bidiResolver.runCount()) return; @@ -340,11 +397,9 @@ const Vector<IntRect>& GraphicsContext::focusRingRects() const return m_common->m_focusRingRects; } -static const int cInterpolationCutoff = 800 * 800; - void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale) { - if (paintingDisabled()) + if (paintingDisabled() || !image) return; float tsw = src.width(); @@ -362,19 +417,18 @@ void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const Float if (th == -1) th = image->height(); - bool shouldUseLowQualityInterpolation = useLowQualityScale && (tsw != tw || tsh != th) && tsw * tsh > cInterpolationCutoff; - if (shouldUseLowQualityInterpolation) { + if (useLowQualityScale) { save(); - setUseLowQualityImageInterpolation(true); + setImageInterpolationQuality(InterpolationNone); } image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), op); - if (shouldUseLowQualityInterpolation) + if (useLowQualityScale) restore(); } void GraphicsContext::drawTiledImage(Image* image, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op) { - if (paintingDisabled()) + if (paintingDisabled() || !image) return; image->drawTiled(this, rect, srcPoint, tileSize, op); @@ -382,12 +436,12 @@ void GraphicsContext::drawTiledImage(Image* image, const IntRect& rect, const In void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op) { - if (paintingDisabled()) + if (paintingDisabled() || !image) return; if (hRule == Image::StretchTile && vRule == Image::StretchTile) // Just do a scale. - return drawImage(image, dest, srcRect); + return drawImage(image, dest, srcRect, op); image->drawTiled(this, dest, srcRect, hRule, vRule, op); } @@ -423,7 +477,14 @@ void GraphicsContext::setTextDrawingMode(int mode) setPlatformTextDrawingMode(mode); } -#if !PLATFORM(CG) +void GraphicsContext::fillRect(const FloatRect& rect, Generator& generator) +{ + if (paintingDisabled()) + return; + generator.fill(this, rect); +} + +#if !PLATFORM(CG) && !PLATFORM(SKIA) // Implement this if you want to go ahead and push the drawing mode into your native context // immediately. void GraphicsContext::setPlatformTextDrawingMode(int mode) @@ -431,7 +492,7 @@ void GraphicsContext::setPlatformTextDrawingMode(int mode) } #endif -#if !PLATFORM(QT) && !PLATFORM(CAIRO) +#if !PLATFORM(QT) && !PLATFORM(CAIRO) && !PLATFORM(SKIA) void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&) { } diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index f75c287..95bdc90 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -26,6 +26,7 @@ #ifndef GraphicsContext_h #define GraphicsContext_h +#include "DashArray.h" #include "FloatRect.h" #include "Image.h" #include "IntRect.h" @@ -34,21 +35,21 @@ #include <wtf/Noncopyable.h> #include <wtf/Platform.h> -#ifdef ANDROID_CANVAS_IMPL - #include "PlatformGraphics.h" -#endif - #if PLATFORM(CG) typedef struct CGContext PlatformGraphicsContext; #elif PLATFORM(CAIRO) typedef struct _cairo PlatformGraphicsContext; #elif PLATFORM(QT) +QT_BEGIN_NAMESPACE class QPainter; +QT_END_NAMESPACE typedef QPainter PlatformGraphicsContext; #elif PLATFORM(SGL) namespace WebCore { class PlatformGraphicsContext; } +class SkPaint; +struct SkPoint; #elif PLATFORM(WX) class wxGCDC; class wxWindowDC; @@ -68,6 +69,8 @@ class wxWindowDC; #else typedef wxWindowDC PlatformGraphicsContext; #endif +#elif PLATFORM(SKIA) +typedef class PlatformContextSkia PlatformGraphicsContext; #else typedef void PlatformGraphicsContext; #endif @@ -79,6 +82,14 @@ typedef struct _GdkEventExpose GdkEventExpose; #if PLATFORM(WIN) typedef struct HDC__* HDC; +#if !PLATFORM(CG) +// UInt8 is defined in CoreFoundation/CFBase.h +typedef unsigned char UInt8; +#endif +#endif + +#if PLATFORM(QT) && defined(Q_WS_WIN) +#include <windows.h> #endif namespace WebCore { @@ -89,10 +100,14 @@ namespace WebCore { class AffineTransform; class Font; + class Generator; + class Gradient; class GraphicsContextPrivate; class GraphicsContextPlatformPrivate; + class ImageBuffer; class KURL; class Path; + class Pattern; class TextRun; // These bits can be ORed together for a total of 8 possible text drawing modes. @@ -108,6 +123,22 @@ namespace WebCore { DashedStroke }; + enum InterpolationQuality { + InterpolationDefault, + InterpolationNone, + InterpolationLow, + InterpolationMedium, + InterpolationHigh + }; + + // FIXME: Currently these constants have to match the values used in the SVG + // DOM API. That's a mistake. We need to make cut that dependency. + enum GradientSpreadMethod { + SpreadMethodPad = 1, + SpreadMethodReflect = 2, + SpreadMethodRepeat = 3 + }; + class GraphicsContext : Noncopyable { public: GraphicsContext(PlatformGraphicsContext*); @@ -124,104 +155,67 @@ namespace WebCore { void setStrokeStyle(const StrokeStyle& style); Color strokeColor() const; void setStrokeColor(const Color&); + void setStrokePattern(PassRefPtr<Pattern>); + void setStrokeGradient(PassRefPtr<Gradient>); + WindRule fillRule() const; + void setFillRule(WindRule); + GradientSpreadMethod spreadMethod() const; + void setSpreadMethod(GradientSpreadMethod); Color fillColor() const; void setFillColor(const Color&); + void setFillPattern(PassRefPtr<Pattern>); + void setFillGradient(PassRefPtr<Gradient>); + +#if PLATFORM(SGL) + /* these should be pused to apple. needed for CanvasStyle.cpp */ + void setCMYKAFillColor(float c, float m, float y, float k, float a); + void setCMYKAStrokeColor(float c, float m, float y, float k, float a); + // initialize a paint for filling + void setupFillPaint(SkPaint*); + // initialize a paint for stroking + void setupStrokePaint(SkPaint*); + // initialize a paint for a shadow, or if false is returned, the + // parameters are left untouched + bool setupShadowPaint(SkPaint* paint, SkPoint* offset); + // returns true if there is a valid (non-transparent) fill color + bool willFill() const; + // returns true if there is a valid (non-transparent) stroke color + bool willStroke() const; + + /** platform-specific factory method to return a bitmap graphicscontext, + called by <canvas> when we need to draw offscreen. Caller is responsible for + deleting the context. Use drawOffscreenContext() to draw the context's image + onto another graphics context. + */ + static GraphicsContext* createOffscreenContext(int width, int height); +#endif + void save(); void restore(); - + // These draw methods will do both stroking and filling. void drawRect(const IntRect&); void drawLine(const IntPoint&, const IntPoint&); void drawEllipse(const IntRect&); void drawConvexPolygon(size_t numPoints, const FloatPoint*, bool shouldAntialias = false); -#ifdef ANDROID_CANVAS_IMPL - /** Fill the specified path using the optional gradient or pattern, using the following - precedence. If/when gradients/patterns are added to the graphics context, these - parameters can go away - 1) use gradient if gradient != null - 2) use pattern if pattern != null - 3) use color in the graphics context - */ - void fillPath(const Path&, PlatformGradient*, PlatformPattern*); - /** Stroke the specified path using the optional gradient or pattern, using the following - precedence. If/when gradients/patterns are added to the graphics context, these - parameters can go away - 1) use gradient if gradient != null - 2) use pattern if pattern != null - 3) use color in the graphics context - */ - void strokePath(const Path&, PlatformGradient*, PlatformPattern*); - /** Fill the specified rect using the optional gradient or pattern, using the following - precedence. If/when gradients/patterns are added to the graphics context, these - parameters can go away - 1) use gradient if gradient != null - 2) use pattern if pattern != null - 3) use color in the graphics context - */ - void fillRect(const FloatRect&, PlatformGradient*, PlatformPattern*); - /** Stroke the specified rect using the optional gradient or pattern, using the following - precedence. If/when gradients/patterns are added to the graphics context, these - parameters can go away - 1) use gradient if gradient != null - 2) use pattern if pattern != null - 3) use color in the graphics context - */ - void strokeRect(const FloatRect&, float lineWidth, PlatformGradient*, PlatformPattern*); - - /** Return a platform specific linear-gradient. Use freePlatformGradient() when you are - done with it. - stopData is { stop, red, green, blue, alpha } per entry - */ - static PlatformGradient* newPlatformLinearGradient(const FloatPoint& p0, const FloatPoint& p1, - const float stopData[5], int count); - - /** Return a platform specific radial-gradient. Use freePlatformGradient() when you are - done with it. - stopData is { stop, red, green, blue, alpha } per entry - */ - static PlatformGradient* newPlatformRadialGradient(const FloatPoint& p0, float r0, - const FloatPoint& p1, float r1, - const float stopData[5], int count); - static void freePlatformGradient(PlatformGradient*); - - /** Return a platform specific pattern. Use freePlatformPattern() when you are - done with it. - */ - static PlatformPattern* newPlatformPattern(Image* image, - Image::TileRule hRule, Image::TileRule vRule); - static void freePlatformPattern(PlatformPattern*); - - /** platform-specific factory method to return a bitmap graphicscontext, - called by <canvas> when we need to draw offscreen. Caller is responsible for - deleting the context. Use drawOffscreenContext() to draw the context's image - onto another graphics context. - */ - static GraphicsContext* createOffscreenContext(int width, int height); - /** Called with a context returned by createOffscreenContext. Draw the underlying - bitmap to the current context. Similar to drawImage(), but this hides how - to extract the bitmap from ctx from the portable code. - If srcRect is NULL, it is assumed that we want to draw the entire bitmap represented - by the GraphicsContext. - */ - void drawOffscreenContext(GraphicsContext* ctx, const WebCore::FloatRect* srcRect, - const WebCore::FloatRect& dstRect); - - /** Return the clip bounds in local coordinates. It can be an approximation, as long as - the returned bounds completely enclose the actual clip. - */ - FloatRect getClipLocalBounds() const; -#endif + void drawPath(); + void fillPath(); + void strokePath(); // Arc drawing (used by border-radius in CSS) just supports stroking at the moment. void strokeArc(const IntRect&, int startAngle, int angleSpan); - - void fillRect(const IntRect&, const Color&); + + void fillRect(const FloatRect&); void fillRect(const FloatRect&, const Color&); + void fillRect(const FloatRect&, Generator&); void fillRoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color&); + void clearRect(const FloatRect&); + + void strokeRect(const FloatRect&); void strokeRect(const FloatRect&, float lineWidth); void drawImage(Image*, const IntPoint&, CompositeOperator = CompositeSourceOver); @@ -235,26 +229,23 @@ namespace WebCore { void drawTiledImage(Image*, const IntRect& destRect, const IntRect& srcRect, Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile, CompositeOperator = CompositeSourceOver); -#if PLATFORM(CG) - void setUseLowQualityImageInterpolation(bool = true); - bool useLowQualityImageInterpolation() const; -#else - void setUseLowQualityImageInterpolation(bool = true) {} - bool useLowQualityImageInterpolation() const { return false; } -#endif - void clip(const IntRect&); + void setImageInterpolationQuality(InterpolationQuality); + InterpolationQuality imageInterpolationQuality() const; + + void clip(const FloatRect&); void addRoundedRectClip(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight); void addInnerRoundedRectClip(const IntRect&, int thickness); 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 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 IntPoint&); + void drawBidiText(const TextRun&, const FloatPoint&); void drawHighlightForText(const TextRun&, const IntPoint&, int h, const Color& backgroundColor, int from = 0, int to = -1); FloatRect roundToDevicePixels(const FloatRect&); @@ -272,6 +263,7 @@ namespace WebCore { void endTransparencyLayer(); void setShadow(const IntSize&, int blur, const Color&); + bool getShadow(IntSize&, int&, Color&) const; void clearShadow(); void initFocusRing(int width, int offset); @@ -281,10 +273,14 @@ namespace WebCore { IntRect focusRingBoundingRect(); void setLineCap(LineCap); + void setLineDash(const DashArray&, float dashOffset); void setLineJoin(LineJoin); void setMiterLimit(float); void setAlpha(float); +#if PLATFORM(CAIRO) + float getAlpha(); +#endif void setCompositeOperation(CompositeOperator); @@ -307,14 +303,43 @@ namespace WebCore { void setUseAntialiasing(bool = true); #if PLATFORM(WIN) - GraphicsContext(HDC); // FIXME: To be removed. + GraphicsContext(HDC, bool hasAlpha = false); // FIXME: To be removed. bool inTransparencyLayer() const; - HDC getWindowsContext(const IntRect&, bool supportAlphaBlend = true); // The passed in rect is used to create a bitmap for compositing inside transparency layers. - void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend = true); // The passed in HDC should be the one handed back by getWindowsContext. + 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. + + class WindowsBitmap : public Noncopyable { + public: + WindowsBitmap(HDC, IntSize); + ~WindowsBitmap(); + + HDC hdc() const { return m_hdc; } + UInt8* buffer() const { return m_bitmapBuffer; } + unsigned bufferLength() const { return m_bitmapBufferLength; } + IntSize size() const { return m_size; } + unsigned bytesPerRow() const { return m_bytesPerRow; } + + private: + HDC m_hdc; + HBITMAP m_bitmap; + UInt8* m_bitmapBuffer; + unsigned m_bitmapBufferLength; + IntSize m_size; + unsigned m_bytesPerRow; + }; + + WindowsBitmap* createWindowsBitmap(IntSize); + // The bitmap should be non-premultiplied. + void drawWindowsBitmap(WindowsBitmap*, const IntPoint&); +#endif + +#if PLATFORM(QT) && defined(Q_WS_WIN) + HDC getWindowsContext(const IntRect&, bool supportAlphaBlend = true, bool mayCreateBitmap = true); + void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend = true, bool mayCreateBitmap = true); #endif #if PLATFORM(QT) - void setFillRule(WindRule); + bool inTransparencyLayer() const; PlatformPath* currentPath(); #endif @@ -322,18 +347,23 @@ namespace WebCore { void setGdkExposeEvent(GdkEventExpose*); GdkDrawable* gdkDrawable() const; GdkEventExpose* gdkExposeEvent() const; - IntPoint translatePoint(const IntPoint&) const; #endif private: void savePlatformState(); void restorePlatformState(); + void setPlatformTextDrawingMode(int); + void setPlatformFont(const Font& font); + void setPlatformStrokeColor(const Color&); void setPlatformStrokeStyle(const StrokeStyle&); void setPlatformStrokeThickness(float); + void setPlatformFillColor(const Color&); - void setPlatformFont(const Font& font); + + void setPlatformShadow(const IntSize&, int blur, const Color&); + void clearPlatformShadow(); int focusRingWidth() const; int focusRingOffset() const; @@ -343,7 +373,7 @@ namespace WebCore { static void destroyGraphicsContextPrivate(GraphicsContextPrivate*); GraphicsContextPrivate* m_common; - GraphicsContextPlatformPrivate* m_data; + GraphicsContextPlatformPrivate* m_data; // Deprecated; m_commmon can just be downcasted. To be removed. }; } // namespace WebCore diff --git a/WebCore/platform/graphics/GraphicsContextPrivate.h b/WebCore/platform/graphics/GraphicsContextPrivate.h new file mode 100644 index 0000000..de94527 --- /dev/null +++ b/WebCore/platform/graphics/GraphicsContextPrivate.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GraphicsContextPrivate_h +#define GraphicsContextPrivate_h + +#include "Font.h" +#include "Gradient.h" +#include "GraphicsContext.h" +#include "Pattern.h" + +namespace WebCore { + +// FIXME: This is a place-holder until we decide to add +// real color space support to WebCore. At that time, ColorSpace will be a +// class and instances will be held off of Colors. There will be +// special singleton Gradient and Pattern color spaces to mark when +// a fill or stroke is using a gradient or pattern instead of a solid color. +// https://bugs.webkit.org/show_bug.cgi?id=20558 + enum ColorSpace { + SolidColorSpace, + PatternColorSpace, + GradientColorSpace + }; + + struct GraphicsContextState { + GraphicsContextState() + : textDrawingMode(cTextFill) + , strokeStyle(SolidStroke) + , strokeThickness(0) +#if PLATFORM(CAIRO) + , globalAlpha(1.0f) +#endif + , strokeColorSpace(SolidColorSpace) + , strokeColor(Color::black) + , fillRule(RULE_NONZERO) + , fillColorSpace(SolidColorSpace) + , fillColor(Color::black) + , paintingDisabled(false) + , shadowBlur(0) + { + } + + Font font; + int textDrawingMode; + + StrokeStyle strokeStyle; + float strokeThickness; +#if PLATFORM(CAIRO) + float globalAlpha; +#endif + ColorSpace strokeColorSpace; + Color strokeColor; + RefPtr<Gradient> strokeGradient; + RefPtr<Pattern> strokePattern; + + WindRule fillRule; + GradientSpreadMethod spreadMethod; + ColorSpace fillColorSpace; + Color fillColor; + RefPtr<Gradient> fillGradient; + RefPtr<Pattern> fillPattern; + + bool paintingDisabled; + + IntSize shadowSize; + unsigned shadowBlur; + Color shadowColor; + }; + + class GraphicsContextPrivate { + public: + GraphicsContextPrivate() + : m_focusRingWidth(0) + , m_focusRingOffset(0) + , m_updatingControlTints(false) + { + } + + GraphicsContextState state; + Vector<GraphicsContextState> stack; + Vector<IntRect> m_focusRingRects; + int m_focusRingWidth; + int m_focusRingOffset; + bool m_updatingControlTints; + }; + +} // namespace WebCore + +#endif // GraphicsContextPrivate_h diff --git a/WebCore/platform/graphics/GraphicsTypes.cpp b/WebCore/platform/graphics/GraphicsTypes.cpp index 736356f..761bf40 100644 --- a/WebCore/platform/graphics/GraphicsTypes.cpp +++ b/WebCore/platform/graphics/GraphicsTypes.cpp @@ -116,4 +116,74 @@ String lineJoinName(LineJoin join) return names[join]; } +String textAlignName(TextAlign align) +{ + ASSERT(align >= 0); + ASSERT(align < 5); + const char* const names[5] = { "start", "end", "left", "center", "right" }; + return names[align]; +} + +bool parseTextAlign(const String& s, TextAlign& align) +{ + if (s == "start") { + align = StartTextAlign; + return true; + } + if (s == "end") { + align = EndTextAlign; + return true; + } + if (s == "left") { + align = LeftTextAlign; + return true; + } + if (s == "center") { + align = CenterTextAlign; + return true; + } + if (s == "right") { + align = RightTextAlign; + return true; + } + return false; +} + +String textBaselineName(TextBaseline baseline) +{ + ASSERT(baseline >= 0); + ASSERT(baseline < 6); + const char* const names[6] = { "alphabetic", "top", "middle", "bottom", "ideographic", "hanging" }; + return names[baseline]; +} + +bool parseTextBaseline(const String& s, TextBaseline& baseline) +{ + if (s == "alphabetic") { + baseline = AlphabeticTextBaseline; + return true; + } + if (s == "top") { + baseline = TopTextBaseline; + return true; + } + if (s == "middle") { + baseline = MiddleTextBaseline; + return true; + } + if (s == "bottom") { + baseline = BottomTextBaseline; + return true; + } + if (s == "ideographic") { + baseline = IdeographicTextBaseline; + return true; + } + if (s == "hanging") { + baseline = HangingTextBaseline; + return true; + } + return false; +} + } diff --git a/WebCore/platform/graphics/GraphicsTypes.h b/WebCore/platform/graphics/GraphicsTypes.h index b3ca99a..cdf5e31 100644 --- a/WebCore/platform/graphics/GraphicsTypes.h +++ b/WebCore/platform/graphics/GraphicsTypes.h @@ -56,6 +56,10 @@ namespace WebCore { enum HorizontalAlignment { AlignLeft, AlignRight, AlignHCenter }; + enum TextBaseline { AlphabeticTextBaseline, TopTextBaseline, MiddleTextBaseline, BottomTextBaseline, IdeographicTextBaseline, HangingTextBaseline }; + + enum TextAlign { StartTextAlign, EndTextAlign, LeftTextAlign, CenterTextAlign, RightTextAlign }; + String compositeOperatorName(CompositeOperator); bool parseCompositeOperator(const String&, CompositeOperator&); @@ -65,6 +69,12 @@ namespace WebCore { String lineJoinName(LineJoin); bool parseLineJoin(const String&, LineJoin&); -} + String textAlignName(TextAlign); + bool parseTextAlign(const String&, TextAlign&); + + String textBaselineName(TextBaseline); + bool parseTextBaseline(const String&, TextBaseline&); + +} // namespace WebCore #endif diff --git a/WebCore/platform/graphics/Icon.h b/WebCore/platform/graphics/Icon.h index 310c25a..444c67c 100644 --- a/WebCore/platform/graphics/Icon.h +++ b/WebCore/platform/graphics/Icon.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -21,8 +21,10 @@ #ifndef Icon_h #define Icon_h +#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Forward.h> +#include <wtf/Vector.h> #if PLATFORM(MAC) #include <wtf/RetainPtr.h> @@ -36,7 +38,9 @@ typedef struct HICON__* HICON; #elif PLATFORM(QT) #include <QIcon> #elif PLATFORM(GTK) -#include <gdk/gdk.h> +typedef struct _GdkPixbuf GdkPixbuf; +#elif PLATFORM(CHROMIUM) +#include "PlatformIcon.h" #endif namespace WebCore { @@ -47,29 +51,33 @@ class String; class Icon : public RefCounted<Icon> { public: - Icon(); + static PassRefPtr<Icon> createIconForFile(const String& filename); + static PassRefPtr<Icon> createIconForFiles(const Vector<String>& filenames); + ~Icon(); - - static PassRefPtr<Icon> newIconForFile(const String& filename); void paint(GraphicsContext*, const IntRect&); #if PLATFORM(WIN) - Icon(HICON); + static PassRefPtr<Icon> create(HICON hIcon) { return adoptRef(new Icon(hIcon)); } #endif private: #if PLATFORM(MAC) Icon(NSImage*); -#endif -#if PLATFORM(MAC) RetainPtr<NSImage> m_nsImage; #elif PLATFORM(WIN) + Icon(HICON); HICON m_hIcon; #elif PLATFORM(QT) + Icon(); QIcon m_icon; #elif PLATFORM(GTK) + Icon(); GdkPixbuf* m_icon; +#elif PLATFORM(CHROMIUM) + Icon(const PlatformIcon&); + PlatformIcon m_icon; #endif }; diff --git a/WebCore/platform/graphics/Image.cpp b/WebCore/platform/graphics/Image.cpp index 8bf7c68..ca6954e 100644 --- a/WebCore/platform/graphics/Image.cpp +++ b/WebCore/platform/graphics/Image.cpp @@ -28,6 +28,7 @@ #include "Image.h" #include "AffineTransform.h" +#include "BitmapImage.h" #include "GraphicsContext.h" #include "IntRect.h" #include "MIMETypeRegistry.h" @@ -49,6 +50,12 @@ Image::~Image() { } +Image* Image::nullImage() +{ + static RefPtr<Image> nullImage = BitmapImage::create(); + return nullImage.get(); +} + bool Image::supportsType(const String& type) { return MIMETypeRegistry::isSupportedImageResourceMIMEType(type); @@ -118,9 +125,6 @@ static inline FloatSize calculatePatternScale(const FloatRect& dstRect, const Fl void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, CompositeOperator op) { - if (!nativeImageForCurrentFrame()) - return; - if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), op); return; @@ -161,9 +165,6 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl // FIXME: Merge with the other drawTiled eventually, since we need a combination of both for some things. void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, TileRule hRule, TileRule vRule, CompositeOperator op) { - if (!nativeImageForCurrentFrame()) - return; - if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, dstRect, solidColor(), op); return; diff --git a/WebCore/platform/graphics/Image.h b/WebCore/platform/graphics/Image.h index 4ef5bf7..1419b2d 100644 --- a/WebCore/platform/graphics/Image.h +++ b/WebCore/platform/graphics/Image.h @@ -47,9 +47,15 @@ struct CGContext; #endif #if PLATFORM(WIN) +typedef struct tagSIZE SIZE; +typedef SIZE* LPSIZE; typedef struct HBITMAP__ *HBITMAP; #endif +#if PLATFORM(SKIA) +class NativeImageSkia; +#endif + #if PLATFORM(QT) #include <QPixmap> #endif @@ -73,15 +79,23 @@ class String; // This class gets notified when an image creates or destroys decoded frames and when it advances animation frames. class ImageObserver; -class Image : Noncopyable { +class Image : public RefCounted<Image> { + friend class GeneratedImage; friend class GraphicsContext; public: - Image(ImageObserver* = 0); virtual ~Image(); - static Image* loadPlatformResource(const char* name); + static PassRefPtr<Image> create(ImageObserver* = 0); + static PassRefPtr<Image> loadPlatformResource(const char* name); static bool supportsType(const String&); + virtual bool isBitmapImage() const { return false; } + + // Derived classes should override this if they can assure that + // the image contains only resources from its own security origin. + virtual bool hasSingleSecurityOrigin() const { return false; } + + static Image* nullImage(); bool isNull() const; // These are only used for SVGImage right now @@ -97,11 +111,9 @@ public: bool setData(PassRefPtr<SharedBuffer> data, bool allDataReceived); virtual bool dataChanged(bool allDataReceived) { return false; } - - // FIXME: PDF/SVG will be underreporting decoded sizes and will be unable to prune because these functions are not - // implemented yet for those image types. - virtual void destroyDecodedData(bool incremental = false) {}; - virtual unsigned decodedSize() const { return 0; } + + virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) = 0; + virtual unsigned decodedSize() const = 0; SharedBuffer* data() { return m_data.get(); } @@ -128,10 +140,6 @@ public: virtual CGImageRef getCGImageRef() { return 0; } #endif -#if PLATFORM(QT) - virtual QPixmap* getPixmap() const { return 0; } -#endif - #if PLATFORM(WIN) virtual bool getHBITMAP(HBITMAP) { return false; } virtual bool getHBITMAPOfSize(HBITMAP, LPSIZE) { return false; } @@ -143,9 +151,10 @@ public: #endif protected: + Image(ImageObserver* = 0); + static void fillWithSolidColor(GraphicsContext* ctxt, const FloatRect& dstRect, const Color& color, CompositeOperator op); -private: #if PLATFORM(WIN) virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator) { } #endif diff --git a/WebCore/platform/graphics/ImageBuffer.h b/WebCore/platform/graphics/ImageBuffer.h index c815c8d..7c68fc8 100644 --- a/WebCore/platform/graphics/ImageBuffer.h +++ b/WebCore/platform/graphics/ImageBuffer.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 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 @@ -27,69 +27,59 @@ #ifndef ImageBuffer_h #define ImageBuffer_h +#include "Image.h" #include "IntSize.h" +#include "ImageBufferData.h" #include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> #include <memory> -#if PLATFORM(CG) -typedef struct CGImage* CGImageRef; -#endif - -#if PLATFORM(QT) -#include <QPixmap> -class QPainter; -#endif - -#if PLATFORM(SGL) -#include <memory> -#endif - -#if PLATFORM(CAIRO) -typedef struct _cairo_surface cairo_surface_t; -#endif - namespace WebCore { class GraphicsContext; - class RenderObject; + class ImageData; + class IntPoint; + class IntRect; + class String; class ImageBuffer : Noncopyable { public: - static std::auto_ptr<ImageBuffer> create(const IntSize&, bool grayScale); + // Will return a null pointer on allocation failure. + static std::auto_ptr<ImageBuffer> create(const IntSize& size, bool grayScale) + { + bool success = false; + std::auto_ptr<ImageBuffer> buf(new ImageBuffer(size, grayScale, success)); + if (success) + return buf; + return std::auto_ptr<ImageBuffer>(); + } + ~ImageBuffer(); - IntSize size() const { return m_size; } + const IntSize& size() const { return m_size; } GraphicsContext* context() const; -#if PLATFORM(CG) - CGImageRef cgImage() const; -#elif PLATFORM(QT) - QPixmap* pixmap() const; -#elif PLATFORM(CAIRO) - cairo_surface_t* surface() const; -#endif + Image* image() const; + + void clearImage() { m_image.clear(); } + + PassRefPtr<ImageData> getImageData(const IntRect& rect) const; + void putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint); + + String toDataURL(const String& mimeType) const; private: - void* m_data; - IntSize m_size; + ImageBufferData m_data; + IntSize m_size; OwnPtr<GraphicsContext> m_context; + mutable RefPtr<Image> m_image; -#if PLATFORM(CG) - ImageBuffer(void* imageData, const IntSize&, std::auto_ptr<GraphicsContext>); - mutable CGImageRef m_cgImage; -#elif PLATFORM(QT) - ImageBuffer(const QPixmap &px); - mutable QPixmap m_pixmap; - mutable QPainter* m_painter; -#elif PLATFORM(CAIRO) - ImageBuffer(cairo_surface_t* surface); - mutable cairo_surface_t* m_surface; -#endif -#if PLATFORM(SGL) - ImageBuffer(const IntSize&, std::auto_ptr<GraphicsContext>); -#endif + // This constructor will place its succes into the given out-variable + // so that create() knows when it should return failure. + ImageBuffer(const IntSize&, bool grayScale, bool& success); }; -} -#endif +} // namespace WebCore + +#endif // ImageBuffer_h diff --git a/WebCore/platform/graphics/ImageSource.h b/WebCore/platform/graphics/ImageSource.h index c99b00f..a9f346d 100644 --- a/WebCore/platform/graphics/ImageSource.h +++ b/WebCore/platform/graphics/ImageSource.h @@ -36,7 +36,10 @@ typedef struct CGImageSource* CGImageSourceRef; typedef struct CGImage* CGImageRef; typedef const struct __CFData* CFDataRef; #elif PLATFORM(QT) +#include <qglobal.h> +QT_BEGIN_NAMESPACE class QPixmap; +QT_END_NAMESPACE #elif PLATFORM(CAIRO) struct _cairo_surface; typedef struct _cairo_surface cairo_surface_t; @@ -44,6 +47,8 @@ typedef struct _cairo_surface cairo_surface_t; #include "SkString.h" class SkBitmapRef; class PrivateAndroidImageSourceRec; +#elif PLATFORM(SKIA) +class NativeImageSkia; #endif namespace WebCore { @@ -71,10 +76,14 @@ struct NativeImageSourcePtr { }; typedef const Vector<char>* NativeBytePtr; typedef SkBitmapRef* NativeImagePtr; -#else +#elif PLATFORM(CAIRO) class ImageDecoder; typedef ImageDecoder* NativeImageSourcePtr; typedef cairo_surface_t* NativeImagePtr; +#elif PLATFORM(SKIA) +class ImageDecoder; +typedef ImageDecoder* NativeImageSourcePtr; +typedef NativeImageSkia* NativeImagePtr; #endif const int cAnimationLoopOnce = -1; @@ -93,7 +102,8 @@ public: bool isSizeAvailable(); IntSize size() const; - + IntSize frameSizeAtIndex(size_t) const; + int repetitionCount(); size_t frameCount() const; diff --git a/WebCore/platform/graphics/IntPoint.h b/WebCore/platform/graphics/IntPoint.h index bd34d2a..cb24b4e 100644 --- a/WebCore/platform/graphics/IntPoint.h +++ b/WebCore/platform/graphics/IntPoint.h @@ -45,7 +45,9 @@ typedef struct _NSPoint NSPoint; typedef struct tagPOINT POINT; typedef struct tagPOINTS POINTS; #elif PLATFORM(QT) +QT_BEGIN_NAMESPACE class QPoint; +QT_END_NAMESPACE #elif PLATFORM(GTK) typedef struct _GdkPoint GdkPoint; #endif @@ -57,6 +59,11 @@ class TPoint; class wxPoint; #endif +#if PLATFORM(SKIA) +struct SkPoint; +struct SkIPoint; +#endif + namespace WebCore { class IntPoint { @@ -72,6 +79,23 @@ public: void move(int dx, int dy) { m_x += dx; m_y += dy; } + IntPoint expandedTo(const IntPoint& other) const + { + return IntPoint(m_x > other.m_x ? m_x : other.m_x, + m_y > other.m_y ? m_y : other.m_y); + } + + IntPoint shrunkTo(const IntPoint& other) const + { + return IntPoint(m_x < other.m_x ? m_x : other.m_x, + m_y < other.m_y ? m_y : other.m_y); + } + + void clampNegativeToZero() + { + *this = expandedTo(IntPoint()); + } + #if PLATFORM(CG) explicit IntPoint(const CGPoint&); // don't do this implicitly since it's lossy operator CGPoint() const; @@ -104,6 +128,12 @@ public: operator wxPoint() const; #endif +#if PLATFORM(SKIA) + IntPoint(const SkIPoint&); + operator SkIPoint() const; + operator SkPoint() const; +#endif + private: int m_x, m_y; }; diff --git a/WebCore/platform/graphics/IntRect.h b/WebCore/platform/graphics/IntRect.h index 84adf20..03784a3 100644 --- a/WebCore/platform/graphics/IntRect.h +++ b/WebCore/platform/graphics/IntRect.h @@ -44,7 +44,9 @@ typedef struct _NSRect NSRect; #if PLATFORM(WIN) typedef struct tagRECT RECT; #elif PLATFORM(QT) +QT_BEGIN_NAMESPACE class QRect; +QT_END_NAMESPACE #elif PLATFORM(GTK) typedef struct _GdkRectangle GdkRectangle; #endif @@ -56,6 +58,11 @@ class TRect; class wxRect; #endif +#if PLATFORM(SKIA) +struct SkRect; +struct SkIRect; +#endif + namespace WebCore { class FloatRect; @@ -151,6 +158,12 @@ public: operator CGRect() const; #endif +#if PLATFORM(SKIA) + IntRect(const SkIRect&); + operator SkRect() const; + operator SkIRect() const; +#endif + #if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES) operator NSRect() const; #endif diff --git a/WebCore/platform/graphics/IntSize.h b/WebCore/platform/graphics/IntSize.h index faf58c6..7245408 100644 --- a/WebCore/platform/graphics/IntSize.h +++ b/WebCore/platform/graphics/IntSize.h @@ -43,7 +43,10 @@ typedef struct _NSSize NSSize; #if PLATFORM(WIN) typedef struct tagSIZE SIZE; #elif PLATFORM(QT) +#include <qglobal.h> +QT_BEGIN_NAMESPACE class QSize; +QT_END_NAMESPACE #endif #if PLATFORM(SYMBIAN) class TSize; diff --git a/WebCore/platform/graphics/IntSizeHash.h b/WebCore/platform/graphics/IntSizeHash.h index 142533e..ad6eac3 100644 --- a/WebCore/platform/graphics/IntSizeHash.h +++ b/WebCore/platform/graphics/IntSizeHash.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006, 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 @@ -31,7 +31,6 @@ namespace WTF { template<> struct IntHash<IntSize> { static unsigned hash(const IntSize& key) { return intHash((static_cast<uint64_t>(key.width()) << 32 | key.height())); } static bool equal(const IntSize& a, const IntSize& b) { return a == b; } - static const bool safeToCompareToEmptyOrDeleted = true; }; template<> struct DefaultHash<IntSize> { typedef IntHash<IntSize> Hash; }; @@ -39,8 +38,8 @@ namespace WTF { template<> struct HashTraits<IntSize> : GenericHashTraits<IntSize> { static const bool emptyValueIsZero = true; static const bool needsDestruction = false; - static const bool needsRef = false; - static IntSize deletedValue() { return IntSize(-1, -1); } + static void constructDeletedValue(IntSize& slot) { new (&slot) IntSize(-1, -1); } + static bool isDeletedValue(const IntSize& value) { return value.width() == -1 && value.height() == -1; } }; } // namespace WTF diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index 82e9950..21e31fc 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -30,6 +30,9 @@ #include "IntRect.h" #include "MIMETypeRegistry.h" +#include "FrameView.h" +#include "Frame.h" +#include "Document.h" #if PLATFORM(MAC) #include "MediaPlayerPrivateQTKit.h" @@ -37,6 +40,10 @@ #include "MediaPlayerPrivateQuickTimeWin.h" #elif PLATFORM(GTK) #include "MediaPlayerPrivateGStreamer.h" +#elif PLATFORM(QT) +#include "MediaPlayerPrivatePhonon.h" +#elif PLATFORM(CHROMIUM) +#include "MediaPlayerPrivateChromium.h" #endif namespace WebCore { @@ -44,10 +51,10 @@ namespace WebCore { MediaPlayer::MediaPlayer(MediaPlayerClient* client) : m_mediaPlayerClient(client) , m_private(new MediaPlayerPrivate(this)) - , m_parentWidget(0) + , m_frameView(0) , m_visible(false) , m_rate(1.0f) - , m_volume(0.5f) + , m_volume(1.0f) { } @@ -56,7 +63,7 @@ MediaPlayer::~MediaPlayer() delete m_private; } -void MediaPlayer::load(String url) +void MediaPlayer::load(const String& url) { m_private->load(url); } @@ -111,6 +118,14 @@ bool MediaPlayer::hasVideo() return m_private->hasVideo(); } +bool MediaPlayer::inMediaDocument() +{ + Frame* frame = m_frameView ? m_frameView->frame() : 0; + Document* document = frame ? frame->document() : 0; + + return document && document->isMediaDocument(); +} + MediaPlayer::NetworkState MediaPlayer::networkState() { return m_private->networkState(); @@ -128,10 +143,8 @@ float MediaPlayer::volume() const void MediaPlayer::setVolume(float volume) { - if (volume != m_volume) { - m_volume = volume; - m_private->setVolume(volume); - } + m_volume = volume; + m_private->setVolume(volume); } float MediaPlayer::rate() const @@ -141,8 +154,6 @@ float MediaPlayer::rate() const void MediaPlayer::setRate(float rate) { - if (rate == m_rate) - return; m_rate = rate; m_private->setRate(rate); } @@ -184,8 +195,6 @@ unsigned MediaPlayer::totalBytes() void MediaPlayer::setRect(const IntRect& r) { - if (m_rect == r) - return; m_rect = r; m_private->setRect(r); } @@ -197,8 +206,6 @@ bool MediaPlayer::visible() const void MediaPlayer::setVisible(bool b) { - if (m_visible == b) - return; m_visible = b; m_private->setVisible(b); } @@ -208,6 +215,13 @@ void MediaPlayer::paint(GraphicsContext* p, const IntRect& r) m_private->paint(p, r); } +bool MediaPlayer::supportsType(const String& type) +{ + HashSet<String> types; + getSupportedTypes(types); + return MIMETypeRegistry::isSupportedMediaMIMEType(type) && types.contains(type); +} + void MediaPlayer::getSupportedTypes(HashSet<String>& types) { MediaPlayerPrivate::getSupportedTypes(types); diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 2c78664..1beab95 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -35,15 +35,14 @@ namespace WebCore { +class FrameView; class GraphicsContext; class IntSize; class MediaPlayer; class MediaPlayerPrivate; class String; -class Widget; -class MediaPlayerClient -{ +class MediaPlayerClient { public: virtual ~MediaPlayerClient() { } virtual void mediaPlayerNetworkStateChanged(MediaPlayer*) { } @@ -59,18 +58,19 @@ public: virtual ~MediaPlayer(); static bool isAvailable(); + static bool supportsType(const String&); static void getSupportedTypes(HashSet<String>&); IntSize naturalSize(); bool hasVideo(); - Widget* parentWidget() const { return m_parentWidget; } - void setParentWidget(Widget* parent) { m_parentWidget = parent; } + void setFrameView(FrameView* frameView) { m_frameView = frameView; } + bool inMediaDocument(); IntRect rect() const { return m_rect; } void setRect(const IntRect& r); - void load(String url); + void load(const String& url); void cancelLoad(); bool visible() const; @@ -124,7 +124,7 @@ private: MediaPlayerClient* m_mediaPlayerClient; MediaPlayerPrivate* m_private; - Widget* m_parentWidget; + FrameView* m_frameView; IntRect m_rect; bool m_visible; float m_rate; diff --git a/WebCore/platform/graphics/Path.h b/WebCore/platform/graphics/Path.h index d63ba0b..06e6ee4 100644 --- a/WebCore/platform/graphics/Path.h +++ b/WebCore/platform/graphics/Path.h @@ -30,7 +30,10 @@ #if PLATFORM(CG) typedef struct CGPath PlatformPath; #elif PLATFORM(QT) +#include <qglobal.h> +QT_BEGIN_NAMESPACE class QPainterPath; +QT_END_NAMESPACE typedef QPainterPath PlatformPath; #elif PLATFORM(SGL) class SkPath; @@ -43,6 +46,9 @@ namespace WebCore { struct CairoPath; } typedef WebCore::CairoPath PlatformPath; +#elif PLATFORM(SKIA) +class SkPath; +typedef SkPath PlatformPath; #else typedef void PlatformPath; #endif @@ -95,8 +101,8 @@ namespace WebCore { void moveTo(const FloatPoint&); void addLineTo(const FloatPoint&); - void addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& point); - void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint&); + void addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint); + void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint); void addArcTo(const FloatPoint&, const FloatPoint&, float radius); void closeSubpath(); @@ -106,9 +112,6 @@ namespace WebCore { void translate(const FloatSize&); - void setWindingRule(WindRule rule) { m_rule = rule; } - WindRule windingRule() const { return m_rule; } - String debugString() const; PlatformPath* platformPath() const { return m_path; } @@ -125,7 +128,6 @@ namespace WebCore { private: PlatformPath* m_path; - WindRule m_rule; }; } diff --git a/WebCore/platform/graphics/Pattern.cpp b/WebCore/platform/graphics/Pattern.cpp new file mode 100644 index 0000000..d388bd7 --- /dev/null +++ b/WebCore/platform/graphics/Pattern.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Pattern.h" + +#include "Image.h" + +namespace WebCore { + +Pattern::Pattern(Image* image, bool repeatX, bool repeatY) + : m_tileImage(image) + , m_repeatX(repeatX) + , m_repeatY(repeatY) +{ + ASSERT(image); +} + +Pattern::~Pattern() +{ +} + +} diff --git a/WebCore/platform/graphics/Pattern.h b/WebCore/platform/graphics/Pattern.h new file mode 100644 index 0000000..985c7c0 --- /dev/null +++ b/WebCore/platform/graphics/Pattern.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Pattern_h +#define Pattern_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +#if PLATFORM(CG) +typedef struct CGPattern* CGPatternRef; +typedef CGPatternRef PlatformPatternPtr; +#elif PLATFORM(CAIRO) +#include <cairo.h> +typedef cairo_pattern_t* PlatformPatternPtr; +#elif PLATFORM(SKIA) || PLATFORM(SGL) +class SkShader; +typedef SkShader* PlatformPatternPtr; +#elif PLATFORM(QT) +#include <QBrush> +typedef QBrush PlatformPatternPtr; +#elif PLATFORM(WX) +#if USE(WXGC) +class wxGraphicsBrush; +typedef wxGraphicsBrush* PlatformPatternPtr; +#else +class wxBrush; +typedef wxBrush* PlatformPatternPtr; +#endif // USE(WXGC) +#endif + +namespace WebCore { + class AffineTransform; + class Image; + + class Pattern : public RefCounted<Pattern> { + public: + static PassRefPtr<Pattern> create(Image* tileImage, bool repeatX, bool repeatY) + { + return adoptRef(new Pattern(tileImage, repeatX, repeatY)); + } + virtual ~Pattern(); + + Image* tileImage() const { return m_tileImage.get(); } + + PlatformPatternPtr createPlatformPattern(const AffineTransform& patternTransform) const; + + private: + Pattern(Image*, bool repeatX, bool repeatY); + + RefPtr<Image> m_tileImage; + bool m_repeatX; + bool m_repeatY; + }; + +} //namespace + +#endif diff --git a/WebCore/platform/graphics/SimpleFontData.cpp b/WebCore/platform/graphics/SimpleFontData.cpp index 24812f7..372fcc8 100644 --- a/WebCore/platform/graphics/SimpleFontData.cpp +++ b/WebCore/platform/graphics/SimpleFontData.cpp @@ -30,6 +30,7 @@ #include "config.h" #include "SimpleFontData.h" +#include "FontCache.h" #if ENABLE(SVG_FONTS) #include "SVGFontData.h" #include "SVGFontFaceElement.h" @@ -115,13 +116,16 @@ SimpleFontData::SimpleFontData(const FontPlatformData& f, bool customFont, bool SimpleFontData::~SimpleFontData() { + if (!isCustomFont()) { + if (m_smallCapsFontData) + FontCache::releaseFontData(m_smallCapsFontData); + GlyphPageTreeNode::pruneTreeFontData(this); + } + #if ENABLE(SVG_FONTS) && !PLATFORM(QT) if (!m_svgFontData || !m_svgFontData->svgFontFaceElement()) #endif platformDestroy(); - - // We only get deleted when the cache gets cleared. Since the smallCapsRenderer is also in that cache, - // it will be deleted then, so we don't need to do anything here. } float SimpleFontData::widthForGlyph(Glyph glyph) const diff --git a/WebCore/platform/graphics/SimpleFontData.h b/WebCore/platform/graphics/SimpleFontData.h index b2eec1f..5f26cbf 100644 --- a/WebCore/platform/graphics/SimpleFontData.h +++ b/WebCore/platform/graphics/SimpleFontData.h @@ -1,7 +1,7 @@ /* * This file is part of the internal font implementation. * - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006, 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 @@ -29,7 +29,7 @@ #include "GlyphWidthMap.h" #include <wtf/OwnPtr.h> -#if PLATFORM(MAC) +#if USE(ATSUI) typedef struct OpaqueATSUStyle* ATSUStyle; #endif @@ -37,6 +37,10 @@ typedef struct OpaqueATSUStyle* ATSUStyle; #include <usp10.h> #endif +#if PLATFORM(CAIRO) +#include <cairo.h> +#endif + namespace WebCore { class FontDescription; @@ -88,6 +92,14 @@ public: #if PLATFORM(MAC) NSFont* getNSFont() const { return m_font.font(); } +#endif + +#if USE(CORE_TEXT) + CTFontRef getCTFont() const; + CFDictionaryRef getCFStringAttributes() const; +#endif + +#if USE(ATSUI) void checkShapesArabic() const; bool shapesArabic() const { @@ -106,7 +118,7 @@ public: static bool shouldApplyMacAscentHack(); #endif -#if PLATFORM(GTK) +#if PLATFORM(CAIRO) void setFont(cairo_t*) const; #endif @@ -120,6 +132,12 @@ private: void commonInit(); +#if PLATFORM(WIN) + void initGDIFont(); + void platformCommonDestroy(); + float widthForGDIGlyph(Glyph glyph) const; +#endif + public: int m_ascent; int m_descent; @@ -149,12 +167,17 @@ public: mutable SimpleFontData* m_smallCapsFontData; -#if PLATFORM(CG) +#if PLATFORM(CG) || PLATFORM(WIN) float m_syntheticBoldOffset; #endif #if PLATFORM(MAC) +#ifdef BUILDING_ON_TIGER void* m_styleGroup; +#endif +#endif + +#if USE(ATSUI) mutable ATSUStyle m_ATSUStyle; mutable bool m_ATSUStyleInitialized; mutable bool m_ATSUMirrors; @@ -162,6 +185,11 @@ public: mutable bool m_shapesArabic; #endif +#if USE(CORE_TEXT) + mutable RetainPtr<CTFontRef> m_CTFont; + mutable RetainPtr<CFDictionaryRef> m_CFStringAttributes; +#endif + #if PLATFORM(WIN) bool m_isSystemFont; mutable SCRIPT_CACHE m_scriptCache; diff --git a/WebCore/platform/graphics/StringTruncator.cpp b/WebCore/platform/graphics/StringTruncator.cpp index 0490a9f..b6c86ce 100644 --- a/WebCore/platform/graphics/StringTruncator.cpp +++ b/WebCore/platform/graphics/StringTruncator.cpp @@ -31,7 +31,6 @@ #include "CharacterNames.h" #include "Font.h" -#include "FontPlatformData.h" #include "TextBreakIterator.h" #include <wtf/Assertions.h> #include <wtf/Vector.h> diff --git a/WebCore/platform/graphics/TextRun.h b/WebCore/platform/graphics/TextRun.h new file mode 100644 index 0000000..166b047 --- /dev/null +++ b/WebCore/platform/graphics/TextRun.h @@ -0,0 +1,126 @@ +/* + * 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, 2006, 2007 Apple Computer, Inc. + * + * 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 TextRun_h +#define TextRun_h + +#include "PlatformString.h" + +namespace WebCore { + +class RenderObject; +class SVGPaintServer; + +class TextRun { +public: + TextRun(const UChar* c, int len, bool allowTabs = false, int xpos = 0, int padding = 0, bool rtl = false, bool directionalOverride = false, + bool applyRunRounding = true, bool applyWordRounding = true) + : m_characters(c) + , m_len(len) + , m_xpos(xpos) + , m_padding(padding) + , m_allowTabs(allowTabs) + , m_rtl(rtl) + , m_directionalOverride(directionalOverride) + , m_applyRunRounding(applyRunRounding) + , m_applyWordRounding(applyWordRounding) + , m_disableSpacing(false) +#if ENABLE(SVG_FONTS) + , m_referencingRenderObject(0) + , m_activePaintServer(0) +#endif + { + } + + TextRun(const String& s, bool allowTabs = false, int xpos = 0, int padding = 0, bool rtl = false, bool directionalOverride = false, + bool applyRunRounding = true, bool applyWordRounding = true) + : m_characters(s.characters()) + , m_len(s.length()) + , m_xpos(xpos) + , m_padding(padding) + , m_allowTabs(allowTabs) + , m_rtl(rtl) + , m_directionalOverride(directionalOverride) + , m_applyRunRounding(applyRunRounding) + , m_applyWordRounding(applyWordRounding) + , m_disableSpacing(false) +#if ENABLE(SVG_FONTS) + , m_referencingRenderObject(0) + , m_activePaintServer(0) +#endif + { + } + + UChar operator[](int i) const { return m_characters[i]; } + const UChar* data(int i) const { return &m_characters[i]; } + + const UChar* characters() const { return m_characters; } + int length() const { return m_len; } + + void setText(const UChar* c, int len) { m_characters = c; m_len = len; } + + bool allowTabs() const { return m_allowTabs; } + int xPos() const { return m_xpos; } + int padding() const { return m_padding; } + bool rtl() const { return m_rtl; } + bool ltr() const { return !m_rtl; } + bool directionalOverride() const { return m_directionalOverride; } + bool applyRunRounding() const { return m_applyRunRounding; } + bool applyWordRounding() const { return m_applyWordRounding; } + bool spacingDisabled() const { return m_disableSpacing; } + + void disableSpacing() { m_disableSpacing = true; } + void disableRoundingHacks() { m_applyRunRounding = m_applyWordRounding = false; } + void setRTL(bool b) { m_rtl = b; } + void setDirectionalOverride(bool override) { m_directionalOverride = override; } + +#if ENABLE(SVG_FONTS) + RenderObject* referencingRenderObject() const { return m_referencingRenderObject; } + void setReferencingRenderObject(RenderObject* object) { m_referencingRenderObject = object; } + + SVGPaintServer* activePaintServer() const { return m_activePaintServer; } + void setActivePaintServer(SVGPaintServer* object) { m_activePaintServer = object; } +#endif + +private: + const UChar* m_characters; + int m_len; + + int m_xpos; + int m_padding; + bool m_allowTabs; + bool m_rtl; + bool m_directionalOverride; + bool m_applyRunRounding; + bool m_applyWordRounding; + bool m_disableSpacing; + +#if ENABLE(SVG_FONTS) + RenderObject* m_referencingRenderObject; + SVGPaintServer* m_activePaintServer; +#endif +}; + +} + +#endif diff --git a/WebCore/platform/graphics/UnitBezier.h b/WebCore/platform/graphics/UnitBezier.h new file mode 100644 index 0000000..973d75b --- /dev/null +++ b/WebCore/platform/graphics/UnitBezier.h @@ -0,0 +1,123 @@ +/* + * 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 UnitBezier_h +#define UnitBezier_h + +#include <math.h> + +namespace WebCore { + + struct UnitBezier { + UnitBezier(double p1x, double p1y, double p2x, double p2y) + { + // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). + cx = 3.0 * p1x; + bx = 3.0 * (p2x - p1x) - cx; + ax = 1.0 - cx -bx; + + cy = 3.0 * p1y; + by = 3.0 * (p2y - p1y) - cy; + ay = 1.0 - cy - by; + } + + double sampleCurveX(double t) + { + // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. + return ((ax * t + bx) * t + cx) * t; + } + + double sampleCurveY(double t) + { + return ((ay * t + by) * t + cy) * t; + } + + double sampleCurveDerivativeX(double t) + { + return (3.0 * ax * t + 2.0 * bx) * t + cx; + } + + // Given an x value, find a parametric value it came from. + double solveCurveX(double x, double epsilon) + { + double t0; + double t1; + double t2; + double x2; + double d2; + int i; + + // First try a few iterations of Newton's method -- normally very fast. + for (t2 = x, i = 0; i < 8; i++) { + x2 = sampleCurveX(t2) - x; + if (fabs (x2) < epsilon) + return t2; + d2 = sampleCurveDerivativeX(t2); + if (fabs(d2) < 1e-6) + break; + t2 = t2 - x2 / d2; + } + + // Fall back to the bisection method for reliability. + t0 = 0.0; + t1 = 1.0; + t2 = x; + + if (t2 < t0) + return t0; + if (t2 > t1) + return t1; + + while (t0 < t1) { + x2 = sampleCurveX(t2); + if (fabs(x2 - x) < epsilon) + return t2; + if (x > x2) + t0 = t2; + else + t1 = t2; + t2 = (t1 - t0) * .5 + t0; + } + + // Failure. + return t2; + } + + double solve(double x, double epsilon) + { + return sampleCurveY(solveCurveX(x, epsilon)); + } + + private: + double ax; + double bx; + double cx; + + double ay; + double by; + double cy; + }; +} +#endif diff --git a/WebCore/platform/graphics/WidthIterator.cpp b/WebCore/platform/graphics/WidthIterator.cpp new file mode 100644 index 0000000..a66b234 --- /dev/null +++ b/WebCore/platform/graphics/WidthIterator.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * 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 "WidthIterator.h" + +#include "Font.h" +#include "GlyphBuffer.h" +#include "SimpleFontData.h" +#include <wtf/MathExtras.h> + +#if USE(ICU_UNICODE) +#include <unicode/unorm.h> +#endif + +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +// According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values +static const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8; + +WidthIterator::WidthIterator(const Font* font, const TextRun& run) + : m_font(font) + , m_run(run) + , m_end(run.length()) + , m_currentCharacter(0) + , m_runWidthSoFar(0) + , m_finalRoundingWidth(0) +{ + // If the padding is non-zero, count the number of spaces in the run + // and divide that by the padding for per space addition. + m_padding = m_run.padding(); + if (!m_padding) + m_padPerSpace = 0; + else { + float numSpaces = 0; + for (int i = 0; i < run.length(); i++) + if (Font::treatAsSpace(m_run[i])) + numSpaces++; + + if (numSpaces == 0) + m_padPerSpace = 0; + else + m_padPerSpace = ceilf(m_run.padding() / numSpaces); + } +} + +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS +#define SIGNAL_ADJUSTED_WIDTHS() adjustedWidths = true +bool WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) +#else +#define SIGNAL_ADJUSTED_WIDTHS() +void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) +#endif +{ + if (offset > m_end) + offset = m_end; + + int currentCharacter = m_currentCharacter; + const UChar* cp = m_run.data(currentCharacter); + + bool rtl = m_run.rtl(); + bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_padding) && !m_run.spacingDisabled(); + + float runWidthSoFar = m_runWidthSoFar; + float lastRoundingWidth = m_finalRoundingWidth; + +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + bool adjustedWidths = false; +#endif + while (currentCharacter < offset) { + UChar32 c = *cp; + unsigned clusterLength = 1; + if (c >= 0x3041) { + if (c <= 0x30FE) { + // Deal with Hiragana and Katakana voiced and semi-voiced syllables. + // Normalize into composed form, and then look for glyph with base + combined mark. + // Check above for character range to minimize performance impact. + UChar32 normalized = normalizeVoicingMarks(currentCharacter); + if (normalized) { + c = normalized; + clusterLength = 2; + } + } else if (U16_IS_SURROGATE(c)) { + if (!U16_IS_SURROGATE_LEAD(c)) + break; + + // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) + // code point before glyph lookup. + // Make sure we have another character and it's a low surrogate. + if (currentCharacter + 1 >= m_run.length()) + break; + UChar low = cp[1]; + if (!U16_IS_TRAIL(low)) + break; + c = U16_GET_SUPPLEMENTARY(c, low); + clusterLength = 2; + } + } + + const GlyphData& glyphData = m_font->glyphDataForCharacter(c, rtl); + Glyph glyph = glyphData.glyph; + const SimpleFontData* fontData = glyphData.fontData; + + ASSERT(fontData); + + // Now that we have a glyph and font data, get its width. + float width; + if (c == '\t' && m_run.allowTabs()) { + float tabWidth = m_font->tabWidth(); + width = tabWidth - fmodf(m_run.xPos() + runWidthSoFar, tabWidth); + SIGNAL_ADJUSTED_WIDTHS(); + } else { + width = fontData->widthForGlyph(glyph); +#ifndef ANDROID_NEVER_ROUND_FONT_METRICS + // We special case spaces in two ways when applying word rounding. + // First, we round spaces to an adjusted width in all fonts. + // Second, in fixed-pitch fonts we ensure that all characters that + // match the width of the space character have the same width as the space character. + if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding()) { + width = fontData->m_adjustedSpaceWidth; + SIGNAL_ADJUSTED_WIDTHS(); + } +#endif + } + + if (hasExtraSpacing) { + // Account for letter-spacing. + if (width && m_font->letterSpacing()) { + width += m_font->letterSpacing(); + SIGNAL_ADJUSTED_WIDTHS(); + } + + if (Font::treatAsSpace(c)) { + // Account for padding. WebCore uses space padding to justify text. + // We distribute the specified padding over the available spaces in the run. + if (m_padding) { + // Use left over padding if not evenly divisible by number of spaces. + if (m_padding < m_padPerSpace) { + width += m_padding; + m_padding = 0; + } else { + width += m_padPerSpace; + m_padding -= m_padPerSpace; + } + SIGNAL_ADJUSTED_WIDTHS(); + } + + // Account for word spacing. + // We apply additional space between "words" by adding width to the space character. + if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) { + width += m_font->wordSpacing(); + SIGNAL_ADJUSTED_WIDTHS(); + } + } + } + + // Advance past the character we just dealt with. + cp += clusterLength; + currentCharacter += clusterLength; + + // Account for float/integer impedance mismatch between CG and KHTML. "Words" (characters + // followed by a character defined by isRoundingHackCharacter()) are always an integer width. + // We adjust the width of the last character of a "word" to ensure an integer width. + // If we move KHTML to floats we can remove this (and related) hacks. + + float oldWidth = width; + +#ifndef ANDROID_NEVER_ROUND_FONT_METRICS + // Force characters that are used to determine word boundaries for the rounding hack + // to be integer width, so following words will start on an integer boundary. + if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c)) { + width = ceilf(width); + SIGNAL_ADJUSTED_WIDTHS(); + } + + // Check to see if the next character is a "rounding hack character", if so, adjust + // width so that the total run width will be on an integer boundary. + if ((m_run.applyWordRounding() && currentCharacter < m_run.length() && Font::isRoundingHackCharacter(*cp)) + || (m_run.applyRunRounding() && currentCharacter >= m_end)) { + float totalWidth = runWidthSoFar + width; + width += ceilf(totalWidth) - totalWidth; + SIGNAL_ADJUSTED_WIDTHS(); + } +#endif + + runWidthSoFar += width; + + if (glyphBuffer) + glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width)); + + lastRoundingWidth = width - oldWidth; + } + + m_currentCharacter = currentCharacter; + m_runWidthSoFar = runWidthSoFar; + m_finalRoundingWidth = lastRoundingWidth; + +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + return adjustedWidths; +#endif +} + +bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer) +{ + glyphBuffer->clear(); + advance(m_currentCharacter + 1, glyphBuffer); + float w = 0; + for (int i = 0; i < glyphBuffer->size(); ++i) + w += glyphBuffer->advanceAt(i); + width = w; + return !glyphBuffer->isEmpty(); +} + +UChar32 WidthIterator::normalizeVoicingMarks(int currentCharacter) +{ + if (currentCharacter + 1 < m_end) { + if (combiningClass(m_run[currentCharacter + 1]) == hiraganaKatakanaVoicingMarksCombiningClass) { +#if USE(ICU_UNICODE) + // Normalize into composed form using 3.2 rules. + UChar normalizedCharacters[2] = { 0, 0 }; + UErrorCode uStatus = U_ZERO_ERROR; + int32_t resultLength = unorm_normalize(m_run.data(currentCharacter), 2, + UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uStatus); + if (resultLength == 1 && uStatus == 0) + return normalizedCharacters[0]; +#elif USE(QT4_UNICODE) + QString tmp(reinterpret_cast<const QChar*>(m_run.data(currentCharacter)), 2); + QString res = tmp.normalized(QString::NormalizationForm_C, QChar::Unicode_3_2); + if (res.length() == 1) + return res.at(0).unicode(); +#endif + } + } + return 0; +} + +} diff --git a/WebCore/platform/graphics/WidthIterator.h b/WebCore/platform/graphics/WidthIterator.h new file mode 100644 index 0000000..a0eb26d --- /dev/null +++ b/WebCore/platform/graphics/WidthIterator.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * 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. + * + */ + +#ifndef WidthIterator_h +#define WidthIterator_h + +#include <wtf/unicode/Unicode.h> + +namespace WebCore { + +class Font; +class GlyphBuffer; +class TextRun; + +struct WidthIterator { + WidthIterator(const Font*, const TextRun&); + +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + bool advance(int to, GlyphBuffer* = 0); +#else + void advance(int to, GlyphBuffer* = 0); +#endif + bool advanceOneCharacter(float& width, GlyphBuffer* = 0); + + const Font* m_font; + + const TextRun& m_run; + int m_end; + + unsigned m_currentCharacter; + float m_runWidthSoFar; + float m_padding; + float m_padPerSpace; + float m_finalRoundingWidth; + +private: + UChar32 normalizeVoicingMarks(int currentCharacter); +}; + +} + +#endif diff --git a/WebCore/platform/graphics/android/AffineTransformAndroid.cpp b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp index d00aa40..6c5abae 100644 --- a/WebCore/platform/graphics/android/AffineTransformAndroid.cpp +++ b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp @@ -31,6 +31,8 @@ AffineTransform::AffineTransform() { m_transform.reset(); } + +AffineTransform::AffineTransform(const SkMatrix& mat) : m_transform(mat) {} AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) { @@ -100,6 +102,48 @@ void AffineTransform::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 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)); + } + AffineTransform &AffineTransform::scale(double sx, double sy) { m_transform.preScale(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); diff --git a/WebCore/platform/graphics/android/FontAndroid.cpp b/WebCore/platform/graphics/android/FontAndroid.cpp index 340d1c1..d53c3ea 100644 --- a/WebCore/platform/graphics/android/FontAndroid.cpp +++ b/WebCore/platform/graphics/android/FontAndroid.cpp @@ -34,6 +34,7 @@ #include "IntRect.h" #include "SkCanvas.h" +#include "SkLayerDrawLooper.h" #include "SkPaint.h" #include "SkTemplates.h" #include "SkTypeface.h" @@ -41,15 +42,78 @@ namespace WebCore { +static void updateForFont(SkPaint* paint, const SimpleFontData* font) { + font->platformData().setupPaint(paint); + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); +} + +static SkPaint* setupFill(SkPaint* paint, GraphicsContext* gc, + const SimpleFontData* font) { + gc->setupFillPaint(paint); + updateForFont(paint, font); + return paint; +} + +static SkPaint* setupStroke(SkPaint* paint, GraphicsContext* gc, + const SimpleFontData* font) { + gc->setupStrokePaint(paint); + updateForFont(paint, font); + return paint; +} + +static bool setupForText(SkPaint* paint, GraphicsContext* gc, + const SimpleFontData* font) { + int mode = gc->textDrawingMode(); + + if ((mode & (cTextFill | cTextStroke)) == (cTextFill | cTextStroke)) { + SkLayerDrawLooper* looper = new SkLayerDrawLooper; + paint->setLooper(looper)->unref(); + + // we clear the looper, in case we have a shadow + + SkPaint* fillP = NULL; + SkPaint* strokeP = NULL; + if (gc->willStroke()) { + strokeP = setupStroke(looper->addLayer(), gc, font); + strokeP->setLooper(NULL); + } + if (gc->willFill()) { + fillP = setupFill(looper->addLayer(), gc, font); + fillP->setLooper(NULL); + } + + SkPaint shadowPaint; + SkPoint offset; + if (gc->setupShadowPaint(&shadowPaint, &offset)) { + SkPaint* p = looper->addLayer(offset.fX, offset.fY); + *p = shadowPaint; + if (strokeP && !fillP) { + // stroke the shadow if we have stroke but no fill + p->setStyle(SkPaint::kStroke_Style); + p->setStrokeWidth(strokeP->getStrokeWidth()); + } + updateForFont(p, font); + } + } else if (mode & cTextFill) { + (void)setupFill(paint, gc, font); + } else if (mode & cTextStroke) { + (void)setupStroke(paint, gc, font); + } else { + return false; + } + return true; +} + void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { - SkCanvas* canvas = gc->platformContext()->mCanvas; - SkPaint paint; + SkPaint paint; - font->platformData().setupPaint(&paint); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setColor(gc->fillColor().rgb()); + if (!setupForText(&paint, gc, font)) { + return; + } + + SkCanvas* canvas = gc->platformContext()->mCanvas; SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert @@ -57,6 +121,7 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS if (glyphBuffer.hasAdjustedWidths()) { const GlyphBufferAdvance* adv = glyphBuffer.advances(from); SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); @@ -68,9 +133,9 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, y += SkFloatToScalar(adv[i].height()); } canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); - } else { + } else +#endif canvas->drawText(glyphs, numGlyphs << 1, x, y, paint); - } } FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int, int) const @@ -95,16 +160,9 @@ void Font::drawComplexText(GraphicsContext* gc, TextRun const& run, FloatPoint c SkCanvas* canvas = gc->platformContext()->mCanvas; SkPaint paint; - primaryFont()->platformData().setupPaint(&paint); - paint.setColor(gc->fillColor().rgb()); - -#if 0 - int n = run.to() - run.from(); -printf("------------- complex draw %d chars", n); - for (int i = 0; i < n; i++) - printf(" %04X", run.data(run.from())[i]); - printf("\n"); -#endif + if (!setupForText(&paint, gc, primaryFont())) { + return; + } canvas->drawText(run.characters(), run.length() << 1, SkFloatToScalar(point.x()), SkFloatToScalar(point.y()), diff --git a/WebCore/platform/graphics/android/FontCacheAndroid.cpp b/WebCore/platform/graphics/android/FontCacheAndroid.cpp index 903159e..387e486 100644 --- a/WebCore/platform/graphics/android/FontCacheAndroid.cpp +++ b/WebCore/platform/graphics/android/FontCacheAndroid.cpp @@ -30,7 +30,8 @@ #include "FontCache.h" #include "FontPlatformData.h" #include "Font.h" - +#include "NotImplemented.h" +#include "SimpleFontData.h" #include "SkPaint.h" #include "SkTypeface.h" #include "SkUtils.h" @@ -43,33 +44,9 @@ void FontCache::platformInit() const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) { - return font.primaryFont(); // do I need to make a copy (i.e. does the caller delete what I return? - -#if 0 - // IMLangFontLink::MapFont Method does what we want. - IMLangFontLink2* langFontLink = getFontLinkInterface(); - if (!langFontLink) - return 0; - - FontData* fontData = 0; - HDC hdc = GetDC(0); - DWORD fontCodePages; - langFontLink->GetFontCodePages(hdc, font.primaryFont()->m_font.hfont(), &fontCodePages); - - DWORD actualCodePages; - long cchActual; - langFontLink->GetStrCodePages(characters, length, fontCodePages, &actualCodePages, &cchActual); - if (cchActual) { - HFONT result; - if (langFontLink->MapFont(hdc, actualCodePages, characters[0], &result) == S_OK) { - fontData = new FontData(FontPlatformData(result, font.fontDescription().computedPixelSize())); - fontData->setIsMLangFont(); - } - } - - ReleaseDC(0, hdc); - return fontData; -#endif + // since all of our fonts logically map to the fallback, we can always claim + // that each font supports all characters. + return font.primaryFont(); } FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) @@ -96,12 +73,6 @@ static char* AtomicStringToUTF8String(const AtomicString& utf16) return utf8; } -bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family) -{ - ASSERT(0); // FIXME HACK unimplemented - return false; -} - FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) { char* storage = 0; @@ -136,7 +107,7 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD } int style = SkTypeface::kNormal; - if (fontDescription.weight() >= cBoldWeight) + if (fontDescription.weight() >= FontWeightBold) style |= SkTypeface::kBold; if (fontDescription.italic()) style |= SkTypeface::kItalic; @@ -152,5 +123,11 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD return result; } + // new as of SVN change 36269, Sept 8, 2008 +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) +{ + // Don't understand this yet, but it seems safe to leave unimplemented +} + } diff --git a/WebCore/platform/graphics/android/FontCustomPlatformData.cpp b/WebCore/platform/graphics/android/FontCustomPlatformData.cpp index eea5e36..8519861 100644 --- a/WebCore/platform/graphics/android/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/android/FontCustomPlatformData.cpp @@ -36,7 +36,8 @@ FontCustomPlatformData::~FontCustomPlatformData() // the unref is enough to release the font data... } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, + FontRenderingMode mode) { // turn bold/italic into fakeBold/fakeItalic if (m_typeface != NULL) { diff --git a/WebCore/platform/graphics/android/FontCustomPlatformData.h b/WebCore/platform/graphics/android/FontCustomPlatformData.h index f072b6f..d31f623 100644 --- a/WebCore/platform/graphics/android/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/android/FontCustomPlatformData.h @@ -18,6 +18,7 @@ #define FontCustomPlatformData_h_ #include <wtf/Noncopyable.h> +#include "FontRenderingMode.h" class SkTypeface; @@ -33,7 +34,7 @@ namespace WebCore { SkTypeface* typeface() const { return m_typeface; } - FontPlatformData fontPlatformData(int size, bool bold, bool italic); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode ); private: SkTypeface* m_typeface; diff --git a/WebCore/platform/graphics/android/FontPlatformData.h b/WebCore/platform/graphics/android/FontPlatformData.h index d6933d9..d3f5923 100644 --- a/WebCore/platform/graphics/android/FontPlatformData.h +++ b/WebCore/platform/graphics/android/FontPlatformData.h @@ -24,16 +24,16 @@ #ifndef FontPlatformData_H #define FontPlatformData_H +#include "StringImpl.h" + class SkPaint; class SkTypeface; namespace WebCore { -class FontPlatformData -{ +class FontPlatformData { public: - static FontPlatformData Deleted() - { + static FontPlatformData Deleted() { return FontPlatformData(NULL, -1, false, false); } @@ -43,6 +43,12 @@ public: FontPlatformData(const FontPlatformData& src, float textSize); ~FontPlatformData(); + FontPlatformData(WTF::HashTableDeletedValueType) + : mTypeface(hashTableDeletedFontValue()) { } + bool isHashTableDeletedValue() const { + return mTypeface == hashTableDeletedFontValue(); + } + FontPlatformData& operator=(const FontPlatformData&); bool operator==(const FontPlatformData& a) const; @@ -54,51 +60,12 @@ private: float mTextSize; bool mFakeBold; bool mFakeItalic; -}; - -#if 0 // windows port -class FontPlatformData -{ -public: - class Deleted {}; - - // Used for deleted values in the font cache's hash tables. - FontPlatformData(Deleted) - : m_font((HFONT)-1), m_fontFace(0), m_scaledFont(0), m_size(0) - {} - - FontPlatformData() - : m_font(0), m_fontFace(0), m_scaledFont(0), m_size(0) - {} - - FontPlatformData(HFONT, int size); - ~FontPlatformData(); - - HFONT hfont() const { return m_font; } - cairo_font_face_t* fontFace() const { return m_fontFace; } - cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } - - int size() const { return m_size; } - - unsigned hash() const - { - return StringImpl::computeHash((UChar*)(&m_font), sizeof(HFONT) / sizeof(UChar)); - } - bool operator==(const FontPlatformData& other) const - { - return m_font == other.m_font && m_fontFace == other.m_fontFace && - m_scaledFont == other.m_scaledFont && m_size == other.m_size; + static SkTypeface* hashTableDeletedFontValue() { + return reinterpret_cast<SkTypeface*>(-1); } - -private: - HFONT m_font; - cairo_font_face_t* m_fontFace; - cairo_scaled_font_t* m_scaledFont; - int m_size; }; -#endif - -} + +} /* namespace */ #endif diff --git a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp index 4b4186a..247d8bc 100644 --- a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp +++ b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp @@ -66,7 +66,10 @@ FontPlatformData::FontPlatformData() FontPlatformData::FontPlatformData(const FontPlatformData& src) { - src.mTypeface->safeRef(); + if (hashTableDeletedFontValue() != src.mTypeface) { + src.mTypeface->safeRef(); + } + mTypeface = src.mTypeface; mTextSize = src.mTextSize; @@ -80,8 +83,10 @@ FontPlatformData::FontPlatformData(const FontPlatformData& src) FontPlatformData::FontPlatformData(SkTypeface* tf, float textSize, bool fakeBold, bool fakeItalic) : mTypeface(tf), mTextSize(textSize), mFakeBold(fakeBold), mFakeItalic(fakeItalic) { - mTypeface->safeRef(); - + if (hashTableDeletedFontValue() != mTypeface) { + mTypeface->safeRef(); + } + inc_count(); trace(3); } @@ -89,7 +94,9 @@ FontPlatformData::FontPlatformData(SkTypeface* tf, float textSize, bool fakeBold FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) : mTypeface(src.mTypeface), mTextSize(textSize), mFakeBold(src.mFakeBold), mFakeItalic(src.mFakeItalic) { - mTypeface->safeRef(); + if (hashTableDeletedFontValue() != mTypeface) { + mTypeface->safeRef(); + } inc_count(); trace(4); @@ -102,13 +109,21 @@ FontPlatformData::~FontPlatformData() SkDebugf("----------- ~FontPlatformData\n"); #endif - mTypeface->safeUnref(); + if (hashTableDeletedFontValue() != mTypeface) { + mTypeface->safeUnref(); + } } FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) { - SkRefCnt_SafeAssign(mTypeface, src.mTypeface); + if (hashTableDeletedFontValue() != src.mTypeface) { + src.mTypeface->safeRef(); + } + if (hashTableDeletedFontValue() != mTypeface) { + mTypeface->safeUnref(); + } + mTypeface = src.mTypeface; mTextSize = src.mTextSize; mFakeBold = src.mFakeBold; mFakeItalic = src.mFakeItalic; @@ -133,7 +148,7 @@ void FontPlatformData::setupPaint(SkPaint* paint) const bool FontPlatformData::operator==(const FontPlatformData& a) const { - return SkTypeface::Equal(mTypeface, a.mTypeface) && + return mTypeface == a.mTypeface && mTextSize == a.mTextSize && mFakeBold == a.mFakeBold && mFakeItalic == a.mFakeItalic; @@ -141,9 +156,18 @@ bool FontPlatformData::operator==(const FontPlatformData& a) const unsigned FontPlatformData::hash() const { - unsigned h = SkTypeface::UniqueID(mTypeface); + unsigned h; + + if (hashTableDeletedFontValue() == mTypeface) { + h = reinterpret_cast<unsigned>(mTypeface); + } else { + h = SkTypeface::UniqueID(mTypeface); + } + + uint32_t sizeAsInt = *reinterpret_cast<const uint32_t*>(&mTextSize); + h ^= 0x01010101 * (((int)mFakeBold << 1) | (int)mFakeItalic); - h ^= *(uint32_t*)&mTextSize; + h ^= sizeAsInt; return h; } diff --git a/WebCore/platform/graphics/android/GradientAndroid.cpp b/WebCore/platform/graphics/android/GradientAndroid.cpp new file mode 100644 index 0000000..c5882ef --- /dev/null +++ b/WebCore/platform/graphics/android/GradientAndroid.cpp @@ -0,0 +1,107 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "Gradient.h" + +#include "android_graphics.h" +#include "CSSParser.h" +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "SkCanvas.h" +#include "SkColorShader.h" +#include "SkGradientShader.h" +#include "SkPaint.h" + +class PlatformGradientRec { +public: + PlatformGradientRec() : m_shader(NULL) {} + ~PlatformGradientRec() { m_shader->safeUnref(); } + + SkShader* m_shader; + SkShader::TileMode m_tileMode; +}; + +namespace WebCore { + +void Gradient::platformDestroy() +{ + delete m_gradient; +} + +static U8CPU F2B(float x) +{ + return (int)(x * 255); +} + +SkShader* Gradient::getShader(SkShader::TileMode mode) { + if (NULL == m_gradient) { + m_gradient = new PlatformGradientRec; + } else if (m_gradient->m_tileMode == mode) { + return m_gradient->m_shader; + } + + SkPoint pts[2]; + + android_setpt(&pts[0], m_p0); + android_setpt(&pts[1], m_p1); + size_t count = m_stops.size(); + SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); + SkColor* colors = (SkColor*)storage.get(); + SkScalar* pos = (SkScalar*)(colors + count); + + Vector<ColorStop>::iterator iter = m_stops.begin(); + int i = -1; + while (i++, iter != m_stops.end()) { + pos[i] = SkFloatToScalar(iter->stop); + colors[i] = SkColorSetARGB(F2B(iter->alpha), F2B(iter->red), + F2B(iter->green), F2B(iter->blue)); + ++iter; + } + + SkShader* s; + if (0 == count) { + // it seems the spec says a zero-size gradient draws transparent + s = new SkColorShader(0); + } else if (m_radial) { + s = SkGradientShader::CreateRadial(pts[0], SkFloatToScalar(m_r0), + colors, pos, count, mode); + } else { + s = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + } + + m_gradient->m_shader->safeUnref(); + m_gradient->m_shader = s; + m_gradient->m_tileMode = mode; + return s; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + SkRect r; + SkPaint paint; + // we don't care about the mode, so try to use the existing one + SkShader::TileMode mode = m_gradient ? m_gradient->m_tileMode : + SkShader::kClamp_TileMode; + + paint.setAntiAlias(true); + paint.setShader(this->getShader(mode)); + android_gc2canvas(context)->drawRect(*android_setrect(&r, rect), paint); +} + + +} //namespace diff --git a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp index 23ac51d..901e56e 100644 --- a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp +++ b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp @@ -16,18 +16,21 @@ */ #include "config.h" +#include "Gradient.h" #include "GraphicsContext.h" +#include "GraphicsContextPrivate.h" #include "NotImplemented.h" #include "Path.h" +#include "Pattern.h" #include "SkBlurDrawLooper.h" +#include "SkBlurMaskFilter.h" #include "SkCanvas.h" #include "SkColorPriv.h" #include "SkDashPathEffect.h" #include "SkDevice.h" #include "SkPaint.h" #include "SkPorterDuff.h" -#include "WebCoreViewBridge.h" #include "PlatformGraphicsContext.h" #include "AffineTransform.h" @@ -47,6 +50,10 @@ static int RoundToInt(float x) return (int)roundf(x); } +template <typename T> T* deepCopyPtr(const T* src) { + return src ? new T(*src) : NULL; +} + /* TODO / questions mAlpha: how does this interact with the alpha in Color? multiply them together? @@ -55,46 +62,77 @@ static int RoundToInt(float x) Is Color premultiplied or not? If it is, then I can't blindly pass it to paint.setColor() */ +struct ShadowRec { + SkScalar mBlur; // >0 means valid shadow + SkScalar mDx; + SkScalar mDy; + SkColor mColor; +}; + class GraphicsContextPlatformPrivate { public: GraphicsContext* mCG; // back-ptr to our parent PlatformGraphicsContext* mPgc; struct State { + SkPath* mPath; float mMiterLimit; float mAlpha; - SkDrawLooper* mLooper; + float mStrokeThickness; SkPaint::Cap mLineCap; SkPaint::Join mLineJoin; SkPorterDuff::Mode mPorterDuffMode; int mDashRatio; //ratio of the length of a dash to its width + ShadowRec mShadow; + SkColor mFillColor; + SkColor mStrokeColor; + bool mUseAA; - State() - { + State() { + mPath = NULL; // lazily allocated mMiterLimit = 4; mAlpha = 1; - mLooper = NULL; + mStrokeThickness = 0.0f; // Same as default in GraphicsContextPrivate.h mLineCap = SkPaint::kDefault_Cap; mLineJoin = SkPaint::kDefault_Join; mPorterDuffMode = SkPorterDuff::kSrcOver_Mode; mDashRatio = 3; + mUseAA = true; + mShadow.mBlur = 0; + mFillColor = SK_ColorBLACK; + mStrokeColor = SK_ColorBLACK; } - State(const State& other) - { - other.mLooper->safeRef(); + State(const State& other) { memcpy(this, &other, sizeof(State)); + mPath = deepCopyPtr<SkPath>(other.mPath); } - ~State() - { - mLooper->safeUnref(); + ~State() { + delete mPath; } - SkDrawLooper* setDrawLooper(SkDrawLooper* dl) - { - SkRefCnt_SafeAssign(mLooper, dl); - return dl; + void setShadow(int radius, int dx, int dy, SkColor c) { + // cut the radius in half, to visually match the effect seen in + // safari browser + mShadow.mBlur = SkScalarHalf(SkIntToScalar(radius)); + mShadow.mDx = SkIntToScalar(dx); + mShadow.mDy = SkIntToScalar(dy); + mShadow.mColor = c; + } + + bool setupShadowPaint(SkPaint* paint, SkPoint* offset) { + if (mShadow.mBlur > 0) { + paint->setAntiAlias(true); + paint->setDither(true); + paint->setPorterDuffXfermode(mPorterDuffMode); + paint->setColor(mShadow.mColor); + paint->setMaskFilter(SkBlurMaskFilter::Create(mShadow.mBlur, + SkBlurMaskFilter::kNormal_BlurStyle))->unref(); + offset->set(mShadow.mDx, mShadow.mDy); + return true; + } + return false; } SkColor applyAlpha(SkColor c) const @@ -115,16 +153,13 @@ public: GraphicsContextPlatformPrivate(GraphicsContext* cg, PlatformGraphicsContext* pgc) : mCG(cg) - , mPgc(pgc), mStateStack(sizeof(State)) - - { + , mPgc(pgc), mStateStack(sizeof(State)) { State* state = (State*)mStateStack.push_back(); new (state) State(); mState = state; } - ~GraphicsContextPlatformPrivate() - { + ~GraphicsContextPlatformPrivate() { if (mPgc && mPgc->deleteUs()) delete mPgc; @@ -134,41 +169,70 @@ public: this->restore(); } - void save() - { + void save() { State* newState = (State*)mStateStack.push_back(); new (newState) State(*mState); mState = newState; } - void restore() - { + void restore() { mState->~State(); mStateStack.pop_back(); mState = (State*)mStateStack.back(); } - void setup_paint_common(SkPaint* paint) const - { -#ifdef SK_DEBUGx - { - SkPaint defaultPaint; - - SkASSERT(*paint == defaultPaint); - } -#endif + void setFillColor(const Color& c) { + mState->mFillColor = c.rgb(); + } - paint->setAntiAlias(true); + void setStrokeColor(const Color& c) { + mState->mStrokeColor = c.rgb(); + } + + void setStrokeThickness(float f) { + mState->mStrokeThickness = f; + } + + void beginPath() { + if (mState->mPath) { + mState->mPath->reset(); + } + } + + void addPath(const SkPath& other) { + if (!mState->mPath) { + mState->mPath = new SkPath(other); + } else { + mState->mPath->addPath(other); + } + } + + // may return null + SkPath* getPath() const { return mState->mPath; } + + void setup_paint_common(SkPaint* paint) const { + paint->setAntiAlias(mState->mUseAA); paint->setDither(true); paint->setPorterDuffXfermode(mState->mPorterDuffMode); - paint->setLooper(mState->mLooper); + if (mState->mShadow.mBlur > 0) { + SkDrawLooper* looper = new SkBlurDrawLooper(mState->mShadow.mBlur, + mState->mShadow.mDx, + mState->mShadow.mDy, + mState->mShadow.mColor); + paint->setLooper(looper)->unref(); + } + + /* need to sniff textDrawingMode(), which returns the bit_OR of... + const int cTextInvisible = 0; + const int cTextFill = 1; + const int cTextStroke = 2; + const int cTextClip = 4; + */ } - void setup_paint_fill(SkPaint* paint) const - { + void setup_paint_fill(SkPaint* paint) const { this->setup_paint_common(paint); - - paint->setColor(mState->applyAlpha(mCG->fillColor().rgb())); + paint->setColor(mState->mFillColor); } /* sets up the paint for stroking. Returns true if the style is really @@ -176,15 +240,17 @@ public: */ bool setup_paint_stroke(SkPaint* paint, SkRect* rect) { this->setup_paint_common(paint); - - float width = mCG->strokeThickness(); + paint->setColor(mState->mStrokeColor); + + float width = mState->mStrokeThickness; - //this allows dashing and dotting to work properly for hairline strokes + // this allows dashing and dotting to work properly for hairline strokes + // FIXME: Should we only do this for dashed and dotted strokes? if (0 == width) { width = 1; } - paint->setColor(mState->applyAlpha(mCG->strokeColor().rgb())); +// paint->setColor(mState->applyAlpha(mCG->strokeColor().rgb())); paint->setStyle(SkPaint::kStroke_Style); paint->setStrokeWidth(SkFloatToScalar(width)); paint->setStrokeCap(mState->mLineCap); @@ -241,32 +307,6 @@ GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height) return ctx; } -void GraphicsContext::drawOffscreenContext(GraphicsContext* ctx, const FloatRect* srcRect, const FloatRect& dstRect) -{ - const SkBitmap& bm = GC2Canvas(ctx)->getDevice()->accessBitmap(false); - SkIRect src; - SkRect dst; - SkPaint paint; - - paint.setFilterBitmap(true); - - GC2Canvas(this)->drawBitmapRect(bm, - srcRect ? android_setrect(&src, *srcRect) : NULL, - *android_setrect(&dst, dstRect), - &paint); -} - -FloatRect GraphicsContext::getClipLocalBounds() const -{ - SkRect r; - - if (!GC2Canvas(this)->getClipBounds(&r)) - r.setEmpty(); - - return FloatRect(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), - SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); -} - //////////////////////////////////////////////////////////////////////////////////////////////// GraphicsContext::GraphicsContext(PlatformGraphicsContext *gc) @@ -298,6 +338,14 @@ void GraphicsContext::restorePlatformState() m_data->restore(); } +bool GraphicsContext::willFill() const { + return m_data->mState->mFillColor != 0; +} + +bool GraphicsContext::willStroke() const { + return m_data->mState->mStrokeColor != 0; +} + // Draws a filled rectangle with a stroked border. void GraphicsContext::drawRect(const IntRect& rect) { @@ -521,124 +569,31 @@ void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* poin for (size_t i = 1; i < numPoints; i++) path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y())); + if (GC2Canvas(this)->quickReject(path, shouldAntialias ? + SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) { + return; + } + if (fillColor().rgb() & 0xFF000000) { m_data->setup_paint_fill(&paint); + paint.setAntiAlias(shouldAntialias); GC2Canvas(this)->drawPath(path, paint); } if (strokeStyle() != NoStroke) { paint.reset(); m_data->setup_paint_stroke(&paint, NULL); + paint.setAntiAlias(shouldAntialias); GC2Canvas(this)->drawPath(path, paint); } } -#ifdef ANDROID_CANVAS_IMPL - -static void check_set_shader(SkPaint* paint, SkShader* s0, SkShader* s1) -{ - if (s0) { - paint->setShader(s0); - } - else if (s1) { - paint->setShader(s1); - } -} - -void GraphicsContext::fillPath(const Path& webCorePath, PlatformGradient* grad, PlatformPattern* pat) -{ - if (paintingDisabled()) - return; - - SkPaint paint; - - m_data->setup_paint_fill(&paint); - check_set_shader(&paint, grad, pat); - - const SkPath& path = *webCorePath.platformPath(); - -#if 0 - SkDebugf("---- fillPath\n"); - SkPath::Iter iter(path, false); - SkPoint pts[4]; - for (;;) { - SkString str; - const SkPoint* p = &pts[1]; - int n = 0; - const char* name = ""; - switch (iter.next(pts)) { - case SkPath::kMove_Verb: - name = " M"; - p = &pts[0]; - n = 1; - break; - case SkPath::kLine_Verb: - name = " L"; - n = 1; - break; - case SkPath::kQuad_Verb: - name = " Q"; - n = 2; - break; - case SkPath::kCubic_Verb: - name = " C"; - n = 3; - break; - case SkPath::kClose_Verb: - name = " X"; - n = 0; - break; - case SkPath::kDone_Verb: - goto DONE; - } - str.append(name); - for (int i = 0; i < n; i++) { - str.append(" "); - str.appendScalar(p[i].fX); - str.append(" "); - str.appendScalar(p[i].fY); - } - SkDebugf("\"%s\"\n", str.c_str()); - } -DONE: -#endif - - GC2Canvas(this)->drawPath(path, paint); -} - -void GraphicsContext::strokePath(const Path& webCorePath, PlatformGradient* grad, PlatformPattern* pat) -{ - if (paintingDisabled()) - return; - - SkPaint paint; - - m_data->setup_paint_stroke(&paint, NULL); - check_set_shader(&paint, grad, pat); - - GC2Canvas(this)->drawPath(*webCorePath.platformPath(), paint); -} - -void GraphicsContext::fillRect(const FloatRect& rect, PlatformGradient* grad, PlatformPattern* pat) -{ - if (paintingDisabled()) - return; - - SkRect r; - SkPaint paint; - - m_data->setup_paint_fill(&paint); - check_set_shader(&paint, grad, pat); - - GC2Canvas(this)->drawRect(*android_setrect(&r, rect), 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; - + SkPaint paint; SkPath path; SkScalar radii[8]; @@ -653,137 +608,19 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef radii[6] = SkIntToScalar(bottomLeft.width()); radii[7] = SkIntToScalar(bottomLeft.height()); path.addRoundRect(*android_setrect(&r, rect), radii); - + m_data->setup_paint_fill(&paint); GC2Canvas(this)->drawPath(path, paint); } -void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth, PlatformGradient* grad, PlatformPattern* pat) +void GraphicsContext::fillRect(const FloatRect& rect) { - if (paintingDisabled()) - return; - - SkRect r; SkPaint paint; - - m_data->setup_paint_stroke(&paint, NULL); - paint.setStrokeWidth(SkFloatToScalar(lineWidth)); - check_set_shader(&paint, grad, pat); - - GC2Canvas(this)->drawRect(*android_setrect(&r, rect), paint); -} - -static U8CPU F2B(float x) -{ - return (int)(x * 255); -} - -static SkColor make_color(float a, float r, float g, float b) -{ - return SkColorSetARGB(F2B(a), F2B(r), F2B(g), F2B(b)); -} - -PlatformGradient* GraphicsContext::newPlatformLinearGradient(const FloatPoint& p0, const FloatPoint& p1, - const float stopData[5], int count) -{ - SkPoint pts[2]; - - android_setpt(&pts[0], p0); - android_setpt(&pts[1], p1); - - SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); - SkColor* colors = (SkColor*)storage.get(); - SkScalar* pos = (SkScalar*)(colors + count); - - for (int i = 0; i < count; i++) - { - pos[i] = SkFloatToScalar(stopData[0]); - colors[i] = make_color(stopData[4], stopData[1], stopData[2], stopData[3]); - stopData += 5; - } - - return SkGradientShader::CreateLinear(pts, colors, pos, count, - SkShader::kClamp_TileMode); -} - -PlatformGradient* GraphicsContext::newPlatformRadialGradient(const FloatPoint& p0, float r0, - const FloatPoint& p1, float r1, - const float stopData[5], int count) -{ - SkPoint center; - - android_setpt(¢er, p1); - - SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); - SkColor* colors = (SkColor*)storage.get(); - SkScalar* pos = (SkScalar*)(colors + count); + SkRect r; - for (int i = 0; i < count; i++) - { - pos[i] = SkFloatToScalar(stopData[0]); - colors[i] = make_color(stopData[4], stopData[1], stopData[2], stopData[3]); - stopData += 5; - } - - return SkGradientShader::CreateRadial(center, SkFloatToScalar(r1), - colors, pos, count, - SkShader::kClamp_TileMode); -} - -void GraphicsContext::freePlatformGradient(PlatformGradient* shader) -{ - shader->safeUnref(); -} - -PlatformPattern* GraphicsContext::newPlatformPattern(Image* image, - Image::TileRule hRule, - Image::TileRule vRule) -{ -//printf("----------- Image %p, [%d %d] %d %d\n", image, image->width(), image->height(), hRule, vRule); - if (NULL == image) - return NULL; - - SkBitmapRef* bm = image->nativeImageForCurrentFrame(); - if (NULL == bm) - return NULL; - - return SkShader::CreateBitmapShader(bm->bitmap(), - android_convert_TileRule(hRule), - android_convert_TileRule(vRule)); -} - -void GraphicsContext::freePlatformPattern(PlatformPattern* shader) -{ - shader->safeUnref(); -} - -#endif - -#if 0 -static int getBlendedColorComponent(int c, int a) -{ - // We use white. - float alpha = (float)(a) / 255; - int whiteBlend = 255 - a; - c -= whiteBlend; - return (int)(c/alpha); -} -#endif - -void GraphicsContext::fillRect(const IntRect& rect, const Color& color) -{ - if (paintingDisabled()) - return; - - if (color.rgb() & 0xFF000000) { - SkPaint paint; - SkRect r; - - android_setrect(&r, rect); - m_data->setup_paint_common(&paint); - paint.setColor(color.rgb()); - GC2Canvas(this)->drawRect(r, paint); - } + android_setrect(&r, rect); + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawRect(r, paint); } void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) @@ -797,12 +634,13 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) android_setrect(&r, rect); m_data->setup_paint_common(&paint); - paint.setColor(color.rgb()); + paint.setColor(color.rgb()); // punch in the specified color + paint.setShader(NULL); // in case we had one set GC2Canvas(this)->drawRect(r, paint); } } -void GraphicsContext::clip(const IntRect& rect) +void GraphicsContext::clip(const FloatRect& rect) { if (paintingDisabled()) return; @@ -872,6 +710,10 @@ void GraphicsContext::clipOut(const Path& p) GC2Canvas(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op); } +void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) { + SkDebugf("xxxxxxxxxxxxxxxxxx clipToImageBuffer not implemented\n"); +} + ////////////////////////////////////////////////////////////////////////////////////////////////// #if SVG_SUPPORT @@ -904,38 +746,76 @@ void GraphicsContext::endTransparencyLayer() GC2Canvas(this)->restore(); } -void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color) + /////////////////////////////////////////////////////////////////////////// + + void GraphicsContext::setupFillPaint(SkPaint* paint) { + m_data->setup_paint_fill(paint); + } + + void GraphicsContext::setupStrokePaint(SkPaint* paint) { + m_data->setup_paint_stroke(paint, NULL); + } + + bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) { + return m_data->mState->setupShadowPaint(paint, offset); + } + + // referenced from CanvasStyle.cpp + void GraphicsContext::setCMYKAFillColor(float c, float m, float y, float k, float a) { + float r = 1 - (c + k); + float g = 1 - (m + k); + float b = 1 - (y + k); + return this->setFillColor(Color(r, g, b, a)); + } + + // referenced from CanvasStyle.cpp + void GraphicsContext::setCMYKAStrokeColor(float c, float m, float y, float k, float a) { + float r = 1 - (c + k); + float g = 1 - (m + k); + float b = 1 - (y + k); + return this->setStrokeColor(Color(r, g, b, a)); + } + + void GraphicsContext::setPlatformStrokeColor(const Color& c) { + m_data->setStrokeColor(c); + } + + void GraphicsContext::setPlatformStrokeThickness(float f) { + m_data->setStrokeThickness(f); + } + + void GraphicsContext::setPlatformFillColor(const Color& c) { + m_data->setFillColor(c); + } + +void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Color& color) { if (paintingDisabled()) return; - if (blur > 0) - { - SkColor c; - - if (color.isValid()) - c = color.rgb(); - else - c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" Apple shadow color - - SkDrawLooper* dl = new SkBlurDrawLooper(SkIntToScalar(blur), - SkIntToScalar(size.width()), - SkIntToScalar(size.height()), - c); - m_data->mState->setDrawLooper(dl)->unref(); + if (blur <= 0) { + this->clearPlatformShadow(); } - else - m_data->mState->setDrawLooper(NULL); + + SkColor c; + if (color.isValid()) { + c = color.rgb(); + } else { + c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" Apple shadow color + } + m_data->mState->setShadow(blur, size.width(), size.height(), c); } -void GraphicsContext::clearShadow() +void GraphicsContext::clearPlatformShadow() { if (paintingDisabled()) return; - m_data->mState->setDrawLooper(NULL); + m_data->mState->setShadow(0, 0, 0, 0); } +/////////////////////////////////////////////////////////////////////////////// + void GraphicsContext::drawFocusRing(const Color& color) { // Do nothing, since we draw the focus ring independently. @@ -1074,41 +954,152 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) { - // appears to be PDF specific, so we ignore it +// appears to be PDF specific, so we ignore it #if 0 +if (paintingDisabled()) + return; + +CFURLRef urlRef = link.createCFURL(); +if (urlRef) { + CGContextRef context = platformContext(); + + // Get the bounding box to handle clipping. + CGRect box = CGContextGetClipBoundingBox(context); + + IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height); + IntRect rect = destRect; + rect.intersect(intBox); + + CGPDFContextSetURLForRect(context, urlRef, + CGRectApplyAffineTransform(rect, CGContextGetCTM(context))); + + CFRelease(urlRef); +} +#endif +} + +void GraphicsContext::setUseAntialiasing(bool useAA) { if (paintingDisabled()) return; - - CFURLRef urlRef = link.createCFURL(); - if (urlRef) { - CGContextRef context = platformContext(); - - // Get the bounding box to handle clipping. - CGRect box = CGContextGetClipBoundingBox(context); + m_data->mState->mUseAA = useAA; +} + +AffineTransform GraphicsContext::getCTM() const { + return AffineTransform(GC2Canvas(this)->getTotalMatrix()); +} - IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height); - IntRect rect = destRect; - rect.intersect(intBox); +/////////////////////////////////////////////////////////////////////////////// + +void GraphicsContext::beginPath() { + m_data->beginPath(); +} + +void GraphicsContext::addPath(const Path& p) { + m_data->addPath(*p.platformPath()); +} - CGPDFContextSetURLForRect(context, urlRef, - CGRectApplyAffineTransform(rect, CGContextGetCTM(context))); +void GraphicsContext::drawPath() { + this->fillPath(); + this->strokePath(); +} - CFRelease(urlRef); +static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm) { + SkShader::TileMode mode = SkShader::kClamp_TileMode; + + switch (sm) { + case SpreadMethodPad: + mode = SkShader::kClamp_TileMode; + break; + case SpreadMethodReflect: + mode = SkShader::kMirror_TileMode; + break; + case SpreadMethodRepeat: + mode = SkShader::kRepeat_TileMode; + break; + } + return mode; +} + +void extactShader(SkPaint* paint, ColorSpace cs, Pattern* pat, Gradient* grad, + GradientSpreadMethod sm) { + switch (cs) { + case PatternColorSpace: + // createPlatformPattern() returns a new inst + paint->setShader(pat->createPlatformPattern( + AffineTransform()))->safeUnref(); + break; + case GradientColorSpace: { + // grad->getShader() returns a cached obj + paint->setShader(grad->getShader(SpreadMethod2TileMode(sm))); + break; + } + default: + break; } -#endif } -// we don't need to push these down, since we query the current state and build our paint at draw-time +void GraphicsContext::fillPath() { + SkPath* path = m_data->getPath(); + if (paintingDisabled() || !path) + return; + + switch (this->fillRule()) { + case RULE_NONZERO: + path->setFillType(SkPath::kWinding_FillType); + break; + case RULE_EVENODD: + path->setFillType(SkPath::kEvenOdd_FillType); + break; + } -void GraphicsContext::setPlatformStrokeColor(const Color&) {} -void GraphicsContext::setPlatformStrokeThickness(float) {} -void GraphicsContext::setPlatformFillColor(const Color&) {} + SkPaint paint; + m_data->setup_paint_fill(&paint); + extactShader(&paint, m_common->state.fillColorSpace, + m_common->state.fillPattern.get(), + m_common->state.fillGradient.get(), spreadMethod()); -// functions new to Feb-19 tip of tree merge: -AffineTransform GraphicsContext::getCTM() const { - notImplemented(); - return AffineTransform(); + GC2Canvas(this)->drawPath(*path, paint); } +void GraphicsContext::strokePath() { + const SkPath* path = m_data->getPath(); + if (paintingDisabled() || !path || strokeStyle() == NoStroke) + return; + + SkPaint paint; + m_data->setup_paint_stroke(&paint, NULL); + + extactShader(&paint, m_common->state.strokeColorSpace, + m_common->state.strokePattern.get(), + m_common->state.strokeGradient.get(), spreadMethod()); + + GC2Canvas(this)->drawPath(*path, paint); } + +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) +{ + /* + enum InterpolationQuality { + InterpolationDefault, + InterpolationNone, + InterpolationLow, + InterpolationMedium, + InterpolationHigh + }; + + TODO: record this, so we can know when to use bitmap-filtering when we draw + ... not sure how meaningful this will be given our playback model. + + Certainly safe to do nothing for the present. + */ +} + +} // namespace WebCore + +/////////////////////////////////////////////////////////////////////////////// + +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 00145af..a54f440 100644 --- a/WebCore/platform/graphics/android/ImageAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageAndroid.cpp @@ -37,17 +37,27 @@ #include "SkBitmapRef.h" #include "SkCanvas.h" #include "SkColorPriv.h" +#include "SkImageDecoder.h" #include "SkShader.h" #include "SkString.h" +#include "SkTemplates.h" #include <utils/AssetManager.h> //#define TRACE_SUBSAMPLED_BITMAPS +//#define TRACE_SKIPPED_BITMAPS -android::AssetManager* gGlobalAssetMgr; +android::AssetManager* globalAssetManager() { + static android::AssetManager* gGlobalAssetMgr; + if (!gGlobalAssetMgr) { + gGlobalAssetMgr = new android::AssetManager(); + gGlobalAssetMgr->addDefaultAssets(); + } + return gGlobalAssetMgr; +} namespace WebCore { - + void FrameData::clear() { if (m_frame) { @@ -58,63 +68,83 @@ void FrameData::clear() } } -SkBitmapRef* BitmapImage::getBitmap() +BitmapImage::BitmapImage(SkBitmapRef* ref, ImageObserver* observer) + : Image(observer) + , m_currentFrame(0) + , m_frames(0) + , m_frameTimer(0) + , m_repetitionCount(0) + , m_repetitionsComplete(0) + , m_isSolidColor(false) + , m_animationFinished(true) + , m_allDataReceived(true) + , m_haveSize(true) + , m_sizeAvailable(true) + , m_decodedSize(0) + , m_haveFrameCount(true) + , m_frameCount(1) { - return m_bitmapRef; + initPlatformData(); + + m_size = IntSize(ref->bitmap().width(), ref->bitmap().height()); + + m_frames.grow(1); + m_frames[0].m_frame = ref; + m_frames[0].m_hasAlpha = !ref->bitmap().isOpaque(); + checkForSolidColor(); + ref->ref(); } + void BitmapImage::initPlatformData() { - m_bitmapRef = NULL; m_source.clearURL(); } void BitmapImage::invalidatePlatformData() { - if (m_bitmapRef) { - m_bitmapRef->unref(); - m_bitmapRef = NULL; - } } void BitmapImage::checkForSolidColor() { m_isSolidColor = false; - if (this->frameCount() > 1) { - if (!m_bitmapRef) { - return; + if (frameCount() == 1) { + SkBitmapRef* ref = frameAtIndex(0); + if (!ref) { + return; // keep solid == false } - const SkBitmap& bm = m_bitmapRef->bitmap(); + const SkBitmap& bm = ref->bitmap(); + if (bm.width() != 1 || bm.height() != 1) { + return; // keep solid == false + } - if (bm.width() == 1 && bm.height() == 1) { - SkAutoLockPixels alp(bm); - if (bm.getPixels() == NULL) { - return; - } + SkAutoLockPixels alp(bm); + if (!bm.readyToDraw()) { + return; // keep solid == false + } - SkPMColor color; - switch (bm.getConfig()) { - case SkBitmap::kARGB_8888_Config: - color = *bm.getAddr32(0, 0); - break; - case SkBitmap::kRGB_565_Config: - color = SkPixel16ToPixel32(*bm.getAddr16(0, 0)); - break; - case SkBitmap::kIndex8_Config: { - SkColorTable* ctable = bm.getColorTable(); - if (!ctable) { - return; - } - color = (*ctable)[*bm.getAddr8(0, 0)]; - break; - } - default: // don't check other configs - return; + SkPMColor color; + switch (bm.getConfig()) { + case SkBitmap::kARGB_8888_Config: + color = *bm.getAddr32(0, 0); + break; + case SkBitmap::kRGB_565_Config: + color = SkPixel16ToPixel32(*bm.getAddr16(0, 0)); + break; + case SkBitmap::kIndex8_Config: { + SkColorTable* ctable = bm.getColorTable(); + if (!ctable) { + return; + } + color = (*ctable)[*bm.getAddr8(0, 0)]; + break; } - m_isSolidColor = true; - m_solidColor = android_SkPMColorToWebCoreColor(color); + default: + return; // keep solid == false } + m_isSolidColor = true; + m_solidColor = android_SkPMColorToWebCoreColor(color); } } @@ -125,36 +155,46 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, if (!image) { // If it's too early we won't have an image yet. return; } - + // in case we get called with an incomplete bitmap const SkBitmap& bitmap = image->bitmap(); if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { +#ifdef TRACE_SKIPPED_BITMAPS + SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n", + bitmap.width(), bitmap.height(), + bitmap.getPixels(), bitmap.pixelRef()); +#endif return; } - + SkIRect srcR; SkRect dstR; float invScaleX = (float)bitmap.width() / image->origWidth(); float invScaleY = (float)bitmap.height() / image->origHeight(); - + android_setrect(&dstR, dstRect); android_setrect_scaled(&srcR, srcRect, invScaleX, invScaleY); if (srcR.isEmpty() || dstR.isEmpty()) { +#ifdef TRACE_SKIPPED_BITMAPS + SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n", + bitmap.width(), bitmap.height(), + srcR.isEmpty(), dstR.isEmpty()); +#endif return; } SkCanvas* canvas = ctxt->platformContext()->mCanvas; SkPaint paint; - + paint.setFilterBitmap(true); paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint); - + startAnimation(); - + #ifdef TRACE_SUBSAMPLED_BITMAPS if (bitmap.width() != image->origWidth() || - bitmap.height() != image->origHeight()) { + bitmap.height() != image->origHeight()) { SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n", bitmap.width(), bitmap.height(), image->origWidth(), image->origHeight()); @@ -184,16 +224,16 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { return; } - + SkRect dstR; android_setrect(&dstR, destRect); if (dstR.isEmpty()) { return; } - + SkCanvas* canvas = ctxt->platformContext()->mCanvas; SkPaint paint; - + SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); @@ -201,21 +241,21 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, // now paint is the only owner of shader paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); paint.setFilterBitmap(true); - + SkMatrix matrix(patternTransform); - + float scaleX = (float)image->origWidth() / bitmap.width(); float scaleY = (float)image->origHeight() / bitmap.height(); matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY)); - + matrix.postTranslate(SkFloatToScalar(phase.x()), SkFloatToScalar(phase.y())); shader->setLocalMatrix(matrix); canvas->drawRect(dstR, paint); - + #ifdef TRACE_SUBSAMPLED_BITMAPS if (bitmap.width() != image->origWidth() || - bitmap.height() != image->origHeight()) { + bitmap.height() != image->origHeight()) { SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n", bitmap.width(), bitmap.height(), image->origWidth(), image->origHeight(), @@ -225,30 +265,32 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, } // missingImage, textAreaResizeCorner -Image* Image::loadPlatformResource(const char *name) +PassRefPtr<Image> Image::loadPlatformResource(const char *name) { - if (NULL == gGlobalAssetMgr) { - gGlobalAssetMgr = new android::AssetManager(); - gGlobalAssetMgr->addDefaultAssets(); - } - + android::AssetManager* am = globalAssetManager(); + SkString path("webkit/"); path.append(name); path.append(".png"); - - android::Asset* a = gGlobalAssetMgr->open(path.c_str(), - android::Asset::ACCESS_BUFFER); + + android::Asset* a = am->open(path.c_str(), + android::Asset::ACCESS_BUFFER); if (a == NULL) { SkDebugf("---------------- failed to open image asset %s\n", name); return NULL; } - - Image* image = new BitmapImage; - RefPtr<SharedBuffer> buffer = - new SharedBuffer((const char*)a->getBuffer(false), a->getLength()); - image->setData(buffer, true); - delete a; - return image; + + SkAutoTDelete<android::Asset> ad(a); + + SkBitmap bm; + if (SkImageDecoder::DecodeMemory(a->getBuffer(false), a->getLength(), &bm)) { + SkBitmapRef* ref = new SkBitmapRef(bm); + // create will call ref(), so we need aur() to release ours upon return + SkAutoUnref aur(ref); + return BitmapImage::create(ref, 0); + } + return Image::nullImage(); } -} +} // namespace + diff --git a/WebCore/platform/graphics/android/ImageBufferAndroid.cpp b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp index 65fb7cd..b8c0138 100644 --- a/WebCore/platform/graphics/android/ImageBufferAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp @@ -16,29 +16,34 @@ */ #include "config.h" +#include "BitmapImage.h" #include "ImageBuffer.h" +#include "ImageData.h" +#include "NotImplemented.h" +#include "android_graphics.h" #include "GraphicsContext.h" +#include "PlatformGraphicsContext.h" +#include "SkBitmapRef.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkDevice.h" +#include "SkUnPreMultiply.h" using namespace std; namespace WebCore { -auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool grayScale) +ImageBufferData::ImageBufferData(const IntSize&) { - // Ignore grayScale for now, since SkBitmap doesn't support it... yet - - GraphicsContext* ctx = GraphicsContext::createOffscreenContext(size.width(), size.height()); - - auto_ptr<GraphicsContext> context(ctx); - - return auto_ptr<ImageBuffer>(new ImageBuffer(size, context)); } - -ImageBuffer::ImageBuffer(const IntSize& size, auto_ptr<GraphicsContext> context) - : m_data(NULL), m_size(size), m_context(context.release()) +ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) + : m_data(size) + , m_size(size) { + m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height())); + success = true; } ImageBuffer::~ImageBuffer() @@ -50,4 +55,154 @@ GraphicsContext* ImageBuffer::context() const return m_context.get(); } +/* This guy needs to make a deep copy of the bitmap, so that the returned + image doesn't reflect any subsequent changes to the canvas' backend. + e.g. this is called when <canvas> wants to make a Pattern, which needs + to snapshot the current pixels when it is created. + */ +Image* ImageBuffer::image() const +{ + if (!m_image) { + ASSERT(context()); + SkCanvas* canvas = context()->platformContext()->mCanvas; + SkDevice* device = canvas->getDevice(); + const SkBitmap& orig = device->accessBitmap(false); + + SkBitmap copy; + orig.copyTo(©, orig.config()); + + SkBitmapRef* ref = new SkBitmapRef(copy); + m_image = BitmapImage::create(ref, 0); + ref->unref(); + } + return m_image.get(); +} + +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const +{ + GraphicsContext* gc = this->context(); + if (!gc) { + return 0; + } + + const SkBitmap& src = android_gc2canvas(gc)->getDevice()->accessBitmap(false); + SkAutoLockPixels alp(src); + if (!src.getPixels()) { + return 0; + } + + PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); + unsigned char* data = result->data()->data().data(); + + if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) + memset(data, 0, result->data()->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.x() + rect.width(); + if (endx > m_size.width()) + endx = m_size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.y() + rect.height(); + if (endy > m_size.height()) + endy = m_size.height(); + int numRows = endy - originy; + + unsigned srcPixelsPerRow = src.rowBytesAsPixels(); + unsigned destBytesPerRow = 4 * rect.width(); + + const SkPMColor* srcRows = src.getAddr32(originx, originy); + unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + // ugh, it appears they want unpremultiplied pixels + SkColor c = SkUnPreMultiply::PMColorToColor(srcRows[x]); + int basex = x * 4; + destRows[basex + 0] = SkColorGetR(c); + destRows[basex + 1] = SkColorGetG(c); + destRows[basex + 2] = SkColorGetB(c); + destRows[basex + 3] = SkColorGetA(c); + } + srcRows += srcPixelsPerRow; + destRows += destBytesPerRow; + } + return result; +} + +void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) +{ + GraphicsContext* gc = this->context(); + if (!gc) { + return; + } + + const SkBitmap& dst = android_gc2canvas(gc)->getDevice()->accessBitmap(true); + SkAutoLockPixels alp(dst); + if (!dst.getPixels()) { + return; + } + + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < m_size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= m_size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < m_size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= m_size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * source->width(); + unsigned dstPixelsPerRow = dst.rowBytesAsPixels(); + + 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++) { + int basex = x * 4; + dstRows[x] = SkPackARGB32(srcRows[basex + 3], + srcRows[basex + 0], + srcRows[basex + 1], + srcRows[basex + 2]); + } + dstRows += dstPixelsPerRow; + srcRows += srcBytesPerRow; + } +} + + +String ImageBuffer::toDataURL(const String&) const +{ + // leaving this unimplemented, until I understand what its for (and what it + // really is). + return "data:,"; // I think this means we couldn't make the data url +} + } diff --git a/WebCore/platform/graphics/android/ImageBufferData.h b/WebCore/platform/graphics/android/ImageBufferData.h new file mode 100644 index 0000000..8050146 --- /dev/null +++ b/WebCore/platform/graphics/android/ImageBufferData.h @@ -0,0 +1,31 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ImageBufferData_h +#define ImageBufferData_h + +namespace WebCore { + +class IntSize; + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp index 144968d..3dc7557 100644 --- a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp @@ -19,6 +19,7 @@ #include "ImageDecoder.h" #include "ImageSource.h" #include "IntSize.h" +#include "NotImplemented.h" #include "SharedBuffer.h" #include "PlatformString.h" @@ -42,6 +43,10 @@ SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src); // we don't want to lose too much on the round-up to a page size (4K) #define MIN_ASHMEM_ALLOC_SIZE (32*1024) +// don't use RLE for images smaller than this, since they incur a drawing cost +// (and don't work as patterns yet) we only want to use RLE when we must +#define MIN_RLE_ALLOC_SIZE (512*1024) + static bool should_use_ashmem(const SkBitmap& bm) { return bm.getSize() >= MIN_ASHMEM_ALLOC_SIZE; } @@ -72,7 +77,7 @@ static bool shouldReencodeAsRLE(const SkBitmap& bm) { && bm.width() >= 64 // narrower than this won't compress well in RLE && - bm.getSize() > (250*1024); + bm.getSize() > MIN_RLE_ALLOC_SIZE; } /////////////////////////////////////////////////////////////////////////////// @@ -227,6 +232,8 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) m_decoder.m_image = new PrivateAndroidImageSourceRec(tmp, origW, origH, sampleSize); + +// SkDebugf("----- started: [%d %d] %s\n", origW, origH, m_decoder.m_url.c_str()); } PrivateAndroidImageSourceRec* decoder = m_decoder.m_image; @@ -240,10 +247,10 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) SkStream* strm = new SharedBufferStream(data); // imageref now owns the stream object if (should_use_ashmem(*bm)) { -// SkDebugf("---- use ashmem for image [%d %d]\n", bm.width(), bm.height()); +// SkDebugf("---- use ashmem for image [%d %d]\n", bm->width(), bm->height()); ref = new SkImageRef_ashmem(strm, bm->config(), decoder->fSampleSize); } else { -// SkDebugf("---- use globalpool for image [%d %d]\n", bm.width(), bm.height()); +// SkDebugf("---- use globalpool for image [%d %d]\n", bm->width(), bm->height()); ref = new SkImageRef_GlobalPool(strm, bm->config(), decoder->fSampleSize); } @@ -256,6 +263,8 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) ref->setURI(m_decoder.m_url); // our bitmap is now the only owner of the imageref bm->setPixelRef(ref)->unref(); + +// SkDebugf("---- finished: [%d %d] %s\n", bm->width(), bm->height(), ref->getURI()); } } @@ -333,4 +342,10 @@ void ImageSource::clear() // do nothing, since the cache is managed elsewhere } +IntSize ImageSource::frameSizeAtIndex(size_t index) const +{ + // for now, all (1) of our frames are the same size + return this->size(); +} + } diff --git a/WebCore/platform/graphics/android/PathAndroid.cpp b/WebCore/platform/graphics/android/PathAndroid.cpp index 68f4c0d..f6d3d43 100644 --- a/WebCore/platform/graphics/android/PathAndroid.cpp +++ b/WebCore/platform/graphics/android/PathAndroid.cpp @@ -122,7 +122,16 @@ void Path::closeSubpath() m_path->close(); } -static const float gPI = 3.1415926f; +static const float gPI = 3.14159265f; +static const float g2PI = 6.28318531f; +static const float g180OverPI = 57.29577951308f; + +static float fast_mod(float angle, float max) { + if (angle >= max || angle <= -max) { + angle = fmodf(angle, max); + } + return angle; +} void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise) { @@ -134,23 +143,43 @@ void Path::addArc(const FloatPoint& p, float r, float sa, float ea, oval.set(cx - radius, cy - radius, cx + radius, cy + radius); float sweep = ea - sa; - // check for a circle - if (sweep >= 2*gPI || sweep <= -2*gPI) { - m_path->addOval(oval); - } else { - SkScalar startDegrees = SkFloatToScalar(sa * 180 / gPI); - SkScalar sweepDegrees = SkFloatToScalar(sweep * 180 / gPI); - - if (clockwise && sweepDegrees > 0) { - sweepDegrees -= SkIntToScalar(360); - } else if (!clockwise && sweepDegrees < 0) { - sweepDegrees = SkIntToScalar(360) - sweepDegrees; - } + bool prependOval = false; + + /* Note if clockwise and the sign of the sweep disagree. This particular + logic was deduced from http://canvex.lazyilluminati.com/misc/arc.html + */ + if (clockwise && (sweep > 0 || sweep < -g2PI)) { + sweep = fmodf(sweep, g2PI) - g2PI; + } else if (!clockwise && (sweep < 0 || sweep > g2PI)) { + sweep = fmodf(sweep, g2PI) + g2PI; + } + + // If the abs(sweep) >= 2PI, then we need to add a circle before we call + // arcTo, since it treats the sweep mod 2PI. We don't have a prepend call, + // so we just remember this, and at the end create a new path with an oval + // and our current path, and then swap then. + // + if (sweep >= g2PI || sweep <= -g2PI) { + prependOval = true; +// SkDebugf("addArc sa=%g ea=%g cw=%d sweep %g treat as circle\n", sa, ea, clockwise, sweep); + + // now reduce sweep to just the amount we need, so that the current + // point is left where the caller expects it. + sweep = fmodf(sweep, g2PI); + } -// SkDebugf("addArc sa=%g ea=%g cw=%d start=%g sweep=%g\n", sa, ea, clockwise, -// SkScalarToFloat(startDegrees), SkScalarToFloat(sweepDegrees)); + sa = fast_mod(sa, g2PI); + SkScalar startDegrees = SkFloatToScalar(sa * g180OverPI); + SkScalar sweepDegrees = SkFloatToScalar(sweep * g180OverPI); - m_path->arcTo(oval, startDegrees, sweepDegrees, false); +// SkDebugf("addArc sa=%g ea=%g cw=%d sweep=%g ssweep=%g\n", sa, ea, clockwise, sweep, SkScalarToFloat(sweepDegrees)); + m_path->arcTo(oval, startDegrees, sweepDegrees, false); + + if (prependOval) { + SkPath tmp; + tmp.addOval(oval); + tmp.addPath(*m_path); + m_path->swap(tmp); } } diff --git a/WebCore/platform/graphics/android/PatternAndroid.cpp b/WebCore/platform/graphics/android/PatternAndroid.cpp new file mode 100644 index 0000000..fa52b79 --- /dev/null +++ b/WebCore/platform/graphics/android/PatternAndroid.cpp @@ -0,0 +1,46 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "Pattern.h" + +#include "android_graphics.h" +#include "GraphicsContext.h" +#include "SkBitmapRef.h" +#include "SkCanvas.h" +#include "SkColorShader.h" +#include "SkShader.h" +#include "SkPaint.h" + +namespace WebCore { + +static SkShader::TileMode toTileMode(bool doRepeat) { + return doRepeat ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; +} + +SkShader* Pattern::createPlatformPattern(const AffineTransform& transform) const +{ + SkBitmapRef* ref = tileImage()->nativeImageForCurrentFrame(); + SkShader* s = SkShader::CreateBitmapShader(ref->bitmap(), + toTileMode(m_repeatX), + toTileMode(m_repeatY)); + + // TODO: do I treat transform as a local matrix??? + return s; +} + +} //namespace diff --git a/WebCore/platform/graphics/android/PlatformGraphics.h b/WebCore/platform/graphics/android/PlatformGraphics.h deleted file mode 100644 index 6efdb43..0000000 --- a/WebCore/platform/graphics/android/PlatformGraphics.h +++ /dev/null @@ -1,25 +0,0 @@ -/* -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef PlatformGraphics_d -#define PlatformGraphics_d - -typedef class SkShader PlatformGradient; -typedef class SkShader PlatformPattern; - -#endif - diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp index af443b8..86d5c0a 100644 --- a/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp @@ -21,11 +21,10 @@ #include "SkCanvas.h" namespace WebCore { - -PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas) : mCanvas(canvas), m_deleteCanvas(false) + +PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas, WTF::Vector<Container>* buttons) + : mCanvas(canvas), m_deleteCanvas(false), m_buttons(buttons) { - // This is useful only if this Canvas is part of an SkPicture object. - m_buttons = new SkTDArray<Container*>; } PlatformGraphicsContext::PlatformGraphicsContext() : m_deleteCanvas(true) @@ -42,32 +41,29 @@ PlatformGraphicsContext::~PlatformGraphicsContext() // printf("-------------------- deleting offscreen canvas\n"); delete mCanvas; } - if (m_buttons != NULL) { - m_buttons->deleteAll(); - delete m_buttons; - } -} - -SkTDArray<Container*>* PlatformGraphicsContext::getAndClearButtonInfo() -{ - // The caller is now responsible for deleting the array - SkTDArray<Container*>* buttons = m_buttons; - m_buttons = NULL; - return buttons; } void PlatformGraphicsContext::storeButtonInfo(Node* node, const IntRect& r) { if (m_buttons == NULL) return; - // Initialize all of the nodes to have disabled state, so that we guarantee - // that we paint all of them the first time. - RenderSkinAndroid::State state = RenderSkinAndroid::kDisabled; - Container* container = new Container(node, r, state); + // Check to see if we already have a Container for this node. If so, update + // it with the new rectangle and make the new recording canvas reference + // its picture. + Container* end = m_buttons->end(); + for (Container* ptr = m_buttons->begin(); ptr != end; ptr++) { + if (ptr->matches(node)) { + mCanvas->drawPicture(*(ptr->picture())); + ptr->setRect(r); + return; + } + } + // We did not have a Container representing this node, so create a new one. + Container container(node, r); // Place a reference to our subpicture in the Picture. - mCanvas->drawPicture(*(container->picture())); + mCanvas->drawPicture(*(container.picture())); // Keep track of the information about the button. - *m_buttons->append() = container; + m_buttons->append(container); } } // WebCore diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.h b/WebCore/platform/graphics/android/PlatformGraphicsContext.h index a2d7ebe..d09dee2 100644 --- a/WebCore/platform/graphics/android/PlatformGraphicsContext.h +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.h @@ -30,9 +30,8 @@ class WebCore::Node; class Container { public: - Container(WebCore::Node* node, const WebCore::IntRect& r, - WebCore::RenderSkinAndroid::State is) - : m_node(node), m_rect(r), m_state(is) + Container(WebCore::Node* node, const WebCore::IntRect& r) + : m_node(node), m_rect(r), m_state(WebCore::RenderSkinAndroid::kDisabled) { m_picture = new SkPicture; } @@ -41,31 +40,81 @@ public: { m_picture->unref(); } + + Container& operator=(const Container& src) + { + if (this != &src) { + m_node = src.m_node; + if (m_picture) + m_picture->unref(); + m_picture = src.m_picture; + m_picture->ref(); + m_rect = src.m_rect; + m_state = WebCore::RenderSkinAndroid::kDisabled; + } + return *this; + } - bool matches(WebCore::Node* match) { return m_node == match; } + Container(const Container& src) + { + m_node = src.m_node; + m_picture = src.m_picture; + m_picture->ref(); + m_rect = src.m_rect; + m_state = WebCore::RenderSkinAndroid::kDisabled; + } + + // m_picture has a ref count of 1 to begin with. It will increase each time + // m_picture is referenced by another picture. When the other pictures are + // deleted, the ref count gets decremented. If the ref count is one, then + // no other pictures reference this one, so the button is no longer being + // used, and therefore can be removed. + bool canBeRemoved() + { + return m_picture->getRefCnt() == 1; + } + bool matches(const WebCore::Node* match) { return m_node == match; } + + const WebCore::Node* node() const { return m_node; } + // Provide a pointer to our SkPicture. SkPicture* picture() { return m_picture; } + + WebCore::IntRect rect() { return m_rect; } + + // Update the rectangle with a new rectangle, as the positioning of this + // button may have changed due to a new layout. If it is a new rectangle, + // set its state to disabled, so that it will be redrawn when we cycle + // through the list of buttons. + void setRect(WebCore::IntRect r) + { + if (m_rect != r) { + m_rect = r; + m_state = WebCore::RenderSkinAndroid::kDisabled; + } + } // Update the focus state of this button, depending on whether it // corresponds to the focused node passed in. If its state has changed, // re-record to the subpicture, so the master picture will reflect the // change. - void updateFocusState(WebCore::Node* focus) + void updateFocusState(WebCore::RenderSkinAndroid::State state) { - WebCore::RenderSkinAndroid::State state = m_node == focus ? - WebCore::RenderSkinAndroid::kFocused : WebCore::RenderSkinAndroid::kNormal; if (state == m_state) return; + // If this button is being told to draw focused, but it is already in a + // pressed state, leave it in the pressed state, to show that it is + // being followed. + if (m_state == WebCore::RenderSkinAndroid::kPressed && + state == WebCore::RenderSkinAndroid::kFocused) + return; m_state = state; SkCanvas* canvas = m_picture->beginRecording(m_rect.width(), m_rect.height()); WebCore::RenderSkinButton::Draw(canvas, m_rect, state); m_picture->endRecording(); } private: - // Mark copy and assignment private so noone can use them. - Container& operator=(const Container& src) { return *this; } - Container(const Container& src) { } // Only used for comparison, since after it is stored it will be transferred // to the UI thread. WebCore::Node* m_node; @@ -82,11 +131,18 @@ private: namespace WebCore { + class GraphicsContext; + class PlatformGraphicsContext { public: PlatformGraphicsContext(); - PlatformGraphicsContext(SkCanvas* canvas); + // Pass in a recording canvas, and an array of button information to be + // updated. + PlatformGraphicsContext(SkCanvas* canvas, WTF::Vector<Container>* buttons); ~PlatformGraphicsContext(); + + void setupFillPaint(GraphicsContext*, SkPaint*); + void setupStrokePaint(GraphicsContext*, SkPaint*); SkCanvas* mCanvas; @@ -95,12 +151,9 @@ public: // nod/rect, and record a new subpicture for this node/button in the current // mCanvas void storeButtonInfo(Node* node, const IntRect& r); - // Detaches button array (if any), returning it to the caller and setting our - // internal ptr to NULL - SkTDArray<Container*>* getAndClearButtonInfo(); private: bool m_deleteCanvas; - SkTDArray<Container*>* m_buttons; + WTF::Vector<Container>* m_buttons; }; } diff --git a/WebCore/platform/graphics/android/android_graphics.cpp b/WebCore/platform/graphics/android/android_graphics.cpp index 3964ee1..16f3cfc 100644 --- a/WebCore/platform/graphics/android/android_graphics.cpp +++ b/WebCore/platform/graphics/android/android_graphics.cpp @@ -155,13 +155,14 @@ WebCore::Color android_SkPMColorToWebCoreColor(SkPMColor pm) { SkColor c = SkPMColorToColor(pm); - return WebCore::Color(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), SkColorGetA(c)); + // need the cast to find the right constructor + return WebCore::Color((int)SkColorGetR(c), (int)SkColorGetG(c), + (int)SkColorGetB(c), (int)SkColorGetA(c)); } const static SkColor focusOuterColors[] = { SkColorSetARGB(0xff, 0xB3, 0x3F, 0x08), // normal focus ring select SkColorSetARGB(0xff, 0x46, 0xb0, 0x00), // fake focus ring select, for phone, email, text - SkColorSetARGB(0x00, 0x00, 0x00, 0x00), // no ring, for buttons SkColorSetARGB(0xff, 0xb0, 0x16, 0x00), // invalid focus ring color SkColorSetARGB(0xff, 0xAD, 0x5C, 0x0A), // normal focus ring pressed SkColorSetARGB(0xff, 0x36, 0xc0, 0x00) // fake focus ring pressed @@ -170,7 +171,6 @@ const static SkColor focusOuterColors[] = { const static SkColor focusInnerColors[] = { SkColorSetARGB(0xff, 0xFE, 0x92, 0x30), // normal focus ring select SkColorSetARGB(0xff, 0x8c, 0xd9, 0x00), // fake focus ring select, for phone, email, text - SkColorSetARGB(0x00, 0x00, 0x00, 0x00), // no ring, for buttons SkColorSetARGB(0xff, 0xd9, 0x2c, 0x00), // invalid focus ring color SkColorSetARGB(0xff, 0xFE, 0xBD, 0x3A), // normal focus ring pressed SkColorSetARGB(0xff, 0x7c, 0xe9, 0x00) // fake focus ring pressed @@ -178,8 +178,7 @@ const static SkColor focusInnerColors[] = { const static SkColor focusPressedColors[] = { SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B), // normal focus ring pressed - SkColorSetARGB(0x80, 0x7c, 0xe9, 0x00), // fake focus ring pressed - SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B) // button focus ring pressed + SkColorSetARGB(0x80, 0x7c, 0xe9, 0x00) // fake focus ring pressed }; #define FOCUS_RING_ROUNDEDNESS SkIntToScalar(5) // used to draw corners @@ -209,8 +208,6 @@ void FocusRing::DrawRing(SkCanvas* canvas, paint.setColor(focusPressedColors[flavor - NORMAL_ANIMATING]); canvas->drawPath(path, paint); } - if (flavor == BUTTON_ANIMATING) - return; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(FOCUS_RING_OUTER_DIAMETER); paint.setColor(focusOuterColors[flavor]); diff --git a/WebCore/platform/graphics/android/android_graphics.h b/WebCore/platform/graphics/android/android_graphics.h index 91c56b7..3bfa5ac 100644 --- a/WebCore/platform/graphics/android/android_graphics.h +++ b/WebCore/platform/graphics/android/android_graphics.h @@ -35,6 +35,7 @@ namespace WebCore { class FloatRect; class IntPoint; class IntRect; + class GraphicsContext; } SkPoint* android_setpt(SkPoint* dst, const WebCore::IntPoint& src); @@ -51,10 +52,12 @@ SkShader::TileMode android_convert_TileRule(WebCore::Image::TileRule); WebCore::Color android_SkPMColorToWebCoreColor(SkPMColor pm); +SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc); + // Data and methods for focus rings // used to inflate node cache entry -#define FOCUS_RING_HIT_TEST_RADIUS SkIntToScalar(5) +#define FOCUS_RING_HIT_TEST_RADIUS 5 // used to inval rectangle enclosing pressed state of focus ring #define FOCUS_RING_OUTER_DIAMETER SkFixedToScalar(SkIntToFixed(13)>>2) // 13/4 == 3.25 @@ -64,11 +67,9 @@ public: enum Flavor { NORMAL_FLAVOR, FAKE_FLAVOR, - BUTTON_NO_RING, INVALID_FLAVOR, NORMAL_ANIMATING, FAKE_ANIMATING, - BUTTON_ANIMATING, ANIMATING_COUNT = 2 }; diff --git a/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp b/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp index 1080d2d..0f2fccd 100644 --- a/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp +++ b/WebCore/platform/graphics/cairo/AffineTransformCairo.cpp @@ -41,17 +41,17 @@ AffineTransform::AffineTransform() AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) { - cairo_matrix_init(&m_transform, a, c, b, d, tx, ty); + cairo_matrix_init(&m_transform, a, b, c, d, tx, ty); } -AffineTransform::AffineTransform(const cairo_matrix_t &matrix) +AffineTransform::AffineTransform(const PlatformAffineTransform& matrix) { m_transform = matrix; } void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) { - cairo_matrix_init(&m_transform, a, c, b, d, tx, ty); + cairo_matrix_init(&m_transform, a, b, c, d, tx, ty); } void AffineTransform::map(double x, double y, double* x2, double* y2) const @@ -147,22 +147,22 @@ void AffineTransform::setA(double a) double AffineTransform::b() const { - return m_transform.xy; + return m_transform.yx; } void AffineTransform::setB(double b) { - m_transform.xy = b; + m_transform.yx = b; } double AffineTransform::c() const { - return m_transform.yx; + return m_transform.xy; } void AffineTransform::setC(double c) { - m_transform.yx = c; + m_transform.xy = c; } double AffineTransform::d() const diff --git a/WebCore/platform/graphics/cairo/FontCairo.cpp b/WebCore/platform/graphics/cairo/FontCairo.cpp new file mode 100644 index 0000000..9da9426 --- /dev/null +++ b/WebCore/platform/graphics/cairo/FontCairo.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Font.h" + +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "SimpleFontData.h" + +namespace WebCore { + +void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, const FloatPoint& point) const +{ + cairo_t* cr = context->platformContext(); + cairo_save(cr); + + font->setFont(cr); + + GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); + + float offset = point.x(); + for (int i = 0; i < numGlyphs; i++) { + glyphs[i].x = offset; + glyphs[i].y = point.y(); + offset += glyphBuffer.advanceAt(from + i); + } + + Color fillColor = context->fillColor(); + + // Text shadow, inspired by FontMac + IntSize shadowSize; + int shadowBlur = 0; + Color shadowColor; + bool hasShadow = context->textDrawingMode() == cTextFill && + context->getShadow(shadowSize, shadowBlur, shadowColor); + + // TODO: Blur support + if (hasShadow) { + // Disable graphics context shadows (not yet implemented) and paint them manually + context->clearShadow(); + Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); + cairo_save(cr); + + float red, green, blue, alpha; + shadowFillColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha); + + cairo_translate(cr, shadowSize.width(), shadowSize.height()); + cairo_show_glyphs(cr, glyphs, numGlyphs); + + cairo_restore(cr); + } + + if (context->textDrawingMode() & cTextFill) { + float red, green, blue, alpha; + fillColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha); + + cairo_show_glyphs(cr, glyphs, numGlyphs); + } + + if (context->textDrawingMode() & cTextStroke) { + Color strokeColor = context->strokeColor(); + float red, green, blue, alpha; + strokeColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha); + cairo_glyph_path(cr, glyphs, numGlyphs); + cairo_set_line_width(cr, context->strokeThickness()); + cairo_stroke(cr); + } + + // Re-enable the platform shadow we disabled earlier + if (hasShadow) + context->setShadow(shadowSize, shadowBlur, shadowColor); + + cairo_restore(cr); +} + +} diff --git a/WebCore/platform/graphics/cairo/GradientCairo.cpp b/WebCore/platform/graphics/cairo/GradientCairo.cpp new file mode 100644 index 0000000..7776424 --- /dev/null +++ b/WebCore/platform/graphics/cairo/GradientCairo.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Gradient.h" + +#include "CSSParser.h" +#include "GraphicsContext.h" +#include <cairo.h> + +namespace WebCore { + +void Gradient::platformDestroy() +{ + if (m_gradient) { + cairo_pattern_destroy(m_gradient); + m_gradient = 0; + } +} + +cairo_pattern_t* Gradient::platformGradient() +{ + if (m_gradient) + return m_gradient; + + if (m_radial) + m_gradient = cairo_pattern_create_radial(m_p0.x(), m_p0.y(), m_r0, m_p1.x(), m_p1.y(), m_r1); + else + m_gradient = cairo_pattern_create_linear(m_p0.x(), m_p0.y(), m_p1.x(), m_p1.y()); + + Vector<ColorStop>::iterator stopIterator = m_stops.begin(); + while (stopIterator != m_stops.end()) { + cairo_pattern_add_color_stop_rgba(m_gradient, stopIterator->stop, stopIterator->red, stopIterator->green, stopIterator->blue, stopIterator->alpha); + ++stopIterator; + } + + return m_gradient; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + cairo_t* cr = context->platformContext(); + + cairo_save(cr); + cairo_set_source(cr, platformGradient()); + cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); + cairo_fill(cr); + cairo_restore(cr); +} + +} //namespace diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index 08e8616..c403f44 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -1,6 +1,8 @@ /* * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> + * Copyright (C) 2008 Nuanti Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,10 +35,13 @@ #include "CairoPath.h" #include "FloatRect.h" #include "Font.h" +#include "ImageBuffer.h" #include "IntRect.h" #include "NotImplemented.h" #include "Path.h" +#include "Pattern.h" #include "SimpleFontData.h" + #include <cairo.h> #include <math.h> #include <stdio.h> @@ -48,6 +53,7 @@ #elif PLATFORM(WIN) #include <cairo-win32.h> #endif +#include "GraphicsContextPrivate.h" #include "GraphicsContextPlatformPrivateCairo.h" #ifndef M_PI @@ -72,6 +78,25 @@ static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const cairo_fill(cr); } +static inline cairo_pattern_t* applySpreadMethod(cairo_pattern_t* pattern, GradientSpreadMethod spreadMethod) +{ + switch (spreadMethod) { + case SpreadMethodPad: + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); + break; + case SpreadMethodReflect: + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT); + break; + case SpreadMethodRepeat: + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + break; + default: + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE); + break; + } + return pattern; +} + GraphicsContext::GraphicsContext(PlatformGraphicsContext* cr) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate) @@ -102,11 +127,13 @@ cairo_t* GraphicsContext::platformContext() const void GraphicsContext::savePlatformState() { cairo_save(m_data->cr); + m_data->save(); } void GraphicsContext::restorePlatformState() { cairo_restore(m_data->cr); + m_data->restore(); } // Draws a filled rectangle with a stroked border. @@ -287,32 +314,88 @@ void GraphicsContext::drawEllipse(const IntRect& rect) cairo_new_path(cr); } -// FIXME: This function needs to be adjusted to match the functionality on the Mac side. void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) { - if (paintingDisabled()) - return; - - if (strokeStyle() == NoStroke) + if (paintingDisabled() || strokeStyle() == NoStroke) return; int x = rect.x(); int y = rect.y(); float w = rect.width(); -#if 0 // FIXME: unused so far float h = rect.height(); float scaleFactor = h / w; float reverseScaleFactor = w / h; -#endif - float r = w / 2; + + float hRadius = w / 2; + float vRadius = h / 2; float fa = startAngle; float falen = fa + angleSpan; cairo_t* cr = m_data->cr; cairo_save(cr); - cairo_arc_negative(cr, x + r, y + r, r, -fa * M_PI/180, -falen * M_PI/180); + + if (w != h) + cairo_scale(cr, 1., scaleFactor); + + cairo_arc_negative(cr, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, -fa * M_PI/180, -falen * M_PI/180); + + if (w != h) + cairo_scale(cr, 1., reverseScaleFactor); + + float width = strokeThickness(); + int patWidth = 0; + + switch (strokeStyle()) { + case DottedStroke: + patWidth = static_cast<int>(width / 2); + break; + case DashedStroke: + patWidth = 3 * static_cast<int>(width / 2); + break; + default: + break; + } + setColor(cr, strokeColor()); - cairo_set_line_width(cr, strokeThickness()); + + if (patWidth) { + // Example: 80 pixels with a width of 30 pixels. + // Remainder is 20. The maximum pixels of line we could paint + // will be 50 pixels. + int distance; + if (hRadius == vRadius) + distance = static_cast<int>((M_PI * hRadius) / 2.0); + else // We are elliptical and will have to estimate the distance + distance = static_cast<int>((M_PI * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0)) / 2.0); + + int remainder = distance % patWidth; + int coverage = distance - remainder; + int numSegments = coverage / patWidth; + + float patternOffset = 0.0; + // Special case 1px dotted borders for speed. + if (patWidth == 1) + patternOffset = 1.0; + else { + bool evenNumberOfSegments = numSegments % 2 == 0; + if (remainder) + evenNumberOfSegments = !evenNumberOfSegments; + if (evenNumberOfSegments) { + if (remainder) { + patternOffset += patWidth - remainder; + patternOffset += remainder / 2.0; + } else + patternOffset = patWidth / 2.0; + } else { + if (remainder) + patternOffset = (patWidth - remainder) / 2.0; + } + } + + double dash = patWidth; + cairo_set_dash(cr, &dash, 1, patternOffset); + } + cairo_stroke(cr); cairo_restore(cr); } @@ -350,13 +433,96 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points cairo_restore(cr); } -void GraphicsContext::fillRect(const IntRect& rect, const Color& color) +void GraphicsContext::fillPath() { if (paintingDisabled()) return; - if (color.alpha()) - fillRectSourceOver(m_data->cr, rect, color); + cairo_t* cr = m_data->cr; + cairo_save(cr); + + 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); + } + break; + case PatternColorSpace: + cairo_set_source(cr, m_common->state.fillPattern.get()->createPlatformPattern(getCTM())); + 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(); + pattern = applySpreadMethod(pattern, spreadMethod()); + cairo_set_source(cr, pattern); + cairo_clip(cr); + cairo_paint_with_alpha(cr, m_common->state.globalAlpha); + break; + } + cairo_restore(cr); +} + +void GraphicsContext::strokePath() +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + 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); + } + break; + case PatternColorSpace: + cairo_set_source(cr, m_common->state.strokePattern.get()->createPlatformPattern(getCTM())); + 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); + break; + case GradientColorSpace: + cairo_pattern_t* pattern = m_common->state.strokeGradient.get()->platformGradient(); + pattern = applySpreadMethod(pattern, spreadMethod()); + cairo_set_source(cr, pattern); + 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); + break; + } + cairo_restore(cr); +} + +void GraphicsContext::drawPath() +{ + fillPath(); + strokePath(); +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + cairo_t* cr = m_data->cr; + cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); + fillPath(); } void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) @@ -368,7 +534,7 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) fillRectSourceOver(m_data->cr, rect, color); } -void GraphicsContext::clip(const IntRect& rect) +void GraphicsContext::clip(const FloatRect& rect) { if (paintingDisabled()) return; @@ -379,6 +545,7 @@ void GraphicsContext::clip(const IntRect& rect) cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); + m_data->clip(rect); } void GraphicsContext::drawFocusRing(const Color& color) @@ -386,27 +553,48 @@ void GraphicsContext::drawFocusRing(const Color& color) if (paintingDisabled()) return; - int radius = (focusRingWidth() - 1) / 2; - int offset = radius + focusRingOffset(); - const Vector<IntRect>& rects = focusRingRects(); unsigned rectCount = rects.size(); - IntRect finalFocusRect; - for (unsigned i = 0; i < rectCount; i++) { - IntRect focusRect = rects[i]; - focusRect.inflate(offset); - finalFocusRect.unite(focusRect); - } cairo_t* cr = m_data->cr; cairo_save(cr); - // FIXME: These rects should be rounded - cairo_rectangle(cr, finalFocusRect.x(), finalFocusRect.y(), finalFocusRect.width(), finalFocusRect.height()); + cairo_push_group(cr); + cairo_new_path(cr); + +#if PLATFORM(GTK) + GdkRegion* reg = gdk_region_new(); + for (unsigned i = 0; i < rectCount; i++) { + GdkRectangle rect = rects[i]; + gdk_region_union_with_rect(reg, &rect); + } + gdk_cairo_region(cr, reg); + gdk_region_destroy(reg); + + setColor(cr, color); + cairo_set_line_width(cr, 2.0f); + setPlatformStrokeStyle(DottedStroke); +#else + int radius = (focusRingWidth() - 1) / 2; + for (unsigned i = 0; i < rectCount; i++) + addPath(Path::createRoundedRectangle(rects[i], FloatSize(radius, radius))); // Force the alpha to 50%. This matches what the Mac does with outline rings. Color ringColor(color.red(), color.green(), color.blue(), 127); setColor(cr, ringColor); - cairo_stroke(cr); + cairo_set_line_width(cr, focusRingWidth()); + setPlatformStrokeStyle(SolidStroke); +#endif + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_stroke_preserve(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); + cairo_fill(cr); + + cairo_pop_group_to_source(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_paint(cr); cairo_restore(cr); } @@ -430,7 +618,6 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, if (paintingDisabled()) return; -#if PLATFORM(GTK) cairo_t* cr = m_data->cr; cairo_save(cr); @@ -441,13 +628,14 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, else cairo_set_source_rgb(cr, 1, 0, 0); +#if PLATFORM(GTK) // We ignore most of the provided constants in favour of the platform style pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness); - - cairo_restore(cr); #else notImplemented(); #endif + + cairo_restore(cr); } FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) @@ -480,6 +668,7 @@ void GraphicsContext::translate(float x, float y) cairo_t* cr = m_data->cr; cairo_translate(cr, x, y); + m_data->translate(x, y); } IntPoint GraphicsContext::origin() @@ -492,14 +681,14 @@ IntPoint GraphicsContext::origin() void GraphicsContext::setPlatformFillColor(const Color& col) { - // FIXME: this is probably a no-op but I'm not sure - // notImplemented(); // commented-out because it's chatty and clutters output + // Cairo contexts can't hold separate fill and stroke colors + // so we set them just before we actually fill or stroke } void GraphicsContext::setPlatformStrokeColor(const Color& col) { - // FIXME: this is probably a no-op but I'm not sure - //notImplemented(); // commented-out because it's chatty and clutters output + // Cairo contexts can't hold separate fill and stroke colors + // so we set them just before we actually fill or stroke } void GraphicsContext::setPlatformStrokeThickness(float strokeThickness) @@ -532,9 +721,6 @@ void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle) case DashedStroke: cairo_set_dash(m_data->cr, dashPattern, 2, 0); break; - default: - notImplemented(); - break; } } @@ -543,9 +729,6 @@ void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) notImplemented(); } -#if PLATFORM(GTK) -// FIXME: This should be moved to something like GraphicsContextCairoGTK.cpp, -// as there is a Windows implementation in platform/graphics/win/GraphicsContextCairoWin.cpp void GraphicsContext::concatCTM(const AffineTransform& transform) { if (paintingDisabled()) @@ -554,8 +737,8 @@ void GraphicsContext::concatCTM(const AffineTransform& transform) cairo_t* cr = m_data->cr; const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); cairo_transform(cr, matrix); + m_data->concatCTM(transform); } -#endif void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) { @@ -580,13 +763,20 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness cairo_set_fill_rule(cr, savedFillRule); } +void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer* imageBuffer) +{ + if (paintingDisabled()) + return; -void GraphicsContext::setShadow(IntSize const&, int, Color const&) + notImplemented(); +} + +void GraphicsContext::setPlatformShadow(IntSize const&, int, Color const&) { notImplemented(); } -void GraphicsContext::clearShadow() +void GraphicsContext::clearPlatformShadow() { notImplemented(); } @@ -599,6 +789,7 @@ void GraphicsContext::beginTransparencyLayer(float opacity) cairo_t* cr = m_data->cr; cairo_push_group(cr); m_data->layers.append(opacity); + m_data->beginTransparencyLayer(); } void GraphicsContext::endTransparencyLayer() @@ -611,6 +802,7 @@ void GraphicsContext::endTransparencyLayer() cairo_pop_group_to_source(cr); cairo_paint_with_alpha(cr, m_data->layers.last()); m_data->layers.removeLast(); + m_data->endTransparencyLayer(); } void GraphicsContext::clearRect(const FloatRect& rect) @@ -635,9 +827,8 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float width) cairo_t* cr = m_data->cr; cairo_save(cr); cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); - setColor(cr, strokeColor()); cairo_set_line_width(cr, width); - cairo_stroke(cr); + strokePath(); cairo_restore(cr); } @@ -661,6 +852,11 @@ void GraphicsContext::setLineCap(LineCap lineCap) cairo_set_line_cap(m_data->cr, cairoCap); } +void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) +{ + cairo_set_dash(m_data->cr, dashes.data(), dashes.size(), dashOffset); +} + void GraphicsContext::setLineJoin(LineJoin lineJoin) { if (paintingDisabled()) @@ -689,9 +885,14 @@ void GraphicsContext::setMiterLimit(float miter) cairo_set_miter_limit(m_data->cr, miter); } -void GraphicsContext::setAlpha(float) +void GraphicsContext::setAlpha(float alpha) { - notImplemented(); + m_common->state.globalAlpha = alpha; +} + +float GraphicsContext::getAlpha() +{ + return m_common->state.globalAlpha; } static inline cairo_operator_t toCairoOperator(CompositeOperator op) @@ -772,6 +973,7 @@ void GraphicsContext::clip(const Path& path) cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); + m_data->clip(path); } void GraphicsContext::clipOut(const Path& path) @@ -779,6 +981,7 @@ void GraphicsContext::clipOut(const Path& path) if (paintingDisabled()) return; +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,4,0) cairo_t* cr = m_data->cr; double x1, y1, x2, y2; cairo_clip_extents(cr, &x1, &y1, &x2, &y2); @@ -789,6 +992,9 @@ void GraphicsContext::clipOut(const Path& path) cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); +#else + notImplemented(); +#endif } void GraphicsContext::rotate(float radians) @@ -797,6 +1003,7 @@ void GraphicsContext::rotate(float radians) return; cairo_rotate(m_data->cr, radians); + m_data->rotate(radians); } void GraphicsContext::scale(const FloatSize& size) @@ -805,6 +1012,7 @@ void GraphicsContext::scale(const FloatSize& size) return; cairo_scale(m_data->cr, size.width(), size.height()); + m_data->scale(size); } void GraphicsContext::clipOut(const IntRect& r) @@ -812,6 +1020,7 @@ void GraphicsContext::clipOut(const IntRect& r) if (paintingDisabled()) return; +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,4,0) cairo_t* cr = m_data->cr; double x1, y1, x2, y2; cairo_clip_extents(cr, &x1, &y1, &x2, &y2); @@ -821,6 +1030,9 @@ void GraphicsContext::clipOut(const IntRect& r) cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); +#else + notImplemented(); +#endif } void GraphicsContext::clipOutEllipseInRect(const IntRect& r) @@ -865,17 +1077,6 @@ GdkDrawable* GraphicsContext::gdkDrawable() const return GDK_DRAWABLE(m_data->expose->window); } - -IntPoint GraphicsContext::translatePoint(const IntPoint& point) const -{ - cairo_matrix_t tm; - cairo_get_matrix(m_data->cr, &tm); - double x = point.x(); - double y = point.y(); - - cairo_matrix_transform_point(&tm, &x, &y); - return IntPoint(x, y); -} #endif void GraphicsContext::setUseAntialiasing(bool enable) @@ -889,6 +1090,15 @@ void GraphicsContext::setUseAntialiasing(bool enable) cairo_set_antialias(m_data->cr, enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); } +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) +{ +} + +InterpolationQuality GraphicsContext::imageInterpolationQuality() const +{ + return InterpolationDefault; +} + } // namespace WebCore #endif // PLATFORM(CAIRO) diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index f0ef490..9a14555 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -44,7 +44,7 @@ namespace WebCore { class GraphicsContextPlatformPrivate { public: GraphicsContextPlatformPrivate() - : cr(0) + : cr(0) #if PLATFORM(GTK) , expose(0) #elif PLATFORM(WIN) @@ -62,8 +62,28 @@ public: #if PLATFORM(WIN) // On Windows, we need to update the HDC for form controls to draw in the right place. + void save(); + void restore(); + void clip(const FloatRect&); + void clip(const Path&); + void scale(const FloatSize&); + void rotate(float); + void translate(float, float); + void concatCTM(const AffineTransform&); void beginTransparencyLayer() { m_transparencyCount++; } void endTransparencyLayer() { m_transparencyCount--; } +#else + // On everything else, we do nothing. + void save() {} + void restore() {} + void clip(const FloatRect&) {} + void clip(const Path&) {} + void scale(const FloatSize&) {} + void rotate(float) {} + void translate(float, float) {} + void concatCTM(const AffineTransform&) {} + void beginTransparencyLayer() {} + void endTransparencyLayer() {} #endif cairo_t* cr; diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 776529e..5f65ed2 100644 --- a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) 2007 Holger Hans Peter Freyther <zecke@selfish.org> + * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,39 +28,47 @@ #include "config.h" #include "ImageBuffer.h" +#include "Base64.h" +#include "BitmapImage.h" #include "GraphicsContext.h" -#include <cairo.h> +#include "ImageData.h" +#include "MIMETypeRegistry.h" #include "NotImplemented.h" +#include "Pattern.h" +#include "PlatformString.h" + +#include <cairo.h> +#include <wtf/Vector.h> using namespace std; namespace WebCore { -auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool) +ImageBufferData::ImageBufferData(const IntSize& size) + : m_surface(0) { - cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - size.width(), size.height()); - if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) - return auto_ptr<ImageBuffer>(); - - return auto_ptr<ImageBuffer>(new ImageBuffer(surface)); } -ImageBuffer::ImageBuffer(_cairo_surface* surface) - : m_surface(surface) +ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) + : m_data(size) + , m_size(size) { - cairo_t* cr = cairo_create(m_surface); - m_context.set(new GraphicsContext(cr)); + success = false; // Make early return mean error. + m_data.m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + size.width(), + size.height()); + if (cairo_surface_status(m_data.m_surface) != CAIRO_STATUS_SUCCESS) + return; // create will notice we didn't set m_initialized and fail. - /* - * The context is now owned by the GraphicsContext - */ - cairo_destroy(cr); + cairo_t* cr = cairo_create(m_data.m_surface); + m_context.set(new GraphicsContext(cr)); + cairo_destroy(cr); // The context is now owned by the GraphicsContext. + success = true; } ImageBuffer::~ImageBuffer() { - cairo_surface_destroy(m_surface); + cairo_surface_destroy(m_data.m_surface); } GraphicsContext* ImageBuffer::context() const @@ -67,10 +76,149 @@ GraphicsContext* ImageBuffer::context() const return m_context.get(); } -cairo_surface_t* ImageBuffer::surface() const +Image* ImageBuffer::image() const +{ + if (!m_image) { + // It's assumed that if image() is called, the actual rendering to the + // GraphicsContext must be done. + ASSERT(context()); + // BitmapImage will release the passed in surface on destruction + m_image = BitmapImage::create(cairo_surface_reference(m_data.m_surface)); + } + return m_image.get(); +} + +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const +{ + ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); + + 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(); + + 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()); + + 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; + + int stride = cairo_image_surface_get_stride(m_data.m_surface); + unsigned destBytesPerRow = 4 * rect.width(); + + unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + unsigned char *row = dataSrc + stride * (y + originy); + for (int x = 0; x < numColumns; x++) { + uint32_t *pixel = (uint32_t *) row + x + originx; + int basex = x * 4; + if (unsigned int alpha = (*pixel & 0xff000000) >> 24) { + destRows[basex] = (*pixel & 0x00ff0000) >> 16; + destRows[basex + 1] = (*pixel & 0x0000ff00) >> 8; + destRows[basex + 2] = (*pixel & 0x000000ff); + destRows[basex + 3] = alpha; + } else + reinterpret_cast<uint32_t*>(destRows + basex)[0] = pixel[0]; + } + destRows += destBytesPerRow; + } + + return result; +} + +void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) +{ + ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); + + unsigned char* dataDst = cairo_image_surface_get_data(m_data.m_surface); + + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < m_size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= m_size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < m_size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= m_size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * source->width(); + int stride = cairo_image_surface_get_stride(m_data.m_surface); + + 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++) { + uint32_t *pixel = (uint32_t *) row + x + destx; + int basex = x * 4; + if (unsigned int alpha = srcRows[basex + 3]) { + *pixel = alpha << 24 | srcRows[basex] << 16 | srcRows[basex + 1] << 8 | srcRows[basex + 2]; + } else + pixel[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + } + srcRows += srcBytesPerRow; + } +} + +static cairo_status_t writeFunction(void* closure, const unsigned char* data, unsigned int length) { - return m_surface; + Vector<char>* in = reinterpret_cast<Vector<char>*>(closure); + in->append(data, length); + return CAIRO_STATUS_SUCCESS; } +String ImageBuffer::toDataURL(const String& mimeType) const +{ + cairo_surface_t* image = cairo_get_target(context()->platformContext()); + if (!image) + return "data:,"; + String actualMimeType("image/png"); + if (MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)) + actualMimeType = mimeType; + + Vector<char> in; + // Only PNG output is supported for now. + cairo_surface_write_to_png_stream(image, writeFunction, &in); + + Vector<char> out; + base64Encode(in, out); + + return "data:" + actualMimeType + ";base64," + String(out.data(), out.size()); } + +} // namespace WebCore diff --git a/WebCore/platform/graphics/cairo/ImageBufferData.h b/WebCore/platform/graphics/cairo/ImageBufferData.h new file mode 100644 index 0000000..49f15df --- /dev/null +++ b/WebCore/platform/graphics/cairo/ImageBufferData.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageBufferData_h +#define ImageBufferData_h + +#include "cairo.h" + +namespace WebCore { + +class IntSize; + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); + + cairo_surface_t* m_surface; +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/WebCore/platform/graphics/cairo/ImageCairo.cpp b/WebCore/platform/graphics/cairo/ImageCairo.cpp index 4faa512..0a35cf2 100644 --- a/WebCore/platform/graphics/cairo/ImageCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageCairo.cpp @@ -43,23 +43,56 @@ void FrameData::clear() if (m_frame) { cairo_surface_destroy(m_frame); m_frame = 0; - m_duration = 0.; - m_hasAlpha = true; + // NOTE: We purposefully don't reset metadata here, so that even if we + // throw away previously-decoded data, animation loops can still access + // properties like frame durations without re-decoding. } } -void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, CompositeOperator op) +BitmapImage::BitmapImage(cairo_surface_t* surface, ImageObserver* observer) + : Image(observer) + , m_currentFrame(0) + , m_frames(0) + , m_frameTimer(0) + , m_repetitionCount(cAnimationNone) + , m_repetitionCountStatus(Unknown) + , m_repetitionsComplete(0) + , m_isSolidColor(false) + , m_animationFinished(true) + , m_allDataReceived(true) + , m_haveSize(true) + , m_sizeAvailable(true) + , m_decodedSize(0) + , m_haveFrameCount(true) + , m_frameCount(1) { - if (!m_source.initialized()) - return; + initPlatformData(); - FloatRect srcRect(src); - FloatRect dstRect(dst); + // TODO: check to be sure this is an image surface + + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + m_decodedSize = width * height * 4; + m_size = IntSize(width, height); + + m_frames.grow(1); + m_frames[0].m_frame = surface; + m_frames[0].m_hasAlpha = cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR; + m_frames[0].m_haveMetadata = true; + checkForSolidColor(); +} + +void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, CompositeOperator op) +{ + startAnimation(); cairo_surface_t* image = frameAtIndex(m_currentFrame); if (!image) // If it's too early we won't have an image yet. return; + FloatRect srcRect(src); + FloatRect dstRect(dst); + if (mayFillWithSolidColor()) { fillWithSolidColor(context, dstRect, solidColor(), op); return; @@ -97,12 +130,11 @@ void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const Flo cairo_set_source(cr, pattern); cairo_pattern_destroy(pattern); cairo_rectangle(cr, 0, 0, dstRect.width(), dstRect.height()); - cairo_fill(cr); + cairo_clip(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); cairo_restore(cr); - startAnimation(); - if (imageObserver()) imageObserver()->didDraw(this); } diff --git a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp index 8cc15b3..b7a4cbb 100644 --- a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp @@ -26,19 +26,21 @@ #include "config.h" #include "ImageSource.h" -#include "SharedBuffer.h" #if PLATFORM(CAIRO) +#include "BMPImageDecoder.h" #include "GIFImageDecoder.h" +#include "ICOImageDecoder.h" #include "JPEGImageDecoder.h" #include "PNGImageDecoder.h" -#include "BMPImageDecoder.h" -#include "ICOImageDecoder.h" -#include "XBMImageDecoder.h" - +#include "SharedBuffer.h" #include <cairo.h> +#if !PLATFORM(WIN) +#include "XBMImageDecoder.h" +#endif + namespace WebCore { ImageDecoder* createDecoder(const Vector<char>& data) @@ -78,17 +80,20 @@ ImageDecoder* createDecoder(const Vector<char>& data) !memcmp(contents, "\000\000\002\000", 4)) return new ICOImageDecoder(); +#if !PLATFORM(WIN) // XBMs require 8 bytes of info. if (length >= 8 && strncmp(contents, "#define ", 8) == 0) return new XBMImageDecoder(); +#endif // Give up. We don't know what the heck this is. return 0; } ImageSource::ImageSource() - : m_decoder(0) -{} + : m_decoder(0) +{ +} ImageSource::~ImageSource() { @@ -137,6 +142,11 @@ IntSize ImageSource::size() const return m_decoder->size(); } +IntSize ImageSource::frameSizeAtIndex(size_t) const +{ + return size(); +} + int ImageSource::repetitionCount() { if (!m_decoder) @@ -152,6 +162,9 @@ size_t ImageSource::frameCount() const NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { + if (!initialized()) + return 0; + if (!m_decoder) return 0; diff --git a/WebCore/platform/graphics/cairo/PathCairo.cpp b/WebCore/platform/graphics/cairo/PathCairo.cpp index e4c9f72..3f8d588 100644 --- a/WebCore/platform/graphics/cairo/PathCairo.cpp +++ b/WebCore/platform/graphics/cairo/PathCairo.cpp @@ -76,15 +76,15 @@ void Path::clear() bool Path::isEmpty() const { - // FIXME: if/when the patch to get current pt return status is applied - // double dx,dy; - // return cairo_get_current_point(cr, &dx, &dy); - cairo_t* cr = platformPath()->m_cr; +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,5,10) + return !cairo_has_current_point(cr); +#else cairo_path_t* p = cairo_copy_path(cr); bool hasData = p->num_data; cairo_path_destroy(p); return !hasData; +#endif } void Path::translate(const FloatSize& p) @@ -180,7 +180,11 @@ FloatRect Path::boundingRect() const { cairo_t* cr = platformPath()->m_cr; double x0, x1, y0, y1; - cairo_fill_extents(cr, &x0, &y0, &x1, &y1); +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 6, 0) + cairo_path_extents(cr, &x0, &y0, &x1, &y1); +#else + cairo_stroke_extents(cr, &x0, &y0, &x1, &y1); +#endif return FloatRect(x0, y0, x1 - x0, y1 - y0); } @@ -239,6 +243,7 @@ void Path::transform(const AffineTransform& trans) { cairo_t* m_cr = platformPath()->m_cr; cairo_matrix_t c_matrix = cairo_matrix_t(trans); + cairo_matrix_invert(&c_matrix); cairo_transform(m_cr, &c_matrix); } diff --git a/WebCore/platform/graphics/cairo/PatternCairo.cpp b/WebCore/platform/graphics/cairo/PatternCairo.cpp new file mode 100644 index 0000000..16cebf8 --- /dev/null +++ b/WebCore/platform/graphics/cairo/PatternCairo.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Pattern.h" + +#include "AffineTransform.h" +#include "GraphicsContext.h" + +#include <cairo.h> + +namespace WebCore { + +cairo_pattern_t* Pattern::createPlatformPattern(const AffineTransform& 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); + cairo_pattern_set_matrix(pattern, pattern_matrix); + if (m_repeatX || m_repeatY) + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + return pattern; +} + +} diff --git a/WebCore/platform/graphics/cg/AffineTransformCG.cpp b/WebCore/platform/graphics/cg/AffineTransformCG.cpp index 8fdd1e6..4f0bca0 100644 --- a/WebCore/platform/graphics/cg/AffineTransformCG.cpp +++ b/WebCore/platform/graphics/cg/AffineTransformCG.cpp @@ -37,8 +37,8 @@ namespace WebCore { AffineTransform::AffineTransform() + : m_transform(CGAffineTransformIdentity) { - m_transform = CGAffineTransformIdentity; } AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) @@ -51,9 +51,9 @@ AffineTransform::AffineTransform(double a, double b, double c, double d, double narrowPrecisionToCGFloat(ty)); } -AffineTransform::AffineTransform(CGAffineTransform t) +AffineTransform::AffineTransform(const PlatformAffineTransform& t) + : m_transform(t) { - m_transform = t; } void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) @@ -190,7 +190,7 @@ AffineTransform AffineTransform::inverse() const return AffineTransform(); } -AffineTransform::operator CGAffineTransform() const +AffineTransform::operator PlatformAffineTransform() const { return m_transform; } diff --git a/WebCore/platform/graphics/cg/GradientCG.cpp b/WebCore/platform/graphics/cg/GradientCG.cpp new file mode 100644 index 0000000..c189fd5 --- /dev/null +++ b/WebCore/platform/graphics/cg/GradientCG.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Gradient.h" + +#include "CSSParser.h" +#include "GraphicsContext.h" + +#include <ApplicationServices/ApplicationServices.h> + +namespace WebCore { + +void Gradient::platformDestroy() +{ + CGShadingRelease(m_gradient); + m_gradient = 0; +} + +static void gradientCallback(void* info, const CGFloat* in, CGFloat* out) +{ + float r, g, b, a; + static_cast<const Gradient*>(info)->getColor(*in, &r, &g, &b, &a); + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = a; +} + +CGShadingRef Gradient::platformGradient() +{ + if (m_gradient) + return m_gradient; + + const CGFloat intervalRanges[2] = { 0, 1 }; + const CGFloat colorComponentRanges[4 * 2] = { 0, 1, 0, 1, 0, 1, 0, 1 }; + const CGFunctionCallbacks gradientCallbacks = { 0, gradientCallback, 0 }; + CGFunctionRef colorFunction = CGFunctionCreate(this, 1, intervalRanges, 4, colorComponentRanges, &gradientCallbacks); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + if (m_radial) + m_gradient = CGShadingCreateRadial(colorSpace, m_p0, m_r0, m_p1, m_r1, colorFunction, true, true); + else + m_gradient = CGShadingCreateAxial(colorSpace, m_p0, m_p1, colorFunction, true, true); + + CGColorSpaceRelease(colorSpace); + CGFunctionRelease(colorFunction); + + return m_gradient; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + context->clip(rect); + CGContextDrawShading(context->platformContext(), platformGradient()); +} + +} //namespace diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index c490dcb..3f0e6e7 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,11 +30,21 @@ #include "AffineTransform.h" #include "FloatConversion.h" +#include "GraphicsContextPrivate.h" #include "GraphicsContextPlatformPrivateCG.h" +#include "ImageBuffer.h" #include "KURL.h" #include "Path.h" +#include "Pattern.h" +#include <CoreGraphics/CGBitmapContext.h> #include <CoreGraphics/CGPDFContext.h> #include <wtf/MathExtras.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/RetainPtr.h> + +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) +#define HAVE_CG_INTERPOLATION_MEDIUM 1 +#endif using namespace std; @@ -98,6 +109,8 @@ void GraphicsContext::restorePlatformState() // Draws a filled rectangle with a stroked border. void GraphicsContext::drawRect(const IntRect& rect) { + // FIXME: this function does not handle patterns and gradients + // like drawPath does, it probably should. if (paintingDisabled()) return; @@ -252,14 +265,7 @@ void GraphicsContext::drawEllipse(const IntRect& rect) CGContextAddArc(context, rect.x() + r, rect.y() + r, r, 0.0f, 2.0f * piFloat, 0); CGContextClosePath(context); - if (fillColor().alpha()) { - if (strokeStyle() != NoStroke) - // stroke and fill - CGContextDrawPath(context, kCGPathFillStroke); - else - CGContextFillPath(context); - } else if (strokeStyle() != NoStroke) - CGContextStrokePath(context); + drawPath(); } @@ -372,29 +378,177 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points CGContextAddLineToPoint(context, points[i].x(), points[i].y()); CGContextClosePath(context); - if (fillColor().alpha()) { - if (strokeStyle() != NoStroke) - CGContextDrawPath(context, kCGPathEOFillStroke); - else - CGContextEOFillPath(context); - } else - CGContextStrokePath(context); + drawPath(); CGContextRestoreGState(context); } -void GraphicsContext::fillRect(const IntRect& rect, const Color& color) +static void applyStrokePattern(GraphicsContext* context, Pattern* pattern) +{ + CGContextRef cgContext = context->platformContext(); + + CGPatternRef platformPattern = pattern->createPlatformPattern(context->getCTM()); + if (!platformPattern) + return; + + CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0); + CGContextSetStrokeColorSpace(cgContext, patternSpace); + CGColorSpaceRelease(patternSpace); + + const CGFloat patternAlpha = 1; + CGContextSetStrokePattern(cgContext, platformPattern, &patternAlpha); + CGPatternRelease(platformPattern); +} + +static void applyFillPattern(GraphicsContext* context, Pattern* pattern) +{ + CGContextRef cgContext = context->platformContext(); + + CGPatternRef platformPattern = pattern->createPlatformPattern(context->getCTM()); + if (!platformPattern) + return; + + CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0); + CGContextSetFillColorSpace(cgContext, patternSpace); + CGColorSpaceRelease(patternSpace); + + const CGFloat patternAlpha = 1; + CGContextSetFillPattern(cgContext, platformPattern, &patternAlpha); + CGPatternRelease(platformPattern); +} + +static inline bool calculateDrawingMode(const GraphicsContextState& state, CGPathDrawingMode& mode) +{ + bool shouldFill = state.fillColorSpace == PatternColorSpace || state.fillColor.alpha(); + bool shouldStroke = state.strokeColorSpace == PatternColorSpace || (state.strokeStyle != NoStroke && state.strokeColor.alpha()); + bool useEOFill = state.fillRule == RULE_EVENODD; + + if (shouldFill) { + if (shouldStroke) { + if (useEOFill) + mode = kCGPathEOFillStroke; + else + mode = kCGPathFillStroke; + } else { // fill, no stroke + if (useEOFill) + mode = kCGPathEOFill; + else + mode = kCGPathFill; + } + } else { + // Setting mode to kCGPathStroke even if shouldStroke is false. In that case, we return false and mode will not be used, + // but the compiler will not compain about an uninitialized variable. + mode = kCGPathStroke; + } + + return shouldFill || shouldStroke; +} + +void GraphicsContext::drawPath() { if (paintingDisabled()) return; - if (color.alpha()) { - CGContextRef context = platformContext(); - Color oldFillColor = fillColor(); - if (oldFillColor != color) - setCGFillColor(context, color); + + CGContextRef context = platformContext(); + const GraphicsContextState& state = m_common->state; + + if (state.fillColorSpace == GradientColorSpace || state.strokeColorSpace == GradientColorSpace) { + // We don't have any optimized way to fill & stroke a path using gradients + fillPath(); + strokePath(); + return; + } + + if (state.fillColorSpace == PatternColorSpace) + applyFillPattern(this, m_common->state.fillPattern.get()); + if (state.strokeColorSpace == PatternColorSpace) + applyStrokePattern(this, m_common->state.strokePattern.get()); + + CGPathDrawingMode drawingMode; + if (calculateDrawingMode(state, drawingMode)) + CGContextDrawPath(context, drawingMode); +} + +static inline void fillPathWithFillRule(CGContextRef context, WindRule fillRule) +{ + if (fillRule == RULE_EVENODD) + CGContextEOFillPath(context); + else + CGContextFillPath(context); +} + +void GraphicsContext::fillPath() +{ + if (paintingDisabled()) + return; + + CGContextRef context = platformContext(); + switch (m_common->state.fillColorSpace) { + case SolidColorSpace: + if (fillColor().alpha()) + fillPathWithFillRule(context, fillRule()); + break; + case PatternColorSpace: + applyFillPattern(this, m_common->state.fillPattern.get()); + fillPathWithFillRule(context, fillRule()); + break; + case GradientColorSpace: + CGContextSaveGState(context); + if (fillRule() == RULE_EVENODD) + CGContextEOClip(context); + else + CGContextClip(context); + CGContextDrawShading(context, m_common->state.fillGradient->platformGradient()); + CGContextRestoreGState(context); + break; + } +} + +void GraphicsContext::strokePath() +{ + if (paintingDisabled()) + return; + + CGContextRef context = platformContext(); + switch (m_common->state.strokeColorSpace) { + case SolidColorSpace: + if (fillColor().alpha()) + CGContextStrokePath(context); + break; + case PatternColorSpace: + applyStrokePattern(this, m_common->state.strokePattern.get()); + CGContextStrokePath(context); + break; + case GradientColorSpace: + CGContextSaveGState(context); + CGContextReplacePathWithStrokedPath(context); + CGContextClip(context); + CGContextDrawShading(context, m_common->state.strokeGradient->platformGradient()); + CGContextRestoreGState(context); + break; + } +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + CGContextRef context = platformContext(); + switch (m_common->state.fillColorSpace) { + case SolidColorSpace: + if (fillColor().alpha()) + CGContextFillRect(context, rect); + break; + case PatternColorSpace: + applyFillPattern(this, m_common->state.fillPattern.get()); CGContextFillRect(context, rect); - if (oldFillColor != color) - setCGFillColor(context, oldFillColor); + break; + case GradientColorSpace: + CGContextSaveGState(context); + CGContextClipToRect(context, rect); + CGContextDrawShading(context, m_common->state.fillGradient->platformGradient()); + CGContextRestoreGState(context); + break; } } @@ -424,14 +578,13 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef setCGFillColor(context, color); addPath(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); - CGContextFillPath(context); + fillPath(); if (oldFillColor != color) setCGFillColor(context, oldFillColor); } - -void GraphicsContext::clip(const IntRect& rect) +void GraphicsContext::clip(const FloatRect& rect) { if (paintingDisabled()) return; @@ -478,6 +631,18 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness CGContextEOClip(context); } +void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer* imageBuffer) +{ + if (paintingDisabled()) + return; + + CGContextTranslateCTM(platformContext(), rect.x(), rect.y() + rect.height()); + CGContextScaleCTM(platformContext(), 1, -1); + CGContextClipToMask(platformContext(), FloatRect(FloatPoint(), rect.size()), imageBuffer->image()->getCGImageRef()); + CGContextScaleCTM(platformContext(), 1, -1); + CGContextTranslateCTM(platformContext(), -rect.x(), -rect.y() - rect.height()); +} + void GraphicsContext::beginTransparencyLayer(float opacity) { if (paintingDisabled()) @@ -501,19 +666,28 @@ void GraphicsContext::endTransparencyLayer() m_data->m_userToDeviceTransformKnownToBeIdentity = false; } -void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color) +void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Color& color) { - // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp - blur = min(blur, 1000); - if (paintingDisabled()) return; CGContextRef context = platformContext(); + CGAffineTransform transform = CGContextGetCTM(context); + + CGFloat A = transform.a * transform.a + transform.b * transform.b; + CGFloat B = transform.a * transform.c + transform.b * transform.d; + CGFloat C = B; + CGFloat D = transform.c * transform.c + transform.d * transform.d; - CGFloat width = size.width(); - CGFloat height = size.height(); + CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D))))); + + // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp + CGFloat blurRadius = min(blur * smallEigenvalue, narrowPrecisionToCGFloat(1000.0)); + + CGSize sizeInDeviceSpace = CGSizeApplyAffineTransform(size, transform); + + CGFloat width = sizeInDeviceSpace.width; + CGFloat height = sizeInDeviceSpace.height; -#ifdef BUILDING_ON_TIGER // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated // to the desired integer. static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128); @@ -526,23 +700,22 @@ void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& colo height += extraShadowOffset; else if (height < 0) height -= extraShadowOffset; -#endif // Check for an invalid color, as this means that the color was not set for the shadow // and we should therefore just use the default shadow color. if (!color.isValid()) - CGContextSetShadow(context, CGSizeMake(width, -height), blur); // y is flipped. + CGContextSetShadow(context, CGSizeMake(width, height), blurRadius); else { CGColorRef colorCG = cgColor(color); CGContextSetShadowWithColor(context, - CGSizeMake(width, -height), // y is flipped. - blur, + CGSizeMake(width, height), + blurRadius, colorCG); CGColorRelease(colorCG); } } -void GraphicsContext::clearShadow() +void GraphicsContext::clearPlatformShadow() { if (paintingDisabled()) return; @@ -594,6 +767,11 @@ void GraphicsContext::setLineCap(LineCap cap) } } +void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) +{ + CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size()); +} + void GraphicsContext::setLineJoin(LineJoin join) { if (paintingDisabled()) @@ -610,7 +788,7 @@ void GraphicsContext::setLineJoin(LineJoin join) break; } } - + void GraphicsContext::beginPath() { CGContextBeginPath(platformContext()); @@ -789,20 +967,57 @@ void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) } } -void GraphicsContext::setUseLowQualityImageInterpolation(bool lowQualityMode) +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) { if (paintingDisabled()) return; - - CGContextSetInterpolationQuality(platformContext(), lowQualityMode ? kCGInterpolationNone : kCGInterpolationDefault); + + CGInterpolationQuality quality = kCGInterpolationDefault; + switch (mode) { + case InterpolationDefault: + quality = kCGInterpolationDefault; + break; + case InterpolationNone: + quality = kCGInterpolationNone; + break; + case InterpolationLow: + quality = kCGInterpolationLow; + break; + + // Fall through to InterpolationHigh if kCGInterpolationMedium is not available + case InterpolationMedium: +#if HAVE(CG_INTERPOLATION_MEDIUM) + quality = kCGInterpolationMedium; + break; +#endif + case InterpolationHigh: + quality = kCGInterpolationHigh; + break; + } + CGContextSetInterpolationQuality(platformContext(), quality); } -bool GraphicsContext::useLowQualityImageInterpolation() const +InterpolationQuality GraphicsContext::imageInterpolationQuality() const { if (paintingDisabled()) - return false; - - return CGContextGetInterpolationQuality(platformContext()); + return InterpolationDefault; + + CGInterpolationQuality quality = CGContextGetInterpolationQuality(platformContext()); + switch (quality) { + case kCGInterpolationDefault: + return InterpolationDefault; + case kCGInterpolationNone: + return InterpolationNone; + case kCGInterpolationLow: + return InterpolationLow; +#if HAVE(CG_INTERPOLATION_MEDIUM) + case kCGInterpolationMedium: + return InterpolationMedium; +#endif + case kCGInterpolationHigh: + return InterpolationHigh; + } + return InterpolationDefault; } void GraphicsContext::setPlatformTextDrawingMode(int mode) @@ -924,6 +1139,6 @@ void GraphicsContext::setCompositeOperation(CompositeOperator mode) CGContextSetBlendMode(platformContext(), target); } #endif - + } diff --git a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h index 937481b..8827ff7 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h +++ b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h @@ -45,11 +45,11 @@ public: CGContextRelease(m_cgContext); } -#if PLATFORM(MAC) +#if PLATFORM(MAC) || PLATFORM(CHROMIUM) // These methods do nothing on Mac. void save() {} void restore() {} - void clip(const IntRect&) {} + void clip(const FloatRect&) {} void clip(const Path&) {} void scale(const FloatSize&) {} void rotate(float) {} @@ -63,7 +63,7 @@ public: // On Windows, we need to update the HDC for form controls to draw in the right place. void save(); void restore(); - void clip(const IntRect&); + void clip(const FloatRect&); void clip(const Path&); void scale(const FloatSize&); void rotate(float); @@ -71,9 +71,7 @@ public: void concatCTM(const AffineTransform&); void beginTransparencyLayer() { m_transparencyCount++; } void endTransparencyLayer() { m_transparencyCount--; } -#endif -#if PLATFORM(WIN) HDC m_hdc; unsigned m_transparencyCount; #endif diff --git a/WebCore/platform/graphics/cg/ImageBufferCG.cpp b/WebCore/platform/graphics/cg/ImageBufferCG.cpp index 2d1ac01..502313b 100644 --- a/WebCore/platform/graphics/cg/ImageBufferCG.cpp +++ b/WebCore/platform/graphics/cg/ImageBufferCG.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,65 +21,69 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ImageBuffer.h" +#include "Base64.h" +#include "BitmapImage.h" +#include "CString.h" #include "GraphicsContext.h" - +#include "ImageData.h" +#include "MIMETypeRegistry.h" +#include "PlatformString.h" #include <ApplicationServices/ApplicationServices.h> #include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/RetainPtr.h> using namespace std; namespace WebCore { -auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool grayScale) +ImageBufferData::ImageBufferData(const IntSize&) + : m_data(0) { +} + +ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) + : m_data(size) + , m_size(size) +{ + success = false; // Make early return mean failure. + unsigned bytesPerRow; if (size.width() < 0 || size.height() < 0) - return auto_ptr<ImageBuffer>(); - unsigned int bytesPerRow = size.width(); + return; + bytesPerRow = size.width(); if (!grayScale) { // Protect against overflow if (bytesPerRow > 0x3FFFFFFF) - return auto_ptr<ImageBuffer>(); + return; bytesPerRow *= 4; } - void* imageBuffer = fastCalloc(size.height(), bytesPerRow); - if (!imageBuffer) - return auto_ptr<ImageBuffer>(); - + m_data.m_data = tryFastCalloc(size.height(), bytesPerRow); + ASSERT((reinterpret_cast<size_t>(m_data.m_data) & 2) == 0); + CGColorSpaceRef colorSpace = grayScale ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB(); - CGContextRef cgContext = CGBitmapContextCreate(imageBuffer, size.width(), size.height(), 8, bytesPerRow, + CGContextRef cgContext = CGBitmapContextCreate(m_data.m_data, size.width(), size.height(), 8, bytesPerRow, colorSpace, grayScale ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpace); - if (!cgContext) { - fastFree(imageBuffer); - return auto_ptr<ImageBuffer>(); - } + if (!cgContext) + return; - auto_ptr<GraphicsContext> context(new GraphicsContext(cgContext)); + m_context.set(new GraphicsContext(cgContext)); + m_context->scale(FloatSize(1, -1)); + m_context->translate(0, -size.height()); CGContextRelease(cgContext); - - return auto_ptr<ImageBuffer>(new ImageBuffer(imageBuffer, size, context)); -} - - -ImageBuffer::ImageBuffer(void* imageData, const IntSize& size, auto_ptr<GraphicsContext> context) - : m_data(imageData) - , m_size(size) - , m_context(context.release()) - , m_cgImage(0) -{ + success = true; } ImageBuffer::~ImageBuffer() { - fastFree(m_data); - CGImageRelease(m_cgImage); + fastFree(m_data.m_data); } GraphicsContext* ImageBuffer::context() const @@ -86,16 +91,185 @@ GraphicsContext* ImageBuffer::context() const return m_context.get(); } -CGImageRef ImageBuffer::cgImage() const +Image* ImageBuffer::image() const { - // It's assumed that if cgImage() is called, the actual rendering to the - // contained GraphicsContext must be done, as we create the CGImageRef here. - if (!m_cgImage) { + if (!m_image) { + // It's assumed that if image() is called, the actual rendering to the + // GraphicsContext must be done. ASSERT(context()); - m_cgImage = CGBitmapContextCreateImage(context()->platformContext()); + CGImageRef cgImage = CGBitmapContextCreateImage(context()->platformContext()); + // BitmapImage will release the passed in CGImage on destruction + m_image = BitmapImage::create(cgImage); + } + return m_image.get(); +} + +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const +{ + PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); + unsigned char* data = result->data()->data().data(); + + if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) + memset(data, 0, result->data()->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.x() + rect.width(); + if (endx > m_size.width()) + endx = m_size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.y() + rect.height(); + if (endy > m_size.height()) + endy = m_size.height(); + int numRows = endy - originy; + + unsigned srcBytesPerRow = 4 * m_size.width(); + unsigned destBytesPerRow = 4 * rect.width(); + + // ::create ensures that all ImageBuffers have valid data, so we don't need to check it here. + unsigned char* srcRows = reinterpret_cast<unsigned char*>(m_data.m_data) + originy * srcBytesPerRow + originx * 4; + unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + if (unsigned char alpha = srcRows[basex + 3]) { + destRows[basex] = (srcRows[basex] * 255) / alpha; + destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha; + destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha; + destRows[basex + 3] = alpha; + } else + reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + } + srcRows += srcBytesPerRow; + destRows += destBytesPerRow; + } + return result; +} + +void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) +{ + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < m_size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= m_size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < m_size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= m_size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * source->width(); + unsigned destBytesPerRow = 4 * m_size.width(); + + unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; + unsigned char* destRows = reinterpret_cast<unsigned char*>(m_data.m_data) + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + unsigned char alpha = srcRows[basex + 3]; + if (alpha != 255) { + destRows[basex] = (srcRows[basex] * alpha + 254) / 255; + destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255; + destRows[basex + 2] = (srcRows[basex + 2] * alpha + 254) / 255; + destRows[basex + 3] = alpha; + } else + reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + } + destRows += destBytesPerRow; + srcRows += srcBytesPerRow; } +} + +static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType) +{ +#if PLATFORM(MAC) + RetainPtr<CFStringRef> mimeTypeCFString(AdoptCF, mimeType.createCFString()); + return RetainPtr<CFStringRef>(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeTypeCFString.get(), 0)); +#else + // FIXME: Add Windows support for all the supported UTIs when a way to convert from MIMEType to UTI reliably is found. + // For now, only support PNG, JPEG, and GIF. See <rdar://problem/6095286>. + static const CFStringRef kUTTypePNG = CFSTR("public.png"); + static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg"); + static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif"); - return m_cgImage; + if (equalIgnoringCase(mimeType, "image/png")) + return kUTTypePNG; + if (equalIgnoringCase(mimeType, "image/jpeg")) + return kUTTypeJPEG; + if (equalIgnoringCase(mimeType, "image/gif")) + return kUTTypeGIF; + + ASSERT_NOT_REACHED(); + return kUTTypePNG; +#endif } +String ImageBuffer::toDataURL(const String& mimeType) const +{ + ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); + + RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context()->platformContext())); + if (!image) + return "data:,"; + + size_t width = CGImageGetWidth(image.get()); + size_t height = CGImageGetHeight(image.get()); + + OwnArrayPtr<uint32_t> imageData(new uint32_t[width * height]); + if (!imageData) + return "data:,"; + + RetainPtr<CGImageRef> transformedImage(AdoptCF, CGBitmapContextCreateImage(context()->platformContext())); + if (!transformedImage) + return "data:,"; + + RetainPtr<CFMutableDataRef> transformedImageData(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0)); + if (!transformedImageData) + return "data:,"; + + RetainPtr<CGImageDestinationRef> imageDestination(AdoptCF, CGImageDestinationCreateWithData(transformedImageData.get(), + utiFromMIMEType(mimeType).get(), 1, 0)); + if (!imageDestination) + return "data:,"; + + CGImageDestinationAddImage(imageDestination.get(), transformedImage.get(), 0); + CGImageDestinationFinalize(imageDestination.get()); + + Vector<char> in; + in.append(CFDataGetBytePtr(transformedImageData.get()), CFDataGetLength(transformedImageData.get())); + + Vector<char> out; + base64Encode(in, out); + out.append('\0'); + + return String::format("data:%s;base64,%s", mimeType.utf8().data(), out.data()); } + +} // namespace WebCore diff --git a/WebCore/platform/graphics/cg/ImageBufferData.h b/WebCore/platform/graphics/cg/ImageBufferData.h new file mode 100644 index 0000000..5e6fc4c --- /dev/null +++ b/WebCore/platform/graphics/cg/ImageBufferData.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageBufferData_h +#define ImageBufferData_h + +namespace WebCore { + +class IntSize; + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); + + void* m_data; +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/WebCore/platform/graphics/cg/ImageCG.cpp b/WebCore/platform/graphics/cg/ImageCG.cpp index 5958d86..8609c46 100644 --- a/WebCore/platform/graphics/cg/ImageCG.cpp +++ b/WebCore/platform/graphics/cg/ImageCG.cpp @@ -37,7 +37,7 @@ #include "PlatformString.h" #include <ApplicationServices/ApplicationServices.h> -#if PLATFORM(MAC) +#if PLATFORM(MAC) || PLATFORM(CHROMIUM) #include "WebCoreSystemInterface.h" #endif @@ -52,8 +52,9 @@ void FrameData::clear() if (m_frame) { CGImageRelease(m_frame); m_frame = 0; - m_duration = 0.0f; - m_hasAlpha = true; + // NOTE: We purposefully don't reset metadata here, so that even if we + // throw away previously-decoded data, animation loops can still access + // properties like frame durations without re-decoding. } } @@ -61,6 +62,37 @@ void FrameData::clear() // Image Class // ================================================ +BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer) + : Image(observer) + , m_currentFrame(0) + , m_frames(0) + , m_frameTimer(0) + , m_repetitionCount(cAnimationNone) + , m_repetitionCountStatus(Unknown) + , m_repetitionsComplete(0) + , m_isSolidColor(false) + , m_animationFinished(true) + , m_allDataReceived(true) + , m_haveSize(true) + , m_sizeAvailable(true) + , m_decodedSize(0) + , m_haveFrameCount(true) + , m_frameCount(1) +{ + initPlatformData(); + + CGFloat width = CGImageGetWidth(cgImage); + CGFloat height = CGImageGetHeight(cgImage); + m_decodedSize = width * height * 4; + m_size = IntSize(width, height); + + m_frames.grow(1); + m_frames[0].m_frame = cgImage; + m_frames[0].m_hasAlpha = true; + m_frames[0].m_haveMetadata = true; + checkForSolidColor(); +} + // Drawing Routines void BitmapImage::checkForSolidColor() @@ -97,78 +129,73 @@ CGImageRef BitmapImage::getCGImageRef() return frameAtIndex(0); } -void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp) +void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp) { - if (!m_source.initialized()) - return; - - CGRect fr = ctxt->roundToDevicePixels(srcRect); - CGRect ir = ctxt->roundToDevicePixels(dstRect); + startAnimation(); CGImageRef image = frameAtIndex(m_currentFrame); if (!image) // If it's too early we won't have an image yet. return; if (mayFillWithSolidColor()) { - fillWithSolidColor(ctxt, ir, solidColor(), compositeOp); + fillWithSolidColor(ctxt, destRect, solidColor(), compositeOp); return; } - // Get the height (in adjusted, i.e. scaled, coords) of the portion of the image - // that is currently decoded. This could be less that the actual height. - CGSize selfSize = size(); // full image size, in pixels - float curHeight = CGImageGetHeight(image); // height of loaded portion, in pixels - - CGSize adjustedSize = selfSize; - if (curHeight < selfSize.height) { - adjustedSize.height *= curHeight / selfSize.height; - - // Is the amount of available bands less than what we need to draw? If so, - // we may have to clip 'fr' if it goes outside the available bounds. - if (CGRectGetMaxY(fr) > adjustedSize.height) { - float frHeight = adjustedSize.height - fr.origin.y; // clip fr to available bounds - if (frHeight <= 0) - return; // clipped out entirely - ir.size.height *= (frHeight / fr.size.height); // scale ir proportionally to fr - fr.size.height = frHeight; - } - } + float currHeight = CGImageGetHeight(image); + if (currHeight <= srcRect.y()) + return; CGContextRef context = ctxt->platformContext(); ctxt->save(); + bool shouldUseSubimage = false; + + // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image + // and then set a clip to the portion that we want to display. + FloatRect adjustedDestRect = destRect; + FloatSize selfSize = currentFrameSize(); + if (srcRect.size() != selfSize) { + CGInterpolationQuality interpolationQuality = CGContextGetInterpolationQuality(context); + // When the image is scaled using high-quality interpolation, we create a temporary CGImage + // containing only the portion we want to display. We need to do this because high-quality + // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed + // into the destination rect. See <rdar://problem/6112909>. + shouldUseSubimage = (interpolationQuality == kCGInterpolationHigh || interpolationQuality == kCGInterpolationDefault) && srcRect.size() != destRect.size(); + if (shouldUseSubimage) { + image = CGImageCreateWithImageInRect(image, srcRect); + if (currHeight < srcRect.bottom()) { + ASSERT(CGImageGetHeight(image) == currHeight - CGRectIntegral(srcRect).origin.y); + adjustedDestRect.setHeight(destRect.height() / srcRect.height() * CGImageGetHeight(image)); + } + } else { + float xScale = srcRect.width() / destRect.width(); + float yScale = srcRect.height() / destRect.height(); + + adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale)); + adjustedDestRect.setSize(FloatSize(selfSize.width() / xScale, selfSize.height() / yScale)); + + CGContextClipToRect(context, destRect); + } + } + + // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. + if (!shouldUseSubimage && currHeight < selfSize.height()) + adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height()); + // Flip the coords. ctxt->setCompositeOperation(compositeOp); - CGContextTranslateCTM(context, ir.origin.x, ir.origin.y); + CGContextTranslateCTM(context, adjustedDestRect.x(), adjustedDestRect.bottom()); CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -ir.size.height); - - // Translated to origin, now draw at 0,0. - ir.origin.x = ir.origin.y = 0; - - // If we're drawing a sub portion of the image then create - // a image for the sub portion and draw that. - // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html - if (fr.size.width != adjustedSize.width || fr.size.height != adjustedSize.height) { - // Convert ft to image pixel coords: - float xscale = adjustedSize.width / selfSize.width; - float yscale = adjustedSize.height / curHeight; // yes, curHeight, not selfSize.height! - fr.origin.x /= xscale; - fr.origin.y /= yscale; - fr.size.width /= xscale; - fr.size.height /= yscale; - - image = CGImageCreateWithImageInRect(image, fr); - if (image) { - CGContextDrawImage(context, ir, image); - CFRelease(image); - } - } else // Draw the whole image. - CGContextDrawImage(context, ir, image); - + adjustedDestRect.setLocation(FloatPoint()); + + // Draw the image. + CGContextDrawImage(context, adjustedDestRect, image); + + if (shouldUseSubimage) + CGImageRelease(image); + ctxt->restore(); - - startAnimation(); if (imageObserver()) imageObserver()->didDraw(this); @@ -183,6 +210,9 @@ void Image::drawPatternCallback(void* info, CGContextRef context) void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { + if (!nativeImageForCurrentFrame()) + return; + ASSERT(patternTransform.isInvertible()); if (!patternTransform.isInvertible()) // Avoid a hang under CGContextDrawTiledImage on release builds. @@ -192,9 +222,8 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const ctxt->save(); CGContextClipToRect(context, destRect); ctxt->setCompositeOperation(op); - CGContextTranslateCTM(context, destRect.x(), destRect.y()); + CGContextTranslateCTM(context, destRect.x(), destRect.y() + destRect.height()); CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -destRect.height()); // Compute the scaled tile size. float scaledTileHeight = tileRect.height() * narrowPrecisionToFloat(patternTransform.d()); diff --git a/WebCore/platform/graphics/cg/ImageSourceCG.cpp b/WebCore/platform/graphics/cg/ImageSourceCG.cpp index 2bfc204..73907c9 100644 --- a/WebCore/platform/graphics/cg/ImageSourceCG.cpp +++ b/WebCore/platform/graphics/cg/ImageSourceCG.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,15 +25,17 @@ #include "config.h" #include "ImageSource.h" -#include "SharedBuffer.h" #if PLATFORM(CG) #include "IntSize.h" +#include "SharedBuffer.h" #include <ApplicationServices/ApplicationServices.h> namespace WebCore { +static const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32"); + ImageSource::ImageSource() : m_decoder(0) { @@ -52,15 +54,13 @@ void ImageSource::clear() } } -const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32"); - CFDictionaryRef imageSourceOptions() { static CFDictionaryRef options; if (!options) { - const void *keys[2] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 }; - const void *values[2] = { kCFBooleanTrue, kCFBooleanTrue }; + const void* keys[2] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 }; + const void* values[2] = { kCFBooleanTrue, kCFBooleanTrue }; options = CFDictionaryCreate(NULL, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } @@ -108,10 +108,10 @@ bool ImageSource::isSizeAvailable() return result; } -IntSize ImageSource::size() const +IntSize ImageSource::frameSizeAtIndex(size_t index) const { IntSize result; - CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()); + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions()); if (properties) { int w = 0, h = 0; CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); @@ -120,16 +120,23 @@ IntSize ImageSource::size() const num = (CFNumberRef)CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); if (num) CFNumberGetValue(num, kCFNumberIntType, &h); - result = IntSize(w, h); + result = IntSize(w, h); CFRelease(properties); } return result; } +IntSize ImageSource::size() const +{ + return frameSizeAtIndex(0); +} + int ImageSource::repetitionCount() { int result = cAnimationLoopOnce; // No property means loop once. - + if (!initialized()) + return result; + // A property with value 0 means loop forever. CFDictionaryRef properties = CGImageSourceCopyProperties(m_decoder, imageSourceOptions()); if (properties) { @@ -154,7 +161,23 @@ size_t ImageSource::frameCount() const CGImageRef ImageSource::createFrameAtIndex(size_t index) { - return CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions()); + if (!initialized()) + return 0; + + CGImageRef image = CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions()); + CFStringRef imageUTI = CGImageSourceGetType(m_decoder); + static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image"); + if (!imageUTI || !CFEqual(imageUTI, xbmUTI)) + return image; + + // If it is an xbm image, mask out all the white areas to render them transparent. + const CGFloat maskingColors[6] = {255, 255, 255, 255, 255, 255}; + CGImageRef maskedImage = CGImageCreateWithMaskingColors(image, maskingColors); + if (!maskedImage) + return image; + + CGImageRelease(image); + return maskedImage; } bool ImageSource::frameIsCompleteAtIndex(size_t index) @@ -164,6 +187,9 @@ bool ImageSource::frameIsCompleteAtIndex(size_t index) float ImageSource::frameDurationAtIndex(size_t index) { + if (!initialized()) + return 0; + float duration = 0; CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions()); if (properties) { diff --git a/WebCore/platform/graphics/cg/PDFDocumentImage.h b/WebCore/platform/graphics/cg/PDFDocumentImage.h index caddc05..5c9d4e1 100644 --- a/WebCore/platform/graphics/cg/PDFDocumentImage.h +++ b/WebCore/platform/graphics/cg/PDFDocumentImage.h @@ -38,14 +38,25 @@ namespace WebCore { class PDFDocumentImage : public Image { public: - PDFDocumentImage(); + static PassRefPtr<PDFDocumentImage> create() + { + return adoptRef(new PDFDocumentImage); + } ~PDFDocumentImage(); - + + virtual bool hasSingleSecurityOrigin() const { return true; } + virtual bool dataChanged(bool allDataReceived); + // FIXME: PDF Images are underreporting decoded sizes and will be unable + // to prune because these functions are not implemented yet. + virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) { } + virtual unsigned decodedSize() const { return 0; } + virtual IntSize size() const; private: + PDFDocumentImage(); virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); void setCurrentPage(int); diff --git a/WebCore/platform/graphics/cg/PathCG.cpp b/WebCore/platform/graphics/cg/PathCG.cpp index c0a0caf..1382589 100644 --- a/WebCore/platform/graphics/cg/PathCG.cpp +++ b/WebCore/platform/graphics/cg/PathCG.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. - * 2006 Rob Buis <buis@kde.org> + * 2006, 2008 Rob Buis <buis@kde.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -189,17 +189,17 @@ static void CGPathToCFStringApplierFunction(void* info, const CGPathElement *ele CGPoint* points = element->points; switch (element->type) { case kCGPathElementMoveToPoint: - CFStringAppendFormat(string, 0, CFSTR("M%.2f,%.2f"), points[0].x, points[0].y); + CFStringAppendFormat(string, 0, CFSTR("M%.2f,%.2f "), points[0].x, points[0].y); break; case kCGPathElementAddLineToPoint: - CFStringAppendFormat(string, 0, CFSTR("L%.2f,%.2f"), points[0].x, points[0].y); + CFStringAppendFormat(string, 0, CFSTR("L%.2f,%.2f "), points[0].x, points[0].y); break; case kCGPathElementAddQuadCurveToPoint: - CFStringAppendFormat(string, 0, CFSTR("Q%.2f,%.2f,%.2f,%.2f"), + CFStringAppendFormat(string, 0, CFSTR("Q%.2f,%.2f,%.2f,%.2f "), points[0].x, points[0].y, points[1].x, points[1].y); break; case kCGPathElementAddCurveToPoint: - CFStringAppendFormat(string, 0, CFSTR("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f"), + CFStringAppendFormat(string, 0, CFSTR("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f "), points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y); break; @@ -215,6 +215,8 @@ static CFStringRef CFStringFromCGPath(CGPathRef path) CFMutableStringRef string = CFStringCreateMutable(NULL, 0); CGPathApply(path, string, CGPathToCFStringApplierFunction); + CFStringTrimWhitespace(string); + return string; } diff --git a/WebCore/platform/graphics/cg/PatternCG.cpp b/WebCore/platform/graphics/cg/PatternCG.cpp new file mode 100644 index 0000000..e1f7a69 --- /dev/null +++ b/WebCore/platform/graphics/cg/PatternCG.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Pattern.h" + +#include "AffineTransform.h" +#include "GraphicsContext.h" + +#include <ApplicationServices/ApplicationServices.h> + +namespace WebCore { + +static void patternCallback(void* info, CGContextRef context) +{ + CGImageRef platformImage = static_cast<Image*>(info)->getCGImageRef(); + if (!platformImage) + return; + + CGRect rect = GraphicsContext(context).roundToDevicePixels( + FloatRect(0, 0, CGImageGetWidth(platformImage), CGImageGetHeight(platformImage))); + CGContextDrawImage(context, rect, platformImage); +} + +static void patternReleaseCallback(void* info) +{ + static_cast<Image*>(info)->deref(); +} + +CGPatternRef Pattern::createPlatformPattern(const AffineTransform& transform) const +{ + IntRect tileRect = tileImage()->rect(); + + AffineTransform patternTransform = transform; + patternTransform.scale(1, -1); + patternTransform.translate(0, -tileRect.height()); + + // If FLT_MAX should also be used for xStep or yStep, nothing is rendered. Using fractions of FLT_MAX also + // result in nothing being rendered. + // INT_MAX is almost correct, but there seems to be some number wrapping occuring making the fill + // pattern is not filled correctly. + // So, just pick a really large number that works. + float xStep = m_repeatX ? tileRect.width() : (100000000.0f); + float yStep = m_repeatY ? tileRect.height() : (100000000.0f); + + // The pattern will release the tile when it's done rendering in patternReleaseCallback + tileImage()->ref(); + + const CGPatternCallbacks patternCallbacks = { 0, patternCallback, patternReleaseCallback }; + return CGPatternCreate(tileImage(), tileRect, patternTransform, xStep, yStep, + kCGPatternTilingConstantSpacing, TRUE, &patternCallbacks); +} + +} diff --git a/WebCore/platform/graphics/filters/FEBlend.cpp b/WebCore/platform/graphics/filters/FEBlend.cpp new file mode 100644 index 0000000..7210367 --- /dev/null +++ b/WebCore/platform/graphics/filters/FEBlend.cpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@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 + aint 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" + +#if ENABLE(SVG) && ENABLE(SVG_FILTERS) +#include "FEBlend.h" + +namespace WebCore { + +FEBlend::FEBlend(FilterEffect* in, FilterEffect* in2, BlendModeType mode) + : FilterEffect() + , m_in(in) + , m_in2(in2) + , m_mode(mode) +{ +} + +PassRefPtr<FEBlend> FEBlend::create(FilterEffect* in, FilterEffect* in2, BlendModeType mode) +{ + return adoptRef(new FEBlend(in, in2, mode)); +} + +FilterEffect* FEBlend::in2() const +{ + return m_in2.get(); +} + +void FEBlend::setIn2(FilterEffect* in2) +{ + m_in2 = in2; +} + +BlendModeType FEBlend::blendMode() const +{ + return m_mode; +} + +void FEBlend::setBlendMode(BlendModeType mode) +{ + m_mode = mode; +} + +void FEBlend::apply() +{ +} + +void FEBlend::dump() +{ +} + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FILTERS) diff --git a/WebCore/platform/graphics/filters/FEBlend.h b/WebCore/platform/graphics/filters/FEBlend.h new file mode 100644 index 0000000..b2835e8 --- /dev/null +++ b/WebCore/platform/graphics/filters/FEBlend.h @@ -0,0 +1,64 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@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 + aint 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 SVGFEBlend_h +#define SVGFEBlend_h + +#if ENABLE(SVG) && ENABLE(SVG_FILTERS) +#include "FilterEffect.h" + +namespace WebCore { + + enum BlendModeType { + FEBLEND_MODE_UNKNOWN = 0, + FEBLEND_MODE_NORMAL = 1, + FEBLEND_MODE_MULTIPLY = 2, + FEBLEND_MODE_SCREEN = 3, + FEBLEND_MODE_DARKEN = 4, + FEBLEND_MODE_LIGHTEN = 5 + }; + + class FEBlend : public FilterEffect { + public: + static PassRefPtr<FEBlend> create(FilterEffect*, FilterEffect*, BlendModeType); + + FilterEffect* in2() const; + void setIn2(FilterEffect*); + + BlendModeType blendMode() const; + void setBlendMode(BlendModeType); + + virtual void apply(); + virtual void dump(); + + private: + FEBlend(FilterEffect*, FilterEffect*, BlendModeType); + + RefPtr<FilterEffect> m_in; + RefPtr<FilterEffect> m_in2; + BlendModeType m_mode; + }; + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FILTERS) + +#endif // SVGFEBlend_h diff --git a/WebCore/platform/graphics/filters/FEColorMatrix.cpp b/WebCore/platform/graphics/filters/FEColorMatrix.cpp new file mode 100644 index 0000000..f783106 --- /dev/null +++ b/WebCore/platform/graphics/filters/FEColorMatrix.cpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@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 + aint 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" + +#if ENABLE(SVG) && ENABLE(SVG_FILTERS) +#include "FEColorMatrix.h" + +namespace WebCore { + +FEColorMatrix::FEColorMatrix(FilterEffect* in, ColorMatrixType type, const Vector<float>& values) + : FilterEffect() + , m_in(in) + , m_type(type) + , m_values(values) +{ +} + +PassRefPtr<FEColorMatrix> FEColorMatrix::create(FilterEffect* in, ColorMatrixType type, const Vector<float>& values) +{ + return adoptRef(new FEColorMatrix(in, type, values)); +} + +ColorMatrixType FEColorMatrix::type() const +{ + return m_type; +} + +void FEColorMatrix::setType(ColorMatrixType type) +{ + m_type = type; +} + +const Vector<float>& FEColorMatrix::values() const +{ + return m_values; +} + +void FEColorMatrix::setValues(const Vector<float> &values) +{ + m_values = values; +} + +void FEColorMatrix::apply() +{ +} + +void FEColorMatrix::dump() +{ +} + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FILTERS) diff --git a/WebCore/platform/graphics/filters/FEColorMatrix.h b/WebCore/platform/graphics/filters/FEColorMatrix.h new file mode 100644 index 0000000..d8193ed --- /dev/null +++ b/WebCore/platform/graphics/filters/FEColorMatrix.h @@ -0,0 +1,64 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@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 + aint 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 SVGFEColorMatrix_h +#define SVGFEColorMatrix_h + +#if ENABLE(SVG) && ENABLE(SVG_FILTERS) +#include "FilterEffect.h" +#include <wtf/Vector.h> + +namespace WebCore { + + enum ColorMatrixType { + FECOLORMATRIX_TYPE_UNKNOWN = 0, + FECOLORMATRIX_TYPE_MATRIX = 1, + FECOLORMATRIX_TYPE_SATURATE = 2, + FECOLORMATRIX_TYPE_HUEROTATE = 3, + FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4 + }; + + class FEColorMatrix : public FilterEffect { + public: + static PassRefPtr<FEColorMatrix> create(FilterEffect*, ColorMatrixType, const Vector<float>&); + + ColorMatrixType type() const; + void setType(ColorMatrixType); + + const Vector<float>& values() const; + void setValues(const Vector<float>&); + + virtual void apply(); + virtual void dump(); + + private: + FEColorMatrix(FilterEffect*, ColorMatrixType, const Vector<float>&); + + RefPtr<FilterEffect> m_in; + ColorMatrixType m_type; + Vector<float> m_values; + }; + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FILTERS) + +#endif // SVGFEColorMatrix_h diff --git a/WebCore/platform/graphics/filters/FEComponentTransfer.cpp b/WebCore/platform/graphics/filters/FEComponentTransfer.cpp new file mode 100644 index 0000000..708ea3e --- /dev/null +++ b/WebCore/platform/graphics/filters/FEComponentTransfer.cpp @@ -0,0 +1,96 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@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 + aint 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" + +#if ENABLE(SVG) && ENABLE(SVG_FILTERS) +#include "FEComponentTransfer.h" + +namespace WebCore { + +FEComponentTransfer::FEComponentTransfer(FilterEffect* in, const ComponentTransferFunction& redFunc, + const ComponentTransferFunction& greenFunc, const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc) + : FilterEffect() + , m_in(in) + , m_redFunc(redFunc) + , m_greenFunc(greenFunc) + , m_blueFunc(blueFunc) + , m_alphaFunc(alphaFunc) +{ +} + +PassRefPtr<FEComponentTransfer> FEComponentTransfer::create(FilterEffect* in, const ComponentTransferFunction& redFunc, + const ComponentTransferFunction& greenFunc, const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc) +{ + return adoptRef(new FEComponentTransfer(in, redFunc, greenFunc, blueFunc, alphaFunc)); +} + +ComponentTransferFunction FEComponentTransfer::redFunction() const +{ + return m_redFunc; +} + +void FEComponentTransfer::setRedFunction(const ComponentTransferFunction& func) +{ + m_redFunc = func; +} + +ComponentTransferFunction FEComponentTransfer::greenFunction() const +{ + return m_greenFunc; +} + +void FEComponentTransfer::setGreenFunction(const ComponentTransferFunction& func) +{ + m_greenFunc = func; +} + +ComponentTransferFunction FEComponentTransfer::blueFunction() const +{ + return m_blueFunc; +} + +void FEComponentTransfer::setBlueFunction(const ComponentTransferFunction& func) +{ + m_blueFunc = func; +} + +ComponentTransferFunction FEComponentTransfer::alphaFunction() const +{ + return m_alphaFunc; +} + +void FEComponentTransfer::setAlphaFunction(const ComponentTransferFunction& func) +{ + m_alphaFunc = func; +} + +void FEComponentTransfer::apply() +{ +} + +void FEComponentTransfer::dump() +{ +} + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FILTERS) diff --git a/WebCore/platform/graphics/filters/FEComponentTransfer.h b/WebCore/platform/graphics/filters/FEComponentTransfer.h new file mode 100644 index 0000000..20d70c0 --- /dev/null +++ b/WebCore/platform/graphics/filters/FEComponentTransfer.h @@ -0,0 +1,99 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@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 + aint 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 SVGFEComponentTransfer_h +#define SVGFEComponentTransfer_h + +#if ENABLE(SVG) && ENABLE(SVG_FILTERS) +#include "FilterEffect.h" +#include "SVGFEDisplacementMap.h" + +#include <wtf/Vector.h> + +namespace WebCore { + + enum ComponentTransferType { + FECOMPONENTTRANSFER_TYPE_UNKNOWN = 0, + FECOMPONENTTRANSFER_TYPE_IDENTITY = 1, + FECOMPONENTTRANSFER_TYPE_TABLE = 2, + FECOMPONENTTRANSFER_TYPE_DISCRETE = 3, + FECOMPONENTTRANSFER_TYPE_LINEAR = 4, + FECOMPONENTTRANSFER_TYPE_GAMMA = 5 + }; + + struct ComponentTransferFunction { + ComponentTransferFunction() + : type(FECOMPONENTTRANSFER_TYPE_UNKNOWN) + , slope(0.0f) + , intercept(0.0f) + , amplitude(0.0f) + , exponent(0.0f) + , offset(0.0f) + { + } + + ComponentTransferType type; + + float slope; + float intercept; + float amplitude; + float exponent; + float offset; + + Vector<float> tableValues; + }; + + class FEComponentTransfer : public FilterEffect { + public: + static PassRefPtr<FEComponentTransfer> create(FilterEffect*, const ComponentTransferFunction&, + const ComponentTransferFunction&, const ComponentTransferFunction&, const ComponentTransferFunction&); + + ComponentTransferFunction redFunction() const; + void setRedFunction(const ComponentTransferFunction&); + + ComponentTransferFunction greenFunction() const; + void setGreenFunction(const ComponentTransferFunction&); + + ComponentTransferFunction blueFunction() const; + void setBlueFunction(const ComponentTransferFunction&); + + ComponentTransferFunction alphaFunction() const; + void setAlphaFunction(const ComponentTransferFunction&); + + virtual void apply(); + virtual void dump(); + + private: + FEComponentTransfer(FilterEffect*,const ComponentTransferFunction&, const ComponentTransferFunction&, + const ComponentTransferFunction&, const ComponentTransferFunction&); + + RefPtr<FilterEffect> m_in; + ComponentTransferFunction m_redFunc; + ComponentTransferFunction m_greenFunc; + ComponentTransferFunction m_blueFunc; + ComponentTransferFunction m_alphaFunc; + }; + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FILTERS) + +#endif // SVGFEComponentTransfer_h diff --git a/WebCore/platform/graphics/filters/FEComposite.cpp b/WebCore/platform/graphics/filters/FEComposite.cpp new file mode 100644 index 0000000..0b5ce94 --- /dev/null +++ b/WebCore/platform/graphics/filters/FEComposite.cpp @@ -0,0 +1,108 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@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 + aint 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" + +#if ENABLE(SVG) && ENABLE(SVG_FILTERS) +#include "FEComposite.h" + +namespace WebCore { + +FEComposite::FEComposite(FilterEffect* in, FilterEffect* in2, const CompositeOperationType& type, + const float& k1, const float& k2, const float& k3, const float& k4) + : FilterEffect() + , m_in(in) + , m_in2(in2) + , m_type(type) + , m_k1(k1) + , m_k2(k2) + , m_k3(k3) + , m_k4(k4) +{ +} + +PassRefPtr<FEComposite> FEComposite::create(FilterEffect* in, FilterEffect* in2, const CompositeOperationType& type, + const float& k1, const float& k2, const float& k3, const float& k4) +{ + return adoptRef(new FEComposite(in, in2, type, k1, k2, k3, k4)); +} + +CompositeOperationType FEComposite::operation() const +{ + return m_type; +} + +void FEComposite::setOperation(CompositeOperationType type) +{ + m_type = type; +} + +float FEComposite::k1() const +{ + return m_k1; +} + +void FEComposite::setK1(float k1) +{ + m_k1 = k1; +} + +float FEComposite::k2() const +{ + return m_k2; +} + +void FEComposite::setK2(float k2) +{ + m_k2 = k2; +} + +float FEComposite::k3() const +{ + return m_k3; +} + +void FEComposite::setK3(float k3) +{ + m_k3 = k3; +} + +float FEComposite::k4() const +{ + return m_k4; +} + +void FEComposite::setK4(float k4) +{ + m_k4 = k4; +} + +void FEComposite::apply() +{ +} + +void FEComposite::dump() +{ +} + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FILTERS) diff --git a/WebCore/platform/graphics/filters/FEComposite.h b/WebCore/platform/graphics/filters/FEComposite.h new file mode 100644 index 0000000..d205395 --- /dev/null +++ b/WebCore/platform/graphics/filters/FEComposite.h @@ -0,0 +1,81 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@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 + aint 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 SVGFEComposite_h +#define SVGFEComposite_h + +#if ENABLE(SVG) && ENABLE(SVG_FILTERS) +#include "FilterEffect.h" +#include "PlatformString.h" + +namespace WebCore { + + enum CompositeOperationType { + FECOMPOSITE_OPERATOR_UNKNOWN = 0, + FECOMPOSITE_OPERATOR_OVER = 1, + FECOMPOSITE_OPERATOR_IN = 2, + FECOMPOSITE_OPERATOR_OUT = 3, + FECOMPOSITE_OPERATOR_ATOP = 4, + FECOMPOSITE_OPERATOR_XOR = 5, + FECOMPOSITE_OPERATOR_ARITHMETIC = 6 + }; + + class FEComposite : public FilterEffect { + public: + static PassRefPtr<FEComposite> create(FilterEffect*, FilterEffect*, const CompositeOperationType&, + const float&, const float&, const float&, const float&); + + CompositeOperationType operation() const; + void setOperation(CompositeOperationType); + + float k1() const; + void setK1(float); + + float k2() const; + void setK2(float); + + float k3() const; + void setK3(float); + + float k4() const; + void setK4(float); + + virtual void apply(); + virtual void dump(); + + private: + FEComposite(FilterEffect*, FilterEffect*, const CompositeOperationType&, + const float&, const float&, const float&, const float&); + + RefPtr<FilterEffect> m_in; + RefPtr<FilterEffect> m_in2; + CompositeOperationType m_type; + float m_k1; + float m_k2; + float m_k3; + float m_k4; + }; + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FILTERS) + +#endif // SVGFEComposite_h diff --git a/WebCore/platform/graphics/gtk/FontCacheGtk.cpp b/WebCore/platform/graphics/gtk/FontCacheGtk.cpp index aec5758..d2b43cc 100644 --- a/WebCore/platform/graphics/gtk/FontCacheGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontCacheGtk.cpp @@ -1,31 +1,21 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com - * All rights reserved. + * Copyright (C) 2008 Alp Toker <alp@atoker.com> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: + * 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. * - * 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 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. * - * 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" @@ -45,12 +35,33 @@ void FontCache::platformInit() const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) { - return new SimpleFontData(FontPlatformData(font.fontDescription(), font.family().family())); +#if defined(USE_FREETYPE) + FcResult fresult; + FontPlatformData* prim = const_cast<FontPlatformData*>(&font.primaryFont()->m_font); + + if (!prim->m_fallbacks) + prim->m_fallbacks = FcFontSort(NULL, prim->m_pattern, FcTrue, NULL, &fresult); + + FcFontSet* fs = prim->m_fallbacks; + + for (int i = 0; i < fs->nfont; i++) { + FcPattern* fin = FcFontRenderPrepare(NULL, prim->m_pattern, fs->fonts[i]); + cairo_font_face_t* fontFace = cairo_ft_font_face_create_for_pattern(fin); + FontPlatformData alternateFont(fontFace, font.fontDescription().computedPixelSize(), false, false); + cairo_font_face_destroy(fontFace); + alternateFont.m_pattern = fin; + SimpleFontData* sfd = getCachedFontData(&alternateFont); + if (sfd->containsCharacters(characters, length)) + return sfd; + } +#endif + + return 0; } FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) { - return new FontPlatformData(font.fontDescription(), font.family().family()); + return 0; } FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) @@ -61,10 +72,8 @@ FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fo return getCachedFontPlatformData(fontDescription, timesStr); } -bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family) +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) { - FontPlatformData platformData(fontDescription, family); - return platformData.m_pattern != 0; } FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) diff --git a/WebCore/platform/graphics/gtk/FontCustomPlatformData.cpp b/WebCore/platform/graphics/gtk/FontCustomPlatformData.cpp index 3ff63c2..bb2e064 100644 --- a/WebCore/platform/graphics/gtk/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/gtk/FontCustomPlatformData.cpp @@ -31,7 +31,7 @@ FontCustomPlatformData::~FontCustomPlatformData() cairo_font_face_destroy(m_fontFace); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode) { return FontPlatformData(m_fontFace, size, bold, italic); } diff --git a/WebCore/platform/graphics/gtk/FontCustomPlatformData.h b/WebCore/platform/graphics/gtk/FontCustomPlatformData.h index 237904d..b36cc79 100644 --- a/WebCore/platform/graphics/gtk/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/gtk/FontCustomPlatformData.h @@ -21,6 +21,7 @@ #ifndef FontCustomPlatformData_h #define FontCustomPlatformData_h +#include "FontRenderingMode.h" #include <wtf/Noncopyable.h> typedef struct _cairo_font_face cairo_font_face_t; @@ -37,7 +38,7 @@ struct FontCustomPlatformData : Noncopyable { ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); cairo_font_face_t* m_fontFace; }; diff --git a/WebCore/platform/graphics/gtk/FontCustomPlatformDataPango.cpp b/WebCore/platform/graphics/gtk/FontCustomPlatformDataPango.cpp new file mode 100644 index 0000000..4f2f2bb --- /dev/null +++ b/WebCore/platform/graphics/gtk/FontCustomPlatformDataPango.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 Alp Toker <alp@atoker.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. + * + */ + +#include "config.h" +#include "FontCustomPlatformData.h" + +#include "SharedBuffer.h" +#include "FontPlatformData.h" + +namespace WebCore { + +FontCustomPlatformData::~FontCustomPlatformData() +{ +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode) +{ + return FontPlatformData(m_fontFace, size, bold, italic); +} + +static void releaseData(void* data) +{ + static_cast<SharedBuffer*>(data)->deref(); +} + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) +{ + // FIXME: we need support in pango to read fonts from memory to implement this.y + return 0; +} + +} diff --git a/WebCore/platform/graphics/gtk/FontGtk.cpp b/WebCore/platform/graphics/gtk/FontGtk.cpp index 5d50c6e..288ba91 100644 --- a/WebCore/platform/graphics/gtk/FontGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontGtk.cpp @@ -5,6 +5,7 @@ * Copyright (c) 2007 Kouhei Sutou * Copyright (C) 2007 Alp Toker <alp@atoker.com> * Copyright (C) 2008 Xan Lopez <xan@gnome.org> + * Copyright (C) 2008 Nuanti Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +38,18 @@ #include "SimpleFontData.h" #include <cairo.h> +#include <gdk/gdk.h> #include <pango/pango.h> #include <pango/pangocairo.h> +#if defined(USE_FREETYPE) +#include <pango/pangofc-fontmap.h> +#endif + +#if !defined(PANGO_VERSION_CHECK) +// PANGO_VERSION_CHECK() and pango_layout_get_line_readonly() appeared in 1.5.2 +#define pango_layout_get_line_readonly pango_layout_get_line +#define PANGO_VERSION_CHECK(major,minor,micro) 0 +#endif namespace WebCore { @@ -88,7 +99,7 @@ static void utf16_to_utf8(const UChar* aText, gint aLength, char* &text, gint &l } glong items_written; - text = g_utf16_to_utf8(aText, aLength, NULL, &items_written, NULL); + text = g_utf16_to_utf8(reinterpret_cast<const gunichar2*>(aText), aLength, NULL, &items_written, NULL); length = items_written; if (need_copy) @@ -133,10 +144,29 @@ static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int fro static void setPangoAttributes(const Font* font, const TextRun& run, PangoLayout* layout) { +#if defined(USE_FREETYPE) + if (font->primaryFont()->m_font.m_pattern) { + PangoFontDescription* desc = pango_fc_font_description_from_pattern(font->primaryFont()->m_font.m_pattern, FALSE); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + } +#elif defined(USE_PANGO) + if (font->primaryFont()->m_font.m_font) { + PangoFontDescription* desc = pango_font_describe(font->primaryFont()->m_font.m_font); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + } +#endif + + pango_layout_set_auto_dir(layout, FALSE); + + PangoContext* pangoContext = pango_layout_get_context(layout); + PangoDirection direction = run.rtl() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR; + pango_context_set_base_dir(pangoContext, direction); PangoAttrList* list = pango_attr_list_new(); PangoAttribute* attr; - attr = pango_attr_size_new_absolute((int)(font->size() * PANGO_SCALE)); + attr = pango_attr_size_new_absolute(font->pixelSize() * PANGO_SCALE); attr->end_index = G_MAXUINT; pango_attr_list_insert_before(list, attr); @@ -151,78 +181,111 @@ static void setPangoAttributes(const Font* font, const TextRun& run, PangoLayout pango_layout_set_attributes(layout, list); pango_attr_list_unref(list); - - pango_layout_set_auto_dir(layout, FALSE); - - PangoContext* pangoContext = pango_layout_get_context(layout); - PangoDirection direction = run.rtl() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR; - pango_context_set_base_dir(pangoContext, direction); } -void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, - int from, int numGlyphs, const FloatPoint& point) const +void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { - cairo_t* cr = graphicsContext->platformContext(); + cairo_t* cr = context->platformContext(); cairo_save(cr); + cairo_translate(cr, point.x(), point.y()); - // Set the text color to use for drawing. - float red, green, blue, alpha; - Color penColor = graphicsContext->fillColor(); - penColor.getRGBA(red, green, blue, alpha); - cairo_set_source_rgba(cr, red, green, blue, alpha); - - font->setFont(cr); + PangoLayout* layout = pango_cairo_create_layout(cr); + setPangoAttributes(this, run, layout); - GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); + gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); + pango_layout_set_text(layout, utf8, -1); - float offset = point.x(); + // Our layouts are single line + PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); - for (int i = 0; i < numGlyphs; i++) { - glyphs[i].x = offset; - glyphs[i].y = point.y(); - offset += glyphBuffer.advanceAt(from + i); + GdkRegion* partialRegion = NULL; + if (to - from != run.length()) { + // Clip the region of the run to be rendered + char* start = g_utf8_offset_to_pointer(utf8, from); + char* end = g_utf8_offset_to_pointer(start, to - from); + int ranges[] = {start - utf8, end - utf8}; + partialRegion = gdk_pango_layout_line_get_clip_region(layoutLine, 0, 0, ranges, 1); + gdk_region_shrink(partialRegion, 0, -pixelSize()); } - cairo_show_glyphs(cr, glyphs, numGlyphs); - cairo_restore(cr); -} + Color fillColor = context->fillColor(); + float red, green, blue, alpha; -void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const -{ - cairo_t* cr = context->platformContext(); - cairo_save(cr); + // Text shadow, inspired by FontMac + IntSize shadowSize; + int shadowBlur = 0; + Color shadowColor; + bool hasShadow = context->textDrawingMode() == cTextFill && + context->getShadow(shadowSize, shadowBlur, shadowColor); - PangoLayout* layout = pango_cairo_create_layout(cr); + // TODO: Blur support + if (hasShadow) { + // Disable graphics context shadows (not yet implemented) and paint them manually + context->clearShadow(); + Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); + cairo_save(cr); - gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), from, to); - pango_layout_set_text(layout, utf8, -1); - g_free(utf8); + shadowFillColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha); - setPangoAttributes(this, run, layout); + cairo_translate(cr, shadowSize.width(), shadowSize.height()); - // Set the text color to use for drawing. - float red, green, blue, alpha; - Color penColor = context->fillColor(); - penColor.getRGBA(red, green, blue, alpha); + if (partialRegion) { + gdk_cairo_region(cr, partialRegion); + cairo_clip(cr); + } + + pango_cairo_show_layout_line(cr, layoutLine); + + cairo_restore(cr); + } + + fillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); - // Our layouts are single line - cairo_move_to(cr, point.x(), point.y()); - PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); + if (partialRegion) { + gdk_cairo_region(cr, partialRegion); + cairo_clip(cr); + } + pango_cairo_show_layout_line(cr, layoutLine); + if (context->textDrawingMode() & cTextStroke) { + Color strokeColor = context->strokeColor(); + strokeColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha); + pango_cairo_layout_line_path(cr, layoutLine); + cairo_set_line_width(cr, context->strokeThickness()); + cairo_stroke(cr); + } + + // Re-enable the platform shadow we disabled earlier + if (hasShadow) + context->setShadow(shadowSize, shadowBlur, shadowColor); + + // Pango sometimes leaves behind paths we don't want + cairo_new_path(cr); + + if (partialRegion) + gdk_region_destroy(partialRegion); + + g_free(utf8); g_object_unref(layout); + cairo_restore(cr); } -// FIXME: we should create the layout with our actual context, but it seems -// we can't access it from here +// We should create the layout with our actual context but we can't access it from here. static PangoLayout* getDefaultPangoLayout(const TextRun& run) { - PangoFontMap* map = pango_cairo_font_map_get_default(); - PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(map)); + static PangoFontMap* map = pango_cairo_font_map_get_default(); +#if PANGO_VERSION_CHECK(1,21,5) + static PangoContext* pangoContext = pango_font_map_create_context(map); +#else + // Deprecated in Pango 1.21. + static PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(map)); +#endif PangoLayout* layout = pango_layout_new(pangoContext); - g_object_unref(pangoContext); return layout; } @@ -237,11 +300,11 @@ float Font::floatWidthForComplexText(const TextRun& run) const gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); - g_free(utf8); - int layoutWidth; - pango_layout_get_size(layout, &layoutWidth, 0); - float width = (float)layoutWidth / (double)PANGO_SCALE; + int width; + pango_layout_get_pixel_size(layout, &width, 0); + + g_free(utf8); g_object_unref(layout); return width; @@ -258,6 +321,9 @@ int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includ int index, trailing; pango_layout_xy_to_index(layout, x * PANGO_SCALE, 1, &index, &trailing); glong offset = g_utf8_pointer_to_offset(utf8, utf8 + index); + if (includePartialGlyphs) + offset += trailing; + g_free(utf8); g_object_unref(layout); @@ -266,8 +332,40 @@ int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includ FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const { - notImplemented(); - return FloatRect(); + PangoLayout* layout = getDefaultPangoLayout(run); + setPangoAttributes(this, run, layout); + + gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); + pango_layout_set_text(layout, utf8, -1); + + char* start = g_utf8_offset_to_pointer(utf8, from); + char* end = g_utf8_offset_to_pointer(start, to - from); + + if (run.ltr()) { + from = start - utf8; + to = end - utf8; + } else { + from = end - utf8; + to = start - utf8; + } + + PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); + int x_pos; + + x_pos = 0; + if (from < layoutLine->length) + pango_layout_line_index_to_x(layoutLine, from, FALSE, &x_pos); + float beforeWidth = PANGO_PIXELS_FLOOR(x_pos); + + x_pos = 0; + if (run.ltr() || to < layoutLine->length) + pango_layout_line_index_to_x(layoutLine, to, FALSE, &x_pos); + float afterWidth = PANGO_PIXELS(x_pos); + + g_free(utf8); + g_object_unref(layout); + + return FloatRect(point.x() + beforeWidth, point.y(), afterWidth - beforeWidth, h); } } diff --git a/WebCore/platform/graphics/gtk/FontPlatformData.h b/WebCore/platform/graphics/gtk/FontPlatformData.h index 778d525..efa5dd5 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformData.h +++ b/WebCore/platform/graphics/gtk/FontPlatformData.h @@ -5,6 +5,7 @@ * Copyright (C) 2006 Apple Computer, Inc. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2007 Pioneer Research Center USA, Inc. * All rights reserved. * * This library is free software; you can redistribute it and/or @@ -30,21 +31,42 @@ #include "GlyphBuffer.h" #include "FontDescription.h" #include <cairo.h> +#if defined(USE_FREETYPE) #include <cairo-ft.h> #include <fontconfig/fcfreetype.h> +#elif defined(USE_PANGO) +#include <pango/pangocairo.h> +#else +#error "Must defined a font backend" +#endif namespace WebCore { class FontPlatformData { public: - class Deleted {}; - FontPlatformData(Deleted) - : m_pattern(reinterpret_cast<FcPattern*>(-1)) + FontPlatformData(WTF::HashTableDeletedValueType) +#if defined(USE_FREETYPE) + : m_pattern(hashTableDeletedFontValue()) + , m_fallbacks(0) +#elif defined(USE_PANGO) + : m_context(0) + , m_font(hashTableDeletedFontValue()) +#else +#error "Must defined a font backend" +#endif , m_scaledFont(0) { } FontPlatformData() +#if defined(USE_FREETYPE) : m_pattern(0) + , m_fallbacks(0) +#elif defined(USE_PANGO) + : m_context(0) + , m_font(0) +#else +#error "Must defined a font backend" +#endif , m_scaledFont(0) { } @@ -58,21 +80,51 @@ public: static bool init(); bool isFixedPitch(); - float size() const { return m_fontDescription.specifiedSize(); } + float size() const { return m_size; } void setFont(cairo_t*) const; unsigned hash() const { +#if defined(USE_FREETYPE) + if (m_pattern) + return FcPatternHash(m_pattern); +#endif uintptr_t hashCodes[1] = { reinterpret_cast<uintptr_t>(m_scaledFont) }; - return StringImpl::computeHash( reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); + return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); } bool operator==(const FontPlatformData&) const; + bool isHashTableDeletedValue() const { +#if defined(USE_FREETYPE) + return m_pattern == hashTableDeletedFontValue(); +#elif defined(USE_PANGO) + return m_font == hashTableDeletedFontValue(); +#endif + }; +#if defined(USE_FREETYPE) FcPattern* m_pattern; - FontDescription m_fontDescription; + FcFontSet* m_fallbacks; +#elif defined(USE_PANGO) + static PangoFontMap* m_fontMap; + static GHashTable* m_hashTable; + + PangoContext* m_context; + PangoFont* m_font; +#else +#error "Must defined a font backend" +#endif + float m_size; + bool m_syntheticBold; + bool m_syntheticOblique; cairo_scaled_font_t* m_scaledFont; +private: +#if defined(USE_FREETYPE) + static FcPattern *hashTableDeletedFontValue() { return reinterpret_cast<FcPattern*>(-1); } +#elif defined(USE_PANGO) + static PangoFont *hashTableDeletedFontValue() { return reinterpret_cast<PangoFont*>(-1); } +#endif }; } diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp index ef5cb4a..17d789b 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp @@ -37,19 +37,23 @@ namespace WebCore { FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName) : m_pattern(0) - , m_fontDescription(fontDescription) + , m_fallbacks(0) + , m_size(fontDescription.computedPixelSize()) + , m_syntheticBold(false) + , m_syntheticOblique(false) , m_scaledFont(0) { FontPlatformData::init(); - CString familyNameString = familyName.domString().utf8(); + CString familyNameString = familyName.string().utf8(); const char* fcfamily = familyNameString.data(); int fcslant = FC_SLANT_ROMAN; + // FIXME: Map all FontWeight values to fontconfig weights. int fcweight = FC_WEIGHT_NORMAL; - float fcsize = fontDescription.computedSize(); + double fcsize = fontDescription.computedPixelSize(); if (fontDescription.italic()) fcslant = FC_SLANT_ITALIC; - if (fontDescription.bold()) + if (fontDescription.weight() >= FontWeight600) fcweight = FC_WEIGHT_BOLD; int type = fontDescription.genericFamily(); @@ -64,27 +68,30 @@ FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const goto freePattern; switch (type) { - case FontDescription::SerifFamily: - fcfamily = "serif"; - break; - case FontDescription::SansSerifFamily: - fcfamily = "sans-serif"; - break; - case FontDescription::MonospaceFamily: - fcfamily = "monospace"; - break; - case FontDescription::NoFamily: - case FontDescription::StandardFamily: - default: - fcfamily = "sans-serif"; + case FontDescription::SerifFamily: + fcfamily = "serif"; + break; + case FontDescription::SansSerifFamily: + fcfamily = "sans-serif"; + break; + case FontDescription::MonospaceFamily: + fcfamily = "monospace"; + break; + case FontDescription::StandardFamily: + fcfamily = "sans-serif"; + break; + case FontDescription::NoFamily: + default: + fcfamily = NULL; + break; } - if (!FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fcfamily))) - goto freePattern; - if (!FcPatternAddInteger(pattern, FC_SLANT, fcslant)) + if (fcfamily && !FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fcfamily))) goto freePattern; if (!FcPatternAddInteger(pattern, FC_WEIGHT, fcweight)) goto freePattern; + if (!FcPatternAddInteger(pattern, FC_SLANT, fcslant)) + goto freePattern; if (!FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fcsize)) goto freePattern; @@ -98,7 +105,7 @@ FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const goto freePattern; fontFace = cairo_ft_font_face_create_for_pattern(m_pattern); cairo_matrix_t ctm; - cairo_matrix_init_scale(&fontMatrix, m_fontDescription.computedSize(), m_fontDescription.computedSize()); + cairo_matrix_init_scale(&fontMatrix, fontDescription.computedPixelSize(), fontDescription.computedPixelSize()); cairo_matrix_init_identity(&ctm); #if GTK_CHECK_VERSION(2,10,0) @@ -119,36 +126,39 @@ freePattern: FontPlatformData::FontPlatformData(float size, bool bold, bool italic) : m_pattern(0) - , m_fontDescription() + , m_fallbacks(0) + , m_size(size) + , m_syntheticBold(bold) + , m_syntheticOblique(italic) , m_scaledFont(0) { - m_fontDescription.setSpecifiedSize(size); - m_fontDescription.setBold(bold); - m_fontDescription.setItalic(italic); } FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, int size, bool bold, bool italic) : m_pattern(0) - , m_fontDescription() + , m_fallbacks(0) + , m_size(size) + , m_syntheticBold(bold) + , m_syntheticOblique(italic) , m_scaledFont(0) { - m_fontDescription.setSpecifiedSize(size); - m_fontDescription.setBold(bold); - m_fontDescription.setItalic(italic); - cairo_matrix_t fontMatrix; cairo_matrix_init_scale(&fontMatrix, size, size); cairo_matrix_t ctm; cairo_matrix_init_identity(&ctm); - cairo_font_options_t* options = cairo_font_options_create(); + static const cairo_font_options_t* defaultOptions = cairo_font_options_create(); + const cairo_font_options_t* options = NULL; - // We force antialiasing and disable hinting to provide consistent - // typographic qualities for custom fonts on all platforms. - cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); +#if GTK_CHECK_VERSION(2,10,0) + if (GdkScreen* screen = gdk_screen_get_default()) + options = gdk_screen_get_font_options(screen); +#endif + // gdk_screen_get_font_options() returns NULL if no default options are + // set, so we always have to check. + if (!options) + options = defaultOptions; m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options); - cairo_font_options_destroy(options); } bool FontPlatformData::init() diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp new file mode 100644 index 0000000..be3fd43 --- /dev/null +++ b/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2007 Pioneer Research Center USA, 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 "FontPlatformData.h" + +#include "CString.h" +#include "PlatformString.h" +#include "FontDescription.h" +#include <cairo.h> +#include <assert.h> + +#include <pango/pango.h> +#include <pango/pangocairo.h> + +#if !defined(PANGO_VERSION_CHECK) +#define PANGO_VERSION_CHECK(major,minor,micro) 0 +#endif + +// Use cairo-ft i a recent enough Pango version isn't available +#if !PANGO_VERSION_CHECK(1,18,0) +#include <cairo-ft.h> +#include <pango/pangofc-fontmap.h> +#endif +#include <gtk/gtk.h> + +namespace WebCore { + + PangoFontMap* FontPlatformData::m_fontMap = 0; + GHashTable* FontPlatformData::m_hashTable = 0; + +FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName) + : m_context(0) + , m_font(0) + , m_size(fontDescription.computedSize()) + , m_syntheticBold(false) + , m_syntheticOblique(false) + , m_scaledFont(0) +{ + FontPlatformData::init(); + + CString stored_family = familyName.string().utf8(); + char const* families[] = { + stored_family.data(), + NULL + }; + + switch (fontDescription.genericFamily()) { + case FontDescription::SerifFamily: + families[1] = "serif"; + break; + case FontDescription::SansSerifFamily: + families[1] = "sans"; + break; + case FontDescription::MonospaceFamily: + families[1] = "monospace"; + break; + case FontDescription::NoFamily: + case FontDescription::StandardFamily: + default: + families[1] = "sans"; + break; + } + + PangoFontDescription* description = pango_font_description_new(); + pango_font_description_set_absolute_size(description, fontDescription.computedSize() * PANGO_SCALE); + + // FIXME: Map all FontWeight values to Pango font weights. + if (fontDescription.weight() >= FontWeight600) + pango_font_description_set_weight(description, PANGO_WEIGHT_BOLD); + if (fontDescription.italic()) + pango_font_description_set_style(description, PANGO_STYLE_ITALIC); + +#if PANGO_VERSION_CHECK(1,21,5) // deprecated in 1.21 + m_context = pango_font_map_create_context(m_fontMap); +#else + m_context = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(m_fontMap)); +#endif + for (unsigned int i = 0; !m_font && i < G_N_ELEMENTS(families); i++) { + pango_font_description_set_family(description, families[i]); + pango_context_set_font_description(m_context, description); + m_font = pango_font_map_load_font(m_fontMap, m_context, description); + } + +#if PANGO_VERSION_CHECK(1,18,0) + if (m_font) + m_scaledFont = cairo_scaled_font_reference(pango_cairo_font_get_scaled_font(PANGO_CAIRO_FONT(m_font))); +#else + // This compatibility code for older versions of Pango is not well-tested. + if (m_font) { + PangoFcFont* fcfont = PANGO_FC_FONT(m_font); + cairo_font_face_t* face = cairo_ft_font_face_create_for_pattern(fcfont->font_pattern); + double size; + if (FcPatternGetDouble(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch) + size = 12.0; + cairo_matrix_t fontMatrix; + cairo_matrix_init_scale(&fontMatrix, size, size); + cairo_font_options_t* fontOptions; + if (pango_cairo_context_get_font_options(m_context)) + fontOptions = cairo_font_options_copy(pango_cairo_context_get_font_options(m_context)); + else + fontOptions = cairo_font_options_create(); + cairo_matrix_t ctm; + cairo_matrix_init_identity(&ctm); + m_scaledFont = cairo_scaled_font_create(face, &fontMatrix, &ctm, fontOptions); + cairo_font_options_destroy(fontOptions); + cairo_font_face_destroy(face); + } +#endif + pango_font_description_free(description); +} + +FontPlatformData::FontPlatformData(float size, bool bold, bool italic) + : m_context(0) + , m_font(0) + , m_size(size) + , m_syntheticBold(bold) + , m_syntheticOblique(italic) + , m_scaledFont(0) +{ +} + +FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, int size, bool bold, bool italic) + : m_context(0) + , m_font(0) + , m_size(size) + , m_syntheticBold(bold) + , m_syntheticOblique(italic) + , m_scaledFont(0) +{ + cairo_matrix_t fontMatrix; + cairo_matrix_init_scale(&fontMatrix, size, size); + cairo_matrix_t ctm; + cairo_matrix_init_identity(&ctm); + cairo_font_options_t* options = cairo_font_options_create(); + + // We force antialiasing and disable hinting to provide consistent + // typographic qualities for custom fonts on all platforms. + cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); + + m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options); + cairo_font_options_destroy(options); +} + +bool FontPlatformData::init() +{ + static bool initialized = false; + if (initialized) + return true; + initialized = true; + + if (!m_fontMap) + m_fontMap = pango_cairo_font_map_get_default(); + if (!m_hashTable) { + PangoFontFamily** families = 0; + int n_families = 0; + + m_hashTable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref); + + pango_font_map_list_families(m_fontMap, &families, &n_families); + + for (int family = 0; family < n_families; family++) + g_hash_table_insert(m_hashTable, + g_strdup(pango_font_family_get_name(families[family])), + g_object_ref(families[family])); + + g_free(families); + } + + return true; +} + +FontPlatformData::~FontPlatformData() +{ + // Destroy takes place in FontData::platformDestroy(). +} + +bool FontPlatformData::isFixedPitch() +{ + PangoFontDescription* description = pango_font_describe_with_absolute_size(m_font); + PangoFontFamily* family = reinterpret_cast<PangoFontFamily*>(g_hash_table_lookup(m_hashTable, pango_font_description_get_family(description))); + pango_font_description_free(description); + return pango_font_family_is_monospace(family); +} + +void FontPlatformData::setFont(cairo_t* cr) const +{ + ASSERT(m_scaledFont); + + cairo_set_scaled_font(cr, m_scaledFont); +} + +bool FontPlatformData::operator==(const FontPlatformData& other) const +{ + if (m_font == other.m_font) + return true; + if (m_font == 0 || m_font == reinterpret_cast<PangoFont*>(-1) + || other.m_font == 0 || other.m_font == reinterpret_cast<PangoFont*>(-1)) + return false; + PangoFontDescription* thisDesc = pango_font_describe(m_font); + PangoFontDescription* otherDesc = pango_font_describe(other.m_font); + bool result = pango_font_description_equal(thisDesc, otherDesc); + pango_font_description_free(otherDesc); + pango_font_description_free(thisDesc); + return result; +} + +} diff --git a/WebCore/platform/graphics/gtk/GlyphPageTreeNodePango.cpp b/WebCore/platform/graphics/gtk/GlyphPageTreeNodePango.cpp new file mode 100644 index 0000000..8fada5c --- /dev/null +++ b/WebCore/platform/graphics/gtk/GlyphPageTreeNodePango.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk> + * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * + * 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 "GlyphPageTreeNode.h" + +#include "SimpleFontData.h" +#include <pango/pango-font.h> + +namespace WebCore { + +static PangoGlyph pango_font_get_glyph(PangoFont* font, PangoContext* context, gunichar wc) +{ + PangoGlyph result = 0; + gchar buffer[7]; + + gint length = g_unichar_to_utf8(wc, buffer); + g_return_val_if_fail(length, 0); + + GList* items = pango_itemize(context, buffer, 0, length, NULL, NULL); + + if (g_list_length(items) == 1) { + PangoItem* item = reinterpret_cast<PangoItem*>(items->data); + PangoFont* tmpFont = item->analysis.font; + item->analysis.font = font; + + PangoGlyphString* glyphs = pango_glyph_string_new(); + pango_shape(buffer, length, &item->analysis, glyphs); + + item->analysis.font = tmpFont; + + if (glyphs->num_glyphs == 1) + result = glyphs->glyphs[0].glyph; + else + g_warning("didn't get 1 glyph but %d", glyphs->num_glyphs); + + pango_glyph_string_free(glyphs); + } + + g_list_foreach(items, (GFunc)pango_item_free, NULL); + g_list_free(items); + + return result; +} + +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + // The bufferLength will be greater than the glyph page size if the buffer has Unicode supplementary characters. + // We won't support this for now. + if (bufferLength > GlyphPage::size) + return false; + + if (!fontData->m_font.m_font || fontData->m_font.m_font == reinterpret_cast<PangoFont*>(-1)) + return false; + + bool haveGlyphs = false; + for (unsigned i = 0; i < length; i++) { + Glyph glyph = pango_font_get_glyph(fontData->m_font.m_font, fontData->m_font.m_context, buffer[i]); + if (!glyph) + setGlyphDataForIndex(offset + i, 0, 0); + else { + setGlyphDataForIndex(offset + i, glyph, fontData); + haveGlyphs = true; + } + } + + return haveGlyphs; +} + +} diff --git a/WebCore/platform/graphics/gtk/IconGtk.cpp b/WebCore/platform/graphics/gtk/IconGtk.cpp index c6e9a14..d8b38a0 100644 --- a/WebCore/platform/graphics/gtk/IconGtk.cpp +++ b/WebCore/platform/graphics/gtk/IconGtk.cpp @@ -43,12 +43,11 @@ namespace WebCore { Icon::Icon() : m_icon(0) { - notImplemented(); } Icon::~Icon() { - if(m_icon) + if (m_icon) g_object_unref(m_icon); } @@ -89,7 +88,7 @@ static String lookupIconName(String MIMEType) return GTK_STOCK_FILE; } -PassRefPtr<Icon> Icon::newIconForFile(const String& filename) +PassRefPtr<Icon> Icon::createIconForFile(const String& filename) { if (!g_path_skip_root(filename.utf8().data())) return 0; @@ -97,13 +96,24 @@ PassRefPtr<Icon> Icon::newIconForFile(const String& filename) String MIMEType = MIMETypeRegistry::getMIMETypeForPath(filename); String iconName = lookupIconName(MIMEType); - Icon* icon = new Icon; + RefPtr<Icon> icon = adoptRef(new Icon); icon->m_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), iconName.utf8().data(), 16, GTK_ICON_LOOKUP_USE_BUILTIN, NULL); - return icon->m_icon ? icon : 0; + if (!icon->m_icon) + return 0; + return icon.release(); +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +{ + //FIXME: Implement this + return 0; } void Icon::paint(GraphicsContext* context, const IntRect& rect) { + if (context->paintingDisabled()) + return; + // TODO: Scale/clip the image if necessary. cairo_t* cr = context->platformContext(); cairo_save(cr); diff --git a/WebCore/platform/graphics/gtk/ImageGtk.cpp b/WebCore/platform/graphics/gtk/ImageGtk.cpp index a74bc54..b745209 100644 --- a/WebCore/platform/graphics/gtk/ImageGtk.cpp +++ b/WebCore/platform/graphics/gtk/ImageGtk.cpp @@ -26,8 +26,6 @@ #include "config.h" #include "BitmapImage.h" -#include "Image.h" -#include "NotImplemented.h" // This function loads resources from WebKit Vector<char> loadResourceIntoArray(const char*); @@ -42,12 +40,13 @@ void BitmapImage::invalidatePlatformData() { } -Image* Image::loadPlatformResource(const char *name) +PassRefPtr<Image> Image::loadPlatformResource(const char *name) { Vector<char> arr = loadResourceIntoArray(name); - BitmapImage* img = new BitmapImage; - RefPtr<SharedBuffer> buffer = new SharedBuffer(arr.data(), arr.size()); + RefPtr<BitmapImage> img = BitmapImage::create(); + RefPtr<SharedBuffer> buffer = SharedBuffer::create(arr.data(), arr.size()); img->setData(buffer, true); - return img; + return img.release(); } + } diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp index c60bc20..1f0cac6 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp @@ -35,6 +35,7 @@ #include "NotImplemented.h" #include "ScrollView.h" #include "Widget.h" +#include <wtf/GOwnPtr.h> #include <gdk/gdkx.h> #include <gst/base/gstbasesrc.h> @@ -42,7 +43,6 @@ #include <gst/interfaces/mixer.h> #include <gst/interfaces/xoverlay.h> #include <gst/video/video.h> -#include <libgnomevfs/gnome-vfs.h> #include <limits> #include <math.h> @@ -54,20 +54,17 @@ gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpoin { if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) { - GError* err; - gchar* debug; + GOwnPtr<GError> err; + GOwnPtr<gchar> debug; - gst_message_parse_error(message, &err, &debug); + gst_message_parse_error(message, &err.outPtr(), &debug.outPtr()); if (err->code == 3) { LOG_VERBOSE(Media, "File not found"); MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); if (mp) mp->loadingFailed(); - } else { + } else LOG_VERBOSE(Media, "Error: %d, %s", err->code, err->message); - g_error_free(err); - g_free(debug); - } } return true; } diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp index a754c45..1ca3e95 100644 --- a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp @@ -62,19 +62,25 @@ void SimpleFontData::platformInit() void SimpleFontData::platformDestroy() { - if (!isCustomFont()) { - if (m_font.m_pattern && ((FcPattern*)-1 != m_font.m_pattern)) { - FcPatternDestroy(m_font.m_pattern); - m_font.m_pattern = 0; - } + delete m_smallCapsFontData; - if (m_font.m_scaledFont) { - cairo_scaled_font_destroy(m_font.m_scaledFont); - m_font.m_scaledFont = 0; - } + if (isCustomFont()) + return; + + if (m_font.m_pattern && ((FcPattern*)-1 != m_font.m_pattern)) { + FcPatternDestroy(m_font.m_pattern); + m_font.m_pattern = 0; } - delete m_smallCapsFontData; + if (m_font.m_fallbacks) { + FcFontSetDestroy(m_font.m_fallbacks); + m_font.m_fallbacks = 0; + } + + if (m_font.m_scaledFont) { + cairo_scaled_font_destroy(m_font.m_scaledFont); + m_font.m_scaledFont = 0; + } } SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp new file mode 100644 index 0000000..8621865 --- /dev/null +++ b/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2007 Pioneer Research Center USA, 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 "SimpleFontData.h" + +#include "FloatRect.h" +#include "Font.h" +#include "FontCache.h" +#include "FontDescription.h" +#include "GlyphBuffer.h" +#include <cairo.h> +#include <wtf/MathExtras.h> + +namespace WebCore { + +void SimpleFontData::platformInit() +{ + cairo_font_extents_t font_extents; + cairo_text_extents_t text_extents; + cairo_scaled_font_extents(m_font.m_scaledFont, &font_extents); + m_ascent = static_cast<int>(font_extents.ascent); + m_descent = static_cast<int>(font_extents.descent); + m_lineSpacing = static_cast<int>(font_extents.height); + cairo_scaled_font_text_extents(m_font.m_scaledFont, "x", &text_extents); + m_xHeight = text_extents.height; + cairo_scaled_font_text_extents(m_font.m_scaledFont, " ", &text_extents); + m_spaceWidth = static_cast<int>(text_extents.x_advance); + m_lineGap = m_lineSpacing - m_ascent - m_descent; +} + +void SimpleFontData::platformDestroy() +{ + if (!isCustomFont()) { + + if (m_font.m_font && m_font.m_font != reinterpret_cast<PangoFont*>(-1)) { + g_object_unref(m_font.m_font); + m_font.m_font = 0; + } + + if (m_font.m_context) { + g_object_unref (m_font.m_context); + m_font.m_context = 0; + } + + if (m_font.m_scaledFont) { + cairo_scaled_font_destroy(m_font.m_scaledFont); + m_font.m_scaledFont = 0; + } + } + + delete m_smallCapsFontData; +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_smallCapsFontData) { + FontDescription desc = FontDescription(fontDescription); + desc.setSpecifiedSize(0.70f*fontDescription.computedSize()); + const FontPlatformData* pdata = new FontPlatformData(desc, desc.family().family()); + m_smallCapsFontData = new SimpleFontData(*pdata); + } + return m_smallCapsFontData; +} + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + 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); + + for (int i = 0; i < length; i++) { + if (PANGO_COVERAGE_NONE == pango_coverage_get(requested, i)) { + result = false; + break; + } + } + + pango_coverage_unref(requested); + pango_coverage_unref(available); + + return result; +} + +void SimpleFontData::determinePitch() +{ + m_treatAsFixedPitch = m_font.isFixedPitch(); +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + ASSERT(m_font.m_scaledFont); + + cairo_glyph_t cglyph = { glyph, 0, 0 }; + cairo_text_extents_t extents; + cairo_scaled_font_glyph_extents(m_font.m_scaledFont, &cglyph, 1, &extents); + + float w = (float)m_spaceWidth; + if (cairo_scaled_font_status(m_font.m_scaledFont) == CAIRO_STATUS_SUCCESS && extents.x_advance != 0) + w = (float)extents.x_advance; + return w; +} + +void SimpleFontData::setFont(cairo_t* cr) const +{ + ASSERT(cr); + m_font.setFont(cr); +} + +} diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp index 7e97688..04df7ac 100644 --- a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp @@ -40,10 +40,10 @@ GST_DEBUG_CATEGORY_STATIC(webkit_video_sink_debug); #define GST_CAT_DEFAULT webkit_video_sink_debug static GstElementDetails webkit_video_sink_details = - GST_ELEMENT_DETAILS("WebKit video sink", - "Sink/Video", - "Sends video data from a GStreamer pipeline to a Cairo surface", - "Alp Toker <alp@atoker.com>"); + GST_ELEMENT_DETAILS((gchar*) "WebKit video sink", + (gchar*) "Sink/Video", + (gchar*) "Sends video data from a GStreamer pipeline to a Cairo surface", + (gchar*) "Alp Toker <alp@atoker.com>"); enum { PROP_0, @@ -181,7 +181,7 @@ webkit_video_sink_set_caps(GstBaseSink* bsink, GstCaps* caps) priv->par_n = priv->par_d = 1; gst_structure_get_int(structure, "red_mask", &red_mask); - priv->rgb_ordering = (red_mask == 0xff000000); + priv->rgb_ordering = (red_mask == static_cast<int>(0xff000000)); return TRUE; } @@ -208,8 +208,6 @@ webkit_video_sink_dispose(GObject* object) static void webkit_video_sink_finalize(GObject* object) { - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object); - G_OBJECT_CLASS(parent_class)->finalize(object); } diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm index 5c89715..96fdc39 100644 --- a/WebCore/platform/graphics/mac/ColorMac.mm +++ b/WebCore/platform/graphics/mac/ColorMac.mm @@ -39,8 +39,6 @@ namespace WebCore { // NSColor calls don't throw, so no need to block Cocoa exceptions in this file static RGBA32 oldAquaFocusRingColor = 0xFF7DADD9; -static bool tintIsKnown; -static void (*tintChangeFunction)(); static RGBA32 systemFocusRingColor; static bool useOldAquaFocusRingColor; @@ -126,29 +124,17 @@ CGColorRef cgColor(const Color& c) return CGColorFromNSColor(nsColor(c)); } -static void observeTint() -{ - ASSERT(!tintIsKnown); - [[NSNotificationCenter defaultCenter] addObserver:[WebCoreControlTintObserver class] - selector:@selector(controlTintDidChange) - name:NSControlTintDidChangeNotification - object:NSApp]; - [WebCoreControlTintObserver controlTintDidChange]; - tintIsKnown = true; -} - -void setFocusRingColorChangeFunction(void (*function)()) -{ - ASSERT(!tintChangeFunction); - tintChangeFunction = function; - if (!tintIsKnown) - observeTint(); -} - Color focusRingColor() { - if (!tintIsKnown) - observeTint(); + static bool tintIsKnown = false; + if (!tintIsKnown) { + [[NSNotificationCenter defaultCenter] addObserver:[WebCoreControlTintObserver class] + selector:@selector(controlTintDidChange) + name:NSControlTintDidChangeNotification + object:NSApp]; + [WebCoreControlTintObserver controlTintDidChange]; + tintIsKnown = true; + } if (usesTestModeFocusRingColor()) return oldAquaFocusRingColor; diff --git a/WebCore/platform/graphics/mac/CoreTextController.cpp b/WebCore/platform/graphics/mac/CoreTextController.cpp new file mode 100644 index 0000000..171a7ec --- /dev/null +++ b/WebCore/platform/graphics/mac/CoreTextController.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2007, 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 "CoreTextController.h" + +#if USE(CORE_TEXT) + +#include "CharacterNames.h" +#include "Font.h" +#include "FontCache.h" +#include "SimpleFontData.h" +#include "TextBreakIterator.h" +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +static inline CGFloat roundCGFloat(CGFloat f) +{ + if (sizeof(CGFloat) == sizeof(float)) + return roundf(static_cast<float>(f)); + return static_cast<CGFloat>(round(f)); +} + +static inline CGFloat ceilCGFloat(CGFloat f) +{ + if (sizeof(CGFloat) == sizeof(float)) + return ceilf(static_cast<float>(f)); + return static_cast<CGFloat>(ceil(f)); +} + +CoreTextController::CoreTextRun::CoreTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) + : m_CTRun(ctRun) + , m_fontData(fontData) + , m_characters(characters) + , m_stringLocation(stringLocation) + , m_stringLength(stringLength) +{ + m_glyphCount = CTRunGetGlyphCount(ctRun); + m_indices = CTRunGetStringIndicesPtr(ctRun); + if (!m_indices) { + m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); + CFDataIncreaseLength(m_indicesData.get(), m_glyphCount * sizeof(CFIndex)); + m_indices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_indicesData.get())); + CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), const_cast<CFIndex*>(m_indices)); + } +} + +// Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on +// glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path. +CoreTextController::CoreTextRun::CoreTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) + : m_fontData(fontData) + , m_characters(characters) + , m_stringLocation(stringLocation) + , m_stringLength(stringLength) +{ + Vector<CFIndex, 16> indices; + unsigned r = 0; + while (r < stringLength) { + indices.append(r); + if (U_IS_SURROGATE(characters[r])) { + ASSERT(r + 1 < stringLength); + ASSERT(U_IS_SURROGATE_LEAD(characters[r])); + ASSERT(U_IS_TRAIL(characters[r + 1])); + r += 2; + } else + r++; + } + m_glyphCount = indices.size(); + if (!ltr) { + for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) + std::swap(indices[r], indices[end]); + } + m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); + CFDataAppendBytes(m_indicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex)); + m_indices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_indicesData.get())); +} + +CoreTextController::CoreTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection) + : m_font(*font) + , m_run(run) + , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) + , m_currentCharacter(0) + , m_end(run.length()) + , m_totalWidth(0) + , m_runWidthSoFar(0) + , m_numGlyphsSoFar(0) + , m_currentRun(0) + , m_glyphInCurrentRun(0) + , m_finalRoundingWidth(0) + , m_lastRoundingGlyph(0) +{ + m_padding = m_run.padding(); + if (!m_padding) + m_padPerSpace = 0; + else { + float numSpaces = 0; + for (int s = 0; s < m_run.length(); s++) + if (Font::treatAsSpace(m_run[s])) + numSpaces++; + + if (numSpaces == 0) + m_padPerSpace = 0; + else + m_padPerSpace = ceilf(m_run.padding() / numSpaces); + } + + collectCoreTextRuns(); + adjustGlyphsAndAdvances(); +} + +int CoreTextController::offsetForPosition(int h, bool includePartialGlyphs) +{ + // FIXME: For positions occurring within a ligature, we should return the closest "ligature caret" or + // approximate it by dividing the width of the ligature by the number of characters it encompasses. + // However, Core Text does not expose a low-level API for directly finding + // out how many characters a ligature encompasses (the "attachment count"). + if (h >= m_totalWidth) + return m_run.ltr() ? m_end : 0; + if (h < 0) + return m_run.ltr() ? 0 : m_end; + + CGFloat x = h; + + size_t runCount = m_coreTextRuns.size(); + size_t offsetIntoAdjustedGlyphs = 0; + + for (size_t r = 0; r < runCount; ++r) { + const CoreTextRun& coreTextRun = m_coreTextRuns[r]; + for (unsigned j = 0; j < coreTextRun.glyphCount(); ++j) { + CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width; + if (x <= adjustedAdvance) { + CFIndex hitIndex = coreTextRun.indexAt(j); + int stringLength = coreTextRun.stringLength(); + TextBreakIterator* characterIterator = characterBreakIterator(coreTextRun.characters(), stringLength); + int clusterStart; + if (isTextBreak(characterIterator, hitIndex)) + clusterStart = hitIndex; + else { + clusterStart = textBreakPreceding(characterIterator, hitIndex); + if (clusterStart == TextBreakDone) + clusterStart = 0; + } + + if (!includePartialGlyphs) + return coreTextRun.stringLocation() + clusterStart; + + int clusterEnd = textBreakFollowing(characterIterator, hitIndex); + if (clusterEnd == TextBreakDone) + clusterEnd = stringLength; + + CGFloat clusterWidth = adjustedAdvance; + // FIXME: The search stops at the boundaries of coreTextRun. In theory, it should go on into neighboring CoreTextRuns + // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no + // reordering and on font fallback should occur within a CTLine. + if (clusterEnd - clusterStart > 1) { + int firstGlyphBeforeCluster = j - 1; + while (firstGlyphBeforeCluster && coreTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) { + CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width; + clusterWidth += width; + x += width; + firstGlyphBeforeCluster--; + } + unsigned firstGlyphAfterCluster = j + 1; + while (firstGlyphAfterCluster < coreTextRun.glyphCount() && coreTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) { + clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width; + firstGlyphAfterCluster++; + } + } + if (x <= clusterWidth / 2) + return coreTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); + else + return coreTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); + } + x -= adjustedAdvance; + } + offsetIntoAdjustedGlyphs += coreTextRun.glyphCount(); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +void CoreTextController::collectCoreTextRuns() +{ + if (!m_end) + return; + + // We break up glyph run generation for the string by FontData and (if needed) the use of small caps. + const UChar* cp = m_run.characters(); + bool hasTrailingSoftHyphen = m_run[m_end - 1] == softHyphen; + + if (m_font.isSmallCaps() || hasTrailingSoftHyphen) + m_smallCapsBuffer.resize(m_end); + + unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0; + const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp; + const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end; + + // FIXME: Using HYPHEN-MINUS rather than HYPHEN because Times has a HYPHEN-MINUS glyph that looks like its + // SOFT-HYPHEN glyph, and has no HYPHEN glyph. + static const UChar hyphen = '-'; + + if (hasTrailingSoftHyphen && m_run.rtl()) { + collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); + indexOfFontTransition--; + curr--; + } + + GlyphData glyphData; + GlyphData nextGlyphData; + + bool isSurrogate = U16_IS_SURROGATE(*curr); + if (isSurrogate) { + if (m_run.ltr()) { + if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) + return; + nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); + } else { + if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) + return; + nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); + } + } else + nextGlyphData = m_font.glyphDataForCharacter(*curr, false); + + UChar newC = 0; + + bool isSmallCaps; + bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr; + + if (nextIsSmallCaps) + m_smallCapsBuffer[curr - cp] = newC; + + while (true) { + curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1); + if (curr == end) + break; + + glyphData = nextGlyphData; + isSmallCaps = nextIsSmallCaps; + int index = curr - cp; + isSurrogate = U16_IS_SURROGATE(*curr); + UChar c = *curr; + bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK); + if (isSurrogate) { + if (m_run.ltr()) { + if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) + return; + nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); + } else { + if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) + return; + nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); + } + } else + nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps); + + if (!isSurrogate && m_font.isSmallCaps()) { + nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c; + if (nextIsSmallCaps) + m_smallCapsBuffer[index] = forceSmallCaps ? c : newC; + } + + if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) { + int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition; + int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition; + collectCoreTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0); + indexOfFontTransition = index; + } + } + + int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition - (hasTrailingSoftHyphen ? 1 : 0); + if (itemLength) { + int itemStart = m_run.rtl() ? 0 : indexOfFontTransition; + collectCoreTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0); + } + + if (hasTrailingSoftHyphen && m_run.ltr()) + collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); +} + +void CoreTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) +{ + // FIXME: For offsets falling inside a ligature, we should advance only as far as the appropriate "ligature caret" + // or divide the width of the ligature by the number of offsets it encompasses and make an advance proportional + // to the offsets into the ligature. However, Core Text does not expose a low-level API for + // directly finding out how many characters a ligature encompasses (the "attachment count"). + if (static_cast<int>(offset) > m_end) + offset = m_end; + + if (offset <= m_currentCharacter) + return; + + m_currentCharacter = offset; + + size_t runCount = m_coreTextRuns.size(); + + bool ltr = m_run.ltr(); + + unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar; + while (m_currentRun < runCount) { + const CoreTextRun& coreTextRun = m_coreTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun]; + size_t glyphCount = coreTextRun.glyphCount(); + unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun; + while (m_glyphInCurrentRun < glyphCount) { + if (coreTextRun.indexAt(g) + coreTextRun.stringLocation() >= m_currentCharacter) + return; + CGSize adjustedAdvance = m_adjustedAdvances[k]; + if (glyphBuffer) + glyphBuffer->add(m_adjustedGlyphs[k], coreTextRun.fontData(), adjustedAdvance); + m_runWidthSoFar += adjustedAdvance.width; + m_numGlyphsSoFar++; + m_glyphInCurrentRun++; + if (ltr) { + g++; + k++; + } else { + g--; + k--; + } + } + m_currentRun++; + m_glyphInCurrentRun = 0; + } + if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size()) + m_runWidthSoFar += m_finalRoundingWidth; +} + +void CoreTextController::collectCoreTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) +{ + if (!fontData) { + // Create a run of missing glyphs from the primary font. + m_coreTextRuns.append(CoreTextRun(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr())); + return; + } + + RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(NULL, cp, length, kCFAllocatorNull)); + + RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes())); + + RetainPtr<CTTypesetterRef> typesetter; + + if (!m_mayUseNaturalWritingDirection || m_run.directionalOverride()) { + 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())); + } else + typesetter.adoptCF(CTTypesetterCreateWithAttributedString(attributedString.get())); + + RetainPtr<CTLineRef> line(AdoptCF, CTTypesetterCreateLine(typesetter.get(), CFRangeMake(0, 0))); + + CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); + + CFIndex runCount = CFArrayGetCount(runArray); + + for (CFIndex r = 0; r < runCount; r++) { + CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); + ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); + m_coreTextRuns.append(CoreTextRun(ctRun, fontData, cp, stringLocation, length)); + } +} + +void CoreTextController::adjustGlyphsAndAdvances() +{ + size_t runCount = m_coreTextRuns.size(); + for (size_t r = 0; r < runCount; ++r) { + const CoreTextRun& coreTextRun = m_coreTextRuns[r]; + unsigned glyphCount = coreTextRun.glyphCount(); + const SimpleFontData* fontData = coreTextRun.fontData(); + + Vector<CGGlyph, 256> glyphsVector; + const CGGlyph* glyphs; + + Vector<CGSize, 256> advancesVector; + const CGSize* advances; + + if (coreTextRun.ctRun()) { + glyphs = CTRunGetGlyphsPtr(coreTextRun.ctRun()); + if (!glyphs) { + glyphsVector.grow(glyphCount); + CTRunGetGlyphs(coreTextRun.ctRun(), CFRangeMake(0, 0), glyphsVector.data()); + glyphs = glyphsVector.data(); + } + + advances = CTRunGetAdvancesPtr(coreTextRun.ctRun()); + if (!advances) { + advancesVector.grow(glyphCount); + CTRunGetAdvances(coreTextRun.ctRun(), CFRangeMake(0, 0), advancesVector.data()); + advances = advancesVector.data(); + } + } else { + // Synthesize a run of missing glyphs. + glyphsVector.fill(0, glyphCount); + glyphs = glyphsVector.data(); + advancesVector.fill(CGSizeMake(fontData->widthForGlyph(0), 0), glyphCount); + advances = advancesVector.data(); + } + + bool lastRun = r + 1 == runCount; + const UChar* cp = coreTextRun.characters(); + CGFloat roundedSpaceWidth = roundCGFloat(fontData->m_spaceWidth); + bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances(); + bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled(); + + + for (unsigned i = 0; i < glyphCount; i++) { + CFIndex characterIndex = coreTextRun.indexAt(i); + UChar ch = *(cp + characterIndex); + bool lastGlyph = lastRun && i + 1 == glyphCount; + UChar nextCh; + if (lastGlyph) + nextCh = ' '; + else if (i + 1 < glyphCount) + nextCh = *(cp + coreTextRun.indexAt(i + 1)); + else + nextCh = *(m_coreTextRuns[r + 1].characters() + m_coreTextRuns[r + 1].indexAt(0)); + + bool treatAsSpace = Font::treatAsSpace(ch); + CGGlyph glyph = treatAsSpace ? fontData->m_spaceGlyph : glyphs[i]; + CGSize advance = treatAsSpace ? CGSizeMake(fontData->m_spaceWidth, advances[i].height) : advances[i]; + + if (ch == '\t' && m_run.allowTabs()) { + float tabWidth = m_font.tabWidth(); + advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth, tabWidth); + } else if (ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) { + advance.width = 0; + glyph = fontData->m_spaceGlyph; + } + + float roundedAdvanceWidth = roundf(advance.width); + if (roundsAdvances) + advance.width = roundedAdvanceWidth; + + advance.width += fontData->m_syntheticBoldOffset; + + // We special case spaces in two ways when applying word rounding. + // First, we round spaces to an adjusted width in all fonts. + // Second, in fixed-pitch fonts we ensure that all glyphs that + // match the width of the space glyph have the same width as the space glyph. + if (roundedAdvanceWidth == roundedSpaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding()) + advance.width = fontData->m_adjustedSpaceWidth; + + if (hasExtraSpacing) { + // If we're a glyph with an advance, go ahead and add in letter-spacing. + // That way we weed out zero width lurkers. This behavior matches the fast text code path. + if (advance.width && m_font.letterSpacing()) + advance.width += m_font.letterSpacing(); + + // Handle justification and word-spacing. + if (glyph == fontData->m_spaceGlyph) { + // Account for padding. WebCore uses space padding to justify text. + // We distribute the specified padding over the available spaces in the run. + if (m_padding) { + // Use leftover padding if not evenly divisible by number of spaces. + if (m_padding < m_padPerSpace) { + advance.width += m_padding; + m_padding = 0; + } else { + advance.width += m_padPerSpace; + m_padding -= m_padPerSpace; + } + } + + // Account for word-spacing. + if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) + advance.width += m_font.wordSpacing(); + } + } + + // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters + // followed by a character defined by isRoundingHackCharacter()) are always an integer width. + // We adjust the width of the last character of a "word" to ensure an integer width. + // Force characters that are used to determine word boundaries for the rounding hack + // to be integer width, so the following words will start on an integer boundary. + if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch)) + advance.width = ceilCGFloat(advance.width); + + // Check to see if the next character is a "rounding hack character", if so, adjust the + // width so that the total run width will be on an integer boundary. + if (m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh) || m_run.applyRunRounding() && lastGlyph) { + CGFloat totalWidth = m_totalWidth + advance.width; + CGFloat extraWidth = ceilCGFloat(totalWidth) - totalWidth; + if (m_run.ltr()) + advance.width += extraWidth; + else { + m_totalWidth += extraWidth; + if (m_lastRoundingGlyph) + m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth; + else + m_finalRoundingWidth = extraWidth; + m_lastRoundingGlyph = m_adjustedAdvances.size() + 1; + } + } + + m_totalWidth += advance.width; + advance.height *= -1; + m_adjustedAdvances.append(advance); + m_adjustedGlyphs.append(glyph); + } + } +} + +} // namespace WebCore + +#endif // USE(CORE_TEXT) diff --git a/WebCore/platform/graphics/mac/CoreTextController.h b/WebCore/platform/graphics/mac/CoreTextController.h new file mode 100644 index 0000000..8dbb7fb --- /dev/null +++ b/WebCore/platform/graphics/mac/CoreTextController.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007, 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 CoreTextController_h +#define CoreTextController_h + +#if USE(CORE_TEXT) + +#include "Font.h" +#include "GlyphBuffer.h" +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class CoreTextController { +public: + CoreTextController(const Font*, const TextRun&, bool mayUseNaturalWritingDirection = false); + + // Advance and emit glyphs up to the specified character. + void advance(unsigned to, GlyphBuffer* = 0); + + // Compute the character offset for a given x coordinate. + int offsetForPosition(int x, bool includePartialGlyphs); + + // Returns the width of everything we've consumed so far. + float runWidthSoFar() const { return m_runWidthSoFar; } + + float totalWidth() const { return m_totalWidth; } + + // Extra width to the left of the leftmost glyph. + float finalRoundingWidth() const { return m_finalRoundingWidth; } + +private: + class CoreTextRun { + public: + CoreTextRun(CTRunRef, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength); + CoreTextRun(const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr); + + CTRunRef ctRun() const { return m_CTRun.get(); } + unsigned glyphCount() const { return m_glyphCount; } + const SimpleFontData* fontData() const { return m_fontData; } + const UChar* characters() const { return m_characters; } + unsigned stringLocation() const { return m_stringLocation; } + size_t stringLength() const { return m_stringLength; } + CFIndex indexAt(size_t i) const { return m_indices[i]; } + + private: + RetainPtr<CTRunRef> m_CTRun; + unsigned m_glyphCount; + const SimpleFontData* m_fontData; + const UChar* m_characters; + unsigned m_stringLocation; + size_t m_stringLength; + const CFIndex* m_indices; + // Used only if CTRunGet*Ptr fails or if this is a missing glyphs run. + RetainPtr<CFMutableDataRef> m_indicesData; + }; + + void collectCoreTextRuns(); + void collectCoreTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); + void adjustGlyphsAndAdvances(); + + const Font& m_font; + const TextRun& m_run; + bool m_mayUseNaturalWritingDirection; + + Vector<UChar, 256> m_smallCapsBuffer; + + Vector<CoreTextRun, 16> m_coreTextRuns; + Vector<CGSize, 256> m_adjustedAdvances; + Vector<CGGlyph, 256> m_adjustedGlyphs; + + unsigned m_currentCharacter; + int m_end; + + CGFloat m_totalWidth; + + float m_runWidthSoFar; + unsigned m_numGlyphsSoFar; + size_t m_currentRun; + unsigned m_glyphInCurrentRun; + float m_finalRoundingWidth; + float m_padding; + float m_padPerSpace; + + unsigned m_lastRoundingGlyph; +}; + +} // namespace WebCore +#endif // USE(CORE_TEXT) +#endif // CoreTextController_h diff --git a/WebCore/platform/graphics/mac/FontCacheMac.mm b/WebCore/platform/graphics/mac/FontCacheMac.mm index ffb3068..e7cda66 100644 --- a/WebCore/platform/graphics/mac/FontCacheMac.mm +++ b/WebCore/platform/graphics/mac/FontCacheMac.mm @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,84 +42,37 @@ typedef int NSInteger; namespace WebCore { -static bool getAppDefaultValue(CFStringRef key, int *v) +static void fontCacheATSNotificationCallback(ATSFontNotificationInfoRef, void*) { - CFPropertyListRef value; - - value = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication, - kCFPreferencesAnyUser, - kCFPreferencesAnyHost); - if (value == 0) { - value = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication, - kCFPreferencesCurrentUser, - kCFPreferencesAnyHost); - if (value == 0) - return false; - } - - if (CFGetTypeID(value) == CFNumberGetTypeID()) { - if (v != 0) - CFNumberGetValue((const CFNumberRef)value, kCFNumberIntType, v); - } else if (CFGetTypeID(value) == CFStringGetTypeID()) { - if (v != 0) - *v = CFStringGetIntValue((const CFStringRef)value); - } else { - CFRelease(value); - return false; - } - - CFRelease(value); - return true; + FontCache::invalidate(); } -static bool getUserDefaultValue(CFStringRef key, int *v) +void FontCache::platformInit() { - CFPropertyListRef value; - - value = CFPreferencesCopyValue(key, kCFPreferencesAnyApplication, - kCFPreferencesCurrentUser, - kCFPreferencesCurrentHost); - if (value == 0) - return false; - - if (CFGetTypeID(value) == CFNumberGetTypeID()) { - if (v != 0) - CFNumberGetValue((const CFNumberRef)value, kCFNumberIntType, v); - } else if (CFGetTypeID(value) == CFStringGetTypeID()) { - if (v != 0) - *v = CFStringGetIntValue((const CFStringRef)value); - } else { - CFRelease(value); - return false; - } - - CFRelease(value); - return true; + wkSetUpFontCache(); + // FIXME: Passing kATSFontNotifyOptionReceiveWhileSuspended may be an overkill and does not seem to work anyway. + ATSFontNotificationSubscribe(fontCacheATSNotificationCallback, kATSFontNotifyOptionReceiveWhileSuspended, 0, 0); } -static int getLCDScaleParameters(void) +static int toAppKitFontWeight(FontWeight fontWeight) { - int mode; - CFStringRef key; - - key = CFSTR("AppleFontSmoothing"); - if (!getAppDefaultValue(key, &mode)) { - if (!getUserDefaultValue(key, &mode)) - return 1; - } - - if (wkFontSmoothingModeIsLCD(mode)) - return 4; - return 1; + static int appKitFontWeights[] = { + 2, // FontWeight100 + 3, // FontWeight200 + 4, // FontWeight300 + 5, // FontWeight400 + 6, // FontWeight500 + 8, // FontWeight600 + 9, // FontWeight700 + 10, // FontWeight800 + 12, // FontWeight900 + }; + return appKitFontWeights[fontWeight]; } -#define MINIMUM_GLYPH_CACHE_SIZE 1536 * 1024 - -void FontCache::platformInit() +static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight) { - size_t s = MINIMUM_GLYPH_CACHE_SIZE*getLCDScaleParameters(); - - wkSetUpFontCache(s); + return appKitFontWeight >= 7; } const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) @@ -126,8 +80,7 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, cons const FontPlatformData& platformData = font.fontDataAt(0)->fontDataForCharacter(characters[0])->platformData(); NSFont *nsFont = platformData.font(); - NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(characters) - length:length freeWhenDone:NO]; + NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(characters) length:length freeWhenDone:NO]; NSFont *substituteFont = wkGetFontInLanguageForRange(nsFont, string, NSMakeRange(0, length)); [string release]; @@ -135,49 +88,45 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, cons substituteFont = wkGetFontInLanguageForCharacter(nsFont, characters[0]); if (!substituteFont) return 0; - + // Use the family name from the AppKit-supplied substitute font, requesting the // traits, weight, and size we want. One way this does better than the original // AppKit request is that it takes synthetic bold and oblique into account. // But it does create the possibility that we could end up with a font that // doesn't actually cover the characters we need. - NSFontManager *manager = [NSFontManager sharedFontManager]; + NSFontManager *fontManager = [NSFontManager sharedFontManager]; NSFontTraitMask traits; NSInteger weight; CGFloat size; if (nsFont) { - traits = [manager traitsOfFont:nsFont]; + traits = [fontManager traitsOfFont:nsFont]; if (platformData.m_syntheticBold) traits |= NSBoldFontMask; if (platformData.m_syntheticOblique) - traits |= NSItalicFontMask; - weight = [manager weightOfFont:nsFont]; + traits |= NSFontItalicTrait; + weight = [fontManager weightOfFont:nsFont]; size = [nsFont pointSize]; } else { // For custom fonts nsFont is nil. - traits = (font.bold() ? NSBoldFontMask : 0) | (font.italic() ? NSItalicFontMask : 0); - weight = 5; + traits = font.italic() ? NSFontItalicTrait : 0; + weight = toAppKitFontWeight(font.weight()); size = font.pixelSize(); } - NSFont *bestVariation = [manager fontWithFamily:[substituteFont familyName] - traits:traits - weight:weight - size:size]; - if (bestVariation) + if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size]) substituteFont = bestVariation; - substituteFont = font.fontDescription().usePrinterFont() - ? [substituteFont printerFont] : [substituteFont screenFont]; + substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont]; - NSFontTraitMask substituteFontTraits = [manager traitsOfFont:substituteFont]; + NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont]; + NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont]; FontPlatformData alternateFont(substituteFont, - !font.isPlatformFont() && (traits & NSBoldFontMask) && !(substituteFontTraits & NSBoldFontMask), - !font.isPlatformFont() && (traits & NSItalicFontMask) && !(substituteFontTraits & NSItalicFontMask)); + !font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight), + !font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait)); return getCachedFontData(&alternateFont); } @@ -220,42 +169,33 @@ FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fo return platformFont; } -bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family) +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) { - NSFontTraitMask traits = 0; - if (fontDescription.italic()) - traits |= NSItalicFontMask; - if (fontDescription.bold()) - traits |= NSBoldFontMask; - float size = fontDescription.computedPixelSize(); - - NSFont* nsFont = [WebFontCache fontWithFamily:family traits:traits size:size]; - return nsFont != 0; + [WebFontCache getTraits:traitsMasks inFamily:familyName]; } FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) { - NSFontTraitMask traits = 0; - if (fontDescription.italic()) - traits |= NSItalicFontMask; - if (fontDescription.bold()) - traits |= NSBoldFontMask; + NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0; + NSInteger weight = toAppKitFontWeight(fontDescription.weight()); float size = fontDescription.computedPixelSize(); - - NSFont* nsFont = [WebFontCache fontWithFamily:family traits:traits size:size]; + + NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size]; if (!nsFont) return 0; + NSFontManager *fontManager = [NSFontManager sharedFontManager]; NSFontTraitMask actualTraits = 0; - if (fontDescription.bold() || fontDescription.italic()) - actualTraits = [[NSFontManager sharedFontManager] traitsOfFont:nsFont]; - + if (fontDescription.italic()) + actualTraits = [fontManager traitsOfFont:nsFont]; + NSInteger actualWeight = [fontManager weightOfFont:nsFont]; + FontPlatformData* result = new FontPlatformData; - + // Use the correct font for print vs. screen. result->setFont(fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont]); - result->m_syntheticBold = (traits & NSBoldFontMask) && !(actualTraits & NSBoldFontMask); - result->m_syntheticOblique = (traits & NSItalicFontMask) && !(actualTraits & NSItalicFontMask); + result->m_syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); + result->m_syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); return result; } diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp index f143458..1fb144c 100644 --- a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp @@ -33,7 +33,7 @@ FontCustomPlatformData::~FontCustomPlatformData() CGFontRelease(m_cgFont); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode) { return FontPlatformData(m_cgFont, (ATSUFontID)m_atsFont, size, bold, italic); } diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.h b/WebCore/platform/graphics/mac/FontCustomPlatformData.h index d2e83ca..1e73ae0 100644 --- a/WebCore/platform/graphics/mac/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.h @@ -21,6 +21,7 @@ #ifndef FontCustomPlatformData_h #define FontCustomPlatformData_h +#include "FontRenderingMode.h" #include <wtf/Noncopyable.h> typedef struct CGFont* CGFontRef; @@ -38,7 +39,7 @@ struct FontCustomPlatformData : Noncopyable { {} ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); ATSFontContainerRef m_atsContainer; ATSFontRef m_atsFont; diff --git a/WebCore/platform/graphics/mac/FontMac.mm b/WebCore/platform/graphics/mac/FontMac.mm index 06d8d9e..bef18d0 100644 --- a/WebCore/platform/graphics/mac/FontMac.mm +++ b/WebCore/platform/graphics/mac/FontMac.mm @@ -1,6 +1,4 @@ -/** - * This file is part of the html renderer for KDE. - * +/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) @@ -20,20 +18,14 @@ * 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. - * */ #import "config.h" #import "Font.h" -#import "BlockExceptions.h" -#import "CharacterNames.h" -#import "FontFallbackList.h" #import "GlyphBuffer.h" #import "GraphicsContext.h" -#import "IntRect.h" #import "Logging.h" -#import "ShapeArabic.h" #import "SimpleFontData.h" #import "WebCoreSystemInterface.h" #import "WebCoreTextRenderer.h" @@ -50,559 +42,6 @@ using namespace std; namespace WebCore { -// ================================================================= -// Font Class (Platform-Specific Portion) -// ================================================================= - -struct ATSULayoutParameters -{ - ATSULayoutParameters(const TextRun& run) - : m_run(run) - , m_font(0) - , m_fonts(0) - , m_charBuffer(0) - , m_hasSyntheticBold(false) - , m_syntheticBoldPass(false) - , m_padPerSpace(0) - {} - - void initialize(const Font*, const GraphicsContext* = 0); - - const TextRun& m_run; - - const Font* m_font; - - ATSUTextLayout m_layout; - const SimpleFontData **m_fonts; - - UChar *m_charBuffer; - bool m_hasSyntheticBold; - bool m_syntheticBoldPass; - float m_padPerSpace; -}; - -// Be sure to free the array allocated by this function. -static TextRun addDirectionalOverride(const TextRun& run, bool rtl) -{ - UChar* charactersWithOverride = new UChar[run.length() + 2]; - charactersWithOverride[0] = rtl ? rightToLeftOverride : leftToRightOverride; - memcpy(&charactersWithOverride[1], run.data(0), sizeof(UChar) * run.length()); - charactersWithOverride[run.length() + 1] = popDirectionalFormatting; - - TextRun result = run; - result.setText(charactersWithOverride, run.length() + 2); - return result; -} - -static void initializeATSUStyle(const SimpleFontData* fontData) -{ - // The two NSFont calls in this method (pointSize and _atsFontID) do not raise exceptions. - - if (!fontData->m_ATSUStyleInitialized) { - OSStatus status; - ByteCount propTableSize; - - status = ATSUCreateStyle(&fontData->m_ATSUStyle); - if (status != noErr) - LOG_ERROR("ATSUCreateStyle failed (%d)", status); - - ATSUFontID fontID = fontData->platformData().m_atsuFontID; - if (fontID == 0) { - ATSUDisposeStyle(fontData->m_ATSUStyle); - LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font()); - return; - } - - CGAffineTransform transform = CGAffineTransformMakeScale(1, -1); - if (fontData->m_font.m_syntheticOblique) - transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); - Fixed fontSize = FloatToFixed(fontData->platformData().m_size); - - // Turn off automatic kerning until it is supported in the CG code path (6136 in bugzilla) - Fract kerningInhibitFactor = FloatToFract(1.0); - ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag }; - ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) }; - ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor }; - status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues); - if (status != noErr) - LOG_ERROR("ATSUSetAttributes failed (%d)", status); - status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize); - if (status == noErr) // naively assume that if a 'prop' table exists then it contains mirroring info - fontData->m_ATSUMirrors = true; - else if (status == kATSInvalidFontTableAccess) - fontData->m_ATSUMirrors = false; - else - LOG_ERROR("ATSFontGetTable failed (%d)", status); - - // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bugzilla 6135 is fixed. - // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are - // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example. - // See bugzilla 5166. - if ([[fontData->m_font.font() coveredCharacterSet] characterIsMember:'a']) { - ATSUFontFeatureType featureTypes[] = { kLigaturesType }; - ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector }; - status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors); - } - - fontData->m_ATSUStyleInitialized = true; - } -} - -static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, URefCon iRefCon, - void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus) -{ - ATSULayoutParameters *params = (ATSULayoutParameters *)iRefCon; - OSStatus status; - ItemCount count; - ATSLayoutRecord *layoutRecords; - - if (params->m_run.applyWordRounding()) { - status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count); - if (status != noErr) { - *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue; - return status; - } - - Fixed lastNativePos = 0; - float lastAdjustedPos = 0; - const UChar* characters = params->m_charBuffer ? params->m_charBuffer : params->m_run.characters(); - const SimpleFontData **renderers = params->m_fonts; - const SimpleFontData *renderer; - const SimpleFontData *lastRenderer = 0; - UChar ch, nextCh; - ByteCount offset = layoutRecords[0].originalOffset; - nextCh = *(UChar *)(((char *)characters)+offset); - bool shouldRound = false; - bool syntheticBoldPass = params->m_syntheticBoldPass; - Fixed syntheticBoldOffset = 0; - ATSGlyphRef spaceGlyph = 0; - bool hasExtraSpacing = params->m_font->letterSpacing() || params->m_font->wordSpacing() | params->m_run.padding(); - float padding = params->m_run.padding(); - // In the CoreGraphics code path, the rounding hack is applied in logical order. - // Here it is applied in visual left-to-right order, which may be better. - ItemCount lastRoundingChar = 0; - ItemCount i; - for (i = 1; i < count; i++) { - bool isLastChar = i == count - 1; - renderer = renderers[offset / 2]; - if (renderer != lastRenderer) { - lastRenderer = renderer; - spaceGlyph = renderer->m_spaceGlyph; - // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems - // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI - // does in any of its device-metrics modes. - shouldRound = [renderer->m_font.font() renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode; - if (syntheticBoldPass) - syntheticBoldOffset = FloatToFixed(renderer->m_syntheticBoldOffset); - } - float width; - if (nextCh == zeroWidthSpace || Font::treatAsZeroWidthSpace(nextCh) && !Font::treatAsSpace(nextCh)) { - width = 0; - layoutRecords[i-1].glyphID = spaceGlyph; - } else { - width = FixedToFloat(layoutRecords[i].realPos - lastNativePos); - if (shouldRound) - width = roundf(width); - width += renderer->m_syntheticBoldOffset; - if (renderer->m_treatAsFixedPitch ? width == renderer->m_spaceWidth : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace)) - width = renderer->m_adjustedSpaceWidth; - } - lastNativePos = layoutRecords[i].realPos; - - if (hasExtraSpacing) { - if (width && params->m_font->letterSpacing()) - width +=params->m_font->letterSpacing(); - if (Font::treatAsSpace(nextCh)) { - if (params->m_run.padding()) { - if (padding < params->m_padPerSpace) { - width += padding; - padding = 0; - } else { - width += params->m_padPerSpace; - padding -= params->m_padPerSpace; - } - } - if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing()) - width += params->m_font->wordSpacing(); - } - } - - ch = nextCh; - offset = layoutRecords[i].originalOffset; - // Use space for nextCh at the end of the loop so that we get inside the rounding hack code. - // We won't actually round unless the other conditions are satisfied. - nextCh = isLastChar ? ' ' : *(UChar *)(((char *)characters)+offset); - - if (Font::isRoundingHackCharacter(ch)) - width = ceilf(width); - lastAdjustedPos = lastAdjustedPos + width; - if (Font::isRoundingHackCharacter(nextCh) && (!isLastChar || params->m_run.applyRunRounding())){ - if (params->m_run.ltr()) - lastAdjustedPos = ceilf(lastAdjustedPos); - else { - float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos; - Fixed rw = FloatToFixed(roundingWidth); - ItemCount j; - for (j = lastRoundingChar; j < i; j++) - layoutRecords[j].realPos += rw; - lastRoundingChar = i; - lastAdjustedPos += roundingWidth; - } - } - if (syntheticBoldPass) { - if (syntheticBoldOffset) - layoutRecords[i-1].realPos += syntheticBoldOffset; - else - layoutRecords[i-1].glyphID = spaceGlyph; - } - layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos); - } - - status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords); - } - *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled; - return noErr; -} - -static inline bool isArabicLamWithAlefLigature(UChar c) -{ - return c >= 0xfef5 && c <= 0xfefc; -} - -static void shapeArabic(const UChar* source, UChar* dest, unsigned totalLength, unsigned shapingStart) -{ - while (shapingStart < totalLength) { - unsigned shapingEnd; - // We do not want to pass a Lam with Alef ligature followed by a space to the shaper, - // since we want to be able to identify this sequence as the result of shaping a Lam - // followed by an Alef and padding with a space. - bool foundLigatureSpace = false; - for (shapingEnd = shapingStart; !foundLigatureSpace && shapingEnd < totalLength - 1; ++shapingEnd) - foundLigatureSpace = isArabicLamWithAlefLigature(source[shapingEnd]) && source[shapingEnd + 1] == ' '; - shapingEnd++; - - UErrorCode shapingError = U_ZERO_ERROR; - unsigned charsWritten = shapeArabic(source + shapingStart, shapingEnd - shapingStart, dest + shapingStart, shapingEnd - shapingStart, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR, &shapingError); - - if (U_SUCCESS(shapingError) && charsWritten == shapingEnd - shapingStart) { - for (unsigned j = shapingStart; j < shapingEnd - 1; ++j) { - if (isArabicLamWithAlefLigature(dest[j]) && dest[j + 1] == ' ') - dest[++j] = zeroWidthSpace; - } - if (foundLigatureSpace) { - dest[shapingEnd] = ' '; - shapingEnd++; - } else if (isArabicLamWithAlefLigature(dest[shapingEnd - 1])) { - // u_shapeArabic quirk: if the last two characters in the source string are a Lam and an Alef, - // the space is put at the beginning of the string, despite U_SHAPE_LENGTH_FIXED_SPACES_NEAR. - ASSERT(dest[shapingStart] == ' '); - dest[shapingStart] = zeroWidthSpace; - } - } else { - // Something went wrong. Abandon shaping and just copy the rest of the buffer. - LOG_ERROR("u_shapeArabic failed(%d)", shapingError); - shapingEnd = totalLength; - memcpy(dest + shapingStart, source + shapingStart, (shapingEnd - shapingStart) * sizeof(UChar)); - } - shapingStart = shapingEnd; - } -} - -void ATSULayoutParameters::initialize(const Font* font, const GraphicsContext* graphicsContext) -{ - m_font = font; - - const SimpleFontData* fontData = font->primaryFont(); - m_fonts = new const SimpleFontData*[m_run.length()]; - m_charBuffer = font->isSmallCaps() ? new UChar[m_run.length()] : 0; - - ATSUTextLayout layout; - OSStatus status; - ATSULayoutOperationOverrideSpecifier overrideSpecifier; - - initializeATSUStyle(fontData); - - // FIXME: This is currently missing the following required features that the CoreGraphics code path has: - // - \n, \t, and nonbreaking space render as a space. - - UniCharCount runLength = m_run.length(); - - if (m_charBuffer) - memcpy(m_charBuffer, m_run.characters(), runLength * sizeof(UChar)); - - status = ATSUCreateTextLayoutWithTextPtr( - (m_charBuffer ? m_charBuffer : m_run.characters()), - 0, // offset - runLength, // length - runLength, // total length - 1, // styleRunCount - &runLength, // length of style run - &fontData->m_ATSUStyle, - &layout); - if (status != noErr) - LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status); - m_layout = layout; - ATSUSetTextLayoutRefCon(m_layout, (URefCon)this); - - // FIXME: There are certain times when this method is called, when we don't have access to a GraphicsContext - // measuring text runs with floatWidthForComplexText is one example. - // ATSUI requires that we pass a valid CGContextRef to it when specifying kATSUCGContextTag (crashes when passed 0) - // ATSUI disables sub-pixel rendering if kATSUCGContextTag is not specified! So we're in a bind. - // Sometimes [[NSGraphicsContext currentContext] graphicsPort] may return the wrong (or no!) context. Nothing we can do about it (yet). - CGContextRef cgContext = graphicsContext ? graphicsContext->platformContext() : (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - - ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers; - Boolean rtl = m_run.rtl(); - overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment; - overrideSpecifier.overrideUPP = overrideLayoutOperation; - ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag }; - ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) }; - ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier }; - - status = ATSUSetLayoutControls(layout, (m_run.applyWordRounding() ? 4 : 3), tags, sizes, values); - if (status != noErr) - LOG_ERROR("ATSUSetLayoutControls failed(%d)", status); - - status = ATSUSetTransientFontMatching(layout, YES); - if (status != noErr) - LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status); - - m_hasSyntheticBold = false; - ATSUFontID ATSUSubstituteFont; - UniCharArrayOffset substituteOffset = 0; - UniCharCount substituteLength; - UniCharArrayOffset lastOffset; - const SimpleFontData* substituteFontData = 0; - - while (substituteOffset < runLength) { - // FIXME: Using ATSUMatchFontsToText() here results in several problems: the CSS font family list is not necessarily followed for the 2nd - // and onwards unmatched characters; segmented fonts do not work correctly; behavior does not match the simple text and Uniscribe code - // paths. Change this function to use Font::glyphDataForCharacter() for each character instead. - lastOffset = substituteOffset; - status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength); - if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) { - const FontData* fallbackFontData = m_font->fontDataForCharacters(m_run.characters() + substituteOffset, substituteLength); - substituteFontData = fallbackFontData ? fallbackFontData->fontDataForCharacter(m_run[0]) : 0; - if (substituteFontData) { - initializeATSUStyle(substituteFontData); - if (substituteFontData->m_ATSUStyle) - ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength); - } else - substituteFontData = fontData; - } else { - substituteOffset = runLength; - substituteLength = 0; - } - - bool shapedArabic = false; - bool isSmallCap = false; - UniCharArrayOffset firstSmallCap = 0; - const SimpleFontData *r = fontData; - UniCharArrayOffset i; - for (i = lastOffset; ; i++) { - if (i == substituteOffset || i == substituteOffset + substituteLength) { - if (isSmallCap) { - isSmallCap = false; - initializeATSUStyle(r->smallCapsFontData(m_font->fontDescription())); - ATSUSetRunStyle(layout, r->smallCapsFontData(m_font->fontDescription())->m_ATSUStyle, firstSmallCap, i - firstSmallCap); - } - if (i == substituteOffset && substituteLength > 0) - r = substituteFontData; - else - break; - } - if (!shapedArabic && WTF::Unicode::isArabicChar(m_run[i]) && !r->shapesArabic()) { - shapedArabic = true; - if (!m_charBuffer) { - m_charBuffer = new UChar[runLength]; - memcpy(m_charBuffer, m_run.characters(), i * sizeof(UChar)); - ATSUTextMoved(layout, m_charBuffer); - } - shapeArabic(m_run.characters(), m_charBuffer, runLength, i); - } - if (m_run.rtl() && !r->m_ATSUMirrors) { - UChar mirroredChar = u_charMirror(m_run[i]); - if (mirroredChar != m_run[i]) { - if (!m_charBuffer) { - m_charBuffer = new UChar[runLength]; - memcpy(m_charBuffer, m_run.characters(), runLength * sizeof(UChar)); - ATSUTextMoved(layout, m_charBuffer); - } - m_charBuffer[i] = mirroredChar; - } - } - if (m_font->isSmallCaps()) { - const SimpleFontData* smallCapsData = r->smallCapsFontData(m_font->fontDescription()); - UChar c = m_charBuffer[i]; - UChar newC; - if (U_GET_GC_MASK(c) & U_GC_M_MASK) - m_fonts[i] = isSmallCap ? smallCapsData : r; - else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) { - m_charBuffer[i] = newC; - if (!isSmallCap) { - isSmallCap = true; - firstSmallCap = i; - } - m_fonts[i] = smallCapsData; - } else { - if (isSmallCap) { - isSmallCap = false; - initializeATSUStyle(smallCapsData); - ATSUSetRunStyle(layout, smallCapsData->m_ATSUStyle, firstSmallCap, i - firstSmallCap); - } - m_fonts[i] = r; - } - } else - m_fonts[i] = r; - if (m_fonts[i]->m_syntheticBoldOffset) - m_hasSyntheticBold = true; - } - substituteOffset += substituteLength; - } - if (m_run.padding()) { - float numSpaces = 0; - unsigned k; - for (k = 0; k < runLength; k++) - if (Font::treatAsSpace(m_run[k])) - numSpaces++; - - if (numSpaces == 0) - m_padPerSpace = 0; - else - m_padPerSpace = ceilf(m_run.padding() / numSpaces); - } else - m_padPerSpace = 0; -} - -static void disposeATSULayoutParameters(ATSULayoutParameters *params) -{ - ATSUDisposeTextLayout(params->m_layout); - delete []params->m_charBuffer; - delete []params->m_fonts; -} - -FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const -{ - TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run; - if (run.directionalOverride()) { - from++; - to++; - } - - ATSULayoutParameters params(adjustedRun); - params.initialize(this); - - ATSTrapezoid firstGlyphBounds; - ItemCount actualNumBounds; - - OSStatus status = ATSUGetGlyphBounds(params.m_layout, 0, 0, from, to - from, kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds); - if (status != noErr || actualNumBounds != 1) { - static ATSTrapezoid zeroTrapezoid = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; - firstGlyphBounds = zeroTrapezoid; - } - disposeATSULayoutParameters(¶ms); - - float beforeWidth = MIN(FixedToFloat(firstGlyphBounds.lowerLeft.x), FixedToFloat(firstGlyphBounds.upperLeft.x)); - float afterWidth = MAX(FixedToFloat(firstGlyphBounds.lowerRight.x), FixedToFloat(firstGlyphBounds.upperRight.x)); - - FloatRect rect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); - - if (run.directionalOverride()) - delete []adjustedRun.characters(); - - return rect; -} - -void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const -{ - OSStatus status; - - int drawPortionLength = to - from; - TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run; - if (run.directionalOverride()) - from++; - - ATSULayoutParameters params(adjustedRun); - params.initialize(this, graphicsContext); - - // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0). - CGContextRef context = graphicsContext->platformContext(); - - CGContextTranslateCTM(context, point.x(), point.y()); - status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); - if (status == noErr && params.m_hasSyntheticBold) { - // Force relayout for the bold pass - ATSUClearLayoutCache(params.m_layout, 0); - params.m_syntheticBoldPass = true; - status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); - } - CGContextTranslateCTM(context, -point.x(), -point.y()); - - if (status != noErr) - // Nothing to do but report the error (dev build only). - LOG_ERROR("ATSUDrawText() failed(%d)", status); - - disposeATSULayoutParameters(¶ms); - - if (run.directionalOverride()) - delete []adjustedRun.characters(); -} - -float Font::floatWidthForComplexText(const TextRun& run) const -{ - if (run.length() == 0) - return 0; - - ATSULayoutParameters params(run); - params.initialize(this); - - OSStatus status; - - ATSTrapezoid firstGlyphBounds; - ItemCount actualNumBounds; - status = ATSUGetGlyphBounds(params.m_layout, 0, 0, 0, run.length(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds); - if (status != noErr) - LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status); - if (actualNumBounds != 1) - LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds); - - disposeATSULayoutParameters(¶ms); - - return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) - - MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x)); -} - -int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const -{ - TextRun adjustedRun = run.directionalOverride() ? addDirectionalOverride(run, run.rtl()) : run; - - ATSULayoutParameters params(adjustedRun); - params.initialize(this); - - UniCharArrayOffset primaryOffset = 0; - - // FIXME: No idea how to avoid including partial glyphs. - // Not even sure if that's the behavior this yields now. - Boolean isLeading; - UniCharArrayOffset secondaryOffset = 0; - OSStatus status = ATSUPositionToOffset(params.m_layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset); - unsigned offset; - if (status == noErr) { - offset = (unsigned)primaryOffset; - if (run.directionalOverride() && offset > 0) - offset--; - } else - // Failed to find offset! Return 0 offset. - offset = 0; - - disposeATSULayoutParameters(¶ms); - - if (run.directionalOverride()) - delete []adjustedRun.characters(); - - return offset; -} - void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = context->platformContext(); @@ -628,7 +67,7 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); } - CGContextSetFont(cgContext, platformData.m_cgFont); + CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; if (drawFont) @@ -644,7 +83,28 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons CGContextSetFontSize(cgContext, 1.0f); } else CGContextSetFontSize(cgContext, platformData.m_size); - + + IntSize shadowSize; + int shadowBlur; + Color shadowColor; + context->getShadow(shadowSize, shadowBlur, shadowColor); + + bool hasSimpleShadow = context->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; + if (hasSimpleShadow) { + // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. + context->clearShadow(); + Color fillColor = context->fillColor(); + Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); + context->setFillColor(shadowFillColor); + CGContextSetTextPosition(cgContext, point.x() + shadowSize.width(), point.y() + shadowSize.height()); + CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + if (font->m_syntheticBoldOffset) { + CGContextSetTextPosition(cgContext, point.x() + shadowSize.width() + font->m_syntheticBoldOffset, point.y() + shadowSize.height()); + CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + } + context->setFillColor(fillColor); + } + CGContextSetTextPosition(cgContext, point.x(), point.y()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->m_syntheticBoldOffset) { @@ -652,6 +112,9 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } + if (hasSimpleShadow) + context->setShadow(shadowSize, shadowBlur, shadowColor); + if (originalShouldUseFontSmoothing != newShouldUseFontSmoothing) CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing); } diff --git a/WebCore/platform/graphics/mac/FontMacATSUI.mm b/WebCore/platform/graphics/mac/FontMacATSUI.mm new file mode 100644 index 0000000..9a45c5a --- /dev/null +++ b/WebCore/platform/graphics/mac/FontMacATSUI.mm @@ -0,0 +1,623 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * 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. + */ + +#import "config.h" +#import "Font.h" + +#if USE(ATSUI) + +#import "CharacterNames.h" +#import "GraphicsContext.h" +#import "Logging.h" +#import "ShapeArabic.h" +#import "SimpleFontData.h" +#import <wtf/OwnArrayPtr.h> + +#define SYNTHETIC_OBLIQUE_ANGLE 14 + +#ifdef __LP64__ +#define URefCon void* +#else +#define URefCon UInt32 +#endif + +using namespace std; + +namespace WebCore { + +struct ATSULayoutParameters : Noncopyable +{ + ATSULayoutParameters(const TextRun& run) + : m_run(run) + , m_font(0) + , m_hasSyntheticBold(false) + , m_syntheticBoldPass(false) + , m_padPerSpace(0) + {} + + ~ATSULayoutParameters() + { + ATSUDisposeTextLayout(m_layout); + } + + void initialize(const Font*, const GraphicsContext* = 0); + + const TextRun& m_run; + + const Font* m_font; + + ATSUTextLayout m_layout; + OwnArrayPtr<const SimpleFontData*> m_fonts; + + OwnArrayPtr<UChar> m_charBuffer; + bool m_hasSyntheticBold; + bool m_syntheticBoldPass; + float m_padPerSpace; +}; + +static TextRun copyRunForDirectionalOverrideIfNecessary(const TextRun& run, OwnArrayPtr<UChar>& charactersWithOverride) +{ + if (!run.directionalOverride()) + return run; + + charactersWithOverride.set(new UChar[run.length() + 2]); + charactersWithOverride[0] = run.rtl() ? rightToLeftOverride : leftToRightOverride; + memcpy(&charactersWithOverride[1], run.data(0), sizeof(UChar) * run.length()); + charactersWithOverride[run.length() + 1] = popDirectionalFormatting; + + TextRun result = run; + result.setText(charactersWithOverride.get(), run.length() + 2); + return result; +} + +static bool fontHasMirroringInfo(ATSUFontID fontID) +{ + ByteCount propTableSize; + OSStatus status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize); + if (status == noErr) // naively assume that if a 'prop' table exists then it contains mirroring info + return true; + else if (status != kATSInvalidFontTableAccess) // anything other than a missing table is logged as an error + LOG_ERROR("ATSFontGetTable failed (%d)", status); + + return false; +} + +static void disableLigatures(const SimpleFontData* fontData) +{ + // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are + // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example. + // See bugzilla 5166. + if (fontData->platformData().allowsLigatures()) + return; + + ATSUFontFeatureType featureTypes[] = { kLigaturesType }; + ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector }; + OSStatus status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors); + if (status != noErr) + LOG_ERROR("ATSUSetFontFeatures failed (%d) -- ligatures remain enabled", status); +} + +static void initializeATSUStyle(const SimpleFontData* fontData) +{ + if (fontData->m_ATSUStyleInitialized) + return; + + ATSUFontID fontID = fontData->platformData().m_atsuFontID; + if (!fontID) { + LOG_ERROR("unable to get ATSUFontID for %@", fontData->m_font.font()); + return; + } + + OSStatus status = ATSUCreateStyle(&fontData->m_ATSUStyle); + if (status != noErr) + // Who knows how many ATSU functions will crash when passed a NULL style... + LOG_ERROR("ATSUCreateStyle failed (%d)", status); + + CGAffineTransform transform = CGAffineTransformMakeScale(1, -1); + if (fontData->m_font.m_syntheticOblique) + transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); + Fixed fontSize = FloatToFixed(fontData->platformData().m_size); + ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) }; + // Turn off automatic kerning until it is supported in the CG code path (bug 6136) + Fract kerningInhibitFactor = FloatToFract(1.0); + + ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag }; + ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor }; + status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues); + if (status != noErr) + LOG_ERROR("ATSUSetAttributes failed (%d)", status); + + fontData->m_ATSUMirrors = fontHasMirroringInfo(fontID); + + // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bug 6135 is fixed. + disableLigatures(fontData); + + fontData->m_ATSUStyleInitialized = true; +} + +static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector iCurrentOperation, ATSULineRef iLineRef, URefCon iRefCon, + void *iOperationCallbackParameterPtr, ATSULayoutOperationCallbackStatus *oCallbackStatus) +{ + ATSULayoutParameters* params = reinterpret_cast<ATSULayoutParameters*>(iRefCon); + OSStatus status; + ItemCount count; + ATSLayoutRecord *layoutRecords; + + if (params->m_run.applyWordRounding()) { + status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count); + if (status != noErr) { + *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue; + return status; + } + + Fixed lastNativePos = 0; + float lastAdjustedPos = 0; + const UChar* characters = params->m_charBuffer ? params->m_charBuffer.get() : params->m_run.characters(); + const SimpleFontData** renderers = params->m_fonts.get(); + const SimpleFontData* renderer; + const SimpleFontData* lastRenderer = 0; + ByteCount offset = layoutRecords[0].originalOffset; + UChar nextCh = *(UChar *)(((char *)characters)+offset); + bool shouldRound = false; + bool syntheticBoldPass = params->m_syntheticBoldPass; + Fixed syntheticBoldOffset = 0; + ATSGlyphRef spaceGlyph = 0; + bool hasExtraSpacing = (params->m_font->letterSpacing() || params->m_font->wordSpacing() || params->m_run.padding()) && !params->m_run.spacingDisabled(); + float padding = params->m_run.padding(); + // In the CoreGraphics code path, the rounding hack is applied in logical order. + // Here it is applied in visual left-to-right order, which may be better. + ItemCount lastRoundingChar = 0; + ItemCount i; + for (i = 1; i < count; i++) { + bool isLastChar = i == count - 1; + renderer = renderers[offset / 2]; + if (renderer != lastRenderer) { + lastRenderer = renderer; + spaceGlyph = renderer->m_spaceGlyph; + // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems + // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI + // does in any of its device-metrics modes. + shouldRound = renderer->platformData().roundsGlyphAdvances(); + if (syntheticBoldPass) + syntheticBoldOffset = FloatToFixed(renderer->m_syntheticBoldOffset); + } + float width; + if (nextCh == zeroWidthSpace || Font::treatAsZeroWidthSpace(nextCh) && !Font::treatAsSpace(nextCh)) { + width = 0; + layoutRecords[i-1].glyphID = spaceGlyph; + } else { + width = FixedToFloat(layoutRecords[i].realPos - lastNativePos); + if (shouldRound) + width = roundf(width); + width += renderer->m_syntheticBoldOffset; + if (renderer->m_treatAsFixedPitch ? width == renderer->m_spaceWidth : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace)) + width = renderer->m_adjustedSpaceWidth; + } + lastNativePos = layoutRecords[i].realPos; + + if (hasExtraSpacing) { + if (width && params->m_font->letterSpacing()) + width +=params->m_font->letterSpacing(); + if (Font::treatAsSpace(nextCh)) { + if (params->m_run.padding()) { + if (padding < params->m_padPerSpace) { + width += padding; + padding = 0; + } else { + width += params->m_padPerSpace; + padding -= params->m_padPerSpace; + } + } + if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing()) + width += params->m_font->wordSpacing(); + } + } + + UChar ch = nextCh; + offset = layoutRecords[i].originalOffset; + // Use space for nextCh at the end of the loop so that we get inside the rounding hack code. + // We won't actually round unless the other conditions are satisfied. + nextCh = isLastChar ? ' ' : *(UChar *)(((char *)characters)+offset); + + if (Font::isRoundingHackCharacter(ch)) + width = ceilf(width); + lastAdjustedPos = lastAdjustedPos + width; + if (Font::isRoundingHackCharacter(nextCh) && (!isLastChar || params->m_run.applyRunRounding())){ + if (params->m_run.ltr()) + lastAdjustedPos = ceilf(lastAdjustedPos); + else { + float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos; + Fixed rw = FloatToFixed(roundingWidth); + ItemCount j; + for (j = lastRoundingChar; j < i; j++) + layoutRecords[j].realPos += rw; + lastRoundingChar = i; + lastAdjustedPos += roundingWidth; + } + } + if (syntheticBoldPass) { + if (syntheticBoldOffset) + layoutRecords[i-1].realPos += syntheticBoldOffset; + else + layoutRecords[i-1].glyphID = spaceGlyph; + } + layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos); + } + + status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords); + } + *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled; + return noErr; +} + +static inline bool isArabicLamWithAlefLigature(UChar c) +{ + return c >= 0xfef5 && c <= 0xfefc; +} + +static void shapeArabic(const UChar* source, UChar* dest, unsigned totalLength, unsigned shapingStart) +{ + while (shapingStart < totalLength) { + unsigned shapingEnd; + // We do not want to pass a Lam with Alef ligature followed by a space to the shaper, + // since we want to be able to identify this sequence as the result of shaping a Lam + // followed by an Alef and padding with a space. + bool foundLigatureSpace = false; + for (shapingEnd = shapingStart; !foundLigatureSpace && shapingEnd < totalLength - 1; ++shapingEnd) + foundLigatureSpace = isArabicLamWithAlefLigature(source[shapingEnd]) && source[shapingEnd + 1] == ' '; + shapingEnd++; + + UErrorCode shapingError = U_ZERO_ERROR; + unsigned charsWritten = shapeArabic(source + shapingStart, shapingEnd - shapingStart, dest + shapingStart, shapingEnd - shapingStart, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR, &shapingError); + + if (U_SUCCESS(shapingError) && charsWritten == shapingEnd - shapingStart) { + for (unsigned j = shapingStart; j < shapingEnd - 1; ++j) { + if (isArabicLamWithAlefLigature(dest[j]) && dest[j + 1] == ' ') + dest[++j] = zeroWidthSpace; + } + if (foundLigatureSpace) { + dest[shapingEnd] = ' '; + shapingEnd++; + } else if (isArabicLamWithAlefLigature(dest[shapingEnd - 1])) { + // u_shapeArabic quirk: if the last two characters in the source string are a Lam and an Alef, + // the space is put at the beginning of the string, despite U_SHAPE_LENGTH_FIXED_SPACES_NEAR. + ASSERT(dest[shapingStart] == ' '); + dest[shapingStart] = zeroWidthSpace; + } + } else { + // Something went wrong. Abandon shaping and just copy the rest of the buffer. + LOG_ERROR("u_shapeArabic failed(%d)", shapingError); + shapingEnd = totalLength; + memcpy(dest + shapingStart, source + shapingStart, (shapingEnd - shapingStart) * sizeof(UChar)); + } + shapingStart = shapingEnd; + } +} + +void ATSULayoutParameters::initialize(const Font* font, const GraphicsContext* graphicsContext) +{ + m_font = font; + + const SimpleFontData* fontData = font->primaryFont(); + m_fonts.set(new const SimpleFontData*[m_run.length()]); + if (font->isSmallCaps()) + m_charBuffer.set(new UChar[m_run.length()]); + + ATSUTextLayout layout; + OSStatus status; + ATSULayoutOperationOverrideSpecifier overrideSpecifier; + + initializeATSUStyle(fontData); + + // FIXME: This is currently missing the following required features that the CoreGraphics code path has: + // - \n, \t, and nonbreaking space render as a space. + + UniCharCount runLength = m_run.length(); + + if (m_charBuffer) + memcpy(m_charBuffer.get(), m_run.characters(), runLength * sizeof(UChar)); + + status = ATSUCreateTextLayoutWithTextPtr( + (m_charBuffer ? m_charBuffer.get() : m_run.characters()), + 0, // offset + runLength, // length + runLength, // total length + 1, // styleRunCount + &runLength, // length of style run + &fontData->m_ATSUStyle, + &layout); + if (status != noErr) + LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status); + m_layout = layout; + ATSUSetTextLayoutRefCon(m_layout, (URefCon)this); + + // FIXME: There are certain times when this method is called, when we don't have access to a GraphicsContext + // measuring text runs with floatWidthForComplexText is one example. + // ATSUI requires that we pass a valid CGContextRef to it when specifying kATSUCGContextTag (crashes when passed 0) + // ATSUI disables sub-pixel rendering if kATSUCGContextTag is not specified! So we're in a bind. + // Sometimes [[NSGraphicsContext currentContext] graphicsPort] may return the wrong (or no!) context. Nothing we can do about it (yet). + CGContextRef cgContext = graphicsContext ? graphicsContext->platformContext() : (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + + ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers; + Boolean rtl = m_run.rtl(); + overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment; + overrideSpecifier.overrideUPP = overrideLayoutOperation; + ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag }; + ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) }; + ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier }; + + status = ATSUSetLayoutControls(layout, (m_run.applyWordRounding() ? 4 : 3), tags, sizes, values); + if (status != noErr) + LOG_ERROR("ATSUSetLayoutControls failed(%d)", status); + + status = ATSUSetTransientFontMatching(layout, YES); + if (status != noErr) + LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status); + + m_hasSyntheticBold = false; + ATSUFontID ATSUSubstituteFont; + UniCharArrayOffset substituteOffset = 0; + UniCharCount substituteLength; + UniCharArrayOffset lastOffset; + const SimpleFontData* substituteFontData = 0; + + while (substituteOffset < runLength) { + // FIXME: Using ATSUMatchFontsToText() here results in several problems: the CSS font family list is not necessarily followed for the 2nd + // and onwards unmatched characters; segmented fonts do not work correctly; behavior does not match the simple text and Uniscribe code + // paths. Change this function to use Font::glyphDataForCharacter() for each character instead. + lastOffset = substituteOffset; + status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength); + if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) { + const FontData* fallbackFontData = m_font->fontDataForCharacters(m_run.characters() + substituteOffset, substituteLength); + substituteFontData = fallbackFontData ? fallbackFontData->fontDataForCharacter(m_run[0]) : 0; + if (substituteFontData) { + initializeATSUStyle(substituteFontData); + if (substituteFontData->m_ATSUStyle) + ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength); + } else + substituteFontData = fontData; + } else { + substituteOffset = runLength; + substituteLength = 0; + } + + bool shapedArabic = false; + bool isSmallCap = false; + UniCharArrayOffset firstSmallCap = 0; + const SimpleFontData *r = fontData; + UniCharArrayOffset i; + for (i = lastOffset; ; i++) { + if (i == substituteOffset || i == substituteOffset + substituteLength) { + if (isSmallCap) { + isSmallCap = false; + initializeATSUStyle(r->smallCapsFontData(m_font->fontDescription())); + ATSUSetRunStyle(layout, r->smallCapsFontData(m_font->fontDescription())->m_ATSUStyle, firstSmallCap, i - firstSmallCap); + } + if (i == substituteOffset && substituteLength > 0) + r = substituteFontData; + else + break; + } + if (!shapedArabic && WTF::Unicode::isArabicChar(m_run[i]) && !r->shapesArabic()) { + shapedArabic = true; + if (!m_charBuffer) { + m_charBuffer.set(new UChar[runLength]); + memcpy(m_charBuffer.get(), m_run.characters(), i * sizeof(UChar)); + ATSUTextMoved(layout, m_charBuffer.get()); + } + shapeArabic(m_run.characters(), m_charBuffer.get(), runLength, i); + } + if (m_run.rtl() && !r->m_ATSUMirrors) { + UChar mirroredChar = u_charMirror(m_run[i]); + if (mirroredChar != m_run[i]) { + if (!m_charBuffer) { + m_charBuffer.set(new UChar[runLength]); + memcpy(m_charBuffer.get(), m_run.characters(), runLength * sizeof(UChar)); + ATSUTextMoved(layout, m_charBuffer.get()); + } + m_charBuffer[i] = mirroredChar; + } + } + if (m_font->isSmallCaps()) { + const SimpleFontData* smallCapsData = r->smallCapsFontData(m_font->fontDescription()); + UChar c = m_charBuffer[i]; + UChar newC; + if (U_GET_GC_MASK(c) & U_GC_M_MASK) + m_fonts[i] = isSmallCap ? smallCapsData : r; + else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) { + m_charBuffer[i] = newC; + if (!isSmallCap) { + isSmallCap = true; + firstSmallCap = i; + } + m_fonts[i] = smallCapsData; + } else { + if (isSmallCap) { + isSmallCap = false; + initializeATSUStyle(smallCapsData); + ATSUSetRunStyle(layout, smallCapsData->m_ATSUStyle, firstSmallCap, i - firstSmallCap); + } + m_fonts[i] = r; + } + } else + m_fonts[i] = r; + if (m_fonts[i]->m_syntheticBoldOffset) + m_hasSyntheticBold = true; + } + substituteOffset += substituteLength; + } + if (m_run.padding()) { + float numSpaces = 0; + unsigned k; + for (k = 0; k < runLength; k++) + if (Font::treatAsSpace(m_run[k])) + numSpaces++; + + if (numSpaces == 0) + m_padPerSpace = 0; + else + m_padPerSpace = ceilf(m_run.padding() / numSpaces); + } else + m_padPerSpace = 0; +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const +{ + OwnArrayPtr<UChar> charactersWithOverride; + TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); + if (run.directionalOverride()) { + from++; + to++; + } + + ATSULayoutParameters params(adjustedRun); + params.initialize(this); + + ATSTrapezoid firstGlyphBounds; + ItemCount actualNumBounds; + + OSStatus status = ATSUGetGlyphBounds(params.m_layout, 0, 0, from, to - from, kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds); + if (status != noErr || actualNumBounds != 1) { + static ATSTrapezoid zeroTrapezoid = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; + firstGlyphBounds = zeroTrapezoid; + } + + float beforeWidth = MIN(FixedToFloat(firstGlyphBounds.lowerLeft.x), FixedToFloat(firstGlyphBounds.upperLeft.x)); + float afterWidth = MAX(FixedToFloat(firstGlyphBounds.lowerRight.x), FixedToFloat(firstGlyphBounds.upperRight.x)); + + FloatRect rect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); + + return rect; +} + +void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const +{ + OSStatus status; + + int drawPortionLength = to - from; + OwnArrayPtr<UChar> charactersWithOverride; + TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); + if (run.directionalOverride()) + from++; + + ATSULayoutParameters params(adjustedRun); + params.initialize(this, graphicsContext); + + // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0). + CGContextRef context = graphicsContext->platformContext(); + CGContextTranslateCTM(context, point.x(), point.y()); + + IntSize shadowSize; + int shadowBlur; + Color shadowColor; + graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor); + + bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; + if (hasSimpleShadow) { + // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. + graphicsContext->clearShadow(); + Color fillColor = graphicsContext->fillColor(); + Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); + graphicsContext->setFillColor(shadowFillColor); + CGContextTranslateCTM(context, shadowSize.width(), shadowSize.height()); + status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); + if (status == noErr && params.m_hasSyntheticBold) { + // Force relayout for the bold pass + ATSUClearLayoutCache(params.m_layout, 0); + params.m_syntheticBoldPass = true; + status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); + // Force relayout for the next pass + ATSUClearLayoutCache(params.m_layout, 0); + params.m_syntheticBoldPass = false; + } + CGContextTranslateCTM(context, -shadowSize.width(), -shadowSize.height()); + graphicsContext->setFillColor(fillColor); + } + + status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); + if (status == noErr && params.m_hasSyntheticBold) { + // Force relayout for the bold pass + ATSUClearLayoutCache(params.m_layout, 0); + params.m_syntheticBoldPass = true; + status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); + } + CGContextTranslateCTM(context, -point.x(), -point.y()); + + if (status != noErr) + // Nothing to do but report the error (dev build only). + LOG_ERROR("ATSUDrawText() failed(%d)", status); + + if (hasSimpleShadow) + graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor); +} + +float Font::floatWidthForComplexText(const TextRun& run) const +{ + if (run.length() == 0) + return 0; + + ATSULayoutParameters params(run); + params.initialize(this); + + OSStatus status; + + ATSTrapezoid firstGlyphBounds; + ItemCount actualNumBounds; + status = ATSUGetGlyphBounds(params.m_layout, 0, 0, 0, run.length(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds); + if (status != noErr) + LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status); + if (actualNumBounds != 1) + LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds); + + return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) - + MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x)); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const +{ + OwnArrayPtr<UChar> charactersWithOverride; + TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); + + ATSULayoutParameters params(adjustedRun); + params.initialize(this); + + UniCharArrayOffset primaryOffset = 0; + + // FIXME: No idea how to avoid including partial glyphs. + // Not even sure if that's the behavior this yields now. + Boolean isLeading; + UniCharArrayOffset secondaryOffset = 0; + OSStatus status = ATSUPositionToOffset(params.m_layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset); + unsigned offset; + if (status == noErr) { + offset = (unsigned)primaryOffset; + if (run.directionalOverride() && offset > 0) + offset--; + } else + // Failed to find offset! Return 0 offset. + offset = 0; + + return offset; +} + +} +#endif // USE(ATSUI) diff --git a/WebCore/platform/graphics/mac/FontMacCoreText.cpp b/WebCore/platform/graphics/mac/FontMacCoreText.cpp new file mode 100644 index 0000000..5fb9d5d --- /dev/null +++ b/WebCore/platform/graphics/mac/FontMacCoreText.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2006, 2007, 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 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 "Font.h" + +#if USE(CORE_TEXT) + +#include "CoreTextController.h" +#include "FontFallbackList.h" +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "IntRect.h" +#include "SimpleFontData.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, + int from, int to) const +{ + CoreTextController controller(this, run); + controller.advance(from); + float beforeWidth = controller.runWidthSoFar(); + controller.advance(to); + float afterWidth = controller.runWidthSoFar(); + + // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning + if (run.rtl()) { + float totalWidth = controller.totalWidth(); + return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); + } + + return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); +} + +void Font::drawComplexText(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(); + CoreTextController controller(this, run); + controller.advance(from); + float beforeWidth = controller.runWidthSoFar(); + controller.advance(to, &glyphBuffer); + + // We couldn't generate any glyphs for the run. Give up. + if (glyphBuffer.isEmpty()) + return; + + float afterWidth = controller.runWidthSoFar(); + + if (run.rtl()) { + startX += controller.totalWidth() + controller.finalRoundingWidth() - afterWidth; + for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) + glyphBuffer.swap(i, end); + } else + startX += beforeWidth; + + // Draw the glyph buffer now at the starting point returned in startX. + FloatPoint startPoint(startX, point.y()); + drawGlyphBuffer(context, glyphBuffer, run, startPoint); +} + +float Font::floatWidthForComplexText(const TextRun& run) const +{ + CoreTextController controller(this, run, true); + return controller.totalWidth(); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const +{ + CoreTextController controller(this, run); + return controller.offsetForPosition(x, includePartialGlyphs); +} + +} +#endif // USE(CORE_TEXT) diff --git a/WebCore/platform/graphics/mac/FontPlatformData.h b/WebCore/platform/graphics/mac/FontPlatformData.h index 8f118e0..40a2dbd 100644 --- a/WebCore/platform/graphics/mac/FontPlatformData.h +++ b/WebCore/platform/graphics/mac/FontPlatformData.h @@ -1,8 +1,8 @@ /* - * 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. + * This file is part of the internal font implementation. + * It should not be included by source files outside it. * - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006, 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 @@ -34,46 +34,52 @@ class NSFont; typedef struct CGFont* CGFontRef; typedef UInt32 ATSUFontID; +#ifndef BUILDING_ON_TIGER +typedef const struct __CTFont* CTFontRef; +#endif #include <CoreFoundation/CFBase.h> #include <objc/objc-auto.h> +#include <wtf/RetainPtr.h> namespace WebCore { -struct FontPlatformData { - class Deleted {}; - - FontPlatformData(Deleted) - : m_syntheticBold(false), m_syntheticOblique(false), m_cgFont(0), m_atsuFontID(0), m_size(0), m_font((NSFont*)-1) - {} +#ifndef BUILDING_ON_TIGER +inline CTFontRef toCTFontRef(NSFont *nsFont) { return reinterpret_cast<CTFontRef>(nsFont); } +#endif - FontPlatformData(float s, bool b, bool o) - : m_syntheticBold(b) - , m_syntheticOblique(o) - , m_cgFont(0) +struct FontPlatformData { + FontPlatformData(float size, bool syntheticBold, bool syntheticOblique) + : m_syntheticBold(syntheticBold) + , m_syntheticOblique(syntheticOblique) , m_atsuFontID(0) - , m_size(s) + , m_size(size) , m_font(0) +#ifdef BUILDING_ON_TIGER + , m_cgFont(0) +#endif { } - FontPlatformData(NSFont* f = 0, bool b = false, bool o = false); + FontPlatformData(NSFont * = 0, bool syntheticBold = false, bool syntheticOblique = false); FontPlatformData(CGFontRef f, ATSUFontID fontID, float s, bool b , bool o) - : m_syntheticBold(b), m_syntheticOblique(o), m_cgFont(f), m_atsuFontID(fontID), m_size(s), m_font(0) + : m_syntheticBold(b), m_syntheticOblique(o), m_atsuFontID(fontID), m_size(s), m_font(0), m_cgFont(f) { } - FontPlatformData(const FontPlatformData& f); + FontPlatformData(const FontPlatformData&); ~FontPlatformData(); + FontPlatformData(WTF::HashTableDeletedValueType) : m_font(hashTableDeletedFontValue()) { } + bool isHashTableDeletedValue() const { return m_font == hashTableDeletedFontValue(); } + float size() const { return m_size; } bool m_syntheticBold; bool m_syntheticOblique; - - CGFontRef m_cgFont; // It is not necessary to refcount this, since either an NSFont owns it or some CachedFont has it referenced. + ATSUFontID m_atsuFontID; float m_size; @@ -84,6 +90,8 @@ struct FontPlatformData { return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); } + const FontPlatformData& operator=(const FontPlatformData& f); + bool operator==(const FontPlatformData& other) const { return m_font == other.m_font && m_syntheticBold == other.m_syntheticBold && m_syntheticOblique == other.m_syntheticOblique && @@ -91,10 +99,26 @@ struct FontPlatformData { } NSFont *font() const { return m_font; } - void setFont(NSFont* font); + void setFont(NSFont *font); + + bool roundsGlyphAdvances() const; + bool allowsLigatures() const; + +#ifndef BUILDING_ON_TIGER + CGFontRef cgFont() const { return m_cgFont.get(); } +#else + CGFontRef cgFont() const { return m_cgFont; } +#endif private: + static NSFont *hashTableDeletedFontValue() { return reinterpret_cast<NSFont *>(-1); } + NSFont *m_font; +#ifndef BUILDING_ON_TIGER + RetainPtr<CGFontRef> m_cgFont; +#else + CGFontRef m_cgFont; // It is not necessary to refcount this, since either an NSFont owns it or some CachedFont has it referenced. +#endif }; } diff --git a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm index d1e00d1..15e573d 100644 --- a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm +++ b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm @@ -27,19 +27,24 @@ namespace WebCore { -FontPlatformData::FontPlatformData(NSFont* f, bool b , bool o) +FontPlatformData::FontPlatformData(NSFont *f, bool b , bool o) : m_syntheticBold(b), m_syntheticOblique(o), m_font(f) { if (f) CFRetain(f); m_size = f ? [f pointSize] : 0.0f; +#ifndef BUILDING_ON_TIGER + m_cgFont = CTFontCopyGraphicsFont(toCTFontRef(f), 0); + m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(f), 0); +#else m_cgFont = wkGetCGFontFromNSFont(f); m_atsuFontID = wkGetNSFontATSUFontId(f); +#endif } FontPlatformData::FontPlatformData(const FontPlatformData& f) { - m_font = (f.m_font && f.m_font != (NSFont*)-1) ? (NSFont*)CFRetain(f.m_font) : f.m_font; + m_font = f.m_font && f.m_font != reinterpret_cast<NSFont *>(-1) ? static_cast<const NSFont *>(CFRetain(f.m_font)) : f.m_font; m_syntheticBold = f.m_syntheticBold; m_syntheticOblique = f.m_syntheticOblique; m_size = f.m_size; @@ -49,22 +54,54 @@ FontPlatformData::FontPlatformData(const FontPlatformData& f) FontPlatformData:: ~FontPlatformData() { - if (m_font && m_font != (NSFont*)-1) + if (m_font && m_font != reinterpret_cast<NSFont *>(-1)) CFRelease(m_font); } -void FontPlatformData::setFont(NSFont* font) { +const FontPlatformData& FontPlatformData::operator=(const FontPlatformData& f) +{ + m_syntheticBold = f.m_syntheticBold; + m_syntheticOblique = f.m_syntheticOblique; + m_size = f.m_size; + m_cgFont = f.m_cgFont; + m_atsuFontID = f.m_atsuFontID; + if (m_font == f.m_font) + return *this; + if (f.m_font && f.m_font != reinterpret_cast<NSFont *>(-1)) + CFRetain(f.m_font); + if (m_font && m_font != reinterpret_cast<NSFont *>(-1)) + CFRelease(m_font); + m_font = f.m_font; + return *this; +} + +void FontPlatformData::setFont(NSFont *font) +{ if (m_font == font) return; if (font) CFRetain(font); - if (m_font && m_font != (NSFont*)-1) + if (m_font && m_font != reinterpret_cast<NSFont *>(-1)) CFRelease(m_font); m_font = font; m_size = font ? [font pointSize] : 0.0f; +#ifndef BUILDING_ON_TIGER + m_cgFont = CTFontCopyGraphicsFont(toCTFontRef(font), 0); + m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(font), 0); +#else m_cgFont = wkGetCGFontFromNSFont(font); m_atsuFontID = wkGetNSFontATSUFontId(font); +#endif } +bool FontPlatformData::roundsGlyphAdvances() const +{ + return [m_font renderingMode] == NSFontAntialiasedIntegerAdvancementsRenderingMode; +} + +bool FontPlatformData::allowsLigatures() const +{ + return ![[m_font coveredCharacterSet] characterIsMember:'a']; } +} // namespace WebCore diff --git a/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp b/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp index b9f2da3..143e665 100644 --- a/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp +++ b/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp @@ -37,6 +37,21 @@ namespace WebCore { bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { + bool haveGlyphs = false; + +#ifndef BUILDING_ON_TIGER + Vector<CGGlyph, 512> glyphs(bufferLength); + wkGetGlyphsForCharacters(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); + + for (unsigned i = 0; i < length; ++i) { + if (!glyphs[i]) + setGlyphDataForIndex(offset + i, 0, 0); + else { + setGlyphDataForIndex(offset + i, glyphs[i], fontData); + haveGlyphs = true; + } + } +#else // Use an array of long so we get good enough alignment. long glyphVector[(GLYPH_VECTOR_SIZE + sizeof(long) - 1) / sizeof(long)]; @@ -56,7 +71,6 @@ bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned b return false; } - bool haveGlyphs = false; ATSLayoutRecord* glyphRecord = (ATSLayoutRecord*)wkGetGlyphVectorFirstRecord(glyphVector); for (unsigned i = 0; i < length; i++) { Glyph glyph = glyphRecord->glyphID; @@ -69,6 +83,7 @@ bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned b glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + wkGetGlyphVectorRecordSize(glyphVector)); } wkClearGlyphVector(&glyphVector); +#endif return haveGlyphs; } diff --git a/WebCore/platform/graphics/mac/IconMac.mm b/WebCore/platform/graphics/mac/IconMac.mm index cda73a0..63abe59 100644 --- a/WebCore/platform/graphics/mac/IconMac.mm +++ b/WebCore/platform/graphics/mac/IconMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -28,10 +28,6 @@ namespace WebCore { -Icon::Icon() -{ -} - Icon::Icon(NSImage *image) : m_nsImage(image) { @@ -43,7 +39,7 @@ Icon::~Icon() { } -PassRefPtr<Icon> Icon::newIconForFile(const String& filename) +PassRefPtr<Icon> Icon::createIconForFile(const String& filename) { // Don't pass relative filenames -- we don't want a result that depends on the current directory. // Need 0U here to disambiguate String::operator[] from operator(NSString*, int)[] @@ -54,7 +50,23 @@ PassRefPtr<Icon> Icon::newIconForFile(const String& filename) if (!image) return 0; - return new Icon(image); + return adoptRef(new Icon(image)); +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +{ + if (filenames.isEmpty()) + return 0; +#ifdef BUILDING_ON_TIGER + // FIXME: find a better image to use on Tiger. + return createIconForFile(filenames[0]); +#else + NSImage* image = [NSImage imageNamed:NSImageNameMultipleDocuments]; + if (!image) + return 0; + + return adoptRef(new Icon(image)); +#endif } void Icon::paint(GraphicsContext* context, const IntRect& rect) diff --git a/WebCore/platform/graphics/mac/ImageMac.mm b/WebCore/platform/graphics/mac/ImageMac.mm index 0b14d71..a0d257b 100644 --- a/WebCore/platform/graphics/mac/ImageMac.mm +++ b/WebCore/platform/graphics/mac/ImageMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,8 +30,12 @@ #import "FoundationExtras.h" #import "GraphicsContext.h" #import "PlatformString.h" -#import "WebCoreFrameBridge.h" -#import "WebCoreSystemInterface.h" + +@interface WebCoreBundleFinder : NSObject +@end + +@implementation WebCoreBundleFinder +@end namespace WebCore { @@ -48,24 +52,22 @@ void BitmapImage::invalidatePlatformData() m_tiffRep = 0; } -Image* Image::loadPlatformResource(const char *name) +PassRefPtr<Image> Image::loadPlatformResource(const char *name) { - static BitmapImage nullImage; - - NSBundle *bundle = [NSBundle bundleForClass:[WebCoreFrameBridge class]]; + NSBundle *bundle = [NSBundle bundleForClass:[WebCoreBundleFinder class]]; NSString *imagePath = [bundle pathForResource:[NSString stringWithUTF8String:name] ofType:@"tiff"]; NSData *namedImageData = [NSData dataWithContentsOfFile:imagePath]; if (namedImageData) { - Image* image = new BitmapImage; + RefPtr<Image> image = BitmapImage::create(); image->setData(SharedBuffer::wrapNSData(namedImageData), true); - return image; + return image.release(); } - + // We have reports indicating resource loads are failing, but we don't yet know the root cause(s). // Two theories are bad installs (image files are missing), and too-many-open-files. // See rdar://5607381 ASSERT_NOT_REACHED(); - return &nullImage; + return Image::nullImage(); } CFDataRef BitmapImage::getTIFFRepresentation() diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index 8975d9b..3f18ab4 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -36,14 +36,20 @@ #import <QTKit/QTTime.h> @class QTMovie; @class QTMovieView; +@class QTVideoRendererWebKitOnly; @class WebCoreMovieObserver; #else class QTMovie; class QTMovieView; class QTTime; +class QTVideoRendererWebKitOnly; class WebCoreMovieObserver; #endif +#ifndef DRAW_FRAME_RATE +#define DRAW_FRAME_RATE 0 +#endif + namespace WebCore { class MediaPlayerPrivate : Noncopyable { @@ -99,8 +105,12 @@ public: private: void createQTMovie(const String& url); + void setUpVideoRendering(); + void tearDownVideoRendering(); void createQTMovieView(); void detachQTMovieView(); + void createQTVideoRenderer(); + void destroyQTVideoRenderer(); QTTime createQTTime(float time) const; void updateStates(); @@ -115,6 +125,7 @@ private: MediaPlayer* m_player; RetainPtr<QTMovie> m_qtMovie; RetainPtr<QTMovieView> m_qtMovieView; + RetainPtr<QTVideoRendererWebKitOnly> m_qtVideoRenderer; RetainPtr<WebCoreMovieObserver> m_objcObserver; float m_seekTo; float m_endTime; @@ -124,6 +135,12 @@ private: MediaPlayer::ReadyState m_readyState; bool m_startedPlaying; bool m_isStreaming; + bool m_visible; +#if DRAW_FRAME_RATE + int m_frameCountWhilePlaying; + double m_timeStartedPlaying; + double m_timeStoppedPlaying; +#endif }; } diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index 85c7a9e..0ec56d6 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 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 @@ -32,12 +32,20 @@ #import "BlockExceptions.h" #import "GraphicsContext.h" #import "KURL.h" -#import "ScrollView.h" +#import "FrameView.h" #import "SoftLinking.h" #import "WebCoreSystemInterface.h" #import <QTKit/QTKit.h> #import <objc/objc-runtime.h> +#if DRAW_FRAME_RATE +#import "Font.h" +#import "Frame.h" +#import "Document.h" +#import "RenderObject.h" +#import "RenderStyle.h" +#endif + #ifdef BUILDING_ON_TIGER static IMP method_setImplementation(Method m, IMP imp) { @@ -59,6 +67,7 @@ SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieAskUnresolvedDataRefsAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieDataSizeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieDidEndNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieHasVideoAttribute, NSString *) @@ -74,6 +83,7 @@ SOFT_LINK_POINTER(QTKit, QTMovieTimeScaleAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieURLAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieVolumeDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, NSString *) #define QTMovie getQTMovieClass() #define QTMovieView getQTMovieViewClass() @@ -83,6 +93,7 @@ SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *) #define QTMediaTypeSound getQTMediaTypeSound() #define QTMediaTypeText getQTMediaTypeText() #define QTMediaTypeVideo getQTMediaTypeVideo() +#define QTMovieAskUnresolvedDataRefsAttribute getQTMovieAskUnresolvedDataRefsAttribute() #define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute() #define QTMovieDidEndNotification getQTMovieDidEndNotification() #define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute() @@ -98,6 +109,7 @@ SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *) #define QTMovieURLAttribute getQTMovieURLAttribute() #define QTMovieVolumeDidChangeNotification getQTMovieVolumeDidChangeNotification() #define QTSecurityPolicyNoCrossSiteAttribute getQTSecurityPolicyNoCrossSiteAttribute() +#define QTVideoRendererWebKitOnlyNewImageAvailableNotification getQTVideoRendererWebKitOnlyNewImageAvailableNotification() // Older versions of the QTKit header don't have these constants. #if !defined QTKIT_VERSION_MAX_ALLOWED || QTKIT_VERSION_MAX_ALLOWED <= QTKIT_VERSION_7_0 @@ -116,10 +128,12 @@ using namespace std; @interface WebCoreMovieObserver : NSObject { MediaPlayerPrivate* m_callback; + NSView* m_view; BOOL m_delayCallbacks; } -(id)initWithCallback:(MediaPlayerPrivate*)callback; -(void)disconnect; +-(void)setView:(NSView*)view; -(void)repaint; -(void)setDelayCallbacks:(BOOL)shouldDelay; -(void)loadStateChanged:(NSNotification *)notification; @@ -129,11 +143,19 @@ using namespace std; -(void)didEnd:(NSNotification *)notification; @end +@protocol WebKitVideoRenderingDetails +-(void)setMovie:(id)movie; +-(void)drawInRect:(NSRect)rect; +@end + namespace WebCore { static const float endPointTimerInterval = 0.020f; + +#ifdef BUILDING_ON_TIGER static const long minimumQuickTimeVersion = 0x07300000; // 7.3 - +#endif + MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this]) @@ -145,12 +167,18 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_readyState(MediaPlayer::DataUnavailable) , m_startedPlaying(false) , m_isStreaming(false) + , m_visible(false) +#if DRAW_FRAME_RATE + , m_frameCountWhilePlaying(0) + , m_timeStartedPlaying(0) + , m_timeStoppedPlaying(0) +#endif { } MediaPlayerPrivate::~MediaPlayerPrivate() { - detachQTMovieView(); + tearDownVideoRendering(); [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; [m_objcObserver.get() disconnect]; @@ -160,23 +188,29 @@ void MediaPlayerPrivate::createQTMovie(const String& url) { [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; - m_qtMovie = 0; + if (m_qtMovie) { + destroyQTVideoRenderer(); + m_qtMovie = 0; + } // Disable streaming support for now, <rdar://problem/5693967> - if (url.startsWith("rtsp:")) + if (protocolIs(url, "rtsp")) return; - - NSDictionary* movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: - KURL(url.deprecatedString()).getNSURL(), QTMovieURLAttribute, + + NSURL *cocoaURL = KURL(url); + NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: + cocoaURL, QTMovieURLAttribute, [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, + [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, + [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute", // FIXME: Use defined attribute when required version of QT supports this attribute nil]; NSError* error = nil; m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]); // FIXME: Find a proper way to detect streaming content. - m_isStreaming = url.startsWith("rtsp:"); + m_isStreaming = protocolIs(url, "rtsp"); if (!m_qtMovie) return; @@ -220,13 +254,16 @@ static void mainThreadSetNeedsDisplay(id self, SEL _cmd) [delegate repaint]; } +static Class QTVideoRendererClass() +{ + static Class QTVideoRendererWebKitOnlyClass = NSClassFromString(@"QTVideoRendererWebKitOnly"); + return QTVideoRendererWebKitOnlyClass; +} + void MediaPlayerPrivate::createQTMovieView() { detachQTMovieView(); - if (!m_player->m_parentWidget || !m_qtMovie) - return; - static bool addedCustomMethods = false; if (!addedCustomMethods) { Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView"); @@ -241,7 +278,7 @@ void MediaPlayerPrivate::createQTMovieView() m_qtMovieView.adoptNS([[QTMovieView alloc] init]); setRect(m_player->rect()); - NSView* parentView = static_cast<ScrollView*>(m_player->m_parentWidget)->getDocumentView(); + NSView* parentView = m_player->m_frameView->documentView(); [parentView addSubview:m_qtMovieView.get()]; #ifdef BUILDING_ON_TIGER // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy @@ -249,17 +286,24 @@ void MediaPlayerPrivate::createQTMovieView() #else [m_qtMovieView.get() setDelegate:m_objcObserver.get()]; #endif + [m_objcObserver.get() setView:m_qtMovieView.get()]; [m_qtMovieView.get() setMovie:m_qtMovie.get()]; [m_qtMovieView.get() setControllerVisible:NO]; [m_qtMovieView.get() setPreservesAspectRatio:NO]; // the area not covered by video should be transparent [m_qtMovieView.get() setFillColor:[NSColor clearColor]]; - wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES); + + // If we're in a media document, allow QTMovieView to render in its default mode; + // otherwise tell it to draw synchronously. + // Note that we expect mainThreadSetNeedsDisplay to be invoked only when synchronous drawing is requested. + if (!m_player->inMediaDocument()) + wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES); } void MediaPlayerPrivate::detachQTMovieView() { if (m_qtMovieView) { + [m_objcObserver.get() setView:nil]; #ifdef BUILDING_ON_TIGER // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy [m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:nil]; @@ -271,6 +315,59 @@ void MediaPlayerPrivate::detachQTMovieView() } } +void MediaPlayerPrivate::createQTVideoRenderer() +{ + destroyQTVideoRenderer(); + + m_qtVideoRenderer.adoptNS([[QTVideoRendererClass() alloc] init]); + if (!m_qtVideoRenderer) + return; + + // associate our movie with our instance of QTVideoRendererWebKitOnly + [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()]; + + // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(newImageAvailable:) + name:QTVideoRendererWebKitOnlyNewImageAvailableNotification + object:m_qtVideoRenderer.get()]; +} + +void MediaPlayerPrivate::destroyQTVideoRenderer() +{ + if (!m_qtVideoRenderer) + return; + + // stop observing the renderer's notifications before we toss it + [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get() + name:QTVideoRendererWebKitOnlyNewImageAvailableNotification + object:m_qtVideoRenderer.get()]; + + // disassociate our movie from our instance of QTVideoRendererWebKitOnly + [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:nil]; + + m_qtVideoRenderer = nil; +} + +void MediaPlayerPrivate::setUpVideoRendering() +{ + if (!m_player->m_frameView || !m_qtMovie) + return; + + if (m_player->inMediaDocument() || !QTVideoRendererClass() ) + createQTMovieView(); + else + createQTVideoRenderer(); +} + +void MediaPlayerPrivate::tearDownVideoRendering() +{ + if (m_qtMovieView) + detachQTMovieView(); + else + destroyQTVideoRenderer(); +} + QTTime MediaPlayerPrivate::createQTTime(float time) const { if (!m_qtMovie) @@ -295,8 +392,6 @@ void MediaPlayerPrivate::load(const String& url) [m_objcObserver.get() setDelayCallbacks:YES]; createQTMovie(url); - if (m_player->visible()) - createQTMovieView(); [m_objcObserver.get() loadStateChanged:nil]; [m_objcObserver.get() setDelayCallbacks:NO]; @@ -307,6 +402,9 @@ void MediaPlayerPrivate::play() if (!m_qtMovie) return; m_startedPlaying = true; +#if DRAW_FRAME_RATE + m_frameCountWhilePlaying = 0; +#endif [m_objcObserver.get() setDelayCallbacks:YES]; [m_qtMovie.get() setRate:m_player->rate()]; [m_objcObserver.get() setDelayCallbacks:NO]; @@ -318,6 +416,9 @@ void MediaPlayerPrivate::pause() if (!m_qtMovie) return; m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; +#endif [m_objcObserver.get() setDelayCallbacks:YES]; [m_qtMovie.get() stop]; [m_objcObserver.get() setDelayCallbacks:NO]; @@ -521,7 +622,7 @@ void MediaPlayerPrivate::cancelLoad() if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) return; - detachQTMovieView(); + tearDownVideoRendering(); m_qtMovie = nil; updateStates(); @@ -534,14 +635,14 @@ void MediaPlayerPrivate::updateStates() long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError); - if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData) { + if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData && !m_player->inMediaDocument()) { unsigned enabledTrackCount; disableUnsupportedTracks(enabledTrackCount); // FIXME: We should differentiate between load errors and decode errors <rdar://problem/5605692> if (!enabledTrackCount) loadState = QTMovieLoadStateError; } - + // "Loaded" is reserved for fully buffered movies, never the case when streaming if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { if (m_networkState < MediaPlayer::Loaded) @@ -576,6 +677,9 @@ void MediaPlayerPrivate::updateStates() m_player->networkStateChanged(); if (m_readyState != oldReadyState) m_player->readyStateChanged(); + + if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::LoadedMetaData && m_player->visible()) + setUpVideoRendering(); } void MediaPlayerPrivate::loadStateChanged() @@ -602,6 +706,9 @@ void MediaPlayerPrivate::didEnd() { m_endPointTimer.stop(); m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; +#endif updateStates(); m_player->timeChanged(); } @@ -610,23 +717,42 @@ void MediaPlayerPrivate::setRect(const IntRect& r) { if (!m_qtMovieView) return; - // We don't really need the QTMovieView in any specific location so let's just get it out of the way - // where it won't intercept events or try to bring up the context menu. - IntRect farAwayButCorrectSize(r); - farAwayButCorrectSize.move(-1000000, -1000000); - [m_qtMovieView.get() setFrame:farAwayButCorrectSize]; + + if (m_player->inMediaDocument()) + // We need the QTMovieView to be placed in the proper location for document mode. + [m_qtMovieView.get() setFrame:r]; + else { + // We don't really need the QTMovieView in any specific location so let's just get it out of the way + // where it won't intercept events or try to bring up the context menu. + IntRect farAwayButCorrectSize(r); + farAwayButCorrectSize.move(-1000000, -1000000); + [m_qtMovieView.get() setFrame:farAwayButCorrectSize]; + } } void MediaPlayerPrivate::setVisible(bool b) { - if (b) - createQTMovieView(); - else - detachQTMovieView(); + if (m_visible != b) { + m_visible = b; + if (b) { + if (m_networkState >= MediaPlayer::LoadedMetaData) + setUpVideoRendering(); + } else + tearDownVideoRendering(); + } } void MediaPlayerPrivate::repaint() { +#if DRAW_FRAME_RATE + if (m_startedPlaying) { + m_frameCountWhilePlaying++; + // to eliminate preroll costs from our calculation, + // our frame rate calculation excludes the first frame drawn after playback starts + if (1==m_frameCountWhilePlaying) + m_timeStartedPlaying = [NSDate timeIntervalSinceReferenceDate]; + } +#endif m_player->repaint(); } @@ -635,16 +761,52 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) if (context->paintingDisabled()) return; NSView *view = m_qtMovieView.get(); - if (view == nil) + id qtVideoRenderer = m_qtVideoRenderer.get(); + if (!view && !qtVideoRenderer) return; + [m_objcObserver.get() setDelayCallbacks:YES]; BEGIN_BLOCK_OBJC_EXCEPTIONS; context->save(); context->translate(r.x(), r.y() + r.height()); context->scale(FloatSize(1.0f, -1.0f)); + context->setImageInterpolationQuality(InterpolationLow); IntRect paintRect(IntPoint(0, 0), IntSize(r.width(), r.height())); NSGraphicsContext* newContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context->platformContext() flipped:NO]; - [view displayRectIgnoringOpacity:paintRect inContext:newContext]; + + // draw the current video frame + if (qtVideoRenderer) { + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:newContext]; + [(id<WebKitVideoRenderingDetails>)qtVideoRenderer drawInRect:paintRect]; + [NSGraphicsContext restoreGraphicsState]; + } else + [view displayRectIgnoringOpacity:paintRect inContext:newContext]; + +#if DRAW_FRAME_RATE + // Draw the frame rate only after having played more than 10 frames. + if (m_frameCountWhilePlaying > 10) { + Frame* frame = m_player->m_frameView ? m_player->m_frameView->frame() : NULL; + Document* document = frame ? frame->document() : NULL; + RenderObject* renderer = document ? document->renderer() : NULL; + RenderStyle* styleToUse = renderer ? renderer->style() : NULL; + if (styleToUse) { + double frameRate = (m_frameCountWhilePlaying - 1) / ( m_startedPlaying ? ([NSDate timeIntervalSinceReferenceDate] - m_timeStartedPlaying) : + (m_timeStoppedPlaying - m_timeStartedPlaying) ); + String text = String::format("%1.2f", frameRate); + TextRun textRun(text.characters(), text.length()); + const Color color(255, 0, 0); + context->scale(FloatSize(1.0f, -1.0f)); + context->setFont(styleToUse->font()); + context->setStrokeColor(color); + context->setStrokeStyle(SolidStroke); + context->setStrokeThickness(1.0f); + context->setFillColor(color); + context->drawText(textRun, IntPoint(2, -3)); + } + } +#endif + context->restore(); END_BLOCK_OBJC_EXCEPTIONS; [m_objcObserver.get() setDelayCallbacks:NO]; @@ -668,9 +830,9 @@ void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) bool MediaPlayerPrivate::isAvailable() { +#ifdef BUILDING_ON_TIGER SInt32 version; OSErr result; - // This Carbon API is available in 64 bit too result = Gestalt(gestaltQuickTime, &version); if (result != noErr) { LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support."); @@ -681,6 +843,10 @@ bool MediaPlayerPrivate::isAvailable() return false; } return true; +#else + // On 10.5 and higher, QuickTime will always be new enough for <video> and <audio> support, so we just check that the framework can be loaded. + return QTKitLibrary(); +#endif } void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) @@ -784,6 +950,17 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) m_callback = 0; } +-(NSMenu*)menuForEventDelegate:(NSEvent*)theEvent +{ + // Get the contextual menu from the QTMovieView's superview, the frame view + return [[m_view superview] menuForEvent:theEvent]; +} + +-(void)setView:(NSView*)view +{ + m_view = view; +} + -(void)repaint { if (m_delayCallbacks) @@ -832,6 +1009,11 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) m_callback->didEnd(); } +- (void)newImageAvailable:(NSNotification *)notification +{ + [self repaint]; +} + - (void)setDelayCallbacks:(BOOL)shouldDelay { m_delayCallbacks = shouldDelay; diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index 1f45c94..4ee5933 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -56,13 +56,14 @@ static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x * (c bool initFontData(SimpleFontData* fontData) { - if (!fontData->m_font.m_cgFont) + if (!fontData->m_font.cgFont()) return false; +#ifdef BUILDING_ON_TIGER ATSUStyle fontStyle; if (ATSUCreateStyle(&fontStyle) != noErr) return false; - + ATSUFontID fontId = fontData->m_font.m_atsuFontID; if (!fontId) { ATSUDisposeStyle(fontStyle); @@ -84,6 +85,7 @@ bool initFontData(SimpleFontData* fontData) } ATSUDisposeStyle(fontStyle); +#endif return true; } @@ -96,16 +98,63 @@ static NSString *webFallbackFontFamily(void) return webFallbackFontFamily.get(); } +#if !ERROR_DISABLED +#ifdef __LP64__ +static NSString* pathFromFont(NSFont*) +{ + // FMGetATSFontRefFromFont is not available in 64-bit. As pathFromFont is only used for debugging + // purposes, returning nil is acceptable. + return nil; +} +#else +static NSString* pathFromFont(NSFont *font) +{ +#ifndef BUILDING_ON_TIGER + ATSFontRef atsFont = FMGetATSFontRefFromFont(CTFontGetPlatformFont(toCTFontRef(font), 0)); +#else + ATSFontRef atsFont = FMGetATSFontRefFromFont(wkGetNSFontATSUFontId(font)); +#endif + FSRef fileRef; + +#ifndef BUILDING_ON_TIGER + OSStatus status = ATSFontGetFileReference(atsFont, &fileRef); + if (status != noErr) + return nil; +#else + FSSpec oFile; + OSStatus status = ATSFontGetFileSpecification(atsFont, &oFile); + if (status != noErr) + return nil; + + status = FSpMakeFSRef(&oFile, &fileRef); + if (status != noErr) + return nil; +#endif + + UInt8 filePathBuffer[PATH_MAX]; + status = FSRefMakePath(&fileRef, filePathBuffer, PATH_MAX); + if (status == noErr) + return [NSString stringWithUTF8String:(const char*)filePathBuffer]; + + return nil; +} +#endif // __LP64__ +#endif // !ERROR_DISABLED + void SimpleFontData::platformInit() { +#ifdef BUILDING_ON_TIGER m_styleGroup = 0; +#endif +#if USE(ATSUI) m_ATSUStyleInitialized = false; m_ATSUMirrors = false; m_checkedShapesArabic = false; m_shapesArabic = false; +#endif m_syntheticBoldOffset = m_font.m_syntheticBold ? 1.0f : 0.f; - + bool failedSetup = false; if (!initFontData(this)) { // Ack! Something very bad happened, like a corrupt font. @@ -126,9 +175,12 @@ void SimpleFontData::platformInit() #if !ERROR_DISABLED RetainPtr<NSFont> initialFont = m_font.font(); #endif - m_font.setFont([[NSFontManager sharedFontManager] convertFont:m_font.font() toFamily:fallbackFontFamily]); + if (m_font.font()) + m_font.setFont([[NSFontManager sharedFontManager] convertFont:m_font.font() toFamily:fallbackFontFamily]); + else + m_font.setFont([NSFont fontWithName:fallbackFontFamily size:m_font.size()]); #if !ERROR_DISABLED - NSString *filePath = wkPathFromFont(initialFont.get()); + NSString *filePath = pathFromFont(initialFont.get()); if (!filePath) filePath = @"not known"; #endif @@ -165,7 +217,15 @@ void SimpleFontData::platformInit() int iAscent; int iDescent; int iLineGap; - wkGetFontMetrics(m_font.m_cgFont, &iAscent, &iDescent, &iLineGap, &m_unitsPerEm); +#ifdef BUILDING_ON_TIGER + wkGetFontMetrics(m_font.cgFont(), &iAscent, &iDescent, &iLineGap, &m_unitsPerEm); +#else + iAscent = CGFontGetAscent(m_font.cgFont()); + iDescent = CGFontGetDescent(m_font.cgFont()); + iLineGap = CGFontGetLeading(m_font.cgFont()); + m_unitsPerEm = CGFontGetUnitsPerEm(m_font.cgFont()); +#endif + float pointSize = m_font.m_size; float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize; float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize; @@ -179,6 +239,13 @@ void SimpleFontData::platformInit() NSString *familyName = [m_font.font() familyName]; if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"Helvetica"] || [familyName isEqualToString:@"Courier"]) fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f); + else if ([familyName isEqualToString:@"Geeza Pro"]) { + // Geeza Pro has glyphs that draw slightly above the ascent or far below the descent. Adjust + // those vertical metrics to better match reality, so that diacritics at the bottom of one line + // do not overlap diacritics at the top of the next line. + fAscent *= 1.08f; + fDescent *= 2.f; + } m_ascent = lroundf(fAscent); m_descent = lroundf(fDescent); @@ -209,11 +276,14 @@ void SimpleFontData::platformInit() void SimpleFontData::platformDestroy() { +#ifdef BUILDING_ON_TIGER if (m_styleGroup) wkReleaseStyleGroup(m_styleGroup); - +#endif +#if USE(ATSUI) if (m_ATSUStyleInitialized) ATSUDisposeStyle(m_ATSUStyle); +#endif } SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const @@ -290,13 +360,14 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const float pointSize = m_font.m_size; CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize); CGSize advance; - if (!wkGetGlyphTransformedAdvances(m_font.m_cgFont, font, &m, &glyph, &advance)) { + if (!wkGetGlyphTransformedAdvances(m_font.cgFont(), font, &m, &glyph, &advance)) { LOG_ERROR("Unable to cache glyph widths for %@ %f", [font displayName], pointSize); advance.width = 0; } return advance.width + m_syntheticBoldOffset; } +#if USE(ATSUI) void SimpleFontData::checkShapesArabic() const { ASSERT(!m_checkedShapesArabic); @@ -325,5 +396,40 @@ void SimpleFontData::checkShapesArabic() const LOG_ERROR("ATSFontGetTable failed (%d)", status); } } +#endif +#if USE(CORE_TEXT) +CTFontRef SimpleFontData::getCTFont() const +{ + if (getNSFont()) + return toCTFontRef(getNSFont()); + if (!m_CTFont) + m_CTFont.adoptCF(CTFontCreateWithGraphicsFont(m_font.cgFont(), m_font.size(), NULL, NULL)); + return m_CTFont.get(); } + +CFDictionaryRef SimpleFontData::getCFStringAttributes() const +{ + if (m_CFStringAttributes) + return m_CFStringAttributes.get(); + + static const float kerningAdjustmentValue = 0; + static CFNumberRef kerningAdjustment = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &kerningAdjustmentValue); + + static const int ligaturesNotAllowedValue = 0; + static CFNumberRef ligaturesNotAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesNotAllowedValue); + + static const int ligaturesAllowedValue = 1; + static CFNumberRef ligaturesAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesAllowedValue); + + static const void* attributeKeys[] = { kCTFontAttributeName, kCTKernAttributeName, kCTLigatureAttributeName }; + const void* attributeValues[] = { getCTFont(), kerningAdjustment, platformData().allowsLigatures() ? ligaturesAllowed : ligaturesNotAllowed }; + + m_CFStringAttributes.adoptCF(CFDictionaryCreate(NULL, attributeKeys, attributeValues, sizeof(attributeKeys) / sizeof(*attributeKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + + return m_CFStringAttributes.get(); +} + +#endif + +} // namespace WebCore diff --git a/WebCore/platform/graphics/qt/AffineTransformQt.cpp b/WebCore/platform/graphics/qt/AffineTransformQt.cpp index 8bd5c87..2793043 100644 --- a/WebCore/platform/graphics/qt/AffineTransformQt.cpp +++ b/WebCore/platform/graphics/qt/AffineTransformQt.cpp @@ -41,7 +41,7 @@ AffineTransform::AffineTransform(double a, double b, double c, double d, double { } -AffineTransform::AffineTransform(const QMatrix& matrix) +AffineTransform::AffineTransform(const PlatformAffineTransform& matrix) : m_transform(matrix) { } diff --git a/WebCore/platform/graphics/qt/FontCacheQt.cpp b/WebCore/platform/graphics/qt/FontCacheQt.cpp index 8fb3fba..be31d96 100644 --- a/WebCore/platform/graphics/qt/FontCacheQt.cpp +++ b/WebCore/platform/graphics/qt/FontCacheQt.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Trolltech ASA + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -26,14 +26,8 @@ namespace WebCore { -bool FontCache::fontExists(const FontDescription &desc, const AtomicString& family) +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) { - // try to construct a QFont inside WebCore::Font to see if we know about this font - FontDescription fnt(desc); - FontFamily fam; - fam.setFamily(family); - fnt.setFamily(fam); - return Font(fnt, /*letterSpacing*/0, /*wordSpacing*/0).font().exactMatch(); } FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription&, const AtomicString& family, bool checkingAlternateName) @@ -51,4 +45,12 @@ FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription&) return 0; } +void FontCache::addClient(FontSelector*) +{ } + +void FontCache::removeClient(FontSelector*) +{ +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/qt/FontCustomPlatformData.cpp b/WebCore/platform/graphics/qt/FontCustomPlatformData.cpp index 67193d4..8fc3ea0 100644 --- a/WebCore/platform/graphics/qt/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/qt/FontCustomPlatformData.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Trolltech ASA + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -33,7 +33,7 @@ FontCustomPlatformData::~FontCustomPlatformData() QFontDatabase::removeApplicationFont(handle); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode) { FontPlatformData result; result.handle = handle; diff --git a/WebCore/platform/graphics/qt/FontCustomPlatformData.h b/WebCore/platform/graphics/qt/FontCustomPlatformData.h index b7a2b15..da5159d 100644 --- a/WebCore/platform/graphics/qt/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/qt/FontCustomPlatformData.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Trolltech ASA + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -22,6 +22,7 @@ #ifndef FontCustomPlatformData_h_ #define FontCustomPlatformData_h_ +#include "FontRenderingMode.h" #include <wtf/Noncopyable.h> namespace WebCore { @@ -34,7 +35,7 @@ struct FontCustomPlatformData : Noncopyable { int handle; // for use with QFontDatabase::addApplicationFont/removeApplicationFont - FontPlatformData fontPlatformData(int size, bool bold, bool italic); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); }; FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer); diff --git a/WebCore/platform/graphics/qt/FontPlatformData.h b/WebCore/platform/graphics/qt/FontPlatformData.h index 7daf6ed..e4363be 100644 --- a/WebCore/platform/graphics/qt/FontPlatformData.h +++ b/WebCore/platform/graphics/qt/FontPlatformData.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Trolltech ASA + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp index 29b63bf..e2ef605 100644 --- a/WebCore/platform/graphics/qt/FontQt.cpp +++ b/WebCore/platform/graphics/qt/FontQt.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Trolltech ASA + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -15,10 +15,8 @@ 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. - - 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 "Font.h" #include "FontDescription.h" @@ -64,12 +62,12 @@ Font::Font(const FontDescription& description, short letterSpacing, short wordSp m_font.setFamily(familyName); m_font.setPixelSize(qRound(description.computedSize())); m_font.setItalic(description.italic()); - if (description.bold()) { - // Qt's Bold is 75, Webkit is 63. + // FIXME: Map all FontWeight values to QFont weights. + if (description.weight() >= FontWeight600) m_font.setWeight(QFont::Bold); - } else { - m_font.setWeight(description.weight()); - } + else + m_font.setWeight(QFont::Normal); + bool smallCaps = description.smallCaps(); m_font.setCapitalization(smallCaps ? QFont::SmallCaps : QFont::MixedCase); @@ -133,6 +131,12 @@ void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& QString string = qstring(run); + // text shadow + IntSize shadowSize; + int shadowBlur; + Color shadowColor; + bool hasShadow = ctx->textDrawingMode() == cTextFill && ctx->getShadow(shadowSize, shadowBlur, shadowColor); + if (from > 0 || to < run.length()) { QTextLayout layout(string, m_font); QTextLine line = setupLayout(&layout, run); @@ -145,9 +149,31 @@ void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& int ascent = fm.ascent(); QRectF clip(point.x() + x1, point.y() - ascent, x2 - x1, fm.height()); + if (hasShadow) { + // TODO: when blur support is added, the clip will need to account + // for the blur radius + qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0; + if (shadowSize.width() > 0) + dx2 = shadowSize.width(); + else + dx1 = -shadowSize.width(); + if (shadowSize.height() > 0) + dy2 = shadowSize.height(); + else + dy1 = -shadowSize.height(); + // expand the clip rect to include the text shadow as well + clip.adjust(dx1, dx2, dy1, dy2); + } p->save(); p->setClipRect(clip.toRect()); QPointF pt(point.x(), point.y() - ascent); + if (hasShadow) { + p->save(); + p->setPen(QColor(shadowColor)); + p->translate(shadowSize.width(), shadowSize.height()); + line.draw(p, pt); + p->restore(); + } line.draw(p, pt); p->restore(); return; @@ -157,6 +183,14 @@ void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& QPointF pt(point.x(), point.y()); int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; + if (hasShadow) { + // TODO: text shadow blur support + p->save(); + p->setPen(QColor(shadowColor)); + p->translate(shadowSize.width(), shadowSize.height()); + p->drawText(pt, string, flags, run.padding()); + p->restore(); + } p->drawText(pt, string, flags, run.padding()); } @@ -165,7 +199,9 @@ int Font::width(const TextRun& run) const if (!run.length()) return 0; QString string = qstring(run); - int w = QFontMetrics(m_font).width(string); + QTextLayout layout(string, m_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) if (treatAsSpace(run[0])) w -= m_wordSpacing; @@ -178,6 +214,13 @@ 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 { QString string = qstring(run); @@ -272,12 +315,12 @@ Font::Font(const FontDescription& description, short letterSpacing, short wordSp m_font.setFamily(familyName); m_font.setPixelSize(qRound(description.computedSize())); m_font.setItalic(description.italic()); - if (description.bold()) { - // Qt's Bold is 75, Webkit is 63. + // FIXME: Map all FontWeight values to QFont weights. + if (description.weight() >= FontWeight600) m_font.setWeight(QFont::Bold); - } else { - m_font.setWeight(description.weight()); - } + else + m_font.setWeight(QFont::Normal); + QFontMetrics metrics = QFontMetrics(m_font); m_spaceWidth = metrics.width(QLatin1Char(' ')); m_scFont = m_font; @@ -469,6 +512,13 @@ 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 { Vector<TextRunComponent, 1024> components; @@ -633,6 +683,11 @@ 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(); diff --git a/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp b/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp index 220807e..d32cc63 100644 --- a/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp +++ b/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Trolltech ASA + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public diff --git a/WebCore/platform/graphics/qt/GradientQt.cpp b/WebCore/platform/graphics/qt/GradientQt.cpp new file mode 100644 index 0000000..f414efa --- /dev/null +++ b/WebCore/platform/graphics/qt/GradientQt.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Gradient.h" + +#include "CSSParser.h" +#include "NotImplemented.h" + +#include <QGradient> + +namespace WebCore { + +void Gradient::platformDestroy() +{ + delete m_gradient; + m_gradient = 0; +} + +QGradient* Gradient::platformGradient() +{ + if (m_gradient) + return m_gradient; + + if (m_radial) + m_gradient = new QRadialGradient(m_p1.x(), m_p1.y(), m_r1, m_p0.x(), m_p0.y()); + else + m_gradient = new QLinearGradient(m_p0.x(), m_p0.y(), m_p1.x(), m_p1.y()); + + QColor stopColor; + Vector<ColorStop>::iterator stopIterator = m_stops.begin(); + qreal lastStop; + const qreal lastStopDiff = 0.0000001; + while (stopIterator != m_stops.end()) { + stopColor.setRgbF(stopIterator->red, stopIterator->green, stopIterator->blue, stopIterator->alpha); + if (qFuzzyCompare(lastStop, qreal(stopIterator->stop))) + lastStop = stopIterator->stop + lastStopDiff; + else + lastStop = stopIterator->stop; + if (m_radial && m_r0) + lastStop = m_r0 / m_r1 + lastStop * (1.0f - m_r0 / m_r1); + m_gradient->setColorAt(lastStop, stopColor); + ++stopIterator; + } + + return m_gradient; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + notImplemented(); +} + +} //namespace diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index cf097c8..600d77c 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -5,6 +5,9 @@ * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org> * Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org> * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> * * All rights reserved. * @@ -32,20 +35,32 @@ #include "config.h" +#ifdef Q_WS_WIN +#include <windows.h> +#endif + #include "AffineTransform.h" -#include "Path.h" #include "Color.h" -#include "GraphicsContext.h" +#include "FloatConversion.h" #include "Font.h" +#include "GraphicsContext.h" +#include "GraphicsContextPrivate.h" +#include "ImageBuffer.h" +#include "Path.h" +#include "Pattern.h" #include "Pen.h" #include "NotImplemented.h" -#include <QStack> +#include <QDebug> +#include <QGradient> #include <QPainter> -#include <QPolygonF> -#include <QPainterPath> #include <QPaintDevice> -#include <QDebug> +#include <QPaintEngine> +#include <QPainterPath> +#include <QPixmap> +#include <QPolygonF> +#include <QStack> +#include <QVector> #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -137,6 +152,22 @@ static Qt::PenStyle toQPenStyle(StrokeStyle style) return Qt::NoPen; } +static inline QGradient applySpreadMethod(QGradient gradient, GradientSpreadMethod spreadMethod) +{ + switch (spreadMethod) { + case SpreadMethodPad: + gradient.setSpread(QGradient::PadSpread); + break; + case SpreadMethodReflect: + gradient.setSpread(QGradient::ReflectSpread); + break; + case SpreadMethodRepeat: + gradient.setSpread(QGradient::RepeatSpread); + break; + } + return gradient; +} + struct TransparencyLayer { TransparencyLayer(const QPainter* p, const QRect &rect) @@ -145,13 +176,15 @@ struct TransparencyLayer offset = rect.topLeft(); pixmap.fill(Qt::transparent); painter.begin(&pixmap); + painter.setRenderHint(QPainter::Antialiasing, p->testRenderHint(QPainter::Antialiasing)); painter.translate(-offset); painter.setPen(p->pen()); painter.setBrush(p->brush()); painter.setTransform(p->transform(), true); painter.setOpacity(p->opacity()); painter.setFont(p->font()); - painter.setCompositionMode(p->compositionMode()); + if (painter.paintEngine()->hasFeature(QPaintEngine::PorterDuff)) + painter.setCompositionMode(p->compositionMode()); painter.setClipPath(p->clipPath()); } @@ -168,24 +201,6 @@ private: TransparencyLayer & operator=(const TransparencyLayer &) { return *this; } }; -struct TextShadow -{ - TextShadow() - : x(0) - , y(0) - , blur(0) - { - } - - bool isNull() { return !x && !y && !blur; } - - int x; - int y; - int blur; - - Color color; -}; - class GraphicsContextPlatformPrivate { public: @@ -203,12 +218,12 @@ public: return &layers.top()->painter; } - QPaintDevice* device; + bool antiAliasingForRectsAndLines; QStack<TransparencyLayer *> layers; QPainter* redirect; - TextShadow shadow; + QBrush solidColor; // Only used by SVG for now. QPainterPath currentPath; @@ -221,12 +236,18 @@ private: GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p) { painter = p; - device = painter ? painter->device() : 0; redirect = 0; - // FIXME: Maybe only enable in SVG mode? - if (painter) - painter->setRenderHint(QPainter::Antialiasing); + solidColor = QBrush(Qt::black); + + if (painter) { + // use the default the QPainter was constructed with + antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing); + // FIXME: Maybe only enable in SVG mode? + painter->setRenderHint(QPainter::Antialiasing, true); + } else { + antiAliasingForRectsAndLines = false; + } } GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate() @@ -386,7 +407,13 @@ void GraphicsContext::drawRect(const IntRect& rect) if (paintingDisabled()) return; - m_data->p()->drawRect(rect); + QPainter *p = m_data->p(); + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); + + p->drawRect(rect); + + p->setRenderHint(QPainter::Antialiasing, antiAlias); } // FIXME: Now that this is refactored, it should be shared by all contexts. @@ -429,8 +456,25 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) FloatPoint p1 = point1; FloatPoint p2 = point2; + QPainter *p = m_data->p(); + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); adjustLineToPixelBoundaries(p1, p2, strokeThickness(), strokeStyle()); - m_data->p()->drawLine(p1, p2); + + IntSize shadowSize; + int shadowBlur; + Color shadowColor; + if (textDrawingMode() == cTextFill && getShadow(shadowSize, shadowBlur, shadowColor)) { + p->save(); + p->translate(shadowSize.width(), shadowSize.height()); + p->setPen(QColor(shadowColor)); + p->drawLine(p1, p2); + p->restore(); + } + + p->drawLine(p1, p2); + + p->setRenderHint(QPainter::Antialiasing, antiAlias); } // This method is only used to draw the little circles used in lists. @@ -447,7 +491,13 @@ void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSp if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f || !strokeColor().alpha()) return; - m_data->p()->drawArc(rect, startAngle * 16, angleSpan * 16); + QPainter *p = m_data->p(); + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); + + p->drawArc(rect, startAngle * 16, angleSpan * 16); + + p->setRenderHint(QPainter::Antialiasing, antiAlias); } void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) @@ -470,12 +520,80 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points p->restore(); } -void GraphicsContext::fillRect(const IntRect& rect, const Color& c) +void GraphicsContext::fillPath() +{ + if (paintingDisabled()) + return; + + QPainter *p = m_data->p(); + QPainterPath path = m_data->currentPath; + + switch (m_common->state.fillColorSpace) { + case SolidColorSpace: + if (fillColor().alpha()) + p->fillPath(path, p->brush()); + break; + case PatternColorSpace: + p->fillPath(path, QBrush(m_common->state.fillPattern.get()->createPlatformPattern(getCTM()))); + break; + case GradientColorSpace: + QGradient* gradient = m_common->state.fillGradient.get()->platformGradient(); + *gradient = applySpreadMethod(*gradient, spreadMethod()); + p->fillPath(path, QBrush(*gradient)); + break; + } +} + +void GraphicsContext::strokePath() +{ + if (paintingDisabled()) + return; + + QPainter *p = m_data->p(); + QPen pen = p->pen(); + QPainterPath path = m_data->currentPath; + + switch (m_common->state.strokeColorSpace) { + case SolidColorSpace: + if (strokeColor().alpha()) + p->strokePath(path, pen); + break; + case PatternColorSpace: { + pen.setBrush(QBrush(m_common->state.strokePattern.get()->createPlatformPattern(getCTM()))); + p->setPen(pen); + p->strokePath(path, pen); + break; + } + case GradientColorSpace: { + QGradient* gradient = m_common->state.strokeGradient.get()->platformGradient(); + *gradient = applySpreadMethod(*gradient, spreadMethod()); + pen.setBrush(QBrush(*gradient)); + p->setPen(pen); + p->strokePath(path, pen); + break; + } + } +} + +void GraphicsContext::fillRect(const FloatRect& rect) { if (paintingDisabled()) return; - m_data->p()->fillRect(rect, QColor(c)); + QPainter *p = m_data->p(); + + switch (m_common->state.fillColorSpace) { + case SolidColorSpace: + if (fillColor().alpha()) + p->fillRect(rect, p->brush()); + break; + case PatternColorSpace: + p->fillRect(rect, QBrush(m_common->state.fillPattern.get()->createPlatformPattern(getCTM()))); + break; + case GradientColorSpace: + p->fillRect(rect, QBrush(*(m_common->state.fillGradient.get()->platformGradient()))); + break; + } } void GraphicsContext::fillRect(const FloatRect& rect, const Color& c) @@ -483,7 +601,8 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& c) if (paintingDisabled()) return; - m_data->p()->fillRect(rect, QColor(c)); + m_data->solidColor.setColor(QColor(c)); + m_data->p()->fillRect(rect, m_data->solidColor); } void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color) @@ -505,9 +624,9 @@ void GraphicsContext::addPath(const Path& path) m_data->currentPath = *(path.platformPath()); } -void GraphicsContext::setFillRule(WindRule rule) +bool GraphicsContext::inTransparencyLayer() const { - m_data->currentPath.setFillRule(rule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill); + return !m_data->layers.isEmpty(); } PlatformPath* GraphicsContext::currentPath() @@ -515,7 +634,7 @@ PlatformPath* GraphicsContext::currentPath() return &m_data->currentPath; } -void GraphicsContext::clip(const IntRect& rect) +void GraphicsContext::clip(const FloatRect& rect) { if (paintingDisabled()) return; @@ -531,7 +650,6 @@ void GraphicsContext::clip(const IntRect& rect) * RenderTheme handles drawing focus on widgets which * need it. */ -void setFocusRingColorChangeFunction(void (*)()) { } Color focusRingColor() { return Color(0, 0, 0); } void GraphicsContext::drawFocusRing(const Color& color) { @@ -545,6 +663,8 @@ void GraphicsContext::drawFocusRing(const Color& color) return; QPainter *p = m_data->p(); + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); const QPen oldPen = p->pen(); const QBrush oldBrush = p->brush(); @@ -568,6 +688,8 @@ void GraphicsContext::drawFocusRing(const Color& color) #endif p->setPen(oldPen); p->setBrush(oldBrush); + + p->setRenderHint(QPainter::Antialiasing, antiAlias); } void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing) @@ -597,23 +719,16 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) return FloatRect(QRectF(result)); } -void GraphicsContext::setShadow(const IntSize& pos, int blur, const Color &color) +void GraphicsContext::setPlatformShadow(const IntSize& pos, int blur, const Color &color) { - if (paintingDisabled()) - return; - - m_data->shadow.x = pos.width(); - m_data->shadow.y = pos.height(); - m_data->shadow.blur = blur; - m_data->shadow.color = color; + // Qt doesn't support shadows natively, they are drawn manually in the draw* + // functions } -void GraphicsContext::clearShadow() +void GraphicsContext::clearPlatformShadow() { - if (paintingDisabled()) - return; - - m_data->shadow = TextShadow(); + // Qt doesn't support shadows natively, they are drawn manually in the draw* + // functions } void GraphicsContext::beginTransparencyLayer(float opacity) @@ -623,20 +738,18 @@ void GraphicsContext::beginTransparencyLayer(float opacity) int x, y, w, h; x = y = 0; - w = m_data->device->width(); - h = m_data->device->height(); - QPainter *p = m_data->p(); + const QPaintDevice *device = p->device(); + w = device->width(); + h = device->height(); + QRectF clip = p->clipPath().boundingRect(); - bool ok; - QTransform transform = p->transform().inverted(&ok); - if (ok) { - QRectF deviceClip = transform.mapRect(clip); - x = int(qBound(qreal(0), deviceClip.x(), (qreal)w)); - y = int(qBound(qreal(0), deviceClip.y(), (qreal)h)); - w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2); - h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2); - } + QRectF deviceClip = p->transform().mapRect(clip); + x = int(qBound(qreal(0), deviceClip.x(), (qreal)w)); + y = int(qBound(qreal(0), deviceClip.y(), (qreal)h)); + w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2); + h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2); + TransparencyLayer * layer = new TransparencyLayer(m_data->p(), QRect(x, y, w, h)); layer->opacity = opacity; @@ -668,9 +781,11 @@ void GraphicsContext::clearRect(const FloatRect& rect) QPainter *p = m_data->p(); QPainter::CompositionMode currentCompositionMode = p->compositionMode(); - p->setCompositionMode(QPainter::CompositionMode_Source); + if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) + p->setCompositionMode(QPainter::CompositionMode_Source); p->eraseRect(rect); - p->setCompositionMode(currentCompositionMode); + if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) + p->setCompositionMode(currentCompositionMode); } void GraphicsContext::strokeRect(const FloatRect& rect, float width) @@ -678,12 +793,12 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float width) if (paintingDisabled()) return; - QPainter *p = m_data->p(); QPainterPath path; path.addRect(rect); - QPen nPen = p->pen(); - nPen.setWidthF(width); - p->strokePath(path, nPen); + setStrokeThickness(width); + m_data->currentPath = path; + + strokePath(); } void GraphicsContext::setLineCap(LineCap lc) @@ -697,6 +812,26 @@ void GraphicsContext::setLineCap(LineCap lc) p->setPen(nPen); } +void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) +{ + QPainter* p = m_data->p(); + QPen pen = p->pen(); + unsigned dashLength = dashes.size(); + if (dashLength) { + QVector<qreal> pattern; + unsigned count = dashLength; + if (dashLength % 2) + count *= 2; + + for (unsigned i = 0; i < count; i++) + pattern.append(dashes[i % dashLength] / narrowPrecisionToFloat(pen.widthF())); + + pen.setDashPattern(pattern); + pen.setDashOffset(dashOffset); + } + p->setPen(pen); +} + void GraphicsContext::setLineJoin(LineJoin lj) { if (paintingDisabled()) @@ -732,7 +867,8 @@ void GraphicsContext::setCompositeOperation(CompositeOperator op) if (paintingDisabled()) return; - m_data->p()->setCompositionMode(toQtCompositionMode(op)); + if (m_data->p()->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) + m_data->p()->setCompositionMode(toQtCompositionMode(op)); } void GraphicsContext::clip(const Path& path) @@ -780,7 +916,7 @@ void GraphicsContext::rotate(float radians) if (paintingDisabled()) return; - m_data->p()->rotate(radians); + m_data->p()->rotate(180/M_PI*radians); } void GraphicsContext::scale(const FloatSize& s) @@ -795,7 +931,7 @@ void GraphicsContext::clipOut(const IntRect& rect) { if (paintingDisabled()) return; - + QPainter *p = m_data->p(); QRectF clipBounds = p->clipPath().boundingRect(); QPainterPath newClip; @@ -810,7 +946,7 @@ void GraphicsContext::clipOutEllipseInRect(const IntRect& rect) { if (paintingDisabled()) return; - + QPainter *p = m_data->p(); QRectF clipBounds = p->clipPath().boundingRect(); QPainterPath newClip; @@ -821,6 +957,11 @@ void GraphicsContext::clipOutEllipseInRect(const IntRect& rect) p->setClipPath(newClip, Qt::IntersectClip); } +void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) +{ + notImplemented(); +} + void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) { @@ -872,7 +1013,7 @@ void GraphicsContext::setPlatformStrokeColor(const Color& color) } void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle) -{ +{ if (paintingDisabled()) return; QPainter *p = m_data->p(); @@ -905,6 +1046,101 @@ void GraphicsContext::setUseAntialiasing(bool enable) m_data->p()->setRenderHint(QPainter::Antialiasing, enable); } +#ifdef Q_WS_WIN +#include <windows.h> + +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); + + if (dstRect.isEmpty()) + return 0; + + // Create a bitmap DC in which to draw. + BITMAPINFO bitmapInfo; + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = dstRect.width(); + bitmapInfo.bmiHeader.biHeight = dstRect.height(); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + bitmapInfo.bmiHeader.biSizeImage = 0; + bitmapInfo.bmiHeader.biXPelsPerMeter = 0; + bitmapInfo.bmiHeader.biYPelsPerMeter = 0; + bitmapInfo.bmiHeader.biClrUsed = 0; + bitmapInfo.bmiHeader.biClrImportant = 0; + + void* pixels = 0; + HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); + if (!bitmap) + return 0; + + HDC displayDC = ::GetDC(0); + HDC bitmapDC = ::CreateCompatibleDC(displayDC); + ::ReleaseDC(0, displayDC); + + ::SelectObject(bitmapDC, bitmap); + + // Fill our buffer with clear if we're going to alpha blend. + if (supportAlphaBlend) { + BITMAP bmpInfo; + GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); + int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; + memset(bmpInfo.bmBits, 0, bufferSize); + } + + // Make sure we can do world transforms. + SetGraphicsMode(bitmapDC, GM_ADVANCED); + + // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap. + XFORM xform; + xform.eM11 = 1.0f; + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = 1.0f; + xform.eDx = -dstRect.x(); + xform.eDy = -dstRect.y(); + ::SetWorldTransform(bitmapDC, &xform); + + + 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); + + if (hdc) { + + if (!dstRect.isEmpty()) { + + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); + BITMAP info; + GetObject(bitmap, sizeof(info), &info); + ASSERT(info.bmBitsPixel == 32); + + QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha); + m_data->p()->drawPixmap(dstRect, pixmap); + + ::DeleteObject(bitmap); + } + + ::DeleteDC(hdc); + } +} +#endif + +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) +{ +} + +InterpolationQuality GraphicsContext::imageInterpolationQuality() const +{ + return InterpolationDefault; +} + } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/graphics/qt/IconQt.cpp b/WebCore/platform/graphics/qt/IconQt.cpp index e39d161..b04668c 100644 --- a/WebCore/platform/graphics/qt/IconQt.cpp +++ b/WebCore/platform/graphics/qt/IconQt.cpp @@ -22,7 +22,6 @@ #include "Icon.h" #include "GraphicsContext.h" -#include "DeprecatedString.h" #include "PlatformString.h" #include "IntRect.h" #include "NotImplemented.h" @@ -42,11 +41,17 @@ Icon::~Icon() { } -PassRefPtr<Icon> Icon::newIconForFile(const String& filename) +PassRefPtr<Icon> Icon::createIconForFile(const String& filename) { - Icon *i = new Icon; + RefPtr<Icon> i = adoptRef(new Icon); i->m_icon = QIcon(filename); - return PassRefPtr<Icon>(i); + return i.release(); +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +{ + //FIXME: Implement this + return 0; } void Icon::paint(GraphicsContext* ctx, const IntRect& rect) diff --git a/WebCore/platform/graphics/qt/ImageBufferData.h b/WebCore/platform/graphics/qt/ImageBufferData.h new file mode 100644 index 0000000..222dabe --- /dev/null +++ b/WebCore/platform/graphics/qt/ImageBufferData.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageBufferData_h +#define ImageBufferData_h + +#include <QPainter> +#include <QPixmap> + +#include "OwnPtr.h" + +namespace WebCore { + +class IntSize; + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); + + QPixmap m_pixmap; + OwnPtr<QPainter> m_painter; +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index c95d8c8..d4ab59f 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Holger Hans Peter Freyther * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,47 +27,89 @@ #include "config.h" #include "ImageBuffer.h" +#include "CString.h" #include "GraphicsContext.h" +#include "ImageData.h" +#include "MIMETypeRegistry.h" +#include "NotImplemented.h" +#include "StillImageQt.h" +#include <QBuffer> +#include <QImageWriter> #include <QPainter> #include <QPixmap> namespace WebCore { -std::auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool grayScale) +ImageBufferData::ImageBufferData(const IntSize& size) + : m_pixmap(size) { - QPixmap px(size); - return std::auto_ptr<ImageBuffer>(new ImageBuffer(px)); + m_pixmap.fill(QColor(Qt::transparent)); + m_painter.set(new QPainter(&m_pixmap)); } -ImageBuffer::ImageBuffer(const QPixmap& px) - : m_pixmap(px), - m_painter(0) +ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) + : m_data(size) + , m_size(size) { - m_painter = new QPainter(&m_pixmap); - m_context.set(new GraphicsContext(m_painter)); + m_context.set(new GraphicsContext(m_data.m_painter.get())); + success = true; } ImageBuffer::~ImageBuffer() { - delete m_painter; } GraphicsContext* ImageBuffer::context() const { - if (!m_painter->isActive()) - m_painter->begin(&m_pixmap); + ASSERT(m_data.m_painter->isActive()); return m_context.get(); } -QPixmap* ImageBuffer::pixmap() const +Image* ImageBuffer::image() const { - if (!m_painter) - return &m_pixmap; - if (m_painter->isActive()) - m_painter->end(); - return &m_pixmap; + if (!m_image) { + // It's assumed that if image() is called, the actual rendering to the + // GraphicsContext must be done. + ASSERT(context()); + m_image = StillImage::create(m_data.m_pixmap); + } + + return m_image.get(); +} + +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect&) const +{ + notImplemented(); + return 0; +} + +void ImageBuffer::putImageData(ImageData*, const IntRect&, const IntPoint&) +{ + notImplemented(); +} + +// We get a mimeType here but QImageWriter does not support mimetypes but +// only formats (png, gif, jpeg..., xpm). So assume we get image/ as image +// mimetypes and then remove the image/ to get the Qt format. +String ImageBuffer::toDataURL(const String& mimeType) const +{ + ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); + + if (!mimeType.startsWith("image/")) + return "data:,"; + + // prepare our target + QByteArray data; + QBuffer buffer(&data); + buffer.open(QBuffer::WriteOnly); + + if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data())) + return "data:,"; + + buffer.close(); + return String::format("data:%s;base64,%s", mimeType.utf8().data(), data.toBase64().data()); } } diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp index 44ab0d3..e3b00a1 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Friedemann Kleint <fkleint@trolltech.com> - * Copyright (C) 2006 Trolltech ASA + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * All rights reserved. * @@ -206,7 +206,7 @@ void ImageDecoderQt::reset() void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived) { reset(); - ReadContext readContext(data, ReadContext::LoadIncrementally, m_imageList); + ReadContext readContext(data, ReadContext::LoadComplete, m_imageList); if (debugImageDecoderQt) qDebug() << " setData " << data.size() << " image bytes, complete=" << allDataReceived; diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.h b/WebCore/platform/graphics/qt/ImageDecoderQt.h index 32f91d1..3573dd0 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.h +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Friedemann Kleint <fkleint@trolltech.com> - * Copyright (C) 2006 Trolltech ASA + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/WebCore/platform/graphics/qt/ImageQt.cpp b/WebCore/platform/graphics/qt/ImageQt.cpp index 206aee3..9234c69 100644 --- a/WebCore/platform/graphics/qt/ImageQt.cpp +++ b/WebCore/platform/graphics/qt/ImageQt.cpp @@ -36,6 +36,7 @@ #include "GraphicsContext.h" #include "AffineTransform.h" #include "NotImplemented.h" +#include "StillImageQt.h" #include "qwebsettings.h" #include <QPixmap> @@ -53,17 +54,15 @@ // This function loads resources into WebKit static QPixmap loadResourcePixmap(const char *name) { - const QString resource = name; - QPixmap pixmap; - if (resource == "missingImage") + if (qstrcmp(name, "missingImage") == 0) pixmap = QWebSettings::webGraphic(QWebSettings::MissingImageGraphic); - else if (resource == "nullPlugin") + else if (qstrcmp(name, "nullPlugin") == 0) pixmap = QWebSettings::webGraphic(QWebSettings::MissingPluginGraphic); - else if (resource == "urlIcon") - pixmap = QWebSettings::webGraphic(QWebSettings::DefaultFaviconGraphic); - else if (resource == "textAreaResizeCorner") - pixmap = QWebSettings::webGraphic(QWebSettings::TextAreaResizeCornerGraphic); + else if (qstrcmp(name, "urlIcon") == 0) + pixmap = QWebSettings::webGraphic(QWebSettings::DefaultFrameIconGraphic); + else if (qstrcmp(name, "textAreaResizeCorner") == 0) + pixmap = QWebSettings::webGraphic(QWebSettings::TextAreaSizeGripCornerGraphic); return pixmap; } @@ -74,8 +73,9 @@ void FrameData::clear() { if (m_frame) { m_frame = 0; - m_duration = 0.0f; - m_hasAlpha = true; + // NOTE: We purposefully don't reset metadata here, so that even if we + // throw away previously-decoded data, animation loops can still access + // properties like frame durations without re-decoding. } } @@ -85,10 +85,9 @@ void FrameData::clear() // Image Class // ================================================ -Image* Image::loadPlatformResource(const char* name) +PassRefPtr<Image> Image::loadPlatformResource(const char* name) { - BitmapImage* img = new BitmapImage(loadResourcePixmap(name)); - return img; + return StillImage::create(loadResourcePixmap(name)); } @@ -98,39 +97,20 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const notImplemented(); } -BitmapImage::BitmapImage(const QPixmap &pixmap, ImageObserver *observer) - : Image(observer) - , m_currentFrame(0) - , m_frames(0) - , m_frameTimer(0) - , m_repetitionCount(0) - , m_repetitionsComplete(0) - , m_isSolidColor(false) - , m_animatingImageType(true) - , m_animationFinished(false) - , m_allDataReceived(false) - , m_haveSize(false) - , m_sizeAvailable(false) - , m_decodedSize(0) -{ - m_pixmap = new QPixmap(pixmap); -} - void BitmapImage::initPlatformData() { - m_pixmap = 0; } void BitmapImage::invalidatePlatformData() { - delete m_pixmap; - m_pixmap = 0; } // Drawing Routines void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatRect& src, CompositeOperator op) { + startAnimation(); + QPixmap* image = nativeImageForCurrentFrame(); if (!image) return; @@ -154,8 +134,6 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, painter->drawPixmap(dst, *image, src); ctxt->restore(); - - startAnimation(); } void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, @@ -171,23 +149,14 @@ void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, pixmap = pixmap.copy(tr); } - if (patternTransform.isIdentity()) { - ctxt->save(); - ctxt->setCompositeOperation(op); - QPainter* p = ctxt->platformContext(); - p->setBrushOrigin(phase); - p->drawTiledPixmap(destRect, pixmap); - ctxt->restore(); - } else { - QBrush b(pixmap); - b.setMatrix(patternTransform); - ctxt->save(); - ctxt->setCompositeOperation(op); - QPainter* p = ctxt->platformContext(); - p->setBrushOrigin(phase); - p->fillRect(destRect, b); - ctxt->restore(); - } + QBrush b(pixmap); + b.setMatrix(patternTransform); + ctxt->save(); + ctxt->setCompositeOperation(op); + QPainter* p = ctxt->platformContext(); + p->setBrushOrigin(phase); + p->fillRect(destRect, b); + ctxt->restore(); } void BitmapImage::checkForSolidColor() @@ -196,14 +165,6 @@ void BitmapImage::checkForSolidColor() m_isSolidColor = false; } -QPixmap* BitmapImage::getPixmap() const -{ - if (!m_pixmap) - return const_cast<BitmapImage*>(this)->frameAtIndex(0); - else - return m_pixmap; -} - } diff --git a/WebCore/platform/graphics/qt/ImageSourceQt.cpp b/WebCore/platform/graphics/qt/ImageSourceQt.cpp index 84d503f..1d14f9d 100644 --- a/WebCore/platform/graphics/qt/ImageSourceQt.cpp +++ b/WebCore/platform/graphics/qt/ImageSourceQt.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2006 Trolltech ASA + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * All rights reserved. * @@ -31,63 +31,29 @@ #include "ImageDecoderQt.h" #include "SharedBuffer.h" +#include <QBuffer> #include <QImage> -#include <qdebug.h> - +#include <QImageReader> namespace WebCore { - enum ImageFormat { ImageFormat_None, ImageFormat_GIF, ImageFormat_PNG, ImageFormat_JPEG, - ImageFormat_BMP, ImageFormat_ICO, ImageFormat_XBM }; - -ImageFormat detectImageFormat(const SharedBuffer& data) +static bool canHandleImage(const SharedBuffer& _data) { // 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 ImageFormat_None; - - 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 ImageFormat_GIF; - - // Test for PNG. - if (uContents[0] == 0x89 && - uContents[1] == 0x50 && - uContents[2] == 0x4E && - uContents[3] == 0x47) - return ImageFormat_PNG; - - // JPEG - if (uContents[0] == 0xFF && - uContents[1] == 0xD8 && - uContents[2] == 0xFF) - return ImageFormat_JPEG; - - // BMP - if (strncmp(contents, "BM", 2) == 0) - return ImageFormat_BMP; - - // 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 ImageFormat_ICO; - - // XBMs require 8 bytes of info. - if (length >= 8 && strncmp(contents, "#define ", 8) == 0) - return ImageFormat_XBM; - - // Give up. We don't know what the heck this is. - return ImageFormat_None; + 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 (detectImageFormat(data) != ImageFormat_None) - return new ImageDecoderQt(); - return 0; + if (!canHandleImage(data)) + return 0; + return new ImageDecoderQt(); } ImageSource::ImageSource() @@ -136,6 +102,11 @@ IntSize ImageSource::size() const return m_decoder->size(); } +IntSize ImageSource::frameSizeAtIndex(size_t) const +{ + return size(); +} + int ImageSource::repetitionCount() { if (!m_decoder) diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp new file mode 100644 index 0000000..431e68e --- /dev/null +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -0,0 +1,533 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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 "MediaPlayerPrivatePhonon.h" + +#include <limits> + +#include "CString.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "Widget.h" +#include <wtf/HashSet.h> + +#include <QDebug> +#include <QPainter> +#include <QWidget> +#include <QMetaEnum> +#include <QUrl> +#include <QEvent> +#include <phonon> + +using namespace Phonon; + +#define LOG_MEDIAOBJECT() (LOG(Media,"%s", debugMediaObject(this, *m_mediaObject).constData())) + +static QByteArray debugMediaObject(WebCore::MediaPlayerPrivate* mediaPlayer, const MediaObject& mediaObject) +{ + QByteArray byteArray; + QTextStream stream(&byteArray); + + const QMetaObject* metaObj = mediaPlayer->metaObject(); + QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState")); + + stream << "debugMediaObject -> Phonon::MediaObject("; + stream << "State: " << phononStates.valueToKey(mediaObject.state()); + stream << " | Current time: " << mediaObject.currentTime(); + stream << " | Remaining time: " << mediaObject.remainingTime(); + stream << " | Total time: " << mediaObject.totalTime(); + stream << " | Meta-data: "; + QMultiMap<QString, QString> map = mediaObject.metaData(); + for (QMap<QString, QString>::const_iterator it = map.constBegin(); + it != map.constEnd(); ++it) { + stream << "(" << it.key() << ", " << it.value() << ")"; + } + stream << " | Has video: " << mediaObject.hasVideo(); + stream << " | Is seekable: " << mediaObject.isSeekable(); + stream << ")"; + + stream.flush(); + + return byteArray; +} + +using namespace WTF; + +namespace WebCore { + +MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) + : m_player(player) + , m_networkState(MediaPlayer::Empty) + , m_readyState(MediaPlayer::DataUnavailable) + , m_mediaObject(new MediaObject()) + , m_videoWidget(new VideoWidget(0)) + , m_audioOutput(new AudioOutput()) + , m_isVisible(false) +{ + // Hint to Phonon to disable overlay painting + m_videoWidget->setAttribute(Qt::WA_DontShowOnScreen); + + createPath(m_mediaObject, m_videoWidget); + createPath(m_mediaObject, m_audioOutput); + + // Make sure we get updates for each frame + m_videoWidget->installEventFilter(this); + foreach(QWidget* widget, qFindChildren<QWidget*>(m_videoWidget)) { + widget->installEventFilter(this); + } + + 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))); + connect(m_mediaObject, SIGNAL(bufferStatus(int)), this, SLOT(bufferStatus(int))); + connect(m_mediaObject, SIGNAL(finished()), this, SLOT(finished())); + 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))); +} + +MediaPlayerPrivate::~MediaPlayerPrivate() +{ + LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting videowidget"); + m_videoWidget->close(); + delete m_videoWidget; + m_videoWidget = 0; + + LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting audiooutput"); + delete m_audioOutput; + m_audioOutput = 0; + + LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting mediaobject"); + delete m_mediaObject; + m_mediaObject = 0; +} + +void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&) +{ + notImplemented(); +} + +bool MediaPlayerPrivate::hasVideo() const +{ + bool hasVideo = m_mediaObject->hasVideo(); + LOG(Media, "MediaPlayerPrivatePhonon::hasVideo() -> %s", hasVideo ? "true" : "false"); + return hasVideo; +} + +void MediaPlayerPrivate::load(String url) +{ + LOG(Media, "MediaPlayerPrivatePhonon::load(\"%s\")", url.utf8().data()); + + // We are now loading + if (m_networkState != MediaPlayer::Loading) { + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + } + + // And we don't have any data yet + if (m_readyState != MediaPlayer::DataUnavailable) { + m_readyState = MediaPlayer::DataUnavailable; + m_player->readyStateChanged(); + } + + m_mediaObject->setCurrentSource(QUrl(url)); + m_audioOutput->setVolume(m_player->volume()); + setVisible(m_player->visible()); +} + +void MediaPlayerPrivate::cancelLoad() +{ + notImplemented(); +} + + +void MediaPlayerPrivate::play() +{ + LOG(Media, "MediaPlayerPrivatePhonon::play()"); + m_mediaObject->play(); +} + +void MediaPlayerPrivate::pause() +{ + LOG(Media, "MediaPlayerPrivatePhonon::pause()"); + m_mediaObject->pause(); +} + + +bool MediaPlayerPrivate::paused() const +{ + bool paused = m_mediaObject->state() == Phonon::PausedState; + LOG(Media, "MediaPlayerPrivatePhonon::paused() --> %s", paused ? "true" : "false"); + return paused; +} + +void MediaPlayerPrivate::seek(float position) +{ + LOG(Media, "MediaPlayerPrivatePhonon::seek(%f)", position); + + if (!m_mediaObject->isSeekable()) + return; + + if (position > duration()) + position = duration(); + + m_mediaObject->seek(position * 1000.0f); +} + +bool MediaPlayerPrivate::seeking() const +{ + return false; +} + +float MediaPlayerPrivate::duration() const +{ + if (m_networkState < MediaPlayer::LoadedMetaData) + return 0.0f; + + float duration = m_mediaObject->totalTime() / 1000.0f; + + if (duration == 0.0f) // We are streaming + duration = std::numeric_limits<float>::infinity(); + + LOG(Media, "MediaPlayerPrivatePhonon::duration() --> %f", duration); + return duration; +} + +float MediaPlayerPrivate::currentTime() const +{ + float currentTime = m_mediaObject->currentTime() / 1000.0f; + + LOG(Media, "MediaPlayerPrivatePhonon::currentTime() --> %f", currentTime); + return currentTime; +} + +void MediaPlayerPrivate::setEndTime(float endTime) +{ + notImplemented(); +} + +float MediaPlayerPrivate::maxTimeBuffered() const +{ + notImplemented(); + return 0.0f; +} + +float MediaPlayerPrivate::maxTimeSeekable() const +{ + notImplemented(); + return 0.0f; +} + +unsigned MediaPlayerPrivate::bytesLoaded() const +{ + notImplemented(); + return 0; +} + +bool MediaPlayerPrivate::totalBytesKnown() const +{ + //notImplemented(); + return false; +} + +unsigned MediaPlayerPrivate::totalBytes() const +{ + //notImplemented(); + return 0; +} + +void MediaPlayerPrivate::setRate(float) +{ + notImplemented(); +} + +void MediaPlayerPrivate::setVolume(float volume) +{ + LOG(Media, "MediaPlayerPrivatePhonon::setVolume()"); + m_audioOutput->setVolume(volume); +} + +void MediaPlayerPrivate::setMuted(bool muted) +{ + LOG(Media, "MediaPlayerPrivatePhonon::setMuted()"); + m_audioOutput->setMuted(muted); +} + + +int MediaPlayerPrivate::dataRate() const +{ + // This is not used at the moment + return 0; +} + + +MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const +{ + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState")); + LOG(Media, "MediaPlayerPrivatePhonon::networkState() --> %s", networkStates.valueToKey(m_networkState)); + return m_networkState; +} + +MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const +{ + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState")); + LOG(Media, "MediaPlayerPrivatePhonon::readyState() --> %s", readyStates.valueToKey(m_readyState)); + return m_readyState; +} + +void MediaPlayerPrivate::updateStates() +{ + MediaPlayer::NetworkState oldNetworkState = m_networkState; + MediaPlayer::ReadyState oldReadyState = m_readyState; + + Phonon::State phononState = m_mediaObject->state(); + + if (phononState == Phonon::StoppedState) { + if (oldNetworkState < MediaPlayer::LoadedMetaData) { + m_networkState = MediaPlayer::LoadedMetaData; + m_readyState = MediaPlayer::DataUnavailable; + m_mediaObject->pause(); + } + } else if (phononState == Phonon::PausedState) { + m_networkState = MediaPlayer::LoadedFirstFrame; + m_readyState = MediaPlayer::CanPlayThrough; + } else if (phononState == Phonon::ErrorState) { + if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) { + m_networkState = MediaPlayer::LoadFailed; + m_readyState = MediaPlayer::DataUnavailable; + cancelLoad(); + } else { + m_mediaObject->pause(); + } + } + + if (seeking()) + m_readyState = MediaPlayer::DataUnavailable; + + if (m_networkState != oldNetworkState) { + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState")); + LOG(Media, "Network state changed from '%s' to '%s'", + networkStates.valueToKey(oldNetworkState), + networkStates.valueToKey(m_networkState)); + m_player->networkStateChanged(); + } + + if (m_readyState != oldReadyState) { + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState")); + LOG(Media, "Ready state changed from '%s' to '%s'", + readyStates.valueToKey(oldReadyState), + readyStates.valueToKey(m_readyState)); + m_player->readyStateChanged(); + } +} + +void MediaPlayerPrivate::setVisible(bool visible) +{ + m_isVisible = visible; + LOG(Media, "MediaPlayerPrivatePhonon::setVisible(%s)", visible ? "true" : "false"); + + m_videoWidget->setVisible(m_isVisible); +} + +void MediaPlayerPrivate::setRect(const IntRect& newRect) +{ + if (!m_videoWidget) + return; + + LOG(Media, "MediaPlayerPrivatePhonon::setRect(%d,%d %dx%d)", + newRect.x(), newRect.y(), + newRect.width(), newRect.height()); + + QRect currentRect = m_videoWidget->rect(); + + if (newRect.width() != currentRect.width() || newRect.height() != currentRect.height()) + 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()) { + LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", + 0, 0); + return IntSize(); + } + + if (m_networkState < MediaPlayer::LoadedMetaData) { + LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", + 0, 0); + return IntSize(); + } + + QSize videoSize = m_videoWidget->sizeHint(); + IntSize naturalSize(videoSize.width(), videoSize.height()); + LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", + naturalSize.width(), naturalSize.height()); + return naturalSize; +} + +bool MediaPlayerPrivate::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == QEvent::Paint) + 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()) + return; + + if (!m_isVisible) + return; + + QPainter* painter = graphicsContect->platformContext(); + + painter->fillRect(rect, Qt::black); + + m_videoWidget->render(painter, QPoint(rect.x(), rect.y()), + QRegion(0, 0, rect.width(), rect.height())); +} + +// ====================== Phonon::MediaObject signals ====================== + +void MediaPlayerPrivate::stateChanged(Phonon::State newState, Phonon::State oldState) +{ + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState")); + LOG(Media, "MediaPlayerPrivatePhonon::stateChanged(newState=%s, oldState=%s)", + phononStates.valueToKey(newState), phononStates.valueToKey(oldState)); + + updateStates(); +} + +void MediaPlayerPrivate::tick(qint64) +{ + updateStates(); + m_player->timeChanged(); +} + +void MediaPlayerPrivate::metaDataChanged() +{ + LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()"); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivate::seekableChanged(bool) +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivate::hasVideoChanged(bool hasVideo) +{ + LOG(Media, "MediaPlayerPrivatePhonon::hasVideoChanged(%s)", hasVideo ? "true" : "false"); +} + +void MediaPlayerPrivate::bufferStatus(int) +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivate::finished() +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivate::currentSourceChanged(const Phonon::MediaSource&) +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivate::aboutToFinish() +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivate::prefinishMarkReached(qint32) +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivate::totalTimeChanged(qint64 totalTime) +{ + LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%d)", totalTime); + LOG_MEDIAOBJECT(); +} + +} // namespace WebCore + +#include "moc_MediaPlayerPrivatePhonon.cpp" diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h new file mode 100644 index 0000000..5eb2a09 --- /dev/null +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h @@ -0,0 +1,159 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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 MediaPlayerPrivatePhonon_h +#define MediaPlayerPrivatePhonon_h + +#include "MediaPlayer.h" +#include <wtf/Noncopyable.h> + +#include <QObject> +#include <phononnamespace.h> + +QT_BEGIN_NAMESPACE +class QWidget; +class QUrl; + +namespace Phonon { + class MediaObject; + class VideoWidget; + class AudioOutput; + class MediaSource; +} +QT_END_NAMESPACE + +namespace WebCore { + + class MediaPlayerPrivate : public QObject, Noncopyable { + + Q_OBJECT + + public: + MediaPlayerPrivate(MediaPlayer*); + ~MediaPlayerPrivate(); + + // These enums are used for debugging + Q_ENUMS(ReadyState NetworkState PhononState) + + enum ReadyState { + DataUnavailable, + CanShowCurrentFrame, + CanPlay, + CanPlayThrough + }; + + enum NetworkState { + Empty, + LoadFailed, + Loading, + LoadedMetaData, + LoadedFirstFrame, + Loaded + }; + + enum PhononState { + LoadingState, + StoppedState, + PlayingState, + BufferingState, + PausedState, + ErrorState + }; + + IntSize naturalSize() const; + bool hasVideo() const; + + void load(String url); + void cancelLoad(); + + void play(); + void pause(); + + bool paused() const; + bool seeking() const; + + float duration() const; + float currentTime() const; + void seek(float); + void setEndTime(float); + + void setRate(float); + void setVolume(float); + void setMuted(bool); + + 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 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; } + + protected: + bool eventFilter(QObject*, QEvent*); + + private slots: + void stateChanged(Phonon::State, Phonon::State); + void tick(qint64); + void metaDataChanged(); + void seekableChanged(bool); + void hasVideoChanged(bool); + void bufferStatus(int); + void finished(); + void currentSourceChanged(const Phonon::MediaSource&); + void aboutToFinish(); + void prefinishMarkReached(qint32); + void totalTimeChanged(qint64); + + private: + void updateStates(); + + MediaPlayer* m_player; + + MediaPlayer::NetworkState m_networkState; + MediaPlayer::ReadyState m_readyState; + + Phonon::MediaObject* m_mediaObject; + Phonon::VideoWidget* m_videoWidget; + Phonon::AudioOutput* m_audioOutput; + + bool m_isVisible; + }; +} + +#endif // MediaPlayerPrivatePhonon_h diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index 240350f..76f375c 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -155,7 +155,7 @@ void Path::addArc(const FloatPoint& p, float r, float sar, float ear, bool antic // this is also due to switched coordinate system // we would end up with a 0 span instead of 360 - if (!(qFuzzyCompare(span + (ea - sa), 0.0) && + if (!(qFuzzyCompare(span + (ea - sa) + 1, 1.0) && qFuzzyCompare(qAbs(span), 360.0))) { span += ea - sa; } @@ -194,10 +194,10 @@ String Path::debugString() const switch (cur.type) { case QPainterPath::MoveToElement: - ret += QString("M %1 %2").arg(cur.x).arg(cur.y); + ret += QString(QLatin1String("M %1 %2")).arg(cur.x).arg(cur.y); break; case QPainterPath::LineToElement: - ret += QString("L %1 %2").arg(cur.x).arg(cur.y); + ret += QString(QLatin1String("L %1 %2")).arg(cur.x).arg(cur.y); break; case QPainterPath::CurveToElement: { @@ -207,7 +207,7 @@ String Path::debugString() const Q_ASSERT(c1.type == QPainterPath::CurveToDataElement); Q_ASSERT(c2.type == QPainterPath::CurveToDataElement); - ret += QString("C %1 %2 %3 %4 %5 %6").arg(cur.x).arg(cur.y).arg(c1.x).arg(c1.y).arg(c2.x).arg(c2.y); + ret += QString(QLatin1String("C %1 %2 %3 %4 %5 %6")).arg(cur.x).arg(cur.y).arg(c1.x).arg(c1.y).arg(c2.x).arg(c2.y); i += 2; break; diff --git a/WebCore/platform/graphics/qt/PatternQt.cpp b/WebCore/platform/graphics/qt/PatternQt.cpp new file mode 100644 index 0000000..883a258 --- /dev/null +++ b/WebCore/platform/graphics/qt/PatternQt.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Pattern.h" + +#include "AffineTransform.h" +#include "GraphicsContext.h" + +namespace WebCore { + +QBrush Pattern::createPlatformPattern(const AffineTransform& transform) const +{ + QPixmap* pixmap = tileImage()->nativeImageForCurrentFrame(); + if (!pixmap) + return QBrush(); + + QBrush brush(*pixmap); + brush.setMatrix(transform); + + return brush; +} + +} diff --git a/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp b/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp index 1a45ce4..1ffce33 100644 --- a/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp +++ b/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Trolltech ASA + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -22,10 +22,14 @@ #include "config.h" #include "SimpleFontData.h" +#include "SVGFontData.h" + namespace WebCore { SimpleFontData::SimpleFontData(const FontPlatformData& font, bool customFont, bool loading, SVGFontData*) - : m_font(font), m_isCustomFont(customFont), m_isLoading(loading) + : m_font(font) + , m_isCustomFont(customFont) + , m_isLoading(loading) { } diff --git a/WebCore/platform/graphics/qt/StillImageQt.cpp b/WebCore/platform/graphics/qt/StillImageQt.cpp new file mode 100644 index 0000000..95b3bc8 --- /dev/null +++ b/WebCore/platform/graphics/qt/StillImageQt.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * 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 "StillImageQt.h" + +#include "GraphicsContext.h" +#include "IntSize.h" + +#include <QPainter> + +namespace WebCore { + +StillImage::StillImage(const QPixmap& pixmap) + : m_pixmap(pixmap) +{} + +IntSize StillImage::size() const +{ + return IntSize(m_pixmap.width(), m_pixmap.height()); +} + +NativeImagePtr StillImage::nativeImageForCurrentFrame() +{ + return const_cast<NativeImagePtr>(&m_pixmap); +} + +void StillImage::draw(GraphicsContext* ctxt, const FloatRect& dst, + const FloatRect& src, CompositeOperator op) +{ + if (m_pixmap.isNull()) + return; + + ctxt->save(); + ctxt->setCompositeOperation(op); + QPainter* painter(ctxt->platformContext()); + painter->drawPixmap(dst, m_pixmap, src); + ctxt->restore(); +} + +} diff --git a/WebCore/platform/graphics/qt/StillImageQt.h b/WebCore/platform/graphics/qt/StillImageQt.h new file mode 100644 index 0000000..37b8b2c --- /dev/null +++ b/WebCore/platform/graphics/qt/StillImageQt.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef StillImageQt_h +#define StillImageQt_h + +#include "Image.h" + +namespace WebCore { + + class StillImage : public Image { + public: + static PassRefPtr<StillImage> create(const QPixmap& pixmap) + { + return adoptRef(new StillImage(pixmap)); + } + + // 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 unsigned decodedSize() const { return 0; } + + virtual IntSize size() const; + virtual NativeImagePtr nativeImageForCurrentFrame(); + virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); + + private: + StillImage(const QPixmap& pixmap); + + QPixmap m_pixmap; + }; + +} + +#endif diff --git a/WebCore/platform/graphics/win/ColorSafari.cpp b/WebCore/platform/graphics/win/ColorSafari.cpp index e7fbf47..a04fd81 100644 --- a/WebCore/platform/graphics/win/ColorSafari.cpp +++ b/WebCore/platform/graphics/win/ColorSafari.cpp @@ -68,9 +68,4 @@ Color focusRingColor() return focusRingColor; } -void setFocusRingColorChangeFunction(void (*)()) -{ - notImplemented(); -} - } // namespace WebCore diff --git a/WebCore/platform/graphics/win/FontCGWin.cpp b/WebCore/platform/graphics/win/FontCGWin.cpp index 8b59a48..1766cd9 100644 --- a/WebCore/platform/graphics/win/FontCGWin.cpp +++ b/WebCore/platform/graphics/win/FontCGWin.cpp @@ -26,11 +26,14 @@ #include "config.h" #include "Font.h" +#include "AffineTransform.h" +#include "FloatConversion.h" #include "GlyphBuffer.h" #include "GraphicsContext.h" #include "IntRect.h" #include "SimpleFontData.h" #include "UniscribeController.h" +#include "WebCoreTextRenderer.h" #include <ApplicationServices/ApplicationServices.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> #include <wtf/MathExtras.h> @@ -39,59 +42,266 @@ namespace WebCore { const int syntheticObliqueAngle = 14; -void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, - int from, int numGlyphs, const FloatPoint& point) const +static inline CGFloat toCGFloat(FIXED f) { - if (font->m_font.useGDI()) { - // FIXME: Support alpha blending. - // FIXME: Support text stroke/fill. - // FIXME: Support text shadow. - Color fillColor = graphicsContext->fillColor(); - if (fillColor.alpha() == 0) + return f.value + f.fract / CGFloat(65536.0); +} + +static CGPathRef createPathForGlyph(HDC hdc, Glyph glyph) +{ + CGMutablePathRef path = CGPathCreateMutable(); + + static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; + GLYPHMETRICS glyphMetrics; + // GGO_NATIVE matches the outline perfectly when Windows font smoothing is off. + // GGO_NATIVE | GGO_UNHINTED does not match perfectly either when Windows font smoothing is on or off. + DWORD outlineLength = GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, 0, 0, &identity); + ASSERT(outlineLength >= 0); + if (outlineLength < 0) + return path; + + Vector<UInt8> outline(outlineLength); + GetGlyphOutline(hdc, glyph, GGO_GLYPH_INDEX | GGO_NATIVE, &glyphMetrics, outlineLength, outline.data(), &identity); + + unsigned offset = 0; + while (offset < outlineLength) { + LPTTPOLYGONHEADER subpath = reinterpret_cast<LPTTPOLYGONHEADER>(outline.data() + offset); + ASSERT(subpath->dwType == TT_POLYGON_TYPE); + if (subpath->dwType != TT_POLYGON_TYPE) + return path; + + CGPathMoveToPoint(path, 0, toCGFloat(subpath->pfxStart.x), toCGFloat(subpath->pfxStart.y)); + + unsigned subpathOffset = sizeof(*subpath); + while (subpathOffset < subpath->cb) { + LPTTPOLYCURVE segment = reinterpret_cast<LPTTPOLYCURVE>(reinterpret_cast<UInt8*>(subpath) + subpathOffset); + switch (segment->wType) { + case TT_PRIM_LINE: + for (unsigned i = 0; i < segment->cpfx; i++) + CGPathAddLineToPoint(path, 0, toCGFloat(segment->apfx[i].x), toCGFloat(segment->apfx[i].y)); + break; + + case TT_PRIM_QSPLINE: + for (unsigned i = 0; i < segment->cpfx; i++) { + CGFloat x = toCGFloat(segment->apfx[i].x); + CGFloat y = toCGFloat(segment->apfx[i].y); + CGFloat cpx; + CGFloat cpy; + + if (i == segment->cpfx - 2) { + cpx = toCGFloat(segment->apfx[i + 1].x); + cpy = toCGFloat(segment->apfx[i + 1].y); + i++; + } else { + cpx = (toCGFloat(segment->apfx[i].x) + toCGFloat(segment->apfx[i + 1].x)) / 2; + cpy = (toCGFloat(segment->apfx[i].y) + toCGFloat(segment->apfx[i + 1].y)) / 2; + } + + CGPathAddQuadCurveToPoint(path, 0, x, y, cpx, cpy); + } + break; + + case TT_PRIM_CSPLINE: + for (unsigned i = 0; i < segment->cpfx; i += 3) { + CGFloat cp1x = toCGFloat(segment->apfx[i].x); + CGFloat cp1y = toCGFloat(segment->apfx[i].y); + CGFloat cp2x = toCGFloat(segment->apfx[i + 1].x); + CGFloat cp2y = toCGFloat(segment->apfx[i + 1].y); + CGFloat x = toCGFloat(segment->apfx[i + 2].x); + CGFloat y = toCGFloat(segment->apfx[i + 2].y); + + CGPathAddCurveToPoint(path, 0, cp1x, cp1y, cp2x, cp2y, x, y); + } + break; + + default: + ASSERT_NOT_REACHED(); + return path; + } + + subpathOffset += sizeof(*segment) + (segment->cpfx - 1) * sizeof(segment->apfx[0]); + } + CGPathCloseSubpath(path); + offset += subpath->cb; + } + return path; +} + +static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, const FloatPoint& point) +{ + Color fillColor = graphicsContext->fillColor(); + + bool drawIntoBitmap = false; + int drawingMode = graphicsContext->textDrawingMode(); + if (drawingMode == cTextFill) { + if (!fillColor.alpha()) return; - // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances. - Vector<int, 2048> gdiAdvances; - int totalWidth = 0; - for (int i = 0; i < numGlyphs; i++) { - gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i))); - totalWidth += gdiAdvances[i]; + drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer(); + if (!drawIntoBitmap) { + IntSize size; + int blur; + Color color; + graphicsContext->getShadow(size, blur, color); + drawIntoBitmap = !size.isEmpty() || blur; } + } + + // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances. + Vector<int, 2048> gdiAdvances; + int totalWidth = 0; + for (int i = 0; i < numGlyphs; i++) { + gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i))); + totalWidth += gdiAdvances[i]; + } + HDC hdc = 0; + OwnPtr<GraphicsContext::WindowsBitmap> bitmap; + IntRect textRect; + if (!drawIntoBitmap) + hdc = graphicsContext->getWindowsContext(textRect, true, false); + if (!hdc) { + drawIntoBitmap = true; // We put slop into this rect, since glyphs can overflow the ascent/descent bounds and the left/right edges. + // FIXME: Can get glyphs' optical bounds (even from CG) to get this right. int lineGap = font->lineGap(); - IntRect textRect(point.x() - lineGap, point.y() - font->ascent() - lineGap, totalWidth + 2 * lineGap, font->lineSpacing()); - HDC hdc = graphicsContext->getWindowsContext(textRect); - SelectObject(hdc, font->m_font.hfont()); + textRect = IntRect(point.x() - (font->ascent() + font->descent()) / 2, point.y() - font->ascent() - lineGap, totalWidth + font->ascent() + font->descent(), font->lineSpacing()); + bitmap.set(graphicsContext->createWindowsBitmap(textRect.size())); + memset(bitmap->buffer(), 255, bitmap->bufferLength()); + hdc = bitmap->hdc(); - // Set the correct color. - HDC textDrawingDC = hdc; + XFORM xform; + xform.eM11 = 1.0f; + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = 1.0f; + xform.eDx = -textRect.x(); + xform.eDy = -textRect.y(); + SetWorldTransform(hdc, &xform); + } + + SelectObject(hdc, font->m_font.hfont()); + + // Set the correct color. + if (drawIntoBitmap) + SetTextColor(hdc, RGB(0, 0, 0)); + else SetTextColor(hdc, RGB(fillColor.red(), fillColor.green(), fillColor.blue())); - SetBkMode(hdc, TRANSPARENT); - SetTextAlign(hdc, TA_LEFT | TA_BASELINE); + SetBkMode(hdc, TRANSPARENT); + SetTextAlign(hdc, TA_LEFT | TA_BASELINE); - // Uniscribe gives us offsets to help refine the positioning of combining glyphs. - FloatSize translation = glyphBuffer.offsetAt(from); - if (translation.width() || translation.height()) { - XFORM xform; - xform.eM11 = 1.0; - xform.eM12 = 0; + // Uniscribe gives us offsets to help refine the positioning of combining glyphs. + FloatSize translation = glyphBuffer.offsetAt(from); + if (translation.width() || translation.height()) { + XFORM xform; + xform.eM11 = 1.0; + xform.eM12 = 0; + xform.eM21 = 0; + xform.eM22 = 1.0; + xform.eDx = translation.width(); + xform.eDy = translation.height(); + ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); + } + + if (drawingMode == cTextFill) { + XFORM xform; + xform.eM11 = 1.0; + xform.eM12 = 0; + xform.eM21 = font->platformData().syntheticOblique() ? -tanf(syntheticObliqueAngle * piFloat / 180.0f) : 0; + xform.eM22 = 1.0; + xform.eDx = point.x(); + xform.eDy = point.y(); + ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); + ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); + if (font->m_syntheticBoldOffset) { xform.eM21 = 0; - xform.eM22 = 1.0; - xform.eDx = translation.width(); - xform.eDy = translation.height(); + xform.eDx = font->m_syntheticBoldOffset; + xform.eDy = 0; ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); + ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); + } + } else { + RetainPtr<CGMutablePathRef> path(AdoptCF, CGPathCreateMutable()); + + XFORM xform; + GetWorldTransform(hdc, &xform); + AffineTransform 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)); + initialGlyphTransform.tx = 0; + initialGlyphTransform.ty = 0; + CGAffineTransform glyphTranslation = CGAffineTransformIdentity; + + for (unsigned i = 0; i < numGlyphs; ++i) { + RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); + CGAffineTransform glyphTransform = CGAffineTransformConcat(initialGlyphTransform, glyphTranslation); + CGPathAddPath(path.get(), &glyphTransform, glyphPath.get()); + glyphTranslation = CGAffineTransformTranslate(glyphTranslation, gdiAdvances[i], 0); + } + + CGContextRef cgContext = graphicsContext->platformContext(); + CGContextSaveGState(cgContext); + + BOOL fontSmoothingEnabled = false; + SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0); + CGContextSetShouldAntialias(cgContext, fontSmoothingEnabled); + + CGContextScaleCTM(cgContext, 1.0, -1.0); + CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height())); + + if (drawingMode & cTextFill) { + CGContextAddPath(cgContext, path.get()); + CGContextFillPath(cgContext); + if (font->m_syntheticBoldOffset) { + CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); + CGContextAddPath(cgContext, path.get()); + CGContextFillPath(cgContext); + CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + } + } + if (drawingMode & cTextStroke) { + CGContextAddPath(cgContext, path.get()); + CGContextStrokePath(cgContext); + if (font->m_syntheticBoldOffset) { + CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); + CGContextAddPath(cgContext, path.get()); + CGContextStrokePath(cgContext); + CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + } } - ExtTextOut(hdc, point.x(), point.y(), ETO_GLYPH_INDEX, 0, (WCHAR*)glyphBuffer.glyphs(from), numGlyphs, gdiAdvances.data()); + CGContextRestoreGState(cgContext); + } + + if (drawIntoBitmap) { + UInt8* buffer = bitmap->buffer(); + unsigned bufferLength = bitmap->bufferLength(); + for (unsigned i = 0; i < bufferLength; i += 4) { + // Use green, which is always in the middle. + UInt8 alpha = (255 - buffer[i + 1]) * fillColor.alpha() / 255; + buffer[i] = fillColor.blue(); + buffer[i + 1] = fillColor.green(); + buffer[i + 2] = fillColor.red(); + buffer[i + 3] = alpha; + } + graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.topLeft()); + } else + graphicsContext->releaseWindowsContext(hdc, textRect, true, false); +} - graphicsContext->releaseWindowsContext(hdc, textRect); +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(); - uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext); + uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, WebCoreShouldUseFontSmoothing()); const FontPlatformData& platformData = font->platformData(); @@ -102,25 +312,49 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo matrix.d = -matrix.d; if (platformData.syntheticOblique()) { - static float skew = -tanf(syntheticObliqueAngle * acosf(0) / 90.0f); + static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } + CGContextSetTextMatrix(cgContext, matrix); + // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); - if (translation.width() || translation.height()) - CGAffineTransformTranslate(matrix, translation.width(), translation.height()); - - CGContextSetTextMatrix(cgContext, matrix); CGContextSetFontSize(cgContext, platformData.size()); - CGContextSetTextPosition(cgContext, point.x(), point.y()); + wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false); + + IntSize shadowSize; + int shadowBlur; + Color shadowColor; + graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor); + + bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; + if (hasSimpleShadow) { + // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. + graphicsContext->clearShadow(); + Color fillColor = graphicsContext->fillColor(); + Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); + graphicsContext->setFillColor(shadowFillColor); + CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width(), point.y() + translation.height() + shadowSize.height()); + CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + if (font->m_syntheticBoldOffset) { + CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width() + font->m_syntheticBoldOffset, point.y() + translation.height() + shadowSize.height()); + CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + } + graphicsContext->setFillColor(fillColor); + } + + CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->m_syntheticBoldOffset) { - CGContextSetTextPosition(cgContext, point.x() + font->m_syntheticBoldOffset, point.y()); + CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->m_syntheticBoldOffset, point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } + if (hasSimpleShadow) + graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor); + wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); } diff --git a/WebCore/platform/graphics/win/FontCacheWin.cpp b/WebCore/platform/graphics/win/FontCacheWin.cpp index 502863b..49b3d76 100644 --- a/WebCore/platform/graphics/win/FontCacheWin.cpp +++ b/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 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 @@ -35,8 +35,10 @@ #include "UnicodeRange.h" #include <windows.h> #include <mlang.h> +#if PLATFORM(CG) #include <ApplicationServices/ApplicationServices.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> +#endif using std::min; @@ -45,7 +47,9 @@ namespace WebCore void FontCache::platformInit() { +#if PLATFORM(CG) wkSetUpFontCache(1536 * 1024 * 4); // This size matches Mac. +#endif } IMLangFontLink2* FontCache::getFontLinkInterface() @@ -304,55 +308,169 @@ FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fo return getCachedFontPlatformData(fontDescription, timesStr); } -bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family) +static LONG toGDIFontWeight(FontWeight fontWeight) { - LOGFONT winfont; - - // The size here looks unusual. The negative number is intentional. The logical size constant is 32. - winfont.lfHeight = -fontDescription.computedPixelSize() * 32; - winfont.lfWidth = 0; - winfont.lfEscapement = 0; - winfont.lfOrientation = 0; - winfont.lfUnderline = false; - winfont.lfStrikeOut = false; - winfont.lfCharSet = DEFAULT_CHARSET; -#if PLATFORM(CG) - winfont.lfOutPrecision = OUT_TT_ONLY_PRECIS; + 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]; +} + +static inline bool isGDIFontWeightBold(LONG gdiFontWeight) +{ + return gdiFontWeight >= FW_SEMIBOLD; +} + +static LONG adjustedGDIFontWeight(LONG gdiFontWeight, const String& family) +{ + static AtomicString lucidaStr("Lucida Grande"); + if (equalIgnoringCase(family, lucidaStr)) { + if (gdiFontWeight == FW_NORMAL) + return FW_MEDIUM; + if (gdiFontWeight == FW_BOLD) + return FW_SEMIBOLD; + } + return gdiFontWeight; +} + +struct MatchImprovingProcData { + MatchImprovingProcData(LONG desiredWeight, bool desiredItalic) + : m_desiredWeight(desiredWeight) + , m_desiredItalic(desiredItalic) + , m_hasMatched(false) + { + } + + LONG m_desiredWeight; + bool m_desiredItalic; + bool m_hasMatched; + LOGFONT m_chosen; +}; + +static int CALLBACK matchImprovingEnumProc(CONST LOGFONT* candidate, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) +{ + MatchImprovingProcData* matchData = reinterpret_cast<MatchImprovingProcData*>(lParam); + + if (!matchData->m_hasMatched) { + matchData->m_hasMatched = true; + matchData->m_chosen = *candidate; + return 1; + } + + if (!candidate->lfItalic != !matchData->m_chosen.lfItalic) { + if (!candidate->lfItalic == !matchData->m_desiredItalic) + matchData->m_chosen = *candidate; + + return 1; + } + + unsigned chosenWeightDeltaMagnitude = abs(matchData->m_chosen.lfWeight - matchData->m_desiredWeight); + unsigned candidateWeightDeltaMagnitude = abs(candidate->lfWeight - matchData->m_desiredWeight); + + // If both are the same distance from the desired weight, prefer the candidate if it is further from regular. + if (chosenWeightDeltaMagnitude == candidateWeightDeltaMagnitude && abs(candidate->lfWeight - FW_NORMAL) > abs(matchData->m_chosen.lfWeight - FW_NORMAL)) { + matchData->m_chosen = *candidate; + return 1; + } + + // Otherwise, prefer the one closer to the desired weight. + if (candidateWeightDeltaMagnitude < chosenWeightDeltaMagnitude) + matchData->m_chosen = *candidate; + + return 1; +} + +static HFONT createGDIFont(const AtomicString& family, LONG desiredWeight, bool desiredItalic, int size) +{ + HDC hdc = GetDC(0); + + LOGFONT logFont; + logFont.lfCharSet = DEFAULT_CHARSET; + unsigned familyLength = min(family.length(), static_cast<unsigned>(LF_FACESIZE - 1)); + memcpy(logFont.lfFaceName, family.characters(), familyLength * sizeof(UChar)); + logFont.lfFaceName[familyLength] = 0; + logFont.lfPitchAndFamily = 0; + + MatchImprovingProcData matchData(desiredWeight, desiredItalic); + EnumFontFamiliesEx(hdc, &logFont, matchImprovingEnumProc, reinterpret_cast<LPARAM>(&matchData), 0); + + ReleaseDC(0, hdc); + + if (!matchData.m_hasMatched) + return 0; + + matchData.m_chosen.lfHeight = -size; + matchData.m_chosen.lfWidth = 0; + matchData.m_chosen.lfEscapement = 0; + matchData.m_chosen.lfOrientation = 0; + matchData.m_chosen.lfUnderline = false; + matchData.m_chosen.lfStrikeOut = false; + matchData.m_chosen.lfCharSet = DEFAULT_CHARSET; +#if PLATFORM(CG) || PLATFORM(CAIRO) + matchData.m_chosen.lfOutPrecision = OUT_TT_ONLY_PRECIS; #else - winfont.lfOutPrecision = OUT_TT_PRECIS; + matchData.m_chosen.lfOutPrecision = OUT_TT_PRECIS; #endif - winfont.lfQuality = 5; // Force cleartype. - winfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - winfont.lfItalic = fontDescription.italic(); - - // FIXME: Support weights for real. Do our own enumeration of the available weights. - // We can't rely on Windows here, since we need to follow the CSS2 algorithm for how to fill in - // gaps in the weight list. - // FIXME: Hardcoding Lucida Grande for now. It uses different weights than typical Win32 fonts - // (500/600 instead of 400/700). - static AtomicString lucidaStr("Lucida Grande"); - if (equalIgnoringCase(family, lucidaStr)) - winfont.lfWeight = fontDescription.bold() ? 600 : 500; - else - winfont.lfWeight = fontDescription.bold() ? 700 : 400; - int len = min(family.length(), (unsigned int)LF_FACESIZE - 1); - memcpy(winfont.lfFaceName, family.characters(), len * sizeof(WORD)); - winfont.lfFaceName[len] = '\0'; - - HFONT hfont = CreateFontIndirect(&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. - HDC dc = GetDC(0); - SaveDC(dc); - SelectObject(dc, hfont); - WCHAR name[LF_FACESIZE]; - GetTextFace(dc, LF_FACESIZE, name); - RestoreDC(dc, -1); - ReleaseDC(0, dc); - - DeleteObject(hfont); - - return !wcsicmp(winfont.lfFaceName, name); + matchData.m_chosen.lfQuality = DEFAULT_QUALITY; + matchData.m_chosen.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + return CreateFontIndirect(&matchData.m_chosen); +} + +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 = adjustedGDIFontWeight(logFont->lfWeight, procData->m_familyName); + 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) @@ -364,57 +482,34 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD bool useGDI = fontDescription.renderingMode() == AlternateRenderingMode && !isLucidaGrande; - LOGFONT winfont; - - // The size here looks unusual. The negative number is intentional. The logical size constant is 32. We do this - // for subpixel precision when rendering using Uniscribe. This masks rounding errors related to the HFONT metrics being - // different from the CGFont metrics. - // FIXME: We will eventually want subpixel precision for GDI mode, but the scaled rendering doesn't look as nice. That may be solvable though. - winfont.lfHeight = -fontDescription.computedPixelSize() * (useGDI ? 1 : 32); - 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 = DEFAULT_QUALITY; - winfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - winfont.lfItalic = fontDescription.italic(); - - // FIXME: Support weights for real. Do our own enumeration of the available weights. - // We can't rely on Windows here, since we need to follow the CSS2 algorithm for how to fill in - // gaps in the weight list. - // FIXME: Hardcoding Lucida Grande for now. It uses different weights than typical Win32 fonts - // (500/600 instead of 400/700). - if (isLucidaGrande) { - winfont.lfWeight = fontDescription.bold() ? 600 : 500; - useGDI = false; // Never use GDI for Lucida Grande. - } else - winfont.lfWeight = fontDescription.bold() ? 700 : 400; - int len = min(family.length(), (unsigned int)LF_FACESIZE - 1); - memcpy(winfont.lfFaceName, family.characters(), len * sizeof(WORD)); - winfont.lfFaceName[len] = '\0'; - - HFONT hfont = CreateFontIndirect(&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. - HDC dc = GetDC(0); - SaveDC(dc); - SelectObject(dc, hfont); - WCHAR name[LF_FACESIZE]; - GetTextFace(dc, LF_FACESIZE, name); - RestoreDC(dc, -1); - ReleaseDC(0, dc); - - if (_wcsicmp(winfont.lfFaceName, name)) { - DeleteObject(hfont); + // The logical size constant is 32. We do this for subpixel precision when rendering using Uniscribe. + // This masks rounding errors related to the HFONT metrics being different from the CGFont metrics. + // FIXME: We will eventually want subpixel precision for GDI mode, but the scaled rendering doesn't + // look as nice. That may be solvable though. + LONG weight = adjustedGDIFontWeight(toGDIFontWeight(fontDescription.weight()), family); + HFONT hfont = createGDIFont(family, weight, fontDescription.italic(), fontDescription.computedPixelSize() * (useGDI ? 1 : 32)); + + if (!hfont) return 0; - } - - FontPlatformData* result = new FontPlatformData(hfont, fontDescription.computedPixelSize(), - fontDescription.bold(), fontDescription.italic(), useGDI); - if (!result->cgFont()) { + + if (isLucidaGrande) + useGDI = false; // Never use GDI for Lucida Grande. + + LOGFONT logFont; + GetObject(hfont, sizeof(LOGFONT), &logFont); + + bool synthesizeBold = isGDIFontWeightBold(weight) && !isGDIFontWeightBold(logFont.lfWeight); + bool synthesizeItalic = fontDescription.italic() && !logFont.lfItalic; + + FontPlatformData* result = new FontPlatformData(hfont, fontDescription.computedPixelSize(), synthesizeBold, synthesizeItalic, useGDI); + +#if PLATFORM(CG) + bool fontCreationFailed = !result->cgFont(); +#elif PLATFORM(CAIRO) + bool fontCreationFailed = !result->fontFace(); +#endif + + if (fontCreationFailed) { // The creation of the CGFontRef failed for some reason. We already asserted in debug builds, but to make // absolutely sure that we don't use this font, go ahead and return 0 so that we can fall back to the next // font. diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp index cf03502..26fceba 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (C) 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 @@ -21,21 +21,72 @@ #include "config.h" #include "FontCustomPlatformData.h" -#include <wtf/RetainPtr.h> -#include <ApplicationServices/ApplicationServices.h> -#include "SharedBuffer.h" +#include "Base64.h" #include "FontPlatformData.h" +#include "OpenTypeUtilities.h" +#include "SharedBuffer.h" +#include "SoftLinking.h" +#include <ApplicationServices/ApplicationServices.h> +#include <wtf/RetainPtr.h> + +// From t2embapi.h, which is missing from the Microsoft Platform SDK. +typedef unsigned long(WINAPIV *READEMBEDPROC) (void*, void*, unsigned long); +struct TTLOADINFO; +#define TTLOAD_PRIVATE 0x00000001 +#define LICENSE_PREVIEWPRINT 0x0004 +#define E_NONE 0x0000L namespace WebCore { +using namespace std; + +SOFT_LINK_LIBRARY(T2embed); +SOFT_LINK(T2embed, TTLoadEmbeddedFont, LONG, __stdcall, (HANDLE* phFontReference, ULONG ulFlags, ULONG* pulPrivStatus, ULONG ulPrivs, ULONG* pulStatus, READEMBEDPROC lpfnReadFromStream, LPVOID lpvReadStream, LPWSTR szWinFamilyName, LPSTR szMacFamilyName, TTLOADINFO* pTTLoadInfo), (phFontReference, ulFlags,pulPrivStatus, ulPrivs, pulStatus, lpfnReadFromStream, lpvReadStream, szWinFamilyName, szMacFamilyName, pTTLoadInfo)); +SOFT_LINK(T2embed, TTGetNewFontName, LONG, __stdcall, (HANDLE* phFontReference, LPWSTR szWinFamilyName, long cchMaxWinName, LPSTR szMacFamilyName, long cchMaxMacName), (phFontReference, szWinFamilyName, cchMaxWinName, szMacFamilyName, cchMaxMacName)); +SOFT_LINK(T2embed, TTDeleteEmbeddedFont, LONG, __stdcall, (HANDLE hFontReference, ULONG ulFlags, ULONG* pulStatus), (hFontReference, ulFlags, pulStatus)); + FontCustomPlatformData::~FontCustomPlatformData() { CGFontRelease(m_cgFont); + if (m_fontReference) { + if (m_name.isNull()) { + ASSERT(T2embedLibrary()); + ULONG status; + TTDeleteEmbeddedFont(m_fontReference, 0, &status); + } else + RemoveFontMemResourceEx(m_fontReference); + } } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontRenderingMode renderingMode) { - return FontPlatformData(m_cgFont, size, bold, italic); + ASSERT(m_cgFont); + ASSERT(m_fontReference); + ASSERT(T2embedLibrary()); + + LOGFONT logFont; + if (m_name.isNull()) + TTGetNewFontName(&m_fontReference, logFont.lfFaceName, LF_FACESIZE, 0, 0); + else + memcpy(logFont.lfFaceName, m_name.charactersWithNullTermination(), sizeof(logFont.lfFaceName[0]) * min(static_cast<size_t>(LF_FACESIZE), 1 + m_name.length())); + + logFont.lfHeight = -size; + if (renderingMode == NormalRenderingMode) + logFont.lfHeight *= 32; + 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 = CLEARTYPE_QUALITY; + logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + logFont.lfItalic = italic; + logFont.lfWeight = bold ? 700 : 400; + + HFONT hfont = CreateFontIndirect(&logFont); + return FontPlatformData(hfont, m_cgFont, size, bold, italic, renderingMode == AlternateRenderingMode); } const void* getData(void* info) @@ -60,9 +111,82 @@ size_t getBytesWithOffset(void *info, void* buffer, size_t offset, size_t count) return availBytes; } +// 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 = 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 = 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 = max<int>(m_overlayDst - m_offset, 0); + size_t srcOffset = max<int>(0, m_offset - m_overlayDst); + size_t bytesToCopy = 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()); +} + FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) { ASSERT_ARG(buffer, buffer); + ASSERT(T2embedLibrary()); // Get CG to create the font. CGDataProviderDirectAccessCallbacks callbacks = { &getData, &releaseData, &getBytesWithOffset, NULL }; @@ -70,7 +194,42 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider.get()); if (!cgFont) return 0; - return new FontCustomPlatformData(cgFont); + + // 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)) { + CGFontRelease(cgFont); + 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) { + CGFontRelease(cgFont); + return 0; + } + } + + return new FontCustomPlatformData(cgFont, fontReference, fontName); } } diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.h b/WebCore/platform/graphics/win/FontCustomPlatformData.h index 9f30424..34a9851 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Computer, Inc. + * Copyright (C) 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 @@ -21,6 +21,8 @@ #ifndef FontCustomPlatformData_h #define FontCustomPlatformData_h +#include "FontRenderingMode.h" +#include "PlatformString.h" #include <wtf/Noncopyable.h> typedef struct CGFont* CGFontRef; @@ -31,17 +33,23 @@ class FontPlatformData; class SharedBuffer; struct FontCustomPlatformData : Noncopyable { - FontCustomPlatformData(CGFontRef cgFont) - : m_cgFont(cgFont) - {} + FontCustomPlatformData(CGFontRef cgFont, HANDLE fontReference, const String& name) + : m_cgFont(cgFont) + , m_fontReference(fontReference) + , m_name(name) + { + } + ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); CGFontRef m_cgFont; + HANDLE m_fontReference; + String m_name; }; -FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer); +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); } diff --git a/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp new file mode 100644 index 0000000..e54d85a --- /dev/null +++ b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007, 2008 Apple Computer, Inc. + * + * 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 "FontCustomPlatformDataCairo.h" + +#include "SharedBuffer.h" +#include "FontPlatformData.h" +#include <wtf/RetainPtr.h> + +namespace WebCore { + +FontCustomPlatformDataCairo::~FontCustomPlatformDataCairo() +{ + cairo_font_face_destroy(m_fontFace); +} + +FontPlatformData FontCustomPlatformDataCairo::fontPlatformData(int size, bool bold, bool italic) +{ + return FontPlatformData(m_fontFace, size, bold, italic); +} + +static void releaseData(void* data) +{ + static_cast<SharedBuffer*>(data)->deref(); +} + +FontCustomPlatformDataCairo* createFontCustomPlatformData(SharedBuffer* buffer) +{ + ASSERT_ARG(buffer, buffer); + + buffer->ref(); + HFONT font = reinterpret_cast<HFONT>(buffer); + cairo_font_face_t* fontFace = cairo_win32_font_face_create_for_hfont(font); + if (!fontFace) + return 0; + + static cairo_user_data_key_t bufferKey; + cairo_font_face_set_user_data(fontFace, &bufferKey, buffer, releaseData); + + return new FontCustomPlatformDataCairo(fontFace); +} + +} diff --git a/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h new file mode 100644 index 0000000..87794b5 --- /dev/null +++ b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. + * + * 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 FontCustomPlatformDataCairo_h +#define FontCustomPlatformDataCairo_h + +#include <wtf/Noncopyable.h> + +#include <cairo.h> + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformDataCairo : Noncopyable { + FontCustomPlatformDataCairo(cairo_font_face_t* fontFace) + : m_fontFace(fontFace) + { + } + ~FontCustomPlatformDataCairo(); + + FontPlatformData fontPlatformData(int size, bool bold, bool italic); + + cairo_font_face_t* m_fontFace; +}; + +FontCustomPlatformDataCairo* createFontCustomPlatformData(SharedBuffer*); + +} + +#endif diff --git a/WebCore/platform/graphics/win/FontPlatformData.h b/WebCore/platform/graphics/win/FontPlatformData.h index 15d23cd..d61afa8 100644 --- a/WebCore/platform/graphics/win/FontPlatformData.h +++ b/WebCore/platform/graphics/win/FontPlatformData.h @@ -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 Apple Inc. + * Copyright (C) 2006, 2007, 2008 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,45 +25,58 @@ #define FontPlatformData_H #include "StringImpl.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RetainPtr.h> +#include <wtf/RefCounted.h> -typedef struct HFONT__ *HFONT; -typedef struct CGFont *CGFontRef; +#if PLATFORM(CAIRO) +#include <cairo-win32.h> +#endif + +typedef struct HFONT__* HFONT; +typedef struct CGFont* CGFontRef; namespace WebCore { class FontDescription; -class FontPlatformData -{ +class FontPlatformData { public: - class Deleted {}; - - // Used for deleted values in the font cache's hash tables. - FontPlatformData(Deleted) - : m_font((HFONT)-1) - , m_cgFont(NULL) - , m_size(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_useGDI(false) - {} - FontPlatformData() - : m_font(0) - , m_cgFont(NULL) - , m_size(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_useGDI(false) - {} +#if PLATFORM(CAIRO) + : m_fontFace(0) + , +#else + : +#endif + m_size(0) + , m_syntheticBold(false) + , m_syntheticOblique(false) + , m_useGDI(false) + { + } FontPlatformData(HFONT, float size, bool bold, bool oblique, bool useGDI); FontPlatformData(float size, bool bold, bool oblique); - FontPlatformData(CGFontRef, float size, bool bold, bool oblique); + +#if PLATFORM(CG) + FontPlatformData(HFONT, CGFontRef, float size, bool bold, bool oblique, bool useGDI); +#elif PLATFORM(CAIRO) + FontPlatformData(cairo_font_face_t*, float size, bool bold, bool oblique); +#endif ~FontPlatformData(); - HFONT hfont() const { return m_font; } - CGFontRef cgFont() const { return m_cgFont; } + FontPlatformData(WTF::HashTableDeletedValueType) : m_font(WTF::HashTableDeletedValue) { } + bool isHashTableDeletedValue() const { return m_font.isHashTableDeletedValue(); } + + HFONT hfont() const { return m_font->hfont(); } +#if PLATFORM(CG) + CGFontRef cgFont() const { return m_cgFont.get(); } +#elif PLATFORM(CAIRO) + void setFont(cairo_t* ft) const; + cairo_font_face_t* fontFace() const { return m_fontFace; } + cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } +#endif float size() const { return m_size; } void setSize(float size) { m_size = size; } @@ -73,19 +86,55 @@ public: unsigned hash() const { - return StringImpl::computeHash((UChar*)(&m_font), sizeof(HFONT) / sizeof(UChar)); + return m_font->hash(); } bool operator==(const FontPlatformData& other) const { - return m_font == other.m_font && m_cgFont ==other.m_cgFont && m_size == other.m_size && + return m_font == other.m_font && +#if PLATFORM(CG) + m_cgFont == other.m_cgFont && +#elif PLATFORM(CAIRO) + m_fontFace == other.m_fontFace && + m_scaledFont == other.m_scaledFont && +#endif + m_size == other.m_size && m_syntheticBold == other.m_syntheticBold && m_syntheticOblique == other.m_syntheticOblique && m_useGDI == other.m_useGDI; } private: - HFONT m_font; - CGFontRef m_cgFont; + class RefCountedHFONT : public RefCounted<RefCountedHFONT> { + public: + static PassRefPtr<RefCountedHFONT> create(HFONT hfont) { return adoptRef(new RefCountedHFONT(hfont)); } + static PassRefPtr<RefCountedHFONT> createDeleted() { return adoptRef(new RefCountedHFONT(reinterpret_cast<HFONT>(-1))); } + + ~RefCountedHFONT() { if (m_hfont != reinterpret_cast<HFONT>(-1)) DeleteObject(m_hfont); } + + HFONT hfont() const { return m_hfont; } + unsigned hash() const + { + return StringImpl::computeHash(reinterpret_cast<const UChar*>(&m_hfont), sizeof(HFONT) / sizeof(UChar)); + } + + private: + RefCountedHFONT(HFONT hfont) + : m_hfont(hfont) + { + } + + HFONT m_hfont; + }; + + void platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName); + + RefPtr<RefCountedHFONT> m_font; +#if PLATFORM(CG) + RetainPtr<CGFontRef> m_cgFont; +#elif PLATFORM(CAIRO) + cairo_font_face_t* m_fontFace; + cairo_scaled_font_t* m_scaledFont; +#endif float m_size; bool m_syntheticBold; diff --git a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp new file mode 100644 index 0000000..bbfdb9f --- /dev/null +++ b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp @@ -0,0 +1,134 @@ +/* + * 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. + * + * 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" + +#include "PlatformString.h" +#include "StringHash.h" +#include <ApplicationServices/ApplicationServices.h> +#include <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> + +using std::min; + +namespace WebCore { + +static inline USHORT readBigEndianWord(const BYTE* word) { return (word[0] << 8) | word[1]; } + +static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc) +{ + const DWORD cMaxNameTableSize = 1024 * 1024; + + static HashMap<String, RetainPtr<CFStringRef> > nameMap; + + // Check our hash first. + String faceString(faceName); + RetainPtr<CFStringRef> result = nameMap.get(faceString); + if (result) + return result.get(); + + // We need to obtain the PostScript name from the name table and use it instead,. + DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards + if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize) + return NULL; + + Vector<BYTE> bufferVector(bufferSize); + BYTE* buffer = bufferVector.data(); + if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR) + return NULL; + + if (bufferSize < 6) + return NULL; + + USHORT numberOfRecords = readBigEndianWord(buffer + 2); + UINT stringsOffset = readBigEndianWord(buffer + 4); + if (bufferSize < stringsOffset) + return NULL; + + BYTE* strings = buffer + stringsOffset; + + // Now walk each name record looking for a Mac or Windows PostScript name. + UINT offset = 6; + for (int i = 0; i < numberOfRecords; i++) { + if (bufferSize < offset + 12) + return NULL; + + USHORT platformID = readBigEndianWord(buffer + offset); + USHORT encodingID = readBigEndianWord(buffer + offset + 2); + USHORT languageID = readBigEndianWord(buffer + offset + 4); + USHORT nameID = readBigEndianWord(buffer + offset + 6); + USHORT length = readBigEndianWord(buffer + offset + 8); + USHORT nameOffset = readBigEndianWord(buffer + offset + 10); + + if (platformID == 3 && encodingID == 1 && languageID == 0x409 && nameID == 6) { + // This is a Windows PostScript name and is therefore UTF-16. + // Pass Big Endian as the encoding. + if (bufferSize < stringsOffset + nameOffset + length) + return NULL; + result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingUTF16BE, false)); + break; + } else if (platformID == 1 && encodingID == 0 && languageID == 0 && nameID == 6) { + // This is a Mac PostScript name and is therefore ASCII. + // See http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html + if (bufferSize < stringsOffset + nameOffset + length) + return NULL; + result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingASCII, false)); + break; + } + + offset += 12; + } + + if (result) + nameMap.set(faceString, result); + return result.get(); +} + +void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName) +{ + // Try the face name first. Windows may end up localizing this name, and CG doesn't know about + // the localization. If the create fails, we'll try the PostScript name. + RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName))); + m_cgFont.adoptCF(CGFontCreateWithFontName(fullName.get())); + if (!m_cgFont) { + CFStringRef postScriptName = getPostScriptName(fullName.get(), hdc); + if (postScriptName) { + m_cgFont.adoptCF(CGFontCreateWithFontName(postScriptName)); + ASSERT(m_cgFont); + } + } +} + +FontPlatformData::FontPlatformData(HFONT hfont, CGFontRef font, float size, bool bold, bool oblique, bool useGDI) + : m_font(RefCountedHFONT::create(hfont)) + , m_size(size) + , m_cgFont(font) + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) + , m_useGDI(useGDI) +{ +} + +} diff --git a/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp new file mode 100644 index 0000000..438d0a9 --- /dev/null +++ b/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp @@ -0,0 +1,89 @@ +/* + * 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) 2007 Alp Toker + * Copyright (C) 2008 Brent Fulgham + * + * 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" + +#include "PlatformString.h" +#include "StringHash.h" +#include <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> + +#include <cairo-win32.h> + +using std::min; + +namespace WebCore { + +void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName) +{ + m_fontFace = cairo_win32_font_face_create_for_hfont(font); + cairo_matrix_t sizeMatrix, ctm; + cairo_matrix_init_identity(&ctm); + cairo_matrix_init_scale(&sizeMatrix, size, size); + + static cairo_font_options_t* fontOptions = 0; + if (!fontOptions) + { + fontOptions = cairo_font_options_create(); + cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_SUBPIXEL); + } + + m_scaledFont = cairo_scaled_font_create(m_fontFace, &sizeMatrix, &ctm, fontOptions); +} + +FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool oblique) + : m_font(0) + , m_size(size) + , m_fontFace(fontFace) + , m_scaledFont(0) + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) + , m_useGDI(false) +{ + cairo_matrix_t fontMatrix; + cairo_matrix_init_scale(&fontMatrix, size, size); + cairo_matrix_t ctm; + cairo_matrix_init_identity(&ctm); + cairo_font_options_t* options = cairo_font_options_create(); + + // We force antialiasing and disable hinting to provide consistent + // typographic qualities for custom fonts on all platforms. + cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); + + m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options); + cairo_font_options_destroy(options); +} + +void FontPlatformData::setFont(cairo_t* cr) const +{ + ASSERT(m_scaledFont); + + cairo_set_scaled_font(cr, m_scaledFont); +} + +} diff --git a/WebCore/platform/graphics/win/FontPlatformDataWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp index a5fab36..4b4df5a 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp @@ -2,7 +2,8 @@ * 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 Apple Inc. + * Copyright (C) 2006, 2007, 2008 Apple Inc. + * Copyright (C) 2008 Brent Fulgham * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,7 +27,6 @@ #include "PlatformString.h" #include "StringHash.h" -#include <ApplicationServices/ApplicationServices.h> #include <wtf/HashMap.h> #include <wtf/RetainPtr.h> #include <wtf/Vector.h> @@ -35,105 +35,17 @@ using std::min; namespace WebCore { -static const int Bold = (1 << 0); -static const int Italic = (1 << 1); -static const int BoldOblique = (1 << 2); - -static inline USHORT readBigEndianWord(const BYTE* word) { return (word[0] << 8) | word[1]; } - -static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc) -{ - const DWORD cMaxNameTableSize = 1024 * 1024; - - static HashMap<String, RetainPtr<CFStringRef> > nameMap; - - // Check our hash first. - String faceString(faceName); - RetainPtr<CFStringRef> result = nameMap.get(faceString); - if (result) - return result.get(); - - // We need to obtain the PostScript name from the name table and use it instead,. - DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards - if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize) - return NULL; - - Vector<BYTE> bufferVector(bufferSize); - BYTE* buffer = bufferVector.data(); - if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR) - return NULL; - - if (bufferSize < 6) - return NULL; - - USHORT numberOfRecords = readBigEndianWord(buffer + 2); - UINT stringsOffset = readBigEndianWord(buffer + 4); - if (bufferSize < stringsOffset) - return NULL; - - BYTE* strings = buffer + stringsOffset; - - // Now walk each name record looking for a Mac or Windows PostScript name. - UINT offset = 6; - for (int i = 0; i < numberOfRecords; i++) { - if (bufferSize < offset + 12) - return NULL; - - USHORT platformID = readBigEndianWord(buffer + offset); - USHORT encodingID = readBigEndianWord(buffer + offset + 2); - USHORT languageID = readBigEndianWord(buffer + offset + 4); - USHORT nameID = readBigEndianWord(buffer + offset + 6); - USHORT length = readBigEndianWord(buffer + offset + 8); - USHORT nameOffset = readBigEndianWord(buffer + offset + 10); - - if (platformID == 3 && encodingID == 1 && languageID == 0x409 && nameID == 6) { - // This is a Windows PostScript name and is therefore UTF-16. - // Pass Big Endian as the encoding. - if (bufferSize < stringsOffset + nameOffset + length) - return NULL; - result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingUTF16BE, false)); - break; - } else if (platformID == 1 && encodingID == 0 && languageID == 0 && nameID == 6) { - // This is a Mac PostScript name and is therefore ASCII. - // See http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html - if (bufferSize < stringsOffset + nameOffset + length) - return NULL; - result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingASCII, false)); - break; - } - - offset += 12; - } - - if (result) - nameMap.set(faceString, result); - return result.get(); -} - -static int CALLBACK enumStylesCallback(const LOGFONT* logFont, const TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) -{ - int *style = reinterpret_cast<int*>(lParam); - - // FIXME: In order to accommodate Lucida we go ahead and consider a weight of 600 to be bold. - // This does mean we'll consider demibold and semibold fonts on windows to also be bold. This - // is rare enough that it seems like an ok hack for now. - if (logFont->lfWeight >= 600) { - if (logFont->lfItalic) - *style |= BoldOblique; - else - *style |= Bold; - } else if (logFont->lfItalic) - *style |= Italic; - - return 1; -} - FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool oblique, bool useGDI) - : m_font(font) + : m_font(RefCountedHFONT::create(font)) , m_size(size) +#if PLATFORM(CG) , m_cgFont(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) +#elif PLATFORM(CAIRO) + , m_fontFace(0) + , m_scaledFont(0) +#endif + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) , m_useGDI(useGDI) { HDC hdc = GetDC(0); @@ -142,54 +54,16 @@ FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool obliq SelectObject(hdc, font); UINT bufferSize = GetOutlineTextMetrics(hdc, 0, NULL); - ASSERT_WITH_MESSAGE(bufferSize != 0, "Bitmap fonts not supported with CoreGraphics."); + ASSERT_WITH_MESSAGE(bufferSize, "Bitmap fonts not supported with CoreGraphics."); - if (bufferSize != 0) { + if (bufferSize) { OUTLINETEXTMETRICW* metrics = (OUTLINETEXTMETRICW*)malloc(bufferSize); GetOutlineTextMetricsW(hdc, bufferSize, metrics); WCHAR* faceName = (WCHAR*)((uintptr_t)metrics + (uintptr_t)metrics->otmpFaceName); - if (!useGDI && (bold || oblique)) { - LOGFONT logFont; - - int len = min((int)wcslen(faceName), LF_FACESIZE - 1); - memcpy(logFont.lfFaceName, faceName, len * sizeof(WORD)); - logFont.lfFaceName[len] = '\0'; - logFont.lfCharSet = metrics->otmTextMetrics.tmCharSet; - logFont.lfPitchAndFamily = 0; - - int styles = 0; - EnumFontFamiliesEx(hdc, &logFont, enumStylesCallback, reinterpret_cast<LPARAM>(&styles), 0); - - // Check if we need to synthesize bold or oblique. The rule that complicates things here - // is that if the requested font is bold and oblique, and both a bold font and an oblique font - // exist, the bold font should be used, and oblique synthesized. - if (bold && oblique) { - if (styles == 0) { - m_syntheticBold = true; - m_syntheticOblique = true; - } else if (styles & Bold) - m_syntheticOblique = true; - else if (styles & Italic) - m_syntheticBold = true; - } else if (bold && (!(styles & Bold))) - m_syntheticBold = true; - else if (oblique && !(styles & Italic)) - m_syntheticOblique = true; - } + platformDataInit(font, size, hdc, faceName); - // Try the face name first. Windows may end up localizing this name, and CG doesn't know about - // the localization. If the create fails, we'll try the PostScript name. - RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName))); - m_cgFont = CGFontCreateWithFontName(fullName.get()); - if (!m_cgFont) { - CFStringRef postScriptName = getPostScriptName(fullName.get(), hdc); - if (postScriptName) { - m_cgFont = CGFontCreateWithFontName(postScriptName); - ASSERT(m_cgFont); - } - } free(metrics); } @@ -198,19 +72,13 @@ FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool obliq } FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) - : m_font(0) - , m_size(size) + : m_size(size) +#if PLATFORM(CG) , m_cgFont(0) - , m_syntheticBold(bold) - , m_syntheticOblique(oblique) - , m_useGDI(false) -{ -} - -FontPlatformData::FontPlatformData(CGFontRef font, float size, bool bold, bool oblique) - : m_font(0) - , m_size(size) - , m_cgFont(font) +#elif PLATFORM(CAIRO) + , m_fontFace(0) + , m_scaledFont(0) +#endif , m_syntheticBold(bold) , m_syntheticOblique(oblique) , m_useGDI(false) diff --git a/WebCore/platform/graphics/win/GlyphPageTreeNodeWin.cpp b/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp index 23b756c..c11fc1b 100644 --- a/WebCore/platform/graphics/win/GlyphPageTreeNodeWin.cpp +++ b/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 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 @@ -36,9 +36,9 @@ namespace WebCore { bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { - // The bufferLength will be greater than the glyph page size if the buffer has Unicode supplementary characters. + // bufferLength will be greater than the requested number of glyphs if the buffer contains surrogate pairs. // We won't support this for now. - if (bufferLength > GlyphPage::size) + if (bufferLength > length) return false; bool haveGlyphs = false; diff --git a/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp b/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp new file mode 100644 index 0000000..b679ced --- /dev/null +++ b/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2006, 2007, 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 "GlyphPageTreeNode.h" + +#include "SimpleFontData.h" + +namespace WebCore { + +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + // bufferLength will be greater than the requested number of glyphs if the buffer contains surrogate pairs. + // We won't support this for now. + if (bufferLength > length) + return false; + + bool haveGlyphs = false; + + HDC dc = GetDC((HWND)0); + SaveDC(dc); + SelectObject(dc, fontData->platformData().hfont()); + + TEXTMETRIC tm; + GetTextMetrics(dc, &tm); + + WORD localGlyphBuffer[GlyphPage::size * 2]; + DWORD result = GetGlyphIndices(dc, buffer, bufferLength, localGlyphBuffer, 0); + bool success = result != GDI_ERROR && static_cast<unsigned>(result) == bufferLength; + if (success) { + for (unsigned i = 0; i < length; i++) { + Glyph glyph = localGlyphBuffer[i]; + if (!glyph) + setGlyphDataForIndex(offset + i, 0, 0); + else { + setGlyphDataForIndex(offset + i, glyph, fontData); + haveGlyphs = true; + } + } + } + RestoreDC(dc, -1); + ReleaseDC(0, dc); + + return haveGlyphs; +} + +} diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index 58829b5..5a4279a 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -38,7 +38,7 @@ using namespace std; namespace WebCore { -static CGContextRef CGContextWithHDC(HDC hdc) +static CGContextRef CGContextWithHDC(HDC hdc, bool hasAlpha) { HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); @@ -46,8 +46,10 @@ static CGContextRef CGContextWithHDC(HDC hdc) GetObject(bitmap, sizeof(info), &info); ASSERT(info.bmBitsPixel == 32); + + CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | (hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst); CGContextRef context = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, - info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst); + info.bmWidthBytes, deviceRGB, bitmapInfo); CGColorSpaceRelease(deviceRGB); // Flip coords @@ -60,9 +62,9 @@ static CGContextRef CGContextWithHDC(HDC hdc) return context; } -GraphicsContext::GraphicsContext(HDC hdc) +GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha) : m_common(createGraphicsContextPrivate()) - , m_data(new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc))) + , m_data(new GraphicsContextPlatformPrivate(CGContextWithHDC(hdc, hasAlpha))) { CGContextRelease(m_data->m_cgContext); m_data->m_hdc = hdc; @@ -76,9 +78,12 @@ GraphicsContext::GraphicsContext(HDC hdc) bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } -HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend) +// FIXME: Is it possible to merge getWindowsContext and createWindowsBitmap into a single API +// suitable for all clients? +HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { - if (inTransparencyLayer()) { + // FIXME: Should a bitmap be created also when a shadow is set? + if (mayCreateBitmap && inTransparencyLayer()) { if (dstRect.isEmpty()) return 0; @@ -133,9 +138,9 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha return m_data->m_hdc; } -void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend) +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { - if (hdc && inTransparencyLayer()) { + if (mayCreateBitmap && hdc && inTransparencyLayer()) { if (dstRect.isEmpty()) return; @@ -168,75 +173,60 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo m_data->restore(); } -void GraphicsContextPlatformPrivate::save() +GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size) + : m_hdc(0) + , m_size(size) { - if (!m_hdc) + BITMAPINFO bitmapInfo; + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = m_size.width(); + bitmapInfo.bmiHeader.biHeight = m_size.height(); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + bitmapInfo.bmiHeader.biSizeImage = 0; + bitmapInfo.bmiHeader.biXPelsPerMeter = 0; + bitmapInfo.bmiHeader.biYPelsPerMeter = 0; + bitmapInfo.bmiHeader.biClrUsed = 0; + bitmapInfo.bmiHeader.biClrImportant = 0; + + m_bitmap = CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&m_bitmapBuffer), 0, 0); + if (!m_bitmap) return; - SaveDC(m_hdc); -} -void GraphicsContextPlatformPrivate::restore() -{ - if (!m_hdc) - return; - RestoreDC(m_hdc, -1); -} + m_hdc = CreateCompatibleDC(hdc); + SelectObject(m_hdc, m_bitmap); -void GraphicsContextPlatformPrivate::clip(const IntRect& clipRect) -{ - if (!m_hdc) - return; - IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); -} + BITMAP bmpInfo; + GetObject(m_bitmap, sizeof(bmpInfo), &bmpInfo); + m_bytesPerRow = bmpInfo.bmWidthBytes; + m_bitmapBufferLength = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; -void GraphicsContextPlatformPrivate::clip(const Path&) -{ - notImplemented(); + SetGraphicsMode(m_hdc, GM_ADVANCED); } -void GraphicsContextPlatformPrivate::scale(const FloatSize& size) +GraphicsContext::WindowsBitmap::~WindowsBitmap() { - if (!m_hdc) + if (!m_bitmap) return; - XFORM xform; - xform.eM11 = size.width(); - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = size.height(); - xform.eDx = 0.0f; - xform.eDy = 0.0f; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} -static const double deg2rad = 0.017453292519943295769; // pi/180 + DeleteDC(m_hdc); + DeleteObject(m_bitmap); +} -void GraphicsContextPlatformPrivate::rotate(float degreesAngle) +GraphicsContext::WindowsBitmap* GraphicsContext::createWindowsBitmap(IntSize size) { - float radiansAngle = degreesAngle * deg2rad; - float cosAngle = cosf(radiansAngle); - float sinAngle = sinf(radiansAngle); - XFORM xform; - xform.eM11 = cosAngle; - xform.eM12 = -sinAngle; - xform.eM21 = sinAngle; - xform.eM22 = cosAngle; - xform.eDx = 0.0f; - xform.eDy = 0.0f; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); + return new WindowsBitmap(m_data->m_hdc, size); } -void GraphicsContextPlatformPrivate::translate(float x , float y) +void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point) { - if (!m_hdc) - return; - XFORM xform; - xform.eM11 = 1.0f; - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = 1.0f; - xform.eDx = x; - xform.eDy = y; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); + RetainPtr<CGColorSpaceRef> deviceRGB(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CFDataRef> imageData(AdoptCF, CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, image->buffer(), image->bufferLength(), kCFAllocatorNull)); + 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) diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index c3fbdd7..3dcf6ba 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -37,7 +37,7 @@ using namespace std; namespace WebCore { -GraphicsContext::GraphicsContext(HDC dc) +GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate) { @@ -58,8 +58,13 @@ GraphicsContext::GraphicsContext(HDC dc) } } -HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend) +HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { + // FIXME: We aren't really doing anything with the 'mayCreateBitmap' flag. This needs + // to be addressed. + if (dstRect.isEmpty()) + return 0; + // This is probably wrong, and definitely out of date. Pulled from old SVN cairo_surface_t* surface = cairo_get_target(platformContext()); HDC hdc = cairo_win32_surface_get_dc(surface); @@ -77,36 +82,41 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha xform.eM22 = mat.yy; xform.eDx = mat.x0; xform.eDy = mat.y0; - SetWorldTransform(hdc, &xform); + ::SetWorldTransform(hdc, &xform); return hdc; } bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } -void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend) +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { + // FIXME: We aren't really doing anything with the 'mayCreateBitmap' flag. This needs + // to be addressed. + if (dstRect.isEmpty()) + return; + cairo_surface_t* surface = cairo_get_target(platformContext()); HDC hdc2 = cairo_win32_surface_get_dc(surface); RestoreDC(hdc2, -1); cairo_surface_mark_dirty(surface); } -void GraphicsContext::concatCTM(const AffineTransform& transform) +void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) { - cairo_surface_t* surface = cairo_get_target(platformContext()); + cairo_surface_t* surface = cairo_get_target(cr); HDC hdc = cairo_win32_surface_get_dc(surface); SaveDC(hdc); - cairo_matrix_t mat; - cairo_get_matrix(platformContext(), &mat); + const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); + XFORM xform; - xform.eM11 = mat.xx; - xform.eM12 = mat.xy; - xform.eM21 = mat.yx; - xform.eM22 = mat.yy; - xform.eDx = mat.x0; - xform.eDy = mat.y0; + xform.eM11 = matrix->xx; + xform.eM12 = matrix->xy; + xform.eM21 = matrix->yx; + xform.eM22 = matrix->yy; + xform.eDx = matrix->x0; + xform.eDy = matrix->y0; ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); } diff --git a/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/WebCore/platform/graphics/win/GraphicsContextWin.cpp index a8f2148..dbf9fad 100644 --- a/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -26,6 +26,12 @@ #include "config.h" #include "GraphicsContext.h" +#if PLATFORM(CG) +#include "GraphicsContextPlatformPrivateCG.h" +#elif PLATFORM(CAIRO) +#include "GraphicsContextPlatformPrivateCairo.h" +#endif + #include "AffineTransform.h" #include "NotImplemented.h" #include "Path.h" @@ -37,6 +43,77 @@ namespace WebCore { class SVGResourceImage; +void GraphicsContextPlatformPrivate::save() +{ + if (!m_hdc) + return; + SaveDC(m_hdc); +} + +void GraphicsContextPlatformPrivate::restore() +{ + if (!m_hdc) + return; + RestoreDC(m_hdc, -1); +} + +void GraphicsContextPlatformPrivate::clip(const FloatRect& clipRect) +{ + if (!m_hdc) + return; + IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); +} + +void GraphicsContextPlatformPrivate::clip(const Path&) +{ + notImplemented(); +} + +void GraphicsContextPlatformPrivate::scale(const FloatSize& size) +{ + if (!m_hdc) + return; + XFORM xform; + xform.eM11 = size.width(); + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = size.height(); + xform.eDx = 0.0f; + xform.eDy = 0.0f; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +static const double deg2rad = 0.017453292519943295769; // pi/180 + +void GraphicsContextPlatformPrivate::rotate(float degreesAngle) +{ + float radiansAngle = degreesAngle * deg2rad; + float cosAngle = cosf(radiansAngle); + float sinAngle = sinf(radiansAngle); + XFORM xform; + xform.eM11 = cosAngle; + xform.eM12 = -sinAngle; + xform.eM21 = sinAngle; + xform.eM22 = cosAngle; + xform.eDx = 0.0f; + xform.eDy = 0.0f; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +void GraphicsContextPlatformPrivate::translate(float x , float y) +{ + if (!m_hdc) + return; + XFORM xform; + xform.eM11 = 1.0f; + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = 1.0f; + xform.eDx = x; + xform.eDy = y; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + #if ENABLE(SVG) GraphicsContext* contextForImage(SVGResourceImage*) { diff --git a/WebCore/platform/graphics/win/IconWin.cpp b/WebCore/platform/graphics/win/IconWin.cpp index 60cebe3..c02b56e 100644 --- a/WebCore/platform/graphics/win/IconWin.cpp +++ b/WebCore/platform/graphics/win/IconWin.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2006, 2007 Apple Inc. +* Copyright (C) 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 @@ -23,27 +23,25 @@ #include "GraphicsContext.h" #include "PlatformString.h" +#include <tchar.h> #include <windows.h> namespace WebCore { -Icon::Icon() - : m_hIcon(0) -{ -} +static const int shell32MultipleFileIconIndex = 54; Icon::Icon(HICON icon) : m_hIcon(icon) { + ASSERT(icon); } Icon::~Icon() { - if (m_hIcon) - DestroyIcon(m_hIcon); + DestroyIcon(m_hIcon); } -PassRefPtr<Icon> Icon::newIconForFile(const String& filename) +PassRefPtr<Icon> Icon::createIconForFile(const String& filename) { SHFILEINFO sfi; memset(&sfi, 0, sizeof(sfi)); @@ -52,9 +50,23 @@ PassRefPtr<Icon> Icon::newIconForFile(const String& filename) if (!SHGetFileInfo(tmpFilename.charactersWithNullTermination(), 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SHELLICONSIZE | SHGFI_SMALLICON)) return 0; - Icon* icon = new Icon(); - icon->m_hIcon = sfi.hIcon; - return icon; + return adoptRef(new Icon(sfi.hIcon)); +} + +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>&) +{ + TCHAR buffer[MAX_PATH]; + UINT length = ::GetSystemDirectory(buffer, ARRAYSIZE(buffer)); + if (!length) + return 0; + + if (_tcscat_s(buffer, TEXT("\\shell32.dll"))) + return 0; + + HICON hIcon; + if (!::ExtractIconEx(buffer, shell32MultipleFileIconIndex, 0, &hIcon, 1)) + return 0; + return adoptRef(new Icon(hIcon)); } void Icon::paint(GraphicsContext* context, const IntRect& r) diff --git a/WebCore/platform/graphics/win/ImageWin.cpp b/WebCore/platform/graphics/win/ImageWin.cpp index 2d3a87a..54c5b41 100644 --- a/WebCore/platform/graphics/win/ImageWin.cpp +++ b/WebCore/platform/graphics/win/ImageWin.cpp @@ -42,12 +42,12 @@ void BitmapImage::invalidatePlatformData() { } -Image* Image::loadPlatformResource(const char *name) +PassRefPtr<Image> Image::loadPlatformResource(const char *name) { RefPtr<SharedBuffer> buffer = loadResourceIntoBuffer(name); - BitmapImage* img = new BitmapImage; + RefPtr<BitmapImage> img = BitmapImage::create(); img->setData(buffer.release(), true); - return img; + return img.release(); } bool BitmapImage::getHBITMAP(HBITMAP bmp) diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index 65b3db6..cef4217 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -28,13 +28,22 @@ #if ENABLE(VIDEO) #include "MediaPlayerPrivateQuickTimeWin.h" -#include "DeprecatedString.h" #include "GraphicsContext.h" #include "KURL.h" #include "QTMovieWin.h" #include "ScrollView.h" #include <wtf/MathExtras.h> +#if DRAW_FRAME_RATE +#include "Font.h" +#include "FrameView.h" +#include "Frame.h" +#include "Document.h" +#include "RenderObject.h" +#include "RenderStyle.h" +#include "Windows.h" +#endif + using namespace std; namespace WebCore { @@ -51,6 +60,11 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_readyState(MediaPlayer::DataUnavailable) , m_startedPlaying(false) , m_isStreaming(false) +#if DRAW_FRAME_RATE + , m_frameCountWhilePlaying(0) + , m_timeStartedPlaying(0) + , m_timeStoppedPlaying(0) +#endif { } @@ -88,6 +102,9 @@ void MediaPlayerPrivate::play() if (!m_qtMovie) return; m_startedPlaying = true; +#if DRAW_FRAME_RATE + m_frameCountWhilePlaying = 0; +#endif m_qtMovie->play(); startEndPointTimerIfNeeded(); @@ -98,6 +115,9 @@ void MediaPlayerPrivate::pause() if (!m_qtMovie) return; m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = GetTickCount(); +#endif m_qtMovie->pause(); m_endPointTimer.stop(); } @@ -304,7 +324,7 @@ void MediaPlayerPrivate::updateStates() long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError; - if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData) { + if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData && !m_player->inMediaDocument()) { unsigned enabledTrackCount; m_qtMovie->disableUnsupportedTracks(enabledTrackCount); // FIXME: We should differentiate between load errors and decode errors <rdar://problem/5605692> @@ -352,6 +372,9 @@ void MediaPlayerPrivate::didEnd() { m_endPointTimer.stop(); m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = GetTickCount(); +#endif updateStates(); m_player->timeChanged(); } @@ -376,6 +399,31 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) HDC hdc = p->getWindowsContext(r); m_qtMovie->paint(hdc, r.x(), r.y()); p->releaseWindowsContext(hdc, r); + +#if DRAW_FRAME_RATE + if (m_frameCountWhilePlaying > 10) { + Frame* frame = m_player->m_frameView ? m_player->m_frameView->frame() : NULL; + Document* document = frame ? frame->document() : NULL; + RenderObject* renderer = document ? document->renderer() : NULL; + RenderStyle* styleToUse = renderer ? renderer->style() : NULL; + if (styleToUse) { + double frameRate = (m_frameCountWhilePlaying - 1) / (0.001 * ( m_startedPlaying ? (GetTickCount() - m_timeStartedPlaying) : + (m_timeStoppedPlaying - m_timeStartedPlaying) )); + String text = String::format("%1.2f", frameRate); + TextRun textRun(text.characters(), text.length()); + const Color color(255, 0, 0); + p->save(); + p->translate(r.x(), r.y() + r.height()); + p->setFont(styleToUse->font()); + p->setStrokeColor(color); + p->setStrokeStyle(SolidStroke); + p->setStrokeThickness(1.0f); + p->setFillColor(color); + p->drawText(textRun, IntPoint(2, -3)); + p->restore(); + } + } +#endif } void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) @@ -417,6 +465,15 @@ void MediaPlayerPrivate::movieTimeChanged(QTMovieWin* movie) void MediaPlayerPrivate::movieNewImageAvailable(QTMovieWin* movie) { ASSERT(m_qtMovie.get() == movie); +#if DRAW_FRAME_RATE + if (m_startedPlaying) { + m_frameCountWhilePlaying++; + // to eliminate preroll costs from our calculation, + // our frame rate calculation excludes the first frame drawn after playback starts + if (1==m_frameCountWhilePlaying) + m_timeStartedPlaying = GetTickCount(); + } +#endif m_player->repaint(); } diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h index 37b5b05..c4c893c 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -33,6 +33,10 @@ #include <QTMovieWin.h> #include <wtf/OwnPtr.h> +#ifndef DRAW_FRAME_RATE +#define DRAW_FRAME_RATE 0 +#endif + namespace WebCore { class GraphicsContext; @@ -111,6 +115,11 @@ private: MediaPlayer::ReadyState m_readyState; bool m_startedPlaying; bool m_isStreaming; +#if DRAW_FRAME_RATE + int m_frameCountWhilePlaying; + int m_timeStartedPlaying; + int m_timeStoppedPlaying; +#endif }; } diff --git a/WebCore/platform/graphics/win/OpenTypeUtilities.cpp b/WebCore/platform/graphics/win/OpenTypeUtilities.cpp new file mode 100644 index 0000000..1951320 --- /dev/null +++ b/WebCore/platform/graphics/win/OpenTypeUtilities.cpp @@ -0,0 +1,387 @@ +/* + * 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 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 "OpenTypeUtilities.h" + +#include "SharedBuffer.h" + +namespace WebCore { + +struct BigEndianUShort { + operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; } + BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { } + unsigned short v; +}; + +struct BigEndianULong { + operator unsigned() const { return (v & 0xff) << 24 | (v & 0xff00) << 8 | (v & 0xff0000) >> 8 | v >> 24; } + BigEndianULong(unsigned u) : v((u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24) { } + unsigned v; +}; + +#pragma pack(1) + +struct EOTPrefix { + unsigned eotSize; + unsigned fontDataSize; + unsigned version; + unsigned flags; + UInt8 fontPANOSE[10]; + UInt8 charset; + UInt8 italic; + unsigned weight; + unsigned short fsType; + unsigned short magicNumber; + unsigned unicodeRange[4]; + unsigned codePageRange[2]; + unsigned checkSumAdjustment; + unsigned reserved[4]; + unsigned short padding1; +}; + +struct TableDirectoryEntry { + BigEndianULong tag; + BigEndianULong checkSum; + BigEndianULong offset; + BigEndianULong length; +}; + +struct sfntHeader { + Fixed version; + BigEndianUShort numTables; + BigEndianUShort searchRange; + BigEndianUShort entrySelector; + BigEndianUShort rangeShift; + TableDirectoryEntry tables[1]; +}; + +struct OS2Table { + BigEndianUShort version; + BigEndianUShort avgCharWidth; + BigEndianUShort weightClass; + BigEndianUShort widthClass; + BigEndianUShort fsType; + BigEndianUShort subscriptXSize; + BigEndianUShort subscriptYSize; + BigEndianUShort subscriptXOffset; + BigEndianUShort subscriptYOffset; + BigEndianUShort superscriptXSize; + BigEndianUShort superscriptYSize; + BigEndianUShort superscriptXOffset; + BigEndianUShort superscriptYOffset; + BigEndianUShort strikeoutSize; + BigEndianUShort strikeoutPosition; + BigEndianUShort familyClass; + UInt8 panose[10]; + BigEndianULong unicodeRange[4]; + UInt8 vendID[4]; + BigEndianUShort fsSelection; + BigEndianUShort firstCharIndex; + BigEndianUShort lastCharIndex; + BigEndianUShort typoAscender; + BigEndianUShort typoDescender; + BigEndianUShort typoLineGap; + BigEndianUShort winAscent; + BigEndianUShort winDescent; + BigEndianULong codePageRange[2]; + BigEndianUShort xHeight; + BigEndianUShort capHeight; + BigEndianUShort defaultChar; + BigEndianUShort breakChar; + BigEndianUShort maxContext; +}; + +struct headTable { + Fixed version; + Fixed fontRevision; + BigEndianULong checkSumAdjustment; + BigEndianULong magicNumber; + BigEndianUShort flags; + BigEndianUShort unitsPerEm; + long long created; + long long modified; + BigEndianUShort xMin; + BigEndianUShort xMax; + BigEndianUShort yMin; + BigEndianUShort yMax; + BigEndianUShort macStyle; + BigEndianUShort lowestRectPPEM; + BigEndianUShort fontDirectionHint; + BigEndianUShort indexToLocFormat; + BigEndianUShort glyphDataFormat; +}; + +struct nameRecord { + BigEndianUShort platformID; + BigEndianUShort encodingID; + BigEndianUShort languageID; + BigEndianUShort nameID; + BigEndianUShort length; + BigEndianUShort offset; +}; + +struct nameTable { + BigEndianUShort format; + BigEndianUShort count; + BigEndianUShort stringOffset; + nameRecord nameRecords[1]; +}; + +#pragma pack() + +static void appendBigEndianStringToEOTHeader(Vector<UInt8, 512>& eotHeader, const BigEndianUShort* string, unsigned short length) +{ + size_t size = eotHeader.size(); + eotHeader.resize(size + length + 2 * sizeof(unsigned short)); + UChar* dst = reinterpret_cast<UChar*>(eotHeader.data() + size); + unsigned i = 0; + dst[i++] = length; + unsigned numCharacters = length / 2; + for (unsigned j = 0; j < numCharacters; j++) + dst[i++] = string[j]; + dst[i] = 0; +} + +bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength) +{ + overlayDst = 0; + overlaySrc = 0; + overlayLength = 0; + + size_t dataLength = fontData->size(); + const char* data = fontData->data(); + + eotHeader.resize(sizeof(EOTPrefix)); + EOTPrefix* prefix = reinterpret_cast<EOTPrefix*>(eotHeader.data()); + + prefix->fontDataSize = dataLength; + prefix->version = 0x00020001; + prefix->flags = 0; + + if (dataLength < offsetof(sfntHeader, tables)) + return false; + + const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data); + + if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry)) + return false; + + bool haveOS2 = false; + bool haveHead = false; + bool haveName = false; + + const BigEndianUShort* familyName = 0; + unsigned short familyNameLength = 0; + const BigEndianUShort* subfamilyName = 0; + unsigned short subfamilyNameLength = 0; + const BigEndianUShort* fullName = 0; + unsigned short fullNameLength = 0; + const BigEndianUShort* versionString = 0; + unsigned short versionStringLength = 0; + + for (unsigned i = 0; i < sfnt->numTables; i++) { + unsigned tableOffset = sfnt->tables[i].offset; + unsigned tableLength = sfnt->tables[i].length; + + if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength) + return false; + + unsigned tableTag = sfnt->tables[i].tag; + switch (tableTag) { + case 'OS/2': + { + if (dataLength < tableOffset + sizeof(OS2Table)) + return false; + + haveOS2 = true; + const OS2Table* OS2 = reinterpret_cast<const OS2Table*>(data + tableOffset); + for (unsigned j = 0; j < 10; j++) + prefix->fontPANOSE[j] = OS2->panose[j]; + prefix->italic = OS2->fsSelection & 0x01; + prefix->weight = OS2->weightClass; + // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value. + // Since ATS does not enforce this on Mac OS X, we do not enforce it either. + prefix->fsType = 0; + for (unsigned j = 0; j < 4; j++) + prefix->unicodeRange[j] = OS2->unicodeRange[j]; + for (unsigned j = 0; j < 2; j++) + prefix->codePageRange[j] = OS2->codePageRange[j]; + break; + } + case 'head': + { + if (dataLength < tableOffset + sizeof(headTable)) + return false; + + haveHead = true; + const headTable* head = reinterpret_cast<const headTable*>(data + tableOffset); + prefix->checkSumAdjustment = head->checkSumAdjustment; + break; + } + case 'name': + { + if (dataLength < tableOffset + offsetof(nameTable, nameRecords)) + return false; + + haveName = true; + const nameTable* name = reinterpret_cast<const nameTable*>(data + tableOffset); + for (int j = 0; j < name->count; j++) { + if (dataLength < tableOffset + offsetof(nameTable, nameRecords) + (j + 1) * sizeof(nameRecord)) + return false; + if (name->nameRecords[j].platformID == 3 && name->nameRecords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) { + if (dataLength < tableOffset + name->stringOffset + name->nameRecords[j].offset + name->nameRecords[j].length) + return false; + + unsigned short nameLength = name->nameRecords[j].length; + const BigEndianUShort* nameString = reinterpret_cast<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRecords[j].offset); + + switch (name->nameRecords[j].nameID) { + case 1: + familyNameLength = nameLength; + familyName = nameString; + break; + case 2: + subfamilyNameLength = nameLength; + subfamilyName = nameString; + break; + case 4: + fullNameLength = nameLength; + fullName = nameString; + break; + case 5: + versionStringLength = nameLength; + versionString = nameString; + break; + default: + break; + } + } + } + break; + } + default: + break; + } + if (haveOS2 && haveHead && haveName) + break; + } + + prefix->charset = DEFAULT_CHARSET; + prefix->magicNumber = 0x504c; + prefix->reserved[0] = 0; + prefix->reserved[1] = 0; + prefix->reserved[2] = 0; + prefix->reserved[3] = 0; + prefix->padding1 = 0; + + appendBigEndianStringToEOTHeader(eotHeader, familyName, familyNameLength); + appendBigEndianStringToEOTHeader(eotHeader, subfamilyName, subfamilyNameLength); + appendBigEndianStringToEOTHeader(eotHeader, versionString, versionStringLength); + + // If possible, ensure that the family name is a prefix of the full name. + if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, familyNameLength)) { + overlaySrc = reinterpret_cast<const char*>(fullName) - data; + overlayDst = reinterpret_cast<const char*>(familyName) - data; + overlayLength = familyNameLength; + } + + appendBigEndianStringToEOTHeader(eotHeader, fullName, fullNameLength); + + unsigned short padding = 0; + eotHeader.append(reinterpret_cast<UInt8*>(&padding), sizeof(padding)); + + prefix->eotSize = eotHeader.size() + fontData->size(); + + return true; +} + +HANDLE renameAndActivateFont(SharedBuffer* fontData, const String& fontName) +{ + size_t originalDataSize = fontData->size(); + const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData->data()); + + unsigned t; + for (t = 0; t < sfnt->numTables; ++t) { + if (sfnt->tables[t].tag == 'name') + break; + } + if (t == sfnt->numTables) + return 0; + + const int nameRecordCount = 5; + + // Rounded up to a multiple of 4 to simplify the checksum calculation. + size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4; + + Vector<char> rewrittenFontData(fontData->size() + nameTableSize); + char* data = rewrittenFontData.data(); + memcpy(data, fontData->data(), originalDataSize); + + // Make the table directory entry point to the new 'name' table. + sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data); + rewrittenSfnt->tables[t].length = nameTableSize; + rewrittenSfnt->tables[t].offset = originalDataSize; + + // Write the new 'name' table after the original font data. + nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize); + name->format = 0; + name->count = nameRecordCount; + name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord); + for (unsigned i = 0; i < nameRecordCount; ++i) { + name->nameRecords[i].platformID = 3; + name->nameRecords[i].encodingID = 1; + name->nameRecords[i].languageID = 0x0409; + name->nameRecords[i].offset = 0; + name->nameRecords[i].length = fontName.length() * sizeof(UChar); + } + + // The required 'name' record types: Family, Style, Unique, Full and PostScript. + name->nameRecords[0].nameID = 1; + name->nameRecords[1].nameID = 2; + name->nameRecords[2].nameID = 3; + name->nameRecords[3].nameID = 4; + name->nameRecords[4].nameID = 6; + + for (unsigned i = 0; i < fontName.length(); ++i) + reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i]; + + // Update the table checksum in the directory entry. + rewrittenSfnt->tables[t].checkSum = 0; + for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i) + rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i]; + + DWORD numFonts = 0; + HANDLE fontHandle = AddFontMemResourceEx(data, originalDataSize + nameTableSize, 0, &numFonts); + + if (fontHandle && numFonts != 1) { + RemoveFontMemResourceEx(fontHandle); + return 0; + } + + return fontHandle; +} + +} diff --git a/WebCore/platform/graphics/win/OpenTypeUtilities.h b/WebCore/platform/graphics/win/OpenTypeUtilities.h new file mode 100644 index 0000000..ab35551 --- /dev/null +++ b/WebCore/platform/graphics/win/OpenTypeUtilities.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 COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OpenTypeUtilities_h +#define OpenTypeUtilities_h + +#include "PlatformString.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class SharedBuffer; + +bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength); +HANDLE renameAndActivateFont(SharedBuffer*, const String&); + +} // namespace WebCore + +#endif // OpenTypeUtilities_h diff --git a/WebCore/platform/graphics/win/QTMovieWin.cpp b/WebCore/platform/graphics/win/QTMovieWin.cpp index 80c6d50..8eee41b 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.cpp +++ b/WebCore/platform/graphics/win/QTMovieWin.cpp @@ -30,6 +30,7 @@ // Put Movies.h first so build failures here point clearly to QuickTime #include <Movies.h> +#include <QuickTimeComponents.h> #include <GXMath.h> #include <QTML.h> @@ -59,6 +60,7 @@ union UppParam { static MovieDrawingCompleteUPP gMovieDrawingCompleteUPP = 0; static HashSet<QTMovieWinPrivate*>* gTaskList; static Vector<CFStringRef>* gSupportedTypes = 0; +static SInt32 quickTimeVersion = 0; static void updateTaskTimer(int maxInterval = 1000) { @@ -82,16 +84,19 @@ public: void startTask(); void endTask(); + void createMovieController(); void registerDrawingCallback(); void drawingComplete(); void updateGWorld(); void createGWorld(); void deleteGWorld(); + void clearGWorld(); void setSize(int, int); QTMovieWin* m_movieWin; Movie m_movie; + MovieController m_movieController; bool m_tasking; QTMovieWinClient* m_client; long m_loadState; @@ -112,6 +117,7 @@ public: QTMovieWinPrivate::QTMovieWinPrivate() : m_movieWin(0) , m_movie(0) + , m_movieController(0) , m_tasking(false) , m_client(0) , m_loadState(0) @@ -135,6 +141,8 @@ QTMovieWinPrivate::~QTMovieWinPrivate() endTask(); if (m_gWorld) deleteGWorld(); + if (m_movieController) + DisposeMovieController(m_movieController); if (m_movie) DisposeMovie(m_movie); } @@ -175,8 +183,12 @@ void QTMovieWinPrivate::task() { ASSERT(m_tasking); - if (!m_loadError) - MoviesTask(m_movie, 0); + if (!m_loadError) { + if (m_movieController) + MCIdle(m_movieController); + else + MoviesTask(m_movie, 0); + } // GetMovieLoadState documentation says that you should not call it more often than every quarter of a second. if (systemTime() >= m_lastLoadStateCheckTime + 0.25 || m_loadError) { @@ -184,7 +196,15 @@ void QTMovieWinPrivate::task() // This is different from QTKit API and seems strange. long loadState = m_loadError ? kMovieLoadStateError : GetMovieLoadState(m_movie); if (loadState != m_loadState) { + + // we only need to erase the movie gworld when the load state changes to loaded while it + // is visible as the gworld is destroyed/created when visibility changes + if (loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded && m_visible) + clearGWorld(); + m_loadState = loadState; + if (!m_movieController && m_loadState >= kMovieLoadStateLoaded) + createMovieController(); m_client->movieLoadStateChanged(m_movieWin); } m_lastLoadStateCheckTime = systemTime(); @@ -209,6 +229,27 @@ void QTMovieWinPrivate::task() endTask(); } +void QTMovieWinPrivate::createMovieController() +{ + Rect bounds; + long flags; + + if (!m_movie) + return; + + if (m_movieController) + DisposeMovieController(m_movieController); + + GetMovieBox(m_movie, &bounds); + flags = mcTopLeftMovie | mcNotVisible; + m_movieController = NewMovieController(m_movie, &bounds, flags); + if (!m_movieController) + return; + + MCSetControllerPort(m_movieController, m_gWorld); + MCSetControllerAttached(m_movieController, false); +} + void QTMovieWinPrivate::registerDrawingCallback() { UppParam param; @@ -218,7 +259,7 @@ void QTMovieWinPrivate::registerDrawingCallback() void QTMovieWinPrivate::drawingComplete() { - if (!m_gWorld) + if (!m_gWorld || m_loadState < kMovieLoadStateLoaded) return; m_client->movieNewImageAvailable(m_movieWin); } @@ -257,12 +298,36 @@ void QTMovieWinPrivate::createGWorld() if (err) return; GetMovieGWorld(m_movie, &m_savedGWorld, 0); + if (m_movieController) + MCSetControllerPort(m_movieController, m_gWorld); SetMovieGWorld(m_movie, m_gWorld, 0); bounds.right = m_width; bounds.bottom = m_height; + if (m_movieController) + MCSetControllerBoundsRect(m_movieController, &bounds); SetMovieBox(m_movie, &bounds); } +void QTMovieWinPrivate::clearGWorld() +{ + if (!m_movie||!m_gWorld) + return; + + GrafPtr savePort; + GetPort(&savePort); + MacSetPort((GrafPtr)m_gWorld); + + Rect bounds; + bounds.top = 0; + bounds.left = 0; + bounds.right = m_gWorldWidth; + bounds.bottom = m_gWorldHeight; + EraseRect(&bounds); + + MacSetPort(savePort); +} + + void QTMovieWinPrivate::setSize(int width, int height) { if (m_width == width && m_height == height) @@ -276,6 +341,8 @@ void QTMovieWinPrivate::setSize(int width, int height) bounds.left = 0; bounds.right = width; bounds.bottom = height; + if (m_movieController) + MCSetControllerBoundsRect(m_movieController, &bounds); SetMovieBox(m_movie, &bounds); updateGWorld(); } @@ -283,6 +350,8 @@ void QTMovieWinPrivate::setSize(int width, int height) void QTMovieWinPrivate::deleteGWorld() { ASSERT(m_gWorld); + if (m_movieController) + MCSetControllerPort(m_movieController, m_savedGWorld); if (m_movie) SetMovieGWorld(m_movie, m_savedGWorld, 0); m_savedGWorld = 0; @@ -308,24 +377,37 @@ QTMovieWin::~QTMovieWin() void QTMovieWin::play() { - StartMovie(m_private->m_movie); + if (m_private->m_movieController) + MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)GetMoviePreferredRate(m_private->m_movie)); + else + StartMovie(m_private->m_movie); m_private->startTask(); } void QTMovieWin::pause() { - StopMovie(m_private->m_movie); + if (m_private->m_movieController) + MCDoAction(m_private->m_movieController, mcActionPlay, 0); + else + StopMovie(m_private->m_movie); updateTaskTimer(); } float QTMovieWin::rate() const { + if (!m_private->m_movie) + return 0; return FixedToFloat(GetMovieRate(m_private->m_movie)); } void QTMovieWin::setRate(float rate) { - SetMovieRate(m_private->m_movie, FloatToFixed(rate)); + if (!m_private->m_movie) + return; + if (m_private->m_movieController) + MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate)); + else + SetMovieRate(m_private->m_movie, FloatToFixed(rate)); updateTaskTimer(); } @@ -353,19 +435,26 @@ void QTMovieWin::setCurrentTime(float time) const return; m_private->m_seeking = true; TimeScale scale = GetMovieTimeScale(m_private->m_movie); - SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale)); + if (m_private->m_movieController){ + QTRestartAtTimeRecord restart = { time * scale , 0 }; + MCDoAction(m_private->m_movieController, mcActionRestartAtTime, (void *)&restart); + } else + SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale)); updateTaskTimer(); } void QTMovieWin::setVolume(float volume) { + if (!m_private->m_movie) + return; SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256)); } unsigned QTMovieWin::dataSize() const { - // FIXME: How to get this? - return 1000; + if (!m_private->m_movie) + return 0; + return GetMovieDataSize(m_private->m_movie, 0, GetMovieDuration(m_private->m_movie)); } float QTMovieWin::maxTimeLoaded() const @@ -385,8 +474,10 @@ long QTMovieWin::loadState() const void QTMovieWin::getNaturalSize(int& width, int& height) { - Rect rect; - GetMovieNaturalBoundsRect(m_private->m_movie, &rect); + Rect rect = { 0, }; + + if (m_private->m_movie) + GetMovieNaturalBoundsRect(m_private->m_movie, &rect); width = rect.right; height = rect.bottom; } @@ -428,6 +519,9 @@ void QTMovieWin::load(const UChar* url, int len) m_private->endTask(); if (m_private->m_gWorld) m_private->deleteGWorld(); + if (m_private->m_movieController) + DisposeMovieController(m_private->m_movieController); + m_private->m_movieController = 0; DisposeMovie(m_private->m_movie); m_private->m_movie = 0; } @@ -630,18 +724,89 @@ static void initializeSupportedTypes() { if (gSupportedTypes) return; - // FIXME: This list might not be complete. - // There must be some way to get it out from QuickTime. + gSupportedTypes = new Vector<CFStringRef>; - gSupportedTypes->append(CFSTR("video/3gpp")); - gSupportedTypes->append(CFSTR("video/3gpp2")); - gSupportedTypes->append(CFSTR("video/mp4")); - gSupportedTypes->append(CFSTR("video/mpeg")); + if (quickTimeVersion < minimumQuickTimeVersion) { + LOG_ERROR("QuickTime version %x detected, at least %x required. Returning empty list of supported media MIME types.", quickTimeVersion, minimumQuickTimeVersion); + return; + } + + // QuickTime doesn't have an importer for video/quicktime. Add it manually. gSupportedTypes->append(CFSTR("video/quicktime")); - gSupportedTypes->append(CFSTR("audio/ac3")); - gSupportedTypes->append(CFSTR("audio/aiff")); - gSupportedTypes->append(CFSTR("audio/basic")); - gSupportedTypes->append(CFSTR("audio/mpeg")); + + for (int index = 0; index < 2; index++) { + ComponentDescription findCD; + + // look at all movie importers that can import in place and are installed. + findCD.componentType = MovieImportType; + findCD.componentSubType = 0; + findCD.componentManufacturer = 0; + findCD.componentFlagsMask = cmpIsMissing | movieImportSubTypeIsFileExtension | canMovieImportInPlace | dontAutoFileMovieImport; + + // look at those registered by HFS file types the first time through, by file extension the second time + findCD.componentFlags = canMovieImportInPlace | (index ? movieImportSubTypeIsFileExtension : 0); + + long componentCount = CountComponents(&findCD); + if (!componentCount) + continue; + + Component comp = 0; + while (comp = FindNextComponent(comp, &findCD)) { + // Does this component have a MIME type container? + ComponentDescription infoCD; + OSErr err = GetComponentInfo(comp, &infoCD, nil /*name*/, nil /*info*/, nil /*icon*/); + if (err) + continue; + if (!(infoCD.componentFlags & hasMovieImportMIMEList)) + continue; + QTAtomContainer mimeList = NULL; + err = MovieImportGetMIMETypeList((ComponentInstance)comp, &mimeList); + if (err || !mimeList) + continue; + + // Grab every type from the container. + QTLockContainer(mimeList); + int typeCount = QTCountChildrenOfType(mimeList, kParentAtomIsContainer, kMimeInfoMimeTypeTag); + for (int typeIndex = 1; typeIndex <= typeCount; typeIndex++) { + QTAtom mimeTag = QTFindChildByIndex(mimeList, 0, kMimeInfoMimeTypeTag, typeIndex, NULL); + if (!mimeTag) + continue; + char* atomData; + long typeLength; + if (noErr != QTGetAtomDataPtr(mimeList, mimeTag, &typeLength, &atomData)) + continue; + + char typeBuffer[256]; + if (typeLength >= sizeof(typeBuffer)) + continue; + memcpy(typeBuffer, atomData, typeLength); + typeBuffer[typeLength] = 0; + + // Only add "audio/..." and "video/..." types. + if (strncmp(typeBuffer, "audio/", 6) && strncmp(typeBuffer, "video/", 6)) + continue; + + CFStringRef cfMimeType = CFStringCreateWithCString(NULL, typeBuffer, kCFStringEncodingUTF8); + if (!cfMimeType) + continue; + + // Only add each type once. + bool alreadyAdded = false; + for (int addedIndex = 0; addedIndex < gSupportedTypes->size(); addedIndex++) { + CFStringRef type = gSupportedTypes->at(addedIndex); + if (kCFCompareEqualTo == CFStringCompare(cfMimeType, type, kCFCompareCaseInsensitive)) { + alreadyAdded = true; + break; + } + } + if (!alreadyAdded) + gSupportedTypes->append(cfMimeType); + else + CFRelease(cfMimeType); + } + DisposeHandle(mimeList); + } + } } unsigned QTMovieWin::countSupportedTypes() @@ -676,15 +841,14 @@ bool QTMovieWin::initializeQuickTime() initialized = true; // Initialize and check QuickTime version OSErr result = InitializeQTML(0); - SInt32 version = 0; if (result == noErr) - result = Gestalt(gestaltQuickTime, &version); + result = Gestalt(gestaltQuickTime, &quickTimeVersion); if (result != noErr) { LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support."); return false; } - if (version < minimumQuickTimeVersion) { - LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", version, minimumQuickTimeVersion); + if (quickTimeVersion < minimumQuickTimeVersion) { + LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", quickTimeVersion, minimumQuickTimeVersion); return false; } EnterMovies(); diff --git a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp index 0c31672..8b5ab87 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp @@ -34,7 +34,9 @@ #include "FontCache.h" #include "FloatRect.h" #include "FontDescription.h" +#include "PlatformString.h" #include <wtf/MathExtras.h> +#include <wtf/RetainPtr.h> #include <unicode/uchar.h> #include <unicode/unorm.h> #include <ApplicationServices/ApplicationServices.h> @@ -54,32 +56,9 @@ void SimpleFontData::platformInit() m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; - - if (m_font.useGDI()) { - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - OUTLINETEXTMETRIC metrics; - GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); - TEXTMETRIC& textMetrics = metrics.otmTextMetrics; - m_ascent = textMetrics.tmAscent; - m_descent = textMetrics.tmDescent; - m_lineGap = textMetrics.tmExternalLeading; - m_lineSpacing = m_ascent + m_descent + m_lineGap; - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. - - GLYPHMETRICS gm; - MAT2 mat = { 1, 0, 0, 1 }; - DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); - if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) - m_xHeight = gm.gmptGlyphOrigin.y; - - m_unitsPerEm = metrics.otmEMSquare; - - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); - - return; - } + + if (m_font.useGDI()) + return initGDIFont(); CGFontRef font = m_font.cgFont(); int iAscent = CGFontGetAscent(font); @@ -139,29 +118,13 @@ void SimpleFontData::platformInit() void SimpleFontData::platformDestroy() { - if (!isCustomFont()) { - DeleteObject(m_font.hfont()); - CGFontRelease(m_font.cgFont()); - } - - // We don't hash this on Win32, so it's effectively owned by us. - delete m_smallCapsFontData; - - ScriptFreeCache(&m_scriptCache); - delete m_scriptFontProperties; + platformCommonDestroy(); } float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { - if (m_font.useGDI()) { - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - int width; - GetCharWidthI(hdc, glyph, 1, 0, &width); - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); - return width; - } + if (m_font.useGDI()) + return widthForGDIGlyph(glyph); CGFontRef font = m_font.cgFont(); float pointSize = m_font.size(); diff --git a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp index e7b2c81..07d5305 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp @@ -34,7 +34,10 @@ #include "Font.h" #include "FontCache.h" #include "FontDescription.h" +#include "MathExtras.h" #include "NotImplemented.h" +#include <cairo.h> +#include <cairo-win32.h> #include <mlang.h> #include <tchar.h> @@ -45,58 +48,84 @@ void SimpleFontData::platformInit() m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; - - if (m_font.useGDI()) { - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - OUTLINETEXTMETRIC metrics; - GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); - TEXTMETRIC& textMetrics = metrics.otmTextMetrics; - m_ascent = textMetrics.tmAscent; - m_descent = textMetrics.tmDescent; - m_lineGap = textMetrics.tmExternalLeading; - m_lineSpacing = m_ascent + m_descent + m_lineGap; - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. + m_syntheticBoldOffset = 0; + if (m_font.useGDI()) + return initGDIFont(); + + HDC hdc = GetDC(0); + SaveDC(hdc); + + cairo_scaled_font_t* scaledFont = m_font.scaledFont(); + const double metricsMultiplier = cairo_win32_scaled_font_get_metrics_factor(scaledFont) * m_font.size(); + + cairo_win32_scaled_font_select_font(scaledFont, hdc); + + TEXTMETRIC textMetrics; + GetTextMetrics(hdc, &textMetrics); + m_ascent = lroundf(textMetrics.tmAscent * metricsMultiplier); + m_descent = lroundf(textMetrics.tmDescent * metricsMultiplier); + m_xHeight = m_ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. + m_lineGap = lroundf(textMetrics.tmExternalLeading * metricsMultiplier); + m_lineSpacing = m_ascent + m_descent + m_lineGap; + + OUTLINETEXTMETRIC metrics; + if (GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics) > 0) { + // This is a TrueType font. We might be able to get an accurate xHeight GLYPHMETRICS gm; MAT2 mat = { 1, 0, 0, 1 }; DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) - m_xHeight = gm.gmptGlyphOrigin.y; - - m_unitsPerEm = metrics.otmEMSquare; + m_xHeight = gm.gmptGlyphOrigin.y * metricsMultiplier; + } - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); + cairo_win32_scaled_font_done_font(scaledFont); - return; - } + m_isSystemFont = false; + m_scriptCache = 0; + m_scriptFontProperties = 0; - // FIXME: This section should determine font dimensions (see CG implementation). - notImplemented(); + RestoreDC(hdc, -1); + ReleaseDC(0, hdc); } void SimpleFontData::platformDestroy() { - notImplemented(); + cairo_font_face_destroy(m_font.fontFace()); + cairo_scaled_font_destroy(m_font.scaledFont()); + + DeleteObject(m_font.hfont()); + + platformCommonDestroy(); } float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { - if (m_font.useGDI()) { - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - int width; - GetCharWidthI(hdc, glyph, 1, 0, &width); - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); - return width; - } + if (m_font.useGDI()) + return widthForGDIGlyph(glyph); + + HDC hdc = GetDC(0); + SaveDC(hdc); + + cairo_scaled_font_t* scaledFont = m_font.scaledFont(); + cairo_win32_scaled_font_select_font(scaledFont, hdc); - // FIXME: Flesh out with Cairo/win32 font implementation - notImplemented(); + int width; + GetCharWidthI(hdc, glyph, 1, 0, &width); - return 0; + cairo_win32_scaled_font_done_font(scaledFont); + + RestoreDC(hdc, -1); + ReleaseDC(0, hdc); + + const double metricsMultiplier = cairo_win32_scaled_font_get_metrics_factor(scaledFont) * m_font.size(); + return width * metricsMultiplier; +} + +void SimpleFontData::setFont(cairo_t* cr) const +{ + ASSERT(cr); + m_font.setFont(cr); } } diff --git a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp index 344d964..0e9f9fb 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp @@ -60,6 +60,42 @@ bool SimpleFontData::shouldApplyMacAscentHack() return g_shouldApplyMacAscentHack; } +void SimpleFontData::initGDIFont() +{ + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); + OUTLINETEXTMETRIC metrics; + GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); + TEXTMETRIC& textMetrics = metrics.otmTextMetrics; + m_ascent = textMetrics.tmAscent; + m_descent = textMetrics.tmDescent; + m_lineGap = textMetrics.tmExternalLeading; + m_lineSpacing = m_ascent + m_descent + m_lineGap; + m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. + + GLYPHMETRICS gm; + MAT2 mat = { 1, 0, 0, 1 }; + DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); + if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) + m_xHeight = gm.gmptGlyphOrigin.y; + + m_unitsPerEm = metrics.otmEMSquare; + + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + + return; +} + +void SimpleFontData::platformCommonDestroy() +{ + // We don't hash this on Win32, so it's effectively owned by us. + delete m_smallCapsFontData; + + ScriptFreeCache(&m_scriptCache); + delete m_scriptFontProperties; +} + SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const { if (!m_smallCapsFontData) { @@ -73,7 +109,7 @@ SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDes GetObject(m_font.hfont(), sizeof(LOGFONT), &winfont); winfont.lfHeight = -lroundf(smallCapsHeight * (m_font.useGDI() ? 1 : 32)); HFONT hfont = CreateFontIndirect(&winfont); - m_smallCapsFontData = new SimpleFontData(FontPlatformData(hfont, smallCapsHeight, fontDescription.bold(), fontDescription.italic(), m_font.useGDI())); + m_smallCapsFontData = new SimpleFontData(FontPlatformData(hfont, smallCapsHeight, m_font.syntheticBold(), m_font.syntheticOblique(), m_font.useGDI())); } } return m_smallCapsFontData; @@ -137,6 +173,17 @@ void SimpleFontData::determinePitch() ReleaseDC(0, dc); } +float SimpleFontData::widthForGDIGlyph(Glyph glyph) const +{ + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); + int width; + GetCharWidthI(hdc, glyph, 1, 0, &width); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + return width + m_syntheticBoldOffset; +} + SCRIPT_FONTPROPERTIES* SimpleFontData::scriptFontProperties() const { if (!m_scriptFontProperties) { diff --git a/WebCore/platform/graphics/win/UniscribeController.cpp b/WebCore/platform/graphics/win/UniscribeController.cpp index 876ff43..371bc51 100644 --- a/WebCore/platform/graphics/win/UniscribeController.cpp +++ b/WebCore/platform/graphics/win/UniscribeController.cpp @@ -299,7 +299,9 @@ bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const S offsetX = roundf(offsetX); offsetY = roundf(offsetY); } - + + advance += fontData->m_syntheticBoldOffset; + // We special case spaces in two ways when applying word rounding. // First, we round spaces to an adjusted width in all fonts. // Second, in fixed-pitch fonts we ensure that all glyphs that @@ -361,7 +363,7 @@ bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const S // as well, so that when the time comes to draw those glyphs, we can apply the appropriate // translation. if (glyphBuffer) { - FloatSize size(offsetX, offsetY); + FloatSize size(offsetX, -offsetY); glyphBuffer->add(glyph, fontData, advance, &size); } @@ -422,25 +424,9 @@ bool UniscribeController::shape(const UChar* str, int len, SCRIPT_ITEM item, con if (FAILED(shapeResult)) return false; - - // FIXME: We need to do better than this. Falling back on the entire item is not good enough. - // We may still have missing glyphs even if we succeeded. We need to treat missing glyphs as - // a failure so that we will fall back to another font. - bool containsMissingGlyphs = false; - SCRIPT_FONTPROPERTIES* fontProperties = fontData->scriptFontProperties(); - for (int i = 0; i < glyphCount; i++) { - WORD glyph = glyphs[i]; - if (glyph == fontProperties->wgDefault) { - containsMissingGlyphs = true; - break; - } - } - - if (containsMissingGlyphs) - return false; - glyphs.resize(glyphCount); - visualAttributes.resize(glyphCount); + glyphs.shrink(glyphCount); + visualAttributes.shrink(glyphCount); return true; } diff --git a/WebCore/platform/graphics/wx/AffineTransformWx.cpp b/WebCore/platform/graphics/wx/AffineTransformWx.cpp index b9c504d..12485ae 100644 --- a/WebCore/platform/graphics/wx/AffineTransformWx.cpp +++ b/WebCore/platform/graphics/wx/AffineTransformWx.cpp @@ -37,7 +37,7 @@ namespace WebCore { #if USE(WXGC) -AffineTransform::AffineTransform(const wxGraphicsMatrix &matrix) +AffineTransform::AffineTransform(const PlatformAffineTransform& matrix) { m_transform = matrix; } @@ -48,8 +48,8 @@ AffineTransform::AffineTransform(double a, double b, double c, double d, double #if USE(WXGC) wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); m_transform = renderer->CreateMatrix(); - m_transform.Set(a, b, c, d, e, f); #endif + setMatrix(a, b, c, d, e, f); } AffineTransform::AffineTransform() @@ -69,6 +69,13 @@ AffineTransform AffineTransform::inverse() const return *this; } +void AffineTransform::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 { notImplemented(); @@ -132,6 +139,9 @@ bool AffineTransform::operator== (const AffineTransform &other) const { #if USE(WXGC) return m_transform.IsEqual((wxGraphicsMatrix)other); +#else + notImplemented(); + return true; #endif } @@ -154,4 +164,88 @@ AffineTransform::operator wxGraphicsMatrix() const } #endif +double AffineTransform::a() const +{ + double a = 0; +#if USE(WXGC) + m_transform.Get(&a); +#endif + return a; +} + +void AffineTransform::setA(double a) +{ + setMatrix(a, b(), c(), d(), e(), f()); +} + +double AffineTransform::b() const +{ + double b = 0; +#if USE(WXGC) + m_transform.Get(&b); +#endif + return b; +} + +void AffineTransform::setB(double b) +{ + setMatrix(a(), b, c(), d(), e(), f()); +} + +double AffineTransform::c() const +{ + double c = 0; +#if USE(WXGC) + m_transform.Get(&c); +#endif + return c; +} + +void AffineTransform::setC(double c) +{ + setMatrix(a(), b(), c, d(), e(), f()); +} + +double AffineTransform::d() const +{ + double d = 0; +#if USE(WXGC) + m_transform.Get(&d); +#endif + return d; +} + +void AffineTransform::setD(double d) +{ + setMatrix(a(), b(), c(), d, e(), f()); +} + +double AffineTransform::e() const +{ + double e = 0; +#if USE(WXGC) + m_transform.Get(&e); +#endif + return e; +} + +void AffineTransform::setE(double e) +{ + setMatrix(a(), b(), c(), d(), e, f()); +} + +double AffineTransform::f() const +{ + double f = 0; +#if USE(WXGC) + m_transform.Get(&f); +#endif + return f; +} + +void AffineTransform::setF(double f) +{ + setMatrix(a(), b(), c(), d(), e(), f); +} + } diff --git a/WebCore/platform/graphics/wx/FontCacheWx.cpp b/WebCore/platform/graphics/wx/FontCacheWx.cpp index eb41c89..db107e4 100755 --- a/WebCore/platform/graphics/wx/FontCacheWx.cpp +++ b/WebCore/platform/graphics/wx/FontCacheWx.cpp @@ -64,10 +64,9 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD return new FontPlatformData(fontDescription,family); } -bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family) +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) { notImplemented(); - return true; } } diff --git a/WebCore/platform/graphics/wx/FontPlatformData.h b/WebCore/platform/graphics/wx/FontPlatformData.h index f48068b..e3a3cce 100644 --- a/WebCore/platform/graphics/wx/FontPlatformData.h +++ b/WebCore/platform/graphics/wx/FontPlatformData.h @@ -41,11 +41,9 @@ namespace WebCore { class FontPlatformData { public: - class Deleted {}; - enum FontState { UNINITIALIZED, DELETED, VALID }; - FontPlatformData(Deleted) + FontPlatformData(WTF::HashTableDeletedValueType) : m_fontState(DELETED) { } @@ -76,7 +74,7 @@ public: case UNINITIALIZED: return 0; case VALID: - return m_fontHash; + return computeHash(); } } @@ -87,11 +85,19 @@ public: else return m_fontState == other.m_fontState; } + + bool isHashTableDeletedValue() const { return m_fontState == DELETED; } unsigned computeHash() const { - wxCharBuffer charBuffer(m_font.GetNativeFontInfoDesc().mb_str(wxConvUTF8)); - const char* contents = charBuffer; - return StringImpl::computeHash( (UChar*)contents, strlen(contents)); + ASSERT(m_font.Ok()); + + // 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 + uintptr_t hashCodes[6] = { m_font.GetPointSize(), m_font.GetFamily(), m_font.GetStyle(), + m_font.GetWeight(), m_font.GetUnderlined(), + StringImpl::computeHash(m_font.GetFaceName().mb_str(wxConvUTF8)) }; + + return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); } private: diff --git a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp index f3fb480..7162eab 100755 --- a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp +++ b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp @@ -54,9 +54,9 @@ static wxFontFamily fontFamilyToWxFontFamily(const int family) } } -static wxFontWeight fontWeightToWxFontWeight(bool isBold) +static wxFontWeight fontWeightToWxFontWeight(FontWeight weight) { - if (isBold) + if (weight >= FontWeight600) return wxFONTWEIGHT_BOLD; return wxFONTWEIGHT_NORMAL; @@ -81,17 +81,17 @@ FontPlatformData::FontPlatformData(const FontDescription& desc, const AtomicStri m_font = wxFont( wxSize(0, -desc.computedPixelSize()), fontFamilyToWxFontFamily(desc.genericFamily()), italicToWxFontStyle(desc.italic()), - fontWeightToWxFontWeight(desc.bold()), + fontWeightToWxFontWeight(desc.weight()), false, - family.domString() + family.string() ); #else m_font = wxFont( desc.computedPixelSize(), fontFamilyToWxFontFamily(desc.genericFamily()), italicToWxFontStyle(desc.italic()), - fontWeightToWxFontWeight(desc.bold()), + fontWeightToWxFontWeight(desc.weight()), false, - family.domString() + family.string() ); #endif m_fontState = VALID; diff --git a/WebCore/platform/graphics/wx/FontWx.cpp b/WebCore/platform/graphics/wx/FontWx.cpp index e94ae3b..07223e9 100644 --- a/WebCore/platform/graphics/wx/FontWx.cpp +++ b/WebCore/platform/graphics/wx/FontWx.cpp @@ -33,9 +33,9 @@ #include "NotImplemented.h" #include "SimpleFontData.h" -#include "fontprops.h" -#include <wx/defs.h> #include <wx/dcclient.h> +#include "fontprops.h" +#include "non-kerned-drawing.h" namespace WebCore { @@ -45,33 +45,11 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo // prepare DC Color color = graphicsContext->fillColor(); -#if USE(WXGC) - wxGCDC* dc = (wxGCDC*)graphicsContext->platformContext(); - wxFont wxfont = font->getWxFont(); - if (wxfont.IsOk()) - dc->SetFont(wxfont); - dc->SetTextForeground(color); -#else - wxDC* dc = graphicsContext->platformContext(); - dc->SetTextBackground(color); - dc->SetTextForeground(color); - dc->SetFont(font->getWxFont()); -#endif - - // convert glyphs to wxString - GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); - int offset = point.x(); - wxString text = wxEmptyString; - for (unsigned i = 0; i < numGlyphs; i++) { - text = text.Append((wxChar)glyphs[i]); - offset += glyphBuffer.advanceAt(from + i); - } - - // the y point is actually the bottom point of the text, turn it into the top - float height = font->ascent() - font->descent(); - wxCoord ypoint = (wxCoord) (point.y() - height); - - dc->DrawText(text, (wxCoord)point.x(), ypoint); + // We can't use wx drawing methods on Win/Linux because they automatically kern text + // so we've created a function with platform dependent drawing implementations that + // will hopefully be folded into wx once the API has solidified. + // see platform/wx/wxcode/<platform> for the implementations. + drawTextWithSpacing(graphicsContext, font, color, glyphBuffer, from, numGlyphs, point); } FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const diff --git a/WebCore/platform/graphics/win/FontCairoWin.cpp b/WebCore/platform/graphics/wx/GradientWx.cpp index 302e79d..fc4661e 100644 --- a/WebCore/platform/graphics/win/FontCairoWin.cpp +++ b/WebCore/platform/graphics/wx/GradientWx.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Kevin Ollivier <kevino@theolliviers.com> All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,19 +24,27 @@ */ #include "config.h" -#include "Font.h" +#include "Gradient.h" -#include "GlyphBuffer.h" -#include "GraphicsContext.h" +#include "CSSParser.h" #include "NotImplemented.h" -#include "SimpleFontData.h" namespace WebCore { -void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, - int from, int numGlyphs, const FloatPoint& point) const +void Gradient::platformDestroy() { notImplemented(); } +PlatformGradient Gradient::platformGradient() +{ + notImplemented(); + return 0; } + +void Gradient::fill(GraphicsContext*, const FloatRect&) +{ + notImplemented(); +} + +} //namespace diff --git a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp index 78149ad..435e7ce 100644 --- a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp +++ b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp @@ -252,22 +252,12 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points delete [] polygon; } -void GraphicsContext::fillRect(const IntRect& rect, const Color& color) -{ - if (paintingDisabled()) - return; - - m_data->context->SetPen(*wxTRANSPARENT_PEN); - m_data->context->SetBrush(wxBrush(color)); - m_data->context->DrawRectangle(rect.x(), rect.y(), rect.width(), rect.height()); -} - void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) { if (paintingDisabled()) return; - m_data->context->SetPen(wxPen(color)); + m_data->context->SetPen(*wxTRANSPARENT_PEN); m_data->context->SetBrush(wxBrush(color)); m_data->context->DrawRectangle(rect.x(), rect.y(), rect.width(), rect.height()); } @@ -288,7 +278,7 @@ void GraphicsContext::drawFocusRing(const Color& color) notImplemented(); } -void GraphicsContext::clip(const IntRect& r) +void GraphicsContext::clip(const FloatRect& r) { wxWindowDC* windc = dynamic_cast<wxWindowDC*>(m_data->context); wxPoint pos(0, 0); @@ -334,7 +324,7 @@ void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool pr return; IntPoint endPoint = origin + IntSize(width, 0); - m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle()))); + m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), wxSOLID)); m_data->context->DrawLine(origin.x(), origin.y(), endPoint.x(), endPoint.y()); } @@ -354,6 +344,11 @@ void GraphicsContext::clip(const Path&) notImplemented(); } +void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) +{ + notImplemented(); +} + AffineTransform GraphicsContext::getCTM() const { notImplemented(); @@ -422,6 +417,16 @@ void GraphicsContext::setCompositeOperation(CompositeOperator op) m_data->context->SetLogicalFunction(getWxCompositingOperation(op, false)); } +void GraphicsContext::beginPath() +{ + notImplemented(); +} + +void GraphicsContext::addPath(const Path& path) +{ + notImplemented(); +} + void GraphicsContext::setPlatformStrokeColor(const Color& color) { if (paintingDisabled()) @@ -466,4 +471,33 @@ void GraphicsContext::setUseAntialiasing(bool enable) notImplemented(); } +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) +{ +} + +InterpolationQuality GraphicsContext::imageInterpolationQuality() const +{ + return InterpolationDefault; +} + +void GraphicsContext::fillPath() +{ +} + +void GraphicsContext::strokePath() +{ +} + +void GraphicsContext::drawPath() +{ + fillPath(); + strokePath(); +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; +} + } diff --git a/WebCore/platform/graphics/wx/ImageBufferData.h b/WebCore/platform/graphics/wx/ImageBufferData.h new file mode 100644 index 0000000..d4a6114 --- /dev/null +++ b/WebCore/platform/graphics/wx/ImageBufferData.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 Kevin Ollivier. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageBufferData_h +#define ImageBufferData_h + + +#include "OwnPtr.h" + +namespace WebCore { + +class IntSize; + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/WebCore/platform/graphics/wx/ImageBufferWx.cpp b/WebCore/platform/graphics/wx/ImageBufferWx.cpp index bc54a88..ea3dfe8 100644 --- a/WebCore/platform/graphics/wx/ImageBufferWx.cpp +++ b/WebCore/platform/graphics/wx/ImageBufferWx.cpp @@ -25,13 +25,22 @@ #include "config.h" #include "ImageBuffer.h" + #include "GraphicsContext.h" +#include "ImageData.h" +#include "NotImplemented.h" namespace WebCore { -std::auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize&, bool grayScale) +ImageBufferData::ImageBufferData(const IntSize&) { - return std::auto_ptr<ImageBuffer>(new ImageBuffer()); +} + +ImageBuffer::ImageBuffer(const IntSize&, bool grayScale, bool& success) : + m_data(IntSize()) +{ + notImplemented(); + success = false; } ImageBuffer::~ImageBuffer() @@ -40,7 +49,31 @@ ImageBuffer::~ImageBuffer() GraphicsContext* ImageBuffer::context() const { + notImplemented(); return 0; } +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect&) const +{ + notImplemented(); + return 0; +} + +void ImageBuffer::putImageData(ImageData*, const IntRect&, const IntPoint&) +{ + notImplemented(); } + +String ImageBuffer::toDataURL(const String&) const +{ + notImplemented(); + return String(); +} + +Image* ImageBuffer::image() const +{ + notImplemented(); + return 0; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/wx/ImageSourceWx.cpp b/WebCore/platform/graphics/wx/ImageSourceWx.cpp index fc36635..3ce4f2a 100644 --- a/WebCore/platform/graphics/wx/ImageSourceWx.cpp +++ b/WebCore/platform/graphics/wx/ImageSourceWx.cpp @@ -128,6 +128,11 @@ IntSize ImageSource::size() const return m_decoder->size(); } +IntSize ImageSource::frameSizeAtIndex(size_t) const +{ + return size(); +} + int ImageSource::repetitionCount() { if (!m_decoder) @@ -215,7 +220,13 @@ float ImageSource::frameDurationAtIndex(size_t index) if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; - return buffer->duration() / 1000.0f; + float duration = buffer->duration() / 1000.0f; + + // Follow other ports (and WinIE's) behavior to slow annoying ads that + // specify a 0 duration. + if (duration < 0.051f) + return 0.100f; + return duration; } bool ImageSource::frameHasAlphaAtIndex(size_t index) diff --git a/WebCore/platform/graphics/wx/ImageWx.cpp b/WebCore/platform/graphics/wx/ImageWx.cpp index 785466c..a05a31f 100644 --- a/WebCore/platform/graphics/wx/ImageWx.cpp +++ b/WebCore/platform/graphics/wx/ImageWx.cpp @@ -57,8 +57,9 @@ void FrameData::clear() if (m_frame) { delete m_frame; m_frame = 0; - m_duration = 0.; - m_hasAlpha = true; + // NOTE: We purposefully don't reset metadata here, so that even if we + // throw away previously-decoded data, animation loops can still access + // properties like frame durations without re-decoding. } } @@ -66,13 +67,13 @@ void FrameData::clear() // Image Class // ================================================ -Image* Image::loadPlatformResource(const char *name) +PassRefPtr<Image> Image::loadPlatformResource(const char *name) { Vector<char> arr = loadResourceIntoArray(name); - Image* img = new BitmapImage(); - RefPtr<SharedBuffer> buffer = new SharedBuffer(arr.data(), arr.size()); + RefPtr<Image> img = BitmapImage::create(); + RefPtr<SharedBuffer> buffer = SharedBuffer::create(arr.data(), arr.size()); img->setData(buffer, true); - return img; + return img.release(); } void BitmapImage::initPlatformData() @@ -87,30 +88,49 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatR if (!m_source.initialized()) return; + if (mayFillWithSolidColor()) { + fillWithSolidColor(ctxt, dst, solidColor(), op); + return; + } + #if USE(WXGC) wxGCDC* context = (wxGCDC*)ctxt->platformContext(); #else wxWindowDC* context = ctxt->platformContext(); #endif + startAnimation(); + wxBitmap* bitmap = frameAtIndex(m_currentFrame); if (!bitmap) // If it's too early we won't have an image yet. return; - - IntSize selfSize = size(); - FloatRect srcRect(src); - FloatRect dstRect(dst); // If we're drawing a sub portion of the image or scaling then create // a pattern transformation on the image and draw the transformed pattern. // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html // FIXME: NYI + ctxt->save(); + + // Set the compositing operation. + ctxt->setCompositeOperation(op); + + IntRect srcIntRect(src); + IntRect dstIntRect(dst); + bool rescaling = false; + if ((dstIntRect.width() != srcIntRect.width()) || (dstIntRect.height() != srcIntRect.height())) + { + rescaling = true; + wxImage img = bitmap->ConvertToImage(); + img.Rescale(dstIntRect.width(), dstIntRect.height()); + bitmap = new wxBitmap(img); + } wxMemoryDC mydc; ASSERT(bitmap->GetRefData()); mydc.SelectObject(*bitmap); - context->Blit((wxCoord)dst.x(),(wxCoord)dst.y(), (wxCoord)dst.width(), (wxCoord)dst.height(), &mydc, - (wxCoord)src.x(), (wxCoord)src.y(), wxCOPY, true); + + context->Blit((wxCoord)dstIntRect.x(),(wxCoord)dstIntRect.y(), (wxCoord)dstIntRect.width(), (wxCoord)dstIntRect.height(), &mydc, + (wxCoord)srcIntRect.x(), (wxCoord)srcIntRect.y(), wxCOPY, true); mydc.SelectObject(wxNullBitmap); // NB: delete is causing crashes during page load, but not during the deletion @@ -118,10 +138,14 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatR // suddenly becomes invalid after returning. It's possible these errors deal // with reentrancy and threding problems. //delete bitmap; - startAnimation(); + if (rescaling) + { + delete bitmap; + bitmap = NULL; + } + ctxt->restore(); } - void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& dstRect) { if (!m_source.initialized()) diff --git a/WebCore/platform/graphics/wx/PathWx.cpp b/WebCore/platform/graphics/wx/PathWx.cpp index 86ecb93..5ff9914 100644 --- a/WebCore/platform/graphics/wx/PathWx.cpp +++ b/WebCore/platform/graphics/wx/PathWx.cpp @@ -171,4 +171,10 @@ void Path::apply(void* info, PathApplierFunction function) const notImplemented(); } +bool Path::isEmpty() const +{ + notImplemented(); + return false; +} + } |