【问题标题】:Xamarin Android Software rendering doesn't support hardware bitmapsXamarin Android 软件渲染不支持硬件位图
【发布时间】:2020-09-09 02:25:45
【问题描述】:

我有一个应用程序,我需要在其中拍照并将其转换为灰度。它曾经在Android 9.0下工作。我升级到 Android 11.0,显然有一个重大变化。我创建了一个可以重现问题的独立活动:

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Provider;
using Android.Support.Design.Widget;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
using System;

namespace BitmapRenderIssue
{
    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
    public class MainActivity : AppCompatActivity
    {
        private const int CAPTURE_PHOTO = 100;
        private static Android.Net.Uri ImageUri;
        ImageView imageView1;
        private static Bitmap CurrImage;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            SetContentView(Resource.Layout.activity_main);

            Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
            SetSupportActionBar(toolbar);

            imageView1 = FindViewById<ImageView>(Resource.Id.imageView1);
            FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
            fab.Click += FabOnClick;
        }

        public override bool OnCreateOptionsMenu(IMenu menu)
        {
            MenuInflater.Inflate(Resource.Menu.menu_main, menu);
            return true;
        }

        public override bool OnOptionsItemSelected(IMenuItem item)
        {
            int id = item.ItemId;
            if (id == Resource.Id.action_settings)
            {
                return true;
            }

            return base.OnOptionsItemSelected(item);
        }

        private void FabOnClick(object sender, EventArgs eventArgs)
        {
            TakePicture();
        }

        public bool isCameraPermissionGranted()
        {
            if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
            {
                if (CheckSelfPermission(Android.Manifest.Permission.Camera)
                        == Permission.Granted)
                {
                    return true;
                }
                else
                {
                    RequestPermissions(new String[] { Android.Manifest.Permission.Camera }, 1);
                    return false;
                }
            }
            else
            { //permission is automatically granted on sdk<23 upon installation
                return true;
            }
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
        {
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            if (grantResults[0] == Permission.Granted)
            {
                CapturePhoto();
            }
        }

        public void CapturePhoto()
        {
            ImageUri = ContentResolver.Insert(MediaStore.Images.Media.ExternalContentUri, new ContentValues());
            var i = new Intent(MediaStore.ActionImageCapture);
            i.PutExtra(MediaStore.ExtraOutput, ImageUri);
            StartActivityForResult(i, CAPTURE_PHOTO);
        }

        public void TakePicture()
        {

            if (isCameraPermissionGranted())
                CapturePhoto();
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            if (resultCode == Result.Ok && requestCode == CAPTURE_PHOTO && ImageUri != null)
            {
                Android.Util.Log.Info("CameraScanning", "Getting Picture");

                var src = ImageDecoder.CreateSource(ContentResolver, ImageUri);
                Bitmap img = ImageDecoder.DecodeBitmap(src);

                CurrImage = ImageProcessing.Grayscale_ColorMatrixColorFilter(img);

                img.Recycle();
                img.Dispose();

                imageView1.SetImageBitmap(CurrImage);

                Java.IO.File xFile = new Java.IO.File(ImageUri.Path);
                if (xFile.Exists())
                    xFile.Delete();
                ImageUri = null;
            }
        }

    }
}

这是有问题的函数的代码:

public static class ImageProcessing
{
    public static Bitmap Grayscale_ColorMatrixColorFilter(Bitmap src)
    {
        int width = src.Width;
        int height = src.Height;

        Bitmap dest = Bitmap.CreateBitmap(width, height, Bitmap.Config.Rgb565);

        Canvas canvas = new Canvas(dest);

        Paint paint = new Paint();
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.SetSaturation(0); //value of 0 maps the color to gray-scale
        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
        paint.SetColorFilter(filter);
        canvas.DrawBitmap(src, 0, 0, paint);

        return dest;
    }
}

此函数在调用 canvas.DrawBitmap(src, 0, 0, paint); 时抛出“软件渲染不支持硬件位图”;

我已经尝试了Software rendering doesn't support hardware bitmap in Oreo 此处找到的所有 hardwareAccelerated 选项

我创建了一个包含可以重现此错误的 Visual Studio 项目的存储库。只需运行该应用程序。单击浮动操作按钮。拍一张照片。点击确定。会发生错误。

https://github.com/JimWilcox3/BitmapRenderIssue

谢谢, 吉姆

【问题讨论】:

    标签: android xamarin xamarin.android


    【解决方案1】:

    好的,这就是我想出的。在活动中,我添加了一个新类来充当 OnHeaderDecoded 事件的侦听器。在这个类中,我设置了 MutableRequired = true。

        private class OnHeaderDecodedListener : Java.Lang.Object, IOnHeaderDecodedListener
        {
    
            public void OnHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source)
            {
                decoder.MutableRequired = true;
            }
    
        }
    

    然后在 OnActivityResult 中,我改变了

    Bitmap img = ImageDecoder.DecodeBitmap(src); 
    

    Bitmap img = ImageDecoder.DecodeBitmap(src, new OnHeaderDecodedListener());
    

    这导致它创建了一个不是硬件位图的可变位图。

    干杯, 吉姆

    【讨论】:

    • 感谢分享。不要忘记接受答案。
    • @WendyZang-MSFT 它会让我明天接受我自己的答案。 Stack Overflow 让您等待几天才能接受自己的答案。