private void checkForCorrectness() { Quad?scaling1LastQuad = null; Quad?scaling2LastQuad = null; Quad?scalingTargetLastQuad = null; AddUntilStep("ensure dimensions don't change", () => { if (scaling1LastQuad.HasValue && scaling2LastQuad.HasValue) { // check inter-frame changes to make sure they match expectations. Assert.That(scaling1.ScreenSpaceDrawQuad.AlmostEquals(scaling1LastQuad.Value), Is.True); Assert.That(scaling2.ScreenSpaceDrawQuad.AlmostEquals(scaling2LastQuad.Value), Is.True); } scaling1LastQuad = scaling1.ScreenSpaceDrawQuad; scaling2LastQuad = scaling2.ScreenSpaceDrawQuad; // wait for scaling to stop. bool scalingFinished = scalingTargetLastQuad.HasValue && scaleTarget.ScreenSpaceDrawQuad.AlmostEquals(scalingTargetLastQuad.Value); scalingTargetLastQuad = scaleTarget.ScreenSpaceDrawQuad; return(scalingFinished); }); }
public override void ApplyState() { base.ApplyState(); if (!Source.Masking && (Source.BorderThickness != 0.0f || edgeEffect.Type != EdgeEffectType.None)) { throw new InvalidOperationException("Can not have border effects/edge effects if masking is disabled."); } Vector3 scale = DrawInfo.MatrixInverse.ExtractScale(); maskingInfo = !Source.Masking ? (MaskingInfo?)null : new MaskingInfo { ScreenSpaceAABB = Source.ScreenSpaceDrawQuad.AABB, MaskingRect = Source.DrawRectangle, ToMaskingSpace = DrawInfo.MatrixInverse, CornerRadius = Source.CornerRadius, BorderThickness = Source.BorderThickness, BorderColour = Source.BorderColour, // We are setting the linear blend range to the approximate size of a _pixel_ here. // This results in the optimal trade-off between crispness and smoothness of the // edges of the masked region according to sampling theory. BlendRange = Source.MaskingSmoothness * (scale.X + scale.Y) / 2, AlphaExponent = 1, }; edgeEffect = Source.EdgeEffect; screenSpaceMaskingQuad = null; Shader = Source.Shader; forceLocalVertexBatch = Source.ForceLocalVertexBatch; }
private void drawEdgeEffect() { if (maskingInfo == null || edgeEffect.Type == EdgeEffectType.None || edgeEffect.Radius <= 0.0f || edgeEffect.Colour.Linear.A <= 0) { 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 = maskingInfo.Value.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.EdgeOffset = edgeEffect.Offset; edgeEffectMaskingInfo.Hollow = edgeEffect.Hollow; edgeEffectMaskingInfo.HollowCornerRadius = maskingInfo.Value.CornerRadius + edgeEffect.Radius; 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(DrawColourInfo.Colour.TopLeft.Linear.A); colour.BottomLeft.MultiplyAlpha(DrawColourInfo.Colour.BottomLeft.Linear.A); colour.TopRight.MultiplyAlpha(DrawColourInfo.Colour.TopRight.Linear.A); colour.BottomRight.MultiplyAlpha(DrawColourInfo.Colour.BottomRight.Linear.A); DrawQuad( Texture.WhitePixel, 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(); }
/// <summary>Debug print a <see cref="Quad"/>.</summary> public static string Print(Quad?quad) { if (quad is null) { return("null"); } return($"{Print(quad.Subject, false)} " + $"{Print(quad.Predicate, false)} " + $"{Print(quad.Object, false)} ."); }
public override void ApplyState() { base.ApplyState(); if (!Source.Masking && (Source.BorderThickness != 0.0f || Source.EdgeEffect.Type != EdgeEffectType.None)) { throw new InvalidOperationException("Can not have border effects/edge effects if masking is disabled."); } Vector3 scale = DrawInfo.MatrixInverse.ExtractScale(); float blendRange = Source.MaskingSmoothness * (scale.X + scale.Y) / 2; // Calculate a shrunk rectangle which is free from corner radius/smoothing/border effects float shrinkage = Source.CornerRadius - Source.CornerRadius * cos_45 + blendRange + Source.borderThickness; // Normalise to handle negative sizes, and clamp the shrinkage to prevent size from going negative. RectangleF shrunkDrawRectangle = Source.DrawRectangle.Normalize(); shrunkDrawRectangle = shrunkDrawRectangle.Shrink(new Vector2(Math.Min(shrunkDrawRectangle.Width / 2, shrinkage), Math.Min(shrunkDrawRectangle.Height / 2, shrinkage))); maskingInfo = !Source.Masking ? (MaskingInfo?)null : new MaskingInfo { ScreenSpaceAABB = Source.ScreenSpaceDrawQuad.AABB, MaskingRect = Source.DrawRectangle.Normalize(), ConservativeScreenSpaceQuad = Quad.FromRectangle(shrunkDrawRectangle) * DrawInfo.Matrix, ToMaskingSpace = DrawInfo.MatrixInverse, CornerRadius = Source.effectiveCornerRadius, CornerExponent = Source.CornerExponent, BorderThickness = Source.BorderThickness, BorderColour = Source.BorderColour, // We are setting the linear blend range to the approximate size of a _pixel_ here. // This results in the optimal trade-off between crispness and smoothness of the // edges of the masked region according to sampling theory. BlendRange = blendRange, AlphaExponent = 1, }; edgeEffect = Source.EdgeEffect; screenSpaceMaskingQuad = null; Shader = Source.Shader; forceLocalVertexBatch = Source.ForceLocalVertexBatch; sourceChildrenCount = Source.internalChildren.Count; }
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; 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); Shader.Unbind(); GLWrapper.PopMaskingInfo(); }
private void drawEdgeEffect() { if (MaskingInfo == null || EdgeEffect.Type == EdgeEffectType.None || EdgeEffect.Radius <= 0.0f || EdgeEffect.Colour.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; edgeEffectMaskingInfo.BlendRange = EdgeEffect.Radius; GLWrapper.PushMaskingInfo(edgeEffectMaskingInfo); GLWrapper.SetBlend(new BlendingInfo(EdgeEffect.Type == EdgeEffectType.Glow ? BlendingMode.Additive : BlendingMode.Mixture)); Shader.Bind(); Color4 colour = EdgeEffect.Colour; colour.A *= DrawInfo.Colour.A; Texture.WhitePixel.Draw(ScreenSpaceMaskingQuad.Value, colour); Shader.Unbind(); GLWrapper.PopMaskingInfo(); }
public void Process(Interval <int> interval, ITextString match) { //var matrix = page.RotateMatrix; // Defining the highlight box of the text pattern match... IList <Quad> highlightQuads = new List <Quad>(); { /* * NOTE: A text pattern match may be split across multiple contiguous lines, * so we have to define a distinct highlight box for each text chunk. */ Quad?textQuad = null; foreach (TextChar textChar in match.TextChars) { var textCharQuad = textChar.Quad; if (!textQuad.HasValue) { textQuad = textCharQuad; } else { if (textCharQuad.Top > textQuad.Value.Bottom) { highlightQuads.Add(textQuad.Value); textQuad = textCharQuad; } else { textQuad = Quad.Union(textQuad.Value, textCharQuad); } } } highlightQuads.Add(textQuad.Value); } // Highlight the text pattern match! new TextMarkup(page, highlightQuads, null, TextMarkupType.Highlight); }
public void Invalidate() { text = null; quad = null; }
/// <inheritdoc /> public bool Equals(Quad?x, Quad?y) => x?.Subject == y?.Subject;