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