//-------------------------------------------------------------- #region XNA Content Pipeline //-------------------------------------------------------------- /// <summary> /// Converts an XNA <see cref="TextureContent"/> to a DigitalRune <see cref="Texture"/>. /// </summary> /// <param name="textureContent">The <see cref="TextureContent"/>.</param> /// <returns>The <see cref="Texture"/>.</returns> public static Texture ToTexture(TextureContent textureContent) { SurfaceFormat surfaceFormat; var bitmapContent0 = textureContent.Faces[0][0]; if (!bitmapContent0.TryGetFormat(out surfaceFormat)) { throw new InvalidContentException("Invalid surface format.", textureContent.Identity); } var texture2DContent = textureContent as Texture2DContent; if (texture2DContent != null) { var description = new TextureDescription { Dimension = TextureDimension.Texture2D, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = 1, MipLevels = texture2DContent.Mipmaps.Count, ArraySize = 1, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = texture2DContent.Mipmaps[mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, 0, 0)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } return(texture); } var textureCubeContent = textureContent as TextureCubeContent; if (textureCubeContent != null) { var description = new TextureDescription { Dimension = TextureDimension.TextureCube, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = 1, MipLevels = textureCubeContent.Faces[0].Count, ArraySize = 6, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int faceIndex = 0; faceIndex < 6; faceIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = textureCubeContent.Faces[faceIndex][mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, faceIndex, 0)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } } return(texture); } var texture3DContent = textureContent as Texture3DContent; if (texture3DContent != null) { var description = new TextureDescription { Dimension = TextureDimension.Texture3D, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = texture3DContent.Faces.Count, MipLevels = texture3DContent.Faces[0].Count, ArraySize = 1, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int zIndex = 0; zIndex < description.Depth; zIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = texture3DContent.Faces[zIndex][mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, 0, zIndex)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } } return(texture); } throw new InvalidOperationException("Invalid texture dimension."); }
//-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="Texture"/> class. /// </summary> /// <param name="description">The description.</param> /// <exception cref="ArgumentException"> /// The <paramref name="description"/> is invalid. /// </exception> public Texture(TextureDescription description) { ValidateTexture(description); Description = description; Images = CreateImageCollection(description); }
/// <summary> /// Decodes the TGA file header. /// </summary> /// <param name="stream">The stream.</param> /// <param name="offset">The offset in the stream at which the data starts.</param> /// <param name="convFlags">The conversion flags.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="stream"/> is <see langword="null"/>. /// </exception> /// <exception cref="InvalidDataException"> /// Invalid data. /// </exception> /// <exception cref="NotSupportedException"> /// The specified format is not supported. /// </exception> private static TextureDescription DecodeTGAHeader(Stream stream, out int offset, out ConversionFlags convFlags) { if (stream == null) { throw new ArgumentNullException("stream"); } TextureDescription description = new TextureDescription { Dimension = TextureDimension.Texture2D, Format = DataFormat.R8G8B8A8_UNORM, }; offset = 0; convFlags = ConversionFlags.None; long size = stream.Length - stream.Position; int sizeOfTGAHeader = Marshal.SizeOf(typeof(Header)); if (size < sizeOfTGAHeader) { throw new InvalidDataException("The TGA file is corrupt."); } var header = stream.ReadStruct <Header>(); if (header.ColorMapType != 0 || header.ColorMapLength != 0) { throw new NotSupportedException("TGA files with color maps are not supported."); } if ((header.Descriptor & (DescriptorFlags.Interleaved2Way | DescriptorFlags.Interleaved4Way)) != 0) { throw new NotSupportedException("TGA files with interleaved images are not supported."); } if (header.Width == 0 || header.Height == 0) { throw new NotSupportedException("The TGA file is corrupt. Width and height are invalid."); } switch (header.ImageType) { case ImageType.TrueColor: case ImageType.TrueColorRLE: switch (header.BitsPerPixel) { case 16: description.Format = DataFormat.B5G5R5A1_UNORM; break; case 24: description.Format = DataFormat.R8G8B8A8_UNORM; convFlags |= ConversionFlags.Expand; // We could use DXGI_FORMAT_B8G8R8X8_UNORM, but we prefer DXGI 1.0 formats break; case 32: description.Format = DataFormat.R8G8B8A8_UNORM; // We could use DXGI.Format.B8G8R8A8_UNORM, but we prefer DXGI 1.0 formats break; } if (header.ImageType == ImageType.TrueColorRLE) { convFlags |= ConversionFlags.RLE; } break; case ImageType.BlackAndWhite: case ImageType.BlackAndWhiteRLE: switch (header.BitsPerPixel) { case 8: description.Format = DataFormat.R8_UNORM; break; default: throw new NotSupportedException("The black-and-white format used by the TGA file is not supported. Only 8-bit black-and-white images are supported."); } if (header.ImageType == ImageType.BlackAndWhiteRLE) { convFlags |= ConversionFlags.RLE; } break; case ImageType.NoImage: case ImageType.ColorMapped: case ImageType.ColorMappedRLE: throw new NotSupportedException("The image format used by the TGA file is not supported."); default: throw new InvalidDataException("Unknown image format used by the TGA file."); } description.Width = header.Width; description.Height = header.Height; description.Depth = 1; description.MipLevels = 1; description.ArraySize = 1; if ((header.Descriptor & DescriptorFlags.InvertX) != 0) { convFlags |= ConversionFlags.InvertX; } if ((header.Descriptor & DescriptorFlags.InvertY) != 0) { convFlags |= ConversionFlags.InvertY; } offset = sizeOfTGAHeader; if (header.IDLength != 0) { offset += header.IDLength; } return(description); }
/// <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 ImageCollection CreateImageCollection(TextureDescription description, bool skipMipLevel0 = false) { int numberOfImages, pixelSize; DetermineImages(description, out numberOfImages, out pixelSize); var images = new ImageCollection(numberOfImages); int index = 0; switch (description.Dimension) { case TextureDimension.Texture1D: case TextureDimension.Texture2D: case TextureDimension.TextureCube: Debug.Assert(description.ArraySize != 0); Debug.Assert(description.MipLevels > 0); for (int item = 0; item < description.ArraySize; item++) { int w = description.Width; int h = description.Height; for (int level = 0; level < description.MipLevels; level++) { if (!skipMipLevel0 || level != 0) images[index] = new Image(w, h, description.Format); index++; if (h > 1) h >>= 1; if (w > 1) w >>= 1; } } break; case TextureDimension.Texture3D: { Debug.Assert(description.MipLevels > 0); Debug.Assert(description.Depth > 0); int w = description.Width; int h = description.Height; int d = description.Depth; for (int level = 0; level < description.MipLevels; level++) { for (int slice = 0; slice < d; slice++) { // We use the same memory organization that Direct3D 11 needs for D3D11_SUBRESOURCE_DATA // with all slices of a given mip level being continuous in memory. if (!skipMipLevel0 || level != 0) images[index] = new Image(w, h, description.Format); index++; } if (h > 1) h >>= 1; if (w > 1) w >>= 1; if (d > 1) d >>= 1; } } break; default: Debug.Fail("Unexpected texture dimension"); break; } return images; }
private static Texture DecodeMultiframe(ImagingFactory imagingFactory, WicFlags flags, TextureDescription description, BitmapDecoder decoder) { var texture = new Texture(description); Guid dstFormat = ToWic(description.Format, false); for (int index = 0; index < description.ArraySize; ++index) { var image = texture.Images[index]; using (var frame = decoder.GetFrame(index)) { var pfGuid = frame.PixelFormat; var size = frame.Size; if (size.Width == description.Width && size.Height == description.Height) { // This frame does not need resized if (pfGuid == dstFormat) { frame.CopyPixels(image.Data, image.RowPitch); } else { using (var converter = new FormatConverter(imagingFactory)) { converter.Initialize(frame, dstFormat, GetWicDither(flags), null, 0, BitmapPaletteType.Custom); converter.CopyPixels(image.Data, image.RowPitch); } } } else { // This frame needs resizing using (var scaler = new BitmapScaler(imagingFactory)) { scaler.Initialize(frame, description.Width, description.Height, GetWicInterp(flags)); Guid pfScaler = scaler.PixelFormat; if (pfScaler == dstFormat) { scaler.CopyPixels(image.Data, image.RowPitch); } else { // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we // convert it to our desired format using (var converter = new FormatConverter(imagingFactory)) { converter.Initialize(scaler, dstFormat, GetWicDither(flags), null, 0, BitmapPaletteType.Custom); converter.CopyPixels(image.Data, image.RowPitch); } } } } } } return(texture); }
private static Texture DecodeSingleframe(ImagingFactory imagingFactory, WicFlags flags, TextureDescription description, Guid convertGuid, BitmapFrameDecode frame) { var texture = new Texture(description); var image = texture.Images[0]; if (convertGuid == Guid.Empty) { frame.CopyPixels(image.Data, image.RowPitch); } else { using (var converter = new FormatConverter(imagingFactory)) { converter.Initialize(frame, convertGuid, GetWicDither(flags), null, 0, BitmapPaletteType.Custom); converter.CopyPixels(image.Data, image.RowPitch); } } return(texture); }
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); }