diff options
Diffstat (limited to 'libs/hwui')
99 files changed, 9737 insertions, 3513 deletions
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp new file mode 100644 index 0000000..c1af5f5 --- /dev/null +++ b/libs/hwui/AmbientShadow.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <math.h> +#include <utils/Log.h> +#include <utils/Vector.h> + +#include "AmbientShadow.h" +#include "ShadowTessellator.h" +#include "Vertex.h" + +namespace android { +namespace uirenderer { + +/** + * Calculate the shadows as a triangle strips while alpha value as the + * shadow values. + * + * @param isCasterOpaque Whether the caster is opaque. + * @param vertices The shadow caster's polygon, which is represented in a Vector3 + * array. + * @param vertexCount The length of caster's polygon in terms of number of + * vertices. + * @param centroid3d The centroid of the shadow caster. + * @param heightFactor The factor showing the higher the object, the lighter the + * shadow. + * @param geomFactor The factor scaling the geometry expansion along the normal. + * + * @param shadowVertexBuffer Return an floating point array of (x, y, a) + * triangle strips mode. + */ +VertexBufferMode AmbientShadow::createAmbientShadow(bool isCasterOpaque, + const Vector3* vertices, int vertexCount, const Vector3& centroid3d, + float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { + const int rays = SHADOW_RAY_COUNT; + VertexBufferMode mode = kVertexBufferMode_OnePolyRingShadow; + // Validate the inputs. + if (vertexCount < 3 || heightFactor <= 0 || rays <= 0 + || geomFactor <= 0) { +#if DEBUG_SHADOW + ALOGW("Invalid input for createAmbientShadow(), early return!"); +#endif + return mode; // vertex buffer is empty, so any mode doesn't matter. + } + + Vector<Vector2> dir; // TODO: use C++11 unique_ptr + dir.setCapacity(rays); + float rayDist[rays]; + float rayHeight[rays]; + calculateRayDirections(rays, vertices, vertexCount, centroid3d, dir.editArray()); + + // Calculate the length and height of the points along the edge. + // + // The math here is: + // Intersect each ray (starting from the centroid) with the polygon. + for (int i = 0; i < rays; i++) { + int edgeIndex; + float edgeFraction; + float rayDistance; + calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex, + edgeFraction, rayDistance); + rayDist[i] = rayDistance; + if (edgeIndex < 0 || edgeIndex >= vertexCount) { +#if DEBUG_SHADOW + ALOGW("Invalid edgeIndex!"); +#endif + edgeIndex = 0; + } + float h1 = vertices[edgeIndex].z; + float h2 = vertices[((edgeIndex + 1) % vertexCount)].z; + rayHeight[i] = h1 + edgeFraction * (h2 - h1); + } + + // The output buffer length basically is roughly rays * layers, but since we + // need triangle strips, so we need to duplicate vertices to accomplish that. + AlphaVertex* shadowVertices = + shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT); + + // Calculate the vertex of the shadows. + // + // The math here is: + // Along the edges of the polygon, for each intersection point P (generated above), + // calculate the normal N, which should be perpendicular to the edge of the + // polygon (represented by the neighbor intersection points) . + // Shadow's vertices will be generated as : P + N * scale. + const Vector2 centroid2d = Vector2(centroid3d.x, centroid3d.y); + for (int rayIndex = 0; rayIndex < rays; rayIndex++) { + Vector2 normal(1.0f, 0.0f); + calculateNormal(rays, rayIndex, dir.array(), rayDist, normal); + + // The vertex should be start from rayDist[i] then scale the + // normalizeNormal! + Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] + + centroid2d; + + // outer ring of points, expanded based upon height of each ray intersection + float expansionDist = rayHeight[rayIndex] * heightFactor * + geomFactor; + AlphaVertex::set(&shadowVertices[rayIndex], + intersection.x + normal.x * expansionDist, + intersection.y + normal.y * expansionDist, + 0.0f); + + // inner ring of points + float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor); + AlphaVertex::set(&shadowVertices[rays + rayIndex], + intersection.x, + intersection.y, + opacity); + } + + // If caster isn't opaque, we need to to fill the umbra by storing the umbra's + // centroid in the innermost ring of vertices. + if (!isCasterOpaque) { + mode = kVertexBufferMode_TwoPolyRingShadow; + float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor); + AlphaVertex centroidXYA; + AlphaVertex::set(¢roidXYA, centroid2d.x, centroid2d.y, centroidAlpha); + for (int rayIndex = 0; rayIndex < rays; rayIndex++) { + shadowVertices[2 * rays + rayIndex] = centroidXYA; + } + } + +#if DEBUG_SHADOW + for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) { + ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, + shadowVertices[i].y, shadowVertices[i].alpha); + } +#endif + return mode; +} + +/** + * Generate an array of rays' direction vectors. + * To make sure the vertices generated are clockwise, the directions are from PI + * to -PI. + * + * @param rays The number of rays shooting out from the centroid. + * @param vertices Vertices of the polygon. + * @param vertexCount The number of vertices. + * @param centroid3d The centroid of the polygon. + * @param dir Return the array of ray vectors. + */ +void AmbientShadow::calculateRayDirections(const int rays, const Vector3* vertices, + const int vertexCount, const Vector3& centroid3d, Vector2* dir) { + // If we don't have enough rays, then fall back to the uniform distribution. + if (vertexCount * 2 > rays) { + float deltaAngle = 2 * M_PI / rays; + for (int i = 0; i < rays; i++) { + dir[i].x = cosf(M_PI - deltaAngle * i); + dir[i].y = sinf(M_PI - deltaAngle * i); + } + return; + } + + // If we have enough rays, then we assign each vertices a ray, and distribute + // the rest uniformly. + float rayThetas[rays]; + + const int uniformRayCount = rays - vertexCount; + const float deltaAngle = 2 * M_PI / uniformRayCount; + + // We have to generate all the vertices' theta anyway and we also need to + // find the minimal, so let's precompute it first. + // Since the incoming polygon is clockwise, we can find the dip to identify + // the minimal theta. + float polyThetas[vertexCount]; + int maxPolyThetaIndex = 0; + for (int i = 0; i < vertexCount; i++) { + polyThetas[i] = atan2(vertices[i].y - centroid3d.y, + vertices[i].x - centroid3d.x); + if (i > 0 && polyThetas[i] > polyThetas[i - 1]) { + maxPolyThetaIndex = i; + } + } + + // Both poly's thetas and uniform thetas are in decrease order(clockwise) + // from PI to -PI. + int polyThetaIndex = maxPolyThetaIndex; + float polyTheta = polyThetas[maxPolyThetaIndex]; + int uniformThetaIndex = 0; + float uniformTheta = M_PI; + for (int i = 0; i < rays; i++) { + // Compare both thetas and pick the smaller one and move on. + bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA; + if (polyTheta > uniformTheta || hasThetaCollision) { + if (hasThetaCollision) { + // Shift the uniformTheta to middle way between current polyTheta + // and next uniform theta. The next uniform theta can wrap around + // to exactly PI safely here. + // Note that neither polyTheta nor uniformTheta can be FLT_MAX + // due to the hasThetaCollision is true. + uniformTheta = (polyTheta + M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2; +#if DEBUG_SHADOW + ALOGD("Shifted uniformTheta to %f", uniformTheta); +#endif + } + rayThetas[i] = polyTheta; + polyThetaIndex = (polyThetaIndex + 1) % vertexCount; + if (polyThetaIndex != maxPolyThetaIndex) { + polyTheta = polyThetas[polyThetaIndex]; + } else { + // out of poly points. + polyTheta = - FLT_MAX; + } + } else { + rayThetas[i] = uniformTheta; + uniformThetaIndex++; + if (uniformThetaIndex < uniformRayCount) { + uniformTheta = M_PI - deltaAngle * uniformThetaIndex; + } else { + // out of uniform points. + uniformTheta = - FLT_MAX; + } + } + } + + for (int i = 0; i < rays; i++) { +#if DEBUG_SHADOW + ALOGD("No. %d : %f", i, rayThetas[i] * 180 / M_PI); +#endif + // TODO: Fix the intersection precision problem and remvoe the delta added + // here. + dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA); + dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA); + } +} + +/** + * Calculate the intersection of a ray hitting the polygon. + * + * @param vertices The shadow caster's polygon, which is represented in a + * Vector3 array. + * @param vertexCount The length of caster's polygon in terms of number of vertices. + * @param start The starting point of the ray. + * @param dir The direction vector of the ray. + * + * @param outEdgeIndex Return the index of the segment (or index of the starting + * vertex) that ray intersect with. + * @param outEdgeFraction Return the fraction offset from the segment starting + * index. + * @param outRayDist Return the ray distance from centroid to the intersection. + */ +void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount, + const Vector3& start, const Vector2& dir, int& outEdgeIndex, + float& outEdgeFraction, float& outRayDist) { + float startX = start.x; + float startY = start.y; + float dirX = dir.x; + float dirY = dir.y; + // Start the search from the last edge from poly[len-1] to poly[0]. + int p1 = vertexCount - 1; + + for (int p2 = 0; p2 < vertexCount; p2++) { + float p1x = vertices[p1].x; + float p1y = vertices[p1].y; + float p2x = vertices[p2].x; + float p2y = vertices[p2].y; + + // The math here is derived from: + // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0; + // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0; + float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x); + if (div != 0) { + float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div); + if (t > 0 && t <= 1) { + float t2 = (p1x * (startY - p2y) + + p2x * (p1y - startY) + + startX * (p2y - p1y)) / div; + if (t2 > 0) { + outEdgeIndex = p1; + outRayDist = t2; + outEdgeFraction = t; + return; + } + } + } + p1 = p2; + } + return; +}; + +/** + * Calculate the normal at the intersection point between a ray and the polygon. + * + * @param rays The total number of rays. + * @param currentRayIndex The index of the ray which the normal is based on. + * @param dir The array of the all the rays directions. + * @param rayDist The pre-computed ray distances array. + * + * @param normal Return the normal. + */ +void AmbientShadow::calculateNormal(int rays, int currentRayIndex, + const Vector2* dir, const float* rayDist, Vector2& normal) { + int preIndex = (currentRayIndex - 1 + rays) % rays; + int postIndex = (currentRayIndex + 1) % rays; + Vector2 p1 = dir[preIndex] * rayDist[preIndex]; + Vector2 p2 = dir[postIndex] * rayDist[postIndex]; + + // Now the rays are going CW around the poly. + Vector2 delta = p2 - p1; + if (delta.length() != 0) { + delta.normalize(); + // Calculate the normal , which is CCW 90 rotate to the delta. + normal.x = - delta.y; + normal.y = delta.x; + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h new file mode 100644 index 0000000..451bfbe --- /dev/null +++ b/libs/hwui/AmbientShadow.h @@ -0,0 +1,57 @@ + +/* + * Copyright (C) 2013 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 ANDROID_HWUI_AMBIENT_SHADOW_H +#define ANDROID_HWUI_AMBIENT_SHADOW_H + +#include "Debug.h" +#include "OpenGLRenderer.h" +#include "Vector.h" +#include "VertexBuffer.h" + +namespace android { +namespace uirenderer { + +/** + * AmbientShadow is used to calculate the ambient shadow value around a polygon. + * + * TODO: calculateIntersection() now is O(N*M), where N is the number of + * polygon's vertics and M is the number of rays. In fact, by staring tracing + * the vertex from the previous intersection, the algorithm can be O(N + M); + */ +class AmbientShadow { +public: + static VertexBufferMode createAmbientShadow(bool isCasterOpaque, const Vector3* poly, + int polyLength, const Vector3& centroid3d, float heightFactor, + float geomFactor, VertexBuffer& shadowVertexBuffer); + +private: + static void calculateRayDirections(const int rays, const Vector3* vertices, + const int vertexCount, const Vector3& centroid3d, Vector2* dir); + + static void calculateIntersection(const Vector3* poly, int nbVertices, + const Vector3& start, const Vector2& dir, int& outEdgeIndex, + float& outEdgeFraction, float& outRayDist); + + static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir, + const float* rayDist, Vector2& normal); +}; // AmbientShadow + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_AMBIENT_SHADOW_H diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 411c133..2cadf09 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -4,18 +4,22 @@ include $(CLEAR_VARS) # Only build libhwui when USE_OPENGL_RENDERER is # defined in the current device/board configuration ifeq ($(USE_OPENGL_RENDERER),true) - LOCAL_SRC_FILES:= \ + LOCAL_SRC_FILES := \ utils/Blur.cpp \ + utils/GLUtils.cpp \ utils/SortedListImpl.cpp \ thread/TaskManager.cpp \ font/CacheTexture.cpp \ font/Font.cpp \ + AmbientShadow.cpp \ + Animator.cpp \ AssetAtlas.cpp \ FontRenderer.cpp \ GammaFontRenderer.cpp \ Caches.cpp \ DisplayList.cpp \ DeferredDisplayList.cpp \ + DeferredLayerUpdater.cpp \ DisplayListLogBuffer.cpp \ DisplayListRenderer.cpp \ Dither.cpp \ @@ -23,6 +27,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) FboCache.cpp \ GradientCache.cpp \ Image.cpp \ + Interpolator.cpp \ Layer.cpp \ LayerCache.cpp \ LayerRenderer.cpp \ @@ -36,30 +41,41 @@ ifeq ($(USE_OPENGL_RENDERER),true) Program.cpp \ ProgramCache.cpp \ RenderBufferCache.cpp \ + RenderNode.cpp \ + RenderProperties.cpp \ ResourceCache.cpp \ - SkiaColorFilter.cpp \ + ShadowTessellator.cpp \ SkiaShader.cpp \ Snapshot.cpp \ + SpotShadow.cpp \ + StatefulBaseRenderer.cpp \ Stencil.cpp \ Texture.cpp \ TextureCache.cpp \ TextDropShadowCache.cpp +# RenderThread stuff + LOCAL_SRC_FILES += \ + renderthread/CanvasContext.cpp \ + renderthread/DrawFrameTask.cpp \ + renderthread/RenderProxy.cpp \ + renderthread/RenderTask.cpp \ + renderthread/RenderThread.cpp \ + renderthread/TimeLord.cpp + intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ $(LOCAL_PATH)/../../include/utils \ - external/skia/include/core \ - external/skia/include/effects \ - external/skia/include/images \ - external/skia/src/core \ - external/skia/src/ports \ - external/skia/include/utils + external/skia/src/core + + include external/stlport/libstlport.mk LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES + LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE_CLASS := SHARED_LIBRARIES - LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui + LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui LOCAL_MODULE := libhwui LOCAL_MODULE_TAGS := optional diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp new file mode 100644 index 0000000..6a3003e --- /dev/null +++ b/libs/hwui/Animator.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "RT-Animator" + +#include "Animator.h" + +#include <set> + +#include "RenderNode.h" +#include "RenderProperties.h" + +namespace android { +namespace uirenderer { + +/************************************************************ + * Base animator + ************************************************************/ + +BaseAnimator::BaseAnimator() + : mInterpolator(0) + , mPlayState(PENDING) + , mStartTime(0) + , mDuration(300) { + +} + +BaseAnimator::~BaseAnimator() { + setInterpolator(NULL); +} + +void BaseAnimator::setInterpolator(Interpolator* interpolator) { + delete mInterpolator; + mInterpolator = interpolator; +} + +void BaseAnimator::setDuration(nsecs_t duration) { + mDuration = duration; +} + +bool BaseAnimator::animateFrame(TreeInfo& info) { + if (mPlayState == PENDING) { + mPlayState = RUNNING; + mStartTime = info.frameTimeMs; + // No interpolator was set, use the default + if (!mInterpolator) { + setInterpolator(Interpolator::createDefaultInterpolator()); + } + onAnimationStarted(); + } + + float fraction = 1.0f; + if (mPlayState == RUNNING) { + fraction = mDuration > 0 ? (float)(info.frameTimeMs - mStartTime) / mDuration : 1.0f; + if (fraction >= 1.0f) { + fraction = 1.0f; + mPlayState = FINISHED; + } + } + fraction = mInterpolator->interpolate(fraction); + onAnimationUpdated(fraction); + + if (mPlayState == FINISHED) { + onAnimationFinished(); + callOnFinishedListener(info); + return true; + } + return false; +} + +void BaseAnimator::callOnFinishedListener(TreeInfo& info) { + if (mListener.get()) { + if (!info.animationHook) { + mListener->onAnimationFinished(this); + } else { + info.animationHook->callOnFinished(this, mListener.get()); + } + } +} + +/************************************************************ + * BaseRenderNodeAnimator + ************************************************************/ + +BaseRenderNodeAnimator::BaseRenderNodeAnimator( + BaseRenderNodeAnimator::DeltaValueType deltaType, float delta) + : mTarget(0) + , mDeltaValueType(deltaType) + , mDeltaValue(delta) + , mFromValue(-1) { +} + +bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) { + mTarget = target; + bool finished = animateFrame(info); + mTarget = NULL; + return finished; +} + +void BaseRenderNodeAnimator::onAnimationStarted() { + mFromValue = getValue(); + + if (mDeltaValueType == BaseRenderNodeAnimator::ABSOLUTE) { + mDeltaValue = (mDeltaValue - mFromValue); + mDeltaValueType = BaseRenderNodeAnimator::DELTA; + } +} + +void BaseRenderNodeAnimator::onAnimationUpdated(float fraction) { + float value = mFromValue + (mDeltaValue * fraction); + setValue(value); +} + +/************************************************************ + * RenderPropertyAnimator + ************************************************************/ + +// Maps RenderProperty enum to accessors +const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = { + {&RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, + {&RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, + {&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, + {&RenderProperties::getScaleX, &RenderProperties::setScaleX }, + {&RenderProperties::getScaleY, &RenderProperties::setScaleY }, + {&RenderProperties::getRotation, &RenderProperties::setRotation }, + {&RenderProperties::getRotationX, &RenderProperties::setRotationX }, + {&RenderProperties::getRotationY, &RenderProperties::setRotationY }, + {&RenderProperties::getX, &RenderProperties::setX }, + {&RenderProperties::getY, &RenderProperties::setY }, + {&RenderProperties::getZ, &RenderProperties::setZ }, + {&RenderProperties::getAlpha, &RenderProperties::setAlpha }, +}; + +RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, + DeltaValueType deltaType, float deltaValue) + : BaseRenderNodeAnimator(deltaType, deltaValue) + , mPropertyAccess(PROPERTY_ACCESSOR_LUT[property]) { +} + +float RenderPropertyAnimator::getValue() const { + return (target()->animatorProperties().*mPropertyAccess.getter)(); +} + +void RenderPropertyAnimator::setValue(float value) { + (target()->animatorProperties().*mPropertyAccess.setter)(value); +} + +/************************************************************ + * CanvasPropertyPrimitiveAnimator + ************************************************************/ + +CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator( + CanvasPropertyPrimitive* property, DeltaValueType deltaType, float deltaValue) + : BaseRenderNodeAnimator(deltaType, deltaValue) + , mProperty(property) { +} + +float CanvasPropertyPrimitiveAnimator::getValue() const { + return mProperty->value; +} + +void CanvasPropertyPrimitiveAnimator::setValue(float value) { + mProperty->value = value; +} + +/************************************************************ + * CanvasPropertySkPaintAnimator + ************************************************************/ + +CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator( + CanvasPropertyPaint* property, PaintField field, + DeltaValueType deltaType, float deltaValue) + : BaseRenderNodeAnimator(deltaType, deltaValue) + , mProperty(property) + , mField(field) { +} + +float CanvasPropertyPaintAnimator::getValue() const { + switch (mField) { + case STROKE_WIDTH: + return mProperty->value.getStrokeWidth(); + case ALPHA: + return mProperty->value.getAlpha(); + } + LOG_ALWAYS_FATAL("Unknown field %d", (int) mField); + return -1; +} + +void CanvasPropertyPaintAnimator::setValue(float value) { + switch (mField) { + case STROKE_WIDTH: + mProperty->value.setStrokeWidth(value); + return; + case ALPHA: + mProperty->value.setAlpha(value); + return; + } + LOG_ALWAYS_FATAL("Unknown field %d", (int) mField); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h new file mode 100644 index 0000000..0b074cc --- /dev/null +++ b/libs/hwui/Animator.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2014 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 ANIMATOR_H +#define ANIMATOR_H + +#include <cutils/compiler.h> +#include <utils/StrongPointer.h> + +#include "CanvasProperty.h" +#include "Interpolator.h" +#include "TreeInfo.h" +#include "utils/Macros.h" +#include "utils/VirtualLightRefBase.h" + +namespace android { +namespace uirenderer { + +class RenderNode; +class RenderProperties; + +class AnimationListener : public VirtualLightRefBase { +public: + ANDROID_API virtual void onAnimationFinished(BaseAnimator*) = 0; +protected: + ANDROID_API virtual ~AnimationListener() {} +}; + +// Helper class to contain generic animator helpers +class BaseAnimator : public VirtualLightRefBase { + PREVENT_COPY_AND_ASSIGN(BaseAnimator); +public: + + ANDROID_API void setInterpolator(Interpolator* interpolator); + ANDROID_API void setDuration(nsecs_t durationInMs); + ANDROID_API void setListener(AnimationListener* listener) { + mListener = listener; + } + + bool isFinished() { return mPlayState == FINISHED; } + +protected: + BaseAnimator(); + virtual ~BaseAnimator(); + + // This is the main animation entrypoint that subclasses should call + // to generate the onAnimation* lifecycle events + // Returns true if the animation has finished, false otherwise + bool animateFrame(TreeInfo& info); + + // Called when PlayState switches from PENDING to RUNNING + virtual void onAnimationStarted() {} + virtual void onAnimationUpdated(float fraction) = 0; + virtual void onAnimationFinished() {} + +private: + void callOnFinishedListener(TreeInfo& info); + + enum PlayState { + PENDING, + RUNNING, + FINISHED, + }; + + Interpolator* mInterpolator; + PlayState mPlayState; + long mStartTime; + long mDuration; + + sp<AnimationListener> mListener; +}; + +class BaseRenderNodeAnimator : public BaseAnimator { +public: + // Since the UI thread doesn't necessarily know what the current values + // actually are and thus can't do the calculations, this is used to inform + // the animator how to lazy-resolve the input value + enum DeltaValueType { + // The delta value represents an absolute value endpoint + // mDeltaValue needs to be recalculated to be mDelta = (mDelta - fromValue) + // in onAnimationStarted() + ABSOLUTE = 0, + // The final value represents an offset from the current value + // No recalculation is needed + DELTA, + }; + + bool animate(RenderNode* target, TreeInfo& info); + +protected: + BaseRenderNodeAnimator(DeltaValueType deltaType, float deltaValue); + + RenderNode* target() const { return mTarget; } + virtual float getValue() const = 0; + virtual void setValue(float value) = 0; + +private: + virtual void onAnimationStarted(); + virtual void onAnimationUpdated(float fraction); + + // mTarget is only valid inside animate() + RenderNode* mTarget; + + BaseRenderNodeAnimator::DeltaValueType mDeltaValueType; + float mDeltaValue; + float mFromValue; +}; + +class RenderPropertyAnimator : public BaseRenderNodeAnimator { +public: + enum RenderProperty { + TRANSLATION_X = 0, + TRANSLATION_Y, + TRANSLATION_Z, + SCALE_X, + SCALE_Y, + ROTATION, + ROTATION_X, + ROTATION_Y, + X, + Y, + Z, + ALPHA, + }; + + ANDROID_API RenderPropertyAnimator(RenderProperty property, + DeltaValueType deltaType, float deltaValue); + +protected: + ANDROID_API virtual float getValue() const; + ANDROID_API virtual void setValue(float value); + +private: + typedef void (RenderProperties::*SetFloatProperty)(float value); + typedef float (RenderProperties::*GetFloatProperty)() const; + + struct PropertyAccessors { + GetFloatProperty getter; + SetFloatProperty setter; + }; + + PropertyAccessors mPropertyAccess; + + static const PropertyAccessors PROPERTY_ACCESSOR_LUT[]; +}; + +class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator { +public: + ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property, + DeltaValueType deltaType, float deltaValue); +protected: + ANDROID_API virtual float getValue() const; + ANDROID_API virtual void setValue(float value); +private: + sp<CanvasPropertyPrimitive> mProperty; +}; + +class CanvasPropertyPaintAnimator : public BaseRenderNodeAnimator { +public: + enum PaintField { + STROKE_WIDTH = 0, + ALPHA, + }; + + ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, + PaintField field, DeltaValueType deltaType, float deltaValue); +protected: + ANDROID_API virtual float getValue() const; + ANDROID_API virtual void setValue(float value); +private: + sp<CanvasPropertyPaint> mProperty; + PaintField mField; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* ANIMATOR_H */ diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp index e8c3d3c..fc86e4f 100644 --- a/libs/hwui/AssetAtlas.cpp +++ b/libs/hwui/AssetAtlas.cpp @@ -74,12 +74,12 @@ void AssetAtlas::terminate() { // Entries /////////////////////////////////////////////////////////////////////////////// -AssetAtlas::Entry* AssetAtlas::getEntry(SkBitmap* const bitmap) const { +AssetAtlas::Entry* AssetAtlas::getEntry(const SkBitmap* bitmap) const { ssize_t index = mEntries.indexOfKey(bitmap); return index >= 0 ? mEntries.valueAt(index) : NULL; } -Texture* AssetAtlas::getEntryTexture(SkBitmap* const bitmap) const { +Texture* AssetAtlas::getEntryTexture(const SkBitmap* bitmap) const { ssize_t index = mEntries.indexOfKey(bitmap); return index >= 0 ? mEntries.valueAt(index)->texture : NULL; } diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h index 163bdbc..2ec556e 100644 --- a/libs/hwui/AssetAtlas.h +++ b/libs/hwui/AssetAtlas.h @@ -160,13 +160,13 @@ public: * Returns the entry in the atlas associated with the specified * bitmap. If the bitmap is not in the atlas, return NULL. */ - Entry* getEntry(SkBitmap* const bitmap) const; + Entry* getEntry(const SkBitmap* bitmap) const; /** * Returns the texture for the atlas entry associated with the * specified bitmap. If the bitmap is not in the atlas, return NULL. */ - Texture* getEntryTexture(SkBitmap* const bitmap) const; + Texture* getEntryTexture(const SkBitmap* bitmap) const; /** * Returns the current generation id of the atlas. @@ -186,7 +186,7 @@ private: const bool mBlendKey; const bool mOpaqueKey; - KeyedVector<SkBitmap*, Entry*> mEntries; + KeyedVector<const SkBitmap*, Entry*> mEntries; }; // class AssetAtlas }; // namespace uirenderer diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index f8d3589..43223ec 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -23,6 +23,7 @@ #include "DisplayListRenderer.h" #include "Properties.h" #include "LayerRenderer.h" +#include "ShadowTessellator.h" namespace android { @@ -55,6 +56,7 @@ Caches::Caches(): Singleton<Caches>(), initProperties(); initStaticProperties(); initExtensions(); + initTempProperties(); mDebugLevel = readDebugLevel(); ALOGD("Enabling debug mode %d", mDebugLevel); @@ -85,7 +87,7 @@ bool Caches::init() { mRegionMesh = NULL; mMeshIndices = 0; - + mShadowStripsIndices = 0; blend = false; lastSrcMode = GL_ZERO; lastDstMode = GL_ZERO; @@ -222,6 +224,9 @@ void Caches::terminate() { mMeshIndices = 0; mRegionMesh = NULL; + glDeleteBuffers(1, &mShadowStripsIndices); + mShadowStripsIndices = 0; + fboCache.clear(); programCache.clear(); @@ -310,24 +315,15 @@ void Caches::clearGarbage() { pathCache.clearGarbage(); patchCache.clearGarbage(); - Vector<DisplayList*> displayLists; Vector<Layer*> layers; { // scope for the lock Mutex::Autolock _l(mGarbageLock); - displayLists = mDisplayListGarbage; layers = mLayerGarbage; - mDisplayListGarbage.clear(); mLayerGarbage.clear(); } - size_t count = displayLists.size(); - for (size_t i = 0; i < count; i++) { - DisplayList* displayList = displayLists.itemAt(i); - delete displayList; - } - - count = layers.size(); + size_t count = layers.size(); for (size_t i = 0; i < count; i++) { Layer* layer = layers.itemAt(i); delete layer; @@ -340,11 +336,6 @@ void Caches::deleteLayerDeferred(Layer* layer) { mLayerGarbage.push(layer); } -void Caches::deleteDisplayListDeferred(DisplayList* displayList) { - Mutex::Autolock _l(mGarbageLock); - mDisplayListGarbage.push(displayList); -} - void Caches::flush(FlushMode mode) { FLUSH_LOGD("Flushing caches (mode %d)", mode); @@ -403,7 +394,7 @@ bool Caches::unbindMeshBuffer() { return false; } -bool Caches::bindIndicesBuffer(const GLuint buffer) { +bool Caches::bindIndicesBufferInternal(const GLuint buffer) { if (mCurrentIndicesBuffer != buffer) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); mCurrentIndicesBuffer = buffer; @@ -412,7 +403,7 @@ bool Caches::bindIndicesBuffer(const GLuint buffer) { return false; } -bool Caches::bindIndicesBuffer() { +bool Caches::bindQuadIndicesBuffer() { if (!mMeshIndices) { uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6]; for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) { @@ -427,7 +418,7 @@ bool Caches::bindIndicesBuffer() { } glGenBuffers(1, &mMeshIndices); - bool force = bindIndicesBuffer(mMeshIndices); + bool force = bindIndicesBufferInternal(mMeshIndices); glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t), regionIndices, GL_STATIC_DRAW); @@ -435,7 +426,23 @@ bool Caches::bindIndicesBuffer() { return force; } - return bindIndicesBuffer(mMeshIndices); + return bindIndicesBufferInternal(mMeshIndices); +} + +bool Caches::bindShadowIndicesBuffer() { + if (!mShadowStripsIndices) { + uint16_t* shadowIndices = new uint16_t[MAX_SHADOW_INDEX_COUNT]; + ShadowTessellator::generateShadowIndices(shadowIndices); + glGenBuffers(1, &mShadowStripsIndices); + bool force = bindIndicesBufferInternal(mShadowStripsIndices); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t), + shadowIndices, GL_STATIC_DRAW); + + delete[] shadowIndices; + return force; + } + + return bindIndicesBufferInternal(mShadowStripsIndices); } bool Caches::unbindIndicesBuffer() { @@ -473,7 +480,7 @@ bool Caches::unbindPixelBuffer() { // Meshes and textures /////////////////////////////////////////////////////////////////////////////// -void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride) { +void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) { GLuint slot = currentProgram->position; glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); @@ -482,7 +489,7 @@ void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei str } } -void Caches::bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride) { +void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) { GLuint slot = currentProgram->texCoords; glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); @@ -676,5 +683,49 @@ TextureVertex* Caches::getRegionMesh() { return mRegionMesh; } +/////////////////////////////////////////////////////////////////////////////// +// Temporary Properties +/////////////////////////////////////////////////////////////////////////////// + +void Caches::initTempProperties() { + propertyAmbientShadowStrength = 25; + propertySpotShadowStrength = 25; + + propertyLightDiameter = -1.0f; + propertyLightPosY = -1.0f; + propertyLightPosZ = -1.0f; + propertyAmbientRatio = -1.0f; +} + +void Caches::setTempProperty(const char* name, const char* value) { + ALOGD("setting property %s to %s", name, value); + if (!strcmp(name, "ambientShadowStrength")) { + propertyAmbientShadowStrength = atoi(value); + ALOGD("ambient shadow strength = 0x%x out of 0xff", propertyAmbientShadowStrength); + return; + } else if (!strcmp(name, "spotShadowStrength")) { + propertySpotShadowStrength = atoi(value); + ALOGD("spot shadow strength = 0x%x out of 0xff", propertySpotShadowStrength); + return; + } else if (!strcmp(name, "ambientRatio")) { + propertyAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0); + ALOGD("ambientRatio = %.2f", propertyAmbientRatio); + return; + } else if (!strcmp(name, "lightDiameter")) { + propertyLightDiameter = fmin(fmax(atof(value), 0.0), 3000.0); + ALOGD("lightDiameter = %.2f", propertyLightDiameter); + return; + } else if (!strcmp(name, "lightPosY")) { + propertyLightPosY = fmin(fmax(atof(value), 0.0), 3000.0); + ALOGD("lightPos Y = %.2f", propertyLightPosY); + return; + } else if (!strcmp(name, "lightPosZ")) { + propertyLightPosZ = fmin(fmax(atof(value), 0.0), 3000.0); + ALOGD("lightPos Z = %.2f", propertyLightPosZ); + return; + } + ALOGD(" failed"); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 282aee9..2e2ee15 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -62,7 +62,7 @@ namespace uirenderer { static const uint32_t gMaxNumberOfQuads = 2048; // Generates simple and textured vertices -#define FV(x, y, u, v) { { x, y }, { u, v } } +#define FV(x, y, u, v) { x, y, u, v } // This array is never used directly but used as a memcpy source in the // OpenGLRenderer constructor @@ -102,7 +102,7 @@ struct CacheLogger { // Caches /////////////////////////////////////////////////////////////////////////////// -class DisplayList; +class RenderNode; class ANDROID_API Caches: public Singleton<Caches> { Caches(); @@ -166,11 +166,6 @@ public: */ void deleteLayerDeferred(Layer* layer); - /* - * Can be used to delete a display list from a non EGL thread. - */ - void deleteDisplayListDeferred(DisplayList* layer); - /** * Binds the VBO used to render simple textured quads. */ @@ -190,8 +185,8 @@ public: * Binds a global indices buffer that can draw up to * gMaxNumberOfQuads quads. */ - bool bindIndicesBuffer(); - bool bindIndicesBuffer(const GLuint buffer); + bool bindQuadIndicesBuffer(); + bool bindShadowIndicesBuffer(); bool unbindIndicesBuffer(); /** @@ -208,13 +203,13 @@ public: * Binds an attrib to the specified float vertex pointer. * Assumes a stride of gMeshStride and a size of 2. */ - void bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride); + void bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride); /** * Binds an attrib to the specified float vertex pointer. * Assumes a stride of gMeshStride and a size of 2. */ - void bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride); + void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride); /** * Resets the vertex pointers. @@ -353,6 +348,17 @@ public: PFNGLLABELOBJECTEXTPROC setLabel; PFNGLGETOBJECTLABELEXTPROC getLabel; + // TEMPORARY properties + void initTempProperties(); + void setTempProperty(const char* name, const char* value); + + float propertyLightDiameter; + float propertyLightPosY; + float propertyLightPosZ; + float propertyAmbientRatio; + int propertyAmbientShadowStrength; + int propertySpotShadowStrength; + private: enum OverdrawColorSet { kColorSet_Default = 0, @@ -364,6 +370,8 @@ private: void initConstraints(); void initStaticProperties(); + bool bindIndicesBufferInternal(const GLuint buffer); + static void eventMarkNull(GLsizei length, const GLchar* marker) { } static void startMarkNull(GLsizei length, const GLchar* marker) { } static void endMarkNull() { } @@ -379,9 +387,9 @@ private: GLuint mCurrentBuffer; GLuint mCurrentIndicesBuffer; GLuint mCurrentPixelBuffer; - void* mCurrentPositionPointer; + const void* mCurrentPositionPointer; GLsizei mCurrentPositionStride; - void* mCurrentTexCoordsPointer; + const void* mCurrentTexCoordsPointer; GLsizei mCurrentTexCoordsStride; bool mTexCoordsArrayEnabled; @@ -400,10 +408,10 @@ private: // Global index buffer GLuint mMeshIndices; + GLuint mShadowStripsIndices; mutable Mutex mGarbageLock; Vector<Layer*> mLayerGarbage; - Vector<DisplayList*> mDisplayListGarbage; DebugLevel mDebugLevel; bool mInitialized; diff --git a/libs/hwui/CanvasProperty.h b/libs/hwui/CanvasProperty.h new file mode 100644 index 0000000..2e1d176 --- /dev/null +++ b/libs/hwui/CanvasProperty.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 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 CANVASPROPERTY_H +#define CANVASPROPERTY_H + +#include "utils/Macros.h" +#include "utils/VirtualLightRefBase.h" + +#include <SkPaint.h> + +namespace android { +namespace uirenderer { + +class CanvasPropertyPrimitive : public VirtualLightRefBase { + PREVENT_COPY_AND_ASSIGN(CanvasPropertyPrimitive); +public: + CanvasPropertyPrimitive(float initialValue) : value(initialValue) {} + + float value; +}; + +class CanvasPropertyPaint : public VirtualLightRefBase { + PREVENT_COPY_AND_ASSIGN(CanvasPropertyPaint); +public: + CanvasPropertyPaint(const SkPaint& initialValue) : value(initialValue) {} + + SkPaint value; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* CANVASPROPERTY_H */ diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 786f12a..d6dc6ad 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -85,6 +85,9 @@ // Turn on to highlight drawing batches and merged batches with different colors #define DEBUG_MERGE_BEHAVIOR 0 +// Turn on to enable debugging shadow +#define DEBUG_SHADOW 0 + #if DEBUG_INIT #define INIT_LOGD(...) ALOGD(__VA_ARGS__) #else diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 7eb7028..45b6624 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -190,7 +190,7 @@ public: // Overlapping other operations is only allowed for text without shadow. For other ops, // multiDraw isn't guaranteed to overdraw correctly - if (!isTextBatch || state->mDrawModifiers.mHasShadow) { + if (!isTextBatch || op->hasTextShadow()) { if (intersects(state->mBounds)) return false; } const DeferredDisplayState* lhs = state; @@ -224,6 +224,11 @@ public: if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false; + if (op->mPaint && mOps[0].op->mPaint && + op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) { + return false; + } + /* Draw Modifiers compatibility check * * Shadows are ignored, as only text uses them, and in that case they are drawn @@ -239,7 +244,6 @@ public: const DrawModifiers& lhsMod = lhs->mDrawModifiers; const DrawModifiers& rhsMod = rhs->mDrawModifiers; if (lhsMod.mShader != rhsMod.mShader) return false; - if (lhsMod.mColorFilter != rhsMod.mColorFilter) return false; // Draw filter testing expects bit fields to be clear if filter not set. if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false; diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 3dcbd0b..fca3588 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -79,13 +79,13 @@ public: }; class DeferredDisplayList { + friend class DeferStateStruct; // used to give access to allocator public: DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) : mBounds(bounds), mAvoidOverdraw(avoidOverdraw) { clear(); } ~DeferredDisplayList() { clear(); } - void reset(const Rect& bounds) { mBounds.set(bounds); } enum OpBatchId { kOpBatch_None = 0, // Don't batch @@ -120,6 +120,8 @@ public: void addDrawOp(OpenGLRenderer& renderer, DrawOp* op); private: + DeferredDisplayList(const DeferredDisplayList& other); // disallow copy + DeferredDisplayState* createState() { return new (mAllocator) DeferredDisplayState(); } diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp new file mode 100644 index 0000000..285c8c3 --- /dev/null +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2014 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 "DeferredLayerUpdater.h" + +#include "OpenGLRenderer.h" + +#include "LayerRenderer.h" + +namespace android { +namespace uirenderer { + +DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer) + : mDisplayList(0) + , mSurfaceTexture(0) + , mTransform(0) + , mNeedsGLContextAttach(false) + , mUpdateTexImage(false) + , mLayer(layer) + , mCaches(Caches::getInstance()) { + mWidth = mLayer->layer.getWidth(); + mHeight = mLayer->layer.getHeight(); + mBlend = mLayer->isBlend(); + mColorFilter = SkSafeRef(mLayer->getColorFilter()); + mAlpha = mLayer->getAlpha(); + mMode = mLayer->getMode(); + mDirtyRect.setEmpty(); +} + +DeferredLayerUpdater::~DeferredLayerUpdater() { + SkSafeUnref(mColorFilter); + setTransform(0); + if (mLayer) { + mCaches.resourceCache.decrementRefcount(mLayer); + } +} + +void DeferredLayerUpdater::setPaint(const SkPaint* paint) { + OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode); + SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : NULL; + SkRefCnt_SafeAssign(mColorFilter, colorFilter); +} + +void DeferredLayerUpdater::setDisplayList(RenderNode* displayList, + int left, int top, int right, int bottom) { + mDisplayList = displayList; + if (mDirtyRect.isEmpty()) { + mDirtyRect.set(left, top, right, bottom); + } else { + mDirtyRect.unionWith(Rect(left, top, right, bottom)); + } +} + +bool DeferredLayerUpdater::apply(TreeInfo& info) { + bool success = true; + // These properties are applied the same to both layer types + mLayer->setColorFilter(mColorFilter); + mLayer->setAlpha(mAlpha, mMode); + + if (mDisplayList.get()) { + if (mWidth != mLayer->layer.getWidth() || mHeight != mLayer->layer.getHeight()) { + success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight); + } + mLayer->setBlend(mBlend); + mDisplayList->prepareTree(info); + mLayer->updateDeferred(mDisplayList.get(), + mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom); + mDirtyRect.setEmpty(); + mDisplayList = 0; + } else if (mSurfaceTexture.get()) { + if (mNeedsGLContextAttach) { + mNeedsGLContextAttach = false; + mSurfaceTexture->attachToContext(mLayer->getTexture()); + } + if (mUpdateTexImage) { + mUpdateTexImage = false; + doUpdateTexImage(); + } + if (mTransform) { + mLayer->getTransform().load(*mTransform); + setTransform(0); + } + } + return success; +} + +void DeferredLayerUpdater::doUpdateTexImage() { + if (mSurfaceTexture->updateTexImage() == NO_ERROR) { + float transform[16]; + + int64_t frameNumber = mSurfaceTexture->getFrameNumber(); + // If the GLConsumer queue is in synchronous mode, need to discard all + // but latest frame, using the frame number to tell when we no longer + // have newer frames to target. Since we can't tell which mode it is in, + // do this unconditionally. + int dropCounter = 0; + while (mSurfaceTexture->updateTexImage() == NO_ERROR) { + int64_t newFrameNumber = mSurfaceTexture->getFrameNumber(); + if (newFrameNumber == frameNumber) break; + frameNumber = newFrameNumber; + dropCounter++; + } + + bool forceFilter = false; + sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer(); + if (buffer != NULL) { + // force filtration if buffer size != layer size + forceFilter = mWidth != buffer->getWidth() + || mHeight != buffer->getHeight(); + } + + #if DEBUG_RENDERER + if (dropCounter > 0) { + RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); + } + #endif + mSurfaceTexture->getTransformMatrix(transform); + GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); + + LayerRenderer::updateTextureLayer(mLayer, mWidth, mHeight, + !mBlend, forceFilter, renderTarget, transform); + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h new file mode 100644 index 0000000..cc62caa --- /dev/null +++ b/libs/hwui/DeferredLayerUpdater.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2014 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 DEFERREDLAYERUPDATE_H_ +#define DEFERREDLAYERUPDATE_H_ + +#include <cutils/compiler.h> +#include <gui/GLConsumer.h> +#include <SkColorFilter.h> +#include <SkMatrix.h> +#include <utils/StrongPointer.h> + +#include "Layer.h" +#include "OpenGLRenderer.h" +#include "Rect.h" +#include "RenderNode.h" + +namespace android { +namespace uirenderer { + +// Container to hold the properties a layer should be set to at the start +// of a render pass +class DeferredLayerUpdater { +public: + // Note that DeferredLayerUpdater assumes it is taking ownership of the layer + // and will not call incrementRef on it as a result. + ANDROID_API DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer = 0); + ANDROID_API ~DeferredLayerUpdater(); + + ANDROID_API bool setSize(uint32_t width, uint32_t height) { + if (mWidth != width || mHeight != height) { + mWidth = width; + mHeight = height; + return true; + } + return false; + } + + ANDROID_API bool setBlend(bool blend) { + if (blend != mBlend) { + mBlend = blend; + return true; + } + return false; + } + + ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& texture, bool needsAttach) { + if (texture.get() != mSurfaceTexture.get()) { + mNeedsGLContextAttach = needsAttach; + mSurfaceTexture = texture; + } + } + + ANDROID_API void updateTexImage() { + mUpdateTexImage = true; + } + + ANDROID_API void setTransform(const SkMatrix* matrix) { + delete mTransform; + mTransform = matrix ? new SkMatrix(*matrix) : 0; + } + + ANDROID_API void setDisplayList(RenderNode* displayList, + int left, int top, int right, int bottom); + + ANDROID_API void setPaint(const SkPaint* paint); + + ANDROID_API bool apply(TreeInfo& info); + + ANDROID_API Layer* backingLayer() { + return mLayer; + } + + ANDROID_API Layer* detachBackingLayer() { + Layer* layer = mLayer; + mLayer = 0; + return layer; + } + +private: + // Generic properties + uint32_t mWidth; + uint32_t mHeight; + bool mBlend; + SkColorFilter* mColorFilter; + int mAlpha; + SkXfermode::Mode mMode; + + // Layer type specific properties + // displayList and surfaceTexture are mutually exclusive, only 1 may be set + // dirtyRect is only valid if displayList is set + sp<RenderNode> mDisplayList; + Rect mDirtyRect; + sp<GLConsumer> mSurfaceTexture; + SkMatrix* mTransform; + bool mNeedsGLContextAttach; + bool mUpdateTexImage; + + Layer* mLayer; + Caches& mCaches; + + void doUpdateTexImage(); +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* DEFERREDLAYERUPDATE_H_ */ diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index bb6526e..dac86cb 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -14,7 +14,12 @@ * limitations under the License. */ +#define ATRACE_TAG ATRACE_TAG_VIEW + #include <SkCanvas.h> +#include <algorithm> + +#include <utils/Trace.h> #include "Debug.h" #include "DisplayList.h" @@ -24,533 +29,83 @@ namespace android { namespace uirenderer { -void DisplayList::outputLogBuffer(int fd) { - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); - if (logBuffer.isEmpty()) { - return; - } - - FILE *file = fdopen(fd, "a"); - - fprintf(file, "\nRecent DisplayList operations\n"); - logBuffer.outputCommands(file); - - String8 cachesLog; - Caches::getInstance().dumpMemoryUsage(cachesLog); - fprintf(file, "\nCaches:\n%s", cachesLog.string()); - fprintf(file, "\n"); - - fflush(file); +DisplayListData::DisplayListData() + : projectionReceiveIndex(-1) + , functorCount(0) + , hasDrawOps(false) { } -DisplayList::DisplayList(const DisplayListRenderer& recorder) : - mDestroyed(false), mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL), - mStaticMatrix(NULL), mAnimationMatrix(NULL) { - - initFromDisplayListRenderer(recorder); +DisplayListData::~DisplayListData() { + cleanupResources(); } -DisplayList::~DisplayList() { - mDestroyed = true; - clearResources(); -} - -void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) { - if (displayList) { - DISPLAY_LIST_LOGD("Deferring display list destruction"); - Caches::getInstance().deleteDisplayListDeferred(displayList); - } -} - -void DisplayList::clearResources() { - mDisplayListData = NULL; - - mClipRectOp = NULL; - mSaveLayerOp = NULL; - mSaveOp = NULL; - mRestoreToCountOp = NULL; - - delete mTransformMatrix; - delete mTransformCamera; - delete mTransformMatrix3D; - delete mStaticMatrix; - delete mAnimationMatrix; - - mTransformMatrix = NULL; - mTransformCamera = NULL; - mTransformMatrix3D = NULL; - mStaticMatrix = NULL; - mAnimationMatrix = NULL; - +void DisplayListData::cleanupResources() { Caches& caches = Caches::getInstance(); - caches.unregisterFunctors(mFunctorCount); + caches.unregisterFunctors(functorCount); caches.resourceCache.lock(); - for (size_t i = 0; i < mBitmapResources.size(); i++) { - caches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i)); - } - - for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) { - SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i); - caches.resourceCache.decrementRefcountLocked(bitmap); - caches.resourceCache.destructorLocked(bitmap); - } - - for (size_t i = 0; i < mFilterResources.size(); i++) { - caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i)); - } - - for (size_t i = 0; i < mPatchResources.size(); i++) { - caches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i)); - } - - for (size_t i = 0; i < mShaders.size(); i++) { - caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i)); - caches.resourceCache.destructorLocked(mShaders.itemAt(i)); - } - - for (size_t i = 0; i < mSourcePaths.size(); i++) { - caches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i)); - } - - for (size_t i = 0; i < mLayers.size(); i++) { - caches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i)); - } - - caches.resourceCache.unlock(); - - for (size_t i = 0; i < mPaints.size(); i++) { - delete mPaints.itemAt(i); - } - - for (size_t i = 0; i < mRegions.size(); i++) { - delete mRegions.itemAt(i); - } - - for (size_t i = 0; i < mPaths.size(); i++) { - delete mPaths.itemAt(i); - } - - for (size_t i = 0; i < mMatrices.size(); i++) { - delete mMatrices.itemAt(i); - } - - mBitmapResources.clear(); - mOwnedBitmapResources.clear(); - mFilterResources.clear(); - mPatchResources.clear(); - mShaders.clear(); - mSourcePaths.clear(); - mPaints.clear(); - mRegions.clear(); - mPaths.clear(); - mMatrices.clear(); - mLayers.clear(); -} - -void DisplayList::reset() { - clearResources(); - init(); -} - -void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) { - if (reusing) { - // re-using display list - clear out previous allocations - clearResources(); - } - - init(); - - mDisplayListData = recorder.getDisplayListData(); - mSize = mDisplayListData->allocator.usedSize(); - - if (mSize == 0) { - return; - } - - // allocate reusable ops for state-deferral - LinearAllocator& alloc = mDisplayListData->allocator; - mClipRectOp = new (alloc) ClipRectOp(); - mSaveLayerOp = new (alloc) SaveLayerOp(); - mSaveOp = new (alloc) SaveOp(); - mRestoreToCountOp = new (alloc) RestoreToCountOp(); - if (CC_UNLIKELY(!mSaveOp)) { // temporary debug logging - ALOGW("Error: %s's SaveOp not allocated, size %d", getName(), mSize); - CRASH(); - } - - mFunctorCount = recorder.getFunctorCount(); - - Caches& caches = Caches::getInstance(); - caches.registerFunctors(mFunctorCount); - caches.resourceCache.lock(); - - const Vector<SkBitmap*>& bitmapResources = recorder.getBitmapResources(); for (size_t i = 0; i < bitmapResources.size(); i++) { - SkBitmap* resource = bitmapResources.itemAt(i); - mBitmapResources.add(resource); - caches.resourceCache.incrementRefcountLocked(resource); + caches.resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i)); } - const Vector<SkBitmap*> &ownedBitmapResources = recorder.getOwnedBitmapResources(); for (size_t i = 0; i < ownedBitmapResources.size(); i++) { - SkBitmap* resource = ownedBitmapResources.itemAt(i); - mOwnedBitmapResources.add(resource); - caches.resourceCache.incrementRefcountLocked(resource); - } - - const Vector<SkiaColorFilter*>& filterResources = recorder.getFilterResources(); - for (size_t i = 0; i < filterResources.size(); i++) { - SkiaColorFilter* resource = filterResources.itemAt(i); - mFilterResources.add(resource); - caches.resourceCache.incrementRefcountLocked(resource); + const SkBitmap* bitmap = ownedBitmapResources.itemAt(i); + caches.resourceCache.decrementRefcountLocked(bitmap); + caches.resourceCache.destructorLocked(bitmap); } - const Vector<Res_png_9patch*>& patchResources = recorder.getPatchResources(); for (size_t i = 0; i < patchResources.size(); i++) { - Res_png_9patch* resource = patchResources.itemAt(i); - mPatchResources.add(resource); - caches.resourceCache.incrementRefcountLocked(resource); + caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i)); } - const Vector<SkiaShader*>& shaders = recorder.getShaders(); for (size_t i = 0; i < shaders.size(); i++) { - SkiaShader* resource = shaders.itemAt(i); - mShaders.add(resource); - caches.resourceCache.incrementRefcountLocked(resource); + caches.resourceCache.decrementRefcountLocked(shaders.itemAt(i)); + caches.resourceCache.destructorLocked(shaders.itemAt(i)); } - const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths(); for (size_t i = 0; i < sourcePaths.size(); i++) { - mSourcePaths.add(sourcePaths.itemAt(i)); - caches.resourceCache.incrementRefcountLocked(sourcePaths.itemAt(i)); + caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i)); } - const Vector<Layer*>& layers = recorder.getLayers(); for (size_t i = 0; i < layers.size(); i++) { - mLayers.add(layers.itemAt(i)); - caches.resourceCache.incrementRefcountLocked(layers.itemAt(i)); + caches.resourceCache.decrementRefcountLocked(layers.itemAt(i)); } caches.resourceCache.unlock(); - mPaints.appendVector(recorder.getPaints()); - mRegions.appendVector(recorder.getRegions()); - mPaths.appendVector(recorder.getPaths()); - mMatrices.appendVector(recorder.getMatrices()); -} - -void DisplayList::init() { - mSize = 0; - mIsRenderable = true; - mFunctorCount = 0; - mLeft = 0; - mTop = 0; - mRight = 0; - mBottom = 0; - mClipToBounds = true; - mAlpha = 1; - mHasOverlappingRendering = true; - mTranslationX = 0; - mTranslationY = 0; - mRotation = 0; - mRotationX = 0; - mRotationY= 0; - mScaleX = 1; - mScaleY = 1; - mPivotX = 0; - mPivotY = 0; - mCameraDistance = 0; - mMatrixDirty = false; - mMatrixFlags = 0; - mPrevWidth = -1; - mPrevHeight = -1; - mWidth = 0; - mHeight = 0; - mPivotExplicitlySet = false; - mCaching = false; -} - -size_t DisplayList::getSize() { - return mSize; -} - -/** - * This function is a simplified version of replay(), where we simply retrieve and log the - * display list. This function should remain in sync with the replay() function. - */ -void DisplayList::output(uint32_t level) { - ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this, - mName.string(), isRenderable()); - ALOGD("%*s%s %d", level * 2, "", "Save", - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); - - outputViewProperties(level); - int flags = DisplayListOp::kOpLogFlag_Recurse; - for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { - mDisplayListData->displayListOps[i]->output(level, flags); + for (size_t i = 0; i < paints.size(); i++) { + delete paints.itemAt(i); } - ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string()); -} - -float DisplayList::getPivotX() { - updateMatrix(); - return mPivotX; -} - -float DisplayList::getPivotY() { - updateMatrix(); - return mPivotY; -} - -void DisplayList::updateMatrix() { - if (mMatrixDirty) { - if (!mTransformMatrix) { - mTransformMatrix = new SkMatrix(); - } - if (mMatrixFlags == 0 || mMatrixFlags == TRANSLATION) { - mTransformMatrix->reset(); - } else { - if (!mPivotExplicitlySet) { - if (mWidth != mPrevWidth || mHeight != mPrevHeight) { - mPrevWidth = mWidth; - mPrevHeight = mHeight; - mPivotX = mPrevWidth / 2.0f; - mPivotY = mPrevHeight / 2.0f; - } - } - if ((mMatrixFlags & ROTATION_3D) == 0) { - mTransformMatrix->setTranslate(mTranslationX, mTranslationY); - mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY); - mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); - } else { - if (!mTransformCamera) { - mTransformCamera = new Sk3DView(); - mTransformMatrix3D = new SkMatrix(); - } - mTransformMatrix->reset(); - mTransformCamera->save(); - mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); - mTransformCamera->rotateX(mRotationX); - mTransformCamera->rotateY(mRotationY); - mTransformCamera->rotateZ(-mRotation); - mTransformCamera->getMatrix(mTransformMatrix3D); - mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY); - mTransformMatrix3D->postTranslate(mPivotX + mTranslationX, - mPivotY + mTranslationY); - mTransformMatrix->postConcat(*mTransformMatrix3D); - mTransformCamera->restore(); - } - } - mMatrixDirty = false; + for (size_t i = 0; i < regions.size(); i++) { + delete regions.itemAt(i); } -} -void DisplayList::outputViewProperties(const int level) { - updateMatrix(); - if (mLeft != 0 || mTop != 0) { - ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop); - } - if (mStaticMatrix) { - ALOGD("%*sConcatMatrix (static) %p: " MATRIX_STRING, - level * 2, "", mStaticMatrix, MATRIX_ARGS(mStaticMatrix)); - } - if (mAnimationMatrix) { - ALOGD("%*sConcatMatrix (animation) %p: " MATRIX_STRING, - level * 2, "", mAnimationMatrix, MATRIX_ARGS(mAnimationMatrix)); - } - if (mMatrixFlags != 0) { - if (mMatrixFlags == TRANSLATION) { - ALOGD("%*sTranslate %f, %f", level * 2, "", mTranslationX, mTranslationY); - } else { - ALOGD("%*sConcatMatrix %p: " MATRIX_STRING, - level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix)); - } + for (size_t i = 0; i < paths.size(); i++) { + delete paths.itemAt(i); } - bool clipToBoundsNeeded = mCaching ? false : mClipToBounds; - if (mAlpha < 1) { - if (mCaching) { - ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha); - } else if (!mHasOverlappingRendering) { - ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha); - } else { - int flags = SkCanvas::kHasAlphaLayer_SaveFlag; - if (clipToBoundsNeeded) { - flags |= SkCanvas::kClipToLayer_SaveFlag; - clipToBoundsNeeded = false; // clipping done by save layer - } - ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "", - (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop, - (int)(mAlpha * 255), flags); - } - } - if (clipToBoundsNeeded) { - ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f, - (float) mRight - mLeft, (float) mBottom - mTop); + for (size_t i = 0; i < matrices.size(); i++) { + delete matrices.itemAt(i); } -} -/* - * For property operations, we pass a savecount of 0, since the operations aren't part of the - * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in - * base saveCount (i.e., how RestoreToCount uses saveCount + mCount) - */ -#define PROPERTY_SAVECOUNT 0 - -template <class T> -void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, - const int level) { -#if DEBUG_DISPLAY_LIST - outputViewProperties(level); -#endif - updateMatrix(); - if (mLeft != 0 || mTop != 0) { - renderer.translate(mLeft, mTop); - } - if (mStaticMatrix) { - renderer.concatMatrix(mStaticMatrix); - } else if (mAnimationMatrix) { - renderer.concatMatrix(mAnimationMatrix); - } - if (mMatrixFlags != 0) { - if (mMatrixFlags == TRANSLATION) { - renderer.translate(mTranslationX, mTranslationY); - } else { - renderer.concatMatrix(mTransformMatrix); - } - } - bool clipToBoundsNeeded = mCaching ? false : mClipToBounds; - if (mAlpha < 1) { - if (mCaching) { - renderer.setOverrideLayerAlpha(mAlpha); - } else if (!mHasOverlappingRendering) { - renderer.scaleAlpha(mAlpha); - } else { - // TODO: should be able to store the size of a DL at record time and not - // have to pass it into this call. In fact, this information might be in the - // location/size info that we store with the new native transform data. - int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag; - if (clipToBoundsNeeded) { - saveFlags |= SkCanvas::kClipToLayer_SaveFlag; - clipToBoundsNeeded = false; // clipping done by saveLayer - } - handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, - mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT, - mClipToBounds); - } - } - if (clipToBoundsNeeded) { - handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op), - PROPERTY_SAVECOUNT, mClipToBounds); - } -} - -class DeferOperationHandler { -public: - DeferOperationHandler(DeferStateStruct& deferStruct, int level) - : mDeferStruct(deferStruct), mLevel(level) {} - inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { - operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); - } -private: - DeferStateStruct& mDeferStruct; - const int mLevel; -}; - -void DisplayList::defer(DeferStateStruct& deferStruct, const int level) { - DeferOperationHandler handler(deferStruct, level); - iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level); -} - -class ReplayOperationHandler { -public: - ReplayOperationHandler(ReplayStateStruct& replayStruct, int level) - : mReplayStruct(replayStruct), mLevel(level) {} - inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { -#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS - mReplayStruct.mRenderer.eventMark(operation->name()); -#endif - operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); - } -private: - ReplayStateStruct& mReplayStruct; - const int mLevel; -}; - -void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) { - ReplayOperationHandler handler(replayStruct, level); - - replayStruct.mRenderer.startMark(mName.string()); - iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level); - replayStruct.mRenderer.endMark(); - - DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(), - replayStruct.mDrawGlStatus); + bitmapResources.clear(); + ownedBitmapResources.clear(); + patchResources.clear(); + shaders.clear(); + sourcePaths.clear(); + paints.clear(); + regions.clear(); + paths.clear(); + matrices.clear(); + layers.clear(); } -/** - * This function serves both defer and replay modes, and will organize the displayList's component - * operations for a single frame: - * - * Every 'simple' operation that affects just the matrix and alpha (or other factors of - * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom - * defer logic) and operations in displayListOps are issued through the 'handler' which handles the - * defer vs replay logic, per operation - */ -template <class T> -void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) { - if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging - ALOGW("Error: %s is drawing after destruction, size %d", getName(), mSize); - CRASH(); - } - if (mSize == 0 || mAlpha <= 0) { - DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string()); - return; - } - -#if DEBUG_DISPLAY_LIST - Rect* clipRect = renderer.getClipRect(); - DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f", - level * 2, "", this, mName.string(), clipRect->left, clipRect->top, - clipRect->right, clipRect->bottom); -#endif - - int restoreTo = renderer.getSaveCount(); - handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), - PROPERTY_SAVECOUNT, mClipToBounds); - - DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); - - setViewProperties<T>(renderer, handler, level + 1); - - if (mClipToBounds && renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) { - DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); - handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds); - renderer.restoreToCount(restoreTo); - renderer.setOverrideLayerAlpha(1.0f); - return; - } - - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); - int saveCount = renderer.getSaveCount() - 1; - for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { - DisplayListOp *op = mDisplayListData->displayListOps[i]; - -#if DEBUG_DISPLAY_LIST - op->output(level + 1); -#endif - - logBuffer.writeCommand(level, op->name()); - handler(op, saveCount, mClipToBounds); - } +void DisplayListData::addChild(DrawDisplayListOp* op) { + LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawDisplayListOp with no render node!"); - DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); - handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds); - renderer.restoreToCount(restoreTo); - renderer.setOverrideLayerAlpha(1.0f); + mChildren.push(op); + mReferenceHolders.push(op->renderNode()); } }; // namespace uirenderer diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 1cd5f1c..eaeb772 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -26,6 +26,7 @@ #include <private/hwui/DrawGlInfo.h> +#include <utils/KeyedVector.h> #include <utils/LinearAllocator.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> @@ -37,12 +38,10 @@ #include <androidfw/ResourceTypes.h> #include "Debug.h" - -#define TRANSLATION 0x0001 -#define ROTATION 0x0002 -#define ROTATION_3D 0x0004 -#define SCALE 0x0008 -#define PIVOT 0x0010 +#include "Matrix.h" +#include "DeferredDisplayList.h" +#include "RenderProperties.h" +#include "utils/VirtualLightRefBase.h" class SkBitmap; class SkPaint; @@ -58,492 +57,105 @@ class DisplayListRenderer; class OpenGLRenderer; class Rect; class Layer; -class SkiaColorFilter; class SkiaShader; class ClipRectOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; +class DrawDisplayListOp; -struct DeferStateStruct { - DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) - : mDeferredList(deferredList), mRenderer(renderer), mReplayFlags(replayFlags) {} - DeferredDisplayList& mDeferredList; +/** + * Holds data used in the playback a tree of DisplayLists. + */ +class PlaybackStateStruct { +protected: + PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator) + : mRenderer(renderer), mReplayFlags(replayFlags), mAllocator(allocator){} + +public: OpenGLRenderer& mRenderer; const int mReplayFlags; + + // Allocator with the lifetime of a single frame. + // replay uses an Allocator owned by the struct, while defer shares the DeferredDisplayList's Allocator + LinearAllocator * const mAllocator; }; -struct ReplayStateStruct { - ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags) - : mRenderer(renderer), mDirty(dirty), mReplayFlags(replayFlags), - mDrawGlStatus(DrawGlInfo::kStatusDone) {} - OpenGLRenderer& mRenderer; - Rect& mDirty; - const int mReplayFlags; - status_t mDrawGlStatus; +class DeferStateStruct : public PlaybackStateStruct { +public: + DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) + : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)), + mDeferredList(deferredList) {} + + DeferredDisplayList& mDeferredList; }; -/** - * Refcounted structure that holds data used in display list stream - */ -class DisplayListData : public LightRefBase<DisplayListData> { +class ReplayStateStruct : public PlaybackStateStruct { public: - LinearAllocator allocator; - Vector<DisplayListOp*> displayListOps; + ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags) + : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator), + mDirty(dirty), mDrawGlStatus(DrawGlInfo::kStatusDone) {} + + Rect& mDirty; + status_t mDrawGlStatus; + LinearAllocator mReplayAllocator; }; /** - * Replays recorded drawing commands. + * Data structure that holds the list of commands used in display list stream */ -class DisplayList { +class DisplayListData { public: - DisplayList(const DisplayListRenderer& recorder); - ANDROID_API ~DisplayList(); - - // See flags defined in DisplayList.java - enum ReplayFlag { - kReplayFlag_ClipChildren = 0x1 - }; - - - ANDROID_API size_t getSize(); - ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList); - ANDROID_API static void outputLogBuffer(int fd); - - void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); + DisplayListData(); + ~DisplayListData(); - void defer(DeferStateStruct& deferStruct, const int level); - void replay(ReplayStateStruct& replayStruct, const int level); - - void output(uint32_t level = 0); - - ANDROID_API void reset(); - - void setRenderable(bool renderable) { - mIsRenderable = renderable; - } - - bool isRenderable() const { - return mIsRenderable; - } - - void setName(const char* name) { - if (name) { - char* lastPeriod = strrchr(name, '.'); - if (lastPeriod) { - mName.setTo(lastPeriod + 1); - } else { - mName.setTo(name); - } - } - } - - const char* getName() const { - return mName.string(); - } - - void setClipToBounds(bool clipToBounds) { - mClipToBounds = clipToBounds; - } - - void setStaticMatrix(SkMatrix* matrix) { - delete mStaticMatrix; - mStaticMatrix = new SkMatrix(*matrix); - } - - // Can return NULL - SkMatrix* getStaticMatrix() { - return mStaticMatrix; - } - - void setAnimationMatrix(SkMatrix* matrix) { - delete mAnimationMatrix; - if (matrix) { - mAnimationMatrix = new SkMatrix(*matrix); - } else { - mAnimationMatrix = NULL; - } - } - - void setAlpha(float alpha) { - alpha = fminf(1.0f, fmaxf(0.0f, alpha)); - if (alpha != mAlpha) { - mAlpha = alpha; - } - } - - float getAlpha() const { - return mAlpha; - } - - void setHasOverlappingRendering(bool hasOverlappingRendering) { - mHasOverlappingRendering = hasOverlappingRendering; - } - - bool hasOverlappingRendering() const { - return mHasOverlappingRendering; - } - - void setTranslationX(float translationX) { - if (translationX != mTranslationX) { - mTranslationX = translationX; - mMatrixDirty = true; - if (mTranslationX == 0.0f && mTranslationY == 0.0f) { - mMatrixFlags &= ~TRANSLATION; - } else { - mMatrixFlags |= TRANSLATION; - } - } - } - - float getTranslationX() const { - return mTranslationX; - } - - void setTranslationY(float translationY) { - if (translationY != mTranslationY) { - mTranslationY = translationY; - mMatrixDirty = true; - if (mTranslationX == 0.0f && mTranslationY == 0.0f) { - mMatrixFlags &= ~TRANSLATION; - } else { - mMatrixFlags |= TRANSLATION; - } - } - } - - float getTranslationY() const { - return mTranslationY; - } - - void setRotation(float rotation) { - if (rotation != mRotation) { - mRotation = rotation; - mMatrixDirty = true; - if (mRotation == 0.0f) { - mMatrixFlags &= ~ROTATION; - } else { - mMatrixFlags |= ROTATION; - } - } - } - - float getRotation() const { - return mRotation; - } - - void setRotationX(float rotationX) { - if (rotationX != mRotationX) { - mRotationX = rotationX; - mMatrixDirty = true; - if (mRotationX == 0.0f && mRotationY == 0.0f) { - mMatrixFlags &= ~ROTATION_3D; - } else { - mMatrixFlags |= ROTATION_3D; - } - } - } - - float getRotationX() const { - return mRotationX; - } - - void setRotationY(float rotationY) { - if (rotationY != mRotationY) { - mRotationY = rotationY; - mMatrixDirty = true; - if (mRotationX == 0.0f && mRotationY == 0.0f) { - mMatrixFlags &= ~ROTATION_3D; - } else { - mMatrixFlags |= ROTATION_3D; - } - } - } - - float getRotationY() const { - return mRotationY; - } - - void setScaleX(float scaleX) { - if (scaleX != mScaleX) { - mScaleX = scaleX; - mMatrixDirty = true; - if (mScaleX == 1.0f && mScaleY == 1.0f) { - mMatrixFlags &= ~SCALE; - } else { - mMatrixFlags |= SCALE; - } - } - } - - float getScaleX() const { - return mScaleX; - } - - void setScaleY(float scaleY) { - if (scaleY != mScaleY) { - mScaleY = scaleY; - mMatrixDirty = true; - if (mScaleX == 1.0f && mScaleY == 1.0f) { - mMatrixFlags &= ~SCALE; - } else { - mMatrixFlags |= SCALE; - } - } - } - - float getScaleY() const { - return mScaleY; - } - - void setPivotX(float pivotX) { - mPivotX = pivotX; - mMatrixDirty = true; - if (mPivotX == 0.0f && mPivotY == 0.0f) { - mMatrixFlags &= ~PIVOT; - } else { - mMatrixFlags |= PIVOT; - } - mPivotExplicitlySet = true; - } - - ANDROID_API float getPivotX(); - - void setPivotY(float pivotY) { - mPivotY = pivotY; - mMatrixDirty = true; - if (mPivotX == 0.0f && mPivotY == 0.0f) { - mMatrixFlags &= ~PIVOT; - } else { - mMatrixFlags |= PIVOT; - } - mPivotExplicitlySet = true; - } - - ANDROID_API float getPivotY(); - - void setCameraDistance(float distance) { - if (distance != mCameraDistance) { - mCameraDistance = distance; - mMatrixDirty = true; - if (!mTransformCamera) { - mTransformCamera = new Sk3DView(); - mTransformMatrix3D = new SkMatrix(); - } - mTransformCamera->setCameraLocation(0, 0, distance); - } - } - - float getCameraDistance() const { - return mCameraDistance; - } - - void setLeft(int left) { - if (left != mLeft) { - mLeft = left; - mWidth = mRight - mLeft; - if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) { - mMatrixDirty = true; - } - } - } - - float getLeft() const { - return mLeft; - } - - void setTop(int top) { - if (top != mTop) { - mTop = top; - mHeight = mBottom - mTop; - if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) { - mMatrixDirty = true; - } - } - } - - float getTop() const { - return mTop; - } - - void setRight(int right) { - if (right != mRight) { - mRight = right; - mWidth = mRight - mLeft; - if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) { - mMatrixDirty = true; - } - } - } + // allocator into which all ops were allocated + LinearAllocator allocator; - float getRight() const { - return mRight; - } + // pointers to all ops within display list, pointing into allocator data + Vector<DisplayListOp*> displayListOps; - void setBottom(int bottom) { - if (bottom != mBottom) { - mBottom = bottom; - mHeight = mBottom - mTop; - if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) { - mMatrixDirty = true; - } - } - } + // index of DisplayListOp restore, after which projected descendents should be drawn + int projectionReceiveIndex; - float getBottom() const { - return mBottom; - } + Vector<const SkBitmap*> bitmapResources; + Vector<const SkBitmap*> ownedBitmapResources; + Vector<const Res_png_9patch*> patchResources; - void setLeftTop(int left, int top) { - if (left != mLeft || top != mTop) { - mLeft = left; - mTop = top; - mWidth = mRight - mLeft; - mHeight = mBottom - mTop; - if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) { - mMatrixDirty = true; - } - } - } + Vector<const SkPaint*> paints; + Vector<const SkPath*> paths; + SortedVector<const SkPath*> sourcePaths; + Vector<const SkRegion*> regions; + Vector<const SkMatrix*> matrices; + Vector<SkiaShader*> shaders; + Vector<Layer*> layers; + uint32_t functorCount; + bool hasDrawOps; - void setLeftTopRightBottom(int left, int top, int right, int bottom) { - if (left != mLeft || top != mTop || right != mRight || bottom != mBottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - mWidth = mRight - mLeft; - mHeight = mBottom - mTop; - if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) { - mMatrixDirty = true; - } - } + bool isEmpty() { + return !displayListOps.size(); } - void offsetLeftRight(float offset) { - if (offset != 0) { - mLeft += offset; - mRight += offset; - if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) { - mMatrixDirty = true; - } - } - } + void addChild(DrawDisplayListOp* childOp); + const Vector<DrawDisplayListOp*>& children() { return mChildren; } - void offsetTopBottom(float offset) { - if (offset != 0) { - mTop += offset; - mBottom += offset; - if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) { - mMatrixDirty = true; - } - } + void refProperty(CanvasPropertyPrimitive* prop) { + mReferenceHolders.push(prop); } - void setCaching(bool caching) { - mCaching = caching; + void refProperty(CanvasPropertyPaint* prop) { + mReferenceHolders.push(prop); } - int getWidth() { - return mWidth; - } +private: + Vector< sp<VirtualLightRefBase> > mReferenceHolders; - int getHeight() { - return mHeight; - } + // list of children display lists for quick, non-drawing traversal + Vector<DrawDisplayListOp*> mChildren; -private: - void outputViewProperties(const int level); - - template <class T> - inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level); - - template <class T> - inline void iterate(OpenGLRenderer& renderer, T& handler, const int level); - - void init(); - - void clearResources(); - - void updateMatrix(); - - class TextContainer { - public: - size_t length() const { - return mByteLength; - } - - const char* text() const { - return (const char*) mText; - } - - size_t mByteLength; - const char* mText; - }; - - Vector<SkBitmap*> mBitmapResources; - Vector<SkBitmap*> mOwnedBitmapResources; - Vector<SkiaColorFilter*> mFilterResources; - Vector<Res_png_9patch*> mPatchResources; - - Vector<SkPaint*> mPaints; - Vector<SkPath*> mPaths; - SortedVector<SkPath*> mSourcePaths; - Vector<SkRegion*> mRegions; - Vector<SkMatrix*> mMatrices; - Vector<SkiaShader*> mShaders; - Vector<Layer*> mLayers; - - sp<DisplayListData> mDisplayListData; - - size_t mSize; - - bool mIsRenderable; - uint32_t mFunctorCount; - - String8 mName; - bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed - - // View properties - bool mClipToBounds; - float mAlpha; - bool mHasOverlappingRendering; - float mTranslationX, mTranslationY; - float mRotation, mRotationX, mRotationY; - float mScaleX, mScaleY; - float mPivotX, mPivotY; - float mCameraDistance; - int mLeft, mTop, mRight, mBottom; - int mWidth, mHeight; - int mPrevWidth, mPrevHeight; - bool mPivotExplicitlySet; - bool mMatrixDirty; - bool mMatrixIsIdentity; - uint32_t mMatrixFlags; - SkMatrix* mTransformMatrix; - Sk3DView* mTransformCamera; - SkMatrix* mTransformMatrix3D; - SkMatrix* mStaticMatrix; - SkMatrix* mAnimationMatrix; - bool mCaching; - - /** - * State operations - needed to defer displayList property operations (for example, when setting - * an alpha causes a SaveLayerAlpha to occur). These operations point into mDisplayListData's - * allocation, or null if uninitialized. - * - * These are initialized (via friend re-constructors) when a displayList is issued in either - * replay or deferred mode. If replaying, the ops are not used until the next frame. If - * deferring, the ops may be stored in the DeferredDisplayList to be played back a second time. - * - * They should be used at most once per frame (one call to 'iterate') to avoid overwriting data - */ - ClipRectOp* mClipRectOp; - SaveLayerOp* mSaveLayerOp; - SaveOp* mSaveOp; - RestoreToCountOp* mRestoreToCountOp; -}; // class DisplayList + void cleanupResources(); +}; }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 842e028..f1d70eb 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -21,6 +21,8 @@ #define LOG_TAG "OpenGLRenderer" #endif +#include <SkPath.h> +#include <SkPathOps.h> #include <SkXfermode.h> #include <private/hwui/DrawGlInfo.h> @@ -111,7 +113,7 @@ public: class DrawOp : public DisplayListOp { friend class MergingDrawBatch; public: - DrawOp(SkPaint* paint) + DrawOp(const SkPaint* paint) : mPaint(paint), mQuickRejected(false) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, @@ -181,14 +183,21 @@ public: return OpenGLRenderer::getAlphaDirect(mPaint); } + virtual bool hasTextShadow() const { + return false; + } + inline float strokeWidthOutset() { - float width = mPaint->getStrokeWidth(); - if (width == 0) return 0.5f; // account for hairline - return width * 0.5f; + // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced + // 1.0 stroke, treat 1.0 as minimum. + + // TODO: it would be nice if this could take scale into account, but scale isn't stable + // since higher levels of the view hierarchy can change scale out from underneath it. + return fmaxf(mPaint->getStrokeWidth(), 1) * 0.5f; } protected: - SkPaint* getPaint(OpenGLRenderer& renderer) { + const SkPaint* getPaint(OpenGLRenderer& renderer) { return renderer.filterPaint(mPaint); } @@ -209,22 +218,22 @@ protected: } - SkPaint* mPaint; // should be accessed via getPaint() when applying + const SkPaint* mPaint; // should be accessed via getPaint() when applying bool mQuickRejected; }; class DrawBoundedOp : public DrawOp { public: - DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint) + DrawBoundedOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawOp(paint), mLocalBounds(left, top, right, bottom) {} - DrawBoundedOp(const Rect& localBounds, SkPaint* paint) + DrawBoundedOp(const Rect& localBounds, const SkPaint* paint) : DrawOp(paint), mLocalBounds(localBounds) {} // Calculates bounds as smallest rect encompassing all points // NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in // subclass' constructor) - DrawBoundedOp(const float* points, int count, SkPaint* paint) + DrawBoundedOp(const float* points, int count, const SkPaint* paint) : DrawOp(paint), mLocalBounds(points[0], points[1], points[0], points[1]) { for (int i = 2; i < count; i += 2) { mLocalBounds.left = fminf(mLocalBounds.left, points[i]); @@ -235,15 +244,15 @@ public: } // default empty constructor for bounds, to be overridden in child constructor body - DrawBoundedOp(SkPaint* paint): DrawOp(paint) { } + DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { } bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { localBounds.set(mLocalBounds); - if (drawModifiers.mHasShadow) { - // TODO: inspect paint's looper directly + OpenGLRenderer::TextShadow textShadow; + if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) { Rect shadow(mLocalBounds); - shadow.translate(drawModifiers.mShadowDx, drawModifiers.mShadowDy); - shadow.outset(drawModifiers.mShadowRadius); + shadow.translate(textShadow.dx, textShadow.dx); + shadow.outset(textShadow.radius); localBounds.unionWith(shadow); } return true; @@ -259,7 +268,6 @@ protected: /////////////////////////////////////////////////////////////////////////////// class SaveOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: SaveOp(int flags) : mFlags(flags) {} @@ -282,17 +290,10 @@ public: int getFlags() const { return mFlags; } private: - SaveOp() {} - DisplayListOp* reinit(int flags) { - mFlags = flags; - return this; - } - int mFlags; }; class RestoreToCountOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: RestoreToCountOp(int count) : mCount(count) {} @@ -315,21 +316,25 @@ public: virtual const char* name() { return "RestoreToCount"; } private: - RestoreToCountOp() {} - DisplayListOp* reinit(int count) { - mCount = count; - return this; - } - int mCount; }; class SaveLayerOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: - SaveLayerOp(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags) - : mArea(left, top, right, bottom), mAlpha(alpha), mMode(mode), mFlags(flags) {} + SaveLayerOp(float left, float top, float right, float bottom, int alpha, int flags) + : mArea(left, top, right, bottom) + , mPaint(&mCachedPaint) + , mFlags(flags) + , mConvexMask(NULL) { + mCachedPaint.setAlpha(alpha); + } + + SaveLayerOp(float left, float top, float right, float bottom, const SkPaint* paint, int flags) + : mArea(left, top, right, bottom) + , mPaint(paint) + , mFlags(flags) + , mConvexMask(NULL) + {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, bool useQuickReject) { @@ -340,11 +345,12 @@ public: // NOTE: don't issue full saveLayer, since that has side effects/is costly. instead just // setup the snapshot for deferral, and re-issue the op at flush time deferStruct.mRenderer.saveLayerDeferred(mArea.left, mArea.top, mArea.right, mArea.bottom, - mAlpha, mMode, mFlags); + mPaint, mFlags); } virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mMode, mFlags); + renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, + mPaint, mFlags, mConvexMask); } virtual void output(int level, uint32_t logFlags) const { @@ -356,23 +362,26 @@ public: int getFlags() { return mFlags; } + // Called to make SaveLayerOp clip to the provided mask when drawing back/restored + void setMask(const SkPath* convexMask) { + mConvexMask = convexMask; + } + private: - // Special case, reserved for direct DisplayList usage - SaveLayerOp() {} - DisplayListOp* reinit(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags) { - mArea.set(left, top, right, bottom); - mAlpha = alpha; - mMode = mode; - mFlags = flags; - return this; - } - - bool isSaveLayerAlpha() const { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; } + bool isSaveLayerAlpha() const { + SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint); + int alpha = OpenGLRenderer::getAlphaDirect(mPaint); + return alpha < 255 && mode == SkXfermode::kSrcOver_Mode; + } + Rect mArea; - int mAlpha; - SkXfermode::Mode mMode; + const SkPaint* mPaint; + SkPaint mCachedPaint; int mFlags; + + // Convex path, points at data in RenderNode, valid for the duration of the frame only + // Only used for masking the SaveLayer which wraps projected RenderNodes + const SkPath* mConvexMask; }; class TranslateOp : public StateOp { @@ -456,7 +465,7 @@ private: class SetMatrixOp : public StateOp { public: - SetMatrixOp(SkMatrix* matrix) + SetMatrixOp(const SkMatrix* matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -465,7 +474,7 @@ public: virtual void output(int level, uint32_t logFlags) const { if (mMatrix) { - OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix)); + OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix)); } else { OP_LOGS("SetMatrix (reset)"); } @@ -474,12 +483,12 @@ public: virtual const char* name() { return "SetMatrix"; } private: - SkMatrix* mMatrix; + const SkMatrix* mMatrix; }; class ConcatMatrixOp : public StateOp { public: - ConcatMatrixOp(SkMatrix* matrix) + ConcatMatrixOp(const SkMatrix* matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -487,13 +496,13 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("ConcatMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix)); + OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix)); } virtual const char* name() { return "ConcatMatrix"; } private: - SkMatrix* mMatrix; + const SkMatrix* mMatrix; }; class ClipOp : public StateOp { @@ -514,14 +523,12 @@ public: } protected: - ClipOp() {} virtual bool isRect() { return false; } SkRegion::Op mOp; }; class ClipRectOp : public ClipOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op) : ClipOp(op), mArea(left, top, right, bottom) {} @@ -540,19 +547,12 @@ protected: virtual bool isRect() { return true; } private: - ClipRectOp() {} - DisplayListOp* reinit(float left, float top, float right, float bottom, SkRegion::Op op) { - mOp = op; - mArea.set(left, top, right, bottom); - return this; - } - Rect mArea; }; class ClipPathOp : public ClipOp { public: - ClipPathOp(SkPath* path, SkRegion::Op op) + ClipPathOp(const SkPath* path, SkRegion::Op op) : ClipOp(op), mPath(path) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -568,12 +568,12 @@ public: virtual const char* name() { return "ClipPath"; } private: - SkPath* mPath; + const SkPath* mPath; }; class ClipRegionOp : public ClipOp { public: - ClipRegionOp(SkRegion* region, SkRegion::Op op) + ClipRegionOp(const SkRegion* region, SkRegion::Op op) : ClipOp(op), mRegion(region) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -589,7 +589,7 @@ public: virtual const char* name() { return "ClipRegion"; } private: - SkRegion* mRegion; + const SkRegion* mRegion; }; class ResetShaderOp : public StateOp { @@ -623,73 +623,6 @@ private: SkiaShader* mShader; }; -class ResetColorFilterOp : public StateOp { -public: - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.resetColorFilter(); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOGS("ResetColorFilter"); - } - - virtual const char* name() { return "ResetColorFilter"; } -}; - -class SetupColorFilterOp : public StateOp { -public: - SetupColorFilterOp(SkiaColorFilter* colorFilter) - : mColorFilter(colorFilter) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.setupColorFilter(mColorFilter); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOG("SetupColorFilter, filter %p", mColorFilter); - } - - virtual const char* name() { return "SetupColorFilter"; } - -private: - SkiaColorFilter* mColorFilter; -}; - -class ResetShadowOp : public StateOp { -public: - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.resetShadow(); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOGS("ResetShadow"); - } - - virtual const char* name() { return "ResetShadow"; } -}; - -class SetupShadowOp : public StateOp { -public: - SetupShadowOp(float radius, float dx, float dy, int color) - : mRadius(radius), mDx(dx), mDy(dy), mColor(color) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.setupShadow(mRadius, mDx, mDy, mColor); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor); - } - - virtual const char* name() { return "SetupShadow"; } - -private: - float mRadius; - float mDx; - float mDy; - int mColor; -}; - class ResetPaintFilterOp : public StateOp { public: virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -729,7 +662,7 @@ private: class DrawBitmapOp : public DrawBoundedOp { public: - DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint) + DrawBitmapOp(const SkBitmap* bitmap, float left, float top, const SkPaint* paint) : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint), mBitmap(bitmap), mAtlas(Caches::getInstance().assetAtlas) { mEntry = mAtlas.getEntry(bitmap); @@ -823,12 +756,12 @@ public: deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() && !state.mClipSideFlags && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode && - (mBitmap->getConfig() != SkBitmap::kA8_Config); + (mBitmap->config() != SkBitmap::kA8_Config); } const SkBitmap* bitmap() { return mBitmap; } protected: - SkBitmap* mBitmap; + const SkBitmap* mBitmap; const AssetAtlas& mAtlas; uint32_t mEntryGenerationId; AssetAtlas::Entry* mEntry; @@ -837,7 +770,7 @@ protected: class DrawBitmapMatrixOp : public DrawBoundedOp { public: - DrawBitmapMatrixOp(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) + DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) { mLocalBounds.set(0, 0, bitmap->width(), bitmap->height()); const mat4 transform(*matrix); @@ -849,7 +782,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix)); + OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(mMatrix)); } virtual const char* name() { return "DrawBitmapMatrix"; } @@ -860,14 +793,15 @@ public: } private: - SkBitmap* mBitmap; - SkMatrix* mMatrix; + const SkBitmap* mBitmap; + const SkMatrix* mMatrix; }; class DrawBitmapRectOp : public DrawBoundedOp { public: - DrawBitmapRectOp(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, - float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) + DrawBitmapRectOp(const SkBitmap* bitmap, + float srcLeft, float srcTop, float srcRight, float srcBottom, + float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) : DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint), mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {} @@ -890,13 +824,13 @@ public: } private: - SkBitmap* mBitmap; + const SkBitmap* mBitmap; Rect mSrc; }; class DrawBitmapDataOp : public DrawBitmapOp { public: - DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint) + DrawBitmapDataOp(const SkBitmap* bitmap, float left, float top, const SkPaint* paint) : DrawBitmapOp(bitmap, left, top, paint) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -918,8 +852,8 @@ public: class DrawBitmapMeshOp : public DrawBoundedOp { public: - DrawBitmapMeshOp(SkBitmap* bitmap, int meshWidth, int meshHeight, - float* vertices, int* colors, SkPaint* paint) + DrawBitmapMeshOp(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) : DrawBoundedOp(vertices, 2 * (meshWidth + 1) * (meshHeight + 1), paint), mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight), mVertices(vertices), mColors(colors) {} @@ -941,17 +875,17 @@ public: } private: - SkBitmap* mBitmap; + const SkBitmap* mBitmap; int mMeshWidth; int mMeshHeight; - float* mVertices; - int* mColors; + const float* mVertices; + const int* mColors; }; class DrawPatchOp : public DrawBoundedOp { public: - DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, SkPaint* paint) + DrawPatchOp(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) : DrawBoundedOp(left, top, right, bottom, paint), mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL), mAtlas(Caches::getInstance().assetAtlas) { @@ -1028,8 +962,8 @@ public: TextureVertex* opVertices = opMesh->vertices; for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { TextureVertex::set(vertex++, - opVertices->position[0] + tx, opVertices->position[1] + ty, - opVertices->texture[0], opVertices->texture[1]); + opVertices->x + tx, opVertices->y + ty, + opVertices->u, opVertices->v); } // Dirty the current layer if possible. When the 9-patch does not @@ -1083,8 +1017,8 @@ public: } private: - SkBitmap* mBitmap; - Res_png_9patch* mPatch; + const SkBitmap* mBitmap; + const Res_png_9patch* mPatch; uint32_t mGenerationId; const Patch* mMesh; @@ -1097,7 +1031,7 @@ private: class DrawColorOp : public DrawOp { public: DrawColorOp(int color, SkXfermode::Mode mode) - : DrawOp(0), mColor(color), mMode(mode) {}; + : DrawOp(NULL), mColor(color), mMode(mode) {}; virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawColor(mColor, mMode); @@ -1116,7 +1050,7 @@ private: class DrawStrokableOp : public DrawBoundedOp { public: - DrawStrokableOp(float left, float top, float right, float bottom, SkPaint* paint) + DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawBoundedOp(left, top, right, bottom, paint) {}; bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { @@ -1141,7 +1075,7 @@ public: class DrawRectOp : public DrawStrokableOp { public: - DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint) + DrawRectOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1165,7 +1099,7 @@ public: class DrawRectsOp : public DrawBoundedOp { public: - DrawRectsOp(const float* rects, int count, SkPaint* paint) + DrawRectsOp(const float* rects, int count, const SkPaint* paint) : DrawBoundedOp(rects, count, paint), mRects(rects), mCount(count) {} @@ -1192,7 +1126,7 @@ private: class DrawRoundRectOp : public DrawStrokableOp { public: DrawRoundRectOp(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* paint) + float rx, float ry, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1213,7 +1147,7 @@ private: class DrawCircleOp : public DrawStrokableOp { public: - DrawCircleOp(float x, float y, float radius, SkPaint* paint) + DrawCircleOp(float x, float y, float radius, const SkPaint* paint) : DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint), mX(x), mY(y), mRadius(radius) {} @@ -1233,9 +1167,30 @@ private: float mRadius; }; +class DrawCirclePropsOp : public DrawOp { +public: + DrawCirclePropsOp(float* x, float* y, float* radius, const SkPaint* paint) + : DrawOp(paint), mX(x), mY(y), mRadius(radius) {} + + virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + return renderer.drawCircle(*mX, *mY, *mRadius, getPaint(renderer)); + } + + virtual void output(int level, uint32_t logFlags) const { + OP_LOG("Draw Circle Props x %p, y %p, r %p", mX, mY, mRadius); + } + + virtual const char* name() { return "DrawCircleProps"; } + +private: + float* mX; + float* mY; + float* mRadius; +}; + class DrawOvalOp : public DrawStrokableOp { public: - DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint) + DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1253,7 +1208,7 @@ public: class DrawArcOp : public DrawStrokableOp { public: DrawArcOp(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint), mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {} @@ -1278,7 +1233,7 @@ private: class DrawPathOp : public DrawBoundedOp { public: - DrawPathOp(SkPath* path, SkPaint* paint) + DrawPathOp(const SkPath* path, const SkPaint* paint) : DrawBoundedOp(paint), mPath(path) { float left, top, offset; uint32_t width, height; @@ -1294,7 +1249,7 @@ public: virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { - SkPaint* paint = getPaint(renderer); + const SkPaint* paint = getPaint(renderer); renderer.getCaches().pathCache.precache(mPath, paint); deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; @@ -1307,12 +1262,12 @@ public: virtual const char* name() { return "DrawPath"; } private: - SkPath* mPath; + const SkPath* mPath; }; class DrawLinesOp : public DrawBoundedOp { public: - DrawLinesOp(float* points, int count, SkPaint* paint) + DrawLinesOp(const float* points, int count, const SkPaint* paint) : DrawBoundedOp(points, count, paint), mPoints(points), mCount(count) { mLocalBounds.outset(strokeWidthOutset()); @@ -1336,13 +1291,13 @@ public: } protected: - float* mPoints; + const float* mPoints; int mCount; }; class DrawPointsOp : public DrawLinesOp { public: - DrawPointsOp(float* points, int count, SkPaint* paint) + DrawPointsOp(const float* points, int count, const SkPaint* paint) : DrawLinesOp(points, count, paint) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1358,16 +1313,20 @@ public: class DrawSomeTextOp : public DrawOp { public: - DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint) + DrawSomeTextOp(const char* text, int bytesCount, int count, const SkPaint* paint) : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {}; virtual void output(int level, uint32_t logFlags) const { OP_LOG("Draw some text, %d bytes", mBytesCount); } + virtual bool hasTextShadow() const { + return OpenGLRenderer::hasTextShadow(mPaint); + } + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { - SkPaint* paint = getPaint(renderer); + const SkPaint* paint = getPaint(renderer); FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); fontRenderer.precache(paint, mText, mCount, mat4::identity()); @@ -1385,7 +1344,7 @@ protected: class DrawTextOnPathOp : public DrawSomeTextOp { public: DrawTextOnPathOp(const char* text, int bytesCount, int count, - SkPath* path, float hOffset, float vOffset, SkPaint* paint) + const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) : DrawSomeTextOp(text, bytesCount, count, paint), mPath(path), mHOffset(hOffset), mVOffset(vOffset) { /* TODO: inherit from DrawBounded and init mLocalBounds */ @@ -1399,7 +1358,7 @@ public: virtual const char* name() { return "DrawTextOnPath"; } private: - SkPath* mPath; + const SkPath* mPath; float mHOffset; float mVOffset; }; @@ -1407,7 +1366,7 @@ private: class DrawPosTextOp : public DrawSomeTextOp { public: DrawPosTextOp(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint) + const float* positions, const SkPaint* paint) : DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) { /* TODO: inherit from DrawBounded and init mLocalBounds */ } @@ -1425,7 +1384,7 @@ private: class DrawTextOp : public DrawBoundedOp { public: DrawTextOp(const char* text, int bytesCount, int count, float x, float y, - const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds) + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds) : DrawBoundedOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count), mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) { memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float)); @@ -1433,7 +1392,7 @@ public: virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { - SkPaint* paint = getPaint(renderer); + const SkPaint* paint = getPaint(renderer); FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); const mat4& transform = renderer.findBestFontTransform(state.mMatrix); if (mPrecacheTransform != transform) { @@ -1502,7 +1461,7 @@ private: class DrawFunctorOp : public DrawOp { public: DrawFunctorOp(Functor* functor) - : DrawOp(0), mFunctor(functor) {} + : DrawOp(NULL), mFunctor(functor) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { renderer.startMark("GL functor"); @@ -1522,21 +1481,22 @@ private: }; class DrawDisplayListOp : public DrawBoundedOp { + friend class RenderNode; // grant DisplayList access to info of child public: - DrawDisplayListOp(DisplayList* displayList, int flags) + DrawDisplayListOp(RenderNode* displayList, int flags, const mat4& transformFromParent) : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0), - mDisplayList(displayList), mFlags(flags) {} + mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable()) { - mDisplayList->defer(deferStruct, level + 1); + if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { + mDisplayList->deferNodeInParent(deferStruct, level + 1); } } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable()) { - mDisplayList->replay(replayStruct, level + 1); + if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { + mDisplayList->replayNodeInParent(replayStruct, level + 1); } } @@ -1554,15 +1514,86 @@ public: virtual const char* name() { return "DrawDisplayList"; } + RenderNode* renderNode() { return mDisplayList; } + private: - DisplayList* mDisplayList; - int mFlags; + RenderNode* mDisplayList; + const int mFlags; + + /////////////////////////// + // Properties below are used by DisplayList::computeOrderingImpl() and iterate() + /////////////////////////// + /** + * Records transform vs parent, used for computing total transform without rerunning DL contents + */ + const mat4 mTransformFromParent; + + /** + * Holds the transformation between the projection surface ViewGroup and this DisplayList + * drawing instance. Represents any translations / transformations done within the drawing of + * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this + * DisplayList draw instance. + * + * Note: doesn't include any transformation recorded within the DisplayList and its properties. + */ + mat4 mTransformFromCompositingAncestor; + bool mSkipInOrderDraw; +}; + +/** + * Not a canvas operation, used only by 3d / z ordering logic in RenderNode::iterate() + */ +class DrawShadowOp : public DrawOp { +public: + DrawShadowOp(const mat4& transformXY, const mat4& transformZ, + float casterAlpha, bool casterUnclipped, + float fallbackWidth, float fallbackHeight, + const SkPath* outline, const SkPath* revealClip) + : DrawOp(NULL), mTransformXY(transformXY), mTransformZ(transformZ), + mCasterAlpha(casterAlpha), mCasterUnclipped(casterUnclipped), + mFallbackWidth(fallbackWidth), mFallbackHeight(fallbackHeight), + mOutline(outline), mRevealClip(revealClip) {} + + virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + SkPath casterPerimeter; + if (!mOutline || mOutline->isEmpty()) { + casterPerimeter.addRect(0, 0, mFallbackWidth, mFallbackHeight); + } else { + casterPerimeter = *mOutline; + } + + if (mRevealClip) { + // intersect the outline with the convex reveal clip + Op(casterPerimeter, *mRevealClip, kIntersect_PathOp, &casterPerimeter); + } + + return renderer.drawShadow(mTransformXY, mTransformZ, + mCasterAlpha, mCasterUnclipped, &casterPerimeter); + } + + virtual void output(int level, uint32_t logFlags) const { + OP_LOG("DrawShadow of outline %p", mOutline); + } + + virtual const char* name() { return "DrawShadow"; } + +private: + const mat4 mTransformXY; + const mat4 mTransformZ; + const float mCasterAlpha; + const bool mCasterUnclipped; + const float mFallbackWidth; + const float mFallbackHeight; + + // these point at convex SkPaths owned by RenderProperties, or null + const SkPath* mOutline; + const SkPath* mRevealClip; }; class DrawLayerOp : public DrawOp { public: DrawLayerOp(Layer* layer, float x, float y) - : DrawOp(0), mLayer(layer), mX(x), mY(y) {} + : DrawOp(NULL), mLayer(layer), mX(x), mY(y) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawLayer(mLayer, mX, mY); diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 8866029..2391e80 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -21,120 +21,58 @@ #include <private/hwui/DrawGlInfo.h> -#include "DisplayList.h" +#include "Caches.h" #include "DeferredDisplayList.h" #include "DisplayListLogBuffer.h" #include "DisplayListOp.h" #include "DisplayListRenderer.h" -#include "Caches.h" +#include "RenderNode.h" namespace android { namespace uirenderer { DisplayListRenderer::DisplayListRenderer(): - mCaches(Caches::getInstance()), mDisplayListData(new DisplayListData), + mCaches(Caches::getInstance()), mDisplayListData(0), mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false), - mHasDrawOps(false), mFunctorCount(0) { + mRestoreSaveCount(-1) { } DisplayListRenderer::~DisplayListRenderer() { - reset(); + LOG_ALWAYS_FATAL_IF(mDisplayListData, + "Destroyed a DisplayListRenderer during a record!"); } -void DisplayListRenderer::reset() { - mDisplayListData = new DisplayListData(); - mCaches.resourceCache.lock(); - - for (size_t i = 0; i < mBitmapResources.size(); i++) { - mCaches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i)); - } - - for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) { - mCaches.resourceCache.decrementRefcountLocked(mOwnedBitmapResources.itemAt(i)); - } - - for (size_t i = 0; i < mFilterResources.size(); i++) { - mCaches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i)); - } - - for (size_t i = 0; i < mPatchResources.size(); i++) { - mCaches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i)); - } - - for (size_t i = 0; i < mShaders.size(); i++) { - mCaches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i)); - } - - for (size_t i = 0; i < mSourcePaths.size(); i++) { - mCaches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i)); - } - - for (size_t i = 0; i < mLayers.size(); i++) { - mCaches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i)); - } - - mCaches.resourceCache.unlock(); - - mBitmapResources.clear(); - mOwnedBitmapResources.clear(); - mFilterResources.clear(); - mPatchResources.clear(); - mSourcePaths.clear(); +/////////////////////////////////////////////////////////////////////////////// +// Operations +/////////////////////////////////////////////////////////////////////////////// - mShaders.clear(); +DisplayListData* DisplayListRenderer::finishRecording() { mShaderMap.clear(); - - mPaints.clear(); mPaintMap.clear(); - - mRegions.clear(); mRegionMap.clear(); - - mPaths.clear(); mPathMap.clear(); - - mMatrices.clear(); - - mLayers.clear(); - - mHasDrawOps = false; - mFunctorCount = 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// Operations -/////////////////////////////////////////////////////////////////////////////// - -DisplayList* DisplayListRenderer::getDisplayList(DisplayList* displayList) { - if (!displayList) { - displayList = new DisplayList(*this); - } else { - displayList->initFromDisplayListRenderer(*this, true); - } - displayList->setRenderable(mHasDrawOps); - return displayList; -} - -bool DisplayListRenderer::isDeferred() { - return true; + DisplayListData* data = mDisplayListData; + mDisplayListData = 0; + return data; } void DisplayListRenderer::setViewport(int width, int height) { - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + // TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used + mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1); - mWidth = width; - mHeight = height; + initializeViewport(width, height); } status_t DisplayListRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { - mSnapshot = new Snapshot(mFirstSnapshot, - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); - mSaveCount = 1; - mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight); - mDirtyClip = opaque; + LOG_ALWAYS_FATAL_IF(mDisplayListData, + "prepareDirty called a second time during a recording!"); + mDisplayListData = new DisplayListData(); + initializeSaveStack(0, 0, getWidth(), getHeight()); + + mDirtyClip = opaque; mRestoreSaveCount = -1; return DrawGlInfo::kStatusDone; // No invalidate needed at record-time @@ -154,13 +92,13 @@ void DisplayListRenderer::resume() { status_t DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) { // Ignore dirty during recording, it matters only when we replay addDrawOp(new (alloc()) DrawFunctorOp(functor)); - mFunctorCount++; + mDisplayListData->functorCount++; return DrawGlInfo::kStatusDone; // No invalidate needed at record-time } int DisplayListRenderer::save(int flags) { addStateOp(new (alloc()) SaveOp(flags)); - return OpenGLRenderer::save(flags); + return StatefulBaseRenderer::save(flags); } void DisplayListRenderer::restore() { @@ -171,84 +109,90 @@ void DisplayListRenderer::restore() { mRestoreSaveCount--; insertTranslate(); - OpenGLRenderer::restore(); + StatefulBaseRenderer::restore(); } void DisplayListRenderer::restoreToCount(int saveCount) { mRestoreSaveCount = saveCount; insertTranslate(); - OpenGLRenderer::restoreToCount(saveCount); + StatefulBaseRenderer::restoreToCount(saveCount); } int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags) { - addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, alpha, mode, flags)); - return OpenGLRenderer::save(flags); + const SkPaint* paint, int flags) { + paint = refPaint(paint); + addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, flags)); + return StatefulBaseRenderer::save(flags); } -void DisplayListRenderer::translate(float dx, float dy) { +void DisplayListRenderer::translate(float dx, float dy, float dz) { + // ignore dz, not used at defer time mHasTranslate = true; mTranslateX += dx; mTranslateY += dy; insertRestoreToCount(); - OpenGLRenderer::translate(dx, dy); + StatefulBaseRenderer::translate(dx, dy, dz); } void DisplayListRenderer::rotate(float degrees) { addStateOp(new (alloc()) RotateOp(degrees)); - OpenGLRenderer::rotate(degrees); + StatefulBaseRenderer::rotate(degrees); } void DisplayListRenderer::scale(float sx, float sy) { addStateOp(new (alloc()) ScaleOp(sx, sy)); - OpenGLRenderer::scale(sx, sy); + StatefulBaseRenderer::scale(sx, sy); } void DisplayListRenderer::skew(float sx, float sy) { addStateOp(new (alloc()) SkewOp(sx, sy)); - OpenGLRenderer::skew(sx, sy); + StatefulBaseRenderer::skew(sx, sy); } -void DisplayListRenderer::setMatrix(SkMatrix* matrix) { +void DisplayListRenderer::setMatrix(const SkMatrix* matrix) { matrix = refMatrix(matrix); addStateOp(new (alloc()) SetMatrixOp(matrix)); - OpenGLRenderer::setMatrix(matrix); + StatefulBaseRenderer::setMatrix(matrix); } -void DisplayListRenderer::concatMatrix(SkMatrix* matrix) { +void DisplayListRenderer::concatMatrix(const SkMatrix* matrix) { matrix = refMatrix(matrix); addStateOp(new (alloc()) ConcatMatrixOp(matrix)); - OpenGLRenderer::concatMatrix(matrix); + StatefulBaseRenderer::concatMatrix(matrix); } bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op)); - return OpenGLRenderer::clipRect(left, top, right, bottom, op); + return StatefulBaseRenderer::clipRect(left, top, right, bottom, op); } -bool DisplayListRenderer::clipPath(SkPath* path, SkRegion::Op op) { +bool DisplayListRenderer::clipPath(const SkPath* path, SkRegion::Op op) { path = refPath(path); addStateOp(new (alloc()) ClipPathOp(path, op)); - return OpenGLRenderer::clipPath(path, op); + return StatefulBaseRenderer::clipPath(path, op); } -bool DisplayListRenderer::clipRegion(SkRegion* region, SkRegion::Op op) { +bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { region = refRegion(region); addStateOp(new (alloc()) ClipRegionOp(region, op)); - return OpenGLRenderer::clipRegion(region, op); + return StatefulBaseRenderer::clipRegion(region, op); } -status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList, +status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList, Rect& dirty, int32_t flags) { // dirty is an out parameter and should not be recorded, // it matters only when replaying the display list - // TODO: To be safe, the display list should be ref-counted in the - // resources cache, but we rely on the caller (UI toolkit) to - // do the right thing for now + if (displayList->stagingProperties().isProjectionReceiver()) { + // use staging property, since recording on UI thread + mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size(); + } - addDrawOp(new (alloc()) DrawDisplayListOp(displayList, flags)); + DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList, + flags, *currentTransform()); + addDrawOp(op); + mDisplayListData->addChild(op); return DrawGlInfo::kStatusDone; } @@ -258,7 +202,8 @@ status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) { return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) { +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { bitmap = refBitmap(bitmap); paint = refPaint(paint); @@ -266,7 +211,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) { bitmap = refBitmap(bitmap); matrix = refMatrix(matrix); paint = refPaint(paint); @@ -275,9 +221,9 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkP return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, SkPaint* paint) { + float dstRight, float dstBottom, const SkPaint* paint) { bitmap = refBitmap(bitmap); paint = refPaint(paint); @@ -296,8 +242,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, - SkPaint* paint) { +status_t DisplayListRenderer::drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { bitmap = refBitmapData(bitmap); paint = refPaint(paint); @@ -305,21 +251,21 @@ status_t DisplayListRenderer::drawBitmapData(SkBitmap* bitmap, float left, float return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, - float* vertices, int* colors, SkPaint* paint) { - int count = (meshWidth + 1) * (meshHeight + 1) * 2; +status_t DisplayListRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) { + int vertexCount = (meshWidth + 1) * (meshHeight + 1); bitmap = refBitmap(bitmap); - vertices = refBuffer<float>(vertices, count); + vertices = refBuffer<float>(vertices, vertexCount * 2); // 2 floats per vertex paint = refPaint(paint); - colors = refBuffer<int>(colors, count); + colors = refBuffer<int>(colors, vertexCount); // 1 color per vertex addDrawOp(new (alloc()) DrawBitmapMeshOp(bitmap, meshWidth, meshHeight, vertices, colors, paint)); return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, SkPaint* paint) { +status_t DisplayListRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) { bitmap = refBitmap(bitmap); patch = refPatch(patch); paint = refPaint(paint); @@ -334,41 +280,52 @@ status_t DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) { } status_t DisplayListRenderer::drawRect(float left, float top, float right, float bottom, - SkPaint* paint) { + const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, paint)); return DrawGlInfo::kStatusDone; } status_t DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* paint) { + float rx, float ry, const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, paint)); return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) { +status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, paint)); return DrawGlInfo::kStatusDone; } +status_t DisplayListRenderer::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, + CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { + mDisplayListData->refProperty(x); + mDisplayListData->refProperty(y); + mDisplayListData->refProperty(radius); + mDisplayListData->refProperty(paint); + addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value, + &radius->value, &paint->value)); + return DrawGlInfo::kStatusDone; +} + status_t DisplayListRenderer::drawOval(float left, float top, float right, float bottom, - SkPaint* paint) { + const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, paint)); return DrawGlInfo::kStatusDone; } status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint)); return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) { +status_t DisplayListRenderer::drawPath(const SkPath* path, const SkPaint* paint) { path = refPath(path); paint = refPaint(paint); @@ -376,7 +333,7 @@ status_t DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) { return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) { +status_t DisplayListRenderer::drawLines(const float* points, int count, const SkPaint* paint) { points = refBuffer<float>(points, count); paint = refPaint(paint); @@ -384,7 +341,7 @@ status_t DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) { +status_t DisplayListRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { points = refBuffer<float>(points, count); paint = refPaint(paint); @@ -393,7 +350,7 @@ status_t DisplayListRenderer::drawPoints(float* points, int count, SkPaint* pain } status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, int count, - SkPath* path, float hOffset, float vOffset, SkPaint* paint) { + const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) { if (!text || count <= 0) return DrawGlInfo::kStatusDone; text = refText(text, bytesCount); @@ -407,7 +364,7 @@ status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, i } status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint) { + const float* positions, const SkPaint* paint) { if (!text || count <= 0) return DrawGlInfo::kStatusDone; text = refText(text, bytesCount); @@ -420,7 +377,7 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int } status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count, - float x, float y, const float* positions, SkPaint* paint, + float x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { if (!text || count <= 0) return DrawGlInfo::kStatusDone; @@ -435,7 +392,7 @@ status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int cou return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawRects(const float* rects, int count, SkPaint* paint) { +status_t DisplayListRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { if (count <= 0) return DrawGlInfo::kStatusDone; rects = refBuffer<float>(rects, count); @@ -453,25 +410,6 @@ void DisplayListRenderer::setupShader(SkiaShader* shader) { addStateOp(new (alloc()) SetupShaderOp(shader)); } -void DisplayListRenderer::resetColorFilter() { - addStateOp(new (alloc()) ResetColorFilterOp()); -} - -void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) { - filter = refColorFilter(filter); - addStateOp(new (alloc()) SetupColorFilterOp(filter)); -} - -void DisplayListRenderer::resetShadow() { - addStateOp(new (alloc()) ResetShadowOp()); - OpenGLRenderer::resetShadow(); -} - -void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) { - addStateOp(new (alloc()) SetupShadowOp(radius, dx, dy, color)); - OpenGLRenderer::setupShadow(radius, dx, dy, color); -} - void DisplayListRenderer::resetPaintFilter() { addStateOp(new (alloc()) ResetPaintFilterOp()); } @@ -506,12 +444,12 @@ void DisplayListRenderer::addStateOp(StateOp* op) { void DisplayListRenderer::addDrawOp(DrawOp* op) { Rect localBounds; if (op->getLocalBounds(mDrawModifiers, localBounds)) { - bool rejected = quickRejectNoScissor(localBounds.left, localBounds.top, + bool rejected = quickRejectConservative(localBounds.left, localBounds.top, localBounds.right, localBounds.bottom); op->setQuickRejected(rejected); } - mHasDrawOps = true; + mDisplayListData->hasDrawOps = true; addOpInternal(op); } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index d233150..185179a 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -22,9 +22,9 @@ #include <SkPath.h> #include <cutils/compiler.h> -#include "DisplayList.h" #include "DisplayListLogBuffer.h" #include "OpenGLRenderer.h" +#include "RenderNode.h" namespace android { namespace uirenderer { @@ -54,143 +54,112 @@ class DrawOp; class StateOp; /** - * Records drawing commands in a display list for latter playback. + * Records drawing commands in a display list for later playback into an OpenGLRenderer. */ class DisplayListRenderer: public OpenGLRenderer { public: ANDROID_API DisplayListRenderer(); virtual ~DisplayListRenderer(); - ANDROID_API DisplayList* getDisplayList(DisplayList* displayList); + ANDROID_API DisplayListData* finishRecording(); - virtual bool isDeferred(); + virtual bool isRecording() const { return true; } +// ---------------------------------------------------------------------------- +// Frame state operations +// ---------------------------------------------------------------------------- virtual void setViewport(int width, int height); virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); virtual void finish(); - - virtual status_t callDrawGLFunction(Functor *functor, Rect& dirty); - virtual void interrupt(); virtual void resume(); +// ---------------------------------------------------------------------------- +// Canvas state operations +// ---------------------------------------------------------------------------- + // Save (layer) virtual int save(int flags); virtual void restore(); virtual void restoreToCount(int saveCount); - virtual int saveLayer(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags); + const SkPaint* paint, int flags); - virtual void translate(float dx, float dy); + // Matrix + virtual void translate(float dx, float dy, float dz); virtual void rotate(float degrees); virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - virtual void setMatrix(SkMatrix* matrix); - virtual void concatMatrix(SkMatrix* matrix); + virtual void setMatrix(const SkMatrix* matrix); + virtual void concatMatrix(const SkMatrix* matrix); + // Clip virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - virtual bool clipPath(SkPath* path, SkRegion::Op op); - virtual bool clipRegion(SkRegion* region, SkRegion::Op op); - - virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags); - virtual status_t drawLayer(Layer* layer, float x, float y); - virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); - virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint); - virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, SkPaint* paint); - virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint); - virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, - float* vertices, int* colors, SkPaint* paint); - virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, SkPaint* paint); - virtual status_t drawColor(int color, SkXfermode::Mode mode); - virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint); - virtual status_t drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* paint); - virtual status_t drawCircle(float x, float y, float radius, SkPaint* paint); - virtual status_t drawOval(float left, float top, float right, float bottom, SkPaint* paint); - virtual status_t drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint); - virtual status_t drawPath(SkPath* path, SkPaint* paint); - virtual status_t drawLines(float* points, int count, SkPaint* paint); - virtual status_t drawPoints(float* points, int count, SkPaint* paint); - virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path, - float hOffset, float vOffset, SkPaint* paint); - virtual status_t drawPosText(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint); - virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, - const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds, - DrawOpMode drawOpMode); - - virtual status_t drawRects(const float* rects, int count, SkPaint* paint); + virtual bool clipPath(const SkPath* path, SkRegion::Op op); + virtual bool clipRegion(const SkRegion* region, SkRegion::Op op); + // Misc - should be implemented with SkPaint inspection virtual void resetShader(); virtual void setupShader(SkiaShader* shader); - virtual void resetColorFilter(); - virtual void setupColorFilter(SkiaColorFilter* filter); - - virtual void resetShadow(); - virtual void setupShadow(float radius, float dx, float dy, int color); - virtual void resetPaintFilter(); virtual void setupPaintFilter(int clearBits, int setBits); - ANDROID_API void reset(); - - sp<DisplayListData> getDisplayListData() const { - return mDisplayListData; - } - - const Vector<SkBitmap*>& getBitmapResources() const { - return mBitmapResources; - } - - const Vector<SkBitmap*>& getOwnedBitmapResources() const { - return mOwnedBitmapResources; - } - - const Vector<SkiaColorFilter*>& getFilterResources() const { - return mFilterResources; - } - - const Vector<Res_png_9patch*>& getPatchResources() const { - return mPatchResources; - } - - const Vector<SkiaShader*>& getShaders() const { - return mShaders; - } - - const Vector<SkPaint*>& getPaints() const { - return mPaints; - } - - const Vector<SkPath*>& getPaths() const { - return mPaths; - } - - const SortedVector<SkPath*>& getSourcePaths() const { - return mSourcePaths; - } +// ---------------------------------------------------------------------------- +// Canvas draw operations +// ---------------------------------------------------------------------------- + virtual status_t drawColor(int color, SkXfermode::Mode mode); - const Vector<SkRegion*>& getRegions() const { - return mRegions; - } + // Bitmap-based + virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint); + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint); + virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint); + virtual status_t drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint); + virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint); + virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint); + + // Shapes + virtual status_t drawRect(float left, float top, float right, float bottom, + const SkPaint* paint); + virtual status_t drawRects(const float* rects, int count, const SkPaint* paint); + virtual status_t drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, const SkPaint* paint); + virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint); + virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, + CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint); + virtual status_t drawOval(float left, float top, float right, float bottom, + const SkPaint* paint); + virtual status_t drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint); + virtual status_t drawPath(const SkPath* path, const SkPaint* paint); + virtual status_t drawLines(const float* points, int count, const SkPaint* paint); + virtual status_t drawPoints(const float* points, int count, const SkPaint* paint); - const Vector<Layer*>& getLayers() const { - return mLayers; - } + // Text + virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, + DrawOpMode drawOpMode = kDrawOpMode_Immediate); + virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, + float hOffset, float vOffset, const SkPaint* paint); + virtual status_t drawPosText(const char* text, int bytesCount, int count, + const float* positions, const SkPaint* paint); - const Vector<SkMatrix*>& getMatrices() const { - return mMatrices; - } +// ---------------------------------------------------------------------------- +// Canvas draw operations - special +// ---------------------------------------------------------------------------- + virtual status_t drawLayer(Layer* layer, float x, float y); + virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, + int32_t replayFlags); - uint32_t getFunctorCount() const { - return mFunctorCount; - } + // TODO: rename for consistency + virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); private: void insertRestoreToCount(); @@ -206,8 +175,9 @@ private: } template<class T> - inline T* refBuffer(const T* srcBuffer, int32_t count) { - if (srcBuffer == NULL) return NULL; + inline const T* refBuffer(const T* srcBuffer, int32_t count) { + if (!srcBuffer) return NULL; + T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T)); memcpy(dstBuffer, srcBuffer, count * sizeof(T)); return dstBuffer; @@ -217,86 +187,88 @@ private: return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength); } - inline SkPath* refPath(SkPath* path) { + inline const SkPath* refPath(const SkPath* path) { if (!path) return NULL; - SkPath* pathCopy = mPathMap.valueFor(path); + const SkPath* pathCopy = mPathMap.valueFor(path); if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) { - pathCopy = new SkPath(*path); - pathCopy->setSourcePath(path); + SkPath* newPathCopy = new SkPath(*path); + newPathCopy->setSourcePath(path); + + pathCopy = newPathCopy; // replaceValueFor() performs an add if the entry doesn't exist mPathMap.replaceValueFor(path, pathCopy); - mPaths.add(pathCopy); + mDisplayListData->paths.add(pathCopy); } - if (mSourcePaths.indexOf(path) < 0) { + if (mDisplayListData->sourcePaths.indexOf(path) < 0) { mCaches.resourceCache.incrementRefcount(path); - mSourcePaths.add(path); + mDisplayListData->sourcePaths.add(path); } return pathCopy; } - inline SkPaint* refPaint(SkPaint* paint) { + inline const SkPaint* refPaint(const SkPaint* paint) { if (!paint) { return paint; } - SkPaint* paintCopy = mPaintMap.valueFor(paint); + const SkPaint* paintCopy = mPaintMap.valueFor(paint); if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) { paintCopy = new SkPaint(*paint); // replaceValueFor() performs an add if the entry doesn't exist mPaintMap.replaceValueFor(paint, paintCopy); - mPaints.add(paintCopy); + mDisplayListData->paints.add(paintCopy); } return paintCopy; } - inline SkRegion* refRegion(SkRegion* region) { + inline const SkRegion* refRegion(const SkRegion* region) { if (!region) { return region; } - SkRegion* regionCopy = mRegionMap.valueFor(region); + const SkRegion* regionCopy = mRegionMap.valueFor(region); // TODO: Add generation ID to SkRegion if (regionCopy == NULL) { regionCopy = new SkRegion(*region); // replaceValueFor() performs an add if the entry doesn't exist mRegionMap.replaceValueFor(region, regionCopy); - mRegions.add(regionCopy); + mDisplayListData->regions.add(regionCopy); } return regionCopy; } - inline SkMatrix* refMatrix(SkMatrix* matrix) { + inline const SkMatrix* refMatrix(const SkMatrix* matrix) { if (matrix) { // Copying the matrix is cheap and prevents against the user changing // the original matrix before the operation that uses it - SkMatrix* copy = new SkMatrix(*matrix); - mMatrices.add(copy); + const SkMatrix* copy = new SkMatrix(*matrix); + mDisplayListData->matrices.add(copy); return copy; } return matrix; } inline Layer* refLayer(Layer* layer) { - mLayers.add(layer); + mDisplayListData->layers.add(layer); mCaches.resourceCache.incrementRefcount(layer); return layer; } - inline SkBitmap* refBitmap(SkBitmap* bitmap) { + inline const SkBitmap* refBitmap(const SkBitmap* bitmap) { // Note that this assumes the bitmap is immutable. There are cases this won't handle // correctly, such as creating the bitmap from scratch, drawing with it, changing its // contents, and drawing again. The only fix would be to always copy it the first time, // which doesn't seem worth the extra cycles for this unlikely case. - mBitmapResources.add(bitmap); + mDisplayListData->bitmapResources.add(bitmap); mCaches.resourceCache.incrementRefcount(bitmap); return bitmap; } - inline SkBitmap* refBitmapData(SkBitmap* bitmap) { - mOwnedBitmapResources.add(bitmap); + inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) { + mDisplayListData->ownedBitmapResources.add(bitmap); mCaches.resourceCache.incrementRefcount(bitmap); return bitmap; } @@ -310,60 +282,33 @@ private: shaderCopy = shader->copy(); // replaceValueFor() performs an add if the entry doesn't exist mShaderMap.replaceValueFor(shader, shaderCopy); - mShaders.add(shaderCopy); + mDisplayListData->shaders.add(shaderCopy); mCaches.resourceCache.incrementRefcount(shaderCopy); } return shaderCopy; } - inline SkiaColorFilter* refColorFilter(SkiaColorFilter* colorFilter) { - mFilterResources.add(colorFilter); - mCaches.resourceCache.incrementRefcount(colorFilter); - return colorFilter; - } - - inline Res_png_9patch* refPatch(Res_png_9patch* patch) { - mPatchResources.add(patch); + inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) { + mDisplayListData->patchResources.add(patch); mCaches.resourceCache.incrementRefcount(patch); return patch; } - Vector<SkBitmap*> mBitmapResources; - Vector<SkBitmap*> mOwnedBitmapResources; - Vector<SkiaColorFilter*> mFilterResources; - Vector<Res_png_9patch*> mPatchResources; - - Vector<SkPaint*> mPaints; - DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap; - - Vector<SkPath*> mPaths; - DefaultKeyedVector<SkPath*, SkPath*> mPathMap; - - SortedVector<SkPath*> mSourcePaths; - - Vector<SkRegion*> mRegions; - DefaultKeyedVector<SkRegion*, SkRegion*> mRegionMap; - - Vector<SkiaShader*> mShaders; + DefaultKeyedVector<const SkPaint*, const SkPaint*> mPaintMap; + DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap; + DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap; DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap; - Vector<SkMatrix*> mMatrices; - - Vector<Layer*> mLayers; - - int mRestoreSaveCount; - Caches& mCaches; - sp<DisplayListData> mDisplayListData; + DisplayListData* mDisplayListData; float mTranslateX; float mTranslateY; bool mHasTranslate; - bool mHasDrawOps; - uint32_t mFunctorCount; + int mRestoreSaveCount; - friend class DisplayList; + friend class RenderNode; }; // class DisplayListRenderer diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 8d19ca2..647c281 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -72,18 +72,19 @@ status_t TextSetupFunctor::operator ()(int what, void* data) { break; } } - renderer->setupDrawColorFilter(); + renderer->setupDrawColorFilter(paint->getColorFilter()); renderer->setupDrawShader(); - renderer->setupDrawBlending(true, mode); + renderer->setupDrawBlending(paint); renderer->setupDrawProgram(); - renderer->setupDrawModelView(x, y, x, y, pureTranslate, true); + renderer->setupDrawModelView(kModelViewMode_Translate, false, + 0.0f, 0.0f, 0.0f, 0.0f, pureTranslate); // Calling setupDrawTexture with the name 0 will enable the // uv attributes and increase the texture unit count // texture binding will be performed by the font renderer as // needed renderer->setupDrawTexture(0); renderer->setupDrawPureColorUniforms(); - renderer->setupDrawColorFilterUniforms(); + renderer->setupDrawColorFilterUniforms(paint->getColorFilter()); renderer->setupDrawShaderUniforms(pureTranslate); renderer->setupDrawTextGammaUniforms(); @@ -497,7 +498,7 @@ void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) { } checkTextureUpdate(); - caches.bindIndicesBuffer(); + caches.bindQuadIndicesBuffer(); if (!mDrawn) { // If returns true, a VBO was bound and we must @@ -514,8 +515,8 @@ void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) { texture->setLinearFiltering(mLinearFiltering, false); TextureVertex* mesh = texture->mesh(); - caches.bindPositionVertexPointer(force, &mesh[0].position[0]); - caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]); + caches.bindPositionVertexPointer(force, &mesh[0].x); + caches.bindTexCoordsVertexPointer(force, &mesh[0].u); force = false; glDrawElements(GL_TRIANGLES, texture->meshElementCount(), @@ -586,11 +587,11 @@ void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, } } -void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) { +void FontRenderer::setFont(const SkPaint* paint, const mat4& matrix) { mCurrentFont = Font::create(this, paint, matrix); } -FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, +FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { checkInit(); @@ -675,7 +676,8 @@ void FontRenderer::finishRender() { issueDrawCommand(); } -void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) { +void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, + const mat4& matrix) { Font* font = Font::create(this, paint, matrix); font->precache(paint, text, numGlyphs); } @@ -684,7 +686,7 @@ void FontRenderer::endPrecaching() { checkTextureUpdate(); } -bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, +bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds, Functor* functor, bool forceFinish) { if (!mCurrentFont) { @@ -702,8 +704,8 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t return mDrawn; } -bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, +bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, float hOffset, float vOffset, Rect* bounds, Functor* functor) { if (!mCurrentFont) { ALOGE("No font set"); diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index aa7e776..9259028 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -44,8 +44,6 @@ namespace RSC { } #endif -class Functor; - namespace android { namespace uirenderer { @@ -64,7 +62,7 @@ public: }; TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate, - int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(), + int alpha, SkXfermode::Mode mode, const SkPaint* paint): Functor(), renderer(renderer), x(x), y(y), pureTranslate(pureTranslate), alpha(alpha), mode(mode), paint(paint) { } @@ -78,7 +76,7 @@ public: bool pureTranslate; int alpha; SkXfermode::Mode mode; - SkPaint* paint; + const SkPaint* paint; }; /////////////////////////////////////////////////////////////////////////////// @@ -97,20 +95,20 @@ public: mGammaTable = gammaTable; } - void setFont(SkPaint* paint, const mat4& matrix); + void setFont(const SkPaint* paint, const mat4& matrix); - void precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix); + void precache(const SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix); void endPrecaching(); // bounds is an out parameter - bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds, - Functor* functor, bool forceFinish = true); + bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions, + Rect* bounds, Functor* functor, bool forceFinish = true); // bounds is an out parameter - bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds, - Functor* functor); + bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, + float hOffset, float vOffset, Rect* bounds, Functor* functor); struct DropShadow { DropShadow() { }; @@ -130,7 +128,7 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it - DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex, + DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions); void setTextureFiltering(bool linearFiltering) { diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 0916942..ffd1e8c 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -115,7 +115,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) { // Callbacks /////////////////////////////////////////////////////////////////////////////// -void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) { +void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) { if (texture) { const uint32_t size = texture->width * texture->height * bytesPerPixel(); mSize -= size; @@ -185,7 +185,7 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, mCache.removeOldest(); } - generateTexture(colors, positions, count, texture); + generateTexture(colors, positions, texture); mSize += size; mCache.put(gradient, texture); @@ -238,8 +238,7 @@ void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float am dst += 4 * sizeof(float); } -void GradientCache::generateTexture(uint32_t* colors, float* positions, - int count, Texture* texture) { +void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* texture) { const uint32_t width = texture->width; const GLsizei rowBytes = width * bytesPerPixel(); uint8_t pixels[rowBytes * texture->height]; diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 43934d9..6a783b1 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -151,7 +151,7 @@ private: Texture* addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count); - void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture); + void generateTexture(uint32_t* colors, float* positions, Texture* texture); struct GradientInfo { uint32_t width; diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp new file mode 100644 index 0000000..004ddf5 --- /dev/null +++ b/libs/hwui/Interpolator.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 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 "Interpolator.h" + +#include <math.h> + +namespace android { +namespace uirenderer { + +Interpolator* Interpolator::createDefaultInterpolator() { + return new AccelerateDecelerateInterpolator(); +} + +float AccelerateDecelerateInterpolator::interpolate(float input) { + return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h new file mode 100644 index 0000000..2cfb60c --- /dev/null +++ b/libs/hwui/Interpolator.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 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 INTERPOLATOR_H +#define INTERPOLATOR_H + +namespace android { +namespace uirenderer { + +class Interpolator { +public: + virtual ~Interpolator() {} + + virtual float interpolate(float input) = 0; + + static Interpolator* createDefaultInterpolator(); + +protected: + Interpolator() {} +}; + +class AccelerateDecelerateInterpolator : public Interpolator { +public: + AccelerateDecelerateInterpolator() {} + virtual ~AccelerateDecelerateInterpolator() {} + + virtual float interpolate(float input); +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* INTERPOLATOR_H */ diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index bd371a3..9606e58 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -18,12 +18,12 @@ #include <utils/Log.h> -#include "DisplayList.h" +#include "Caches.h" #include "DeferredDisplayList.h" #include "Layer.h" #include "LayerRenderer.h" #include "OpenGLRenderer.h" -#include "Caches.h" +#include "RenderNode.h" namespace android { namespace uirenderer { @@ -46,17 +46,20 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight): stencil = NULL; debugDrawUpdate = false; hasDrawnSinceUpdate = false; + forceFilter = false; deferredList = NULL; + convexMask = NULL; caches.resourceCache.incrementRefcount(this); } Layer::~Layer() { - if (colorFilter) caches.resourceCache.decrementRefcount(colorFilter); + SkSafeUnref(colorFilter); removeFbo(); deleteTexture(); delete[] mesh; delete deferredList; + delete renderer; } uint32_t Layer::computeIdealWidth(uint32_t layerWidth) { @@ -67,6 +70,13 @@ uint32_t Layer::computeIdealHeight(uint32_t layerHeight) { return uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE); } +void Layer::requireRenderer() { + if (!renderer) { + renderer = new LayerRenderer(this); + renderer->initProperties(); + } +} + bool Layer::resize(const uint32_t width, const uint32_t height) { uint32_t desiredWidth = computeIdealWidth(width); uint32_t desiredHeight = computeIdealWidth(height); @@ -131,18 +141,22 @@ void Layer::removeFbo(bool flush) { } } -void Layer::setPaint(SkPaint* paint) { +void Layer::updateDeferred(RenderNode* displayList, + int left, int top, int right, int bottom) { + requireRenderer(); + this->displayList = displayList; + const Rect r(left, top, right, bottom); + dirtyRect.unionWith(r); + deferredUpdateScheduled = true; +} + +void Layer::setPaint(const SkPaint* paint) { OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); + setColorFilter((paint) ? paint->getColorFilter() : NULL); } -void Layer::setColorFilter(SkiaColorFilter* filter) { - if (colorFilter) { - caches.resourceCache.decrementRefcount(colorFilter); - } - colorFilter = filter; - if (colorFilter) { - caches.resourceCache.incrementRefcount(colorFilter); - } +void Layer::setColorFilter(SkColorFilter* filter) { + SkRefCnt_SafeAssign(colorFilter, filter); } void Layer::bindTexture() const { @@ -194,25 +208,23 @@ void Layer::defer() { dirtyRect.set(0, 0, width, height); } - if (deferredList) { - deferredList->reset(dirtyRect); - } else { - deferredList = new DeferredDisplayList(dirtyRect); - } + delete deferredList; + deferredList = new DeferredDisplayList(dirtyRect); + DeferStateStruct deferredState(*deferredList, *renderer, - DisplayList::kReplayFlag_ClipChildren); + RenderNode::kReplayFlag_ClipChildren); renderer->initViewport(width, height); renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - displayList->defer(deferredState, 0); + displayList->computeOrdering(); + displayList->deferNodeTree(deferredState); deferredUpdateScheduled = false; } void Layer::cancelDefer() { - renderer = NULL; displayList = NULL; deferredUpdateScheduled = false; if (deferredList) { @@ -231,7 +243,6 @@ void Layer::flush() { deferredList->flush(*renderer, dirtyRect); renderer->finish(); - renderer = NULL; dirtyRect.setEmpty(); displayList = NULL; @@ -243,10 +254,9 @@ void Layer::render() { renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - renderer->drawDisplayList(displayList, dirtyRect, DisplayList::kReplayFlag_ClipChildren); + renderer->drawDisplayList(displayList.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren); renderer->finish(); - renderer = NULL; dirtyRect.setEmpty(); diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index b70042f..49610d5 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -17,7 +17,9 @@ #ifndef ANDROID_HWUI_LAYER_H #define ANDROID_HWUI_LAYER_H +#include <cutils/compiler.h> #include <sys/types.h> +#include <utils/StrongPointer.h> #include <GLES2/gl2.h> @@ -26,9 +28,9 @@ #include <SkPaint.h> #include <SkXfermode.h> +#include "Matrix.h" #include "Rect.h" #include "RenderBuffer.h" -#include "SkiaColorFilter.h" #include "Texture.h" #include "Vertex.h" @@ -42,14 +44,15 @@ namespace uirenderer { // Forward declarations class Caches; class OpenGLRenderer; -class DisplayList; +class RenderNode; class DeferredDisplayList; class DeferStateStruct; /** * A layer has dimensions and is backed by an OpenGL texture or FBO. */ -struct Layer { +class Layer { +public: Layer(const uint32_t layerWidth, const uint32_t layerHeight); ~Layer(); @@ -82,14 +85,8 @@ struct Layer { regionRect.translate(layer.left, layer.top); } - void updateDeferred(OpenGLRenderer* renderer, DisplayList* displayList, - int left, int top, int right, int bottom) { - this->renderer = renderer; - this->displayList = displayList; - const Rect r(left, top, right, bottom); - dirtyRect.unionWith(r); - deferredUpdateScheduled = true; - } + void updateDeferred(RenderNode* displayList, + int left, int top, int right, int bottom); inline uint32_t getWidth() const { return texture.width; @@ -115,7 +112,7 @@ struct Layer { texture.height = height; } - ANDROID_API void setPaint(SkPaint* paint); + ANDROID_API void setPaint(const SkPaint* paint); inline void setBlend(bool blend) { texture.blend = blend; @@ -125,6 +122,14 @@ struct Layer { return texture.blend; } + inline void setForceFilter(bool forceFilter) { + this->forceFilter = forceFilter; + } + + inline bool getForceFilter() const { + return forceFilter; + } + inline void setAlpha(int alpha) { this->alpha = alpha; } @@ -216,11 +221,19 @@ struct Layer { this->textureLayer = textureLayer; } - inline SkiaColorFilter* getColorFilter() const { + inline SkColorFilter* getColorFilter() const { return colorFilter; } - ANDROID_API void setColorFilter(SkiaColorFilter* filter); + ANDROID_API void setColorFilter(SkColorFilter* filter); + + inline void setConvexMask(const SkPath* convexMask) { + this->convexMask = convexMask; + } + + inline const SkPath* getConvexMask() { + return convexMask; + } void bindStencilRenderBuffer() const; @@ -284,12 +297,14 @@ struct Layer { */ bool deferredUpdateScheduled; OpenGLRenderer* renderer; - DisplayList* displayList; + sp<RenderNode> displayList; Rect dirtyRect; bool debugDrawUpdate; bool hasDrawnSinceUpdate; private: + void requireRenderer(); + Caches& caches; /** @@ -338,12 +353,18 @@ private: /** * Color filter used to draw this layer. Optional. */ - SkiaColorFilter* colorFilter; + SkColorFilter* colorFilter; + + /** + * Indicates raster data backing the layer is scaled, requiring filtration. + */ + bool forceFilter; /** * Opacity of the layer. */ int alpha; + /** * Blending mode of the layer. */ @@ -365,6 +386,13 @@ private: */ DeferredDisplayList* deferredList; + /** + * This convex path should be used to mask the layer's draw to the screen. + * + * Data not owned/managed by layer object. + */ + const SkPath* convexMask; + }; // struct Layer }; // namespace uirenderer diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index f8076cc..e0ac2ba 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -92,7 +92,7 @@ void LayerRenderer::finish() { // who will invoke OpenGLRenderer::resume() } -GLint LayerRenderer::getTargetFbo() const { +GLuint LayerRenderer::getTargetFbo() const { return mLayer->getFbo(); } @@ -117,7 +117,7 @@ void LayerRenderer::ensureStencilBuffer() { /////////////////////////////////////////////////////////////////////////////// Region* LayerRenderer::getRegion() const { - if (getSnapshot()->flags & Snapshot::kFlagFboTarget) { + if (currentSnapshot()->flags & Snapshot::kFlagFboTarget) { return OpenGLRenderer::getRegion(); } return &mLayer->region; @@ -184,7 +184,7 @@ void LayerRenderer::generateMesh() { // Layers management /////////////////////////////////////////////////////////////////////////////// -Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) { +Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height); Caches& caches = Caches::getInstance(); @@ -221,7 +221,6 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque layer->texCoords.set(0.0f, height / float(layer->getHeight()), width / float(layer->getWidth()), 0.0f); layer->setAlpha(255, SkXfermode::kSrcOver_Mode); - layer->setBlend(!isOpaque); layer->setColorFilter(NULL); layer->setDirty(true); layer->region.clear(); @@ -270,13 +269,12 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { return true; } -Layer* LayerRenderer::createTextureLayer(bool isOpaque) { +Layer* LayerRenderer::createTextureLayer() { LAYER_RENDERER_LOGD("Creating new texture layer"); Layer* layer = new Layer(0, 0); layer->setCacheable(false); layer->setTextureLayer(true); - layer->setBlend(!isOpaque); layer->setEmpty(true); layer->setFbo(0); layer->setAlpha(255, SkXfermode::kSrcOver_Mode); @@ -292,14 +290,15 @@ Layer* LayerRenderer::createTextureLayer(bool isOpaque) { } void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, - bool isOpaque, GLenum renderTarget, float* transform) { + bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform) { if (layer) { layer->setBlend(!isOpaque); + layer->setForceFilter(forceFilter); layer->setSize(width, height); layer->layer.set(0.0f, 0.0f, width, height); layer->region.set(width, height); layer->regionRect.set(0.0f, 0.0f, width, height); - layer->getTexTransform().load(transform); + layer->getTexTransform().load(textureTransform); if (renderTarget != layer->getRenderTarget()) { layer->setRenderTarget(renderTarget); diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index 5f86731..40e461a 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -52,11 +52,11 @@ public: virtual status_t clear(float left, float top, float right, float bottom, bool opaque); virtual void finish(); - ANDROID_API static Layer* createTextureLayer(bool isOpaque); - ANDROID_API static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false); + ANDROID_API static Layer* createTextureLayer(); + ANDROID_API static Layer* createRenderLayer(uint32_t width, uint32_t height); ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); ANDROID_API static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, - bool isOpaque, GLenum renderTarget, float* transform); + bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform); ANDROID_API static void destroyLayer(Layer* layer); ANDROID_API static void destroyLayerDeferred(Layer* layer); ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap); @@ -67,7 +67,7 @@ protected: virtual void ensureStencilBuffer(); virtual bool hasLayer() const; virtual Region* getRegion() const; - virtual GLint getTargetFbo() const; + virtual GLuint getTargetFbo() const; virtual bool suppressErrorChecks() const; private: diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index ba22071..2268386 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -89,8 +89,9 @@ uint8_t Matrix4::getType() const { float m01 = data[kSkewX]; float m10 = data[kSkewY]; float m11 = data[kScaleY]; + float m32 = data[kTranslateZ]; - if (m01 != 0.0f || m10 != 0.0f) { + if (m01 != 0.0f || m10 != 0.0f || m32 != 0.0f) { mType |= kTypeAffine; } @@ -131,11 +132,13 @@ bool Matrix4::changesBounds() const { } bool Matrix4::isPureTranslate() const { - return getGeometryType() <= kTypeTranslate; + // NOTE: temporary hack to workaround ignoreTransform behavior with Z values + // TODO: separate this into isPure2dTranslate vs isPure3dTranslate + return getGeometryType() <= kTypeTranslate && (data[kTranslateZ] == 0.0f); } bool Matrix4::isSimple() const { - return getGeometryType() <= (kTypeScale | kTypeTranslate); + return getGeometryType() <= (kTypeScale | kTypeTranslate) && (data[kTranslateZ] == 0.0f); } bool Matrix4::isIdentity() const { @@ -382,6 +385,19 @@ void Matrix4::loadOrtho(float left, float right, float bottom, float top, float mType = kTypeTranslate | kTypeScale | kTypeRectToRect; } +float Matrix4::mapZ(const Vector3& orig) const { + // duplicates logic for mapPoint3d's z coordinate + return orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ]; +} + +void Matrix4::mapPoint3d(Vector3& vec) const { + //TODO: optimize simple case + const Vector3 orig(vec); + vec.x = orig.x * data[kScaleX] + orig.y * data[kSkewX] + orig.z * data[8] + data[kTranslateX]; + vec.y = orig.x * data[kSkewY] + orig.y * data[kScaleY] + orig.z * data[9] + data[kTranslateY]; + vec.z = orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ]; +} + #define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c) void Matrix4::mapPoint(float& x, float& y) const { @@ -466,8 +482,8 @@ void Matrix4::decomposeScale(float& sx, float& sy) const { sy = copysignf(sqrtf(len), data[mat4::kScaleY]); } -void Matrix4::dump() const { - ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType()); +void Matrix4::dump(const char* label) const { + ALOGD("%s[simple=%d, type=0x%x", label ? label : "Matrix4", isSimple(), getType()); ALOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]); ALOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]); ALOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index b861ba4..e33a001 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -26,12 +26,20 @@ namespace android { namespace uirenderer { -#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]" -#define MATRIX_ARGS(m) \ +#define SK_MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]" +#define SK_MATRIX_ARGS(m) \ (m)->get(0), (m)->get(1), (m)->get(2), \ (m)->get(3), (m)->get(4), (m)->get(5), \ (m)->get(6), (m)->get(7), (m)->get(8) +#define MATRIX_4_STRING "[%.2f %.2f %.2f %.2f] [%.2f %.2f %.2f %.2f]" \ + " [%.2f %.2f %.2f %.2f] [%.2f %.2f %.2f %.2f]" +#define MATRIX_4_ARGS(m) \ + (m)->data[0], (m)->data[4], (m)->data[8], (m)->data[12], \ + (m)->data[1], (m)->data[5], (m)->data[9], (m)->data[13], \ + (m)->data[2], (m)->data[6], (m)->data[10], (m)->data[14], \ + (m)->data[3], (m)->data[7], (m)->data[11], (m)->data[15] \ + /////////////////////////////////////////////////////////////////////////////// // Classes /////////////////////////////////////////////////////////////////////////////// @@ -134,17 +142,18 @@ public: void multiply(float v); - void translate(float x, float y) { + void translate(float x, float y, float z = 0) { if ((getType() & sGeometryMask) <= kTypeTranslate) { data[kTranslateX] += x; data[kTranslateY] += y; + data[kTranslateZ] += z; } else { // Doing a translation will only affect the translate bit of the type // Save the type uint8_t type = mType; Matrix4 u; - u.loadTranslate(x, y, 0.0f); + u.loadTranslate(x, y, z); multiply(u); // Restore the type and fix the translate bit @@ -190,15 +199,17 @@ public: void copyTo(float* v) const; void copyTo(SkMatrix& v) const; - void mapRect(Rect& r) const; - void mapPoint(float& x, float& y) const; + float mapZ(const Vector3& orig) const; + void mapPoint3d(Vector3& vec) const; + void mapPoint(float& x, float& y) const; // 2d only + void mapRect(Rect& r) const; // 2d only float getTranslateX() const; float getTranslateY() const; void decomposeScale(float& sx, float& sy) const; - void dump() const; + void dump(const char* label = NULL) const; static const Matrix4& identity(); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 4d76bed..20b038d 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -36,7 +36,10 @@ #include "Fence.h" #include "PathTessellator.h" #include "Properties.h" +#include "ShadowTessellator.h" +#include "utils/GLUtils.h" #include "Vector.h" +#include "VertexBuffer.h" namespace android { namespace uirenderer { @@ -50,7 +53,12 @@ namespace uirenderer { #define ALPHA_THRESHOLD 0 -#define FILTER(paint) (!paint || paint->isFilterBitmap() ? GL_LINEAR : GL_NEAREST) +static GLenum getFilter(const SkPaint* paint) { + if (!paint || paint->getFilterLevel() != SkPaint::kNone_FilterLevel) { + return GL_LINEAR; + } + return GL_NEAREST; +} /////////////////////////////////////////////////////////////////////////////// // Globals @@ -127,7 +135,6 @@ OpenGLRenderer::OpenGLRenderer(): memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices)); - mFirstSnapshot = new Snapshot; mFrameStarted = false; mCountOverdraw = false; @@ -154,22 +161,6 @@ void OpenGLRenderer::initProperties() { // Setup /////////////////////////////////////////////////////////////////////////////// -void OpenGLRenderer::setName(const char* name) { - if (name) { - mName.setTo(name); - } else { - mName.clear(); - } -} - -const char* OpenGLRenderer::getName() const { - return mName.string(); -} - -bool OpenGLRenderer::isDeferred() { - return false; -} - void OpenGLRenderer::setViewport(int width, int height) { initViewport(width, height); @@ -180,26 +171,17 @@ void OpenGLRenderer::setViewport(int width, int height) { } void OpenGLRenderer::initViewport(int width, int height) { - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); - - mWidth = width; - mHeight = height; + mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1); - mFirstSnapshot->height = height; - mFirstSnapshot->viewport.set(0, 0, width, height); + initializeViewport(width, height); } void OpenGLRenderer::setupFrameState(float left, float top, float right, float bottom, bool opaque) { mCaches.clearGarbage(); + initializeSaveStack(left, top, right, bottom); mOpaque = opaque; - mSnapshot = new Snapshot(mFirstSnapshot, - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); - mSnapshot->fbo = getTargetFbo(); - mSaveCount = 1; - - mSnapshot->setClip(left, top, right, bottom); mTilingClip.set(left, top, right, bottom); } @@ -211,14 +193,14 @@ status_t OpenGLRenderer::startFrame() { discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom); - glViewport(0, 0, mWidth, mHeight); + glViewport(0, 0, getWidth(), getHeight()); // Functors break the tiling extension in pretty spectacular ways // This ensures we don't use tiling when a functor is going to be // invoked during the frame mSuppressTiling = mCaches.hasRegisteredFunctors(); - startTiling(mSnapshot, true); + startTilingCurrentClip(true); debugOverdraw(true, true); @@ -226,10 +208,6 @@ status_t OpenGLRenderer::startFrame() { mTilingClip.right, mTilingClip.bottom, mOpaque); } -status_t OpenGLRenderer::prepare(bool opaque) { - return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque); -} - status_t OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { @@ -239,7 +217,7 @@ status_t OpenGLRenderer::prepareDirty(float left, float top, // The framebuffer renderer will first defer the display list // for each layer and wait until the first drawing command // to start the frame - if (mSnapshot->fbo == 0) { + if (currentSnapshot()->fbo == 0) { syncState(); updateLayers(); } else { @@ -254,7 +232,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa // perform a discard to let the driver know we don't need to preserve // the back buffer for this frame. if (mExtensions.hasDiscardFramebuffer() && - left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) { + left <= 0.0f && top <= 0.0f && right >= getWidth() && bottom >= getHeight()) { const bool isFbo = getTargetFbo() == 0; const GLenum attachments[] = { isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0, @@ -266,7 +244,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { if (!opaque || mCountOverdraw) { mCaches.enableScissor(); - mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top); + mCaches.setScissor(left, currentSnapshot()->height - bottom, right - left, bottom - top); glClear(GL_COLOR_BUFFER_BIT); return DrawGlInfo::kStatusDrew; } @@ -283,14 +261,16 @@ void OpenGLRenderer::syncState() { } } -void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) { +void OpenGLRenderer::startTilingCurrentClip(bool opaque) { if (!mSuppressTiling) { - Rect* clip = &mTilingClip; - if (s->flags & Snapshot::kFlagFboTarget) { - clip = &(s->layer->clipRect); + const Snapshot* snapshot = currentSnapshot(); + + const Rect* clip = &mTilingClip; + if (snapshot->flags & Snapshot::kFlagFboTarget) { + clip = &(snapshot->layer->clipRect); } - startTiling(*clip, s->height, opaque); + startTiling(*clip, snapshot->height, opaque); } } @@ -317,24 +297,7 @@ void OpenGLRenderer::finish() { if (!suppressErrorChecks()) { #if DEBUG_OPENGL - GLenum status = GL_NO_ERROR; - while ((status = glGetError()) != GL_NO_ERROR) { - ALOGD("GL error from OpenGLRenderer: 0x%x", status); - switch (status) { - case GL_INVALID_ENUM: - ALOGE(" GL_INVALID_ENUM"); - break; - case GL_INVALID_VALUE: - ALOGE(" GL_INVALID_VALUE"); - break; - case GL_INVALID_OPERATION: - ALOGE(" GL_INVALID_OPERATION"); - break; - case GL_OUT_OF_MEMORY: - ALOGE(" Out of memory!"); - break; - } - } + GLUtils::dumpGLErrors(); #endif #if DEBUG_MEMORY_USAGE @@ -369,7 +332,7 @@ void OpenGLRenderer::interrupt() { } void OpenGLRenderer::resume() { - sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot; + const Snapshot* snapshot = currentSnapshot(); glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); debugOverdraw(true, false); @@ -391,7 +354,7 @@ void OpenGLRenderer::resume() { } void OpenGLRenderer::resumeAfterLayer() { - sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot; + const Snapshot* snapshot = currentSnapshot(); glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); debugOverdraw(true, false); @@ -400,63 +363,14 @@ void OpenGLRenderer::resumeAfterLayer() { dirtyClip(); } -void OpenGLRenderer::detachFunctor(Functor* functor) { - mFunctors.remove(functor); -} - -void OpenGLRenderer::attachFunctor(Functor* functor) { - mFunctors.add(functor); -} - -status_t OpenGLRenderer::invokeFunctors(Rect& dirty) { - status_t result = DrawGlInfo::kStatusDone; - size_t count = mFunctors.size(); - - if (count > 0) { - interrupt(); - SortedVector<Functor*> functors(mFunctors); - mFunctors.clear(); - - DrawGlInfo info; - info.clipLeft = 0; - info.clipTop = 0; - info.clipRight = 0; - info.clipBottom = 0; - info.isLayer = false; - info.width = 0; - info.height = 0; - memset(info.transform, 0, sizeof(float) * 16); - - for (size_t i = 0; i < count; i++) { - Functor* f = functors.itemAt(i); - result |= (*f)(DrawGlInfo::kModeProcess, &info); - - if (result & DrawGlInfo::kStatusDraw) { - Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom); - dirty.unionWith(localDirty); - } - - if (result & DrawGlInfo::kStatusInvoke) { - mFunctors.add(f); - } - } - resume(); - } - - return result; -} - status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { - if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; - - detachFunctor(functor); + if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; - - Rect clip(*mSnapshot->clipRect); + Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); // Since we don't know what the functor will draw, let's dirty - // tne entire clip region + // the entire clip region if (hasLayer()) { dirtyLayerUnchecked(clip, getRegion()); } @@ -467,9 +381,9 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { info.clipRight = clip.right; info.clipBottom = clip.bottom; info.isLayer = hasLayer(); - info.width = getSnapshot()->viewport.getWidth(); - info.height = getSnapshot()->height; - getSnapshot()->transform->copyTo(&info.transform[0]); + info.width = currentSnapshot()->viewport.getWidth(); + info.height = currentSnapshot()->height; + currentTransform()->copyTo(&info.transform[0]); bool dirtyClip = mDirtyClip; // setup GL state for functor @@ -482,19 +396,10 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { interrupt(); // call functor immediately after GL state setup - status_t result = (*functor)(DrawGlInfo::kModeDraw, &info); - - if (result != DrawGlInfo::kStatusDone) { - Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom); - dirty.unionWith(localDirty); - - if (result & DrawGlInfo::kStatusInvoke) { - mFunctors.add(functor); - } - } + (*functor)(DrawGlInfo::kModeDraw, &info); resume(); - return result | DrawGlInfo::kStatusDrew; + return DrawGlInfo::kStatusDrew; } /////////////////////////////////////////////////////////////////////////////// @@ -532,7 +437,7 @@ void OpenGLRenderer::renderOverdraw() { const Rect* clip = &mTilingClip; mCaches.enableScissor(); - mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom, + mCaches.setScissor(clip->left, firstSnapshot()->height - clip->bottom, clip->right - clip->left, clip->bottom - clip->top); // 1x overdraw @@ -556,9 +461,9 @@ void OpenGLRenderer::renderOverdraw() { } void OpenGLRenderer::countOverdraw() { - size_t count = mWidth * mHeight; + size_t count = getWidth() * getHeight(); uint32_t* buffer = new uint32_t[count]; - glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); + glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); size_t total = 0; for (size_t i = 0; i < count; i++) { @@ -576,7 +481,7 @@ void OpenGLRenderer::countOverdraw() { bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { if (layer->deferredUpdateScheduled && layer->renderer && - layer->displayList && layer->displayList->isRenderable()) { + layer->displayList.get() && layer->displayList->isRenderable()) { ATRACE_CALL(); Rect& dirty = layer->dirtyRect; @@ -594,7 +499,7 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { if (inFrame) { resumeAfterLayer(); - startTiling(mSnapshot); + startTilingCurrentClip(); } layer->debugDrawUpdate = mCaches.debugLayersUpdates; @@ -715,50 +620,17 @@ void OpenGLRenderer::flushLayerUpdates() { // State management /////////////////////////////////////////////////////////////////////////////// -int OpenGLRenderer::getSaveCount() const { - return mSaveCount; -} - -int OpenGLRenderer::save(int flags) { - return saveSnapshot(flags); -} - -void OpenGLRenderer::restore() { - if (mSaveCount > 1) { - restoreSnapshot(); - } -} - -void OpenGLRenderer::restoreToCount(int saveCount) { - if (saveCount < 1) saveCount = 1; - - while (mSaveCount > saveCount) { - restoreSnapshot(); - } -} - -int OpenGLRenderer::saveSnapshot(int flags) { - mSnapshot = new Snapshot(mSnapshot, flags); - return mSaveCount++; -} - -bool OpenGLRenderer::restoreSnapshot() { - bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; - bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; - bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho; - - sp<Snapshot> current = mSnapshot; - sp<Snapshot> previous = mSnapshot->previous; +void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { + bool restoreOrtho = removed.flags & Snapshot::kFlagDirtyOrtho; + bool restoreClip = removed.flags & Snapshot::kFlagClipSet; + bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer; if (restoreOrtho) { - Rect& r = previous->viewport; + const Rect& r = restored.viewport; glViewport(r.left, r.top, r.right, r.bottom); - mOrthoMatrix.load(current->orthoMatrix); + mProjectionMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'? } - mSaveCount--; - mSnapshot = previous; - if (restoreClip) { dirtyClip(); } @@ -766,11 +638,9 @@ bool OpenGLRenderer::restoreSnapshot() { if (restoreLayer) { endMark(); // Savelayer startMark("ComposeLayer"); - composeLayer(current, previous); + composeLayer(removed, restored); endMark(); } - - return restoreClip; } /////////////////////////////////////////////////////////////////////////////// @@ -778,12 +648,11 @@ bool OpenGLRenderer::restoreSnapshot() { /////////////////////////////////////////////////////////////////////////////// int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags) { - const GLuint previousFbo = mSnapshot->fbo; + const SkPaint* paint, int flags, const SkPath* convexMask) { const int count = saveSnapshot(flags); - if (!mSnapshot->isIgnored()) { - createLayer(left, top, right, bottom, alpha, mode, flags, previousFbo); + if (!currentSnapshot()->isIgnored()) { + createLayer(left, top, right, bottom, paint, flags, convexMask); } return count; @@ -792,22 +661,22 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) { const Rect untransformedBounds(bounds); - currentTransform().mapRect(bounds); + currentTransform()->mapRect(bounds); // Layers only make sense if they are in the framebuffer's bounds - if (bounds.intersect(*mSnapshot->clipRect)) { + if (bounds.intersect(*currentClipRect())) { // We cannot work with sub-pixels in this case bounds.snapToPixelBoundaries(); // When the layer is not an FBO, we may use glCopyTexImage so we // need to make sure the layer does not extend outside the bounds // of the framebuffer - if (!bounds.intersect(mSnapshot->previous->viewport)) { + if (!bounds.intersect(currentSnapshot()->previous->viewport)) { bounds.setEmpty(); } else if (fboLayer) { clip.set(bounds); mat4 inverse; - inverse.loadInverse(currentTransform()); + inverse.loadInverse(*currentTransform()); inverse.mapRect(clip); clip.snapToPixelBoundaries(); if (clip.intersect(untransformedBounds)) { @@ -834,11 +703,10 @@ void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect } int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags) { - const GLuint previousFbo = mSnapshot->fbo; + const SkPaint* paint, int flags) { const int count = saveSnapshot(flags); - if (!mSnapshot->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) { + if (!currentSnapshot()->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) { // initialize the snapshot as though it almost represents an FBO layer so deferred draw // operations will be able to store and restore the current clip and transform info, and // quick rejection will be correct (for display lists) @@ -846,9 +714,9 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float Rect bounds(left, top, right, bottom); Rect clip; calculateLayerBoundsAndClip(bounds, clip, true); - updateSnapshotIgnoreForLayer(bounds, clip, true, alpha); + updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint)); - if (!mSnapshot->isIgnored()) { + if (!currentSnapshot()->isIgnored()) { mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); @@ -858,7 +726,6 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float return count; } - /** * Layers are viewed by Skia are slightly different than layers in image editing * programs (for instance.) When a layer is created, previously created layers @@ -911,7 +778,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float * something actually gets drawn are the layers regions cleared. */ bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo) { + const SkPaint* paint, int flags, const SkPath* convexMask) { LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top); LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); @@ -921,10 +788,10 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto Rect clip; Rect bounds(left, top, right, bottom); calculateLayerBoundsAndClip(bounds, clip, fboLayer); - updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, alpha); + updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint)); // Bail out if we won't draw in this snapshot - if (mSnapshot->isIgnored()) { + if (currentSnapshot()->isIgnored()) { return false; } @@ -934,13 +801,14 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto return false; } - layer->setAlpha(alpha, mode); + layer->setPaint(paint); layer->layer.set(bounds); layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()), bounds.getWidth() / float(layer->getWidth()), 0.0f); - layer->setColorFilter(mDrawModifiers.mColorFilter); + layer->setBlend(true); layer->setDirty(false); + layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache // Save the layer in the snapshot mSnapshot->flags |= Snapshot::kFlagIsLayer; @@ -948,7 +816,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto startMark("SaveLayer"); if (fboLayer) { - return createFboLayer(layer, bounds, clip, previousFbo); + return createFboLayer(layer, bounds, clip); } else { // Copy the framebuffer into the layer layer->bindTexture(); @@ -974,7 +842,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto return true; } -bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) { +bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { layer->clipRect.set(clip); layer->setFbo(mCaches.fboCache.get()); @@ -986,7 +854,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); mSnapshot->height = bounds.getHeight(); - mSnapshot->orthoMatrix.load(mOrthoMatrix); + mSnapshot->orthoMatrix.load(mProjectionMatrix); endTiling(); debugOverdraw(false, false); @@ -1003,7 +871,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->getTexture(), 0); - startTiling(mSnapshot, true); + startTilingCurrentClip(true); // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering mCaches.enableScissor(); @@ -1015,7 +883,8 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui // Change the ortho projection glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + + mProjectionMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); return true; } @@ -1023,18 +892,19 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui /** * Read the documentation of createLayer() before doing anything in this method. */ -void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { - if (!current->layer) { +void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& restored) { + if (!removed.layer) { ALOGE("Attempting to compose a layer that does not exist"); return; } - Layer* layer = current->layer; + Layer* layer = removed.layer; const Rect& rect = layer->layer; - const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer; + const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer; bool clipRequired = false; - quickRejectNoScissor(rect, &clipRequired); // safely ignore return, should never be rejected + calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom, + &clipRequired, false); // safely ignore return, should never be rejected mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); if (fboLayer) { @@ -1046,15 +916,19 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { layer->removeFbo(false); // Unbind current FBO and restore previous one - glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, restored.fbo); debugOverdraw(true, false); - startTiling(previous); + startTilingCurrentClip(); } if (!fboLayer && layer->getAlpha() < 255) { - drawColorRect(rect.left, rect.top, rect.right, rect.bottom, - layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true); + SkPaint layerPaint; + layerPaint.setAlpha(layer->getAlpha()); + layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode); + layerPaint.setColorFilter(layer->getColorFilter()); + + drawColorRect(rect.left, rect.top, rect.right, rect.bottom, &layerPaint, true); // Required below, composeLayerRect() will divide by 255 layer->setAlpha(255); } @@ -1066,14 +940,8 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { // When the layer is stored in an FBO, we can save a bit of fillrate by // drawing only the dirty region if (fboLayer) { - dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform); - if (layer->getColorFilter()) { - setupColorFilter(layer->getColorFilter()); - } + dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *restored.transform); composeLayerRegion(layer, rect); - if (layer->getColorFilter()) { - resetColorFilter(); - } } else if (!rect.isEmpty()) { dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); @@ -1088,6 +956,7 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { dirtyClip(); // Failing to add the layer to the cache should happen only if the layer is too large + layer->setConvexMask(NULL); if (!mCaches.layerCache.put(layer)) { LAYER_LOGD("Deleting layer"); Caches::getInstance().resourceCache.decrementRefcount(layer); @@ -1105,30 +974,33 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { } setupDrawTextureTransform(); setupDrawColor(alpha, alpha, alpha, alpha); - setupDrawColorFilter(); - setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode()); + setupDrawColorFilter(layer->getColorFilter()); + setupDrawBlending(layer); setupDrawProgram(); setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); + setupDrawColorFilterUniforms(layer->getColorFilter()); if (layer->getRenderTarget() == GL_TEXTURE_2D) { setupDrawTexture(layer->getTexture()); } else { setupDrawExternalTexture(layer->getTexture()); } - if (currentTransform().isPureTranslate() && + if (currentTransform()->isPureTranslate() && + !layer->getForceFilter() && layer->getWidth() == (uint32_t) rect.getWidth() && layer->getHeight() == (uint32_t) rect.getHeight()) { - const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f); - const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f); + const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); + const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); layer->setFilter(GL_NEAREST); - setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + x, y, x + rect.getWidth(), y + rect.getHeight(), true); } else { layer->setFilter(GL_LINEAR); - setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + rect.left, rect.top, rect.right, rect.bottom); } setupDrawTextureTransformUniforms(layer->getTexTransform()); - setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]); + setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u); glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); } @@ -1141,15 +1013,15 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) float x = rect.left; float y = rect.top; - bool simpleTransform = currentTransform().isPureTranslate() && + bool simpleTransform = currentTransform()->isPureTranslate() && layer->getWidth() == (uint32_t) rect.getWidth() && layer->getHeight() == (uint32_t) rect.getHeight(); if (simpleTransform) { // When we're swapping, the layer is already in screen coordinates if (!swap) { - x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f); - y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f); + x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); + y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); } layer->setFilter(GL_NEAREST, true); @@ -1157,11 +1029,15 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) layer->setFilter(GL_LINEAR, true); } - float alpha = getLayerAlpha(layer); - bool blend = layer->isBlend() || alpha < 1.0f; + SkPaint layerPaint; + layerPaint.setAlpha(getLayerAlpha(layer) * 255); + layerPaint.setXfermodeMode(layer->getMode()); + layerPaint.setColorFilter(layer->getColorFilter()); + + bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f; drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(), - layer->getTexture(), alpha, layer->getMode(), blend, - &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + layer->getTexture(), &layerPaint, blend, + &mMeshVertices[0].x, &mMeshVertices[0].u, GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform); resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); @@ -1190,6 +1066,38 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) #define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND) void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { + if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw + + if (layer->getConvexMask()) { + save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); + + // clip to the area of the layer the mask can be larger + clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0)); + + SkiaShader* oldShader = mDrawModifiers.mShader; + + // create LayerShader to map SaveLayer content into subsequent draw + SkMatrix shaderMatrix; + shaderMatrix.setTranslate(rect.left, rect.bottom); + shaderMatrix.preScale(1, -1); + SkiaLayerShader layerShader(layer, &shaderMatrix); + mDrawModifiers.mShader = &layerShader; + + // Since the drawing primitive is defined in local drawing space, + // we don't need to modify the draw matrix + const SkPath* maskPath = layer->getConvexMask(); + DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint)); + + mDrawModifiers.mShader = oldShader; + restore(); + + return; + } + if (layer->region.isRect()) { layer->setRegionAsRect(); @@ -1199,90 +1107,91 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { return; } - if (CC_LIKELY(!layer->region.isEmpty())) { - size_t count; - const android::Rect* rects; - Region safeRegion; - if (CC_LIKELY(hasRectToRectTransform())) { - rects = layer->region.getArray(&count); - } else { - safeRegion = Region::createTJunctionFreeRegion(layer->region); - rects = safeRegion.getArray(&count); - } + // standard Region based draw + size_t count; + const android::Rect* rects; + Region safeRegion; + if (CC_LIKELY(hasRectToRectTransform())) { + rects = layer->region.getArray(&count); + } else { + safeRegion = Region::createTJunctionFreeRegion(layer->region); + rects = safeRegion.getArray(&count); + } - const float alpha = getLayerAlpha(layer); - const float texX = 1.0f / float(layer->getWidth()); - const float texY = 1.0f / float(layer->getHeight()); - const float height = rect.getHeight(); + const float alpha = getLayerAlpha(layer); + const float texX = 1.0f / float(layer->getWidth()); + const float texY = 1.0f / float(layer->getHeight()); + const float height = rect.getHeight(); - setupDraw(); + setupDraw(); - // We must get (and therefore bind) the region mesh buffer - // after we setup drawing in case we need to mess with the - // stencil buffer in setupDraw() - TextureVertex* mesh = mCaches.getRegionMesh(); - uint32_t numQuads = 0; + // We must get (and therefore bind) the region mesh buffer + // after we setup drawing in case we need to mess with the + // stencil buffer in setupDraw() + TextureVertex* mesh = mCaches.getRegionMesh(); + uint32_t numQuads = 0; - setupDrawWithTexture(); - setupDrawColor(alpha, alpha, alpha, alpha); - setupDrawColorFilter(); - setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode(), false); - setupDrawProgram(); - setupDrawDirtyRegionsDisabled(); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); - setupDrawTexture(layer->getTexture()); - if (currentTransform().isPureTranslate()) { - const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f); - const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f); + setupDrawWithTexture(); + setupDrawColor(alpha, alpha, alpha, alpha); + setupDrawColorFilter(layer->getColorFilter()); + setupDrawBlending(layer); + setupDrawProgram(); + setupDrawDirtyRegionsDisabled(); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(layer->getColorFilter()); + setupDrawTexture(layer->getTexture()); + if (currentTransform()->isPureTranslate()) { + const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); + const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); - layer->setFilter(GL_NEAREST); - setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true); - } else { - layer->setFilter(GL_LINEAR); - setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); - } - setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]); + layer->setFilter(GL_NEAREST); + setupDrawModelView(kModelViewMode_Translate, false, + x, y, x + rect.getWidth(), y + rect.getHeight(), true); + } else { + layer->setFilter(GL_LINEAR); + setupDrawModelView(kModelViewMode_Translate, false, + rect.left, rect.top, rect.right, rect.bottom); + } + setupDrawMeshIndices(&mesh[0].x, &mesh[0].u); - for (size_t i = 0; i < count; i++) { - const android::Rect* r = &rects[i]; - - const float u1 = r->left * texX; - const float v1 = (height - r->top) * texY; - const float u2 = r->right * texX; - const float v2 = (height - r->bottom) * texY; - - // TODO: Reject quads outside of the clip - TextureVertex::set(mesh++, r->left, r->top, u1, v1); - TextureVertex::set(mesh++, r->right, r->top, u2, v1); - TextureVertex::set(mesh++, r->left, r->bottom, u1, v2); - TextureVertex::set(mesh++, r->right, r->bottom, u2, v2); - - numQuads++; - - if (numQuads >= gMaxNumberOfQuads) { - DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6, - GL_UNSIGNED_SHORT, NULL)); - numQuads = 0; - mesh = mCaches.getRegionMesh(); - } - } + for (size_t i = 0; i < count; i++) { + const android::Rect* r = &rects[i]; + + const float u1 = r->left * texX; + const float v1 = (height - r->top) * texY; + const float u2 = r->right * texX; + const float v2 = (height - r->bottom) * texY; + + // TODO: Reject quads outside of the clip + TextureVertex::set(mesh++, r->left, r->top, u1, v1); + TextureVertex::set(mesh++, r->right, r->top, u2, v1); + TextureVertex::set(mesh++, r->left, r->bottom, u1, v2); + TextureVertex::set(mesh++, r->right, r->bottom, u2, v2); - if (numQuads > 0) { + numQuads++; + + if (numQuads >= gMaxNumberOfQuads) { DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL)); + numQuads = 0; + mesh = mCaches.getRegionMesh(); } + } + + if (numQuads > 0) { + DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6, + GL_UNSIGNED_SHORT, NULL)); + } #if DEBUG_LAYERS_AS_REGIONS - drawRegionRects(layer->region); + drawRegionRectsDebug(layer->region); #endif - layer->region.clear(); - } + layer->region.clear(); } -void OpenGLRenderer::drawRegionRects(const Region& region) { #if DEBUG_LAYERS_AS_REGIONS +void OpenGLRenderer::drawRegionRectsDebug(const Region& region) { size_t count; const android::Rect* rects = region.getArray(&count); @@ -1300,15 +1209,15 @@ void OpenGLRenderer::drawRegionRects(const Region& region) { top = rects[i].top; } + SkPaint paint; + paint.setColor(colors[offset + (i & 0x1)]); Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom); - drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)], - SkXfermode::kSrcOver_Mode); + drawColorRect(r.left, r.top, r.right, r.bottom, paint); } -#endif } +#endif -void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color, - SkXfermode::Mode mode, bool dirty) { +void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty) { Vector<float> rects; SkRegion::Iterator it(region); @@ -1321,7 +1230,7 @@ void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color, it.next(); } - drawColorRects(rects.array(), rects.size(), color, mode, true, dirty, false); + drawColorRects(rects.array(), rects.size(), &paint, true, dirty, false); } void OpenGLRenderer::dirtyLayer(const float left, const float top, @@ -1342,7 +1251,7 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top, } void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { - if (bounds.intersect(*mSnapshot->clipRect)) { + if (bounds.intersect(*currentClipRect())) { bounds.snapToPixelBoundaries(); android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); if (!dirty.isEmpty()) { @@ -1351,12 +1260,12 @@ void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { } } -void OpenGLRenderer::drawIndexedQuads(Vertex* mesh, GLsizei quadsCount) { +void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) { GLsizei elementsCount = quadsCount * 6; while (elementsCount > 0) { GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6); - setupDrawIndexedVertices(&mesh[0].position[0]); + setupDrawIndexedVertices(&mesh[0].x); glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL); elementsCount -= drawCount; @@ -1370,7 +1279,7 @@ void OpenGLRenderer::clearLayerRegions() { const size_t count = mLayers.size(); if (count == 0) return; - if (!mSnapshot->isIgnored()) { + if (!currentSnapshot()->isIgnored()) { // Doing several glScissor/glClear here can negatively impact // GPUs with a tiler architecture, instead we draw quads with // the Clear blending mode @@ -1398,14 +1307,18 @@ void OpenGLRenderer::clearLayerRegions() { // the same thing again mLayers.clear(); + SkPaint clearPaint; + clearPaint.setXfermodeMode(SkXfermode::kClear_Mode); + setupDraw(false); setupDrawColor(0.0f, 0.0f, 0.0f, 1.0f); - setupDrawBlending(true, SkXfermode::kClear_Mode); + setupDrawBlending(&clearPaint, true); setupDrawProgram(); setupDrawPureColorUniforms(); - setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true); + setupDrawModelView(kModelViewMode_Translate, false, + 0.0f, 0.0f, 0.0f, 0.0f, true); - drawIndexedQuads(&mesh[0], count); + issueIndexedQuadDraw(&mesh[0], count); if (scissorChanged) mCaches.enableScissor(); } else { @@ -1421,58 +1334,58 @@ void OpenGLRenderer::clearLayerRegions() { /////////////////////////////////////////////////////////////////////////////// bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) { - const Rect& currentClip = *(mSnapshot->clipRect); - const mat4& currentMatrix = *(mSnapshot->transform); + const Rect* currentClip = currentClipRect(); + const mat4* currentMatrix = currentTransform(); if (stateDeferFlags & kStateDeferFlag_Draw) { // state has bounds initialized in local coordinates if (!state.mBounds.isEmpty()) { - currentMatrix.mapRect(state.mBounds); + currentMatrix->mapRect(state.mBounds); Rect clippedBounds(state.mBounds); // NOTE: if we ever want to use this clipping info to drive whether the scissor // is used, it should more closely duplicate the quickReject logic (in how it uses // snapToPixelBoundaries) - if(!clippedBounds.intersect(currentClip)) { + if(!clippedBounds.intersect(*currentClip)) { // quick rejected return true; } state.mClipSideFlags = kClipSide_None; - if (!currentClip.contains(state.mBounds)) { + if (!currentClip->contains(state.mBounds)) { int& flags = state.mClipSideFlags; // op partially clipped, so record which sides are clipped for clip-aware merging - if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left; - if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top; - if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right; - if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom; + if (currentClip->left > state.mBounds.left) flags |= kClipSide_Left; + if (currentClip->top > state.mBounds.top) flags |= kClipSide_Top; + if (currentClip->right < state.mBounds.right) flags |= kClipSide_Right; + if (currentClip->bottom < state.mBounds.bottom) flags |= kClipSide_Bottom; } state.mBounds.set(clippedBounds); } else { // Empty bounds implies size unknown. Label op as conservatively clipped to disable // overdraw avoidance (since we don't know what it overlaps) state.mClipSideFlags = kClipSide_ConservativeFull; - state.mBounds.set(currentClip); + state.mBounds.set(*currentClip); } } state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip); if (state.mClipValid) { - state.mClip.set(currentClip); + state.mClip.set(*currentClip); } // Transform, drawModifiers, and alpha always deferred, since they are used by state operations // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything) - state.mMatrix.load(currentMatrix); + state.mMatrix.load(*currentMatrix); state.mDrawModifiers = mDrawModifiers; - state.mAlpha = mSnapshot->alpha; + state.mAlpha = currentSnapshot()->alpha; return false; } void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) { - currentTransform().load(state.mMatrix); - mDrawModifiers = state.mDrawModifiers; + setMatrix(state.mMatrix); mSnapshot->alpha = state.mAlpha; + mDrawModifiers = state.mDrawModifiers; if (state.mClipValid && !skipClipRestore) { mSnapshot->setClip(state.mClip.left, state.mClip.top, @@ -1492,64 +1405,21 @@ void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) { if (clipRect != NULL) { mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); } else { - mSnapshot->setClip(0, 0, mWidth, mHeight); + mSnapshot->setClip(0, 0, getWidth(), getHeight()); } dirtyClip(); mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled); } /////////////////////////////////////////////////////////////////////////////// -// Transforms -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::translate(float dx, float dy) { - currentTransform().translate(dx, dy); -} - -void OpenGLRenderer::rotate(float degrees) { - currentTransform().rotate(degrees, 0.0f, 0.0f, 1.0f); -} - -void OpenGLRenderer::scale(float sx, float sy) { - currentTransform().scale(sx, sy, 1.0f); -} - -void OpenGLRenderer::skew(float sx, float sy) { - currentTransform().skew(sx, sy); -} - -void OpenGLRenderer::setMatrix(SkMatrix* matrix) { - if (matrix) { - currentTransform().load(*matrix); - } else { - currentTransform().loadIdentity(); - } -} - -bool OpenGLRenderer::hasRectToRectTransform() { - return CC_LIKELY(currentTransform().rectToRect()); -} - -void OpenGLRenderer::getMatrix(SkMatrix* matrix) { - currentTransform().copyTo(*matrix); -} - -void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { - SkMatrix transform; - currentTransform().copyTo(transform); - transform.preConcat(*matrix); - currentTransform().load(transform); -} - -/////////////////////////////////////////////////////////////////////////////// // Clipping /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::setScissorFromClip() { - Rect clip(*mSnapshot->clipRect); + Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); - if (mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom, + if (mCaches.setScissor(clip.left, currentSnapshot()->height - clip.bottom, clip.getWidth(), clip.getHeight())) { mDirtyClip = false; } @@ -1560,7 +1430,7 @@ void OpenGLRenderer::ensureStencilBuffer() { // cannot attach a stencil buffer to fbo0 dynamically. Let's // just hope we have one when hasLayer() returns false. if (hasLayer()) { - attachStencilBufferToLayer(mSnapshot->layer); + attachStencilBufferToLayer(currentSnapshot()->layer); } } @@ -1582,7 +1452,7 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) { void OpenGLRenderer::setStencilFromClip() { if (!mCaches.debugOverdraw) { - if (!mSnapshot->clipRegion->isEmpty()) { + if (!currentSnapshot()->clipRegion->isEmpty()) { // NOTE: The order here is important, we must set dirtyClip to false // before any draw call to avoid calling back into this method mDirtyClip = false; @@ -1601,20 +1471,26 @@ void OpenGLRenderer::setStencilFromClip() { mCaches.stencil.clear(); if (resetScissor) mCaches.disableScissor(); + SkPaint paint; + paint.setColor(0xff000000); + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + // NOTE: We could use the region contour path to generate a smaller mesh // Since we are using the stencil we could use the red book path // drawing technique. It might increase bandwidth usage though. // The last parameter is important: we are not drawing in the color buffer // so we don't want to dirty the current layer, if any - drawRegionRects(*mSnapshot->clipRegion, 0xff000000, SkXfermode::kSrc_Mode, false); + drawRegionRects(*(currentSnapshot()->clipRegion), paint, false); mCaches.stencil.enableTest(); // Draw the region used to generate the stencil if the appropriate debug // mode is enabled if (mCaches.debugStencilClip == Caches::kStencilShowRegion) { - drawRegionRects(*mSnapshot->clipRegion, 0x7f0000ff, SkXfermode::kSrcOver_Mode); + paint.setColor(0x7f0000ff); + paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); + drawRegionRects(*(currentSnapshot()->clipRegion), paint); } } else { mCaches.stencil.disable(); @@ -1622,50 +1498,31 @@ void OpenGLRenderer::setStencilFromClip() { } } -const Rect& OpenGLRenderer::getClipBounds() { - return mSnapshot->getLocalClip(); -} - -bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom, - bool snapOut, bool* clipRequired) { - if (mSnapshot->isIgnored() || bottom <= top || right <= left) { - return true; - } - - Rect r(left, top, right, bottom); - currentTransform().mapRect(r); - r.snapGeometryToPixelBoundaries(snapOut); - - Rect clipRect(*mSnapshot->clipRect); - clipRect.snapToPixelBoundaries(); - - if (!clipRect.intersects(r)) return true; - - if (clipRequired) *clipRequired = !clipRect.contains(r); - return false; -} - -bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom, - SkPaint* paint) { - // AA geometry will likely have a ramp around it (not accounted for in local bounds). Snap out - // the final mapped rect to ensure correct clipping behavior for the ramp. - bool snapOut = paint->isAntiAlias(); +/** + * Returns false and sets scissor enable based upon bounds if drawing won't be clipped out. + * + * @param paint if not null, the bounds will be expanded to account for stroke depending on paint + * style, and tessellated AA ramp + */ +bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom, + const SkPaint* paint) { + bool clipRequired = false; + bool snapOut = paint && paint->isAntiAlias(); - if (paint->getStyle() != SkPaint::kFill_Style) { + if (paint && paint->getStyle() != SkPaint::kFill_Style) { float outset = paint->getStrokeWidth() * 0.5f; - return quickReject(left - outset, top - outset, right + outset, bottom + outset, snapOut); - } else { - return quickReject(left, top, right, bottom, snapOut); + left -= outset; + top -= outset; + right += outset; + bottom += outset; } -} -bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom, bool snapOut) { - bool clipRequired = false; - if (quickRejectNoScissor(left, top, right, bottom, snapOut, &clipRequired)) { + if (calculateQuickRejectForScissor(left, top, right, bottom, &clipRequired, snapOut)) { return true; } - if (!isDeferred()) { + if (!isRecording()) { + // not quick rejected, so enable the scissor if clipRequired mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); } return false; @@ -1673,66 +1530,13 @@ bool OpenGLRenderer::quickReject(float left, float top, float right, float botto void OpenGLRenderer::debugClip() { #if DEBUG_CLIP_REGIONS - if (!isDeferred() && !mSnapshot->clipRegion->isEmpty()) { - drawRegionRects(*mSnapshot->clipRegion, 0x7f00ff00, SkXfermode::kSrcOver_Mode); - } -#endif -} - -bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { - if (CC_LIKELY(currentTransform().rectToRect())) { - bool clipped = mSnapshot->clip(left, top, right, bottom, op); - if (clipped) { - dirtyClip(); - } - return !mSnapshot->clipRect->isEmpty(); - } - - SkPath path; - path.addRect(left, top, right, bottom); + if (!isRecording() && !currentSnapshot()->clipRegion->isEmpty()) { + SkPaint paint; + paint.setColor(0x7f00ff00); + drawRegionRects(*(currentSnapshot()->clipRegion, paint); - return OpenGLRenderer::clipPath(&path, op); -} - -bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) { - SkMatrix transform; - currentTransform().copyTo(transform); - - SkPath transformed; - path->transform(transform, &transformed); - - SkRegion clip; - if (!mSnapshot->previous->clipRegion->isEmpty()) { - clip.setRegion(*mSnapshot->previous->clipRegion); - } else { - if (mSnapshot->previous == mFirstSnapshot) { - clip.setRect(0, 0, mWidth, mHeight); - } else { - Rect* bounds = mSnapshot->previous->clipRect; - clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom); - } } - - SkRegion region; - region.setPath(transformed, clip); - - bool clipped = mSnapshot->clipRegionTransformed(region, op); - if (clipped) { - dirtyClip(); - } - return !mSnapshot->clipRect->isEmpty(); -} - -bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) { - bool clipped = mSnapshot->clipRegionTransformed(*region, op); - if (clipped) { - dirtyClip(); - } - return !mSnapshot->clipRect->isEmpty(); -} - -Rect* OpenGLRenderer::getClipRect() { - return mSnapshot->clipRect; +#endif } /////////////////////////////////////////////////////////////////////////////// @@ -1740,7 +1544,7 @@ Rect* OpenGLRenderer::getClipRect() { /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::setupDraw(bool clear) { - // TODO: It would be best if we could do this before quickReject() + // TODO: It would be best if we could do this before quickRejectSetupScissor() // changes the scissor test state if (clear) clearLayerRegions(); // Make sure setScissor & setStencil happen at the beginning of @@ -1798,7 +1602,7 @@ void OpenGLRenderer::setupDrawColor(int color, int alpha) { mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f; mColorB = mColorA * ((color ) & 0xFF) / 255.0f; mColorSet = true; - mSetShaderColor = mDescription.setColor(mColorR, mColorG, mColorB, mColorA); + mSetShaderColor = mDescription.setColorModulate(mColorA); } void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) { @@ -1807,7 +1611,7 @@ void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) { mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f; mColorB = mColorA * ((color ) & 0xFF) / 255.0f; mColorSet = true; - mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA); + mSetShaderColor = mDescription.setAlpha8ColorModulate(mColorR, mColorG, mColorB, mColorA); } void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) { @@ -1820,7 +1624,7 @@ void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) { mColorG = g; mColorB = b; mColorSet = true; - mSetShaderColor = mDescription.setColor(r, g, b, a); + mSetShaderColor = mDescription.setColorModulate(a); } void OpenGLRenderer::setupDrawShader() { @@ -1829,9 +1633,17 @@ void OpenGLRenderer::setupDrawShader() { } } -void OpenGLRenderer::setupDrawColorFilter() { - if (mDrawModifiers.mColorFilter) { - mDrawModifiers.mColorFilter->describe(mDescription, mExtensions); +void OpenGLRenderer::setupDrawColorFilter(const SkColorFilter* filter) { + if (filter == NULL) { + return; + } + + SkXfermode::Mode mode; + if (filter->asColorMode(NULL, &mode)) { + mDescription.colorOp = ProgramDescription::kColorBlend; + mDescription.colorMode = mode; + } else if (filter->asColorMatrix(NULL)) { + mDescription.colorOp = ProgramDescription::kColorMatrix; } } @@ -1843,22 +1655,26 @@ void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) { } } -void OpenGLRenderer::setupDrawBlending(SkXfermode::Mode mode, bool swapSrcDst) { +void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) { + SkXfermode::Mode mode = layer->getMode(); // When the blending mode is kClear_Mode, we need to use a modulate color // argb=1,0,0,0 accountForClear(mode); - bool blend = (mColorSet && mColorA < 1.0f) || - (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()); + bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f || + (mColorSet && mColorA < 1.0f) || + (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) || + layer->getColorFilter(); chooseBlending(blend, mode, mDescription, swapSrcDst); } -void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) { +void OpenGLRenderer::setupDrawBlending(const SkPaint* paint, bool blend, bool swapSrcDst) { + SkXfermode::Mode mode = getXfermodeDirect(paint); // When the blending mode is kClear_Mode, we need to use a modulate color // argb=1,0,0,0 accountForClear(mode); blend |= (mColorSet && mColorA < 1.0f) || (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) || - (mDrawModifiers.mColorFilter && mDrawModifiers.mColorFilter->blend()); + (paint && paint->getColorFilter()); chooseBlending(blend, mode, mDescription, swapSrcDst); } @@ -1870,39 +1686,20 @@ void OpenGLRenderer::setupDrawDirtyRegionsDisabled() { mTrackDirtyRegions = false; } -void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float right, float bottom, - bool ignoreTransform) { - mModelView.loadTranslate(left, top, 0.0f); - if (!ignoreTransform) { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform()); - if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, currentTransform()); - } else { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity()); - if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); +void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset, + float left, float top, float right, float bottom, bool ignoreTransform) { + mModelViewMatrix.loadTranslate(left, top, 0.0f); + if (mode == kModelViewMode_TranslateAndScale) { + mModelViewMatrix.scale(right - left, bottom - top, 1.0f); } -} - -void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) { - mCaches.currentProgram->set(mOrthoMatrix, mat4::identity(), currentTransform(), offset); -} -void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom, - bool ignoreTransform, bool ignoreModelView) { - if (!ignoreModelView) { - mModelView.loadTranslate(left, top, 0.0f); - mModelView.scale(right - left, bottom - top, 1.0f); - } else { - mModelView.loadIdentity(); - } bool dirty = right - left > 0.0f && bottom - top > 0.0f; if (!ignoreTransform) { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform()); - if (mTrackDirtyRegions && dirty) { - dirtyLayer(left, top, right, bottom, currentTransform()); - } + mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, *currentTransform(), offset); + if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *currentTransform()); } else { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity()); - if (mTrackDirtyRegions && dirty) dirtyLayer(left, top, right, bottom); + mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, mat4::identity(), offset); + if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); } } @@ -1921,24 +1718,60 @@ void OpenGLRenderer::setupDrawPureColorUniforms() { void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) { if (mDrawModifiers.mShader) { if (ignoreTransform) { - mModelView.loadInverse(currentTransform()); + // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform() + // because it was built into modelView / the geometry, and the SkiaShader needs to + // compensate. + mat4 modelViewWithoutTransform; + modelViewWithoutTransform.loadInverse(*currentTransform()); + modelViewWithoutTransform.multiply(mModelViewMatrix); + mModelViewMatrix.load(modelViewWithoutTransform); } mDrawModifiers.mShader->setupProgram(mCaches.currentProgram, - mModelView, *mSnapshot, &mTextureUnit); + mModelViewMatrix, *mSnapshot, &mTextureUnit); } } -void OpenGLRenderer::setupDrawShaderIdentityUniforms() { - if (mDrawModifiers.mShader) { - mDrawModifiers.mShader->setupProgram(mCaches.currentProgram, - mat4::identity(), *mSnapshot, &mTextureUnit); +void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) { + if (NULL == filter) { + return; } -} -void OpenGLRenderer::setupDrawColorFilterUniforms() { - if (mDrawModifiers.mColorFilter) { - mDrawModifiers.mColorFilter->setupProgram(mCaches.currentProgram); + SkColor color; + SkXfermode::Mode mode; + if (filter->asColorMode(&color, &mode)) { + const int alpha = SkColorGetA(color); + const GLfloat a = alpha / 255.0f; + const GLfloat r = a * SkColorGetR(color) / 255.0f; + const GLfloat g = a * SkColorGetG(color) / 255.0f; + const GLfloat b = a * SkColorGetB(color) / 255.0f; + glUniform4f(mCaches.currentProgram->getUniform("colorBlend"), r, g, b, a); + return; } + + SkScalar srcColorMatrix[20]; + if (filter->asColorMatrix(srcColorMatrix)) { + + float colorMatrix[16]; + memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float)); + memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float)); + memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float)); + memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float)); + + // Skia uses the range [0..255] for the addition vector, but we need + // the [0..1] range to apply the vector in GLSL + float colorVector[4]; + colorVector[0] = srcColorMatrix[4] / 255.0f; + colorVector[1] = srcColorMatrix[9] / 255.0f; + colorVector[2] = srcColorMatrix[14] / 255.0f; + colorVector[3] = srcColorMatrix[19] / 255.0f; + + glUniformMatrix4fv(mCaches.currentProgram->getUniform("colorMatrix"), 1, + GL_FALSE, colorMatrix); + glUniform4fv(mCaches.currentProgram->getUniform("colorMatrixVector"), 1, colorVector); + return; + } + + // it is an error if we ever get here } void OpenGLRenderer::setupDrawTextGammaUniforms() { @@ -1972,7 +1805,8 @@ void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) { GL_FALSE, &transform.data[0]); } -void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { +void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices, + const GLvoid* texCoords, GLuint vbo) { bool force = false; if (!vertices || vbo) { force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); @@ -1988,7 +1822,8 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint v mCaches.unbindIndicesBuffer(); } -void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors) { +void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices, + const GLvoid* texCoords, const GLvoid* colors) { bool force = mCaches.unbindMeshBuffer(); GLsizei stride = sizeof(ColorTextureVertex); @@ -2005,7 +1840,8 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* mCaches.unbindIndicesBuffer(); } -void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { +void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices, + const GLvoid* texCoords, GLuint vbo) { bool force = false; // If vbo is != 0 we want to treat the vertices parameter as an offset inside // a VBO. However, if vertices is set to NULL and vbo == 0 then we want to @@ -2015,7 +1851,7 @@ void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, G } else { force = mCaches.unbindMeshBuffer(); } - mCaches.bindIndicesBuffer(); + mCaches.bindQuadIndicesBuffer(); mCaches.bindPositionVertexPointer(force, vertices); if (mCaches.currentProgram->texCoords >= 0) { @@ -2025,7 +1861,7 @@ void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, G void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { bool force = mCaches.unbindMeshBuffer(); - mCaches.bindIndicesBuffer(); + mCaches.bindQuadIndicesBuffer(); mCaches.bindPositionVertexPointer(force, vertices, gVertexStride); } @@ -2033,44 +1869,36 @@ void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { // Drawing /////////////////////////////////////////////////////////////////////////////// -status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, +status_t OpenGLRenderer::drawDisplayList(RenderNode* displayList, Rect& dirty, int32_t replayFlags) { status_t status; // All the usual checks and setup operations (quickReject, setupDraw, etc.) // will be performed by the display list itself if (displayList && displayList->isRenderable()) { + // compute 3d ordering + displayList->computeOrdering(); if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { status = startFrame(); ReplayStateStruct replayStruct(*this, dirty, replayFlags); - displayList->replay(replayStruct, 0); + displayList->replayNodeTree(replayStruct); return status | replayStruct.mDrawGlStatus; } bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs! - DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw); + DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); - displayList->defer(deferStruct, 0); + displayList->deferNodeTree(deferStruct); flushLayers(); status = startFrame(); - return status | deferredList.flush(*this, dirty); + return deferredList.flush(*this, dirty) | status; } return DrawGlInfo::kStatusDone; } -void OpenGLRenderer::outputDisplayList(DisplayList* displayList) { - if (displayList) { - displayList->output(1); - } -} - -void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint) { - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - +void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint) { int color = paint != NULL ? paint->getColor() : 0; float x = left; @@ -2079,20 +1907,20 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk texture->setWrap(GL_CLAMP_TO_EDGE, true); bool ignoreTransform = false; - if (currentTransform().isPureTranslate()) { - x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f); - y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f); + if (currentTransform()->isPureTranslate()) { + x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f); + y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f); ignoreTransform = true; texture->setFilter(GL_NEAREST, true); } else { - texture->setFilter(FILTER(paint), true); + texture->setFilter(getFilter(paint), true); } // No need to check for a UV mapper on the texture object, only ARGB_8888 // bitmaps get packed in the atlas drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id, - paint != NULL, color, alpha, mode, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, + paint, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform); } @@ -2101,44 +1929,41 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk * will not set the scissor enable or dirty the current layer, if any. * The caller is responsible for properly dirtying the current layer. */ -status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, - TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint) { +status_t OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, + int bitmapCount, TextureVertex* vertices, bool pureTranslate, + const Rect& bounds, const SkPaint* paint) { mCaches.activeTexture(0); Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - texture->setWrap(GL_CLAMP_TO_EDGE, true); - texture->setFilter(pureTranslate ? GL_NEAREST : FILTER(paint), true); + texture->setFilter(pureTranslate ? GL_NEAREST : getFilter(paint), true); const float x = (int) floorf(bounds.left + 0.5f); const float y = (int) floorf(bounds.top + 0.5f); - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { - int color = paint != NULL ? paint->getColor() : 0; + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(), - texture->id, paint != NULL, color, alpha, mode, - &vertices[0].position[0], &vertices[0].texture[0], - GL_TRIANGLES, bitmapCount * 6, true, true, false); + texture->id, paint, &vertices[0].x, &vertices[0].u, + GL_TRIANGLES, bitmapCount * 6, true, + kModelViewMode_Translate, false); } else { drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(), - texture->id, alpha / 255.0f, mode, texture->blend, - &vertices[0].position[0], &vertices[0].texture[0], - GL_TRIANGLES, bitmapCount * 6, false, true, 0, true, false); + texture->id, paint, texture->blend, &vertices[0].x, &vertices[0].u, + GL_TRIANGLES, bitmapCount * 6, false, true, 0, + kModelViewMode_Translate, false); } return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) { +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { const float right = left + bitmap->width(); const float bottom = top + bitmap->height(); - if (quickReject(left, top, right, bottom)) { + if (quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2147,7 +1972,7 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkP if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { drawAlphaBitmap(texture, left, top, paint); } else { drawTextureRect(left, top, right, bottom, texture, paint); @@ -2156,12 +1981,13 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkP return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) { Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); const mat4 transform(*matrix); transform.mapRect(r); - if (quickReject(r.left, r.top, r.right, r.bottom)) { + if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) { return DrawGlInfo::kStatusDone; } @@ -2174,7 +2000,7 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* // to the vertex shader. The save/restore is a bit overkill. save(SkCanvas::kMatrix_SaveFlag); concatMatrix(matrix); - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { drawAlphaBitmap(texture, 0.0f, 0.0f, paint); } else { drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint); @@ -2184,11 +2010,12 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint) { +status_t OpenGLRenderer::drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { const float right = left + bitmap->width(); const float bottom = top + bitmap->height(); - if (quickReject(left, top, right, bottom)) { + if (quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2196,7 +2023,7 @@ status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, Texture* texture = mCaches.textureCache.getTransient(bitmap); const AutoTexture autoCleanup(texture); - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { drawAlphaBitmap(texture, left, top, paint); } else { drawTextureRect(left, top, right, bottom, texture, paint); @@ -2205,9 +2032,9 @@ status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, - float* vertices, int* colors, SkPaint* paint) { - if (!vertices || mSnapshot->isIgnored()) { +status_t OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) { + if (!vertices || currentSnapshot()->isIgnored()) { return DrawGlInfo::kStatusDone; } @@ -2221,14 +2048,16 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes const uint32_t count = meshWidth * meshHeight * 6; - ColorTextureVertex mesh[count]; - ColorTextureVertex* vertex = mesh; + Vector<ColorTextureVertex> mesh; // TODO: use C++11 unique_ptr + mesh.setCapacity(count); + ColorTextureVertex* vertex = mesh.editArray(); bool cleanupColors = false; if (!colors) { uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1); - colors = new int[colorsCount]; - memset(colors, 0xff, colorsCount * sizeof(int)); + int* newColors = new int[colorsCount]; + memset(newColors, 0xff, colorsCount * sizeof(int)); + colors = newColors; cleanupColors = true; } @@ -2271,7 +2100,7 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes } } - if (quickReject(left, top, right, bottom)) { + if (quickRejectSetupScissor(left, top, right, bottom)) { if (cleanupColors) delete[] colors; return DrawGlInfo::kStatusDone; } @@ -2286,7 +2115,7 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes const AutoTexture autoCleanup(texture); texture->setWrap(GL_CLAMP_TO_EDGE, true); - texture->setFilter(FILTER(paint), true); + texture->setFilter(getFilter(paint), true); int alpha; SkXfermode::Mode mode; @@ -2295,21 +2124,21 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes float a = alpha / 255.0f; if (hasLayer()) { - dirtyLayer(left, top, right, bottom, currentTransform()); + dirtyLayer(left, top, right, bottom, *currentTransform()); } setupDraw(); setupDrawWithTextureAndColor(); setupDrawColor(a, a, a, a); - setupDrawColorFilter(); - setupDrawBlending(true, mode, false); + setupDrawColorFilter(getColorFilter(paint)); + setupDrawBlending(paint, true); setupDrawProgram(); setupDrawDirtyRegionsDisabled(); - setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, false); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, 0.0f, 0.0f, 1.0f, 1.0f); setupDrawTexture(texture->id); setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); - setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0], &mesh[0].color[0]); + setupDrawColorFilterUniforms(getColorFilter(paint)); + setupDrawMesh(&mesh[0].x, &mesh[0].u, &mesh[0].r); glDrawArrays(GL_TRIANGLES, 0, count); @@ -2323,11 +2152,11 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, - SkPaint* paint) { - if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) { + const SkPaint* paint) { + if (quickRejectSetupScissor(dstLeft, dstTop, dstRight, dstBottom)) { return DrawGlInfo::kStatusDone; } @@ -2349,10 +2178,6 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, mCaches.unbindMeshBuffer(); resetDrawTextureTexCoords(u1, v1, u2, v2); - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - texture->setWrap(GL_CLAMP_TO_EDGE, true); float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft); @@ -2365,9 +2190,9 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, bool useScaleTransform = mDrawModifiers.mShader && scaled; bool ignoreTransform = false; - if (CC_LIKELY(currentTransform().isPureTranslate() && !useScaleTransform)) { - float x = (int) floorf(dstLeft + currentTransform().getTranslateX() + 0.5f); - float y = (int) floorf(dstTop + currentTransform().getTranslateY() + 0.5f); + if (CC_LIKELY(currentTransform()->isPureTranslate() && !useScaleTransform)) { + float x = (int) floorf(dstLeft + currentTransform()->getTranslateX() + 0.5f); + float y = (int) floorf(dstTop + currentTransform()->getTranslateY() + 0.5f); dstRight = x + (dstRight - dstLeft); dstBottom = y + (dstBottom - dstTop); @@ -2375,10 +2200,10 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, dstLeft = x; dstTop = y; - texture->setFilter(scaled ? FILTER(paint) : GL_NEAREST, true); + texture->setFilter(scaled ? getFilter(paint) : GL_NEAREST, true); ignoreTransform = true; } else { - texture->setFilter(FILTER(paint), true); + texture->setFilter(getFilter(paint), true); } if (CC_UNLIKELY(useScaleTransform)) { @@ -2393,16 +2218,15 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, dstBottom = srcBottom - srcTop; } - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { - int color = paint ? paint->getColor() : 0; + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom, - texture->id, paint != NULL, color, alpha, mode, - &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + texture->id, paint, + &mMeshVertices[0].x, &mMeshVertices[0].u, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform); } else { drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, - texture->id, alpha / 255.0f, mode, texture->blend, - &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + texture->id, paint, texture->blend, + &mMeshVertices[0].x, &mMeshVertices[0].u, GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform); } @@ -2415,9 +2239,9 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, SkPaint* paint) { - if (quickReject(left, top, right, bottom)) { +status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) { + if (quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2428,9 +2252,10 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint); } -status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, - float left, float top, float right, float bottom, SkPaint* paint) { - if (quickReject(left, top, right, bottom)) { +status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh, + AssetAtlas::Entry* entry, float left, float top, float right, float bottom, + const SkPaint* paint) { + if (quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2443,15 +2268,11 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl texture->setWrap(GL_CLAMP_TO_EDGE, true); texture->setFilter(GL_LINEAR, true); - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - - const bool pureTranslate = currentTransform().isPureTranslate(); + const bool pureTranslate = currentTransform()->isPureTranslate(); // Mark the current layer dirty where we are going to draw the patch if (hasLayer() && mesh->hasEmptyQuads) { - const float offsetX = left + currentTransform().getTranslateX(); - const float offsetY = top + currentTransform().getTranslateY(); + const float offsetX = left + currentTransform()->getTranslateX(); + const float offsetY = top + currentTransform()->getTranslateY(); const size_t count = mesh->quads.size(); for (size_t i = 0; i < count; i++) { const Rect& bounds = mesh->quads.itemAt(i); @@ -2461,27 +2282,26 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight()); } else { dirtyLayer(left + bounds.left, top + bounds.top, - left + bounds.right, top + bounds.bottom, currentTransform()); + left + bounds.right, top + bounds.bottom, *currentTransform()); } } } + bool ignoreTransform = false; if (CC_LIKELY(pureTranslate)) { - const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f); - const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f); + const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f); + const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f); right = x + right - left; bottom = y + bottom - top; - drawIndexedTextureMesh(x, y, right, bottom, texture->id, alpha / 255.0f, - mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset, - GL_TRIANGLES, mesh->indexCount, false, true, - mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads); - } else { - drawIndexedTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, - mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset, - GL_TRIANGLES, mesh->indexCount, false, false, - mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads); + left = x; + top = y; + ignoreTransform = true; } + drawIndexedTextureMesh(left, top, right, bottom, texture->id, paint, + texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset, + GL_TRIANGLES, mesh->indexCount, false, ignoreTransform, + mCaches.patchCache.getMeshBuffer(), kModelViewMode_Translate, !mesh->hasEmptyQuads); } return DrawGlInfo::kStatusDrew; @@ -2492,8 +2312,8 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl * will not set the scissor enable or dirty the current layer, if any. * The caller is responsible for properly dirtying the current layer. */ -status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry, - TextureVertex* vertices, uint32_t indexCount, SkPaint* paint) { +status_t OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, + TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint) { mCaches.activeTexture(0); Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); if (!texture) return DrawGlInfo::kStatusDone; @@ -2502,58 +2322,62 @@ status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry, texture->setWrap(GL_CLAMP_TO_EDGE, true); texture->setFilter(GL_LINEAR, true); - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - - drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f, - mode, texture->blend, &vertices[0].position[0], &vertices[0].texture[0], - GL_TRIANGLES, indexCount, false, true, 0, true, false); + drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, paint, + texture->blend, &vertices[0].x, &vertices[0].u, + GL_TRIANGLES, indexCount, false, true, 0, kModelViewMode_Translate, false); return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint, - bool useOffset) { +status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode, + const VertexBuffer& vertexBuffer, const SkPaint* paint, bool useOffset) { + // not missing call to quickReject/dirtyLayer, always done at a higher level if (!vertexBuffer.getVertexCount()) { // no vertices to draw return DrawGlInfo::kStatusDone; } int color = paint->getColor(); - SkXfermode::Mode mode = getXfermode(paint->getXfermode()); bool isAA = paint->isAntiAlias(); setupDraw(); setupDrawNoTexture(); if (isAA) setupDrawAA(); setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha); - setupDrawColorFilter(); + setupDrawColorFilter(getColorFilter(paint)); setupDrawShader(); - setupDrawBlending(isAA, mode); + setupDrawBlending(paint, isAA); setupDrawProgram(); - setupDrawModelViewIdentity(useOffset); + setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0); setupDrawColorUniforms(); - setupDrawColorFilterUniforms(); - setupDrawShaderIdentityUniforms(); + setupDrawColorFilterUniforms(getColorFilter(paint)); + setupDrawShaderUniforms(); - void* vertices = vertexBuffer.getBuffer(); + const void* vertices = vertexBuffer.getBuffer(); bool force = mCaches.unbindMeshBuffer(); mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride); mCaches.resetTexCoordsVertexPointer(); - mCaches.unbindIndicesBuffer(); + int alphaSlot = -1; if (isAA) { void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset; alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha"); - // TODO: avoid enable/disable in back to back uses of the alpha attribute glEnableVertexAttribArray(alphaSlot); glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords); } - glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount()); + if (mode == kVertexBufferMode_Standard) { + mCaches.unbindIndicesBuffer(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount()); + } else if (mode == kVertexBufferMode_OnePolyRingShadow) { + mCaches.bindShadowIndicesBuffer(); + glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); + } else if (mode == kVertexBufferMode_TwoPolyRingShadow) { + mCaches.bindShadowIndicesBuffer(); + glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); + } if (isAA) { glDisableVertexAttribArray(alphaSlot); @@ -2571,18 +2395,18 @@ status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPa * * Doesn't yet support joins, caps, or path effects. */ -status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) { +status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) { VertexBuffer vertexBuffer; // TODO: try clipping large paths to viewport - PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer); + PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer); if (hasLayer()) { SkRect bounds = path.getBounds(); - PathTessellator::expandBoundsForStroke(bounds, paint, false); - dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform()); + PathTessellator::expandBoundsForStroke(bounds, paint); + dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); } - return drawVertexBuffer(vertexBuffer, paint); + return drawVertexBuffer(kVertexBufferMode_Standard, vertexBuffer, paint); } /** @@ -2596,58 +2420,64 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) { * TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce * memory transfer by removing need for degenerate vertices. */ -status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { - if (mSnapshot->isIgnored() || count < 4) return DrawGlInfo::kStatusDone; +status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) { + if (currentSnapshot()->isIgnored() || count < 4) return DrawGlInfo::kStatusDone; count &= ~0x3; // round down to nearest four VertexBuffer buffer; SkRect bounds; - PathTessellator::tessellateLines(points, count, paint, mSnapshot->transform, bounds, buffer); + PathTessellator::tessellateLines(points, count, paint, *currentTransform(), bounds, buffer); - if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) { + // can't pass paint, since style would be checked for outset. outset done by tessellation. + if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) { return DrawGlInfo::kStatusDone; } - dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform()); + dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); bool useOffset = !paint->isAntiAlias(); - return drawVertexBuffer(buffer, paint, useOffset); + return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset); } -status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) { - if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone; +status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { + if (currentSnapshot()->isIgnored() || count < 2) return DrawGlInfo::kStatusDone; count &= ~0x1; // round down to nearest two VertexBuffer buffer; SkRect bounds; - PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer); + PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), bounds, buffer); - if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) { + // can't pass paint, since style would be checked for outset. outset done by tessellation. + if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) { return DrawGlInfo::kStatusDone; } - dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform()); + dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); bool useOffset = !paint->isAntiAlias(); - return drawVertexBuffer(buffer, paint, useOffset); + return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset); } status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { // No need to check against the clip, we fill the clip region - if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; + if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; - Rect& clip(*mSnapshot->clipRect); + Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); - drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true); + SkPaint paint; + paint.setColor(color); + paint.setXfermodeMode(mode); + + drawColorRect(clip.left, clip.top, clip.right, clip.bottom, &paint, true); return DrawGlInfo::kStatusDrew; } status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture, - SkPaint* paint) { + const SkPaint* paint) { if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); @@ -2660,8 +2490,8 @@ status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* tex } status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) || + float rx, float ry, const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; } @@ -2685,8 +2515,8 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float return drawConvexPath(path, p); } -status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(x - radius, y - radius, +status_t OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(x - radius, y - radius, x + radius, y + radius, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; @@ -2707,8 +2537,8 @@ status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) } status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom, - SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) || + const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; } @@ -2729,8 +2559,8 @@ status_t OpenGLRenderer::drawOval(float left, float top, float right, float bott } status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) || + float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; } @@ -2766,8 +2596,9 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto // See SkPaintDefaults.h #define SkPaintDefaults_MiterLimit SkIntToScalar(4) -status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) || +status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, + const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; } @@ -2791,51 +2622,56 @@ status_t OpenGLRenderer::drawRect(float left, float top, float right, float bott return drawConvexPath(path, p); } - if (p->isAntiAlias() && !currentTransform().isSimple()) { + if (p->isAntiAlias() && !currentTransform()->isSimple()) { SkPath path; path.addRect(left, top, right, bottom); return drawConvexPath(path, p); } else { - drawColorRect(left, top, right, bottom, p->getColor(), getXfermode(p->getXfermode())); + drawColorRect(left, top, right, bottom, p); return DrawGlInfo::kStatusDrew; } } -void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count, - const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode, - float x, float y) { +void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, + int bytesCount, int count, const float* positions, + FontRenderer& fontRenderer, int alpha, float x, float y) { mCaches.activeTexture(0); + TextShadow textShadow; + if (!getTextShadow(paint, &textShadow)) { + LOG_ALWAYS_FATAL("failed to query shadow attributes"); + } + // NOTE: The drop shadow will not perform gamma correction // if shader-based correction is enabled mCaches.dropShadowCache.setFontRenderer(fontRenderer); const ShadowTexture* shadow = mCaches.dropShadowCache.get( - paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions); + paint, text, bytesCount, count, textShadow.radius, positions); // If the drop shadow exceeds the max texture size or couldn't be // allocated, skip drawing if (!shadow) return; const AutoTexture autoCleanup(shadow); - const float sx = x - shadow->left + mDrawModifiers.mShadowDx; - const float sy = y - shadow->top + mDrawModifiers.mShadowDy; + const float sx = x - shadow->left + textShadow.dx; + const float sy = y - shadow->top + textShadow.dy; - const int shadowAlpha = ((mDrawModifiers.mShadowColor >> 24) & 0xFF) * mSnapshot->alpha; - int shadowColor = mDrawModifiers.mShadowColor; + const int shadowAlpha = ((textShadow.color >> 24) & 0xFF) * mSnapshot->alpha; if (mDrawModifiers.mShader) { - shadowColor = 0xffffffff; + textShadow.color = SK_ColorWHITE; } setupDraw(); setupDrawWithTexture(true); - setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha); - setupDrawColorFilter(); + setupDrawAlpha8Color(textShadow.color, shadowAlpha < 255 ? shadowAlpha : alpha); + setupDrawColorFilter(getColorFilter(paint)); setupDrawShader(); - setupDrawBlending(true, mode); + setupDrawBlending(paint, true); setupDrawProgram(); - setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + sx, sy, sx + shadow->width, sy + shadow->height); setupDrawTexture(shadow->id); setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); + setupDrawColorFilterUniforms(getColorFilter(paint)); setupDrawShaderUniforms(); setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); @@ -2843,18 +2679,18 @@ void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesC } bool OpenGLRenderer::canSkipText(const SkPaint* paint) const { - float alpha = (mDrawModifiers.mHasShadow ? 1.0f : paint->getAlpha()) * mSnapshot->alpha; + float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * mSnapshot->alpha; return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode; } status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint) { - if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) { + const float* positions, const SkPaint* paint) { + if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) { return DrawGlInfo::kStatusDone; } // NOTE: Skia does not support perspective transform on drawPosText yet - if (!currentTransform().isSimple()) { + if (!currentTransform()->isSimple()) { return DrawGlInfo::kStatusDone; } @@ -2862,10 +2698,10 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count float x = 0.0f; float y = 0.0f; - const bool pureTranslate = currentTransform().isPureTranslate(); + const bool pureTranslate = currentTransform()->isPureTranslate(); if (pureTranslate) { - x = (int) floorf(x + currentTransform().getTranslateX() + 0.5f); - y = (int) floorf(y + currentTransform().getTranslateY() + 0.5f); + x = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f); + y = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f); } FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint); @@ -2875,13 +2711,13 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) { + if (CC_UNLIKELY(hasTextShadow(paint))) { drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, - alpha, mode, 0.0f, 0.0f); + alpha, 0.0f, 0.0f); } // Pick the appropriate texture filtering - bool linearFilter = currentTransform().changesBounds(); + bool linearFilter = currentTransform()->changesBounds(); if (pureTranslate && !linearFilter) { linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f; } @@ -2897,7 +2733,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count positions, hasActiveLayer ? &bounds : NULL, &functor)) { if (hasActiveLayer) { if (!pureTranslate) { - currentTransform().mapRect(bounds); + currentTransform()->mapRect(bounds); } dirtyLayerUnchecked(bounds, getRegion()); } @@ -2915,7 +2751,7 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const { fontTransform = mat4::identity(); } else { float sx, sy; - currentTransform().decomposeScale(sx, sy); + currentTransform()->decomposeScale(sx, sy); fontTransform.loadScale(sx, sy, 1.0f); } } @@ -2923,14 +2759,14 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const { } status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, - const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds, + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { if (drawOpMode == kDrawOpMode_Immediate) { // The checks for corner-case ignorable text and quick rejection is only done for immediate // drawing as ops from DeferredDisplayList are already filtered for these - if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) || - quickReject(bounds)) { + if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint) || + quickRejectSetupScissor(bounds)) { return DrawGlInfo::kStatusDone; } } @@ -2938,7 +2774,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f const float oldX = x; const float oldY = y; - const mat4& transform = currentTransform(); + const mat4& transform = *currentTransform(); const bool pureTranslate = transform.isPureTranslate(); if (CC_LIKELY(pureTranslate)) { @@ -2952,10 +2788,10 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint); - if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) { + if (CC_UNLIKELY(hasTextShadow(paint))) { fontRenderer.setFont(paint, mat4::identity()); drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, - alpha, mode, oldX, oldY); + alpha, oldX, oldY); } const bool hasActiveLayer = hasLayer(); @@ -2979,7 +2815,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f fontRenderer.setTextureFiltering(linearFilter); // TODO: Implement better clipping for scaled/rotated text - const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect; + const Rect* clip = !pureTranslate ? NULL : currentClipRect(); Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); bool status; @@ -3004,14 +2840,14 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f dirtyLayerUnchecked(layerBounds, getRegion()); } - drawTextDecorations(text, bytesCount, totalAdvance, oldX, oldY, paint); + drawTextDecorations(totalAdvance, oldX, oldY, paint); return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path, - float hOffset, float vOffset, SkPaint* paint) { - if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) { +status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, + const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) { + if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) { return DrawGlInfo::kStatusDone; } @@ -3035,7 +2871,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path, hOffset, vOffset, hasActiveLayer ? &bounds : NULL, &functor)) { if (hasActiveLayer) { - currentTransform().mapRect(bounds); + currentTransform()->mapRect(bounds); dirtyLayerUnchecked(bounds, getRegion()); } } @@ -3043,8 +2879,8 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { - if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; +status_t OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) { + if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; mCaches.activeTexture(0); @@ -3069,14 +2905,14 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { if (layer->isTextureLayer()) { transform = &layer->getTransform(); if (!transform->isIdentity()) { - save(0); - currentTransform().multiply(*transform); + save(SkCanvas::kMatrix_SaveFlag); + concatMatrix(*transform); } } bool clipRequired = false; - const bool rejected = quickRejectNoScissor(x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight(), false, &clipRequired); + const bool rejected = calculateQuickRejectForScissor(x, y, + x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, false); if (rejected) { if (transform && !transform->isIdentity()) { @@ -3091,33 +2927,31 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { mCaches.activeTexture(0); if (CC_LIKELY(!layer->region.isEmpty())) { - SkiaColorFilter* oldFilter = mDrawModifiers.mColorFilter; - mDrawModifiers.mColorFilter = layer->getColorFilter(); - if (layer->region.isRect()) { DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, composeLayerRect(layer, layer->regionRect)); } else if (layer->mesh) { + const float a = getLayerAlpha(layer); setupDraw(); setupDrawWithTexture(); setupDrawColor(a, a, a, a); - setupDrawColorFilter(); - setupDrawBlending(layer->isBlend() || a < 1.0f, layer->getMode(), false); + setupDrawColorFilter(layer->getColorFilter()); + setupDrawBlending(layer); setupDrawProgram(); setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); + setupDrawColorFilterUniforms(layer->getColorFilter()); setupDrawTexture(layer->getTexture()); - if (CC_LIKELY(currentTransform().isPureTranslate())) { - int tx = (int) floorf(x + currentTransform().getTranslateX() + 0.5f); - int ty = (int) floorf(y + currentTransform().getTranslateY() + 0.5f); + if (CC_LIKELY(currentTransform()->isPureTranslate())) { + int tx = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f); + int ty = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f); layer->setFilter(GL_NEAREST); - setupDrawModelViewTranslate(tx, ty, + setupDrawModelView(kModelViewMode_Translate, false, tx, ty, tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true); } else { layer->setFilter(GL_LINEAR); - setupDrawModelViewTranslate(x, y, + setupDrawModelView(kModelViewMode_Translate, false, x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight()); } @@ -3127,7 +2961,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { while (elementsCount > 0) { GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6); - setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]); + setupDrawMeshIndices(&mesh[0].x, &mesh[0].u); DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL)); @@ -3138,16 +2972,16 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { } #if DEBUG_LAYERS_AS_REGIONS - drawRegionRects(layer->region); + drawRegionRectsDebug(layer->region); #endif } - mDrawModifiers.mColorFilter = oldFilter; - if (layer->debugDrawUpdate) { layer->debugDrawUpdate = false; - drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), - 0x7f00ff00, SkXfermode::kSrcOver_Mode); + + SkPaint paint; + paint.setColor(0x7f00ff00); + drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), &paint); } } layer->hasDrawnSinceUpdate = true; @@ -3175,34 +3009,6 @@ void OpenGLRenderer::setupShader(SkiaShader* shader) { } /////////////////////////////////////////////////////////////////////////////// -// Color filters -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::resetColorFilter() { - mDrawModifiers.mColorFilter = NULL; -} - -void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) { - mDrawModifiers.mColorFilter = filter; -} - -/////////////////////////////////////////////////////////////////////////////// -// Drop shadow -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::resetShadow() { - mDrawModifiers.mHasShadow = false; -} - -void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) { - mDrawModifiers.mHasShadow = true; - mDrawModifiers.mShadowRadius = radius; - mDrawModifiers.mShadowDx = dx; - mDrawModifiers.mShadowDy = dy; - mDrawModifiers.mShadowColor = color; -} - -/////////////////////////////////////////////////////////////////////////////// // Draw filters /////////////////////////////////////////////////////////////////////////////// @@ -3220,7 +3026,7 @@ void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) { mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags; } -SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) { +const SkPaint* OpenGLRenderer::filterPaint(const SkPaint* paint) { if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) { return paint; } @@ -3238,7 +3044,7 @@ SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) { // Drawing implementation /////////////////////////////////////////////////////////////////////////////// -Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) { +Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) { Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap); if (!texture) { return mCaches.textureCache.get(bitmap); @@ -3247,8 +3053,8 @@ Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) { } void OpenGLRenderer::drawPathTexture(const PathTexture* texture, - float x, float y, SkPaint* paint) { - if (quickReject(x, y, x + texture->width, y + texture->height)) { + float x, float y, const SkPaint* paint) { + if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) { return; } @@ -3259,14 +3065,15 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture, setupDraw(); setupDrawWithTexture(true); setupDrawAlpha8Color(paint->getColor(), alpha); - setupDrawColorFilter(); + setupDrawColorFilter(getColorFilter(paint)); setupDrawShader(); - setupDrawBlending(true, mode); + setupDrawBlending(paint, true); setupDrawProgram(); - setupDrawModelView(x, y, x + texture->width, y + texture->height); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + x, y, x + texture->width, y + texture->height); setupDrawTexture(texture->id); setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); + setupDrawColorFilterUniforms(getColorFilter(paint)); setupDrawShaderUniforms(); setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); @@ -3278,8 +3085,8 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture, #define kStdUnderline_Offset (1.0f / 9.0f) #define kStdUnderline_Thickness (1.0f / 18.0f) -void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float underlineWidth, - float x, float y, SkPaint* paint) { +void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y, + const SkPaint* paint) { // Handle underline and strike-through uint32_t flags = paint->getFlags(); if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { @@ -3323,27 +3130,120 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float } } -status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint) { - if (mSnapshot->isIgnored()) { +status_t OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { + if (currentSnapshot()->isIgnored()) { return DrawGlInfo::kStatusDone; } - int color = paint->getColor(); - // If a shader is set, preserve only the alpha - if (mDrawModifiers.mShader) { - color |= 0x00ffffff; + return drawColorRects(rects, count, paint, false, true, true); +} + +static void mapPointFakeZ(Vector3& point, const mat4& transformXY, const mat4& transformZ) { + // map z coordinate with true 3d matrix + point.z = transformZ.mapZ(point); + + // map x,y coordinates with draw/Skia matrix + transformXY.mapPoint(point.x, point.y); +} + +status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ, + float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter) { + if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; + + // TODO: use quickRejectWithScissor. For now, always force enable scissor. + mCaches.enableScissor(); + + SkPaint paint; + paint.setAntiAlias(true); // want to use AlphaVertex + + // tessellate caster outline into a 2d polygon + Vector<Vertex> casterVertices2d; + const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value + PathTessellator::approximatePathOutlineVertices(*casterPerimeter, + casterRefinementThresholdSquared, casterVertices2d); + if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) { + ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(), + casterVertices2d.size()); } - SkXfermode::Mode mode = getXfermode(paint->getXfermode()); - return drawColorRects(rects, count, color, mode); + if (casterVertices2d.size() == 0) { + // empty caster polygon computed from path + return DrawGlInfo::kStatusDone; + } + + // map 2d caster poly into 3d + const int casterVertexCount = casterVertices2d.size(); + Vector3 casterPolygon[casterVertexCount]; + float minZ = FLT_MAX; + float maxZ = -FLT_MAX; + for (int i = 0; i < casterVertexCount; i++) { + const Vertex& point2d = casterVertices2d[i]; + casterPolygon[i] = Vector3(point2d.x, point2d.y, 0); + mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ); + minZ = fmin(minZ, casterPolygon[i].z); + maxZ = fmax(maxZ, casterPolygon[i].z); + } + + // map the centroid of the caster into 3d + Vector2 centroid = ShadowTessellator::centroid2d( + reinterpret_cast<const Vector2*>(casterVertices2d.array()), + casterVertexCount); + Vector3 centroid3d(centroid.x, centroid.y, 0); + mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ); + + // if the caster intersects the z=0 plane, lift it in Z so it doesn't + if (minZ < SHADOW_MIN_CASTER_Z) { + float casterLift = SHADOW_MIN_CASTER_Z - minZ; + for (int i = 0; i < casterVertexCount; i++) { + casterPolygon[i].z += casterLift; + } + centroid3d.z += casterLift; + } + + // Check whether we want to draw the shadow at all by checking the caster's + // bounds against clip. + // We only have ortho projection, so we can just ignore the Z in caster for + // simple rejection calculation. + Rect localClip = mSnapshot->getLocalClip(); + Rect casterBounds(casterPerimeter->getBounds()); + casterTransformXY.mapRect(casterBounds); + + bool isCasterOpaque = (casterAlpha == 1.0f) && casterUnclipped; + // draw caster's shadows + if (mCaches.propertyAmbientShadowStrength > 0) { + paint.setARGB(casterAlpha * mCaches.propertyAmbientShadowStrength, 0, 0, 0); + VertexBuffer ambientShadowVertexBuffer; + VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow( + isCasterOpaque, casterPolygon, casterVertexCount, centroid3d, + casterBounds, localClip, maxZ, ambientShadowVertexBuffer); + drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint); + } + + if (mCaches.propertySpotShadowStrength > 0) { + paint.setARGB(casterAlpha * mCaches.propertySpotShadowStrength, 0, 0, 0); + VertexBuffer spotShadowVertexBuffer; + VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow( + isCasterOpaque, casterPolygon, casterVertexCount, + *currentTransform(), getWidth(), getHeight(), casterBounds, localClip, + spotShadowVertexBuffer); + drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint); + } + + return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color, - SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) { +status_t OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint, + bool ignoreTransform, bool dirty, bool clip) { if (count == 0) { return DrawGlInfo::kStatusDone; } + int color = paint->getColor(); + // If a shader is set, preserve only the alpha + if (mDrawModifiers.mShader) { + color |= 0x00ffffff; + } + float left = FLT_MAX; float top = FLT_MAX; float right = FLT_MIN; @@ -3369,34 +3269,36 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color bottom = fmaxf(bottom, b); } - if (clip && quickReject(left, top, right, bottom)) { + if (clip && quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } setupDraw(); setupDrawNoTexture(); - setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha); + setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha); setupDrawShader(); - setupDrawColorFilter(); - setupDrawBlending(mode); + setupDrawColorFilter(getColorFilter(paint)); + setupDrawBlending(paint); setupDrawProgram(); setupDrawDirtyRegionsDisabled(); - setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, ignoreTransform, true); + setupDrawModelView(kModelViewMode_Translate, false, + 0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform); setupDrawColorUniforms(); setupDrawShaderUniforms(); - setupDrawColorFilterUniforms(); + setupDrawColorFilterUniforms(getColorFilter(paint)); if (dirty && hasLayer()) { - dirtyLayer(left, top, right, bottom, currentTransform()); + dirtyLayer(left, top, right, bottom, *currentTransform()); } - drawIndexedQuads(&mesh[0], count / 4); + issueIndexedQuadDraw(&mesh[0], count / 4); return DrawGlInfo::kStatusDrew; } void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, - int color, SkXfermode::Mode mode, bool ignoreTransform) { + const SkPaint* paint, bool ignoreTransform) { + int color = paint->getColor(); // If a shader is set, preserve only the alpha if (mDrawModifiers.mShader) { color |= 0x00ffffff; @@ -3404,34 +3306,31 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot setupDraw(); setupDrawNoTexture(); - setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha); + setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha); setupDrawShader(); - setupDrawColorFilter(); - setupDrawBlending(mode); + setupDrawColorFilter(getColorFilter(paint)); + setupDrawBlending(paint); setupDrawProgram(); - setupDrawModelView(left, top, right, bottom, ignoreTransform); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + left, top, right, bottom, ignoreTransform); setupDrawColorUniforms(); setupDrawShaderUniforms(ignoreTransform); - setupDrawColorFilterUniforms(); + setupDrawColorFilterUniforms(getColorFilter(paint)); setupDrawSimpleMesh(); glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); } void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, - Texture* texture, SkPaint* paint) { - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - + Texture* texture, const SkPaint* paint) { texture->setWrap(GL_CLAMP_TO_EDGE, true); GLvoid* vertices = (GLvoid*) NULL; GLvoid* texCoords = (GLvoid*) gMeshTextureOffset; if (texture->uvMapper) { - vertices = &mMeshVertices[0].position[0]; - texCoords = &mMeshVertices[0].texture[0]; + vertices = &mMeshVertices[0].x; + texCoords = &mMeshVertices[0].u; Rect uvs(0.0f, 0.0f, 1.0f, 1.0f); texture->uvMapper->map(uvs); @@ -3439,17 +3338,17 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom); } - if (CC_LIKELY(currentTransform().isPureTranslate())) { - const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f); - const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f); + if (CC_LIKELY(currentTransform()->isPureTranslate())) { + const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f); + const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f); texture->setFilter(GL_NEAREST, true); drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id, - alpha / 255.0f, mode, texture->blend, vertices, texCoords, + paint, texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount, false, true); } else { - texture->setFilter(FILTER(paint), true); - drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, + texture->setFilter(getFilter(paint), true); + drawTextureMesh(left, top, right, bottom, texture->id, paint, texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount); } @@ -3458,86 +3357,85 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b } } -void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, - GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) { - drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend, - (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount); -} - void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom, - GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, + GLuint texture, const SkPaint* paint, bool blend, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) { + bool swapSrcDst, bool ignoreTransform, GLuint vbo, + ModelViewMode modelViewMode, bool dirty) { + + int a; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &a, &mode); + const float alpha = a / 255.0f; setupDraw(); setupDrawWithTexture(); setupDrawColor(alpha, alpha, alpha, alpha); - setupDrawColorFilter(); - setupDrawBlending(blend, mode, swapSrcDst); + setupDrawColorFilter(getColorFilter(paint)); + setupDrawBlending(paint, blend, swapSrcDst); setupDrawProgram(); if (!dirty) setupDrawDirtyRegionsDisabled(); - if (!ignoreScale) { - setupDrawModelView(left, top, right, bottom, ignoreTransform); - } else { - setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform); - } + setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); setupDrawTexture(texture); setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); + setupDrawColorFilterUniforms(getColorFilter(paint)); setupDrawMesh(vertices, texCoords, vbo); glDrawArrays(drawMode, 0, elementsCount); } void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom, - GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, + GLuint texture, const SkPaint* paint, bool blend, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) { + bool swapSrcDst, bool ignoreTransform, GLuint vbo, + ModelViewMode modelViewMode, bool dirty) { + + int a; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &a, &mode); + const float alpha = a / 255.0f; setupDraw(); setupDrawWithTexture(); setupDrawColor(alpha, alpha, alpha, alpha); - setupDrawColorFilter(); - setupDrawBlending(blend, mode, swapSrcDst); + setupDrawColorFilter(getColorFilter(paint)); + setupDrawBlending(paint, blend, swapSrcDst); setupDrawProgram(); if (!dirty) setupDrawDirtyRegionsDisabled(); - if (!ignoreScale) { - setupDrawModelView(left, top, right, bottom, ignoreTransform); - } else { - setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform); - } + setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); setupDrawTexture(texture); setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); + setupDrawColorFilterUniforms(getColorFilter(paint)); setupDrawMeshIndices(vertices, texCoords, vbo); glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL); } void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom, - GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode, + GLuint texture, const SkPaint* paint, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool ignoreTransform, bool ignoreScale, bool dirty) { + bool ignoreTransform, ModelViewMode modelViewMode, bool dirty) { + + int color = paint != NULL ? paint->getColor() : 0; + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); setupDraw(); setupDrawWithTexture(true); - if (hasColor) { + if (paint != NULL) { setupDrawAlpha8Color(color, alpha); } - setupDrawColorFilter(); + setupDrawColorFilter(getColorFilter(paint)); setupDrawShader(); - setupDrawBlending(true, mode); + setupDrawBlending(paint, true); setupDrawProgram(); if (!dirty) setupDrawDirtyRegionsDisabled(); - if (!ignoreScale) { - setupDrawModelView(left, top, right, bottom, ignoreTransform); - } else { - setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform); - } + setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); setupDrawTexture(texture); setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(); - setupDrawShaderUniforms(); + setupDrawColorFilterUniforms(getColorFilter(paint)); + setupDrawShaderUniforms(ignoreTransform); setupDrawMesh(vertices, texCoords); glDrawArrays(drawMode, 0, elementsCount); @@ -3618,23 +3516,23 @@ void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, flo TextureVertex::setUV(v++, u2, v2); } -void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const { +void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const { getAlphaAndModeDirect(paint, alpha, mode); if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) { // if drawing a layer, ignore the paint's alpha *alpha = mDrawModifiers.mOverrideLayerAlpha * 255; } - *alpha *= mSnapshot->alpha; + *alpha *= currentSnapshot()->alpha; } -float OpenGLRenderer::getLayerAlpha(Layer* layer) const { +float OpenGLRenderer::getLayerAlpha(const Layer* layer) const { float alpha; if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) { alpha = mDrawModifiers.mOverrideLayerAlpha; } else { alpha = layer->getAlpha() / 255.0f; } - return alpha * mSnapshot->alpha; + return alpha * currentSnapshot()->alpha; } }; // namespace uirenderer diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 9afb7ad..4f7f01e 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -21,12 +21,15 @@ #include <GLES2/gl2ext.h> #include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkColorFilter.h> #include <SkMatrix.h> #include <SkPaint.h> #include <SkRegion.h> #include <SkShader.h> #include <SkXfermode.h> +#include <utils/Blur.h> #include <utils/Functor.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> @@ -41,16 +44,23 @@ #include "Matrix.h" #include "Program.h" #include "Rect.h" +#include "Renderer.h" +#include "StatefulBaseRenderer.h" #include "Snapshot.h" -#include "Vertex.h" -#include "SkiaShader.h" -#include "SkiaColorFilter.h" #include "UvMapper.h" +#include "Vertex.h" #include "Caches.h" +#include "CanvasProperty.h" namespace android { namespace uirenderer { +class DeferredDisplayState; +class RenderNode; +class TextSetupFunctor; +class VertexBuffer; +class SkiaShader; + struct DrawModifiers { DrawModifiers() { reset(); @@ -61,16 +71,8 @@ struct DrawModifiers { } SkiaShader* mShader; - SkiaColorFilter* mColorFilter; float mOverrideLayerAlpha; - // Drop shadow - bool mHasShadow; - float mShadowRadius; - float mShadowDx; - float mShadowDy; - int mShadowColor; - // Draw filters bool mHasDrawFilter; int mPaintFilterClearBits; @@ -82,12 +84,6 @@ enum StateDeferFlags { kStateDeferFlag_Clip = 0x2 }; -enum DrawOpMode { - kDrawOpMode_Immediate, - kDrawOpMode_Defer, - kDrawOpMode_Flush -}; - enum ClipSideFlags { kClipSide_None = 0x0, kClipSide_Left = 0x1, @@ -98,105 +94,47 @@ enum ClipSideFlags { kClipSide_ConservativeFull = 0x1F }; +/** + * Defines additional transformation that should be applied by the model view matrix, beyond that of + * the currentTransform() + */ +enum ModelViewMode { + /** + * Used when the model view should simply translate geometry passed to the shader. The resulting + * matrix will be a simple translation. + */ + kModelViewMode_Translate = 0, + + /** + * Used when the model view should translate and scale geometry. The resulting matrix will be a + * translation + scale. This is frequently used together with VBO 0, the (0,0,1,1) rect. + */ + kModelViewMode_TranslateAndScale = 1, +}; + +enum VertexBufferMode { + kVertexBufferMode_Standard = 0, + kVertexBufferMode_OnePolyRingShadow = 1, + kVertexBufferMode_TwoPolyRingShadow = 2 +}; + /////////////////////////////////////////////////////////////////////////////// // Renderer /////////////////////////////////////////////////////////////////////////////// - -class DeferredDisplayState; -class DisplayList; -class TextSetupFunctor; -class VertexBuffer; - /** - * OpenGL renderer used to draw accelerated 2D graphics. The API is a - * simplified version of Skia's Canvas API. + * OpenGL Renderer implementation. */ -class OpenGLRenderer { +class OpenGLRenderer : public StatefulBaseRenderer { public: ANDROID_API OpenGLRenderer(); virtual ~OpenGLRenderer(); - /** - * Sets the name of this renderer. The name is optional and - * empty by default. If the pointer is null the name is set - * to the empty string. - */ - ANDROID_API void setName(const char* name); - - /** - * Returns the name of this renderer as UTF8 string. - * The returned pointer is never null. - */ - ANDROID_API const char* getName() const; - - /** - * Read externally defined properties to control the behavior - * of the renderer. - */ ANDROID_API void initProperties(); - /** - * Indicates whether this renderer executes drawing commands immediately. - * If this method returns true, the drawing commands will be executed - * later. - */ - virtual bool isDeferred(); - - /** - * Sets the dimension of the underlying drawing surface. This method must - * be called at least once every time the drawing surface changes size. - * - * @param width The width in pixels of the underlysing surface - * @param height The height in pixels of the underlysing surface - */ virtual void setViewport(int width, int height); - - /** - * Prepares the renderer to draw a frame. This method must be invoked - * at the beginning of each frame. When this method is invoked, the - * entire drawing surface is assumed to be redrawn. - * - * @param opaque If true, the target surface is considered opaque - * and will not be cleared. If false, the target surface - * will be cleared - */ - ANDROID_API status_t prepare(bool opaque); - - /** - * Prepares the renderer to draw a frame. This method must be invoked - * at the beginning of each frame. Only the specified rectangle of the - * frame is assumed to be dirty. A clip will automatically be set to - * the specified rectangle. - * - * @param left The left coordinate of the dirty rectangle - * @param top The top coordinate of the dirty rectangle - * @param right The right coordinate of the dirty rectangle - * @param bottom The bottom coordinate of the dirty rectangle - * @param opaque If true, the target surface is considered opaque - * and will not be cleared. If false, the target surface - * will be cleared in the specified dirty rectangle - */ virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); - - /** - * Indicates the end of a frame. This method must be invoked whenever - * the caller is done rendering a frame. - */ virtual void finish(); - - /** - * This method must be invoked before handing control over to a draw functor. - * See callDrawGLFunction() for instance. - * - * This command must not be recorded inside display lists. - */ virtual void interrupt(); - - /** - * This method must be invoked after getting control back from a draw functor. - * - * This command must not be recorded inside display lists. - */ virtual void resume(); ANDROID_API void setCountOverdrawEnabled(bool enabled) { @@ -207,9 +145,6 @@ public: return mCountOverdraw ? mOverdraw : 0.0f; } - ANDROID_API status_t invokeFunctors(Rect& dirty); - ANDROID_API void detachFunctor(Functor* functor); - ANDROID_API void attachFunctor(Functor* functor); virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); ANDROID_API void pushLayerUpdate(Layer* layer); @@ -217,124 +152,81 @@ public: ANDROID_API void clearLayerUpdates(); ANDROID_API void flushLayerUpdates(); - ANDROID_API int getSaveCount() const; - virtual int save(int flags); - virtual void restore(); - virtual void restoreToCount(int saveCount); - - ANDROID_API int saveLayer(float left, float top, float right, float bottom, - SkPaint* paint, int flags) { - SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode; - if (paint) mode = getXfermode(paint->getXfermode()); - return saveLayer(left, top, right, bottom, paint ? paint->getAlpha() : 255, mode, flags); - } - ANDROID_API int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, int flags) { - return saveLayer(left, top, right, bottom, alpha, SkXfermode::kSrcOver_Mode, flags); - } - virtual int saveLayer(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags); - - int saveLayerDeferred(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags); - - virtual void translate(float dx, float dy); - virtual void rotate(float degrees); - virtual void scale(float sx, float sy); - virtual void skew(float sx, float sy); - - bool hasRectToRectTransform(); - ANDROID_API void getMatrix(SkMatrix* matrix); - virtual void setMatrix(SkMatrix* matrix); - virtual void concatMatrix(SkMatrix* matrix); - - ANDROID_API const Rect& getClipBounds(); - - /** - * Performs a quick reject but adjust the bounds to account for stroke width if necessary, - * and handling snapOut for AA geometry. - */ - bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint); - - /** - * Returns false and sets scissor based upon bounds if drawing won't be clipped out - */ - bool quickReject(float left, float top, float right, float bottom, bool snapOut = false); - bool quickReject(const Rect& bounds) { - return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom); + ANDROID_API virtual int saveLayer(float left, float top, float right, float bottom, + const SkPaint* paint, int flags) { + return saveLayer(left, top, right, bottom, paint, flags, NULL); } - /** - * Same as quickReject, without the scissor, instead returning clipRequired through pointer. - * clipRequired will be only set if not rejected - */ - ANDROID_API bool quickRejectNoScissor(float left, float top, float right, float bottom, - bool snapOut = false, bool* clipRequired = NULL); - bool quickRejectNoScissor(const Rect& bounds, bool* clipRequired = NULL) { - return quickRejectNoScissor(bounds.left, bounds.top, bounds.right, bounds.bottom, - clipRequired); - } + // Specialized saveLayer implementation, which will pass the convexMask to an FBO layer, if + // created, which will in turn clip to that mask when drawn back/restored. + int saveLayer(float left, float top, float right, float bottom, + const SkPaint* paint, int flags, const SkPath* convexMask); - virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - virtual bool clipPath(SkPath* path, SkRegion::Op op); - virtual bool clipRegion(SkRegion* region, SkRegion::Op op); - virtual Rect* getClipRect(); + int saveLayerDeferred(float left, float top, float right, float bottom, + const SkPaint* paint, int flags); - virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags); - virtual void outputDisplayList(DisplayList* displayList); + virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); virtual status_t drawLayer(Layer* layer, float x, float y); - virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); - status_t drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, - TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint); - virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint); - virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint); + status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, + TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint); + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint); + virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, SkPaint* paint); - virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint); - virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, - float* vertices, int* colors, SkPaint* paint); - status_t drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry, - TextureVertex* vertices, uint32_t indexCount, SkPaint* paint); - virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, SkPaint* paint); - status_t drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, - float left, float top, float right, float bottom, SkPaint* paint); + float dstRight, float dstBottom, const SkPaint* paint); + virtual status_t drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint); + virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint); + status_t drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, + TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint); + virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint); + status_t drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, + float left, float top, float right, float bottom, const SkPaint* paint); virtual status_t drawColor(int color, SkXfermode::Mode mode); - virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint); + virtual status_t drawRect(float left, float top, float right, float bottom, + const SkPaint* paint); virtual status_t drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* paint); - virtual status_t drawCircle(float x, float y, float radius, SkPaint* paint); - virtual status_t drawOval(float left, float top, float right, float bottom, SkPaint* paint); + float rx, float ry, const SkPaint* paint); + virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint); + virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, + CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { + // TODO: Remove once android_view_GLES20Canvas uses DisplayListRenderer + // directly + return drawCircle(x->value, y->value, radius->value, &paint->value); + } + virtual status_t drawOval(float left, float top, float right, float bottom, + const SkPaint* paint); virtual status_t drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint); - virtual status_t drawPath(SkPath* path, SkPaint* paint); - virtual status_t drawLines(float* points, int count, SkPaint* paint); - virtual status_t drawPoints(float* points, int count, SkPaint* paint); - virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path, - float hOffset, float vOffset, SkPaint* paint); + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint); + virtual status_t drawPath(const SkPath* path, const SkPaint* paint); + virtual status_t drawLines(const float* points, int count, const SkPaint* paint); + virtual status_t drawPoints(const float* points, int count, const SkPaint* paint); + virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, + float hOffset, float vOffset, const SkPaint* paint); virtual status_t drawPosText(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint); + const float* positions, const SkPaint* paint); virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, - const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds, + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode = kDrawOpMode_Immediate); - virtual status_t drawRects(const float* rects, int count, SkPaint* paint); + virtual status_t drawRects(const float* rects, int count, const SkPaint* paint); + + status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ, + float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter); virtual void resetShader(); virtual void setupShader(SkiaShader* shader); - virtual void resetColorFilter(); - virtual void setupColorFilter(SkiaColorFilter* filter); - - virtual void resetShadow(); - virtual void setupShadow(float radius, float dx, float dy, int color); - virtual void resetPaintFilter(); virtual void setupPaintFilter(int clearBits, int setBits); // If this value is set to < 1.0, it overrides alpha set on layer (see drawBitmap, drawLayer) void setOverrideLayerAlpha(float alpha) { mDrawModifiers.mOverrideLayerAlpha = alpha; } - SkPaint* filterPaint(SkPaint* paint); + const SkPaint* filterPaint(const SkPaint* paint); /** * Store the current display state (most importantly, the current clip and transform), and @@ -350,7 +242,7 @@ public: void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; } ANDROID_API bool isCurrentTransformSimple() { - return mSnapshot->transform->isSimple(); + return currentTransform()->isSimple(); } Caches& getCaches() { @@ -362,8 +254,8 @@ public: return mSnapshot->clipRegion->isEmpty(); } - int getViewportWidth() { return getSnapshot()->viewport.getWidth(); } - int getViewportHeight() { return getSnapshot()->viewport.getHeight(); } + int getViewportWidth() { return currentSnapshot()->viewport.getWidth(); } + int getViewportHeight() { return currentSnapshot()->viewport.getHeight(); } /** * Scales the alpha on the current snapshot. This alpha value will be modulated @@ -400,12 +292,12 @@ public: * @param alpha Where to store the resulting alpha * @param mode Where to store the resulting xfermode */ - static inline void getAlphaAndModeDirect(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) { + static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) { *mode = getXfermodeDirect(paint); *alpha = getAlphaDirect(paint); } - static inline SkXfermode::Mode getXfermodeDirect(SkPaint* paint) { + static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) { if (!paint) return SkXfermode::kSrcOver_Mode; return getXfermode(paint->getXfermode()); } @@ -415,6 +307,31 @@ public: return paint->getAlpha(); } + struct TextShadow { + SkScalar radius; + float dx; + float dy; + SkColor color; + }; + + static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) { + SkDrawLooper::BlurShadowRec blur; + if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) { + if (textShadow) { + textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma); + textShadow->dx = blur.fOffset.fX; + textShadow->dy = blur.fOffset.fY; + textShadow->color = blur.fColor; + } + return true; + } + return false; + } + + static inline bool hasTextShadow(const SkPaint* paint) { + return getTextShadow(paint, NULL); + } + /** * Return the best transform to use to rasterize text given a full * transform matrix. @@ -477,6 +394,13 @@ protected: */ void attachStencilBufferToLayer(Layer* layer); + bool quickRejectSetupScissor(float left, float top, float right, float bottom, + const SkPaint* paint = NULL); + bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = NULL) { + return quickRejectSetupScissor(bounds.left, bounds.top, + bounds.right, bounds.bottom, paint); + } + /** * Compose the layer defined in the current snapshot with the layer * defined by the previous snapshot. @@ -486,7 +410,7 @@ protected: * @param curent The current snapshot containing the layer to compose * @param previous The previous snapshot to compose the current layer with */ - virtual void composeLayer(sp<Snapshot> current, sp<Snapshot> previous); + virtual void composeLayer(const Snapshot& current, const Snapshot& previous); /** * Marks the specified region as dirty at the specified bounds. @@ -494,13 +418,6 @@ protected: void dirtyLayerUnchecked(Rect& bounds, Region* region); /** - * Returns the current snapshot. - */ - sp<Snapshot> getSnapshot() const { - return mSnapshot; - } - - /** * Returns the region of the current layer. */ virtual Region* getRegion() const { @@ -517,7 +434,7 @@ protected: /** * Returns the name of the FBO this renderer is rendering into. */ - virtual GLint getTargetFbo() const { + virtual GLuint getTargetFbo() const { return 0; } @@ -538,25 +455,21 @@ protected: * @param alpha Where to store the resulting alpha * @param mode Where to store the resulting xfermode */ - inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const; + inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const; /** * Gets the alpha from a layer, accounting for snapshot alpha and overrideLayerAlpha * * @param layer The layer from which the alpha is extracted */ - inline float getLayerAlpha(Layer* layer) const; + inline float getLayerAlpha(const Layer* layer) const; /** - * Safely retrieves the mode from the specified xfermode. If the specified - * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode. + * Safely retrieves the ColorFilter from the given Paint. If the paint is + * null then null is returned. */ - static inline SkXfermode::Mode getXfermode(SkXfermode* mode) { - SkXfermode::Mode resultMode; - if (!SkXfermode::AsMode(mode, &resultMode)) { - resultMode = SkXfermode::kSrcOver_Mode; - } - return resultMode; + static inline SkColorFilter* getColorFilter(const SkPaint* paint) { + return paint ? paint->getColorFilter() : NULL; } /** @@ -582,12 +495,11 @@ private: /** * Tells the GPU what part of the screen is about to be redrawn. - * This method will use the clip rect that we started drawing the - * frame with. + * This method will use the current layer space clip rect. * This method needs to be invoked every time getTargetFbo() is * bound again. */ - void startTiling(const sp<Snapshot>& snapshot, bool opaque = false); + void startTilingCurrentClip(bool opaque = false); /** * Tells the GPU what part of the screen is about to be redrawn. @@ -602,23 +514,7 @@ private: */ void endTiling(); - /** - * Saves the current state of the renderer as a new snapshot. - * The new snapshot is saved in mSnapshot and the previous snapshot - * is linked from mSnapshot->previous. - * - * @param flags The save flags; see SkCanvas for more information - * - * @return The new save count. This value can be passed to #restoreToCount() - */ - int saveSnapshot(int flags); - - /** - * Restores the current snapshot; mSnapshot becomes mSnapshot->previous. - * - * @return True if the clip was modified. - */ - bool restoreSnapshot(); + void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored); /** * Sets the clipping rectangle using glScissor. The clip is defined by @@ -654,12 +550,12 @@ private: * @param alpha The translucency of the layer * @param mode The blending mode of the layer * @param flags The layer save flags - * @param previousFbo The name of the current framebuffer + * @param mask A mask to use when drawing the layer back, may be empty * * @return True if the layer was successfully created, false otherwise */ bool createLayer(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo); + const SkPaint* paint, int flags, const SkPath* convexMask); /** * Creates a new layer stored in the specified snapshot as an FBO. @@ -667,9 +563,8 @@ private: * @param layer The layer to store as an FBO * @param snapshot The snapshot associated with the new layer * @param bounds The bounds of the layer - * @param previousFbo The name of the current framebuffer */ - bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo); + bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip); /** * Compose the specified layer as a region. @@ -716,12 +611,11 @@ private: * @param top The top coordinate of the rectangle * @param right The right coordinate of the rectangle * @param bottom The bottom coordinate of the rectangle - * @param color The rectangle's ARGB color, defined as a packed 32 bits word - * @param mode The Skia xfermode to use + * @param paint The paint containing the color, blending mode, etc. * @param ignoreTransform True if the current transform should be ignored */ void drawColorRect(float left, float top, float right, float bottom, - int color, SkXfermode::Mode mode, bool ignoreTransform = false); + const SkPaint* paint, bool ignoreTransform = false); /** * Draws a series of colored rectangles with the specified color. The specified @@ -730,15 +624,13 @@ private: * * @param rects A list of rectangles, 4 floats (left, top, right, bottom) * per rectangle - * @param color The rectangles' ARGB color, defined as a packed 32 bits word - * @param mode The Skia xfermode to use + * @param paint The paint containing the color, blending mode, etc. * @param ignoreTransform True if the current transform should be ignored * @param dirty True if calling this method should dirty the current layer * @param clip True if the rects should be clipped, false otherwise */ - status_t drawColorRects(const float* rects, int count, int color, - SkXfermode::Mode mode, bool ignoreTransform = false, - bool dirty = true, bool clip = true); + status_t drawColorRects(const float* rects, int count, const SkPaint* paint, + bool ignoreTransform = false, bool dirty = true, bool clip = true); /** * Draws the shape represented by the specified path texture. @@ -751,7 +643,7 @@ private: * @param texture The texture reprsenting the shape * @param paint The paint to draw the shape with */ - status_t drawShape(float left, float top, const PathTexture* texture, SkPaint* paint); + status_t drawShape(float left, float top, const PathTexture* texture, const SkPaint* paint); /** * Draws the specified texture as an alpha bitmap. Alpha bitmaps obey @@ -762,7 +654,7 @@ private: * @param top The y coordinate of the bitmap * @param paint The paint to render with */ - void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint); + void drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint); /** * Renders a strip of polygons with the specified paint, used for tessellated geometry. @@ -771,8 +663,8 @@ private: * @param paint The paint to render with * @param useOffset Offset the vertexBuffer (used in drawing non-AA lines) */ - status_t drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint, - bool useOffset = false); + status_t drawVertexBuffer(VertexBufferMode mode, const VertexBuffer& vertexBuffer, + const SkPaint* paint, bool useOffset = false); /** * Renders the convex hull defined by the specified path as a strip of polygons. @@ -780,23 +672,7 @@ private: * @param path The hull of the path to draw * @param paint The paint to render with */ - status_t drawConvexPath(const SkPath& path, SkPaint* paint); - - /** - * Draws a textured rectangle with the specified texture. The specified coordinates - * are transformed by the current snapshot's transform matrix. - * - * @param left The left coordinate of the rectangle - * @param top The top coordinate of the rectangle - * @param right The right coordinate of the rectangle - * @param bottom The bottom coordinate of the rectangle - * @param texture The texture name to map onto the rectangle - * @param alpha An additional translucency parameter, between 0.0f and 1.0f - * @param mode The blending mode - * @param blend True if the texture contains an alpha channel - */ - void drawTextureRect(float left, float top, float right, float bottom, GLuint texture, - float alpha, SkXfermode::Mode mode, bool blend); + status_t drawConvexPath(const SkPath& path, const SkPaint* paint); /** * Draws a textured rectangle with the specified texture. The specified coordinates @@ -810,7 +686,7 @@ private: * @param paint The paint containing the alpha, blending mode, etc. */ void drawTextureRect(float left, float top, float right, float bottom, - Texture* texture, SkPaint* paint); + Texture* texture, const SkPaint* paint); /** * Draws a textured mesh with the specified texture. If the indices are omitted, @@ -822,8 +698,7 @@ private: * @param right The right coordinate of the rectangle * @param bottom The bottom coordinate of the rectangle * @param texture The texture name to map onto the rectangle - * @param alpha An additional translucency parameter, between 0.0f and 1.0f - * @param mode The blending mode + * @param paint The paint containing the alpha, blending mode, colorFilter, etc. * @param blend True if the texture contains an alpha channel * @param vertices The vertices that define the mesh * @param texCoords The texture coordinates of each vertex @@ -831,32 +706,33 @@ private: * @param swapSrcDst Whether or not the src and dst blending operations should be swapped * @param ignoreTransform True if the current transform should be ignored * @param vbo The VBO used to draw the mesh - * @param ignoreScale True if the model view matrix should not be scaled + * @param modelViewMode Defines whether the model view matrix should be scaled * @param dirty True if calling this method should dirty the current layer */ void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, - float alpha, SkXfermode::Mode mode, bool blend, + const SkPaint* paint, bool blend, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0, - bool ignoreScale = false, bool dirty = true); + ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, bool dirty = true); void drawIndexedTextureMesh(float left, float top, float right, float bottom, GLuint texture, - float alpha, SkXfermode::Mode mode, bool blend, + const SkPaint* paint, bool blend, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0, - bool ignoreScale = false, bool dirty = true); + ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, bool dirty = true); void drawAlpha8TextureMesh(float left, float top, float right, float bottom, - GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode, + GLuint texture, const SkPaint* paint, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool ignoreTransform, bool ignoreScale = false, bool dirty = true); + bool ignoreTransform, ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, + bool dirty = true); /** * Draws the specified list of vertices as quads using indexed GL_TRIANGLES. * If the number of vertices to draw exceeds the number of indices we have * pre-allocated, this method will generate several glDrawElements() calls. */ - void drawIndexedQuads(Vertex* mesh, GLsizei quadsCount); + void issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount); /** * Draws text underline and strike-through if needed. @@ -868,8 +744,7 @@ private: * @param y The y coordinate where the text will be drawn * @param paint The paint to draw the text with */ - void drawTextDecorations(const char* text, int bytesCount, float totalAdvance, - float x, float y, SkPaint* paint); + void drawTextDecorations(float totalAdvance, float x, float y, const SkPaint* paint); /** * Draws shadow layer on text (with optional positions). @@ -881,12 +756,11 @@ private: * @param positions The x, y positions of individual glyphs (or NULL) * @param fontRenderer The font renderer object * @param alpha The alpha value for drawing the shadow - * @param mode The xfermode for drawing the shadow * @param x The x coordinate where the shadow will be drawn * @param y The y coordinate where the shadow will be drawn */ - void drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count, - const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode, + void drawTextShadow(const SkPaint* paint, const char* text, int bytesCount, int count, + const float* positions, FontRenderer& fontRenderer, int alpha, float x, float y); /** @@ -898,7 +772,7 @@ private: * @param y The y coordinate where the texture will be drawn * @param paint The paint to draw the texture with */ - void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint); + void drawPathTexture(const PathTexture* texture, float x, float y, const SkPaint* paint); /** * Resets the texture coordinates stored in mMeshVertices. Setting the values @@ -971,32 +845,42 @@ private: void setupDrawAlpha8Color(int color, int alpha); void setupDrawTextGamma(const SkPaint* paint); void setupDrawShader(); - void setupDrawColorFilter(); - void setupDrawBlending(SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode, - bool swapSrcDst = false); - void setupDrawBlending(bool blend = true, SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode, - bool swapSrcDst = false); + void setupDrawColorFilter(const SkColorFilter* filter); + void setupDrawBlending(const Layer* layer, bool swapSrcDst = false); + void setupDrawBlending(const SkPaint* paint, bool blend = true, bool swapSrcDst = false); void setupDrawProgram(); void setupDrawDirtyRegionsDisabled(); - void setupDrawModelViewIdentity(bool offset = false); - void setupDrawModelView(float left, float top, float right, float bottom, - bool ignoreTransform = false, bool ignoreModelView = false); - void setupDrawModelViewTranslate(float left, float top, float right, float bottom, - bool ignoreTransform = false); + + /** + * Setup the current program matrices based upon the nature of the geometry. + * + * @param mode If kModelViewMode_Translate, the geometry must be translated by the left and top + * parameters. If kModelViewMode_TranslateAndScale, the geometry that exists in the (0,0, 1,1) + * space must be scaled up and translated to fill the quad provided in (l,t,r,b). These + * transformations are stored in the modelView matrix and uploaded to the shader. + * + * @param offset Set to true if the the matrix should be fudged (translated) slightly to disambiguate + * geometry pixel positioning. See Vertex::GeometryFudgeFactor(). + * + * @param ignoreTransform Set to true if l,t,r,b coordinates already in layer space, + * currentTransform() will be ignored. (e.g. when drawing clip in layer coordinates to stencil, + * or when simple translation has been extracted) + */ + void setupDrawModelView(ModelViewMode mode, bool offset, + float left, float top, float right, float bottom, bool ignoreTransform = false); void setupDrawColorUniforms(); void setupDrawPureColorUniforms(); - void setupDrawShaderIdentityUniforms(); void setupDrawShaderUniforms(bool ignoreTransform = false); - void setupDrawColorFilterUniforms(); + void setupDrawColorFilterUniforms(const SkColorFilter* paint); void setupDrawSimpleMesh(); void setupDrawTexture(GLuint texture); void setupDrawExternalTexture(GLuint texture); void setupDrawTextureTransform(); void setupDrawTextureTransformUniforms(mat4& transform); void setupDrawTextGammaUniforms(); - void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0); - void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors); - void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0); + void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords = NULL, GLuint vbo = 0); + void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords, const GLvoid* colors); + void setupDrawMeshIndices(const GLvoid* vertices, const GLvoid* texCoords, GLuint vbo = 0); void setupDrawIndexedVertices(GLvoid* vertices); void accountForClear(SkXfermode::Mode mode); @@ -1004,18 +888,19 @@ private: void updateLayers(); void flushLayers(); +#if DEBUG_LAYERS_AS_REGIONS /** * Renders the specified region as a series of rectangles. This method * is used for debugging only. */ - void drawRegionRects(const Region& region); + void drawRegionRectsDebug(const Region& region); +#endif /** * Renders the specified region as a series of rectangles. The region * must be in screen-space coordinates. */ - void drawRegionRects(const SkRegion& region, int color, SkXfermode::Mode mode, - bool dirty = false); + void drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty = false); /** * Draws the current clip region if any. Only when DEBUG_CLIP_REGIONS @@ -1034,10 +919,6 @@ private: mDirtyClip = true; } - inline mat4& currentTransform() const { - return *mSnapshot->transform; - } - inline const UvMapper& getMapper(const Texture* texture) { return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper; } @@ -1047,23 +928,27 @@ private: * come from the texture cache or an atlas. If this method returns * NULL, the texture could not be found and/or allocated. */ - Texture* getTexture(SkBitmap* bitmap); + Texture* getTexture(const SkBitmap* bitmap); - // Dimensions of the drawing surface - int mWidth, mHeight; + // Ortho matrix used for projection in shaders + mat4 mProjectionMatrix; - // Matrix used for ortho projection in shaders - mat4 mOrthoMatrix; - - // Model-view matrix used to position/size objects - mat4 mModelView; + /** + * Model-view matrix used to position/size objects + * + * Stores operation-local modifications to the draw matrix that aren't incorporated into the + * currentTransform(). + * + * If generated with kModelViewMode_Translate, mModelViewMatrix will reflect an x/y offset, + * e.g. the offset in drawLayer(). If generated with kModelViewMode_TranslateAndScale, + * mModelViewMatrix will reflect a translation and scale, e.g. the translation and scale + * required to make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height + * of a bitmap. + * + * Used as input to SkiaShader transformation. + */ + mat4 mModelViewMatrix; - // Number of saved states - int mSaveCount; - // Base state - sp<Snapshot> mFirstSnapshot; - // Current state - sp<Snapshot> mSnapshot; // State used to define the clipping region Rect mTilingClip; // Is the target render surface opaque @@ -1087,14 +972,9 @@ private: // List of rectangles to clear after saveLayer() is invoked Vector<Rect*> mLayers; - // List of functors to invoke after a frame is drawn - SortedVector<Functor*> mFunctors; // List of layers to update at the beginning of a frame Vector<Layer*> mLayerUpdates; - // Indicates whether the clip must be restored - bool mDirtyClip; - // The following fields are used to setup drawing // Used to describe the shaders to generate ProgramDescription mDescription; @@ -1122,9 +1002,6 @@ private: bool mCountOverdraw; float mOverdraw; - // Optional name of the renderer - String8 mName; - friend class DisplayListRenderer; friend class Layer; friend class TextSetupFunctor; diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h new file mode 100644 index 0000000..530be30 --- /dev/null +++ b/libs/hwui/Outline.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 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 OUTLINE_H +#define OUTLINE_H + +#include <SkPath.h> + +#include "Rect.h" + +namespace android { +namespace uirenderer { + +class Outline { +public: + Outline() + : mShouldClip(false) + , mType(kOutlineType_None) + , mRadius(0) {} + + void setRoundRect(int left, int top, int right, int bottom, int radius) { + mType = kOutlineType_RoundRect; + mBounds.set(left, top, right, bottom); + mRadius = radius; + mPath.reset(); + mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), + radius, radius); + } + + void setConvexPath(const SkPath* outline) { + if (!outline) { + setEmpty(); + return; + } + mType = kOutlineType_ConvexPath; + mPath = *outline; + mBounds.set(outline->getBounds()); + } + + void setEmpty() { + mType = kOutlineType_None; + mPath.reset(); + } + + void setShouldClip(bool clip) { + mShouldClip = clip; + } + + bool willClip() const { + // only round rect outlines can be used for clipping + return mShouldClip && (mType == kOutlineType_RoundRect); + } + + const SkPath* getPath() const { + if (mType == kOutlineType_None) return NULL; + + return &mPath; + } + +private: + enum OutlineType { + kOutlineType_None = 0, + kOutlineType_ConvexPath = 1, + kOutlineType_RoundRect = 2 + }; + + bool mShouldClip; + OutlineType mType; + Rect mBounds; + float mRadius; + SkPath mPath; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* OUTLINE_H */ diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index b5e8838..1ba045d 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -36,7 +36,8 @@ namespace uirenderer { // 9-patch structures /////////////////////////////////////////////////////////////////////////////// -struct Patch { +class Patch { +public: Patch(); ~Patch(); diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index cf8adf8..5a49f38 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -51,7 +51,7 @@ PathDescription::PathDescription(): memset(&shape, 0, sizeof(Shape)); } -PathDescription::PathDescription(ShapeType type, SkPaint* paint): +PathDescription::PathDescription(ShapeType type, const SkPaint* paint): type(type), join(paint->getStrokeJoin()), cap(paint->getStrokeCap()), @@ -82,7 +82,7 @@ int PathDescription::compare(const PathDescription& rhs) const { // Utilities /////////////////////////////////////////////////////////////////////////////// -bool PathCache::canDrawAsConvexPath(SkPath* path, SkPaint* paint) { +bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) { // NOTE: This should only be used after PathTessellator handles joins properly return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity; } @@ -415,7 +415,7 @@ void PathCache::clearGarbage() { * in the cache. The source path is also used to reclaim garbage when a * Dalvik Path object is collected. */ -static SkPath* getSourcePath(SkPath* path) { +static const SkPath* getSourcePath(const SkPath* path) { const SkPath* sourcePath = path->getSourcePath(); if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) { return const_cast<SkPath*>(sourcePath); @@ -423,7 +423,7 @@ static SkPath* getSourcePath(SkPath* path) { return path; } -PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { +PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) { path = getSourcePath(path); PathDescription entry(kShapePath, paint); @@ -461,7 +461,7 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { return texture; } -void PathCache::precache(SkPath* path, SkPaint* paint) { +void PathCache::precache(const SkPath* path, const SkPaint* paint) { if (!Caches::getInstance().tasks.canRunTasks()) { return; } @@ -509,7 +509,7 @@ void PathCache::precache(SkPath* path, SkPaint* paint) { /////////////////////////////////////////////////////////////////////////////// PathTexture* PathCache::getRoundRect(float width, float height, - float rx, float ry, SkPaint* paint) { + float rx, float ry, const SkPaint* paint) { PathDescription entry(kShapeRoundRect, paint); entry.shape.roundRect.mWidth = width; entry.shape.roundRect.mHeight = height; @@ -534,7 +534,7 @@ PathTexture* PathCache::getRoundRect(float width, float height, // Circles /////////////////////////////////////////////////////////////////////////////// -PathTexture* PathCache::getCircle(float radius, SkPaint* paint) { +PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) { PathDescription entry(kShapeCircle, paint); entry.shape.circle.mRadius = radius; @@ -554,7 +554,7 @@ PathTexture* PathCache::getCircle(float radius, SkPaint* paint) { // Ovals /////////////////////////////////////////////////////////////////////////////// -PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) { +PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) { PathDescription entry(kShapeOval, paint); entry.shape.oval.mWidth = width; entry.shape.oval.mHeight = height; @@ -577,7 +577,7 @@ PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) { // Rects /////////////////////////////////////////////////////////////////////////////// -PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) { +PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) { PathDescription entry(kShapeRect, paint); entry.shape.rect.mWidth = width; entry.shape.rect.mHeight = height; @@ -601,7 +601,7 @@ PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) { /////////////////////////////////////////////////////////////////////////////// PathTexture* PathCache::getArc(float width, float height, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { PathDescription entry(kShapeArc, paint); entry.shape.arc.mWidth = width; entry.shape.arc.mHeight = height; diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 16d20a8..847853a 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -32,7 +32,7 @@ class SkBitmap; class SkCanvas; class SkPaint; class SkPath; -class SkRect; +struct SkRect; namespace android { namespace uirenderer { @@ -116,7 +116,7 @@ struct PathDescription { SkPathEffect* pathEffect; union Shape { struct Path { - SkPath* mPath; + const SkPath* mPath; } path; struct RoundRect { float mWidth; @@ -145,7 +145,7 @@ struct PathDescription { } shape; PathDescription(); - PathDescription(ShapeType shapeType, SkPaint* paint); + PathDescription(ShapeType shapeType, const SkPaint* paint); hash_t hash() const; @@ -207,13 +207,13 @@ public: */ uint32_t getSize(); - PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint); - PathTexture* getCircle(float radius, SkPaint* paint); - PathTexture* getOval(float width, float height, SkPaint* paint); - PathTexture* getRect(float width, float height, SkPaint* paint); + PathTexture* getRoundRect(float width, float height, float rx, float ry, const SkPaint* paint); + PathTexture* getCircle(float radius, const SkPaint* paint); + PathTexture* getOval(float width, float height, const SkPaint* paint); + PathTexture* getRect(float width, float height, const SkPaint* paint); PathTexture* getArc(float width, float height, float startAngle, float sweepAngle, - bool useCenter, SkPaint* paint); - PathTexture* get(SkPath* path, SkPaint* paint); + bool useCenter, const SkPaint* paint); + PathTexture* get(const SkPath* path, const SkPaint* paint); /** * Removes the specified path. This is meant to be called from threads @@ -239,9 +239,9 @@ public: /** * Precaches the specified path using background threads. */ - void precache(SkPath* path, SkPaint* paint); + void precache(const SkPath* path, const SkPaint* paint); - static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint); + static bool canDrawAsConvexPath(SkPath* path, const SkPaint* paint); static void computePathBounds(const SkPath* path, const SkPaint* paint, float& left, float& top, float& offset, uint32_t& width, uint32_t& height); static void computeBounds(const SkRect& bounds, const SkPaint* paint, @@ -292,7 +292,7 @@ private: class PathTask: public Task<SkBitmap*> { public: - PathTask(SkPath* path, SkPaint* paint, PathTexture* texture): + PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture): path(path), paint(paint), texture(texture) { } @@ -300,8 +300,8 @@ private: delete future()->get(); } - SkPath* path; - SkPaint* paint; + const SkPath* path; + const SkPaint* paint; PathTexture* texture; }; diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index 3970913..4ef2158 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#define LOG_TAG "PathTessellator" +#define LOG_TAG "OpenGLRenderer" #define LOG_NDEBUG 1 -#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#define ATRACE_TAG ATRACE_TAG_VIEW #define VERTEX_DEBUG 0 @@ -24,11 +24,11 @@ #define DEBUG_DUMP_ALPHA_BUFFER() \ for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ ALOGD("point %d at %f %f, alpha %f", \ - i, buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); \ + i, buffer[i].x, buffer[i].y, buffer[i].alpha); \ } #define DEBUG_DUMP_BUFFER() \ for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ - ALOGD("point %d at %f %f", i, buffer[i].position[0], buffer[i].position[1]); \ + ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \ } #else #define DEBUG_DUMP_ALPHA_BUFFER() @@ -53,27 +53,22 @@ namespace android { namespace uirenderer { -#define THRESHOLD 0.5f +#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f) #define ROUND_CAP_THRESH 0.25f #define PI 3.1415926535897932f -void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint, - bool forceExpand) { - if (forceExpand || paint->getStyle() != SkPaint::kFill_Style) { +/** + * Note: this function doesn't account for the AA case with sub-pixel line thickness (not just 0 < + * width < 1.0, canvas scale factors in as well) so this can't be used for points/lines + */ +void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint) { + if (paint->getStyle() != SkPaint::kFill_Style) { float outset = paint->getStrokeWidth() * 0.5f; if (outset == 0) outset = 0.5f; // account for hairline bounds.outset(outset, outset); } } -inline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) { - Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]); -} - -inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) { - AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha); -} - /** * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices @@ -93,16 +88,16 @@ inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& norma */ struct PaintInfo { public: - PaintInfo(const SkPaint* paint, const mat4 *transform) : + PaintInfo(const SkPaint* paint, const mat4& transform) : style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()), inverseScaleX(1.0f), inverseScaleY(1.0f), halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) { // compute inverse scales - if (CC_UNLIKELY(!transform->isPureTranslate())) { - float m00 = transform->data[Matrix4::kScaleX]; - float m01 = transform->data[Matrix4::kSkewY]; - float m10 = transform->data[Matrix4::kSkewX]; - float m11 = transform->data[Matrix4::kScaleY]; + if (CC_UNLIKELY(!transform.isPureTranslate())) { + float m00 = transform.data[Matrix4::kScaleX]; + float m01 = transform.data[Matrix4::kSkewY]; + float m10 = transform.data[Matrix4::kSkewX]; + float m11 = transform.data[Matrix4::kScaleY]; float scaleX = sqrt(m00 * m00 + m01 * m01); float scaleY = sqrt(m10 * m10 + m11 * m11); inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f; @@ -159,6 +154,17 @@ public: } return 0; } + + /** + * Outset the bounds of point data (for line endpoints or points) to account for AA stroke + * geometry. + */ + void expandBoundsForStrokeAA(SkRect& bounds) const { + float outset = halfStrokeWidth; + if (outset == 0) outset = 0.5f; + bounds.outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(), + outset * inverseScaleY + Vertex::GeometryFudgeFactor()); + } }; void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) { @@ -170,9 +176,9 @@ void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& int srcAindex = 0; int srcBindex = perimeter.size() - 1; while (srcAindex <= srcBindex) { - copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]); + buffer[currentIndex++] = perimeter[srcAindex]; if (srcAindex == srcBindex) break; - copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]); + buffer[currentIndex++] = perimeter[srcBindex]; srcAindex++; srcBindex--; } @@ -192,25 +198,25 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver int currentIndex = 0; const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); paintInfo.scaleOffsetForStrokeWidth(totalOffset); Vertex::set(&buffer[currentIndex++], - current->position[0] + totalOffset.x, - current->position[1] + totalOffset.y); + current->x + totalOffset.x, + current->y + totalOffset.y); Vertex::set(&buffer[currentIndex++], - current->position[0] - totalOffset.x, - current->position[1] - totalOffset.y); + current->x - totalOffset.x, + current->y - totalOffset.y); last = current; current = next; @@ -218,8 +224,8 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver } // wrap around to beginning - copyVertex(&buffer[currentIndex++], &buffer[0]); - copyVertex(&buffer[currentIndex++], &buffer[1]); + buffer[currentIndex++] = buffer[0]; + buffer[currentIndex++] = buffer[1]; DEBUG_DUMP_BUFFER(); } @@ -229,7 +235,7 @@ static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& cente vec2 strokeOffset = normal; paintInfo.scaleOffsetForStrokeWidth(strokeOffset); - vec2 referencePoint(center.position[0], center.position[1]); + vec2 referencePoint(center.x, center.y); if (paintInfo.cap == SkPaint::kSquare_Cap) { referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1); } @@ -255,11 +261,11 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, if (extra > 0) { // tessellate both round caps float beginTheta = atan2( - - (vertices[0].position[0] - vertices[1].position[0]), - vertices[0].position[1] - vertices[1].position[1]); + - (vertices[0].x - vertices[1].x), + vertices[0].y - vertices[1].y); float endTheta = atan2( - - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]), - vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]); + - (vertices[lastIndex].x - vertices[lastIndex - 1].x), + vertices[lastIndex].y - vertices[lastIndex - 1].y); const float dTheta = PI / (extra + 1); const float radialScale = 2.0f / (1 + cos(dTheta)); @@ -275,37 +281,37 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, vec2 beginRadialOffset(cos(beginTheta), sin(beginTheta)); paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset); Vertex::set(&buffer[capOffset], - vertices[0].position[0] + beginRadialOffset.x, - vertices[0].position[1] + beginRadialOffset.y); + vertices[0].x + beginRadialOffset.x, + vertices[0].y + beginRadialOffset.y); endTheta += dTheta; vec2 endRadialOffset(cos(endTheta), sin(endTheta)); paintInfo.scaleOffsetForStrokeWidth(endRadialOffset); Vertex::set(&buffer[allocSize - 1 - capOffset], - vertices[lastIndex].position[0] + endRadialOffset.x, - vertices[lastIndex].position[1] + endRadialOffset.y); + vertices[lastIndex].x + endRadialOffset.x, + vertices[lastIndex].y + endRadialOffset.y); } } int currentIndex = extra; const Vertex* last = &(vertices[0]); const Vertex* current = &(vertices[1]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true); for (unsigned int i = 1; i < vertices.size() - 1; i++) { const Vertex* next = &(vertices[i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal); paintInfo.scaleOffsetForStrokeWidth(strokeOffset); - vec2 center(current->position[0], current->position[1]); + vec2 center(current->x, current->y); Vertex::set(&buffer[currentIndex++], center + strokeOffset); Vertex::set(&buffer[currentIndex++], center - strokeOffset); @@ -329,7 +335,7 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices) */ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, - VertexBuffer& vertexBuffer) { + VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) { AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2); // generate alpha points - fill Alpha vertex gaps in between each point with @@ -337,13 +343,13 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver int currentIndex = 0; const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); // AA point offset from original point is that point's normal, such that each side is offset @@ -351,13 +357,13 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver vec2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal)); AlphaVertex::set(&buffer[currentIndex++], - current->position[0] + totalOffset.x, - current->position[1] + totalOffset.y, + current->x + totalOffset.x, + current->y + totalOffset.y, 0.0f); AlphaVertex::set(&buffer[currentIndex++], - current->position[0] - totalOffset.x, - current->position[1] - totalOffset.y, - 1.0f); + current->x - totalOffset.x, + current->y - totalOffset.y, + maxAlpha); last = current; current = next; @@ -365,8 +371,8 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver } // wrap around to beginning - copyAlphaVertex(&buffer[currentIndex++], &buffer[0]); - copyAlphaVertex(&buffer[currentIndex++], &buffer[1]); + buffer[currentIndex++] = buffer[0]; + buffer[currentIndex++] = buffer[1]; // zig zag between all previous points on the inside of the hull to create a // triangle strip that fills the hull, repeating the first inner point to @@ -374,9 +380,9 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver int srcAindex = 0; int srcBindex = perimeter.size() - 1; while (srcAindex <= srcBindex) { - copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]); + buffer[currentIndex++] = buffer[srcAindex * 2 + 1]; if (srcAindex == srcBindex) break; - copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]); + buffer[currentIndex++] = buffer[srcBindex * 2 + 1]; srcAindex++; srcBindex--; } @@ -416,7 +422,7 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& // determine referencePoint, the center point for the 4 primary cap vertices const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1); - vec2 referencePoint(point->position[0], point->position[1]); + vec2 referencePoint(point->x, point->y); if (paintInfo.cap == SkPaint::kSquare_Cap) { // To account for square cap, move the primary cap vertices (that create the AA edge) by the // stroke offset vector (rotated to be parallel to the stroke) @@ -471,8 +477,8 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& if (isFirst && i == extra - extraOffset) { //copy most recent two points to first two points - copyAlphaVertex(&buffer[0], &buffer[capPerimIndex - 2]); - copyAlphaVertex(&buffer[1], &buffer[capPerimIndex - 1]); + buffer[0] = buffer[capPerimIndex - 2]; + buffer[1] = buffer[capPerimIndex - 1]; capPerimIndex = 2; // start writing the rest of the round cap at index 2 } @@ -482,28 +488,28 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4; int capFillIndex = startCapFillIndex; for (int i = 0; i < extra + 2; i += 2) { - copyAlphaVertex(&buffer[capFillIndex++], &buffer[1 + i]); + buffer[capFillIndex++] = buffer[1 + i]; // TODO: to support odd numbers of divisions, break here on the last iteration - copyAlphaVertex(&buffer[capFillIndex++], &buffer[startCapFillIndex - 3 - i]); + buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i]; } } else { int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2); for (int i = 0; i < extra + 2; i += 2) { - copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 1 + i]); + buffer[capFillIndex++] = buffer[capIndex + 1 + i]; // TODO: to support odd numbers of divisions, break here on the last iteration - copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 3 + 2 * extra - i]); + buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i]; } } return; } if (isFirst) { - copyAlphaVertex(&buffer[0], &buffer[postCapIndex + 2]); - copyAlphaVertex(&buffer[1], &buffer[postCapIndex + 3]); - copyAlphaVertex(&buffer[postCapIndex + 4], &buffer[1]); // degenerate tris (the only two!) - copyAlphaVertex(&buffer[postCapIndex + 5], &buffer[postCapIndex + 1]); + buffer[0] = buffer[postCapIndex + 2]; + buffer[1] = buffer[postCapIndex + 3]; + buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!) + buffer[postCapIndex + 5] = buffer[postCapIndex + 1]; } else { - copyAlphaVertex(&buffer[6 * vertices.size()], &buffer[postCapIndex + 1]); - copyAlphaVertex(&buffer[6 * vertices.size() + 1], &buffer[postCapIndex + 3]); + buffer[6 * vertices.size()] = buffer[postCapIndex + 1]; + buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3]; } } @@ -576,8 +582,8 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, const Vertex* last = &(vertices[0]); const Vertex* current = &(vertices[1]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); // TODO: use normal from bezier traversal for cap, instead of from vertices @@ -585,8 +591,8 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, for (unsigned int i = 1; i < vertices.size() - 1; i++) { const Vertex* next = &(vertices[i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); @@ -598,30 +604,30 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, innerOffset -= AAOffset; AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + outerOffset.x, - current->position[1] + outerOffset.y, + current->x + outerOffset.x, + current->y + outerOffset.y, 0.0f); AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex--], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex--], - current->position[0] - outerOffset.x, - current->position[1] - outerOffset.y, + current->x - outerOffset.x, + current->y - outerOffset.y, 0.0f); current = next; @@ -646,13 +652,13 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); @@ -664,30 +670,30 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V innerOffset -= AAOffset; AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + outerOffset.x, - current->position[1] + outerOffset.y, + current->x + outerOffset.x, + current->y + outerOffset.y, 0.0f); AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex++], - current->position[0] - outerOffset.x, - current->position[1] - outerOffset.y, + current->x - outerOffset.x, + current->y - outerOffset.y, 0.0f); last = current; @@ -696,23 +702,23 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V } // wrap each strip around to beginning, creating degenerate tris to bridge strips - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]); - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]); - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]); + buffer[currentAAOuterIndex++] = buffer[0]; + buffer[currentAAOuterIndex++] = buffer[1]; + buffer[currentAAOuterIndex++] = buffer[1]; - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]); - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]); - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]); + buffer[currentStrokeIndex++] = buffer[offset]; + buffer[currentStrokeIndex++] = buffer[offset + 1]; + buffer[currentStrokeIndex++] = buffer[offset + 1]; - copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]); - copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]); + buffer[currentAAInnerIndex++] = buffer[2 * offset]; + buffer[currentAAInnerIndex++] = buffer[2 * offset + 1]; // don't need to create last degenerate tri DEBUG_DUMP_ALPHA_BUFFER(); } void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, - const mat4 *transform, VertexBuffer& vertexBuffer) { + const mat4& transform, VertexBuffer& vertexBuffer) { ATRACE_CALL(); const PaintInfo paintInfo(paint, transform); @@ -733,7 +739,8 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, // force close if we're filling the path, since fill path expects closed perimeter. bool forceClose = paintInfo.style != SkPaint::kStroke_Style; bool wasClosed = approximatePathOutlineVertices(path, forceClose, - threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, tempVertices); + threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, + OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices); if (!tempVertices.size()) { // path was empty, return without allocating vertex buffer @@ -743,7 +750,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, #if VERTEX_DEBUG for (unsigned int i = 0; i < tempVertices.size(); i++) { ALOGD("orig path: point at %f %f", - tempVertices[i].position[0], tempVertices[i].position[1]); + tempVertices[i].x, tempVertices[i].y); } #endif @@ -780,7 +787,7 @@ static void expandRectToCoverVertex(SkRect& rect, float x, float y) { rect.fBottom = fmaxf(rect.fBottom, y); } static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) { - expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]); + expandRectToCoverVertex(rect, vertex.x, vertex.y); } template <class TYPE> @@ -799,8 +806,8 @@ static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint); } -void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) { +void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint, + const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer) { const PaintInfo paintInfo(paint, transform); // determine point shape @@ -818,7 +825,8 @@ void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* Vector<Vertex> outlineVertices; approximatePathOutlineVertices(path, true, paintInfo.inverseScaleX * paintInfo.inverseScaleX, - paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices); + paintInfo.inverseScaleY * paintInfo.inverseScaleY, + OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices); if (!outlineVertices.size()) return; @@ -829,15 +837,18 @@ void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* getFillVerticesFromPerimeter(outlineVertices, tempBuffer); instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds); } else { - getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer); + // note: pass maxAlpha directly, since we want fill to be alpha modulated + getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha); instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds); } - expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke + // expand bounds from vertex coords to pixel data + paintInfo.expandBoundsForStrokeAA(bounds); + } -void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) { +void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint, + const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer) { ATRACE_CALL(); const PaintInfo paintInfo(paint, transform); @@ -873,20 +884,26 @@ void PathTessellator::tessellateLines(const float* points, int count, SkPaint* p expandRectToCoverVertex(bounds, tempVerticesData[1]); } - expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke - // since multiple objects tessellated into buffer, separate them with degen tris if (paintInfo.isAA) { vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize); } else { vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize); } + + // expand bounds from vertex coords to pixel data + paintInfo.expandBoundsForStrokeAA(bounds); } /////////////////////////////////////////////////////////////////////////////// // Simple path line approximation /////////////////////////////////////////////////////////////////////////////// +bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared, + Vector<Vertex>& outputVertices) { + return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices); +} + void pushToVector(Vector<Vertex>& vertices, float x, float y) { // TODO: make this not yuck vertices.push(); @@ -895,7 +912,8 @@ void pushToVector(Vector<Vertex>& vertices, float x, float y) { } bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose, - float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, + Vector<Vertex>& outputVertices) { ATRACE_CALL(); // TODO: to support joins other than sharp miter, join vertices should be labelled in the @@ -922,7 +940,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[0].x(), pts[0].y(), pts[2].x(), pts[2].y(), pts[1].x(), pts[1].y(), - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); break; case SkPath::kCubic_Verb: ALOGV("kCubic_Verb"); @@ -931,7 +949,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[1].x(), pts[1].y(), pts[3].x(), pts[3].y(), pts[2].x(), pts[2].y(), - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); break; default: break; @@ -939,8 +957,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo } int size = outputVertices.size(); - if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] && - outputVertices[0].position[1] == outputVertices[size - 1].position[1]) { + if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x && + outputVertices[0].y == outputVertices[size - 1].y) { outputVertices.pop(); return true; } @@ -954,7 +972,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo void PathTessellator::recursiveCubicBezierVertices( float p1x, float p1y, float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, - float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, + Vector<Vertex>& outputVertices) { float dx = p2x - p1x; float dy = p2y - p1y; float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); @@ -963,7 +982,7 @@ void PathTessellator::recursiveCubicBezierVertices( // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors - if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, p2x, p2y); } else { @@ -987,11 +1006,11 @@ void PathTessellator::recursiveCubicBezierVertices( recursiveCubicBezierVertices( p1x, p1y, p1c1x, p1c1y, mx, my, p1c1c2x, p1c1c2y, - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); recursiveCubicBezierVertices( mx, my, p2c1c2x, p2c1c2y, p2x, p2y, p2c2x, p2c2y, - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); } } @@ -999,12 +1018,13 @@ void PathTessellator::recursiveQuadraticBezierVertices( float ax, float ay, float bx, float by, float cx, float cy, - float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, + Vector<Vertex>& outputVertices) { float dx = bx - ax; float dy = by - ay; float d = (cx - bx) * dy - (cy - by) * dx; - if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, bx, by); } else { @@ -1018,9 +1038,9 @@ void PathTessellator::recursiveQuadraticBezierVertices( float my = (acy + bcy) * 0.5f; recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); } } diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h index 85797fc..a215b7a 100644 --- a/libs/hwui/PathTessellator.h +++ b/libs/hwui/PathTessellator.h @@ -22,100 +22,73 @@ #include "Matrix.h" #include "Rect.h" #include "Vertex.h" +#include "VertexBuffer.h" namespace android { namespace uirenderer { -class VertexBuffer { +class PathTessellator { public: - VertexBuffer(): - mBuffer(0), - mVertexCount(0), - mCleanupMethod(NULL) - {} - - ~VertexBuffer() { - if (mCleanupMethod) mCleanupMethod(mBuffer); - } + static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint); /** - This should be the only method used by the PathTessellator. Subsequent calls to alloc will - allocate space within the first allocation (useful if you want to eventually allocate - multiple regions within a single VertexBuffer, such as with PathTessellator::tesselateLines() + * Populates a VertexBuffer with a tessellated approximation of the input convex path, as a single + * triangle strip. Note: joins are not currently supported. + * + * @param path The path to be approximated + * @param paint The paint the path will be drawn with, indicating AA, painting style + * (stroke vs fill), stroke width, stroke cap & join style, etc. + * @param transform The transform the path is to be drawn with, used to drive stretch-aware path + * vertex approximation, and correct AA ramp offsetting. + * @param vertexBuffer The output buffer */ - template <class TYPE> - TYPE* alloc(int vertexCount) { - if (mVertexCount) { - TYPE* reallocBuffer = (TYPE*)mReallocBuffer; - // already have allocated the buffer, re-allocate space within - if (mReallocBuffer != mBuffer) { - // not first re-allocation, leave space for degenerate triangles to separate strips - reallocBuffer += 2; - } - mReallocBuffer = reallocBuffer + vertexCount; - return reallocBuffer; - } - mVertexCount = vertexCount; - mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount]; - mCleanupMethod = &(cleanup<TYPE>); - - return (TYPE*)mBuffer; - } - - template <class TYPE> - void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) { - int verticesToCopy = srcBuffer.getVertexCount(); - - TYPE* dst = alloc<TYPE>(verticesToCopy); - TYPE* src = (TYPE*)srcBuffer.getBuffer(); - - for (int i = 0; i < verticesToCopy; i++) { - TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset); - } - } - - void* getBuffer() const { return mBuffer; } // shouldn't be const, since not a const ptr? - unsigned int getVertexCount() const { return mVertexCount; } - - template <class TYPE> - void createDegenerateSeparators(int allocSize) { - TYPE* end = (TYPE*)mBuffer + mVertexCount; - for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) { - memcpy(degen, degen - 1, sizeof(TYPE)); - memcpy(degen + 1, degen + 2, sizeof(TYPE)); - } - } - -private: - template <class TYPE> - static void cleanup(void* buffer) { - delete[] (TYPE*)buffer; - } - - void* mBuffer; - unsigned int mVertexCount; - - void* mReallocBuffer; // used for multi-allocation - - void (*mCleanupMethod)(void*); -}; - -class PathTessellator { -public: - static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint, bool forceExpand); - static void tessellatePath(const SkPath& path, const SkPaint* paint, - const mat4 *transform, VertexBuffer& vertexBuffer); + const mat4& transform, VertexBuffer& vertexBuffer); - static void tessellatePoints(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer); + /** + * Populates a VertexBuffer with a tessellated approximation of points as a single triangle + * strip (with degenerate tris separating), respecting the shape defined by the paint cap. + * + * @param points The center vertices of the points to be drawn + * @param count The number of floats making up the point vertices + * @param paint The paint the points will be drawn with indicating AA, stroke width & cap + * @param transform The transform the points will be drawn with, used to drive stretch-aware path + * vertex approximation, and correct AA ramp offsetting + * @param bounds An output rectangle, which returns the total area covered by the output buffer + * @param vertexBuffer The output buffer + */ + static void tessellatePoints(const float* points, int count, const SkPaint* paint, + const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer); - static void tessellateLines(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer); + /** + * Populates a VertexBuffer with a tessellated approximation of lines as a single triangle + * strip (with degenerate tris separating). + * + * @param points Pairs of endpoints defining the lines to be drawn + * @param count The number of floats making up the line vertices + * @param paint The paint the lines will be drawn with indicating AA, stroke width & cap + * @param transform The transform the points will be drawn with, used to drive stretch-aware path + * vertex approximation, and correct AA ramp offsetting + * @param bounds An output rectangle, which returns the total area covered by the output buffer + * @param vertexBuffer The output buffer + */ + static void tessellateLines(const float* points, int count, const SkPaint* paint, + const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer); + + /** + * Approximates a convex, CW outline into a Vector of 2d vertices. + * + * @param path The outline to be approximated + * @param thresholdSquared The threshold of acceptable error (in pixels) when approximating + * @param outputVertices An empty Vector which will be populated with the output + */ + static bool approximatePathOutlineVertices(const SkPath &path, float thresholdSquared, + Vector<Vertex> &outputVertices); private: static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose, - float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex> &outputVertices); + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, + Vector<Vertex> &outputVertices); /* endpoints a & b, @@ -125,7 +98,7 @@ private: float ax, float ay, float bx, float by, float cx, float cy, - float sqrInvScaleX, float sqrInvScaleY, + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex> &outputVertices); /* @@ -137,7 +110,7 @@ private: float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, - float sqrInvScaleX, float sqrInvScaleY, + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex> &outputVertices); }; diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 58f5325..ee77897 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -173,7 +173,7 @@ void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, // up and to the left. // This offset value is based on an assumption that some hardware may use as // little as 12.4 precision, so we offset by slightly more than 1/16. - p.translate(Vertex::gGeometryFudgeFactor, Vertex::gGeometryFudgeFactor); + p.translate(Vertex::GeometryFudgeFactor(), Vertex::GeometryFudgeFactor()); glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]); } mProjection = projectionMatrix; diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index f6ac8ec..33c91b3 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -51,9 +51,8 @@ namespace uirenderer { #define PROGRAM_KEY_GRADIENT 0x8 #define PROGRAM_KEY_BITMAP_FIRST 0x10 #define PROGRAM_KEY_COLOR_MATRIX 0x20 -#define PROGRAM_KEY_COLOR_LIGHTING 0x40 -#define PROGRAM_KEY_COLOR_BLEND 0x80 -#define PROGRAM_KEY_BITMAP_NPOT 0x100 +#define PROGRAM_KEY_COLOR_BLEND 0x40 +#define PROGRAM_KEY_BITMAP_NPOT 0x80 #define PROGRAM_KEY_SWAP_SRC_DST 0x2000 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 @@ -104,7 +103,6 @@ struct ProgramDescription { enum ColorModifier { kColorNone = 0, kColorMatrix, - kColorLighting, kColorBlend }; @@ -207,7 +205,7 @@ struct ProgramDescription { * the fragment shader. When this method returns true, the program should * be provided with a modulation color. */ - bool setColor(const float r, const float g, const float b, const float a) { + bool setColorModulate(const float a) { modulate = a < COLOR_COMPONENT_THRESHOLD; return modulate; } @@ -217,7 +215,7 @@ struct ProgramDescription { * the fragment shader. When this method returns true, the program should * be provided with a modulation color. */ - bool setAlpha8Color(const float r, const float g, const float b, const float a) { + bool setAlpha8ColorModulate(const float r, const float g, const float b, const float a) { modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD || g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD; return modulate; @@ -248,9 +246,6 @@ struct ProgramDescription { case kColorMatrix: key |= PROGRAM_KEY_COLOR_MATRIX; break; - case kColorLighting: - key |= PROGRAM_KEY_COLOR_LIGHTING; - break; case kColorBlend: key |= PROGRAM_KEY_COLOR_BLEND; key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT; diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index a5ce6f6..6d50410 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -148,15 +148,12 @@ const char* gFS_Uniforms_GradientSampler[2] = { }; const char* gFS_Uniforms_BitmapSampler = "uniform sampler2D bitmapSampler;\n"; -const char* gFS_Uniforms_ColorOp[4] = { +const char* gFS_Uniforms_ColorOp[3] = { // None "", // Matrix "uniform mat4 colorMatrix;\n" "uniform vec4 colorMatrixVector;\n", - // Lighting - "uniform vec4 lightingMul;\n" - "uniform vec4 lightingAdd;\n", // PorterDuff "uniform vec4 colorBlend;\n" }; @@ -311,17 +308,13 @@ const char* gFS_Main_FragColor_Blend = " gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n"; const char* gFS_Main_FragColor_Blend_Swap = " gl_FragColor = blendFramebuffer(gl_LastFragColor, fragColor);\n"; -const char* gFS_Main_ApplyColorOp[4] = { +const char* gFS_Main_ApplyColorOp[3] = { // None "", // Matrix " fragColor *= colorMatrix;\n" " fragColor += colorMatrixVector;\n" " fragColor.rgb *= fragColor.a;\n", - // Lighting - " float lightingAlpha = fragColor.a;\n" - " fragColor = min(fragColor * lightingMul + (lightingAdd * lightingAlpha), lightingAlpha);\n" - " fragColor.a = lightingAlpha;\n", // PorterDuff " fragColor = blendColors(colorBlend, fragColor);\n" }; diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index dabd8d4..f38d8b7 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -18,6 +18,7 @@ #define ANDROID_HWUI_RECT_H #include <cmath> +#include <SkRect.h> #include <utils/Log.h> @@ -29,6 +30,8 @@ namespace uirenderer { #define RECT_STRING "%7.2f %7.2f %7.2f %7.2f" #define RECT_ARGS(r) \ (r).left, (r).top, (r).right, (r).bottom +#define SK_RECT_ARGS(r) \ + (r).left(), (r).top(), (r).right(), (r).bottom() /////////////////////////////////////////////////////////////////////////////// // Structs @@ -68,6 +71,13 @@ public: bottom(height) { } + inline Rect(const SkRect& rect): + left(rect.fLeft), + top(rect.fTop), + right(rect.fRight), + bottom(rect.fBottom) { + } + friend int operator==(const Rect& a, const Rect& b) { return !memcmp(&a, &b, sizeof(a)); } @@ -165,6 +175,10 @@ public: bottom += dy; } + void inset(float delta) { + outset(-delta); + } + void outset(float delta) { left -= delta; top -= delta; @@ -190,19 +204,19 @@ public: * from this inset will only incur similarly small errors in output, due to transparency * in extreme outside of the geometry. */ - left = floorf(left + Vertex::gGeometryFudgeFactor); - top = floorf(top + Vertex::gGeometryFudgeFactor); - right = ceilf(right - Vertex::gGeometryFudgeFactor); - bottom = ceilf(bottom - Vertex::gGeometryFudgeFactor); + left = floorf(left + Vertex::GeometryFudgeFactor()); + top = floorf(top + Vertex::GeometryFudgeFactor()); + right = ceilf(right - Vertex::GeometryFudgeFactor()); + bottom = ceilf(bottom - Vertex::GeometryFudgeFactor()); } else { /* For other geometry, we do the regular rounding in order to snap, but also outset the * bounds by a fudge factor. This ensures that ambiguous geometry (e.g. a non-AA Rect * with top left at (0.5, 0.5)) will err on the side of a larger damage rect. */ - left = floorf(left + 0.5f - Vertex::gGeometryFudgeFactor); - top = floorf(top + 0.5f - Vertex::gGeometryFudgeFactor); - right = floorf(right + 0.5f + Vertex::gGeometryFudgeFactor); - bottom = floorf(bottom + 0.5f + Vertex::gGeometryFudgeFactor); + left = floorf(left + 0.5f - Vertex::GeometryFudgeFactor()); + top = floorf(top + 0.5f - Vertex::GeometryFudgeFactor()); + right = floorf(right + 0.5f + Vertex::GeometryFudgeFactor()); + bottom = floorf(bottom + 0.5f + Vertex::GeometryFudgeFactor()); } } @@ -213,8 +227,15 @@ public: bottom = floorf(bottom + 0.5f); } - void dump() const { - ALOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom); + void roundOut() { + left = floorf(left); + top = floorf(top); + right = ceilf(right); + bottom = ceilf(bottom); + } + + void dump(const char* label) const { + ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom); } private: diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp new file mode 100644 index 0000000..9902ff1 --- /dev/null +++ b/libs/hwui/RenderNode.cpp @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2014 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. + */ + +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include "RenderNode.h" + +#include <algorithm> + +#include <SkCanvas.h> +#include <algorithm> + +#include <utils/Trace.h> + +#include "Debug.h" +#include "DisplayListOp.h" +#include "DisplayListLogBuffer.h" +#include "utils/MathUtils.h" + +namespace android { +namespace uirenderer { + +void RenderNode::outputLogBuffer(int fd) { + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); + if (logBuffer.isEmpty()) { + return; + } + + FILE *file = fdopen(fd, "a"); + + fprintf(file, "\nRecent DisplayList operations\n"); + logBuffer.outputCommands(file); + + String8 cachesLog; + Caches::getInstance().dumpMemoryUsage(cachesLog); + fprintf(file, "\nCaches:\n%s", cachesLog.string()); + fprintf(file, "\n"); + + fflush(file); +} + +RenderNode::RenderNode() + : mNeedsPropertiesSync(false) + , mNeedsDisplayListDataSync(false) + , mDisplayListData(0) + , mStagingDisplayListData(0) + , mNeedsAnimatorsSync(false) { +} + +RenderNode::~RenderNode() { + delete mDisplayListData; + delete mStagingDisplayListData; +} + +void RenderNode::setStagingDisplayList(DisplayListData* data) { + mNeedsDisplayListDataSync = true; + delete mStagingDisplayListData; + mStagingDisplayListData = data; + if (mStagingDisplayListData) { + Caches::getInstance().registerFunctors(mStagingDisplayListData->functorCount); + } +} + +/** + * This function is a simplified version of replay(), where we simply retrieve and log the + * display list. This function should remain in sync with the replay() function. + */ +void RenderNode::output(uint32_t level) { + ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this, + getName(), isRenderable()); + ALOGD("%*s%s %d", level * 2, "", "Save", + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + + properties().debugOutputProperties(level); + int flags = DisplayListOp::kOpLogFlag_Recurse; + for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { + mDisplayListData->displayListOps[i]->output(level, flags); + } + + ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName()); +} + +void RenderNode::prepareTree(TreeInfo& info) { + ATRACE_CALL(); + + prepareTreeImpl(info); +} + +void RenderNode::prepareTreeImpl(TreeInfo& info) { + if (info.performStagingPush) { + pushStagingChanges(info); + } + if (info.evaluateAnimations) { + evaluateAnimations(info); + } + prepareSubTree(info, mDisplayListData); +} + +static bool is_finished(const sp<BaseRenderNodeAnimator>& animator) { + return animator->isFinished(); +} + +void RenderNode::pushStagingChanges(TreeInfo& info) { + if (mNeedsPropertiesSync) { + mNeedsPropertiesSync = false; + mProperties = mStagingProperties; + } + if (mNeedsAnimatorsSync) { + mAnimators.resize(mStagingAnimators.size()); + std::vector< sp<BaseRenderNodeAnimator> >::iterator it; + // hint: this means copy_if_not() + it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(), + mAnimators.begin(), is_finished); + mAnimators.resize(std::distance(mAnimators.begin(), it)); + } + if (mNeedsDisplayListDataSync) { + mNeedsDisplayListDataSync = false; + // Do a push pass on the old tree to handle freeing DisplayListData + // that are no longer used + TreeInfo oldTreeInfo; + prepareSubTree(oldTreeInfo, mDisplayListData); + // TODO: The damage for the old tree should be accounted for + delete mDisplayListData; + mDisplayListData = mStagingDisplayListData; + mStagingDisplayListData = 0; + } +} + +class AnimateFunctor { +public: + AnimateFunctor(RenderNode* target, TreeInfo& info) + : mTarget(target), mInfo(info) {} + + bool operator() (sp<BaseRenderNodeAnimator>& animator) { + return animator->animate(mTarget, mInfo); + } +private: + RenderNode* mTarget; + TreeInfo& mInfo; +}; + +void RenderNode::evaluateAnimations(TreeInfo& info) { + if (!mAnimators.size()) return; + + AnimateFunctor functor(this, info); + std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd; + newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); + mAnimators.erase(newEnd, mAnimators.end()); + mProperties.updateMatrix(); + info.out.hasAnimations |= mAnimators.size(); +} + +void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { + if (subtree) { + TextureCache& cache = Caches::getInstance().textureCache; + info.out.hasFunctors |= subtree->functorCount; + // TODO: Fix ownedBitmapResources to not require disabling prepareTextures + // and thus falling out of async drawing path. + if (subtree->ownedBitmapResources.size()) { + info.prepareTextures = false; + } + for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) { + info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]); + } + for (size_t i = 0; i < subtree->children().size(); i++) { + RenderNode* childNode = subtree->children()[i]->mDisplayList; + childNode->prepareTreeImpl(info); + } + } +} + +/* + * For property operations, we pass a savecount of 0, since the operations aren't part of the + * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in + * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount()) + */ +#define PROPERTY_SAVECOUNT 0 + +template <class T> +void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { +#if DEBUG_DISPLAY_LIST + properties().debugOutputProperties(handler.level() + 1); +#endif + if (properties().getLeft() != 0 || properties().getTop() != 0) { + renderer.translate(properties().getLeft(), properties().getTop()); + } + if (properties().getStaticMatrix()) { + renderer.concatMatrix(properties().getStaticMatrix()); + } else if (properties().getAnimationMatrix()) { + renderer.concatMatrix(properties().getAnimationMatrix()); + } + if (properties().hasTransformMatrix()) { + if (properties().isTransformTranslateOnly()) { + renderer.translate(properties().getTranslationX(), properties().getTranslationY()); + } else { + renderer.concatMatrix(*properties().getTransformMatrix()); + } + } + bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds(); + if (properties().getAlpha() < 1) { + if (properties().getCaching()) { + renderer.setOverrideLayerAlpha(properties().getAlpha()); + } else if (!properties().getHasOverlappingRendering()) { + renderer.scaleAlpha(properties().getAlpha()); + } else { + // TODO: should be able to store the size of a DL at record time and not + // have to pass it into this call. In fact, this information might be in the + // location/size info that we store with the new native transform data. + int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag; + if (clipToBoundsNeeded) { + saveFlags |= SkCanvas::kClipToLayer_SaveFlag; + clipToBoundsNeeded = false; // clipping done by saveLayer + } + + SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( + 0, 0, properties().getWidth(), properties().getHeight(), + properties().getAlpha() * 255, saveFlags); + handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); + } + } + if (clipToBoundsNeeded) { + ClipRectOp* op = new (handler.allocator()) ClipRectOp( + 0, 0, properties().getWidth(), properties().getHeight(), SkRegion::kIntersect_Op); + handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); + } + + if (CC_UNLIKELY(properties().hasClippingPath())) { + // TODO: optimize for round rect/circle clipping + const SkPath* path = properties().getClippingPath(); + ClipPathOp* op = new (handler.allocator()) ClipPathOp(path, SkRegion::kIntersect_Op); + handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); + } +} + +/** + * Apply property-based transformations to input matrix + * + * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4 + * matrix computation instead of the Skia 3x3 matrix + camera hackery. + */ +void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) { + if (properties().getLeft() != 0 || properties().getTop() != 0) { + matrix.translate(properties().getLeft(), properties().getTop()); + } + if (properties().getStaticMatrix()) { + mat4 stat(*properties().getStaticMatrix()); + matrix.multiply(stat); + } else if (properties().getAnimationMatrix()) { + mat4 anim(*properties().getAnimationMatrix()); + matrix.multiply(anim); + } + + bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ()); + if (properties().hasTransformMatrix() || applyTranslationZ) { + if (properties().isTransformTranslateOnly()) { + matrix.translate(properties().getTranslationX(), properties().getTranslationY(), + true3dTransform ? properties().getZ() : 0.0f); + } else { + if (!true3dTransform) { + matrix.multiply(*properties().getTransformMatrix()); + } else { + mat4 true3dMat; + true3dMat.loadTranslate( + properties().getPivotX() + properties().getTranslationX(), + properties().getPivotY() + properties().getTranslationY(), + properties().getZ()); + true3dMat.rotate(properties().getRotationX(), 1, 0, 0); + true3dMat.rotate(properties().getRotationY(), 0, 1, 0); + true3dMat.rotate(properties().getRotation(), 0, 0, 1); + true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1); + true3dMat.translate(-properties().getPivotX(), -properties().getPivotY()); + + matrix.multiply(true3dMat); + } + } + } +} + +/** + * Organizes the DisplayList hierarchy to prepare for background projection reordering. + * + * This should be called before a call to defer() or drawDisplayList() + * + * Each DisplayList that serves as a 3d root builds its list of composited children, + * which are flagged to not draw in the standard draw loop. + */ +void RenderNode::computeOrdering() { + ATRACE_CALL(); + mProjectedNodes.clear(); + + // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that + // transform properties are applied correctly to top level children + if (mDisplayListData == NULL) return; + for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children()[i]; + childOp->mDisplayList->computeOrderingImpl(childOp, + properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity()); + } +} + +void RenderNode::computeOrderingImpl( + DrawDisplayListOp* opState, + const SkPath* outlineOfProjectionSurface, + Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, + const mat4* transformFromProjectionSurface) { + mProjectedNodes.clear(); + if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return; + + // TODO: should avoid this calculation in most cases + // TODO: just calculate single matrix, down to all leaf composited elements + Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); + localTransformFromProjectionSurface.multiply(opState->mTransformFromParent); + + if (properties().getProjectBackwards()) { + // composited projectee, flag for out of order draw, save matrix, and store in proj surface + opState->mSkipInOrderDraw = true; + opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface); + compositedChildrenOfProjectionSurface->add(opState); + } else { + // standard in order draw + opState->mSkipInOrderDraw = false; + } + + if (mDisplayListData->children().size() > 0) { + const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0; + bool haveAppliedPropertiesToProjection = false; + for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children()[i]; + RenderNode* child = childOp->mDisplayList; + + const SkPath* projectionOutline = NULL; + Vector<DrawDisplayListOp*>* projectionChildren = NULL; + const mat4* projectionTransform = NULL; + if (isProjectionReceiver && !child->properties().getProjectBackwards()) { + // if receiving projections, collect projecting descendent + + // Note that if a direct descendent is projecting backwards, we pass it's + // grandparent projection collection, since it shouldn't project onto it's + // parent, where it will already be drawing. + projectionOutline = properties().getOutline().getPath(); + projectionChildren = &mProjectedNodes; + projectionTransform = &mat4::identity(); + } else { + if (!haveAppliedPropertiesToProjection) { + applyViewPropertyTransforms(localTransformFromProjectionSurface); + haveAppliedPropertiesToProjection = true; + } + projectionOutline = outlineOfProjectionSurface; + projectionChildren = compositedChildrenOfProjectionSurface; + projectionTransform = &localTransformFromProjectionSurface; + } + child->computeOrderingImpl(childOp, + projectionOutline, projectionChildren, projectionTransform); + } + } +} + +class DeferOperationHandler { +public: + DeferOperationHandler(DeferStateStruct& deferStruct, int level) + : mDeferStruct(deferStruct), mLevel(level) {} + inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { + operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); + } + inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } + inline void startMark(const char* name) {} // do nothing + inline void endMark() {} + inline int level() { return mLevel; } + inline int replayFlags() { return mDeferStruct.mReplayFlags; } + +private: + DeferStateStruct& mDeferStruct; + const int mLevel; +}; + +void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) { + DeferOperationHandler handler(deferStruct, 0); + if (MathUtils::isPositive(properties().getZ())) { + issueDrawShadowOperation(Matrix4::identity(), handler); + } + issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); +} + +void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) { + DeferOperationHandler handler(deferStruct, level); + issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); +} + +class ReplayOperationHandler { +public: + ReplayOperationHandler(ReplayStateStruct& replayStruct, int level) + : mReplayStruct(replayStruct), mLevel(level) {} + inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { +#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS + mReplayStruct.mRenderer.eventMark(operation->name()); +#endif + operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); + } + inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } + inline void startMark(const char* name) { + mReplayStruct.mRenderer.startMark(name); + } + inline void endMark() { + mReplayStruct.mRenderer.endMark(); + } + inline int level() { return mLevel; } + inline int replayFlags() { return mReplayStruct.mReplayFlags; } + +private: + ReplayStateStruct& mReplayStruct; + const int mLevel; +}; + +void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) { + ReplayOperationHandler handler(replayStruct, 0); + if (MathUtils::isPositive(properties().getZ())) { + issueDrawShadowOperation(Matrix4::identity(), handler); + } + issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); +} + +void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) { + ReplayOperationHandler handler(replayStruct, level); + issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); +} + +void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) { + if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return; + + for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children()[i]; + RenderNode* child = childOp->mDisplayList; + float childZ = child->properties().getZ(); + + if (!MathUtils::isZero(childZ)) { + zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp)); + childOp->mSkipInOrderDraw = true; + } else if (!child->properties().getProjectBackwards()) { + // regular, in order drawing DisplayList + childOp->mSkipInOrderDraw = false; + } + } + + // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order) + std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end()); +} + +template <class T> +void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) { + if (properties().getAlpha() <= 0.0f) return; + + mat4 shadowMatrixXY(transformFromParent); + applyViewPropertyTransforms(shadowMatrixXY); + + // Z matrix needs actual 3d transformation, so mapped z values will be correct + mat4 shadowMatrixZ(transformFromParent); + applyViewPropertyTransforms(shadowMatrixZ, true); + + const SkPath* outlinePath = properties().getOutline().getPath(); + const RevealClip& revealClip = properties().getRevealClip(); + const SkPath* revealClipPath = revealClip.hasConvexClip() + ? revealClip.getPath() : NULL; // only pass the reveal clip's path if it's convex + + /** + * The drawing area of the caster is always the same as the its perimeter (which + * the shadow system uses) *except* in the inverse clip case. Inform the shadow + * system that the caster's drawing area (as opposed to its perimeter) has been + * clipped, so that it knows the caster can't be opaque. + */ + bool casterUnclipped = !revealClip.willClip() || revealClip.hasConvexClip(); + + DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp( + shadowMatrixXY, shadowMatrixZ, + properties().getAlpha(), casterUnclipped, + properties().getWidth(), properties().getHeight(), + outlinePath, revealClipPath); + handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); +} + +#define SHADOW_DELTA 0.1f + +template <class T> +void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes, + ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) { + const int size = zTranslatedNodes.size(); + if (size == 0 + || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f) + || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) { + // no 3d children to draw + return; + } + + /** + * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters + * with very similar Z heights to draw together. + * + * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are + * underneath both, and neither's shadow is drawn on top of the other. + */ + const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); + size_t drawIndex, shadowIndex, endIndex; + if (mode == kNegativeZChildren) { + drawIndex = 0; + endIndex = nonNegativeIndex; + shadowIndex = endIndex; // draw no shadows + } else { + drawIndex = nonNegativeIndex; + endIndex = size; + shadowIndex = drawIndex; // potentially draw shadow for each pos Z child + } + + DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "", + endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive"); + + float lastCasterZ = 0.0f; + while (shadowIndex < endIndex || drawIndex < endIndex) { + if (shadowIndex < endIndex) { + DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value; + RenderNode* caster = casterOp->mDisplayList; + const float casterZ = zTranslatedNodes[shadowIndex].key; + // attempt to render the shadow if the caster about to be drawn is its caster, + // OR if its caster's Z value is similar to the previous potential caster + if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { + caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler); + + lastCasterZ = casterZ; // must do this even if current caster not casting a shadow + shadowIndex++; + continue; + } + } + + // only the actual child DL draw needs to be in save/restore, + // since it modifies the renderer's matrix + int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); + + DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value; + RenderNode* child = childOp->mDisplayList; + + renderer.concatMatrix(childOp->mTransformFromParent); + childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone + handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); + childOp->mSkipInOrderDraw = true; + + renderer.restoreToCount(restoreTo); + drawIndex++; + } +} + +template <class T> +void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) { + DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size()); + const SkPath* projectionReceiverOutline = properties().getOutline().getPath(); + bool maskProjecteesWithPath = projectionReceiverOutline != NULL + && !projectionReceiverOutline->isRect(NULL); + int restoreTo = renderer.getSaveCount(); + + // If the projection reciever has an outline, we mask each of the projected rendernodes to it + // Either with clipRect, or special saveLayer masking + LinearAllocator& alloc = handler.allocator(); + if (projectionReceiverOutline != NULL) { + const SkRect& outlineBounds = projectionReceiverOutline->getBounds(); + if (projectionReceiverOutline->isRect(NULL)) { + // mask to the rect outline simply with clipRect + handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); + ClipRectOp* clipOp = new (alloc) ClipRectOp( + outlineBounds.left(), outlineBounds.top(), + outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op); + handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); + } else { + // wrap the projected RenderNodes with a SaveLayer that will mask to the outline + SaveLayerOp* op = new (alloc) SaveLayerOp( + outlineBounds.left(), outlineBounds.top(), + outlineBounds.right(), outlineBounds.bottom(), + 255, SkCanvas::kARGB_ClipLayer_SaveFlag); + op->setMask(projectionReceiverOutline); + handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); + + /* TODO: add optimizations here to take advantage of placement/size of projected + * children (which may shrink saveLayer area significantly). This is dependent on + * passing actual drawing/dirtying bounds of projected content down to native. + */ + } + } + + // draw projected nodes + for (size_t i = 0; i < mProjectedNodes.size(); i++) { + DrawDisplayListOp* childOp = mProjectedNodes[i]; + + // matrix save, concat, and restore can be done safely without allocating operations + int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); + renderer.concatMatrix(childOp->mTransformFromCompositingAncestor); + childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone + handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); + childOp->mSkipInOrderDraw = true; + renderer.restoreToCount(restoreTo); + } + + if (projectionReceiverOutline != NULL) { + handler(new (alloc) RestoreToCountOp(restoreTo), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); + } +} + +/** + * This function serves both defer and replay modes, and will organize the displayList's component + * operations for a single frame: + * + * Every 'simple' state operation that affects just the matrix and alpha (or other factors of + * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom + * defer logic) and operations in displayListOps are issued through the 'handler' which handles the + * defer vs replay logic, per operation + */ +template <class T> +void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { + const int level = handler.level(); + if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) { + DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName()); + return; + } + + handler.startMark(getName()); + +#if DEBUG_DISPLAY_LIST + const Rect& clipRect = renderer.getLocalClipBounds(); + DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f", + level * 2, "", this, getName(), + clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); +#endif + + LinearAllocator& alloc = handler.allocator(); + int restoreTo = renderer.getSaveCount(); + handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); + + DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); + + setViewProperties<T>(renderer, handler); + + bool quickRejected = properties().getClipToBounds() + && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); + if (!quickRejected) { + Vector<ZDrawDisplayListOpPair> zTranslatedNodes; + buildZSortedChildList(zTranslatedNodes); + + // for 3d root, draw children with negative z values + issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler); + + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); + const int saveCountOffset = renderer.getSaveCount() - 1; + const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; + for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { + DisplayListOp *op = mDisplayListData->displayListOps[i]; + +#if DEBUG_DISPLAY_LIST + op->output(level + 1); +#endif + logBuffer.writeCommand(level, op->name()); + handler(op, saveCountOffset, properties().getClipToBounds()); + + if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) { + issueOperationsOfProjectedChildren(renderer, handler); + } + } + + // for 3d root, draw children with positive z values + issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler); + } + + DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); + handler(new (alloc) RestoreToCountOp(restoreTo), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); + renderer.setOverrideLayerAlpha(1.0f); + + DISPLAY_LIST_LOGD("%*sDone (%p, %s)", level * 2, "", this, getName()); + handler.endMark(); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h new file mode 100644 index 0000000..159903c --- /dev/null +++ b/libs/hwui/RenderNode.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2014 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 RENDERNODE_H +#define RENDERNODE_H + +#ifndef LOG_TAG + #define LOG_TAG "OpenGLRenderer" +#endif + +#include <set> +#include <vector> + +#include <SkCamera.h> +#include <SkMatrix.h> + +#include <private/hwui/DrawGlInfo.h> + +#include <utils/KeyedVector.h> +#include <utils/LinearAllocator.h> +#include <utils/RefBase.h> +#include <utils/SortedVector.h> +#include <utils/String8.h> +#include <utils/Vector.h> + +#include <cutils/compiler.h> + +#include <androidfw/ResourceTypes.h> + +#include "Debug.h" +#include "Matrix.h" +#include "DeferredDisplayList.h" +#include "DisplayList.h" +#include "RenderProperties.h" +#include "TreeInfo.h" +#include "utils/VirtualLightRefBase.h" + +class SkBitmap; +class SkPaint; +class SkPath; +class SkRegion; + +namespace android { +namespace uirenderer { + +class DeferredDisplayList; +class DisplayListOp; +class DisplayListRenderer; +class OpenGLRenderer; +class Rect; +class Layer; +class SkiaShader; + +class ClipRectOp; +class SaveLayerOp; +class SaveOp; +class RestoreToCountOp; +class DrawDisplayListOp; + +/** + * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. + * + * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording + * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData + * (which holds the actual data), and DisplayList (which holds properties and performs playback onto + * a renderer). + * + * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's + * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay + * attached. + */ +class RenderNode : public VirtualLightRefBase { +public: + ANDROID_API RenderNode(); + ANDROID_API virtual ~RenderNode(); + + // See flags defined in DisplayList.java + enum ReplayFlag { + kReplayFlag_ClipChildren = 0x1 + }; + + ANDROID_API static void outputLogBuffer(int fd); + + ANDROID_API void setStagingDisplayList(DisplayListData* newData); + + void computeOrdering(); + + void deferNodeTree(DeferStateStruct& deferStruct); + void deferNodeInParent(DeferStateStruct& deferStruct, const int level); + + void replayNodeTree(ReplayStateStruct& replayStruct); + void replayNodeInParent(ReplayStateStruct& replayStruct, const int level); + + ANDROID_API void output(uint32_t level = 1); + + bool isRenderable() const { + return mDisplayListData && mDisplayListData->hasDrawOps; + } + + const char* getName() const { + return mName.string(); + } + + void setName(const char* name) { + if (name) { + char* lastPeriod = strrchr(name, '.'); + if (lastPeriod) { + mName.setTo(lastPeriod + 1); + } else { + mName.setTo(name); + } + } + } + + const RenderProperties& properties() { + return mProperties; + } + + RenderProperties& animatorProperties() { + return mProperties; + } + + const RenderProperties& stagingProperties() { + return mStagingProperties; + } + + RenderProperties& mutateStagingProperties() { + mNeedsPropertiesSync = true; + return mStagingProperties; + } + + int getWidth() { + return properties().getWidth(); + } + + int getHeight() { + return properties().getHeight(); + } + + ANDROID_API virtual void prepareTree(TreeInfo& info); + + // UI thread only! + ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator) { + mStagingAnimators.insert(animator); + mNeedsAnimatorsSync = true; + } + + // UI thread only! + ANDROID_API void removeAnimator(const sp<BaseRenderNodeAnimator>& animator) { + mStagingAnimators.erase(animator); + mNeedsAnimatorsSync = true; + } + +private: + typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair; + + static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) { + for (size_t i = 0; i < nodes.size(); i++) { + if (nodes[i].key >= 0.0f) return i; + } + return nodes.size(); + } + + enum ChildrenSelectMode { + kNegativeZChildren, + kPositiveZChildren + }; + + void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false); + + void computeOrderingImpl(DrawDisplayListOp* opState, + const SkPath* outlineOfProjectionSurface, + Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, + const mat4* transformFromProjectionSurface); + + template <class T> + inline void setViewProperties(OpenGLRenderer& renderer, T& handler); + + void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes); + + template<class T> + inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler); + + template <class T> + inline void issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes, + ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler); + + template <class T> + inline void issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler); + + /** + * Issue the RenderNode's operations into a handler, recursing for subtrees through + * DrawDisplayListOp's defer() or replay() methods + */ + template <class T> + inline void issueOperations(OpenGLRenderer& renderer, T& handler); + + class TextContainer { + public: + size_t length() const { + return mByteLength; + } + + const char* text() const { + return (const char*) mText; + } + + size_t mByteLength; + const char* mText; + }; + + void prepareTreeImpl(TreeInfo& info); + void pushStagingChanges(TreeInfo& info); + void evaluateAnimations(TreeInfo& info); + void prepareSubTree(TreeInfo& info, DisplayListData* subtree); + + String8 mName; + + bool mNeedsPropertiesSync; + RenderProperties mProperties; + RenderProperties mStagingProperties; + + bool mNeedsDisplayListDataSync; + DisplayListData* mDisplayListData; + DisplayListData* mStagingDisplayListData; + + bool mNeedsAnimatorsSync; + std::set< sp<BaseRenderNodeAnimator> > mStagingAnimators; + std::vector< sp<BaseRenderNodeAnimator> > mAnimators; + + /** + * Draw time state - these properties are only set and used during rendering + */ + + // for projection surfaces, contains a list of all children items + Vector<DrawDisplayListOp*> mProjectedNodes; +}; // class RenderNode + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* RENDERNODE_H */ diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp new file mode 100644 index 0000000..99de1fc --- /dev/null +++ b/libs/hwui/RenderProperties.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you mPrimitiveFields.may not use this file except in compliance with the License. + * You mPrimitiveFields.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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "RenderProperties.h" + +#include <utils/Trace.h> + +#include <SkCanvas.h> +#include <SkMatrix.h> +#include <SkPath.h> +#include <SkPathOps.h> + +#include "Matrix.h" +#include "utils/MathUtils.h" + +namespace android { +namespace uirenderer { + +RenderProperties::PrimitiveFields::PrimitiveFields() + : mClipToBounds(true) + , mProjectBackwards(false) + , mProjectionReceiver(false) + , mAlpha(1) + , mHasOverlappingRendering(true) + , mElevation(0) + , mTranslationX(0), mTranslationY(0), mTranslationZ(0) + , mRotation(0), mRotationX(0), mRotationY(0) + , mScaleX(1), mScaleY(1) + , mPivotX(0), mPivotY(0) + , mLeft(0), mTop(0), mRight(0), mBottom(0) + , mWidth(0), mHeight(0) + , mPivotExplicitlySet(false) + , mMatrixOrPivotDirty(false) + , mCaching(false) { +} + +RenderProperties::ComputedFields::ComputedFields() + : mTransformMatrix(NULL) + , mClipPath(NULL) + , mClipPathOp(SkRegion::kIntersect_Op) { +} + +RenderProperties::ComputedFields::~ComputedFields() { + delete mTransformMatrix; + delete mClipPath; +} + +RenderProperties::RenderProperties() + : mStaticMatrix(NULL) + , mAnimationMatrix(NULL) { +} + +RenderProperties::~RenderProperties() { + delete mStaticMatrix; + delete mAnimationMatrix; +} + +RenderProperties& RenderProperties::operator=(const RenderProperties& other) { + if (this != &other) { + mPrimitiveFields = other.mPrimitiveFields; + setStaticMatrix(other.getStaticMatrix()); + setAnimationMatrix(other.getAnimationMatrix()); + setCameraDistance(other.getCameraDistance()); + + // Update the computed clip path + updateClipPath(); + + // Force recalculation of the matrix, since other's dirty bit may be clear + mPrimitiveFields.mMatrixOrPivotDirty = true; + updateMatrix(); + } + return *this; +} + +void RenderProperties::debugOutputProperties(const int level) const { + if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) { + ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mPrimitiveFields.mLeft, mPrimitiveFields.mTop); + } + if (mStaticMatrix) { + ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING, + level * 2, "", mStaticMatrix, SK_MATRIX_ARGS(mStaticMatrix)); + } + if (mAnimationMatrix) { + ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING, + level * 2, "", mAnimationMatrix, SK_MATRIX_ARGS(mAnimationMatrix)); + } + if (hasTransformMatrix()) { + if (isTransformTranslateOnly()) { + ALOGD("%*sTranslate %.2f, %.2f, %.2f", + level * 2, "", getTranslationX(), getTranslationY(), getZ()); + } else { + ALOGD("%*sConcatMatrix %p: " SK_MATRIX_STRING, + level * 2, "", mComputedFields.mTransformMatrix, SK_MATRIX_ARGS(mComputedFields.mTransformMatrix)); + } + } + + bool clipToBoundsNeeded = mPrimitiveFields.mCaching ? false : mPrimitiveFields.mClipToBounds; + if (mPrimitiveFields.mAlpha < 1) { + if (mPrimitiveFields.mCaching) { + ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha); + } else if (!mPrimitiveFields.mHasOverlappingRendering) { + ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha); + } else { + int flags = SkCanvas::kHasAlphaLayer_SaveFlag; + if (clipToBoundsNeeded) { + flags |= SkCanvas::kClipToLayer_SaveFlag; + clipToBoundsNeeded = false; // clipping done by save layer + } + ALOGD("%*sSaveLayerAlpha %d, %d, %d, %d, %d, 0x%x", level * 2, "", + 0, 0, getWidth(), getHeight(), + (int)(mPrimitiveFields.mAlpha * 255), flags); + } + } + if (clipToBoundsNeeded) { + ALOGD("%*sClipRect %d, %d, %d, %d", level * 2, "", + 0, 0, getWidth(), getHeight()); + } +} + +void RenderProperties::updateMatrix() { + if (mPrimitiveFields.mMatrixOrPivotDirty) { + if (!mComputedFields.mTransformMatrix) { + // only allocate a mPrimitiveFields.matrix if we have a complex transform + mComputedFields.mTransformMatrix = new SkMatrix(); + } + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mPivotX = mPrimitiveFields.mWidth / 2.0f; + mPrimitiveFields.mPivotY = mPrimitiveFields.mHeight / 2.0f; + } + SkMatrix* transform = mComputedFields.mTransformMatrix; + transform->reset(); + if (MathUtils::isZero(getRotationX()) && MathUtils::isZero(getRotationY())) { + transform->setTranslate(getTranslationX(), getTranslationY()); + transform->preRotate(getRotation(), getPivotX(), getPivotY()); + transform->preScale(getScaleX(), getScaleY(), getPivotX(), getPivotY()); + } else { + SkMatrix transform3D; + mComputedFields.mTransformCamera.save(); + transform->preScale(getScaleX(), getScaleY(), getPivotX(), getPivotY()); + mComputedFields.mTransformCamera.rotateX(mPrimitiveFields.mRotationX); + mComputedFields.mTransformCamera.rotateY(mPrimitiveFields.mRotationY); + mComputedFields.mTransformCamera.rotateZ(-mPrimitiveFields.mRotation); + mComputedFields.mTransformCamera.getMatrix(&transform3D); + transform3D.preTranslate(-getPivotX(), -getPivotY()); + transform3D.postTranslate(getPivotX() + getTranslationX(), + getPivotY() + getTranslationY()); + transform->postConcat(transform3D); + mComputedFields.mTransformCamera.restore(); + } + mPrimitiveFields.mMatrixOrPivotDirty = false; + } +} + +void RenderProperties::updateClipPath() { + const SkPath* outlineClipPath = mPrimitiveFields.mOutline.willClip() + ? mPrimitiveFields.mOutline.getPath() : NULL; + const SkPath* revealClipPath = mPrimitiveFields.mRevealClip.getPath(); + + if (!outlineClipPath && !revealClipPath) { + // mComputedFields.mClipPath doesn't need to be updated, since it won't be used + return; + } + + if (mComputedFields.mClipPath == NULL) { + mComputedFields.mClipPath = new SkPath(); + } + SkPath* clipPath = mComputedFields.mClipPath; + mComputedFields.mClipPathOp = SkRegion::kIntersect_Op; + + if (outlineClipPath && revealClipPath) { + SkPathOp op = kIntersect_PathOp; + if (mPrimitiveFields.mRevealClip.isInverseClip()) { + op = kDifference_PathOp; // apply difference step in the Op below, instead of draw time + } + + Op(*outlineClipPath, *revealClipPath, op, clipPath); + } else if (outlineClipPath) { + *clipPath = *outlineClipPath; + } else { + *clipPath = *revealClipPath; + if (mPrimitiveFields.mRevealClip.isInverseClip()) { + // apply difference step at draw time + mComputedFields.mClipPathOp = SkRegion::kDifference_Op; + } + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h new file mode 100644 index 0000000..6fc8bce --- /dev/null +++ b/libs/hwui/RenderProperties.h @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2014 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 RENDERNODEPROPERTIES_H +#define RENDERNODEPROPERTIES_H + +#include <algorithm> +#include <stddef.h> +#include <vector> +#include <cutils/compiler.h> +#include <androidfw/ResourceTypes.h> + +#include <SkCamera.h> +#include <SkMatrix.h> +#include <SkRegion.h> + +#include "Animator.h" +#include "Rect.h" +#include "RevealClip.h" +#include "Outline.h" + +class SkBitmap; +class SkPaint; + +namespace android { +namespace uirenderer { + +class Matrix4; +class RenderNode; + +/* + * Data structure that holds the properties for a RenderNode + */ +class RenderProperties { +public: + RenderProperties(); + virtual ~RenderProperties(); + + RenderProperties& operator=(const RenderProperties& other); + + void setClipToBounds(bool clipToBounds) { + mPrimitiveFields.mClipToBounds = clipToBounds; + } + + void setProjectBackwards(bool shouldProject) { + mPrimitiveFields.mProjectBackwards = shouldProject; + } + + void setProjectionReceiver(bool shouldRecieve) { + mPrimitiveFields.mProjectionReceiver = shouldRecieve; + } + + bool isProjectionReceiver() const { + return mPrimitiveFields.mProjectionReceiver; + } + + void setStaticMatrix(const SkMatrix* matrix) { + delete mStaticMatrix; + if (matrix) { + mStaticMatrix = new SkMatrix(*matrix); + } else { + mStaticMatrix = NULL; + } + } + + // Can return NULL + const SkMatrix* getStaticMatrix() const { + return mStaticMatrix; + } + + void setAnimationMatrix(const SkMatrix* matrix) { + delete mAnimationMatrix; + if (matrix) { + mAnimationMatrix = new SkMatrix(*matrix); + } else { + mAnimationMatrix = NULL; + } + } + + void setAlpha(float alpha) { + alpha = fminf(1.0f, fmaxf(0.0f, alpha)); + if (alpha != mPrimitiveFields.mAlpha) { + mPrimitiveFields.mAlpha = alpha; + } + } + + float getAlpha() const { + return mPrimitiveFields.mAlpha; + } + + void setHasOverlappingRendering(bool hasOverlappingRendering) { + mPrimitiveFields.mHasOverlappingRendering = hasOverlappingRendering; + } + + bool hasOverlappingRendering() const { + return mPrimitiveFields.mHasOverlappingRendering; + } + + void setElevation(float elevation) { + if (elevation != mPrimitiveFields.mElevation) { + mPrimitiveFields.mElevation = elevation; + // mMatrixOrPivotDirty not set, since matrix doesn't respect Z + } + } + + float getElevation() const { + return mPrimitiveFields.mElevation; + } + + void setTranslationX(float translationX) { + if (translationX != mPrimitiveFields.mTranslationX) { + mPrimitiveFields.mTranslationX = translationX; + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + + float getTranslationX() const { + return mPrimitiveFields.mTranslationX; + } + + void setTranslationY(float translationY) { + if (translationY != mPrimitiveFields.mTranslationY) { + mPrimitiveFields.mTranslationY = translationY; + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + + float getTranslationY() const { + return mPrimitiveFields.mTranslationY; + } + + void setTranslationZ(float translationZ) { + if (translationZ != mPrimitiveFields.mTranslationZ) { + mPrimitiveFields.mTranslationZ = translationZ; + // mMatrixOrPivotDirty not set, since matrix doesn't respect Z + } + } + + float getTranslationZ() const { + return mPrimitiveFields.mTranslationZ; + } + + // Animation helper + void setX(float value) { + setTranslationX(value - getLeft()); + } + + // Animation helper + float getX() const { + return getLeft() + getTranslationX(); + } + + // Animation helper + void setY(float value) { + setTranslationY(value - getTop()); + } + + // Animation helper + float getY() const { + return getTop() + getTranslationY(); + } + + // Animation helper + void setZ(float value) { + setTranslationZ(value - getElevation()); + } + + float getZ() const { + return getElevation() + getTranslationZ(); + } + + void setRotation(float rotation) { + if (rotation != mPrimitiveFields.mRotation) { + mPrimitiveFields.mRotation = rotation; + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + + float getRotation() const { + return mPrimitiveFields.mRotation; + } + + void setRotationX(float rotationX) { + if (rotationX != mPrimitiveFields.mRotationX) { + mPrimitiveFields.mRotationX = rotationX; + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + + float getRotationX() const { + return mPrimitiveFields.mRotationX; + } + + void setRotationY(float rotationY) { + if (rotationY != mPrimitiveFields.mRotationY) { + mPrimitiveFields.mRotationY = rotationY; + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + + float getRotationY() const { + return mPrimitiveFields.mRotationY; + } + + void setScaleX(float scaleX) { + if (scaleX != mPrimitiveFields.mScaleX) { + mPrimitiveFields.mScaleX = scaleX; + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + + float getScaleX() const { + return mPrimitiveFields.mScaleX; + } + + void setScaleY(float scaleY) { + if (scaleY != mPrimitiveFields.mScaleY) { + mPrimitiveFields.mScaleY = scaleY; + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + + float getScaleY() const { + return mPrimitiveFields.mScaleY; + } + + void setPivotX(float pivotX) { + mPrimitiveFields.mPivotX = pivotX; + mPrimitiveFields.mMatrixOrPivotDirty = true; + mPrimitiveFields.mPivotExplicitlySet = true; + } + + /* Note that getPivotX and getPivotY are adjusted by updateMatrix(), + * so the value returned mPrimitiveFields.may be stale if the RenderProperties has been + * mPrimitiveFields.modified since the last call to updateMatrix() + */ + float getPivotX() const { + return mPrimitiveFields.mPivotX; + } + + void setPivotY(float pivotY) { + mPrimitiveFields.mPivotY = pivotY; + mPrimitiveFields.mMatrixOrPivotDirty = true; + mPrimitiveFields.mPivotExplicitlySet = true; + } + + float getPivotY() const { + return mPrimitiveFields.mPivotY; + } + + bool isPivotExplicitlySet() const { + return mPrimitiveFields.mPivotExplicitlySet; + } + + void setCameraDistance(float distance) { + if (distance != getCameraDistance()) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance); + } + } + + float getCameraDistance() const { + // TODO: update getCameraLocationZ() to be const + return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ(); + } + + void setLeft(int left) { + if (left != mPrimitiveFields.mLeft) { + mPrimitiveFields.mLeft = left; + mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + } + + float getLeft() const { + return mPrimitiveFields.mLeft; + } + + void setTop(int top) { + if (top != mPrimitiveFields.mTop) { + mPrimitiveFields.mTop = top; + mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + } + + float getTop() const { + return mPrimitiveFields.mTop; + } + + void setRight(int right) { + if (right != mPrimitiveFields.mRight) { + mPrimitiveFields.mRight = right; + mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + } + + float getRight() const { + return mPrimitiveFields.mRight; + } + + void setBottom(int bottom) { + if (bottom != mPrimitiveFields.mBottom) { + mPrimitiveFields.mBottom = bottom; + mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + } + + float getBottom() const { + return mPrimitiveFields.mBottom; + } + + void setLeftTop(int left, int top) { + if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop) { + mPrimitiveFields.mLeft = left; + mPrimitiveFields.mTop = top; + mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; + mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + } + + void setLeftTopRightBottom(int left, int top, int right, int bottom) { + if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop + || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) { + mPrimitiveFields.mLeft = left; + mPrimitiveFields.mTop = top; + mPrimitiveFields.mRight = right; + mPrimitiveFields.mBottom = bottom; + mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; + mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + } + + void offsetLeftRight(float offset) { + if (offset != 0) { + mPrimitiveFields.mLeft += offset; + mPrimitiveFields.mRight += offset; + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + } + + void offsetTopBottom(float offset) { + if (offset != 0) { + mPrimitiveFields.mTop += offset; + mPrimitiveFields.mBottom += offset; + if (!mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + } + } + } + + void setCaching(bool caching) { + mPrimitiveFields.mCaching = caching; + } + + int getWidth() const { + return mPrimitiveFields.mWidth; + } + + int getHeight() const { + return mPrimitiveFields.mHeight; + } + + const SkMatrix* getAnimationMatrix() const { + return mAnimationMatrix; + } + + bool hasTransformMatrix() const { + return getTransformMatrix() && !getTransformMatrix()->isIdentity(); + } + + // May only call this if hasTransformMatrix() is true + bool isTransformTranslateOnly() const { + return getTransformMatrix()->getType() == SkMatrix::kTranslate_Mask; + } + + const SkMatrix* getTransformMatrix() const { + LOG_ALWAYS_FATAL_IF(mPrimitiveFields.mMatrixOrPivotDirty, "Cannot get a dirty matrix!"); + return mComputedFields.mTransformMatrix; + } + + bool getCaching() const { + return mPrimitiveFields.mCaching; + } + + bool getClipToBounds() const { + return mPrimitiveFields.mClipToBounds; + } + + bool getHasOverlappingRendering() const { + return mPrimitiveFields.mHasOverlappingRendering; + } + + const Outline& getOutline() const { + return mPrimitiveFields.mOutline; + } + + const RevealClip& getRevealClip() const { + return mPrimitiveFields.mRevealClip; + } + + bool getProjectBackwards() const { + return mPrimitiveFields.mProjectBackwards; + } + + void debugOutputProperties(const int level) const; + + ANDROID_API void updateMatrix(); + + ANDROID_API void updateClipPath(); + + // signals that mComputedFields.mClipPath is up to date, and should be used for clipping + bool hasClippingPath() const { + return mPrimitiveFields.mOutline.willClip() || mPrimitiveFields.mRevealClip.willClip(); + } + + const SkPath* getClippingPath() const { + return hasClippingPath() ? mComputedFields.mClipPath : NULL; + } + + SkRegion::Op getClippingPathOp() const { + return mComputedFields.mClipPathOp; + } + + Outline& mutableOutline() { + return mPrimitiveFields.mOutline; + } + + RevealClip& mutableRevealClip() { + return mPrimitiveFields.mRevealClip; + } + +private: + + // Rendering properties + struct PrimitiveFields { + PrimitiveFields(); + + Outline mOutline; + RevealClip mRevealClip; + bool mClipToBounds; + bool mProjectBackwards; + bool mProjectionReceiver; + float mAlpha; + bool mHasOverlappingRendering; + float mElevation; + float mTranslationX, mTranslationY, mTranslationZ; + float mRotation, mRotationX, mRotationY; + float mScaleX, mScaleY; + float mPivotX, mPivotY; + int mLeft, mTop, mRight, mBottom; + int mWidth, mHeight; + bool mPivotExplicitlySet; + bool mMatrixOrPivotDirty; + bool mCaching; + } mPrimitiveFields; + + SkMatrix* mStaticMatrix; + SkMatrix* mAnimationMatrix; + + /** + * These fields are all generated from other properties and are not set directly. + */ + struct ComputedFields { + ComputedFields(); + ~ComputedFields(); + + /** + * Stores the total transformation of the DisplayList based upon its scalar + * translate/rotate/scale properties. + * + * In the common translation-only case, the matrix isn't necessarily allocated, + * and the mTranslation properties are used directly. + */ + SkMatrix* mTransformMatrix; + + Sk3DView mTransformCamera; + SkPath* mClipPath; // TODO: remove this, create new ops for efficient/special case clipping + SkRegion::Op mClipPathOp; + } mComputedFields; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* RENDERNODEPROPERTIES_H */ diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h new file mode 100644 index 0000000..57db816 --- /dev/null +++ b/libs/hwui/Renderer.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2013 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 ANDROID_HWUI_RENDERER_H +#define ANDROID_HWUI_RENDERER_H + +#include <SkRegion.h> + +#include <utils/String8.h> + +#include "AssetAtlas.h" +#include "SkPaint.h" + +namespace android { + +class Functor; +struct Res_png_9patch; + +namespace uirenderer { + +class RenderNode; +class Layer; +class Matrix4; +class SkiaColorFilter; +class SkiaShader; +class Patch; + +enum DrawOpMode { + kDrawOpMode_Immediate, + kDrawOpMode_Defer, + kDrawOpMode_Flush +}; + +/** + * Hwui's abstract version of Canvas. + * + * Provides methods for frame state operations, as well as the SkCanvas style transform/clip state, + * and varied drawing operations. + * + * Should at some point interact with native SkCanvas. + */ +class ANDROID_API Renderer { +public: + virtual ~Renderer() {} + + /** + * Indicates whether this renderer is recording drawing commands for later playback. + * If this method returns true, the drawing commands are deferred. + */ + virtual bool isRecording() const { + return false; + } + + /** + * Safely retrieves the mode from the specified xfermode. If the specified + * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode. + */ + static inline SkXfermode::Mode getXfermode(SkXfermode* mode) { + SkXfermode::Mode resultMode; + if (!SkXfermode::AsMode(mode, &resultMode)) { + resultMode = SkXfermode::kSrcOver_Mode; + } + return resultMode; + } + +// ---------------------------------------------------------------------------- +// Frame state operations +// ---------------------------------------------------------------------------- + /** + * Sets the dimension of the underlying drawing surface. This method must + * be called at least once every time the drawing surface changes size. + * + * @param width The width in pixels of the underlysing surface + * @param height The height in pixels of the underlysing surface + */ + virtual void setViewport(int width, int height) = 0; + + /** + * Prepares the renderer to draw a frame. This method must be invoked + * at the beginning of each frame. When this method is invoked, the + * entire drawing surface is assumed to be redrawn. + * + * @param opaque If true, the target surface is considered opaque + * and will not be cleared. If false, the target surface + * will be cleared + */ + virtual status_t prepare(bool opaque) = 0; + + /** + * Prepares the renderer to draw a frame. This method must be invoked + * at the beginning of each frame. Only the specified rectangle of the + * frame is assumed to be dirty. A clip will automatically be set to + * the specified rectangle. + * + * @param left The left coordinate of the dirty rectangle + * @param top The top coordinate of the dirty rectangle + * @param right The right coordinate of the dirty rectangle + * @param bottom The bottom coordinate of the dirty rectangle + * @param opaque If true, the target surface is considered opaque + * and will not be cleared. If false, the target surface + * will be cleared in the specified dirty rectangle + */ + virtual status_t prepareDirty(float left, float top, float right, float bottom, + bool opaque) = 0; + + /** + * Indicates the end of a frame. This method must be invoked whenever + * the caller is done rendering a frame. + */ + virtual void finish() = 0; + + /** + * This method must be invoked before handing control over to a draw functor. + * See callDrawGLFunction() for instance. + * + * This command must not be recorded inside display lists. + */ + virtual void interrupt() = 0; + + /** + * This method must be invoked after getting control back from a draw functor. + * + * This command must not be recorded inside display lists. + */ + virtual void resume() = 0; + +// ---------------------------------------------------------------------------- +// Canvas state operations +// ---------------------------------------------------------------------------- + // Save (layer) + virtual int getSaveCount() const = 0; + virtual int save(int flags) = 0; + virtual void restore() = 0; + virtual void restoreToCount(int saveCount) = 0; + + virtual int saveLayer(float left, float top, float right, float bottom, + const SkPaint* paint, int flags) = 0; + + int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags) { + SkPaint paint; + paint.setAlpha(alpha); + return saveLayer(left, top, right, bottom, &paint, flags); + } + + // Matrix + virtual void getMatrix(SkMatrix* outMatrix) const = 0; + virtual void translate(float dx, float dy, float dz = 0.0f) = 0; + virtual void rotate(float degrees) = 0; + virtual void scale(float sx, float sy) = 0; + virtual void skew(float sx, float sy) = 0; + + virtual void setMatrix(const SkMatrix* matrix) = 0; + virtual void concatMatrix(const SkMatrix* matrix) = 0; + + // clip + virtual const Rect& getLocalClipBounds() const = 0; + virtual bool quickRejectConservative(float left, float top, + float right, float bottom) const = 0; + virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0; + virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0; + virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0; + + // Misc - should be implemented with SkPaint inspection + virtual void resetShader() = 0; + virtual void setupShader(SkiaShader* shader) = 0; + + virtual void resetPaintFilter() = 0; + virtual void setupPaintFilter(int clearBits, int setBits) = 0; + +// ---------------------------------------------------------------------------- +// Canvas draw operations +// ---------------------------------------------------------------------------- + virtual status_t drawColor(int color, SkXfermode::Mode mode) = 0; + + // Bitmap-based + virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) = 0; + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) = 0; + virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint) = 0; + virtual status_t drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) = 0; + virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) = 0; + virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) = 0; + + // Shapes + virtual status_t drawRect(float left, float top, float right, float bottom, + const SkPaint* paint) = 0; + virtual status_t drawRects(const float* rects, int count, const SkPaint* paint) = 0; + virtual status_t drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, const SkPaint* paint) = 0; + virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint) = 0; + virtual status_t drawOval(float left, float top, float right, float bottom, + const SkPaint* paint) = 0; + virtual status_t drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) = 0; + virtual status_t drawPath(const SkPath* path, const SkPaint* paint) = 0; + virtual status_t drawLines(const float* points, int count, const SkPaint* paint) = 0; + virtual status_t drawPoints(const float* points, int count, const SkPaint* paint) = 0; + + // Text + virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, + DrawOpMode drawOpMode = kDrawOpMode_Immediate) = 0; + virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, + float hOffset, float vOffset, const SkPaint* paint) = 0; + virtual status_t drawPosText(const char* text, int bytesCount, int count, + const float* positions, const SkPaint* paint) = 0; + +// ---------------------------------------------------------------------------- +// Canvas draw operations - special +// ---------------------------------------------------------------------------- + virtual status_t drawLayer(Layer* layer, float x, float y) = 0; + virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, + int32_t replayFlags) = 0; + + // TODO: rename for consistency + virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0; +}; // class Renderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_RENDERER_H diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 77292bf..13a3e8e 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -40,7 +40,7 @@ void ResourceCache::logCache() { ResourceCache::ResourceCache() { Mutex::Autolock _l(mLock); - mCache = new KeyedVector<void*, ResourceReference*>(); + mCache = new KeyedVector<const void*, ResourceReference*>(); } ResourceCache::~ResourceCache() { @@ -61,13 +61,13 @@ void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) incrementRefcountLocked(resource, resourceType); } -void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) { +void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) { bitmapResource->pixelRef()->globalRef(); SkSafeRef(bitmapResource->getColorTable()); incrementRefcount((void*) bitmapResource, kBitmap); } -void ResourceCache::incrementRefcount(SkPath* pathResource) { +void ResourceCache::incrementRefcount(const SkPath* pathResource) { incrementRefcount((void*) pathResource, kPath); } @@ -76,12 +76,7 @@ void ResourceCache::incrementRefcount(SkiaShader* shaderResource) { incrementRefcount((void*) shaderResource, kShader); } -void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) { - SkSafeRef(filterResource->getSkColorFilter()); - incrementRefcount((void*) filterResource, kColorFilter); -} - -void ResourceCache::incrementRefcount(Res_png_9patch* patchResource) { +void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { incrementRefcount((void*) patchResource, kNinePatch); } @@ -99,13 +94,13 @@ void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourc ref->refCount++; } -void ResourceCache::incrementRefcountLocked(SkBitmap* bitmapResource) { +void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) { bitmapResource->pixelRef()->globalRef(); SkSafeRef(bitmapResource->getColorTable()); incrementRefcountLocked((void*) bitmapResource, kBitmap); } -void ResourceCache::incrementRefcountLocked(SkPath* pathResource) { +void ResourceCache::incrementRefcountLocked(const SkPath* pathResource) { incrementRefcountLocked((void*) pathResource, kPath); } @@ -114,12 +109,7 @@ void ResourceCache::incrementRefcountLocked(SkiaShader* shaderResource) { incrementRefcountLocked((void*) shaderResource, kShader); } -void ResourceCache::incrementRefcountLocked(SkiaColorFilter* filterResource) { - SkSafeRef(filterResource->getSkColorFilter()); - incrementRefcountLocked((void*) filterResource, kColorFilter); -} - -void ResourceCache::incrementRefcountLocked(Res_png_9patch* patchResource) { +void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) { incrementRefcountLocked((void*) patchResource, kNinePatch); } @@ -132,13 +122,13 @@ void ResourceCache::decrementRefcount(void* resource) { decrementRefcountLocked(resource); } -void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) { +void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) { bitmapResource->pixelRef()->globalUnref(); SkSafeUnref(bitmapResource->getColorTable()); decrementRefcount((void*) bitmapResource); } -void ResourceCache::decrementRefcount(SkPath* pathResource) { +void ResourceCache::decrementRefcount(const SkPath* pathResource) { decrementRefcount((void*) pathResource); } @@ -147,12 +137,7 @@ void ResourceCache::decrementRefcount(SkiaShader* shaderResource) { decrementRefcount((void*) shaderResource); } -void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) { - SkSafeUnref(filterResource->getSkColorFilter()); - decrementRefcount((void*) filterResource); -} - -void ResourceCache::decrementRefcount(Res_png_9patch* patchResource) { +void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { decrementRefcount((void*) patchResource); } @@ -173,13 +158,13 @@ void ResourceCache::decrementRefcountLocked(void* resource) { } } -void ResourceCache::decrementRefcountLocked(SkBitmap* bitmapResource) { +void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) { bitmapResource->pixelRef()->globalUnref(); SkSafeUnref(bitmapResource->getColorTable()); decrementRefcountLocked((void*) bitmapResource); } -void ResourceCache::decrementRefcountLocked(SkPath* pathResource) { +void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) { decrementRefcountLocked((void*) pathResource); } @@ -188,12 +173,7 @@ void ResourceCache::decrementRefcountLocked(SkiaShader* shaderResource) { decrementRefcountLocked((void*) shaderResource); } -void ResourceCache::decrementRefcountLocked(SkiaColorFilter* filterResource) { - SkSafeUnref(filterResource->getSkColorFilter()); - decrementRefcountLocked((void*) filterResource); -} - -void ResourceCache::decrementRefcountLocked(Res_png_9patch* patchResource) { +void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) { decrementRefcountLocked((void*) patchResource); } @@ -224,12 +204,12 @@ void ResourceCache::destructorLocked(SkPath* resource) { } } -void ResourceCache::destructor(SkBitmap* resource) { +void ResourceCache::destructor(const SkBitmap* resource) { Mutex::Autolock _l(mLock); destructorLocked(resource); } -void ResourceCache::destructorLocked(SkBitmap* resource) { +void ResourceCache::destructorLocked(const SkBitmap* resource) { ssize_t index = mCache->indexOfKey(resource); ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; if (ref == NULL) { @@ -266,25 +246,6 @@ void ResourceCache::destructorLocked(SkiaShader* resource) { } } -void ResourceCache::destructor(SkiaColorFilter* resource) { - Mutex::Autolock _l(mLock); - destructorLocked(resource); -} - -void ResourceCache::destructorLocked(SkiaColorFilter* resource) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; - if (ref == NULL) { - // If we're not tracking this resource, just delete it - delete resource; - return; - } - ref->destroyed = true; - if (ref->refCount == 0) { - deleteResourceReferenceLocked(resource, ref); - } -} - void ResourceCache::destructor(Res_png_9patch* resource) { Mutex::Autolock _l(mLock); destructorLocked(resource); @@ -348,7 +309,7 @@ bool ResourceCache::recycleLocked(SkBitmap* resource) { * This method should only be called while the mLock mutex is held (that mutex is grabbed * by the various destructor() and recycle() methods which call this method). */ -void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceReference* ref) { +void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) { if (ref->recycled && ref->resourceType == kBitmap) { ((SkBitmap*) resource)->setPixels(NULL, NULL); } @@ -377,11 +338,6 @@ void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceRefere delete shader; } break; - case kColorFilter: { - SkiaColorFilter* filter = (SkiaColorFilter*) resource; - delete filter; - } - break; case kNinePatch: { if (Caches::hasInstance()) { Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource); diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h index ea0c1b5..4097ba4 100644 --- a/libs/hwui/ResourceCache.h +++ b/libs/hwui/ResourceCache.h @@ -20,7 +20,6 @@ #include <cutils/compiler.h> #include <SkBitmap.h> -#include <SkiaColorFilter.h> #include <SkiaShader.h> #include <utils/KeyedVector.h> @@ -38,7 +37,6 @@ namespace uirenderer { enum ResourceType { kBitmap, kShader, - kColorFilter, kNinePatch, kPath, kLayer @@ -70,51 +68,45 @@ public: void lock(); void unlock(); - void incrementRefcount(SkPath* resource); - void incrementRefcount(SkBitmap* resource); + void incrementRefcount(const SkPath* resource); + void incrementRefcount(const SkBitmap* resource); void incrementRefcount(SkiaShader* resource); - void incrementRefcount(SkiaColorFilter* resource); - void incrementRefcount(Res_png_9patch* resource); + void incrementRefcount(const Res_png_9patch* resource); void incrementRefcount(Layer* resource); - void incrementRefcountLocked(SkPath* resource); - void incrementRefcountLocked(SkBitmap* resource); + void incrementRefcountLocked(const SkPath* resource); + void incrementRefcountLocked(const SkBitmap* resource); void incrementRefcountLocked(SkiaShader* resource); - void incrementRefcountLocked(SkiaColorFilter* resource); - void incrementRefcountLocked(Res_png_9patch* resource); + void incrementRefcountLocked(const Res_png_9patch* resource); void incrementRefcountLocked(Layer* resource); - void decrementRefcount(SkBitmap* resource); - void decrementRefcount(SkPath* resource); + void decrementRefcount(const SkBitmap* resource); + void decrementRefcount(const SkPath* resource); void decrementRefcount(SkiaShader* resource); - void decrementRefcount(SkiaColorFilter* resource); - void decrementRefcount(Res_png_9patch* resource); + void decrementRefcount(const Res_png_9patch* resource); void decrementRefcount(Layer* resource); - void decrementRefcountLocked(SkBitmap* resource); - void decrementRefcountLocked(SkPath* resource); + void decrementRefcountLocked(const SkBitmap* resource); + void decrementRefcountLocked(const SkPath* resource); void decrementRefcountLocked(SkiaShader* resource); - void decrementRefcountLocked(SkiaColorFilter* resource); - void decrementRefcountLocked(Res_png_9patch* resource); + void decrementRefcountLocked(const Res_png_9patch* resource); void decrementRefcountLocked(Layer* resource); void destructor(SkPath* resource); - void destructor(SkBitmap* resource); + void destructor(const SkBitmap* resource); void destructor(SkiaShader* resource); - void destructor(SkiaColorFilter* resource); void destructor(Res_png_9patch* resource); void destructorLocked(SkPath* resource); - void destructorLocked(SkBitmap* resource); + void destructorLocked(const SkBitmap* resource); void destructorLocked(SkiaShader* resource); - void destructorLocked(SkiaColorFilter* resource); void destructorLocked(Res_png_9patch* resource); bool recycle(SkBitmap* resource); bool recycleLocked(SkBitmap* resource); private: - void deleteResourceReferenceLocked(void* resource, ResourceReference* ref); + void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref); void incrementRefcount(void* resource, ResourceType resourceType); void incrementRefcountLocked(void* resource, ResourceType resourceType); @@ -131,7 +123,7 @@ private: */ mutable Mutex mLock; - KeyedVector<void*, ResourceReference*>* mCache; + KeyedVector<const void*, ResourceReference*>* mCache; }; }; // namespace uirenderer diff --git a/libs/hwui/RevealClip.h b/libs/hwui/RevealClip.h new file mode 100644 index 0000000..ece8498 --- /dev/null +++ b/libs/hwui/RevealClip.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2014 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 REVEALCLIP_H +#define REVEALCLIP_H + +#include <SkPath.h> + +#include "Rect.h" + +namespace android { +namespace uirenderer { + +class RevealClip { +public: + RevealClip() + : mShouldClip(false) + , mInverseClip(false) + , mX(0) + , mY(0) + , mRadius(0) {} + + void set(bool shouldClip, bool inverseClip, float x, float y, float radius) { + mShouldClip = shouldClip; + mInverseClip = inverseClip; + mX = x; + mY = y; + mRadius = radius; + + mPath.rewind(); + if (mShouldClip) { + mPath.addCircle(x, y, radius); + } + } + + bool hasConvexClip() const { + return mShouldClip && !mInverseClip; + } + + bool isInverseClip() const { + return mInverseClip; + } + + bool willClip() const { + return mShouldClip; + } + + const SkPath* getPath() const { + if (!mShouldClip) return NULL; + + return &mPath; + } + +private: + bool mShouldClip; + bool mInverseClip; + float mX; + float mY; + float mRadius; + SkPath mPath; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* REVEALCLIP_H */ diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp new file mode 100644 index 0000000..be49aed --- /dev/null +++ b/libs/hwui/ShadowTessellator.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "OpenGLRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include <math.h> +#include <utils/Log.h> +#include <utils/Trace.h> + +#include "AmbientShadow.h" +#include "Caches.h" +#include "ShadowTessellator.h" +#include "SpotShadow.h" + +namespace android { +namespace uirenderer { + +template<typename T> +static inline T max(T a, T b) { + return a > b ? a : b; +} + +VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, + const Vector3& centroid3d, const Rect& casterBounds, + const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) { + ATRACE_CALL(); + + // A bunch of parameters to tweak the shadow. + // TODO: Allow some of these changable by debug settings or APIs. + float heightFactor = 1.0f / 128; + const float geomFactor = 64; + + Caches& caches = Caches::getInstance(); + if (CC_UNLIKELY(caches.propertyAmbientRatio > 0.0f)) { + heightFactor *= caches.propertyAmbientRatio; + } + + Rect ambientShadowBounds(casterBounds); + ambientShadowBounds.outset(maxZ * geomFactor * heightFactor); + + if (!localClip.intersects(ambientShadowBounds)) { +#if DEBUG_SHADOW + ALOGD("Ambient shadow is out of clip rect!"); +#endif + return kVertexBufferMode_OnePolyRingShadow; + } + + return AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon, + casterVertexCount, centroid3d, heightFactor, geomFactor, + shadowVertexBuffer); + +} + +VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, + const mat4& receiverTransform, + int screenWidth, int screenHeight, const Rect& casterBounds, + const Rect& localClip, VertexBuffer& shadowVertexBuffer) { + ATRACE_CALL(); + + Caches& caches = Caches::getInstance(); + + // A bunch of parameters to tweak the shadow. + // TODO: Allow some of these changable by debug settings or APIs. + int maximal = max(screenWidth, screenHeight); + Vector3 lightCenter(screenWidth * 0.5f, 0, maximal); + + if (CC_UNLIKELY(caches.propertyLightPosY > 0)) { + lightCenter.y = - caches.propertyLightPosY; // negated since this shifts up + } + if (CC_UNLIKELY(caches.propertyLightPosZ > 0)) { + lightCenter.z = caches.propertyLightPosZ; + } + + +#if DEBUG_SHADOW + ALOGD("light center %f %f %f", lightCenter.x, lightCenter.y, lightCenter.z); +#endif + + // light position (because it's in local space) needs to compensate for receiver transform + // TODO: should apply to light orientation, not just position + Matrix4 reverseReceiverTransform; + reverseReceiverTransform.loadInverse(receiverTransform); + reverseReceiverTransform.mapPoint3d(lightCenter); + + float lightSize = maximal / 4; + const int lightVertexCount = 8; + + if (CC_UNLIKELY(caches.propertyLightDiameter > 0)) { + lightSize = caches.propertyLightDiameter; + } + + // Now light and caster are both in local space, we will check whether + // the shadow is within the clip area. + Rect lightRect = Rect(lightCenter.x - lightSize, lightCenter.y - lightSize, + lightCenter.x + lightSize, lightCenter.y + lightSize); + lightRect.unionWith(localClip); + if (!lightRect.intersects(casterBounds)) { +#if DEBUG_SHADOW + ALOGD("Spot shadow is out of clip rect!"); +#endif + return kVertexBufferMode_OnePolyRingShadow; + } + + VertexBufferMode mode = SpotShadow::createSpotShadow(isCasterOpaque, + casterPolygon, casterVertexCount, lightCenter, lightSize, + lightVertexCount, shadowVertexBuffer); + +#if DEBUG_SHADOW + if(shadowVertexBuffer.getVertexCount() <= 0) { + ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount()); + } +#endif + return mode; +} + +void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) { + int currentIndex = 0; + const int rays = SHADOW_RAY_COUNT; + // For the penumbra area. + for (int layer = 0; layer < 2; layer ++) { + int baseIndex = layer * rays; + for (int i = 0; i < rays; i++) { + shadowIndices[currentIndex++] = i + baseIndex; + shadowIndices[currentIndex++] = rays + i + baseIndex; + } + // To close the loop, back to the ray 0. + shadowIndices[currentIndex++] = 0 + baseIndex; + // Note this is the same as the first index of next layer loop. + shadowIndices[currentIndex++] = rays + baseIndex; + } + +#if DEBUG_SHADOW + if (currentIndex != MAX_SHADOW_INDEX_COUNT) { + ALOGW("vertex index count is wrong. current %d, expected %d", + currentIndex, MAX_SHADOW_INDEX_COUNT); + } + for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) { + ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]); + } +#endif +} + +/** + * Calculate the centroid of a 2d polygon. + * + * @param poly The polygon, which is represented in a Vector2 array. + * @param polyLength The length of the polygon in terms of number of vertices. + * @return the centroid of the polygon. + */ +Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { + double sumx = 0; + double sumy = 0; + int p1 = polyLength - 1; + double area = 0; + for (int p2 = 0; p2 < polyLength; p2++) { + double x1 = poly[p1].x; + double y1 = poly[p1].y; + double x2 = poly[p2].x; + double y2 = poly[p2].y; + double a = (x1 * y2 - x2 * y1); + sumx += (x1 + x2) * a; + sumy += (y1 + y2) * a; + area += a; + p1 = p2; + } + + Vector2 centroid = poly[0]; + if (area != 0) { + centroid = Vector2(sumx / (3 * area), sumy / (3 * area)); + } else { + ALOGW("Area is 0 while computing centroid!"); + } + return centroid; +} + +/** + * Test whether the polygon is order in clockwise. + * + * @param polygon the polygon as a Vector2 array + * @param len the number of points of the polygon + */ +bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) { + double sum = 0; + double p1x = polygon[len - 1].x; + double p1y = polygon[len - 1].y; + for (int i = 0; i < len; i++) { + + double p2x = polygon[i].x; + double p2y = polygon[i].y; + sum += p1x * p2y - p2x * p1y; + p1x = p2x; + p1y = p2y; + } + return sum < 0; +} + +bool ShadowTessellator::isClockwisePath(const SkPath& path) { + SkPath::Iter iter(path, false); + SkPoint pts[4]; + SkPath::Verb v; + + Vector<Vector2> arrayForDirection; + while (SkPath::kDone_Verb != (v = iter.next(pts))) { + switch (v) { + case SkPath::kMove_Verb: + arrayForDirection.add(Vector2(pts[0].x(), pts[0].y())); + break; + case SkPath::kLine_Verb: + arrayForDirection.add(Vector2(pts[1].x(), pts[1].y())); + break; + case SkPath::kQuad_Verb: + arrayForDirection.add(Vector2(pts[1].x(), pts[1].y())); + arrayForDirection.add(Vector2(pts[2].x(), pts[2].y())); + break; + case SkPath::kCubic_Verb: + arrayForDirection.add(Vector2(pts[1].x(), pts[1].y())); + arrayForDirection.add(Vector2(pts[2].x(), pts[2].y())); + arrayForDirection.add(Vector2(pts[3].x(), pts[3].y())); + break; + default: + break; + } + } + + return isClockwise(arrayForDirection.array(), arrayForDirection.size()); +} + +void ShadowTessellator::reverseVertexArray(Vertex* polygon, int len) { + int n = len / 2; + for (int i = 0; i < n; i++) { + Vertex tmp = polygon[i]; + int k = len - 1 - i; + polygon[i] = polygon[k]; + polygon[k] = tmp; + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h new file mode 100644 index 0000000..e5a3da1 --- /dev/null +++ b/libs/hwui/ShadowTessellator.h @@ -0,0 +1,109 @@ + +/* + * Copyright (C) 2013 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 ANDROID_HWUI_SHADOW_TESSELLATOR_H +#define ANDROID_HWUI_SHADOW_TESSELLATOR_H + +#include "Debug.h" +#include "Matrix.h" +#include "OpenGLRenderer.h" +#include "VertexBuffer.h" + +namespace android { +namespace uirenderer { + +// All SHADOW_* are used to define all the geometry property of shadows. +// Use a simplified example to illustrate the geometry setup here. +// Assuming we use 6 rays and only 1 layer, Then we will have 2 hexagons, which +// are 0 to 5 and 6 to 11. The area between them will be the penumbra area, and +// the area inside the 2nd hexagon is the umbra. +// Ambient shadow is using only 1 layer for opaque caster, otherwise, spot +// shadow and ambient shadow are using 2 layers. +// Triange strip indices for penumbra area: (0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11, 0, 6) +// 0 +// +// 5 6 1 +// 11 7 +// +// 10 8 +// 4 9 2 +// +// 3 + +// The total number of rays starting from the centroid of shadow area, in order +// to generate the shadow geometry. +#define SHADOW_RAY_COUNT 128 + +// The total number of all the vertices representing the shadow. +// For the case we only have 1 layer, then we will just fill only 2/3 of it. +#define SHADOW_VERTEX_COUNT (3 * SHADOW_RAY_COUNT) + +// The total number of indices used for drawing the shadow geometry as triangle strips. +// Depending on the mode we are drawing, we can have 1 layer or 2 layers. +// Therefore, we only build the longer index buffer. +#define TWO_POLY_RING_SHADOW_INDEX_COUNT (4 * (SHADOW_RAY_COUNT + 1)) +#define ONE_POLY_RING_SHADOW_INDEX_COUNT (2 * (SHADOW_RAY_COUNT + 1)) + +#define MAX_SHADOW_INDEX_COUNT TWO_POLY_RING_SHADOW_INDEX_COUNT + +#define SHADOW_MIN_CASTER_Z 0.001f + +#define MINIMAL_DELTA_THETA (M_PI / 180 / 1000) + +class ShadowTessellator { +public: + static VertexBufferMode tessellateAmbientShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, + const Vector3& centroid3d, const Rect& casterBounds, + const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer); + + static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, + const mat4& receiverTransform, + int screenWidth, int screenHeight, const Rect& casterBounds, + const Rect& localClip, VertexBuffer& shadowVertexBuffer); + + static void generateShadowIndices(uint16_t* shadowIndices); + + static Vector2 centroid2d(const Vector2* poly, int polyLength); + + static bool isClockwise(const Vector2* polygon, int len); + + /** + * Determine whether the path is clockwise, using the control points. + * + * TODO: Given the skia is using inverted Y coordinate, shadow system needs + * to convert to the same coordinate to avoid the extra reverse. + * + * @param path The path to be examined. + */ + static bool isClockwisePath(const SkPath &path); + + /** + * Reverse the vertex array. + * + * @param polygon The vertex array to be reversed. + * @param len The length of the vertex array. + */ + static void reverseVertexArray(Vertex* polygon, int len); + +}; // ShadowTessellator + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SHADOW_TESSELLATOR_H diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp deleted file mode 100644 index df918be..0000000 --- a/libs/hwui/SkiaColorFilter.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 "SkiaColorFilter.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Base color filter -/////////////////////////////////////////////////////////////////////////////// - -SkiaColorFilter::SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend): - mType(type), mBlend(blend), mSkFilter(skFilter) { -} - -SkiaColorFilter::~SkiaColorFilter() { -} - -/////////////////////////////////////////////////////////////////////////////// -// Color matrix filter -/////////////////////////////////////////////////////////////////////////////// - -SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter* skFilter, float* matrix, float* vector): - SkiaColorFilter(skFilter, kColorMatrix, true), mMatrix(matrix), mVector(vector) { - // Skia uses the range [0..255] for the addition vector, but we need - // the [0..1] range to apply the vector in GLSL - for (int i = 0; i < 4; i++) { - mVector[i] /= 255.0f; - } - - // TODO: We should be smarter about this - mBlend = true; -} - -SkiaColorMatrixFilter::~SkiaColorMatrixFilter() { - delete[] mMatrix; - delete[] mVector; -} - -void SkiaColorMatrixFilter::describe(ProgramDescription& description, - const Extensions& extensions) { - description.colorOp = ProgramDescription::kColorMatrix; -} - -void SkiaColorMatrixFilter::setupProgram(Program* program) { - glUniformMatrix4fv(program->getUniform("colorMatrix"), 1, GL_FALSE, &mMatrix[0]); - glUniform4fv(program->getUniform("colorMatrixVector"), 1, mVector); -} - -/////////////////////////////////////////////////////////////////////////////// -// Lighting color filter -/////////////////////////////////////////////////////////////////////////////// - -SkiaLightingFilter::SkiaLightingFilter(SkColorFilter* skFilter, int multiply, int add): - SkiaColorFilter(skFilter, kLighting, true) { - mMulR = ((multiply >> 16) & 0xFF) / 255.0f; - mMulG = ((multiply >> 8) & 0xFF) / 255.0f; - mMulB = ((multiply ) & 0xFF) / 255.0f; - - mAddR = ((add >> 16) & 0xFF) / 255.0f; - mAddG = ((add >> 8) & 0xFF) / 255.0f; - mAddB = ((add ) & 0xFF) / 255.0f; - - // A lighting filter always ignores alpha - mBlend = false; -} - -void SkiaLightingFilter::describe(ProgramDescription& description, const Extensions& extensions) { - description.colorOp = ProgramDescription::kColorLighting; -} - -void SkiaLightingFilter::setupProgram(Program* program) { - glUniform4f(program->getUniform("lightingMul"), mMulR, mMulG, mMulB, 1.0f); - glUniform4f(program->getUniform("lightingAdd"), mAddR, mAddG, mAddB, 0.0f); -} - -/////////////////////////////////////////////////////////////////////////////// -// Blend color filter -/////////////////////////////////////////////////////////////////////////////// - -SkiaBlendFilter::SkiaBlendFilter(SkColorFilter* skFilter, int color, SkXfermode::Mode mode): - SkiaColorFilter(skFilter, kBlend, true), mMode(mode) { - const int alpha = (color >> 24) & 0xFF; - mA = alpha / 255.0f; - mR = mA * ((color >> 16) & 0xFF) / 255.0f; - mG = mA * ((color >> 8) & 0xFF) / 255.0f; - mB = mA * ((color ) & 0xFF) / 255.0f; - - // TODO: We should do something smarter here - mBlend = true; -} - -void SkiaBlendFilter::describe(ProgramDescription& description, const Extensions& extensions) { - description.colorOp = ProgramDescription::kColorBlend; - description.colorMode = mMode; -} - -void SkiaBlendFilter::setupProgram(Program* program) { - glUniform4f(program->getUniform("colorBlend"), mR, mG, mB, mA); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h deleted file mode 100644 index 2feb834..0000000 --- a/libs/hwui/SkiaColorFilter.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 ANDROID_HWUI_SKIA_COLOR_FILTER_H -#define ANDROID_HWUI_SKIA_COLOR_FILTER_H - -#include <GLES2/gl2.h> -#include <SkColorFilter.h> - -#include <cutils/compiler.h> - -#include "ProgramCache.h" -#include "Extensions.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Base color filter -/////////////////////////////////////////////////////////////////////////////// - -/** - * Represents a Skia color filter. A color filter modifies a ProgramDescription - * and sets uniforms on the resulting shaders. - */ -struct SkiaColorFilter { - /** - * Type of Skia color filter in use. - */ - enum Type { - kNone, - kColorMatrix, - kLighting, - kBlend, - }; - - ANDROID_API SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend); - virtual ~SkiaColorFilter(); - - virtual void describe(ProgramDescription& description, const Extensions& extensions) = 0; - virtual void setupProgram(Program* program) = 0; - - inline bool blend() const { - return mBlend; - } - - Type type() const { - return mType; - } - - SkColorFilter* getSkColorFilter() { - return mSkFilter; - } - -protected: - Type mType; - bool mBlend; - -private: - SkColorFilter *mSkFilter; -}; // struct SkiaColorFilter - -/////////////////////////////////////////////////////////////////////////////// -// Implementations -/////////////////////////////////////////////////////////////////////////////// - -/** - * A color filter that multiplies the source color with a matrix and adds a vector. - */ -struct SkiaColorMatrixFilter: public SkiaColorFilter { - ANDROID_API SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector); - ~SkiaColorMatrixFilter(); - - void describe(ProgramDescription& description, const Extensions& extensions); - void setupProgram(Program* program); - -private: - float* mMatrix; - float* mVector; -}; // struct SkiaColorMatrixFilter - -/** - * A color filters that multiplies the source color with a fixed value and adds - * another fixed value. Ignores the alpha channel of both arguments. - */ -struct SkiaLightingFilter: public SkiaColorFilter { - ANDROID_API SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add); - - void describe(ProgramDescription& description, const Extensions& extensions); - void setupProgram(Program* program); - -private: - GLfloat mMulR, mMulG, mMulB; - GLfloat mAddR, mAddG, mAddB; -}; // struct SkiaLightingFilter - -/** - * A color filters that blends the source color with a specified destination color - * and PorterDuff blending mode. - */ -struct SkiaBlendFilter: public SkiaColorFilter { - ANDROID_API SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode); - - void describe(ProgramDescription& description, const Extensions& extensions); - void setupProgram(Program* program); - -private: - SkXfermode::Mode mMode; - GLfloat mR, mG, mB, mA; -}; // struct SkiaBlendFilter - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_SKIA_COLOR_FILTER_H diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 797ed10..6a4a0c8 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -73,7 +73,7 @@ SkiaShader::SkiaShader(): mCaches(NULL) { } SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, - SkShader::TileMode tileY, SkMatrix* matrix, bool blend): + SkShader::TileMode tileY, const SkMatrix* matrix, bool blend): mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend), mCaches(NULL) { setMatrix(matrix); @@ -101,6 +101,49 @@ void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelVi } /////////////////////////////////////////////////////////////////////////////// +// Layer shader +/////////////////////////////////////////////////////////////////////////////// + +SkiaLayerShader::SkiaLayerShader(Layer* layer, const SkMatrix* matrix): + SkiaShader(kBitmap, NULL, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, + matrix, layer->isBlend()), mLayer(layer) { + updateLocalMatrix(matrix); +} + +SkiaShader* SkiaLayerShader::copy() { + SkiaLayerShader* copy = new SkiaLayerShader(); + copy->copyFrom(*this); + copy->mLayer = mLayer; + return copy; +} + +void SkiaLayerShader::describe(ProgramDescription& description, const Extensions& extensions) { + description.hasBitmap = true; +} + +void SkiaLayerShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + GLuint textureSlot = (*textureUnit)++; + Caches::getInstance().activeTexture(textureSlot); + + const float width = mLayer->getWidth(); + const float height = mLayer->getHeight(); + + mat4 textureTransform; + computeScreenSpaceMatrix(textureTransform, modelView); + + // Uniforms + mLayer->bindTexture(); + mLayer->setWrap(GL_CLAMP_TO_EDGE); + mLayer->setFilter(GL_LINEAR); + + glUniform1i(program->getUniform("bitmapSampler"), textureSlot); + glUniformMatrix4fv(program->getUniform("textureTransform"), 1, + GL_FALSE, &textureTransform.data[0]); + glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height); +} + +/////////////////////////////////////////////////////////////////////////////// // Bitmap shader /////////////////////////////////////////////////////////////////////////////// @@ -142,7 +185,7 @@ void SkiaBitmapShader::describe(ProgramDescription& description, const Extension } void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, - const Snapshot& snapshot, GLuint* textureUnit) { + const Snapshot&, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); @@ -228,7 +271,7 @@ void SkiaLinearGradientShader::describe(ProgramDescription& description, } void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView, - const Snapshot& snapshot, GLuint* textureUnit) { + const Snapshot&, GLuint* textureUnit) { if (CC_UNLIKELY(!mIsSimple)) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); @@ -264,7 +307,7 @@ static void toCircularUnitMatrix(const float x, const float y, const float radiu SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend): - SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key, + SkiaSweepGradientShader(kCircularGradient, colors, positions, count, key, tileMode, matrix, blend) { SkMatrix unitMatrix; toCircularUnitMatrix(x, y, radius, &unitMatrix); @@ -314,11 +357,12 @@ SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* col mIsSimple = count == 2; } -SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, +SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend): SkiaShader(type, key, tileMode, tileMode, matrix, blend), mColors(colors), mPositions(positions), mCount(count) { + // protected method, that doesn't setup mUnitMatrix - should be handled by subclass mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode; } diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index cc56c50..9f30257 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -43,7 +43,8 @@ class Caches; * Represents a Skia shader. A shader will modify the GL context and active * program to recreate the original effect. */ -struct SkiaShader { +class SkiaShader { +public: /** * Type of Skia shader in use. */ @@ -57,7 +58,7 @@ struct SkiaShader { }; ANDROID_API SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, - SkShader::TileMode tileY, SkMatrix* matrix, bool blend); + SkShader::TileMode tileY, const SkMatrix* matrix, bool blend); virtual ~SkiaShader(); virtual SkiaShader* copy() = 0; @@ -87,7 +88,7 @@ struct SkiaShader { return mGenerationId; } - void setMatrix(SkMatrix* matrix) { + void setMatrix(const SkMatrix* matrix) { updateLocalMatrix(matrix); mGenerationId++; } @@ -133,6 +134,24 @@ private: /////////////////////////////////////////////////////////////////////////////// /** + * A shader that draws a layer. + */ +struct SkiaLayerShader: public SkiaShader { + SkiaLayerShader(Layer* layer, const SkMatrix* matrix); + SkiaShader* copy(); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + +private: + SkiaLayerShader() { + } + + Layer* mLayer; +}; // struct SkiaLayerShader + +/** * A shader that draws a bitmap. */ struct SkiaBitmapShader: public SkiaShader { @@ -192,7 +211,7 @@ struct SkiaSweepGradientShader: public SkiaShader { GLuint* textureUnit); protected: - SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions, + SkiaSweepGradientShader(Type type, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); SkiaSweepGradientShader() { } diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index d26ee38..6bfa203 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -27,9 +27,15 @@ namespace uirenderer { // Constructors /////////////////////////////////////////////////////////////////////////////// -Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), - invisible(false), empty(false), alpha(1.0f) { - +Snapshot::Snapshot() + : flags(0) + , previous(NULL) + , layer(NULL) + , fbo(0) + , invisible(false) + , empty(false) + , height(0) + , alpha(1.0f) { transform = &mTransformRoot; clipRect = &mClipRectRoot; region = NULL; @@ -40,10 +46,16 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), * Copies the specified snapshot/ The specified snapshot is stored as * the previous snapshot. */ -Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): - flags(0), previous(s), layer(s->layer), fbo(s->fbo), - invisible(s->invisible), empty(false), - viewport(s->viewport), height(s->height), alpha(s->alpha) { +Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) + : flags(0) + , previous(s) + , layer(s->layer) + , fbo(s->fbo) + , invisible(s->invisible) + , empty(false) + , viewport(s->viewport) + , height(s->height) + , alpha(s->alpha) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 5bdb18a..038aea8 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -108,7 +108,12 @@ public: * Returns the current clip in local coordinates. The clip rect is * transformed by the inverse transform matrix. */ - ANDROID_API const Rect& getLocalClip(); + const Rect& getLocalClip(); + + /** + * Returns the current clip in render target coordinates. + */ + const Rect& getRenderTargetClip() { return *clipRect; } /** * Resets the clip to the specified rect. @@ -238,7 +243,7 @@ private: mat4 mTransformRoot; Rect mClipRectRoot; - Rect mLocalClip; + Rect mLocalClip; // don't use directly, call getLocalClip() which initializes this SkRegion mClipRegionRoot; diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp new file mode 100644 index 0000000..3ebe7b4 --- /dev/null +++ b/libs/hwui/SpotShadow.cpp @@ -0,0 +1,931 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#define SHADOW_SHRINK_SCALE 0.1f + +#include <math.h> +#include <stdlib.h> +#include <utils/Log.h> + +#include "ShadowTessellator.h" +#include "SpotShadow.h" +#include "Vertex.h" + +namespace android { +namespace uirenderer { + +static const double EPSILON = 1e-7; + +/** + * Calculate the angle between and x and a y coordinate. + * The atan2 range from -PI to PI. + */ +static float angle(const Vector2& point, const Vector2& center) { + return atan2(point.y - center.y, point.x - center.x); +} + +/** + * Calculate the intersection of a ray with the line segment defined by two points. + * + * Returns a negative value in error conditions. + + * @param rayOrigin The start of the ray + * @param dx The x vector of the ray + * @param dy The y vector of the ray + * @param p1 The first point defining the line segment + * @param p2 The second point defining the line segment + * @return The distance along the ray if it intersects with the line segment, negative if otherwise + */ +static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy, + const Vector2& p1, const Vector2& p2) { + // The math below is derived from solving this formula, basically the + // intersection point should stay on both the ray and the edge of (p1, p2). + // solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]); + + double divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x); + if (divisor == 0) return -1.0f; // error, invalid divisor + +#if DEBUG_SHADOW + double interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor; + if (interpVal < 0 || interpVal > 1) { + ALOGW("rayIntersectPoints is hitting outside the segment %f", interpVal); + } +#endif + + double distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) + + rayOrigin.x * (p2.y - p1.y)) / divisor; + + return distance; // may be negative in error cases +} + +/** + * Sort points by their X coordinates + * + * @param points the points as a Vector2 array. + * @param pointsLength the number of vertices of the polygon. + */ +void SpotShadow::xsort(Vector2* points, int pointsLength) { + quicksortX(points, 0, pointsLength - 1); +} + +/** + * compute the convex hull of a collection of Points + * + * @param points the points as a Vector2 array. + * @param pointsLength the number of vertices of the polygon. + * @param retPoly pre allocated array of floats to put the vertices + * @return the number of points in the polygon 0 if no intersection + */ +int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) { + xsort(points, pointsLength); + int n = pointsLength; + Vector2 lUpper[n]; + lUpper[0] = points[0]; + lUpper[1] = points[1]; + + int lUpperSize = 2; + + for (int i = 2; i < n; i++) { + lUpper[lUpperSize] = points[i]; + lUpperSize++; + + while (lUpperSize > 2 && !ccw( + lUpper[lUpperSize - 3].x, lUpper[lUpperSize - 3].y, + lUpper[lUpperSize - 2].x, lUpper[lUpperSize - 2].y, + lUpper[lUpperSize - 1].x, lUpper[lUpperSize - 1].y)) { + // Remove the middle point of the three last + lUpper[lUpperSize - 2].x = lUpper[lUpperSize - 1].x; + lUpper[lUpperSize - 2].y = lUpper[lUpperSize - 1].y; + lUpperSize--; + } + } + + Vector2 lLower[n]; + lLower[0] = points[n - 1]; + lLower[1] = points[n - 2]; + + int lLowerSize = 2; + + for (int i = n - 3; i >= 0; i--) { + lLower[lLowerSize] = points[i]; + lLowerSize++; + + while (lLowerSize > 2 && !ccw( + lLower[lLowerSize - 3].x, lLower[lLowerSize - 3].y, + lLower[lLowerSize - 2].x, lLower[lLowerSize - 2].y, + lLower[lLowerSize - 1].x, lLower[lLowerSize - 1].y)) { + // Remove the middle point of the three last + lLower[lLowerSize - 2] = lLower[lLowerSize - 1]; + lLowerSize--; + } + } + + // output points in CW ordering + const int total = lUpperSize + lLowerSize - 2; + int outIndex = total - 1; + for (int i = 0; i < lUpperSize; i++) { + retPoly[outIndex] = lUpper[i]; + outIndex--; + } + + for (int i = 1; i < lLowerSize - 1; i++) { + retPoly[outIndex] = lLower[i]; + outIndex--; + } + // TODO: Add test harness which verify that all the points are inside the hull. + return total; +} + +/** + * Test whether the 3 points form a counter clockwise turn. + * + * @return true if a right hand turn + */ +bool SpotShadow::ccw(double ax, double ay, double bx, double by, + double cx, double cy) { + return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > EPSILON; +} + +/** + * Calculates the intersection of poly1 with poly2 and put in poly2. + * Note that both poly1 and poly2 must be in CW order already! + * + * @param poly1 The 1st polygon, as a Vector2 array. + * @param poly1Length The number of vertices of 1st polygon. + * @param poly2 The 2nd and output polygon, as a Vector2 array. + * @param poly2Length The number of vertices of 2nd polygon. + * @return number of vertices in output polygon as poly2. + */ +int SpotShadow::intersection(const Vector2* poly1, int poly1Length, + Vector2* poly2, int poly2Length) { +#if DEBUG_SHADOW + if (!ShadowTessellator::isClockwise(poly1, poly1Length)) { + ALOGW("Poly1 is not clockwise! Intersection is wrong!"); + } + if (!ShadowTessellator::isClockwise(poly2, poly2Length)) { + ALOGW("Poly2 is not clockwise! Intersection is wrong!"); + } +#endif + Vector2 poly[poly1Length * poly2Length + 2]; + int count = 0; + int pcount = 0; + + // If one vertex from one polygon sits inside another polygon, add it and + // count them. + for (int i = 0; i < poly1Length; i++) { + if (testPointInsidePolygon(poly1[i], poly2, poly2Length)) { + poly[count] = poly1[i]; + count++; + pcount++; + + } + } + + int insidePoly2 = pcount; + for (int i = 0; i < poly2Length; i++) { + if (testPointInsidePolygon(poly2[i], poly1, poly1Length)) { + poly[count] = poly2[i]; + count++; + } + } + + int insidePoly1 = count - insidePoly2; + // If all vertices from poly1 are inside poly2, then just return poly1. + if (insidePoly2 == poly1Length) { + memcpy(poly2, poly1, poly1Length * sizeof(Vector2)); + return poly1Length; + } + + // If all vertices from poly2 are inside poly1, then just return poly2. + if (insidePoly1 == poly2Length) { + return poly2Length; + } + + // Since neither polygon fully contain the other one, we need to add all the + // intersection points. + Vector2 intersection; + for (int i = 0; i < poly2Length; i++) { + for (int j = 0; j < poly1Length; j++) { + int poly2LineStart = i; + int poly2LineEnd = ((i + 1) % poly2Length); + int poly1LineStart = j; + int poly1LineEnd = ((j + 1) % poly1Length); + bool found = lineIntersection( + poly2[poly2LineStart].x, poly2[poly2LineStart].y, + poly2[poly2LineEnd].x, poly2[poly2LineEnd].y, + poly1[poly1LineStart].x, poly1[poly1LineStart].y, + poly1[poly1LineEnd].x, poly1[poly1LineEnd].y, + intersection); + if (found) { + poly[count].x = intersection.x; + poly[count].y = intersection.y; + count++; + } else { + Vector2 delta = poly2[i] - poly1[j]; + if (delta.lengthSquared() < EPSILON) { + poly[count] = poly2[i]; + count++; + } + } + } + } + + if (count == 0) { + return 0; + } + + // Sort the result polygon around the center. + Vector2 center(0.0f, 0.0f); + for (int i = 0; i < count; i++) { + center += poly[i]; + } + center /= count; + sort(poly, count, center); + +#if DEBUG_SHADOW + // Since poly2 is overwritten as the result, we need to save a copy to do + // our verification. + Vector2 oldPoly2[poly2Length]; + int oldPoly2Length = poly2Length; + memcpy(oldPoly2, poly2, sizeof(Vector2) * poly2Length); +#endif + + // Filter the result out from poly and put it into poly2. + poly2[0] = poly[0]; + int lastOutputIndex = 0; + for (int i = 1; i < count; i++) { + Vector2 delta = poly[i] - poly2[lastOutputIndex]; + if (delta.lengthSquared() >= EPSILON) { + poly2[++lastOutputIndex] = poly[i]; + } else { + // If the vertices are too close, pick the inner one, because the + // inner one is more likely to be an intersection point. + Vector2 delta1 = poly[i] - center; + Vector2 delta2 = poly2[lastOutputIndex] - center; + if (delta1.lengthSquared() < delta2.lengthSquared()) { + poly2[lastOutputIndex] = poly[i]; + } + } + } + int resultLength = lastOutputIndex + 1; + +#if DEBUG_SHADOW + testConvex(poly2, resultLength, "intersection"); + testConvex(poly1, poly1Length, "input poly1"); + testConvex(oldPoly2, oldPoly2Length, "input poly2"); + + testIntersection(poly1, poly1Length, oldPoly2, oldPoly2Length, poly2, resultLength); +#endif + + return resultLength; +} + +/** + * Sort points about a center point + * + * @param poly The in and out polyogon as a Vector2 array. + * @param polyLength The number of vertices of the polygon. + * @param center the center ctr[0] = x , ctr[1] = y to sort around. + */ +void SpotShadow::sort(Vector2* poly, int polyLength, const Vector2& center) { + quicksortCirc(poly, 0, polyLength - 1, center); +} + +/** + * Swap points pointed to by i and j + */ +void SpotShadow::swap(Vector2* points, int i, int j) { + Vector2 temp = points[i]; + points[i] = points[j]; + points[j] = temp; +} + +/** + * quick sort implementation about the center. + */ +void SpotShadow::quicksortCirc(Vector2* points, int low, int high, + const Vector2& center) { + int i = low, j = high; + int p = low + (high - low) / 2; + float pivot = angle(points[p], center); + while (i <= j) { + while (angle(points[i], center) > pivot) { + i++; + } + while (angle(points[j], center) < pivot) { + j--; + } + + if (i <= j) { + swap(points, i, j); + i++; + j--; + } + } + if (low < j) quicksortCirc(points, low, j, center); + if (i < high) quicksortCirc(points, i, high, center); +} + +/** + * Sort points by x axis + * + * @param points points to sort + * @param low start index + * @param high end index + */ +void SpotShadow::quicksortX(Vector2* points, int low, int high) { + int i = low, j = high; + int p = low + (high - low) / 2; + float pivot = points[p].x; + while (i <= j) { + while (points[i].x < pivot) { + i++; + } + while (points[j].x > pivot) { + j--; + } + + if (i <= j) { + swap(points, i, j); + i++; + j--; + } + } + if (low < j) quicksortX(points, low, j); + if (i < high) quicksortX(points, i, high); +} + +/** + * Test whether a point is inside the polygon. + * + * @param testPoint the point to test + * @param poly the polygon + * @return true if the testPoint is inside the poly. + */ +bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, + const Vector2* poly, int len) { + bool c = false; + double testx = testPoint.x; + double testy = testPoint.y; + for (int i = 0, j = len - 1; i < len; j = i++) { + double startX = poly[j].x; + double startY = poly[j].y; + double endX = poly[i].x; + double endY = poly[i].y; + + if (((endY > testy) != (startY > testy)) && + (testx < (startX - endX) * (testy - endY) + / (startY - endY) + endX)) { + c = !c; + } + } + return c; +} + +/** + * Make the polygon turn clockwise. + * + * @param polygon the polygon as a Vector2 array. + * @param len the number of points of the polygon + */ +void SpotShadow::makeClockwise(Vector2* polygon, int len) { + if (polygon == 0 || len == 0) { + return; + } + if (!ShadowTessellator::isClockwise(polygon, len)) { + reverse(polygon, len); + } +} + +/** + * Reverse the polygon + * + * @param polygon the polygon as a Vector2 array + * @param len the number of points of the polygon + */ +void SpotShadow::reverse(Vector2* polygon, int len) { + int n = len / 2; + for (int i = 0; i < n; i++) { + Vector2 tmp = polygon[i]; + int k = len - 1 - i; + polygon[i] = polygon[k]; + polygon[k] = tmp; + } +} + +/** + * Intersects two lines in parametric form. This function is called in a tight + * loop, and we need double precision to get things right. + * + * @param x1 the x coordinate point 1 of line 1 + * @param y1 the y coordinate point 1 of line 1 + * @param x2 the x coordinate point 2 of line 1 + * @param y2 the y coordinate point 2 of line 1 + * @param x3 the x coordinate point 1 of line 2 + * @param y3 the y coordinate point 1 of line 2 + * @param x4 the x coordinate point 2 of line 2 + * @param y4 the y coordinate point 2 of line 2 + * @param ret the x,y location of the intersection + * @return true if it found an intersection + */ +inline bool SpotShadow::lineIntersection(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4, Vector2& ret) { + double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + if (d == 0.0) return false; + + double dx = (x1 * y2 - y1 * x2); + double dy = (x3 * y4 - y3 * x4); + double x = (dx * (x3 - x4) - (x1 - x2) * dy) / d; + double y = (dx * (y3 - y4) - (y1 - y2) * dy) / d; + + // The intersection should be in the middle of the point 1 and point 2, + // likewise point 3 and point 4. + if (((x - x1) * (x - x2) > EPSILON) + || ((x - x3) * (x - x4) > EPSILON) + || ((y - y1) * (y - y2) > EPSILON) + || ((y - y3) * (y - y4) > EPSILON)) { + // Not interesected + return false; + } + ret.x = x; + ret.y = y; + return true; + +} + +/** + * Compute a horizontal circular polygon about point (x , y , height) of radius + * (size) + * + * @param points number of the points of the output polygon. + * @param lightCenter the center of the light. + * @param size the light size. + * @param ret result polygon. + */ +void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, + float size, Vector3* ret) { + // TODO: Caching all the sin / cos values and store them in a look up table. + for (int i = 0; i < points; i++) { + double angle = 2 * i * M_PI / points; + ret[i].x = cosf(angle) * size + lightCenter.x; + ret[i].y = sinf(angle) * size + lightCenter.y; + ret[i].z = lightCenter.z; + } +} + +/** +* Generate the shadow from a spot light. +* +* @param poly x,y,z vertexes of a convex polygon that occludes the light source +* @param polyLength number of vertexes of the occluding polygon +* @param lightCenter the center of the light +* @param lightSize the radius of the light source +* @param lightVertexCount the vertex counter for the light polygon +* @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return +* empty strip if error. +* +*/ +VertexBufferMode SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly, + int polyLength, const Vector3& lightCenter, float lightSize, + int lightVertexCount, VertexBuffer& retStrips) { + Vector3 light[lightVertexCount * 3]; + computeLightPolygon(lightVertexCount, lightCenter, lightSize, light); + computeSpotShadow(isCasterOpaque, light, lightVertexCount, lightCenter, poly, + polyLength, retStrips); + return kVertexBufferMode_TwoPolyRingShadow; +} + +/** + * Generate the shadow spot light of shape lightPoly and a object poly + * + * @param lightPoly x,y,z vertex of a convex polygon that is the light source + * @param lightPolyLength number of vertexes of the light source polygon + * @param poly x,y,z vertexes of a convex polygon that occludes the light source + * @param polyLength number of vertexes of the occluding polygon + * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return + * empty strip if error. + */ +void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly, + int lightPolyLength, const Vector3& lightCenter, const Vector3* poly, + int polyLength, VertexBuffer& shadowTriangleStrip) { + // Point clouds for all the shadowed vertices + Vector2 shadowRegion[lightPolyLength * polyLength]; + // Shadow polygon from one point light. + Vector2 outline[polyLength]; + Vector2 umbraMem[polyLength * lightPolyLength]; + Vector2* umbra = umbraMem; + + int umbraLength = 0; + + // Validate input, receiver is always at z = 0 plane. + bool inputPolyPositionValid = true; + for (int i = 0; i < polyLength; i++) { + if (poly[i].z >= lightPoly[0].z) { + inputPolyPositionValid = false; + ALOGW("polygon above the light"); + break; + } + } + + // If the caster's position is invalid, don't draw anything. + if (!inputPolyPositionValid) { + return; + } + + // Calculate the umbra polygon based on intersections of all outlines + int k = 0; + for (int j = 0; j < lightPolyLength; j++) { + int m = 0; + for (int i = 0; i < polyLength; i++) { + // After validating the input, deltaZ is guaranteed to be positive. + float deltaZ = lightPoly[j].z - poly[i].z; + float ratioZ = lightPoly[j].z / deltaZ; + float x = lightPoly[j].x - ratioZ * (lightPoly[j].x - poly[i].x); + float y = lightPoly[j].y - ratioZ * (lightPoly[j].y - poly[i].y); + + Vector2 newPoint = Vector2(x, y); + shadowRegion[k] = newPoint; + outline[m] = newPoint; + + k++; + m++; + } + + // For the first light polygon's vertex, use the outline as the umbra. + // Later on, use the intersection of the outline and existing umbra. + if (umbraLength == 0) { + for (int i = 0; i < polyLength; i++) { + umbra[i] = outline[i]; + } + umbraLength = polyLength; + } else { + int col = ((j * 255) / lightPolyLength); + umbraLength = intersection(outline, polyLength, umbra, umbraLength); + if (umbraLength == 0) { + break; + } + } + } + + // Generate the penumbra area using the hull of all shadow regions. + int shadowRegionLength = k; + Vector2 penumbra[k]; + int penumbraLength = hull(shadowRegion, shadowRegionLength, penumbra); + + Vector2 fakeUmbra[polyLength]; + if (umbraLength < 3) { + // If there is no real umbra, make a fake one. + for (int i = 0; i < polyLength; i++) { + float deltaZ = lightCenter.z - poly[i].z; + float ratioZ = lightCenter.z / deltaZ; + float x = lightCenter.x - ratioZ * (lightCenter.x - poly[i].x); + float y = lightCenter.y - ratioZ * (lightCenter.y - poly[i].y); + + fakeUmbra[i].x = x; + fakeUmbra[i].y = y; + } + + // Shrink the centroid's shadow by 10%. + // TODO: Study the magic number of 10%. + Vector2 shadowCentroid = + ShadowTessellator::centroid2d(fakeUmbra, polyLength); + for (int i = 0; i < polyLength; i++) { + fakeUmbra[i] = shadowCentroid * (1.0f - SHADOW_SHRINK_SCALE) + + fakeUmbra[i] * SHADOW_SHRINK_SCALE; + } +#if DEBUG_SHADOW + ALOGD("No real umbra make a fake one, centroid2d = %f , %f", + shadowCentroid.x, shadowCentroid.y); +#endif + // Set the fake umbra, whose size is the same as the original polygon. + umbra = fakeUmbra; + umbraLength = polyLength; + } + + generateTriangleStrip(isCasterOpaque, penumbra, penumbraLength, umbra, + umbraLength, poly, polyLength, shadowTriangleStrip); +} + +/** + * Converts a polygon specified with CW vertices into an array of distance-from-centroid values. + * + * Returns false in error conditions + * + * @param poly Array of vertices. Note that these *must* be CW. + * @param polyLength The number of vertices in the polygon. + * @param polyCentroid The centroid of the polygon, from which rays will be cast + * @param rayDist The output array for the calculated distances, must be SHADOW_RAY_COUNT in size + */ +bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& polyCentroid, + float* rayDist) { + const int rays = SHADOW_RAY_COUNT; + const float step = M_PI * 2 / rays; + + const Vector2* lastVertex = &(poly[polyLength - 1]); + float startAngle = angle(*lastVertex, polyCentroid); + + // Start with the ray that's closest to and less than startAngle + int rayIndex = floor((startAngle - EPSILON) / step); + rayIndex = (rayIndex + rays) % rays; // ensure positive + + for (int polyIndex = 0; polyIndex < polyLength; polyIndex++) { + /* + * For a given pair of vertices on the polygon, poly[i-1] and poly[i], the rays that + * intersect these will be those that are between the two angles from the centroid that the + * vertices define. + * + * Because the polygon vertices are stored clockwise, the closest ray with an angle + * *smaller* than that defined by angle(poly[i], centroid) will be the first ray that does + * not intersect with poly[i-1], poly[i]. + */ + float currentAngle = angle(poly[polyIndex], polyCentroid); + + // find first ray that will not intersect the line segment poly[i-1] & poly[i] + int firstRayIndexOnNextSegment = floor((currentAngle - EPSILON) / step); + firstRayIndexOnNextSegment = (firstRayIndexOnNextSegment + rays) % rays; // ensure positive + + // Iterate through all rays that intersect with poly[i-1], poly[i] line segment. + // This may be 0 rays. + while (rayIndex != firstRayIndexOnNextSegment) { + float distanceToIntersect = rayIntersectPoints(polyCentroid, + cos(rayIndex * step), + sin(rayIndex * step), + *lastVertex, poly[polyIndex]); + if (distanceToIntersect < 0) { +#if DEBUG_SHADOW + ALOGW("ERROR: convertPolyToRayDist failed"); +#endif + return false; // error case, abort + } + + rayDist[rayIndex] = distanceToIntersect; + + rayIndex = (rayIndex - 1 + rays) % rays; + } + lastVertex = &poly[polyIndex]; + } + + return true; +} + +int SpotShadow::calculateOccludedUmbra(const Vector2* umbra, int umbraLength, + const Vector3* poly, int polyLength, Vector2* occludedUmbra) { + // Occluded umbra area is computed as the intersection of the projected 2D + // poly and umbra. + for (int i = 0; i < polyLength; i++) { + occludedUmbra[i].x = poly[i].x; + occludedUmbra[i].y = poly[i].y; + } + + // Both umbra and incoming polygon are guaranteed to be CW, so we can call + // intersection() directly. + return intersection(umbra, umbraLength, + occludedUmbra, polyLength); +} + +#define OCLLUDED_UMBRA_SHRINK_FACTOR 0.95f +/** + * Generate a triangle strip given two convex polygons + * + * @param penumbra The outer polygon x,y vertexes + * @param penumbraLength The number of vertexes in the outer polygon + * @param umbra The inner outer polygon x,y vertexes + * @param umbraLength The number of vertexes in the inner polygon + * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return + * empty strip if error. +**/ +void SpotShadow::generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra, + int penumbraLength, const Vector2* umbra, int umbraLength, + const Vector3* poly, int polyLength, VertexBuffer& shadowTriangleStrip) { + const int rays = SHADOW_RAY_COUNT; + const int size = 2 * rays; + const float step = M_PI * 2 / rays; + // Centroid of the umbra. + Vector2 centroid = ShadowTessellator::centroid2d(umbra, umbraLength); +#if DEBUG_SHADOW + ALOGD("centroid2d = %f , %f", centroid.x, centroid.y); +#endif + // Intersection to the penumbra. + float penumbraDistPerRay[rays]; + // Intersection to the umbra. + float umbraDistPerRay[rays]; + // Intersection to the occluded umbra area. + float occludedUmbraDistPerRay[rays]; + + // convert CW polygons to ray distance encoding, aborting on conversion failure + if (!convertPolyToRayDist(umbra, umbraLength, centroid, umbraDistPerRay)) return; + if (!convertPolyToRayDist(penumbra, penumbraLength, centroid, penumbraDistPerRay)) return; + + bool hasOccludedUmbraArea = false; + if (isCasterOpaque) { + Vector2 occludedUmbra[polyLength + umbraLength]; + int occludedUmbraLength = calculateOccludedUmbra(umbra, umbraLength, poly, polyLength, + occludedUmbra); + // Make sure the centroid is inside the umbra, otherwise, fall back to the + // approach as if there is no occluded umbra area. + if (testPointInsidePolygon(centroid, occludedUmbra, occludedUmbraLength)) { + hasOccludedUmbraArea = true; + // Shrink the occluded umbra area to avoid pixel level artifacts. + for (int i = 0; i < occludedUmbraLength; i ++) { + occludedUmbra[i] = centroid + (occludedUmbra[i] - centroid) * + OCLLUDED_UMBRA_SHRINK_FACTOR; + } + if (!convertPolyToRayDist(occludedUmbra, occludedUmbraLength, centroid, + occludedUmbraDistPerRay)) { + return; + } + } + } + + AlphaVertex* shadowVertices = + shadowTriangleStrip.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT); + + // Calculate the vertices (x, y, alpha) in the shadow area. + AlphaVertex centroidXYA; + AlphaVertex::set(¢roidXYA, centroid.x, centroid.y, 1.0f); + for (int rayIndex = 0; rayIndex < rays; rayIndex++) { + float dx = cosf(step * rayIndex); + float dy = sinf(step * rayIndex); + + // penumbra ring + float penumbraDistance = penumbraDistPerRay[rayIndex]; + AlphaVertex::set(&shadowVertices[rayIndex], + dx * penumbraDistance + centroid.x, + dy * penumbraDistance + centroid.y, 0.0f); + + // umbra ring + float umbraDistance = umbraDistPerRay[rayIndex]; + AlphaVertex::set(&shadowVertices[rays + rayIndex], + dx * umbraDistance + centroid.x, dy * umbraDistance + centroid.y, 1.0f); + + // occluded umbra ring + if (hasOccludedUmbraArea) { + float occludedUmbraDistance = occludedUmbraDistPerRay[rayIndex]; + AlphaVertex::set(&shadowVertices[2 * rays + rayIndex], + dx * occludedUmbraDistance + centroid.x, + dy * occludedUmbraDistance + centroid.y, 1.0f); + } else { + // Put all vertices of the occluded umbra ring at the centroid. + shadowVertices[2 * rays + rayIndex] = centroidXYA; + } + } +} + +/** + * This is only for experimental purpose. + * After intersections are calculated, we could smooth the polygon if needed. + * So far, we don't think it is more appealing yet. + * + * @param level The level of smoothness. + * @param rays The total number of rays. + * @param rayDist (In and Out) The distance for each ray. + * + */ +void SpotShadow::smoothPolygon(int level, int rays, float* rayDist) { + for (int k = 0; k < level; k++) { + for (int i = 0; i < rays; i++) { + float p1 = rayDist[(rays - 1 + i) % rays]; + float p2 = rayDist[i]; + float p3 = rayDist[(i + 1) % rays]; + rayDist[i] = (p1 + p2 * 2 + p3) / 4; + } + } +} + +#if DEBUG_SHADOW + +#define TEST_POINT_NUMBER 128 + +/** + * Calculate the bounds for generating random test points. + */ +void SpotShadow::updateBound(const Vector2 inVector, Vector2& lowerBound, + Vector2& upperBound ) { + if (inVector.x < lowerBound.x) { + lowerBound.x = inVector.x; + } + + if (inVector.y < lowerBound.y) { + lowerBound.y = inVector.y; + } + + if (inVector.x > upperBound.x) { + upperBound.x = inVector.x; + } + + if (inVector.y > upperBound.y) { + upperBound.y = inVector.y; + } +} + +/** + * For debug purpose, when things go wrong, dump the whole polygon data. + */ +static void dumpPolygon(const Vector2* poly, int polyLength, const char* polyName) { + for (int i = 0; i < polyLength; i++) { + ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y); + } +} + +/** + * Test whether the polygon is convex. + */ +bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength, + const char* name) { + bool isConvex = true; + for (int i = 0; i < polygonLength; i++) { + Vector2 start = polygon[i]; + Vector2 middle = polygon[(i + 1) % polygonLength]; + Vector2 end = polygon[(i + 2) % polygonLength]; + + double delta = (double(middle.x) - start.x) * (double(end.y) - start.y) - + (double(middle.y) - start.y) * (double(end.x) - start.x); + bool isCCWOrCoLinear = (delta >= EPSILON); + + if (isCCWOrCoLinear) { + ALOGW("(Error Type 2): polygon (%s) is not a convex b/c start (x %f, y %f)," + "middle (x %f, y %f) and end (x %f, y %f) , delta is %f !!!", + name, start.x, start.y, middle.x, middle.y, end.x, end.y, delta); + isConvex = false; + break; + } + } + return isConvex; +} + +/** + * Test whether or not the polygon (intersection) is within the 2 input polygons. + * Using Marte Carlo method, we generate a random point, and if it is inside the + * intersection, then it must be inside both source polygons. + */ +void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, + const Vector2* poly2, int poly2Length, + const Vector2* intersection, int intersectionLength) { + // Find the min and max of x and y. + Vector2 lowerBound(FLT_MAX, FLT_MAX); + Vector2 upperBound(-FLT_MAX, -FLT_MAX); + for (int i = 0; i < poly1Length; i++) { + updateBound(poly1[i], lowerBound, upperBound); + } + for (int i = 0; i < poly2Length; i++) { + updateBound(poly2[i], lowerBound, upperBound); + } + + bool dumpPoly = false; + for (int k = 0; k < TEST_POINT_NUMBER; k++) { + // Generate a random point between minX, minY and maxX, maxY. + double randomX = rand() / double(RAND_MAX); + double randomY = rand() / double(RAND_MAX); + + Vector2 testPoint; + testPoint.x = lowerBound.x + randomX * (upperBound.x - lowerBound.x); + testPoint.y = lowerBound.y + randomY * (upperBound.y - lowerBound.y); + + // If the random point is in both poly 1 and 2, then it must be intersection. + if (testPointInsidePolygon(testPoint, intersection, intersectionLength)) { + if (!testPointInsidePolygon(testPoint, poly1, poly1Length)) { + dumpPoly = true; + ALOGW("(Error Type 1): one point (%f, %f) in the intersection is" + " not in the poly1", + testPoint.x, testPoint.y); + } + + if (!testPointInsidePolygon(testPoint, poly2, poly2Length)) { + dumpPoly = true; + ALOGW("(Error Type 1): one point (%f, %f) in the intersection is" + " not in the poly2", + testPoint.x, testPoint.y); + } + } + } + + if (dumpPoly) { + dumpPolygon(intersection, intersectionLength, "intersection"); + for (int i = 1; i < intersectionLength; i++) { + Vector2 delta = intersection[i] - intersection[i - 1]; + ALOGD("Intersetion i, %d Vs i-1 is delta %f", i, delta.lengthSquared()); + } + + dumpPolygon(poly1, poly1Length, "poly 1"); + dumpPolygon(poly2, poly2Length, "poly 2"); + } +} +#endif + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h new file mode 100644 index 0000000..fb3e6d5 --- /dev/null +++ b/libs/hwui/SpotShadow.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 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 ANDROID_HWUI_SPOT_SHADOW_H +#define ANDROID_HWUI_SPOT_SHADOW_H + +#include "Debug.h" +#include "Vector.h" +#include "VertexBuffer.h" + +namespace android { +namespace uirenderer { + +class SpotShadow { +public: + static VertexBufferMode createSpotShadow(bool isCasterOpaque, const Vector3* poly, + int polyLength, const Vector3& lightCenter, float lightSize, + int lightVertexCount, VertexBuffer& retStrips); + +private: + static int calculateOccludedUmbra(const Vector2* umbra, int umbraLength, + const Vector3* poly, int polyLength, Vector2* occludedUmbra); + static void computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly, + int lightPolyLength, const Vector3& lightCenter, const Vector3* poly, + int polyLength, VertexBuffer& retstrips); + + static void computeLightPolygon(int points, const Vector3& lightCenter, + float size, Vector3* ret); + + static void smoothPolygon(int level, int rays, float* rayDist); + static float rayIntersectPoly(const Vector2* poly, int polyLength, + const Vector2& point, float dx, float dy); + + static void xsort(Vector2* points, int pointsLength); + static int hull(Vector2* points, int pointsLength, Vector2* retPoly); + static bool ccw(double ax, double ay, double bx, double by, double cx, double cy); + static int intersection(const Vector2* poly1, int poly1length, Vector2* poly2, int poly2length); + static void sort(Vector2* poly, int polyLength, const Vector2& center); + + static void swap(Vector2* points, int i, int j); + static void quicksortCirc(Vector2* points, int low, int high, const Vector2& center); + static void quicksortX(Vector2* points, int low, int high); + + static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len); + static void makeClockwise(Vector2* polygon, int len); + static void reverse(Vector2* polygon, int len); + static inline bool lineIntersection(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4, Vector2& ret); + + static void generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra, + int penumbraLength, const Vector2* umbra, int umbraLength, + const Vector3* poly, int polyLength, VertexBuffer& retstrips); + +#if DEBUG_SHADOW + // Verification utility function. + static bool testConvex(const Vector2* polygon, int polygonLength, + const char* name); + static void testIntersection(const Vector2* poly1, int poly1Length, + const Vector2* poly2, int poly2Length, + const Vector2* intersection, int intersectionLength); + static void updateBound(const Vector2 inVector, Vector2& lowerBound, Vector2& upperBound ); +#endif + +}; // SpotShadow + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SPOT_SHADOW_H diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp new file mode 100644 index 0000000..05f6cf8 --- /dev/null +++ b/libs/hwui/StatefulBaseRenderer.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2014 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 <SkCanvas.h> + +#include "StatefulBaseRenderer.h" + +namespace android { +namespace uirenderer { + +StatefulBaseRenderer::StatefulBaseRenderer() : + mDirtyClip(false), mWidth(-1), mHeight(-1), + mSaveCount(1), mFirstSnapshot(new Snapshot), mSnapshot(mFirstSnapshot) { +} + +void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop, + float clipRight, float clipBottom) { + mSnapshot = new Snapshot(mFirstSnapshot, + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); + mSnapshot->fbo = getTargetFbo(); + mSaveCount = 1; +} + +void StatefulBaseRenderer::initializeViewport(int width, int height) { + mWidth = width; + mHeight = height; + + mFirstSnapshot->height = height; + mFirstSnapshot->viewport.set(0, 0, width, height); +} + +/////////////////////////////////////////////////////////////////////////////// +// Save (layer) +/////////////////////////////////////////////////////////////////////////////// + +/** + * Non-virtual implementation of save, guaranteed to save without side-effects + * + * The approach here and in restoreSnapshot(), allows subclasses to directly manipulate the save + * stack, and ensures restoreToCount() doesn't call back into subclass overrides. + */ +int StatefulBaseRenderer::saveSnapshot(int flags) { + mSnapshot = new Snapshot(mSnapshot, flags); + return mSaveCount++; +} + +int StatefulBaseRenderer::save(int flags) { + return saveSnapshot(flags); +} + +/** + * Non-virtual implementation of restore, guaranteed to restore without side-effects. + */ +void StatefulBaseRenderer::restoreSnapshot() { + sp<Snapshot> toRemove = mSnapshot; + sp<Snapshot> toRestore = mSnapshot->previous; + + mSaveCount--; + mSnapshot = toRestore; + + // subclass handles restore implementation + onSnapshotRestored(*toRemove, *toRestore); +} + +void StatefulBaseRenderer::restore() { + if (mSaveCount > 1) { + restoreSnapshot(); + } +} + +void StatefulBaseRenderer::restoreToCount(int saveCount) { + if (saveCount < 1) saveCount = 1; + + while (mSaveCount > saveCount) { + restoreSnapshot(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Matrix +/////////////////////////////////////////////////////////////////////////////// + +void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const { + mSnapshot->transform->copyTo(*matrix); +} + +void StatefulBaseRenderer::translate(float dx, float dy, float dz) { + mSnapshot->transform->translate(dx, dy, dz); +} + +void StatefulBaseRenderer::rotate(float degrees) { + mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); +} + +void StatefulBaseRenderer::scale(float sx, float sy) { + mSnapshot->transform->scale(sx, sy, 1.0f); +} + +void StatefulBaseRenderer::skew(float sx, float sy) { + mSnapshot->transform->skew(sx, sy); +} + +void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) { + if (matrix) { + mSnapshot->transform->load(*matrix); + } else { + mSnapshot->transform->loadIdentity(); + } +} + +void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) { + mSnapshot->transform->load(matrix); +} + +void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) { + mat4 transform(*matrix); + mSnapshot->transform->multiply(transform); +} + +void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) { + mSnapshot->transform->multiply(matrix); +} + +/////////////////////////////////////////////////////////////////////////////// +// Clip +/////////////////////////////////////////////////////////////////////////////// + +bool StatefulBaseRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { + if (CC_LIKELY(currentTransform()->rectToRect())) { + mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op); + return !mSnapshot->clipRect->isEmpty(); + } + + SkPath path; + path.addRect(left, top, right, bottom); + + return StatefulBaseRenderer::clipPath(&path, op); +} + +bool StatefulBaseRenderer::clipPath(const SkPath* path, SkRegion::Op op) { + SkMatrix transform; + currentTransform()->copyTo(transform); + + SkPath transformed; + path->transform(transform, &transformed); + + SkRegion clip; + if (!mSnapshot->previous->clipRegion->isEmpty()) { + clip.setRegion(*mSnapshot->previous->clipRegion); + } else { + if (mSnapshot->previous == firstSnapshot()) { + clip.setRect(0, 0, getWidth(), getHeight()); + } else { + Rect* bounds = mSnapshot->previous->clipRect; + clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom); + } + } + + SkRegion region; + region.setPath(transformed, clip); + + mDirtyClip |= mSnapshot->clipRegionTransformed(region, op); + return !mSnapshot->clipRect->isEmpty(); +} + +bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { + mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op); + return !mSnapshot->clipRect->isEmpty(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Quick Rejection +/////////////////////////////////////////////////////////////////////////////// + +/** + * Calculates whether content drawn within the passed bounds would be outside of, or intersect with + * the clipRect. Does not modify the scissor. + * + * @param clipRequired if not null, will be set to true if element intersects clip + * (and wasn't rejected) + * + * @param snapOut if set, the geometry will be treated as having an AA ramp. + * See Rect::snapGeometryToPixelBoundaries() + */ +bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, + float right, float bottom, bool* clipRequired, bool snapOut) const { + if (mSnapshot->isIgnored() || bottom <= top || right <= left) { + return true; + } + + Rect r(left, top, right, bottom); + currentTransform()->mapRect(r); + r.snapGeometryToPixelBoundaries(snapOut); + + Rect clipRect(*currentClipRect()); + clipRect.snapToPixelBoundaries(); + + if (!clipRect.intersects(r)) return true; + + // clip is required if geometry intersects clip rect + if (clipRequired) *clipRequired = !clipRect.contains(r); + return false; +} + +/** + * Returns false if drawing won't be clipped out. + * + * Makes the decision conservatively, by rounding out the mapped rect before comparing with the + * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but + * rejection is still desired. + * + * This function, unlike quickRejectSetupScissor, should be used where precise geometry information + * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass + * rejection where precise rejection isn't important, or precise information isn't available. + */ +bool StatefulBaseRenderer::quickRejectConservative(float left, float top, + float right, float bottom) const { + if (mSnapshot->isIgnored() || bottom <= top || right <= left) { + return true; + } + + Rect r(left, top, right, bottom); + currentTransform()->mapRect(r); + r.roundOut(); // rounded out to be conservative + + Rect clipRect(*currentClipRect()); + clipRect.snapToPixelBoundaries(); + + if (!clipRect.intersects(r)) return true; + + return false; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h new file mode 100644 index 0000000..64354ac --- /dev/null +++ b/libs/hwui/StatefulBaseRenderer.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2014 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 ANDROID_HWUI_STATEFUL_BASE_RENDERER_H +#define ANDROID_HWUI_STATEFUL_BASE_RENDERER_H + +#include <utils/RefBase.h> + +#include "Renderer.h" +#include "Snapshot.h" + +namespace android { +namespace uirenderer { + +/** + * Abstract Renderer subclass, which implements Canvas state methods. + * + * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the + * Renderer interface. Drawing and recording classes that extend StatefulBaseRenderer will have + * different use cases: + * + * Drawing subclasses (i.e. OpenGLRenderer) can query attributes (such as transform) or hook into + * changes (e.g. save/restore) with minimal surface area for manipulating the stack itself. + * + * Recording subclasses (i.e. DisplayListRenderer) can both record and pass through state operations + * to StatefulBaseRenderer, so that not only will querying operations work (getClip/Matrix), but so + * that quickRejection can also be used. + */ +class StatefulBaseRenderer : public Renderer { +public: + StatefulBaseRenderer(); + + virtual status_t prepare(bool opaque) { + return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque); + } + void initializeViewport(int width, int height); + void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom); + + // getters + bool hasRectToRectTransform() const { + return CC_LIKELY(currentTransform()->rectToRect()); + } + + // Save (layer) + virtual int getSaveCount() const { return mSaveCount; } + virtual int save(int flags); + virtual void restore(); + virtual void restoreToCount(int saveCount); + //virtual int saveLayer(float left, float top, float right, float bottom, + // int alpha, SkXfermode::Mode mode, int flags); + + // Matrix + virtual void getMatrix(SkMatrix* outMatrix) const; + virtual void translate(float dx, float dy, float dz = 0.0f); + virtual void rotate(float degrees); + virtual void scale(float sx, float sy); + virtual void skew(float sx, float sy); + + virtual void setMatrix(const SkMatrix* matrix); + void setMatrix(const Matrix4& matrix); // internal only convenience method + virtual void concatMatrix(const SkMatrix* matrix); + void concatMatrix(const Matrix4& matrix); // internal only convenience method + + // Clip + virtual const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); } + + virtual bool quickRejectConservative(float left, float top, float right, float bottom) const; + + virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); + virtual bool clipPath(const SkPath* path, SkRegion::Op op); + virtual bool clipRegion(const SkRegion* region, SkRegion::Op op); + +protected: + const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); } + + int getWidth() { return mWidth; } + int getHeight() { return mHeight; } + + // Save + int saveSnapshot(int flags); + void restoreSnapshot(); + + // allows subclasses to control what value is stored in snapshot's fbo field in + // initializeSaveStack + virtual GLuint getTargetFbo() const { + return -1; + } + + // Clip + bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, + bool* clipRequired, bool snapOut) const; + + /** + * Called just after a restore has occurred. The 'removed' snapshot popped from the stack, + * 'restored' snapshot has become the top/current. + * + * Subclasses can override this method to handle layer restoration + */ + virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}; + + inline const Rect* currentClipRect() const { + return mSnapshot->clipRect; + } + + inline const mat4* currentTransform() const { + return mSnapshot->transform; + } + + inline const Snapshot* currentSnapshot() const { + return mSnapshot != NULL ? mSnapshot.get() : mFirstSnapshot.get(); + } + + inline const Snapshot* firstSnapshot() const { + return mFirstSnapshot.get(); + } + + // indicites that the clip has been changed since the last time it was consumed + bool mDirtyClip; + +private: + // Dimensions of the drawing surface + int mWidth, mHeight; + + // Number of saved states + int mSaveCount; + + // Base state + sp<Snapshot> mFirstSnapshot; + +protected: + // Current state + // TODO: should become private, once hooks needed by OpenGLRenderer are added + sp<Snapshot> mSnapshot; + +}; // class StatefulBaseRenderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_STATEFUL_BASE_RENDERER_H diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp index 2764523..8ce57db 100644 --- a/libs/hwui/Stencil.cpp +++ b/libs/hwui/Stencil.cpp @@ -35,7 +35,7 @@ namespace uirenderer { Stencil::Stencil(): mState(kDisabled) { } -uint32_t Stencil::getStencilSize() { +uint8_t Stencil::getStencilSize() { return STENCIL_BUFFER_SIZE; } diff --git a/libs/hwui/Stencil.h b/libs/hwui/Stencil.h index 83ad668..c5e5186 100644 --- a/libs/hwui/Stencil.h +++ b/libs/hwui/Stencil.h @@ -40,7 +40,7 @@ public: * Returns the desired size for the stencil buffer. If the returned value * is 0, then no stencil buffer is required. */ - ANDROID_API static uint32_t getStencilSize(); + ANDROID_API static uint8_t getStencilSize(); /** * Returns the smallest stencil format accepted by render buffers. diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index 0b2c130..4eec462 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -147,7 +147,7 @@ void TextDropShadowCache::setMaxSize(uint32_t maxSize) { // Callbacks /////////////////////////////////////////////////////////////////////////////// -void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) { +void TextDropShadowCache::operator()(ShadowText&, ShadowTexture*& texture) { if (texture) { mSize -= texture->bitmapSize; @@ -168,7 +168,7 @@ void TextDropShadowCache::clear() { mCache.clear(); } -ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len, +ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, uint32_t len, int numGlyphs, float radius, const float* positions) { ShadowText entry(paint, radius, len, text, positions); ShadowTexture* texture = mCache.get(entry); diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index 04d7357..54b930b 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -38,7 +38,7 @@ struct ShadowText { } // len is the number of bytes in text - ShadowText(SkPaint* paint, float radius, uint32_t len, const char* srcText, + ShadowText(const SkPaint* paint, float radius, uint32_t len, const char* srcText, const float* positions): len(len), radius(radius), positions(positions) { // TODO: Propagate this through the API, we should not cast here @@ -135,7 +135,7 @@ public: */ void operator()(ShadowText& text, ShadowTexture*& texture); - ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len, + ShadowTexture* get(const SkPaint* paint, const char* text, uint32_t len, int numGlyphs, float radius, const float* positions); /** diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 7923ce7..e783905 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -25,14 +25,14 @@ namespace android { namespace uirenderer { Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0), - cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), + cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false), mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE), mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST), mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) { } Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0), - cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), + cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false), mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE), mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST), mFirstFilter(true), mFirstWrap(true), mCaches(caches) { diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index d48ec59..d5601f8 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -94,6 +94,12 @@ public: */ const UvMapper* uvMapper; + /** + * Whether or not the Texture is marked in use and thus not evictable for + * the current frame. This is reset at the start of a new frame. + */ + bool isInUse; + private: /** * Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE. diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index a9ab2c6..34e2265 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -34,7 +34,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// TextureCache::TextureCache(): - mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity), mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)), mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) { char property[PROPERTY_VALUE_MAX]; @@ -58,7 +58,7 @@ TextureCache::TextureCache(): } TextureCache::TextureCache(uint32_t maxByteSize): - mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity), mSize(0), mMaxSize(maxByteSize) { init(); } @@ -103,7 +103,7 @@ void TextureCache::setFlushRate(float flushRate) { // Callbacks /////////////////////////////////////////////////////////////////////////////// -void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) { +void TextureCache::operator()(const SkBitmap*&, Texture*& texture) { // This will be called already locked if (texture) { mSize -= texture->bitmapSize; @@ -121,29 +121,49 @@ void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) { // Caching /////////////////////////////////////////////////////////////////////////////// -Texture* TextureCache::get(SkBitmap* bitmap) { +void TextureCache::resetMarkInUse() { + LruCache<const SkBitmap*, Texture*>::Iterator iter(mCache); + while (iter.next()) { + iter.value()->isInUse = false; + } +} + +bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) { + if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { + ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)", + bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize); + return false; + } + return true; +} + +// Returns a prepared Texture* that either is already in the cache or can fit +// in the cache (and is thus added to the cache) +Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) { Texture* texture = mCache.get(bitmap); if (!texture) { - if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { - ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)", - bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize); + if (!canMakeTextureFromBitmap(bitmap)) { return NULL; } const uint32_t size = bitmap->rowBytes() * bitmap->height(); + bool canCache = size < mMaxSize; // Don't even try to cache a bitmap that's bigger than the cache - if (size < mMaxSize) { - while (mSize + size > mMaxSize) { + while (canCache && mSize + size > mMaxSize) { + Texture* oldest = mCache.peekOldestValue(); + if (oldest && !oldest->isInUse) { mCache.removeOldest(); + } else { + canCache = false; } } - texture = new Texture(); - texture->bitmapSize = size; - generateTexture(bitmap, texture, false); + if (canCache) { + texture = new Texture(); + texture->bitmapSize = size; + generateTexture(bitmap, texture, false); - if (size < mMaxSize) { mSize += size; TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d", bitmap, texture->id, size, mSize); @@ -151,17 +171,43 @@ Texture* TextureCache::get(SkBitmap* bitmap) { ALOGD("Texture created, size = %d", size); } mCache.put(bitmap, texture); - } else { - texture->cleanup = true; } - } else if (bitmap->getGenerationID() != texture->generation) { + } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) { + // Texture was in the cache but is dirty, re-upload + // TODO: Re-adjust the cache size if the bitmap's dimensions have changed generateTexture(bitmap, texture, true); } return texture; } -Texture* TextureCache::getTransient(SkBitmap* bitmap) { +bool TextureCache::prefetchAndMarkInUse(const SkBitmap* bitmap) { + Texture* texture = getCachedTexture(bitmap); + if (texture) { + texture->isInUse = true; + } + return texture; +} + +Texture* TextureCache::get(const SkBitmap* bitmap) { + Texture* texture = getCachedTexture(bitmap); + + if (!texture) { + if (!canMakeTextureFromBitmap(bitmap)) { + return NULL; + } + + const uint32_t size = bitmap->rowBytes() * bitmap->height(); + texture = new Texture(); + texture->bitmapSize = size; + generateTexture(bitmap, texture, false); + texture->cleanup = true; + } + + return texture; +} + +Texture* TextureCache::getTransient(const SkBitmap* bitmap) { Texture* texture = new Texture(); texture->bitmapSize = bitmap->rowBytes() * bitmap->height(); texture->cleanup = true; @@ -171,11 +217,11 @@ Texture* TextureCache::getTransient(SkBitmap* bitmap) { return texture; } -void TextureCache::remove(SkBitmap* bitmap) { +void TextureCache::remove(const SkBitmap* bitmap) { mCache.remove(bitmap); } -void TextureCache::removeDeferred(SkBitmap* bitmap) { +void TextureCache::removeDeferred(const SkBitmap* bitmap) { Mutex::Autolock _l(mLock); mGarbage.push(bitmap); } @@ -211,7 +257,7 @@ void TextureCache::flush() { } } -void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) { +void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) { SkAutoLockPixels alp(*bitmap); if (!bitmap->readyToDraw()) { @@ -239,7 +285,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege Caches::getInstance().bindTexture(texture->id); - switch (bitmap->getConfig()) { + switch (bitmap->config()) { case SkBitmap::kA8_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, 1); uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), @@ -267,7 +313,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege texture->blend = !bitmap->isOpaque(); break; default: - ALOGW("Unsupported bitmap config: %d", bitmap->getConfig()); + ALOGW("Unsupported bitmap config: %d", bitmap->config()); break; } @@ -284,7 +330,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege } } -void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap, +void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height) { SkBitmap rgbaBitmap; rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType()); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 57fc19a..48a10c2 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -49,7 +49,7 @@ namespace uirenderer { * Any texture added to the cache causing the cache to grow beyond the maximum * allowed size will also cause the oldest texture to be kicked out. */ -class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> { +class TextureCache: public OnEntryRemoved<const SkBitmap*, Texture*> { public: TextureCache(); TextureCache(uint32_t maxByteSize); @@ -59,28 +59,40 @@ public: * Used as a callback when an entry is removed from the cache. * Do not invoke directly. */ - void operator()(SkBitmap*& bitmap, Texture*& texture); + void operator()(const SkBitmap*& bitmap, Texture*& texture); + + /** + * Resets all Textures to not be marked as in use + */ + void resetMarkInUse(); + + /** + * Attempts to precache the SkBitmap. Returns true if a Texture was successfully + * acquired for the bitmap, false otherwise. If a Texture was acquired it is + * marked as in use. + */ + bool prefetchAndMarkInUse(const SkBitmap* bitmap); /** * Returns the texture associated with the specified bitmap. If the texture * cannot be found in the cache, a new texture is generated. */ - Texture* get(SkBitmap* bitmap); + Texture* get(const SkBitmap* bitmap); /** * Returns the texture associated with the specified bitmap. The generated * texture is not kept in the cache. The caller must destroy the texture. */ - Texture* getTransient(SkBitmap* bitmap); + Texture* getTransient(const SkBitmap* bitmap); /** * Removes the texture associated with the specified bitmap. * Upon remove the texture is freed. */ - void remove(SkBitmap* bitmap); + void remove(const SkBitmap* bitmap); /** * Removes the texture associated with the specified bitmap. This is meant * to be called from threads that are not the EGL context thread. */ - void removeDeferred(SkBitmap* bitmap); + void removeDeferred(const SkBitmap* bitmap); /** * Process deferred removals. */ @@ -116,21 +128,26 @@ public: void setFlushRate(float flushRate); private: + + bool canMakeTextureFromBitmap(const SkBitmap* bitmap); + + Texture* getCachedTexture(const SkBitmap* bitmap); + /** * Generates the texture from a bitmap into the specified texture structure. * * @param regenerate If true, the bitmap data is reuploaded into the texture, but * no new texture is generated. */ - void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); + void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false); - void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height); + void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height); void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); void init(); - LruCache<SkBitmap*, Texture*> mCache; + LruCache<const SkBitmap*, Texture*> mCache; uint32_t mSize; uint32_t mMaxSize; @@ -140,7 +157,7 @@ private: bool mDebugEnabled; - Vector<SkBitmap*> mGarbage; + Vector<const SkBitmap*> mGarbage; mutable Mutex mLock; }; // class TextureCache diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h new file mode 100644 index 0000000..fc5994c --- /dev/null +++ b/libs/hwui/TreeInfo.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 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 TREEINFO_H +#define TREEINFO_H + +#include <utils/Timers.h> + +namespace android { +namespace uirenderer { + +class BaseAnimator; +class AnimationListener; + +class AnimationHook { +public: + virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) = 0; +protected: + ~AnimationHook() {} +}; + +struct TreeInfo { + // The defaults here should be safe for everyone but DrawFrameTask to use as-is. + TreeInfo() + : frameTimeMs(0) + , animationHook(NULL) + , prepareTextures(false) + , performStagingPush(true) + , evaluateAnimations(false) + {} + + nsecs_t frameTimeMs; + AnimationHook* animationHook; + bool prepareTextures; + bool performStagingPush; + bool evaluateAnimations; + + struct Out { + Out() + : hasFunctors(false) + , hasAnimations(false) + , requiresUiRedraw(false) + {} + bool hasFunctors; + // This is only updated if evaluateAnimations is true + bool hasAnimations; + // This is set to true if there is an animation that RenderThread cannot + // animate itself, such as if hasFunctors is true + // This is only set if hasAnimations is true + bool requiresUiRedraw; + } out; + + // TODO: Damage calculations +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* TREEINFO_H */ diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index 497924e..c61cb61 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -36,6 +36,10 @@ struct Vector2 { x(px), y(py) { } + float lengthSquared() const { + return x * x + y * y; + } + float length() const { return sqrt(x * x + y * y); } @@ -107,6 +111,25 @@ struct Vector2 { } }; // class Vector2 +class Vector3 { +public: + float x; + float y; + float z; + + Vector3() : + x(0.0f), y(0.0f), z(0.0f) { + } + + Vector3(float px, float py, float pz) : + x(px), y(py), z(pz) { + } + + void dump() { + ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z); + } +}; + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index 790d4fc..5d7a199 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -33,13 +33,14 @@ struct Vertex { * Program::set()), and used to make geometry damage rect calculation conservative (see * Rect::snapGeometryToPixelBoundaries()) */ - static const float gGeometryFudgeFactor = 0.0656f; + static float GeometryFudgeFactor() { return 0.0656f; } - float position[2]; + + float x, y; static inline void set(Vertex* vertex, float x, float y) { - vertex[0].position[0] = x; - vertex[0].position[1] = y; + vertex[0].x = x; + vertex[0].y = y; } static inline void set(Vertex* vertex, vec2 val) { @@ -47,7 +48,7 @@ struct Vertex { } static inline void copyWithOffset(Vertex* vertex, const Vertex& src, float x, float y) { - set(vertex, src.position[0] + x, src.position[1] + y); + set(vertex, src.x + x, src.y + y); } }; // struct Vertex @@ -56,19 +57,19 @@ struct Vertex { * Simple structure to describe a vertex with a position and texture UV. */ struct TextureVertex { - float position[2]; - float texture[2]; + float x, y; + float u, v; static inline void set(TextureVertex* vertex, float x, float y, float u, float v) { - vertex[0].position[0] = x; - vertex[0].position[1] = y; - vertex[0].texture[0] = u; - vertex[0].texture[1] = v; + vertex[0].x = x; + vertex[0].y = y; + vertex[0].u = u; + vertex[0].v = v; } static inline void setUV(TextureVertex* vertex, float u, float v) { - vertex[0].texture[0] = u; - vertex[0].texture[1] = v; + vertex[0].u = u; + vertex[0].v = v; } }; // struct TextureVertex @@ -76,17 +77,17 @@ struct TextureVertex { * Simple structure to describe a vertex with a position, texture UV and ARGB color. */ struct ColorTextureVertex : TextureVertex { - float color[4]; + float r, g, b, a; static inline void set(ColorTextureVertex* vertex, float x, float y, float u, float v, int color) { TextureVertex::set(vertex, x, y, u, v); const float a = ((color >> 24) & 0xff) / 255.0f; - vertex[0].color[0] = a * ((color >> 16) & 0xff) / 255.0f; - vertex[0].color[1] = a * ((color >> 8) & 0xff) / 255.0f; - vertex[0].color[2] = a * ((color ) & 0xff) / 255.0f; - vertex[0].color[3] = a; + vertex[0].r = a * ((color >> 16) & 0xff) / 255.0f; + vertex[0].g = a * ((color >> 8) & 0xff) / 255.0f; + vertex[0].b = a * ((color ) & 0xff) / 255.0f; + vertex[0].a = a; } }; // struct ColorTextureVertex @@ -103,7 +104,7 @@ struct AlphaVertex : Vertex { static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src, float x, float y) { - Vertex::set(vertex, src.position[0] + x, src.position[1] + y); + Vertex::set(vertex, src.x + x, src.y + y); vertex[0].alpha = src.alpha; } diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h new file mode 100644 index 0000000..8b6872e --- /dev/null +++ b/libs/hwui/VertexBuffer.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 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 ANDROID_HWUI_VERTEX_BUFFER_H +#define ANDROID_HWUI_VERTEX_BUFFER_H + + +namespace android { +namespace uirenderer { + +class VertexBuffer { +public: + VertexBuffer(): + mBuffer(0), + mVertexCount(0), + mCleanupMethod(NULL) + {} + + ~VertexBuffer() { + if (mCleanupMethod) mCleanupMethod(mBuffer); + } + + /** + This should be the only method used by the Tessellator. Subsequent calls to + alloc will allocate space within the first allocation (useful if you want to + eventually allocate multiple regions within a single VertexBuffer, such as + with PathTessellator::tesselateLines()) + */ + template <class TYPE> + TYPE* alloc(int vertexCount) { + if (mVertexCount) { + TYPE* reallocBuffer = (TYPE*)mReallocBuffer; + // already have allocated the buffer, re-allocate space within + if (mReallocBuffer != mBuffer) { + // not first re-allocation, leave space for degenerate triangles to separate strips + reallocBuffer += 2; + } + mReallocBuffer = reallocBuffer + vertexCount; + return reallocBuffer; + } + mVertexCount = vertexCount; + mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount]; + mCleanupMethod = &(cleanup<TYPE>); + + return (TYPE*)mBuffer; + } + + template <class TYPE> + void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) { + int verticesToCopy = srcBuffer.getVertexCount(); + + TYPE* dst = alloc<TYPE>(verticesToCopy); + TYPE* src = (TYPE*)srcBuffer.getBuffer(); + + for (int i = 0; i < verticesToCopy; i++) { + TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset); + } + } + + const void* getBuffer() const { return mBuffer; } + unsigned int getVertexCount() const { return mVertexCount; } + + template <class TYPE> + void createDegenerateSeparators(int allocSize) { + TYPE* end = (TYPE*)mBuffer + mVertexCount; + for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) { + memcpy(degen, degen - 1, sizeof(TYPE)); + memcpy(degen + 1, degen + 2, sizeof(TYPE)); + } + } + +private: + template <class TYPE> + static void cleanup(void* buffer) { + delete[] (TYPE*)buffer; + } + + void* mBuffer; + unsigned int mVertexCount; + + void* mReallocBuffer; // used for multi-allocation + + void (*mCleanupMethod)(void*); +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_VERTEX_BUFFER_H diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index d5f38b5..24ffb80 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -114,7 +114,7 @@ CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint3 mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount), mCaches(Caches::getInstance()) { mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, - mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true); + mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE); // OpenGL ES 3.0+ lets us specify the row length for unpack operations such // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture. @@ -143,7 +143,7 @@ void CacheTexture::init() { // reset, then create a new remainder space to start again reset(); mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, - mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true); + mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE); } void CacheTexture::releaseMesh() { diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h index 61b38f8..4cc4f22 100644 --- a/libs/hwui/font/CacheTexture.h +++ b/libs/hwui/font/CacheTexture.h @@ -54,7 +54,7 @@ struct CacheBlock { CacheBlock* mNext; CacheBlock* mPrev; - CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false): + CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height): mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) { } diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index 0a6e6ef..d22cb8a 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -268,7 +268,7 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float glyph->mCacheTexture); } -CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) { +CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bool precaching) { CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(textUnit); if (cachedGlyph) { // Is the glyph still in texture cache? @@ -284,14 +284,14 @@ CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool pre return cachedGlyph; } -void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, +void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, const float* positions) { render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL, positions); } -void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, - int numGlyphs, SkPath* path, float hOffset, float vOffset) { +void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, + int numGlyphs, const SkPath* path, float hOffset, float vOffset) { if (numGlyphs == 0 || text == NULL || len == 0) { return; } @@ -340,7 +340,7 @@ void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len } } -void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds, const float* positions) { if (bounds == NULL) { ALOGE("No return rectangle provided to measure text"); @@ -350,7 +350,7 @@ void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t le render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions); } -void Font::precache(SkPaint* paint, const char* text, int numGlyphs) { +void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { ATRACE_NAME("precacheText"); if (numGlyphs == 0 || text == NULL) { @@ -371,7 +371,7 @@ void Font::precache(SkPaint* paint, const char* text, int numGlyphs) { } } -void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { if (numGlyphs == 0 || text == NULL || len == 0) { @@ -417,8 +417,8 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len } } -void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, SkGlyphCache* skiaGlyphCache, - CachedGlyphInfo* glyph, bool precaching) { +void Font::updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph, + SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching) { glyph->mAdvanceX = skiaGlyph.fAdvanceX; glyph->mAdvanceY = skiaGlyph.fAdvanceY; glyph->mBitmapLeft = skiaGlyph.fLeft; @@ -461,7 +461,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, SkGlyphCac } } -CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) { +CachedGlyphInfo* Font::cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching) { CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); mCachedGlyphs.add(glyph, newGlyph); diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h index 1e0e0c8..6ddd4f2 100644 --- a/libs/hwui/font/Font.h +++ b/libs/hwui/font/Font.h @@ -79,11 +79,11 @@ public: ~Font(); - void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, const float* positions); - void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, - int numGlyphs, SkPath* path, float hOffset, float vOffset); + void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, + int numGlyphs, const SkPath* path, float hOffset, float vOffset); const Font::FontDescription& getDescription() const { return mDescription; @@ -108,20 +108,20 @@ private: MEASURE, }; - void precache(SkPaint* paint, const char* text, int numGlyphs); + void precache(const SkPaint* paint, const char* text, int numGlyphs); - void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, + void render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions); - void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + void measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds, const float* positions); void invalidateTextureCache(CacheTexture* cacheTexture = NULL); - CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching); - void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, SkGlyphCache* skiaGlyphCache, - CachedGlyphInfo* glyph, bool precaching); + CachedGlyphInfo* cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching); + void updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph, + SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching); void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, @@ -138,7 +138,8 @@ private: void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, SkPathMeasure& measure, SkPoint* position, SkVector* tangent); - CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false); + CachedGlyphInfo* getCachedGlyph(const SkPaint* paint, glyph_t textUnit, + bool precaching = false); FontRenderer* mState; FontDescription mDescription; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp new file mode 100644 index 0000000..5a23158 --- /dev/null +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "CanvasContext" + +#include "CanvasContext.h" + +#include <cutils/properties.h> +#include <private/hwui/DrawGlInfo.h> +#include <strings.h> + +#include "RenderThread.h" +#include "../Caches.h" +#include "../DeferredLayerUpdater.h" +#include "../LayerRenderer.h" +#include "../OpenGLRenderer.h" +#include "../Stencil.h" + +#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" +#define GLES_VERSION 2 + +#ifdef USE_OPENGL_RENDERER +// Android-specific addition that is used to show when frames began in systrace +EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); +#endif + +namespace android { +namespace uirenderer { +namespace renderthread { + +#define ERROR_CASE(x) case x: return #x; +static const char* egl_error_str(EGLint error) { + switch (error) { + ERROR_CASE(EGL_SUCCESS) + ERROR_CASE(EGL_NOT_INITIALIZED) + ERROR_CASE(EGL_BAD_ACCESS) + ERROR_CASE(EGL_BAD_ALLOC) + ERROR_CASE(EGL_BAD_ATTRIBUTE) + ERROR_CASE(EGL_BAD_CONFIG) + ERROR_CASE(EGL_BAD_CONTEXT) + ERROR_CASE(EGL_BAD_CURRENT_SURFACE) + ERROR_CASE(EGL_BAD_DISPLAY) + ERROR_CASE(EGL_BAD_MATCH) + ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) + ERROR_CASE(EGL_BAD_NATIVE_WINDOW) + ERROR_CASE(EGL_BAD_PARAMETER) + ERROR_CASE(EGL_BAD_SURFACE) + ERROR_CASE(EGL_CONTEXT_LOST) + default: + return "Unknown error"; + } +} +static const char* egl_error_str() { + return egl_error_str(eglGetError()); +} + +static bool load_dirty_regions_property() { + char buf[PROPERTY_VALUE_MAX]; + int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); + return !strncasecmp("true", buf, len); +} + +// This class contains the shared global EGL objects, such as EGLDisplay +// and EGLConfig, which are re-used by CanvasContext +class GlobalContext { +public: + static GlobalContext* get(); + + // Returns true on success, false on failure + void initialize(); + + bool hasContext(); + + void usePBufferSurface(); + EGLSurface createSurface(EGLNativeWindowType window); + void destroySurface(EGLSurface surface); + + void destroy(); + + bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } + // Returns true if the current surface changed, false if it was already current + bool makeCurrent(EGLSurface surface); + void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); + void swapBuffers(EGLSurface surface); + + bool enableDirtyRegions(EGLSurface surface); + +private: + GlobalContext(); + // GlobalContext is never destroyed, method is purposely not implemented + ~GlobalContext(); + + void loadConfig(); + void createContext(); + void initAtlas(); + + static GlobalContext* sContext; + + EGLDisplay mEglDisplay; + EGLConfig mEglConfig; + EGLContext mEglContext; + EGLSurface mPBufferSurface; + + const bool mRequestDirtyRegions; + bool mCanSetDirtyRegions; + + EGLSurface mCurrentSurface; +}; + +GlobalContext* GlobalContext::sContext = 0; + +GlobalContext* GlobalContext::get() { + if (!sContext) { + sContext = new GlobalContext(); + } + return sContext; +} + +GlobalContext::GlobalContext() + : mEglDisplay(EGL_NO_DISPLAY) + , mEglConfig(0) + , mEglContext(EGL_NO_CONTEXT) + , mPBufferSurface(EGL_NO_SURFACE) + , mRequestDirtyRegions(load_dirty_regions_property()) + , mCurrentSurface(EGL_NO_SURFACE) { + mCanSetDirtyRegions = mRequestDirtyRegions; + ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); +} + +void GlobalContext::initialize() { + if (hasContext()) return; + + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, + "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); + + EGLint major, minor; + LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, + "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); + + ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); + + loadConfig(); + createContext(); + usePBufferSurface(); + Caches::getInstance().init(); + initAtlas(); +} + +bool GlobalContext::hasContext() { + return mEglDisplay != EGL_NO_DISPLAY; +} + +void GlobalContext::loadConfig() { + EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + EGLint attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_STENCIL_SIZE, Stencil::getStencilSize(), + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, + EGL_NONE + }; + + EGLint num_configs = 1; + if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) + || num_configs != 1) { + // Failed to get a valid config + if (mCanSetDirtyRegions) { + ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); + // Try again without dirty regions enabled + mCanSetDirtyRegions = false; + loadConfig(); + } else { + LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); + } + } +} + +void GlobalContext::createContext() { + EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; + mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); + LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, + "Failed to create context, error = %s", egl_error_str()); +} + +void GlobalContext::initAtlas() { + // TODO implement + // For now just run without an atlas +} + +void GlobalContext::usePBufferSurface() { + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, + "usePBufferSurface() called on uninitialized GlobalContext!"); + + if (mPBufferSurface == EGL_NO_SURFACE) { + EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; + mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); + } + makeCurrent(mPBufferSurface); +} + +EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) { + initialize(); + return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); +} + +void GlobalContext::destroySurface(EGLSurface surface) { + if (isCurrent(surface)) { + makeCurrent(EGL_NO_SURFACE); + } + if (!eglDestroySurface(mEglDisplay, surface)) { + ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); + } +} + +void GlobalContext::destroy() { + if (mEglDisplay == EGL_NO_DISPLAY) return; + + usePBufferSurface(); + if (Caches::hasInstance()) { + Caches::getInstance().terminate(); + } + + eglDestroyContext(mEglDisplay, mEglContext); + eglDestroySurface(mEglDisplay, mPBufferSurface); + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mEglDisplay); + eglReleaseThread(); + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + mPBufferSurface = EGL_NO_SURFACE; + mCurrentSurface = EGL_NO_SURFACE; +} + +bool GlobalContext::makeCurrent(EGLSurface surface) { + if (isCurrent(surface)) return false; + + if (surface == EGL_NO_SURFACE) { + // If we are setting EGL_NO_SURFACE we don't care about any of the potential + // return errors, which would only happen if mEglDisplay had already been + // destroyed in which case the current context is already NO_CONTEXT + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { + LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", + (void*)surface, egl_error_str()); + } + mCurrentSurface = surface; + return true; +} + +void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { + LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, + "Tried to beginFrame on EGL_NO_SURFACE!"); + makeCurrent(surface); + if (width) { + eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width); + } + if (height) { + eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); + } + eglBeginFrame(mEglDisplay, surface); +} + +void GlobalContext::swapBuffers(EGLSurface surface) { + eglSwapBuffers(mEglDisplay, surface); + EGLint err = eglGetError(); + LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS, + "Encountered EGL error %d %s during rendering", err, egl_error_str(err)); +} + +bool GlobalContext::enableDirtyRegions(EGLSurface surface) { + if (!mRequestDirtyRegions) return false; + + if (mCanSetDirtyRegions) { + if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { + ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", + (void*) surface, egl_error_str()); + return false; + } + return true; + } + // Perhaps it is already enabled? + EGLint value; + if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { + ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", + (void*) surface, egl_error_str()); + return false; + } + return value == EGL_BUFFER_PRESERVED; +} + +CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode) + : mRenderThread(RenderThread::getInstance()) + , mEglSurface(EGL_NO_SURFACE) + , mDirtyRegionsEnabled(false) + , mOpaque(!translucent) + , mCanvas(0) + , mHaveNewSurface(false) + , mRootRenderNode(rootRenderNode) { + mGlobalContext = GlobalContext::get(); +} + +CanvasContext::~CanvasContext() { + destroyCanvasAndSurface(); + mRenderThread.removeFrameCallback(this); +} + +void CanvasContext::destroyCanvasAndSurface() { + if (mCanvas) { + delete mCanvas; + mCanvas = 0; + } + setSurface(NULL); +} + +void CanvasContext::setSurface(EGLNativeWindowType window) { + if (mEglSurface != EGL_NO_SURFACE) { + mGlobalContext->destroySurface(mEglSurface); + mEglSurface = EGL_NO_SURFACE; + } + + if (window) { + mEglSurface = mGlobalContext->createSurface(window); + LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, + "Failed to create EGLSurface for window %p, eglErr = %s", + (void*) window, egl_error_str()); + } + + if (mEglSurface != EGL_NO_SURFACE) { + mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); + mHaveNewSurface = true; + makeCurrent(); + } else { + mRenderThread.removeFrameCallback(this); + } +} + +void CanvasContext::swapBuffers() { + mGlobalContext->swapBuffers(mEglSurface); + mHaveNewSurface = false; +} + +void CanvasContext::requireSurface() { + LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, + "requireSurface() called but no surface set!"); + makeCurrent(); +} + +bool CanvasContext::initialize(EGLNativeWindowType window) { + if (mCanvas) return false; + setSurface(window); + mCanvas = new OpenGLRenderer(); + mCanvas->initProperties(); + return true; +} + +void CanvasContext::updateSurface(EGLNativeWindowType window) { + setSurface(window); +} + +void CanvasContext::pauseSurface(EGLNativeWindowType window) { + // TODO: For now we just need a fence, in the future suspend any animations + // and such to prevent from trying to render into this surface +} + +void CanvasContext::setup(int width, int height) { + if (!mCanvas) return; + mCanvas->setViewport(width, height); +} + +void CanvasContext::setOpaque(bool opaque) { + mOpaque = opaque; +} + +void CanvasContext::makeCurrent() { + // TODO: Figure out why this workaround is needed, see b/13913604 + // In the meantime this matches the behavior of GLRenderer, so it is not a regression + mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface); +} + +void CanvasContext::prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, + TreeInfo& info) { + LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot prepareDraw without a canvas!"); + makeCurrent(); + + processLayerUpdates(layerUpdaters, info); + if (info.out.hasAnimations) { + // TODO: Uh... crap? + } + prepareTree(info); +} + +void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, + TreeInfo& info) { + for (size_t i = 0; i < layerUpdaters->size(); i++) { + DeferredLayerUpdater* update = layerUpdaters->itemAt(i); + bool success = update->apply(info); + LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); + if (update->backingLayer()->deferredUpdateScheduled) { + mCanvas->pushLayerUpdate(update->backingLayer()); + } + } +} + +void CanvasContext::prepareTree(TreeInfo& info) { + mRenderThread.removeFrameCallback(this); + + info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); + mRootRenderNode->prepareTree(info); + + if (info.out.hasAnimations) { + if (info.out.hasFunctors) { + info.out.requiresUiRedraw = true; + } else if (!info.out.requiresUiRedraw) { + // If animationsNeedsRedraw is set don't bother posting for an RT anim + // as we will just end up fighting the UI thread. + mRenderThread.postFrameCallback(this); + } + } +} + +void CanvasContext::draw(Rect* dirty) { + LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, + "drawDisplayList called on a context with no canvas or surface!"); + + EGLint width, height; + mGlobalContext->beginFrame(mEglSurface, &width, &height); + if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { + mCanvas->setViewport(width, height); + dirty = NULL; + } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { + dirty = NULL; + } + + status_t status; + if (dirty && !dirty->isEmpty()) { + status = mCanvas->prepareDirty(dirty->left, dirty->top, + dirty->right, dirty->bottom, mOpaque); + } else { + status = mCanvas->prepare(mOpaque); + } + + Rect outBounds; + status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); + + // TODO: Draw debug info + // TODO: Performance tracking + + mCanvas->finish(); + + if (status & DrawGlInfo::kStatusDrew) { + swapBuffers(); + } +} + +// Called by choreographer to do an RT-driven animation +void CanvasContext::doFrame() { + if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { + return; + } + + ATRACE_CALL(); + + TreeInfo info; + info.evaluateAnimations = true; + info.performStagingPush = false; + info.prepareTextures = false; + + prepareTree(info); + draw(NULL); +} + +void CanvasContext::invokeFunctor(Functor* functor) { + ATRACE_CALL(); + DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; + if (mGlobalContext->hasContext()) { + requireGlContext(); + mode = DrawGlInfo::kModeProcess; + } + (*functor)(mode, NULL); + + if (mCanvas) { + mCanvas->resume(); + } +} + +bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { + requireGlContext(); + TreeInfo info; + layer->apply(info); + return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); +} + +void CanvasContext::runWithGlContext(RenderTask* task) { + requireGlContext(); + task->run(); +} + +Layer* CanvasContext::createRenderLayer(int width, int height) { + requireSurface(); + return LayerRenderer::createRenderLayer(width, height); +} + +Layer* CanvasContext::createTextureLayer() { + requireSurface(); + return LayerRenderer::createTextureLayer(); +} + +void CanvasContext::requireGlContext() { + if (mEglSurface != EGL_NO_SURFACE) { + makeCurrent(); + } else { + mGlobalContext->usePBufferSurface(); + } +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h new file mode 100644 index 0000000..dcb9858 --- /dev/null +++ b/libs/hwui/renderthread/CanvasContext.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 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 CANVASCONTEXT_H_ +#define CANVASCONTEXT_H_ + +#include <cutils/compiler.h> +#include <EGL/egl.h> +#include <SkBitmap.h> +#include <utils/Functor.h> +#include <utils/Vector.h> + +#include "../RenderNode.h" +#include "RenderTask.h" +#include "RenderThread.h" + +#define FUNCTOR_PROCESS_DELAY 4 + +namespace android { +namespace uirenderer { + +class DeferredLayerUpdater; +class OpenGLRenderer; +class Rect; +class Layer; + +namespace renderthread { + +class GlobalContext; + +// This per-renderer class manages the bridge between the global EGL context +// and the render surface. +class CanvasContext : public IFrameCallback { +public: + CanvasContext(bool translucent, RenderNode* rootRenderNode); + virtual ~CanvasContext(); + + bool initialize(EGLNativeWindowType window); + void updateSurface(EGLNativeWindowType window); + void pauseSurface(EGLNativeWindowType window); + void setup(int width, int height); + void setOpaque(bool opaque); + void makeCurrent(); + void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info); + void draw(Rect* dirty); + void destroyCanvasAndSurface(); + + // IFrameCallback, Chroreographer-driven frame callback entry point + virtual void doFrame(); + + bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); + + void invokeFunctor(Functor* functor); + + void runWithGlContext(RenderTask* task); + + Layer* createRenderLayer(int width, int height); + Layer* createTextureLayer(); + +private: + void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info); + void prepareTree(TreeInfo& info); + + void setSurface(EGLNativeWindowType window); + void swapBuffers(); + void requireSurface(); + + void requireGlContext(); + + GlobalContext* mGlobalContext; + RenderThread& mRenderThread; + EGLSurface mEglSurface; + bool mDirtyRegionsEnabled; + + bool mOpaque; + OpenGLRenderer* mCanvas; + bool mHaveNewSurface; + + const sp<RenderNode> mRootRenderNode; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* CANVASCONTEXT_H_ */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp new file mode 100644 index 0000000..3b8786c --- /dev/null +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2014 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. + */ + +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include "DrawFrameTask.h" + +#include <utils/Log.h> +#include <utils/Trace.h> + +#include "../DisplayList.h" +#include "../RenderNode.h" +#include "CanvasContext.h" +#include "RenderThread.h" + +namespace android { +namespace uirenderer { +namespace renderthread { + +DrawFrameTask::DrawFrameTask() + : mRenderThread(NULL) + , mContext(NULL) + , mFrameTimeNanos(0) + , mSyncResult(kSync_OK) { +} + +DrawFrameTask::~DrawFrameTask() { +} + +void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) { + mRenderThread = thread; + mContext = context; +} + +void DrawFrameTask::addLayer(DeferredLayerUpdater* layer) { + LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to addLayer with!"); + + mLayers.push(layer); +} + +void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) { + for (size_t i = 0; i < mLayers.size(); i++) { + if (mLayers[i] == layer) { + mLayers.removeAt(i); + break; + } + } +} + +void DrawFrameTask::setDirty(int left, int top, int right, int bottom) { + mDirty.set(left, top, right, bottom); +} + +int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) { + LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); + + mSyncResult = kSync_OK; + mFrameTimeNanos = frameTimeNanos; + postAndWait(); + + // Reset the single-frame data + mFrameTimeNanos = 0; + mDirty.setEmpty(); + + return mSyncResult; +} + +void DrawFrameTask::postAndWait() { + AutoMutex _lock(mLock); + mRenderThread->queue(this); + mSignal.wait(mLock); +} + +void DrawFrameTask::run() { + ATRACE_NAME("DrawFrame"); + + bool canUnblockUiThread = syncFrameState(); + + // Grab a copy of everything we need + Rect dirty(mDirty); + CanvasContext* context = mContext; + + // From this point on anything in "this" is *UNSAFE TO ACCESS* + if (canUnblockUiThread) { + unblockUiThread(); + } + + context->draw(&dirty); + + if (!canUnblockUiThread) { + unblockUiThread(); + } +} + +static void initTreeInfo(TreeInfo& info) { + info.prepareTextures = true; + info.performStagingPush = true; + info.evaluateAnimations = true; +} + +bool DrawFrameTask::syncFrameState() { + ATRACE_CALL(); + mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos); + mContext->makeCurrent(); + Caches::getInstance().textureCache.resetMarkInUse(); + TreeInfo info; + initTreeInfo(info); + mContext->prepareDraw(&mLayers, info); + if (info.out.hasAnimations) { + // TODO: dirty calculations, for now just do a full-screen inval + mDirty.setEmpty(); + if (info.out.requiresUiRedraw) { + mSyncResult |= kSync_UIRedrawRequired; + } + } + // If prepareTextures is false, we ran out of texture cache space + return !info.out.hasFunctors && info.prepareTextures; +} + +void DrawFrameTask::unblockUiThread() { + AutoMutex _lock(mLock); + mSignal.signal(); +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h new file mode 100644 index 0000000..b9307e1 --- /dev/null +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2014 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 DRAWFRAMETASK_H +#define DRAWFRAMETASK_H + +#include <utils/Condition.h> +#include <utils/Mutex.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + +#include "RenderTask.h" + +#include "../Rect.h" + +namespace android { +namespace uirenderer { + +class DeferredLayerUpdater; +class DisplayListData; +class RenderNode; + +namespace renderthread { + +class CanvasContext; +class RenderThread; + +enum SyncResult { + kSync_OK = 0, + kSync_UIRedrawRequired = 1 << 1, +}; + +/* + * This is a special Super Task. It is re-used multiple times by RenderProxy, + * and contains state (such as layer updaters & new DisplayListDatas) that is + * tracked across many frames not just a single frame. + * It is the sync-state task, and will kick off the post-sync draw + */ +class DrawFrameTask : public RenderTask { +public: + DrawFrameTask(); + virtual ~DrawFrameTask(); + + void setContext(RenderThread* thread, CanvasContext* context); + + void addLayer(DeferredLayerUpdater* layer); + void removeLayer(DeferredLayerUpdater* layer); + + void setDirty(int left, int top, int right, int bottom); + int drawFrame(nsecs_t frameTimeNanos); + + virtual void run(); + +private: + void postAndWait(); + bool syncFrameState(); + void unblockUiThread(); + + Mutex mLock; + Condition mSignal; + + RenderThread* mRenderThread; + CanvasContext* mContext; + + /********************************************* + * Single frame data + *********************************************/ + Rect mDirty; + nsecs_t mFrameTimeNanos; + + int mSyncResult; + + /********************************************* + * Multi frame data + *********************************************/ + Vector<DeferredLayerUpdater*> mLayers; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* DRAWFRAMETASK_H */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp new file mode 100644 index 0000000..82a2dbc --- /dev/null +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "RenderProxy" + +#include "RenderProxy.h" + +#include "CanvasContext.h" +#include "RenderTask.h" +#include "RenderThread.h" + +#include "../DeferredLayerUpdater.h" +#include "../DisplayList.h" +#include "../LayerRenderer.h" +#include "../Rect.h" + +namespace android { +namespace uirenderer { +namespace renderthread { + +#define ARGS(method) method ## Args + +#define CREATE_BRIDGE0(name) CREATE_BRIDGE(name,,,,,,,,) +#define CREATE_BRIDGE1(name, a1) CREATE_BRIDGE(name, a1,,,,,,,) +#define CREATE_BRIDGE2(name, a1, a2) CREATE_BRIDGE(name, a1,a2,,,,,,) +#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,) +#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,) +#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \ + typedef struct { \ + a1; a2; a3; a4; a5; a6; a7; a8; \ + } ARGS(name); \ + static void* Bridge_ ## name(ARGS(name)* args) + +#define SETUP_TASK(method) \ + LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \ + "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \ + METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \ + MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ + ARGS(method) *args = (ARGS(method) *) task->payload() + +CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) { + return new CanvasContext(args->translucent, args->rootRenderNode); +} + +RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) + : mRenderThread(RenderThread::getInstance()) + , mContext(0) { + SETUP_TASK(createContext); + args->translucent = translucent; + args->rootRenderNode = rootRenderNode; + mContext = (CanvasContext*) postAndWait(task); + mDrawFrameTask.setContext(&mRenderThread, mContext); +} + +RenderProxy::~RenderProxy() { + destroyContext(); +} + +CREATE_BRIDGE1(destroyContext, CanvasContext* context) { + delete args->context; + return NULL; +} + +void RenderProxy::destroyContext() { + if (mContext) { + SETUP_TASK(destroyContext); + args->context = mContext; + mContext = 0; + mDrawFrameTask.setContext(NULL, NULL); + // This is also a fence as we need to be certain that there are no + // outstanding mDrawFrame tasks posted before it is destroyed + postAndWait(task); + } +} + +CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) { + args->thread->timeLord().setFrameInterval(args->frameIntervalNanos); + return NULL; +} + +void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) { + SETUP_TASK(setFrameInterval); + args->thread = &mRenderThread; + args->frameIntervalNanos = frameIntervalNanos; + post(task); +} + +CREATE_BRIDGE0(loadSystemProperties) { + bool needsRedraw = false; + if (Caches::hasInstance()) { + needsRedraw = Caches::getInstance().initProperties(); + } + return (void*) needsRedraw; +} + +bool RenderProxy::loadSystemProperties() { + SETUP_TASK(loadSystemProperties); + return (bool) postAndWait(task); +} + +CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) { + return (void*) args->context->initialize(args->window); +} + +bool RenderProxy::initialize(const sp<ANativeWindow>& window) { + SETUP_TASK(initialize); + args->context = mContext; + args->window = window.get(); + return (bool) postAndWait(task); +} + +CREATE_BRIDGE2(updateSurface, CanvasContext* context, EGLNativeWindowType window) { + args->context->updateSurface(args->window); + return NULL; +} + +void RenderProxy::updateSurface(const sp<ANativeWindow>& window) { + SETUP_TASK(updateSurface); + args->context = mContext; + args->window = window.get(); + postAndWait(task); +} + +CREATE_BRIDGE2(pauseSurface, CanvasContext* context, EGLNativeWindowType window) { + args->context->pauseSurface(args->window); + return NULL; +} + +void RenderProxy::pauseSurface(const sp<ANativeWindow>& window) { + SETUP_TASK(pauseSurface); + args->context = mContext; + args->window = window.get(); + postAndWait(task); +} + +CREATE_BRIDGE3(setup, CanvasContext* context, int width, int height) { + args->context->setup(args->width, args->height); + return NULL; +} + +void RenderProxy::setup(int width, int height) { + SETUP_TASK(setup); + args->context = mContext; + args->width = width; + args->height = height; + post(task); +} + +CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) { + args->context->setOpaque(args->opaque); + return NULL; +} + +void RenderProxy::setOpaque(bool opaque) { + SETUP_TASK(setOpaque); + args->context = mContext; + args->opaque = opaque; + post(task); +} + +int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, + int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { + mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); + return mDrawFrameTask.drawFrame(frameTimeNanos); +} + +CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) { + args->context->destroyCanvasAndSurface(); + return NULL; +} + +void RenderProxy::destroyCanvasAndSurface() { + SETUP_TASK(destroyCanvasAndSurface); + args->context = mContext; + // destroyCanvasAndSurface() needs a fence as when it returns the + // underlying BufferQueue is going to be released from under + // the render thread. + postAndWait(task); +} + +CREATE_BRIDGE2(invokeFunctor, CanvasContext* context, Functor* functor) { + args->context->invokeFunctor(args->functor); + return NULL; +} + +void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { + ATRACE_CALL(); + SETUP_TASK(invokeFunctor); + args->context = mContext; + args->functor = functor; + if (waitForCompletion) { + postAndWait(task); + } else { + post(task); + } +} + +CREATE_BRIDGE2(runWithGlContext, CanvasContext* context, RenderTask* task) { + args->context->runWithGlContext(args->task); + return NULL; +} + +void RenderProxy::runWithGlContext(RenderTask* gltask) { + SETUP_TASK(runWithGlContext); + args->context = mContext; + args->task = gltask; + postAndWait(task); +} + +CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) { + Layer* layer = args->context->createRenderLayer(args->width, args->height); + if (!layer) return 0; + return new DeferredLayerUpdater(layer); +} + +DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) { + SETUP_TASK(createDisplayListLayer); + args->width = width; + args->height = height; + args->context = mContext; + void* retval = postAndWait(task); + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval); + mDrawFrameTask.addLayer(layer); + return layer; +} + +CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) { + Layer* layer = args->context->createTextureLayer(); + if (!layer) return 0; + return new DeferredLayerUpdater(layer); +} + +DeferredLayerUpdater* RenderProxy::createTextureLayer() { + SETUP_TASK(createTextureLayer); + args->context = mContext; + void* retval = postAndWait(task); + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval); + mDrawFrameTask.addLayer(layer); + return layer; +} + +CREATE_BRIDGE1(destroyLayer, Layer* layer) { + LayerRenderer::destroyLayer(args->layer); + return NULL; +} + +CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer, + SkBitmap* bitmap) { + bool success = args->context->copyLayerInto(args->layer, args->bitmap); + return (void*) success; +} + +bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { + SETUP_TASK(copyLayerInto); + args->context = mContext; + args->layer = layer; + args->bitmap = bitmap; + return (bool) postAndWait(task); +} + +void RenderProxy::destroyLayer(DeferredLayerUpdater* layer) { + mDrawFrameTask.removeLayer(layer); + SETUP_TASK(destroyLayer); + args->layer = layer->detachBackingLayer(); + post(task); +} + +CREATE_BRIDGE0(fence) { + // Intentionally empty + return NULL; +} + +void RenderProxy::fence() { + SETUP_TASK(fence); + postAndWait(task); +} + +void RenderProxy::post(RenderTask* task) { + mRenderThread.queue(task); +} + +void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) { + void* retval; + task->setReturnPtr(&retval); + SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition); + AutoMutex _lock(mSyncMutex); + mRenderThread.queue(&syncTask); + mSyncCondition.wait(mSyncMutex); + return retval; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h new file mode 100644 index 0000000..4a7e70a --- /dev/null +++ b/libs/hwui/renderthread/RenderProxy.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2013 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 RENDERPROXY_H_ +#define RENDERPROXY_H_ + +#include "RenderTask.h" + +#include <cutils/compiler.h> +#include <EGL/egl.h> +#include <SkBitmap.h> +#include <utils/Condition.h> +#include <utils/Functor.h> +#include <utils/Mutex.h> +#include <utils/Timers.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + +#include "DrawFrameTask.h" + +namespace android { +namespace uirenderer { + +class DeferredLayerUpdater; +class RenderNode; +class DisplayListData; +class Layer; +class Rect; + +namespace renderthread { + +class CanvasContext; +class ErrorChannel; +class RenderThread; +class RenderProxyBridge; + +/* + * RenderProxy is strictly single threaded. All methods must be invoked on the owning + * thread. It is important to note that RenderProxy may be deleted while it has + * tasks post()'d as a result. Therefore any RenderTask that is post()'d must not + * reference RenderProxy or any of its fields. The exception here is that postAndWait() + * references RenderProxy fields. This is safe as RenderProxy cannot + * be deleted if it is blocked inside a call. + */ +class ANDROID_API RenderProxy { +public: + ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode); + ANDROID_API virtual ~RenderProxy(); + + ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos); + ANDROID_API bool loadSystemProperties(); + + ANDROID_API bool initialize(const sp<ANativeWindow>& window); + ANDROID_API void updateSurface(const sp<ANativeWindow>& window); + ANDROID_API void pauseSurface(const sp<ANativeWindow>& window); + ANDROID_API void setup(int width, int height); + ANDROID_API void setOpaque(bool opaque); + ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, + int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); + ANDROID_API void destroyCanvasAndSurface(); + + ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion); + + ANDROID_API void runWithGlContext(RenderTask* task); + + ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height); + ANDROID_API DeferredLayerUpdater* createTextureLayer(); + ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); + ANDROID_API void destroyLayer(DeferredLayerUpdater* layer); + + ANDROID_API void fence(); + +private: + RenderThread& mRenderThread; + CanvasContext* mContext; + + DrawFrameTask mDrawFrameTask; + + Mutex mSyncMutex; + Condition mSyncCondition; + + void destroyContext(); + + void post(RenderTask* task); + void* postAndWait(MethodInvokeRenderTask* task); + + // Friend class to help with bridging + friend class RenderProxyBridge; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* RENDERPROXY_H_ */ diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp new file mode 100644 index 0000000..7ca61e4 --- /dev/null +++ b/libs/hwui/renderthread/RenderTask.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "RenderTask" + +#include "RenderTask.h" + +#include <utils/Log.h> +#include <utils/Condition.h> +#include <utils/Mutex.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +void SignalingRenderTask::run() { + mTask->run(); + mLock->lock(); + mSignal->signal(); + mLock->unlock(); +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h new file mode 100644 index 0000000..1554a16 --- /dev/null +++ b/libs/hwui/renderthread/RenderTask.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2013 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 RENDERTASK_H_ +#define RENDERTASK_H_ + +#include <cutils/compiler.h> +#include <utils/Timers.h> + +namespace android { +class Mutex; +class Condition; +namespace uirenderer { +namespace renderthread { + +#define METHOD_INVOKE_PAYLOAD_SIZE (8 * sizeof(void*)) + +/* + * Notes about memory management + * + * RenderThread will only invoke RenderTask::run(). It is the responsibility + * of the RenderTask to know if it needs to suicide at the end of run() or + * if some other lifecycle is being used. As such, it is not valid to reference + * anything on RenderTask after the first call to run(). + * + * For example SignalingRenderTask + * is expected to be stack allocated by the calling thread, so it does not + * suicide in run() but instead relies on the caller to destroy it. + * + * MethodInvokeRenderTask however is currently allocated with new, so it will + * suicide at the end of run(). TODO: Replace this with a small pool to avoid + * malloc/free churn of small objects? + */ + +class ANDROID_API RenderTask { +public: + ANDROID_API RenderTask() : mNext(0), mRunAt(0) {} + ANDROID_API virtual ~RenderTask() {} + + ANDROID_API virtual void run() = 0; + + RenderTask* mNext; + nsecs_t mRunAt; // nano-seconds on the SYSTEM_TIME_MONOTONIC clock +}; + +class SignalingRenderTask : public RenderTask { +public: + // Takes ownership of task, caller owns lock and signal + SignalingRenderTask(RenderTask* task, Mutex* lock, Condition* signal) + : mTask(task), mLock(lock), mSignal(signal) {} + virtual void run(); + +private: + RenderTask* mTask; + Mutex* mLock; + Condition* mSignal; +}; + +typedef void* (*RunnableMethod)(void* data); + +class MethodInvokeRenderTask : public RenderTask { +public: + MethodInvokeRenderTask(RunnableMethod method) + : mMethod(method), mReturnPtr(0) {} + + void* payload() { return mData; } + void setReturnPtr(void** retptr) { mReturnPtr = retptr; } + + virtual void run() { + void* retval = mMethod(mData); + if (mReturnPtr) { + *mReturnPtr = retval; + } + // Commit suicide + delete this; + } +private: + RunnableMethod mMethod; + char mData[METHOD_INVOKE_PAYLOAD_SIZE]; + void** mReturnPtr; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* RENDERTASK_H_ */ diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp new file mode 100644 index 0000000..35a3eab --- /dev/null +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "RenderThread" + +#include "RenderThread.h" + +#include <gui/DisplayEventReceiver.h> +#include <utils/Log.h> + +#include "CanvasContext.h" +#include "RenderProxy.h" + +namespace android { +using namespace uirenderer::renderthread; +ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread); + +namespace uirenderer { +namespace renderthread { + +// Number of events to read at a time from the DisplayEventReceiver pipe. +// The value should be large enough that we can quickly drain the pipe +// using just a few large reads. +static const size_t EVENT_BUFFER_SIZE = 100; + +// Slight delay to give the UI time to push us a new frame before we replay +static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0; + +TaskQueue::TaskQueue() : mHead(0), mTail(0) {} + +RenderTask* TaskQueue::next() { + RenderTask* ret = mHead; + if (ret) { + mHead = ret->mNext; + if (!mHead) { + mTail = 0; + } + ret->mNext = 0; + } + return ret; +} + +RenderTask* TaskQueue::peek() { + return mHead; +} + +void TaskQueue::queue(RenderTask* task) { + // Since the RenderTask itself forms the linked list it is not allowed + // to have the same task queued twice + LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!"); + if (mTail) { + // Fast path if we can just append + if (mTail->mRunAt <= task->mRunAt) { + mTail->mNext = task; + mTail = task; + } else { + // Need to find the proper insertion point + RenderTask* previous = 0; + RenderTask* next = mHead; + while (next && next->mRunAt <= task->mRunAt) { + previous = next; + next = next->mNext; + } + if (!previous) { + task->mNext = mHead; + mHead = task; + } else { + previous->mNext = task; + if (next) { + task->mNext = next; + } else { + mTail = task; + } + } + } + } else { + mTail = mHead = task; + } +} + +void TaskQueue::remove(RenderTask* task) { + // TaskQueue is strict here to enforce that users are keeping track of + // their RenderTasks due to how their memory is managed + LOG_ALWAYS_FATAL_IF(!task->mNext && mTail != task, + "Cannot remove a task that isn't in the queue!"); + + // If task is the head we can just call next() to pop it off + // Otherwise we need to scan through to find the task before it + if (peek() == task) { + next(); + } else { + RenderTask* previous = mHead; + while (previous->mNext != task) { + previous = previous->mNext; + } + previous->mNext = task->mNext; + if (mTail == task) { + mTail = previous; + } + } +} + +class DispatchFrameCallbacks : public RenderTask { +private: + RenderThread* mRenderThread; +public: + DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {} + + virtual void run() { + mRenderThread->dispatchFrameCallbacks(); + } +}; + +RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() + , mNextWakeup(LLONG_MAX) + , mDisplayEventReceiver(0) + , mVsyncRequested(false) + , mFrameCallbackTaskPending(false) + , mFrameCallbackTask(0) { + mFrameCallbackTask = new DispatchFrameCallbacks(this); + mLooper = new Looper(false); + run("RenderThread"); +} + +RenderThread::~RenderThread() { +} + +void RenderThread::initializeDisplayEventReceiver() { + LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?"); + mDisplayEventReceiver = new DisplayEventReceiver(); + status_t status = mDisplayEventReceiver->initCheck(); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver " + "failed with status: %d", status); + + // Register the FD + mLooper->addFd(mDisplayEventReceiver->getFd(), 0, + Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); +} + +int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("Display event receiver pipe was closed or an error occurred. " + "events=0x%x", events); + return 0; // remove the callback + } + + if (!(events & Looper::EVENT_INPUT)) { + ALOGW("Received spurious callback for unhandled poll event. " + "events=0x%x", events); + return 1; // keep the callback + } + + reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue(); + + return 1; // keep the callback +} + +static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) { + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + nsecs_t latest = 0; + ssize_t n; + while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + for (ssize_t i = 0; i < n; i++) { + const DisplayEventReceiver::Event& ev = buf[i]; + switch (ev.header.type) { + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: + latest = ev.header.timestamp; + break; + } + } + } + if (n < 0) { + ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); + } + return latest; +} + +void RenderThread::drainDisplayEventQueue() { + nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver); + if (vsyncEvent > 0) { + mVsyncRequested = false; + mTimeLord.vsyncReceived(vsyncEvent); + if (!mFrameCallbackTaskPending) { + mFrameCallbackTaskPending = true; + //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY); + queue(mFrameCallbackTask); + } + } +} + +void RenderThread::dispatchFrameCallbacks() { + mFrameCallbackTaskPending = false; + + std::set<IFrameCallback*> callbacks; + mFrameCallbacks.swap(callbacks); + + for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) { + (*it)->doFrame(); + } +} + +bool RenderThread::threadLoop() { + initializeDisplayEventReceiver(); + + int timeoutMillis = -1; + for (;;) { + int result = mLooper->pollOnce(timeoutMillis); + LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, + "RenderThread Looper POLL_ERROR!"); + + nsecs_t nextWakeup; + // Process our queue, if we have anything + while (RenderTask* task = nextTask(&nextWakeup)) { + task->run(); + // task may have deleted itself, do not reference it again + } + if (nextWakeup == LLONG_MAX) { + timeoutMillis = -1; + } else { + nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC); + timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos); + if (timeoutMillis < 0) { + timeoutMillis = 0; + } + } + } + + return false; +} + +void RenderThread::queue(RenderTask* task) { + AutoMutex _lock(mLock); + mQueue.queue(task); + if (mNextWakeup && task->mRunAt < mNextWakeup) { + mNextWakeup = 0; + mLooper->wake(); + } +} + +void RenderThread::queueDelayed(RenderTask* task, int delayMs) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + task->mRunAt = now + milliseconds_to_nanoseconds(delayMs); + queue(task); +} + +void RenderThread::remove(RenderTask* task) { + AutoMutex _lock(mLock); + mQueue.remove(task); +} + +void RenderThread::postFrameCallback(IFrameCallback* callback) { + mFrameCallbacks.insert(callback); + if (!mVsyncRequested) { + mVsyncRequested = true; + status_t status = mDisplayEventReceiver->requestNextVsync(); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, + "requestNextVsync failed with status: %d", status); + } +} + +void RenderThread::removeFrameCallback(IFrameCallback* callback) { + mFrameCallbacks.erase(callback); +} + +RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) { + AutoMutex _lock(mLock); + RenderTask* next = mQueue.peek(); + if (!next) { + mNextWakeup = LLONG_MAX; + } else { + // Most tasks won't be delayed, so avoid unnecessary systemTime() calls + if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) { + next = mQueue.next(); + } + mNextWakeup = next->mRunAt; + } + if (nextWakeup) { + *nextWakeup = mNextWakeup; + } + return next; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h new file mode 100644 index 0000000..215d294 --- /dev/null +++ b/libs/hwui/renderthread/RenderThread.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2013 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 RENDERTHREAD_H_ +#define RENDERTHREAD_H_ + +#include "RenderTask.h" + +#include <memory> +#include <set> + +#include <cutils/compiler.h> +#include <utils/Looper.h> +#include <utils/Mutex.h> +#include <utils/Singleton.h> +#include <utils/Thread.h> + +#include "TimeLord.h" + +namespace android { +class DisplayEventReceiver; + +namespace uirenderer { +namespace renderthread { + +class DispatchFrameCallbacks; + +class TaskQueue { +public: + TaskQueue(); + + RenderTask* next(); + void queue(RenderTask* task); + RenderTask* peek(); + void remove(RenderTask* task); + +private: + RenderTask* mHead; + RenderTask* mTail; +}; + +// Mimics android.view.Choreographer.FrameCallback +class IFrameCallback { +public: + virtual void doFrame() = 0; + +protected: + ~IFrameCallback() {} +}; + +class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> { +public: + // RenderThread takes complete ownership of tasks that are queued + // and will delete them after they are run + ANDROID_API void queue(RenderTask* task); + void queueDelayed(RenderTask* task, int delayMs); + void remove(RenderTask* task); + + // Mimics android.view.Choreographer + void postFrameCallback(IFrameCallback* callback); + void removeFrameCallback(IFrameCallback* callback); + + TimeLord& timeLord() { return mTimeLord; } + +protected: + virtual bool threadLoop(); + +private: + friend class Singleton<RenderThread>; + friend class DispatchFrameCallbacks; + + RenderThread(); + virtual ~RenderThread(); + + void initializeDisplayEventReceiver(); + static int displayEventReceiverCallback(int fd, int events, void* data); + void drainDisplayEventQueue(); + void dispatchFrameCallbacks(); + + // Returns the next task to be run. If this returns NULL nextWakeup is set + // to the time to requery for the nextTask to run. mNextWakeup is also + // set to this time + RenderTask* nextTask(nsecs_t* nextWakeup); + + sp<Looper> mLooper; + Mutex mLock; + + nsecs_t mNextWakeup; + TaskQueue mQueue; + + DisplayEventReceiver* mDisplayEventReceiver; + bool mVsyncRequested; + std::set<IFrameCallback*> mFrameCallbacks; + bool mFrameCallbackTaskPending; + DispatchFrameCallbacks* mFrameCallbackTask; + + TimeLord mTimeLord; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* RENDERTHREAD_H_ */ diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp new file mode 100644 index 0000000..758d96e --- /dev/null +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 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 "TimeLord.h" + +namespace android { +namespace uirenderer { +namespace renderthread { + +TimeLord::TimeLord() + : mFrameIntervalNanos(0) + , mFrameTimeNanos(0) { +} + +void TimeLord::vsyncReceived(nsecs_t vsync) { + if (vsync > mFrameTimeNanos) { + mFrameTimeNanos = vsync; + } +} + +nsecs_t TimeLord::frameTimeMs() { + // Logic copied from Choreographer.java + nsecs_t now = systemTime(CLOCK_MONOTONIC); + nsecs_t jitterNanos = now - mFrameTimeNanos; + if (jitterNanos >= mFrameIntervalNanos) { + nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos; + mFrameTimeNanos = now - lastFrameOffset; + } + return nanoseconds_to_milliseconds(mFrameTimeNanos); +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h new file mode 100644 index 0000000..52c6d9e --- /dev/null +++ b/libs/hwui/renderthread/TimeLord.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 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 TIMELORD_H +#define TIMELORD_H + +#include <utils/Timers.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class RenderThread; + +// This class serves as a helper to filter & manage frame times from multiple sources +// ensuring that time flows linearly and smoothly +class TimeLord { +public: + void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; } + void vsyncReceived(nsecs_t vsync); + nsecs_t frameTimeMs(); + +private: + friend class RenderThread; + + TimeLord(); + ~TimeLord() {} + + nsecs_t mFrameIntervalNanos; + nsecs_t mFrameTimeNanos; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* TIMELORD_H */ diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp index 85d90d0..c020b40 100644 --- a/libs/hwui/utils/Blur.cpp +++ b/libs/hwui/utils/Blur.cpp @@ -23,6 +23,31 @@ namespace android { namespace uirenderer { +// This constant approximates the scaling done in the software path's +// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)). +static const float BLUR_SIGMA_SCALE = 0.57735f; + +float Blur::convertRadiusToSigma(float radius) { + return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; +} + +float Blur::convertSigmaToRadius(float sigma) { + return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f; +} + +/** + * HWUI has used a slightly different equation than Skia to generate the value + * for sigma and to preserve compatibility we have kept that logic. + * + * Based on some experimental radius and sigma values we approximate the + * equation sigma = f(radius) as sigma = radius * 0.3 + 0.6. The larger the + * radius gets, the more our gaussian blur will resemble a box blur since with + * large sigma the gaussian curve begins to lose its shape. + */ +static float legacyConvertRadiusToSigma(float radius) { + return radius > 0 ? 0.3f * radius + 0.6f : 0.0f; +} + void Blur::generateGaussianWeights(float* weights, int32_t radius) { // Compute gaussian weights for the blur // e is the euler's number @@ -31,13 +56,7 @@ void Blur::generateGaussianWeights(float* weights, int32_t radius) { // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) // x is of the form [-radius .. 0 .. radius] // and sigma varies with radius. - // Based on some experimental radius values and sigma's - // we approximately fit sigma = f(radius) as - // sigma = radius * 0.3 + 0.6 - // The larger the radius gets, the more our gaussian blur - // will resemble a box blur since with large sigma - // the gaussian curve begins to lose its shape - float sigma = 0.3f * (float) radius + 0.6f; + float sigma = legacyConvertRadiusToSigma((float) radius); // Now compute the coefficints // We will store some redundant values to save some math during diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h index 6c176e9..79aff65 100644 --- a/libs/hwui/utils/Blur.h +++ b/libs/hwui/utils/Blur.h @@ -18,12 +18,18 @@ #define ANDROID_HWUI_BLUR_H #include <stdint.h> +#include <cutils/compiler.h> namespace android { namespace uirenderer { class Blur { public: + // If radius > 0, return the corresponding sigma, else return 0 + ANDROID_API static float convertRadiusToSigma(float radius); + // If sigma > 0.6, return the corresponding radius, else return 0 + ANDROID_API static float convertSigmaToRadius(float sigma); + static void generateGaussianWeights(float* weights, int32_t radius); static void horizontal(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest, int32_t width, int32_t height); diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp new file mode 100644 index 0000000..9b298ca --- /dev/null +++ b/libs/hwui/utils/GLUtils.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/Log.h> + +#include "GLUtils.h" + +namespace android { +namespace uirenderer { + +void GLUtils::dumpGLErrors() { + GLenum status = GL_NO_ERROR; + while ((status = glGetError()) != GL_NO_ERROR) { + switch (status) { + case GL_INVALID_ENUM: + ALOGE("GL error: GL_INVALID_ENUM"); + break; + case GL_INVALID_VALUE: + ALOGE("GL error: GL_INVALID_VALUE"); + break; + case GL_INVALID_OPERATION: + ALOGE("GL error: GL_INVALID_OPERATION"); + break; + case GL_OUT_OF_MEMORY: + ALOGE("GL error: Out of memory!"); + break; + default: + ALOGE("GL error: 0x%x", status); + } + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h new file mode 100644 index 0000000..890e374 --- /dev/null +++ b/libs/hwui/utils/GLUtils.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 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 GLUTILS_H +#define GLUTILS_H + +namespace android { +namespace uirenderer { + +class GLUtils { +private: +public: + /** + * Print out any GL errors with ALOGE + */ + static void dumpGLErrors(); + +}; // class GLUtils + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* GLUTILS_H */ diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h new file mode 100644 index 0000000..14a3ec0 --- /dev/null +++ b/libs/hwui/utils/Macros.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2014 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 MACROS_H +#define MACROS_H + +#define PREVENT_COPY_AND_ASSIGN(Type) \ + private: \ + Type(const Type&); \ + void operator=(const Type&) + + +#endif /* MACROS_H */ diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h new file mode 100644 index 0000000..8ba44dc --- /dev/null +++ b/libs/hwui/utils/MathUtils.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 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 MATHUTILS_H +#define MATHUTILS_H + +namespace android { +namespace uirenderer { + +class MathUtils { +private: + static const float gNonZeroEpsilon = 0.001f; +public: + /** + * Check for floats that are close enough to zero. + */ + inline static bool isZero(float value) { + return (value >= -gNonZeroEpsilon) && (value <= gNonZeroEpsilon); + } + + inline static bool isPositive(float value) { + return value >= gNonZeroEpsilon; + } +}; // class MathUtils + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* MATHUTILS_H */ diff --git a/libs/hwui/utils/VirtualLightRefBase.h b/libs/hwui/utils/VirtualLightRefBase.h new file mode 100644 index 0000000..b545aab --- /dev/null +++ b/libs/hwui/utils/VirtualLightRefBase.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 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 VIRTUALLIGHTREFBASE_H +#define VIRTUALLIGHTREFBASE_H + +#include <utils/RefBase.h> + +namespace android { +namespace uirenderer { + +// This is a wrapper around LightRefBase that simply enforces a virtual +// destructor to eliminate the template requirement of LightRefBase +class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> { +public: + virtual ~VirtualLightRefBase() {} +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* VIRTUALLIGHTREFBASE_H */ |