有一些处理高分辨率图片的应用程序会遇到的问题。例如,因为应用程序可使用使用 PhotoChooserTask 和html
MediaLibrary APIs 从图片库获取图片,用户可能遭遇意想不到的像内存占用太高甚至用尽了内存。所以,下面为windows
在应用程序间分享图片制定了一些规则:api
—应用程序应该把高分辨率的照片保存到应用的本地存储里,低分辨率的图片保存到图片库。网络
—当应用程序从图片库中打开一些图片时,能够匹配图片库中的图片和本地存储中的高分辨率照片,好比,根据app
照片文件的文件名async
—应用程序这么作就必须保证适时的清理应用程序本地存储,以免没用的高分辨率图片占用磁盘空间。ui
关于保存图片的低分辨率版本,500万像素是比较恰当的,既能够在 A3 的纸上进行清晰打印,尺寸又足够小能够适应this
各类分享的场合。spa
由于你在图片库中只保存了低分辨率的图片,因此强烈建议你为你的应用程序添加富媒体支持。从而让用户code
方便的到你的应用程序中查看原图,从而有增长了你的应用程序的使用频率。
上面描述的规则和富媒体扩展,你的应用都应该支持,例如,示例应用 Photo Inspector 和 Nokia Pro Camera 应用程序。
另外,高分辨率的图片也不能被其余第三方应用程序得到。
双保存之保存到本地存储和图片库
下面的示例演示了你如何保存高分辨率版本图片到应用的本地存储,低分辨率的版本保持到
照片库以供其它应用使用。特别是当照片在 1000+万 以上的大尺寸时,须要考虑压缩图片而且使用双保存
策略。若是图片超过了 500-800万的范围双保存一般是有意义的。有些像使用 870万分辨率的手机拍出的照
片不须要作额外的工做去压缩图片。除非你考虑到其它特殊的场合,好比分享到一些社交网络上。确保图片
没有超过 Windows Phone 8 的 4096X 4096 像素最大纹理尺寸是比较合理的,由于这可能致使应用程序出现
意想不到的问题,从而能够适当避免处理这么大分辨率的图片。
下面的代码片断演示了缩小图片到 500万(保存到图片库的推荐尺寸)。注意这里演示的双保存是依赖
文件的名称—保持不一样分辨率版本的文件使用相同的文件名称。
using Microsoft.Xna.Framework.Media; using Microsoft.Xna.Framework.Media.PhoneExtensions; using Nokia.Graphics.Imaging; using Windows.Foundation; ... public class Utilities { ... /// <summary> /// Asynchronously saves a low resolution version of given photo to MediaLibrary. If the photo is too /// large to be saved to MediaLibrary as is, also saves the original high resolution photo to application's /// local storage so that the high resolution version is not lost. /// <param name="image">Photo to save</param> /// <returns>Path to the saved file in MediaLibrary</returns> /// </summary> public static async Task<string> SaveAsync(IBuffer image) { var savedPath = ""; if (image != null && image.Length > 0) { uint maxBytes = 2 * 1024 * 1024; // 2 megabytes var maxPixels = 5 * 1024 * 1024; // 5 megapixels var maxSize = new Size(4096, 4096); // Maximum texture size on WP8 is 4096x4096 AutoResizeConfiguration resizeConfiguration = null; using (var editingSession = new EditingSession(image)) { if (editingSession.Dimensions.Width * editingSession.Dimensions.Height > maxPixels) { var compactedSize = CalculateSize(editingSession.Dimensions, maxSize, maxPixels); resizeConfiguration = new AutoResizeConfiguration(maxBytes, compactedSize, new Size(0, 0), AutoResizeMode.Automatic, 0, ColorSpace.Yuv420); } } var filenameBase = "myphotoapp_" + DateTime.UtcNow.Ticks.ToString(); if (resizeConfiguration != null) { // Store high resolution original to application local storage using (var store = IsolatedStorageFile.GetUserStoreForApplication()) { var localPath = @"\LocalImages"; if (!store.DirectoryExists(localPath )) { store.CreateDirectory(localPath ); } using (var file = store.CreateFile(localPath + @"\" + filenameBase + @".jpg")) { using (var localImage = image.AsStream()) { localImage.CopyTo(file); file.Flush(); } } } // Compact the image for saving to the library image = await Nokia.Graphics.Imaging.JpegTools.AutoResizeAsync(image, resizeConfiguration); } using (var library = new MediaLibrary()) { using (var libraryImage = image.AsStream()) { using (var picture = library.SavePictureToCameraRoll(filenameBase, libraryImage)) { savedPath = picture.GetPath(); } } } } return savedPath; } /// <summary> /// Calculates a new size from originalSize so that the maximum area is maxArea /// and maximum size is maxSize. Aspect ratio is preserved. /// </summary> /// <param name="originalSize">Original size</param> /// <param name="maxArea">Maximum area</param> /// <param name="maxSize">Maximum size</param> /// <returns>Area in same aspect ratio fits the limits set in maxArea and maxSize</returns> private static Size CalculateSize(Size originalSize, Size maxSize, double maxArea) { // Make sure that the image does not exceed the maximum size var width = originalSize.Width; var height = originalSize.Height; if (width > maxSize.Width) { var scale = maxSize.Width / width; width = width * scale; height = height * scale; } if (height > maxSize.Height) { var scale = maxSize.Height / height; width = width * scale; height = height * scale; } // Make sure that the image does not exceed the maximum area var originalPixels = width * height; if (originalPixels > maxArea) { var scale = Math.Sqrt(maxArea / originalPixels); width = originalSize.Width * scale; height = originalSize.Height * scale; } return new Size(width, height); } ... }
匹配图片库里和本地保存的文件
PhotoChooserTask and MediaLibrary APIs
如今你有两个分辨率版本的图片,分别在图片库中的分辨率版本和应用本地存储中的高分辨率原图,若是在应用中使用
PhotoChooserTask 或者 Medialibrary APIs 打开图片,下面演示了你如何检查选择的图片,与之匹配的本地高分辨率
是否可用。
using Microsoft.Phone.Tasks; ... public partial class PreviewPage : PhoneApplicationPage { private PhotoChooserTask _photoChooserTask = new PhotoChooserTask(); ... public PreviewPage() { ... _photoChooserTask.Completed += PhotoChooserTask_Completed; } ... private void PhotoChooserTask_Completed(object sender, PhotoResult e) { if (e.TaskResult == TaskResult.OK) { if (e.ChosenPhoto.CanRead && e.ChosenPhoto.Length > 0) { var localStream = Utilities.LocalPhotoFromLibraryPath(e.OriginalFileName); if (localStream != null) { e.ChosenPhoto.Close(); localStream.Position = 0; // Note that while we use the high resolution original here, the BitmapImage // will scale it down for display due to the texture size limit. Therefore // using the high resolution photo would only make sense if we would also be // doing something really high resolution dependent with it InitializePreview(localStream); } else { InitializePreview(e.ChosenPhoto); } } } } ... } ... public class Utilities { ... /// <summary> /// Takes a MediaLibrary photo path and tries to find a local high resolution copy of the same photo. /// </summary> /// <param name="libraryPath">Path to a photo in MediaLibrary</param> /// <returns>Stream to a local copy of the same photo</returns> public static Stream LocalPhotoFromLibraryPath(string libraryPath) { var localPathCandidate = @"\LocalImages\" + FilenameFromPath(libraryPath); using (var store = IsolatedStorageFile.GetUserStoreForApplication()) { if (store.FileExists(localPathCandidate)) { return store.OpenFile(localPathCandidate, FileMode.Open); } } return null; } /// <summary> /// Takes a local high resolution photo path and tries to find MediaLibrary copy of the same photo. /// </summary> /// <param name="localPath">Path to a locally saved photo</param> /// <returns>Stream to a MediaLibrary copy of the same photo</returns> public static Stream LibraryPhotoFromLocalPath(string localPath) { var localFilename = FilenameFromPath(localPath); using (var library = new MediaLibrary()) { using (var pictures = library.Pictures) { for (int i = 0; i < pictures.Count; i++) { using (var picture = pictures[i]) { var libraryFilename = FilenameFromPath(picture.GetPath()); if (localFilename == libraryFilename) { return picture.GetImage(); } } } } } return null; } /// <summary> /// Takes a full path to a file and returns the last path component. /// </summary> /// <param name="path">Path</param> /// <returns>Last component of the given path</returns> private static string FilenameFromPath(string path) { var pathParts = path.Split('\\'); return pathParts[pathParts.Length - 1]; } ... }
富媒体和照片编辑选择器扩展
一个很实用的方式就是你的应用和媒体库创建连接,能够参考 Rich media extensibility (MSDN) 和 Photo edit picker
extensibility (MSDN) 进行集成。从而鼓励用户用你的应用程序打开图片。
经过富媒体扩展,你保存到媒体库中的图片会显示一个连接,用来启动你的应用,当用户单击这个连接打开你的应用时,
会在导航路径中同时传递一个特殊的 token ,你可使用相应的 api 利用这个 token 来打开照片。
经过照片编辑选择器扩展,任何保存在图片库中的图片均可以经过编辑菜单中的编辑选项在你的应用中打开。和富媒体扩展的
方式同样,你的应用也是经过导航的 URI 中的 token 来获取相应的图片。
下面的示例演示你怎样经过一个照片的 token 匹配到本地保存的高分辨率版本的照片
... public class Utilities { ... /// <summary> /// Takes a MediaLibrary photo token and tries to find a local high resolution copy of the same photo. /// </summary> /// <param name="token">Photo token</param> public static Stream LocalPhotoFromLibraryToken(string token) { using (var library = new MediaLibrary()) { using (var picture = library.GetPictureFromToken(token)) { var libraryPath = picture.GetPath(); return LocalPhotoFromLibraryPath(libraryPath ); } } } ... }
从图片库中获取图片的缩略图
若是你的应用程序使用的双保存,意味着你保存在本地的图片在图片库中还有相应的副本,你能够很轻松的从
图片库获取相应的缩略图,而不须要本身在应用程序中另外保存副本。
下面的代码演示了如何从图片库中获取你本地保存图片的缩略图。
using Microsoft.Xna.Framework.Media.PhoneExtensions; ... public class Utilities { ... public struct Photo { public string Filename = null; public Stream Thumbnail = null; } /// <summary> /// Attempts to get a thumbnail for given photo filenames from MediaLibrary. /// </summary> /// <param name="localFilenames">Photo filenames</param> /// <returns>List of photo items for which a thumbnail was found</returns> private static List<Photo> GetLibraryThumbnails(List<string> localFilenames) { var photos = new List<Photo>(); using (var library = new MediaLibrary()) { using (var pictures = library.Pictures) { foreach (var localFilename in localFilenames) { for (int i = 0; i < pictures.Count; i++) { using (var picture = pictures[i]) { var libraryPath = picture.GetPath(); var libraryFilename = FilenameFromPath(libraryPath); if (localFilename == libraryFilename) { var thumbnail = picture.GetThumbnail(); var photo = new Photo() { Filename = localFilename, Thumbnail = thumbnail }; photos.Add(photo); break; } } } } } } return photos; } ... }
保持应用的本地存储整洁
第三个关于保存和加载照片须要注意的就是应用的本地存储避免保存不须要的照片。
下面的方法演示了经过遍历本地保存的图片而且检查媒体库中是否还有相应的副本,
若是没有,则删除本地的文件。
using Microsoft.Xna.Framework.Media.PhoneExtensions; ... public class Utilities { ... /// <summary> /// Goes through all the locally saved photos and tries to find a match for them in the MediaLibrary. /// If a match is not found (photo has been deleted from the MediaLibrary) this routine deletes /// also the locally saved photo. /// </summary> public void CleanLocalPhotos() { using (var store = IsolatedStorageFile.GetUserStoreForApplication()) { var localPath = @"\LocalImages"; if (store.DirectoryExists(localPath )) { var array = store.GetFileNames(localPath + @"\*"); using (var library = new MediaLibrary()) { using (var pictures = library.Pictures) { foreach (var localFilename in array) { var found = false; for (int i = 0; i < pictures.Count && !found; i++) { using (var picture = pictures[i]) { var libraryFilename = FilenameFromPath(picture.GetPath()); if (localFilename == libraryFilename) { found = true; } } } if (!found) { store.DeleteFile(localPath + @"\" + localFilename); } } } } } } } ... }
Nokia Wiki 原文连接:http://developer.nokia.com/Resources/Library/Lumia/#!imaging/working-with-high-resolution-photos/accessing-and-saving-photos.html