Home | History | Annotate | Download | only in plugins
      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