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
33package com.jme3.texture.plugins;
34
35import com.jme3.asset.AssetInfo;
36import com.jme3.asset.AssetLoadException;
37import com.jme3.asset.AssetLoader;
38import com.jme3.asset.TextureKey;
39import com.jme3.texture.Image;
40import com.jme3.texture.Image.Format;
41import com.jme3.util.BufferUtils;
42import java.awt.Transparency;
43import java.awt.color.ColorSpace;
44import java.awt.image.*;
45import java.io.IOException;
46import java.io.InputStream;
47import java.nio.ByteBuffer;
48import javax.imageio.ImageIO;
49
50public class AWTLoader implements AssetLoader {
51
52    public static final ColorModel AWT_RGBA4444 = new DirectColorModel(16,
53                                                                       0xf000,
54                                                                       0x0f00,
55                                                                       0x00f0,
56                                                                       0x000f);
57
58    public static final ColorModel AWT_RGBA5551
59            = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
60                                      new int[]{5, 5, 5, 1},
61                                      true,
62                                      false,
63                                      Transparency.BITMASK,
64                                      DataBuffer.TYPE_BYTE);
65
66    private Object extractImageData(BufferedImage img){
67        DataBuffer buf = img.getRaster().getDataBuffer();
68        switch (buf.getDataType()){
69            case DataBuffer.TYPE_BYTE:
70                DataBufferByte byteBuf = (DataBufferByte) buf;
71                return byteBuf.getData();
72            case DataBuffer.TYPE_USHORT:
73                DataBufferUShort shortBuf = (DataBufferUShort) buf;
74                return shortBuf.getData();
75        }
76        return null;
77    }
78
79    private void flipImage(byte[] img, int width, int height, int bpp){
80        int scSz = (width * bpp) / 8;
81        byte[] sln = new byte[scSz];
82        int y2 = 0;
83        for (int y1 = 0; y1 < height / 2; y1++){
84            y2 = height - y1 - 1;
85            System.arraycopy(img, y1 * scSz, sln, 0,         scSz);
86            System.arraycopy(img, y2 * scSz, img, y1 * scSz, scSz);
87            System.arraycopy(sln, 0,         img, y2 * scSz, scSz);
88        }
89    }
90
91    private void flipImage(short[] img, int width, int height, int bpp){
92        int scSz = (width * bpp) / 8;
93        scSz /= 2; // Because shorts are 2 bytes
94        short[] sln = new short[scSz];
95        int y2 = 0;
96        for (int y1 = 0; y1 < height / 2; y1++){
97            y2 = height - y1 - 1;
98            System.arraycopy(img, y1 * scSz, sln, 0,         scSz);
99            System.arraycopy(img, y2 * scSz, img, y1 * scSz, scSz);
100            System.arraycopy(sln, 0,         img, y2 * scSz, scSz);
101        }
102    }
103
104    public Image load(BufferedImage img, boolean flipY){
105        int width = img.getWidth();
106        int height = img.getHeight();
107
108        switch (img.getType()){
109            case BufferedImage.TYPE_4BYTE_ABGR: // most common in PNG images w/ alpha
110               byte[] dataBuf1 = (byte[]) extractImageData(img);
111               if (flipY)
112                   flipImage(dataBuf1, width, height, 32);
113
114               ByteBuffer data1 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4);
115               data1.put(dataBuf1);
116               return new Image(Format.ABGR8, width, height, data1);
117            case BufferedImage.TYPE_3BYTE_BGR: // most common in JPEG images
118               byte[] dataBuf2 = (byte[]) extractImageData(img);
119               if (flipY)
120                   flipImage(dataBuf2, width, height, 24);
121
122               ByteBuffer data2 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3);
123               data2.put(dataBuf2);
124               return new Image(Format.BGR8, width, height, data2);
125            case BufferedImage.TYPE_BYTE_GRAY: // grayscale fonts
126                byte[] dataBuf3 = (byte[]) extractImageData(img);
127                if (flipY)
128                    flipImage(dataBuf3, width, height, 8);
129                ByteBuffer data3 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight());
130                data3.put(dataBuf3);
131                return new Image(Format.Luminance8, width, height, data3);
132            case BufferedImage.TYPE_USHORT_GRAY: // grayscale heightmap
133                short[] dataBuf4 = (short[]) extractImageData(img);
134                if (flipY)
135                    flipImage(dataBuf4, width, height, 16);
136
137                ByteBuffer data4 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*2);
138                data4.asShortBuffer().put(dataBuf4);
139                return new Image(Format.Luminance16, width, height, data4);
140            default:
141                break;
142        }
143
144        if (img.getTransparency() == Transparency.OPAQUE){
145            ByteBuffer data = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3);
146            // no alpha
147            for (int y = 0; y < height; y++){
148                for (int x = 0; x < width; x++){
149                    int ny = y;
150                    if (flipY){
151                        ny = height - y - 1;
152                    }
153
154                    int rgb = img.getRGB(x,ny);
155                    byte r = (byte) ((rgb & 0x00FF0000) >> 16);
156                    byte g = (byte) ((rgb & 0x0000FF00) >> 8);
157                    byte b = (byte) ((rgb & 0x000000FF));
158                    data.put(r).put(g).put(b);
159                }
160            }
161            data.flip();
162            return new Image(Format.RGB8, width, height, data);
163        }else{
164            ByteBuffer data = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4);
165            // no alpha
166            for (int y = 0; y < height; y++){
167                for (int x = 0; x < width; x++){
168                    int ny = y;
169                    if (flipY){
170                        ny = height - y - 1;
171                    }
172
173                    int rgb = img.getRGB(x,ny);
174                    byte a = (byte) ((rgb & 0xFF000000) >> 24);
175                    byte r = (byte) ((rgb & 0x00FF0000) >> 16);
176                    byte g = (byte) ((rgb & 0x0000FF00) >> 8);
177                    byte b = (byte) ((rgb & 0x000000FF));
178                    data.put(r).put(g).put(b).put(a);
179                }
180            }
181            data.flip();
182            return new Image(Format.RGBA8, width, height, data);
183        }
184    }
185
186    public Image load(InputStream in, boolean flipY) throws IOException{
187        ImageIO.setUseCache(false);
188        BufferedImage img = ImageIO.read(in);
189        if (img == null){
190            return null;
191        }
192        return load(img, flipY);
193    }
194
195    public Object load(AssetInfo info) throws IOException {
196        if (ImageIO.getImageReadersBySuffix(info.getKey().getExtension()) != null){
197            boolean flip = ((TextureKey) info.getKey()).isFlipY();
198            InputStream in = null;
199            try {
200                in = info.openStream();
201                Image img = load(in, flip);
202                if (img == null){
203                    throw new AssetLoadException("The given image cannot be loaded " + info.getKey());
204                }
205                return img;
206            } finally {
207                if (in != null){
208                    in.close();
209                }
210            }
211        }else{
212            throw new AssetLoadException("The extension " + info.getKey().getExtension() + " is not supported");
213        }
214    }
215}
216