/// <summary> /// /// </summary> /// <param name="stream"></param> /// <param name="destFilePath"></param> /// <param name="destFileName"></param> /// <param name="cropOptions"></param> /// <param name="resizeOptions"></param> /// <param name="compressOptions"></param> public void ProcessAndSaveImageFromStream(Stream stream, string destFilePath, string destFileName, ImageCropOpts cropOptions, ImageResizeOpts resizeOptions, ImageCrompressOpts compressOptions, ImageCropOpts maxDimensionsAftersize) { stream.Seek(0, SeekOrigin.Begin); using (var image = Image.FromStream(stream)) { //-- Set the processed image to the original image incase we don't apply any cropping or resizing we'll save the image as is. Image processedImg = image; if (cropOptions != null) { processedImg = ImageCropper.Crop(processedImg, cropOptions); } if (resizeOptions != null) { processedImg = ImageResizer.ResizeImage(processedImg, resizeOptions); } //-- Clip the width or height if it's still too large after resized if (maxDimensionsAftersize != null) { if ((maxDimensionsAftersize.W != 0) && (processedImg.Width > maxDimensionsAftersize.W)) { var widthCropOptions = new ImageCropOpts(0, 0, maxDimensionsAftersize.W, processedImg.Height); processedImg = ImageCropper.Crop(processedImg, widthCropOptions); } if ((maxDimensionsAftersize.H != 0) && (processedImg.Height > maxDimensionsAftersize.H)) { var heightCropOptions = new ImageCropOpts(0, 0, processedImg.Width, maxDimensionsAftersize.H); processedImg = ImageCropper.Crop(processedImg, heightCropOptions); } } //-- TODO: think about putting in a max dimensions crop, where if an image is resized by exact width or height, we clip //-- the corresponding height or width //if (maxDimensionsAftersize != null) { } using (var compressedImgStream = ImageCompressor.CompressImage(processedImg, compressOptions)) { //-- If there's been no cropping or resizing & the original image size is small enough //-- then we're going to save the original stream to avoid image quality degradation if (processedImg.Height == image.Height && processedImg.Width == processedImg.Width) { bool originalImageSizeUnderThreshold = (stream.Length < compressOptions.OriginalByteLengthThreshold); //-- If it's under the threshold then we just use the original image would compounding the compression if (originalImageSizeUnderThreshold) { stream.CopyTo(compressedImgStream); //http://stackoverflow.com/questions/230128/best-way-to-copy-between-two-stream-instances-c-sharp stream.Position = compressedImgStream.Position = 0; ImagePersister.SaveImage(compressedImgStream, destFilePath, destFileName); return; } } ImagePersister.SaveImage(compressedImgStream, destFilePath, destFileName); } } }
/// <summary> /// Take an existing image somewhere on the internet and save it using the destination path/name, crop and resize options. /// </summary> /// <param name="originalImgUrl"></param> /// <param name="destFilePath"></param> /// <param name="destFileName"></param> /// <param name="cropOptions"></param> /// <param name="resizeOptions"></param> /// <remarks> /// This method is useful when saving images from "web" (external sites). It is also useful during the upload / cropping process /// </remarks> public void ProcessAndSaveImageFromWebUrl(string originalImgUrl, string destFilePath, string destFileName, ImageCropOpts cropOptions, ImageResizeOpts resizeOptions, ImageCrompressOpts compressOptions) { using (Stream imageStream = new ImageDownloader().DownloadImageAsStream(originalImgUrl)) { ProcessAndSaveImageFromStream(imageStream, destFilePath, destFileName, cropOptions, resizeOptions, compressOptions, null); } }
/// <summary> /// Crop an image based on start x, start y, width & height /// </summary> /// <param name="img"></param> /// <param name="x"></param> /// <param name="y"></param> /// <param name="w"></param> /// <param name="h"></param> /// <returns></returns> /// <remarks> /// Stangely for large images the x/y dimensions don't always come in right (e.g. /// http://upload.wikimedia.org/wikipedia/commons/a/a8/Vista_de_Madrid_desde_Callao_01.jpg /// In these cases we just check and fix the crop dimensions to be the image width & height /// </remarks> public static Image Crop(Image img, ImageCropOpts o) { var error = string.Empty; if (o == null) { error = "Cannot crop image with null crop options..."; } else if (o.W == 0 || o.H == 0) { error = string.Format("Cannot crop image with w[{0}],h[{1}]. Cropping with a 0px dimension is not possible.", o.W, o.H); } else if (img.Width < (o.X + o.W) || img.Height < (o.Y + o.H)) { var errorMsg = string.Format("Cannot crop image {0}x{1} with crop options x[{2}],w[{3}] and y[{4}],h[5] as crop dimensions exceed the image dimensions.", img.Width, img.Height, o.X, o.W, o.Y, o.H); } if (!string.IsNullOrEmpty(error)) { throw new ArgumentException(error); } //-- Stop stack overflow exception as per remark above if (o.W > img.Width) { o.W = img.Width; } if (o.H > img.Height) { o.H = img.Height; } var cropArea = new System.Drawing.Rectangle(o.X, o.Y, o.W, o.H); return(Crop(img, cropArea)); }
/// <summary> /// Save a thumbnail of an image with x/y dimensions specified by thumbW & thumbH /// </summary> /// <param name="stream"></param> /// <param name="destFilePath"></param> /// <param name="destFileName"></param> /// <param name="thumbW"></param> /// <param name="thumbH"></param> /// <param name="x"></param> /// <param name="y"></param> /// <param name="compressOptions"></param> private void SaveThumbImageWithResizeThenCrop(Stream stream, string destFilePath, string destFileName, int thumbW, int thumbH, ImageCropOpts cropOpts, ImageCrompressOpts compressOptions) { var thumbPixelBuffer = 0; ImageResizeOpts resizeOpts = new ImageResizeOpts() { Mode = ResizeMode.ShrinkOnly, Height = thumbW + thumbPixelBuffer, Width = thumbH + thumbPixelBuffer }; stream.Seek(0, SeekOrigin.Begin); using (var image = Image.FromStream(stream)) { //-- Set the processed image to the original image incase we don't apply any cropping or resizing we'll save the image as is. Image processedImg = image; //-- Do the initial crop if (cropOpts != null) { processedImg = ImageCropper.Crop(image, cropOpts); } //-- If our cropped image dimensions are bigger than the Thumb buffer dimensions if (processedImg.Width > resizeOpts.Width && processedImg.Height > resizeOpts.Height) { processedImg = ImageResizer.ResizeImage(processedImg, resizeOpts); } //-- In case the image width is smaller than the desired thumb width, we enlarge it if (processedImg.Width < thumbW) { //-- If the image is smaller we first enlarge the image based of width, if the height is still to small we enlarge it again var enlargeWidthOptions = new ImageResizeOpts() { Mode = ResizeMode.ExactWidth, Height = 0, Width = thumbH }; //-- First try the width processedImg = ImageResizer.ResizeImage(processedImg, enlargeWidthOptions); } //-- In case the image height is smaller than the desired Thumb height, we enlarge it if (processedImg.Height < thumbH) { ImageResizeOpts enlargeHeightOptions = new ImageResizeOpts() { Mode = ResizeMode.ExactHeight, Height = thumbW, Width = 0 }; processedImg = ImageResizer.ResizeImage(processedImg, enlargeHeightOptions); } //-- If needed, crop the width to be exact if (processedImg.Width > thumbW) { var widthCropOptions = new ImageCropOpts(0, 0, thumbW, processedImg.Height); processedImg = ImageCropper.Crop(processedImg, widthCropOptions); } //-- If needed, crop the height to be exact if (processedImg.Height > thumbH) { var heightCropOptions = new ImageCropOpts(0, 0, processedImg.Width, thumbH); processedImg = ImageCropper.Crop(processedImg, heightCropOptions); } //-- Save the thumb compressed heavily using (var compressedImage = ImageCompressor.CompressImage(processedImg, compressOptions)) { ImagePersister.SaveImage(compressedImage, destFilePath, destFileName); } } }
/// <summary> /// /// </summary> /// <param name="stream"></param> /// <param name="destFilePath"></param> /// <param name="destFileName"></param> /// <param name="x"></param> /// <param name="y"></param> /// <remarks> /// Used for profile Avatar thumbs /// </remarks> public void SaveThumb40x40_HighCompressed(Stream stream, string destFilePath, string destFileName, ImageCropOpts cropOpts) { var compressOptions = new ImageCrompressOpts(90, ImageConstants.Kb05); SaveThumbImageWithResizeThenCrop(stream, destFilePath, destFileName, 40, 40, cropOpts, compressOptions); }