Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c93d29cf authored by Derek Sollenberger's avatar Derek Sollenberger
Browse files

Enforce that RuntimeShader is only hardware accelerated

Throw an illegal argument exception any time a Paint with
a RuntimeShader is attempted to be drawn into a software
canvas. Also mark that a Picture that uses a RuntimeShader is
properly marked as requiring hardware acceleration.
These tests check that the use of RuntimeShaders trigger

Bug: 189102731
Bug: 201546136
Test: atest CtsUiRenderingTestCases:RuntimeShaderTests
Change-Id: Id0ee4d1f05e2975031121298b45f925ee74f6818
parent dc63dc4d
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -1896,7 +1896,7 @@ public class ViewDebug {

        private Canvas mCanvas;
        private Bitmap mBitmap;
        private boolean mEnabledHwBitmapsInSwMode;
        private boolean mEnabledHwFeaturesInSwMode;

        @Override
        public Canvas getCanvas(View view, int width, int height) {
@@ -1913,7 +1913,7 @@ public class ViewDebug {
            if (mCanvas == null) {
                mCanvas = new Canvas();
            }
            mEnabledHwBitmapsInSwMode = mCanvas.isHwBitmapsInSwModeEnabled();
            mEnabledHwFeaturesInSwMode = mCanvas.isHwFeaturesInSwModeEnabled();
            mCanvas.setBitmap(mBitmap);
            return mCanvas;
        }
@@ -1921,7 +1921,7 @@ public class ViewDebug {
        @Override
        public Bitmap createBitmap() {
            mCanvas.setBitmap(null);
            mCanvas.setHwBitmapsInSwModeEnabled(mEnabledHwBitmapsInSwMode);
            mCanvas.setHwFeaturesInSwModeEnabled(mEnabledHwFeaturesInSwMode);
            return mBitmap;
        }
    }
+60 −57
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ public abstract class BaseCanvas {
     * @hide
     */
    protected int mDensity = Bitmap.DENSITY_NONE;
    private boolean mAllowHwBitmapsInSwMode = false;
    private boolean mAllowHwFeaturesInSwMode = false;

    protected void throwIfCannotDraw(Bitmap bitmap) {
        if (bitmap.isRecycled()) {
@@ -101,14 +101,14 @@ public abstract class BaseCanvas {

    public void drawArc(float left, float top, float right, float bottom, float startAngle,
            float sweepAngle, boolean useCenter, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
                useCenter, paint.getNativeInstance());
    }

    public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
            @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
                paint);
    }
@@ -119,14 +119,14 @@ public abstract class BaseCanvas {

    public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
        throwIfCannotDraw(bitmap);
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top,
                paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity,
                bitmap.mDensity);
    }

    public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), matrix.ni(),
                paint != null ? paint.getNativeInstance() : 0);
    }
@@ -137,7 +137,7 @@ public abstract class BaseCanvas {
            throw new NullPointerException();
        }
        throwIfCannotDraw(bitmap);
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        final long nativePaint = paint == null ? 0 : paint.getNativeInstance();

        int left, top, right, bottom;
@@ -163,7 +163,7 @@ public abstract class BaseCanvas {
            throw new NullPointerException();
        }
        throwIfCannotDraw(bitmap);
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        final long nativePaint = paint == null ? 0 : paint.getNativeInstance();

        float left, top, right, bottom;
@@ -202,7 +202,7 @@ public abstract class BaseCanvas {
                || (lastScanline + width > length)) {
            throw new ArrayIndexOutOfBoundsException();
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        // quick escape if there's nothing to draw
        if (width == 0 || height == 0) {
            return;
@@ -226,7 +226,7 @@ public abstract class BaseCanvas {
        if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
            throw new ArrayIndexOutOfBoundsException();
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        if (meshWidth == 0 || meshHeight == 0) {
            return;
        }
@@ -243,7 +243,7 @@ public abstract class BaseCanvas {
    }

    public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
    }

@@ -275,23 +275,23 @@ public abstract class BaseCanvas {

    public void drawLine(float startX, float startY, float stopX, float stopY,
            @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
    }

    public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
            @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
    }

    public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        drawLines(pts, 0, pts.length, paint);
    }

    public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
    }

