/// <summary> /// Function called before rendering begins. /// </summary> /// <returns> /// TRUE to continue rendering, FALSE to exit. /// </returns> protected override bool OnBeforeRender() { GorgonRenderTargetView target = Gorgon2D.Target; if (_randomTexture == null) { GenerateRandomNoise(); } if (target == null) { target = Gorgon2D.DefaultTarget; } switch (target.Resource.ResourceType) { case ResourceType.Buffer: var buffer = (GorgonBuffer)target.Resource; var info = GorgonBufferFormatInfo.GetInfo(buffer.Settings.DefaultShaderViewFormat); _currentTargetSize = new Vector2(buffer.SizeInBytes / info.BitDepth, 1); break; case ResourceType.Texture1D: case ResourceType.Texture2D: case ResourceType.Texture3D: var texture = (GorgonTexture)target.Resource; _currentTargetSize = new Vector2(texture.Settings.Width, texture.Settings.Height); break; } RememberTextureSampler(ShaderType.Pixel, 1); RememberShaderResource(ShaderType.Pixel, 1); RememberConstantBuffer(ShaderType.Pixel, 1); RememberConstantBuffer(ShaderType.Pixel, 2); RememberConstantBuffer(ShaderType.Pixel, 3); Gorgon2D.PixelShader.Resources[1] = _randomTexture; Gorgon2D.PixelShader.TextureSamplers[1] = new GorgonTextureSamplerStates { TextureFilter = TextureFilter.Linear, HorizontalAddressing = TextureAddressing.Wrap, VerticalAddressing = TextureAddressing.Wrap, DepthAddressing = TextureAddressing.Clamp, MipLODBias = 0.0f, MaxAnisotropy = 1, ComparisonFunction = ComparisonOperator.Never, BorderColor = Color.White, MinLOD = -3.402823466e+38f, MaxLOD = 3.402823466e+38f }; Gorgon2D.PixelShader.ConstantBuffers[1] = _timingBuffer; Gorgon2D.PixelShader.ConstantBuffers[2] = _scratchBuffer; Gorgon2D.PixelShader.ConstantBuffers[3] = _sepiaBuffer; return(base.OnBeforeRender()); }
/// <summary> /// Initializes a new instance of the <see cref="Gorgon2DTarget"/> struct. /// </summary> /// <param name="target">The target.</param> /// <param name="depthStencil">The depth/stencil view.</param> public Gorgon2DTarget(GorgonRenderTargetView target, GorgonDepthStencilView depthStencil) { Target = target; DepthStencil = null; SwapChain = null; Height = 1; switch (target.Resource.ResourceType) { case ResourceType.Buffer: var buffer = (GorgonBuffer)target.Resource; var info = GorgonBufferFormatInfo.GetInfo(buffer.Settings.DefaultShaderViewFormat); Width = buffer.SizeInBytes / info.SizeInBytes; Viewport = new GorgonViewport(0, 0, Width, Height, 0, 1.0f); break; case ResourceType.Texture1D: var target1D = (GorgonRenderTarget1D)target.Resource; Width = target1D.Settings.Width; Viewport = target1D.Viewport; DepthStencil = depthStencil ?? target1D.DepthStencilBuffer; break; case ResourceType.Texture2D: var target2D = (GorgonRenderTarget2D)target.Resource; Width = target2D.Settings.Width; Height = target2D.Settings.Height; Viewport = target2D.Viewport; if (target2D.IsSwapChain) { SwapChain = (GorgonSwapChain)target2D; } DepthStencil = depthStencil ?? target2D.DepthStencilBuffer; break; case ResourceType.Texture3D: var target3D = (GorgonRenderTarget3D)target.Resource; Width = target3D.Settings.Width; Height = target3D.Settings.Height; Viewport = target3D.Viewport; break; default: throw new GorgonException(GorgonResult.CannotBind, "Could not bind a render target. Resource type is unknown. Should not see this error."); } }
/// <summary> /// Returns a collection of standard values for the data type this type converter is designed for when provided with a format context. /// </summary> /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext" /> that provides a format context that can be used to extract additional information about the environment from which this converter is invoked. This parameter or properties of this parameter can be null.</param> /// <returns> /// A <see cref="T:System.ComponentModel.TypeConverter.StandardValuesCollection" /> that holds a standard set of valid values, or null if the data type does not support a standard set of values. /// </returns> public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { var content = (GorgonImageContent)((ContentTypeDescriptor)context.Instance).Content; var formatList = (BufferFormat[])Enum.GetValues(typeof(BufferFormat)); BufferFormat currentFormat = content.ImageFormat; var formats = (from format in formatList let formatInfo = GorgonBufferFormatInfo.GetInfo(format) where (!formatInfo.HasDepth) && (!formatInfo.HasStencil) && (!formatInfo.IsTypeless) && (!formatInfo.IsCompressed) && (((content.ImageType == ImageType.Image1D) && (ContentObject.Graphics.VideoDevice.Supports1DTextureFormat(format))) || ((content.ImageType == ImageType.Image2D || content.ImageType == ImageType.ImageCube) && (ContentObject.Graphics.VideoDevice.Supports2DTextureFormat(format))) || ((content.ImageType == ImageType.Image3D) && (ContentObject.Graphics.VideoDevice.Supports3DTextureFormat(format)))) && (content.Codec.SupportedFormats.Any(item => item == format)) && ((content.MipCount == 1) || ((content.MipCount > 1) && (ContentObject.Graphics.VideoDevice.SupportsMipMaps(format)))) select format); return(new StandardValuesCollection(GorgonImageData.CanConvertToAny(currentFormat, formats))); }
/// <summary> /// Initializes a new instance of the <see cref="GorgonView"/> class. /// </summary> /// <param name="resource">The resource to bind to the view.</param> /// <param name="format">The format of the view.</param> protected GorgonView(GorgonResource resource, BufferFormat format) { Resource = resource; Format = format; FormatInformation = GorgonBufferFormatInfo.GetInfo(Format); }
/// <summary> /// Function to perform any post processing on image data. /// </summary> internal void PostProcess(GorgonImageData data) { IImageSettings destSettings = data.Settings.Clone(); int width = Width > 0 ? Width : data.Settings.Width; int height = Height > 0 ? Height : data.Settings.Height; int mipCount = MipCount > 0 ? MipCount : data.Settings.MipCount; BufferFormat format = (Format != BufferFormat.Unknown) ? Format : data.Settings.Format; Rectangle newSize = Rectangle.Empty; int mipStart = 0; var sourceInfo = GorgonBufferFormatInfo.GetInfo(data.Settings.Format); var destInfo = GorgonBufferFormatInfo.GetInfo(format); // First, confirm whether we can perform format conversions. using (var wic = new GorgonWICImage()) { Guid srcPixelFormat = wic.GetGUID(data.Settings.Format); Guid destPixelFormat = wic.GetGUID(format); // Do nothing if we can't do anything with the source format. if (srcPixelFormat == Guid.Empty) { return; } // Cancel conversion if we're using the same format. if (srcPixelFormat == destPixelFormat) { destPixelFormat = Guid.Empty; } // Get the new size. if ((width != data.Settings.Width) || (height != data.Settings.Height)) { newSize = new Rectangle(0, 0, width, height); } // Set up destination buffer settings. destSettings.Format = format; destSettings.Width = width; destSettings.Height = height; destSettings.MipCount = mipCount; // Ensure we don't go over the maximum. int maxMips = GorgonImageData.GetMaxMipCount(destSettings); if (mipCount > maxMips) { mipCount = maxMips; } // Nothing's going to happen here, so leave. if ((destPixelFormat == Guid.Empty) && (newSize == Rectangle.Empty) && (mipCount == 1)) { return; } // Create our worker buffer. GorgonImageData destData; using (destData = new GorgonImageData(destSettings)) { // The first step is to convert and resize our images: if ((destPixelFormat != Guid.Empty) || (newSize != Rectangle.Empty)) { for (int array = 0; array < destSettings.ArrayCount; array++) { // We're not going to copy mip levels at this point, that will come in the next step. for (int depth = 0; depth < destSettings.Depth; depth++) { // Get our source/destination buffers. var sourceBuffer = data.Buffers[0, data.Settings.ImageType == ImageType.Image3D ? depth : array]; var destBuffer = destData.Buffers[0, data.Settings.ImageType == ImageType.Image3D ? depth : array]; var dataRect = new DataRectangle(sourceBuffer.Data.BasePointer, sourceBuffer.PitchInformation.RowPitch); // Create a temporary WIC bitmap to work with. using (var bitmap = new Bitmap(wic.Factory, sourceBuffer.Width, sourceBuffer.Height, srcPixelFormat, dataRect, sourceBuffer.PitchInformation.SlicePitch)) { wic.TransformImageData(bitmap, destBuffer.Data.BasePointer, destBuffer.PitchInformation.RowPitch, destBuffer.PitchInformation.SlicePitch, destPixelFormat, sourceInfo.IssRGB, destInfo.IssRGB, Dithering, newSize, Clip, Filter); } } } // Adjust the mip map starting point. mipStart = 1; } // Next, we need to build mip maps. if (destSettings.MipCount > 1) { // The first step is to convert and resize our images: for (int array = 0; array < destSettings.ArrayCount; array++) { int mipDepth = destSettings.Depth; for (int mip = mipStart; mip < destSettings.MipCount; mip++) { // We're not going to copy mip levels at this point, that will come in the next step. for (int depth = 0; depth < mipDepth; depth++) { // Get our source/destination buffers. var sourceBuffer = destData.Buffers[0, data.Settings.ImageType == ImageType.Image3D ? (destSettings.Depth / mipDepth) * depth : array]; var destBuffer = destData.Buffers[mip, data.Settings.ImageType == ImageType.Image3D ? depth : array]; var dataRect = new DataRectangle(sourceBuffer.Data.BasePointer, sourceBuffer.PitchInformation.RowPitch); // Create a temporary WIC bitmap to work with. using (var bitmap = new Bitmap(wic.Factory, sourceBuffer.Width, sourceBuffer.Height, srcPixelFormat, dataRect, sourceBuffer.PitchInformation.SlicePitch)) { wic.TransformImageData(bitmap, destBuffer.Data.BasePointer, destBuffer.PitchInformation.RowPitch, destBuffer.PitchInformation.SlicePitch, Guid.Empty, false, false, ImageDithering.None, new Rectangle(0, 0, destBuffer.Width, destBuffer.Height), false, Filter); } } if (mipDepth > 1) { mipDepth >>= 1; } } } } // Update our data. data.TakeOwnership(destData); } } }
/// <summary> /// Function to persist image data to a stream. /// </summary> /// <param name="imageData"><see cref="GorgonLibrary.Graphics.GorgonImageData">Gorgon image data</see> to persist.</param> /// <param name="stream">Stream that will contain the data.</param> protected internal override void SaveToStream(GorgonImageData imageData, Stream stream) { // Use a binary writer. using (var writer = new GorgonBinaryWriter(stream, true)) { // Write the header for the file. TGAConversionFlags conversionFlags; WriteHeader(imageData.Settings, writer, out conversionFlags); GorgonFormatPitch pitch; if ((conversionFlags & TGAConversionFlags.RGB888) == TGAConversionFlags.RGB888) { pitch = new GorgonFormatPitch(imageData.Settings.Width * 3, imageData.Settings.Width * 3 * imageData.Settings.Height); } else { var formatInfo = GorgonBufferFormatInfo.GetInfo(imageData.Settings.Format); pitch = formatInfo.GetPitch(imageData.Settings.Width, imageData.Settings.Height, PitchFlags.None); } // Get the pointer to the first mip/array/depth level. var srcPointer = (byte *)imageData.Buffers[0].Data.UnsafePointer; var srcPitch = imageData.Buffers[0].PitchInformation; // If the two pitches are equal, then just write out the buffer. if ((pitch == srcPitch) && (conversionFlags == TGAConversionFlags.None)) { writer.Write(srcPointer, srcPitch.SlicePitch); return; } // If we have to do a conversion, create a worker buffer. using (var convertBuffer = new GorgonDataStream(pitch.SlicePitch)) { var destPtr = (byte *)convertBuffer.UnsafePointer; // Write out each scan line. for (int y = 0; y < imageData.Settings.Height; y++) { if ((conversionFlags & TGAConversionFlags.RGB888) == TGAConversionFlags.RGB888) { Compress24BPPScanLine(srcPointer, srcPitch.RowPitch, destPtr, pitch.RowPitch); } else if ((conversionFlags & TGAConversionFlags.Swizzle) == TGAConversionFlags.Swizzle) { SwizzleScanline(srcPointer, srcPitch.RowPitch, destPtr, pitch.RowPitch, imageData.Settings.Format, ImageBitFlags.None); } else { CopyScanline(srcPointer, srcPitch.RowPitch, destPtr, pitch.RowPitch, imageData.Settings.Format, ImageBitFlags.None); } destPtr += pitch.RowPitch; srcPointer += srcPitch.RowPitch; } // Persist to the stream. writer.Write(convertBuffer.UnsafePointer, pitch.SlicePitch); } } }
/// <summary> /// Function to perform the copying of image data into the buffer. /// </summary> /// <param name="stream">Stream containing the image data.</param> /// <param name="image">Image data.</param> /// <param name="conversionFlags">Flags used to convert the image.</param> private void CopyImageData(GorgonDataStream stream, GorgonImageData image, TGAConversionFlags conversionFlags) { var buffer = image.Buffers[0]; // Get the first buffer only. var formatInfo = GorgonBufferFormatInfo.GetInfo(image.Settings.Format); GorgonFormatPitch srcPitch = (conversionFlags & TGAConversionFlags.Expand) == TGAConversionFlags.Expand ? new GorgonFormatPitch(image.Settings.Width * 3, image.Settings.Width * 3 * image.Settings.Height) : formatInfo.GetPitch(image.Settings.Width, image.Settings.Height, PitchFlags.None); // Otherwise, allocate a buffer for conversion. var srcPtr = (byte *)stream.PositionPointerUnsafe; var destPtr = (byte *)buffer.Data.UnsafePointer; // Adjust destination for inverted axes. if ((conversionFlags & TGAConversionFlags.InvertX) == TGAConversionFlags.InvertX) { destPtr += buffer.PitchInformation.RowPitch - formatInfo.SizeInBytes; } if ((conversionFlags & TGAConversionFlags.InvertY) != TGAConversionFlags.InvertY) { destPtr += (image.Settings.Height - 1) * buffer.PitchInformation.RowPitch; } // Get bounds of image memory. var scanSize = (int)(stream.Length - stream.Position); byte *endScan = (byte *)stream.PositionPointerUnsafe + scanSize; int opaqueLineCount = 0; for (int y = 0; y < image.Settings.Height; y++) { bool setOpaque; if ((conversionFlags & TGAConversionFlags.RLE) == TGAConversionFlags.RLE) { setOpaque = ReadCompressed(ref srcPtr, image.Settings.Width, destPtr, endScan, image.Settings.Format, conversionFlags); } else { setOpaque = ReadUncompressed(srcPtr, srcPitch.RowPitch, destPtr, image.Settings.Format, conversionFlags); srcPtr += srcPitch.RowPitch; } if ((setOpaque) && (SetOpaqueIfZeroAlpha)) { opaqueLineCount++; } if ((conversionFlags & TGAConversionFlags.InvertY) != TGAConversionFlags.InvertY) { destPtr -= buffer.PitchInformation.RowPitch; } else { destPtr += buffer.PitchInformation.RowPitch; } } if (opaqueLineCount != image.Settings.Height) { return; } // Set the alpha to opaque if we don't have any alpha values (i.e. alpha = 0 for all pixels). destPtr = (byte *)buffer.Data.UnsafePointer; for (int y = 0; y < image.Settings.Height; y++) { CopyScanline(destPtr, buffer.PitchInformation.RowPitch, destPtr, buffer.PitchInformation.RowPitch, image.Settings.Format, ImageBitFlags.OpaqueAlpha); destPtr += buffer.PitchInformation.RowPitch; } }
/// <summary> /// Function to clip the sprite region from the texture. /// </summary> /// <param name="texture">Texture containing the image data to clip.</param> /// <returns>A rectangle for the sprite. Or an empty rectangle if no sprite was selected.</returns> public unsafe Rectangle Clip(GorgonTexture2D texture) { GorgonImageData imageData = null; try { // Constrain our mouse to the texture. _startPoint.X = _startPoint.X.Max(0).Min(texture.Settings.Width - 1); _startPoint.Y = _startPoint.Y.Max(0).Min(texture.Settings.Height - 1); imageData = GorgonImageData.CreateFromTexture(texture); _stride = imageData.Buffers[0].PitchInformation.RowPitch; _format = imageData.Settings.Format; _bytesPerPixel = GorgonBufferFormatInfo.GetInfo(_format).SizeInBytes; _textureWidth = imageData.Settings.Width; // Get a pointer to the buffer. byte *bytePtr = (byte *)imageData.UnsafePointer; if (IsMaskValue(bytePtr, _startPoint.X, _startPoint.Y)) { return(Rectangle.Empty); } Queue <ClipSpan> clipSpans = new Queue <ClipSpan>(); _pixels = new bool[imageData.Settings.Width * imageData.Settings.Height]; int left = _startPoint.X; int top = _startPoint.Y; int right = left; int bottom = top; // Get the initial span from our starting point and add it to our queue. ClipSpan span = GetSpan(bytePtr, left, top); clipSpans.Enqueue(span); while (clipSpans.Count > 0) { // Take the span off the queue. span = clipSpans.Dequeue(); // Find the next vertical span above and below the current span. int west = span.Start; int east = span.End; int north = span.Y - 1; int south = span.Y + 1; // Check each pixel between the start and end of the upper and lower spans. for (int x = west; x <= east; ++x) { int pixelindex = _textureWidth * north + x; if ((span.Y > 0) && (!IsMaskValue(bytePtr, x, north)) && (!_pixels[pixelindex])) { clipSpans.Enqueue(GetSpan(bytePtr, x, north)); } pixelindex = _textureWidth * south + x; if ((span.Y >= imageData.Settings.Height - 1) || (IsMaskValue(bytePtr, x, south)) || (_pixels[pixelindex])) { continue; } clipSpans.Enqueue(GetSpan(bytePtr, x, south)); } // Update the boundaries. left = west.Min(left); right = (east + 1).Max(right); top = (north + 1).Min(top); bottom = south.Max(bottom); } return(Rectangle.FromLTRB(left, top, right, bottom)); } finally { if (imageData != null) { imageData.Dispose(); } } }
/// <summary> /// Function to create a default render target view. /// </summary> protected override void OnCreateDefaultRenderTargetView() { var info = GorgonBufferFormatInfo.GetInfo(Settings.DefaultShaderViewFormat); DefaultRenderTargetView = OnGetRenderTargetView(Settings.Format, Settings.Format, 0, Settings.SizeInBytes / info.SizeInBytes); }