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; }
//------------------------------------------------------------------------------------- // Decodes an image array, resizing/format converting as needed //------------------------------------------------------------------------------------- private static Image DecodeMultiframe(WICFlags flags, ImageDescription metadata, BitmapDecoder decoder) { var image = Image.New(metadata); Guid sourceGuid; if (!ToWIC(metadata.Format, out sourceGuid)) { return(null); } for (int index = 0; index < metadata.ArraySize; ++index) { var pixelBuffer = image.PixelBuffer[index, 0]; using (var frame = decoder.GetFrame(index)) { var pfGuid = frame.PixelFormat; var size = frame.Size; if (pfGuid == sourceGuid) { if (size.Width == metadata.Width && size.Height == metadata.Height) { // This frame does not need resized or format converted, just copy... frame.CopyPixels(pixelBuffer.RowStride, pixelBuffer.DataPointer, pixelBuffer.BufferStride); } else { // This frame needs resizing, but not format converted using (var scaler = new BitmapScaler(Factory)) { scaler.Initialize(frame, metadata.Width, metadata.Height, GetWICInterp(flags)); scaler.CopyPixels(pixelBuffer.RowStride, pixelBuffer.DataPointer, pixelBuffer.BufferStride); } } } else { // This frame required format conversion using (var converter = new FormatConverter(Factory)) { converter.Initialize(frame, pfGuid, GetWICDither(flags), null, 0, BitmapPaletteType.Custom); if (size.Width == metadata.Width && size.Height == metadata.Height) { converter.CopyPixels(pixelBuffer.RowStride, pixelBuffer.DataPointer, pixelBuffer.BufferStride); } else { // This frame needs resizing, but not format converted using (var scaler = new BitmapScaler(Factory)) { scaler.Initialize(frame, metadata.Width, metadata.Height, GetWICInterp(flags)); scaler.CopyPixels(pixelBuffer.RowStride, pixelBuffer.DataPointer, pixelBuffer.BufferStride); } } } } } } return(image); }
/// <summary> /// Function to read multiple frames from a decoder that supports multiple frames. /// </summary> /// <param name="wic">WIC interface.</param> /// <param name="data">Image data to populate.</param> /// <param name="decoder">Decoder for the image.</param> private void ReadFrames(GorgonWICImage wic, GorgonImageData data, BitmapDecoder decoder) { Guid bestPixelFormat = wic.GetGUID(data.Settings.Format); // Find the best fit pixel format. if (bestPixelFormat == Guid.Empty) { throw new IOException(string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, data.Settings.Format)); } // Use the image array as the frames. int arrayCount = _actualArrayCount.Min(data.Settings.ArrayCount); for (int array = 0; array < arrayCount; array++) { var buffer = data.Buffers[0, array]; // Get the frame data. using (var frame = decoder.GetFrame(array)) { IntPtr bufferPointer = buffer.Data.BasePointer; Guid frameFormat = frame.PixelFormat; int frameWidth = frame.Size.Width; int frameHeight = frame.Size.Height; var frameOffset = GetFrameOffset(frame); // Calculate the pointer offset if we have an offset from the frame. Only offset if we're clipping the image though. if (((frameOffset.Y != 0) || (frameOffset.X != 0)) && (Clip)) { bufferPointer = buffer.Data.BasePointer + (frameOffset.Y * buffer.PitchInformation.RowPitch) + (frameOffset.X * (buffer.PitchInformation.RowPitch / buffer.Width)); } // Confirm that we actually need to perform clipping. bool needsSizeAdjust = (frameWidth + frameOffset.X > data.Settings.Width) || (frameHeight + frameOffset.Y > data.Settings.Height); // If the formats match, then we don't need to do conversion. if (bestPixelFormat == frameFormat) { // If the width and height are the same then we can just do a straight copy into the buffer. if (((frameWidth == data.Settings.Width) && (frameHeight == data.Settings.Height)) || ((!needsSizeAdjust) && (Clip))) { frame.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); continue; } // We need to scale the image up/down to the size of our image data. if (!Clip) { using (var scaler = new BitmapScaler(wic.Factory)) { scaler.Initialize(frame, data.Settings.Width, data.Settings.Height, (BitmapInterpolationMode)Filter); scaler.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); } continue; } using (var clipper = new BitmapClipper(wic.Factory)) { clipper.Initialize(frame, new Rectangle(0, 0, data.Settings.Width, data.Settings.Height)); clipper.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); } continue; } // Poop. We need to convert this image. using (var converter = new FormatConverter(wic.Factory)) { converter.Initialize(frame, bestPixelFormat, (BitmapDitherType)Dithering, null, 0.0, BitmapPaletteType.Custom); if (((frameWidth == data.Settings.Width) && (frameHeight == data.Settings.Height)) || ((!needsSizeAdjust) && (Clip))) { converter.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); continue; } // And we need to scale the image. if (!Clip) { using (var scaler = new BitmapScaler(wic.Factory)) { scaler.Initialize(converter, data.Settings.Width, data.Settings.Height, (BitmapInterpolationMode)Filter); scaler.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); } continue; } using (var clipper = new BitmapClipper(wic.Factory)) { clipper.Initialize(frame, new Rectangle(0, 0, data.Settings.Width, data.Settings.Height)); clipper.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); } } } } }
/// <summary> /// 從WIC Frame建立貼圖資源(非DDS) /// </summary> /// <param name="d3dContext">If a Direct3D 11 device context is provided and the current device supports it for the given pixel format, it will auto-generate mipmaps.</param> private static Result CreateWICTexture(Device device, DeviceContext d3dContext, BitmapFrameDecode frame, int maxsize, ResourceUsage usage, BindFlags bind, CpuAccessFlags cpuAccess, ResourceOptionFlags option, LoadFlags load, out Resource texture, out ShaderResourceView textureView) { texture = null; textureView = null; if (frame.Size.Width <= 0 || frame.Size.Height <= 0) { return(Result.InvalidArg); } if (maxsize == 0) { switch (device.FeatureLevel) { case FeatureLevel.Level_9_1: case FeatureLevel.Level_9_2: maxsize = 2048 /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; break; case FeatureLevel.Level_9_3: maxsize = 4096 /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; break; case FeatureLevel.Level_10_0: case FeatureLevel.Level_10_1: maxsize = 8192 /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; break; default: maxsize = Resource.MaximumTexture2DSize; /*D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION*/ break; } } Size2 frameSize = frame.Size; Size2 targetSize; if (frameSize.Width > maxsize || frameSize.Height > maxsize) { double ratio = Convert.ToDouble(frameSize.Height) / Convert.ToDouble(frameSize.Width); if (frameSize.Width > frameSize.Height) { targetSize.Width = maxsize; targetSize.Height = Math.Max(1, Convert.ToInt32(maxsize * ratio)); } else { targetSize.Height = maxsize; targetSize.Width = Math.Max(1, Convert.ToInt32(maxsize / ratio)); } } else { targetSize = frameSize; } #region Determine format Guid sourceFormat = frame.PixelFormat; Guid targetFormat = sourceFormat; DXGI.Format format = sourceFormat.ConvertWICToDXGIFormat(); int bpp = 0; if (format == DXGI.Format.Unknown) { if (sourceFormat == PixelFormat.Format96bppRGBFixedPoint) { if (WIC2) { targetFormat = PixelFormat.Format96bppRGBFloat; format = DXGI.Format.R32G32B32_Float; bpp = 96; } else { targetFormat = PixelFormat.Format128bppRGBAFloat; format = DXGI.Format.R32G32B32A32_Float; bpp = 128; } } else { targetFormat = sourceFormat.ConvertToNearest(); format = targetFormat.ConvertWICToDXGIFormat(); bpp = PixelFormat.GetBitsPerPixel(targetFormat); } if (format == DXGI.Format.Unknown) { return(Result.GetResultFromWin32Error(unchecked ((int)0x80070032))); } } else { bpp = PixelFormat.GetBitsPerPixel(sourceFormat); } if (format == DXGI.Format.R32G32B32_Float && d3dContext != null) { // Special case test for optional device support for autogen mipchains for R32G32B32_FLOAT var formatSupport = device.CheckFormatSupport(format); if (!formatSupport.HasFlag(FormatSupport.MipAutogen)) { targetFormat = PixelFormat.Format128bppRGBAFloat; format = DXGI.Format.R32G32B32A32_Float; bpp = 128; } } if (bpp == 0) { return(Result.Fail); } if (load.HasFlag(LoadFlags.ForceSrgb)) { format = format.MakeSRgb(); } else if (!load.HasFlag(LoadFlags.ignoreSrgb)) { bool sRGB = false; try { var metareader = frame.MetadataQueryReader; var containerFormat = metareader.ContainerFormat; if (containerFormat == ContainerFormatGuids.Png) { // Check for sRGB chunk if (metareader.TryGetMetadataByName("/sRGB/RenderingIntent", out var value) == Result.Ok) { sRGB = true; } } else if (metareader.TryGetMetadataByName("System.Image.ColorSpace", out var value) == Result.Ok) { sRGB = true; } if (sRGB) { format = format.MakeSRgb(); } } catch (SharpDXException) { // BMP, ICO are not supported. } } // Verify our target format is supported by the current device var support = device.CheckFormatSupport(format); if (!support.HasFlag(FormatSupport.Texture2D)) { targetFormat = PixelFormat.Format32bppRGBA; format = DXGI.Format.R8G8B8A8_UNorm; bpp = 32; } #endregion int stride = (targetSize.Width * bpp + 7) / 8; // round int imageSize = stride * targetSize.Height; IntPtr temp = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(imageSize); if (sourceFormat == targetFormat && frameSize == targetSize) // 不需要格式轉換 且 不需要改變大小 { frame.CopyPixels(stride, new DataPointer(temp, imageSize)); } else if (frameSize == targetSize) // 需要格式轉換 { using (var factory = new ImagingFactory2()) using (var coverter = new FormatConverter(factory)) { if (coverter.CanConvert(sourceFormat, targetFormat)) { coverter.Initialize(frame, targetFormat, BitmapDitherType.ErrorDiffusion, null, 0, BitmapPaletteType.MedianCut); coverter.CopyPixels(stride, new DataPointer(temp, imageSize)); } else { return(Result.UnexpectedFailure); } } } else if (sourceFormat == targetFormat) // 需要改變大小 { using (var factory = new ImagingFactory2()) using (var scaler = new BitmapScaler(factory)) { scaler.Initialize(frame, targetSize.Width, targetSize.Height, BitmapInterpolationMode.Fant); var pfScaler = scaler.PixelFormat; if (targetFormat == pfScaler) { scaler.CopyPixels(stride, new DataPointer(temp, imageSize)); } } } else // 需要格式轉換 且 需要改變大小 { using (var factory = new ImagingFactory2()) using (var scaler = new BitmapScaler(factory)) using (var coverter = new FormatConverter(factory)) { scaler.Initialize(frame, targetSize.Width, targetSize.Height, BitmapInterpolationMode.Fant); var pfScaler = scaler.PixelFormat; if (coverter.CanConvert(pfScaler, targetFormat)) { coverter.Initialize(scaler, targetFormat, BitmapDitherType.ErrorDiffusion, null, 0, BitmapPaletteType.MedianCut); coverter.CopyPixels(stride, new DataPointer(temp, imageSize)); } else { return(Result.UnexpectedFailure); } } } var autogen = false; if (d3dContext != null) { var formatSupport = device.CheckFormatSupport(format); if (formatSupport.HasFlag(FormatSupport.MipAutogen)) { autogen = true; } } var texture2DDescription = new Texture2DDescription() { Width = targetSize.Width, Height = targetSize.Height, MipLevels = autogen ? 0 : 1, ArraySize = 1, Format = format, SampleDescription = new DXGI.SampleDescription(1, 0), Usage = usage, CpuAccessFlags = cpuAccess, }; if (autogen) { texture2DDescription.BindFlags = bind | BindFlags.RenderTarget; texture2DDescription.OptionFlags = option | ResourceOptionFlags.GenerateMipMaps; } else { texture2DDescription.BindFlags = bind; texture2DDescription.OptionFlags = option; } Result result = Result.Ok; // 建立Texture2D !!! try { if (autogen) { texture = new Texture2D(device, texture2DDescription); } else { texture = new Texture2D(device, texture2DDescription, new DataBox[] { new DataBox(temp, stride, imageSize) }); } } catch (SharpDXException e) { System.Diagnostics.Debug.WriteLine(e.ToString()); result = Result.Fail; } if (result.Success) { var SRVDesc = new ShaderResourceViewDescription() { Format = format, Dimension = ShaderResourceViewDimension.Texture2D, Texture2D = new ShaderResourceViewDescription.Texture2DResource() { MipLevels = autogen ? -1 : 1 }, }; try { textureView = new ShaderResourceView(device, texture, SRVDesc); if (autogen) { DataBox data = new DataBox(temp, stride, imageSize); d3dContext.UpdateSubresource(data, texture); d3dContext.GenerateMips(textureView); } } catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.ToString()); Utilities.Dispose(ref texture); result = Result.Fail; } } // 釋放 Unmanaged 資源 System.Runtime.InteropServices.Marshal.FreeCoTaskMem(temp); return(result); }