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> /// Produce a Difference image from a screen capture and a RenderBuffer. For every pixel, if it is an exact match or /// if the difference is within the provided tolerance, the pixel is marked as black. Otherwise the diff value is used. /// If the captured image is smaller than the expected image, throw an exception and refuse to compare them. /// Otherwise, compare the expected image with the matching region (the upper left corner) of the rendered image. /// We'll do the comparison by x,y coordinates and not pointer math (ie: y*width +x) to ensure correct matching. /// </summary> /// <returns>A new Render buffer with the Diff image on the framebuffer and a color coded image on the tbuffer.</returns> public static RenderBuffer ComputeDifference(Color[,] captured, RenderBuffer expected) { if (expected.Width > captured.GetLength(0) || expected.Height > captured.GetLength(1)) { throw new ApplicationException(exceptionCapturedRenderedEqual); } RenderBuffer result = new RenderBuffer(expected.Width, expected.Height); // We want to write to this directly, set z-test to always write ... result.DepthTestFunction = DepthTestFunction.Always; // We want to ignore any potential z-tolerance as well ... for (int y = 0; y < result.Height; y++) { for (int x = 0; x < result.Width; x++) { // Ignore alpha differences. Color diff = ColorOperations.AbsoluteDifference(expected.FrameBuffer[x, y], captured[x, y]); diff.A = 0xff; result.FrameBuffer[x, y] = diff; // Make perfect matches black if (ColorOperations.AreWithinTolerance(captured[x, y], expected.FrameBuffer[x, y], Colors.Black)) { result.ToleranceBuffer[x, y] = Colors.Black; result.FrameBuffer[x, y] = Colors.Black; } // Treat within tolerance as separate case else if (ColorOperations.AreWithinTolerance(captured[x, y], expected.FrameBuffer[x, y], expected.ToleranceBuffer[x, y])) { result.ToleranceBuffer[x, y] = codedColor[0]; result.FrameBuffer[x, y] = Colors.Black; } // Otherwise do color coding else { for (int i = 1; i < codedColor.Length; i++) { if (ColorOperations.AreWithinTolerance( captured[x, y], expected.FrameBuffer[x, y], ColorOperations.Add(toleranceThreshold[i], expected.ToleranceBuffer[x, y]))) { result.ToleranceBuffer[x, y] = codedColor[i]; break; } } } } } return(result); }
/// <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); }