在尝试构建图像裁剪器控件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不再支持像视口这样的东西:
P.S。微软真的应该停止搞乱他们的平台。曾经在过去工作的东西不再适用,没有其他好的选择。超级沮丧。
答案 0 :(得分: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;
}
}