/// <summary>Runs this elimination technique over the supplied puzzle state and previously computed possible numbers.</summary> /// <param name="state">The puzzle state.</param> /// <param name="possibleNumbers">The previously computed possible numbers.</param> /// <param name="numberOfChanges">The number of changes made by this elimination technique.</param> /// <returns>Whether more changes may be possible based on changes made during this execution.</returns> internal override bool Execute( PuzzleState state, bool exitEarlyWhenSoleFound, FastBitArray[][] possibleNumbers, out int numberOfChanges, out bool exitedEarly) { numberOfChanges = 0; exitedEarly = false; // Eliminate impossible numbers based on numbers already set in the grid for (int i = 0; i < state.GridSize; i++) { for (int j = 0; j < state.GridSize; j++) { // If this cell has a value, we use it to eliminate numbers in other cells if (state[i, j].HasValue) { byte valueToEliminate = state[i, j].Value; // eliminate numbers in same row for (int y = 0; y < state.GridSize; y++) { if (possibleNumbers[i][y][valueToEliminate]) { numberOfChanges++; possibleNumbers[i][y][valueToEliminate] = false; } } // eliminate numbers in same column for (int x = 0; x < state.GridSize; x++) { if (possibleNumbers[x][j][valueToEliminate]) { numberOfChanges++; possibleNumbers[x][j][valueToEliminate] = false; } } // eliminate numbers in same box int boxStartX = (i / state.BoxSize) * state.BoxSize; for (int x = boxStartX; x < boxStartX + state.BoxSize; x++) { int boxStartY = (j / state.BoxSize) * state.BoxSize; for (int y = boxStartY; y < boxStartY + state.BoxSize; y++) { if (possibleNumbers[x][y][valueToEliminate]) { numberOfChanges++; possibleNumbers[x][y][valueToEliminate] = false; } } } } } } return false; }
/// <summary>Runs this elimination technique over the supplied puzzle state and previously computed possible numbers.</summary> /// <param name="state">The puzzle state.</param> /// <param name="possibleNumbers">The previously computed possible numbers.</param> /// <param name="numberOfChanges">The number of changes made by this elimination technique.</param> /// <returns>Whether more changes may be possible based on changes made during this execution.</returns> internal override bool Execute( PuzzleState state, bool exitEarlyWhenSoleFound, FastBitArray[][] possibleNumbers, out int numberOfChanges, out bool exitedEarly) { numberOfChanges = 0; exitedEarly = false; // Check each row to see if it contains the start of an xwing for(int row=0; row<state.GridSize; row++) { int count = 0; // used to find the two first-row members of the x-wing int [] foundColumns = new int[2]; // used to store the two first-row members of the x-wing // We'll be checking all numbers to see whether they're in an x-wing for(int n=0; n<state.GridSize; n++) { // Now look at every column in the row, and find the occurrences of the number. // For it to be a valid x-wing, it must have two and only two of the given number as a possibility. for(int column=0; column<state.GridSize; column++) { if (possibleNumbers[row][column][n] || (state[row,column].HasValue && state[row,column].Value == n)) { count++; if (count <= foundColumns.Length) foundColumns[count-1] = column; else break; } } // Assuming we found a row that has two and only two cells with the number as a possibility if (count == 2) { // Look for another row that has the same property for(int subRow=row+1; subRow<state.GridSize; subRow++) { bool validXwingFound = true; for(int subColumn=0; subColumn<state.GridSize && validXwingFound; subColumn++) { bool isMatchingColumn = (subColumn == foundColumns[0] || subColumn == foundColumns[1]); bool hasPossibleNumber = possibleNumbers[subRow][subColumn][n] || (state[subRow,subColumn].HasValue && state[subRow,subColumn].Value == n); if ((hasPossibleNumber && !isMatchingColumn) || (!hasPossibleNumber && isMatchingColumn)) validXwingFound = false; } // If another row is found that has two and only two cells with the number // as a possibility, and if those two cells are in the same two columns // as the original row, woo hoo, we've got an x-wing, and we can eliminate // that number from every other cell in the columns containing the numbers. if (validXwingFound) { for(int elimRow=0; elimRow<state.GridSize; elimRow++) { if (elimRow != row && elimRow != subRow) { for(int locationNum=0; locationNum<2; locationNum++) { if (possibleNumbers[elimRow][foundColumns[locationNum]][n]) { possibleNumbers[elimRow][foundColumns[locationNum]][n] = false; numberOfChanges++; if (exitEarlyWhenSoleFound && possibleNumbers[elimRow][foundColumns[locationNum]].CountSet == 1) { exitedEarly = true; return false; } } } } } break; } } } } } return numberOfChanges != 0; }
/// <summary>Runs this elimination technique over the supplied puzzle state and previously computed possible numbers.</summary> /// <param name="state">The puzzle state.</param> /// <param name="exitEarlyWhenSoleFound">Whether the method can exit early when a cell with only one possible number is found.</param> /// <param name="possibleNumbers">The previously computed possible numbers.</param> /// <param name="numberOfChanges">The number of changes made by this elimination technique.</param> /// <param name="exitedEarly">Whether the method exited early due to a cell with only one value being found.</param> /// <returns>Whether more changes may be possible based on changes made during this execution.</returns> internal override bool Execute( PuzzleState state, bool exitEarlyWhenSoleFound, FastBitArray[][] possibleNumbers, out int numberOfChanges, out bool exitedEarly) { numberOfChanges = 0; exitedEarly = false; byte gridSize = state.GridSize; byte boxSize = state.BoxSize; // For each number that can exist in the puzzle (0-8, etc.) for (byte n = 0; n < gridSize; n++) { // For each row, if number only exists as a possibility in one cell, set it. for (byte x = 0; x < gridSize; x++) { int? seenIndex = null; for (byte y = 0; y < gridSize; y++) { if (possibleNumbers[x][y][n]) { // If this is the first time we're seeing the number, remember // where we're seeing it if (!seenIndex.HasValue) seenIndex = y; // We've seen this number before, so move on else { seenIndex = null; break; } } } if (seenIndex.HasValue && possibleNumbers[x][seenIndex.Value].CountSet > 1) { possibleNumbers[x][seenIndex.Value].SetAll(false); possibleNumbers[x][seenIndex.Value][n] = true; numberOfChanges++; if (exitEarlyWhenSoleFound) { exitedEarly = true; return false; } } } // For each column, if number only exists as a possibility in one cell, set it. // Same basic logic as above. for (byte y = 0; y < gridSize; y++) { int? seenIndex = null; for (byte x = 0; x < gridSize; x++) { if (possibleNumbers[x][y][n]) { if (!seenIndex.HasValue) seenIndex = x; else { seenIndex = null; break; } } } if (seenIndex.HasValue && possibleNumbers[seenIndex.Value][y].CountSet > 1) { possibleNumbers[seenIndex.Value][y].SetAll(false); possibleNumbers[seenIndex.Value][y][n] = true; numberOfChanges++; if (exitEarlyWhenSoleFound) { exitedEarly = true; return false; } } } // For each grid, if number only exists as a possibility in one cell, set it. // Same basic logic as above. for (byte gridNum = 0; gridNum < gridSize; gridNum++) { byte gridX = (byte)(gridNum % boxSize); byte gridY = (byte)(gridNum / boxSize); byte startX = (byte)(gridX * boxSize); byte startY = (byte)(gridY * boxSize); bool canEliminate = true; Point? seenIndex = null; for (byte x = startX; x < startX + boxSize && canEliminate; x++) { for (byte y = startY; y < startY + boxSize; y++) { if (possibleNumbers[x][y][n]) { if (!seenIndex.HasValue) seenIndex = new Point(x, y); else { canEliminate = false; seenIndex = null; break; } } } } if (seenIndex.HasValue && canEliminate && possibleNumbers[seenIndex.Value.X][seenIndex.Value.Y].CountSet > 1) { possibleNumbers[seenIndex.Value.X][seenIndex.Value.Y].SetAll(false); possibleNumbers[seenIndex.Value.X][seenIndex.Value.Y][n] = true; numberOfChanges++; if (exitEarlyWhenSoleFound) { exitedEarly = true; return false; } } } } return numberOfChanges != 0; }
/// <summary>Analyzes the state of the puzzle to determine whether it is a solution or not.</summary> /// <returns>The status of the puzzle.</returns> private PuzzleStatus AnalyzeSolutionStatus() { // Need a way of keeping track of what numbers have been used (in each row, column, box, etc.) // A bit array is a great way to do this, where each bit corresponds to a true/false value // as to whether a number was already used in a particular scenario. FastBitArray numbersUsed = new FastBitArray(_gridSize); // Make sure every column contains the right numbers. It's ok if a column has holes // as long as those cells have possibilities, in which case it's a puzzle in progress. // However, two numbers can't be used in the same column, even if there are holes. for (int i = 0; i < _gridSize; i++) { numbersUsed.SetAll(false); for (int j = 0; j < _gridSize; j++) { if (_grid[i, j].HasValue) { int value = _grid[i, j].Value; if (numbersUsed[value]) { return(PuzzleStatus.CannotBeSolved); } numbersUsed[value] = true; } } } // Same for rows for (int j = 0; j < _gridSize; j++) { numbersUsed.SetAll(false); for (int i = 0; i < _gridSize; i++) { if (_grid[i, j].HasValue) { int value = _grid[i, j].Value; if (numbersUsed[value]) { return(PuzzleStatus.CannotBeSolved); } numbersUsed[value] = true; } } } // Same for boxes for (int boxNumber = 0; boxNumber < _gridSize; boxNumber++) { numbersUsed.SetAll(false); int boxStartX = (boxNumber / _boxSize) * _boxSize; for (int x = boxStartX; x < boxStartX + _boxSize; x++) { int boxStartY = (boxNumber % _boxSize) * _boxSize; for (int y = boxStartY; y < boxStartY + _boxSize; y++) { if (_grid[x, y].HasValue) { int value = _grid[x, y].Value; if (numbersUsed[value]) { return(PuzzleStatus.CannotBeSolved); } numbersUsed[value] = true; } } } } // Now figure out whether this is a solved puzzle or a work in progress // based on whether there are any holes for (int i = 0; i < _gridSize; i++) { for (int j = 0; j < _gridSize; j++) { if (!_grid[i, j].HasValue) { return(PuzzleStatus.InProgress); } } } // If we made it this far, this state is a valid solution! Woo hoo! return(PuzzleStatus.Solved); }
/// <summary>Performs hidden subset elimination on one dimension of possible numbers.</summary> /// <param name="possibleNumbers">The row/column/box to analyze.</param> /// <returns>The number of changes that were made to the possible numbers.</returns> private int EliminateHiddenSubsets(FastBitArray [] possibleNumbers, bool exitEarlyWhenSoleFound, out bool exitedEarly) { int changesMade = 0; exitedEarly = false; int numLocations; int [] foundLocations = _foundLocations; // optimization, rather than allocating on each call // We'll look starting with each cell in the row/column/box for (int i = 0; i < possibleNumbers.Length; i++) { // Only look at the cell if it has at least subsetSize values set, // otherwise it can't be part of a hidden subset int numPossible = possibleNumbers[i].CountSet; if (numPossible >= _subsetSize) { // For each combination foreach (int [] combination in CreateCombinations(_subsetSize, possibleNumbers[i].GetSetBits())) { // Find other cells that contain that same combination, // but only up to the subset size numLocations = 0; foundLocations[numLocations++] = i; for (int j = i + 1; j < possibleNumbers.Length && numLocations < foundLocations.Length; j++) { if (AllAreSet(combination, possibleNumbers[j])) { foundLocations[numLocations++] = j; } } if (numLocations == foundLocations.Length) { bool isValidHidden = true; // Make sure that none of the numbers appear in any other cell for (int j = 0; j < possibleNumbers.Length && isValidHidden; j++) { bool isFoundLocation = Array.BinarySearch(foundLocations, j) >= 0; if (!isFoundLocation && AnyAreSet(combination, possibleNumbers[j])) { isValidHidden = false; break; } } // If this is a valid hidden subset, eliminate all other numbers // from each cell in the subset if (isValidHidden) { foreach (int foundLoc in foundLocations) { FastBitArray possibleNumbersForLoc = possibleNumbers[foundLoc]; foreach (int n in possibleNumbersForLoc.GetSetBits()) { if (Array.BinarySearch(combination, n) < 0) { possibleNumbersForLoc[n] = false; changesMade++; } } if (exitEarlyWhenSoleFound && possibleNumbersForLoc.CountSet == 1) { exitedEarly = true; return(changesMade); } } break; } } } } } return(changesMade); }
public static AssetSprite.CollisionMaskInfo GetInfoForSprite(ProjectFile pf, GMSprite spr, out List <Bitmap> bitmaps, bool suggestPrecise = false) { bitmaps = new List <Bitmap>(spr.TextureItems.Count); var info = new AssetSprite.CollisionMaskInfo { Mode = (MaskMode)spr.BBoxMode }; if (spr.SepMasks == GMSprite.SepMaskType.AxisAlignedRect) { info.Type = MaskType.Rectangle; } else if (spr.SepMasks == GMSprite.SepMaskType.RotatedRect) { info.Type = MaskType.RectangleWithRotation; } // Some basic conditions to bail if (spr.CollisionMasks.Count != 1 && spr.CollisionMasks.Count != spr.TextureItems.Count) { return(info); } if (spr.CollisionMasks.Count == 0) { return(info); } // Get bitmaps from frames bitmaps = GetBitmaps(pf, spr.Width, spr.Height, spr.TextureItems); List <BitmapData> bitmapData = new List <BitmapData>(bitmaps.Count); foreach (var item in bitmaps) { bitmapData.Add(item.BasicLockBits()); } int boundLeft = Math.Clamp(spr.MarginLeft, 0, spr.Width), boundRight = Math.Clamp(spr.MarginRight, 0, spr.Width - 1), boundTop = Math.Clamp(spr.MarginTop, 0, spr.Height), boundBottom = Math.Clamp(spr.MarginBottom, 0, spr.Height - 1); switch (spr.SepMasks) { case GMSprite.SepMaskType.AxisAlignedRect: case GMSprite.SepMaskType.RotatedRect: switch (info.Mode) { case MaskMode.Automatic: // Scan for the lowest alpha value in the bounding box // When comparing each pixel, compare to the one in that spot with the highest alpha in every frame bool foundNonzero = false; byte lowest = 0; byte highest = 0; int stride = ((spr.Width + 7) / 8) * 8; FastBitArray mask = new FastBitArray(spr.CollisionMasks[0].Memory.Span); int strideFactor = boundTop * stride; for (int y = boundTop; y <= boundBottom; y++) { for (int x = boundLeft; x <= boundRight; x++) { if (mask.GetReverse(x + strideFactor)) { byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y); if (highestAlpha > highest) { highest = highestAlpha; } if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest)) { lowest = highestAlpha; foundNonzero = true; } } } strideFactor += stride; } if (foundNonzero) { if (lowest == highest) { lowest = 0; // Could be anything } else { --lowest; } } info.AlphaTolerance = lowest; break; case MaskMode.Manual: info.Left = spr.MarginLeft; info.Right = spr.MarginRight; info.Top = spr.MarginTop; info.Bottom = spr.MarginBottom; break; } break; case GMSprite.SepMaskType.Precise: { int stride = ((spr.Width + 7) / 8) * 8; bool foundNonzero = false; byte lowest = 0; byte highest = 0; if (spr.CollisionMasks.Count > 1 && spr.CollisionMasks.Count == spr.TextureItems.Count) { info.Type = MaskType.PrecisePerFrame; unsafe { for (int i = 0; i < spr.CollisionMasks.Count; i++) { BitmapData item = bitmapData[i]; FastBitArray mask = new FastBitArray(spr.CollisionMasks[i].Memory.Span); int strideFactor = boundTop * stride; for (int y = boundTop; y <= boundBottom; y++) { for (int x = boundLeft; x <= boundRight; x++) { if (mask.GetReverse(x + strideFactor)) { byte val = *((byte *)item.Scan0 + (x * 4) + (y * item.Stride) + 3); if (val > highest) { highest = val; } if (val != 0 && (!foundNonzero || val < lowest)) { lowest = val; foundNonzero = true; } } } strideFactor += stride; } } } } else { info.Type = MaskType.Precise; // Scan for highest alpha, as well as diamond/ellipses FastBitArray mask = new FastBitArray(spr.CollisionMasks[0].Memory.Span); bool isDiamond = true, isEllipse = true; float centerX = ((spr.MarginLeft + spr.MarginRight) / 2); float centerY = ((spr.MarginTop + spr.MarginBottom) / 2); float radiusX = centerX - spr.MarginLeft + 0.5f; float radiusY = centerY - spr.MarginTop + 0.5f; int strideFactor = boundTop * stride; if (!suggestPrecise && radiusX > 0f && radiusY > 0f) { for (int y = boundTop; y <= boundBottom; y++) { for (int x = boundLeft; x <= boundRight; x++) { float normalX = (x - centerX) / radiusX; float normalY = (y - centerY) / radiusY; bool inDiamond = Math.Abs(normalX) + Math.Abs(normalY) <= 1f; bool inEllipse = Math.Pow(normalX, 2.0d) + Math.Pow(normalY, 2.0d) <= 1.0d; if (mask.GetReverse(x + strideFactor)) { isDiamond &= inDiamond; isEllipse &= inEllipse; byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y); if (highestAlpha > highest) { highest = highestAlpha; } if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest)) { lowest = highestAlpha; foundNonzero = true; } } // Can't eliminate based on this, they can be split into pieces with multiple frames //else //{ // isDiamond &= !inDiamond; // isEllipse &= !inEllipse; //} } strideFactor += stride; } } else { // Version without diamond/ellipse checks isDiamond = false; isEllipse = false; for (int y = boundTop; y <= boundBottom; y++) { for (int x = boundLeft; x <= boundRight; x++) { if (mask.GetReverse(x + strideFactor)) { byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y); if (highestAlpha > highest) { highest = highestAlpha; } if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest)) { lowest = highestAlpha; foundNonzero = true; } } } strideFactor += stride; } } if (isDiamond) { info.Type = MaskType.Diamond; } else if (isEllipse) { info.Type = MaskType.Ellipse; } } if (info.Mode == MaskMode.Manual || (info.Mode == MaskMode.Automatic && info.Type != MaskType.Precise && info.Type != MaskType.PrecisePerFrame)) { info.Left = spr.MarginLeft; info.Right = spr.MarginRight; info.Top = spr.MarginTop; info.Bottom = spr.MarginBottom; } if (info.Mode == MaskMode.Automatic || info.Type == MaskType.Precise || (info.Mode == MaskMode.Manual && info.Type == MaskType.PrecisePerFrame)) { if (foundNonzero) { if (lowest == highest) { lowest = 0; // Could be anything } else { --lowest; } } info.AlphaTolerance = lowest; } } break; } for (int i = 0; i < bitmaps.Count; i++) { bitmaps[i].UnlockBits(bitmapData[i]); } return(info); }
public static List <BufferRegion> GetMasksForSprite(ProjectFile pf, AssetSprite spr, out Rect maskbbox, List <Bitmap> bitmaps = null) { if (bitmaps == null) { bitmaps = GetBitmaps(pf, spr.Width, spr.Height, spr.TextureItems); } if (bitmaps.Count == 0) { maskbbox = new Rect(0, 0, 0, 0); return(new List <BufferRegion>()); } var info = spr.CollisionMask; if (info.Left == null || info.Top == null || info.Right == null || info.Bottom == null) { maskbbox = new Rect(spr.Width - 1, spr.Height - 1, 0, 0); } else { maskbbox = null; } if (spr.CollisionMask.Type == MaskType.PrecisePerFrame) { // Get masks for individual frames List <BufferRegion> res = new List <BufferRegion>(bitmaps.Count); for (int i = 0; i < bitmaps.Count; i++) { res.Add(new BufferRegion(GetMaskForBitmap(bitmaps[i], spr, ref maskbbox).ToByteArray())); } if (maskbbox != null) { maskbbox.Left = Math.Max(0, maskbbox.Left); maskbbox.Top = Math.Max(0, maskbbox.Top); maskbbox.Right = Math.Min(spr.Width - 1, maskbbox.Right); maskbbox.Bottom = Math.Min(spr.Height - 1, maskbbox.Bottom); } return(res); } else { // Get the mask for the first frame, then add following frames FastBitArray mask = GetMaskForBitmap(bitmaps[0], spr, ref maskbbox); for (int i = 1; i < bitmaps.Count; i++) { GetMaskForBitmap(bitmaps[i], spr, ref maskbbox, mask); } if (maskbbox != null) { maskbbox.Left = Math.Max(0, maskbbox.Left); maskbbox.Top = Math.Max(0, maskbbox.Top); maskbbox.Right = Math.Min(spr.Width - 1, maskbbox.Right); maskbbox.Bottom = Math.Min(spr.Height - 1, maskbbox.Bottom); } return(new List <BufferRegion> { new BufferRegion(mask.ToByteArray()) }); } }
public static unsafe FastBitArray GetMaskForBitmap(Bitmap bmp, AssetSprite spr, ref Rect maskbbox, FastBitArray existingMask = null) { int stride = ((spr.Width + 7) / 8) * 8; FastBitArray res = existingMask ?? new FastBitArray(stride * spr.Height); Rect bbox = GetBBoxForBitmap(bmp, spr); var info = spr.CollisionMask; int sprLeft, sprTop, sprRight, sprBottom; // Word of note: There's a lot of copies of nearly the same code here. This is to reduce the number of conditions. int strideFactor = bbox.Top * stride; switch (info.Type) { case MaskType.Rectangle: case MaskType.RectangleWithRotation: if (maskbbox != null) { for (int y = bbox.Top; y <= bbox.Bottom; y++) { for (int x = bbox.Left; x <= bbox.Right; x++) { res.SetTrueReverse(x + strideFactor); if (x < maskbbox.Left) { maskbbox.Left = x; } if (y < maskbbox.Top) { maskbbox.Top = y; } if (x > maskbbox.Right) { maskbbox.Right = x; } if (y > maskbbox.Bottom) { maskbbox.Bottom = y; } } strideFactor += stride; } } else { for (int y = bbox.Top; y <= bbox.Bottom; y++) { for (int x = bbox.Left; x <= bbox.Right; x++) { res.SetTrueReverse(x + strideFactor); } strideFactor += stride; } } break; case MaskType.Precise: case MaskType.PrecisePerFrame: int tolerance = info.AlphaTolerance ?? 0; var data = bmp.BasicLockBits(); unsafe { byte *ptr = (byte *)data.Scan0; if (maskbbox != null) { for (int y = bbox.Top; y <= bbox.Bottom; y++) { for (int x = bbox.Left; x <= bbox.Right; x++) { if (*(ptr + (x * 4) + (y * data.Stride) + 3) > tolerance) { res.SetTrueReverse(x + strideFactor); if (x < maskbbox.Left) { maskbbox.Left = x; } if (y < maskbbox.Top) { maskbbox.Top = y; } if (x > maskbbox.Right) { maskbbox.Right = x; } if (y > maskbbox.Bottom) { maskbbox.Bottom = y; } } } strideFactor += stride; } } else { for (int y = bbox.Top; y <= bbox.Bottom; y++) { for (int x = bbox.Left; x <= bbox.Right; x++) { if (*(ptr + (x * 4) + (y * data.Stride) + 3) > tolerance) { res.SetTrueReverse(x + strideFactor); } } strideFactor += stride; } } } bmp.UnlockBits(data); break; case MaskType.Diamond: { if (info.Mode == MaskMode.FullImage) { sprLeft = 0; sprTop = 0; sprRight = spr.Width - 1; sprBottom = spr.Height - 1; } else { sprLeft = (int)info.Left; sprTop = (int)info.Top; sprRight = (int)info.Right; sprBottom = (int)info.Bottom; } float centerX = (sprLeft + sprRight) / 2; float centerY = (sprTop + sprBottom) / 2; float radiusX = centerX - sprLeft + 0.5f; float radiusY = centerY - sprTop + 0.5f; if (radiusX <= 0 || radiusY <= 0) { break; } if (maskbbox != null) { for (int y = bbox.Top; y <= bbox.Bottom; y++) { for (int x = bbox.Left; x <= bbox.Right; x++) { float normalX = (x - centerX) / radiusX; float normalY = (y - centerY) / radiusY; if (Math.Abs(normalX) + Math.Abs(normalY) <= 1f) { res.SetTrueReverse(x + strideFactor); if (x < maskbbox.Left) { maskbbox.Left = x; } if (y < maskbbox.Top) { maskbbox.Top = y; } if (x > maskbbox.Right) { maskbbox.Right = x; } if (y > maskbbox.Bottom) { maskbbox.Bottom = y; } } } strideFactor += stride; } } else { for (int y = bbox.Top; y <= bbox.Bottom; y++) { for (int x = bbox.Left; x <= bbox.Right; x++) { float normalX = (x - centerX) / radiusX; float normalY = (y - centerY) / radiusY; if (Math.Abs(normalX) + Math.Abs(normalY) <= 1f) { res.SetTrueReverse(x + strideFactor); } } strideFactor += stride; } } break; } case MaskType.Ellipse: { if (info.Mode == MaskMode.FullImage) { sprLeft = 0; sprTop = 0; sprRight = spr.Width - 1; sprBottom = spr.Height - 1; } else { sprLeft = (int)info.Left; sprTop = (int)info.Top; sprRight = (int)info.Right; sprBottom = (int)info.Bottom; } float centerX = (sprLeft + sprRight) / 2; float centerY = (sprTop + sprBottom) / 2; float radiusX = centerX - sprLeft + 0.5f; float radiusY = centerY - sprTop + 0.5f; if (radiusX <= 0 || radiusY <= 0) { break; } if (maskbbox != null) { for (int y = bbox.Top; y <= bbox.Bottom; y++) { for (int x = bbox.Left; x <= bbox.Right; x++) { float normalX = (x - centerX) / radiusX; float normalY = (y - centerY) / radiusY; if (Math.Pow(normalX, 2.0d) + Math.Pow(normalY, 2.0d) <= 1.0d) { res.SetTrueReverse(x + strideFactor); if (x < maskbbox.Left) { maskbbox.Left = x; } if (y < maskbbox.Top) { maskbbox.Top = y; } if (x > maskbbox.Right) { maskbbox.Right = x; } if (y > maskbbox.Bottom) { maskbbox.Bottom = y; } } } strideFactor += stride; } } else { for (int y = bbox.Top; y <= bbox.Bottom; y++) { for (int x = bbox.Left; x <= bbox.Right; x++) { float normalX = (x - centerX) / radiusX; float normalY = (y - centerY) / radiusY; if (Math.Pow(normalX, 2.0d) + Math.Pow(normalY, 2.0d) <= 1.0d) { res.SetTrueReverse(x + strideFactor); } } strideFactor += stride; } } break; } } return(res); }