@@ -299,18 +299,19 @@ public abstract class BaseCanvas {
        if (oval == null) {
            throw new NullPointerException();
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
    }

    public void drawPaint(@NonNull Paint paint) {
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance());
    }

    public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
        Bitmap bitmap = patch.getBitmap();
        throwIfCannotDraw(bitmap);
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
        nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
                dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -320,7 +321,7 @@ public abstract class BaseCanvas {
    public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
        Bitmap bitmap = patch.getBitmap();
        throwIfCannotDraw(bitmap);
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
        nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
                dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -328,7 +329,7 @@ public abstract class BaseCanvas {
    }

    public void drawPath(@NonNull Path path, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        if (path.isSimplePath && path.rects != null) {
            nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
        } else {
@@ -337,18 +338,18 @@ public abstract class BaseCanvas {
    }

    public void drawPoint(float x, float y, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
    }

    public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
            @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
    }

    public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        drawPoints(pts, 0, pts.length, paint);
    }

@@ -359,7 +360,7 @@ public abstract class BaseCanvas {
        if (index < 0 || index + count > text.length || count * 2 > pos.length) {
            throw new IndexOutOfBoundsException();
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        for (int i = 0; i < count; i++) {
            drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
        }
@@ -368,22 +369,22 @@ public abstract class BaseCanvas {
    @Deprecated
    public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
            @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
    }

    public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
    }

    public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        drawRect(r.left, r.top, r.right, r.bottom, paint);
    }

    public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawRect(mNativeCanvasWrapper,
                rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
    }
@@ -394,13 +395,13 @@ public abstract class BaseCanvas {

    public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
            @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
                paint.getNativeInstance());
    }

    public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
    }

