Clipping a low resolution image using Transforms and assigning it to my user control

假如想象 提交于 2019-12-05 03:32:53

I got it working with some changes and trigonometry here.

MainPage.xaml

<Page
    x:Class="MagnifierApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MagnifierApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid
        x:Name="LayoutGrid"
        Margin="0,0"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        PointerMoved="LayoutGrid_OnPointerMoved"
        PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
        PointerPressed="LayoutGrid_OnPointerPressed"
        PointerReleased="LayoutGrid_OnPointerReleased">
        <Image
            x:Name="BigImage"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Stretch="Uniform"
            Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
        <local:Magnifier
            x:Name="MagnifierTip"
            VerticalAlignment="Top"
            HorizontalAlignment="Left"
            Visibility="Collapsed" />
    </Grid>
</Page>

MainPage.xaml.cs

using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;

namespace MagnifierApp
{
    public sealed partial class MainPage : Page
    {
        private double zoomScale = 2;
        private double pointerX = 0;
        private double pointerY = 0;
        private const double MinZoomScale = .25;
        private const double MaxZoomScale = 32;
        private bool isFirst = true;

        public MainPage()
        {
            this.InitializeComponent();

            var bi = (BitmapImage)BigImage.Source;
            bi.ImageOpened += bi_ImageOpened;
            this.SizeChanged += MainPage_SizeChanged;
        }

        void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
        {
            this.UpdateImageLayout();
        }

        void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            this.UpdateImageLayout();
        }

        private void UpdateImageLayout()
        {
            var bi = (BitmapImage)BigImage.Source;

            if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
                bi.PixelHeight < this.LayoutGrid.ActualHeight)
            {
                this.BigImage.Stretch = Stretch.None;
            }
            else
            {
                this.BigImage.Stretch = Stretch.Uniform;
            }

            this.UpdateMagnifier();
        }

        private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (!e.Pointer.IsInContact)
            {
                return;
            }

            var point = e.GetCurrentPoint(this.LayoutGrid);
            this.pointerX = point.Position.X;
            this.pointerY = point.Position.Y;
            this.UpdateMagnifier();
        }

        private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
        {
            if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
            {
                zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
            }
            else
            {
                zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
            }

            this.UpdateMagnifier();
        }

        private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            if (!e.Pointer.IsInContact) { return; }

            var point = e.GetCurrentPoint(this.LayoutGrid);

            this.pointerX = point.Position.X;
            this.pointerY = point.Position.Y;
            this.UpdateMagnifier();
            MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
        }

        private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
        {
            MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        }

        private void UpdateMagnifier()
        {
            var bi = (BitmapImage)BigImage.Source;

            double offsetX;
            double offsetY;
            double imageScale;
            CalculateImageScaleAndOffsets(bi, out offsetX, out offsetY, out imageScale);

            MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
            MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
            MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
            MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;

            MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
            MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;

            MagnifierTip.MagnifierTranslateTransform.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
            MagnifierTip.MagnifierTranslateTransform.Y = this.pointerY - (MagnifierTip.ActualHeight);

            bool tooHigh = MagnifierTip.MagnifierTranslateTransform.Y < 0;
            bool tooLeft = MagnifierTip.MagnifierTranslateTransform.X < 0;
            bool tooRight = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth;

            if (tooHigh || tooLeft || tooRight)
            {
                double angle = 0.0;

                if (tooLeft && !tooHigh)
                {
                    var dx = -MagnifierTip.MagnifierTranslateTransform.X;
                    var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
                    var arad = Math.Asin(dx / r);
                    angle = arad * 180 / Math.PI;
                }
                else if (tooLeft && tooHigh)
                {
                    var dx = -MagnifierTip.MagnifierTranslateTransform.X;
                    var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
                    var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
                    var arad1 = Math.Asin(dx / r);
                    var arad2 = Math.Acos((r - dy) / r);
                    var arad = Math.Max(arad1, arad2);
                    angle = arad * 180 / Math.PI;
                }
                else if (tooHigh && !tooRight)
                {
                    var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
                    var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
                    var arad = Math.Acos((r - dy) / r);

                    if (MagnifierTip.MagnifierTranslateTransform.X + Math.Sin(arad) * r + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth)
                    {
                        arad = -arad;
                    }

                    angle = arad * 180 / Math.PI;
                }
                else if (tooHigh)
                {
                    var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
                    var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
                    var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
                    var arad1 = -Math.Acos((r - dy) / r);
                    var arad2 = -Math.Asin(dx / r);
                    var arad = Math.Min(arad1, arad2);
                    angle = arad * 180 / Math.PI;
                }
                else //if (tooRight)
                {
                    var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
                    var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
                    var arad = -Math.Asin(dx / r);
                    angle = arad * 180 / Math.PI;
                }

                MagnifierTip.RotateTransform.Angle = angle;
                MagnifierTip.LensRotateTransform.Angle = -angle;
            }
            else
            {
                MagnifierTip.RotateTransform.Angle = 0;
                MagnifierTip.LensRotateTransform.Angle = 0;
            }
        }

        private void CalculateImageScaleAndOffsets(BitmapImage bi, out double offsetX, out double offsetY, out double imageScale)
        {
            var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
            var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;

            if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
                bi.PixelHeight < this.LayoutGrid.ActualHeight)
            {
                offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
                offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
                imageScale = 1;
            }
            else if (imageRatio < gridRatio)
            {
                offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
                offsetY = 0;
                imageScale = BigImage.ActualHeight / bi.PixelHeight;
            }
            else
            {
                offsetX = 0;
                offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
                imageScale = BigImage.ActualWidth / bi.PixelWidth;
            }
        }
    }
}

