From 31dbc523d9ee6fd7d7e46c540b5f675eeb559ed7 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Mon, 9 May 2011 16:25:46 +0100 Subject: 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 --- .../platform/graphics/android/AndroidAnimation.cpp | 387 ++++++ .../platform/graphics/android/AndroidAnimation.h | 107 ++ .../android/BackedDoubleBufferedTexture.cpp | 293 ++++ .../graphics/android/BackedDoubleBufferedTexture.h | 146 ++ .../platform/graphics/android/BaseLayerAndroid.cpp | 381 ++++++ .../platform/graphics/android/BaseLayerAndroid.h | 76 ++ .../WebCore/platform/graphics/android/BaseTile.cpp | 490 +++++++ .../WebCore/platform/graphics/android/BaseTile.h | 151 +++ .../graphics/android/BitmapAllocatorAndroid.cpp | 71 + .../graphics/android/BitmapAllocatorAndroid.h | 58 + .../platform/graphics/android/ClassTracker.cpp | 74 ++ .../platform/graphics/android/ClassTracker.h | 51 + .../graphics/android/DeleteTextureOperation.h | 61 + .../graphics/android/DoubleBufferedTexture.cpp | 180 +++ .../graphics/android/DoubleBufferedTexture.h | 73 + .../platform/graphics/android/FontAndroid.cpp | 1043 +++++++++++++++ .../platform/graphics/android/FontCacheAndroid.cpp | 180 +++ .../graphics/android/FontCustomPlatformData.cpp | 89 ++ .../graphics/android/FontCustomPlatformData.h | 59 + .../platform/graphics/android/FontDataAndroid.cpp | 133 ++ .../platform/graphics/android/FontPlatformData.h | 123 ++ .../graphics/android/FontPlatformDataAndroid.cpp | 235 ++++ .../WebCore/platform/graphics/android/GLUtils.cpp | 424 ++++++ Source/WebCore/platform/graphics/android/GLUtils.h | 74 ++ .../platform/graphics/android/GLWebViewState.cpp | 574 ++++++++ .../platform/graphics/android/GLWebViewState.h | 300 +++++ .../platform/graphics/android/GlyphMapAndroid.cpp | 86 ++ .../platform/graphics/android/GradientAndroid.cpp | 128 ++ .../graphics/android/GraphicsContextAndroid.cpp | 1334 +++++++++++++++++++ .../graphics/android/GraphicsLayerAndroid.cpp | 989 ++++++++++++++ .../graphics/android/GraphicsLayerAndroid.h | 163 +++ .../platform/graphics/android/HarfbuzzSkia.cpp | 220 +++ .../platform/graphics/android/HarfbuzzSkia.h | 40 + .../platform/graphics/android/ImageAndroid.cpp | 369 ++++++ .../graphics/android/ImageBufferAndroid.cpp | 253 ++++ .../platform/graphics/android/ImageBufferData.h | 40 + .../graphics/android/ImageSourceAndroid.cpp | 505 +++++++ .../platform/graphics/android/LayerAndroid.cpp | 1402 ++++++++++++++++++++ .../platform/graphics/android/LayerAndroid.h | 384 ++++++ .../platform/graphics/android/LayerTexture.cpp | 71 + .../platform/graphics/android/LayerTexture.h | 76 ++ .../platform/graphics/android/MediaLayer.cpp | 152 +++ .../WebCore/platform/graphics/android/MediaLayer.h | 77 ++ .../graphics/android/MediaPlayerPrivateAndroid.h | 142 ++ .../platform/graphics/android/MediaTexture.cpp | 274 ++++ .../platform/graphics/android/MediaTexture.h | 90 ++ .../graphics/android/PaintLayerOperation.cpp | 78 ++ .../graphics/android/PaintLayerOperation.h | 74 ++ .../graphics/android/PaintTileOperation.cpp | 81 ++ .../platform/graphics/android/PaintTileOperation.h | 47 + .../platform/graphics/android/PathAndroid.cpp | 341 +++++ .../platform/graphics/android/PatternAndroid.cpp | 70 + .../graphics/android/PlatformGraphicsContext.cpp | 74 ++ .../graphics/android/PlatformGraphicsContext.h | 165 +++ .../platform/graphics/android/QueuedOperation.h | 85 ++ .../graphics/android/ScrollableLayerAndroid.cpp | 39 + .../graphics/android/ScrollableLayerAndroid.h | 68 + .../platform/graphics/android/ShaderProgram.cpp | 446 +++++++ .../platform/graphics/android/ShaderProgram.h | 107 ++ .../graphics/android/SharedBufferStream.cpp | 53 + .../platform/graphics/android/SharedBufferStream.h | 55 + .../platform/graphics/android/SharedTexture.cpp | 221 +++ .../platform/graphics/android/SharedTexture.h | 137 ++ .../platform/graphics/android/SkBitmapRef.h | 62 + .../platform/graphics/android/TextureOwner.h | 43 + .../graphics/android/TexturesGenerator.cpp | 201 +++ .../platform/graphics/android/TexturesGenerator.h | 73 + .../platform/graphics/android/TiledPage.cpp | 355 +++++ .../WebCore/platform/graphics/android/TiledPage.h | 114 ++ .../platform/graphics/android/TilesManager.cpp | 440 ++++++ .../platform/graphics/android/TilesManager.h | 162 +++ .../graphics/android/VideoLayerAndroid.cpp | 220 +++ .../platform/graphics/android/VideoLayerAndroid.h | 96 ++ .../platform/graphics/android/VideoListener.h | 92 ++ .../platform/graphics/android/android_graphics.cpp | 176 +++ .../platform/graphics/android/android_graphics.h | 76 ++ 76 files changed, 16879 insertions(+) create mode 100644 Source/WebCore/platform/graphics/android/AndroidAnimation.cpp create mode 100644 Source/WebCore/platform/graphics/android/AndroidAnimation.h create mode 100644 Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.cpp create mode 100644 Source/WebCore/platform/graphics/android/BackedDoubleBufferedTexture.h create mode 100644 Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/BaseLayerAndroid.h create mode 100644 Source/WebCore/platform/graphics/android/BaseTile.cpp create mode 100644 Source/WebCore/platform/graphics/android/BaseTile.h create mode 100644 Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/BitmapAllocatorAndroid.h create mode 100644 Source/WebCore/platform/graphics/android/ClassTracker.cpp create mode 100644 Source/WebCore/platform/graphics/android/ClassTracker.h create mode 100644 Source/WebCore/platform/graphics/android/DeleteTextureOperation.h create mode 100644 Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp create mode 100644 Source/WebCore/platform/graphics/android/DoubleBufferedTexture.h create mode 100644 Source/WebCore/platform/graphics/android/FontAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/FontCacheAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/FontCustomPlatformData.cpp create mode 100644 Source/WebCore/platform/graphics/android/FontCustomPlatformData.h create mode 100644 Source/WebCore/platform/graphics/android/FontDataAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/FontPlatformData.h create mode 100644 Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/GLUtils.cpp create mode 100644 Source/WebCore/platform/graphics/android/GLUtils.h create mode 100644 Source/WebCore/platform/graphics/android/GLWebViewState.cpp create mode 100644 Source/WebCore/platform/graphics/android/GLWebViewState.h create mode 100644 Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/GradientAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h create mode 100644 Source/WebCore/platform/graphics/android/HarfbuzzSkia.cpp create mode 100644 Source/WebCore/platform/graphics/android/HarfbuzzSkia.h create mode 100644 Source/WebCore/platform/graphics/android/ImageAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/ImageBufferData.h create mode 100644 Source/WebCore/platform/graphics/android/ImageSourceAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/LayerAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/LayerAndroid.h create mode 100644 Source/WebCore/platform/graphics/android/LayerTexture.cpp create mode 100644 Source/WebCore/platform/graphics/android/LayerTexture.h create mode 100644 Source/WebCore/platform/graphics/android/MediaLayer.cpp create mode 100644 Source/WebCore/platform/graphics/android/MediaLayer.h create mode 100644 Source/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h create mode 100644 Source/WebCore/platform/graphics/android/MediaTexture.cpp create mode 100644 Source/WebCore/platform/graphics/android/MediaTexture.h create mode 100644 Source/WebCore/platform/graphics/android/PaintLayerOperation.cpp create mode 100644 Source/WebCore/platform/graphics/android/PaintLayerOperation.h create mode 100644 Source/WebCore/platform/graphics/android/PaintTileOperation.cpp create mode 100644 Source/WebCore/platform/graphics/android/PaintTileOperation.h create mode 100644 Source/WebCore/platform/graphics/android/PathAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/PatternAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp create mode 100644 Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h create mode 100644 Source/WebCore/platform/graphics/android/QueuedOperation.h create mode 100644 Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h create mode 100644 Source/WebCore/platform/graphics/android/ShaderProgram.cpp create mode 100644 Source/WebCore/platform/graphics/android/ShaderProgram.h create mode 100644 Source/WebCore/platform/graphics/android/SharedBufferStream.cpp create mode 100644 Source/WebCore/platform/graphics/android/SharedBufferStream.h create mode 100644 Source/WebCore/platform/graphics/android/SharedTexture.cpp create mode 100644 Source/WebCore/platform/graphics/android/SharedTexture.h create mode 100644 Source/WebCore/platform/graphics/android/SkBitmapRef.h create mode 100644 Source/WebCore/platform/graphics/android/TextureOwner.h create mode 100644 Source/WebCore/platform/graphics/android/TexturesGenerator.cpp create mode 100644 Source/WebCore/platform/graphics/android/TexturesGenerator.h create mode 100644 Source/WebCore/platform/graphics/android/TiledPage.cpp create mode 100644 Source/WebCore/platform/graphics/android/TiledPage.h create mode 100644 Source/WebCore/platform/graphics/android/TilesManager.cpp create mode 100644 Source/WebCore/platform/graphics/android/TilesManager.h create mode 100644 Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp create mode 100644 Source/WebCore/platform/graphics/android/VideoLayerAndroid.h create mode 100644 Source/WebCore/platform/graphics/android/VideoListener.h create mode 100644 Source/WebCore/platform/graphics/android/android_graphics.cpp create mode 100644 Source/WebCore/platform/graphics/android/android_graphics.h (limited to 'Source/WebCore/platform/graphics') 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 + +#ifdef DEBUG + +#include +#include + +#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(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(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(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(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::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 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::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 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 { + public: + AndroidAnimation(AnimatedPropertyID type, + const Animation* animation, + KeyframeValueList* operations, + double beginTime); + AndroidAnimation(AndroidAnimation* anim); + + virtual ~AndroidAnimation(); + virtual PassRefPtr 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 m_timingFunction; + String m_name; + AnimatedPropertyID m_type; + KeyframeValueList* m_operations; + LayerAndroid* m_originalLayer; +}; + +class AndroidOpacityAnimation : public AndroidAnimation { + public: + static PassRefPtr create(const Animation* animation, + KeyframeValueList* operations, + double beginTime); + AndroidOpacityAnimation(const Animation* animation, + KeyframeValueList* operations, + double beginTime); + AndroidOpacityAnimation(AndroidOpacityAnimation* anim); + virtual PassRefPtr copy(); + + virtual bool evaluate(LayerAndroid* layer, double time); +}; + +class AndroidTransformAnimation : public AndroidAnimation { + public: + static PassRefPtr create( + const Animation* animation, + KeyframeValueList* operations, + double beginTime); + AndroidTransformAnimation(const Animation* animation, + KeyframeValueList* operations, + double beginTime); + + AndroidTransformAnimation(AndroidTransformAnimation* anim); + virtual PassRefPtr 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 + +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 + +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 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 +#include +#endif // USE(ACCELERATED_COMPOSITING) + +#ifdef DEBUG + +#include +#include + +#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 +#include +#include +#include +#include + +#ifdef DEBUG + +#include +#include +#include + +#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(realTileRect.fLeft) % static_cast(tileWidth); + finalRealRect.fTop = static_cast(realTileRect.fTop) % static_cast(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 +#include +#include +#include + +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 +#include +#include + +#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::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 +#include + +#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 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(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 + +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( + 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 + +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 +#include +#include +#include +#include +#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*, 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(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& normalizedRun, OwnArrayPtr& 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 m_normalizedRun; + OwnArrayPtr 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(&platformData); + m_item.font->userData = opaquePlatformData; +} + +HB_FontRec* TextRunWalker::allocHarfbuzzFont() +{ + HB_FontRec* font = reinterpret_cast(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& normalizedRun, OwnArrayPtr& 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(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*, 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(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(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 + +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& 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 +#include + +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 + +#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 { + public: + static PassRefPtr 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 m_harfbuzzFace; + + static SkTypeface* hashTableDeletedFontValue() { + return reinterpret_cast(-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(mTypeface); + } else { + h = SkTypeface::UniqueID(mTypeface); + } + + uint32_t sizeAsInt = *reinterpret_cast(&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(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 +#include +#include + +#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(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(""); + 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(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 +#include +#include +#include + +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 + +#include +#include + +#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& 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; ilock(); + 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(floorf(viewport.fLeft * invTileContentWidth)), + static_cast(floorf(viewport.fTop * invTileContentHeight)), + static_cast(ceilf(viewport.fRight * invTileContentWidth)), + static_cast(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(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(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 + +// 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& 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 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 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::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 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 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(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(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(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(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::kHasAlphaLayer_SaveFlag + | SkCanvas::kFullColorLayer_SaveFlag + | SkCanvas::kClipToLayer_SaveFlag)); + else + ASSERT(0); + } + } + + void applyAntiAliasedClipPaths(WTF::Vector& 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&, 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 +#include +#include + +#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::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(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& 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(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(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(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(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(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(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(const_cast(originalValue->timingFunction())); + FloatAnimationValue* value = new FloatAnimationValue(originalValue->keyTime(), + originalValue->value(), + timingFunction); + operationsList->insert(value); + } + + RefPtr 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(const_cast(originalValue->timingFunction())); + TransformAnimationValue* value = new TransformAnimationValue(originalValue->keyTime(), + originalValue->value(), + timingFunction); + operationsList->insert(value); + } + + RefPtr 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(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(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&); + 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 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(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), reinterpret_cast(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(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(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + OwnArrayPtr 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(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(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(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + OwnArrayPtr 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(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(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(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(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(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 + +//#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::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 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 ImageBuffer::copyImage() const +{ + ASSERT(context()); + SkCanvas* canvas = context()->platformContext()->mCanvas; + SkDevice* device = canvas->getDevice(); + const SkBitmap& orig = device->accessBitmap(false); + + SkBitmap copy; + orig.copyTo(©, orig.config()); + + SkBitmapRef* ref = new SkBitmapRef(copy); + RefPtr 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 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 imageCopy = copyImage(); + imageCopy->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); +} + +PassRefPtr 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 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 pngEncodedData; + pngEncodedData.append(pngStream.getStream(), pngStream.getOffset()); + Vector 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& 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 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& 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 + +#define LAYER_DEBUG // Add diagonals for debugging +#undef LAYER_DEBUG + +#include +#include +#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 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(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(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 prpAnim) +{ + RefPtr anim = prpAnim; + pair 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 > 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 > 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& 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* region) const +{ + SkRect local; + local.set(0, 0, std::numeric_limits::max(), + std::numeric_limits::max()); + clipInner(region, local); +} + +void LayerAndroid::clipInner(SkTDArray* 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& 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 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 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 +#include + +#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& 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 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* region) const; + const LayerAndroid* find(int* xPtr, int* yPtr, SkPicture* root) const; + const LayerAndroid* findById(int uniqueID) const + { + return const_cast(this)->findById(uniqueID); + } + LayerAndroid* findById(int uniqueID); + LayerAndroid* getChild(int index) const + { + return static_cast(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* 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, RefPtr > 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 +#include + +#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 + +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 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&) { } + 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 +#include +#include +#include +#include +#include "WebCoreJni.h" + +#define LAYER_DEBUG +#undef LAYER_DEBUG + +#ifdef DEBUG + +#include +#include + +#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() +{ + 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 +#include + +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 { + +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 m_surfaceTexture; + sp m_surfaceTextureClient; + sp 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(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(operation); + if (op->baseLayer() == m_baseLayer) + return true; + } + return false; +} + +bool PaintLayerTextureFilter::check(QueuedOperation* operation) +{ + if (operation->type() == QueuedOperation::PaintLayer) { + PaintLayerOperation* op = static_cast(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(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::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* 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* 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* 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 +#include +#include +#include + +#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 + +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 + +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 +#include +#include +#include +#include + +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 +#include +#include + +#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 + +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 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 +#include +#include + +#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(floorf(inval.x() * invTileContentWidth)); + const int firstDirtyTileY = static_cast(floorf(inval.y() * invTileContentHeight)); + const int lastDirtyTileX = static_cast(ceilf(inval.right() * invTileContentWidth)); + const int lastDirtyTileY = static_cast(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 + +#ifdef DEBUG + +#include +#include +#include + +#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( + android_atomic_acquire_load(reinterpret_cast(&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(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 + +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 m_textures; + Vector m_layersTextures; + + unsigned int m_layersMemoryUsage; + + int m_maxTextureCount; + bool m_expandedTileBounds; + + bool m_generatorReady; + + bool m_showVisualIndicator; + + sp 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 +#include + +#if USE(ACCELERATED_COMPOSITING) + +#ifdef DEBUG +#include +#include + +#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 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 + +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 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 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 +#include +#include +#include "WebCoreJni.h" + +#ifdef DEBUG + +#include +#include + +#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& rings() { return m_rings; } +private: + friend class WebView; + WebViewCore* m_viewImpl; // copy for convenience + WTF::Vector 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 -- cgit v1.1