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