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