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