操纵图像(翻译+缩放),然后在Universal App中裁剪图像?

时间:2015-01-01 22:06:41

标签: c# image winrt-xaml crop writeablebitmapex

在尝试构建图像裁剪器控件2天后,我转向StackOverflow寻找答案。

我将我的WP8.0(Silverlight)应用程序移植到WinRT(WP8.1和W8.1的通用应用程序),我得到了所有内容......除了用户从中选择图像的页面图片库,它被加载到页面上。然后用户可以移动图像或放大图像。有一个白色的矩形边框是裁剪矩形(864px 605px - 这也应该是输出)。

最好的方法是什么?加载的图像分辨率较高,因此我的猜测是将屏幕上的像素重新计算到图像像素。此代码适用于翻译,但是当图像缩放时,会出现奇怪的偏移:

XAML:     

<Path x:Name="SelectionPath" Stroke="Red" StrokeThickness="3" />

C#

public async void SetImage(StorageFile selectedFile)
    {
        Photo.ManipulationDelta += Composite_ManipulationDelta;
        compositeTranslation = new CompositeTransform();
        Photo.RenderTransform = this.compositeTranslation;
        Photo.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY | ManipulationModes.Scale | ManipulationModes.Rotate;
        storageFile = selectedFile;



        storageFile = selectedFile;
        IRandomAccessStream inputStream = await selectedFile.OpenAsync(FileAccessMode.Read);

       var properties = await storageFile.Properties.GetImagePropertiesAsync();
       ImgWidth = properties.Width;


        BitmapImage tempImage = new BitmapImage();

        tempImage.SetSource(inputStream);
        Photo.Source = tempImage;
    }

    void Composite_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        // scale the image.
        compositeTranslation.CenterX = Photo.ActualWidth / 2;
        compositeTranslation.CenterY = Photo.ActualHeight / 2;
        compositeTranslation.ScaleX *= e.Delta.Scale;
        compositeTranslation.ScaleY *= e.Delta.Scale;
        compositeTranslation.TranslateX += e.Delta.Translation.X;
        compositeTranslation.TranslateY += e.Delta.Translation.Y;


        GeneralTransform transform = Photo.TransformToVisual(LayoutRoot);
        Point controlPosition = transform.TransformPoint(new Point(0, 0));

        int pointX = (int)controlPosition.X;
        int pointY = (int)controlPosition.Y;



    }
public async Task CropImage()
    {

        IBuffer data = await FileIO.ReadBufferAsync(storageFile);

        // create a stream from the file
        InMemoryRandomAccessStream ms = new InMemoryRandomAccessStream();
        DataWriter dw = new DataWriter(ms);
        dw.WriteBuffer(data);
        await dw.StoreAsync();
        ms.Seek(0);

        // find out how big the image is, don't need this if you already know
        BitmapImage bm = new BitmapImage();
        await bm.SetSourceAsync(ms);

        // create a writable bitmap of the right size
        WriteableBitmap wb = new WriteableBitmap(bm.PixelWidth, bm.PixelHeight);
        ms.Seek(0);

        // load the writable bitpamp from the stream
        await wb.SetSourceAsync(ms);

               GeneralTransform transform = Photo.TransformToVisual(LayoutRoot);
        Point controlPosition = transform.TransformPoint(new Point(0, 0));
        int pointX = (int)controlPosition.X;
        int pointY = (int)controlPosition.Y;

        int CropPosX = 0 - pointX;
        int CropPosY = 0 - pointY;

        int UIXPos = (int)Math.Round(CropPosX * compositeTranslation.ScaleX);
        int UIYPos = 413 + (int)Math.Round(CropPosY * compositeTranslation.ScaleY);

        double ImgFactor = ImgWidth / Photo.ActualWidth;
        int ImgXPos = (int)Math.Round(UIXPos * ImgFactor);
        int ImgYPos = (int)Math.Round(UIYPos * ImgFactor);

        //int CropWidth = 864;
        //int CropHeight = 605;



        int CropWidth = (int)Math.Round(864 * ImgFactor);
        int CropHeight = (int)Math.Round(605 * ImgFactor);
        WriteableBitmap wb2 = wb.Crop(ImgXPos, ImgYPos, CropWidth, CropHeight);
        CroppedImage.Source = wb2;
    }

非常感谢任何指针!

Lumia Imaging SDK(项目:Image Sequencer)完全符合我的需求,但他们的示例基于Silverlight。 WinRT不再支持像视口这样的东西:

http://developer.nokia.com/resources/library/Lumia/nokia-imaging-sdk/sample-projects/image-sequencer.html#toc_Seealso

P.S。微软真的应该停止搞乱他们的平台。曾经在过去工作的东西不再适用,没有其他好的选择。超级沮丧。

2 个答案:

答案 0 :(得分:1)

您可以在此处找到此示例(和其他)移植到通用应用程序:

https://github.com/Microsoft/image-sequencer?files=1

答案 1 :(得分:0)

