public PRRect Insert(int width, int height, ChoiceHeuristic method) { PRRect newNode = new PRRect(); int score1 = 0; // Unused in this function. We don't need to know the score after finding the position. int score2 = 0; switch(method) { case ChoiceHeuristic.ShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break; case ChoiceHeuristic.BottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break; case ChoiceHeuristic.ContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1); break; case ChoiceHeuristic.LongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break; case ChoiceHeuristic.AreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break; } if (newNode.height == 0) return newNode; int numRectanglesToProcess = freeRectangles.Count; for(int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(freeRectangles[i], ref newNode)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); usedRectangles.Add(newNode); return newNode; }
PRRect ScoreRect(int width, int height, ChoiceHeuristic method, ref int score1, ref int score2) { PRRect newNode = new PRRect(); score1 = int.MaxValue; score2 = int.MaxValue; switch (method) { case ChoiceHeuristic.ShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break; case ChoiceHeuristic.BottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break; case ChoiceHeuristic.ContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1); score1 = -score1; // Reverse since we are minimizing, but for contact point score bigger is better. break; case ChoiceHeuristic.LongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break; case ChoiceHeuristic.AreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break; } // Cannot fit the current rectangle. if (newNode.height == 0) { score1 = int.MaxValue; score2 = int.MaxValue; } return(newNode); }
public void Insert(List <PRRect> rects, List <PRRect> dst, ChoiceHeuristic method) { dst.Clear(); while (rects.Count > 0) { int bestScore1 = int.MaxValue; int bestScore2 = int.MaxValue; int bestRectIndex = -1; PRRect bestNode = new PRRect(); for (int i = 0; i < rects.Count; ++i) { int score1 = 0; int score2 = 0; PRRect newNode = ScoreRect(rects[i].width, rects[i].height, method, ref score1, ref score2); if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2)) { bestScore1 = score1; bestScore2 = score2; bestNode = newNode; bestRectIndex = i; } } if (bestRectIndex == -1) { return; } PlaceRect(bestNode); rects.RemoveAt(bestRectIndex); } }
public PRRect Clone() { PRRect rect = new PRRect(); rect.x = x; rect.y = y; rect.width = width; rect.height = height; return rect; }
public PRRect Clone() { PRRect rect = new PRRect(); rect.x = x; rect.y = y; rect.width = width; rect.height = height; return(rect); }
PRRect FindPositionForNewNodeBestAreaFit(int width, int height, ref int bestAreaFit, ref int bestShortSideFit) { PRRect bestNode = new PRRect(); //memset(&bestNode, 0, sizeof(Rect)); bestAreaFit = int.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i) { int areaFit = freeRectangles[i].width * freeRectangles[i].height - width * height; // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int leftoverHoriz = Mathf.Abs(freeRectangles[i].width - width); int leftoverVert = Mathf.Abs(freeRectangles[i].height - height); int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestShortSideFit = shortSideFit; bestAreaFit = areaFit; } } if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int leftoverHoriz = Mathf.Abs(freeRectangles[i].width - height); int leftoverVert = Mathf.Abs(freeRectangles[i].height - width); int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestShortSideFit = shortSideFit; bestAreaFit = areaFit; } } } return(bestNode); }
public void Init(int width, int height) { binWidth = width; binHeight = height; allowRotations = false; PRRect n = new PRRect(); n.x = 0; n.y = 0; n.width = width; n.height = height; usedRectangles.Clear(); freeRectangles.Clear(); freeRectangles.Add(n); }
void PlaceRect(PRRect node) { int numRectanglesToProcess = freeRectangles.Count; for (int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(freeRectangles[i], ref node)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); usedRectangles.Add(node); }
public PRRect Insert(int width, int height, ChoiceHeuristic method) { PRRect newNode = new PRRect(); int score1 = 0; // Unused in this function. We don't need to know the score after finding the position. int score2 = 0; switch (method) { case ChoiceHeuristic.ShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break; case ChoiceHeuristic.BottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break; case ChoiceHeuristic.ContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1); break; case ChoiceHeuristic.LongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break; case ChoiceHeuristic.AreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break; } if (newNode.height == 0) { return(newNode); } int numRectanglesToProcess = freeRectangles.Count; for (int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(freeRectangles[i], ref newNode)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); usedRectangles.Add(newNode); return(newNode); }
PRRect FindPositionForNewNodeBottomLeft(int width, int height, ref int bestY, ref int bestX) { PRRect bestNode = new PRRect(); //memset(bestNode, 0, sizeof(Rect)); bestY = int.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i) { // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int topSideY = freeRectangles[i].y + height; if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestY = topSideY; bestX = freeRectangles[i].x; } } if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int topSideY = freeRectangles[i].y + width; if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestY = topSideY; bestX = freeRectangles[i].x; } } } return(bestNode); }
PRRect FindPositionForNewNodeContactPoint(int width, int height, ref int bestContactScore) { PRRect bestNode = new PRRect(); //memset(&bestNode, 0, sizeof(Rect)); bestContactScore = -1; for (int i = 0; i < freeRectangles.Count; ++i) { // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int score = ContactPointScoreNode(freeRectangles[i].x, freeRectangles[i].y, width, height); if (score > bestContactScore) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestContactScore = score; } } if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int score = ContactPointScoreNode(freeRectangles[i].x, freeRectangles[i].y, height, width); if (score > bestContactScore) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestContactScore = score; } } } return(bestNode); }
PRRect FindPositionForNewNodeBottomLeft(int width, int height, ref int bestY, ref int bestX) { PRRect bestNode = new PRRect(); //memset(bestNode, 0, sizeof(Rect)); bestY = int.MaxValue; for(int i = 0; i < freeRectangles.Count; ++i) { // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int topSideY = freeRectangles[i].y + height; if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestY = topSideY; bestX = freeRectangles[i].x; } } if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int topSideY = freeRectangles[i].y + width; if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestY = topSideY; bestX = freeRectangles[i].x; } } } return bestNode; }
bool IsContainedIn(PRRect a, PRRect b) { return(a.x >= b.x && a.y >= b.y && a.x + a.width <= b.x + b.width && a.y + a.height <= b.y + b.height); }
bool SplitFreeNode(PRRect freeNode, ref PRRect usedNode) { // Test with SAT if the rectangles even intersect. if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x || usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y) { return(false); } if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) { // New node at the top side of the used node. if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) { PRRect newNode = new PRRect(); newNode.x = freeNode.x; newNode.y = freeNode.y; newNode.width = freeNode.width; newNode.height = usedNode.y - newNode.y; freeRectangles.Add(newNode); } // New node at the bottom side of the used node. if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) { PRRect newNode = new PRRect(); newNode.x = freeNode.x; newNode.y = usedNode.y + usedNode.height; newNode.width = freeNode.width; newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height); freeRectangles.Add(newNode); } } if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) { // New node at the left side of the used node. if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) { PRRect newNode = new PRRect(); newNode.x = freeNode.x; newNode.y = freeNode.y; newNode.width = usedNode.x - newNode.x; newNode.height = freeNode.height; freeRectangles.Add(newNode); } // New node at the right side of the used node. if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) { PRRect newNode = new PRRect(); newNode.x = usedNode.x + usedNode.width; newNode.y = freeNode.y; newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width); newNode.height = freeNode.height; freeRectangles.Add(newNode); } } return(true); }
bool SplitFreeNode(PRRect freeNode, ref PRRect usedNode) { // Test with SAT if the rectangles even intersect. if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x || usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y) return false; if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) { // New node at the top side of the used node. if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) { PRRect newNode = new PRRect(); newNode.x = freeNode.x; newNode.y = freeNode.y; newNode.width = freeNode.width; newNode.height = usedNode.y - newNode.y; freeRectangles.Add(newNode); } // New node at the bottom side of the used node. if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) { PRRect newNode = new PRRect(); newNode.x = freeNode.x; newNode.y = usedNode.y + usedNode.height; newNode.width = freeNode.width; newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height); freeRectangles.Add(newNode); } } if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) { // New node at the left side of the used node. if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) { PRRect newNode = new PRRect(); newNode.x = freeNode.x; newNode.y = freeNode.y; newNode.width = usedNode.x - newNode.x; newNode.height = freeNode.height; freeRectangles.Add(newNode); } // New node at the right side of the used node. if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) { PRRect newNode = new PRRect(); newNode.x = usedNode.x + usedNode.width; newNode.y = freeNode.y; newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width); newNode.height = freeNode.height; freeRectangles.Add(newNode); } } return true; }
PRRect FindPositionForNewNodeContactPoint(int width, int height, ref int bestContactScore) { PRRect bestNode = new PRRect(); //memset(&bestNode, 0, sizeof(Rect)); bestContactScore = -1; for(int i = 0; i < freeRectangles.Count; ++i) { // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int score = ContactPointScoreNode(freeRectangles[i].x, freeRectangles[i].y, width, height); if (score > bestContactScore) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestContactScore = score; } } if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int score = ContactPointScoreNode(freeRectangles[i].x, freeRectangles[i].y, height, width); if (score > bestContactScore) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestContactScore = score; } } } return bestNode; }
PRRect ScoreRect(int width, int height, ChoiceHeuristic method, ref int score1, ref int score2) { PRRect newNode = new PRRect(); score1 = int.MaxValue; score2 = int.MaxValue; switch(method) { case ChoiceHeuristic.ShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break; case ChoiceHeuristic.BottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break; case ChoiceHeuristic.ContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1); score1 = -score1; // Reverse since we are minimizing, but for contact point score bigger is better. break; case ChoiceHeuristic.LongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break; case ChoiceHeuristic.AreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break; } // Cannot fit the current rectangle. if (newNode.height == 0) { score1 = int.MaxValue; score2 = int.MaxValue; } return newNode; }
private void CalculateElementPacking() { int elementCount = elements.Count; //use the min area of all the elements to start with a sensible estimated size for the atlas to be int minArea = 0; for (int e = 0; e < elementCount; e++) { minArea += elements[e].expandedWidth * elements[e].expandedHeight; } int tryWidth = 16; int tryHeight = 16; int tries = 0; while ((tryWidth - link.padding) * (tryHeight - link.padding) < minArea && tries++ < 100) { if (tries % 2 == 0) //alternate increasing width and height until we find a size that fits everything { tryWidth *= 2; } else { tryHeight *= 2; } } tries = 0; //subtract padding because we can't pack right up to the padded border //but don't subtract padding*2 because the element sizes already account for padding on one side PRPacker packer = new PRPacker(tryWidth - link.padding, tryHeight - link.padding); while (tries++ < 100) //tries is to prevent infinite loops { bool didFail = false; for (int e = 0; e < elementCount; e++) { PRAtlasElement element = elements[e]; //Debug.Log("Try fitting " + element.expandedWidth + ", " + element.expandedHeight + " into " + tryWidth+","+tryHeight); //TODO update PRRect rect = packer.Insert(element.expandedWidth, element.expandedHeight, PRPacker.ChoiceHeuristic.ShortSideFit); if (rect.width == 0 && rect.height == 0) //both at 0 means it failed { didFail = true; if (tryWidth <= tryHeight) //alternate increasing width and height until we find a size that fits everything { tryWidth *= 2; } else { tryHeight *= 2; } packer.Init(tryWidth - link.padding, tryHeight - link.padding); break; } else { element.packedRect = rect; element.packedRect.x += element.padding; //push the rect off the wall (note, y doesn't need this for the reason below) //flip packing y coord. This is because the algorithm tries to pack everything around 0,0 //and we want 0,0 to be top left instead of bottom left (which it would be with Unity's coord system) //there's no real reason for it to be top left, except that it's what people are used to. element.packedRect.y = (tryHeight - element.packedRect.y) - element.packedRect.height; } } if (!didFail) { atlasWidth = tryWidth; atlasHeight = tryHeight; break; //we're done! } } }
PRRect FindPositionForNewNodeBestAreaFit(int width, int height, ref int bestAreaFit, ref int bestShortSideFit) { PRRect bestNode = new PRRect(); //memset(&bestNode, 0, sizeof(Rect)); bestAreaFit = int.MaxValue; for(int i = 0; i < freeRectangles.Count; ++i) { int areaFit = freeRectangles[i].width * freeRectangles[i].height - width * height; // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int leftoverHoriz = Mathf.Abs(freeRectangles[i].width - width); int leftoverVert = Mathf.Abs(freeRectangles[i].height - height); int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestShortSideFit = shortSideFit; bestAreaFit = areaFit; } } if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int leftoverHoriz = Mathf.Abs(freeRectangles[i].width - height); int leftoverVert = Mathf.Abs(freeRectangles[i].height - width); int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestShortSideFit = shortSideFit; bestAreaFit = areaFit; } } } return bestNode; }
public void Insert(List<PRRect> rects, List<PRRect> dst, ChoiceHeuristic method) { dst.Clear(); while(rects.Count > 0) { int bestScore1 = int.MaxValue; int bestScore2 = int.MaxValue; int bestRectIndex = -1; PRRect bestNode = new PRRect(); for(int i = 0; i < rects.Count; ++i) { int score1 = 0; int score2 = 0; PRRect newNode = ScoreRect(rects[i].width, rects[i].height, method, ref score1, ref score2); if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2)) { bestScore1 = score1; bestScore2 = score2; bestNode = newNode; bestRectIndex = i; } } if (bestRectIndex == -1) return; PlaceRect(bestNode); rects.RemoveAt(bestRectIndex); } }
bool IsContainedIn(PRRect a, PRRect b) { return a.x >= b.x && a.y >= b.y && a.x+a.width <= b.x+b.width && a.y+a.height <= b.y+b.height; }
private IEnumerator <string> CreateAtlasTexture() { atlasTexture = new Texture2D(atlasWidth, atlasHeight, TextureFormat.ARGB32, false); atlasTexture.filterMode = FilterMode.Bilinear; atlasTexture.SetPixels32(new Color32[atlasWidth * atlasHeight]); //clear it out (using color32 for speed) int elementCount = elements.Count; for (int e = 0; e < elementCount; e++) { PRAtlasElement element = elements[e]; yield return("Packing " + element.name); PRRect packedRect = element.packedRect; int extrude = element.extrude; int outputX = packedRect.x + extrude; int outputY = packedRect.y + extrude; int outputWidth = packedRect.width - extrude * 2 - element.padding; int outputHeight = packedRect.height - extrude * 2 - element.padding; if (link.scale == 1.0f) { Color[] elementPixels = element.texture.GetPixels(element.sourceTrimX, element.sourceTrimY, element.sourceTrimWidth, element.sourceTrimHeight, 0); atlasTexture.SetPixels(outputX, outputY, outputWidth, outputHeight, elementPixels); //atlasTexture.SetPixels(outputX,outputY,outputWidth,outputHeight,elementPixels); if (extrude != 0) //do extruding by pulling pixels from each edge { //left elementPixels = element.texture.GetPixels(element.sourceTrimX, element.sourceTrimY, 1, element.sourceTrimHeight, 0); for (int c = 0; c < extrude; c++) { atlasTexture.SetPixels(outputX - (c + 1), outputY, 1, outputHeight, elementPixels); } //right elementPixels = element.texture.GetPixels(element.sourceTrimX + element.sourceTrimWidth - 1, element.sourceTrimY, 1, element.sourceTrimHeight, 0); for (int c = 0; c < extrude; c++) { atlasTexture.SetPixels(outputX + outputWidth + c, outputY, 1, outputHeight, elementPixels); } //bottom elementPixels = element.texture.GetPixels(element.sourceTrimX, element.sourceTrimY, element.sourceTrimWidth, 1, 0); for (int c = 0; c < extrude; c++) { atlasTexture.SetPixels(outputX, outputY - (c + 1), outputWidth, 1, elementPixels); } //top elementPixels = element.texture.GetPixels(element.sourceTrimX, element.sourceTrimY + element.sourceTrimHeight - 1, element.sourceTrimWidth, 1, 0); for (int c = 0; c < extrude; c++) { atlasTexture.SetPixels(outputX, outputY + outputHeight + c, outputWidth, 1, elementPixels); } //bottom left Color[] cornerPixels = RXArrayUtil.CreateArrayFilledWithItem <Color>(element.texture.GetPixel(element.sourceTrimX, element.sourceTrimY), extrude * extrude); atlasTexture.SetPixels(outputX - extrude, outputY - extrude, extrude, extrude, cornerPixels); //top left cornerPixels = RXArrayUtil.CreateArrayFilledWithItem <Color>(element.texture.GetPixel(element.sourceTrimX, element.sourceTrimY + element.sourceTrimHeight - 1), extrude * extrude); atlasTexture.SetPixels(outputX - extrude, outputY + outputHeight, extrude, extrude, cornerPixels); //top right cornerPixels = RXArrayUtil.CreateArrayFilledWithItem <Color>(element.texture.GetPixel(element.sourceTrimX + element.sourceTrimWidth - 1, element.sourceTrimY + element.sourceTrimHeight - 1), extrude * extrude); atlasTexture.SetPixels(outputX + outputWidth, outputY + outputHeight, extrude, extrude, cornerPixels); //bottom right cornerPixels = RXArrayUtil.CreateArrayFilledWithItem <Color>(element.texture.GetPixel(element.sourceTrimX + element.sourceTrimWidth - 1, element.sourceTrimY), extrude * extrude); atlasTexture.SetPixels(outputX + outputWidth, outputY - extrude, extrude, extrude, cornerPixels); } } else //scale isn't 1, so do a bilinear resample of the texture into the atlas { /* * Texture2D sourceTexture = element.texture; * * float ratioX = (1.0f / (float)element.sourceFullWidth); * float ratioY = (1.0f / (float)element.sourceFullHeight); * * float trimX = ratioX * element.sourceTrimX; * float trimY = ratioY * element.sourceTrimY; */ Texture2D sourceTexture = element.texture; Color[] outputPixels = new Color[outputWidth * outputHeight]; float sourceRatioX = 1.0f / (float)element.sourceFullWidth; //the width of one pixel in uv coords float sourceRatioY = 1.0f / (float)element.sourceFullHeight; //the height of one pixel in uv coords float trimPercentX = sourceRatioX * (float)element.sourceTrimX; float trimPercentY = sourceRatioY * (float)element.sourceTrimY; float trimPercentWidth = sourceRatioX * (float)element.sourceTrimWidth; float trimPercentHeight = sourceRatioY * (float)element.sourceTrimHeight; trimPercentX += sourceRatioX * 0.5f; trimPercentY += sourceRatioY * 0.5f; trimPercentWidth -= sourceRatioX * 1.0f; trimPercentHeight -= sourceRatioX * 1.0f; float outputRatioX = 1.0f / (float)(outputWidth - 1); float outputRatioY = 1.0f / (float)(outputHeight - 1); //Debug.Log(string.Format("{0} tpx:{1} tpy:{2} tpw:{3} tph:{4}",element.name,trimPercentX,trimPercentY,trimPercentWidth,trimPercentHeight)); for (int r = 0; r < outputHeight; r++) { for (int c = 0; c < outputWidth; c++) { float colPercent = (float)c * outputRatioX; float rowPercent = (float)r * outputRatioY; outputPixels[r * outputWidth + c] = sourceTexture.GetPixelBilinear(trimPercentX + colPercent * trimPercentWidth, trimPercentY + rowPercent * trimPercentHeight); // //multi sample with a kernel // float bx = trimPercentX + colPercent*trimPercentWidth; // float by = trimPercentY + rowPercent*trimPercentHeight; // // Color outputColor; // // //outputColor = sourceTexture.GetPixelBilinear(baseX, baseY) * 0.7f; // // float sx = sourceRatioX*(1.0f-link.scale); // float sy = sourceRatioY*(1.0f-link.scale); // // float dsx = sx*2; // float dsy = sy*2; // // float one = 1.0f/64.0f; // float three = 3.0f/64.0f; // float nine = 9.0f/64.0f; // // //from https://groups.google.com/forum/#!topic/comp.graphics.algorithms/XpcOL2xiKO4 // // outputColor = sourceTexture.GetPixelBilinear(bx-dsx, by+dsy) * one; // outputColor += sourceTexture.GetPixelBilinear(bx-sx, by+dsy) * three; // outputColor += sourceTexture.GetPixelBilinear(bx+sx, by+dsy) * three; // outputColor += sourceTexture.GetPixelBilinear(bx+dsx, by+dsy) * one; // // outputColor += sourceTexture.GetPixelBilinear(bx-dsx, by+sy) * three; // outputColor += sourceTexture.GetPixelBilinear(bx-sx, by+sy) * nine; // outputColor += sourceTexture.GetPixelBilinear(bx+sx, by+sy) * nine; // outputColor += sourceTexture.GetPixelBilinear(bx+dsx, by+sy) * three; // // outputColor += sourceTexture.GetPixelBilinear(bx-dsx, by-sy) * three; // outputColor += sourceTexture.GetPixelBilinear(bx-sx, by-sy) * nine; // outputColor += sourceTexture.GetPixelBilinear(bx+sx, by-sy) * nine; // outputColor += sourceTexture.GetPixelBilinear(bx+dsx, by-sy) * three; // // outputColor += sourceTexture.GetPixelBilinear(bx-dsx, by-dsy) * one; // outputColor += sourceTexture.GetPixelBilinear(bx-sx, by-dsy) * three; // outputColor += sourceTexture.GetPixelBilinear(bx+sx, by-dsy) * three; // outputColor += sourceTexture.GetPixelBilinear(bx+dsx, by-dsy) * one; // outputColor = sourceTexture.GetPixelBilinear(bx, by) * 0.7f; // // outputColor += sourceTexture.GetPixelBilinear(bx+sourceRatioX, by) * 0.05f; // outputColor += sourceTexture.GetPixelBilinear(bx-sourceRatioX, by) * 0.05f; // outputColor += sourceTexture.GetPixelBilinear(bx, by+sourceRatioY) * 0.05f; // outputColor += sourceTexture.GetPixelBilinear(bx, by-sourceRatioY) * 0.05f; // // outputColor += sourceTexture.GetPixelBilinear(bx+sourceRatioX, by+sourceRatioY) * 0.0025f; // outputColor += sourceTexture.GetPixelBilinear(bx+sourceRatioX, by-sourceRatioY) * 0.0025f; // outputColor += sourceTexture.GetPixelBilinear(bx-sourceRatioX, by+sourceRatioY) * 0.0025f; // outputColor += sourceTexture.GetPixelBilinear(bx-sourceRatioX, by-sourceRatioY) * 0.0025f; // // outputPixels[r*outputWidth + c] = outputColor; //nearest neighbor // int pixelX = Mathf.RoundToInt((trimPercentX + colPercent*trimPercentWidth) * (float)sourceTexture.width); // int pixelY = Mathf.RoundToInt((trimPercentY + rowPercent*trimPercentHeight) * (float)sourceTexture.height); // outputPixels[r*outputWidth + c] = sourceTexture.GetPixel(pixelX,pixelY); } } atlasTexture.SetPixels(outputX, outputY, outputWidth, outputHeight, outputPixels); if (element.extrude != 0) { //yield return "Extruding " + element.name; int heightExtruded = outputHeight + extrude * 2; Color[] sidePixels = new Color[extrude * heightExtruded]; //used for the left and right sides //LEFT SIDE for (int r = 0; r < heightExtruded; r++) //figure out row colour and then fill that row { int outputRow = Mathf.Max(0, Mathf.Min(outputHeight - 1, r - extrude)); Color color = outputPixels[outputRow * outputWidth]; for (int c = 0; c < extrude; c++) { sidePixels[r * extrude + c] = color; } atlasTexture.SetPixels(outputX - extrude, outputY - extrude, extrude, heightExtruded, sidePixels); } //RIGHT SIDE for (int r = 0; r < heightExtruded; r++) //figure out row colour and then fill that row { int outputRow = Mathf.Max(0, Mathf.Min(outputHeight - 1, r - extrude)); Color color = outputPixels[outputRow * outputWidth + outputWidth - 1]; for (int c = 0; c < extrude; c++) { sidePixels[r * extrude + c] = color; } atlasTexture.SetPixels(outputX + outputWidth, outputY - extrude, extrude, heightExtruded, sidePixels); } sidePixels = new Color[extrude * outputWidth]; //used for the top and bottom sides //BOTTOM SIDE for (int c = 0; c < outputWidth; c++) //figure out row colour and then fill that row { Color color = outputPixels[c]; for (int r = 0; r < extrude; r++) { sidePixels[r * outputWidth + c] = color; } atlasTexture.SetPixels(outputX, outputY - extrude, outputWidth, extrude, sidePixels); } //TOP SIDE int topOffset = (outputHeight - 1) * outputWidth; //start at the top row for (int c = 0; c < outputWidth; c++) //figure out row colour and then fill that row { Color color = outputPixels[topOffset + c]; for (int r = 0; r < extrude; r++) { sidePixels[r * outputWidth + c] = color; } atlasTexture.SetPixels(outputX, outputY + outputHeight, outputWidth, extrude, sidePixels); } } } } // int count = 5; // for(int i = 0; i < count; i++) // { // float percent = (float)i/(float)(count-1); // Debug.Log(i + " percent " + percent); // } yield return("Writing PNG to disk"); atlasTexture.Apply(false, false); File.WriteAllBytes(link.atlasFilePath + pngSuffix, atlasTexture.EncodeToPNG()); if (PRViewAtlasWindow.instance != null && PRViewAtlasWindow.instance.link == link) { PRViewAtlasWindow.instance.UpdateAtlas(); } //in theory this should make it unreadable, //but it doesn't matter because we're about to delete it anyway //atlasTexture.Apply(false,true); //This stuff would be handy, but it's SO SLOW and unreliable that it's not worth it // TextureImporter importer = TextureImporter.GetAtPath(link.atlasFilePath+pngSuffix) as TextureImporter; // // if(importer != null) // { // importer.maxTextureSize = Mathf.Max(importer.maxTextureSize,packedWidth,packedHeight); // importer.alphaIsTransparency = true; // AssetDatabase.ImportAsset(importer.assetPath); // } // // yield return "Importing Atlas"; }
void PlaceRect(PRRect node) { int numRectanglesToProcess = freeRectangles.Count; for(int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(freeRectangles[i], ref node)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); usedRectangles.Add(node); }