001package net.minecraft.client.renderer.texture;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import java.util.ArrayList;
006import java.util.Arrays;
007import java.util.HashSet;
008import java.util.Iterator;
009import java.util.List;
010import java.util.Set;
011import net.minecraft.client.renderer.StitcherException;
012
013@SideOnly(Side.CLIENT)
014public class Stitcher
015{
016    private final Set setStitchHolders;
017    private final List stitchSlots;
018    private int currentWidth;
019    private int currentHeight;
020    private final int maxWidth;
021    private final int maxHeight;
022    private final boolean forcePowerOf2;
023
024    /** Max size (width or height) of a single tile */
025    private final int maxTileDimension;
026    private Texture atlasTexture;
027    private final String textureName;
028
029    public Stitcher(String par1Str, int par2, int par3, boolean par4)
030    {
031        this(par1Str, par2, par3, par4, 0);
032    }
033
034    public Stitcher(String par1, int par2, int par3, boolean par4, int par5)
035    {
036        this.setStitchHolders = new HashSet(256);
037        this.stitchSlots = new ArrayList(256);
038        this.currentWidth = 0;
039        this.currentHeight = 0;
040        this.textureName = par1;
041        this.maxWidth = par2;
042        this.maxHeight = par3;
043        this.forcePowerOf2 = par4;
044        this.maxTileDimension = par5;
045    }
046
047    public void addStitchHolder(StitchHolder par1StitchHolder)
048    {
049        if (this.maxTileDimension > 0)
050        {
051            par1StitchHolder.setNewDimension(this.maxTileDimension);
052        }
053
054        this.setStitchHolders.add(par1StitchHolder);
055    }
056
057    public Texture getTexture()
058    {
059        if (this.forcePowerOf2)
060        {
061            this.currentWidth = this.getCeilPowerOf2(this.currentWidth);
062            this.currentHeight = this.getCeilPowerOf2(this.currentHeight);
063        }
064
065        this.atlasTexture = TextureManager.instance().createEmptyTexture(this.textureName, 1, this.currentWidth, this.currentHeight, 6408);
066        this.atlasTexture.fillRect(this.atlasTexture.getTextureRect(), -65536);
067        List list = this.getStichSlots();
068
069        for (int i = 0; i < list.size(); ++i)
070        {
071            StitchSlot stitchslot = (StitchSlot)list.get(i);
072            StitchHolder stitchholder = stitchslot.getStitchHolder();
073            this.atlasTexture.copyFrom(stitchslot.getOriginX(), stitchslot.getOriginY(), stitchholder.func_98150_a(), stitchholder.isRotated());
074        }
075
076        TextureManager.instance().registerTexture(this.textureName, this.atlasTexture);
077        return this.atlasTexture;
078    }
079
080    public void doStitch()
081    {
082        StitchHolder[] astitchholder = (StitchHolder[])this.setStitchHolders.toArray(new StitchHolder[this.setStitchHolders.size()]);
083        Arrays.sort(astitchholder);
084        this.atlasTexture = null;
085
086        for (int i = 0; i < astitchholder.length; ++i)
087        {
088            StitchHolder stitchholder = astitchholder[i];
089
090            if (!this.allocateSlot(stitchholder))
091            {
092                throw new StitcherException(stitchholder);
093            }
094        }
095    }
096
097    public List getStichSlots()
098    {
099        ArrayList arraylist = new ArrayList();
100        Iterator iterator = this.stitchSlots.iterator();
101
102        while (iterator.hasNext())
103        {
104            StitchSlot stitchslot = (StitchSlot)iterator.next();
105            stitchslot.getAllStitchSlots(arraylist);
106        }
107
108        return arraylist;
109    }
110
111    /**
112     * Returns power of 2 >= the specified value
113     */
114    private int getCeilPowerOf2(int par1)
115    {
116        int j = par1 - 1;
117        j |= j >> 1;
118        j |= j >> 2;
119        j |= j >> 4;
120        j |= j >> 8;
121        j |= j >> 16;
122        return j + 1;
123    }
124
125    /**
126     * Attempts to find space for specified tile
127     */
128    private boolean allocateSlot(StitchHolder par1StitchHolder)
129    {
130        for (int i = 0; i < this.stitchSlots.size(); ++i)
131        {
132            if (((StitchSlot)this.stitchSlots.get(i)).func_94182_a(par1StitchHolder))
133            {
134                return true;
135            }
136
137            par1StitchHolder.rotate();
138
139            if (((StitchSlot)this.stitchSlots.get(i)).func_94182_a(par1StitchHolder))
140            {
141                return true;
142            }
143
144            par1StitchHolder.rotate();
145        }
146
147        return this.expandAndAllocateSlot(par1StitchHolder);
148    }
149
150    /**
151     * Expand stitched texture in order to make space for specified tile
152     */
153    private boolean expandAndAllocateSlot(StitchHolder par1StitchHolder)
154    {
155        int i = Math.min(par1StitchHolder.getHeight(), par1StitchHolder.getWidth());
156        boolean flag = this.currentWidth == 0 && this.currentHeight == 0;
157        boolean flag1;
158
159        if (this.forcePowerOf2)
160        {
161            int j = this.getCeilPowerOf2(this.currentWidth);
162            int k = this.getCeilPowerOf2(this.currentHeight);
163            int l = this.getCeilPowerOf2(this.currentWidth + i);
164            int i1 = this.getCeilPowerOf2(this.currentHeight + i);
165            boolean flag2 = l <= this.maxWidth;
166            boolean flag3 = i1 <= this.maxHeight;
167
168            if (!flag2 && !flag3)
169            {
170                return false;
171            }
172
173            int j1 = Math.max(par1StitchHolder.getHeight(), par1StitchHolder.getWidth());
174
175            if (flag && !flag2 && this.getCeilPowerOf2(this.currentHeight + j1) > this.maxHeight)
176            {
177                return false;
178            }
179
180            boolean flag4 = j != l;
181            boolean flag5 = k != i1;
182
183            if (flag4 ^ flag5)
184            {
185                flag1 = flag5 && flag3; //Forge: Bug fix: Attempt to fill all downward space before expanding width
186            }
187            else
188            {
189                flag1 = flag2 && j <= k;
190            }
191        }
192        else
193        {
194            boolean flag6 = this.currentWidth + i <= this.maxWidth;
195            boolean flag7 = this.currentHeight + i <= this.maxHeight;
196
197            if (!flag6 && !flag7)
198            {
199                return false;
200            }
201
202            flag1 = (flag || this.currentWidth <= this.currentHeight) && flag6;
203        }
204
205        StitchSlot stitchslot;
206
207        if (flag1)
208        {
209            if (par1StitchHolder.getWidth() > par1StitchHolder.getHeight())
210            {
211                par1StitchHolder.rotate();
212            }
213
214            if (this.currentHeight == 0)
215            {
216                this.currentHeight = par1StitchHolder.getHeight();
217            }
218
219            stitchslot = new StitchSlot(this.currentWidth, 0, par1StitchHolder.getWidth(), this.currentHeight);
220            this.currentWidth += par1StitchHolder.getWidth();
221        }
222        else
223        {
224            stitchslot = new StitchSlot(0, this.currentHeight, this.currentWidth, par1StitchHolder.getHeight());
225            this.currentHeight += par1StitchHolder.getHeight();
226        }
227
228        stitchslot.func_94182_a(par1StitchHolder);
229        this.stitchSlots.add(stitchslot);
230        return true;
231    }
232}