using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media.Imaging;


    /// <summary>
    /// Offers some tools for editing bitmaps.
    /// </summary>
    public class BitmapTools
    {
        /// <summary>
        /// Gets the cropped bitmap asynchronously.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="startPoint">The start point.</param>
        /// <param name="cropSize">Size of the corp.</param>
        /// <param name="scale">The scale.</param>
        /// <returns>The cropped image.</returns>
        public static async Task<WriteableBitmap> GetCroppedBitmapAsync(IRandomAccessStream originalImage,
            Point startPoint, Size cropSize, double scale)
        {
            if (double.IsNaN(scale) || double.IsInfinity(scale))
            {
                scale = 1;
            }

            // Convert start point and size to integer.
            var startPointX = (uint)Math.Floor(startPoint.X * scale);
            var startPointY = (uint)Math.Floor(startPoint.Y * scale);
            var height = (uint)Math.Floor(cropSize.Height * scale);
            var width = (uint)Math.Floor(cropSize.Width * scale);

            // Create a decoder from the stream. With the decoder, we can get 
            // the properties of the image.
            var decoder = await BitmapDecoder.CreateAsync(originalImage);

            // The scaledSize of original image.
            var scaledWidth = (uint)Math.Floor(decoder.PixelWidth * scale);
            var scaledHeight = (uint)Math.Floor(decoder.PixelHeight * scale);

            // Refine the start point and the size. 
            if (startPointX + width > scaledWidth)
            {
                startPointX = scaledWidth - width;
            }

            if (startPointY + height > scaledHeight)
            {
                startPointY = scaledHeight - height;
            }

            // Get the cropped pixels.
            var pixels = await GetPixelData(decoder, startPointX, startPointY, width, height,
                scaledWidth, scaledHeight);

            // Stream the bytes into a WriteableBitmap
            var cropBmp = new WriteableBitmap((int)width, (int)height);
            var pixStream = cropBmp.PixelBuffer.AsStream();
            pixStream.Write(pixels, 0, (int)(width * height * 4));

            return cropBmp;
        }

        /// <summary>
        /// Gets the pixel data.
        /// </summary>
        /// <remarks>
        /// If you want to get the pixel data of a scaled image, set the scaledWidth and scaledHeight
        /// of the scaled image.
        /// </remarks>
        /// <param name="decoder">The bitmap decoder.</param>
        /// <param name="startPointX">The X coordinate of the start point.</param>
        /// <param name="startPointY">The Y coordinate of the start point.</param>
        /// <param name="width">The width of the source rect.</param>
        /// <param name="height">The height of the source rect.</param>
        /// <param name="scaledWidth">The desired width.</param>
        /// <param name="scaledHeight">The desired height.</param>
        /// <returns>The image data.</returns>
        private static async Task<byte[]> GetPixelData(BitmapDecoder decoder, uint startPointX, uint startPointY,
            uint width, uint height, uint scaledWidth, uint scaledHeight)
        {
            var transform = new BitmapTransform();
            var bounds = new BitmapBounds();
            bounds.X = startPointX;
            bounds.Y = startPointY;
            bounds.Height = height;
            bounds.Width = width;
            transform.Bounds = bounds;

            transform.ScaledWidth = scaledWidth;
            transform.ScaledHeight = scaledHeight;

            // Get the cropped pixels within the bounds of transform.
            var pix = await decoder.GetPixelDataAsync(
                BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Straight,
                transform,
                ExifOrientationMode.IgnoreExifOrientation,
                ColorManagementMode.ColorManageToSRgb);
            var pixels = pix.DetachPixelData();
            return pixels;
        }

        /// <summary>
        /// Resizes the specified stream.
        /// </summary>
        /// <param name="sourceStream">The source stream to resize.</param>
        /// <param name="newWidth">The width of the resized image.</param>
        /// <param name="newHeight">The height of the resized image.</param>
        /// <returns>The resized image stream.</returns>
        public static async Task<InMemoryRandomAccessStream> Resize(IRandomAccessStream sourceStream, uint newWidth,
            uint newHeight)
        {
            var destinationStream = new InMemoryRandomAccessStream();

            var decoder = await BitmapDecoder.CreateAsync(sourceStream);
            var transform = new BitmapTransform { ScaledWidth = newWidth, ScaledHeight = newHeight };

            var pixelData = await decoder.GetPixelDataAsync(
                BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Straight,
                transform,
                ExifOrientationMode.RespectExifOrientation,
                ColorManagementMode.DoNotColorManage);

            var encoder =
                await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, destinationStream);

            encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, newWidth, newHeight, 96, 96,
                pixelData.DetachPixelData());
            await encoder.FlushAsync();

            return destinationStream;
        }

        /// <summary>
        /// Rotates the given stream.
        /// </summary>
        /// <param name="randomAccessStream">The random access stream.</param>
        /// <param name="rotation">The rotation.</param>
        /// <returns>The stream.</returns>
        public static async Task<InMemoryRandomAccessStream> Rotate(IRandomAccessStream randomAccessStream,
            BitmapRotation rotation)
        {
            var decoder = await BitmapDecoder.CreateAsync(randomAccessStream);

            var rotatedStream = new InMemoryRandomAccessStream();

            var encoder = await BitmapEncoder.CreateForTranscodingAsync(rotatedStream, decoder);

            encoder.BitmapTransform.Rotation = rotation;
            encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;

            await encoder.FlushAsync();

            return rotatedStream;
        }
    }