@@ -410,7 +411,7 @@ public abstract class BaseCanvas {
     */
    public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
            @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        float outerLeft = outer.left;
        float outerTop = outer.top;
        float outerRight = outer.right;
@@ -431,7 +432,7 @@ public abstract class BaseCanvas {
     */
    public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
            @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        if (innerRadii == null || outerRadii == null
                || innerRadii.length != 8 || outerRadii.length != 8) {
            throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
@@ -509,7 +510,7 @@ public abstract class BaseCanvas {
                (text.length - index - count)) < 0) {
            throw new IndexOutOfBoundsException();
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
                paint.getNativeInstance());
    }
@@ -519,7 +520,7 @@ public abstract class BaseCanvas {
        if ((start | end | (end - start) | (text.length() - end)) < 0) {
            throw new IndexOutOfBoundsException();
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        if (text instanceof String || text instanceof SpannedString ||
                text instanceof SpannableString) {
            nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
@@ -537,7 +538,7 @@ public abstract class BaseCanvas {
    }

    public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
                paint.getNativeInstance());
    }
@@ -547,7 +548,7 @@ public abstract class BaseCanvas {
        if ((start | end | (end - start) | (text.length() - end)) < 0) {
            throw new IndexOutOfBoundsException();
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
                paint.getNativeInstance());
    }
@@ -557,7 +558,7 @@ public abstract class BaseCanvas {
        if (index < 0 || index + count > text.length) {
            throw new ArrayIndexOutOfBoundsException();
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
                path.readOnlyNI(), hOffset, vOffset,
                paint.mBidiFlags, paint.getNativeInstance());
@@ -566,7 +567,7 @@ public abstract class BaseCanvas {
    public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
            float vOffset, @NonNull Paint paint) {
        if (text.length() > 0) {
            throwIfHasHwBitmapInSwMode(paint);
            throwIfHasHwFeaturesInSwMode(paint);
            nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
                    paint.mBidiFlags, paint.getNativeInstance());
        }
@@ -587,7 +588,7 @@ public abstract class BaseCanvas {
            throw new IndexOutOfBoundsException();
        }

        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
                x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
    }
@@ -606,7 +607,7 @@ public abstract class BaseCanvas {
            throw new IndexOutOfBoundsException();
        }

        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        if (text instanceof String || text instanceof SpannedString ||
                text instanceof SpannableString) {
            nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
@@ -664,7 +665,7 @@ public abstract class BaseCanvas {
        if (indices != null) {
            checkRange(indices.length, indexOffset, indexCount);
        }
        throwIfHasHwBitmapInSwMode(paint);
        throwIfHasHwFeaturesInSwMode(paint);
        nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
                vertOffset, texs, texOffset, colors, colorOffset,
                indices, indexOffset, indexCount, paint.getNativeInstance());
@@ -680,50 +681,52 @@ public abstract class BaseCanvas {
    /**
     * @hide
     */
    public void setHwBitmapsInSwModeEnabled(boolean enabled) {
        mAllowHwBitmapsInSwMode = enabled;
    public void setHwFeaturesInSwModeEnabled(boolean enabled) {
        mAllowHwFeaturesInSwMode = enabled;
    }

    /**
     * @hide
     */
    public boolean isHwBitmapsInSwModeEnabled() {
        return mAllowHwBitmapsInSwMode;
    public boolean isHwFeaturesInSwModeEnabled() {
        return mAllowHwFeaturesInSwMode;
    }

    /**
     * If true throw an exception
     * @hide
     */
    protected void onHwBitmapInSwMode() {
        if (!mAllowHwBitmapsInSwMode) {
            throw new IllegalArgumentException(
                    "Software rendering doesn't support hardware bitmaps");
        }
    protected boolean onHwFeatureInSwMode() {
        return !mAllowHwFeaturesInSwMode;
    }

    private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
            onHwBitmapInSwMode();
        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE
                && onHwFeatureInSwMode()) {
            throw new IllegalArgumentException(
                    "Software rendering doesn't support hardware bitmaps");
        }
    }

    private void throwIfHasHwBitmapInSwMode(Paint p) {
    private void throwIfHasHwFeaturesInSwMode(Paint p) {
        if (isHardwareAccelerated() || p == null) {
            return;
        }
        throwIfHasHwBitmapInSwMode(p.getShader());
        throwIfHasHwFeaturesInSwMode(p.getShader());
    }

    private void throwIfHasHwBitmapInSwMode(Shader shader) {
    private void throwIfHasHwFeaturesInSwMode(Shader shader) {
        if (shader == null) {
            return;
        }
        if (shader instanceof BitmapShader) {
            throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap);
        }
        if (shader instanceof ComposeShader) {
            throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderA);
            throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderB);
        } else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) {
            throw new IllegalArgumentException(
                    "Software rendering doesn't support RuntimeShader");
        } else if (shader instanceof ComposeShader) {
            throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA);
            throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB);
        }
    }

+9 −6
Original line number Diff line number Diff line
@@ -124,7 +124,7 @@ public class Picture {
    public void endRecording() {
        verifyValid();
        if (mRecordingCanvas != null) {
            mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
            mRequiresHwAcceleration = mRecordingCanvas.mUsesHwFeature;
            mRecordingCanvas = null;
            nativeEndRecording(mNativePicture);
        }
@@ -182,8 +182,10 @@ public class Picture {
        if (mRecordingCanvas != null) {
            endRecording();
        }
        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
            canvas.onHwBitmapInSwMode();
        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()
                && canvas.onHwFeatureInSwMode()) {
            throw new IllegalArgumentException("Software rendering not supported for Pictures that"
                    + " require hardware acceleration");
        }
        nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
    }
@@ -242,7 +244,7 @@ public class Picture {

    private static class PictureCanvas extends Canvas {
        private final Picture mPicture;
        boolean mHoldsHwBitmap;
        boolean mUsesHwFeature;

        public PictureCanvas(Picture pict, long nativeCanvas) {
            super(nativeCanvas);
@@ -265,8 +267,9 @@ public class Picture {
        }

        @Override
        protected void onHwBitmapInSwMode() {
            mHoldsHwBitmap = true;
        protected boolean onHwFeatureInSwMode() {
            mUsesHwFeature = true;
            return false;
        }
    }
}