public static unsafe void Save( DDSSaveInfo info, TextureCollection textures, Stream output, DdsProgressCallback progressCallback) { StreamIOCallbacks streamIO = new StreamIOCallbacks(output); IOCallbacks callbacks = new IOCallbacks { Read = streamIO.Read, Write = streamIO.Write, Seek = streamIO.Seek, GetSize = streamIO.GetSize }; DDSBitmapData[] bitmapData = CreateBitmapDataArray(textures, info.arraySize, info.mipLevels); int hr; unsafe { fixed(DDSBitmapData *pBitmapData = bitmapData) { if (IntPtr.Size == 8) { hr = DdsIO_x64.Save(info, pBitmapData, (uint)bitmapData.Length, callbacks, progressCallback); } else { hr = DdsIO_x86.Save(info, pBitmapData, (uint)bitmapData.Length, callbacks, progressCallback); } } } GC.KeepAlive(streamIO); GC.KeepAlive(callbacks); GC.KeepAlive(progressCallback); if (FAILED(hr)) { if (streamIO.CallbackExceptionInfo != null) { streamIO.CallbackExceptionInfo.Throw(); } else { switch (hr) { case HResult.CanceledError: throw new OperationCanceledException(); default: Marshal.ThrowExceptionForHR(hr); break; } } } }
private static DDSBitmapData[] CreateBitmapDataArray(TextureCollection textures, int arraySize, int mipLevels) { DDSBitmapData[] array = new DDSBitmapData[textures.Count]; for (int i = 0; i < arraySize; ++i) { int startIndex = i * mipLevels; for (int j = 0; j < mipLevels; ++j) { int index = startIndex + j; array[index] = new DDSBitmapData(textures[index].Surface); } } return(array); }
internal void Save(TextureCollection textures, Stream output, ProgressEventHandler progressCallback) { DdsHeader header = new DdsHeader(this.width, this.height, this.arraySize, this.mipLevels, this.format); output.WriteUInt32(DdsMagic); header.Write(output); double progressTotal = this.arraySize * this.mipLevels; for (int i = 0; i < this.arraySize; ++i) { int startIndex = i * this.mipLevels; for (int j = 0; j < this.mipLevels; ++j) { int index = startIndex + j; WritePixelData(textures[index].Surface, output); progressCallback?.Invoke(this, new ProgressEventArgs((index / progressTotal) * 100.0, true)); } } }
public static void Save( IServiceProvider services, Document input, Stream output, DdsFileFormat format, DdsErrorMetric errorMetric, BC7CompressionMode compressionMode, bool cubeMap, bool generateMipmaps, ResamplingAlgorithm sampling, Surface scratchSurface, ProgressEventHandler progressCallback) { using (RenderArgs args = new RenderArgs(scratchSurface)) { input.Render(args, true); } DdsNative.DdsProgressCallback ddsProgress = null; if (progressCallback != null) { ddsProgress = (UIntPtr done, UIntPtr total) => { double progress = (double)done.ToUInt64() / (double)total.ToUInt64(); try { progressCallback(null, new ProgressEventArgs(progress * 100.0, true)); return(true); } catch (OperationCanceledException) { return(false); } }; } int width = scratchSurface.Width; int height = scratchSurface.Height; int arraySize = 1; Size?cubeMapFaceSize = null; if (cubeMap && IsCrossedCubeMapSize(scratchSurface)) { if (width > height) { width /= 4; height /= 3; } else { width /= 3; height /= 4; } arraySize = 6; cubeMapFaceSize = new Size(width, height); } int mipLevels = generateMipmaps ? GetMipCount(width, height) : 1; bool enableHardwareAcceleration = (bool)services.GetService <ISettingsService>().GetSetting(AppSettingPaths.UI.EnableHardwareAcceleration).Value; DdsNative.DDSSaveInfo info = new DdsNative.DDSSaveInfo { width = width, height = height, arraySize = arraySize, mipLevels = mipLevels, format = format, errorMetric = errorMetric, compressionMode = compressionMode, cubeMap = cubeMapFaceSize.HasValue, enableHardwareAcceleration = enableHardwareAcceleration }; using (TextureCollection textures = GetTextures(scratchSurface, cubeMapFaceSize, mipLevels, sampling)) { DdsNative.Save(info, textures, output, ddsProgress); } }
private static TextureCollection GetTextures(Surface scratchSurface, Size?cubeMapFaceSize, int mipLevels, ResamplingAlgorithm algorithm) { TextureCollection textures = null; TextureCollection tempTextures = null; try { tempTextures = new TextureCollection(mipLevels); if (cubeMapFaceSize.HasValue) { // DirectX 10+ requires DDS cube maps to have all 6 faces. tempTextures.Capacity *= 6; Size faceSize = cubeMapFaceSize.Value; Point[] cubeMapOffsets = new Point[6]; // Split the crossed image into the individual cube map faces. // // The crossed image uses the same layout as the Intel® Texture Works DDS plug-in for Adobe Photoshop® // (https://github.com/GameTechDev/Intel-Texture-Works-Plugin) // // The DirectXTex texassemble utility and Unity® both use different layouts, so there does not appear // to be any common standard for a crossed image. // // The cube map faces in a DDS file are always ordered: +X, -X, +Y, -Y, +Z, -Z. if (scratchSurface.Width > scratchSurface.Height) { // A horizontal crossed image uses the following layout: // // [ +Y ] // [ -X ][ +Z ][ +X ][ -Z ] // [ -Y ] // cubeMapOffsets[0] = new Point(faceSize.Width * 2, faceSize.Height); // +X cubeMapOffsets[1] = new Point(0, faceSize.Height); // -X cubeMapOffsets[2] = new Point(faceSize.Width, 0); // +Y cubeMapOffsets[3] = new Point(faceSize.Width, faceSize.Height * 2); // -Y cubeMapOffsets[4] = new Point(faceSize.Width, faceSize.Height); // +Z cubeMapOffsets[5] = new Point(faceSize.Width * 3, faceSize.Height); // -Z } else { // A vertical crossed image uses the following layout: // // [ +Y ] // [ -X ][ +Z ][ +X ] // [ -Y ] // [ -Z ] // cubeMapOffsets[0] = new Point(faceSize.Width * 2, faceSize.Height); // +X cubeMapOffsets[1] = new Point(0, faceSize.Height); // -X cubeMapOffsets[2] = new Point(faceSize.Width, 0); // +Y cubeMapOffsets[3] = new Point(faceSize.Width, faceSize.Height * 2); // -Y cubeMapOffsets[4] = new Point(faceSize.Width, faceSize.Height); // +Z cubeMapOffsets[5] = new Point(faceSize.Width, faceSize.Height * 3); // -Z } for (int i = 0; i < 6; ++i) { Point srcStartOffset = cubeMapOffsets[i]; tempTextures.Add(new Texture(scratchSurface.CreateWindow(srcStartOffset.X, srcStartOffset.Y, faceSize.Width, faceSize.Height), true)); if (mipLevels > 1) { Surface cubeMapSurface = tempTextures[tempTextures.Count - 1].Surface; for (int j = 1; j < mipLevels; ++j) { int mipWidth = Math.Max(1, cubeMapSurface.Width >> j); int mipHeight = Math.Max(1, cubeMapSurface.Height >> j); tempTextures.Add(CreateMipTexture(cubeMapSurface, mipWidth, mipHeight, algorithm)); } } } } else { tempTextures.Add(new Texture(scratchSurface, false)); if (mipLevels > 1) { for (int j = 1; j < mipLevels; ++j) { int mipWidth = Math.Max(1, scratchSurface.Width >> j); int mipHeight = Math.Max(1, scratchSurface.Height >> j); tempTextures.Add(CreateMipTexture(scratchSurface, mipWidth, mipHeight, algorithm)); } } } textures = tempTextures; tempTextures = null; } finally { if (tempTextures != null) { tempTextures.Dispose(); } } return(textures); }
public static unsafe void Save( DDSSaveInfo info, TextureCollection textures, Stream output, DdsProgressCallback progressCallback) { StreamIOCallbacks streamIO = new StreamIOCallbacks(output); IOCallbacks callbacks = new IOCallbacks { Read = streamIO.Read, Write = streamIO.Write, Seek = streamIO.Seek, GetSize = streamIO.GetSize }; DDSBitmapData[] bitmapData = CreateBitmapDataArray(textures, info.arraySize, info.mipLevels); int hr; unsafe { fixed(DDSBitmapData *pBitmapData = bitmapData) { #if NET47 if (IntPtr.Size == 8) #else if (RuntimeInformation.ProcessArchitecture == Architecture.X64) #endif { hr = DdsIO_x64.Save(info, pBitmapData, (uint)bitmapData.Length, callbacks, progressCallback); } #if NET47 else if (IntPtr.Size == 4) #else else if (RuntimeInformation.ProcessArchitecture == Architecture.X86) #endif { hr = DdsIO_x86.Save(info, pBitmapData, (uint)bitmapData.Length, callbacks, progressCallback); } else { throw new PlatformNotSupportedException(); } } } GC.KeepAlive(streamIO); GC.KeepAlive(callbacks); GC.KeepAlive(progressCallback); if (FAILED(hr)) { if (streamIO.CallbackExceptionInfo != null) { streamIO.CallbackExceptionInfo.Throw(); } else { switch (hr) { case HResult.CanceledError: throw new OperationCanceledException(); case HResult.UnknownDdsSaveFormat: throw new InvalidOperationException("The DDSFileFormat value does not map to a DXGI format."); default: Marshal.ThrowExceptionForHR(hr); break; } } } }