public static void CopyTo(this ANDROIDIMAGE src, ref MemoryBitmap <System.Numerics.Vector3> dst)
        {
            if (dst.Width != src.Width || dst.Height != src.Height)
            {
                dst = default;
            }
            if (dst.IsEmpty)
            {
                dst = new MemoryBitmap <System.Numerics.Vector3>(src.Width, src.Height, Pixel.BGR96F.Format);
            }

            src._CopyYuv445345To(dst.AsSpanBitmap());
        }
Beispiel #2
0
        public void OnImageAvailable(Android.Media.ImageReader imageReader)
        {
            Android.Media.Image     image            = null;
            Android.Graphics.Bitmap bitmapFromPlane0 = null;
            Android.Graphics.Bitmap bitmapToSave     = null;

            try
            {
                image = imageReader.AcquireLatestImage();

                if (image != null)
                {
                    Android.Media.Image.Plane[] planes = image.GetPlanes();
                    if (planes.Length > 0)
                    {
                        int rowPadding = planes[0].RowStride - planes[0].PixelStride * mWidth;

                        bitmapFromPlane0 = Android.Graphics.Bitmap.CreateBitmap
                                           (
                            width: (mWidth + rowPadding / planes[0].PixelStride), // 此处不能直接采用 Width, 否则保存的图片会出现花屏
                            height: mHeight,
                            config: Android.Graphics.Bitmap.Config.Argb8888
                                           );

                        bitmapFromPlane0.CopyPixelsFromBuffer(planes[0].Buffer);

                        var imagefileInfo = MyAndroidScreenshot.GetScreenshotFileInfo(mImageFileDateTime, mDirName); // 传入目录名称

                        if (imagefileInfo.Directory.Exists == false)
                        {
                            imagefileInfo.Directory.Create();
                        }

                        string imageFile = imagefileInfo.FullName;

                        // bitmapFromPlane0 多增加的长度减至 屏幕的 Width
                        bitmapToSave = Android.Graphics.Bitmap.CreateBitmap(bitmapFromPlane0, 0, 0, mWidth, mHeight);

                        using (System.IO.FileStream stream = new System.IO.FileStream(path: imageFile, System.IO.FileMode.CreateNew))
                        {
                            bitmapToSave.Compress(Android.Graphics.Bitmap.CompressFormat.Png, 60, stream); // 压缩图片 // TODO 控制图片质量
                            stream.Flush();
                        }

                        // 发送广播
                        Android.App.Application.Context.SendBroadcast
                        (
                            new Android.Content.Intent
                            (
                                action: Android.Content.Intent.ActionMediaScannerScanFile,
                                uri: Android.Net.Uri.FromFile(new Java.IO.File(imageFile))
                            )
                        );

                        System.Diagnostics.Debug.WriteLine("MyImageReaderListener-OnImageAvailable - 成功保存屏幕截图");
                    }
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine("MyImageReaderListener-OnImageAvailable - 捕获异常");
                System.Diagnostics.Debug.WriteLine(e.GetFullInfo());
                System.Diagnostics.Debugger.Break();
            }
            finally
            {
                if (bitmapFromPlane0 != null)
                {
                    bitmapFromPlane0.Recycle();
                    bitmapFromPlane0 = null;
                }

                if (image != null)
                {
                    image.Close();
                    image = null;
                }

                MyAndroidScreenshot.GetInstance().ReleaseAfterScreenCaptureSave();

                MyAndroidScreenshot.s_IsRunning.Set(false);
            }
        }
        private static void _CopyYuv445345To(this ANDROIDIMAGE src, SpanBitmap dst)
        {
            // http://werner-dittmann.blogspot.com/2016/03/solving-some-mysteries-about-androids.html

            if (src == null)
            {
                throw new ArgumentNullException(nameof(src));
            }
            if (src.Format != ANDROIDGFX.ImageFormatType.Yuv420888)
            {
                throw new ArgumentNullException(nameof(src.Format));
            }
            if (dst.PixelFormat != Pixel.BGR24.Format)
            {
                throw new ArgumentNullException(nameof(dst.PixelFormat));
            }

            var planes = src.GetPlanes();

            var yPlane = planes[0].Buffer;
            var uPlane = planes[1].Buffer;
            var vPlane = planes[2].Buffer;

            int total      = yPlane.Capacity();
            int uvCapacity = uPlane.Capacity();
            int width      = planes[0].RowStride;

            int yPos = 0;

            for (int y = 0; y < src.Height; y++)
            {
                int uvPos = (y >> 1) * width;

                var dstRow = dst.UseScanlineBytes(y);

                for (int x = 0; x < width; x++)
                {
                    if (uvPos >= uvCapacity - 1)
                    {
                        break;
                    }
                    if (yPos >= total)
                    {
                        break;
                    }

                    int y1 = yPlane.Get(yPos++) & 0xff;

                    /*
                     * The ordering of the u (Cb) and v (Cr) bytes inside the planes is a
                     * bit strange. The _first_ byte of the u-plane and the _second_ byte
                     * of the v-plane build the u/v pair and belong to the first two pixels
                     * (y-bytes), thus usual YUV 420 behavior. What the Android devs did
                     * here (IMHO): just copy the interleaved NV21 U/V data to two planes
                     * but keep the offset of the interleaving.
                     */

                    int u = (uPlane.Get(uvPos) & 0xff) - 128;
                    int v = (vPlane.Get(uvPos + 1) & 0xff) - 128;
                    if ((x & 1) == 1)
                    {
                        uvPos += 2;
                    }

                    // This is the integer variant to convert YCbCr to RGB, NTSC values.
                    // formulae found at
                    // https://software.intel.com/en-us/android/articles/trusted-tools-in-the-new-android-world-optimization-techniques-from-intel-sse-intrinsics-to
                    // and on StackOverflow etc.


                    int y1192 = 1192 * y1;
                    int r     = (y1192 + 1634 * v);
                    int g     = (y1192 - 833 * v - 400 * u);
                    int b     = (y1192 + 2066 * u);

                    r = (r < 0) ? 0 : ((r > 262143) ? 262143 : r);
                    g = (g < 0) ? 0 : ((g > 262143) ? 262143 : g);
                    b = (b < 0) ? 0 : ((b > 262143) ? 262143 : b);

                    dstRow[x * 3 + 0] = (Byte)(b >> 10);
                    dstRow[x * 3 + 1] = (Byte)(g >> 10);
                    dstRow[x * 3 + 2] = (Byte)(r >> 10);
                }
            }
        }