/// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// </summary> public void Dispose() { _preParsedData?.Dispose(); _preParsedData = null; GC.SuppressFinalize(this); }
/// <summary> /// Function to return the object. /// </summary> /// <returns>The object created or updated by this builder.</returns> /// <exception cref="GorgonException">Thrown if the polygonal sprite has less than 3 vertices.</exception> /// <remarks> /// <para> /// This will return a <see cref="GorgonPolySprite"/> for use with the <see cref="Gorgon2D.DrawPolygonSprite"/> method. The object returned implements <see cref="IDisposable"/>, so it is the /// responsibility of the user to dispose of the object when they are done with it. /// </para> /// <para> /// <note type="warning"> /// <para> /// A polygon sprite must have a minimum of 3 vertices. If it does not, then this method will throw an exception. /// </para> /// </note> /// </para> /// </remarks> /// <seealso cref="GorgonPolySprite"/> /// <seealso cref="Gorgon2D"/> public GorgonPolySprite Build() { if (_workingSprite.RwVertices.Count < 3) { throw new GorgonException(GorgonResult.CannotCreate, Resources.GOR2D_ERR_POLY_SPRITE_NOT_ENOUGH_VERTS); } var newSprite = new GorgonPolySprite(); CopySprite(newSprite, _workingSprite); newSprite.Renderable.ActualVertexCount = newSprite.RwVertices.Count; if ((newSprite.Renderable.Vertices == null) || (newSprite.Renderable.Vertices.Length < newSprite.RwVertices.Count)) { newSprite.Renderable.Vertices = new Gorgon2DVertex[newSprite.RwVertices.Count]; } for (int i = 0; i < newSprite.RwVertices.Count; ++i) { newSprite.Renderable.Vertices[i] = newSprite.RwVertices[i].Vertex; } // Enforce clockwise ordering. _triangulator.EnsureWindingOrder(newSprite.Renderable.Vertices, WindingOrder.CounterClockwise); // Split the polygon hull into triangles. (GorgonNativeBuffer <int> indices, DX.RectangleF bounds) = _triangulator.Triangulate(newSprite.Renderable.Vertices, WindingOrder.CounterClockwise); GorgonNativeBuffer <Gorgon2DVertex> vertexData = newSprite.Renderable.Vertices.ToNativeBuffer(); try { newSprite.Renderable.IndexBuffer = new GorgonIndexBuffer(Graphics, new GorgonIndexBufferInfo { Binding = VertexIndexBufferBinding.None, Use16BitIndices = false, IndexCount = indices.Length, Usage = ResourceUsage.Immutable }, indices); newSprite.Renderable.VertexBuffer = GorgonVertexBufferBinding.CreateVertexBuffer(Graphics, new GorgonVertexBufferInfo { Usage = ResourceUsage.Immutable, Binding = VertexIndexBufferBinding.None, SizeInBytes = Gorgon2DVertex.SizeInBytes * newSprite.RwVertices.Count }, vertexData); newSprite.Renderable.ActualVertexCount = newSprite.RwVertices.Count; newSprite.Renderable.IndexCount = indices.Length; newSprite.Bounds = new DX.RectangleF(newSprite.Position.X, newSprite.Position.Y, bounds.Width, bounds.Height); } finally { vertexData?.Dispose(); indices?.Dispose(); } return(newSprite); }
/// <summary> /// Function to convert the image data into a premultiplied format. /// </summary> /// <param name="baseImage">The image to convert.</param> /// <returns>A <see cref="IGorgonImage"/> containing the image data with the premultiplied alpha pixel data.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> is <b>null</b>.</exception> /// <exception cref="ArgumentException">Thrown when image format is compressed.</exception> /// <remarks> /// <para> /// Use this to convert an image to a premultiplied format. This takes each Red, Green and Blue element and multiplies them by the Alpha element. /// </para> /// <para> /// If the image does not contain alpha then the method will return right away and no alterations to the image will be performed. /// </para> /// </remarks> public static IGorgonImage ConvertToPremultipliedAlpha(this IGorgonImage baseImage) { IGorgonImage newImage = null; GorgonNativeBuffer <byte> imageData = null; if (baseImage == null) { throw new ArgumentNullException(nameof(baseImage)); } if (!baseImage.FormatInfo.HasAlpha) { return(baseImage); } if (baseImage.FormatInfo.IsCompressed) { throw new ArgumentException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, baseImage.Format), nameof(baseImage)); } try { var cloneImageInfo = new GorgonImageInfo(baseImage) { HasPreMultipliedAlpha = true }; imageData = new GorgonNativeBuffer <byte>(baseImage.ImageData.Length); baseImage.ImageData.CopyTo(imageData); unsafe { newImage = new GorgonImage(cloneImageInfo, new GorgonReadOnlyPointer((void *)imageData, imageData.SizeInBytes)); int arrayOrDepth = newImage.ImageType == ImageType.Image3D ? newImage.Depth : newImage.ArrayCount; for (int mip = 0; mip < newImage.MipCount; ++mip) { for (int i = 0; i < arrayOrDepth; ++i) { IGorgonImageBuffer buffer = newImage.Buffers[mip, i]; byte *ptr = (byte *)buffer.Data; int rowPitch = buffer.PitchInformation.RowPitch; for (int y = 0; y < buffer.Height; ++y) { ImageUtilities.SetPremultipliedScanline(ptr, rowPitch, ptr, rowPitch, buffer.Format); ptr += rowPitch; } } } } newImage.CopyTo(baseImage); return(baseImage); } finally { imageData?.Dispose(); newImage?.Dispose(); } }
/// <summary> /// Function to persist a <see cref="IGorgonImage"/> to a stream. /// </summary> /// <param name="imageData">A <see cref="IGorgonImage"/> to persist to the stream.</param> /// <param name="stream">The stream that will receive the image data.</param> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="stream"/>, or the <paramref name="imageData"/> parameter is <b>null</b>.</exception> /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="stream"/> is read only.</exception> /// <exception cref="NotSupportedException">Thrown when the image data in the stream has a pixel format that is unsupported by the codec.</exception> /// <remarks> /// <para> /// When persisting image data via a codec, the image must have a format that the codec can recognize. This list of supported formats is provided by the <see cref="SupportedPixelFormats"/> /// property. Applications may convert their image data a supported format before saving the data using a codec. /// </para> /// </remarks> public override void SaveToStream(IGorgonImage imageData, Stream stream) { // Ensure that we can actually read this format. We do not perform total pixel conversion on behalf of the user, they are responsible for that. // We will, however, support swizzling and pixel compression (e.g. 32 -> 24 bit). if (Array.IndexOf(_supportedFormats, imageData.Format) == -1) { throw new NotSupportedException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, imageData.Format)); } using (var writer = new GorgonBinaryWriter(stream, true)) { // Write the header for the file before we dump the file contents. TgaHeader header = GetHeader(imageData, out TGAConversionFlags conversionFlags); GorgonPitchLayout destPitch; if ((conversionFlags & TGAConversionFlags.RGB888) == TGAConversionFlags.RGB888) { destPitch = new GorgonPitchLayout(imageData.Width * 3, imageData.Width * 3 * imageData.Height); } else { var formatInfo = new GorgonFormatInfo(imageData.Format); destPitch = formatInfo.GetPitchForFormat(imageData.Width, imageData.Height); } GorgonPitchLayout srcPitch = imageData.Buffers[0].PitchInformation; // If the two pitches are equal and we have no conversion requirements, then just write out the buffer. if ((destPitch == srcPitch) && (conversionFlags == TGAConversionFlags.None)) { writer.WriteValue(ref header); writer.WriteRange(imageData.Buffers[0].Data, count: srcPitch.SlicePitch); return; } unsafe { // Get the pointer to the first mip/array/depth level. byte *srcPointer = (byte *)imageData.Buffers[0].Data; var lineBuffer = new GorgonNativeBuffer <byte>(srcPitch.RowPitch); try { // Persist the working buffer to the stream. writer.WriteValue(ref header); // Write out each scan line. for (int y = 0; y < imageData.Height; y++) { byte *destPtr = (byte *)lineBuffer; if ((conversionFlags & TGAConversionFlags.RGB888) == TGAConversionFlags.RGB888) { ImageUtilities.Compress24BPPScanLine(srcPointer, srcPitch.RowPitch, destPtr, destPitch.RowPitch, (conversionFlags & TGAConversionFlags.Swizzle) == TGAConversionFlags.Swizzle); } else if ((conversionFlags & TGAConversionFlags.Swizzle) == TGAConversionFlags.Swizzle) { ImageUtilities.SwizzleScanline(srcPointer, srcPitch.RowPitch, destPtr, destPitch.RowPitch, imageData.Format, ImageBitFlags.None); } else { ImageUtilities.CopyScanline(srcPointer, srcPitch.RowPitch, destPtr, destPitch.RowPitch, imageData.Format, ImageBitFlags.None); } srcPointer += srcPitch.RowPitch; writer.WriteRange(lineBuffer, count: destPitch.RowPitch); } } finally { lineBuffer?.Dispose(); } } } }