private Color GetTolerance(Point point, Color expected) { // pixelCenter is factored into this point int x = (int)Math.Floor(point.X - Const.pixelCenterX); int y = (int)Math.Floor(point.Y - Const.pixelCenterY); Color missingPixelTolerance = ColorOperations.ColorFromArgb(0, 0, 0, 0); Color lookupTolerance = ColorOperations.ColorFromArgb(0, 0, 0, 0); for (int j = y; j < y + 2; j++) { for (int i = x; i < x + 2; i++) { Color?current = SafeGetPixel(i, j); if (current.HasValue) { // Keep the max of the diff tolerance and the existing tolerance Color diff = ColorOperations.AbsoluteDifference(current.Value, expected); lookupTolerance = ColorOperations.Max(lookupTolerance, diff); lookupTolerance = ColorOperations.Max(lookupTolerance, toleranceBuffer[i, j]); } else { // increase tolerance by 25% since this pixel's value is unknown missingPixelTolerance = ColorOperations.Add(missingPixelTolerance, ColorOperations.ColorFromArgb(.25, .25, .25, .25)); } } } return(ColorOperations.Add(lookupTolerance, missingPixelTolerance)); }
/// <summary/> virtual public Color FilteredErrorLookup(Point uvLow, Point uvHigh, Color computedColor) { // Mipmapping uses a recursive texture pyramid where each level is 1/4 of the previous one. // This makes it necessary to maintain a 1:1 width:height sample ratio for // error tolerance estimation since that is the area that the mipmapped sample was computed from. double uvWidth = uvHigh.X - uvLow.X; double uvHeight = uvHigh.Y - uvLow.Y; // Force 1:1 aspect ratio on the larger side if (uvWidth > uvHeight) { double vFix = (uvWidth - uvHeight) / 2.0; uvLow.Y -= vFix; uvHigh.Y += vFix; } else { double uFix = (uvHeight - uvWidth) / 2.0; uvLow.X -= uFix; uvHigh.X += uFix; } // Define where we want to have texel centers double texelCenterX = 0.5; double texelCenterY = 0.5; // -/+ texelCenters so that we always at least compare against the raw indices of bilinear filtering int xLo = (int)Math.Floor(uvLow.X * width - (1 - texelCenterX)); int xHi = (int)Math.Ceiling(uvHigh.X * width + texelCenterX); int yLo = (int)Math.Floor(uvLow.Y * height - (1 - texelCenterY)); int yHi = (int)Math.Ceiling(uvHigh.Y * height + texelCenterY); Color tolerance = Color.FromArgb(0x00, 0x00, 0x00, 0x00); for (int y = yLo; y <= yHi; y++) { for (int x = xLo; x <= xHi; x++) { Color current = GetColor(x, y); Color diff = ColorOperations.AbsoluteDifference(current, computedColor); tolerance = ColorOperations.Max(tolerance, diff); } } return(tolerance); }
public override Color FilteredErrorLookup(Point uvLow, Point uvHigh, Color computedColor) { int lowerIndex = 0; int upperIndex = 0; Color computedError; // look for appropriate levels while (upperIndex < levels.Count && mipmapFactor > levels[upperIndex].PixelSize) { upperIndex++; } upperIndex = Math.Min(upperIndex, levels.Count - 1); lowerIndex = Math.Max(upperIndex - 1, 0); // Now perform error lookup on the mipmap levels if (lowerIndex == upperIndex) { // no need to blend, clear case of too near (pure bilinear mag) // or too far (1 pixel last level) computedError = levels[lowerIndex].FilteredErrorLookup(uvLow, uvHigh, computedColor); } else { // Find the colors of the adjacent levels Color lowerLevel = levels[lowerIndex].FilteredErrorLookup(uvLow, uvHigh, computedColor); Color upperLevel = levels[upperIndex].FilteredErrorLookup(uvLow, uvHigh, computedColor); // Find a suitable blend factor double lowerPixelSize = levels[lowerIndex].PixelSize; double upperPixelSize = levels[upperIndex].PixelSize; // upper pixel is 2x larger than lower pixel double blendFactor = (mipmapFactor / lowerPixelSize) - 1; // return the mix computedError = ColorOperations.Blend(upperLevel, lowerLevel, blendFactor); } // Compare against bilinear error Color baseError = base.FilteredErrorLookup(uvLow, uvHigh, computedColor); computedError = ColorOperations.Max(computedError, baseError); return(computedError); }
private void ApplyDropShadow(DropShadowBitmapEffect effect, Rect effectInput) { RenderBuffer clone = Clone(); clone.boundsOverride = effectInput; double depth = MathEx.ConvertToAbsolutePixels(effect.ShadowDepth); double angle = MathEx.ToRadians(effect.Direction); Vector pixelOffset = new Vector(depth * Math.Cos(angle), depth * Math.Sin(-angle)); Color effectColor = effect.Color; effectColor = ColorOperations.ScaleAlpha(effectColor, effect.Opacity); double blurRadius = 1.0 + (effect.Softness * 9.0); int xEnd = (int)effectInput.Right; int yEnd = (int)effectInput.Bottom; for (int y = (int)effectInput.Y; y < yEnd; y++) { for (int x = (int)effectInput.X; x < xEnd; x++) { Point point = new Point(x + Const.pixelCenterX, y + Const.pixelCenterY) - pixelOffset; Color?color = clone.GetPixelSample(point, blurRadius); if (color.HasValue) { double opacity = ColorOperations.ByteToDouble(color.Value.A); Color shadow = ColorOperations.ScaleAlpha(effectColor, opacity); shadow = ColorOperations.PreMultiplyColor(shadow); frameBuffer[x, y] = ColorOperations.PreMultipliedAlphaBlend(clone.frameBuffer[x, y], shadow); if (frameBuffer[x, y] != clone.frameBuffer[x, y]) { // Transfer the tolerance (silhouette, etc) over too Color tolerance = clone.GetToleranceSample(point, blurRadius); toleranceBuffer[x, y] = ColorOperations.Max(tolerance, clone.toleranceBuffer[x, y]); } } } } }