diff options
Diffstat (limited to 'WebCore/platform')
163 files changed, 7944 insertions, 1103 deletions
diff --git a/WebCore/platform/ScrollAnimator.cpp b/WebCore/platform/ScrollAnimator.cpp new file mode 100644 index 0000000..c863c1d --- /dev/null +++ b/WebCore/platform/ScrollAnimator.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScrollAnimator.h" + +#include "ScrollbarClient.h" +#include <algorithm> + +namespace WebCore { + +#if !OS(WINDOWS) +ScrollAnimator* ScrollAnimator::create(ScrollbarClient* client) +{ + return new ScrollAnimator(client); +} +#endif + +ScrollAnimator::ScrollAnimator(ScrollbarClient* client) + : m_client(client) + , m_currentPosX(0) + , m_currentPosY(0) +{ +} + +ScrollAnimator::~ScrollAnimator() +{ +} + +bool ScrollAnimator::scroll(ScrollbarOrientation orientation, ScrollGranularity, float step, float multiplier) +{ + float* currentPos = (orientation == HorizontalScrollbar) ? &m_currentPosX : &m_currentPosY; + float newPos = std::max(std::min(*currentPos + (step * multiplier), static_cast<float>(m_client->scrollSize(orientation))), 0.0f); + if (*currentPos == newPos) + return false; + *currentPos = newPos; + m_client->setScrollOffsetFromAnimation(IntPoint(m_currentPosX, m_currentPosY)); + return true; +} + +void ScrollAnimator::setScrollPositionAndStopAnimation(ScrollbarOrientation orientation, float pos) +{ + if (orientation == HorizontalScrollbar) + m_currentPosX = pos; + else + m_currentPosY = pos; +} + +} // namespace WebCore diff --git a/WebCore/platform/ScrollAnimator.h b/WebCore/platform/ScrollAnimator.h new file mode 100644 index 0000000..e674339 --- /dev/null +++ b/WebCore/platform/ScrollAnimator.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ScrollAnimator_h +#define ScrollAnimator_h + +#include "ScrollTypes.h" + +namespace WebCore { + +class ScrollbarClient; + +class ScrollAnimator { +public: + static ScrollAnimator* create(ScrollbarClient*); + + ScrollAnimator(ScrollbarClient* client); + virtual ~ScrollAnimator(); + + // Computes a scroll destination for the given parameters. Returns false if + // already at the destination. Otherwise, starts scrolling towards the + // destination and returns true. Scrolling may be immediate or animated. + // The base class implementation always scrolls immediately, never animates. + virtual bool scroll(ScrollbarOrientation, ScrollGranularity, float step, float multiplier); + + // Stops any animation in the given direction and updates the ScrollAnimator + // with the current scroll position. This does not cause a callback to the + // ScrollbarClient. + virtual void setScrollPositionAndStopAnimation(ScrollbarOrientation, float); + +protected: + ScrollbarClient* m_client; + float m_currentPosX; // We avoid using a FloatPoint in order to reduce + float m_currentPosY; // subclass code complexity. +}; + +} // namespace WebCore +#endif diff --git a/WebCore/platform/ScrollAnimatorWin.cpp b/WebCore/platform/ScrollAnimatorWin.cpp new file mode 100644 index 0000000..8b7d0f6 --- /dev/null +++ b/WebCore/platform/ScrollAnimatorWin.cpp @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScrollAnimatorWin.h" + +#include "ScrollbarClient.h" +#include "ScrollbarTheme.h" +#include <algorithm> +#include <wtf/CurrentTime.h> + +namespace WebCore { + +// static +ScrollAnimator* ScrollAnimator::create(ScrollbarClient* client) +{ + return new ScrollAnimatorWin(client); +} + +const double ScrollAnimatorWin::animationTimerDelay = 0.01; + +ScrollAnimatorWin::PerAxisData::PerAxisData(ScrollAnimatorWin* parent, float* currentPos) + : m_currentPos(currentPos) + , m_desiredPos(0) + , m_currentVelocity(0) + , m_desiredVelocity(0) + , m_lastAnimationTime(0) + , m_animationTimer(parent, &ScrollAnimatorWin::animationTimerFired) +{ +} + + +ScrollAnimatorWin::ScrollAnimatorWin(ScrollbarClient* client) + : ScrollAnimator(client) + , m_horizontalData(this, &m_currentPosX) + , m_verticalData(this, &m_currentPosY) +{ +} + +ScrollAnimatorWin::~ScrollAnimatorWin() +{ + stopAnimationTimerIfNeeded(&m_horizontalData); + stopAnimationTimerIfNeeded(&m_verticalData); +} + +bool ScrollAnimatorWin::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier) +{ + // Don't animate jumping to the beginning or end of the document. + if (granularity == ScrollByDocument) + return ScrollAnimator::scroll(orientation, granularity, step, multiplier); + + // This is an animatable scroll. Calculate the scroll delta. + PerAxisData* data = (orientation == VerticalScrollbar) ? &m_verticalData : &m_horizontalData; + float newPos = std::max(std::min(data->m_desiredPos + (step * multiplier), static_cast<float>(m_client->scrollSize(orientation))), 0.0f); + if (newPos == data->m_desiredPos) + return false; + data->m_desiredPos = newPos; + + // Calculate the animation velocity. + if (*data->m_currentPos == data->m_desiredPos) + return false; + bool alreadyAnimating = data->m_animationTimer.isActive(); + // There are a number of different sources of scroll requests. We want to + // make both keyboard and wheel-generated scroll requests (which can come at + // unpredictable rates) and autoscrolling from holding down the mouse button + // on a scrollbar part (where the request rate can be obtained from the + // scrollbar theme) feel smooth, responsive, and similar. + // + // When autoscrolling, the scrollbar's autoscroll timer will call us to + // increment the desired position by |step| (with |multiplier| == 1) every + // ScrollbarTheme::nativeTheme()->autoscrollTimerDelay() seconds. If we set + // the desired velocity to exactly this rate, smooth scrolling will neither + // race ahead (and then have to slow down) nor increasingly lag behind, but + // will be smooth and synchronized. + // + // Note that because of the acceleration period, the current position in + // this case would lag the desired one by a small, constant amount (see + // comments on animateScroll()); the exact amount is given by + // lag = |step| - v(0.5tA + tD) + // Where + // v = The steady-state velocity, + // |step| / ScrollbarTheme::nativeTheme()->autoscrollTimerDelay() + // tA = accelerationTime() + // tD = The time we pretend has already passed when starting to scroll, + // |animationTimerDelay| + // + // This lag provides some buffer against timer jitter so we're less likely + // to hit the desired position and stop (and thus have to re-accelerate, + // causing a visible hitch) while waiting for the next autoscroll increment. + // + // Thus, for autoscroll-timer-triggered requests, the ideal steady-state + // distance to travel in each time interval is: + // float animationStep = step; + // Note that when we're not already animating, this is exactly the same as + // the distance to the target position. We'll return to that in a moment. + // + // For keyboard and wheel scrolls, we don't know when the next increment + // will be requested. If we set the target velocity based on how far away + // from the target position we are, then for keyboard/wheel events that come + // faster than the autoscroll delay, we'll asymptotically approach the + // velocity needed to stay smoothly in sync with the user's actions; for + // events that come slower, we'll scroll one increment and then pause until + // the next event fires. + float animationStep = abs(newPos - *data->m_currentPos); + // If a key is held down (or the wheel continually spun), then once we have + // reached a velocity close to the steady-state velocity, we're likely to + // hit the desired position at around the same time we'd expect the next + // increment to occur -- bad because it leads to hitching as described above + // (if autoscroll-based requests didn't result in a small amount of constant + // lag). So if we're called again while already animating, we want to trim + // the animationStep slightly to maintain lag like what's described above. + // (I say "maintain" since we'll already be lagged due to the acceleration + // during the first scroll period.) + // + // Remember that trimming won't cause us to fall steadily further behind + // here, because the further behind we are, the larger the base step value + // above. Given the scrolling algorithm in animateScroll(), the practical + // effect will actually be that, assuming a constant trim factor, we'll lag + // by a constant amount depending on the rate at which increments occur + // compared to the autoscroll timer delay. The exact lag is given by + // lag = |step| * ((r / k) - 1) + // Where + // r = The ratio of the autoscroll repeat delay, + // ScrollbarTheme::nativeTheme()->autoscrollTimerDelay(), to the + // key/wheel repeat delay (i.e. > 1 when keys repeat faster) + // k = The velocity trim constant given below + // + // We want to choose the trim factor such that for calls that come at the + // autoscroll timer rate, we'll wind up with the same lag as in the + // "perfect" case described above (or, to put it another way, we'll end up + // with |animationStep| == |step| * |multiplier| despite the actual distance + // calculated above being larger than that). This will result in "perfect" + // behavior for autoscrolling without having to special-case it. + if (alreadyAnimating) + animationStep /= (2.0 - ((1.0 / ScrollbarTheme::nativeTheme()->autoscrollTimerDelay()) * (0.5 * accelerationTime() + animationTimerDelay))); + // The result of all this is that single keypresses or wheel flicks will + // scroll in the same time period as single presses of scrollbar elements; + // holding the mouse down on a scrollbar part will scroll as fast as + // possible without hitching; and other repeated scroll events will also + // scroll with the same time lag as holding down the mouse on a scrollbar + // part. + data->m_desiredVelocity = animationStep / ScrollbarTheme::nativeTheme()->autoscrollTimerDelay(); + + // If we're not already scrolling, start. + if (!alreadyAnimating) + animateScroll(data); + return true; +} + +void ScrollAnimatorWin::setScrollPositionAndStopAnimation(ScrollbarOrientation orientation, float pos) +{ + PerAxisData* data = (orientation == HorizontalScrollbar) ? &m_horizontalData : &m_verticalData; + stopAnimationTimerIfNeeded(data); + *data->m_currentPos = pos; + data->m_desiredPos = pos; + data->m_currentVelocity = 0; + data->m_desiredVelocity = 0; +} + +// static +double ScrollAnimatorWin::accelerationTime() +{ + // We elect to use ScrollbarTheme::nativeTheme()->autoscrollTimerDelay() as + // the length of time we'll take to accelerate from 0 to our target + // velocity. Choosing a larger value would produce a more pronounced + // acceleration effect. + return ScrollbarTheme::nativeTheme()->autoscrollTimerDelay(); +} + +void ScrollAnimatorWin::animationTimerFired(Timer<ScrollAnimatorWin>* timer) +{ + animateScroll((timer == &m_horizontalData.m_animationTimer) ? &m_horizontalData : &m_verticalData); +} + +void ScrollAnimatorWin::stopAnimationTimerIfNeeded(PerAxisData* data) +{ + if (data->m_animationTimer.isActive()) + data->m_animationTimer.stop(); +} + +void ScrollAnimatorWin::animateScroll(PerAxisData* data) +{ + // Note on smooth scrolling perf versus non-smooth scrolling perf: + // The total time to perform a complete scroll is given by + // t = t0 + 0.5tA - tD + tS + // Where + // t0 = The time to perform the scroll without smooth scrolling + // tA = The acceleration time, + // ScrollbarTheme::nativeTheme()->autoscrollTimerDelay() (see below) + // tD = |animationTimerDelay| + // tS = A value less than or equal to the time required to perform a + // single scroll increment, i.e. the work done due to calling + // client()->valueChanged() (~0 for simple pages, larger for complex + // pages). + // + // Because tA and tD are fairly small, the total lag (as users perceive it) + // is negligible for simple pages and roughly tS for complex pages. Without + // knowing in advance how large tS is it's hard to do better than this. + // Perhaps we could try to remember previous values and forward-compensate. + + + // We want to update the scroll position based on the time it's been since + // our last update. This may be longer than our ideal time, especially if + // the page is complex or the system is slow. + // + // To avoid feeling laggy, if we've just started smooth scrolling we pretend + // we've already accelerated for one ideal interval, so that we'll scroll at + // least some distance immediately. + double lastScrollInterval = data->m_currentVelocity ? (WTF::currentTime() - data->m_lastAnimationTime) : animationTimerDelay; + + // Figure out how far we've actually traveled and update our current + // velocity. + float distanceTraveled; + if (data->m_currentVelocity < data->m_desiredVelocity) { + // We accelerate at a constant rate until we reach the desired velocity. + float accelerationRate = data->m_desiredVelocity / accelerationTime(); + + // Figure out whether contant acceleration has caused us to reach our + // target velocity. + float potentialVelocityChange = accelerationRate * lastScrollInterval; + float potentialNewVelocity = data->m_currentVelocity + potentialVelocityChange; + if (potentialNewVelocity > data->m_desiredVelocity) { + // We reached the target velocity at some point between our last + // update and now. The distance traveled can be calculated in two + // pieces: the distance traveled while accelerating, and the + // distance traveled after reaching the target velocity. + float actualVelocityChange = data->m_desiredVelocity - data->m_currentVelocity; + float accelerationInterval = actualVelocityChange / accelerationRate; + // The distance traveled under constant acceleration is the area + // under a line segment with a constant rising slope. Break this + // into a triangular portion atop a rectangular portion and sum. + distanceTraveled = ((data->m_currentVelocity + (actualVelocityChange / 2)) * accelerationInterval); + // The distance traveled at the target velocity is simply + // (target velocity) * (remaining time after accelerating). + distanceTraveled += (data->m_desiredVelocity * (lastScrollInterval - accelerationInterval)); + data->m_currentVelocity = data->m_desiredVelocity; + } else { + // Constant acceleration through the entire time interval. + distanceTraveled = (data->m_currentVelocity + (potentialVelocityChange / 2)) * lastScrollInterval; + data->m_currentVelocity = potentialNewVelocity; + } + } else { + // We've already reached the target velocity, so the distance we've + // traveled is simply (current velocity) * (elapsed time). + distanceTraveled = data->m_currentVelocity * lastScrollInterval; + // If our desired velocity has decreased, drop the current velocity too. + data->m_currentVelocity = data->m_desiredVelocity; + } + + // Now update the scroll position based on the distance traveled. + if (distanceTraveled >= abs(data->m_desiredPos - *data->m_currentPos)) { + // We've traveled far enough to reach the desired position. Stop smooth + // scrolling. + *data->m_currentPos = data->m_desiredPos; + data->m_currentVelocity = 0; + data->m_desiredVelocity = 0; + } else { + // Not yet at the target position. Travel towards it and set up the + // next update. + if (*data->m_currentPos > data->m_desiredPos) + distanceTraveled = -distanceTraveled; + *data->m_currentPos += distanceTraveled; + data->m_animationTimer.startOneShot(animationTimerDelay); + data->m_lastAnimationTime = WTF::currentTime(); + } + m_client->setScrollOffsetFromAnimation(IntPoint(*m_horizontalData.m_currentPos, *m_verticalData.m_currentPos)); +} + +} // namespace WebCore diff --git a/WebCore/platform/ScrollAnimatorWin.h b/WebCore/platform/ScrollAnimatorWin.h new file mode 100644 index 0000000..002a454 --- /dev/null +++ b/WebCore/platform/ScrollAnimatorWin.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ScrollAnimatorWin_h +#define ScrollAnimatorWin_h + +#include "ScrollAnimator.h" +#include "Timer.h" + +namespace WebCore { + +class ScrollAnimatorWin : public ScrollAnimator { +public: + ScrollAnimatorWin(ScrollbarClient*); + virtual ~ScrollAnimatorWin(); + + virtual bool scroll(ScrollbarOrientation, ScrollGranularity, float step, float multiplier); + virtual void setScrollPositionAndStopAnimation(ScrollbarOrientation, float); + +private: + struct PerAxisData { + PerAxisData(ScrollAnimatorWin* parent, float* currentPos); + + float* m_currentPos; + float m_desiredPos; + float m_currentVelocity; + float m_desiredVelocity; + double m_lastAnimationTime; + Timer<ScrollAnimatorWin> m_animationTimer; + }; + + static double accelerationTime(); + static const double animationTimerDelay; + + void animationTimerFired(Timer<ScrollAnimatorWin>*); + void stopAnimationTimerIfNeeded(PerAxisData*); + void animateScroll(PerAxisData*); + + PerAxisData m_horizontalData; + PerAxisData m_verticalData; +}; + +} +#endif diff --git a/WebCore/platform/ScrollView.cpp b/WebCore/platform/ScrollView.cpp index 854fef5..43badd0 100644 --- a/WebCore/platform/ScrollView.cpp +++ b/WebCore/platform/ScrollView.cpp @@ -303,6 +303,20 @@ IntPoint ScrollView::maximumScrollPosition() const return IntPoint(maximumOffset.width(), maximumOffset.height()); } +int ScrollView::scrollSize(ScrollbarOrientation orientation) const +{ + Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get(); + return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0; +} + +void ScrollView::setScrollOffsetFromAnimation(const IntPoint& offset) +{ + if (m_horizontalScrollbar) + m_horizontalScrollbar->setValue(offset.x(), Scrollbar::FromScrollAnimator); + if (m_verticalScrollbar) + m_verticalScrollbar->setValue(offset.y(), Scrollbar::FromScrollAnimator); +} + void ScrollView::valueChanged(Scrollbar* scrollbar) { // Figure out if we really moved. @@ -471,7 +485,7 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset) m_horizontalScrollbar->setSuppressInvalidation(true); m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); m_horizontalScrollbar->setProportion(clientWidth, contentsWidth()); - m_horizontalScrollbar->setValue(scroll.width()); + m_horizontalScrollbar->setValue(scroll.width(), Scrollbar::NotFromScrollAnimator); if (m_scrollbarsSuppressed) m_horizontalScrollbar->setSuppressInvalidation(false); } @@ -493,7 +507,7 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset) m_verticalScrollbar->setSuppressInvalidation(true); m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); m_verticalScrollbar->setProportion(clientHeight, contentsHeight()); - m_verticalScrollbar->setValue(scroll.height()); + m_verticalScrollbar->setValue(scroll.height(), Scrollbar::NotFromScrollAnimator); if (m_scrollbarsSuppressed) m_verticalScrollbar->setSuppressInvalidation(false); } @@ -703,7 +717,12 @@ void ScrollView::wheelEvent(PlatformWheelEvent& e) if (negative) deltaY = -deltaY; } - scrollBy(IntSize(-deltaX, -deltaY)); + + // Should we fall back on scrollBy() if there is no scrollbar for a non-zero delta? + if (deltaY && m_verticalScrollbar) + m_verticalScrollbar->scroll(ScrollUp, ScrollByPixel, deltaY); + if (deltaX && m_horizontalScrollbar) + m_horizontalScrollbar->scroll(ScrollLeft, ScrollByPixel, deltaX); } } diff --git a/WebCore/platform/ScrollView.h b/WebCore/platform/ScrollView.h index 5624d70..a55fed4 100644 --- a/WebCore/platform/ScrollView.h +++ b/WebCore/platform/ScrollView.h @@ -40,6 +40,7 @@ #endif #if PLATFORM(GTK) +#include "GRefPtrGtk.h" typedef struct _GtkAdjustment GtkAdjustment; #endif @@ -57,7 +58,9 @@ class ScrollView : public Widget, public ScrollbarClient { public: ~ScrollView(); - // ScrollbarClient function. FrameView overrides the others. + // ScrollbarClient functions. FrameView overrides the others. + virtual int scrollSize(ScrollbarOrientation orientation) const; + virtual void setScrollOffsetFromAnimation(const IntPoint&); virtual void valueChanged(Scrollbar*); // The window thats hosts the ScrollView. The ScrollView will communicate scrolls and repaints to the @@ -347,9 +350,11 @@ private: #if PLATFORM(GTK) public: void setGtkAdjustments(GtkAdjustment* hadj, GtkAdjustment* vadj, bool resetValues = true); - GtkAdjustment* m_horizontalAdjustment; - GtkAdjustment* m_verticalAdjustment; void setScrollOffset(const IntSize& offset) { m_scrollOffset = offset; } + +private: + PlatformRefPtr<GtkAdjustment> m_horizontalAdjustment; + PlatformRefPtr<GtkAdjustment> m_verticalAdjustment; #endif #if PLATFORM(WX) diff --git a/WebCore/platform/Scrollbar.cpp b/WebCore/platform/Scrollbar.cpp index ff8f66f..398584a 100644 --- a/WebCore/platform/Scrollbar.cpp +++ b/WebCore/platform/Scrollbar.cpp @@ -40,7 +40,7 @@ using namespace std; -#if PLATFORM(CHROMIUM) && OS(LINUX) +#if PLATFORM(CHROMIUM) && OS(LINUX) || PLATFORM(GTK) // The position of the scrollbar thumb affects the appearance of the steppers, so // when the thumb moves, we have to invalidate them for painting. #define THUMB_POSITION_AFFECTS_BUTTONS @@ -48,7 +48,7 @@ using namespace std; namespace WebCore { -#if !PLATFORM(GTK) && !PLATFORM(EFL) +#if !PLATFORM(EFL) PassRefPtr<Scrollbar> Scrollbar::createNativeScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size) { return adoptRef(new Scrollbar(client, orientation, size)); @@ -101,12 +101,12 @@ Scrollbar::~Scrollbar() m_theme->unregisterScrollbar(this); } -bool Scrollbar::setValue(int v) +bool Scrollbar::setValue(int v, ScrollSource source) { v = max(min(v, m_totalSize - m_visibleSize), 0); if (value() == v) return false; // Our value stayed the same. - setCurrentPos(v); + setCurrentPos(v, source); return true; } @@ -154,8 +154,10 @@ bool Scrollbar::scroll(ScrollDirection direction, ScrollGranularity granularity, } if (direction == ScrollUp || direction == ScrollLeft) multiplier = -multiplier; + if (client()) + return client()->scroll(m_orientation, granularity, step, multiplier); - return setCurrentPos(max(min(m_currentPos + (step * multiplier), static_cast<float>(m_totalSize - m_visibleSize)), 0.0f)); + return setCurrentPos(max(min(m_currentPos + (step * multiplier), static_cast<float>(m_totalSize - m_visibleSize)), 0.0f), NotFromScrollAnimator); } void Scrollbar::updateThumb() @@ -287,11 +289,14 @@ void Scrollbar::moveThumb(int pos) else if (delta < 0) delta = max(-thumbPos, delta); if (delta) - setCurrentPos(static_cast<float>(thumbPos + delta) * maximum() / (trackLen - thumbLen)); + setCurrentPos(static_cast<float>(thumbPos + delta) * maximum() / (trackLen - thumbLen), NotFromScrollAnimator); } -bool Scrollbar::setCurrentPos(float pos) +bool Scrollbar::setCurrentPos(float pos, ScrollSource source) { + if ((source != FromScrollAnimator) && client()) + client()->setScrollPositionAndStopAnimation(m_orientation, pos); + if (pos == m_currentPos) return false; @@ -336,7 +341,7 @@ bool Scrollbar::mouseMoved(const PlatformMouseEvent& evt) { if (m_pressedPart == ThumbPart) { if (theme()->shouldSnapBackToDragOrigin(this, evt)) - setCurrentPos(m_dragOrigin); + setCurrentPos(m_dragOrigin, NotFromScrollAnimator); else { moveThumb(m_orientation == HorizontalScrollbar ? convertFromContainingWindow(evt.pos()).x() : diff --git a/WebCore/platform/Scrollbar.h b/WebCore/platform/Scrollbar.h index f8ef96d..276bf60 100644 --- a/WebCore/platform/Scrollbar.h +++ b/WebCore/platform/Scrollbar.h @@ -42,6 +42,11 @@ class PlatformMouseEvent; class Scrollbar : public Widget { public: + enum ScrollSource { + FromScrollAnimator, + NotFromScrollAnimator, + }; + virtual ~Scrollbar(); // Must be implemented by platforms that can't simply use the Scrollbar base class. Right now the only platform that is not using the base class is GTK. @@ -75,7 +80,7 @@ public: virtual void setPressedPart(ScrollbarPart); void setSteps(int lineStep, int pageStep, int pixelsPerStep = 1); - bool setValue(int); + bool setValue(int, ScrollSource source); void setProportion(int visibleSize, int totalSize); void setPressedPos(int p) { m_pressedPos = p; } @@ -167,7 +172,7 @@ protected: private: virtual bool isScrollbar() const { return true; } - bool setCurrentPos(float pos); + bool setCurrentPos(float pos, ScrollSource source); }; } diff --git a/WebCore/platform/ScrollbarClient.cpp b/WebCore/platform/ScrollbarClient.cpp new file mode 100644 index 0000000..2f81a93 --- /dev/null +++ b/WebCore/platform/ScrollbarClient.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScrollbarClient.h" + +#include "ScrollAnimator.h" + +namespace WebCore { + +ScrollbarClient::ScrollbarClient() + : m_scrollAnimator(ScrollAnimator::create(this)) +{ +} + +ScrollbarClient::~ScrollbarClient() +{ +} + +bool ScrollbarClient::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier) +{ + return m_scrollAnimator->scroll(orientation, granularity, step, multiplier); +} + +void ScrollbarClient::setScrollPositionAndStopAnimation(ScrollbarOrientation orientation, float pos) +{ + m_scrollAnimator->setScrollPositionAndStopAnimation(orientation, pos); +} + +} // namespace WebCore diff --git a/WebCore/platform/ScrollbarClient.h b/WebCore/platform/ScrollbarClient.h index fa94ecc..ab3b10e 100644 --- a/WebCore/platform/ScrollbarClient.h +++ b/WebCore/platform/ScrollbarClient.h @@ -26,21 +26,28 @@ #ifndef ScrollbarClient_h #define ScrollbarClient_h +#include "IntPoint.h" #include "IntRect.h" #include "Scrollbar.h" #include <wtf/Vector.h> namespace WebCore { +class ScrollAnimator; + class ScrollbarClient { public: - virtual ~ScrollbarClient() { } - virtual void valueChanged(Scrollbar*) = 0; + ScrollbarClient(); + virtual ~ScrollbarClient(); - virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&) = 0; + bool scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier); + void setScrollPositionAndStopAnimation(ScrollbarOrientation orientation, float pos); + virtual int scrollSize(ScrollbarOrientation orientation) const = 0; + virtual void setScrollOffsetFromAnimation(const IntPoint&) = 0; + virtual void valueChanged(Scrollbar*) = 0; + virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&) = 0; virtual bool isActive() const = 0; - virtual bool scrollbarCornerPresent() const = 0; virtual void getTickmarks(Vector<IntRect>&) const { } @@ -52,21 +59,21 @@ public: { return scrollbar->Widget::convertToContainingView(scrollbarRect); } - virtual IntRect convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const { return scrollbar->Widget::convertFromContainingView(parentRect); } - virtual IntPoint convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const { return scrollbar->Widget::convertToContainingView(scrollbarPoint); } - virtual IntPoint convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const { return scrollbar->Widget::convertFromContainingView(parentPoint); } + +private: + OwnPtr<ScrollAnimator> m_scrollAnimator; }; } diff --git a/WebCore/platform/ScrollbarThemeComposite.cpp b/WebCore/platform/ScrollbarThemeComposite.cpp index d28e1c3..fdac14d 100644 --- a/WebCore/platform/ScrollbarThemeComposite.cpp +++ b/WebCore/platform/ScrollbarThemeComposite.cpp @@ -296,10 +296,9 @@ void ScrollbarThemeComposite::paintScrollCorner(ScrollView* view, GraphicsContex { FrameView* frameView = static_cast<FrameView*>(view); Page* page = frameView->frame() ? frameView->frame()->page() : 0; - if (page && page->settings()->shouldPaintCustomScrollbars()) { - if (!page->chrome()->client()->paintCustomScrollCorner(context, cornerRect)) - context->fillRect(cornerRect, Color::white, DeviceColorSpace); - } + if (page && page->settings()->shouldPaintCustomScrollbars() && page->chrome()->client()->paintCustomScrollCorner(context, cornerRect)) + return; + context->fillRect(cornerRect, Color::white, DeviceColorSpace); } } diff --git a/WebCore/platform/UUID.cpp b/WebCore/platform/UUID.cpp index 852e3ae..fdbf601 100644 --- a/WebCore/platform/UUID.cpp +++ b/WebCore/platform/UUID.cpp @@ -84,10 +84,11 @@ String createCanonicalUUIDString() FILE* fptr = fopen("/proc/sys/kernel/random/uuid", "r"); if (!fptr) return String(); - char uuidStr[37] = {0}; - if (!fgets(uuidStr, sizeof(uuidStr) - 1, fptr)) - return String(); + char uuidStr[37]; + char* result = fgets(uuidStr, sizeof(uuidStr), fptr); fclose(fptr); + if (!result) + return String(); String canonicalUuidStr = String(uuidStr).lower(); // make it lower. ASSERT(canonicalUuidStr[uuidVersionIdentifierIndex] == uuidVersionRequired); return canonicalUuidStr; diff --git a/WebCore/platform/android/TemporaryLinkStubs.cpp b/WebCore/platform/android/TemporaryLinkStubs.cpp index bedb91b..3c5c8b4 100644 --- a/WebCore/platform/android/TemporaryLinkStubs.cpp +++ b/WebCore/platform/android/TemporaryLinkStubs.cpp @@ -104,7 +104,7 @@ namespace WebCore { // This function tells the bridge that a resource was loaded from the cache and thus // the app may update progress with the amount of data loaded. -void CheckCacheObjectStatus(DocLoader*, CachedResource*) +void CheckCacheObjectStatus(CachedResourceLoader*, CachedResource*) { ASSERT_NOT_REACHED(); notImplemented(); diff --git a/WebCore/platform/animation/Animation.cpp b/WebCore/platform/animation/Animation.cpp index bc33a9e..112ee36 100644 --- a/WebCore/platform/animation/Animation.cpp +++ b/WebCore/platform/animation/Animation.cpp @@ -106,23 +106,23 @@ bool Animation::animationsMatch(const Animation* o, bool matchPlayStates) const if (!o) return false; - bool result = m_name == o->m_name && - m_property == o->m_property && - m_iterationCount == o->m_iterationCount && - m_delay == o->m_delay && - m_duration == o->m_duration && - m_timingFunction == o->m_timingFunction && - m_direction == o->m_direction && - m_fillMode == o->m_fillMode && - m_delaySet == o->m_delaySet && - m_directionSet == o->m_directionSet && - m_durationSet == o->m_durationSet && - m_fillModeSet == o->m_fillModeSet && - m_iterationCountSet == o->m_iterationCountSet && - m_nameSet == o->m_nameSet && - m_propertySet == o->m_propertySet && - m_timingFunctionSet == o->m_timingFunctionSet && - m_isNone == o->m_isNone; + bool result = m_name == o->m_name + && m_property == o->m_property + && m_iterationCount == o->m_iterationCount + && m_delay == o->m_delay + && m_duration == o->m_duration + && *(m_timingFunction.get()) == *(o->m_timingFunction.get()) + && m_direction == o->m_direction + && m_fillMode == o->m_fillMode + && m_delaySet == o->m_delaySet + && m_directionSet == o->m_directionSet + && m_durationSet == o->m_durationSet + && m_fillModeSet == o->m_fillModeSet + && m_iterationCountSet == o->m_iterationCountSet + && m_nameSet == o->m_nameSet + && m_propertySet == o->m_propertySet + && m_timingFunctionSet == o->m_timingFunctionSet + && m_isNone == o->m_isNone; if (!result) return false; diff --git a/WebCore/platform/animation/Animation.h b/WebCore/platform/animation/Animation.h index cabb0eb..9130415 100644 --- a/WebCore/platform/animation/Animation.h +++ b/WebCore/platform/animation/Animation.h @@ -26,6 +26,7 @@ #define Animation_h #include "PlatformString.h" +#include "RenderStyleConstants.h" #include "TimingFunction.h" #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> @@ -94,7 +95,7 @@ public: const String& name() const { return m_name; } unsigned playState() const { return m_playState; } int property() const { return m_property; } - const TimingFunction& timingFunction() const { return m_timingFunction; } + const PassRefPtr<TimingFunction> timingFunction() const { return m_timingFunction; } void setDelay(double c) { m_delay = c; m_delaySet = true; } void setDirection(AnimationDirection d) { m_direction = d; m_directionSet = true; } @@ -104,7 +105,7 @@ public: void setName(const String& n) { m_name = n; m_nameSet = true; } void setPlayState(unsigned d) { m_playState = d; m_playStateSet = true; } void setProperty(int t) { m_property = t; m_propertySet = true; } - void setTimingFunction(const TimingFunction& f) { m_timingFunction = f; m_timingFunctionSet = true; } + void setTimingFunction(PassRefPtr<TimingFunction> f) { m_timingFunction = f; m_timingFunctionSet = true; } void setIsNoneAnimation(bool n) { m_isNone = n; } @@ -129,7 +130,7 @@ private: int m_iterationCount; double m_delay; double m_duration; - TimingFunction m_timingFunction; + RefPtr<TimingFunction> m_timingFunction; AnimationDirection m_direction : 1; unsigned m_fillMode : 2; @@ -156,7 +157,7 @@ public: static String initialAnimationName() { return String("none"); } static unsigned initialAnimationPlayState() { return AnimPlayStatePlaying; } static int initialAnimationProperty() { return cAnimateAll; } - static TimingFunction initialAnimationTimingFunction() { return TimingFunction(); } + static PassRefPtr<TimingFunction> initialAnimationTimingFunction() { return CubicBezierTimingFunction::create(); } }; } // namespace WebCore diff --git a/WebCore/platform/animation/TimingFunction.h b/WebCore/platform/animation/TimingFunction.h index d3f71ff..8ef2d8f 100644 --- a/WebCore/platform/animation/TimingFunction.h +++ b/WebCore/platform/animation/TimingFunction.h @@ -25,53 +25,87 @@ #ifndef TimingFunction_h #define TimingFunction_h -#include "RenderStyleConstants.h" +#include <wtf/RefCounted.h> namespace WebCore { -struct TimingFunction : FastAllocBase { - TimingFunction() - : m_type(CubicBezierTimingFunction) - , m_x1(0.25) - , m_y1(0.1) - , m_x2(0.25) - , m_y2(1.0) +class TimingFunction : public RefCounted<TimingFunction> { +public: + + enum TimingFunctionType { + LinearFunction, CubicBezierFunction, StepsFunction + }; + + virtual ~TimingFunction() { } + + bool isLinearTimingFunction() const { return m_type == LinearFunction; } + bool isCubicBezierTimingFunction() const { return m_type == CubicBezierFunction; } + bool isStepsTimingFunction() const { return m_type == StepsFunction; } + + virtual bool operator==(const TimingFunction& other) = 0; + +protected: + TimingFunction(TimingFunctionType type) + : m_type(type) { } + + TimingFunctionType m_type; +}; - // This explicit copy constructor works around an inlining bug in GCC 4.2 (only reproed on mac, but may exist on other platforms). - TimingFunction(const TimingFunction& that) - : m_type(that.m_type) - , m_x1(that.m_x1) - , m_y1(that.m_y1) - , m_x2(that.m_x2) - , m_y2(that.m_y2) +class LinearTimingFunction : public TimingFunction { +public: + static PassRefPtr<LinearTimingFunction> create() { + return adoptRef(new LinearTimingFunction); } - - TimingFunction(ETimingFunctionType timingFunction, double x1 = 0.0, double y1 = 0.0, double x2 = 1.0, double y2 = 1.0) - : m_type(timingFunction) - , m_x1(x1) - , m_y1(y1) - , m_x2(x2) - , m_y2(y2) + + ~LinearTimingFunction() { } + + virtual bool operator==(const TimingFunction& other) { + return other.isLinearTimingFunction(); + } + +private: + LinearTimingFunction() + : TimingFunction(LinearFunction) + { + } +}; + +class CubicBezierTimingFunction : public TimingFunction { +public: + static PassRefPtr<CubicBezierTimingFunction> create(double x1 = 0.25, double y1 = 0.1, double x2 = 0.25, double y2 = 1.0) + { + return adoptRef(new CubicBezierTimingFunction(x1, y1, x2, y2)); } - bool operator==(const TimingFunction& o) const + ~CubicBezierTimingFunction() { } + + virtual bool operator==(const TimingFunction& other) { - return m_type == o.m_type && m_x1 == o.m_x1 && m_y1 == o.m_y1 && m_x2 == o.m_x2 && m_y2 == o.m_y2; + if (other.isCubicBezierTimingFunction()) { + const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(&other); + return m_x1 == ctf->m_x1 && m_y1 == ctf->m_y1 && m_x2 == ctf->m_x2 && m_y2 == ctf->m_y2; + } + return false; } double x1() const { return m_x1; } double y1() const { return m_y1; } double x2() const { return m_x2; } double y2() const { return m_y2; } - - ETimingFunctionType type() const { return m_type; } - + private: - ETimingFunctionType m_type; + CubicBezierTimingFunction(double x1, double y1, double x2, double y2) + : TimingFunction(CubicBezierFunction) + , m_x1(x1) + , m_y1(y1) + , m_x2(x2) + , m_y2(y2) + { + } double m_x1; double m_y1; @@ -79,6 +113,39 @@ private: double m_y2; }; +class StepsTimingFunction : public TimingFunction { +public: + static PassRefPtr<StepsTimingFunction> create(int steps, bool stepAtStart) + { + return adoptRef(new StepsTimingFunction(steps, stepAtStart)); + } + + ~StepsTimingFunction() { } + + virtual bool operator==(const TimingFunction& other) + { + if (other.isStepsTimingFunction()) { + const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(&other); + return m_steps == stf->m_steps && m_stepAtStart == stf->m_stepAtStart; + } + return false; + } + + int numberOfSteps() const { return m_steps; } + bool stepAtStart() const { return m_stepAtStart; } + +private: + StepsTimingFunction(int steps, bool stepAtStart) + : TimingFunction(StepsFunction) + , m_steps(steps) + , m_stepAtStart(stepAtStart) + { + } + + int m_steps; + bool m_stepAtStart; +}; + } // namespace WebCore #endif // TimingFunction_h diff --git a/WebCore/platform/audio/AudioArray.h b/WebCore/platform/audio/AudioArray.h new file mode 100644 index 0000000..9c25b0f --- /dev/null +++ b/WebCore/platform/audio/AudioArray.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioArray_h +#define AudioArray_h + +#include <string.h> +#include <wtf/Vector.h> + +namespace WebCore { + +template<typename T> +class AudioArray : public Vector<T> { +public: + AudioArray() : Vector<T>(0) { } + explicit AudioArray(size_t n) : Vector<T>(n, 0) { } + + void zero() { memset(this->data(), 0, sizeof(T) * this->size()); } + + void zeroRange(unsigned start, unsigned end) + { + bool isSafe = (start <= end) && (end <= this->size()); + ASSERT(isSafe); + if (!isSafe) + return; + + memset(this->data() + start, 0, sizeof(T) * (end - start)); + } + + void copyToRange(T* sourceData, unsigned start, unsigned end) + { + bool isSafe = (start <= end) && (end <= this->size()); + ASSERT(isSafe); + if (!isSafe) + return; + + memcpy(this->data() + start, sourceData, sizeof(T) * (end - start)); + } +}; + +typedef AudioArray<float> AudioFloatArray; +typedef AudioArray<double> AudioDoubleArray; + +} // WebCore + +#endif // AudioArray_h diff --git a/WebCore/platform/audio/AudioBus.cpp b/WebCore/platform/audio/AudioBus.cpp new file mode 100644 index 0000000..6b7ec3f --- /dev/null +++ b/WebCore/platform/audio/AudioBus.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "AudioBus.h" + +#include "Accelerate.h" +#include <algorithm> +#include <assert.h> +#include <math.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +AudioBus::AudioBus(unsigned numberOfChannels, size_t length, bool allocate) + : m_length(length) + , m_busGain(1.0) + , m_isFirstTime(true) + , m_sampleRate(0.0) +{ + m_channels.reserveInitialCapacity(numberOfChannels); + + for (unsigned i = 0; i < numberOfChannels; ++i) { + PassOwnPtr<AudioChannel> channel = allocate ? adoptPtr(new AudioChannel(length)) : adoptPtr(new AudioChannel(0, length)); + m_channels.append(channel); + } + + m_layout = LayoutCanonical; // for now this is the only layout we define +} + +void AudioBus::setChannelMemory(unsigned channelIndex, float* storage, size_t length) +{ + if (channelIndex < m_channels.size()) { + channel(channelIndex)->set(storage, length); + m_length = length; // FIXME: verify that this length matches all the other channel lengths + } +} + +void AudioBus::zero() +{ + for (unsigned i = 0; i < m_channels.size(); ++i) + m_channels[i]->zero(); +} + +AudioChannel* AudioBus::channelByType(unsigned channelType) +{ + // For now we only support canonical channel layouts... + if (m_layout != LayoutCanonical) + return 0; + + switch (numberOfChannels()) { + case 1: // mono + if (channelType == ChannelMono || channelType == ChannelLeft) + return channel(0); + return 0; + + case 2: // stereo + switch (channelType) { + case ChannelLeft: return channel(0); + case ChannelRight: return channel(1); + default: return 0; + } + + case 4: // quad + switch (channelType) { + case ChannelLeft: return channel(0); + case ChannelRight: return channel(1); + case ChannelSurroundLeft: return channel(2); + case ChannelSurroundRight: return channel(3); + default: return 0; + } + + case 5: // 5.0 + switch (channelType) { + case ChannelLeft: return channel(0); + case ChannelRight: return channel(1); + case ChannelCenter: return channel(2); + case ChannelSurroundLeft: return channel(3); + case ChannelSurroundRight: return channel(4); + default: return 0; + } + + case 6: // 5.1 + switch (channelType) { + case ChannelLeft: return channel(0); + case ChannelRight: return channel(1); + case ChannelCenter: return channel(2); + case ChannelLFE: return channel(3); + case ChannelSurroundLeft: return channel(4); + case ChannelSurroundRight: return channel(5); + default: return 0; + } + } + + ASSERT_NOT_REACHED(); + return 0; +} + +// Returns true if the channel count and frame-size match. +bool AudioBus::topologyMatches(const AudioBus& bus) const +{ + if (numberOfChannels() != bus.numberOfChannels()) + return false; // channel mismatch + + // Make sure source bus has enough frames. + if (length() > bus.length()) + return false; // frame-size mismatch + + return true; +} + +PassOwnPtr<AudioBus> AudioBus::createBufferFromRange(AudioBus* sourceBuffer, unsigned startFrame, unsigned endFrame) +{ + size_t numberOfSourceFrames = sourceBuffer->length(); + unsigned numberOfChannels = sourceBuffer->numberOfChannels(); + + // Sanity checking + bool isRangeSafe = startFrame < endFrame && endFrame <= numberOfSourceFrames; + ASSERT(isRangeSafe); + if (!isRangeSafe) + return 0; + + size_t rangeLength = endFrame - startFrame; + + OwnPtr<AudioBus> audioBus = adoptPtr(new AudioBus(numberOfChannels, rangeLength)); + audioBus->setSampleRate(sourceBuffer->sampleRate()); + + for (unsigned i = 0; i < numberOfChannels; ++i) + audioBus->channel(i)->copyFromRange(sourceBuffer->channel(i), startFrame, endFrame); + + return audioBus.release(); +} + +float AudioBus::maxAbsValue() const +{ + float max = 0.0f; + for (unsigned i = 0; i < numberOfChannels(); ++i) { + const AudioChannel* channel = this->channel(i); + max = std::max(max, channel->maxAbsValue()); + } + + return max; +} + +void AudioBus::normalize() +{ + float max = maxAbsValue(); + if (max) + scale(1.0f / max); +} + +void AudioBus::scale(double scale) +{ + for (unsigned i = 0; i < numberOfChannels(); ++i) + channel(i)->scale(scale); +} + +// Just copies the samples from the source bus to this one. +// This is just a simple copy if the number of channels match, otherwise a mixup or mixdown is done. +// For now, we just support a mixup from mono -> stereo. +void AudioBus::copyFrom(const AudioBus& sourceBus) +{ + if (&sourceBus == this) + return; + + if (numberOfChannels() == sourceBus.numberOfChannels()) { + for (unsigned i = 0; i < numberOfChannels(); ++i) + channel(i)->copyFrom(sourceBus.channel(i)); + } else if (numberOfChannels() == 2 && sourceBus.numberOfChannels() == 1) { + // Handle mono -> stereo case (for now simply copy mono channel into both left and right) + // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center... + const AudioChannel* sourceChannel = sourceBus.channel(0); + channel(0)->copyFrom(sourceChannel); + channel(1)->copyFrom(sourceChannel); + } else { + // Case not handled + ASSERT_NOT_REACHED(); + } +} + +void AudioBus::sumFrom(const AudioBus &sourceBus) +{ + if (numberOfChannels() == sourceBus.numberOfChannels()) { + for (unsigned i = 0; i < numberOfChannels(); ++i) + channel(i)->sumFrom(sourceBus.channel(i)); + } else if (numberOfChannels() == 2 && sourceBus.numberOfChannels() == 1) { + // Handle mono -> stereo case (for now simply sum mono channel into both left and right) + // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center... + const AudioChannel* sourceChannel = sourceBus.channel(0); + channel(0)->sumFrom(sourceChannel); + channel(1)->sumFrom(sourceChannel); + } else { + // Case not handled + ASSERT_NOT_REACHED(); + } +} + +void AudioBus::processWithGainFromMonoStereo(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus) +{ + // We don't want to suddenly change the gain from mixing one time slice to the next, + // so we "de-zipper" by slowly changing the gain each sample-frame until we've achieved the target gain. + + // FIXME: optimize this method (SSE, etc.) + // FIXME: Need fast path here when gain has converged on targetGain. In this case, de-zippering is no longer needed. + // FIXME: Need fast path when this==sourceBus && lastMixGain==targetGain==1.0 && sumToBus==false (this is a NOP) + + // Take master bus gain into account as well as the targetGain. + double totalDesiredGain = m_busGain * targetGain; + + // First time, snap directly to totalDesiredGain. + double gain = m_isFirstTime ? totalDesiredGain : *lastMixGain; + m_isFirstTime = false; + + int numberOfSourceChannels = sourceBus.numberOfChannels(); + int numberOfDestinationChannels = numberOfChannels(); + + AudioBus& sourceBusSafe = const_cast<AudioBus&>(sourceBus); + const float* sourceL = sourceBusSafe.channelByType(ChannelLeft)->data(); + const float* sourceR = numberOfSourceChannels > 1 ? sourceBusSafe.channelByType(ChannelRight)->data() : 0; + + float* destinationL = channelByType(ChannelLeft)->data(); + float* destinationR = numberOfDestinationChannels > 1 ? channelByType(ChannelRight)->data() : 0; + + const double DezipperRate = 0.005; + int framesToProcess = length(); + + if (sumToBus) { + // Sum to our bus + if (sourceR && destinationR) { + // Stereo + while (framesToProcess--) { + float sampleL = *sourceL++; + float sampleR = *sourceR++; + *destinationL++ += static_cast<float>(gain * sampleL); + *destinationR++ += static_cast<float>(gain * sampleR); + + // Slowly change gain to desired gain. + gain += (totalDesiredGain - gain) * DezipperRate; + } + } else if (destinationR) { + // Mono -> stereo (mix equally into L and R) + // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center... + while (framesToProcess--) { + float sample = *sourceL++; + *destinationL++ += static_cast<float>(gain * sample); + *destinationR++ += static_cast<float>(gain * sample); + + // Slowly change gain to desired gain. + gain += (totalDesiredGain - gain) * DezipperRate; + } + } else { + // Mono + while (framesToProcess--) { + float sampleL = *sourceL++; + *destinationL++ += static_cast<float>(gain * sampleL); + + // Slowly change gain to desired gain. + gain += (totalDesiredGain - gain) * DezipperRate; + } + } + } else { + // Process directly (without summing) to our bus + if (sourceR && destinationR) { + // Stereo + while (framesToProcess--) { + float sampleL = *sourceL++; + float sampleR = *sourceR++; + *destinationL++ = static_cast<float>(gain * sampleL); + *destinationR++ = static_cast<float>(gain * sampleR); + + // Slowly change gain to desired gain. + gain += (totalDesiredGain - gain) * DezipperRate; + } + } else if (destinationR) { + // Mono -> stereo (mix equally into L and R) + // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center... + while (framesToProcess--) { + float sample = *sourceL++; + *destinationL++ = static_cast<float>(gain * sample); + *destinationR++ = static_cast<float>(gain * sample); + + // Slowly change gain to desired gain. + gain += (totalDesiredGain - gain) * DezipperRate; + } + } else { + // Mono + while (framesToProcess--) { + float sampleL = *sourceL++; + *destinationL++ = static_cast<float>(gain * sampleL); + + // Slowly change gain to desired gain. + gain += (totalDesiredGain - gain) * DezipperRate; + } + } + } + + // Save the target gain as the starting point for next time around. + *lastMixGain = gain; +} + +void AudioBus::processWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus) +{ + // Make sure we're summing from same type of bus. + // We *are* able to sum from mono -> stereo + if (sourceBus.numberOfChannels() != 1 && !topologyMatches(sourceBus)) + return; + + // Dispatch for different channel layouts + switch (numberOfChannels()) { + case 1: // mono + case 2: // stereo + processWithGainFromMonoStereo(sourceBus, lastMixGain, targetGain, sumToBus); + break; + case 4: // FIXME: implement quad + case 5: // FIXME: implement 5.0 + default: + ASSERT_NOT_REACHED(); + break; + } +} + +void AudioBus::copyWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain) +{ + processWithGainFrom(sourceBus, lastMixGain, targetGain, false); +} + +void AudioBus::sumWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain) +{ + processWithGainFrom(sourceBus, lastMixGain, targetGain, true); +} + +} // WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/AudioBus.h b/WebCore/platform/audio/AudioBus.h new file mode 100644 index 0000000..72357e8 --- /dev/null +++ b/WebCore/platform/audio/AudioBus.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioBus_h +#define AudioBus_h + +#include "AudioChannel.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// An AudioBus represents a collection of one or more AudioChannels. +// The data layout is "planar" as opposed to "interleaved". +// An AudioBus with one channel is mono, an AudioBus with two channels is stereo, etc. +class AudioBus : public Noncopyable { +public: + enum { + ChannelLeft = 0, + ChannelRight = 1, + ChannelCenter = 2, // center and mono are the same + ChannelMono = 2, + ChannelLFE = 3, + ChannelSurroundLeft = 4, + ChannelSurroundRight = 5, + }; + + enum { + LayoutCanonical = 0 + // Can define non-standard layouts here + }; + + // allocate indicates whether or not to initially have the AudioChannels created with managed storage. + // Normal usage is to pass true here, in which case the AudioChannels will memory-manage their own storage. + // If allocate is false then setChannelMemory() has to be called later on for each channel before the AudioBus is useable... + AudioBus(unsigned numberOfChannels, size_t length, bool allocate = true); + + // Tells the given channel to use an externally allocated buffer. + void setChannelMemory(unsigned channelIndex, float* storage, size_t length); + + // Channels + unsigned numberOfChannels() const { return m_channels.size(); } + + AudioChannel* channel(unsigned channel) { return m_channels[channel].get(); } + const AudioChannel* channel(unsigned channel) const { return const_cast<AudioBus*>(this)->m_channels[channel].get(); } + AudioChannel* channelByType(unsigned type); + + // Number of sample-frames + size_t length() const { return m_length; } + + // Sample-rate : 0.0 if unknown or "don't care" + double sampleRate() const { return m_sampleRate; } + void setSampleRate(double sampleRate) { m_sampleRate = sampleRate; } + + // Zeroes all channels. + void zero(); + + // Returns true if the channel count and frame-size match. + bool topologyMatches(const AudioBus &sourceBus) const; + + // Creates a new buffer from a range in the source buffer. + // 0 may be returned if the range does not fit in the sourceBuffer + static PassOwnPtr<AudioBus> createBufferFromRange(AudioBus* sourceBuffer, unsigned startFrame, unsigned endFrame); + + // Scales all samples by the same amount. + void scale(double scale); + + // Master gain for this bus - used with sumWithGainFrom() below + void setGain(double gain) { m_busGain = gain; } + double gain() { return m_busGain; } + + void reset() { m_isFirstTime = true; } // for de-zippering + + // Assuming sourceBus has the same topology, copies sample data from each channel of sourceBus to our corresponding channel. + void copyFrom(const AudioBus &sourceBus); + + // Sums the sourceBus into our bus with unity gain. + // Our own internal gain m_busGain is ignored. + void sumFrom(const AudioBus &sourceBus); + + // Copy or sum each channel from sourceBus into our corresponding channel. + // We scale by targetGain (and our own internal gain m_busGain), performing "de-zippering" to smoothly change from *lastMixGain to (targetGain*m_busGain). + // The caller is responsible for setting up lastMixGain to point to storage which is unique for every "stream" which will be summed to this bus. + // This represents the dezippering memory. + void copyWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain); + void sumWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain); + + // Returns maximum absolute value across all channels (useful for normalization). + float maxAbsValue() const; + + // Makes maximum absolute value == 1.0 (if possible). + void normalize(); + +protected: + AudioBus() { }; + + void processWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus); + void processWithGainFromMonoStereo(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus); + + size_t m_length; + + Vector<OwnPtr<AudioChannel> > m_channels; + + int m_layout; + + double m_busGain; + bool m_isFirstTime; + double m_sampleRate; // 0.0 if unknown or N/A +}; + +} // WebCore + +#endif // AudioBus_h diff --git a/WebCore/platform/audio/AudioChannel.cpp b/WebCore/platform/audio/AudioChannel.cpp new file mode 100644 index 0000000..ad38219 --- /dev/null +++ b/WebCore/platform/audio/AudioChannel.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "AudioChannel.h" + +#include "Accelerate.h" +#include <algorithm> +#include <math.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +void AudioChannel::scale(double scale) +{ + float s = static_cast<float>(scale); + vsmul(data(), 1, &s, data(), 1, length()); +} + +void AudioChannel::copyFrom(const AudioChannel* sourceChannel) +{ + bool isSafe = (sourceChannel && sourceChannel->length() >= length()); + ASSERT(isSafe); + if (!isSafe) + return; + + memcpy(data(), sourceChannel->data(), sizeof(float) * length()); +} + +void AudioChannel::copyFromRange(const AudioChannel* sourceChannel, unsigned startFrame, unsigned endFrame) +{ + // Check that range is safe for reading from sourceChannel. + bool isRangeSafe = sourceChannel && startFrame < endFrame && endFrame <= sourceChannel->length(); + ASSERT(isRangeSafe); + if (!isRangeSafe) + return; + + // Check that this channel has enough space. + size_t rangeLength = endFrame - startFrame; + bool isRangeLengthSafe = rangeLength <= length(); + ASSERT(isRangeLengthSafe); + if (!isRangeLengthSafe) + return; + + const float* source = sourceChannel->data(); + float* destination = data(); + memcpy(destination, source + startFrame, sizeof(float) * rangeLength); +} + +void AudioChannel::sumFrom(const AudioChannel* sourceChannel) +{ + bool isSafe = sourceChannel && sourceChannel->length() >= length(); + ASSERT(isSafe); + if (!isSafe) + return; + + vadd(data(), 1, sourceChannel->data(), 1, data(), 1, length()); +} + +float AudioChannel::maxAbsValue() const +{ + const float* p = data(); + int n = length(); + + float max = 0.0f; + while (n--) + max = std::max(max, fabsf(*p++)); + + return max; +} + +} // WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/AudioChannel.h b/WebCore/platform/audio/AudioChannel.h new file mode 100644 index 0000000..511048c --- /dev/null +++ b/WebCore/platform/audio/AudioChannel.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioChannel_h +#define AudioChannel_h + +#include "AudioFloatArray.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +// An AudioChannel represents a buffer of non-interleaved floating-point audio samples. +// The PCM samples are normally assumed to be in a nominal range -1.0 -> +1.0 +class AudioChannel : public Noncopyable { +public: + // Memory can be externally referenced, or can be internally allocated with an AudioFloatArray. + + // Reference an external buffer. + AudioChannel(float* storage, size_t length) + : m_length(length), m_rawPointer(storage) { } + + // Manage storage for us. + explicit AudioChannel(size_t length) + : m_length(length) + , m_rawPointer(0) + { + m_memBuffer = adoptPtr(new AudioFloatArray(length)); + } + + // A "blank" audio channel -- must call set() before it's useful... + AudioChannel() + : m_length(0) + , m_rawPointer(0) + { + } + + // Redefine the memory for this channel. + // storage represents external memory not managed by this object. + void set(float* storage, size_t length) + { + m_memBuffer.clear(); // cleanup managed storage + m_rawPointer = storage; + m_length = length; + } + + // How many sample-frames do we contain? + size_t length() const { return m_length; } + + // Direct access to PCM sample data + float* data() { return m_rawPointer ? m_rawPointer : m_memBuffer->data(); } + const float* data() const { return m_rawPointer ? m_rawPointer : m_memBuffer->data(); } + + // Zeroes out all sample values in buffer. + void zero() + { + if (m_memBuffer.get()) + m_memBuffer->zero(); + else + memset(m_rawPointer, 0, sizeof(float) * m_length); + } + + // Scales all samples by the same amount. + void scale(double scale); + + // A simple memcpy() from the source channel + void copyFrom(const AudioChannel* sourceChannel); + + // Copies the given range from the source channel. + void copyFromRange(const AudioChannel* sourceChannel, unsigned startFrame, unsigned endFrame); + + // Sums (with unity gain) from the source channel. + void sumFrom(const AudioChannel* sourceChannel); + + // Returns maximum absolute value (useful for normalization). + float maxAbsValue() const; + +private: + size_t m_length; + + float* m_rawPointer; + OwnPtr<AudioFloatArray> m_memBuffer; +}; + +} // WebCore + +#endif // AudioChannel_h diff --git a/WebCore/platform/audio/AudioDSPKernel.h b/WebCore/platform/audio/AudioDSPKernel.h new file mode 100644 index 0000000..d9be6dc --- /dev/null +++ b/WebCore/platform/audio/AudioDSPKernel.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioDSPKernel_h +#define AudioDSPKernel_h + +#include "AudioDSPKernelProcessor.h" + +namespace WebCore { + +// AudioDSPKernel does the processing for one channel of an AudioDSPKernelProcessor. + +class AudioDSPKernel { +public: + AudioDSPKernel(AudioDSPKernelProcessor* kernelProcessor) + : m_kernelProcessor(kernelProcessor) + { + } + + virtual ~AudioDSPKernel() { }; + + // Subclasses must override process() to do the processing and reset() to reset DSP state. + virtual void process(const float* source, float* destination, size_t framesToProcess) = 0; + virtual void reset() = 0; + + double sampleRate() const { return processor()->sampleRate(); } + double nyquist() const { return 0.5 * sampleRate(); } + + AudioDSPKernelProcessor* processor() { return m_kernelProcessor; } + const AudioDSPKernelProcessor* processor() const { return m_kernelProcessor; } + +protected: + AudioDSPKernelProcessor* m_kernelProcessor; +}; + +} // namespace WebCore + +#endif // AudioDSPKernel_h diff --git a/WebCore/platform/audio/AudioDSPKernelProcessor.cpp b/WebCore/platform/audio/AudioDSPKernelProcessor.cpp new file mode 100644 index 0000000..d79afd5 --- /dev/null +++ b/WebCore/platform/audio/AudioDSPKernelProcessor.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "AudioDSPKernelProcessor.h" + +#include "AudioDSPKernel.h" + +namespace WebCore { + +// setNumberOfChannels() may later be called if the object is not yet in an "initialized" state. +AudioDSPKernelProcessor::AudioDSPKernelProcessor(double sampleRate, unsigned numberOfChannels) + : AudioProcessor(sampleRate) + , m_numberOfChannels(numberOfChannels) + , m_hasJustReset(true) +{ +} + +void AudioDSPKernelProcessor::initialize() +{ + if (isInitialized()) + return; + + ASSERT(!m_kernels.size()); + + // Create processing kernels, one per channel. + for (unsigned i = 0; i < numberOfChannels(); ++i) + m_kernels.append(createKernel()); + + m_initialized = true; +} + +void AudioDSPKernelProcessor::uninitialize() +{ + if (!isInitialized()) + return; + + m_kernels.clear(); + + m_initialized = false; +} + +void AudioDSPKernelProcessor::process(AudioBus* source, AudioBus* destination, size_t framesToProcess) +{ + ASSERT(source && destination); + if (!source || !destination) + return; + + if (!isInitialized()) { + destination->zero(); + return; + } + + bool channelCountMatches = source->numberOfChannels() == destination->numberOfChannels() && source->numberOfChannels() == m_kernels.size(); + ASSERT(channelCountMatches); + if (!channelCountMatches) + return; + + for (unsigned i = 0; i < m_kernels.size(); ++i) + m_kernels[i]->process(source->channel(i)->data(), destination->channel(i)->data(), framesToProcess); +} + +// Resets filter state +void AudioDSPKernelProcessor::reset() +{ + if (!isInitialized()) + return; + + // Forces snap to parameter values - first time. + // Any processing depending on this value must set it to false at the appropriate time. + m_hasJustReset = true; + + for (unsigned i = 0; i < m_kernels.size(); ++i) + m_kernels[i]->reset(); +} + +void AudioDSPKernelProcessor::setNumberOfChannels(unsigned numberOfChannels) +{ + ASSERT(!isInitialized()); + if (!isInitialized()) + m_numberOfChannels = numberOfChannels; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/AudioDSPKernelProcessor.h b/WebCore/platform/audio/AudioDSPKernelProcessor.h new file mode 100644 index 0000000..e87a810 --- /dev/null +++ b/WebCore/platform/audio/AudioDSPKernelProcessor.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioDSPKernelProcessor_h +#define AudioDSPKernelProcessor_h + +#include "AudioBus.h" +#include "AudioProcessor.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class AudioBus; +class AudioDSPKernel; +class AudioProcessor; + +// AudioDSPKernelProcessor processes one input -> one output (N channels each) +// It uses one AudioDSPKernel object per channel to do the processing, thus there is no cross-channel processing. +// Despite this limitation it turns out to be a very common and useful type of processor. + +class AudioDSPKernelProcessor : public AudioProcessor { +public: + // numberOfChannels may be later changed if object is not yet in an "initialized" state + AudioDSPKernelProcessor(double sampleRate, unsigned numberOfChannels); + + // Subclasses create the appropriate type of processing kernel here. + // We'll call this to create a kernel for each channel. + virtual PassOwnPtr<AudioDSPKernel> createKernel() = 0; + + // AudioProcessor methods + virtual void initialize(); + virtual void uninitialize(); + virtual void process(AudioBus* source, AudioBus* destination, size_t framesToProcess); + virtual void reset(); + virtual void setNumberOfChannels(unsigned numberOfChannels); + + unsigned numberOfChannels() const { return m_numberOfChannels; } + +protected: + unsigned m_numberOfChannels; + Vector<OwnPtr<AudioDSPKernel> > m_kernels; + bool m_hasJustReset; +}; + +} // namespace WebCore + +#endif // AudioDSPKernelProcessor_h diff --git a/WebCore/platform/audio/AudioProcessor.h b/WebCore/platform/audio/AudioProcessor.h new file mode 100644 index 0000000..69ba40f --- /dev/null +++ b/WebCore/platform/audio/AudioProcessor.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioProcessor_h +#define AudioProcessor_h + +namespace WebCore { + +class AudioBus; + +// AudioProcessor is an abstract base class representing an audio signal processing object with a single input and a single output, +// where the number of input channels equals the number of output channels. It can be used as one part of a complex DSP algorithm, +// or as the processor for a basic (one input - one output) AudioNode. + +class AudioProcessor { +public: + AudioProcessor(double sampleRate) + : m_initialized(false) + , m_sampleRate(sampleRate) + { + } + + virtual ~AudioProcessor() { } + + // Full initialization can be done here instead of in the constructor. + virtual void initialize() = 0; + virtual void uninitialize() = 0; + + // Processes the source to destination bus. The number of channels must match in source and destination. + virtual void process(AudioBus* source, AudioBus* destination, size_t framesToProcess) = 0; + + // Resets filter state + virtual void reset() = 0; + + virtual void setNumberOfChannels(unsigned) = 0; + + bool isInitialized() const { return m_initialized; } + + double sampleRate() const { return m_sampleRate; } + +protected: + bool m_initialized; + double m_sampleRate; +}; + +} // namespace WebCore + +#endif // AudioProcessor_h diff --git a/WebCore/platform/audio/AudioSourceProvider.h b/WebCore/platform/audio/AudioSourceProvider.h new file mode 100644 index 0000000..773546a --- /dev/null +++ b/WebCore/platform/audio/AudioSourceProvider.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioSourceProvider_h +#define AudioSourceProvider_h + +namespace WebCore { + +class AudioBus; + +// Abstract base-class for a pull-model client. +// provideInput() gets called repeatedly to render time-slices of a continuous audio stream. +class AudioSourceProvider { +public: + virtual void provideInput(AudioBus* bus, size_t framesToProcess) = 0; + virtual ~AudioSourceProvider() { } +}; + +} // WebCore + +#endif // AudioSourceProvider_h diff --git a/WebCore/platform/audio/Biquad.cpp b/WebCore/platform/audio/Biquad.cpp new file mode 100644 index 0000000..6918dd6 --- /dev/null +++ b/WebCore/platform/audio/Biquad.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "Biquad.h" + +#include "Accelerate.h" +#include <algorithm> +#include <float.h> +#include <math.h> +#include <stdio.h> + +namespace WebCore { + +const int kBufferSize = 1024; + +Biquad::Biquad() +{ +#if OS(DARWIN) + // Allocate two samples more for filter history + m_inputBuffer.resize(kBufferSize + 2); + m_outputBuffer.resize(kBufferSize + 2); +#endif + + // Initialize as pass-thru (straight-wire, no filter effect) + m_a0 = 1.0; + m_a1 = 0.0; + m_a2 = 0.0; + m_b1 = 0.0; + m_b2 = 0.0; + + m_g = 1.0; + + reset(); // clear filter memory +} + +void Biquad::process(const float* sourceP, float* destP, size_t framesToProcess) +{ +#if OS(DARWIN) + // Use vecLib if available + processFast(sourceP, destP, framesToProcess); +#else + int n = framesToProcess; + + // Create local copies of member variables + double x1 = m_x1; + double x2 = m_x2; + double y1 = m_y1; + double y2 = m_y2; + + double a0 = m_a0; + double a1 = m_a1; + double a2 = m_a2; + double b1 = m_b1; + double b2 = m_b2; + + while (n--) { + // FIXME: this can be optimized by pipelining the multiply adds... + float x = *sourceP++; + float y = a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2; + + y *= m_g; + + *destP++ = y; + + // Update state variables + x2 = x1; + x1 = x; + y2 = y1; + y1 = y; + } + + // Local variables back to member + m_x1 = x1; + m_x2 = x2; + m_y1 = y1; + m_y2 = y2; + + m_a0 = a0; + m_a1 = a1; + m_a2 = a2; + m_b1 = b1; + m_b2 = b2; +#endif +} + +#if OS(DARWIN) + +// Here we have optimized version using Accelerate.framework + +void Biquad::processFast(const float* sourceP, float* destP, size_t framesToProcess) +{ + // Filter coefficients + double B[5]; + B[0] = m_a0; + B[1] = m_a1; + B[2] = m_a2; + B[3] = m_b1; + B[4] = m_b2; + + double* inputP = m_inputBuffer.data(); + double* outputP = m_outputBuffer.data(); + + double* input2P = inputP + 2; + double* output2P = outputP + 2; + + // Break up processing into smaller slices (kBufferSize) if necessary. + + int n = framesToProcess; + + while (n > 0) { + int framesThisTime = n < kBufferSize ? n : kBufferSize; + + // Copy input to input buffer + for (int i = 0; i < framesThisTime; ++i) + input2P[i] = *sourceP++; + + processSliceFast(inputP, outputP, B, framesThisTime); + + // Copy output buffer to output (converts float -> double). + for (int i = 0; i < framesThisTime; ++i) + *destP++ = static_cast<float>(output2P[i]); + + n -= framesThisTime; + } +} + +void Biquad::processSliceFast(double* sourceP, double* destP, double* coefficientsP, size_t framesToProcess) +{ + // Use double-precision for filter stability + vDSP_deq22D(sourceP, 1, coefficientsP, destP, 1, framesToProcess); + + // Save history. Note that sourceP and destP reference m_inputBuffer and m_outputBuffer respectively. + // These buffers are allocated (in the constructor) with space for two extra samples so it's OK to access + // array values two beyond framesToProcess. + sourceP[0] = sourceP[framesToProcess - 2 + 2]; + sourceP[1] = sourceP[framesToProcess - 1 + 2]; + destP[0] = destP[framesToProcess - 2 + 2]; + destP[1] = destP[framesToProcess - 1 + 2]; +} + +#endif // OS(DARWIN) + + +void Biquad::reset() +{ + m_x1 = m_x2 = m_y1 = m_y2 = 0.0; + +#if OS(DARWIN) + // Two extra samples for filter history + double* inputP = m_inputBuffer.data(); + inputP[0] = 0.0; + inputP[1] = 0.0; + + double* outputP = m_outputBuffer.data(); + outputP[0] = 0.0; + outputP[1] = 0.0; +#endif +} + +void Biquad::setLowpassParams(double cutoff, double resonance) +{ + resonance = std::max(0.0, resonance); // can't go negative + + double g = pow(10.0, 0.05 * resonance); + double d = sqrt((4.0 - sqrt(16.0 - 16.0 / (g * g))) / 2.0); + + // Compute biquad coefficients for lopass filter + double theta = M_PI * cutoff; + double sn = 0.5 * d * sin(theta); + double beta = 0.5 * (1.0 - sn) / (1.0 + sn); + double gamma = (0.5 + beta) * cos(theta); + double alpha = 0.25 * (0.5 + beta - gamma); + + m_a0 = 2.0 * alpha; + m_a1 = 2.0 * 2.0*alpha; + m_a2 = 2.0 * alpha; + m_b1 = 2.0 * -gamma; + m_b2 = 2.0 * beta; +} + +void Biquad::setHighpassParams(double cutoff, double resonance) +{ + resonance = std::max(0.0, resonance); // can't go negative + + double g = pow(10.0, 0.05 * resonance); + double d = sqrt((4.0 - sqrt(16.0 - 16.0 / (g * g))) / 2.0); + + // Compute biquad coefficients for highpass filter + double theta = M_PI * cutoff; + double sn = 0.5 * d * sin(theta); + double beta = 0.5 * (1.0 - sn) / (1.0 + sn); + double gamma = (0.5 + beta) * cos(theta); + double alpha = 0.25 * (0.5 + beta + gamma); + + m_a0 = 2.0 * alpha; + m_a1 = 2.0 * -2.0*alpha; + m_a2 = 2.0 * alpha; + m_b1 = 2.0 * -gamma; + m_b2 = 2.0 * beta; +} + +void Biquad::setLowShelfParams(double cutoff, double dbGain) +{ + double theta = M_PI * cutoff; + + double A = pow(10.0, dbGain / 40.0); + double S = 1.0; // filter slope (1.0 is max value) + double alpha = 0.5 * sin(theta) * sqrt((A + 1.0 / A) * (1.0 / S - 1.0) + 2.0); + + double k = cos(theta); + double k2 = 2.0 * sqrt(A) * alpha; + + double b0 = A * ((A + 1.0) - (A - 1.0) * k + k2); + double b1 = 2.0 * A * ((A - 1.0) - (A + 1.0) * k); + double b2 = A * ((A + 1.0) - (A - 1.0) * k - k2); + double a0 = (A + 1.0) + (A - 1.0) * k + k2; + double a1 = -2.0 * ((A - 1.0) + (A + 1.0) * k); + double a2 = (A + 1.0) + (A - 1.0) * k - k2; + + double a0Inverse = 1.0 / a0; + + m_a0 = b0 * a0Inverse; + m_a1 = b1 * a0Inverse; + m_a2 = b2 * a0Inverse; + m_b1 = a1 * a0Inverse; + m_b2 = a2 * a0Inverse; +} + +void Biquad::setZeroPolePairs(const Complex &zero, const Complex &pole) +{ + m_a0 = 1.0; + m_a1 = -2.0 * zero.real(); + + double zeroMag = abs(zero); + m_a2 = zeroMag * zeroMag; + + m_b1 = -2.0 * pole.real(); + + double poleMag = abs(pole); + m_b2 = poleMag * poleMag; +} + +void Biquad::setAllpassPole(const Complex &pole) +{ + Complex zero = Complex(1.0, 0.0) / pole; + setZeroPolePairs(zero, pole); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/Biquad.h b/WebCore/platform/audio/Biquad.h new file mode 100644 index 0000000..d68bf4e --- /dev/null +++ b/WebCore/platform/audio/Biquad.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Biquad_h +#define Biquad_h + +#include "AudioArray.h" +#include <sys/types.h> +#include <wtf/Complex.h> +#include <wtf/Platform.h> + +namespace WebCore { + +// A basic biquad (two-zero / two-pole digital filter) +// +// It can be configured to a number of common and very useful filters: +// lowpass, highpass, shelving, parameteric, notch, allpass, ... + +class Biquad { +public: + Biquad(); + virtual ~Biquad() { } + + void process(const float* sourceP, float* destP, size_t framesToProcess); + + // cutoff is 0-1 normalized, resonance is in dB >= 0.0 + void setLowpassParams(double cutoff, double resonance); + void setHighpassParams(double cutoff, double resonance); + + void setLowShelfParams(double cutoff, double dbGain); + + // FIXME: need to implement a few more common filters + // void setHighShelfParams(double cutoff, double dbGain); + // void setParametricEQParams(double cutoff, double resonance); + + // Set the biquad coefficients given a single zero (other zero will be conjugate) + // and a single pole (other pole will be conjugate) + void setZeroPolePairs(const Complex& zero, const Complex& pole); + + // Set the biquad coefficients given a single pole (other pole will be conjugate) + // (The zeroes will be the inverse of the poles) + void setAllpassPole(const Complex& pole); + + // Resets filter state + void reset(); + +private: + // Filter coefficients + double m_a0; + double m_a1; + double m_a2; + double m_b1; + double m_b2; + + double m_g; + + // Filter memory + double m_x1; // input delayed by 1 sample + double m_x2; // input delayed by 2 samples + double m_y1; // output delayed by 1 sample + double m_y2; // output delayed by 2 samples + +#if OS(DARWIN) + void processFast(const float* sourceP, float* destP, size_t framesToProcess); + void processSliceFast(double* sourceP, double* destP, double* coefficientsP, size_t framesToProcess); + + AudioDoubleArray m_inputBuffer; + AudioDoubleArray m_outputBuffer; +#endif +}; + +} // namespace WebCore + +#endif // Biquad_h diff --git a/WebCore/platform/audio/Distance.cpp b/WebCore/platform/audio/Distance.cpp new file mode 100644 index 0000000..0f1b005 --- /dev/null +++ b/WebCore/platform/audio/Distance.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "Distance.h" + +#include <algorithm> +#include <math.h> + +using namespace std; + +namespace WebCore { + +DistanceEffect::DistanceEffect() + : m_model(ModelInverse) + , m_isClamped(true) + , m_refDistance(1.0) + , m_maxDistance(10000.0) + , m_rolloffFactor(1.0) +{ +} + +double DistanceEffect::gain(double distance) +{ + // don't go beyond maximum distance + distance = min(distance, m_maxDistance); + + // if clamped, don't get closer than reference distance + if (m_isClamped) + distance = max(distance, m_refDistance); + + switch (m_model) { + case ModelLinear: + return linearGain(distance); + break; + case ModelInverse: + return inverseGain(distance); + break; + case ModelExponential: + return exponentialGain(distance); + break; + + default: + return 0.0; + } +} + +double DistanceEffect::linearGain(double distance) +{ + return (1.0 - m_rolloffFactor * (distance - m_refDistance)) / (m_maxDistance - m_refDistance); +} + +double DistanceEffect::inverseGain(double distance) +{ + return m_refDistance / (m_refDistance + m_rolloffFactor * (distance - m_refDistance)); +} + +double DistanceEffect::exponentialGain(double distance) +{ + return pow(distance / m_refDistance, -m_rolloffFactor); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/Distance.h b/WebCore/platform/audio/Distance.h new file mode 100644 index 0000000..c7edded --- /dev/null +++ b/WebCore/platform/audio/Distance.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Distance_h +#define Distance_h + +namespace WebCore { + +// Distance models are defined according to the OpenAL specification + +class DistanceEffect { +public: + enum ModelType { + ModelLinear = 0, + ModelInverse = 1, + ModelExponential = 2 + }; + + DistanceEffect(); + + // Returns scalar gain for the given distance the current distance model is used + double gain(double distance); + + ModelType model() { return m_model; } + + void setModel(ModelType model, bool clamped) + { + m_model = model; + m_isClamped = clamped; + } + + // Distance params + void setRefDistance(double refDistance) { m_refDistance = refDistance; } + void setMaxDistance(double maxDistance) { m_maxDistance = maxDistance; } + void setRolloffFactor(double rolloffFactor) { m_rolloffFactor = rolloffFactor; } + + double refDistance() const { return m_refDistance; } + double maxDistance() const { return m_maxDistance; } + double rolloffFactor() const { return m_rolloffFactor; } + +protected: + double linearGain(double distance); + double inverseGain(double distance); + double exponentialGain(double distance); + + ModelType m_model; + bool m_isClamped; + double m_refDistance; + double m_maxDistance; + double m_rolloffFactor; +}; + +} // namespace WebCore + +#endif // Distance_h diff --git a/WebCore/platform/audio/FFTFrame.cpp b/WebCore/platform/audio/FFTFrame.cpp new file mode 100644 index 0000000..17292b6 --- /dev/null +++ b/WebCore/platform/audio/FFTFrame.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "FFTFrame.h" + +#include <wtf/Complex.h> +#include <wtf/MathExtras.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +void FFTFrame::doPaddedFFT(float* data, size_t dataSize) +{ + // Zero-pad the impulse response + AudioFloatArray paddedResponse(fftSize()); // zero-initialized + paddedResponse.copyToRange(data, 0, dataSize); + + // Get the frequency-domain version of padded response + doFFT(paddedResponse.data()); +} + +PassOwnPtr<FFTFrame> FFTFrame::createInterpolatedFrame(const FFTFrame& frame1, const FFTFrame& frame2, double x) +{ + OwnPtr<FFTFrame> newFrame = adoptPtr(new FFTFrame(frame1.fftSize())); + + newFrame->interpolateFrequencyComponents(frame1, frame2, x); + + // In the time-domain, the 2nd half of the response must be zero, to avoid circular convolution aliasing... + int fftSize = newFrame->fftSize(); + AudioFloatArray buffer(fftSize); + newFrame->doInverseFFT(buffer.data()); + buffer.zeroRange(fftSize / 2, fftSize); + + // Put back into frequency domain. + newFrame->doFFT(buffer.data()); + + return newFrame.release(); +} + +void FFTFrame::interpolateFrequencyComponents(const FFTFrame& frame1, const FFTFrame& frame2, double interp) +{ + // FIXME : with some work, this method could be optimized + + float* realP = realData(); + float* imagP = imagData(); + + const float* realP1 = frame1.realData(); + const float* imagP1 = frame1.imagData(); + const float* realP2 = frame2.realData(); + const float* imagP2 = frame2.imagData(); + + m_FFTSize = frame1.fftSize(); + m_log2FFTSize = frame1.log2FFTSize(); + + double s1base = (1.0 - interp); + double s2base = interp; + + double phaseAccum = 0.0; + double lastPhase1 = 0.0; + double lastPhase2 = 0.0; + + realP[0] = static_cast<float>(s1base * realP1[0] + s2base * realP2[0]); + imagP[0] = static_cast<float>(s1base * imagP1[0] + s2base * imagP2[0]); + + int n = m_FFTSize / 2; + + for (int i = 1; i < n; ++i) { + Complex c1(realP1[i], imagP1[i]); + Complex c2(realP2[i], imagP2[i]); + + double mag1 = abs(c1); + double mag2 = abs(c2); + + // Interpolate magnitudes in decibels + double mag1db = 20.0 * log10(mag1); + double mag2db = 20.0 * log10(mag2); + + double s1 = s1base; + double s2 = s2base; + + double magdbdiff = mag1db - mag2db; + + // Empirical tweak to retain higher-frequency zeroes + double threshold = (i > 16) ? 5.0 : 2.0; + + if (magdbdiff < -threshold && mag1db < 0.0) { + s1 = pow(s1, 0.75); + s2 = 1.0 - s1; + } else if (magdbdiff > threshold && mag2db < 0.0) { + s2 = pow(s2, 0.75); + s1 = 1.0 - s2; + } + + // Average magnitude by decibels instead of linearly + double magdb = s1 * mag1db + s2 * mag2db; + double mag = pow(10.0, 0.05 * magdb); + + // Now, deal with phase + double phase1 = arg(c1); + double phase2 = arg(c2); + + double deltaPhase1 = phase1 - lastPhase1; + double deltaPhase2 = phase2 - lastPhase2; + lastPhase1 = phase1; + lastPhase2 = phase2; + + // Unwrap phase deltas + if (deltaPhase1 > M_PI) + deltaPhase1 -= 2.0 * M_PI; + if (deltaPhase1 < -M_PI) + deltaPhase1 += 2.0 * M_PI; + if (deltaPhase2 > M_PI) + deltaPhase2 -= 2.0 * M_PI; + if (deltaPhase2 < -M_PI) + deltaPhase2 += 2.0 * M_PI; + + // Blend group-delays + double deltaPhaseBlend; + + if (deltaPhase1 - deltaPhase2 > M_PI) + deltaPhaseBlend = s1 * deltaPhase1 + s2 * (2.0 * M_PI + deltaPhase2); + else if (deltaPhase2 - deltaPhase1 > M_PI) + deltaPhaseBlend = s1 * (2.0 * M_PI + deltaPhase1) + s2 * deltaPhase2; + else + deltaPhaseBlend = s1 * deltaPhase1 + s2 * deltaPhase2; + + phaseAccum += deltaPhaseBlend; + + // Unwrap + if (phaseAccum > M_PI) + phaseAccum -= 2.0 * M_PI; + if (phaseAccum < -M_PI) + phaseAccum += 2.0 * M_PI; + + Complex c = complexFromMagnitudePhase(mag, phaseAccum); + + realP[i] = static_cast<float>(c.real()); + imagP[i] = static_cast<float>(c.imag()); + } +} + +double FFTFrame::extractAverageGroupDelay() +{ + float* realP = realData(); + float* imagP = imagData(); + + double aveSum = 0.0; + double weightSum = 0.0; + double lastPhase = 0.0; + + int halfSize = fftSize() / 2; + + const double kSamplePhaseDelay = (2.0 * M_PI) / double(fftSize()); + + // Calculate weighted average group delay + for (int i = 0; i < halfSize; i++) { + Complex c(realP[i], imagP[i]); + double mag = abs(c); + double phase = arg(c); + + double deltaPhase = phase - lastPhase; + lastPhase = phase; + + // Unwrap + if (deltaPhase < -M_PI) + deltaPhase += 2.0 * M_PI; + if (deltaPhase > M_PI) + deltaPhase -= 2.0 * M_PI; + + aveSum += mag * deltaPhase; + weightSum += mag; + } + + // Note how we invert the phase delta wrt frequency since this is how group delay is defined + double ave = aveSum / weightSum; + double aveSampleDelay = -ave / kSamplePhaseDelay; + + // Leave 20 sample headroom (for leading edge of impulse) + if (aveSampleDelay > 20.0) + aveSampleDelay -= 20.0; + + // Remove average group delay (minus 20 samples for headroom) + addConstantGroupDelay(-aveSampleDelay); + + // Remove DC offset + realP[0] = 0.0f; + + return aveSampleDelay; +} + +void FFTFrame::addConstantGroupDelay(double sampleFrameDelay) +{ + int halfSize = fftSize() / 2; + + float* realP = realData(); + float* imagP = imagData(); + + const double kSamplePhaseDelay = (2.0 * M_PI) / double(fftSize()); + + double phaseAdj = -sampleFrameDelay * kSamplePhaseDelay; + + // Add constant group delay + for (int i = 1; i < halfSize; i++) { + Complex c(realP[i], imagP[i]); + double mag = abs(c); + double phase = arg(c); + + phase += i * phaseAdj; + + Complex c2 = complexFromMagnitudePhase(mag, phase); + + realP[i] = static_cast<float>(c2.real()); + imagP[i] = static_cast<float>(c2.imag()); + } +} + +#ifndef NDEBUG +void FFTFrame::print() +{ + FFTFrame& frame = *this; + float* realP = frame.realData(); + float* imagP = frame.imagData(); + printf("**** \n"); + printf("DC = %f : nyquist = %f\n", realP[0], imagP[0]); + + int n = m_FFTSize / 2; + + for (int i = 1; i < n; i++) { + double mag = sqrt(realP[i] * realP[i] + imagP[i] * imagP[i]); + double phase = atan2(realP[i], imagP[i]); + + printf("[%d] (%f %f)\n", i, mag, phase); + } + printf("****\n"); +} +#endif // NDEBUG + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/FFTFrame.h b/WebCore/platform/audio/FFTFrame.h new file mode 100644 index 0000000..6147fc1 --- /dev/null +++ b/WebCore/platform/audio/FFTFrame.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FFTFrame_h +#define FFTFrame_h + +#include "AudioArray.h" + +#if OS(DARWIN) +#include <Accelerate/Accelerate.h> +#endif + +#include <wtf/PassOwnPtr.h> +#include <wtf/Platform.h> + +namespace WebCore { + +// Defines the interface for an "FFT frame", an object which is able to perform a forward +// and reverse FFT, internally storing the resultant frequency-domain data. + +class FFTFrame { +public: + // The constructors, destructor, and methods up to the CROSS-PLATFORM section have platform-dependent implementations. + + FFTFrame(unsigned fftSize); + FFTFrame(); // creates a blank/empty frame for later use with createInterpolatedFrame() + FFTFrame(const FFTFrame& frame); + ~FFTFrame(); + + static void cleanup(); + void doFFT(float* data); + void doInverseFFT(float* data); + void multiply(const FFTFrame& frame); // multiplies ourself with frame : effectively operator*=() + + float* realData() const; + float* imagData() const; + + void print(); // for debugging + + // CROSS-PLATFORM + // The remaining public methods have cross-platform implementations: + + // Interpolates from frame1 -> frame2 as x goes from 0.0 -> 1.0 + static PassOwnPtr<FFTFrame> createInterpolatedFrame(const FFTFrame& frame1, const FFTFrame& frame2, double x); + + void doPaddedFFT(float* data, size_t dataSize); // zero-padding with dataSize <= fftSize + double extractAverageGroupDelay(); + void addConstantGroupDelay(double sampleFrameDelay); + + unsigned fftSize() const { return m_FFTSize; } + unsigned log2FFTSize() const { return m_log2FFTSize; } + +private: + unsigned m_FFTSize; + unsigned m_log2FFTSize; + + void interpolateFrequencyComponents(const FFTFrame& frame1, const FFTFrame& frame2, double x); + +#if OS(DARWIN) + DSPSplitComplex& dspSplitComplex() { return m_frame; } + DSPSplitComplex dspSplitComplex() const { return m_frame; } + + static FFTSetup fftSetupForSize(unsigned fftSize); + + static FFTSetup* fftSetups; + + FFTSetup m_FFTSetup; + + DSPSplitComplex m_frame; + AudioFloatArray m_realData; + AudioFloatArray m_imagData; +#endif // OS(DARWIN) +}; + +} // namespace WebCore + +#endif // FFTFrame_h diff --git a/WebCore/platform/audio/Panner.cpp b/WebCore/platform/audio/Panner.cpp new file mode 100644 index 0000000..29a1fbe --- /dev/null +++ b/WebCore/platform/audio/Panner.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "Panner.h" + +#include "EqualPowerPanner.h" +#include "HRTFPanner.h" +#include "PassThroughPanner.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +PassOwnPtr<Panner> Panner::create(PanningModel model, double sampleRate) +{ + OwnPtr<Panner> panner; + + switch (model) { + case PanningModelEqualPower: + panner = adoptPtr(new EqualPowerPanner()); + break; + + case PanningModelHRTF: + panner = adoptPtr(new HRTFPanner(sampleRate)); + break; + + case PanningModelPassthrough: + panner = adoptPtr(new PassThroughPanner()); + break; + + // FIXME: sound field panning is not yet implemented... + case PanningModelSoundField: + default: + ASSERT_NOT_REACHED(); + return 0; + } + + return panner.release(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/Panner.h b/WebCore/platform/audio/Panner.h new file mode 100644 index 0000000..c8e219b --- /dev/null +++ b/WebCore/platform/audio/Panner.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Panner_h +#define Panner_h + +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class AudioBus; + +// Abstract base class for panning a mono or stereo source. + +class Panner { +public: + enum { + PanningModelPassthrough = 0, + PanningModelEqualPower = 1, + PanningModelHRTF = 2, + PanningModelSoundField = 3 + }; + + typedef unsigned PanningModel; + + static PassOwnPtr<Panner> create(PanningModel model, double sampleRate); + + virtual ~Panner() { }; + + PanningModel panningModel() const { return m_panningModel; } + + virtual void pan(double azimuth, double elevation, AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess) = 0; + + virtual void reset() = 0; + +protected: + Panner(PanningModel model) : m_panningModel(model) { } + + PanningModel m_panningModel; +}; + +} // namespace WebCore + +#endif // Panner_h diff --git a/WebCore/platform/audio/mac/FFTFrameMac.cpp b/WebCore/platform/audio/mac/FFTFrameMac.cpp new file mode 100644 index 0000000..0f7efb7 --- /dev/null +++ b/WebCore/platform/audio/mac/FFTFrameMac.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Mac OS X - specific FFTFrame implementation + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "FFTFrame.h" + +namespace WebCore { + +const int kMaxFFTPow2Size = 24; + +FFTSetup* FFTFrame::fftSetups = 0; + +// Normal constructor: allocates for a given fftSize +FFTFrame::FFTFrame(unsigned fftSize) + : m_realData(fftSize) + , m_imagData(fftSize) +{ + m_FFTSize = fftSize; + m_log2FFTSize = static_cast<unsigned>(log2(fftSize)); + + // We only allow power of two + ASSERT(1UL << m_log2FFTSize == m_FFTSize); + + // Lazily create and share fftSetup with other frames + m_FFTSetup = fftSetupForSize(fftSize); + + // Setup frame data + m_frame.realp = m_realData.data(); + m_frame.imagp = m_imagData.data(); +} + +// Creates a blank/empty frame (interpolate() must later be called) +FFTFrame::FFTFrame() + : m_realData(0) + , m_imagData(0) +{ + // Later will be set to correct values when interpolate() is called + m_frame.realp = 0; + m_frame.imagp = 0; + + m_FFTSize = 0; + m_log2FFTSize = 0; +} + +// Copy constructor +FFTFrame::FFTFrame(const FFTFrame& frame) + : m_FFTSize(frame.m_FFTSize) + , m_log2FFTSize(frame.m_log2FFTSize) + , m_FFTSetup(frame.m_FFTSetup) + , m_realData(frame.m_FFTSize) + , m_imagData(frame.m_FFTSize) +{ + // Setup frame data + m_frame.realp = m_realData.data(); + m_frame.imagp = m_imagData.data(); + + // Copy/setup frame data + unsigned nbytes = sizeof(float) * m_FFTSize; + memcpy(realData(), frame.m_frame.realp, nbytes); + memcpy(imagData(), frame.m_frame.imagp, nbytes); +} + +FFTFrame::~FFTFrame() +{ +} + +void FFTFrame::multiply(const FFTFrame& frame) +{ + FFTFrame& frame1 = *this; + const FFTFrame& frame2 = frame; + + float* realP1 = frame1.realData(); + float* imagP1 = frame1.imagData(); + const float* realP2 = frame2.realData(); + const float* imagP2 = frame2.imagData(); + + // Scale accounts for vecLib's peculiar scaling + // This ensures the right scaling all the way back to inverse FFT + float scale = 0.5f; + + // Multiply packed DC/nyquist component + realP1[0] *= scale * realP2[0]; + imagP1[0] *= scale * imagP2[0]; + + // Multiply the rest, skipping packed DC/Nyquist components + DSPSplitComplex sc1 = frame1.dspSplitComplex(); + sc1.realp++; + sc1.imagp++; + + DSPSplitComplex sc2 = frame2.dspSplitComplex(); + sc2.realp++; + sc2.imagp++; + + unsigned halfSize = m_FFTSize / 2; + + // Complex multiply + vDSP_zvmul(&sc1, 1, &sc2, 1, &sc1, 1, halfSize - 1, 1 /* normal multiplication */); + + // We've previously scaled the packed part, now scale the rest..... + vDSP_vsmul(sc1.realp, 1, &scale, sc1.realp, 1, halfSize - 1); + vDSP_vsmul(sc1.imagp, 1, &scale, sc1.imagp, 1, halfSize - 1); +} + +void FFTFrame::doFFT(float* data) +{ + vDSP_ctoz((DSPComplex*)data, 2, &m_frame, 1, m_FFTSize / 2); + vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_FORWARD); +} + +void FFTFrame::doInverseFFT(float* data) +{ + vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_INVERSE); + vDSP_ztoc(&m_frame, 1, (DSPComplex*)data, 2, m_FFTSize / 2); + + // Do final scaling so that x == IFFT(FFT(x)) + float scale = 0.5f / m_FFTSize; + vDSP_vsmul(data, 1, &scale, data, 1, m_FFTSize); +} + +FFTSetup FFTFrame::fftSetupForSize(unsigned fftSize) +{ + if (!fftSetups) { + fftSetups = (FFTSetup*)malloc(sizeof(FFTSetup) * kMaxFFTPow2Size); + memset(fftSetups, 0, sizeof(FFTSetup) * kMaxFFTPow2Size); + } + + int pow2size = static_cast<int>(log2(fftSize)); + ASSERT(pow2size < kMaxFFTPow2Size); + if (!fftSetups[pow2size]) + fftSetups[pow2size] = vDSP_create_fftsetup(pow2size, FFT_RADIX2); + + return fftSetups[pow2size]; +} + +void FFTFrame::cleanup() +{ + if (!fftSetups) + return; + + for (int i = 0; i < kMaxFFTPow2Size; ++i) { + if (fftSetups[i]) + vDSP_destroy_fftsetup(fftSetups[i]); + } + + free(fftSetups); + fftSetups = 0; +} + +float* FFTFrame::realData() const +{ + return m_frame.realp; +} + +float* FFTFrame::imagData() const +{ + return m_frame.imagp; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/chromium/ChromiumBridge.h b/WebCore/platform/chromium/ChromiumBridge.h index bc86de2..894799c 100644 --- a/WebCore/platform/chromium/ChromiumBridge.h +++ b/WebCore/platform/chromium/ChromiumBridge.h @@ -190,6 +190,9 @@ namespace WebCore { // That is committed size for Windows and virtual memory size for POSIX static int memoryUsageMB(); + // Same as above, but always returns actual value, without any caches. + static int actualMemoryUsageMB(); + // MimeType ----------------------------------------------------------- static bool isSupportedImageMIMEType(const String& mimeType); static bool isSupportedJavaScriptMIMEType(const String& mimeType); diff --git a/WebCore/platform/chromium/ClipboardChromium.cpp b/WebCore/platform/chromium/ClipboardChromium.cpp index 23508a6..aff1466 100644 --- a/WebCore/platform/chromium/ClipboardChromium.cpp +++ b/WebCore/platform/chromium/ClipboardChromium.cpp @@ -533,7 +533,7 @@ void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame) m_dataObject->textHtml = createMarkup(selectedRange, 0, AnnotateForInterchange, false, AbsoluteURLs); m_dataObject->htmlBaseUrl = frame->document()->url(); - String str = frame->selectedText(); + String str = frame->editor()->selectedText(); #if OS(WINDOWS) replaceNewlinesWithWindowsStyleNewlines(str); #endif diff --git a/WebCore/platform/chromium/PasteboardChromium.cpp b/WebCore/platform/chromium/PasteboardChromium.cpp index 58373b1..ba69b00 100644 --- a/WebCore/platform/chromium/PasteboardChromium.cpp +++ b/WebCore/platform/chromium/PasteboardChromium.cpp @@ -84,7 +84,7 @@ void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, String html = createMarkup(selectedRange, 0, AnnotateForInterchange, false, AbsoluteURLs); ExceptionCode ec = 0; KURL url = selectedRange->startContainer(ec)->document()->url(); - String plainText = frame->selectedText(); + String plainText = frame->editor()->selectedText(); #if OS(WINDOWS) replaceNewlinesWithWindowsStyleNewlines(plainText); #endif diff --git a/WebCore/platform/efl/ScrollbarEfl.cpp b/WebCore/platform/efl/ScrollbarEfl.cpp index e413260..6b00a37 100644 --- a/WebCore/platform/efl/ScrollbarEfl.cpp +++ b/WebCore/platform/efl/ScrollbarEfl.cpp @@ -84,7 +84,7 @@ static void scrollbarEflEdjeMessage(void* data, Evas_Object* o, Edje_Message_Typ m = static_cast<Edje_Message_Float*>(msg); v = m->val * (that->totalSize() - that->visibleSize()); - that->setValue(v); + that->setValue(v, Scrollbar::NotFromScrollAnimator); } void ScrollbarEfl::setParent(ScrollView* view) diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp index 0e93d4f..a0cf5a4 100644 --- a/WebCore/platform/graphics/Font.cpp +++ b/WebCore/platform/graphics/Font.cpp @@ -277,6 +277,8 @@ Font::CodePath Font::codePath(const TextRun& run) const return Complex; #endif + CodePath result = Simple; + // Start from 0 since drawing and highlighting also measure the characters before run->from for (int i = 0; i < run.length(); i++) { const UChar c = run[i]; @@ -312,8 +314,10 @@ Font::CodePath Font::codePath(const TextRun& run) const if (c < 0x1E00) // U+1E00 through U+2000 characters with diacritics and stacked diacritics continue; - if (c <= 0x2000) - return SimpleWithGlyphOverflow; + if (c <= 0x2000) { + result = SimpleWithGlyphOverflow; + continue; + } if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols continue; @@ -329,7 +333,7 @@ Font::CodePath Font::codePath(const TextRun& run) const if (typesettingFeatures()) return Complex; - return Simple; + return result; } } diff --git a/WebCore/platform/graphics/GraphicsContext.cpp b/WebCore/platform/graphics/GraphicsContext.cpp index 94f3424..3dfdb20 100644 --- a/WebCore/platform/graphics/GraphicsContext.cpp +++ b/WebCore/platform/graphics/GraphicsContext.cpp @@ -467,7 +467,7 @@ void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorS { if (paintingDisabled() || !image) return; - + float tsw = src.width(); float tsh = src.height(); float tw = dest.width(); @@ -489,7 +489,7 @@ void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorS } image->draw(this, styleColorSpace, dest, src, op, useLowQualityScale); - + if (useLowQualityScale) restore(); } @@ -571,6 +571,21 @@ void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&) } #endif +#if !PLATFORM(SKIA) +void GraphicsContext::setSharedGraphicsContext3D(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&) +{ +} + +void GraphicsContext::syncSoftwareCanvas() +{ +} + +void GraphicsContext::markDirtyRect(const IntRect&) +{ +} +#endif + + void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, const StrokeStyle& penStyle) { // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic @@ -600,14 +615,4 @@ void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2 } } -#if !PLATFORM(SKIA) -void GraphicsContext::setGraphicsContext3D(GraphicsContext3D*, const IntSize&) -{ -} - -void GraphicsContext::syncSoftwareCanvas() -{ -} -#endif - } diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index c5440f3..7863b95 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -21,7 +21,7 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GraphicsContext_h @@ -131,16 +131,17 @@ namespace WebCore { const int cMisspellingLinePatternGapWidth = 1; class AffineTransform; + class DrawingBuffer; class Font; class Generator; class Gradient; - class GraphicsContext3D; class GraphicsContextPlatformPrivate; class GraphicsContextPrivate; class ImageBuffer; class KURL; class Path; class Pattern; + class SharedGraphicsContext3D; class TextRun; // These bits can be ORed together for a total of 8 possible text drawing modes. @@ -456,8 +457,9 @@ namespace WebCore { pattern getHaikuStrokeStyle(); #endif - void setGraphicsContext3D(GraphicsContext3D*, const IntSize&); + void setSharedGraphicsContext3D(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&); void syncSoftwareCanvas(); + void markDirtyRect(const IntRect&); // Hints that a portion of the backing store is dirty. private: void savePlatformState(); diff --git a/WebCore/platform/graphics/GraphicsLayer.h b/WebCore/platform/graphics/GraphicsLayer.h index 0f74cd5..68a580e 100644 --- a/WebCore/platform/graphics/GraphicsLayer.h +++ b/WebCore/platform/graphics/GraphicsLayer.h @@ -85,18 +85,18 @@ class FloatPoint3D; class GraphicsContext; class Image; class TextStream; -struct TimingFunction; +class TimingFunction; // Base class for animation values (also used for transitions). Here to // represent values for properties being animated via the GraphicsLayer, // without pulling in style-related data from outside of the platform directory. class AnimationValue : public Noncopyable { public: - AnimationValue(float keyTime, const TimingFunction* timingFunction = 0) + AnimationValue(float keyTime, PassRefPtr<TimingFunction> timingFunction = 0) : m_keyTime(keyTime) { if (timingFunction) - m_timingFunction = adoptPtr(new TimingFunction(*timingFunction)); + m_timingFunction = timingFunction; } virtual ~AnimationValue() { } @@ -106,13 +106,13 @@ public: private: float m_keyTime; - OwnPtr<TimingFunction> m_timingFunction; + RefPtr<TimingFunction> m_timingFunction; }; // Used to store one float value of an animation. class FloatAnimationValue : public AnimationValue { public: - FloatAnimationValue(float keyTime, float value, const TimingFunction* timingFunction = 0) + FloatAnimationValue(float keyTime, float value, PassRefPtr<TimingFunction> timingFunction = 0) : AnimationValue(keyTime, timingFunction) , m_value(value) { @@ -127,7 +127,7 @@ private: // Used to store one transform value in a keyframe list. class TransformAnimationValue : public AnimationValue { public: - TransformAnimationValue(float keyTime, const TransformOperations* value = 0, const TimingFunction* timingFunction = 0) + TransformAnimationValue(float keyTime, const TransformOperations* value = 0, PassRefPtr<TimingFunction> timingFunction = 0) : AnimationValue(keyTime, timingFunction) { if (value) diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index a5db85f..2588d8d 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -699,6 +699,12 @@ void MediaPlayer::rateChanged() m_mediaPlayerClient->mediaPlayerRateChanged(this); } +void MediaPlayer::playbackStateChanged() +{ + if (m_mediaPlayerClient) + m_mediaPlayerClient->mediaPlayerPlaybackStateChanged(this); +} + } #endif diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 643f17f..bf445e3 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -70,6 +70,7 @@ struct PlatformMedia { QTMovieVisualContextType, GStreamerGWorldType, ChromiumMediaPlayerType, + QtMediaPlayerType, } type; union { @@ -78,6 +79,7 @@ struct PlatformMedia { QTMovieVisualContext* qtMovieVisualContext; GStreamerGWorld* gstreamerGWorld; MediaPlayerPrivateInterface* chromiumMediaPlayer; + MediaPlayerPrivateInterface* qtMediaPlayer; } media; }; @@ -119,6 +121,9 @@ public: // the playback rate has changed virtual void mediaPlayerRateChanged(MediaPlayer*) { } + // the play/pause status changed + virtual void mediaPlayerPlaybackStateChanged(MediaPlayer*) { } + // The MediaPlayer has found potentially problematic media content. // This is used internally to trigger swapping from a <video> // element to an <embed> in standalone documents @@ -244,6 +249,7 @@ public: void timeChanged(); void sizeChanged(); void rateChanged(); + void playbackStateChanged(); void durationChanged(); void repaint(); diff --git a/WebCore/platform/graphics/Path.cpp b/WebCore/platform/graphics/Path.cpp index 333afcb..4e2de53 100644 --- a/WebCore/platform/graphics/Path.cpp +++ b/WebCore/platform/graphics/Path.cpp @@ -39,7 +39,7 @@ static const float QUARTER = 0.552f; // approximation of control point positions // to simulate a quarter of a circle. namespace WebCore { -#if !PLATFORM(OPENVG) +#if !PLATFORM(OPENVG) && !PLATFORM(QT) static void pathLengthApplierFunction(void* info, const PathElement* element) { PathTraversalState& traversalState = *static_cast<PathTraversalState*>(info); diff --git a/WebCore/platform/graphics/Path.h b/WebCore/platform/graphics/Path.h index 61ea328..9896713 100644 --- a/WebCore/platform/graphics/Path.h +++ b/WebCore/platform/graphics/Path.h @@ -133,6 +133,11 @@ namespace WebCore { void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint); void addArcTo(const FloatPoint&, const FloatPoint&, float radius); void closeSubpath(); +#if PLATFORM(QT) + void closeCanvasSubpath(); +#else + void closeCanvasSubpath() { closeSubpath(); } +#endif void addArc(const FloatPoint&, float radius, float startAngle, float endAngle, bool anticlockwise); void addRect(const FloatRect&); diff --git a/WebCore/platform/graphics/cairo/FontCacheCairo.cpp b/WebCore/platform/graphics/cairo/FontCacheCairo.cpp index cb54549..5d3263e 100644 --- a/WebCore/platform/graphics/cairo/FontCacheCairo.cpp +++ b/WebCore/platform/graphics/cairo/FontCacheCairo.cpp @@ -23,7 +23,7 @@ #include "CString.h" #include "Font.h" -#include "GOwnPtrCairo.h" +#include "OwnPtrCairo.h" #include "SimpleFontData.h" #include <wtf/Assertions.h> @@ -115,15 +115,15 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD CString familyNameString = family.string().utf8(); const char* fcfamily = familyNameString.data(); - GOwnPtr<FcPattern> pattern(FcPatternCreate()); + OwnPtr<FcPattern> pattern(FcPatternCreate()); if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(fcfamily))) return 0; - GOwnPtr<FcObjectSet> objectSet(FcObjectSetCreate()); + OwnPtr<FcObjectSet> objectSet(FcObjectSetCreate()); if (!FcObjectSetAdd(objectSet.get(), FC_FAMILY)) return 0; - GOwnPtr<FcFontSet> fontSet(FcFontList(0, pattern.get(), objectSet.get())); + OwnPtr<FcFontSet> fontSet(FcFontList(0, pattern.get(), objectSet.get())); if (!fontSet) return 0; diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index 19cc518..283e75a 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -562,7 +562,7 @@ void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* poin cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr); - cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); + cairo_set_antialias(cr, antialiased ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); addConvexPolygonToContext(cr, numPoints, points); cairo_clip(cr); diff --git a/WebCore/platform/graphics/cairo/GOwnPtrCairo.cpp b/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp index 12df3cf..9be8670 100644 --- a/WebCore/platform/graphics/cairo/GOwnPtrCairo.cpp +++ b/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp @@ -18,7 +18,7 @@ */ #include "config.h" -#include "GOwnPtrCairo.h" +#include "OwnPtrCairo.h" #if defined(USE_FREETYPE) #include <cairo-ft.h> @@ -28,19 +28,19 @@ namespace WTF { #if defined(USE_FREETYPE) -template <> void freeOwnedGPtr<FcPattern>(FcPattern* ptr) +template <> void deleteOwnedPtr<FcPattern>(FcPattern* ptr) { if (ptr) FcPatternDestroy(ptr); } -template <> void freeOwnedGPtr<FcObjectSet>(FcObjectSet* ptr) +template <> void deleteOwnedPtr<FcObjectSet>(FcObjectSet* ptr) { if (ptr) FcObjectSetDestroy(ptr); } -template <> void freeOwnedGPtr<FcFontSet>(FcFontSet* ptr) +template <> void deleteOwnedPtr<FcFontSet>(FcFontSet* ptr) { if (ptr) FcFontSetDestroy(ptr); diff --git a/WebCore/platform/graphics/cairo/GOwnPtrCairo.h b/WebCore/platform/graphics/cairo/OwnPtrCairo.h index b099707..29f4562 100644 --- a/WebCore/platform/graphics/cairo/GOwnPtrCairo.h +++ b/WebCore/platform/graphics/cairo/OwnPtrCairo.h @@ -17,10 +17,10 @@ * Boston, MA 02110-1301 USA */ -#ifndef GOwnPtrCairo_h -#define GOwnPtrCairo_h +#ifndef OwnPtrCairo_h +#define OwnPtrCairo_h -#include "GOwnPtr.h" +#include "OwnPtr.h" #if defined(USE_FREETYPE) typedef struct _FcPattern FcPattern; @@ -31,9 +31,9 @@ typedef struct _FcFontSet FcFontSet; namespace WTF { #if defined(USE_FREETYPE) -template <> void freeOwnedGPtr<FcPattern>(FcPattern*); -template <> void freeOwnedGPtr<FcObjectSet>(FcObjectSet*); -template <> void freeOwnedGPtr<FcFontSet>(FcFontSet*); +template <> void deleteOwnedPtr<FcPattern>(FcPattern*); +template <> void deleteOwnedPtr<FcObjectSet>(FcObjectSet*); +template <> void deleteOwnedPtr<FcFontSet>(FcFontSet*); #endif } // namespace WTF diff --git a/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp b/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp index c4008cc..fadc385 100644 --- a/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp @@ -119,6 +119,7 @@ bool GraphicsContext3D::getImageData(Image* image, default: return false; } + break; case kCGImageAlphaNone: switch (componentsPerPixel) { case 1: diff --git a/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.cpp b/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.cpp new file mode 100644 index 0000000..9826c3e --- /dev/null +++ b/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "Canvas2DLayerChromium.h" + +#include "DrawingBuffer.h" + +#include <GLES2/gl2.h> + +namespace WebCore { + +PassRefPtr<Canvas2DLayerChromium> Canvas2DLayerChromium::create(DrawingBuffer* drawingBuffer, GraphicsLayerChromium* owner) +{ + return adoptRef(new Canvas2DLayerChromium(drawingBuffer, owner)); +} + +Canvas2DLayerChromium::Canvas2DLayerChromium(DrawingBuffer* drawingBuffer, GraphicsLayerChromium* owner) + : CanvasLayerChromium(owner) + , m_drawingBuffer(drawingBuffer) +{ +} + +Canvas2DLayerChromium::~Canvas2DLayerChromium() +{ + if (m_textureId) + glDeleteTextures(1, &m_textureId); +} + +void Canvas2DLayerChromium::updateContents() +{ + if (!m_drawingBuffer) + return; + if (m_textureChanged) { // We have to generate a new backing texture. + if (m_textureId) + glDeleteTextures(1, &m_textureId); + glGenTextures(1, &m_textureId); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textureId); + IntSize size = m_drawingBuffer->size(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + // Set the min-mag filters to linear and wrap modes to GL_CLAMP_TO_EDGE + // to get around NPOT texture limitations of GLES. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_textureChanged = false; + // FIXME: The glFinish() here is required because we have to make sure that the texture created in this + // context (the compositor context) is actually created by the service side before the child context + // attempts to use it (in publishToPlatformLayer). glFinish() is currently the only call with strong + // enough semantics to promise this, but is actually much stronger. Ideally we'd do something like + // inserting a fence here and waiting for it before trying to publish. + glFinish(); + } + // Update the contents of the texture used by the compositor. + if (m_contentsDirty) { + m_drawingBuffer->publishToPlatformLayer(); + m_contentsDirty = false; + } +} + +void Canvas2DLayerChromium::setTextureChanged() +{ + m_textureChanged = true; +} + +unsigned Canvas2DLayerChromium::textureId() const +{ + return m_textureId; +} + +void Canvas2DLayerChromium::setDrawingBuffer(DrawingBuffer* drawingBuffer) +{ + if (drawingBuffer != m_drawingBuffer) { + m_drawingBuffer = drawingBuffer; + m_textureChanged = true; + } +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.h b/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.h new file mode 100644 index 0000000..0031229 --- /dev/null +++ b/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef Canvas2DLayerChromium_h +#define Canvas2DLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "CanvasLayerChromium.h" + +namespace WebCore { + +class DrawingBuffer; + +// A layer containing an accelerated 2d canvas +class Canvas2DLayerChromium : public CanvasLayerChromium { +public: + static PassRefPtr<Canvas2DLayerChromium> create(DrawingBuffer*, GraphicsLayerChromium* owner); + virtual ~Canvas2DLayerChromium(); + virtual bool drawsContent() { return true; } + virtual void updateContents(); + + void setTextureChanged(); + unsigned textureId() const; + void setDrawingBuffer(DrawingBuffer*); + +private: + explicit Canvas2DLayerChromium(DrawingBuffer*, GraphicsLayerChromium* owner); + DrawingBuffer* m_drawingBuffer; + + static unsigned m_shaderProgramId; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/WebCore/platform/graphics/chromium/CanvasLayerChromium.cpp b/WebCore/platform/graphics/chromium/CanvasLayerChromium.cpp index bbf091c..56a7262 100644 --- a/WebCore/platform/graphics/chromium/CanvasLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/CanvasLayerChromium.cpp @@ -34,12 +34,14 @@ #include "CanvasLayerChromium.h" -#include "GraphicsContext3D.h" #include "LayerRendererChromium.h" + #include <GLES2/gl2.h> namespace WebCore { +unsigned CanvasLayerChromium::m_shaderProgramId = 0; + CanvasLayerChromium::SharedValues::SharedValues() : m_canvasShaderProgram(0) , m_shaderSamplerLocation(-1) @@ -93,49 +95,15 @@ CanvasLayerChromium::SharedValues::~SharedValues() GLC(glDeleteProgram(m_canvasShaderProgram)); } -PassRefPtr<CanvasLayerChromium> CanvasLayerChromium::create(GraphicsLayerChromium* owner) -{ - return adoptRef(new CanvasLayerChromium(owner)); -} - CanvasLayerChromium::CanvasLayerChromium(GraphicsLayerChromium* owner) : LayerChromium(owner) - , m_context(0) + , m_textureChanged(true) , m_textureId(0) - , m_textureChanged(false) -{ -} - -void CanvasLayerChromium::updateContents() { - ASSERT(m_context); - if (m_textureChanged) { - glBindTexture(GL_TEXTURE_2D, m_textureId); - // Set the min-mag filters to linear and wrap modes to GL_CLAMP_TO_EDGE - // to get around NPOT texture limitations of GLES. - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - m_textureChanged = false; - } - // Update the contents of the texture used by the compositor. - if (m_contentsDirty) { - if (m_prepareTextureCallback) - m_prepareTextureCallback->willPrepareTexture(); - m_context->prepareTexture(); - m_contentsDirty = false; - } } -void CanvasLayerChromium::setContext(const GraphicsContext3D* context) +CanvasLayerChromium::~CanvasLayerChromium() { - m_context = const_cast<GraphicsContext3D*>(context); - - unsigned int textureId = m_context->platformTexture(); - if (textureId != m_textureId) - m_textureChanged = true; - m_textureId = textureId; } void CanvasLayerChromium::draw() @@ -150,6 +118,7 @@ void CanvasLayerChromium::draw() drawTexturedQuad(layerRenderer()->projectionMatrix(), drawTransform(), bounds().width(), bounds().height(), drawOpacity(), sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); + } } diff --git a/WebCore/platform/graphics/chromium/CanvasLayerChromium.h b/WebCore/platform/graphics/chromium/CanvasLayerChromium.h index 053efff..d591c73 100644 --- a/WebCore/platform/graphics/chromium/CanvasLayerChromium.h +++ b/WebCore/platform/graphics/chromium/CanvasLayerChromium.h @@ -38,17 +38,12 @@ namespace WebCore { -class GraphicsContext3D; - -// A Layer containing a WebGL or accelerated 2d canvas +// Base class for WebGL and accelerated 2d canvases. class CanvasLayerChromium : public LayerChromium { public: - static PassRefPtr<CanvasLayerChromium> create(GraphicsLayerChromium* owner = 0); - virtual bool drawsContent() { return m_context; } - virtual void updateContents(); - virtual void draw(); + virtual ~CanvasLayerChromium(); - void setContext(const GraphicsContext3D* context); + virtual void draw(); class SharedValues { public: @@ -69,21 +64,16 @@ public: bool m_initialized; }; - class PrepareTextureCallback : public Noncopyable { - public: - virtual void willPrepareTexture() = 0; - }; - void setPrepareTextureCallback(PassOwnPtr<PrepareTextureCallback> callback) { m_prepareTextureCallback = callback; } - -private: +protected: explicit CanvasLayerChromium(GraphicsLayerChromium* owner); - GraphicsContext3D* m_context; - unsigned m_textureId; bool m_textureChanged; - OwnPtr<PrepareTextureCallback> m_prepareTextureCallback; + unsigned m_textureId; + +private: + static unsigned m_shaderProgramId; }; } #endif // USE(ACCELERATED_COMPOSITING) -#endif +#endif // CanvasLayerChromium_h diff --git a/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp b/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp index 974933d..48119bb 100644 --- a/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp @@ -42,6 +42,8 @@ #include "PlatformContextSkia.h" #include "skia/ext/platform_canvas.h" #elif PLATFORM(CG) +#include "LocalCurrentGraphicsContext.h" + #include <CoreGraphics/CGBitmapContext.h> #endif @@ -152,12 +154,6 @@ void ContentLayerChromium::updateContents() IntSize requiredTextureSize; IntSize bitmapSize; -#if PLATFORM(SKIA) - const SkBitmap* skiaBitmap = 0; - OwnPtr<skia::PlatformCanvas> canvas; - OwnPtr<PlatformContextSkia> skiaContext; - OwnPtr<GraphicsContext> graphicsContext; - requiredTextureSize = m_bounds; IntRect boundsRect(IntPoint(0, 0), m_bounds); @@ -171,17 +167,18 @@ void ContentLayerChromium::updateContents() dirtyRect.intersect(boundsRect); } +#if PLATFORM(SKIA) + const SkBitmap* skiaBitmap = 0; + OwnPtr<skia::PlatformCanvas> canvas; + OwnPtr<PlatformContextSkia> skiaContext; + OwnPtr<GraphicsContext> graphicsContext; + canvas.set(new skia::PlatformCanvas(dirtyRect.width(), dirtyRect.height(), false)); skiaContext.set(new PlatformContextSkia(canvas.get())); -#if OS(WINDOWS) - // This is needed to get text to show up correctly. Without it, - // GDI renders with zero alpha and the text becomes invisible. - // Unfortunately, setting this to true disables cleartype. + // This is needed to get text to show up correctly. // FIXME: Does this take us down a very slow text rendering path? - // FIXME: why is this is a windows-only call ? skiaContext->setDrawingToImageBuffer(true); -#endif graphicsContext.set(new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(skiaContext.get()))); @@ -201,19 +198,6 @@ void ContentLayerChromium::updateContents() bitmapSize = IntSize(skiaBitmap->width(), skiaBitmap->height()); } #elif PLATFORM(CG) - requiredTextureSize = m_bounds; - IntRect boundsRect(IntPoint(0, 0), m_bounds); - - // If the texture needs to be reallocated then we must redraw the entire - // contents of the layer. - if (requiredTextureSize != m_allocatedTextureSize) - dirtyRect = boundsRect; - else { - // Clip the dirtyRect to the size of the layer to avoid drawing outside - // the bounds of the backing texture. - dirtyRect.intersect(boundsRect); - } - Vector<uint8_t> tempVector; int rowBytes = 4 * dirtyRect.width(); tempVector.resize(rowBytes * dirtyRect.height()); @@ -225,6 +209,7 @@ void ContentLayerChromium::updateContents() kCGImageAlphaPremultipliedLast)); GraphicsContext graphicsContext(contextCG.get()); + LocalCurrentGraphicsContext scopedNSGraphicsContext(&graphicsContext); // Translate the graphics contxt into the coordinate system of the dirty rect. graphicsContext.translate(-dirtyRect.x(), -dirtyRect.y()); diff --git a/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp b/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp new file mode 100644 index 0000000..64981ee --- /dev/null +++ b/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "DrawingBuffer.h" + +#include "GraphicsContext3D.h" +#include "SharedGraphicsContext3D.h" + +#if USE(ACCELERATED_COMPOSITING) +#include "Canvas2DLayerChromium.h" +#endif + +#include <GLES2/gl2.h> +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES 1 +#endif +#include <GLES2/gl2ext.h> + +namespace WebCore { + +struct DrawingBufferInternal { + unsigned offscreenColorTexture; +#if USE(ACCELERATED_COMPOSITING) + RefPtr<Canvas2DLayerChromium> platformLayer; +#endif +}; + +static unsigned generateColorTexture(SharedGraphicsContext3D* context, const IntSize& size) +{ + unsigned offscreenColorTexture = context->createTexture(); + if (!offscreenColorTexture) + return 0; + + context->bindTexture(GraphicsContext3D::TEXTURE_2D, offscreenColorTexture); + context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::NEAREST); + context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::NEAREST); + context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); + context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); + context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, size.width(), size.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, 0); + context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, offscreenColorTexture, 0); + + return offscreenColorTexture; +} + + +DrawingBuffer::DrawingBuffer(SharedGraphicsContext3D* context, const IntSize& size, unsigned framebuffer) + : m_context(context) + , m_size(size) + , m_framebuffer(framebuffer) + , m_internal(new DrawingBufferInternal) +{ + context->bindFramebuffer(framebuffer); + m_internal->offscreenColorTexture = generateColorTexture(context, size); +} + +DrawingBuffer::~DrawingBuffer() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_internal->platformLayer) + m_internal->platformLayer->setDrawingBuffer(0); +#endif + m_context->bindFramebuffer(m_framebuffer); + m_context->deleteTexture(m_internal->offscreenColorTexture); + m_context->deleteFramebuffer(m_framebuffer); +} + +#if USE(ACCELERATED_COMPOSITING) +void DrawingBuffer::publishToPlatformLayer() +{ + if (m_callback) + m_callback->willPublish(); + unsigned parentTexture = m_internal->platformLayer->textureId(); + // FIXME: We do the copy in the canvas' (child) context so that it executes in the correct order relative to + // other commands in the child context. This ensures that the parent texture always contains a complete + // frame and not some intermediate result. However, there is no synchronization to ensure that this copy + // happens before the compositor draws. This means we might draw stale frames sometimes. Ideally this + // would insert a fence into the child command stream that the compositor could wait for. + m_context->makeContextCurrent(); + glCopyTextureToParentTexture(m_internal->offscreenColorTexture, parentTexture); + glFlush(); +} +#endif + +void DrawingBuffer::reset(const IntSize& newSize) +{ + if (m_size == newSize) + return; + m_size = newSize; + + m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_internal->offscreenColorTexture); + m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, m_size.width(), m_size.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, 0); + +#if USE(ACCELERATED_COMPOSITING) + if (m_internal->platformLayer) + m_internal->platformLayer->setTextureChanged(); +#endif +} + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* DrawingBuffer::platformLayer() +{ + if (!m_internal->platformLayer) + m_internal->platformLayer = Canvas2DLayerChromium::create(this, 0); + return m_internal->platformLayer.get(); +} +#endif + +unsigned DrawingBuffer::getRenderingResultsAsTexture() +{ + return m_internal->offscreenColorTexture; +} + +} diff --git a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp index 78b7517..8a77501 100644 --- a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -372,6 +372,8 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, int numGlyphs, const FloatPoint& point) const { + graphicsContext->platformContext()->prepareForSoftwareDraw(); + SkColor color = graphicsContext->platformContext()->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp index ec79b82..696cd9c 100644 --- a/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -65,13 +65,13 @@ static bool isCanvasMultiLayered(SkCanvas* canvas) return !layerIterator.done(); } -static void adjustTextRenderMode(SkPaint* paint, bool isCanvasMultiLayered) +static void adjustTextRenderMode(SkPaint* paint, PlatformContextSkia* skiaContext) { // Our layers only have a single alpha channel. This means that subpixel // rendered text cannot be compositied correctly when the layer is // collapsed. Therefore, subpixel text is disabled when we are drawing - // onto a layer. - if (isCanvasMultiLayered) + // onto a layer or when the compositor is being used. + if (isCanvasMultiLayered(skiaContext->canvas()) || skiaContext->isDrawingToImageBuffer()) paint->setLCDRenderText(false); } @@ -100,16 +100,17 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, y += SkFloatToScalar(adv[i].height()); } + gc->platformContext()->prepareForSoftwareDraw(); + SkCanvas* canvas = gc->platformContext()->canvas(); int textMode = gc->platformContext()->getTextDrawingMode(); - bool haveMultipleLayers = isCanvasMultiLayered(canvas); // We draw text up to two times (once for fill, once for stroke). if (textMode & cTextFill) { SkPaint paint; gc->platformContext()->setupPaintForFilling(&paint); font->platformData().setupPaint(&paint); - adjustTextRenderMode(&paint, haveMultipleLayers); + adjustTextRenderMode(&paint, gc->platformContext()); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setColor(gc->fillColor().rgb()); canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); @@ -122,7 +123,7 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, SkPaint paint; gc->platformContext()->setupPaintForStroking(&paint, 0, 0); font->platformData().setupPaint(&paint); - adjustTextRenderMode(&paint, haveMultipleLayers); + adjustTextRenderMode(&paint, gc->platformContext()); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setColor(gc->strokeColor().rgb()); @@ -499,7 +500,11 @@ private: // We overflowed our arrays. Resize and retry. // HB_ShapeItem fills in m_item.num_glyphs with the needed size. deleteGlyphArrays(); - createGlyphArrays(m_item.num_glyphs); + // The |+ 1| here is a workaround for a bug in Harfbuzz: the Khmer + // shaper (at least) can fail because of insufficient glyph buffers + // and request 0 additional glyphs: throwing us into an infinite + // loop. + createGlyphArrays(m_item.num_glyphs + 1); } } @@ -522,8 +527,11 @@ private: m_xPositions[i] = m_offsetX + position + offsetX; double advance = truncateFixedPointToInteger(m_item.advances[i]); - unsigned glyphIndex = m_item.item.pos + logClustersIndex; - if (isWordBreak(glyphIndex, isRTL)) { + // The first half of the conjuction works around the case where + // output glyphs aren't associated with any codepoints by the + // clusters log. + if (logClustersIndex < m_item.item.length + && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) { advance += m_wordSpacingAdjustment; if (m_padding > 0) { @@ -547,7 +555,7 @@ private: while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i) logClustersIndex--; } else { - while (logClustersIndex < m_item.num_glyphs && logClusters()[logClustersIndex] == i) + while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i) logClustersIndex++; } @@ -637,7 +645,6 @@ void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, } TextRunWalker walker(run, point.x(), this); - bool haveMultipleLayers = isCanvasMultiLayered(canvas); walker.setWordSpacingAdjustment(wordSpacing()); walker.setLetterSpacingAdjustment(letterSpacing()); walker.setPadding(run.padding()); @@ -645,13 +652,13 @@ void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, while (walker.nextScriptRun()) { if (fill) { walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); - adjustTextRenderMode(&fillPaint, haveMultipleLayers); + adjustTextRenderMode(&fillPaint, gc->platformContext()); canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint); } if (stroke) { walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); - adjustTextRenderMode(&strokePaint, haveMultipleLayers); + adjustTextRenderMode(&strokePaint, gc->platformContext()); canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint); } } diff --git a/WebCore/platform/graphics/chromium/GLES2Canvas.cpp b/WebCore/platform/graphics/chromium/GLES2Canvas.cpp index 82d4c0b..46aecf4 100644 --- a/WebCore/platform/graphics/chromium/GLES2Canvas.cpp +++ b/WebCore/platform/graphics/chromium/GLES2Canvas.cpp @@ -32,10 +32,12 @@ #include "GLES2Canvas.h" +#include "DrawingBuffer.h" #include "FloatRect.h" #include "GraphicsContext3D.h" #include "IntRect.h" #include "PlatformString.h" +#include "SharedGraphicsContext3D.h" #include "SolidFillShader.h" #include "TexShader.h" #include "Texture.h" @@ -61,37 +63,35 @@ struct GLES2Canvas::State { AffineTransform m_ctm; }; -GLES2Canvas::GLES2Canvas(GraphicsContext3D* context, const IntSize& size) - : m_context(context) +GLES2Canvas::GLES2Canvas(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const IntSize& size) + : m_size(size) + , m_context(context) + , m_drawingBuffer(drawingBuffer) , m_state(0) - , m_quadVertices(0) - , m_solidFillShader(SolidFillShader::create(context)) - , m_texShader(TexShader::create(context)) { m_flipMatrix.translate(-1.0f, 1.0f); m_flipMatrix.scale(2.0f / size.width(), -2.0f / size.height()); - m_context->reshape(size.width(), size.height()); - m_context->viewport(0, 0, size.width(), size.height()); - m_stateStack.append(State()); m_state = &m_stateStack.last(); - - // Force the source over composite mode to be applied. - m_lastCompositeOp = CompositeClear; - applyCompositeOperator(CompositeSourceOver); } GLES2Canvas::~GLES2Canvas() { - m_context->deleteBuffer(m_quadVertices); +} + +void GLES2Canvas::bindFramebuffer() +{ + m_drawingBuffer->bind(); } void GLES2Canvas::clearRect(const FloatRect& rect) { + bindFramebuffer(); if (m_state->m_ctm.isIdentity()) { - m_context->scissor(rect.x(), rect.y(), rect.width(), rect.height()); + m_context->scissor(rect); m_context->enable(GraphicsContext3D::SCISSOR_TEST); + m_context->clearColor(Color(RGBA32(0))); m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT); m_context->disable(GraphicsContext3D::SCISSOR_TEST); } else { @@ -104,16 +104,17 @@ void GLES2Canvas::clearRect(const FloatRect& rect) void GLES2Canvas::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) { - applyCompositeOperator(m_state->m_compositeOp); - - m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, getQuadVertices()); + m_context->applyCompositeOperator(m_state->m_compositeOp); + m_context->useQuadVertices(); AffineTransform matrix(m_flipMatrix); matrix.multLeft(m_state->m_ctm); matrix.translate(rect.x(), rect.y()); matrix.scale(rect.width(), rect.height()); - m_solidFillShader->use(matrix, color); + m_context->useFillSolidProgram(matrix, color); + + bindFramebuffer(); m_context->drawArrays(GraphicsContext3D::TRIANGLE_STRIP, 0, 4); } @@ -165,21 +166,33 @@ void GLES2Canvas::restore() m_state = &m_stateStack.last(); } +void GLES2Canvas::drawTexturedRect(unsigned texture, const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace colorSpace, CompositeOperator compositeOp) +{ + m_context->applyCompositeOperator(compositeOp); + + m_context->useQuadVertices(); + m_context->setActiveTexture(GraphicsContext3D::TEXTURE0); + + m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, texture); + + drawQuad(textureSize, srcRect, dstRect, m_state->m_ctm, m_state->m_alpha); +} + void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace colorSpace, CompositeOperator compositeOp) { drawTexturedRect(texture, srcRect, dstRect, m_state->m_ctm, m_state->m_alpha, colorSpace, compositeOp); } + void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha, ColorSpace colorSpace, CompositeOperator compositeOp) { - applyCompositeOperator(compositeOp); - - m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, getQuadVertices()); - checkGLError("glBindBuffer"); - + m_context->applyCompositeOperator(compositeOp); const TilingData& tiles = texture->tiles(); IntRect tileIdxRect = tiles.overlappedTileIndices(srcRect); + m_context->useQuadVertices(); + m_context->setActiveTexture(GraphicsContext3D::TEXTURE0); + for (int y = tileIdxRect.y(); y <= tileIdxRect.bottom(); y++) { for (int x = tileIdxRect.x(); x <= tileIdxRect.right(); x++) drawTexturedRectTile(texture, tiles.tileIndex(x, y), srcRect, dstRect, transform, alpha); @@ -193,7 +206,6 @@ void GLES2Canvas::drawTexturedRectTile(Texture* texture, int tile, const FloatRe const TilingData& tiles = texture->tiles(); - m_context->activeTexture(GraphicsContext3D::TEXTURE0); texture->bindTile(tile); FloatRect srcRectClippedInTileSpace; @@ -202,18 +214,24 @@ void GLES2Canvas::drawTexturedRectTile(Texture* texture, int tile, const FloatRe IntRect tileBoundsWithBorder = tiles.tileBoundsWithBorder(tile); + drawQuad(tileBoundsWithBorder.size(), srcRectClippedInTileSpace, dstRectIntersected, transform, alpha); +} + +void GLES2Canvas::drawQuad(const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha) +{ AffineTransform matrix(m_flipMatrix); matrix.multLeft(transform); - matrix.translate(dstRectIntersected.x(), dstRectIntersected.y()); - matrix.scale(dstRectIntersected.width(), dstRectIntersected.height()); + matrix.translate(dstRect.x(), dstRect.y()); + matrix.scale(dstRect.width(), dstRect.height()); AffineTransform texMatrix; - texMatrix.scale(1.0f / tileBoundsWithBorder.width(), 1.0f / tileBoundsWithBorder.height()); - texMatrix.translate(srcRectClippedInTileSpace.x(), srcRectClippedInTileSpace.y()); - texMatrix.scale(srcRectClippedInTileSpace.width(), srcRectClippedInTileSpace.height()); + texMatrix.scale(1.0f / textureSize.width(), 1.0f / textureSize.height()); + texMatrix.translate(srcRect.x(), srcRect.y()); + texMatrix.scale(srcRect.width(), srcRect.height()); - m_texShader->use(matrix, texMatrix, 0, alpha); + bindFramebuffer(); + m_context->useTextureProgram(matrix, texMatrix, alpha); m_context->drawArrays(GraphicsContext3D::TRIANGLE_STRIP, 0, 4); checkGLError("glDrawArrays"); } @@ -223,98 +241,14 @@ void GLES2Canvas::setCompositeOperation(CompositeOperator op) m_state->m_compositeOp = op; } -void GLES2Canvas::applyCompositeOperator(CompositeOperator op) -{ - if (op == m_lastCompositeOp) - return; - - switch (op) { - case CompositeClear: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::ZERO); - break; - case CompositeCopy: - m_context->disable(GraphicsContext3D::BLEND); - break; - case CompositeSourceOver: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); - break; - case CompositeSourceIn: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::DST_ALPHA, GraphicsContext3D::ZERO); - break; - case CompositeSourceOut: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ONE_MINUS_DST_ALPHA, GraphicsContext3D::ZERO); - break; - case CompositeSourceAtop: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::DST_ALPHA, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); - break; - case CompositeDestinationOver: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ONE_MINUS_DST_ALPHA, GraphicsContext3D::ONE); - break; - case CompositeDestinationIn: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::SRC_ALPHA); - break; - case CompositeDestinationOut: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); - break; - case CompositeDestinationAtop: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ONE_MINUS_DST_ALPHA, GraphicsContext3D::SRC_ALPHA); - break; - case CompositeXOR: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ONE_MINUS_DST_ALPHA, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); - break; - case CompositePlusDarker: - case CompositeHighlight: - // unsupported - m_context->disable(GraphicsContext3D::BLEND); - break; - case CompositePlusLighter: - m_context->enable(GraphicsContext3D::BLEND); - m_context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE); - break; - } - m_lastCompositeOp = op; -} - -unsigned GLES2Canvas::getQuadVertices() -{ - if (!m_quadVertices) { - float vertices[] = { 0.0f, 0.0f, 1.0f, - 1.0f, 0.0f, 1.0f, - 0.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f }; - m_quadVertices = m_context->createBuffer(); - m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_quadVertices); - m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, sizeof(vertices), vertices, GraphicsContext3D::STATIC_DRAW); - } - return m_quadVertices; -} - Texture* GLES2Canvas::createTexture(NativeImagePtr ptr, Texture::Format format, int width, int height) { - PassRefPtr<Texture> texture = m_textures.get(ptr); - if (texture) - return texture.get(); - - texture = Texture::create(m_context, format, width, height); - Texture* t = texture.get(); - m_textures.set(ptr, texture); - return t; + return m_context->createTexture(ptr, format, width, height); } Texture* GLES2Canvas::getTexture(NativeImagePtr ptr) { - PassRefPtr<Texture> texture = m_textures.get(ptr); - return texture ? texture.get() : 0; + return m_context->getTexture(ptr); } void GLES2Canvas::checkGLError(const char* header) diff --git a/WebCore/platform/graphics/chromium/GLES2Canvas.h b/WebCore/platform/graphics/chromium/GLES2Canvas.h index f49ac8b..6fc1a0e 100644 --- a/WebCore/platform/graphics/chromium/GLES2Canvas.h +++ b/WebCore/platform/graphics/chromium/GLES2Canvas.h @@ -45,16 +45,14 @@ namespace WebCore { class Color; +class DrawingBuffer; class FloatRect; class GraphicsContext3D; -class SolidFillShader; -class TexShader; - -typedef HashMap<NativeImagePtr, RefPtr<Texture> > TextureHashMap; +class SharedGraphicsContext3D; class GLES2Canvas : public Noncopyable { public: - GLES2Canvas(GraphicsContext3D*, const IntSize&); + GLES2Canvas(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&); ~GLES2Canvas(); void fillRect(const FloatRect&, const Color&, ColorSpace); @@ -74,28 +72,33 @@ public: // non-standard functions // These are not standard GraphicsContext functions, and should be pushed // down into a PlatformContextGLES2 at some point. + void drawTexturedRect(unsigned texture, const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace, CompositeOperator); void drawTexturedRect(Texture*, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform&, float alpha, ColorSpace, CompositeOperator); void drawTexturedRect(Texture*, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace, CompositeOperator); - GraphicsContext3D* context() { return m_context; } Texture* createTexture(NativeImagePtr, Texture::Format, int width, int height); Texture* getTexture(NativeImagePtr); + SharedGraphicsContext3D* context() const { return m_context; } + + void bindFramebuffer(); + + DrawingBuffer* drawingBuffer() const { return m_drawingBuffer; } + private: void drawTexturedRectTile(Texture* texture, int tile, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform&, float alpha); + void drawQuad(const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform&, float alpha); void applyCompositeOperator(CompositeOperator); void checkGLError(const char* header); - unsigned getQuadVertices(); - GraphicsContext3D* m_context; + IntSize m_size; + + SharedGraphicsContext3D* m_context; + DrawingBuffer* m_drawingBuffer; + struct State; WTF::Vector<State> m_stateStack; State* m_state; - unsigned m_quadVertices; - OwnPtr<SolidFillShader> m_solidFillShader; - OwnPtr<TexShader> m_texShader; AffineTransform m_flipMatrix; - TextureHashMap m_textures; - CompositeOperator m_lastCompositeOp; // This is the one last set, not necessarily the one in the state stack. }; } diff --git a/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp b/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp index 648e35f..bbae72a 100644 --- a/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp @@ -45,7 +45,9 @@ #include "GraphicsLayerChromium.h" +#include "Canvas2DLayerChromium.h" #include "ContentLayerChromium.h" +#include "DrawingBuffer.h" #include "FloatConversion.h" #include "FloatRect.h" #include "Image.h" @@ -104,6 +106,12 @@ GraphicsLayerChromium::GraphicsLayerChromium(GraphicsLayerClient* client) GraphicsLayerChromium::~GraphicsLayerChromium() { + if (m_layer) + m_layer->setOwner(0); + if (m_contentsLayer) + m_contentsLayer->setOwner(0); + if (m_transformLayer) + m_transformLayer->setOwner(0); } void GraphicsLayerChromium::setName(const String& inName) @@ -290,6 +298,7 @@ void GraphicsLayerChromium::setContentsNeedsDisplay() if (m_contentsLayer) m_contentsLayer->setNeedsDisplay(); } + void GraphicsLayerChromium::setNeedsDisplay() { if (drawsContent()) @@ -344,13 +353,13 @@ void GraphicsLayerChromium::setContentsToCanvas(PlatformLayer* platformLayer) bool childrenChanged = false; if (platformLayer) { platformLayer->setOwner(this); - if (!m_contentsLayer.get() || m_contentsLayerPurpose != ContentsLayerForCanvas) { + if (m_contentsLayer.get() != platformLayer) { setupContentsLayer(platformLayer); m_contentsLayer = platformLayer; m_contentsLayerPurpose = ContentsLayerForCanvas; childrenChanged = true; } - platformLayer->setNeedsDisplay(); + m_contentsLayer->setNeedsDisplay(); updateContentsRect(); } else { if (m_contentsLayer) { diff --git a/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h b/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h index 9dff66a..dde443d 100644 --- a/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h +++ b/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h @@ -138,7 +138,7 @@ private: NoContentsLayer = 0, ContentsLayerForImage, ContentsLayerForVideo, - ContentsLayerForCanvas + ContentsLayerForCanvas, }; ContentsLayerPurpose m_contentsLayerPurpose; diff --git a/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp index 4fd3ba0..59e8122 100644 --- a/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp +++ b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp @@ -38,6 +38,7 @@ #include "SkPath.h" #include "SkPoint.h" #include "SkRect.h" +#include "SkUtils.h" extern "C" { #include "harfbuzz-shaper.h" @@ -61,6 +62,15 @@ static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_u font->setupPaint(&paint); paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + unsigned codepoints = 0; + for (hb_uint32 i = 0; i < length; i++) { + if (!SkUTF16_IsHighSurrogate(characters[i])) + codepoints++; + if (codepoints > *glyphsSize) + return 0; + } + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), reinterpret_cast<uint16_t*>(glyphs)); // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our diff --git a/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp b/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp index 09b388d..060bb46 100644 --- a/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp @@ -108,8 +108,9 @@ void ImageLayerChromium::updateContents() // completely overwrite its contents with the image below. // Try to reuse the color space from the image to preserve its colors. // Some images use a color space (such as indexed) unsupported by the bitmap context. - RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGImageGetColorSpace(cgImage)); - CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace.get()); + RetainPtr<CGColorSpaceRef> colorSpaceReleaser; + CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage); + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); switch (colorSpaceModel) { case kCGColorSpaceModelMonochrome: case kCGColorSpaceModelRGB: @@ -118,12 +119,13 @@ void ImageLayerChromium::updateContents() case kCGColorSpaceModelDeviceN: break; default: - colorSpace.adoptCF(CGColorSpaceCreateDeviceRGB()); + colorSpaceReleaser.adoptCF(CGColorSpaceCreateDeviceRGB()); + colorSpace = colorSpaceReleaser.get(); break; } RetainPtr<CGContextRef> tempContext(AdoptCF, CGBitmapContextCreate(tempVector.data(), width, height, 8, tempRowBytes, - colorSpace.get(), + colorSpace, kCGImageAlphaPremultipliedLast)); CGContextSetBlendMode(tempContext.get(), kCGBlendModeCopy); CGContextDrawImage(tempContext.get(), diff --git a/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp b/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp index 50338d2..4708310 100644 --- a/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp +++ b/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp @@ -34,11 +34,11 @@ #if USE(ACCELERATED_COMPOSITING) #include "LayerRendererChromium.h" -#include "CanvasLayerChromium.h" -#include "ContentLayerChromium.h" +#include "Canvas2DLayerChromium.h" #include "GLES2Context.h" #include "LayerChromium.h" #include "NotImplemented.h" +#include "WebGLLayerChromium.h" #if PLATFORM(SKIA) #include "NativeImageSkia.h" #include "PlatformContextSkia.h" @@ -77,7 +77,14 @@ static inline bool compareLayerZ(const LayerChromium* a, const LayerChromium* b) PassOwnPtr<LayerRendererChromium> LayerRendererChromium::create(PassOwnPtr<GLES2Context> gles2Context) { - return new LayerRendererChromium(gles2Context); + if (!gles2Context) + return 0; + + OwnPtr<LayerRendererChromium> layerRenderer(new LayerRendererChromium(gles2Context)); + if (!layerRenderer->hardwareCompositing()) + return 0; + + return layerRenderer.release(); } LayerRendererChromium::LayerRendererChromium(PassOwnPtr<GLES2Context> gles2Context) @@ -91,7 +98,7 @@ LayerRendererChromium::LayerRendererChromium(PassOwnPtr<GLES2Context> gles2Conte , m_currentShader(0) , m_gles2Context(gles2Context) { - m_hardwareCompositing = (m_gles2Context && initializeSharedObjects()); + m_hardwareCompositing = initializeSharedObjects(); } LayerRendererChromium::~LayerRendererChromium() @@ -118,10 +125,7 @@ void LayerRendererChromium::setRootLayerCanvasSize(const IntSize& size) // the old ones. m_rootLayerCanvas = new skia::PlatformCanvas(size.width(), size.height(), false); m_rootLayerSkiaContext = new PlatformContextSkia(m_rootLayerCanvas.get()); -#if OS(WINDOWS) - // FIXME: why is this is a windows-only call ? m_rootLayerSkiaContext->setDrawingToImageBuffer(true); -#endif m_rootLayerGraphicsContext = new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(m_rootLayerSkiaContext.get())); #elif PLATFORM(CG) // Release the previous CGBitmapContext before reallocating the backing store as a precaution. diff --git a/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp b/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp index 7ff98b9..c0da285 100644 --- a/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp @@ -96,14 +96,9 @@ void VideoLayerChromium::updateContents() m_canvas = new skia::PlatformCanvas(dirtyRect.width(), dirtyRect.height(), true); m_skiaContext = new PlatformContextSkia(m_canvas.get()); -#if OS(WINDOWS) - // This is needed to get text to show up correctly. Without it, - // GDI renders with zero alpha and the text becomes invisible. - // Unfortunately, setting this to true disables cleartype. + // This is needed to get text to show up correctly. // FIXME: Does this take us down a very slow text rendering path? - // FIXME: Why is this is a windows-only call? m_skiaContext->setDrawingToImageBuffer(true); -#endif m_graphicsContext = new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(m_skiaContext.get())); } diff --git a/WebCore/platform/graphics/chromium/WebGLLayerChromium.cpp b/WebCore/platform/graphics/chromium/WebGLLayerChromium.cpp new file mode 100644 index 0000000..411f416 --- /dev/null +++ b/WebCore/platform/graphics/chromium/WebGLLayerChromium.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "WebGLLayerChromium.h" + +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" +#include <GLES2/gl2.h> + +namespace WebCore { + +PassRefPtr<WebGLLayerChromium> WebGLLayerChromium::create(GraphicsLayerChromium* owner) +{ + return adoptRef(new WebGLLayerChromium(owner)); +} + +WebGLLayerChromium::WebGLLayerChromium(GraphicsLayerChromium* owner) + : CanvasLayerChromium(owner) + , m_context(0) +{ +} + +void WebGLLayerChromium::updateContents() +{ + ASSERT(m_context); + if (m_textureChanged) { + glBindTexture(GL_TEXTURE_2D, m_textureId); + // Set the min-mag filters to linear and wrap modes to GL_CLAMP_TO_EDGE + // to get around NPOT texture limitations of GLES. + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_textureChanged = false; + } + // Update the contents of the texture used by the compositor. + if (m_contentsDirty) { + m_context->prepareTexture(); + m_contentsDirty = false; + } +} + +void WebGLLayerChromium::setContext(const GraphicsContext3D* context) +{ + m_context = const_cast<GraphicsContext3D*>(context); + + unsigned int textureId = m_context->platformTexture(); + if (textureId != m_textureId) + m_textureChanged = true; + m_textureId = textureId; +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/chromium/WebGLLayerChromium.h b/WebCore/platform/graphics/chromium/WebGLLayerChromium.h new file mode 100644 index 0000000..11b8db7 --- /dev/null +++ b/WebCore/platform/graphics/chromium/WebGLLayerChromium.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef WebGLLayerChromium_h +#define WebGLLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "CanvasLayerChromium.h" + +namespace WebCore { + +class GraphicsContext3D; + +// A Layer containing a WebGL canvas +class WebGLLayerChromium : public CanvasLayerChromium { +public: + static PassRefPtr<WebGLLayerChromium> create(GraphicsLayerChromium* owner = 0); + virtual bool drawsContent() { return m_context; } + virtual void updateContents(); + + void setContext(const GraphicsContext3D* context); + +private: + explicit WebGLLayerChromium(GraphicsLayerChromium* owner); + GraphicsContext3D* m_context; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/WebCore/platform/graphics/gpu/DrawingBuffer.cpp b/WebCore/platform/graphics/gpu/DrawingBuffer.cpp new file mode 100644 index 0000000..dc80954 --- /dev/null +++ b/WebCore/platform/graphics/gpu/DrawingBuffer.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "DrawingBuffer.h" + +#include "GraphicsContext3D.h" +#include "SharedGraphicsContext3D.h" + +namespace WebCore { + +PassOwnPtr<DrawingBuffer> DrawingBuffer::create(SharedGraphicsContext3D* context, const IntSize& size) +{ + unsigned framebuffer = context->createFramebuffer(); + ASSERT(framebuffer); + if (!framebuffer) + return 0; + return adoptPtr(new DrawingBuffer(context, size, framebuffer)); +} + +void DrawingBuffer::bind() +{ + m_context->bindFramebuffer(m_framebuffer); + m_context->setViewport(m_size); +} + +void DrawingBuffer::setWillPublishCallback(PassOwnPtr<WillPublishCallback> callback) +{ + m_callback = callback; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/gpu/DrawingBuffer.h b/WebCore/platform/graphics/gpu/DrawingBuffer.h new file mode 100644 index 0000000..23e6f4a --- /dev/null +++ b/WebCore/platform/graphics/gpu/DrawingBuffer.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DrawingBuffer_h +#define DrawingBuffer_h + +#include "GraphicsLayer.h" +#include "IntSize.h" + +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class SharedGraphicsContext3D; + +struct DrawingBufferInternal; + +// Manages a rendering target (framebuffer + attachment) for a canvas. Can publish its rendering +// results to a PlatformLayer for compositing. +class DrawingBuffer : public Noncopyable { +public: + static PassOwnPtr<DrawingBuffer> create(SharedGraphicsContext3D*, const IntSize&); + ~DrawingBuffer(); + + void reset(const IntSize&); + void bind(); + IntSize size() const { return m_size; } + +#if USE(ACCELERATED_COMPOSITING) + PlatformLayer* platformLayer(); + void publishToPlatformLayer(); +#endif + + unsigned getRenderingResultsAsTexture(); + + class WillPublishCallback : public Noncopyable { + public: + virtual void willPublish() = 0; + }; + + void setWillPublishCallback(PassOwnPtr<WillPublishCallback>); +private: + DrawingBuffer(SharedGraphicsContext3D*, const IntSize&, unsigned framebuffer); + + SharedGraphicsContext3D* m_context; + IntSize m_size; + unsigned m_framebuffer; + + OwnPtr<WillPublishCallback> m_callback; + OwnPtr<DrawingBufferInternal> m_internal; +}; + +} // namespace WebCore + +#endif // DrawingBuffer_h diff --git a/WebCore/platform/graphics/gpu/LoopBlinnClassifier.cpp b/WebCore/platform/graphics/gpu/LoopBlinnClassifier.cpp new file mode 100644 index 0000000..e43dc37 --- /dev/null +++ b/WebCore/platform/graphics/gpu/LoopBlinnClassifier.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2010 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 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 "LoopBlinnClassifier.h" + +#include "LoopBlinnMathUtils.h" + +namespace WebCore { + +using LoopBlinnMathUtils::approxEqual; +using LoopBlinnMathUtils::roundToZero; + +LoopBlinnClassifier::Result LoopBlinnClassifier::classify(const FloatPoint& c0, + const FloatPoint& c1, + const FloatPoint& c2, + const FloatPoint& c3) +{ + // Consult the chapter for the definitions of the following + // (terse) variable names. Note that the b0..b3 coordinates are + // homogeneous, so the "z" value (actually the w coordinate) must + // be 1.0. + FloatPoint3D b0(c0.x(), c0.y(), 1.0f); + FloatPoint3D b1(c1.x(), c1.y(), 1.0f); + FloatPoint3D b2(c2.x(), c2.y(), 1.0f); + FloatPoint3D b3(c3.x(), c3.y(), 1.0f); + + // Compute a1..a3. + float a1 = b0 * b3.cross(b2); + float a2 = b1 * b0.cross(b3); + float a3 = b2 * b1.cross(b0); + + // Compute d1..d3. + float d1 = a1 - 2 * a2 + 3 * a3; + float d2 = -a2 + 3 * a3; + float d3 = 3 * a3; + + // Experimentation has shown that the texture coordinates computed + // from these values quickly become huge, leading to roundoff errors + // and artifacts in the shader. It turns out that if we normalize + // the vector defined by (d1, d2, d3), this fixes the problem of the + // texture coordinates getting too large without affecting the + // classification results. + FloatPoint3D nd(d1, d2, d3); + nd.normalize(); + d1 = nd.x(); + d2 = nd.y(); + d3 = nd.z(); + + // Compute the discriminant. + // term0 is a common term in the computation which helps decide + // which way to classify the cusp case: as serpentine or loop. + float term0 = (3 * d2 * d2 - 4 * d1 * d3); + float discriminant = d1 * d1 * term0; + + // Experimentation has also shown that when the classification is + // near the boundary between one curve type and another, the shader + // becomes numerically unstable, particularly with the cusp case. + // Correct for this by rounding d1..d3 and the discriminant to zero + // when they get near it. + d1 = roundToZero(d1); + d2 = roundToZero(d2); + d3 = roundToZero(d3); + discriminant = roundToZero(discriminant); + + // Do the classification. + if (approxEqual(b0, b1) && approxEqual(b0, b2) && approxEqual(b0, b3)) + return Result(kPoint, d1, d2, d3); + + if (!discriminant) { + if (!d1 && !d2) { + if (!d3) + return Result(kLine, d1, d2, d3); + return Result(kQuadratic, d1, d2, d3); + } + + if (!d1) + return Result(kCusp, d1, d2, d3); + + // This is the boundary case described in Loop and Blinn's + // SIGGRAPH '05 paper of a cusp with inflection at infinity. + // Because term0 might not be exactly 0, we decide between using + // the serpentine and loop cases depending on its sign to avoid + // taking the square root of a negative number when computing the + // cubic texture coordinates. + if (term0 < 0) + return Result(kLoop, d1, d2, d3); + + return Result(kSerpentine, d1, d2, d3); + } + + if (discriminant > 0) + return Result(kSerpentine, d1, d2, d3); + + // discriminant < 0 + return Result(kLoop, d1, d2, d3); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/gpu/LoopBlinnClassifier.h b/WebCore/platform/graphics/gpu/LoopBlinnClassifier.h new file mode 100644 index 0000000..c665844 --- /dev/null +++ b/WebCore/platform/graphics/gpu/LoopBlinnClassifier.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 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 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. + */ + +// Cubic curve classification algorithm from "Rendering Vector Art on +// the GPU" by Loop and Blinn, GPU Gems 3, Chapter 25: +// http://http.developer.nvidia.com/GPUGems3/gpugems3_ch25.html . + +#ifndef LoopBlinnClassifier_h +#define LoopBlinnClassifier_h + +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class FloatPoint; + +// Classifies cubic curves into specific types. +class LoopBlinnClassifier : public Noncopyable { +public: + // The types of cubic curves. + enum CurveType { + kSerpentine, + kCusp, + kLoop, + kQuadratic, + kLine, + kPoint + }; + + // The result of the classifier. + struct Result { + public: + Result(CurveType inputCurveType, float inputD1, float inputD2, float inputD3) + : curveType(inputCurveType) + , d1(inputD1) + , d2(inputD2) + , d3(inputD3) { } + + CurveType curveType; + + // These are coefficients used later in the computation of + // texture coordinates per vertex. + float d1; + float d2; + float d3; + }; + + // Classifies the given cubic bezier curve starting at c0, ending + // at c3, and affected by control points c1 and c2. + static Result classify(const FloatPoint& c0, + const FloatPoint& c1, + const FloatPoint& c2, + const FloatPoint& c3); + +private: + // This class does not need to be instantiated. + LoopBlinnClassifier() { } +}; + +} // namespace WebCore + +#endif // LoopBlinnClassifier_h diff --git a/WebCore/platform/graphics/gpu/LoopBlinnConstants.h b/WebCore/platform/graphics/gpu/LoopBlinnConstants.h new file mode 100644 index 0000000..1997d9f --- /dev/null +++ b/WebCore/platform/graphics/gpu/LoopBlinnConstants.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 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 AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LoopBlinnConstants_h +#define LoopBlinnConstants_h + +namespace WebCore { +namespace LoopBlinnConstants { + +enum FillSide { + LeftSide, + RightSide +}; + +} // namespace LoopBlinnConstants +} // namespace WebCore + +#endif // LoopBlinnConstants_h diff --git a/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.cpp b/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.cpp new file mode 100644 index 0000000..61ebc9b --- /dev/null +++ b/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.cpp @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2010 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 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 "LoopBlinnMathUtils.h" + +#include "FloatPoint.h" +#include "MathExtras.h" +#include <algorithm> +#include <string.h> // for memcpy + +namespace WebCore { +namespace LoopBlinnMathUtils { + +namespace { + +// Utility functions local to this file. +int orientation(const FloatPoint& p1, + const FloatPoint& p2, + const FloatPoint& p3) +{ + float crossProduct = (p2.y() - p1.y()) * (p3.x() - p2.x()) - (p3.y() - p2.y()) * (p2.x() - p1.x()); + return (crossProduct < 0.0f) ? -1 : ((crossProduct > 0.0f) ? 1 : 0); +} + +bool edgeEdgeTest(const FloatSize& v0Delta, + const FloatPoint& v0, + const FloatPoint& u0, + const FloatPoint& u1) +{ + // This edge to edge test is based on Franlin Antonio's gem: "Faster + // Line Segment Intersection", in Graphics Gems III, pp. 199-202. + float ax = v0Delta.width(); + float ay = v0Delta.height(); + float bx = u0.x() - u1.x(); + float by = u0.y() - u1.y(); + float cx = v0.x() - u0.x(); + float cy = v0.y() - u0.y(); + float f = ay * bx - ax * by; + float d = by * cx - bx * cy; + if ((f > 0 && d >= 0 && d <= f) || (f < 0 && d <= 0 && d >= f)) { + float e = ax * cy - ay * cx; + + // This additional test avoids reporting coincident edges, which + // is the behavior we want. + if (approxEqual(e, 0) || approxEqual(f, 0) || approxEqual(e, f)) + return false; + + if (f > 0) + return e >= 0 && e <= f; + + return e <= 0 && e >= f; + } + return false; +} + +bool edgeAgainstTriangleEdges(const FloatPoint& v0, + const FloatPoint& v1, + const FloatPoint& u0, + const FloatPoint& u1, + const FloatPoint& u2) +{ + FloatSize delta = v1 - v0; + // Test edge u0, u1 against v0, v1. + if (edgeEdgeTest(delta, v0, u0, u1)) + return true; + // Test edge u1, u2 against v0, v1. + if (edgeEdgeTest(delta, v0, u1, u2)) + return true; + // Test edge u2, u1 against v0, v1. + if (edgeEdgeTest(delta, v0, u2, u0)) + return true; + return false; +} + +// A roundoff factor in the cubic classification and texture coordinate +// generation algorithms. It primarily determines the handling of corner +// cases during the classification process. Be careful when adjusting it; +// it has been determined empirically to work well. When changing it, you +// should look in particular at shapes that contain quadratic curves and +// ensure they still look smooth. Once pixel tests are running against this +// algorithm, they should provide sufficient coverage to ensure that +// adjusting the constant won't break anything. +const float Epsilon = 5.0e-4f; + +} // anonymous namespace + +// Exported routines + +float roundToZero(float val) +{ + if (val < Epsilon && val > -Epsilon) + return 0; + return val; +} + +bool approxEqual(const FloatPoint& v0, const FloatPoint& v1) +{ + return (v0 - v1).diagonalLengthSquared() < Epsilon * Epsilon; +} + +bool approxEqual(const FloatPoint3D& v0, const FloatPoint3D& v1) +{ + return (v0 - v1).lengthSquared() < Epsilon * Epsilon; +} + +bool approxEqual(float f0, float f1) +{ + return fabsf(f0 - f1) < Epsilon; +} + +bool linesIntersect(const FloatPoint& p1, + const FloatPoint& q1, + const FloatPoint& p2, + const FloatPoint& q2) +{ + return (orientation(p1, q1, p2) != orientation(p1, q1, q2) + && orientation(p2, q2, p1) != orientation(p2, q2, q1)); +} + +bool pointInTriangle(const FloatPoint& point, + const FloatPoint& a, + const FloatPoint& b, + const FloatPoint& c) +{ + // Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html + float x0 = c.x() - a.x(); + float y0 = c.y() - a.y(); + float x1 = b.x() - a.x(); + float y1 = b.y() - a.y(); + float x2 = point.x() - a.x(); + float y2 = point.y() - a.y(); + + float dot00 = x0 * x0 + y0 * y0; + float dot01 = x0 * x1 + y0 * y1; + float dot02 = x0 * x2 + y0 * y2; + float dot11 = x1 * x1 + y1 * y1; + float dot12 = x1 * x2 + y1 * y2; + float denominator = dot00 * dot11 - dot01 * dot01; + if (!denominator) + // Triangle is zero-area. Treat query point as not being inside. + return false; + // Compute + float inverseDenominator = 1.0f / denominator; + float u = (dot11 * dot02 - dot01 * dot12) * inverseDenominator; + float v = (dot00 * dot12 - dot01 * dot02) * inverseDenominator; + + return (u > 0.0f) && (v > 0.0f) && (u + v < 1.0f); +} + +bool trianglesOverlap(const FloatPoint& a1, + const FloatPoint& b1, + const FloatPoint& c1, + const FloatPoint& a2, + const FloatPoint& b2, + const FloatPoint& c2) +{ + // Derived from coplanar_tri_tri() at + // http://jgt.akpeters.com/papers/ShenHengTang03/tri_tri.html , + // simplified for the 2D case and modified so that overlapping edges + // do not report overlapping triangles. + + // Test all edges of triangle 1 against the edges of triangle 2. + if (edgeAgainstTriangleEdges(a1, b1, a2, b2, c2) + || edgeAgainstTriangleEdges(b1, c1, a2, b2, c2) + || edgeAgainstTriangleEdges(c1, a1, a2, b2, c2)) + return true; + // Finally, test if tri1 is totally contained in tri2 or vice versa. + // The paper above only performs the first two point-in-triangle tests. + // Because we define that triangles sharing a vertex or edge don't + // overlap, we must perform additional tests to see whether one + // triangle is contained in the other. + if (pointInTriangle(a1, a2, b2, c2) + || pointInTriangle(a2, a1, b1, c1) + || pointInTriangle(b1, a2, b2, c2) + || pointInTriangle(b2, a1, b1, c1) + || pointInTriangle(c1, a2, b2, c2) + || pointInTriangle(c2, a1, b1, c1)) + return true; + return false; +} + +namespace { + +// Helper routines for public XRay queries below. All of this code +// originated in Skia; see include/core/ and src/core/, SkScalar.h and +// SkGeometry.{cpp,h}. + +const float NearlyZeroConstant = (1.0f / (1 << 12)); + +bool nearlyZero(float x, float tolerance = NearlyZeroConstant) +{ + ASSERT(tolerance > 0.0f); + return ::fabsf(x) < tolerance; +} + +// Linearly interpolate between a and b, based on t. +// If t is 0, return a; if t is 1, return b; else interpolate. +// t must be [0..1]. +float interpolate(float a, float b, float t) +{ + ASSERT(t >= 0 && t <= 1); + return a + (b - a) * t; +} + +float evaluateCubic(float controlPoint0, float controlPoint1, float controlPoint2, float controlPoint3, float t) +{ + ASSERT(t >= 0 && t <= 1); + + if (!t) + return controlPoint0; + + float ab = interpolate(controlPoint0, controlPoint1, t); + float bc = interpolate(controlPoint1, controlPoint2, t); + float cd = interpolate(controlPoint2, controlPoint3, t); + float abc = interpolate(ab, bc, t); + float bcd = interpolate(bc, cd, t); + return interpolate(abc, bcd, t); +} + +// Evaluates the point on the source cubic specified by t, 0 <= t <= 1.0. +FloatPoint evaluateCubicAt(const FloatPoint cubic[4], float t) +{ + return FloatPoint(evaluateCubic(cubic[0].x(), cubic[1].x(), cubic[2].x(), cubic[3].x(), t), + evaluateCubic(cubic[0].y(), cubic[1].y(), cubic[2].y(), cubic[3].y(), t)); +} + +bool xRayCrossesMonotonicCubic(const XRay& xRay, const FloatPoint cubic[4], bool& ambiguous) +{ + ambiguous = false; + + // Find the minimum and maximum y of the extrema, which are the + // first and last points since this cubic is monotonic + float minY = std::min(cubic[0].y(), cubic[3].y()); + float maxY = std::max(cubic[0].y(), cubic[3].y()); + + if (xRay.y() == cubic[0].y() + || xRay.y() < minY + || xRay.y() > maxY) { + // The query line definitely does not cross the curve + ambiguous = (xRay.y() == cubic[0].y()); + return false; + } + + const bool pointAtExtremum = (xRay.y() == cubic[3].y()); + + float minX = std::min(std::min(std::min(cubic[0].x(), cubic[1].x()), + cubic[2].x()), + cubic[3].x()); + if (xRay.x() < minX) { + // The query line definitely crosses the curve + ambiguous = pointAtExtremum; + return true; + } + + float maxX = std::max(std::max(std::max(cubic[0].x(), cubic[1].x()), + cubic[2].x()), + cubic[3].x()); + if (xRay.x() > maxX) + // The query line definitely does not cross the curve + return false; + + // Do a binary search to find the parameter value which makes y as + // close as possible to the query point. See whether the query + // line's origin is to the left of the associated x coordinate. + + // MaxIterations is chosen as the number of mantissa bits for a float, + // since there's no way we are going to get more precision by + // iterating more times than that. + const int MaxIterations = 23; + FloatPoint evaluatedPoint; + int iter = 0; + float upperT; + float lowerT; + // Need to invert direction of t parameter if cubic goes up + // instead of down + if (cubic[3].y() > cubic[0].y()) { + upperT = 1; + lowerT = 0; + } else { + upperT = 0; + lowerT = 1; + } + do { + float t = 0.5f * (upperT + lowerT); + evaluatedPoint = evaluateCubicAt(cubic, t); + if (xRay.y() > evaluatedPoint.y()) + lowerT = t; + else + upperT = t; + } while (++iter < MaxIterations && !nearlyZero(evaluatedPoint.y() - xRay.y())); + + // FIXME: once we have more regression tests for this code, + // determine whether this should be using a fuzzy test. + if (xRay.x() <= evaluatedPoint.x()) { + ambiguous = pointAtExtremum; + return true; + } + return false; +} + +// Divides the numerator by the denominator safely for the case where +// the result must lie in the range (0..1). Result indicates whether +// the result is valid. +bool safeUnitDivide(float numerator, float denominator, float& ratio) +{ + if (numerator < 0) { + // Make the "numerator >= denominator" check below work. + numerator = -numerator; + denominator = -denominator; + } + if (!numerator || !denominator || numerator >= denominator) + return false; + float r = numerator / denominator; + if (isnan(r)) + return false; + ASSERT(r >= 0 && r < 1); + if (!r) // catch underflow if numerator <<<< denominator + return false; + ratio = r; + return true; +} + +// From Numerical Recipes in C. +// +// q = -1/2 (b + sign(b) sqrt[b*b - 4*a*c]) +// x1 = q / a +// x2 = c / q +// +// Returns the number of real roots of the equation [0..2]. Roots are +// returned in sorted order, smaller root first. +int findUnitQuadRoots(float a, float b, float c, float roots[2]) +{ + if (!a) + return safeUnitDivide(-c, b, roots[0]) ? 1 : 0; + + float discriminant = b*b - 4*a*c; + if (discriminant < 0 || isnan(discriminant)) // complex roots + return 0; + discriminant = sqrtf(discriminant); + + float q = (b < 0) ? -(b - discriminant) / 2 : -(b + discriminant) / 2; + int numberOfRoots = 0; + if (safeUnitDivide(q, a, roots[numberOfRoots])) + ++numberOfRoots; + if (safeUnitDivide(c, q, roots[numberOfRoots])) + ++numberOfRoots; + if (numberOfRoots == 2) { + // Seemingly have two roots. Check for equality and sort. + if (roots[0] == roots[1]) + return 1; + if (roots[0] > roots[1]) + std::swap(roots[0], roots[1]); + } + return numberOfRoots; +} + +// Cubic'(t) = pt^2 + qt + r, where +// p = 3(-a + 3(b - c) + d) +// q = 6(a - 2b + c) +// r = 3(b - a) +// Solve for t, keeping only those that fit between 0 < t < 1. +int findCubicExtrema(float a, float b, float c, float d, float tValues[2]) +{ + // Divide p, q, and r by 3 to simplify the equations. + float p = d - a + 3*(b - c); + float q = 2*(a - b - b + c); + float r = b - a; + + return findUnitQuadRoots(p, q, r, tValues); +} + +void interpolateCubicCoords(float controlPoint0, float controlPoint1, float controlPoint2, float controlPoint3, float* dst, float t) +{ + float ab = interpolate(controlPoint0, controlPoint1, t); + float bc = interpolate(controlPoint1, controlPoint2, t); + float cd = interpolate(controlPoint2, controlPoint3, t); + float abc = interpolate(ab, bc, t); + float bcd = interpolate(bc, cd, t); + float abcd = interpolate(abc, bcd, t); + + dst[0] = controlPoint0; + dst[2] = ab; + dst[4] = abc; + dst[6] = abcd; + dst[8] = bcd; + dst[10] = cd; + dst[12] = controlPoint3; +} + +#ifndef NDEBUG +bool isUnitInterval(float x) +{ + return x > 0 && x < 1; +} +#endif + +void chopCubicAtTValues(const FloatPoint src[4], FloatPoint dst[], const float tValues[], int roots) +{ +#ifndef NDEBUG + for (int i = 0; i < roots - 1; ++i) { + ASSERT(isUnitInterval(tValues[i])); + ASSERT(isUnitInterval(tValues[i+1])); + ASSERT(tValues[i] < tValues[i+1]); + } +#endif + + if (!roots) { + // nothing to chop + for (int j = 0; j < 4; ++j) + dst[j] = src[j]; + return; + } + + float t = tValues[0]; + FloatPoint tmp[4]; + for (int j = 0; j < 4; ++j) + tmp[j] = src[j]; + + for (int i = 0; i < roots; ++i) { + chopCubicAt(tmp, dst, t); + if (i == roots - 1) + break; + + dst += 3; + // Make tmp contain the remaining cubic (after the first chop). + for (int j = 0; j < 4; ++j) + tmp[j] = dst[j]; + + // Watch out for the case that the renormalized t isn't in range. + if (!safeUnitDivide(tValues[i+1] - tValues[i], 1.0f - tValues[i], t)) { + // If it isn't, just create a degenerate cubic. + dst[4] = dst[5] = dst[6] = tmp[3]; + break; + } + } +} + +void flattenDoubleCubicYExtrema(FloatPoint coords[7]) +{ + coords[2].setY(coords[3].y()); + coords[4].setY(coords[3].y()); +} + +int chopCubicAtYExtrema(const FloatPoint src[4], FloatPoint dst[10]) +{ + float tValues[2]; + int roots = findCubicExtrema(src[0].y(), src[1].y(), src[2].y(), src[3].y(), tValues); + + chopCubicAtTValues(src, dst, tValues, roots); + if (roots) { + // we do some cleanup to ensure our Y extrema are flat + flattenDoubleCubicYExtrema(&dst[0]); + if (roots == 2) + flattenDoubleCubicYExtrema(&dst[3]); + } + return roots; +} + +} // anonymous namespace + +// Public cubic operations. + +void chopCubicAt(const FloatPoint src[4], FloatPoint dst[7], float t) +{ + ASSERT(t >= 0 && t <= 1); + + float output[14]; + interpolateCubicCoords(src[0].x(), src[1].x(), src[2].x(), src[3].x(), &output[0], t); + interpolateCubicCoords(src[0].y(), src[1].y(), src[2].y(), src[3].y(), &output[1], t); + for (int i = 0; i < 7; i++) + dst[i].set(output[2 * i], output[2 * i + 1]); +} + +// Public XRay queries. + +bool xRayCrossesLine(const XRay& xRay, const FloatPoint pts[2], bool& ambiguous) +{ + ambiguous = false; + + // Determine quick discards. + // Consider query line going exactly through point 0 to not + // intersect, for symmetry with xRayCrossesMonotonicCubic. + if (xRay.y() == pts[0].y()) { + ambiguous = true; + return false; + } + if (xRay.y() < pts[0].y() && xRay.y() < pts[1].y()) + return false; + if (xRay.y() > pts[0].y() && xRay.y() > pts[1].y()) + return false; + if (xRay.x() > pts[0].x() && xRay.x() > pts[1].x()) + return false; + // Determine degenerate cases + if (nearlyZero(pts[0].y() - pts[1].y())) + return false; + if (nearlyZero(pts[0].x() - pts[1].x())) { + // We've already determined the query point lies within the + // vertical range of the line segment. + if (xRay.x() <= pts[0].x()) { + ambiguous = (xRay.y() == pts[1].y()); + return true; + } + return false; + } + // Ambiguity check + if (xRay.y() == pts[1].y()) { + if (xRay.x() <= pts[1].x()) { + ambiguous = true; + return true; + } + return false; + } + // Full line segment evaluation + float deltaY = pts[1].y() - pts[0].y(); + float deltaX = pts[1].x() - pts[0].x(); + float slope = deltaY / deltaX; + float b = pts[0].y() - slope * pts[0].x(); + // Solve for x coordinate at y = xRay.y() + float x = (xRay.y() - b) / slope; + return xRay.x() <= x; +} + +int numXRayCrossingsForCubic(const XRay& xRay, const FloatPoint cubic[4], bool& ambiguous) +{ + int numCrossings = 0; + FloatPoint monotonicCubics[10]; + int numMonotonicCubics = 1 + chopCubicAtYExtrema(cubic, monotonicCubics); + ambiguous = false; + FloatPoint* monotonicCubicsPointer = &monotonicCubics[0]; + for (int i = 0; i < numMonotonicCubics; ++i) { + if (xRayCrossesMonotonicCubic(xRay, monotonicCubicsPointer, ambiguous)) + ++numCrossings; + if (ambiguous) + return 0; + monotonicCubicsPointer += 3; + } + return numCrossings; +} + +} // namespace LoopBlinnMathUtils +} // namespace WebCore diff --git a/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.h b/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.h new file mode 100644 index 0000000..b9d19c5 --- /dev/null +++ b/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2010 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 AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LoopBlinnMathUtils_h +#define LoopBlinnMathUtils_h + +#include "FloatPoint.h" +#include "FloatPoint3D.h" +#include <math.h> + +namespace WebCore { + +// Use a namespace for these so we can easily import them. +namespace LoopBlinnMathUtils { + +float roundToZero(float val); +bool approxEqual(const FloatPoint& v0, const FloatPoint& v1); +bool approxEqual(const FloatPoint3D& v0, const FloatPoint3D& v1); +bool approxEqual(float f0, float f1); + +// Determines whether the line segment between (p1, q1) intersects +// that between (p2, q2). +bool linesIntersect(const FloatPoint& p1, + const FloatPoint& q1, + const FloatPoint& p2, + const FloatPoint& q2); + +// Determines whether "point" is inside the 2D triangle defined by +// vertices a, b, and c. This test defines that points exactly on an +// edge are not considered to be inside the triangle. +bool pointInTriangle(const FloatPoint& point, + const FloatPoint& a, + const FloatPoint& b, + const FloatPoint& c); + +// Determines whether the triangles defined by the points (a1, b1, c1) +// and (a2, b2, c2) overlap. The definition of this function is that +// if the two triangles only share an adjacent edge or vertex, they +// are not considered to overlap. +bool trianglesOverlap(const FloatPoint& a1, + const FloatPoint& b1, + const FloatPoint& c1, + const FloatPoint& a2, + const FloatPoint& b2, + const FloatPoint& c2); + +// Given a src cubic bezier, chops it at the specified t value, +// where 0 < t < 1, and returns the two new cubics in dst[0..3] +// and dst[3..6]. +void chopCubicAt(const FloatPoint src[4], FloatPoint dst[7], float t); + +// "X-Ray" queries. An XRay is a half-line originating at the given +// point and extending to x=+infinity. +typedef FloatPoint XRay; + +// Given an arbitrary cubic bezier, return the number of times an XRay +// crosses the cubic. Valid return values are [0..3]. +// +// By definition the cubic is open at the starting point; in other +// words, if pt.fY is equivalent to cubic[0].fY, and pt.fX is to the +// left of the curve, the line is not considered to cross the curve, +// but if it is equal to cubic[3].fY then it is considered to +// cross. +// +// Outgoing "ambiguous" argument indicates whether the answer is ambiguous +// because the query occurred exactly at one of the endpoints' y +// coordinates or at a tangent point, indicating that another query y +// coordinate is preferred for robustness. +int numXRayCrossingsForCubic(const XRay& xRay, + const FloatPoint cubic[4], + bool& ambiguous); + +// Given a line segment from lineEndpoints[0] to lineEndpoints[1], and an +// XRay, returns true if they intersect. Outgoing "ambiguous" argument +// indicates whether the answer is ambiguous because the query occurred +// exactly at one of the endpoints' y coordinates, indicating that another +// query y coordinate is preferred for robustness. +bool xRayCrossesLine(const XRay& xRay, + const FloatPoint lineEndpoints[2], + bool& ambiguous); + +} // namespace LoopBlinnMathUtils + +} // namespace WebCore + +#endif // LoopBlinnMathUtils_h diff --git a/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.cpp b/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.cpp new file mode 100644 index 0000000..ac82637 --- /dev/null +++ b/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010 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 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 "LoopBlinnTextureCoords.h" + +#include <math.h> +#include <wtf/Assertions.h> + +namespace WebCore { + +LoopBlinnTextureCoords::Result LoopBlinnTextureCoords::compute(const LoopBlinnClassifier::Result& classification, LoopBlinnConstants::FillSide sideToFill) +{ + // Loop and Blinn's formulation states that the right side of the + // curve is defined to be the inside (filled region), but for some + // reason it looks like with the default orientation parameters we + // are filling the left side of the curve. Regardless, because we + // can receive arbitrarily oriented curves as input, we might have + // to reverse the orientation of the cubic texture coordinates even + // in cases where the paper doesn't say it is necessary. + bool reverseOrientation = false; + static const float OneThird = 1.0f / 3.0f; + static const float TwoThirds = 2.0f / 3.0f; + LoopBlinnClassifier::CurveType curveType = classification.curveType; + + LoopBlinnTextureCoords::Result result; + + switch (curveType) { + case LoopBlinnClassifier::kSerpentine: { + float t1 = sqrtf(9.0f * classification.d2 * classification.d2 - 12 * classification.d1 * classification.d3); + float ls = 3.0f * classification.d2 - t1; + float lt = 6.0f * classification.d1; + float ms = 3.0f * classification.d2 + t1; + float mt = lt; + float ltMinusLs = lt - ls; + float mtMinusMs = mt - ms; + result.klmCoordinates[0] = FloatPoint3D(ls * ms, + ls * ls * ls, + ms * ms * ms); + result.klmCoordinates[1] = FloatPoint3D(OneThird * (3.0f * ls * ms - ls * mt - lt * ms), + ls * ls * (ls - lt), + ms * ms * (ms - mt)); + result.klmCoordinates[2] = FloatPoint3D(OneThird * (lt * (mt - 2.0f * ms) + ls * (3.0f * ms - 2.0f * mt)), + ltMinusLs * ltMinusLs * ls, + mtMinusMs * mtMinusMs * ms); + result.klmCoordinates[3] = FloatPoint3D(ltMinusLs * mtMinusMs, + -(ltMinusLs * ltMinusLs * ltMinusLs), + -(mtMinusMs * mtMinusMs * mtMinusMs)); + if (classification.d1 < 0.0f) + reverseOrientation = true; + break; + } + + case LoopBlinnClassifier::kLoop: { + float t1 = sqrtf(4.0f * classification.d1 * classification.d3 - 3.0f * classification.d2 * classification.d2); + float ls = classification.d2 - t1; + float lt = 2.0f * classification.d1; + float ms = classification.d2 + t1; + float mt = lt; + + // Figure out whether there is a rendering artifact requiring + // the curve to be subdivided by the caller. + float ql = ls / lt; + float qm = ms / mt; + if (0.0f < ql && ql < 1.0f) { + result.hasRenderingArtifact = true; + result.subdivisionParameterValue = ql; + return result; + } + + if (0.0f < qm && qm < 1.0f) { + result.hasRenderingArtifact = true; + result.subdivisionParameterValue = qm; + return result; + } + + float ltMinusLs = lt - ls; + float mtMinusMs = mt - ms; + result.klmCoordinates[0] = FloatPoint3D(ls * ms, + ls * ls * ms, + ls * ms * ms); + result.klmCoordinates[1] = FloatPoint3D(OneThird * (-ls * mt - lt * ms + 3.0f * ls * ms), + -OneThird * ls * (ls * (mt - 3.0f * ms) + 2.0f * lt * ms), + -OneThird * ms * (ls * (2.0f * mt - 3.0f * ms) + lt * ms)); + result.klmCoordinates[2] = FloatPoint3D(OneThird * (lt * (mt - 2.0f * ms) + ls * (3.0f * ms - 2.0f * mt)), + OneThird * (lt - ls) * (ls * (2.0f * mt - 3.0f * ms) + lt * ms), + OneThird * (mt - ms) * (ls * (mt - 3.0f * ms) + 2.0f * lt * ms)); + result.klmCoordinates[3] = FloatPoint3D(ltMinusLs * mtMinusMs, + -(ltMinusLs * ltMinusLs) * mtMinusMs, + -ltMinusLs * mtMinusMs * mtMinusMs); + reverseOrientation = ((classification.d1 > 0.0f && result.klmCoordinates[0].x() < 0.0f) + || (classification.d1 < 0.0f && result.klmCoordinates[0].x() > 0.0f)); + break; + } + + case LoopBlinnClassifier::kCusp: { + float ls = classification.d3; + float lt = 3.0f * classification.d2; + float lsMinusLt = ls - lt; + result.klmCoordinates[0] = FloatPoint3D(ls, + ls * ls * ls, + 1.0f); + result.klmCoordinates[1] = FloatPoint3D(ls - OneThird * lt, + ls * ls * lsMinusLt, + 1.0f); + result.klmCoordinates[2] = FloatPoint3D(ls - TwoThirds * lt, + lsMinusLt * lsMinusLt * ls, + 1.0f); + result.klmCoordinates[3] = FloatPoint3D(lsMinusLt, + lsMinusLt * lsMinusLt * lsMinusLt, + 1.0f); + break; + } + + case LoopBlinnClassifier::kQuadratic: { + result.klmCoordinates[0] = FloatPoint3D(0, 0, 0); + result.klmCoordinates[1] = FloatPoint3D(OneThird, 0, OneThird); + result.klmCoordinates[2] = FloatPoint3D(TwoThirds, OneThird, TwoThirds); + result.klmCoordinates[3] = FloatPoint3D(1, 1, 1); + if (classification.d3 < 0) + reverseOrientation = true; + break; + } + + case LoopBlinnClassifier::kLine: + case LoopBlinnClassifier::kPoint: + result.isLineOrPoint = true; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + + if (sideToFill == LoopBlinnConstants::RightSide) + reverseOrientation = !reverseOrientation; + + if (reverseOrientation) { + for (int i = 0; i < 4; ++i) { + result.klmCoordinates[i].setX(-result.klmCoordinates[i].x()); + result.klmCoordinates[i].setY(-result.klmCoordinates[i].y()); + } + } + + return result; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.h b/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.h new file mode 100644 index 0000000..5fdeb3b --- /dev/null +++ b/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 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 AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LoopBlinnTextureCoords_h +#define LoopBlinnTextureCoords_h + +#include "FloatPoint3D.h" +#include "LoopBlinnClassifier.h" +#include "LoopBlinnConstants.h" + +#include <wtf/Noncopyable.h> + +namespace WebCore { + +// Computes three-dimensional texture coordinates for the control +// points of a cubic curve for rendering via the shader in "Rendering +// Vector Art on the GPU" by Loop and Blinn, GPU Gems 3, Chapter 25. +class LoopBlinnTextureCoords { +public: + // Container for the cubic texture coordinates and other associated + // information. + struct Result { + Result() + : isLineOrPoint(false) + , hasRenderingArtifact(false) + , subdivisionParameterValue(0.0f) { } + + // The (k, l, m) texture coordinates that are to be associated + // with the four control points of the cubic curve. + FloatPoint3D klmCoordinates[4]; + + // Indicates whether the curve is a line or a point, in which case + // we do not need to add its triangles to the mesh. + bool isLineOrPoint; + + // For the loop case, indicates whether a rendering artifact was + // detected, in which case the curve needs to be further + // subdivided. + bool hasRenderingArtifact; + + // If a rendering artifact will occur for the given loop curve, + // this is the parameter value (0 <= value <= 1) at which the + // curve needs to be subdivided to fix the artifact. + float subdivisionParameterValue; + }; + + // Computes the texture coordinates for a cubic curve segment's + // control points, given the classification of the curve as well as + // an indication of which side is to be filled. + static Result compute(const LoopBlinnClassifier::Result& classification, + LoopBlinnConstants::FillSide sideToFill); + +private: + // This class does not need to be instantiated. + LoopBlinnTextureCoords() { } +}; + +} // namespace WebCore + +#endif // LoopBlinnTextureCoords_h diff --git a/WebCore/platform/graphics/gpu/PODArena.h b/WebCore/platform/graphics/gpu/PODArena.h new file mode 100644 index 0000000..f68baef --- /dev/null +++ b/WebCore/platform/graphics/gpu/PODArena.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2010 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 AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PODArena_h +#define PODArena_h + +#include <stdint.h> +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// An arena which allocates only Plain Old Data (POD), or classes and +// structs bottoming out in Plain Old Data. NOTE: the constructors of +// the objects allocated in this arena are called, but _not_ their +// destructors. + +class PODArena : public RefCounted<PODArena> { +public: + // The arena is configured with an allocator, which is responsible + // for allocating and freeing chunks of memory at a time. + class Allocator : public RefCounted<Allocator> { + public: + virtual void* allocate(size_t size) = 0; + virtual void free(void* ptr) = 0; + protected: + virtual ~Allocator() { } + friend class WTF::RefCounted<Allocator>; + }; + + // The Arena's default allocator, which uses fastMalloc and + // fastFree to allocate chunks of storage. + class FastMallocAllocator : public Allocator { + public: + static PassRefPtr<FastMallocAllocator> create() + { + return adoptRef(new FastMallocAllocator); + } + + virtual void* allocate(size_t size) { return fastMalloc(size); } + virtual void free(void* ptr) { fastFree(ptr); } + + protected: + FastMallocAllocator() { } + }; + + // Creates a new PODArena configured with a FastMallocAllocator. + static PassRefPtr<PODArena> create() + { + return adoptRef(new PODArena); + } + + // Creates a new PODArena configured with the given Allocator. + static PassRefPtr<PODArena> create(PassRefPtr<Allocator> allocator) + { + return adoptRef(new PODArena(allocator)); + } + + // Allocates an object from the arena. + template<class T> T* allocateObject() + { + void* ptr = allocateBase<T>(); + if (ptr) { + // Use placement operator new to allocate a T at this location. + new(ptr) T(); + } + return static_cast<T*>(ptr); + } + + // Allocates an object from the arena, calling a single-argument constructor. + template<class T, class Argument1Type> T* allocateObject(const Argument1Type& argument1) + { + void* ptr = allocateBase<T>(); + if (ptr) { + // Use placement operator new to allocate a T at this location. + new(ptr) T(argument1); + } + return static_cast<T*>(ptr); + } + + // The initial size of allocated chunks; increases as necessary to + // satisfy large allocations. Mainly public for unit tests. + enum { + DefaultChunkSize = 16384 + }; + +protected: + ~PODArena() { } + friend class WTF::RefCounted<PODArena>; + +private: + PODArena() + : m_allocator(FastMallocAllocator::create()) + , m_current(0) + , m_currentChunkSize(DefaultChunkSize) { } + + explicit PODArena(PassRefPtr<Allocator> allocator) + : m_allocator(allocator) + , m_current(0) + , m_currentChunkSize(DefaultChunkSize) { } + + // Returns the alignment requirement for classes and structs on the + // current platform. + template <class T> static size_t minAlignment() + { + return WTF_ALIGN_OF(T); + } + + template<class T> void* allocateBase() + { + void* ptr = 0; + size_t roundedSize = roundUp(sizeof(T), minAlignment<T>()); + if (m_current) + ptr = m_current->allocate(roundedSize); + + if (!ptr) { + if (roundedSize > m_currentChunkSize) + m_currentChunkSize = roundedSize; + m_chunks.append(adoptPtr(new Chunk(m_allocator.get(), m_currentChunkSize))); + m_current = m_chunks.last().get(); + ptr = m_current->allocate(roundedSize); + } + return ptr; + } + + // Rounds up the given allocation size to the specified alignment. + size_t roundUp(size_t size, size_t alignment) + { + ASSERT(!(alignment % 2)); + return (size + alignment - 1) & ~(alignment - 1); + } + + // Manages a chunk of memory and individual allocations out of it. + class Chunk : public Noncopyable { + public: + // Allocates a block of memory of the given size from the passed + // Allocator. + Chunk(Allocator* allocator, size_t size) + : m_allocator(allocator) + , m_size(size) + , m_currentOffset(0) + { + m_base = static_cast<uint8_t*>(m_allocator->allocate(size)); + } + + // Frees the memory allocated from the Allocator in the + // constructor. + ~Chunk() + { + m_allocator->free(m_base); + } + + // Returns a pointer to "size" bytes of storage, or 0 if this + // Chunk could not satisfy the allocation. + void* allocate(size_t size) + { + // Check for overflow + if (m_currentOffset + size < m_currentOffset) + return 0; + + if (m_currentOffset + size > m_size) + return 0; + + void* result = m_base + m_currentOffset; + m_currentOffset += size; + return result; + } + + private: + Allocator* m_allocator; + uint8_t* m_base; + size_t m_size; + size_t m_currentOffset; + }; + + RefPtr<Allocator> m_allocator; + Chunk* m_current; + size_t m_currentChunkSize; + Vector<OwnPtr<Chunk> > m_chunks; +}; + +} // namespace WebCore + +#endif // PODArena_h diff --git a/WebCore/platform/graphics/gpu/PODInterval.h b/WebCore/platform/graphics/gpu/PODInterval.h new file mode 100644 index 0000000..9df69ba --- /dev/null +++ b/WebCore/platform/graphics/gpu/PODInterval.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010 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 AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PODInterval_h +#define PODInterval_h + +#ifndef NDEBUG +#include "StringBuilder.h" +#endif + +namespace WebCore { + +// Class representing a closed interval which can hold an arbitrary +// Plain Old Datatype (POD) as its endpoints and a piece of user +// data. An important characteristic for the algorithms we use is that +// if two intervals have identical endpoints but different user data, +// they are not considered to be equal. This situation can arise when +// representing the vertical extents of bounding boxes of overlapping +// triangles, where the pointer to the triangle is the user data of +// the interval. +// +// *Note* that the destructors of type T and UserData will *not* be +// called by this class. They must not allocate any memory that is +// required to be cleaned up in their destructors. +// +// The following constructors and operators must be implemented on +// type T: +// +// - Copy constructor (if user data is desired) +// - operator< +// - operator== +// - operator= +// +// If the UserData type is specified, it must support a copy +// constructor and assignment operator. +// +// In debug mode, printing of intervals and the data they contain is +// enabled. This requires the following functions to be available: +// +// String valueToString(const T&); +// String valueToString(const UserData&); +// +// Note that this class requires a copy constructor and assignment +// operator in order to be stored in the red-black tree. + +template<class T, class UserData = void*> +class PODInterval { +public: + // Constructor from endpoints. This constructor only works when the + // UserData type is a pointer or other type which can be initialized + // with 0. + PODInterval(const T& low, const T& high) + : m_low(low) + , m_high(high) + , m_data(0) + , m_maxHigh(high) + { + } + + // Constructor from two endpoints plus explicit user data. + PODInterval(const T& low, const T& high, const UserData data) + : m_low(low) + , m_high(high) + , m_data(data) + , m_maxHigh(high) + { + } + + const T& low() const { return m_low; } + const T& high() const { return m_high; } + const UserData& data() const { return m_data; } + + bool overlaps(const T& low, const T& high) const + { + if (this->high() < low) + return false; + if (high < this->low()) + return false; + return true; + } + + bool overlaps(const PODInterval& other) const + { + return overlaps(other.low(), other.high()); + } + + // Returns true if this interval is "less" than the other. The + // comparison is performed on the low endpoints of the intervals. + bool operator<(const PODInterval& other) const + { + return low() < other.low(); + } + + // Returns true if this interval is strictly equal to the other, + // including comparison of the user data. + bool operator==(const PODInterval& other) const + { + return (low() == other.low() + && high() == other.high() + && data() == other.data()); + } + + const T& maxHigh() const { return m_maxHigh; } + void setMaxHigh(const T& maxHigh) { m_maxHigh = maxHigh; } + +#ifndef NDEBUG + // Support for printing PODIntervals. + String toString() const + { + StringBuilder builder; + builder.append("[PODInterval ("); + builder.append(valueToString(low())); + builder.append(", "); + builder.append(valueToString(high())); + builder.append("), data="); + builder.append(valueToString(data())); + builder.append(", maxHigh="); + builder.append(valueToString(maxHigh())); + builder.append("]"); + return builder.toString(); + } +#endif + +private: + T m_low; + T m_high; + UserData m_data; + T m_maxHigh; +}; + +} // namespace WebCore + +#endif // PODInterval_h diff --git a/WebCore/platform/graphics/gpu/PODIntervalTree.h b/WebCore/platform/graphics/gpu/PODIntervalTree.h new file mode 100644 index 0000000..c0a86aa --- /dev/null +++ b/WebCore/platform/graphics/gpu/PODIntervalTree.h @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2010 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 AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PODIntervalTree_h +#define PODIntervalTree_h + +#include "PODArena.h" +#include "PODInterval.h" +#include "PODRedBlackTree.h" +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// An interval tree, which is a form of augmented red-black tree. It +// supports efficient (O(lg n)) insertion, removal and querying of +// intervals in the tree. +template<class T, class UserData = void*> +class PODIntervalTree : public Noncopyable, + public PODRedBlackTree<PODInterval<T, UserData> > { +public: + // Typedef to reduce typing when declaring intervals to be stored in + // this tree. + typedef PODInterval<T, UserData> IntervalType; + + PODIntervalTree() + : PODRedBlackTree<IntervalType>() + { + init(); + } + + explicit PODIntervalTree(PassRefPtr<PODArena> arena) + : PODRedBlackTree<IntervalType>(arena) + { + init(); + } + + // Returns all intervals in the tree which overlap the given query + // interval. The returned intervals are sorted by increasing low + // endpoint. + Vector<IntervalType> allOverlaps(const IntervalType& interval) const + { + Vector<IntervalType> result; + allOverlaps(interval, result); + return result; + } + + // Returns all intervals in the tree which overlap the given query + // interval. The returned intervals are sorted by increasing low + // endpoint. + void allOverlaps(const IntervalType& interval, Vector<IntervalType>& result) const + { + // Explicit dereference of "this" required because of + // inheritance rules in template classes. + searchForOverlapsFrom(this->root(), interval, result); + } + + // Helper to create interval objects. + static IntervalType createInterval(const T& low, const T& high, const UserData data = 0) + { + return IntervalType(low, high, data); + } + + virtual bool checkInvariants() const + { + if (!PODRedBlackTree<IntervalType>::checkInvariants()) + return false; + if (!this->root()) + return true; + return checkInvariantsFromNode(this->root(), 0); + } + +private: + typedef typename PODRedBlackTree<IntervalType>::Node IntervalNode; + + // Initializes the tree. + void init() + { + // Explicit dereference of "this" required because of + // inheritance rules in template classes. + this->setNeedsFullOrderingComparisons(true); + } + + // Starting from the given node, adds all overlaps with the given + // interval to the result vector. The intervals are sorted by + // increasing low endpoint. + void searchForOverlapsFrom(IntervalNode* node, const IntervalType& interval, Vector<IntervalType>& res) const + { + if (!node) + return; + + // Because the intervals are sorted by left endpoint, inorder + // traversal produces results sorted as desired. + + // See whether we need to traverse the left subtree. + IntervalNode* left = node->left(); + if (left + // This is phrased this way to avoid the need for operator + // <= on type T. + && !(left->data().maxHigh() < interval.low())) + searchForOverlapsFrom(left, interval, res); + + // Check for overlap with current node. + if (node->data().overlaps(interval)) + res.append(node->data()); + + // See whether we need to traverse the right subtree. + // This is phrased this way to avoid the need for operator <= + // on type T. + if (!(interval.high() < node->data().low())) + searchForOverlapsFrom(node->right(), interval, res); + } + + virtual bool updateNode(IntervalNode* node) + { + // Would use const T&, but need to reassign this reference in this + // function. + const T* curMax = &node->data().high(); + IntervalNode* left = node->left(); + if (left) { + if (*curMax < left->data().maxHigh()) + curMax = &left->data().maxHigh(); + } + IntervalNode* right = node->right(); + if (right) { + if (*curMax < right->data().maxHigh()) + curMax = &right->data().maxHigh(); + } + // This is phrased like this to avoid needing operator!= on type T. + if (!(*curMax == node->data().maxHigh())) { + node->data().setMaxHigh(*curMax); + return true; + } + return false; + } + + bool checkInvariantsFromNode(IntervalNode* node, T* currentMaxValue) const + { + // These assignments are only done in order to avoid requiring + // a default constructor on type T. + T leftMaxValue(node->data().maxHigh()); + T rightMaxValue(node->data().maxHigh()); + IntervalNode* left = node->left(); + IntervalNode* right = node->right(); + if (left) { + if (!checkInvariantsFromNode(left, &leftMaxValue)) + return false; + } + if (right) { + if (!checkInvariantsFromNode(right, &rightMaxValue)) + return false; + } + if (!left && !right) { + // Base case. + if (currentMaxValue) + *currentMaxValue = node->data().high(); + return (node->data().high() == node->data().maxHigh()); + } + T localMaxValue(node->data().maxHigh()); + if (!left || !right) { + if (left) + localMaxValue = leftMaxValue; + else + localMaxValue = rightMaxValue; + } else + localMaxValue = (leftMaxValue < rightMaxValue) ? rightMaxValue : leftMaxValue; + if (localMaxValue < node->data().high()) + localMaxValue = node->data().high(); + if (!(localMaxValue == node->data().maxHigh())) { +#ifndef NDEBUG + String localMaxValueString = valueToString(localMaxValue); + LOG_ERROR("PODIntervalTree verification failed at node 0x%p: localMaxValue=%s and data=%s", + node, localMaxValueString.utf8().data(), node->data().toString().utf8().data()); +#endif + return false; + } + if (currentMaxValue) + *currentMaxValue = localMaxValue; + return true; + } +}; + +#ifndef NDEBUG +// Support for printing PODIntervals at the PODRedBlackTree level. +template<class T, class UserData> +String valueToString(const PODInterval<T, UserData>& interval) +{ + return interval.toString(); +} +#endif + +} // namespace WebCore + +#endif // PODIntervalTree_h diff --git a/WebCore/platform/graphics/gpu/PODRedBlackTree.h b/WebCore/platform/graphics/gpu/PODRedBlackTree.h new file mode 100644 index 0000000..9b02037 --- /dev/null +++ b/WebCore/platform/graphics/gpu/PODRedBlackTree.h @@ -0,0 +1,750 @@ +/* + * Copyright (C) 2010 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 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. + */ + +// A red-black tree, which is a form of a balanced binary tree. It +// supports efficient insertion, deletion and queries of comparable +// elements. The same element may be inserted multiple times. The +// algorithmic complexity of common operations is: +// +// Insertion: O(lg(n)) +// Deletion: O(lg(n)) +// Querying: O(lg(n)) +// +// The data type T that is stored in this red-black tree must be only +// Plain Old Data (POD), or bottom out into POD. It must _not_ rely on +// having its destructor called. This implementation internally +// allocates storage in large chunks and does not call the destructor +// on each object. +// +// Type T must supply a default constructor, a copy constructor, and +// the "<" and "==" operators. +// +// In debug mode, printing of the data contained in the tree is +// enabled. This requires the following function to be available: +// +// String valueToString(const T&); +// +// Note that when complex types are stored in this red/black tree, it +// is possible that single invocations of the "<" and "==" operators +// will be insufficient to describe the ordering of elements in the +// tree during queries. As a concrete example, consider the case where +// intervals are stored in the tree sorted by low endpoint. The "<" +// operator on the Interval class only compares the low endpoint, but +// the "==" operator takes into account the high endpoint as well. +// This makes the necessary logic for querying and deletion somewhat +// more complex. In order to properly handle such situations, the +// property "needsFullOrderingComparisons" must be set to true on +// the tree. +// +// This red-black tree is designed to be _augmented_; subclasses can +// add additional and summary information to each node to efficiently +// store and index more complex data structures. A concrete example is +// the IntervalTree, which extends each node with a summary statistic +// to efficiently store one-dimensional intervals. +// +// The design of this red-black tree comes from Cormen, Leiserson, +// and Rivest, _Introduction to Algorithms_, MIT Press, 1990. + +#ifndef PODRedBlackTree_h +#define PODRedBlackTree_h + +#include "PODArena.h" +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> +#include <wtf/RefPtr.h> +#ifndef NDEBUG +#include "Logging.h" +#include "PlatformString.h" +#include "StringBuilder.h" +#include <wtf/text/CString.h> +#endif + +namespace WebCore { + +template<class T> +class PODRedBlackTree { +public: + // Visitor interface for walking all of the tree's elements. + class Visitor { + public: + virtual void visit(const T& data) = 0; + protected: + virtual ~Visitor() { } + }; + + // Constructs a new red-black tree, allocating temporary objects + // from a newly constructed PODArena. + PODRedBlackTree() + : m_arena(PODArena::create()) + , m_root(0) + , m_needsFullOrderingComparisons(false) +#ifndef NDEBUG + , m_verboseDebugging(false) +#endif + { + } + + // Constructs a new red-black tree, allocating temporary objects + // from the given PODArena. + explicit PODRedBlackTree(PassRefPtr<PODArena> arena) + : m_arena(arena) + , m_root(0) + , m_needsFullOrderingComparisons(false) +#ifndef NDEBUG + , m_verboseDebugging(false) +#endif + { + } + + virtual ~PODRedBlackTree() { } + + void add(const T& data) + { + Node* node = m_arena->allocateObject<Node, T>(data); + insertNode(node); + } + + // Returns true if the datum was found in the tree. + bool remove(const T& data) + { + Node* node = treeSearch(data); + if (node) { + deleteNode(node); + return true; + } + return false; + } + + bool contains(const T& data) const { return treeSearch(data); } + + void visitInorder(Visitor* visitor) const + { + if (!m_root) + return; + visitInorderImpl(m_root, visitor); + } + + int size() const + { + Counter counter; + visitInorder(&counter); + return counter.count(); + } + + // See the class documentation for an explanation of this property. + void setNeedsFullOrderingComparisons(bool needsFullOrderingComparisons) + { + m_needsFullOrderingComparisons = needsFullOrderingComparisons; + } + + virtual bool checkInvariants() const + { + int blackCount; + return checkInvariantsFromNode(m_root, &blackCount); + } + +#ifndef NDEBUG + // Dumps the tree's contents to the logging info stream for + // debugging purposes. + void dump() const + { + dumpFromNode(m_root, 0); + } + + // Turns on or off verbose debugging of the tree, causing many + // messages to be logged during insertion and other operations in + // debug mode. + void setVerboseDebugging(bool verboseDebugging) + { + m_verboseDebugging = verboseDebugging; + } +#endif + +protected: + enum Color { + Red = 1, + Black + }; + + // The base Node class which is stored in the tree. Nodes are only + // an internal concept; users of the tree deal only with the data + // they store in it. + class Node : public Noncopyable { + public: + // Constructor. Newly-created nodes are colored red. + explicit Node(const T& data) + : m_left(0) + , m_right(0) + , m_parent(0) + , m_color(Red) + , m_data(data) + { + } + + virtual ~Node() { } + + Color color() const { return m_color; } + void setColor(Color color) { m_color = color; } + + // Fetches the user data. + T& data() { return m_data; } + + // Copies all user-level fields from the source node, but not + // internal fields. For example, the base implementation of this + // method copies the "m_data" field, but not the child or parent + // fields. Any augmentation information also does not need to be + // copied, as it will be recomputed. Subclasses must call the + // superclass implementation. + virtual void copyFrom(Node* src) { m_data = src->data(); } + + Node* left() const { return m_left; } + void setLeft(Node* node) { m_left = node; } + + Node* right() const { return m_right; } + void setRight(Node* node) { m_right = node; } + + Node* parent() const { return m_parent; } + void setParent(Node* node) { m_parent = node; } + + private: + Node* m_left; + Node* m_right; + Node* m_parent; + Color m_color; + T m_data; + }; + + // Returns the root of the tree, which is needed by some subclasses. + Node* root() const { return m_root; } + +private: + // This virtual method is the hook that subclasses should use when + // augmenting the red-black tree with additional per-node summary + // information. For example, in the case of an interval tree, this + // is used to compute the maximum endpoint of the subtree below the + // given node based on the values in the left and right children. It + // is guaranteed that this will be called in the correct order to + // properly update such summary information based only on the values + // in the left and right children. This method should return true if + // the node's summary information changed. + virtual bool updateNode(Node* node) { return false; } + + //---------------------------------------------------------------------- + // Generic binary search tree operations + // + + // Searches the tree for the given datum. + Node* treeSearch(const T& data) const + { + if (m_needsFullOrderingComparisons) + return treeSearchFullComparisons(m_root, data); + + return treeSearchNormal(m_root, data); + } + + // Searches the tree using the normal comparison operations, + // suitable for simple data types such as numbers. + Node* treeSearchNormal(Node* current, const T& data) const + { + while (current) { + if (current->data() == data) + return current; + if (data < current->data()) + current = current->left(); + else + current = current->right(); + } + return 0; + } + + // Searches the tree using multiple comparison operations, required + // for data types with more complex behavior such as intervals. + Node* treeSearchFullComparisons(Node* current, const T& data) const + { + if (!current) + return 0; + if (data < current->data()) + return treeSearchFullComparisons(current->left(), data); + if (current->data() < data) + return treeSearchFullComparisons(current->right(), data); + if (data == current->data()) + return current; + + // We may need to traverse both the left and right subtrees. + Node* result = treeSearchFullComparisons(current->left(), data); + if (!result) + result = treeSearchFullComparisons(current->right(), data); + return result; + } + + void treeInsert(Node* z) + { + Node* y = 0; + Node* x = m_root; + while (x) { + y = x; + if (z->data() < x->data()) + x = x->left(); + else + x = x->right(); + } + z->setParent(y); + if (!y) + m_root = z; + else { + if (z->data() < y->data()) + y->setLeft(z); + else + y->setRight(z); + } + } + + // Finds the node following the given one in sequential ordering of + // their data, or null if none exists. + Node* treeSuccessor(Node* x) + { + if (x->right()) + return treeMinimum(x->right()); + Node* y = x->parent(); + while (y && x == y->right()) { + x = y; + y = y->parent(); + } + return y; + } + + // Finds the minimum element in the sub-tree rooted at the given + // node. + Node* treeMinimum(Node* x) + { + while (x->left()) + x = x->left(); + return x; + } + + // Helper for maintaining the augmented red-black tree. + void propagateUpdates(Node* start) + { + bool shouldContinue = true; + while (start && shouldContinue) { + shouldContinue = updateNode(start); + start = start->parent(); + } + } + + //---------------------------------------------------------------------- + // Red-Black tree operations + // + + // Left-rotates the subtree rooted at x. + // Returns the new root of the subtree (x's right child). + Node* leftRotate(Node* x) + { + // Set y. + Node* y = x->right(); + + // Turn y's left subtree into x's right subtree. + x->setRight(y->left()); + if (y->left()) + y->left()->setParent(x); + + // Link x's parent to y. + y->setParent(x->parent()); + if (!x->parent()) + m_root = y; + else { + if (x == x->parent()->left()) + x->parent()->setLeft(y); + else + x->parent()->setRight(y); + } + + // Put x on y's left. + y->setLeft(x); + x->setParent(y); + + // Update nodes lowest to highest. + updateNode(x); + updateNode(y); + return y; + } + + // Right-rotates the subtree rooted at y. + // Returns the new root of the subtree (y's left child). + Node* rightRotate(Node* y) + { + // Set x. + Node* x = y->left(); + + // Turn x's right subtree into y's left subtree. + y->setLeft(x->right()); + if (x->right()) + x->right()->setParent(y); + + // Link y's parent to x. + x->setParent(y->parent()); + if (!y->parent()) + m_root = x; + else { + if (y == y->parent()->left()) + y->parent()->setLeft(x); + else + y->parent()->setRight(x); + } + + // Put y on x's right. + x->setRight(y); + y->setParent(x); + + // Update nodes lowest to highest. + updateNode(y); + updateNode(x); + return x; + } + + // Inserts the given node into the tree. + void insertNode(Node* x) + { + treeInsert(x); + x->setColor(Red); + updateNode(x); + + logIfVerbose(" PODRedBlackTree::InsertNode"); + + // The node from which to start propagating updates upwards. + Node* updateStart = x->parent(); + + while (x != m_root && x->parent()->color() == Red) { + if (x->parent() == x->parent()->parent()->left()) { + Node* y = x->parent()->parent()->right(); + if (y && y->color() == Red) { + // Case 1 + logIfVerbose(" Case 1/1"); + x->parent()->setColor(Black); + y->setColor(Black); + x->parent()->parent()->setColor(Red); + updateNode(x->parent()); + x = x->parent()->parent(); + updateNode(x); + updateStart = x->parent(); + } else { + if (x == x->parent()->right()) { + logIfVerbose(" Case 1/2"); + // Case 2 + x = x->parent(); + leftRotate(x); + } + // Case 3 + logIfVerbose(" Case 1/3"); + x->parent()->setColor(Black); + x->parent()->parent()->setColor(Red); + Node* newSubTreeRoot = rightRotate(x->parent()->parent()); + updateStart = newSubTreeRoot->parent(); + } + } else { + // Same as "then" clause with "right" and "left" exchanged. + Node* y = x->parent()->parent()->left(); + if (y && y->color() == Red) { + // Case 1 + logIfVerbose(" Case 2/1"); + x->parent()->setColor(Black); + y->setColor(Black); + x->parent()->parent()->setColor(Red); + updateNode(x->parent()); + x = x->parent()->parent(); + updateNode(x); + updateStart = x->parent(); + } else { + if (x == x->parent()->left()) { + // Case 2 + logIfVerbose(" Case 2/2"); + x = x->parent(); + rightRotate(x); + } + // Case 3 + logIfVerbose(" Case 2/3"); + x->parent()->setColor(Black); + x->parent()->parent()->setColor(Red); + Node* newSubTreeRoot = leftRotate(x->parent()->parent()); + updateStart = newSubTreeRoot->parent(); + } + } + } + + propagateUpdates(updateStart); + + m_root->setColor(Black); + } + + // Restores the red-black property to the tree after splicing out + // a node. Note that x may be null, which is why xParent must be + // supplied. + void deleteFixup(Node* x, Node* xParent) + { + while (x != m_root && (!x || x->color() == Black)) { + if (x == xParent->left()) { + // Note: the text points out that w can not be null. + // The reason is not obvious from simply looking at + // the code; it comes about from the properties of the + // red-black tree. + Node* w = xParent->right(); + ASSERT(w); // x's sibling should not be null. + if (w->color() == Red) { + // Case 1 + w->setColor(Black); + xParent->setColor(Red); + leftRotate(xParent); + w = xParent->right(); + } + if ((!w->left() || w->left()->color() == Black) + && (!w->right() || w->right()->color() == Black)) { + // Case 2 + w->setColor(Red); + x = xParent; + xParent = x->parent(); + } else { + if (!w->right() || w->right()->color() == Black) { + // Case 3 + w->left()->setColor(Black); + w->setColor(Red); + rightRotate(w); + w = xParent->right(); + } + // Case 4 + w->setColor(xParent->color()); + xParent->setColor(Black); + if (w->right()) + w->right()->setColor(Black); + leftRotate(xParent); + x = m_root; + xParent = x->parent(); + } + } else { + // Same as "then" clause with "right" and "left" + // exchanged. + + // Note: the text points out that w can not be null. + // The reason is not obvious from simply looking at + // the code; it comes about from the properties of the + // red-black tree. + Node* w = xParent->left(); + ASSERT(w); // x's sibling should not be null. + if (w->color() == Red) { + // Case 1 + w->setColor(Black); + xParent->setColor(Red); + rightRotate(xParent); + w = xParent->left(); + } + if ((!w->right() || w->right()->color() == Black) + && (!w->left() || w->left()->color() == Black)) { + // Case 2 + w->setColor(Red); + x = xParent; + xParent = x->parent(); + } else { + if (!w->left() || w->left()->color() == Black) { + // Case 3 + w->right()->setColor(Black); + w->setColor(Red); + leftRotate(w); + w = xParent->left(); + } + // Case 4 + w->setColor(xParent->color()); + xParent->setColor(Black); + if (w->left()) + w->left()->setColor(Black); + rightRotate(xParent); + x = m_root; + xParent = x->parent(); + } + } + } + if (x) + x->setColor(Black); + } + + // Deletes the given node from the tree. Note that this + // particular node may not actually be removed from the tree; + // instead, another node might be removed and its contents + // copied into z. + void deleteNode(Node* z) + { + // Y is the node to be unlinked from the tree. + Node* y; + if (!z->left() || !z->right()) + y = z; + else + y = treeSuccessor(z); + + // Y is guaranteed to be non-null at this point. + Node* x; + if (y->left()) + x = y->left(); + else + x = y->right(); + + // X is the child of y which might potentially replace y in + // the tree. X might be null at this point. + Node* xParent; + if (x) { + x->setParent(y->parent()); + xParent = x->parent(); + } else + xParent = y->parent(); + if (!y->parent()) + m_root = x; + else { + if (y == y->parent()->left()) + y->parent()->setLeft(x); + else + y->parent()->setRight(x); + } + if (y != z) { + z->copyFrom(y); + // This node has changed location in the tree and must be updated. + updateNode(z); + // The parent and its parents may now be out of date. + propagateUpdates(z->parent()); + } + + // If we haven't already updated starting from xParent, do so now. + if (xParent && xParent != y && xParent != z) + propagateUpdates(xParent); + if (y->color() == Black) + deleteFixup(x, xParent); + } + + // Visits the subtree rooted at the given node in order. + void visitInorderImpl(Node* node, Visitor* visitor) const + { + if (node->left()) + visitInorderImpl(node->left(), visitor); + visitor->visit(node->data()); + if (node->right()) + visitInorderImpl(node->right(), visitor); + } + + //---------------------------------------------------------------------- + // Helper class for size() + + // A Visitor which simply counts the number of visited elements. + class Counter : public Visitor, public Noncopyable { + public: + Counter() + : m_count(0) { } + + virtual void visit(const T& data) { ++m_count; } + int count() const { return m_count; } + + private: + int m_count; + }; + + //---------------------------------------------------------------------- + // Verification and debugging routines + // + + // Returns in the "blackCount" parameter the number of black + // children along all paths to all leaves of the given node. + bool checkInvariantsFromNode(Node* node, int* blackCount) const + { + // Base case is a leaf node. + if (!node) { + *blackCount = 1; + return true; + } + + // Each node is either red or black. + if (!(node->color() == Red || node->color() == Black)) + return false; + + // Every leaf (or null) is black. + + if (node->color() == Red) { + // Both of its children are black. + if (!((!node->left() || node->left()->color() == Black))) + return false; + if (!((!node->right() || node->right()->color() == Black))) + return false; + } + + // Every simple path to a leaf node contains the same number of + // black nodes. + int leftCount = 0, rightCount = 0; + bool leftValid = checkInvariantsFromNode(node->left(), &leftCount); + bool rightValid = checkInvariantsFromNode(node->right(), &rightCount); + if (!leftValid || !rightValid) + return false; + *blackCount = leftCount + (node->color() == Black ? 1 : 0); + return leftCount == rightCount; + } + +#ifdef NDEBUG + void logIfVerbose(const char* output) const { } +#else + void logIfVerbose(const char* output) const + { + if (m_verboseDebugging) + LOG_ERROR("%s", output); + } +#endif + +#ifndef NDEBUG + // Dumps the subtree rooted at the given node. + void dumpFromNode(Node* node, int indentation) const + { + StringBuilder builder; + for (int i = 0; i < indentation; i++) + builder.append(" "); + builder.append("-"); + if (node) { + builder.append(" "); + builder.append(valueToString(node->data())); + builder.append((node->color() == Black) ? " (black)" : " (red)"); + } + LOG_ERROR("%s", builder.toString().ascii().data()); + if (node) { + dumpFromNode(node->left(), indentation + 2); + dumpFromNode(node->right(), indentation + 2); + } + } +#endif + + //---------------------------------------------------------------------- + // Data members + + RefPtr<PODArena> m_arena; + Node* m_root; + bool m_needsFullOrderingComparisons; +#ifndef NDEBUG + bool m_verboseDebugging; +#endif +}; + +} // namespace WebCore + +#endif // PODRedBlackTree_h diff --git a/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp b/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp new file mode 100644 index 0000000..6424293 --- /dev/null +++ b/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "SharedGraphicsContext3D.h" + +#include "AffineTransform.h" +#include "Color.h" +#include "FloatRect.h" +#include "GraphicsContext3D.h" +#include "GraphicsTypes.h" +#include "IntSize.h" +#include "SolidFillShader.h" +#include "TexShader.h" + +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +// static +PassRefPtr<SharedGraphicsContext3D> SharedGraphicsContext3D::create(PassOwnPtr<GraphicsContext3D> context) +{ + return adoptRef(new SharedGraphicsContext3D(context)); +} + +SharedGraphicsContext3D::SharedGraphicsContext3D(PassOwnPtr<GraphicsContext3D> context) + : m_context(context) + , m_quadVertices(0) + , m_solidFillShader(SolidFillShader::create(m_context.get())) + , m_texShader(TexShader::create(m_context.get())) +{ +} + +SharedGraphicsContext3D::~SharedGraphicsContext3D() +{ + m_context->deleteBuffer(m_quadVertices); +} + +void SharedGraphicsContext3D::makeContextCurrent() +{ + m_context->makeContextCurrent(); +} + +void SharedGraphicsContext3D::scissor(const FloatRect& rect) +{ + m_context->scissor(rect.x(), rect.y(), rect.width(), rect.height()); +} + +void SharedGraphicsContext3D::enable(unsigned capacity) +{ + m_context->enable(capacity); +} + +void SharedGraphicsContext3D::disable(unsigned capacity) +{ + m_context->disable(capacity); +} + +void SharedGraphicsContext3D::clearColor(const Color& color) +{ + float rgba[4]; + color.getRGBA(rgba[0], rgba[1], rgba[2], rgba[3]); + m_context->clearColor(rgba[0], rgba[1], rgba[2], rgba[3]); +} + +void SharedGraphicsContext3D::clear(unsigned mask) +{ + m_context->clear(mask); +} + +void SharedGraphicsContext3D::drawArrays(unsigned long mode, long first, long count) +{ + m_context->drawArrays(mode, first, count); +} + +unsigned long SharedGraphicsContext3D::getError() +{ + return m_context->getError(); +} + +void SharedGraphicsContext3D::getIntegerv(unsigned long pname, int* value) +{ + m_context->getIntegerv(pname, value); +} + +unsigned SharedGraphicsContext3D::createFramebuffer() +{ + return m_context->createFramebuffer(); +} + +unsigned SharedGraphicsContext3D::createTexture() +{ + return m_context->createTexture(); +} + +void SharedGraphicsContext3D::deleteFramebuffer(unsigned framebuffer) +{ + m_context->deleteFramebuffer(framebuffer); +} + +void SharedGraphicsContext3D::deleteTexture(unsigned texture) +{ + m_context->deleteTexture(texture); +} + +void SharedGraphicsContext3D::framebufferTexture2D(unsigned long target, unsigned long attachment, unsigned long textarget, unsigned texture, long level) +{ + m_context->framebufferTexture2D(target, attachment, textarget, texture, level); +} + +void SharedGraphicsContext3D::texParameteri(unsigned target, unsigned pname, int param) +{ + m_context->texParameteri(target, pname, param); +} + +int SharedGraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, void* pixels) +{ + return m_context->texImage2D(target, level, internalformat, width, height, border, format, type, pixels); +} + +int SharedGraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, unsigned width, unsigned height, unsigned format, unsigned type, void* pixels) +{ + return m_context->texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); +} + +void SharedGraphicsContext3D::readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type, void* data) +{ + m_context->readPixels(x, y, width, height, format, type, data); +} + +bool SharedGraphicsContext3D::supportsBGRA() +{ + return m_context->supportsBGRA(); +} + +Texture* SharedGraphicsContext3D::createTexture(NativeImagePtr ptr, Texture::Format format, int width, int height) +{ + RefPtr<Texture> texture = m_textures.get(ptr); + if (texture) + return texture.get(); + + texture = Texture::create(m_context.get(), format, width, height); + Texture* t = texture.get(); + m_textures.set(ptr, texture); + return t; +} + +Texture* SharedGraphicsContext3D::getTexture(NativeImagePtr ptr) +{ + RefPtr<Texture> texture = m_textures.get(ptr); + return texture ? texture.get() : 0; +} + +PassRefPtr<Texture> SharedGraphicsContext3D::createTexture(Texture::Format format, int width, int height) +{ + return Texture::create(m_context.get(), format, width, height); +} + +void SharedGraphicsContext3D::applyCompositeOperator(CompositeOperator op) +{ + switch (op) { + case CompositeClear: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::ZERO); + break; + case CompositeCopy: + m_context->disable(GraphicsContext3D::BLEND); + break; + case CompositeSourceOver: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); + break; + case CompositeSourceIn: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::DST_ALPHA, GraphicsContext3D::ZERO); + break; + case CompositeSourceOut: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ONE_MINUS_DST_ALPHA, GraphicsContext3D::ZERO); + break; + case CompositeSourceAtop: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::DST_ALPHA, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); + break; + case CompositeDestinationOver: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ONE_MINUS_DST_ALPHA, GraphicsContext3D::ONE); + break; + case CompositeDestinationIn: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::SRC_ALPHA); + break; + case CompositeDestinationOut: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); + break; + case CompositeDestinationAtop: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ONE_MINUS_DST_ALPHA, GraphicsContext3D::SRC_ALPHA); + break; + case CompositeXOR: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ONE_MINUS_DST_ALPHA, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); + break; + case CompositePlusDarker: + case CompositeHighlight: + // unsupported + m_context->disable(GraphicsContext3D::BLEND); + break; + case CompositePlusLighter: + m_context->enable(GraphicsContext3D::BLEND); + m_context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE); + break; + } +} + +void SharedGraphicsContext3D::useQuadVertices() +{ + if (!m_quadVertices) { + float vertices[] = { 0.0f, 0.0f, 1.0f, + 1.0f, 0.0f, 1.0f, + 0.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f }; + m_quadVertices = m_context->createBuffer(); + m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_quadVertices); + m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, sizeof(vertices), vertices, GraphicsContext3D::STATIC_DRAW); + } else { + m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_quadVertices); + } +} + +void SharedGraphicsContext3D::setActiveTexture(unsigned textureUnit) +{ + m_context->activeTexture(textureUnit); +} + +void SharedGraphicsContext3D::bindTexture(unsigned target, unsigned texture) +{ + m_context->bindTexture(target, texture); +} + +void SharedGraphicsContext3D::useFillSolidProgram(const AffineTransform& transform, const Color& color) +{ + m_solidFillShader->use(transform, color); +} + +void SharedGraphicsContext3D::useTextureProgram(const AffineTransform& transform, const AffineTransform& texTransform, float alpha) +{ + m_texShader->use(transform, texTransform, 0, alpha); +} + +void SharedGraphicsContext3D::bindFramebuffer(unsigned framebuffer) +{ + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, framebuffer); +} + +void SharedGraphicsContext3D::setViewport(const IntSize& size) +{ + m_context->viewport(0, 0, size.width(), size.height()); +} + +bool SharedGraphicsContext3D::paintsIntoCanvasBuffer() const +{ + return m_context->paintsIntoCanvasBuffer(); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.h b/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.h new file mode 100644 index 0000000..1baa0f6 --- /dev/null +++ b/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SharedGraphicsContext3D_h +#define SharedGraphicsContext3D_h + +#include "GraphicsTypes.h" +#include "ImageSource.h" +#include "Texture.h" + +#include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class AffineTransform; +class Color; +class GraphicsContext3D; +class FloatRect; +class IntSize; +class SolidFillShader; +class TexShader; + +typedef HashMap<NativeImagePtr, RefPtr<Texture> > TextureHashMap; + +class SharedGraphicsContext3D : public RefCounted<SharedGraphicsContext3D> { +public: + static PassRefPtr<SharedGraphicsContext3D> create(PassOwnPtr<GraphicsContext3D>); + ~SharedGraphicsContext3D(); + + // Functions that delegate directly to GraphicsContext3D, with caching + void makeContextCurrent(); + void bindFramebuffer(unsigned framebuffer); + void setViewport(const IntSize&); + void scissor(const FloatRect&); + void enable(unsigned capacity); + void disable(unsigned capacity); + void clearColor(const Color&); + void clear(unsigned mask); + void drawArrays(unsigned long mode, long first, long count); + unsigned long getError(); + void getIntegerv(unsigned long pname, int* value); + + unsigned createFramebuffer(); + unsigned createTexture(); + + void deleteFramebuffer(unsigned framebuffer); + void deleteTexture(unsigned texture); + + void framebufferTexture2D(unsigned long target, unsigned long attachment, unsigned long textarget, unsigned, long level); + void texParameteri(unsigned target, unsigned pname, int param); + int texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, void* pixels); + int texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, unsigned width, unsigned height, unsigned format, unsigned type, void* pixels); + + void readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type, void* data); + + bool paintsIntoCanvasBuffer() const; + + // Shared logic for canvas 2d + void applyCompositeOperator(CompositeOperator); + void useQuadVertices(); + + void useFillSolidProgram(const AffineTransform&, const Color&); + void useTextureProgram(const AffineTransform&, const AffineTransform&, float alpha); + + void setActiveTexture(unsigned textureUnit); + void bindTexture(unsigned target, unsigned texture); + + bool supportsBGRA(); + + // Creates a texture associated with the given image. Is owned by this context's + // TextureHashMap. + Texture* createTexture(NativeImagePtr, Texture::Format, int width, int height); + Texture* getTexture(NativeImagePtr); + + // Creates a texture that is not associated with any image. The caller takes ownership of + // the texture. + PassRefPtr<Texture> createTexture(Texture::Format, int width, int height); + +private: + SharedGraphicsContext3D(PassOwnPtr<GraphicsContext3D> context); + + OwnPtr<GraphicsContext3D> m_context; + + unsigned m_quadVertices; + + OwnPtr<SolidFillShader> m_solidFillShader; + OwnPtr<TexShader> m_texShader; + + TextureHashMap m_textures; +}; + +} // namespace WebCore + +#endif // SharedGraphicsContext3D_h diff --git a/WebCore/platform/graphics/gpu/Texture.cpp b/WebCore/platform/graphics/gpu/Texture.cpp index 557603c..95436ba 100644 --- a/WebCore/platform/graphics/gpu/Texture.cpp +++ b/WebCore/platform/graphics/gpu/Texture.cpp @@ -32,10 +32,15 @@ #include "Texture.h" +#include "FloatRect.h" #include "GraphicsContext3D.h" #include "IntRect.h" + +#include <algorithm> #include <wtf/OwnArrayPtr.h> +using namespace std; + namespace WebCore { @@ -81,7 +86,6 @@ PassRefPtr<Texture> Texture::create(GraphicsContext3D* context, Format format, i { int maxTextureSize = 0; context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize); - TilingData tiling(maxTextureSize, width, height, true); int numTiles = tiling.numTiles(); @@ -120,15 +124,18 @@ static uint32_t* copySubRect(uint32_t* src, int srcX, int srcY, uint32_t* dst, i if (!swizzle && width == srcStride) return srcOffset; - uint32_t* dstPixel = dst; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width ; x++) { - uint32_t pixel = srcOffset[x + y * srcStride]; - if (swizzle) + if (swizzle) { + uint32_t* dstPixel = dst; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width ; ++x) { + uint32_t pixel = srcOffset[x + y * srcStride]; *dstPixel = pixel & 0xFF00FF00 | ((pixel & 0x00FF0000) >> 16) | ((pixel & 0x000000FF) << 16); - else - *dstPixel = pixel; - dstPixel++; + dstPixel++; + } + } + } else { + for (int y = 0; y < height; ++y) { + memcpy(dst + y * width, srcOffset + y * srcStride, 4 * width); } } return dst; @@ -136,6 +143,11 @@ static uint32_t* copySubRect(uint32_t* src, int srcX, int srcY, uint32_t* dst, i void Texture::load(void* pixels) { + updateSubRect(pixels, IntRect(0, 0, m_tiles.totalSizeX(), m_tiles.totalSizeY())); +} + +void Texture::updateSubRect(void* pixels, const IntRect updateRect) +{ uint32_t* pixels32 = static_cast<uint32_t*>(pixels); unsigned int glFormat = 0; unsigned int glType = 0; @@ -145,26 +157,42 @@ void Texture::load(void* pixels) ASSERT(glFormat == GraphicsContext3D::RGBA && glType == GraphicsContext3D::UNSIGNED_BYTE); // FIXME: This could use PBO's to save doing an extra copy here. } - OwnArrayPtr<uint32_t> tempBuff(new uint32_t[m_tiles.maxTextureSize() * m_tiles.maxTextureSize()]); + int tempBuffSize = // Temporary buffer size is the smaller of the max texture size or the updateRect + min(m_tiles.maxTextureSize(), m_tiles.borderTexels() + updateRect.width()) * + min(m_tiles.maxTextureSize(), m_tiles.borderTexels() + updateRect.height()); + OwnArrayPtr<uint32_t> tempBuff(new uint32_t[tempBuffSize]); + + for (int tile = 0; tile < m_tiles.numTiles(); tile++) { + // Intersect with tile + IntRect tileBoundsWithBorder = m_tiles.tileBoundsWithBorder(tile); - for (int i = 0; i < m_tiles.numTiles(); i++) { - IntRect tileBoundsWithBorder = m_tiles.tileBoundsWithBorder(i); + IntRect updateRectIntersected = updateRect; + updateRectIntersected.intersect(tileBoundsWithBorder); + IntRect dstRect = updateRectIntersected; + dstRect.move(-tileBoundsWithBorder.x(), -tileBoundsWithBorder.y()); + + if (updateRectIntersected.isEmpty()) + continue; + + // Copy sub rectangle out of larger pixel data uint32_t* uploadBuff = 0; if (swizzle) { uploadBuff = copySubRect<true>( - pixels32, tileBoundsWithBorder.x(), tileBoundsWithBorder.y(), - tempBuff.get(), tileBoundsWithBorder.width(), tileBoundsWithBorder.height(), m_tiles.totalSizeX()); + pixels32, updateRectIntersected.x(), updateRectIntersected.y(), + tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSizeX()); } else { uploadBuff = copySubRect<false>( - pixels32, tileBoundsWithBorder.x(), tileBoundsWithBorder.y(), - tempBuff.get(), tileBoundsWithBorder.width(), tileBoundsWithBorder.height(), m_tiles.totalSizeX()); + pixels32, updateRectIntersected.x(), updateRectIntersected.y(), + tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSizeX()); } - m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_tileTextureIds->at(i)); - m_context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, 0, 0, - tileBoundsWithBorder.width(), - tileBoundsWithBorder.height(), glFormat, glType, uploadBuff); + m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_tileTextureIds->at(tile)); + m_context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0 /* level */, + dstRect.x(), + dstRect.y(), + updateRectIntersected.width(), + updateRectIntersected.height(), glFormat, glType, uploadBuff); } } diff --git a/WebCore/platform/graphics/gpu/Texture.h b/WebCore/platform/graphics/gpu/Texture.h index 7e4a72b..eda475e 100644 --- a/WebCore/platform/graphics/gpu/Texture.h +++ b/WebCore/platform/graphics/gpu/Texture.h @@ -41,6 +41,8 @@ namespace WebCore { class GraphicsContext3D; +class IntRect; + class Texture : public RefCounted<Texture> { public: ~Texture(); @@ -48,6 +50,7 @@ public: static PassRefPtr<Texture> create(GraphicsContext3D*, Format, int width, int height); void bindTile(int tile); void load(void* pixels); + void updateSubRect(void* pixels, const IntRect); Format format() const { return m_format; } const TilingData& tiles() const { return m_tiles; } private: diff --git a/WebCore/platform/graphics/gpu/TilingData.h b/WebCore/platform/graphics/gpu/TilingData.h index f12e66e..1bdc51a 100644 --- a/WebCore/platform/graphics/gpu/TilingData.h +++ b/WebCore/platform/graphics/gpu/TilingData.h @@ -44,6 +44,7 @@ public: int maxTextureSize() const { return m_maxTextureSize; } int totalSizeX() const { return m_totalSizeX; } int totalSizeY() const { return m_totalSizeY; } + int borderTexels() const { return m_borderTexels; } int numTiles() const { return numTilesX() * numTilesY(); } int numTilesX() const { return m_numTilesX; } diff --git a/WebCore/platform/graphics/gstreamer/DataSourceGStreamer.cpp b/WebCore/platform/graphics/gstreamer/DataSourceGStreamer.cpp index 567da74..63555bf 100644 --- a/WebCore/platform/graphics/gstreamer/DataSourceGStreamer.cpp +++ b/WebCore/platform/graphics/gstreamer/DataSourceGStreamer.cpp @@ -18,6 +18,7 @@ #include "config.h" #include "DataSourceGStreamer.h" +#if ENABLE(VIDEO) #include <gio/gio.h> #include <glib.h> @@ -241,3 +242,5 @@ static void webkit_data_src_uri_handler_init(gpointer g_iface, gpointer iface_da iface->get_uri = webkit_data_src_uri_get_uri; iface->set_uri = webkit_data_src_uri_set_uri; } + +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/DataSourceGStreamer.h b/WebCore/platform/graphics/gstreamer/DataSourceGStreamer.h index 38f69b1..453685a 100644 --- a/WebCore/platform/graphics/gstreamer/DataSourceGStreamer.h +++ b/WebCore/platform/graphics/gstreamer/DataSourceGStreamer.h @@ -19,6 +19,8 @@ #ifndef DataSourceGStreamer_h #define DataSourceGStreamer_h +#if ENABLE(VIDEO) + #include <glib-object.h> #include <gst/base/gstbasesrc.h> @@ -51,4 +53,5 @@ GType webkit_data_src_get_type(void); G_END_DECLS +#endif // ENABLE(VIDEO) #endif diff --git a/WebCore/platform/graphics/gstreamer/GOwnPtrGStreamer.cpp b/WebCore/platform/graphics/gstreamer/GOwnPtrGStreamer.cpp index 75bf5e7..6333437 100644 --- a/WebCore/platform/graphics/gstreamer/GOwnPtrGStreamer.cpp +++ b/WebCore/platform/graphics/gstreamer/GOwnPtrGStreamer.cpp @@ -32,4 +32,4 @@ template <> void freeOwnedGPtr<GstElement>(GstElement* ptr) } } -#endif +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/GOwnPtrGStreamer.h b/WebCore/platform/graphics/gstreamer/GOwnPtrGStreamer.h index 6655f38..84a3e30 100644 --- a/WebCore/platform/graphics/gstreamer/GOwnPtrGStreamer.h +++ b/WebCore/platform/graphics/gstreamer/GOwnPtrGStreamer.h @@ -19,6 +19,7 @@ #ifndef GOwnPtrGStreamer_h #define GOwnPtrGStreamer_h +#if ENABLE(VIDEO) #include "GOwnPtr.h" @@ -30,4 +31,5 @@ template<> void freeOwnedGPtr<GstElement>(GstElement* ptr); } +#endif // ENABLE(VIDEO) #endif diff --git a/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp index e1efdd4..2ff4f38 100644 --- a/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp +++ b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp @@ -18,11 +18,10 @@ */ #include "config.h" - #include "GStreamerGWorld.h" +#if ENABLE(VIDEO) #include "GOwnPtrGStreamer.h" - #include <gst/gst.h> #include <gst/interfaces/xoverlay.h> @@ -200,3 +199,4 @@ void GStreamerGWorld::setWindowOverlay(GstMessage* message) } } +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/GStreamerGWorld.h b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.h index 659052a..282f13c 100644 --- a/WebCore/platform/graphics/gstreamer/GStreamerGWorld.h +++ b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.h @@ -20,7 +20,6 @@ #ifndef GStreamerGWorld_h #define GStreamerGWorld_h - #if ENABLE(VIDEO) #include "PlatformVideoWindow.h" @@ -63,5 +62,5 @@ private: }; } -#endif +#endif // ENABLE(VIDEO) #endif diff --git a/WebCore/platform/graphics/gstreamer/ImageGStreamer.h b/WebCore/platform/graphics/gstreamer/ImageGStreamer.h index 9288df9..4a4ff2b 100644 --- a/WebCore/platform/graphics/gstreamer/ImageGStreamer.h +++ b/WebCore/platform/graphics/gstreamer/ImageGStreamer.h @@ -23,7 +23,6 @@ #if ENABLE(VIDEO) #include "BitmapImage.h" - #include <gst/gst.h> #include <gst/video/video.h> #include <wtf/PassRefPtr.h> @@ -60,6 +59,5 @@ class ImageGStreamer : public RefCounted<ImageGStreamer> { }; } -#endif - +#endif // ENABLE(VIDEO) #endif diff --git a/WebCore/platform/graphics/gstreamer/ImageGStreamerCG.mm b/WebCore/platform/graphics/gstreamer/ImageGStreamerCG.mm index 421b90f..076df4a 100644 --- a/WebCore/platform/graphics/gstreamer/ImageGStreamerCG.mm +++ b/WebCore/platform/graphics/gstreamer/ImageGStreamerCG.mm @@ -19,6 +19,7 @@ #include "config.h" #include "ImageGStreamer.h" +#if ENABLE(VIDEO) using namespace WebCore; @@ -55,3 +56,5 @@ ImageGStreamer::~ImageGStreamer() m_image = 0; } + +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp b/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp index aeaee19..2fed892 100644 --- a/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp +++ b/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp @@ -18,12 +18,13 @@ */ #include "config.h" +#include "ImageGStreamer.h" + +#if ENABLE(VIDEO) #include "GOwnPtr.h" -#include "ImageGStreamer.h" using namespace std; - using namespace WebCore; PassRefPtr<ImageGStreamer> ImageGStreamer::createImage(GstBuffer* buffer) @@ -64,3 +65,4 @@ ImageGStreamer::~ImageGStreamer() m_image = 0; } +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 5628eb3..4791b4c 100644 --- a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -22,11 +22,8 @@ */ #include "config.h" - -#if ENABLE(VIDEO) - #include "MediaPlayerPrivateGStreamer.h" - +#if ENABLE(VIDEO) #include "ColorSpace.h" #include "DataSourceGStreamer.h" @@ -49,14 +46,13 @@ #include "VideoSinkGStreamer.h" #include "WebKitWebSourceGStreamer.h" #include "Widget.h" -#include <wtf/text/CString.h> - #include <GOwnPtr.h> #include <gst/gst.h> #include <gst/interfaces/mixer.h> #include <gst/video/video.h> #include <limits> #include <math.h> +#include <wtf/text/CString.h> // GstPlayFlags flags from playbin2. It is the policy of GStreamer to // not publicly expose element-specific enums. That's why this @@ -843,8 +839,12 @@ void MediaPlayerPrivateGStreamer::updateStates() // Try to figure out ready and network states. if (state == GST_STATE_READY) { - m_readyState = MediaPlayer::HaveNothing; + m_readyState = MediaPlayer::HaveMetadata; m_networkState = MediaPlayer::Empty; + // Cache the duration without emiting the durationchange + // event because it's taken care of by the media element + // in this precise case. + cacheDuration(); } else if (maxTimeLoaded() == duration()) { m_networkState = MediaPlayer::Loaded; m_readyState = MediaPlayer::HaveEnoughData; @@ -880,13 +880,6 @@ void MediaPlayerPrivateGStreamer::updateStates() m_readyState = MediaPlayer::HaveEnoughData; m_paused = false; - if (!m_mediaDuration) { - float newDuration = duration(); - m_mediaDurationKnown = !isinf(newDuration); - if (m_mediaDurationKnown) - m_mediaDuration = newDuration; - } - if (m_buffering) { m_readyState = MediaPlayer::HaveCurrentData; m_networkState = MediaPlayer::Loading; @@ -1119,7 +1112,7 @@ void MediaPlayerPrivateGStreamer::didEnd() timeChanged(); } -void MediaPlayerPrivateGStreamer::durationChanged() +void MediaPlayerPrivateGStreamer::cacheDuration() { // Reset cached media duration m_mediaDuration = 0; @@ -1143,8 +1136,16 @@ void MediaPlayerPrivateGStreamer::durationChanged() if (!isinf(newDuration)) m_mediaDuration = newDuration; +} - m_player->durationChanged(); +void MediaPlayerPrivateGStreamer::durationChanged() +{ + float previousDuration = m_mediaDuration; + + cacheDuration(); + + if (m_mediaDuration != previousDuration) + m_player->durationChanged(); } bool MediaPlayerPrivateGStreamer::supportsMuting() const @@ -1266,6 +1267,7 @@ static HashSet<String> mimeTypeCache() if (g_str_equal(name, "audio/x-vorbis")) { cache.add(String("audio/ogg")); + cache.add(String("audio/x-vorbis+ogg")); cached = true; } @@ -1460,4 +1462,4 @@ void MediaPlayerPrivateGStreamer::createGSTPlayBin() } -#endif +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h index f2f684b..6d1392d 100644 --- a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h +++ b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h @@ -22,7 +22,6 @@ #ifndef MediaPlayerPrivateGStreamer_h #define MediaPlayerPrivateGStreamer_h - #if ENABLE(VIDEO) #include <wtf/Forward.h> @@ -132,6 +131,7 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface { static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); static bool isAvailable(); + void cacheDuration(); void updateStates(); void cancelSeek(); void endPointTimerFired(Timer<MediaPlayerPrivateGStreamer>*); @@ -179,5 +179,5 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface { }; } -#endif +#endif // ENABLE(VIDEO) #endif diff --git a/WebCore/platform/graphics/gstreamer/PlatformVideoWindow.h b/WebCore/platform/graphics/gstreamer/PlatformVideoWindow.h index 8d99f05..3c4904b 100644 --- a/WebCore/platform/graphics/gstreamer/PlatformVideoWindow.h +++ b/WebCore/platform/graphics/gstreamer/PlatformVideoWindow.h @@ -19,7 +19,6 @@ #ifndef PlatformVideoWindow_h #define PlatformVideoWindow_h - #if ENABLE(VIDEO) #include "Widget.h" @@ -45,6 +44,5 @@ class PlatformVideoWindow : public RefCounted<PlatformVideoWindow> { }; } -#endif - +#endif // ENABLE(VIDEO) #endif diff --git a/WebCore/platform/graphics/gstreamer/PlatformVideoWindowEfl.cpp b/WebCore/platform/graphics/gstreamer/PlatformVideoWindowEfl.cpp index 5c0e6ea..68ab7ac 100644 --- a/WebCore/platform/graphics/gstreamer/PlatformVideoWindowEfl.cpp +++ b/WebCore/platform/graphics/gstreamer/PlatformVideoWindowEfl.cpp @@ -19,6 +19,7 @@ #include "config.h" #include "PlatformVideoWindow.h" +#if ENABLE(VIDEO) #include "NotImplemented.h" @@ -33,3 +34,5 @@ PlatformVideoWindow::~PlatformVideoWindow() { notImplemented(); } + +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/PlatformVideoWindowGtk.cpp b/WebCore/platform/graphics/gstreamer/PlatformVideoWindowGtk.cpp index c5f835c..88b6552 100644 --- a/WebCore/platform/graphics/gstreamer/PlatformVideoWindowGtk.cpp +++ b/WebCore/platform/graphics/gstreamer/PlatformVideoWindowGtk.cpp @@ -19,8 +19,10 @@ #include "config.h" #include "PlatformVideoWindow.h" +#if ENABLE(VIDEO) #include <gtk/gtk.h> + #ifdef GDK_WINDOWING_X11 #include <gdk/gdkx.h> // for GDK_WINDOW_XID #endif @@ -59,3 +61,4 @@ PlatformVideoWindow::~PlatformVideoWindow() m_videoWindowId = 0; } +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp b/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp index dd8c3ee..00fef4b 100644 --- a/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp +++ b/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp @@ -28,6 +28,7 @@ #include "config.h" #include "VideoSinkGStreamer.h" +#if ENABLE(VIDEO) #include <glib.h> #include <gst/gst.h> @@ -370,3 +371,4 @@ webkit_video_sink_new(void) return (GstElement*)g_object_new(WEBKIT_TYPE_VIDEO_SINK, 0); } +#endif // ENABLE(VIDEO) diff --git a/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.h b/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.h index 4fb0b73..767e83f 100644 --- a/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.h +++ b/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.h @@ -20,6 +20,8 @@ #ifndef VideoSinkGStreamer_h #define VideoSinkGStreamer_h +#if ENABLE(VIDEO) + #include <glib-object.h> #include <gst/video/gstvideosink.h> @@ -75,4 +77,5 @@ GstElement *webkit_video_sink_new(void); G_END_DECLS +#endif // ENABLE(VIDEO) #endif diff --git a/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp b/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp index 1059b59..bcd59c6 100644 --- a/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp +++ b/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp @@ -18,6 +18,7 @@ #include "config.h" #include "WebKitWebSourceGStreamer.h" +#if ENABLE(VIDEO) #include "Document.h" #include "GOwnPtr.h" @@ -790,3 +791,5 @@ void StreamingClient::cannotShowURL(ResourceHandle*) GST_ELEMENT_ERROR(m_src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", m_src->priv->uri), (0)); } +#endif // ENABLE(VIDEO) + diff --git a/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.h b/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.h index ae19640..1594062 100644 --- a/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.h +++ b/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.h @@ -18,6 +18,7 @@ #ifndef WebKitWebSourceGStreamer_h #define WebKitWebSourceGStreamer_h +#if ENABLE(VIDEO) #include "Frame.h" #include <gst/gst.h> @@ -49,4 +50,5 @@ void webKitWebSrcSetFrame(WebKitWebSrc* src, WebCore::Frame* frame); G_END_DECLS +#endif // ENABLE(VIDEO) #endif diff --git a/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm b/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm index ab4ef4b..54b261c 100644 --- a/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm +++ b/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm @@ -31,7 +31,6 @@ #import "BlockExceptions.h" -#include "ANGLE/ResourceLimits.h" #include "ArrayBuffer.h" #include "ArrayBufferView.h" #include "WebGLObject.h" @@ -186,23 +185,23 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attrs, HostWi TBuiltInResource ANGLEResources; - ANGLEResources.maxVertexAttribs = 0; - getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &ANGLEResources.maxVertexAttribs); - ANGLEResources.maxVertexUniformVectors = 0; - getIntegerv(GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS, &ANGLEResources.maxVertexUniformVectors); - ANGLEResources.maxVaryingVectors = 0; - getIntegerv(GraphicsContext3D::MAX_VARYING_VECTORS, &ANGLEResources.maxVaryingVectors); - ANGLEResources.maxVertexTextureImageUnits = 0; - getIntegerv(GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &ANGLEResources.maxVertexTextureImageUnits); - ANGLEResources.maxCombinedTextureImageUnits = 0; - getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ANGLEResources.maxCombinedTextureImageUnits); - ANGLEResources.maxTextureImageUnits = 0; - getIntegerv(GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS, &ANGLEResources.maxTextureImageUnits); - ANGLEResources.maxFragmentUniformVectors = 0; - getIntegerv(GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS, &ANGLEResources.maxFragmentUniformVectors); + ANGLEResources.MaxVertexAttribs = 0; + getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &ANGLEResources.MaxVertexAttribs); + ANGLEResources.MaxVertexUniformVectors = 0; + getIntegerv(GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS, &ANGLEResources.MaxVertexUniformVectors); + ANGLEResources.MaxVaryingVectors = 0; + getIntegerv(GraphicsContext3D::MAX_VARYING_VECTORS, &ANGLEResources.MaxVaryingVectors); + ANGLEResources.MaxVertexTextureImageUnits = 0; + getIntegerv(GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxVertexTextureImageUnits); + ANGLEResources.MaxCombinedTextureImageUnits = 0; + getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxCombinedTextureImageUnits); + ANGLEResources.MaxTextureImageUnits = 0; + getIntegerv(GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxTextureImageUnits); + ANGLEResources.MaxFragmentUniformVectors = 0; + getIntegerv(GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS, &ANGLEResources.MaxFragmentUniformVectors); // Always set to 1 for OpenGL ES. - ANGLEResources.maxDrawBuffers = 1; + ANGLEResources.MaxDrawBuffers = 1; m_compiler.setResources(ANGLEResources); diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm index 315cc00..5fedaff 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -272,16 +272,16 @@ static TransformationMatrix flipTransform() } #endif -static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction) -{ - switch (timingFunction.type()) { - case LinearTimingFunction: - return [CAMediaTimingFunction functionWithName:@"linear"]; - case CubicBezierTimingFunction: - return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(timingFunction.x1()) :static_cast<float>(timingFunction.y1()) - :static_cast<float>(timingFunction.x2()) :static_cast<float>(timingFunction.y2())]; - } - return 0; +static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction* timingFunction) +{ + // By this point, timing functions can only be linear or cubic, not steps. + ASSERT(!timingFunction->isStepsTimingFunction()); + if (timingFunction->isCubicBezierTimingFunction()) { + const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction); + return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(ctf->x1()) :static_cast<float>(ctf->y1()) + :static_cast<float>(ctf->x2()) :static_cast<float>(ctf->y2())]; + } else + return [CAMediaTimingFunction functionWithName:@"linear"]; } static void setLayerBorderColor(PlatformLayer* layer, const Color& color) @@ -356,6 +356,20 @@ static NSDictionary* nullActionsDictionary() return actions; } +static bool animationHasStepsTimingFunction(const KeyframeValueList& valueList, const Animation* anim) +{ + if (anim->timingFunction()->isStepsTimingFunction()) + return true; + + for (unsigned i = 0; i < valueList.size(); ++i) { + const TimingFunction* timingFunction = valueList.at(i)->timingFunction(); + if (timingFunction && timingFunction->isStepsTimingFunction()) + return true; + } + + return false; +} + PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) { return new GraphicsLayerCA(client); @@ -716,6 +730,11 @@ bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const Int return false; #endif + // CoreAnimation does not handle the steps() timing function. Fall back + // to software animation in that case. + if (animationHasStepsTimingFunction(valueList, anim)) + return false; + bool createdAnimations = false; if (valueList.property() == AnimatedPropertyWebkitTransform) createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, keyframesName, timeOffset, boxSize); @@ -1913,9 +1932,9 @@ CAMediaTimingFunction* GraphicsLayerCA::timingFunctionForAnimationValue(const An if (animValue->timingFunction()) tf = animValue->timingFunction(); else if (anim->isTimingFunctionSet()) - tf = &anim->timingFunction(); + tf = anim->timingFunction().get(); - return getCAMediaTimingFunction(tf ? *tf : TimingFunction()); + return getCAMediaTimingFunction(tf ? tf : CubicBezierTimingFunction::create().get()); } bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim) diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp index 89badcc..e164f8c 100644 --- a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -1213,7 +1213,7 @@ PlatformLayer* GraphicsLayerQt::platformLayer() const template <typename T> struct KeyframeValueQt { - TimingFunction timingFunction; + const TimingFunction* timingFunction; T value; }; @@ -1230,23 +1230,32 @@ static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, q return bezier.solve(t, solveEpsilon(duration)); } -static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, double duration) +static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t) +{ + if (stepAtStart) + return qMin(1.0, (floor(numSteps * t) + 1) / numSteps); + return floor(numSteps * t) / numSteps; +} + +static inline qreal applyTimingFunction(const TimingFunction* timingFunction, qreal progress, double duration) { // We want the timing function to be as close as possible to what the web-developer intended, so // we're using the same function used by WebCore when compositing is disabled. Using easing-curves // would probably work for some of the cases, but wouldn't really buy us anything as we'd have to // convert the bezier function back to an easing curve. - if (timingFunction.type() == LinearTimingFunction) - return progress; - if (timingFunction.type() == CubicBezierTimingFunction) { - return solveCubicBezierFunction(timingFunction.x1(), - timingFunction.y1(), - timingFunction.x2(), - timingFunction.y2(), + if (timingFunction->isCubicBezierTimingFunction()) { + const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction); + return solveCubicBezierFunction(ctf->x1(), + ctf->y1(), + ctf->x2(), + ctf->y2(), double(progress), double(duration) / 1000); - } - return progress; + } else if (timingFunction->isStepsTimingFunction()) { + const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction); + return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress)); + } else + return progress; } // Helper functions to safely get a value out of WebCore's AnimationValue*. @@ -1322,9 +1331,9 @@ public: const AnimationValue* animationValue = values.at(i); KeyframeValueQt<T> keyframeValue; if (animationValue->timingFunction()) - keyframeValue.timingFunction = *animationValue->timingFunction(); + keyframeValue.timingFunction = animationValue->timingFunction(); else - keyframeValue.timingFunction = anim->timingFunction(); + keyframeValue.timingFunction = anim->timingFunction().get(); webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value); m_keyframeValues[animationValue->keyTime()] = keyframeValue; } @@ -1381,7 +1390,7 @@ protected: const KeyframeValueQt<T>& fromKeyframe = it.value(); const KeyframeValueQt<T>& toKeyframe = it2.value(); - const TimingFunction& timingFunc = fromKeyframe.timingFunction; + const TimingFunction* timingFunc = fromKeyframe.timingFunction; const T& fromValue = fromKeyframe.value; const T& toValue = toKeyframe.value; diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp index 4ad5571..605dcb7 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp @@ -604,6 +604,14 @@ PlatformLayer* MediaPlayerPrivate::platformLayer() const } #endif +PlatformMedia MediaPlayerPrivate::platformMedia() const +{ + PlatformMedia pm; + pm.type = PlatformMedia::QtMediaPlayerType; + pm.media.qtMediaPlayer = const_cast<MediaPlayerPrivate*>(this); + return pm; +} + } // namespace WebCore #include "moc_MediaPlayerPrivateQt.cpp" diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h index 165efde..117187d 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h @@ -99,6 +99,7 @@ public: virtual PlatformLayer* platformLayer() const; #endif + virtual PlatformMedia platformMedia() const; private slots: void mediaStatusChanged(QMediaPlayer::MediaStatus); void handleError(QMediaPlayer::Error); diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index ce5da2e..df54684 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -264,6 +264,11 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) void Path::closeSubpath() { + m_path.closeSubpath(); +} + +void Path::closeCanvasSubpath() +{ const int elementCount = m_path.elementCount(); if (!elementCount) @@ -469,6 +474,31 @@ void Path::transform(const AffineTransform& transform) m_path = qTransform.map(m_path); } +float Path::length() +{ + return m_path.length(); +} + +FloatPoint Path::pointAtLength(float length, bool& ok) +{ + ok = (length >= 0 && length <= m_path.length()); + + qreal percent = m_path.percentAtLength(length); + QPointF point = m_path.pointAtPercent(percent); + + return point; +} + +float Path::normalAngleAtLength(float length, bool& ok) +{ + ok = (length >= 0 && length <= m_path.length()); + + qreal percent = m_path.percentAtLength(length); + qreal angle = m_path.angleAtPercent(percent); + + return angle; +} + } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp b/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp index b6d6e65..b6d6e65 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h b/WebCore/platform/graphics/skia/FontCustomPlatformData.h index d451c9c..d451c9c 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/skia/FontCustomPlatformData.h diff --git a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp index 1c80d49..d618c19 100644 --- a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp +++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -1,10 +1,10 @@ /* * Copyright (c) 2006, Google Inc. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -14,7 +14,7 @@ * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -65,7 +65,7 @@ namespace { inline int fastMod(int value, int max) { int sign = SkExtractSign(value); - + value = SkApplySign(value, sign); if (value >= max) value %= max; @@ -391,7 +391,7 @@ void GraphicsContext::canvasClip(const Path& path) if (!isPathSkiaSafe(getCTM(), p)) return; - platformContext()->canvas()->clipPath(p); + platformContext()->canvasClipPath(p); } void GraphicsContext::clipOut(const IntRect& rect) @@ -499,7 +499,7 @@ void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* poin if (numPoints <= 1) return; - + // FIXME: IMPLEMENT!! } @@ -767,7 +767,7 @@ void GraphicsContext::fillRect(const FloatRect& rect) ClipRectToCanvas(*platformContext()->canvas(), r, &r); } - if (platformContext()->useGPU() && !m_common->state.fillPattern && !m_common->state.fillGradient && !platformContext()->getDrawLooper()) { + if (platformContext()->useGPU() && platformContext()->canAccelerate()) { platformContext()->prepareForHardwareDraw(); platformContext()->gpuCanvas()->fillRect(rect); return; @@ -789,7 +789,7 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorS if (paintingDisabled()) return; - if (platformContext()->useGPU() && !m_common->state.fillPattern && !m_common->state.fillGradient) { + if (platformContext()->useGPU() && platformContext()->canAccelerate()) { platformContext()->prepareForHardwareDraw(); platformContext()->gpuCanvas()->fillRect(rect, color, colorSpace); return; @@ -1240,14 +1240,19 @@ void GraphicsContext::translate(float w, float h) WebCoreFloatToSkScalar(h)); } -void GraphicsContext::setGraphicsContext3D(GraphicsContext3D* context3D, const IntSize& size) +void GraphicsContext::syncSoftwareCanvas() { - platformContext()->setGraphicsContext3D(context3D, size); + platformContext()->syncSoftwareCanvas(); } -void GraphicsContext::syncSoftwareCanvas() +void GraphicsContext::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* framebuffer, const IntSize& size) { - platformContext()->syncSoftwareCanvas(); + platformContext()->setSharedGraphicsContext3D(context, framebuffer, size); +} + +void GraphicsContext::markDirtyRect(const IntRect& rect) +{ + platformContext()->markDirtyRect(rect); } } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp index 9403406..2be7dc5 100644 --- a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -36,10 +36,12 @@ #include "Base64.h" #include "BitmapImage.h" #include "BitmapImageSingleFrameSkia.h" +#include "DrawingBuffer.h" +#include "GLES2Canvas.h" #include "GraphicsContext.h" #include "ImageData.h" -#include "PlatformContextSkia.h" #include "PNGImageEncoder.h" +#include "PlatformContextSkia.h" #include "SkColorPriv.h" #include "SkiaUtils.h" @@ -67,9 +69,7 @@ ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, b m_data.m_platformContext.setCanvas(&m_data.m_canvas); m_context.set(new GraphicsContext(&m_data.m_platformContext)); -#if OS(WINDOWS) m_context->platformContext()->setDrawingToImageBuffer(true); -#endif // Make the background transparent. It would be nice if this wasn't // required, but the canvas is currently filled with the magic transparency @@ -100,14 +100,26 @@ PassRefPtr<Image> ImageBuffer::copyImage() const void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const { -#if OS(LINUX) || OS(WINDOWS) context->platformContext()->beginLayerClippedToImage(rect, this); -#endif } void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, bool useLowQualityScale) { + if (m_data.m_platformContext.useGPU() && context->platformContext()->useGPU()) { + if (context->platformContext()->canAccelerate()) { + DrawingBuffer* sourceDrawingBuffer = m_data.m_platformContext.gpuCanvas()->drawingBuffer(); + unsigned sourceTexture = sourceDrawingBuffer->getRenderingResultsAsTexture(); + FloatRect destRectFlipped(destRect); + destRectFlipped.setY(destRect.y() + destRect.height()); + destRectFlipped.setHeight(-destRect.height()); + context->platformContext()->prepareForHardwareDraw(); + context->platformContext()->gpuCanvas()->drawTexturedRect(sourceTexture, m_size, srcRect, destRectFlipped, styleColorSpace, op); + return; + } + m_data.m_platformContext.syncSoftwareCanvas(); + } + RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context); context->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); } diff --git a/WebCore/platform/graphics/skia/ImageSkia.cpp b/WebCore/platform/graphics/skia/ImageSkia.cpp index aed289f..9625b34 100644 --- a/WebCore/platform/graphics/skia/ImageSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSkia.cpp @@ -47,6 +47,7 @@ #include "SkRect.h" #include "SkShader.h" #include "SkiaUtils.h" +#include "Texture.h" #include "skia/ext/image_operations.h" #include "skia/ext/platform_canvas.h" @@ -468,7 +469,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. - if (ctxt->platformContext()->useGPU()) { + if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) { drawBitmapGLES2(ctxt, bm, normSrcRect, normDstRect, colorSpace, compositeOp); return; } @@ -496,7 +497,7 @@ void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt, if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. - if (ctxt->platformContext()->useGPU()) { + if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) { drawBitmapGLES2(ctxt, &m_nativeImage, srcRect, dstRect, styleColorSpace, compositeOp); return; } diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index 3b1d015..88fbcdd 100644..100755 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -1,10 +1,10 @@ /* * Copyright (c) 2008, Google Inc. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -14,7 +14,7 @@ * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -33,12 +33,13 @@ #include "PlatformContextSkia.h" #include "AffineTransform.h" -#include "CanvasLayerChromium.h" +#include "DrawingBuffer.h" #include "GLES2Canvas.h" #include "GraphicsContext.h" #include "GraphicsContext3D.h" #include "ImageBuffer.h" #include "NativeImageSkia.h" +#include "SharedGraphicsContext3D.h" #include "SkiaUtils.h" #include "Texture.h" #include "TilingData.h" @@ -96,18 +97,19 @@ struct PlatformContextSkia::State { // color to produce a new output color. SkColor applyAlpha(SkColor) const; -#if OS(LINUX) || OS(WINDOWS) // If non-empty, the current State is clipped to this image. SkBitmap m_imageBufferClip; // If m_imageBufferClip is non-empty, this is the region the image is clipped to. FloatRect m_clip; -#endif // This is a list of clipping paths which are currently active, in the // order in which they were pushed. WTF::Vector<SkPath> m_antiAliasClipPaths; InterpolationQuality m_interpolationQuality; + // If we currently have a canvas (non-antialiased path) clip applied. + bool m_canvasClipApplied; + PlatformContextSkia::State cloneInheritedProperties(); private: // Not supported. @@ -133,6 +135,7 @@ PlatformContextSkia::State::State() , m_dash(0) , m_textDrawingMode(cTextFill) , m_interpolationQuality(InterpolationHigh) + , m_canvasClipApplied(false) { } @@ -153,12 +156,11 @@ PlatformContextSkia::State::State(const State& other) , m_lineJoin(other.m_lineJoin) , m_dash(other.m_dash) , m_textDrawingMode(other.m_textDrawingMode) -#if OS(LINUX) || OS(WINDOWS) , m_imageBufferClip(other.m_imageBufferClip) , m_clip(other.m_clip) -#endif , m_antiAliasClipPaths(other.m_antiAliasClipPaths) , m_interpolationQuality(other.m_interpolationQuality) + , m_canvasClipApplied(other.m_canvasClipApplied) { // Up the ref count of these. saveRef does nothing if 'this' is NULL. m_looper->safeRef(); @@ -181,7 +183,7 @@ PlatformContextSkia::State PlatformContextSkia::State::cloneInheritedProperties( PlatformContextSkia::State state(*this); // Everything is inherited except for the clip paths. - state.m_antiAliasClipPaths.clear(); + state.m_antiAliasClipPaths.clear(); return state; } @@ -203,9 +205,7 @@ SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const // Danger: canvas can be NULL. PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) : m_canvas(canvas) -#if OS(WINDOWS) , m_drawingToImageBuffer(false) -#endif , m_useGPU(false) , m_gpuCanvas(0) , m_backingStoreState(None) @@ -216,12 +216,8 @@ PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) PlatformContextSkia::~PlatformContextSkia() { -#if USE(ACCELERATED_COMPOSITING) - if (m_gpuCanvas) { - CanvasLayerChromium* layer = static_cast<CanvasLayerChromium*>(m_gpuCanvas->context()->platformLayer()); - layer->setPrepareTextureCallback(0); - } -#endif + if (m_gpuCanvas) + m_gpuCanvas->drawingBuffer()->setWillPublishCallback(0); } void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) @@ -229,7 +225,6 @@ void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) m_canvas = canvas; } -#if OS(WINDOWS) void PlatformContextSkia::setDrawingToImageBuffer(bool value) { m_drawingToImageBuffer = value; @@ -239,7 +234,6 @@ bool PlatformContextSkia::isDrawingToImageBuffer() const { return m_drawingToImageBuffer; } -#endif void PlatformContextSkia::save() { @@ -248,17 +242,14 @@ void PlatformContextSkia::save() m_stateStack.append(m_state->cloneInheritedProperties()); m_state = &m_stateStack.last(); -#if OS(LINUX) || OS(WINDOWS) // The clip image only needs to be applied once. Reset the image so that we // don't attempt to clip multiple times. m_state->m_imageBufferClip.reset(); -#endif // Save our native canvas. canvas()->save(); } -#if OS(LINUX) || OS(WINDOWS) void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect, const ImageBuffer* imageBuffer) { @@ -287,7 +278,6 @@ void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect, m_state->m_imageBufferClip = *bitmap; } } -#endif void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath) { @@ -306,12 +296,10 @@ void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath) void PlatformContextSkia::restore() { -#if OS(LINUX) || OS(WINDOWS) if (!m_state->m_imageBufferClip.empty()) { applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip); canvas()->restore(); } -#endif if (!m_state->m_antiAliasClipPaths.isEmpty()) applyAntiAliasedClipPaths(m_state->m_antiAliasClipPaths); @@ -337,7 +325,7 @@ void PlatformContextSkia::drawRect(SkRect rect) // We do a fill of four rects to simulate the stroke of a border. SkColor oldFillColor = m_state->m_fillColor; - // setFillColor() will set the shader to NULL, so save a ref to it now. + // setFillColor() will set the shader to NULL, so save a ref to it now. SkShader* oldFillShader = m_state->m_fillShader; oldFillShader->safeRef(); setFillColor(m_state->m_strokeColor); @@ -462,7 +450,7 @@ void PlatformContextSkia::setXfermodeMode(SkXfermode::Mode pdm) void PlatformContextSkia::setFillColor(SkColor color) { m_state->m_fillColor = color; - setFillShader(NULL); + setFillShader(0); } SkDrawLooper* PlatformContextSkia::getDrawLooper() const @@ -483,7 +471,7 @@ void PlatformContextSkia::setStrokeStyle(StrokeStyle strokeStyle) void PlatformContextSkia::setStrokeColor(SkColor strokeColor) { m_state->m_strokeColor = strokeColor; - setStrokeShader(NULL); + setStrokeShader(0); } float PlatformContextSkia::getStrokeThickness() const @@ -559,6 +547,12 @@ SkPath PlatformContextSkia::currentPathInLocalCoordinates() const return localPath; } +void PlatformContextSkia::canvasClipPath(const SkPath& path) +{ + m_state->m_canvasClipApplied = true; + m_canvas->clipPath(path); +} + void PlatformContextSkia::setFillRule(SkPath::FillType fr) { m_path.setFillType(fr); @@ -630,7 +624,6 @@ bool PlatformContextSkia::hasImageResamplingHint() const return !m_imageResamplingHintSrcSize.isEmpty() && !m_imageResamplingHintDstSize.isEmpty(); } -#if OS(LINUX) || OS(WINDOWS) void PlatformContextSkia::applyClipFromImage(const FloatRect& rect, const SkBitmap& imageBuffer) { // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we @@ -639,7 +632,6 @@ void PlatformContextSkia::applyClipFromImage(const FloatRect& rect, const SkBitm paint.setXfermodeMode(SkXfermode::kDstIn_Mode); m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint); } -#endif void PlatformContextSkia::applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths) { @@ -678,33 +670,47 @@ void PlatformContextSkia::applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths) m_canvas->restore(); } -#if USE(ACCELERATED_COMPOSITING) -class PrepareTextureCallbackImpl : public CanvasLayerChromium::PrepareTextureCallback { +bool PlatformContextSkia::canAccelerate() const +{ + return !m_state->m_fillShader // Can't accelerate with a fill gradient or pattern. + && !m_state->m_looper // Can't accelerate with a shadow. + && !m_state->m_canvasClipApplied; // Can't accelerate with a clip to path applied. +} + +class WillPublishCallbackImpl : public DrawingBuffer::WillPublishCallback { public: - static PassOwnPtr<PrepareTextureCallbackImpl> create(PlatformContextSkia* pcs) + static PassOwnPtr<WillPublishCallback> create(PlatformContextSkia* pcs) { - return new PrepareTextureCallbackImpl(pcs); + return adoptPtr(new WillPublishCallbackImpl(pcs)); } - virtual void willPrepareTexture() + virtual void willPublish() { m_pcs->prepareForHardwareDraw(); } + private: - explicit PrepareTextureCallbackImpl(PlatformContextSkia* pcs) : m_pcs(pcs) {} + explicit WillPublishCallbackImpl(PlatformContextSkia* pcs) + : m_pcs(pcs) + { + } + PlatformContextSkia* m_pcs; }; -#endif -void PlatformContextSkia::setGraphicsContext3D(GraphicsContext3D* context, const WebCore::IntSize& size) +void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const WebCore::IntSize& size) { - m_useGPU = true; - m_gpuCanvas = new GLES2Canvas(context, size); - m_uploadTexture.clear(); -#if USE(ACCELERATED_COMPOSITING) - CanvasLayerChromium* layer = static_cast<CanvasLayerChromium*>(context->platformLayer()); - layer->setPrepareTextureCallback(PrepareTextureCallbackImpl::create(this)); -#endif + if (context && drawingBuffer) { + m_useGPU = true; + m_gpuCanvas = new GLES2Canvas(context, drawingBuffer, size); + m_uploadTexture.clear(); + drawingBuffer->setWillPublishCallback(WillPublishCallbackImpl::create(this)); + } else { + syncSoftwareCanvas(); + m_uploadTexture.clear(); + m_gpuCanvas.clear(); + m_useGPU = false; + } } void PlatformContextSkia::prepareForSoftwareDraw() const @@ -725,9 +731,7 @@ void PlatformContextSkia::prepareForSoftwareDraw() const // of a compositing operation). if (m_state->m_xferMode == SkXfermode::kSrcOver_Mode) { - // Last drawn on hardware; clear out the canvas. - m_canvas->getDevice()->eraseColor(0); - // Start compositing into the empty canvas. + // Note that we have rendering results in both the hardware and software backing stores. m_backingStoreState = Mixed; } else { readbackHardwareToSoftware(); @@ -780,17 +784,42 @@ void PlatformContextSkia::syncSoftwareCanvas() const m_backingStoreState = Software; } +void PlatformContextSkia::markDirtyRect(const IntRect& rect) +{ + if (!m_useGPU) + return; + + switch (m_backingStoreState) { + case Software: + case Mixed: + m_softwareDirtyRect.unite(rect); + return; + case Hardware: + return; + default: + ASSERT_NOT_REACHED(); + } +} + void PlatformContextSkia::uploadSoftwareToHardware(CompositeOperator op) const { const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(false); SkAutoLockPixels lock(bitmap); - GraphicsContext3D* context = m_gpuCanvas->context(); + SharedGraphicsContext3D* context = m_gpuCanvas->context(); if (!m_uploadTexture || m_uploadTexture->tiles().totalSizeX() < bitmap.width() || m_uploadTexture->tiles().totalSizeY() < bitmap.height()) - m_uploadTexture = Texture::create(context, Texture::BGRA8, bitmap.width(), bitmap.height()); - m_uploadTexture->load(bitmap.getPixels()); - IntRect rect(0, 0, bitmap.width(), bitmap.height()); + m_uploadTexture = context->createTexture(Texture::BGRA8, bitmap.width(), bitmap.height()); + + m_uploadTexture->updateSubRect(bitmap.getPixels(), m_softwareDirtyRect); AffineTransform identity; - gpuCanvas()->drawTexturedRect(m_uploadTexture.get(), rect, rect, identity, 1.0, DeviceColorSpace, op); + gpuCanvas()->drawTexturedRect(m_uploadTexture.get(), m_softwareDirtyRect, m_softwareDirtyRect, identity, 1.0, DeviceColorSpace, op); + // Clear out the region of the software canvas we just uploaded. + m_canvas->save(); + m_canvas->resetMatrix(); + SkRect bounds = m_softwareDirtyRect; + m_canvas->clipRect(bounds, SkRegion::kReplace_Op); + m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); + m_canvas->restore(); + m_softwareDirtyRect.setWidth(0); // Clear dirty rect. } void PlatformContextSkia::readbackHardwareToSoftware() const @@ -799,7 +828,8 @@ void PlatformContextSkia::readbackHardwareToSoftware() const SkAutoLockPixels lock(bitmap); int width = bitmap.width(), height = bitmap.height(); OwnArrayPtr<uint32_t> buf(new uint32_t[width]); - GraphicsContext3D* context = m_gpuCanvas->context(); + SharedGraphicsContext3D* context = m_gpuCanvas->context(); + m_gpuCanvas->bindFramebuffer(); // Flips the image vertically. for (int y = 0; y < height; ++y) { uint32_t* pixels = bitmap.getAddr32(0, y); @@ -814,6 +844,7 @@ void PlatformContextSkia::readbackHardwareToSoftware() const } } } + m_softwareDirtyRect.unite(IntRect(0, 0, width, height)); // Mark everything as dirty. } } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h index 82edc16..4ba85d1 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -1,10 +1,10 @@ /* * Copyright (c) 2008, Google Inc. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -14,7 +14,7 @@ * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -35,7 +35,6 @@ #include "Noncopyable.h" #include "SkDashPathEffect.h" -#include "SkDeque.h" #include "SkDrawLooper.h" #include "SkPaint.h" #include "SkPath.h" @@ -46,9 +45,10 @@ namespace WebCore { enum CompositeOperator; +class DrawingBuffer; class GLES2Canvas; -class Texture; class GraphicsContext3D; +class Texture; // This class holds the platform-specific state for GraphicsContext. We put // most of our Skia wrappers on this class. In theory, a lot of this stuff could @@ -78,7 +78,6 @@ public: // to the constructor. void setCanvas(skia::PlatformCanvas*); -#if OS(WINDOWS) // If false we're rendering to a GraphicsContext for a web page, if false // we're not (as is the case when rendering to a canvas object). // If this is true the contents have not been marked up with the magic @@ -86,7 +85,6 @@ public: // correctly updated. void setDrawingToImageBuffer(bool); bool isDrawingToImageBuffer() const; -#endif void save(); void restore(); @@ -95,9 +93,7 @@ public: // |rect|. This layer is implicitly restored when the next restore is // invoked. // NOTE: |imageBuffer| may be deleted before the |restore| is invoked. -#if OS(LINUX) || OS(WINDOWS) void beginLayerClippedToImage(const FloatRect&, const ImageBuffer*); -#endif void clipPathAntiAliased(const SkPath&); // Sets up the common flags on a paint for antialiasing, effects, etc. @@ -143,6 +139,8 @@ public: void addPath(const SkPath&); SkPath currentPathInLocalCoordinates() const; + void canvasClipPath(const SkPath&); + // Returns the fill color. The returned color has it's alpha adjusted // by the current alpha. SkColor effectiveFillColor() const; @@ -180,8 +178,10 @@ public: void setImageResamplingHint(const IntSize& srcSize, const FloatSize& dstSize); void clearImageResamplingHint(); bool hasImageResamplingHint() const; + + bool canAccelerate() const; bool useGPU() { return m_useGPU; } - void setGraphicsContext3D(GraphicsContext3D*, const IntSize&); + void setSharedGraphicsContext3D(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&); GLES2Canvas* gpuCanvas() const { return m_gpuCanvas.get(); } // Call these before making a call that manipulates the underlying @@ -190,13 +190,12 @@ public: void prepareForHardwareDraw() const; // Call to force the skia::PlatformCanvas to contain all rendering results. void syncSoftwareCanvas() const; + void markDirtyRect(const IntRect& rect); private: -#if OS(LINUX) || OS(WINDOWS) // Used when restoring and the state has an image clip. Only shows the pixels in // m_canvas that are also in imageBuffer. void applyClipFromImage(const FloatRect&, const SkBitmap&); -#endif void applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths); void uploadSoftwareToHardware(CompositeOperator) const; @@ -218,17 +217,16 @@ private: // Current path in global coordinates. SkPath m_path; - // Stores image sizes for a hint to compute image resampling modes. + // Stores image sizes for a hint to compute image resampling modes. // Values are used in ImageSkia.cpp IntSize m_imageResamplingHintSrcSize; FloatSize m_imageResamplingHintDstSize; -#if OS(WINDOWS) bool m_drawingToImageBuffer; -#endif bool m_useGPU; OwnPtr<GLES2Canvas> m_gpuCanvas; mutable enum { None, Software, Mixed, Hardware } m_backingStoreState; mutable RefPtr<Texture> m_uploadTexture; + mutable IntRect m_softwareDirtyRect; }; } diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index 7b65e96..0203d42 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -79,37 +79,37 @@ GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha) // suitable for all clients? void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { - if (mayCreateBitmap && hdc && inTransparencyLayer()) { - if (dstRect.isEmpty()) - return; - - HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); - - // Need to make a CGImage out of the bitmap's pixel buffer and then draw - // it into our context. - BITMAP info; - GetObject(bitmap, sizeof(info), &info); - ASSERT(info.bmBitsPixel == 32); - - CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); - CGContextRef bitmapContext = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, - info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | - (supportAlphaBlend ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst)); - CGColorSpaceRelease(deviceRGB); - - CGImageRef image = CGBitmapContextCreateImage(bitmapContext); - CGContextDrawImage(m_data->m_cgContext.get(), dstRect, image); - - // Delete all our junk. - CGImageRelease(image); - CGContextRelease(bitmapContext); - ::DeleteDC(hdc); - ::DeleteObject(bitmap); - + bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || inTransparencyLayer()); + if (!createdBitmap) { + m_data->restore(); return; } - m_data->restore(); + if (dstRect.isEmpty()) + return; + + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); + + // Need to make a CGImage out of the bitmap's pixel buffer and then draw + // it into our context. + BITMAP info; + GetObject(bitmap, sizeof(info), &info); + ASSERT(info.bmBitsPixel == 32); + + CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + CGContextRef bitmapContext = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, + info.bmWidthBytes, deviceRGB, kCGBitmapByteOrder32Little | + (supportAlphaBlend ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst)); + CGColorSpaceRelease(deviceRGB); + + CGImageRef image = CGBitmapContextCreateImage(bitmapContext); + CGContextDrawImage(m_data->m_cgContext.get(), dstRect, image); + + // Delete all our junk. + CGImageRelease(image); + CGContextRelease(bitmapContext); + ::DeleteDC(hdc); + ::DeleteObject(bitmap); } void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point) diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index 94df6ae..a989c24 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -92,7 +92,8 @@ static void setRGBABitmapAlpha(unsigned char* bytes, size_t length, unsigned cha void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { - if (!mayCreateBitmap || !hdc || !inTransparencyLayer()) { + bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || inTransparencyLayer()); + if (!hdc || !createdBitmap) { m_data->restore(); return; } diff --git a/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/WebCore/platform/graphics/win/GraphicsContextWin.cpp index 161b9c6..f1953e4 100644 --- a/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -101,7 +101,7 @@ GraphicsContext::WindowsBitmap* GraphicsContext::createWindowsBitmap(IntSize siz HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { // FIXME: Should a bitmap be created also when a shadow is set? - if (mayCreateBitmap && inTransparencyLayer()) { + if (mayCreateBitmap && (!m_data->m_hdc || inTransparencyLayer())) { if (dstRect.isEmpty()) return 0; diff --git a/WebCore/platform/graphics/win/IconWin.cpp b/WebCore/platform/graphics/win/IconWin.cpp index cc9343a..05959e0 100644 --- a/WebCore/platform/graphics/win/IconWin.cpp +++ b/WebCore/platform/graphics/win/IconWin.cpp @@ -23,6 +23,7 @@ #include "Icon.h" #include "GraphicsContext.h" +#include "LocalWindowsContext.h" #include "PlatformString.h" #include <tchar.h> #include <windows.h> @@ -90,11 +91,8 @@ void Icon::paint(GraphicsContext* context, const IntRect& r) #if OS(WINCE) context->drawIcon(m_hIcon, r, DI_NORMAL); #else - HDC hdc = context->getWindowsContext(r); - - DrawIconEx(hdc, r.x(), r.y(), m_hIcon, r.width(), r.height(), 0, 0, DI_NORMAL); - - context->releaseWindowsContext(hdc, r); + LocalWindowsContext windowContext(context, r); + DrawIconEx(windowContext.hdc(), r.x(), r.y(), m_hIcon, r.width(), r.height(), 0, 0, DI_NORMAL); #endif } diff --git a/WebCore/platform/graphics/win/LocalWindowsContext.h b/WebCore/platform/graphics/win/LocalWindowsContext.h new file mode 100755 index 0000000..c216140 --- /dev/null +++ b/WebCore/platform/graphics/win/LocalWindowsContext.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 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 LocalWindowsContext_h +#define LocalWindowsContext_h + +#include "config.h" +#include "GraphicsContext.h" + +namespace WebCore { + +class LocalWindowsContext : public Noncopyable { +public: + LocalWindowsContext(GraphicsContext* graphicsContext, const IntRect& rect, bool supportAlphaBlend = true, bool mayCreateBitmap = true) + : m_graphicsContext(graphicsContext) + , m_rect(rect) + , m_supportAlphaBlend(supportAlphaBlend) + , m_mayCreateBitmap(mayCreateBitmap) + { + m_hdc = m_graphicsContext->getWindowsContext(m_rect, m_supportAlphaBlend, m_mayCreateBitmap); + } + + ~LocalWindowsContext() + { + m_graphicsContext->releaseWindowsContext(m_hdc, m_rect, m_supportAlphaBlend, m_mayCreateBitmap); + } + + HDC hdc() const { return m_hdc; } + +private: + GraphicsContext* m_graphicsContext; + HDC m_hdc; + IntRect m_rect; + bool m_supportAlphaBlend; + bool m_mayCreateBitmap; +}; + +} // namespace WebCore +#endif // LocalWindowsContext_h diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index 34f1135..c37f5d5 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -600,6 +600,7 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) bool usingTempBitmap = false; OwnPtr<GraphicsContext::WindowsBitmap> bitmap; + // FIXME: use LocalWindowsContext. HDC hdc = p->getWindowsContext(r); if (!hdc) { // The graphics context doesn't have an associated HDC so create a temporary diff --git a/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp b/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp index 2a355c2..630a8af 100644 --- a/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp +++ b/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp @@ -408,10 +408,7 @@ bool WKCACFLayerRenderer::createRenderer() m_d3dDevice = device; - D3DXMATRIXA16 projection; - D3DXMatrixOrthoOffCenterRH(&projection, rect.left, rect.right, rect.top, rect.bottom, -1.0f, 1.0f); - - m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection); + initD3DGeometry(); m_renderer = CARenderOGLNew(&kCARenderDX9Callbacks, m_d3dDevice.get(), 0); diff --git a/WebCore/platform/gtk/ClipboardGtk.cpp b/WebCore/platform/gtk/ClipboardGtk.cpp index b2c32a4..3fe5ba1 100644 --- a/WebCore/platform/gtk/ClipboardGtk.cpp +++ b/WebCore/platform/gtk/ClipboardGtk.cpp @@ -348,7 +348,7 @@ void ClipboardGtk::writeRange(Range* range, Frame* frame) { ASSERT(range); - m_dataObject->setText(frame->selectedText()); + m_dataObject->setText(frame->editor()->selectedText()); m_dataObject->setMarkup(createMarkup(range, 0, AnnotateForInterchange, false, AbsoluteURLs)); if (m_clipboard) diff --git a/WebCore/platform/gtk/GeolocationServiceGtk.cpp b/WebCore/platform/gtk/GeolocationServiceGtk.cpp index 69a0843..5b34c68 100644 --- a/WebCore/platform/gtk/GeolocationServiceGtk.cpp +++ b/WebCore/platform/gtk/GeolocationServiceGtk.cpp @@ -19,6 +19,7 @@ #include "config.h" #include "GeolocationServiceGtk.h" +#if ENABLE(GEOLOCATION) #include "GOwnPtr.h" #include "NotImplemented.h" @@ -211,3 +212,4 @@ void GeolocationServiceGtk::setError(PositionError::ErrorCode errorCode, const c } } +#endif // ENABLE(GEOLOCATION) diff --git a/WebCore/platform/gtk/GeolocationServiceGtk.h b/WebCore/platform/gtk/GeolocationServiceGtk.h index c123017..46249ed 100644 --- a/WebCore/platform/gtk/GeolocationServiceGtk.h +++ b/WebCore/platform/gtk/GeolocationServiceGtk.h @@ -19,6 +19,7 @@ #ifndef GeolocationServiceGtk_h #define GeolocationServiceGtk_h +#if ENABLE(GEOLOCATION) #include "GeolocationService.h" #include "Geoposition.h" @@ -70,4 +71,5 @@ namespace WebCore { }; } +#endif // ENABLE(GEOLOCATION) #endif diff --git a/WebCore/platform/gtk/MainFrameScrollbarGtk.cpp b/WebCore/platform/gtk/MainFrameScrollbarGtk.cpp new file mode 100644 index 0000000..c2e24e0 --- /dev/null +++ b/WebCore/platform/gtk/MainFrameScrollbarGtk.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2007, 2009 Holger Hans Peter Freyther zecke@selfish.org + * Copyright (C) 2010 Gustavo Noronha Silva <gns@gnome.org> + * Copyright (C) 2010 Collabora Ltd. + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "MainFrameScrollbarGtk.h" + +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "IntRect.h" +#include <gtk/gtk.h> + +using namespace WebCore; + +PassRefPtr<MainFrameScrollbarGtk> MainFrameScrollbarGtk::create(ScrollbarClient* client, ScrollbarOrientation orientation, GtkAdjustment* adj) +{ + return adoptRef(new MainFrameScrollbarGtk(client, orientation, adj)); +} + +// Main frame scrollbars are slaves to a GtkAdjustment. If a main frame +// scrollbar has an m_adjustment, it belongs to the container (a GtkWidget such +// as GtkScrolledWindow). The adjustment may also be null, in which case there +// is no containing view or the parent ScrollView is in some sort of transition +// state. These scrollbars are never painted, as the container takes care of +// that. They exist only to shuttle data from the GtkWidget container into +// WebCore and vice-versa. +MainFrameScrollbarGtk::MainFrameScrollbarGtk(ScrollbarClient* client, ScrollbarOrientation orientation, GtkAdjustment* adjustment) + : Scrollbar(client, orientation, RegularScrollbar) + , m_adjustment(0) +{ + attachAdjustment(adjustment); + + // We have nothing to show as we are solely operating on the GtkAdjustment. + resize(0, 0); +} + +MainFrameScrollbarGtk::~MainFrameScrollbarGtk() +{ + if (m_adjustment) + detachAdjustment(); +} + +void MainFrameScrollbarGtk::attachAdjustment(GtkAdjustment* adjustment) +{ + if (m_adjustment) + detachAdjustment(); + + m_adjustment = adjustment; + if (!m_adjustment) + return; + + g_signal_connect(m_adjustment.get(), "value-changed", G_CALLBACK(MainFrameScrollbarGtk::gtkValueChanged), this); + updateThumbProportion(); + updateThumbPosition(); +} + +void MainFrameScrollbarGtk::detachAdjustment() +{ + if (!m_adjustment) + return; + + g_signal_handlers_disconnect_by_func(G_OBJECT(m_adjustment.get()), (gpointer)MainFrameScrollbarGtk::gtkValueChanged, this); + + // For the case where we only operate on the GtkAdjustment it is best to + // reset the values so that the surrounding scrollbar gets updated, or + // e.g. for a GtkScrolledWindow the scrollbar gets hidden. + gtk_adjustment_configure(m_adjustment.get(), 0, 0, 0, 0, 0, 0); + + m_adjustment = 0; +} + +void MainFrameScrollbarGtk::updateThumbPosition() +{ + if (!m_adjustment || gtk_adjustment_get_value(m_adjustment.get()) == m_currentPos) + return; + gtk_adjustment_set_value(m_adjustment.get(), m_currentPos); +} + +void MainFrameScrollbarGtk::updateThumbProportion() +{ + if (!m_adjustment) + return; + gtk_adjustment_configure(m_adjustment.get(), + gtk_adjustment_get_value(m_adjustment.get()), + gtk_adjustment_get_lower(m_adjustment.get()), + m_totalSize, + m_lineStep, + m_pageStep, + m_visibleSize); +} + +void MainFrameScrollbarGtk::gtkValueChanged(GtkAdjustment*, MainFrameScrollbarGtk* that) +{ + that->setValue(static_cast<int>(gtk_adjustment_get_value(that->m_adjustment.get())), NotFromScrollAnimator); +} + +void MainFrameScrollbarGtk::paint(GraphicsContext* context, const IntRect& rect) +{ + // Main frame scrollbars are not painted by WebCore. + return; +} diff --git a/WebCore/platform/gtk/MainFrameScrollbarGtk.h b/WebCore/platform/gtk/MainFrameScrollbarGtk.h new file mode 100644 index 0000000..8271ef5 --- /dev/null +++ b/WebCore/platform/gtk/MainFrameScrollbarGtk.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef MainFrameScrollbarGtk_h +#define MainFrameScrollbarGtk_h + +#include "GRefPtrGtk.h" +#include "Scrollbar.h" +#include <wtf/PassRefPtr.h> + +typedef struct _GtkAdjustment GtkAdjustment; + +namespace WebCore { + +class MainFrameScrollbarGtk : public Scrollbar { +public: + ~MainFrameScrollbarGtk(); + virtual void paint(GraphicsContext*, const IntRect&); + void detachAdjustment(); + void attachAdjustment(GtkAdjustment*); + static PassRefPtr<MainFrameScrollbarGtk> create(ScrollbarClient*, ScrollbarOrientation, GtkAdjustment*); + +protected: + virtual void updateThumbPosition(); + virtual void updateThumbProportion(); + +private: + MainFrameScrollbarGtk(ScrollbarClient*, ScrollbarOrientation, GtkAdjustment*); + static void gtkValueChanged(GtkAdjustment*, MainFrameScrollbarGtk*); + + PlatformRefPtr<GtkAdjustment> m_adjustment; +}; + +} + +#endif // ScrollbarGtk_h diff --git a/WebCore/platform/gtk/PasteboardGtk.cpp b/WebCore/platform/gtk/PasteboardGtk.cpp index ddb9768..76b7bb0 100644 --- a/WebCore/platform/gtk/PasteboardGtk.cpp +++ b/WebCore/platform/gtk/PasteboardGtk.cpp @@ -67,7 +67,7 @@ void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, { GtkClipboard* clipboard = m_helper->getClipboard(frame); DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); - dataObject->setText(frame->selectedText()); + dataObject->setText(frame->editor()->selectedText()); dataObject->setMarkup(createMarkup(selectedRange, 0, AnnotateForInterchange, false, AbsoluteURLs)); m_helper->writeClipboardContents(clipboard); } diff --git a/WebCore/platform/gtk/ScrollViewGtk.cpp b/WebCore/platform/gtk/ScrollViewGtk.cpp index 565123c..ebcdaa1 100644 --- a/WebCore/platform/gtk/ScrollViewGtk.cpp +++ b/WebCore/platform/gtk/ScrollViewGtk.cpp @@ -39,10 +39,10 @@ #include "GtkVersioning.h" #include "HostWindow.h" #include "IntRect.h" +#include "MainFrameScrollbarGtk.h" #include "Page.h" #include "PlatformMouseEvent.h" #include "PlatformWheelEvent.h" -#include "ScrollbarGtk.h" #include "ScrollbarTheme.h" #include <gtk/gtk.h> @@ -53,8 +53,6 @@ namespace WebCore { void ScrollView::platformInit() { - m_horizontalAdjustment = 0; - m_verticalAdjustment = 0; } void ScrollView::platformDestroy() @@ -65,9 +63,8 @@ void ScrollView::platformDestroy() PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation) { - // If this is an interior frame scrollbar, we want to create a scrollbar without - // passing a GtkAdjustment. This will cause the Scrollbar to create a native GTK+ - // scrollbar. + // If this is an interior frame scrollbar, we want to create a totally fake + // scrollbar with no GtkAdjustment backing it. if (parent()) return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); @@ -75,10 +72,10 @@ PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientati // and defers to our GtkAdjustment for all of its state. Note that the GtkAdjustment // may be null here. if (orientation == HorizontalScrollbar) - return ScrollbarGtk::createScrollbar(this, orientation, m_horizontalAdjustment); + return MainFrameScrollbarGtk::create(this, orientation, m_horizontalAdjustment.get()); // VerticalScrollbar - return ScrollbarGtk::createScrollbar(this, orientation, m_verticalAdjustment); + return MainFrameScrollbarGtk::create(this, orientation, m_verticalAdjustment.get()); } /* @@ -89,15 +86,23 @@ void ScrollView::setGtkAdjustments(GtkAdjustment* hadj, GtkAdjustment* vadj, boo { ASSERT(!hadj == !vadj); + // If this is a non-main frame ScrollView, we do not want to set the + // m_horizontalAdjustments & m_verticalAdjustments members. At the same + // time we want to to allow FrameLoaderClientGtk.cpp to call + // ScrollView::setGtkAdjustments(0, 0) unconditionally. + ASSERT(!parent() || !hadj); + if (parent()) + return; + m_horizontalAdjustment = hadj; m_verticalAdjustment = vadj; if (!m_horizontalAdjustment) { - ScrollbarGtk* hScrollbar = reinterpret_cast<ScrollbarGtk*>(horizontalScrollbar()); + MainFrameScrollbarGtk* hScrollbar = reinterpret_cast<MainFrameScrollbarGtk*>(horizontalScrollbar()); if (hScrollbar) hScrollbar->detachAdjustment(); - ScrollbarGtk* vScrollbar = reinterpret_cast<ScrollbarGtk*>(verticalScrollbar()); + MainFrameScrollbarGtk* vScrollbar = reinterpret_cast<MainFrameScrollbarGtk*>(verticalScrollbar()); if (vScrollbar) vScrollbar->detachAdjustment(); @@ -109,11 +114,11 @@ void ScrollView::setGtkAdjustments(GtkAdjustment* hadj, GtkAdjustment* vadj, boo setHasVerticalScrollbar(true); setHasHorizontalScrollbar(true); - ScrollbarGtk* hScrollbar = reinterpret_cast<ScrollbarGtk*>(horizontalScrollbar()); - hScrollbar->attachAdjustment(m_horizontalAdjustment); + MainFrameScrollbarGtk* hScrollbar = reinterpret_cast<MainFrameScrollbarGtk*>(horizontalScrollbar()); + hScrollbar->attachAdjustment(m_horizontalAdjustment.get()); - ScrollbarGtk* vScrollbar = reinterpret_cast<ScrollbarGtk*>(verticalScrollbar()); - vScrollbar->attachAdjustment(m_verticalAdjustment); + MainFrameScrollbarGtk* vScrollbar = reinterpret_cast<MainFrameScrollbarGtk*>(verticalScrollbar()); + vScrollbar->attachAdjustment(m_verticalAdjustment.get()); // We used to reset everything to 0 here, but when page cache // is enabled we reuse FrameViews that are cached. Since their @@ -124,7 +129,7 @@ void ScrollView::setGtkAdjustments(GtkAdjustment* hadj, GtkAdjustment* vadj, boo // able to report correct values. int horizontalPageStep = max(max<int>(frameRect().width() * Scrollbar::minFractionToStepWhenPaging(), frameRect().width() - Scrollbar::maxOverlapBetweenPages()), 1); - gtk_adjustment_configure(m_horizontalAdjustment, + gtk_adjustment_configure(m_horizontalAdjustment.get(), resetValues ? 0 : scrollOffset().width(), 0, resetValues ? 0 : contentsSize().width(), resetValues ? 0 : Scrollbar::pixelsPerLineStep(), @@ -132,7 +137,7 @@ void ScrollView::setGtkAdjustments(GtkAdjustment* hadj, GtkAdjustment* vadj, boo resetValues ? 0 : frameRect().width()); int verticalPageStep = max(max<int>(frameRect().height() * Scrollbar::minFractionToStepWhenPaging(), frameRect().height() - Scrollbar::maxOverlapBetweenPages()), 1); - gtk_adjustment_configure(m_verticalAdjustment, + gtk_adjustment_configure(m_verticalAdjustment.get(), resetValues ? 0 : scrollOffset().height(), 0, resetValues ? 0 : contentsSize().height(), resetValues ? 0 : Scrollbar::pixelsPerLineStep(), @@ -142,72 +147,73 @@ void ScrollView::setGtkAdjustments(GtkAdjustment* hadj, GtkAdjustment* vadj, boo void ScrollView::platformAddChild(Widget* child) { - if (!GTK_IS_SOCKET(child->platformWidget())) - gtk_container_add(GTK_CONTAINER(hostWindow()->platformPageClient()), child->platformWidget()); } void ScrollView::platformRemoveChild(Widget* child) { - GtkWidget* parent; - - // HostWindow can be NULL here. If that's the case - // let's grab the child's parent instead. - if (hostWindow()) - parent = GTK_WIDGET(hostWindow()->platformPageClient()); - else - parent = GTK_WIDGET(gtk_widget_get_parent(child->platformWidget())); - - if (GTK_IS_CONTAINER(parent) && parent == gtk_widget_get_parent(child->platformWidget())) - gtk_container_remove(GTK_CONTAINER(parent), child->platformWidget()); } IntRect ScrollView::visibleContentRect(bool includeScrollbars) const { - if (!m_horizontalAdjustment) + // If we are an interior frame scrollbar or are in some sort of transition + // state, just calculate our size based on what the GTK+ theme says the + // scrollbar width should be. + if (parent() || !hostWindow() || !hostWindow()->platformPageClient()) { return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()), IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)), max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0)))); + } - // Main frame. + // We don't have a parent, so we are the main frame and thus have + // a parent widget which we can use to measure the visible region. GtkWidget* measuredWidget = hostWindow()->platformPageClient(); - GtkWidget* parent = gtk_widget_get_parent(measuredWidget); + GtkWidget* parentWidget = gtk_widget_get_parent(measuredWidget); // We may not be in a widget that displays scrollbars, but we may // have other kinds of decoration that make us smaller. - if (parent && includeScrollbars) - measuredWidget = parent; + if (parentWidget && includeScrollbars) + measuredWidget = parentWidget; GtkAllocation allocation; -#if GTK_CHECK_VERSION(2, 18, 0) gtk_widget_get_allocation(measuredWidget, &allocation); -#else - allocation = measuredWidget->allocation; -#endif return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()), IntSize(allocation.width, allocation.height)); } -void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode, bool, bool) +void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode, bool horizontalLock, bool verticalLock) { - if (horizontalMode == m_horizontalScrollbarMode && verticalMode == m_verticalScrollbarMode) - return; + // FIXME: Restructure the ScrollView abstraction so that we do not have to + // copy this verbatim from ScrollView.cpp. Until then, we should make sure this + // is kept in sync. + bool needsUpdate = false; + + if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) { + m_horizontalScrollbarMode = horizontalMode; + needsUpdate = true; + } - m_horizontalScrollbarMode = horizontalMode; - m_verticalScrollbarMode = verticalMode; + if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) { + m_verticalScrollbarMode = verticalMode; + needsUpdate = true; + } - // We don't really care about reporting policy changes on frames - // that have no adjustments attached to them. - if (!m_horizontalAdjustment) { + if (horizontalLock) + setHorizontalScrollbarLock(); + + if (verticalLock) + setVerticalScrollbarLock(); + + if (needsUpdate) updateScrollbars(scrollOffset()); - return; - } - if (!isFrameView()) + // We don't need to report policy changes on ScrollView's unless this + // one has an adjustment attached and it is a main frame. + if (!m_horizontalAdjustment || parent() || !isFrameView()) return; // For frames that do have adjustments attached, we want to report // policy changes, so that they may be applied to the widget to - // which the WebView has been added, for instance. + // which the WebView's container (e.g. GtkScrolledWindow). if (hostWindow()) hostWindow()->scrollbarsModeDidChange(); } diff --git a/WebCore/platform/gtk/ScrollbarGtk.cpp b/WebCore/platform/gtk/ScrollbarGtk.cpp deleted file mode 100644 index 8a1c4fa..0000000 --- a/WebCore/platform/gtk/ScrollbarGtk.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2007, 2009 Holger Hans Peter Freyther zecke@selfish.org - * Copyright (C) 2010 Gustavo Noronha Silva <gns@gnome.org> - * Copyright (C) 2010 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" -#include "ScrollbarGtk.h" - -#include "FrameView.h" -#include "GraphicsContext.h" -#include "GtkVersioning.h" -#include "IntRect.h" -#include "ScrollbarTheme.h" - -#include <gtk/gtk.h> - -using namespace WebCore; - -PassRefPtr<Scrollbar> Scrollbar::createNativeScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size) -{ - return adoptRef(new ScrollbarGtk(client, orientation, size)); -} - -PassRefPtr<ScrollbarGtk> ScrollbarGtk::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, GtkAdjustment* adj) -{ - return adoptRef(new ScrollbarGtk(client, orientation, adj)); -} - -static gboolean gtkScrollEventCallback(GtkWidget* widget, GdkEventScroll* event, ScrollbarGtk*) -{ - /* Scroll only if our parent rejects the scroll event. The rationale for - * this is that we want the main frame to scroll when we move the mouse - * wheel over a child scrollbar in most cases. */ - return gtk_widget_event(gtk_widget_get_parent(widget), reinterpret_cast<GdkEvent*>(event)); -} - -ScrollbarGtk::ScrollbarGtk(ScrollbarClient* client, ScrollbarOrientation orientation, - ScrollbarControlSize controlSize) - : Scrollbar(client, orientation, controlSize) - , m_adjustment(GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0))) -{ - GtkWidget* scrollBar = orientation == HorizontalScrollbar ? - gtk_hscrollbar_new(m_adjustment): - gtk_vscrollbar_new(m_adjustment); - gtk_widget_show(scrollBar); - g_object_ref(m_adjustment); - g_signal_connect(m_adjustment, "value-changed", G_CALLBACK(ScrollbarGtk::gtkValueChanged), this); - g_signal_connect(scrollBar, "scroll-event", G_CALLBACK(gtkScrollEventCallback), this); - - setPlatformWidget(scrollBar); - - /* - * assign a sane default width and height to the Scrollbar, otherwise - * we will end up with a 0 width scrollbar. - */ - resize(ScrollbarTheme::nativeTheme()->scrollbarThickness(), - ScrollbarTheme::nativeTheme()->scrollbarThickness()); -} - -// Create a ScrollbarGtk on top of an existing GtkAdjustment but do not create a -// GtkScrollbar on top of this adjustment. The goal is to have a WebCore::Scrollbar -// that will manipulate the GtkAdjustment properties, will react to the changed -// value but will not consume any space on the screen and will not be painted -// at all. It is achieved by not calling setPlatformWidget. -ScrollbarGtk::ScrollbarGtk(ScrollbarClient* client, ScrollbarOrientation orientation, GtkAdjustment* adjustment) - : Scrollbar(client, orientation, RegularScrollbar) - , m_adjustment(adjustment) -{ - if (m_adjustment) { - g_object_ref(m_adjustment); - g_signal_connect(m_adjustment, "value-changed", G_CALLBACK(ScrollbarGtk::gtkValueChanged), this); - } - - // We have nothing to show as we are solely operating on the GtkAdjustment - resize(0, 0); -} - -ScrollbarGtk::~ScrollbarGtk() -{ - if (m_adjustment) - detachAdjustment(); -} - -void ScrollbarGtk::attachAdjustment(GtkAdjustment* adjustment) -{ - if (platformWidget()) - return; - - if (m_adjustment) - detachAdjustment(); - - m_adjustment = adjustment; - - if (m_adjustment) { - g_object_ref(m_adjustment); - g_signal_connect(m_adjustment, "value-changed", G_CALLBACK(ScrollbarGtk::gtkValueChanged), this); - } - - updateThumbProportion(); - updateThumbPosition(); -} - -void ScrollbarGtk::detachAdjustment() -{ - if (!m_adjustment) - return; - - g_signal_handlers_disconnect_by_func(G_OBJECT(m_adjustment), (gpointer)ScrollbarGtk::gtkValueChanged, this); - - // For the case where we only operate on the GtkAdjustment it is best to - // reset the values so that the surrounding scrollbar gets updated, or - // e.g. for a GtkScrolledWindow the scrollbar gets hidden. - gtk_adjustment_configure(m_adjustment, 0, 0, 0, 0, 0, 0); - - g_object_unref(m_adjustment); - m_adjustment = 0; -} - -IntPoint ScrollbarGtk::getLocationInParentWindow(const IntRect& rect) -{ - IntPoint loc; - - if (parent()->isScrollViewScrollbar(this)) - loc = parent()->convertToContainingWindow(rect.location()); - else - loc = parent()->contentsToWindow(rect.location()); - - return loc; -} - -void ScrollbarGtk::frameRectsChanged() -{ - if (!parent() || !platformWidget()) - return; - - IntPoint loc = getLocationInParentWindow(frameRect()); - - // Don't allow the allocation size to be negative - IntSize sz = frameRect().size(); - sz.clampNegativeToZero(); - - GtkAllocation allocation = { loc.x(), loc.y(), sz.width(), sz.height() }; - gtk_widget_size_allocate(platformWidget(), &allocation); -} - -void ScrollbarGtk::updateThumbPosition() -{ - if (!m_adjustment) - return; - - if (gtk_adjustment_get_value(m_adjustment) != m_currentPos) - gtk_adjustment_set_value(m_adjustment, m_currentPos); -} - -void ScrollbarGtk::updateThumbProportion() -{ - if (!m_adjustment) - return; - - gtk_adjustment_configure(m_adjustment, - gtk_adjustment_get_value(m_adjustment), - gtk_adjustment_get_lower(m_adjustment), - m_totalSize, - m_lineStep, - m_pageStep, - m_visibleSize); -} - -void ScrollbarGtk::setFrameRect(const IntRect& rect) -{ - Widget::setFrameRect(rect); - frameRectsChanged(); -} - -void ScrollbarGtk::gtkValueChanged(GtkAdjustment*, ScrollbarGtk* that) -{ - that->setValue(static_cast<int>(gtk_adjustment_get_value(that->m_adjustment))); -} - -void ScrollbarGtk::setEnabled(bool shouldEnable) -{ - if (enabled() == shouldEnable) - return; - - Scrollbar::setEnabled(shouldEnable); - if (platformWidget()) - gtk_widget_set_sensitive(platformWidget(), shouldEnable); -} - -/* - * Strategy to painting a Widget: - * 1.) do not paint if there is no GtkWidget set - * 2.) We assume that the widget has no window and that frameRectsChanged positioned - * the widget correctly. ATM we do not honor the GraphicsContext translation. - */ -void ScrollbarGtk::paint(GraphicsContext* context, const IntRect& rect) -{ - if (!platformWidget()) - return; - - if (!context->gdkExposeEvent()) - return; - - GtkWidget* widget = platformWidget(); - ASSERT(!gtk_widget_get_has_window(widget)); - - GdkEvent* event = gdk_event_new(GDK_EXPOSE); - event->expose = *context->gdkExposeEvent(); - event->expose.area = static_cast<GdkRectangle>(rect); - - IntPoint loc = getLocationInParentWindow(rect); - - event->expose.area.x = loc.x(); - event->expose.area.y = loc.y(); - -#ifdef GTK_API_VERSION_2 - event->expose.region = gdk_region_rectangle(&event->expose.area); -#else - event->expose.region = cairo_region_create_rectangle(&event->expose.area); -#endif - - /* - * This will be unref'ed by gdk_event_free. - */ - g_object_ref(event->expose.window); - - /* - * If we are going to paint do the translation and GtkAllocation manipulation. - */ -#ifdef GTK_API_VERSION_2 - if (!gdk_region_empty(event->expose.region)) -#else - if (!cairo_region_is_empty(event->expose.region)) -#endif - gtk_widget_send_expose(widget, event); - - gdk_event_free(event); -} diff --git a/WebCore/platform/gtk/ScrollbarGtk.h b/WebCore/platform/gtk/ScrollbarGtk.h deleted file mode 100644 index 1a078c9..0000000 --- a/WebCore/platform/gtk/ScrollbarGtk.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2004, 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 - * 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 ScrollbarGtk_h -#define ScrollbarGtk_h - -#include "Scrollbar.h" -#include <wtf/PassRefPtr.h> - -namespace WebCore { - -class ScrollbarGtk : public Scrollbar { -public: - friend class Scrollbar; - friend class ScrollView; - ~ScrollbarGtk(); - - virtual void setFrameRect(const IntRect&); - virtual void paint(GraphicsContext*, const IntRect&); - - virtual bool handleMouseMoveEvent(const PlatformMouseEvent&) { return false; } - virtual bool handleMouseOutEvent(const PlatformMouseEvent&) { return false; } - virtual bool handleMousePressEvent(const PlatformMouseEvent&) { return false; } - virtual bool handleMouseReleaseEvent(const PlatformMouseEvent&) { return false; } - - virtual void setEnabled(bool); - - virtual void frameRectsChanged(); - -protected: - static PassRefPtr<ScrollbarGtk> createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, GtkAdjustment*); - - ScrollbarGtk(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize); - ScrollbarGtk(ScrollbarClient*, ScrollbarOrientation, GtkAdjustment*); - - virtual void updateThumbPosition(); - virtual void updateThumbProportion(); - - void detachAdjustment(); - void attachAdjustment(GtkAdjustment*); -private: - static void gtkValueChanged(GtkAdjustment*, ScrollbarGtk*); - IntPoint getLocationInParentWindow(const IntRect&); - GtkAdjustment* m_adjustment; -}; - -} - -#endif // ScrollbarGtk_h diff --git a/WebCore/platform/gtk/ScrollbarThemeGtk.cpp b/WebCore/platform/gtk/ScrollbarThemeGtk.cpp index b6efe54..2e942fe 100644 --- a/WebCore/platform/gtk/ScrollbarThemeGtk.cpp +++ b/WebCore/platform/gtk/ScrollbarThemeGtk.cpp @@ -82,6 +82,8 @@ void ScrollbarThemeGtk::updateThemeProperties() m_stepperSpacing = metrics.stepper_spacing; m_minThumbLength = metrics.min_slider_size; m_troughUnderSteppers = metrics.trough_under_steppers; + m_hasForwardButtonStartPart = metrics.has_secondary_forward_stepper; + m_hasBackButtonEndPart = metrics.has_secondary_backward_stepper; if (!gScrollbars) return; @@ -120,30 +122,45 @@ bool ScrollbarThemeGtk::hasThumb(Scrollbar* scrollbar) IntRect ScrollbarThemeGtk::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool) { - // We do not support multiple steppers per end yet. - if (part == BackButtonEndPart) + if (part == BackButtonEndPart && !m_hasBackButtonEndPart) return IntRect(); + int x = scrollbar->x() + m_troughBorderWidth; + int y = scrollbar->y() + m_troughBorderWidth; IntSize size = buttonSize(scrollbar); - return IntRect(scrollbar->x() + m_troughBorderWidth, scrollbar->y() + m_troughBorderWidth, size.width(), size.height()); + if (part == BackButtonStartPart) + return IntRect(x, y, size.width(), size.height()); + + // BackButtonEndPart (alternate button) + if (scrollbar->orientation() == HorizontalScrollbar) + return IntRect(scrollbar->x() + scrollbar->width() - m_troughBorderWidth - (2 * size.width()), y, size.width(), size.height()); + + // VerticalScrollbar alternate button + return IntRect(x, scrollbar->y() + scrollbar->height() - m_troughBorderWidth - (2 * size.height()), size.width(), size.height()); } IntRect ScrollbarThemeGtk::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool) { - // We do not support multiple steppers per end yet. - if (part == ForwardButtonStartPart) + if (part == ForwardButtonStartPart && !m_hasForwardButtonStartPart) return IntRect(); IntSize size = buttonSize(scrollbar); - int x, y; if (scrollbar->orientation() == HorizontalScrollbar) { - x = scrollbar->x() + scrollbar->width() - size.width() - m_troughBorderWidth; - y = scrollbar->y() + m_troughBorderWidth; - } else { - x = scrollbar->x() + m_troughBorderWidth; - y = scrollbar->y() + scrollbar->height() - size.height() - m_troughBorderWidth; + int y = scrollbar->y() + m_troughBorderWidth; + if (part == ForwardButtonEndPart) + return IntRect(scrollbar->x() + scrollbar->width() - size.width() - m_troughBorderWidth, y, size.width(), size.height()); + + // ForwardButtonStartPart (alternate button) + return IntRect(scrollbar->x() + m_troughBorderWidth + size.width(), y, size.width(), size.height()); } - return IntRect(x, y, size.width(), size.height()); + + // VerticalScrollbar + int x = scrollbar->x() + m_troughBorderWidth; + if (part == ForwardButtonEndPart) + return IntRect(x, scrollbar->y() + scrollbar->height() - size.height() - m_troughBorderWidth, size.width(), size.height()); + + // ForwardButtonStartPart (alternate button) + return IntRect(x, scrollbar->y() + m_troughBorderWidth + size.height(), size.width(), size.height()); } IntRect ScrollbarThemeGtk::trackRect(Scrollbar* scrollbar, bool) @@ -157,17 +174,28 @@ IntRect ScrollbarThemeGtk::trackRect(Scrollbar* scrollbar, bool) // The fatness of the scrollbar on the non-movement axis. int thickness = scrollbarThickness(scrollbar->controlSize()); + int alternateButtonOffset = 0; + int alternateButtonWidth = 0; + if (m_hasForwardButtonStartPart) { + alternateButtonOffset += m_stepperSize; + alternateButtonWidth += m_stepperSize; + } + if (m_hasBackButtonEndPart) + alternateButtonWidth += m_stepperSize; + if (scrollbar->orientation() == HorizontalScrollbar) { // Once the scrollbar becomes smaller than the natural size of the // two buttons, the track disappears. if (scrollbar->width() < 2 * thickness) return IntRect(); - return IntRect(scrollbar->x() + movementAxisPadding, scrollbar->y(), scrollbar->width() - (2 * movementAxisPadding), thickness); + return IntRect(scrollbar->x() + movementAxisPadding + alternateButtonOffset, scrollbar->y(), + scrollbar->width() - (2 * movementAxisPadding) - alternateButtonWidth, thickness); } if (scrollbar->height() < 2 * thickness) return IntRect(); - return IntRect(scrollbar->x(), scrollbar->y() + movementAxisPadding, thickness, scrollbar->height() - (2 * movementAxisPadding)); + return IntRect(scrollbar->x(), scrollbar->y() + movementAxisPadding + alternateButtonOffset, + thickness, scrollbar->height() - (2 * movementAxisPadding) - alternateButtonWidth); } void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) @@ -304,10 +332,10 @@ void ScrollbarThemeGtk::paintButton(GraphicsContext* context, Scrollbar* scrollb if (scrollbar->orientation() == VerticalScrollbar) flags |= MOZ_GTK_STEPPER_VERTICAL; - if (part == ForwardButtonEndPart) { + if (part == ForwardButtonEndPart) + flags |= (MOZ_GTK_STEPPER_DOWN | MOZ_GTK_STEPPER_BOTTOM); + if (part == ForwardButtonStartPart) flags |= MOZ_GTK_STEPPER_DOWN; - flags |= MOZ_GTK_STEPPER_BOTTOM; - } GtkWidgetState state; state.focused = TRUE; @@ -315,7 +343,9 @@ void ScrollbarThemeGtk::paintButton(GraphicsContext* context, Scrollbar* scrollb state.canDefault = TRUE; if ((BackButtonStartPart == part && scrollbar->currentPos()) - || (ForwardButtonEndPart == part && scrollbar->currentPos() != scrollbar->maximum())) { + || (BackButtonEndPart == part && scrollbar->currentPos()) + || (ForwardButtonEndPart == part && scrollbar->currentPos() != scrollbar->maximum()) + || (ForwardButtonStartPart == part && scrollbar->currentPos() != scrollbar->maximum())) { state.disabled = FALSE; state.active = part == scrollbar->pressedPart(); state.inHover = part == scrollbar->hoveredPart(); diff --git a/WebCore/platform/gtk/ScrollbarThemeGtk.h b/WebCore/platform/gtk/ScrollbarThemeGtk.h index eff2fee..8f990d5 100644 --- a/WebCore/platform/gtk/ScrollbarThemeGtk.h +++ b/WebCore/platform/gtk/ScrollbarThemeGtk.h @@ -68,6 +68,8 @@ protected: int m_stepperSpacing; int m_minThumbLength; bool m_troughUnderSteppers; + bool m_hasForwardButtonStartPart; + bool m_hasBackButtonEndPart; }; } diff --git a/WebCore/platform/gtk/gtk2drawing.c b/WebCore/platform/gtk/gtk2drawing.c index b33fb1f..ba69cdc 100644 --- a/WebCore/platform/gtk/gtk2drawing.c +++ b/WebCore/platform/gtk/gtk2drawing.c @@ -3079,6 +3079,8 @@ moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) "stepper_size", &metrics->stepper_size, "stepper_spacing", &metrics->stepper_spacing, "trough_under_steppers", &metrics->trough_under_steppers, + "has_secondary_forward_stepper", &metrics->has_secondary_forward_stepper, + "has_secondary_backward_stepper", &metrics->has_secondary_backward_stepper, NULL); metrics->min_slider_size = gtk_range_get_min_slider_size(GTK_RANGE(gParts->horizScrollbarWidget)); diff --git a/WebCore/platform/gtk/gtkdrawing.h b/WebCore/platform/gtk/gtkdrawing.h index 9d06d5d..c00da97 100644 --- a/WebCore/platform/gtk/gtkdrawing.h +++ b/WebCore/platform/gtk/gtkdrawing.h @@ -76,6 +76,8 @@ typedef struct { gint stepper_spacing; gint min_slider_size; gboolean trough_under_steppers; + gboolean has_secondary_forward_stepper; + gboolean has_secondary_backward_stepper; } MozGtkScrollbarMetrics; typedef struct _GtkThemeParts { diff --git a/WebCore/platform/network/FormData.cpp b/WebCore/platform/network/FormData.cpp index 786f1b6..406a375 100644 --- a/WebCore/platform/network/FormData.cpp +++ b/WebCore/platform/network/FormData.cpp @@ -226,7 +226,7 @@ void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncod FormDataBuilder::addFilenameToMultiPartHeader(header, encoding, name); // Add the content type if it is available. - if (value.blob()->type().isEmpty()) + if (!value.blob()->type().isEmpty()) FormDataBuilder::addContentTypeToMultiPartHeader(header, value.blob()->type().latin1()); } diff --git a/WebCore/platform/network/android/ResourceHandleAndroid.cpp b/WebCore/platform/network/android/ResourceHandleAndroid.cpp index 2cd1ba9..2a2ea74 100644 --- a/WebCore/platform/network/android/ResourceHandleAndroid.cpp +++ b/WebCore/platform/network/android/ResourceHandleAndroid.cpp @@ -27,7 +27,7 @@ #include "ResourceHandle.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" @@ -51,6 +51,7 @@ ResourceHandle::~ResourceHandle() bool ResourceHandle::start(Frame* frame) { +<<<<<<< HEAD DocumentLoader* docLoader = frame->loader()->activeDocumentLoader(); MainResourceLoader* mainLoader = docLoader->mainResourceLoader(); bool isMainResource = @@ -58,6 +59,11 @@ bool ResourceHandle::start(Frame* frame) bool isPrivateBrowsing = false; if (frame->settings()) isPrivateBrowsing = frame->settings()->privateBrowsingEnabled(); +======= + DocumentLoader* documentLoader = frame->loader()->activeDocumentLoader(); + MainResourceLoader* mainLoader = documentLoader->mainResourceLoader(); + bool isMainResource = mainLoader && (mainLoader->handle() == this); +>>>>>>> webkit.org at r67178 PassRefPtr<ResourceLoaderAndroid> loader = ResourceLoaderAndroid::start(this, d->m_firstRequest, frame->loader()->client(), isMainResource, false, isPrivateBrowsing); diff --git a/WebCore/platform/network/cf/ResourceHandleCFNet.cpp b/WebCore/platform/network/cf/ResourceHandleCFNet.cpp index 419e397..209906e 100644 --- a/WebCore/platform/network/cf/ResourceHandleCFNet.cpp +++ b/WebCore/platform/network/cf/ResourceHandleCFNet.cpp @@ -34,7 +34,7 @@ #include "Base64.h" #include "CookieStorageWin.h" #include "CredentialStorage.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "FormDataStreamCFNet.h" #include "Frame.h" #include "FrameLoader.h" diff --git a/WebCore/platform/network/curl/ResourceHandleCurl.cpp b/WebCore/platform/network/curl/ResourceHandleCurl.cpp index a4c1f8e..096905d 100644 --- a/WebCore/platform/network/curl/ResourceHandleCurl.cpp +++ b/WebCore/platform/network/curl/ResourceHandleCurl.cpp @@ -28,7 +28,7 @@ #include "config.h" #include "ResourceHandle.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "NotImplemented.h" #include "ResourceHandleInternal.h" #include "ResourceHandleManager.h" diff --git a/WebCore/platform/network/curl/ResourceHandleManager.cpp b/WebCore/platform/network/curl/ResourceHandleManager.cpp index ba68351..8d2a1bf 100644 --- a/WebCore/platform/network/curl/ResourceHandleManager.cpp +++ b/WebCore/platform/network/curl/ResourceHandleManager.cpp @@ -612,7 +612,8 @@ static void parseDataUrl(ResourceHandle* handle) // WebCore's decoder fails on Acid3 test 97 (whitespace). Vector<char> out; - if (base64Decode(data.latin1().data(), data.latin1().length(), out) && out.size() > 0) + CString latin1 = data.latin1(); + if (base64Decode(latin1.data(), latin1.length(), out) && out.size() > 0) client->didReceiveData(handle, out.data(), out.size(), 0); } else { // We have to convert to UTF-16 early due to limitations in KURL diff --git a/WebCore/platform/network/curl/ResourceRequest.h b/WebCore/platform/network/curl/ResourceRequest.h index 40e1e8f..12dc214 100644 --- a/WebCore/platform/network/curl/ResourceRequest.h +++ b/WebCore/platform/network/curl/ResourceRequest.h @@ -56,6 +56,11 @@ namespace WebCore { { } + ResourceRequest(CFURLRequestRef) + : ResourceRequestBase() + { + } + // Needed for compatibility. CFURLRequestRef cfURLRequest() const { return 0; } diff --git a/WebCore/platform/network/curl/ResourceResponse.h b/WebCore/platform/network/curl/ResourceResponse.h index 6195f61..860e902 100644 --- a/WebCore/platform/network/curl/ResourceResponse.h +++ b/WebCore/platform/network/curl/ResourceResponse.h @@ -28,6 +28,8 @@ #include "ResourceResponseBase.h" +typedef const struct _CFURLResponse* CFURLResponseRef; + namespace WebCore { class ResourceResponse : public ResourceResponseBase { @@ -46,6 +48,9 @@ public: void setResponseFired(bool fired) { m_responseFired = fired; } bool responseFired() { return m_responseFired; } + // Needed for compatibility. + CFURLResponseRef cfURLResponse() const { return 0; } + private: bool m_responseFired; }; diff --git a/WebCore/platform/network/mac/ResourceHandleMac.mm b/WebCore/platform/network/mac/ResourceHandleMac.mm index d014bb3..0af86d0 100644 --- a/WebCore/platform/network/mac/ResourceHandleMac.mm +++ b/WebCore/platform/network/mac/ResourceHandleMac.mm @@ -32,7 +32,7 @@ #import "BlobRegistry.h" #import "BlockExceptions.h" #import "CredentialStorage.h" -#import "DocLoader.h" +#import "CachedResourceLoader.h" #import "EmptyProtocolDefinitions.h" #import "FormDataStreamMac.h" #import "Frame.h" diff --git a/WebCore/platform/network/qt/ResourceHandleQt.cpp b/WebCore/platform/network/qt/ResourceHandleQt.cpp index f91eecb..3548467 100644 --- a/WebCore/platform/network/qt/ResourceHandleQt.cpp +++ b/WebCore/platform/network/qt/ResourceHandleQt.cpp @@ -31,7 +31,7 @@ #include "ResourceHandle.h" #include "ChromeClientQt.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Frame.h" #include "FrameLoaderClientQt.h" #include "NotImplemented.h" diff --git a/WebCore/platform/network/qt/ResourceRequestQt.cpp b/WebCore/platform/network/qt/ResourceRequestQt.cpp index 637c71f..4d576c7 100644 --- a/WebCore/platform/network/qt/ResourceRequestQt.cpp +++ b/WebCore/platform/network/qt/ResourceRequestQt.cpp @@ -64,6 +64,11 @@ QNetworkRequest ResourceRequest::toNetworkRequest(QObject* originatingFrame) con request.setRawHeader(name, ""); } + // Make sure we always have an Accept header; some sites require this to + // serve subresources + if (!request.hasRawHeader("Accept")) + request.setRawHeader("Accept", "*/*"); + switch (cachePolicy()) { case ReloadIgnoringCacheData: request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); diff --git a/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/WebCore/platform/network/soup/ResourceHandleSoup.cpp index 96a2f25..dadbd22 100644 --- a/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -31,7 +31,7 @@ #include "CString.h" #include "ChromeClient.h" #include "CookieJarSoup.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "FileSystem.h" #include "Frame.h" #include "GOwnPtrSoup.h" diff --git a/WebCore/platform/network/soup/SocketStreamHandle.h b/WebCore/platform/network/soup/SocketStreamHandle.h index 64139e5..2ba4504 100644 --- a/WebCore/platform/network/soup/SocketStreamHandle.h +++ b/WebCore/platform/network/soup/SocketStreamHandle.h @@ -32,8 +32,8 @@ #ifndef SocketStreamHandle_h #define SocketStreamHandle_h +#include "GRefPtr.h" #include "SocketStreamHandleBase.h" - #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> @@ -48,12 +48,21 @@ namespace WebCore { static PassRefPtr<SocketStreamHandle> create(const KURL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } virtual ~SocketStreamHandle(); + void connected(GSocketConnection*, GError*); + void readBytes(signed long, GError*); + void writeReady(); protected: virtual int platformSend(const char* data, int length); virtual void platformClose(); private: + PlatformRefPtr<GSocketConnection> m_socketConnection; + PlatformRefPtr<GInputStream> m_inputStream; + PlatformRefPtr<GOutputStream> m_outputStream; + PlatformRefPtr<GSource> m_writeReadySource; + char* m_readBuffer; + SocketStreamHandle(const KURL&, SocketStreamHandleClient*); // No authentication for streams per se, but proxy may ask for credentials. @@ -61,6 +70,8 @@ namespace WebCore { void receivedCredential(const AuthenticationChallenge&, const Credential&); void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); void receivedCancellation(const AuthenticationChallenge&); + void beginWaitingForSocketWritability(); + void stopWaitingForSocketWritability(); }; } // namespace WebCore diff --git a/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp b/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp index 6aa33fc..d73b499 100644 --- a/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp +++ b/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp @@ -31,38 +31,161 @@ #include "config.h" #include "SocketStreamHandle.h" +#include "CString.h" +#include "GOwnPtr.h" #include "KURL.h" #include "Logging.h" +#include "NotFound.h" #include "NotImplemented.h" +#include "SocketStreamError.h" #include "SocketStreamHandleClient.h" +#include "Vector.h" +#include <gio/gio.h> +#include <glib.h> + +#define READ_BUFFER_SIZE 1024 namespace WebCore { +// These functions immediately call the similarly named SocketStreamHandle methods. +static void connectedCallback(GSocketClient*, GAsyncResult*, SocketStreamHandle*); +static void readReadyCallback(GInputStream*, GAsyncResult*, SocketStreamHandle*); +static gboolean writeReadyCallback(GSocket*, GIOCondition, SocketStreamHandle*); + +// Having a list of active handles means that we do not have to worry about WebCore +// reference counting in GLib callbacks. Once the handle is off the active handles list +// we just ignore it in the callback. We avoid a lot of extra checks and tricky +// situations this way. +static Vector<SocketStreamHandle*> gActiveHandles; +bool isActiveHandle(SocketStreamHandle* handle) +{ + return gActiveHandles.find(handle) != notFound; +} + +void deactivateHandle(SocketStreamHandle* handle) +{ + size_t handleIndex = gActiveHandles.find(handle); + if (handleIndex != notFound) + gActiveHandles.remove(handleIndex); +} + SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client) : SocketStreamHandleBase(url, client) + , m_readBuffer(0) { - LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); - notImplemented(); + // No support for SSL sockets yet. + if (url.protocolIs("wss")) + return; + unsigned int port = url.hasPort() ? url.port() : 80; + + gActiveHandles.append(this); + PlatformRefPtr<GSocketClient> socketClient = adoptPlatformRef(g_socket_client_new()); + g_socket_client_connect_to_host_async(socketClient.get(), url.host().utf8().data(), port, 0, + reinterpret_cast<GAsyncReadyCallback>(connectedCallback), this); } SocketStreamHandle::~SocketStreamHandle() { - LOG(Network, "SocketStreamHandle %p delete", this); + // If for some reason we were destroyed without closing, ensure that we are deactivated. + deactivateHandle(this); setClient(0); - notImplemented(); } -int SocketStreamHandle::platformSend(const char*, int) +void SocketStreamHandle::connected(GSocketConnection* socketConnection, GError* error) { - LOG(Network, "SocketStreamHandle %p platformSend", this); - notImplemented(); - return 0; + if (error) { + m_client->didFail(this, SocketStreamError(error->code)); + return; + } + + m_socketConnection = adoptPlatformRef(socketConnection); + m_outputStream = g_io_stream_get_output_stream(G_IO_STREAM(m_socketConnection.get())); + m_inputStream = g_io_stream_get_input_stream(G_IO_STREAM(m_socketConnection.get())); + + m_readBuffer = new char[READ_BUFFER_SIZE]; + g_input_stream_read_async(m_inputStream.get(), m_readBuffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, 0, + reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), this); + + // The client can close the handle, potentially removing the last reference. + RefPtr<SocketStreamHandle> protect(this); + m_state = Open; + m_client->didOpen(this); + if (!m_socketConnection) // Client closed the connection. + return; +} + +void SocketStreamHandle::readBytes(signed long bytesRead, GError* error) +{ + if (error) { + m_client->didFail(this, SocketStreamError(error->code)); + return; + } + + if (!bytesRead) { + close(); + return; + } + + // The client can close the handle, potentially removing the last reference. + RefPtr<SocketStreamHandle> protect(this); + m_client->didReceiveData(this, m_readBuffer, bytesRead); + if (m_inputStream) // The client may have closed the connection. + g_input_stream_read_async(m_inputStream.get(), m_readBuffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, 0, + reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), this); +} + +void SocketStreamHandle::writeReady() +{ + // We no longer have buffered data, so stop waiting for the socket to be writable. + if (!bufferedAmount()) { + stopWaitingForSocketWritability(); + return; + } + + sendPendingData(); +} + +int SocketStreamHandle::platformSend(const char* data, int length) +{ + if (!g_socket_condition_check(g_socket_connection_get_socket(m_socketConnection.get()), G_IO_OUT)) { + beginWaitingForSocketWritability(); + return 0; + } + + GOwnPtr<GError> error; + gssize written = g_output_stream_write(m_outputStream.get(), data, length, 0, &error.outPtr()); + if (error) { + m_client->didFail(this, SocketStreamError(error->code)); // FIXME: Provide a sensible error. + return 0; + } + + // If we did not send all the bytes we were given, we know that + // SocketStreamHandleBase will need to send more in the future. + if (written < length) + beginWaitingForSocketWritability(); + + return written; } void SocketStreamHandle::platformClose() { - LOG(Network, "SocketStreamHandle %p platformClose", this); - notImplemented(); + // We remove this handle from the active handles list first, to disable all callbacks. + deactivateHandle(this); + stopWaitingForSocketWritability(); + + if (m_socketConnection) { + GOwnPtr<GError> error; + g_io_stream_close(G_IO_STREAM(m_socketConnection.get()), 0, &error.outPtr()); + if (error) + m_client->didFail(this, SocketStreamError(error->code)); // FIXME: Provide a sensible error. + m_socketConnection = 0; + } + + m_outputStream = 0; + m_inputStream = 0; + delete m_readBuffer; + + m_client->didClose(this); } void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&) @@ -85,4 +208,70 @@ void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&) notImplemented(); } +void SocketStreamHandle::beginWaitingForSocketWritability() +{ + if (m_writeReadySource) // Already waiting. + return; + + m_writeReadySource = adoptPlatformRef(g_socket_create_source( + g_socket_connection_get_socket(m_socketConnection.get()), static_cast<GIOCondition>(G_IO_OUT), 0)); + g_source_set_callback(m_writeReadySource.get(), reinterpret_cast<GSourceFunc>(writeReadyCallback), this, 0); + g_source_attach(m_writeReadySource.get(), 0); +} + +void SocketStreamHandle::stopWaitingForSocketWritability() +{ + if (!m_writeReadySource) // Not waiting. + return; + + g_source_remove(g_source_get_id(m_writeReadySource.get())); + m_writeReadySource = 0; +} + +static void connectedCallback(GSocketClient* client, GAsyncResult* result, SocketStreamHandle* handle) +{ + // Always finish the connection, even if this SocketStreamHandle was deactivated earlier. + GOwnPtr<GError> error; + GSocketConnection* socketConnection = g_socket_client_connect_to_host_finish(client, result, &error.outPtr()); + + // The SocketStreamHandle has been deactivated, so just close the connection, ignoring errors. + if (!isActiveHandle(handle)) { + g_io_stream_close(G_IO_STREAM(socketConnection), 0, &error.outPtr()); + return; + } + + handle->connected(socketConnection, error.get()); +} + +static void readReadyCallback(GInputStream* stream, GAsyncResult* result, SocketStreamHandle* handle) +{ + // Always finish the read, even if this SocketStreamHandle was deactivated earlier. + GOwnPtr<GError> error; + gssize bytesRead = g_input_stream_read_finish(stream, result, &error.outPtr()); + + if (!isActiveHandle(handle)) + return; + handle->readBytes(bytesRead, error.get()); +} + +static gboolean writeReadyCallback(GSocket*, GIOCondition condition, SocketStreamHandle* handle) +{ + if (!isActiveHandle(handle)) + return FALSE; + + // G_IO_HUP and G_IO_ERR are are always active. See: + // http://library.gnome.org/devel/gio/stable/GSocket.html#g-socket-create-source + if (condition & G_IO_HUP) { + handle->close(); + return FALSE; + } + if (condition & G_IO_ERR) { + handle->client()->didFail(handle, SocketStreamError(0)); // FIXME: Provide a sensible error. + return FALSE; + } + if (condition & G_IO_OUT) + handle->writeReady(); + return TRUE; +} + } // namespace WebCore diff --git a/WebCore/platform/network/win/ResourceHandleWin.cpp b/WebCore/platform/network/win/ResourceHandleWin.cpp index 3dabd91..2af03c0 100644 --- a/WebCore/platform/network/win/ResourceHandleWin.cpp +++ b/WebCore/platform/network/win/ResourceHandleWin.cpp @@ -27,7 +27,7 @@ #include "config.h" #include "ResourceHandle.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Document.h" #include "Frame.h" #include "FrameLoader.h" diff --git a/WebCore/platform/qt/ClipboardQt.cpp b/WebCore/platform/qt/ClipboardQt.cpp index f677d28..6cbde0c 100644 --- a/WebCore/platform/qt/ClipboardQt.cpp +++ b/WebCore/platform/qt/ClipboardQt.cpp @@ -315,7 +315,7 @@ void ClipboardQt::writeRange(Range* range, Frame* frame) if (!m_writableData) m_writableData = new QMimeData; - QString text = frame->selectedText(); + QString text = frame->editor()->selectedText(); text.replace(QChar(0xa0), QLatin1Char(' ')); m_writableData->setText(text); m_writableData->setHtml(createMarkup(range, 0, AnnotateForInterchange, false, AbsoluteURLs)); diff --git a/WebCore/platform/qt/PasteboardQt.cpp b/WebCore/platform/qt/PasteboardQt.cpp index fc53124..ac54fdb 100644 --- a/WebCore/platform/qt/PasteboardQt.cpp +++ b/WebCore/platform/qt/PasteboardQt.cpp @@ -61,7 +61,7 @@ Pasteboard* Pasteboard::generalPasteboard() void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame) { QMimeData* md = new QMimeData; - QString text = frame->selectedText(); + QString text = frame->editor()->selectedText(); text.replace(QChar(0xa0), QLatin1Char(' ')); md->setText(text); diff --git a/WebCore/platform/qt/ScrollbarQt.cpp b/WebCore/platform/qt/ScrollbarQt.cpp index 85dc107..a517064 100644 --- a/WebCore/platform/qt/ScrollbarQt.cpp +++ b/WebCore/platform/qt/ScrollbarQt.cpp @@ -76,11 +76,11 @@ bool Scrollbar::contextMenu(const PlatformMouseEvent& event) const QPoint pos = convertFromContainingWindow(event.pos()); moveThumb(horizontal ? pos.x() : pos.y()); } else if (actionSelected == actScrollTop) - setValue(0); + scroll(horizontal ? ScrollLeft : ScrollUp, ScrollByDocument); else if (actionSelected == actScrollBottom) - setValue(maximum()); + scroll(horizontal ? ScrollRight : ScrollDown, ScrollByDocument); else if (actionSelected == actPageUp) - scroll(horizontal ? ScrollLeft: ScrollUp, ScrollByPage); + scroll(horizontal ? ScrollLeft : ScrollUp, ScrollByPage); else if (actionSelected == actPageDown) scroll(horizontal ? ScrollRight : ScrollDown, ScrollByPage); else if (actionSelected == actScrollUp) diff --git a/WebCore/platform/text/CharacterNames.h b/WebCore/platform/text/CharacterNames.h index f06246c..9f8f807 100644 --- a/WebCore/platform/text/CharacterNames.h +++ b/WebCore/platform/text/CharacterNames.h @@ -52,6 +52,7 @@ const UChar leftSingleQuotationMark = 0x2018; const UChar leftToRightEmbed = 0x202A; const UChar leftToRightMark = 0x200E; const UChar leftToRightOverride = 0x202D; +const UChar minusSign = 0x2212; const UChar newlineCharacter = 0x000A; const UChar noBreakSpace = 0x00A0; const UChar objectReplacementCharacter = 0xFFFC; diff --git a/WebCore/platform/text/wince/TextCodecWinCE.cpp b/WebCore/platform/text/wince/TextCodecWinCE.cpp index 578f789..2789148 100644 --- a/WebCore/platform/text/wince/TextCodecWinCE.cpp +++ b/WebCore/platform/text/wince/TextCodecWinCE.cpp @@ -24,7 +24,6 @@ #include "config.h" #include "TextCodecWinCE.h" -#include "ce_textcodecs.h" #include "FontCache.h" #include "PlatformString.h" #include <mlang.h> @@ -43,7 +42,6 @@ struct CharsetInfo { String m_friendlyName; UINT m_codePage; Vector<CString> m_aliases; - bool m_usesNativeCodec; }; class LanguageManager { @@ -82,45 +80,8 @@ static LanguageManager& languageManager() return lm; } -static void addCharset(UINT codePage, const char* charsetName, const wchar_t* friendlyName, const char* charsetAliases, bool nativeSupport = false) -{ - CharsetInfo info; - info.m_codePage = codePage; - info.m_name = charsetName; - info.m_friendlyName = friendlyName; - info.m_usesNativeCodec = nativeSupport; - const char* begin = charsetAliases; - for (;;) { - const char* end = strchr(begin, '|'); - CString alias = end ? CString(begin, end - begin) : begin; - if (alias.length()) - info.m_aliases.append(alias); - if (!end) - break; - begin = end + 1; - } - knownCharsets().set(info.m_name.data(), info); - if (codePage != CP_ACP) - codePageCharsets().set(codePage, info.m_name); -} - LanguageManager::LanguageManager() { - // 437, 708, 709, 710, 720, 737, 775, 850, 852 - addCharset(932, "SHIFT_JIS", L"Japanese (SHIFT_JIS)", "shift_jis"); - addCharset(936, "GBK", L"Chinese Simplified (GBK)", "gbk|gb2312"); - addCharset(949, "KSC5601", L"Korean (KSC5601)", "ks_c_5601-1987|ksc5601|euc-kr|euckr|x-euc-kr"); - addCharset(950, "BIG5", L"Chinese Traditional (BIG5)", "big5"); - addCharset(1361, "JOHAB", L"Korean (Johab)", "johab|korean.johab"); - addCharset(51932, "EUC-JP", L"Japanese (EUC)", "euc-jp|eucjp|x-euc-jp", true); - addCharset(874, "CP874", L"Thai (Windows)", "cp874|windows-874", true); - addCharset(CP_ACP, "TIS620", L"Thai (TIS 620)", "tis620|ISO-8859-11|ISO-IR-166|TIS-620|TIS620-0TIS620.2529-1|TIS620.2533-0|TIS620.2533-1|thai8", true); - addCharset(CP_ACP, "MACTHAI", L"Thai (Mac OS)", "macthai|x-mac-thai|mac-thai", true); - supportedCharsets().add("EUC-JP"); - supportedCharsets().add("CP874"); - supportedCharsets().add("TIS620"); - supportedCharsets().add("MACTHAI"); - IEnumCodePage* enumInterface; IMultiLanguage* mli = FontCache::getMultiLanguageInterface(); if (mli && S_OK == mli->EnumCodePages(MIMECONTF_BROWSER, &enumInterface)) { @@ -148,7 +109,6 @@ LanguageManager::LanguageManager() info.m_aliases.append(name); info.m_aliases.append(String(cpInfo.wszHeaderCharset).latin1()); info.m_aliases.append(String(cpInfo.wszBodyCharset).latin1()); - info.m_usesNativeCodec = false; String cpName = String::format("cp%d", cpInfo.uiCodePage); info.m_aliases.append(cpName.latin1()); supportedCharsets().add(i->second.data()); @@ -265,52 +225,6 @@ static void decode(Vector<UChar, 8192>& result, const char* encodingName, const codePage = CP_UTF8; else codePage = CP_ACP; - } else { - codePage = i->second.m_codePage; - if (i->second.m_usesNativeCodec) { - typedef int (*FuncEucMbToWc)(wchar_t *pwc, const unsigned char *s, int n); - FuncEucMbToWc encMbToWc = 0; - if (!strcmp(encodingName, "EUC-JP")) - encMbToWc = TextCodecsCE::euc_jp_mbtowc; - else if (!strcmp(encodingName, "CP874")) - encMbToWc = TextCodecsCE::cp874_mbtowc; - else if (!strcmp(encodingName, "TIS620")) - encMbToWc = TextCodecsCE::tis620_mbtowc; - else if (!strcmp(encodingName, "MACTHAI")) - encMbToWc = TextCodecsCE::mac_thai_mbtowc; - - if (encMbToWc) { - const char* const srcStart = bytes; - const char* const srcEnd = bytes + length; - int lastSize = result.size(); - result.resize(lastSize + length); - for (;;) { - UChar* dst = result.data() + lastSize; - const UChar* const dstEnd = result.data() + result.size(); - for (; dst < dstEnd && bytes < srcEnd; ++dst) { - int numberEncoded = encMbToWc(dst, (const unsigned char*)bytes, srcEnd - bytes); - if (numberEncoded >= 0) - bytes += numberEncoded; - else { - if (numberEncoded == RET_ILSEQ) - sawInvalidChar = true; - break; - } - } - if (bytes == srcEnd || dst != dstEnd) { - *left = srcEnd - bytes; - result.resize(dst - result.data()); - return; - } - lastSize = result.size(); - result.resize(result.size() + 256); - } - } else { - *left = 0; - result.append(bytes, length); - return; - } - } } DWORD flags = getCodePageFlags(codePage); diff --git a/WebCore/platform/win/ClipboardWin.cpp b/WebCore/platform/win/ClipboardWin.cpp index 2915f9d..6d9930c 100644 --- a/WebCore/platform/win/ClipboardWin.cpp +++ b/WebCore/platform/win/ClipboardWin.cpp @@ -776,7 +776,7 @@ void ClipboardWin::writeRange(Range* selectedRange, Frame* frame) if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE))) ::GlobalFree(medium.hGlobal); - String str = frame->selectedText(); + String str = frame->editor()->selectedText(); replaceNewlinesWithWindowsStyleNewlines(str); replaceNBSPWithSpace(str); medium.hGlobal = createGlobalData(str); diff --git a/WebCore/platform/win/PasteboardWin.cpp b/WebCore/platform/win/PasteboardWin.cpp index c065f04..4b20adb 100644 --- a/WebCore/platform/win/PasteboardWin.cpp +++ b/WebCore/platform/win/PasteboardWin.cpp @@ -131,7 +131,7 @@ void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, } // Put plain string on the pasteboard. CF_UNICODETEXT covers CF_TEXT as well - String str = frame->selectedText(); + String str = frame->editor()->selectedText(); replaceNewlinesWithWindowsStyleNewlines(str); replaceNBSPWithSpace(str); if (::OpenClipboard(m_owner)) { diff --git a/WebCore/platform/win/PopupMenuWin.cpp b/WebCore/platform/win/PopupMenuWin.cpp index 6e22024..a782b03 100644 --- a/WebCore/platform/win/PopupMenuWin.cpp +++ b/WebCore/platform/win/PopupMenuWin.cpp @@ -531,12 +531,12 @@ bool PopupMenuWin::scrollToRevealSelection() int index = focusedIndex(); if (index < m_scrollOffset) { - m_scrollbar->setValue(index); + m_scrollbar->setValue(index, Scrollbar::NotFromScrollAnimator); return true; } if (index >= m_scrollOffset + visibleItems()) { - m_scrollbar->setValue(index - visibleItems() + 1); + m_scrollbar->setValue(index - visibleItems() + 1, Scrollbar::NotFromScrollAnimator); return true; } @@ -664,6 +664,17 @@ void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc) ::ReleaseDC(m_popup, localDC); } +int PopupMenuWin::scrollSize(ScrollbarOrientation orientation) const +{ + return ((orientation == VerticalScrollbar) && m_scrollbar) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0; +} + +void PopupMenuWin::setScrollOffsetFromAnimation(const IntPoint& offset) +{ + if (m_scrollbar) + m_scrollbar->setValue(offset.y(), Scrollbar::FromScrollAnimator); +} + void PopupMenuWin::valueChanged(Scrollbar* scrollBar) { ASSERT(m_scrollbar); diff --git a/WebCore/platform/win/PopupMenuWin.h b/WebCore/platform/win/PopupMenuWin.h index d4a4255..bfec7aa 100644 --- a/WebCore/platform/win/PopupMenuWin.h +++ b/WebCore/platform/win/PopupMenuWin.h @@ -91,6 +91,8 @@ private: void setScrollbarCapturingMouse(bool b) { m_scrollbarCapturingMouse = b; } // ScrollBarClient + virtual int scrollSize(ScrollbarOrientation orientation) const; + virtual void setScrollOffsetFromAnimation(const IntPoint&); virtual void valueChanged(Scrollbar*); virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&); virtual bool isActive() const { return true; } diff --git a/WebCore/platform/win/ScrollbarThemeWin.cpp b/WebCore/platform/win/ScrollbarThemeWin.cpp index a8d374a..ff3aaa4 100644 --- a/WebCore/platform/win/ScrollbarThemeWin.cpp +++ b/WebCore/platform/win/ScrollbarThemeWin.cpp @@ -27,6 +27,7 @@ #include "ScrollbarThemeWin.h" #include "GraphicsContext.h" +#include "LocalWindowsContext.h" #include "PlatformMouseEvent.h" #include "Scrollbar.h" #include "SoftLinking.h" @@ -240,14 +241,17 @@ void ScrollbarThemeWin::paintTrackPiece(GraphicsContext* context, Scrollbar* scr bool alphaBlend = false; if (scrollbarTheme) alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, part, state); - HDC hdc = context->getWindowsContext(rect, alphaBlend); + + LocalWindowsContext windowsContext(context, rect, alphaBlend); RECT themeRect(rect); + if (scrollbarTheme) - DrawThemeBackground(scrollbarTheme, hdc, part, state, &themeRect, 0); + DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), part, state, &themeRect, 0); else { DWORD color3DFace = ::GetSysColor(COLOR_3DFACE); DWORD colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR); DWORD colorWindow = ::GetSysColor(COLOR_WINDOW); + HDC hdc = windowsContext.hdc(); if ((color3DFace != colorScrollbar) && (colorWindow != colorScrollbar)) ::FillRect(hdc, &themeRect, HBRUSH(COLOR_SCROLLBAR+1)); else { @@ -265,7 +269,6 @@ void ScrollbarThemeWin::paintTrackPiece(GraphicsContext* context, Scrollbar* scr ::DeleteObject(patternBitmap); } } - context->releaseWindowsContext(hdc, rect, alphaBlend); } void ScrollbarThemeWin::paintButton(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part) @@ -308,14 +311,13 @@ void ScrollbarThemeWin::paintButton(GraphicsContext* context, Scrollbar* scrollb bool alphaBlend = false; if (scrollbarTheme) alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, SP_BUTTON, xpState); - HDC hdc = context->getWindowsContext(rect, alphaBlend); + LocalWindowsContext windowsContext(context, rect, alphaBlend); RECT themeRect(rect); if (scrollbarTheme) - DrawThemeBackground(scrollbarTheme, hdc, SP_BUTTON, xpState, &themeRect, 0); + DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), SP_BUTTON, xpState, &themeRect, 0); else - ::DrawFrameControl(hdc, &themeRect, DFC_SCROLL, classicState); - context->releaseWindowsContext(hdc, rect, alphaBlend); + ::DrawFrameControl(windowsContext.hdc(), &themeRect, DFC_SCROLL, classicState); } static IntRect gripperRect(int thickness, const IntRect& thumbRect) |