• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package com.jme3.texture.plugins;
33 
34 import com.jme3.math.FastMath;
35 import com.jme3.texture.Image.Format;
36 import com.jme3.util.BufferUtils;
37 import java.nio.ByteBuffer;
38 import java.nio.ByteOrder;
39 
40 /**
41  * DXTFlipper is a utility class used to flip along Y axis DXT compressed textures.
42  *
43  * @author Kirill Vainer
44  */
45 public class DXTFlipper {
46 
47     private static final ByteBuffer bb = ByteBuffer.allocate(8);
48 
49     static {
50         bb.order(ByteOrder.LITTLE_ENDIAN);
51     }
52 
readCode5(long data, int x, int y)53     private static long readCode5(long data, int x, int y){
54         long shift =   (4 * y + x) * 3;
55         long mask = 0x7;
56         mask <<= shift;
57         long code = data & mask;
58         code >>= shift;
59         return code;
60     }
61 
writeCode5(long data, int x, int y, long code)62     private static long writeCode5(long data, int x, int y, long code){
63         long shift =  (4 * y + x) * 3;
64         long mask = 0x7;
65         code = (code & mask) << shift;
66         mask <<= shift;
67         mask = ~mask;
68         data &= mask;
69         data |= code; // write new code
70         return data;
71     }
72 
flipDXT5Block(byte[] block, int h)73     private static void flipDXT5Block(byte[] block, int h){
74         if (h == 1)
75             return;
76 
77         byte c0 = block[0];
78         byte c1 = block[1];
79 
80         bb.clear();
81         bb.put(block, 2, 6).flip();
82         bb.clear();
83         long l = bb.getLong();
84         long n = l;
85 
86         if (h == 2){
87             n = writeCode5(n, 0, 0, readCode5(l, 0, 1));
88             n = writeCode5(n, 1, 0, readCode5(l, 1, 1));
89             n = writeCode5(n, 2, 0, readCode5(l, 2, 1));
90             n = writeCode5(n, 3, 0, readCode5(l, 3, 1));
91 
92             n = writeCode5(n, 0, 1, readCode5(l, 0, 0));
93             n = writeCode5(n, 1, 1, readCode5(l, 1, 0));
94             n = writeCode5(n, 2, 1, readCode5(l, 2, 0));
95             n = writeCode5(n, 3, 1, readCode5(l, 3, 0));
96         }else{
97             n = writeCode5(n, 0, 0, readCode5(l, 0, 3));
98             n = writeCode5(n, 1, 0, readCode5(l, 1, 3));
99             n = writeCode5(n, 2, 0, readCode5(l, 2, 3));
100             n = writeCode5(n, 3, 0, readCode5(l, 3, 3));
101 
102             n = writeCode5(n, 0, 1, readCode5(l, 0, 2));
103             n = writeCode5(n, 1, 1, readCode5(l, 1, 2));
104             n = writeCode5(n, 2, 1, readCode5(l, 2, 2));
105             n = writeCode5(n, 3, 1, readCode5(l, 3, 2));
106 
107             n = writeCode5(n, 0, 2, readCode5(l, 0, 1));
108             n = writeCode5(n, 1, 2, readCode5(l, 1, 1));
109             n = writeCode5(n, 2, 2, readCode5(l, 2, 1));
110             n = writeCode5(n, 3, 2, readCode5(l, 3, 1));
111 
112             n = writeCode5(n, 0, 3, readCode5(l, 0, 0));
113             n = writeCode5(n, 1, 3, readCode5(l, 1, 0));
114             n = writeCode5(n, 2, 3, readCode5(l, 2, 0));
115             n = writeCode5(n, 3, 3, readCode5(l, 3, 0));
116         }
117 
118         bb.clear();
119         bb.putLong(n);
120         bb.clear();
121         bb.get(block, 2, 6).flip();
122 
123         assert c0 == block[0] && c1 == block[1];
124     }
125 
flipDXT3Block(byte[] block, int h)126     private static void flipDXT3Block(byte[] block, int h){
127         if (h == 1)
128             return;
129 
130         // first row
131         byte tmp0 = block[0];
132         byte tmp1 = block[1];
133 
134         if (h == 2){
135             block[0] = block[2];
136             block[1] = block[3];
137 
138             block[2] = tmp0;
139             block[3] = tmp1;
140         }else{
141             // write last row to first row
142             block[0] = block[6];
143             block[1] = block[7];
144 
145             // write first row to last row
146             block[6] = tmp0;
147             block[7] = tmp1;
148 
149             // 2nd row
150             tmp0 = block[2];
151             tmp1 = block[3];
152 
153             // write 3rd row to 2nd
154             block[2] = block[4];
155             block[3] = block[5];
156 
157             // write 2nd row to 3rd
158             block[4] = tmp0;
159             block[5] = tmp1;
160         }
161     }
162 
163     /**
164      * Flips a DXT color block or a DXT3 alpha block
165      * @param block
166      * @param h
167      */
flipDXT1orDXTA3Block(byte[] block, int h)168     private static void flipDXT1orDXTA3Block(byte[] block, int h){
169         byte tmp;
170         switch (h){
171             case 1:
172                 return;
173             case 2:
174                 // keep header intact (the two colors)
175                 // header takes 4 bytes
176 
177                 // flip only two top rows
178                 tmp = block[4+1];
179                 block[4+1] = block[4+0];
180                 block[4+0] = tmp;
181                 return;
182             default:
183                 // keep header intact (the two colors)
184                 // header takes 4 bytes
185 
186                 // flip first & fourth row
187                 tmp = block[4+3];
188                 block[4+3] = block[4+0];
189                 block[4+0] = tmp;
190 
191                 // flip second and third row
192                 tmp = block[4+2];
193                 block[4+2] = block[4+1];
194                 block[4+1] = tmp;
195                 return;
196         }
197     }
198 
flipDXT(ByteBuffer img, int w, int h, Format format)199     public static ByteBuffer flipDXT(ByteBuffer img, int w, int h, Format format){
200         int blocksX = (int) FastMath.ceil((float)w / 4f);
201         int blocksY = (int) FastMath.ceil((float)h / 4f);
202 
203         int type;
204         switch (format){
205             case DXT1:
206             case DXT1A:
207                 type = 1;
208                 break;
209             case DXT3:
210                 type = 2;
211                 break;
212             case DXT5:
213                 type = 3;
214                 break;
215             case LATC:
216                 type = 4;
217                 break;
218             case LTC:
219                 type = 5;
220                 break;
221             default:
222                 throw new IllegalArgumentException();
223         }
224 
225         // DXT1 uses 8 bytes per block,
226         // DXT3, DXT5, LATC use 16 bytes per block
227         int bpb = type == 1 || type == 5 ? 8 : 16;
228 
229         ByteBuffer retImg = BufferUtils.createByteBuffer(blocksX * blocksY * bpb);
230 
231         if (h == 1){
232             retImg.put(img);
233             retImg.rewind();
234             return retImg;
235         }else if (h == 2){
236             byte[] colorBlock = new byte[8];
237             byte[] alphaBlock = type != 1 && type != 5 ? new byte[8] : null;
238             for (int x = 0; x < blocksX; x++){
239                 // prepeare for block reading
240                 int blockByteOffset = x * bpb;
241                 img.position(blockByteOffset);
242                 img.limit(blockByteOffset + bpb);
243 
244                 img.get(colorBlock);
245                 if (type == 4 || type == 5)
246                     flipDXT5Block(colorBlock, h);
247                 else
248                     flipDXT1orDXTA3Block(colorBlock, h);
249 
250                 // write block (no need to flip block indexes, only pixels
251                 // inside block
252                 retImg.put(colorBlock);
253 
254                 if (alphaBlock != null){
255                     img.get(alphaBlock);
256                     switch (type){
257                         case 2:
258                             flipDXT3Block(alphaBlock, h); break;
259                         case 3:
260                         case 4:
261                             flipDXT5Block(alphaBlock, h);
262                             break;
263                     }
264                     retImg.put(alphaBlock);
265                 }
266             }
267             retImg.rewind();
268             return retImg;
269         }else if (h >= 4){
270             byte[] colorBlock = new byte[8];
271             byte[] alphaBlock = type != 1 && type != 5 ? new byte[8] : null;
272             for (int y = 0; y < blocksY; y++){
273                 for (int x = 0; x < blocksX; x++){
274                     // prepeare for block reading
275                     int blockIdx = y * blocksX + x;
276                     int blockByteOffset = blockIdx * bpb;
277 
278                     img.position(blockByteOffset);
279                     img.limit(blockByteOffset + bpb);
280 
281                     blockIdx = (blocksY - y - 1) * blocksX + x;
282                     blockByteOffset = blockIdx * bpb;
283 
284                     retImg.position(blockByteOffset);
285                     retImg.limit(blockByteOffset + bpb);
286 
287                     if (alphaBlock != null){
288                         img.get(alphaBlock);
289                         switch (type){
290                             case 2:
291                                 flipDXT3Block(alphaBlock, h);
292                                 break;
293                             case 3:
294                             case 4:
295                                 flipDXT5Block(alphaBlock, h);
296                                 break;
297                         }
298                         retImg.put(alphaBlock);
299                     }
300 
301                     img.get(colorBlock);
302                     if (type == 4 || type == 5)
303                         flipDXT5Block(colorBlock, h);
304                     else
305                         flipDXT1orDXTA3Block(colorBlock, h);
306 
307                     retImg.put(colorBlock);
308                 }
309             }
310             retImg.limit(retImg.capacity());
311             retImg.position(0);
312             return retImg;
313         }else{
314             return null;
315         }
316     }
317 
318 }
319