/// <summary> /// Save the uploaded photo as an image on the user folder /// with a greater size than usual but limited, /// for later use on cropping and resizing tasks. /// </summary> /// <param name="photo"></param> /// <param name="virtualPath"></param> public static void SaveEditableProfilePicture(int userID, Stream photo, float angle) { // Check folder or create string virtualPath = LcUrl.RenderAppPath + GetUserPhotoFolder(userID); var folder = System.Web.HttpContext.Current.Server.MapPath(virtualPath); if (!Directory.Exists(folder)) { Directory.CreateDirectory(folder); } // Use file as image using (var srcImg = System.Drawing.Image.FromStream(photo)) { // Resize to maximum allowed size (but not upscale) to allow user cropping later var auxImg = LcImaging.Rotate(srcImg, angle, DefaultBackground); var img = LcImaging.Resize(auxImg, profilePictureFixedSizeWidth * profilePictureOriginalScale, profilePictureFixedSizeHeight * profilePictureOriginalScale, profilePictureSizeMode, LcImaging.AnchorPosition.Center, DefaultBackground); auxImg.Dispose(); // Save: img.Save(folder + avatarName + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg); img.Dispose(); } photo.Dispose(); }
/// <summary> /// Resize, rotate, incoming image, disposing it and saving transformations to disk /// </summary> /// <param name="image"></param> /// <param name="pathFileName"></param> /// <param name="angle"></param> private static void PrepareAndSaveEditablePhoto(Image image, string pathFileName, float angle) { Image img = null; try { // Resize to maximum allowed size (but not upscale) to allow user cropping later var auxImg = LcImaging.Rotate(image, angle, DefaultBackground); var w = FixedSizeWidth * WorkPhotoOriginalScale; var h = FixedSizeHeight * WorkPhotoOriginalScale; if (auxImg.Width < auxImg.Height) { // It's portrait, keep original in portrait to prevent the problem of photo getting smaller // with each rotation; further processing creating optimized photos will make it landscape. var aux = w; w = h; h = aux; } img = LcImaging.Resize(auxImg, w, h, LcImaging.SizeMode.Contain, LcImaging.AnchorPosition.Center, DefaultBackground); auxImg.Dispose(); // Free source, no needed any more and if comes from file, saving will crash image.Dispose(); // Save: img.Save(pathFileName, System.Drawing.Imaging.ImageFormat.Jpeg); } finally { if (img != null) { img.Dispose(); } } }
/// <summary> /// Create from the 'editable image' processed versions: cropped with the given values and /// optimized sizes. /// </summary> /// <param name="virtualPath"></param> private static string ProcessWorkPhoto(string path, string fileName, int x, int y, int width, int height) { var regularFileName = ""; // fileName could be given by a previous save including suffixes, // we need it without suffixes in order to work properly: fileName = System.IO.Path.GetFileNameWithoutExtension(LcUtils.GetNameWithoutSuffix(fileName)); // Remove previous cropped/sized/adapted photos (except editable one), all start with fileName plus dash // File.Delete doesn't allow wildcards, find and delete each one foreach (var f in Directory.GetFiles(path, fileName + "-*", SearchOption.TopDirectoryOnly)) { File.Delete(f); } Image cropImg = null; // Create optimized files using (var img = System.Drawing.Image.FromFile(path + fileName + ".jpg")) { // Crop, if any size // On cropping: // User used the reduced image (fixed size) to choose what to crop, // while we will crop the 'original', x larger image, so multiply every unit cropImg = width > 0 ? LcImaging.Crop(img, x * WorkPhotoOriginalScale, y * WorkPhotoOriginalScale, width * WorkPhotoOriginalScale, height * WorkPhotoOriginalScale) : img; // Size prefix var sizeName = "-" + FixedSizeWidth.ToString() + "x" + FixedSizeHeight.ToString(); // Save image with regular size using (var modImg = LcImaging.Resize(cropImg, FixedSizeWidth, FixedSizeHeight, LcImaging.SizeMode.Contain, LcImaging.AnchorPosition.Center, DefaultBackground)) { regularFileName = fileName + sizeName + ".jpg"; modImg.Save(path + regularFileName, System.Drawing.Imaging.ImageFormat.Jpeg); } // Same as previous but for hi-res 2x devices: (real pixel sizes is double but preserve the original size name to recognize it better adding the @2x suffix) using (var modImg = LcImaging.Resize(cropImg, FixedSizeWidth * 2, FixedSizeHeight * 2, LcImaging.SizeMode.Contain, LcImaging.AnchorPosition.Center, DefaultBackground)) { modImg.Save(path + fileName + sizeName + "@2x.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); } // NOTE Creation of images with more sizes (for small user widgets on reviews/bookings/etc) or filters go here } // if there was a crop: if (width > 0) { // TODO To Review when we enable cropping at UI again, because this gets more complicated with rotation and keeping original orientation // at the 'editable' photo; maybe the cropping must happen when saving the editable version, and not here at each optimized image //// Replace original image with the cropped version, for future 'crop again' tasks //using (var replacedEditableImg = LcImaging.Resize(cropImg, FixedSizeWidth * 4, FixedSizeHeight * 4, LcImaging.SizeMode.Contain, LcImaging.AnchorPosition.Center, DefaultBackground)) //{ // replacedEditableImg.Save(path + fileName + ".jpg"); //} } cropImg.Dispose(); return(regularFileName); }
/// <summary> /// Sets the previously uploaded 'editable avatar' image as the /// current user avatar, cropped with the given values and /// optimized sizes. /// </summary> /// <param name="virtualPath"></param> public static void SaveProfilePicture(int userID, int x, int y, int width, int height) { string virtualPath = LcUrl.RenderAppPath + GetUserPhotoFolder(userID); var folder = System.Web.HttpContext.Current.Server.MapPath(virtualPath); // Remove previous cropped/sized/adapted photos (except editable one), all start with avatarNamePrefix // File.Delete doesn't allow wildcards, find and delete each one foreach (var f in Directory.GetFiles(folder, avatarNamePrefix + "*", SearchOption.TopDirectoryOnly)) { File.Delete(f); } // Create optimized files using (var img = System.Drawing.Image.FromFile(folder + avatarName + ".jpg")) { // Crop var cropImg = LcImaging.Crop(img, x, y, width, height); img.Dispose(); // Size prefix var sizeName = profilePictureFixedSizeWidth.ToString() + "x" + profilePictureFixedSizeHeight.ToString(); // Save image with profile size and original color using (var modImg = LcImaging.Resize(cropImg, profilePictureFixedSizeWidth, profilePictureFixedSizeHeight, LcImaging.SizeMode.Cover, LcImaging.AnchorPosition.Center)) { modImg.Save(folder + avatarNamePrefix + sizeName + ".jpg"); } // Save image with profile size and grayscale (-gray) using (var modImg = LcImaging.Grayscale(LcImaging.Resize(cropImg, profilePictureFixedSizeWidth, profilePictureFixedSizeHeight, LcImaging.SizeMode.Cover, LcImaging.AnchorPosition.Center))) { modImg.Save(folder + avatarNamePrefix + sizeName + "-gray.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); } // Same as previous but for hi-res 2x devices: (real pixel sizes is double but preserve the original size name to recognize it better adding the @2x suffix using (var modImg = LcImaging.Grayscale(LcImaging.Resize(cropImg, profilePictureFixedSizeWidth * 2, profilePictureFixedSizeHeight * 2, LcImaging.SizeMode.Cover, LcImaging.AnchorPosition.Center))) { modImg.Save(folder + avatarNamePrefix + sizeName + "*****@*****.**", System.Drawing.Imaging.ImageFormat.Jpeg); } // NOTE Creation of images with more sizes (for small user widgets on reviews/bookings/etc) or filters go here cropImg.Dispose(); } }
/// <summary> /// Ask to edit the original saved profile picture applying a rotation. /// </summary> /// <param name="userID"></param> /// <param name="angle"></param> /// <returns>True when exists and everything fine, false if no photo exists so couldn't perform task. Throw exception if error</returns> public static bool EditEditableProfilePicture(int userID, float angle) { string virtualPath = LcUrl.RenderAppPath + GetUserPhotoFolder(userID); var folder = System.Web.HttpContext.Current.Server.MapPath(virtualPath); if (!Directory.Exists(folder)) { return(false); } var file = folder + avatarName + ".jpg"; if (!File.Exists(file)) { return(false); } // Use file as image Image img = null; try { using (var srcImg = System.Drawing.Image.FromFile(file)) { // Resize to maximum allowed size (but not upscale) to allow user cropping later var auxImg = LcImaging.Rotate(srcImg, angle, DefaultBackground); img = LcImaging.Resize(auxImg, profilePictureFixedSizeWidth * profilePictureOriginalScale, profilePictureFixedSizeHeight * profilePictureOriginalScale, profilePictureSizeMode, LcImaging.AnchorPosition.Center, DefaultBackground); auxImg.Dispose(); } // Save: img.Save(file, System.Drawing.Imaging.ImageFormat.Jpeg); } finally { if (img != null) { img.Dispose(); } } return(true); }
/// <summary> /// Sends user profile picture, or default image, to the Web Response Output. /// </summary> /// <param name="userID"></param> /// <param name="type"></param> public static void OutputProfilePicture(int userID, string type) { if (type == "2x") { type = "@2x"; } else if (!String.IsNullOrWhiteSpace(type)) { type = "-" + type; } var Response = HttpContext.Current.Response; var userFolder = GetUserPhotoFolder(userID); bool imageAdapted = false; var sizeName = profilePictureFixedSizeWidth.ToString() + "x" + profilePictureFixedSizeHeight.ToString(); // And it happens again, new size change, so the next comment, just two times: // To fix #558, because the change of size (so file name changed too), we do double try // for a while // TODO FUTURE: When there are no more oldSize (or a few, convert it manually), remove this and its use const string veryOldSizeName = "176x184"; const string oldSizeName = "112x118"; // "$avatar" // Standard name of pre-adapted image, just // serve ever the profile adapted photo for now instead the big colored original: var userPhoto = "$avatar-" + sizeName + "-gray" + type + ".jpg"; imageAdapted = true; // Physical image path to userPhoto var path = HttpContext.Current.Request.MapPath(LcUrl.RenderAppPath + userFolder + userPhoto); // Try the OLD size if it doesn't exists the new: if (!System.IO.File.Exists(path)) { imageAdapted = false; // Update name and path userPhoto = "$avatar-" + oldSizeName + "-gray" + type + ".jpg"; path = HttpContext.Current.Request.MapPath(LcUrl.RenderAppPath + userFolder + userPhoto); } // Try the VERY OLD size if it doesn't exists the new: if (!System.IO.File.Exists(path)) { imageAdapted = false; // Update name and path userPhoto = "$avatar-" + veryOldSizeName + "-gray" + type + ".jpg"; path = HttpContext.Current.Request.MapPath(LcUrl.RenderAppPath + userFolder + userPhoto); } // Last fallback: default image if (!System.IO.File.Exists(path)) { imageAdapted = true; if (type == "@2x") { userPhoto = "img/userphotos/u0/avatar-2x.png"; } else { userPhoto = "img/userphotos/u0/avatar.png"; } path = HttpContext.Current.Server.MapPath(LcUrl.RenderAppPath + userPhoto); } try { if (imageAdapted) { new WebImage(path).Write(); } else { // Transform image to Grayscale and profile photo size to avoid big images: // Is inside a using block because ensure close the image ressources and the file using (var img = LcImaging.Grayscale(LcImaging.Resize(new System.Drawing.Bitmap(path), profilePictureFixedSizeWidth, profilePictureFixedSizeHeight, profilePictureSizeMode, LcImaging.AnchorPosition.Center))) { // Telling to the browser that this is a JPG image Response.ContentType = "image/jpg"; // Send the transformed image to the browser img.Save(Response.OutputStream, ImageFormat.Jpeg); } } } catch { } }