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