/// <summary> /// Resizes the texture. (If original texture has mipmaps, all mipmap levels are automatically /// recreated.) /// </summary> /// <param name="width">The new width.</param> /// <param name="height">The new height.</param> /// <param name="depth">The new depth. Must be 1 for 2D textures and cube map textures.</param> /// <param name="filter">The filter to use for resizing.</param> /// <param name="alphaTransparency"> /// <see langword="true"/> if the image contains uses non-premultiplied alpha; otherwise, /// <see langword="false"/> if the image uses premultiplied alpha or has no alpha. /// </param> /// <param name="wrapMode"> /// The texture address mode that will be used for sampling the at runtime. /// </param> /// <returns>The resized texture.</returns> public Texture Resize(int width, int height, int depth, ResizeFilter filter, bool alphaTransparency, TextureAddressMode wrapMode) { var description = Description; description.Width = width; description.Height = height; description.Depth = depth; var resizedTexture = new Texture(description); // Resize mipmap level 0. for (int arrayIndex = 0; arrayIndex < description.ArraySize; arrayIndex++) TextureHelper.Resize(this, 0, arrayIndex, resizedTexture, 0, arrayIndex, filter, alphaTransparency, wrapMode); // Regenerate mipmap levels, if necessary. if (description.MipLevels > 1) resizedTexture.GenerateMipmaps(filter, alphaTransparency, wrapMode); return resizedTexture; }
public static void Save(Image image, Stream stream) { if (image == null) throw new ArgumentNullException("image"); if (stream == null) throw new ArgumentNullException("stream"); var convFlags = ConversionFlags.None; var header = EncodeTgaHeader(image, ref convFlags); // Write header. stream.WriteStruct(header); // Determine memory required for image data. int rowPitch = (convFlags & ConversionFlags.Format888) != 0 ? image.Width * 3 : image.RowPitch; // Write pixels. using (var sourceStream = new MemoryStream(image.Data, false)) using (var reader = new BinaryReader(sourceStream)) using (var writer = new BinaryWriter(stream, Encoding.Default, true)) #else using (var writer = new BinaryWriter(stream, Encoding.Default)) // Warning: Closes the stream! { for (int y = 0; y < image.Height; y++) { if ((convFlags & ConversionFlags.Format888) != 0) { Copy24BppScanline(reader, image.RowPitch, writer); } else if ((convFlags & ConversionFlags.Swizzle) != 0) { TextureHelper.SwizzleScanline(reader, image.RowPitch, writer, rowPitch, image.Format, ScanlineFlags.None); } else { TextureHelper.CopyScanline(reader, image.RowPitch, writer, rowPitch, image.Format, ScanlineFlags.None); } } } }
/// <summary> /// Determines whether conversion to the specified texture format is supported. /// </summary> /// <param name="format">The desired texture format.</param> /// <returns> /// <see langword="true"/> if the conversion from the current format to <paramref name="format"/> /// is supported; otherwise, <see langword="false"/>. /// </returns> public bool CanConvertTo(DataFormat format) { var srcFormat = Description.Format; var dstFormat = format; // srcFormat -> dstFormat if (TextureHelper.CanConvert(srcFormat, dstFormat)) return true; // srcFormat -> R32G32B32A32_FLOAT -> dstFormat if (TextureHelper.CanConvert(srcFormat, DataFormat.R32G32B32A32_FLOAT) && TextureHelper.CanConvert(DataFormat.R32G32B32A32_FLOAT, dstFormat)) return true; // srcFormat -> R8G8B8A8_UNORM -> dstFormat if (TextureHelper.CanConvert(srcFormat, DataFormat.R8G8B8A8_UNORM) && TextureHelper.CanConvert(DataFormat.R8G8B8A8_UNORM, dstFormat)) return true; return false; }
/// <summary> /// Gets the pixel at the specified position. /// </summary> /// <param name="x">The x position.</param> /// <param name="y">The y position.</param> /// <param name="wrapMode">The wrap mode.</param> /// <returns>The pixel color.</returns> public Vector4 GetPixel(int x, int y, TextureAddressMode wrapMode) { switch (wrapMode) { case TextureAddressMode.Clamp: x = TextureHelper.WrapClamp(x, _width); y = TextureHelper.WrapClamp(y, _height); break; case TextureAddressMode.Repeat: x = TextureHelper.WrapRepeat(x, _width); y = TextureHelper.WrapRepeat(y, _height); break; case TextureAddressMode.Mirror: x = TextureHelper.WrapMirror(x, _width); y = TextureHelper.WrapMirror(y, _height); break; } return(GetPixel(y * _width + x)); }
/// <summary> /// (Re-)Generates all mipmap levels. /// </summary> /// <param name="filter">The filter to use for resizing.</param> /// <param name="alphaTransparency"> /// <see langword="true"/> if the image contains uses non-premultiplied alpha; otherwise, /// <see langword="false"/> if the image uses premultiplied alpha or has no alpha. /// </param> /// <param name="wrapMode"> /// The texture address mode that will be used for sampling the at runtime. /// </param> public void GenerateMipmaps(ResizeFilter filter, bool alphaTransparency, TextureAddressMode wrapMode) { var oldDescription = Description; var newDescription = Description; // Determine number of mipmap levels. if (oldDescription.Dimension == TextureDimension.Texture3D) newDescription.MipLevels = TextureHelper.CalculateMipLevels(oldDescription.Width, oldDescription.Height, oldDescription.Depth); else newDescription.MipLevels = TextureHelper.CalculateMipLevels(oldDescription.Width, oldDescription.Height); if (oldDescription.MipLevels != newDescription.MipLevels) { // Update Description and Images. var oldImages = Images; Description = newDescription; ValidateTexture(newDescription); // Recreate image collection. (Mipmap level 0 is copied from existing image collection.) Images = CreateImageCollection(newDescription, true); for (int arrayIndex = 0; arrayIndex < newDescription.ArraySize; arrayIndex++) { for (int zIndex = 0; zIndex < newDescription.Depth; zIndex++) { int oldIndex = oldDescription.GetImageIndex(0, arrayIndex, zIndex); int newIndex = newDescription.GetImageIndex(0, arrayIndex, zIndex); Images[newIndex] = oldImages[oldIndex]; } } } // Downsample mipmap levels. for (int arrayIndex = 0; arrayIndex < newDescription.ArraySize; arrayIndex++) for (int mipIndex = 0; mipIndex < newDescription.MipLevels - 1; mipIndex++) TextureHelper.Resize(this, mipIndex, arrayIndex, this, mipIndex + 1, arrayIndex, filter, alphaTransparency, wrapMode); }
/// <summary> /// Determines the number of image array entries and pixel size. /// </summary> /// <param name="description">The texture description.</param> /// <param name="nImages">The number of entries in the image array.</param> /// <param name="pixelSize">The total pixel size.</param> private static void DetermineImages(TextureDescription description, out int nImages, out int pixelSize) { Debug.Assert(description.Width > 0 && description.Height > 0 && description.Depth > 0); Debug.Assert(description.ArraySize > 0); Debug.Assert(description.MipLevels > 0); pixelSize = 0; nImages = 0; switch (description.Dimension) { case TextureDimension.Texture1D: case TextureDimension.Texture2D: case TextureDimension.TextureCube: for (int item = 0; item < description.ArraySize; item++) { int w = description.Width; int h = description.Height; for (int level = 0; level < description.MipLevels; level++) { int rowPitch, slicePitch; TextureHelper.ComputePitch(description.Format, w, h, out rowPitch, out slicePitch, ComputePitchFlags.None); pixelSize += slicePitch; nImages++; if (h > 1) h >>= 1; if (w > 1) w >>= 1; } } break; case TextureDimension.Texture3D: { int w = description.Width; int h = description.Height; int d = description.Depth; for (int level = 0; level < description.MipLevels; level++) { int rowPitch, slicePitch; TextureHelper.ComputePitch(description.Format, w, h, out rowPitch, out slicePitch, ComputePitchFlags.None); for (int slice = 0; slice < d; slice++) { pixelSize += slicePitch; nImages++; } if (h > 1) h >>= 1; if (w > 1) w >>= 1; if (d > 1) d >>= 1; } } break; default: Debug.Fail("Unexpected texture dimension"); break; } }
private static TextureDescription DecodeMetadata(ImagingFactory imagingFactory, WicFlags flags, BitmapDecoder decoder, BitmapFrameDecode frame, out Guid pixelFormat) { var size = frame.Size; var description = new TextureDescription { Dimension = TextureDimension.Texture2D, Width = size.Width, Height = size.Height, Depth = 1, MipLevels = 1, ArraySize = (flags & WicFlags.AllFrames) != 0 ? decoder.FrameCount : 1, Format = DetermineFormat(imagingFactory, frame.PixelFormat, flags, out pixelFormat) }; if (description.Format == DataFormat.Unknown) throw new NotSupportedException("The pixel format is not supported."); if ((flags & WicFlags.IgnoreSrgb) == 0) { // Handle sRGB. #pragma warning disable 168 try { Guid containerFormat = decoder.ContainerFormat; var metareader = frame.MetadataQueryReader; if (metareader != null) { // Check for sRGB color space metadata. bool sRgb = false; if (containerFormat == ContainerFormatGuids.Png) { // Check for sRGB chunk. if (metareader.GetMetadataByName("/sRGB/RenderingIntent") != null) sRgb = true; } else if (containerFormat == ContainerFormatGuids.Jpeg) { if (Equals(metareader.GetMetadataByName("/app1/ifd/exif/{ushort=40961}"), 1)) sRgb = true; } else if (containerFormat == ContainerFormatGuids.Tiff) { if (Equals(metareader.GetMetadataByName("/ifd/exif/{ushort=40961}"), 1)) sRgb = true; } else { if (Equals(metareader.GetMetadataByName("System.Image.ColorSpace"), 1)) sRgb = true; } if (sRgb) description.Format = TextureHelper.MakeSRgb(description.Format); } } // ReSharper disable once EmptyGeneralCatchClause catch (Exception exception) { // Some formats just don't support metadata (BMP, ICO, etc.). } } #pragma warning restore 168 return description; }