/// <summary> /// Writes the managed data to the native value. /// </summary> /// <param name="thisPtr">Optional pointer to the memory that will hold the native value.</param> /// <param name="nativeValue">Output native value</param> void IMarshalable <Node, AiNode> .ToNative(IntPtr thisPtr, out AiNode nativeValue) { nativeValue = new AiNode(); // -------------------------------- nativeValue.Name = new AiString(m_name); nativeValue.Transformation = AssimpConv.ConvertTransformToAssimp(m_transform); nativeValue.Parent = IntPtr.Zero; nativeValue.NumMeshes = (uint)m_meshes.Count; nativeValue.Meshes = IntPtr.Zero; #if WITH_NODE_METADATA nativeValue.MetaData = IntPtr.Zero; #endif if (nativeValue.NumMeshes > 0) { nativeValue.Meshes = MemoryHelper.ToNativeArray <int>(m_meshes.ToArray()); } //Now descend through the children nativeValue.NumChildren = (uint)m_children.Count; int numChildren = (int)nativeValue.NumChildren; int stride = IntPtr.Size; IntPtr childrenPtr = IntPtr.Zero; if (numChildren > 0) { childrenPtr = MemoryHelper.AllocateMemory(numChildren * IntPtr.Size); for (int i = 0; i < numChildren; i++) { IntPtr currPos = MemoryHelper.AddIntPtr(childrenPtr, stride * i); Node child = m_children[i]; IntPtr childPtr = IntPtr.Zero; //Recursively create the children and its children if (child != null) { childPtr = ToNativeRecursive(thisPtr, child); } //Write the child's node ptr to our array MemoryHelper.Write <IntPtr>(currPos, ref childPtr); } } //Finally finish writing to the native struct nativeValue.Children = childrenPtr; }
/// <summary> /// Writes DDS formatted data to a stream. Image data is expected to be DXGI-compliant data, but an effort is made to write out D3D9-compatible headers when possible. /// </summary> /// <param name="output">Output stream.</param> /// <param name="mipChains">Mipmap chains to write. Each mipmap chain represents a single face (so > 1 represents an array texture or a Cubemap). All faces must have /// equivalent dimensions and each chain must have the same number of mipmaps.</param> /// <param name="format">DXGI format the image data is stored as.</param> /// <param name="texDim">Dimension of the texture to write.</param> /// <param name="flags">Flags to control how the DDS data is saved.</param> /// <returns>True if writing the data was successful, false if otherwise.</returns> public static bool Write(Stream output, List <MipChain> mipChains, DXGIFormat format, TextureDimension texDim, DDSFlags flags = DDSFlags.None) { if (output == null || !output.CanWrite || mipChains == null || mipChains.Count == 0 || mipChains[0].Count == 0 || format == DXGIFormat.Unknown) { return(false); } //Extract details int width, height, depth, arrayCount, mipCount; MipData firstMip = mipChains[0][0]; width = firstMip.Width; height = firstMip.Height; depth = firstMip.Depth; arrayCount = mipChains.Count; mipCount = mipChains[0].Count; if (!ValidateInternal(mipChains, format, texDim)) { return(false); } //Setup a transfer buffer StreamTransferBuffer buffer = new StreamTransferBuffer(firstMip.RowPitch, false); //Write out header if (!WriteHeader(output, buffer, texDim, format, width, height, depth, arrayCount, mipCount, flags)) { return(false); } //Iterate over each array face... for (int i = 0; i < arrayCount; i++) { MipChain mipChain = mipChains[i]; //Iterate over each mip face... for (int mipLevel = 0; mipLevel < mipCount; mipLevel++) { MipData mip = mipChain[mipLevel]; //Compute pitch, based on MSDN programming guide. We will write out these pitches rather than the supplied in order to conform to the recomendation //that we compute pitch based on format int realMipWidth, realMipHeight, dstRowPitch, dstSlicePitch, bytesPerPixel; ImageHelper.ComputePitch(format, mip.Width, mip.Height, out dstRowPitch, out dstSlicePitch, out realMipWidth, out realMipHeight, out bytesPerPixel); //Ensure write buffer is sufficiently sized for a single scanline if (buffer.Length < dstRowPitch) { buffer.Resize(dstRowPitch, false); } //Sanity check if (dstRowPitch < mip.RowPitch) { return(false); } IntPtr srcPtr = mip.Data; //Advance stream one slice at a time... for (int slice = 0; slice < mip.Depth; slice++) { int bytesToWrite = dstSlicePitch; IntPtr sPtr = srcPtr; //Copy scanline into temp buffer, write to output for (int row = 0; row < realMipHeight; row++) { MemoryHelper.CopyMemory(buffer.Pointer, sPtr, dstRowPitch); buffer.WriteBytes(output, dstRowPitch); bytesToWrite -= dstRowPitch; //Advance to next scanline in source data sPtr = MemoryHelper.AddIntPtr(sPtr, mip.RowPitch); } //Pad slice if necessary if (bytesToWrite > 0) { MemoryHelper.ClearMemory(buffer.Pointer, 0, bytesToWrite); buffer.WriteBytes(output, bytesToWrite); } //Advance source pointer to next slice srcPtr = MemoryHelper.AddIntPtr(srcPtr, mip.SlicePitch); } } } return(true); }
private IntPtr ToNativeRecursive(IntPtr parentPtr, Node node) { if (node == null) { return(IntPtr.Zero); } int sizeofNative = MemoryHelper.SizeOf <AiNode>(); //Allocate the memory that will hold the node IntPtr nodePtr = MemoryHelper.AllocateMemory(sizeofNative); //First fill the native struct AiNode nativeValue = new AiNode(); nativeValue.Name = new AiString(node.m_name); nativeValue.Transformation = AssimpConv.ConvertTransformToAssimp(node.m_transform); nativeValue.Parent = parentPtr; nativeValue.NumMeshes = (uint)node.m_meshes.Count; nativeValue.Meshes = MemoryHelper.ToNativeArray <int>(node.m_meshes.ToArray()); #if WITH_NODE_METADATA nativeValue.MetaData = IntPtr.Zero; #endif //Now descend through the children nativeValue.NumChildren = (uint)node.m_children.Count; int numChildren = (int)nativeValue.NumChildren; int stride = IntPtr.Size; IntPtr childrenPtr = IntPtr.Zero; if (numChildren > 0) { childrenPtr = MemoryHelper.AllocateMemory(numChildren * IntPtr.Size); for (int i = 0; i < numChildren; i++) { IntPtr currPos = MemoryHelper.AddIntPtr(childrenPtr, stride * i); Node child = node.m_children[i]; IntPtr childPtr = IntPtr.Zero; //Recursively create the children and its children if (child != null) { childPtr = ToNativeRecursive(nodePtr, child); } //Write the child's node ptr to our array MemoryHelper.Write <IntPtr>(currPos, ref childPtr); } } //Finall finish writing to the native struct, and write the whole thing to the memory we allocated previously nativeValue.Children = childrenPtr; MemoryHelper.Write <AiNode>(nodePtr, ref nativeValue); return(nodePtr); }
/// <summary> /// Reads DDS formatted data from a stream. Image data is always returned as DXGI-Compliant /// format, therefore some old legacy formats will automatically be converted. /// </summary> /// <param name="input">Input stream.</param> /// <param name="flags">Flags to control how the DDS data is loaded.</param> /// <returns>Loaded image data, or null if the data failed to load.</returns> public static DDSContainer Read(Stream input, DDSFlags flags = DDSFlags.None) { StreamTransferBuffer buffer = new StreamTransferBuffer(); Header header; Header10?headerExt; //Reads + validates header(s) if (!ReadHeader(input, buffer, out header, out headerExt)) { return(null); } //Gather up metadata List <MipChain> mipChains = null; DXGIFormat format = DXGIFormat.Unknown; TextureDimension texDim = TextureDimension.Two; ConversionFlags convFlags = ConversionFlags.None; bool legacyDword = (flags & DDSFlags.LegacyDword) == DDSFlags.LegacyDword ? true : false; int width = Math.Max((int)header.Width, 1); int height = Math.Max((int)header.Height, 1); int depth = Math.Max((int)header.Depth, 1); int mipCount = (int)header.MipMapCount; int arrayCount = 1; //Has extended header, a modern DDS if (headerExt.HasValue) { Header10 extendedHeader = headerExt.Value; arrayCount = (int)extendedHeader.ArraySize; format = extendedHeader.Format; switch (extendedHeader.ResourceDimension) { case D3D10ResourceDimension.Texture1D: { texDim = TextureDimension.One; if (height > 1 || depth > 1) { return(null); } } break; case D3D10ResourceDimension.Texture2D: { if ((extendedHeader.MiscFlags & Header10Flags.TextureCube) == Header10Flags.TextureCube) { //Specifies # of cubemaps, so to get total # of faces must multiple by 6 arrayCount *= 6; texDim = TextureDimension.Cube; } else { texDim = TextureDimension.Two; } if (depth > 1) { return(null); } } break; case D3D10ResourceDimension.Texture3D: { texDim = TextureDimension.Three; if (arrayCount > 1 || (header.Caps2 & HeaderCaps2.Volume) != HeaderCaps2.Volume) { return(null); } } break; } } else { //Otherwise, read legacy DDS and possibly convert data //Check volume flag if ((header.Caps2 & HeaderCaps2.Volume) == HeaderCaps2.Volume) { texDim = TextureDimension.Three; } else { //legacy DDS could not express 1D textures, so either a cubemap or a 2D non-array texture if ((header.Caps2 & HeaderCaps2.Cubemap) == HeaderCaps2.Cubemap) { //Must have all six faces. DirectX 8 and above always would write out all 6 faces if ((header.Caps2 & HeaderCaps2.Cubemap_AllFaces) != HeaderCaps2.Cubemap_AllFaces) { return(null); } arrayCount = 6; texDim = TextureDimension.Cube; } else { texDim = TextureDimension.Two; } } format = FormatConverter.DetermineDXGIFormat(header.PixelFormat, flags, out convFlags); } //Modify conversion flags, if necessary FormatConverter.ModifyConversionFormat(ref format, ref convFlags, flags); //If palette image, the palette will be the first thing int[] palette = null; if (FormatConverter.HasConversionFlag(convFlags, ConversionFlags.Pal8)) { palette = new int[256]; int palSize = palette.Length * sizeof(int); buffer.ReadBytes(input, palSize); if (buffer.LastReadByteCount != palSize) { return(null); } MemoryHelper.CopyBytes <int>(buffer.ByteArray, 0, palette, 0, palette.Length); } //Now read data based on available mip/arrays mipChains = new List <MipChain>(arrayCount); byte[] scanline = buffer.ByteArray; IntPtr scanlinePtr = buffer.Pointer; bool noPadding = (flags & DDSFlags.NoPadding) == DDSFlags.NoPadding ? true : false; bool isCompressed = FormatConverter.IsCompressed(format); bool errored = false; try { //Iterate over each array face... for (int i = 0; i < arrayCount; i++) { MipChain mipChain = new MipChain(mipCount); mipChains.Add(mipChain); //Iterate over each mip face... for (int mipLevel = 0; mipLevel < mipCount; mipLevel++) { //Calculate mip dimensions int mipWidth = width; int mipHeight = height; int mipDepth = depth; ImageHelper.CalculateMipmapLevelDimensions(mipLevel, ref mipWidth, ref mipHeight, ref mipDepth); //Compute pitch, based on MSDN programming guide which says PitchOrLinearSize is unreliable and to calculate based on format. //"real" mip width/height is the given mip width/height for all non-compressed, compressed images it will be smaller since each block //is a 4x4 region of pixels. int realMipWidth, realMipHeight, dstRowPitch, dstSlicePitch, bytesPerPixel; ImageHelper.ComputePitch(format, mipWidth, mipHeight, out dstRowPitch, out dstSlicePitch, out realMipWidth, out realMipHeight, out bytesPerPixel, legacyDword); int srcRowPitch = dstRowPitch; int srcSlicePitch = dstSlicePitch; //Are we converting from a legacy format, possibly? if (!headerExt.HasValue) { int legacySize = FormatConverter.LegacyFormatBitsPerPixelFromConversionFlag(convFlags); if (legacySize != 0) { srcRowPitch = (realMipWidth * legacySize + 7) / 8; srcSlicePitch = srcRowPitch * realMipHeight; } } //If output data is requested not to have padding, recompute destination pitches if (noPadding) { dstRowPitch = bytesPerPixel * realMipWidth; dstSlicePitch = dstRowPitch * realMipHeight; } //Setup memory to hold the loaded image MipData mipSurface = new MipData(mipWidth, mipHeight, mipDepth, dstRowPitch, dstSlicePitch); mipChain.Add(mipSurface); //Ensure read buffer is sufficiently sized for a single scanline if (buffer.Length < srcRowPitch) { buffer.Resize(srcRowPitch, false); } IntPtr dstPtr = mipSurface.Data; //Advance stream one slice at a time... for (int slice = 0; slice < mipDepth; slice++) { long slicePos = input.Position; IntPtr dPtr = dstPtr; //Copy scanline into temp buffer, do any conversions, copy to output for (int row = 0; row < realMipHeight; row++) { int numBytesRead = input.Read(scanline, 0, srcRowPitch); if (numBytesRead != srcRowPitch) { errored = true; System.Diagnostics.Debug.Assert(false); return(null); } //Copy scanline, optionally convert data FormatConverter.CopyScanline(dPtr, dstRowPitch, scanlinePtr, srcRowPitch, format, convFlags, palette); //Increment dest pointer to next row dPtr = MemoryHelper.AddIntPtr(dPtr, dstRowPitch); } //Advance stream and destination pointer to the next slice input.Position = slicePos + srcSlicePitch; dstPtr = MemoryHelper.AddIntPtr(dstPtr, dstSlicePitch); } } } } finally { //If errored, clean up any mip surfaces we allocated...no null entries should have been made either if (errored) { DisposeMipChains(mipChains); } } if (!ValidateInternal(mipChains, format, texDim)) { System.Diagnostics.Debug.Assert(false); return(null); } return(new DDSContainer(mipChains, format, texDim)); }