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 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 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 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 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 */ 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 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