summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-09 16:25:46 +0100
committerSteve Block <steveblock@google.com>2011-05-12 14:28:33 +0100
commit31dbc523d9ee6fd7d7e46c540b5f675eeb559ed7 (patch)
tree3e73099a4bf4c6849c6f448aa3aba9fe408998f8 /Source/WebCore/platform/graphics
parentcad810f21b803229eb11403f9209855525a25d57 (diff)
downloadexternal_webkit-31dbc523d9ee6fd7d7e46c540b5f675eeb559ed7.zip
external_webkit-31dbc523d9ee6fd7d7e46c540b5f675eeb559ed7.tar.gz
external_webkit-31dbc523d9ee6fd7d7e46c540b5f675eeb559ed7.tar.bz2
Merge WebKit at r75315: Move Android-specific WebCore files to Source
This moves files in the following WebCore subdirectories ... - bindings/js - bindings/v8/custom - plugins/android - platform/android - platform/graphics/android - page/ - css/ - dom/ - loader/archive/android
Diffstat (limited to 'Source/WebCore/platform/graphics')
-rw-r--r--Source/WebCore/platform/graphics/android/AndroidAnimation.cpp387
-rw-r--r--Source/WebCore/platform/graphics/android/AndroidAnimation.h107
-rw-r--r--Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp293
-rw-r--r--Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h146
-rw-r--r--Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp381
-rw-r--r--Source/WebCore/platform/graphics/android/BaseLayerAndroid.h76
-rw-r--r--Source/WebCore/platform/graphics/android/BaseTile.cpp490
-rw-r--r--Source/WebCore/platform/graphics/android/BaseTile.h151
-rw-r--r--Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.cpp71
-rw-r--r--Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.h58
-rw-r--r--Source/WebCore/platform/graphics/android/ClassTracker.cpp74
-rw-r--r--Source/WebCore/platform/graphics/android/ClassTracker.h51
-rw-r--r--Source/WebCore/platform/graphics/android/DeleteTextureOperation.h61
-rw-r--r--Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp180
-rw-r--r--Source/WebCore/platform/graphics/android/DoubleBufferedTexture.h73
-rw-r--r--Source/WebCore/platform/graphics/android/FontAndroid.cpp1043
-rw-r--r--Source/WebCore/platform/graphics/android/FontCacheAndroid.cpp180
-rw-r--r--Source/WebCore/platform/graphics/android/FontCustomPlatformData.cpp89
-rw-r--r--Source/WebCore/platform/graphics/android/FontCustomPlatformData.h59
-rw-r--r--Source/WebCore/platform/graphics/android/FontDataAndroid.cpp133
-rw-r--r--Source/WebCore/platform/graphics/android/FontPlatformData.h123
-rw-r--r--Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp235
-rw-r--r--Source/WebCore/platform/graphics/android/GLUtils.cpp424
-rw-r--r--Source/WebCore/platform/graphics/android/GLUtils.h74
-rw-r--r--Source/WebCore/platform/graphics/android/GLWebViewState.cpp574
-rw-r--r--Source/WebCore/platform/graphics/android/GLWebViewState.h300
-rw-r--r--Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp86
-rw-r--r--Source/WebCore/platform/graphics/android/GradientAndroid.cpp128
-rw-r--r--Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp1334
-rw-r--r--Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp989
-rw-r--r--Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h163
-rw-r--r--Source/WebCore/platform/graphics/android/HarfbuzzSkia.cpp220
-rw-r--r--Source/WebCore/platform/graphics/android/HarfbuzzSkia.h40
-rw-r--r--Source/WebCore/platform/graphics/android/ImageAndroid.cpp369
-rw-r--r--Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp253
-rw-r--r--Source/WebCore/platform/graphics/android/ImageBufferData.h40
-rw-r--r--Source/WebCore/platform/graphics/android/ImageSourceAndroid.cpp505
-rw-r--r--Source/WebCore/platform/graphics/android/LayerAndroid.cpp1402
-rw-r--r--Source/WebCore/platform/graphics/android/LayerAndroid.h384
-rw-r--r--Source/WebCore/platform/graphics/android/LayerTexture.cpp71
-rw-r--r--Source/WebCore/platform/graphics/android/LayerTexture.h76
-rw-r--r--Source/WebCore/platform/graphics/android/MediaLayer.cpp152
-rw-r--r--Source/WebCore/platform/graphics/android/MediaLayer.h77
-rw-r--r--Source/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h142
-rw-r--r--Source/WebCore/platform/graphics/android/MediaTexture.cpp274
-rw-r--r--Source/WebCore/platform/graphics/android/MediaTexture.h90
-rw-r--r--Source/WebCore/platform/graphics/android/PaintLayerOperation.cpp78
-rw-r--r--Source/WebCore/platform/graphics/android/PaintLayerOperation.h74
-rw-r--r--Source/WebCore/platform/graphics/android/PaintTileOperation.cpp81
-rw-r--r--Source/WebCore/platform/graphics/android/PaintTileOperation.h47
-rw-r--r--Source/WebCore/platform/graphics/android/PathAndroid.cpp341
-rw-r--r--Source/WebCore/platform/graphics/android/PatternAndroid.cpp70
-rw-r--r--Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp74
-rw-r--r--Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h165
-rw-r--r--Source/WebCore/platform/graphics/android/QueuedOperation.h85
-rw-r--r--Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp39
-rw-r--r--Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h68
-rw-r--r--Source/WebCore/platform/graphics/android/ShaderProgram.cpp446
-rw-r--r--Source/WebCore/platform/graphics/android/ShaderProgram.h107
-rw-r--r--Source/WebCore/platform/graphics/android/SharedBufferStream.cpp53
-rw-r--r--Source/WebCore/platform/graphics/android/SharedBufferStream.h55
-rw-r--r--Source/WebCore/platform/graphics/android/SharedTexture.cpp221
-rw-r--r--Source/WebCore/platform/graphics/android/SharedTexture.h137
-rw-r--r--Source/WebCore/platform/graphics/android/SkBitmapRef.h62
-rw-r--r--Source/WebCore/platform/graphics/android/TextureOwner.h43
-rw-r--r--Source/WebCore/platform/graphics/android/TexturesGenerator.cpp201
-rw-r--r--Source/WebCore/platform/graphics/android/TexturesGenerator.h73
-rw-r--r--Source/WebCore/platform/graphics/android/TiledPage.cpp355
-rw-r--r--Source/WebCore/platform/graphics/android/TiledPage.h114
-rw-r--r--Source/WebCore/platform/graphics/android/TilesManager.cpp440
-rw-r--r--Source/WebCore/platform/graphics/android/TilesManager.h162
-rw-r--r--Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp220
-rw-r--r--Source/WebCore/platform/graphics/android/VideoLayerAndroid.h96
-rw-r--r--Source/WebCore/platform/graphics/android/VideoListener.h92
-rw-r--r--Source/WebCore/platform/graphics/android/android_graphics.cpp176
-rw-r--r--Source/WebCore/platform/graphics/android/android_graphics.h76
76 files changed, 16879 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/android/AndroidAnimation.cpp b/Source/WebCore/platform/graphics/android/AndroidAnimation.cpp
new file mode 100644
index 0000000..1eb7f39
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/AndroidAnimation.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config.h"
+#include "AndroidAnimation.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "Animation.h"
+#include "GraphicsLayerAndroid.h"
+
+#include "Timer.h"
+#include "TimingFunction.h"
+#include "TranslateTransformOperation.h"
+#include "UnitBezier.h"
+
+#include <wtf/CurrentTime.h>
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "AndroidAnimation", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+
+namespace WebCore {
+
+static long gDebugAndroidAnimationInstances;
+
+long AndroidAnimation::instancesCount()
+{
+ return gDebugAndroidAnimationInstances;
+}
+
+AndroidAnimation::AndroidAnimation(AnimatedPropertyID type,
+ const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime)
+ : m_beginTime(beginTime)
+ , m_duration(animation->duration())
+ , m_finished(false)
+ , m_iterationCount(animation->iterationCount())
+ , m_direction(animation->direction())
+ , m_timingFunction(animation->timingFunction())
+ , m_type(type)
+ , m_operations(operations)
+ , m_originalLayer(0)
+{
+ ASSERT(m_timingFunction);
+
+ if (!static_cast<int>(beginTime)) // time not set
+ m_beginTime = WTF::currentTime();
+
+ gDebugAndroidAnimationInstances++;
+}
+
+AndroidAnimation::AndroidAnimation(AndroidAnimation* anim)
+ : m_beginTime(anim->m_beginTime)
+ , m_duration(anim->m_duration)
+ , m_finished(anim->m_finished)
+ , m_iterationCount(anim->m_iterationCount)
+ , m_direction(anim->m_direction)
+ , m_timingFunction(anim->m_timingFunction)
+ , m_type(anim->m_type)
+ , m_operations(anim->m_operations)
+ , m_originalLayer(0)
+{
+ gDebugAndroidAnimationInstances++;
+}
+
+AndroidAnimation::~AndroidAnimation()
+{
+ gDebugAndroidAnimationInstances--;
+}
+
+double AndroidAnimation::elapsedTime(double time)
+{
+ if (m_beginTime <= 0.000001) // overflow or not correctly set
+ m_beginTime = time;
+
+ m_elapsedTime = time - m_beginTime;
+
+ if (m_duration <= 0)
+ m_duration = 0.000001;
+
+ if (m_elapsedTime < 0) // animation not yet started.
+ return 0;
+
+ return m_elapsedTime;
+}
+
+bool AndroidAnimation::checkIterationsAndProgress(double time, float* finalProgress)
+{
+ double progress = elapsedTime(time);
+ double dur = m_duration;
+ if (m_iterationCount > 0)
+ dur *= m_iterationCount;
+
+ if (m_duration <= 0)
+ return false;
+
+ // If not infinite, return false if we are done
+ if (m_iterationCount > 0 && progress > dur) {
+ *finalProgress = 1.0;
+ return false;
+ }
+
+ double fractionalTime = progress / m_duration;
+ int integralTime = static_cast<int>(fractionalTime);
+
+ fractionalTime -= integralTime;
+
+ if ((m_direction == Animation::AnimationDirectionAlternate) && (integralTime & 1))
+ fractionalTime = 1 - fractionalTime;
+
+ *finalProgress = fractionalTime;
+ return true;
+}
+
+double AndroidAnimation::applyTimingFunction(float from, float to, double progress,
+ const TimingFunction* tf)
+{
+ double fractionalTime = progress;
+ double offset = from;
+ double scale = 1.0 / (to - from);
+
+ if (scale != 1 || offset)
+ fractionalTime = (fractionalTime - offset) * scale;
+
+ const TimingFunction* timingFunction = tf;
+
+ if (!timingFunction)
+ timingFunction = m_timingFunction.get();
+
+ if (timingFunction && timingFunction->isCubicBezierTimingFunction()) {
+ const CubicBezierTimingFunction* bezierFunction = static_cast<const CubicBezierTimingFunction*>(timingFunction);
+ UnitBezier bezier(bezierFunction->x1(),
+ bezierFunction->y1(),
+ bezierFunction->x2(),
+ bezierFunction->y2());
+ if (m_duration > 0)
+ fractionalTime = bezier.solve(fractionalTime, 1.0f / (200.0f * m_duration));
+ } else if (timingFunction && timingFunction->isStepsTimingFunction()) {
+ const StepsTimingFunction* stepFunction = static_cast<const StepsTimingFunction*>(timingFunction);
+ if (stepFunction->stepAtStart()) {
+ fractionalTime = (floor(stepFunction->numberOfSteps() * fractionalTime) + 1) / stepFunction->numberOfSteps();
+ if (fractionalTime > 1.0)
+ fractionalTime = 1.0;
+ } else {
+ fractionalTime = floor(stepFunction->numberOfSteps() * fractionalTime) / stepFunction->numberOfSteps();
+ }
+ }
+ return fractionalTime;
+}
+
+PassRefPtr<AndroidOpacityAnimation> AndroidOpacityAnimation::create(
+ const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime)
+{
+ return adoptRef(new AndroidOpacityAnimation(animation, operations,
+ beginTime));
+}
+
+AndroidOpacityAnimation::AndroidOpacityAnimation(const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime)
+ : AndroidAnimation(AnimatedPropertyOpacity, animation, operations, beginTime)
+{
+}
+
+AndroidOpacityAnimation::AndroidOpacityAnimation(AndroidOpacityAnimation* anim)
+ : AndroidAnimation(anim)
+{
+}
+
+PassRefPtr<AndroidAnimation> AndroidOpacityAnimation::copy()
+{
+ return adoptRef(new AndroidOpacityAnimation(this));
+}
+
+void AndroidAnimation::pickValues(double progress, int* start, int* end)
+{
+ float distance = -1;
+ unsigned int foundAt = 0;
+ for (unsigned int i = 0; i < m_operations->size(); i++) {
+ const AnimationValue* value = m_operations->at(i);
+ float key = value->keyTime();
+ float d = progress - key;
+ if (distance == -1 || (d >= 0 && d < distance && i + 1 < m_operations->size())) {
+ distance = d;
+ foundAt = i;
+ }
+ }
+
+ *start = foundAt;
+
+ if (foundAt + 1 < m_operations->size())
+ *end = foundAt + 1;
+ else
+ *end = foundAt;
+}
+
+bool AndroidOpacityAnimation::evaluate(LayerAndroid* layer, double time)
+{
+ float progress;
+ if (!checkIterationsAndProgress(time, &progress))
+ return false;
+
+ if (progress < 0) // we still want to be evaluated until we get progress > 0
+ return true;
+
+ if (progress >= 1) {
+ m_finished = true;
+ if (layer != m_originalLayer)
+ return false;
+ }
+
+ if (!m_originalLayer)
+ m_originalLayer = layer;
+
+ // First, we need to get the from and to values
+
+ int from, to;
+ pickValues(progress, &from, &to);
+ FloatAnimationValue* fromValue = (FloatAnimationValue*) m_operations->at(from);
+ FloatAnimationValue* toValue = (FloatAnimationValue*) m_operations->at(to);
+
+ XLOG("[layer %d] opacity fromValue %x, key %.2f, toValue %x, key %.2f for progress %.2f",
+ layer->uniqueId(),
+ fromValue, fromValue->keyTime(),
+ toValue, toValue->keyTime(), progress);
+
+ // We now have the correct two values to work with, let's compute the
+ // progress value
+
+ const TimingFunction* timingFunction = fromValue->timingFunction();
+ progress = applyTimingFunction(fromValue->keyTime(), toValue->keyTime(),
+ progress, timingFunction);
+ float value = fromValue->value() + ((toValue->value() - fromValue->value()) * progress);
+
+ layer->setOpacity(value);
+
+ XLOG("AndroidOpacityAnimation::evaluate(%p, %p, %.2f) value=%.6f", this, layer, time, value);
+
+ return true;
+}
+
+PassRefPtr<AndroidTransformAnimation> AndroidTransformAnimation::create(
+ const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime)
+{
+ return adoptRef(new AndroidTransformAnimation(animation, operations, beginTime));
+}
+
+AndroidTransformAnimation::AndroidTransformAnimation(const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime)
+ : AndroidAnimation(AnimatedPropertyWebkitTransform, animation, operations, beginTime)
+{
+}
+
+AndroidTransformAnimation::AndroidTransformAnimation(AndroidTransformAnimation* anim)
+ : AndroidAnimation(anim)
+{
+}
+
+PassRefPtr<AndroidAnimation> AndroidTransformAnimation::copy()
+{
+ return adoptRef(new AndroidTransformAnimation(this));
+}
+
+bool AndroidTransformAnimation::evaluate(LayerAndroid* layer, double time)
+{
+ float progress;
+ bool ret = true;
+ if (!checkIterationsAndProgress(time, &progress)) {
+ m_finished = true;
+ ret = false;
+ }
+
+ if (progress < 0) // we still want to be evaluated until we get progress > 0
+ return true;
+
+ if (progress >= 1) {
+ m_finished = true;
+ if (layer != m_originalLayer)
+ return false;
+ }
+
+ if (!m_originalLayer)
+ m_originalLayer = layer;
+
+ IntSize size(layer->getSize().width(), layer->getSize().height());
+ TransformationMatrix matrix;
+ XLOG("Evaluate transforms animations, %d operations, progress %.2f for layer %d (%d, %d)"
+ , m_operations->size(), progress, layer->uniqueId(), size.width(), size.height());
+
+ if (!m_operations->size())
+ return false;
+
+ // First, we need to get the from and to values
+
+ int from, to;
+ pickValues(progress, &from, &to);
+ TransformAnimationValue* fromValue = (TransformAnimationValue*) m_operations->at(from);
+ TransformAnimationValue* toValue = (TransformAnimationValue*) m_operations->at(to);
+
+ XLOG("[layer %d] fromValue %x, key %.2f, toValue %x, key %.2f for progress %.2f",
+ layer->uniqueId(),
+ fromValue, fromValue->keyTime(),
+ toValue, toValue->keyTime(), progress);
+
+ // We now have the correct two values to work with, let's compute the
+ // progress value
+
+ const TimingFunction* timingFunction = fromValue->timingFunction();
+ float p = applyTimingFunction(fromValue->keyTime(), toValue->keyTime(),
+ progress, timingFunction);
+ XLOG("progress %.2f => %.2f from: %.2f to: %.2f", progress, p, fromValue->keyTime(),
+ toValue->keyTime());
+ progress = p;
+
+ // With both values and the progress, we also need to check out that
+ // the operations are compatible (i.e. we are animating the same number
+ // of values; if not we do a matrix blend)
+
+ TransformationMatrix transformMatrix;
+ bool valid = true;
+ unsigned int fromSize = fromValue->value()->size();
+ if (fromSize) {
+ if (toValue->value()->size() != fromSize)
+ valid = false;
+ else {
+ for (unsigned int j = 0; j < fromSize && valid; j++) {
+ if (!fromValue->value()->operations()[j]->isSameType(
+ *toValue->value()->operations()[j]))
+ valid = false;
+ }
+ }
+ }
+
+ if (valid) {
+ for (size_t i = 0; i < toValue->value()->size(); ++i)
+ toValue->value()->operations()[i]->blend(fromValue->value()->at(i),
+ progress)->apply(transformMatrix, size);
+ } else {
+ TransformationMatrix source;
+
+ fromValue->value()->apply(size, source);
+ toValue->value()->apply(size, transformMatrix);
+
+ transformMatrix.blend(source, progress);
+ }
+
+ // Set the final transform on the layer
+ layer->setTransform(transformMatrix);
+
+ return ret;
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/AndroidAnimation.h b/Source/WebCore/platform/graphics/android/AndroidAnimation.h
new file mode 100644
index 0000000..6f1410d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/AndroidAnimation.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AndroidAnimation_h
+#define AndroidAnimation_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FloatPoint.h"
+#include "FloatPoint3D.h"
+#include "GraphicsLayer.h"
+#include "HashMap.h"
+#include "LayerAndroid.h"
+#include "RefPtr.h"
+#include "Timer.h"
+#include "TransformOperation.h"
+#include "Vector.h"
+
+namespace WebCore {
+
+class TimingFunction;
+
+class AndroidAnimation : public RefCounted<AndroidAnimation> {
+ public:
+ AndroidAnimation(AnimatedPropertyID type,
+ const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime);
+ AndroidAnimation(AndroidAnimation* anim);
+
+ virtual ~AndroidAnimation();
+ virtual PassRefPtr<AndroidAnimation> copy() = 0;
+ double elapsedTime(double time);
+ void pickValues(double progress, int* start, int* end);
+ bool checkIterationsAndProgress(double time, float* finalProgress);
+ double applyTimingFunction(float from, float to, double progress,
+ const TimingFunction* timingFunction);
+ virtual bool evaluate(LayerAndroid* layer, double time) = 0;
+ static long instancesCount();
+ void setName(const String& name) { m_name = name; }
+ String name() { return m_name; }
+ AnimatedPropertyID type() { return m_type; }
+ bool finished() { return m_finished; }
+
+ protected:
+ double m_beginTime;
+ double m_elapsedTime;
+ double m_duration;
+ bool m_finished;
+ int m_iterationCount;
+ int m_direction;
+ RefPtr<TimingFunction> m_timingFunction;
+ String m_name;
+ AnimatedPropertyID m_type;
+ KeyframeValueList* m_operations;
+ LayerAndroid* m_originalLayer;
+};
+
+class AndroidOpacityAnimation : public AndroidAnimation {
+ public:
+ static PassRefPtr<AndroidOpacityAnimation> create(const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime);
+ AndroidOpacityAnimation(const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime);
+ AndroidOpacityAnimation(AndroidOpacityAnimation* anim);
+ virtual PassRefPtr<AndroidAnimation> copy();
+
+ virtual bool evaluate(LayerAndroid* layer, double time);
+};
+
+class AndroidTransformAnimation : public AndroidAnimation {
+ public:
+ static PassRefPtr<AndroidTransformAnimation> create(
+ const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime);
+ AndroidTransformAnimation(const Animation* animation,
+ KeyframeValueList* operations,
+ double beginTime);
+
+ AndroidTransformAnimation(AndroidTransformAnimation* anim);
+ virtual PassRefPtr<AndroidAnimation> copy();
+
+ virtual bool evaluate(LayerAndroid* layer, double time);
+};
+
+} // namespace WebCore
+
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif // AndroidAnimation_h
diff --git a/Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp b/Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp
new file mode 100644
index 0000000..964422a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "BackedDoubleBufferedTexture.h"
+
+#include "BaseTile.h"
+#include "ClassTracker.h"
+#include "DeleteTextureOperation.h"
+#include "GLUtils.h"
+#include "TilesManager.h"
+
+#define LOG_NDEBUG 1
+#define LOG_TAG "BackedDoubleBufferedTexture.cpp"
+#include <utils/Log.h>
+
+namespace WebCore {
+
+BackedDoubleBufferedTexture::BackedDoubleBufferedTexture(uint32_t w, uint32_t h,
+ SkBitmap* bitmap,
+ SkBitmap::Config config)
+ : DoubleBufferedTexture(eglGetCurrentContext())
+ , m_usedLevel(-1)
+ , m_config(config)
+ , m_owner(0)
+ , m_delayedReleaseOwner(0)
+ , m_delayedRelease(false)
+ , m_busy(false)
+{
+ m_size.set(w, h);
+ if (bitmap) {
+ m_bitmap = bitmap;
+ m_sharedBitmap = true;
+ m_canvas = new SkCanvas(*m_bitmap);
+ } else {
+ m_bitmap = 0;
+ m_sharedBitmap = false;
+ m_canvas = 0;
+ }
+
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("BackedDoubleBufferedTexture");
+#endif
+}
+
+SkCanvas* BackedDoubleBufferedTexture::canvas()
+{
+ if (!m_bitmap && !m_sharedBitmap) {
+ m_bitmap = new SkBitmap();
+ m_bitmap->setConfig(m_config, m_size.width(), m_size.height());
+ m_bitmap->allocPixels();
+ m_bitmap->eraseColor(0);
+ m_canvas = new SkCanvas(*m_bitmap);
+ }
+ return m_canvas;
+}
+
+BackedDoubleBufferedTexture::~BackedDoubleBufferedTexture()
+{
+ if (!m_sharedBitmap)
+ delete m_bitmap;
+ delete m_canvas;
+ SharedTexture* textures[3] = { &m_textureA, &m_textureB, 0 };
+ destroyTextures(textures);
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("BackedDoubleBufferedTexture");
+#endif
+}
+
+void BackedDoubleBufferedTexture::destroyTextures(SharedTexture** textures)
+{
+ int x = 0;
+ while (textures[x]) {
+ // We need to delete the source texture and EGLImage in the texture
+ // generation thread. In theory we should be able to delete the EGLImage
+ // from either thread, but it currently throws an error if not deleted
+ // in the same EGLContext from which it was created.
+ textures[x]->lock();
+ DeleteTextureOperation* operation = new DeleteTextureOperation(
+ textures[x]->getSourceTextureId(), textures[x]->getEGLImage());
+ textures[x]->unlock();
+ TilesManager::instance()->scheduleOperation(operation);
+ x++;
+ }
+}
+
+TextureInfo* BackedDoubleBufferedTexture::producerLock()
+{
+ m_busyLock.lock();
+ m_busy = true;
+ m_busyLock.unlock();
+ return DoubleBufferedTexture::producerLock();
+}
+
+void BackedDoubleBufferedTexture::producerRelease()
+{
+ DoubleBufferedTexture::producerRelease();
+ setNotBusy();
+}
+
+void BackedDoubleBufferedTexture::producerReleaseAndSwap()
+{
+ DoubleBufferedTexture::producerReleaseAndSwap();
+ setNotBusy();
+}
+
+void BackedDoubleBufferedTexture::setNotBusy()
+{
+ android::Mutex::Autolock lock(m_busyLock);
+ m_busy = false;
+ if (m_delayedRelease) {
+ if (m_owner == m_delayedReleaseOwner)
+ m_owner = 0;
+
+ m_delayedRelease = false;
+ m_delayedReleaseOwner = 0;
+ }
+ m_busyCond.signal();
+}
+
+bool BackedDoubleBufferedTexture::busy()
+{
+ android::Mutex::Autolock lock(m_busyLock);
+ return m_busy;
+}
+
+bool BackedDoubleBufferedTexture::textureExist(TextureInfo* textureInfo)
+{
+ if (!m_bitmap)
+ return false;
+
+ if (!m_bitmap->width() || !m_bitmap->height())
+ return false;
+
+ if (textureInfo->m_width == m_bitmap->width() &&
+ textureInfo->m_height == m_bitmap->height())
+ return true;
+
+ return false;
+}
+
+void BackedDoubleBufferedTexture::producerUpdate(TextureInfo* textureInfo)
+{
+ if (!m_bitmap)
+ return;
+
+ // no need to upload a texture since the bitmap is empty
+ if (!m_bitmap->width() && !m_bitmap->height()) {
+ producerRelease();
+ return;
+ }
+
+ if (textureExist(textureInfo))
+ GLUtils::updateTextureWithBitmap(textureInfo->m_textureId, *m_bitmap);
+ else {
+ GLUtils::createTextureWithBitmap(textureInfo->m_textureId, *m_bitmap);
+ textureInfo->m_width = m_bitmap->width();
+ textureInfo->m_height = m_bitmap->height();
+ }
+
+ if (!m_sharedBitmap) {
+ delete m_bitmap;
+ delete m_canvas;
+ m_bitmap = 0;
+ m_canvas = 0;
+ }
+
+ producerReleaseAndSwap();
+}
+
+bool BackedDoubleBufferedTexture::acquire(TextureOwner* owner, bool force)
+{
+ if (m_owner == owner) {
+ if (m_delayedRelease) {
+ m_delayedRelease = false;
+ m_delayedReleaseOwner = 0;
+ }
+ return true;
+ }
+
+ return setOwner(owner, force);
+}
+
+bool BackedDoubleBufferedTexture::tryAcquire(TextureOwner* owner, TiledPage* currentPage, TiledPage* nextPage)
+{
+ m_busyLock.lock();
+ if (!m_busy
+ && m_owner
+ && m_owner->page() != currentPage
+ && m_owner->page() != nextPage) {
+ m_busyLock.unlock();
+ return this->acquire(owner);
+ }
+ m_busyLock.unlock();
+ return false;
+}
+
+bool BackedDoubleBufferedTexture::setOwner(TextureOwner* owner, bool force)
+{
+ // if the writable texture is busy (i.e. currently being written to) then we
+ // can't change the owner out from underneath that texture
+ m_busyLock.lock();
+ while (m_busy && force)
+ m_busyCond.wait(m_busyLock);
+ bool busy = m_busy;
+ m_busyLock.unlock();
+
+ if (!busy) {
+ // if we are not busy we can try to remove the texture from the layer;
+ // LayerAndroid::removeTexture() is protected by the same lock as
+ // LayerAndroid::paintBitmapGL(), so either we execute removeTexture()
+ // first and paintBitmapGL() will bail out, or we execute it after,
+ // and paintBitmapGL() will mark the texture as busy before
+ // relinquishing the lock. LayerAndroid::removeTexture() will call
+ // BackedDoubleBufferedTexture::release(), which will then do nothing
+ // if the texture is busy and we then don't return true.
+ bool proceed = true;
+ if (m_owner && m_owner != owner)
+ proceed = m_owner->removeTexture(this);
+
+ if (proceed) {
+ m_owner = owner;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BackedDoubleBufferedTexture::release(TextureOwner* owner)
+{
+ android::Mutex::Autolock lock(m_busyLock);
+ if (m_owner != owner)
+ return false;
+
+ if (!m_busy) {
+ m_owner = 0;
+ } else {
+ m_delayedRelease = true;
+ m_delayedReleaseOwner = owner;
+ }
+ return true;
+}
+
+void BackedDoubleBufferedTexture::setTile(TextureInfo* info, int x, int y,
+ float scale, unsigned int pictureCount)
+{
+ TextureTileInfo* textureInfo = m_texturesInfo.get(getWriteableTexture());
+ if (!textureInfo) {
+ textureInfo = new TextureTileInfo();
+ }
+ textureInfo->m_x = x;
+ textureInfo->m_y = y;
+ textureInfo->m_scale = scale;
+ textureInfo->m_picture = pictureCount;
+ m_texturesInfo.set(getWriteableTexture(), textureInfo);
+}
+
+bool BackedDoubleBufferedTexture::readyFor(BaseTile* baseTile)
+{
+ TextureTileInfo* info = m_texturesInfo.get(getReadableTexture());
+ if (info &&
+ (info->m_x == baseTile->x()) &&
+ (info->m_y == baseTile->y()) &&
+ (info->m_scale == baseTile->scale()) &&
+ (info->m_picture == baseTile->lastPaintedPicture())) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h b/Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h
new file mode 100644
index 0000000..7c2ea90
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 BackedDoubleBufferedTexture_h
+#define BackedDoubleBufferedTexture_h
+
+#include "DoubleBufferedTexture.h"
+#include "GLWebViewState.h"
+#include "TextureOwner.h"
+#include <SkBitmap.h>
+
+class SkCanvas;
+
+namespace WebCore {
+
+class BaseTile;
+
+class TextureTileInfo {
+ public:
+ TextureTileInfo()
+ : m_x(-1)
+ , m_y(-1)
+ , m_layerId(-1)
+ , m_scale(0)
+ , m_texture(0)
+ , m_picture(0)
+ {
+ }
+ int m_x;
+ int m_y;
+ int m_layerId;
+ float m_scale;
+ TextureInfo* m_texture;
+ unsigned int m_picture;
+};
+
+// DoubleBufferedTexture using a SkBitmap as backing mechanism
+class BackedDoubleBufferedTexture : public DoubleBufferedTexture {
+public:
+ // This object is to be constructed on the consumer's thread and must have
+ // a width and height greater than 0.
+ BackedDoubleBufferedTexture(uint32_t w, uint32_t h,
+ SkBitmap* bitmap = 0,
+ SkBitmap::Config config = SkBitmap::kARGB_8888_Config);
+ virtual ~BackedDoubleBufferedTexture();
+
+ // these functions override their parent
+ virtual TextureInfo* producerLock();
+ virtual void producerRelease();
+ virtual void producerReleaseAndSwap();
+
+ // updates the texture with current bitmap and releases (and if needed also
+ // swaps) the texture.
+ virtual void producerUpdate(TextureInfo* textureInfo);
+ void producerUpdate(TextureInfo* textureInfo, SkBitmap* bitmap, SkIRect& rect);
+ bool textureExist(TextureInfo* textureInfo);
+
+ // The level can be one of the following values:
+ // * -1 for an unused texture.
+ // * 0 for the tiles intersecting with the viewport.
+ // * n where n > 0 for the distance between the viewport and the tile.
+ // We use this to prioritize the order in which we reclaim textures, see
+ // TilesManager::getAvailableTexture() for more information.
+ int usedLevel() { return m_usedLevel; }
+ void setUsedLevel(int used) { m_usedLevel = used; }
+
+ // allows consumer thread to assign ownership of the texture to the tile. It
+ // returns false if ownership cannot be transferred because the tile is busy
+ bool acquire(TextureOwner* owner, bool force = false);
+ bool release(TextureOwner* owner);
+ bool tryAcquire(TextureOwner* owner, TiledPage* currentPage, TiledPage* nextPage);
+
+ // set the texture owner if not busy. Return false if busy, true otherwise.
+ bool setOwner(TextureOwner* owner, bool force = false);
+
+ // private member accessor functions
+ TextureOwner* owner() { return m_owner; } // only used by the consumer thread
+ TextureOwner* delayedReleaseOwner() { return m_delayedReleaseOwner; }
+ SkCanvas* canvas(); // only used by the producer thread
+ SkBitmap* bitmap() { return m_bitmap; }
+
+ bool busy();
+ void setNotBusy();
+
+ const SkSize& getSize() const { return m_size; }
+
+ void setTile(TextureInfo* info, int x, int y, float scale, unsigned int pictureCount);
+ bool readyFor(BaseTile* baseTile);
+
+protected:
+ HashMap<SharedTexture*, TextureTileInfo*> m_texturesInfo;
+
+private:
+ void destroyTextures(SharedTexture** textures);
+
+ SkBitmap* m_bitmap;
+ bool m_sharedBitmap;
+ SkSize m_size;
+ SkCanvas* m_canvas;
+ int m_usedLevel;
+ SkBitmap::Config m_config;
+ TextureOwner* m_owner;
+
+ // When trying to release a texture, we may delay this if the texture is
+ // currently used (busy being painted). We use the following two variables
+ // to do so in setNotBusy()
+ TextureOwner* m_delayedReleaseOwner;
+ bool m_delayedRelease;
+
+ // This values signals that the texture is currently in use by the consumer.
+ // This allows us to prevent the owner of the texture from changing while the
+ // consumer is holding a lock on the texture.
+ bool m_busy;
+ // We mutex protect the reads/writes of m_busy to ensure that we are reading
+ // the most up-to-date value even across processors in an SMP system.
+ android::Mutex m_busyLock;
+ // We use this condition variable to signal that the texture
+ // is not busy anymore
+ android::Condition m_busyCond;
+};
+
+} // namespace WebCore
+
+#endif // BackedDoubleBufferedTexture_h
diff --git a/Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp
new file mode 100644
index 0000000..09d80a1
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "BaseLayerAndroid.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "ClassTracker.h"
+#include "GLUtils.h"
+#include "ShaderProgram.h"
+#include "SkCanvas.h"
+#include "TilesManager.h"
+#include <GLES2/gl2.h>
+#include <wtf/CurrentTime.h>
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "BaseLayerAndroid", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+using namespace android;
+
+BaseLayerAndroid::BaseLayerAndroid()
+#if USE(ACCELERATED_COMPOSITING)
+ : m_glWebViewState(0),
+ m_color(Color::white)
+#endif
+{
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("BaseLayerAndroid");
+#endif
+}
+
+BaseLayerAndroid::~BaseLayerAndroid()
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (TilesManager::hardwareAccelerationEnabled())
+ TilesManager::instance()->removeOperationsForBaseLayer(this);
+#endif
+ m_content.clear();
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("BaseLayerAndroid");
+#endif
+}
+
+void BaseLayerAndroid::setContent(const PictureSet& src)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ // FIXME: We lock here because we do not want
+ // to paint and change the m_content concurrently.
+ // We should instead refactor PictureSet to use
+ // an atomic refcounting scheme and use atomic operations
+ // to swap PictureSets.
+ android::Mutex::Autolock lock(m_drawLock);
+#endif
+ m_content.set(src);
+ // FIXME: We cannot set the size of the base layer because it will screw up
+ // the matrix used. We need to fix matrix computation for the base layer
+ // and then we can set the size.
+ // setSize(src.width(), src.height());
+}
+
+void BaseLayerAndroid::setExtra(SkPicture& src)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ android::Mutex::Autolock lock(m_drawLock);
+#endif
+ m_extra.swap(src);
+}
+
+void BaseLayerAndroid::drawCanvas(SkCanvas* canvas)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ android::Mutex::Autolock lock(m_drawLock);
+#endif
+ if (!m_content.isEmpty())
+ m_content.draw(canvas);
+ // TODO : replace with !m_extra.isEmpty() once such a call exists
+ if (m_extra.width() > 0)
+ m_extra.draw(canvas);
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+bool BaseLayerAndroid::drawBasePictureInGL(SkRect& viewport, float scale, double currentTime)
+{
+ if (!m_glWebViewState)
+ return false;
+
+ bool goingDown = m_previousVisible.fTop - viewport.fTop <= 0;
+ bool goingLeft = m_previousVisible.fLeft - viewport.fLeft >= 0;
+
+ m_glWebViewState->setViewport(viewport, scale);
+
+ const SkIRect& viewportTileBounds = m_glWebViewState->viewportTileBounds();
+ XLOG("drawBasePicture, TX: %d, TY: %d scale %.2f", viewportTileBounds.fLeft,
+ viewportTileBounds.fTop, scale);
+
+ if (scale == m_glWebViewState->currentScale()
+ || m_glWebViewState->preZoomBounds().isEmpty())
+ m_glWebViewState->setPreZoomBounds(viewportTileBounds);
+
+ bool prepareNextTiledPage = false;
+ // If we have a different scale than the current one, we have to
+ // decide what to do. The current behaviour is to delay an update,
+ // so that we do not slow down zooming unnecessarily.
+ if (m_glWebViewState->currentScale() != scale
+ && (m_glWebViewState->scaleRequestState() == GLWebViewState::kNoScaleRequest
+ || m_glWebViewState->futureScale() != scale)
+ || m_glWebViewState->scaleRequestState() == GLWebViewState::kWillScheduleRequest) {
+
+ // schedule the new request
+ m_glWebViewState->scheduleUpdate(currentTime, viewportTileBounds, scale);
+
+ // If it's a new request, we will have to prepare the page.
+ if (m_glWebViewState->scaleRequestState() == GLWebViewState::kRequestNewScale)
+ prepareNextTiledPage = true;
+ }
+
+ // If the viewport has changed since we scheduled the request, we also need to prepare.
+ if (((m_glWebViewState->scaleRequestState() == GLWebViewState::kRequestNewScale)
+ || (m_glWebViewState->scaleRequestState() == GLWebViewState::kReceivedNewScale))
+ && (m_glWebViewState->futureViewport() != viewportTileBounds))
+ prepareNextTiledPage = true;
+
+ bool zooming = false;
+ if (m_glWebViewState->scaleRequestState() != GLWebViewState::kNoScaleRequest) {
+ prepareNextTiledPage = true;
+ zooming = true;
+ }
+
+ // Display the current page
+ TiledPage* tiledPage = m_glWebViewState->frontPage();
+ tiledPage->setScale(m_glWebViewState->currentScale());
+
+ // Let's prepare the page if needed
+ if (prepareNextTiledPage) {
+ TiledPage* nextTiledPage = m_glWebViewState->backPage();
+ nextTiledPage->setScale(scale);
+ m_glWebViewState->setFutureViewport(viewportTileBounds);
+ m_glWebViewState->lockBaseLayerUpdate();
+ nextTiledPage->prepare(goingDown, goingLeft, viewportTileBounds);
+ // Cancel pending paints for the foreground page
+ TilesManager::instance()->removePaintOperationsForPage(tiledPage, false);
+ }
+
+ float transparency = 1;
+ bool doSwap = false;
+
+ // If we fired a request, let's check if it's ready to use
+ if (m_glWebViewState->scaleRequestState() == GLWebViewState::kRequestNewScale) {
+ TiledPage* nextTiledPage = m_glWebViewState->backPage();
+ if (nextTiledPage->ready(viewportTileBounds, m_glWebViewState->futureScale()))
+ m_glWebViewState->setScaleRequestState(GLWebViewState::kReceivedNewScale);
+ }
+
+ // If the page is ready, display it. We do a short transition between
+ // the two pages (current one and future one with the new scale factor)
+ if (m_glWebViewState->scaleRequestState() == GLWebViewState::kReceivedNewScale) {
+ TiledPage* nextTiledPage = m_glWebViewState->backPage();
+ double transitionTime = (scale < m_glWebViewState->currentScale()) ?
+ m_glWebViewState->zoomOutTransitionTime(currentTime) :
+ m_glWebViewState->zoomInTransitionTime(currentTime);
+
+ float newTilesTransparency = 1;
+ if (scale < m_glWebViewState->currentScale())
+ newTilesTransparency = 1 - m_glWebViewState->zoomOutTransparency(currentTime);
+ else
+ transparency = m_glWebViewState->zoomInTransparency(currentTime);
+
+ nextTiledPage->draw(newTilesTransparency, viewportTileBounds);
+
+ // The transition between the two pages is finished, swap them
+ if (currentTime > transitionTime) {
+ m_glWebViewState->resetTransitionTime();
+ doSwap = true;
+ }
+ }
+
+ const SkIRect& preZoomBounds = m_glWebViewState->preZoomBounds();
+
+ TiledPage* nextTiledPage = m_glWebViewState->backPage();
+ bool needsRedraw = false;
+
+ // We are now using an hybrid model -- during scrolling,
+ // we will display the current tiledPage even if some tiles are
+ // out of date. When standing still on the other hand, we wait until
+ // the back page is ready before swapping the pages, ensuring that the
+ // displayed content is in sync.
+ if (!doSwap && !zooming && !m_glWebViewState->moving()) {
+ if (!tiledPage->ready(preZoomBounds, m_glWebViewState->currentScale())) {
+ m_glWebViewState->lockBaseLayerUpdate();
+ nextTiledPage->setScale(m_glWebViewState->currentScale());
+ nextTiledPage->prepare(goingDown, goingLeft, preZoomBounds);
+ }
+ if (nextTiledPage->ready(preZoomBounds, m_glWebViewState->currentScale())) {
+ nextTiledPage->draw(transparency, preZoomBounds);
+ m_glWebViewState->resetFrameworkInval();
+ m_glWebViewState->unlockBaseLayerUpdate();
+ doSwap = true;
+ } else {
+ tiledPage->draw(transparency, preZoomBounds);
+ }
+ } else {
+ if (tiledPage->ready(preZoomBounds, m_glWebViewState->currentScale()))
+ m_glWebViewState->resetFrameworkInval();
+
+ // Ask for the tiles and draw -- tiles may be out of date.
+ if (!zooming)
+ m_glWebViewState->unlockBaseLayerUpdate();
+
+ if (!prepareNextTiledPage)
+ tiledPage->prepare(goingDown, goingLeft, preZoomBounds);
+ tiledPage->draw(transparency, preZoomBounds);
+ }
+
+ if (m_glWebViewState->scaleRequestState() != GLWebViewState::kNoScaleRequest
+ || !tiledPage->ready(preZoomBounds, m_glWebViewState->currentScale()))
+ needsRedraw = true;
+
+ if (doSwap) {
+ m_glWebViewState->setCurrentScale(scale);
+ m_glWebViewState->swapPages();
+ m_glWebViewState->unlockBaseLayerUpdate();
+ }
+
+ m_glWebViewState->paintExtras();
+ return needsRedraw;
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+bool BaseLayerAndroid::drawGL(LayerAndroid* compositedRoot,
+ IntRect& viewRect, SkRect& visibleRect,
+ IntRect& webViewRect, int titleBarHeight,
+ IntRect& screenClip, float scale, SkColor color)
+{
+ bool needsRedraw = false;
+#if USE(ACCELERATED_COMPOSITING)
+ int left = viewRect.x();
+ int top = viewRect.y();
+ int width = viewRect.width();
+ int height = viewRect.height();
+ XLOG("drawBasePicture drawGL() viewRect: %d, %d, %d, %d - %.2f",
+ left, top, width, height, scale);
+
+ m_glWebViewState->setBackgroundColor(color);
+ glClearColor((float)m_color.red() / 255.0,
+ (float)m_color.green() / 255.0,
+ (float)m_color.blue() / 255.0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glViewport(left, top, width, height);
+ ShaderProgram* shader = TilesManager::instance()->shader();
+ if (shader->program() == -1) {
+ XLOG("Reinit shader");
+ shader->init();
+ }
+ glUseProgram(shader->program());
+ glUniform1i(shader->textureSampler(), 0);
+ shader->setViewRect(viewRect);
+ shader->setViewport(visibleRect);
+ shader->setWebViewRect(webViewRect);
+ shader->setTitleBarHeight(titleBarHeight);
+ shader->setScreenClip(screenClip);
+ shader->resetBlending();
+
+ double currentTime = WTF::currentTime();
+ needsRedraw = drawBasePictureInGL(visibleRect, scale, currentTime);
+ if (!needsRedraw)
+ m_glWebViewState->resetFrameworkInval();
+
+ if (compositedRoot) {
+ TransformationMatrix ident;
+
+ bool animsRunning = compositedRoot->evaluateAnimations();
+ if (animsRunning)
+ needsRedraw = true;
+
+ compositedRoot->updateFixedLayersPositions(visibleRect);
+ FloatRect clip(0, 0, viewRect.width(), viewRect.height());
+ compositedRoot->updateGLPositions(ident, clip, 1);
+ SkMatrix matrix;
+ matrix.setTranslate(left, top);
+
+ // At this point, the previous LayerAndroid* root has been destroyed,
+ // which will have removed the layers as owners of the textures.
+ // Let's now do a pass to reserve the textures for the current tree;
+ // it will only reserve existing textures, not create them on demand.
+#ifdef DEBUG
+ TilesManager::instance()->printLayersTextures("reserve");
+#endif
+ // Get the current scale; if we are zooming, we don't change the scale
+ // factor immediately (see BaseLayerAndroid::drawBasePictureInGL()), but
+ // we change the scaleRequestState. When the state is kReceivedNewScale
+ // we can use the future scale instead of the current scale to request
+ // new textures. After a transition time, the scaleRequestState will be
+ // reset and the current scale will be set to the future scale.
+ float scale = m_glWebViewState->currentScale();
+ if (m_glWebViewState->scaleRequestState() == GLWebViewState::kReceivedNewScale) {
+ scale = m_glWebViewState->futureScale();
+ }
+ bool fullSetup = true;
+ if ((m_glWebViewState->previouslyUsedRoot() == compositedRoot) &&
+ (compositedRoot->getScale() == scale) &&
+ (!m_glWebViewState->moving()))
+ fullSetup = false;
+
+ compositedRoot->setScale(scale);
+
+ if (fullSetup) {
+ compositedRoot->computeTextureSize(currentTime);
+ compositedRoot->reserveGLTextures();
+
+#ifdef DEBUG
+ int size = compositedRoot->countTextureSize();
+ int nbLayers = compositedRoot->nbLayers();
+ XLOG("We are using %d Mb for %d layers", size / 1024 / 1024, nbLayers);
+ compositedRoot->showLayers();
+#endif
+ // Now that we marked the textures being used, we delete
+ // the unnecessary ones to make space...
+ TilesManager::instance()->cleanupLayersTextures(compositedRoot);
+ }
+ // Finally do another pass to create new textures and schedule
+ // repaints if needed
+ compositedRoot->createGLTextures();
+
+ if (compositedRoot->drawGL(m_glWebViewState, matrix))
+ needsRedraw = true;
+ else if (!animsRunning)
+ m_glWebViewState->resetLayersDirtyArea();
+
+ } else {
+ TilesManager::instance()->cleanupLayersTextures(0);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ m_previousVisible = visibleRect;
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#ifdef DEBUG
+ ClassTracker::instance()->show();
+#endif
+ return needsRedraw;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/android/BaseLayerAndroid.h b/Source/WebCore/platform/graphics/android/BaseLayerAndroid.h
new file mode 100644
index 0000000..38e7e47
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/BaseLayerAndroid.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 BaseLayerAndroid_h
+#define BaseLayerAndroid_h
+
+#include "Color.h"
+#include "GLWebViewState.h"
+#include "IntRect.h"
+#include "PictureSet.h"
+#include "SkLayer.h"
+#include "SkPicture.h"
+
+namespace WebCore {
+
+class BaseLayerAndroid : public SkLayer {
+
+public:
+ BaseLayerAndroid();
+ virtual ~BaseLayerAndroid();
+
+#if USE(ACCELERATED_COMPOSITING)
+ void setGLWebViewState(GLWebViewState* infos) { m_glWebViewState = infos; }
+ void setBackgroundColor(Color& color) { m_color = color; }
+#endif
+ void setContent(const android::PictureSet& src);
+ void setExtra(SkPicture& extra);
+ android::PictureSet* content() { return &m_content; }
+ // This method will paint using the current PictureSet onto
+ // the passed canvas. We used it to paint the GL tiles as well as
+ // WebView::copyBaseContentToPicture(), so a lock is necessary as
+ // we are running in different threads.
+ void drawCanvas(SkCanvas* canvas);
+
+ bool drawGL(LayerAndroid* compositedRoot, IntRect& rect, SkRect& viewport,
+ IntRect& webViewRect, int titleBarHeight, IntRect& screenClip,
+ float scale, SkColor color = SK_ColorWHITE);
+ void swapExtra(BaseLayerAndroid* base) { m_extra.swap(base->m_extra); }
+private:
+#if USE(ACCELERATED_COMPOSITING)
+ bool drawBasePictureInGL(SkRect& viewport, float scale, double currentTime);
+
+ GLWebViewState* m_glWebViewState;
+ android::Mutex m_drawLock;
+ Color m_color;
+#endif
+ android::PictureSet m_content;
+ SkPicture m_extra;
+ SkRect m_previousVisible;
+};
+
+} // namespace WebCore
+
+#endif // BaseLayerAndroid_h
diff --git a/Source/WebCore/platform/graphics/android/BaseTile.cpp b/Source/WebCore/platform/graphics/android/BaseTile.cpp
new file mode 100644
index 0000000..c74a278
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/BaseTile.cpp
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "BaseTile.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "GLUtils.h"
+#include "SkBitmap.h"
+#include "SkBitmapRef.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "TilesManager.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <cutils/atomic.h>
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTile", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+BaseTile::BaseTile()
+ : m_page(0)
+ , m_x(-1)
+ , m_y(-1)
+ , m_texture(0)
+ , m_scale(1)
+ , m_dirty(true)
+ , m_repaintPending(false)
+ , m_usable(true)
+ , m_lastDirtyPicture(0)
+ , m_fullRepaintA(true)
+ , m_fullRepaintB(true)
+ , m_lastPaintedPicture(0)
+{
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("BaseTile");
+#endif
+ m_currentDirtyArea = &m_dirtyAreaA;
+}
+
+BaseTile::~BaseTile()
+{
+ setUsedLevel(-1);
+ if (m_texture)
+ m_texture->release(this);
+
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("BaseTile");
+#endif
+}
+
+// All the following functions must be called from the main GL thread.
+
+void BaseTile::setContents(TiledPage* page, int x, int y)
+{
+ android::AutoMutex lock(m_atomicSync);
+ m_page = page;
+ m_x = x;
+ m_y = y;
+}
+
+void BaseTile::reserveTexture()
+{
+ BackedDoubleBufferedTexture* texture = TilesManager::instance()->getAvailableTexture(this);
+
+ android::AutoMutex lock(m_atomicSync);
+ if (texture && m_texture != texture) {
+ m_lastPaintedPicture = 0;
+ fullInval();
+ m_texture = texture;
+ }
+}
+
+bool BaseTile::removeTexture(BackedDoubleBufferedTexture* texture)
+{
+ XLOG("%x removeTexture res: %x... page %x", this, m_texture, m_page);
+ // We update atomically, so paintBitmap() can see the correct value
+ android::AutoMutex lock(m_atomicSync);
+ if (m_texture == texture)
+ m_texture = 0;
+ return true;
+}
+
+void BaseTile::fullInval()
+{
+ m_dirtyAreaA.setEmpty();
+ m_dirtyAreaB.setEmpty();
+ m_fullRepaintA = true;
+ m_fullRepaintB = true;
+ m_dirty = true;
+}
+
+void BaseTile::setScale(float scale)
+{
+ android::AutoMutex lock(m_atomicSync);
+ if (m_scale != scale) {
+ m_scale = scale;
+ fullInval();
+ }
+}
+
+void BaseTile::markAsDirty(int unsigned pictureCount,
+ const SkRegion& dirtyArea)
+{
+ android::AutoMutex lock(m_atomicSync);
+ m_lastDirtyPicture = pictureCount;
+ m_dirtyAreaA.op(dirtyArea, SkRegion::kUnion_Op);
+ m_dirtyAreaB.op(dirtyArea, SkRegion::kUnion_Op);
+ m_dirty = true;
+}
+
+void BaseTile::setUsable(bool usable)
+{
+ android::AutoMutex lock(m_atomicSync);
+ m_usable = usable;
+}
+
+
+bool BaseTile::isDirty()
+{
+ android::AutoMutex lock(m_atomicSync);
+ return m_dirty;
+}
+
+bool BaseTile::isRepaintPending()
+{
+ android::AutoMutex lock(m_atomicSync);
+ return m_repaintPending;
+}
+
+void BaseTile::setRepaintPending(bool pending)
+{
+ android::AutoMutex lock(m_atomicSync);
+ m_repaintPending = pending;
+}
+
+void BaseTile::setUsedLevel(int usedLevel)
+{
+ if (m_texture)
+ m_texture->setUsedLevel(usedLevel);
+}
+
+int BaseTile::usedLevel()
+{
+ if (m_texture)
+ return m_texture->usedLevel();
+ return -1;
+}
+
+void BaseTile::draw(float transparency, SkRect& rect, float scale)
+{
+ if (m_x < 0 || m_y < 0 || m_scale != scale)
+ return;
+
+ // No need to mutex protect reads of m_texture as it is only written to by
+ // the consumer thread.
+ if (!m_texture) {
+ XLOG("%x on page %x (%d, %d) trying to draw, but no m_texture!", this, m_page, x(), y());
+ return;
+ }
+
+ // Early return if set to un-usable in purpose!
+ m_atomicSync.lock();
+ bool usable = m_usable;
+ bool isTexturePainted = m_lastPaintedPicture;
+ m_atomicSync.unlock();
+ if (!usable) {
+ XLOG("early return at BaseTile::draw b/c tile set to unusable !");
+ return;
+ }
+ if (!isTexturePainted) {
+ XLOG("early return at BaseTile::draw b/c tile is not painted !");
+ return;
+ }
+
+ TextureInfo* textureInfo = m_texture->consumerLock();
+ if (!textureInfo) {
+ XLOG("%x (%d, %d) trying to draw, but no textureInfo!", this, x(), y());
+ m_texture->consumerRelease();
+ return;
+ }
+
+ if (m_texture->readyFor(this)) {
+ XLOG("draw tile %d, %d, %.2f with texture %x", x(), y(), scale(), m_texture);
+ TilesManager::instance()->shader()->drawQuad(rect, textureInfo->m_textureId,
+ transparency);
+ }
+ m_texture->consumerRelease();
+}
+
+bool BaseTile::isTileReady()
+{
+ if (!m_texture)
+ return false;
+ if (m_texture->owner() != this)
+ return false;
+
+ android::AutoMutex lock(m_atomicSync);
+ if (m_dirty)
+ return false;
+
+ m_texture->consumerLock();
+ bool ready = m_texture->readyFor(this);
+ m_texture->consumerRelease();
+
+ if (ready)
+ return true;
+
+ m_dirty = true;
+ return false;
+}
+
+void BaseTile::drawTileInfo(SkCanvas* canvas,
+ BackedDoubleBufferedTexture* texture,
+ int x, int y, float scale,
+ int pictureCount)
+{
+ SkPaint paint;
+ char str[256];
+ snprintf(str, 256, "(%d,%d) %.2f, tl%x tx%x p%x c%x",
+ x, y, scale, this, texture, m_page, pictureCount);
+ paint.setARGB(255, 0, 0, 0);
+ canvas->drawText(str, strlen(str), 0, 10, paint);
+ paint.setARGB(255, 255, 0, 0);
+ canvas->drawText(str, strlen(str), 0, 11, paint);
+}
+
+// This is called from the texture generation thread
+void BaseTile::paintBitmap()
+{
+
+ // We acquire the values below atomically. This ensures that we are reading
+ // values correctly across cores. Further, once we have these values they
+ // can be updated by other threads without consequence.
+ m_atomicSync.lock();
+ bool dirty = m_dirty;
+ BackedDoubleBufferedTexture* texture = m_texture;
+ SkRegion dirtyArea = *m_currentDirtyArea;
+ float scale = m_scale;
+ const int x = m_x;
+ const int y = m_y;
+ m_atomicSync.unlock();
+
+ if (!dirty || !texture) {
+ return;
+ }
+
+ TiledPage* tiledPage = m_page;
+
+ texture->producerAcquireContext();
+ TextureInfo* textureInfo = texture->producerLock();
+
+ // at this point we can safely check the ownership (if the texture got
+ // transferred to another BaseTile under us)
+ if (texture->owner() != this || texture->usedLevel() > 1) {
+ texture->producerRelease();
+ return;
+ }
+
+ SkSize size = texture->getSize();
+ float tileWidth = size.width();
+ float tileHeight = size.height();
+
+ const float invScale = 1 / scale;
+ float w = tileWidth * invScale;
+ float h = tileHeight * invScale;
+
+ SkCanvas* canvas;
+ unsigned int pictureCount = 0;
+
+ SkRegion::Iterator cliperator(dirtyArea);
+
+ bool fullRepaint = false;
+ if (((m_currentDirtyArea == &m_dirtyAreaA) && m_fullRepaintA) ||
+ ((m_currentDirtyArea == &m_dirtyAreaB) && m_fullRepaintB))
+ fullRepaint = true;
+
+ if (fullRepaint) {
+ SkIRect rect;
+ pictureCount = paintPartialBitmap(rect, 0, 0, scale, texture,
+ textureInfo, tiledPage, true);
+ } else {
+ while (!cliperator.done()) {
+ SkRect dirtyRect;
+ dirtyRect.set(cliperator.rect());
+
+ float left = x * tileWidth;
+ float top = y * tileHeight;
+
+ // compute the rect to corresponds to pixels
+ SkRect realTileRect;
+ realTileRect.fLeft = left;
+ realTileRect.fTop = top;
+ realTileRect.fRight = left + tileWidth;
+ realTileRect.fBottom = top + tileHeight;
+
+ // scale the dirtyRect for intersect computation.
+ SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale,
+ dirtyRect.height() * scale);
+ realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale);
+
+ if (!realTileRect.intersect(realDirtyRect)) {
+ cliperator.next();
+ continue;
+ }
+
+ realTileRect.fLeft = floorf(realTileRect.fLeft);
+ realTileRect.fTop = floorf(realTileRect.fTop);
+ realTileRect.fRight = ceilf(realTileRect.fRight);
+ realTileRect.fBottom = ceilf(realTileRect.fBottom);
+
+ SkIRect finalRealRect;
+ finalRealRect.fLeft = static_cast<int>(realTileRect.fLeft) % static_cast<int>(tileWidth);
+ finalRealRect.fTop = static_cast<int>(realTileRect.fTop) % static_cast<int>(tileHeight);
+ finalRealRect.fRight = finalRealRect.fLeft + realTileRect.width();
+ finalRealRect.fBottom = finalRealRect.fTop + realTileRect.height();
+
+ // the canvas translate can be recomputed accounting for the scale
+ float tx = - realTileRect.fLeft / scale;
+ float ty = - realTileRect.fTop / scale;
+
+ pictureCount = paintPartialBitmap(finalRealRect, tx, ty, scale, texture,
+ textureInfo, tiledPage);
+
+ cliperator.next();
+ }
+ }
+ XLOG("%x update texture %x for tile %d, %d scale %.2f (m_scale: %.2f)", this, textureInfo, x, y, scale, m_scale);
+
+ m_atomicSync.lock();
+ texture->setTile(textureInfo, x, y, scale, pictureCount);
+ texture->producerReleaseAndSwap();
+
+ if (texture == m_texture) {
+ m_lastPaintedPicture = pictureCount;
+
+ // set the fullrepaint flags
+
+ if ((m_currentDirtyArea == &m_dirtyAreaA) && m_fullRepaintA)
+ m_fullRepaintA = false;
+
+ if ((m_currentDirtyArea == &m_dirtyAreaB) && m_fullRepaintB)
+ m_fullRepaintB = false;
+
+ // The various checks to see if we are still dirty...
+
+ m_dirty = false;
+
+ if (m_scale != scale)
+ m_dirty = true;
+
+ if (!fullRepaint)
+ m_currentDirtyArea->op(dirtyArea, SkRegion::kDifference_Op);
+
+ if (!m_currentDirtyArea->isEmpty())
+ m_dirty = true;
+
+ // Now we can swap the dirty areas
+
+ m_currentDirtyArea = m_currentDirtyArea == &m_dirtyAreaA ? &m_dirtyAreaB : &m_dirtyAreaA;
+
+ if (!m_currentDirtyArea->isEmpty())
+ m_dirty = true;
+
+ if (!m_dirty)
+ m_usable = true;
+ }
+
+ m_atomicSync.unlock();
+}
+
+int BaseTile::paintPartialBitmap(SkIRect r, float ptx, float pty,
+ float scale, BackedDoubleBufferedTexture* texture,
+ TextureInfo* textureInfo,
+ TiledPage* tiledPage, bool fullRepaint)
+{
+ SkIRect rect = r;
+ float tx = ptx;
+ float ty = pty;
+ if (!texture->textureExist(textureInfo)) {
+ fullRepaint = true;
+ }
+
+ if ((rect.width() > TilesManager::instance()->tileWidth()) ||
+ (rect.height() > TilesManager::instance()->tileHeight()))
+ fullRepaint = true;
+
+ if (fullRepaint) {
+ rect.set(0, 0, TilesManager::instance()->tileWidth(),
+ TilesManager::instance()->tileHeight());
+ tx = - x() * TilesManager::instance()->tileWidth() / scale;
+ ty = - y() * TilesManager::instance()->tileHeight() / scale;
+ }
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height());
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+
+ SkCanvas canvas(bitmap);
+ canvas.drawARGB(255, 255, 255, 255);
+
+ SkPicture picture;
+ SkCanvas* nCanvas = picture.beginRecording(rect.width(), rect.height());
+ nCanvas->scale(scale, scale);
+ nCanvas->translate(tx, ty);
+ int pictureCount = tiledPage->paintBaseLayerContent(nCanvas);
+ picture.endRecording();
+
+ bool visualIndicator = TilesManager::instance()->getShowVisualIndicator();
+ if (visualIndicator)
+ canvas.save();
+ picture.draw(&canvas);
+ if (visualIndicator)
+ canvas.restore();
+
+ if (visualIndicator) {
+ int color = 20 + pictureCount % 100;
+ canvas.drawARGB(color, 0, 255, 0);
+
+ SkPaint paint;
+ paint.setARGB(128, 255, 0, 0);
+ paint.setStrokeWidth(3);
+ canvas.drawLine(0, 0, rect.width(), rect.height(), paint);
+ paint.setARGB(128, 0, 255, 0);
+ canvas.drawLine(0, rect.height(), rect.width(), 0, paint);
+ paint.setARGB(128, 0, 0, 255);
+ canvas.drawLine(0, 0, rect.width(), 0, paint);
+ canvas.drawLine(rect.width(), 0, rect.width(), rect.height(), paint);
+
+ drawTileInfo(&canvas, texture, x(), y(), scale, pictureCount);
+ }
+
+ if (!texture->textureExist(textureInfo)) {
+ GLUtils::createTextureWithBitmap(textureInfo->m_textureId, bitmap);
+ textureInfo->m_width = rect.width();
+ textureInfo->m_height = rect.height();
+ } else {
+ GLUtils::updateTextureWithBitmap(textureInfo->m_textureId, rect.fLeft, rect.fTop, bitmap);
+ }
+
+ bitmap.reset();
+
+ return pictureCount;
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/BaseTile.h b/Source/WebCore/platform/graphics/android/BaseTile.h
new file mode 100644
index 0000000..7b28f76
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/BaseTile.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 BaseTile_h
+#define BaseTile_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "HashMap.h"
+#include "SharedTexture.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+#include "TextureOwner.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <utils/threads.h>
+
+namespace WebCore {
+
+class TiledPage;
+class BackedDoubleBufferedTexture;
+
+/**
+ * An individual tile that is used to construct part of a webpage's BaseLayer of
+ * content. Each tile is assigned to a TiledPage and is responsible for drawing
+ * and displaying their section of the page. The lifecycle of a tile is:
+ *
+ * 1. Each tile is created on the main GL thread and assigned to a specific
+ * location within a TiledPage.
+ * 2. When needed the tile is passed to the background thread where it paints
+ * the BaseLayer's most recent PictureSet to a bitmap which is then uploaded
+ * to the GPU.
+ * 3. After the bitmap is uploaded to the GPU the main GL thread then uses the
+ * tile's draw() function to display the tile to the screen.
+ * 4. Steps 2-3 are repeated as necessary.
+ * 5. The tile is destroyed when the user navigates to a new page.
+ *
+ */
+class BaseTile : public TextureOwner {
+public:
+ BaseTile();
+ ~BaseTile();
+
+ void setContents(TiledPage* page, int x, int y);
+ bool isAvailable() const { return !m_texture; }
+
+ void reserveTexture();
+ void setUsedLevel(int);
+ int usedLevel();
+ bool isTileReady();
+ void draw(float transparency, SkRect& rect, float scale);
+
+ // the only thread-safe function called by the background thread
+ void paintBitmap();
+ int paintPartialBitmap(SkIRect rect, float tx, float ty,
+ float scale, BackedDoubleBufferedTexture* texture,
+ TextureInfo* textureInfo,
+ TiledPage* tiledPage,
+ bool fullRepaint = false);
+ void drawTileInfo(SkCanvas* canvas,
+ BackedDoubleBufferedTexture* texture,
+ int x, int y, float scale, int pictureCount);
+
+ void markAsDirty(const unsigned int pictureCount,
+ const SkRegion& dirtyArea);
+ bool isDirty();
+ bool isRepaintPending();
+ void setRepaintPending(bool pending);
+ void setUsable(bool usable);
+ float scale() const { return m_scale; }
+ void setScale(float scale);
+ void fullInval();
+
+ int x() const { return m_x; }
+ int y() const { return m_y; }
+ unsigned int lastPaintedPicture() const { return m_lastPaintedPicture; }
+ BackedDoubleBufferedTexture* texture() { return m_texture; }
+
+ // TextureOwner implementation
+ virtual bool removeTexture(BackedDoubleBufferedTexture* texture);
+ virtual TiledPage* page() { return m_page; }
+
+private:
+ // these variables are only set when the object is constructed
+ TiledPage* m_page;
+ int m_x;
+ int m_y;
+
+ // The remaining variables can be updated throughout the lifetime of the object
+ BackedDoubleBufferedTexture* m_texture;
+ float m_scale;
+ // used to signal that the that the tile is out-of-date and needs to be redrawn
+ bool m_dirty;
+ // used to signal that a repaint is pending
+ bool m_repaintPending;
+ // used to signal whether or not the draw can use this tile.
+ bool m_usable;
+ // stores the id of the latest picture from webkit that caused this tile to
+ // become dirty. A tile is no longer dirty when it has been painted with a
+ // picture that is newer than this value.
+ unsigned int m_lastDirtyPicture;
+
+ // store the dirty region
+ SkRegion m_dirtyAreaA;
+ SkRegion m_dirtyAreaB;
+ bool m_fullRepaintA;
+ bool m_fullRepaintB;
+ SkRegion* m_currentDirtyArea;
+
+ // stores the id of the latest picture painted to the tile. If the id is 0
+ // then we know that the picture has not yet been painted an there is nothing
+ // to display (dirty or otherwise).
+ unsigned int m_lastPaintedPicture;
+
+ // This mutex serves two purposes. (1) It ensures that certain operations
+ // happen atomically and (2) it makes sure those operations are synchronized
+ // across all threads and cores.
+ android::Mutex m_atomicSync;
+
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // BaseTile_h
diff --git a/Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.cpp b/Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.cpp
new file mode 100644
index 0000000..66340ee
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "BitmapAllocatorAndroid.h"
+#include "SharedBufferStream.h"
+#include "SkImageRef_GlobalPool.h"
+#include "SkImageRef_ashmem.h"
+
+// made this up, so we don't waste a file-descriptor on small images, plus
+// we don't want to lose too much on the round-up to a page size (4K)
+#define MIN_ASHMEM_ALLOC_SIZE (32*1024)
+
+
+static bool should_use_ashmem(const SkBitmap& bm) {
+ return bm.getSize() >= MIN_ASHMEM_ALLOC_SIZE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace WebCore {
+
+BitmapAllocatorAndroid::BitmapAllocatorAndroid(SharedBuffer* data,
+ int sampleSize)
+{
+ fStream = new SharedBufferStream(data);
+ fSampleSize = sampleSize;
+}
+
+BitmapAllocatorAndroid::~BitmapAllocatorAndroid()
+{
+ fStream->unref();
+}
+
+bool BitmapAllocatorAndroid::allocPixelRef(SkBitmap* bitmap, SkColorTable*)
+{
+ SkPixelRef* ref;
+ if (should_use_ashmem(*bitmap)) {
+// SkDebugf("ashmem [%d %d]\n", bitmap->width(), bitmap->height());
+ ref = new SkImageRef_ashmem(fStream, bitmap->config(), fSampleSize);
+ } else {
+// SkDebugf("globalpool [%d %d]\n", bitmap->width(), bitmap->height());
+ ref = new SkImageRef_GlobalPool(fStream, bitmap->config(), fSampleSize);
+ }
+ bitmap->setPixelRef(ref)->unref();
+ return true;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.h b/Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.h
new file mode 100644
index 0000000..11716e3
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 WebCore_BitmapAllocatorAndroid_DEFINED
+#define WebCore_BitmapAllocatorAndroid_DEFINED
+
+#include "SkBitmap.h"
+
+namespace WebCore {
+
+ class SharedBuffer;
+ class SharedBufferStream;
+
+ /** Returns a custom allocator that takes advantage of ashmem and global
+ pools to best manage the pixel memory for a decoded image. This should
+ be used for images that are logically immutable, and can be re-decoded
+ at will based on available memory.
+ */
+ class BitmapAllocatorAndroid : public SkBitmap::Allocator {
+ public:
+ BitmapAllocatorAndroid(SharedBuffer* data, int sampleSize);
+ virtual ~BitmapAllocatorAndroid();
+
+ // overrides
+ virtual bool allocPixelRef(SkBitmap*, SkColorTable*);
+
+ private:
+ SharedBufferStream* fStream;
+ int fSampleSize;
+ };
+
+}
+
+size_t computeMaxBitmapSizeForCache();
+
+#endif
diff --git a/Source/WebCore/platform/graphics/android/ClassTracker.cpp b/Source/WebCore/platform/graphics/android/ClassTracker.cpp
new file mode 100644
index 0000000..ad2b4dd
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ClassTracker.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "ClassTracker.h"
+
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "ClassTracker", __VA_ARGS__)
+
+namespace WebCore {
+
+ClassTracker* ClassTracker::instance()
+{
+ if (!gInstance)
+ gInstance = new ClassTracker();
+ return gInstance;
+}
+
+ClassTracker* ClassTracker::gInstance = 0;
+
+void ClassTracker::increment(String name)
+{
+ int value = 0;
+ if (m_classes.contains(name))
+ value = m_classes.get(name);
+
+ m_classes.set(name, value + 1);
+}
+
+void ClassTracker::decrement(String name)
+{
+ int value = 0;
+ if (m_classes.contains(name))
+ value = m_classes.get(name);
+
+ m_classes.set(name, value - 1);
+}
+
+void ClassTracker::show()
+{
+ XLOG("*** Tracking %d classes ***", m_classes.size());
+ for (HashMap<String, int>::iterator iter = m_classes.begin(); iter != m_classes.end(); ++iter) {
+ XLOG("class %s has %d instances",
+ iter->first.latin1().data(), iter->second);
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/android/ClassTracker.h b/Source/WebCore/platform/graphics/android/ClassTracker.h
new file mode 100644
index 0000000..0afde59
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ClassTracker.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 ClassTracker_h
+#define ClassTracker_h
+
+#include <wtf/HashMap.h>
+#include <wtf/text/StringHash.h>
+
+#define DEBUG_COUNT // Add instances tracking
+#undef DEBUG_COUNT
+
+namespace WebCore {
+
+class ClassTracker {
+ public:
+ static ClassTracker* instance();
+ void show();
+ void increment(String name);
+ void decrement(String name);
+ private:
+ ClassTracker() {};
+ HashMap<String, int> m_classes;
+ static ClassTracker* gInstance;
+};
+
+}
+
+#endif // ClassTracker_h
diff --git a/Source/WebCore/platform/graphics/android/DeleteTextureOperation.h b/Source/WebCore/platform/graphics/android/DeleteTextureOperation.h
new file mode 100644
index 0000000..32717a0
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/DeleteTextureOperation.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 DeleteTextureOperation_h
+#define DeleteTextureOperation_h
+
+#include "GLUtils.h"
+#include "QueuedOperation.h"
+
+namespace WebCore {
+
+class DeleteTextureOperation : public QueuedOperation {
+ public:
+ DeleteTextureOperation(GLuint textureId, EGLImageKHR eglImage)
+ : QueuedOperation(QueuedOperation::DeleteTexture, 0)
+ , m_textureId(textureId)
+ , m_eglImage(eglImage) {}
+ virtual bool operator==(const QueuedOperation* operation)
+ {
+ if (operation->type() != type())
+ return false;
+ const DeleteTextureOperation* op = static_cast<const DeleteTextureOperation*>(operation);
+ return op->m_textureId == m_textureId;
+ }
+ virtual void run()
+ {
+ if (m_textureId)
+ GLUtils::deleteTexture(&m_textureId);
+ if (m_eglImage)
+ eglDestroyImageKHR(eglGetCurrentDisplay(), m_eglImage);
+ }
+ private:
+ GLuint m_textureId;
+ EGLImageKHR m_eglImage;
+};
+
+}
+
+#endif // DeleteTextureOperation_h
diff --git a/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp b/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp
new file mode 100644
index 0000000..395bb2b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "DoubleBufferedTexture.h"
+
+#include "ClassTracker.h"
+#include "GLUtils.h"
+
+#define LOG_NDEBUG 1
+#define LOG_TAG "DoubleBufferedTexture.cpp"
+#include <utils/Log.h>
+
+namespace WebCore {
+
+DoubleBufferedTexture::DoubleBufferedTexture(EGLContext sharedContext)
+{
+ m_display = eglGetCurrentDisplay();
+ m_pContext = EGL_NO_CONTEXT;
+ m_cContext = sharedContext;
+ m_writeableTexture = &m_textureA;
+ m_lockedConsumerTexture = GL_NO_TEXTURE;
+ m_supportsEGLImage = GLUtils::isEGLImageSupported();
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("DoubleBufferedTexture");
+#endif
+}
+
+DoubleBufferedTexture::~DoubleBufferedTexture()
+{
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("DoubleBufferedTexture");
+#endif
+}
+
+SharedTexture* DoubleBufferedTexture::getWriteableTexture()
+{
+ return reinterpret_cast<SharedTexture*>(
+ android_atomic_release_load((int32_t*)&m_writeableTexture));
+}
+
+SharedTexture* DoubleBufferedTexture::getReadableTexture()
+{
+ return (getWriteableTexture() != &m_textureA) ? &m_textureA : &m_textureB;
+}
+
+EGLContext DoubleBufferedTexture::producerAcquireContext()
+{
+
+ if (m_pContext != EGL_NO_CONTEXT) {
+ LOGV("AquireContext has previously generated a context.\n");
+ return m_pContext;
+ }
+
+ // check to see if a context already exists on this thread
+ EGLContext context = eglGetCurrentContext();
+
+ // if no context exists then create one
+ if (context == EGL_NO_CONTEXT) {
+ EGLContext sharedContext = m_supportsEGLImage ? EGL_NO_CONTEXT : m_cContext;
+ context = GLUtils::createBackgroundContext(sharedContext);
+ }
+
+ if (context == EGL_NO_CONTEXT) {
+ LOGE("eglCreateContext failed");
+ return EGL_NO_CONTEXT;
+ }
+
+ // initialize the producer's textures
+ m_textureA.lock();
+ m_textureB.lock();
+ m_textureA.initSourceTexture();
+ m_textureB.initSourceTexture();
+ LOGV("Initialized Textures A/B (%d:%d)", m_textureA.getSourceTextureId(),
+ m_textureB.getSourceTextureId());
+ m_textureA.unlock();
+ m_textureB.unlock();
+
+ m_pContext = context;
+ return context;
+}
+
+void DoubleBufferedTexture::producerDeleteTextures()
+{
+ m_textureA.lock();
+ m_textureB.lock();
+ LOGV("Deleting Producer Textures A/B (%d:%d)", m_textureA.getSourceTextureId(),
+ m_textureB.getSourceTextureId());
+ m_textureA.deleteSourceTexture();
+ m_textureB.deleteSourceTexture();
+ m_textureA.unlock();
+ m_textureB.unlock();
+}
+
+void DoubleBufferedTexture::consumerDeleteTextures()
+{
+ m_textureA.lock();
+ m_textureB.lock();
+ LOGV("Deleting Consumer Textures A/B (%d:%d)", m_textureA.getTargetTextureId(),
+ m_textureB.getTargetTextureId());
+ m_textureA.deleteTargetTexture();
+ m_textureB.deleteTargetTexture();
+ m_textureA.unlock();
+ m_textureB.unlock();
+}
+
+TextureInfo* DoubleBufferedTexture::producerLock()
+{
+ SharedTexture* sharedTex = getWriteableTexture();
+ LOGV("Acquiring P Lock (%d)", sharedTex->getSourceTextureId());
+ TextureInfo* texInfo = sharedTex->lockSource();
+ LOGV("Acquired P Lock");
+
+ return texInfo;
+}
+
+void DoubleBufferedTexture::producerRelease()
+{
+ // get the writable texture and unlock it
+ SharedTexture* sharedTex = getWriteableTexture();
+ LOGV("Releasing P Lock (%d)", sharedTex->getSourceTextureId());
+ sharedTex->releaseSource();
+ LOGV("Released P Lock (%d)", sharedTex->getSourceTextureId());
+}
+
+void DoubleBufferedTexture::producerReleaseAndSwap()
+{
+ producerRelease();
+
+ // swap the front and back buffers using an atomic op for the memory barrier
+ android_atomic_acquire_store((int32_t)getReadableTexture(), (int32_t*)&m_writeableTexture);
+}
+
+TextureInfo* DoubleBufferedTexture::consumerLock()
+{
+ SharedTexture* sharedTex = getReadableTexture();
+ LOGV("Acquiring C Lock (%d)", sharedTex->getSourceTextureId());
+ m_lockedConsumerTexture = sharedTex;
+
+ TextureInfo* texInfo = sharedTex->lockTarget();
+ LOGV("Acquired C Lock");
+
+ if (!texInfo)
+ LOGV("Released C Lock (Empty)");
+
+ return texInfo;
+}
+
+void DoubleBufferedTexture::consumerRelease()
+{
+ // we must check to see what texture the consumer had locked since the
+ // producer may have swapped out the readable buffer
+ SharedTexture* sharedTex = m_lockedConsumerTexture;
+ sharedTex->releaseTarget();
+ LOGV("Released C Lock (%d)", sharedTex->getSourceTextureId());
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.h b/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.h
new file mode 100644
index 0000000..ba56c0e
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 DoubleBufferedTexture_h
+#define DoubleBufferedTexture_h
+
+#include "SharedTexture.h"
+#include <EGL/egl.h>
+
+namespace WebCore {
+
+class DoubleBufferedTexture {
+public:
+ // consumer thread functions
+ DoubleBufferedTexture(EGLContext sharedContext);
+ virtual ~DoubleBufferedTexture();
+
+ // provider thread functions
+ virtual TextureInfo* producerLock();
+ virtual void producerRelease();
+ virtual void producerReleaseAndSwap();
+ EGLContext producerAcquireContext();
+ void producerDeleteTextures();
+
+ // consumer thread functions
+ TextureInfo* consumerLock();
+ void consumerRelease();
+ void consumerDeleteTextures();
+
+protected:
+ SharedTexture* getReadableTexture();
+ SharedTexture* getWriteableTexture();
+
+ SharedTexture m_textureA;
+ SharedTexture m_textureB;
+
+private:
+
+ SharedTexture* m_writeableTexture;
+ SharedTexture* m_lockedConsumerTexture; // only used by the consumer
+
+ EGLDisplay m_display;
+ EGLContext m_pContext;
+ EGLContext m_cContext;
+
+ bool m_supportsEGLImage;
+};
+
+} // namespace WebCore
+
+#endif // DoubleBufferedTexture_h
diff --git a/Source/WebCore/platform/graphics/android/FontAndroid.cpp b/Source/WebCore/platform/graphics/android/FontAndroid.cpp
new file mode 100644
index 0000000..6966e7d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/FontAndroid.cpp
@@ -0,0 +1,1043 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "EmojiFont.h"
+#include "Font.h"
+#include "FontData.h"
+#include "FontFallbackList.h"
+#include "GraphicsContext.h"
+#include "GlyphBuffer.h"
+#include "IntRect.h"
+#include "NotImplemented.h"
+#include "PlatformGraphicsContext.h"
+#include "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkLayerDrawLooper.h"
+#include "SkPaint.h"
+#include "SkTemplates.h"
+#include "SkTypeface.h"
+#include "SkUtils.h"
+
+#ifdef SUPPORT_COMPLEX_SCRIPTS
+#include "HarfbuzzSkia.h"
+#include <unicode/normlzr.h>
+#include <unicode/uchar.h>
+#include <wtf/OwnArrayPtr.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/unicode/Unicode.h>
+#endif
+
+using namespace android;
+
+namespace WebCore {
+
+static void updateForFont(SkPaint* paint, const SimpleFontData* font) {
+ font->platformData().setupPaint(paint);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+}
+
+static SkPaint* setupFill(SkPaint* paint, GraphicsContext* gc,
+ const SimpleFontData* font) {
+ gc->setupFillPaint(paint);
+ updateForFont(paint, font);
+ return paint;
+}
+
+static SkPaint* setupStroke(SkPaint* paint, GraphicsContext* gc,
+ const SimpleFontData* font) {
+ gc->setupStrokePaint(paint);
+ updateForFont(paint, font);
+ return paint;
+}
+
+static bool setupForText(SkPaint* paint, GraphicsContext* gc,
+ const SimpleFontData* font) {
+ int mode = gc->textDrawingMode() & (TextModeFill | TextModeStroke);
+ if (!mode)
+ return false;
+
+ FloatSize shadowOffset;
+ float shadowBlur;
+ Color shadowColor;
+ ColorSpace shadowColorSpace;
+ bool hasShadow = gc->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
+ bool hasBothStrokeAndFill =
+ (mode & (TextModeStroke | TextModeFill)) == (TextModeStroke | TextModeFill);
+ if (hasShadow || hasBothStrokeAndFill) {
+ SkLayerDrawLooper* looper = new SkLayerDrawLooper;
+ paint->setLooper(looper)->unref();
+
+ // Specify the behavior of the looper
+ SkLayerDrawLooper::LayerInfo info;
+ info.fPaintBits = SkLayerDrawLooper::kEntirePaint_Bits;
+ info.fColorMode = SkXfermode::kSrc_Mode;
+
+ // The paint is only valid until the looper receives another call to
+ // addLayer(). Therefore, we must cache certain state for later use.
+ bool hasFillPaint = false;
+ bool hasStrokePaint = false;
+ SkScalar strokeWidth;
+
+ if ((mode & TextModeStroke) && gc->willStroke()) {
+ strokeWidth = setupStroke(looper->addLayer(info), gc, font)->getStrokeWidth();
+ hasStrokePaint = true;
+ }
+ if ((mode & TextModeFill) && gc->willFill()) {
+ setupFill(looper->addLayer(info), gc, font);
+ hasFillPaint = true;
+ }
+
+ if (hasShadow) {
+ SkPaint shadowPaint;
+ SkPoint offset;
+ if (gc->setupShadowPaint(&shadowPaint, &offset)) {
+
+ // add an offset to the looper when creating a shadow layer
+ info.fOffset.set(offset.fX, offset.fY);
+
+ SkPaint* p = looper->addLayer(info);
+ *p = shadowPaint;
+
+ // Currently, only GraphicsContexts associated with the
+ // HTMLCanvasElement have shadows ignore transforms set. This
+ // allows us to distinguish between CSS and Canvas shadows which
+ // have different rendering specifications.
+ if (gc->shadowsIgnoreTransforms()) {
+ SkColorFilter* cf = SkColorFilter::CreateModeFilter(p->getColor(),
+ SkXfermode::kSrcIn_Mode);
+ p->setColorFilter(cf)->unref();
+ } else { // in CSS
+ p->setShader(NULL);
+ }
+
+ if (hasStrokePaint && !hasFillPaint) {
+ // stroke the shadow if we have stroke but no fill
+ p->setStyle(SkPaint::kStroke_Style);
+ p->setStrokeWidth(strokeWidth);
+ }
+ updateForFont(p, font);
+ }
+ }
+ } else if (mode & TextModeFill) {
+ (void)setupFill(paint, gc, font);
+ } else if (mode & TextModeStroke) {
+ (void)setupStroke(paint, gc, font);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool Font::canReturnFallbackFontsForComplexText()
+{
+ return false;
+}
+
+void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
+ const GlyphBuffer& glyphBuffer, int from, int numGlyphs,
+ const FloatPoint& point) const
+{
+ // compile-time assert
+ SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t));
+
+ SkPaint paint;
+ if (!setupForText(&paint, gc, font)) {
+ return;
+ }
+
+ SkScalar x = SkFloatToScalar(point.x());
+ SkScalar y = SkFloatToScalar(point.y());
+ const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from);
+ const GlyphBufferAdvance* adv = glyphBuffer.advances(from);
+ SkAutoSTMalloc<32, SkPoint> storage(numGlyphs);
+ SkPoint* pos = storage.get();
+
+ SkCanvas* canvas = gc->platformContext()->mCanvas;
+
+ /* We need an array of [x,y,x,y,x,y,...], but webkit is giving us
+ point.xy + [width, height, width, height, ...], so we have to convert
+ */
+
+ if (EmojiFont::IsAvailable()) {
+ // set filtering, to make scaled images look nice(r)
+ paint.setFilterBitmap(true);
+
+ int localIndex = 0;
+ int localCount = 0;
+ for (int i = 0; i < numGlyphs; i++) {
+ if (EmojiFont::IsEmojiGlyph(glyphs[i])) {
+ if (localCount)
+ canvas->drawPosText(&glyphs[localIndex],
+ localCount * sizeof(uint16_t),
+ &pos[localIndex], paint);
+ EmojiFont::Draw(canvas, glyphs[i], x, y, paint);
+ // reset local index/count track for "real" glyphs
+ localCount = 0;
+ localIndex = i + 1;
+ } else {
+ pos[i].set(x, y);
+ localCount += 1;
+ }
+ x += SkFloatToScalar(adv[i].width());
+ y += SkFloatToScalar(adv[i].height());
+ }
+ // draw the last run of glyphs (if any)
+ if (localCount)
+ canvas->drawPosText(&glyphs[localIndex],
+ localCount * sizeof(uint16_t),
+ &pos[localIndex], paint);
+ } else {
+ for (int i = 0; i < numGlyphs; i++) {
+ pos[i].set(x, y);
+ x += SkFloatToScalar(adv[i].width());
+ y += SkFloatToScalar(adv[i].height());
+ }
+ canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint);
+ }
+}
+
+void Font::drawEmphasisMarksForComplexText(WebCore::GraphicsContext*, WebCore::TextRun const&, WTF::AtomicString const&, WebCore::FloatPoint const&, int, int) const
+{
+ notImplemented();
+}
+
+#ifndef SUPPORT_COMPLEX_SCRIPTS
+
+FloatRect Font::selectionRectForComplexText(const TextRun& run,
+ const FloatPoint& point, int h, int, int) const
+{
+ SkPaint paint;
+ SkScalar width, left;
+ SkPaint::FontMetrics metrics;
+
+ primaryFont()->platformData().setupPaint(&paint);
+
+ width = paint.measureText(run.characters(), run.length() << 1);
+ SkScalar spacing = paint.getFontMetrics(&metrics);
+
+ return FloatRect(point.x(),
+ point.y(),
+ roundf(SkScalarToFloat(width)),
+ roundf(SkScalarToFloat(spacing)));
+}
+
+void Font::drawComplexText(GraphicsContext* gc, TextRun const& run,
+ FloatPoint const& point, int, int) const
+{
+ SkCanvas* canvas = gc->platformContext()->mCanvas;
+ SkPaint paint;
+
+ if (!setupForText(&paint, gc, primaryFont())) {
+ return;
+ }
+
+ // go to chars, instead of glyphs, which was set by setupForText()
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+
+ canvas->drawText(run.characters(), run.length() << 1,
+ SkFloatToScalar(point.x()), SkFloatToScalar(point.y()),
+ paint);
+}
+
+float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
+{
+ SkPaint paint;
+
+ primaryFont()->platformData().setupPaint(&paint);
+
+//printf("--------- complext measure %d chars\n", run.to() - run.from());
+
+ SkScalar width = paint.measureText(run.characters(), run.length() << 1);
+ return SkScalarToFloat(width);
+}
+
+int Font::offsetForPositionForComplexText(const TextRun& run, float x,
+ bool includePartialGlyphs) const
+{
+ SkPaint paint;
+ int count = run.length();
+ SkAutoSTMalloc<64, SkScalar> storage(count);
+ SkScalar* widths = storage.get();
+
+ primaryFont()->platformData().setupPaint(&paint);
+
+ count = paint.getTextWidths(run.characters(), count << 1, widths);
+
+ if (count > 0)
+ {
+ SkScalar pos = 0;
+ for (int i = 0; i < count; i++)
+ {
+ if (x < SkScalarRound(pos + SkScalarHalf(widths[i])))
+ return i;
+ pos += widths[i];
+ }
+ }
+ return count;
+}
+
+#else
+
+// TODO Should we remove the multilayer support?
+// If yes. remove isCanvasMultiLayered() and adjustTextRenderMode().
+static bool isCanvasMultiLayered(SkCanvas* canvas)
+{
+ SkCanvas::LayerIter layerIterator(canvas, false);
+ layerIterator.next();
+ return !layerIterator.done();
+}
+
+static void adjustTextRenderMode(SkPaint* paint, bool isCanvasMultiLayered)
+{
+ // 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)
+ paint->setLCDRenderText(false);
+}
+
+// Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't
+// handle subpixel positioning so this function is used to truncate Harfbuzz
+// values to a number of pixels.
+static int truncateFixedPointToInteger(HB_Fixed value)
+{
+ return value >> 6;
+}
+
+// TextRunWalker walks a TextRun and presents each script run in sequence. A
+// TextRun is a sequence of code-points with the same embedding level (i.e. they
+// are all left-to-right or right-to-left). A script run is a subsequence where
+// all the characters have the same script (e.g. Arabic, Thai etc). Shaping is
+// only ever done with script runs since the shapers only know how to deal with
+// a single script.
+//
+// After creating it, the script runs are either iterated backwards or forwards.
+// It defaults to backwards for RTL and forwards otherwise (which matches the
+// presentation order), however you can set it with |setBackwardsIteration|.
+//
+// Once you have setup the object, call |nextScriptRun| to get the first script
+// run. This will return false when the iteration is complete. At any time you
+// can call |reset| to start over again.
+class TextRunWalker {
+public:
+ TextRunWalker(const TextRun&, unsigned, const Font*);
+ ~TextRunWalker();
+
+ bool isWordBreak(unsigned, bool);
+ // setPadding sets a number of pixels to be distributed across the TextRun.
+ // WebKit uses this to justify text.
+ void setPadding(int);
+ void reset();
+ void setBackwardsIteration(bool);
+ // Advance to the next script run, returning false when the end of the
+ // TextRun has been reached.
+ bool nextScriptRun();
+ float widthOfFullRun();
+
+ // setWordSpacingAdjustment sets a delta (in pixels) which is applied at
+ // each word break in the TextRun.
+ void setWordSpacingAdjustment(int wordSpacingAdjustment)
+ {
+ m_wordSpacingAdjustment = wordSpacingAdjustment;
+ }
+
+ // setLetterSpacingAdjustment sets an additional number of pixels that is
+ // added to the advance after each output cluster. This matches the behaviour
+ // of WidthIterator::advance.
+ //
+ // (NOTE: currently does nothing because I don't know how to get the
+ // cluster information from Harfbuzz.)
+ void setLetterSpacingAdjustment(int letterSpacingAdjustment)
+ {
+ m_letterSpacing = letterSpacingAdjustment;
+ }
+
+ // setWordAndLetterSpacing calls setWordSpacingAdjustment() and
+ // setLetterSpacingAdjustment() to override m_wordSpacingAdjustment
+ // and m_letterSpacing.
+ void setWordAndLetterSpacing(int wordSpacingAdjustment, int letterSpacingAdjustment);
+
+ // Set the x offset for the next script run. This affects the values in
+ // |xPositions|
+ void setXOffsetToZero() { m_offsetX = 0; }
+ bool rtl() const { return m_run.rtl(); }
+ const uint16_t* glyphs() const { return m_glyphs16; }
+
+ // Return the length of the array returned by |glyphs|
+ unsigned length() const { return m_item.num_glyphs; }
+
+ // Return the x offset for each of the glyphs. Note that this is translated
+ // by the current x offset and that the x offset is updated for each script
+ // run.
+ const SkScalar* xPositions() const { return m_xPositions; }
+
+ // Get the advances (widths) for each glyph.
+ const HB_Fixed* advances() const { return m_item.advances; }
+
+ // Return the width (in px) of the current script run.
+ unsigned width() const { return m_pixelWidth; }
+
+ // Return the cluster log for the current script run. For example:
+ // script run: f i a n c é (fi gets ligatured)
+ // log clutrs: 0 0 1 2 3 4
+ // So, for each input code point, the log tells you which output glyph was
+ // generated for it.
+ const unsigned short* logClusters() const { return m_item.log_clusters; }
+
+ // return the number of code points in the current script run
+ unsigned numCodePoints() const { return m_numCodePoints; }
+
+ const FontPlatformData* fontPlatformDataForScriptRun() {
+ return reinterpret_cast<FontPlatformData*>(m_item.font->userData);
+ }
+
+private:
+ void setupFontForScriptRun();
+ HB_FontRec* allocHarfbuzzFont();
+ void deleteGlyphArrays();
+ void createGlyphArrays(int);
+ void resetGlyphArrays();
+ void shapeGlyphs();
+ void setGlyphXPositions(bool);
+
+ static void normalizeSpacesAndMirrorChars(const UChar* source, bool rtl,
+ UChar* destination, int length);
+ static const TextRun& getNormalizedTextRun(const TextRun& originalRun,
+ OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer);
+
+ // This matches the logic in RenderBlock::findNextLineBreak
+ static bool isCodepointSpace(HB_UChar16 c) { return c == ' ' || c == '\t'; }
+
+ const Font* const m_font;
+ HB_ShaperItem m_item;
+ uint16_t* m_glyphs16; // A vector of 16-bit glyph ids.
+ SkScalar* m_xPositions; // A vector of x positions for each glyph.
+ ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|.
+ const unsigned m_startingX; // Offset in pixels of the first script run.
+ unsigned m_offsetX; // Offset in pixels to the start of the next script run.
+ unsigned m_pixelWidth; // Width (in px) of the current script run.
+ unsigned m_numCodePoints; // Code points in current script run.
+ unsigned m_glyphsArrayCapacity; // Current size of all the Harfbuzz arrays.
+
+ OwnPtr<TextRun> m_normalizedRun;
+ OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run.
+ const TextRun& m_run;
+ bool m_iterateBackwards;
+ int m_wordSpacingAdjustment; // delta adjustment (pixels) for each word break.
+ float m_padding; // pixels to be distributed over the line at word breaks.
+ float m_padPerWordBreak; // pixels to be added to each word break.
+ float m_padError; // |m_padPerWordBreak| might have a fractional component.
+ // Since we only add a whole number of padding pixels at
+ // each word break we accumulate error. This is the
+ // number of pixels that we are behind so far.
+ unsigned m_letterSpacing; // pixels to be added after each glyph.
+};
+
+
+TextRunWalker::TextRunWalker(const TextRun& run, unsigned startingX, const Font* font)
+ : m_font(font)
+ , m_startingX(startingX)
+ , m_offsetX(m_startingX)
+ , m_run(getNormalizedTextRun(run, m_normalizedRun, m_normalizedBuffer))
+ , m_iterateBackwards(m_run.rtl())
+ , m_wordSpacingAdjustment(0)
+ , m_padding(0)
+ , m_padPerWordBreak(0)
+ , m_padError(0)
+ , m_letterSpacing(0)
+{
+ // Do not use |run| inside this constructor. Use |m_run| instead.
+
+ memset(&m_item, 0, sizeof(m_item));
+ // We cannot know, ahead of time, how many glyphs a given script run
+ // will produce. We take a guess that script runs will not produce more
+ // than twice as many glyphs as there are code points plus a bit of
+ // padding and fallback if we find that we are wrong.
+ createGlyphArrays((m_run.length() + 2) * 2);
+
+ m_item.log_clusters = new unsigned short[m_run.length()];
+
+ m_item.face = 0;
+ m_item.font = allocHarfbuzzFont();
+
+ m_item.item.bidiLevel = m_run.rtl();
+
+ m_item.string = m_run.characters();
+ m_item.stringLength = m_run.length();
+
+ reset();
+}
+
+TextRunWalker::~TextRunWalker()
+{
+ fastFree(m_item.font);
+ deleteGlyphArrays();
+ delete[] m_item.log_clusters;
+}
+
+bool TextRunWalker::isWordBreak(unsigned index, bool isRTL)
+{
+ if (!isRTL)
+ return index && isCodepointSpace(m_item.string[index])
+ && !isCodepointSpace(m_item.string[index - 1]);
+ return index != m_item.stringLength - 1 && isCodepointSpace(m_item.string[index])
+ && !isCodepointSpace(m_item.string[index + 1]);
+}
+
+// setPadding sets a number of pixels to be distributed across the TextRun.
+// WebKit uses this to justify text.
+void TextRunWalker::setPadding(int padding)
+{
+ m_padding = padding;
+ if (!m_padding)
+ return;
+
+ // If we have padding to distribute, then we try to give an equal
+ // amount to each space. The last space gets the smaller amount, if
+ // any.
+ unsigned numWordBreaks = 0;
+ bool isRTL = m_iterateBackwards;
+
+ for (unsigned i = 0; i < m_item.stringLength; i++) {
+ if (isWordBreak(i, isRTL))
+ numWordBreaks++;
+ }
+
+ if (numWordBreaks)
+ m_padPerWordBreak = m_padding / numWordBreaks;
+ else
+ m_padPerWordBreak = 0;
+}
+
+void TextRunWalker::reset()
+{
+ if (m_iterateBackwards)
+ m_indexOfNextScriptRun = m_run.length() - 1;
+ else
+ m_indexOfNextScriptRun = 0;
+ m_offsetX = m_startingX;
+}
+
+void TextRunWalker::setBackwardsIteration(bool isBackwards)
+{
+ m_iterateBackwards = isBackwards;
+ reset();
+}
+
+// Advance to the next script run, returning false when the end of the
+// TextRun has been reached.
+bool TextRunWalker::nextScriptRun()
+{
+ if (m_iterateBackwards) {
+ // In right-to-left mode we need to render the shaped glyph backwards and
+ // also render the script runs themselves backwards. So given a TextRun:
+ // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai)
+ // we render:
+ // TTTTTTCAAAAAAA
+ // (and the glyphs in each A, C and T section are backwards too)
+ if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(),
+ m_run.length(), &m_indexOfNextScriptRun))
+ return false;
+ } else {
+ if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(),
+ m_run.length(), &m_indexOfNextScriptRun))
+ return false;
+
+ // It is actually wrong to consider script runs at all in this code.
+ // Other WebKit code (e.g. Mac) segments complex text just by finding
+ // the longest span of text covered by a single font.
+ // But we currently need to call hb_utf16_script_run_next anyway to fill
+ // in the harfbuzz data structures to e.g. pick the correct script's shaper.
+ // So we allow that to run first, then do a second pass over the range it
+ // found and take the largest subregion that stays within a single font.
+ const FontData* glyphData = m_font->glyphDataForCharacter(
+ m_item.string[m_item.item.pos], false).fontData;
+ unsigned endOfRun;
+ for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) {
+ const FontData* nextGlyphData = m_font->glyphDataForCharacter(
+ m_item.string[m_item.item.pos + endOfRun], false).fontData;
+ if (nextGlyphData != glyphData)
+ break;
+ }
+ m_item.item.length = endOfRun;
+ m_indexOfNextScriptRun = m_item.item.pos + endOfRun;
+ }
+
+ setupFontForScriptRun();
+ shapeGlyphs();
+ setGlyphXPositions(rtl());
+
+ return true;
+}
+
+float TextRunWalker::widthOfFullRun()
+{
+ float widthSum = 0;
+ while (nextScriptRun())
+ widthSum += width();
+
+ return widthSum;
+}
+
+void TextRunWalker::setWordAndLetterSpacing(int wordSpacingAdjustment,
+ int letterSpacingAdjustment)
+{
+ setWordSpacingAdjustment(wordSpacingAdjustment);
+ setLetterSpacingAdjustment(letterSpacingAdjustment);
+}
+
+void TextRunWalker::setupFontForScriptRun()
+{
+ const FontData* fontData = m_font->glyphDataForCharacter(
+ m_item.string[m_item.item.pos], false).fontData;
+ const FontPlatformData& platformData =
+ fontData->fontDataForCharacter(' ')->platformData();
+ m_item.face = platformData.harfbuzzFace();
+ void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData);
+ m_item.font->userData = opaquePlatformData;
+}
+
+HB_FontRec* TextRunWalker::allocHarfbuzzFont()
+{
+ HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
+ memset(font, 0, sizeof(HB_FontRec));
+ font->klass = &harfbuzzSkiaClass;
+ font->userData = 0;
+ // The values which harfbuzzSkiaClass returns are already scaled to
+ // pixel units, so we just set all these to one to disable further
+ // scaling.
+ font->x_ppem = 1;
+ font->y_ppem = 1;
+ font->x_scale = 1;
+ font->y_scale = 1;
+
+ return font;
+}
+
+void TextRunWalker::deleteGlyphArrays()
+{
+ delete[] m_item.glyphs;
+ delete[] m_item.attributes;
+ delete[] m_item.advances;
+ delete[] m_item.offsets;
+ delete[] m_glyphs16;
+ delete[] m_xPositions;
+}
+
+void TextRunWalker::createGlyphArrays(int size)
+{
+ m_item.glyphs = new HB_Glyph[size];
+ m_item.attributes = new HB_GlyphAttributes[size];
+ m_item.advances = new HB_Fixed[size];
+ m_item.offsets = new HB_FixedPoint[size];
+
+ m_glyphs16 = new uint16_t[size];
+ m_xPositions = new SkScalar[size];
+
+ m_item.num_glyphs = size;
+ m_glyphsArrayCapacity = size; // Save the GlyphArrays size.
+}
+
+void TextRunWalker::resetGlyphArrays()
+{
+ int size = m_item.num_glyphs;
+ // All the types here don't have pointers. It is safe to reset to
+ // zero unless Harfbuzz breaks the compatibility in the future.
+ memset(m_item.glyphs, 0, size * sizeof(m_item.glyphs[0]));
+ memset(m_item.attributes, 0, size * sizeof(m_item.attributes[0]));
+ memset(m_item.advances, 0, size * sizeof(m_item.advances[0]));
+ memset(m_item.offsets, 0, size * sizeof(m_item.offsets[0]));
+ memset(m_glyphs16, 0, size * sizeof(m_glyphs16[0]));
+ memset(m_xPositions, 0, size * sizeof(m_xPositions[0]));
+}
+
+void TextRunWalker::shapeGlyphs()
+{
+ // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to
+ // HB_ShapeItem() used less space than was available, the capacity of
+ // the array may be larger than the current value of m_item.num_glyphs.
+ // So, we need to reset the num_glyphs to the capacity of the array.
+ m_item.num_glyphs = m_glyphsArrayCapacity;
+ resetGlyphArrays();
+ while (!HB_ShapeItem(&m_item)) {
+ // 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 << 1);
+ resetGlyphArrays();
+ }
+}
+
+void TextRunWalker::setGlyphXPositions(bool isRTL)
+{
+ int position = 0;
+ // logClustersIndex indexes logClusters for the first (or last when
+ // RTL) codepoint of the current glyph. Each time we advance a glyph,
+ // we skip over all the codepoints that contributed to the current
+ // glyph.
+ unsigned logClustersIndex = isRTL && m_item.num_glyphs ? m_item.num_glyphs - 1 : 0;
+
+ for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) {
+ // Glyphs are stored in logical order, but for layout purposes we
+ // always go left to right.
+ int i = isRTL ? m_item.num_glyphs - iter - 1 : iter;
+
+ m_glyphs16[i] = m_item.glyphs[i];
+ int offsetX = truncateFixedPointToInteger(m_item.offsets[i].x);
+ m_xPositions[i] = SkIntToScalar(m_offsetX + position + offsetX);
+
+ int advance = truncateFixedPointToInteger(m_item.advances[i]);
+ // The first half of the conjunction 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) {
+ int toPad = roundf(m_padPerWordBreak + m_padError);
+ m_padError += m_padPerWordBreak - toPad;
+
+ if (m_padding < toPad)
+ toPad = m_padding;
+ m_padding -= toPad;
+ advance += toPad;
+ }
+ }
+
+ // TODO We would like to add m_letterSpacing after each cluster, but I
+ // don't know where the cluster information is. This is typically
+ // fine for Roman languages, but breaks more complex languages
+ // terribly.
+ // advance += m_letterSpacing;
+
+ if (isRTL) {
+ while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i)
+ logClustersIndex--;
+ } else {
+ while (logClustersIndex < m_item.item.length
+ && logClusters()[logClustersIndex] == i)
+ logClustersIndex++;
+ }
+
+ position += advance;
+ }
+
+ m_pixelWidth = position;
+ m_offsetX += m_pixelWidth;
+}
+
+void TextRunWalker::normalizeSpacesAndMirrorChars(const UChar* source, bool rtl,
+ UChar* destination, int length)
+{
+ int position = 0;
+ bool error = false;
+ // Iterate characters in source and mirror character if needed.
+ while (position < length) {
+ UChar32 character;
+ int nextPosition = position;
+ U16_NEXT(source, nextPosition, length, character);
+ if (Font::treatAsSpace(character))
+ character = ' ';
+ else if (rtl)
+ character = u_charMirror(character);
+ U16_APPEND(destination, position, length, character, error);
+ ASSERT(!error);
+ position = nextPosition;
+ }
+}
+
+const TextRun& TextRunWalker::getNormalizedTextRun(const TextRun& originalRun,
+ OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer)
+{
+ // Normalize the text run in three ways:
+ // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks
+ // (U+0300..) are used in the run. This conversion is necessary since most OpenType
+ // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in
+ // their GSUB tables.
+ //
+ // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since
+ // the API returns FALSE (= not normalized) for complex runs that don't require NFC
+ // normalization (e.g., Arabic text). Unless the run contains the diacritical marks,
+ // Harfbuzz will do the same thing for us using the GSUB table.
+ // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs
+ // for characters like '\n' otherwise.
+ // 3) Convert mirrored characters such as parenthesis for rtl text.
+
+ // Convert to NFC form if the text has diacritical marks.
+ icu::UnicodeString normalizedString;
+ UErrorCode error = U_ZERO_ERROR;
+
+ for (int16_t i = 0; i < originalRun.length(); ++i) {
+ UChar ch = originalRun[i];
+ if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
+ icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(),
+ originalRun.length()), UNORM_NFC, 0 /* no options */,
+ normalizedString, error);
+ if (U_FAILURE(error))
+ return originalRun;
+ break;
+ }
+ }
+
+ // Normalize space and mirror parenthesis for rtl text.
+ int normalizedBufferLength;
+ const UChar* sourceText;
+ if (normalizedString.isEmpty()) {
+ normalizedBufferLength = originalRun.length();
+ sourceText = originalRun.characters();
+ } else {
+ normalizedBufferLength = normalizedString.length();
+ sourceText = normalizedString.getBuffer();
+ }
+
+ normalizedBuffer.set(new UChar[normalizedBufferLength + 1]);
+
+ normalizeSpacesAndMirrorChars(sourceText, originalRun.rtl(), normalizedBuffer.get(),
+ normalizedBufferLength);
+
+ normalizedRun.set(new TextRun(originalRun));
+ normalizedRun->setText(normalizedBuffer.get(), normalizedBufferLength);
+ return *normalizedRun;
+}
+
+FloatRect Font::selectionRectForComplexText(const TextRun& run,
+ const FloatPoint& point, int height, int from, int to) const
+{
+
+ int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1;
+ TextRunWalker walker(run, 0, this);
+ walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
+
+ // Base will point to the x offset for the current script run. Note that, in
+ // the LTR case, width will be 0.
+ int base = walker.rtl() ? walker.widthOfFullRun() : 0;
+ const int leftEdge = base;
+
+ // We want to enumerate the script runs in code point order in the following
+ // code. This call also resets |walker|.
+ walker.setBackwardsIteration(false);
+
+ while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) {
+ // TextRunWalker will helpfully accumulate the x offsets for different
+ // script runs for us. For this code, however, we always want the x
+ // offsets to start from zero so we call this before each script run.
+ walker.setXOffsetToZero();
+
+ if (walker.rtl())
+ base -= walker.width();
+
+ int numCodePoints = static_cast<int>(walker.numCodePoints());
+ if (fromX == -1 && from < numCodePoints) {
+ // |from| is within this script run. So we index the clusters log to
+ // find which glyph this code-point contributed to and find its x
+ // position.
+ int glyph = walker.logClusters()[from];
+ fromX = base + walker.xPositions()[glyph];
+ fromAdvance = walker.advances()[glyph];
+ } else
+ from -= numCodePoints;
+
+ if (toX == -1 && to < numCodePoints) {
+ int glyph = walker.logClusters()[to];
+ toX = base + walker.xPositions()[glyph];
+ toAdvance = walker.advances()[glyph];
+ } else
+ to -= numCodePoints;
+
+ if (!walker.rtl())
+ base += walker.width();
+ }
+
+ // The position in question might be just after the text.
+ const int rightEdge = base;
+ if (fromX == -1 && !from)
+ fromX = leftEdge;
+ else if (walker.rtl())
+ fromX += truncateFixedPointToInteger(fromAdvance);
+
+ if (toX == -1 && !to)
+ toX = rightEdge;
+
+ ASSERT(fromX != -1 && toX != -1);
+
+ if (fromX < toX)
+ return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
+
+ return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
+}
+
+void Font::drawComplexText(GraphicsContext* gc, TextRun const& run,
+ FloatPoint const& point, int, int) const
+{
+ if (!run.length())
+ return;
+
+ int mode = gc->textDrawingMode();
+ bool fill = mode & TextModeFill;
+ bool stroke = mode & TextModeStroke;
+ if (!fill && !stroke)
+ return;
+
+ SkPaint fillPaint, strokePaint;
+ if (fill)
+ setupFill(&fillPaint, gc, primaryFont());
+ if (stroke)
+ setupStroke(&strokePaint, gc, primaryFont());
+
+ SkCanvas* canvas = gc->platformContext()->mCanvas;
+ bool haveMultipleLayers = isCanvasMultiLayered(canvas);
+ TextRunWalker walker(run, point.x(), this);
+ walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
+ walker.setPadding(run.padding());
+
+ while (walker.nextScriptRun()) {
+ if (fill) {
+ walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
+ adjustTextRenderMode(&fillPaint, haveMultipleLayers);
+ canvas->drawPosTextH(walker.glyphs(), walker.length() << 1,
+ walker.xPositions(), point.y(), fillPaint);
+ }
+ if (stroke) {
+ walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
+ adjustTextRenderMode(&strokePaint, haveMultipleLayers);
+ canvas->drawPosTextH(walker.glyphs(), walker.length() << 1,
+ walker.xPositions(), point.y(), strokePaint);
+ }
+ }
+}
+
+float Font::floatWidthForComplexText(const TextRun& run,
+ HashSet<const SimpleFontData*>*, GlyphOverflow*) const
+{
+ TextRunWalker walker(run, 0, this);
+ walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
+ return walker.widthOfFullRun();
+}
+
+static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x)
+{
+ const HB_Fixed* advances = walker.advances();
+ int glyphIndex;
+ if (walker.rtl()) {
+ for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) {
+ if (x < truncateFixedPointToInteger(advances[glyphIndex]))
+ break;
+ x -= truncateFixedPointToInteger(advances[glyphIndex]);
+ }
+ } else {
+ for (glyphIndex = 0; glyphIndex < static_cast<int>(walker.length());
+ ++glyphIndex) {
+ if (x < truncateFixedPointToInteger(advances[glyphIndex]))
+ break;
+ x -= truncateFixedPointToInteger(advances[glyphIndex]);
+ }
+ }
+
+ return glyphIndex;
+}
+
+int Font::offsetForPositionForComplexText(const TextRun& run, float x,
+ bool includePartialGlyphs) const
+{
+ // (Mac code ignores includePartialGlyphs, and they don't know what it's
+ // supposed to do, so we just ignore it as well.)
+ TextRunWalker walker(run, 0, this);
+ walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());
+
+ // If this is RTL text, the first glyph from the left is actually the last
+ // code point. So we need to know how many code points there are total in
+ // order to subtract. This is different from the length of the TextRun
+ // because UTF-16 surrogate pairs are a single code point, but 32-bits long.
+ // In LTR we leave this as 0 so that we get the correct value for
+ // |basePosition|, below.
+ unsigned totalCodePoints = 0;
+ if (walker.rtl()) {
+ ssize_t offset = 0;
+ while (offset < run.length()) {
+ utf16_to_code_point(run.characters(), run.length(), &offset);
+ totalCodePoints++;
+ }
+ }
+
+ unsigned basePosition = totalCodePoints;
+
+ // For RTL:
+ // code-point order: abcd efg hijkl
+ // on screen: lkjih gfe dcba
+ // ^ ^
+ // | |
+ // basePosition--| |
+ // totalCodePoints----|
+ // Since basePosition is currently the total number of code-points, the
+ // first thing we do is decrement it so that it's pointing to the start of
+ // the current script-run.
+ //
+ // For LTR, basePosition is zero so it already points to the start of the
+ // first script run.
+ while (walker.nextScriptRun()) {
+ if (walker.rtl())
+ basePosition -= walker.numCodePoints();
+
+ if (x >= 0 && x < static_cast<int>(walker.width())) {
+ // The x value in question is within this script run. We consider
+ // each glyph in presentation order and stop when we find the one
+ // covering this position.
+ const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x);
+
+ // Now that we have a glyph index, we have to turn that into a
+ // code-point index. Because of ligatures, several code-points may
+ // have gone into a single glyph. We iterate over the clusters log
+ // and find the first code-point which contributed to the glyph.
+
+ // Some shapers (i.e. Khmer) will produce cluster logs which report
+ // that /no/ code points contributed to certain glyphs. Because of
+ // this, we take any code point which contributed to the glyph in
+ // question, or any subsequent glyph. If we run off the end, then
+ // we take the last code point.
+ const unsigned short* log = walker.logClusters();
+ for (unsigned j = 0; j < walker.numCodePoints(); ++j) {
+ if (log[j] >= glyphIndex)
+ return basePosition + j;
+ }
+
+ return basePosition + walker.numCodePoints() - 1;
+ }
+
+ x -= walker.width();
+
+ if (!walker.rtl())
+ basePosition += walker.numCodePoints();
+ }
+
+ return basePosition;
+}
+#endif
+
+}
diff --git a/Source/WebCore/platform/graphics/android/FontCacheAndroid.cpp b/Source/WebCore/platform/graphics/android/FontCacheAndroid.cpp
new file mode 100644
index 0000000..428628c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/FontCacheAndroid.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FontCache.h"
+
+#include "Font.h"
+#include "FontPlatformData.h"
+#include "NotImplemented.h"
+#include "SimpleFontData.h"
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include "SkUtils.h"
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+static const char* getFallbackFontName(const FontDescription& fontDescription)
+{
+ switch (fontDescription.genericFamily()) {
+ case FontDescription::StandardFamily:
+ case FontDescription::SerifFamily:
+ return "serif";
+ case FontDescription::SansSerifFamily:
+ return "sans-serif";
+ case FontDescription::MonospaceFamily:
+ return "monospace";
+ case FontDescription::CursiveFamily:
+ return "cursive";
+ case FontDescription::FantasyFamily:
+ return "fantasy";
+ case FontDescription::NoFamily:
+ default:
+ return "";
+ }
+}
+
+static bool isFallbackFamily(String family)
+{
+ return family.startsWith("-webkit-")
+ || equalIgnoringCase(family, "serif")
+ || equalIgnoringCase(family, "sans-serif")
+ || equalIgnoringCase(family, "sans")
+ || equalIgnoringCase(family, "monospace")
+ || equalIgnoringCase(family, "cursive")
+ || equalIgnoringCase(family, "fantasy");
+}
+
+static char* AtomicStringToUTF8String(const AtomicString& utf16)
+{
+ SkASSERT(sizeof(uint16_t) == sizeof(utf16.characters()[0]));
+ const uint16_t* uni = (uint16_t*)utf16.characters();
+
+ size_t bytes = SkUTF16_ToUTF8(uni, utf16.length(), 0);
+ char* utf8 = (char*)sk_malloc_throw(bytes + 1);
+
+ (void)SkUTF16_ToUTF8(uni, utf16.length(), utf8);
+ utf8[bytes] = 0;
+ return utf8;
+}
+
+
+void FontCache::platformInit()
+{
+}
+
+const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
+{
+ // since all of our fonts logically map to the fallback, we can always claim
+ // that each font supports all characters.
+ return font.primaryFont();
+}
+
+SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font)
+{
+ return 0;
+}
+
+SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& description)
+{
+ static const AtomicString sansStr("sans-serif");
+ static const AtomicString serifStr("serif");
+ static const AtomicString monospaceStr("monospace");
+
+ FontPlatformData* fontPlatformData = 0;
+ switch (description.genericFamily()) {
+ case FontDescription::SerifFamily:
+ fontPlatformData = getCachedFontPlatformData(description, serifStr);
+ break;
+ case FontDescription::MonospaceFamily:
+ fontPlatformData = getCachedFontPlatformData(description, monospaceStr);
+ break;
+ case FontDescription::SansSerifFamily:
+ default:
+ fontPlatformData = getCachedFontPlatformData(description, sansStr);
+ break;
+ }
+
+ ASSERT(fontPlatformData);
+ return getCachedFontData(fontPlatformData);
+}
+
+FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
+{
+ char* storage = 0;
+ const char* name = 0;
+ FontPlatformData* result = 0;
+
+ if (family.length()) {
+ storage = AtomicStringToUTF8String(family);
+ name = storage;
+ } else
+ name = getFallbackFontName(fontDescription);
+
+ int style = SkTypeface::kNormal;
+ if (fontDescription.weight() >= FontWeightBold)
+ style |= SkTypeface::kBold;
+ if (fontDescription.italic())
+ style |= SkTypeface::kItalic;
+
+ // CreateFromName always returns a typeface, falling back to a default font
+ // if the one requested is not found. Calling Equal() with a null pointer
+ // serves to compare the returned font against the default, with the caveat
+ // that the default is always of normal style. If we detect the default, we
+ // ignore it and allow WebCore to give us the next font on the CSS fallback
+ // list. The only exception is if the family name is a commonly used generic
+ // family, as when called by getSimilarFontPlatformData() and
+ // getLastResortFallbackFont(). In this case, the default font is an
+ // acceptable result.
+
+ SkTypeface* tf = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
+
+ if (!SkTypeface::Equal(tf, 0) || isFallbackFamily(family.string())) {
+ // We had to use normal styling to see if this was a default font. If
+ // we need bold or italic, replace with the corrected typeface.
+ if (style != SkTypeface::kNormal) {
+ tf->unref();
+ tf = SkTypeface::CreateFromName(name, (SkTypeface::Style)style);
+ }
+
+ result = new FontPlatformData(tf, fontDescription.computedSize(),
+ (style & SkTypeface::kBold) && !tf->isBold(),
+ (style & SkTypeface::kItalic) && !tf->isItalic());
+ }
+
+ tf->unref();
+ sk_free(storage);
+ return result;
+}
+
+ // new as of SVN change 36269, Sept 8, 2008
+void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
+{
+ // Don't understand this yet, but it seems safe to leave unimplemented
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/android/FontCustomPlatformData.cpp
new file mode 100644
index 0000000..4795d9e
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/FontCustomPlatformData.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FontCustomPlatformData.h"
+
+#include "SkTypeface.h"
+#include "SkStream.h"
+#include "SharedBuffer.h"
+#include "FontPlatformData.h"
+
+namespace WebCore {
+
+FontCustomPlatformData::FontCustomPlatformData(SkTypeface* face)
+{
+ SkSafeRef(face);
+ m_typeface = face;
+}
+
+FontCustomPlatformData::~FontCustomPlatformData()
+{
+ SkSafeUnref(m_typeface);
+ // the unref is enough to release the font data...
+}
+
+FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic,
+ FontOrientation, FontRenderingMode)
+{
+ // turn bold/italic into fakeBold/fakeItalic
+ if (m_typeface != NULL) {
+ if (m_typeface->isBold() == bold)
+ bold = false;
+ if (m_typeface->isItalic() == italic)
+ italic = false;
+ }
+ return FontPlatformData(m_typeface, size, bold, italic);
+}
+
+FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer)
+{
+ // pass true until we know how we can share the data, and not have to
+ // make a copy of it.
+ SkStream* stream = new SkMemoryStream(buffer->data(), buffer->size(), true);
+ SkTypeface* face = SkTypeface::CreateFromStream(stream);
+ // Release the stream.
+ stream->unref();
+ if (0 == face) {
+ SkDebugf("--------- SkTypeface::CreateFromBuffer failed %d\n",
+ buffer->size());
+ return NULL;
+ }
+
+ SkAutoUnref aur(face);
+
+ return new FontCustomPlatformData(face);
+}
+
+bool FontCustomPlatformData::supportsFormat(const String& format)
+{
+ return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype")
+#if ENABLE(OPENTYPE_SANITIZER)
+ || equalIgnoringCase(format, "woff")
+#endif
+ ;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/android/FontCustomPlatformData.h
new file mode 100644
index 0000000..3514ae7
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/FontCustomPlatformData.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FontCustomPlatformData_h
+#define FontCustomPlatformData_h
+
+#include "FontOrientation.h"
+#include "FontRenderingMode.h"
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+
+class SkTypeface;
+
+namespace WebCore {
+
+class FontPlatformData;
+class SharedBuffer;
+
+class FontCustomPlatformData : public Noncopyable {
+public:
+ FontCustomPlatformData(SkTypeface* face);
+ ~FontCustomPlatformData();
+
+ SkTypeface* typeface() const { return m_typeface; }
+
+ FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode);
+
+ static bool supportsFormat(const String&);
+private:
+ SkTypeface* m_typeface;
+};
+
+FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer);
+
+} // namespace WebCore
+
+#endif // FontCustomPlatformData_h
diff --git a/Source/WebCore/platform/graphics/android/FontDataAndroid.cpp b/Source/WebCore/platform/graphics/android/FontDataAndroid.cpp
new file mode 100644
index 0000000..545dcf7
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/FontDataAndroid.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "EmojiFont.h"
+#include "Font.h"
+#include "FontCache.h"
+#include "SimpleFontData.h"
+#include "FloatRect.h"
+#include "FontDescription.h"
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include "SkTime.h"
+
+using namespace android;
+
+namespace WebCore {
+
+void SimpleFontData::platformInit()
+{
+ SkPaint paint;
+ SkPaint::FontMetrics metrics;
+
+ m_platformData.setupPaint(&paint);
+ (void)paint.getFontMetrics(&metrics);
+
+ // use ceil instead of round to favor descent, given a lot of accidental
+ // clipping of descenders (e.g. 14pt 'g') in textedit fields
+ int d = SkScalarCeil(metrics.fDescent);
+ int s = SkScalarRound(metrics.fDescent - metrics.fAscent);
+ int a = s - d;
+
+ m_ascent = a;
+ m_descent = d;
+ m_xHeight = SkScalarToFloat(-metrics.fAscent) * 0.56f; // hack I stole from the window's port
+ m_lineSpacing = a + d;
+ m_lineGap = SkScalarRound(metrics.fLeading);
+}
+
+void SimpleFontData::platformCharWidthInit()
+{
+ m_avgCharWidth = 0.f;
+ m_maxCharWidth = 0.f;
+ initCharWidths();
+}
+
+void SimpleFontData::platformDestroy()
+{
+}
+
+SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
+{
+ if (!m_derivedFontData)
+ m_derivedFontData = DerivedFontData::create(isCustomFont());
+ if (!m_derivedFontData->smallCaps)
+ m_derivedFontData->smallCaps = new SimpleFontData(FontPlatformData(m_platformData, fontDescription.computedSize() * 0.7f));
+
+ return m_derivedFontData->smallCaps.get();
+}
+
+SimpleFontData* SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const
+{
+ if (!m_derivedFontData)
+ m_derivedFontData = DerivedFontData::create(isCustomFont());
+ if (!m_derivedFontData->emphasisMark)
+ m_derivedFontData->emphasisMark = new SimpleFontData(FontPlatformData(m_platformData, fontDescription.computedSize() * 0.5f));
+
+ return m_derivedFontData->emphasisMark.get();
+}
+
+bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
+{
+ SkPaint paint;
+
+ m_platformData.setupPaint(&paint);
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ return paint.containsText(characters, length << 1);
+}
+
+void SimpleFontData::determinePitch()
+{
+ m_treatAsFixedPitch = m_platformData.isFixedPitch();
+}
+
+FloatRect SimpleFontData::platformBoundsForGlyph(Glyph) const
+{
+ return FloatRect();
+}
+
+float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
+{
+ SkASSERT(sizeof(glyph) == 2); // compile-time assert
+
+ SkPaint paint;
+
+ m_platformData.setupPaint(&paint);
+
+ float advanceWidth;
+ if (EmojiFont::IsEmojiGlyph(glyph))
+ advanceWidth = EmojiFont::GetAdvanceWidth(glyph, paint);
+ else {
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ advanceWidth = SkScalarToFloat(paint.measureText(&glyph, 2));
+ }
+ return advanceWidth;
+}
+
+
+}
diff --git a/Source/WebCore/platform/graphics/android/FontPlatformData.h b/Source/WebCore/platform/graphics/android/FontPlatformData.h
new file mode 100644
index 0000000..ba30efe
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/FontPlatformData.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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.
+ */
+
+// This file is part of the internal font implementation. It should not be included by anyone other than
+// FontMac.cpp, FontWin.cpp and Font.cpp.
+
+#ifndef FontPlatformData_H
+#define FontPlatformData_H
+
+#include "FontOrientation.h"
+#include <wtf/text/StringImpl.h>
+
+#ifndef NDEBUG
+#include "PlatformString.h"
+#endif
+
+class SkPaint;
+class SkTypeface;
+
+struct HB_FaceRec_;
+
+namespace WebCore {
+
+class FontPlatformData {
+public:
+ static FontPlatformData Deleted() {
+ return FontPlatformData(NULL, -1, false, false);
+ }
+
+ FontPlatformData();
+ FontPlatformData(const FontPlatformData&);
+ FontPlatformData(SkTypeface*, float textSize, bool fakeBold, bool fakeItalic);
+ FontPlatformData(const FontPlatformData& src, float textSize);
+ FontPlatformData(float size, bool syntheticBold, bool syntheticOblique);
+
+ ~FontPlatformData();
+
+ FontPlatformData(WTF::HashTableDeletedValueType)
+ : mTypeface(hashTableDeletedFontValue()) { }
+ bool isHashTableDeletedValue() const {
+ return mTypeface == hashTableDeletedFontValue();
+ }
+
+ FontOrientation orientation() const { return Horizontal; } // FIXME: Implement.
+
+ FontPlatformData& operator=(const FontPlatformData&);
+ bool operator==(const FontPlatformData& a) const;
+
+ void setupPaint(SkPaint*) const;
+
+ // -------------------------------------------------------------------------
+ // Return Skia's unique id for this font. This encodes both the style and
+ // the font's file name so refers to a single face.
+ // -------------------------------------------------------------------------
+ uint32_t uniqueID() const;
+
+ float size() const { return mTextSize; }
+ unsigned hash() const;
+ bool isFixedPitch() const;
+
+#ifndef NDEBUG
+ String description() const { return ""; }
+#endif
+
+ HB_FaceRec_* harfbuzzFace() const;
+
+private:
+ class RefCountedHarfbuzzFace : public RefCounted<RefCountedHarfbuzzFace> {
+ public:
+ static PassRefPtr<RefCountedHarfbuzzFace> create(HB_FaceRec_* harfbuzzFace)
+ {
+ return adoptRef(new RefCountedHarfbuzzFace(harfbuzzFace));
+ }
+
+ ~RefCountedHarfbuzzFace();
+
+ HB_FaceRec_* face() const { return m_harfbuzzFace; }
+
+ private:
+ RefCountedHarfbuzzFace(HB_FaceRec_* harfbuzzFace) : m_harfbuzzFace(harfbuzzFace)
+ {
+ }
+
+ HB_FaceRec_* m_harfbuzzFace;
+ };
+
+ SkTypeface* mTypeface;
+ float mTextSize;
+ bool mFakeBold;
+ bool mFakeItalic;
+ mutable RefPtr<RefCountedHarfbuzzFace> m_harfbuzzFace;
+
+ static SkTypeface* hashTableDeletedFontValue() {
+ return reinterpret_cast<SkTypeface*>(-1);
+ }
+};
+
+} /* namespace */
+
+#endif
diff --git a/Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp b/Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp
new file mode 100644
index 0000000..337a94d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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.
+ */
+
+//This file is part of the internal font implementation. It should not be included by anyone other than
+// FontMac.cpp, FontWin.cpp and Font.cpp.
+
+#include "config.h"
+#include "FontPlatformData.h"
+
+#ifdef SUPPORT_COMPLEX_SCRIPTS
+#include "HarfbuzzSkia.h"
+#endif
+#include "SkPaint.h"
+#include "SkTypeface.h"
+
+//#define TRACE_FONTPLATFORMDATA_LIFE
+//#define COUNT_FONTPLATFORMDATA_LIFE
+
+#ifdef COUNT_FONTPLATFORMDATA_LIFE
+static int gCount;
+static int gMaxCount;
+
+static void inc_count()
+{
+ if (++gCount > gMaxCount)
+ {
+ gMaxCount = gCount;
+ SkDebugf("---------- FontPlatformData %d\n", gMaxCount);
+ }
+}
+
+static void dec_count() { --gCount; }
+#else
+ #define inc_count()
+ #define dec_count()
+#endif
+
+#ifdef TRACE_FONTPLATFORMDATA_LIFE
+ #define trace(num) SkDebugf("FontPlatformData%d %p %g %d %d\n", num, mTypeface, mTextSize, mFakeBold, mFakeItalic)
+#else
+ #define trace(num)
+#endif
+
+namespace WebCore {
+
+FontPlatformData::RefCountedHarfbuzzFace::~RefCountedHarfbuzzFace()
+{
+#ifdef SUPPORT_COMPLEX_SCRIPTS
+ HB_FreeFace(m_harfbuzzFace);
+#endif
+}
+
+FontPlatformData::FontPlatformData()
+ : mTypeface(NULL), mTextSize(0), mFakeBold(false), mFakeItalic(false)
+{
+ inc_count();
+ trace(1);
+}
+
+FontPlatformData::FontPlatformData(const FontPlatformData& src)
+{
+ if (hashTableDeletedFontValue() != src.mTypeface) {
+ SkSafeRef(src.mTypeface);
+ }
+
+ mTypeface = src.mTypeface;
+
+ mTextSize = src.mTextSize;
+ mFakeBold = src.mFakeBold;
+ mFakeItalic = src.mFakeItalic;
+ m_harfbuzzFace = src.m_harfbuzzFace;
+
+ inc_count();
+ trace(2);
+}
+
+FontPlatformData::FontPlatformData(SkTypeface* tf, float textSize, bool fakeBold, bool fakeItalic)
+ : mTypeface(tf), mTextSize(textSize), mFakeBold(fakeBold), mFakeItalic(fakeItalic)
+{
+ if (hashTableDeletedFontValue() != mTypeface) {
+ SkSafeRef(mTypeface);
+ }
+
+ inc_count();
+ trace(3);
+}
+
+FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize)
+ : mTypeface(src.mTypeface), mTextSize(textSize), mFakeBold(src.mFakeBold), mFakeItalic(src.mFakeItalic),
+ m_harfbuzzFace(src.m_harfbuzzFace)
+{
+ if (hashTableDeletedFontValue() != mTypeface) {
+ SkSafeRef(mTypeface);
+ }
+
+ inc_count();
+ trace(4);
+}
+
+FontPlatformData::FontPlatformData(float size, bool bold, bool oblique)
+ : mTypeface(NULL), mTextSize(size), mFakeBold(bold), mFakeItalic(oblique)
+{
+ inc_count();
+ trace(5);
+}
+
+FontPlatformData::~FontPlatformData()
+{
+ dec_count();
+#ifdef TRACE_FONTPLATFORMDATA_LIFE
+ SkDebugf("----------- ~FontPlatformData\n");
+#endif
+
+ if (hashTableDeletedFontValue() != mTypeface) {
+ SkSafeUnref(mTypeface);
+ }
+}
+
+FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src)
+{
+ if (hashTableDeletedFontValue() != src.mTypeface) {
+ SkSafeRef(src.mTypeface);
+ }
+ if (hashTableDeletedFontValue() != mTypeface) {
+ SkSafeUnref(mTypeface);
+ }
+
+ mTypeface = src.mTypeface;
+ mTextSize = src.mTextSize;
+ mFakeBold = src.mFakeBold;
+ mFakeItalic = src.mFakeItalic;
+ m_harfbuzzFace = src.m_harfbuzzFace;
+
+ return *this;
+}
+
+void FontPlatformData::setupPaint(SkPaint* paint) const
+{
+ float ts = mTextSize;
+ if (!(ts > 0))
+ ts = 12;
+
+ if (hashTableDeletedFontValue() == mTypeface)
+ paint->setTypeface(0);
+ else
+ paint->setTypeface(mTypeface);
+
+ paint->setAntiAlias(true);
+ paint->setSubpixelText(true);
+ paint->setHinting(SkPaint::kSlight_Hinting);
+ paint->setTextSize(SkFloatToScalar(ts));
+ paint->setFakeBoldText(mFakeBold);
+ paint->setTextSkewX(mFakeItalic ? -SK_Scalar1/4 : 0);
+#ifndef SUPPORT_COMPLEX_SCRIPTS
+ paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+#endif
+}
+
+uint32_t FontPlatformData::uniqueID() const
+{
+ if (hashTableDeletedFontValue() == mTypeface)
+ return SkTypeface::UniqueID(0);
+ else
+ return SkTypeface::UniqueID(mTypeface);
+}
+
+bool FontPlatformData::operator==(const FontPlatformData& a) const
+{
+ return mTypeface == a.mTypeface &&
+ mTextSize == a.mTextSize &&
+ mFakeBold == a.mFakeBold &&
+ mFakeItalic == a.mFakeItalic;
+}
+
+unsigned FontPlatformData::hash() const
+{
+ unsigned h;
+
+ if (hashTableDeletedFontValue() == mTypeface) {
+ h = reinterpret_cast<unsigned>(mTypeface);
+ } else {
+ h = SkTypeface::UniqueID(mTypeface);
+ }
+
+ uint32_t sizeAsInt = *reinterpret_cast<const uint32_t*>(&mTextSize);
+
+ h ^= 0x01010101 * (((int)mFakeBold << 1) | (int)mFakeItalic);
+ h ^= sizeAsInt;
+ return h;
+}
+
+bool FontPlatformData::isFixedPitch() const
+{
+ if (mTypeface && (mTypeface != hashTableDeletedFontValue()))
+ return mTypeface->isFixedWidth();
+ else
+ return false;
+}
+
+HB_FaceRec_* FontPlatformData::harfbuzzFace() const
+{
+#ifdef SUPPORT_COMPLEX_SCRIPTS
+ if (!m_harfbuzzFace)
+ m_harfbuzzFace = RefCountedHarfbuzzFace::create(
+ HB_NewFace(const_cast<FontPlatformData*>(this), harfbuzzSkiaGetTable));
+
+ return m_harfbuzzFace->face();
+#else
+ return NULL;
+#endif
+}
+}
diff --git a/Source/WebCore/platform/graphics/android/GLUtils.cpp b/Source/WebCore/platform/graphics/android/GLUtils.cpp
new file mode 100644
index 0000000..5eabb85
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GLUtils.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "GLUtils.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "ShaderProgram.h"
+
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLUtils", __VA_ARGS__)
+
+namespace WebCore {
+
+using namespace android;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Matrix utilities
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void GLUtils::toGLMatrix(GLfloat* flattened, const TransformationMatrix& m)
+{
+ flattened[0] = m.m11(); // scaleX
+ flattened[1] = m.m12(); // skewY
+ flattened[2] = m.m13();
+ flattened[3] = m.m14(); // persp0
+ flattened[4] = m.m21(); // skewX
+ flattened[5] = m.m22(); // scaleY
+ flattened[6] = m.m23();
+ flattened[7] = m.m24(); // persp1
+ flattened[8] = m.m31();
+ flattened[9] = m.m32();
+ flattened[10] = m.m33();
+ flattened[11] = m.m34();
+ flattened[12] = m.m41(); // transX
+ flattened[13] = m.m42(); // transY
+ flattened[14] = m.m43();
+ flattened[15] = m.m44(); // persp2
+}
+
+void GLUtils::toSkMatrix(SkMatrix& matrix, const TransformationMatrix& m)
+{
+ matrix[0] = m.m11(); // scaleX
+ matrix[1] = m.m21(); // skewX
+ matrix[2] = m.m41(); // transX
+ matrix[3] = m.m12(); // skewY
+ matrix[4] = m.m22(); // scaleY
+ matrix[5] = m.m42(); // transY
+ matrix[6] = m.m14(); // persp0
+ matrix[7] = m.m24(); // persp1
+ matrix[8] = m.m44(); // persp2
+}
+
+void GLUtils::setOrthographicMatrix(TransformationMatrix& ortho, float left, float top,
+ float right, float bottom, float nearZ, float farZ)
+{
+ float deltaX = right - left;
+ float deltaY = top - bottom;
+ float deltaZ = farZ - nearZ;
+ if (!deltaX || !deltaY || !deltaZ)
+ return;
+
+ ortho.setM11(2.0f / deltaX);
+ ortho.setM41(-(right + left) / deltaX);
+ ortho.setM22(2.0f / deltaY);
+ ortho.setM42(-(top + bottom) / deltaY);
+ ortho.setM33(-2.0f / deltaZ);
+ ortho.setM43(-(nearZ + farZ) / deltaZ);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// GL & EGL error checks
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void crashIfOOM(GLint errorCode) {
+ const GLint OOM_ERROR_CODE = 0x505;
+ if (errorCode == OOM_ERROR_CODE) {
+ XLOG("Fatal OOM detected.");
+ CRASH();
+ }
+}
+
+void GLUtils::checkEglError(const char* op, EGLBoolean returnVal)
+{
+ if (returnVal != EGL_TRUE)
+ XLOG("EGL ERROR - %s() returned %d\n", op, returnVal);
+
+ for (EGLint error = eglGetError(); error != EGL_SUCCESS; error = eglGetError()) {
+ XLOG("after %s() eglError (0x%x)\n", op, error);
+ crashIfOOM(error);
+ }
+}
+
+bool GLUtils::checkGlError(const char* op)
+{
+ bool ret = false;
+ for (GLint error = glGetError(); error; error = glGetError()) {
+ XLOG("GL ERROR - after %s() glError (0x%x)\n", op, error);
+ crashIfOOM(error);
+ ret = true;
+ }
+ return ret;
+}
+
+bool GLUtils::checkGlErrorOn(void* p, const char* op)
+{
+ bool ret = false;
+ for (GLint error = glGetError(); error; error = glGetError()) {
+ XLOG("GL ERROR on %x - after %s() glError (0x%x)\n", p, op, error);
+ crashIfOOM(error);
+ ret = true;
+ }
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// GL & EGL extension checks
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool GLUtils::isEGLImageSupported()
+{
+ const char* eglExtensions = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS);
+ const char* glExtensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
+
+ return eglExtensions && glExtensions
+ && strstr(eglExtensions, "EGL_KHR_image_base")
+ && strstr(eglExtensions, "EGL_KHR_gl_texture_2D_image")
+ && strstr(glExtensions, "GL_OES_EGL_image");
+}
+
+bool GLUtils::isEGLFenceSyncSupported()
+{
+ const char* eglExtensions = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS);
+ return eglExtensions && strstr(eglExtensions, "EGL_KHR_fence_sync");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Textures utilities
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static GLenum getInternalFormat(SkBitmap::Config config)
+{
+ switch (config) {
+ case SkBitmap::kA8_Config:
+ return GL_ALPHA;
+ case SkBitmap::kARGB_4444_Config:
+ return GL_RGBA;
+ case SkBitmap::kARGB_8888_Config:
+ return GL_RGBA;
+ case SkBitmap::kRGB_565_Config:
+ return GL_RGB;
+ default:
+ return -1;
+ }
+}
+
+static GLenum getType(SkBitmap::Config config)
+{
+ switch (config) {
+ case SkBitmap::kA8_Config:
+ return GL_UNSIGNED_BYTE;
+ case SkBitmap::kARGB_4444_Config:
+ return GL_UNSIGNED_SHORT_4_4_4_4;
+ case SkBitmap::kARGB_8888_Config:
+ return GL_UNSIGNED_BYTE;
+ case SkBitmap::kIndex8_Config:
+ return -1; // No type for compressed data.
+ case SkBitmap::kRGB_565_Config:
+ return GL_UNSIGNED_SHORT_5_6_5;
+ default:
+ return -1;
+ }
+}
+
+static EGLConfig defaultPbufferConfig(EGLDisplay display)
+{
+ EGLConfig config;
+ EGLint numConfigs;
+
+ static const EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ eglChooseConfig(display, configAttribs, &config, 1, &numConfigs);
+ GLUtils::checkEglError("eglPbufferConfig");
+ if (numConfigs != 1)
+ LOGI("eglPbufferConfig failed (%d)\n", numConfigs);
+
+ return config;
+}
+
+static EGLSurface createPbufferSurface(EGLDisplay display, const EGLConfig& config,
+ EGLint* errorCode)
+{
+ const EGLint attribList[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+ EGLSurface surface = eglCreatePbufferSurface(display, config, attribList);
+
+ if (errorCode)
+ *errorCode = eglGetError();
+ else
+ GLUtils::checkEglError("eglCreatePbufferSurface");
+
+ if (surface == EGL_NO_SURFACE)
+ return EGL_NO_SURFACE;
+
+ return surface;
+}
+
+EGLContext GLUtils::createBackgroundContext(EGLContext sharedContext)
+{
+ checkEglError("<init>");
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ checkEglError("eglGetDisplay");
+ if (display == EGL_NO_DISPLAY) {
+ XLOG("eglGetDisplay returned EGL_NO_DISPLAY");
+ return EGL_NO_CONTEXT;
+ }
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLBoolean returnValue = eglInitialize(display, &majorVersion, &minorVersion);
+ checkEglError("eglInitialize", returnValue);
+ if (returnValue != EGL_TRUE) {
+ XLOG("eglInitialize failed\n");
+ return EGL_NO_CONTEXT;
+ }
+
+ EGLConfig config = defaultPbufferConfig(display);
+ EGLSurface surface = createPbufferSurface(display, config, 0);
+
+ EGLint surfaceConfigId;
+ EGLBoolean success = eglGetConfigAttrib(display, config, EGL_CONFIG_ID, &surfaceConfigId);
+
+ EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ EGLContext context = eglCreateContext(display, config, sharedContext, contextAttribs);
+ checkEglError("eglCreateContext");
+ if (context == EGL_NO_CONTEXT) {
+ XLOG("eglCreateContext failed\n");
+ return EGL_NO_CONTEXT;
+ }
+
+ returnValue = eglMakeCurrent(display, surface, surface, context);
+ checkEglError("eglMakeCurrent", returnValue);
+ if (returnValue != EGL_TRUE) {
+ XLOG("eglMakeCurrent failed\n");
+ return EGL_NO_CONTEXT;
+ }
+
+ return context;
+}
+
+void GLUtils::deleteTexture(GLuint* texture)
+{
+ glDeleteTextures(1, texture);
+ GLUtils::checkGlError("glDeleteTexture");
+ *texture = 0;
+}
+
+GLuint GLUtils::createSampleColorTexture(int r, int g, int b) {
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ GLubyte pixels[4 *3] = {
+ r, g, b,
+ r, g, b,
+ r, g, b,
+ r, g, b
+ };
+ glBindTexture(GL_TEXTURE_2D, texture);
+ GLUtils::checkGlError("glBindTexture");
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
+ GLUtils::checkGlError("glTexImage2D");
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ return texture;
+}
+
+GLuint GLUtils::createSampleTexture()
+{
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ GLubyte pixels[4 *3] = {
+ 255, 0, 0,
+ 0, 255, 0,
+ 0, 0, 255,
+ 255, 255, 0
+ };
+ glBindTexture(GL_TEXTURE_2D, texture);
+ GLUtils::checkGlError("glBindTexture");
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
+ GLUtils::checkGlError("glTexImage2D");
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ return texture;
+}
+
+void GLUtils::createTextureWithBitmap(GLuint texture, SkBitmap& bitmap, GLint filter)
+{
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ GLUtils::checkGlError("glBindTexture");
+ SkBitmap::Config config = bitmap.getConfig();
+ int internalformat = getInternalFormat(config);
+ int type = getType(config);
+ bitmap.lockPixels();
+ glTexImage2D(GL_TEXTURE_2D, 0, internalformat, bitmap.width(), bitmap.height(),
+ 0, internalformat, type, bitmap.getPixels());
+ bitmap.unlockPixels();
+ if (GLUtils::checkGlError("glTexImage2D")) {
+ XLOG("GL ERROR: glTexImage2D parameters are : bitmap.width() %d, bitmap.height() %d,"
+ " internalformat 0x%x, type 0x%x, bitmap.getPixels() %p",
+ bitmap.width(), bitmap.height(), internalformat, type, bitmap.getPixels());
+ }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+
+ // The following is a workaround -- remove when EGLImage texture upload is fixed.
+ GLuint fboID;
+ glGenFramebuffers(1, &fboID);
+ glBindFramebuffer(GL_FRAMEBUFFER, fboID);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+ glCheckFramebufferStatus(GL_FRAMEBUFFER); // should return GL_FRAMEBUFFER_COMPLETE
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO
+ glDeleteFramebuffers(1, &fboID);
+}
+
+void GLUtils::updateTextureWithBitmap(GLuint texture, SkBitmap& bitmap, GLint filter)
+{
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ GLUtils::checkGlError("glBindTexture");
+ SkBitmap::Config config = bitmap.getConfig();
+ int internalformat = getInternalFormat(config);
+ int type = getType(config);
+ bitmap.lockPixels();
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
+ internalformat, type, bitmap.getPixels());
+ bitmap.unlockPixels();
+ if (GLUtils::checkGlError("glTexSubImage2D")) {
+ XLOG("GL ERROR: glTexSubImage2D parameters are : bitmap.width() %d, bitmap.height() %d,"
+ " internalformat 0x%x, type 0x%x, bitmap.getPixels() %p",
+ bitmap.width(), bitmap.height(), internalformat, type, bitmap.getPixels());
+ }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+}
+
+void GLUtils::updateTextureWithBitmap(GLuint texture, int x, int y, SkBitmap& bitmap, GLint filter)
+{
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ GLUtils::checkGlError("glBindTexture");
+ SkBitmap::Config config = bitmap.getConfig();
+ int internalformat = getInternalFormat(config);
+ int type = getType(config);
+ bitmap.lockPixels();
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, bitmap.width(), bitmap.height(),
+ internalformat, type, bitmap.getPixels());
+ bitmap.unlockPixels();
+ if (GLUtils::checkGlError("glTexSubImage2D")) {
+ XLOG("GL ERROR: glTexSubImage2D parameters are : bitmap.width() %d, bitmap.height() %d,"
+ " x %d, y %d, internalformat 0x%x, type 0x%x, bitmap.getPixels() %p",
+ bitmap.width(), bitmap.height(), x, y, internalformat, type, bitmap.getPixels());
+ }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+}
+
+void GLUtils::createEGLImageFromTexture(GLuint texture, EGLImageKHR* image)
+{
+ EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(texture);
+ static const EGLint attr[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
+ *image = eglCreateImageKHR(eglGetCurrentDisplay(), eglGetCurrentContext(),
+ EGL_GL_TEXTURE_2D_KHR, buffer, attr);
+ GLUtils::checkEglError("eglCreateImage", (*image != EGL_NO_IMAGE_KHR));
+}
+
+void GLUtils::createTextureFromEGLImage(GLuint texture, EGLImageKHR image, GLint filter)
+{
+ glBindTexture(GL_TEXTURE_2D, texture);
+ GLUtils::checkGlError("glBindTexture");
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/GLUtils.h b/Source/WebCore/platform/graphics/android/GLUtils.h
new file mode 100644
index 0000000..1e8df39
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GLUtils.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 GLUtils_h
+#define GLUtils_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "TransformationMatrix.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace WebCore {
+
+class GLUtils {
+
+public:
+ // Matrix utilities
+ static void toGLMatrix(GLfloat* flattened, const TransformationMatrix& matrix);
+ static void toSkMatrix(SkMatrix& skmatrix, const TransformationMatrix& matrix);
+ static void setOrthographicMatrix(TransformationMatrix& ortho, float left, float top,
+ float right, float bottom, float nearZ, float farZ);
+
+ // GL & EGL error checks
+ static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE);
+ static bool checkGlErrorOn(void* p, const char* op);
+ static bool checkGlError(const char* op);
+
+ // GL & EGL extension checks
+ static bool isEGLImageSupported();
+ static bool isEGLFenceSyncSupported();
+
+ // Texture utilities
+ static EGLContext createBackgroundContext(EGLContext sharedContext);
+ static void deleteTexture(GLuint* texture);
+ static GLuint createSampleColorTexture(int r, int g, int b);
+ static GLuint createSampleTexture();
+ static void createTextureWithBitmap(GLuint texture, SkBitmap& bitmap, GLint filter = GL_LINEAR);
+ static void updateTextureWithBitmap(GLuint texture, SkBitmap& bitmap, GLint filter = GL_LINEAR);
+ static void updateTextureWithBitmap(GLuint texture, int x, int y, SkBitmap& bitmap, GLint filter = GL_LINEAR);
+ static void createEGLImageFromTexture(GLuint texture, EGLImageKHR* image);
+ static void createTextureFromEGLImage(GLuint texture, EGLImageKHR image, GLint filter = GL_LINEAR);
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // GLUtils_h
diff --git a/Source/WebCore/platform/graphics/android/GLWebViewState.cpp b/Source/WebCore/platform/graphics/android/GLWebViewState.cpp
new file mode 100644
index 0000000..4acd563
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GLWebViewState.cpp
@@ -0,0 +1,574 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "GLWebViewState.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "BaseLayerAndroid.h"
+#include "ClassTracker.h"
+#include "GLUtils.h"
+#include "LayerAndroid.h"
+#include "TilesManager.h"
+#include <wtf/CurrentTime.h>
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+
+#undef XLOGC
+#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
+
+#ifdef DEBUG
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+#define FIRST_TILED_PAGE_ID 1
+#define SECOND_TILED_PAGE_ID 2
+
+#define FRAMERATE_CAP 0.01666 // We cap at 60 fps
+
+namespace WebCore {
+
+using namespace android;
+
+GLWebViewState::GLWebViewState(android::Mutex* buttonMutex)
+ : m_scaleRequestState(kNoScaleRequest)
+ , m_currentScale(1)
+ , m_futureScale(1)
+ , m_updateTime(-1)
+ , m_transitionTime(-1)
+ , m_baseLayer(0)
+ , m_currentBaseLayer(0)
+ , m_previouslyUsedRoot(0)
+ , m_currentPictureCounter(0)
+ , m_usePageA(true)
+ , m_frameworkInval(0, 0, 0, 0)
+ , m_frameworkLayersInval(0, 0, 0, 0)
+ , m_globalButtonMutex(buttonMutex)
+ , m_baseLayerUpdate(true)
+ , m_backgroundColor(SK_ColorWHITE)
+ , m_prevDrawTime(0)
+ , m_displayRings(false)
+ , m_focusRingTexture(-1)
+{
+ m_viewport.setEmpty();
+ m_previousViewport.setEmpty();
+ m_futureViewportTileBounds.setEmpty();
+ m_viewportTileBounds.setEmpty();
+ m_preZoomBounds.setEmpty();
+
+ m_tiledPageA = new TiledPage(FIRST_TILED_PAGE_ID, this);
+ m_tiledPageB = new TiledPage(SECOND_TILED_PAGE_ID, this);
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("GLWebViewState");
+#endif
+#ifdef MEASURES_PERF
+ m_timeCounter = 0;
+ m_totalTimeCounter = 0;
+ m_measurePerfs = false;
+#endif
+}
+
+GLWebViewState::~GLWebViewState()
+{
+ // We have to destroy the two tiled pages first as their destructor
+ // may depend on the existence of this GLWebViewState and some of its
+ // instance variables in order to complete.
+ // Explicitely, currently we need to have the m_currentBaseLayer around
+ // in order to complete any pending paint operations (the tiled pages
+ // will remove any pending operations, and wait if one is underway).
+ delete m_tiledPageA;
+ delete m_tiledPageB;
+ SkSafeUnref(m_currentBaseLayer);
+ SkSafeUnref(m_baseLayer);
+ m_baseLayer = 0;
+ m_currentBaseLayer = 0;
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("GLWebViewState");
+#endif
+}
+
+void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
+ bool showVisualIndicator, bool isPictureAfterFirstLayout)
+{
+ android::Mutex::Autolock lock(m_baseLayerLock);
+ if (!layer || isPictureAfterFirstLayout) {
+ m_tiledPageA->setUsable(false);
+ m_tiledPageB->setUsable(false);
+ }
+ if (isPictureAfterFirstLayout) {
+ m_baseLayerUpdate = true;
+ m_invalidateRegion.setEmpty();
+ }
+ if (m_baseLayer && layer)
+ m_baseLayer->swapExtra(layer);
+
+ SkSafeRef(layer);
+ SkSafeUnref(m_baseLayer);
+ m_baseLayer = layer;
+ if (m_baseLayer)
+ m_baseLayer->setGLWebViewState(this);
+
+ // We only update the layers if we are not currently
+ // waiting for a tiledPage to be painted
+ if (m_baseLayerUpdate) {
+ SkSafeRef(layer);
+ SkSafeUnref(m_currentBaseLayer);
+ m_currentBaseLayer = layer;
+ }
+ m_displayRings = false;
+ invalRegion(inval);
+
+#ifdef MEASURES_PERF
+ if (m_measurePerfs && !showVisualIndicator)
+ dumpMeasures();
+ m_measurePerfs = showVisualIndicator;
+#endif
+
+ TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
+}
+
+void GLWebViewState::setRings(Vector<IntRect>& rings, bool isPressed)
+{
+ android::Mutex::Autolock lock(m_baseLayerLock);
+ m_displayRings = true;
+ m_rings = rings;
+ m_ringsIsPressed = isPressed;
+}
+
+void GLWebViewState::invalRegion(const SkRegion& region)
+{
+ SkRegion::Iterator iterator(region);
+ while (!iterator.done()) {
+ SkIRect r = iterator.rect();
+ IntRect ir(r.fLeft, r.fTop, r.width(), r.height());
+ inval(ir);
+ iterator.next();
+ }
+}
+
+void GLWebViewState::unlockBaseLayerUpdate() {
+ if (m_baseLayerUpdate)
+ return;
+
+ m_baseLayerUpdate = true;
+ android::Mutex::Autolock lock(m_baseLayerLock);
+ SkSafeRef(m_baseLayer);
+ SkSafeUnref(m_currentBaseLayer);
+ m_currentBaseLayer = m_baseLayer;
+
+ invalRegion(m_invalidateRegion);
+ m_invalidateRegion.setEmpty();
+}
+
+void GLWebViewState::setExtra(BaseLayerAndroid* layer, SkPicture& picture,
+ const IntRect& rect, bool allowSame)
+{
+ android::Mutex::Autolock lock(m_baseLayerLock);
+ if (!m_baseLayerUpdate)
+ return;
+
+ layer->setExtra(picture);
+
+ if (!allowSame && m_lastInval == rect)
+ return;
+
+ if (!rect.isEmpty())
+ inval(rect);
+ if (!m_lastInval.isEmpty())
+ inval(m_lastInval);
+ m_lastInval = rect;
+ m_displayRings = false;
+}
+
+void GLWebViewState::inval(const IntRect& rect)
+{
+ if (m_baseLayerUpdate) {
+ m_currentPictureCounter++;
+ if (!rect.isEmpty()) {
+ // find which tiles fall within the invalRect and mark them as dirty
+ m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
+ m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
+ if (m_frameworkInval.isEmpty())
+ m_frameworkInval = rect;
+ else
+ m_frameworkInval.unite(rect);
+ XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
+ m_frameworkInval.y(), m_frameworkInval.right(), m_frameworkInval.bottom(),
+ rect.x(), rect.y(), rect.right(), rect.bottom());
+ }
+ } else {
+ m_invalidateRegion.op(rect.x(), rect.y(), rect.right(), rect.bottom(), SkRegion::kUnion_Op);
+ }
+}
+
+void GLWebViewState::resetRings()
+{
+ m_displayRings = false;
+}
+
+void GLWebViewState::drawFocusRing(IntRect& srcRect)
+{
+ // TODO: use a 9-patch texture to draw the focus ring
+ // instead of plain colors
+ const float alpha = 0.3;
+ float borderAlpha = 0.40;
+
+ const int r = 104;
+ const int g = 153;
+ const int b = 255;
+
+ int padding = 4;
+ int border = 1;
+ int fuzzyBorder = border * 2;
+ if (!m_ringsIsPressed) {
+ padding = 0;
+ border = 2;
+ fuzzyBorder = 3;
+ borderAlpha = 0.2;
+ }
+ if (m_focusRingTexture == -1)
+ m_focusRingTexture = GLUtils::createSampleColorTexture(r, g, b);
+
+ SkRect rLeft, rTop, rRight, rBottom, rOverlay;
+
+ IntRect rect(srcRect.x() - padding, srcRect.y() - padding,
+ srcRect.width() + (padding * 2), srcRect.height() + (padding * 2));
+ rLeft.set(rect.x() - border, rect.y(),
+ rect.x(), rect.y() + rect.height());
+ rTop.set(rect.x() - border, rect.y() - border,
+ rect.x() + rect.width() + border, rect.y());
+ rRight.set(rect.x() + rect.width(), rect.y(),
+ rect.x() + rect.width() + border,
+ rect.y() + rect.height());
+ rBottom.set(rect.x() - border, rect.y() + rect.height(),
+ rect.x() + rect.width() + border,
+ rect.y() + rect.height() + border);
+ rOverlay.set(rect.x() - fuzzyBorder, rect.y() - fuzzyBorder,
+ rect.x() + rect.width() + fuzzyBorder,
+ rect.y() + rect.height() + fuzzyBorder);
+
+ TilesManager::instance()->shader()->drawQuad(rLeft, m_focusRingTexture, borderAlpha);
+ TilesManager::instance()->shader()->drawQuad(rTop, m_focusRingTexture, borderAlpha);
+ TilesManager::instance()->shader()->drawQuad(rRight, m_focusRingTexture, borderAlpha);
+ TilesManager::instance()->shader()->drawQuad(rBottom, m_focusRingTexture, borderAlpha);
+ if (m_ringsIsPressed) {
+ TilesManager::instance()->shader()->drawQuad(rOverlay, m_focusRingTexture, alpha);
+ } else {
+ rLeft.set(rect.x() - fuzzyBorder, rect.y(),
+ rect.x(), rect.y() + rect.height());
+ rTop.set(rect.x() - fuzzyBorder, rect.y() - fuzzyBorder,
+ rect.x() + rect.width() + fuzzyBorder, rect.y());
+ rRight.set(rect.x() + rect.width(), rect.y(),
+ rect.x() + rect.width() + fuzzyBorder,
+ rect.y() + rect.height());
+ rBottom.set(rect.x() - fuzzyBorder, rect.y() + rect.height(),
+ rect.x() + rect.width() + fuzzyBorder,
+ rect.y() + rect.height() + fuzzyBorder);
+ TilesManager::instance()->shader()->drawQuad(rLeft, m_focusRingTexture, alpha);
+ TilesManager::instance()->shader()->drawQuad(rTop, m_focusRingTexture, alpha);
+ TilesManager::instance()->shader()->drawQuad(rRight, m_focusRingTexture, alpha);
+ TilesManager::instance()->shader()->drawQuad(rBottom, m_focusRingTexture, alpha);
+ }
+}
+
+void GLWebViewState::paintExtras()
+{
+ if (m_displayRings) {
+ // TODO: handles correctly the multi-rings case
+ for (int i=0; i<m_rings.size(); i++) {
+ IntRect rect = m_rings.at(i);
+ drawFocusRing(rect);
+ }
+ }
+}
+
+unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
+{
+ android::Mutex::Autolock lock(m_baseLayerLock);
+ if (m_currentBaseLayer) {
+ m_globalButtonMutex->lock();
+ m_currentBaseLayer->drawCanvas(canvas);
+ m_globalButtonMutex->unlock();
+ }
+ return m_currentPictureCounter;
+}
+
+void GLWebViewState::scheduleUpdate(const double& currentTime,
+ const SkIRect& viewport, float scale)
+{
+ // if no update time, set it
+ if (updateTime() == -1) {
+ m_scaleRequestState = kWillScheduleRequest;
+ setUpdateTime(currentTime + s_updateInitialDelay);
+ setFutureScale(scale);
+ setFutureViewport(viewport);
+ return;
+ }
+
+ if (currentTime < updateTime())
+ return;
+
+ // we reached the scheduled update time, check if we can update
+ if (futureScale() == scale) {
+ // we are still with the previous scale, let's go
+ // with the update
+ m_scaleRequestState = kRequestNewScale;
+ setUpdateTime(-1);
+ } else {
+ // we reached the update time, but the planned update was for
+ // a different scale factor -- meaning the user is still probably
+ // in the process of zooming. Let's push the update time a bit.
+ setUpdateTime(currentTime + s_updateDelay);
+ setFutureScale(scale);
+ setFutureViewport(viewport);
+ }
+}
+
+double GLWebViewState::zoomInTransitionTime(double currentTime)
+{
+ if (m_transitionTime == -1)
+ m_transitionTime = currentTime + s_zoomInTransitionDelay;
+ return m_transitionTime;
+}
+
+double GLWebViewState::zoomOutTransitionTime(double currentTime)
+{
+ if (m_transitionTime == -1)
+ m_transitionTime = currentTime + s_zoomOutTransitionDelay;
+ return m_transitionTime;
+}
+
+
+float GLWebViewState::zoomInTransparency(double currentTime)
+{
+ float t = zoomInTransitionTime(currentTime) - currentTime;
+ t *= s_invZoomInTransitionDelay;
+ return fmin(1, fmax(0, t));
+}
+
+float GLWebViewState::zoomOutTransparency(double currentTime)
+{
+ float t = zoomOutTransitionTime(currentTime) - currentTime;
+ t *= s_invZoomOutTransitionDelay;
+ return fmin(1, fmax(0, t));
+}
+
+TiledPage* GLWebViewState::sibling(TiledPage* page)
+{
+ return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA;
+}
+
+TiledPage* GLWebViewState::frontPage()
+{
+ android::Mutex::Autolock lock(m_tiledPageLock);
+ return m_usePageA ? m_tiledPageA : m_tiledPageB;
+}
+
+TiledPage* GLWebViewState::backPage()
+{
+ android::Mutex::Autolock lock(m_tiledPageLock);
+ return m_usePageA ? m_tiledPageB : m_tiledPageA;
+}
+
+void GLWebViewState::swapPages()
+{
+ android::Mutex::Autolock lock(m_tiledPageLock);
+ m_usePageA ^= true;
+ TiledPage* working = m_usePageA ? m_tiledPageB : m_tiledPageA;
+ if (m_scaleRequestState != kNoScaleRequest)
+ TilesManager::instance()->resetTextureUsage(working);
+
+ m_scaleRequestState = kNoScaleRequest;
+}
+
+int GLWebViewState::baseContentWidth()
+{
+ return m_currentBaseLayer ? m_currentBaseLayer->getWidth() : 0;
+
+}
+int GLWebViewState::baseContentHeight()
+{
+ return m_currentBaseLayer ? m_currentBaseLayer->getHeight() : 0;
+}
+
+void GLWebViewState::setViewport(SkRect& viewport, float scale)
+{
+ m_previousViewport = m_viewport;
+ if ((m_viewport == viewport) &&
+ (m_futureScale == scale))
+ return;
+
+ m_viewport = viewport;
+ XLOG("New VIEWPORT %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f currentScale: %.2f futureScale: %.2f)",
+ m_viewport.fLeft, m_viewport.fTop, m_viewport.fRight, m_viewport.fBottom,
+ m_viewport.width(), m_viewport.height(), scale, m_currentScale, m_futureScale);
+
+ const float invTileContentWidth = scale / TilesManager::tileWidth();
+ const float invTileContentHeight = scale / TilesManager::tileHeight();
+
+ m_viewportTileBounds.set(
+ static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)),
+ static_cast<int>(floorf(viewport.fTop * invTileContentHeight)),
+ static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)),
+ static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight)));
+
+ int maxTextureCount = (m_viewportTileBounds.width() + TilesManager::instance()->expandedTileBoundsX() * 2 + 1) *
+ (m_viewportTileBounds.height() + TilesManager::instance()->expandedTileBoundsY() * 2 + 1) * 2;
+ TilesManager::instance()->setMaxTextureCount(maxTextureCount);
+ m_tiledPageA->updateBaseTileSize();
+ m_tiledPageB->updateBaseTileSize();
+}
+
+#ifdef MEASURES_PERF
+void GLWebViewState::dumpMeasures()
+{
+ for (int i = 0; i < m_timeCounter; i++) {
+ XLOGC("%d delay: %d ms", m_totalTimeCounter + i,
+ static_cast<int>(m_delayTimes[i]*1000));
+ m_delayTimes[i] = 0;
+ }
+ m_totalTimeCounter += m_timeCounter;
+ m_timeCounter = 0;
+}
+#endif // MEASURES_PERF
+
+void GLWebViewState::resetFrameworkInval()
+{
+ m_frameworkInval.setX(0);
+ m_frameworkInval.setY(0);
+ m_frameworkInval.setWidth(0);
+ m_frameworkInval.setHeight(0);
+}
+
+void GLWebViewState::addDirtyArea(const IntRect& rect)
+{
+ if (rect.isEmpty())
+ return;
+
+ IntRect inflatedRect = rect;
+ inflatedRect.inflate(8);
+ if (m_frameworkLayersInval.isEmpty())
+ m_frameworkLayersInval = inflatedRect;
+ else
+ m_frameworkLayersInval.unite(inflatedRect);
+}
+
+void GLWebViewState::resetLayersDirtyArea()
+{
+ m_frameworkLayersInval.setX(0);
+ m_frameworkLayersInval.setY(0);
+ m_frameworkLayersInval.setWidth(0);
+ m_frameworkLayersInval.setHeight(0);
+}
+
+bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
+ IntRect& webViewRect, int titleBarHeight,
+ IntRect& clip, float scale, SkColor color)
+{
+ glFinish();
+
+ double currentTime = WTF::currentTime();
+ double delta = currentTime - m_prevDrawTime;
+
+ if (delta < FRAMERATE_CAP) {
+ unsigned int usecs = (FRAMERATE_CAP - delta) * 1E6;
+ usleep(usecs);
+ }
+
+ m_prevDrawTime = currentTime;
+
+ m_baseLayerLock.lock();
+ BaseLayerAndroid* baseLayer = m_currentBaseLayer;
+ SkSafeRef(baseLayer);
+ BaseLayerAndroid* baseForComposited = m_baseLayer;
+ SkSafeRef(baseForComposited);
+ m_baseLayerLock.unlock();
+ if (!baseLayer) {
+ SkSafeUnref(baseForComposited);
+ return false;
+ }
+
+ XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
+ rect.x(), rect.y(), rect.right(), rect.bottom(),
+ viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);
+
+ resetLayersDirtyArea();
+
+ if (!baseForComposited ||
+ (baseForComposited && !baseForComposited->countChildren())) {
+ SkSafeRef(baseLayer);
+ SkSafeUnref(baseForComposited);
+ baseForComposited = baseLayer;
+ }
+
+ LayerAndroid* compositedRoot = 0;
+ if (baseForComposited && baseForComposited->countChildren() >= 1)
+ compositedRoot = static_cast<LayerAndroid*>(baseForComposited->getChild(0));
+
+ bool ret = baseLayer->drawGL(compositedRoot, rect, viewport, webViewRect, titleBarHeight, clip, scale, color);
+ m_previouslyUsedRoot = compositedRoot;
+ if (ret) {
+ FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(m_frameworkInval);
+ IntRect inval(frameworkInval.x(), frameworkInval.y(), frameworkInval.width(), frameworkInval.height());
+
+ inval.unite(m_frameworkLayersInval);
+
+ invalRect->setX(inval.x());
+ invalRect->setY(inval.y());
+ invalRect->setWidth(inval.width());
+ invalRect->setHeight(inval.height());
+
+ XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
+ inval.y(), inval.right(), inval.bottom());
+ } else {
+ resetFrameworkInval();
+ }
+
+#ifdef MEASURES_PERF
+ if (m_measurePerfs) {
+ m_delayTimes[m_timeCounter++] = delta;
+ if (m_timeCounter >= MAX_MEASURES_PERF)
+ dumpMeasures();
+ }
+#endif
+
+ SkSafeUnref(baseForComposited);
+ SkSafeUnref(baseLayer);
+ return ret;
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/GLWebViewState.h b/Source/WebCore/platform/graphics/android/GLWebViewState.h
new file mode 100644
index 0000000..980dd3c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GLWebViewState.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 GLWebViewState_h
+#define GLWebViewState_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "DrawExtra.h"
+#include "IntRect.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+#include "TiledPage.h"
+#include <utils/threads.h>
+
+// Performance measurements probe
+// To use it, enable the visual indicators in debug mode.
+// turning off the visual indicators will flush the measures.
+// #define MEASURES_PERF
+#define MAX_MEASURES_PERF 2000
+
+namespace WebCore {
+
+class BaseLayerAndroid;
+class LayerAndroid;
+
+/////////////////////////////////////////////////////////////////////////////////
+// GL Architecture
+/////////////////////////////////////////////////////////////////////////////////
+//
+// To draw things, WebView use a tree of layers. The root of that tree is a
+// BaseLayerAndroid, which may have numerous LayerAndroid over it. The content
+// of those layers are SkPicture, the content of the BaseLayer is an PictureSet.
+//
+// When drawing, we therefore have one large "surface" that is the BaseLayer,
+// and (possibly) additional surfaces (usually smaller), which are the
+// LayerAndroids. The BaseLayer usually corresponds to the normal web page
+// content, the Layers are used for some parts such as specific divs (e.g. fixed
+// position divs, or elements using CSS3D transforms, or containing video,
+// plugins, etc.).
+//
+// *** NOTE: The GL drawing architecture only paints the BaseLayer for now.
+//
+// The rendering model is to use tiles to display the BaseLayer (as obviously a
+// BaseLayer's area can be arbitrarly large). The idea is to compute a set of
+// tiles covering the viewport's area, paint those tiles using the webview's
+// content (i.e. the BaseLayer's PictureSet), then display those tiles.
+// We check which tile we should use at every frame.
+//
+// Overview
+// ---------
+//
+// The tiles are grouped into a TiledPage -- basically a map of tiles covering
+// the BaseLayer's surface. When drawing, we ask the TiledPage to prepare()
+// itself then draw itself on screen. The prepare() function is the one
+// that schedules tiles to be painted -- i.e. the subset of tiles that intersect
+// with the current viewport. When they are ready, we can display
+// the TiledPage.
+//
+// Note that BaseLayerAndroid::drawGL() will return true to the java side if
+// there is a need to be called again (i.e. if we do not have up to date
+// textures or a transition is going on).
+//
+// Tiles are implemented as a BaseTile. It knows how to paint itself with the
+// PictureSet, and to display itself. A GL texture is usually associated to it.
+//
+// We also works with two TiledPages -- one to display the page at the
+// current scale factor, and another we use to paint the page at a different
+// scale factor. I.e. when we zoom, we use TiledPage A, with its tiles scaled
+// accordingly (and therefore possible loss of quality): this is fast as it's
+// purely a hardware operation. When the user is done zooming, we ask for
+// TiledPage B to be painted at the new scale factor, covering the
+// viewport's area. When B is ready, we swap it with A.
+//
+// Texture allocation
+// ------------------
+//
+// Obviously we cannot have every BaseTile having a GL texture -- we need to
+// get the GL textures from an existing pool, and reuse them.
+//
+// The way we do it is that when we call TiledPage::prepare(), we group the
+// tiles we need (i.e. in the viewport and dirty) into a TilesSet and call
+// BaseTile::reserveTexture() for each tile (which ensures there is a specific
+// GL textures backing the BaseTiles).
+//
+// reserveTexture() will ask the TilesManager for a texture. The allocation
+// mechanism goal is to (in order):
+// - prefers to allocate the same texture as the previous time
+// - prefers to allocate textures that are as far from the viewport as possible
+// - prefers to allocate textures that are used by different TiledPages
+//
+// Note that to compute the distance of each tile from the viewport, each time
+// we prepare() a TiledPage. Also during each prepare() we compute which tiles
+// are dirty based on the info we have received from webkit.
+//
+// BaseTile Invalidation
+// ------------------
+//
+// We do not want to redraw a tile if the tile is up-to-date. A tile is
+// considered to be dirty an in need of redrawing in the following cases
+// - the tile has acquires a new texture
+// - webkit invalidates all or part of the tiles contents
+//
+// To handle the case of webkit invalidation we store two ids (counters) of the
+// pictureSets in the tile. The first id (A) represents the pictureSet used to
+// paint the tile and the second id (B) represents the pictureSet in which the
+// tile was invalidated by webkit. Thus, if A < B then tile is dirty.
+//
+// Painting scheduling
+// -------------------
+//
+// The next operation is to schedule this TilesSet to be painted
+// (TilesManager::schedulePaintForTilesSet()). TexturesGenerator
+// will get the TilesSet and ask the BaseTiles in it to be painted.
+//
+// BaseTile::paintBitmap() will paint the texture using the BaseLayer's
+// PictureSet (calling TiledPage::paintBaseLayerContent() which in turns
+// calls GLWebViewState::paintBaseLayerContent()).
+//
+// Note that TexturesGenerator is running in a separate thread, the textures
+// are shared using EGLImages (this is necessary to not slow down the rendering
+// speed -- updating GL textures in the main GL thread would slow things down).
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+class GLWebViewState {
+public:
+ enum GLScaleStates {
+ kNoScaleRequest = 0,
+ kWillScheduleRequest = 1,
+ kRequestNewScale = 2,
+ kReceivedNewScale = 3
+ };
+ typedef int32_t GLScaleState;
+
+ GLWebViewState(android::Mutex* globalButtonMutex);
+ ~GLWebViewState();
+ GLScaleState scaleRequestState() const { return m_scaleRequestState; }
+ void setScaleRequestState(GLScaleState state) { m_scaleRequestState = state; }
+ float currentScale() const { return m_currentScale; }
+ void setCurrentScale(float scale) { m_currentScale = scale; }
+ float futureScale() const { return m_futureScale; }
+ void setFutureScale(float scale) { m_futureScale = scale; }
+ const SkIRect& futureViewport() const { return m_futureViewportTileBounds; }
+ void setFutureViewport(const SkIRect& viewport) { m_futureViewportTileBounds = viewport; }
+ double updateTime() const { return m_updateTime; }
+ void setUpdateTime(double value) { m_updateTime = value; }
+ double zoomInTransitionTime(double currentTime);
+ double zoomOutTransitionTime(double currentTime);
+ float zoomInTransparency(double currentTime);
+ float zoomOutTransparency(double currentTime);
+ void resetTransitionTime() { m_transitionTime = -1; }
+
+ unsigned int paintBaseLayerContent(SkCanvas* canvas);
+ void setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval, bool showVisualIndicator,
+ bool isPictureAfterFirstLayout);
+ void setExtra(BaseLayerAndroid*, SkPicture&, const IntRect&, bool allowSame);
+ void scheduleUpdate(const double& currentTime, const SkIRect& viewport, float scale);
+ void paintExtras();
+
+ void setRings(Vector<IntRect>& rings, bool isPressed);
+ void resetRings();
+ void drawFocusRing(IntRect& rect);
+
+ TiledPage* sibling(TiledPage* page);
+ TiledPage* frontPage();
+ TiledPage* backPage();
+ void swapPages();
+
+ // dimensions of the current base layer
+ int baseContentWidth();
+ int baseContentHeight();
+
+ void setViewport(SkRect& viewport, float scale);
+
+ // a rect containing the coordinates of all tiles in the current viewport
+ const SkIRect& viewportTileBounds() const { return m_viewportTileBounds; }
+ // a rect containing the viewportTileBounds before there was a scale change
+ const SkIRect& preZoomBounds() const { return m_preZoomBounds; }
+ void setPreZoomBounds(const SkIRect& bounds) { m_preZoomBounds = bounds; }
+
+ unsigned int currentPictureCounter() const { return m_currentPictureCounter; }
+
+ void lockBaseLayerUpdate() { m_baseLayerUpdate = false; }
+ void unlockBaseLayerUpdate();
+
+ bool moving() {
+ // This will only works if we are not zooming -- we check
+ // for this in BaseLayerAndroid::drawBasePictureInGL()
+ if ((m_viewport.fLeft != m_previousViewport.fLeft ||
+ m_viewport.fTop != m_previousViewport.fTop) &&
+ m_viewport.width() == m_previousViewport.width() &&
+ m_viewport.height() == m_previousViewport.height())
+ return true;
+ return false;
+ }
+
+ bool drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
+ IntRect& webViewRect, int titleBarHeight,
+ IntRect& clip, float scale, SkColor color = SK_ColorWHITE);
+
+ void setBackgroundColor(SkColor color) { m_backgroundColor = color; }
+ SkColor getBackgroundColor() { return m_backgroundColor; }
+
+#ifdef MEASURES_PERF
+ void dumpMeasures();
+#endif
+
+ void resetFrameworkInval();
+ void addDirtyArea(const IntRect& rect);
+ void resetLayersDirtyArea();
+ LayerAndroid* previouslyUsedRoot() { return m_previouslyUsedRoot; }
+
+private:
+ void inval(const IntRect& rect); // caller must hold m_baseLayerLock
+ void invalRegion(const SkRegion& region);
+
+ // Delay between scheduling a new page when the scale
+ // factor changes (i.e. zooming in or out)
+ static const double s_updateInitialDelay = 0.3; // 300 ms
+ // If the scale factor continued to change and we completed
+ // the original delay, we push back the update by this value
+ static const double s_updateDelay = 0.1; // 100 ms
+
+ // Delay for the transition between the two pages
+ static const double s_zoomInTransitionDelay = 0.1; // 100 ms
+ static const double s_invZoomInTransitionDelay = 10;
+ static const double s_zoomOutTransitionDelay = 0.2; // 200 ms
+ static const double s_invZoomOutTransitionDelay = 5;
+
+ GLScaleState m_scaleRequestState;
+ float m_currentScale;
+ float m_futureScale;
+ double m_updateTime;
+ double m_transitionTime;
+ android::Mutex m_tiledPageLock;
+ SkRect m_viewport;
+ SkRect m_previousViewport;
+ SkIRect m_viewportTileBounds;
+ SkIRect m_futureViewportTileBounds;
+ SkIRect m_preZoomBounds;
+ android::Mutex m_baseLayerLock;
+ BaseLayerAndroid* m_baseLayer;
+ BaseLayerAndroid* m_currentBaseLayer;
+ LayerAndroid* m_previouslyUsedRoot;
+
+ unsigned int m_currentPictureCounter;
+ bool m_usePageA;
+ TiledPage* m_tiledPageA;
+ TiledPage* m_tiledPageB;
+ IntRect m_lastInval;
+ IntRect m_frameworkInval;
+ IntRect m_frameworkLayersInval;
+ android::Mutex* m_globalButtonMutex;
+
+ bool m_baseLayerUpdate;
+ SkRegion m_invalidateRegion;
+
+ SkColor m_backgroundColor;
+ double m_prevDrawTime;
+
+#ifdef MEASURES_PERF
+ unsigned int m_totalTimeCounter;
+ int m_timeCounter;
+ double m_delayTimes[MAX_MEASURES_PERF];
+ bool m_measurePerfs;
+#endif
+ bool m_displayRings;
+ Vector<IntRect> m_rings;
+ bool m_ringsIsPressed;
+ int m_focusRingTexture;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // GLWebViewState_h
diff --git a/Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp b/Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp
new file mode 100644
index 0000000..da9d99a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "EmojiFont.h"
+#include "GlyphPageTreeNode.h"
+#include "SkTemplates.h"
+#include "SkPaint.h"
+#include "SkUtils.h"
+#include "SimpleFontData.h"
+
+using namespace android;
+
+namespace WebCore {
+
+#define NO_BREAK_SPACE_UNICHAR 0xA0
+
+bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData)
+{
+ if (SkUTF16_IsHighSurrogate(buffer[bufferLength-1])) {
+ SkDebugf("%s last char is high-surrogate", __FUNCTION__);
+ return false;
+ }
+
+ SkPaint paint;
+ fontData->platformData().setupPaint(&paint);
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+
+ SkAutoSTMalloc <GlyphPage::size, uint16_t> glyphStorage(length);
+ uint16_t* glyphs = glyphStorage.get();
+ unsigned count = paint.textToGlyphs(buffer, bufferLength << 1, glyphs);
+ if (count != length) {
+ SkDebugf("%s count != length\n", __FUNCTION__);
+ return false;
+ }
+
+ unsigned allGlyphs = 0; // track if any of the glyphIDs are non-zero
+
+ // search for emoji. If we knew for sure that buffer was a contiguous range
+ // of chars, we could quick-reject the range to avoid this loop (usually)
+ if (EmojiFont::IsAvailable()) {
+ const UChar* curr = buffer;
+ for (unsigned i = 0; i < length; i++) {
+ SkUnichar uni = SkUTF16_NextUnichar(&curr);
+ uint16_t glyphID = glyphs[i];
+ // only sniff if the normal font failed to recognize it
+ if (!glyphID)
+ glyphID = EmojiFont::UnicharToGlyph(uni);
+ setGlyphDataForIndex(offset + i, glyphID, fontData);
+ allGlyphs |= glyphID;
+ }
+ } else {
+ for (unsigned i = 0; i < length; i++) {
+ uint16_t glyphID = glyphs[i];
+ setGlyphDataForIndex(offset + i, glyphID, fontData);
+ allGlyphs |= glyphID;
+ }
+ }
+ return allGlyphs != 0;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/GradientAndroid.cpp b/Source/WebCore/platform/graphics/android/GradientAndroid.cpp
new file mode 100644
index 0000000..b8dc9dd
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GradientAndroid.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Gradient.h"
+
+#include "android_graphics.h"
+#include "CSSParser.h"
+#include "GraphicsContext.h"
+#include "NotImplemented.h"
+#include "SkCanvas.h"
+#include "SkColorShader.h"
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+
+class PlatformGradientRec {
+public:
+ PlatformGradientRec() : m_shader(NULL) {}
+ ~PlatformGradientRec() { SkSafeUnref(m_shader); }
+
+ SkShader* m_shader;
+ SkShader::TileMode m_tileMode;
+ int m_colorCountWhenShaderWasBuilt;
+};
+
+namespace WebCore {
+
+void Gradient::platformDestroy()
+{
+ delete m_gradient;
+ m_gradient = 0;
+}
+
+static U8CPU F2B(float x)
+{
+ return (int)(x * 255);
+}
+
+SkShader* Gradient::getShader(SkShader::TileMode mode)
+{
+ if (NULL == m_gradient)
+ m_gradient = new PlatformGradientRec;
+ else if (mode == m_gradient->m_tileMode)
+ return m_gradient->m_shader;
+
+ // need to ensure that the m_stops array is sorted. We call getColor()
+ // which, as a side effect, does the sort.
+ // TODO: refactor Gradient.h to formally expose a sort method
+ {
+ float r, g, b, a;
+ this->getColor(0, &r, &g, &b, &a);
+ }
+
+ SkPoint pts[2] = { m_p0, m_p1 }; // convert to SkPoint
+
+ const size_t count = m_stops.size();
+ SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar)));
+ SkColor* colors = (SkColor*)storage.get();
+ SkScalar* pos = (SkScalar*)(colors + count);
+
+ Vector<ColorStop>::iterator iter = m_stops.begin();
+ for (int i = 0; iter != m_stops.end(); i++) {
+ pos[i] = SkFloatToScalar(iter->stop);
+ colors[i] = SkColorSetARGB(F2B(iter->alpha), F2B(iter->red),
+ F2B(iter->green), F2B(iter->blue));
+ ++iter;
+ }
+
+ SkShader* s;
+ if (m_radial)
+ s = SkGradientShader::CreateTwoPointRadial(pts[0],
+ SkFloatToScalar(m_r0),
+ pts[1],
+ SkFloatToScalar(m_r1),
+ colors, pos, count, mode);
+ else
+ s = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+
+ if (NULL == s)
+ s = new SkColorShader(0);
+
+ // zap our previous shader, if present
+ SkSafeUnref(m_gradient->m_shader);
+ m_gradient->m_shader = s;
+ m_gradient->m_tileMode = mode;
+ SkMatrix matrix = m_gradientSpaceTransformation;
+ s->setLocalMatrix(matrix);
+
+ return s;
+}
+
+void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
+{
+ SkRect r;
+ SkPaint paint;
+ // we don't care about the mode, so try to use the existing one
+ SkShader::TileMode mode = m_gradient ? m_gradient->m_tileMode :
+ SkShader::kClamp_TileMode;
+
+ paint.setAntiAlias(true);
+ paint.setShader(this->getShader(mode));
+ android_gc2canvas(context)->drawRect(rect, paint);
+}
+
+
+} //namespace
diff --git a/Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp
new file mode 100644
index 0000000..888be5b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp
@@ -0,0 +1,1334 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+#include "GraphicsContext.h"
+
+#include "AffineTransform.h"
+#include "Gradient.h"
+#include "NotImplemented.h"
+#include "Path.h"
+#include "Pattern.h"
+#include "PlatformGraphicsContext.h"
+#include "SkBitmapRef.h"
+#include "SkBlurDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDashPathEffect.h"
+#include "SkDevice.h"
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkiaUtils.h"
+#include "TransformationMatrix.h"
+#include "android_graphics.h"
+
+using namespace std;
+
+#define GC2CANVAS(ctx) (ctx)->m_data->getPlatformGfxCtx()->mCanvas
+
+namespace WebCore {
+
+static int RoundToInt(float x)
+{
+ return (int)roundf(x);
+}
+
+template <typename T> T* deepCopyPtr(const T* src)
+{
+ return src ? new T(*src) : 0;
+}
+
+// Set a bitmap shader that mimics dashing by width-on, width-off.
+// Returns false if it could not succeed (e.g. there was an existing shader)
+static bool setBitmapDash(SkPaint* paint, int width) {
+ if (width <= 0 || paint->getShader())
+ return false;
+
+ SkColor c = paint->getColor();
+
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1);
+ bm.allocPixels();
+ bm.lockPixels();
+
+ // set the ON pixel
+ *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c),
+ SkColorGetG(c), SkColorGetB(c));
+ // set the OFF pixel
+ *bm.getAddr32(1, 0) = 0;
+ bm.unlockPixels();
+
+ SkMatrix matrix;
+ matrix.setScale(SkIntToScalar(width), SK_Scalar1);
+
+ SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+ SkShader::kClamp_TileMode);
+ s->setLocalMatrix(matrix);
+
+ paint->setShader(s)->unref();
+ return true;
+}
+
+// TODO / questions
+
+// alpha: how does this interact with the alpha in Color? multiply them together?
+// mode: do I always respect this? If so, then
+// the rgb() & 0xFF000000 check will abort drawing too often
+// Is Color premultiplied or not? If it is, then I can't blindly pass it to paint.setColor()
+
+struct ShadowRec {
+ SkScalar blur;
+ SkScalar dx;
+ SkScalar dy;
+ SkColor color; // alpha>0 means valid shadow
+ ShadowRec(SkScalar b = 0,
+ SkScalar x = 0,
+ SkScalar y = 0,
+ SkColor c = 0) // by default, alpha=0, so no shadow
+ : blur(b), dx(x), dy(y), color(c)
+ {};
+};
+
+class GraphicsContextPlatformPrivate {
+public:
+ struct State {
+ SkPath* path;
+ SkPathEffect* pathEffect;
+ float miterLimit;
+ float alpha;
+ float strokeThickness;
+ SkPaint::Cap lineCap;
+ SkPaint::Join lineJoin;
+ SkXfermode::Mode mode;
+ int dashRatio; // Ratio of the length of a dash to its width
+ ShadowRec shadow;
+ SkColor fillColor;
+ SkColor strokeColor;
+ bool useAA;
+ // This is a list of clipping paths which are currently active, in the
+ // order in which they were pushed.
+ WTF::Vector<SkPath> antiAliasClipPaths;
+
+ State()
+ : path(0) // Lazily allocated
+ , pathEffect(0)
+ , miterLimit(4)
+ , alpha(1)
+ , strokeThickness(0) // Same as default in GraphicsContextPrivate.h
+ , lineCap(SkPaint::kDefault_Cap)
+ , lineJoin(SkPaint::kDefault_Join)
+ , mode(SkXfermode::kSrcOver_Mode)
+ , dashRatio(3)
+ , fillColor(SK_ColorBLACK)
+ , strokeColor(SK_ColorBLACK)
+ , useAA(true)
+ {
+ }
+
+ State(const State& other)
+ : pathEffect(other.pathEffect)
+ , miterLimit(other.miterLimit)
+ , alpha(other.alpha)
+ , strokeThickness(other.strokeThickness)
+ , lineCap(other.lineCap)
+ , lineJoin(other.lineJoin)
+ , mode(other.mode)
+ , dashRatio(other.dashRatio)
+ , shadow(other.shadow)
+ , fillColor(other.fillColor)
+ , strokeColor(other.strokeColor)
+ , useAA(other.useAA)
+ {
+ path = deepCopyPtr<SkPath>(other.path);
+ SkSafeRef(pathEffect);
+ }
+
+ ~State()
+ {
+ delete path;
+ SkSafeUnref(pathEffect);
+ }
+
+ void setShadow(int radius, int dx, int dy, SkColor c)
+ {
+ // Cut the radius in half, to visually match the effect seen in
+ // safari browser
+ shadow.blur = SkScalarHalf(SkIntToScalar(radius));
+ shadow.dx = SkIntToScalar(dx);
+ shadow.dy = SkIntToScalar(dy);
+ shadow.color = c;
+ }
+
+ bool setupShadowPaint(GraphicsContext* ctx, SkPaint* paint, SkPoint* offset)
+ {
+ paint->setAntiAlias(true);
+ paint->setDither(true);
+ paint->setXfermodeMode(mode);
+ paint->setColor(shadow.color);
+ offset->set(shadow.dx, shadow.dy);
+
+ // Currently, only GraphicsContexts associated with the
+ // HTMLCanvasElement have shadows ignore transforms set. This
+ // allows us to distinguish between CSS and Canvas shadows which
+ // have different rendering specifications.
+ uint32_t flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
+ if (ctx->shadowsIgnoreTransforms()) {
+ offset->fY = -offset->fY;
+ flags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag;
+ }
+
+ if (shadow.blur > 0) {
+ paint->setMaskFilter(SkBlurMaskFilter::Create(shadow.blur,
+ SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+ }
+ return SkColorGetA(shadow.color) && (shadow.blur || shadow.dx || shadow.dy);
+ }
+
+ SkColor applyAlpha(SkColor c) const
+ {
+ int s = RoundToInt(alpha * 256);
+ if (s >= 256)
+ return c;
+ if (s < 0)
+ return 0;
+
+ int a = SkAlphaMul(SkColorGetA(c), s);
+ return (c & 0x00FFFFFF) | (a << 24);
+ }
+ };
+
+ GraphicsContextPlatformPrivate(GraphicsContext* gfxCtx, PlatformGraphicsContext* platformGfxCtx)
+ : m_parentGfxCtx(gfxCtx)
+ , m_platformGfxCtx(platformGfxCtx)
+ , m_stateStack(sizeof(State))
+ {
+ State* state = static_cast<State*>(m_stateStack.push_back());
+ new (state) State();
+ m_state = state;
+ }
+
+ ~GraphicsContextPlatformPrivate()
+ {
+ // We force restores so we don't leak any subobjects owned by our
+ // stack of State records.
+ while (m_stateStack.count() > 0)
+ this->restore();
+
+ if (m_platformGfxCtx && m_platformGfxCtx->deleteUs())
+ delete m_platformGfxCtx;
+ }
+
+ void save()
+ {
+ State* newState = static_cast<State*>(m_stateStack.push_back());
+ new (newState) State(*m_state);
+ m_state = newState;
+ }
+
+ void restore()
+ {
+ if (!m_state->antiAliasClipPaths.isEmpty())
+ applyAntiAliasedClipPaths(m_state->antiAliasClipPaths);
+
+ m_state->~State();
+ m_stateStack.pop_back();
+ m_state = static_cast<State*>(m_stateStack.back());
+ }
+
+ void setFillColor(const Color& c)
+ {
+ m_state->fillColor = c.rgb();
+ }
+
+ void setStrokeColor(const Color& c)
+ {
+ m_state->strokeColor = c.rgb();
+ }
+
+ void setStrokeThickness(float f)
+ {
+ m_state->strokeThickness = f;
+ }
+
+ void beginPath()
+ {
+ if (m_state->path)
+ m_state->path->reset();
+ }
+
+ void addPath(const SkPath& other)
+ {
+ if (!m_state->path)
+ m_state->path = new SkPath(other);
+ else
+ m_state->path->addPath(other);
+ }
+
+ // May return null
+ SkPath* getPath() const
+ {
+ return m_state->path;
+ }
+
+ void setupPaintCommon(SkPaint* paint) const
+ {
+ paint->setAntiAlias(m_state->useAA);
+ paint->setDither(true);
+ paint->setXfermodeMode(m_state->mode);
+ if (SkColorGetA(m_state->shadow.color) > 0) {
+
+ // Currently, only GraphicsContexts associated with the
+ // HTMLCanvasElement have shadows ignore transforms set. This
+ // allows us to distinguish between CSS and Canvas shadows which
+ // have different rendering specifications.
+ SkScalar dy = m_state->shadow.dy;
+ uint32_t flags = SkBlurDrawLooper::kHighQuality_BlurFlag;
+ if (m_parentGfxCtx->shadowsIgnoreTransforms()) {
+ dy = -dy;
+ flags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag;
+ flags |= SkBlurDrawLooper::kOverrideColor_BlurFlag;
+ }
+
+ SkDrawLooper* looper = new SkBlurDrawLooper(m_state->shadow.blur,
+ m_state->shadow.dx,
+ dy,
+ m_state->shadow.color,
+ flags);
+ paint->setLooper(looper)->unref();
+ }
+ }
+
+ void setupPaintFill(SkPaint* paint) const
+ {
+ this->setupPaintCommon(paint);
+ paint->setColor(m_state->applyAlpha(m_state->fillColor));
+ }
+
+ void setupPaintBitmap(SkPaint* paint) const
+ {
+ this->setupPaintCommon(paint);
+ // We only want the global alpha for bitmaps,
+ // so just give applyAlpha opaque black
+ paint->setColor(m_state->applyAlpha(0xFF000000));
+ }
+
+ // Sets up the paint for stroking. Returns true if the style is really
+ // just a dash of squares (the size of the paint's stroke-width.
+ bool setupPaintStroke(SkPaint* paint, SkRect* rect, bool isHLine = false)
+ {
+ this->setupPaintCommon(paint);
+ paint->setColor(m_state->applyAlpha(m_state->strokeColor));
+
+ float width = m_state->strokeThickness;
+
+ // This allows dashing and dotting to work properly for hairline strokes
+ // FIXME: Should we only do this for dashed and dotted strokes?
+ if (!width)
+ width = 1;
+
+ paint->setStyle(SkPaint::kStroke_Style);
+ paint->setStrokeWidth(SkFloatToScalar(width));
+ paint->setStrokeCap(m_state->lineCap);
+ paint->setStrokeJoin(m_state->lineJoin);
+ paint->setStrokeMiter(SkFloatToScalar(m_state->miterLimit));
+
+ if (rect && (RoundToInt(width) & 1))
+ rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
+
+ SkPathEffect* pe = m_state->pathEffect;
+ if (pe) {
+ paint->setPathEffect(pe);
+ return false;
+ }
+ switch (m_parentGfxCtx->strokeStyle()) {
+ case NoStroke:
+ case SolidStroke:
+ width = 0;
+ break;
+ case DashedStroke:
+ width = m_state->dashRatio * width;
+ break;
+ // No break
+ case DottedStroke:
+ break;
+ }
+
+ if (width > 0) {
+ // Return true if we're basically a dotted dash of squares
+ bool justSqrs = RoundToInt(width) == RoundToInt(paint->getStrokeWidth());
+
+ if (justSqrs || !isHLine || !setBitmapDash(paint, width)) {
+#if 0
+ // this is slow enough that we just skip it for now
+ // see http://b/issue?id=4163023
+ SkScalar intervals[] = { width, width };
+ pe = new SkDashPathEffect(intervals, 2, 0);
+ paint->setPathEffect(pe)->unref();
+#endif
+ }
+ return justSqrs;
+ }
+ return false;
+ }
+
+ void clipPathAntiAliased(const SkPath& clipPath)
+ {
+ // If we are currently tracking any anti-alias clip paths, then we already
+ // have a layer in place and don't need to add another.
+ bool haveLayerOutstanding = m_state->antiAliasClipPaths.size();
+
+ // See comments in applyAntiAliasedClipPaths about how this works.
+ m_state->antiAliasClipPaths.append(clipPath);
+ if (!haveLayerOutstanding) {
+ SkRect bounds = clipPath.getBounds();
+ if (m_platformGfxCtx && m_platformGfxCtx->mCanvas)
+ m_platformGfxCtx->mCanvas->saveLayerAlpha(&bounds, 255,
+ static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag
+ | SkCanvas::kFullColorLayer_SaveFlag
+ | SkCanvas::kClipToLayer_SaveFlag));
+ else
+ ASSERT(0);
+ }
+ }
+
+ void applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths)
+ {
+ // Anti-aliased clipping:
+ //
+ // Refer to PlatformContextSkia.cpp's applyAntiAliasedClipPaths() for more details
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kClear_Mode);
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ if (m_platformGfxCtx && m_platformGfxCtx->mCanvas) {
+ for (size_t i = paths.size() - 1; i < paths.size(); --i) {
+ paths[i].setFillType(SkPath::kInverseWinding_FillType);
+ m_platformGfxCtx->mCanvas->drawPath(paths[i], paint);
+ }
+ m_platformGfxCtx->mCanvas->restore();
+ } else
+ ASSERT(0);
+ }
+
+ PlatformGraphicsContext* getPlatformGfxCtx()
+ {
+ return m_platformGfxCtx;
+ }
+
+ State* getState()
+ {
+ return m_state;
+ }
+private:
+ State* m_state;
+ GraphicsContext* m_parentGfxCtx; // Back-ptr to our parent
+ PlatformGraphicsContext* m_platformGfxCtx;
+ SkDeque m_stateStack;
+ // Not supported yet
+ State& operator=(const State&);
+};
+
+static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm)
+{
+ SkShader::TileMode mode = SkShader::kClamp_TileMode;
+
+ switch (sm) {
+ case SpreadMethodPad:
+ mode = SkShader::kClamp_TileMode;
+ break;
+ case SpreadMethodReflect:
+ mode = SkShader::kMirror_TileMode;
+ break;
+ case SpreadMethodRepeat:
+ mode = SkShader::kRepeat_TileMode;
+ break;
+ }
+ return mode;
+}
+
+static void extactShader(SkPaint* paint, Pattern* pat, Gradient* grad)
+{
+ if (pat) {
+ // platformPattern() returns a cached obj
+ paint->setShader(pat->platformPattern(AffineTransform()));
+ } else if (grad) {
+ // grad->getShader() returns a cached obj
+ GradientSpreadMethod sm = grad->spreadMethod();
+ paint->setShader(grad->getShader(SpreadMethod2TileMode(sm)));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height)
+{
+ PlatformGraphicsContext* pgc = new PlatformGraphicsContext();
+
+ SkBitmap bitmap;
+
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+ pgc->mCanvas->setBitmapDevice(bitmap);
+
+ GraphicsContext* ctx = new GraphicsContext(pgc);
+ return ctx;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
+{
+ m_data = new GraphicsContextPlatformPrivate(this, gc);
+ setPaintingDisabled(!gc || !gc->mCanvas);
+}
+
+void GraphicsContext::platformDestroy()
+{
+ delete m_data;
+}
+
+void GraphicsContext::savePlatformState()
+{
+ // Save our private State
+ m_data->save();
+ // Save our native canvas
+ GC2CANVAS(this)->save();
+}
+
+void GraphicsContext::restorePlatformState()
+{
+ // Restore our private State
+ m_data->restore();
+ // Restore our native canvas
+ GC2CANVAS(this)->restore();
+}
+
+bool GraphicsContext::willFill() const
+{
+ return m_data->getState()->fillColor;
+}
+
+bool GraphicsContext::willStroke() const
+{
+ return m_data->getState()->strokeColor;
+}
+
+const SkPath* GraphicsContext::getCurrPath() const
+{
+ return m_data->getState()->path;
+}
+
+// Draws a filled rectangle with a stroked border.
+void GraphicsContext::drawRect(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+ SkRect r(rect);
+
+ if (fillColor().alpha()) {
+ m_data->setupPaintFill(&paint);
+ GC2CANVAS(this)->drawRect(r, paint);
+ }
+
+ // According to GraphicsContext.h, stroking inside drawRect always means
+ // a stroke of 1 inside the rect.
+ if (strokeStyle() != NoStroke && strokeColor().alpha()) {
+ paint.reset();
+ m_data->setupPaintStroke(&paint, &r);
+ paint.setPathEffect(0); // No dashing please
+ paint.setStrokeWidth(SK_Scalar1); // Always just 1.0 width
+ r.inset(SK_ScalarHalf, SK_ScalarHalf); // Ensure we're "inside"
+ GC2CANVAS(this)->drawRect(r, paint);
+ }
+}
+
+// This is only used to draw borders.
+void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
+{
+ if (paintingDisabled())
+ return;
+
+ StrokeStyle style = strokeStyle();
+ if (style == NoStroke)
+ return;
+
+ SkPaint paint;
+ SkCanvas* canvas = GC2CANVAS(this);
+ const int idx = SkAbs32(point2.x() - point1.x());
+ const int idy = SkAbs32(point2.y() - point1.y());
+
+ // Special-case horizontal and vertical lines that are really just dots
+ if (m_data->setupPaintStroke(&paint, 0, !idy) && (!idx || !idy)) {
+ const SkScalar diameter = paint.getStrokeWidth();
+ const SkScalar radius = SkScalarHalf(diameter);
+ SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x()));
+ SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y()));
+ SkScalar dx, dy;
+ int count;
+ SkRect bounds;
+
+ if (!idy) { // Horizontal
+ bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius);
+ x += radius;
+ dx = diameter * 2;
+ dy = 0;
+ count = idx;
+ } else { // Vertical
+ bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy));
+ y += radius;
+ dx = 0;
+ dy = diameter * 2;
+ count = idy;
+ }
+
+ // The actual count is the number of ONs we hit alternating
+ // ON(diameter), OFF(diameter), ...
+ {
+ SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter);
+ // Now compute the number of cells (ON and OFF)
+ count = SkScalarRound(width);
+ // Now compute the number of ONs
+ count = (count + 1) >> 1;
+ }
+
+ SkAutoMalloc storage(count * sizeof(SkPoint));
+ SkPoint* verts = (SkPoint*)storage.get();
+ // Now build the array of vertices to past to drawPoints
+ for (int i = 0; i < count; i++) {
+ verts[i].set(x, y);
+ x += dx;
+ y += dy;
+ }
+
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setPathEffect(0);
+
+ // Clipping to bounds is not required for correctness, but it does
+ // allow us to reject the entire array of points if we are completely
+ // offscreen. This is common in a webpage for android, where most of
+ // the content is clipped out. If drawPoints took an (optional) bounds
+ // parameter, that might even be better, as we would *just* use it for
+ // culling, and not both wacking the canvas' save/restore stack.
+ canvas->save(SkCanvas::kClip_SaveFlag);
+ canvas->clipRect(bounds);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint);
+ canvas->restore();
+ } else {
+ SkPoint pts[2] = { point1, point2 };
+ canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
+ }
+}
+
+static void setrectForUnderline(SkRect* r, GraphicsContext* context, const IntPoint& point, int yOffset, int width)
+{
+ float lineThickness = context->strokeThickness();
+#if 0
+ if (lineThickness < 1) // Do we really need/want this?
+ lineThickness = 1;
+#endif
+ r->fLeft = SkIntToScalar(point.x());
+ r->fTop = SkIntToScalar(point.y() + yOffset);
+ r->fRight = r->fLeft + SkIntToScalar(width);
+ r->fBottom = r->fTop + SkFloatToScalar(lineThickness);
+}
+
+void GraphicsContext::drawLineForText(IntPoint const& pt, int width, bool)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect r;
+ setrectForUnderline(&r, this, pt, 0, width);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(this->strokeColor().rgb());
+
+ GC2CANVAS(this)->drawRect(r, paint);
+}
+
+// TODO: Should we draw different based on TextCheckingLineStyle?
+void GraphicsContext::drawLineForTextChecking(const IntPoint& pt, int width, TextCheckingLineStyle)
+{
+ if (paintingDisabled())
+ return;
+
+ SkRect r;
+ setrectForUnderline(&r, this, pt, 0, width);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorRED); // Is this specified somewhere?
+
+ GC2CANVAS(this)->drawRect(r, paint);
+}
+
+// This method is only used to draw the little circles used in lists.
+void GraphicsContext::drawEllipse(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+ SkRect oval(rect);
+
+ if (fillColor().rgb() & 0xFF000000) {
+ m_data->setupPaintFill(&paint);
+ GC2CANVAS(this)->drawOval(oval, paint);
+ }
+ if (strokeStyle() != NoStroke) {
+ paint.reset();
+ m_data->setupPaintStroke(&paint, &oval);
+ GC2CANVAS(this)->drawOval(oval, paint);
+ }
+}
+
+static inline int fastMod(int value, int max)
+{
+ int sign = SkExtractSign(value);
+
+ value = SkApplySign(value, sign);
+ if (value >= max)
+ value %= max;
+ return SkApplySign(value, sign);
+}
+
+void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPath path;
+ SkPaint paint;
+ SkRect oval(r);
+
+ if (strokeStyle() == NoStroke) {
+ m_data->setupPaintFill(&paint); // We want the fill color
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkFloatToScalar(this->strokeThickness()));
+ } else
+ m_data->setupPaintStroke(&paint, 0);
+
+ // We do this before converting to scalar, so we don't overflow SkFixed
+ startAngle = fastMod(startAngle, 360);
+ angleSpan = fastMod(angleSpan, 360);
+
+ path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
+ GC2CANVAS(this)->drawPath(path, paint);
+}
+
+void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
+{
+ if (paintingDisabled())
+ return;
+
+ if (numPoints <= 1)
+ return;
+
+ SkPaint paint;
+ SkPath path;
+
+ path.incReserve(numPoints);
+ path.moveTo(SkFloatToScalar(points[0].x()), SkFloatToScalar(points[0].y()));
+ for (size_t i = 1; i < numPoints; i++)
+ path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y()));
+
+ if (GC2CANVAS(this)->quickReject(path, shouldAntialias ?
+ SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) {
+ return;
+ }
+
+ if (fillColor().rgb() & 0xFF000000) {
+ m_data->setupPaintFill(&paint);
+ paint.setAntiAlias(shouldAntialias);
+ GC2CANVAS(this)->drawPath(path, paint);
+ }
+
+ if (strokeStyle() != NoStroke) {
+ paint.reset();
+ m_data->setupPaintStroke(&paint, 0);
+ paint.setAntiAlias(shouldAntialias);
+ GC2CANVAS(this)->drawPath(path, paint);
+ }
+}
+
+void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
+ const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+ SkPath path;
+ SkScalar radii[8];
+
+ radii[0] = SkIntToScalar(topLeft.width());
+ radii[1] = SkIntToScalar(topLeft.height());
+ radii[2] = SkIntToScalar(topRight.width());
+ radii[3] = SkIntToScalar(topRight.height());
+ radii[4] = SkIntToScalar(bottomRight.width());
+ radii[5] = SkIntToScalar(bottomRight.height());
+ radii[6] = SkIntToScalar(bottomLeft.width());
+ radii[7] = SkIntToScalar(bottomLeft.height());
+ path.addRoundRect(rect, radii);
+
+ m_data->setupPaintFill(&paint);
+ GC2CANVAS(this)->drawPath(path, paint);
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect)
+{
+ SkPaint paint;
+
+ m_data->setupPaintFill(&paint);
+
+ extactShader(&paint,
+ m_state.fillPattern.get(),
+ m_state.fillGradient.get());
+
+ GC2CANVAS(this)->drawRect(rect, paint);
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
+{
+ if (paintingDisabled())
+ return;
+
+ if (color.rgb() & 0xFF000000) {
+ SkPaint paint;
+
+ m_data->setupPaintCommon(&paint);
+ paint.setColor(color.rgb()); // Punch in the specified color
+ paint.setShader(0); // In case we had one set
+
+ // Sometimes we record and draw portions of the page, using clips
+ // for each portion. The problem with this is that webkit, sometimes,
+ // sees that we're only recording a portion, and they adjust some of
+ // their rectangle coordinates accordingly (e.g.
+ // RenderBoxModelObject::paintFillLayerExtended() which calls
+ // rect.intersect(paintInfo.rect) and then draws the bg with that
+ // rect. The result is that we end up drawing rects that are meant to
+ // seam together (one for each portion), but if the rects have
+ // fractional coordinates (e.g. we are zoomed by a fractional amount)
+ // we will double-draw those edges, resulting in visual cracks or
+ // artifacts.
+
+ // The fix seems to be to just turn off antialasing for rects (this
+ // entry-point in GraphicsContext seems to have been sufficient,
+ // though perhaps we'll find we need to do this as well in fillRect(r)
+ // as well.) Currently setupPaintCommon() enables antialiasing.
+
+ // Since we never show the page rotated at a funny angle, disabling
+ // antialiasing seems to have no real down-side, and it does fix the
+ // bug when we're zoomed (and drawing portions that need to seam).
+ paint.setAntiAlias(false);
+
+ GC2CANVAS(this)->drawRect(rect, paint);
+ }
+}
+
+void GraphicsContext::clip(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ GC2CANVAS(this)->clipRect(rect);
+}
+
+void GraphicsContext::clip(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->clipPathAntiAliased(*path.platformPath());
+}
+
+void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPath path;
+ SkRect r(rect);
+
+ path.addOval(r, SkPath::kCW_Direction);
+ // Only perform the inset if we won't invert r
+ if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
+ // Adding one to the thickness doesn't make the border too thick as
+ // it's painted over afterwards. But without this adjustment the
+ // border appears a little anemic after anti-aliasing.
+ r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1));
+ path.addOval(r, SkPath::kCCW_Direction);
+ }
+ m_data->clipPathAntiAliased(path);
+}
+
+void GraphicsContext::canvasClip(const Path& path)
+{
+ clip(path);
+}
+
+void GraphicsContext::clipOut(const IntRect& r)
+{
+ if (paintingDisabled())
+ return;
+
+ GC2CANVAS(this)->clipRect(r, SkRegion::kDifference_Op);
+}
+
+#if ENABLE(SVG)
+void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
+{
+ if (paintingDisabled())
+ return;
+
+ // FIXME: Be smarter about this.
+ beginPath();
+ addPath(pathToClip);
+
+ const SkPath* oldPath = m_data->getPath();
+ SkPath path(*oldPath);
+ path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
+ GC2CANVAS(this)->clipPath(path);
+}
+#endif
+
+void GraphicsContext::clipOut(const Path& p)
+{
+ if (paintingDisabled())
+ return;
+
+ GC2CANVAS(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if SVG_SUPPORT
+KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
+{
+ return new KRenderingDeviceContextQuartz(platformContext());
+}
+#endif
+
+// These are the flags we need when we call saveLayer for transparency.
+// Since it does not appear that webkit intends this to also save/restore
+// the matrix or clip, I do not give those flags (for performance)
+#define TRANSPARENCY_SAVEFLAGS \
+ (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | \
+ SkCanvas::kFullColorLayer_SaveFlag)
+
+void GraphicsContext::beginTransparencyLayer(float opacity)
+{
+ if (paintingDisabled())
+ return;
+
+ SkCanvas* canvas = GC2CANVAS(this);
+ canvas->saveLayerAlpha(0, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS);
+}
+
+void GraphicsContext::endTransparencyLayer()
+{
+ if (paintingDisabled())
+ return;
+
+ GC2CANVAS(this)->restore();
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void GraphicsContext::setupBitmapPaint(SkPaint* paint)
+{
+ m_data->setupPaintBitmap(paint);
+}
+
+void GraphicsContext::setupFillPaint(SkPaint* paint)
+{
+ m_data->setupPaintFill(paint);
+}
+
+void GraphicsContext::setupStrokePaint(SkPaint* paint)
+{
+ m_data->setupPaintStroke(paint, 0);
+}
+
+bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset)
+{
+ return m_data->getState()->setupShadowPaint(this, paint, offset);
+}
+
+void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace)
+{
+ m_data->setStrokeColor(c);
+}
+
+void GraphicsContext::setPlatformStrokeThickness(float f)
+{
+ m_data->setStrokeThickness(f);
+}
+
+void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace)
+{
+ m_data->setFillColor(c);
+}
+
+void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
+{
+ if (paintingDisabled())
+ return;
+
+ if (blur <= 0)
+ this->clearPlatformShadow();
+
+ SkColor c;
+ if (color.isValid())
+ c = color.rgb();
+ else
+ c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color
+ m_data->getState()->setShadow(blur, size.width(), size.height(), c);
+}
+
+void GraphicsContext::clearPlatformShadow()
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->getState()->setShadow(0, 0, 0, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GraphicsContext::drawFocusRing(const Vector<IntRect>&, int, int, const Color&)
+{
+ // Do nothing, since we draw the focus ring independently.
+}
+
+void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&)
+{
+ // Do nothing, since we draw the focus ring independently.
+}
+
+PlatformGraphicsContext* GraphicsContext::platformContext() const
+{
+ ASSERT(!paintingDisabled());
+ return m_data->getPlatformGfxCtx();
+}
+
+void GraphicsContext::setMiterLimit(float limit)
+{
+ m_data->getState()->miterLimit = limit;
+}
+
+void GraphicsContext::setAlpha(float alpha)
+{
+ m_data->getState()->alpha = alpha;
+}
+
+void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
+{
+ m_data->getState()->mode = WebCoreCompositeToSkiaComposite(op);
+}
+
+void GraphicsContext::clearRect(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+
+ m_data->setupPaintFill(&paint);
+ paint.setXfermodeMode(SkXfermode::kClear_Mode);
+ GC2CANVAS(this)->drawRect(rect, paint);
+}
+
+void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
+{
+ if (paintingDisabled())
+ return;
+
+ SkPaint paint;
+
+ m_data->setupPaintStroke(&paint, 0);
+ paint.setStrokeWidth(SkFloatToScalar(lineWidth));
+ GC2CANVAS(this)->drawRect(rect, paint);
+}
+
+void GraphicsContext::setLineCap(LineCap cap)
+{
+ switch (cap) {
+ case ButtCap:
+ m_data->getState()->lineCap = SkPaint::kButt_Cap;
+ break;
+ case RoundCap:
+ m_data->getState()->lineCap = SkPaint::kRound_Cap;
+ break;
+ case SquareCap:
+ m_data->getState()->lineCap = SkPaint::kSquare_Cap;
+ break;
+ default:
+ SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap));
+ break;
+ }
+}
+
+#if ENABLE(SVG)
+void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
+{
+ if (paintingDisabled())
+ return;
+
+ size_t dashLength = dashes.size();
+ if (!dashLength)
+ return;
+
+ size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
+ SkScalar* intervals = new SkScalar[count];
+
+ for (unsigned int i = 0; i < count; i++)
+ intervals[i] = SkFloatToScalar(dashes[i % dashLength]);
+ SkPathEffect **effectPtr = &m_data->getState()->pathEffect;
+ SkSafeUnref(*effectPtr);
+ *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset));
+
+ delete[] intervals;
+}
+#endif
+
+void GraphicsContext::setLineJoin(LineJoin join)
+{
+ switch (join) {
+ case MiterJoin:
+ m_data->getState()->lineJoin = SkPaint::kMiter_Join;
+ break;
+ case RoundJoin:
+ m_data->getState()->lineJoin = SkPaint::kRound_Join;
+ break;
+ case BevelJoin:
+ m_data->getState()->lineJoin = SkPaint::kBevel_Join;
+ break;
+ default:
+ SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join));
+ break;
+ }
+}
+
+void GraphicsContext::scale(const FloatSize& size)
+{
+ if (paintingDisabled())
+ return;
+ GC2CANVAS(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
+}
+
+void GraphicsContext::rotate(float angleInRadians)
+{
+ if (paintingDisabled())
+ return;
+ GC2CANVAS(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f)));
+}
+
+void GraphicsContext::translate(float x, float y)
+{
+ if (paintingDisabled())
+ return;
+ GC2CANVAS(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y));
+}
+
+void GraphicsContext::concatCTM(const AffineTransform& affine)
+{
+ if (paintingDisabled())
+ return;
+ GC2CANVAS(this)->concat(affine);
+}
+
+// This is intended to round the rect to device pixels (through the CTM)
+// and then invert the result back into source space, with the hope that when
+// it is drawn (through the matrix), it will land in the "right" place (i.e.
+// on pixel boundaries).
+
+// For android, we record this geometry once and then draw it though various
+// scale factors as the user zooms, without re-recording. Thus this routine
+// should just leave the original geometry alone.
+
+// If we instead draw into bitmap tiles, we should then perform this
+// transform -> round -> inverse step.
+
+FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
+{
+ return rect;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
+{
+// Appears to be PDF specific, so we ignore it
+#if 0
+if (paintingDisabled())
+ return;
+
+CFURLRef urlRef = link.createCFURL();
+if (urlRef) {
+ CGContextRef context = platformContext();
+
+ // Get the bounding box to handle clipping.
+ CGRect box = CGContextGetClipBoundingBox(context);
+
+ IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
+ IntRect rect = destRect;
+ rect.intersect(intBox);
+
+ CGPDFContextSetURLForRect(context, urlRef,
+ CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
+
+ CFRelease(urlRef);
+}
+#endif
+}
+
+void GraphicsContext::setPlatformShouldAntialias(bool useAA)
+{
+ if (paintingDisabled())
+ return;
+ m_data->getState()->useAA = useAA;
+}
+
+AffineTransform GraphicsContext::getCTM() const
+{
+ const SkMatrix& m = GC2CANVAS(this)->getTotalMatrix();
+ return AffineTransform(SkScalarToDouble(m.getScaleX()), // a
+ SkScalarToDouble(m.getSkewY()), // b
+ SkScalarToDouble(m.getSkewX()), // c
+ SkScalarToDouble(m.getScaleY()), // d
+ SkScalarToDouble(m.getTranslateX()), // e
+ SkScalarToDouble(m.getTranslateY())); // f
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GraphicsContext::beginPath()
+{
+ m_data->beginPath();
+}
+
+void GraphicsContext::addPath(const Path& p)
+{
+ m_data->addPath(*p.platformPath());
+}
+
+void GraphicsContext::fillPath(const Path& pathToFill)
+{
+ // FIXME: Be smarter about this.
+ beginPath();
+ addPath(pathToFill);
+
+ SkPath* path = m_data->getPath();
+ if (paintingDisabled() || !path)
+ return;
+
+ switch (this->fillRule()) {
+ case RULE_NONZERO:
+ path->setFillType(SkPath::kWinding_FillType);
+ break;
+ case RULE_EVENODD:
+ path->setFillType(SkPath::kEvenOdd_FillType);
+ break;
+ }
+
+ SkPaint paint;
+ m_data->setupPaintFill(&paint);
+
+ extactShader(&paint,
+ m_state.fillPattern.get(),
+ m_state.fillGradient.get());
+
+ GC2CANVAS(this)->drawPath(*path, paint);
+}
+
+void GraphicsContext::strokePath(const Path& pathToStroke)
+{
+ // FIXME: Be smarter about this.
+ beginPath();
+ addPath(pathToStroke);
+
+ const SkPath* path = m_data->getPath();
+ if (paintingDisabled() || !path)
+ return;
+
+ SkPaint paint;
+ m_data->setupPaintStroke(&paint, 0);
+
+ extactShader(&paint,
+ m_state.strokePattern.get(),
+ m_state.strokeGradient.get());
+
+ GC2CANVAS(this)->drawPath(*path, paint);
+}
+
+InterpolationQuality GraphicsContext::imageInterpolationQuality() const
+{
+ notImplemented();
+ return InterpolationDefault;
+}
+
+void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
+{
+#if 0
+ enum InterpolationQuality {
+ InterpolationDefault,
+ InterpolationNone,
+ InterpolationLow,
+ InterpolationMedium,
+ InterpolationHigh
+ };
+#endif
+ // TODO: record this, so we can know when to use bitmap-filtering when we draw
+ // ... not sure how meaningful this will be given our playback model.
+
+ // Certainly safe to do nothing for the present.
+}
+
+void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias)
+{
+ if (paintingDisabled())
+ return;
+
+ if (numPoints <= 1)
+ return;
+
+ // FIXME: IMPLEMENT!
+}
+
+} // namespace WebCore
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc)
+{
+ return gc->platformContext()->mCanvas;
+}
diff --git a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp
new file mode 100644
index 0000000..e8120f9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp
@@ -0,0 +1,989 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config.h"
+#include "GraphicsLayerAndroid.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "AndroidAnimation.h"
+#include "Animation.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "Image.h"
+#include "Length.h"
+#include "MediaLayer.h"
+#include "PlatformBridge.h"
+#include "PlatformGraphicsContext.h"
+#include "RenderLayerBacking.h"
+#include "RenderView.h"
+#include "RotateTransformOperation.h"
+#include "ScaleTransformOperation.h"
+#include "ScrollableLayerAndroid.h"
+#include "SkCanvas.h"
+#include "SkLayer.h"
+#include "TransformationMatrix.h"
+#include "TranslateTransformOperation.h"
+
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef LOG
+#define LOG(...) android_printLog(ANDROID_LOG_DEBUG, "GraphicsLayer", __VA_ARGS__)
+#define MLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GraphicsLayer", __VA_ARGS__)
+#define TLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GraphicsLayer", __VA_ARGS__)
+
+#undef LOG
+#define LOG(...)
+#undef MLOG
+#define MLOG(...)
+#undef TLOG
+#define TLOG(...)
+#undef LAYER_DEBUG
+
+using namespace std;
+
+static bool gPaused;
+static double gPausedDelay;
+
+namespace WebCore {
+
+static int gDebugGraphicsLayerAndroidInstances = 0;
+inline int GraphicsLayerAndroid::instancesCount()
+{
+ return gDebugGraphicsLayerAndroidInstances;
+}
+
+static String propertyIdToString(AnimatedPropertyID property)
+{
+ switch (property) {
+ case AnimatedPropertyWebkitTransform:
+ return "transform";
+ case AnimatedPropertyOpacity:
+ return "opacity";
+ case AnimatedPropertyBackgroundColor:
+ return "backgroundColor";
+ case AnimatedPropertyInvalid:
+ ASSERT_NOT_REACHED();
+ }
+ ASSERT_NOT_REACHED();
+ return "";
+}
+
+PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client)
+{
+ return new GraphicsLayerAndroid(client);
+}
+
+SkLength convertLength(Length len)
+{
+ SkLength length;
+ length.type = SkLength::Undefined;
+ length.value = 0;
+ if (len.type() == WebCore::Percent) {
+ length.type = SkLength::Percent;
+ length.value = len.percent();
+ }
+ if (len.type() == WebCore::Fixed) {
+ length.type = SkLength::Fixed;
+ length.value = len.value();
+ }
+ return length;
+}
+
+static RenderLayer* renderLayerFromClient(GraphicsLayerClient* client)
+{
+ if (client)
+ return static_cast<RenderLayerBacking*>(client)->owningLayer();
+ return 0;
+}
+
+GraphicsLayerAndroid::GraphicsLayerAndroid(GraphicsLayerClient* client) :
+ GraphicsLayer(client),
+ m_needsSyncChildren(false),
+ m_needsSyncMask(false),
+ m_needsRepaint(false),
+ m_needsNotifyClient(false),
+ m_haveContents(false),
+ m_haveImage(false),
+ m_newImage(false),
+ m_imageRef(0),
+ m_foregroundLayer(0),
+ m_foregroundClipLayer(0)
+{
+ RenderLayer* renderLayer = renderLayerFromClient(m_client);
+ m_contentLayer = new LayerAndroid(renderLayer);
+ gDebugGraphicsLayerAndroidInstances++;
+}
+
+GraphicsLayerAndroid::~GraphicsLayerAndroid()
+{
+ m_contentLayer->unref();
+ SkSafeUnref(m_foregroundLayer);
+ SkSafeUnref(m_foregroundClipLayer);
+ gDebugGraphicsLayerAndroidInstances--;
+}
+
+void GraphicsLayerAndroid::setName(const String& name)
+{
+ GraphicsLayer::setName(name);
+}
+
+NativeLayer GraphicsLayerAndroid::nativeLayer() const
+{
+ LOG("(%x) nativeLayer", this);
+ return 0;
+}
+
+bool GraphicsLayerAndroid::setChildren(const Vector<GraphicsLayer*>& children)
+{
+ bool childrenChanged = GraphicsLayer::setChildren(children);
+ if (childrenChanged) {
+ m_needsSyncChildren = true;
+ askForSync();
+ }
+
+ return childrenChanged;
+}
+
+void GraphicsLayerAndroid::addChild(GraphicsLayer* childLayer)
+{
+#ifndef NDEBUG
+ const String& name = childLayer->name();
+ LOG("(%x) addChild: %x (%s)", this, childLayer, name.latin1().data());
+#endif
+ GraphicsLayer::addChild(childLayer);
+ m_needsSyncChildren = true;
+ askForSync();
+}
+
+void GraphicsLayerAndroid::addChildAtIndex(GraphicsLayer* childLayer, int index)
+{
+ LOG("(%x) addChild %x AtIndex %d", this, childLayer, index);
+ GraphicsLayer::addChildAtIndex(childLayer, index);
+ m_needsSyncChildren = true;
+ askForSync();
+}
+
+void GraphicsLayerAndroid::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling)
+{
+ LOG("(%x) addChild %x Below %x", this, childLayer, sibling);
+ GraphicsLayer::addChildBelow(childLayer, sibling);
+ m_needsSyncChildren = true;
+ askForSync();
+}
+
+void GraphicsLayerAndroid::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling)
+{
+ LOG("(%x) addChild %x Above %x", this, childLayer, sibling);
+ GraphicsLayer::addChildAbove(childLayer, sibling);
+ m_needsSyncChildren = true;
+ askForSync();
+}
+
+bool GraphicsLayerAndroid::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
+{
+ LOG("(%x) replaceChild %x by %x", this, oldChild, newChild);
+ bool ret = GraphicsLayer::replaceChild(oldChild, newChild);
+ if (ret) {
+ m_needsSyncChildren = true;
+ askForSync();
+ }
+ return ret;
+}
+
+void GraphicsLayerAndroid::removeFromParent()
+{
+ LOG("(%x) removeFromParent()", this);
+ GraphicsLayerAndroid* parent = static_cast<GraphicsLayerAndroid*>(m_parent);
+ GraphicsLayer::removeFromParent();
+ // Update the parent's children.
+ if (parent) {
+ parent->m_needsSyncChildren = true;
+ askForSync();
+ }
+}
+
+void GraphicsLayerAndroid::updateFixedPosition()
+{
+ if (!m_client)
+ return;
+
+ RenderLayer* renderLayer = renderLayerFromClient(m_client);
+ RenderView* view = static_cast<RenderView*>(renderLayer->renderer());
+
+ // We will need the Iframe flag in the LayerAndroid tree for fixed position
+ if (view && view->isRenderIFrame())
+ m_contentLayer->setIsIframe(true);
+ // If we are a fixed position layer, just set it
+ if (view->isPositioned() && view->style()->position() == FixedPosition) {
+ // We need to get the passed CSS properties for the element
+ SkLength left, top, right, bottom;
+ left = convertLength(view->style()->left());
+ top = convertLength(view->style()->top());
+ right = convertLength(view->style()->right());
+ bottom = convertLength(view->style()->bottom());
+
+ // We also need to get the margin...
+ SkLength marginLeft, marginTop, marginRight, marginBottom;
+ marginLeft = convertLength(view->style()->marginLeft());
+ marginTop = convertLength(view->style()->marginTop());
+ marginRight = convertLength(view->style()->marginRight());
+ marginBottom = convertLength(view->style()->marginBottom());
+
+ // The layer can be bigger than the element we want to draw;
+ // not only that, the layout rect of the element might also be
+ // different from the visible rect of that element (i.e. the element
+ // has a CSS shadow property -- the shadow is "outside" the element).
+ // We thus need to:
+ // 1/ get the size of the element (w,h), using the layoutOverflow rect
+ // 2/ pass the current offset of the painting relative to the layer
+ int w = view->rightLayoutOverflow() - view->leftLayoutOverflow();
+ int h = view->bottomLayoutOverflow() - view->topLayoutOverflow();
+ int paintingOffsetX = - offsetFromRenderer().width();
+ int paintingOffsetY = - offsetFromRenderer().height();
+
+ SkRect viewRect;
+ viewRect.set(paintingOffsetX, paintingOffsetY, paintingOffsetX + w, paintingOffsetY + h);
+ IntPoint renderLayerPos(renderLayer->x(), renderLayer->y());
+ m_contentLayer->setFixedPosition(left, top, right, bottom,
+ marginLeft, marginTop,
+ marginRight, marginBottom,
+ renderLayerPos,
+ viewRect);
+ }
+}
+
+void GraphicsLayerAndroid::setPosition(const FloatPoint& point)
+{
+ if (point == m_position)
+ return;
+
+ FloatPoint pos(point);
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ // Add the scroll position back in. When scrolling a layer, all the children
+ // are positioned based on the content scroll. Adding the scroll position
+ // back in allows the children to draw based on 0,0.
+ RenderLayer* layer = renderLayerFromClient(m_client);
+ if (layer && layer->parent() && layer->parent()->hasOverflowScroll())
+ pos += layer->parent()->scrolledContentOffset();
+#endif
+
+ GraphicsLayer::setPosition(pos);
+
+#ifdef LAYER_DEBUG_2
+ LOG("(%x) setPosition(%.2f,%.2f) pos(%.2f, %.2f) anchor(%.2f,%.2f) size(%.2f, %.2f)",
+ this, point.x(), point.y(), m_position.x(), m_position.y(),
+ m_anchorPoint.x(), m_anchorPoint.y(), m_size.width(), m_size.height());
+#endif
+ m_contentLayer->setPosition(pos.x(), pos.y());
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setPreserves3D(bool preserves3D)
+{
+ if (preserves3D == m_preserves3D)
+ return;
+
+ GraphicsLayer::setPreserves3D(preserves3D);
+ m_contentLayer->setPreserves3D(preserves3D);
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setAnchorPoint(const FloatPoint3D& point)
+{
+ if (point == m_anchorPoint)
+ return;
+ GraphicsLayer::setAnchorPoint(point);
+ m_contentLayer->setAnchorPoint(point.x(), point.y());
+ m_contentLayer->setAnchorPointZ(point.z());
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setSize(const FloatSize& size)
+{
+ if (size == m_size)
+ return;
+ MLOG("(%x) setSize (%.2f,%.2f)", this, size.width(), size.height());
+ GraphicsLayer::setSize(size);
+
+ // If it is a media layer the size may have changed as a result of the media
+ // element (e.g. plugin) gaining focus. Therefore, we must sync the size of
+ // the focus' outline so that our UI thread can draw accordingly.
+ if (m_contentLayer->isMedia() && m_client) {
+ RenderLayer* layer = renderLayerFromClient(m_client);
+ RenderBox* box = layer->renderBox();
+ int outline = box->view()->maximalOutlineSize();
+ static_cast<MediaLayer*>(m_contentLayer)->setOutlineSize(outline);
+ LOG("Media Outline: %d %p %p %p", outline, m_client, layer, box);
+ LOG("Media Size: %g,%g", size.width(), size.height());
+ }
+
+ m_contentLayer->setSize(size.width(), size.height());
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setBackfaceVisibility(bool b)
+{
+ GraphicsLayer::setBackfaceVisibility(b);
+ m_contentLayer->setBackfaceVisibility(b);
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setTransform(const TransformationMatrix& t)
+{
+ if (t == m_transform)
+ return;
+
+ GraphicsLayer::setTransform(t);
+ m_contentLayer->setTransform(t);
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setChildrenTransform(const TransformationMatrix& t)
+{
+ if (t == m_childrenTransform)
+ return;
+ LOG("(%x) setChildrenTransform", this);
+
+ GraphicsLayer::setChildrenTransform(t);
+ m_contentLayer->setChildrenTransform(t);
+ for (unsigned int i = 0; i < m_children.size(); i++) {
+ GraphicsLayer* layer = m_children[i];
+ layer->setTransform(t);
+ if (layer->children().size())
+ layer->setChildrenTransform(t);
+ }
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setMaskLayer(GraphicsLayer* layer)
+{
+ if (layer == m_maskLayer)
+ return;
+
+ GraphicsLayer::setMaskLayer(layer);
+ m_needsSyncMask = true;
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setMasksToBounds(bool masksToBounds)
+{
+ if (masksToBounds == m_masksToBounds)
+ return;
+ GraphicsLayer::setMasksToBounds(masksToBounds);
+ m_needsSyncMask = true;
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setDrawsContent(bool drawsContent)
+{
+ if (drawsContent == m_drawsContent)
+ return;
+ GraphicsLayer::setDrawsContent(drawsContent);
+ if (m_drawsContent) {
+ m_haveContents = true;
+ setNeedsDisplay();
+ }
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setBackgroundColor(const Color& color)
+{
+ if (color == m_backgroundColor)
+ return;
+ LOG("(%x) setBackgroundColor", this);
+ GraphicsLayer::setBackgroundColor(color);
+ SkColor c = SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue());
+ m_contentLayer->setBackgroundColor(c);
+ m_haveContents = true;
+ askForSync();
+}
+
+void GraphicsLayerAndroid::clearBackgroundColor()
+{
+ LOG("(%x) clearBackgroundColor", this);
+ GraphicsLayer::clearBackgroundColor();
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setContentsOpaque(bool opaque)
+{
+ if (opaque == m_contentsOpaque)
+ return;
+ LOG("(%x) setContentsOpaque (%d)", this, opaque);
+ GraphicsLayer::setContentsOpaque(opaque);
+ m_haveContents = true;
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setOpacity(float opacity)
+{
+ LOG("(%x) setOpacity: %.2f", this, opacity);
+ float clampedOpacity = max(0.0f, min(opacity, 1.0f));
+
+ if (clampedOpacity == m_opacity)
+ return;
+
+ MLOG("(%x) setFinalOpacity: %.2f=>%.2f (%.2f)", this,
+ opacity, clampedOpacity, m_opacity);
+ GraphicsLayer::setOpacity(clampedOpacity);
+ m_contentLayer->setOpacity(clampedOpacity);
+ askForSync();
+}
+
+void GraphicsLayerAndroid::setNeedsDisplay()
+{
+ LOG("(%x) setNeedsDisplay()", this);
+ FloatRect rect(0, 0, m_size.width(), m_size.height());
+ setNeedsDisplayInRect(rect);
+}
+
+// Helper to set and clear the painting phase as well as auto restore the
+// original phase.
+class PaintingPhase {
+public:
+ PaintingPhase(GraphicsLayer* layer)
+ : m_layer(layer)
+ , m_originalPhase(layer->paintingPhase()) {}
+
+ ~PaintingPhase()
+ {
+ m_layer->setPaintingPhase(m_originalPhase);
+ }
+
+ void set(GraphicsLayerPaintingPhase phase)
+ {
+ m_layer->setPaintingPhase(phase);
+ }
+
+ void clear(GraphicsLayerPaintingPhase phase)
+ {
+ m_layer->setPaintingPhase(
+ (GraphicsLayerPaintingPhase) (m_originalPhase & ~phase));
+ }
+private:
+ GraphicsLayer* m_layer;
+ GraphicsLayerPaintingPhase m_originalPhase;
+};
+
+void GraphicsLayerAndroid::updateScrollingLayers()
+{
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ RenderLayer* layer = renderLayerFromClient(m_client);
+ if (!layer || !m_haveContents)
+ return;
+ bool hasOverflowScroll = m_foregroundLayer || m_contentLayer->contentIsScrollable();
+ bool layerNeedsOverflow = layer->hasOverflowScroll();
+ bool iframeNeedsOverflow = layer->isRootLayer() &&
+ layer->renderer()->frame()->ownerRenderer() &&
+ layer->renderer()->frame()->view()->hasOverflowScroll();
+
+ if (hasOverflowScroll && (layerNeedsOverflow || iframeNeedsOverflow)) {
+ // Already has overflow layers.
+ return;
+ }
+ if (!hasOverflowScroll && !layerNeedsOverflow && !iframeNeedsOverflow) {
+ // Does not need overflow layers.
+ return;
+ }
+ if (layerNeedsOverflow || iframeNeedsOverflow) {
+ ASSERT(!hasOverflowScroll);
+ if (layerNeedsOverflow) {
+ ASSERT(!m_foregroundLayer && !m_foregroundClipLayer);
+ m_foregroundLayer = new ScrollableLayerAndroid(layer);
+ m_foregroundClipLayer = new LayerAndroid(layer);
+ m_foregroundClipLayer->setMasksToBounds(true);
+ m_foregroundClipLayer->addChild(m_foregroundLayer);
+ m_contentLayer->addChild(m_foregroundClipLayer);
+ } else {
+ ASSERT(iframeNeedsOverflow && !m_contentLayer->contentIsScrollable());
+ // No need to copy the children as they will be removed and synced.
+ m_contentLayer->removeChildren();
+ // Replace the content layer with a scrollable layer.
+ LayerAndroid* layer = new ScrollableLayerAndroid(*m_contentLayer);
+ m_contentLayer->unref();
+ m_contentLayer = layer;
+ if (m_parent) {
+ // The content layer has changed so the parent needs to sync
+ // children.
+ static_cast<GraphicsLayerAndroid*>(m_parent)->m_needsSyncChildren = true;
+ }
+ }
+ // Need to rebuild our children based on the new structure.
+ m_needsSyncChildren = true;
+ } else {
+ ASSERT(hasOverflowScroll && !layerNeedsOverflow && !iframeNeedsOverflow);
+ ASSERT(m_contentLayer);
+ // Remove the foreground layers.
+ if (m_foregroundLayer) {
+ m_foregroundLayer->unref();
+ m_foregroundLayer = 0;
+ m_foregroundClipLayer->unref();
+ m_foregroundClipLayer = 0;
+ }
+ // No need to copy over children.
+ m_contentLayer->removeChildren();
+ LayerAndroid* layer = new LayerAndroid(*m_contentLayer);
+ m_contentLayer->unref();
+ m_contentLayer = layer;
+ if (m_parent) {
+ // The content layer has changed so the parent needs to sync
+ // children.
+ static_cast<GraphicsLayerAndroid*>(m_parent)->m_needsSyncChildren = true;
+ }
+ // Children are all re-parented.
+ m_needsSyncChildren = true;
+ }
+#endif
+}
+
+bool GraphicsLayerAndroid::repaint()
+{
+ LOG("(%x) repaint(), gPaused(%d) m_needsRepaint(%d) m_haveContents(%d) ",
+ this, gPaused, m_needsRepaint, m_haveContents);
+
+ if (!gPaused && m_haveContents && m_needsRepaint && !m_haveImage) {
+ // with SkPicture, we request the entire layer's content.
+ IntRect layerBounds(0, 0, m_size.width(), m_size.height());
+
+ RenderLayer* layer = renderLayerFromClient(m_client);
+ if (m_foregroundLayer) {
+ PaintingPhase phase(this);
+ // Paint the background into a separate context.
+ phase.set(GraphicsLayerPaintBackground);
+ if (!paintContext(m_contentLayer->recordContext(), layerBounds))
+ return false;
+
+ // Construct the foreground layer and draw.
+ RenderBox* box = layer->renderBox();
+ int outline = box->view()->maximalOutlineSize();
+ IntRect contentsRect(0, 0,
+ box->borderLeft() + box->borderRight() + layer->scrollWidth(),
+ box->borderTop() + box->borderBottom() + layer->scrollHeight());
+ contentsRect.inflate(outline);
+ // Update the foreground layer size.
+ m_foregroundLayer->setSize(contentsRect.width(), contentsRect.height());
+ // Paint everything else into the main recording canvas.
+ phase.clear(GraphicsLayerPaintBackground);
+
+ // Paint at 0,0.
+ IntSize scroll = layer->scrolledContentOffset();
+ layer->scrollToOffset(0, 0, true, false);
+ // At this point, it doesn't matter if painting failed.
+ (void) paintContext(m_foregroundLayer->recordContext(), contentsRect);
+ layer->scrollToOffset(scroll.width(), scroll.height(), true, false);
+
+ // Construct the clip layer for masking the contents.
+ IntRect clip = layer->renderer()->absoluteBoundingBoxRect();
+ // absoluteBoundingBoxRect does not include the outline so we need
+ // to offset the position.
+ int x = box->borderLeft() + outline;
+ int y = box->borderTop() + outline;
+ int width = clip.width() - box->borderLeft() - box->borderRight();
+ int height = clip.height() - box->borderTop() - box->borderBottom();
+ m_foregroundClipLayer->setPosition(x, y);
+ m_foregroundClipLayer->setSize(width, height);
+
+ // Need to offset the foreground layer by the clip layer in order
+ // for the contents to be in the correct position.
+ m_foregroundLayer->setPosition(-x, -y);
+ // Set the scrollable bounds of the layer.
+ m_foregroundLayer->setScrollLimits(-x, -y, m_size.width(), m_size.height());
+ m_foregroundLayer->needsRepaint();
+ } else {
+ // If there is no contents clip, we can draw everything into one
+ // picture.
+ if (!paintContext(m_contentLayer->recordContext(), layerBounds))
+ return false;
+ // Check for a scrollable iframe and report the scrolling
+ // limits based on the view size.
+ if (m_contentLayer->contentIsScrollable()) {
+ FrameView* view = layer->renderer()->frame()->view();
+ static_cast<ScrollableLayerAndroid*>(m_contentLayer)->setScrollLimits(
+ m_position.x(), m_position.y(), view->layoutWidth(), view->layoutHeight());
+ }
+ }
+
+ LOG("(%x) repaint() on (%.2f,%.2f) contentlayer(%.2f,%.2f,%.2f,%.2f)paintGraphicsLayer called!",
+ this, m_size.width(), m_size.height(),
+ m_contentLayer->getPosition().fX,
+ m_contentLayer->getPosition().fY,
+ m_contentLayer->getSize().width(),
+ m_contentLayer->getSize().height());
+
+ m_contentLayer->needsRepaint();
+ m_needsRepaint = false;
+ m_invalidatedRects.clear();
+
+ return true;
+ }
+ if (m_needsRepaint && m_haveImage && m_newImage) {
+ // We need to tell the GL thread that we will need to repaint the
+ // texture. Only do so if we effectively have a new image!
+ m_contentLayer->needsRepaint();
+ m_newImage = false;
+ m_needsRepaint = false;
+ return true;
+ }
+ return false;
+}
+
+bool GraphicsLayerAndroid::paintContext(SkPicture* context,
+ const IntRect& rect)
+{
+ SkAutoPictureRecord arp(context, rect.width(), rect.height());
+ SkCanvas* canvas = arp.getRecordingCanvas();
+
+ if (!canvas)
+ return false;
+
+ PlatformGraphicsContext platformContext(canvas, 0);
+ GraphicsContext graphicsContext(&platformContext);
+
+ paintGraphicsLayerContents(graphicsContext, rect);
+ return true;
+}
+
+void GraphicsLayerAndroid::setNeedsDisplayInRect(const FloatRect& rect)
+{
+ for (unsigned int i = 0; i < m_children.size(); i++) {
+ GraphicsLayer* layer = m_children[i];
+ if (layer) {
+ FloatRect childrenRect = m_transform.mapRect(rect);
+ layer->setNeedsDisplayInRect(childrenRect);
+ }
+ }
+
+ if (!m_haveImage && !drawsContent()) {
+ LOG("(%x) setNeedsDisplay(%.2f,%.2f,%.2f,%.2f) doesn't have content, bypass...",
+ this, rect.x(), rect.y(), rect.width(), rect.height());
+ return;
+ }
+
+ bool addInval = true;
+ const size_t maxDirtyRects = 8;
+ for (size_t i = 0; i < m_invalidatedRects.size(); ++i) {
+ if (m_invalidatedRects[i].contains(rect)) {
+ addInval = false;
+ break;
+ }
+ }
+
+#ifdef LAYER_DEBUG
+ LOG("(%x) layer %d setNeedsDisplayInRect(%d) - (%.2f, %.2f, %.2f, %.2f)", this,
+ m_contentLayer->uniqueId(), m_needsRepaint, rect.x(), rect.y(), rect.width(), rect.height());
+#endif
+
+ if (addInval) {
+ if (m_invalidatedRects.size() < maxDirtyRects)
+ m_invalidatedRects.append(rect);
+ else
+ m_invalidatedRects[0].unite(rect);
+ }
+
+ m_needsRepaint = true;
+ askForSync();
+}
+
+void GraphicsLayerAndroid::pauseDisplay(bool state)
+{
+ gPaused = state;
+ if (gPaused)
+ gPausedDelay = WTF::currentTime() + 1;
+}
+
+bool GraphicsLayerAndroid::addAnimation(const KeyframeValueList& valueList,
+ const IntSize& boxSize,
+ const Animation* anim,
+ const String& keyframesName,
+ double beginTime)
+{
+ if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2)
+ return false;
+
+ bool createdAnimations = false;
+ if (valueList.property() == AnimatedPropertyWebkitTransform) {
+ createdAnimations = createTransformAnimationsFromKeyframes(valueList,
+ anim,
+ keyframesName,
+ beginTime,
+ boxSize);
+ } else {
+ createdAnimations = createAnimationFromKeyframes(valueList,
+ anim,
+ keyframesName,
+ beginTime);
+ }
+ if (createdAnimations)
+ askForSync();
+ return createdAnimations;
+}
+
+bool GraphicsLayerAndroid::createAnimationFromKeyframes(const KeyframeValueList& valueList,
+ const Animation* animation, const String& keyframesName, double beginTime)
+{
+ bool isKeyframe = valueList.size() > 2;
+ TLOG("createAnimationFromKeyframes(%d), name(%s) beginTime(%.2f)",
+ isKeyframe, keyframesName.latin1().data(), beginTime);
+
+ switch (valueList.property()) {
+ case AnimatedPropertyInvalid: break;
+ case AnimatedPropertyWebkitTransform: break;
+ case AnimatedPropertyBackgroundColor: break;
+ case AnimatedPropertyOpacity: {
+ MLOG("ANIMATEDPROPERTYOPACITY");
+
+ KeyframeValueList* operationsList = new KeyframeValueList(AnimatedPropertyOpacity);
+ for (unsigned int i = 0; i < valueList.size(); i++) {
+ FloatAnimationValue* originalValue = (FloatAnimationValue*)valueList.at(i);
+ PassRefPtr<TimingFunction> timingFunction(const_cast<TimingFunction*>(originalValue->timingFunction()));
+ FloatAnimationValue* value = new FloatAnimationValue(originalValue->keyTime(),
+ originalValue->value(),
+ timingFunction);
+ operationsList->insert(value);
+ }
+
+ RefPtr<AndroidOpacityAnimation> anim = AndroidOpacityAnimation::create(animation,
+ operationsList,
+ beginTime);
+ if (keyframesName.isEmpty())
+ anim->setName(propertyIdToString(valueList.property()));
+ else
+ anim->setName(keyframesName);
+
+ m_contentLayer->addAnimation(anim.release());
+ needsNotifyClient();
+ return true;
+ } break;
+ }
+ return false;
+}
+
+void GraphicsLayerAndroid::needsNotifyClient()
+{
+ m_needsNotifyClient = true;
+ askForSync();
+}
+
+bool GraphicsLayerAndroid::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList,
+ const Animation* animation,
+ const String& keyframesName,
+ double beginTime,
+ const IntSize& boxSize)
+{
+ ASSERT(valueList.property() == AnimatedPropertyWebkitTransform);
+ TLOG("createTransformAnimationFromKeyframes, name(%s) beginTime(%.2f)",
+ keyframesName.latin1().data(), beginTime);
+
+ KeyframeValueList* operationsList = new KeyframeValueList(AnimatedPropertyWebkitTransform);
+ for (unsigned int i = 0; i < valueList.size(); i++) {
+ TransformAnimationValue* originalValue = (TransformAnimationValue*)valueList.at(i);
+ PassRefPtr<TimingFunction> timingFunction(const_cast<TimingFunction*>(originalValue->timingFunction()));
+ TransformAnimationValue* value = new TransformAnimationValue(originalValue->keyTime(),
+ originalValue->value(),
+ timingFunction);
+ operationsList->insert(value);
+ }
+
+ RefPtr<AndroidTransformAnimation> anim = AndroidTransformAnimation::create(animation,
+ operationsList,
+ beginTime);
+
+ if (keyframesName.isEmpty())
+ anim->setName(propertyIdToString(valueList.property()));
+ else
+ anim->setName(keyframesName);
+
+
+ m_contentLayer->addAnimation(anim.release());
+
+ needsNotifyClient();
+ return true;
+}
+
+void GraphicsLayerAndroid::removeAnimationsForProperty(AnimatedPropertyID anID)
+{
+ TLOG("NRO removeAnimationsForProperty(%d)", anID);
+ m_contentLayer->removeAnimationsForProperty(anID);
+ askForSync();
+}
+
+void GraphicsLayerAndroid::removeAnimationsForKeyframes(const String& keyframesName)
+{
+ TLOG("NRO removeAnimationsForKeyframes(%s)", keyframesName.latin1().data());
+ m_contentLayer->removeAnimationsForKeyframes(keyframesName);
+ askForSync();
+}
+
+void GraphicsLayerAndroid::pauseAnimation(const String& keyframesName)
+{
+ TLOG("NRO pauseAnimation(%s)", keyframesName.latin1().data());
+}
+
+void GraphicsLayerAndroid::suspendAnimations(double time)
+{
+ TLOG("NRO suspendAnimations(%.2f)", time);
+}
+
+void GraphicsLayerAndroid::resumeAnimations()
+{
+ TLOG("NRO resumeAnimations()");
+}
+
+void GraphicsLayerAndroid::setContentsToImage(Image* image)
+{
+ TLOG("(%x) setContentsToImage", this, image);
+ if (image) {
+ m_haveContents = true;
+ m_haveImage = true;
+ // Only pass the new image if it's a different one
+ if (image->nativeImageForCurrentFrame() != m_imageRef) {
+ m_newImage = true;
+ m_contentLayer->setContentsImage(image->nativeImageForCurrentFrame());
+ // remember the passed image.
+ m_imageRef = image->nativeImageForCurrentFrame();
+ setNeedsDisplay();
+ askForSync();
+ }
+ }
+}
+
+void GraphicsLayerAndroid::setContentsToMedia(PlatformLayer* mediaLayer)
+{
+ // Only fullscreen video on Android, so media doesn't get it's own layer.
+ // We might still have other layers though.
+ if (m_contentLayer != mediaLayer && mediaLayer) {
+
+ // TODO add a copy method to LayerAndroid to sync everything
+ // copy data from the original content layer to the new one
+ mediaLayer->setPosition(m_contentLayer->getPosition().fX,
+ m_contentLayer->getPosition().fY);
+ mediaLayer->setSize(m_contentLayer->getWidth(), m_contentLayer->getHeight());
+ mediaLayer->setDrawTransform(m_contentLayer->drawTransform());
+
+ mediaLayer->ref();
+ m_contentLayer->unref();
+ m_contentLayer = mediaLayer;
+
+ // If the parent exists then notify it to re-sync it's children
+ if (m_parent) {
+ GraphicsLayerAndroid* parent = static_cast<GraphicsLayerAndroid*>(m_parent);
+ parent->m_needsSyncChildren = true;
+ }
+ m_needsSyncChildren = true;
+
+ setNeedsDisplay();
+ askForSync();
+ }
+}
+
+PlatformLayer* GraphicsLayerAndroid::platformLayer() const
+{
+ LOG("platformLayer");
+ return m_contentLayer;
+}
+
+#ifndef NDEBUG
+void GraphicsLayerAndroid::setDebugBackgroundColor(const Color& color)
+{
+}
+
+void GraphicsLayerAndroid::setDebugBorder(const Color& color, float borderWidth)
+{
+}
+#endif
+
+void GraphicsLayerAndroid::setZPosition(float position)
+{
+ if (position == m_zPosition)
+ return;
+ LOG("(%x) setZPosition: %.2f", this, position);
+ GraphicsLayer::setZPosition(position);
+ askForSync();
+}
+
+void GraphicsLayerAndroid::askForSync()
+{
+ if (!m_client)
+ return;
+
+ if (m_client)
+ m_client->notifySyncRequired(this);
+}
+
+void GraphicsLayerAndroid::syncChildren()
+{
+ if (m_needsSyncChildren) {
+ m_contentLayer->removeChildren();
+ LayerAndroid* layer = m_contentLayer;
+ if (m_foregroundClipLayer) {
+ m_contentLayer->addChild(m_foregroundClipLayer);
+ // Use the scrollable content layer as the parent of the children so
+ // that they move with the content.
+ layer = m_foregroundLayer;
+ layer->removeChildren();
+ }
+ for (unsigned int i = 0; i < m_children.size(); i++)
+ layer->addChild(m_children[i]->platformLayer());
+ m_needsSyncChildren = false;
+ }
+}
+
+void GraphicsLayerAndroid::syncMask()
+{
+ if (m_needsSyncMask) {
+ if (m_maskLayer) {
+ LayerAndroid* mask = m_maskLayer->platformLayer();
+ m_contentLayer->setMaskLayer(mask);
+ } else
+ m_contentLayer->setMaskLayer(0);
+
+ m_contentLayer->setMasksToBounds(m_masksToBounds);
+ m_needsSyncMask = false;
+ }
+}
+
+void GraphicsLayerAndroid::syncCompositingState()
+{
+ for (unsigned int i = 0; i < m_children.size(); i++)
+ m_children[i]->syncCompositingState();
+
+ updateScrollingLayers();
+ updateFixedPosition();
+ syncChildren();
+ syncMask();
+
+ if (!gPaused || WTF::currentTime() >= gPausedDelay)
+ repaint();
+}
+
+void GraphicsLayerAndroid::notifyClientAnimationStarted()
+{
+ for (unsigned int i = 0; i < m_children.size(); i++)
+ static_cast<GraphicsLayerAndroid*>(m_children[i])->notifyClientAnimationStarted();
+
+ if (m_needsNotifyClient) {
+ if (client())
+ client()->notifyAnimationStarted(this, WTF::currentTime());
+ m_needsNotifyClient = false;
+ }
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h
new file mode 100644
index 0000000..10db5a1
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GraphicsLayerAndroid_h
+#define GraphicsLayerAndroid_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FloatRect.h"
+#include "Frame.h"
+#include "GraphicsLayer.h"
+#include "GraphicsLayerClient.h"
+#include "LayerAndroid.h"
+#include "RefPtr.h"
+#include "SkBitmapRef.h"
+#include "Vector.h"
+
+class FloatPoint3D;
+class Image;
+class SkBitmapRef;
+
+namespace WebCore {
+
+class ScrollableLayerAndroid;
+
+class GraphicsLayerAndroid : public GraphicsLayer {
+public:
+
+ GraphicsLayerAndroid(GraphicsLayerClient*);
+ virtual ~GraphicsLayerAndroid();
+
+ virtual void setName(const String&);
+
+ // for hosting this GraphicsLayer in a native layer hierarchy
+ virtual NativeLayer nativeLayer() const;
+
+ virtual bool setChildren(const Vector<GraphicsLayer*>&);
+ virtual void addChild(GraphicsLayer*);
+ virtual void addChildAtIndex(GraphicsLayer*, int index);
+ virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling);
+ virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling);
+ virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild);
+
+ virtual void removeFromParent();
+
+ virtual void setPosition(const FloatPoint&);
+ virtual void setPreserves3D(bool b);
+ virtual void setAnchorPoint(const FloatPoint3D&);
+ virtual void setSize(const FloatSize&);
+
+ virtual void setBackfaceVisibility(bool b);
+ virtual void setTransform(const TransformationMatrix&);
+
+ virtual void setChildrenTransform(const TransformationMatrix&);
+
+ virtual void setMaskLayer(GraphicsLayer*);
+ virtual void setMasksToBounds(bool);
+ virtual void setDrawsContent(bool);
+
+ virtual void setBackgroundColor(const Color&);
+ virtual void clearBackgroundColor();
+
+ virtual void setContentsOpaque(bool);
+
+ virtual void setOpacity(float);
+
+ virtual void setNeedsDisplay();
+ virtual void setNeedsDisplayInRect(const FloatRect&);
+
+ virtual bool addAnimation(const KeyframeValueList& valueList,
+ const IntSize& boxSize,
+ const Animation* anim,
+ const String& keyframesName,
+ double beginTime);
+ bool createTransformAnimationsFromKeyframes(const KeyframeValueList&,
+ const Animation*,
+ const String& keyframesName,
+ double beginTime,
+ const IntSize& boxSize);
+ bool createAnimationFromKeyframes(const KeyframeValueList&,
+ const Animation*,
+ const String& keyframesName,
+ double beginTime);
+
+ virtual void removeAnimationsForProperty(AnimatedPropertyID);
+ virtual void removeAnimationsForKeyframes(const String& keyframesName);
+ virtual void pauseAnimation(const String& keyframesName);
+
+ virtual void suspendAnimations(double time);
+ virtual void resumeAnimations();
+
+ virtual void setContentsToImage(Image*);
+ virtual void setContentsToMedia(PlatformLayer*);
+ virtual PlatformLayer* platformLayer() const;
+
+ void pauseDisplay(bool state);
+
+#ifndef NDEBUG
+ virtual void setDebugBackgroundColor(const Color&);
+ virtual void setDebugBorder(const Color&, float borderWidth);
+#endif
+
+ virtual void setZPosition(float);
+
+ virtual void syncCompositingState();
+ void notifyClientAnimationStarted();
+
+ LayerAndroid* contentLayer() { return m_contentLayer; }
+
+ static int instancesCount();
+
+private:
+
+ void askForSync();
+ void syncPositionState();
+ void syncChildren();
+ void syncMask();
+
+ void updateFixedPosition();
+ void updateScrollingLayers();
+
+ // with SkPicture, we always repaint the entire layer's content.
+ bool repaint();
+ void needsNotifyClient();
+
+ bool paintContext(SkPicture* context, const IntRect& rect);
+
+ bool m_needsSyncChildren;
+ bool m_needsSyncMask;
+ bool m_needsRepaint;
+ bool m_needsNotifyClient;
+
+ bool m_haveContents;
+ bool m_haveImage;
+ bool m_newImage;
+ SkBitmapRef* m_imageRef; // only used to remember previously passed images
+
+ Vector<FloatRect> m_invalidatedRects;
+
+ LayerAndroid* m_contentLayer;
+ ScrollableLayerAndroid* m_foregroundLayer;
+ LayerAndroid* m_foregroundClipLayer;
+};
+
+} // namespace WebCore
+
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif // GraphicsLayerAndroid_h
diff --git a/Source/WebCore/platform/graphics/android/HarfbuzzSkia.cpp b/Source/WebCore/platform/graphics/android/HarfbuzzSkia.cpp
new file mode 100644
index 0000000..81841f2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/HarfbuzzSkia.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ * Copyright 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "FontPlatformData.h"
+#include "wtf/OwnArrayPtr.h"
+
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+
+extern "C" {
+#include "harfbuzz-shaper.h"
+}
+
+// This file implements the callbacks which Harfbuzz requires by using Skia
+// calls. See the Harfbuzz source for references about what these callbacks do.
+
+namespace WebCore {
+
+static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value)
+{
+ // HB_Fixed is a 26.6 fixed point format.
+ return value * 64;
+}
+
+static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
+{
+ FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+ SkPaint paint;
+
+ font->setupPaint(&paint);
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ 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
+ // |glyphs| array needs to be converted.
+ for (int i = numGlyphs - 1; i >= 0; --i) {
+ uint16_t value;
+ // We use a memcpy to avoid breaking strict aliasing rules.
+ memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(value));
+ glyphs[i] = value;
+ }
+
+ *glyphsSize = numGlyphs;
+ return 1;
+}
+
+static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs, HB_Fixed* advances, int flags)
+{
+ FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+ SkPaint paint;
+
+ font->setupPaint(&paint);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ OwnArrayPtr<uint16_t> glyphs16(new uint16_t[numGlyphs]);
+ if (!glyphs16.get())
+ return;
+ for (unsigned i = 0; i < numGlyphs; ++i)
+ glyphs16[i] = glyphs[i];
+ paint.getTextWidths(glyphs16.get(), numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances));
+
+ // The |advances| values which Skia outputs are SkScalars, which are floats
+ // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
+ // These two formats are both 32-bits long.
+ for (unsigned i = 0; i < numGlyphs; ++i) {
+ float value;
+ // We use a memcpy to avoid breaking strict aliasing rules.
+ memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(value));
+ advances[i] = SkiaScalarToHarfbuzzFixed(value);
+ }
+}
+
+static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
+{
+ FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+ SkPaint paint;
+
+ font->setupPaint(&paint);
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+
+ OwnArrayPtr<uint16_t> glyphs16(new uint16_t[length]);
+ glyphs16.get();
+ int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16.get());
+
+ for (int i = 0; i < numGlyphs; ++i) {
+ if (!glyphs16[i])
+ return false;
+ }
+
+ return true;
+}
+
+static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
+{
+ FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+ SkPaint paint;
+
+ if (flags & HB_ShaperFlag_UseDesignMetrics)
+ return HB_Err_Invalid_Argument; // This is requesting pre-hinted positions. We can't support this.
+
+ font->setupPaint(&paint);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ uint16_t glyph16 = glyph;
+ SkPath path;
+ paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
+ uint32_t numPoints = path.getPoints(0, 0);
+ if (point >= numPoints)
+ return HB_Err_Invalid_SubTable;
+ SkPoint* points = reinterpret_cast<SkPoint*>(fastMalloc(sizeof(SkPoint) * (point + 1)));
+ if (!points)
+ return HB_Err_Invalid_SubTable;
+ // Skia does let us get a single point from the path.
+ path.getPoints(points, point + 1);
+ *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX);
+ *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY);
+ *resultingNumPoints = numPoints;
+ fastFree(points);
+
+ return HB_Err_Ok;
+}
+
+static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
+{
+ FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+ SkPaint paint;
+
+ font->setupPaint(&paint);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ uint16_t glyph16 = glyph;
+ SkScalar width;
+ SkRect bounds;
+ paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
+
+ metrics->x = SkiaScalarToHarfbuzzFixed(bounds.fLeft);
+ metrics->y = SkiaScalarToHarfbuzzFixed(bounds.fTop);
+ metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width());
+ metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height());
+
+ metrics->xOffset = SkiaScalarToHarfbuzzFixed(width);
+ // We can't actually get the |y| correct because Skia doesn't export
+ // the vertical advance. However, nor we do ever render vertical text at
+ // the moment so it's unimportant.
+ metrics->yOffset = 0;
+}
+
+static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
+{
+ FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+ SkPaint paint;
+
+ font->setupPaint(&paint);
+ SkPaint::FontMetrics skiaMetrics;
+ paint.getFontMetrics(&skiaMetrics);
+
+ switch (metric) {
+ case HB_FontAscent:
+ return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent);
+ // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them.
+ default:
+ return 0;
+ }
+}
+
+HB_FontClass harfbuzzSkiaClass = {
+ stringToGlyphs,
+ glyphsToAdvances,
+ canRender,
+ getOutlinePoint,
+ getGlyphMetrics,
+ getFontMetric,
+};
+
+HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
+{
+ FontPlatformData* font = reinterpret_cast<FontPlatformData*>(voidface);
+
+ const size_t tableSize = SkFontHost::GetTableSize(font->uniqueID(), tag);
+ if (!tableSize)
+ return HB_Err_Invalid_Argument;
+ // If Harfbuzz specified a NULL buffer then it's asking for the size of the table.
+ if (!buffer) {
+ *len = tableSize;
+ return HB_Err_Ok;
+ }
+
+ if (*len < tableSize)
+ return HB_Err_Invalid_Argument;
+ SkFontHost::GetTableData(font->uniqueID(), tag, 0, tableSize, buffer);
+ return HB_Err_Ok;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/android/HarfbuzzSkia.h b/Source/WebCore/platform/graphics/android/HarfbuzzSkia.h
new file mode 100644
index 0000000..d26bbe2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/HarfbuzzSkia.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ * Copyright 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 HarfbuzzSkia_h
+#define HarfbuzzSkia_h
+
+extern "C" {
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-unicode.h"
+}
+
+namespace WebCore {
+ HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
+ extern const HB_FontClass harfbuzzSkiaClass;
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/graphics/android/ImageAndroid.cpp b/Source/WebCore/platform/graphics/android/ImageAndroid.cpp
new file mode 100644
index 0000000..01fe272
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ImageAndroid.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TransformationMatrix.h"
+#include "BitmapImage.h"
+#include "Image.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "PlatformGraphicsContext.h"
+#include "PlatformString.h"
+#include "SharedBuffer.h"
+
+#include "android_graphics.h"
+#include "SkBitmapRef.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkImageDecoder.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+#include "SkiaUtils.h"
+
+#include <utils/AssetManager.h>
+
+//#define TRACE_SUBSAMPLED_BITMAPS
+//#define TRACE_SKIPPED_BITMAPS
+
+android::AssetManager* globalAssetManager() {
+ static android::AssetManager* gGlobalAssetMgr;
+ if (!gGlobalAssetMgr) {
+ gGlobalAssetMgr = new android::AssetManager();
+ gGlobalAssetMgr->addDefaultAssets();
+ }
+ return gGlobalAssetMgr;
+}
+
+namespace WebCore {
+
+bool FrameData::clear(bool clearMetadata)
+{
+ if (clearMetadata)
+ m_haveMetadata = false;
+
+ if (m_frame) {
+ m_frame->unref();
+ m_frame = 0;
+ return true;
+ }
+ return false;
+}
+
+BitmapImage::BitmapImage(SkBitmapRef* ref, ImageObserver* observer)
+ : Image(observer)
+ , m_currentFrame(0)
+ , m_frames(0)
+ , m_frameTimer(0)
+ , m_repetitionCount(0)
+ , m_repetitionCountStatus(Unknown)
+ , m_repetitionsComplete(0)
+ , m_isSolidColor(false)
+ , m_animationFinished(true)
+ , m_allDataReceived(true)
+ , m_haveSize(true)
+ , m_sizeAvailable(true)
+ , m_decodedSize(0)
+ , m_haveFrameCount(true)
+ , m_frameCount(1)
+{
+ initPlatformData();
+
+ m_size = IntSize(ref->bitmap().width(), ref->bitmap().height());
+
+ m_frames.grow(1);
+ m_frames[0].m_frame = ref;
+ m_frames[0].m_hasAlpha = !ref->bitmap().isOpaque();
+ checkForSolidColor();
+ ref->ref();
+}
+
+
+void BitmapImage::initPlatformData()
+{
+ m_source.clearURL();
+}
+
+void BitmapImage::invalidatePlatformData()
+{
+}
+
+void BitmapImage::checkForSolidColor()
+{
+ m_checkedForSolidColor = true;
+ m_isSolidColor = false;
+ if (frameCount() == 1) {
+ SkBitmapRef* ref = frameAtIndex(0);
+ if (!ref) {
+ return; // keep solid == false
+ }
+
+ const SkBitmap& bm = ref->bitmap();
+ if (bm.width() != 1 || bm.height() != 1) {
+ return; // keep solid == false
+ }
+
+ SkAutoLockPixels alp(bm);
+ if (!bm.readyToDraw()) {
+ return; // keep solid == false
+ }
+
+ SkPMColor color;
+ switch (bm.getConfig()) {
+ case SkBitmap::kARGB_8888_Config:
+ color = *bm.getAddr32(0, 0);
+ break;
+ case SkBitmap::kRGB_565_Config:
+ color = SkPixel16ToPixel32(*bm.getAddr16(0, 0));
+ break;
+ case SkBitmap::kIndex8_Config: {
+ SkColorTable* ctable = bm.getColorTable();
+ if (!ctable) {
+ return;
+ }
+ color = (*ctable)[*bm.getAddr8(0, 0)];
+ break;
+ }
+ default:
+ return; // keep solid == false
+ }
+ m_isSolidColor = true;
+ m_solidColor = SkPMColorToWebCoreColor(color);
+ }
+}
+
+static void round(SkIRect* dst, const WebCore::FloatRect& src)
+{
+ dst->set(SkScalarRound(SkFloatToScalar(src.x())),
+ SkScalarRound(SkFloatToScalar(src.y())),
+ SkScalarRound(SkFloatToScalar((src.x() + src.width()))),
+ SkScalarRound(SkFloatToScalar((src.y() + src.height()))));
+}
+
+static void round_scaled(SkIRect* dst, const WebCore::FloatRect& src,
+ float sx, float sy)
+{
+ dst->set(SkScalarRound(SkFloatToScalar(src.x() * sx)),
+ SkScalarRound(SkFloatToScalar(src.y() * sy)),
+ SkScalarRound(SkFloatToScalar((src.x() + src.width()) * sx)),
+ SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy)));
+}
+
+static inline void fixPaintForBitmapsThatMaySeam(SkPaint* paint) {
+ /* Bitmaps may be drawn to seem next to other images. If we are drawn
+ zoomed, or at fractional coordinates, we may see cracks/edges if
+ we antialias, because that will cause us to draw the same pixels
+ more than once (e.g. from the left and right bitmaps that share
+ an edge).
+
+ Disabling antialiasing fixes this, and since so far we are never
+ rotated at non-multiple-of-90 angles, this seems to do no harm
+ */
+ paint->setAntiAlias(false);
+}
+
+void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
+ const FloatRect& srcRect, ColorSpace,
+ CompositeOperator compositeOp)
+{
+ startAnimation();
+
+ SkBitmapRef* image = this->nativeImageForCurrentFrame();
+ if (!image) { // If it's too early we won't have an image yet.
+ return;
+ }
+
+ // in case we get called with an incomplete bitmap
+ const SkBitmap& bitmap = image->bitmap();
+ if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) {
+#ifdef TRACE_SKIPPED_BITMAPS
+ SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n",
+ bitmap.width(), bitmap.height(),
+ bitmap.getPixels(), bitmap.pixelRef());
+#endif
+ return;
+ }
+
+ SkIRect srcR;
+ SkRect dstR(dstRect);
+ float invScaleX = (float)bitmap.width() / image->origWidth();
+ float invScaleY = (float)bitmap.height() / image->origHeight();
+
+ round_scaled(&srcR, srcRect, invScaleX, invScaleY);
+ if (srcR.isEmpty() || dstR.isEmpty()) {
+#ifdef TRACE_SKIPPED_BITMAPS
+ SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n",
+ bitmap.width(), bitmap.height(),
+ srcR.isEmpty(), dstR.isEmpty());
+#endif
+ return;
+ }
+
+ SkCanvas* canvas = ctxt->platformContext()->mCanvas;
+ SkPaint paint;
+
+ ctxt->setupBitmapPaint(&paint); // need global alpha among other things
+ paint.setFilterBitmap(true);
+ paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
+ fixPaintForBitmapsThatMaySeam(&paint);
+ canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint);
+
+#ifdef TRACE_SUBSAMPLED_BITMAPS
+ if (bitmap.width() != image->origWidth() ||
+ bitmap.height() != image->origHeight()) {
+ SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n",
+ bitmap.width(), bitmap.height(),
+ image->origWidth(), image->origHeight());
+ }
+#endif
+}
+
+void BitmapImage::setURL(const String& str)
+{
+ m_source.setURL(str);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect,
+ const AffineTransform& patternTransform,
+ const FloatPoint& phase, ColorSpace,
+ CompositeOperator compositeOp, const FloatRect& destRect)
+{
+ SkBitmapRef* image = this->nativeImageForCurrentFrame();
+ if (!image) { // If it's too early we won't have an image yet.
+ return;
+ }
+
+ // in case we get called with an incomplete bitmap
+ const SkBitmap& origBitmap = image->bitmap();
+ if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL) {
+ return;
+ }
+
+ SkRect dstR(destRect);
+ if (dstR.isEmpty()) {
+ return;
+ }
+
+ SkIRect srcR;
+ // we may have to scale if the image has been subsampled (so save RAM)
+ bool imageIsSubSampled = image->origWidth() != origBitmap.width() ||
+ image->origHeight() != origBitmap.height();
+ float scaleX = 1;
+ float scaleY = 1;
+ if (imageIsSubSampled) {
+ scaleX = (float)image->origWidth() / origBitmap.width();
+ scaleY = (float)image->origHeight() / origBitmap.height();
+// SkDebugf("----- subsampled %g %g\n", scaleX, scaleY);
+ round_scaled(&srcR, srcRect, 1 / scaleX, 1 / scaleY);
+ } else {
+ round(&srcR, srcRect);
+ }
+
+ // now extract the proper subset of the src image
+ SkBitmap bitmap;
+ if (!origBitmap.extractSubset(&bitmap, srcR)) {
+ SkDebugf("--- Image::drawPattern calling extractSubset failed\n");
+ return;
+ }
+
+ SkCanvas* canvas = ctxt->platformContext()->mCanvas;
+ SkPaint paint;
+ ctxt->setupBitmapPaint(&paint); // need global alpha among other things
+
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ paint.setShader(shader)->unref();
+ // now paint is the only owner of shader
+ paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
+ paint.setFilterBitmap(true);
+ fixPaintForBitmapsThatMaySeam(&paint);
+
+ SkMatrix matrix(patternTransform);
+
+ if (imageIsSubSampled) {
+ matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY));
+ }
+ // We also need to translate it such that the origin of the pattern is the
+ // origin of the destination rect, which is what WebKit expects. Skia uses
+ // the coordinate system origin as the base for the patter. If WebKit wants
+ // a shifted image, it will shift it from there using the patternTransform.
+ float tx = phase.x() + srcRect.x() * patternTransform.a();
+ float ty = phase.y() + srcRect.y() * patternTransform.d();
+ matrix.postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
+ shader->setLocalMatrix(matrix);
+#if 0
+ SkDebugf("--- drawPattern: src [%g %g %g %g] dst [%g %g %g %g] transform [%g %g %g %g %g %g] matrix [%g %g %g %g %g %g]\n",
+ srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(),
+ destRect.x(), destRect.y(), destRect.width(), destRect.height(),
+ patternTransform.a(), patternTransform.b(), patternTransform.c(),
+ patternTransform.d(), patternTransform.e(), patternTransform.f(),
+ matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+#endif
+ canvas->drawRect(dstR, paint);
+
+#ifdef TRACE_SUBSAMPLED_BITMAPS
+ if (bitmap.width() != image->origWidth() ||
+ bitmap.height() != image->origHeight()) {
+ SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n",
+ bitmap.width(), bitmap.height(),
+ image->origWidth(), image->origHeight(),
+ SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height()));
+ }
+#endif
+}
+
+// missingImage, textAreaResizeCorner
+PassRefPtr<Image> Image::loadPlatformResource(const char *name)
+{
+ android::AssetManager* am = globalAssetManager();
+
+ SkString path("webkit/");
+ path.append(name);
+ path.append(".png");
+
+ android::Asset* a = am->open(path.c_str(),
+ android::Asset::ACCESS_BUFFER);
+ if (a == NULL) {
+ SkDebugf("---------------- failed to open image asset %s\n", name);
+ return NULL;
+ }
+
+ SkAutoTDelete<android::Asset> ad(a);
+
+ SkBitmap bm;
+ if (SkImageDecoder::DecodeMemory(a->getBuffer(false), a->getLength(), &bm)) {
+ SkBitmapRef* ref = new SkBitmapRef(bm);
+ // create will call ref(), so we need aur() to release ours upon return
+ SkAutoUnref aur(ref);
+ return BitmapImage::create(ref, 0);
+ }
+ return Image::nullImage();
+}
+
+} // namespace
diff --git a/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp b/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp
new file mode 100644
index 0000000..c7adb25
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ImageBuffer.h"
+
+#include "Base64.h"
+#include "BitmapImage.h"
+#include "ColorSpace.h"
+#include "ImageData.h"
+#include "GraphicsContext.h"
+#include "NotImplemented.h"
+#include "PlatformGraphicsContext.h"
+#include "SkBitmapRef.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkImageEncoder.h"
+#include "SkStream.h"
+#include "SkUnPreMultiply.h"
+#include "android_graphics.h"
+
+using namespace std;
+
+namespace WebCore {
+
+ImageBufferData::ImageBufferData(const IntSize&)
+{
+}
+
+ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success)
+ : m_data(size)
+ , m_size(size)
+{
+ m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height()));
+ success = true;
+}
+
+ImageBuffer::~ImageBuffer()
+{
+}
+
+GraphicsContext* ImageBuffer::context() const
+{
+ return m_context.get();
+}
+
+bool ImageBuffer::drawsUsingCopy() const
+{
+ return true;
+}
+
+PassRefPtr<Image> ImageBuffer::copyImage() const
+{
+ ASSERT(context());
+ SkCanvas* canvas = context()->platformContext()->mCanvas;
+ SkDevice* device = canvas->getDevice();
+ const SkBitmap& orig = device->accessBitmap(false);
+
+ SkBitmap copy;
+ orig.copyTo(&copy, orig.config());
+
+ SkBitmapRef* ref = new SkBitmapRef(copy);
+ RefPtr<Image> image = BitmapImage::create(ref, 0);
+ ref->unref();
+ return image;
+}
+
+void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const
+{
+ SkDebugf("xxxxxxxxxxxxxxxxxx clip not implemented\n");
+}
+
+void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, bool useLowQualityScale)
+{
+ RefPtr<Image> imageCopy = copyImage();
+ context->drawImage(imageCopy.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale);
+}
+
+void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
+{
+ RefPtr<Image> imageCopy = copyImage();
+ imageCopy->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
+}
+
+PassRefPtr<ImageData> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
+{
+ GraphicsContext* gc = this->context();
+ if (!gc) {
+ return 0;
+ }
+
+ const SkBitmap& src = android_gc2canvas(gc)->getDevice()->accessBitmap(false);
+ SkAutoLockPixels alp(src);
+ if (!src.getPixels()) {
+ return 0;
+ }
+
+ // ! Can't use PassRefPtr<>, otherwise the second access will cause crash.
+ RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
+ unsigned char* data = result->data()->data()->data();
+
+ if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height())
+ memset(data, 0, result->data()->length());
+
+ int originx = rect.x();
+ int destx = 0;
+ if (originx < 0) {
+ destx = -originx;
+ originx = 0;
+ }
+ int endx = rect.x() + rect.width();
+ if (endx > m_size.width())
+ endx = m_size.width();
+ int numColumns = endx - originx;
+
+ int originy = rect.y();
+ int desty = 0;
+ if (originy < 0) {
+ desty = -originy;
+ originy = 0;
+ }
+ int endy = rect.y() + rect.height();
+ if (endy > m_size.height())
+ endy = m_size.height();
+ int numRows = endy - originy;
+
+ unsigned srcPixelsPerRow = src.rowBytesAsPixels();
+ unsigned destBytesPerRow = 4 * rect.width();
+
+ const SkPMColor* srcRows = src.getAddr32(originx, originy);
+ unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
+ for (int y = 0; y < numRows; ++y) {
+ for (int x = 0; x < numColumns; x++) {
+ // ugh, it appears they want unpremultiplied pixels
+ SkColor c = SkUnPreMultiply::PMColorToColor(srcRows[x]);
+ int basex = x * 4;
+ destRows[basex + 0] = SkColorGetR(c);
+ destRows[basex + 1] = SkColorGetG(c);
+ destRows[basex + 2] = SkColorGetB(c);
+ destRows[basex + 3] = SkColorGetA(c);
+ }
+ srcRows += srcPixelsPerRow;
+ destRows += destBytesPerRow;
+ }
+ return result;
+}
+
+void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
+{
+ GraphicsContext* gc = this->context();
+ if (!gc) {
+ return;
+ }
+
+ const SkBitmap& dst = android_gc2canvas(gc)->getDevice()->accessBitmap(true);
+ SkAutoLockPixels alp(dst);
+ if (!dst.getPixels()) {
+ return;
+ }
+
+ ASSERT(sourceRect.width() > 0);
+ ASSERT(sourceRect.height() > 0);
+
+ int originx = sourceRect.x();
+ int destx = destPoint.x() + sourceRect.x();
+ ASSERT(destx >= 0);
+ ASSERT(destx < m_size.width());
+ ASSERT(originx >= 0);
+ ASSERT(originx <= sourceRect.right());
+
+ int endx = destPoint.x() + sourceRect.right();
+ ASSERT(endx <= m_size.width());
+
+ int numColumns = endx - destx;
+
+ int originy = sourceRect.y();
+ int desty = destPoint.y() + sourceRect.y();
+ ASSERT(desty >= 0);
+ ASSERT(desty < m_size.height());
+ ASSERT(originy >= 0);
+ ASSERT(originy <= sourceRect.bottom());
+
+ int endy = destPoint.y() + sourceRect.bottom();
+ ASSERT(endy <= m_size.height());
+ int numRows = endy - desty;
+
+ unsigned srcBytesPerRow = 4 * source->width();
+ unsigned dstPixelsPerRow = dst.rowBytesAsPixels();
+
+ unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4;
+ SkPMColor* dstRows = dst.getAddr32(destx, desty);
+ for (int y = 0; y < numRows; ++y) {
+ for (int x = 0; x < numColumns; x++) {
+ int basex = x * 4;
+ dstRows[x] = SkPackARGB32(srcRows[basex + 3],
+ srcRows[basex + 0],
+ srcRows[basex + 1],
+ srcRows[basex + 2]);
+ }
+ dstRows += dstPixelsPerRow;
+ srcRows += srcBytesPerRow;
+ }
+}
+
+
+String ImageBuffer::toDataURL(const String&, const double*) const
+{
+ // Encode the image into a vector.
+ SkDynamicMemoryWStream pngStream;
+ const SkBitmap& dst = android_gc2canvas(context())->getDevice()->accessBitmap(true);
+ SkImageEncoder::EncodeStream(&pngStream, dst, SkImageEncoder::kPNG_Type, 100);
+
+ // Convert it into base64.
+ Vector<char> pngEncodedData;
+ pngEncodedData.append(pngStream.getStream(), pngStream.getOffset());
+ Vector<char> base64EncodedData;
+ base64Encode(pngEncodedData, base64EncodedData);
+ // Append with a \0 so that it's a valid string.
+ base64EncodedData.append('\0');
+
+ // And the resulting string.
+ return String::format("data:image/png;base64,%s", base64EncodedData.data());
+}
+
+void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookupTable)
+{
+ notImplemented();
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/ImageBufferData.h b/Source/WebCore/platform/graphics/android/ImageBufferData.h
new file mode 100644
index 0000000..269b365
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ImageBufferData.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ImageBufferData_h
+#define ImageBufferData_h
+
+namespace WebCore {
+
+class IntSize;
+
+class ImageBufferData {
+public:
+ ImageBufferData(const IntSize&);
+};
+
+} // namespace WebCore
+
+#endif // ImageBufferData_h
diff --git a/Source/WebCore/platform/graphics/android/ImageSourceAndroid.cpp b/Source/WebCore/platform/graphics/android/ImageSourceAndroid.cpp
new file mode 100644
index 0000000..982302c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ImageSourceAndroid.cpp
@@ -0,0 +1,505 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "BitmapAllocatorAndroid.h"
+#include "ImageSource.h"
+#include "IntSize.h"
+#include "NotImplemented.h"
+#include "SharedBuffer.h"
+#include "PlatformString.h"
+
+#include "SkBitmapRef.h"
+#include "SkImageDecoder.h"
+#include "SkImageRef.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+#ifdef ANDROID_ANIMATED_GIF
+ #include "EmojiFont.h"
+ #include "GIFImageDecoder.h"
+
+ using namespace android;
+#endif
+
+// TODO: We should make use of some of the common code in platform/graphics/ImageSource.cpp.
+
+SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src);
+
+//#define TRACE_SUBSAMPLE_BITMAPS
+//#define TRACE_RLE_BITMAPS
+
+
+// flag to tell us when we're on a large-ram device (e.g. >= 256M)
+#ifdef ANDROID_LARGE_MEMORY_DEVICE
+ // don't use RLE for images smaller than this, since they incur a drawing cost
+ // (and don't work as patterns yet) we only want to use RLE when we must
+ #define MIN_RLE_ALLOC_SIZE (8*1024*1024)
+
+ // see dox for computeMaxBitmapSizeForCache()
+ #define MAX_SIZE_BEFORE_SUBSAMPLE (32*1024*1024)
+
+ // preserve quality for 24/32bit src
+ static const SkBitmap::Config gPrefConfigTable[6] = {
+ SkBitmap::kIndex8_Config, // src: index, opaque
+ SkBitmap::kIndex8_Config, // src: index, alpha
+ SkBitmap::kRGB_565_Config, // src: 16bit, opaque
+ SkBitmap::kARGB_8888_Config, // src: 16bit, alpha (promote to 32bit)
+ SkBitmap::kARGB_8888_Config, // src: 32bit, opaque
+ SkBitmap::kARGB_8888_Config, // src: 32bit, alpha
+ };
+#else
+ #define MIN_RLE_ALLOC_SIZE (2*1024*1024)
+ #define MAX_SIZE_BEFORE_SUBSAMPLE (2*1024*1024)
+
+ // tries to minimize memory usage (i.e. demote opaque 32bit -> 16bit)
+ static const SkBitmap::Config gPrefConfigTable[6] = {
+ SkBitmap::kIndex8_Config, // src: index, opaque
+ SkBitmap::kIndex8_Config, // src: index, alpha
+ SkBitmap::kRGB_565_Config, // src: 16bit, opaque
+ SkBitmap::kARGB_8888_Config, // src: 16bit, alpha (promote to 32bit)
+ SkBitmap::kRGB_565_Config, // src: 32bit, opaque (demote to 16bit)
+ SkBitmap::kARGB_8888_Config, // src: 32bit, alpha
+ };
+#endif
+
+/* Images larger than this should be subsampled. Using ashmem, the decoded
+ pixels will be purged as needed, so this value can be pretty large. Making
+ it too small hurts image quality (e.g. abc.com background). 2Meg works for
+ the sites I've tested, but if we hit important sites that need more, we
+ should try increasing it and see if it has negative impact on performance
+ (i.e. we end up thrashing because we need to keep decoding images that have
+ been purged.
+
+ Perhaps this value should be some fraction of the available RAM...
+*/
+size_t computeMaxBitmapSizeForCache() {
+ return MAX_SIZE_BEFORE_SUBSAMPLE;
+}
+
+/* 8bit images larger than this should be recompressed in RLE, to reduce
+ on the imageref cache.
+
+ Downside: performance, since we have to decode/encode
+ and then rle-decode when we draw.
+*/
+static bool shouldReencodeAsRLE(const SkBitmap& bm) {
+ const SkBitmap::Config c = bm.config();
+ return (SkBitmap::kIndex8_Config == c || SkBitmap::kA8_Config == c)
+ &&
+ bm.width() >= 64 // narrower than this won't compress well in RLE
+ &&
+ bm.getSize() > MIN_RLE_ALLOC_SIZE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class PrivateAndroidImageSourceRec : public SkBitmapRef {
+public:
+ PrivateAndroidImageSourceRec(const SkBitmap& bm, int origWidth,
+ int origHeight, int sampleSize)
+ : SkBitmapRef(bm), fSampleSize(sampleSize), fAllDataReceived(false) {
+ this->setOrigSize(origWidth, origHeight);
+ }
+
+ int fSampleSize;
+ bool fAllDataReceived;
+};
+
+namespace WebCore {
+
+ImageSource::ImageSource(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
+ : m_alphaOption(alphaOption)
+ , m_gammaAndColorProfileOption(gammaAndColorProfileOption)
+{
+ m_decoder.m_image = NULL;
+#ifdef ANDROID_ANIMATED_GIF
+ m_decoder.m_gifDecoder = 0;
+#endif
+}
+
+ImageSource::~ImageSource() {
+ delete m_decoder.m_image;
+#ifdef ANDROID_ANIMATED_GIF
+ delete m_decoder.m_gifDecoder;
+#endif
+}
+
+bool ImageSource::initialized() const {
+ return
+#ifdef ANDROID_ANIMATED_GIF
+ m_decoder.m_gifDecoder ||
+#endif
+ m_decoder.m_image != NULL;
+}
+
+static int computeSampleSize(const SkBitmap& bitmap) {
+ const size_t maxSize = computeMaxBitmapSizeForCache();
+ size_t size = bitmap.getSize();
+ int sampleSize = 1;
+
+ while (size > maxSize) {
+ sampleSize <<= 1;
+ size >>= 2;
+ }
+
+#ifdef TRACE_SUBSAMPLE_BITMAPS
+ if (sampleSize > 1) {
+ SkDebugf("------- bitmap [%d %d] config=%d origSize=%d predictSize=%d sampleSize=%d\n",
+ bitmap.width(), bitmap.height(), bitmap.config(),
+ bitmap.getSize(), size, sampleSize);
+ }
+#endif
+ return sampleSize;
+}
+
+static SkPixelRef* convertToRLE(SkBitmap* bm, const void* data, size_t len) {
+ if (!shouldReencodeAsRLE(*bm)) {
+ return NULL;
+ }
+
+ SkBitmap tmp;
+
+ if (!SkImageDecoder::DecodeMemory(data, len, &tmp) || !tmp.getPixels()) {
+ return NULL;
+ }
+
+ SkPixelRef* ref = SkCreateRLEPixelRef(tmp);
+ if (NULL == ref) {
+ return NULL;
+ }
+
+#ifdef TRACE_RLE_BITMAPS
+ SkDebugf("---- reencode bitmap as RLE: [%d %d] encodeSize=%d\n",
+ tmp.width(), tmp.height(), len);
+#endif
+
+ bm->setConfig(SkBitmap::kRLE_Index8_Config, tmp.width(), tmp.height());
+ return ref;
+}
+
+void ImageSource::clearURL()
+{
+ m_decoder.m_url.reset();
+}
+
+void ImageSource::setURL(const String& url)
+{
+ if (url.startsWith("data:")) {
+ clearURL();
+ } else {
+ m_decoder.m_url.setUTF16(url.characters(), url.length());
+ }
+}
+
+#ifdef ANDROID_ANIMATED_GIF
+// we only animate small GIFs for now, to save memory
+// also, we only support this in Japan, hence the Emoji check
+static bool should_use_animated_gif(int width, int height) {
+#ifdef ANDROID_LARGE_MEMORY_DEVICE
+ return true;
+#else
+ return EmojiFont::IsAvailable() &&
+ width <= 32 && height <= 32;
+#endif
+}
+#endif
+
+void ImageSource::setData(SharedBuffer* data, bool allDataReceived)
+{
+#ifdef ANDROID_ANIMATED_GIF
+ // This is only necessary if we allow ourselves to partially decode GIF
+ bool disabledAnimatedGif = false;
+ if (m_decoder.m_gifDecoder
+ && !m_decoder.m_gifDecoder->failed()) {
+ m_decoder.m_gifDecoder->setData(data, allDataReceived);
+ if (!allDataReceived || m_decoder.m_gifDecoder->frameCount() != 1)
+ return;
+ disabledAnimatedGif = true;
+ delete m_decoder.m_gifDecoder;
+ m_decoder.m_gifDecoder = 0;
+ }
+#endif
+ if (NULL == m_decoder.m_image
+#ifdef ANDROID_ANIMATED_GIF
+ && !m_decoder.m_gifDecoder
+#endif
+ ) {
+ SkBitmap tmp;
+
+ SkMemoryStream stream(data->data(), data->size(), false);
+ SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
+ if (!codec)
+ return;
+
+ SkAutoTDelete<SkImageDecoder> ad(codec);
+ codec->setPrefConfigTable(gPrefConfigTable);
+ if (!codec->decode(&stream, &tmp, SkImageDecoder::kDecodeBounds_Mode))
+ return;
+
+ int origW = tmp.width();
+ int origH = tmp.height();
+
+#ifdef ANDROID_ANIMATED_GIF
+ // First, check to see if this is an animated GIF
+ const Vector<char>& buffer = data->buffer();
+ const char* contents = buffer.data();
+ if (buffer.size() > 3 && strncmp(contents, "GIF8", 4) == 0
+ && should_use_animated_gif(origW, origH)
+ && !disabledAnimatedGif) {
+ // This means we are looking at a GIF, so create special
+ // GIF Decoder
+ // Need to wait for all data received if we are assigning an
+ // allocator (which we are not at the moment).
+ if (!m_decoder.m_gifDecoder /*&& allDataReceived*/)
+ m_decoder.m_gifDecoder = new GIFImageDecoder(m_alphaOption, m_gammaAndColorProfileOption);
+ int frameCount = 0;
+ if (!m_decoder.m_gifDecoder->failed()) {
+ m_decoder.m_gifDecoder->setData(data, allDataReceived);
+ if (!allDataReceived)
+ return;
+ frameCount = m_decoder.m_gifDecoder->frameCount();
+ }
+ if (frameCount != 1)
+ return;
+ delete m_decoder.m_gifDecoder;
+ m_decoder.m_gifDecoder = 0;
+ }
+#endif
+
+ int sampleSize = computeSampleSize(tmp);
+ if (sampleSize > 1) {
+ codec->setSampleSize(sampleSize);
+ stream.rewind();
+ if (!codec->decode(&stream, &tmp,
+ SkImageDecoder::kDecodeBounds_Mode)) {
+ return;
+ }
+ }
+
+ m_decoder.m_image = new PrivateAndroidImageSourceRec(tmp, origW, origH,
+ sampleSize);
+
+// SkDebugf("----- started: [%d %d] %s\n", origW, origH, m_decoder.m_url.c_str());
+ }
+
+ PrivateAndroidImageSourceRec* decoder = m_decoder.m_image;
+ if (allDataReceived && decoder && !decoder->fAllDataReceived) {
+ decoder->fAllDataReceived = true;
+
+ SkBitmap* bm = &decoder->bitmap();
+ SkPixelRef* ref = convertToRLE(bm, data->data(), data->size());
+
+ if (ref) {
+ bm->setPixelRef(ref)->unref();
+ } else {
+ BitmapAllocatorAndroid alloc(data, decoder->fSampleSize);
+ if (!alloc.allocPixelRef(bm, NULL)) {
+ return;
+ }
+ ref = bm->pixelRef();
+ }
+
+ // we promise to never change the pixels (makes picture recording fast)
+ ref->setImmutable();
+ // give it the URL if we have one
+ ref->setURI(m_decoder.m_url);
+ }
+}
+
+bool ImageSource::isSizeAvailable()
+{
+ return
+#ifdef ANDROID_ANIMATED_GIF
+ (m_decoder.m_gifDecoder
+ && m_decoder.m_gifDecoder->isSizeAvailable()) ||
+#endif
+ m_decoder.m_image != NULL;
+}
+
+IntSize ImageSource::size() const
+{
+#ifdef ANDROID_ANIMATED_GIF
+ if (m_decoder.m_gifDecoder)
+ return m_decoder.m_gifDecoder->size();
+#endif
+ if (m_decoder.m_image) {
+ return IntSize(m_decoder.m_image->origWidth(), m_decoder.m_image->origHeight());
+ }
+ return IntSize(0, 0);
+}
+
+int ImageSource::repetitionCount()
+{
+#ifdef ANDROID_ANIMATED_GIF
+ if (m_decoder.m_gifDecoder)
+ return m_decoder.m_gifDecoder->repetitionCount();
+ if (!m_decoder.m_image) return 0;
+#endif
+ return 1;
+ // A property with value 0 means loop forever.
+}
+
+size_t ImageSource::frameCount() const
+{
+#ifdef ANDROID_ANIMATED_GIF
+ if (m_decoder.m_gifDecoder) {
+ return m_decoder.m_gifDecoder->failed() ? 0
+ : m_decoder.m_gifDecoder->frameCount();
+ }
+#endif
+ // i.e. 0 frames if we're not decoded, or 1 frame if we are
+ return m_decoder.m_image != NULL;
+}
+
+SkBitmapRef* ImageSource::createFrameAtIndex(size_t index)
+{
+#ifdef ANDROID_ANIMATED_GIF
+ if (m_decoder.m_gifDecoder) {
+ RGBA32Buffer* buffer =
+ m_decoder.m_gifDecoder->frameBufferAtIndex(index);
+ if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
+ return 0;
+ SkBitmap& bitmap = buffer->bitmap();
+ SkPixelRef* pixelRef = bitmap.pixelRef();
+ if (pixelRef)
+ pixelRef->setURI(m_decoder.m_url);
+ return new SkBitmapRef(bitmap);
+ }
+#else
+ SkASSERT(index == 0);
+#endif
+ SkASSERT(m_decoder.m_image != NULL);
+ m_decoder.m_image->ref();
+ return m_decoder.m_image;
+}
+
+float ImageSource::frameDurationAtIndex(size_t index)
+{
+ float duration = 0;
+#ifdef ANDROID_ANIMATED_GIF
+ if (m_decoder.m_gifDecoder) {
+ RGBA32Buffer* buffer
+ = m_decoder.m_gifDecoder->frameBufferAtIndex(index);
+ if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
+ return 0;
+ duration = buffer->duration() / 1000.0f;
+ }
+#else
+ SkASSERT(index == 0);
+#endif
+
+ // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
+ // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
+ // a duration of <= 10 ms. See gfxImageFrame::GetTimeout in Gecko or Radar 4051389 for more.
+ if (duration <= 0.010f)
+ duration = 0.100f;
+ return duration;
+}
+
+bool ImageSource::frameHasAlphaAtIndex(size_t index)
+{
+#ifdef ANDROID_ANIMATED_GIF
+ if (m_decoder.m_gifDecoder) {
+ if (!m_decoder.m_gifDecoder->supportsAlpha())
+ return false;
+
+ RGBA32Buffer* buffer =
+ m_decoder.m_gifDecoder->frameBufferAtIndex(index);
+ if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
+ return false;
+
+ return buffer->hasAlpha();
+ }
+#else
+ SkASSERT(0 == index);
+#endif
+
+ if (NULL == m_decoder.m_image)
+ return true; // if we're not sure, assume the worse-case
+ const PrivateAndroidImageSourceRec& decoder = *m_decoder.m_image;
+ // if we're 16bit, we know even without all the data available
+ if (decoder.bitmap().getConfig() == SkBitmap::kRGB_565_Config)
+ return false;
+
+ if (!decoder.fAllDataReceived)
+ return true; // if we're not sure, assume the worse-case
+
+ return !decoder.bitmap().isOpaque();
+}
+
+bool ImageSource::frameIsCompleteAtIndex(size_t index)
+{
+#ifdef ANDROID_ANIMATED_GIF
+ if (m_decoder.m_gifDecoder) {
+ RGBA32Buffer* buffer =
+ m_decoder.m_gifDecoder->frameBufferAtIndex(index);
+ return buffer && buffer->status() == RGBA32Buffer::FrameComplete;
+ }
+#else
+ SkASSERT(0 == index);
+#endif
+ return m_decoder.m_image && m_decoder.m_image->fAllDataReceived;
+}
+
+void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived)
+{
+#ifdef ANDROID_ANIMATED_GIF
+ if (!destroyAll) {
+ if (m_decoder.m_gifDecoder)
+ m_decoder.m_gifDecoder->clearFrameBufferCache(clearBeforeFrame);
+ return;
+ }
+
+ delete m_decoder.m_gifDecoder;
+ m_decoder.m_gifDecoder = 0;
+ if (data)
+ setData(data, allDataReceived);
+#endif
+ // do nothing, since the cache is managed elsewhere
+}
+
+IntSize ImageSource::frameSizeAtIndex(size_t index) const
+{
+ // for now, all (1) of our frames are the same size
+ return this->size();
+}
+
+String ImageSource::filenameExtension() const
+{
+ // FIXME: need to add virtual to our decoders to return "jpg/png/gif/..."
+#ifdef ANDROID_ANIMATED_GIF
+ if (m_decoder.m_gifDecoder)
+ return m_decoder.m_gifDecoder->filenameExtension();
+#endif
+ return String();
+}
+
+bool ImageSource::getHotSpot(IntPoint&) const
+{
+ return false;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.cpp b/Source/WebCore/platform/graphics/android/LayerAndroid.cpp
new file mode 100644
index 0000000..7bef420
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/LayerAndroid.cpp
@@ -0,0 +1,1402 @@
+#include "config.h"
+#include "LayerAndroid.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "AndroidAnimation.h"
+#include "ClassTracker.h"
+#include "DrawExtra.h"
+#include "GLUtils.h"
+#include "MediaLayer.h"
+#include "PaintLayerOperation.h"
+#include "ParseCanvas.h"
+#include "SkBitmapRef.h"
+#include "SkBounder.h"
+#include "SkDrawFilter.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "TilesManager.h"
+#include <wtf/CurrentTime.h>
+
+#define LAYER_DEBUG // Add diagonals for debugging
+#undef LAYER_DEBUG
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "LayerAndroid", __VA_ARGS__)
+
+#ifdef DEBUG
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "LayerAndroid", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+static int gUniqueId;
+
+class OpacityDrawFilter : public SkDrawFilter {
+ public:
+ OpacityDrawFilter(int opacity) : m_opacity(opacity) { }
+ virtual void filter(SkPaint* paint, Type)
+ {
+ paint->setAlpha(m_opacity);
+ }
+ private:
+ int m_opacity;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+LayerAndroid::LayerAndroid(RenderLayer* owner) : SkLayer(),
+ m_haveClip(false),
+ m_isFixed(false),
+ m_isIframe(false),
+ m_backfaceVisibility(true),
+ m_visible(true),
+ m_preserves3D(false),
+ m_anchorPointZ(0),
+ m_recordingPicture(0),
+ m_contentsImage(0),
+ m_extra(0),
+ m_uniqueId(++gUniqueId),
+ m_drawingTexture(0),
+ m_reservedTexture(0),
+ m_pictureUsed(0),
+ m_requestSent(false),
+ m_scale(1),
+ m_lastComputeTextureSize(0),
+ m_owningLayer(owner)
+{
+ m_backgroundColor = 0;
+
+ m_preserves3D = false;
+ m_dirty = false;
+ m_iframeOffset.set(0,0);
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("LayerAndroid");
+#endif
+}
+
+LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer),
+ m_haveClip(layer.m_haveClip),
+ m_isIframe(layer.m_isIframe),
+ m_contentsImage(0),
+ m_extra(0), // deliberately not copied
+ m_uniqueId(layer.m_uniqueId),
+ m_drawingTexture(0),
+ m_reservedTexture(0),
+ m_requestSent(false),
+ m_owningLayer(layer.m_owningLayer)
+{
+ m_isFixed = layer.m_isFixed;
+ copyBitmap(layer.m_contentsImage);
+ m_renderLayerPos = layer.m_renderLayerPos;
+ m_transform = layer.m_transform;
+ m_backfaceVisibility = layer.m_backfaceVisibility;
+ m_visible = layer.m_visible;
+ m_backgroundColor = layer.m_backgroundColor;
+
+ m_fixedLeft = layer.m_fixedLeft;
+ m_fixedTop = layer.m_fixedTop;
+ m_fixedRight = layer.m_fixedRight;
+ m_fixedBottom = layer.m_fixedBottom;
+ m_fixedMarginLeft = layer.m_fixedMarginLeft;
+ m_fixedMarginTop = layer.m_fixedMarginTop;
+ m_fixedMarginRight = layer.m_fixedMarginRight;
+ m_fixedMarginBottom = layer.m_fixedMarginBottom;
+ m_fixedRect = layer.m_fixedRect;
+ m_iframeOffset = layer.m_iframeOffset;
+ m_recordingPicture = layer.m_recordingPicture;
+ SkSafeRef(m_recordingPicture);
+
+ m_preserves3D = layer.m_preserves3D;
+ m_anchorPointZ = layer.m_anchorPointZ;
+ m_drawTransform = layer.m_drawTransform;
+ m_childrenTransform = layer.m_childrenTransform;
+ m_dirty = layer.m_dirty;
+ m_pictureUsed = layer.m_pictureUsed;
+ m_scale = layer.m_scale;
+ m_lastComputeTextureSize = 0;
+
+ for (int i = 0; i < layer.countChildren(); i++)
+ addChild(layer.getChild(i)->copy())->unref();
+
+ KeyframesMap::const_iterator end = layer.m_animations.end();
+ for (KeyframesMap::const_iterator it = layer.m_animations.begin(); it != end; ++it) {
+ pair<String, int> key((it->second)->name(), (it->second)->type());
+ m_animations.add(key, (it->second)->copy());
+ }
+
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("LayerAndroid");
+#endif
+}
+
+LayerAndroid::LayerAndroid(SkPicture* picture) : SkLayer(),
+ m_haveClip(false),
+ m_isFixed(false),
+ m_isIframe(false),
+ m_recordingPicture(picture),
+ m_contentsImage(0),
+ m_extra(0),
+ m_uniqueId(-1),
+ m_drawingTexture(0),
+ m_reservedTexture(0),
+ m_requestSent(false),
+ m_scale(1),
+ m_lastComputeTextureSize(0),
+ m_owningLayer(0)
+{
+ m_backgroundColor = 0;
+ m_dirty = false;
+ SkSafeRef(m_recordingPicture);
+ m_iframeOffset.set(0,0);
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("LayerAndroid");
+#endif
+}
+
+bool LayerAndroid::removeTexture(BackedDoubleBufferedTexture* aTexture)
+{
+ LayerTexture* texture = static_cast<LayerTexture*>(aTexture);
+ android::AutoMutex lock(m_atomicSync);
+
+ bool textureReleased = true;
+ if (!texture) { // remove ourself from both textures
+ if (m_drawingTexture)
+ textureReleased &= m_drawingTexture->release(this);
+ if (m_reservedTexture &&
+ m_reservedTexture != m_drawingTexture)
+ textureReleased &= m_reservedTexture->release(this);
+ } else {
+ if (m_drawingTexture && m_drawingTexture == texture)
+ textureReleased &= m_drawingTexture->release(this);
+ if (m_reservedTexture &&
+ m_reservedTexture == texture &&
+ m_reservedTexture != m_drawingTexture)
+ textureReleased &= m_reservedTexture->release(this);
+ }
+ if (m_drawingTexture &&
+ ((m_drawingTexture->owner() != this) ||
+ (m_drawingTexture->delayedReleaseOwner() == this)))
+ m_drawingTexture = 0;
+ if (m_reservedTexture &&
+ ((m_reservedTexture->owner() != this) ||
+ (m_reservedTexture->delayedReleaseOwner() == this)))
+ m_reservedTexture = 0;
+ return textureReleased;
+}
+
+LayerAndroid::~LayerAndroid()
+{
+ removeTexture(0);
+ removeChildren();
+ delete m_extra;
+ delete m_contentsImage;
+ SkSafeUnref(m_recordingPicture);
+ m_animations.clear();
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("LayerAndroid");
+#endif
+}
+
+static int gDebugNbAnims = 0;
+
+bool LayerAndroid::evaluateAnimations()
+{
+ double time = WTF::currentTime();
+ gDebugNbAnims = 0;
+ return evaluateAnimations(time);
+}
+
+bool LayerAndroid::hasAnimations() const
+{
+ for (int i = 0; i < countChildren(); i++) {
+ if (getChild(i)->hasAnimations())
+ return true;
+ }
+ return !!m_animations.size();
+}
+
+bool LayerAndroid::evaluateAnimations(double time)
+{
+ bool hasRunningAnimations = false;
+ for (int i = 0; i < countChildren(); i++) {
+ if (getChild(i)->evaluateAnimations(time))
+ hasRunningAnimations = true;
+ }
+
+ m_hasRunningAnimations = false;
+ int nbAnims = 0;
+ KeyframesMap::const_iterator end = m_animations.end();
+ for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
+ gDebugNbAnims++;
+ nbAnims++;
+ LayerAndroid* currentLayer = const_cast<LayerAndroid*>(this);
+ if (!(it->second)->finished() &&
+ (it->second)->evaluate(currentLayer, time))
+ m_hasRunningAnimations = true;
+ }
+
+ return hasRunningAnimations || m_hasRunningAnimations;
+}
+
+void LayerAndroid::addDirtyArea(GLWebViewState* glWebViewState)
+{
+ IntSize layerSize(getSize().width(), getSize().height());
+
+ FloatRect area = TilesManager::instance()->shader()->rectInInvScreenCoord(drawTransform(), layerSize);
+ FloatRect clip = TilesManager::instance()->shader()->convertScreenCoordToInvScreenCoord(m_clippingRect);
+
+ area.intersect(clip);
+ IntRect dirtyArea(area.x(), area.y(), area.width(), area.height());
+ glWebViewState->addDirtyArea(dirtyArea);
+}
+
+void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> prpAnim)
+{
+ RefPtr<AndroidAnimation> anim = prpAnim;
+ pair<String, int> key(anim->name(), anim->type());
+ removeAnimationsForProperty(anim->type());
+ m_animations.add(key, anim);
+}
+
+void LayerAndroid::removeAnimationsForProperty(AnimatedPropertyID property)
+{
+ KeyframesMap::const_iterator end = m_animations.end();
+ Vector<pair<String, int> > toDelete;
+ for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
+ if ((it->second)->type() == property)
+ toDelete.append(it->first);
+ }
+
+ for (unsigned int i = 0; i < toDelete.size(); i++)
+ m_animations.remove(toDelete[i]);
+}
+
+void LayerAndroid::removeAnimationsForKeyframes(const String& name)
+{
+ KeyframesMap::const_iterator end = m_animations.end();
+ Vector<pair<String, int> > toDelete;
+ for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) {
+ if ((it->second)->name() == name)
+ toDelete.append(it->first);
+ }
+
+ for (unsigned int i = 0; i < toDelete.size(); i++)
+ m_animations.remove(toDelete[i]);
+}
+
+// We only use the bounding rect of the layer as mask...
+// TODO: use a real mask?
+void LayerAndroid::setMaskLayer(LayerAndroid* layer)
+{
+ if (layer)
+ m_haveClip = true;
+}
+
+void LayerAndroid::setBackgroundColor(SkColor color)
+{
+ m_backgroundColor = color;
+}
+
+static int gDebugChildLevel;
+
+FloatPoint LayerAndroid::translation() const
+{
+ TransformationMatrix::DecomposedType tDecomp;
+ m_transform.decompose(tDecomp);
+ FloatPoint p(tDecomp.translateX, tDecomp.translateY);
+ return p;
+}
+
+SkRect LayerAndroid::bounds() const
+{
+ SkRect rect;
+ bounds(&rect);
+ return rect;
+}
+
+void LayerAndroid::bounds(SkRect* rect) const
+{
+ const SkPoint& pos = this->getPosition();
+ const SkSize& size = this->getSize();
+
+ // The returned rect has the translation applied
+ // FIXME: apply the full transform to the rect,
+ // and fix the text selection accordingly
+ FloatPoint p(pos.fX, pos.fY);
+ p = m_transform.mapPoint(p);
+ rect->fLeft = p.x();
+ rect->fTop = p.y();
+ rect->fRight = p.x() + size.width();
+ rect->fBottom = p.y() + size.height();
+}
+
+static bool boundsIsUnique(const SkTDArray<SkRect>& region,
+ const SkRect& local)
+{
+ for (int i = 0; i < region.count(); i++) {
+ if (region[i].contains(local))
+ return false;
+ }
+ return true;
+}
+
+void LayerAndroid::clipArea(SkTDArray<SkRect>* region) const
+{
+ SkRect local;
+ local.set(0, 0, std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::max());
+ clipInner(region, local);
+}
+
+void LayerAndroid::clipInner(SkTDArray<SkRect>* region,
+ const SkRect& local) const
+{
+ SkRect localBounds;
+ bounds(&localBounds);
+ localBounds.intersect(local);
+ if (localBounds.isEmpty())
+ return;
+ if (m_recordingPicture && boundsIsUnique(*region, localBounds))
+ *region->append() = localBounds;
+ for (int i = 0; i < countChildren(); i++)
+ getChild(i)->clipInner(region, m_haveClip ? localBounds : local);
+}
+
+class FindCheck : public SkBounder {
+public:
+ FindCheck()
+ : m_drew(false)
+ , m_drewText(false)
+ {
+ }
+
+ bool drew() const { return m_drew; }
+ bool drewText() const { return m_drewText; }
+ void reset() { m_drew = m_drewText = false; }
+
+protected:
+ virtual bool onIRect(const SkIRect& )
+ {
+ m_drew = true;
+ return false;
+ }
+
+ virtual bool onIRectGlyph(const SkIRect& , const SkBounder::GlyphRec& )
+ {
+ m_drew = m_drewText = true;
+ return false;
+ }
+
+ bool m_drew;
+ bool m_drewText;
+};
+
+class FindCanvas : public ParseCanvas {
+public:
+ void draw(SkPicture* picture, SkScalar offsetX, SkScalar offsetY)
+ {
+ save();
+ translate(-offsetX, -offsetY);
+ picture->draw(this);
+ restore();
+ }
+};
+
+class LayerAndroid::FindState {
+public:
+ static const int TOUCH_SLOP = 10;
+
+ FindState(int x, int y)
+ : m_x(x)
+ , m_y(y)
+ , m_bestX(x)
+ , m_bestY(y)
+ , m_best(0)
+ {
+ m_bitmap.setConfig(SkBitmap::kARGB_8888_Config, TOUCH_SLOP * 2,
+ TOUCH_SLOP * 2);
+ m_checker.setBounder(&m_findCheck);
+ m_checker.setBitmapDevice(m_bitmap);
+ }
+
+ const LayerAndroid* best() const { return m_best; }
+ int bestX() const { return m_bestX; }
+ int bestY() const { return m_bestY; }
+
+ bool drew(SkPicture* picture, const SkRect& localBounds)
+ {
+ m_findCheck.reset();
+ SkScalar localX = SkIntToScalar(m_x - TOUCH_SLOP) - localBounds.fLeft;
+ SkScalar localY = SkIntToScalar(m_y - TOUCH_SLOP) - localBounds.fTop;
+ m_checker.draw(picture, localX, localY);
+ return m_findCheck.drew();
+ }
+
+ bool drewText() { return m_findCheck.drewText(); }
+
+ void setBest(const LayerAndroid* best, int x, int y)
+ {
+ m_best = best;
+ m_bestX = x;
+ m_bestY = y;
+ }
+ int x() const { return m_x; }
+ int y() const { return m_y; }
+
+ void setLocation(int x, int y)
+ {
+ m_x = x;
+ m_y = y;
+ }
+
+protected:
+ int m_x;
+ int m_y;
+ int m_bestX;
+ int m_bestY;
+ const LayerAndroid* m_best;
+ FindCheck m_findCheck;
+ SkBitmap m_bitmap;
+ FindCanvas m_checker;
+};
+
+void LayerAndroid::findInner(LayerAndroid::FindState& state) const
+{
+ int x = state.x();
+ int y = state.y();
+ SkRect localBounds;
+ bounds(&localBounds);
+ if (!localBounds.contains(x, y))
+ return;
+ // Move into local coordinates.
+ state.setLocation(x - localBounds.fLeft, y - localBounds.fTop);
+ for (int i = 0; i < countChildren(); i++)
+ getChild(i)->findInner(state);
+ // Move back into the parent coordinates.
+ int testX = state.x();
+ int testY = state.y();
+ state.setLocation(x + localBounds.fLeft, y + localBounds.fTop);
+ if (!m_recordingPicture)
+ return;
+ if (!contentIsScrollable() && !state.drew(m_recordingPicture, localBounds))
+ return;
+ state.setBest(this, testX, testY); // set last match (presumably on top)
+}
+
+const LayerAndroid* LayerAndroid::find(int* xPtr, int* yPtr, SkPicture* root) const
+{
+ FindState state(*xPtr, *yPtr);
+ SkRect rootBounds;
+ rootBounds.setEmpty();
+ if (root && state.drew(root, rootBounds) && state.drewText())
+ return 0; // use the root picture only if it contains the text
+ findInner(state);
+ *xPtr = state.bestX();
+ *yPtr = state.bestY();
+ return state.best();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void LayerAndroid::updateFixedLayersPositions(SkRect viewport, LayerAndroid* parentIframeLayer)
+{
+ // If this is an iframe, accumulate the offset from the parent with
+ // current position, and change the parent pointer.
+ if (m_isIframe) {
+ // If this is the top level, take the current position
+ SkPoint parentOffset;
+ parentOffset.set(0,0);
+ if (parentIframeLayer)
+ parentOffset = parentIframeLayer->getPosition();
+
+ m_iframeOffset = parentOffset + getPosition();
+
+ parentIframeLayer = this;
+ }
+
+ if (m_isFixed) {
+ // So if this is a fixed layer inside a iframe, use the iframe offset
+ // and the iframe's size as the viewport and pass to the children
+ if (parentIframeLayer) {
+ viewport = SkRect::MakeXYWH(parentIframeLayer->m_iframeOffset.fX,
+ parentIframeLayer->m_iframeOffset.fY,
+ parentIframeLayer->getSize().width(),
+ parentIframeLayer->getSize().height());
+ }
+ float w = viewport.width();
+ float h = viewport.height();
+ float dx = viewport.fLeft;
+ float dy = viewport.fTop;
+ float x = dx;
+ float y = dy;
+
+ // It turns out that when it is 'auto', the webkit computation will
+ // take one more factor into account, that is the original render
+ // layer's X,Y, such that it will align well with the parent's layer.
+ if (!(m_fixedLeft.defined() || m_fixedRight.defined()))
+ x += m_renderLayerPos.x();
+
+ if (!(m_fixedTop.defined() || m_fixedBottom.defined()))
+ y += m_renderLayerPos.y();
+
+ if (m_fixedLeft.defined() || !m_fixedRight.defined())
+ x += m_fixedMarginLeft.calcFloatValue(w) + m_fixedLeft.calcFloatValue(w) - m_fixedRect.fLeft;
+ else
+ x += w - m_fixedMarginRight.calcFloatValue(w) - m_fixedRight.calcFloatValue(w) - m_fixedRect.fRight;
+
+ if (m_fixedTop.defined() || !m_fixedBottom.defined())
+ y += m_fixedMarginTop.calcFloatValue(h) + m_fixedTop.calcFloatValue(h) - m_fixedRect.fTop;
+ else
+ y += h - m_fixedMarginBottom.calcFloatValue(h) - m_fixedBottom.calcFloatValue(h) - m_fixedRect.fBottom;
+
+ this->setPosition(x, y);
+ }
+
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->updateFixedLayersPositions(viewport, parentIframeLayer);
+}
+
+void LayerAndroid::updatePositions()
+{
+ // apply the viewport to us
+ if (!m_isFixed) {
+ // turn our fields into a matrix.
+ //
+ // TODO: this should happen in the caller, and we should remove these
+ // fields from our subclass
+ SkMatrix matrix;
+ GLUtils::toSkMatrix(matrix, m_transform);
+ this->setMatrix(matrix);
+ }
+
+ // now apply it to our children
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->updatePositions();
+}
+
+void LayerAndroid::updateGLPositions(const TransformationMatrix& parentMatrix,
+ const FloatRect& clipping, float opacity)
+{
+ IntSize layerSize(getSize().width(), getSize().height());
+ FloatPoint anchorPoint(getAnchorPoint().fX, getAnchorPoint().fY);
+ FloatPoint position(getPosition().fX, getPosition().fY);
+ float centerOffsetX = (0.5f - anchorPoint.x()) * layerSize.width();
+ float centerOffsetY = (0.5f - anchorPoint.y()) * layerSize.height();
+ float originX = anchorPoint.x() * layerSize.width();
+ float originY = anchorPoint.y() * layerSize.height();
+ TransformationMatrix localMatrix;
+ if (!m_isFixed)
+ localMatrix = parentMatrix;
+ localMatrix.translate3d(originX + position.x(),
+ originY + position.y(),
+ anchorPointZ());
+ localMatrix.multLeft(m_transform);
+ localMatrix.translate3d(-originX,
+ -originY,
+ -anchorPointZ());
+
+ setDrawTransform(localMatrix);
+ m_zValue = TilesManager::instance()->shader()->zValue(drawTransform(), getSize().width(), getSize().height());
+
+ opacity *= getOpacity();
+ setDrawOpacity(opacity);
+
+ if (m_haveClip) {
+ // The clipping rect calculation and intersetion will be done in Screen Coord now.
+ FloatRect clip =
+ TilesManager::instance()->shader()->rectInScreenCoord(drawTransform(), layerSize);
+ clip.intersect(clipping);
+ setDrawClip(clip);
+ } else {
+ setDrawClip(clipping);
+ }
+
+ if (!m_backfaceVisibility
+ && drawTransform().inverse().m33() < 0) {
+ setVisible(false);
+ return;
+ } else {
+ setVisible(true);
+ }
+
+ int count = this->countChildren();
+ if (!count)
+ return;
+
+ // Flatten to 2D if the layer doesn't preserve 3D.
+ if (!preserves3D()) {
+ localMatrix.setM13(0);
+ localMatrix.setM23(0);
+ localMatrix.setM31(0);
+ localMatrix.setM32(0);
+ localMatrix.setM33(1);
+ localMatrix.setM34(0);
+ localMatrix.setM43(0);
+ }
+
+ // now apply it to our children
+
+ if (!m_childrenTransform.isIdentity()) {
+ localMatrix.translate(getSize().width() * 0.5f, getSize().height() * 0.5f);
+ localMatrix.multLeft(m_childrenTransform);
+ localMatrix.translate(-getSize().width() * 0.5f, -getSize().height() * 0.5f);
+ }
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->updateGLPositions(localMatrix, drawClip(), opacity);
+}
+
+void LayerAndroid::copyBitmap(SkBitmap* bitmap)
+{
+ if (!bitmap)
+ return;
+
+ delete m_contentsImage;
+ m_contentsImage = new SkBitmap();
+ SkBitmap::Config config = bitmap->config();
+ int w = bitmap->width();
+ int h = bitmap->height();
+ m_contentsImage->setConfig(config, w, h);
+ bitmap->copyTo(m_contentsImage, config);
+}
+
+void LayerAndroid::setContentsImage(SkBitmapRef* img)
+{
+ copyBitmap(&img->bitmap());
+}
+
+bool LayerAndroid::needsTexture()
+{
+ return m_contentsImage || (prepareContext()
+ && m_recordingPicture->width() && m_recordingPicture->height());
+}
+
+IntRect LayerAndroid::clippedRect() const
+{
+ IntRect r(0, 0, getWidth(), getHeight());
+ IntRect tr = drawTransform().mapRect(r);
+ IntRect cr = TilesManager::instance()->shader()->clippedRectWithViewport(tr);
+ IntRect rect = drawTransform().inverse().mapRect(cr);
+ return rect;
+}
+
+bool LayerAndroid::outsideViewport()
+{
+ return m_layerTextureRect.width() == 0 &&
+ m_layerTextureRect.height() == 0;
+}
+
+int LayerAndroid::fullTextureSize() const
+{
+ return getWidth() * m_scale * getHeight() * m_scale * 4;
+}
+
+int LayerAndroid::clippedTextureSize() const
+{
+ IntRect cr = clippedRect();
+ return cr.width() * cr.height() * 4;
+}
+
+int LayerAndroid::countTextureSize()
+{
+ int size = clippedTextureSize();
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ size += getChild(i)->countTextureSize();
+ return size;
+}
+
+int LayerAndroid::nbLayers()
+{
+ int nb = 1;
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ nb += getChild(i)->nbLayers();
+ return nb;
+}
+
+void LayerAndroid::collect(Vector<LayerAndroid*>& layers, int& size)
+{
+ m_layerTextureRect = clippedRect();
+ if (!outsideViewport()) {
+ layers.append(this);
+ size += fullTextureSize();
+ }
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ getChild(i)->collect(layers, size);
+}
+
+static inline bool compareLayerFullSize(const LayerAndroid* a, const LayerAndroid* b)
+{
+ const int sizeA = a->fullTextureSize();
+ const int sizeB = b->fullTextureSize();
+ return sizeA > sizeB;
+}
+
+void LayerAndroid::computeTextureSize(double time)
+{
+ if (m_lastComputeTextureSize + s_computeTextureDelay > time)
+ return;
+ m_lastComputeTextureSize = time;
+
+ // First, we collect the layers, computing m_layerTextureRect
+ // as being clipped against the viewport
+ Vector <LayerAndroid*> layers;
+ int total = 0;
+ collect(layers, total);
+
+ // Then we sort them by the size the full texture would need
+ std::stable_sort(layers.begin(), layers.end(), compareLayerFullSize);
+
+ // Now, let's determinate which layer can use a full texture
+ int max = TilesManager::instance()->maxLayersAllocation();
+ int maxLayerSize = TilesManager::instance()->maxLayerAllocation();
+ XLOG("*** layers sorted by size ***");
+ XLOG("total memory needed: %d bytes (%d Mb), max %d Mb",
+ total, total / 1024 / 1024, max / 1024 / 1024);
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ LayerAndroid* layer = layers[i];
+ bool clipped = true;
+ // If we are under the maximum, and the layer inspected
+ // needs a texture less than the maxLayerSize, use the full texture.
+ if ((total < max) &&
+ (layer->fullTextureSize() < maxLayerSize) &&
+ (layer->getWidth() * m_scale < TilesManager::instance()->getMaxTextureSize()) &&
+ (layer->getHeight() * m_scale < TilesManager::instance()->getMaxTextureSize())) {
+ IntRect full(0, 0, layer->getWidth(), layer->getHeight());
+ layer->m_layerTextureRect = full;
+ clipped = false;
+ } else {
+ // Otherwise, the layer is clipped; update the total
+ total -= layer->fullTextureSize();
+ total += layer->clippedTextureSize();
+ }
+ XLOG("Layer %d (%.2f, %.2f) %d bytes (clipped: %s)",
+ layer->uniqueId(), layer->getWidth(), layer->getHeight(),
+ layer->fullTextureSize(),
+ clipped ? "YES" : "NO");
+ }
+ XLOG("total memory used after clipping: %d bytes (%d Mb), max %d Mb",
+ total, total / 1024 / 1024, max / 1024 / 1024);
+ XLOG("*** end of sorted layers ***");
+}
+
+void LayerAndroid::showLayers(int indent)
+{
+ IntRect cr = clippedRect();
+ int size = cr.width() * cr.height() * 4;
+
+ char space[256];
+ int p = 0;
+ for (; p < indent; p++)
+ space[p] = ' ';
+ space[p] = '\0';
+
+ bool outside = outsideViewport();
+ if (needsTexture() && !outside) {
+ XLOGC("%s Layer %d (%.2f, %.2f), cropped to (%d, %d, %d, %d), using %d Mb",
+ space, uniqueId(), getWidth(), getHeight(),
+ cr.x(), cr.y(), cr.width(), cr.height(), size / 1024 / 1024);
+ } else if (needsTexture() && outside) {
+ XLOGC("%s Layer %d is outside the viewport", space, uniqueId());
+ } else {
+ XLOGC("%s Layer %d has no texture", space, uniqueId());
+ }
+
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ getChild(i)->showLayers(indent + 1);
+}
+
+void LayerAndroid::reserveGLTextures()
+{
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->reserveGLTextures();
+
+ if (!needsTexture())
+ return;
+
+ if (outsideViewport())
+ return;
+
+ LayerTexture* reservedTexture = 0;
+ reservedTexture = TilesManager::instance()->getExistingTextureForLayer(
+ this, m_layerTextureRect);
+
+ // If we do not have a drawing texture (i.e. new LayerAndroid tree),
+ // we get any one available.
+ if (!m_drawingTexture) {
+ LayerTexture* texture = reservedTexture;
+ m_drawingTexture =
+ TilesManager::instance()->getExistingTextureForLayer(
+ this, m_layerTextureRect, true, texture);
+
+ if (!m_drawingTexture)
+ m_drawingTexture = reservedTexture;
+ }
+
+ // SMP flush
+ android::AutoMutex lock(m_atomicSync);
+ // we set the reservedTexture if it's different from the drawing texture
+ if (m_reservedTexture != reservedTexture &&
+ ((reservedTexture != m_drawingTexture) ||
+ (m_reservedTexture == 0 && m_drawingTexture == 0))) {
+ // Call release on the reserved texture if it is not the same as the
+ // drawing texture.
+ if (m_reservedTexture && (m_reservedTexture != m_drawingTexture))
+ m_reservedTexture->release(this);
+ m_reservedTexture = reservedTexture;
+ }
+}
+
+void LayerAndroid::createGLTextures()
+{
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->createGLTextures();
+
+ if (!needsTexture())
+ return;
+
+ if (outsideViewport())
+ return;
+
+ if (m_drawingTexture && !needsScheduleRepaint(m_drawingTexture))
+ return;
+
+ LayerTexture* reservedTexture = m_reservedTexture;
+ if (!reservedTexture)
+ reservedTexture = TilesManager::instance()->createTextureForLayer(this, m_layerTextureRect);
+
+ if (!reservedTexture)
+ return;
+
+ // SMP flush
+ m_atomicSync.lock();
+ m_reservedTexture = reservedTexture;
+ m_atomicSync.unlock();
+
+ if (reservedTexture &&
+ reservedTexture->ready() &&
+ (reservedTexture != m_drawingTexture)) {
+ if (m_drawingTexture) {
+ TilesManager::instance()->removeOperationsForTexture(m_drawingTexture);
+ m_drawingTexture->release(this);
+ }
+ m_drawingTexture = reservedTexture;
+ }
+
+ if (!needsScheduleRepaint(reservedTexture))
+ return;
+
+ m_atomicSync.lock();
+ if (!m_requestSent) {
+ m_requestSent = true;
+ m_atomicSync.unlock();
+ XLOG("We schedule a paint for layer %d (%x), because m_dirty %d, using texture %x (%d, %d)",
+ uniqueId(), this, m_dirty, m_reservedTexture,
+ m_reservedTexture->rect().width(), m_reservedTexture->rect().height());
+ PaintLayerOperation* operation = new PaintLayerOperation(this);
+ TilesManager::instance()->scheduleOperation(operation);
+ } else {
+ XLOG("We don't schedule a paint for layer %d (%x), because we already sent a request",
+ uniqueId(), this);
+ m_atomicSync.unlock();
+ }
+}
+
+bool LayerAndroid::needsScheduleRepaint(LayerTexture* texture)
+{
+ if (!texture)
+ return false;
+
+ if (!texture->ready()) {
+ m_dirty = true;
+ return true;
+ }
+
+ TextureInfo* textureInfo = texture->consumerLock();
+ if (!texture->readyFor(this) ||
+ (texture->rect() != m_layerTextureRect))
+ m_dirty = true;
+ texture->consumerRelease();
+
+ return m_dirty;
+}
+
+static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b)
+{
+ return a->zValue() > b->zValue();
+}
+
+bool LayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
+{
+ TilesManager::instance()->shader()->clip(m_clippingRect);
+ if (!m_visible)
+ return false;
+
+ if (m_drawingTexture) {
+ TextureInfo* textureInfo = m_drawingTexture->consumerLock();
+ bool ready = m_drawingTexture->readyFor(this);
+ if (textureInfo && (!m_contentsImage || (ready && m_contentsImage))) {
+ SkRect bounds;
+ bounds.set(m_drawingTexture->rect());
+ XLOG("LayerAndroid %d %x (%.2f, %.2f) drawGL (texture %x, %d, %d, %d, %d)",
+ uniqueId(), this, getWidth(), getHeight(),
+ m_drawingTexture, bounds.x(), bounds.y(),
+ bounds.width(), bounds.height());
+ //TODO determine when drawing if the alpha value is used.
+ TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), bounds,
+ textureInfo->m_textureId,
+ m_drawOpacity, true);
+ }
+ if (!ready)
+ m_dirty = true;
+ m_drawingTexture->consumerRelease();
+ } else if (needsTexture()) {
+ m_dirty = true;
+ }
+
+ // When the layer is dirty, the UI thread should be notified to redraw.
+ bool askPaint = drawChildrenGL(glWebViewState, matrix);
+ m_atomicSync.lock();
+ askPaint |= m_dirty;
+ if ((m_dirty && needsTexture()) || m_hasRunningAnimations || drawTransform().hasPerspective())
+ addDirtyArea(glWebViewState);
+
+ m_atomicSync.unlock();
+ return askPaint;
+}
+
+
+bool LayerAndroid::drawChildrenGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
+{
+ bool askPaint = false;
+ int count = this->countChildren();
+ if (count > 0) {
+ Vector <LayerAndroid*> sublayers;
+ for (int i = 0; i < count; i++)
+ sublayers.append(this->getChild(i));
+
+ // now we sort for the transparency
+ std::stable_sort(sublayers.begin(), sublayers.end(), compareLayerZ);
+ for (int i = 0; i < count; i++) {
+ LayerAndroid* layer = sublayers[i];
+ askPaint |= layer->drawGL(glWebViewState, matrix);
+ }
+ }
+
+ return askPaint;
+}
+
+void LayerAndroid::setScale(float scale)
+{
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->setScale(scale);
+
+ android::AutoMutex lock(m_atomicSync);
+ m_scale = scale;
+}
+
+// This is called from the texture generation thread
+void LayerAndroid::paintBitmapGL()
+{
+ // We acquire the values below atomically. This ensures that we are reading
+ // values correctly across cores. Further, once we have these values they
+ // can be updated by other threads without consequence.
+ m_atomicSync.lock();
+ LayerTexture* texture = m_reservedTexture;
+
+ if (!texture) {
+ m_atomicSync.unlock();
+ XLOG("Layer %d doesn't have a texture!", uniqueId());
+ return;
+ }
+
+ XLOG("LayerAndroid %d paintBitmapGL, texture used %x (%d, %d)", uniqueId(), texture,
+ texture->rect().width(), texture->rect().height());
+
+ // We need to mark the texture as busy before relinquishing the lock
+ // -- so that TilesManager::cleanupLayersTextures() can check if the texture
+ // is used before trying to destroy it
+ // If LayerAndroid::removeTexture() is called before us, we'd have bailed
+ // out early as texture would have been null; if it is called after us, we'd
+ // have marked the texture has being busy, and the texture will not be
+ // destroyed immediately.
+ texture->producerAcquireContext();
+ TextureInfo* textureInfo = texture->producerLock();
+ m_atomicSync.unlock();
+
+ // at this point we can safely check the ownership (if the texture got
+ // transferred to another BaseTile under us)
+ if (texture->owner() != this) {
+ texture->producerRelease();
+ return;
+ }
+
+ XLOG("LayerAndroid %d %x (%.2f, %.2f) paintBitmapGL WE ARE PAINTING", uniqueId(), this, getWidth(), getHeight());
+ SkCanvas* canvas = texture->canvas();
+ float scale = texture->scale();
+
+ IntRect textureRect = texture->rect();
+ canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
+
+ if (m_contentsImage) {
+ contentDraw(canvas);
+ } else {
+ SkPicture picture;
+ SkCanvas* nCanvas = picture.beginRecording(textureRect.width(),
+ textureRect.height());
+ nCanvas->scale(scale, scale);
+ nCanvas->translate(-textureRect.x(), -textureRect.y());
+ contentDraw(nCanvas);
+ picture.endRecording();
+ picture.draw(canvas);
+ }
+ extraDraw(canvas);
+
+ m_atomicSync.lock();
+ texture->setTextureInfoFor(this);
+
+ m_dirty = false;
+ m_requestSent = false;
+
+ XLOG("LayerAndroid %d paintBitmapGL PAINTING DONE, updating the texture", uniqueId());
+ texture->producerUpdate(textureInfo);
+
+ m_atomicSync.unlock();
+
+ XLOG("LayerAndroid %d paintBitmapGL UPDATING DONE", uniqueId());
+}
+
+void LayerAndroid::extraDraw(SkCanvas* canvas)
+{
+ m_atomicSync.lock();
+ if (m_extra)
+ canvas->drawPicture(*m_extra);
+ m_atomicSync.unlock();
+}
+
+void LayerAndroid::contentDraw(SkCanvas* canvas)
+{
+ if (m_contentsImage) {
+ SkRect dest;
+ dest.set(0, 0, getSize().width(), getSize().height());
+ canvas->drawBitmapRect(*m_contentsImage, 0, dest);
+ } else {
+ canvas->drawPicture(*m_recordingPicture);
+ }
+
+ if (TilesManager::instance()->getShowVisualIndicator()) {
+ float w = getSize().width();
+ float h = getSize().height();
+ SkPaint paint;
+ paint.setARGB(128, 255, 0, 0);
+ canvas->drawLine(0, 0, w, h, paint);
+ canvas->drawLine(0, h, w, 0, paint);
+ paint.setARGB(128, 0, 255, 0);
+ canvas->drawLine(0, 0, 0, h, paint);
+ canvas->drawLine(0, h, w, h, paint);
+ canvas->drawLine(w, h, w, 0, paint);
+ canvas->drawLine(w, 0, 0, 0, paint);
+
+ if (m_isFixed) {
+ SkPaint paint;
+ paint.setARGB(80, 255, 0, 0);
+ canvas->drawRect(m_fixedRect, paint);
+ }
+ }
+}
+
+void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity)
+{
+ if (m_haveClip) {
+ SkRect r;
+ r.set(0, 0, getSize().width(), getSize().height());
+ canvas->clipRect(r);
+ return;
+ }
+
+ if (!prepareContext())
+ return;
+
+ // we just have this save/restore for opacity...
+ SkAutoCanvasRestore restore(canvas, true);
+
+ int canvasOpacity = SkScalarRound(opacity * 255);
+ if (canvasOpacity < 255)
+ canvas->setDrawFilter(new OpacityDrawFilter(canvasOpacity));
+
+ contentDraw(canvas);
+}
+
+SkPicture* LayerAndroid::recordContext()
+{
+ if (prepareContext(true))
+ return m_recordingPicture;
+ return 0;
+}
+
+bool LayerAndroid::prepareContext(bool force)
+{
+ if (masksToBounds())
+ return false;
+
+ if (force || !m_recordingPicture ||
+ (m_recordingPicture &&
+ ((m_recordingPicture->width() != (int) getSize().width()) ||
+ (m_recordingPicture->height() != (int) getSize().height())))) {
+ SkSafeUnref(m_recordingPicture);
+ m_recordingPicture = new SkPicture();
+ }
+
+ return m_recordingPicture;
+}
+
+SkRect LayerAndroid::subtractLayers(const SkRect& visibleRect) const
+{
+ SkRect result;
+ if (m_recordingPicture) {
+ SkRect globalRect = bounds();
+ globalRect.offset(-getPosition()); // localToGlobal adds in position
+ SkMatrix globalMatrix;
+ localToGlobal(&globalMatrix);
+ globalMatrix.mapRect(&globalRect);
+ SkIRect roundedGlobal;
+ globalRect.round(&roundedGlobal);
+ SkIRect iVisibleRect;
+ visibleRect.round(&iVisibleRect);
+ SkRegion visRegion(iVisibleRect);
+ visRegion.op(roundedGlobal, SkRegion::kDifference_Op);
+ result.set(visRegion.getBounds());
+#if DEBUG_NAV_UI
+ SkDebugf("%s visibleRect=(%g,%g,r=%g,b=%g) globalRect=(%g,%g,r=%g,b=%g)"
+ "result=(%g,%g,r=%g,b=%g)", __FUNCTION__,
+ visibleRect.fLeft, visibleRect.fTop,
+ visibleRect.fRight, visibleRect.fBottom,
+ globalRect.fLeft, globalRect.fTop,
+ globalRect.fRight, globalRect.fBottom,
+ result.fLeft, result.fTop, result.fRight, result.fBottom);
+#endif
+ } else
+ result = visibleRect;
+ for (int i = 0; i < countChildren(); i++)
+ result = getChild(i)->subtractLayers(result);
+ return result;
+}
+
+// Debug tools : dump the layers tree in a file.
+// The format is simple:
+// properties have the form: key = value;
+// all statements are finished with a semi-colon.
+// value can be:
+// - int
+// - float
+// - array of elements
+// - composed type
+// a composed type enclose properties in { and }
+// an array enclose composed types in { }, separated with a comma.
+// exemple:
+// {
+// x = 3;
+// y = 4;
+// value = {
+// x = 3;
+// y = 4;
+// };
+// anarray = [
+// { x = 3; },
+// { y = 4; }
+// ];
+// }
+
+void lwrite(FILE* file, const char* str)
+{
+ fwrite(str, sizeof(char), strlen(str), file);
+}
+
+void writeIndent(FILE* file, int indentLevel)
+{
+ if (indentLevel)
+ fprintf(file, "%*s", indentLevel*2, " ");
+}
+
+void writeln(FILE* file, int indentLevel, const char* str)
+{
+ writeIndent(file, indentLevel);
+ lwrite(file, str);
+ lwrite(file, "\n");
+}
+
+void writeIntVal(FILE* file, int indentLevel, const char* str, int value)
+{
+ writeIndent(file, indentLevel);
+ fprintf(file, "%s = %d;\n", str, value);
+}
+
+void writeHexVal(FILE* file, int indentLevel, const char* str, int value)
+{
+ writeIndent(file, indentLevel);
+ fprintf(file, "%s = %x;\n", str, value);
+}
+
+void writeFloatVal(FILE* file, int indentLevel, const char* str, float value)
+{
+ writeIndent(file, indentLevel);
+ fprintf(file, "%s = %.3f;\n", str, value);
+}
+
+void writePoint(FILE* file, int indentLevel, const char* str, SkPoint point)
+{
+ writeIndent(file, indentLevel);
+ fprintf(file, "%s = { x = %.3f; y = %.3f; };\n", str, point.fX, point.fY);
+}
+
+void writeSize(FILE* file, int indentLevel, const char* str, SkSize size)
+{
+ writeIndent(file, indentLevel);
+ fprintf(file, "%s = { w = %.3f; h = %.3f; };\n", str, size.width(), size.height());
+}
+
+void writeRect(FILE* file, int indentLevel, const char* str, SkRect rect)
+{
+ writeIndent(file, indentLevel);
+ fprintf(file, "%s = { x = %.3f; y = %.3f; w = %.3f; h = %.3f; };\n",
+ str, rect.fLeft, rect.fTop, rect.width(), rect.height());
+}
+
+void writeLength(FILE* file, int indentLevel, const char* str, SkLength length)
+{
+ if (!length.defined())
+ return;
+ writeIndent(file, indentLevel);
+ fprintf(file, "%s = { type = %d; value = %.2f; };\n", str, length.type, length.value);
+}
+
+void writeMatrix(FILE* file, int indentLevel, const char* str, const TransformationMatrix& matrix)
+{
+ writeIndent(file, indentLevel);
+ fprintf(file, "%s = { (%.2f,%.2f,%.2f,%.2f),(%.2f,%.2f,%.2f,%.2f),"
+ "(%.2f,%.2f,%.2f,%.2f),(%.2f,%.2f,%.2f,%.2f) };\n",
+ str,
+ matrix.m11(), matrix.m12(), matrix.m13(), matrix.m14(),
+ matrix.m21(), matrix.m22(), matrix.m23(), matrix.m24(),
+ matrix.m31(), matrix.m32(), matrix.m33(), matrix.m34(),
+ matrix.m41(), matrix.m42(), matrix.m43(), matrix.m44());
+}
+
+void LayerAndroid::dumpLayers(FILE* file, int indentLevel) const
+{
+ writeln(file, indentLevel, "{");
+
+ writeHexVal(file, indentLevel + 1, "layer", (int)this);
+ writeIntVal(file, indentLevel + 1, "layerId", m_uniqueId);
+ writeIntVal(file, indentLevel + 1, "haveClip", m_haveClip);
+ writeIntVal(file, indentLevel + 1, "isFixed", m_isFixed);
+ writeIntVal(file, indentLevel + 1, "m_isIframe", m_isIframe);
+ writePoint(file, indentLevel + 1, "m_iframeOffset", m_iframeOffset);
+
+ writeFloatVal(file, indentLevel + 1, "opacity", getOpacity());
+ writeSize(file, indentLevel + 1, "size", getSize());
+ writePoint(file, indentLevel + 1, "position", getPosition());
+ writePoint(file, indentLevel + 1, "anchor", getAnchorPoint());
+
+ writeMatrix(file, indentLevel + 1, "drawMatrix", drawTransform());
+ writeMatrix(file, indentLevel + 1, "transformMatrix", m_transform);
+
+ if (m_isFixed) {
+ writeLength(file, indentLevel + 1, "fixedLeft", m_fixedLeft);
+ writeLength(file, indentLevel + 1, "fixedTop", m_fixedTop);
+ writeLength(file, indentLevel + 1, "fixedRight", m_fixedRight);
+ writeLength(file, indentLevel + 1, "fixedBottom", m_fixedBottom);
+ writeLength(file, indentLevel + 1, "fixedMarginLeft", m_fixedMarginLeft);
+ writeLength(file, indentLevel + 1, "fixedMarginTop", m_fixedMarginTop);
+ writeLength(file, indentLevel + 1, "fixedMarginRight", m_fixedMarginRight);
+ writeLength(file, indentLevel + 1, "fixedMarginBottom", m_fixedMarginBottom);
+ writeRect(file, indentLevel + 1, "fixedRect", m_fixedRect);
+ }
+
+ if (m_recordingPicture) {
+ writeIntVal(file, indentLevel + 1, "m_recordingPicture.width", m_recordingPicture->width());
+ writeIntVal(file, indentLevel + 1, "m_recordingPicture.height", m_recordingPicture->height());
+ }
+
+ if (countChildren()) {
+ writeln(file, indentLevel + 1, "children = [");
+ for (int i = 0; i < countChildren(); i++) {
+ if (i > 0)
+ writeln(file, indentLevel + 1, ", ");
+ getChild(i)->dumpLayers(file, indentLevel + 1);
+ }
+ writeln(file, indentLevel + 1, "];");
+ }
+ writeln(file, indentLevel, "}");
+}
+
+void LayerAndroid::dumpToLog() const
+{
+ FILE* file = fopen("/data/data/com.android.browser/layertmp", "w");
+ dumpLayers(file, 0);
+ fclose(file);
+ file = fopen("/data/data/com.android.browser/layertmp", "r");
+ char buffer[512];
+ bzero(buffer, sizeof(buffer));
+ while (fgets(buffer, sizeof(buffer), file))
+ SkDebugf("%s", buffer);
+ fclose(file);
+}
+
+LayerAndroid* LayerAndroid::findById(int match)
+{
+ if (m_uniqueId == match)
+ return this;
+ for (int i = 0; i < countChildren(); i++) {
+ LayerAndroid* result = getChild(i)->findById(match);
+ if (result)
+ return result;
+ }
+ return 0;
+}
+
+void LayerAndroid::setExtra(DrawExtra* extra)
+{
+ for (int i = 0; i < countChildren(); i++)
+ getChild(i)->setExtra(extra);
+
+ android::AutoMutex lock(m_atomicSync);
+ if (extra || (m_extra && !extra))
+ m_dirty = true;
+
+ delete m_extra;
+ m_extra = 0;
+
+ if (!extra)
+ return;
+
+ if (m_recordingPicture) {
+ IntRect dummy; // inval area, unused for now
+ m_extra = new SkPicture();
+ SkCanvas* canvas = m_extra->beginRecording(m_recordingPicture->width(),
+ m_recordingPicture->height());
+ extra->draw(canvas, this, &dummy);
+ m_extra->endRecording();
+ }
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.h b/Source/WebCore/platform/graphics/android/LayerAndroid.h
new file mode 100644
index 0000000..d4510c5
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/LayerAndroid.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LayerAndroid_h
+#define LayerAndroid_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FloatPoint.h"
+#include "FloatPoint3D.h"
+#include "FloatRect.h"
+#include "GraphicsLayerClient.h"
+#include "LayerTexture.h"
+#include "RefPtr.h"
+#include "SkColor.h"
+#include "SkLayer.h"
+#include "TextureOwner.h"
+#include "TransformationMatrix.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/text/StringHash.h>
+
+#ifndef BZERO_DEFINED
+#define BZERO_DEFINED
+// http://www.opengroup.org/onlinepubs/000095399/functions/bzero.html
+// For maximum portability, it is recommended to replace the function call to bzero() as follows:
+#define bzero(b, len) (memset((b), '\0', (len)), (void) 0)
+#endif
+
+class SkBitmapRef;
+class SkCanvas;
+class SkMatrix;
+class SkPicture;
+
+namespace android {
+class DrawExtra;
+}
+
+using namespace android;
+
+struct SkLength {
+ enum SkLengthType { Undefined, Auto, Relative, Percent, Fixed, Static, Intrinsic, MinIntrinsic };
+ SkLengthType type;
+ SkScalar value;
+ SkLength()
+ {
+ type = Undefined;
+ value = 0;
+ }
+ bool defined() const
+ {
+ if (type == Undefined)
+ return false;
+ return true;
+ }
+ float calcFloatValue(float max) const
+ {
+ switch (type) {
+ case Percent:
+ return (max * value) / 100.0f;
+ case Fixed:
+ return value;
+ default:
+ return value;
+ }
+ }
+};
+
+namespace WebCore {
+
+class AndroidAnimation;
+class BackedDoubleBufferedTexture;
+class LayerAndroidFindState;
+class RenderLayer;
+class TiledPage;
+
+class LayerAndroid : public SkLayer, public TextureOwner {
+
+public:
+ LayerAndroid(RenderLayer* owner);
+ LayerAndroid(const LayerAndroid& layer);
+ LayerAndroid(SkPicture*);
+ virtual ~LayerAndroid();
+
+ // TextureOwner methods
+ virtual bool removeTexture(BackedDoubleBufferedTexture* texture);
+
+ LayerTexture* texture() { return m_reservedTexture; }
+ virtual TiledPage* page() { return 0; }
+
+ void setBackfaceVisibility(bool value) { m_backfaceVisibility = value; }
+ void setTransform(const TransformationMatrix& matrix) { m_transform = matrix; }
+ FloatPoint translation() const;
+ SkRect bounds() const;
+ IntRect clippedRect() const;
+ bool outsideViewport();
+
+ // Debug/info functions
+ int countTextureSize();
+ int nbLayers();
+ void showLayers(int indent = 0);
+
+ // Texture size functions
+ void computeTextureSize(double time);
+ void collect(Vector<LayerAndroid*>& layers,
+ int& size);
+ int clippedTextureSize() const;
+ int fullTextureSize() const;
+
+ // called on the root layer
+ void reserveGLTextures();
+ void createGLTextures();
+
+ virtual bool needsTexture();
+ bool needsScheduleRepaint(LayerTexture* texture);
+
+ void setScale(float scale);
+ float getScale() { return m_scale; }
+ virtual bool drawGL(GLWebViewState*, SkMatrix&);
+ bool drawChildrenGL(GLWebViewState*, SkMatrix&);
+ virtual void paintBitmapGL();
+ void updateGLPositions(const TransformationMatrix& parentMatrix,
+ const FloatRect& clip, float opacity);
+ void setDrawOpacity(float opacity) { m_drawOpacity = opacity; }
+ void setVisible(bool value) { m_visible = value; }
+
+ bool preserves3D() { return m_preserves3D; }
+ void setPreserves3D(bool value) { m_preserves3D = value; }
+ void setAnchorPointZ(float z) { m_anchorPointZ = z; }
+ float anchorPointZ() { return m_anchorPointZ; }
+ void setDrawTransform(const TransformationMatrix& transform) { m_drawTransform = transform; }
+ const TransformationMatrix& drawTransform() const { return m_drawTransform; }
+ void setChildrenTransform(const TransformationMatrix& t) { m_childrenTransform = t; }
+ void setDrawClip(const FloatRect& rect) { m_clippingRect = rect; }
+ const FloatRect& drawClip() { return m_clippingRect; }
+
+ void setFixedPosition(SkLength left, // CSS left property
+ SkLength top, // CSS top property
+ SkLength right, // CSS right property
+ SkLength bottom, // CSS bottom property
+ SkLength marginLeft, // CSS margin-left property
+ SkLength marginTop, // CSS margin-top property
+ SkLength marginRight, // CSS margin-right property
+ SkLength marginBottom, // CSS margin-bottom property
+ const IntPoint& renderLayerPos, // For undefined fixed position
+ SkRect viewRect) { // view rect, can be smaller than the layer's
+ m_fixedLeft = left;
+ m_fixedTop = top;
+ m_fixedRight = right;
+ m_fixedBottom = bottom;
+ m_fixedMarginLeft = marginLeft;
+ m_fixedMarginTop = marginTop;
+ m_fixedMarginRight = marginRight;
+ m_fixedMarginBottom = marginBottom;
+ m_fixedRect = viewRect;
+ m_isFixed = true;
+ m_renderLayerPos = renderLayerPos;
+ setInheritFromRootTransform(true);
+ }
+
+ void setBackgroundColor(SkColor color);
+ void setMaskLayer(LayerAndroid*);
+ void setMasksToBounds(bool masksToBounds)
+ {
+ m_haveClip = masksToBounds;
+ }
+ bool masksToBounds() const { return m_haveClip; }
+
+ SkPicture* recordContext();
+
+ void addAnimation(PassRefPtr<AndroidAnimation> anim);
+ void removeAnimationsForProperty(AnimatedPropertyID property);
+ void removeAnimationsForKeyframes(const String& name);
+ bool evaluateAnimations();
+ bool evaluateAnimations(double time);
+ bool hasAnimations() const;
+ void addDirtyArea(GLWebViewState*);
+
+ SkPicture* picture() const { return m_recordingPicture; }
+
+ // remove layers bounds from visible rectangle to show what can be
+ // scrolled into view; returns original minus layer bounds in global space.
+ SkRect subtractLayers(const SkRect& visibleRect) const;
+
+ void dumpLayers(FILE*, int indentLevel) const;
+ void dumpToLog() const;
+
+ /** Call this with the current viewport (scrolling, zoom) to update
+ the position of the fixed layers.
+
+ This call is recursive, so it should be called on the root of the
+ hierarchy.
+ */
+ void updateFixedLayersPositions(SkRect viewPort, LayerAndroid* parentIframeLayer = 0);
+
+ /** Call this to update the position attribute, so that later calls
+ like bounds() will report the corrected position.
+
+ This call is recursive, so it should be called on the root of the
+ hierarchy.
+ */
+ void updatePositions();
+
+ void clipArea(SkTDArray<SkRect>* region) const;
+ const LayerAndroid* find(int* xPtr, int* yPtr, SkPicture* root) const;
+ const LayerAndroid* findById(int uniqueID) const
+ {
+ return const_cast<LayerAndroid*>(this)->findById(uniqueID);
+ }
+ LayerAndroid* findById(int uniqueID);
+ LayerAndroid* getChild(int index) const
+ {
+ return static_cast<LayerAndroid*>(this->INHERITED::getChild(index));
+ }
+ void setExtra(DrawExtra* extra); // does not assign ownership
+ int uniqueId() const { return m_uniqueId; }
+ bool isFixed() { return m_isFixed; }
+
+ /** This sets a content image -- calling it means we will use
+ the image directly when drawing the layer instead of using
+ the content painted by WebKit. See comments below for
+ m_recordingPicture and m_contentsImage.
+ */
+ void setContentsImage(SkBitmapRef* img);
+ bool hasContentsImage() { return m_contentsImage; }
+ void copyBitmap(SkBitmap*);
+
+ void bounds(SkRect*) const;
+
+ virtual bool contentIsScrollable() const { return false; }
+ virtual LayerAndroid* copy() const { return new LayerAndroid(*this); }
+
+ void needsRepaint() { m_pictureUsed++; }
+ unsigned int pictureUsed() { return m_pictureUsed; }
+ void contentDraw(SkCanvas*);
+ void extraDraw(SkCanvas*);
+
+ virtual bool isMedia() const { return false; }
+ virtual bool isVideo() const { return false; }
+
+ RenderLayer* owningLayer() const { return m_owningLayer; }
+
+ void setIsIframe(bool isIframe) { m_isIframe = isIframe; }
+ float zValue() const { return m_zValue; }
+
+protected:
+ virtual void onDraw(SkCanvas*, SkScalar opacity);
+
+private:
+ class FindState;
+#if DUMP_NAV_CACHE
+ friend class CachedLayer::Debug; // debugging access only
+#endif
+
+ void findInner(FindState&) const;
+ bool prepareContext(bool force = false);
+ void clipInner(SkTDArray<SkRect>* region, const SkRect& local) const;
+
+ bool m_haveClip;
+ bool m_isFixed;
+ bool m_backgroundColorSet;
+ bool m_isIframe;
+
+ SkLength m_fixedLeft;
+ SkLength m_fixedTop;
+ SkLength m_fixedRight;
+ SkLength m_fixedBottom;
+ SkLength m_fixedMarginLeft;
+ SkLength m_fixedMarginTop;
+ SkLength m_fixedMarginRight;
+ SkLength m_fixedMarginBottom;
+ SkRect m_fixedRect;
+
+ SkPoint m_iframeOffset;
+ // When fixed element is undefined or auto, the render layer's position
+ // is needed for offset computation
+ IntPoint m_renderLayerPos;
+
+ TransformationMatrix m_transform;
+ float m_zValue;
+ bool m_backfaceVisibility;
+ bool m_visible;
+
+ SkColor m_backgroundColor;
+
+ bool m_preserves3D;
+ float m_anchorPointZ;
+ float m_drawOpacity;
+ TransformationMatrix m_drawTransform;
+ TransformationMatrix m_childrenTransform;
+ FloatRect m_clippingRect;
+
+ // Note that m_recordingPicture and m_contentsImage are mutually exclusive;
+ // m_recordingPicture is used when WebKit is asked to paint the layer's
+ // content, while m_contentsImage contains an image that we directly
+ // composite, using the layer's dimensions as a destination rect.
+ // We do this as if the layer only contains an image, directly compositing
+ // it is a much faster method than using m_recordingPicture.
+ SkPicture* m_recordingPicture;
+
+ SkBitmap* m_contentsImage;
+
+ typedef HashMap<pair<String, int>, RefPtr<AndroidAnimation> > KeyframesMap;
+ KeyframesMap m_animations;
+ SkPicture* m_extra;
+ int m_uniqueId;
+
+ // We have two textures pointers -- one if the texture we are currently
+ // using to draw (m_drawingTexture), the other one is the one we get
+ // from trying to reserve a texture from the TilesManager. Usually, they
+ // are identical, but in some cases they are not (different scaling
+ // resulting in the need for different geometry, at initilisation, and
+ // if the texture asked does not fit in memory)
+ LayerTexture* m_drawingTexture;
+ LayerTexture* m_reservedTexture;
+
+ // rect used to query TilesManager for the right texture
+ IntRect m_layerTextureRect;
+
+ // used to signal that the tile is out-of-date and needs to be redrawn
+ bool m_dirty;
+ unsigned int m_pictureUsed;
+
+ // used to signal the framework we need a repaint
+ bool m_hasRunningAnimations;
+
+ // painting request sent
+ bool m_requestSent;
+
+ float m_scale;
+
+ // We try to not always compute the texture size, as this is quite heavy
+ static const double s_computeTextureDelay = 0.2; // 200 ms
+ double m_lastComputeTextureSize;
+
+ // This mutex serves two purposes. (1) It ensures that certain operations
+ // happen atomically and (2) it makes sure those operations are synchronized
+ // across all threads and cores.
+ android::Mutex m_atomicSync;
+
+ RenderLayer* m_owningLayer;
+
+ typedef SkLayer INHERITED;
+};
+
+}
+
+#else
+
+class SkPicture;
+
+namespace WebCore {
+
+class LayerAndroid {
+public:
+ LayerAndroid(SkPicture* picture) :
+ m_recordingPicture(picture), // does not assign ownership
+ m_uniqueId(-1)
+ {}
+ SkPicture* picture() const { return m_recordingPicture; }
+ int uniqueId() const { return m_uniqueId; }
+private:
+ SkPicture* m_recordingPicture;
+ int m_uniqueId;
+};
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif // LayerAndroid_h
diff --git a/Source/WebCore/platform/graphics/android/LayerTexture.cpp b/Source/WebCore/platform/graphics/android/LayerTexture.cpp
new file mode 100644
index 0000000..f311f32
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/LayerTexture.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "LayerTexture.h"
+
+#include "LayerAndroid.h"
+
+namespace WebCore {
+
+unsigned int LayerTexture::pictureUsed()
+{
+ consumerLock();
+ TextureTileInfo* info = m_texturesInfo.get(getReadableTexture());
+ unsigned int pictureUsed = 0;
+ if (info)
+ pictureUsed = info->m_picture;
+ consumerRelease();
+ return pictureUsed;
+}
+
+void LayerTexture::setTextureInfoFor(LayerAndroid* layer)
+{
+ TextureTileInfo* textureInfo = m_texturesInfo.get(getWriteableTexture());
+ if (!textureInfo) {
+ textureInfo = new TextureTileInfo();
+ }
+ textureInfo->m_layerId = layer->uniqueId();
+ textureInfo->m_picture = layer->pictureUsed();
+ textureInfo->m_scale = layer->getScale();
+ m_texturesInfo.set(getWriteableTexture(), textureInfo);
+ m_layerId = layer->uniqueId();
+ m_scale = layer->getScale();
+ if (!m_ready)
+ m_ready = true;
+}
+
+bool LayerTexture::readyFor(LayerAndroid* layer)
+{
+ TextureTileInfo* info = m_texturesInfo.get(getReadableTexture());
+ if (info &&
+ info->m_layerId == layer->uniqueId() &&
+ info->m_scale == layer->getScale() &&
+ info->m_picture == layer->pictureUsed())
+ return true;
+ return false;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/android/LayerTexture.h b/Source/WebCore/platform/graphics/android/LayerTexture.h
new file mode 100644
index 0000000..be3594f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/LayerTexture.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 LayerTexture_h
+#define LayerTexture_h
+
+#include "BackedDoubleBufferedTexture.h"
+#include "ClassTracker.h"
+#include "IntRect.h"
+
+namespace WebCore {
+
+class LayerTexture : public BackedDoubleBufferedTexture {
+ public:
+ LayerTexture(uint32_t w, uint32_t h,
+ SkBitmap::Config config = SkBitmap::kARGB_8888_Config)
+ : BackedDoubleBufferedTexture(w, h, 0, config)
+ , m_layerId(0)
+ , m_scale(1)
+ , m_ready(false)
+ {
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("LayerTexture");
+#endif
+ }
+ virtual ~LayerTexture()
+ {
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("LayerTexture");
+#endif
+ };
+
+ void setTextureInfoFor(LayerAndroid* layer);
+ bool readyFor(LayerAndroid* layer);
+ void setRect(const IntRect& r) { m_rect = r; }
+ IntRect& rect() { return m_rect; }
+ int id() { return m_layerId; }
+ float scale() { return m_scale; }
+ void setId(int id) { m_layerId = id; }
+ void setScale(float scale) { m_scale = scale; }
+ bool ready() { return m_ready; }
+ unsigned int pictureUsed();
+
+ private:
+
+ IntRect m_rect;
+ int m_layerId;
+ float m_scale;
+ bool m_ready;
+};
+
+} // namespace WebCore
+
+#endif // LayerTexture_h
diff --git a/Source/WebCore/platform/graphics/android/MediaLayer.cpp b/Source/WebCore/platform/graphics/android/MediaLayer.cpp
new file mode 100644
index 0000000..1ba6d46
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/MediaLayer.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "MediaLayer.h"
+#include "MediaTexture.h"
+#include "TilesManager.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#define LAYER_DEBUG
+#undef LAYER_DEBUG
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "MediaLayer", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+MediaLayer::MediaLayer(jobject weakWebViewRef) : LayerAndroid((RenderLayer*) NULL)
+{
+ m_bufferedTexture = new MediaTexture(EGL_NO_CONTEXT);
+ m_bufferedTexture->producerInc();
+ m_videoTexture = new VideoTexture(weakWebViewRef);
+ m_videoTexture->incStrong(this);
+
+ m_isCopy = false;
+ m_currentTextureInfo = 0;
+ m_isContentInverted = false;
+ m_outlineSize = 0;
+ XLOG("Creating Media Layer %p", this);
+ XLOG("producer: %d consumer: %d", m_bufferedTexture->getProducerCount(),
+ m_bufferedTexture->getConsumerCount());
+}
+
+MediaLayer::MediaLayer(const MediaLayer& layer) : LayerAndroid(layer)
+{
+ m_bufferedTexture = layer.getTexture();
+ m_bufferedTexture->consumerInc();
+ m_videoTexture = layer.m_videoTexture;
+ m_videoTexture->incStrong(this);
+
+ m_isCopy = true;
+ m_currentTextureInfo = 0;
+ m_isContentInverted = layer.m_isContentInverted;
+ m_outlineSize = layer.m_outlineSize;
+ XLOG("Creating Media Layer Copy %p -> %p", &layer, this);
+ XLOG("producer: %d consumer: %d COPY", m_bufferedTexture->getProducerCount(),
+ m_bufferedTexture->getConsumerCount());
+}
+
+MediaLayer::~MediaLayer()
+{
+ XLOG("Deleting Media Layer");
+ XLOG("producer: %d consumer: %d %s", m_bufferedTexture->getProducerCount(),
+ m_bufferedTexture->getConsumerCount(), (m_isCopy) ? "COPY" : "");
+
+ if (m_isCopy)
+ m_bufferedTexture->consumerDec();
+ else
+ m_bufferedTexture->producerDec();
+ m_videoTexture->decStrong(this);
+}
+
+bool MediaLayer::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
+{
+ TilesManager::instance()->shader()->clip(drawClip());
+
+ // when the plugin gains focus webkit applies an outline to the
+ // widget, which causes the layer to expand to accommodate the
+ // outline. Therefore, we shrink the rect by the outline's dimensions
+ // to ensure the plugin does not draw outside of its bounds.
+ SkRect mediaBounds;
+ mediaBounds.set(0, 0, getSize().width(), getSize().height());
+ mediaBounds.inset(m_outlineSize, m_outlineSize);
+
+ // check to see if we need to create a video texture
+ m_videoTexture->initNativeWindowIfNeeded();
+ // draw any video content if present
+ m_videoTexture->drawVideo(drawTransform(), mediaBounds);
+
+ // draw the primary content
+ if (m_bufferedTexture) {
+ TextureInfo* textureInfo = m_bufferedTexture->consumerLock();
+ if (textureInfo && textureInfo->m_width != 0 && textureInfo->m_height != 0) {
+ // the layer's shader draws the content inverted so we must undo
+ // that change in the transformation matrix
+ TransformationMatrix m = drawTransform();
+ if (!m_isContentInverted) {
+ m.flipY();
+ m.translate(0, -getSize().height());
+ }
+
+ bool forceBlending = textureInfo->m_internalFormat == GL_RGBA ||
+ textureInfo->m_internalFormat == GL_BGRA_EXT ||
+ textureInfo->m_internalFormat == GL_ALPHA;
+ TilesManager::instance()->shader()->drawLayerQuad(m, mediaBounds,
+ textureInfo->m_textureId,
+ 1.0f, forceBlending);
+ }
+ m_bufferedTexture->consumerRelease();
+ }
+
+ return drawChildrenGL(glWebViewState, matrix);
+}
+
+ANativeWindow* MediaLayer::acquireNativeWindowForVideo()
+{
+ return m_videoTexture->requestNewWindow();
+}
+
+void MediaLayer::setWindowDimensionsForVideo(const ANativeWindow* window, const SkRect& dimensions)
+{
+ if (window != m_videoTexture->getNativeWindow())
+ return;
+
+ //TODO validate that the dimensions do not exceed the plugin's bounds
+ m_videoTexture->setDimensions(dimensions);
+}
+
+void MediaLayer::releaseNativeWindowForVideo(ANativeWindow* window)
+{
+ if (window == m_videoTexture->getNativeWindow())
+ m_videoTexture->releaseNativeWindow();
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/MediaLayer.h b/Source/WebCore/platform/graphics/android/MediaLayer.h
new file mode 100644
index 0000000..8a07134
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/MediaLayer.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MediaLayer_h
+#define MediaLayer_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "MediaTexture.h"
+#include "LayerAndroid.h"
+#include <jni.h>
+
+namespace android {
+ class SurfaceTexture;
+}
+
+namespace WebCore {
+
+class MediaLayer : public LayerAndroid {
+
+public:
+ MediaLayer(jobject weakWebViewRef);
+ MediaLayer(const MediaLayer& layer);
+ virtual ~MediaLayer();
+
+ virtual bool drawGL(GLWebViewState*, SkMatrix&);
+ virtual void paintBitmapGL() const { };
+ virtual bool needsTexture() { return false; }
+
+ virtual bool isMedia() const { return true; }
+ virtual LayerAndroid* copy() const { return new MediaLayer(*this); }
+
+ MediaTexture* getTexture() const { return m_bufferedTexture; }
+
+ void setCurrentTextureInfo(TextureInfo* info) { m_currentTextureInfo = info; }
+ TextureInfo* getCurrentTextureInfo() const { return m_currentTextureInfo; }
+
+ void invertContents(bool invertContent) { m_isContentInverted = invertContent; }
+ void setOutlineSize(int size) { m_outlineSize = size; }
+
+ // functions to manipulate secondary layers for video playback
+ ANativeWindow* acquireNativeWindowForVideo();
+ void setWindowDimensionsForVideo(const ANativeWindow* window, const SkRect& dimensions);
+ void releaseNativeWindowForVideo(ANativeWindow* window);
+
+private:
+ bool m_isCopy;
+
+ // Primary GL texture variables
+ MediaTexture* m_bufferedTexture;
+ TextureInfo* m_currentTextureInfo;
+
+ bool m_isContentInverted;
+ int m_outlineSize;
+
+ // Video texture variables
+ VideoTexture* m_videoTexture;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif // MediaLayer_h
diff --git a/Source/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h b/Source/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h
new file mode 100644
index 0000000..404ef08
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2009,2010 The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 MediaPlayerPrivateAndroid_h
+#define MediaPlayerPrivateAndroid_h
+
+#if ENABLE(VIDEO)
+
+class SkBitmap;
+
+#include "MediaPlayerPrivate.h"
+#include "TimeRanges.h"
+#include "VideoLayerAndroid.h"
+
+namespace WebCore {
+
+class MediaPlayerPrivate : public MediaPlayerPrivateInterface {
+public:
+ virtual ~MediaPlayerPrivate();
+
+ static void registerMediaEngine(MediaEngineRegistrar);
+
+ virtual void load(const String& url) = 0;
+ virtual void cancelLoad() { }
+
+ virtual void play() = 0;
+ virtual void pause();
+
+ virtual IntSize naturalSize() const { return m_naturalSize; }
+
+ virtual bool supportsFullscreen() const = 0;
+ virtual bool hasAudio() const = 0;
+ virtual bool hasVideo() const = 0;
+
+ virtual void setVisible(bool);
+
+ virtual float duration() const { return m_duration; }
+
+ virtual float currentTime() const { return m_currentTime; };
+ virtual void seek(float time);
+ virtual bool seeking() const { return false; }
+
+ virtual void setEndTime(float time) { }
+
+ virtual void setRate(float) { }
+ virtual bool paused() const { return m_paused; }
+
+ virtual void setVolume(float) { }
+
+ virtual MediaPlayer::NetworkState networkState() const { return m_networkState; }
+ virtual MediaPlayer::ReadyState readyState() const { return m_readyState; }
+
+ virtual float maxTimeSeekable() const { return 0; }
+ virtual PassRefPtr<TimeRanges> buffered() const { return TimeRanges::create(); }
+
+ virtual int dataRate() const { return 0; }
+
+ virtual bool totalBytesKnown() const { return totalBytes() > 0; }
+ virtual unsigned totalBytes() const { return 0; }
+ virtual unsigned bytesLoaded() const { return 0; }
+
+ virtual void setSize(const IntSize&) { }
+
+ virtual bool canLoadPoster() const { return false; }
+ virtual void setPoster(const String&) { }
+ virtual void prepareToPlay();
+
+ virtual void paint(GraphicsContext*, const IntRect&) { }
+
+ virtual void onPrepared(int duration, int width, int height) { }
+ void onEnded();
+ void onPaused();
+ virtual void onPosterFetched(SkBitmap*) { }
+ void onBuffering(int percent);
+ void onTimeupdate(int position);
+
+ // These following two functions are used to turn on inline video support
+ bool supportsAcceleratedRendering() const { return true; }
+ LayerAndroid* platformLayer() const
+ {
+ return m_videoLayer;
+ }
+ void onStopFullscreen();
+
+protected:
+ // Android-specific methods and fields.
+ static MediaPlayerPrivateInterface* create(MediaPlayer* player);
+ static void getSupportedTypes(HashSet<String>&) { }
+ static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs);
+
+ MediaPlayerPrivate(MediaPlayer *);
+ virtual void createJavaPlayerIfNeeded() { }
+
+ MediaPlayer* m_player;
+ String m_url;
+ struct JavaGlue;
+ JavaGlue* m_glue;
+
+ float m_duration;
+ float m_currentTime;
+
+ bool m_paused;
+ MediaPlayer::ReadyState m_readyState;
+ MediaPlayer::NetworkState m_networkState;
+
+ SkBitmap* m_poster; // not owned
+ String m_posterUrl;
+
+ IntSize m_naturalSize;
+ bool m_naturalSizeUnknown;
+
+ bool m_isVisible;
+ VideoLayerAndroid* m_videoLayer;
+};
+
+} // namespace WebCore
+
+#endif
+
+#endif // MediaPlayerPrivateAndroid_h
diff --git a/Source/WebCore/platform/graphics/android/MediaTexture.cpp b/Source/WebCore/platform/graphics/android/MediaTexture.cpp
new file mode 100644
index 0000000..14f0c20
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/MediaTexture.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "MediaTexture.h"
+#include "TilesManager.h"
+#include "GLUtils.h"
+#include "VideoListener.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include <android/native_window.h>
+#include <gui/SurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+#include <wtf/CurrentTime.h>
+#include <JNIUtility.h>
+#include "WebCoreJni.h"
+
+#define LAYER_DEBUG
+#undef LAYER_DEBUG
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "MediaTexture", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+MediaTexture::MediaTexture(EGLContext sharedContext) : DoubleBufferedTexture(sharedContext)
+{
+ m_producerRefCount = 0;
+ m_consumerRefCount = 0;
+}
+
+/* Increment the number of objects in the producer's thread that hold a reference
+ * to this object. In practice, there is often only one producer reference for
+ * the lifetime of the object.
+ */
+void MediaTexture::producerInc()
+{
+ android::Mutex::Autolock lock(m_mediaLock);
+ m_producerRefCount++;
+}
+
+/* Decrement the number of objects in the producer's thread that are holding a
+ * reference to this object. When removing the last reference we must cleanup
+ * all GL objects that are associated with the producer's thread. There may not
+ * be a consumer reference as the object may not have synced to the UI thread,
+ * in which case the producer needs to handle the deletion of the object.
+ */
+void MediaTexture::producerDec()
+{
+ bool needsDeleted = false;
+
+ m_mediaLock.lock();
+ m_producerRefCount--;
+ if (m_producerRefCount == 0) {
+ producerDeleteTextures();
+ if (m_consumerRefCount < 1) {
+ XLOG("INFO: This texture has not been synced to the UI thread");
+ needsDeleted = true;
+ }
+ }
+ m_mediaLock.unlock();
+
+ if (needsDeleted) {
+ XLOG("Deleting MediaTexture Object");
+ delete this;
+ }
+}
+
+/* Increment the number of objects in the consumer's thread that hold a reference
+ * to this object. In practice, there can be multiple producer references as the
+ * consumer (i.e. UI) thread may have multiple copies of the layer tree.
+ */
+void MediaTexture::consumerInc()
+{
+ android::Mutex::Autolock lock(m_mediaLock);
+ m_consumerRefCount++;
+}
+
+/* Decrement the number of objects in the consumer's thread that are holding a
+ * reference to this object. When removing the last reference we must delete
+ * this object and by extension cleanup all GL objects that are associated with
+ * the consumer's thread. At the time of deletion if there is a remaining
+ * producer reference we must cleanup the consumer GL objects in the event that
+ * this texture will not be re-synced with the UI thread.
+ */
+void MediaTexture::consumerDec()
+{
+ bool needsDeleted = false;
+
+ m_mediaLock.lock();
+ m_consumerRefCount--;
+ if (m_consumerRefCount == 0) {
+ consumerDeleteTextures();
+ if (m_producerRefCount < 1) {
+ XLOG("WARNING: This texture still exists within webkit.");
+ needsDeleted = true;
+ }
+ }
+ m_mediaLock.unlock();
+
+ if (needsDeleted) {
+ XLOG("Deleting MediaTexture Object");
+ delete this;
+ }
+}
+
+VideoTexture::VideoTexture(jobject weakWebViewRef) : android::LightRefBase<VideoTexture>()
+{
+ m_weakWebViewRef = weakWebViewRef;
+ m_textureId = 0;
+ m_dimensions.setEmpty();
+ m_newWindowRequest = false;
+ m_newWindowReady = false;
+ m_videoListener = new VideoListener(m_weakWebViewRef);
+}
+
+VideoTexture::~VideoTexture()
+{
+ releaseNativeWindow();
+ if (m_textureId)
+ glDeleteTextures(1, &m_textureId);
+ if (m_weakWebViewRef) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteWeakGlobalRef(m_weakWebViewRef);
+ }
+}
+
+void VideoTexture::initNativeWindowIfNeeded()
+{
+ {
+ android::Mutex::Autolock lock(m_videoLock);
+
+ if(!m_newWindowRequest)
+ return;
+
+ // reuse an existing texture if possible
+ if (!m_textureId)
+ glGenTextures(1, &m_textureId);
+
+ m_surfaceTexture = new android::SurfaceTexture(m_textureId);
+ m_surfaceTextureClient = new android::SurfaceTextureClient(m_surfaceTexture);
+
+ //setup callback
+ m_videoListener->resetFrameAvailable();
+ m_surfaceTexture->setFrameAvailableListener(m_videoListener);
+
+ m_newWindowRequest = false;
+ m_newWindowReady = true;
+ }
+ m_newVideoRequestCond.signal();
+}
+
+void VideoTexture::drawVideo(const TransformationMatrix& matrix, const SkRect& parentBounds)
+{
+ android::Mutex::Autolock lock(m_videoLock);
+
+ if(!m_surfaceTexture.get() || m_dimensions.isEmpty()
+ || !m_videoListener->isFrameAvailable())
+ return;
+
+ m_surfaceTexture->updateTexImage();
+
+ float surfaceMatrix[16];
+ m_surfaceTexture->getTransformMatrix(surfaceMatrix);
+
+ SkRect dimensions = m_dimensions;
+ dimensions.offset(parentBounds.fLeft, parentBounds.fTop);
+
+#ifdef DEBUG
+ if (!parentBounds.contains(dimensions)) {
+ XLOG("The video exceeds is parent's bounds.");
+ }
+#endif // DEBUG
+
+ TilesManager::instance()->shader()->drawVideoLayerQuad(matrix, surfaceMatrix,
+ dimensions, m_textureId);
+}
+
+ANativeWindow* VideoTexture::requestNewWindow()
+{
+ android::Mutex::Autolock lock(m_videoLock);
+
+ // the window was not ready before the timeout so return it this time
+ if (m_newWindowReady) {
+ m_newWindowReady = false;
+ return m_surfaceTextureClient.get();
+ }
+ // we only allow for one texture, so if one already exists return null
+ else if (m_surfaceTextureClient.get()) {
+ return 0;
+ }
+
+ m_newWindowRequest = true;
+
+ // post an inval message to the UI thread to fulfill the request
+ if (m_weakWebViewRef) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
+ if (localWebViewRef) {
+ jclass wvClass = env->GetObjectClass(localWebViewRef);
+ jmethodID postInvalMethod = env->GetMethodID(wvClass, "postInvalidate", "()V");
+ env->CallVoidMethod(localWebViewRef, postInvalMethod);
+ env->DeleteLocalRef(wvClass);
+ env->DeleteLocalRef(localWebViewRef);
+ }
+ checkException(env);
+ }
+
+ //block until the request can be fulfilled or we time out
+ bool timedOut = false;
+ while (m_newWindowRequest && !timedOut) {
+ int ret = m_newVideoRequestCond.waitRelative(m_videoLock, 500000000); // .5 sec
+ timedOut = ret == TIMED_OUT;
+ }
+
+ if (m_surfaceTextureClient.get())
+ m_newWindowReady = false;
+
+ return m_surfaceTextureClient.get();
+}
+
+ANativeWindow* VideoTexture::getNativeWindow()
+{
+ android::Mutex::Autolock lock(m_videoLock);
+ return m_surfaceTextureClient.get();
+}
+
+void VideoTexture::releaseNativeWindow()
+{
+ android::Mutex::Autolock lock(m_videoLock);
+ m_dimensions.setEmpty();
+
+ if (m_surfaceTexture.get())
+ m_surfaceTexture->setFrameAvailableListener(0);
+
+ // clear the strong pointer references
+ m_surfaceTextureClient.clear();
+ m_surfaceTexture.clear();
+}
+
+void VideoTexture::setDimensions(const SkRect& dimensions)
+{
+ android::Mutex::Autolock lock(m_videoLock);
+ m_dimensions = dimensions;
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/MediaTexture.h b/Source/WebCore/platform/graphics/android/MediaTexture.h
new file mode 100644
index 0000000..722afd8
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/MediaTexture.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MediaTexture_h
+#define MediaTexture_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "RefPtr.h"
+#include "DoubleBufferedTexture.h"
+#include "LayerAndroid.h"
+#include <utils/RefBase.h>
+#include <jni.h>
+
+namespace android {
+ class SurfaceTexture;
+}
+
+namespace WebCore {
+
+class VideoListener;
+
+class MediaTexture : public DoubleBufferedTexture {
+
+public:
+ MediaTexture(EGLContext sharedContext);
+
+ void producerInc();
+ void producerDec();
+ void consumerInc();
+ void consumerDec();
+
+ int getProducerCount() { android::Mutex::Autolock lock(m_mediaLock); return m_producerRefCount; }
+ int getConsumerCount() { android::Mutex::Autolock lock(m_mediaLock); return m_consumerRefCount; }
+
+private:
+ android::Mutex m_mediaLock;
+ int m_producerRefCount;
+ int m_consumerRefCount;
+};
+
+class VideoTexture : public android::LightRefBase<VideoTexture> {
+
+public:
+ VideoTexture(jobject weakWebViewRef);
+ ~VideoTexture();
+
+ void initNativeWindowIfNeeded();
+ void drawVideo(const TransformationMatrix& matrix, const SkRect& parentBounds);
+
+ ANativeWindow* requestNewWindow();
+ ANativeWindow* getNativeWindow();
+ void releaseNativeWindow();
+ void setDimensions(const SkRect& dimensions);
+
+
+private:
+ GLuint m_textureId;
+ sp<android::SurfaceTexture> m_surfaceTexture;
+ sp<ANativeWindow> m_surfaceTextureClient;
+ sp<VideoListener> m_videoListener;
+ SkRect m_dimensions;
+ bool m_newWindowRequest;
+ bool m_newWindowReady;
+
+ jobject m_weakWebViewRef;
+
+ android::Mutex m_videoLock;
+ android::Condition m_newVideoRequestCond;
+};
+
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif // MediaTexture_h
diff --git a/Source/WebCore/platform/graphics/android/PaintLayerOperation.cpp b/Source/WebCore/platform/graphics/android/PaintLayerOperation.cpp
new file mode 100644
index 0000000..35867c7
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/PaintLayerOperation.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "PaintLayerOperation.h"
+
+#include "LayerAndroid.h"
+
+bool PaintLayerOperation::operator==(const QueuedOperation* operation)
+{
+ if (operation->type() != type())
+ return false;
+ const PaintLayerOperation* op = static_cast<const PaintLayerOperation*>(operation);
+ return op->m_layer->uniqueId() == m_layer->uniqueId();
+}
+
+void PaintLayerOperation::run()
+{
+ if (m_layer)
+ m_layer->paintBitmapGL();
+}
+
+SkLayer* PaintLayerOperation::baseLayer()
+{
+ if (!m_layer)
+ return 0;
+
+ return m_layer->getRootLayer();
+}
+
+LayerTexture* PaintLayerOperation::texture()
+{
+ if (!m_layer)
+ return 0;
+ return m_layer->texture();
+}
+
+bool PaintLayerBaseFilter::check(QueuedOperation* operation)
+{
+ if (operation->type() == QueuedOperation::PaintLayer) {
+ PaintLayerOperation* op = static_cast<PaintLayerOperation*>(operation);
+ if (op->baseLayer() == m_baseLayer)
+ return true;
+ }
+ return false;
+}
+
+bool PaintLayerTextureFilter::check(QueuedOperation* operation)
+{
+ if (operation->type() == QueuedOperation::PaintLayer) {
+ PaintLayerOperation* op = static_cast<PaintLayerOperation*>(operation);
+ if (op->texture() == m_texture)
+ return true;
+ }
+ return false;
+}
diff --git a/Source/WebCore/platform/graphics/android/PaintLayerOperation.h b/Source/WebCore/platform/graphics/android/PaintLayerOperation.h
new file mode 100644
index 0000000..74e87af
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/PaintLayerOperation.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 PaintLayerOperation_h
+#define PaintLayerOperation_h
+
+#include "QueuedOperation.h"
+
+class SkLayer;
+
+namespace WebCore {
+
+class LayerAndroid;
+class LayerTexture;
+
+class PaintLayerOperation : public QueuedOperation {
+ public:
+ PaintLayerOperation(LayerAndroid* layer)
+ : QueuedOperation(QueuedOperation::PaintLayer, 0)
+ , m_layer(layer) {}
+ virtual ~PaintLayerOperation() {}
+ virtual bool operator==(const QueuedOperation* operation);
+ virtual void run();
+ SkLayer* baseLayer();
+ LayerAndroid* layer() { return m_layer; }
+ LayerTexture* texture();
+
+ private:
+ LayerAndroid* m_layer;
+};
+
+class PaintLayerBaseFilter : public OperationFilter {
+ public:
+ PaintLayerBaseFilter(SkLayer* layer) : m_baseLayer(layer) {}
+ virtual bool check(QueuedOperation* operation);
+
+ private:
+ SkLayer* m_baseLayer;
+};
+
+class PaintLayerTextureFilter : public OperationFilter {
+ public:
+ PaintLayerTextureFilter(LayerTexture* texture) : m_texture(texture) {}
+ virtual bool check(QueuedOperation* operation);
+
+ private:
+ LayerTexture* m_texture;
+};
+
+}
+
+#endif // PaintLayerOperation_h
diff --git a/Source/WebCore/platform/graphics/android/PaintTileOperation.cpp b/Source/WebCore/platform/graphics/android/PaintTileOperation.cpp
new file mode 100644
index 0000000..222b69b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/PaintTileOperation.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "PaintTileOperation.h"
+
+namespace WebCore {
+
+PaintTileOperation::PaintTileOperation(BaseTile* tile)
+ : QueuedOperation(QueuedOperation::PaintTile, tile->page())
+ , m_tile(tile)
+{
+ if (m_tile)
+ m_tile->setRepaintPending(true);
+}
+
+PaintTileOperation::~PaintTileOperation()
+{
+ if (m_tile) {
+ m_tile->setRepaintPending(false);
+ m_tile = 0;
+ }
+}
+
+bool PaintTileOperation::operator==(const QueuedOperation* operation)
+{
+ if (operation->type() != type())
+ return false;
+ const PaintTileOperation* op = static_cast<const PaintTileOperation*>(operation);
+ return op->m_tile == m_tile;
+}
+
+void PaintTileOperation::run()
+{
+ if (m_tile) {
+ m_tile->paintBitmap();
+ m_tile->setRepaintPending(false);
+ m_tile = 0;
+ }
+}
+
+int PaintTileOperation::priority()
+{
+ if (!m_tile || m_tile->usedLevel() < 0)
+ return -1;
+ bool goingDown = m_tile->page()->scrollingDown();
+ SkIRect *rect = m_tile->page()->expandedTileBounds();
+ int firstTileX = rect->fLeft;
+ int nbTilesWidth = rect->width();
+ int priority = m_tile->x() - firstTileX;
+ if (goingDown)
+ priority += (rect->fBottom - m_tile->y()) * nbTilesWidth;
+ else
+ priority += (m_tile->y() - rect->fTop) * nbTilesWidth;
+ priority += m_tile->usedLevel() * 100000;
+ return priority;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/PaintTileOperation.h b/Source/WebCore/platform/graphics/android/PaintTileOperation.h
new file mode 100644
index 0000000..0920f32
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/PaintTileOperation.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 PaintTileSetOperation_h
+#define PaintTileSetOperation_h
+
+#include "QueuedOperation.h"
+
+namespace WebCore {
+
+class PaintTileOperation : public QueuedOperation {
+ public:
+ PaintTileOperation(BaseTile* tile);
+ virtual ~PaintTileOperation();
+ virtual bool operator==(const QueuedOperation* operation);
+ virtual void run();
+ virtual int priority();
+
+ private:
+ BaseTile* m_tile;
+};
+
+}
+
+#endif // PaintTileSetOperation_h
diff --git a/Source/WebCore/platform/graphics/android/PathAndroid.cpp b/Source/WebCore/platform/graphics/android/PathAndroid.cpp
new file mode 100644
index 0000000..e0d7171
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/PathAndroid.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Path.h"
+
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "ImageBuffer.h"
+#include "PlatformGraphicsContext.h"
+#include "SkiaUtils.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "StrokeStyleApplier.h"
+#include "TransformationMatrix.h"
+#include "android_graphics.h"
+
+namespace WebCore {
+
+Path::Path()
+{
+ m_path = new SkPath;
+// m_path->setFlags(SkPath::kWinding_FillType);
+}
+
+Path::Path(const Path& other)
+{
+ m_path = new SkPath(*other.m_path);
+}
+
+Path::~Path()
+{
+ delete m_path;
+}
+
+Path& Path::operator=(const Path& other)
+{
+ *m_path = *other.m_path;
+ return *this;
+}
+
+bool Path::isEmpty() const
+{
+ return m_path->isEmpty();
+}
+
+bool Path::hasCurrentPoint() const
+{
+ // webkit wants to know if we have any points, including any moveTos.
+ // Skia's empty() will return true if it has just a moveTo, so we need to
+ // call getPoints(NULL), which returns the number of points,
+ // including moveTo.
+ return m_path->getPoints(0, 0) > 0;
+}
+
+FloatPoint Path::currentPoint() const
+{
+ if (hasCurrentPoint()) {
+ SkPoint lastPt;
+ m_path->getLastPt(&lastPt);
+ return lastPt;
+ }
+ float quietNaN = std::numeric_limits<float>::quiet_NaN();
+ return FloatPoint(quietNaN, quietNaN);
+}
+
+bool Path::contains(const FloatPoint& point, WindRule rule) const
+{
+ SkRegion rgn, clip;
+
+ int x = (int)floorf(point.x());
+ int y = (int)floorf(point.y());
+ clip.setRect(x, y, x + 1, y + 1);
+
+ SkPath::FillType ft = m_path->getFillType(); // save
+ m_path->setFillType(rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
+
+ bool contains = rgn.setPath(*m_path, clip);
+
+ m_path->setFillType(ft); // restore
+ return contains;
+}
+
+void Path::translate(const FloatSize& size)
+{
+ m_path->offset(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
+}
+
+FloatRect Path::boundingRect() const
+{
+ const SkRect& r = m_path->getBounds();
+ return FloatRect( SkScalarToFloat(r.fLeft),
+ SkScalarToFloat(r.fTop),
+ SkScalarToFloat(r.width()),
+ SkScalarToFloat(r.height()));
+}
+
+void Path::moveTo(const FloatPoint& point)
+{
+ m_path->moveTo(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()));
+}
+
+void Path::addLineTo(const FloatPoint& p)
+{
+ m_path->lineTo(SkFloatToScalar(p.x()), SkFloatToScalar(p.y()));
+}
+
+void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
+{
+ m_path->quadTo( SkFloatToScalar(cp.x()), SkFloatToScalar(cp.y()),
+ SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y()));
+}
+
+void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
+{
+ m_path->cubicTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()),
+ SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()),
+ SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y()));
+}
+
+void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
+{
+ m_path->arcTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()),
+ SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()),
+ SkFloatToScalar(radius));
+}
+
+void Path::closeSubpath()
+{
+ m_path->close();
+}
+
+static const float gPI = 3.14159265f;
+static const float g2PI = 6.28318531f;
+static const float g180OverPI = 57.29577951308f;
+
+static float fast_mod(float angle, float max) {
+ if (angle >= max || angle <= -max) {
+ angle = fmodf(angle, max);
+ }
+ return angle;
+}
+
+void Path::addArc(const FloatPoint& p, float r, float sa, float ea,
+ bool clockwise) {
+ SkScalar cx = SkFloatToScalar(p.x());
+ SkScalar cy = SkFloatToScalar(p.y());
+ SkScalar radius = SkFloatToScalar(r);
+
+ SkRect oval;
+ oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
+
+ float sweep = ea - sa;
+ bool prependOval = false;
+
+ /* Note if clockwise and the sign of the sweep disagree. This particular
+ logic was deduced from http://canvex.lazyilluminati.com/misc/arc.html
+ */
+ if (clockwise && (sweep > 0 || sweep < -g2PI)) {
+ sweep = fmodf(sweep, g2PI) - g2PI;
+ } else if (!clockwise && (sweep < 0 || sweep > g2PI)) {
+ sweep = fmodf(sweep, g2PI) + g2PI;
+ }
+
+ // If the abs(sweep) >= 2PI, then we need to add a circle before we call
+ // arcTo, since it treats the sweep mod 2PI. We don't have a prepend call,
+ // so we just remember this, and at the end create a new path with an oval
+ // and our current path, and then swap then.
+ //
+ if (sweep >= g2PI || sweep <= -g2PI) {
+ prependOval = true;
+// SkDebugf("addArc sa=%g ea=%g cw=%d sweep %g treat as circle\n", sa, ea, clockwise, sweep);
+
+ // now reduce sweep to just the amount we need, so that the current
+ // point is left where the caller expects it.
+ sweep = fmodf(sweep, g2PI);
+ }
+
+ sa = fast_mod(sa, g2PI);
+ SkScalar startDegrees = SkFloatToScalar(sa * g180OverPI);
+ SkScalar sweepDegrees = SkFloatToScalar(sweep * g180OverPI);
+
+// SkDebugf("addArc sa=%g ea=%g cw=%d sweep=%g ssweep=%g\n", sa, ea, clockwise, sweep, SkScalarToFloat(sweepDegrees));
+ m_path->arcTo(oval, startDegrees, sweepDegrees, false);
+
+ if (prependOval) {
+ SkPath tmp;
+ tmp.addOval(oval);
+ tmp.addPath(*m_path);
+ m_path->swap(tmp);
+ }
+}
+
+void Path::addRect(const FloatRect& rect)
+{
+ m_path->addRect(rect);
+}
+
+void Path::addEllipse(const FloatRect& rect)
+{
+ m_path->addOval(rect);
+}
+
+void Path::clear()
+{
+ m_path->reset();
+}
+
+static FloatPoint* setfpts(FloatPoint dst[], const SkPoint src[], int count)
+{
+ for (int i = 0; i < count; i++)
+ {
+ dst[i].setX(SkScalarToFloat(src[i].fX));
+ dst[i].setY(SkScalarToFloat(src[i].fY));
+ }
+ return dst;
+}
+
+void Path::apply(void* info, PathApplierFunction function) const
+{
+ SkPath::Iter iter(*m_path, false);
+ SkPoint pts[4];
+
+ PathElement elem;
+ FloatPoint fpts[3];
+
+ for (;;)
+ {
+ switch (iter.next(pts)) {
+ case SkPath::kMove_Verb:
+ elem.type = PathElementMoveToPoint;
+ elem.points = setfpts(fpts, &pts[0], 1);
+ break;
+ case SkPath::kLine_Verb:
+ elem.type = PathElementAddLineToPoint;
+ elem.points = setfpts(fpts, &pts[1], 1);
+ break;
+ case SkPath::kQuad_Verb:
+ elem.type = PathElementAddQuadCurveToPoint;
+ elem.points = setfpts(fpts, &pts[1], 2);
+ break;
+ case SkPath::kCubic_Verb:
+ elem.type = PathElementAddCurveToPoint;
+ elem.points = setfpts(fpts, &pts[1], 3);
+ break;
+ case SkPath::kClose_Verb:
+ elem.type = PathElementCloseSubpath;
+ elem.points = setfpts(fpts, 0, 0);
+ break;
+ case SkPath::kDone_Verb:
+ return;
+ }
+ function(info, &elem);
+ }
+}
+
+void Path::transform(const AffineTransform& xform)
+{
+ m_path->transform(xform);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Computes the bounding box for the stroke and style currently selected into
+// the given bounding box. This also takes into account the stroke width.
+static FloatRect boundingBoxForCurrentStroke(GraphicsContext* context)
+{
+ const SkPath* path = context->getCurrPath();
+ if (NULL == path) {
+ return FloatRect();
+ }
+
+ SkPaint paint;
+ context->setupStrokePaint(&paint);
+ SkPath fillPath;
+ paint.getFillPath(*path, &fillPath);
+ const SkRect& r = fillPath.getBounds();
+ return FloatRect(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop),
+ SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
+}
+
+FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
+{
+ GraphicsContext* scratch = scratchContext();
+ scratch->save();
+ scratch->beginPath();
+ scratch->addPath(*this);
+
+ if (applier)
+ applier->strokeStyle(scratch);
+
+ FloatRect r = boundingBoxForCurrentStroke(scratch);
+ scratch->restore();
+ return r;
+}
+
+#if ENABLE(SVG)
+bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
+{
+ GraphicsContext* scratch = scratchContext();
+ scratch->save();
+
+ applier->strokeStyle(scratch);
+
+ SkPaint paint;
+ scratch->setupStrokePaint(&paint);
+ SkPath strokePath;
+ paint.getFillPath(*platformPath(), &strokePath);
+ bool contains = SkPathContainsPoint(&strokePath, point,
+ SkPath::kWinding_FillType);
+
+ scratch->restore();
+ return contains;
+}
+#endif
+
+}
diff --git a/Source/WebCore/platform/graphics/android/PatternAndroid.cpp b/Source/WebCore/platform/graphics/android/PatternAndroid.cpp
new file mode 100644
index 0000000..568036c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/PatternAndroid.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "Pattern.h"
+
+#include "android_graphics.h"
+#include "GraphicsContext.h"
+#include "SkBitmapRef.h"
+#include "SkCanvas.h"
+#include "SkColorShader.h"
+#include "SkShader.h"
+#include "SkPaint.h"
+
+namespace WebCore {
+
+static SkShader::TileMode toTileMode(bool doRepeat) {
+ return doRepeat ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode;
+}
+
+void Pattern::platformDestroy()
+{
+ SkSafeUnref(m_pattern);
+ m_pattern = 0;
+}
+
+SkShader* Pattern::platformPattern(const AffineTransform&)
+{
+ if (m_pattern)
+ return m_pattern;
+
+ SkBitmapRef* ref = tileImage()->nativeImageForCurrentFrame();
+ if (!ref)
+ return 0;
+ m_pattern = SkShader::CreateBitmapShader(ref->bitmap(),
+ toTileMode(m_repeatX),
+ toTileMode(m_repeatY));
+ m_pattern->setLocalMatrix(m_patternSpaceTransformation);
+ return m_pattern;
+}
+
+void Pattern::setPlatformPatternSpaceTransform()
+{
+ if (m_pattern)
+ m_pattern->setLocalMatrix(m_patternSpaceTransformation);
+}
+
+} //namespace
diff --git a/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp
new file mode 100644
index 0000000..fcdcce9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "Node.h"
+#include "PlatformGraphicsContext.h"
+#include "SkCanvas.h"
+
+namespace WebCore {
+
+PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas, WTF::Vector<Container>* buttons)
+ : mCanvas(canvas), m_deleteCanvas(false), m_buttons(buttons)
+{
+}
+
+PlatformGraphicsContext::PlatformGraphicsContext()
+ : mCanvas(new SkCanvas), m_deleteCanvas(true), m_buttons(0)
+{
+}
+
+PlatformGraphicsContext::~PlatformGraphicsContext()
+{
+ if (m_deleteCanvas) {
+// printf("-------------------- deleting offscreen canvas\n");
+ delete mCanvas;
+ }
+}
+
+void PlatformGraphicsContext::storeButtonInfo(Node* node, const IntRect& r)
+{
+ if (m_buttons == NULL)
+ return;
+ // Check to see if we already have a Container for this node. If so, update
+ // it with the new rectangle and make the new recording canvas reference
+ // its picture.
+ Container* end = m_buttons->end();
+ for (Container* ptr = m_buttons->begin(); ptr != end; ptr++) {
+ if (ptr->matches(node)) {
+ mCanvas->drawPicture(*(ptr->picture()));
+ ptr->setRect(r);
+ return;
+ }
+ }
+ // We did not have a Container representing this node, so create a new one.
+ Container container(node, r);
+ // Place a reference to our subpicture in the Picture.
+ mCanvas->drawPicture(*(container.picture()));
+ // Keep track of the information about the button.
+ m_buttons->append(container);
+}
+
+} // WebCore
diff --git a/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h
new file mode 100644
index 0000000..0ce86d2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 platform_graphics_context_h
+#define platform_graphics_context_h
+
+#include "IntRect.h"
+#include "RenderSkinAndroid.h"
+#include "RenderSkinButton.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkTDArray.h"
+
+class SkCanvas;
+
+class Container {
+public:
+ Container(WebCore::Node* node, const WebCore::IntRect& r)
+ : m_node(node), m_rect(r), m_state(WebCore::RenderSkinAndroid::kDisabled)
+ {
+ m_picture = new SkPicture;
+ }
+
+ ~Container()
+ {
+ m_picture->unref();
+ }
+
+ Container& operator=(const Container& src)
+ {
+ if (this != &src) {
+ m_node = src.m_node;
+ if (m_picture)
+ m_picture->unref();
+ m_picture = src.m_picture;
+ m_picture->ref();
+ m_rect = src.m_rect;
+ m_state = WebCore::RenderSkinAndroid::kDisabled;
+ }
+ return *this;
+ }
+
+ Container(const Container& src)
+ {
+ m_node = src.m_node;
+ m_picture = src.m_picture;
+ m_picture->ref();
+ m_rect = src.m_rect;
+ m_state = WebCore::RenderSkinAndroid::kDisabled;
+ }
+
+ // m_picture has a ref count of 1 to begin with. It will increase each time
+ // m_picture is referenced by another picture. When the other pictures are
+ // deleted, the ref count gets decremented. If the ref count is one, then
+ // no other pictures reference this one, so the button is no longer being
+ // used, and therefore can be removed.
+ bool canBeRemoved()
+ {
+ return m_picture->getRefCnt() == 1;
+ }
+
+ bool matches(const WebCore::Node* match) { return m_node == match; }
+
+ const WebCore::Node* node() const { return m_node; }
+
+ // Provide a pointer to our SkPicture.
+ SkPicture* picture() { return m_picture; }
+
+ WebCore::IntRect rect() { return m_rect; }
+
+ // Update the rectangle with a new rectangle, as the positioning of this
+ // button may have changed due to a new layout. If it is a new rectangle,
+ // set its state to disabled, so that it will be redrawn when we cycle
+ // through the list of buttons.
+ void setRect(WebCore::IntRect r)
+ {
+ if (m_rect != r) {
+ m_rect = r;
+ m_state = WebCore::RenderSkinAndroid::kDisabled;
+ }
+ }
+
+ // Update the focus state of this button, depending on whether it
+ // corresponds to the focused node passed in. If its state has changed,
+ // re-record to the subpicture, so the master picture will reflect the
+ // change.
+ void updateFocusState(WebCore::RenderSkinAndroid::State state,
+ const WebCore::RenderSkinButton* buttonSkin)
+ {
+ if (state == m_state)
+ return;
+ // If this button is being told to draw focused, but it is already in a
+ // pressed state, leave it in the pressed state, to show that it is
+ // being followed.
+ if (m_state == WebCore::RenderSkinAndroid::kPressed &&
+ state == WebCore::RenderSkinAndroid::kFocused)
+ return;
+ m_state = state;
+ SkCanvas* canvas = m_picture->beginRecording(m_rect.right(), m_rect.bottom());
+ buttonSkin->draw(canvas, m_rect, state);
+ m_picture->endRecording();
+ }
+private:
+ // Only used for comparison, since after it is stored it will be transferred
+ // to the UI thread.
+ WebCore::Node* m_node;
+ // The rectangle representing the bounds of the button.
+ WebCore::IntRect m_rect;
+ // An SkPicture that, thanks to storeButtonInfo, is pointed to by the master
+ // picture, so that we can rerecord this button without rerecording the
+ // world.
+ SkPicture* m_picture;
+ // The state of the button - Currently kFocused or kNormal (and kDisabled
+ // as an initial value), but could be expanded to include other states.
+ WebCore::RenderSkinAndroid::State m_state;
+};
+
+namespace WebCore {
+
+ class GraphicsContext;
+
+class PlatformGraphicsContext {
+public:
+ PlatformGraphicsContext();
+ // Pass in a recording canvas, and an array of button information to be
+ // updated.
+ PlatformGraphicsContext(SkCanvas* canvas, WTF::Vector<Container>* buttons);
+ ~PlatformGraphicsContext();
+
+ SkCanvas* mCanvas;
+
+ bool deleteUs() const { return m_deleteCanvas; }
+ // If our graphicscontext has a button list, add a new container for the
+ // nod/rect, and record a new subpicture for this node/button in the current
+ // mCanvas
+ void storeButtonInfo(Node* node, const IntRect& r);
+private:
+ bool m_deleteCanvas;
+ WTF::Vector<Container>* m_buttons;
+};
+
+}
+#endif
diff --git a/Source/WebCore/platform/graphics/android/QueuedOperation.h b/Source/WebCore/platform/graphics/android/QueuedOperation.h
new file mode 100644
index 0000000..98f3e2f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/QueuedOperation.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 QueuedOperation_h
+#define QueuedOperation_h
+
+#include "TiledPage.h"
+
+namespace WebCore {
+
+class QueuedOperation {
+ public:
+ enum OperationType { Undefined, PaintTile, PaintLayer, DeleteTexture };
+ QueuedOperation(OperationType type, TiledPage* page)
+ : m_type(type)
+ , m_page(page) {}
+ virtual ~QueuedOperation() {}
+ virtual void run() = 0;
+ virtual bool operator==(const QueuedOperation* operation) = 0;
+ virtual int priority() { return -1; }
+ OperationType type() const { return m_type; }
+ TiledPage* page() const { return m_page; }
+ private:
+ OperationType m_type;
+ TiledPage* m_page;
+};
+
+class OperationFilter {
+ public:
+ virtual ~OperationFilter() {}
+ virtual bool check(QueuedOperation* operation) = 0;
+};
+
+class PageFilter : public OperationFilter {
+ public:
+ PageFilter(TiledPage* page) : m_page(page) {}
+ virtual bool check(QueuedOperation* operation)
+ {
+ if (operation->page() == m_page)
+ return true;
+ return false;
+ }
+ private:
+ TiledPage* m_page;
+};
+
+class PagePaintFilter : public OperationFilter {
+ public:
+ PagePaintFilter(TiledPage* page) : m_page(page) {}
+ virtual bool check(QueuedOperation* operation)
+ {
+ if (operation->type() == QueuedOperation::PaintTile
+ && operation->page() == m_page)
+ return true;
+ return false;
+ }
+ private:
+ TiledPage* m_page;
+};
+
+}
+
+#endif // QueuedOperation_h
diff --git a/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp
new file mode 100644
index 0000000..ca8f03c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp
@@ -0,0 +1,39 @@
+#include "config.h"
+#include "ScrollableLayerAndroid.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+namespace WebCore {
+
+bool ScrollableLayerAndroid::scrollTo(int x, int y)
+{
+ SkIRect scrollBounds;
+ getScrollRect(&scrollBounds);
+ if (!scrollBounds.fRight && !scrollBounds.fBottom)
+ return false;
+
+ SkScalar newX = SkScalarPin(x, 0, scrollBounds.fRight);
+ SkScalar newY = SkScalarPin(y, 0, scrollBounds.fBottom);
+ // Check for no change.
+ if (newX == scrollBounds.fLeft && newY == scrollBounds.fTop)
+ return false;
+
+ SkScalar diffX = newX - scrollBounds.fLeft;
+ SkScalar diffY = newY - scrollBounds.fTop;
+ const SkPoint& pos = getPosition();
+ setPosition(pos.fX - diffX, pos.fY - diffY);
+ return true;
+}
+
+void ScrollableLayerAndroid::getScrollRect(SkIRect* out) const
+{
+ const SkPoint& pos = getPosition();
+ out->fLeft = m_scrollLimits.fLeft - pos.fX;
+ out->fTop = m_scrollLimits.fTop - pos.fY;
+ out->fRight = getSize().width() - m_scrollLimits.width();
+ out->fBottom = getSize().height() - m_scrollLimits.height();
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h b/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h
new file mode 100644
index 0000000..b23f056
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ScrollableLayerAndroid_h
+#define ScrollableLayerAndroid_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerAndroid.h"
+
+namespace WebCore {
+
+class ScrollableLayerAndroid : public LayerAndroid {
+
+public:
+ ScrollableLayerAndroid(RenderLayer* owner)
+ : LayerAndroid(owner) {}
+ ScrollableLayerAndroid(const ScrollableLayerAndroid& layer)
+ : LayerAndroid(layer)
+ , m_scrollLimits(layer.m_scrollLimits) {}
+ ScrollableLayerAndroid(const LayerAndroid& layer)
+ : LayerAndroid(layer)
+ {
+ m_scrollLimits.setEmpty();
+ }
+ virtual ~ScrollableLayerAndroid() {};
+
+ virtual bool contentIsScrollable() const { return true; }
+
+ virtual LayerAndroid* copy() const { return new ScrollableLayerAndroid(*this); }
+
+ // Returns true if the content position has changed.
+ bool scrollTo(int dx, int dy);
+
+ // Fills the rect with the current scroll offset and the maximum scroll.
+ // fLeft = scrollX
+ // fTop = scrollY
+ // fRight = maxX
+ // fBottom = maxY
+ void getScrollRect(SkIRect* out) const;
+
+ void setScrollLimits(float x, float y, float width, float height)
+ {
+ m_scrollLimits.set(x, y, x + width, y + height);
+ }
+
+private:
+ SkRect m_scrollLimits;
+};
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif // LayerAndroid_h
diff --git a/Source/WebCore/platform/graphics/android/ShaderProgram.cpp b/Source/WebCore/platform/graphics/android/ShaderProgram.cpp
new file mode 100644
index 0000000..6933890
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ShaderProgram.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "ShaderProgram.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FloatPoint3D.h"
+#include "GLUtils.h"
+
+#include <GLES2/gl2.h>
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "ShaderProgram", __VA_ARGS__)
+
+namespace WebCore {
+
+static const char gVertexShader[] =
+ "attribute vec4 vPosition;\n"
+ "uniform mat4 projectionMatrix;\n"
+ "varying vec2 v_texCoord;\n"
+ "void main() {\n"
+ " gl_Position = projectionMatrix * vPosition;\n"
+ " v_texCoord = vec2(vPosition);\n"
+ "}\n";
+
+static const char gFragmentShader[] =
+ "precision mediump float;\n"
+ "varying vec2 v_texCoord; \n"
+ "uniform float alpha; \n"
+ "uniform sampler2D s_texture; \n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
+ " gl_FragColor *= alpha; "
+ "}\n";
+
+static const char gVideoVertexShader[] =
+ "attribute vec4 vPosition;\n"
+ "uniform mat4 textureMatrix;\n"
+ "uniform mat4 projectionMatrix;\n"
+ "varying vec2 v_texCoord;\n"
+ "void main() {\n"
+ " gl_Position = projectionMatrix * vPosition;\n"
+ " v_texCoord = vec2(textureMatrix * vec4(vPosition.x, 1.0 - vPosition.y, 0.0, 1.0));\n"
+ "}\n";
+
+static const char gVideoFragmentShader[] =
+ "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES s_yuvTexture;\n"
+ "varying vec2 v_texCoord;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(s_yuvTexture, v_texCoord);\n"
+ "}\n";
+
+GLuint ShaderProgram::loadShader(GLenum shaderType, const char* pSource)
+{
+ GLuint shader = glCreateShader(shaderType);
+ if (shader) {
+ glShaderSource(shader, 1, &pSource, 0);
+ glCompileShader(shader);
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen) {
+ char* buf = (char*) malloc(infoLen);
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, 0, buf);
+ XLOG("could not compile shader %d:\n%s\n", shaderType, buf);
+ free(buf);
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ }
+ return shader;
+}
+
+GLuint ShaderProgram::createProgram(const char* pVertexSource, const char* pFragmentSource)
+{
+ GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+ if (!vertexShader) {
+ XLOG("couldn't load the vertex shader!");
+ return -1;
+ }
+
+ GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+ if (!pixelShader) {
+ XLOG("couldn't load the pixel shader!");
+ return -1;
+ }
+
+ GLuint program = glCreateProgram();
+ if (program) {
+ glAttachShader(program, vertexShader);
+ GLUtils::checkGlError("glAttachShader vertex");
+ glAttachShader(program, pixelShader);
+ GLUtils::checkGlError("glAttachShader pixel");
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = (char*) malloc(bufLength);
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, 0, buf);
+ XLOG("could not link program:\n%s\n", buf);
+ free(buf);
+ }
+ }
+ glDeleteProgram(program);
+ program = -1;
+ }
+ }
+ return program;
+}
+
+ShaderProgram::ShaderProgram()
+ : m_blendingEnabled(false)
+{
+ init();
+}
+
+void ShaderProgram::init()
+{
+ m_program = createProgram(gVertexShader, gFragmentShader);
+ m_videoProgram = createProgram(gVideoVertexShader, gVideoFragmentShader);
+ if (m_program == -1 || m_videoProgram == -1)
+ return;
+
+ m_hProjectionMatrix = glGetUniformLocation(m_program, "projectionMatrix");
+ m_hAlpha = glGetUniformLocation(m_program, "alpha");
+ m_hTexSampler = glGetUniformLocation(m_program, "s_texture");
+
+ m_hPosition = glGetAttribLocation(m_program, "vPosition");
+
+ m_hVideoProjectionMatrix = glGetUniformLocation(m_videoProgram, "projectionMatrix");
+ m_hVideoTextureMatrix = glGetUniformLocation(m_videoProgram, "textureMatrix");
+ m_hVideoTexSampler = glGetUniformLocation(m_videoProgram, "s_yuvTexture");
+
+ m_hVideoPosition = glGetAttribLocation(m_program, "vPosition");
+
+ const GLfloat coord[] = {
+ 0.0f, 0.0f, // C
+ 1.0f, 0.0f, // D
+ 0.0f, 1.0f, // A
+ 1.0f, 1.0f // B
+ };
+
+ glGenBuffers(1, m_textureBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
+ glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), coord, GL_STATIC_DRAW);
+}
+
+void ShaderProgram::resetBlending()
+{
+ glDisable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendEquation(GL_FUNC_ADD);
+ m_blendingEnabled = false;
+}
+
+void ShaderProgram::setBlendingState(bool enableBlending)
+{
+ if (enableBlending == m_blendingEnabled)
+ return;
+
+ if (enableBlending)
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+
+ m_blendingEnabled = enableBlending;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Drawing
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void ShaderProgram::setViewport(SkRect& viewport)
+{
+ TransformationMatrix ortho;
+ GLUtils::setOrthographicMatrix(ortho, viewport.fLeft, viewport.fTop,
+ viewport.fRight, viewport.fBottom, -1000, 1000);
+ m_projectionMatrix = ortho;
+ m_viewport = viewport;
+}
+
+void ShaderProgram::setProjectionMatrix(SkRect& geometry)
+{
+ TransformationMatrix translate;
+ translate.translate3d(geometry.fLeft, geometry.fTop, 0.0);
+ TransformationMatrix scale;
+ scale.scale3d(geometry.width(), geometry.height(), 1.0);
+
+ TransformationMatrix total = m_projectionMatrix;
+ total.multLeft(translate);
+ total.multLeft(scale);
+
+ GLfloat projectionMatrix[16];
+ GLUtils::toGLMatrix(projectionMatrix, total);
+ glUniformMatrix4fv(m_hProjectionMatrix, 1, GL_FALSE, projectionMatrix);
+}
+
+void ShaderProgram::drawQuad(SkRect& geometry, int textureId, float opacity)
+{
+ setProjectionMatrix(geometry);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
+ glEnableVertexAttribArray(m_hPosition);
+ glVertexAttribPointer(m_hPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ glUniform1f(alpha(), opacity);
+
+ setBlendingState(opacity < 1.0);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ GLUtils::checkGlError("drawQuad");
+}
+
+void ShaderProgram::setViewRect(const IntRect& viewRect)
+{
+ m_viewRect = viewRect;
+
+ // We do clipping using glScissor, which needs to take
+ // coordinates in screen space. The following matrix transform
+ // content coordinates in screen coordinates.
+ TransformationMatrix translate;
+ translate.translate(1.0, 1.0);
+
+ TransformationMatrix scale;
+ scale.scale3d(m_viewRect.width() * 0.5f, m_viewRect.height() * 0.5f, 1);
+
+ m_documentToScreenMatrix = m_projectionMatrix;
+ m_documentToScreenMatrix.multiply(translate);
+ m_documentToScreenMatrix.multiply(scale);
+
+ m_documentToInvScreenMatrix = m_projectionMatrix;
+ translate.scale3d(1, -1, 1);
+ m_documentToInvScreenMatrix.multiply(translate);
+ m_documentToInvScreenMatrix.multiply(scale);
+}
+
+// This function transform a clip rect extracted from the current layer
+// into a clip rect in screen coordinates -- used by the clipping rects
+FloatRect ShaderProgram::rectInScreenCoord(const TransformationMatrix& drawMatrix, const IntSize& size)
+{
+ FloatRect srect(0, 0, size.width(), size.height());
+ TransformationMatrix renderMatrix = drawMatrix;
+ renderMatrix.multiply(m_documentToScreenMatrix);
+ return renderMatrix.mapRect(srect);
+}
+
+// used by the partial screen invals
+FloatRect ShaderProgram::rectInInvScreenCoord(const TransformationMatrix& drawMatrix, const IntSize& size)
+{
+ FloatRect srect(0, 0, size.width(), size.height());
+ TransformationMatrix renderMatrix = drawMatrix;
+ renderMatrix.multiply(m_documentToInvScreenMatrix);
+ return renderMatrix.mapRect(srect);
+}
+
+FloatRect ShaderProgram::rectInInvScreenCoord(const FloatRect& rect)
+{
+ return m_documentToInvScreenMatrix.mapRect(rect);
+}
+
+FloatRect ShaderProgram::rectInScreenCoord(const FloatRect& rect)
+{
+ return m_documentToScreenMatrix.mapRect(rect);
+}
+
+FloatRect ShaderProgram::convertInvScreenCoordToScreenCoord(const FloatRect& rect)
+{
+ FloatRect documentRect = m_documentToInvScreenMatrix.inverse().mapRect(rect);
+ return rectInScreenCoord(documentRect);
+}
+
+FloatRect ShaderProgram::convertScreenCoordToInvScreenCoord(const FloatRect& rect)
+{
+ FloatRect documentRect = m_documentToScreenMatrix.inverse().mapRect(rect);
+ return rectInInvScreenCoord(documentRect);
+}
+
+void ShaderProgram::setScreenClip(const IntRect& clip)
+{
+ m_screenClip = clip;
+ IntRect mclip = clip;
+
+ // the clip from frameworks is in full screen coordinates
+ mclip.setY(clip.y() - m_webViewRect.y() - m_titleBarHeight);
+ FloatRect tclip = convertInvScreenCoordToScreenCoord(mclip);
+ IntRect screenClip(tclip.x(), tclip.y(), tclip.width(), tclip.height());
+ m_screenClip = screenClip;
+}
+
+// clip is in screen coordinates
+void ShaderProgram::clip(const FloatRect& clip)
+{
+ if (clip == m_clipRect)
+ return;
+
+ if (clip.width() == 0 && clip.height() == 0)
+ return;
+
+ // we should only call glScissor in this function, so that we can easily
+ // track the current clipping rect.
+
+ IntRect screenClip(clip.x(),
+ clip.y(),
+ clip.width(), clip.height());
+
+ if (!m_screenClip.isEmpty())
+ screenClip.intersect(m_screenClip);
+
+ screenClip.setY(screenClip.y() + m_viewRect.y());
+ if (screenClip.x() < 0) {
+ int w = screenClip.width();
+ w += screenClip.x();
+ screenClip.setX(0);
+ screenClip.setWidth(w);
+ }
+ if (screenClip.y() < 0) {
+ int h = screenClip.height();
+ h += screenClip.y();
+ screenClip.setY(0);
+ screenClip.setHeight(h);
+ }
+
+ glScissor(screenClip.x(), screenClip.y(), screenClip.width(), screenClip.height());
+
+ m_clipRect = clip;
+}
+
+IntRect ShaderProgram::clippedRectWithViewport(const IntRect& rect, int margin)
+{
+ IntRect viewport(m_viewport.fLeft - margin, m_viewport.fTop - margin,
+ m_viewport.width() + margin, m_viewport.height() + margin);
+ viewport.intersect(rect);
+ return viewport;
+}
+
+float ShaderProgram::zValue(const TransformationMatrix& drawMatrix, float w, float h)
+{
+ TransformationMatrix renderMatrix = drawMatrix;
+ renderMatrix.scale3d(w, h, 1);
+ renderMatrix.multiply(m_projectionMatrix);
+ FloatPoint3D point(0.5, 0.5, 0.0);
+ FloatPoint3D result = renderMatrix.mapPoint(point);
+ return result.z();
+}
+
+void ShaderProgram::drawLayerQuad(const TransformationMatrix& drawMatrix,
+ SkRect& geometry, int textureId, float opacity,
+ bool forceBlending)
+{
+
+ TransformationMatrix renderMatrix = drawMatrix;
+ // move the drawing depending on where the texture is on the layer
+ renderMatrix.translate(geometry.fLeft, geometry.fTop);
+ renderMatrix.scale3d(geometry.width(), geometry.height(), 1);
+ renderMatrix.multiply(m_projectionMatrix);
+
+ GLfloat projectionMatrix[16];
+ GLUtils::toGLMatrix(projectionMatrix, renderMatrix);
+ glUniformMatrix4fv(m_hProjectionMatrix, 1, GL_FALSE, projectionMatrix);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
+ glEnableVertexAttribArray(m_hPosition);
+ glVertexAttribPointer(m_hPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ glUniform1f(alpha(), opacity);
+
+ setBlendingState(forceBlending || opacity < 1.0);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+void ShaderProgram::drawVideoLayerQuad(const TransformationMatrix& drawMatrix,
+ float* textureMatrix, SkRect& geometry,
+ int textureId)
+{
+ // switch to our custom yuv video rendering program
+ glUseProgram(m_videoProgram);
+
+ TransformationMatrix renderMatrix = drawMatrix;
+ renderMatrix.translate(geometry.fLeft, geometry.fTop);
+ renderMatrix.scale3d(geometry.width(), geometry.height(), 1);
+ renderMatrix.multiply(m_projectionMatrix);
+
+ GLfloat projectionMatrix[16];
+ GLUtils::toGLMatrix(projectionMatrix, renderMatrix);
+ glUniformMatrix4fv(m_hVideoProjectionMatrix, 1, GL_FALSE, projectionMatrix);
+ glUniformMatrix4fv(m_hVideoTextureMatrix, 1, GL_FALSE, textureMatrix);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
+ glEnableVertexAttribArray(m_hVideoPosition);
+ glVertexAttribPointer(m_hVideoPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+ setBlendingState(false);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ // switch back to our normal rendering program
+ glUseProgram(m_program);
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/ShaderProgram.h b/Source/WebCore/platform/graphics/android/ShaderProgram.h
new file mode 100644
index 0000000..55afe4f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/ShaderProgram.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ShaderProgram_h
+#define ShaderProgram_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FloatRect.h"
+#include "IntRect.h"
+#include "SkRect.h"
+#include "TransformationMatrix.h"
+#include <GLES2/gl2.h>
+
+namespace WebCore {
+
+class ShaderProgram {
+ public:
+ ShaderProgram();
+ void init();
+ int projectionMatrix() { return m_hProjectionMatrix; }
+ int alpha() { return m_hAlpha; }
+ int textureSampler() { return m_hTexSampler; }
+ int program() { return m_program; }
+
+ // Drawing
+ void setViewport(SkRect& viewport);
+ void drawQuad(SkRect& geometry, int textureId, float opacity);
+ float zValue(const TransformationMatrix& drawMatrix, float w, float h);
+ void drawLayerQuad(const TransformationMatrix& drawMatrix,
+ SkRect& geometry, int textureId, float opacity,
+ bool forceBlending = false);
+ void drawVideoLayerQuad(const TransformationMatrix& drawMatrix,
+ float* textureMatrix, SkRect& geometry, int textureId);
+ void setViewRect(const IntRect& viewRect);
+ FloatRect rectInScreenCoord(const TransformationMatrix& drawMatrix,
+ const IntSize& size);
+ FloatRect rectInInvScreenCoord(const TransformationMatrix& drawMatrix,
+ const IntSize& size);
+
+ FloatRect rectInInvScreenCoord(const FloatRect& rect);
+ FloatRect rectInScreenCoord(const FloatRect& rect);
+ FloatRect convertInvScreenCoordToScreenCoord(const FloatRect& rect);
+ FloatRect convertScreenCoordToInvScreenCoord(const FloatRect& rect);
+
+ void setTitleBarHeight(int height) { m_titleBarHeight = height; }
+ void setWebViewRect(const IntRect& rect) { m_webViewRect = rect; }
+ void setScreenClip(const IntRect& clip);
+ void clip(const FloatRect& rect);
+ IntRect clippedRectWithViewport(const IntRect& rect, int margin = 0);
+
+ void resetBlending();
+
+ private:
+ GLuint loadShader(GLenum shaderType, const char* pSource);
+ GLuint createProgram(const char* vertexSource, const char* fragmentSource);
+ void setProjectionMatrix(SkRect& geometry);
+
+ void setBlendingState(bool enableBlending);
+
+ bool m_blendingEnabled;
+
+ int m_program;
+ int m_videoProgram;
+
+ TransformationMatrix m_projectionMatrix;
+ GLuint m_textureBuffer[1];
+
+ TransformationMatrix m_documentToScreenMatrix;
+ TransformationMatrix m_documentToInvScreenMatrix;
+ SkRect m_viewport;
+ IntRect m_viewRect;
+ FloatRect m_clipRect;
+ IntRect m_screenClip;
+ int m_titleBarHeight;
+ IntRect m_webViewRect;
+
+ // uniforms
+ int m_hProjectionMatrix;
+ int m_hAlpha;
+ int m_hTexSampler;
+ int m_hVideoProjectionMatrix;
+ int m_hVideoTextureMatrix;
+ int m_hVideoTexSampler;
+
+ // attribs
+ GLint m_hPosition;
+ GLint m_hVideoPosition;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // ShaderProgram_h
diff --git a/Source/WebCore/platform/graphics/android/SharedBufferStream.cpp b/Source/WebCore/platform/graphics/android/SharedBufferStream.cpp
new file mode 100644
index 0000000..952495b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/SharedBufferStream.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "JavaSharedClient.h"
+#include "SharedBuffer.h"
+#include "SharedBufferStream.h"
+
+using namespace android;
+
+namespace WebCore {
+
+ static void CallDeref(void* buffer) {
+ ((SharedBuffer*)buffer)->deref();
+ }
+
+ SharedBufferStream::SharedBufferStream(SharedBuffer* buffer)
+ : SkMemoryStream(buffer->data(), buffer->size(), false) {
+ fBuffer = buffer;
+ buffer->ref();
+ }
+
+ SharedBufferStream::~SharedBufferStream() {
+ // we can't necessarily call fBuffer->deref() here, as we may be
+ // in a different thread from webkit, and SharedBuffer is not
+ // threadsafe. Therefore we defer it until it can be executed in the
+ // webkit thread.
+ JavaSharedClient::EnqueueFunctionPtr(CallDeref, fBuffer);
+ }
+
+}
diff --git a/Source/WebCore/platform/graphics/android/SharedBufferStream.h b/Source/WebCore/platform/graphics/android/SharedBufferStream.h
new file mode 100644
index 0000000..da59b69
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/SharedBufferStream.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 WebCore_SharedBufferStream_DEFINED
+#define WebCore_SharedBufferStream_DEFINED
+
+#include "SkStream.h"
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+ /** Subclass of SkStream that wrapps a webcore SharedBuffer object. To
+ allow this object to be deleted from any thread, the impl will ensure
+ that we unref the SharedBuffer object from the correct webcore thread.
+ */
+ class SharedBufferStream : public SkMemoryStream {
+ public:
+ SharedBufferStream(SharedBuffer* buffer);
+ virtual ~SharedBufferStream();
+
+ private:
+ // don't allow this to change our data. should not get called, but we
+ // override here just to be sure
+ virtual void setMemory(const void* data, size_t length, bool copyData) {
+ sk_throw();
+ }
+
+ // we share ownership of this with webkit
+ SharedBuffer* fBuffer;
+ };
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/android/SharedTexture.cpp b/Source/WebCore/platform/graphics/android/SharedTexture.cpp
new file mode 100644
index 0000000..040a28a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/SharedTexture.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "SharedTexture.h"
+
+#include "GLUtils.h"
+
+#define LOG_NDEBUG 1
+#define LOG_TAG "SharedTexture.cpp"
+#include <utils/Log.h>
+
+namespace WebCore {
+
+TextureInfo::TextureInfo()
+{
+ m_textureId = GL_NO_TEXTURE;
+ m_width = 0;
+ m_height = 0;
+ m_internalFormat = 0;
+}
+
+bool TextureInfo::equalsAttributes(const TextureInfo* otherTexture)
+{
+ return otherTexture->m_width == m_width
+ && otherTexture->m_height == m_height
+ && otherTexture->m_internalFormat == m_internalFormat;
+}
+
+void TextureInfo::copyAttributes(const TextureInfo* sourceTexture)
+{
+ m_width = sourceTexture->m_width;
+ m_height = sourceTexture->m_height;
+ m_internalFormat = sourceTexture->m_internalFormat;
+}
+
+bool TextureInfo::operator==(const TextureInfo& otherTexture)
+{
+ return otherTexture.m_textureId == m_textureId && equalsAttributes(&otherTexture);
+}
+
+SharedTexture::SharedTexture()
+{
+ m_eglImage = EGL_NO_IMAGE_KHR;
+ m_isNewImage = true;
+ m_syncObject = EGL_NO_SYNC_KHR;
+
+ // Defer initialization of these values until we initialize the source
+ // texture. This ensures that this initialization happens in the appropriate
+ // thread.
+ m_display = 0;
+ m_supportsEGLImage = false;
+ m_supportsEGLFenceSyncKHR = false;
+}
+
+// called by the consumer when it no longer wants to consume and after it has
+// terminated all providers. If EGLImages are used, the deletion of the
+// source texture and EGLImage is the responsibility of the caller.
+SharedTexture::~SharedTexture()
+{
+ deleteTargetTexture();
+}
+
+void SharedTexture::initSourceTexture()
+{
+
+ m_display = eglGetCurrentDisplay();
+ m_supportsEGLImage = GLUtils::isEGLImageSupported();
+ m_supportsEGLFenceSyncKHR = GLUtils::isEGLFenceSyncSupported();
+
+ // TODO temporarily disable fence sync until the EGL framework fixes
+ // performance issues that result from consistently adding/removing fences.
+ m_supportsEGLFenceSyncKHR = false;
+
+ LOGI("imageEGL: %d syncKHR: %d", m_supportsEGLImage, m_supportsEGLFenceSyncKHR);
+
+ glGenTextures(1, &m_sourceTexture.m_textureId);
+}
+
+
+void SharedTexture::deleteSourceTexture()
+{
+ // We need to delete the source texture and EGLImage in the thread in which
+ // it was created. In theory we should be able to delete the EGLImage
+ // from either thread, but it currently throws an error if not deleted
+ // in the same EGLContext from which it was created.
+ if (m_supportsEGLImage) {
+ GLUtils::deleteTexture(&m_sourceTexture.m_textureId);
+ if (m_eglImage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(eglGetCurrentDisplay(), m_eglImage);
+ m_eglImage = EGL_NO_IMAGE_KHR;
+ m_isNewImage = true;
+ }
+ LOGI("Deleted Source Texture and EGLImage");
+ }
+}
+
+void SharedTexture::deleteTargetTexture()
+{
+ if (m_supportsEGLImage)
+ GLUtils::deleteTexture(&m_targetTexture.m_textureId);
+ else
+ GLUtils::deleteTexture(&m_sourceTexture.m_textureId);
+}
+
+TextureInfo* SharedTexture::lockSource()
+{
+ m_lock.lock();
+
+ if (m_supportsEGLFenceSyncKHR && m_syncObject != EGL_NO_SYNC_KHR) {
+
+ EGLint status = eglClientWaitSyncKHR(m_display, m_syncObject, 0, 1000000);
+
+ if (status == EGL_TIMEOUT_EXPIRED_KHR)
+ LOGE("Sync timeout for shared texture (%d)", m_sourceTexture.m_textureId);
+
+ eglDestroySyncKHR(m_display, m_syncObject);
+ m_syncObject = EGL_NO_SYNC_KHR;
+ }
+
+ return &m_sourceTexture;
+}
+
+void SharedTexture::releaseSource()
+{
+ if (m_supportsEGLImage) {
+ // delete the existing image if needed
+ if (!m_sourceTexture.equalsAttributes(&m_targetTexture)) {
+ if (m_eglImage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(m_display, m_eglImage);
+ m_eglImage = EGL_NO_IMAGE_KHR;
+ m_isNewImage = true;
+ }
+ m_targetTexture.copyAttributes(&m_sourceTexture);
+ }
+
+ // create an image from the texture, only when the texture is valid
+ if (m_eglImage == EGL_NO_IMAGE_KHR && m_sourceTexture.m_width
+ && m_sourceTexture.m_height) {
+ GLUtils::createEGLImageFromTexture(m_sourceTexture.m_textureId, &m_eglImage);
+ LOGV("Generating Image (%d) 0x%x", m_sourceTexture.m_textureId, m_eglImage);
+
+ glFinish(); // ensures the texture is ready to be used by the consumer
+ }
+
+ } else {
+
+ m_targetTexture = m_sourceTexture;
+
+ // in the case of shared contexts we must flush the texture edits to the
+ // GPU. This ensures the edits complete prior to allowing the texture to
+ // be bound on the producers context.
+ glFlush();
+ }
+
+ m_lock.unlock();
+}
+
+TextureInfo* SharedTexture::lockTarget()
+{
+ m_lock.lock();
+
+ if ((!m_supportsEGLImage && m_targetTexture.m_textureId == GL_NO_TEXTURE)
+ || (m_supportsEGLImage && m_eglImage == EGL_NO_IMAGE_KHR)) {
+ m_lock.unlock();
+ return 0;
+ }
+
+ if (m_supportsEGLImage && (m_isNewImage || m_targetTexture.m_textureId == GL_NO_TEXTURE)) {
+ if (m_targetTexture.m_textureId == GL_NO_TEXTURE)
+ glGenTextures(1, &m_targetTexture.m_textureId);
+
+ GLUtils::createTextureFromEGLImage(m_targetTexture.m_textureId, m_eglImage);
+ LOGV("Generating Consumer Texture from 0x%x", m_eglImage);
+ m_isNewImage = false;
+ }
+
+ return &m_targetTexture;
+}
+
+void SharedTexture::releaseTarget()
+{
+
+ if (m_supportsEGLFenceSyncKHR) {
+ if (m_syncObject != EGL_NO_SYNC_KHR)
+ eglDestroySyncKHR(m_display, m_syncObject);
+ m_syncObject = eglCreateSyncKHR(m_display, EGL_SYNC_FENCE_KHR, 0);
+ } else {
+ // TODO the flush currently prevents the screen from getting partial
+ // updates but the only way to guarantee this is to call glFinish. Until
+ // we support an EGL sync we will leave flush enable in order to test
+ // with modest performance.
+ glFlush();
+ }
+
+ m_lock.unlock();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/android/SharedTexture.h b/Source/WebCore/platform/graphics/android/SharedTexture.h
new file mode 100644
index 0000000..37d6091
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/SharedTexture.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 SharedTexture_h
+#define SharedTexture_h
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <utils/threads.h>
+
+namespace WebCore {
+
+static const GLuint GL_NO_TEXTURE = 0;
+
+/**
+ * TextureInfo is a class that stores both the texture and metadata about the
+ * texture.
+ */
+class TextureInfo {
+public:
+
+ TextureInfo();
+
+ bool equalsAttributes(const TextureInfo* otherTexture);
+ void copyAttributes(const TextureInfo* sourceTexture);
+
+ bool operator==(const TextureInfo& otherTexture);
+
+ GLuint m_textureId;
+ int32_t m_width;
+ int32_t m_height;
+ GLenum m_internalFormat;
+};
+
+/**
+ * SharedTexture is a class that encapsulates all the necessary variables
+ * needed to share a single texture across threads. In the case that threads
+ * communicate using EGL's sharedContext mechanism or do not support the proper
+ * EGLImage extensions the targetTexture, eglImage, and isNewImage variables are
+ * not used.
+ */
+class SharedTexture {
+public:
+ // consumer thread functions
+ SharedTexture();
+ ~SharedTexture();
+
+ TextureInfo* lockSource();
+ void releaseSource();
+
+ TextureInfo* lockTarget();
+ void releaseTarget();
+
+ // these locks are only used for the methods below
+ void lock() { m_lock.lock(); }
+ void unlock() { m_lock.unlock(); }
+
+ void initSourceTexture(); // producer thread only
+ void deleteSourceTexture(); // producer thread only
+ void deleteTargetTexture(); // consumer thread only
+ GLuint getSourceTextureId() { return m_sourceTexture.m_textureId; }
+ GLuint getTargetTextureId() { return m_targetTexture.m_textureId; }
+ EGLImageKHR getEGLImage() { return m_eglImage; }
+
+private:
+ /**
+ * The mutex is used to ensure that the contents of the struct are current across
+ * threads and that only one thread is manipulating the texture at a given time.
+ */
+ android::Mutex m_lock;
+ /**
+ * The texture and its associated metadata that is used by the producer. The
+ * texture is created in the producer's thread and can only be read by the
+ * consumer when the consumer shares the same context as the producer. The
+ * metadata is used to track changes to the texture that would orphan the
+ * target texture and require a new EGLImage to be constructed.
+ */
+ TextureInfo m_sourceTexture;
+ /**
+ * The target texture stores the id and metadata of the texture that is to be
+ * used by the consumer. In the case where EGLImages are supported this hold
+ * the current eglImage target.
+ */
+ TextureInfo m_targetTexture;
+ /**
+ * The EGLImage is used to share the texture between EGLContexts on two
+ * different threads. This serves as an alternative to sharing the contexts
+ * but is only used if GL and EGL support the required extensions.
+ */
+ EGLImageKHR m_eglImage;
+ /**
+ * This flag is used to determine if the eglImage has been updated. This
+ * signals the consumer thread to rebind the targetTexture to the new image.
+ */
+ bool m_isNewImage;
+ /**
+ * The sync allows the consumer to release the lock prior to the commands
+ * executing on the GPU. Prior to releasing the lock the consumer creates
+ * a sync object and stores it here. After locking the texture the client
+ * must check that the sync has completed prior to manipulating the texture.
+ * This value is only used if the proper EGL extensions are supported.
+ */
+ EGLSyncKHR m_syncObject;
+
+ EGLDisplay m_display;
+
+ bool m_supportsEGLImage;
+ bool m_supportsEGLFenceSyncKHR;
+};
+
+} // namespace WebCore
+
+#endif // SharedTexture_h
diff --git a/Source/WebCore/platform/graphics/android/SkBitmapRef.h b/Source/WebCore/platform/graphics/android/SkBitmapRef.h
new file mode 100644
index 0000000..2a6e59e
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/SkBitmapRef.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 SkBitmapRef_DEFINED
+#define SkBitmapRef_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkBitmap.h"
+
+class SkBitmapRef : public SkRefCnt {
+public:
+ SkBitmapRef() : fOrigWidth(0), fOrigHeight(0), fAccessed(false) {}
+ explicit SkBitmapRef(const SkBitmap& src)
+ : fBitmap(src),
+ fOrigWidth(src.width()),
+ fOrigHeight(src.height()),
+ fAccessed(false) {}
+
+ const SkBitmap& bitmap() const { return fBitmap; }
+ SkBitmap& bitmap() { return fBitmap; }
+
+ int origWidth() const { return fOrigWidth; }
+ int origHeight() const { return fOrigHeight; }
+
+ void setOrigSize(int width, int height) {
+ fOrigWidth = width;
+ fOrigHeight = height;
+ }
+ // return true if this is not the first access
+ // mark it true so all subsequent calls return true
+ bool accessed() { bool result = fAccessed;
+ fAccessed = true; return result; }
+
+private:
+ SkBitmap fBitmap;
+ int fOrigWidth, fOrigHeight;
+ bool fAccessed;
+};
+
+#endif
diff --git a/Source/WebCore/platform/graphics/android/TextureOwner.h b/Source/WebCore/platform/graphics/android/TextureOwner.h
new file mode 100644
index 0000000..aebbf90
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/TextureOwner.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 TextureOwner_h
+#define TextureOwner_h
+
+namespace WebCore {
+
+class TiledPage;
+class BackedDoubleBufferedTexture;
+
+class TextureOwner {
+public:
+ virtual ~TextureOwner() { }
+ virtual bool removeTexture(BackedDoubleBufferedTexture* texture) = 0;
+ virtual TiledPage* page() = 0;
+};
+
+}
+
+#endif // TextureOwner_h
diff --git a/Source/WebCore/platform/graphics/android/TexturesGenerator.cpp b/Source/WebCore/platform/graphics/android/TexturesGenerator.cpp
new file mode 100644
index 0000000..e6bef6a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/TexturesGenerator.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "TexturesGenerator.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "BaseLayerAndroid.h"
+#include "GLUtils.h"
+#include "PaintLayerOperation.h"
+#include "TilesManager.h"
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TexturesGenerator", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+void TexturesGenerator::scheduleOperation(QueuedOperation* operation)
+{
+ {
+ android::Mutex::Autolock lock(mRequestedOperationsLock);
+ mRequestedOperations.append(operation);
+ }
+ mRequestedOperationsCond.signal();
+}
+
+void TexturesGenerator::removeOperationsForPage(TiledPage* page)
+{
+ removeOperationsForFilter(new PageFilter(page));
+}
+
+void TexturesGenerator::removePaintOperationsForPage(TiledPage* page, bool waitForRunning)
+{
+ removeOperationsForFilter(new PagePaintFilter(page), waitForRunning);
+}
+
+void TexturesGenerator::removeOperationsForBaseLayer(BaseLayerAndroid* layer)
+{
+ removeOperationsForFilter(new PaintLayerBaseFilter(layer));
+}
+
+void TexturesGenerator::removeOperationsForTexture(LayerTexture* texture)
+{
+ removeOperationsForFilter(new PaintLayerTextureFilter(texture));
+}
+
+void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter)
+{
+ removeOperationsForFilter(filter, true);
+}
+
+void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter, bool waitForRunning)
+{
+ android::Mutex::Autolock lock(mRequestedOperationsLock);
+ for (unsigned int i = 0; i < mRequestedOperations.size();) {
+ QueuedOperation* operation = mRequestedOperations[i];
+ if (filter->check(operation)) {
+ mRequestedOperations.remove(i);
+ delete operation;
+ } else {
+ i++;
+ }
+ }
+
+ if (waitForRunning) {
+ QueuedOperation* operation = m_currentOperation;
+ if (operation && filter->check(operation))
+ m_waitForCompletion = true;
+
+ delete filter;
+
+ // At this point, it means that we are currently executing an operation that
+ // we want to be removed -- we should wait until it is done, so that
+ // when we return our caller can be sure that there is no more operations
+ // in the queue matching the given filter.
+ while (m_waitForCompletion)
+ mRequestedOperationsCond.wait(mRequestedOperationsLock);
+ } else {
+ delete filter;
+ }
+}
+
+status_t TexturesGenerator::readyToRun()
+{
+ TilesManager::instance()->markGeneratorAsReady();
+ XLOG("Thread ready to run");
+ return NO_ERROR;
+}
+
+// Must be called from within a lock!
+QueuedOperation* TexturesGenerator::popNext()
+{
+ // Priority can change between when it was added and now
+ // Hence why the entire queue is rescanned
+ QueuedOperation* current = mRequestedOperations.last();
+ int currentPriority = current->priority();
+ if (currentPriority < 0) {
+ mRequestedOperations.removeLast();
+ return current;
+ }
+ int currentIndex = mRequestedOperations.size() - 1;
+ // Scan from the back to make removing faster (less items to copy)
+ for (int i = mRequestedOperations.size() - 2; i >= 0; i--) {
+ QueuedOperation *next = mRequestedOperations[i];
+ int nextPriority = next->priority();
+ if (nextPriority < 0) {
+ // Found a very high priority item, go ahead and just handle it now
+ mRequestedOperations.remove(i);
+ return next;
+ }
+ if (nextPriority < currentPriority) {
+ current = next;
+ currentPriority = nextPriority;
+ currentIndex = i;
+ }
+ }
+ mRequestedOperations.remove(currentIndex);
+ return current;
+}
+
+bool TexturesGenerator::threadLoop()
+{
+ // Check if we have any pending operations.
+ mRequestedOperationsLock.lock();
+ while (!mRequestedOperations.size())
+ mRequestedOperationsCond.wait(mRequestedOperationsLock);
+
+ XLOG("threadLoop, got signal");
+ mRequestedOperationsLock.unlock();
+
+ m_currentOperation = 0;
+ bool stop = false;
+ while (!stop) {
+ mRequestedOperationsLock.lock();
+ if (mRequestedOperations.size())
+ m_currentOperation = popNext();
+ mRequestedOperationsLock.unlock();
+
+ if (m_currentOperation) {
+ XLOG("threadLoop, painting the request with priority %d", m_currentOperation->priority());
+ m_currentOperation->run();
+ }
+
+ mRequestedOperationsLock.lock();
+ if (m_currentOperation) {
+ delete m_currentOperation;
+ m_currentOperation = 0;
+ }
+ if (!mRequestedOperations.size())
+ stop = true;
+ if (m_waitForCompletion) {
+ m_waitForCompletion = false;
+ mRequestedOperationsCond.signal();
+ }
+ mRequestedOperationsLock.unlock();
+
+ }
+ XLOG("threadLoop empty");
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/TexturesGenerator.h b/Source/WebCore/platform/graphics/android/TexturesGenerator.h
new file mode 100644
index 0000000..b03f52d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/TexturesGenerator.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 TexturesGenerator_h
+#define TexturesGenerator_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerTexture.h"
+#include "QueuedOperation.h"
+#include "TiledPage.h"
+#include <utils/threads.h>
+
+namespace WebCore {
+
+using namespace android;
+
+class BaseLayerAndroid;
+class LayerAndroid;
+
+class TexturesGenerator : public Thread {
+public:
+ TexturesGenerator() : Thread()
+ , m_waitForCompletion(false)
+ , m_currentOperation(0) { }
+ virtual ~TexturesGenerator() { }
+ virtual status_t readyToRun();
+
+ void removeOperationsForPage(TiledPage* page);
+ void removeOperationsForBaseLayer(BaseLayerAndroid* layer);
+ void removeOperationsForTexture(LayerTexture* texture);
+ void removePaintOperationsForPage(TiledPage* page, bool waitForRunning);
+ void removeOperationsForFilter(OperationFilter* filter);
+ void removeOperationsForFilter(OperationFilter* filter, bool waitForRunning);
+
+ void scheduleOperation(QueuedOperation* operation);
+
+private:
+ QueuedOperation* popNext();
+ virtual bool threadLoop();
+ Vector<QueuedOperation*> mRequestedOperations;
+ android::Mutex mRequestedOperationsLock;
+ android::Condition mRequestedOperationsCond;
+ bool m_waitForCompletion;
+ QueuedOperation* m_currentOperation;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // TexturesGenerator_h
diff --git a/Source/WebCore/platform/graphics/android/TiledPage.cpp b/Source/WebCore/platform/graphics/android/TiledPage.cpp
new file mode 100644
index 0000000..5212871
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/TiledPage.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "TiledPage.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "GLUtils.h"
+#include "IntRect.h"
+#include "PaintTileOperation.h"
+#include "TilesManager.h"
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+using namespace android;
+
+TiledPage::TiledPage(int id, GLWebViewState* state)
+ : m_baseTiles(0)
+ , m_baseTileSize(0)
+ , m_id(id)
+ , m_scale(1)
+ , m_invScale(1)
+ , m_glWebViewState(state)
+ , m_latestPictureInval(0)
+ , m_prepare(false)
+{
+ m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("TiledPage");
+#endif
+}
+
+void TiledPage::updateBaseTileSize()
+{
+ // This value must be at least 1 greater than the max number of allowed
+ // textures. This is because prepare() asks for a tile before it reserves
+ // a texture for that tile. If all textures are currently in use by the
+ // page then there will be no available tile and having the extra tile
+ // ensures that this does not happen. After claiming the extra tile the call
+ // to reserveTexture() will cause some other tile in the page to lose it's
+ // texture and become available, thus ensuring that we always have at least
+ // one tile that is available.
+ int baseTileSize = TilesManager::instance()->maxTextureCount() + 1;
+ if (baseTileSize > m_baseTileSize)
+ m_baseTileSize = baseTileSize;
+}
+
+TiledPage::~TiledPage()
+{
+ // In order to delete the page we must ensure that none of its BaseTiles are
+ // currently painting or scheduled to be painted by the TextureGenerator
+ TilesManager::instance()->removeOperationsForPage(this);
+ delete[] m_baseTiles;
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("TiledPage");
+#endif
+}
+
+BaseTile* TiledPage::getBaseTile(int x, int y) const
+{
+ for (int j = 0; j < m_baseTileSize; j++) {
+ BaseTile& tile = m_baseTiles[j];
+ if (tile.x() == x && tile.y() == y && !tile.isAvailable())
+ return &tile;
+ }
+ return 0;
+}
+
+void TiledPage::setUsable(bool usable)
+{
+ for (int j = 0; j < m_baseTileSize; j++) {
+ BaseTile& tile = m_baseTiles[j];
+ tile.setUsable(usable);
+ }
+ return;
+}
+
+void TiledPage::invalidateRect(const IntRect& inval, const unsigned int pictureCount)
+{
+ // Given the current scale level we need to mark the appropriate tiles as dirty
+ const float invTileContentWidth = m_scale / TilesManager::tileWidth();
+ const float invTileContentHeight = m_scale / TilesManager::tileHeight();
+
+ const int firstDirtyTileX = static_cast<int>(floorf(inval.x() * invTileContentWidth));
+ const int firstDirtyTileY = static_cast<int>(floorf(inval.y() * invTileContentHeight));
+ const int lastDirtyTileX = static_cast<int>(ceilf(inval.right() * invTileContentWidth));
+ const int lastDirtyTileY = static_cast<int>(ceilf(inval.bottom() * invTileContentHeight));
+
+ XLOG("Marking X %d-%d and Y %d-%d dirty", firstDirtyTileX, lastDirtyTileX, firstDirtyTileY, lastDirtyTileY);
+ // We defer marking the tile as dirty until the next time we need to prepare
+ // to draw.
+ m_invalRegion.op(firstDirtyTileX, firstDirtyTileY, lastDirtyTileX, lastDirtyTileY, SkRegion::kUnion_Op);
+ m_invalTilesRegion.op(inval.x(), inval.y(), inval.right(), inval.bottom(), SkRegion::kUnion_Op);
+ m_latestPictureInval = pictureCount;
+}
+
+void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
+{
+ if (y < 0)
+ return;
+
+ for (int i = 0; i < tilesInRow; i++) {
+ int x = firstTileX;
+
+ // If we are goingLeft, we want to schedule the tiles starting from the
+ // right (and to the left if not). This is because tiles are appended to
+ // the list and the texture uploader goes through the set front to back.
+ if (goingLeft)
+ x += (tilesInRow - 1) - i;
+ else
+ x += i;
+
+ if (x < 0)
+ continue;
+
+ BaseTile* currentTile = 0;
+ BaseTile* availableTile = 0;
+ for (int j = 0; j < m_baseTileSize; j++) {
+ BaseTile& tile = m_baseTiles[j];
+ if (tile.x() == x && tile.y() == y) {
+ currentTile = &tile;
+ break;
+ }
+ if (!availableTile && tile.isAvailable())
+ availableTile = &tile;
+ }
+
+ if (!currentTile && availableTile) {
+ currentTile = availableTile;
+ currentTile->setContents(this, x, y);
+ }
+
+ if (currentTile) {
+ currentTile->setScale(m_scale);
+
+ // ensure there is a texture associated with the tile and then check to
+ // see if the texture is dirty and in need of repainting
+ currentTile->reserveTexture();
+ updateTileUsedLevel(tileBounds, *currentTile);
+ if (currentTile->isDirty() && !currentTile->isRepaintPending()) {
+ PaintTileOperation *operation = new PaintTileOperation(currentTile);
+ TilesManager::instance()->scheduleOperation(operation);
+ } else if (currentTile->isDirty()) {
+ XLOG("Tile %dx%d is dirty, but awaiting repaint", currentTile->x(), currentTile->y());
+ }
+ }
+ }
+}
+
+void TiledPage::updateTileUsedLevel(const SkIRect& tileBounds, BaseTile& tile)
+{
+ const int lastTileX = tileBounds.fRight - 1;
+ const int lastTileY = tileBounds.fBottom - 1;
+
+ // set the used level of the tile (e.g. distance from the viewport)
+ int dx = 0;
+ int dy = 0;
+
+ if (tileBounds.fLeft > tile.x())
+ dx = tileBounds.fLeft - tile.x();
+ else if (lastTileX < tile.x())
+ dx = tile.x() - lastTileX;
+
+ if (tileBounds.fTop > tile.y())
+ dy = tileBounds.fTop - tile.y();
+ else if (lastTileY < tile.y())
+ dy = tile.y() - lastTileY;
+
+ int d = std::max(dx, dy);
+
+ tile.setUsedLevel(d);
+}
+
+void TiledPage::updateTileState(const SkIRect& tileBounds)
+{
+ if (!m_glWebViewState || tileBounds.isEmpty()) {
+ m_invalRegion.setEmpty();
+ m_invalTilesRegion.setEmpty();
+ return;
+ }
+
+ for (int x = 0; x < m_baseTileSize; x++) {
+
+ BaseTile& tile = m_baseTiles[x];
+
+ // if the tile no longer has a texture then proceed to the next tile
+ if (tile.isAvailable())
+ continue;
+
+ // if the tile is in the dirty region then we must invalidate it
+ if (m_invalRegion.contains(tile.x(), tile.y()))
+ tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
+
+ updateTileUsedLevel(tileBounds, tile);
+ }
+
+ // clear the invalidated region as all tiles within that region have now
+ // been marked as dirty.
+ m_invalRegion.setEmpty();
+ m_invalTilesRegion.setEmpty();
+}
+
+void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds)
+{
+ if (!m_glWebViewState)
+ return;
+
+ // update the tiles distance from the viewport
+ updateTileState(tileBounds);
+ m_prepare = true;
+ m_scrollingDown = goingDown;
+
+ int firstTileX = tileBounds.fLeft;
+ int firstTileY = tileBounds.fTop;
+ int nbTilesWidth = tileBounds.width();
+ int nbTilesHeight = tileBounds.height();
+
+ int lastTileX = tileBounds.fRight - 1;
+ int lastTileY = tileBounds.fBottom - 1;
+
+ const int baseContentHeight = m_glWebViewState->baseContentHeight();
+ const int baseContentWidth = m_glWebViewState->baseContentWidth();
+
+ // Expand number of tiles to allow tiles outside of viewport to be prepared for
+ // smoother scrolling.
+ int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
+ int nMaxTilesPerPage = m_baseTileSize / 2;
+ int expandX = TilesManager::instance()->expandedTileBoundsX();
+ int expandY = TilesManager::instance()->expandedTileBoundsY();
+ if (nTilesToPrepare + (nbTilesHeight * expandX * 2) <= nMaxTilesPerPage) {
+ firstTileX -= expandX;
+ lastTileX += expandX;
+ nbTilesWidth += expandX * 2;
+ }
+ if (nTilesToPrepare + (nbTilesWidth * expandY * 2) <= nMaxTilesPerPage) {
+ firstTileY -= expandY;
+ lastTileY += expandY;
+ nbTilesHeight += expandY * 2;
+ }
+ m_expandedTileBounds.fLeft = firstTileX;
+ m_expandedTileBounds.fTop = firstTileY;
+ m_expandedTileBounds.fRight = lastTileX;
+ m_expandedTileBounds.fBottom = lastTileY;
+
+ for (int i = 0; i < nbTilesHeight; i++)
+ prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
+}
+
+bool TiledPage::ready(const SkIRect& tileBounds, float scale)
+{
+ if (!m_glWebViewState)
+ return false;
+
+ if (!m_invalRegion.isEmpty() && !m_prepare)
+ return false;
+
+ if (m_scale != scale)
+ return false;
+
+ for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
+ for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
+ BaseTile* t = getBaseTile(x, y);
+ if (!t || !t->isTileReady())
+ return false;
+ }
+ }
+ m_prepare = false;
+ return true;
+}
+
+void TiledPage::draw(float transparency, const SkIRect& tileBounds)
+{
+ if (!m_glWebViewState)
+ return;
+
+ const float tileWidth = TilesManager::tileWidth() * m_invScale;
+ const float tileHeight = TilesManager::tileHeight() * m_invScale;
+
+ SkIRect actualTileBounds = tileBounds;
+ actualTileBounds.fTop -= TilesManager::instance()->expandedTileBoundsY();
+ actualTileBounds.fBottom += TilesManager::instance()->expandedTileBoundsY();
+ actualTileBounds.fLeft -= TilesManager::instance()->expandedTileBoundsX();
+ actualTileBounds.fRight += TilesManager::instance()->expandedTileBoundsX();
+
+ for (int j = 0; j < m_baseTileSize; j++) {
+ BaseTile& tile = m_baseTiles[j];
+ if (actualTileBounds.contains(tile.x(), tile.y())) {
+
+ SkRect rect;
+ rect.fLeft = tile.x() * tileWidth;
+ rect.fTop = tile.y() * tileHeight;
+ rect.fRight = rect.fLeft + tileWidth;
+ rect.fBottom = rect.fTop + tileHeight;
+
+ tile.draw(transparency, rect, m_scale);
+ }
+ }
+}
+
+unsigned int TiledPage::paintBaseLayerContent(SkCanvas* canvas)
+{
+ if (m_glWebViewState)
+ return m_glWebViewState->paintBaseLayerContent(canvas);
+ return 0;
+}
+
+TiledPage* TiledPage::sibling()
+{
+ if (!m_glWebViewState)
+ return 0;
+ return m_glWebViewState->sibling(this);
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/TiledPage.h b/Source/WebCore/platform/graphics/android/TiledPage.h
new file mode 100644
index 0000000..1aa3e61
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/TiledPage.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 TiledPage_h
+#define TiledPage_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "BaseTile.h"
+#include "SkCanvas.h"
+#include "SkRegion.h"
+
+namespace WebCore {
+
+class GLWebViewState;
+class IntRect;
+
+/**
+ * The TiledPage represents a map of BaseTiles covering the viewport. Each
+ * GLWebViewState contains two TiledPages, one to display the page at the
+ * current scale factor, and another in the background that we use to paint the
+ * page at a different scale factor. For instance, when we zoom using one
+ * TiledPage its tiles are scaled in hardware and therefore are subject to a
+ * loss of quality. To address this when the user finishes zooming we paint the
+ * background TilePage at the new scale factor. When the background TilePage is
+ * ready, we swap it with the currently displaying TiledPage.
+ */
+class TiledPage {
+public:
+ TiledPage(int id, GLWebViewState* state);
+ ~TiledPage();
+
+ // returns the other TiledPage who shares the same GLWebViewState
+ TiledPage* sibling();
+
+ // prepare the page for display on the screen
+ void prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds);
+ // check to see if the page is ready for display
+ bool ready(const SkIRect& tileBounds, float scale);
+ // draw the page on the screen
+ void draw(float transparency, const SkIRect& tileBounds);
+
+ // used by individual tiles to generate the bitmap for their tile
+ unsigned int paintBaseLayerContent(SkCanvas*);
+ // used by individual tiles to get the information about the current picture
+ GLWebViewState* glWebViewState() { return m_glWebViewState; }
+
+ float scale() const { return m_scale; }
+ void setScale(float scale) { m_scale = scale; m_invScale = 1 / scale; }
+
+ void invalidateRect(const IntRect& invalRect, const unsigned int pictureCount);
+ void setUsable(bool usable);
+ void updateBaseTileSize();
+ bool scrollingDown() { return m_scrollingDown; }
+ SkIRect* expandedTileBounds() { return &m_expandedTileBounds; }
+
+private:
+ void updateTileState(const SkIRect& tileBounds);
+ void prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds);
+ void updateTileUsedLevel(const SkIRect& tileBounds, BaseTile& tile);
+
+ BaseTile* getBaseTile(int x, int y) const;
+
+ // array of tiles used to compose a page. The tiles are allocated in the
+ // constructor to prevent them from potentially being allocated on the stack
+ BaseTile* m_baseTiles;
+ // stores the number of tiles in the m_baseTiles array. This enables us to
+ // quickly iterate over the array without have to check it's size
+ int m_baseTileSize;
+ int m_id;
+ float m_scale;
+ float m_invScale;
+ GLWebViewState* m_glWebViewState;
+
+ // used to identify the tiles that have been invalidated (marked dirty) since
+ // the last time updateTileState() has been called. The region is stored in
+ // terms of the (x,y) coordinates used to determine the location of the tile
+ // within the page, not in content/view pixel coordinates.
+ SkRegion m_invalRegion;
+
+ // inval regions in content coordinates
+ SkRegion m_invalTilesRegion;
+ unsigned int m_latestPictureInval;
+ bool m_prepare;
+ bool m_scrollingDown;
+ SkIRect m_expandedTileBounds;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // TiledPage_h
diff --git a/Source/WebCore/platform/graphics/android/TilesManager.cpp b/Source/WebCore/platform/graphics/android/TilesManager.cpp
new file mode 100644
index 0000000..5a9a164
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/TilesManager.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "TilesManager.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "BaseTile.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include <cutils/atomic.h>
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TilesManager", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+// Important: We need at least twice as much textures as is needed to cover
+// one viewport, otherwise the allocation may stall.
+// We need n textures for one TiledPage, and another n textures for the
+// second page used when scaling.
+// In our case, we use 300x300 textures. On the tablet, this equates to
+// at least 5 * 3 = 15 textures. We also enable offscreen textures to a maximum
+// of 101 textures used (i.e. ~70Mb max, accounting for the double buffer textures)
+#define EXPANDED_TILE_BOUNDS_X 1
+#define EXPANDED_TILE_BOUNDS_Y 2
+#define MAX_TEXTURE_ALLOCATION 3+(5+EXPANDED_TILE_BOUNDS_X*2)*(3+EXPANDED_TILE_BOUNDS_Y*2)*2
+#define TILE_WIDTH 300
+#define TILE_HEIGHT 300
+
+// Define a maximum amount of ram used by layers
+#define MAX_LAYERS_ALLOCATION 33554432 // 32Mb
+// Define a maximum amount of ram used by one layer
+#define MAX_LAYER_ALLOCATION 8388608 // 8Mb
+#define BYTES_PER_PIXEL 4 // 8888 config
+
+namespace WebCore {
+
+GLint TilesManager::getMaxTextureSize()
+{
+ static GLint maxTextureSize = 0;
+ if (!maxTextureSize)
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ return maxTextureSize;
+}
+
+int TilesManager::getMaxTextureAllocation()
+{
+ return MAX_TEXTURE_ALLOCATION;
+}
+
+TilesManager::TilesManager()
+ : m_layersMemoryUsage(0)
+ , m_maxTextureCount(0)
+ , m_expandedTileBounds(false)
+ , m_generatorReady(false)
+ , m_showVisualIndicator(false)
+{
+ XLOG("TilesManager ctor");
+ m_textures.reserveCapacity(MAX_TEXTURE_ALLOCATION);
+ m_tilesBitmap = new SkBitmap();
+ m_tilesBitmap->setConfig(SkBitmap::kARGB_8888_Config, tileWidth(), tileHeight());
+ m_tilesBitmap->allocPixels();
+ m_tilesBitmap->eraseColor(0);
+ m_pixmapsGenerationThread = new TexturesGenerator();
+ m_pixmapsGenerationThread->run("TexturesGenerator");
+}
+
+void TilesManager::allocateTiles()
+{
+ int nbTexturesToAllocate = m_maxTextureCount - m_textures.size();
+ XLOG("%d tiles to allocate (%d textures planned)", nbTexturesToAllocate, m_maxTextureCount);
+ int nbTexturesAllocated = 0;
+ for (int i = 0; i < nbTexturesToAllocate; i++) {
+ BackedDoubleBufferedTexture* texture = new BackedDoubleBufferedTexture(
+ tileWidth(), tileHeight(), m_tilesBitmap);
+ // the atomic load ensures that the texture has been fully initialized
+ // before we pass a pointer for other threads to operate on
+ BackedDoubleBufferedTexture* loadedTexture =
+ reinterpret_cast<BackedDoubleBufferedTexture*>(
+ android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture)));
+ m_textures.append(loadedTexture);
+ nbTexturesAllocated++;
+ }
+ XLOG("allocated %d textures", nbTexturesAllocated);
+}
+
+void TilesManager::printTextures()
+{
+#ifdef DEBUG
+ XLOG("++++++");
+ for (unsigned int i = 0; i < m_textures.size(); i++) {
+ BackedDoubleBufferedTexture* texture = m_textures[i];
+ BaseTile* o = 0;
+ if (texture->owner())
+ o = (BaseTile*) texture->owner();
+ int x = -1;
+ int y = -1;
+ if (o) {
+ x = o->x();
+ y = o->y();
+ }
+ XLOG("[%d] texture %x usedLevel: %d busy: %d owner: %x (%d, %d) page: %x scale: %.2f",
+ i, texture, texture->usedLevel(),
+ texture->busy(), o, x, y, o ? o->page() : 0, o ? o->scale() : 0);
+ }
+ XLOG("------");
+#endif // DEBUG
+}
+
+void TilesManager::resetTextureUsage(TiledPage* page)
+{
+ android::Mutex::Autolock lock(m_texturesLock);
+ for (unsigned int i = 0; i < m_textures.size(); i++) {
+ BackedDoubleBufferedTexture* texture = m_textures[i];
+ TextureOwner* owner = texture->owner();
+ if (owner) {
+ if (owner->page() == page)
+ texture->setUsedLevel(-1);
+ }
+ }
+}
+
+BackedDoubleBufferedTexture* TilesManager::getAvailableTexture(BaseTile* owner)
+{
+ android::Mutex::Autolock lock(m_texturesLock);
+
+ // Sanity check that the tile does not already own a texture
+ if (owner->texture() && owner->texture()->owner() == owner) {
+ owner->texture()->setUsedLevel(0);
+ XLOG("same owner (%d, %d), getAvailableTexture(%x) => texture %x",
+ owner->x(), owner->y(), owner, owner->texture());
+ return owner->texture();
+ }
+
+ // The heuristic for selecting a texture is as follows:
+ // 1. return an unused texture if one exists
+ // 2. return the farthest texture from the viewport (from any tiled page)
+ // 3. return any texture not used by the tile's page or the page's sibiling
+ //
+ // The texture level indicates a tiles closeness to the current viewport
+ BackedDoubleBufferedTexture* farthestTexture = 0;
+ int farthestTextureLevel = 0;
+ const unsigned int max = m_textures.size();
+ for (unsigned int i = 0; i < max; i++) {
+ BackedDoubleBufferedTexture* texture = m_textures[i];
+ if (texture->usedLevel() == -1) { // found an unused texture, grab it
+ farthestTexture = texture;
+ break;
+ }
+ if (farthestTextureLevel < texture->usedLevel()) {
+ farthestTextureLevel = texture->usedLevel();
+ farthestTexture = texture;
+ }
+ }
+ if (farthestTexture && farthestTexture->acquire(owner)) {
+ XLOG("farthest texture, getAvailableTexture(%x) => texture %x (level %d)",
+ owner, farthestTexture, farthestTexture->usedLevel());
+ farthestTexture->setUsedLevel(0);
+ return farthestTexture;
+ }
+
+ // At this point, all textures are used or we failed to aquire the farthest
+ // texture. Now let's just grab a texture not in use by either of the two
+ // tiled pages associated with this view.
+ TiledPage* currentPage = owner->page();
+ TiledPage* nextPage = currentPage->sibling();
+ for (unsigned int i = 0; i < max; i++) {
+ BackedDoubleBufferedTexture* texture = m_textures[i];
+ if (texture->tryAcquire(owner, currentPage, nextPage)) {
+ XLOG("grab a texture that wasn't ours, (%x != %x) at %d => texture %x",
+ owner->page(), texture->owner()->page(), i, texture);
+ texture->setUsedLevel(0);
+ return texture;
+ }
+ }
+
+ XLOG("Couldn't find an available texture for BaseTile %x (%d, %d) !!!",
+ owner, owner->x(), owner->y());
+#ifdef DEBUG
+ printTextures();
+#endif // DEBUG
+ return 0;
+}
+
+LayerTexture* TilesManager::getExistingTextureForLayer(LayerAndroid* layer,
+ const IntRect& rect,
+ bool any,
+ LayerTexture* texture)
+{
+ android::Mutex::Autolock lock(m_texturesLock);
+ LayerTexture* best = 0;
+ unsigned newestPictureUsed = 0;
+ for (unsigned int i = 0; i< m_layersTextures.size(); i++) {
+ if (m_layersTextures[i]->id() != layer->uniqueId())
+ continue;
+ if (!any && rect != m_layersTextures[i]->rect())
+ continue;
+ if (!any && layer->getScale() != m_layersTextures[i]->scale())
+ continue;
+ if (any && texture == m_layersTextures[i])
+ continue;
+
+ if (m_layersTextures[i]->ready()) {
+ unsigned int pictureUsed = m_layersTextures[i]->pictureUsed();
+ if (pictureUsed >= newestPictureUsed) {
+ newestPictureUsed = pictureUsed;
+ best = m_layersTextures[i];
+ }
+ }
+
+ XLOG("return layer %d (%x) for tile %d (%x)",
+ i, m_layersTextures[i],
+ layer->uniqueId(), layer);
+ }
+
+ if (best && best->acquire(layer, any))
+ return best;
+ return 0;
+}
+
+void TilesManager::printLayersTextures(const char* s)
+{
+#ifdef DEBUG
+ XLOG(">>> print layers textures (%s)", s);
+ for (unsigned int i = 0; i< m_layersTextures.size(); i++) {
+ XLOG("[%d] %s, texture %x for layer %d (w: %.2f, h: %.2f), owner: %x",
+ i, s, m_layersTextures[i],
+ m_layersTextures[i]->id(),
+ m_layersTextures[i]->getSize().fWidth,
+ m_layersTextures[i]->getSize().fHeight,
+ m_layersTextures[i]->owner());
+ }
+ XLOG("<<< print layers textures (%s)", s);
+#endif
+}
+
+void TilesManager::cleanupLayersTextures(LayerAndroid* layer, bool forceCleanup)
+{
+ android::Mutex::Autolock lock(m_texturesLock);
+ SkLayer* rootLayer = 0;
+ if (layer)
+ rootLayer = layer->getRootLayer();
+#ifdef DEBUG
+ if (forceCleanup)
+ XLOG("FORCE cleanup");
+ XLOG("before cleanup, memory %d", m_layersMemoryUsage);
+ printLayersTextures("before cleanup");
+#endif
+ for (unsigned int i = 0; i< m_layersTextures.size();) {
+ LayerTexture* texture = m_layersTextures[i];
+
+ if (forceCleanup && texture->owner()) {
+ LayerAndroid* textureLayer =
+ static_cast<LayerAndroid*>(texture->owner());
+ if (textureLayer->getRootLayer() != rootLayer) {
+ // We only want to force destroy layers
+ // that are not used by the current page
+ XLOG("force removing texture %x for layer %d",
+ texture, textureLayer->uniqueId());
+ textureLayer->removeTexture(texture);
+ }
+ }
+
+ // We only try to destroy textures that have no owners.
+ // This could be due to:
+ // 1) - the LayerAndroid dtor has been called (i.e. when swapping
+ // a LayerAndroid tree with a new one)
+ // 2) - or due to the above code, forcing a destroy.
+ // If the texture has been forced to be released (case #2), it
+ // could still be in use (in the middle of being painted). So we
+ // need to check that's not the case by checking busy(). See
+ // LayerAndroid::paintBitmapGL().
+ if (!texture->owner() && !texture->busy()) {
+ m_layersMemoryUsage -= (int) texture->getSize().fWidth
+ * (int) texture->getSize().fHeight * BYTES_PER_PIXEL;
+ m_layersTextures.remove(i);
+ // We can destroy the texture. We first remove it from the textures
+ // list, and then remove any queued drawing. At this point we know
+ // the texture has been removed from the layer, and that it's not
+ // busy, so it's safe to delete.
+ m_pixmapsGenerationThread->removeOperationsForTexture(texture);
+ XLOG("delete texture %x", texture);
+ delete texture;
+ } else {
+ // only iterate if we don't delete (if we delete, no need to as we
+ // remove the element from the array)
+ i++;
+ }
+ }
+#ifdef DEBUG
+ printLayersTextures("after cleanup");
+ XLOG("after cleanup, memory %d", m_layersMemoryUsage);
+#endif
+}
+
+LayerTexture* TilesManager::createTextureForLayer(LayerAndroid* layer, const IntRect& rect)
+{
+ int w = rect.width() * layer->getScale();
+ int h = rect.height() * layer->getScale();
+ unsigned int size = w * h * BYTES_PER_PIXEL;
+
+ // We will not allocate textures that:
+ // 1) cannot be handled by the graphic card (maxTextureSize &
+ // totalMaxTextureSize)
+ // 2) will make us go past our texture limit (MAX_LAYERS_ALLOCATION)
+
+ GLint maxTextureSize = getMaxTextureSize();
+ unsigned totalMaxTextureSize = maxTextureSize * maxTextureSize * BYTES_PER_PIXEL;
+ bool large = w > maxTextureSize || h > maxTextureSize || size > totalMaxTextureSize;
+ XLOG("createTextureForLayer(%d) @scale %.2f => %d, %d (too large? %x)", layer->uniqueId(),
+ layer->getScale(), w, h, large);
+
+ // For now just return 0 if too large
+ if (large)
+ return 0;
+
+ if (w == 0 || h == 0) // empty layer
+ return 0;
+
+ if (m_layersMemoryUsage + size > MAX_LAYERS_ALLOCATION)
+ cleanupLayersTextures(layer, true);
+
+ LayerTexture* texture = new LayerTexture(w, h);
+ texture->setId(layer->uniqueId());
+ texture->setRect(rect);
+ texture->setScale(layer->getScale());
+
+ android::Mutex::Autolock lock(m_texturesLock);
+ m_layersTextures.append(texture);
+ texture->acquire(layer);
+ m_layersMemoryUsage += size;
+ return texture;
+}
+
+int TilesManager::maxLayersAllocation()
+{
+ return MAX_LAYERS_ALLOCATION;
+}
+
+int TilesManager::maxLayerAllocation()
+{
+ return MAX_LAYER_ALLOCATION;
+}
+
+int TilesManager::maxTextureCount()
+{
+ android::Mutex::Autolock lock(m_texturesLock);
+ return m_maxTextureCount;
+}
+
+void TilesManager::setMaxTextureCount(int max)
+{
+ XLOG("setMaxTextureCount: %d (current: %d, total:%d)",
+ max, m_maxTextureCount, MAX_TEXTURE_ALLOCATION);
+ if (m_maxTextureCount &&
+ (max > MAX_TEXTURE_ALLOCATION ||
+ max <= m_maxTextureCount))
+ return;
+
+ android::Mutex::Autolock lock(m_texturesLock);
+
+ if (max < MAX_TEXTURE_ALLOCATION)
+ m_maxTextureCount = max;
+ else
+ m_maxTextureCount = MAX_TEXTURE_ALLOCATION;
+
+ allocateTiles();
+}
+
+float TilesManager::tileWidth()
+{
+ return TILE_WIDTH;
+}
+
+float TilesManager::tileHeight()
+{
+ return TILE_HEIGHT;
+}
+
+int TilesManager::expandedTileBoundsX() {
+ return m_expandedTileBounds ? EXPANDED_TILE_BOUNDS_X : 0;
+}
+
+int TilesManager::expandedTileBoundsY() {
+ return m_expandedTileBounds ? EXPANDED_TILE_BOUNDS_Y : 0;
+}
+
+TilesManager* TilesManager::instance()
+{
+ if (!gInstance) {
+ gInstance = new TilesManager();
+ XLOG("instance(), new gInstance is %x", gInstance);
+ XLOG("Waiting for the generator...");
+ gInstance->waitForGenerator();
+ XLOG("Generator ready!");
+ }
+ return gInstance;
+}
+
+TilesManager* TilesManager::gInstance = 0;
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/TilesManager.h b/Source/WebCore/platform/graphics/android/TilesManager.h
new file mode 100644
index 0000000..6d49cca
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/TilesManager.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 TilesManager_h
+#define TilesManager_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "BackedDoubleBufferedTexture.h"
+#include "BaseTile.h"
+#include "LayerAndroid.h"
+#include "LayerTexture.h"
+#include "ShaderProgram.h"
+#include "TexturesGenerator.h"
+#include "TiledPage.h"
+#include <utils/threads.h>
+
+namespace WebCore {
+
+class TilesManager {
+public:
+ static TilesManager* instance();
+ static GLint getMaxTextureSize();
+ static int getMaxTextureAllocation();
+
+ static bool hardwareAccelerationEnabled()
+ {
+ return gInstance != 0;
+ }
+
+ void removeOperationsForPage(TiledPage* page)
+ {
+ m_pixmapsGenerationThread->removeOperationsForPage(page);
+ }
+
+ void removePaintOperationsForPage(TiledPage* page, bool waitForCompletion)
+ {
+ m_pixmapsGenerationThread->removePaintOperationsForPage(page, waitForCompletion);
+ }
+
+ void removeOperationsForBaseLayer(BaseLayerAndroid* layer)
+ {
+ m_pixmapsGenerationThread->removeOperationsForBaseLayer(layer);
+ }
+
+ void removeOperationsForTexture(LayerTexture* texture)
+ {
+ m_pixmapsGenerationThread->removeOperationsForTexture(texture);
+ }
+
+ void scheduleOperation(QueuedOperation* operation)
+ {
+ m_pixmapsGenerationThread->scheduleOperation(operation);
+ }
+
+ ShaderProgram* shader() { return &m_shader; }
+
+ BackedDoubleBufferedTexture* getAvailableTexture(BaseTile* owner);
+
+ void printLayersTextures(const char* s);
+ void cleanupLayersTextures(LayerAndroid* layer, bool forceCleanup = false);
+ LayerTexture* getExistingTextureForLayer(LayerAndroid* layer, const IntRect& rect,
+ bool any = false, LayerTexture* texture = 0);
+ LayerTexture* createTextureForLayer(LayerAndroid* layer, const IntRect& rect);
+
+ void markGeneratorAsReady()
+ {
+ {
+ android::Mutex::Autolock lock(m_generatorLock);
+ m_generatorReady = true;
+ }
+ m_generatorReadyCond.signal();
+ }
+
+ void printTextures();
+
+ void resetTextureUsage(TiledPage* page);
+
+ int maxLayersAllocation();
+ int maxLayerAllocation();
+ int maxTextureCount();
+ void setMaxTextureCount(int max);
+ static float tileWidth();
+ static float tileHeight();
+ int expandedTileBoundsX();
+ int expandedTileBoundsY();
+
+ void allocateTiles();
+
+ void setExpandedTileBounds(bool enabled) {
+ m_expandedTileBounds = enabled;
+ }
+
+ bool getShowVisualIndicator() {
+ return m_showVisualIndicator;
+ }
+
+ void setShowVisualIndicator(bool showVisualIndicator) {
+ m_showVisualIndicator = showVisualIndicator;
+ }
+
+private:
+
+ TilesManager();
+
+ void waitForGenerator()
+ {
+ android::Mutex::Autolock lock(m_generatorLock);
+ while (!m_generatorReady)
+ m_generatorReadyCond.wait(m_generatorLock);
+ }
+
+ Vector<BackedDoubleBufferedTexture*> m_textures;
+ Vector<LayerTexture*> m_layersTextures;
+
+ unsigned int m_layersMemoryUsage;
+
+ int m_maxTextureCount;
+ bool m_expandedTileBounds;
+
+ bool m_generatorReady;
+
+ bool m_showVisualIndicator;
+
+ sp<TexturesGenerator> m_pixmapsGenerationThread;
+
+ android::Mutex m_texturesLock;
+ android::Mutex m_generatorLock;
+ android::Condition m_generatorReadyCond;
+
+ static TilesManager* gInstance;
+
+ ShaderProgram m_shader;
+ SkBitmap* m_tilesBitmap;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // TilesManager_h
diff --git a/Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp
new file mode 100644
index 0000000..32e518d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "VideoLayerAndroid.h"
+
+#include "RenderSkinMediaButton.h"
+#include "TilesManager.h"
+#include <GLES2/gl2.h>
+#include <gui/SurfaceTexture.h>
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#ifdef DEBUG
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "VideoLayerAndroid", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+GLuint VideoLayerAndroid::m_spinnerOuterTextureId = 0;
+GLuint VideoLayerAndroid::m_spinnerInnerTextureId = 0;
+GLuint VideoLayerAndroid::m_posterTextureId = 0;
+GLuint VideoLayerAndroid::m_backgroundTextureId = 0;
+bool VideoLayerAndroid::m_createdTexture = false;
+
+double VideoLayerAndroid::m_rotateDegree = 0;
+
+const IntRect VideoLayerAndroid::buttonRect(0, 0, IMAGESIZE, IMAGESIZE);
+
+VideoLayerAndroid::VideoLayerAndroid()
+ : LayerAndroid((RenderLayer*)0)
+{
+ init();
+}
+
+VideoLayerAndroid::VideoLayerAndroid(const VideoLayerAndroid& layer)
+ : LayerAndroid(layer)
+{
+ init();
+}
+
+void VideoLayerAndroid::init()
+{
+ // m_surfaceTexture is only useful on UI thread, no need to copy.
+ // And it will be set at setBaseLayer timeframe
+
+ m_playerState = INITIALIZED;
+ m_textureId = 0;
+}
+
+// We can use this function to set the Layer to point to surface texture.
+void VideoLayerAndroid::setSurfaceTexture(sp<SurfaceTexture> texture,
+ int textureName, PlayerState playerState)
+{
+ m_surfaceTexture = texture;
+ m_textureId = textureName;
+
+ m_playerState = playerState;
+}
+
+GLuint VideoLayerAndroid::createSpinnerInnerTexture()
+{
+ return createTextureFromImage(RenderSkinMediaButton::SPINNER_INNER);
+}
+
+GLuint VideoLayerAndroid::createSpinnerOuterTexture()
+{
+ return createTextureFromImage(RenderSkinMediaButton::SPINNER_OUTER);
+}
+
+GLuint VideoLayerAndroid::createPosterTexture()
+{
+ return createTextureFromImage(RenderSkinMediaButton::VIDEO);
+}
+
+GLuint VideoLayerAndroid::createTextureFromImage(int buttonType)
+{
+ SkRect rect = SkRect(buttonRect);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height());
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+
+ SkCanvas canvas(bitmap);
+ canvas.drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
+ RenderSkinMediaButton::Draw(&canvas, buttonRect, buttonType, true);
+
+ GLuint texture;
+ glGenTextures(1, &texture);
+
+ GLUtils::createTextureWithBitmap(texture, bitmap);
+ bitmap.reset();
+ return texture;
+}
+
+GLuint VideoLayerAndroid::createBackgroundTexture()
+{
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ GLubyte pixels[4 *3] = {
+ 128, 128, 128,
+ 128, 128, 128,
+ 128, 128, 128,
+ 128, 128, 128
+ };
+ glBindTexture(GL_TEXTURE_2D, texture);
+ GLUtils::checkGlError("glBindTexture");
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
+ GLUtils::checkGlError("glTexImage2D");
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ return texture;
+}
+
+bool VideoLayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
+{
+ // Lazily allocated the textures.
+ if (!m_createdTexture) {
+ m_backgroundTextureId = createBackgroundTexture();
+ m_spinnerOuterTextureId = createSpinnerOuterTexture();
+ m_spinnerInnerTextureId = createSpinnerInnerTexture();
+ m_posterTextureId = createPosterTexture();
+ m_createdTexture = true;
+ }
+
+ SkRect rect = SkRect::MakeSize(getSize());
+ GLfloat surfaceMatrix[16];
+
+ SkRect innerRect = SkRect(buttonRect);
+ if (innerRect.contains(rect))
+ innerRect = rect;
+
+ innerRect.offset((rect.width() - IMAGESIZE) / 2 , (rect.height() - IMAGESIZE) / 2);
+
+ // Draw the poster image, the progressing image or the Video depending
+ // on the player's state.
+ if (m_playerState == PREPARING) {
+ // Show the progressing animation, with two rotating circles
+ TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), rect,
+ m_backgroundTextureId,
+ 0.5, true);
+
+ TransformationMatrix addReverseRotation;
+ TransformationMatrix addRotation = drawTransform();
+ addRotation.translate(innerRect.fLeft, innerRect.fTop);
+ addRotation.translate(IMAGESIZE / 2, IMAGESIZE / 2);
+ addReverseRotation = addRotation;
+ addRotation.rotate(m_rotateDegree);
+ addRotation.translate(-IMAGESIZE / 2, -IMAGESIZE / 2);
+
+ SkRect size = SkRect::MakeWH(innerRect.width(), innerRect.height());
+ TilesManager::instance()->shader()->drawLayerQuad(addRotation, size,
+ m_spinnerOuterTextureId,
+ 1, true);
+
+ addReverseRotation.rotate(-m_rotateDegree);
+ addReverseRotation.translate(-IMAGESIZE / 2, -IMAGESIZE / 2);
+
+ TilesManager::instance()->shader()->drawLayerQuad(addReverseRotation, size,
+ m_spinnerInnerTextureId,
+ 1, true);
+
+ m_rotateDegree += ROTATESTEP;
+
+ } else if (m_playerState == PLAYING && m_surfaceTexture.get()) {
+ // Show the real video.
+ m_surfaceTexture->updateTexImage();
+ m_surfaceTexture->getTransformMatrix(surfaceMatrix);
+ TilesManager::instance()->shader()->drawVideoLayerQuad(drawTransform(),
+ surfaceMatrix,
+ rect, m_textureId);
+ } else {
+ // Show the poster
+ TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), rect,
+ m_backgroundTextureId,
+ 0.5, true);
+
+ TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), innerRect,
+ m_posterTextureId,
+ 1, true);
+ }
+
+ return drawChildrenGL(glWebViewState, matrix);
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/Source/WebCore/platform/graphics/android/VideoLayerAndroid.h b/Source/WebCore/platform/graphics/android/VideoLayerAndroid.h
new file mode 100644
index 0000000..eac565e
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/VideoLayerAndroid.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 VideoLayerAndroid_h
+#define VideoLayerAndroid_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "GLUtils.h"
+#include "LayerAndroid.h"
+#include <jni.h>
+
+namespace android {
+class SurfaceTexture;
+}
+
+namespace WebCore {
+
+// state get from UI thread to decide which image to draw.
+// PREPARING should be the progressing image
+// PLAYING will be the Video (Surface Texture).
+// Otherwise will draw a static image.
+// NOTE: These values are matching the ones in HTML5VideoView.java
+// Please keep them in sync when changed here.
+typedef enum {INITIALIZED, PREPARING, PREPARED, PLAYING} PlayerState;
+
+class VideoLayerAndroid : public LayerAndroid {
+
+public:
+ VideoLayerAndroid();
+ explicit VideoLayerAndroid(const VideoLayerAndroid& layer);
+
+ virtual bool isVideo() const { return true; }
+ virtual LayerAndroid* copy() const { return new VideoLayerAndroid(*this); }
+
+ // The following 3 functions are called in UI thread only.
+ virtual bool drawGL(GLWebViewState*, SkMatrix& matrix);
+ void setSurfaceTexture(sp<SurfaceTexture> texture, int textureName, PlayerState playerState);
+ GLuint createBackgroundTexture();
+ GLuint createSpinnerOuterTexture();
+ GLuint createSpinnerInnerTexture();
+ GLuint createPosterTexture();
+
+private:
+ GLuint createTextureFromImage(int buttonType);
+ void init();
+ // Surface texture for showing the video is actually allocated in Java side
+ // and passed into this native code.
+ GLuint m_textureId;
+ sp<android::SurfaceTexture> m_surfaceTexture;
+
+ PlayerState m_playerState;
+
+ // Texture for showing the static image will be created at native side.
+ // TODO: instead using a shared texture, we could make a texture pool to
+ // show different screen shots for different videos
+ static bool m_createdTexture;
+ static GLuint m_backgroundTextureId;
+ static GLuint m_posterTextureId;
+ static GLuint m_spinnerOuterTextureId;
+ static GLuint m_spinnerInnerTextureId;
+
+ static double m_rotateDegree;
+
+ static const int ROTATESTEP = 12;
+ static const int IMAGESIZE = 64;
+ static const IntRect buttonRect;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif // VideoLayerAndroid_h
diff --git a/Source/WebCore/platform/graphics/android/VideoListener.h b/Source/WebCore/platform/graphics/android/VideoListener.h
new file mode 100644
index 0000000..7cac6d6
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/VideoListener.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VideoListener_h
+#define VideoListener_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include <gui/SurfaceTexture.h>
+#include <jni.h>
+#include <JNIUtility.h>
+#include "WebCoreJni.h"
+
+#ifdef DEBUG
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "VideoListener", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+class VideoListener : public android::SurfaceTexture::FrameAvailableListener {
+
+public:
+ VideoListener(jobject weakWebViewRef)
+ : m_weakWebViewRef(weakWebViewRef)
+ , m_postInvalMethod(0)
+ , m_frameAvailable(false)
+ {
+ if (!m_weakWebViewRef)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
+ if (localWebViewRef) {
+ jclass wvClass = env->GetObjectClass(localWebViewRef);
+ m_postInvalMethod = env->GetMethodID(wvClass, "postInvalidate", "()V");
+ env->DeleteLocalRef(wvClass);
+ env->DeleteLocalRef(localWebViewRef);
+ }
+ checkException(env);
+ }
+
+ virtual void onFrameAvailable()
+ {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
+ if (localWebViewRef) {
+ env->CallVoidMethod(localWebViewRef, m_postInvalMethod);
+ env->DeleteLocalRef(localWebViewRef);
+ }
+ checkException(env);
+ if (!m_frameAvailable) {
+ m_frameAvailable = true;
+ }
+ }
+
+ void resetFrameAvailable() { m_frameAvailable = false; }
+ bool isFrameAvailable() { return m_frameAvailable; }
+
+private:
+ jobject m_weakWebViewRef;
+ jmethodID m_postInvalMethod;
+ bool m_frameAvailable;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // VideoListener_h
diff --git a/Source/WebCore/platform/graphics/android/android_graphics.cpp b/Source/WebCore/platform/graphics/android/android_graphics.cpp
new file mode 100644
index 0000000..e50cfec
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/android_graphics.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "CachedPrefix.h"
+#include "android_graphics.h"
+#include "CachedColor.h"
+#include "CachedRoot.h"
+#include "IntRect.h"
+#include "LayerAndroid.h"
+#include "SkCanvas.h"
+#include "SkCornerPathEffect.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "WebViewCore.h"
+
+namespace android {
+
+// The CSS values for the inner and outer widths may be specified as fractions
+#define WIDTH_SCALE 0.0625f // 1/16, to offset the scale in CSSStyleSelector
+
+void CursorRing::draw(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval)
+{
+ if (!m_lastBounds.isEmpty()) {
+ *inval = m_lastBounds;
+ m_lastBounds = IntRect(0, 0, 0, 0);
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ int layerId = m_node->isInLayer() ? m_frame->layer(m_node)->uniqueId() : -1;
+ if (layer->uniqueId() != layerId)
+ return;
+#endif
+ if (canvas->quickReject(m_bounds, SkCanvas::kAA_EdgeType)) {
+ DBG_NAV_LOGD("canvas->quickReject cursorNode=%d (nodePointer=%p)"
+ " bounds=(%d,%d,w=%d,h=%d)", m_node->index(), m_node->nodePointer(),
+ m_bounds.x(), m_bounds.y(), m_bounds.width(), m_bounds.height());
+ return;
+ }
+ const CachedColor& colors = m_frame->color(m_node);
+ unsigned rectCount = m_rings.size();
+ SkRegion rgn;
+ SkPath path;
+ for (unsigned i = 0; i < rectCount; i++)
+ {
+ SkRect r(m_rings[i]);
+ SkIRect ir;
+
+ r.round(&ir);
+ ir.inset(-colors.outset(), -colors.outset());
+ rgn.op(ir, SkRegion::kUnion_Op);
+ }
+ rgn.getBoundaryPath(&path);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setPathEffect(new SkCornerPathEffect(
+ SkIntToScalar(colors.radius())))->unref();
+ SkColor outer;
+ SkColor inner;
+ if (m_isPressed) {
+ SkColor pressed;
+ pressed = colors.fillColor();
+ paint.setColor(pressed);
+ canvas->drawPath(path, paint);
+ outer = colors.pressedOuterColor();
+ inner = colors.pressedInnerColor();
+ } else {
+ outer = colors.selectedOuterColor();
+ inner = colors.selectedInnerColor();
+ }
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(colors.outerWidth() * WIDTH_SCALE);
+ paint.setColor(outer);
+ canvas->drawPath(path, paint);
+ paint.setStrokeWidth(colors.innerWidth() * WIDTH_SCALE);
+ paint.setColor(inner);
+ canvas->drawPath(path, paint);
+ SkRect localBounds, globalBounds;
+ localBounds = path.getBounds();
+ float width = std::max(colors.innerWidth(), colors.outerWidth());
+ width *= WIDTH_SCALE;
+ localBounds.inset(-width, -width);
+ const SkMatrix& matrix = canvas->getTotalMatrix();
+ matrix.mapRect(&globalBounds, localBounds);
+ SkIRect globalIBounds;
+ globalBounds.round(&globalIBounds);
+ m_lastBounds = globalIBounds;
+ inval->unite(m_lastBounds);
+}
+
+void CursorRing::setIsButton(const CachedNode* node)
+{
+ m_isButton = false;
+ m_viewImpl->gButtonMutex.lock();
+ // If this is a button drawn by us (rather than webkit) do not draw the
+ // cursor ring, since its cursor will be shown by a change in what we draw.
+ // Should be in sync with recordButtons, since that will be called
+ // before this.
+ if (m_viewImpl->m_buttons.size() > 0) {
+ WebCore::Node* cursorPointer = (WebCore::Node*) node->nodePointer();
+ Container* end = m_viewImpl->m_buttons.end();
+ for (Container* ptr = m_viewImpl->m_buttons.begin(); ptr != end; ptr++) {
+ if (ptr->matches(cursorPointer)) {
+ m_isButton = true;
+ break;
+ }
+ }
+ }
+ m_viewImpl->gButtonMutex.unlock();
+}
+
+bool CursorRing::setup()
+{
+ m_node->localCursorRings(m_frame, &m_rings);
+ if (!m_rings.size()) {
+ DBG_NAV_LOG("!rings.size()");
+ m_viewImpl->m_hasCursorBounds = false;
+ return false;
+ }
+ setIsButton(m_node);
+ m_bounds = m_node->localBounds(m_frame);
+ m_viewImpl->updateCursorBounds(m_root, m_frame, m_node);
+
+ bool useHitBounds = m_node->useHitBounds();
+ if (useHitBounds)
+ m_bounds = m_node->localHitBounds(m_frame);
+ if (useHitBounds || m_node->useBounds()) {
+ m_rings.clear();
+ m_rings.append(m_bounds);
+ }
+ m_absBounds = m_node->bounds(m_frame);
+ const CachedColor& colors = m_frame->color(m_node);
+ m_bounds.inflate(SkScalarCeil(colors.outerWidth()));
+ m_absBounds.inflate(SkScalarCeil(colors.outerWidth()));
+ if (!m_node->hasCursorRing() || (m_node->isPlugin() && m_node->isFocus()))
+ return false;
+#if DEBUG_NAV_UI
+ const WebCore::IntRect& ring = m_rings[0];
+ DBG_NAV_LOGD("cursorNode=%d (nodePointer=%p) pressed=%s rings=%d"
+ " (%d, %d, %d, %d) isPlugin=%s",
+ m_node->index(), m_node->nodePointer(),
+ m_isPressed ? "true" : "false",
+ m_rings.size(), ring.x(), ring.y(), ring.width(), ring.height(),
+ m_node->isPlugin() ? "true" : "false");
+ DBG_NAV_LOGD("[%d] inner=%d outer=%d outset=%d radius=%d"
+ " fill=0x%08x pin=0x%08x pout=0x%08x sin=0x%08x sout=0x%08x",
+ m_node->colorIndex(), colors.innerWidth(), colors.outerWidth(),
+ colors.outset(), colors.radius(), colors.fillColor(),
+ colors.pressedInnerColor(), colors.pressedOuterColor(),
+ colors.selectedInnerColor(), colors.selectedInnerColor());
+#endif
+ return true;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/android_graphics.h b/Source/WebCore/platform/graphics/android/android_graphics.h
new file mode 100644
index 0000000..89312b5
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/android_graphics.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 android_graphics_DEFINED
+#define android_graphics_DEFINED
+
+#include "DrawExtra.h"
+#include "IntRect.h"
+#include "SkTypes.h"
+#include "wtf/Vector.h"
+
+namespace WebCore {
+ class GraphicsContext;
+}
+
+SkCanvas* android_gc2canvas(GraphicsContext* gc);
+
+namespace android {
+
+class CachedFrame;
+class CachedNode;
+class CachedRoot;
+class WebViewCore;
+
+// Data and methods for cursor rings
+
+// used to inflate node cache entry
+#define CURSOR_RING_HIT_TEST_RADIUS 5
+
+class CursorRing : public DrawExtra {
+public:
+ CursorRing(WebViewCore* core) : m_viewImpl(core) {}
+ virtual ~CursorRing() {}
+ virtual void draw(SkCanvas* , LayerAndroid* , IntRect* );
+ void setIsButton(const CachedNode* );
+ bool setup();
+ WTF::Vector<IntRect>& rings() { return m_rings; }
+private:
+ friend class WebView;
+ WebViewCore* m_viewImpl; // copy for convenience
+ WTF::Vector<IntRect> m_rings;
+ IntRect m_bounds;
+ IntRect m_absBounds;
+ IntRect m_lastBounds;
+ const CachedRoot* m_root;
+ const CachedFrame* m_frame;
+ const CachedNode* m_node;
+ bool m_isButton;
+ bool m_isPressed;
+};
+
+}
+
+#endif