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, 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, 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, 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, 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, null /*data*/); 457 return null; 458 } 459 460 @LayoutlibDelegate nativeWriteToParcel(long nativeBitmap, int density, Parcel p)461 /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, int density, Parcel p) { 462 // This is only called when sending a bitmap through aidl, so really this should not 463 // be called. 464 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 465 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.", 466 null, null /*data*/); 467 return false; 468 } 469 470 @LayoutlibDelegate nativeExtractAlpha(long nativeBitmap, long nativePaint, int[] offsetXY)471 /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint, 472 int[] offsetXY) { 473 Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap); 474 if (bitmap == null) { 475 return null; 476 } 477 478 // get the paint which can be null if nativePaint is 0. 479 Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint); 480 481 if (paint != null && paint.getMaskFilter() != null) { 482 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER, 483 "MaskFilter not supported in Bitmap.extractAlpha", 484 null, null, null /*data*/); 485 } 486 487 int alpha = paint != null ? paint.getAlpha() : 0xFF; 488 BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha); 489 490 // create the delegate. The actual Bitmap config is only an alpha channel 491 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8); 492 delegate.mIsMutable = true; 493 494 // the density doesn't matter, it's set by the Java method. 495 return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE), 496 Density.DEFAULT_DENSITY /*density*/); 497 } 498 499 @LayoutlibDelegate nativeIsPremultiplied(long nativeBitmap)500 /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) { 501 // get the delegate from the native int. 502 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 503 return delegate != null && delegate.mIsPremultiplied; 504 505 } 506 507 @LayoutlibDelegate nativeSetPremultiplied(long nativeBitmap, boolean isPremul)508 /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) { 509 // get the delegate from the native int. 510 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 511 if (delegate == null) { 512 return; 513 } 514 515 delegate.mIsPremultiplied = isPremul; 516 } 517 518 @LayoutlibDelegate nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, boolean isPremul)519 /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, 520 boolean isPremul) { 521 // get the delegate from the native int. 522 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 523 if (delegate == null) { 524 return; 525 } 526 527 delegate.mHasAlpha = hasAlpha; 528 } 529 530 @LayoutlibDelegate nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap)531 /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) { 532 // get the delegate from the native int. 533 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 534 if (delegate == null) { 535 return; 536 } 537 538 delegate.mHasMipMap = hasMipMap; 539 } 540 541 @LayoutlibDelegate nativeSameAs(long nb0, long nb1)542 /*package*/ static boolean nativeSameAs(long nb0, long nb1) { 543 Bitmap_Delegate delegate1 = sManager.getDelegate(nb0); 544 if (delegate1 == null) { 545 return false; 546 } 547 548 Bitmap_Delegate delegate2 = sManager.getDelegate(nb1); 549 if (delegate2 == null) { 550 return false; 551 } 552 553 BufferedImage image1 = delegate1.getImage(); 554 BufferedImage image2 = delegate2.getImage(); 555 if (delegate1.mConfig != delegate2.mConfig || 556 image1.getWidth() != image2.getWidth() || 557 image1.getHeight() != image2.getHeight()) { 558 return false; 559 } 560 561 // get the internal data 562 int w = image1.getWidth(); 563 int h = image2.getHeight(); 564 int[] argb1 = new int[w*h]; 565 int[] argb2 = new int[w*h]; 566 567 image1.getRGB(0, 0, w, h, argb1, 0, w); 568 image2.getRGB(0, 0, w, h, argb2, 0, w); 569 570 // compares 571 if (delegate1.mConfig == Config.ALPHA_8) { 572 // in this case we have to manually compare the alpha channel as the rest is garbage. 573 final int length = w*h; 574 for (int i = 0 ; i < length ; i++) { 575 if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) { 576 return false; 577 } 578 } 579 return true; 580 } 581 582 return Arrays.equals(argb1, argb2); 583 } 584 585 @LayoutlibDelegate nativeGetAllocationByteCount(long nativeBitmap)586 /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) { 587 // get the delegate from the native int. 588 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 589 if (delegate == null) { 590 return 0; 591 } 592 int size = nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight(); 593 return size < 0 ? Integer.MAX_VALUE : size; 594 595 } 596 597 @LayoutlibDelegate nativePrepareToDraw(long nativeBitmap)598 /*package*/ static void nativePrepareToDraw(long nativeBitmap) { 599 // do nothing as Bitmap_Delegate does not have caches 600 } 601 602 @LayoutlibDelegate nativeCopyPreserveInternalConfig(long nativeBitmap)603 /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) { 604 Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap); 605 if (srcBmpDelegate == null) { 606 return null; 607 } 608 609 BufferedImage srcImage = srcBmpDelegate.getImage(); 610 611 // create the image 612 BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null), 613 srcImage.isAlphaPremultiplied(), null); 614 615 // create a delegate with the content of the stream. 616 Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig()); 617 delegate.mIsMutable = srcBmpDelegate.mIsMutable; 618 619 return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE), 620 Bitmap.getDefaultDensity()); 621 } 622 623 @LayoutlibDelegate nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, long nativeColorSpace)624 /*package*/ static Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, 625 long nativeColorSpace) { 626 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 627 "Bitmap.nativeWrapHardwareBufferBitmap() is not supported", null, null, null); 628 return null; 629 } 630 631 @LayoutlibDelegate nativeCreateGraphicBufferHandle(long nativeBitmap)632 /*package*/ static GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap) { 633 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 634 "Bitmap.nativeCreateGraphicBufferHandle() is not supported", null /*data*/); 635 return null; 636 } 637 638 @LayoutlibDelegate nativeIsSRGB(long nativeBitmap)639 /*package*/ static boolean nativeIsSRGB(long nativeBitmap) { 640 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 641 "Color spaces are not supported", null, null /*data*/); 642 return false; 643 } 644 645 @LayoutlibDelegate nativeComputeColorSpace(long nativePtr)646 /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) { 647 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 648 "Color spaces are not supported", null, null /*data*/); 649 return null; 650 } 651 652 @LayoutlibDelegate nativeSetColorSpace(long nativePtr, long nativeColorSpace)653 /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) { 654 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 655 "Color spaces are not supported", null, null /*data*/); 656 } 657 658 @LayoutlibDelegate nativeIsSRGBLinear(long nativePtr)659 /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) { 660 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 661 "Color spaces are not supported", null, null /*data*/); 662 return false; 663 } 664 665 @LayoutlibDelegate nativeSetImmutable(long nativePtr)666 /*package*/ static void nativeSetImmutable(long nativePtr) { 667 Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr); 668 if (bmpDelegate == null) { 669 return; 670 } 671 bmpDelegate.mIsMutable = false; 672 } 673 674 @LayoutlibDelegate nativeIsImmutable(long nativePtr)675 /*package*/ static boolean nativeIsImmutable(long nativePtr) { 676 Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr); 677 if (bmpDelegate == null) { 678 return false; 679 } 680 return !bmpDelegate.mIsMutable; 681 } 682 683 @LayoutlibDelegate nativeGetHardwareBuffer(long nativeBitmap)684 /*package*/ static HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap) { 685 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, 686 "HardwareBuffer is not supported", null, null /*data*/); 687 return null; 688 } 689 690 // ---- Private delegate/helper methods ---- 691 Bitmap_Delegate(BufferedImage image, Config config)692 private Bitmap_Delegate(BufferedImage image, Config config) { 693 mImage = image; 694 mConfig = config; 695 } 696 createBitmap(Bitmap_Delegate delegate, Set<BitmapCreateFlags> createFlags, int density)697 private static Bitmap createBitmap(Bitmap_Delegate delegate, 698 Set<BitmapCreateFlags> createFlags, int density) { 699 // get its native_int 700 long nativeInt = sManager.addNewDelegate(delegate); 701 702 int width = delegate.mImage.getWidth(); 703 int height = delegate.mImage.getHeight(); 704 boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED); 705 706 // and create/return a new Bitmap with it 707 return new Bitmap(nativeInt, width, height, density, isPremultiplied, 708 null /*ninePatchChunk*/, null /* layoutBounds */, true /* fromMalloc */); 709 } 710 getPremultipliedBitmapCreateFlags(boolean isMutable)711 private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) { 712 Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED); 713 if (isMutable) { 714 createFlags.add(BitmapCreateFlags.MUTABLE); 715 } 716 return createFlags; 717 } 718 719 /** 720 * Creates and returns a copy of a given BufferedImage. 721 * <p/> 722 * if alpha is different than 255, then it is applied to the alpha channel of each pixel. 723 * 724 * @param image the image to copy 725 * @param imageType the type of the new image 726 * @param alpha an optional alpha modifier 727 * @return a new BufferedImage 728 */ createCopy(BufferedImage image, int imageType, int alpha)729 /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) { 730 int w = image.getWidth(); 731 int h = image.getHeight(); 732 733 BufferedImage result = new BufferedImage(w, h, imageType); 734 735 int[] argb = new int[w * h]; 736 image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth()); 737 738 if (alpha != 255) { 739 final int length = argb.length; 740 for (int i = 0 ; i < length; i++) { 741 int a = (argb[i] >>> 24 * alpha) / 255; 742 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF); 743 } 744 } 745 746 result.setRGB(0, 0, w, h, argb, 0, w); 747 748 return result; 749 } 750 751 } 752