Home | History | Annotate | Download | only in converters
      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 
     33 package jme3tools.converters;
     34 
     35 import com.jme3.texture.Image;
     36 import com.jme3.texture.Image.Format;
     37 import com.jme3.texture.plugins.AWTLoader;
     38 import com.jme3.util.BufferUtils;
     39 import java.awt.Transparency;
     40 import java.awt.color.ColorSpace;
     41 import java.awt.image.*;
     42 import java.nio.ByteBuffer;
     43 import java.nio.ByteOrder;
     44 import java.util.EnumMap;
     45 
     46 public class ImageToAwt {
     47 
     48     private static final EnumMap<Format, DecodeParams> params
     49             = new EnumMap<Format, DecodeParams>(Format.class);
     50 
     51     private static class DecodeParams {
     52 
     53         final int bpp, am, rm, gm, bm, as, rs, gs, bs, im, is;
     54 
     55         public DecodeParams(int bpp, int am, int rm, int gm, int bm, int as, int rs, int gs, int bs, int im, int is) {
     56             this.bpp = bpp;
     57             this.am = am;
     58             this.rm = rm;
     59             this.gm = gm;
     60             this.bm = bm;
     61             this.as = as;
     62             this.rs = rs;
     63             this.gs = gs;
     64             this.bs = bs;
     65             this.im = im;
     66             this.is = is;
     67         }
     68 
     69         public DecodeParams(int bpp, int rm, int rs, int im, int is, boolean alpha){
     70             this.bpp = bpp;
     71             if (alpha){
     72                 this.am = rm;
     73                 this.as = rs;
     74                 this.rm = 0;
     75                 this.rs = 0;
     76             }else{
     77                 this.rm = rm;
     78                 this.rs = rs;
     79                 this.am = 0;
     80                 this.as = 0;
     81             }
     82 
     83             this.gm = 0;
     84             this.bm = 0;
     85             this.gs = 0;
     86             this.bs = 0;
     87             this.im = im;
     88             this.is = is;
     89         }
     90 
     91         public DecodeParams(int bpp, int rm, int rs, int im, int is){
     92             this(bpp, rm, rs, im, is, false);
     93         }
     94     }
     95 
     96     static {
     97         final int mx___ = 0xff000000;
     98         final int m_x__ = 0x00ff0000;
     99         final int m__x_ = 0x0000ff00;
    100         final int m___x = 0x000000ff;
    101         final int sx___ = 24;
    102         final int s_x__ = 16;
    103         final int s__x_ = 8;
    104         final int s___x = 0;
    105         final int mxxxx = 0xffffffff;
    106         final int sxxxx = 0;
    107 
    108         final int m4x___ = 0xf000;
    109         final int m4_x__ = 0x0f00;
    110         final int m4__x_ = 0x00f0;
    111         final int m4___x = 0x000f;
    112         final int s4x___ = 12;
    113         final int s4_x__ = 8;
    114         final int s4__x_ = 4;
    115         final int s4___x = 0;
    116 
    117         final int m5___  = 0xf800;
    118         final int m_5__  = 0x07c0;
    119         final int m__5_  = 0x003e;
    120         final int m___1  = 0x0001;
    121 
    122         final int s5___  = 11;
    123         final int s_5__  = 6;
    124         final int s__5_  = 1;
    125         final int s___1  = 0;
    126 
    127         final int m5__   = 0xf800;
    128         final int m_6_   = 0x07e0;
    129         final int m__5   = 0x001f;
    130 
    131         final int s5__   = 11;
    132         final int s_6_   = 5;
    133         final int s__5   = 0;
    134 
    135         final int mxx__  = 0xffff0000;
    136         final int sxx__  = 32;
    137         final int m__xx  = 0x0000ffff;
    138         final int s__xx  = 0;
    139 
    140         // note: compressed, depth, or floating point formats not included here..
    141 
    142         params.put(Format.ABGR8,    new DecodeParams(4, mx___, m___x, m__x_, m_x__,
    143                                                         sx___, s___x, s__x_, s_x__,
    144                                                         mxxxx, sxxxx));
    145         params.put(Format.ARGB4444, new DecodeParams(2, m4x___, m4_x__, m4__x_, m4___x,
    146                                                         s4x___, s4_x__, s4__x_, s4___x,
    147                                                         mxxxx, sxxxx));
    148         params.put(Format.Alpha16,  new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, true));
    149         params.put(Format.Alpha8,   new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, true));
    150         params.put(Format.BGR8,     new DecodeParams(3, 0,     m___x, m__x_, m_x__,
    151                                                         0,     s___x, s__x_, s_x__,
    152                                                         mxxxx, sxxxx));
    153         params.put(Format.Luminance16, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false));
    154         params.put(Format.Luminance8,  new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false));
    155         params.put(Format.Luminance16Alpha16, new DecodeParams(4, m__xx, mxx__, 0, 0,
    156                                                                   s__xx, sxx__, 0, 0,
    157                                                                   mxxxx, sxxxx));
    158         params.put(Format.Luminance16F, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false));
    159         params.put(Format.Luminance16FAlpha16F, new DecodeParams(4, m__xx, mxx__, 0, 0,
    160                                                                     s__xx, sxx__, 0, 0,
    161                                                                     mxxxx, sxxxx));
    162         params.put(Format.Luminance32F, new DecodeParams(4, mxxxx, sxxxx, mxxxx, sxxxx, false));
    163         params.put(Format.Luminance8,   new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false));
    164         params.put(Format.RGB5A1,       new DecodeParams(2, m___1, m5___, m_5__, m__5_,
    165                                                             s___1, s5___, s_5__, s__5_,
    166                                                             mxxxx, sxxxx));
    167         params.put(Format.RGB565,       new DecodeParams(2, 0,     m5__ , m_6_ , m__5,
    168                                                             0,     s5__ , s_6_ , s__5,
    169                                                             mxxxx, sxxxx));
    170         params.put(Format.RGB8,         new DecodeParams(3, 0,     m_x__, m__x_, m___x,
    171                                                             0,     s_x__, s__x_, s___x,
    172                                                             mxxxx, sxxxx));
    173         params.put(Format.RGBA8,        new DecodeParams(4, m___x, mx___, m_x__, m__x_,
    174                                                             s___x, sx___, s_x__, s__x_,
    175                                                             mxxxx, sxxxx));
    176     }
    177 
    178     private static int Ix(int x, int y, int w){
    179         return y * w + x;
    180     }
    181 
    182     private static int readPixel(ByteBuffer buf, int idx, int bpp){
    183         buf.position(idx);
    184         int original = buf.get() & 0xff;
    185         while ((--bpp) > 0){
    186             original = (original << 8) | (buf.get() & 0xff);
    187         }
    188         return original;
    189     }
    190 
    191     private static void writePixel(ByteBuffer buf, int idx, int pixel, int bpp){
    192         buf.position(idx);
    193         while ((--bpp) >= 0){
    194 //            pixel = pixel >> 8;
    195             byte bt = (byte) ((pixel >> (bpp * 8)) & 0xff);
    196 //            buf.put( (byte) (pixel & 0xff) );
    197             buf.put(bt);
    198         }
    199     }
    200 
    201 
    202     /**
    203      * Convert an AWT image to jME image.
    204      */
    205     public static void convert(BufferedImage image, Format format, ByteBuffer buf){
    206         DecodeParams p = params.get(format);
    207         if (p == null)
    208             throw new UnsupportedOperationException("Image format " + format + " is not supported");
    209 
    210         int width = image.getWidth();
    211         int height = image.getHeight();
    212 
    213         boolean alpha = true;
    214         boolean luminance = false;
    215 
    216         int reductionA = 8 - Integer.bitCount(p.am);
    217         int reductionR = 8 - Integer.bitCount(p.rm);
    218         int reductionG = 8 - Integer.bitCount(p.gm);
    219         int reductionB = 8 - Integer.bitCount(p.bm);
    220 
    221         int initialPos = buf.position();
    222         for (int y = 0; y < height; y++){
    223             for (int x = 0; x < width; x++){
    224                 // Get ARGB
    225                 int argb = image.getRGB(x, y);
    226 
    227                 // Extract color components
    228                 int a = (argb & 0xff000000) >> 24;
    229                 int r = (argb & 0x00ff0000) >> 16;
    230                 int g = (argb & 0x0000ff00) >> 8;
    231                 int b = (argb & 0x000000ff);
    232 
    233                 // Remove anything after 8 bits
    234                 a = a & 0xff;
    235                 r = r & 0xff;
    236                 g = g & 0xff;
    237                 b = b & 0xff;
    238 
    239                 // Set full alpha if target image has no alpha
    240                 if (!alpha)
    241                     a = 0xff;
    242 
    243                 // Convert color to luminance if target
    244                 // image is in luminance format
    245                 if (luminance){
    246                     // convert RGB to luminance
    247                 }
    248 
    249                 // Do bit reduction, assumes proper rounding has already been
    250                 // done.
    251                 a = a >> reductionA;
    252                 r = r >> reductionR;
    253                 g = g >> reductionG;
    254                 b = b >> reductionB;
    255 
    256                 // Put components into appropriate positions
    257                 a = (a << p.as) & p.am;
    258                 r = (r << p.rs) & p.rm;
    259                 g = (g << p.gs) & p.gm;
    260                 b = (b << p.bs) & p.bm;
    261 
    262                 int outputPixel = ((a | r | g | b) << p.is) & p.im;
    263                 int i = initialPos + (Ix(x,y,width) * p.bpp);
    264                 writePixel(buf, i, outputPixel, p.bpp);
    265             }
    266         }
    267     }
    268 
    269     private static final double LOG2 = Math.log(2);
    270 
    271     public static void createData(Image image, boolean mipmaps){
    272         int bpp = image.getFormat().getBitsPerPixel();
    273         int w = image.getWidth();
    274         int h = image.getHeight();
    275         if (!mipmaps){
    276             image.setData(BufferUtils.createByteBuffer(w*h*bpp/8));
    277             return;
    278         }
    279         int expectedMipmaps = 1 + (int) Math.ceil(Math.log(Math.max(h, w)) / LOG2);
    280         int[] mipMapSizes = new int[expectedMipmaps];
    281         int total = 0;
    282         for (int i = 0; i < mipMapSizes.length; i++){
    283             int size = (w * h * bpp) / 8;
    284             total += size;
    285             mipMapSizes[i] = size;
    286             w /= 2;
    287             h /= 2;
    288         }
    289         image.setMipMapSizes(mipMapSizes);
    290         image.setData(BufferUtils.createByteBuffer(total));
    291     }
    292 
    293     /**
    294      * Convert the image from the given format to the output format.
    295      * It is assumed that both images have buffers with the appropriate
    296      * number of elements and that both have the same dimensions.
    297      *
    298      * @param input
    299      * @param output
    300      */
    301     public static void convert(Image input, Image output){
    302         DecodeParams inParams  = params.get(input.getFormat());
    303         DecodeParams outParams = params.get(output.getFormat());
    304 
    305         if (inParams == null || outParams == null)
    306             throw new UnsupportedOperationException();
    307 
    308         int width  = input.getWidth();
    309         int height = input.getHeight();
    310 
    311         if (width != output.getWidth() || height != output.getHeight())
    312             throw new IllegalArgumentException();
    313 
    314         ByteBuffer inData = input.getData(0);
    315 
    316         boolean inAlpha = false;
    317         boolean inLum = false;
    318         boolean inRGB = false;
    319         if (inParams.am != 0) {
    320             inAlpha = true;
    321         }
    322 
    323         if (inParams.rm != 0 && inParams.gm == 0 && inParams.bm == 0) {
    324             inLum = true;
    325         } else if (inParams.rm != 0 && inParams.gm != 0 && inParams.bm != 0) {
    326             inRGB = true;
    327         }
    328 
    329         int expansionA = 8 - Integer.bitCount(inParams.am);
    330         int expansionR = 8 - Integer.bitCount(inParams.rm);
    331         int expansionG = 8 - Integer.bitCount(inParams.gm);
    332         int expansionB = 8 - Integer.bitCount(inParams.bm);
    333 
    334         int inputPixel;
    335         for (int y = 0; y < height; y++){
    336             for (int x = 0; x < width; x++){
    337                 int i = Ix(x, y, width) * inParams.bpp;
    338                 inputPixel = (readPixel(inData, i, inParams.bpp) & inParams.im) >> inParams.is;
    339 
    340                 int a = (inputPixel & inParams.am) >> inParams.as;
    341                 int r = (inputPixel & inParams.rm) >> inParams.rs;
    342                 int g = (inputPixel & inParams.gm) >> inParams.gs;
    343                 int b = (inputPixel & inParams.bm) >> inParams.bs;
    344 
    345                 r = r & 0xff;
    346                 g = g & 0xff;
    347                 b = b & 0xff;
    348                 a = a & 0xff;
    349 
    350                 a = a << expansionA;
    351                 r = r << expansionR;
    352                 g = g << expansionG;
    353                 b = b << expansionB;
    354 
    355                 if (inLum)
    356                     b = g = r;
    357 
    358                 if (!inAlpha)
    359                     a = 0xff;
    360 
    361 //                int argb = (a << 24) | (r << 16) | (g << 8) | b;
    362 //                out.setRGB(x, y, argb);
    363             }
    364         }
    365     }
    366 
    367     public static BufferedImage convert(Image image, boolean do16bit, boolean fullalpha, int mipLevel){
    368         Format format = image.getFormat();
    369         DecodeParams p = params.get(image.getFormat());
    370         if (p == null)
    371             throw new UnsupportedOperationException();
    372 
    373         int width = image.getWidth();
    374         int height = image.getHeight();
    375 
    376         int level = mipLevel;
    377         while (--level >= 0){
    378             width  /= 2;
    379             height /= 2;
    380         }
    381 
    382         ByteBuffer buf = image.getData(0);
    383         buf.order(ByteOrder.LITTLE_ENDIAN);
    384 
    385         BufferedImage out;
    386 
    387         boolean alpha = false;
    388         boolean luminance = false;
    389         boolean rgb = false;
    390         if (p.am != 0)
    391             alpha = true;
    392 
    393         if (p.rm != 0 && p.gm == 0 && p.bm == 0)
    394             luminance = true;
    395         else if (p.rm != 0 && p.gm != 0 && p.bm != 0)
    396             rgb = true;
    397 
    398         // alpha OR luminance but not both
    399         if ( (alpha && !rgb && !luminance) || (luminance && !alpha && !rgb) ){
    400             out = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
    401         }else if ( (rgb && alpha) || (luminance && alpha) ){
    402             if (do16bit){
    403                 if (fullalpha){
    404                     ColorModel model = AWTLoader.AWT_RGBA4444;
    405                     WritableRaster raster = model.createCompatibleWritableRaster(width, width);
    406                     out = new BufferedImage(model, raster, false, null);
    407                 }else{
    408                     // RGB5_A1
    409                     ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
    410                     int[] nBits = {5, 5, 5, 1};
    411                     int[] bOffs = {0, 1, 2, 3};
    412                     ColorModel colorModel = new ComponentColorModel(cs, nBits, true, false,
    413                                                                     Transparency.BITMASK,
    414                                                                     DataBuffer.TYPE_BYTE);
    415                     WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
    416                                                                            width, height,
    417                                                                            width*2, 2,
    418                                                                            bOffs, null);
    419                     out = new BufferedImage(colorModel, raster, false, null);
    420                 }
    421             }else{
    422                 out = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    423             }
    424         }else{
    425             if (do16bit){
    426                 out = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_565_RGB);
    427             }else{
    428                 out = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    429             }
    430         }
    431 
    432         int expansionA = 8 - Integer.bitCount(p.am);
    433         int expansionR = 8 - Integer.bitCount(p.rm);
    434         int expansionG = 8 - Integer.bitCount(p.gm);
    435         int expansionB = 8 - Integer.bitCount(p.bm);
    436 
    437         if (expansionR < 0){
    438             expansionR = 0;
    439         }
    440 
    441         int mipPos = 0;
    442         for (int i = 0; i < mipLevel; i++){
    443             mipPos += image.getMipMapSizes()[i];
    444         }
    445         int inputPixel;
    446         for (int y = 0; y < height; y++){
    447             for (int x = 0; x < width; x++){
    448                 int i = mipPos + (Ix(x,y,width) * p.bpp);
    449                 inputPixel = (readPixel(buf,i,p.bpp) & p.im) >> p.is;
    450                 int a = (inputPixel & p.am) >> p.as;
    451                 int r = (inputPixel & p.rm) >> p.rs;
    452                 int g = (inputPixel & p.gm) >> p.gs;
    453                 int b = (inputPixel & p.bm) >> p.bs;
    454 
    455                 r = r & 0xff;
    456                 g = g & 0xff;
    457                 b = b & 0xff;
    458                 a = a & 0xff;
    459 
    460                 a = a << expansionA;
    461                 r = r << expansionR;
    462                 g = g << expansionG;
    463                 b = b << expansionB;
    464 
    465                 if (luminance)
    466                     b = g = r;
    467 
    468                 if (!alpha)
    469                     a = 0xff;
    470 
    471                 int argb = (a << 24) | (r << 16) | (g << 8) | b;
    472                 out.setRGB(x, y, argb);
    473             }
    474         }
    475 
    476         return out;
    477     }
    478 
    479 }
    480