有一些處理高分辨率圖片的應用程序會遇到的問題。例如,因爲應用程序可使用使用 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