Ограничить панорамирование SKCanvasView в SkiaSharp - PullRequest
1 голос
/ 07 мая 2020

Я делаю в своем приложении функцию просмотра изображений, которая позволяет пользователю масштабировать и панорамировать.

Я использовал следующую документацию, чтобы добиться этого с помощью SkiaSharp.

https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/touch

Сейчас я пытаюсь ограничить пользователя от уменьшения масштаба или панорамирования за края растрового изображения, загруженного в SKCanvasView.

Я ограничил масштабирование, но проблема в том, что я не могу понять, как ограничить панорамирование, я не могу найти ЛЮБОЙ пример того, как это сделать онлайн. Есть ли гуру SkiaSharp, достигший этого?

Вот код ...

Соответствующий XAML:

<Grid BackgroundColor="#141d3d">
                <Grid.Effects>
                    <tt:TouchEffect Capture="True" TouchAction="OnTouchEffectAction" />
                </Grid.Effects>

                <skia:SKCanvasView x:Name="canvasView" PaintSurface="OnCanvasViewPaintSurface" />

                <Button Text="CLOSE"
                            TextColor="White"
                            VerticalOptions="Start"
                            HorizontalOptions="End"
                            BackgroundColor="Transparent"
                            Command="{Binding CmdCloseFullImg}" />
            </Grid>

XAML.cs

public partial class AePage : ContentPage
    {
        private AeViewModel aeViewModel = new AeViewModel();

        private TouchManipulationBitmap bitmap = new TouchManipulationBitmap();
        private List<long> touchIds = new List<long>();

        public static float CanvasWidth { get; set; }
        public static float CanvasHeight { get; set; }

        public AePage()
        {
            InitializeComponent();

            BindingContext = aeViewModel;
        }



        private void OnCanvasViewPaintSurface(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs e)
        {
            SKImageInfo info = e.Info;
            SKCanvas canvas = e.Surface.Canvas;
            canvas.Clear();

            // Display the bitmap
            bitmap.Paint(info, canvas);
        }

        private void OnTouchEffectAction(object sender, TouchActionEventArgs e)
        {
            CanvasWidth = canvasView.CanvasSize.Width;
            CanvasHeight = canvasView.CanvasSize.Height;

            // Convert Xamarin.Forms point to pixels
            TouchTrackingPoint pt = e.Location;
            SKPoint point = new SKPoint((float)(canvasView.CanvasSize.Width * pt.X / canvasView.Width),
                            (float)(canvasView.CanvasSize.Height * pt.Y / canvasView.Height));
            switch (e.Type)
            {
                case TouchActionType.Pressed:
                    touchIds.Add(e.Id);
                    bitmap.ProcessTouchEvent(e.Id, e.Type, point);
                    break;

                case TouchActionType.Moved:
                    if (touchIds.Contains(e.Id))
                    {
                        bitmap.ProcessTouchEvent(e.Id, e.Type, point);
                        canvasView.InvalidateSurface();
                    }
                    break;

                case TouchActionType.Released:
                case TouchActionType.Cancelled:
                    if (touchIds.Contains(e.Id))
                    {
                        bitmap.ProcessTouchEvent(e.Id, e.Type, point);
                        touchIds.Remove(e.Id);
                        canvasView.InvalidateSurface();
                    }
                    break;
            }
        }
    }

Класс TouchManipulationBitmap:

