private void expandBounds(Vector2 pos) { if (pos.X - PathWidth < minX) { minX = pos.X - PathWidth; } if (pos.Y - PathWidth < minY) { minY = pos.Y - PathWidth; } if (pos.X + PathWidth > maxX) { maxX = pos.X + PathWidth; } if (pos.Y + PathWidth > maxY) { maxY = pos.Y + PathWidth; } RectangleF b = bounds; if ((RelativeSizeAxes & Axes.X) == 0) { Width = b.Width; } if ((RelativeSizeAxes & Axes.Y) == 0) { Height = b.Height; } }
public void SetDragRectangle(RectangleF rectangle) { var topLeft = Parent.ToLocalSpace(rectangle.TopLeft); var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight); Position = topLeft; Size = bottomRight - topLeft; }
public override void DrawQuad(Quad vertexQuad, RectangleF?textureRect, ColourInfo drawColour, Action <TexturedVertex2D> vertexAction = null, Vector2?inflationPercentage = null, Vector2?blendRangeOverride = null) { if (IsDisposed) { throw new ObjectDisposedException(ToString(), "Can not draw a quad with a disposed texture."); } RectangleF texRect = GetTextureRect(textureRect); Vector2 inflationAmount = inflationPercentage.HasValue ? new Vector2(inflationPercentage.Value.X * texRect.Width, inflationPercentage.Value.Y * texRect.Height) : Vector2.Zero; RectangleF inflatedTexRect = texRect.Inflate(inflationAmount); Vector2 blendRange = blendRangeOverride ?? inflationAmount; if (vertexAction == null) { if (quadBatch == null) { quadBatch = new QuadBatch <TexturedVertex2D>(512, 128); } vertexAction = quadBatch.Add; } vertexAction(new TexturedVertex2D { Position = vertexQuad.BottomLeft, TexturePosition = new Vector2(inflatedTexRect.Left, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = blendRange, Colour = drawColour.BottomLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexQuad.BottomRight, TexturePosition = new Vector2(inflatedTexRect.Right, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = blendRange, Colour = drawColour.BottomRight.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexQuad.TopRight, TexturePosition = new Vector2(inflatedTexRect.Right, inflatedTexRect.Top), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = blendRange, Colour = drawColour.TopRight.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexQuad.TopLeft, TexturePosition = new Vector2(inflatedTexRect.Left, inflatedTexRect.Top), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = blendRange, Colour = drawColour.TopLeft.Linear, }); FrameStatistics.Increment(StatisticsCounterType.KiloPixels, (long)vertexQuad.ConservativeArea); }
private void drawFrameBufferToBackBuffer(FrameBuffer frameBuffer, RectangleF drawRectangle, ColourInfo colourInfo) { // The strange Y coordinate and Height are a result of OpenGL coordinate systems having Y grow upwards and not downwards. RectangleF textureRect = new RectangleF(0, frameBuffer.Texture.Height, frameBuffer.Texture.Width, -frameBuffer.Texture.Height); if (frameBuffer.Texture.Bind()) { // Color was already applied by base.Draw(); no need to re-apply. Thus we use White here. frameBuffer.Texture.DrawQuad(drawRectangle, textureRect, colourInfo); } }
/// <summary> /// Blits sprite to OpenGL display with specified parameters. /// </summary> public override void Draw(Quad vertexQuad, RectangleF?textureRect, Color4 drawColour, VertexBatch <TexturedVertex2d> spriteBatch = null) { Debug.Assert(!isDisposed); if (!Bind()) { return; } RectangleF texRect = textureRect != null ? new RectangleF(textureRect.Value.X, textureRect.Value.Y, textureRect.Value.Width, textureRect.Value.Height) : new RectangleF(0, 0, Width, Height); texRect.X /= width; texRect.Y /= height; texRect.Width /= width; texRect.Height /= height; if (spriteBatch == null) { if (TextureGLSingle.spriteBatch == null) { TextureGLSingle.spriteBatch = new QuadBatch <TexturedVertex2d>(1, 100); } spriteBatch = TextureGLSingle.spriteBatch; } spriteBatch.Add(new TexturedVertex2d { Position = vertexQuad.BottomLeft, TexturePosition = new Vector2(texRect.Left, texRect.Bottom), Colour = drawColour }); spriteBatch.Add(new TexturedVertex2d { Position = vertexQuad.BottomRight, TexturePosition = new Vector2(texRect.Right, texRect.Bottom), Colour = drawColour }); spriteBatch.Add(new TexturedVertex2d { Position = vertexQuad.TopRight, TexturePosition = new Vector2(texRect.Right, texRect.Top), Colour = drawColour }); spriteBatch.Add(new TexturedVertex2d { Position = vertexQuad.TopLeft, TexturePosition = new Vector2(texRect.Left, texRect.Top), Colour = drawColour }); }
/// <summary> /// Crop the texture. /// </summary> /// <param name="cropRectangle">The rectangle the cropped texture should reference.</param> /// <param name="relativeSizeAxes">Which axes have a relative size in [0,1] in relation to the texture size.</param> /// <param name="wrapModeS">The texture wrap mode in horizontal direction.</param> /// <param name="wrapModeT">The texture wrap mode in vertical direction.</param> /// <returns>The cropped texture.</returns> public Texture Crop(RectangleF cropRectangle, Axes relativeSizeAxes = Axes.None, WrapMode wrapModeS = WrapMode.None, WrapMode wrapModeT = WrapMode.None) { if (relativeSizeAxes != Axes.None) { Vector2 scale = new Vector2( relativeSizeAxes.HasFlag(Axes.X) ? Width : 1, relativeSizeAxes.HasFlag(Axes.Y) ? Height : 1 ); cropRectangle *= scale; } return(new Texture(new TextureGLSub(cropRectangle, TextureGL, wrapModeS, wrapModeT))); }
public override RectangleF GetTextureRect(RectangleF?textureRect) { RectangleF texRect = textureRect != null ? new RectangleF(textureRect.Value.X, textureRect.Value.Y, textureRect.Value.Width, textureRect.Value.Height) : new RectangleF(0, 0, Width, Height); texRect.X /= width; texRect.Y /= height; texRect.Width /= width; texRect.Height /= height; return(texRect); }
private void drawEdgeEffect() { if (MaskingInfo == null || EdgeEffect.Type == EdgeEffectType.None || EdgeEffect.Radius <= 0.0f || EdgeEffect.Colour.Linear.A <= 0.0f) { return; } RectangleF effectRect = MaskingInfo.Value.MaskingRect.Inflate(EdgeEffect.Radius).Offset(EdgeEffect.Offset); if (!ScreenSpaceMaskingQuad.HasValue) { ScreenSpaceMaskingQuad = Quad.FromRectangle(effectRect) * DrawInfo.Matrix; } MaskingInfo edgeEffectMaskingInfo = MaskingInfo.Value; edgeEffectMaskingInfo.MaskingRect = effectRect; edgeEffectMaskingInfo.ScreenSpaceAABB = ScreenSpaceMaskingQuad.Value.AABB; edgeEffectMaskingInfo.CornerRadius += EdgeEffect.Radius + EdgeEffect.Roundness; edgeEffectMaskingInfo.BorderThickness = 0; // HACK HACK HACK. We abuse blend range to give us the linear alpha gradient of // the edge effect along its radius using the same rounded-corners shader. edgeEffectMaskingInfo.BlendRange = EdgeEffect.Radius; edgeEffectMaskingInfo.AlphaExponent = 2; edgeEffectMaskingInfo.Hollow = EdgeEffect.Hollow; GLWrapper.PushMaskingInfo(edgeEffectMaskingInfo); GLWrapper.SetBlend(new BlendingInfo(EdgeEffect.Type == EdgeEffectType.Glow ? BlendingMode.Additive : BlendingMode.Mixture)); Shader.Bind(); ColourInfo colour = ColourInfo.SingleColour(EdgeEffect.Colour); colour.TopLeft.MultiplyAlpha(DrawInfo.Colour.TopLeft.Linear.A); colour.BottomLeft.MultiplyAlpha(DrawInfo.Colour.BottomLeft.Linear.A); colour.TopRight.MultiplyAlpha(DrawInfo.Colour.TopRight.Linear.A); colour.BottomRight.MultiplyAlpha(DrawInfo.Colour.BottomRight.Linear.A); Texture.WhitePixel.DrawQuad( ScreenSpaceMaskingQuad.Value, colour, null, null, null, // HACK HACK HACK. We re-use the unused vertex blend range to store the original // masking blend range when rendering edge effects. This is needed for smooth inner edges // with a hollow edge effect. new Vector2(MaskingInfo.Value.BlendRange)); Shader.Unbind(); GLWrapper.PopMaskingInfo(); }
protected virtual RectangleF textureBounds(RectangleF?textureRect = null) { RectangleF texRect = textureRect ?? new RectangleF(0, 0, DisplayWidth, DisplayHeight); if (DpiScale != 1) { texRect.Width *= DpiScale; texRect.Height *= DpiScale; texRect.X *= DpiScale; texRect.Y *= DpiScale; } return(texRect); }
public void Draw(Quad vertexQuad, Color4 colour, RectangleF?textureRect = null, VertexBatch <TexturedVertex2d> spriteBatch = null) { RectangleF texRect = textureRect ?? new RectangleF(0, 0, DisplayWidth, DisplayHeight); if (DpiScale != 1) { texRect.Width *= DpiScale; texRect.Height *= DpiScale; texRect.X *= DpiScale; texRect.Y *= DpiScale; } TextureGL?.Draw(vertexQuad, texRect, colour, spriteBatch); }
/// <summary> /// Blits sprite to OpenGL display with specified parameters. /// </summary> public override void Draw(Quad vertexQuad, RectangleF?textureRect, Color4 drawColour, VertexBatch <TexturedVertex2D> spriteBatch = null, Vector2?inflationPercentage = null) { Debug.Assert(!isDisposed); RectangleF texRect = GetTextureRect(textureRect); if (inflationPercentage.HasValue) { texRect = texRect.Inflate(new Vector2(inflationPercentage.Value.X * texRect.Width, inflationPercentage.Value.Y * texRect.Height)); } if (spriteBatch == null) { if (TextureGLSingle.spriteBatch == null) { TextureGLSingle.spriteBatch = new QuadBatch <TexturedVertex2D>(512, 128); } spriteBatch = TextureGLSingle.spriteBatch; } Color4 linearColour = drawColour.toLinear(); spriteBatch.Add(new TexturedVertex2D { Position = vertexQuad.BottomLeft, TexturePosition = new Vector2(texRect.Left, texRect.Bottom), Colour = linearColour }); spriteBatch.Add(new TexturedVertex2D { Position = vertexQuad.BottomRight, TexturePosition = new Vector2(texRect.Right, texRect.Bottom), Colour = linearColour }); spriteBatch.Add(new TexturedVertex2D { Position = vertexQuad.TopRight, TexturePosition = new Vector2(texRect.Right, texRect.Top), Colour = linearColour }); spriteBatch.Add(new TexturedVertex2D { Position = vertexQuad.TopLeft, TexturePosition = new Vector2(texRect.Left, texRect.Top), Colour = linearColour }); FrameStatistics.Increment(StatisticsCounterType.KiloPixels, (long)vertexQuad.ConservativeArea); }
private RectangleF boundsInParent(RectangleF?textureRect) { RectangleF actualBounds = bounds; if (textureRect.HasValue) { RectangleF localBounds = textureRect.Value; actualBounds.X += localBounds.X; actualBounds.Y += localBounds.Y; actualBounds.Width = Math.Min(localBounds.Width, bounds.Width); actualBounds.Height = Math.Min(localBounds.Height, bounds.Height); } return(actualBounds); }
/// <summary> /// Blits sprite to OpenGL display with specified parameters. /// </summary> public override void Draw(Quad vertexQuad, RectangleF?textureRect, Color4 drawColour, VertexBatch <TexturedVertex2d> spriteBatch = null) { RectangleF actualBounds = bounds; if (textureRect.HasValue) { RectangleF localBounds = textureRect.Value; actualBounds.X += localBounds.X; actualBounds.Y += localBounds.Y; actualBounds.Width = Math.Min(localBounds.Width, bounds.Width); actualBounds.Height = Math.Min(localBounds.Height, bounds.Height); } parent.Draw(vertexQuad, actualBounds, drawColour, spriteBatch); }
/// <summary> /// Applies a new orthographic projection rectangle. /// </summary> /// <param name="ortho">The orthographic projection rectangle.</param> public static void PushOrtho(RectangleF ortho) { FlushCurrentBatch(); ortho_stack.Push(ortho); if (Ortho == ortho) { return; } Ortho = ortho; ProjectionMatrix = Matrix4.CreateOrthographicOffCenter(Ortho.Left, Ortho.Right, Ortho.Bottom, Ortho.Top, -1, 1); Shader.SetGlobalProperty(@"g_ProjMatrix", ProjectionMatrix); UpdateScissorToCurrentViewportAndOrtho(); }
public override void DrawQuad(Quad vertexQuad, RectangleF?textureRect, ColourInfo drawColour, Action <TexturedVertex2D> vertexAction = null, Vector2?inflationPercentage = null) { Debug.Assert(!isDisposed); RectangleF texRect = GetTextureRect(textureRect); if (inflationPercentage.HasValue) { texRect = texRect.Inflate(new Vector2(inflationPercentage.Value.X * texRect.Width, inflationPercentage.Value.Y * texRect.Height)); } if (vertexAction == null) { if (quadBatch == null) { quadBatch = new QuadBatch <TexturedVertex2D>(512, 128); } vertexAction = quadBatch.Add; } vertexAction(new TexturedVertex2D { Position = vertexQuad.BottomLeft, TexturePosition = new Vector2(texRect.Left, texRect.Bottom), Colour = drawColour.BottomLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexQuad.BottomRight, TexturePosition = new Vector2(texRect.Right, texRect.Bottom), Colour = drawColour.BottomRight.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexQuad.TopRight, TexturePosition = new Vector2(texRect.Right, texRect.Top), Colour = drawColour.TopRight.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexQuad.TopLeft, TexturePosition = new Vector2(texRect.Left, texRect.Top), Colour = drawColour.TopLeft.Linear, }); FrameStatistics.Increment(StatisticsCounterType.KiloPixels, (long)vertexQuad.ConservativeArea); }
public override void DrawTriangle(Triangle vertexTriangle, RectangleF?textureRect, ColourInfo drawColour, Action <TexturedVertex2D> vertexAction = null, Vector2?inflationPercentage = null) { Debug.Assert(!isDisposed); RectangleF texRect = GetTextureRect(textureRect); if (inflationPercentage.HasValue) { texRect = texRect.Inflate(new Vector2(inflationPercentage.Value.X * texRect.Width, inflationPercentage.Value.Y * texRect.Height)); } if (vertexAction == null) { if (triangleBatch == null) { // We multiply the size param by 3 such that the amount of vertices is a multiple of the amount of vertices // per primitive (triangles in this case). Otherwise overflowing the batch will result in wrong // grouping of vertices into primitives. triangleBatch = new LinearBatch <TexturedVertex2D>(512 * 3, 128, PrimitiveType.Triangles); } vertexAction = triangleBatch.Add; } vertexAction(new TexturedVertex2D { Position = vertexTriangle.P0, TexturePosition = new Vector2((texRect.Left + texRect.Right) / 2, texRect.Top), Colour = drawColour.TopLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P1, TexturePosition = new Vector2(texRect.Left, texRect.Bottom), Colour = drawColour.BottomLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P2, TexturePosition = new Vector2(texRect.Right, texRect.Bottom), Colour = drawColour.BottomRight.Linear, }); FrameStatistics.Increment(StatisticsCounterType.KiloPixels, (long)vertexTriangle.ConservativeArea); }
public InfoOverlay() { RelativeSizeAxes = Axes.Both; Children = new[] { layout = new FlashyBox(d => d.ToScreenSpace(d.LayoutRectangle)) { Colour = Color4.Green, Alpha = 0.5f, }, shape = new FlashyBox(d => d.ScreenSpaceDrawQuad) { Colour = Color4.Blue, Alpha = 0.5f, }, childShape = new FlashyBox(delegate(Drawable d) { var c = d as CompositeDrawable; if (c == null) { return(d.ScreenSpaceDrawQuad); } RectangleF rect = new RectangleF(c.ChildOffset, c.ChildSize); return(d.ToScreenSpace(rect)); }) { Colour = Color4.Red, Alpha = 0.5f, }, // We're adding this guy twice to get a border in a somewhat hacky way. new FlashyBox(d => quadAroundPosition(d.ToScreenSpace(d.OriginPosition), 5)) { Colour = Color4.Blue, }, new FlashyBox(d => quadAroundPosition(d.ToScreenSpace(d.OriginPosition), 3)) { Colour = Color4.Yellow, }, }; }
/// <summary> /// Applies the last orthographic projection rectangle. /// </summary> public static void PopOrtho() { Trace.Assert(ortho_stack.Count > 1); FlushCurrentBatch(); ortho_stack.Pop(); RectangleF actualRect = ortho_stack.Peek(); if (Ortho == actualRect) { return; } Ortho = actualRect; ProjectionMatrix = Matrix4.CreateOrthographicOffCenter(Ortho.Left, Ortho.Right, Ortho.Bottom, Ortho.Top, -1, 1); Shader.SetGlobalProperty(@"g_ProjMatrix", ProjectionMatrix); UpdateScissorToCurrentViewportAndOrtho(); }
public static void UpdateScissorToCurrentViewportAndOrtho() { RectangleF viewportRect = Viewport; Vector2 offset = viewportRect.TopLeft - Ortho.TopLeft; RectangleI currentScissorRect = scissor_rect_stack.Peek(); RectangleI scissorRect = new RectangleI( currentScissorRect.X + (int)Math.Floor(offset.X), Viewport.Height - currentScissorRect.Bottom - (int)Math.Ceiling(offset.Y), currentScissorRect.Width, currentScissorRect.Height); if (!Precision.AlmostEquals(offset, Vector2.Zero)) { ++scissorRect.Width; ++scissorRect.Height; } GL.Scissor(scissorRect.X, scissorRect.Y, scissorRect.Width, scissorRect.Height); }
protected override void UpdateVertices() { Vertices.Clear(); Normals.Clear(); float cornerRadius = (drawable as CompositeDrawable)?.CornerRadius ?? 0; // Sides RectangleF rect = drawable.DrawRectangle; Vector2[] corners = { rect.TopLeft, rect.TopRight, rect.BottomRight, rect.BottomLeft }; const int amount_side_steps = 2; for (int i = 0; i < 4; ++i) { Vector2 a = corners[i]; Vector2 b = corners[(i + 1) % 4]; Vector2 diff = b - a; float length = diff.Length; Vector2 dir = diff / length; float usableLength = Math.Max(length - 2 * cornerRadius, 0); Vector2 normal = (b - a).PerpendicularRight.Normalized(); for (int j = 0; j < amount_side_steps; ++j) { Vertices.Add(a + dir * (cornerRadius + j * usableLength / (amount_side_steps - 1))); Normals.Add(normal); } } const int amount_corner_steps = 10; if (cornerRadius > 0) { // Rounded corners Vector2[] offsets = { new Vector2(cornerRadius, cornerRadius), new Vector2(-cornerRadius, cornerRadius), new Vector2(-cornerRadius, -cornerRadius), new Vector2(cornerRadius, -cornerRadius), }; for (int i = 0; i < 4; ++i) { Vector2 a = corners[i]; float startTheta = (i - 1) * (float)Math.PI / 2; for (int j = 0; j < amount_corner_steps; ++j) { float theta = startTheta + j * (float)Math.PI / (2 * (amount_corner_steps - 1)); Vector2 normal = new Vector2((float)Math.Sin(theta), (float)Math.Cos(theta)); Vertices.Add(a + offsets[i] + normal * cornerRadius); Normals.Add(normal); } } } // To simulation space Matrix3 mat = drawable.DrawInfo.Matrix * ScreenToSimulationSpace; Matrix3 normMat = mat.Inverted(); normMat.Transpose(); // Remove translation normMat.M31 = normMat.M32 = normMat.M13 = normMat.M23 = 0; Vector2 translation = Vector2.Zero * normMat; for (int i = 0; i < Vertices.Count; ++i) { Vertices[i] *= mat; Normals[i] = (Normals[i] * normMat - translation).Normalized(); } }
public override void Draw(Action <TexturedVertex2D> vertexAction) { currentFrameBufferIndex = originalIndex; Vector2 frameBufferSize = new Vector2((float)Math.Ceiling(ScreenSpaceDrawRectangle.Width), (float)Math.Ceiling(ScreenSpaceDrawRectangle.Height)); if (UpdateVersion > DrawVersion.Value || frameBufferSize != FrameBuffers[0].Size) { DrawVersion.Value = UpdateVersion; using (establishFrameBufferViewport(frameBufferSize)) { drawChildren(vertexAction, frameBufferSize); // Blur post-processing in case a blur radius is defined. if (BlurRadius.X > 0 || BlurRadius.Y > 0) { GL.Disable(EnableCap.ScissorTest); if (BlurRadius.X > 0) { drawBlurredFrameBuffer(BlurRadius.X, BlurSigma.X, BlurRotation); } if (BlurRadius.Y > 0) { drawBlurredFrameBuffer(BlurRadius.Y, BlurSigma.Y, BlurRotation + 90); } GL.Enable(EnableCap.ScissorTest); } } finalizeFrameBuffer(); } RectangleF drawRectangle = FilteringMode == All.Nearest ? new RectangleF(ScreenSpaceDrawRectangle.X, ScreenSpaceDrawRectangle.Y, frameBufferSize.X, frameBufferSize.Y) : ScreenSpaceDrawRectangle; Shader.Bind(); if (DrawOriginal && EffectPlacement == EffectPlacement.InFront) { GLWrapper.SetBlend(DrawInfo.Blending); drawFrameBufferToBackBuffer(FrameBuffers[originalIndex], drawRectangle, DrawInfo.Colour); } // Blit the final framebuffer to screen. GLWrapper.SetBlend(new BlendingInfo(EffectBlending)); ColourInfo effectColour = DrawInfo.Colour; effectColour.ApplyChild(EffectColour); drawFrameBufferToBackBuffer(FrameBuffers[0], drawRectangle, effectColour); if (DrawOriginal && EffectPlacement == EffectPlacement.Behind) { GLWrapper.SetBlend(DrawInfo.Blending); drawFrameBufferToBackBuffer(FrameBuffers[originalIndex], drawRectangle, DrawInfo.Colour); } Shader.Unbind(); }
public override void Draw(Action <TexturedVertex2D> vertexAction) { base.Draw(vertexAction); if (texture?.Available != true || points == null || points.Count == 0) { return; } shader.Bind(); texture.TextureGL.Bind(); Vector2 localInflationAmount = new Vector2(0, 1) * DrawInfo.MatrixInverse.ExtractScale().Xy; // We're dealing with a _large_ number of points, so we need to optimise the quadToDraw * drawInfo.Matrix multiplications below // for points that are going to be masked out anyway. This allows for higher resolution graphs at larger scales with virtually no performance loss. // Since the points are generated in the local coordinate space, we need to convert the screen space masking quad coordinates into the local coordinate space RectangleF localMaskingRectangle = (Quad.FromRectangle(GLWrapper.CurrentMaskingInfo.ScreenSpaceAABB) * DrawInfo.MatrixInverse).AABBFloat; float separation = drawSize.X / (points.Count - 1); for (int i = 0; i < points.Count - 1; i++) { float leftX = i * separation; float rightX = (i + 1) * separation; if (rightX < localMaskingRectangle.Left) { continue; } if (leftX > localMaskingRectangle.Right) { break; // X is always increasing } Color4 frequencyColour = baseColour; // colouring is applied in the order of interest to a viewer. frequencyColour = Interpolation.ValueAt(points[i].MidIntensity / midMax, frequencyColour, midColour, 0, 1); // high end (cymbal) can help find beat, so give it priority over mids. frequencyColour = Interpolation.ValueAt(points[i].HighIntensity / highMax, frequencyColour, highColour, 0, 1); // low end (bass drum) is generally the best visual aid for beat matching, so give it priority over high/mid. frequencyColour = Interpolation.ValueAt(points[i].LowIntensity / lowMax, frequencyColour, lowColour, 0, 1); ColourInfo finalColour = DrawColourInfo.Colour; finalColour.ApplyChild(frequencyColour); Quad quadToDraw; switch (channels) { default: case 2: { float height = drawSize.Y / 2; quadToDraw = new Quad( new Vector2(leftX, height - points[i].Amplitude[0] * height), new Vector2(rightX, height - points[i + 1].Amplitude[0] * height), new Vector2(leftX, height + points[i].Amplitude[1] * height), new Vector2(rightX, height + points[i + 1].Amplitude[1] * height) ); break; } case 1: { quadToDraw = new Quad( new Vector2(leftX, drawSize.Y - points[i].Amplitude[0] * drawSize.Y), new Vector2(rightX, drawSize.Y - points[i + 1].Amplitude[0] * drawSize.Y), new Vector2(leftX, drawSize.Y), new Vector2(rightX, drawSize.Y) ); break; } } quadToDraw *= DrawInfo.Matrix; DrawQuad(texture, quadToDraw, finalColour, null, vertexBatch.AddAction, Vector2.Divide(localInflationAmount, quadToDraw.Size)); } shader.Unbind(); }
public override void Draw(Action <TexturedVertex2D> vertexAction) { base.Draw(vertexAction); if (Points == null || Points.Count == 0) { return; } Shader.Bind(); Texture.TextureGL.Bind(); Vector2 localInflationAmount = new Vector2(0, 1) * DrawInfo.MatrixInverse.ExtractScale().Xy; // We're dealing with a _large_ number of points, so we need to optimise the quadToDraw * drawInfo.Matrix multiplications below // for points that are going to be masked out anyway. This allows for higher resolution graphs at larger scales with virtually no performance loss. // Since the points are generated in the local coordinate space, we need to convert the screen space masking quad coordinates into the local coordinate space RectangleF localMaskingRectangle = (Quad.FromRectangle(GLWrapper.CurrentMaskingInfo.ScreenSpaceAABB) * DrawInfo.MatrixInverse).AABBFloat; float separation = DrawSize.X / (Points.Count - 1); for (int i = 0; i < Points.Count - 1; i++) { float leftX = i * separation; float rightX = (i + 1) * separation; if (rightX < localMaskingRectangle.Left) { continue; } if (leftX > localMaskingRectangle.Right) { break; // X is always increasing } ColourInfo colour = DrawInfo.Colour; Quad quadToDraw; switch (Channels) { default: case 2: { float height = DrawSize.Y / 2; quadToDraw = new Quad( new Vector2(leftX, height - Points[i].Amplitude[0] * height), new Vector2(rightX, height - Points[i + 1].Amplitude[0] * height), new Vector2(leftX, height + Points[i].Amplitude[1] * height), new Vector2(rightX, height + Points[i + 1].Amplitude[1] * height) ); } break; case 1: { quadToDraw = new Quad( new Vector2(leftX, DrawSize.Y - Points[i].Amplitude[0] * DrawSize.Y), new Vector2(rightX, DrawSize.Y - Points[i + 1].Amplitude[0] * DrawSize.Y), new Vector2(leftX, DrawSize.Y), new Vector2(rightX, DrawSize.Y) ); break; } } quadToDraw *= DrawInfo.Matrix; Texture.DrawQuad(quadToDraw, colour, null, Shared.VertexBatch.Add, Vector2.Divide(localInflationAmount, quadToDraw.Size)); } Shader.Unbind(); }
public override void DrawTriangle(Triangle vertexTriangle, RectangleF?textureRect, ColourInfo drawColour, Action <TexturedVertex2D> vertexAction = null, Vector2?inflationPercentage = null) { Debug.Assert(!IsDisposed); RectangleF texRect = GetTextureRect(textureRect); Vector2 inflationAmount = inflationPercentage.HasValue ? new Vector2(inflationPercentage.Value.X * texRect.Width, inflationPercentage.Value.Y * texRect.Height) : Vector2.Zero; RectangleF inflatedTexRect = texRect.Inflate(inflationAmount); if (vertexAction == null) { if (triangleBatch == null) { // We multiply the size param by 3 such that the amount of vertices is a multiple of the amount of vertices // per primitive (triangles in this case). Otherwise overflowing the batch will result in wrong // grouping of vertices into primitives. triangleBatch = new LinearBatch <TexturedVertex2D>(512 * 3, 128, PrimitiveType.Triangles); } vertexAction = triangleBatch.Add; } // We split the triangle into two, such that we can obtain smooth edges with our // texture coordinate trick. We might want to revert this to drawing a single // triangle in case we ever need proper texturing, or if the additional vertices // end up becoming an overhead (unlikely). SRGBColour topColour = (drawColour.TopLeft + drawColour.TopRight) / 2; SRGBColour bottomColour = (drawColour.BottomLeft + drawColour.BottomRight) / 2; // Left triangle half vertexAction(new TexturedVertex2D { Position = vertexTriangle.P0, TexturePosition = new Vector2(inflatedTexRect.Left, inflatedTexRect.Top), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = topColour.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P1, TexturePosition = new Vector2(inflatedTexRect.Left, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = drawColour.BottomLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = (vertexTriangle.P1 + vertexTriangle.P2) / 2, TexturePosition = new Vector2((inflatedTexRect.Left + inflatedTexRect.Right) / 2, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = bottomColour.Linear, }); // Right triangle half vertexAction(new TexturedVertex2D { Position = vertexTriangle.P0, TexturePosition = new Vector2(inflatedTexRect.Right, inflatedTexRect.Top), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = topColour.Linear, }); vertexAction(new TexturedVertex2D { Position = (vertexTriangle.P1 + vertexTriangle.P2) / 2, TexturePosition = new Vector2((inflatedTexRect.Left + inflatedTexRect.Right) / 2, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = bottomColour.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P2, TexturePosition = new Vector2(inflatedTexRect.Right, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = drawColour.BottomRight.Linear, }); FrameStatistics.Increment(StatisticsCounterType.KiloPixels, (long)vertexTriangle.ConservativeArea); }
public override void DrawTriangle(Triangle vertexTriangle, RectangleF?textureRect, ColourInfo drawColour, Action <TexturedVertex2D> vertexAction = null, Vector2?inflationPercentage = null) { if (IsDisposed) { throw new ObjectDisposedException(ToString(), "Can not draw a triangle with a disposed texture."); } RectangleF texRect = GetTextureRect(textureRect); Vector2 inflationAmount = inflationPercentage.HasValue ? new Vector2(inflationPercentage.Value.X * texRect.Width, inflationPercentage.Value.Y * texRect.Height) : Vector2.Zero; RectangleF inflatedTexRect = texRect.Inflate(inflationAmount); if (vertexAction == null) { vertexAction = default_triangle_action; } // We split the triangle into two, such that we can obtain smooth edges with our // texture coordinate trick. We might want to revert this to drawing a single // triangle in case we ever need proper texturing, or if the additional vertices // end up becoming an overhead (unlikely). SRGBColour topColour = (drawColour.TopLeft + drawColour.TopRight) / 2; SRGBColour bottomColour = (drawColour.BottomLeft + drawColour.BottomRight) / 2; // Left triangle half vertexAction(new TexturedVertex2D { Position = vertexTriangle.P0, TexturePosition = new Vector2(inflatedTexRect.Left, inflatedTexRect.Top), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = topColour.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P1, TexturePosition = new Vector2(inflatedTexRect.Left, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = drawColour.BottomLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = (vertexTriangle.P1 + vertexTriangle.P2) / 2, TexturePosition = new Vector2((inflatedTexRect.Left + inflatedTexRect.Right) / 2, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = bottomColour.Linear, }); // Right triangle half vertexAction(new TexturedVertex2D { Position = vertexTriangle.P0, TexturePosition = new Vector2(inflatedTexRect.Right, inflatedTexRect.Top), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = topColour.Linear, }); vertexAction(new TexturedVertex2D { Position = (vertexTriangle.P1 + vertexTriangle.P2) / 2, TexturePosition = new Vector2((inflatedTexRect.Left + inflatedTexRect.Right) / 2, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = bottomColour.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P2, TexturePosition = new Vector2(inflatedTexRect.Right, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = drawColour.BottomRight.Linear, }); FrameStatistics.Add(StatisticsCounterType.Pixels, (long)vertexTriangle.ConservativeArea); }