• 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 
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 
DecodeParams(int bpp, int am, int rm, int gm, int bm, int as, int rs, int gs, int bs, int im, int is)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 
DecodeParams(int bpp, int rm, int rs, int im, int is, boolean alpha)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 
DecodeParams(int bpp, int rm, int rs, int im, int is)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 
params.put(Format.ABGR8, new DecodeParams(4, mx___, m___x, m__x_, m_x__, sx___, s___x, s__x_, s_x__, mxxxx, sxxxx))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));
params.put(Format.ARGB4444, new DecodeParams(2, m4x___, m4_x__, m4__x_, m4___x, s4x___, s4_x__, s4__x_, s4___x, 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));
params.put(Format.Alpha16, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, true))148         params.put(Format.Alpha16,  new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, true));
params.put(Format.Alpha8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, true))149         params.put(Format.Alpha8,   new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, true));
params.put(Format.BGR8, new DecodeParams(3, 0, m___x, m__x_, m_x__, 0, s___x, s__x_, s_x__, mxxxx, sxxxx))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));
params.put(Format.Luminance16, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false))153         params.put(Format.Luminance16, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false));
params.put(Format.Luminance8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false))154         params.put(Format.Luminance8,  new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false));
params.put(Format.Luminance16Alpha16, new DecodeParams(4, m__xx, mxx__, 0, 0, s__xx, sxx__, 0, 0, mxxxx, sxxxx))155         params.put(Format.Luminance16Alpha16, new DecodeParams(4, m__xx, mxx__, 0, 0,
156                                                                   s__xx, sxx__, 0, 0,
157                                                                   mxxxx, sxxxx));
params.put(Format.Luminance16F, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false))158         params.put(Format.Luminance16F, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false));
params.put(Format.Luminance16FAlpha16F, new DecodeParams(4, m__xx, mxx__, 0, 0, s__xx, sxx__, 0, 0, mxxxx, sxxxx))159         params.put(Format.Luminance16FAlpha16F, new DecodeParams(4, m__xx, mxx__, 0, 0,
160                                                                     s__xx, sxx__, 0, 0,
161                                                                     mxxxx, sxxxx));
params.put(Format.Luminance32F, new DecodeParams(4, mxxxx, sxxxx, mxxxx, sxxxx, false))162         params.put(Format.Luminance32F, new DecodeParams(4, mxxxx, sxxxx, mxxxx, sxxxx, false));
params.put(Format.Luminance8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false))163         params.put(Format.Luminance8,   new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false));
params.put(Format.RGB5A1, new DecodeParams(2, m___1, m5___, m_5__, m__5_, s___1, s5___, s_5__, s__5_, mxxxx, sxxxx))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));
params.put(Format.RGB565, new DecodeParams(2, 0, m5__ , m_6_ , m__5, 0, s5__ , s_6_ , s__5, 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));
params.put(Format.RGB8, new DecodeParams(3, 0, m_x__, m__x_, m___x, 0, s_x__, s__x_, s___x, 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));
params.put(Format.RGBA8, new DecodeParams(4, m___x, mx___, m_x__, m__x_, s___x, sx___, s_x__, s__x_, 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 
Ix(int x, int y, int w)178     private static int Ix(int x, int y, int w){
179         return y * w + x;
180     }
181 
readPixel(ByteBuffer buf, int idx, int bpp)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 
writePixel(ByteBuffer buf, int idx, int pixel, int bpp)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      */
convert(BufferedImage image, Format format, ByteBuffer buf)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 
createData(Image image, boolean mipmaps)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      */
convert(Image input, Image output)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 
convert(Image image, boolean do16bit, boolean fullalpha, int mipLevel)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