public class TouchManipulationBitmap
    {
        public SKBitmap bitmap;

        public TouchManipulationManager TouchManager { set; get; }

        public static SKMatrix Matrix { set; get; }

        private Dictionary<long, TouchManipulationInfo> touchDictionary = new Dictionary<long, TouchManipulationInfo>();

        public TouchManipulationBitmap()
        {
            this.bitmap = ReturnSKBitmap();
            Matrix = SKMatrix.MakeIdentity();
            TouchManager = new TouchManipulationManager();
        }

        public SKBitmap ReturnSKBitmap()
        {
            string resourceId = "MetroAlarmHandlerMobile.Media.David DP.jpg";
            Assembly assembly = GetType().GetTypeInfo().Assembly;

            using (System.IO.Stream stream = assembly.GetManifestResourceStream(resourceId))
            {
                return SKBitmap.Decode(stream);
            }
        }

        public void Paint(SKImageInfo info, SKCanvas canvas)
        {
            canvas.Save();
            SKMatrix matrix = Matrix;
            canvas.Concat(ref matrix);
            float scale = Math.Min((float)info.Width / bitmap.Width, (float)info.Height / bitmap.Height);

            float x = (info.Width - scale * bitmap.Width) / 2;
            float y = (info.Height - scale * bitmap.Height) / 2;

            SKRect destRect = new SKRect(x, y, x + scale * bitmap.Width, y + scale * bitmap.Height);
            canvas.DrawBitmap(bitmap, destRect);
            canvas.Restore();

            Console.WriteLine($"SCALE: {Matrix.ScaleX}");
            Console.WriteLine($"TRANSLATION: X = {Matrix.TransX} Y = {Matrix.TransY}");
        }

        public void ProcessTouchEvent(long id, TouchActionType type, SKPoint location)
        {
            switch (type)
            {
                case TouchActionType.Pressed:
                    touchDictionary.Add(id, new TouchManipulationInfo
                    {
                        PreviousPoint = location,
                        NewPoint = location
                    });
                    break;

                case TouchActionType.Moved:
                    TouchManipulationInfo info = touchDictionary[id];
                    info.NewPoint = location;
                    Manipulate();
                    info.PreviousPoint = info.NewPoint;
                    break;

                case TouchActionType.Released:
                    touchDictionary[id].NewPoint = location;
                    Manipulate();
                    touchDictionary.Remove(id);
                    break;

                case TouchActionType.Cancelled:
                    touchDictionary.Remove(id);
                    break;
            }
        }

        private void Manipulate()
        {
            TouchManipulationInfo[] infos = new TouchManipulationInfo[touchDictionary.Count];
            touchDictionary.Values.CopyTo(infos, 0);
            SKMatrix touchMatrix = SKMatrix.MakeIdentity();

            if (infos.Length == 1)
            {
                SKPoint prevPoint = infos[0].PreviousPoint;
                SKPoint newPoint = infos[0].NewPoint;
                SKPoint pivotPoint = Matrix.MapPoint(bitmap.Width / 2, bitmap.Height / 2);

                touchMatrix = TouchManager.OneFingerManipulate(prevPoint, newPoint, pivotPoint);
            }
            else if (infos.Length >= 2)
            {
                int pivotIndex = infos[0].NewPoint == infos[0].PreviousPoint ? 0 : 1;
                SKPoint pivotPoint = infos[pivotIndex].NewPoint;
                SKPoint newPoint = infos[1 - pivotIndex].NewPoint;
                SKPoint prevPoint = infos[1 - pivotIndex].PreviousPoint;

                touchMatrix = TouchManager.TwoFingerManipulate(prevPoint, newPoint, pivotPoint);
            }

            SKMatrix matrix = Matrix;
            SKMatrix.PostConcat(ref matrix, touchMatrix);
            Matrix = matrix;
        }
    }

Класс TouchManipulationManager:

public class TouchManipulationManager
    {
        private float Magnitude(SKPoint point)
        {
            return (float)Math.Sqrt(Math.Pow(point.X, 2) + Math.Pow(point.Y, 2));
        }

        public SKMatrix OneFingerManipulate(SKPoint prevPoint, SKPoint newPoint, SKPoint pivotPoint)
        {
            SKMatrix touchMatrix = SKMatrix.MakeIdentity();
            SKPoint delta = newPoint - prevPoint;


            // Multiply the rotation matrix by a translation matrix
            SKMatrix.PostConcat(ref touchMatrix, SKMatrix.MakeTranslation(delta.X, delta.Y));

            return touchMatrix;
        }

        public SKMatrix TwoFingerManipulate(SKPoint prevPoint, SKPoint newPoint, SKPoint pivotPoint)
        {
            SKMatrix touchMatrix = SKMatrix.MakeIdentity();
            SKPoint oldVector = prevPoint - pivotPoint;
            SKPoint newVector = newPoint - pivotPoint;

            float scale = Magnitude(newVector) / Magnitude(oldVector);

            if (TouchManipulationBitmap.Matrix.ScaleX <= 1 && scale <= 1) return touchMatrix;

            if (!float.IsNaN(scale) && !float.IsInfinity(scale))
            {
                SKMatrix.PostConcat(ref touchMatrix, SKMatrix.MakeScale(scale, scale, pivotPoint.X, pivotPoint.Y));
            }

            return touchMatrix;
        }
    }

Класс TouchManipulationInfo:

public class TouchManipulationInfo
    {
        public SKPoint PreviousPoint { set; get; }

        public SKPoint NewPoint { set; get; }
    }
...