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.ILayoutLog; 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(), null); 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 return createBitmap(image, null, createFlags, density); 176 } 177 178 /** 179 * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage} 180 * 181 * @param image the bitmap content 182 * @param ninePatchChunk serialized ninepatch data 183 * @param density the density associated with the bitmap 184 * 185 * @see Bitmap#isPremultiplied() 186 * @see Bitmap#isMutable() 187 * @see Bitmap#getDensity() 188 */ createBitmap(BufferedImage image, byte[] ninePatchChunk, Set<BitmapCreateFlags> createFlags, Density density)189 public static Bitmap createBitmap(BufferedImage image, byte[] ninePatchChunk, 190 Set<BitmapCreateFlags> createFlags, Density density) { 191 // create a delegate with the given image. 192 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888); 193 delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE); 194 195 return createBitmap(delegate, createFlags, density.getDpiValue(), ninePatchChunk); 196 } 197 getBufferedImageType()198 private static int getBufferedImageType() { 199 return BufferedImage.TYPE_INT_ARGB; 200 } 201 202 /** 203 * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}. 204 */ getImage()205 public BufferedImage getImage() { 206 return mImage; 207 } 208 209 /** 210 * Returns the Android bitmap config. Note that this not the config of the underlying 211 * Java2D bitmap. 212 */ getConfig()213 public Config getConfig() { 214 return mConfig; 215 } 216 217 /** 218 * Returns the hasAlpha rendering hint 219 * @return true if the bitmap alpha should be used at render time 220 */ hasAlpha()221 public boolean hasAlpha() { 222 return mHasAlpha && mConfig != Config.RGB_565; 223 } 224 225 /** 226 * Update the generationId. 227 * 228 * @see Bitmap#getGenerationId() 229 */ change()230 public void change() { 231 mGenerationId++; 232 } 233 234 // ---- native methods ---- 235 236 @LayoutlibDelegate nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean isMutable, long nativeColorSpace)237 /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width, 238 int height, int nativeConfig, boolean isMutable, long nativeColorSpace) { 239 int imageType = getBufferedImageType(); 240 241 // create the image 242 BufferedImage image = new BufferedImage(width, height, imageType); 243 244 if (colors != null) { 245 image.setRGB(0, 0, width, height, colors, offset, stride); 246 } 247 248 // create a delegate with the content of the stream. 249 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig)); 250 delegate.mIsMutable = isMutable; 251 252 return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable), 253 Bitmap.getDefaultDensity(), null); 254 } 255 256 @LayoutlibDelegate nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable)257 /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) { 258 Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap); 259 if (srcBmpDelegate == null) { 260 return null; 261 } 262 263 BufferedImage srcImage = srcBmpDelegate.getImage(); 264 265 int width = srcImage.getWidth(); 266 int height = srcImage.getHeight(); 267 268 int imageType = getBufferedImageType(); 269 270 // create the image 271 BufferedImage image = new BufferedImage(width, height, imageType); 272 273 // copy the source image into the image. 274 int[] argb = new int[width * height]; 275 srcImage.getRGB(0, 0, width, height, argb, 0, width); 276 image.setRGB(0, 0, width, height, argb, 0, width); 277 278 // create a delegate with the content of the stream. 279 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig)); 280 delegate.mIsMutable = isMutable; 281 282 return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable), 283 Bitmap.getDefaultDensity(), null); 284 } 285 286 @LayoutlibDelegate nativeCopyAshmem(long nativeSrcBitmap)287 /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) { 288 // Unused method; no implementation provided. 289 assert false; 290 return null; 291 } 292 293 @LayoutlibDelegate nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig)294 /*package*/ static Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig) { 295 // Unused method; no implementation provided. 296 assert false; 297 return null; 298 } 299 300 @LayoutlibDelegate nativeGetNativeFinalizer()301 /*package*/ static long nativeGetNativeFinalizer() { 302 synchronized (Bitmap_Delegate.class) { 303 if (sFinalizer == -1) { 304 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor); 305 } 306 return sFinalizer; 307 } 308 } 309 310 @LayoutlibDelegate nativeRecycle(long nativeBitmap)311 /*package*/ static void nativeRecycle(long nativeBitmap) { 312 // In our case recycle() is a no-op. We will let the finalizer to dispose the bitmap. 313 } 314 315 @LayoutlibDelegate nativeReconfigure(long nativeBitmap, int width, int height, int config, boolean isPremultiplied)316 /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height, 317 int config, boolean isPremultiplied) { 318 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 319 "Bitmap.reconfigure() is not supported", null, null /*data*/); 320 } 321 322 @LayoutlibDelegate nativeCompress(long nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage)323 /*package*/ static boolean nativeCompress(long nativeBitmap, int format, int quality, 324 OutputStream stream, byte[] tempStorage) { 325 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 326 "Bitmap.compress() is not supported", null, null /*data*/); 327 return true; 328 } 329 330 @LayoutlibDelegate nativeErase(long nativeBitmap, int color)331 /*package*/ static void nativeErase(long nativeBitmap, int color) { 332 // get the delegate from the native int. 333 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 334 if (delegate == null) { 335 return; 336 } 337 338 BufferedImage image = delegate.mImage; 339 340 Graphics2D g = image.createGraphics(); 341 try { 342 g.setColor(new java.awt.Color(color, true)); 343 344 g.fillRect(0, 0, image.getWidth(), image.getHeight()); 345 } finally { 346 g.dispose(); 347 } 348 } 349 350 @LayoutlibDelegate nativeErase(long nativeBitmap, long colorSpacePtr, long color)351 /*package*/ static void nativeErase(long nativeBitmap, long colorSpacePtr, long color) { 352 nativeErase(nativeBitmap, Color.toArgb(color)); 353 } 354 355 @LayoutlibDelegate nativeRowBytes(long nativeBitmap)356 /*package*/ static int nativeRowBytes(long nativeBitmap) { 357 // get the delegate from the native int. 358 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 359 if (delegate == null) { 360 return 0; 361 } 362 363 return delegate.mImage.getWidth(); 364 } 365 366 @LayoutlibDelegate nativeConfig(long nativeBitmap)367 /*package*/ static int nativeConfig(long nativeBitmap) { 368 // get the delegate from the native int. 369 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 370 if (delegate == null) { 371 return 0; 372 } 373 374 return delegate.mConfig.nativeInt; 375 } 376 377 @LayoutlibDelegate nativeHasAlpha(long nativeBitmap)378 /*package*/ static boolean nativeHasAlpha(long nativeBitmap) { 379 // get the delegate from the native int. 380 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 381 return delegate == null || delegate.mHasAlpha; 382 383 } 384 385 @LayoutlibDelegate nativeHasMipMap(long nativeBitmap)386 /*package*/ static boolean nativeHasMipMap(long nativeBitmap) { 387 // get the delegate from the native int. 388 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 389 return delegate == null || delegate.mHasMipMap; 390 391 } 392 393 @LayoutlibDelegate nativeGetPixel(long nativeBitmap, int x, int y)394 /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) { 395 // get the delegate from the native int. 396 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 397 if (delegate == null) { 398 return 0; 399 } 400 401 return delegate.mImage.getRGB(x, y); 402 } 403 404 @LayoutlibDelegate nativeGetColor(long nativeBitmap, int x, int y)405 /*package*/ static long nativeGetColor(long nativeBitmap, int x, int y) { 406 return nativeGetPixel(nativeBitmap, x, y); 407 } 408 409 @LayoutlibDelegate nativeGetPixels(long nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height)410 /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset, 411 int stride, int x, int y, int width, int height) { 412 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 413 if (delegate == null) { 414 return; 415 } 416 417 delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride); 418 } 419 420 421 @LayoutlibDelegate nativeSetPixel(long nativeBitmap, int x, int y, int color)422 /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) { 423 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 424 if (delegate == null) { 425 return; 426 } 427 428 delegate.getImage().setRGB(x, y, color); 429 } 430 431 @LayoutlibDelegate nativeSetPixels(long nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height)432 /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset, 433 int stride, int x, int y, int width, int height) { 434 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 435 if (delegate == null) { 436 return; 437 } 438 439 delegate.getImage().setRGB(x, y, width, height, colors, offset, stride); 440 } 441 442 @LayoutlibDelegate nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst)443 /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) { 444 // FIXME implement native delegate 445 Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED, 446 "Bitmap.copyPixelsToBuffer is not supported.", null, null, null /*data*/); 447 } 448 449 @LayoutlibDelegate nativeCopyPixelsFromBuffer(long nb, Buffer src)450 /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) { 451 // FIXME implement native delegate 452 Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED, 453 "Bitmap.copyPixelsFromBuffer is not supported.", null, null, null /*data*/); 454 } 455 456 @LayoutlibDelegate nativeGenerationId(long nativeBitmap)457 /*package*/ static int nativeGenerationId(long nativeBitmap) { 458 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 459 if (delegate == null) { 460 return 0; 461 } 462 463 return delegate.mGenerationId; 464 } 465 466 @LayoutlibDelegate nativeCreateFromParcel(Parcel p)467 /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) { 468 // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only 469 // used during aidl call so really this should not be called. 470 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 471 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.", 472 null, null /*data*/); 473 return null; 474 } 475 476 @LayoutlibDelegate nativeWriteToParcel(long nativeBitmap, int density, Parcel p)477 /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, int density, Parcel p) { 478 // This is only called when sending a bitmap through aidl, so really this should not 479 // be called. 480 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 481 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.", 482 null, null /*data*/); 483 return false; 484 } 485 486 @LayoutlibDelegate nativeExtractAlpha(long nativeBitmap, long nativePaint, int[] offsetXY)487 /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint, 488 int[] offsetXY) { 489 Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap); 490 if (bitmap == null) { 491 return null; 492 } 493 494 // get the paint which can be null if nativePaint is 0. 495 Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint); 496 497 if (paint != null && paint.getMaskFilter() != null) { 498 Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MASKFILTER, 499 "MaskFilter not supported in Bitmap.extractAlpha", 500 null, null, null /*data*/); 501 } 502 503 int alpha = paint != null ? paint.getAlpha() : 0xFF; 504 BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha); 505 506 // create the delegate. The actual Bitmap config is only an alpha channel 507 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8); 508 delegate.mIsMutable = true; 509 510 // the density doesn't matter, it's set by the Java method. 511 return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE), 512 Density.DEFAULT_DENSITY /*density*/, null); 513 } 514 515 @LayoutlibDelegate nativeIsPremultiplied(long nativeBitmap)516 /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) { 517 // get the delegate from the native int. 518 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 519 return delegate != null && delegate.mIsPremultiplied; 520 521 } 522 523 @LayoutlibDelegate nativeSetPremultiplied(long nativeBitmap, boolean isPremul)524 /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) { 525 // get the delegate from the native int. 526 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 527 if (delegate == null) { 528 return; 529 } 530 531 delegate.mIsPremultiplied = isPremul; 532 } 533 534 @LayoutlibDelegate nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, boolean isPremul)535 /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, 536 boolean isPremul) { 537 // get the delegate from the native int. 538 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 539 if (delegate == null) { 540 return; 541 } 542 543 delegate.mHasAlpha = hasAlpha; 544 } 545 546 @LayoutlibDelegate nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap)547 /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) { 548 // get the delegate from the native int. 549 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 550 if (delegate == null) { 551 return; 552 } 553 554 delegate.mHasMipMap = hasMipMap; 555 } 556 557 @LayoutlibDelegate nativeSameAs(long nb0, long nb1)558 /*package*/ static boolean nativeSameAs(long nb0, long nb1) { 559 Bitmap_Delegate delegate1 = sManager.getDelegate(nb0); 560 if (delegate1 == null) { 561 return false; 562 } 563 564 Bitmap_Delegate delegate2 = sManager.getDelegate(nb1); 565 if (delegate2 == null) { 566 return false; 567 } 568 569 BufferedImage image1 = delegate1.getImage(); 570 BufferedImage image2 = delegate2.getImage(); 571 if (delegate1.mConfig != delegate2.mConfig || 572 image1.getWidth() != image2.getWidth() || 573 image1.getHeight() != image2.getHeight()) { 574 return false; 575 } 576 577 // get the internal data 578 int w = image1.getWidth(); 579 int h = image2.getHeight(); 580 int[] argb1 = new int[w*h]; 581 int[] argb2 = new int[w*h]; 582 583 image1.getRGB(0, 0, w, h, argb1, 0, w); 584 image2.getRGB(0, 0, w, h, argb2, 0, w); 585 586 // compares 587 if (delegate1.mConfig == Config.ALPHA_8) { 588 // in this case we have to manually compare the alpha channel as the rest is garbage. 589 final int length = w*h; 590 for (int i = 0 ; i < length ; i++) { 591 if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) { 592 return false; 593 } 594 } 595 return true; 596 } 597 598 return Arrays.equals(argb1, argb2); 599 } 600 601 @LayoutlibDelegate nativeGetAllocationByteCount(long nativeBitmap)602 /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) { 603 // get the delegate from the native int. 604 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); 605 if (delegate == null) { 606 return 0; 607 } 608 int size = nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight(); 609 return size < 0 ? Integer.MAX_VALUE : size; 610 611 } 612 613 @LayoutlibDelegate nativePrepareToDraw(long nativeBitmap)614 /*package*/ static void nativePrepareToDraw(long nativeBitmap) { 615 // do nothing as Bitmap_Delegate does not have caches 616 } 617 618 @LayoutlibDelegate nativeCopyPreserveInternalConfig(long nativeBitmap)619 /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) { 620 Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap); 621 if (srcBmpDelegate == null) { 622 return null; 623 } 624 625 BufferedImage srcImage = srcBmpDelegate.getImage(); 626 627 // create the image 628 BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null), 629 srcImage.isAlphaPremultiplied(), null); 630 631 // create a delegate with the content of the stream. 632 Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig()); 633 delegate.mIsMutable = srcBmpDelegate.mIsMutable; 634 635 return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE), 636 Bitmap.getDefaultDensity(), null); 637 } 638 639 @LayoutlibDelegate nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, long nativeColorSpace)640 /*package*/ static Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, 641 long nativeColorSpace) { 642 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 643 "Bitmap.nativeWrapHardwareBufferBitmap() is not supported", null, null, null); 644 return null; 645 } 646 647 @LayoutlibDelegate nativeIsSRGB(long nativeBitmap)648 /*package*/ static boolean nativeIsSRGB(long nativeBitmap) { 649 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 650 "Color spaces are not supported", null, null /*data*/); 651 return false; 652 } 653 654 @LayoutlibDelegate nativeComputeColorSpace(long nativePtr)655 /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) { 656 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 657 "Color spaces are not supported", null, null /*data*/); 658 return null; 659 } 660 661 @LayoutlibDelegate nativeSetColorSpace(long nativePtr, long nativeColorSpace)662 /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) { 663 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 664 "Color spaces are not supported", null, null /*data*/); 665 } 666 667 @LayoutlibDelegate nativeIsSRGBLinear(long nativePtr)668 /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) { 669 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 670 "Color spaces are not supported", null, null /*data*/); 671 return false; 672 } 673 674 @LayoutlibDelegate nativeSetImmutable(long nativePtr)675 /*package*/ static void nativeSetImmutable(long nativePtr) { 676 Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr); 677 if (bmpDelegate == null) { 678 return; 679 } 680 bmpDelegate.mIsMutable = false; 681 } 682 683 @LayoutlibDelegate nativeIsImmutable(long nativePtr)684 /*package*/ static boolean nativeIsImmutable(long nativePtr) { 685 Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr); 686 if (bmpDelegate == null) { 687 return false; 688 } 689 return !bmpDelegate.mIsMutable; 690 } 691 692 @LayoutlibDelegate nativeGetHardwareBuffer(long nativeBitmap)693 /*package*/ static HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap) { 694 Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, 695 "HardwareBuffer is not supported", null, null /*data*/); 696 return null; 697 } 698 699 // ---- Private delegate/helper methods ---- 700 Bitmap_Delegate(BufferedImage image, Config config)701 private Bitmap_Delegate(BufferedImage image, Config config) { 702 mImage = image; 703 mConfig = config; 704 } 705 createBitmap(Bitmap_Delegate delegate, Set<BitmapCreateFlags> createFlags, int density, byte[] ninePatchChunk)706 private static Bitmap createBitmap(Bitmap_Delegate delegate, 707 Set<BitmapCreateFlags> createFlags, int density, byte[] ninePatchChunk) { 708 // get its native_int 709 long nativeInt = sManager.addNewDelegate(delegate); 710 711 int width = delegate.mImage.getWidth(); 712 int height = delegate.mImage.getHeight(); 713 boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED); 714 715 // and create/return a new Bitmap with it 716 return new Bitmap(nativeInt, width, height, density, isPremultiplied, 717 ninePatchChunk, null /* layoutBounds */, true /* fromMalloc */); 718 } 719 getPremultipliedBitmapCreateFlags(boolean isMutable)720 private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) { 721 Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED); 722 if (isMutable) { 723 createFlags.add(BitmapCreateFlags.MUTABLE); 724 } 725 return createFlags; 726 } 727 728 /** 729 * Creates and returns a copy of a given BufferedImage. 730 * <p/> 731 * if alpha is different than 255, then it is applied to the alpha channel of each pixel. 732 * 733 * @param image the image to copy 734 * @param imageType the type of the new image 735 * @param alpha an optional alpha modifier 736 * @return a new BufferedImage 737 */ createCopy(BufferedImage image, int imageType, int alpha)738 /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) { 739 int w = image.getWidth(); 740 int h = image.getHeight(); 741 742 BufferedImage result = new BufferedImage(w, h, imageType); 743 744 int[] argb = new int[w * h]; 745 image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth()); 746 747 if (alpha != 255) { 748 final int length = argb.length; 749 for (int i = 0 ; i < length; i++) { 750 int a = (argb[i] >>> 24 * alpha) / 255; 751 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF); 752 } 753 } 754 755 result.setRGB(0, 0, w, h, argb, 0, w); 756 757 return result; 758 } 759 760 } 761