diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index d9a71fd8b4368..bb1691035a815 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -433,20 +433,16 @@ class GLES20Canvas extends HardwareCanvas { @Override public boolean clipPath(Path path) { - // TODO: Implement - path.computeBounds(mPathBounds, true); - return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top, - mPathBounds.right, mPathBounds.bottom, Region.Op.INTERSECT.nativeInt); + return nClipPath(mRenderer, path.mNativePath, Region.Op.INTERSECT.nativeInt); } @Override public boolean clipPath(Path path, Region.Op op) { - // TODO: Implement - path.computeBounds(mPathBounds, true); - return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top, - mPathBounds.right, mPathBounds.bottom, op.nativeInt); + return nClipPath(mRenderer, path.mNativePath, op.nativeInt); } + private static native boolean nClipPath(int renderer, int path, int op); + @Override public boolean clipRect(float left, float top, float right, float bottom) { return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); @@ -465,8 +461,8 @@ class GLES20Canvas extends HardwareCanvas { return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } - private static native boolean nClipRect(int renderer, int left, int top, int right, int bottom, - int op); + private static native boolean nClipRect(int renderer, int left, int top, + int right, int bottom, int op); @Override public boolean clipRect(Rect rect) { @@ -492,20 +488,16 @@ class GLES20Canvas extends HardwareCanvas { @Override public boolean clipRegion(Region region) { - // TODO: Implement - region.getBounds(mClipBounds); - return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top, - mClipBounds.right, mClipBounds.bottom, Region.Op.INTERSECT.nativeInt); + return nClipRegion(mRenderer, region.mNativeRegion, Region.Op.INTERSECT.nativeInt); } @Override public boolean clipRegion(Region region, Region.Op op) { - // TODO: Implement - region.getBounds(mClipBounds); - return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top, - mClipBounds.right, mClipBounds.bottom, op.nativeInt); + return nClipRegion(mRenderer, region.mNativeRegion, op.nativeInt); } + private static native boolean nClipRegion(int renderer, int region, int op); + @Override public boolean getClipBounds(Rect bounds) { return nGetClipBounds(mRenderer, bounds); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 7ecdcbefc88ad..0d45bbc20ccaf 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * 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. @@ -14,7 +14,6 @@ * limitations under the License. */ - package android.view; import android.content.ComponentCallbacks2; @@ -173,6 +172,17 @@ public abstract class HardwareRenderer { */ public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw"; + /** + * Turn on to allow region clipping (see + * {@link android.graphics.Canvas#clipPath(android.graphics.Path)} and + * {@link android.graphics.Canvas#clipRegion(android.graphics.Region)}. + * + * When this option is turned on a stencil buffer is always required. + * If this option is off a stencil buffer is only created when the overdraw + * debugging mode is turned on. + */ + private static final boolean REGION_CLIPPING_ENABLED = false; + /** * A process can set this flag to false to prevent the use of hardware * rendering. @@ -876,10 +886,12 @@ public abstract class HardwareRenderer { changed = true; mShowOverdraw = value; - if (surface != null && isEnabled()) { - if (validate()) { - sEglConfig = loadEglConfig(); - invalidate(surface); + if (!REGION_CLIPPING_ENABLED) { + if (surface != null && isEnabled()) { + if (validate()) { + sEglConfig = loadEglConfig(); + invalidate(surface); + } } } } @@ -1752,6 +1764,11 @@ public abstract class HardwareRenderer { @Override int[] getConfig(boolean dirtyRegions) { + //noinspection PointlessBooleanExpression + final int stencilSize = mShowOverdraw || REGION_CLIPPING_ENABLED ? + GLES20Canvas.getStencilSize() : 0; + final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + return new int[] { EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, @@ -1760,14 +1777,12 @@ public abstract class HardwareRenderer { EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_CONFIG_CAVEAT, EGL_NONE, - // TODO: Find a better way to choose the stencil size - EGL_STENCIL_SIZE, mShowOverdraw ? GLES20Canvas.getStencilSize() : 0, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT | - (dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), + EGL_STENCIL_SIZE, stencilSize, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, EGL_NONE }; } - + @Override void initCaches() { GLES20Canvas.initCaches(); diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index a80fc50797e0d..85d290507d875 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -274,6 +274,16 @@ static bool android_view_GLES20Canvas_clipRect(JNIEnv* env, jobject clazz, return renderer->clipRect(float(left), float(top), float(right), float(bottom), op); } +static bool android_view_GLES20Canvas_clipPath(JNIEnv* env, jobject clazz, + OpenGLRenderer* renderer, SkPath* path, SkRegion::Op op) { + return renderer->clipPath(path, op); +} + +static bool android_view_GLES20Canvas_clipRegion(JNIEnv* env, jobject clazz, + OpenGLRenderer* renderer, SkRegion* region, SkRegion::Op op) { + return renderer->clipRegion(region, op); +} + static bool android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, jobject rect) { const android::uirenderer::Rect& bounds(renderer->getClipBounds()); @@ -960,6 +970,8 @@ static JNINativeMethod gMethods[] = { { "nQuickReject", "(IFFFF)Z", (void*) android_view_GLES20Canvas_quickReject }, { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF }, { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect }, + { "nClipPath", "(III)Z", (void*) android_view_GLES20Canvas_clipPath }, + { "nClipRegion", "(III)Z", (void*) android_view_GLES20Canvas_clipRegion }, { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate }, { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate }, diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 6795ac3205d18..dfc4e25cc085c 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -35,6 +35,9 @@ // Turn on to enable layers debugging when rendered as regions #define DEBUG_LAYERS_AS_REGIONS 0 +// Turn on to enable debugging when the clip is not a rect +#define DEBUG_CLIP_REGIONS 0 + // Turn on to display debug info about vertex/fragment shaders #define DEBUG_PROGRAMS 0 diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 06574cd55cf3b..f0c9ce4748906 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -44,6 +44,8 @@ const char* DisplayList::OP_NAMES[] = { "SetMatrix", "ConcatMatrix", "ClipRect", + "ClipPath", + "ClipRegion", "DrawDisplayList", "DrawLayer", "DrawBitmap", @@ -166,6 +168,10 @@ void DisplayList::clearResources() { 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++) { SkPath* path = mPaths.itemAt(i); caches.pathCache.remove(path); @@ -182,6 +188,7 @@ void DisplayList::clearResources() { mShaders.clear(); mSourcePaths.clear(); mPaints.clear(); + mRegions.clear(); mPaths.clear(); mMatrices.clear(); mLayers.clear(); @@ -259,20 +266,10 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde caches.resourceCache.unlock(); - const Vector& paints = recorder.getPaints(); - for (size_t i = 0; i < paints.size(); i++) { - mPaints.add(paints.itemAt(i)); - } - - const Vector& paths = recorder.getPaths(); - for (size_t i = 0; i < paths.size(); i++) { - mPaths.add(paths.itemAt(i)); - } - - const Vector& matrices = recorder.getMatrices(); - for (size_t i = 0; i < matrices.size(); i++) { - mMatrices.add(matrices.itemAt(i)); - } + mPaints.appendVector(recorder.getPaints()); + mRegions.appendVector(recorder.getRegions()); + mPaths.appendVector(recorder.getPaths()); + mMatrices.appendVector(recorder.getMatrices()); } void DisplayList::init() { @@ -429,6 +426,18 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { f1, f2, f3, f4, regionOp); } break; + case ClipPath: { + SkPath* path = getPath(); + int regionOp = getInt(); + ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp); + } + break; + case ClipRegion: { + SkRegion* region = getRegion(); + int regionOp = getInt(); + ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp); + } + break; case DrawDisplayList: { DisplayList* displayList = getDisplayList(); int32_t flags = getInt(); @@ -1031,6 +1040,20 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp); } break; + case ClipPath: { + SkPath* path = getPath(); + int32_t regionOp = getInt(); + DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp); + renderer.clipPath(path, (SkRegion::Op) regionOp); + } + break; + case ClipRegion: { + SkRegion* region = getRegion(); + int32_t regionOp = getInt(); + DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp); + renderer.clipRegion(region, (SkRegion::Op) regionOp); + } + break; case DrawDisplayList: { DisplayList* displayList = getDisplayList(); int32_t flags = getInt(); @@ -1415,6 +1438,9 @@ void DisplayListRenderer::reset() { mPaints.clear(); mPaintMap.clear(); + mRegions.clear(); + mRegionMap.clear(); + mPaths.clear(); mPathMap.clear(); @@ -1571,6 +1597,20 @@ bool DisplayListRenderer::clipRect(float left, float top, float right, float bot return OpenGLRenderer::clipRect(left, top, right, bottom, op); } +bool DisplayListRenderer::clipPath(SkPath* path, SkRegion::Op op) { + addOp(DisplayList::ClipPath); + addPath(path); + addInt(op); + return OpenGLRenderer::clipPath(path, op); +} + +bool DisplayListRenderer::clipRegion(SkRegion* region, SkRegion::Op op) { + addOp(DisplayList::ClipRegion); + addRegion(region); + addInt(op); + return OpenGLRenderer::clipRegion(region, op); +} + status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags, uint32_t level) { // dirty is an out parameter and should not be recorded, diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 5f766770b18f1..d78d54efa2759 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -86,6 +86,8 @@ public: SetMatrix, ConcatMatrix, ClipRect, + ClipPath, + ClipRegion, // Drawing operations DrawDisplayList, DrawLayer, @@ -458,6 +460,10 @@ private: return (SkPath*) getInt(); } + SkRegion* getRegion() { + return (SkRegion*) getInt(); + } + SkPaint* getPaint(OpenGLRenderer& renderer) { return renderer.filterPaint((SkPaint*) getInt()); } @@ -497,6 +503,7 @@ private: Vector mPaints; Vector mPaths; SortedVector mSourcePaths; + Vector mRegions; Vector mMatrices; Vector mShaders; Vector mLayers; @@ -578,6 +585,8 @@ public: virtual void concatMatrix(SkMatrix* matrix); 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, uint32_t level = 0); @@ -658,6 +667,10 @@ public: return mSourcePaths; } + const Vector& getRegions() const { + return mRegions; + } + const Vector& getLayers() const { return mLayers; } @@ -803,6 +816,26 @@ private: return paintCopy; } + inline SkRegion* addRegion(SkRegion* region) { + if (!region) { + addInt((int) NULL); + return region; + } + + 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); + } + + addInt((int) regionCopy); + + return regionCopy; + } + inline void addDisplayList(DisplayList* displayList) { // TODO: To be safe, the display list should be ref-counted in the // resources cache, but we rely on the caller (UI toolkit) to @@ -877,6 +910,9 @@ private: SortedVector mSourcePaths; + Vector mRegions; + DefaultKeyedVector mRegionMap; + Vector mShaders; DefaultKeyedVector mShaderMap; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 8cda72989c707..bb1edbb6e2303 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1288,10 +1288,38 @@ bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, bool clipped = mSnapshot->clip(left, top, right, bottom, op); if (clipped) { dirtyClip(); +#if DEBUG_CLIP_REGIONS + if (!isDeferred() && mSnapshot->clipRegion && !mSnapshot->clipRegion->isRect()) { + int count = 0; + Vector rects; + SkRegion::Iterator it(*mSnapshot->clipRegion); + while (!it.done()) { + const SkIRect& r = it.rect(); + rects.push(r.fLeft); + rects.push(r.fTop); + rects.push(r.fRight); + rects.push(r.fBottom); + count++; + it.next(); + } + + drawColorRects(rects.array(), count, 0x7f00ff00, SkXfermode::kSrcOver_Mode, true); + } +#endif } return !mSnapshot->clipRect->isEmpty(); } +bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) { + const SkRect& bounds = path->getBounds(); + return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op); +} + +bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) { + const SkIRect& bounds = region->getBounds(); + return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op); +} + Rect* OpenGLRenderer::getClipRect() { return mSnapshot->clipRect; } @@ -3046,6 +3074,19 @@ status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint return DrawGlInfo::kStatusDone; } + int color = paint->getColor(); + // If a shader is set, preserve only the alpha + if (mShader) { + color |= 0x00ffffff; + } + SkXfermode::Mode mode = getXfermode(paint->getXfermode()); + + return drawColorRects(rects, count, color, mode); +} + +status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color, + SkXfermode::Mode mode, bool ignoreTransform) { + float left = FLT_MAX; float top = FLT_MAX; float right = FLT_MIN; @@ -3081,13 +3122,6 @@ status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint if (count == 0) return DrawGlInfo::kStatusDone; - int color = paint->getColor(); - // If a shader is set, preserve only the alpha - if (mShader) { - color |= 0x00ffffff; - } - SkXfermode::Mode mode = getXfermode(paint->getXfermode()); - setupDraw(); setupDrawNoTexture(); setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha); @@ -3096,7 +3130,7 @@ status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint setupDrawBlending(mode); setupDrawProgram(); setupDrawDirtyRegionsDisabled(); - setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f); + setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, ignoreTransform, true); setupDrawColorUniforms(); setupDrawShaderUniforms(); setupDrawColorFilterUniforms(); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 5520edbcb1d58..f07325f966d8a 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -164,6 +164,8 @@ public: ANDROID_API bool quickReject(float left, float top, float right, float bottom); bool quickRejectNoScissor(float left, float top, float right, float bottom); 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(); virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags, @@ -498,7 +500,8 @@ private: /** * Draws a colored rectangle with the specified color. The specified coordinates - * are transformed by the current snapshot's transform matrix. + * are transformed by the current snapshot's transform matrix unless specified + * otherwise. * * @param left The left coordinate of the rectangle * @param top The top coordinate of the rectangle @@ -511,6 +514,20 @@ private: void drawColorRect(float left, float top, float right, float bottom, int color, SkXfermode::Mode mode, bool ignoreTransform = false); + /** + * Draws a series of colored rectangles with the specified color. The specified + * coordinates are transformed by the current snapshot's transform matrix unless + * specified otherwise. + * + * @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 ignoreTransform True if the current transform should be ignored + */ + status_t drawColorRects(const float* rects, int count, int color, + SkXfermode::Mode mode, bool ignoreTransform = false); + /** * Draws the shape represented by the specified path texture. * This method invokes drawPathTexture() but takes into account diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index fbc84554dcdfc..d947299114c1f 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -130,6 +130,7 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) { switch (op) { case SkRegion::kIntersect_Op: { if (CC_UNLIKELY(clipRegion)) { + ensureClipRegion(); clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op); } else { clipped = clipRect->intersect(r); @@ -142,6 +143,7 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) { } case SkRegion::kUnion_Op: { if (CC_UNLIKELY(clipRegion)) { + ensureClipRegion(); clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op); } else { clipped = clipRect->unionWith(r); diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java index b2a508b0cc16c..d5daa5f673d90 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java @@ -18,13 +18,7 @@ package com.android.test.hwui; import android.app.Activity; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.Region; import android.os.Bundle; import android.view.View; @@ -50,8 +44,10 @@ public class ClipRegionActivity extends Activity { canvas.save(); canvas.clipRect(100.0f, 100.0f, getWidth() - 100.0f, getHeight() - 100.0f, Region.Op.DIFFERENCE); - canvas.drawARGB(255, 255, 0, 0); + canvas.drawARGB(128, 255, 0, 0); canvas.restore(); + + invalidate(); } } }