Magnifier.xaml

<UserControl
    x:Class="MagnifierApp.Magnifier"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MagnifierApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Height="227"
    Width="170">
    <Grid
        Height="227"
        Width="170"
        RenderTransformOrigin="0.5,1">
        <Grid.RenderTransform>
            <TransformGroup>
                <RotateTransform
                    x:FieldModifier="public"
                    x:Name="RotateTransform"
                    Angle="0" />
                <TranslateTransform
                    x:Name="MagnifierTranslateTransform"
                    x:FieldModifier="public" />
            </TransformGroup>
        </Grid.RenderTransform>
        <Ellipse
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            IsHitTestVisible="False"
            Width="152"
            Height="152"
            Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}"
            Margin="9,10,0,0"
            RenderTransformOrigin="0.5,0.5"
            StrokeThickness="0">
        </Ellipse>
        <Ellipse
            x:Name="MagnifierEllipse"
            x:FieldModifier="public"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            IsHitTestVisible="False"
            Width="152"
            Height="152"
            Stroke="White"
            Margin="9,10,0,0"
            RenderTransformOrigin="0.5,0.5"
            StrokeThickness="0">
            <Ellipse.RenderTransform>
                <RotateTransform
                    x:FieldModifier="public"
                    x:Name="LensRotateTransform"
                    Angle="0" />
            </Ellipse.RenderTransform>
            <Ellipse.Fill>
                <ImageBrush
                    ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
                    Stretch="None"
                    AlignmentX="Left"
                    AlignmentY="Top">
                    <ImageBrush.Transform>
                        <TransformGroup>
                            <TranslateTransform
                                x:FieldModifier="public"
                                x:Name="PositionTransform" />
                            <ScaleTransform
                                x:FieldModifier="public"
                                x:Name="ZoomTransform" />
                            <TranslateTransform
                                x:FieldModifier="public"
                                x:Name="CenterTransform" />
                        </TransformGroup>
                    </ImageBrush.Transform>
                </ImageBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Path
            Data="M85,11 C43.8548,11 10.5,44.3548 10.5,85.5 C10.5,126.645 43.8548,160 85,160 C126.145,160 159.5,126.645 159.5,85.5 C159.5,44.3548 126.145,11 85,11 z M85,0.5 C131.668,0.5 169.5,38.3319 169.5,85 C169.5,103.959 163.256,121.459 152.713,135.558 L151.895,136.625 L152,136.625 L84.2999,226 L18,136.625 L18.1054,136.625 L17.2872,135.558 C6.74375,121.459 0.5,103.959 0.5,85 C0.5,38.3319 38.3319,0.5 85,0.5 z"
            Margin="0,0.5,0,0"
            Stretch="Fill"
            Stroke="Black"
            UseLayoutRounding="False"
            StrokeThickness="0"
            Fill="White" />
    </Grid>
</UserControl>

The fix for 1 here is to Update the magnifier even when within

LayoutGrid_OnPointerPressed

So the updated method looks like

 private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;

            var point = e.GetCurrentPoint(this.LayoutGrid);
            this.pointerX = point.Position.X;
            this.pointerY = point.Position.Y;
            this.UpdateMagnifier();
        }

This gives you the desired behavior that you are talking about.

For resolving the bounds issue

calculate the pointer values above with respect to BitImage instead of LayoutGrid.

if (this.pointerX < 0.0 || this.pointerY < 0.0) return; if (this.pointerX > BigImage.ActualWidth || this.pointerY > BigImage.ActualHeight) return; var bi = (BitmapImage)BigImage.Source;

The rotation when reaching close the edge is all about carefully calculating the rotation angle as it reaches the edge and applying that Rotation transform.

MagnifierTip.RenderTransform = new RotateTransform{Angle=<CalculatedValue>, CenterX=<CalculatedValue>, CenterY=<CalculatedValue>};

Hope this helps.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!