Android UnsupportedOperationException на Canvas.clipPath

Я использую библиотеку обрезки изображений здесь https://github.com/lvillani/android-cropimage в моем проекте, чтобы обрезать изображения, хранящиеся на устройстве.

Однако некоторые пользователи сообщают о сбоях со следующей трассировкой стека

java.lang.UnsupportedOperationException
at android.view.GLES20Canvas.clipPath(GLES20Canvas.java:413)
at com.android.camera.HighlightView.draw(HighlightView.java:101)
at com.android.camera.CropImageView.onDraw(CropImage.java:783)
at android.view.View.draw(View.java:11006)
at android.view.View.getDisplayList(View.java:10445)
at android.view.ViewGroup.drawChild(ViewGroup.java:2850)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2489)
at android.view.View.getDisplayList(View.java:10443)
at android.view.ViewGroup.drawChild(ViewGroup.java:2850)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2489)
at android.view.View.getDisplayList(View.java:10443)
at android.view.ViewGroup.drawChild(ViewGroup.java:2850)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2489)
at android.view.View.getDisplayList(View.java:10443)
at android.view.ViewGroup.drawChild(ViewGroup.java:2850)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2489)
at android.view.View.getDisplayList(View.java:10443)
at android.view.ViewGroup.drawChild(ViewGroup.java:2850)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2489)
at android.view.View.draw(View.java:11009)
at android.widget.FrameLayout.draw(FrameLayout.java:450)
at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2154)
at android.view.View.getDisplayList(View.java:10445)
at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:853)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:1961)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1679)
at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2558)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4697)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:787)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:554)
at dalvik.system.NativeStart.main(Native Method)

От поиска я думаю, что это вызвано аппаратным ускорением только на определенных устройствах. Я отключил аппаратное ускорение в своем манифесте, но исключение все еще происходит. Я также обнаружил, что "Обходное решение - это определение проблемных операций в вашем коде, затем их рисование в растровое изображение, а затем разбить растровое изображение на ваш ускоренный холст".

Проблемный код в соответствии со трассировкой стека

protected void draw(Canvas canvas) {
    if (mHidden) {
        return;
    }
    canvas.save();
    Path path = new Path();
    if (!hasFocus()) {
        mOutlinePaint.setColor(0xFF000000);
        canvas.drawRect(mDrawRect, mOutlinePaint);
    } else {
        Rect viewDrawingRect = new Rect();
        mContext.getDrawingRect(viewDrawingRect);
        if (mCircle) {
            float width  = mDrawRect.width();
            float height = mDrawRect.height();
            path.addCircle(mDrawRect.left + (width  / 2),
                           mDrawRect.top + (height / 2),
                           width / 2,
                           Path.Direction.CW);
            mOutlinePaint.setColor(0xFFEF04D6);
        } else {
            path.addRect(new RectF(mDrawRect), Path.Direction.CW);
            mOutlinePaint.setColor(0xFFFF8A00);
        }
        canvas.clipPath(path, Region.Op.DIFFERENCE);
        canvas.drawRect(viewDrawingRect,
                hasFocus() ? mFocusPaint : mNoFocusPaint);

        canvas.restore();
        canvas.drawPath(path, mOutlinePaint);

        if (mMode == ModifyMode.Grow) {
            if (mCircle) {
                int width  = mResizeDrawableDiagonal.getIntrinsicWidth();
                int height = mResizeDrawableDiagonal.getIntrinsicHeight();

                int d  = (int) Math.round(Math.cos(/*45deg*/Math.PI / 4D)
                        * (mDrawRect.width() / 2D));
                int x  = mDrawRect.left
                        + (mDrawRect.width() / 2) + d - width / 2;
                int y  = mDrawRect.top
                        + (mDrawRect.height() / 2) - d - height / 2;
                mResizeDrawableDiagonal.setBounds(x, y,
                        x + mResizeDrawableDiagonal.getIntrinsicWidth(),
                        y + mResizeDrawableDiagonal.getIntrinsicHeight());
                mResizeDrawableDiagonal.draw(canvas);
            } else {
                int left    = mDrawRect.left   + 1;
                int right   = mDrawRect.right  + 1;
                int top     = mDrawRect.top    + 4;
                int bottom  = mDrawRect.bottom + 3;

                int widthWidth   =
                        mResizeDrawableWidth.getIntrinsicWidth() / 2;
                int widthHeight  =
                        mResizeDrawableWidth.getIntrinsicHeight() / 2;
                int heightHeight =
                        mResizeDrawableHeight.getIntrinsicHeight() / 2;
                int heightWidth  =
                        mResizeDrawableHeight.getIntrinsicWidth() / 2;

                int xMiddle = mDrawRect.left
                        + ((mDrawRect.right  - mDrawRect.left) / 2);
                int yMiddle = mDrawRect.top
                        + ((mDrawRect.bottom - mDrawRect.top) / 2);

                mResizeDrawableWidth.setBounds(left - widthWidth,
                                               yMiddle - widthHeight,
                                               left + widthWidth,
                                               yMiddle + widthHeight);
                mResizeDrawableWidth.draw(canvas);

                mResizeDrawableWidth.setBounds(right - widthWidth,
                                               yMiddle - widthHeight,
                                               right + widthWidth,
                                               yMiddle + widthHeight);
                mResizeDrawableWidth.draw(canvas);

                mResizeDrawableHeight.setBounds(xMiddle - heightWidth,
                                                top - heightHeight,
                                                xMiddle + heightWidth,
                                                top + heightHeight);
                mResizeDrawableHeight.draw(canvas);

                mResizeDrawableHeight.setBounds(xMiddle - heightWidth,
                                                bottom - heightHeight,
                                                xMiddle + heightWidth,
                                                bottom + heightHeight);
                mResizeDrawableHeight.draw(canvas);
            }
        }
    }
}

Как я могу рисовать растровое изображение, а затем blit на холст?

Любая помощь будет принята с благодарностью!

Ответ 1

На устройствах ICS существует опция разработчика для принудительного ускорения аппаратного ускорения, даже если приложение не запрашивает его. Именно это и вызывает аварии. Вы должны использовать что-то вроде этого, чтобы заставить его использовать рендеринг программного обеспечения:

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
   myCusomView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

Ответ 2

Я думаю, вы можете нарисовать свое представление на экранном растровом изображении и скопировать растровое изображение на экранное полотно. Например,

protected void draw(Canvas canvas) {
    if (mHidden) {
        return;
    }

    Bitmap bitmap = createOffScreenBitmap();
    canvas.drawa(bitmap,0f, 0f, null);

}


private Bitmap drawOffScreenBitmap(){

    // Draw whatever you want to draw right here.
}

Кроме того, вы можете использовать PorterDuffxfermode, который поддерживает аппаратное ускорение вместо clippath.

Ответ 3

myCustomView = просмотреть имя класса или использовать this

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
   this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

Я получаю решение, используя тот метод.