/// <summary> /// Function to resize the image to a new width, height and/or depth. /// </summary> /// <param name="baseImage">The image to resize.</param> /// <param name="newWidth">The new width for the image.</param> /// <param name="newHeight">The new height for the image (for <see cref="ImageType.Image2D"/> and <see cref="ImageType.ImageCube"/> images).</param> /// <param name="newDepth">The new depth for the image (for <see cref="ImageType.Image3D"/> images).</param> /// <param name="filter">[Optional] The type of filtering to apply to the scaled image to help smooth larger and smaller images.</param> /// <returns>A <see cref="IGorgonImage"/> containing the resized image.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> parameter is <b>null</b>.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown if the <paramref name="newWidth"/>, <paramref name="newHeight"/>, or <paramref name="newDepth"/> parameters are less than 1.</exception> /// <exception cref="GorgonException">Thrown if there was an error during resizing regarding image pixel format conversion due to the type of <paramref name="filter"/> applied.</exception> /// <remarks> /// <para> /// This method will change the size of an existing image and return a larger or smaller version of itself as a new <see cref="IGorgonImage"/>. /// </para> /// </remarks> public static IGorgonImage Resize(this IGorgonImage baseImage, int newWidth, int newHeight, int newDepth, ImageFilter filter = ImageFilter.Point) { if (baseImage == null) { throw new ArgumentNullException(nameof(baseImage)); } if (newWidth < 1) { throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_WIDTH_TOO_SMALL, nameof(newWidth)); } if ((newHeight < 1) && ((baseImage.ImageType == ImageType.Image2D) || (baseImage.ImageType == ImageType.ImageCube))) { throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_HEIGHT_TOO_SMALL, nameof(newHeight)); } if ((newDepth < 1) && (baseImage.ImageType == ImageType.Image3D)) { throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_DEPTH_TOO_SMALL, nameof(newDepth)); } // Only use the appropriate dimensions. switch (baseImage.ImageType) { case ImageType.Image1D: newHeight = baseImage.Height; break; case ImageType.Image2D: case ImageType.ImageCube: newDepth = baseImage.Depth; break; } // If we haven't actually changed the size, then skip out. if ((newWidth == baseImage.Width) && (newHeight == baseImage.Height) && (newDepth == baseImage.Depth)) { return(baseImage); } var wic = new WicUtilities(); IGorgonImage newImage = null; try { int calcMipLevels = GorgonImage.CalculateMaxMipCount(newWidth, newHeight, newDepth).Min(baseImage.MipCount); newImage = wic.Resize(baseImage, 0, 0, newWidth, newHeight, newDepth, calcMipLevels, filter, ResizeMode.Scale); newImage.CopyTo(baseImage); return(baseImage); } finally { newImage?.Dispose(); wic.Dispose(); } }
/// <summary> /// Function to crop the image to the rectangle passed to the parameters. /// </summary> /// <param name="baseImage">The image to resize.</param> /// <param name="cropRect">The rectangle that will be used to crop the image.</param> /// <param name="newDepth">The new depth for the image (for <see cref="ImageType.Image3D"/> images).</param> /// <returns>A <see cref="IGorgonImage"/> containing the resized image.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> parameter is <b>null</b>.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown if the <paramref name="newDepth"/> parameter is less than 1.</exception> /// <remarks> /// <para> /// This method will crop the existing image a smaller version of itself as a new <see cref="IGorgonImage"/>. If the sizes are the same, or the <paramref name="cropRect"/> is larger than the size /// of the <paramref name="baseImage"/>, then no changes will be made. /// </para> /// </remarks> public static IGorgonImage Crop(this IGorgonImage baseImage, DX.Rectangle cropRect, int newDepth) { if (baseImage == null) { throw new ArgumentNullException(nameof(baseImage)); } if ((newDepth < 1) && (baseImage.ImageType == ImageType.Image3D)) { throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_DEPTH_TOO_SMALL, nameof(newDepth)); } // Only use the appropriate dimensions. switch (baseImage.ImageType) { case ImageType.Image1D: cropRect.Height = baseImage.Height; break; case ImageType.Image2D: case ImageType.ImageCube: newDepth = baseImage.Depth; break; } // If the intersection of the crop rectangle and the source buffer are the same (and the depth is the same), then we don't need to crop. var bufferRect = new DX.Rectangle(0, 0, baseImage.Width, baseImage.Height); var clipRect = DX.Rectangle.Intersect(cropRect, bufferRect); if ((bufferRect.Equals(ref clipRect)) && (newDepth == baseImage.Depth)) { return(baseImage); } var wic = new WicUtilities(); IGorgonImage newImage = null; try { int calcMipLevels = GorgonImage.CalculateMaxMipCount(cropRect.Width, cropRect.Height, newDepth).Min(baseImage.MipCount); newImage = wic.Resize(baseImage, cropRect.X, cropRect.Y, cropRect.Width, cropRect.Height, newDepth, calcMipLevels, ImageFilter.Point, ResizeMode.Crop); // Send the data over to the new image. newImage.CopyTo(baseImage); return(baseImage); } finally { newImage?.Dispose(); wic.Dispose(); } }
/// <summary> /// Function to expand an image width, height, and/or depth. /// </summary> /// <param name="baseImage">The image to expand.</param> /// <param name="newWidth">The new width of the image.</param> /// <param name="newHeight">The new height of the image.</param> /// <param name="newDepth">The new depth of the image.</param> /// <param name="anchor">[Optional] The anchor point for placing the image data after the image is expanded.</param> /// <returns>The expanded image.</returns> /// <remarks> /// <para> /// This will expand the size of an image, but not stretch the actual image data. This will leave a padding around the original image area filled with transparent pixels. /// </para> /// <para> /// The image data can be repositioned in the new image by specifying an <paramref name="anchor"/> point. /// </para> /// <para> /// If the new size of the image is smaller than that of the <paramref name="baseImage"/>, then the new size is constrained to the old size. Cropping is not supported by this method. /// </para> /// <para> /// If a user wishes to resize the image, then call the <see cref="Resize"/> method, of if they wish to crop an image, use the <see cref="Crop"/> method. /// </para> /// </remarks> public static IGorgonImage Expand(this IGorgonImage baseImage, int newWidth, int newHeight, int newDepth, ImageExpandAnchor anchor = ImageExpandAnchor.UpperLeft) { IGorgonImage workingImage = null; WicUtilities wic = null; try { // Constrain to the correct sizes. newWidth = newWidth.Max(baseImage.Width); newHeight = newHeight.Max(baseImage.Height); newDepth = newDepth.Max(baseImage.Depth); // Only use the appropriate dimensions. switch (baseImage.ImageType) { case ImageType.Image1D: newHeight = baseImage.Height; break; case ImageType.Image2D: case ImageType.ImageCube: newDepth = baseImage.Depth; break; } // We don't shink with this method, use the Crop method for that. if ((newWidth <= baseImage.Width) && (newHeight <= baseImage.Height) && (newDepth <= baseImage.Depth)) { return(baseImage); } wic = new WicUtilities(); workingImage = new GorgonImage(new GorgonImageInfo(baseImage) { Width = newWidth, Height = newHeight, Depth = newDepth }); DX.Point position = DX.Point.Zero; switch (anchor) { case ImageExpandAnchor.UpperMiddle: position = new DX.Point((newWidth / 2) - (baseImage.Width / 2), 0); break; case ImageExpandAnchor.UpperRight: position = new DX.Point(newWidth - baseImage.Width, 0); break; case ImageExpandAnchor.MiddleLeft: position = new DX.Point(0, (newHeight / 2) - (baseImage.Height / 2)); break; case ImageExpandAnchor.Center: position = new DX.Point((newWidth / 2) - (baseImage.Width / 2), (newHeight / 2) - (baseImage.Height / 2)); break; case ImageExpandAnchor.MiddleRight: position = new DX.Point(newWidth - baseImage.Width, (newHeight / 2) - (baseImage.Height / 2)); break; case ImageExpandAnchor.BottomLeft: position = new DX.Point(0, newHeight - baseImage.Height); break; case ImageExpandAnchor.BottomMiddle: position = new DX.Point((newWidth / 2) - (baseImage.Width / 2), newHeight - baseImage.Height); break; case ImageExpandAnchor.BottomRight: position = new DX.Point(newWidth - baseImage.Width, newHeight - baseImage.Height); break; } int calcMipLevels = GorgonImage.CalculateMaxMipCount(newWidth, newHeight, newDepth).Min(baseImage.MipCount); workingImage = wic.Resize(baseImage, position.X, position.Y, newWidth, newHeight, newDepth, calcMipLevels, ImageFilter.Point, ResizeMode.Expand); // Send the data over to the new image. workingImage.CopyTo(baseImage); return(baseImage); } finally { workingImage?.Dispose(); wic?.Dispose(); } }