1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics; 18 19 import com.android.ide.common.rendering.api.LayoutLog; 20 import com.android.layoutlib.bridge.Bridge; 21 import com.android.layoutlib.bridge.impl.DelegateManager; 22 import com.android.resources.Density; 23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 24 25 import android.annotation.Nullable; 26 import android.graphics.Bitmap.Config; 27 import android.os.Parcel; 28 29 import java.awt.Graphics2D; 30 import java.awt.image.BufferedImage; 31 import java.io.File; 32 import java.io.IOException; 33 import java.io.InputStream; 34 import java.io.OutputStream; 35 import java.nio.Buffer; 36 import java.util.Arrays; 37 import java.util.EnumSet; 38 import java.util.Set; 39 40 import javax.imageio.ImageIO; 41 42 /** 43 * Delegate implementing the native methods of android.graphics.Bitmap 44 * 45 * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced 46 * by calls to methods of the same name in this delegate class. 47 * 48 * This class behaves like the original native implementation, but in Java, keeping previously 49 * native data into its own objects and mapping them to int that are sent back and forth between 50 * it and the original Bitmap class. 51 * 52 * @see DelegateManager 53 * 54 */ 55 public final class Bitmap_Delegate { 56 57 58 public enum BitmapCreateFlags { 59 PREMULTIPLIED, MUTABLE 60 } 61 62 // ---- delegate manager ---- 63 private static final DelegateManager<Bitmap_Delegate> sManager = 64 new DelegateManager<Bitmap_Delegate>(Bitmap_Delegate.class); 65 66 // ---- delegate helper data ---- 67 68 // ---- delegate data ---- 69 private final Config mConfig; 70 private BufferedImage mImage; 71 private boolean mHasAlpha = true; 72 private boolean mHasMipMap = false; // TODO: check the default. 73 private boolean mIsPremultiplied = true; 74 private int mGenerationId = 0; 75 76 77 // ---- Public Helper methods ---- 78 79 /** 80 * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object. 81 */ getDelegate(long native_bitmap)82 public static Bitmap_Delegate getDelegate(long native_bitmap) { 83 return sManager.getDelegate(native_bitmap); 84 } 85 86 @Nullable getDelegate(@ullable Bitmap bitmap)87 public static Bitmap_Delegate getDelegate(@Nullable Bitmap bitmap) { 88 // refSkPixelRef is a hack to get the native pointer: see #nativeRefPixelRef() 89 return bitmap == null ? null : getDelegate(bitmap.refSkPixelRef()); 90 } 91 92 /** 93 * Creates and returns a {@link Bitmap} initialized with the given file content. 94 * 95 * @param input the file from which to read the bitmap content 96 * @param isMutable whether the bitmap is mutable 97 * @param density the density associated with the bitmap 98 * 99 * @see Bitmap#isMutable() 100 * @see Bitmap#getDensity() 101 */ createBitmap(File input, boolean isMutable, Density density)102 public static Bitmap createBitmap(File input, boolean isMutable, Density density) 103 throws IOException { 104 return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density); 105 } 106 107 /** 108 * Creates and returns a {@link Bitmap} initialized with the given file content. 109 * 110 * @param input the file from which to read the bitmap content 111 * @param density the density associated with the bitmap 112 * 113 * @see Bitmap#isPremultiplied() 114 * @see Bitmap#isMutable() 115 * @see Bitmap#getDensity() 116 */ createBitmap(File input, Set<BitmapCreateFlags> createFlags, Density density)117 public static Bitmap createBitmap(File input, Set<BitmapCreateFlags> createFlags, 118 Density density) throws IOException { 119 // create a delegate with the content of the file. 120 Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888); 121 122 return createBitmap(delegate, createFlags, density.getDpiValue()); 123 } 124 125 /** 126 * Creates and returns a {@link Bitmap} initialized with the given stream content. 127 * 128 * @param input the stream from which to read the bitmap content 129 * @param isMutable whether the bitmap is mutable 130 * @param density the density associated with the bitmap 131 * 132 * @see Bitmap#isMutable() 133 * @see Bitmap#getDensity() 134 */ createBitmap(InputStream input, boolean isMutable, Density density)135 public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density) 136 throws IOException { 137 return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density); 138 } 139 140 /** 141 * Creates and returns a {@link Bitmap} initialized with the given stream content. 142 * 143 * @param input the stream from which to read the bitmap content 144 * @param density the density associated with the bitmap 145 * 146 * @see Bitmap#isPremultiplied() 147 * @see Bitmap#isMutable() 148 * @see Bitmap#getDensity() 149 */ createBitmap(InputStream input, Set<BitmapCreateFlags> createFlags, Density density)150 public static Bitmap createBitmap(InputStream input, Set<BitmapCreateFlags> createFlags, 151 Density density) throws IOException { 152 // create a delegate with the content of the stream. 153 Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888); 154 155 return createBitmap(delegate, createFlags, density.getDpiValue()); 156 } 157 158 /** 159 * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage} 160 * 161 * @param image the bitmap content 162 * @param isMutable whether the bitmap is mutable 163 * @param density the density associated with the bitmap 164 * 165 * @see Bitmap#isMutable() 166 * @see Bitmap#getDensity() 167 */ createBitmap(BufferedImage image, boolean isMutable, Density density)168 public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density) { 169 return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density); 170 } 171 172 /** 173 * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage} 174 * 175 * @param image the bitmap content 176 * @param density the density associated with the bitmap 177 * 178 * @see Bitmap#isPremultiplied() 179 * @see Bitmap#isMutable() 180 * @see Bitmap#getDensity() 181 */ createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags, Density density)182 public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags, 183 Density density) { 184 // create a delegate with the given image. 185 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888); 186 187 return createBitmap(delegate, createFlags, density.getDpiValue()); 188 } 189 getBufferedImageType()190 private static int getBufferedImageType() { 191 return BufferedImage.TYPE_INT_ARGB; 192 } 193 194 /** 195 * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}. 196 */ getImage()197 public BufferedImage getImage() { 198 return mImage; 199 } 200 201 /** 202 * Returns the Android bitmap config. Note that this not the config of the underlying 203 * Java2D bitmap. 204 */ getConfig()205 public Config getConfig() { 206 return mConfig; 207 } 208 209 /** 210 * Returns the hasAlpha rendering hint 211 * @return true if the bitmap alpha should be used at render time 212 */ hasAlpha()213 public boolean hasAlpha() { 214 return mHasAlpha && mConfig != Config.RGB_565; 215 } 216 217 /** 218 * Update the generationId. 219 * 220 * @see Bitmap#getGenerationId() 221 */ change()222 public void change() { 223 mGenerationId++; 224 } 225 226 // ---- native methods ---- 227 228 @LayoutlibDelegate nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean isMutable)229 /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width, 230 int height, int nativeConfig, boolean isMutable) { 231 int imageType = getBufferedImageType(); 232 233 // create the image 234 BufferedImage image = new BufferedImage(width, height, imageType); 235 236 if (colors != null) { 237 image.setRGB(0, 0, width, height, colors, offset, stride); 238 } 239 240 // create a delegate with the content of the stream. 241 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig)); 242 243 return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable), 244 Bitmap.getDefaultDensity()); 245 } 246 247 @LayoutlibDelegate nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable)248 /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) { 249 Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap); 250 if (srcBmpDelegate == null) { 251 return null; 252 } 253 254 BufferedImage srcImage = srcBmpDelegate.getImage(); 255 256 int width = srcImage.getWidth(); 257 int height = srcImage.getHeight(); 258 259 int imageType = getBufferedImageType(); 260 261 // create the image 262 BufferedImage image = new BufferedImage(width, height, imageType); 263 264 // copy the source image into the image. 265 int[] argb = new int[width * height]; 266 srcImage.getRGB(0, 0, width, height, argb, 0, width); 267 image.setRGB(0, 0, width, height, argb, 0, width); 268 269 // create a delegate with the content of the stream. 270 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig)); 271 272 return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable), 273 Bitmap.getDefaultDensity()); 274 } 275 276 @LayoutlibDelegate nativeCopyAshmem(long nativeSrcBitmap)277 /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) { 278 // Unused method; no implementation provided. 279 assert false; 280 return null; 281 } 282 283 @LayoutlibDelegate nativeDestructor(long nativeBitmap)284 /*package*/ static void nativeDestructor(long nativeBitmap) { 285 sManager.removeJavaReferenceFor(nativeBitmap); 286 } 287 288 @LayoutlibDelegate nativeRecycle(long nativeBitmap)289 /*package*/ static boolean nativeRecycle(long nativeBitmap) { 290 sManager.removeJavaReferenceFor(nativeBitmap); 291 return true; 292 } 293 294 @LayoutlibDelegate nativeReconfigure(long nativeBitmap, int width, int height, int config, int allocSize, boolean isPremultiplied)295 /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height, 296 int config, int allocSize, boolean isPremultiplied) { 297 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 298 "Bitmap.reconfigure() is not supported", null /*data*/); 299 } 300 301 @LayoutlibDelegate nativeCompress(long nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage)302 /*package*/ static boolean nativeCompress(long nativeBitmap, int format, int quality, 303 OutputStream stream, byte[] tempStorage) { 304 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 305 "Bitmap.compress() is not supported", null /*data*/); 306 return true; 307 } 308 309 @LayoutlibDelegate nativeErase(long nativeBitmap, int color)310 /*package*/ static void nativeErase(long nativeBitmap, int color) { 311 // get the delegate from the native int. 312 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 313 if (delegate == null) { 314 return; 315 } 316 317 BufferedImage image = delegate.mImage; 318 319 Graphics2D g = image.createGraphics(); 320 try { 321 g.setColor(new java.awt.Color(color, true)); 322 323 g.fillRect(0, 0, image.getWidth(), image.getHeight()); 324 } finally { 325 g.dispose(); 326 } 327 } 328 329 @LayoutlibDelegate nativeRowBytes(long nativeBitmap)330 /*package*/ static int nativeRowBytes(long nativeBitmap) { 331 // get the delegate from the native int. 332 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 333 if (delegate == null) { 334 return 0; 335 } 336 337 return delegate.mImage.getWidth(); 338 } 339 340 @LayoutlibDelegate nativeConfig(long nativeBitmap)341 /*package*/ static int nativeConfig(long nativeBitmap) { 342 // get the delegate from the native int. 343 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 344 if (delegate == null) { 345 return 0; 346 } 347 348 return delegate.mConfig.nativeInt; 349 } 350 351 @LayoutlibDelegate nativeHasAlpha(long nativeBitmap)352 /*package*/ static boolean nativeHasAlpha(long nativeBitmap) { 353 // get the delegate from the native int. 354 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 355 return delegate == null || delegate.mHasAlpha; 356 357 } 358 359 @LayoutlibDelegate nativeHasMipMap(long nativeBitmap)360 /*package*/ static boolean nativeHasMipMap(long nativeBitmap) { 361 // get the delegate from the native int. 362 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 363 return delegate == null || delegate.mHasMipMap; 364 365 } 366 367 @LayoutlibDelegate nativeGetPixel(long nativeBitmap, int x, int y)368 /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) { 369 // get the delegate from the native int. 370 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 371 if (delegate == null) { 372 return 0; 373 } 374 375 return delegate.mImage.getRGB(x, y); 376 } 377 378 @LayoutlibDelegate nativeGetPixels(long nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height)379 /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset, 380 int stride, int x, int y, int width, int height) { 381 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 382 if (delegate == null) { 383 return; 384 } 385 386 delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride); 387 } 388 389 390 @LayoutlibDelegate nativeSetPixel(long nativeBitmap, int x, int y, int color)391 /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) { 392 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 393 if (delegate == null) { 394 return; 395 } 396 397 delegate.getImage().setRGB(x, y, color); 398 } 399 400 @LayoutlibDelegate nativeSetPixels(long nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height)401 /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset, 402 int stride, int x, int y, int width, int height) { 403 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 404 if (delegate == null) { 405 return; 406 } 407 408 delegate.getImage().setRGB(x, y, width, height, colors, offset, stride); 409 } 410 411 @LayoutlibDelegate nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst)412 /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) { 413 // FIXME implement native delegate 414 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 415 "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/); 416 } 417 418 @LayoutlibDelegate nativeCopyPixelsFromBuffer(long nb, Buffer src)419 /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) { 420 // FIXME implement native delegate 421 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 422 "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/); 423 } 424 425 @LayoutlibDelegate nativeGenerationId(long nativeBitmap)426 /*package*/ static int nativeGenerationId(long nativeBitmap) { 427 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 428 if (delegate == null) { 429 return 0; 430 } 431 432 return delegate.mGenerationId; 433 } 434 435 @LayoutlibDelegate nativeCreateFromParcel(Parcel p)436 /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) { 437 // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only 438 // used during aidl call so really this should not be called. 439 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 440 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.", 441 null /*data*/); 442 return null; 443 } 444 445 @LayoutlibDelegate nativeWriteToParcel(long nativeBitmap, boolean isMutable, int density, Parcel p)446 /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, boolean isMutable, 447 int density, Parcel p) { 448 // This is only called when sending a bitmap through aidl, so really this should not 449 // be called. 450 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 451 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.", 452 null /*data*/); 453 return false; 454 } 455 456 @LayoutlibDelegate nativeExtractAlpha(long nativeBitmap, long nativePaint, int[] offsetXY)457 /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint, 458 int[] offsetXY) { 459 Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap); 460 if (bitmap == null) { 461 return null; 462 } 463 464 // get the paint which can be null if nativePaint is 0. 465 Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint); 466 467 if (paint != null && paint.getMaskFilter() != null) { 468 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER, 469 "MaskFilter not supported in Bitmap.extractAlpha", 470 null, null /*data*/); 471 } 472 473 int alpha = paint != null ? paint.getAlpha() : 0xFF; 474 BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha); 475 476 // create the delegate. The actual Bitmap config is only an alpha channel 477 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8); 478 479 // the density doesn't matter, it's set by the Java method. 480 return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE), 481 Density.DEFAULT_DENSITY /*density*/); 482 } 483 484 @LayoutlibDelegate nativeIsPremultiplied(long nativeBitmap)485 /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) { 486 // get the delegate from the native int. 487 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 488 return delegate != null && delegate.mIsPremultiplied; 489 490 } 491 492 @LayoutlibDelegate nativeSetPremultiplied(long nativeBitmap, boolean isPremul)493 /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) { 494 // get the delegate from the native int. 495 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 496 if (delegate == null) { 497 return; 498 } 499 500 delegate.mIsPremultiplied = isPremul; 501 } 502 503 @LayoutlibDelegate nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, boolean isPremul)504 /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, 505 boolean isPremul) { 506 // get the delegate from the native int. 507 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 508 if (delegate == null) { 509 return; 510 } 511 512 delegate.mHasAlpha = hasAlpha; 513 } 514 515 @LayoutlibDelegate nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap)516 /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) { 517 // get the delegate from the native int. 518 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 519 if (delegate == null) { 520 return; 521 } 522 523 delegate.mHasMipMap = hasMipMap; 524 } 525 526 @LayoutlibDelegate nativeSameAs(long nb0, long nb1)527 /*package*/ static boolean nativeSameAs(long nb0, long nb1) { 528 Bitmap_Delegate delegate1 = sManager.getDelegate(nb0); 529 if (delegate1 == null) { 530 return false; 531 } 532 533 Bitmap_Delegate delegate2 = sManager.getDelegate(nb1); 534 if (delegate2 == null) { 535 return false; 536 } 537 538 BufferedImage image1 = delegate1.getImage(); 539 BufferedImage image2 = delegate2.getImage(); 540 if (delegate1.mConfig != delegate2.mConfig || 541 image1.getWidth() != image2.getWidth() || 542 image1.getHeight() != image2.getHeight()) { 543 return false; 544 } 545 546 // get the internal data 547 int w = image1.getWidth(); 548 int h = image2.getHeight(); 549 int[] argb1 = new int[w*h]; 550 int[] argb2 = new int[w*h]; 551 552 image1.getRGB(0, 0, w, h, argb1, 0, w); 553 image2.getRGB(0, 0, w, h, argb2, 0, w); 554 555 // compares 556 if (delegate1.mConfig == Config.ALPHA_8) { 557 // in this case we have to manually compare the alpha channel as the rest is garbage. 558 final int length = w*h; 559 for (int i = 0 ; i < length ; i++) { 560 if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) { 561 return false; 562 } 563 } 564 return true; 565 } 566 567 return Arrays.equals(argb1, argb2); 568 } 569 570 // Only used by AssetAtlasService, which we don't care about. 571 @LayoutlibDelegate nativeRefPixelRef(long nativeBitmap)572 /*package*/ static long nativeRefPixelRef(long nativeBitmap) { 573 // Hack: This is called by Bitmap.refSkPixelRef() and LayoutLib uses that method to get 574 // the native pointer from a Bitmap. So, we return nativeBitmap here. 575 return nativeBitmap; 576 } 577 578 // ---- Private delegate/helper methods ---- 579 Bitmap_Delegate(BufferedImage image, Config config)580 private Bitmap_Delegate(BufferedImage image, Config config) { 581 mImage = image; 582 mConfig = config; 583 } 584 createBitmap(Bitmap_Delegate delegate, Set<BitmapCreateFlags> createFlags, int density)585 private static Bitmap createBitmap(Bitmap_Delegate delegate, 586 Set<BitmapCreateFlags> createFlags, int density) { 587 // get its native_int 588 long nativeInt = sManager.addNewDelegate(delegate); 589 590 int width = delegate.mImage.getWidth(); 591 int height = delegate.mImage.getHeight(); 592 boolean isMutable = createFlags.contains(BitmapCreateFlags.MUTABLE); 593 boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED); 594 595 // and create/return a new Bitmap with it 596 return new Bitmap(nativeInt, null /* buffer */, width, height, density, isMutable, 597 isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */); 598 } 599 getPremultipliedBitmapCreateFlags(boolean isMutable)600 private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) { 601 Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED); 602 if (isMutable) { 603 createFlags.add(BitmapCreateFlags.MUTABLE); 604 } 605 return createFlags; 606 } 607 608 /** 609 * Creates and returns a copy of a given BufferedImage. 610 * <p/> 611 * if alpha is different than 255, then it is applied to the alpha channel of each pixel. 612 * 613 * @param image the image to copy 614 * @param imageType the type of the new image 615 * @param alpha an optional alpha modifier 616 * @return a new BufferedImage 617 */ createCopy(BufferedImage image, int imageType, int alpha)618 /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) { 619 int w = image.getWidth(); 620 int h = image.getHeight(); 621 622 BufferedImage result = new BufferedImage(w, h, imageType); 623 624 int[] argb = new int[w * h]; 625 image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth()); 626 627 if (alpha != 255) { 628 final int length = argb.length; 629 for (int i = 0 ; i < length; i++) { 630 int a = (argb[i] >>> 24 * alpha) / 255; 631 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF); 632 } 633 } 634 635 result.setRGB(0, 0, w, h, argb, 0, w); 636 637 return result; 638 } 639 640 } 641